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, 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 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, 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, 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 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, items=[], 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 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 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): """ 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", 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" 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: versionindex = pkg.rfind("-") if versionindex == -1: raise Exception("Invalid pkg name: " + pkg) package = pkg[:versionindex] if package in pkg_to_rpm_map: if pkg_to_rpm_map[package]['rpm'] is not None: name = pkg_to_rpm_map[package]['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', "../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): """ 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 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
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 '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, '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.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 modules.commons.log(modules.commons.LOG_INFO, "[tdnf] {0}".format(output)) 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: if output == 'Running transaction\n': state = 4 else: 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) #packages_to_install[package])) # 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 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') 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 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)) if self.ks_config == None: self.window.content_window().getch() 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 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 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'] == '/': 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") 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() 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() # 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 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_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
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.rpm_path] 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 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)