def get_disk_summary(self): '''Return a string summary of the disk selection''' disk = self.install_profile.disk solaris_data = disk.get_solaris_data() if isinstance(solaris_data, SliceInfo): slice_data = solaris_data part_data = None elif isinstance(solaris_data, PartitionInfo): part_data = solaris_data slice_data = part_data.get_solaris_data() else: part_data = None slice_data = None format_dict = {} disk_string = [_("Disk: %(disk-size).1fGB %(disk-type)s")] format_dict['disk-size'] = disk.size.size_as("gb") format_dict['disk-type'] = disk.type if part_data is not None: disk_string.append(_("Partition: %(part-size).1fGB %(part-type)s")) format_dict['part-size'] = part_data.size.size_as("gb") format_dict['part-type'] = part_data.get_description() if not slice_data is None: if part_data is None or not part_data.use_whole_segment: disk_string.append(_("Slice %(slice-num)i: %(slice-size).1fGB" " %(pool)s")) format_dict['slice-num'] = slice_data.number format_dict['slice-size'] = slice_data.size.size_as("gb") format_dict['pool'] = slice_data.type[1] return "\n".join(disk_string) % format_dict
def validate(self): '''Ensure the Solaris partition or ZFS Root exists and is large enough ''' disk_info = self.disk_win.disk_info if self.is_x86 and not self.x86_slice_mode: min_size_text = _("The Solaris2 partition must be at least" " %(size).1fGB") missing_part = _("There must be exactly one Solaris2 partition.") else: min_size_text = _("The size of %(pool)s must be at least" " %(size).1fGB") missing_part = _("There must be one ZFS root pool, '%(pool)s.'") min_size = round(get_minimum_size().size_as("gb"), 1) format_dict = {'pool': SliceInfo.DEFAULT_POOL, 'size': min_size} try: part = disk_info.get_solaris_data(check_multiples=True) except ValueError: part = None if part is None: raise UIMessage(missing_part % format_dict) # 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) part_size = round(part.size.size_as("gb"), 1) if part_size < min_size: raise UIMessage(min_size_text % format_dict)
def build_summary(self): '''Build a textual summary from the install_profile''' lines = [] lines.append(_("Software: %s") % self.get_release()) lines.append("") lines.append(self.get_zfs_summary()) 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.install_profile.system.locale is None: self.install_profile.system.determine_locale() lines.append( _(" Default language: %s") % self.install_profile.system.actual_lang) lines.append("") lines.append(_("Users:")) lines.extend(self.get_users()) lines.append("") lines.append(_("Network:")) lines.extend(self.get_networks()) return "\n".join(lines)
def validate(self): '''Ensure the Solaris partition or ZFS Root exists and is large enough ''' disk_info = self.disk_win.disk_info if self.is_x86 and not self.x86_slice_mode: min_size_text = _("The Solaris2 partition must be at least" " %(size).1fGB") missing_part = _("There must be exactly one Solaris2 partition.") else: min_size_text = _("The size of %(pool)s must be at least" " %(size).1fGB") missing_part = _("There must be one ZFS root pool, '%(pool)s.'") min_size = round(get_minimum_size().size_as("gb"), 1) format_dict = {'pool' : SliceInfo.DEFAULT_POOL, 'size': min_size} try: part = disk_info.get_solaris_data(check_multiples=True) except ValueError: part = None if part is None: raise UIMessage(missing_part % format_dict) # 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) part_size = round(part.size.size_as("gb"), 1) if part_size < min_size: raise UIMessage(min_size_text % format_dict)
def build_summary(self): '''Build a textual summary from the install_profile''' lines = [] lines.append(_("Software: %s") % self.get_release()) lines.append("") lines.append(self.get_zfs_summary()) 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.install_profile.system.locale is None: self.install_profile.system.determine_locale() lines.append( _(" Default language: %s") % self.install_profile.system.actual_lang) lines.append("") lines.append(_("Users:")) if SummaryScreen.SONICLE_USER: lines.append( " privileged user sonicle created automatically (password sonicle)" ) else: lines.extend(self.get_users()) lines.append("") lines.append(_("Network:")) lines.extend(self.get_networks()) return "\n".join(lines)
def display_help(self): '''Display the single file help screen''' # customize header help_header = "%s: %s" logging.debug("self.screen is =%s", self.screen) if self.screen in self.help_dict: help_header = help_header % (_("Help"), self.help_dict[self.screen][1]) help_text = self.get_help_text(self.help_dict[self.screen][0]) else: help_header = help_header % (_("Help"), _("Not Available")) help_text = _("Help for this screen is not available") self.main_win.set_header_text(help_header) help_text = convert_paragraph(help_text, self.win_size_x - 5) logging.debug("help_text #lines=%d, text is \n%s", len(help_text), help_text) area = WindowArea(x_loc=0, y_loc=1, scrollable_lines=(len(help_text) + 1)) area.lines = self.win_size_y - 1 area.columns = self.win_size_x self.scroll_region = ScrollWindow(area, window=self.center_win) self.scroll_region.add_paragraph(help_text, start_x=(area.x_loc + 3)) self.center_win.activate_object(self.scroll_region)
def validate(self): '''Verify each of the edit fields''' year_value = self.year_edit.get_text() month_value = self.month_edit.get_text() day_value = self.day_edit.get_text() hour_value = self.hour_edit.get_text() minute_value = self.minute_edit.get_text() logging.debug("year_value=%s", year_value) logging.debug("month_value=%s", month_value) logging.debug("day_value=%s", day_value) logging.debug("hour_value=%s", hour_value) logging.debug("minute_value=%s", minute_value) had_err = False if not self.year_edit.run_on_exit(): had_err = True if not self.month_edit.run_on_exit(): had_err = True if not self.day_edit.run_on_exit(): had_err = True if not self.hour_edit.run_on_exit(): had_err = True if not self.minute_edit.run_on_exit(): had_err = True if had_err: raise UIMessage, _("Invalid date/time. See errors above.")
def hour_on_exit(hour_edit): '''Check hour when exiting field''' hour_str = hour_edit.get_text() logging.debug("hour_on_exit, =%s=", hour_str) if not hour_str: raise UIMessage, _("Hour out of range") hour_valid(hour_edit) return True
def username_valid(edit_field): '''Ensure the username is not "root" or "jack"''' user_str = edit_field.get_text() if user_str == "root": raise UIMessage, _("'root' cannot be used") elif user_str == "jack": raise UIMessage, _("'jack' cannot be used") return True
def minute_on_exit(minute_edit): '''Check minute when exiting field''' minute_str = minute_edit.get_text() logging.debug("minute_on_exit, =%s=", minute_str) if not minute_str: raise UIMessage, _("Minute out of range") minute_valid(minute_edit) return True
def pass_match(edit_field, linked_win=None): '''Make sure passwords match''' confirm_pass = edit_field.get_text() if linked_win is None or linked_win.get_text() == confirm_pass: return True else: edit_field.clear_text() linked_win.clear_text() raise UIMessage, _("Passwords don't match")
def day_on_exit(day_edit): '''Check day when exiting field''' day_str = day_edit.get_text() logging.debug("day_on_exit, =%s=", day_str) day_valid(day_edit) if (len(day_str) == 0 or int(day_str) == 0): logging.debug("on exit day out of range=%s", day_str) raise UIMessage, _("Day out of range") return True
def month_on_exit(month_edit): '''Check month when exiting field''' month_str = month_edit.get_text() logging.debug("month_on_exit, =%s=", month_str) month_valid(month_edit) if (len(month_str) == 0 or int(month_str) == 0): logging.debug("on exit month out of range=%s", month_str) raise UIMessage, _("Month out of range") return True
def exit_text_installer(logname=None, errcode=0): '''Close out the logger and exit with errcode''' logging.info("**** END ****") logging.shutdown() if logname is not None: print _("Exiting Text Installer. Log is available at:\n%s") % logname if isinstance(errcode, unicode): errcode = errcode.encode(get_encoding()) sys.exit(errcode)
def minute_valid(minute_edit): '''Check validity of minute as each char entered''' minute_str = minute_edit.get_text() logging.log(LOG_LEVEL_INPUT, "validating minute, text=%s=", minute_str) if minute_str and not minute_str.isdigit(): raise UIMessage, _("Minute must be numeric") if len(minute_str) >= 2 and (int(minute_str) > 59): raise UIMessage, _("Minute out of range") return True
def year_on_exit(year_edit): '''Check year when exiting field''' year_str = year_edit.get_text() logging.debug("year_on_exit, =%s=", year_str) year_valid(year_edit) if (len(year_str) != 4): logging.debug("on exit year out of range=%s", input) raise UIMessage, _("Year out of range") return True
def hour_valid(hour_edit): '''Check validity of hour as each char entered''' hour_str = hour_edit.get_text() logging.log(LOG_LEVEL_INPUT, "validating hour, text=%s=", hour_str) if hour_str and not hour_str.isdigit(): raise UIMessage, _("Hour must be numeric") if len(hour_str) >= 2 and (int(hour_str) > 23): logging.debug("hour out of range, =%s", hour_str) raise UIMessage, _("Hour out of range") return True
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 = "- " 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) zpool_install_action = Action(curses.KEY_F5, _("InstallToExistingPool"), self.zpool_install) self.main_win.actions[zpool_install_action.key] = zpool_install_action def zpool_install(self, dummy): '''We write down install_profile.install_to_pool flag and show the next screen. ''' self.install_profile.install_to_pool = True return self.main_win.screen_list.get_next(self) def _show(self): '''Display the static paragraph WELCOME_TEXT''' log_file = self.install_profile.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) # If user returned back to this screen, forget about hitting F5 if self.install_profile.install_to_pool: self.install_profile.install_to_pool = False self.install_profile.pool_name = None self.install_profile.be_name = InstallProfile.DEFAULT_BE_NAME
def get_networks(self): '''Build a summary of the networks in the install_profile, returned as a list of strings ''' network_summary = [] network_summary.append( _(" Computer name: %s") % self.install_profile.system.hostname) nic = self.install_profile.nic if nic.type == NetworkInfo.AUTOMATIC: network_summary.append(_(" Network Configuration: Automatic")) elif nic.type == NetworkInfo.NONE: network_summary.append(_(" Network Configuration: None")) else: network_summary.append( _(" Manual Configuration: %s") % nic.nic_name) network_summary.append(_(" IP Address: %s") % nic.ip_address) network_summary.append(_(" Netmask: %s") % nic.netmask) if nic.gateway: network_summary.append(_(" Gateway: %s") % nic.gateway) if nic.dns_address: network_summary.append(_(" DNS: %s") % nic.dns_address) if nic.domain: network_summary.append(_(" Domain: %s") % nic.domain) return network_summary
def __init__(self, main_win, screen=None): super(TimeZone, self).__init__(main_win) self.sys_info = None if screen is None: self.screen = TimeZone.TIMEZONE else: self.screen = screen self.tz_tuples = None self.tz_list = None self.cur_timezone_idx = 0 self.cur_timezone_parent = None self.last_timezone_parent = None self.cur_continent = None self.cur_country = None self.scroll_region = None self.last_country = None self.last_continent = None if self.screen == TimeZone.TIMEZONE: self.header_text = _("Time Zone") self.intro = _("Select your time zone.") self.title = _("Time Zones") elif self.screen == TimeZone.LOCATIONS: self.header_text = _("Time Zone: Locations") self.intro = _("Select the location that contains your time zone.") self.title = _("Locations") else: self.header_text = _("Time Zone: Regions") self.intro = _("Select the region that contains your time zone.") self.title = _("Regions")
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 if self.has_partition_data: max_parts = PartitionInfo.MAX_STANDARD_PARTITIONS else: max_parts = min(len(self.disk_info.slices), self.left_win.area.lines) win = self.left_win y_loc = 0 for next_part in self.disk_info.get_parts(): if y_loc >= max_parts: if win is self.left_win: win = self.right_win y_loc = 0 max_parts = win.area.lines else: if self.has_partition_data: num_extra = len(self.disk_info.partitions) - part_index more_parts_txt = _("%d more partitions") % num_extra else: num_extra = len(self.disk_info.slices) - part_index 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_part.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_part.number), y_loc, x_loc, self.headers[field][0] - 1) x_loc += self.headers[field][0] field += 1 win.add_text("%*.1f" % (self.headers[field][0]-1, next_part.size.size_as("gb")), 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 get_zfs_summary(self): '''Return a string summary of the root pool configuration''' pool_name = SliceInfo.DEFAULT_POOL.data pool_type = self.install_profile.zpool_type if (pool_type): return _("ZFS Pool name: %(name)s, type: %(type)s") % { "name": pool_name, "type": pool_type } else: return _("ZFS Pool name: %s") % (pool_name)
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 username_valid_alphanum(edit_field): '''Ensure username is alphanumeric, and does not start with a digit''' user_str = edit_field.get_text() if not user_str: return True if user_str[0].isalpha(): if user_str.isalnum(): return True else: raise UIMessage, _("Username must be alphanumeric") else: raise UIMessage, _("First character must be a-zA-Z")
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 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() if text.endswith(" "): raise UIMessage(_('Only the digits 0-9 and "." are valid.')) vals = text.split(".") if len(vals) > 2: raise UIMessage(_('A number can only have one "."')) 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 "." are valid.')) 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(".") if not text: text = "0" new_size = DiskSpace(text + "gb") max_size = edit_field.data_obj.get_max_size(disk_win.disk_info) # 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.size_as("gb"), 1) max_size_rounded = round(max_size, 1) if new_size_rounded > max_size_rounded: raise UIMessage( _("The new size (%(size).1f) is greater than " "the available space (%(avail).1f)") % { "size": new_size_rounded, "avail": max_size_rounded }) size_diff = abs(new_size.size_as("gb") - edit_field.data_obj.orig_size) if size_diff > DiskWindow.SIZE_PRECISION: edit_field.data_obj.size = new_size edit_field.data_obj.adjust_offset(disk_win.disk_info) else: edit_field.data_obj.size = "%fgb" % edit_field.data_obj.orig_size disk_win.update_avail_space() disk_win.no_ut_refresh() part_field = disk_win.find_part_field(edit_field.data_obj)[1] disk_win.mark_if_destroyed(part_field) return True
def reset_actions(self): '''Reset the actions to the defaults, clearing any custom actions registered by individual screens ''' self.continue_action = Action(curses.KEY_F2, _("Continue"), self.screen_list.get_next) self.back_action = Action(curses.KEY_F3, _("Back"), self.screen_list.previous_screen) self.help_action = Action(curses.KEY_F6, _("Help"), self.screen_list.show_help) self.quit_action = Action(curses.KEY_F9, _("Quit"), self.screen_list.quit) self.set_default_actions()
def validate(self): '''Ensure hostname is set and a network type is chosen (unless no NICs are present) ''' hostname_text = self.hostname.get_text() if not hostname_text: raise UIMessage(_("A Hostname is required.")) if len(hostname_text) < 2: raise UIMessage(_("A Hostname must be at least two characters.")) if self.have_nic: item_key = self.center_win.get_active_object().item_key if item_key not in self.net_type_dict: raise UIMessage(_("Select the wired network configuration: " "Automatically or None."))
def get_users(self): '''Build a summary of the user information, and return it as a list of strings ''' root = self.install_profile.users[0] primary = self.install_profile.users[1] 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 validate(self): '''Verify the syntactical validity of the IP Address fields''' ip_fields = [self.ip_field, self.netmask_field, self.gateway_field, self.dns_field] for field in ip_fields: validate_ip(field) if not self.ip_field.get_text(): raise UIMessage(_("IP Address must not be empty")) if not self.netmask_field.get_text(): raise UIMessage(_("Netmask must not be empty")) if self.domain_field.get_text() and not self.dns_field.get_text(): raise UIMessage(_("DNS server required if Domain set"))
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_profile.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 the continue key for help screen and Help key for help topics screen. Redirect F2_Continue to display the selected topic, when at the topics list ''' logging.debug("in set_actions self.class_name=%s", self.__class__.__name__) # change F6 description self.main_win.help_action.text = HelpScreen.HELP_INDEX # change continue to call continue_action, rather than # normal continue. Though we stay on the same screen, # we simulate the continue here by changing the screen text. # help_continue = Action(curses.KEY_F2, _("Continue"), self.continue_action) self.main_win.actions[help_continue.key] = help_continue if (self.screen == self.__class__.__name__): # help topics screen self.main_win.actions.pop(self.main_win.help_action.key, None) else: # help screen self.main_win.actions.pop(self.main_win.continue_action.key, None)