def __init__(self, model, controller): self.model = model self.controller = controller self.dev_to_row = {} self.cur_netdevs = [] self.error = Text("", align='center') self.device_table = TablePile(self._build_model_inputs(), spacing=2, colspecs={ 0: ColSpec(rpad=1), 4: ColSpec(can_shrink=True, rpad=1), }) self._create_bond_btn = menu_btn(_("Create bond"), on_press=self._create_bond) bp = button_pile([self._create_bond_btn]) bp.align = 'left' self.listbox = ListBox([self.device_table] + [ bp, ]) self.bottom = Pile([ Text(""), self._build_buttons(), Text(""), ]) self.error_showing = False self.frame = Pile([('pack', Text("")), ('pack', Padding.center_79(Text(_(self.excerpt)))), ('pack', Text("")), Padding.center_90(self.listbox), ('pack', self.bottom)]) self.frame.set_focus(self.bottom) super().__init__(self.frame)
def __init__(self, app): self.app = app rows = [ TableRow([ Text(""), Text(_("DATE")), Text(_("KIND")), Text(_("STATUS")), Text(""), ]) ] self.report_to_row = {} self.app.error_reporter.load_reports() for report in self.app.error_reporter.reports: connect_signal(report, "changed", self._report_changed, report) r = self.report_to_row[report] = self.row_for_report(report) rows.append(r) self.table = TablePile(rows, colspecs={1: ColSpec(can_shrink=True)}) widgets = [ Text(_("Select an error report to view:")), Text(""), self.table, Text(""), button_pile([close_btn(self)]), ] super().__init__("", widgets, 2, 2)
def __init__(self): self.table = TablePile([], spacing=1) self.device_to_checkbox = {} self.device_to_selector = {} self.devices = {} # {device:active|spare} self.all_rows = [] self.no_selector_rows = [] self.supports_spares = True super().__init__(self.table)
def _create(self): # Create the widget for a nic. This consists of a Pile containing a # table, an info line and a blank line. The first row of the table is # the one that can be focused and has a menu for manipulating the nic, # the other rows summarize its address config. # [ name type notes ▸ ] \ # address info | <- table # more address info / # mac / vendor info / model info # <blank line> actions = [] for action in NetDevAction: meth = getattr(self.parent, '_action_' + action.name) opens_dialog = getattr(meth, 'opens_dialog', False) if action in self.dev_info.enabled_actions: actions.append( (action.str(), True, (action, meth), opens_dialog)) menu = ActionMenu(actions) connect_signal(menu, 'action', self.parent._action, self) trows = [ make_action_menu_row([ Text("["), Text(self.dev_info.name), Text(self.dev_info.type), Text(self._notes(), wrap='clip'), menu, Text("]"), ], menu) ] + self._address_rows() self.table = TablePile(trows, colspecs=self.parent.device_colspecs, spacing=2) self.table.bind(self.parent.heading_table) if self.dev_info.type == "vlan": info = _("VLAN {id} on interface {link}").format( id=self.dev_info.vlan.id, link=self.dev_info.vlan.link) elif self.dev_info.type == "bond": info = _("bond master for {interfaces}").format( interfaces=', '.join(self.dev_info.bond.interfaces)) else: info = " / ".join([ self.dev_info.hwaddr, self.dev_info.vendor, self.dev_info.model, ]) return Pile([ ('pack', self.table), ('pack', Color.info_minor(Text(" " + info))), ('pack', Text("")), ])
def _device_widget(self, dev, netdev_i=None): # Create the widget for a nic. This consists of a Pile containing a # table, an info line and a blank line. The first row of the table is # the one that can be focused and has a menu for manipulating the nic, # the other rows summarize its address config. # [ name type notes ▸ ] \ # address info | <- table # more address info / # mac / vendor info / model info # <blank line> if netdev_i is None: netdev_i = len(self.cur_netdevs) self.cur_netdevs[netdev_i:netdev_i] = [dev] actions = [] for action in NetDevAction: meth = getattr(self, '_action_' + action.name) opens_dialog = getattr(meth, 'opens_dialog', False) if dev.supports_action(action): actions.append( (_(action.value), True, (action, meth), opens_dialog)) menu = ActionMenu(actions) connect_signal(menu, 'action', self._action, dev) trows = [ make_action_menu_row([ Text("["), Text(dev.name), Text(dev.type), Text(self._notes_for_device(dev), wrap='clip'), menu, Text("]"), ], menu) ] + self._address_rows_for_device(dev) table = TablePile(trows, colspecs=self.device_colspecs, spacing=2) self.dev_to_table[dev] = table table.bind(self.heading_table) if dev.type == "vlan": info = _("VLAN {id} on interface {link}").format(**dev.config) elif dev.type == "bond": info = _("bond master for {interfaces}").format( interfaces=', '.join(dev.config['interfaces'])) else: info = " / ".join( [dev.info.hwaddr, dev.info.vendor, dev.info.model]) return Pile([ ('pack', table), ('pack', Color.info_minor(Text(" " + info))), ('pack', Text("")), ])
def __init__(self, parent): self.parent = parent self.table = TablePile([], spacing=2, colspecs={ 0: ColSpec(rpad=1), 1: ColSpec(can_shrink=True), 2: ColSpec(min_width=9), 4: ColSpec(rpad=1), 5: ColSpec(rpad=1), }) self._no_mounts_content = Color.info_minor( Text(_("No disks or partitions mounted."))) super().__init__(self.table)
def __init__(self, controller, systems): self.controller = controller heading_table = TablePile([ TableRow([ Color.info_minor(Text(header)) for header in ["LABEL", "MODEL", "PUBLISHER", ""] ]) ], spacing=2) trows = [] systems = sorted( systems, key=lambda s: (s.brand.display_name, s.model.display_name, s.current, s.label)) for s in systems: actions = [] log.debug('actions: %s', s.actions) for act in sorted(s.actions, key=by_preferred_action_type): actions.append( Action(label=act.title.capitalize(), value=act, enabled=True)) menu = ActionMenu(actions) connect_signal(menu, 'action', self._system_action, s) srow = make_action_menu_row([ Text(s.label), Text(s.model.display_name), Text(s.brand.display_name), Text("(installed)" if s.current else ""), menu, ], menu) trows.append(srow) systems_table = TablePile(trows, spacing=2) systems_table.bind(heading_table) rows = [ Pile([heading_table, systems_table]), ] buttons = [] if controller.model.current is not None: # back to options of current system buttons.append(back_btn("BACK", on_press=self.back)) super().__init__( controller.model.current, screen(rows=rows, buttons=button_pile(buttons), focus_buttons=False, excerpt=self.excerpt))
def __init__(self, parent): self.parent = parent self.table = TablePile( [], spacing=2, colspecs={ 0: ColSpec(rpad=2), 1: ColSpec(rpad=2), 2: ColSpec(rpad=2), 3: ColSpec(rpad=2), }) self._no_zdev_content = Color.info_minor( Text(_("No zdev devices found."))) super().__init__(self.table)
def __init__(self, parent, show_available): self.parent = parent self.show_available = show_available self.table = TablePile([], spacing=2, colspecs={ 0: ColSpec(rpad=1), 2: ColSpec(can_shrink=True), 4: ColSpec(min_width=9), 5: ColSpec(rpad=1), }) if show_available: text = _("No available devices") else: text = _("No used devices") self._no_devices_content = Color.info_minor(Text(text)) super().__init__(self.table)
def __init__(self, app): rows = [] keys = GLOBAL_KEYS if app.opts.run_on_serial: keys += SERIAL_GLOBAL_HELP_KEYS for key, text in keys: rows.append(TableRow([Text(_(key)), Text(_(text))])) if app.opts.dry_run: dro = _('(dry-run only)') for key, text in DRY_RUN_KEYS: rows.append(TableRow([Text(_(key)), Text(_(text) + ' ' + dro)])) table = TablePile(rows, spacing=2, colspecs={1: ColSpec(can_shrink=True)}) widgets = [ Pile([ ('pack', Text(rewrap(GLOBAL_KEY_HELP))), ('pack', Text("")), ('pack', table), ]), Text(""), button_pile([close_btn(app, self)]), ] super().__init__(_("Shortcut Keys"), widgets, 0, 2)
def _build_table(self): widget = self.widget if self.field.takes_default_style: widget = Color.string_input(widget) self.caption_text = Text(self.field.caption) self.under_text = Text(self.help) if self.field.caption_first: self.caption_text.align = 'right' first_row = [self.caption_text, _Validator(self, widget)] else: first_row = [ _Validator( self, UrwidPadding(widget, align='right', width=widget_width(widget))), self.caption_text, ] self._rows = [ Toggleable(TableRow(row)) for row in [ first_row, [Text(""), self.under_text], ] ] self._table = TablePile(self._rows, spacing=2, colspecs=form_colspecs)
def __init__(self, parent, obj): self.parent = parent self.obj = obj lines = [ Text( _("Do you really want to delete the {desc} {label}?").format( desc=obj.desc(), label=obj.label)), Text(""), ] stretchy_index = 0 fs = obj.fs() if fs is not None: m = fs.mount() if m is not None: lines.append( Text( _("It is formatted as {fstype} and mounted at " "{path}").format(fstype=fs.fstype, path=m.path))) else: lines.append( Text( _("It is formatted as {fstype} and not mounted."). format(fstype=fs.fstype))) elif hasattr(obj, 'partitions') and len(obj.partitions()) > 0: n = len(obj.partitions()) if obj.type == "lvm_volgroup": if n == 1: things = _("logical volume") else: things = _("logical volumes") else: if n == 1: things = _("partition") else: things = _("partitions") lines.append( Text( _("It contains {n} {things}:").format(n=n, things=things))) lines.append(Text("")) stretchy_index = len(lines) rows = [] for p, cells in summarize_device(obj): if p not in [None, obj]: rows.append(TableRow(cells)) lines.append(TablePile(rows)) else: lines.append(Text(_("It is not formatted or mounted."))) delete_btn = danger_btn(label=_("Delete"), on_press=self.confirm) widgets = lines + [ Text(""), button_pile([ delete_btn, other_btn(label=_("Cancel"), on_press=self.cancel), ]), ] super().__init__("", widgets, stretchy_index, len(lines) + 1)
def __init__(self, controller, netdev_infos): self.controller = controller self.dev_name_to_table = {} self.cur_netdev_names = [] self.error = Text("", align='center') self.device_colspecs = { 0: ColSpec(rpad=1), 3: ColSpec(min_width=15), 4: ColSpec(can_shrink=True, rpad=1), } self.heading_table = TablePile([ TableRow([ Color.info_minor(Text(header)) for header in [ "", "NAME", "TYPE", "NOTES", "", ] ]) ], spacing=2, colspecs=self.device_colspecs) self.device_pile = Pile([self.heading_table]) for dev_info in netdev_infos: self.new_link(dev_info) self._create_bond_btn = menu_btn(_("Create bond"), on_press=self._create_bond) bp = button_pile([self._create_bond_btn]) bp.align = 'left' rows = [ self.device_pile, bp, ] self.buttons = button_pile([ done_btn("TBD", on_press=self.done), # See _route_watcher back_btn(_("Back"), on_press=self.cancel), ]) self.bottom = Pile([ ('pack', self.buttons), ]) self.error_showing = False super().__init__( screen(rows=rows, buttons=self.bottom, focus_buttons=True, excerpt=_(self.excerpt)))
def _build_model_inputs(self): self.heading_table = TablePile([ TableRow([ Color.info_minor(Text(header)) for header in [ "", "NAME", "TYPE", "NOTES", "", ] ]) ], spacing=2, colspecs=self.device_colspecs) rows = [self.heading_table] for dev in self.model.get_all_netdevs(): rows.append(self._device_widget(dev)) return rows
def show_apply_spinner(self): s = Spinner(self.controller.loop) s.start() c = TablePile([ TableRow([ Text(_("Applying changes")), s, ]), ], align='center') self.bottom.contents[0:0] = [ (c, self.bottom.options()), (Text(""), self.bottom.options()), ]
def __init__(self, parent, show_available): self.parent = parent self.show_available = show_available self.table = TablePile( [], spacing=2, colspecs={ 0: ColSpec(rpad=1), 1: ColSpec(can_shrink=True), 2: ColSpec(min_width=9), 3: ColSpec(rpad=1), 4: ColSpec(rpad=1), }) if show_available: text = _("No available devices") else: text = _("No used devices") self._no_devices_content = Color.info_minor(Text(text)) super().__init__(self.table) self.refresh_model_inputs() # I don't really know why this is required: self.table._select_first_selectable()
def ssh_help_texts(ssh_info): texts = [_(SSH_HELP_PROLOGUE), ""] if ssh_info is not None: if len(ssh_info.ips) > 1: texts.append(_(SSH_HELP_MULTIPLE_ADDRESSES)) texts.append("") for ip in ssh_info.ips: texts.append(Text( "{}@{}".format(ssh_info.username, ip), align='center')) else: texts.append(_(SSH_HELP_ONE_ADDRESSES).format( username=ssh_info.username, ip=str(ssh_info.ips[0]))) texts.append("") if ssh_info.authorized_key_fingerprints: if len(ssh_info.authorized_key_fingerprints) == 1: key = ssh_info.authorized_key_fingerprints[0] texts.append(Text(_(SSH_HELP_EPILOGUE_ONE_KEY).format( keytype=key.keytype, fingerprint=key.fingerprint))) else: texts.append(_(SSH_HELP_EPILOGUE_MULTIPLE_KEYS)) texts.append("") rows = [] for key in ssh_info.authorized_key_fingerprints: rows.append( TableRow([Text(key.keytype), Text(key.fingerprint)])) texts.append(TablePile(rows)) if ssh_info.password_kind == PasswordKind.KNOWN: texts.append("") texts.append(SSH_HELP_EPILOGUE_KNOWN_PASS_KEYS.format( password=ssh_info.password)) elif ssh_info.password_kind == PasswordKind.UNKNOWN: texts.append("") texts.append(SSH_HELP_EPILOGUE_UNKNOWN_PASS_KEYS) else: if ssh_info.password_kind == PasswordKind.KNOWN: texts.append(SSH_HELP_EPILOGUE_KNOWN_PASS_NO_KEYS.format( password=ssh_info.password)) elif ssh_info.password_kind == PasswordKind.UNKNOWN: texts.append(SSH_HELP_EPILOGUE_UNKNOWN_PASS_NO_KEYS) texts.append("") texts.append(Text(summarize_host_keys([ (key.keytype, key.fingerprint) for key in ssh_info.host_key_fingerprints ]))) else: texts.append("") texts.append(_(SSH_HELP_NO_ADDRESSES)) return texts
def __init__(self, model, controller): self.model = model self.controller = controller self.dev_to_row = {} self.cur_netdevs = [] self.error = Text("", align='center') self.device_table = TablePile( self._build_model_inputs(), spacing=2, colspecs={ 0: ColSpec(rpad=1), 4: ColSpec(can_shrink=True, rpad=1), }) self._create_bond_btn = menu_btn( _("Create bond"), on_press=self._create_bond) bp = button_pile([self._create_bond_btn]) bp.align = 'left' rows = [ self.device_table, bp, ] buttons = button_pile([ done_btn(_("Done"), on_press=self.done), back_btn(_("Back"), on_press=self.cancel), ]) self.bottom = Pile([ ('pack', buttons), ]) self.error_showing = False super().__init__(screen( rows=rows, buttons=self.bottom, focus_buttons=True, excerpt=self.excerpt))
def __init__(self, parent, disk): self.parent = parent dinfo = disk.info_for_display() rows = [] for label, key in labels_keys: v = str(dinfo[key]) rows.append(TableRow([Text(label, align='right'), Text(v)])) widgets = [ TablePile(rows, colspecs={1: ColSpec(can_shrink=True)}), Text(""), button_pile([done_btn(_("Close"), on_press=self.close)]), ] title = _("Info for {}").format(disk.label) super().__init__(title, widgets, 0, 2)
def __init__(self, parent): super().__init__(parent) options = [] tables = [] for disk in parent.model.all_disks(): for obj, cells in summarize_device(disk): table = TablePile([TableRow(cells)]) tables.append(table) options.append(Option((table, obj is disk, obj))) t0 = tables[0] for t in tables[1:]: t0.bind(t) self.disk.widget.options = options self.disk.widget.index = 0 connect_signal(self.use_lvm.widget, 'change', self._toggle) self.lvm_options.enabled = self.use_lvm.value
def _build_table(self): widget = self.widget if self.field.takes_default_style: widget = Color.string_input(widget) self.caption_text = Text(self.field.caption, align="right") self.under_text = Text(self.help) self._rows = [ Toggleable(TableRow(row)) for row in [ [self.caption_text, _Validator(self, widget)], [Text(""), self.under_text], ] ] self._table = TablePile(self._rows, spacing=2, colspecs=form_colspecs)
def _guidance(self): todos = [] if not self.model.is_root_mounted(): todos.append(_("Mount a filesystem at /")) if self.model.needs_bootloader_partition(): todos.append(_("Select a boot disk")) if not todos: return None rows = [ TableRow([ Text(_("To continue you need to:")), Text(todos[0]), ]), ] for todo in todos[1:]: rows.append(TableRow([Text(""), Text(todo)])) rows.append(TableRow([Text("")])) return TablePile(rows)
def __init__(self, app, parent): rows = [] for key, text in GLOBAL_KEYS: rows.append(TableRow([Text(_(key)), Text(_(text))])) if app.opts.dry_run: for key, text in DRY_RUN_KEYS: rows.append(TableRow([Text(_(key)), Text(_(text))])) table = TablePile( rows, spacing=2, colspecs={1: ColSpec(can_shrink=True)}) widgets = [ Pile([ ('pack', Text(rewrap(GLOBAL_KEY_HELP))), ('pack', Text("")), ('pack', table), ]), Text(""), button_pile([close_btn(parent)]), ] super().__init__(_("Shortcut Keys"), widgets, 0, 2)
def __init__(self, parent): super().__init__(parent) options = [] tables = [] initial = -1 for disk in parent.model.all_disks(): for obj, cells in summarize_device(disk): table = TablePile([TableRow(cells)]) tables.append(table) enabled = False if obj is disk and disk.size > 6 * (2**30): enabled = True if initial < 0: initial = len(options) options.append(Option((table, enabled, obj))) t0 = tables[0] for t in tables[1:]: t0.bind(t) self.disk.widget.options = options self.disk.widget.index = initial connect_signal(self.use_lvm.widget, 'change', self._toggle) self.lvm_options.enabled = self.use_lvm.value
def _build_table(self): widget = self.widget if self.field.takes_default_style: widget = Color.string_input(widget) if self.help is not NO_HELP: self.under_text = Text(self.help) else: self.under_text = Text("") if self.field.caption is NO_CAPTION: first_row = [(2, _Validator(self, widget))] second_row = [(2, self.under_text)] else: self.caption_text = Text(_(self.field.caption)) if self.field.caption_first: self.caption_text.align = 'right' first_row = [self.caption_text, _Validator(self, widget)] else: first_row = [ _Validator( self, UrwidPadding(widget, align='right', width=widget_width(widget))), self.caption_text, ] second_row = [Text(""), self.under_text] rows = [first_row] if self.help is not NO_HELP: rows.append(second_row) self._rows = [Toggleable(TableRow(row)) for row in rows] self._table = TablePile(self._rows, spacing=2, colspecs=form_colspecs)
class NetworkView(BaseView): title = _("Network connections") excerpt = _("Configure at least one interface this server can use to talk " "to other machines, and which preferably provides sufficient " "access for updates.") footer = _("Select an interface to configure it or select Done to " "continue") def __init__(self, model, controller): self.model = model self.controller = controller self.dev_to_row = {} self.cur_netdevs = [] self.error = Text("", align='center') self.device_table = TablePile(self._build_model_inputs(), spacing=2, colspecs={ 0: ColSpec(rpad=1), 4: ColSpec(can_shrink=True, rpad=1), }) self._create_bond_btn = menu_btn(_("Create bond"), on_press=self._create_bond) bp = button_pile([self._create_bond_btn]) bp.align = 'left' self.listbox = ListBox([self.device_table] + [ bp, ]) self.bottom = Pile([ Text(""), self._build_buttons(), Text(""), ]) self.error_showing = False self.frame = Pile([('pack', Text("")), ('pack', Padding.center_79(Text(_(self.excerpt)))), ('pack', Text("")), Padding.center_90(self.listbox), ('pack', self.bottom)]) self.frame.set_focus(self.bottom) super().__init__(self.frame) def _build_buttons(self): back = back_btn(_("Back"), on_press=self.cancel) done = done_btn(_("Done"), on_press=self.done) return button_pile([done, back]) _action_INFO = _stretchy_shower(ViewInterfaceInfo) _action_EDIT_WLAN = _stretchy_shower(NetworkConfigureWLANStretchy) _action_EDIT_IPV4 = _stretchy_shower(EditNetworkStretchy, 4) _action_EDIT_IPV6 = _stretchy_shower(EditNetworkStretchy, 6) _action_EDIT_BOND = _stretchy_shower(BondStretchy) _action_ADD_VLAN = _stretchy_shower(AddVlanStretchy) def _action_DELETE(self, device): self.controller.rm_virtual_interface(device) def _action(self, sender, action, device): action, meth = action log.debug("_action %s %s", action.name, device.name) meth(device) def _cells_for_device(self, dev): notes = [] if dev.is_bond_slave: notes.append( _("enslaved to {}").format(dev._net_info.bond['master'])) for v in 4, 6: if dev.configured_ip_addresses_for_version(v): notes.extend([ "{} (static)".format(a) for a in dev.configured_ip_addresses_for_version(v) ]) elif dev.dhcp_for_version(v): if v == 4: fam = AF_INET elif v == 6: fam = AF_INET6 fam_addresses = [] for a in dev._net_info.addresses.values(): log.debug("a %s", a.serialize()) if a.family == fam and a.source == 'dhcp': fam_addresses.append("{} (from dhcp)".format( a.address)) if fam_addresses: notes.extend(fam_addresses) else: notes.append( _("DHCPv{v} has supplied no addresses").format(v=v)) if notes: notes = ", ".join(notes) else: notes = '-' return (dev.name, dev.type, notes) def new_link(self, new_dev): for i, cur_dev in enumerate(self.cur_netdevs): if cur_dev.name > new_dev.name: netdev_i = i break else: netdev_i = len(self.cur_netdevs) new_rows = self._rows_for_device(new_dev, netdev_i) self.device_table.insert_rows(3 * netdev_i + 1, new_rows) def update_link(self, dev): row = self.dev_to_row[dev] for i, text in enumerate(self._cells_for_device(dev)): row.columns[2 * (i + 1)].set_text(text) def del_link(self, dev): log.debug("del_link %s", (dev in self.cur_netdevs)) if dev in self.cur_netdevs: netdev_i = self.cur_netdevs.index(dev) self.device_table.remove_rows(3 * netdev_i, 3 * (netdev_i + 1)) del self.cur_netdevs[netdev_i] if isinstance(self._w, StretchyOverlay): stretchy = self._w.stretchy if getattr(stretchy, 'device', None) is dev: self.remove_overlay() def _rows_for_device(self, dev, netdev_i=None): if netdev_i is None: netdev_i = len(self.cur_netdevs) rows = [] name, typ, addresses = self._cells_for_device(dev) actions = [] for action in NetDevAction: meth = getattr(self, '_action_' + action.name) opens_dialog = getattr(meth, 'opens_dialog', False) if dev.supports_action(action): actions.append( (_(action.value), True, (action, meth), opens_dialog)) menu = ActionMenu(actions) connect_signal(menu, 'action', self._action, dev) row = make_action_menu_row([ Text("["), Text(name), Text(typ), Text(addresses, wrap='clip'), menu, Text("]"), ], menu) self.dev_to_row[dev] = row.base_widget self.cur_netdevs[netdev_i:netdev_i] = [dev] rows.append(row) if dev.type == "vlan": info = _("VLAN {id} on interface {link}").format( **dev._configuration) elif dev.type == "bond": info = _("bond master for {}").format(', '.join( dev._net_info.bond['slaves'])) else: info = " / ".join([dev.hwaddr, dev.vendor, dev.model]) rows.append( Color.info_minor(TableRow([Text(""), (4, Text(info)), Text("")]))) rows.append(Color.info_minor(TableRow([(4, Text(""))]))) return rows def _build_model_inputs(self): netdevs = self.model.get_all_netdevs() masters = [] for master in netdevs: if not master._net_info.bond['is_master']: continue masters.append((_("Set master to %s") % master.name, True, { 'action': 'add_master', 'master': master }, False)) rows = [] rows.append( TableRow([ Color.info_minor(Text(header)) for header in ["", "NAME", "TYPE", "NOTES / ADDRESSES", ""] ])) for dev in netdevs: rows.extend(self._rows_for_device(dev)) return rows def _create_bond(self, sender): self.show_stretchy_overlay(BondStretchy(self)) def show_network_error(self, action, info=None): self.error_showing = True self.bottom.contents[0:0] = [ (Text(""), self.bottom.options()), (Color.info_error(self.error), self.bottom.options()), ] if action == 'stop-networkd': exc = info[0] self.error.set_text("Stopping systemd-networkd-failed: %r" % (exc.stderr, )) elif action == 'apply': self.error.set_text("Network configuration could not be applied; " "please verify your settings.") elif action == 'timeout': self.error.set_text("Network configuration timed out; " "please verify your settings.") elif action == 'down': self.error.set_text("Downing network interfaces failed.") elif action == 'canceled': self.error.set_text("Network configuration canceled.") elif action == 'add-vlan': self.error.set_text("Failed to add a VLAN tag.") elif action == 'rm-dev': self.error.set_text("Failed to delete a virtual interface.") else: self.error.set_text("An unexpected error has occurred; " "please verify your settings.") def done(self, result): if self.error_showing: self.bottom.contents[0:2] = [] self.controller.network_finish(self.model.render()) def cancel(self, button=None): self.controller.cancel()
class ErrorReportListStretchy(Stretchy): def __init__(self, app): self.app = app rows = [ TableRow([ Text(""), Text(_("DATE")), Text(_("KIND")), Text(_("STATUS")), Text(""), ])] self.report_to_row = {} for report in self.app.controllers.Error.reports: connect_signal(report, "changed", self._report_changed, report) r = self.report_to_row[report] = self.row_for_report(report) rows.append(r) self.table = TablePile(rows, colspecs={1: ColSpec(can_shrink=True)}) widgets = [ Text(_("Select an error report to view:")), Text(""), self.table, Text(""), button_pile([close_btn(self)]), ] super().__init__("", widgets, 2, 2) def open_report(self, sender, report): self.app.add_global_overlay( ErrorReportStretchy(self.app, report, False)) def state_for_report(self, report): if report.seen: return _("VIEWED") return _("UNVIEWED") def cells_for_report(self, report): date = report.pr.get("Date", "???") icon = ClickableIcon(date) connect_signal(icon, 'click', self.open_report, report) return [ Text("["), icon, Text(_(report.kind.value)), Text(_(self.state_for_report(report))), Text("]"), ] def row_for_report(self, report): return Color.menu_button( TableRow(self.cells_for_report(report))) def _report_changed(self, report): old_r = self.report_to_row.get(report) if old_r is None: return old_r = old_r.base_widget new_cells = self.cells_for_report(report) for (s1, old_c), new_c in zip(old_r.cells, new_cells): old_c.set_text(new_c.text) self.table.invalidate()
class MultiDeviceChooser(WidgetWrap, WantsToKnowFormField): signals = ['change'] def __init__(self): self.table = TablePile([], spacing=1) self.device_to_checkbox = {} self.device_to_selector = {} self.devices = {} # {device:active|spare} self.all_rows = [] self.no_selector_rows = [] self.supports_spares = True super().__init__(self.table) @property def value(self): return self.devices @value.setter def value(self, value): log.debug("MDC set value %s", {d.id: v for d, v in value.items()}) self.devices = value for d, s in self.device_to_selector.items(): if d in self.devices: s.enable() s.base_widget.value = self.devices[d] else: s.disable() for d, b in self.device_to_checkbox.items(): b.set_state(d in self.devices) @property def active_devices(self): return {device for device, status in self.devices.items() if status == 'active'} @property def spare_devices(self): return {device for device, status in self.devices.items() if status == 'spare'} def set_supports_spares(self, val): if val == self.supports_spares: return self.supports_spares = val if val: for device in list(self.devices): self.device_to_selector[device].enable() selector = self.device_to_selector[device] self.devices[device] = selector.base_widget.value self.table.set_contents(self.all_rows) else: for device in list(self.devices): self.device_to_selector[device].disable() self.devices[device] = 'active' self.table.set_contents(self.no_selector_rows) def _state_change_device(self, sender, state, device): if state: if self.supports_spares: self.device_to_selector[device].enable() selector = self.device_to_selector[device] self.devices[device] = selector.base_widget.value else: self.device_to_selector[device].disable() del self.devices[device] self._emit('change', self.devices) def _select_active_spare(self, sender, value, device): self.devices[device] = value self._emit('change', self.devices) def _summarize(self, prefix, device): if device.fs() is not None: fs = device.fs() text = prefix + _("formatted as {}").format(fs.fstype) if fs.mount(): text += _(", mounted at {}").format(fs.mount().path) else: text += _(", not mounted") else: text = prefix + _("unused {}").format(device.desc()) return TableRow([(2, Color.info_minor(Text(text)))]) def set_bound_form_field(self, bff): super().set_bound_form_field(bff) self.all_rows = [] for kind, device in bff.form.all_devices: if kind == LABEL: self.all_rows.append(TableRow([ Text(" " + device.label), Text(humanize_size(device.size), align='right') ])) self.no_selector_rows.append(self.all_rows[-1]) self.all_rows.append(TableRow([ (2, Color.info_minor(Text(" " + device.desc()))) ])) self.no_selector_rows.append(self.all_rows[-1]) else: if kind == DEVICE: label = device.label prefix = " " elif kind == PART: label = _(" partition {}").format(device._number) prefix = " " else: raise Exception("unexpected kind {}".format(kind)) box = CheckBox( label, on_state_change=self._state_change_device, user_data=device) self.device_to_checkbox[device] = box size = Text(humanize_size(device.size), align='right') self.all_rows.append(Color.menu_button(TableRow([box, size]))) self.no_selector_rows.append(self.all_rows[-1]) selector = Selector(['active', 'spare']) connect_signal( selector, 'select', self._select_active_spare, device) selector = Toggleable( UrwidPadding( Color.menu_button(selector), left=len(prefix))) selector.disable() self.device_to_selector[device] = selector self.all_rows.append(TableRow([(2, selector)])) # Do not append that one to no_selector_rows! self.all_rows.append(self._summarize(prefix, device)) self.no_selector_rows.append(self.all_rows[-1]) self.table.set_contents(self.all_rows) log.debug("%s", self.table._w.focus_position)
class DeviceList(WidgetWrap): def __init__(self, parent, show_available): self.parent = parent self.show_available = show_available self.table = TablePile( [], spacing=2, colspecs={ 0: ColSpec(rpad=1), 2: ColSpec(can_shrink=True), 4: ColSpec(min_width=9), 5: ColSpec(rpad=1), }) if show_available: text = _("No available devices") else: text = _("No used devices") self._no_devices_content = Color.info_minor(Text(text)) super().__init__(self.table) _disk_INFO = _stretchy_shower(DiskInfoStretchy) _disk_REFORMAT = _stretchy_shower(ConfirmReformatStretchy) _disk_PARTITION = _stretchy_shower(PartitionStretchy) _disk_FORMAT = _stretchy_shower(FormatEntireStretchy) def _disk_REMOVE(self, disk): cd = disk.constructed_device(skip_dm_crypt=False) if cd.type == "dm_crypt": self.parent.model.remove_dm_crypt(cd) disk, cd = cd, cd.constructed_device() if cd.type == "raid": if disk in cd.devices: cd.devices.remove(disk) else: cd.spare_devices.remove(disk) elif cd.type == "lvm_volgroup": cd.devices.remove(disk) else: 1 / 0 disk._constructed_device = None self.parent.refresh_model_inputs() def _disk_TOGGLE_BOOT(self, disk): if boot.is_boot_device(disk): self.parent.controller.remove_boot_disk(disk) else: self.parent.controller.add_boot_disk(disk) self.parent.refresh_model_inputs() _partition_EDIT = _stretchy_shower( lambda parent, part: PartitionStretchy(parent, part.device, part)) _partition_REMOVE = _disk_REMOVE _partition_DELETE = _stretchy_shower(ConfirmDeleteStretchy) _raid_EDIT = _stretchy_shower(RaidStretchy) _raid_PARTITION = _disk_PARTITION _raid_FORMAT = _disk_FORMAT _raid_REFORMAT = _disk_REFORMAT _raid_REMOVE = _disk_REMOVE _raid_DELETE = _partition_DELETE _lvm_volgroup_EDIT = _stretchy_shower(VolGroupStretchy) _lvm_volgroup_CREATE_LV = _disk_PARTITION _lvm_volgroup_DELETE = _partition_DELETE _lvm_partition_EDIT = _stretchy_shower( lambda parent, part: PartitionStretchy(parent, part.volgroup, part)) _lvm_partition_DELETE = _partition_DELETE def _action(self, sender, value, device): action, meth = value log.debug('_action %s %s', action, device.id) meth(device) def _label_REMOVE(self, action, device): cd = device.constructed_device() if cd: return _("Remove from {device}").format(device=labels.desc(cd)) else: return action.str() def _label_PARTITION(self, action, device): return _("Add {ptype} Partition").format( ptype=device.ptable_for_new_partition().upper()) def _label_TOGGLE_BOOT(self, action, device): if boot.is_boot_device(device): return _("Stop Using As Boot Device") else: if self.parent.controller.supports_resilient_boot: if boot.all_boot_devices(self.parent.model): return _("Add As Another Boot Device") return _("Use As Boot Device") def _action_menu_for_device(self, device): device_actions = [] for action in DeviceAction.supported(device): label_meth = getattr(self, '_label_{}'.format(action.name), lambda a, d: a.str()) label = label_meth(action, device) enabled, whynot = action.can(device) if whynot: assert not enabled enabled = True label += " *" meth = _whynot_shower(self.parent, action, whynot) else: meth_name = '_{}_{}'.format(device.type, action.name) meth = getattr(self, meth_name) if not whynot and action in [ DeviceAction.DELETE, DeviceAction.REFORMAT ]: label = Color.danger_button(ActionMenuOpenButton(label)) device_actions.append( Action(label=label, enabled=enabled, value=(action, meth), opens_dialog=getattr(meth, 'opens_dialog', False))) menu = ActionMenu(device_actions) connect_signal(menu, 'action', self._action, device) return menu def refresh_model_inputs(self): devices = [ d for d in self.parent.model.all_devices() if (d.available() == self.show_available or ( not self.show_available and d.has_unavailable_partition())) ] if len(devices) == 0: self._w = Padding.push_2(self._no_devices_content) self.table.table_rows = [] return self._w = self.table rows = [] rows.append( Color.info_minor( TableRow([ Text(""), (2, Text(_("DEVICE"))), Text(_("TYPE")), Text(_("SIZE"), align="center"), Text(""), Text(""), ]))) for device in devices: for obj, cells in summarize_device( device, lambda part: part.available() == self.show_available): if obj is not None: menu = self._action_menu_for_device(obj) else: menu = Text("") if obj is device: start, end = '[', ']' else: start, end = '', '' cells = [Text(start)] + cells + [menu, Text(end)] if obj is not None: rows.append(make_action_menu_row(cells, menu)) else: rows.append(TableRow(cells)) if (self.show_available and device.used > 0 and device.free_for_partitions > 0): free = humanize_size(device.free_for_partitions) rows.append( TableRow([ Text(""), (3, Color.info_minor(Text(_("free space")))), Text(free, align="right"), Text(""), Text(""), ])) rows.append(TableRow([Text("")])) self.table.set_contents(rows[:-1]) if self.table._w.focus_position >= len(rows): self.table._w.focus_position = len(rows) - 1 while not self.table._w.focus.selectable(): self.table._w.focus_position -= 1
class MountList(WidgetWrap): def __init__(self, parent): self.parent = parent self.table = TablePile( [], spacing=2, colspecs={ 0: ColSpec(rpad=1), 1: ColSpec(can_shrink=True), 2: ColSpec(min_width=9), 4: ColSpec(rpad=1), 5: ColSpec(rpad=1), }) self._no_mounts_content = Color.info_minor( Text(_("No disks or partitions mounted."))) super().__init__(self.table) def _mount_action(self, sender, action, mount): log.debug('_mount_action %s %s', action, mount) if action == 'unmount': self.parent.controller.delete_mount(mount) self.parent.refresh_model_inputs() def refresh_model_inputs(self): mountinfos = [ MountInfo(mount=m) for m in sorted(self.parent.model.all_mounts(), key=lambda m: (m.path == "", m.path)) ] if len(mountinfos) == 0: self.table.set_contents([]) self._w = Padding.push_2(self._no_mounts_content) return self._w = self.table rows = [ TableRow([ Color.info_minor(heading) for heading in [ Text(" "), Text(_("MOUNT POINT")), Text(_("SIZE"), align='center'), Text(_("TYPE")), Text(_("DEVICE TYPE")), Text(" "), Text(" "), ] ]) ] for i, mi in enumerate(mountinfos): path_markup = mi.path if path_markup == "": path_markup = "SWAP" else: for j in range(i - 1, -1, -1): mi2 = mountinfos[j] if mi.startswith(mi2): part1 = "/".join(mi.split_path[:len(mi2.split_path)]) part2 = "/".join([''] + mi.split_path[len(mi2.split_path):]) path_markup = [('info_minor', part1), part2] break if j == 0 and mi2.split_path == ['', '']: path_markup = [ ('info_minor', "/"), "/".join(mi.split_path[1:]), ] actions = [(_("Unmount"), mi.mount.can_delete(), 'unmount')] menu = ActionMenu(actions) connect_signal(menu, 'action', self._mount_action, mi.mount) cells = [ Text("["), Text(path_markup), Text(mi.size, align='right'), Text(mi.fstype), Text(mi.desc), menu, Text("]"), ] row = make_action_menu_row(cells, menu, attr_map='menu_button', focus_map={ None: 'menu_button focus', 'info_minor': 'menu_button focus', }) rows.append(row) self.table.set_contents(rows) if self.table._w.focus_position >= len(rows): self.table._w.focus_position = len(rows) - 1