code motion
This commit is contained in:
parent
79a380f28f
commit
805ad5f495
|
@ -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] = [
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in New Issue