Add mount_volumes() to wk.os.mac
Supports both CoreStorage and APFS containers
This commit is contained in:
parent
a36589b66f
commit
908ffdc999
2 changed files with 182 additions and 7 deletions
|
|
@ -1384,18 +1384,25 @@ def ost_convert_report(original_report, start_index):
|
||||||
def ost_generate_volume_report(dev):
|
def ost_generate_volume_report(dev):
|
||||||
"""Generate volume report for dev, returns list."""
|
"""Generate volume report for dev, returns list."""
|
||||||
report = []
|
report = []
|
||||||
|
vol_report = None
|
||||||
|
|
||||||
# OS Check
|
# OS Check
|
||||||
if PLATFORM != 'Linux':
|
if PLATFORM == 'Darwin':
|
||||||
# TODO: Add macOS volume report
|
vol_report = wk_os.mac.mount_volumes(
|
||||||
|
device_path=dev.path,
|
||||||
|
read_write=False,
|
||||||
|
)
|
||||||
|
elif PLATFORM == 'Linux':
|
||||||
|
vol_report = wk_os.linux.mount_volumes(
|
||||||
|
device_path=dev.path,
|
||||||
|
read_write=False,
|
||||||
|
scan_corestorage=not dev.any_test_failed(),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Volume report unavailable
|
||||||
return report
|
return report
|
||||||
|
|
||||||
# Convert mount_volume report
|
# Convert mount_volume report
|
||||||
vol_report = wk_os.linux.mount_volumes(
|
|
||||||
device_path=dev.path,
|
|
||||||
read_write=False,
|
|
||||||
scan_corestorage=not dev.any_test_failed(),
|
|
||||||
)
|
|
||||||
for line in vol_report:
|
for line in vol_report:
|
||||||
line = std.strip_colors(line)
|
line = std.strip_colors(line)
|
||||||
match = VOLUME_REGEX.match(line)
|
match = VOLUME_REGEX.match(line)
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,12 @@
|
||||||
# vim: sts=2 sw=2 ts=2
|
# vim: sts=2 sw=2 ts=2
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import plistlib
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from wk import std
|
||||||
from wk.exe import run_program
|
from wk.exe import run_program
|
||||||
|
from wk.hw.obj import Disk
|
||||||
|
|
||||||
|
|
||||||
# STATIC VARIABLES
|
# STATIC VARIABLES
|
||||||
|
|
@ -32,6 +35,171 @@ def decode_smc_bytes(text):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_apfs_volumes(device_path):
|
||||||
|
"""Get APFS volumes contained in device_path, returns list."""
|
||||||
|
volumes = []
|
||||||
|
containers = []
|
||||||
|
|
||||||
|
# Get APFS details
|
||||||
|
cmd = ['diskutil', 'apfs', 'list', '-plist']
|
||||||
|
proc = run_program(cmd, check=False, encoding=None, errors=None)
|
||||||
|
try:
|
||||||
|
plist_data = plistlib.loads(proc.stdout)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
# Invalid / corrupt plist data? return empty dict to avoid crash
|
||||||
|
LOG.error('Failed to get diskutil apfs list for %s', device_path)
|
||||||
|
|
||||||
|
# Find container(s) relating to device_path
|
||||||
|
for container in plist_data['Containers']:
|
||||||
|
if container['DesignatedPhysicalStore'] == device_path:
|
||||||
|
containers.append(container)
|
||||||
|
|
||||||
|
# Add volumes
|
||||||
|
for container in containers:
|
||||||
|
for volume in container['Volumes']:
|
||||||
|
volumes.append(Disk(f'/dev/{volume["DeviceIdentifier"]}'))
|
||||||
|
|
||||||
|
# Done
|
||||||
|
return volumes
|
||||||
|
|
||||||
|
|
||||||
|
def get_core_storage_volumes(device_path):
|
||||||
|
"""Get CoreStorage volumes contained in device_path, returns list."""
|
||||||
|
disks = []
|
||||||
|
volumes = []
|
||||||
|
|
||||||
|
# Get coreStorage details
|
||||||
|
cmd = ['diskutil', 'corestorage', 'list', '-plist']
|
||||||
|
proc = run_program(cmd, check=False, encoding=None, errors=None)
|
||||||
|
try:
|
||||||
|
plist_data = plistlib.loads(proc.stdout)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
# invalid / corrupt plist data? return empty dict to avoid crash
|
||||||
|
LOG.error('failed to get diskutil corestorage list for %s', device_path)
|
||||||
|
|
||||||
|
# Find related virtual disks
|
||||||
|
for l_vg in plist_data['CoreStorageLogicalVolumeGroups']:
|
||||||
|
related = False
|
||||||
|
|
||||||
|
# Compare parent physical volumes againt device_path
|
||||||
|
for p_v in l_vg['CoreStoragePhysicalVolumes']:
|
||||||
|
uuid = p_v['CoreStorageUUID']
|
||||||
|
cmd = ['diskutil', 'coreStorage', 'info', '-plist', uuid]
|
||||||
|
try:
|
||||||
|
uuid_data = plistlib.loads(proc.stdout)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
LOG.error('failed to get diskutil corestorage info for %s', uuid)
|
||||||
|
continue
|
||||||
|
if uuid_data['DeviceIdentifier'] == device_path:
|
||||||
|
related = True
|
||||||
|
break
|
||||||
|
|
||||||
|
# Move on if no related p_v was found
|
||||||
|
if not related:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Add logical disks to list
|
||||||
|
for l_v in l_vg['CoreStorageLogicalVolumes']:
|
||||||
|
uuid = l_v['CoreStorageUUID']
|
||||||
|
cmd = ['diskutil', 'coreStorage', 'info', '-plist', uuid]
|
||||||
|
try:
|
||||||
|
uuid_data = plistlib.loads(proc.stdout)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
LOG.error('failed to get diskutil corestorage info for %s', uuid)
|
||||||
|
continue
|
||||||
|
disks.append(uuid_data['DeviceIdentifier'])
|
||||||
|
|
||||||
|
# Get volumes from logical disks
|
||||||
|
for disk in disks:
|
||||||
|
cmd = ['diskutil', 'list', '-plist', f'/dev/{disk}']
|
||||||
|
proc = run_program(cmd, check=False, encoding=None, errors=None)
|
||||||
|
try:
|
||||||
|
disk_data = plistlib.loads(proc.stdout)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
LOG.error('Failed to get diskutil list for %s', disk)
|
||||||
|
continue
|
||||||
|
for part in disk_data['AllDisksAndPartitions'][0]['Partitions']:
|
||||||
|
volumes.append(Disk(f'/dev/{part["DeviceIdentifier"]}'))
|
||||||
|
|
||||||
|
# Done
|
||||||
|
return volumes
|
||||||
|
|
||||||
|
|
||||||
|
def mount_volumes(device_path=None, read_write=False):
|
||||||
|
"""Mount all detected volumes, returns list.
|
||||||
|
|
||||||
|
NOTE: If device_path is specified then only volumes
|
||||||
|
under that path will be mounted.
|
||||||
|
"""
|
||||||
|
report = []
|
||||||
|
volumes = []
|
||||||
|
|
||||||
|
# Get device details
|
||||||
|
cmd = [
|
||||||
|
'diskutil',
|
||||||
|
'list',
|
||||||
|
'-plist',
|
||||||
|
]
|
||||||
|
if device_path:
|
||||||
|
cmd.append(device_path)
|
||||||
|
proc = run_program(cmd, check=False, encoding=None, errors=None)
|
||||||
|
try:
|
||||||
|
plist_data = plistlib.loads(proc.stdout)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
# Invalid / corrupt plist data? return empty dict to avoid crash
|
||||||
|
LOG.error('Failed to get diskutil list for %s', device_path)
|
||||||
|
return report
|
||||||
|
|
||||||
|
# Mount and add volumes
|
||||||
|
for part in plist_data['AllDisksAndPartitions'][0]['Partitions']:
|
||||||
|
cmd = ['diskutil', 'mount', 'readOnly', part['DeviceIdentifier']]
|
||||||
|
if read_write:
|
||||||
|
# NOTE: Unused 2021-03
|
||||||
|
cmd.pop(2)
|
||||||
|
proc = run_program(cmd, check=False)
|
||||||
|
|
||||||
|
# Add volume
|
||||||
|
volumes.append(Disk(f'/dev/{part["DeviceIdentifier"]}'))
|
||||||
|
|
||||||
|
# Add sub-volumes
|
||||||
|
if proc.returncode:
|
||||||
|
if part['Content'] == 'Apple_CoreStorage':
|
||||||
|
volumes.extend(get_core_storage_volumes(part['DeviceIdentifier']))
|
||||||
|
elif part['Content'] == 'Apple_APFS':
|
||||||
|
volumes.extend(get_apfs_volumes(part['DeviceIdentifier']))
|
||||||
|
|
||||||
|
# Build report from volume list
|
||||||
|
for vol in volumes:
|
||||||
|
result = f'{vol.details["name"]:<20}'
|
||||||
|
|
||||||
|
# Containers
|
||||||
|
if vol.details['Content'] in ('Apple_APFS', 'Apple_CoreStorage'):
|
||||||
|
result += f'{vol.details["Content"].replace("Apple_", "")} container'
|
||||||
|
report.append(std.color_string(result, 'BLUE'))
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Unknown partitions
|
||||||
|
if not vol.details['mountpoint']:
|
||||||
|
result += 'Failed to mount'
|
||||||
|
report.append(std.color_string(result, 'RED'))
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Volumes
|
||||||
|
result += f'{"Mounted on "+vol.details.get("mountpoint", "?"):<40}'
|
||||||
|
size = vol.details.get('VolumeSize', -1)
|
||||||
|
free = vol.details.get('FreeSpace', -1)
|
||||||
|
used = size - free
|
||||||
|
result = (
|
||||||
|
f'{result} ({vol.details.get("fstype", "Unknown FS")+",":<5} '
|
||||||
|
f'{std.bytes_to_string(used, decimals=1):>9} used, '
|
||||||
|
f'{std.bytes_to_string(size, decimals=1):>9} size, '
|
||||||
|
)
|
||||||
|
report.append(result)
|
||||||
|
|
||||||
|
# Done
|
||||||
|
return report
|
||||||
|
|
||||||
|
|
||||||
def set_fans(mode):
|
def set_fans(mode):
|
||||||
"""Set fans to auto or max."""
|
"""Set fans to auto or max."""
|
||||||
if mode == 'auto':
|
if mode == 'auto':
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue