diff --git a/etc/os-net-config/samples/vpp_bond.json b/etc/os-net-config/samples/vpp_bond.json new file mode 100644 index 00000000..8f67a358 --- /dev/null +++ b/etc/os-net-config/samples/vpp_bond.json @@ -0,0 +1,23 @@ +{ "network_config": [ + { + "type": "vpp_bond", + "name": "net_bonding0", + "addresses": [ + { + "ip_netmask": "192.0.2.1/24" + } + ], + "bonding_options": "mode=2,xmit_policy=l34", + "members": [ + { + "type": "vpp_interface", + "name": "eth1" + }, + { + "type": "vpp_interface", + "name": "eth2" + } + ] + } + ] +} diff --git a/etc/os-net-config/samples/vpp_bond.yaml b/etc/os-net-config/samples/vpp_bond.yaml new file mode 100644 index 00000000..ce312ecb --- /dev/null +++ b/etc/os-net-config/samples/vpp_bond.yaml @@ -0,0 +1,17 @@ +network_config: + - + type: vpp_bond + # name must be in the format of eth_bondX or net_bondingX, where X is a + # unique number for each bond. + name: net_bonding0 + addresses: + - + ip_netmask: 192.0.2.1/24 + bonding_options: "mode=2,xmit_policy=l34" + members: + - + type: vpp_interface + name: eth1 + - + type: vpp_interface + name: eth2 diff --git a/os_net_config/__init__.py b/os_net_config/__init__.py index 21877af6..656feb77 100644 --- a/os_net_config/__init__.py +++ b/os_net_config/__init__.py @@ -102,6 +102,10 @@ class NetConfig(object): self.add_ovs_dpdk_bond(obj) elif isinstance(obj, objects.VppInterface): self.add_vpp_interface(obj) + elif isinstance(obj, objects.VppBond): + self.add_vpp_bond(obj) + for member in obj.members: + self.add_object(member) elif isinstance(obj, objects.ContrailVrouter): self.add_contrail_vrouter(obj) elif isinstance(obj, objects.ContrailVrouterDpdk): @@ -219,6 +223,13 @@ class NetConfig(object): """ raise NotImplementedError("add_vpp_interface is not implemented.") + def add_vpp_bond(self, vpp_bond): + """Add a VppBond object to the net config object. + + :param vpp_bond: The VppBond object to add. + """ + raise NotImplementedError("add_vpp_bond is not implemented.") + def add_contrail_vrouter(self, contrail_vrouter): """Add a ContrailVrouter object to the net config object. diff --git a/os_net_config/impl_ifcfg.py b/os_net_config/impl_ifcfg.py index 7caedac0..cd332f21 100644 --- a/os_net_config/impl_ifcfg.py +++ b/os_net_config/impl_ifcfg.py @@ -121,6 +121,7 @@ class IfcfgNetConfig(os_net_config.NetConfig): self.ib_interface_data = {} self.linuxteam_data = {} self.vpp_interface_data = {} + self.vpp_bond_data = {} self.member_names = {} self.renamed_interfaces = {} self.bond_primary_ifaces = {} @@ -676,6 +677,9 @@ class IfcfgNetConfig(os_net_config.NetConfig): """ vpp_interface.pci_dev = utils.get_pci_address(vpp_interface.name, False) + if not vpp_interface.pci_dev: + vpp_interface.pci_dev = utils.get_stored_pci_address( + vpp_interface.name, False) vpp_interface.hwaddr = utils.interface_mac(vpp_interface.name) if not self.noop: self.ifdown(vpp_interface.name) @@ -684,6 +688,14 @@ class IfcfgNetConfig(os_net_config.NetConfig): % (vpp_interface.name, vpp_interface.pci_dev)) self.vpp_interface_data[vpp_interface.name] = vpp_interface + def add_vpp_bond(self, vpp_bond): + """Add a VppInterface object to the net config object + + :param vpp_bond: The VPPBond object to add + """ + logger.info('adding vpp bond: %s' % vpp_bond.name) + self.vpp_bond_data[vpp_bond.name] = vpp_bond + def add_contrail_vrouter(self, contrail_vrouter): """Add a ContraiVrouter object to the net config object @@ -806,6 +818,7 @@ class IfcfgNetConfig(os_net_config.NetConfig): stop_dhclient_interfaces = [] ovs_needs_restart = False vpp_interfaces = self.vpp_interface_data.values() + vpp_bonds = self.vpp_bond_data.values() for interface_name, iface_data in self.interface_data.items(): route_data = self.route_data.get(interface_name, '') @@ -1014,9 +1027,10 @@ class IfcfgNetConfig(os_net_config.NetConfig): logger.info('No changes required for InfiniBand iface: %s' % interface_name) - if self.vpp_interface_data: + if self.vpp_interface_data or self.vpp_bond_data: vpp_path = self.root_dir + vpp_config_path() - vpp_config = utils.generate_vpp_config(vpp_path, vpp_interfaces) + vpp_config = utils.generate_vpp_config(vpp_path, vpp_interfaces, + vpp_bonds) if utils.diff(vpp_path, vpp_config): restart_vpp = True update_files[vpp_path] = vpp_config @@ -1139,7 +1153,7 @@ class IfcfgNetConfig(os_net_config.NetConfig): if self.vpp_interface_data: logger.info('Updating VPP mapping') - utils.update_vpp_mapping(vpp_interfaces) + utils.update_vpp_mapping(vpp_interfaces, vpp_bonds) if self.errors: message = 'Failure(s) occurred when applying configuration' diff --git a/os_net_config/objects.py b/os_net_config/objects.py index a4d9af14..af3a50cc 100644 --- a/os_net_config/objects.py +++ b/os_net_config/objects.py @@ -76,6 +76,8 @@ def object_from_json(json): return OvsDpdkBond.from_json(json) elif obj_type == "vpp_interface": return VppInterface.from_json(json) + elif obj_type == "vpp_bond": + return VppBond.from_json(json) elif obj_type == "contrail_vrouter": return ContrailVrouter.from_json(json) elif obj_type == "contrail_vrouter_dpdk": @@ -1225,6 +1227,61 @@ class VppInterface(_BaseOpts): options=options) +class VppBond(_BaseOpts): + """Base class for VPP Bond.""" + def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None, + routes=None, mtu=None, primary=False, nic_mapping=None, + persist_mapping=False, defroute=True, dhclient_args=None, + dns_servers=None, nm_controlled=False, onboot=True, + members=None, bonding_options=None): + addresses = addresses or [] + members = members or [] + + super(VppBond, self).__init__(name, use_dhcp, use_dhcpv6, + addresses, routes, mtu, primary, + nic_mapping, persist_mapping, + defroute, dhclient_args, + dns_servers, nm_controlled, onboot) + self.members = members + self.bonding_options = bonding_options + + @staticmethod + def from_json(json): + name = _get_required_field(json, 'name', 'VppBond') + bonding_options = json.get('bonding_options', '') + + (use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping, + persist_mapping, defroute, dhclient_args, + dns_servers, nm_controlled, onboot) = _BaseOpts.base_opts_from_json( + json, include_primary=False) + + members = [] + members_json = json.get('members', None) + if members_json: + if isinstance(members_json, list): + for member in members_json: + if not member.get('nic_mapping'): + member.update({'nic_mapping': nic_mapping}) + member.update({'persist_mapping': persist_mapping}) + obj = object_from_json(member) + if isinstance(obj, VppInterface): + members.append(obj) + else: + msg = 'Members must be of type vpp_interface' + raise InvalidConfigException(msg) + else: + msg = 'Members must be a list.' + raise InvalidConfigException(msg) + + return VppBond(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6, + addresses=addresses, routes=routes, mtu=mtu, + members=members, nic_mapping=nic_mapping, + persist_mapping=persist_mapping, + defroute=defroute, dhclient_args=dhclient_args, + dns_servers=dns_servers, nm_controlled=nm_controlled, + onboot=onboot, bonding_options=bonding_options) + + class ContrailVrouter(_BaseOpts): """Base class for Contrail Interface. diff --git a/os_net_config/schema.yaml b/os_net_config/schema.yaml index ecb0e021..9f2f7387 100644 --- a/os_net_config/schema.yaml +++ b/os_net_config/schema.yaml @@ -669,6 +669,54 @@ definitions: - name additionalProperties: False + vpp_bond: + type: object + properties: + type: + enum: ["vpp_bond"] + name: + $ref: "#/definitions/string_or_param" + uio_driver: + $ref: "#/definitions/string_or_param" + options: + $ref: "#/definitions/string_or_param" + # common options: + use_dhcp: + $ref: "#/definitions/bool_or_param" + use_dhcp6: + $ref: "#/definitions/bool_or_param" + addresses: + $ref: "#/definitions/list_of_address" + routes: + $ref: "#/definitions/list_of_route" + mtu: + $ref: "#/definitions/int_or_param" + nic_mapping: + $ref: "#/definitions/nic_mapping" + persist_mapping: + $ref: "#/definitions/bool_or_param" + defroute: + $ref: "#/definitions/bool_or_param" + dhclient_args: + $ref: "#/definitions/string_or_param" + dns_servers: + $ref: "#/definitions/list_of_ip_address_string_or_param" + nm_controlled: + $ref: "#/definitions/bool_or_param" + onboot: + $ref: "#/definitions/bool_or_param" + members: + type: array + items: + oneOf: + - $ref: "#/definitions/vpp_interface" + bonding_options: + $ref: "#/definitions/string_or_param" + required: + - type + - name + additionalProperties: False + contrail_vrouter: type: object properties: @@ -1135,6 +1183,7 @@ items: - $ref: "#/definitions/nfvswitch_internal" - $ref: "#/definitions/ib_interface" - $ref: "#/definitions/vpp_interface" + - $ref: "#/definitions/vpp_bond" - $ref: "#/definitions/contrail_vrouter" - $ref: "#/definitions/contrail_vrouter_dpdk" minItems: 1 diff --git a/os_net_config/tests/test_objects.py b/os_net_config/tests/test_objects.py index 010dd95a..00a63a73 100644 --- a/os_net_config/tests/test_objects.py +++ b/os_net_config/tests/test_objects.py @@ -1256,3 +1256,55 @@ class TestVppInterface(base.TestCase): self.assertEqual("em1", vpp_interface.name) self.assertEqual("uio_pci_generic", vpp_interface.uio_driver) self.assertEqual("vlan-strip-offload off", vpp_interface.options) + + +class TestVppBond(base.TestCase): + def test_vpp_interface_from_json(self): + data = """{ +"type": "vpp_bond", +"name": "net_bonding0", +"members": [ + { + "type": "vpp_interface", + "name": "eth1" + }, + { + "type": "vpp_interface", + "name": "eth2" + } +], +"bonding_options": "mode=2,xmit_policy=l34" +} +""" + + vpp_bond = objects.object_from_json(json.loads(data)) + self.assertEqual("net_bonding0", vpp_bond.name) + self.assertEqual("mode=2,xmit_policy=l34", vpp_bond.bonding_options) + vpp_int1 = vpp_bond.members[0] + self.assertEqual("eth1", vpp_int1.name) + vpp_int2 = vpp_bond.members[1] + self.assertEqual("eth2", vpp_int2.name) + + def test_invalid_vpp_interface_from_json(self): + data = """{ +"type": "vpp_bond", +"name": "net_bonding0", +"members": [ + { + "type": "vpp_interface", + "name": "eth1" + }, + { + "type": "interface", + "name": "eth2" + } +], +"bonding_options": "mode=2,xmit_policy=l34" +} +""" + + err = self.assertRaises(objects.InvalidConfigException, + objects.object_from_json, + json.loads(data)) + expected = 'Members must be of type vpp_interface' + self.assertIn(expected, six.text_type(err)) diff --git a/os_net_config/tests/test_utils.py b/os_net_config/tests/test_utils.py index 387d78f2..83ac01ff 100644 --- a/os_net_config/tests/test_utils.py +++ b/os_net_config/tests/test_utils.py @@ -47,6 +47,13 @@ local0 0 down ''' +_VPPBOND_OUTPUT = """ + Name Idx Link Hardware +BondEthernet0 3 up Slave-Idx: 1 2 +TenGigabitEthernet2/0/0 1 slave TenGigabitEthernet2/0/0 +TenGigabitEthernet2/0/1 2 slave TenGigabitEthernet2/0/1 +""" + _INITIAL_VPP_CONFIG = ''' unix { nodaemon @@ -339,8 +346,8 @@ class TestUtils(base.TestCase): shutil.rmtree(tmpdir) - def test_get_vpp_interface_name(self): - def test_execute(name, dummy1, dummy2=None, dummy3=None): + def test_get_vpp_interface(self): + def test_execute(name, *args, **kwargs): if 'systemctl' in name: return None, None if 'vppctl' in name: @@ -348,19 +355,37 @@ class TestUtils(base.TestCase): self.stubs.Set(processutils, 'execute', test_execute) - self.assertEqual('GigabitEthernet0/9/0', - utils._get_vpp_interface_name('0000:00:09.0')) - self.assertIsNone(utils._get_vpp_interface_name(None)) - self.assertIsNone(utils._get_vpp_interface_name('0000:01:09.0')) + int_info = utils._get_vpp_interface('0000:00:09.0') + self.assertIsNotNone(int_info) + self.assertEqual('GigabitEthernet0/9/0', int_info['name']) + self.assertEqual('1', int_info['index']) + self.assertIsNone(utils._get_vpp_interface(None)) + self.assertIsNone(utils._get_vpp_interface('0000:01:09.0')) self.assertRaises(utils.VppException, - utils._get_vpp_interface_name, '0000:09.0') + utils._get_vpp_interface, '0000:09.0') @mock.patch('os_net_config.utils.processutils.execute', return_value=('', None)) def test_get_vpp_interface_name_multiple_iterations(self, mock_execute): - self.assertIsNone(utils._get_vpp_interface_name('0000:00:09.0', 2, 1)) + self.assertIsNone(utils._get_vpp_interface('0000:00:09.0', 2, 1)) self.assertEqual(4, mock_execute.call_count) + def test_get_vpp_bond(self): + def test_execute(name, *args, **kwargs): + if 'systemctl' in name: + return None, None + if 'vppctl' in name: + return _VPPBOND_OUTPUT, None + + self.stubs.Set(processutils, 'execute', test_execute) + bond_info = utils._get_vpp_bond(['1', '2']) + self.assertIsNotNone(bond_info) + self.assertEqual('BondEthernet0', bond_info['name']) + self.assertEqual('3', bond_info['index']) + self.assertIsNone(utils._get_vpp_bond(['1'])) + self.assertIsNone(utils._get_vpp_bond(['1', '2', '3'])) + self.assertIsNone(utils._get_vpp_bond([])) + def test_generate_vpp_config(self): tmpdir = tempfile.mkdtemp() config_path = os.path.join(tmpdir, 'startup.conf') @@ -374,6 +399,7 @@ class TestUtils(base.TestCase): int2 = objects.VppInterface('em2') int2.pci_dev = '0000:00:09.1' interfaces = [int1, int2] + bonds = [] expected_config = ''' unix { exec %s @@ -399,9 +425,45 @@ dpdk { } ''' % vpp_exec_path self.assertEqual(expected_config, - utils.generate_vpp_config(config_path, interfaces)) + utils.generate_vpp_config(config_path, interfaces, + bonds)) + + bonds = [objects.VppBond('net_bonding0', members=interfaces, + bonding_options='mode=2,xmit_policy=l3')] + expected_config = ''' +unix { + exec %s + nodaemon + log /tmp/vpp.log + full-coredump +} + + +api-trace { + on +} + +api-segment { + gid vpp +} + +dpdk { + vdev net_bonding0,slave=0000:00:09.0,slave=0000:00:09.1,mode=2,xmit_policy=l3 + dev 0000:00:09.1 + uio-driver vfio-pci + dev 0000:00:09.0 {vlan-strip-offload off} + +} +''' % vpp_exec_path + self.assertEqual(expected_config, + utils.generate_vpp_config(config_path, interfaces, + bonds)) def test_update_vpp_mapping(self): + tmpdir = tempfile.mkdtemp() + vpp_exec_path = os.path.join(tmpdir, 'vpp-exec') + utils._VPP_EXEC_FILE = vpp_exec_path + def test_get_dpdk_map(): return [{'name': 'eth1', 'pci_address': '0000:00:09.0', 'mac_address': '01:02:03:04:05:06', @@ -409,15 +471,15 @@ dpdk { self.stubs.Set(utils, '_get_dpdk_map', test_get_dpdk_map) - def test_execute(name, dummy1, dummy2=None, dummy3=None): + def test_execute(name, *args, **kwargs): return None, None self.stubs.Set(processutils, 'execute', test_execute) - def test_get_vpp_interface_name(pci_dev, tries, timeout): - return 'GigabitEthernet0/9/0' + def test_get_vpp_interface(pci_dev, tries, timeout): + return {'name': 'GigabitEthernet0/9/0', 'index': '1'} - self.stubs.Set(utils, '_get_vpp_interface_name', - test_get_vpp_interface_name) + self.stubs.Set(utils, '_get_vpp_interface', + test_get_vpp_interface) int1 = objects.VppInterface('eth1', options="vlan-strip-offload off") int1.pci_dev = '0000:00:09.0' @@ -427,7 +489,7 @@ dpdk { int2.hwaddr = '01:02:03:04:05:07' interfaces = [int1, int2] - utils.update_vpp_mapping(interfaces) + utils.update_vpp_mapping(interfaces, []) contents = utils.get_file_data(utils._DPDK_MAPPING_FILE) diff --git a/os_net_config/utils.py b/os_net_config/utils.py index ae5554cc..9b38c202 100644 --- a/os_net_config/utils.py +++ b/os_net_config/utils.py @@ -355,11 +355,12 @@ def restart_vpp(vpp_interfaces): processutils.execute('systemctl', 'restart', 'vpp') -def _get_vpp_interface_name(pci_addr, tries=1, timeout=5): - """Get VPP interface name from a given PCI address +def _get_vpp_interface(pci_addr, tries=1, timeout=5): + """Get VPP interface information from a given PCI address - From a running VPP instance, attempt to find the interface name from - a given PCI address of a NIC. + From a running VPP instance, attempt to find the interface name and index + from a given PCI address of a NIC. The index is used to identify VPP bond + interface associated with the VPP interface. :param pci_addr: PCI address to lookup, in the form of DDDD:BB:SS.F, where - DDDD = Domain @@ -377,7 +378,8 @@ def _get_vpp_interface_name(pci_addr, tries=1, timeout=5): try: timestamp = time.time() processutils.execute('systemctl', 'is-active', 'vpp') - out, err = processutils.execute('vppctl', 'show', 'interface') + out, err = processutils.execute('vppctl', 'show', 'interface', + check_exit_code=False) logger.debug("vppctl show interface\n%s\n%s\n" % (out, err)) m = re.search(r':([0-9a-fA-F]{2}):([0-9a-fA-F]{2}).([0-9a-fA-F])', pci_addr) @@ -388,10 +390,12 @@ def _get_vpp_interface_name(pci_addr, tries=1, timeout=5): else: raise VppException('Invalid PCI address format: %s' % pci_addr) - m = re.search(r'^(\w+%s)\s+' % formatted_pci, out, re.MULTILINE) + m = re.search(r'^(\w+%s)\s+(\d+)' % formatted_pci, out, + re.MULTILINE) if m: - logger.debug('VPP interface found: %s' % m.group(1)) - return m.group(1) + logger.debug('VPP interface found: %s, index: %s' % + (m.group(1), m.group(2))) + return {'name': m.group(1), 'index': m.group(2)} except processutils.ProcessExecutionError: pass @@ -402,7 +406,36 @@ def _get_vpp_interface_name(pci_addr, tries=1, timeout=5): return None -def generate_vpp_config(vpp_config_path, vpp_interfaces): +def _get_vpp_bond(member_ids): + """Get VPP bond information from a given list of VPP interface indices + + :param member_ids: list of VPP interfaces indices for the bond + :return: VPP bond name and index. None if an interface is not found. + """ + if not member_ids: + return None + + member_ids.sort() + member_ids_str = ' '.join(member_ids) + + out, err = processutils.execute('vppctl', 'show', + 'hardware-interfaces', 'bond', 'brief', + check_exit_code=False) + logger.debug('vppctl show hardware-interfaces bond brief\n%s' % out) + m = re.search(r'^\s*(BondEthernet\d+)\s+(\d+)\s+.+Slave-Idx:\s+%s\s*$' % + member_ids_str, + out, + re.MULTILINE) + if m: + logger.debug('Bond found: %s, index: %s' % (m.group(1), m.group(2))) + return {'name': m.group(1), 'index': m.group(2)} + else: + logger.debug('Bond with member indices "%s" not found in VPP' + % member_ids_str) + return None + + +def generate_vpp_config(vpp_config_path, vpp_interfaces, vpp_bonds): """Generate configuration content for VPP Generate interface related configuration content for VPP. Current @@ -419,6 +452,7 @@ def generate_vpp_config(vpp_config_path, vpp_interfaces): :param vpp_config_path: VPP Configuration file path :param vpp_interfaces: List of VPP interface objects + :param vpp_bonds: List of VPP bond objects :return: updated VPP config content. """ @@ -473,8 +507,32 @@ def generate_vpp_config(vpp_config_path, vpp_interfaces): data, flags=re.MULTILINE) else: - logger.debug('pci address not found for interface %s, may have' - 'already been bound to vpp' % vpp_interface.name) + raise VppException('Interface %s has no PCI address and is not' + ' found in mapping file' % vpp_interface.name) + + # Add bond config to 'dpdk' section + for vpp_bond in vpp_bonds: + slave_str = '' + for member in vpp_bond.members: + slave_str += ",slave=%s" % member.pci_dev + if vpp_bond.bonding_options: + options_str = ',' + vpp_bond.bonding_options.strip(' ,') + else: + options_str = '' + + if slave_str: + m = re.search(r'^\s*vdev\s+%s.*$' % vpp_bond.name, + data, re.MULTILINE) + if m: + data = re.sub(m.group(0), r' vdev %s%s%s' + % (vpp_bond.name, slave_str, options_str), + data) + else: + data = re.sub(r'(^\s*dpdk\s*\{)', + r'\1\n vdev %s%s%s' + % (vpp_bond.name, slave_str, options_str), + data, + flags=re.MULTILINE) # Add start up script for VPP to config. This script will be executed by # VPP on service start. @@ -497,36 +555,29 @@ def generate_vpp_config(vpp_config_path, vpp_interfaces): return data -def update_vpp_mapping(vpp_interfaces): +def update_vpp_mapping(vpp_interfaces, vpp_bonds): """Verify VPP interface binding and update mapping file VppException will be raised if interfaces are not properly bound. :param vpp_interfaces: List of VPP interface objects + :param vpp_bonds: List of VPP bond objects """ - vpp_start_cli = "" + cli_list = [] for vpp_int in vpp_interfaces: - if not vpp_int.pci_dev: - dpdk_map = _get_dpdk_map() - for dpdk_int in dpdk_map: - if dpdk_int['name'] == vpp_int.name: - vpp_int.pci_dev = dpdk_int['pci_address'] - break - else: - raise VppException('Interface %s has no PCI address and is not' - ' found in mapping file' % vpp_int.name) - # Try to get VPP interface name. In case VPP service is down # for some reason, we will restart VPP and try again. Currently # only trying one more time, can turn into a retry_counter if needed # in the future. for i in range(2): - vpp_name = _get_vpp_interface_name(vpp_int.pci_dev, - tries=12, timeout=5) - if not vpp_name: + int_info = _get_vpp_interface(vpp_int.pci_dev, + tries=12, timeout=5) + if not int_info: restart_vpp(vpp_interfaces) else: + vpp_int.vpp_name = int_info['name'] + vpp_int.vpp_idx = int_info['index'] break else: raise VppException('Interface %s with pci address %s not ' @@ -534,10 +585,13 @@ def update_vpp_mapping(vpp_interfaces): % (vpp_int.name, vpp_int.pci_dev)) # Generate content of startup script for VPP - for address in vpp_int.addresses: - vpp_start_cli += 'set interface state %s up\n' % vpp_name - vpp_start_cli += 'set interface ip address %s %s/%s\n' \ - % (vpp_name, address.ip, address.prefixlen) + if not vpp_bonds: + cli_list.append('set interface state %s up' + % int_info['name']) + for address in vpp_int.addresses: + cli_list.append('set interface ip address %s %s/%s\n' + % (int_info['name'], address.ip, + address.prefixlen)) logger.info('Updating mapping for vpp interface %s:' 'pci_dev: %s mac address: %s uio driver: %s' @@ -545,9 +599,30 @@ def update_vpp_mapping(vpp_interfaces): vpp_int.uio_driver)) _update_dpdk_map(vpp_int.name, vpp_int.pci_dev, vpp_int.hwaddr, vpp_int.uio_driver) - # Enable VPP service to make the VPP interface configuration - # persistent. - processutils.execute('systemctl', 'enable', 'vpp') + + for vpp_bond in vpp_bonds: + bond_ids = [member.vpp_idx for member in vpp_bond.members] + bond_info = _get_vpp_bond(bond_ids) + if bond_info: + cli_list.append('set interface state %s up' + % bond_info['name']) + for address in vpp_bond.addresses: + cli_list.append('set interface ip address %s %s/%s' + % (bond_info['name'], address.ip, + address.prefixlen)) + else: + raise VppException('Bond %s not found in VPP.' % vpp_bond.name) + + vpp_start_cli = get_file_data(_VPP_EXEC_FILE) + for cli_line in cli_list: + if not re.search(r'^\s*%s\s*$' % cli_line, + vpp_start_cli, re.MULTILINE): + vpp_start_cli += cli_line + '\n' + if diff(_VPP_EXEC_FILE, vpp_start_cli): write_config(_VPP_EXEC_FILE, vpp_start_cli) restart_vpp(vpp_interfaces) + + # Enable VPP service to make the VPP interface configuration + # persistent. + processutils.execute('systemctl', 'enable', 'vpp')