From b9a6d93d6e3d271eda90deda79bd8bc3b20bf51e Mon Sep 17 00:00:00 2001 From: Bilal Baqar Date: Fri, 20 Nov 2015 20:26:17 -0800 Subject: [PATCH 1/3] Adding fabric/data network support --- config.yaml | 18 ++++++++ hooks/pg_dir_context.py | 3 +- hooks/pg_dir_utils.py | 70 +++++++++++++++++++++++-------- templates/kilo/ifcs.conf | 2 +- unit_tests/test_pg_dir_context.py | 13 +++--- 5 files changed, 82 insertions(+), 24 deletions(-) diff --git a/config.yaml b/config.yaml index 3cd0527..c450253 100644 --- a/config.yaml +++ b/config.yaml @@ -11,6 +11,24 @@ options: type: string default: 'juju-br0' description: The interface connected to PLUMgrid Managment network. + os-data-network: + type: string + default: + description: | + The IP address and netmask of the OpenStack Data network (e.g., + 192.168.0.0/24) + . + This network will be used for tenant network traffic in overlay + networks. + fabric-interfaces: + default: 'MANAGEMENT' + type: string + description: | + Interfaces that will provide fabric connectivity on the director nodes. + Provided in form of json in a string. These interfaces have to be connected + to the os-data-network specified in the config. Default value is MANAGEMENT which + will configure the management interface as the fabric interface on each + director. network-device-mtu: type: string default: '1580' diff --git a/hooks/pg_dir_context.py b/hooks/pg_dir_context.py index f183a4d..01154e8 100644 --- a/hooks/pg_dir_context.py +++ b/hooks/pg_dir_context.py @@ -83,8 +83,9 @@ class PGDirContext(context.NeutronContext): pg_ctxt['director_ips_string'] = pg_dir_ips_string pg_ctxt['virtual_ip'] = conf['plumgrid-virtual-ip'] pg_ctxt['pg_hostname'] = "pg-director" - from pg_dir_utils import get_mgmt_interface + from pg_dir_utils import get_mgmt_interface, get_fabric_interface pg_ctxt['interface'] = get_mgmt_interface() + pg_ctxt['fabric_interface'] = get_fabric_interface() pg_ctxt['label'] = get_unit_hostname() pg_ctxt['fabric_mode'] = 'host' virtual_ip_array = re.split('\.', conf['plumgrid-virtual-ip']) diff --git a/hooks/pg_dir_utils.py b/hooks/pg_dir_utils.py index 63bba10..abda352 100644 --- a/hooks/pg_dir_utils.py +++ b/hooks/pg_dir_utils.py @@ -13,7 +13,9 @@ from charmhelpers.contrib.network.ip import ( get_iface_from_addr, get_bridges, get_bridge_nics, - is_ip + is_ip, + is_address_in_network, + get_iface_addr ) from charmhelpers.fetch import ( apt_cache @@ -29,6 +31,7 @@ from charmhelpers.core.host import ( service_start, service_stop, ) +from socket import gethostname as get_unit_hostname import pg_dir_context import subprocess import time @@ -169,22 +172,23 @@ def remove_iovisor(): time.sleep(1) +def interface_exists(interface): + ''' + Checks if interface exists on node. + ''' + try: + subprocess.check_call(['ip', 'link', 'show', interface], + stdout=open(os.devnull, 'w'), + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError: + return False + return True + + def get_mgmt_interface(): ''' Returns the managment interface. ''' - def interface_exists(interface): - ''' - Checks if interface exists on node. - ''' - try: - subprocess.check_call(['ip', 'link', 'show', interface], - stdout=open(os.devnull, 'w'), - stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: - return False - return True - mgmt_interface = config('mgmt-interface') if interface_exists(mgmt_interface): return mgmt_interface @@ -194,17 +198,49 @@ def get_mgmt_interface(): return get_iface_from_addr(unit_get('private-address')) +def get_fabric_interface(): + ''' + Returns the fabric interface. + ''' + fabric_interfaces = config('fabric-interfaces') + if fabric_interfaces == 'MANAGEMENT': + return get_mgmt_interface() + else: + try: + all_fabric_interfaces = json.loads(fabric_interfaces) + except ValueError: + raise ValueError('Invalid json provided for fabric interfaces') + hostname = get_unit_hostname() + if hostname in all_fabric_interfaces: + node_fabric_interface = all_fabric_interfaces[hostname] + elif 'DEFAULT' in all_fabric_interfaces: + node_fabric_interface = all_fabric_interfaces['DEFAULT'] + else: + raise ValueError('No fabric interface provided for node') + if interface_exists(node_fabric_interface): + if is_address_in_network(config('os-data-network'), + get_iface_addr(node_fabric_interface)[0]): + return node_fabric_interface + else: + raise ValueError('Fabric interface not in fabric network') + else: + log('Provided fabric interface %s does not exist' + % node_fabric_interface) + raise ValueError('Provided fabric interface does not exist') + return node_fabric_interface + + def ensure_mtu(): ''' Ensures required MTU of the underlying networking of the node. ''' interface_mtu = config('network-device-mtu') - mgmt_interface = get_mgmt_interface() - if mgmt_interface in get_bridges(): - attached_interfaces = get_bridge_nics(mgmt_interface) + fabric_interface = get_fabric_interface() + if fabric_interface in get_bridges(): + attached_interfaces = get_bridge_nics(fabric_interface) for interface in attached_interfaces: set_nic_mtu(interface, interface_mtu) - set_nic_mtu(mgmt_interface, interface_mtu) + set_nic_mtu(fabric_interface, interface_mtu) def _exec_cmd(cmd=None, error_msg='Command exited with ERRORs', fatal=False): diff --git a/templates/kilo/ifcs.conf b/templates/kilo/ifcs.conf index 2ac1e59..9659932 100644 --- a/templates/kilo/ifcs.conf +++ b/templates/kilo/ifcs.conf @@ -1,2 +1,2 @@ -{{ interface }} = fabric_core host +{{ fabric_interface }} = fabric_core host diff --git a/unit_tests/test_pg_dir_context.py b/unit_tests/test_pg_dir_context.py index 33019de..a82b4b5 100644 --- a/unit_tests/test_pg_dir_context.py +++ b/unit_tests/test_pg_dir_context.py @@ -46,11 +46,12 @@ class PGDirContextTest(CharmTestCase): @patch.object(charmhelpers.contrib.openstack.context, 'unit_private_ip') @patch.object(context, '_pg_dir_ips') @patch.object(utils, 'get_mgmt_interface') - def test_neutroncc_context_api_rel(self, _mgmt_int, _pg_dir_ips, - _unit_priv_ip, _npa, _ens_pkgs, - _save_ff, _https, _is_clus, - _unit_get, _config, _runits, _rids, - _rget): + @patch.object(utils, 'get_fabric_interface') + def test_neutroncc_context_api_rel(self, _fabric_int, _mgmt_int, + _pg_dir_ips, _unit_priv_ip, _npa, + _ens_pkgs, _save_ff, _https, + _is_clus, _unit_get, _config, + _runits, _rids, _rget): def mock_npa(plugin, section, manager): if section == "driver": return "neutron.randomdriver" @@ -74,6 +75,7 @@ class PGDirContextTest(CharmTestCase): self.get_host_ip.return_value = '192.168.100.201' _pg_dir_ips.return_value = ['192.168.100.202', '192.168.100.203'] _mgmt_int.return_value = 'juju-br0' + _fabric_int.return_value = 'juju-br0' napi_ctxt = context.PGDirContext() expect = { 'config': 'neutron.randomconfig', @@ -86,6 +88,7 @@ class PGDirContextTest(CharmTestCase): 'virtual_ip': '192.168.100.250', 'pg_hostname': 'pg-director', 'interface': 'juju-br0', + 'fabric_interface': 'juju-br0', 'label': 'node0', 'fabric_mode': 'host', 'virtual_router_id': '250', From da05e4864213c8df710ebdd2cb98c9e8bd40ff7a Mon Sep 17 00:00:00 2001 From: Bilal Baqar Date: Sat, 21 Nov 2015 14:07:15 -0800 Subject: [PATCH 2/3] Fabric config change only applies restart to the node that has a change --- hooks/pg_dir_hooks.py | 10 ++++++++++ hooks/pg_dir_utils.py | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/hooks/pg_dir_hooks.py b/hooks/pg_dir_hooks.py index 3e4db18..1b35b9b 100755 --- a/hooks/pg_dir_hooks.py +++ b/hooks/pg_dir_hooks.py @@ -30,6 +30,7 @@ from pg_dir_utils import ( ensure_mtu, add_lcm_key, post_pg_license, + fabric_interface_changed ) hooks = Hooks() @@ -71,6 +72,15 @@ def config_changed(): if add_lcm_key(): log("PLUMgrid LCM Key added") return 1 + charm_config = config() + if charm_config.changed('fabric-interfaces'): + if not fabric_interface_changed(): + log("Fabric interface already set") + return 1 + if charm_config.changed('os-data-network'): + if charm_config['fabric-interfaces'] == 'MANAGEMENT': + log('Fabric running on managment network') + return 1 stop_pg() configure_sources(update=True) pkgs = determine_packages() diff --git a/hooks/pg_dir_utils.py b/hooks/pg_dir_utils.py index abda352..dacf0c7 100644 --- a/hooks/pg_dir_utils.py +++ b/hooks/pg_dir_utils.py @@ -198,6 +198,22 @@ def get_mgmt_interface(): return get_iface_from_addr(unit_get('private-address')) +def fabric_interface_changed(): + ''' + Returns true if interface for node changed. + ''' + fabric_interface = get_fabric_interface() + try: + with open(PG_IFCS_CONF, 'r') as ifcs: + for line in ifcs: + if 'fabric_core' in line: + if line.split()[0] == fabric_interface: + return False + except IOError: + return True + return True + + def get_fabric_interface(): ''' Returns the fabric interface. From 43492c939e86b741067f5866192d3b394b2a28cd Mon Sep 17 00:00:00 2001 From: Bilal Baqar Date: Sat, 21 Nov 2015 15:44:24 -0800 Subject: [PATCH 3/3] Removing restart on license post --- hooks/pg_dir_hooks.py | 7 ++++--- unit_tests/test_pg_dir_hooks.py | 17 +---------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/hooks/pg_dir_hooks.py b/hooks/pg_dir_hooks.py index 1b35b9b..939f3c5 100755 --- a/hooks/pg_dir_hooks.py +++ b/hooks/pg_dir_hooks.py @@ -66,13 +66,14 @@ def config_changed(): This hook is run when a config parameter is changed. It also runs on node reboot. ''' - if post_pg_license(): - log("PLUMgrid License Posted") - return 1 if add_lcm_key(): log("PLUMgrid LCM Key added") return 1 charm_config = config() + if charm_config.changed('plumgrid-license-key'): + if post_pg_license(): + log("PLUMgrid License Posted") + return 1 if charm_config.changed('fabric-interfaces'): if not fabric_interface_changed(): log("Fabric interface already set") diff --git a/unit_tests/test_pg_dir_hooks.py b/unit_tests/test_pg_dir_hooks.py index 2fa95a4..979d9c7 100644 --- a/unit_tests/test_pg_dir_hooks.py +++ b/unit_tests/test_pg_dir_hooks.py @@ -9,7 +9,6 @@ _map = utils.restart_map utils.register_configs = MagicMock() utils.restart_map = MagicMock() - import pg_dir_hooks as hooks utils.register_configs = _reg @@ -61,22 +60,8 @@ class PGDirHooksTests(CharmTestCase): self.add_lcm_key.assert_called_with() def test_config_changed_hook(self): - _pkgs = ['plumgrid-lxc', 'iovisor-dkms'] - self.add_lcm_key.return_value = 0 - self.post_pg_license.return_value = 0 - self.determine_packages.return_value = [_pkgs] + self.add_lcm_key.return_value = 1 self._call_hook('config-changed') - self.stop_pg.assert_called_with() - self.configure_sources.assert_called_with(update=True) - self.apt_install.assert_has_calls([ - call(_pkgs, fatal=True, - options=['--force-yes']), - ]) - self.load_iovisor.assert_called_with() - self.ensure_mtu.assert_called_with() - - self.CONFIGS.write_all.assert_called_with() - self.restart_pg.assert_called_with() def test_start(self): self._call_hook('start')