code motion

This commit is contained in:
Michael Hudson-Doyle 2018-06-27 12:06:28 +12:00
parent 79a380f28f
commit 805ad5f495
2 changed files with 131 additions and 236 deletions

View File

@ -19,32 +19,25 @@ Provides network device listings and extended network information
""" """
import ipaddress
import logging import logging
import textwrap
from urwid import ( from urwid import (
connect_signal, connect_signal,
LineBox, LineBox,
ProgressBar, ProgressBar,
Text, Text,
WidgetPlaceholder,
) )
from urwid import Padding as uPadding
from subiquitycore.ui.actionmenu import ActionMenu from subiquitycore.ui.actionmenu import ActionMenu
from subiquitycore.ui.buttons import back_btn, cancel_btn, done_btn, menu_btn from subiquitycore.ui.buttons import back_btn, cancel_btn, done_btn
from subiquitycore.ui.container import ( from subiquitycore.ui.container import (
Columns,
ListBox, ListBox,
Pile, Pile,
WidgetWrap, WidgetWrap,
) )
from subiquitycore.ui.form import Form, StringField, Toggleable, ChoiceField
from subiquitycore.ui.selector import Option
from subiquitycore.ui.stretchy import Stretchy
from subiquitycore.ui.table import ColSpec, TablePile, TableRow from subiquitycore.ui.table import ColSpec, TablePile, TableRow
from subiquitycore.ui.utils import button_pile, Color, make_action_menu_row, Padding from subiquitycore.ui.utils import button_pile, Color, make_action_menu_row, Padding
from .network_configure_manual_interface import EditNetworkStretchy
from subiquitycore.view import BaseView from subiquitycore.view import BaseView
@ -134,124 +127,6 @@ def _build_gateway_ip_info_for_version(dev, version):
return [Text(_("IPv%s is not configured" % version))] return [Text(_("IPv%s is not configured" % version))]
from .network_configure_manual_interface import NetworkConfigForm
network_choices = {
4: [
(_("Automatic (DHCP)"), True, "dhcp"),
(_("Manual"), True, "manual"),
(_("Disabled"), True, "disable"),
],
6: [
(_("Automatic"), True, "accept-ra"),
(_("Automatic (DHCP)"), True, "dhcp"),
(_("Manual"), True, "manual"),
(_("Disabled"), True, "disable"),
],
}
class NetworkMethodForm(Form):
ok_label = _("Save")
method = ChoiceField("IPv{ip_version} Method: ", choices=network_choices[4])
class EditNetworkStretchy(Stretchy):
def __init__(self, parent, device, ip_version):
self.parent = parent
self.device = device
self.ip_version = ip_version
self.method_form = NetworkMethodForm()
self.method_form.method.caption = _("IPv{ip_version} Method: ").format(ip_version=ip_version)
manual_initial = {}
if len(device.configured_ip_addresses_for_version(ip_version)) > 0:
method = 'manual'
addr = ipaddress.ip_interface(
device.configured_ip_addresses_for_version(ip_version)[0])
manual_initial = {
'subnet': str(addr.network),
'address': str(addr.ip),
'nameservers': ', '.join(device.configured_nameservers),
'searchdomains': ', '.join(device.configured_searchdomains),
}
gw = device.configured_gateway_for_version(ip_version)
if gw:
manual_initial['gateway'] = str(gw)
elif self.device.dhcp_for_version(ip_version):
method = 'dhcp'
else:
method = 'disable'
self.method_form.method.value = method
self.method_form.method.widget.options = list(map(Option, network_choices[ip_version]))
connect_signal(self.method_form.method.widget, 'select', self._select_method)
log.debug("manual_initial %s", manual_initial)
self.manual_form = NetworkConfigForm(ip_version, manual_initial)
connect_signal(self.method_form, 'submit', self.done_method)
connect_signal(self.manual_form, 'submit', self.done_manual)
connect_signal(self.method_form, 'cancel', self.cancel)
connect_signal(self.manual_form, 'cancel', self.cancel)
self.form_pile = Pile(self.method_form.as_rows())
self.bp = WidgetPlaceholder(self.method_form.buttons)
self._select_method(None, method)
widgets = [self.form_pile, Text(""), self.bp]
super().__init__(
"Edit {device} IPv{ip_version} configuration".format(device=device.name, ip_version=ip_version),
widgets,
0, 0)
def _select_method(self, sender, method):
rows = []
def r(w):
rows.append((w, self.form_pile.options('pack')))
for row in self.method_form.as_rows():
r(row)
if method == 'manual':
r(Text(""))
for row in self.manual_form.as_rows():
r(row)
self.bp.original_widget = self.manual_form.buttons
else:
self.bp.original_widget = self.method_form.buttons
self.form_pile.contents[:] = rows
def done_method(self, sender):
pass
def done_manual(self, sender):
# XXX this converting from and to and from strings thing is a
# bit out of hand.
gateway = self.form.gateway.value
if gateway is not None:
gateway = str(gateway)
result = {
'network': str(self.form.subnet.value),
'address': str(self.form.address.value),
'gateway': gateway,
'nameservers': list(map(str, self.form.nameservers.value)),
'searchdomains': self.form.searchdomains.value,
}
self.dev.remove_ip_networks_for_version(self.ip_version)
self.dev.remove_nameservers()
self.dev.add_network(self.ip_version, result)
self.parent.refresh_model_inputs()
self.parent.remove_overlay()
def cancel(self, sender=None):
self.parent.remove_overlay()
class NetworkView(BaseView): class NetworkView(BaseView):
title = _("Network connections") title = _("Network connections")
excerpt = _("Configure at least one interface this server can use to talk " excerpt = _("Configure at least one interface this server can use to talk "
@ -265,10 +140,8 @@ class NetworkView(BaseView):
self.controller = controller self.controller = controller
self.items = [] self.items = []
self.error = Text("", align='center') self.error = Text("", align='center')
self.additional_options = Pile(self._build_additional_options())
self.device_table = TablePile(self._build_model_inputs(), spacing=2, colspecs={3:ColSpec(can_shrink=True)}) self.device_table = TablePile(self._build_model_inputs(), spacing=2, colspecs={3:ColSpec(can_shrink=True)})
self.listbox = ListBox([self.device_table] + [ self.listbox = ListBox([self.device_table] + [
Padding.center_79(self.additional_options),
Padding.line_break(""), Padding.line_break(""),
]) ])
self.bottom = Pile([ self.bottom = Pile([
@ -341,115 +214,10 @@ class NetworkView(BaseView):
rows.append(Color.info_minor(TableRow([(4, Text(" " + dev.hwaddr + " " + dev.vendor))]))) rows.append(Color.info_minor(TableRow([(4, Text(" " + dev.hwaddr + " " + dev.vendor))])))
rows.append(Color.info_minor(TableRow([(4, Text(""))]))) rows.append(Color.info_minor(TableRow([(4, Text(""))])))
return rows return rows
ifname_width = 8 # default padding
if netdevs:
ifname_width += max(map(lambda dev: len(dev.name), netdevs))
if ifname_width > 20:
ifname_width = 20
iface_menus = []
# Display each interface -- name in first column, then configured IPs
# in the second.
log.debug('interfaces: {}'.format(netdevs))
for dev in netdevs:
col_1 = []
col_2 = []
col_1.append(
menu_btn(label=dev.name, on_press=self.on_net_dev_press))
if dev.type == 'wlan':
col_2.extend(_build_wifi_info(dev))
if len(dev.actual_ip_addresses) == 0 and (
dev.type == 'eth' and not dev.is_connected):
col_2.append(Color.info_primary(Text(_("Not connected"))))
col_2.extend(_build_gateway_ip_info_for_version(dev, 4))
col_2.extend(_build_gateway_ip_info_for_version(dev, 6))
# Other device info (MAC, vendor/model, speed)
template = ''
if dev.hwaddr:
template += '{} '.format(dev.hwaddr)
# TODO is this to translate?
if dev.is_bond_slave:
template += '(Bonded) '
# TODO to check if this is affected by translations
if not dev.vendor.lower().startswith('unknown'):
vendor = textwrap.wrap(dev.vendor, 15)[0]
template += '{} '.format(vendor)
if not dev.model.lower().startswith('unknown'):
model = textwrap.wrap(dev.model, 20)[0]
template += '{} '.format(model)
if dev.speed:
template += '({})'.format(dev.speed)
col_2.append(Color.info_minor(Text(template)))
iface_menus.append(
Columns([(ifname_width, Pile(col_1)), Pile(col_2)], 2))
return iface_menus
def refresh_model_inputs(self): def refresh_model_inputs(self):
self.device_table.set_contents(self._build_model_inputs()) self.device_table.set_contents(self._build_model_inputs())
def _build_additional_options(self):
labels = []
netdevs = self.model.get_all_netdevs()
# Display default route status
if self.model.default_v4_gateway is not None:
v4_route_source = "via " + self.model.default_v4_gateway
default_v4_route_w = Color.info_minor(
Text(_(" IPv4 default route %s." % v4_route_source)))
labels.append(default_v4_route_w)
if self.model.default_v6_gateway is not None:
v6_route_source = "via " + self.model.default_v6_gateway
default_v6_route_w = Color.info_minor(
Text(" IPv6 default route " + v6_route_source + "."))
labels.append(default_v6_route_w)
max_btn_len = 0
buttons = []
for opt, sig in self.model.get_menu():
if ':set-default-route' in sig:
if len(netdevs) < 2:
log.debug('Skipping default route menu option'
' (only one nic)')
continue
if ':bond-interfaces' in sig:
not_bonded = [dev for dev in netdevs if not dev.is_bonded]
if len(not_bonded) < 2:
log.debug('Skipping bonding menu option'
' (not enough available nics)')
continue
if len(opt) > max_btn_len:
max_btn_len = len(opt)
buttons.append(
menu_btn(
label=opt,
on_press=self.additional_menu_select,
user_data=sig))
buttons = [uPadding(button, align='left', width=max_btn_len + 6)
for button in buttons]
r = labels + buttons
if len(r) > 0:
r[0:0] = [Text("")]
return r
def additional_menu_select(self, result, sig):
self.controller.signal.emit_signal(sig)
def on_net_dev_press(self, result):
log.debug("Selected network dev: {}".format(result.label))
self.controller.network_configure_interface(result.label)
def show_network_error(self, action, info=None): def show_network_error(self, action, info=None):
self.error_showing = True self.error_showing = True
self.bottom.contents[0:0] = [ self.bottom.contents[0:0] = [

View File

@ -16,12 +16,24 @@
import logging import logging
import ipaddress import ipaddress
from urwid import connect_signal, Text from urwid import (
connect_signal,
Text,
WidgetPlaceholder,
)
from subiquitycore.view import BaseView from subiquitycore.view import BaseView
from subiquitycore.ui.buttons import menu_btn from subiquitycore.ui.buttons import menu_btn
from subiquitycore.ui.container import Pile
from subiquitycore.ui.form import (
ChoiceField,
Form,
FormField,
StringField,
)
from subiquitycore.ui.interactive import RestrictedEditor, StringEditor from subiquitycore.ui.interactive import RestrictedEditor, StringEditor
from subiquitycore.ui.form import Form, FormField, StringField from subiquitycore.ui.selector import Option
from subiquitycore.ui.stretchy import Stretchy
log = logging.getLogger( log = logging.getLogger(
@ -113,6 +125,121 @@ class NetworkConfigForm(Form):
return domains return domains
network_choices = {
4: [
(_("Automatic (DHCP)"), True, "dhcp"),
(_("Manual"), True, "manual"),
(_("Disabled"), True, "disable"),
],
6: [
(_("Automatic"), True, "accept-ra"),
(_("Automatic (DHCP)"), True, "dhcp"),
(_("Manual"), True, "manual"),
(_("Disabled"), True, "disable"),
],
}
class NetworkMethodForm(Form):
ok_label = _("Save")
method = ChoiceField("IPv{ip_version} Method: ", choices=network_choices[4])
class EditNetworkStretchy(Stretchy):
def __init__(self, parent, device, ip_version):
self.parent = parent
self.device = device
self.ip_version = ip_version
self.method_form = NetworkMethodForm()
self.method_form.method.caption = _("IPv{ip_version} Method: ").format(ip_version=ip_version)
manual_initial = {}
if len(device.configured_ip_addresses_for_version(ip_version)) > 0:
method = 'manual'
addr = ipaddress.ip_interface(
device.configured_ip_addresses_for_version(ip_version)[0])
manual_initial = {
'subnet': str(addr.network),
'address': str(addr.ip),
'nameservers': ', '.join(device.configured_nameservers),
'searchdomains': ', '.join(device.configured_searchdomains),
}
gw = device.configured_gateway_for_version(ip_version)
if gw:
manual_initial['gateway'] = str(gw)
elif self.device.dhcp_for_version(ip_version):
method = 'dhcp'
else:
method = 'disable'
self.method_form.method.value = method
self.method_form.method.widget.options = list(map(Option, network_choices[ip_version]))
connect_signal(self.method_form.method.widget, 'select', self._select_method)
log.debug("manual_initial %s", manual_initial)
self.manual_form = NetworkConfigForm(ip_version, manual_initial)
connect_signal(self.method_form, 'submit', self.done_method)
connect_signal(self.manual_form, 'submit', self.done_manual)
connect_signal(self.method_form, 'cancel', self.cancel)
connect_signal(self.manual_form, 'cancel', self.cancel)
self.form_pile = Pile(self.method_form.as_rows())
self.bp = WidgetPlaceholder(self.method_form.buttons)
self._select_method(None, method)
widgets = [self.form_pile, Text(""), self.bp]
super().__init__(
"Edit {device} IPv{ip_version} configuration".format(device=device.name, ip_version=ip_version),
widgets,
0, 0)
def _select_method(self, sender, method):
rows = []
def r(w):
rows.append((w, self.form_pile.options('pack')))
for row in self.method_form.as_rows():
r(row)
if method == 'manual':
r(Text(""))
for row in self.manual_form.as_rows():
r(row)
self.bp.original_widget = self.manual_form.buttons
else:
self.bp.original_widget = self.method_form.buttons
self.form_pile.contents[:] = rows
def done_method(self, sender):
pass
def done_manual(self, sender):
# XXX this converting from and to and from strings thing is a
# bit out of hand.
gateway = self.form.gateway.value
if gateway is not None:
gateway = str(gateway)
result = {
'network': str(self.form.subnet.value),
'address': str(self.form.address.value),
'gateway': gateway,
'nameservers': list(map(str, self.form.nameservers.value)),
'searchdomains': self.form.searchdomains.value,
}
self.dev.remove_ip_networks_for_version(self.ip_version)
self.dev.remove_nameservers()
self.dev.add_network(self.ip_version, result)
self.parent.refresh_model_inputs()
self.parent.remove_overlay()
def cancel(self, sender=None):
self.parent.remove_overlay()
class BaseNetworkConfigureManualView(BaseView): class BaseNetworkConfigureManualView(BaseView):
def __init__(self, model, controller, name): def __init__(self, model, controller, name):