commit
0a8db0a3d9
9 changed files with 130 additions and 41 deletions
|
|
@ -1,5 +1,7 @@
|
|||
# Wizard Kit: Functions - Backup
|
||||
|
||||
import ctypes
|
||||
|
||||
from functions.disk import *
|
||||
|
||||
# Regex
|
||||
|
|
@ -30,9 +32,41 @@ def backup_partition(disk, par):
|
|||
|
||||
def fix_path(path):
|
||||
"""Replace invalid filename characters with underscores."""
|
||||
return REGEX_BAD_PATH_NAMES.sub('_', path)
|
||||
local_drive = path[1:2] == ':'
|
||||
new_path = REGEX_BAD_PATH_NAMES.sub('_', path)
|
||||
if local_drive:
|
||||
new_path = '{}:{}'.format(new_path[0:1], new_path[2:])
|
||||
return new_path
|
||||
|
||||
def prep_disk_for_backup(destination, disk, ticket_number):
|
||||
def get_volume_display_name(mountpoint):
|
||||
"""Get display name from volume mountpoint and label, returns str."""
|
||||
name = mountpoint
|
||||
try:
|
||||
kernel32 = ctypes.windll.kernel32
|
||||
vol_name_buffer = ctypes.create_unicode_buffer(1024)
|
||||
fs_name_buffer = ctypes.create_unicode_buffer(1024)
|
||||
serial_number = None
|
||||
max_component_length = None
|
||||
file_system_flags = None
|
||||
|
||||
vol_info = kernel32.GetVolumeInformationW(
|
||||
ctypes.c_wchar_p(mountpoint),
|
||||
vol_name_buffer,
|
||||
ctypes.sizeof(vol_name_buffer),
|
||||
serial_number,
|
||||
max_component_length,
|
||||
file_system_flags,
|
||||
fs_name_buffer,
|
||||
ctypes.sizeof(fs_name_buffer)
|
||||
)
|
||||
|
||||
name = '{} "{}"'.format(name, vol_name_buffer.value)
|
||||
except:
|
||||
pass
|
||||
|
||||
return name
|
||||
|
||||
def prep_disk_for_backup(destination, disk, backup_prefix):
|
||||
"""Gather details about the disk and its partitions.
|
||||
|
||||
This includes partitions that can't be backed up,
|
||||
|
|
@ -71,10 +105,10 @@ def prep_disk_for_backup(destination, disk, ticket_number):
|
|||
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)
|
||||
destination['IP'], destination['Share'], backup_prefix)
|
||||
else:
|
||||
par['Image Path'] = r'{}:\{}'.format(
|
||||
ticket_number, destination['Letter'])
|
||||
destination['Letter'], backup_prefix)
|
||||
par['Image Path'] += r'\{}_{}.wim'.format(
|
||||
par['Number'], par['Image Name'])
|
||||
par['Image Path'] = fix_path(par['Image Path'])
|
||||
|
|
@ -114,7 +148,20 @@ def select_backup_destination(auto_select=True):
|
|||
actions = [
|
||||
{'Name': 'Main Menu', 'Letter': 'M'},
|
||||
]
|
||||
|
||||
|
||||
# Add local disks
|
||||
for d in psutil.disk_partitions():
|
||||
if re.search(r'^{}'.format(global_vars['Env']['SYSTEMDRIVE']), d.mountpoint, re.IGNORECASE):
|
||||
# Skip current OS drive
|
||||
pass
|
||||
elif 'fixed' in d.opts:
|
||||
# Skip DVD, etc
|
||||
destinations.append({
|
||||
'Name': 'Local Disk - {}'.format(
|
||||
get_volume_display_name(d.mountpoint)),
|
||||
'Letter': re.sub(r'^(\w):\\.*$', r'\1', d.mountpoint),
|
||||
})
|
||||
|
||||
# Size check
|
||||
for dest in destinations:
|
||||
if 'IP' in dest:
|
||||
|
|
|
|||
|
|
@ -190,12 +190,17 @@ def extract_item(item, filter='', silent=False):
|
|||
print_standard('Extracting "{item}"...'.format(item=item))
|
||||
try:
|
||||
run_program(cmd)
|
||||
except FileNotFoundError:
|
||||
if not silent:
|
||||
print_warning('WARNING: Archive not found')
|
||||
except subprocess.CalledProcessError:
|
||||
if not silent:
|
||||
print_warning('WARNING: Errors encountered while exctracting data')
|
||||
|
||||
def get_ticket_number():
|
||||
"""Get TicketNumber from user, save in LogDir, and return as str."""
|
||||
if not ENABLED_TICKET_NUMBERS:
|
||||
return None
|
||||
ticket_number = None
|
||||
while ticket_number is None:
|
||||
_input = input('Enter ticket number: ')
|
||||
|
|
@ -208,6 +213,15 @@ def get_ticket_number():
|
|||
f.write(ticket_number)
|
||||
return ticket_number
|
||||
|
||||
def get_simple_string(prompt='Enter string'):
|
||||
"""Get string from user (only alphanumeric/space chars) and return as str."""
|
||||
simple_string = None
|
||||
while simple_string is None:
|
||||
_input = input('{}: '.format(prompt))
|
||||
if re.match(r'^(\w|-| )+$', _input, re.ASCII):
|
||||
simple_string = _input.strip()
|
||||
return simple_string
|
||||
|
||||
def human_readable_size(size, decimals=0):
|
||||
"""Convert size in bytes to a human-readable format and return a str."""
|
||||
# Prep string formatting
|
||||
|
|
|
|||
|
|
@ -534,21 +534,21 @@ def select_destination(folder_path, prompt='Select destination'):
|
|||
|
||||
return path
|
||||
|
||||
def select_source(ticket_number):
|
||||
"""Select backup from those found on the BACKUP_SERVERS for the ticket."""
|
||||
def select_source(backup_prefix):
|
||||
"""Select backup from those found on the BACKUP_SERVERS matching the prefix."""
|
||||
selected_source = None
|
||||
local_sources = []
|
||||
remote_sources = []
|
||||
sources = []
|
||||
mount_backup_shares(read_write=False)
|
||||
|
||||
# Check for ticket folders on servers
|
||||
# Check for prefix folders on servers
|
||||
for server in BACKUP_SERVERS:
|
||||
if server['Mounted']:
|
||||
print_standard('Scanning {}...'.format(server['Name']))
|
||||
for d in os.scandir(r'\\{IP}\{Share}'.format(**server)):
|
||||
if (d.is_dir()
|
||||
and d.name.lower().startswith(ticket_number.lower())):
|
||||
and d.name.lower().startswith(backup_prefix.lower())):
|
||||
# Add folder to remote_sources
|
||||
remote_sources.append({
|
||||
'Name': '{:9}| File-Based: [DIR] {}'.format(
|
||||
|
|
@ -558,19 +558,19 @@ def select_source(ticket_number):
|
|||
'Source': d})
|
||||
|
||||
# Check for images and subfolders
|
||||
for ticket_path in remote_sources.copy():
|
||||
for item in os.scandir(ticket_path['Source'].path):
|
||||
for prefix_path in remote_sources.copy():
|
||||
for item in os.scandir(prefix_path['Source'].path):
|
||||
if item.is_dir():
|
||||
# Add folder to remote_sources
|
||||
remote_sources.append({
|
||||
'Name': r'{:9}| File-Based: [DIR] {}\{}'.format(
|
||||
ticket_path['Server']['Name'], # Server
|
||||
ticket_path['Source'].name, # Ticket folder
|
||||
prefix_path['Server']['Name'], # Server
|
||||
prefix_path['Source'].name, # Prefix folder
|
||||
item.name, # Sub-folder
|
||||
),
|
||||
'Server': ticket_path['Server'],
|
||||
'Server': prefix_path['Server'],
|
||||
'Sort': r'{}\{}'.format(
|
||||
ticket_path['Source'].name, # Ticket folder
|
||||
prefix_path['Source'].name, # Prefix folder
|
||||
item.name, # Sub-folder
|
||||
),
|
||||
'Source': item})
|
||||
|
|
@ -586,15 +586,15 @@ def select_source(ticket_number):
|
|||
remote_sources.append({
|
||||
'Disabled': bool(not is_valid_wim_file(subitem)),
|
||||
'Name': r'{:9}| Image-Based: {:>7} {}\{}\{}'.format(
|
||||
ticket_path['Server']['Name'], # Server
|
||||
prefix_path['Server']['Name'], # Server
|
||||
size, # Size (duh)
|
||||
ticket_path['Source'].name, # Ticket folder
|
||||
prefix_path['Source'].name, # Prefix folder
|
||||
item.name, # Sub-folder
|
||||
subitem.name, # Image file
|
||||
),
|
||||
'Server': ticket_path['Server'],
|
||||
'Server': prefix_path['Server'],
|
||||
'Sort': r'{}\{}\{}'.format(
|
||||
ticket_path['Source'].name, # Ticket folder
|
||||
prefix_path['Source'].name, # Prefix folder
|
||||
item.name, # Sub-folder
|
||||
subitem.name, # Image file
|
||||
),
|
||||
|
|
@ -608,14 +608,14 @@ def select_source(ticket_number):
|
|||
remote_sources.append({
|
||||
'Disabled': bool(not is_valid_wim_file(item)),
|
||||
'Name': r'{:9}| Image-Based: {:>7} {}\{}'.format(
|
||||
ticket_path['Server']['Name'], # Server
|
||||
prefix_path['Server']['Name'], # Server
|
||||
size, # Size (duh)
|
||||
ticket_path['Source'].name, # Ticket folder
|
||||
prefix_path['Source'].name, # Prefix folder
|
||||
item.name, # Image file
|
||||
),
|
||||
'Server': ticket_path['Server'],
|
||||
'Server': prefix_path['Server'],
|
||||
'Sort': r'{}\{}'.format(
|
||||
ticket_path['Source'].name, # Ticket folder
|
||||
prefix_path['Source'].name, # Prefix folder
|
||||
item.name, # Image file
|
||||
),
|
||||
'Source': item})
|
||||
|
|
@ -682,8 +682,8 @@ def select_source(ticket_number):
|
|||
else:
|
||||
selected_source = sources[int(selection)-1]['Source']
|
||||
else:
|
||||
print_error('ERROR: No backups found for ticket: {}.'.format(
|
||||
ticket_number))
|
||||
print_error('ERROR: No backups found using prefix: {}.'.format(
|
||||
backup_prefix))
|
||||
umount_backup_shares()
|
||||
pause("Press Enter to exit...")
|
||||
exit_script()
|
||||
|
|
|
|||
|
|
@ -125,9 +125,9 @@ def menu_diags(*args):
|
|||
if diag_modes[int(selection)-1]['Name'] != 'Quick drive test':
|
||||
# Save log for non-quick tests
|
||||
ticket_number = get_ticket_number()
|
||||
global_vars['LogDir'] = '{}/Tickets/{}'.format(
|
||||
global_vars['LogDir'] = '{}/Logs/{}'.format(
|
||||
global_vars['Env']['HOME'],
|
||||
ticket_number)
|
||||
ticket_number if ticket_number else global_vars['Date-Time'])
|
||||
os.makedirs(global_vars['LogDir'], exist_ok=True)
|
||||
global_vars['LogFile'] = '{}/Hardware Diagnostics.log'.format(
|
||||
global_vars['LogDir'])
|
||||
|
|
|
|||
|
|
@ -75,11 +75,22 @@ def menu_backup():
|
|||
}}
|
||||
set_title('{}: Backup Menu'.format(KIT_NAME_FULL))
|
||||
|
||||
# Set ticket Number
|
||||
# Set backup prefix
|
||||
clear_screen()
|
||||
print_standard('{}\n'.format(global_vars['Title']))
|
||||
ticket_number = get_ticket_number()
|
||||
if ENABLED_TICKET_NUMBERS:
|
||||
backup_prefix = ticket_number
|
||||
else:
|
||||
backup_prefix = get_simple_string(prompt='Enter backup name prefix')
|
||||
backup_prefix = backup_prefix.replace(' ', '_')
|
||||
|
||||
# Assign drive letters
|
||||
try_and_print(
|
||||
message = 'Assigning letters...',
|
||||
function = assign_volume_letters,
|
||||
other_results = other_results)
|
||||
|
||||
# Mount backup shares
|
||||
mount_backup_shares(read_write=True)
|
||||
|
||||
|
|
@ -87,10 +98,6 @@ def menu_backup():
|
|||
destination = select_backup_destination(auto_select=False)
|
||||
|
||||
# Scan disks
|
||||
try_and_print(
|
||||
message = 'Assigning letters...',
|
||||
function = assign_volume_letters,
|
||||
other_results = other_results)
|
||||
result = try_and_print(
|
||||
message = 'Getting disk info...',
|
||||
function = scan_disks,
|
||||
|
|
@ -107,12 +114,13 @@ def menu_backup():
|
|||
raise GenericAbort
|
||||
|
||||
# "Prep" disk
|
||||
prep_disk_for_backup(destination, disk, ticket_number)
|
||||
prep_disk_for_backup(destination, disk, backup_prefix)
|
||||
|
||||
# Display details for backup task
|
||||
clear_screen()
|
||||
print_info('Create Backup - Details:\n')
|
||||
show_data(message='Ticket:', data=ticket_number)
|
||||
if ENABLED_TICKET_NUMBERS:
|
||||
show_data(message='Ticket:', data=ticket_number)
|
||||
show_data(
|
||||
message = 'Source:',
|
||||
data = '[{}] ({}) {} {}'.format(
|
||||
|
|
@ -293,7 +301,8 @@ def menu_setup():
|
|||
# Display details for setup task
|
||||
clear_screen()
|
||||
print_info('Setup Windows - Details:\n')
|
||||
show_data(message='Ticket:', data=ticket_number)
|
||||
if ENABLED_TICKET_NUMBERS:
|
||||
show_data(message='Ticket:', data=ticket_number)
|
||||
show_data(message='Installing:', data=windows_version['Name'])
|
||||
show_data(
|
||||
message = 'Boot Method:',
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
# Features
|
||||
ENABLED_UPLOAD_DATA = False
|
||||
ENABLED_TICKET_NUMBERS = False
|
||||
|
||||
# STATIC VARIABLES (also used by BASH and BATCH files)
|
||||
## NOTE: There are no spaces around the = for easier parsing in BASH and BATCH
|
||||
|
|
|
|||
|
|
@ -29,8 +29,11 @@ if __name__ == '__main__':
|
|||
'FileNotFoundError': 'File not found',
|
||||
},
|
||||
'Warning': {}}
|
||||
print_info('Starting System Checklist for Ticket #{}\n'.format(
|
||||
ticket_number))
|
||||
if ENABLED_TICKET_NUMBERS:
|
||||
print_info('Starting System Checklist for Ticket #{}\n'.format(
|
||||
ticket_number))
|
||||
else:
|
||||
print_info('Starting System Checklist\n')
|
||||
|
||||
# Configure
|
||||
print_info('Configure')
|
||||
|
|
|
|||
|
|
@ -31,8 +31,11 @@ if __name__ == '__main__':
|
|||
'GenericRepair': 'Repaired',
|
||||
'UnsupportedOSError': 'Unsupported OS',
|
||||
}}
|
||||
print_info('Starting System Diagnostics for Ticket #{}\n'.format(
|
||||
ticket_number))
|
||||
if ENABLED_TICKET_NUMBERS:
|
||||
print_info('Starting System Diagnostics for Ticket #{}\n'.format(
|
||||
ticket_number))
|
||||
else:
|
||||
print_info('Starting System Diagnostics\n')
|
||||
|
||||
# Sanitize Environment
|
||||
print_info('Sanitizing Environment')
|
||||
|
|
|
|||
|
|
@ -18,17 +18,29 @@ if __name__ == '__main__':
|
|||
stay_awake()
|
||||
clear_screen()
|
||||
print_info('{}: User Data Transfer Tool\n'.format(KIT_NAME_FULL))
|
||||
|
||||
# Get backup name prefix
|
||||
ticket_number = get_ticket_number()
|
||||
if ENABLED_TICKET_NUMBERS:
|
||||
backup_prefix = ticket_number
|
||||
else:
|
||||
backup_prefix = get_simple_string(prompt='Enter backup name prefix')
|
||||
backup_prefix = backup_prefix.replace(' ', '_')
|
||||
|
||||
# Set destination
|
||||
folder_path = r'{}\Transfer'.format(KIT_NAME_SHORT)
|
||||
dest = select_destination(folder_path=folder_path,
|
||||
prompt='Which disk are we transferring to?')
|
||||
source = select_source(ticket_number)
|
||||
|
||||
# Set source items
|
||||
source = select_source(backup_prefix)
|
||||
items = scan_source(source, dest)
|
||||
|
||||
# Transfer
|
||||
clear_screen()
|
||||
print_info('Transfer Details:\n')
|
||||
show_data('Ticket:', ticket_number)
|
||||
if ENABLED_TICKET_NUMBERS:
|
||||
show_data('Ticket:', ticket_number)
|
||||
show_data('Source:', source.path)
|
||||
show_data('Destination:', dest)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue