Esempio n. 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=None):
        return self.window.do_action()
Esempio n. 2
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()
Esempio n. 3
0
class WindowStringReader(object):
    def __init__(self, maxy, maxx, height, width, ispassword, title,
                 display_string, inputy, install_config):
        self.ispassword = ispassword
        self.title = title
        self.display_string = display_string
        self.inputy = inputy

        self.width = width
        self.height = height
        self.maxx = maxx
        self.maxy = maxy

        self.startx = (self.maxx - self.width) / 2
        self.starty = (self.maxy - self.height) / 2

        self.window = Window(self.height, self.width, self.maxy, self.maxx,
                             self.title, True)
        self.read_text = ReadText(self.window.content_window(), self.inputy,
                                  install_config, self.ispassword)
        self.window.set_action_panel(self.read_text)
        self.window.addstr(0, 0, self.display_string)

    def get_user_string(self, params):
        return self.window.do_action()
Esempio n. 4
0
def main(screen):

   # setup the basic syntax for text files
   text_syntax = SyntaxClass("basic")
   text_syntax.add("character", r".")
   text_syntax.add("word", r" ")
   text_syntax.add("sentance", r"\.")

   # make the main window 
   mainwin = Window(screen, FileBuffer(open("README.markdown")))
   mainwin.render()

   # the event loop
   while True: 
      event = screen.getch()
      if event == ord("q"): break 
      
      if event == ord("e"): mainwin.clear()
      if event == ord("r"): mainwin.render()
      if event == ord("f"): mainwin.addstr("X")
      if event == ord("w"): mainwin.rmove(0, -1)
      if event == ord("a"): mainwin.rmove(-1, 0)
      if event == ord("s"): mainwin.rmove(0, +1)
      if event == ord("d"): mainwin.rmove(+1, 0)
      if event == ord("x"): mainwin.here()

      
      def find(syntaxname):
         return lambda t, x, y: text_syntax.find(syntaxname, t, x, y)

      if event == ord("c"): mainwin.movef(find("character"))
      if event == ord("v"): mainwin.movef(find("word"))
Esempio n. 5
0
class DiskPartitioner(object):
    def __init__(self,  maxy, maxx):
        self.menu_items = []

        self.maxx = maxx
        self.maxy = maxy
        self.win_width = 70
        self.win_height = 17

        self.win_starty = (self.maxy - self.win_height) / 2
        self.win_startx = (self.maxx - self.win_width) / 2

        self.menu_starty = self.win_starty + 10

        # initialize the devices
        self.devices = Device.refresh_devices()

        self.items =   [
                            ('Auto-partitioning - use entire disk',  self.guided_partitions, None),
                            ('Manual - not implemented!',  self.manual_partitions, None),
                        ]
        self.menu = Menu(self.menu_starty,  self.maxx, self.items)

        self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Welcome to the Photon installer', True, self.menu)
        self.window.addstr(0, 0, 'First, we will setup your disks. \n\nYou can: \n\na) use auto-partitioning or\nb) you can do it manually.')

    def guided_partitions(self, params):
        return ActionResult(True, {'guided': True, 'devices': self.devices})

    def manual_partitions(self, params):
        raise NameError('Manual partitioning not implemented')

    def display(self, params):
        return self.window.do_action()
class LinuxSelector(object):
    def __init__(self, maxy, maxx, install_config):
        self.install_config = install_config
        self.maxx = maxx
        self.maxy = maxy
        self.win_width = 60
        self.win_height = 16

        self.win_starty = (self.maxy - self.win_height) // 2
        self.win_startx = (self.maxx - self.win_width) // 2

        self.menu_starty = self.win_starty + 6

    def set_linux_esx_installation(self, is_linux_esx):
        self.install_config['install_linux_esx'] = is_linux_esx
        return ActionResult(True, None)

    def set_linux_installation(self, selected_linux_package):
        self.install_config['linux_flavor'] = selected_linux_package
        return ActionResult(True, None)

    def create_available_linux_menu(self):
        linux_flavors = {"linux":"Generic", "linux-esx":"VMware hypervisor optimized", "linux-aws":"AWS optimized", "linux-secure":"Security hardened", "linux-rt":"Real Time"}

        self.menu_items = []
        for flavor,menu_entry in linux_flavors.items():
            if flavor in self.install_config['packages']:
                if flavor == "linux-esx" and not CommandUtils.is_vmware_virtualization():
                    continue
                self.menu_items.append((menu_entry, self.set_linux_installation, flavor))

        if len(self.menu_items) == 1:
            self.install_config['linux_flavor'] = self.menu_items[0][2]

        self.host_menu = Menu(self.menu_starty, self.maxx, self.menu_items,
                              default_selected=0, tab_enable=False)

        self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx,
                             'Select Linux kernel to install', True, tab_enabled=False,
                             position=1, can_go_next=True)
        self.window.set_action_panel(self.host_menu)

    def display(self):
        if 'ostree' in self.install_config:
            return ActionResult(None, {"inactive_screen": True})

        self.create_available_linux_menu()
        if len(self.menu_items) < 2:
            return ActionResult(None, {"inactive_screen": True})

        self.window.addstr(0, 0, 'Which type of Linux kernel would you like to install?')
        return self.window.do_action()
Esempio n. 7
0
    def display(self):
        if self.setup_network and not self.do_setup_network():
            return ActionResult(False, {'goBack': True})

        file_source = {}
        accepted_chars = list(range(ord('a'), ord('z') + 1))
        accepted_chars.extend(range(ord('A'), ord('Z') + 1))
        accepted_chars.extend(range(ord('0'), ord('9') + 1))
        accepted_chars.extend(
            [ord('-'),
             ord('_'),
             ord('.'),
             ord('~'),
             ord(':'),
             ord('/')])
        result = WindowStringReader(self.maxy, self.maxx, 18, 78, 'url', None,
                                    None, accepted_chars, None, None,
                                    self.title, self.intro, 10, file_source,
                                    'https://', True).get_user_string(None)
        if not result.success:
            return result

        status_window = Window(10, 70, self.maxy, self.maxx,
                               'Installing Photon', False)
        status_window.addstr(1, 0, 'Downloading file...')
        status_window.show_window()

        fd, temp_file = tempfile.mkstemp()
        result, msg = CommandUtils.wget(
            file_source['url'],
            temp_file,
            ask_fn=self.ask_proceed_unsafe_download)
        os.close(fd)
        if not result:
            status_window.adderror('Error: ' + msg +
                                   ' Press any key to go back...')
            status_window.content_window().getch()
            status_window.clearerror()
            status_window.hide_window()
            return ActionResult(False, {'goBack': True})

        if 'additional_files' not in self.install_config:
            self.install_config['additional_files'] = []
        copy = {temp_file: self.dest}
        self.install_config['additional_files'].append(copy)

        return ActionResult(True, None)
Esempio n. 8
0
class License(object):
    def __init__(self, maxy, maxx, eula_file_path, display_title):
        self.maxx = maxx
        self.maxy = maxy
        self.win_width = maxx - 4
        self.win_height = maxy - 4

        self.win_starty = (self.maxy - self.win_height) // 2
        self.win_startx = (self.maxx - self.win_width) // 2

        self.text_starty = self.win_starty + 4
        self.text_height = self.win_height - 6
        self.text_width = self.win_width - 6

        self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx,
                             'Welcome to the Photon installer', False)

        if eula_file_path:
            self.eula_file_path = eula_file_path
        else:
            self.eula_file_path = join(dirname(__file__), 'EULA.txt')

        if display_title:
            self.title = display_title
        else:
            self.title = 'VMWARE LICENSE AGREEMENT'

    def display(self):
        accept_decline_items = [('<Accept>', self.accept_function),
                                ('<Cancel>', self.exit_function)]

        self.window.addstr(0, (self.win_width - len(self.title)) // 2, self.title)
        self.text_pane = TextPane(self.text_starty, self.maxx, self.text_width,
                                  self.eula_file_path, self.text_height, accept_decline_items)

        self.window.set_action_panel(self.text_pane)

        return self.window.do_action()


    def accept_function(self):
        return ActionResult(True, None)

    def exit_function(self):
        exit(0)
Esempio n. 9
0
class License(object):
    def __init__(self, maxy, maxx):
        self.maxx = maxx
        self.maxy = maxy
        self.win_width = maxx - 4
        self.win_height = maxy - 4

        self.win_starty = (self.maxy - self.win_height) / 2
        self.win_startx = (self.maxx - self.win_width) / 2

        self.text_starty = self.win_starty + 4
        self.text_height = self.win_height - 6
        self.text_width = self.win_width - 6

        self.window = Window(self.win_height,
                             self.win_width,
                             self.maxy,
                             self.maxx,
                             'Welcome to the Photon installer',
                             False,
                             items=[])

    def display(self, params):
        accept_decline_items = [('<Accept>', self.accept_function),
                                ('<Cancel>', self.exit_function)]

        title = 'VMWARE 1.0 LICENSE AGREEMENT'
        self.window.addstr(0, (self.win_width - len(title)) / 2, title)
        self.text_pane = TextPane(self.text_starty, self.maxx, self.text_width,
                                  "EULA.txt", self.text_height,
                                  accept_decline_items)

        self.window.set_action_panel(self.text_pane)

        return self.window.do_action()

    def accept_function(self):
        return ActionResult(True, None)

    def exit_function(self):
        exit(0)
Esempio n. 10
0
class WindowStringReader(object):
    def __init__(self, maxy, maxx, height, width, ispassword, confirm_password, title, display_string, inputy, install_config):
        self.title = title
        self.display_string = display_string
        self.inputy = inputy

        self.width = width
        self.height = height
        self.maxx = maxx
        self.maxy = maxy

        self.startx = (self.maxx - self.width) / 2
        self.starty = (self.maxy - self.height) / 2

        self.window = Window(self.height, self.width, self.maxy, self.maxx, self.title, True)
        self.read_text = ReadText(maxy, maxx, self.window.content_window(), self.inputy, install_config, ispassword, confirm_password)
        self.window.set_action_panel(self.read_text)
        self.window.addstr(0, 0, self.display_string)

    def get_user_string(self, params):
        return self.window.do_action()
class DiskPartitioner(object):
    def __init__(self, maxy, maxx):
        self.menu_items = []

        self.maxx = maxx
        self.maxy = maxy
        self.win_width = 70
        self.win_height = 17

        self.win_starty = (self.maxy - self.win_height) / 2
        self.win_startx = (self.maxx - self.win_width) / 2

        self.menu_starty = self.win_starty + 10

        # initialize the devices
        self.devices = Device.refresh_devices()

        self.items = [
            ('Auto-partitioning - use entire disk', self.guided_partitions,
             None),
            ('Manual - not implemented!', self.manual_partitions, None),
        ]
        self.menu = Menu(self.menu_starty, self.maxx, self.items)

        self.window = Window(self.win_height, self.win_width, self.maxy,
                             self.maxx, 'Welcome to the Photon installer',
                             True, self.menu)
        self.window.addstr(
            0, 0,
            'First, we will setup your disks. \n\nYou can: \n\na) use auto-partitioning or\nb) you can do it manually.'
        )

    def guided_partitions(self, params):
        return ActionResult(True, {'guided': True, 'devices': self.devices})

    def manual_partitions(self, params):
        raise NameError('Manual partitioning not implemented')

    def display(self, params):
        return self.window.do_action()
Esempio n. 12
0
class LinuxSelector(object):
    def __init__(self, maxy, maxx, install_config):
        self.install_config = install_config
        self.maxx = maxx
        self.maxy = maxy
        self.win_width = 60
        self.win_height = 13

        self.win_starty = (self.maxy - self.win_height) / 2
        self.win_startx = (self.maxx - self.win_width) / 2

        self.menu_starty = self.win_starty + 6

        self.menu_items = []
        self.menu_items.append(("1. Hypervisor optimized", self.set_linux_esx_installation, True))
        self.menu_items.append(("2. Generic", self.set_linux_esx_installation, False))

        self.host_menu = Menu(self.menu_starty, self.maxx, self.menu_items,
                              default_selected=0, tab_enable=False)

        self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx,
                             'Select Linux kernel to install', True, items=[], tab_enabled=False,
                             position=1, can_go_next=True)
        self.window.set_action_panel(self.host_menu)

    def set_linux_esx_installation(self, is_linux_esx):
        self.install_config['install_linux_esx'] = is_linux_esx
        return ActionResult(True, None)

    def display(self, params):
        self.window.addstr(0, 0, 'The installer has detected that you are installing')
        self.window.addstr(1, 0, 'Photon OS on a VMware hypervisor.')
        self.window.addstr(2, 0, 'Which type of Linux kernel would you like to install?')
        return self.window.do_action()
Esempio n. 13
0
class LinuxSelector(object):
    def __init__(self, maxy, maxx, install_config):
        self.install_config = install_config
        self.maxx = maxx
        self.maxy = maxy
        self.win_width = 60
        self.win_height = 13

        self.win_starty = (self.maxy - self.win_height) // 2
        self.win_startx = (self.maxx - self.win_width) // 2

        self.menu_starty = self.win_starty + 6

        self.menu_items = []
        self.menu_items.append(("1. Hypervisor optimized", self.set_linux_esx_installation, True))
        self.menu_items.append(("2. Generic", self.set_linux_esx_installation, False))

        self.host_menu = Menu(self.menu_starty, self.maxx, self.menu_items,
                              default_selected=0, tab_enable=False)

        self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx,
                             'Select Linux kernel to install', True, tab_enabled=False,
                             position=1, can_go_next=True)
        self.window.set_action_panel(self.host_menu)

    def set_linux_esx_installation(self, is_linux_esx):
        self.install_config['install_linux_esx'] = is_linux_esx
        return ActionResult(True, None)

    def display(self, params):
        self.window.addstr(0, 0, 'The installer has detected that you are installing')
        self.window.addstr(1, 0, 'Photon OS on a VMware hypervisor.')
        self.window.addstr(2, 0, 'Which type of Linux kernel would you like to install?')
        return self.window.do_action()
Esempio n. 14
0
class License(object):
    def __init__(self, maxy, maxx):
        self.maxx = maxx
        self.maxy = maxy
        self.win_width = maxx - 4
        self.win_height = maxy - 4

        self.win_starty = (self.maxy - self.win_height) // 2
        self.win_startx = (self.maxx - self.win_width) // 2

        self.text_starty = self.win_starty + 4
        self.text_height = self.win_height - 6
        self.text_width = self.win_width - 6

        self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx,
                             'Welcome to the Photon installer', False)

    def display(self, params):
        accept_decline_items = [('<Accept>', self.accept_function),
                                ('<Cancel>', self.exit_function)]

        title = 'VMWARE 2.0 LICENSE AGREEMENT'
        self.window.addstr(0, (self.win_width - len(title)) // 2, title)
        self.text_pane = TextPane(self.text_starty, self.maxx, self.text_width,
                                  "EULA.txt", self.text_height, accept_decline_items)

        self.window.set_action_panel(self.text_pane)

        return self.window.do_action()


    def accept_function(self):
        return ActionResult(True, None)

    def exit_function(self):
        exit(0)
Esempio n. 15
0
class SelectDisk(object):
    def __init__(self, maxy, maxx, install_config):
        self.install_config = install_config
        self.menu_items = []

        self.maxx = maxx
        self.maxy = maxy
        self.win_width = 70
        self.win_height = 16

        self.win_starty = (self.maxy - self.win_height) / 2
        self.win_startx = (self.maxx - self.win_width) / 2

        self.menu_starty = self.win_starty + 6
        self.menu_height = 5

        self.window = Window(self.win_height, self.win_width, self.maxy,
                             self.maxx, 'Setup your disk', True)
        self.devices = Device.refresh_devices()

    def guided_partitions(self, device_index):
        menu_height = 9
        menu_width = 40
        menu_starty = (self.maxy - menu_height) / 2 + 5
        confrim_window = ConfirmWindow(
            menu_height, menu_width, self.maxy, self.maxx, menu_starty,
            'This will erase the disk.\nAre you sure?')
        confirmed = confrim_window.do_action().result['yes']

        if not confirmed:
            return ActionResult(confirmed, None)

        #self.install_config['disk'] = self.devices[device_index].path
        #return ActionResult(True, None)

        # Do the partitioning
        self.window.clearerror()
        json_ret = subprocess.check_output([
            'gpartedbin', 'defaultpartitions', self.devices[device_index].path
        ],
                                           stderr=open(os.devnull, 'w'))
        json_dct = json.loads(json_ret)
        if json_dct['success']:
            self.install_config['disk'] = json_dct['data']
        else:
            self.window.adderror('Partitioning failed, you may try again')
        return ActionResult(json_dct['success'], None)

    def display(self, params):
        self.window.addstr(
            0, 0,
            'First, we will setup your disks.\n\nWe have detected {0} disks, choose disk to be auto-partitioned:'
            .format(len(self.devices)))

        self.disk_menu_items = []

        # Fill in the menu items
        for index, device in enumerate(self.devices):
            #if index > 0:
            self.disk_menu_items.append(
                ('{2} - {1} MB @ {0}'.format(device.path, device.size,
                                             device.model),
                 self.guided_partitions, index))

        self.disk_menu = Menu(self.menu_starty, self.maxx,
                              self.disk_menu_items, self.menu_height)

        self.window.set_action_panel(self.disk_menu)

        return self.window.do_action()
Esempio n. 16
0
class Installer(object):
    def __init__(self,
                 install_config,
                 maxy=0,
                 maxx=0,
                 iso_installer=False,
                 rpm_path="../stage/RPMS",
                 log_path="../stage/LOGS"):
        self.install_config = install_config
        self.iso_installer = iso_installer
        self.rpm_path = rpm_path
        self.log_path = log_path
        self.mount_command = "./mk-mount-disk.sh"
        self.prepare_command = "./mk-prepare-system.sh"
        self.finalize_command = "./mk-finalize-system.sh"
        self.chroot_command = "./mk-run-chroot.sh"
        self.setup_grub_command = "./mk-setup-grub.sh"
        self.unmount_disk_command = "./mk-unmount-disk.sh"

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

        self.restart_command = "shutdown"

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

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

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

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

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

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

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

    def unsafe_install(self, params):

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

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

        self.initialize_system()

        if self.iso_installer:
            self.adjust_packages_for_vmware_virt()
            selected_packages = self.install_config['packages']
            state = 0
            packages_to_install = {}
            total_size = 0
            with open(modules.commons.TDNF_CMDLINE_FILE_NAME,
                      "w") as tdnf_cmdline_file:
                tdnf_cmdline_file.write(
                    "tdnf install --installroot {0} --nogpgcheck {1}".format(
                        self.photon_root, " ".join(selected_packages)))
            with open(modules.commons.TDNF_LOG_FILE_NAME, "w") as tdnf_errlog:
                process = subprocess.Popen(
                    ['tdnf', 'install'] + selected_packages + [
                        '--installroot', self.photon_root, '--nogpgcheck',
                        '--assumeyes'
                    ],
                    stdout=subprocess.PIPE,
                    stderr=tdnf_errlog)
                while True:
                    output = process.stdout.readline()
                    if output == '':
                        retval = process.poll()
                        if retval is not None:
                            break
                    if state == 0:
                        if output == 'Installing:\n':
                            state = 1
                    elif state == 1:  #N A EVR Size(readable) Size(in bytes)
                        if output == '\n':
                            state = 2
                            self.progress_bar.update_num_items(total_size)
                        else:
                            info = output.split()
                            package = '{0}-{1}.{2}'.format(
                                info[0], info[2], info[1])
                            packages_to_install[package] = int(info[5])
                            total_size += int(info[5])
                    elif state == 2:
                        if output == 'Downloading:\n':
                            self.progress_bar.update_message('Preparing ...')
                            state = 3
                    elif state == 3:
                        self.progress_bar.update_message(output)
                        if output == 'Running transaction\n':
                            state = 4
                    else:
                        modules.commons.log(modules.commons.LOG_INFO,
                                            "[tdnf] {0}".format(output))
                        prefix = 'Installing/Updating: '
                        if output.startswith(prefix):
                            package = output[len(prefix):].rstrip('\n')
                            self.progress_bar.increment(
                                packages_to_install[package])

                        self.progress_bar.update_message(output)
                # 0 : succeed; 137 : package already installed; 65 : package not found in repo.
                if retval != 0 and retval != 137:
                    modules.commons.log(
                        modules.commons.LOG_ERROR,
                        "Failed to install some packages, refer to {0}".format(
                            modules.commons.TDNF_LOG_FILE_NAME))
                    self.exit_gracefully(None, None)
        else:
            #install packages
            rpms = []
            for rpm in self.rpms_tobeinstalled:
                # We already installed the filesystem in the preparation
                if rpm['package'] == 'filesystem':
                    continue
                rpms.append(rpm['filename'])
            return_value = self.install_package(rpms)
            if return_value != 0:
                self.exit_gracefully(None, None)

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

        if os.path.exists("/etc/resolv.conf"):
            shutil.copy("/etc/resolv.conf", self.photon_root + '/etc/.')
        self.finalize_system()

        if not self.install_config['iso_system']:
            # Execute post installation modules
            self.execute_modules(modules.commons.POST_INSTALL)
            if os.path.exists(modules.commons.KS_POST_INSTALL_LOG_FILE_NAME):
                shutil.copy(modules.commons.KS_POST_INSTALL_LOG_FILE_NAME,
                            self.photon_root + '/var/log/')

            if self.iso_installer and os.path.isdir("/sys/firmware/efi"):
                self.install_config['boot'] = 'efi'
            # install grub
            if 'boot_partition_number' not in self.install_config['disk']:
                self.install_config['disk']['boot_partition_number'] = 1

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

            self.update_fstab()

        if os.path.exists(self.photon_root + '/etc/resolv.conf'):
            os.remove(self.photon_root + '/etc/resolv.conf')

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

        if self.iso_installer:
            self.progress_bar.hide()
            self.window.addstr(
                0, 0,
                'Congratulations, Photon has been installed in {0} secs.\n\nPress any key to continue to boot...'
                .format(self.progress_bar.time_elapsed))
            eject_cdrom = True
            if 'ui_install' in self.install_config:
                self.window.content_window().getch()
            if 'eject_cdrom' in self.install_config and not self.install_config[
                    'eject_cdrom']:
                eject_cdrom = False
            if eject_cdrom:
                process = subprocess.Popen(['eject', '-r'], stdout=self.output)
                process.wait()
        return ActionResult(True, None)

    def copy_rpms(self):
        # prepare the RPMs list
        json_pkg_to_rpm_map = JsonWrapper(
            self.install_config["pkg_to_rpm_map_file"])
        pkg_to_rpm_map = json_pkg_to_rpm_map.read()

        self.rpms_tobeinstalled = []
        selected_packages = self.install_config['packages']

        for pkg in selected_packages:
            if pkg in pkg_to_rpm_map:
                if not pkg_to_rpm_map[pkg]['rpm'] is None:
                    name = pkg_to_rpm_map[pkg]['rpm']
                    basename = os.path.basename(name)
                    self.rpms_tobeinstalled.append({
                        'filename': basename,
                        'path': name,
                        'package': pkg
                    })

        # Copy the rpms
        for rpm in self.rpms_tobeinstalled:
            shutil.copy(rpm['path'], self.photon_root + '/RPMS/')

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

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

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

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

    def bind_repo_dir(self):
        rpm_cache_dir = self.photon_root + '/cache/tdnf/photon-iso/rpms'
        if self.rpm_path.startswith("https://") or self.rpm_path.startswith(
                "http://"):
            return
        if subprocess.call(
            ['mkdir', '-p', rpm_cache_dir]) != 0 or subprocess.call(
                ['mount', '--bind', self.rpm_path, rpm_cache_dir]) != 0:
            modules.commons.log(modules.commons.LOG_ERROR,
                                "Fail to bind cache rpms")
            self.exit_gracefully(None, None)

    def unbind_repo_dir(self):
        rpm_cache_dir = self.photon_root + '/cache/tdnf/photon-iso/rpms'
        if self.rpm_path.startswith("https://") or self.rpm_path.startswith(
                "http://"):
            return
        if subprocess.call(['umount', rpm_cache_dir]) != 0 or subprocess.call(
            ['rm', '-rf', rpm_cache_dir]) != 0:
            modules.commons.log(modules.commons.LOG_ERROR,
                                "Fail to unbind cache rpms")
            self.exit_gracefully(None, None)

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

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

                if 'mountpoint' in partition and partition['mountpoint'] == '/':
                    options = options + ',barrier,noatime,noacl,data=ordered'
                    fsck = 1

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

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

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

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

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

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

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

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

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

    def install_package(self, rpm_file_names):

        rpms = set(rpm_file_names)
        rpm_paths = []
        for root, dirs, files in os.walk(self.rpm_path):
            for f in files:
                if f in rpms:
                    rpm_paths.append(os.path.join(root, f))

        # --nodeps is for hosts which do not support rich dependencies
        rpm_params = [
            '--nodeps', '--root', self.photon_root, '--dbpath', '/var/lib/rpm'
        ]

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

        modules.commons.log(
            modules.commons.LOG_INFO,
            "installing packages {0}, with params {1}".format(
                rpm_paths, rpm_params))
        process = subprocess.Popen(['rpm', '-Uvh'] + rpm_params + rpm_paths,
                                   stderr=subprocess.STDOUT)
        return process.wait()

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

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

    def adjust_packages_for_vmware_virt(self):
        try:
            if self.install_config['install_linux_esx']:
                selected_packages = self.install_config['packages']
                try:
                    selected_packages.remove('linux')
                except ValueError:
                    pass
                try:
                    selected_packages.remove('initramfs')
                except ValueError:
                    pass
                selected_packages.append('linux-esx')
        except KeyError:
            pass

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

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

        return err
Esempio n. 17
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
Esempio n. 18
0
class Installer(object):
    """
    Photon installer
    """
    mount_command = os.path.dirname(__file__)+"/mk-mount-disk.sh"
    finalize_command = "./mk-finalize-system.sh"
    chroot_command = os.path.dirname(__file__)+"/mk-run-chroot.sh"
    unmount_disk_command = os.path.dirname(__file__)+"/mk-unmount-disk.sh"

    def __init__(self, install_config, maxy=0, maxx=0, iso_installer=False,
                 rpm_path=os.path.dirname(__file__)+"/../stage/RPMS", log_path=os.path.dirname(__file__)+"/../stage/LOGS", log_level="info"):
        self.install_config = install_config
        self.install_config['iso_installer'] = iso_installer
        self.rpm_path = rpm_path
        self.log_path = log_path
        self.log_level = log_level

        if 'working_directory' in self.install_config:
            self.working_directory = self.install_config['working_directory']
        else:
            self.working_directory = "/mnt/photon-root"
        if 'prepare_script' in self.install_config:
            self.prepare_command = self.install_config['prepare_script']
        else:
            self.prepare_command = os.path.dirname(__file__)+"/mk-prepare-system.sh"
        self.photon_root = self.working_directory + "/photon-chroot"
        if 'setup_grub_script' in self.install_config:
            self.setup_grub_command = self.install_config['setup_grub_script']
        else:
            self.setup_grub_command = os.path.dirname(__file__)+"/mk-setup-grub.sh"
        self.rpms_tobeinstalled = None

        if self.install_config['iso_installer']:
            self.output = open(os.devnull, 'w')
            #initializing windows
            height = 10
            width = 75
            progress_padding = 5

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

        else:
            self.output = None
        signal.signal(signal.SIGINT, self.exit_gracefully)

    def install(self, params):
        """
        Install photon system and handle exception
        """
        del params
        try:
            return self._unsafe_install()
        except Exception as inst:
            if self.install_config['iso_installer']:
                modules.commons.log(modules.commons.LOG_ERROR, repr(inst))
                self.exit_gracefully(None, None)
            else:
                raise

    def _unsafe_install(self):
        """
        Install photon system
        """
        self._setup_install_repo()
        self._initialize_system()
        self._install_packages()
        self._enable_network_in_chroot()
        self._finalize_system()

        self._disable_network_in_chroot()
        self._cleanup_and_exit()
        return ActionResult(True, None)

    def exit_gracefully(self, signal1, frame1):
        """
        This will be called if the installer interrupted by Ctrl+C, exception
        or other failures
        """
        del signal1
        del frame1
        if self.install_config['iso_installer']:
            self.progress_bar.hide()
            self.window.addstr(0, 0, 'Oops, Installer got interrupted.\n\n' +
                               'Press any key to get to the bash...')
            self.window.content_window().getch()

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

    def _cleanup_and_exit(self):
        """
        Unmount the disk, eject cd and exit
        """
        command = [Installer.unmount_disk_command, '-w', self.photon_root]
        if not self.install_config['iso_system']:
            command.extend(self._generate_partitions_param(reverse=True))
        process = subprocess.Popen(command, stdout=self.output)
        retval = process.wait()
        if retval != 0:
            modules.commons.log(modules.commons.LOG_ERROR, "Failed to unmount disks")
        if self.install_config['iso_installer']:
            self.progress_bar.hide()
            self.window.addstr(0, 0, 'Congratulations, Photon has been installed in {0} secs.\n\n'
                               'Press any key to continue to boot...'
                               .format(self.progress_bar.time_elapsed))
            self._eject_cdrom()

    def _copy_rpms(self):
        """
        Prepare RPM list and copy rpms
        """
        # prepare the RPMs list
        json_pkg_to_rpm_map = JsonWrapper(self.install_config["pkg_to_rpm_map_file"])
        pkg_to_rpm_map = json_pkg_to_rpm_map.read()

        self.rpms_tobeinstalled = []
        selected_packages = self.install_config['packages']

        for pkg in selected_packages:
            versionindex = pkg.rfind("-")
            if versionindex == -1:
                raise Exception("Invalid pkg name: " + pkg)
            package = pkg[:versionindex]
            if pkg in pkg_to_rpm_map:
                if pkg_to_rpm_map[pkg]['rpm'] is not None:
                    name = pkg_to_rpm_map[pkg]['rpm']
                    basename = os.path.basename(name)
                    self.rpms_tobeinstalled.append({'filename': basename, 'path': name,
                                                    'package' : package})

        # Copy the rpms
        for rpm in self.rpms_tobeinstalled:
            shutil.copy(rpm['path'], self.photon_root + '/RPMS/')

    def _copy_files(self):
        """
        Copy the rpm files and instal scripts.
        """
        # Make the photon_root directory if not exits
        process = subprocess.Popen(['mkdir', '-p', self.photon_root], stdout=self.output)
        retval = process.wait()
        if retval != 0:
            modules.commons.log(modules.commons.LOG_ERROR, "Fail to create the root directory")
            self.exit_gracefully(None, None)

        # Copy the installer files
        process = subprocess.Popen(['cp', '-r', os.path.dirname(__file__), self.photon_root],
                                   stdout=self.output)
        retval = process.wait()
        if retval != 0:
            modules.commons.log(modules.commons.LOG_ERROR, "Fail to copy install scripts")
            self.exit_gracefully(None, None)

        # Create the rpms directory
        process = subprocess.Popen(['mkdir', '-p', self.photon_root + '/RPMS'],
                                   stdout=self.output)
        retval = process.wait()
        if retval != 0:
            modules.commons.log(modules.commons.LOG_ERROR, "Fail to create the rpms directory")
            self.exit_gracefully(None, None)
        self._copy_rpms()

    def _bind_installer(self):
        """
        Make the photon_root/installer directory if not exits
        The function finalize_system will access the file /installer/mk-finalize-system.sh
        after chroot to photon_root.
        Bind the /installer folder to self.photon_root/installer, so that after chroot
        to photon_root,
        the file can still be accessed as /installer/mk-finalize-system.sh.
        """
        # Make the photon_root/installer directory if not exits
        if(subprocess.call(['mkdir', '-p',
                            os.path.join(self.photon_root, "installer")]) != 0 or
           subprocess.call(['mount', '--bind', '/installer',
                            os.path.join(self.photon_root, "installer")]) != 0):
            modules.commons.log(modules.commons.LOG_ERROR, "Fail to bind installer")
            self.exit_gracefully(None, None)
    def _unbind_installer(self):
        # unmount the installer directory
        process = subprocess.Popen(['umount', os.path.join(self.photon_root,
                                                           "installer")],
                                   stdout=self.output)
        retval = process.wait()
        if retval != 0:
            modules.commons.log(modules.commons.LOG_ERROR,
                                "Fail to unbind the installer directory")
        # remove the installer directory
        process = subprocess.Popen(['rm', '-rf', os.path.join(self.photon_root, "installer")],
                                   stdout=self.output)
        retval = process.wait()
        if retval != 0:
            modules.commons.log(modules.commons.LOG_ERROR,
                                "Fail to remove the installer directory")
    def _bind_repo_dir(self):
        """
        Bind repo dir for tdnf installation
        """
        rpm_cache_dir = self.photon_root + '/cache/tdnf/photon-iso/rpms'
        if self.rpm_path.startswith("https://") or self.rpm_path.startswith("http://"):
            return
        if (subprocess.call(['mkdir', '-p', rpm_cache_dir]) != 0 or
                subprocess.call(['mount', '--bind', self.rpm_path, rpm_cache_dir]) != 0):
            modules.commons.log(modules.commons.LOG_ERROR, "Fail to bind cache rpms")
            self.exit_gracefully(None, None)

    def _unbind_repo_dir(self):
        """
        Unbind repo dir after installation
        """
        rpm_cache_dir = self.photon_root + '/cache/tdnf/photon-iso/rpms'
        if self.rpm_path.startswith("https://") or self.rpm_path.startswith("http://"):
            return
        if (subprocess.call(['umount', rpm_cache_dir]) != 0 or
                subprocess.call(['rm', '-rf', rpm_cache_dir]) != 0):
            modules.commons.log(modules.commons.LOG_ERROR, "Fail to unbind cache rpms")
            self.exit_gracefully(None, None)

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

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

                if 'mountpoint' in partition and partition['mountpoint'] == '/':
                    options = options + ',barrier,noatime,noacl,data=ordered'
                    fsck = 1

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

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

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

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

    def _initialize_system(self):
        """
        Prepare the system to install photon
        """
        #Setup the disk
        if not self.install_config['iso_system']:
            command = [Installer.mount_command, '-w', self.photon_root]
            command.extend(self._generate_partitions_param())
            process = subprocess.Popen(command, stdout=self.output)
            retval = process.wait()
            if retval != 0:
                modules.commons.log(modules.commons.LOG_INFO,
                                    "Failed to setup the disk for installation")
                self.exit_gracefully(None, None)

        if self.install_config['iso_installer']:
            self._bind_installer()
            self._bind_repo_dir()
            process = subprocess.Popen([self.prepare_command, '-w',
                                        self.photon_root, 'install'],
                                       stdout=self.output)
            retval = process.wait()
            if retval != 0:
                modules.commons.log(modules.commons.LOG_INFO,
                                    "Failed to bind the installer and repo needed by tdnf")
                self.exit_gracefully(None, None)
        else:
            self._copy_files()
            #Setup the filesystem basics
            process = subprocess.Popen([self.prepare_command, '-w', self.photon_root],
                                       stdout=self.output)
            retval = process.wait()
            if retval != 0:
                modules.commons.log(modules.commons.LOG_INFO,
                                    "Failed to setup the file systems basics")
                self.exit_gracefully(None, None)

    def _finalize_system(self):
        """
        Finalize the system after the installation
        """
        #Setup the disk
        process = subprocess.Popen([Installer.chroot_command, '-w', self.photon_root,
                                    Installer.finalize_command, '-w', self.photon_root],
                                   stdout=self.output)
        retval = process.wait()
        if retval != 0:
            modules.commons.log(modules.commons.LOG_ERROR,
                                "Fail to setup the target system after the installation")

        if self.install_config['iso_installer']:

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

            self._unbind_installer()
            self._unbind_repo_dir()
            # Disable the swap file
            process = subprocess.Popen(['swapoff', '-a'], stdout=self.output)
            retval = process.wait()
            if retval != 0:
                modules.commons.log(modules.commons.LOG_ERROR,
                                    "Fail to swapoff")
            # remove the tdnf cache directory and the swapfile.
            process = subprocess.Popen(['rm', '-rf', os.path.join(self.photon_root, "cache")],
                                       stdout=self.output)
            retval = process.wait()
            if retval != 0:
                modules.commons.log(modules.commons.LOG_ERROR,
                                    "Fail to remove the cache")
        if not self.install_config['iso_system']:
            # Execute post installation modules
            self._execute_modules(modules.commons.POST_INSTALL)
            if os.path.exists(modules.commons.KS_POST_INSTALL_LOG_FILE_NAME):
                shutil.copy(modules.commons.KS_POST_INSTALL_LOG_FILE_NAME,
                            self.photon_root + '/var/log/')

            if self.install_config['iso_installer'] and os.path.isdir("/sys/firmware/efi"):
                self.install_config['boot'] = 'efi'
            # install grub
            if 'boot_partition_number' not in self.install_config['disk']:
                self.install_config['disk']['boot_partition_number'] = 1

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

            if retval != 0:
                raise Exception("Bootloader (grub2) setup failed")

            self._update_fstab()
            if not self.install_config['iso_installer']:
                process = subprocess.Popen(['rm', '-rf', os.path.join(self.photon_root, "installer")],
                                           stdout=self.output)
                retval = process.wait()
                if retval != 0:
                    modules.commons.log(modules.commons.LOG_ERROR,
                                        "Fail to remove the installer directory")
                process = subprocess.Popen(['rm', '-rf', os.path.join(self.photon_root, "RPMS")],
                                           stdout=self.output)
                retval = process.wait()
                if retval != 0:
                    modules.commons.log(modules.commons.LOG_ERROR,
                                        "Fail to remove the input rpms directory")


    def _execute_modules(self, phase):
        """
        Execute the scripts in the modules folder
        """
        sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "modules")))
        modules_paths = glob.glob(os.path.abspath(os.path.join(os.path.dirname(__file__), 'modules')) + '/m_*.py')
        for mod_path in modules_paths:
            module = os.path.splitext(os.path.basename(mod_path))[0]
            try:
                __import__(module)
                mod = sys.modules[module]
            except ImportError:
                modules.commons.log(modules.commons.LOG_ERROR,
                                    'Error importing module {}'.format(module))
                continue

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

    def _adjust_packages_for_vmware_virt(self):
        """
        Install linux_esx on Vmware virtual machine if requested
        """
        try:
            if self.install_config['install_linux_esx']:
                regex = re.compile(r'^linux-[0-9]|^initramfs-[0-9]')
                self.install_config['packages'] = [x for x in self.install_config['packages'] if not regex.search(x)]
                self.install_config['packages'].append('linux-esx')
        except KeyError:
            pass

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

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

    def _install_packages(self):
        """
        Install packages using tdnf or rpm command
        """
        if self.install_config['iso_installer']:
            self._tdnf_install_packages()
        else:
            self._rpm_install_packages()

    def _tdnf_install_packages(self):
        """
        Install packages using tdnf command
        """
        self._adjust_packages_for_vmware_virt()
        selected_packages = self.install_config['packages']
        state = 0
        packages_to_install = {}
        total_size = 0
        with open(modules.commons.TDNF_CMDLINE_FILE_NAME, "w") as tdnf_cmdline_file:
            tdnf_cmdline_file.write("tdnf install --installroot {0} --nogpgcheck {1}"
                                    .format(self.photon_root, " ".join(selected_packages)))
        with open(modules.commons.TDNF_LOG_FILE_NAME, "w") as tdnf_errlog:
            process = subprocess.Popen(['tdnf', 'install'] + selected_packages +
                                       ['--installroot', self.photon_root, '--nogpgcheck',
                                        '--assumeyes'], stdout=subprocess.PIPE,
                                       stderr=tdnf_errlog)
            while True:
                output = process.stdout.readline().decode()
                if output == '':
                    retval = process.poll()
                    if retval is not None:
                        break
                if state == 0:
                    if output == 'Installing:\n':
                        state = 1
                elif state == 1: #N A EVR Size(readable) Size(in bytes)
                    if output == '\n':
                        state = 2
                        self.progress_bar.update_num_items(total_size)
                    else:
                        info = output.split()
                        package = '{0}-{1}.{2}'.format(info[0], info[2], info[1])
                        packages_to_install[package] = int(info[5])
                        total_size += int(info[5])
                elif state == 2:
                    if output == 'Downloading:\n':
                        self.progress_bar.update_message('Preparing ...')
                        state = 3
                elif state == 3:
                    self.progress_bar.update_message(output)
                    if output == 'Running transaction\n':
                        state = 4
                else:
                    modules.commons.log(modules.commons.LOG_INFO, "[tdnf] {0}".format(output))
                    prefix = 'Installing/Updating: '
                    if output.startswith(prefix):
                        package = output[len(prefix):].rstrip('\n')
                        self.progress_bar.increment(packages_to_install[package])

                    self.progress_bar.update_message(output)
            # 0 : succeed; 137 : package already installed; 65 : package not found in repo.
            if retval != 0 and retval != 137:
                modules.commons.log(modules.commons.LOG_ERROR,
                                    "Failed to install some packages, refer to {0}"
                                    .format(modules.commons.TDNF_LOG_FILE_NAME))
                self.exit_gracefully(None, None)
        self.progress_bar.show_loading('Finalizing installation')

    def _rpm_install_packages(self):
        """
        Install packages using rpm command
        """
        rpms = []
        for rpm in self.rpms_tobeinstalled:
            # We already installed the filesystem in the preparation
            if rpm['package'] == 'filesystem':
                continue
            rpms.append(rpm['filename'])
        rpms = set(rpms)
        rpm_paths = []
        for root, _, files in os.walk(self.rpm_path):
            for file in files:
                if file in rpms:
                    rpm_paths.append(os.path.join(root, file))

        # --nodeps is for hosts which do not support rich dependencies
        rpm_params = ['--nodeps', '--root', self.photon_root, '--dbpath',
                      '/var/lib/rpm']

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

        modules.commons.log(modules.commons.LOG_INFO,
                            "installing packages {0}, with params {1}"
                            .format(rpm_paths, rpm_params))
        process = subprocess.Popen(['rpm', '-Uvh'] + rpm_params + rpm_paths,
                                   stderr=subprocess.STDOUT)
        return_value = process.wait()
        if return_value != 0:
            self.exit_gracefully(None, None)


    def _eject_cdrom(self):
        """
        Eject the cdrom on request
        """
        eject_cdrom = True
        if 'ui_install' in self.install_config:
            self.window.content_window().getch()
        if 'eject_cdrom' in self.install_config and not self.install_config['eject_cdrom']:
            eject_cdrom = False
        if eject_cdrom:
            process = subprocess.Popen(['eject', '-r'], stdout=self.output)
            process.wait()

    def _enable_network_in_chroot(self):
        """
        Enable network in chroot
        """
        if os.path.exists("/etc/resolv.conf"):
            shutil.copy("/etc/resolv.conf", self.photon_root + '/etc/.')

    def _disable_network_in_chroot(self):
        """
        disable network in chroot
        """
        if os.path.exists(self.photon_root + '/etc/resolv.conf'):
            os.remove(self.photon_root + '/etc/resolv.conf')
Esempio n. 19
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.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()
Esempio n. 20
0
class CustomPartition(object):
    def __init__(self, maxy, maxx, install_config):
        self.maxx = maxx
        self.maxy = maxy
        self.win_width = maxx - 4
        self.win_height = maxy - 4
        self.install_config = install_config
        self.path_checker = []

        self.win_starty = (self.maxy - self.win_height) // 2
        self.win_startx = (self.maxx - self.win_width) // 2

        self.text_starty = self.win_starty + 4
        self.text_height = self.win_height - 6
        self.text_width = self.win_width - 6
        self.cp_config = {}
        self.cp_config['partitionsnumber'] = 0
        self.devices = Device.refresh_devices_bytes()
        self.has_slash = False
        self.has_remain = False
        self.has_empty = False

        self.disk_size = []
        self.disk_to_index = {}
        for index, device in enumerate(self.devices):
            self.disk_size.append((device.path, int(device.size) / 1048576))
            self.disk_to_index[device.path] = index

        self.window = Window(self.win_height,
                             self.win_width,
                             self.maxy,
                             self.maxx,
                             'Welcome to the Photon installer',
                             False,
                             can_go_next=False)
        Device.refresh_devices()

    def display(self):
        if 'autopartition' in self.install_config and self.install_config[
                'autopartition'] == True:
            return ActionResult(True, None)

        self.device_index = self.disk_to_index[self.install_config['disk']]

        self.disk_buttom_items = []
        self.disk_buttom_items.append(('<Next>', self.next))
        self.disk_buttom_items.append(('<Create New>', self.create_function))
        self.disk_buttom_items.append(('<Delete All>', self.delete_function))
        self.disk_buttom_items.append(('<Go Back>', self.go_back))

        self.text_items = []
        self.text_items.append(('Disk', 20))
        self.text_items.append(('Size', 5))
        self.text_items.append(('Type', 5))
        self.text_items.append(('Mountpoint', 20))
        self.table_space = 5

        title = 'Current partitions:\n'
        self.window.addstr(0, (self.win_width - len(title)) // 2, title)

        info = ("Unpartitioned space: " +
                str(self.disk_size[self.device_index][1]) +
                " MB, Total size: " +
                str(int(self.devices[self.device_index].size) / 1048576) +
                " MB")

        self.partition_pane = PartitionPane(
            self.text_starty,
            self.maxx,
            self.text_width,
            self.text_height,
            self.disk_buttom_items,
            config=self.cp_config,
            text_items=self.text_items,
            table_space=self.table_space,
            info=info,
            size_left=str(self.disk_size[self.device_index][1]))

        self.window.set_action_panel(self.partition_pane)

        return self.window.do_action()

    def validate_partition(self, pstr):
        if not pstr:
            return ActionResult(False, None)
        sizedata = pstr[0]
        mtdata = pstr[2]
        typedata = pstr[1]
        devicedata = self.devices[self.device_index].path

        #no empty fields unless swap
        if (typedata == 'swap' and
            (len(mtdata) != 0 or len(typedata) == 0 or len(devicedata) == 0)):
            return False, "invalid swap data "

        if (typedata != 'swap'
                and (len(sizedata) == 0 or len(mtdata) == 0
                     or len(typedata) == 0 or len(devicedata) == 0)):
            if not self.has_empty and mtdata and typedata and devicedata:
                self.has_empty = True
            else:
                return False, "Input cannot be empty"

        if typedata != 'swap' and typedata != 'ext3' and typedata != 'ext4':
            return False, "Invalid type"

        if len(mtdata) != 0 and mtdata[0] != '/':
            return False, "Invalid path"

        if mtdata in self.path_checker:
            return False, "Path already existed"
        #validate disk: must be one of the existing disks
        i = self.device_index

        #valid size: must not exceed memory limit
        curr_size = self.disk_size[i][1]
        if len(sizedata) != 0:
            try:
                int(sizedata)
            except ValueError:
                return False, "invalid device size"

            if int(curr_size) - int(sizedata) < 0:
                return False, "invalid device size"
            #if valid, update the size and return true
            new_size = (self.disk_size[i][0], int(curr_size) - int(sizedata))
            self.disk_size[i] = new_size

        if mtdata == "/":
            self.has_slash = True

        self.path_checker.append(mtdata)
        return True, None

    def create_function(self):
        self.window.hide_window()

        self.cp_config['partition_disk'] = self.devices[self.device_index].path
        self.partition_items = []
        self.partition_items.append(
            ('Size in MB: ' + str(self.disk_size[self.device_index][1]) +
             ' available'))
        self.partition_items.append(('Type: (ext3, ext4, swap)'))
        self.partition_items.append(('Mountpoint:'))
        self.create_window = ReadMulText(
            self.maxy,
            self.maxx,
            0,
            self.cp_config,
            str(self.cp_config['partitionsnumber']) + 'partition_info',
            self.partition_items,
            None,
            None,
            None,
            self.validate_partition,  #validation function of the input
            None,
            True,
        )
        result = self.create_window.do_action()
        if result.success:
            self.cp_config[
                'partitionsnumber'] = self.cp_config['partitionsnumber'] + 1

        #parse the input in install config
        return self.display()

    def delete_function(self):
        self.delete()
        return self.display()

    def go_back(self):
        self.delete()
        self.window.hide_window()
        self.partition_pane.hide()
        return ActionResult(False, {'goBack': True})

    def next(self):
        if self.cp_config['partitionsnumber'] == 0:
            window_height = 9
            window_width = 40
            window_starty = (self.maxy - window_height) // 2 + 5
            confirm_window = ConfirmWindow(
                window_height,
                window_width,
                self.maxy,
                self.maxx,
                window_starty,
                'Partition information cannot be empty',
                info=True)
            confirm_window.do_action()
            return self.display()
        #must have /
        if not self.has_slash:
            window_height = 9
            window_width = 40
            window_starty = (self.maxy - window_height) // 2 + 5
            confirm_window = ConfirmWindow(window_height,
                                           window_width,
                                           self.maxy,
                                           self.maxx,
                                           window_starty,
                                           'Missing /',
                                           info=True)
            confirm_window.do_action()
            return self.display()

        self.window.hide_window()
        self.partition_pane.hide()

        partitions = []
        for i in range(int(self.cp_config['partitionsnumber'])):
            if len(self.cp_config[str(i) + 'partition_info' + str(0)]) == 0:
                sizedata = 0
            else:
                sizedata = int(self.cp_config[str(i) + 'partition_info' +
                                              str(0)])
            mtdata = self.cp_config[str(i) + 'partition_info' + str(2)]
            typedata = self.cp_config[str(i) + 'partition_info' + str(1)]

            partitions = partitions + [
                {
                    "mountpoint": mtdata,
                    "size": sizedata,
                    "filesystem": typedata
                },
            ]
        self.install_config['partitions'] = partitions

        return ActionResult(True, {'goNext': True})

    def delete(self):
        for i in range(int(self.cp_config['partitionsnumber'])):
            self.cp_config[str(i) + 'partition_info' + str(0)] = ''
            self.cp_config[str(i) + 'partition_info' + str(1)] = ''
            self.cp_config[str(i) + 'partition_info' + str(2)] = ''
            self.cp_config[str(i) + 'partition_info' + str(3)] = ''
        del self.disk_size[:]
        for index, device in enumerate(self.devices):
            self.disk_size.append((device.path, int(device.size) / 1048576))
        del self.path_checker[:]
        self.has_slash = False
        self.has_remain = False
        self.has_empty = False
        self.cp_config['partitionsnumber'] = 0
Esempio n. 21
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
Esempio n. 22
0
class Installer(object):
    def __init__(self,
                 install_config,
                 maxy=0,
                 maxx=0,
                 iso_installer=False,
                 tools_path="../stage",
                 rpm_path="../stage/RPMS",
                 log_path="../stage/LOGS",
                 ks_config=None):
        self.install_config = install_config
        self.ks_config = ks_config
        self.iso_installer = iso_installer
        self.tools_path = tools_path
        self.rpm_path = rpm_path
        self.log_path = log_path
        self.mount_command = "./mk-mount-disk.sh"
        self.prepare_command = "./mk-prepare-system.sh"
        self.finalize_command = "./mk-finalize-system.sh"
        self.install_package_command = "./mk-install-package.sh"
        self.chroot_command = "./mk-run-chroot.sh"
        self.setup_grub_command = "./mk-setup-grub.sh"
        self.unmount_disk_command = "./mk-unmount-disk.sh"

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

        self.restart_command = "shutdown"

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

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

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

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

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

        sys.exit(1)

    def install(self, params):
        try:
            return self.unsafe_install(params)
        except:
            if self.iso_installer:
                self.exit_gracefully(None, None)
            else:
                raise

    def unsafe_install(self, params):

        if self.iso_installer:
            self.window.show_window()
            self.progress_bar.initialize('Initializing installation...')
            self.progress_bar.show()

        self.execute_modules(modules.commons.PRE_INSTALL)

        self.initialize_system()

        #install packages
        for rpm in self.rpms_tobeinstalled:
            # We already installed the filesystem in the preparation
            if rpm['package'] == 'filesystem':
                continue
            if self.iso_installer:
                self.progress_bar.update_message('Installing {0}...'.format(
                    rpm['package']))
            return_value = self.install_package(rpm['package'])
            if return_value != 0:
                self.exit_gracefully(None, None)
            if self.iso_installer:
                self.progress_bar.increment(rpm['size'] * self.install_factor)

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

        self.finalize_system()

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

            # install grub
            process = subprocess.Popen([
                self.setup_grub_command, '-w', self.photon_root,
                self.install_config['disk']['disk'],
                self.install_config['disk']['root']
            ],
                                       stdout=self.output)
            retval = process.wait()

        process = subprocess.Popen(
            [self.unmount_disk_command, '-w', self.photon_root],
            stdout=self.output)
        retval = process.wait()

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

        return ActionResult(True, None)

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

        progressbar_num_items = 0
        self.rpms_tobeinstalled = []
        selected_packages = self.install_config['packages']
        for package in selected_packages:
            pattern = package + '-[0-9]*.rpm'
            for rpm in rpms:
                if fnmatch.fnmatch(rpm['name'], pattern):
                    rpm['package'] = package
                    self.rpms_tobeinstalled.append(rpm)
                    progressbar_num_items += rpm[
                        'size'] + rpm['size'] * self.install_factor
                    break

        process = subprocess.Popen(['mkdir', '-p', self.photon_root + '/RPMS'],
                                   stdout=self.output)
        retval = process.wait()

        if self.iso_installer:
            self.progress_bar.update_num_items(progressbar_num_items)

        # Copy the rpms
        for rpm in self.rpms_tobeinstalled:
            shutil.copy(rpm['path'], self.photon_root + '/RPMS/')
            if self.iso_installer:
                self.progress_bar.increment(rpm['size'])

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

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

        self.copy_rpms()

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

        self.copy_files()

        #Setup the filesystem basics
        process = subprocess.Popen(
            [self.prepare_command, '-w', self.photon_root, self.tools_path],
            stdout=self.output)
        retval = process.wait()

    def finalize_system(self):
        #Setup the disk
        process = subprocess.Popen([
            self.chroot_command, '-w', self.photon_root, self.finalize_command,
            '-w', self.photon_root
        ],
                                   stdout=self.output)
        retval = process.wait()
        if self.iso_installer:
            # just copy the initramfs /boot -> /photon_mnt/boot
            shutil.copy('/boot/initrd.img-no-kmods',
                        self.photon_root + '/boot/')
        else:
            #Build the initramfs
            process = subprocess.Popen([
                self.chroot_command, '-w', self.photon_root, './mkinitramfs',
                '-n', '/boot/initrd.img-no-kmods'
            ],
                                       stdout=self.output)
            retval = process.wait()

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

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

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

    def execute_modules(self, phase):
        modules = glob.glob('modules/m_*.py')
        for mod_path in modules:
            module = mod_path.replace('/', '.', 1)
            module = os.path.splitext(module)[0]
            try:
                __import__(module)
                mod = sys.modules[module]
            except ImportError:
                print >> sys.stderr, 'Error importing module %s' % module
                continue

            # the module default is disabled
            if not hasattr(mod, 'enabled') or mod.enabled == False:
                print >> sys.stderr, "module %s is not enabled" % module
                continue
            # check for the install phase
            if not hasattr(mod, 'install_phase'):
                print >> sys.stderr, "Error: can not defind module %s phase" % module
                continue
            if mod.install_phase != phase:
                print >> sys.stderr, "Skipping module %s for phase %s" % (
                    module, phase)
                continue
            if not hasattr(mod, 'execute'):
                print >> sys.stderr, "Error: not able to execute module %s" % module
                continue
            mod.execute(module, self.ks_config, self.install_config,
                        self.photon_root)
Esempio n. 23
0
class Installer(object):
    def __init__(self, install_config, maxy = 0, maxx = 0, iso_installer = False, tools_path = "../stage", rpm_path = "../stage/RPMS", log_path = "../stage/LOGS"):
        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()
Esempio n. 24
0
class SelectDisk(object):
    def __init__(self,  maxy, maxx, install_config):
        self.install_config = install_config
        self.menu_items = []

        self.maxx = maxx
        self.maxy = maxy
        self.win_width = 70
        self.win_height = 16

        self.win_starty = (self.maxy - self.win_height) / 2
        self.win_startx = (self.maxx - self.win_width) / 2

        self.menu_starty = self.win_starty + 6
        self.menu_height = 5
        self.progress_padding = 5
        self.progress_width = self.win_width - self.progress_padding
        self.progress_bar = ProgressBar(self.win_starty + 6, self.win_startx + self.progress_padding / 2, self.progress_width)

        self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Setup your disk', True)
        self.devices = Device.refresh_devices()

    def guided_partitions(self, device_index):
        menu_height = 9
        menu_width = 40
        menu_starty = (self.maxy - menu_height) / 2 + 5
        confrim_window = ConfirmWindow(menu_height, menu_width, self.maxy, self.maxx, menu_starty, 'This will erase the disk.\nAre you sure?')
        confirmed = confrim_window.do_action().result['yes']

        if not confirmed:
            return ActionResult(confirmed, None)

        self.progress_bar.initialize('Partitioning...')
        self.progress_bar.show()
        self.progress_bar.show_loading('Partitioning')

        # Do the partitioning
        self.window.clearerror()
        partitions_data = modules.commons.partition_disk(self.devices[device_index].path, modules.commons.default_partitions)
        if partitions_data == None:
            self.window.adderror('Partitioning failed, you may try again')
        else:
            self.install_config['disk'] = partitions_data

        self.progress_bar.hide()
        return ActionResult(partitions_data != None, None)

    def display(self, params):
        self.window.addstr(0, 0, 'First, we will setup your disks.\n\nWe have detected {0} disks, choose disk to be auto-partitioned:'.format(len(self.devices)))

        self.disk_menu_items = []

        # Fill in the menu items
        for index, device in enumerate(self.devices):
            #if index > 0:
                self.disk_menu_items.append(
                        (
                            '{2} - {1} @ {0}'.format(device.path, device.size, device.model), 
                            self.guided_partitions, 
                            index
                        )
                    )

        self.disk_menu = Menu(self.menu_starty,  self.maxx, self.disk_menu_items, self.menu_height)

        self.window.set_action_panel(self.disk_menu)
        return self.window.do_action()
Esempio n. 25
0
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
Esempio n. 26
0
class SelectDisk(object):
    def __init__(self,  maxy, maxx, install_config):
        self.install_config = install_config
        self.menu_items = []

        self.maxx = maxx
        self.maxy = maxy
        self.win_width = 70
        self.win_height = 16

        self.win_starty = (self.maxy - self.win_height) // 2
        self.win_startx = (self.maxx - self.win_width) // 2

        self.menu_starty = self.win_starty + 6
        self.menu_height = 5
        self.progress_padding = 5
        self.progress_width = self.win_width - self.progress_padding
        self.progress_bar = ProgressBar(self.win_starty + 6, self.win_startx + (self.progress_padding // 2), self.progress_width, new_win=True)

        self.disk_buttom_items = []
        self.disk_buttom_items.append(('<Custom>', self.custom_function, False))
        self.disk_buttom_items.append(('<Auto>', self.auto_function, False))

        self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Select a disk', True, items = self.disk_buttom_items, menu_helper = self.save_index, position = 2, tab_enabled=False)
        self.partition_window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Partition', True)
        self.devices = Device.refresh_devices()

    def guided_partitions(self, params):
        if not 'diskindex' in self.install_config:
            return ActionResult(False, None);

        device_index = self.install_config['diskindex']

        menu_height = 9
        menu_width = 40
        menu_starty = (self.maxy - menu_height) // 2 + 5
        self.install_config['delete_partition'] = True
        confrim_window = ConfirmWindow(menu_height, menu_width, self.maxy, self.maxx, menu_starty, 'This will erase the disk.\nAre you sure?')
        confirmed = confrim_window.do_action().result['yes']

        if confirmed == False:
            self.install_config['skipPrevs'] = True
            return ActionResult(False, {'goBack':True})

        self.install_config['skipPrevs'] = False
        self.progress_bar.initialize('Partitioning...')
        self.progress_bar.show()
        self.progress_bar.show_loading('Partitioning')

        # Do the partitioning
        if 'partitionsnumber' in self.install_config:
                if (int(self.install_config['partitionsnumber']) == 0):
                    partitions_data = modules.commons.partition_disk(self.devices[device_index].path, modules.commons.default_partitions)
                else:
                    partitions = []
                    for i in range (int (self.install_config['partitionsnumber'])):
                        if len(self.install_config[str(i)+'partition_info'+str(0)])==0:
                            sizedata=0
                        else:
                            sizedata = int(self.install_config[str(i)+'partition_info'+str(0)])
                        mtdata = self.install_config[str(i)+'partition_info'+str(2)]
                        typedata = self.install_config[str(i)+'partition_info'+str(1)]

                        partitions = partitions + [
                                    {"mountpoint": mtdata, "size": sizedata, "filesystem": typedata},
                                    ]
                    partitions_data = modules.commons.partition_disk(self.devices[device_index].path, partitions)
        else: 
            partitions_data = modules.commons.partition_disk(self.devices[device_index].path, modules.commons.default_partitions)

        if partitions_data == None:
            self.partition_window.adderror('Partitioning failed, you may try again')
        else:
            self.install_config['disk'] = partitions_data

        self.progress_bar.hide()
        return ActionResult(partitions_data != None, None)

    def display(self, params):
        if 'skipPrevs' in self.install_config:
            self.install_config['skipPrevs'] = False
        self.window.addstr(0, 0, 'Please select a disk and a method how to partition it:\nAuto - single partition for /, no swap partition.\nCustom - for customized partitioning')

        self.disk_menu_items = []

        # Fill in the menu items
        for index, device in enumerate(self.devices):
            #if index > 0:
                self.disk_menu_items.append(
                        (
                            '{2} - {1} @ {0}'.format(device.path, device.size, device.model), 
                            self.save_index, 
                            index
                        )
                    )

        self.disk_menu = Menu(self.menu_starty,  self.maxx, self.disk_menu_items, self.menu_height, tab_enable=False)
        self.disk_menu.can_save_sel(True)

        self.window.set_action_panel(self.disk_menu)
        return self.window.do_action()

    def save_index(self, device_index):
        self.install_config['diskindex'] = device_index
        return ActionResult(True, None)

    def auto_function(self, params):    #default is no partition
        self.install_config['autopartition'] = True
        self.install_config['partitionsnumber'] = 0
        return ActionResult(True, None)

    def custom_function(self, params):  #custom minimize partition number is 1
        self.install_config['autopartition'] = False
        return ActionResult(True, None)
Esempio n. 27
0
class SelectDisk(object):
    def __init__(self,  maxy, maxx, install_config):
        self.install_config = install_config
        self.menu_items = []

        self.maxx = maxx
        self.maxy = maxy
        self.win_width = 70
        self.win_height = 16

        self.win_starty = (self.maxy - self.win_height) / 2
        self.win_startx = (self.maxx - self.win_width) / 2

        self.menu_starty = self.win_starty + 6
        self.menu_height = 5

        self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Setup your disk', True)
        self.devices = Device.refresh_devices()

    def guided_partitions(self, device_index):
        menu_height = 9
        menu_width = 40
        menu_starty = (self.maxy - menu_height) / 2 + 5
        confrim_window = ConfirmWindow(menu_height, menu_width, self.maxy, self.maxx, menu_starty, 'This will erase the disk.\nAre you sure?')
        confirmed = confrim_window.do_action().result['yes']
        
        if not confirmed:
            return ActionResult(confirmed, None)
        
        #self.install_config['disk'] = self.devices[device_index].path
        #return ActionResult(True, None)
        
        # Do the partitioning
        self.window.clearerror()
        json_ret = subprocess.check_output(['gpartedbin', 'defaultpartitions', self.devices[device_index].path], stderr=open(os.devnull, 'w'))
        json_dct = json.loads(json_ret)
        if json_dct['success']:
            self.install_config['disk'] = json_dct['data']
        else:
            self.window.adderror('Partitioning failed, you may try again')
        return ActionResult(json_dct['success'], None)

    def display(self, params):
        self.window.addstr(0, 0, 'First, we will setup your disks.\n\nWe have detected {0} disks, choose disk to be auto-partitioned:'.format(len(self.devices)))

        self.disk_menu_items = []

        # Fill in the menu items
        for index, device in enumerate(self.devices):
            #if index > 0:
                self.disk_menu_items.append(
                        (
                            '{2} - {1} MB @ {0}'.format(device.path, device.size, device.model), 
                            self.guided_partitions, 
                            index
                        )
                    )

        self.disk_menu = Menu(self.menu_starty,  self.maxx, self.disk_menu_items, self.menu_height)

        self.window.set_action_panel(self.disk_menu)

        return self.window.do_action()
Esempio n. 28
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 inturrupted.\n\nPress any key to get to the bash...'
            )
            self.window.content_window().getch()

        sys.exit(1)

    def install(self, params):
        try:
            return self.unsafe_install(params)
        except:
            if self.iso_installer:
                self.exit_gracefully(None, None)
            else:
                raise

    def unsafe_install(self, params):

        if self.iso_installer:
            self.window.show_window()
            self.progress_bar.initialize('Initializing installation...')
            self.progress_bar.show()

        self.execute_modules(modules.commons.PRE_INSTALL)

        self.initialize_system()

        #install packages
        for rpm in self.rpms_tobeinstalled:
            # We already installed the filesystem in the preparation
            if rpm['package'] == 'filesystem':
                continue
            if self.iso_installer:
                self.progress_bar.update_message('Installing {0}...'.format(
                    rpm['package']))
            return_value = self.install_package(rpm['filename'])
            if return_value != 0:
                self.exit_gracefully(None, None)
            if self.iso_installer:
                self.progress_bar.increment(rpm['size'] * self.install_factor)

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

        self.finalize_system()

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

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

            retval = process.wait()

        process = subprocess.Popen(
            [self.unmount_disk_command, '-w', self.photon_root],
            stdout=self.output)
        retval = process.wait()

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

        return ActionResult(True, None)

    def download_file(self, url, directory):
        # TODO: Add errors handling
        urlopener = urllib.URLopener()
        urlopener.retrieve(url, os.path.join(directory, os.path.basename(url)))

    def download_rpms(self):
        repodata_dir = os.path.join(self.photon_root, 'RPMS/repodata')
        process = subprocess.Popen(['mkdir', '-p', repodata_dir],
                                   stdout=self.output)
        retval = process.wait()

        import hawkey
        self.install_factor = 1
        # Load the repo data
        sack = hawkey.Sack()

        repomd_filename = "repomd.xml"
        repomd_url = os.path.join(self.rpm_path, "repodata/repomd.xml")

        self.download_file(repomd_url, repodata_dir)

        # parse to the xml to get the primary and files list
        tree = ET.parse(os.path.join(repodata_dir, repomd_filename))
        # TODO: Get the namespace dynamically from the xml file
        ns = {'ns': 'http://linux.duke.edu/metadata/repo'}

        primary_location = tree.find("./ns:data[@type='primary']/ns:location",
                                     ns).get("href")
        filelists_location = tree.find(
            "./ns:data[@type='filelists']/ns:location", ns).get("href")
        primary_filename = os.path.basename(primary_location)
        filelists_filename = os.path.basename(filelists_location)

        self.download_file(os.path.join(self.rpm_path, primary_location),
                           repodata_dir)
        self.download_file(os.path.join(self.rpm_path, filelists_location),
                           repodata_dir)

        repo = hawkey.Repo("installrepo")
        repo.repomd_fn = os.path.join(repodata_dir, repomd_filename)
        repo.primary_fn = os.path.join(repodata_dir, primary_filename)
        repo.filelists_fn = os.path.join(repodata_dir, filelists_filename)

        sack.load_yum_repo(repo, load_filelists=True)

        progressbar_num_items = 0
        self.rpms_tobeinstalled = []
        selected_packages = self.install_config['packages']
        for package in selected_packages:
            # Locate the package
            q = hawkey.Query(sack).filter(name=package)
            if (len(q) > 0):
                progressbar_num_items += q[
                    0].size + q[0].size * self.install_factor
                self.rpms_tobeinstalled.append({
                    'package':
                    package,
                    'size':
                    q[0].size,
                    'location':
                    q[0].location,
                    'filename':
                    os.path.basename(q[0].location)
                })
            else:
                print >> sys.stderr, "Package %s not found in the repo" % package
                #self.exit_gracefully(None, None)

        self.progress_bar.update_num_items(progressbar_num_items)

        # Download the rpms
        for rpm in self.rpms_tobeinstalled:
            message = 'Downloading {0}...'.format(rpm['filename'])
            self.progress_bar.update_message(message)
            self.download_file(os.path.join(self.rpm_path, rpm['location']),
                               os.path.join(self.photon_root, "RPMS"))
            self.progress_bar.increment(rpm['size'])

        # update the rpms path
        self.rpm_path = os.path.join(self.photon_root, "RPMS")

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

        progressbar_num_items = 0
        self.rpms_tobeinstalled = []
        selected_packages = self.install_config['packages']
        for package in selected_packages:
            pattern = package + '-[0-9]*.rpm'
            for rpm in rpms:
                if fnmatch.fnmatch(rpm['filename'], pattern):
                    rpm['package'] = package
                    self.rpms_tobeinstalled.append(rpm)
                    progressbar_num_items += rpm[
                        'size'] + rpm['size'] * self.install_factor
                    break

        if self.iso_installer:
            self.progress_bar.update_num_items(progressbar_num_items)

        # Copy the rpms
        for rpm in self.rpms_tobeinstalled:
            if self.iso_installer:
                message = 'Copying {0}...'.format(rpm['filename'])
                self.progress_bar.update_message(message)
            shutil.copy(rpm['path'], self.photon_root + '/RPMS/')
            if self.iso_installer:
                self.progress_bar.increment(rpm['size'])

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

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

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

        if self.rpm_path.startswith("http://"):
            self.download_rpms()
        else:
            self.copy_rpms()

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

        self.copy_files()

        #Setup the filesystem basics
        process = subprocess.Popen(
            [self.prepare_command, '-w', self.photon_root], stdout=self.output)
        retval = process.wait()

    def finalize_system(self):
        #Setup the disk
        process = subprocess.Popen([
            self.chroot_command, '-w', self.photon_root, self.finalize_command,
            '-w', self.photon_root
        ],
                                   stdout=self.output)
        retval = process.wait()
        initrd_dir = 'boot'
        initrd_file_name = 'initrd.img-no-kmods'
        if self.iso_installer:
            # just copy the initramfs /boot -> /photon_mnt/boot
            shutil.copy(os.path.join(initrd_dir, initrd_file_name),
                        self.photon_root + '/boot/')
            # remove the installer directory
            process = subprocess.Popen(
                ['rm', '-rf',
                 os.path.join(self.photon_root, "installer")],
                stdout=self.output)
            retval = process.wait()
        else:
            #Build the initramfs by passing in the kernel version
            version_string = ''
            for root, dirs, files in os.walk(self.rpm_path):
                for name in files:
                    if fnmatch.fnmatch(name, 'linux-[0-9]*.rpm'):
                        version_array = name.split('-')
                        if len(version_array) > 2:
                            version_string = version_array[1]

            if 'initrd_dir' in self.install_config:
                initrd_dir = self.install_config['initrd_dir']
            process = subprocess.Popen([
                self.chroot_command, '-w', self.photon_root, './mkinitramfs',
                '-n',
                os.path.join(initrd_dir,
                             initrd_file_name), '-k', version_string
            ],
                                       stdout=self.output)
            retval = process.wait()

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

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

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

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

        return process.wait()

    def execute_modules(self, phase):
        modules = glob.glob('modules/m_*.py')
        for mod_path in modules:
            module = mod_path.replace('/', '.', 1)
            module = os.path.splitext(module)[0]
            try:
                __import__(module)
                mod = sys.modules[module]
            except ImportError:
                print >> sys.stderr, 'Error importing module %s' % module
                continue

            # the module default is disabled
            if not hasattr(mod, 'enabled') or mod.enabled == False:
                print >> sys.stderr, "module %s is not enabled" % module
                continue
            # check for the install phase
            if not hasattr(mod, 'install_phase'):
                print >> sys.stderr, "Error: can not defind module %s phase" % module
                continue
            if mod.install_phase != phase:
                print >> sys.stderr, "Skipping module %s for phase %s" % (
                    module, phase)
                continue
            if not hasattr(mod, 'execute'):
                print >> sys.stderr, "Error: not able to execute module %s" % module
                continue
            mod.execute(module, self.ks_config, self.install_config,
                        self.photon_root)

    def run(self, command, comment=None):
        if comment != None:
            print >> sys.stderr, "Installer: {} ".format(comment)
            self.progress_bar.update_loading_message(comment)

        print >> sys.stderr, "Installer: {} ".format(command)
        process = subprocess.Popen([command], shell=True, stdout=self.output)
        retval = process.wait()
        return retval
Esempio n. 29
0
class SelectDisk(object):
    def __init__(self,  maxy, maxx, install_config):
        self.install_config = install_config
        self.menu_items = []

        self.maxx = maxx
        self.maxy = maxy
        self.win_width = 70
        self.win_height = 16

        self.win_starty = (self.maxy - self.win_height) / 2
        self.win_startx = (self.maxx - self.win_width) / 2

        self.menu_starty = self.win_starty + 6
        self.menu_height = 5
        self.progress_padding = 5
        self.progress_width = self.win_width - self.progress_padding
        self.progress_bar = ProgressBar(self.win_starty + 6, self.win_startx + self.progress_padding / 2, self.progress_width, new_win=True)

        self.disk_buttom_items = []
        self.disk_buttom_items.append(('<Custom>', self.custom_function, False))
        self.disk_buttom_items.append(('<Auto>', self.auto_function, False))

        self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Select a disk', True, items = self.disk_buttom_items, menu_helper = self.save_index, position = 2, tab_enabled=False)
        self.partition_window = Window(self.win_height, self.win_width, self.maxy, self.maxx, 'Partition', True)
        self.devices = Device.refresh_devices()

    def guided_partitions(self, params):
        if not 'diskindex' in self.install_config:
            return ActionResult(False, None);

        device_index = self.install_config['diskindex']

        menu_height = 9
        menu_width = 40
        menu_starty = (self.maxy - menu_height) / 2 + 5
        self.install_config['delete_partition'] = True
        confrim_window = ConfirmWindow(menu_height, menu_width, self.maxy, self.maxx, menu_starty, 'This will erase the disk.\nAre you sure?')
        confirmed = confrim_window.do_action().result['yes']

        if confirmed == False:
            self.install_config['skipPrevs'] = True
            return ActionResult(False, {'goBack':True})

        self.install_config['skipPrevs'] = False
        self.progress_bar.initialize('Partitioning...')
        self.progress_bar.show()
        self.progress_bar.show_loading('Partitioning')

        # Do the partitioning
        if 'partitionsnumber' in self.install_config:
                if (int(self.install_config['partitionsnumber']) == 0):
                    partitions_data = modules.commons.partition_disk(self.devices[device_index].path, modules.commons.default_partitions)
                else:
                    partitions = []
                    for i in range (int (self.install_config['partitionsnumber'])):
                        if len(self.install_config[str(i)+'partition_info'+str(0)])==0:
                            sizedata=0
                        else:
                            sizedata = int(self.install_config[str(i)+'partition_info'+str(0)])
                        mtdata = self.install_config[str(i)+'partition_info'+str(2)]
                        typedata = self.install_config[str(i)+'partition_info'+str(1)]

                        partitions = partitions + [
                                    {"mountpoint": mtdata, "size": sizedata, "filesystem": typedata},
                                    ]
                    partitions_data = modules.commons.partition_disk(self.devices[device_index].path, partitions)
        else: 
            partitions_data = modules.commons.partition_disk(self.devices[device_index].path, modules.commons.default_partitions)

        if partitions_data == None:
            self.partition_window.adderror('Partitioning failed, you may try again')
        else:
            self.install_config['disk'] = partitions_data

        self.progress_bar.hide()
        return ActionResult(partitions_data != None, None)

    def display(self, params):
        if 'skipPrevs' in self.install_config:
            self.install_config['skipPrevs'] = False
        self.window.addstr(0, 0, 'Please select a disk and a method how to partition it:\nAuto - single partition for /, no swap partition.\nCustom - for customized partitioning')

        self.disk_menu_items = []

        # Fill in the menu items
        for index, device in enumerate(self.devices):
            #if index > 0:
                self.disk_menu_items.append(
                        (
                            '{2} - {1} @ {0}'.format(device.path, device.size, device.model), 
                            self.save_index, 
                            index
                        )
                    )

        self.disk_menu = Menu(self.menu_starty,  self.maxx, self.disk_menu_items, self.menu_height, tab_enable=False)
        self.disk_menu.can_save_sel(True)

        self.window.set_action_panel(self.disk_menu)
        return self.window.do_action()

    def save_index(self, device_index):
        self.install_config['diskindex'] = device_index
        return ActionResult(True, None)

    def auto_function(self, params):    #default is no partition
        self.install_config['autopartition'] = True
        self.install_config['partitionsnumber'] = 0
        return ActionResult(True, None)

    def custom_function(self, params):  #custom minimize partition number is 1
        self.install_config['autopartition'] = False
        return ActionResult(True, None)
Esempio n. 30
0
class SelectDisk(object):
    def __init__(self, maxy, maxx, install_config):
        self.install_config = install_config
        self.menu_items = []

        self.maxx = maxx
        self.maxy = maxy
        self.win_width = 70
        self.win_height = 16

        self.win_starty = (self.maxy - self.win_height) / 2
        self.win_startx = (self.maxx - self.win_width) / 2

        self.menu_starty = self.win_starty + 6
        self.menu_height = 5
        self.progress_padding = 5
        self.progress_width = self.win_width - self.progress_padding
        self.progress_bar = ProgressBar(
            self.win_starty + 6, self.win_startx + self.progress_padding / 2,
            self.progress_width)

        self.window = Window(self.win_height, self.win_width, self.maxy,
                             self.maxx, 'Setup your disk', True)
        self.devices = Device.refresh_devices()

    def guided_partitions(self, device_index):
        menu_height = 9
        menu_width = 40
        menu_starty = (self.maxy - menu_height) / 2 + 5
        confrim_window = ConfirmWindow(
            menu_height, menu_width, self.maxy, self.maxx, menu_starty,
            'This will erase the disk.\nAre you sure?')
        confirmed = confrim_window.do_action().result['yes']

        if not confirmed:
            return ActionResult(confirmed, None)

        self.progress_bar.initialize('Partitioning...')
        self.progress_bar.show()
        self.progress_bar.show_loading('Partitioning')

        # Do the partitioning
        self.window.clearerror()
        partitions_data = modules.commons.partition_disk(
            self.devices[device_index].path)
        if partitions_data == None:
            self.window.adderror('Partitioning failed, you may try again')
        else:
            self.install_config['disk'] = partitions_data

        self.progress_bar.hide()
        return ActionResult(partitions_data != None, None)

    def display(self, params):
        self.window.addstr(
            0, 0,
            'First, we will setup your disks.\n\nWe have detected {0} disks, choose disk to be auto-partitioned:'
            .format(len(self.devices)))

        self.disk_menu_items = []

        # Fill in the menu items
        for index, device in enumerate(self.devices):
            #if index > 0:
            self.disk_menu_items.append(
                ('{2} - {1} @ {0}'.format(device.path, device.size,
                                          device.model),
                 self.guided_partitions, index))

        self.disk_menu = Menu(self.menu_starty, self.maxx,
                              self.disk_menu_items, self.menu_height)

        self.window.set_action_panel(self.disk_menu)
        return self.window.do_action()
Esempio n. 31
0
class PartitionISO(object):
    def __init__(self, maxy, maxx, install_config):
        self.maxx = maxx
        self.maxy = maxy
        self.win_width = maxx - 4
        self.win_height = maxy - 4
        self.install_config = install_config
        self.path_checker = []

        self.win_starty = (self.maxy - self.win_height) // 2
        self.win_startx = (self.maxx - self.win_width) // 2

        self.text_starty = self.win_starty + 4
        self.text_height = self.win_height - 6
        self.text_width = self.win_width - 6
        self.install_config['partitionsnumber'] = 0
        self.devices = Device.refresh_devices_bytes()
        self.has_slash = False
        self.has_remain = False
        self.has_empty = False

        self.disk_size = []
        for index, device in enumerate(self.devices):
            self.disk_size.append((device.path, int(device.size) / 1048576))

        self.window = Window(self.win_height, self.win_width, self.maxy, self.maxx,
                             'Welcome to the Photon installer', False, can_go_next=False)
        Device.refresh_devices()

    def display(self, params):
        if 'skipPrevs' in self.install_config and self.install_config['skipPrevs'] == True:
            self.delete()
            return ActionResult(False, {'goBack':True})
        if 'autopartition' in self.install_config and self.install_config['autopartition'] == True:
            return ActionResult(True, None)
        if ('delete_partition' in self.install_config and
                self.install_config['delete_partition'] == True):
            self.delete()
            self.install_config['delete_partition'] = False

        self.device_index = self.install_config['diskindex']

        self.disk_buttom_items = []
        self.disk_buttom_items.append(('<Next>', self.next))
        self.disk_buttom_items.append(('<Create New>', self.create_function))
        self.disk_buttom_items.append(('<Delete All>', self.delete_function))
        self.disk_buttom_items.append(('<Go Back>', self.go_back))

        self.text_items = []
        self.text_items.append(('Disk', 20))
        self.text_items.append(('Size', 5))
        self.text_items.append(('Type', 5))
        self.text_items.append(('Mountpoint', 20))
        self.table_space = 5

        title = 'Current partitions:\n'
        self.window.addstr(0, (self.win_width - len(title)) // 2, title)

        info = ("Unpartitioned space: " +
                str(self.disk_size[self.device_index][1])+
                " MB, Total size: "+
                str(int(self.devices[self.device_index].size)/ 1048576) + " MB")

        self.text_pane = TextPane(self.text_starty, self.maxx, self.text_width,
                                  "EULA.txt", self.text_height, self.disk_buttom_items,
                                  partition=True, popupWindow=True,
                                  install_config=self.install_config,
                                  text_items=self.text_items, table_space=self.table_space,
                                  default_start=1, info=info,
                                  size_left=str(self.disk_size[self.device_index][1]))

        self.window.set_action_panel(self.text_pane)

        return self.window.do_action()

    def validate_partition(self, pstr):
        if not pstr:
            return ActionResult(False, None)
        sizedata = pstr[0]
        mtdata = pstr[2]
        typedata = pstr[1]
        devicedata = self.devices[self.device_index].path

        #no empty fields unless swap
        if (typedata == 'swap' and
                (len(mtdata) != 0 or len(typedata) == 0 or len(devicedata) == 0)):
            return False, "invalid swap data "

        if (typedata != 'swap' and
                (len(sizedata) == 0 or
                 len(mtdata) == 0 or
                 len(typedata) == 0 or
                 len(devicedata) == 0)):
            if not self.has_empty and mtdata and typedata and devicedata:
                self.has_empty = True
            else:
                return False, "Input cannot be empty"

        if typedata != 'swap' and typedata != 'ext3' and typedata != 'ext4':
            return False, "Invalid type"

        if len(mtdata) != 0 and mtdata[0] != '/':
            return False, "Invalid path"

        if mtdata in self.path_checker:
            return False, "Path already existed"
        #validate disk: must be one of the existing disks
        i = self.device_index

        #valid size: must not exceed memory limit
        curr_size = self.disk_size[i][1]
        if len(sizedata) != 0:
            try:
                int(sizedata)
            except ValueError:
                return False, "invalid device size"

            if int(curr_size) - int(sizedata) < 0:
                return False, "invalid device size"
            #if valid, update the size and return true
            new_size = (self.disk_size[i][0], int(curr_size)- int(sizedata))
            self.disk_size[i] = new_size

        if mtdata == "/":
            self.has_slash = True

        self.path_checker.append(mtdata)
        return True, None

    def create_function(self):
        self.window.hide_window()

        self.install_config['partition_disk'] = self.devices[self.device_index].path
        self.partition_items = []
        self.partition_items.append(('Size in MB: ' +
                                     str(self.disk_size[self.device_index][1]) +
                                     ' available'))
        self.partition_items.append(('Type: (ext3, ext4, swap)'))
        self.partition_items.append(('Mountpoint:'))
        self.create_window = ReadMulText(
            self.maxy, self.maxx, 0,
            self.install_config,
            str(self.install_config['partitionsnumber']) + 'partition_info',
            self.partition_items,
            None,
            None,
            None,
            self.validate_partition,   #validation function of the input
            None,
            True,
            )
        result = self.create_window.do_action()
        if result.success:
            self.install_config['partitionsnumber'] = self.install_config['partitionsnumber'] + 1

        #parse the input in install config
        return self.display(False)

    def delete_function(self):
        self.delete()
        return self.display(False)

    def go_back(self):
        self.delete()
        self.window.hide_window()
        self.text_pane.hide()
        return ActionResult(False, {'goBack':True})

    def next(self):
        if self.install_config['partitionsnumber'] == 0:
            window_height = 9
            window_width = 40
            window_starty = (self.maxy-window_height) // 2 + 5
            confirm_window = ConfirmWindow(window_height, window_width, self.maxy,
                                           self.maxx, window_starty,
                                           'Partition information cannot be empty',
                                           info=True)
            confirm_window.do_action()
            return self.display(False)
        #must have /
        if not self.has_slash:
            window_height = 9
            window_width = 40
            window_starty = (self.maxy - window_height) // 2 + 5
            confirm_window = ConfirmWindow(window_height, window_width, self.maxy,
                                           self.maxx, window_starty, 'Missing /',
                                           info=True)
            confirm_window.do_action()
            return self.display(False)

        self.window.hide_window()
        self.text_pane.hide()
        return ActionResult(True, {'goNext':True})

    def delete(self):
        for i in range(int(self.install_config['partitionsnumber'])):
            self.install_config[str(i)+'partition_info'+str(0)] = ''
            self.install_config[str(i)+'partition_info'+str(1)] = ''
            self.install_config[str(i)+'partition_info'+str(2)] = ''
            self.install_config[str(i)+'partition_info'+str(3)] = ''
        del self.disk_size[:]
        for index, device in enumerate(self.devices):
            self.disk_size.append((device.path, int(device.size) / 1048576))
        del self.path_checker[:]
        self.has_slash = False
        self.has_remain = False
        self.has_empty = False
        self.install_config['partitionsnumber'] = 0
Esempio n. 32
0
class Installer(object):
    def __init__(self, install_config, maxy = 0, maxx = 0, iso_installer = False, tools_path = "../stage", rpm_path = "../stage/RPMS", log_path = "../stage/LOGS", ks_config = None):
        self.install_config = install_config
        self.ks_config = ks_config
        self.iso_installer = iso_installer
        self.tools_path = tools_path
        self.rpm_path = rpm_path
        self.log_path = log_path
        self.mount_command = "./mk-mount-disk.sh"
        self.prepare_command = "./mk-prepare-system.sh"
        self.finalize_command = "./mk-finalize-system.sh"
        self.install_package_command = "./mk-install-package.sh"
        self.chroot_command = "./mk-run-chroot.sh"
        self.setup_grub_command = "./mk-setup-grub.sh"
        self.unmount_disk_command = "./mk-unmount-disk.sh"

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

        self.restart_command = "shutdown"

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

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

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

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

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

    def install(self, params):
        try:
            return self.unsafe_install(params)
        except:
            if self.iso_installer:
                self.exit_gracefully(None, None)
            else:
                raise

    def unsafe_install(self, params):

        if self.iso_installer:
            self.window.show_window()
            self.progress_bar.initialize('Initializing installation...')
            self.progress_bar.show()

        self.execute_modules(modules.commons.PRE_INSTALL)

        self.initialize_system()

        #install packages
        for rpm in self.rpms_tobeinstalled:
            # We already installed the filesystem in the preparation
            if rpm['package'] == 'filesystem':
                continue
            if self.iso_installer:
                self.progress_bar.update_message('Installing {0}...'.format(rpm['package']))
            return_value = self.install_package(rpm['package'])
            if return_value != 0:
                self.exit_gracefully(None, None)
            if self.iso_installer:
                self.progress_bar.increment(rpm['size'] * self.install_factor)

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

        self.finalize_system()

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

            # install grub
            process = subprocess.Popen([self.setup_grub_command, '-w', self.photon_root, self.install_config['disk']['disk'], self.install_config['disk']['root']], stdout=self.output)
            retval = process.wait()

        process = subprocess.Popen([self.unmount_disk_command, '-w', self.photon_root], stdout=self.output)
        retval = process.wait()

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

        return ActionResult(True, None)

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

        progressbar_num_items = 0
        self.rpms_tobeinstalled = []
        selected_packages = self.install_config['packages']
        for package in selected_packages:
            pattern = package + '-[0-9]*.rpm'
            for rpm in rpms:
                if fnmatch.fnmatch(rpm['name'], pattern):
                    rpm['package'] = package
                    self.rpms_tobeinstalled.append(rpm)
                    progressbar_num_items += rpm['size'] + rpm['size'] * self.install_factor
                    break

        process = subprocess.Popen(['mkdir', '-p', self.photon_root + '/RPMS'], stdout=self.output)
        retval = process.wait()

        if self.iso_installer:
            self.progress_bar.update_num_items(progressbar_num_items)

        # Copy the rpms
        for rpm in self.rpms_tobeinstalled:
            shutil.copy(rpm['path'], self.photon_root + '/RPMS/')
            if self.iso_installer:
                self.progress_bar.increment(rpm['size'])

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

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

        self.copy_rpms()

    def initialize_system(self):
        #Setup the disk
        if (not self.install_config['iso_system']):
            process = subprocess.Popen([self.mount_command, '-w', self.photon_root, self.install_config['disk']['root']], stdout=self.output)
            retval = process.wait()
        
        self.copy_files()
        
        #Setup the filesystem basics
        process = subprocess.Popen([self.prepare_command, '-w', self.photon_root, self.tools_path], stdout=self.output)
        retval = process.wait()

    def finalize_system(self):
        #Setup the disk
        process = subprocess.Popen([self.chroot_command, '-w', self.photon_root, self.finalize_command, '-w', self.photon_root], stdout=self.output)
        retval = process.wait()
        if self.iso_installer:
            # just copy the initramfs /boot -> /photon_mnt/boot
            shutil.copy('/boot/initrd.img-no-kmods', self.photon_root + '/boot/')
        else:
            #Build the initramfs
            process = subprocess.Popen([self.chroot_command, '-w', self.photon_root, './mkinitramfs', '-n', '/boot/initrd.img-no-kmods'],  stdout=self.output)
            retval = process.wait()


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

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

    def execute_modules(self, phase):
        modules = glob.glob('modules/m_*.py')
        for mod_path in modules:
            module = mod_path.replace('/', '.', 1)
            module = os.path.splitext(module)[0]
            try:
                __import__(module)
                mod = sys.modules[module]
            except ImportError:
                print >> sys.stderr,  'Error importing module %s' % module
                continue
            
            # the module default is disabled
            if not hasattr(mod, 'enabled') or mod.enabled == False:
                print >> sys.stderr,  "module %s is not enabled" % module
                continue
            # check for the install phase
            if not hasattr(mod, 'install_phase'):
                print >> sys.stderr,  "Error: can not defind module %s phase" % module
                continue
            if mod.install_phase != phase:
                print >> sys.stderr,  "Skipping module %s for phase %s" % (module, phase)
                continue
            if not hasattr(mod, 'execute'):
                print >> sys.stderr,  "Error: not able to execute module %s" % module
                continue
            mod.execute(module, self.ks_config, self.install_config, self.photon_root)
Esempio n. 33
0
class SelectDisk(object):
    def __init__(self, maxy, maxx, install_config):
        self.install_config = install_config
        self.menu_items = []

        self.maxx = maxx
        self.maxy = maxy
        self.win_width = 70
        self.win_height = 16

        self.win_starty = (self.maxy - self.win_height) // 2
        self.win_startx = (self.maxx - self.win_width) // 2

        self.menu_starty = self.win_starty + 6
        self.menu_height = 5

        self.disk_buttom_items = []
        self.disk_buttom_items.append(
            ('<Custom>', self.custom_function, False))
        self.disk_buttom_items.append(('<Auto>', self.auto_function, False))

        self.window = Window(self.win_height,
                             self.win_width,
                             self.maxy,
                             self.maxx,
                             'Select a disk',
                             True,
                             items=self.disk_buttom_items,
                             menu_helper=self.save_index,
                             position=2,
                             tab_enabled=False)
        self.devices = Device.refresh_devices()

    def display(self):
        self.window.addstr(
            0, 0, 'Please select a disk and a method how to partition it:\n' +
            'Auto - single partition for /, no swap partition.\n' +
            'Custom - for customized partitioning')

        self.disk_menu_items = []

        # Fill in the menu items
        for index, device in enumerate(self.devices):
            #if index > 0:
            self.disk_menu_items.append(
                ('{2} - {1} @ {0}'.format(device.path, device.size,
                                          device.model), self.save_index,
                 index))

        self.disk_menu = Menu(self.menu_starty,
                              self.maxx,
                              self.disk_menu_items,
                              self.menu_height,
                              tab_enable=False)
        self.disk_menu.can_save_sel(True)

        self.window.set_action_panel(self.disk_menu)
        return self.window.do_action()

    def save_index(self, device_index):
        self.install_config['disk'] = self.devices[device_index].path
        return ActionResult(True, None)

    def auto_function(self):  #default is no partition
        self.install_config['autopartition'] = True
        return ActionResult(True, None)

    def custom_function(self):  #custom minimize partition number is 1
        self.install_config['autopartition'] = False
        return ActionResult(True, None)
Esempio n. 34
0
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')
Esempio n. 35
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