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:
Ryan Harper 2015-09-10 13:25:54 -05:00
parent b3f09832d4
commit baf6e377a9
4 changed files with 163 additions and 7 deletions

View File

@ -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))

View File

@ -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

View File

@ -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()

View File

@ -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)