Set details for Dev/Dir/Image objects

* Colored report data is generated during obj instantiation
  * Code has been moved into its own function
  * Entire colored string is now stored for each Obj
  * (Should make show_selection, etc more mode/Obj agnostic)
* loopback_dev vs image_path is now better separated
  * losetup is called in ImageObj.set_details()
  * loopback -D is still called during program cleanup/wrapup
* get_device_size_in_bytes() has been renamed get_size_in_bytes()
This commit is contained in:
2Shirt 2018-07-25 23:31:04 -06:00
parent 180eb0f9ef
commit 66c7563335
Signed by: 2Shirt
GPG key ID: 152FAC923B0E132C

View file

@ -206,7 +206,10 @@ class DevObj(BaseObj):
def set_details(self):
"""Set details via lsblk."""
self.type = 'Dev'
# TODO Run lsblk
self.details = get_device_details(self.path)
self.name = self.details.get('name', 'UNKNOWN')
self.size = get_size_in_bytes(self.details.get('size', 'UNKNOWN'))
self.report = get_device_report(self.path)
class DirObj(BaseObj):
@ -218,7 +221,10 @@ class DirObj(BaseObj):
def set_details(self):
"""Set details via findmnt."""
self.type = 'Dir'
# TODO Run findmnt
self.details = get_dir_details(self.path)
self.name = self.path
self.size = get_size_in_bytes(self.details.get('avail', 'UNKNOWN'))
self.report = get_dir_report(self.path)
class ImageObj(BaseObj):
@ -230,9 +236,12 @@ class ImageObj(BaseObj):
def set_details(self):
"""Setup loopback device and set details via lsblk."""
self.type = 'Image'
# TODO Run losetup
# TODO Run lsblk
# TODO Remove loopback device
self.loop_dev = setup_loopback_device(self.path)
self.details = get_image_details(self.loopdev)
self.name = self.path[self.path.rfind('/')+1:]
self.size = get_size_in_bytes(self.details.get('size', 'UNKNOWN'))
self.report = get_image_report(self.loop_dev)
self.report = self.report.replace(self.loop_dev, '{Img}')
# Functions
@ -333,28 +342,14 @@ def dest_safety_check(source, dest):
"""Verify the destination is appropriate for the source."""
source_size = source['Details']['size']
if dest['Is Dir']:
cmd = [
'findmnt', '-J',
'-o', 'SOURCE,TARGET,FSTYPE,OPTIONS,SIZE,AVAIL,USED',
'-T', dest['Path']]
result = run_program(cmd)
try:
json_data = json.loads(result.stdout.decode())
except Exception:
# Welp, let's abort
print_error('Failed to verify destination usability.')
abort_ddrescue_tui()
else:
dest_size = json_data['filesystems'][0]['avail']
dest['Free Space'] = dest_size
dest['Filesystem'] = json_data['filesystems'][0]['fstype']
dest['Mount options'] = json_data['filesystems'][0]['options']
# MOVED
pass
else:
dest_size = dest['Details']['size']
# Convert to bytes and compare size
source_size = get_device_size_in_bytes(source_size)
dest_size = get_device_size_in_bytes(dest_size)
source_size = get_size_in_bytes(source_size)
dest_size = get_size_in_bytes(dest_size)
if source['Type'] == 'Image' and dest_size < (source_size * 1.2):
# Imaging: ensure 120% of source size is available
print_error(
@ -413,15 +408,75 @@ def get_device_details(dev_path):
dev_path)
result = run_program(cmd)
except CalledProcessError:
print_error('Failed to get device details for {}'.format(dev_path))
abort_ddrescue_tui()
# Return empty dict and let calling section deal with the issue
return {}
json_data = json.loads(result.stdout.decode())
# Just return the first device (there should only be one)
return json_data['blockdevices'][0]
def get_device_size_in_bytes(s):
def get_device_report(dev_path):
"""Build colored device report using lsblk, returns str."""
result = run_program([
'lsblk', '--nodeps',
'--output', 'NAME,TRAN,TYPE,SIZE,VENDOR,MODEL,SERIAL',
dev_path])
lines = result.stdout.decode().strip().splitlines()
lines.append('')
# FS details (if any)
result = run_program([
'lsblk',
'--output', 'NAME,SIZE,FSTYPE,LABEL,MOUNTPOINT',
dev_path])
lines.extend(result.stdout.decode().strip().splitlines())
# Color label lines
output = []
for line in lines:
if line[0:4] == 'NAME':
output.append('{BLUE}{line}{CLEAR}'.format(line=line, **COLORS))
else:
output.append(line)
# Done
return '\n'.join(output)
def get_dir_details(dir_path):
"""Get dir details via findmnt, returns JSON dict."""
try:
result = run_program([
'findmnt', '-J',
'-o', 'SOURCE,TARGET,FSTYPE,OPTIONS,SIZE,AVAIL,USED',
'-T', dir_path])
json_data = json.loads(result.stdout.decode())
except Exception:
raise GenericError(
'Failed to get directory details for "{}".'.format(self.path))
else:
return json_data['filesystems'][0]
def get_dir_report(dir_path):
"""Build colored dir report using findmnt, returns str."""
output = []
result = run_program([
'findmnt',
'--output', 'SIZE,AVAIL,USED,FSTYPE,OPTIONS',
'--target', dir_path])
for line in result.stdout.decode().strip().splitlines():
if 'FSTYPE' in line:
output.append('{BLUE}{line}{CLEAR}'.format(line=line, **COLORS))
else:
output.append(line)
# Done
return '\n'.join(output)
def get_size_in_bytes(s):
"""Convert size string from lsblk string to bytes, returns int."""
s = re.sub(r'(\d+\.?\d*)\s*([KMGTB])B?', r'\1 \2B', s, re.IGNORECASE)
return convert_to_bytes(s)
@ -437,11 +492,11 @@ def get_recovery_scope_size(source):
source['Total Size'] = 0
if source['Children']:
for child in source['Children']:
child['Size'] = get_device_size_in_bytes(child['Details']['size'])
child['Size'] = get_size_in_bytes(child['Details']['size'])
source['Total Size'] += child['Size']
else:
# Whole dev
source['Size'] = get_device_size_in_bytes(source['Details']['size'])
source['Size'] = get_size_in_bytes(source['Details']['size'])
source['Total Size'] = source['Size']