2011 MacBook Pros and smart groups

cwaldrip
Valued Contributor

This is damn frustrating. Not sure if it's Apple's issue or Casper.

Apple GSX (and MacTracker) lists the model name as "MacBook Pro (15-inch, Early 2011)" or "MacBook Pro (15-inch, Late 2011)" while Casper lists them both as "15-inch MacBook Pro (2011)"!

I'm not sure if Casper is pulling this info from the hardware itself, or from an internal database. But it becomes a big issue when trying to separate Early from Late 2011 machines (13, 15 or 17).

This is a big problem for me because I'm trying to create smart groups for policies so we can deploy Apple Hardware Test to our machines since we reimage them (which removes AHT). The Early models of 2011 MacBook Pros (13/15/17) use versions 3A208, 3A209, and 3A211 of AHT. While the Late models of 2011 MacBook Pros (13/15/17) use version 3A222. But I've found no way of differentiating between at least the Early and Late 2011 15" machines.

  • I can't use Model ID, they're both MacBookPro8,2.
  • I can't use Model name, they're both 15-inch MacBook Pro (2011).
  • I can't use Model Number, they're both A1286.
  • I can't use CPU speed, they both have an identical clock speed (2.0/2.2/2.3 vs. 2.2/2.4/2.5) as a possible configuration.
  • I can't use the video RAM, they both have 1 GB GDDR5 as possible configurations.
  • I can't use the GPU, they both have an AMD Radeon HD 6750M as possible configurations.

If Casper is pulling the model name from a built-in database - STOP! Please. If it's pulling it from the machine, well damnit. :-(

Does anyone have any suggestions for how I can create a smart group to differentiate between the two models of 2011 15" MacBook Pros?

1 ACCEPTED SOLUTION

stevewood
Honored Contributor II
Honored Contributor II

I took Per's code (https://github.com/MagerValp/MacModelShelf), borrowed some code from @chilcote (https://github.com/chilcote/pyfacts/blob/master/pyfacts) and made a few changes to be able to use this as an EA.

You'll need to place Per's macmodelshelf.db file somewhere on these machines and update the script to point to that location, but once done you have an EA that will return the model info.

#!/usr/bin/env python


import sys
import shelve
import urllib2
import subprocess
import plistlib
from xml.etree import ElementTree


DBPATH = "/your/path/to/macmodelshelf"

try:
    macmodelshelf = shelve.open(DBPATH)
except BaseException, e:
    print >>sys.stderr, "Couldn't open macmodelshelf.db: %s" % e
    sys.exit(1)


def model_code(serial):
    if "serial" in serial.lower(): # Workaround for machines with dummy serial numbers.
        return None
    if len(serial) in (12, 13) and serial.startswith("S"): # Remove S prefix from scanned codes.
        serial = serial[1:]
    if len(serial) in (11, 12):
        return serial[8:].decode("ascii")
    return None


def lookup_mac_model_code_from_apple(model_code):
    try:
        f = urllib2.urlopen("http://support-sp.apple.com/sp/product?cc=%s&lang=en_US" % model_code, timeout=2)
        et = ElementTree.parse(f)
        return et.findtext("configCode").decode("utf-8")
    except:
        return None


def model(code):
    global macmodelshelf
    code = code.upper()
    try:
        model = macmodelshelf[code]
    except KeyError:
        print >>sys.stderr, "Looking up %s from Apple" % code
        model = lookup_mac_model_code_from_apple(code)
        if model:
            macmodelshelf[code] = model
    return model


def _dump():
    print "macmodelshelfdump = {"
    for code, model in sorted(macmodelshelf.items()):
        print '    "%s": "%s",' % (code, model)
    print "}"


if __name__ == '__main__':
    try:
        output = subprocess.check_output(['/usr/sbin/ioreg',
                '-c', 'IOPlatformExpertDevice',
                '-d', '2',
                '-a'])
        d = plistlib.readPlistFromString(output)
        #return d['IORegistryEntryChildren'][0]['IOPlatformSerialNumber']

        sernum=d['IORegistryEntryChildren'][0]['IOPlatformSerialNumber']
#        m = model(sernum)
        if len(sernum) in (11, 12, 13):
            m = model(model_code(sernum))
        else:
            m = model(sernum)

        if m:
            print "<result>"+m+"</result>"
            sys.exit(0)
        else:
            print >>sys.stderr, "Unknown model %s" % repr(sys.argv[1])
            sys.exit(1)
    except IndexError:
        print "Usage: macmodelshelf.py serial_number"
        sys.exit(1)

I know this can be cleaned up a lot, and there might be a different way to do this, but I just wanted to prove it could be done with the code. Besides, my Python Fu is not that good.

Hopefully that will help you get part of the way to your SG.

HTH

View solution in original post

8 REPLIES 8

mm2270
Legendary Contributor III

Its JAMF mostly, although in some cases, Apple is also to blame since they very stupidly started deciding to re-use model identifiers for different Macs. (Aside: Who in their right, sane mind decided it was a good idea to re-use a model identifier. Isn't a model identifier supposed to be unique to that specific hardware!? grrrr Apple!!)

Anyway, I've complained often about this myself before. JAMF has a hardcoded table in the JSS database that translates the model ids into a human readable name, but since Apple is using the same id for multiple different models, well, you get the picture.

Your best bet is to implement an Extension Attribute that uses Apple's own process to identify the actual Mac name, and not go by some wacky translation table the way JAMF is doing.
See my post on this old FR for an example of how to do it. There are variations on this general process out there as well, so pick your poison.
https://jamfnation.jamfsoftware.com/featureRequest.html?id=2567#responseChild7202

Edit: Here's a somewhat simplified version of the EA script that gets the same information, just doesn't need to do 2 possible curl commands to get it.

#!/bin/sh

SerialNum=$(ioreg -rd1 -c IOPlatformExpertDevice | awk -F'"' '/IOPlatformSerialNumber/{print $4}')

if [[ ${#SerialNum} -eq 12 ]]; then
    Serial=$(echo "$SerialNum" | tail -c 5)
else
    Serial=$(echo "$SerialNum" | tail -c 4)
fi

FullModelName=$(curl -s "http://support-sp.apple.com/sp/product?cc=$Serial&lang=en_US" | xmllint --format - 2>/dev/null | awk -F'>|<' '/<configCode>/{print $3}')

if [ "$FullModelName" != "" ]; then
    echo "<result>$FullModelName</result>"
else
    echo "<result>NOT FOUND</result>"
fi

stevewood
Honored Contributor II
Honored Contributor II

Is there a way to pull the EMC number or the Order Number? Looking at MacTracker, those are the two sets of identifiers that seem to be different, but I haven't figured out how to pull them. I've been tinkering for the last 30 minutes trying to see if system_profiler or ioreg, or a curl from one of the Apple sites would pull that info down, but I can't find one.

That might be your only choice, other than dropping a text file on the machine at imaging time to ID the machines.

dwandro92
Contributor III

It's not ideal, but you could combine "Model Name 'Like'" and "Serial Number 'Like'" criteria to determine this info. See https://github.com/MagerValp/MacModelShelf for more info

davidacland
Honored Contributor II
Honored Contributor II

Bit of a different approach, but could you create a smart group using the CCC code in the serial number? The last three characters should be be fairly consistent per model.

mm2270
Legendary Contributor III

@davidacland The only possible issue with that approach is that the Serial Number criteria does not have an "ends with" modifier, only "like" so there's a remote possibility of those last 3 characters showing up somewhere else within the serial number. I suppose its probably not likely, but I don't know for sure. I still think getting the full human readable name direct from Apple in an EA is the best approach, since then you can just do something like

Actual model name | is | "MacBook Pro (15-inch, Late 2011)"

to correctly gather all those models into a Smart Group.

I was also thinking that my EA script above could be made to look for a local file with the full model name, and do the curl pull from Apple if its not there, then write the value into the file. Next time the EA script runs, and every time thereafter, it should see the file and pull the stored value. That way its not doing a curl against Apple's site at every inventory. Its not as if a Mac's model name ever changes from day to day :)

davidacland
Honored Contributor II
Honored Contributor II

Thats true. I guess you could grab the CCC via an EA ioreg -l | grep IOPlatformSerialNumber | awk '{print $4}' | sed 's/"//g' | cut -c 10-12

stevewood
Honored Contributor II
Honored Contributor II

I took Per's code (https://github.com/MagerValp/MacModelShelf), borrowed some code from @chilcote (https://github.com/chilcote/pyfacts/blob/master/pyfacts) and made a few changes to be able to use this as an EA.

You'll need to place Per's macmodelshelf.db file somewhere on these machines and update the script to point to that location, but once done you have an EA that will return the model info.

#!/usr/bin/env python


import sys
import shelve
import urllib2
import subprocess
import plistlib
from xml.etree import ElementTree


DBPATH = "/your/path/to/macmodelshelf"

try:
    macmodelshelf = shelve.open(DBPATH)
except BaseException, e:
    print >>sys.stderr, "Couldn't open macmodelshelf.db: %s" % e
    sys.exit(1)


def model_code(serial):
    if "serial" in serial.lower(): # Workaround for machines with dummy serial numbers.
        return None
    if len(serial) in (12, 13) and serial.startswith("S"): # Remove S prefix from scanned codes.
        serial = serial[1:]
    if len(serial) in (11, 12):
        return serial[8:].decode("ascii")
    return None


def lookup_mac_model_code_from_apple(model_code):
    try:
        f = urllib2.urlopen("http://support-sp.apple.com/sp/product?cc=%s&lang=en_US" % model_code, timeout=2)
        et = ElementTree.parse(f)
        return et.findtext("configCode").decode("utf-8")
    except:
        return None


def model(code):
    global macmodelshelf
    code = code.upper()
    try:
        model = macmodelshelf[code]
    except KeyError:
        print >>sys.stderr, "Looking up %s from Apple" % code
        model = lookup_mac_model_code_from_apple(code)
        if model:
            macmodelshelf[code] = model
    return model


def _dump():
    print "macmodelshelfdump = {"
    for code, model in sorted(macmodelshelf.items()):
        print '    "%s": "%s",' % (code, model)
    print "}"


if __name__ == '__main__':
    try:
        output = subprocess.check_output(['/usr/sbin/ioreg',
                '-c', 'IOPlatformExpertDevice',
                '-d', '2',
                '-a'])
        d = plistlib.readPlistFromString(output)
        #return d['IORegistryEntryChildren'][0]['IOPlatformSerialNumber']

        sernum=d['IORegistryEntryChildren'][0]['IOPlatformSerialNumber']
#        m = model(sernum)
        if len(sernum) in (11, 12, 13):
            m = model(model_code(sernum))
        else:
            m = model(sernum)

        if m:
            print "<result>"+m+"</result>"
            sys.exit(0)
        else:
            print >>sys.stderr, "Unknown model %s" % repr(sys.argv[1])
            sys.exit(1)
    except IndexError:
        print "Usage: macmodelshelf.py serial_number"
        sys.exit(1)

I know this can be cleaned up a lot, and there might be a different way to do this, but I just wanted to prove it could be done with the code. Besides, my Python Fu is not that good.

Hopefully that will help you get part of the way to your SG.

HTH

cwaldrip
Valued Contributor

Just over 24 hours... and i'm gobsmacked. I so hope I can make it to JNUC next year so I can buy people some beers. :-)

I'm going to use @stevewood's script. Normally we don't "push" anything to our users since we don't know where they might be, and what type of internet connection they could be using and if they're doing anything critical for air. Since this is really only affecting these 2011 MacBook Pros though, and since their number is blissfully declining, we're going to make an exception for them.

I'll try and get back to this thread with updates.