WizardKit/Scripts/functions/backup.py
Alan Mason 67f08c5042 updated prep_disk_for_backup()
* Partition['Image Path'] is set to the full destination path
  * (i.e. ['Image Path'] + '\\' + ['Image File']
  * Partition['Image File'] variable has been removed
* Simplified ['Backup Warnings'] section
* Added fix_path()
  * Replaces unsupported characters/strings with underscores
2017-11-30 19:45:59 -08:00

160 lines
6.1 KiB
Python

# Wizard Kit PE: Functions - Backup
from functions.common import *
# Regex
REGEX_BAD_PARTITION = re.compile(r'(RAW|Unknown)', re.IGNORECASE)
REGEX_BAD_PATH_NAMES = re.compile(
r'([<>:"/\\\|\?\*]'
r'|^(CON|PRN|AUX|NUL|COM\d*|LPT\d*)$)'
r'|^\s+'
r'|[\s\.]+$',
re.IGNORECASE)
def backup_partition(disk, partition):
if par['Image Exists'] or par['Number'] in disk['Bad Partitions']:
raise GenericAbort
cmd = [
global_vars['Tools']['wimlib-imagex'],
'capture'
'{}:\\'.format(par['Letter']),
par['Image Path'],
par['Image Name'], # Image name
par['Image Name'], # Image description
' --compress=none',
]
dest_dir = re.sub(r'(.*)\\.*$', r'\1', par['Image Path'], re.IGNORECASE)
os.makedirs(dest_dir, exist_ok=True)
run_program(cmd)
def fix_path(path):
return REGEX_BAD_PATH_NAMES.sub('_', path)
def prep_disk_for_backup(destination, disk, ticket_number):
disk['Clobber Risk'] = []
width = len(str(len(disk['Partitions'])))
# Get partition totals
disk['Bad Partitions'] = [par['Number'] for par in disk['Partitions']
if 'Letter' not in par or REGEX_BAD_PARTITION.search(par['FileSystem'])]
num_valid_partitions = len(disk['Partitions']) - len(disk['Bad Partitions'])
disk['Valid Partitions'] = num_valid_partitions
if disk['Valid Partitions'] <= 0:
print_error('ERROR: No partitions can be backed up for this disk')
raise GenericAbort
# Prep partitions
for par in disk['Partitions']:
display = 'Partition {num:>{width}}:\t{size} {fs}'.format(
num = par['Number'],
width = width,
size = par['Size'],
fs = par['FileSystem'])
if par['Number'] in disk['Bad Partitions']:
# Set display string using partition description & OS type
display = ' * {display}\t\t{q}{name}{q}\t{desc} ({os})'.format(
display = display,
q = '"' if par['Name'] != '' else '',
name = par['Name'],
desc = par['Description'],
os = par['OS'])
display = '{YELLOW}{display}{CLEAR}'.format(
display=display, **COLORS)
else:
# Update info for WIM capturing
par['Image Name'] = par['Name'] if par['Name'] else 'Unknown'
if 'IP' in destination:
par['Image Path'] = r'\\{}\{}\{}'.format(
destination['IP'], destination['Share'], ticket_number)
else:
par['Image Path'] = r'{}:\{}'.format(
ticket_number, destination['Letter'])
par['Image Path'] += r'\{}_{}.wim'.format(
par['Number'], par['Image Name'])
par['Image Path'] = fix_path(par['Image Path'])
# Check for existing backups
par['Image Exists'] = os.path.exists(par['Image Path'])
if par['Image Exists']:
disk['Clobber Risk'].append(par['Number'])
display = '{} + {}'.format(COLORS['BLUE'], display)
else:
display = '{} {}'.format(COLORS['CLEAR'], display)
# Append rest of Display String for valid/clobber partitions
display += ' (Used: {used})\t{q}{name}{q}{CLEAR}'.format(
used = par['Used Space'],
q = '"' if par['Name'] != '' else '',
name = par['Name'],
**COLORS)
# For all partitions
par['Display String'] = display
# Set description for bad partitions
warnings = '\n'
if disk['Bad Partitions']:
warnings += '{} * Unsupported filesystem{}\n'.format(
COLORS['YELLOW'], COLORS['CLEAR'])
if disk['Clobber Risk']:
warnings += '{} + Backup exists on {}{}\n'.format(
COLORS['BLUE'], destination['Name'], COLORS['CLEAR'])
if disk['Bad Partitions'] or disk['Clobber Risk']:
warnings += '\n{}Marked partition(s) will NOT be backed up.{}\n'.format(
COLORS['YELLOW'], COLORS['CLEAR'])
disk['Backup Warnings'] = warnings
def select_backup_destination():
# Build menu
dests = []
for server in BACKUP_SERVERS:
if server['Mounted']:
dests.append(server)
actions = [
{'Name': 'Main Menu', 'Letter': 'M'},
]
# Size check
for dest in dests:
if 'IP' in dest:
dest['Usage'] = shutil.disk_usage('\\\\{IP}\\{Share}'.format(**dest))
else:
dest['Usage'] = shutil.disk_usage('{Letter}:\\'.format(**dest))
dest['Free Space'] = human_readable_size(dest['Usage'].free)
dest['Display Name'] = '{Name} ({Free Space} available)'.format(**dest)
# Show menu or bail
if len(dests) > 0:
selection = menu_select('Where are we backing up to?', dests, actions)
if selection == 'M':
return None
else:
return dests[int(selection)-1]
else:
print_warning('No backup destinations found.')
return None
def verify_wim_backup(bin=None, par=None):
# Bail early
if bin is None:
raise Exception('bin path not specified.')
if par is None:
raise Exception('Partition not specified.')
# Verify hiding all output for quicker verification
print(' Partition {Number} Image...\t\t'.format(**par), end='', flush=True)
cmd = '{bin}\\wimlib\\wimlib-imagex verify "{Image Path}" --nocheck'.format(bin=bin, **par)
if not os.path.exists('{Image Path}'.format(**par)):
print_error('Missing.')
else:
try:
run_program(cmd)
print_success('OK.')
except subprocess.CalledProcessError as err:
print_error('Damaged.')
par['Error'] = par.get('Error', []) + err.stderr.decode().splitlines()
raise BackupError
if __name__ == '__main__':
print("This file is not meant to be called directly.")