コード例 #1
0
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()
コード例 #2
0
ファイル: windowstringreader.py プロジェクト: bgrissin/photon
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()
コード例 #3
0
ファイル: installer.py プロジェクト: johnt337/photon
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)
コード例 #4
0
ファイル: installer.py プロジェクト: pbidkar/photon
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
コード例 #5
0
class Installer(object):
    def __init__(self,
                 install_config,
                 maxy=0,
                 maxx=0,
                 iso_installer=False,
                 rpm_path="../stage/RPMS",
                 log_path="../stage/LOGS",
                 ks_config=None):
        self.install_config = install_config
        self.ks_config = ks_config
        self.iso_installer = iso_installer
        self.rpm_path = rpm_path
        self.log_path = log_path
        self.mount_command = "./mk-mount-disk.sh"
        self.prepare_command = "./mk-prepare-system.sh"
        self.finalize_command = "./mk-finalize-system.sh"
        self.install_package_command = "./mk-install-package.sh"
        self.chroot_command = "./mk-run-chroot.sh"
        self.setup_grub_command = "./mk-setup-grub.sh"
        self.unmount_disk_command = "./mk-unmount-disk.sh"

        if self.iso_installer:
            self.working_directory = "/mnt/photon-root"
        elif 'working_directory' in self.install_config:
            self.working_directory = self.install_config['working_directory']
        else:
            self.working_directory = "/mnt/photon-root"
        self.photon_root = self.working_directory + "/photon-chroot"

        self.restart_command = "shutdown"

        if self.iso_installer:
            self.output = open(os.devnull, 'w')
        else:
            self.output = None

        if self.iso_installer:
            #initializing windows
            self.maxy = maxy
            self.maxx = maxx
            self.height = 10
            self.width = 75
            self.progress_padding = 5

            self.progress_width = self.width - self.progress_padding
            self.starty = (self.maxy - self.height) / 2
            self.startx = (self.maxx - self.width) / 2
            self.window = Window(self.height, self.width, self.maxy, self.maxx,
                                 'Installing Photon', False)
            self.progress_bar = ProgressBar(
                self.starty + 3, self.startx + self.progress_padding / 2,
                self.progress_width)

        signal.signal(signal.SIGINT, self.exit_gracefully)

    # This will be called if the installer interrupted by Ctrl+C or exception
    def exit_gracefully(self, signal, frame):
        if self.iso_installer:
            self.progress_bar.hide()
            self.window.addstr(
                0, 0,
                'Opps, Installer got interrupted.\n\nPress any key to get to the bash...'
            )
            self.window.content_window().getch()

        modules.commons.dump(modules.commons.LOG_FILE_NAME)
        sys.exit(1)

    def install(self, params):
        try:
            return self.unsafe_install(params)
        except Exception as inst:
            if self.iso_installer:
                modules.commons.log(modules.commons.LOG_ERROR, repr(inst))
                self.exit_gracefully(None, None)
            else:
                raise

    def unsafe_install(self, params):

        if self.iso_installer:
            self.window.show_window()
            self.progress_bar.initialize('Initializing installation...')
            self.progress_bar.show()
            #self.rpm_path = "https://dl.bintray.com/vmware/photon_release_1.0_TP2_x86_64"
            if self.rpm_path.startswith(
                    "https://") or self.rpm_path.startswith("http://"):
                cmdoption = 's/baseurl.*/baseurl={}/g'.format(
                    self.rpm_path.replace('/', '\/'))
                process = subprocess.Popen([
                    'sed', '-i', cmdoption, '/etc/yum.repos.d/photon-iso.repo'
                ])
                retval = process.wait()
                if retval != 0:
                    modules.commons.log(modules.commons.LOG_INFO,
                                        "Failed to reset repo")
                    self.exit_gracefully(None, None)

            cmdoption = 's/cachedir=\/var/cachedir={}/g'.format(
                self.photon_root.replace('/', '\/'))
            process = subprocess.Popen(
                ['sed', '-i', cmdoption, '/etc/tdnf/tdnf.conf'])
            retval = process.wait()
            if retval != 0:
                modules.commons.log(modules.commons.LOG_INFO,
                                    "Failed to reset tdnf cachedir")
                self.exit_gracefully(None, None)
        self.execute_modules(modules.commons.PRE_INSTALL)

        self.initialize_system()

        if self.iso_installer:
            self.get_size_of_packages()
            selected_packages = self.install_config['packages']
            for package in selected_packages:
                self.progress_bar.update_message(
                    'Installing {0}...'.format(package))
                process = subprocess.Popen([
                    'tdnf', 'install', package, '--installroot',
                    self.photon_root, '--nogpgcheck', '--assumeyes'
                ],
                                           stdout=self.output,
                                           stderr=subprocess.STDOUT)
                retval = process.wait()
                # 0 : succeed; 137 : package already installed; 65 : package not found in repo.
                if retval != 0 and retval != 137:
                    modules.commons.log(
                        modules.commons.LOG_ERROR,
                        "Failed install: {} with error code {}".format(
                            package, retval))
                    self.exit_gracefully(None, None)
                self.progress_bar.increment(self.size_of_packages[package])
        else:
            #install packages
            for rpm in self.rpms_tobeinstalled:
                # We already installed the filesystem in the preparation
                if rpm['package'] == 'filesystem':
                    continue
                return_value = self.install_package(rpm['filename'])
                if return_value != 0:
                    self.exit_gracefully(None, None)

        if self.iso_installer:
            self.progress_bar.show_loading('Finalizing installation')

        self.finalize_system()

        if not self.install_config['iso_system']:
            # Execute post installation modules
            self.execute_modules(modules.commons.POST_INSTALL)

            # install grub
            try:
                if self.install_config['boot'] == 'bios':
                    process = subprocess.Popen([
                        self.setup_grub_command, '-w', self.photon_root,
                        "bios", self.install_config['disk']['disk'],
                        self.install_config['disk']['root'],
                        self.install_config['disk']['boot'],
                        self.install_config['disk']['bootdirectory']
                    ],
                                               stdout=self.output)
                elif self.install_config['boot'] == 'efi':
                    process = subprocess.Popen([
                        self.setup_grub_command, '-w', self.photon_root, "efi",
                        self.install_config['disk']['disk'],
                        self.install_config['disk']['root'],
                        self.install_config['disk']['boot'],
                        self.install_config['disk']['bootdirectory']
                    ],
                                               stdout=self.output)
            except:
                #install bios if variable is not set.
                process = subprocess.Popen([
                    self.setup_grub_command, '-w', self.photon_root, "bios",
                    self.install_config['disk']['disk'],
                    self.install_config['disk']['root'],
                    self.install_config['disk']['boot'],
                    self.install_config['disk']['bootdirectory']
                ],
                                           stdout=self.output)

            retval = process.wait()

            self.update_fstab()

        command = [self.unmount_disk_command, '-w', self.photon_root]
        if not self.install_config['iso_system']:
            command.extend(self.generate_partitions_param(reverse=True))
        process = subprocess.Popen(command, stdout=self.output)
        retval = process.wait()

        if self.iso_installer:
            self.progress_bar.hide()
            self.window.addstr(
                0, 0,
                'Congratulations, Photon has been installed in {0} secs.\n\nPress any key to continue to boot...'
                .format(self.progress_bar.time_elapsed))
            if self.ks_config == None:
                self.window.content_window().getch()

        return ActionResult(True, None)

    def copy_rpms(self):
        # prepare the RPMs list
        rpms = []
        for root, dirs, files in os.walk(self.rpm_path):
            for name in files:
                file = os.path.join(root, name)
                size = os.path.getsize(file)
                rpms.append({'filename': name, 'path': file, 'size': size})

        self.rpms_tobeinstalled = []
        selected_packages = self.install_config['packages']
        for package in selected_packages:
            pattern = package + '-[0-9]*.rpm'
            pattern2 = package + '-[a-z][0-9]*.rpm'
            for rpm in rpms:
                if fnmatch.fnmatch(rpm['filename'],
                                   pattern) or fnmatch.fnmatch(
                                       rpm['filename'], pattern2):
                    rpm['package'] = package
                    self.rpms_tobeinstalled.append(rpm)
                    break
        # Copy the rpms
        for rpm in self.rpms_tobeinstalled:
            shutil.copy(rpm['path'], self.photon_root + '/RPMS/')

    def copy_files(self):
        # Make the photon_root directory if not exits
        process = subprocess.Popen(['mkdir', '-p', self.photon_root],
                                   stdout=self.output)
        retval = process.wait()

        # Copy the installer files
        process = subprocess.Popen(
            ['cp', '-r', "../installer", self.photon_root], stdout=self.output)
        retval = process.wait()

        # Create the rpms directory
        process = subprocess.Popen(['mkdir', '-p', self.photon_root + '/RPMS'],
                                   stdout=self.output)
        retval = process.wait()
        self.copy_rpms()

    def bind_installer(self):
        # Make the photon_root/installer directory if not exits
        process = subprocess.Popen(
            ['mkdir', '-p',
             os.path.join(self.photon_root, "installer")],
            stdout=self.output)
        retval = process.wait()
        # The function finalize_system will access the file /installer/mk-finalize-system.sh after chroot to photon_root.
        # Bind the /installer folder to self.photon_root/installer, so that after chroot to photon_root,
        # the file can still be accessed as /installer/mk-finalize-system.sh.
        process = subprocess.Popen([
            'mount', '--bind', '/installer',
            os.path.join(self.photon_root, "installer")
        ],
                                   stdout=self.output)
        retval = process.wait()

    def update_fstab(self):
        fstab_file = open(os.path.join(self.photon_root, "etc/fstab"), "w")
        fstab_file.write("#system\tmnt-pt\ttype\toptions\tdump\tfsck\n")

        for partition in self.install_config['disk']['partitions']:
            options = 'defaults'
            dump = 1
            fsck = 2

            if 'mountpoint' in partition and partition['mountpoint'] == '/':
                fsck = 1

            if partition['filesystem'] == 'swap':
                mountpoint = 'swap'
                dump = 0
                fsck = 0
            else:
                mountpoint = partition['mountpoint']

            fstab_file.write("{}\t{}\t{}\t{}\t{}\t{}\n".format(
                partition['path'], mountpoint, partition['filesystem'],
                options, dump, fsck))
        # Add the cdrom entry
        fstab_file.write("/dev/cdrom\t/mnt/cdrom\tiso9660\tro,noauto\t0\t0\n")

        fstab_file.close()

    def generate_partitions_param(self, reverse=False):
        if reverse:
            step = -1
        else:
            step = 1
        params = []
        for partition in self.install_config['disk']['partitions'][::step]:
            if partition["filesystem"] == "swap":
                continue

            params.extend([
                '--partitionmountpoint', partition["path"],
                partition["mountpoint"]
            ])
        return params

    def initialize_system(self):
        #Setup the disk
        if (not self.install_config['iso_system']):
            command = [self.mount_command, '-w', self.photon_root]
            command.extend(self.generate_partitions_param())
            process = subprocess.Popen(command, stdout=self.output)
            retval = process.wait()

        if self.iso_installer:
            self.bind_installer()
            process = subprocess.Popen(
                [self.prepare_command, '-w', self.photon_root, 'install'],
                stdout=self.output)
            retval = process.wait()
        else:
            self.copy_files()
            #Setup the filesystem basics
            process = subprocess.Popen(
                [self.prepare_command, '-w', self.photon_root],
                stdout=self.output)
            retval = process.wait()

    def finalize_system(self):
        #Setup the disk
        process = subprocess.Popen([
            self.chroot_command, '-w', self.photon_root, self.finalize_command,
            '-w', self.photon_root
        ],
                                   stdout=self.output)
        retval = process.wait()
        if self.iso_installer:

            modules.commons.dump(modules.commons.LOG_FILE_NAME)
            shutil.copy(modules.commons.LOG_FILE_NAME,
                        self.photon_root + '/var/log/')

            # unmount the installer directory
            process = subprocess.Popen(
                ['umount',
                 os.path.join(self.photon_root, "installer")],
                stdout=self.output)
            retval = process.wait()
            # remove the installer directory
            process = subprocess.Popen(
                ['rm', '-rf',
                 os.path.join(self.photon_root, "installer")],
                stdout=self.output)
            retval = process.wait()
            # Disable the swap file
            process = subprocess.Popen(['swapoff', '-a'], stdout=self.output)
            retval = process.wait()
            # remove the tdnf cache directory and the swapfile.
            process = subprocess.Popen(
                ['rm', '-rf',
                 os.path.join(self.photon_root, "cache")],
                stdout=self.output)

    def install_package(self, package_name):
        rpm_params = ''

        os.environ["RPMROOT"] = self.rpm_path
        rpm_params = rpm_params + ' --force '
        rpm_params = rpm_params + ' --root ' + self.photon_root
        rpm_params = rpm_params + ' --dbpath /var/lib/rpm '

        if ('type' in self.install_config and
            (self.install_config['type']
             in ['micro', 'minimal'])) or self.install_config['iso_system']:
            rpm_params = rpm_params + ' --excludedocs '

        process = subprocess.Popen([
            self.install_package_command, '-w', self.photon_root, package_name,
            rpm_params
        ],
                                   stdout=self.output)

        return process.wait()

    def execute_modules(self, phase):
        modules_paths = glob.glob('modules/m_*.py')
        for mod_path in modules_paths:
            module = mod_path.replace('/', '.', 1)
            module = os.path.splitext(module)[0]
            try:
                __import__(module)
                mod = sys.modules[module]
            except ImportError:
                modules.commons.log(modules.commons.LOG_ERROR,
                                    'Error importing module {}'.format(module))
                continue

            # the module default is disabled
            if not hasattr(mod, 'enabled') or mod.enabled == False:
                modules.commons.log(modules.commons.LOG_INFO,
                                    "module {} is not enabled".format(module))
                continue
            # check for the install phase
            if not hasattr(mod, 'install_phase'):
                modules.commons.log(
                    modules.commons.LOG_ERROR,
                    "Error: can not defind module {} phase".format(module))
                continue
            if mod.install_phase != phase:
                modules.commons.log(
                    modules.commons.LOG_INFO,
                    "Skipping module {0} for phase {1}".format(module, phase))
                continue
            if not hasattr(mod, 'execute'):
                modules.commons.log(
                    modules.commons.LOG_ERROR,
                    "Error: not able to execute module {}".format(module))
                continue
            mod.execute(module, self.ks_config, self.install_config,
                        self.photon_root)

    def get_install_size_of_a_package(self, name_size_pairs, package):
        modules.commons.log(modules.commons.LOG_INFO,
                            "Find the install size of: {} ".format(package))
        for index, name in enumerate(name_size_pairs, start=0):
            if name[name.find(":") + 1:].strip() == package.strip():
                item = name_size_pairs[index + 1]
                size = item[item.find("(") + 1:item.find(")")]
                return int(size)
        raise LookupError(
            "Cannot find package {} in the repo.".format(package))

    def get_size_of_packages(self):
        #call tdnf info to get the install size of all the packages.
        process = subprocess.Popen(
            ['tdnf', 'info', '--installroot', self.photon_root],
            stdout=subprocess.PIPE)
        out, err = process.communicate()
        if err != None and err != 0:
            modules.commons.log(
                modules.commons.LOG_ERROR,
                "Failed to get infomation from : {} with error code {}".format(
                    package, err))

        name_size_pairs = re.findall("(?:^Name.*$)|(?:^.*Install Size.*$)",
                                     out, re.M)
        selected_packages = self.install_config['packages']
        self.size_of_packages = {}
        progressbar_num_items = 0
        for package in selected_packages:
            size = self.get_install_size_of_a_package(name_size_pairs, package)
            progressbar_num_items += size
            self.size_of_packages[package] = size
        self.progress_bar.update_num_items(progressbar_num_items)

    def run(self, command, comment=None):
        if comment != None:
            modules.commons.log(modules.commons.LOG_INFO,
                                "Installer: {} ".format(comment))
            self.progress_bar.update_loading_message(comment)

        modules.commons.log(modules.commons.LOG_INFO,
                            "Installer: {} ".format(command))
        process = subprocess.Popen([command],
                                   shell=True,
                                   stdout=subprocess.PIPE)
        out, err = process.communicate()
        if err != None and err != 0 and "systemd-tmpfiles" not in command:
            modules.commons.log(
                modules.commons.LOG_ERROR,
                "Installer: failed in {} with error code {}".format(
                    command, err))
            modules.commons.log(modules.commons.LOG_ERROR, out)
            self.exit_gracefully(None, None)

        return err
コード例 #6
0
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()
コード例 #7
0
ファイル: installer.py プロジェクト: harishspqr/photon
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()
コード例 #8
0
ファイル: installer.py プロジェクト: TiejunChina/photon
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')
コード例 #9
0
ファイル: installer.py プロジェクト: transcendtron/photon
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')
コード例 #10
0
ファイル: installer.py プロジェクト: HaykoKoryun/photon
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
コード例 #11
0
class Installer(object):
    def __init__(self,
                 install_config,
                 maxy=0,
                 maxx=0,
                 iso_installer=False,
                 rpm_path="../stage/RPMS",
                 log_path="../stage/LOGS",
                 ks_config=None):
        self.install_config = install_config
        self.ks_config = ks_config
        self.iso_installer = iso_installer
        self.rpm_path = rpm_path
        self.log_path = log_path
        self.mount_command = "./mk-mount-disk.sh"
        self.prepare_command = "./mk-prepare-system.sh"
        self.finalize_command = "./mk-finalize-system.sh"
        self.install_package_command = "./mk-install-package.sh"
        self.chroot_command = "./mk-run-chroot.sh"
        self.setup_grub_command = "./mk-setup-grub.sh"
        self.unmount_disk_command = "./mk-unmount-disk.sh"

        if self.iso_installer:
            self.working_directory = "/mnt/photon-root"
        elif 'working_directory' in self.install_config:
            self.working_directory = self.install_config['working_directory']
        else:
            self.working_directory = "/mnt/photon-root"
        self.photon_root = self.working_directory + "/photon-chroot"

        self.restart_command = "shutdown"

        if self.iso_installer:
            self.output = open(os.devnull, 'w')
        else:
            self.output = None

        if self.iso_installer:
            #initializing windows
            self.maxy = maxy
            self.maxx = maxx
            self.height = 10
            self.width = 75
            self.progress_padding = 5

            self.progress_width = self.width - self.progress_padding
            self.starty = (self.maxy - self.height) / 2
            self.startx = (self.maxx - self.width) / 2
            self.window = Window(self.height, self.width, self.maxy, self.maxx,
                                 'Installing Photon', False)
            self.progress_bar = ProgressBar(
                self.starty + 3, self.startx + self.progress_padding / 2,
                self.progress_width)

        signal.signal(signal.SIGINT, self.exit_gracefully)

    # This will be called if the installer interrupted by Ctrl+C or exception
    def exit_gracefully(self, signal, frame):
        if self.iso_installer:
            self.progress_bar.hide()
            self.window.addstr(
                0, 0,
                'Opps, Installer got interrupted.\n\nPress any key to get to the bash...'
            )
            self.window.content_window().getch()

        modules.commons.dump(modules.commons.LOG_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
コード例 #12
0
class Installer(object):
    def __init__(self,
                 install_config,
                 maxy=0,
                 maxx=0,
                 iso_installer=False,
                 rpm_path="../stage/RPMS",
                 log_path="../stage/LOGS",
                 ks_config=None):
        self.install_config = install_config
        self.ks_config = ks_config
        self.iso_installer = iso_installer
        self.rpm_path = rpm_path
        self.log_path = log_path
        self.mount_command = "./mk-mount-disk.sh"
        self.prepare_command = "./mk-prepare-system.sh"
        self.finalize_command = "./mk-finalize-system.sh"
        self.install_package_command = "./mk-install-package.sh"
        self.chroot_command = "./mk-run-chroot.sh"
        self.setup_grub_command = "./mk-setup-grub.sh"
        self.unmount_disk_command = "./mk-unmount-disk.sh"

        if '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
コード例 #13
0
ファイル: installer.py プロジェクト: hartsock/photon
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
コード例 #14
0
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()
コード例 #15
0
ファイル: installer.py プロジェクト: vMarkus/photon
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)