class WindowStringReader(object): def __init__(self, maxy, maxx, height, width, field, confirmation_err_msg, echo_char, accepted_chars, validation_fn, conversion_fn, title, display_string, inputy, install_config, default_string=None, tab_enabled=False): self.title = title self.display_string = display_string self.install_config = install_config self.inputy = inputy self.width = width self.height = height self.maxx = maxx self.maxy = maxy self.startx = (self.maxx - self.width) // 2 self.starty = (self.maxy - self.height) // 2 self.tab_enabled = False self.can_go_next = True self.window = Window(self.height, self.width, self.maxy, self.maxx, self.title, True, tab_enabled=self.tab_enabled, position=1, can_go_next=self.can_go_next, read_text=self.can_go_next) self.read_text = ReadText(maxy, maxx, self.window.content_window(), self.inputy, install_config, field, confirmation_err_msg, echo_char, accepted_chars, validation_fn, conversion_fn, default_string, tab_enabled=self.tab_enabled) self.window.set_action_panel(self.read_text) self.window.addstr(0, 0, self.display_string) def get_user_string(self, params=None): return self.window.do_action()
class WindowStringReader(object): def __init__(self, maxy, maxx, height, width, field, confirmation_err_msg, echo_char, accepted_chars, validation_fn, conversion_fn, title, display_string, inputy, install_config, default_string=None, tab_enabled=False): self.title = title self.display_string = display_string self.install_config = install_config self.inputy = inputy self.width = width self.height = height self.maxx = maxx self.maxy = maxy self.startx = (self.maxx - self.width) // 2 self.starty = (self.maxy - self.height) // 2 self.tab_enabled = False self.can_go_next = True self.window = Window(self.height, self.width, self.maxy, self.maxx, self.title, True, tab_enabled=self.tab_enabled, position=1, can_go_next=self.can_go_next, read_text=self.can_go_next) self.read_text = ReadText(maxy, maxx, self.window.content_window(), self.inputy, install_config, field, confirmation_err_msg, echo_char, accepted_chars, validation_fn, conversion_fn, default_string, tab_enabled=self.tab_enabled) self.window.set_action_panel(self.read_text) self.window.addstr(0, 0, self.display_string) def get_user_string(self, params): return self.window.do_action()
class WindowStringReader(object): def __init__(self, maxy, maxx, height, width, ispassword, title, display_string, inputy, install_config): self.ispassword = ispassword self.title = title self.display_string = display_string self.inputy = inputy self.width = width self.height = height self.maxx = maxx self.maxy = maxy self.startx = (self.maxx - self.width) / 2 self.starty = (self.maxy - self.height) / 2 self.window = Window(self.height, self.width, self.maxy, self.maxx, self.title, True) self.read_text = ReadText(self.window.content_window(), self.inputy, install_config, self.ispassword) self.window.set_action_panel(self.read_text) self.window.addstr(0, 0, self.display_string) def get_user_string(self, params): return self.window.do_action()
def main(screen): # setup the basic syntax for text files text_syntax = SyntaxClass("basic") text_syntax.add("character", r".") text_syntax.add("word", r" ") text_syntax.add("sentance", r"\.") # make the main window mainwin = Window(screen, FileBuffer(open("README.markdown"))) mainwin.render() # the event loop while True: event = screen.getch() if event == ord("q"): break if event == ord("e"): mainwin.clear() if event == ord("r"): mainwin.render() if event == ord("f"): mainwin.addstr("X") if event == ord("w"): mainwin.rmove(0, -1) if event == ord("a"): mainwin.rmove(-1, 0) if event == ord("s"): mainwin.rmove(0, +1) if event == ord("d"): mainwin.rmove(+1, 0) if event == ord("x"): mainwin.here() def find(syntaxname): return lambda t, x, y: text_syntax.find(syntaxname, t, x, y) if event == ord("c"): mainwin.movef(find("character")) if event == ord("v"): mainwin.movef(find("word"))
class DiskPartitioner(object): def __init__(self, maxy, maxx): self.menu_items = [] self.maxx = maxx self.maxy = maxy self.win_width = 70 self.win_height = 17 self.win_starty = (self.maxy - self.win_height) / 2 self.win_startx = (self.maxx - self.win_width) / 2 self.menu_starty = self.win_starty + 10 # initialize the devices self.devices = Device.refresh_devices() self.items = [ ('Auto-partitioning - use entire disk', self.guided_partitions, None), ('Manual - not implemented!', self.manual_partitions, None), ] self.menu = Menu(self.menu_starty, self.maxx, self.items) self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Welcome to the Photon installer', True, self.menu) self.window.addstr(0, 0, 'First, we will setup your disks. \n\nYou can: \n\na) use auto-partitioning or\nb) you can do it manually.') def guided_partitions(self, params): return ActionResult(True, {'guided': True, 'devices': self.devices}) def manual_partitions(self, params): raise NameError('Manual partitioning not implemented') def display(self, params): return self.window.do_action()
class LinuxSelector(object): def __init__(self, maxy, maxx, install_config): self.install_config = install_config self.maxx = maxx self.maxy = maxy self.win_width = 60 self.win_height = 16 self.win_starty = (self.maxy - self.win_height) // 2 self.win_startx = (self.maxx - self.win_width) // 2 self.menu_starty = self.win_starty + 6 def set_linux_esx_installation(self, is_linux_esx): self.install_config['install_linux_esx'] = is_linux_esx return ActionResult(True, None) def set_linux_installation(self, selected_linux_package): self.install_config['linux_flavor'] = selected_linux_package return ActionResult(True, None) def create_available_linux_menu(self): linux_flavors = {"linux":"Generic", "linux-esx":"VMware hypervisor optimized", "linux-aws":"AWS optimized", "linux-secure":"Security hardened", "linux-rt":"Real Time"} self.menu_items = [] for flavor,menu_entry in linux_flavors.items(): if flavor in self.install_config['packages']: if flavor == "linux-esx" and not CommandUtils.is_vmware_virtualization(): continue self.menu_items.append((menu_entry, self.set_linux_installation, flavor)) if len(self.menu_items) == 1: self.install_config['linux_flavor'] = self.menu_items[0][2] self.host_menu = Menu(self.menu_starty, self.maxx, self.menu_items, default_selected=0, tab_enable=False) self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Select Linux kernel to install', True, tab_enabled=False, position=1, can_go_next=True) self.window.set_action_panel(self.host_menu) def display(self): if 'ostree' in self.install_config: return ActionResult(None, {"inactive_screen": True}) self.create_available_linux_menu() if len(self.menu_items) < 2: return ActionResult(None, {"inactive_screen": True}) self.window.addstr(0, 0, 'Which type of Linux kernel would you like to install?') return self.window.do_action()
def display(self): if self.setup_network and not self.do_setup_network(): return ActionResult(False, {'goBack': True}) file_source = {} accepted_chars = list(range(ord('a'), ord('z') + 1)) accepted_chars.extend(range(ord('A'), ord('Z') + 1)) accepted_chars.extend(range(ord('0'), ord('9') + 1)) accepted_chars.extend( [ord('-'), ord('_'), ord('.'), ord('~'), ord(':'), ord('/')]) result = WindowStringReader(self.maxy, self.maxx, 18, 78, 'url', None, None, accepted_chars, None, None, self.title, self.intro, 10, file_source, 'https://', True).get_user_string(None) if not result.success: return result status_window = Window(10, 70, self.maxy, self.maxx, 'Installing Photon', False) status_window.addstr(1, 0, 'Downloading file...') status_window.show_window() fd, temp_file = tempfile.mkstemp() result, msg = CommandUtils.wget( file_source['url'], temp_file, ask_fn=self.ask_proceed_unsafe_download) os.close(fd) if not result: status_window.adderror('Error: ' + msg + ' Press any key to go back...') status_window.content_window().getch() status_window.clearerror() status_window.hide_window() return ActionResult(False, {'goBack': True}) if 'additional_files' not in self.install_config: self.install_config['additional_files'] = [] copy = {temp_file: self.dest} self.install_config['additional_files'].append(copy) return ActionResult(True, None)
class License(object): def __init__(self, maxy, maxx, eula_file_path, display_title): self.maxx = maxx self.maxy = maxy self.win_width = maxx - 4 self.win_height = maxy - 4 self.win_starty = (self.maxy - self.win_height) // 2 self.win_startx = (self.maxx - self.win_width) // 2 self.text_starty = self.win_starty + 4 self.text_height = self.win_height - 6 self.text_width = self.win_width - 6 self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Welcome to the Photon installer', False) if eula_file_path: self.eula_file_path = eula_file_path else: self.eula_file_path = join(dirname(__file__), 'EULA.txt') if display_title: self.title = display_title else: self.title = 'VMWARE LICENSE AGREEMENT' def display(self): accept_decline_items = [('<Accept>', self.accept_function), ('<Cancel>', self.exit_function)] self.window.addstr(0, (self.win_width - len(self.title)) // 2, self.title) self.text_pane = TextPane(self.text_starty, self.maxx, self.text_width, self.eula_file_path, self.text_height, accept_decline_items) self.window.set_action_panel(self.text_pane) return self.window.do_action() def accept_function(self): return ActionResult(True, None) def exit_function(self): exit(0)
class License(object): def __init__(self, maxy, maxx): self.maxx = maxx self.maxy = maxy self.win_width = maxx - 4 self.win_height = maxy - 4 self.win_starty = (self.maxy - self.win_height) / 2 self.win_startx = (self.maxx - self.win_width) / 2 self.text_starty = self.win_starty + 4 self.text_height = self.win_height - 6 self.text_width = self.win_width - 6 self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Welcome to the Photon installer', False, items=[]) def display(self, params): accept_decline_items = [('<Accept>', self.accept_function), ('<Cancel>', self.exit_function)] title = 'VMWARE 1.0 LICENSE AGREEMENT' self.window.addstr(0, (self.win_width - len(title)) / 2, title) self.text_pane = TextPane(self.text_starty, self.maxx, self.text_width, "EULA.txt", self.text_height, accept_decline_items) self.window.set_action_panel(self.text_pane) return self.window.do_action() def accept_function(self): return ActionResult(True, None) def exit_function(self): exit(0)
class WindowStringReader(object): def __init__(self, maxy, maxx, height, width, ispassword, confirm_password, title, display_string, inputy, install_config): self.title = title self.display_string = display_string self.inputy = inputy self.width = width self.height = height self.maxx = maxx self.maxy = maxy self.startx = (self.maxx - self.width) / 2 self.starty = (self.maxy - self.height) / 2 self.window = Window(self.height, self.width, self.maxy, self.maxx, self.title, True) self.read_text = ReadText(maxy, maxx, self.window.content_window(), self.inputy, install_config, ispassword, confirm_password) self.window.set_action_panel(self.read_text) self.window.addstr(0, 0, self.display_string) def get_user_string(self, params): return self.window.do_action()
class DiskPartitioner(object): def __init__(self, maxy, maxx): self.menu_items = [] self.maxx = maxx self.maxy = maxy self.win_width = 70 self.win_height = 17 self.win_starty = (self.maxy - self.win_height) / 2 self.win_startx = (self.maxx - self.win_width) / 2 self.menu_starty = self.win_starty + 10 # initialize the devices self.devices = Device.refresh_devices() self.items = [ ('Auto-partitioning - use entire disk', self.guided_partitions, None), ('Manual - not implemented!', self.manual_partitions, None), ] self.menu = Menu(self.menu_starty, self.maxx, self.items) self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Welcome to the Photon installer', True, self.menu) self.window.addstr( 0, 0, 'First, we will setup your disks. \n\nYou can: \n\na) use auto-partitioning or\nb) you can do it manually.' ) def guided_partitions(self, params): return ActionResult(True, {'guided': True, 'devices': self.devices}) def manual_partitions(self, params): raise NameError('Manual partitioning not implemented') def display(self, params): return self.window.do_action()
class LinuxSelector(object): def __init__(self, maxy, maxx, install_config): self.install_config = install_config self.maxx = maxx self.maxy = maxy self.win_width = 60 self.win_height = 13 self.win_starty = (self.maxy - self.win_height) / 2 self.win_startx = (self.maxx - self.win_width) / 2 self.menu_starty = self.win_starty + 6 self.menu_items = [] self.menu_items.append(("1. Hypervisor optimized", self.set_linux_esx_installation, True)) self.menu_items.append(("2. Generic", self.set_linux_esx_installation, False)) self.host_menu = Menu(self.menu_starty, self.maxx, self.menu_items, default_selected=0, tab_enable=False) self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Select Linux kernel to install', True, items=[], tab_enabled=False, position=1, can_go_next=True) self.window.set_action_panel(self.host_menu) def set_linux_esx_installation(self, is_linux_esx): self.install_config['install_linux_esx'] = is_linux_esx return ActionResult(True, None) def display(self, params): self.window.addstr(0, 0, 'The installer has detected that you are installing') self.window.addstr(1, 0, 'Photon OS on a VMware hypervisor.') self.window.addstr(2, 0, 'Which type of Linux kernel would you like to install?') return self.window.do_action()
class LinuxSelector(object): def __init__(self, maxy, maxx, install_config): self.install_config = install_config self.maxx = maxx self.maxy = maxy self.win_width = 60 self.win_height = 13 self.win_starty = (self.maxy - self.win_height) // 2 self.win_startx = (self.maxx - self.win_width) // 2 self.menu_starty = self.win_starty + 6 self.menu_items = [] self.menu_items.append(("1. Hypervisor optimized", self.set_linux_esx_installation, True)) self.menu_items.append(("2. Generic", self.set_linux_esx_installation, False)) self.host_menu = Menu(self.menu_starty, self.maxx, self.menu_items, default_selected=0, tab_enable=False) self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Select Linux kernel to install', True, tab_enabled=False, position=1, can_go_next=True) self.window.set_action_panel(self.host_menu) def set_linux_esx_installation(self, is_linux_esx): self.install_config['install_linux_esx'] = is_linux_esx return ActionResult(True, None) def display(self, params): self.window.addstr(0, 0, 'The installer has detected that you are installing') self.window.addstr(1, 0, 'Photon OS on a VMware hypervisor.') self.window.addstr(2, 0, 'Which type of Linux kernel would you like to install?') return self.window.do_action()
class License(object): def __init__(self, maxy, maxx): self.maxx = maxx self.maxy = maxy self.win_width = maxx - 4 self.win_height = maxy - 4 self.win_starty = (self.maxy - self.win_height) // 2 self.win_startx = (self.maxx - self.win_width) // 2 self.text_starty = self.win_starty + 4 self.text_height = self.win_height - 6 self.text_width = self.win_width - 6 self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Welcome to the Photon installer', False) def display(self, params): accept_decline_items = [('<Accept>', self.accept_function), ('<Cancel>', self.exit_function)] title = 'VMWARE 2.0 LICENSE AGREEMENT' self.window.addstr(0, (self.win_width - len(title)) // 2, title) self.text_pane = TextPane(self.text_starty, self.maxx, self.text_width, "EULA.txt", self.text_height, accept_decline_items) self.window.set_action_panel(self.text_pane) return self.window.do_action() def accept_function(self): return ActionResult(True, None) def exit_function(self): exit(0)
class SelectDisk(object): def __init__(self, maxy, maxx, install_config): self.install_config = install_config self.menu_items = [] self.maxx = maxx self.maxy = maxy self.win_width = 70 self.win_height = 16 self.win_starty = (self.maxy - self.win_height) / 2 self.win_startx = (self.maxx - self.win_width) / 2 self.menu_starty = self.win_starty + 6 self.menu_height = 5 self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Setup your disk', True) self.devices = Device.refresh_devices() def guided_partitions(self, device_index): menu_height = 9 menu_width = 40 menu_starty = (self.maxy - menu_height) / 2 + 5 confrim_window = ConfirmWindow( menu_height, menu_width, self.maxy, self.maxx, menu_starty, 'This will erase the disk.\nAre you sure?') confirmed = confrim_window.do_action().result['yes'] if not confirmed: return ActionResult(confirmed, None) #self.install_config['disk'] = self.devices[device_index].path #return ActionResult(True, None) # Do the partitioning self.window.clearerror() json_ret = subprocess.check_output([ 'gpartedbin', 'defaultpartitions', self.devices[device_index].path ], stderr=open(os.devnull, 'w')) json_dct = json.loads(json_ret) if json_dct['success']: self.install_config['disk'] = json_dct['data'] else: self.window.adderror('Partitioning failed, you may try again') return ActionResult(json_dct['success'], None) def display(self, params): self.window.addstr( 0, 0, 'First, we will setup your disks.\n\nWe have detected {0} disks, choose disk to be auto-partitioned:' .format(len(self.devices))) self.disk_menu_items = [] # Fill in the menu items for index, device in enumerate(self.devices): #if index > 0: self.disk_menu_items.append( ('{2} - {1} MB @ {0}'.format(device.path, device.size, device.model), self.guided_partitions, index)) self.disk_menu = Menu(self.menu_starty, self.maxx, self.disk_menu_items, self.menu_height) self.window.set_action_panel(self.disk_menu) return self.window.do_action()
class Installer(object): def __init__(self, install_config, maxy=0, maxx=0, iso_installer=False, rpm_path="../stage/RPMS", log_path="../stage/LOGS"): self.install_config = install_config self.iso_installer = iso_installer self.rpm_path = rpm_path self.log_path = log_path self.mount_command = "./mk-mount-disk.sh" self.prepare_command = "./mk-prepare-system.sh" self.finalize_command = "./mk-finalize-system.sh" self.chroot_command = "./mk-run-chroot.sh" self.setup_grub_command = "./mk-setup-grub.sh" self.unmount_disk_command = "./mk-unmount-disk.sh" if 'working_directory' in self.install_config: self.working_directory = self.install_config['working_directory'] else: self.working_directory = "/mnt/photon-root" self.photon_root = self.working_directory + "/photon-chroot" self.restart_command = "shutdown" if self.iso_installer: self.output = open(os.devnull, 'w') else: self.output = None if self.iso_installer: #initializing windows self.maxy = maxy self.maxx = maxx self.height = 10 self.width = 75 self.progress_padding = 5 self.progress_width = self.width - self.progress_padding self.starty = (self.maxy - self.height) // 2 self.startx = (self.maxx - self.width) // 2 self.window = Window(self.height, self.width, self.maxy, self.maxx, 'Installing Photon', False, items=[]) self.progress_bar = ProgressBar( self.starty + 3, self.startx + self.progress_padding // 2, self.progress_width) signal.signal(signal.SIGINT, self.exit_gracefully) # This will be called if the installer interrupted by Ctrl+C or exception def exit_gracefully(self, signal, frame): if self.iso_installer: self.progress_bar.hide() self.window.addstr( 0, 0, 'Oops, Installer got interrupted.\n\nPress any key to get to the bash...' ) self.window.content_window().getch() modules.commons.dump(modules.commons.LOG_FILE_NAME) sys.exit(1) def install(self, params): try: return self.unsafe_install(params) except Exception as inst: if self.iso_installer: modules.commons.log(modules.commons.LOG_ERROR, repr(inst)) self.exit_gracefully(None, None) else: raise def unsafe_install(self, params): if self.iso_installer: self.window.show_window() self.progress_bar.initialize('Initializing installation...') self.progress_bar.show() #self.rpm_path = "https://dl.bintray.com/vmware/photon_release_1.0_TP2_x86_64" if self.rpm_path.startswith( "https://") or self.rpm_path.startswith("http://"): cmdoption = 's/baseurl.*/baseurl={}/g'.format( self.rpm_path.replace('/', '\/')) process = subprocess.Popen([ 'sed', '-i', cmdoption, '/etc/yum.repos.d/photon-iso.repo' ]) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_INFO, "Failed to reset repo") self.exit_gracefully(None, None) cmdoption = 's/cachedir=\/var/cachedir={}/g'.format( self.photon_root.replace('/', '\/')) process = subprocess.Popen( ['sed', '-i', cmdoption, '/etc/tdnf/tdnf.conf']) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_INFO, "Failed to reset tdnf cachedir") self.exit_gracefully(None, None) self.execute_modules(modules.commons.PRE_INSTALL) self.initialize_system() if self.iso_installer: self.adjust_packages_for_vmware_virt() selected_packages = self.install_config['packages'] state = 0 packages_to_install = {} total_size = 0 with open(modules.commons.TDNF_CMDLINE_FILE_NAME, "w") as tdnf_cmdline_file: tdnf_cmdline_file.write( "tdnf install --installroot {0} --nogpgcheck {1}".format( self.photon_root, " ".join(selected_packages))) with open(modules.commons.TDNF_LOG_FILE_NAME, "w") as tdnf_errlog: process = subprocess.Popen( ['tdnf', 'install'] + selected_packages + [ '--installroot', self.photon_root, '--nogpgcheck', '--assumeyes' ], stdout=subprocess.PIPE, stderr=tdnf_errlog) while True: output = process.stdout.readline() if output == '': retval = process.poll() if retval is not None: break if state == 0: if output == 'Installing:\n': state = 1 elif state == 1: #N A EVR Size(readable) Size(in bytes) if output == '\n': state = 2 self.progress_bar.update_num_items(total_size) else: info = output.split() package = '{0}-{1}.{2}'.format( info[0], info[2], info[1]) packages_to_install[package] = int(info[5]) total_size += int(info[5]) elif state == 2: if output == 'Downloading:\n': self.progress_bar.update_message('Preparing ...') state = 3 elif state == 3: self.progress_bar.update_message(output) if output == 'Running transaction\n': state = 4 else: modules.commons.log(modules.commons.LOG_INFO, "[tdnf] {0}".format(output)) prefix = 'Installing/Updating: ' if output.startswith(prefix): package = output[len(prefix):].rstrip('\n') self.progress_bar.increment( packages_to_install[package]) self.progress_bar.update_message(output) # 0 : succeed; 137 : package already installed; 65 : package not found in repo. if retval != 0 and retval != 137: modules.commons.log( modules.commons.LOG_ERROR, "Failed to install some packages, refer to {0}".format( modules.commons.TDNF_LOG_FILE_NAME)) self.exit_gracefully(None, None) else: #install packages rpms = [] for rpm in self.rpms_tobeinstalled: # We already installed the filesystem in the preparation if rpm['package'] == 'filesystem': continue rpms.append(rpm['filename']) return_value = self.install_package(rpms) if return_value != 0: self.exit_gracefully(None, None) if self.iso_installer: self.progress_bar.show_loading('Finalizing installation') if os.path.exists("/etc/resolv.conf"): shutil.copy("/etc/resolv.conf", self.photon_root + '/etc/.') self.finalize_system() if not self.install_config['iso_system']: # Execute post installation modules self.execute_modules(modules.commons.POST_INSTALL) if os.path.exists(modules.commons.KS_POST_INSTALL_LOG_FILE_NAME): shutil.copy(modules.commons.KS_POST_INSTALL_LOG_FILE_NAME, self.photon_root + '/var/log/') if self.iso_installer and os.path.isdir("/sys/firmware/efi"): self.install_config['boot'] = 'efi' # install grub if 'boot_partition_number' not in self.install_config['disk']: self.install_config['disk']['boot_partition_number'] = 1 try: if self.install_config['boot'] == 'bios': process = subprocess.Popen([ self.setup_grub_command, '-w', self.photon_root, "bios", self.install_config['disk']['disk'], self.install_config['disk']['root'], self.install_config['disk']['boot'], self.install_config['disk']['bootdirectory'], str(self.install_config['disk'] ['boot_partition_number']) ], stdout=self.output) elif self.install_config['boot'] == 'efi': process = subprocess.Popen([ self.setup_grub_command, '-w', self.photon_root, "efi", self.install_config['disk']['disk'], self.install_config['disk']['root'], self.install_config['disk']['boot'], self.install_config['disk']['bootdirectory'], str(self.install_config['disk'] ['boot_partition_number']) ], stdout=self.output) except: #install bios if variable is not set. process = subprocess.Popen([ self.setup_grub_command, '-w', self.photon_root, "bios", self.install_config['disk']['disk'], self.install_config['disk']['root'], self.install_config['disk']['boot'], self.install_config['disk']['bootdirectory'], str(self.install_config['disk']['boot_partition_number']) ], stdout=self.output) retval = process.wait() self.update_fstab() if os.path.exists(self.photon_root + '/etc/resolv.conf'): os.remove(self.photon_root + '/etc/resolv.conf') command = [self.unmount_disk_command, '-w', self.photon_root] if not self.install_config['iso_system']: command.extend(self.generate_partitions_param(reverse=True)) process = subprocess.Popen(command, stdout=self.output) retval = process.wait() if self.iso_installer: self.progress_bar.hide() self.window.addstr( 0, 0, 'Congratulations, Photon has been installed in {0} secs.\n\nPress any key to continue to boot...' .format(self.progress_bar.time_elapsed)) eject_cdrom = True if 'ui_install' in self.install_config: self.window.content_window().getch() if 'eject_cdrom' in self.install_config and not self.install_config[ 'eject_cdrom']: eject_cdrom = False if eject_cdrom: process = subprocess.Popen(['eject', '-r'], stdout=self.output) process.wait() return ActionResult(True, None) def copy_rpms(self): # prepare the RPMs list json_pkg_to_rpm_map = JsonWrapper( self.install_config["pkg_to_rpm_map_file"]) pkg_to_rpm_map = json_pkg_to_rpm_map.read() self.rpms_tobeinstalled = [] selected_packages = self.install_config['packages'] for pkg in selected_packages: if pkg in pkg_to_rpm_map: if not pkg_to_rpm_map[pkg]['rpm'] is None: name = pkg_to_rpm_map[pkg]['rpm'] basename = os.path.basename(name) self.rpms_tobeinstalled.append({ 'filename': basename, 'path': name, 'package': pkg }) # Copy the rpms for rpm in self.rpms_tobeinstalled: shutil.copy(rpm['path'], self.photon_root + '/RPMS/') def copy_files(self): # Make the photon_root directory if not exits process = subprocess.Popen(['mkdir', '-p', self.photon_root], stdout=self.output) retval = process.wait() # Copy the installer files process = subprocess.Popen( ['cp', '-r', "../installer", self.photon_root], stdout=self.output) retval = process.wait() # Create the rpms directory process = subprocess.Popen(['mkdir', '-p', self.photon_root + '/RPMS'], stdout=self.output) retval = process.wait() self.copy_rpms() def bind_installer(self): # Make the photon_root/installer directory if not exits process = subprocess.Popen( ['mkdir', '-p', os.path.join(self.photon_root, "installer")], stdout=self.output) retval = process.wait() # The function finalize_system will access the file /installer/mk-finalize-system.sh after chroot to photon_root. # Bind the /installer folder to self.photon_root/installer, so that after chroot to photon_root, # the file can still be accessed as /installer/mk-finalize-system.sh. process = subprocess.Popen([ 'mount', '--bind', '/installer', os.path.join(self.photon_root, "installer") ], stdout=self.output) retval = process.wait() def bind_repo_dir(self): rpm_cache_dir = self.photon_root + '/cache/tdnf/photon-iso/rpms' if self.rpm_path.startswith("https://") or self.rpm_path.startswith( "http://"): return if subprocess.call( ['mkdir', '-p', rpm_cache_dir]) != 0 or subprocess.call( ['mount', '--bind', self.rpm_path, rpm_cache_dir]) != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to bind cache rpms") self.exit_gracefully(None, None) def unbind_repo_dir(self): rpm_cache_dir = self.photon_root + '/cache/tdnf/photon-iso/rpms' if self.rpm_path.startswith("https://") or self.rpm_path.startswith( "http://"): return if subprocess.call(['umount', rpm_cache_dir]) != 0 or subprocess.call( ['rm', '-rf', rpm_cache_dir]) != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to unbind cache rpms") self.exit_gracefully(None, None) def update_fstab(self): with open(os.path.join(self.photon_root, "etc/fstab"), "w") as fstab_file: fstab_file.write("#system\tmnt-pt\ttype\toptions\tdump\tfsck\n") for partition in self.install_config['disk']['partitions']: options = 'defaults' dump = 1 fsck = 2 if 'mountpoint' in partition and partition['mountpoint'] == '/': options = options + ',barrier,noatime,noacl,data=ordered' fsck = 1 if partition['filesystem'] == 'swap': mountpoint = 'swap' dump = 0 fsck = 0 else: mountpoint = partition['mountpoint'] fstab_file.write("{}\t{}\t{}\t{}\t{}\t{}\n".format( partition['path'], mountpoint, partition['filesystem'], options, dump, fsck)) # Add the cdrom entry fstab_file.write( "/dev/cdrom\t/mnt/cdrom\tiso9660\tro,noauto\t0\t0\n") def generate_partitions_param(self, reverse=False): if reverse: step = -1 else: step = 1 params = [] for partition in self.install_config['disk']['partitions'][::step]: if partition["filesystem"] == "swap": continue params.extend([ '--partitionmountpoint', partition["path"], partition["mountpoint"] ]) return params def initialize_system(self): #Setup the disk if (not self.install_config['iso_system']): command = [self.mount_command, '-w', self.photon_root] command.extend(self.generate_partitions_param()) process = subprocess.Popen(command, stdout=self.output) retval = process.wait() if self.iso_installer: self.bind_installer() self.bind_repo_dir() process = subprocess.Popen( [self.prepare_command, '-w', self.photon_root, 'install'], stdout=self.output) retval = process.wait() else: self.copy_files() #Setup the filesystem basics process = subprocess.Popen( [self.prepare_command, '-w', self.photon_root], stdout=self.output) retval = process.wait() def finalize_system(self): #Setup the disk process = subprocess.Popen([ self.chroot_command, '-w', self.photon_root, self.finalize_command, '-w', self.photon_root ], stdout=self.output) retval = process.wait() if self.iso_installer: modules.commons.dump(modules.commons.LOG_FILE_NAME) shutil.copy(modules.commons.LOG_FILE_NAME, self.photon_root + '/var/log/') shutil.copy(modules.commons.TDNF_LOG_FILE_NAME, self.photon_root + '/var/log/') # unmount the installer directory process = subprocess.Popen( ['umount', os.path.join(self.photon_root, "installer")], stdout=self.output) retval = process.wait() # remove the installer directory process = subprocess.Popen( ['rm', '-rf', os.path.join(self.photon_root, "installer")], stdout=self.output) retval = process.wait() self.unbind_repo_dir() # Disable the swap file process = subprocess.Popen(['swapoff', '-a'], stdout=self.output) retval = process.wait() # remove the tdnf cache directory and the swapfile. process = subprocess.Popen( ['rm', '-rf', os.path.join(self.photon_root, "cache")], stdout=self.output) retval = process.wait() def install_package(self, rpm_file_names): rpms = set(rpm_file_names) rpm_paths = [] for root, dirs, files in os.walk(self.rpm_path): for f in files: if f in rpms: rpm_paths.append(os.path.join(root, f)) # --nodeps is for hosts which do not support rich dependencies rpm_params = [ '--nodeps', '--root', self.photon_root, '--dbpath', '/var/lib/rpm' ] if ('type' in self.install_config and (self.install_config['type'] in ['micro', 'minimal'])) or self.install_config['iso_system']: rpm_params.append('--excludedocs') modules.commons.log( modules.commons.LOG_INFO, "installing packages {0}, with params {1}".format( rpm_paths, rpm_params)) process = subprocess.Popen(['rpm', '-Uvh'] + rpm_params + rpm_paths, stderr=subprocess.STDOUT) return process.wait() def execute_modules(self, phase): modules_paths = glob.glob('modules/m_*.py') for mod_path in modules_paths: module = mod_path.replace('/', '.', 1) module = os.path.splitext(module)[0] try: __import__(module) mod = sys.modules[module] except ImportError: modules.commons.log(modules.commons.LOG_ERROR, 'Error importing module {}'.format(module)) continue # the module default is disabled if not hasattr(mod, 'enabled') or mod.enabled == False: modules.commons.log(modules.commons.LOG_INFO, "module {} is not enabled".format(module)) continue # check for the install phase if not hasattr(mod, 'install_phase'): modules.commons.log( modules.commons.LOG_ERROR, "Error: can not defind module {} phase".format(module)) continue if mod.install_phase != phase: modules.commons.log( modules.commons.LOG_INFO, "Skipping module {0} for phase {1}".format(module, phase)) continue if not hasattr(mod, 'execute'): modules.commons.log( modules.commons.LOG_ERROR, "Error: not able to execute module {}".format(module)) continue mod.execute(module, self.install_config, self.photon_root) def adjust_packages_for_vmware_virt(self): try: if self.install_config['install_linux_esx']: selected_packages = self.install_config['packages'] try: selected_packages.remove('linux') except ValueError: pass try: selected_packages.remove('initramfs') except ValueError: pass selected_packages.append('linux-esx') except KeyError: pass def run(self, command, comment=None): if comment != None: modules.commons.log(modules.commons.LOG_INFO, "Installer: {} ".format(comment)) self.progress_bar.update_loading_message(comment) modules.commons.log(modules.commons.LOG_INFO, "Installer: {} ".format(command)) process = subprocess.Popen([command], shell=True, stdout=subprocess.PIPE) out, err = process.communicate() if err != None and err != 0 and "systemd-tmpfiles" not in command: modules.commons.log( modules.commons.LOG_ERROR, "Installer: failed in {} with error code {}".format( command, err)) modules.commons.log(modules.commons.LOG_ERROR, out) self.exit_gracefully(None, None) return err
class Installer(object): def __init__(self, install_config, maxy=0, maxx=0, iso_installer=False, rpm_path="../stage/RPMS", log_path="../stage/LOGS", ks_config=None): self.install_config = install_config self.ks_config = ks_config self.iso_installer = iso_installer self.rpm_path = rpm_path self.log_path = log_path self.mount_command = "./mk-mount-disk.sh" self.prepare_command = "./mk-prepare-system.sh" self.finalize_command = "./mk-finalize-system.sh" self.install_package_command = "./mk-install-package.sh" self.chroot_command = "./mk-run-chroot.sh" self.setup_grub_command = "./mk-setup-grub.sh" self.unmount_disk_command = "./mk-unmount-disk.sh" if self.iso_installer: self.working_directory = "/mnt/photon-root" elif 'working_directory' in self.install_config: self.working_directory = self.install_config['working_directory'] else: self.working_directory = "/mnt/photon-root" self.photon_root = self.working_directory + "/photon-chroot" self.restart_command = "shutdown" if self.iso_installer: self.output = open(os.devnull, 'w') else: self.output = None if self.iso_installer: #initializing windows self.maxy = maxy self.maxx = maxx self.height = 10 self.width = 75 self.progress_padding = 5 self.progress_width = self.width - self.progress_padding self.starty = (self.maxy - self.height) / 2 self.startx = (self.maxx - self.width) / 2 self.window = Window(self.height, self.width, self.maxy, self.maxx, 'Installing Photon', False) self.progress_bar = ProgressBar( self.starty + 3, self.startx + self.progress_padding / 2, self.progress_width) signal.signal(signal.SIGINT, self.exit_gracefully) # This will be called if the installer interrupted by Ctrl+C or exception def exit_gracefully(self, signal, frame): if self.iso_installer: self.progress_bar.hide() self.window.addstr( 0, 0, 'Opps, Installer got interrupted.\n\nPress any key to get to the bash...' ) self.window.content_window().getch() modules.commons.dump(modules.commons.LOG_FILE_NAME) sys.exit(1) def install(self, params): try: return self.unsafe_install(params) except Exception as inst: if self.iso_installer: modules.commons.log(modules.commons.LOG_ERROR, repr(inst)) self.exit_gracefully(None, None) else: raise def unsafe_install(self, params): if self.iso_installer: self.window.show_window() self.progress_bar.initialize('Initializing installation...') self.progress_bar.show() #self.rpm_path = "https://dl.bintray.com/vmware/photon_release_1.0_TP2_x86_64" if self.rpm_path.startswith( "https://") or self.rpm_path.startswith("http://"): cmdoption = 's/baseurl.*/baseurl={}/g'.format( self.rpm_path.replace('/', '\/')) process = subprocess.Popen([ 'sed', '-i', cmdoption, '/etc/yum.repos.d/photon-iso.repo' ]) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_INFO, "Failed to reset repo") self.exit_gracefully(None, None) cmdoption = 's/cachedir=\/var/cachedir={}/g'.format( self.photon_root.replace('/', '\/')) process = subprocess.Popen( ['sed', '-i', cmdoption, '/etc/tdnf/tdnf.conf']) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_INFO, "Failed to reset tdnf cachedir") self.exit_gracefully(None, None) self.execute_modules(modules.commons.PRE_INSTALL) self.initialize_system() if self.iso_installer: self.get_size_of_packages() selected_packages = self.install_config['packages'] for package in selected_packages: self.progress_bar.update_message( 'Installing {0}...'.format(package)) process = subprocess.Popen([ 'tdnf', 'install', package, '--installroot', self.photon_root, '--nogpgcheck', '--assumeyes' ], stdout=self.output, stderr=subprocess.STDOUT) retval = process.wait() # 0 : succeed; 137 : package already installed; 65 : package not found in repo. if retval != 0 and retval != 137: modules.commons.log( modules.commons.LOG_ERROR, "Failed install: {} with error code {}".format( package, retval)) self.exit_gracefully(None, None) self.progress_bar.increment(self.size_of_packages[package]) else: #install packages for rpm in self.rpms_tobeinstalled: # We already installed the filesystem in the preparation if rpm['package'] == 'filesystem': continue return_value = self.install_package(rpm['filename']) if return_value != 0: self.exit_gracefully(None, None) if self.iso_installer: self.progress_bar.show_loading('Finalizing installation') self.finalize_system() if not self.install_config['iso_system']: # Execute post installation modules self.execute_modules(modules.commons.POST_INSTALL) # install grub try: if self.install_config['boot'] == 'bios': process = subprocess.Popen([ self.setup_grub_command, '-w', self.photon_root, "bios", self.install_config['disk']['disk'], self.install_config['disk']['root'], self.install_config['disk']['boot'], self.install_config['disk']['bootdirectory'] ], stdout=self.output) elif self.install_config['boot'] == 'efi': process = subprocess.Popen([ self.setup_grub_command, '-w', self.photon_root, "efi", self.install_config['disk']['disk'], self.install_config['disk']['root'], self.install_config['disk']['boot'], self.install_config['disk']['bootdirectory'] ], stdout=self.output) except: #install bios if variable is not set. process = subprocess.Popen([ self.setup_grub_command, '-w', self.photon_root, "bios", self.install_config['disk']['disk'], self.install_config['disk']['root'], self.install_config['disk']['boot'], self.install_config['disk']['bootdirectory'] ], stdout=self.output) retval = process.wait() self.update_fstab() command = [self.unmount_disk_command, '-w', self.photon_root] if not self.install_config['iso_system']: command.extend(self.generate_partitions_param(reverse=True)) process = subprocess.Popen(command, stdout=self.output) retval = process.wait() if self.iso_installer: self.progress_bar.hide() self.window.addstr( 0, 0, 'Congratulations, Photon has been installed in {0} secs.\n\nPress any key to continue to boot...' .format(self.progress_bar.time_elapsed)) if self.ks_config == None: self.window.content_window().getch() return ActionResult(True, None) def copy_rpms(self): # prepare the RPMs list rpms = [] for root, dirs, files in os.walk(self.rpm_path): for name in files: file = os.path.join(root, name) size = os.path.getsize(file) rpms.append({'filename': name, 'path': file, 'size': size}) self.rpms_tobeinstalled = [] selected_packages = self.install_config['packages'] for package in selected_packages: pattern = package + '-[0-9]*.rpm' pattern2 = package + '-[a-z][0-9]*.rpm' for rpm in rpms: if fnmatch.fnmatch(rpm['filename'], pattern) or fnmatch.fnmatch( rpm['filename'], pattern2): rpm['package'] = package self.rpms_tobeinstalled.append(rpm) break # Copy the rpms for rpm in self.rpms_tobeinstalled: shutil.copy(rpm['path'], self.photon_root + '/RPMS/') def copy_files(self): # Make the photon_root directory if not exits process = subprocess.Popen(['mkdir', '-p', self.photon_root], stdout=self.output) retval = process.wait() # Copy the installer files process = subprocess.Popen( ['cp', '-r', "../installer", self.photon_root], stdout=self.output) retval = process.wait() # Create the rpms directory process = subprocess.Popen(['mkdir', '-p', self.photon_root + '/RPMS'], stdout=self.output) retval = process.wait() self.copy_rpms() def bind_installer(self): # Make the photon_root/installer directory if not exits process = subprocess.Popen( ['mkdir', '-p', os.path.join(self.photon_root, "installer")], stdout=self.output) retval = process.wait() # The function finalize_system will access the file /installer/mk-finalize-system.sh after chroot to photon_root. # Bind the /installer folder to self.photon_root/installer, so that after chroot to photon_root, # the file can still be accessed as /installer/mk-finalize-system.sh. process = subprocess.Popen([ 'mount', '--bind', '/installer', os.path.join(self.photon_root, "installer") ], stdout=self.output) retval = process.wait() def update_fstab(self): fstab_file = open(os.path.join(self.photon_root, "etc/fstab"), "w") fstab_file.write("#system\tmnt-pt\ttype\toptions\tdump\tfsck\n") for partition in self.install_config['disk']['partitions']: options = 'defaults' dump = 1 fsck = 2 if 'mountpoint' in partition and partition['mountpoint'] == '/': fsck = 1 if partition['filesystem'] == 'swap': mountpoint = 'swap' dump = 0 fsck = 0 else: mountpoint = partition['mountpoint'] fstab_file.write("{}\t{}\t{}\t{}\t{}\t{}\n".format( partition['path'], mountpoint, partition['filesystem'], options, dump, fsck)) # Add the cdrom entry fstab_file.write("/dev/cdrom\t/mnt/cdrom\tiso9660\tro,noauto\t0\t0\n") fstab_file.close() def generate_partitions_param(self, reverse=False): if reverse: step = -1 else: step = 1 params = [] for partition in self.install_config['disk']['partitions'][::step]: if partition["filesystem"] == "swap": continue params.extend([ '--partitionmountpoint', partition["path"], partition["mountpoint"] ]) return params def initialize_system(self): #Setup the disk if (not self.install_config['iso_system']): command = [self.mount_command, '-w', self.photon_root] command.extend(self.generate_partitions_param()) process = subprocess.Popen(command, stdout=self.output) retval = process.wait() if self.iso_installer: self.bind_installer() process = subprocess.Popen( [self.prepare_command, '-w', self.photon_root, 'install'], stdout=self.output) retval = process.wait() else: self.copy_files() #Setup the filesystem basics process = subprocess.Popen( [self.prepare_command, '-w', self.photon_root], stdout=self.output) retval = process.wait() def finalize_system(self): #Setup the disk process = subprocess.Popen([ self.chroot_command, '-w', self.photon_root, self.finalize_command, '-w', self.photon_root ], stdout=self.output) retval = process.wait() if self.iso_installer: modules.commons.dump(modules.commons.LOG_FILE_NAME) shutil.copy(modules.commons.LOG_FILE_NAME, self.photon_root + '/var/log/') # unmount the installer directory process = subprocess.Popen( ['umount', os.path.join(self.photon_root, "installer")], stdout=self.output) retval = process.wait() # remove the installer directory process = subprocess.Popen( ['rm', '-rf', os.path.join(self.photon_root, "installer")], stdout=self.output) retval = process.wait() # Disable the swap file process = subprocess.Popen(['swapoff', '-a'], stdout=self.output) retval = process.wait() # remove the tdnf cache directory and the swapfile. process = subprocess.Popen( ['rm', '-rf', os.path.join(self.photon_root, "cache")], stdout=self.output) def install_package(self, package_name): rpm_params = '' os.environ["RPMROOT"] = self.rpm_path rpm_params = rpm_params + ' --force ' rpm_params = rpm_params + ' --root ' + self.photon_root rpm_params = rpm_params + ' --dbpath /var/lib/rpm ' if ('type' in self.install_config and (self.install_config['type'] in ['micro', 'minimal'])) or self.install_config['iso_system']: rpm_params = rpm_params + ' --excludedocs ' process = subprocess.Popen([ self.install_package_command, '-w', self.photon_root, package_name, rpm_params ], stdout=self.output) return process.wait() def execute_modules(self, phase): modules_paths = glob.glob('modules/m_*.py') for mod_path in modules_paths: module = mod_path.replace('/', '.', 1) module = os.path.splitext(module)[0] try: __import__(module) mod = sys.modules[module] except ImportError: modules.commons.log(modules.commons.LOG_ERROR, 'Error importing module {}'.format(module)) continue # the module default is disabled if not hasattr(mod, 'enabled') or mod.enabled == False: modules.commons.log(modules.commons.LOG_INFO, "module {} is not enabled".format(module)) continue # check for the install phase if not hasattr(mod, 'install_phase'): modules.commons.log( modules.commons.LOG_ERROR, "Error: can not defind module {} phase".format(module)) continue if mod.install_phase != phase: modules.commons.log( modules.commons.LOG_INFO, "Skipping module {0} for phase {1}".format(module, phase)) continue if not hasattr(mod, 'execute'): modules.commons.log( modules.commons.LOG_ERROR, "Error: not able to execute module {}".format(module)) continue mod.execute(module, self.ks_config, self.install_config, self.photon_root) def get_install_size_of_a_package(self, name_size_pairs, package): modules.commons.log(modules.commons.LOG_INFO, "Find the install size of: {} ".format(package)) for index, name in enumerate(name_size_pairs, start=0): if name[name.find(":") + 1:].strip() == package.strip(): item = name_size_pairs[index + 1] size = item[item.find("(") + 1:item.find(")")] return int(size) raise LookupError( "Cannot find package {} in the repo.".format(package)) def get_size_of_packages(self): #call tdnf info to get the install size of all the packages. process = subprocess.Popen( ['tdnf', 'info', '--installroot', self.photon_root], stdout=subprocess.PIPE) out, err = process.communicate() if err != None and err != 0: modules.commons.log( modules.commons.LOG_ERROR, "Failed to get infomation from : {} with error code {}".format( package, err)) name_size_pairs = re.findall("(?:^Name.*$)|(?:^.*Install Size.*$)", out, re.M) selected_packages = self.install_config['packages'] self.size_of_packages = {} progressbar_num_items = 0 for package in selected_packages: size = self.get_install_size_of_a_package(name_size_pairs, package) progressbar_num_items += size self.size_of_packages[package] = size self.progress_bar.update_num_items(progressbar_num_items) def run(self, command, comment=None): if comment != None: modules.commons.log(modules.commons.LOG_INFO, "Installer: {} ".format(comment)) self.progress_bar.update_loading_message(comment) modules.commons.log(modules.commons.LOG_INFO, "Installer: {} ".format(command)) process = subprocess.Popen([command], shell=True, stdout=subprocess.PIPE) out, err = process.communicate() if err != None and err != 0 and "systemd-tmpfiles" not in command: modules.commons.log( modules.commons.LOG_ERROR, "Installer: failed in {} with error code {}".format( command, err)) modules.commons.log(modules.commons.LOG_ERROR, out) self.exit_gracefully(None, None) return err
class Installer(object): """ Photon installer """ mount_command = os.path.dirname(__file__)+"/mk-mount-disk.sh" finalize_command = "./mk-finalize-system.sh" chroot_command = os.path.dirname(__file__)+"/mk-run-chroot.sh" unmount_disk_command = os.path.dirname(__file__)+"/mk-unmount-disk.sh" def __init__(self, install_config, maxy=0, maxx=0, iso_installer=False, rpm_path=os.path.dirname(__file__)+"/../stage/RPMS", log_path=os.path.dirname(__file__)+"/../stage/LOGS", log_level="info"): self.install_config = install_config self.install_config['iso_installer'] = iso_installer self.rpm_path = rpm_path self.log_path = log_path self.log_level = log_level if 'working_directory' in self.install_config: self.working_directory = self.install_config['working_directory'] else: self.working_directory = "/mnt/photon-root" if 'prepare_script' in self.install_config: self.prepare_command = self.install_config['prepare_script'] else: self.prepare_command = os.path.dirname(__file__)+"/mk-prepare-system.sh" self.photon_root = self.working_directory + "/photon-chroot" if 'setup_grub_script' in self.install_config: self.setup_grub_command = self.install_config['setup_grub_script'] else: self.setup_grub_command = os.path.dirname(__file__)+"/mk-setup-grub.sh" self.rpms_tobeinstalled = None if self.install_config['iso_installer']: self.output = open(os.devnull, 'w') #initializing windows height = 10 width = 75 progress_padding = 5 progress_width = width - progress_padding starty = (maxy - height) // 2 startx = (maxx - width) // 2 self.window = Window(height, width, maxy, maxx, 'Installing Photon', False) self.progress_bar = ProgressBar(starty + 3, startx + progress_padding // 2, progress_width) else: self.output = None signal.signal(signal.SIGINT, self.exit_gracefully) def install(self, params): """ Install photon system and handle exception """ del params try: return self._unsafe_install() except Exception as inst: if self.install_config['iso_installer']: modules.commons.log(modules.commons.LOG_ERROR, repr(inst)) self.exit_gracefully(None, None) else: raise def _unsafe_install(self): """ Install photon system """ self._setup_install_repo() self._initialize_system() self._install_packages() self._enable_network_in_chroot() self._finalize_system() self._disable_network_in_chroot() self._cleanup_and_exit() return ActionResult(True, None) def exit_gracefully(self, signal1, frame1): """ This will be called if the installer interrupted by Ctrl+C, exception or other failures """ del signal1 del frame1 if self.install_config['iso_installer']: self.progress_bar.hide() self.window.addstr(0, 0, 'Oops, Installer got interrupted.\n\n' + 'Press any key to get to the bash...') self.window.content_window().getch() modules.commons.dump(modules.commons.LOG_FILE_NAME) sys.exit(1) def _cleanup_and_exit(self): """ Unmount the disk, eject cd and exit """ command = [Installer.unmount_disk_command, '-w', self.photon_root] if not self.install_config['iso_system']: command.extend(self._generate_partitions_param(reverse=True)) process = subprocess.Popen(command, stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Failed to unmount disks") if self.install_config['iso_installer']: self.progress_bar.hide() self.window.addstr(0, 0, 'Congratulations, Photon has been installed in {0} secs.\n\n' 'Press any key to continue to boot...' .format(self.progress_bar.time_elapsed)) self._eject_cdrom() def _copy_rpms(self): """ Prepare RPM list and copy rpms """ # prepare the RPMs list json_pkg_to_rpm_map = JsonWrapper(self.install_config["pkg_to_rpm_map_file"]) pkg_to_rpm_map = json_pkg_to_rpm_map.read() self.rpms_tobeinstalled = [] selected_packages = self.install_config['packages'] for pkg in selected_packages: versionindex = pkg.rfind("-") if versionindex == -1: raise Exception("Invalid pkg name: " + pkg) package = pkg[:versionindex] if pkg in pkg_to_rpm_map: if pkg_to_rpm_map[pkg]['rpm'] is not None: name = pkg_to_rpm_map[pkg]['rpm'] basename = os.path.basename(name) self.rpms_tobeinstalled.append({'filename': basename, 'path': name, 'package' : package}) # Copy the rpms for rpm in self.rpms_tobeinstalled: shutil.copy(rpm['path'], self.photon_root + '/RPMS/') def _copy_files(self): """ Copy the rpm files and instal scripts. """ # Make the photon_root directory if not exits process = subprocess.Popen(['mkdir', '-p', self.photon_root], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to create the root directory") self.exit_gracefully(None, None) # Copy the installer files process = subprocess.Popen(['cp', '-r', os.path.dirname(__file__), self.photon_root], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to copy install scripts") self.exit_gracefully(None, None) # Create the rpms directory process = subprocess.Popen(['mkdir', '-p', self.photon_root + '/RPMS'], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to create the rpms directory") self.exit_gracefully(None, None) self._copy_rpms() def _bind_installer(self): """ Make the photon_root/installer directory if not exits The function finalize_system will access the file /installer/mk-finalize-system.sh after chroot to photon_root. Bind the /installer folder to self.photon_root/installer, so that after chroot to photon_root, the file can still be accessed as /installer/mk-finalize-system.sh. """ # Make the photon_root/installer directory if not exits if(subprocess.call(['mkdir', '-p', os.path.join(self.photon_root, "installer")]) != 0 or subprocess.call(['mount', '--bind', '/installer', os.path.join(self.photon_root, "installer")]) != 0): modules.commons.log(modules.commons.LOG_ERROR, "Fail to bind installer") self.exit_gracefully(None, None) def _unbind_installer(self): # unmount the installer directory process = subprocess.Popen(['umount', os.path.join(self.photon_root, "installer")], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to unbind the installer directory") # remove the installer directory process = subprocess.Popen(['rm', '-rf', os.path.join(self.photon_root, "installer")], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to remove the installer directory") def _bind_repo_dir(self): """ Bind repo dir for tdnf installation """ rpm_cache_dir = self.photon_root + '/cache/tdnf/photon-iso/rpms' if self.rpm_path.startswith("https://") or self.rpm_path.startswith("http://"): return if (subprocess.call(['mkdir', '-p', rpm_cache_dir]) != 0 or subprocess.call(['mount', '--bind', self.rpm_path, rpm_cache_dir]) != 0): modules.commons.log(modules.commons.LOG_ERROR, "Fail to bind cache rpms") self.exit_gracefully(None, None) def _unbind_repo_dir(self): """ Unbind repo dir after installation """ rpm_cache_dir = self.photon_root + '/cache/tdnf/photon-iso/rpms' if self.rpm_path.startswith("https://") or self.rpm_path.startswith("http://"): return if (subprocess.call(['umount', rpm_cache_dir]) != 0 or subprocess.call(['rm', '-rf', rpm_cache_dir]) != 0): modules.commons.log(modules.commons.LOG_ERROR, "Fail to unbind cache rpms") self.exit_gracefully(None, None) def _update_fstab(self): """ update fstab """ with open(os.path.join(self.photon_root, "etc/fstab"), "w") as fstab_file: fstab_file.write("#system\tmnt-pt\ttype\toptions\tdump\tfsck\n") for partition in self.install_config['disk']['partitions']: options = 'defaults' dump = 1 fsck = 2 if 'mountpoint' in partition and partition['mountpoint'] == '/': options = options + ',barrier,noatime,noacl,data=ordered' fsck = 1 if partition['filesystem'] == 'swap': mountpoint = 'swap' dump = 0 fsck = 0 else: mountpoint = partition['mountpoint'] fstab_file.write("{}\t{}\t{}\t{}\t{}\t{}\n".format( partition['path'], mountpoint, partition['filesystem'], options, dump, fsck )) # Add the cdrom entry fstab_file.write("/dev/cdrom\t/mnt/cdrom\tiso9660\tro,noauto\t0\t0\n") def _generate_partitions_param(self, reverse=False): """ Generate partition param for mount command """ if reverse: step = -1 else: step = 1 params = [] for partition in self.install_config['disk']['partitions'][::step]: if partition["filesystem"] == "swap": continue params.extend(['--partitionmountpoint', partition["path"], partition["mountpoint"]]) return params def _initialize_system(self): """ Prepare the system to install photon """ #Setup the disk if not self.install_config['iso_system']: command = [Installer.mount_command, '-w', self.photon_root] command.extend(self._generate_partitions_param()) process = subprocess.Popen(command, stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_INFO, "Failed to setup the disk for installation") self.exit_gracefully(None, None) if self.install_config['iso_installer']: self._bind_installer() self._bind_repo_dir() process = subprocess.Popen([self.prepare_command, '-w', self.photon_root, 'install'], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_INFO, "Failed to bind the installer and repo needed by tdnf") self.exit_gracefully(None, None) else: self._copy_files() #Setup the filesystem basics process = subprocess.Popen([self.prepare_command, '-w', self.photon_root], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_INFO, "Failed to setup the file systems basics") self.exit_gracefully(None, None) def _finalize_system(self): """ Finalize the system after the installation """ #Setup the disk process = subprocess.Popen([Installer.chroot_command, '-w', self.photon_root, Installer.finalize_command, '-w', self.photon_root], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to setup the target system after the installation") if self.install_config['iso_installer']: modules.commons.dump(modules.commons.LOG_FILE_NAME) shutil.copy(modules.commons.LOG_FILE_NAME, self.photon_root + '/var/log/') shutil.copy(modules.commons.TDNF_LOG_FILE_NAME, self.photon_root + '/var/log/') self._unbind_installer() self._unbind_repo_dir() # Disable the swap file process = subprocess.Popen(['swapoff', '-a'], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to swapoff") # remove the tdnf cache directory and the swapfile. process = subprocess.Popen(['rm', '-rf', os.path.join(self.photon_root, "cache")], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to remove the cache") if not self.install_config['iso_system']: # Execute post installation modules self._execute_modules(modules.commons.POST_INSTALL) if os.path.exists(modules.commons.KS_POST_INSTALL_LOG_FILE_NAME): shutil.copy(modules.commons.KS_POST_INSTALL_LOG_FILE_NAME, self.photon_root + '/var/log/') if self.install_config['iso_installer'] and os.path.isdir("/sys/firmware/efi"): self.install_config['boot'] = 'efi' # install grub if 'boot_partition_number' not in self.install_config['disk']: self.install_config['disk']['boot_partition_number'] = 1 try: if self.install_config['boot'] == 'bios': process = subprocess.Popen( [self.setup_grub_command, '-w', self.photon_root, "bios", self.install_config['disk']['disk'], self.install_config['disk']['root'], self.install_config['disk']['boot'], self.install_config['disk']['bootdirectory'], str(self.install_config['disk']['boot_partition_number'])], stdout=self.output) elif self.install_config['boot'] == 'efi': process = subprocess.Popen( [self.setup_grub_command, '-w', self.photon_root, "efi", self.install_config['disk']['disk'], self.install_config['disk']['root'], self.install_config['disk']['boot'], self.install_config['disk']['bootdirectory'], str(self.install_config['disk']['boot_partition_number'])], stdout=self.output) except: #install bios if variable is not set. process = subprocess.Popen( [self.setup_grub_command, '-w', self.photon_root, "bios", self.install_config['disk']['disk'], self.install_config['disk']['root'], self.install_config['disk']['boot'], self.install_config['disk']['bootdirectory'], str(self.install_config['disk']['boot_partition_number'])], stdout=self.output) retval = process.wait() if retval != 0: raise Exception("Bootloader (grub2) setup failed") self._update_fstab() if not self.install_config['iso_installer']: process = subprocess.Popen(['rm', '-rf', os.path.join(self.photon_root, "installer")], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to remove the installer directory") process = subprocess.Popen(['rm', '-rf', os.path.join(self.photon_root, "RPMS")], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to remove the input rpms directory") def _execute_modules(self, phase): """ Execute the scripts in the modules folder """ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "modules"))) modules_paths = glob.glob(os.path.abspath(os.path.join(os.path.dirname(__file__), 'modules')) + '/m_*.py') for mod_path in modules_paths: module = os.path.splitext(os.path.basename(mod_path))[0] try: __import__(module) mod = sys.modules[module] except ImportError: modules.commons.log(modules.commons.LOG_ERROR, 'Error importing module {}'.format(module)) continue # the module default is disabled if not hasattr(mod, 'enabled') or mod.enabled is False: modules.commons.log(modules.commons.LOG_INFO, "module {} is not enabled".format(module)) continue # check for the install phase if not hasattr(mod, 'install_phase'): modules.commons.log(modules.commons.LOG_ERROR, "Error: can not defind module {} phase".format(module)) continue if mod.install_phase != phase: modules.commons.log(modules.commons.LOG_INFO, "Skipping module {0} for phase {1}".format(module, phase)) continue if not hasattr(mod, 'execute'): modules.commons.log(modules.commons.LOG_ERROR, "Error: not able to execute module {}".format(module)) continue mod.execute(self.install_config, self.photon_root) def _adjust_packages_for_vmware_virt(self): """ Install linux_esx on Vmware virtual machine if requested """ try: if self.install_config['install_linux_esx']: regex = re.compile(r'^linux-[0-9]|^initramfs-[0-9]') self.install_config['packages'] = [x for x in self.install_config['packages'] if not regex.search(x)] self.install_config['packages'].append('linux-esx') except KeyError: pass def _setup_install_repo(self): """ Setup the tdnf repo for installation """ if self.install_config['iso_installer']: self.window.show_window() self.progress_bar.initialize('Initializing installation...') self.progress_bar.show() #self.rpm_path = "https://dl.bintray.com/vmware/photon_release_1.0_TP2_x86_64" if self.rpm_path.startswith("https://") or self.rpm_path.startswith("http://"): cmdoption = 's/baseurl.*/baseurl={}/g'.format(self.rpm_path.replace('/', r'\/')) process = subprocess.Popen(['sed', '-i', cmdoption, '/etc/yum.repos.d/photon-iso.repo']) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_INFO, "Failed to reset repo") self.exit_gracefully(None, None) cmdoption = (r's/cachedir=\/var/cachedir={}/g' .format(self.photon_root.replace('/', r'\/'))) process = subprocess.Popen(['sed', '-i', cmdoption, '/etc/tdnf/tdnf.conf']) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_INFO, "Failed to reset tdnf cachedir") self.exit_gracefully(None, None) def _install_packages(self): """ Install packages using tdnf or rpm command """ if self.install_config['iso_installer']: self._tdnf_install_packages() else: self._rpm_install_packages() def _tdnf_install_packages(self): """ Install packages using tdnf command """ self._adjust_packages_for_vmware_virt() selected_packages = self.install_config['packages'] state = 0 packages_to_install = {} total_size = 0 with open(modules.commons.TDNF_CMDLINE_FILE_NAME, "w") as tdnf_cmdline_file: tdnf_cmdline_file.write("tdnf install --installroot {0} --nogpgcheck {1}" .format(self.photon_root, " ".join(selected_packages))) with open(modules.commons.TDNF_LOG_FILE_NAME, "w") as tdnf_errlog: process = subprocess.Popen(['tdnf', 'install'] + selected_packages + ['--installroot', self.photon_root, '--nogpgcheck', '--assumeyes'], stdout=subprocess.PIPE, stderr=tdnf_errlog) while True: output = process.stdout.readline().decode() if output == '': retval = process.poll() if retval is not None: break if state == 0: if output == 'Installing:\n': state = 1 elif state == 1: #N A EVR Size(readable) Size(in bytes) if output == '\n': state = 2 self.progress_bar.update_num_items(total_size) else: info = output.split() package = '{0}-{1}.{2}'.format(info[0], info[2], info[1]) packages_to_install[package] = int(info[5]) total_size += int(info[5]) elif state == 2: if output == 'Downloading:\n': self.progress_bar.update_message('Preparing ...') state = 3 elif state == 3: self.progress_bar.update_message(output) if output == 'Running transaction\n': state = 4 else: modules.commons.log(modules.commons.LOG_INFO, "[tdnf] {0}".format(output)) prefix = 'Installing/Updating: ' if output.startswith(prefix): package = output[len(prefix):].rstrip('\n') self.progress_bar.increment(packages_to_install[package]) self.progress_bar.update_message(output) # 0 : succeed; 137 : package already installed; 65 : package not found in repo. if retval != 0 and retval != 137: modules.commons.log(modules.commons.LOG_ERROR, "Failed to install some packages, refer to {0}" .format(modules.commons.TDNF_LOG_FILE_NAME)) self.exit_gracefully(None, None) self.progress_bar.show_loading('Finalizing installation') def _rpm_install_packages(self): """ Install packages using rpm command """ rpms = [] for rpm in self.rpms_tobeinstalled: # We already installed the filesystem in the preparation if rpm['package'] == 'filesystem': continue rpms.append(rpm['filename']) rpms = set(rpms) rpm_paths = [] for root, _, files in os.walk(self.rpm_path): for file in files: if file in rpms: rpm_paths.append(os.path.join(root, file)) # --nodeps is for hosts which do not support rich dependencies rpm_params = ['--nodeps', '--root', self.photon_root, '--dbpath', '/var/lib/rpm'] if (('type' in self.install_config and (self.install_config['type'] in ['micro', 'minimal'])) or self.install_config['iso_system']): rpm_params.append('--excludedocs') modules.commons.log(modules.commons.LOG_INFO, "installing packages {0}, with params {1}" .format(rpm_paths, rpm_params)) process = subprocess.Popen(['rpm', '-Uvh'] + rpm_params + rpm_paths, stderr=subprocess.STDOUT) return_value = process.wait() if return_value != 0: self.exit_gracefully(None, None) def _eject_cdrom(self): """ Eject the cdrom on request """ eject_cdrom = True if 'ui_install' in self.install_config: self.window.content_window().getch() if 'eject_cdrom' in self.install_config and not self.install_config['eject_cdrom']: eject_cdrom = False if eject_cdrom: process = subprocess.Popen(['eject', '-r'], stdout=self.output) process.wait() def _enable_network_in_chroot(self): """ Enable network in chroot """ if os.path.exists("/etc/resolv.conf"): shutil.copy("/etc/resolv.conf", self.photon_root + '/etc/.') def _disable_network_in_chroot(self): """ disable network in chroot """ if os.path.exists(self.photon_root + '/etc/resolv.conf'): os.remove(self.photon_root + '/etc/resolv.conf')
class Installer(object): def __init__(self, install_config, maxy=0, maxx=0, iso_installer=False, local_install=False, tools_path="../stage", rpm_path="../stage/RPMS", log_path="../stage/LOGS"): self.install_config = install_config self.iso_installer = iso_installer self.tools_path = tools_path self.rpm_path = rpm_path self.log_path = log_path self.mount_command = "./mk-mount-disk.sh" self.prepare_command = "./mk-prepare-system.sh" self.finalize_command = "./mk-finalize-system.sh" self.install_package_command = "./mk-install-package.sh" self.chroot_command = "./mk-run-chroot.sh" self.setup_grub_command = "./mk-setup-grub.sh" self.unmount_disk_command = "./mk-unmount-disk.sh" self.local_install = local_install print local_install if local_install: self.scripts_working_directory = "./" elif self.iso_installer: self.scripts_working_directory = "/usr/src/photon" else: self.scripts_working_directory = "./" if self.iso_installer: self.photon_root = "/mnt/photon-root" elif 'working_directory' in self.install_config: self.photon_root = self.install_config['working_directory'] else: self.photon_root = "/mnt/photon-root" self.photon_directory = self.photon_root + "/usr/src/photon" self.restart_command = "shutdown" self.hostname_file = self.photon_root + "/etc/hostname" self.hosts_file = self.photon_root + "/etc/hosts" self.passwd_filename = self.photon_root + "/etc/passwd" self.shadow_filename = self.photon_root + "/etc/shadow" self.authorized_keys_dir = self.photon_root + "/root/.ssh" self.authorized_keys_filename = self.authorized_keys_dir + "/authorized_keys" self.sshd_config_filename = self.photon_root + "/etc/ssh/sshd_config" if self.iso_installer: self.output = open(os.devnull, 'w') else: self.output = None if self.iso_installer: #initializing windows self.maxy = maxy self.maxx = maxx self.height = 10 self.width = 75 self.progress_padding = 5 self.progress_width = self.width - self.progress_padding self.starty = (self.maxy - self.height) / 2 self.startx = (self.maxx - self.width) / 2 self.window = Window(self.height, self.width, self.maxy, self.maxx, 'Installing Photon', False) self.progress_bar = ProgressBar( self.starty + 3, self.startx + self.progress_padding / 2, self.progress_width) signal.signal(signal.SIGINT, self.exit_gracefully) # This will be called if the installer interrupted by Ctrl+C or exception def exit_gracefully(self, signal, frame): if self.iso_installer: self.progress_bar.hide() self.window.addstr( 0, 0, 'Opps, Installer got inturrupted.\n\nPress any key to get to the bash...' ) self.window.content_window().getch() sys.exit(1) def install(self, params): try: return self.unsafe_install(params) except: if self.iso_installer: self.exit_gracefully(None, None) else: raise def unsafe_install(self, params): self.prepare_files_rpms_list() if self.iso_installer: self.window.show_window() self.progress_bar.initialize(self.total_size, 'Initializing installation...') self.progress_bar.show() self.pre_initialize_filesystem() #install packages for rpm in self.rpms_tobeinstalled: # We already installed the filesystem in the preparation if rpm['package'] == 'filesystem': continue if self.iso_installer: self.progress_bar.update_message('Installing {0}...'.format( rpm['package'])) return_value = self.install_package(rpm['package']) if return_value != 0: self.exit_gracefully(None, None) #time.sleep(0.05) if self.iso_installer: self.progress_bar.increment(rpm['size'] * self.install_factor) if self.iso_installer: self.progress_bar.show_loading('Finalizing installation') #finalize system self.finalize_system() #time.sleep(5) if not self.install_config['iso_system'] and not self.local_install: # install grub process = subprocess.Popen([ self.setup_grub_command, '-w', self.photon_root, self.install_config['disk']['disk'], self.install_config['disk']['root'] ], stdout=self.output, stderr=self.output) retval = process.wait() #update root password self.update_root_password() #update hostname self.update_hostname() #update openssh config self.update_openssh_config() process = subprocess.Popen( [self.unmount_disk_command, '-w', self.photon_root], stdout=self.output, stderr=self.output) retval = process.wait() if self.iso_installer: self.progress_bar.hide() self.window.addstr( 0, 0, 'Congratulations, Photon has been installed in {0} secs.\n\nPress any key to continue to boot...' .format(self.progress_bar.time_elapsed)) self.window.content_window().getch() return ActionResult(True, None) def prepare_files_rpms_list(self): self.total_size = 0 self.install_factor = 3 tools_list = (JsonWrapper("tools_list.json")).read() tools = tools_list['base_tools'] # Add the additional iso tools. if self.install_config['iso_system']: tools = tools + tools_list['iso_tools'] self.files_tobecopied = [] for item in tools: src = os.path.join(self.scripts_working_directory, item) if os.path.isfile(src): if item != '.hidden': size = os.path.getsize(src) self.total_size += size self.files_tobecopied.append({ 'name': item, 'path': src, 'size': size }) continue for root, dirs, files in os.walk(src): for name in files: file = os.path.join(root, name) size = os.path.getsize(file) self.total_size += size relative = None if name.endswith(".rpm"): relative = os.path.relpath(file, self.rpm_path) relative = os.path.join("RPMS", relative) self.files_tobecopied.append({ 'name': name, 'path': file, 'relative_path': relative, 'size': size }) # prepare the RPMs # TODO: mbassiouny, do not copy the rpms twice rpms = [] for root, dirs, files in os.walk( os.path.join(self.scripts_working_directory, self.rpm_path)): for name in files: file = os.path.join(root, name) size = os.path.getsize(file) relative = os.path.relpath(file, self.rpm_path) relative = os.path.join("RPMS", relative) rpms.append({ 'name': name, 'path': file, 'relative_path': relative, 'size': size }) self.rpms_tobeinstalled = [] # prepare the RPMs list selected_packages = self.install_config['packages'] for package in selected_packages: pattern = package + '-[0-9]*.rpm' for rpm in rpms: if fnmatch.fnmatch(rpm['name'], pattern): rpm['package'] = package self.rpms_tobeinstalled.append(rpm) self.total_size += rpm[ 'size'] + rpm['size'] * self.install_factor break def copy_file(self, file): if self.iso_installer: message = 'Copying {0}...'.format(file['name']) self.progress_bar.update_message(message) if 'relative_path' in file and file['relative_path'] != None: relative = file['relative_path'] else: relative = os.path.relpath(file['path'], self.scripts_working_directory) dst = os.path.join(self.photon_directory, relative) if not os.path.exists(os.path.dirname(dst)): os.makedirs(os.path.dirname(dst)) shutil.copy(file['path'], dst) def copy_files(self): for file in self.files_tobecopied: self.copy_file(file) #time.sleep(0.05) if self.iso_installer: self.progress_bar.increment(file['size']) for rpm in self.rpms_tobeinstalled: self.copy_file(rpm) #time.sleep(0.05) if self.iso_installer: self.progress_bar.increment(rpm['size']) def pre_initialize_filesystem(self): #Setup the disk if (not self.install_config['iso_system']) and ( not self.local_install): process = subprocess.Popen([ self.mount_command, '-w', self.photon_root, self.install_config['disk']['root'] ], stdout=self.output, stderr=self.output) retval = process.wait() #Setup the filesystem basics self.copy_files() process = subprocess.Popen( [self.prepare_command, '-w', self.photon_root, self.tools_path], stdout=self.output, stderr=self.output) retval = process.wait() def finalize_system(self): #Setup the disk process = subprocess.Popen([ self.chroot_command, '-w', self.photon_root, self.finalize_command, '-w', self.photon_root ], stdout=self.output, stderr=self.output) retval = process.wait() if self.iso_installer: # just copy the initrd /boot -> /photon_mnt/boot shutil.copy('/boot/initrd.img-no-kmods', self.photon_root + '/boot/') elif not self.local_install: #Build the initrd process = subprocess.Popen([ self.chroot_command, '-w', self.photon_root, './mkinitramfs', '-n', '/boot/initrd.img-no-kmods' ], stdout=self.output, stderr=self.output) retval = process.wait() process = subprocess.Popen(["./mk-initrd", '-w', self.photon_root], stdout=self.output, stderr=self.output) retval = process.wait() def install_package(self, package_name): rpm_params = '' if 'type' in self.install_config and (self.install_config['type'] in ['micro', 'minimal']): rpm_params = rpm_params + ' --excludedocs ' process = subprocess.Popen([ self.chroot_command, '-w', self.photon_root, self.install_package_command, '-w', self.photon_root, package_name, rpm_params ], stdout=self.output, stderr=self.output) return process.wait() def replace_string_in_file(self, filename, search_string, replace_string): with open(filename, "r") as source: lines = source.readlines() with open(filename, "w") as destination: for line in lines: destination.write(re.sub(search_string, replace_string, line)) def update_root_password(self): shadow_password = self.install_config['password'] #replace root blank password in passwd file to point to shadow file self.replace_string_in_file(self.passwd_filename, "root::", "root:x:") if os.path.isfile(self.shadow_filename) == False: with open(self.shadow_filename, "w") as destination: destination.write("root:" + shadow_password + ":") else: #add password hash in shadow file self.replace_string_in_file(self.shadow_filename, "root::", "root:" + shadow_password + ":") def update_hostname(self): self.hostname = self.install_config['hostname'] outfile = open(self.hostname_file, 'wb') outfile.write(self.hostname) outfile.close() self.replace_string_in_file( self.hosts_file, r'127\.0\.0\.1\s+localhost', '127.0.0.1\tlocalhost\n127.0.0.1\t' + self.hostname) def update_openssh_config(self): if 'public_key' in self.install_config: # Adding the authorized keys if not os.path.exists(self.authorized_keys_dir): os.makedirs(self.authorized_keys_dir) with open(self.authorized_keys_filename, "a") as destination: destination.write(self.install_config['public_key'] + "\n") os.chmod(self.authorized_keys_filename, 0600) # Change the sshd config to allow root login process = subprocess.Popen([ "sed", "-i", "s/^\\s*PermitRootLogin\s\+no/PermitRootLogin yes/", self.sshd_config_filename ], stdout=self.output, stderr=self.output) return process.wait()
class CustomPartition(object): def __init__(self, maxy, maxx, install_config): self.maxx = maxx self.maxy = maxy self.win_width = maxx - 4 self.win_height = maxy - 4 self.install_config = install_config self.path_checker = [] self.win_starty = (self.maxy - self.win_height) // 2 self.win_startx = (self.maxx - self.win_width) // 2 self.text_starty = self.win_starty + 4 self.text_height = self.win_height - 6 self.text_width = self.win_width - 6 self.cp_config = {} self.cp_config['partitionsnumber'] = 0 self.devices = Device.refresh_devices_bytes() self.has_slash = False self.has_remain = False self.has_empty = False self.disk_size = [] self.disk_to_index = {} for index, device in enumerate(self.devices): self.disk_size.append((device.path, int(device.size) / 1048576)) self.disk_to_index[device.path] = index self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Welcome to the Photon installer', False, can_go_next=False) Device.refresh_devices() def display(self): if 'autopartition' in self.install_config and self.install_config[ 'autopartition'] == True: return ActionResult(True, None) self.device_index = self.disk_to_index[self.install_config['disk']] self.disk_buttom_items = [] self.disk_buttom_items.append(('<Next>', self.next)) self.disk_buttom_items.append(('<Create New>', self.create_function)) self.disk_buttom_items.append(('<Delete All>', self.delete_function)) self.disk_buttom_items.append(('<Go Back>', self.go_back)) self.text_items = [] self.text_items.append(('Disk', 20)) self.text_items.append(('Size', 5)) self.text_items.append(('Type', 5)) self.text_items.append(('Mountpoint', 20)) self.table_space = 5 title = 'Current partitions:\n' self.window.addstr(0, (self.win_width - len(title)) // 2, title) info = ("Unpartitioned space: " + str(self.disk_size[self.device_index][1]) + " MB, Total size: " + str(int(self.devices[self.device_index].size) / 1048576) + " MB") self.partition_pane = PartitionPane( self.text_starty, self.maxx, self.text_width, self.text_height, self.disk_buttom_items, config=self.cp_config, text_items=self.text_items, table_space=self.table_space, info=info, size_left=str(self.disk_size[self.device_index][1])) self.window.set_action_panel(self.partition_pane) return self.window.do_action() def validate_partition(self, pstr): if not pstr: return ActionResult(False, None) sizedata = pstr[0] mtdata = pstr[2] typedata = pstr[1] devicedata = self.devices[self.device_index].path #no empty fields unless swap if (typedata == 'swap' and (len(mtdata) != 0 or len(typedata) == 0 or len(devicedata) == 0)): return False, "invalid swap data " if (typedata != 'swap' and (len(sizedata) == 0 or len(mtdata) == 0 or len(typedata) == 0 or len(devicedata) == 0)): if not self.has_empty and mtdata and typedata and devicedata: self.has_empty = True else: return False, "Input cannot be empty" if typedata != 'swap' and typedata != 'ext3' and typedata != 'ext4': return False, "Invalid type" if len(mtdata) != 0 and mtdata[0] != '/': return False, "Invalid path" if mtdata in self.path_checker: return False, "Path already existed" #validate disk: must be one of the existing disks i = self.device_index #valid size: must not exceed memory limit curr_size = self.disk_size[i][1] if len(sizedata) != 0: try: int(sizedata) except ValueError: return False, "invalid device size" if int(curr_size) - int(sizedata) < 0: return False, "invalid device size" #if valid, update the size and return true new_size = (self.disk_size[i][0], int(curr_size) - int(sizedata)) self.disk_size[i] = new_size if mtdata == "/": self.has_slash = True self.path_checker.append(mtdata) return True, None def create_function(self): self.window.hide_window() self.cp_config['partition_disk'] = self.devices[self.device_index].path self.partition_items = [] self.partition_items.append( ('Size in MB: ' + str(self.disk_size[self.device_index][1]) + ' available')) self.partition_items.append(('Type: (ext3, ext4, swap)')) self.partition_items.append(('Mountpoint:')) self.create_window = ReadMulText( self.maxy, self.maxx, 0, self.cp_config, str(self.cp_config['partitionsnumber']) + 'partition_info', self.partition_items, None, None, None, self.validate_partition, #validation function of the input None, True, ) result = self.create_window.do_action() if result.success: self.cp_config[ 'partitionsnumber'] = self.cp_config['partitionsnumber'] + 1 #parse the input in install config return self.display() def delete_function(self): self.delete() return self.display() def go_back(self): self.delete() self.window.hide_window() self.partition_pane.hide() return ActionResult(False, {'goBack': True}) def next(self): if self.cp_config['partitionsnumber'] == 0: window_height = 9 window_width = 40 window_starty = (self.maxy - window_height) // 2 + 5 confirm_window = ConfirmWindow( window_height, window_width, self.maxy, self.maxx, window_starty, 'Partition information cannot be empty', info=True) confirm_window.do_action() return self.display() #must have / if not self.has_slash: window_height = 9 window_width = 40 window_starty = (self.maxy - window_height) // 2 + 5 confirm_window = ConfirmWindow(window_height, window_width, self.maxy, self.maxx, window_starty, 'Missing /', info=True) confirm_window.do_action() return self.display() self.window.hide_window() self.partition_pane.hide() partitions = [] for i in range(int(self.cp_config['partitionsnumber'])): if len(self.cp_config[str(i) + 'partition_info' + str(0)]) == 0: sizedata = 0 else: sizedata = int(self.cp_config[str(i) + 'partition_info' + str(0)]) mtdata = self.cp_config[str(i) + 'partition_info' + str(2)] typedata = self.cp_config[str(i) + 'partition_info' + str(1)] partitions = partitions + [ { "mountpoint": mtdata, "size": sizedata, "filesystem": typedata }, ] self.install_config['partitions'] = partitions return ActionResult(True, {'goNext': True}) def delete(self): for i in range(int(self.cp_config['partitionsnumber'])): self.cp_config[str(i) + 'partition_info' + str(0)] = '' self.cp_config[str(i) + 'partition_info' + str(1)] = '' self.cp_config[str(i) + 'partition_info' + str(2)] = '' self.cp_config[str(i) + 'partition_info' + str(3)] = '' del self.disk_size[:] for index, device in enumerate(self.devices): self.disk_size.append((device.path, int(device.size) / 1048576)) del self.path_checker[:] self.has_slash = False self.has_remain = False self.has_empty = False self.cp_config['partitionsnumber'] = 0
class Installer(object): def __init__(self, install_config, maxy = 0, maxx = 0, iso_installer = False, rpm_path = "../stage/RPMS", log_path = "../stage/LOGS", ks_config = None): self.install_config = install_config self.ks_config = ks_config self.iso_installer = iso_installer self.rpm_path = rpm_path self.log_path = log_path self.mount_command = "./mk-mount-disk.sh" self.prepare_command = "./mk-prepare-system.sh" self.finalize_command = "./mk-finalize-system.sh" self.install_package_command = "./mk-install-package.sh" self.chroot_command = "./mk-run-chroot.sh" self.setup_grub_command = "./mk-setup-grub.sh" self.unmount_disk_command = "./mk-unmount-disk.sh" if self.iso_installer: self.working_directory = "/mnt/photon-root" elif 'working_directory' in self.install_config: self.working_directory = self.install_config['working_directory'] else: self.working_directory = "/mnt/photon-root" self.photon_root = self.working_directory + "/photon-chroot"; self.restart_command = "shutdown" if self.iso_installer: self.output = open(os.devnull, 'w') else: self.output = None if self.iso_installer: #initializing windows self.maxy = maxy self.maxx = maxx self.height = 10 self.width = 75 self.progress_padding = 5 self.progress_width = self.width - self.progress_padding self.starty = (self.maxy - self.height) / 2 self.startx = (self.maxx - self.width) / 2 self.window = Window(self.height, self.width, self.maxy, self.maxx, 'Installing Photon', False) self.progress_bar = ProgressBar(self.starty + 3, self.startx + self.progress_padding / 2, self.progress_width) signal.signal(signal.SIGINT, self.exit_gracefully) # This will be called if the installer interrupted by Ctrl+C or exception def exit_gracefully(self, signal, frame): if self.iso_installer: self.progress_bar.hide() self.window.addstr(0, 0, 'Opps, Installer got interrupted.\n\nPress any key to get to the bash...') self.window.content_window().getch() modules.commons.dump(modules.commons.LOG_FILE_NAME) sys.exit(1) def install(self, params): try: return self.unsafe_install(params) except Exception as inst: if self.iso_installer: modules.commons.log(modules.commons.LOG_ERROR, repr(inst)) self.exit_gracefully(None, None) else: raise def unsafe_install(self, params): if self.iso_installer: self.window.show_window() self.progress_bar.initialize('Initializing installation...') self.progress_bar.show() #self.rpm_path = "https://dl.bintray.com/vmware/photon_release_1.0_TP2_x86_64" if self.rpm_path.startswith("https://") or self.rpm_path.startswith("http://"): cmdoption = 's/baseurl.*/baseurl={}/g'.format(self.rpm_path.replace('/','\/')) process = subprocess.Popen(['sed', '-i', cmdoption,'/etc/yum.repos.d/photon-iso.repo']) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_INFO, "Failed to reset repo") self.exit_gracefully(None, None) cmdoption = 's/cachedir=\/var/cachedir={}/g'.format(self.photon_root.replace('/','\/')) process = subprocess.Popen(['sed', '-i', cmdoption,'/etc/tdnf/tdnf.conf']) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_INFO, "Failed to reset tdnf cachedir") self.exit_gracefully(None, None) self.execute_modules(modules.commons.PRE_INSTALL) self.initialize_system() if self.iso_installer: self.get_size_of_packages() selected_packages = self.install_config['packages'] for package in selected_packages: self.progress_bar.update_message('Installing {0}...'.format(package)) process = subprocess.Popen(['tdnf', 'install', package, '--installroot', self.photon_root, '--nogpgcheck', '--assumeyes'], stdout=self.output, stderr=subprocess.STDOUT) retval = process.wait() # 0 : succeed; 137 : package already installed; 65 : package not found in repo. if retval != 0 and retval != 137: modules.commons.log(modules.commons.LOG_ERROR, "Failed install: {} with error code {}".format(package, retval)) self.exit_gracefully(None, None) self.progress_bar.increment(self.size_of_packages[package]) else: #install packages for rpm in self.rpms_tobeinstalled: # We already installed the filesystem in the preparation if rpm['package'] == 'filesystem': continue return_value = self.install_package(rpm['filename']) if return_value != 0: self.exit_gracefully(None, None) if self.iso_installer: self.progress_bar.show_loading('Finalizing installation') self.finalize_system() if not self.install_config['iso_system']: # Execute post installation modules self.execute_modules(modules.commons.POST_INSTALL) # install grub try: if self.install_config['boot'] == 'bios': process = subprocess.Popen([self.setup_grub_command, '-w', self.photon_root, "bios", self.install_config['disk']['disk'], self.install_config['disk']['root'], self.install_config['disk']['boot'], self.install_config['disk']['bootdirectory']], stdout=self.output) elif self.install_config['boot'] == 'efi': process = subprocess.Popen([self.setup_grub_command, '-w', self.photon_root, "efi", self.install_config['disk']['disk'], self.install_config['disk']['root'], self.install_config['disk']['boot'], self.install_config['disk']['bootdirectory']], stdout=self.output) except: #install bios if variable is not set. process = subprocess.Popen([self.setup_grub_command, '-w', self.photon_root, "bios", self.install_config['disk']['disk'], self.install_config['disk']['root'], self.install_config['disk']['boot'], self.install_config['disk']['bootdirectory']], stdout=self.output) retval = process.wait() self.update_fstab() command = [self.unmount_disk_command, '-w', self.photon_root] if not self.install_config['iso_system']: command.extend(self.generate_partitions_param(reverse = True)) process = subprocess.Popen(command, stdout=self.output) retval = process.wait() if self.iso_installer: self.progress_bar.hide() self.window.addstr(0, 0, 'Congratulations, Photon has been installed in {0} secs.\n\nPress any key to continue to boot...'.format(self.progress_bar.time_elapsed)) if self.ks_config == None: self.window.content_window().getch() return ActionResult(True, None) def copy_rpms(self): # prepare the RPMs list rpms = [] for root, dirs, files in os.walk(self.rpm_path): for name in files: file = os.path.join(root, name) size = os.path.getsize(file) rpms.append({'filename': name, 'path': file, 'size': size}) self.rpms_tobeinstalled = [] selected_packages = self.install_config['packages'] for package in selected_packages: pattern = package + '-[0-9]*.rpm' pattern2 = package + '-[a-z][0-9]*.rpm' for rpm in rpms: if fnmatch.fnmatch(rpm['filename'], pattern) or fnmatch.fnmatch(rpm['filename'], pattern2): rpm['package'] = package self.rpms_tobeinstalled.append(rpm) break # Copy the rpms for rpm in self.rpms_tobeinstalled: shutil.copy(rpm['path'], self.photon_root + '/RPMS/') def copy_files(self): # Make the photon_root directory if not exits process = subprocess.Popen(['mkdir', '-p', self.photon_root], stdout=self.output) retval = process.wait() # Copy the installer files process = subprocess.Popen(['cp', '-r', "../installer", self.photon_root], stdout=self.output) retval = process.wait() # Create the rpms directory process = subprocess.Popen(['mkdir', '-p', self.photon_root + '/RPMS'], stdout=self.output) retval = process.wait() self.copy_rpms() def bind_installer(self): # Make the photon_root/installer directory if not exits process = subprocess.Popen(['mkdir', '-p', os.path.join(self.photon_root, "installer")], stdout=self.output) retval = process.wait() # The function finalize_system will access the file /installer/mk-finalize-system.sh after chroot to photon_root. # Bind the /installer folder to self.photon_root/installer, so that after chroot to photon_root, # the file can still be accessed as /installer/mk-finalize-system.sh. process = subprocess.Popen(['mount', '--bind', '/installer', os.path.join(self.photon_root, "installer")], stdout=self.output) retval = process.wait() def update_fstab(self): fstab_file = open(os.path.join(self.photon_root, "etc/fstab"), "w") fstab_file.write("#system\tmnt-pt\ttype\toptions\tdump\tfsck\n") for partition in self.install_config['disk']['partitions']: options = 'defaults' dump = 1 fsck = 2 if 'mountpoint' in partition and partition['mountpoint'] == '/': fsck = 1 if partition['filesystem'] == 'swap': mountpoint = 'swap' dump = 0 fsck = 0 else: mountpoint = partition['mountpoint'] fstab_file.write("{}\t{}\t{}\t{}\t{}\t{}\n".format( partition['path'], mountpoint, partition['filesystem'], options, dump, fsck )) # Add the cdrom entry fstab_file.write("/dev/cdrom\t/mnt/cdrom\tiso9660\tro,noauto\t0\t0\n") fstab_file.close() def generate_partitions_param(self, reverse = False): if reverse: step = -1 else: step = 1 params = [] for partition in self.install_config['disk']['partitions'][::step]: if partition["filesystem"] == "swap": continue params.extend(['--partitionmountpoint', partition["path"], partition["mountpoint"]]) return params def initialize_system(self): #Setup the disk if (not self.install_config['iso_system']): command = [self.mount_command, '-w', self.photon_root] command.extend(self.generate_partitions_param()) process = subprocess.Popen(command, stdout=self.output) retval = process.wait() if self.iso_installer: self.bind_installer() process = subprocess.Popen([self.prepare_command, '-w', self.photon_root, 'install'], stdout=self.output) retval = process.wait() else: self.copy_files() #Setup the filesystem basics process = subprocess.Popen([self.prepare_command, '-w', self.photon_root], stdout=self.output) retval = process.wait() def finalize_system(self): #Setup the disk process = subprocess.Popen([self.chroot_command, '-w', self.photon_root, self.finalize_command, '-w', self.photon_root], stdout=self.output) retval = process.wait() if self.iso_installer: modules.commons.dump(modules.commons.LOG_FILE_NAME) shutil.copy(modules.commons.LOG_FILE_NAME, self.photon_root + '/var/log/') # unmount the installer directory process = subprocess.Popen(['umount', os.path.join(self.photon_root, "installer")], stdout=self.output) retval = process.wait() # remove the installer directory process = subprocess.Popen(['rm', '-rf', os.path.join(self.photon_root, "installer")], stdout=self.output) retval = process.wait() # Disable the swap file process = subprocess.Popen(['swapoff', '-a'], stdout=self.output) retval = process.wait() # remove the tdnf cache directory and the swapfile. process = subprocess.Popen(['rm', '-rf', os.path.join(self.photon_root, "cache")], stdout=self.output) def install_package(self, package_name): rpm_params = '' os.environ["RPMROOT"] = self.rpm_path rpm_params = rpm_params + ' --force ' rpm_params = rpm_params + ' --root ' + self.photon_root rpm_params = rpm_params + ' --dbpath /var/lib/rpm ' if ('type' in self.install_config and (self.install_config['type'] in ['micro', 'minimal'])) or self.install_config['iso_system']: rpm_params = rpm_params + ' --excludedocs ' process = subprocess.Popen([self.install_package_command, '-w', self.photon_root, package_name, rpm_params], stdout=self.output) return process.wait() def execute_modules(self, phase): modules_paths = glob.glob('modules/m_*.py') for mod_path in modules_paths: module = mod_path.replace('/', '.', 1) module = os.path.splitext(module)[0] try: __import__(module) mod = sys.modules[module] except ImportError: modules.commons.log(modules.commons.LOG_ERROR, 'Error importing module {}'.format(module)) continue # the module default is disabled if not hasattr(mod, 'enabled') or mod.enabled == False: modules.commons.log(modules.commons.LOG_INFO, "module {} is not enabled".format(module)) continue # check for the install phase if not hasattr(mod, 'install_phase'): modules.commons.log(modules.commons.LOG_ERROR, "Error: can not defind module {} phase".format(module)) continue if mod.install_phase != phase: modules.commons.log(modules.commons.LOG_INFO, "Skipping module {0} for phase {1}".format(module, phase)) continue if not hasattr(mod, 'execute'): modules.commons.log(modules.commons.LOG_ERROR, "Error: not able to execute module {}".format(module)) continue mod.execute(module, self.ks_config, self.install_config, self.photon_root) def get_install_size_of_a_package(self, name_size_pairs, package): modules.commons.log(modules.commons.LOG_INFO, "Find the install size of: {} ".format(package)) for index, name in enumerate(name_size_pairs, start=0): if name[name.find(":") + 1:].strip() == package.strip(): item = name_size_pairs[index + 1] size = item[item.find("(") + 1:item.find(")")] return int(size) raise LookupError("Cannot find package {} in the repo.".format(package)) def get_size_of_packages(self): #call tdnf info to get the install size of all the packages. process = subprocess.Popen(['tdnf', 'info', '--installroot', self.photon_root], stdout=subprocess.PIPE) out,err = process.communicate() if err != None and err != 0: modules.commons.log(modules.commons.LOG_ERROR, "Failed to get infomation from : {} with error code {}".format(package, err)) name_size_pairs = re.findall("(?:^Name.*$)|(?:^.*Install Size.*$)", out, re.M) selected_packages = self.install_config['packages'] self.size_of_packages = {} progressbar_num_items = 0 for package in selected_packages: size = self.get_install_size_of_a_package(name_size_pairs, package) progressbar_num_items += size; self.size_of_packages[package] = size; self.progress_bar.update_num_items(progressbar_num_items) def run(self, command, comment = None): if comment != None: modules.commons.log(modules.commons.LOG_INFO, "Installer: {} ".format(comment)) self.progress_bar.update_loading_message(comment) modules.commons.log(modules.commons.LOG_INFO, "Installer: {} ".format(command)) process = subprocess.Popen([command], shell=True, stdout=subprocess.PIPE) out,err = process.communicate() if err != None and err != 0 and "systemd-tmpfiles" not in command: modules.commons.log(modules.commons.LOG_ERROR, "Installer: failed in {} with error code {}".format(command, err)) modules.commons.log(modules.commons.LOG_ERROR, out) self.exit_gracefully(None, None) return err
class Installer(object): def __init__(self, install_config, maxy=0, maxx=0, iso_installer=False, tools_path="../stage", rpm_path="../stage/RPMS", log_path="../stage/LOGS", ks_config=None): self.install_config = install_config self.ks_config = ks_config self.iso_installer = iso_installer self.tools_path = tools_path self.rpm_path = rpm_path self.log_path = log_path self.mount_command = "./mk-mount-disk.sh" self.prepare_command = "./mk-prepare-system.sh" self.finalize_command = "./mk-finalize-system.sh" self.install_package_command = "./mk-install-package.sh" self.chroot_command = "./mk-run-chroot.sh" self.setup_grub_command = "./mk-setup-grub.sh" self.unmount_disk_command = "./mk-unmount-disk.sh" if self.iso_installer: self.working_directory = "/mnt/photon-root" elif 'working_directory' in self.install_config: self.working_directory = self.install_config['working_directory'] else: self.working_directory = "/mnt/photon-root" self.photon_root = self.working_directory + "/photon-chroot" self.restart_command = "shutdown" if self.iso_installer: self.output = open(os.devnull, 'w') else: self.output = None self.install_factor = 3 if self.iso_installer: #initializing windows self.maxy = maxy self.maxx = maxx self.height = 10 self.width = 75 self.progress_padding = 5 self.progress_width = self.width - self.progress_padding self.starty = (self.maxy - self.height) / 2 self.startx = (self.maxx - self.width) / 2 self.window = Window(self.height, self.width, self.maxy, self.maxx, 'Installing Photon', False) self.progress_bar = ProgressBar( self.starty + 3, self.startx + self.progress_padding / 2, self.progress_width) signal.signal(signal.SIGINT, self.exit_gracefully) # This will be called if the installer interrupted by Ctrl+C or exception def exit_gracefully(self, signal, frame): if self.iso_installer: self.progress_bar.hide() self.window.addstr( 0, 0, 'Opps, Installer got inturrupted.\n\nPress any key to get to the bash...' ) self.window.content_window().getch() sys.exit(1) def install(self, params): try: return self.unsafe_install(params) except: if self.iso_installer: self.exit_gracefully(None, None) else: raise def unsafe_install(self, params): if self.iso_installer: self.window.show_window() self.progress_bar.initialize('Initializing installation...') self.progress_bar.show() self.execute_modules(modules.commons.PRE_INSTALL) self.initialize_system() #install packages for rpm in self.rpms_tobeinstalled: # We already installed the filesystem in the preparation if rpm['package'] == 'filesystem': continue if self.iso_installer: self.progress_bar.update_message('Installing {0}...'.format( rpm['package'])) return_value = self.install_package(rpm['package']) if return_value != 0: self.exit_gracefully(None, None) if self.iso_installer: self.progress_bar.increment(rpm['size'] * self.install_factor) if self.iso_installer: self.progress_bar.show_loading('Finalizing installation') self.finalize_system() if not self.install_config['iso_system']: # Execute post installation modules self.execute_modules(modules.commons.POST_INSTALL) # install grub process = subprocess.Popen([ self.setup_grub_command, '-w', self.photon_root, self.install_config['disk']['disk'], self.install_config['disk']['root'] ], stdout=self.output) retval = process.wait() process = subprocess.Popen( [self.unmount_disk_command, '-w', self.photon_root], stdout=self.output) retval = process.wait() if self.iso_installer: self.progress_bar.hide() self.window.addstr( 0, 0, 'Congratulations, Photon has been installed in {0} secs.\n\nPress any key to continue to boot...' .format(self.progress_bar.time_elapsed)) if self.ks_config == None: self.window.content_window().getch() return ActionResult(True, None) def copy_rpms(self): # prepare the RPMs list rpms = [] for root, dirs, files in os.walk(self.rpm_path): for name in files: file = os.path.join(root, name) size = os.path.getsize(file) rpms.append({'name': name, 'path': file, 'size': size}) progressbar_num_items = 0 self.rpms_tobeinstalled = [] selected_packages = self.install_config['packages'] for package in selected_packages: pattern = package + '-[0-9]*.rpm' for rpm in rpms: if fnmatch.fnmatch(rpm['name'], pattern): rpm['package'] = package self.rpms_tobeinstalled.append(rpm) progressbar_num_items += rpm[ 'size'] + rpm['size'] * self.install_factor break process = subprocess.Popen(['mkdir', '-p', self.photon_root + '/RPMS'], stdout=self.output) retval = process.wait() if self.iso_installer: self.progress_bar.update_num_items(progressbar_num_items) # Copy the rpms for rpm in self.rpms_tobeinstalled: shutil.copy(rpm['path'], self.photon_root + '/RPMS/') if self.iso_installer: self.progress_bar.increment(rpm['size']) def copy_files(self): # Make the photon_root directory if not exits process = subprocess.Popen(['mkdir', '-p', self.photon_root], stdout=self.output) retval = process.wait() # Copy the installer files process = subprocess.Popen( ['cp', '-r', "../installer", self.photon_root], stdout=self.output) retval = process.wait() self.copy_rpms() def initialize_system(self): #Setup the disk if (not self.install_config['iso_system']): process = subprocess.Popen([ self.mount_command, '-w', self.photon_root, self.install_config['disk']['root'] ], stdout=self.output) retval = process.wait() self.copy_files() #Setup the filesystem basics process = subprocess.Popen( [self.prepare_command, '-w', self.photon_root, self.tools_path], stdout=self.output) retval = process.wait() def finalize_system(self): #Setup the disk process = subprocess.Popen([ self.chroot_command, '-w', self.photon_root, self.finalize_command, '-w', self.photon_root ], stdout=self.output) retval = process.wait() if self.iso_installer: # just copy the initramfs /boot -> /photon_mnt/boot shutil.copy('/boot/initrd.img-no-kmods', self.photon_root + '/boot/') else: #Build the initramfs process = subprocess.Popen([ self.chroot_command, '-w', self.photon_root, './mkinitramfs', '-n', '/boot/initrd.img-no-kmods' ], stdout=self.output) retval = process.wait() def install_package(self, package_name): rpm_params = '' if ('type' in self.install_config and (self.install_config['type'] in ['micro', 'minimal'])) or self.install_config['iso_system']: rpm_params = rpm_params + ' --excludedocs ' process = subprocess.Popen([ self.chroot_command, '-w', self.photon_root, self.install_package_command, '-w', self.photon_root, package_name, rpm_params ], stdout=self.output) return process.wait() def execute_modules(self, phase): modules = glob.glob('modules/m_*.py') for mod_path in modules: module = mod_path.replace('/', '.', 1) module = os.path.splitext(module)[0] try: __import__(module) mod = sys.modules[module] except ImportError: print >> sys.stderr, 'Error importing module %s' % module continue # the module default is disabled if not hasattr(mod, 'enabled') or mod.enabled == False: print >> sys.stderr, "module %s is not enabled" % module continue # check for the install phase if not hasattr(mod, 'install_phase'): print >> sys.stderr, "Error: can not defind module %s phase" % module continue if mod.install_phase != phase: print >> sys.stderr, "Skipping module %s for phase %s" % ( module, phase) continue if not hasattr(mod, 'execute'): print >> sys.stderr, "Error: not able to execute module %s" % module continue mod.execute(module, self.ks_config, self.install_config, self.photon_root)
class Installer(object): def __init__(self, install_config, maxy = 0, maxx = 0, iso_installer = False, tools_path = "../stage", rpm_path = "../stage/RPMS", log_path = "../stage/LOGS"): self.install_config = install_config self.iso_installer = iso_installer self.tools_path = tools_path self.rpm_path = rpm_path self.log_path = log_path self.mount_command = "./mk-mount-disk.sh" self.prepare_command = "./mk-prepare-system.sh" self.finalize_command = "./mk-finalize-system.sh" self.install_package_command = "./mk-install-package.sh" self.chroot_command = "./mk-run-chroot.sh" self.setup_grub_command = "./mk-setup-grub.sh" self.unmount_disk_command = "./mk-unmount-disk.sh" if self.iso_installer: self.working_directory = "/mnt/photon-root" elif 'working_directory' in self.install_config: self.working_directory = self.install_config['working_directory'] else: self.working_directory = "/mnt/photon-root" self.photon_root = self.working_directory + "/photon-chroot"; self.restart_command = "shutdown" self.hostname_file = self.photon_root + "/etc/hostname" self.hosts_file = self.photon_root + "/etc/hosts" self.passwd_filename = self.photon_root + "/etc/passwd" self.shadow_filename = self.photon_root + "/etc/shadow" self.authorized_keys_dir = self.photon_root + "/root/.ssh" self.authorized_keys_filename = self.authorized_keys_dir + "/authorized_keys" self.sshd_config_filename = self.photon_root + "/etc/ssh/sshd_config" if self.iso_installer: self.output = open(os.devnull, 'w') else: self.output = None self.install_factor = 3 if self.iso_installer: #initializing windows self.maxy = maxy self.maxx = maxx self.height = 10 self.width = 75 self.progress_padding = 5 self.progress_width = self.width - self.progress_padding self.starty = (self.maxy - self.height) / 2 self.startx = (self.maxx - self.width) / 2 self.window = Window(self.height, self.width, self.maxy, self.maxx, 'Installing Photon', False) self.progress_bar = ProgressBar(self.starty + 3, self.startx + self.progress_padding / 2, self.progress_width) signal.signal(signal.SIGINT, self.exit_gracefully) # This will be called if the installer interrupted by Ctrl+C or exception def exit_gracefully(self, signal, frame): if self.iso_installer: self.progress_bar.hide() self.window.addstr(0, 0, 'Opps, Installer got inturrupted.\n\nPress any key to get to the bash...') self.window.content_window().getch() sys.exit(1) def install(self, params): try: return self.unsafe_install(params) except: if self.iso_installer: self.exit_gracefully(None, None) else: raise def unsafe_install(self, params): if self.iso_installer: self.window.show_window() self.progress_bar.initialize('Initializing installation...') self.progress_bar.show() self.initialize_system() #install packages for rpm in self.rpms_tobeinstalled: # We already installed the filesystem in the preparation if rpm['package'] == 'filesystem': continue if self.iso_installer: self.progress_bar.update_message('Installing {0}...'.format(rpm['package'])) return_value = self.install_package(rpm['package']) if return_value != 0: self.exit_gracefully(None, None) if self.iso_installer: self.progress_bar.increment(rpm['size'] * self.install_factor) if self.iso_installer: self.progress_bar.show_loading('Finalizing installation') self.finalize_system() if not self.install_config['iso_system']: # install grub process = subprocess.Popen([self.setup_grub_command, '-w', self.photon_root, self.install_config['disk']['disk'], self.install_config['disk']['root']], stdout=self.output) retval = process.wait() #update root password self.update_root_password() #update hostname self.update_hostname() #update openssh config self.update_openssh_config() process = subprocess.Popen([self.unmount_disk_command, '-w', self.photon_root], stdout=self.output) retval = process.wait() if self.iso_installer: self.progress_bar.hide() self.window.addstr(0, 0, 'Congratulations, Photon has been installed in {0} secs.\n\nPress any key to continue to boot...'.format(self.progress_bar.time_elapsed)) self.window.content_window().getch() return ActionResult(True, None) def copy_rpms(self): # prepare the RPMs list rpms = [] for root, dirs, files in os.walk(self.rpm_path): for name in files: file = os.path.join(root, name) size = os.path.getsize(file) rpms.append({'name': name, 'path': file, 'size': size}) progressbar_num_items = 0 self.rpms_tobeinstalled = [] selected_packages = self.install_config['packages'] for package in selected_packages: pattern = package + '-[0-9]*.rpm' for rpm in rpms: if fnmatch.fnmatch(rpm['name'], pattern): rpm['package'] = package self.rpms_tobeinstalled.append(rpm) progressbar_num_items += rpm['size'] + rpm['size'] * self.install_factor break process = subprocess.Popen(['mkdir', '-p', self.photon_root + '/RPMS'], stdout=self.output) retval = process.wait() if self.iso_installer: self.progress_bar.update_num_items(progressbar_num_items) # Copy the rpms for rpm in self.rpms_tobeinstalled: shutil.copy(rpm['path'], self.photon_root + '/RPMS/') if self.iso_installer: self.progress_bar.increment(rpm['size']) def copy_files(self): # Make the photon_root directory if not exits process = subprocess.Popen(['mkdir', '-p', self.photon_root], stdout=self.output) retval = process.wait() # Copy the installer files process = subprocess.Popen(['cp', '-r', "../installer", self.photon_root], stdout=self.output) retval = process.wait() # get the RPMS dir form the cd if self.rpm_path == 'cdrom': # Mount the cd to get the RPMS process = subprocess.Popen(['mkdir', '-p', '/mnt/cdrom'], stdout=self.output) retval = process.wait() process = subprocess.Popen(['mount', '/dev/cdrom', '/mnt/cdrom'], stdout=self.output) retval = process.wait() if retval != 0: self.exit_gracefully(None, None) self.rpm_path = '/mnt/cdrom/RPMS' self.tools_path = '/mnt/cdrom' self.copy_rpms() def initialize_system(self): #Setup the disk if (not self.install_config['iso_system']): process = subprocess.Popen([self.mount_command, '-w', self.photon_root, self.install_config['disk']['root']], stdout=self.output) retval = process.wait() self.copy_files() #Setup the filesystem basics process = subprocess.Popen([self.prepare_command, '-w', self.photon_root, self.tools_path], stdout=self.output) retval = process.wait() def finalize_system(self): #Setup the disk process = subprocess.Popen([self.chroot_command, '-w', self.photon_root, self.finalize_command, '-w', self.photon_root], stdout=self.output) retval = process.wait() if self.iso_installer: # just copy the initramfs /boot -> /photon_mnt/boot shutil.copy('/boot/initrd.img-no-kmods', self.photon_root + '/boot/') else: #Build the initramfs process = subprocess.Popen([self.chroot_command, '-w', self.photon_root, './mkinitramfs', '-n', '/boot/initrd.img-no-kmods'], stdout=self.output) retval = process.wait() def install_package(self, package_name): rpm_params = '' if ('type' in self.install_config and (self.install_config['type'] in ['micro', 'minimal'])) or self.install_config['iso_system']: rpm_params = rpm_params + ' --excludedocs ' process = subprocess.Popen([self.chroot_command, '-w', self.photon_root, self.install_package_command, '-w', self.photon_root, package_name, rpm_params], stdout=self.output) return process.wait() def replace_string_in_file(self, filename, search_string, replace_string): with open(filename, "r") as source: lines=source.readlines() with open(filename, "w") as destination: for line in lines: destination.write(re.sub(search_string, replace_string, line)) def update_root_password(self): shadow_password = self.install_config['password'] #replace root blank password in passwd file to point to shadow file self.replace_string_in_file(self.passwd_filename, "root::", "root:x:") if os.path.isfile(self.shadow_filename) == False: with open(self.shadow_filename, "w") as destination: destination.write("root:"+shadow_password+":") else: #add password hash in shadow file self.replace_string_in_file(self.shadow_filename, "root::", "root:"+shadow_password+":") def update_hostname(self): self.hostname = self.install_config['hostname'] outfile = open(self.hostname_file, 'wb') outfile.write(self.hostname) outfile.close() self.replace_string_in_file(self.hosts_file, r'127\.0\.0\.1\s+localhost', '127.0.0.1\tlocalhost\n127.0.0.1\t' + self.hostname) def update_openssh_config(self): if 'public_key' in self.install_config: # Adding the authorized keys if not os.path.exists(self.authorized_keys_dir): os.makedirs(self.authorized_keys_dir) with open(self.authorized_keys_filename, "a") as destination: destination.write(self.install_config['public_key'] + "\n") os.chmod(self.authorized_keys_filename, 0600) # Change the sshd config to allow root login process = subprocess.Popen(["sed", "-i", "s/^\\s*PermitRootLogin\s\+no/PermitRootLogin yes/", self.sshd_config_filename], stdout=self.output) return process.wait()
class SelectDisk(object): def __init__(self, maxy, maxx, install_config): self.install_config = install_config self.menu_items = [] self.maxx = maxx self.maxy = maxy self.win_width = 70 self.win_height = 16 self.win_starty = (self.maxy - self.win_height) / 2 self.win_startx = (self.maxx - self.win_width) / 2 self.menu_starty = self.win_starty + 6 self.menu_height = 5 self.progress_padding = 5 self.progress_width = self.win_width - self.progress_padding self.progress_bar = ProgressBar(self.win_starty + 6, self.win_startx + self.progress_padding / 2, self.progress_width) self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Setup your disk', True) self.devices = Device.refresh_devices() def guided_partitions(self, device_index): menu_height = 9 menu_width = 40 menu_starty = (self.maxy - menu_height) / 2 + 5 confrim_window = ConfirmWindow(menu_height, menu_width, self.maxy, self.maxx, menu_starty, 'This will erase the disk.\nAre you sure?') confirmed = confrim_window.do_action().result['yes'] if not confirmed: return ActionResult(confirmed, None) self.progress_bar.initialize('Partitioning...') self.progress_bar.show() self.progress_bar.show_loading('Partitioning') # Do the partitioning self.window.clearerror() partitions_data = modules.commons.partition_disk(self.devices[device_index].path, modules.commons.default_partitions) if partitions_data == None: self.window.adderror('Partitioning failed, you may try again') else: self.install_config['disk'] = partitions_data self.progress_bar.hide() return ActionResult(partitions_data != None, None) def display(self, params): self.window.addstr(0, 0, 'First, we will setup your disks.\n\nWe have detected {0} disks, choose disk to be auto-partitioned:'.format(len(self.devices))) self.disk_menu_items = [] # Fill in the menu items for index, device in enumerate(self.devices): #if index > 0: self.disk_menu_items.append( ( '{2} - {1} @ {0}'.format(device.path, device.size, device.model), self.guided_partitions, index ) ) self.disk_menu = Menu(self.menu_starty, self.maxx, self.disk_menu_items, self.menu_height) self.window.set_action_panel(self.disk_menu) return self.window.do_action()
class Installer(object): """ Photon installer """ mount_command = os.path.dirname(__file__) + "/mk-mount-disk.sh" finalize_command = "./mk-finalize-system.sh" chroot_command = os.path.dirname(__file__) + "/mk-run-chroot.sh" unmount_disk_command = os.path.dirname(__file__) + "/mk-unmount-disk.sh" default_partitions = [{"mountpoint": "/", "size": 0, "filesystem": "ext4"}] def __init__(self, install_config, maxy=0, maxx=0, iso_installer=False, rpm_path=os.path.dirname(__file__) + "/../stage/RPMS", log_path=os.path.dirname(__file__) + "/../stage/LOGS", log_level="info"): self.exiting = False self.install_config = install_config self.install_config['iso_installer'] = iso_installer self.rpm_path = rpm_path self.logger = Logger.get_logger(log_path, log_level, not iso_installer) self.cmd = CommandUtils(self.logger) if 'working_directory' in self.install_config: self.working_directory = self.install_config['working_directory'] else: self.working_directory = "/mnt/photon-root" self.cmd.run(['mkdir', '-p', self.working_directory]) if 'prepare_script' in self.install_config: self.prepare_command = self.install_config['prepare_script'] else: self.prepare_command = os.path.dirname( __file__) + "/mk-prepare-system.sh" self.photon_root = self.working_directory + "/photon-chroot" self.installer_path = os.path.dirname(os.path.abspath(__file__)) self.tdnf_conf_path = self.working_directory + "/tdnf.conf" self.tdnf_repo_path = self.working_directory + "/photon-local.repo" self.rpm_cache_dir = self.photon_root + '/cache/tdnf/photon-local/rpms' # used by tdnf.conf as cachedir=, tdnf will append the rest self.rpm_cache_dir_short = self.photon_root + '/cache/tdnf' if 'setup_grub_script' in self.install_config: self.setup_grub_command = self.install_config['setup_grub_script'] else: self.setup_grub_command = os.path.dirname( __file__) + "/mk-setup-grub.sh" self.rpms_tobeinstalled = None if self.install_config['iso_installer']: #initializing windows height = 10 width = 75 progress_padding = 5 progress_width = width - progress_padding starty = (maxy - height) // 2 startx = (maxx - width) // 2 self.window = Window(height, width, maxy, maxx, 'Installing Photon', False) self.progress_bar = ProgressBar(starty + 3, startx + progress_padding // 2, progress_width) signal.signal(signal.SIGINT, self.exit_gracefully) def install(self): """ Install photon system and handle exception """ if self.install_config['iso_installer']: self.window.show_window() self.progress_bar.initialize('Initializing installation...') self.progress_bar.show() try: return self._unsafe_install() except Exception as inst: if self.install_config['iso_installer']: self.logger.exception(repr(inst)) self.exit_gracefully() else: raise def _unsafe_install(self): """ Install photon system """ self._format_disk() self._setup_install_repo() self._initialize_system() self._install_packages() self._enable_network_in_chroot() self._finalize_system() self._cleanup_install_repo() self._execute_modules(modules.commons.POST_INSTALL) self._post_install() self._disable_network_in_chroot() self._cleanup_and_exit() return ActionResult(True, None) def exit_gracefully(self, signal1=None, frame1=None): """ This will be called if the installer interrupted by Ctrl+C, exception or other failures """ del signal1 del frame1 if not self.exiting: self.exiting = True if self.install_config['iso_installer']: self.progress_bar.hide() self.window.addstr( 0, 0, 'Oops, Installer got interrupted.\n\n' + 'Press any key to get to the bash...') self.window.content_window().getch() self._cleanup_install_repo() self._cleanup_and_exit() sys.exit(1) def _cleanup_and_exit(self): """ Unmount the disk, eject cd and exit """ command = [Installer.unmount_disk_command, '-w', self.photon_root] command.extend(self._generate_partitions_param(reverse=True)) retval = self.cmd.run(command) if retval != 0: self.logger.error("Failed to unmount disks") if self.install_config['iso_installer']: self.progress_bar.hide() self.window.addstr( 0, 0, 'Congratulations, Photon has been installed in {0} secs.\n\n' 'Press any key to continue to boot...'.format( self.progress_bar.time_elapsed)) if 'ui_install' in self.install_config: self.window.content_window().getch() self._eject_cdrom() def _create_installrpms_list(self): """ Prepare RPM list and copy rpms """ # prepare the RPMs list json_pkg_to_rpm_map = JsonWrapper( self.install_config["pkg_to_rpm_map_file"]) pkg_to_rpm_map = json_pkg_to_rpm_map.read() self.rpms_tobeinstalled = [] selected_packages = self.install_config['packages'] for pkg in selected_packages: versionindex = pkg.rfind("-") if versionindex == -1: raise Exception("Invalid pkg name: " + pkg) package = pkg[:versionindex] if pkg in pkg_to_rpm_map: if pkg_to_rpm_map[pkg]['rpm'] is not None: name = pkg_to_rpm_map[pkg]['rpm'] basename = os.path.basename(name) self.rpms_tobeinstalled.append({ 'filename': basename, 'path': name, 'package': package }) def _copy_files(self): """ Copy the rpm files and instal scripts. """ # Make the photon_root directory if not exits retval = self.cmd.run(['mkdir', '-p', self.photon_root]) if retval != 0: self.logger.error("Fail to create the root directory") self.exit_gracefully() # Copy the installer files retval = self.cmd.run( ['cp', '-r', os.path.dirname(__file__), self.photon_root]) if retval != 0: self.logger.error("Fail to copy install scripts") self.exit_gracefully() self._create_installrpms_list() def _bind_installer(self): """ Make the photon_root/installer directory if not exits The function finalize_system will access the file /installer/mk-finalize-system.sh after chroot to photon_root. Bind the /installer folder to self.photon_root/installer, so that after chroot to photon_root, the file can still be accessed as /installer/mk-finalize-system.sh. """ # Make the photon_root/installer directory if not exits if (self.cmd.run( ['mkdir', '-p', os.path.join(self.photon_root, "installer")]) != 0 or self.cmd.run([ 'mount', '--bind', self.installer_path, os.path.join(self.photon_root, "installer") ]) != 0): self.logger.error("Fail to bind installer") self.exit_gracefully() def _unbind_installer(self): # unmount the installer directory retval = self.cmd.run( ['umount', os.path.join(self.photon_root, "installer")]) if retval != 0: self.logger.error("Fail to unbind the installer directory") # remove the installer directory retval = self.cmd.run( ['rm', '-rf', os.path.join(self.photon_root, "installer")]) if retval != 0: self.logger.error("Fail to remove the installer directory") def _bind_repo_dir(self): """ Bind repo dir for tdnf installation """ if self.rpm_path.startswith("https://") or self.rpm_path.startswith( "http://"): return if (self.cmd.run(['mkdir', '-p', self.rpm_cache_dir]) != 0 or self.cmd.run([ 'mount', '--bind', self.rpm_path, self.rpm_cache_dir ]) != 0): self.logger.error("Fail to bind cache rpms") self.exit_gracefully() def _unbind_repo_dir(self): """ Unbind repo dir after installation """ if self.rpm_path.startswith("https://") or self.rpm_path.startswith( "http://"): return if (self.cmd.run(['umount', self.rpm_cache_dir]) != 0 or self.cmd.run(['rm', '-rf', self.rpm_cache_dir]) != 0): self.logger.error("Fail to unbind cache rpms") def _update_fstab(self): """ update fstab """ with open(os.path.join(self.photon_root, "etc/fstab"), "w") as fstab_file: fstab_file.write("#system\tmnt-pt\ttype\toptions\tdump\tfsck\n") for partition in self.install_config['disk']['partitions']: options = 'defaults' dump = 1 fsck = 2 if 'mountpoint' in partition and partition['mountpoint'] == '/': options = options + ',barrier,noatime,noacl,data=ordered' fsck = 1 if partition['filesystem'] == 'swap': mountpoint = 'swap' dump = 0 fsck = 0 else: mountpoint = partition['mountpoint'] fstab_file.write("{}\t{}\t{}\t{}\t{}\t{}\n".format( partition['path'], mountpoint, partition['filesystem'], options, dump, fsck)) # Add the cdrom entry fstab_file.write( "/dev/cdrom\t/mnt/cdrom\tiso9660\tro,noauto\t0\t0\n") def _generate_partitions_param(self, reverse=False): """ Generate partition param for mount command """ if reverse: step = -1 else: step = 1 params = [] for partition in self.install_config['disk']['partitions'][::step]: if partition["filesystem"] == "swap": continue params.extend([ '--partitionmountpoint', partition["path"], partition["mountpoint"] ]) return params def _initialize_system(self): """ Prepare the system to install photon """ #Setup the disk command = [Installer.mount_command, '-w', self.photon_root] command.extend(self._generate_partitions_param()) retval = self.cmd.run(command) if retval != 0: self.logger.info("Failed to setup the disk for installation") self.exit_gracefully() if self.install_config['iso_installer']: self.progress_bar.update_message('Initializing system...') self._bind_installer() self._bind_repo_dir() retval = self.cmd.run( [self.prepare_command, '-w', self.photon_root, 'install']) if retval != 0: self.logger.info( "Failed to bind the installer and repo needed by tdnf") self.exit_gracefully() else: self._copy_files() #Setup the filesystem basics retval = self.cmd.run( [self.prepare_command, '-w', self.photon_root, self.rpm_path]) if retval != 0: self.logger.info("Failed to setup the file systems basics") self.exit_gracefully() def _finalize_system(self): """ Finalize the system after the installation """ #Setup the disk retval = self.cmd.run([ Installer.chroot_command, '-w', self.photon_root, Installer.finalize_command, '-w', self.photon_root ]) if retval != 0: self.logger.error( "Fail to setup the target system after the installation") def _cleanup_install_repo(self): if self.install_config['iso_installer']: self._unbind_installer() self._unbind_repo_dir() # Disable the swap file retval = self.cmd.run( ['swapoff', self.photon_root + '/cache/swapfile']) if retval != 0: self.logger.error("Fail to swapoff") # remove the tdnf cache directory and the swapfile. retval = self.cmd.run( ['rm', '-rf', os.path.join(self.photon_root, "cache")]) if retval != 0: self.logger.error("Fail to remove the cache") def _post_install(self): # install grub if 'boot_partition_number' not in self.install_config['disk']: self.install_config['disk']['boot_partition_number'] = 1 retval = self.cmd.run([ self.setup_grub_command, '-w', self.photon_root, self.install_config.get('boot', 'bios'), self.install_config['disk']['disk'], self.install_config['disk']['root'], self.install_config['disk']['boot'], self.install_config['disk']['bootdirectory'], str(self.install_config['disk']['boot_partition_number']) ]) if retval != 0: raise Exception("Bootloader (grub2) setup failed") self._update_fstab() if not self.install_config['iso_installer']: retval = self.cmd.run( ['rm', '-rf', os.path.join(self.photon_root, "installer")]) if retval != 0: self.logger.error("Fail to remove the installer directory") def _execute_modules(self, phase): """ Execute the scripts in the modules folder """ sys.path.append( os.path.abspath(os.path.join(os.path.dirname(__file__), "modules"))) modules_paths = glob.glob( os.path.abspath(os.path.join(os.path.dirname(__file__), 'modules')) + '/m_*.py') for mod_path in modules_paths: module = os.path.splitext(os.path.basename(mod_path))[0] try: __import__(module) mod = sys.modules[module] except ImportError: self.logger.error('Error importing module {}'.format(module)) continue # the module default is disabled if not hasattr(mod, 'enabled') or mod.enabled is False: self.logger.info("module {} is not enabled".format(module)) continue # check for the install phase if not hasattr(mod, 'install_phase'): self.logger.error( "Error: can not defind module {} phase".format(module)) continue if mod.install_phase != phase: self.logger.info("Skipping module {0} for phase {1}".format( module, phase)) continue if not hasattr(mod, 'execute'): self.logger.error( "Error: not able to execute module {}".format(module)) continue self.logger.info("Executing: " + module) mod.execute(self) def _adjust_packages_for_vmware_virt(self): """ Install linux_esx on Vmware virtual machine if requested """ try: if self.install_config['install_linux_esx']: regex = re.compile(r'^linux-[0-9]|^initramfs-[0-9]') self.install_config['packages'] = [ x for x in self.install_config['packages'] if not regex.search(x) ] self.install_config['packages'].append('linux-esx') except KeyError: pass def _format_disk(self): """ Partition and format the disk """ # skip partitioning if installer was called from image if not self.install_config['iso_installer']: return self.progress_bar.update_message('Partitioning...') if 'partitions' in self.install_config: partitions = self.install_config['partitions'] else: partitions = Installer.default_partitions # do partitioning partitions_data = self.partition_disk(self.install_config['disk'], partitions) if partitions_data == None: raise Exception("Partitioning failed.") self.install_config['disk'] = partitions_data def _setup_install_repo(self): """ Setup the tdnf repo for installation """ if self.install_config['iso_installer']: keepcache = False with open(self.tdnf_repo_path, "w") as repo_file: repo_file.write("[photon-local]\n") repo_file.write("name=VMWare Photon installer repo\n") if self.rpm_path.startswith( "https://") or self.rpm_path.startswith("http://"): repo_file.write("baseurl={}\n".format( self.rpm_path.replace('/', r'\/'))) else: repo_file.write("baseurl=file://{}\n".format( self.rpm_cache_dir)) keepcache = True repo_file.write("gpgcheck=0\nenabled=1\n") with open(self.tdnf_conf_path, "w") as conf_file: conf_file.writelines([ "[main]\n", "gpgcheck=0\n", "installonly_limit=3\n", "clean_requirements_on_remove=true\n" ]) # baseurl and cachedir are bindmounted to rpm_path, we do not # want input RPMS to be removed after installation. if keepcache: conf_file.write("keepcache=1\n") conf_file.write("repodir={}\n".format(self.working_directory)) conf_file.write("cachedir={}\n".format( self.rpm_cache_dir_short)) def _install_packages(self): """ Install packages using tdnf or rpm command """ if self.install_config['iso_installer']: self._tdnf_install_packages() else: self._rpm_install_packages() def _tdnf_install_packages(self): """ Install packages using tdnf command """ self._adjust_packages_for_vmware_virt() selected_packages = self.install_config['packages'] state = 0 packages_to_install = {} total_size = 0 self.logger.debug("tdnf install --installroot {0} {1} -c {2}".format( self.photon_root, " ".join(selected_packages), self.tdnf_conf_path)) process = subprocess.Popen(['tdnf', 'install'] + selected_packages + [ '--installroot', self.photon_root, '--assumeyes', '-c', self.tdnf_conf_path ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) while True: output = process.stdout.readline().decode() if output == '': retval = process.poll() if retval is not None: break if state == 0: if output == 'Installing:\n': state = 1 elif state == 1: #N A EVR Size(readable) Size(in bytes) if output == '\n': state = 2 self.progress_bar.update_num_items(total_size) else: info = output.split() package = '{0}-{1}.{2}'.format(info[0], info[2], info[1]) packages_to_install[package] = int(info[5]) total_size += int(info[5]) elif state == 2: if output == 'Downloading:\n': self.progress_bar.update_message('Preparing ...') state = 3 elif state == 3: self.progress_bar.update_message(output) if output == 'Running transaction\n': state = 4 else: self.logger.info("[tdnf] {0}".format(output)) prefix = 'Installing/Updating: ' if output.startswith(prefix): package = output[len(prefix):].rstrip('\n') self.progress_bar.increment(packages_to_install[package]) self.progress_bar.update_message(output) # 0 : succeed; 137 : package already installed; 65 : package not found in repo. if retval != 0 and retval != 137: self.logger.error("Failed to install some packages") self.logger.error(process.communicate()[1].decode()) self.exit_gracefully() self.progress_bar.show_loading('Finalizing installation') def _rpm_install_packages(self): """ Install packages using rpm command """ rpms = [] for rpm in self.rpms_tobeinstalled: # We already installed the filesystem in the preparation if rpm['package'] == 'filesystem': continue rpms.append(rpm['filename']) rpms = set(rpms) rpm_paths = [] for root, _, files in os.walk(self.rpm_path): for file in files: if file in rpms: rpm_paths.append(os.path.join(root, file)) # --nodeps is for hosts which do not support rich dependencies rpm_params = [ '--nodeps', '--root', self.photon_root, '--dbpath', '/var/lib/rpm' ] if ('type' in self.install_config and (self.install_config['type'] in ['micro', 'minimal'])): rpm_params.append('--excludedocs') self.logger.info("installing packages {0}, with params {1}".format( rpm_paths, rpm_params)) retval = self.cmd.run(['rpm', '-Uvh'] + rpm_params + rpm_paths) if retval != 0: self.exit_gracefully() def _eject_cdrom(self): """ Eject the cdrom on request """ eject_cdrom = True if 'eject_cdrom' in self.install_config and not self.install_config[ 'eject_cdrom']: eject_cdrom = False if eject_cdrom: self.cmd.run(['eject', '-r']) def _enable_network_in_chroot(self): """ Enable network in chroot """ if os.path.exists("/etc/resolv.conf"): shutil.copy("/etc/resolv.conf", self.photon_root + '/etc/.') def _disable_network_in_chroot(self): """ disable network in chroot """ if os.path.exists(self.photon_root + '/etc/resolv.conf'): os.remove(self.photon_root + '/etc/resolv.conf') def partition_compare(self, p): if 'mountpoint' in p: return (1, len(p['mountpoint']), p['mountpoint']) return (0, 0, "A") def partition_disk(self, disk, partitions): partitions_data = {} partitions_data['disk'] = disk partitions_data['partitions'] = partitions # Clear the disk retval = self.cmd.run(['sgdisk', '-o', '-g', disk]) if retval != 0: self.logger.error("Failed clearing disk {0}".format(disk)) return None # Partitioning the disk extensible_partition = None partitions_count = len(partitions) partition_number = 3 # Add part size and grub flags bios_flag = ':ef02' part_size = '+2M' # Adding the bios partition partition_cmd = ['sgdisk', '-n 1::' + part_size] efi_flag = ':ef00' part_size = '+3M' # Adding the efi partition partition_cmd.extend(['-n 2::' + part_size]) # Adding the known size partitions arch = subprocess.check_output(['uname', '-m'], universal_newlines=True) if "x86" not in arch: partition_number = 2 # Adding the efi partition partition_cmd = ['sgdisk', '-n 1::' + part_size] for partition in partitions: if partition['size'] == 0: # Can not have more than 1 extensible partition if extensible_partition != None: self.logger.error( "Can not have more than 1 extensible partition") return None extensible_partition = partition else: partition_cmd.extend([ '-n', '{}::+{}M'.format(partition_number, partition['size']) ]) partition['partition_number'] = partition_number prefix = '' if 'nvme' in disk or 'mmcblk' in disk: prefix = 'p' partition['path'] = disk + prefix + repr(partition_number) partition_number = partition_number + 1 # Adding the last extendible partition if extensible_partition: partition_cmd.extend( ['-n', repr(extensible_partition['partition_number'])]) partition_cmd.extend(['-p', disk]) # Run the partitioning command retval = self.cmd.run(partition_cmd) if retval != 0: self.logger.error( "Failed partition disk, command: {0}".format(partition_cmd)) return None if "x86" not in arch: retval = self.cmd.run(['sgdisk', '-t1' + efi_flag, disk]) if retval != 0: self.logger.error("Failed to setup efi partition") return None else: retval = self.cmd.run(['sgdisk', '-t1' + bios_flag, disk]) if retval != 0: self.logger.error("Failed to setup bios partition") return None retval = self.cmd.run(['sgdisk', '-t2' + efi_flag, disk]) if retval != 0: self.logger.error("Failed to setup efi partition") return None # Format the filesystem for partition in partitions: if "mountpoint" in partition: if partition['mountpoint'] == '/': partitions_data['root'] = partition['path'] partitions_data['root_partition_number'] = partition[ 'partition_number'] elif partition['mountpoint'] == '/boot': partitions_data['boot'] = partition['path'] partitions_data['boot_partition_number'] = partition[ 'partition_number'] partitions_data['bootdirectory'] = '/' if partition['filesystem'] == "swap": retval = self.cmd.run(['mkswap', partition['path']]) if retval != 0: self.logger.error( "Failed to create swap partition @ {}".format( partition['path'])) return None else: mkfs_cmd = [ 'mkfs', '-t', partition['filesystem'], partition['path'] ] retval = self.cmd.run(mkfs_cmd) if retval != 0: self.logger.error( "Failed to format {} partition @ {}".format( partition['filesystem'], partition['path'])) return None # Check if there is no root partition if 'root' not in partitions_data: self.logger.error("There is no partition assigned to root '/'") return None if 'boot' not in partitions_data: partitions_data['boot'] = partitions_data['root'] partitions_data['boot_partition_number'] = partitions_data[ 'root_partition_number'] partitions_data['bootdirectory'] = '/boot/' partitions.sort(key=lambda p: self.partition_compare(p)) return partitions_data
class SelectDisk(object): def __init__(self, maxy, maxx, install_config): self.install_config = install_config self.menu_items = [] self.maxx = maxx self.maxy = maxy self.win_width = 70 self.win_height = 16 self.win_starty = (self.maxy - self.win_height) // 2 self.win_startx = (self.maxx - self.win_width) // 2 self.menu_starty = self.win_starty + 6 self.menu_height = 5 self.progress_padding = 5 self.progress_width = self.win_width - self.progress_padding self.progress_bar = ProgressBar(self.win_starty + 6, self.win_startx + (self.progress_padding // 2), self.progress_width, new_win=True) self.disk_buttom_items = [] self.disk_buttom_items.append(('<Custom>', self.custom_function, False)) self.disk_buttom_items.append(('<Auto>', self.auto_function, False)) self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Select a disk', True, items = self.disk_buttom_items, menu_helper = self.save_index, position = 2, tab_enabled=False) self.partition_window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Partition', True) self.devices = Device.refresh_devices() def guided_partitions(self, params): if not 'diskindex' in self.install_config: return ActionResult(False, None); device_index = self.install_config['diskindex'] menu_height = 9 menu_width = 40 menu_starty = (self.maxy - menu_height) // 2 + 5 self.install_config['delete_partition'] = True confrim_window = ConfirmWindow(menu_height, menu_width, self.maxy, self.maxx, menu_starty, 'This will erase the disk.\nAre you sure?') confirmed = confrim_window.do_action().result['yes'] if confirmed == False: self.install_config['skipPrevs'] = True return ActionResult(False, {'goBack':True}) self.install_config['skipPrevs'] = False self.progress_bar.initialize('Partitioning...') self.progress_bar.show() self.progress_bar.show_loading('Partitioning') # Do the partitioning if 'partitionsnumber' in self.install_config: if (int(self.install_config['partitionsnumber']) == 0): partitions_data = modules.commons.partition_disk(self.devices[device_index].path, modules.commons.default_partitions) else: partitions = [] for i in range (int (self.install_config['partitionsnumber'])): if len(self.install_config[str(i)+'partition_info'+str(0)])==0: sizedata=0 else: sizedata = int(self.install_config[str(i)+'partition_info'+str(0)]) mtdata = self.install_config[str(i)+'partition_info'+str(2)] typedata = self.install_config[str(i)+'partition_info'+str(1)] partitions = partitions + [ {"mountpoint": mtdata, "size": sizedata, "filesystem": typedata}, ] partitions_data = modules.commons.partition_disk(self.devices[device_index].path, partitions) else: partitions_data = modules.commons.partition_disk(self.devices[device_index].path, modules.commons.default_partitions) if partitions_data == None: self.partition_window.adderror('Partitioning failed, you may try again') else: self.install_config['disk'] = partitions_data self.progress_bar.hide() return ActionResult(partitions_data != None, None) def display(self, params): if 'skipPrevs' in self.install_config: self.install_config['skipPrevs'] = False self.window.addstr(0, 0, 'Please select a disk and a method how to partition it:\nAuto - single partition for /, no swap partition.\nCustom - for customized partitioning') self.disk_menu_items = [] # Fill in the menu items for index, device in enumerate(self.devices): #if index > 0: self.disk_menu_items.append( ( '{2} - {1} @ {0}'.format(device.path, device.size, device.model), self.save_index, index ) ) self.disk_menu = Menu(self.menu_starty, self.maxx, self.disk_menu_items, self.menu_height, tab_enable=False) self.disk_menu.can_save_sel(True) self.window.set_action_panel(self.disk_menu) return self.window.do_action() def save_index(self, device_index): self.install_config['diskindex'] = device_index return ActionResult(True, None) def auto_function(self, params): #default is no partition self.install_config['autopartition'] = True self.install_config['partitionsnumber'] = 0 return ActionResult(True, None) def custom_function(self, params): #custom minimize partition number is 1 self.install_config['autopartition'] = False return ActionResult(True, None)
class SelectDisk(object): def __init__(self, maxy, maxx, install_config): self.install_config = install_config self.menu_items = [] self.maxx = maxx self.maxy = maxy self.win_width = 70 self.win_height = 16 self.win_starty = (self.maxy - self.win_height) / 2 self.win_startx = (self.maxx - self.win_width) / 2 self.menu_starty = self.win_starty + 6 self.menu_height = 5 self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Setup your disk', True) self.devices = Device.refresh_devices() def guided_partitions(self, device_index): menu_height = 9 menu_width = 40 menu_starty = (self.maxy - menu_height) / 2 + 5 confrim_window = ConfirmWindow(menu_height, menu_width, self.maxy, self.maxx, menu_starty, 'This will erase the disk.\nAre you sure?') confirmed = confrim_window.do_action().result['yes'] if not confirmed: return ActionResult(confirmed, None) #self.install_config['disk'] = self.devices[device_index].path #return ActionResult(True, None) # Do the partitioning self.window.clearerror() json_ret = subprocess.check_output(['gpartedbin', 'defaultpartitions', self.devices[device_index].path], stderr=open(os.devnull, 'w')) json_dct = json.loads(json_ret) if json_dct['success']: self.install_config['disk'] = json_dct['data'] else: self.window.adderror('Partitioning failed, you may try again') return ActionResult(json_dct['success'], None) def display(self, params): self.window.addstr(0, 0, 'First, we will setup your disks.\n\nWe have detected {0} disks, choose disk to be auto-partitioned:'.format(len(self.devices))) self.disk_menu_items = [] # Fill in the menu items for index, device in enumerate(self.devices): #if index > 0: self.disk_menu_items.append( ( '{2} - {1} MB @ {0}'.format(device.path, device.size, device.model), self.guided_partitions, index ) ) self.disk_menu = Menu(self.menu_starty, self.maxx, self.disk_menu_items, self.menu_height) self.window.set_action_panel(self.disk_menu) return self.window.do_action()
class Installer(object): def __init__(self, install_config, maxy=0, maxx=0, iso_installer=False, rpm_path="../stage/RPMS", log_path="../stage/LOGS", ks_config=None): self.install_config = install_config self.ks_config = ks_config self.iso_installer = iso_installer self.rpm_path = rpm_path self.log_path = log_path self.mount_command = "./mk-mount-disk.sh" self.prepare_command = "./mk-prepare-system.sh" self.finalize_command = "./mk-finalize-system.sh" self.install_package_command = "./mk-install-package.sh" self.chroot_command = "./mk-run-chroot.sh" self.setup_grub_command = "./mk-setup-grub.sh" self.unmount_disk_command = "./mk-unmount-disk.sh" if self.iso_installer: self.working_directory = "/mnt/photon-root" elif 'working_directory' in self.install_config: self.working_directory = self.install_config['working_directory'] else: self.working_directory = "/mnt/photon-root" self.photon_root = self.working_directory + "/photon-chroot" self.restart_command = "shutdown" if self.iso_installer: self.output = open(os.devnull, 'w') else: self.output = None if self.iso_installer: #initializing windows self.maxy = maxy self.maxx = maxx self.height = 10 self.width = 75 self.progress_padding = 5 self.progress_width = self.width - self.progress_padding self.starty = (self.maxy - self.height) / 2 self.startx = (self.maxx - self.width) / 2 self.window = Window(self.height, self.width, self.maxy, self.maxx, 'Installing Photon', False) self.progress_bar = ProgressBar( self.starty + 3, self.startx + self.progress_padding / 2, self.progress_width) signal.signal(signal.SIGINT, self.exit_gracefully) # This will be called if the installer interrupted by Ctrl+C or exception def exit_gracefully(self, signal, frame): if self.iso_installer: self.progress_bar.hide() self.window.addstr( 0, 0, 'Opps, Installer got inturrupted.\n\nPress any key to get to the bash...' ) self.window.content_window().getch() sys.exit(1) def install(self, params): try: return self.unsafe_install(params) except: if self.iso_installer: self.exit_gracefully(None, None) else: raise def unsafe_install(self, params): if self.iso_installer: self.window.show_window() self.progress_bar.initialize('Initializing installation...') self.progress_bar.show() self.execute_modules(modules.commons.PRE_INSTALL) self.initialize_system() #install packages for rpm in self.rpms_tobeinstalled: # We already installed the filesystem in the preparation if rpm['package'] == 'filesystem': continue if self.iso_installer: self.progress_bar.update_message('Installing {0}...'.format( rpm['package'])) return_value = self.install_package(rpm['filename']) if return_value != 0: self.exit_gracefully(None, None) if self.iso_installer: self.progress_bar.increment(rpm['size'] * self.install_factor) if self.iso_installer: self.progress_bar.show_loading('Finalizing installation') self.finalize_system() if not self.install_config['iso_system']: # Execute post installation modules self.execute_modules(modules.commons.POST_INSTALL) # install grub try: if self.install_config['boot'] == 'bios': process = subprocess.Popen([ self.setup_grub_command, '-w', self.photon_root, "bios", self.install_config['disk']['disk'], self.install_config['disk']['root'] ], stdout=self.output) elif self.install_config['boot'] == 'efi': process = subprocess.Popen([ self.setup_grub_command, '-w', self.photon_root, "efi", self.install_config['disk']['disk'], self.install_config['disk']['root'] ], stdout=self.output) except: #install bios if variable is not set. process = subprocess.Popen([ self.setup_grub_command, '-w', self.photon_root, "bios", self.install_config['disk']['disk'], self.install_config['disk']['root'] ], stdout=self.output) retval = process.wait() process = subprocess.Popen( [self.unmount_disk_command, '-w', self.photon_root], stdout=self.output) retval = process.wait() if self.iso_installer: self.progress_bar.hide() self.window.addstr( 0, 0, 'Congratulations, Photon has been installed in {0} secs.\n\nPress any key to continue to boot...' .format(self.progress_bar.time_elapsed)) if self.ks_config == None: self.window.content_window().getch() return ActionResult(True, None) def download_file(self, url, directory): # TODO: Add errors handling urlopener = urllib.URLopener() urlopener.retrieve(url, os.path.join(directory, os.path.basename(url))) def download_rpms(self): repodata_dir = os.path.join(self.photon_root, 'RPMS/repodata') process = subprocess.Popen(['mkdir', '-p', repodata_dir], stdout=self.output) retval = process.wait() import hawkey self.install_factor = 1 # Load the repo data sack = hawkey.Sack() repomd_filename = "repomd.xml" repomd_url = os.path.join(self.rpm_path, "repodata/repomd.xml") self.download_file(repomd_url, repodata_dir) # parse to the xml to get the primary and files list tree = ET.parse(os.path.join(repodata_dir, repomd_filename)) # TODO: Get the namespace dynamically from the xml file ns = {'ns': 'http://linux.duke.edu/metadata/repo'} primary_location = tree.find("./ns:data[@type='primary']/ns:location", ns).get("href") filelists_location = tree.find( "./ns:data[@type='filelists']/ns:location", ns).get("href") primary_filename = os.path.basename(primary_location) filelists_filename = os.path.basename(filelists_location) self.download_file(os.path.join(self.rpm_path, primary_location), repodata_dir) self.download_file(os.path.join(self.rpm_path, filelists_location), repodata_dir) repo = hawkey.Repo("installrepo") repo.repomd_fn = os.path.join(repodata_dir, repomd_filename) repo.primary_fn = os.path.join(repodata_dir, primary_filename) repo.filelists_fn = os.path.join(repodata_dir, filelists_filename) sack.load_yum_repo(repo, load_filelists=True) progressbar_num_items = 0 self.rpms_tobeinstalled = [] selected_packages = self.install_config['packages'] for package in selected_packages: # Locate the package q = hawkey.Query(sack).filter(name=package) if (len(q) > 0): progressbar_num_items += q[ 0].size + q[0].size * self.install_factor self.rpms_tobeinstalled.append({ 'package': package, 'size': q[0].size, 'location': q[0].location, 'filename': os.path.basename(q[0].location) }) else: print >> sys.stderr, "Package %s not found in the repo" % package #self.exit_gracefully(None, None) self.progress_bar.update_num_items(progressbar_num_items) # Download the rpms for rpm in self.rpms_tobeinstalled: message = 'Downloading {0}...'.format(rpm['filename']) self.progress_bar.update_message(message) self.download_file(os.path.join(self.rpm_path, rpm['location']), os.path.join(self.photon_root, "RPMS")) self.progress_bar.increment(rpm['size']) # update the rpms path self.rpm_path = os.path.join(self.photon_root, "RPMS") def copy_rpms(self): # prepare the RPMs list self.install_factor = 3 rpms = [] for root, dirs, files in os.walk(self.rpm_path): for name in files: file = os.path.join(root, name) size = os.path.getsize(file) rpms.append({'filename': name, 'path': file, 'size': size}) progressbar_num_items = 0 self.rpms_tobeinstalled = [] selected_packages = self.install_config['packages'] for package in selected_packages: pattern = package + '-[0-9]*.rpm' for rpm in rpms: if fnmatch.fnmatch(rpm['filename'], pattern): rpm['package'] = package self.rpms_tobeinstalled.append(rpm) progressbar_num_items += rpm[ 'size'] + rpm['size'] * self.install_factor break if self.iso_installer: self.progress_bar.update_num_items(progressbar_num_items) # Copy the rpms for rpm in self.rpms_tobeinstalled: if self.iso_installer: message = 'Copying {0}...'.format(rpm['filename']) self.progress_bar.update_message(message) shutil.copy(rpm['path'], self.photon_root + '/RPMS/') if self.iso_installer: self.progress_bar.increment(rpm['size']) def copy_files(self): # Make the photon_root directory if not exits process = subprocess.Popen(['mkdir', '-p', self.photon_root], stdout=self.output) retval = process.wait() # Copy the installer files process = subprocess.Popen( ['cp', '-r', "../installer", self.photon_root], stdout=self.output) retval = process.wait() # Create the rpms directory process = subprocess.Popen(['mkdir', '-p', self.photon_root + '/RPMS'], stdout=self.output) retval = process.wait() if self.rpm_path.startswith("http://"): self.download_rpms() else: self.copy_rpms() def initialize_system(self): #Setup the disk if (not self.install_config['iso_system']): process = subprocess.Popen([ self.mount_command, '-w', self.photon_root, self.install_config['disk']['root'] ], stdout=self.output) retval = process.wait() self.copy_files() #Setup the filesystem basics process = subprocess.Popen( [self.prepare_command, '-w', self.photon_root], stdout=self.output) retval = process.wait() def finalize_system(self): #Setup the disk process = subprocess.Popen([ self.chroot_command, '-w', self.photon_root, self.finalize_command, '-w', self.photon_root ], stdout=self.output) retval = process.wait() initrd_dir = 'boot' initrd_file_name = 'initrd.img-no-kmods' if self.iso_installer: # just copy the initramfs /boot -> /photon_mnt/boot shutil.copy(os.path.join(initrd_dir, initrd_file_name), self.photon_root + '/boot/') # remove the installer directory process = subprocess.Popen( ['rm', '-rf', os.path.join(self.photon_root, "installer")], stdout=self.output) retval = process.wait() else: #Build the initramfs by passing in the kernel version version_string = '' for root, dirs, files in os.walk(self.rpm_path): for name in files: if fnmatch.fnmatch(name, 'linux-[0-9]*.rpm'): version_array = name.split('-') if len(version_array) > 2: version_string = version_array[1] if 'initrd_dir' in self.install_config: initrd_dir = self.install_config['initrd_dir'] process = subprocess.Popen([ self.chroot_command, '-w', self.photon_root, './mkinitramfs', '-n', os.path.join(initrd_dir, initrd_file_name), '-k', version_string ], stdout=self.output) retval = process.wait() def install_package(self, package_name): rpm_params = '' os.environ["RPMROOT"] = self.rpm_path rpm_params = rpm_params + ' --force ' rpm_params = rpm_params + ' --root ' + self.photon_root rpm_params = rpm_params + ' --dbpath /var/lib/rpm ' if ('type' in self.install_config and (self.install_config['type'] in ['micro', 'minimal'])) or self.install_config['iso_system']: rpm_params = rpm_params + ' --excludedocs ' process = subprocess.Popen([ self.install_package_command, '-w', self.photon_root, package_name, rpm_params ], stdout=self.output) return process.wait() def execute_modules(self, phase): modules = glob.glob('modules/m_*.py') for mod_path in modules: module = mod_path.replace('/', '.', 1) module = os.path.splitext(module)[0] try: __import__(module) mod = sys.modules[module] except ImportError: print >> sys.stderr, 'Error importing module %s' % module continue # the module default is disabled if not hasattr(mod, 'enabled') or mod.enabled == False: print >> sys.stderr, "module %s is not enabled" % module continue # check for the install phase if not hasattr(mod, 'install_phase'): print >> sys.stderr, "Error: can not defind module %s phase" % module continue if mod.install_phase != phase: print >> sys.stderr, "Skipping module %s for phase %s" % ( module, phase) continue if not hasattr(mod, 'execute'): print >> sys.stderr, "Error: not able to execute module %s" % module continue mod.execute(module, self.ks_config, self.install_config, self.photon_root) def run(self, command, comment=None): if comment != None: print >> sys.stderr, "Installer: {} ".format(comment) self.progress_bar.update_loading_message(comment) print >> sys.stderr, "Installer: {} ".format(command) process = subprocess.Popen([command], shell=True, stdout=self.output) retval = process.wait() return retval
class SelectDisk(object): def __init__(self, maxy, maxx, install_config): self.install_config = install_config self.menu_items = [] self.maxx = maxx self.maxy = maxy self.win_width = 70 self.win_height = 16 self.win_starty = (self.maxy - self.win_height) / 2 self.win_startx = (self.maxx - self.win_width) / 2 self.menu_starty = self.win_starty + 6 self.menu_height = 5 self.progress_padding = 5 self.progress_width = self.win_width - self.progress_padding self.progress_bar = ProgressBar(self.win_starty + 6, self.win_startx + self.progress_padding / 2, self.progress_width, new_win=True) self.disk_buttom_items = [] self.disk_buttom_items.append(('<Custom>', self.custom_function, False)) self.disk_buttom_items.append(('<Auto>', self.auto_function, False)) self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Select a disk', True, items = self.disk_buttom_items, menu_helper = self.save_index, position = 2, tab_enabled=False) self.partition_window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Partition', True) self.devices = Device.refresh_devices() def guided_partitions(self, params): if not 'diskindex' in self.install_config: return ActionResult(False, None); device_index = self.install_config['diskindex'] menu_height = 9 menu_width = 40 menu_starty = (self.maxy - menu_height) / 2 + 5 self.install_config['delete_partition'] = True confrim_window = ConfirmWindow(menu_height, menu_width, self.maxy, self.maxx, menu_starty, 'This will erase the disk.\nAre you sure?') confirmed = confrim_window.do_action().result['yes'] if confirmed == False: self.install_config['skipPrevs'] = True return ActionResult(False, {'goBack':True}) self.install_config['skipPrevs'] = False self.progress_bar.initialize('Partitioning...') self.progress_bar.show() self.progress_bar.show_loading('Partitioning') # Do the partitioning if 'partitionsnumber' in self.install_config: if (int(self.install_config['partitionsnumber']) == 0): partitions_data = modules.commons.partition_disk(self.devices[device_index].path, modules.commons.default_partitions) else: partitions = [] for i in range (int (self.install_config['partitionsnumber'])): if len(self.install_config[str(i)+'partition_info'+str(0)])==0: sizedata=0 else: sizedata = int(self.install_config[str(i)+'partition_info'+str(0)]) mtdata = self.install_config[str(i)+'partition_info'+str(2)] typedata = self.install_config[str(i)+'partition_info'+str(1)] partitions = partitions + [ {"mountpoint": mtdata, "size": sizedata, "filesystem": typedata}, ] partitions_data = modules.commons.partition_disk(self.devices[device_index].path, partitions) else: partitions_data = modules.commons.partition_disk(self.devices[device_index].path, modules.commons.default_partitions) if partitions_data == None: self.partition_window.adderror('Partitioning failed, you may try again') else: self.install_config['disk'] = partitions_data self.progress_bar.hide() return ActionResult(partitions_data != None, None) def display(self, params): if 'skipPrevs' in self.install_config: self.install_config['skipPrevs'] = False self.window.addstr(0, 0, 'Please select a disk and a method how to partition it:\nAuto - single partition for /, no swap partition.\nCustom - for customized partitioning') self.disk_menu_items = [] # Fill in the menu items for index, device in enumerate(self.devices): #if index > 0: self.disk_menu_items.append( ( '{2} - {1} @ {0}'.format(device.path, device.size, device.model), self.save_index, index ) ) self.disk_menu = Menu(self.menu_starty, self.maxx, self.disk_menu_items, self.menu_height, tab_enable=False) self.disk_menu.can_save_sel(True) self.window.set_action_panel(self.disk_menu) return self.window.do_action() def save_index(self, device_index): self.install_config['diskindex'] = device_index return ActionResult(True, None) def auto_function(self, params): #default is no partition self.install_config['autopartition'] = True self.install_config['partitionsnumber'] = 0 return ActionResult(True, None) def custom_function(self, params): #custom minimize partition number is 1 self.install_config['autopartition'] = False return ActionResult(True, None)
class SelectDisk(object): def __init__(self, maxy, maxx, install_config): self.install_config = install_config self.menu_items = [] self.maxx = maxx self.maxy = maxy self.win_width = 70 self.win_height = 16 self.win_starty = (self.maxy - self.win_height) / 2 self.win_startx = (self.maxx - self.win_width) / 2 self.menu_starty = self.win_starty + 6 self.menu_height = 5 self.progress_padding = 5 self.progress_width = self.win_width - self.progress_padding self.progress_bar = ProgressBar( self.win_starty + 6, self.win_startx + self.progress_padding / 2, self.progress_width) self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Setup your disk', True) self.devices = Device.refresh_devices() def guided_partitions(self, device_index): menu_height = 9 menu_width = 40 menu_starty = (self.maxy - menu_height) / 2 + 5 confrim_window = ConfirmWindow( menu_height, menu_width, self.maxy, self.maxx, menu_starty, 'This will erase the disk.\nAre you sure?') confirmed = confrim_window.do_action().result['yes'] if not confirmed: return ActionResult(confirmed, None) self.progress_bar.initialize('Partitioning...') self.progress_bar.show() self.progress_bar.show_loading('Partitioning') # Do the partitioning self.window.clearerror() partitions_data = modules.commons.partition_disk( self.devices[device_index].path) if partitions_data == None: self.window.adderror('Partitioning failed, you may try again') else: self.install_config['disk'] = partitions_data self.progress_bar.hide() return ActionResult(partitions_data != None, None) def display(self, params): self.window.addstr( 0, 0, 'First, we will setup your disks.\n\nWe have detected {0} disks, choose disk to be auto-partitioned:' .format(len(self.devices))) self.disk_menu_items = [] # Fill in the menu items for index, device in enumerate(self.devices): #if index > 0: self.disk_menu_items.append( ('{2} - {1} @ {0}'.format(device.path, device.size, device.model), self.guided_partitions, index)) self.disk_menu = Menu(self.menu_starty, self.maxx, self.disk_menu_items, self.menu_height) self.window.set_action_panel(self.disk_menu) return self.window.do_action()
class PartitionISO(object): def __init__(self, maxy, maxx, install_config): self.maxx = maxx self.maxy = maxy self.win_width = maxx - 4 self.win_height = maxy - 4 self.install_config = install_config self.path_checker = [] self.win_starty = (self.maxy - self.win_height) // 2 self.win_startx = (self.maxx - self.win_width) // 2 self.text_starty = self.win_starty + 4 self.text_height = self.win_height - 6 self.text_width = self.win_width - 6 self.install_config['partitionsnumber'] = 0 self.devices = Device.refresh_devices_bytes() self.has_slash = False self.has_remain = False self.has_empty = False self.disk_size = [] for index, device in enumerate(self.devices): self.disk_size.append((device.path, int(device.size) / 1048576)) self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Welcome to the Photon installer', False, can_go_next=False) Device.refresh_devices() def display(self, params): if 'skipPrevs' in self.install_config and self.install_config['skipPrevs'] == True: self.delete() return ActionResult(False, {'goBack':True}) if 'autopartition' in self.install_config and self.install_config['autopartition'] == True: return ActionResult(True, None) if ('delete_partition' in self.install_config and self.install_config['delete_partition'] == True): self.delete() self.install_config['delete_partition'] = False self.device_index = self.install_config['diskindex'] self.disk_buttom_items = [] self.disk_buttom_items.append(('<Next>', self.next)) self.disk_buttom_items.append(('<Create New>', self.create_function)) self.disk_buttom_items.append(('<Delete All>', self.delete_function)) self.disk_buttom_items.append(('<Go Back>', self.go_back)) self.text_items = [] self.text_items.append(('Disk', 20)) self.text_items.append(('Size', 5)) self.text_items.append(('Type', 5)) self.text_items.append(('Mountpoint', 20)) self.table_space = 5 title = 'Current partitions:\n' self.window.addstr(0, (self.win_width - len(title)) // 2, title) info = ("Unpartitioned space: " + str(self.disk_size[self.device_index][1])+ " MB, Total size: "+ str(int(self.devices[self.device_index].size)/ 1048576) + " MB") self.text_pane = TextPane(self.text_starty, self.maxx, self.text_width, "EULA.txt", self.text_height, self.disk_buttom_items, partition=True, popupWindow=True, install_config=self.install_config, text_items=self.text_items, table_space=self.table_space, default_start=1, info=info, size_left=str(self.disk_size[self.device_index][1])) self.window.set_action_panel(self.text_pane) return self.window.do_action() def validate_partition(self, pstr): if not pstr: return ActionResult(False, None) sizedata = pstr[0] mtdata = pstr[2] typedata = pstr[1] devicedata = self.devices[self.device_index].path #no empty fields unless swap if (typedata == 'swap' and (len(mtdata) != 0 or len(typedata) == 0 or len(devicedata) == 0)): return False, "invalid swap data " if (typedata != 'swap' and (len(sizedata) == 0 or len(mtdata) == 0 or len(typedata) == 0 or len(devicedata) == 0)): if not self.has_empty and mtdata and typedata and devicedata: self.has_empty = True else: return False, "Input cannot be empty" if typedata != 'swap' and typedata != 'ext3' and typedata != 'ext4': return False, "Invalid type" if len(mtdata) != 0 and mtdata[0] != '/': return False, "Invalid path" if mtdata in self.path_checker: return False, "Path already existed" #validate disk: must be one of the existing disks i = self.device_index #valid size: must not exceed memory limit curr_size = self.disk_size[i][1] if len(sizedata) != 0: try: int(sizedata) except ValueError: return False, "invalid device size" if int(curr_size) - int(sizedata) < 0: return False, "invalid device size" #if valid, update the size and return true new_size = (self.disk_size[i][0], int(curr_size)- int(sizedata)) self.disk_size[i] = new_size if mtdata == "/": self.has_slash = True self.path_checker.append(mtdata) return True, None def create_function(self): self.window.hide_window() self.install_config['partition_disk'] = self.devices[self.device_index].path self.partition_items = [] self.partition_items.append(('Size in MB: ' + str(self.disk_size[self.device_index][1]) + ' available')) self.partition_items.append(('Type: (ext3, ext4, swap)')) self.partition_items.append(('Mountpoint:')) self.create_window = ReadMulText( self.maxy, self.maxx, 0, self.install_config, str(self.install_config['partitionsnumber']) + 'partition_info', self.partition_items, None, None, None, self.validate_partition, #validation function of the input None, True, ) result = self.create_window.do_action() if result.success: self.install_config['partitionsnumber'] = self.install_config['partitionsnumber'] + 1 #parse the input in install config return self.display(False) def delete_function(self): self.delete() return self.display(False) def go_back(self): self.delete() self.window.hide_window() self.text_pane.hide() return ActionResult(False, {'goBack':True}) def next(self): if self.install_config['partitionsnumber'] == 0: window_height = 9 window_width = 40 window_starty = (self.maxy-window_height) // 2 + 5 confirm_window = ConfirmWindow(window_height, window_width, self.maxy, self.maxx, window_starty, 'Partition information cannot be empty', info=True) confirm_window.do_action() return self.display(False) #must have / if not self.has_slash: window_height = 9 window_width = 40 window_starty = (self.maxy - window_height) // 2 + 5 confirm_window = ConfirmWindow(window_height, window_width, self.maxy, self.maxx, window_starty, 'Missing /', info=True) confirm_window.do_action() return self.display(False) self.window.hide_window() self.text_pane.hide() return ActionResult(True, {'goNext':True}) def delete(self): for i in range(int(self.install_config['partitionsnumber'])): self.install_config[str(i)+'partition_info'+str(0)] = '' self.install_config[str(i)+'partition_info'+str(1)] = '' self.install_config[str(i)+'partition_info'+str(2)] = '' self.install_config[str(i)+'partition_info'+str(3)] = '' del self.disk_size[:] for index, device in enumerate(self.devices): self.disk_size.append((device.path, int(device.size) / 1048576)) del self.path_checker[:] self.has_slash = False self.has_remain = False self.has_empty = False self.install_config['partitionsnumber'] = 0
class Installer(object): def __init__(self, install_config, maxy = 0, maxx = 0, iso_installer = False, tools_path = "../stage", rpm_path = "../stage/RPMS", log_path = "../stage/LOGS", ks_config = None): self.install_config = install_config self.ks_config = ks_config self.iso_installer = iso_installer self.tools_path = tools_path self.rpm_path = rpm_path self.log_path = log_path self.mount_command = "./mk-mount-disk.sh" self.prepare_command = "./mk-prepare-system.sh" self.finalize_command = "./mk-finalize-system.sh" self.install_package_command = "./mk-install-package.sh" self.chroot_command = "./mk-run-chroot.sh" self.setup_grub_command = "./mk-setup-grub.sh" self.unmount_disk_command = "./mk-unmount-disk.sh" if self.iso_installer: self.working_directory = "/mnt/photon-root" elif 'working_directory' in self.install_config: self.working_directory = self.install_config['working_directory'] else: self.working_directory = "/mnt/photon-root" self.photon_root = self.working_directory + "/photon-chroot"; self.restart_command = "shutdown" if self.iso_installer: self.output = open(os.devnull, 'w') else: self.output = None self.install_factor = 3 if self.iso_installer: #initializing windows self.maxy = maxy self.maxx = maxx self.height = 10 self.width = 75 self.progress_padding = 5 self.progress_width = self.width - self.progress_padding self.starty = (self.maxy - self.height) / 2 self.startx = (self.maxx - self.width) / 2 self.window = Window(self.height, self.width, self.maxy, self.maxx, 'Installing Photon', False) self.progress_bar = ProgressBar(self.starty + 3, self.startx + self.progress_padding / 2, self.progress_width) signal.signal(signal.SIGINT, self.exit_gracefully) # This will be called if the installer interrupted by Ctrl+C or exception def exit_gracefully(self, signal, frame): if self.iso_installer: self.progress_bar.hide() self.window.addstr(0, 0, 'Opps, Installer got inturrupted.\n\nPress any key to get to the bash...') self.window.content_window().getch() sys.exit(1) def install(self, params): try: return self.unsafe_install(params) except: if self.iso_installer: self.exit_gracefully(None, None) else: raise def unsafe_install(self, params): if self.iso_installer: self.window.show_window() self.progress_bar.initialize('Initializing installation...') self.progress_bar.show() self.execute_modules(modules.commons.PRE_INSTALL) self.initialize_system() #install packages for rpm in self.rpms_tobeinstalled: # We already installed the filesystem in the preparation if rpm['package'] == 'filesystem': continue if self.iso_installer: self.progress_bar.update_message('Installing {0}...'.format(rpm['package'])) return_value = self.install_package(rpm['package']) if return_value != 0: self.exit_gracefully(None, None) if self.iso_installer: self.progress_bar.increment(rpm['size'] * self.install_factor) if self.iso_installer: self.progress_bar.show_loading('Finalizing installation') self.finalize_system() if not self.install_config['iso_system']: # Execute post installation modules self.execute_modules(modules.commons.POST_INSTALL) # install grub process = subprocess.Popen([self.setup_grub_command, '-w', self.photon_root, self.install_config['disk']['disk'], self.install_config['disk']['root']], stdout=self.output) retval = process.wait() process = subprocess.Popen([self.unmount_disk_command, '-w', self.photon_root], stdout=self.output) retval = process.wait() if self.iso_installer: self.progress_bar.hide() self.window.addstr(0, 0, 'Congratulations, Photon has been installed in {0} secs.\n\nPress any key to continue to boot...'.format(self.progress_bar.time_elapsed)) if self.ks_config == None: self.window.content_window().getch() return ActionResult(True, None) def copy_rpms(self): # prepare the RPMs list rpms = [] for root, dirs, files in os.walk(self.rpm_path): for name in files: file = os.path.join(root, name) size = os.path.getsize(file) rpms.append({'name': name, 'path': file, 'size': size}) progressbar_num_items = 0 self.rpms_tobeinstalled = [] selected_packages = self.install_config['packages'] for package in selected_packages: pattern = package + '-[0-9]*.rpm' for rpm in rpms: if fnmatch.fnmatch(rpm['name'], pattern): rpm['package'] = package self.rpms_tobeinstalled.append(rpm) progressbar_num_items += rpm['size'] + rpm['size'] * self.install_factor break process = subprocess.Popen(['mkdir', '-p', self.photon_root + '/RPMS'], stdout=self.output) retval = process.wait() if self.iso_installer: self.progress_bar.update_num_items(progressbar_num_items) # Copy the rpms for rpm in self.rpms_tobeinstalled: shutil.copy(rpm['path'], self.photon_root + '/RPMS/') if self.iso_installer: self.progress_bar.increment(rpm['size']) def copy_files(self): # Make the photon_root directory if not exits process = subprocess.Popen(['mkdir', '-p', self.photon_root], stdout=self.output) retval = process.wait() # Copy the installer files process = subprocess.Popen(['cp', '-r', "../installer", self.photon_root], stdout=self.output) retval = process.wait() self.copy_rpms() def initialize_system(self): #Setup the disk if (not self.install_config['iso_system']): process = subprocess.Popen([self.mount_command, '-w', self.photon_root, self.install_config['disk']['root']], stdout=self.output) retval = process.wait() self.copy_files() #Setup the filesystem basics process = subprocess.Popen([self.prepare_command, '-w', self.photon_root, self.tools_path], stdout=self.output) retval = process.wait() def finalize_system(self): #Setup the disk process = subprocess.Popen([self.chroot_command, '-w', self.photon_root, self.finalize_command, '-w', self.photon_root], stdout=self.output) retval = process.wait() if self.iso_installer: # just copy the initramfs /boot -> /photon_mnt/boot shutil.copy('/boot/initrd.img-no-kmods', self.photon_root + '/boot/') else: #Build the initramfs process = subprocess.Popen([self.chroot_command, '-w', self.photon_root, './mkinitramfs', '-n', '/boot/initrd.img-no-kmods'], stdout=self.output) retval = process.wait() def install_package(self, package_name): rpm_params = '' if ('type' in self.install_config and (self.install_config['type'] in ['micro', 'minimal'])) or self.install_config['iso_system']: rpm_params = rpm_params + ' --excludedocs ' process = subprocess.Popen([self.chroot_command, '-w', self.photon_root, self.install_package_command, '-w', self.photon_root, package_name, rpm_params], stdout=self.output) return process.wait() def execute_modules(self, phase): modules = glob.glob('modules/m_*.py') for mod_path in modules: module = mod_path.replace('/', '.', 1) module = os.path.splitext(module)[0] try: __import__(module) mod = sys.modules[module] except ImportError: print >> sys.stderr, 'Error importing module %s' % module continue # the module default is disabled if not hasattr(mod, 'enabled') or mod.enabled == False: print >> sys.stderr, "module %s is not enabled" % module continue # check for the install phase if not hasattr(mod, 'install_phase'): print >> sys.stderr, "Error: can not defind module %s phase" % module continue if mod.install_phase != phase: print >> sys.stderr, "Skipping module %s for phase %s" % (module, phase) continue if not hasattr(mod, 'execute'): print >> sys.stderr, "Error: not able to execute module %s" % module continue mod.execute(module, self.ks_config, self.install_config, self.photon_root)
class SelectDisk(object): def __init__(self, maxy, maxx, install_config): self.install_config = install_config self.menu_items = [] self.maxx = maxx self.maxy = maxy self.win_width = 70 self.win_height = 16 self.win_starty = (self.maxy - self.win_height) // 2 self.win_startx = (self.maxx - self.win_width) // 2 self.menu_starty = self.win_starty + 6 self.menu_height = 5 self.disk_buttom_items = [] self.disk_buttom_items.append( ('<Custom>', self.custom_function, False)) self.disk_buttom_items.append(('<Auto>', self.auto_function, False)) self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Select a disk', True, items=self.disk_buttom_items, menu_helper=self.save_index, position=2, tab_enabled=False) self.devices = Device.refresh_devices() def display(self): self.window.addstr( 0, 0, 'Please select a disk and a method how to partition it:\n' + 'Auto - single partition for /, no swap partition.\n' + 'Custom - for customized partitioning') self.disk_menu_items = [] # Fill in the menu items for index, device in enumerate(self.devices): #if index > 0: self.disk_menu_items.append( ('{2} - {1} @ {0}'.format(device.path, device.size, device.model), self.save_index, index)) self.disk_menu = Menu(self.menu_starty, self.maxx, self.disk_menu_items, self.menu_height, tab_enable=False) self.disk_menu.can_save_sel(True) self.window.set_action_panel(self.disk_menu) return self.window.do_action() def save_index(self, device_index): self.install_config['disk'] = self.devices[device_index].path return ActionResult(True, None) def auto_function(self): #default is no partition self.install_config['autopartition'] = True return ActionResult(True, None) def custom_function(self): #custom minimize partition number is 1 self.install_config['autopartition'] = False return ActionResult(True, None)
class Installer(object): """ Photon installer """ mount_command = "./mk-mount-disk.sh" prepare_command = "./mk-prepare-system.sh" finalize_command = "./mk-finalize-system.sh" chroot_command = "./mk-run-chroot.sh" setup_grub_command = "./mk-setup-grub.sh" unmount_disk_command = "./mk-unmount-disk.sh" def __init__(self, install_config, maxy=0, maxx=0, iso_installer=False, rpm_path="../stage/RPMS", log_path="../stage/LOGS"): self.install_config = install_config self.install_config['iso_installer'] = iso_installer self.rpm_path = rpm_path self.log_path = log_path if 'working_directory' in self.install_config: self.working_directory = self.install_config['working_directory'] else: self.working_directory = "/mnt/photon-root" self.photon_root = self.working_directory + "/photon-chroot" self.rpms_tobeinstalled = None if self.install_config['iso_installer']: self.output = open(os.devnull, 'w') #initializing windows height = 10 width = 75 progress_padding = 5 progress_width = width - progress_padding starty = (maxy - height) // 2 startx = (maxx - width) // 2 self.window = Window(height, width, maxy, maxx, 'Installing Photon', False) self.progress_bar = ProgressBar(starty + 3, startx + progress_padding // 2, progress_width) else: self.output = None signal.signal(signal.SIGINT, self.exit_gracefully) def install(self, params): """ Install photon system and handle exception """ del params try: return self._unsafe_install() except Exception as inst: if self.install_config['iso_installer']: modules.commons.log(modules.commons.LOG_ERROR, repr(inst)) self.exit_gracefully(None, None) else: raise def _unsafe_install(self): """ Install photon system """ self._setup_install_repo() self._initialize_system() self._install_packages() self._enable_network_in_chroot() self._finalize_system() self._disable_network_in_chroot() self._cleanup_and_exit() return ActionResult(True, None) def exit_gracefully(self, signal1, frame1): """ This will be called if the installer interrupted by Ctrl+C, exception or other failures """ del signal1 del frame1 if self.install_config['iso_installer']: self.progress_bar.hide() self.window.addstr(0, 0, 'Oops, Installer got interrupted.\n\n' + 'Press any key to get to the bash...') self.window.content_window().getch() modules.commons.dump(modules.commons.LOG_FILE_NAME) sys.exit(1) def _cleanup_and_exit(self): """ Unmount the disk, eject cd and exit """ command = [Installer.unmount_disk_command, '-w', self.photon_root] if not self.install_config['iso_system']: command.extend(self._generate_partitions_param(reverse=True)) process = subprocess.Popen(command, stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Failed to unmount disks") if self.install_config['iso_installer']: self.progress_bar.hide() self.window.addstr(0, 0, 'Congratulations, Photon has been installed in {0} secs.\n\n' 'Press any key to continue to boot...' .format(self.progress_bar.time_elapsed)) self._eject_cdrom() def _copy_rpms(self): """ Prepare RPM list and copy rpms """ # prepare the RPMs list json_pkg_to_rpm_map = JsonWrapper(self.install_config["pkg_to_rpm_map_file"]) pkg_to_rpm_map = json_pkg_to_rpm_map.read() self.rpms_tobeinstalled = [] selected_packages = self.install_config['packages'] for pkg in selected_packages: if pkg in pkg_to_rpm_map: if pkg_to_rpm_map[pkg]['rpm'] is not None: name = pkg_to_rpm_map[pkg]['rpm'] basename = os.path.basename(name) self.rpms_tobeinstalled.append({'filename': basename, 'path': name, 'package' : pkg}) # Copy the rpms for rpm in self.rpms_tobeinstalled: shutil.copy(rpm['path'], self.photon_root + '/RPMS/') def _copy_files(self): """ Copy the rpm files and instal scripts. """ # Make the photon_root directory if not exits process = subprocess.Popen(['mkdir', '-p', self.photon_root], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to create the root directory") self.exit_gracefully(None, None) # Copy the installer files process = subprocess.Popen(['cp', '-r', "../installer", self.photon_root], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to copy install scripts") self.exit_gracefully(None, None) # Create the rpms directory process = subprocess.Popen(['mkdir', '-p', self.photon_root + '/RPMS'], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to create the rpms directory") self.exit_gracefully(None, None) self._copy_rpms() def _bind_installer(self): """ Make the photon_root/installer directory if not exits The function finalize_system will access the file /installer/mk-finalize-system.sh after chroot to photon_root. Bind the /installer folder to self.photon_root/installer, so that after chroot to photon_root, the file can still be accessed as /installer/mk-finalize-system.sh. """ # Make the photon_root/installer directory if not exits if(subprocess.call(['mkdir', '-p', os.path.join(self.photon_root, "installer")]) != 0 or subprocess.call(['mount', '--bind', '/installer', os.path.join(self.photon_root, "installer")]) != 0): modules.commons.log(modules.commons.LOG_ERROR, "Fail to bind installer") self.exit_gracefully(None, None) def _unbind_installer(self): # unmount the installer directory process = subprocess.Popen(['umount', os.path.join(self.photon_root, "installer")], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to unbind the installer directory") # remove the installer directory process = subprocess.Popen(['rm', '-rf', os.path.join(self.photon_root, "installer")], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to remove the installer directory") def _bind_repo_dir(self): """ Bind repo dir for tdnf installation """ rpm_cache_dir = self.photon_root + '/cache/tdnf/photon-iso/rpms' if self.rpm_path.startswith("https://") or self.rpm_path.startswith("http://"): return if (subprocess.call(['mkdir', '-p', rpm_cache_dir]) != 0 or subprocess.call(['mount', '--bind', self.rpm_path, rpm_cache_dir]) != 0): modules.commons.log(modules.commons.LOG_ERROR, "Fail to bind cache rpms") self.exit_gracefully(None, None) def _unbind_repo_dir(self): """ Unbind repo dir after installation """ rpm_cache_dir = self.photon_root + '/cache/tdnf/photon-iso/rpms' if self.rpm_path.startswith("https://") or self.rpm_path.startswith("http://"): return if (subprocess.call(['umount', rpm_cache_dir]) != 0 or subprocess.call(['rm', '-rf', rpm_cache_dir]) != 0): modules.commons.log(modules.commons.LOG_ERROR, "Fail to unbind cache rpms") self.exit_gracefully(None, None) def _update_fstab(self): """ update fstab """ with open(os.path.join(self.photon_root, "etc/fstab"), "w") as fstab_file: fstab_file.write("#system\tmnt-pt\ttype\toptions\tdump\tfsck\n") for partition in self.install_config['disk']['partitions']: options = 'defaults' dump = 1 fsck = 2 if 'mountpoint' in partition and partition['mountpoint'] == '/': options = options + ',barrier,noatime,noacl,data=ordered' fsck = 1 if partition['filesystem'] == 'swap': mountpoint = 'swap' dump = 0 fsck = 0 else: mountpoint = partition['mountpoint'] fstab_file.write("{}\t{}\t{}\t{}\t{}\t{}\n".format( partition['path'], mountpoint, partition['filesystem'], options, dump, fsck )) # Add the cdrom entry fstab_file.write("/dev/cdrom\t/mnt/cdrom\tiso9660\tro,noauto\t0\t0\n") def _generate_partitions_param(self, reverse=False): """ Generate partition param for mount command """ if reverse: step = -1 else: step = 1 params = [] for partition in self.install_config['disk']['partitions'][::step]: if partition["filesystem"] == "swap": continue params.extend(['--partitionmountpoint', partition["path"], partition["mountpoint"]]) return params def _initialize_system(self): """ Prepare the system to install photon """ #Setup the disk if not self.install_config['iso_system']: command = [Installer.mount_command, '-w', self.photon_root] command.extend(self._generate_partitions_param()) process = subprocess.Popen(command, stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_INFO, "Failed to setup the disk for installation") self.exit_gracefully(None, None) if self.install_config['iso_installer']: self._bind_installer() self._bind_repo_dir() process = subprocess.Popen([Installer.prepare_command, '-w', self.photon_root, 'install'], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_INFO, "Failed to bind the installer and repo needed by tdnf") self.exit_gracefully(None, None) else: self._copy_files() #Setup the filesystem basics process = subprocess.Popen([Installer.prepare_command, '-w', self.photon_root], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_INFO, "Failed to setup the file systems basics") self.exit_gracefully(None, None) def _finalize_system(self): """ Finalize the system after the installation """ #Setup the disk process = subprocess.Popen([Installer.chroot_command, '-w', self.photon_root, Installer.finalize_command, '-w', self.photon_root], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to setup th target system after the installation") if self.install_config['iso_installer']: modules.commons.dump(modules.commons.LOG_FILE_NAME) shutil.copy(modules.commons.LOG_FILE_NAME, self.photon_root + '/var/log/') shutil.copy(modules.commons.TDNF_LOG_FILE_NAME, self.photon_root + '/var/log/') self._unbind_installer() self._unbind_repo_dir() # Disable the swap file process = subprocess.Popen(['swapoff', '-a'], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to swapoff") # remove the tdnf cache directory and the swapfile. process = subprocess.Popen(['rm', '-rf', os.path.join(self.photon_root, "cache")], stdout=self.output) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_ERROR, "Fail to remove the cache") if not self.install_config['iso_system']: # Execute post installation modules self._execute_modules(modules.commons.POST_INSTALL) if os.path.exists(modules.commons.KS_POST_INSTALL_LOG_FILE_NAME): shutil.copy(modules.commons.KS_POST_INSTALL_LOG_FILE_NAME, self.photon_root + '/var/log/') if self.install_config['iso_installer'] and os.path.isdir("/sys/firmware/efi"): self.install_config['boot'] = 'efi' # install grub if 'boot_partition_number' not in self.install_config['disk']: self.install_config['disk']['boot_partition_number'] = 1 try: if self.install_config['boot'] == 'bios': process = subprocess.Popen( [Installer.setup_grub_command, '-w', self.photon_root, "bios", self.install_config['disk']['disk'], self.install_config['disk']['root'], self.install_config['disk']['boot'], self.install_config['disk']['bootdirectory'], str(self.install_config['disk']['boot_partition_number'])], stdout=self.output) elif self.install_config['boot'] == 'efi': process = subprocess.Popen( [Installer.setup_grub_command, '-w', self.photon_root, "efi", self.install_config['disk']['disk'], self.install_config['disk']['root'], self.install_config['disk']['boot'], self.install_config['disk']['bootdirectory'], str(self.install_config['disk']['boot_partition_number'])], stdout=self.output) except: #install bios if variable is not set. process = subprocess.Popen( [Installer.setup_grub_command, '-w', self.photon_root, "bios", self.install_config['disk']['disk'], self.install_config['disk']['root'], self.install_config['disk']['boot'], self.install_config['disk']['bootdirectory'], str(self.install_config['disk']['boot_partition_number'])], stdout=self.output) retval = process.wait() self._update_fstab() def _execute_modules(self, phase): """ Execute the scripts in the modules folder """ sys.path.append("./modules") modules_paths = glob.glob('modules/m_*.py') for mod_path in modules_paths: module = mod_path.replace('/', '.', 1) module = os.path.splitext(module)[0] try: __import__(module) mod = sys.modules[module] except ImportError: modules.commons.log(modules.commons.LOG_ERROR, 'Error importing module {}'.format(module)) continue # the module default is disabled if not hasattr(mod, 'enabled') or mod.enabled is False: modules.commons.log(modules.commons.LOG_INFO, "module {} is not enabled".format(module)) continue # check for the install phase if not hasattr(mod, 'install_phase'): modules.commons.log(modules.commons.LOG_ERROR, "Error: can not defind module {} phase".format(module)) continue if mod.install_phase != phase: modules.commons.log(modules.commons.LOG_INFO, "Skipping module {0} for phase {1}".format(module, phase)) continue if not hasattr(mod, 'execute'): modules.commons.log(modules.commons.LOG_ERROR, "Error: not able to execute module {}".format(module)) continue mod.execute(self.install_config, self.photon_root) def _adjust_packages_for_vmware_virt(self): """ Install linux_esx on Vmware virtual machine if requested """ try: if self.install_config['install_linux_esx']: selected_packages = self.install_config['packages'] try: selected_packages.remove('linux') except ValueError: pass try: selected_packages.remove('initramfs') except ValueError: pass selected_packages.append('linux-esx') except KeyError: pass def _setup_install_repo(self): """ Setup the tdnf repo for installation """ if self.install_config['iso_installer']: self.window.show_window() self.progress_bar.initialize('Initializing installation...') self.progress_bar.show() #self.rpm_path = "https://dl.bintray.com/vmware/photon_release_1.0_TP2_x86_64" if self.rpm_path.startswith("https://") or self.rpm_path.startswith("http://"): cmdoption = 's/baseurl.*/baseurl={}/g'.format(self.rpm_path.replace('/', r'\/')) process = subprocess.Popen(['sed', '-i', cmdoption, '/etc/yum.repos.d/photon-iso.repo']) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_INFO, "Failed to reset repo") self.exit_gracefully(None, None) cmdoption = (r's/cachedir=\/var/cachedir={}/g' .format(self.photon_root.replace('/', r'\/'))) process = subprocess.Popen(['sed', '-i', cmdoption, '/etc/tdnf/tdnf.conf']) retval = process.wait() if retval != 0: modules.commons.log(modules.commons.LOG_INFO, "Failed to reset tdnf cachedir") self.exit_gracefully(None, None) def _install_packages(self): """ Install packages using tdnf or rpm command """ if self.install_config['iso_installer']: self._tdnf_install_packages() else: self._rpm_install_packages() def _tdnf_install_packages(self): """ Install packages using tdnf command """ self._adjust_packages_for_vmware_virt() selected_packages = self.install_config['packages'] state = 0 packages_to_install = {} total_size = 0 with open(modules.commons.TDNF_CMDLINE_FILE_NAME, "w") as tdnf_cmdline_file: tdnf_cmdline_file.write("tdnf install --installroot {0} --nogpgcheck {1}" .format(self.photon_root, " ".join(selected_packages))) with open(modules.commons.TDNF_LOG_FILE_NAME, "w") as tdnf_errlog: process = subprocess.Popen(['tdnf', 'install'] + selected_packages + ['--installroot', self.photon_root, '--nogpgcheck', '--assumeyes'], stdout=subprocess.PIPE, stderr=tdnf_errlog) while True: output = process.stdout.readline().decode() if output == '': retval = process.poll() if retval is not None: break if state == 0: if output == 'Installing:\n': state = 1 elif state == 1: #N A EVR Size(readable) Size(in bytes) if output == '\n': state = 2 self.progress_bar.update_num_items(total_size) else: info = output.split() package = '{0}-{1}.{2}'.format(info[0], info[2], info[1]) packages_to_install[package] = int(info[5]) total_size += int(info[5]) elif state == 2: if output == 'Downloading:\n': self.progress_bar.update_message('Preparing ...') state = 3 elif state == 3: self.progress_bar.update_message(output) if output == 'Running transaction\n': state = 4 else: modules.commons.log(modules.commons.LOG_INFO, "[tdnf] {0}".format(output)) prefix = 'Installing/Updating: ' if output.startswith(prefix): package = output[len(prefix):].rstrip('\n') self.progress_bar.increment(packages_to_install[package]) self.progress_bar.update_message(output) # 0 : succeed; 137 : package already installed; 65 : package not found in repo. if retval != 0 and retval != 137: modules.commons.log(modules.commons.LOG_ERROR, "Failed to install some packages, refer to {0}" .format(modules.commons.TDNF_LOG_FILE_NAME)) self.exit_gracefully(None, None) self.progress_bar.show_loading('Finalizing installation') def _rpm_install_packages(self): """ Install packages using rpm command """ rpms = [] for rpm in self.rpms_tobeinstalled: # We already installed the filesystem in the preparation if rpm['package'] == 'filesystem': continue rpms.append(rpm['filename']) rpms = set(rpms) rpm_paths = [] for root, _, files in os.walk(self.rpm_path): for file in files: if file in rpms: rpm_paths.append(os.path.join(root, file)) # --nodeps is for hosts which do not support rich dependencies rpm_params = ['--nodeps', '--root', self.photon_root, '--dbpath', '/var/lib/rpm'] if (('type' in self.install_config and (self.install_config['type'] in ['micro', 'minimal'])) or self.install_config['iso_system']): rpm_params.append('--excludedocs') modules.commons.log(modules.commons.LOG_INFO, "installing packages {0}, with params {1}" .format(rpm_paths, rpm_params)) process = subprocess.Popen(['rpm', '-Uvh'] + rpm_params + rpm_paths, stderr=subprocess.STDOUT) return_value = process.wait() if return_value != 0: self.exit_gracefully(None, None) def _eject_cdrom(self): """ Eject the cdrom on request """ eject_cdrom = True if 'ui_install' in self.install_config: self.window.content_window().getch() if 'eject_cdrom' in self.install_config and not self.install_config['eject_cdrom']: eject_cdrom = False if eject_cdrom: process = subprocess.Popen(['eject', '-r'], stdout=self.output) process.wait() def _enable_network_in_chroot(self): """ Enable network in chroot """ if os.path.exists("/etc/resolv.conf"): shutil.copy("/etc/resolv.conf", self.photon_root + '/etc/.') def _disable_network_in_chroot(self): """ disable network in chroot """ if os.path.exists(self.photon_root + '/etc/resolv.conf'): os.remove(self.photon_root + '/etc/resolv.conf')
class Installer(object): def __init__(self, install_config, maxy = 0, maxx = 0, iso_installer = False, rpm_path = "../stage/RPMS", log_path = "../stage/LOGS", ks_config = None): self.install_config = install_config self.ks_config = ks_config self.iso_installer = iso_installer self.rpm_path = rpm_path self.log_path = log_path self.mount_command = "./mk-mount-disk.sh" self.prepare_command = "./mk-prepare-system.sh" self.finalize_command = "./mk-finalize-system.sh" self.install_package_command = "./mk-install-package.sh" self.chroot_command = "./mk-run-chroot.sh" self.setup_grub_command = "./mk-setup-grub.sh" self.unmount_disk_command = "./mk-unmount-disk.sh" if self.iso_installer: self.working_directory = "/mnt/photon-root" elif 'working_directory' in self.install_config: self.working_directory = self.install_config['working_directory'] else: self.working_directory = "/mnt/photon-root" self.photon_root = self.working_directory + "/photon-chroot"; self.restart_command = "shutdown" if self.iso_installer: self.output = open(os.devnull, 'w') else: self.output = None if self.iso_installer: #initializing windows self.maxy = maxy self.maxx = maxx self.height = 10 self.width = 75 self.progress_padding = 5 self.progress_width = self.width - self.progress_padding self.starty = (self.maxy - self.height) / 2 self.startx = (self.maxx - self.width) / 2 self.window = Window(self.height, self.width, self.maxy, self.maxx, 'Installing Photon', False) self.progress_bar = ProgressBar(self.starty + 3, self.startx + self.progress_padding / 2, self.progress_width) signal.signal(signal.SIGINT, self.exit_gracefully) # This will be called if the installer interrupted by Ctrl+C or exception def exit_gracefully(self, signal, frame): if self.iso_installer: self.progress_bar.hide() self.window.addstr(0, 0, 'Opps, Installer got interrupted.\n\nPress any key to get to the bash...') self.window.content_window().getch() modules.commons.dump(modules.commons.LOG_ERROR, modules.commons.LOG_FILE_NAME) sys.exit(1) def install(self, params): try: return self.unsafe_install(params) except: if self.iso_installer: self.exit_gracefully(None, None) else: raise def unsafe_install(self, params): if self.iso_installer: self.window.show_window() self.progress_bar.initialize('Initializing installation...') self.progress_bar.show() self.execute_modules(modules.commons.PRE_INSTALL) self.initialize_system() #install packages for rpm in self.rpms_tobeinstalled: # We already installed the filesystem in the preparation if rpm['package'] == 'filesystem': continue if self.iso_installer: self.progress_bar.update_message('Installing {0}...'.format(rpm['package'])) return_value = self.install_package(rpm['filename']) if return_value != 0: self.exit_gracefully(None, None) if self.iso_installer: self.progress_bar.increment(rpm['size'] * self.install_factor) if self.iso_installer: self.progress_bar.show_loading('Finalizing installation') self.finalize_system() if not self.install_config['iso_system']: # Execute post installation modules self.execute_modules(modules.commons.POST_INSTALL) # install grub try: if self.install_config['boot'] == 'bios': process = subprocess.Popen([self.setup_grub_command, '-w', self.photon_root, "bios", self.install_config['disk']['disk'], self.install_config['disk']['root']], stdout=self.output) elif self.install_config['boot'] == 'efi': process = subprocess.Popen([self.setup_grub_command, '-w', self.photon_root, "efi", self.install_config['disk']['disk'], self.install_config['disk']['root']], stdout=self.output) except: #install bios if variable is not set. process = subprocess.Popen([self.setup_grub_command, '-w', self.photon_root, "bios", self.install_config['disk']['disk'], self.install_config['disk']['root']], stdout=self.output) retval = process.wait() process = subprocess.Popen([self.unmount_disk_command, '-w', self.photon_root], stdout=self.output) retval = process.wait() if self.iso_installer: self.progress_bar.hide() self.window.addstr(0, 0, 'Congratulations, Photon has been installed in {0} secs.\n\nPress any key to continue to boot...'.format(self.progress_bar.time_elapsed)) if self.ks_config == None: self.window.content_window().getch() return ActionResult(True, None) def download_file(self, url, directory): # TODO: Add errors handling urlopener = urllib.URLopener() urlopener.retrieve(url, os.path.join(directory, os.path.basename(url))) def download_rpms(self): repodata_dir = os.path.join(self.photon_root, 'RPMS/repodata') process = subprocess.Popen(['mkdir', '-p', repodata_dir], stdout=self.output) retval = process.wait() import hawkey self.install_factor = 1 # Load the repo data sack = hawkey.Sack() repomd_filename = "repomd.xml" repomd_url = os.path.join(self.rpm_path, "repodata/repomd.xml") self.download_file(repomd_url, repodata_dir) # parse to the xml to get the primary and files list tree = ET.parse(os.path.join(repodata_dir, repomd_filename)) # TODO: Get the namespace dynamically from the xml file ns = {'ns': 'http://linux.duke.edu/metadata/repo'} primary_location = tree.find("./ns:data[@type='primary']/ns:location", ns).get("href"); filelists_location = tree.find("./ns:data[@type='filelists']/ns:location", ns).get("href"); primary_filename = os.path.basename(primary_location); filelists_filename = os.path.basename(filelists_location); self.download_file(os.path.join(self.rpm_path, primary_location), repodata_dir) self.download_file(os.path.join(self.rpm_path, filelists_location), repodata_dir) repo = hawkey.Repo("installrepo") repo.repomd_fn = os.path.join(repodata_dir, repomd_filename) repo.primary_fn = os.path.join(repodata_dir, primary_filename) repo.filelists_fn = os.path.join(repodata_dir, filelists_filename) sack.load_yum_repo(repo, load_filelists=True) progressbar_num_items = 0 self.rpms_tobeinstalled = [] selected_packages = self.install_config['packages'] for package in selected_packages: # Locate the package q = hawkey.Query(sack).filter(name=package) if (len(q) > 0): progressbar_num_items += q[0].size + q[0].size * self.install_factor self.rpms_tobeinstalled.append({'package': package, 'size': q[0].size, 'location': q[0].location, 'filename': os.path.basename(q[0].location)}) else: modules.commons.log(modules.commons.LOG_WARNING, "Package {} not found in the repo".format(package)) #self.exit_gracefully(None, None) self.progress_bar.update_num_items(progressbar_num_items) # Download the rpms for rpm in self.rpms_tobeinstalled: message = 'Downloading {0}...'.format(rpm['filename']) self.progress_bar.update_message(message) self.download_file(os.path.join(self.rpm_path, rpm['location']), os.path.join(self.photon_root, "RPMS")) self.progress_bar.increment(rpm['size']) # update the rpms path self.rpm_path = os.path.join(self.photon_root, "RPMS") def copy_rpms(self): # prepare the RPMs list self.install_factor = 3 rpms = [] for root, dirs, files in os.walk(self.rpm_path): for name in files: file = os.path.join(root, name) size = os.path.getsize(file) rpms.append({'filename': name, 'path': file, 'size': size}) progressbar_num_items = 0 self.rpms_tobeinstalled = [] selected_packages = self.install_config['packages'] for package in selected_packages: pattern = package + '-[0-9]*.rpm' for rpm in rpms: if fnmatch.fnmatch(rpm['filename'], pattern): rpm['package'] = package self.rpms_tobeinstalled.append(rpm) progressbar_num_items += rpm['size'] + rpm['size'] * self.install_factor break if self.iso_installer: self.progress_bar.update_num_items(progressbar_num_items) # Copy the rpms for rpm in self.rpms_tobeinstalled: if self.iso_installer: message = 'Copying {0}...'.format(rpm['filename']) self.progress_bar.update_message(message) shutil.copy(rpm['path'], self.photon_root + '/RPMS/') if self.iso_installer: self.progress_bar.increment(rpm['size']) def copy_files(self): # Make the photon_root directory if not exits process = subprocess.Popen(['mkdir', '-p', self.photon_root], stdout=self.output) retval = process.wait() # Copy the installer files process = subprocess.Popen(['cp', '-r', "../installer", self.photon_root], stdout=self.output) retval = process.wait() # Create the rpms directory process = subprocess.Popen(['mkdir', '-p', self.photon_root + '/RPMS'], stdout=self.output) retval = process.wait() if self.rpm_path.startswith("http://"): self.download_rpms() else: self.copy_rpms() def initialize_system(self): #Setup the disk if (not self.install_config['iso_system']): process = subprocess.Popen([self.mount_command, '-w', self.photon_root, self.install_config['disk']['root']], stdout=self.output) retval = process.wait() self.copy_files() #Setup the filesystem basics process = subprocess.Popen([self.prepare_command, '-w', self.photon_root], stdout=self.output) retval = process.wait() def finalize_system(self): #Setup the disk process = subprocess.Popen([self.chroot_command, '-w', self.photon_root, self.finalize_command, '-w', self.photon_root], stdout=self.output) retval = process.wait() initrd_dir = 'boot' initrd_file_name = 'initrd.img-no-kmods' if self.iso_installer: # just copy the initramfs /boot -> /photon_mnt/boot shutil.copy(os.path.join(initrd_dir, initrd_file_name), self.photon_root + '/boot/') # remove the installer directory process = subprocess.Popen(['rm', '-rf', os.path.join(self.photon_root, "installer")], stdout=self.output) retval = process.wait() else: #Build the initramfs by passing in the kernel version version_string = '' for root, dirs, files in os.walk(self.rpm_path): for name in files: if fnmatch.fnmatch(name, 'linux-[0-9]*.rpm'): version_array = name.split('-') if len(version_array) > 2: version_string = version_array[1] if 'initrd_dir' in self.install_config: initrd_dir = self.install_config['initrd_dir'] process = subprocess.Popen([self.chroot_command, '-w', self.photon_root, './mkinitramfs', '-n', os.path.join(initrd_dir, initrd_file_name), '-k', version_string], stdout=self.output) retval = process.wait() def install_package(self, package_name): rpm_params = '' os.environ["RPMROOT"] = self.rpm_path rpm_params = rpm_params + ' --force ' rpm_params = rpm_params + ' --root ' + self.photon_root rpm_params = rpm_params + ' --dbpath /var/lib/rpm ' if ('type' in self.install_config and (self.install_config['type'] in ['micro', 'minimal'])) or self.install_config['iso_system']: rpm_params = rpm_params + ' --excludedocs ' process = subprocess.Popen([self.install_package_command, '-w', self.photon_root, package_name, rpm_params], stdout=self.output) return process.wait() def execute_modules(self, phase): modules_paths = glob.glob('modules/m_*.py') for mod_path in modules_paths: module = mod_path.replace('/', '.', 1) module = os.path.splitext(module)[0] try: __import__(module) mod = sys.modules[module] except ImportError: modules.commons.log(modules.commons.LOG_ERROR, 'Error importing module {}'.format(module)) continue # the module default is disabled if not hasattr(mod, 'enabled') or mod.enabled == False: modules.commons.log(modules.commons.LOG_INFO, "module {} is not enabled".format(module)) continue # check for the install phase if not hasattr(mod, 'install_phase'): modules.commons.log(modules.commons.LOG_ERROR, "Error: can not defind module {} phase".format(module)) continue if mod.install_phase != phase: modules.commons.log(modules.commons.LOG_INFO, "Skipping module {0} for phase {1}".format(module, phase)) continue if not hasattr(mod, 'execute'): modules.commons.log(modules.commons.LOG_ERROR, "Error: not able to execute module {}".format(module)) continue mod.execute(module, self.ks_config, self.install_config, self.photon_root) def run(self, command, comment = None): if comment != None: modules.commons.log(modules.commons.LOG_INFO, "Installer: {} ".format(comment)) self.progress_bar.update_loading_message(comment) modules.commons.log(modules.commons.LOG_INFO, "Installer: {} ".format(command)) process = subprocess.Popen([command], shell=True, stdout=self.output) retval = process.wait() return retval