Created
June 17, 2015 12:31
-
-
Save bochoven/4d27022a63c725c3a2b9 to your computer and use it in GitHub Desktop.
Mac OSX: gather info on local disks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
"""Mac OS X Disk/Volume script for munkireport. | |
https://github.com/munkireport/munkireport-php | |
Some ideas and functions are borrowed from gmacpyutil | |
https://github.com/google/macops | |
The script iterates over all partitions and returns a list | |
of Volumes with basic info (TotalSize, FreeSpace, MountPoint, VolumeName) | |
It also reports SMARTStatus from the underlying Physical Disk(s) | |
in case of a AppleRAID or CoreStorage Volume | |
Disk sizes are converted to strings with a precision of .5GB | |
""" | |
import subprocess | |
import plistlib | |
# Round to .5GB precision (29 bit) | |
def _HalfGB(size): | |
return size >> 29 << 29 | |
def _DictFromDiskutilList(): | |
"""calls diskutil list -plist and returns as dict.""" | |
command = ["/usr/sbin/diskutil", "list", "-plist"] | |
return _DictFromSubprocess(command) | |
def _DictFromDiskutilInfo(deviceid): | |
"""calls diskutil info for a specific device id. | |
Args: | |
deviceid: a given device id for a disk like object | |
Returns: | |
info: dictionary from resulting plist output | |
""" | |
command = ["/usr/sbin/diskutil", "info", "-plist", deviceid] | |
return _DictFromSubprocess(command) | |
def filteredDiskInfo(deviceid): | |
""" Returns a filtered dictionary """ | |
attrs = _DictFromDiskutilInfo(deviceid) | |
keys = ["BusProtocol", "Content", "CoreStorageCompositeDisk", "Ejectable", | |
"FreeSpace", "Internal", "CoreStorageLVGUUID", "MountPoint", | |
"RAIDMaster", "RAIDMemberUUID", "RAIDSetMembers", "SMARTStatus", | |
"SolidState", "TotalSize", "VolumeName"] | |
out = {} | |
for key in keys: | |
try: | |
attribute = key.lower().replace(" ", "") | |
out[attribute] = attrs[key] | |
# pylint: disable=pointless-except | |
except KeyError: # not all objects have all these attributes | |
pass | |
# Set sizes to string with .5GB precision | |
out['totalsize'] = str(_HalfGB(out['totalsize'])); | |
out['freespace'] = str(_HalfGB(out['freespace'])); | |
return out | |
def AllDisksAndPartitions(): | |
"""Returns list of all disks and partitions.""" | |
try: | |
return _DictFromDiskutilList()["AllDisksAndPartitions"] | |
except KeyError: | |
# TODO(user): fix errors to actually provide info... | |
raise Exception("Unable to list all partitions.") | |
def _DictFromSubprocess(command, stdin=None): | |
"""returns a dict based upon a subprocess call with a -plist argument. | |
Args: | |
command: the command to be executed as a list | |
stdin: any standard input required. | |
Returns: | |
dict: dictionary from command output | |
Raises: | |
Exception: Error running command | |
Exception: Error creating plist from standard output | |
""" | |
task = {} | |
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
(stdout, stderr) = p.communicate() | |
if p.returncode is not 0: | |
raise Exception("Error running command: %s, stderr: %s" % | |
(command, stderr)) | |
else: | |
try: | |
return plistlib.readPlistFromString(stdout) | |
except xml.parsers.expat.ExpatError: | |
raise Exception("Error creating plist from output: %s" % stdout) | |
def DiskReport(): | |
""" Retrieve a list of dicts containing disk info """ | |
volumeList = [] | |
coreStorageDict = {} | |
appleRaidDict = {} | |
for disk in AllDisksAndPartitions(): | |
if getattr(disk, 'Partitions', None): | |
for partition in disk.Partitions: | |
if partition.Content == 'Apple_HFS': | |
diskInfo = filteredDiskInfo(partition.DeviceIdentifier) | |
if diskInfo['busprotocol'] == 'Disk Image': | |
# Skip Disk Images | |
continue | |
volumeList.append(diskInfo) | |
elif partition.Content == 'Apple_CoreStorage': | |
diskInfo = filteredDiskInfo(partition.DeviceIdentifier) | |
coreStorageDict[diskInfo['corestoragelvguuid']] = diskInfo | |
elif partition.Content == 'Apple_RAID': | |
diskInfo = filteredDiskInfo(partition.DeviceIdentifier) | |
appleRaidDict[diskInfo['raidmemberuuid']] = diskInfo | |
else: | |
if disk.Content == 'Apple_HFS': | |
diskInfo = filteredDiskInfo(disk.DeviceIdentifier) | |
if diskInfo['busprotocol'] == 'Disk Image': | |
# Skip Disk Images | |
continue | |
if diskInfo.get('corestoragelvguuid', 0): | |
# Update SMART status from Physical Volume | |
pv = coreStorageDict[diskInfo['corestoragelvguuid']] | |
diskInfo.update({'smartstatus': pv['smartstatus']}) | |
if diskInfo.get('raidsetmembers', 0): | |
# Update SMART status from RAID members | |
for member in diskInfo['raidsetmembers']: | |
raidMember = appleRaidDict[member] | |
diskInfo.update({'smartstatus': raidMember['smartstatus']}) | |
# If we find a irregular status, break | |
if raidMember['smartstatus'] not in ['Verified', 'Not Supported']: | |
break | |
volumeList.append(diskInfo) | |
return volumeList | |
# Write disk report to cache | |
print plistlib.writePlistToString(DiskReport()) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment