Generate network configuration
Update network model to generate a network configuration for each active network device. Note, some of the examples won't quite work since we now want updated probert data which includes new information about the source of any discovered ip. Signed-off-by: Ryan Harper <ryan.harper@canonical.com>
This commit is contained in:
parent
1d1f9d5d8f
commit
53b9e59929
|
@ -18,6 +18,8 @@ from subiquity.models import NetworkModel
|
|||
from subiquity.ui.views import NetworkView
|
||||
from subiquity.ui.dummy import DummyView
|
||||
|
||||
from subiquity.curtin import curtin_write_network_actions
|
||||
|
||||
|
||||
class NetworkController(ControllerPolicy):
|
||||
def __init__(self, common):
|
||||
|
@ -34,6 +36,10 @@ class NetworkController(ControllerPolicy):
|
|||
self.ui.set_footer(footer, 20)
|
||||
self.ui.set_body(NetworkView(self.model, self.signal))
|
||||
|
||||
def network_finish(self, actions):
|
||||
curtin_write_network_actions(actions)
|
||||
self.signal.emit_signal('filesystem:show')
|
||||
|
||||
def set_default_route(self):
|
||||
self.ui.set_body(DummyView(self.signal))
|
||||
|
||||
|
|
|
@ -16,6 +16,78 @@
|
|||
import yaml
|
||||
|
||||
|
||||
class NetAction():
|
||||
def __init__(self, **options):
|
||||
for k, v in options.items():
|
||||
setattr(self, k, v)
|
||||
self._action_keys = ['type', 'name', 'params', 'subnets']
|
||||
|
||||
def get(self):
|
||||
action = {k: v for k, v in self.__dict__.items()
|
||||
if k in self._action_keys}
|
||||
return action
|
||||
|
||||
|
||||
class PhysicalAction(NetAction):
|
||||
def __init__(self, **options):
|
||||
if 'type' not in options or options['type'] != 'physical':
|
||||
raise Exception('Invalid type for {}'.format(
|
||||
self.__class__.__name__))
|
||||
if 'name' not in options or len(options['name']) == 0:
|
||||
raise Exception('Invalid name for {}'.format(
|
||||
self.__class__.__name__))
|
||||
if 'mac_address' not in options:
|
||||
raise Exception('{} requires a valid mac_address attr'.format(
|
||||
self.__class__.__name__))
|
||||
super().__init__(**options)
|
||||
self._action_keys.extend(['mac_address'])
|
||||
|
||||
|
||||
class BridgeAction(NetAction):
|
||||
def __init__(self, **options):
|
||||
if 'type' not in options or options['type'] != 'bridge':
|
||||
raise Exception('Invalid type for {}'.format(
|
||||
self.__class__.__name__))
|
||||
if 'name' not in options or len(options['name']) == 0:
|
||||
raise Exception('Invalid name for {}'.format(
|
||||
self.__class__.__name__))
|
||||
if 'bridge_interfaces' not in options:
|
||||
raise Exception('{} requires bridge_interfaces attr'.format(
|
||||
self.__class__.__name__))
|
||||
super().__init__(**options)
|
||||
self._action_keys.extend(['bridge_interfaces'])
|
||||
|
||||
|
||||
class VlanAction(NetAction):
|
||||
def __init__(self, **options):
|
||||
if 'type' not in options or options['type'] != 'vlan':
|
||||
raise Exception('Invalid type for {}'.format(
|
||||
self.__class__.__name__))
|
||||
if 'name' not in options or len(options['name']) == 0:
|
||||
raise Exception('Invalid name for {}'.format(
|
||||
self.__class__.__name__))
|
||||
if 'vlan_id' not in options or 'vlan_link' not in options:
|
||||
raise Exception('{} requires vlan_id and vlan_link attr'.format(
|
||||
self.__class__.__name__))
|
||||
super().__init__(**options)
|
||||
self._action_keys.extend(['vlan_id', 'vlan_link'])
|
||||
|
||||
|
||||
class BondAction(NetAction):
|
||||
def __init__(self, **options):
|
||||
if 'type' not in options or options['type'] != 'bond':
|
||||
raise Exception('Invalid type for {}'.format(
|
||||
self.__class__.__name__))
|
||||
if 'name' not in options or len(options['name']) == 0:
|
||||
raise Exception('Invalid name for {}'.format(
|
||||
self.__class__.__name__))
|
||||
if 'bond_interfaces' not in options:
|
||||
raise Exception('{} requires bond_interfaces attr'.format(
|
||||
self.__class__.__name__))
|
||||
super().__init__(**options)
|
||||
self._action_keys.extend(['bond_interfaces'])
|
||||
|
||||
|
||||
class DiskAction():
|
||||
def __init__(self, action_id, model, serial, ptable='gpt', wipe=None):
|
||||
self._action_id = action_id
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import errno
|
||||
import ipaddress
|
||||
import logging
|
||||
import json
|
||||
import os
|
||||
|
@ -21,6 +22,11 @@ from subiquity.model import ModelPolicy
|
|||
from subiquity.utils import (read_sys_net,
|
||||
sys_dev_path)
|
||||
|
||||
from .actions import (
|
||||
BondAction,
|
||||
PhysicalAction,
|
||||
)
|
||||
|
||||
|
||||
log = logging.getLogger('subiquity.models.network')
|
||||
|
||||
|
@ -49,7 +55,10 @@ class NetworkModel(ModelPolicy):
|
|||
signals = [
|
||||
('Network main view',
|
||||
'network:show',
|
||||
'network')
|
||||
'network'),
|
||||
('Network finish',
|
||||
'network:finish',
|
||||
'network_finish')
|
||||
]
|
||||
|
||||
additional_options = [
|
||||
|
@ -68,6 +77,12 @@ class NetworkModel(ModelPolicy):
|
|||
self.opts = opts
|
||||
self.prober = prober
|
||||
self.network = {}
|
||||
self.configured_interfaces = {}
|
||||
|
||||
def reset(self):
|
||||
log.debug('resetting network model')
|
||||
self.configured_interfaces = {}
|
||||
self.network = {}
|
||||
|
||||
def get_signal_by_name(self, selection):
|
||||
for x, y, z in self.get_signals():
|
||||
|
@ -181,11 +196,67 @@ class NetworkModel(ModelPolicy):
|
|||
return False
|
||||
raise
|
||||
|
||||
def configure_iface(self, ifname):
|
||||
iface_info = self.network[ifname]
|
||||
log.debug('iface_info of {}:\n{}'.format(ifname, iface_info))
|
||||
mac_address = iface_info['hardware']['attrs'].get('address')
|
||||
|
||||
action = {
|
||||
'type': 'physical',
|
||||
'name': ifname,
|
||||
'mac_address': mac_address,
|
||||
'subnets': [],
|
||||
}
|
||||
ip_info = self.network[ifname]['ip']
|
||||
log.debug('iface {} ip info:\n{}'.format(ifname, ip_info))
|
||||
source = ip_info.get('source', None)
|
||||
if source and source['method'] is 'dhcp':
|
||||
action['subnets'].extend([{'type': 'dhcp'}])
|
||||
elif ip_info['addr'] is not None:
|
||||
# FIXME:
|
||||
# - ipv6
|
||||
# - read/fine default dns and route info
|
||||
ip_network = \
|
||||
ipaddress.IPv4Interface("{addr}/{netmask}".format(**ip_info))
|
||||
action['subnets'].extend([{
|
||||
'type': 'static',
|
||||
'address': ip_network.with_prefixlen}])
|
||||
log.debug("Configuring iface: {} as PhysicalAction({})".format(
|
||||
ifname, action))
|
||||
self.configured_interfaces.update({ifname: PhysicalAction(**action)})
|
||||
|
||||
def get_interfaces(self):
|
||||
ignored = ['lo', 'bridge', 'tun', 'tap']
|
||||
return [iface for iface in self.network.keys()
|
||||
if self.network[iface]['type'] not in ignored and
|
||||
self.iface_is_up(iface)]
|
||||
if len(self.configured_interfaces) == 0:
|
||||
ignored = ['lo', 'bridge', 'tun', 'tap']
|
||||
for ifname in self.network.keys():
|
||||
if self.network[ifname]['type'] not in ignored and \
|
||||
self.iface_is_up(ifname):
|
||||
self.configure_iface(ifname)
|
||||
|
||||
return self.configured_interfaces
|
||||
|
||||
def add_bond(self, ifname, interfaces, params=[], subnets=[]):
|
||||
''' take bondname and then a set of options '''
|
||||
action = {
|
||||
'type': 'bond',
|
||||
'name': ifname,
|
||||
'bond_interfaces': interfaces,
|
||||
'params': params,
|
||||
'subnets': subnets,
|
||||
}
|
||||
self.configured_interfaces.update({ifname: BondAction(**action)})
|
||||
log.debug("add_bond: {} as BondAction({})".format(
|
||||
ifname, action))
|
||||
|
||||
def add_subnet(self, ifname, subnet):
|
||||
if ifname not in self.configured_interfaces:
|
||||
raise Exception('No such configured interface: {}'.format(ifname))
|
||||
|
||||
action = self.configured_interfaces[ifname]
|
||||
if 'subnets' in action:
|
||||
action['subnets'].extend([subnet])
|
||||
else:
|
||||
action['subnets'] = [subnet]
|
||||
|
||||
def get_bridges(self):
|
||||
return [iface for iface in self.network.keys()
|
||||
|
|
|
@ -22,6 +22,7 @@ Provides network device listings and extended network information
|
|||
import logging
|
||||
from urwid import (ListBox, Pile, BoxAdapter,
|
||||
Text, Columns)
|
||||
import yaml
|
||||
from subiquity.ui.lists import SimpleList
|
||||
from subiquity.ui.buttons import cancel_btn, menu_btn
|
||||
from subiquity.ui.utils import Padding, Color
|
||||
|
@ -115,7 +116,7 @@ class NetworkView(ViewPolicy):
|
|||
if ipv4_status['provider']:
|
||||
ipv4_template += 'from {provider} '.format(**ipv4_status)
|
||||
col_2.append(Text(ipv4_template))
|
||||
col_2.append(Text("No IPv6 connection")) # vertical holder for ipv6
|
||||
col_2.append(Text("No IPv6 connection")) # vert. holder for ipv6
|
||||
if len(col_2):
|
||||
col_2 = BoxAdapter(SimpleList(col_2, is_selectable=False),
|
||||
height=len(col_2))
|
||||
|
@ -143,7 +144,13 @@ class NetworkView(ViewPolicy):
|
|||
|
||||
def on_net_dev_press(self, result):
|
||||
log.debug("Selected network dev: {}".format(result.label))
|
||||
self.signal.emit_signal('filesystem:show')
|
||||
actions = [action.get() for _, action in
|
||||
self.model.configured_interfaces.items()]
|
||||
log.debug('Configured Network Actions:\n{}'.format(
|
||||
yaml.dump(actions, default_flow_style=False)))
|
||||
|
||||
self.signal.emit_signal('network:finish', actions)
|
||||
|
||||
def cancel(self, button):
|
||||
self.model.reset()
|
||||
self.signal.emit_signal(self.model.get_previous_signal)
|
||||
|
|
Loading…
Reference in New Issue