def main(): InitLog() menu_actions = [ ("Install/Update", do_install), ("Shell", do_shell), ("Reboot", do_reboot), ("Shutdown", do_shutdown), ("Exit", do_exit), ] menu_items = [Dialog.FormLabel(x[0]) for x in menu_actions] menu_dict = {x[0]: x[1] for x in menu_actions} while True: menu = Dialog.Menu("Installation Menu", "", height=12, width=60, menu_items=menu_items) try: result = menu.result if result in menu_dict: try: menu_dict[result]() except InstallationError as e: LogIt("Got InstallationError {}".format(str(e))) try: Dialog.MessageBox( Title(), "An installation error has occurred.", height=5, width=40).run() except: pass except Dialog.DialogEscape: LogIt("Got a DialogEscape after calling {}".format(result)) except Dialog.DialogEscape as e: LogIt("Got DialogEscape for main menu, exiting") sys.exit(0) except SystemExit as e: LogIt("Got system exit {}".format(e.code)) sys.exit(e.code) except BaseException as e: print("Got exception {}".format(str(e)))
def do_install(): """ Do the UI for the install. This will either return, or raise an exception. DialogEscape means that the installer should start over. If we have any command-line arguments, let's handle them now """ arg_parser = argparse.ArgumentParser(description=Title(), prog="Installer") arg_parser.register('type', 'bool', lambda x: x.lower() in ["yes", "y", "true", "t"]) arg_parser.add_argument('-p', '--project', dest='project', default=Project(), help="Specify project (default {})".format( Project())) arg_parser.add_argument('-T', '--train', dest="train", help="Specify train name") arg_parser.add_argument('-U', '--url', dest="url", help="URL to use when fetching files") arg_parser.add_argument('-D', "--data", dest='data_dir', help='Path to /data prototype directory') arg_parser.add_argument('-M', '--manifest', dest='manifest', help="Path to manifest file") arg_parser.add_argument('-P', '--packages', dest='package_dir', help='Path to package files') arg_parser.add_argument( "-B", "--trampoline", dest='trampoline', default=True, type='bool', help="Run post-install scripts on reboot (default)") args = arg_parser.parse_args() if args: LogIt("Command line args: {}".format(args)) SetProject(args.project) try: validate_system() except ValidationError as e: LogIt("Could not validate system: {}".format(e.message)) Dialog.MessageBox( Title(), "\nSystem memory is too small. Minimum memory size is 8Gbytes", height=10, width=45).run() return if args.manifest: if os.path.exists(args.manifest): manifest_path = args.manifest else: Dialog.MessageBox( Title(), "A manifest file was specified on the command line, but does not exist. The manifest file specified was\n\n\t{}" .format(args.manifest), height=15, width=45).run() raise InstallationError( "Command-line manifest file {} does not exist".foramt( args.manifet)) else: manifest_path = "/.mount/{}-MANIFEST".format(Project()) if not os.path.exists(manifest_path): manifest_path = None package_dir = args.package_dir or "/.mount/{}/Packages".format(Project()) if not os.path.exists(package_dir): # This will be used later to see if we should try downloading package_dir = None # If we aren't given a URL, we can try to use the default url. # If we have a manifest file, we can figure out train; # if we don't have a train or manifest file, we're not okay. if (not manifest_path and not args.train): LogIt("Command-line URL {}, train {}, manifest {}".format( args.url, args.train, manifest_path)) box = Dialog.MessageBox(Title(), "", height=15, width=45) box.text = "Neither a manifest file nor train were specified" box.run() raise InstallationError("Incorrect command-line arguments given") conf = Configuration.SystemConfiguration() if conf is None: raise RuntimeError("No configuration?!") # Okay, if we're going to have to download, let's make an update server object if args.url: temp_update_server = Configuration.UpdateServer( name="Installer Server", url=args.url, signing=False) # This is SO cheating # It can't write to the file, but it does that after setting it. try: conf.AddUpdateServer(temp_update_server) except: pass conf.SetUpdateServer("Installer Server", save=False) # If we have a train, and no manifest file, let's grab one if manifest_path: manifest = Manifest.Manifest() try: manifest.LoadPath(manifest_path) except: manifest = None else: manifest = None if args.train and not manifest: try: status = Dialog.MessageBox( Title(), "Attempting to download manifest for train {}".format( args.train), height=15, width=30, wait=False) status.clear() status.run() except: pass try: manifest = conf.FindLatestManifest(train=args.train, require_signature=False) except: manifest = None # At this point, if we don't have a manifest, we can't do anything if manifest is None: LogIt("Could not load a manifest") text = "Despite valiant efforts, no manifest file could be located." Dialog.MessageBox(Title(), text, height=15, width=30).run() raise InstallationError("Unable to locate a manifest file") LogIt("Manifest: Version {}, Train {}, Sequence {}".format( manifest.Version(), manifest.Train(), manifest.Sequence())) do_upgrade = False boot_method = None disks = SelectDisks() if not disks: try: Dialog.MessageBox(Title(), "No suitable disks were found for installation", height=15, width=60).run() except: pass raise InstallationError("No disks selected for installation") if found_bootpool: if UpgradePossible(): text = """The {} installer can upgrade the existing {} installation. Do you want to upgrade?""".format(Project(), Project()) yesno = Dialog.YesNo("Perform Upgrade", text, height=12, width=60, yes_label="Upgrade", no_label="Do not Upgrade", default=True) do_upgrade = yesno.result else: Dialog.MessageBox("No upgrade possible", "").run() format_disks = True if found_bootpool: # If the selected disks are not the same as the existing boot-pool # disks, then we _will_ be formatting, and do not ask this question. disk_set = set([x.name for x in disks]) pool_set = set([Utils.Disk(x).name for x in found_bootpool.disks]) LogIt("disk_set = {}, pool_set = {}".format(disk_set, pool_set)) if pool_set == disk_set: yesno = Dialog.YesNo( Title(), "The {} installer can reformat the disk{}, or create a new boot environment.\nReformatting will erase all of your data" .format(Project(), "s" if len(disks) > 1 else ""), height=10, width=60, yes_label="Re-format", no_label="Create New BE", default=True) format_disks = yesno.result yesno.clear() if format_disks: # If there is already a freenas-boot, and we're not using all of # the disks in it, then this will cause problems. # If we made it this far, there is only one freenas-boot pool. if found_bootpool: pool_disks = [Utils.Disk(x) for x in found_bootpool.disks] disk_set = set([x.name for x in disks]) pool_set = set([x.name for x in pool_disks]) LogIt("disk_set = {}, pool_set = {}".format(disk_set, pool_set)) if not pool_set <= disk_set: # This means there would be two freenas-boot pools, which # is too much of a problem. yesno = Dialog.YesNo( Title(), "The existing boot pool contains disks that are not in the selected set of disks, which would result in errors. Select Start Over, or press Escape, otherwise the {} installer will destroy the existing pool" .format(Project()), width=60, yes_label="Destroy Pool", no_label="Start over", default=False) yesno.prompt += "\nSelected Disks: " + " ,".join( sorted([x.name for x in disks])) yesno.prompt += "\nPool Disks: " + " ,".join( sorted([x.name for x in pool_disks])) if yesno.result is False: raise Dialog.DialogEscape current_method = BootMethod() yesno = Dialog.YesNo( "Boot Method", "{} can boot via BIOS or (U)EFI. Selecting the wrong method can result in a non-bootable system" .format(Project()), height=10, width=60, yes_label="BIOS", no_label="(U)EFI", default=False if current_method == "efi" else True) if yesno.result is True: boot_method = "bios" else: boot_method = "efi" if not do_upgrade: # Ask for root password while True: password_fields = [ Dialog.FormItem( Dialog.FormLabel("Password:"******"", width=20, maximum_input=50, hidden=True)), Dialog.FormItem( Dialog.FormLabel("Confirm Password:"******"", width=20, maximum_input=50, hidden=True)), ] try: password_input = Dialog.Form( "Root Password", "Enter the root password. (Escape to quit, or select No Password)", width=60, height=15, cancel_label="No Password", form_height=10, form_items=password_fields) results = password_input.result if results and results[0].value.value != results[1].value.value: Dialog.MessageBox("Password Error", "Passwords did not match", width=35, ok_label="Try again").run() continue else: new_password = results[0].value.value if results else None break except Dialog.DialogEscape: try: Diallog.MessageBox("No Password Selected", "You have selected an empty password", height=7, width=35).run() except: pass new_password = None break # I'm not sure if this should be done here, or in Install() if package_dir is None: cache_dir = tempfile.mkdtemp() else: cache_dir = package_dir try: Utils.GetPackages(manifest, conf, cache_dir, interactive=True) except BaseException as e: LogIt("GetPackages raised an exception {}".format(str(e))) if package_dir is None: shutil.rmtree(cache_dir, ignore_errors=True) raise LogIt("Done getting packages?") # Let's confirm everything text = "The {} Installer will perform the following actions:\n\n".format( Project()) height = 10 if format_disks: text += "* The following disks will be reformatted, and all data lost:\n" height += 1 for disk in disks: text += "\t* {} {} ({}bytes)\n".format(disk.name, disk.description[:25], SmartSize(disk.size)) height += 1 if found_bootpool: text += "* The existing boot pool will be destroyed\n" height += 1 text += "* {} Booting\n".format( "BIOS" if boot_method is "bios" else "(U)EFI") else: text += "* A new Boot Environment will be created\n" height += 1 if do_upgrade: text += "* {} will be upgraded\n".format(Project()) else: text += "* {} will be freshly installed\n".format(Project()) height += 1 yesno.prompt = text yesno.default = False yesno = Dialog.YesNo("{} Installation Confirmation".format(Project()), text, height=min(15, height), width=60, default=False) if yesno.result == False: LogIt("Installation aborted at first confirmation") raise Dialog.DialogEscape if format_disks: yesno = Dialog.YesNo( "LAST CHANCE", "The {} installer will format the selected disks and all data on them will be erased.\n\nSelect Start Over or hit Escape to start over!" .format(Project()), height=10, width=50, yes_label="Continue", no_label="Start Over", default=False) if yesno.result is False: LogIt("Installation aborted at second confirmation") raise Dialog.DialogEscape # This may take a while, it turns out try: status = Dialog.MessageBox(Title(), "\nBeginning installation", height=7, width=25, wait=False) status.clear() status.run() except: pass # Okay, time to do the install with InstallationHandler() as handler: try: Install.Install( interactive=True, manifest=manifest, config=conf, package_directory=cache_dir, disks=disks if format_disks else None, efi=True if boot_method is "efi" else False, upgrade_from=found_bootpool if found_bootpool else None, upgrade=do_upgrade, data_dir=args.data_dir if args.data_dir else "/data", package_handler=handler.start_package, progress_handler=handler.package_update, password=None if do_upgrade else new_password, trampoline=args.trampoline) except BaseException as e: LogIt("Install got exception {}".format(str(e))) raise return