Пример #1
0
    def print_data(self):
        '''Print static (non-editable) data.

        Slices - fill the left side, then remaining slices on the right side.
        If for some reason not all slices fit, indicate how many more slices
        there area

        Partitions - Put standard partitions on the left, logical partitions
        on the right

        '''

        part_index = 0
        data = self.ui_obj.get_parts_in_use()

        if len(data) == 0:
            return   # should never be this case

        if self.has_partition_data:
            max_parts = MAX_PRIMARY_PARTS
        else:
            max_parts = min(len(data), self.left_win.area.lines)

        win = self.left_win
        y_loc = 0
        for next_data in data:
            LOGGER.debug("next_data: %s", next_data)
            if y_loc >= max_parts:
                if win is self.left_win:
                    win = self.right_win
                    y_loc = 0
                    max_parts = win.area.lines
                else:
                    num_extra = len(data) - part_index
                    if self.has_partition_data:
                        more_parts_txt = _("%d more partitions") % num_extra
                    else:
                        more_parts_txt = _("%d more slices") % num_extra
                    win.add_text(more_parts_txt, win.area.lines, 3)
                    break
            x_loc = DiskWindow.SCROLL_PAD
            field = 0
            win.add_text(next_data.get_description(), y_loc, x_loc,
                         self.headers[field][0] - 1)
            x_loc += self.headers[field][0]
            field += 1
            if not self.has_partition_data:
                win.add_text(str(next_data.name), y_loc, x_loc,
                             self.headers[field][0] - 1)
                x_loc += self.headers[field][0]
                field += 1
            win.add_text(locale.format("%*.1f", (self.headers[field][0] - 1,
                next_data.size.get(Size.gb_units))), y_loc, x_loc,
                self.headers[field][0] - 1)
            x_loc += self.headers[field][0]
            y_loc += 1
            field += 1
            part_index += 1
        self.right_win.use_vert_scroll_bar = False
        self.no_ut_refresh()
Пример #2
0
 def set_actions(self):
     '''Edit Screens add 'Reset' and 'Change Type' actions. Since these
     do not manipulate screen direction, they are captured during
     processing by adding them to center_win's key_dict.
     '''
     super(PartEditScreen, self).set_actions()
     reset_action = Action(curses.KEY_F7, _("Reset"))
     change_action = Action(curses.KEY_F5, _("Change Type"))
     self.main_win.actions[reset_action.key] = reset_action
     self.main_win.actions[change_action.key] = change_action
     self.center_win.key_dict[curses.KEY_F7] = self.on_key_F7
 def set_actions(self):
     '''Edit Screens add 'Reset' and 'Change Type' actions. Since these
     do not manipulate screen direction, they are captured during
     processing by adding them to center_win's key_dict.
     
     '''
     super(PartEditScreen, self).set_actions()
     reset_action = Action(curses.KEY_F7, _("Reset"))
     change_action = Action(curses.KEY_F5, _("Change Type"))
     self.main_win.actions[reset_action.key] = reset_action
     self.main_win.actions[change_action.key] = change_action
     self.center_win.key_dict[curses.KEY_F7] = self.on_key_F7
Пример #4
0
 def build_summary(self):
     '''Build a textual summary from the DOC data'''
     lines = []
     
     lines.append(_("Software: %s") % self.get_release())
     lines.append("")
     lines.append(self.get_disk_summary())
     lines.append("")
     lines.append(self.get_tz_summary())
     lines.append("")
     lines.append(_("Language: *The following can be changed when "
                    "logging in."))
     if self.sysconfig.system.locale is None:
         self.sysconfig.system.determine_locale()
     lines.append(_("  Default language: %s") %
                  self.sysconfig.system.actual_lang)
     lines.append("")
     lines.append(_("Keyboard layout: *The following can be "
                    "changed when logging in."))
     lines.append(_("  Default keyboard layout: %s") %
                  self.sysconfig.system.keyboard)
     lines.append("")
     lines.append(_("Terminal type: %s") %
                  self.sysconfig.system.terminal_type)
     lines.append("")
     lines.append(_("Users:"))
     lines.extend(self.get_users())
     lines.append("")
     lines.append(_("Network:"))
     lines.extend(self.get_networks())
     self._get_nameservice(lines)
     
     return "\n".join(lines)
Пример #5
0
    def get_users(self):
        '''Build a summary of the user information, and return it as a list
        of strings

        '''
        root = self.sysconfig.users.root
        primary = self.sysconfig.users.user
        user_summary = []
        if not root.password:
            user_summary.append(_("  Warning: No root password set"))
        if primary.login_name:
            user_summary.append(_("  Username: %s") % primary.login_name)
        else:
            user_summary.append(_("  No user account"))
        return user_summary
Пример #6
0
    def set_actions(self):
        '''Remove all actions except Quit, and add actions for rebooting
        and viewing the log.

        '''
        self.main_win.actions.pop(curses.KEY_F2)  # Remove F2_Continue
        self.main_win.actions.pop(curses.KEY_F3)  # Remove F3_Back
        self.main_win.actions.pop(curses.KEY_F6)  # Remove F6_Help

        if self.install_data.install_succeeded:
            reboot_action = Action(curses.KEY_F8, _("Reboot"), reboot_system)
            self.main_win.actions[reboot_action.key] = reboot_action

        log_action = Action(curses.KEY_F4, _("View Log"),
                            self.main_win.screen_list.get_next)
        self.main_win.actions[log_action.key] = log_action
 def set_actions(self):
     '''Remove all actions except Quit, and add actions for rebooting
     and viewing the log.
     
     '''
     self.main_win.actions.pop(curses.KEY_F2) # Remove F2_Continue
     self.main_win.actions.pop(curses.KEY_F3) # Remove F3_Back
     self.main_win.actions.pop(curses.KEY_F6) # Remove F6_Help
     
     if self.install_data.install_succeeded:
         reboot_action = Action(curses.KEY_F8, _("Reboot"), reboot_system)
         self.main_win.actions[reboot_action.key] = reboot_action
     
     log_action = Action(curses.KEY_F4, _("View Log"),
                         self.main_win.screen_list.get_next)
     self.main_win.actions[log_action.key] = log_action
Пример #8
0
    def get_disk_summary(self):
        '''Return a string summary of the disk selection'''

        doc = InstallEngine.get_instance().doc
        disk = get_desired_target_disk(doc)

        disk_string = list()

        disk_size_str = locale.format("%.1f",
            disk.disk_prop.dev_size.get(Size.gb_units)) + LOCALIZED_GB

        locale_disk_str = _("Disk: ") + disk_size_str + " " + \
            str(disk.disk_prop.dev_type)
        disk_string.append(locale_disk_str)

        if not disk.whole_disk:

            if disk.label != "VTOC":
                part_data = get_solaris_gpt_partition(doc)
                if part_data is not None:
                    type_str = str(part_data.part_type)
            else:
                part_data = get_solaris_partition(doc)
                if part_data is not None:
                    type_str = str(libdiskmgt_const.PARTITION_ID_MAP[
                                   part_data.part_type])
            if part_data is not None:
                part_size_str = locale.format("%.1f",
                    part_data.size.get(Size.gb_units)) + LOCALIZED_GB

                locale_part_str = _("Partition: ") + part_size_str + " " +\
                    type_str
                disk_string.append(locale_part_str)

            if part_data is None or not part_data.in_zpool:
                slice_data = get_solaris_slice(doc)

                slice_num_str = _("Slice %s: ") % slice_data.name

                slice_size_str = locale.format("%.1f",
                    slice_data.size.get(Size.gb_units)) + LOCALIZED_GB

                locale_slice_str = slice_num_str + slice_size_str + " " +\
                    str(slice_data.in_zpool)
                disk_string.append(locale_slice_str)

        return "\n".join(disk_string)
Пример #9
0
 def get_users(self):
     '''Build a summary of the user information, and return it as a list
     of strings
     
     '''
     user_summary = []
     user_summary.append(_("  root password is blank. set it at next login."))
     return user_summary
Пример #10
0
    def _show(self):
        '''Display the correct text based on whether the installation
        succeeded or failed.

        '''

        self.log_locations["log_tmp"] = self.install_data.log_location
        self.log_locations["log_final"] = self.install_data.log_final
        if self.install_data.install_succeeded:
            self.header_text = InstallStatus.SUCCESS_HEADER
            paragraph_text = InstallStatus.SUCCESS_TEXT

            # inform the user how to set the CHAP password and username on
            # SPARC, if needed
            if platform.processor() == "sparc":
                doc = InstallEngine.get_instance().doc
                disk = get_desired_target_disk(doc)
                iscsi = doc.volatile.get_first_child(name=ISCSI_LABEL,
                                                     class_type=Iscsi)
                if iscsi and iscsi.chap_name is not None and \
                   disk.ctd in iscsi.ctd_list:
                    iscsi_string = list()
                    iscsi_string.append("")
                    iscsi_string.append(_("CHAP username and password must "
                                          "be set at the ok prompt:"))
                    iscsi_string.append(_("ok set-ascii-security-key "
                                          "chap-user <chap name>"))
                    iscsi_string.append(_("ok set-ascii-security-key "
                                          "chap-password <chap password>"))
                    self.iscsi_paragraph = "\n".join(iscsi_string)
        else:
            self.header_text = InstallStatus.FAILED_HEADER
            paragraph_text = InstallStatus.FAILED_TEXT
        self.main_win.set_header_text(self.header_text)

        fmt = {}
        fmt.update(self.log_locations)
        fmt.update(RELEASE)
        self.center_win.add_paragraph(paragraph_text % fmt, 2)
        if self.iscsi_paragraph:
            self.center_win.add_paragraph(self.iscsi_paragraph, 10)
Пример #11
0
    def get_disk_summary(self):
        '''Return a string summary of the disk selection'''

        doc = InstallEngine.get_instance().doc
        disk = get_desired_target_disk(doc)
        
        disk_string = list()

        disk_size_str = locale.format("%.1f",
            disk.disk_prop.dev_size.get(Size.gb_units)) + LOCALIZED_GB

        locale_disk_str = _("Disk: ") + disk_size_str + " " + \
            str(disk.disk_prop.dev_type)
        disk_string.append(locale_disk_str)

        if not disk.whole_disk:

            part_data = get_solaris_partition(doc)
            if part_data is not None:
                part_size_str = locale.format("%.1f",
                    part_data.size.get(Size.gb_units)) + LOCALIZED_GB

                locale_part_str = _("Partition: ") + part_size_str + " " +\
                    str(libdiskmgt_const.PARTITION_ID_MAP[part_data.part_type])
                disk_string.append(locale_part_str)
        
            if part_data is None or not part_data.in_zpool:
                slice_data = get_solaris_slice(doc)

                slice_num_str = _("Slice %s: ") % slice_data.name

                slice_size_str = locale.format("%.1f",
                    slice_data.size.get(Size.gb_units)) + LOCALIZED_GB

                locale_slice_str = slice_num_str + slice_size_str + " " +\
                    str(slice_data.in_zpool)
                disk_string.append(locale_slice_str)

        return "\n".join(disk_string)
Пример #12
0
 def get_log_data(self):
     '''Attempt to read in the install log file. If an error occurs,
     the log_data is set to a string explaining the cause, if possible.
     
     '''
     if self.log_data is None:
         try:
             with open(self.install_data.log_location) as log_file:
                 log_data = log_file.read()
         except (OSError, IOError) as error:
             self.log_data = _("Could not read log file:\n\t%s") % \
                               error.strerror
         max_chars = self.win_size_x - 4
         self.log_data = convert_paragraph(log_data, max_chars)
     return self.log_data
Пример #13
0
class WelcomeScreen(BaseScreen):
    '''First screen of the text installer.
    No special __init__ needed beyond that provided by BaseScreen
    
    '''

    HEADER_TEXT = _("Welcome to %(release)s") % RELEASE
    WELCOME_TEXT = _("Thanks for choosing to install %(release)s! This "
                     "installer enables you to install the %(release)s "
                     "Operating System (OS) on SPARC or x86 systems.\n\n"
                     "The installation log will be at %(log)s.\n\n"
                     "How to navigate through this installer:")
    BULLET_ITEMS = [
        _("Use the function keys listed at the bottom of each "
          "screen to move from screen to screen and to perform "
          "other operations."),
        _("Use the up/down arrow keys to change the selection "
          "or to move between input fields."),
        _("If your keyboard does not have function keys, or "
          "they do not respond, press ESC; the legend at the "
          "bottom of the screen will change to show the ESC keys"
          " for navigation and other functions.")
    ]
    BULLET = "- "
    HELP_DATA = (TUI_HELP + "/%s/welcome.txt",
                 _("Welcome and Navigation Instructions"))

    def __init__(self, main_win, install_data):
        super(WelcomeScreen, self).__init__(main_win)
        self.install_data = install_data

    def set_actions(self):
        '''Remove the F3_Back Action from the first screen'''
        self.main_win.actions.pop(self.main_win.back_action.key, None)

    def _show(self):
        '''Display the static paragraph WELCOME_TEXT'''

        log_file = self.install_data.log_location
        y_loc = 1
        fmt = {"log": log_file}
        fmt.update(RELEASE)
        text = WelcomeScreen.WELCOME_TEXT % fmt
        y_loc += self.center_win.add_paragraph(text, start_y=y_loc)
        x_loc = len(WelcomeScreen.BULLET)

        for bullet in WelcomeScreen.BULLET_ITEMS:
            self.center_win.add_text(WelcomeScreen.BULLET, start_y=y_loc)
            y_loc += self.center_win.add_paragraph(bullet,
                                                   start_y=y_loc,
                                                   start_x=x_loc)
Пример #14
0
class LogViewer(BaseScreen):
    '''Screen for reading and displaying the install log'''

    HEADER_TEXT = _("Installation Log")

    def __init__(self, main_win, install_data):
        super(LogViewer, self).__init__(main_win)
        self.log_data = None
        self.scroll_area = None
        self.install_data = install_data

    def set_actions(self):
        '''Remove all actions except F3_Back'''
        self.main_win.actions.pop(curses.KEY_F2)
        self.main_win.actions.pop(curses.KEY_F6)
        self.main_win.actions.pop(curses.KEY_F9)

    def _show(self):
        '''Create a scrollable region and fill it with the install log'''

        self.center_win.border_size = (0, 0)
        self.scroll_area = WindowArea(self.win_size_y, self.win_size_x, 0, 0,
                                      len(self.get_log_data()))
        log = ScrollWindow(self.scroll_area, window=self.center_win)
        log.add_paragraph(self.get_log_data(), 0, 2)
        self.center_win.activate_object(log)

    def get_log_data(self):
        '''Attempt to read in the install log file. If an error occurs,
        the log_data is set to a string explaining the cause, if possible.
        
        '''
        if self.log_data is None:
            try:
                with open(self.install_data.log_location) as log_file:
                    log_data = log_file.read()
            except (OSError, IOError) as error:
                self.log_data = _("Could not read log file:\n\t%s") % \
                                  error.strerror
            max_chars = self.win_size_x - 4
            self.log_data = convert_paragraph(log_data, max_chars)
        return self.log_data
Пример #15
0
 def get_networks(self):
     '''Build a summary of the networks from the DOC data,
     returned as a list of strings
     
     '''
     network_summary = []
     network_summary.append(_("  Computer name: %s") %
                            self.sysconfig.system.hostname)
     nic = self.sysconfig.nic
     
     if nic.type == NetworkInfo.AUTOMATIC:
         network_summary.append(_("  Network Configuration: Automatic"))
     elif nic.type == NetworkInfo.NONE:
         network_summary.append(_("  Network Configuration: None"))
     elif nic.type == NetworkInfo.MANUAL:
         network_summary.append(_("  Manual Configuration: %s")
                                % NetworkInfo.get_nic_desc(nic.nic_iface))
         network_summary.append(_("    IP Address: %s") % nic.ip_address)
         network_summary.append(_("    Netmask: %s") % nic.netmask)
         if nic.gateway:
             network_summary.append(_("    Router: %s") % nic.gateway)
     return network_summary
Пример #16
0
    def get_networks(self):
        '''Build a summary of the networks from the DOC data,
        returned as a list of strings

        '''
        network_summary = []
        network_summary.append(_("  Computer name: %s") %
                               self.sysconfig.system.hostname)
        nic = self.sysconfig.nic

        if nic.type == NetworkInfo.AUTOMATIC:
            network_summary.append(_("  Network Configuration: Automatic"))
        elif nic.type == NetworkInfo.NONE:
            network_summary.append(_("  Network Configuration: None"))
        elif nic.type == NetworkInfo.MANUAL:
            network_summary.append(_("  Manual Configuration: %s")
                                   % NetworkInfo.get_nic_desc(nic.nic_iface))
            network_summary.append(_("    IP Address: %s") % nic.ip_address)
            network_summary.append(_("    Netmask: %s") % nic.netmask)
            if nic.gateway:
                network_summary.append(_("    Router: %s") % nic.gateway)
        return network_summary
Пример #17
0
def decimal_valid(edit_field, disk_win=None):
    '''Check text to see if it is a decimal number of precision no
    greater than the tenths place.

    '''
    text = edit_field.get_text().lstrip()
    radixchar = locale.localeconv()['decimal_point']
    if text.endswith(" "):
        raise UIMessage(_('Only the digits 0-9 and %s are valid.') % radixchar)
    vals = text.split(radixchar)
    if len(vals) > 2:
        raise UIMessage(_('A number can only have one %s') % radixchar)
    try:
        if len(vals[0]) > 0:
            int(vals[0])
        if len(vals) > 1 and len(vals[1]) > 0:
            int(vals[1])
    except ValueError:
        raise UIMessage(_('Only the digits 0-9 and %s are valid.') % radixchar)
    if len(vals) > 1 and len(vals[1]) > 1:
        raise UIMessage(_("Size can be specified to only one decimal place."))
    if disk_win is not None:
        text = text.rstrip(radixchar)
        if not text:
            text = "0"

        # encode user input per locale for floating point conversion
        text = text.encode(get_encoding())
        new_size = Size(str(locale.atof(text)) + Size.gb_units)
        max_size = edit_field.data_obj.get_max_size()

        # When comparing sizes, check only to the first decimal place,
        # as that is all the user sees. (Rounding errors that could
        # cause the partition/slice layout to be invalid get cleaned up
        # prior to target instantiation)
        new_size_rounded = round(new_size.get(Size.gb_units), 1)
        max_size_rounded = round(max_size.get(Size.gb_units), 1)
        if new_size_rounded > max_size_rounded:
            locale_new_size = locale.format("%.1f", new_size_rounded)
            locale_max_size = locale.format("%.1f", max_size_rounded)
            msg = _("The new size ") + locale_new_size + \
                _(" is greater than the available space ") + locale_max_size
            raise UIMessage(msg)
    return True
Пример #18
0
class PartEditScreen(BaseScreen):
    '''Allows user editing of partitions on a disk, or slices on a
    disk/partition
    '''

    PARTITION_PARAGRAPH = _("Oracle Solaris will be installed into the Solaris"
                            " partition. A partition's type can be changed"
                            " using the F5 key.\n\n"
                            "A partition's size can be increased "
                            "up to its Avail space. Avail space can be "
                            "increased by deleting an adjacent partition. "
                            "Delete a partition by changing it to \"Unused\""
                            " using the F5 key.\n\n"
                            "The four primary partition slots are listed on "
                            "the left. If one is an \"Extended\" partition "
                            "its logical partitions are listed on the "
                            "right.") % RELEASE
    SLICE_PARAGRAPH = _("%(release)s will be installed in the \"%(pool)s\" "
                        "slice. Use the F5 key to change a slice to "
                        "\"%(pool)s.\"\n\n"
                        "A slice's size can be increased up to its Avail "
                        "size. Avail can be increased by deleting an adjacent"
                        " slice. Use the F5 key to delete a slice by changing"
                        " it to \"Unused.\"\n\n"
                        "Slices are listed in disk layout order.")

    HEADER_x86_PART = _("Select Partition: ")
    HEADER_x86_SLICE = _("Select Slice in Fdisk Partition")
    HEADER_SPARC_SLICE = _("Select Slice: ")
    HEADER_TYPE_BOOTABLE = _(" %(type)s %(bootable)s")
    SLICE_DESTROY_TEXT = _("indicates the slice's current content will be "
                           "destroyed")
    PART_DESTROY_TEXT = _("indicates the partition's current content will "
                          "be destroyed")
    BOOTABLE = _("Boot")

    SPARC_HELP = (TUI_HELP + "/%s/"
                  "sparc_solaris_slices_select.txt", _("Select Slice"))
    X86_PART_HELP = (TUI_HELP + "/%s/"
                     "x86_fdisk_partitions_select.txt", _("Select Partition"))
    X86_SLICE_HELP = (TUI_HELP + "/%s/"
                      "x86_fdisk_slices_select.txt", _("Select Slice"))

    X86_SELECTION_ERROR = _("A 'Solaris2' partition must be selected for "
                            "installation")
    SPARC_SELECTION_ERROR = _("An 'rpool' slice must be selected for "
                              "installation")
    MULTIPLE_SOLARIS2_ERROR = _("Invalid layout, more than one 'Solaris2' "
                                "partition found")
    PART_TOO_SMALL = _("'Solaris2' partition is too small, installation "
                       "requires at least %(size).1fGB")
    HELP_FORMAT = "    %s"

    def __init__(self, main_win, target_controller, x86_slice_mode=False):
        super(PartEditScreen, self).__init__(main_win)

        global LOGGER
        LOGGER = logging.getLogger(INSTALL_LOGGER_NAME)

        self.x86_slice_mode = x86_slice_mode
        self.is_x86 = platform.processor() == "i386"
        self.doc = InstallEngine.get_instance().doc

        if self.x86_slice_mode:  # x86, Slice within a partition
            self.instance = ".slice"
            self.header_text = PartEditScreen.HEADER_x86_SLICE
            self.paragraph_text = PartEditScreen.SLICE_PARAGRAPH
            self.destroy_text = PartEditScreen.SLICE_DESTROY_TEXT
            self.help_data = PartEditScreen.X86_SLICE_HELP
        elif self.is_x86:  # x86, Partition on disk
            self.header_text = PartEditScreen.HEADER_x86_PART
            self.paragraph_text = PartEditScreen.PARTITION_PARAGRAPH
            self.destroy_text = PartEditScreen.PART_DESTROY_TEXT
            self.help_data = PartEditScreen.X86_PART_HELP
        else:  # SPARC (Slice on disk)
            self.header_text = PartEditScreen.HEADER_SPARC_SLICE
            self.paragraph_text = PartEditScreen.SLICE_PARAGRAPH
            self.destroy_text = PartEditScreen.SLICE_DESTROY_TEXT
            self.help_data = PartEditScreen.SPARC_HELP
            self.help_format = "  %s"

        self.disk_win = None
        self.tc = target_controller

    def set_actions(self):
        '''Edit Screens add 'Reset' and 'Change Type' actions. Since these
        do not manipulate screen direction, they are captured during
        processing by adding them to center_win's key_dict.
        '''
        super(PartEditScreen, self).set_actions()
        reset_action = Action(curses.KEY_F7, _("Reset"))
        change_action = Action(curses.KEY_F5, _("Change Type"))
        self.main_win.actions[reset_action.key] = reset_action
        self.main_win.actions[change_action.key] = change_action
        self.center_win.key_dict[curses.KEY_F7] = self.on_key_F7

    def on_key_F7(self, dummy):
        '''F7 -> Reset the DiskWindow'''
        self.disk_win.reset()

    def _show(self):
        '''Display the explanatory paragraph and create the DiskWindow'''

        disk = get_desired_target_disk(self.doc)
        if disk.label == "GPT":
            raise SkipException

        if disk.whole_disk or getattr(disk, "use_whole_segment", False):
            LOGGER.debug("disk.whole_disk or disk.use_whole_segment is True, "
                         "skip editing")

            # perform final target validation
            perform_final_validation(self.doc)

            raise SkipException

        if self.x86_slice_mode:
            LOGGER.debug("in x86 slice mode")
            part = get_solaris_partition(self.doc)

            LOGGER.debug(str(part))
            if part is None:
                err_msg = "Critical error - no Solaris partition found"
                LOGGER.error(err_msg)
                raise ValueError(err_msg)
            if part.in_zpool is not None:
                LOGGER.debug("Whole partition selected. Skipping slice edit")
                LOGGER.debug(str(part))

                # remove the in_zpool value from partition, delete any existing
                # slices, and create the needed underneath slices
                part.create_entire_partition_slice(in_zpool=part.in_zpool,
                                                   in_vdev=DEFAULT_VDEV_NAME,
                                                   tag=V_ROOT)

                part.bootid = Partition.ACTIVE
                part.in_zpool = None

                LOGGER.debug(str(part))

                # Make sure in_zpool is not set on the Disk, target controller
                # puts it there in some cases
                disk.in_zpool = None

                # perform final target validation
                perform_final_validation(self.doc)

                raise SkipException
        else:
            part = disk

        if self.x86_slice_mode:
            header = self.header_text
        else:
            bootable = ""
            if self.is_x86 and disk.is_boot_disk():
                bootable = PartEditScreen.BOOTABLE
            disk_size_str = locale.format(
                "%.1f", disk.disk_prop.dev_size.get(
                    Size.gb_units)) + LOCALIZED_GB
            type_boot_str = PartEditScreen.HEADER_TYPE_BOOTABLE % \
                {"type": disk.disk_prop.dev_type,
                 "bootable": bootable}
            header = self.header_text + disk_size_str + type_boot_str
        self.main_win.set_header_text(header)

        y_loc = 1
        fmt_dict = {'pool': ROOT_POOL}
        fmt_dict.update(RELEASE)
        y_loc += self.center_win.add_paragraph(self.paragraph_text % fmt_dict,
                                               y_loc)

        y_loc += 1
        disk_win_area = WindowArea(6, 70, y_loc, 0)

        self.disk_win = DiskWindow(disk_win_area,
                                   part,
                                   window=self.center_win,
                                   editable=True,
                                   error_win=self.main_win.error_line,
                                   target_controller=self.tc)
        y_loc += disk_win_area.lines

        y_loc += 1
        LOGGER.log(LOG_LEVEL_INPUT, "calling addch with params start_y=%s,"
                   "start_x=%s, ch=%c", y_loc, self.center_win.border_size[1],
                   DiskWindow.DESTROYED_MARK)
        self.center_win.window.addch(y_loc, self.center_win.border_size[1],
                                     DiskWindow.DESTROYED_MARK,
                                     self.center_win.color_theme.inactive)
        self.center_win.add_text(self.destroy_text, y_loc, 2)

        self.main_win.do_update()
        self.center_win.activate_object(self.disk_win)

    def validate(self):
        ''' Perform final validation of the desired target
        '''

        if self.is_x86:
            # check for multiple Solaris2 partitions
            desired_disk = get_desired_target_disk(self.doc)
            solaris_list = [
                p for p in desired_disk.get_children(class_type=Partition)
                if p.is_solaris
            ]
            if len(solaris_list) > 1:
                raise UIMessage(PartEditScreen.MULTIPLE_SOLARIS2_ERROR)

            solaris_part = get_solaris_partition(self.doc)
            if solaris_part is None:
                raise UIMessage(PartEditScreen.X86_SELECTION_ERROR)

            # verify the size of the Solaris2 partition is large enough
            min_size = self.tc.minimum_target_size
            if min_size > solaris_part.size:
                raise UIMessage(PartEditScreen.PART_TOO_SMALL %
                                {"size": min_size.get(Size.gb_units)})

            disk = solaris_part.parent

            # unset in_zpool and in_vdev on the partiton and the parent Disk.
            # This is a workaround to CR 7085718
            solaris_part.in_zpool = None
            solaris_part.in_vdev = None
            disk.in_zpool = None
            disk.in_vdev = None

            # create system partitions if required
            try:
                efi_sys, resv, solaris_part = \
                    disk.add_required_partitions(solaris_part)
                LOGGER.debug("EFI System Partition:  %s", str(efi_sys))
                # resv is always None as it is GPT specific so don't log it

            except NoPartitionSlotsFree:
                # If there are no unused partitions left we can't proceed
                LOGGER.warning("No free slots available for EFI system "
                               "partition.")
                raise UIMessage("Too many partitions.  Delete unnecessary "
                                "partitions.")

            except InsufficientSpaceError as ise:
                raise RuntimeError("INTERNAL ERROR: Could not allocate space "
                                   "for EFI system partition on disk %s: %s" %
                                   (disk, str(ise)))

            if not self.x86_slice_mode:
                # delay final validation for x86 until slice mode is
                # completed.
                return

            # final target validation will be performed now
            solaris_part.bootid = Partition.ACTIVE

        else:
            inner_window = self.disk_win.get_active_object()
            edit_object = inner_window.get_active_object()
            ui_object = edit_object.get_active_object()
            if ui_object is None:
                raise UIMessage(PartEditScreen.SPARC_SELECTION_ERROR)

        perform_final_validation(self.doc)
Пример #19
0
class GPTPartEditScreen(PartEditScreen):
    """ subclass to handle GPT specific disks
    """

    GPT_PARTITION_PARAGRAPH = _("Oracle Solaris will be installed into the "
                                "highlighted Solaris partition. A partition's "
                                "type can be changed using the F5 key.\n\n"
                                "A partition's size can be increased up to "
                                "its Avail space. Avail space can be "
                                "increased by deleting an adjacent partition. "
                                "Delete a partition by changing it to "
                                "\"Unused\""
                                " using the F5 key.") % RELEASE
    GPT_PART_DESTROY_TEXT = _("indicates the partition's current content will "
                              "be destroyed")
    HEADER_GPT = _("Select GPT Partition: ")
    GPT_HELP = (TUI_HELP + "/%s/gpt_partitions_select.txt",
                _("Select GPT Partition"))
    PART_TOO_SMALL = _("Selected partition is too small, installation "
                       "requires at least %(size).1fGB")
    SELECTION_ERROR = _("A 'Solaris' partition must be selected for "
                        "installation")

    def __init__(self, main_win, target_controller):
        super(GPTPartEditScreen, self).__init__(main_win, target_controller)

        global LOGGER
        LOGGER = logging.getLogger(INSTALL_LOGGER_NAME)

        self.doc = InstallEngine.get_instance().doc

        self.header_text = GPTPartEditScreen.HEADER_GPT
        self.paragraph_text = GPTPartEditScreen.GPT_PARTITION_PARAGRAPH
        self.destroy_text = GPTPartEditScreen.GPT_PART_DESTROY_TEXT
        self.help_data = GPTPartEditScreen.GPT_HELP

        self.disk_win = None
        self.tc = target_controller

    def _show(self):
        '''Display the explanatory paragraph and create the DiskWindow'''

        disk = get_desired_target_disk(self.doc)
        if disk.label == "VTOC":
            raise SkipException

        if disk.whole_disk:
            LOGGER.debug("disk.whole_disk=True, skip editing")

            # perform final target validation
            perform_final_validation(self.doc)

            raise SkipException

        part = disk

        bootable = ""
        if self.is_x86 and disk.is_boot_disk():
            bootable = PartEditScreen.BOOTABLE

        disk_size_str = locale.format(
            "%.1f", disk.disk_prop.dev_size.get(Size.gb_units)) + LOCALIZED_GB
        type_boot_str = PartEditScreen.HEADER_TYPE_BOOTABLE % \
            {"type": disk.disk_prop.dev_type, "bootable": bootable}
        header = self.header_text + disk_size_str + type_boot_str
        self.main_win.set_header_text(header)

        y_loc = 1
        fmt_dict = {'pool': ROOT_POOL}
        fmt_dict.update(RELEASE)
        y_loc += self.center_win.add_paragraph(self.paragraph_text % fmt_dict,
                                               y_loc)

        y_loc += 1
        disk_win_area = WindowArea(6, 70, y_loc, 0)

        self.disk_win = DiskWindow(disk_win_area,
                                   part,
                                   window=self.center_win,
                                   editable=True,
                                   error_win=self.main_win.error_line,
                                   target_controller=self.tc)
        y_loc += disk_win_area.lines

        y_loc += 1
        LOGGER.log(LOG_LEVEL_INPUT, "calling addch with params start_y=%s,"
                   "start_x=%s, ch=%c", y_loc, self.center_win.border_size[1],
                   DiskWindow.DESTROYED_MARK)
        self.center_win.window.addch(y_loc, self.center_win.border_size[1],
                                     DiskWindow.DESTROYED_MARK,
                                     self.center_win.color_theme.inactive)
        self.center_win.add_text(self.destroy_text, y_loc, 2)

        self.main_win.do_update()
        self.center_win.activate_object(self.disk_win)

    def validate(self):
        ''' Perform final validation of the desired target
        '''

        # Before validation can happen, we need to set the in_zpool and in_vdev
        # attributes on *ONLY* the GPT partition the user selected.  We provide
        # the ability for the user to create multiple 'solaris' GPT partitions,
        # but we only use one of them for the root pool.
        # If there are multiple Solaris partitions the user must select one
        # explicitly by highlighting it. If there is only one though, its
        # selection is implicit.
        gpt_partition = None
        disk = get_desired_target_disk(self.doc)

        # In order to get to the specific DOC object we need to translate
        # through the many window layers:
        # main_win.disk_win.[left | right]_win.list_obj.edit_obj.UI_obj.DOC_obj
        inner_window = self.disk_win.get_active_object()
        edit_object = inner_window.get_active_object()
        ui_object = edit_object.get_active_object()

        # trap on any non Solaris partition
        if ui_object is None:
            parts = disk.get_children(class_type=GPTPartition)
            if parts:
                solparts = [p for p in parts if p.is_solaris]
                # Only one Solaris partition, so its selection is implicit.
                if len(solparts) == 1:
                    gpt_partition = solparts[0]

            if gpt_partition is None:
                raise UIMessage(GPTPartEditScreen.SELECTION_ERROR)
        else:
            gpt_partition = ui_object.data_obj.doc_obj

        # verify the size of the selected partition is large enough
        min_size = self.tc.minimum_target_size
        if min_size > gpt_partition.size:
            raise UIMessage(GPTPartEditScreen.PART_TOO_SMALL %
                            {"size": min_size.get(Size.gb_units)})

        # unset in_zpool and in_vdev on the parent Disk object
        disk.in_zpool = None
        disk.in_vdev = None

        # unset in_zpool and in_vdev on all other GPT Partitions on this disk
        for entry in disk.get_children(class_type=GPTPartition):
            if entry.name != gpt_partition.name:
                entry.in_zpool = None
                entry.in_vdev = None

        # Set in_zpool, in_vdev and the action for this partition
        gpt_partition.in_zpool = DEFAULT_ZPOOL_NAME
        gpt_partition.in_vdev = DEFAULT_VDEV_NAME
        gpt_partition.action = "create"

        # create required EFI System or BIOS boot partitions and reserved
        # partitions
        try:
            sys_part, resv_part, gpt_partition = \
                disk.add_required_partitions(donor=gpt_partition)
            LOGGER.debug("System/Boot Partition:  %s", str(sys_part))
            LOGGER.debug("Reserved Partition:  %s", str(resv_part))

        except NoGPTPartitionSlotsFree:
            # If there are no unused partitions left we can't proceed
            LOGGER.warning("No free slots available for boot partition.")
            raise UIMessage("Too many partitions.  Delete unnecessary "
                            "partitions.")

        except InsufficientSpaceError as ise:
            raise RuntimeError(
                "INTERNAL ERROR: Could not allocate space "
                "for system partition or reserved partition on disk %s: "
                "%s" % (disk, str(ise)))

        perform_final_validation(self.doc)
Пример #20
0
def resize_validate(edit_field, disk_win=None):
    '''Check text to see if it is a decimal number of precision no greater
       than the tenths place. Resize the partition if everything checks
       out.
    '''

    LOGGER.debug("in resize_validate()")
    text = edit_field.get_text().lstrip()
    radixchar = locale.localeconv()['decimal_point']
    if text.endswith(" "):
        raise UIMessage(
            _('Only the digits 0-9 and "%s" are valid.') % radixchar)
    vals = text.split(radixchar)
    if len(vals) > 2:
        raise UIMessage(_('A number can only have one "%s"') % radixchar)
    try:
        if len(vals[0]) > 0:
            int(vals[0])
        if len(vals) > 1 and len(vals[1]) > 0:
            int(vals[1])
    except ValueError:
        raise UIMessage(
            _('Only the digits 0-9 and "%s" are valid.') % radixchar)
    if len(vals) > 1 and len(vals[1]) > 1:
        raise UIMessage(_("Size can be specified to only one decimal place."))

    if disk_win is None:
        return True

    text = text.rstrip(radixchar)
    # If the user deleted all digits, leave partition size alone until user
    # inputs new digits
    if not text:
        LOGGER.debug("No size value digits, skipping resize")
        return True

    part_order = disk_win.ui_obj.get_parts_in_use().index(edit_field.data_obj)
    LOGGER.debug("Part being resized is at index: %s", part_order)

    # encode user input per locale for floating point conversion
    text = text.encode(get_encoding())
    new_size = Size(str(locale.atof(text)) + Size.gb_units)
    max_size = edit_field.data_obj.get_max_size()

    # When comparing user input and display sizes, check only to the first
    # decimal place as that is all the user sees.
    new_size_rounded = round(new_size.get(Size.gb_units), 1)
    max_size_rounded = round(max_size.get(Size.gb_units), 1)
    if new_size_rounded > max_size_rounded:
        locale_new_size = locale.format("%.1f", new_size_rounded)
        locale_max_size = locale.format("%.1f", max_size_rounded)
        msg = _("The new size %(size)s is greater than "
                "the available space %(avail)s") % \
                {"size": locale_new_size,
                 "avail": locale_max_size}
        raise UIMessage(msg)

    new_size_text = text.strip()
    LOGGER.debug("New size text=%s", new_size_text)

    old_size = edit_field.data_obj.size
    new_size_byte = new_size.get(Size.byte_units)

    # Filter out edits that would result in resizing a partition to zero
    if new_size_byte == 0:
        return True

    old_size_byte = old_size.get(Size.byte_units)
    precision_bytes = Size(UI_PRECISION).get(Size.byte_units)

    # Ignore potential rounding artifacts.
    if abs(new_size_byte - old_size_byte) <= precision_bytes:
        return True

    max_size_byte = max_size.get(Size.byte_units)

    if new_size_byte > max_size_byte:
        # Allow for loss of precision from rounding errors, but no greater
        if (new_size_byte - max_size_byte) > precision_bytes:
            raise RuntimeError("Requested partition resize to %d bytes "
                               "exceeds maximum size available: %d" %
                               (new_size_byte, max_size_byte))

        # Clamp the new size at max size otherwise the resize will throw
        # an InsufficientSpaceError.
        LOGGER.debug(
            "Requested partition resize exceeds maximum size: "
            "Clamping %d bytes to %d bytes", new_size_byte, max_size_byte)
        new_size = max_size
    parent_doc_obj = edit_field.data_obj.doc_obj.parent
    if isinstance(parent_doc_obj, Disk):
        if isinstance(edit_field.data_obj.doc_obj, GPTPartition):
            resized_obj = parent_doc_obj.resize_gptpartition(
                edit_field.data_obj.doc_obj,
                new_size.get(Size.gb_units),
                size_units=Size.gb_units)
        elif isinstance(edit_field.data_obj.doc_obj, Partition):
            resized_obj = parent_doc_obj.resize_partition(
                edit_field.data_obj.doc_obj,
                new_size.get(Size.gb_units),
                size_units=Size.gb_units)
        else:
            resized_obj = parent_doc_obj.resize_slice(
                edit_field.data_obj.doc_obj,
                new_size.get(Size.gb_units),
                size_units=Size.gb_units)
    else:
        resized_obj = parent_doc_obj.resize_slice(edit_field.data_obj.doc_obj,
                                                  new_size.get(Size.gb_units),
                                                  size_units=Size.gb_units)

    if isinstance(resized_obj, Partition):
        # Don't do this for GPTPartition because there is no guarantee this
        # will be the installation target partition if there is more than
        # 1 Solaris partition
        resized_obj.in_zpool = ROOT_POOL
    elif isinstance(resized_obj, Slice):
        if resized_obj.in_zpool == ROOT_POOL:
            resized_obj.tag = V_ROOT

    disk_win.set_disk_info(ui_obj=disk_win.ui_obj)
    disk_win.activate_index(part_order)

    dump_doc("After resize")

    return True
Пример #21
0
class SummaryScreen(BaseScreen):
    '''Display a summary of the install profile to the user
    InnerWindow.__init__ is sufficient to initalize an instance
    of SummaryScreen
    '''

    HEADER_TEXT = _("Installation Summary")
    PARAGRAPH = _("Review the settings below before installing."
                                " Go back (F3) to make changes.")

    HELP_DATA = (TUI_HELP + "/%s/summary.txt",
                 _("Installation Summary"))

    INDENT = 2

    def set_actions(self):
        '''Replace the default F2_Continue with F2_Install'''
        install_action = Action(curses.KEY_F2, _("Install"),
                                self.main_win.screen_list.get_next)
        self.main_win.actions[install_action.key] = install_action

    def _show(self):
        '''Prepare a text summary and display it to the user in a ScrollWindow

        '''

        global LOGGER
        LOGGER = logging.getLogger(INSTALL_LOGGER_NAME)

        self.sysconfig = solaris_install.sysconfig.profile.from_engine()

        y_loc = 1
        y_loc += self.center_win.add_paragraph(SummaryScreen.PARAGRAPH, y_loc)

        y_loc += 1
        summary_text = self.build_summary()

        LOGGER.info("The following configuration is used for "
                    "installation: %s\n", summary_text)
        # Wrap the summary text, accounting for the INDENT (used below in
        # the call to add_paragraph)
        max_chars = self.win_size_x - SummaryScreen.INDENT - 1
        summary_text = convert_paragraph(summary_text, max_chars)
        area = WindowArea(x_loc=0, y_loc=y_loc,
                          scrollable_lines=(len(summary_text) + 1))
        area.lines = self.win_size_y - y_loc
        area.columns = self.win_size_x
        scroll_region = ScrollWindow(area, window=self.center_win)
        scroll_region.add_paragraph(summary_text, start_x=SummaryScreen.INDENT)

        self.center_win.activate_object(scroll_region)

    def build_summary(self):
        '''Build a textual summary from the DOC data'''
        lines = []

        lines.append(_("Software: %s") % self.get_release())
        lines.append("")
        lines.append(self.get_disk_summary())
        lines.append("")
        lines.append(self.get_tz_summary())
        lines.append("")
        lines.append(_("Language: *The following can be changed when "
                       "logging in."))
        if self.sysconfig.system.locale is None:
            self.sysconfig.system.determine_locale()
        lines.append(_("  Default language: %s") %
                     self.sysconfig.system.actual_lang)
        lines.append("")
        lines.append(_("Keyboard layout: *The following can be "
                       "changed when logging in."))
        lines.append(_("  Default keyboard layout: %s") %
                     self.sysconfig.system.keyboard)
        lines.append("")
        lines.append(_("Terminal type: %s") %
                     self.sysconfig.system.terminal_type)
        lines.append("")
        lines.append(_("Users:"))
        lines.extend(self.get_users())
        lines.append("")
        lines.append(_("Network:"))
        lines.extend(self.get_networks())
        self._get_nameservice(lines)
        lines.append("")
        lines.append(_("Support configuration:"))
        lines.extend(self.get_support())

        return "\n".join(lines)

    def get_networks(self):
        '''Build a summary of the networks from the DOC data,
        returned as a list of strings

        '''
        network_summary = []
        network_summary.append(_("  Computer name: %s") %
                               self.sysconfig.system.hostname)
        nic = self.sysconfig.nic

        if nic.type == NetworkInfo.AUTOMATIC:
            network_summary.append(_("  Network Configuration: Automatic"))
        elif nic.type == NetworkInfo.NONE:
            network_summary.append(_("  Network Configuration: None"))
        elif nic.type == NetworkInfo.MANUAL:
            network_summary.append(_("  Manual Configuration: %s")
                                   % NetworkInfo.get_nic_desc(nic.nic_iface))
            network_summary.append(_("    IP Address: %s") % nic.ip_address)
            network_summary.append(_("    Netmask: %s") % nic.netmask)
            if nic.gateway:
                network_summary.append(_("    Router: %s") % nic.gateway)
        return network_summary

    def _get_nameservice(self, summary):
        ''' Find all name services information and append to summary '''
        # append lines of name service info to summary
        nameservice_summary(self.sysconfig.nameservice, summary)

    def get_users(self):
        '''Build a summary of the user information, and return it as a list
        of strings

        '''
        root = self.sysconfig.users.root
        primary = self.sysconfig.users.user
        user_summary = []
        if not root.password:
            user_summary.append(_("  Warning: No root password set"))
        if primary.login_name:
            user_summary.append(_("  Username: %s") % primary.login_name)
        else:
            user_summary.append(_("  No user account"))
        return user_summary

    def get_disk_summary(self):
        '''Return a string summary of the disk selection'''

        doc = InstallEngine.get_instance().doc
        disk = get_desired_target_disk(doc)

        disk_string = list()

        disk_size_str = locale.format("%.1f",
            disk.disk_prop.dev_size.get(Size.gb_units)) + LOCALIZED_GB

        locale_disk_str = _("Disk: ") + disk_size_str + " " + \
            str(disk.disk_prop.dev_type)
        disk_string.append(locale_disk_str)

        if not disk.whole_disk:

            if disk.label != "VTOC":
                part_data = get_solaris_gpt_partition(doc)
                if part_data is not None:
                    type_str = str(part_data.part_type)
            else:
                part_data = get_solaris_partition(doc)
                if part_data is not None:
                    type_str = str(libdiskmgt_const.PARTITION_ID_MAP[
                                   part_data.part_type])
            if part_data is not None:
                part_size_str = locale.format("%.1f",
                    part_data.size.get(Size.gb_units)) + LOCALIZED_GB

                locale_part_str = _("Partition: ") + part_size_str + " " +\
                    type_str
                disk_string.append(locale_part_str)

            if part_data is None or not part_data.in_zpool:
                slice_data = get_solaris_slice(doc)

                slice_num_str = _("Slice %s: ") % slice_data.name

                slice_size_str = locale.format("%.1f",
                    slice_data.size.get(Size.gb_units)) + LOCALIZED_GB

                locale_slice_str = slice_num_str + slice_size_str + " " +\
                    str(slice_data.in_zpool)
                disk_string.append(locale_slice_str)

        return "\n".join(disk_string)

    def get_tz_summary(self):
        '''Return a string summary of the timezone selection'''
        timezone = self.sysconfig.system.tz_timezone
        return _("Time Zone: %s") % timezone

    @staticmethod
    def get_release():
        '''Read in the release information from /etc/release'''
        try:
            try:
                release_file = open("/etc/release")
            except IOError:
                LOGGER.warn("Could not read /etc/release")
                release_file = None
                release = RELEASE['release']
            else:
                release = release_file.readline()
        finally:
            if release_file is not None:
                release_file.close()
        return release.strip()

    def get_support(self):
        '''Return a string summary of the support selection.'''
        support_summary = []
        support = self.sysconfig.support

        if support.netcfg == SupportInfo.NOSVC:
            support_summary.append(_("  OCM and ASR services are not "
                                     "installed."))
            return support_summary

        ocm_level = None
        asr_level = None

        if support.mos_email:
            if support.ocm_mos_password or support.ocm_ciphertext:
                ocm_level = "auth"
            elif support.ocm_available:
                ocm_level = "unauth"
            if support.asr_mos_password or support.asr_private_key:
                asr_level = "auth"

        if (ocm_level == None and asr_level == None):
            support_summary.append(_("  No telemetry will be "
                                     "sent automatically"))
        elif ocm_level == "unauth":
            # No need to check ASR; ocm_level == unauth implies no password
            # given, so asr_level will never be auth here.
            support_summary.append(_("  Telemetry will be sent and associated "
                                     "with email address:"))
            support_summary.append("       %s" % support.mos_email)
            support_summary.append(_("    but will not be registered with My "
                                     "Oracle Support because"))
            support_summary.append(_("    no password was saved."))
        else:
            # Equivalent to (ocm_level == "auth" or asr_level == "auth")
            support_summary.append(_("  Telemetry will be sent and will be "
                                     "registered with My Oracle Support"))
            support_summary.append(_("    using email address:"))
            support_summary.append("       %s" % support.mos_email)

            # Use the presence of OCM ciphertext to assume that successful OCM
            # validation took place.
            if support.ocm_ciphertext:
                support_summary.append(_("  MOS credentials validated "
                                         "for OCM"))
            elif support.ocm_available:
                support_summary.append(_("  MOS credentials NOT validated "
                                         "for OCM"))

            # Use the presence of ASR private_key to assume that successful ASR
            # validation took place.
            if support.asr_private_key:
                support_summary.append(_("  MOS credentials validated "
                                         "for ASR"))
            elif support.asr_available:
                support_summary.append(_("  MOS credentials NOT validated "
                                         "for ASR"))

            # Display different messages for different situations.
            if ((support.ocm_available and not support.ocm_ciphertext) or
                (support.asr_available and not support.asr_private_key)):
                # Installer environment.
                support_summary.append(_("  Validation will be attempted "
                                         "again when target (re)boots."))
        if support.netcfg == SupportInfo.PROXY:
            if support.proxy_user:
                proxy_line = (_("  Secure proxy "))
            else:
                proxy_line = (_("  Proxy "))
            proxy_line += (_("specified: host: %s" %
                             support.proxy_hostname))
            if support.proxy_port:
                proxy_line += (_("  port: %s" % support.proxy_port))
            if support.proxy_user:
                proxy_line += (_("  user: %s" % support.proxy_user))
            support_summary.append(proxy_line)
        elif support.netcfg == SupportInfo.HUB:
            if support.ocm_hub:
                support_summary.append(_("  OCM hub: %s" % support.ocm_hub))
            if support.asr_hub:
                support_summary.append(_("  ASR hub: %s" % support.asr_hub))

        return support_summary
Пример #22
0
    def get_support(self):
        '''Return a string summary of the support selection.'''
        support_summary = []
        support = self.sysconfig.support

        if support.netcfg == SupportInfo.NOSVC:
            support_summary.append(_("  OCM and ASR services are not "
                                     "installed."))
            return support_summary

        ocm_level = None
        asr_level = None

        if support.mos_email:
            if support.ocm_mos_password or support.ocm_ciphertext:
                ocm_level = "auth"
            elif support.ocm_available:
                ocm_level = "unauth"
            if support.asr_mos_password or support.asr_private_key:
                asr_level = "auth"

        if (ocm_level == None and asr_level == None):
            support_summary.append(_("  No telemetry will be "
                                     "sent automatically"))
        elif ocm_level == "unauth":
            # No need to check ASR; ocm_level == unauth implies no password
            # given, so asr_level will never be auth here.
            support_summary.append(_("  Telemetry will be sent and associated "
                                     "with email address:"))
            support_summary.append("       %s" % support.mos_email)
            support_summary.append(_("    but will not be registered with My "
                                     "Oracle Support because"))
            support_summary.append(_("    no password was saved."))
        else:
            # Equivalent to (ocm_level == "auth" or asr_level == "auth")
            support_summary.append(_("  Telemetry will be sent and will be "
                                     "registered with My Oracle Support"))
            support_summary.append(_("    using email address:"))
            support_summary.append("       %s" % support.mos_email)

            # Use the presence of OCM ciphertext to assume that successful OCM
            # validation took place.
            if support.ocm_ciphertext:
                support_summary.append(_("  MOS credentials validated "
                                         "for OCM"))
            elif support.ocm_available:
                support_summary.append(_("  MOS credentials NOT validated "
                                         "for OCM"))

            # Use the presence of ASR private_key to assume that successful ASR
            # validation took place.
            if support.asr_private_key:
                support_summary.append(_("  MOS credentials validated "
                                         "for ASR"))
            elif support.asr_available:
                support_summary.append(_("  MOS credentials NOT validated "
                                         "for ASR"))

            # Display different messages for different situations.
            if ((support.ocm_available and not support.ocm_ciphertext) or
                (support.asr_available and not support.asr_private_key)):
                # Installer environment.
                support_summary.append(_("  Validation will be attempted "
                                         "again when target (re)boots."))
        if support.netcfg == SupportInfo.PROXY:
            if support.proxy_user:
                proxy_line = (_("  Secure proxy "))
            else:
                proxy_line = (_("  Proxy "))
            proxy_line += (_("specified: host: %s" %
                             support.proxy_hostname))
            if support.proxy_port:
                proxy_line += (_("  port: %s" % support.proxy_port))
            if support.proxy_user:
                proxy_line += (_("  user: %s" % support.proxy_user))
            support_summary.append(proxy_line)
        elif support.netcfg == SupportInfo.HUB:
            if support.ocm_hub:
                support_summary.append(_("  OCM hub: %s" % support.ocm_hub))
            if support.asr_hub:
                support_summary.append(_("  ASR hub: %s" % support.asr_hub))

        return support_summary
Пример #23
0
class DiscoverySelection(BaseScreen):
    """ Allow the user to select which method of target discovery to use.
    NOTE:  Local disk discovery will always happen, regardless of this choice
    """

    HEADER_TEXT = _("Discovery Selection")
    PARAGRAPH = _("Select discovery method for disks")
    LOCAL_TEXT = _("Local Disks")
    LOCAL_DETAIL = _("Discover local disks")
    ISCSI_TEXT = _("iSCSI")
    ISCSI_DETAIL = _("Discover iSCSI LUNs")

    ITEM_OFFSET = 2
    ITEM_MAX_WIDTH = 21
    ITEM_DESC_OFFSET = ITEM_MAX_WIDTH + ITEM_OFFSET + 1

    HELP_DATA = (TUI_HELP + "/%s/discovery_selection.txt",
                 _("Discovery Selection"))

    def __init__(self, main_win):
        """ screen object containing the discovery selection choices
        """

        global LOGGER
        LOGGER = logging.getLogger(INSTALL_LOGGER_NAME)

        super(DiscoverySelection, self).__init__(main_win)

        self.local = None
        self.iscsi = None
        self.current_selection = 0

    def _show(self):
        """ create the screen for the user to select a discovery method
        """

        y_loc = 1
        y_loc += self.center_win.add_paragraph(DiscoverySelection.PARAGRAPH,
                                               y_loc)

        y_loc += 1
        item_area = WindowArea(1, DiscoverySelection.ITEM_MAX_WIDTH, y_loc, 1)
        self.local = ListItem(item_area,
                              window=self.center_win,
                              text=DiscoverySelection.LOCAL_TEXT)
        self.local.item_key = "local"
        self.center_win.add_text(DiscoverySelection.LOCAL_DETAIL, y_loc,
                                 DiscoverySelection.ITEM_DESC_OFFSET,
                                 self.win_size_x - 3)

        y_loc += 2
        item_area.y_loc = y_loc
        self.iscsi = ListItem(item_area,
                              window=self.center_win,
                              text=DiscoverySelection.ISCSI_TEXT)
        self.iscsi.item_key = "iscsi"
        self.center_win.add_text(DiscoverySelection.ISCSI_DETAIL, y_loc,
                                 DiscoverySelection.ITEM_DESC_OFFSET,
                                 self.win_size_x - 3)

        self.main_win.do_update()
        self.center_win.activate_object(self.current_selection)

    def on_change_screen(self):
        """ save the user's choice in case they return to this screen
        """

        choice = self.center_win.get_active_object().item_key
        LOGGER.debug("discovery selection:  %s", choice)
        eng = InstallEngine.get_instance()

        if choice == "iscsi":
            # remove any existing Iscsi DOC objects so there are no duplicates
            prev_iscsi = eng.doc.volatile.get_first_child(name=ISCSI_LABEL,
                                                          class_type=Iscsi)
            if prev_iscsi:
                prev_iscsi.delete()

            # create an empty Iscsi DOC object
            iscsi_doc_obj = Iscsi(ISCSI_LABEL)

            # add the object to the DOC
            eng.doc.volatile.insert_children(iscsi_doc_obj)
        else:
            # look for an existing iSCSI object in the DOC.  If there is one,
            # remove it
            iscsi_doc_obj = eng.doc.volatile.get_first_child(class_type=Iscsi,
                                                             name=ISCSI_LABEL)

            if iscsi_doc_obj is not None:
                iscsi_doc_obj.delete()

        self.current_selection = self.center_win.active_object
Пример #24
0
class GPTPart(BaseScreen):
    '''Allow user to choose to use the whole disk, or move to the
    GPT partition edit screen.
    '''

    BOOT_TEXT = _("Boot")
    HEADER_TYPE_BOOTABLE = _(" %(type)s %(bootable)s")
    HEADER_GPT = _("GPT Partitions: ")
    PARAGRAPH = _("%(release)s can be installed on the whole disk or a "
                  "GPT partition on the disk.") % RELEASE
    FOUND_PART = _("The following GPT partitions were found on the disk.")
    PROPOSED_PART = _("A partition table was not found. The following is "
                      "proposed.")
    USE_WHOLE_DISK = _("Use the entire disk")
    USE_PART_IN_DISK = _("Use a GPT partition of the disk")
    GPT_HELP = (TUI_HELP + "/%s/gpt_partitions.txt", _("GPT Partitions"))

    def __init__(self, main_win, target_controller):
        super(GPTPart, self).__init__(main_win)
        global LOGGER
        LOGGER = logging.getLogger(INSTALL_LOGGER_NAME)

        self.help_format = "  %s"
        self.header_text = GPTPart.HEADER_GPT
        self.found = GPTPart.FOUND_PART
        self.proposed = GPTPart.PROPOSED_PART
        self.use_whole = GPTPart.USE_WHOLE_DISK
        self.use_part = GPTPart.USE_PART_IN_DISK
        self.help_data = GPTPart.GPT_HELP
        self.disk_win = None
        self.whole_disk_item = None
        self.partial_disk_item = None
        self.relabel_disk_item = None
        self.disk = None
        self.tc = target_controller
        self.use_whole_segment = True
        self.paragraph = GPTPart.PARAGRAPH

    def _show(self):
        '''Display partition data for selected disk, and present the two
        choices
        '''

        doc = InstallEngine.get_instance().doc
        disk = get_desired_target_disk(doc)

        # verify the desired disk has a GPT partition table
        if disk.label == "VTOC":
            raise SkipException

        self.disk = disk

        LOGGER.debug("Working with the following disk:")
        LOGGER.debug(str(self.disk))

        # set the header of the screen
        if self.disk.is_boot_disk():
            bootable = GPTPart.BOOT_TEXT
        else:
            bootable = u""

        disk_size_gb_str = locale.format("%.1f",
            self.disk.disk_prop.dev_size.get(Size.gb_units)) + LOCALIZED_GB

        type_bootable_str = GPTPart.HEADER_TYPE_BOOTABLE % \
            {"type": self.disk.disk_prop.dev_type, "bootable": bootable}

        header_text = self.header_text + disk_size_gb_str + type_bootable_str
        self.main_win.set_header_text(header_text)

        if self.disk.whole_disk:
            LOGGER.debug("disk.whole_disk=True, skip editing")
            raise SkipException

        y_loc = 1
        y_loc += self.center_win.add_paragraph(self.paragraph, start_y=y_loc)

        y_loc += 1
        gpt_partitions = self.disk.get_children(class_type=GPTPartition)
        if gpt_partitions:
            next_line = self.found
        else:
            next_line = self.proposed
        y_loc += self.center_win.add_paragraph(next_line, start_y=y_loc)

        y_loc += 1
        disk_win_area = WindowArea(6, 70, y_loc, 0)
        self.disk_win = DiskWindow(disk_win_area, self.disk,
                                   target_controller=self.tc,
                                   window=self.center_win)
        y_loc += disk_win_area.lines

        y_loc += 3
        whole_disk_width = textwidth(self.use_whole) + 3
        cols = (self.win_size_x - whole_disk_width) / 2
        whole_disk_item_area = WindowArea(1, whole_disk_width, y_loc, cols)
        self.whole_disk_item = ListItem(whole_disk_item_area,
                                        window=self.center_win,
                                        text=self.use_whole,
                                        centered=True)

        y_loc += 1

        partial_width = textwidth(self.use_part) + 3
        cols = (self.win_size_x - partial_width) / 2
        partial_item_area = WindowArea(1, partial_width, y_loc, cols)
        self.partial_disk_item = ListItem(partial_item_area,
                                          window=self.center_win,
                                          text=self.use_part,
                                          centered=True)

        self.main_win.do_update()
        if self.use_whole_segment:
            self.center_win.activate_object(self.whole_disk_item)
        else:
            self.center_win.activate_object(self.partial_disk_item)

    def on_continue(self):
        '''Set the user's selection in the install target.
        '''

        active_object = self.center_win.get_active_object()
        if active_object is self.whole_disk_item:
            LOGGER.debug("Setting whole_disk for %s", self.disk)
            self.use_whole_segment = True
            self.disk = self.tc.select_disk(self.disk, use_whole_disk=True)[0]
        else:
            LOGGER.debug("Setting whole_disk to false")
            self.use_whole_segment = False
            self.disk.whole_disk = False

        dump_doc("At the end of gpt_partitions.continue")
Пример #25
0
class PartEditScreen(BaseScreen):
    '''Allows user editing of partitions on a disk, or slices on a
    disk/partition
    
    '''

    PARTITION_PARAGRAPH = _(
        "Syneto StorageOS will be installed into the Solaris"
        " partition. A partition's type can be changed"
        " using the F5 key.\n\n"
        "A partition's size can be increased "
        "up to its Avail space. Avail space can be "
        "increased by deleting an adjacent partition. "
        "Delete a partition by changing it to \"Unused\""
        " using the F5 key.\n\n"
        "The four primary partition slots are listed on "
        "the left. If one is an \"Extended\" partition "
        "its logical partitions are listed on the "
        "right.") % RELEASE
    SLICE_PARAGRAPH = _("%(release)s will be installed in the \"%(pool)s\" "
                        "slice. Use the F5 key to change a slice to "
                        "\"%(pool)s.\"\n\n"
                        "A slice's size can be increased up to its Avail "
                        "size. Avail can be increased by deleting an adjacent"
                        " slice. Use the F5 key to delete a slice by changing"
                        " it to \"Unused.\"\n\n"
                        "Slices are listed in disk layout order.")

    HEADER_x86_PART = _("Select Partition: ")
    HEADER_x86_SLICE = _("Select Slice in Fdisk Partition")
    HEADER_SPARC_SLICE = _("Select Slice: ")
    HEADER_TYPE_BOOTABLE = _(" %(type)s %(bootable)s")
    SLICE_DESTROY_TEXT = _("indicates the slice's current content will be "
                           "destroyed")
    PART_DESTROY_TEXT = _("indicates the partition's current content will "
                          "be destroyed")
    BOOTABLE = _("Boot")

    SPARC_HELP = (TUI_HELP + "/%s/"
                  "sparc_solaris_slices_select.txt", _("Select Slice"))
    X86_PART_HELP = (TUI_HELP + "/%s/"
                     "x86_fdisk_partitions_select.txt", _("Select Partition"))
    X86_SLICE_HELP = (TUI_HELP + "/%s/"
                      "x86_fdisk_slices_select.txt", _("Select Slice"))

    HELP_FORMAT = "    %s"

    def __init__(self, main_win, target_controller, x86_slice_mode=False):
        super(PartEditScreen, self).__init__(main_win)

        global LOGGER
        LOGGER = logging.getLogger(INSTALL_LOGGER_NAME)

        self.x86_slice_mode = x86_slice_mode
        self.is_x86 = (platform.processor() == "i386")
        self.header_text = platform.processor()

        if self.x86_slice_mode:  # x86, Slice within a partition
            self.instance = ".slice"
            self.header_text = PartEditScreen.HEADER_x86_SLICE
            self.paragraph_text = PartEditScreen.SLICE_PARAGRAPH
            self.destroy_text = PartEditScreen.SLICE_DESTROY_TEXT
            self.help_data = PartEditScreen.X86_SLICE_HELP
        elif self.is_x86:  # x86, Partition on disk
            self.header_text = PartEditScreen.HEADER_x86_PART
            self.paragraph_text = PartEditScreen.PARTITION_PARAGRAPH
            self.destroy_text = PartEditScreen.PART_DESTROY_TEXT
            self.help_data = PartEditScreen.X86_PART_HELP
        else:  # SPARC (Slice on disk)
            self.header_text = PartEditScreen.HEADER_SPARC_SLICE
            self.paragraph_text = PartEditScreen.SLICE_PARAGRAPH
            self.destroy_text = PartEditScreen.SLICE_DESTROY_TEXT
            self.help_data = PartEditScreen.SPARC_HELP
            self.help_format = "  %s"

        self.disk_win = None
        self.tc = target_controller

    def set_actions(self):
        '''Edit Screens add 'Reset' and 'Change Type' actions. Since these
        do not manipulate screen direction, they are captured during
        processing by adding them to center_win's key_dict.
        
        '''
        super(PartEditScreen, self).set_actions()
        reset_action = Action(curses.KEY_F7, _("Reset"))
        change_action = Action(curses.KEY_F5, _("Change Type"))
        self.main_win.actions[reset_action.key] = reset_action
        self.main_win.actions[change_action.key] = change_action
        self.center_win.key_dict[curses.KEY_F7] = self.on_key_F7

    # pylint: disable-msg=C0103
    # F7 is the keyname and appropriate here
    def on_key_F7(self, dummy):
        '''F7 -> Reset the DiskWindow'''
        self.disk_win.reset()
        return None

    def _show(self):
        '''Display the explanatory paragraph and create the DiskWindow'''

        doc = InstallEngine.get_instance().doc

        if self.x86_slice_mode:

            LOGGER.debug("in x86 slice mode")

            disk = get_desired_target_disk(doc)
            if disk.whole_disk:
                LOGGER.debug("disk.whole_disk=True, skip editting")
                disk.whole_disk = False

                # perform final target validation
                perform_final_validation(doc)

                raise SkipException

            part = get_solaris_partition(doc)

            LOGGER.debug(str(part))
            if part is None:
                err_msg = "Critical error - no Solaris partition found"
                LOGGER.error(err_msg)
                raise ValueError(err_msg)
            if part.in_zpool is not None:
                LOGGER.debug("Whole partition selected. Skipping slice edit")
                LOGGER.debug(str(part))

                #
                # remove the in_zpool value from partition, delete
                # any existing slices, and create
                # the needed underneath slices
                #
                # All the logic from here to the part.bootid line
                # can be removed when GPT partitions is supported.
                #
                part.create_entire_partition_slice(in_zpool=part.in_zpool,
                                                   in_vdev=DEFAULT_VDEV_NAME,
                                                   tag=V_ROOT)

                part.bootid = Partition.ACTIVE
                part.in_zpool = None

                LOGGER.debug(str(part))

                # perform final target validation
                perform_final_validation(doc)

                raise SkipException
        else:

            # get selected disk from desired target
            disk = get_desired_target_disk(doc)

            LOGGER.debug("disk.whole_disk: %s", disk.whole_disk)
            LOGGER.debug(str(disk))

            if disk.whole_disk:
                LOGGER.debug("disk.whole_disk true, skipping editing")

                if not self.is_x86:
                    # Unset this so Target Instantiation works correctly
                    disk.whole_disk = False

                    # perform final target validation
                    perform_final_validation(doc)

                raise SkipException

            part = disk

        if self.x86_slice_mode:
            header = self.header_text
        else:
            bootable = ""
            if self.is_x86 and disk.is_boot_disk():
                bootable = PartEditScreen.BOOTABLE
            disk_size_str = locale.format(
                "%.1f", disk.disk_prop.dev_size.get(
                    Size.gb_units)) + LOCALIZED_GB
            type_boot_str = PartEditScreen.HEADER_TYPE_BOOTABLE % \
                {"type": disk.disk_prop.dev_type,
                 "bootable": bootable}
            header = self.header_text + disk_size_str + type_boot_str
        self.main_win.set_header_text(header)

        y_loc = 1
        fmt_dict = {'pool': ROOT_POOL}
        fmt_dict.update(RELEASE)
        y_loc += self.center_win.add_paragraph(self.paragraph_text % fmt_dict,
                                               y_loc)

        y_loc += 1
        disk_win_area = WindowArea(6, 70, y_loc, 0)

        self.disk_win = DiskWindow(disk_win_area,
                                   part,
                                   window=self.center_win,
                                   editable=True,
                                   error_win=self.main_win.error_line,
                                   target_controller=self.tc)
        y_loc += disk_win_area.lines

        y_loc += 1
        LOGGER.log(LOG_LEVEL_INPUT, "calling addch with params start_y=%s,"
                   "start_x=%s, ch=%c", y_loc, self.center_win.border_size[1],
                   DiskWindow.DESTROYED_MARK)
        self.center_win.window.addch(y_loc, self.center_win.border_size[1],
                                     DiskWindow.DESTROYED_MARK,
                                     self.center_win.color_theme.inactive)
        self.center_win.add_text(self.destroy_text, y_loc, 2)

        self.main_win.do_update()
        self.center_win.activate_object(self.disk_win)

    def validate(self):
        ''' Perform final validation of the desired target
        '''

        if self.is_x86 and not self.x86_slice_mode:
            # delay final validation for x86 until slice mode
            # is completed.
            return

        # perform final target validation
        doc = InstallEngine.get_instance().doc

        if self.is_x86:
            solaris_part = get_solaris_partition(doc)
            if solaris_part is None:
                raise RuntimeError("No Solaris2 partition in desired target")
            solaris_part.bootid = Partition.ACTIVE

        perform_final_validation(doc)
Пример #26
0
 def set_actions(self):
     '''Replace the default F2_Continue with F2_Install'''
     install_action = Action(curses.KEY_F2, _("Install"),
                             self.main_win.screen_list.get_next)
     self.main_win.actions[install_action.key] = install_action
Пример #27
0
 def set_actions(self):
     '''Replace the default F2_Continue with F2_Install'''
     install_action = Action(curses.KEY_F2, _("Install"),
                             self.main_win.screen_list.get_next)
     self.main_win.actions[install_action.key] = install_action
Пример #28
0
 def get_tz_summary(self):
     '''Return a string summary of the timezone selection'''
     timezone = self.sysconfig.system.tz_timezone
     return _("Time Zone: %s") % timezone
class DiskScreen(BaseScreen):
    '''
    Allow the user to select a (valid) disk target for installation
    Display the partition/slice table for the highlighted disk
    
    '''

    HEADER_TEXT = _("Disks")
    PARAGRAPH = _("Where should %(release)s be installed?") % RELEASE
    REC_SIZE_TEXT = _("Recommended size: ")
    MIN_SIZE_TEXT = _("    Minimum size: ")
    DISK_SEEK_TEXT = _("Seeking disks on system")
    FOUND_x86 = _("The following partitions were found on the disk.")
    FOUND_SPARC = _("The following slices were found on the disk.")
    PROPOSED_x86 = _("A partition table was not found. The following is "
                     "proposed.")
    PROPOSED_SPARC = _("A VTOC label was not found. The following "
                       "is proposed.")
    PROPOSED_GPT = _("A GPT labeled disk was found. The following is "
                     "proposed.")
    TOO_SMALL = _("Too small")
    TOO_BIG_WARN = _("Limited to %.1f TB")
    GPT_LABELED = _("GPT labeled disk")
    NO_DISKS = _("No disks found. Additional device drivers may " "be needed.")
    NO_TARGETS = _("%(release)s cannot be installed on any disk") % RELEASE
    TGT_ERROR = _("An error occurred while searching for installation"
                  " targets. Please check the install log and file a bug"
                  " at defect.opensolaris.org.")

    DISK_HEADERS = [(8, _("Type")), (10, _(" Size(GB)")), (6, _("Boot")),
                    (26, _("Device")), (10, _("Vendor")), (3, ""),
                    (10, _("Notes"))]  #blank header for the notes column
    VENDOR_LEN = 15

    SPINNER = ["\\", "|", "/", "-"]

    DISK_WARNING_HEADER = _("Warning")
    DISK_WARNING_TOOBIG = _("Only the first %.1fTB can be used.")
    DISK_WARNING_GPT = _("You have chosen a GPT labeled disk. Installing "
                         "onto a GPT labeled disk will cause the loss "
                         "of all existing data and the disk will be "
                         "relabeled as SMI.")

    CANCEL_BUTTON = _("Cancel")
    CONTINUE_BUTTON = _("Continue")

    HELP_DATA = (TUI_HELP + "/%s/disks.txt", _("Disks"))

    def __init__(self, main_win, target_controller):

        global LOGGER
        LOGGER = logging.getLogger(INSTALL_LOGGER_NAME)

        super(DiskScreen, self).__init__(main_win)
        if platform.processor() == "i386":
            self.found_text = DiskScreen.FOUND_x86
            self.proposed_text = DiskScreen.PROPOSED_x86
        else:
            self.found_text = DiskScreen.FOUND_SPARC
            self.proposed_text = DiskScreen.PROPOSED_SPARC

        disk_header_text = []
        for header in DiskScreen.DISK_HEADERS:
            header_str = fit_text_truncate(header[1],
                                           header[0] - 1,
                                           just="left")
            disk_header_text.append(header_str)
        self.disk_header_text = " ".join(disk_header_text)
        max_note_size = DiskScreen.DISK_HEADERS[5][0]
        self.too_small_text = DiskScreen.TOO_SMALL[:max_note_size]
        max_disk_size = (Size(MAX_VTOC)).get(Size.tb_units)
        too_big_warn = DiskScreen.TOO_BIG_WARN % max_disk_size
        self.too_big_warn = too_big_warn[:max_note_size]
        self.disk_warning_too_big = \
            DiskScreen.DISK_WARNING_TOOBIG % max_disk_size

        self.disks = []
        self.existing_pools = []
        self.disk_win = None
        self.disk_detail = None
        self.num_targets = 0
        self.td_handle = None
        self._size_line = None
        self.selected_disk_index = 0
        self._minimum_size = None
        self._recommended_size = None

        self.engine = InstallEngine.get_instance()
        self.doc = self.engine.data_object_cache
        self.tc = target_controller
        self._target_discovery_completed = False
        self._target_discovery_status = InstallEngine.EXEC_SUCCESS
        self._image_size = None

    def determine_minimum(self):
        '''Returns minimum install size, fetching first if needed'''
        self.determine_size_data()
        return self._minimum_size

    minimum_size = property(determine_minimum)

    def determine_recommended(self):
        '''Returns recommended install size, fetching first if needed'''
        self.determine_size_data()
        return self._recommended_size

    recommended_size = property(determine_recommended)

    def determine_size_data(self):
        '''Retrieve the minimum and recommended sizes and generate the string
        to present that information.
        
        '''
        if self._minimum_size is None or self._recommended_size is None:
            self._recommended_size = get_recommended_size(self.tc)
            self._minimum_size = get_minimum_size(self.tc)

    def get_size_line(self):
        '''Returns the line of text displaying the min/recommended sizes'''
        if self._size_line is None:
            rec_size_str = locale.format(
                "%.1f", self.recommended_size.get(
                    Size.gb_units)) + LOCALIZED_GB
            min_size_str = locale.format(
                "%.1f", self.minimum_size.get(Size.gb_units)) + LOCALIZED_GB
            self._size_line = DiskScreen.REC_SIZE_TEXT + rec_size_str + \
                DiskScreen.MIN_SIZE_TEXT + min_size_str

        return self._size_line

    size_line = property(get_size_line)

    def wait_for_disks(self):
        '''Block while waiting for libtd to finish. Catch F9 and quit
        if needed
        
        '''
        self.main_win.actions.pop(curses.KEY_F2, None)
        self.main_win.actions.pop(curses.KEY_F6, None)
        self.main_win.actions.pop(curses.KEY_F3, None)
        self.main_win.show_actions()

        self.center_win.add_text(DiskScreen.DISK_SEEK_TEXT, 5, 1,
                                 self.win_size_x - 3)
        self.main_win.do_update()
        offset = textwidth(DiskScreen.DISK_SEEK_TEXT) + 2
        spin_index = 0
        self.center_win.window.timeout(250)

        while not self._target_discovery_completed:
            input_key = self.main_win.getch()
            if input_key == curses.KEY_F9:
                if self.confirm_quit():
                    raise QuitException
            self.center_win.add_text(DiskScreen.SPINNER[spin_index], 5, offset)
            self.center_win.no_ut_refresh()
            self.main_win.do_update()
            spin_index = (spin_index + 1) % len(DiskScreen.SPINNER)

        self.center_win.window.timeout(-1)
        self.center_win.clear()

        # check the result of target discovery
        if self._target_discovery_status is not InstallEngine.EXEC_SUCCESS:
            err_data = (errsvc.get_errors_by_mod_id(TARGET_DISCOVERY))[0]
            LOGGER.error("Target discovery failed")
            err = err_data.error_data[liberrsvc.ES_DATA_EXCEPTION]
            LOGGER.error(err)
            raise TargetDiscoveryError(
                ("Unexpected error (%s) during target "
                 "discovery. See log for details.") % err)

    def _td_callback(self, status, errsvc):
        '''Callback function for Target Discovery checkpoint execution.
           The status value is saved to be interpreted later.
           This function sets the self._target_discovery_completed 
           value to true so the wait_for_disks() function will know
           to stop displaying the spinner.
 
        '''

        self._target_discovery_status = status
        self._target_discovery_completed = True

    def _show(self):
        '''Create a list of disks to choose from and create the window
        for displaying the partition/slice information from the selected
        disk
        
        '''

        self.wait_for_disks()

        discovered_target = self.doc.persistent.get_first_child( \
            name=Target.DISCOVERED)

        LOGGER.debug(discovered_target)
        if discovered_target is None:
            self.center_win.add_paragraph(DiskScreen.NO_DISKS,
                                          1,
                                          1,
                                          max_x=(self.win_size_x - 1))
            return

        self.disks = discovered_target.get_children(class_type=Disk)
        if not self.disks:
            self.center_win.add_paragraph(DiskScreen.NO_TARGETS,
                                          1,
                                          1,
                                          max_x=(self.win_size_x - 1))
            return

        if self._image_size is None:
            try:
                self._image_size = Size(str(get_image_size(LOGGER)) + \
                    Size.mb_units)
                LOGGER.debug("Image_size: %s", self._image_size)
            except:
                # Unable to get the image size for some reason, allow
                # the target controller to use it's default size.
                LOGGER.debug("Unable to get image size")
                self._image_size = FALLBACK_IMAGE_SIZE

        # initialize the target controller so the min/max size for
        # the installation can be calculated.  Explicitly do not
        # want to select an initial disk at this time in case
        # none of the disks discovered is usable.  The target controller
        # initialization needs to be done everytime we show the disk selection
        # screen so the desired target node in the DOC can be re-populated
        # with information from target discovery.
        self.tc.initialize(image_size=self._image_size, no_initial_disk=True)

        # Go through all the disks found and find ones that have
        # enough space for installation.  At the same time, see if any
        # existing disk is the boot disk.  If a boot disk is found, move
        # it to the front of the list
        num_usable_disks = 0
        boot_disk = None
        for disk in self.disks:
            LOGGER.debug("size: %s, min: %s" % \
                         (disk.disk_prop.dev_size, self.minimum_size))
            if disk.disk_prop.dev_size >= self.minimum_size:
                if disk.is_boot_disk():
                    boot_disk = disk
                num_usable_disks += 1
        if boot_disk is not None:
            self.disks.remove(boot_disk)
            self.disks.insert(0, boot_disk)

        if num_usable_disks == 0:
            self.center_win.add_paragraph(DiskScreen.NO_DISKS,
                                          1,
                                          1,
                                          max_x=(self.win_size_x - 1))
            return

        self.main_win.reset_actions()
        self.main_win.show_actions()

        y_loc = 1
        self.center_win.add_text(DiskScreen.PARAGRAPH, y_loc, 1)

        y_loc += 1
        self.center_win.add_text(self.size_line, y_loc, 1)

        y_loc += 2
        self.center_win.add_text(self.disk_header_text, y_loc, 1)

        y_loc += 1
        self.center_win.window.hline(y_loc, self.center_win.border_size[1] + 1,
                                     curses.ACS_HLINE,
                                     textwidth(self.disk_header_text))

        y_loc += 1
        disk_win_area = WindowArea(4,
                                   textwidth(self.disk_header_text) + 2, y_loc,
                                   0)
        disk_win_area.scrollable_lines = len(self.disks) + 1
        self.disk_win = ScrollWindow(disk_win_area, window=self.center_win)

        disk_item_area = WindowArea(1, disk_win_area.columns - 2, 0, 1)
        disk_index = 0
        len_type = DiskScreen.DISK_HEADERS[0][0] - 1
        len_size = DiskScreen.DISK_HEADERS[1][0] - 1
        len_boot = DiskScreen.DISK_HEADERS[2][0] - 1
        len_dev = DiskScreen.DISK_HEADERS[3][0] - 1
        len_mftr = DiskScreen.DISK_HEADERS[4][0] - 1
        for disk in self.disks:
            disk_text_fields = []
            if disk.disk_prop is None or disk.disk_prop.dev_type is None:
                continue
            type_field = disk.disk_prop.dev_type[:len_type]
            type_field = ljust_columns(type_field, len_type)
            disk_text_fields.append(type_field)
            disk_size = disk.disk_prop.dev_size.get(Size.gb_units)
            size_field = locale.format("%*.1f", (len_size, disk_size))
            disk_text_fields.append(size_field)
            if disk.is_boot_disk():
                bootable_field = "+".center(len_boot)
            else:
                bootable_field = " " * (len_boot)
            disk_text_fields.append(bootable_field)
            device_field = disk.ctd[:len_dev]
            device_field = ljust_columns(device_field, len_dev)
            disk_text_fields.append(device_field)
            vendor = disk.disk_prop.dev_vendor
            if vendor is not None:
                mftr_field = vendor[:len_mftr]
                mftr_field = ljust_columns(mftr_field, len_mftr)
            else:
                mftr_field = " " * len_mftr
            disk_text_fields.append(mftr_field)
            selectable = True
            if disk.disk_prop.dev_size < self.minimum_size:
                note_field = self.too_small_text
                selectable = False
            elif disk_size > Size(MAX_VTOC).get(Size.gb_units):
                note_field = self.too_big_warn
            else:
                note_field = ""
            disk_text_fields.append(note_field)
            disk_text = " ".join(disk_text_fields)
            disk_item_area.y_loc = disk_index
            disk_list_item = ListItem(disk_item_area,
                                      window=self.disk_win,
                                      text=disk_text,
                                      add_obj=selectable)
            disk_list_item.on_make_active = on_activate
            disk_list_item.on_make_active_kwargs["disk"] = disk
            disk_list_item.on_make_active_kwargs["disk_select"] = self
            disk_index += 1

        self.disk_win.no_ut_refresh()

        y_loc += 7
        disk_detail_area = WindowArea(6, 70, y_loc, 1)

        self.disk_detail = DiskWindow(disk_detail_area,
                                      self.disks[0],
                                      target_controller=self.tc,
                                      window=self.center_win)

        self.main_win.do_update()
        self.center_win.activate_object(self.disk_win)
        self.disk_win.activate_object(self.selected_disk_index)

    def on_change_screen(self):
        ''' Save the index of the current selected object
        in case the user returns to this screen later
        
        '''

        # Save the index of the selected object
        self.selected_disk_index = self.disk_win.active_object

        LOGGER.debug("disk_selection.on_change_screen, saved_index: %s",
                     self.selected_disk_index)
        LOGGER.debug(self.doc.persistent)

    def start_discovery(self):

        # start target discovery
        if not self._target_discovery_completed:
            errsvc.clear_error_list()

            self.engine.execute_checkpoints(pause_before=TRANSFER_PREP,
                                            callback=self._td_callback)

    def validate(self):
        '''Validate the size of the disk.'''

        warning_txt = []

        disk = self.disk_detail.ui_obj.doc_obj
        disk_size_gb = disk.disk_prop.dev_size.get(Size.gb_units)
        max_size_gb = Size(MAX_VTOC).get(Size.gb_units)
        if disk_size_gb > max_size_gb:
            warning_txt.append(self.disk_warning_too_big)
        warning_txt = " ".join(warning_txt)

        if warning_txt:
            # warn the user and give user a chance to change
            result = self.main_win.pop_up(DiskScreen.DISK_WARNING_HEADER,
                                          warning_txt,
                                          DiskScreen.CANCEL_BUTTON,
                                          DiskScreen.CONTINUE_BUTTON)

            if not result:
                raise UIMessage()  # let user select different disk
Пример #30
0
class IscsiScreen(BaseScreen):
    """ Allow the user to specify parameters for iSCSI LUNs
    """

    HEADER_TEXT = _("iSCSI Discovery")
    INTRO = _("The installer needs additional information for iSCSI LUN "
              "discovery")
    FOUND_DHCP_LABEL = _("DHCP has iSCSI parameters defined for this host")
    TARGET_IP_LABEL = _("Target IP:")
    TARGET_PORT_LABEL = _("Port:")
    TARGET_LUN_LABEL = _("Target LUN:")
    TARGET_NAME_LABEL = _("Target Name:")
    INITIATOR_NAME_LABEL = _("Initiator Name:")
    USE_CHAP_LABEL = _("Use CHAP:")
    CHAP_NAME_LABEL = _("CHAP Name:")
    CHAP_PASSWORD_LABEL = _("CHAP Password:"******"Required Field")
    USE_CHAP_LABEL = _("If using CHAP for authentication")
    MAPPING_LUN_LABEL = _("Mapping iSCSI LUN...")
    MAPPING_TARGET_LABEL = _("Mapping iSCSI Target...")
    ISCSI_BOOT_LABEL = _("iSCSI boot enabled - Initiator Name set in BIOS")

    # error strings
    MISSING_TARGET_IP = _("Target IP address not provided")
    INVALID_TARGET_IP = _("Invalid Target IP address")
    INVALID_INITIATOR_IQN = _("Invalid Initiator IQN string")
    INVALID_TARGET_IQN = _("Invalid Target IQN string")
    CHAP_USERNAME_MISSING = _("CHAP username not specified")
    CHAP_PASSWORD_MISSING = _("CHAP password not specified")
    CHAP_PASSWORD_TOO_SHORT = _("CHAP password must be between 12 and 16 "
                                "characters")
    UNABLE_TO_MAP = _("Unable to map iSCSI LUN: ")

    HELP_DATA = (TUI_HELP + "/%s/iscsi.txt", _("iSCSI"))

    MAX_IP_LEN = 15
    MAX_LUN_LEN = 4
    MAX_PORT_LEN = 5
    MAX_NAME_LEN = 223
    CHAP_LEN = 16
    ITEM_OFFSET = 2
    DEAD_ZONE = 3
    REQUIRED_MARK = EditField.ASTERISK_CHAR

    def __init__(self, main_win):
        """ screen object containing iSCSI criteria objects
        """

        global LOGGER
        LOGGER = logging.getLogger(INSTALL_LOGGER_NAME)
        super(IscsiScreen, self).__init__(main_win)

        self.target_ip = None
        self.target_port = None
        self.target_lun = None
        self.target_name = None
        self._initiator_name = None
        self.chap_name = None
        self.chap_password = None

        self.full_win_width = self.win_size_x - IscsiScreen.DEAD_ZONE
        self.half_win_width = (self.win_size_x - IscsiScreen.DEAD_ZONE) / 2

        self.default_edit = WindowArea(y_loc=0, lines=1,
                                       columns=IscsiScreen.CHAP_LEN)
        self.right_edit = WindowArea(y_loc=0, lines=1,
                                     columns=IscsiScreen.MAX_PORT_LEN)
        self.name_edit = WindowArea(y_loc=0, lines=1,
            scrollable_columns=IscsiScreen.MAX_NAME_LEN + 1)
        self.chap_edit = WindowArea(y_loc=0, lines=1,
                                    columns=IscsiScreen.CHAP_LEN)
        self.target_ip_list = None
        self.target_ip_edit = None
        self.target_port_list = None
        self.target_port_edit = None
        self.target_lun_list = None
        self.target_lun_edit = None
        self.target_name_edit = None
        self.initiator_name_edit = None
        self.chap_name_edit = None
        self.chap_password_edit = None

        # key map dictionary for Target entries
        self.add_keys = {curses.KEY_LEFT: self.on_arrow_key,
                         curses.KEY_RIGHT: self.on_arrow_key,
                         curses.KEY_DOWN: self.on_arrow_key,
                         curses.KEY_UP: self.on_arrow_key}

        self.iscsi_obj = None
        self.is_iscsiboot = is_iscsiboot()

    def on_arrow_key(self, input_key):
        """ override the default behavior of the arrow keys for specific
        EditFields.
        """

        # get the active object
        active = self.center_win.get_active_object()

        # only allow the right arrow to move from IP to Port
        if input_key == curses.KEY_RIGHT and active is self.target_ip_list:
            self.center_win.activate_object(self.target_port_list)
            return None

        # only allow the left arrow to move from Port to IP
        elif input_key == curses.KEY_LEFT and active is self.target_port_list:
            self.center_win.activate_object(self.target_ip_list)
            return None

        # override the default behavior for the up and down arrows to skip the
        # Port field if moving from IP to LUN
        elif input_key == curses.KEY_DOWN and active is self.target_ip_list:
            self.center_win.activate_object(self.target_lun_list)
            return None
        elif input_key == curses.KEY_UP and active is self.target_lun_list:
            self.center_win.activate_object(self.target_ip_list)
            return None

        return input_key

    def check_dhcp(self):
        """ query the DHCP server for the Rootpath string.  If present, update
        the proper attributes.
        """

        dhcp_params = Iscsi.get_dhcp()
        if dhcp_params is not None:
            self.target_ip,
            self.target_port,
            self.target_lun,
            self.target_name = dhcp_params
            return True
        return False

    @property
    def initiator_name(self):
        """ property to return the initiator name
        """

        if self._initiator_name is None:
            cmd = [ISCSIADM, "list", "initiator-node"]
            p = run(cmd)
            for line in p.stdout.splitlines():
                if line.startswith("Initiator node name:"):
                    self._initiator_name = line.split(": ")[1]
        return self._initiator_name

    @initiator_name.setter
    def initiator_name(self, name):
        """ property setter for _initiator_name
        """

        self._initiator_name = name

    def _show(self):
        """ create the screen to collect user input
        """

        # look in the DOC for an Iscsi object
        eng = InstallEngine.get_instance()
        iscsi_obj = eng.doc.volatile.get_children(name=ISCSI_LABEL,
                                                  class_type=Iscsi)

        # If there's no iscsi object in the DOC, skip this screen
        if not iscsi_obj:
            raise SkipException
        else:
            self.iscsi_obj = iscsi_obj[0]
            LOGGER.debug("show Iscsi object:  %s" % str(self.iscsi_obj))

        y_loc = 1
        self.center_win.add_paragraph(IscsiScreen.INTRO, start_y=y_loc)

        # look to see if DHCP is providing information
        if self.check_dhcp():
            y_loc += 2
            self.center_win.add_paragraph(IscsiScreen.FOUND_DHCP_LABEL,
                                          start_y=y_loc)

        # Target IP
        y_loc += 2

        # Mark this field required
        self.center_win.window.addch(y_loc, 2, IscsiScreen.REQUIRED_MARK,
            self.center_win.color_theme.inactive)

        edit_start = textwidth(IscsiScreen.TARGET_IP_LABEL) + \
                     IscsiScreen.ITEM_OFFSET
        ip_area = WindowArea(y_loc=y_loc, x_loc=1, lines=1,
            columns=edit_start + IscsiScreen.MAX_IP_LEN + 1)

        self.target_ip_list = ListItem(ip_area,
                                       window=self.center_win,
                                       text=IscsiScreen.TARGET_IP_LABEL)
        self.target_ip_list.key_dict.update(self.add_keys)

        self.default_edit.x_loc = edit_start
        self.default_edit.columns = IscsiScreen.MAX_IP_LEN + 1
        self.target_ip_edit = EditField(self.default_edit,
                                        window=self.target_ip_list,
                                        validate=incremental_validate_ip,
                                        error_win=self.main_win.error_line)
        self.target_ip_edit.key_dict.update(self.add_keys)
        if self.target_ip is not None:
            self.target_ip_edit.set_text(self.target_ip)

        # Target Port
        edit_start = ip_area.x_loc + \
                     textwidth(IscsiScreen.TARGET_PORT_LABEL) + \
                     IscsiScreen.ITEM_OFFSET
        port_area = WindowArea(y_loc=y_loc,
            x_loc=self.half_win_width + IscsiScreen.DEAD_ZONE,
            lines=1, columns=edit_start + IscsiScreen.MAX_PORT_LEN + 1)

        self.target_port_list = ListItem(port_area,
                                         window=self.center_win,
                                         text=IscsiScreen.TARGET_PORT_LABEL)
        self.target_port_list.key_dict.update(self.add_keys)

        self.right_edit.x_loc = edit_start
        self.right_edit.columns = IscsiScreen.MAX_PORT_LEN + 1
        self.target_port_edit = EditField(self.right_edit,
                                          window=self.target_port_list,
                                          validate=incremental_validate_digits,
                                          error_win=self.main_win.error_line,
                                          text=Iscsi.ISCSI_DEFAULT_PORT)
        self.target_port_edit.key_dict.update(self.add_keys)
        if self.target_port is not None:
            self.target_port_edit.set_text(self.target_port)

        # Target LUN
        y_loc += 1

        edit_start = textwidth(IscsiScreen.TARGET_LUN_LABEL) + \
                     IscsiScreen.ITEM_OFFSET
        lun_area = WindowArea(y_loc=y_loc, x_loc=1, lines=1,
            columns=edit_start + IscsiScreen.MAX_LUN_LEN + 1)

        self.target_lun_list = ListItem(lun_area,
                                        window=self.center_win,
                                        text=IscsiScreen.TARGET_LUN_LABEL)
        self.target_lun_list.key_dict.update(self.add_keys)

        self.default_edit.x_loc = edit_start
        self.default_edit.columns = IscsiScreen.MAX_LUN_LEN + 1
        self.target_lun_edit = EditField(self.default_edit,
                                         window=self.target_lun_list,
                                         validate=incremental_validate_hex,
                                         error_win=self.main_win.error_line)
        self.target_lun_edit.key_dict.update(self.add_keys)
        if self.target_lun is not None:
            self.target_lun_edit.set_text(self.target_lun)

        # Target Name
        y_loc += 2
        name_area = WindowArea(y_loc=y_loc, x_loc=1, lines=1,
            columns=self.full_win_width)

        name_area.y_loc = y_loc
        target_name_list = ListItem(name_area, window=self.center_win,
                                    text=IscsiScreen.TARGET_NAME_LABEL)

        self.name_edit.x_loc = textwidth(IscsiScreen.TARGET_NAME_LABEL) + \
                                         IscsiScreen.ITEM_OFFSET
        self.name_edit.columns = self.full_win_width - \
                                 textwidth(IscsiScreen.TARGET_NAME_LABEL) - \
                                 IscsiScreen.ITEM_OFFSET
        self.target_name_edit = EditField(self.name_edit,
                                          window=target_name_list)
        if self.target_name is not None:
            self.target_name_edit.set_text(self.target_name)

        # Horizontal line
        y_loc += 1
        self.center_win.window.hline(y_loc, 3, curses.ACS_HLINE,
                                     self.full_win_width)

        # Initiator Name
        y_loc += 1

        if self.is_iscsiboot:
            # the system BIOS is configured for iSCSI boot.  This means the
            # user will be unable to change the initiator-name.  Display the
            # name, but don't allow it to be changed.
            text = "%s  %s" % \
                (IscsiScreen.INITIATOR_NAME_LABEL, self.initiator_name)
            self.center_win.add_text(text, start_y=y_loc, start_x=1)
            y_loc += 1
            self.center_win.add_text(IscsiScreen.ISCSI_BOOT_LABEL,
                                     start_y=y_loc, start_x=1)
        else:
            # display the edit field as normal
            name_area.y_loc = y_loc
            initiator_name_list = ListItem(name_area, window=self.center_win,
                text=IscsiScreen.INITIATOR_NAME_LABEL)

            self.name_edit.x_loc = \
                textwidth(IscsiScreen.INITIATOR_NAME_LABEL) + \
                IscsiScreen.ITEM_OFFSET
            self.name_edit.columns = self.full_win_width - \
                textwidth(IscsiScreen.INITIATOR_NAME_LABEL) - \
                IscsiScreen.ITEM_OFFSET
            self.initiator_name_edit = EditField(self.name_edit,
                                                 window=initiator_name_list,
                                                 text=self.initiator_name)

        y_loc += 2
        self.center_win.add_text(IscsiScreen.USE_CHAP_LABEL, y_loc, 1)

        # CHAP username
        y_loc += 1
        edit_start = textwidth(IscsiScreen.CHAP_NAME_LABEL) + \
                               IscsiScreen.ITEM_OFFSET
        chapname_area = WindowArea(y_loc=y_loc, x_loc=15, lines=1,
            columns=edit_start + IscsiScreen.CHAP_LEN)

        chap_name_list = ListItem(chapname_area, window=self.center_win,
                                  text=IscsiScreen.CHAP_NAME_LABEL)

        self.chap_edit.x_loc = edit_start
        self.chap_edit.columns = IscsiScreen.CHAP_LEN + 1
        self.chap_edit.scrollable_columns = IscsiScreen.MAX_NAME_LEN + 1
        self.chap_name_edit = EditField(self.chap_edit, window=chap_name_list)
        if self.chap_name is not None:
            self.chap_name_edit.set_text(self.chap_name)

        # CHAP password
        y_loc += 1
        edit_start = textwidth(IscsiScreen.CHAP_PASSWORD_LABEL) + \
                     IscsiScreen.ITEM_OFFSET
        chapname_area.y_loc = y_loc
        chap_password_list = ListItem(chapname_area, window=self.center_win,
                                      text=IscsiScreen.CHAP_PASSWORD_LABEL)

        self.chap_edit.x_loc = textwidth(IscsiScreen.CHAP_PASSWORD_LABEL) + \
                               IscsiScreen.ITEM_OFFSET
        self.chap_edit.scrollable_columns = None
        self.chap_password_edit = PasswordField(self.chap_edit,
                                                window=chap_password_list)
        if self.chap_password is not None:
            self.chap_password_edit.set_text(self.chap_password)

        # Legend
        y_loc += 2
        self.center_win.window.addch(y_loc, 1, IscsiScreen.REQUIRED_MARK,
            self.center_win.color_theme.inactive)
        self.center_win.add_text(IscsiScreen.REQUIRED_FIELD_LABEL, y_loc, 1)

        self.main_win.do_update()
        self.center_win.activate_object()

    def on_change_screen(self):
        """ save the user's choices in case they return to this screen
        """

        self.target_ip = self.target_ip_edit.get_text()
        self.target_port = self.target_port_edit.get_text()
        self.target_lun = self.target_lun_edit.get_text()
        self.target_name = self.target_name_edit.get_text()
        if not self.is_iscsiboot:
            self.initiator_name = self.initiator_name_edit.get_text()
        self.chap_name = self.chap_name_edit.get_text()
        self.chap_password = self.chap_password_edit.get_text()

    def validate(self):
        """ validate the iSCSI attributes before continuing to disk selection.
        """

        target_ip = self.target_ip_edit.get_text()
        target_lun = self.target_lun_edit.get_text()
        target_port = self.target_port_edit.get_text()
        target_name = self.target_name_edit.get_text()
        if not self.is_iscsiboot:
            initiator_name = self.initiator_name_edit.get_text()
        chap_name = self.chap_name_edit.get_text()
        chap_password = self.chap_password_edit.get_text()

        # validate the target IP
        if not target_ip:
            raise UIMessage(IscsiScreen.MISSING_TARGET_IP)
        else:
            try:
                IPAddress.convert_address(target_ip)
            except ValueError as error:
                raise UIMessage("%s: %s" % \
                    (IscsiScreen.INVALID_TARGET_IP, str(error)))

        # validate the IQN strings (by default re.match only matches at the
        # beginning of a string)
        if not self.is_iscsiboot and initiator_name:
            if IQN_RE.match(initiator_name) is None:
                raise UIMessage(IscsiScreen.INVALID_INITIATOR_IQN)

        if target_name:
            if IQN_RE.match(target_name) is None:
                raise UIMessage(IscsiScreen.INVALID_TARGET_IQN)

        # validate that both CHAP username and password were specified (or not
        # at all)
        if chap_name and not chap_password:
            raise UIMessage(IscsiScreen.CHAP_PASSWORD_MISSING)
        if chap_password and not chap_name:
            raise UIMessage(IscsiScreen.CHAP_USERNAME_MISSING)

        # validate the CHAP password
        if chap_password:
            if not 12 <= len(chap_password) <= 16:
                raise UIMessage(IscsiScreen.CHAP_PASSWORD_TOO_SHORT)

        # Update the Iscsi DOC object
        self.iscsi_obj.target_ip = target_ip

        # force target_lun back to None if the user comes back and removes the
        # LUN entry from the screen
        if target_lun:
            self.iscsi_obj.target_lun = target_lun
        else:
            self.iscsi_obj.target_lun = None

        if target_name:
            self.iscsi_obj.target_name = target_name
        if target_port:
            self.iscsi_obj.target_port = target_port

        if not self.is_iscsiboot and initiator_name:
            self.iscsi_obj.initiator_name = initiator_name

        if chap_name:
            self.iscsi_obj.chap_name = chap_name
        if chap_password:
            self.iscsi_obj.chap_password = chap_password

        # attempt to connect to the LUN
        if target_lun:
            self.main_win.error_line.display_err(IscsiScreen.MAPPING_LUN_LABEL)
        else:
            self.main_win.error_line.display_err(
                IscsiScreen.MAPPING_TARGET_LABEL)

        try:
            self.iscsi_obj.setup_iscsi()
            LOGGER.debug("Iscsi object:  %s" % str(self.iscsi_obj))
        except (CalledProcessError, RuntimeError) as err:
            # remove the iSCSI configuration since it's invalid
            try:
                self.iscsi_obj.teardown()
            except CalledProcessError:
                # ignore any errors
                pass
            raise UIMessage("%s %s" % (IscsiScreen.UNABLE_TO_MAP, str(err)))
Пример #31
0
class DiskScreen(BaseScreen):
    '''
    Allow the user to select a (valid) disk target for installation
    Display the partition/slice table for the highlighted disk

    '''

    HEADER_TEXT = _("Disks")
    PARAGRAPH = _("Where should %(release)s be installed?") % RELEASE
    REC_SIZE_TEXT = _("Recommended size: ")
    MIN_SIZE_TEXT = _("    Minimum size: ")
    DISK_SEEK_TEXT = _("Seeking disks on system")
    FOUND_x86 = _("The following partitions were found on the disk.")
    FOUND_SPARC = _("The following slices were found on the disk.")
    FOUND_GPT = _("The following GPT partitions were found on the disk.")
    PROPOSED_x86 = _("A partition table was not found. The following is "
                     "proposed.")
    PROPOSED_SPARC = _("A VTOC label was not found. The following "
                       "is proposed.")
    PROPOSED_GPT = _("A GPT labeled disk was not found. The following is "
                     "proposed.")
    TOO_SMALL = "<"
    TOO_BIG = ">"
    INVALID_DISK = "!"
    GPT_LABELED = _("GPT labeled disk")
    NO_DISKS = _("No disks found. Additional device drivers may " "be needed.")
    NO_TARGETS = _("%(release)s cannot be installed on any disk") % RELEASE

    DISK_HEADERS = [(8, _("Type")), (10, _(" Size(GB)")), (6, _("Boot")),
                    (44, _("Device")),
                    (3, "")]  # blank header for the notes column
    VENDOR_LEN = 15

    SPINNER = ["\\", "|", "/", "-"]

    DISK_WARNING_HEADER = _("Warning")
    DISK_WARNING_TOOBIG = _("Only the first %.1fTB can be used.")
    DISK_WARNING_RELABEL = _("You have chosen a GPT labeled disk. Installing "
                             "onto this disk requires it to be relabeled as "
                             "SMI. This causes IMMEDIATE LOSS of all data "
                             "on the disk. Select Continue only if you are "
                             "prepared to erase all data on this disk now.")

    CANCEL_BUTTON = _("Cancel")
    CONTINUE_BUTTON = _("Continue")

    HELP_DATA = (TUI_HELP + "/%s/disks.txt", _("Disks"))

    def __init__(self, main_win, target_controller):
        """ screen object containing the disk selection choice for the user
        """

        global LOGGER
        LOGGER = logging.getLogger(INSTALL_LOGGER_NAME)

        super(DiskScreen, self).__init__(main_win)
        if platform.processor() == "i386":
            self.found_text = DiskScreen.FOUND_x86
            self.proposed_text = DiskScreen.PROPOSED_x86
        else:
            self.found_text = DiskScreen.FOUND_SPARC
            self.proposed_text = DiskScreen.PROPOSED_SPARC

        self.gpt_found_text = DiskScreen.FOUND_GPT
        self.gpt_proposed_text = DiskScreen.PROPOSED_GPT

        disk_header_text = []
        for header in DiskScreen.DISK_HEADERS:
            header_str = fit_text_truncate(header[1],
                                           header[0] - 1,
                                           just="left")
            disk_header_text.append(header_str)

        self.disk_header_text = " ".join(disk_header_text)
        self.max_vtoc_disk_size = (Size(MAX_VTOC)).get(Size.tb_units)
        self.disk_warning_too_big = \
            DiskScreen.DISK_WARNING_TOOBIG % self.max_vtoc_disk_size

        self.disks = []
        self.existing_pools = []
        self.disk_win = None
        self.disk_detail = None
        self.num_targets = 0
        self.td_handle = None
        self._size_line = None
        self.selected_disk_index = 0
        self._minimum_size = None
        self._recommended_size = None

        self.engine = InstallEngine.get_instance()
        self.doc = self.engine.data_object_cache
        self.tc = target_controller
        self._target_discovery_completed = False
        self._target_discovery_status = InstallEngine.EXEC_SUCCESS
        self._image_size = None

        self.iscsi = None
        self._iscsi_target_discovery_completed = False
        self._iscsi_target_discovery_status = InstallEngine.EXEC_SUCCESS

    def determine_minimum(self):
        '''Returns minimum install size, fetching first if needed'''

        self.determine_size_data()
        return self._minimum_size

    minimum_size = property(determine_minimum)

    def determine_recommended(self):
        '''Returns recommended install size, fetching first if needed'''

        self.determine_size_data()
        return self._recommended_size

    recommended_size = property(determine_recommended)

    def determine_size_data(self):
        '''Retrieve the minimum and recommended sizes and generate the string
        to present that information.
        '''

        if self._minimum_size is None or self._recommended_size is None:
            self._recommended_size = get_recommended_size(self.tc)
            self._minimum_size = get_minimum_size(self.tc)

    def get_size_line(self):
        '''Returns the line of text displaying the min/recommended sizes'''

        if self._size_line is None:
            rec_size_str = locale.format(
                "%.1f", self.recommended_size.get(
                    Size.gb_units)) + LOCALIZED_GB
            min_size_str = locale.format(
                "%.1f", self.minimum_size.get(Size.gb_units)) + LOCALIZED_GB
            self._size_line = DiskScreen.REC_SIZE_TEXT + rec_size_str + \
                DiskScreen.MIN_SIZE_TEXT + min_size_str

        return self._size_line

    size_line = property(get_size_line)

    def wait_for_disks(self):
        '''Block while waiting for target discovery to finish. Catch F9 and
        quit if needed
        '''

        self.main_win.actions.pop(curses.KEY_F2, None)
        self.main_win.actions.pop(curses.KEY_F6, None)
        self.main_win.actions.pop(curses.KEY_F3, None)
        self.main_win.show_actions()

        self.center_win.add_text(DiskScreen.DISK_SEEK_TEXT, 5, 1,
                                 self.win_size_x - 3)
        self.main_win.do_update()
        offset = textwidth(DiskScreen.DISK_SEEK_TEXT) + 2
        spin_index = 0
        self.center_win.window.timeout(250)

        while not self._target_discovery_completed:
            input_key = self.main_win.getch()
            if input_key == curses.KEY_F9:
                if self.confirm_quit():
                    raise QuitException
            self.center_win.add_text(DiskScreen.SPINNER[spin_index], 5, offset)
            self.center_win.no_ut_refresh()
            self.main_win.do_update()
            spin_index = (spin_index + 1) % len(DiskScreen.SPINNER)

        self.center_win.window.timeout(-1)
        self.center_win.clear()

        # check the result of target discovery
        if self._target_discovery_status is not InstallEngine.EXEC_SUCCESS:
            err_data = errsvc.get_errors_by_mod_id(TARGET_DISCOVERY)[0]
            LOGGER.error("Target discovery failed")
            err = err_data.error_data[liberrsvc.ES_DATA_EXCEPTION]
            LOGGER.error(err)
            raise TargetDiscoveryError(
                ("Unexpected error (%s) during target "
                 "discovery. See log for details.") % err)

    def wait_for_iscsi_disk(self):
        ''' Block while waiting for iSCSI discovery to finish
        '''

        # check for the existence of an Iscsi object in the DOC.  That object
        # is only added when the user selects 'iSCSI' as the discovery criteria
        self.iscsi = self.doc.volatile.get_first_child(name=ISCSI_LABEL,
                                                       class_type=Iscsi)

        if not self.iscsi:
            return

        self.main_win.actions.pop(curses.KEY_F2, None)
        self.main_win.actions.pop(curses.KEY_F6, None)
        self.main_win.actions.pop(curses.KEY_F3, None)
        self.main_win.show_actions()

        self.center_win.add_text(DiskScreen.DISK_SEEK_TEXT, 5, 1,
                                 self.win_size_x - 3)
        self.main_win.do_update()
        offset = textwidth(DiskScreen.DISK_SEEK_TEXT) + 2
        spin_index = 0
        self.center_win.window.timeout(250)

        # there's an iscsi object in the DOC.  Check for an existing iSCSI
        # target discovery checkpoint.  If found, update the kwargs for
        # discovery to find all the information about the newest mapped LUN.
        # If not, register a new checkpoint.
        kwargs = {"search_type": "disk", "search_name": self.iscsi.ctd_list}

        for checkpoint in self.engine._checkpoints:
            if checkpoint.name == ISCSI_TARGET_DISCOVERY:
                checkpoint.kwargs = kwargs
                break
        else:
            # register a new iSCSI target discovery checkpoint
            self.engine.register_checkpoint(ISCSI_TARGET_DISCOVERY,
                                            "solaris_install/target/discovery",
                                            "TargetDiscovery",
                                            kwargs=kwargs,
                                            insert_before=TRANSFER_PREP)

        # run target discovery again against the iSCSI LUN
        self._iscsi_target_discovery_completed = False
        errsvc.clear_error_list()
        self.engine.execute_checkpoints(start_from=ISCSI_TARGET_DISCOVERY,
                                        pause_before=TRANSFER_PREP,
                                        callback=self._iscsi_td_callback)

        while not self._iscsi_target_discovery_completed:
            input_key = self.main_win.getch()
            if input_key == curses.KEY_F9:
                if self.confirm_quit():
                    raise QuitException

            self.center_win.add_text(DiskScreen.SPINNER[spin_index], 5, offset)
            self.center_win.no_ut_refresh()
            self.main_win.do_update()
            spin_index = (spin_index + 1) % len(DiskScreen.SPINNER)

        self.center_win.window.timeout(-1)
        self.center_win.clear()

        # check the result of target discovery
        if self._iscsi_target_discovery_status is not \
           InstallEngine.EXEC_SUCCESS:
            err_data = errsvc.get_errors_by_mod_id(ISCSI_TARGET_DISCOVERY)[0]
            LOGGER.error("iSCSI target discovery failed")
            err = err_data.error_data[liberrsvc.ES_DATA_EXCEPTION]
            LOGGER.error(err)
            raise TargetDiscoveryError(
                "Unexpected error (%s) during iSCSI "
                "target discovery. See log for details." % err)

    def _td_callback(self, status, errsvc):
        '''Callback function for Target Discovery checkpoint execution.  The
        status value is saved to be interpreted later.  This function sets the
        self._target_discovery_completed value to true so the wait_for_disks()
        function will know to stop displaying the spinner.
        '''

        self._target_discovery_status = status
        self._target_discovery_completed = True

    def _iscsi_td_callback(self, status, errsvc):
        '''Callback function for iSCSI Target Discovery checkpoint execution.
           The status value is saved to be interpreted later.
           This function sets the self._iscsi_target_discovery_completed
           value to true so the wait_for_iscsi_disk() function will know
           to stop displaying the spinner.
        '''

        self._iscsi_target_discovery_status = status
        self._iscsi_target_discovery_completed = True

    def _show(self):
        '''Create a list of disks to choose from and create the window
        for displaying the partition/slice information from the selected
        disk
        '''

        self.wait_for_disks()
        self.wait_for_iscsi_disk()

        discovered_target = self.doc.persistent.get_first_child( \
            name=Target.DISCOVERED)

        LOGGER.debug(discovered_target)
        if discovered_target is None:
            self.center_win.add_paragraph(DiskScreen.NO_DISKS,
                                          1,
                                          1,
                                          max_x=(self.win_size_x - 1))
            return

        self.disks = discovered_target.get_children(class_type=Disk)
        if not self.disks:
            self.center_win.add_paragraph(DiskScreen.NO_TARGETS,
                                          1,
                                          1,
                                          max_x=(self.win_size_x - 1))
            return

        if self._image_size is None:
            try:
                self._image_size = Size(str(get_image_size(LOGGER)) + \
                    Size.mb_units)
                LOGGER.debug("Image_size: %s", self._image_size)
            except:
                # Unable to get the image size for some reason, allow
                # the target controller to use it's default size.
                LOGGER.debug("Unable to get image size")
                self._image_size = FALLBACK_IMAGE_SIZE

        # initialize the target controller so the min/max size for the
        # installation can be calculated.  Explicitly do not want to select an
        # initial disk at this time in case none of the disks discovered is
        # usable.  The target controller initialization needs to be done
        # everytime we show the disk selection screen so the desired target
        # node in the DOC can be re-populated with information from target
        # discovery.
        self.tc.initialize(image_size=self._image_size, no_initial_disk=True)

        # Go through all the disks found and find ones that have enough space
        # for installation.  At the same time, see if any existing disk is the
        # boot disk.  If a boot disk is found, move it to the front of the list
        num_usable_disks = 0
        boot_disk = None
        for disk in self.disks:
            LOGGER.debug("size: %s, min: %s" % \
                         (disk.disk_prop.dev_size, self.minimum_size))
            if disk.disk_prop.dev_size >= self.minimum_size:
                if disk.is_boot_disk():
                    boot_disk = disk
                num_usable_disks += 1

        if boot_disk is not None:
            self.disks.remove(boot_disk)
            self.disks.insert(0, boot_disk)

        if num_usable_disks == 0:
            self.center_win.add_paragraph(DiskScreen.NO_DISKS,
                                          1,
                                          1,
                                          max_x=(self.win_size_x - 1))
            return

        self.main_win.reset_actions()
        self.main_win.show_actions()

        y_loc = 1
        self.center_win.add_text(DiskScreen.PARAGRAPH, y_loc, 1)

        y_loc += 1
        self.center_win.add_text(self.size_line, y_loc, 1)

        y_loc += 2
        self.center_win.add_text(self.disk_header_text, y_loc, 1)

        y_loc += 1
        self.center_win.window.hline(y_loc, self.center_win.border_size[1] + 1,
                                     curses.ACS_HLINE,
                                     textwidth(self.disk_header_text))

        y_loc += 1
        disk_win_area = WindowArea(4,
                                   textwidth(self.disk_header_text) + 2, y_loc,
                                   0)
        disk_win_area.scrollable_lines = len(self.disks) + 1
        self.disk_win = ScrollWindow(disk_win_area, window=self.center_win)

        disk_item_area = WindowArea(1, disk_win_area.columns - 2, 0, 1)
        disk_index = 0
        len_type = DiskScreen.DISK_HEADERS[0][0] - 1
        len_size = DiskScreen.DISK_HEADERS[1][0] - 1
        len_boot = DiskScreen.DISK_HEADERS[2][0] - 1
        len_dev = DiskScreen.DISK_HEADERS[3][0] - 1
        len_notes = DiskScreen.DISK_HEADERS[4][0] - 1
        for disk in self.disks:
            disk_text_fields = []
            dev_type = disk.disk_prop.dev_type
            if dev_type is not None:
                type_field = dev_type[:len_type]
                type_field = ljust_columns(type_field, len_type)
            else:
                type_field = " " * len_type
            disk_text_fields.append(type_field)
            disk_size = disk.disk_prop.dev_size.get(Size.gb_units)
            size_field = locale.format("%*.1f", (len_size, disk_size))
            disk_text_fields.append(size_field)
            if disk.is_boot_disk():
                bootable_field = "+".center(len_boot)
            else:
                bootable_field = " " * (len_boot)
            disk_text_fields.append(bootable_field)

            #
            # Information will be displayed in the device column with
            # the following priority:
            #
            # First priority is to display receptacle information,
            # if available.  If receptacle information is displayed,
            # ctd name will not be displayed.
            #
            # If receptacle information is not available, the ctd name
            # will be displayed.
            #
            # Both items above can take as much as the 44 character wide
            # column as needed.
            #
            # If the receptacle/ctd name is less than 30 characters,
            # manufacturer information will be displayed in the left
            # over space.  There won't be a column heading for the
            # manufacturer information.
            #

            device = disk.receptacle or disk.ctd
            added_device_field = False
            # is there enough room to display the manufacturer?
            if (len_dev - len(device)) >= DiskScreen.VENDOR_LEN:
                vendor = disk.disk_prop.dev_vendor
                if vendor is not None:
                    dev_display_len = len_dev - DiskScreen.VENDOR_LEN
                    device_field = ljust_columns(device, dev_display_len)
                    disk_text_fields.append(device_field)
                    vendor_field = vendor[:DiskScreen.VENDOR_LEN - 1]
                    vendor_field = ljust_columns(vendor_field,
                                                 DiskScreen.VENDOR_LEN - 1)
                    disk_text_fields.append(vendor_field)
                    added_device_field = True

            if not added_device_field:
                device_field = device[:len_dev]
                device_field = ljust_columns(device_field, len_dev)
                disk_text_fields.append(device_field)

            # display "<" or ">" if the disk is too big or too small
            selectable = True
            if disk.disk_prop.dev_size < self.minimum_size:
                selectable = False
                notes_field = DiskScreen.TOO_SMALL.center(len_notes)
                disk_text_fields.append(notes_field)
            elif disk.disk_prop.dev_size > Size(MAX_VTOC):
                notes_field = DiskScreen.TOO_BIG.center(len_notes)
                disk_text_fields.append(notes_field)

            # check the blocksize of the disk.  If it's not 512 bytes and we
            # have an EFI firmware on x86, make the disk unselectable by the
            # user.  See PSARC 2008/769
            elif platform.processor() == "i386" and \
                 disk.geometry.blocksize != 512:
                firmware = SystemFirmware.get()
                if firmware.fw_name == "uefi64":
                    selectable = False
                    notes_field = DiskScreen.INVALID_DISK.center(len_notes)
                    disk_text_fields.append(notes_field)
                    LOGGER.debug(
                        "marking disk %s unselectable as its "
                        "blocksize is not 512 bytes on an UEFI "
                        "firmware x86 system.", disk.ctd)

            disk_text = " ".join(disk_text_fields)

            disk_item_area.y_loc = disk_index
            disk_list_item = ListItem(disk_item_area,
                                      window=self.disk_win,
                                      text=disk_text,
                                      add_obj=selectable)
            disk_list_item.on_make_active = on_activate
            disk_list_item.on_make_active_kwargs["disk"] = disk
            disk_list_item.on_make_active_kwargs["disk_select"] = self
            disk_index += 1

        self.disk_win.no_ut_refresh()

        y_loc += 7
        disk_detail_area = WindowArea(6, 70, y_loc, 1)

        self.disk_detail = DiskWindow(disk_detail_area,
                                      self.disks[0],
                                      target_controller=self.tc,
                                      window=self.center_win)

        self.main_win.do_update()
        self.center_win.activate_object(self.disk_win)
        self.disk_win.activate_object(self.selected_disk_index, jump=True)

    def on_prev(self):
        ''' If the user goes back a screen, teardown any existing Iscsi objects
        and reset iSCSI target discovery
        '''

        if self.iscsi is not None:
            # remove the iscsi Disk objects from the doc and from self.disks
            if self.iscsi.ctd_list:
                discovered_target = self.doc.persistent.get_first_child(
                    name=Target.DISCOVERED)

                for disk in discovered_target.get_descendants(class_type=Disk):
                    if disk.ctd in self.iscsi.ctd_list:
                        disk.delete()

                self.disks = discovered_target.get_children(class_type=Disk)

            # tear down the Iscsi object
            self.iscsi.teardown()

            # reset the selected disk index to 0
            self.selected_disk_index = 0

            # reset the iscsi_discovery status
            self._iscsi_target_discovery_completed = False

            # reset the target controller's discovered_disk
            self.tc._discovered_disks = None

    def on_change_screen(self):
        ''' Save the index of the current selected object in case the user
        returns to this screen later
        '''

        # Save the index of the selected object
        self.selected_disk_index = self.disk_win.active_object

        LOGGER.debug("disk_selection.on_change_screen, saved_index: %s",
                     self.selected_disk_index)
        LOGGER.debug(self.doc.persistent)

    def start_discovery(self):
        # start target discovery
        if not self._target_discovery_completed:
            errsvc.clear_error_list()
            self.engine.execute_checkpoints(pause_before=TRANSFER_PREP,
                                            callback=self._td_callback)

    def validate(self):
        '''Validate the size of the disk.'''

        warning_txt = list()

        disk = self.disk_detail.ui_obj.doc_obj
        disk_size_gb = disk.disk_prop.dev_size.get(Size.gb_units)
        max_vtoc_size_gb = Size(MAX_VTOC).get(Size.gb_units)
        # Disk size warning should only be displayed if we are restricted to
        # VTOC boot disks.
        if not can_use_gpt and disk_size_gb > max_vtoc_size_gb:
            warning_txt.append(self.disk_warning_too_big)
        warning_txt = " ".join(warning_txt)

        if warning_txt:
            # warn the user and give user a chance to change
            result = self.main_win.pop_up(DiskScreen.DISK_WARNING_HEADER,
                                          warning_txt,
                                          DiskScreen.CANCEL_BUTTON,
                                          DiskScreen.CONTINUE_BUTTON)

            if not result:
                raise UIMessage()  # let user select different disk
            # if user didn't quit it is always OK to ignore disk size,
            # that will be forced less than the maximum in partitioning.

        warning_txt = list()

        # We also need to warn the user if we need to relabel the disk from
        # GPT to SMI-VTOC
        if disk.label == "GPT" and not can_use_gpt and \
           disk.disk_prop.dev_type != "iSCSI":
            warning_txt.append(DiskScreen.DISK_WARNING_RELABEL)
        warning_txt = " ".join(warning_txt)

        if warning_txt:
            # warn the user and give user a chance to change
            result = self.main_win.pop_up(DiskScreen.DISK_WARNING_HEADER,
                                          warning_txt,
                                          DiskScreen.CANCEL_BUTTON,
                                          DiskScreen.CONTINUE_BUTTON)

            if not result:
                raise UIMessage()  # let user select different disk

            # if user didn't Cancel it is  OK to relabel the disk.
            # This is one of the lesser known (and potentially dangerous)
            # features of target controller: select_disk() with
            # use_whole_disk=True can force a relabeling of the disk from GPT
            # to VTOC is necessary for booting from the disk
            disk = self.tc.select_disk(disk, use_whole_disk=True)[0]

            # The DiskWindow object needs its disk reference updated too
            self.disk_detail.set_disk_info(disk_info=disk)
Пример #32
0
class InstallStatus(BaseScreen):
    '''
    Display text to the user indicating success or failure of the installation.
    Also provide option for viewing the install log

    '''

    SUCCESS_HEADER = _("Installation Complete")
    FAILED_HEADER = _("Installation Failed")

    SUCCESS_TEXT = _("The installation of %(release)s has completed "
                     "successfully.\n\n"
                     "Reboot to start the newly installed software "
                     "or Quit if you wish to perform additional "
                     "tasks before rebooting.\n\n"
                     "The installation log is available at "
                     "%(log_tmp)s. After reboot it can be found"
                     " at %(log_final)s.")

    FAILED_TEXT = _("The installation did not complete normally.\n\n"
                    "For more information you can review the"
                    " installation log.\n"
                    "The installation log is available at %(log_tmp)s")

    def __init__(self, main_win, install_data):
        super(InstallStatus, self).__init__(main_win)
        self.log_locations = {}
        self.install_data = install_data
        self.iscsi_paragraph = ""

    def set_actions(self):
        '''Remove all actions except Quit, and add actions for rebooting
        and viewing the log.

        '''
        self.main_win.actions.pop(curses.KEY_F2)  # Remove F2_Continue
        self.main_win.actions.pop(curses.KEY_F3)  # Remove F3_Back
        self.main_win.actions.pop(curses.KEY_F6)  # Remove F6_Help

        if self.install_data.install_succeeded:
            reboot_action = Action(curses.KEY_F8, _("Reboot"), reboot_system)
            self.main_win.actions[reboot_action.key] = reboot_action

        log_action = Action(curses.KEY_F4, _("View Log"),
                            self.main_win.screen_list.get_next)
        self.main_win.actions[log_action.key] = log_action

    def _show(self):
        '''Display the correct text based on whether the installation
        succeeded or failed.

        '''

        self.log_locations["log_tmp"] = self.install_data.log_location
        self.log_locations["log_final"] = self.install_data.log_final
        if self.install_data.install_succeeded:
            self.header_text = InstallStatus.SUCCESS_HEADER
            paragraph_text = InstallStatus.SUCCESS_TEXT

            # inform the user how to set the CHAP password and username on
            # SPARC, if needed
            if platform.processor() == "sparc":
                doc = InstallEngine.get_instance().doc
                disk = get_desired_target_disk(doc)
                iscsi = doc.volatile.get_first_child(name=ISCSI_LABEL,
                                                     class_type=Iscsi)
                if iscsi and iscsi.chap_name is not None and \
                   disk.ctd in iscsi.ctd_list:
                    iscsi_string = list()
                    iscsi_string.append("")
                    iscsi_string.append(_("CHAP username and password must "
                                          "be set at the ok prompt:"))
                    iscsi_string.append(_("ok set-ascii-security-key "
                                          "chap-user <chap name>"))
                    iscsi_string.append(_("ok set-ascii-security-key "
                                          "chap-password <chap password>"))
                    self.iscsi_paragraph = "\n".join(iscsi_string)
        else:
            self.header_text = InstallStatus.FAILED_HEADER
            paragraph_text = InstallStatus.FAILED_TEXT
        self.main_win.set_header_text(self.header_text)

        fmt = {}
        fmt.update(self.log_locations)
        fmt.update(RELEASE)
        self.center_win.add_paragraph(paragraph_text % fmt, 2)
        if self.iscsi_paragraph:
            self.center_win.add_paragraph(self.iscsi_paragraph, 10)

    def confirm_quit(self):
        '''No need to confirm after installation is complete'''
        return True
Пример #33
0
class SummaryScreen(BaseScreen):
    '''Display a summary of the install profile to the user
    InnerWindow.__init__ is sufficient to initalize an instance
    of SummaryScreen
    
    '''

    HEADER_TEXT = _("Installation Summary")
    PARAGRAPH = _("Review the settings below before installing."
                  " Go back (F3) to make changes.")

    HELP_DATA = (TUI_HELP + "/%s/summary.txt", _("Installation Summary"))

    INDENT = 2

    def set_actions(self):
        '''Replace the default F2_Continue with F2_Install'''
        install_action = Action(curses.KEY_F2, _("Install"),
                                self.main_win.screen_list.get_next)
        self.main_win.actions[install_action.key] = install_action

    def _show(self):
        '''Prepare a text summary and display it to the user in a ScrollWindow
        
        '''

        global LOGGER
        LOGGER = logging.getLogger(INSTALL_LOGGER_NAME)

        self.sysconfig = solaris_install.sysconfig.profile.from_engine()

        y_loc = 1
        y_loc += self.center_win.add_paragraph(SummaryScreen.PARAGRAPH, y_loc)

        y_loc += 1
        summary_text = self.build_summary()

        LOGGER.info(
            "The following configuration is used for "
            "installation: %s\n", summary_text)
        # Wrap the summary text, accounting for the INDENT (used below in
        # the call to add_paragraph)
        max_chars = self.win_size_x - SummaryScreen.INDENT - 1
        summary_text = convert_paragraph(summary_text, max_chars)
        area = WindowArea(x_loc=0,
                          y_loc=y_loc,
                          scrollable_lines=(len(summary_text) + 1))
        area.lines = self.win_size_y - y_loc
        area.columns = self.win_size_x
        scroll_region = ScrollWindow(area, window=self.center_win)
        scroll_region.add_paragraph(summary_text, start_x=SummaryScreen.INDENT)

        self.center_win.activate_object(scroll_region)

    def build_summary(self):
        '''Build a textual summary from the DOC data'''
        lines = []

        lines.append(_("Software: %s") % self.get_release())
        lines.append("")
        lines.append(self.get_disk_summary())
        lines.append("")
        lines.append(self.get_tz_summary())
        lines.append("")
        lines.append(
            _("Language: *The following can be changed when "
              "logging in."))
        if self.sysconfig.system.locale is None:
            self.sysconfig.system.determine_locale()
        lines.append(
            _("  Default language: %s") % self.sysconfig.system.actual_lang)
        lines.append("")
        lines.append(
            _("Keyboard layout: *The following can be "
              "changed when logging in."))
        lines.append(
            _("  Default keyboard layout: %s") %
            self.sysconfig.system.keyboard)
        lines.append("")
        lines.append(
            _("Terminal type: %s") % self.sysconfig.system.terminal_type)
        lines.append("")
        lines.append(_("Users:"))
        lines.extend(self.get_users())
        lines.append("")
        lines.append(_("Network:"))
        lines.extend(self.get_networks())
        self._get_nameservice(lines)

        return "\n".join(lines)

    def get_networks(self):
        '''Build a summary of the networks from the DOC data,
        returned as a list of strings
        
        '''
        network_summary = []
        network_summary.append(
            _("  Storage Array name: %s") % self.sysconfig.system.hostname)
        nic = self.sysconfig.nic

        if nic.type == NetworkInfo.AUTOMATIC:
            network_summary.append(_("  Network Configuration: Automatic"))
        elif nic.type == NetworkInfo.NONE:
            network_summary.append(_("  Network Configuration: None"))
        elif nic.type == NetworkInfo.MANUAL:
            network_summary.append(
                _("  Manual Configuration: %s") %
                NetworkInfo.get_nic_desc(nic.nic_iface))
            network_summary.append(_("    IP Address: %s") % nic.ip_address)
            network_summary.append(_("    Netmask: %s") % nic.netmask)
            if nic.gateway:
                network_summary.append(_("    Router: %s") % nic.gateway)
        return network_summary

    def _get_nameservice(self, summary):
        ''' Find all name services information and append to summary '''
        # append lines of name service info to summary
        nameservice_summary(self.sysconfig.nameservice, summary)

    def get_users(self):
        '''Build a summary of the user information, and return it as a list
        of strings
        
        '''
        user_summary = []
        user_summary.append(
            _("  root password is blank. set it at next login."))
        return user_summary

    def get_disk_summary(self):
        '''Return a string summary of the disk selection'''

        doc = InstallEngine.get_instance().doc
        disk = get_desired_target_disk(doc)

        disk_string = list()

        disk_size_str = locale.format(
            "%.1f", disk.disk_prop.dev_size.get(Size.gb_units)) + LOCALIZED_GB

        locale_disk_str = _("Disk: ") + disk_size_str + " " + \
            str(disk.disk_prop.dev_type)
        disk_string.append(locale_disk_str)

        if not disk.whole_disk:

            part_data = get_solaris_partition(doc)
            if part_data is not None:
                part_size_str = locale.format(
                    "%.1f", part_data.size.get(Size.gb_units)) + LOCALIZED_GB

                locale_part_str = _("Partition: ") + part_size_str + " " +\
                    str(libdiskmgt_const.PARTITION_ID_MAP[part_data.part_type])
                disk_string.append(locale_part_str)

            if part_data is None or not part_data.in_zpool:
                slice_data = get_solaris_slice(doc)

                slice_num_str = _("Slice %s: ") % slice_data.name

                slice_size_str = locale.format(
                    "%.1f", slice_data.size.get(Size.gb_units)) + LOCALIZED_GB

                locale_slice_str = slice_num_str + slice_size_str + " " +\
                    str(slice_data.in_zpool)
                disk_string.append(locale_slice_str)

        return "\n".join(disk_string)

    def get_tz_summary(self):
        '''Return a string summary of the timezone selection'''
        timezone = self.sysconfig.system.tz_timezone
        return _("Time Zone: %s") % timezone

    @staticmethod
    def get_release():
        '''Read in the release information from /etc/release'''
        try:
            try:
                release_file = open("/etc/release")
            except IOError:
                LOGGER.warn("Could not read /etc/release")
                release_file = None
                release = RELEASE['release']
            else:
                release = release_file.readline()
        finally:
            if release_file is not None:
                release_file.close()
        return release.strip()
Пример #34
0
class DiskWindow(InnerWindow):
    '''Display and edit disk information, including partitions and slices'''

    STATIC_PARTITION_HEADERS = [(12, _("Primary"), _("Logical")),
                                (10, _(" Size(GB)"), _(" Size(GB)"))]

    EDIT_PARTITION_HEADERS = [(13, _("Primary"), _("Logical")),
                              (10, _(" Size(GB)"), _(" Size(GB)")),
                              (7, _(" Avail"), _(" Avail"))]

    STATIC_SLICE_HEADERS = [(13, _("Slice"), _("Slice")),
                            (2, "#", "#"),
                            (10, _(" Size(GB)"), _(" Size(GB)"))]

    EDIT_SLICE_HEADERS = [(13, _("Slice"), _("Slice")),
                          (2, "#", "#"),
                          (10, _(" Size(GB)"), _(" Size(GB)")),
                          (7, _(" Avail"), _(" Avail"))]

    ADD_KEYS = {curses.KEY_LEFT: no_action,
                curses.KEY_RIGHT: no_action}

    DEAD_ZONE = 3
    SCROLL_PAD = 2

    MIN_SIZE = None
    REC_SIZE = None

    SIZE_PRECISION = Size(UI_PRECISION).get(Size.gb_units)

    DESTROYED_MARK = EditField.ASTERISK_CHAR

    def __init__(self, area, disk_info, editable=False,
                 error_win=None, target_controller=None, **kwargs):
        '''See also InnerWindow.__init__

        disk_info (required) - Either a Disk or Partition object
        containing the data to be represented. If a Partition objects is
        provided, it will be used for displaying slice
        data within that partition. If Disk has partition(s), those are
        displayed. If not, but it has slices, then those are displayed. If
        neither partition data nor slice data are available, a ValueError is
        raised.

        headers (required) - List of tuples to populate the header of this
        window with. The first item in each tuple should be the width of the
        header, the second item should be the left side header.

        editable (optional) - If True, the window will be created such that
        data is editable.

        target_controller(optional) - Target controller

        '''

        global LOGGER
        LOGGER = logging.getLogger(INSTALL_LOGGER_NAME)

        self.headers = None
        self.orig_ext_part_field = None
        self.orig_logicals_active = False
        self.ext_part_field = None
        self.error_win = error_win
        self.editable = editable
        self.win_width = None
        self.left_win = None
        self.right_win = None
        self.list_area = None
        self.edit_area = None
        super(DiskWindow, self).__init__(area, add_obj=editable, **kwargs)
        self.left_header_string = None
        self.right_header_string = None
        self._orig_data = None
        self.disk_info = None
        self.has_partition_data = False
        self.key_dict[curses.KEY_LEFT] = self.on_arrow_key
        self.key_dict[curses.KEY_RIGHT] = self.on_arrow_key
        if self.editable:
            self.key_dict[curses.KEY_F5] = self.change_type

        self.tc = target_controller
        self._ui_obj = None
        self.ui_obj = disk_info

        self.set_disk_info(ui_obj=self.ui_obj)

        LOGGER.debug(self.ui_obj)

        if platform.processor() == "sparc":
            self.is_x86 = False
        else:
            self.is_x86 = True

    @property
    def ui_obj(self):
        return self._ui_obj

    @ui_obj.setter
    def ui_obj(self, part):
        ''' create and set the value for ui_obj depending on type '''
        if isinstance(part, Disk):
            self._ui_obj = UIDisk(self.tc, parent=None, doc_obj=part)
        elif isinstance(part, Partition):
            self._ui_obj = UIPartition(self.tc, parent=None, doc_obj=part)
        else:
            # Must be a either a Disk or Partition.  It's an error to be here
            raise RuntimeError("disk_info object is invalid")

    def _init_win(self, window):
        '''Require at least 70 columns and 6 lines to fit current needs for
        display of partitions and slices. Builds two inner ScrollWindows for
        displaying/editing the data.

        '''
        if self.area.columns < 70:
            raise ValueError("Insufficient space - area.columns < 70")
        if self.area.lines < 6:
            raise ValueError("Insufficient space - area.lines < 6")
        self.win_width = (self.area.columns - DiskWindow.DEAD_ZONE
                          + DiskWindow.SCROLL_PAD) / 2

        super(DiskWindow, self)._init_win(window)

        win_area = WindowArea(self.area.lines - 1, self.win_width, 2, 0)
        win_area.scrollable_lines = self.area.lines - 2
        self.left_win = ScrollWindow(win_area, window=self, add_obj=False)
        self.left_win.color = None
        self.left_win.highlight_color = None
        win_area.x_loc = self.win_width + DiskWindow.DEAD_ZONE
        win_area.scrollable_lines = 2 * MAX_EXT_PARTS
        self.right_win = ScrollWindow(win_area, window=self, add_obj=False)
        self.right_win.color = None
        self.right_win.highlight_color = None

    def set_disk_info(self, ui_obj=None, disk_info=None, no_part_ok=False):
        '''Set up this DiskWindow to represent disk_info'''

        if ui_obj is not None:
            disk_info = ui_obj.doc_obj
        elif disk_info is not None:
            self.ui_obj = disk_info
        else:
            # Should never be this case
            raise RuntimeError("Unable to find ui_obj or disk_info")

        part_list = disk_info.get_children(class_type=Partition)
        if part_list:
            self.has_partition_data = True
        else:
            slice_list = disk_info.get_children(class_type=Slice)
            if slice_list:
                self.has_partition_data = False
            else:
                # No partitions and no slices
                if no_part_ok:
                    if self.is_x86:
                        self.has_partition_data = True
                    else:
                        self.has_partition_data = False
                else:
                    return

        if self.has_partition_data:
            if self.editable:
                self.headers = DiskWindow.EDIT_PARTITION_HEADERS
                self.list_area = WindowArea(1, self.headers[0][0] +
                                            self.headers[1][0],
                                            0, DiskWindow.SCROLL_PAD)
                self.edit_area = WindowArea(1, self.headers[1][0], 0,
                                            self.headers[0][0])
            else:
                self.headers = DiskWindow.STATIC_PARTITION_HEADERS
        else:
            if self.editable:
                self.headers = DiskWindow.EDIT_SLICE_HEADERS
                self.list_area = WindowArea(1, self.headers[0][0] +
                                            self.headers[1][0] +
                                            self.headers[2][0],
                                            0, DiskWindow.SCROLL_PAD)
                self.edit_area = WindowArea(1, self.headers[2][0], 0,
                                            self.headers[0][0] +
                                            self.headers[1][0])
            else:
                self.headers = DiskWindow.STATIC_SLICE_HEADERS

        LOGGER.debug("have_partition: %s", self.has_partition_data)
        LOGGER.debug(self.ui_obj)

        self.ui_obj.add_unused_parts(no_part_ok=no_part_ok)

        self.left_win.clear()
        self.right_win.clear()
        self.window.erase()
        self.print_headers()

        if self.editable:
            self.active_object = None
            self.build_edit_fields()
            self.right_win.bottom = max(0, len(self.right_win.all_objects) -
                                        self.right_win.area.lines)
            if self.has_partition_data:
                self.orig_ext_part_field = None
                for obj in self.left_win.objects:
                    if (obj.data_obj.is_extended()):
                        self.orig_ext_part_field = obj
                        self.orig_logicals_active = True
                        break
        else:
            self.print_data()

    def print_headers(self):
        '''Print the headers for the displayed data.

        header[0] - The width of this column. header[1] and header[2] are
                    trimmed to this size
        header[1] - The internationalized text for the left window
        header[2] - The internationalized text for the right window

        '''
        self.left_header_string = []
        self.right_header_string = []
        for header in self.headers:
            left_header_str = header[1]
            right_header_str = header[2]
            # Trim the header to fit in the column width,
            # splitting columns with at least 1 space
            # Pad with extra space(s) to align the columns
            left_header_str = fit_text_truncate(left_header_str,
                                                header[0] - 1, just="left")
            self.left_header_string.append(left_header_str)
            right_header_str = fit_text_truncate(right_header_str,
                                                header[0] - 1, just="left")
            self.right_header_string.append(right_header_str)
        self.left_header_string = " ".join(self.left_header_string)
        self.right_header_string = " ".join(self.right_header_string)
        LOGGER.debug(self.left_header_string)
        self.add_text(self.left_header_string, 0, DiskWindow.SCROLL_PAD)
        right_win_offset = (self.win_width + DiskWindow.DEAD_ZONE +
                            DiskWindow.SCROLL_PAD)
        self.add_text(self.right_header_string, 0, right_win_offset)
        self.window.hline(1, DiskWindow.SCROLL_PAD, curses.ACS_HLINE,
                          textwidth(self.left_header_string))
        self.window.hline(1, right_win_offset, curses.ACS_HLINE,
                          textwidth(self.right_header_string))
        self.no_ut_refresh()

    def print_data(self):
        '''Print static (non-editable) data.

        Slices - fill the left side, then remaining slices on the right side.
        If for some reason not all slices fit, indicate how many more slices
        there area

        Partitions - Put standard partitions on the left, logical partitions
        on the right

        '''

        part_index = 0
        data = self.ui_obj.get_parts_in_use()

        if len(data) == 0:
            return   # should never be this case

        if self.has_partition_data:
            max_parts = MAX_PRIMARY_PARTS
        else:
            max_parts = min(len(data), self.left_win.area.lines)

        win = self.left_win
        y_loc = 0
        for next_data in data:
            LOGGER.debug("next_data: %s", next_data)
            if y_loc >= max_parts:
                if win is self.left_win:
                    win = self.right_win
                    y_loc = 0
                    max_parts = win.area.lines
                else:
                    num_extra = len(data) - part_index
                    if self.has_partition_data:
                        more_parts_txt = _("%d more partitions") % num_extra
                    else:
                        more_parts_txt = _("%d more slices") % num_extra
                    win.add_text(more_parts_txt, win.area.lines, 3)
                    break
            x_loc = DiskWindow.SCROLL_PAD
            field = 0
            win.add_text(next_data.get_description(), y_loc, x_loc,
                         self.headers[field][0] - 1)
            x_loc += self.headers[field][0]
            field += 1
            if not self.has_partition_data:
                win.add_text(str(next_data.name), y_loc, x_loc,
                             self.headers[field][0] - 1)
                x_loc += self.headers[field][0]
                field += 1
            win.add_text(locale.format("%*.1f", (self.headers[field][0] - 1,
                next_data.size.get(Size.gb_units))), y_loc, x_loc,
                self.headers[field][0] - 1)
            x_loc += self.headers[field][0]
            y_loc += 1
            field += 1
            part_index += 1
        self.right_win.use_vert_scroll_bar = False
        self.no_ut_refresh()

    def build_edit_fields(self):
        '''Build subwindows for editing partition sizes

        For slices, fill the left side, then the right (right side scrolling as
        needed, though this shouldn't happen unless the number of slices on
        disk exceeds 8 for some reason)

        For partitions, fill the left side up to MAX_PRIMARY_PARTS,
        and place all logical partitions on the right.

        '''

        data = self.ui_obj.get_parts_in_use()

        if self.has_partition_data:
            max_left_parts = MAX_PRIMARY_PARTS
        else:
            if len(data) == 0:
                return   # should never be this case
            max_left_parts = min(len(data), self.left_win.area.lines)

        part_iter = iter(data)
        try:
            next_part = part_iter.next()
            self.objects.append(self.left_win)
            for y_loc in range(max_left_parts):
                self.list_area.y_loc = y_loc
                self.create_list_item(next_part, self.left_win, self.list_area)
                next_part = part_iter.next()
            self.objects.append(self.right_win)
            for y_loc in range(self.right_win.area.scrollable_lines):
                self.list_area.y_loc = y_loc
                self.create_list_item(next_part, self.right_win,
                                      self.list_area)
                next_part = part_iter.next()
            if len(data) > max_left_parts:
                self.right_win.use_vert_scroll_bar = True
        except StopIteration:
            if len(self.right_win.all_objects) <= self.right_win.area.lines:
                self.right_win.use_vert_scroll_bar = False
            self.right_win.no_ut_refresh()
        else:
            raise ValueError("Could not fit all partitions in DiskWindow")
        self.no_ut_refresh()

    def create_list_item(self, next_part, win, list_area):
        '''Add an entry for next_part (a Partition or Slice) to
        the DiskWindow

        '''
        list_item = ListItem(list_area, window=win, data_obj=next_part)
        list_item.key_dict.update(DiskWindow.ADD_KEYS)
        edit_field = EditField(self.edit_area, window=list_item,
                               numeric_pad=" ",
                               validate=decimal_valid,
                               on_exit=on_exit_edit,
                               error_win=self.error_win,
                               add_obj=False,
                               data_obj=next_part)
        edit_field.right_justify = True
        edit_field.validate_kwargs["disk_win"] = self
        edit_field.on_exit_kwargs["disk_win"] = self
        edit_field.key_dict.update(DiskWindow.ADD_KEYS)
        self.update_part(part_field=list_item)
        return list_item

    def update_part(self, part_info=None, part_field=None):
        '''Sync changed partition data to the screen.'''
        if part_field is None:
            if part_info is None:
                raise ValueError("Must supply either part_info or part_field")
            part_field = self.find_part_field(part_info)[1]
        elif part_info is None:
            part_info = part_field.data_obj
        elif part_field.data_obj is not part_info:
            raise ValueError("part_field must be a ListItem associated with "
                             "part_info")
        if not isinstance(part_field, ListItem):
            raise TypeError("part_field must be a ListItem associated with "
                            "part_info")
        if self.has_partition_data:
            desc_text = part_info.get_description()
        else:
            desc_length = self.headers[0][0] - 1
            desc_text = "%-*.*s %s" % (desc_length, desc_length,
                                       part_info.get_description(),
                                       part_info.name)
        part_field.set_text(desc_text)
        edit_field = part_field.all_objects[0]
        edit_field.set_text(locale.format("%.1f",
                                          part_info.size.get(Size.gb_units)))
        self.mark_if_destroyed(part_field)
        self._update_edit_field(part_info, part_field, edit_field)

        self.update_avail_space(part_info=part_info)
        if self.has_partition_data:
            if part_info.is_extended():
                self.ext_part_field = part_field

    def _update_edit_field(self, part_info, part_field, edit_field):
        '''If the partition/slice is editable, add it to the .objects list.
        If it's also the part_field that's currently selected, then activate
        the edit field.

        '''
        if part_info.editable():
            part_field.objects = [edit_field]
            active_win = self.get_active_object()
            if active_win is not None:
                if active_win.get_active_object() is part_field:
                    part_field.activate_object(edit_field)
        else:
            edit_field.make_inactive()
            part_field.objects = []
            part_field.active_object = None

    def mark_if_destroyed(self, part_field):
        '''Determine if the partition/slice represented by part_field has
        changed such that its contents will be destroyed.

        '''
        part_info = part_field.data_obj
        destroyed = part_info.modified()
        self.mark_destroyed(part_field, destroyed)

    def mark_destroyed(self, part_field, destroyed):
        '''If destroyed is True, add an asterisk indicating that the
        partition or slice's content will be destroyed during installation.
        Otherwise, clear the asterisk

        '''
        y_loc = part_field.area.y_loc
        x_loc = part_field.area.x_loc - 1
        if part_field in self.right_win.objects:
            win = self.right_win
        else:
            win = self.left_win
        if destroyed:
            win.window.addch(y_loc, x_loc, DiskWindow.DESTROYED_MARK,
                             win.color_theme.inactive)
        else:
            win.window.addch(y_loc, x_loc, InnerWindow.BKGD_CHAR)

    def update_avail_space(self, part_number=None, part_info=None):
        '''Update the 'Avail' column for the specified slice or partition.
        If no number is given, all avail columns are updated

        '''
        if part_number is None and part_info is None:
            self._update_all_avail_space()
        else:
            self._update_avail_space(part_number, part_info)

    def _update_all_avail_space(self):
        '''Update the 'Avail' column for all slices or partitions.'''
        idx = 0
        for item in self.left_win.objects:
            self.update_avail_space(idx)
            idx += 1
        for item in self.right_win.objects:
            self.update_avail_space(idx)
            idx += 1
        y_loc = idx - len(self.left_win.objects)
        if self.has_partition_data:
            x_loc = self.headers[0][0] + self.headers[1][0] + 1
            field = 2
        else:
            x_loc = (self.headers[0][0] + self.headers[1][0] +
                     self.headers[2][0] + 1)
            field = 3
        if y_loc > 0:
            self.right_win.add_text(" " * self.headers[field][0],
                                    y_loc, x_loc)

    def _update_avail_space(self, part_number=None, part_info=None):
        '''Update the 'Avail' column for the specified slice or partition.'''
        if part_number is None:
            win, item = self.find_part_field(part_info)
        elif part_number < len(self.left_win.objects):
            win = self.left_win
            item = win.objects[part_number]
        else:
            win = self.right_win
            item = win.objects[part_number - len(self.left_win.objects)]
        if self.has_partition_data:
            x_loc = self.headers[0][0] + self.headers[1][0] + 1
            field = 2
        else:
            x_loc = (self.headers[0][0] + self.headers[1][0] +
                     self.headers[2][0] + 1)
            field = 3
        y_loc = item.area.y_loc
        part = item.data_obj
        max_space = part.get_max_size()
        max_space = locale.format("%*.1f", (self.headers[field][0],
                                             max_space.get(Size.gb_units)))
        win.add_text(max_space, y_loc, x_loc)

    def find_part_field(self, part_info):
        '''Given a PartitionInfo or SliceInfo object, find the associated
        ListItem. This search compares by reference, and will only succeed
        if you have a handle to the exact object referenced by the ListItem

        '''
        for win in [self.left_win, self.right_win]:
            for item in win.objects:
                if item.data_obj is part_info:
                    return win, item
        raise ValueError("Part field not found")

    def reset(self, dummy=None):
        '''Reset ui_obj to value found from Target Discovery.
        Meaningful only for editable DiskWindows

        '''
        if not self.editable:
            return
        doc = InstallEngine.get_instance().doc

        # "reset" the desired target
        reset_obj = None
        if isinstance(self.ui_obj, UIDisk):
            reset_obj = (self.tc.reset_layout(disk=self.ui_obj.doc_obj))[0]
        else:
            # reset the partition by removing the modified Partition, and
            # resetting it with the partition found during target discovery.

            discovered_obj = self.ui_obj.discovered_doc_obj

            desired_disk = get_desired_target_disk(doc)
            desired_part = get_solaris_partition(doc)

            desired_disk.delete_partition(desired_part)
            part_copy = deepcopy(discovered_obj)
            desired_disk.insert_children(part_copy)

            # get the updated reference
            reset_obj = get_solaris_partition(doc)

        dump_doc("After doing reset")

        self.set_disk_info(disk_info=reset_obj)
        self.activate_solaris_data()

    def activate_solaris_data(self):
        '''Find the Solaris Partition / ZFS Root Pool Slice and activate it.

        '''

        if self.editable:
            solaris_part = self.ui_obj.get_solaris_data()
            if solaris_part is None:
                LOGGER.debug("No Solaris data, activating default")
                self.activate_object()
                self.right_win.scroll(scroll_to_line=0)
                return
            disk_order = self.ui_obj.get_parts_in_use().index(solaris_part)
            LOGGER.debug("solaris disk at disk_order = %s", disk_order)
            self.activate_index(disk_order)

    def make_active(self):
        '''On activate, select the solaris partition or ZFS root pool,
        instead of defaulting to 0

        '''
        self.set_color(self.highlight_color)
        self.activate_solaris_data()

    def on_arrow_key(self, input_key):
        '''
        On curses.KEY_LEFT: Move from the right win to the left win
        On curses.KEY_RIGHT: Move from the left to the right

        '''
        if (input_key == curses.KEY_LEFT and
            self.get_active_object() is self.right_win and
            len(self.left_win.objects) > 0):

            active_object = self.right_win.get_active_object().area.y_loc
            if (active_object >= len(self.left_win.objects)):
                active_object = len(self.left_win.objects) - 1
            self.activate_object(self.left_win)
            self.left_win.activate_object(active_object)
            return None
        elif (input_key == curses.KEY_RIGHT and
              self.get_active_object() is self.left_win and
              len(self.right_win.objects) > 0):
            active_line = (self.left_win.active_object +
                             self.right_win.current_line[0])
            active_object = None
            force_to_top = False
            for obj in self.right_win.objects:
                if obj.area.y_loc >= active_line:
                    active_object = obj
                    off_screen = (self.right_win.current_line[0] +
                                  self.right_win.area.lines)
                    if active_object.area.y_loc > off_screen:
                        force_to_top = True
                    break
            if active_object is None:
                active_object = 0
            self.left_win.activate_object(-1, loop=True)
            self.activate_object(self.right_win)
            self.right_win.activate_object_force(active_object,
                                                 force_to_top=force_to_top)
            return None
        return input_key

    def no_ut_refresh(self, abs_y=None, abs_x=None):
        '''Refresh self, left win and right win explicitly'''
        super(DiskWindow, self).no_ut_refresh()
        self.left_win.no_ut_refresh(abs_y, abs_x)
        self.right_win.no_ut_refresh(abs_y, abs_x)

    def change_type(self, dummy):
        '''Cycle the type for the currently active object, and
        update its field

        '''
        LOGGER.debug("changing type")

        part_field = self.get_active_object().get_active_object()
        part_info = part_field.data_obj

        part_order = self.ui_obj.get_parts_in_use().index(part_info)

        old_obj = part_info.discovered_doc_obj
        old_type = list()
        if old_obj is not None:
            if self.has_partition_data:
                old_type.append(old_obj.part_type)
            else:
                if old_obj.in_zpool is not None:
                    old_type.append(old_obj.in_zpool)
                else:
                    in_use = part_info.doc_obj.in_use
                    if in_use is not None:
                        if in_use['used_name']:
                            old_type.append((in_use['used_name'])[0])

        LOGGER.debug("extra type to cycle: %s", old_type)
        part_info.cycle_type(extra_type=old_type)
        self.set_disk_info(ui_obj=self.ui_obj, no_part_ok=True)
        self.activate_index(part_order)

        return None

    def create_extended(self, ext_part_field):
        '''If this is the original extended partition, restore the original
        logical partitions. Otherwise, create a single unused logical
        partition.

        '''
        if not ext_part_field.data_obj.modified():
            self.right_win.clear()
            self.orig_logicals_active = True
            logicals = deepcopy(self._orig_data.get_logicals())
            self.disk_info.partitions.extend(logicals)
            for idx, logical in enumerate(logicals):
                self.list_area.y_loc = idx
                self.create_list_item(logical, self.right_win, self.list_area)
            if self.right_win not in self.objects:
                self.objects.append(self.right_win)
            self.right_win.activate_object_force(0, force_to_top=True)
            self.right_win.make_inactive()
            self.right_win.no_ut_refresh()
        else:
            # Leave old data be, create new Unused logical partition
            if self.right_win not in self.objects:
                self.objects.append(self.right_win)
            self.append_unused_logical()

    def activate_index(self, obj_index):
        '''Activate the object at the specified index '''

        if obj_index < len(self.left_win.objects):
            LOGGER.debug("activating in left_win")
            self.left_win.activate_object(obj_index)
            self.activate_object(self.left_win)
            self.right_win.scroll(scroll_to_line=0)
        else:
            activate = obj_index - len(self.left_win.objects)
            LOGGER.debug('activating in right win')
            self.right_win.activate_object_force(activate, force_to_top=True)
            self.activate_object(self.right_win)
            left_active = self.left_win.get_active_object()
            if left_active is not None:
                left_active.make_inactive()

    def append_unused_logical(self):
        '''Adds a single Unused logical partition to the right window'''
        new_part = self.disk_info.append_unused_logical()
        self.list_area.y_loc = len(self.right_win.all_objects)
        bottom = self.list_area.y_loc - self.right_win.area.lines + 1
        self.right_win.bottom = max(0, bottom)
        self.create_list_item(new_part, self.right_win, self.list_area)
        scroll = len(self.right_win.all_objects) > self.right_win.area.lines
        self.right_win.use_vert_scroll_bar = scroll
        self.right_win.no_ut_refresh()
Пример #35
0
class FDiskPart(BaseScreen):
    '''Allow user to choose to use the whole disk, or move to the
    partition/slice edit screen.
    '''

    BOOT_TEXT = _("Boot")
    HEADER_FDISK = _("Fdisk Partitions: ")
    HEADER_PART_SLICE = _("Solaris Partition Slices")
    HEADER_SLICE = _("Solaris Slices: ")
    HEADER_TYPE_BOOTABLE = _(" %(type)s %(bootable)s")
    PARAGRAPH_FDISK = _("%(release)s can be installed on the whole "
                        "disk or a partition on the disk.") % RELEASE
    PARAGRAPH_PART_SLICE = _("%(release)s can be installed in the "
                             "whole fdisk partition or within a "
                             "slice in the partition") % RELEASE
    PARAGRAPH_SLICE = _("%(release)s can be installed on the whole"
                        " disk or a slice on the disk.") % RELEASE
    FOUND_PART = _("The following partitions were found on the disk.")
    PROPOSED_PART = _("A partition table was not found. The following is"
                      " proposed.")
    FOUND_SLICE = _("The following slices were found on the disk.")
    PROPOSED_SLICE = _("A VTOC label was not found. The following is "
                       "proposed.")
    USE_WHOLE_DISK = _("Use the whole disk")
    USE_WHOLE_PARTITION = _("Use the whole partition")
    USE_SLICE_IN_PART = _("Use a slice in the partition")
    USE_PART_IN_DISK = _("Use a partition of the disk")
    USE_SLICE_IN_DISK = _("Use a slice on the disk")

    SPARC_HELP = (TUI_HELP + "/%s/sparc_solaris_slices.txt",
                  _("Solaris Slices"))
    X86_PART_HELP = (TUI_HELP + "/%s/"
                     "x86_fdisk_partitions.txt",
                     _("Fdisk Partitions"))
    X86_SLICE_HELP = (TUI_HELP + "/%s/x86_fdisk_slices.txt",
                      _("Solaris Partition Slices"))

    def __init__(self, main_win, target_controller, x86_slice_mode=False):
        '''If x86_slice_mode == True, this screen presents options for using a
        whole partition, or a slice within the partition.
        Otherwise, it presents options for using the whole disk, or using a
        partition (x86) or slice (SPARC) within the disk
        '''

        super(FDiskPart, self).__init__(main_win)
        global LOGGER
        LOGGER = logging.getLogger(INSTALL_LOGGER_NAME)
        self.x86_slice_mode = x86_slice_mode
        self.is_x86 = True
        self.help_format = "  %s"
        if platform.processor() == "sparc":  # SPARC, slices on a disk
            self.is_x86 = False
            self.header_text = FDiskPart.HEADER_SLICE
            self.paragraph = FDiskPart.PARAGRAPH_SLICE
            self.found = FDiskPart.FOUND_SLICE
            self.proposed = FDiskPart.PROPOSED_SLICE
            self.use_whole = FDiskPart.USE_WHOLE_DISK
            self.use_part = FDiskPart.USE_SLICE_IN_DISK
            self.help_data = FDiskPart.SPARC_HELP
        elif self.x86_slice_mode:  # x86, slices within a partition
            self.instance = ".slice"
            self.header_text = FDiskPart.HEADER_PART_SLICE
            self.paragraph = FDiskPart.PARAGRAPH_PART_SLICE
            self.found = FDiskPart.FOUND_SLICE
            self.proposed = FDiskPart.PROPOSED_SLICE
            self.use_whole = FDiskPart.USE_WHOLE_PARTITION
            self.use_part = FDiskPart.USE_SLICE_IN_PART
            self.help_data = FDiskPart.X86_SLICE_HELP
            self.help_format = "    %s"
        else:  # x86, partitions on a disk
            self.header_text = FDiskPart.HEADER_FDISK
            self.paragraph = FDiskPart.PARAGRAPH_FDISK
            self.found = FDiskPart.FOUND_PART
            self.proposed = FDiskPart.PROPOSED_PART
            self.use_whole = FDiskPart.USE_WHOLE_DISK
            self.use_part = FDiskPart.USE_PART_IN_DISK
            self.help_data = FDiskPart.X86_PART_HELP
        self.disk_win = None
        self.partial_disk_item = None
        self.whole_disk_item = None
        self.disk = None
        self.tc = target_controller
        self.use_whole_segment = True

    def _show(self):
        '''Display partition data for selected disk, and present the two
        choices
        '''

        doc = InstallEngine.get_instance().doc

        disk = get_desired_target_disk(doc)
        if disk.label == "GPT":
            # this disk has GPT partitions, so skip this screen
            raise SkipException

        if self.x86_slice_mode:
            if disk.whole_disk:
                raise SkipException

            sol_partition = get_solaris_partition(doc)
            if sol_partition is None:
                # Must have a Solaris partition
                err_msg = "Critical error - no Solaris partition found"
                LOGGER.error(err_msg)
                raise ValueError(err_msg)

            LOGGER.debug("Working with the following partition:")
            LOGGER.debug(str(sol_partition))

            # See if there are any slices in the partition
            all_slices = sol_partition.get_children(class_type=Slice)

            if not all_slices:
                LOGGER.info("No previous slices found")

                # Setting the in_zpool flag to indicate the whole
                # partition should be used.  The needed underlying
                # slices will be created in the next step when
                # the in_zpool flag is detected.
                sol_partition.in_zpool = DEFAULT_ZPOOL_NAME

                raise SkipException

            LOGGER.debug("Preserved partition with existing slices, "
                         "presenting option to install into a slice")
            self.disk = sol_partition

        else:
            self.disk = get_desired_target_disk(doc)

            LOGGER.debug("Working with the following disk:")
            LOGGER.debug(str(self.disk))

            if self.disk.whole_disk:
                LOGGER.debug("disk.whole_disk=True, skip editing")
                raise SkipException

            if self.disk.is_boot_disk():
                bootable = FDiskPart.BOOT_TEXT
            else:
                bootable = u""
            disk_size_gb_str = locale.format("%.1f",
                self.disk.disk_prop.dev_size.get(Size.gb_units)) + LOCALIZED_GB

            type_bootable_str = FDiskPart.HEADER_TYPE_BOOTABLE % \
                {"type": self.disk.disk_prop.dev_type,
                 "bootable": bootable}

            header_text = self.header_text + disk_size_gb_str + \
                type_bootable_str
            self.main_win.set_header_text(header_text)

        y_loc = 1
        y_loc += self.center_win.add_paragraph(self.paragraph, start_y=y_loc)

        y_loc += 1
        if self.is_x86 and not self.x86_slice_mode:
            all_parts = self.disk.get_children(class_type=Partition)
        else:
            all_parts = self.disk.get_children(class_type=Slice)

        found_parts = bool(all_parts)

        if found_parts:
            next_line = self.found
        else:
            next_line = self.proposed
        y_loc += self.center_win.add_paragraph(next_line, start_y=y_loc)

        y_loc += 1
        disk_win_area = WindowArea(6, 70, y_loc, 0)
        self.disk_win = DiskWindow(disk_win_area, self.disk,
                                   target_controller=self.tc,
                                   window=self.center_win)
        y_loc += disk_win_area.lines

        y_loc += 3
        whole_disk_width = textwidth(self.use_whole) + 3
        cols = (self.win_size_x - whole_disk_width) / 2
        whole_disk_item_area = WindowArea(1, whole_disk_width, y_loc, cols)
        self.whole_disk_item = ListItem(whole_disk_item_area,
                                        window=self.center_win,
                                        text=self.use_whole,
                                        centered=True)

        y_loc += 1
        partial_width = textwidth(self.use_part) + 3
        cols = (self.win_size_x - partial_width) / 2
        partial_item_area = WindowArea(1, partial_width, y_loc, cols)
        self.partial_disk_item = ListItem(partial_item_area,
                                          window=self.center_win,
                                          text=self.use_part,
                                          centered=True)

        self.main_win.do_update()
        if self.use_whole_segment:
            self.center_win.activate_object(self.whole_disk_item)
        else:
            self.center_win.activate_object(self.partial_disk_item)

    def on_continue(self):
        '''Set the user's selection in the install target. If they chose
        to use the entire disk (or entire partition), define a single
        partition (or slice) to consume the whole disk (or partition)
        '''

        if self.center_win.get_active_object() is self.whole_disk_item:
            self.use_whole_segment = True
            if isinstance(self.disk, Disk):
                LOGGER.debug("Setting whole_disk for %s", self.disk)
                self.disk = self.tc.select_disk(self.disk,
                                                use_whole_disk=True)[0]
            else:
                # it's a partition, set the in_zpool attribute in the object
                # for now.  The next screen will fill in needed slices
                self.disk.in_zpool = DEFAULT_ZPOOL_NAME
        else:
            self.use_whole_segment = False
            if isinstance(self.disk, Disk):
                LOGGER.debug("Setting whole_disk to false for %s", self.disk)
                self.disk.whole_disk = False
            else:
                self.disk.in_zpool = None

        # set an attribute on the desired disk so we don't enter the
        # partition/slice edit screen.
        if isinstance(self.disk, Disk):
            self.disk.use_whole_segment = self.use_whole_segment

        dump_doc("At the end of fdisk_partitions.continue")
Пример #36
0
 def get_tz_summary(self):
     '''Return a string summary of the timezone selection'''
     timezone = self.sysconfig.system.tz_timezone
     return _("Time Zone: %s") % timezone