diff options
Diffstat (limited to 'os_net_config')
-rw-r--r-- | os_net_config/__init__.py | 9 | ||||
-rw-r--r-- | os_net_config/impl_ifcfg.py | 48 | ||||
-rw-r--r-- | os_net_config/objects.py | 198 | ||||
-rw-r--r-- | os_net_config/tests/test_impl_ifcfg.py | 36 | ||||
-rw-r--r-- | os_net_config/tests/test_objects.py | 16 | ||||
-rw-r--r-- | os_net_config/tests/test_utils.py | 130 | ||||
-rw-r--r-- | os_net_config/utils.py | 227 |
7 files changed, 595 insertions, 69 deletions
diff --git a/os_net_config/__init__.py b/os_net_config/__init__.py index faa5e92..cb56059 100644 --- a/os_net_config/__init__.py +++ b/os_net_config/__init__.py @@ -95,6 +95,8 @@ class NetConfig(object): self.add_ovs_dpdk_port(obj) elif isinstance(obj, objects.OvsDpdkBond): self.add_ovs_dpdk_bond(obj) + elif isinstance(obj, objects.VppInterface): + self.add_vpp_interface(obj) def add_interface(self, interface): """Add an Interface object to the net config object. @@ -201,6 +203,13 @@ class NetConfig(object): """ raise NotImplementedError("add_ovs_dpdk_bond is not implemented.") + def add_vpp_interface(self, vpp_interface): + """Add a VppInterface object to the net config object. + + :param vpp_interface: The VppInterface object to add. + """ + raise NotImplementedError("add_vpp_interface is not implemented.") + def apply(self, cleanup=False): """Apply the network configuration. diff --git a/os_net_config/impl_ifcfg.py b/os_net_config/impl_ifcfg.py index 84a5139..8284ad5 100644 --- a/os_net_config/impl_ifcfg.py +++ b/os_net_config/impl_ifcfg.py @@ -54,6 +54,10 @@ def nfvswitch_config_path(): return "/etc/sysconfig/nfvswitch" +def vpp_config_path(): + return "/etc/vpp/startup.conf" + + def route_config_path(name): return "/etc/sysconfig/network-scripts/route-%s" % name @@ -116,6 +120,7 @@ class IfcfgNetConfig(os_net_config.NetConfig): self.linuxbond_data = {} self.ib_interface_data = {} self.linuxteam_data = {} + self.vpp_interface_data = {} self.member_names = {} self.renamed_interfaces = {} self.bond_primary_ifaces = {} @@ -142,7 +147,10 @@ class IfcfgNetConfig(os_net_config.NetConfig): data += "HOTPLUG=yes\n" else: data += "HOTPLUG=no\n" - data += "NM_CONTROLLED=no\n" + if base_opt.nm_controlled: + data += "NM_CONTROLLED=yes\n" + else: + data += "NM_CONTROLLED=no\n" if not base_opt.dns_servers and not base_opt.use_dhcp: data += "PEERDNS=no\n" if isinstance(base_opt, objects.Vlan): @@ -623,6 +631,21 @@ class IfcfgNetConfig(os_net_config.NetConfig): if ovs_dpdk_bond.routes: self._add_routes(ovs_dpdk_bond.name, ovs_dpdk_bond.routes) + def add_vpp_interface(self, vpp_interface): + """Add a VppInterface object to the net config object + + :param vpp_interface: The VppInterface object to add + """ + vpp_interface.pci_dev = utils.get_pci_address(vpp_interface.name, + False) + vpp_interface.hwaddr = utils.interface_mac(vpp_interface.name) + if not self.noop: + self.ifdown(vpp_interface.name) + remove_ifcfg_config(vpp_interface.name) + logger.info('adding vpp interface: %s %s' + % (vpp_interface.name, vpp_interface.pci_dev)) + self.vpp_interface_data[vpp_interface.name] = vpp_interface + def generate_ivs_config(self, ivs_uplinks, ivs_interfaces): """Generate configuration content for ivs.""" @@ -687,6 +710,7 @@ class IfcfgNetConfig(os_net_config.NetConfig): restart_bridges = [] restart_linux_bonds = [] restart_linux_teams = [] + restart_vpp = False update_files = {} all_file_names = [] ivs_uplinks = [] # ivs physical uplinks @@ -695,6 +719,7 @@ class IfcfgNetConfig(os_net_config.NetConfig): nfvswitch_internal_ifaces = [] # nfvswitch internal/management ports stop_dhclient_interfaces = [] ovs_needs_restart = False + vpp_interfaces = self.vpp_interface_data.values() for interface_name, iface_data in self.interface_data.items(): route_data = self.route_data.get(interface_name, '') @@ -903,6 +928,15 @@ class IfcfgNetConfig(os_net_config.NetConfig): logger.info('No changes required for InfiniBand iface: %s' % interface_name) + if self.vpp_interface_data: + vpp_path = self.root_dir + vpp_config_path() + vpp_config = utils.generate_vpp_config(vpp_path, vpp_interfaces) + if utils.diff(vpp_path, vpp_config): + restart_vpp = True + update_files[vpp_path] = vpp_config + else: + logger.info('No changes required for VPP') + if cleanup: for ifcfg_file in glob.iglob(cleanup_pattern()): if ifcfg_file not in all_file_names: @@ -929,6 +963,9 @@ class IfcfgNetConfig(os_net_config.NetConfig): for bridge in restart_bridges: self.ifdown(bridge, iftype='bridge') + for vpp_interface in vpp_interfaces: + self.ifdown(vpp_interface.name) + for oldname, newname in self.renamed_interfaces.items(): self.ifrename(oldname, newname) @@ -1006,4 +1043,13 @@ class IfcfgNetConfig(os_net_config.NetConfig): for vlan in restart_vlans: self.ifup(vlan) + if not self.noop: + if restart_vpp: + logger.info('Restarting VPP') + utils.restart_vpp(vpp_interfaces) + + if self.vpp_interface_data: + logger.info('Updating VPP mapping') + utils.update_vpp_mapping(vpp_interfaces) + return update_files diff --git a/os_net_config/objects.py b/os_net_config/objects.py index 3136be0..ec915da 100644 --- a/os_net_config/objects.py +++ b/os_net_config/objects.py @@ -68,6 +68,8 @@ def object_from_json(json): return OvsDpdkPort.from_json(json) elif obj_type == "ovs_dpdk_bond": return OvsDpdkBond.from_json(json) + elif obj_type == "vpp_interface": + return VppInterface.from_json(json) def _get_required_field(json, name, object_name): @@ -181,7 +183,7 @@ class _BaseOpts(object): 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): + dns_servers=None, nm_controlled=False): addresses = addresses or [] routes = routes or [] dns_servers = dns_servers or [] @@ -209,6 +211,7 @@ class _BaseOpts(object): self.defroute = defroute self.dhclient_args = dhclient_args self.dns_servers = dns_servers + self.nm_controlled = nm_controlled self.bridge_name = None # internal self.linux_bridge_name = None # internal self.ivs_bridge_name = None # internal @@ -244,6 +247,7 @@ class _BaseOpts(object): mtu = json.get('mtu', None) dhclient_args = json.get('dhclient_args') dns_servers = json.get('dns_servers') + nm_controlled = json.get('nm_controlled') primary = strutils.bool_from_string(str(json.get('primary', False))) addresses = [] routes = [] @@ -274,11 +278,11 @@ class _BaseOpts(object): if include_primary: return (use_dhcp, use_dhcpv6, addresses, routes, mtu, primary, nic_mapping, persist_mapping, defroute, dhclient_args, - dns_servers) + dns_servers, nm_controlled) else: return (use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping, persist_mapping, defroute, dhclient_args, - dns_servers) + dns_servers, nm_controlled) class Interface(_BaseOpts): @@ -287,14 +291,16 @@ class Interface(_BaseOpts): 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, ethtool_opts=None, hotplug=False): + dns_servers=None, nm_controlled=False, ethtool_opts=None, + hotplug=False): addresses = addresses or [] routes = routes or [] dns_servers = dns_servers or [] super(Interface, self).__init__(name, use_dhcp, use_dhcpv6, addresses, routes, mtu, primary, nic_mapping, persist_mapping, defroute, - dhclient_args, dns_servers) + dhclient_args, dns_servers, + nm_controlled) self.ethtool_opts = ethtool_opts self.hotplug = hotplug @@ -318,7 +324,7 @@ class Vlan(_BaseOpts): def __init__(self, device, vlan_id, 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): + dhclient_args=None, dns_servers=None, nm_controlled=False): addresses = addresses or [] routes = routes or [] dns_servers = dns_servers or [] @@ -326,7 +332,7 @@ class Vlan(_BaseOpts): super(Vlan, self).__init__(name, use_dhcp, use_dhcpv6, addresses, routes, mtu, primary, nic_mapping, persist_mapping, defroute, dhclient_args, - dns_servers) + dns_servers, nm_controlled) self.vlan_id = int(vlan_id) mapped_nic_names = _mapped_nics(nic_mapping) @@ -350,7 +356,7 @@ class IvsInterface(_BaseOpts): def __init__(self, vlan_id, name='ivs', use_dhcp=False, use_dhcpv6=False, addresses=None, routes=None, mtu=1500, primary=False, nic_mapping=None, persist_mapping=False, defroute=True, - dhclient_args=None, dns_servers=None): + dhclient_args=None, dns_servers=None, nm_controlled=False): addresses = addresses or [] routes = routes or [] dns_servers = dns_servers or [] @@ -359,7 +365,7 @@ class IvsInterface(_BaseOpts): addresses, routes, mtu, primary, nic_mapping, persist_mapping, defroute, dhclient_args, - dns_servers) + dns_servers, nm_controlled) self.vlan_id = int(vlan_id) @staticmethod @@ -376,7 +382,8 @@ class NfvswitchInternal(_BaseOpts): def __init__(self, vlan_id, name='nfvswitch', use_dhcp=False, use_dhcpv6=False, addresses=None, routes=None, mtu=1500, primary=False, nic_mapping=None, persist_mapping=False, - defroute=True, dhclient_args=None, dns_servers=None): + defroute=True, dhclient_args=None, dns_servers=None, + nm_controlled=False): addresses = addresses or [] routes = routes or [] dns_servers = dns_servers or [] @@ -385,7 +392,8 @@ class NfvswitchInternal(_BaseOpts): use_dhcpv6, addresses, routes, mtu, primary, nic_mapping, persist_mapping, defroute, - dhclient_args, dns_servers) + dhclient_args, dns_servers, + nm_controlled) self.vlan_id = int(vlan_id) @staticmethod @@ -403,7 +411,7 @@ class OvsBridge(_BaseOpts): routes=None, mtu=None, members=None, ovs_options=None, ovs_extra=None, nic_mapping=None, persist_mapping=False, defroute=True, dhclient_args=None, dns_servers=None, - fail_mode=None): + nm_controlled=False, fail_mode=None): addresses = addresses or [] routes = routes or [] members = members or [] @@ -411,7 +419,8 @@ class OvsBridge(_BaseOpts): super(OvsBridge, self).__init__(name, use_dhcp, use_dhcpv6, addresses, routes, mtu, False, nic_mapping, persist_mapping, defroute, - dhclient_args, dns_servers) + dhclient_args, dns_servers, + nm_controlled) self.members = members self.ovs_options = ovs_options ovs_extra = ovs_extra or [] @@ -436,7 +445,8 @@ class OvsBridge(_BaseOpts): name = _get_required_field(json, 'name', 'OvsBridge') (use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping, persist_mapping, defroute, - dhclient_args, dns_servers) = _BaseOpts.base_opts_from_json( + dhclient_args, dns_servers, + nm_controlled) = _BaseOpts.base_opts_from_json( json, include_primary=False) ovs_options = json.get('ovs_options') ovs_extra = json.get('ovs_extra', []) @@ -461,7 +471,7 @@ class OvsBridge(_BaseOpts): ovs_extra=ovs_extra, nic_mapping=nic_mapping, persist_mapping=persist_mapping, defroute=defroute, dhclient_args=dhclient_args, dns_servers=dns_servers, - fail_mode=fail_mode) + nm_controlled=nm_controlled, fail_mode=fail_mode) class OvsUserBridge(_BaseOpts): @@ -471,12 +481,12 @@ class OvsUserBridge(_BaseOpts): routes=None, mtu=None, members=None, ovs_options=None, ovs_extra=None, nic_mapping=None, persist_mapping=False, defroute=True, dhclient_args=None, dns_servers=None, - fail_mode=None): + nm_controlled=False, fail_mode=None): super(OvsUserBridge, self).__init__(name, use_dhcp, use_dhcpv6, addresses, routes, mtu, False, nic_mapping, persist_mapping, defroute, dhclient_args, - dns_servers) + dns_servers, nm_controlled) self.members = members or [] self.ovs_options = ovs_options ovs_extra = ovs_extra or [] @@ -503,7 +513,8 @@ class OvsUserBridge(_BaseOpts): name = _get_required_field(json, 'name', 'OvsUserBridge') (use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping, persist_mapping, defroute, - dhclient_args, dns_servers) = _BaseOpts.base_opts_from_json( + dhclient_args, dns_servers, + nm_controlled) = _BaseOpts.base_opts_from_json( json, include_primary=False) ovs_options = json.get('ovs_options') ovs_extra = json.get('ovs_extra', []) @@ -528,7 +539,8 @@ class OvsUserBridge(_BaseOpts): ovs_extra=ovs_extra, nic_mapping=nic_mapping, persist_mapping=persist_mapping, defroute=defroute, dhclient_args=dhclient_args, - dns_servers=dns_servers, fail_mode=fail_mode) + dns_servers=dns_servers, + nm_controlled=nm_controlled, fail_mode=fail_mode) class LinuxBridge(_BaseOpts): @@ -537,7 +549,7 @@ class LinuxBridge(_BaseOpts): def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None, routes=None, mtu=None, members=None, nic_mapping=None, persist_mapping=False, defroute=True, dhclient_args=None, - dns_servers=None): + dns_servers=None, nm_controlled=False): addresses = addresses or [] routes = routes or [] members = members or [] @@ -545,7 +557,8 @@ class LinuxBridge(_BaseOpts): super(LinuxBridge, self).__init__(name, use_dhcp, use_dhcpv6, addresses, routes, mtu, False, nic_mapping, persist_mapping, - defroute, dhclient_args, dns_servers) + defroute, dhclient_args, dns_servers, + nm_controlled) self.members = members for member in self.members: member.linux_bridge_name = name @@ -564,7 +577,7 @@ class LinuxBridge(_BaseOpts): name = _get_required_field(json, 'name', 'LinuxBridge') (use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping, persist_mapping, defroute, dhclient_args, - dns_servers) = _BaseOpts.base_opts_from_json( + dns_servers, nm_controlled) = _BaseOpts.base_opts_from_json( json, include_primary=False) members = [] @@ -583,7 +596,8 @@ class LinuxBridge(_BaseOpts): members=members, nic_mapping=nic_mapping, persist_mapping=persist_mapping, defroute=defroute, dhclient_args=dhclient_args, - dns_servers=dns_servers) + dns_servers=dns_servers, + nm_controlled=nm_controlled) class IvsBridge(_BaseOpts): @@ -602,7 +616,7 @@ class IvsBridge(_BaseOpts): def __init__(self, name='ivs', use_dhcp=False, use_dhcpv6=False, addresses=None, routes=None, mtu=1500, members=None, nic_mapping=None, persist_mapping=False, defroute=True, - dhclient_args=None, dns_servers=None): + dhclient_args=None, dns_servers=None, nm_controlled=False): addresses = addresses or [] routes = routes or [] members = members or [] @@ -610,7 +624,8 @@ class IvsBridge(_BaseOpts): super(IvsBridge, self).__init__(name, use_dhcp, use_dhcpv6, addresses, routes, mtu, False, nic_mapping, persist_mapping, - defroute, dhclient_args, dns_servers) + defroute, dhclient_args, dns_servers, + nm_controlled) self.members = members for member in self.members: if isinstance(member, OvsBond) or isinstance(member, LinuxBond): @@ -625,7 +640,7 @@ class IvsBridge(_BaseOpts): name = 'ivs' (use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping, persist_mapping, defroute, dhclient_args, - dns_servers) = _BaseOpts.base_opts_from_json( + dns_servers, nm_controlled) = _BaseOpts.base_opts_from_json( json, include_primary=False) members = [] @@ -644,7 +659,7 @@ class IvsBridge(_BaseOpts): members=members, nic_mapping=nic_mapping, persist_mapping=persist_mapping, defroute=defroute, dhclient_args=dhclient_args, - dns_servers=dns_servers) + dns_servers=dns_servers, nm_controlled=nm_controlled) class NfvswitchBridge(_BaseOpts): @@ -658,7 +673,8 @@ class NfvswitchBridge(_BaseOpts): def __init__(self, name='nfvswitch', use_dhcp=False, use_dhcpv6=False, addresses=None, routes=None, mtu=1500, members=None, nic_mapping=None, persist_mapping=False, defroute=True, - dhclient_args=None, dns_servers=None, options=""): + dhclient_args=None, dns_servers=None, nm_controlled=False, + options=""): addresses = addresses or [] routes = routes or [] members = members or [] @@ -667,7 +683,7 @@ class NfvswitchBridge(_BaseOpts): addresses, routes, mtu, False, nic_mapping, persist_mapping, defroute, dhclient_args, - dns_servers) + dns_servers, nm_controlled) self.options = options self.members = members for member in self.members: @@ -683,7 +699,7 @@ class NfvswitchBridge(_BaseOpts): name = 'nfvswitch' (use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping, persist_mapping, defroute, dhclient_args, - dns_servers) = _BaseOpts.base_opts_from_json( + dns_servers, nm_controlled) = _BaseOpts.base_opts_from_json( json, include_primary=False) # members @@ -707,7 +723,8 @@ class NfvswitchBridge(_BaseOpts): members=members, nic_mapping=nic_mapping, persist_mapping=persist_mapping, defroute=defroute, dhclient_args=dhclient_args, - dns_servers=dns_servers, options=options) + dns_servers=dns_servers, + nm_controlled=nm_controlled, options=options) class LinuxTeam(_BaseOpts): @@ -716,7 +733,8 @@ class LinuxTeam(_BaseOpts): def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None, routes=None, mtu=None, primary=False, members=None, bonding_options=None, nic_mapping=None, persist_mapping=False, - defroute=True, dhclient_args=None, dns_servers=None): + defroute=True, dhclient_args=None, dns_servers=None, + nm_controlled=False): addresses = addresses or [] routes = routes or [] members = members or [] @@ -724,7 +742,8 @@ class LinuxTeam(_BaseOpts): super(LinuxTeam, self).__init__(name, use_dhcp, use_dhcpv6, addresses, routes, mtu, primary, nic_mapping, persist_mapping, defroute, - dhclient_args, dns_servers) + dhclient_args, dns_servers, + nm_controlled) self.members = members self.bonding_options = bonding_options for member in self.members: @@ -743,7 +762,7 @@ class LinuxTeam(_BaseOpts): name = _get_required_field(json, 'name', 'LinuxTeam') (use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping, persist_mapping, defroute, dhclient_args, - dns_servers) = _BaseOpts.base_opts_from_json( + dns_servers, nm_controlled) = _BaseOpts.base_opts_from_json( json, include_primary=False) bonding_options = json.get('bonding_options') members = [] @@ -763,7 +782,8 @@ class LinuxTeam(_BaseOpts): members=members, bonding_options=bonding_options, nic_mapping=nic_mapping, persist_mapping=persist_mapping, defroute=defroute, - dhclient_args=dhclient_args, dns_servers=dns_servers) + dhclient_args=dhclient_args, dns_servers=dns_servers, + nm_controlled=nm_controlled) class LinuxBond(_BaseOpts): @@ -772,7 +792,8 @@ class LinuxBond(_BaseOpts): def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None, routes=None, mtu=None, primary=False, members=None, bonding_options=None, nic_mapping=None, persist_mapping=False, - defroute=True, dhclient_args=None, dns_servers=None): + defroute=True, dhclient_args=None, dns_servers=None, + nm_controlled=False): addresses = addresses or [] routes = routes or [] members = members or [] @@ -780,7 +801,8 @@ class LinuxBond(_BaseOpts): super(LinuxBond, self).__init__(name, use_dhcp, use_dhcpv6, addresses, routes, mtu, primary, nic_mapping, persist_mapping, defroute, - dhclient_args, dns_servers) + dhclient_args, dns_servers, + nm_controlled) self.members = members self.bonding_options = bonding_options for member in self.members: @@ -799,7 +821,7 @@ class LinuxBond(_BaseOpts): name = _get_required_field(json, 'name', 'LinuxBond') (use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping, persist_mapping, defroute, dhclient_args, - dns_servers) = _BaseOpts.base_opts_from_json( + dns_servers, nm_controlled) = _BaseOpts.base_opts_from_json( json, include_primary=False) bonding_options = json.get('bonding_options') members = [] @@ -819,7 +841,8 @@ class LinuxBond(_BaseOpts): members=members, bonding_options=bonding_options, nic_mapping=nic_mapping, persist_mapping=persist_mapping, defroute=defroute, - dhclient_args=dhclient_args, dns_servers=dns_servers) + dhclient_args=dhclient_args, dns_servers=dns_servers, + nm_controlled=nm_controlled) class OvsBond(_BaseOpts): @@ -829,7 +852,7 @@ class OvsBond(_BaseOpts): routes=None, mtu=None, primary=False, members=None, ovs_options=None, ovs_extra=None, nic_mapping=None, persist_mapping=False, defroute=True, dhclient_args=None, - dns_servers=None): + dns_servers=None, nm_controlled=False): addresses = addresses or [] routes = routes or [] members = members or [] @@ -837,7 +860,7 @@ class OvsBond(_BaseOpts): super(OvsBond, self).__init__(name, use_dhcp, use_dhcpv6, addresses, routes, mtu, primary, nic_mapping, persist_mapping, defroute, dhclient_args, - dns_servers) + dns_servers, nm_controlled) self.members = members self.ovs_options = ovs_options self.ovs_extra = format_ovs_extra(self, ovs_extra) @@ -860,7 +883,7 @@ class OvsBond(_BaseOpts): name = _get_required_field(json, 'name', 'OvsBond') (use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping, persist_mapping, defroute, dhclient_args, - dns_servers) = _BaseOpts.base_opts_from_json( + dns_servers, nm_controlled) = _BaseOpts.base_opts_from_json( json, include_primary=False) ovs_options = json.get('ovs_options') ovs_extra = json.get('ovs_extra', []) @@ -883,7 +906,8 @@ class OvsBond(_BaseOpts): members=members, ovs_options=ovs_options, ovs_extra=ovs_extra, nic_mapping=nic_mapping, persist_mapping=persist_mapping, defroute=defroute, - dhclient_args=dhclient_args, dns_servers=dns_servers) + dhclient_args=dhclient_args, dns_servers=dns_servers, + nm_controlled=nm_controlled) class OvsTunnel(_BaseOpts): @@ -892,15 +916,16 @@ class OvsTunnel(_BaseOpts): 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, tunnel_type=None, ovs_options=None, - ovs_extra=None): + dns_servers=None, nm_controlled=False, tunnel_type=None, + ovs_options=None, ovs_extra=None): addresses = addresses or [] routes = routes or [] dns_servers = dns_servers or [] super(OvsTunnel, self).__init__(name, use_dhcp, use_dhcpv6, addresses, routes, mtu, primary, nic_mapping, persist_mapping, defroute, - dhclient_args, dns_servers) + dhclient_args, dns_servers, + nm_controlled) self.tunnel_type = tunnel_type self.ovs_options = ovs_options or [] self.ovs_extra = format_ovs_extra(self, ovs_extra) @@ -925,8 +950,8 @@ class OvsPatchPort(_BaseOpts): 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, bridge_name=None, peer=None, - ovs_options=None, ovs_extra=None): + dns_servers=None, nm_controlled=False, bridge_name=None, + peer=None, ovs_options=None, ovs_extra=None): addresses = addresses or [] routes = routes or [] dns_servers = dns_servers or [] @@ -934,7 +959,7 @@ class OvsPatchPort(_BaseOpts): addresses, routes, mtu, primary, nic_mapping, persist_mapping, defroute, dhclient_args, - dns_servers) + dns_servers, nm_controlled) self.bridge_name = bridge_name self.peer = peer self.ovs_options = ovs_options or [] @@ -961,14 +986,15 @@ class IbInterface(_BaseOpts): 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, ethtool_opts=None): + dns_servers=None, nm_controlled=False, ethtool_opts=None): addresses = addresses or [] routes = routes or [] dns_servers = dns_servers or [] super(IbInterface, self).__init__(name, use_dhcp, use_dhcpv6, addresses, routes, mtu, primary, nic_mapping, persist_mapping, - defroute, dhclient_args, dns_servers) + defroute, dhclient_args, dns_servers, + nm_controlled) self.ethtool_opts = ethtool_opts @staticmethod @@ -985,14 +1011,14 @@ class OvsDpdkPort(_BaseOpts): 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, members=None, driver='vfio-pci', - ovs_options=None, ovs_extra=None): + dns_servers=None, nm_controlled=False, members=None, + driver='vfio-pci', ovs_options=None, ovs_extra=None): super(OvsDpdkPort, self).__init__(name, use_dhcp, use_dhcpv6, addresses, routes, mtu, primary, nic_mapping, persist_mapping, defroute, dhclient_args, - dns_servers) + dns_servers, nm_controlled) self.members = members or [] self.ovs_options = ovs_options or [] self.ovs_extra = format_ovs_extra(self, ovs_extra) @@ -1047,11 +1073,12 @@ class OvsDpdkBond(_BaseOpts): routes=None, mtu=None, primary=False, members=None, ovs_options=None, ovs_extra=None, nic_mapping=None, persist_mapping=False, defroute=True, dhclient_args=None, - dns_servers=None): + dns_servers=None, nm_controlled=False): super(OvsDpdkBond, self).__init__(name, use_dhcp, use_dhcpv6, addresses, routes, mtu, primary, nic_mapping, persist_mapping, - defroute, dhclient_args, dns_servers) + defroute, dhclient_args, dns_servers, + nm_controlled) self.members = members or [] self.ovs_options = ovs_options self.ovs_extra = format_ovs_extra(self, ovs_extra) @@ -1075,7 +1102,7 @@ class OvsDpdkBond(_BaseOpts): name = _get_required_field(json, 'name', 'OvsDpdkBond') (use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping, persist_mapping, defroute, dhclient_args, - dns_servers) = _BaseOpts.base_opts_from_json( + dns_servers, nm_controlled) = _BaseOpts.base_opts_from_json( json, include_primary=False) ovs_options = json.get('ovs_options') ovs_extra = json.get('ovs_extra', []) @@ -1104,4 +1131,59 @@ class OvsDpdkBond(_BaseOpts): ovs_extra=ovs_extra, nic_mapping=nic_mapping, persist_mapping=persist_mapping, defroute=defroute, dhclient_args=dhclient_args, - dns_servers=dns_servers) + dns_servers=dns_servers, + nm_controlled=nm_controlled) + + +class VppInterface(_BaseOpts): + """Base class for VPP Interface. + + Vector Packet Processing (VPP) is a high performance packet processing + stack that runs in user space in Linux. VPP is used as an alternative to + kernel networking stack for accelerated network data path. VPP uses DPDK + poll-mode drivers to bind system interfaces rather than kernel drivers. + VPP bound interfaces are not visible to kernel networking stack, so we + must handle them separately. + + The following parameters can be specified in addition to base Interface: + - uio_driver: DPDK poll-mode driver name. Defaults to 'vfio-pci', valid + values are 'uio_pci_generic' and 'vfio-pci'. + - options: Interface options such as vlan stripping and tx/rx transmit + queues specification. Defaults to None. Reference for those + configurations can be found at + https://wiki.fd.io/view/VPP/Command-line_Arguments + Example: 'vlan-strip-offload on num-rx-queues 3' + + Note that 'name' attribute is used to indicate the kernel nic that should + be bound to VPP. Once VPP binds the interface, a mapping file will be + updated with the interface's information, and this file will be used in + subsequent runs of os-net-config. + """ + 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, uio_driver='vfio-pci', + options=None): + addresses = addresses or [] + + super(VppInterface, self).__init__(name, use_dhcp, use_dhcpv6, + addresses, routes, mtu, primary, + nic_mapping, persist_mapping, + defroute, dhclient_args, + dns_servers, nm_controlled) + self.uio_driver = uio_driver + self.options = options + # pci_dev contains pci address for the interface, it will be populated + # when interface is added to config object. It will be determined + # either through ethtool or by looking up the dpdk mapping file. + self.pci_dev = None + + @staticmethod + def from_json(json): + name = _get_required_field(json, 'name', 'VppInterface') + uio_driver = json.get('uio_driver', 'vfio-pci') + options = json.get('options', '') + + opts = _BaseOpts.base_opts_from_json(json) + return VppInterface(name, *opts, uio_driver=uio_driver, + options=options) diff --git a/os_net_config/tests/test_impl_ifcfg.py b/os_net_config/tests/test_impl_ifcfg.py index 366c408..d770415 100644 --- a/os_net_config/tests/test_impl_ifcfg.py +++ b/os_net_config/tests/test_impl_ifcfg.py @@ -34,6 +34,14 @@ NM_CONTROLLED=no PEERDNS=no """ +_BASE_IFCFG_NETWORKMANAGER = """# This file is autogenerated by os-net-config +DEVICE=em1 +ONBOOT=yes +HOTPLUG=no +NM_CONTROLLED=yes +PEERDNS=no +""" + _HOTPLUG = """# This file is autogenerated by os-net-config DEVICE=em1 ONBOOT=yes @@ -153,6 +161,19 @@ OVSBOOTPROTO=dhcp OVSDHCPINTERFACES="em1" """ +_NM_CONTROLLED_INTERFACE = _BASE_IFCFG_NETWORKMANAGER + """MASTER=bond1 +SLAVE=yes +BOOTPROTO=none +""" + +_NM_CONTROLLED_BOND = """# This file is autogenerated by os-net-config +DEVICE=bond1 +ONBOOT=yes +HOTPLUG=no +NM_CONTROLLED=yes +PEERDNS=no +""" + _OVS_BRIDGE_DHCP_STANDALONE = _OVS_BRIDGE_DHCP + \ "OVS_EXTRA=\"set bridge br-ctlplane fail_mode=standalone\"\n" @@ -845,6 +866,20 @@ DNS2=5.6.7.8 """ self.assertEqual(em1_config, self.get_interface_config('em1')) + def test_nm_controlled(self): + interface1 = objects.Interface('em1', nm_controlled=True) + interface2 = objects.Interface('em2', nm_controlled=True) + bond = objects.LinuxBond('bond1', nm_controlled=True, + members=[interface1, interface2]) + self.provider.add_linux_bond(bond) + self.provider.add_interface(interface1) + self.provider.add_interface(interface2) + + ifcfg_data = self.get_interface_config('em1') + self.assertEqual(_NM_CONTROLLED_INTERFACE, ifcfg_data) + bond_data = self.get_linux_bond_config('bond1') + self.assertEqual(_NM_CONTROLLED_BOND, bond_data) + def test_network_ovs_dpdk_bridge_and_port(self): nic_mapping = {'nic1': 'eth0', 'nic2': 'eth1', 'nic3': 'eth2'} self.stubbed_mapped_nics = nic_mapping @@ -924,6 +959,7 @@ class TestIfcfgNetConfigApply(base.TestCase): def setUp(self): super(TestIfcfgNetConfigApply, self).setUp() self.temp_ifcfg_file = tempfile.NamedTemporaryFile() + self.temp_bond_file = tempfile.NamedTemporaryFile() self.temp_route_file = tempfile.NamedTemporaryFile() self.temp_route6_file = tempfile.NamedTemporaryFile() self.temp_bridge_file = tempfile.NamedTemporaryFile() diff --git a/os_net_config/tests/test_objects.py b/os_net_config/tests/test_objects.py index f5daf31..4ff87c8 100644 --- a/os_net_config/tests/test_objects.py +++ b/os_net_config/tests/test_objects.py @@ -988,3 +988,19 @@ class TestOvsDpdkBond(base.TestCase): self.assertEqual("vfio-pci", dpdk_port1.driver) iface2 = dpdk_port1.members[0] self.assertEqual("eth2", iface2.name) + + +class TestVppInterface(base.TestCase): + def test_vpp_interface_from_json(self): + data = """{ +"type": "vpp_interface", +"name": "em1", +"uio_driver": "uio_pci_generic", +"options": "vlan-strip-offload off" +} +""" + + vpp_interface = objects.object_from_json(json.loads(data)) + self.assertEqual("em1", vpp_interface.name) + self.assertEqual("uio_pci_generic", vpp_interface.uio_driver) + self.assertEqual("vlan-strip-offload off", vpp_interface.options) diff --git a/os_net_config/tests/test_utils.py b/os_net_config/tests/test_utils.py index 1885cbb..5a8b6af 100644 --- a/os_net_config/tests/test_utils.py +++ b/os_net_config/tests/test_utils.py @@ -21,6 +21,7 @@ import shutil import tempfile import yaml +from os_net_config import objects from os_net_config.tests import base from os_net_config import utils @@ -38,6 +39,33 @@ supports-register-dump: yes supports-priv-flags: no ''' +_VPPCTL_OUTPUT = ''' + Name Idx State Counter Count +GigabitEthernet0/9/0 1 down +local0 0 down + +''' + +_INITIAL_VPP_CONFIG = ''' +unix { + nodaemon + log /tmp/vpp.log + full-coredump +} + + +api-trace { + on +} + +api-segment { + gid vpp +} + +dpdk { +} +''' + class TestUtils(base.TestCase): @@ -83,7 +111,7 @@ class TestUtils(base.TestCase): out = _PCI_OUTPUT return out, None self.stubs.Set(processutils, 'execute', test_execute) - pci = utils._get_pci_address('nic2', False) + pci = utils.get_pci_address('nic2', False) self.assertEqual('0000:00:19.0', pci) def test_get_pci_address_exception(self): @@ -91,7 +119,7 @@ class TestUtils(base.TestCase): if 'ethtool' in name: raise processutils.ProcessExecutionError self.stubs.Set(processutils, 'execute', test_execute) - pci = utils._get_pci_address('nic2', False) + pci = utils.get_pci_address('nic2', False) self.assertEqual(None, pci) def test_get_pci_address_error(self): @@ -99,7 +127,7 @@ class TestUtils(base.TestCase): if 'ethtool' in name: return None, 'Error' self.stubs.Set(processutils, 'execute', test_execute) - pci = utils._get_pci_address('nic2', False) + pci = utils.get_pci_address('nic2', False) self.assertEqual(None, pci) def test_bind_dpdk_interfaces(self): @@ -241,3 +269,99 @@ class TestUtils(base.TestCase): self.assertEqual(utils._is_active_nic('enp129s2'), False) shutil.rmtree(tmpdir) + + def test_get_vpp_interface_name(self): + def test_execute(name, dummy1, dummy2=None, dummy3=None): + if 'systemctl' in name: + return None, None + if 'vppctl' in name: + return _VPPCTL_OUTPUT, None + + 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')) + self.assertRaises(utils.VppException, + utils._get_vpp_interface_name, '0000:09.0') + + def test_generate_vpp_config(self): + tmpdir = tempfile.mkdtemp() + config_path = os.path.join(tmpdir, 'startup.conf') + with open(config_path, 'w') as f: + f.write(_INITIAL_VPP_CONFIG) + vpp_exec_path = os.path.join(tmpdir, 'vpp-exec') + utils._VPP_EXEC_FILE = vpp_exec_path + + int1 = objects.VppInterface('em1', options="vlan-strip-offload off") + int1.pci_dev = '0000:00:09.0' + int2 = objects.VppInterface('em2') + int2.pci_dev = '0000:00:09.1' + interfaces = [int1, int2] + expected_config = ''' +unix { + exec %s + nodaemon + log /tmp/vpp.log + full-coredump +} + + +api-trace { + on +} + +api-segment { + gid vpp +} + +dpdk { + 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)) + + def test_update_vpp_mapping(self): + def test_get_dpdk_map(): + return [{'name': 'eth1', 'pci_address': '0000:00:09.0', + 'mac_address': '01:02:03:04:05:06', + 'driver': 'vfio-pci'}] + + self.stubs.Set(utils, '_get_dpdk_map', test_get_dpdk_map) + + def test_execute(name, dummy1, dummy2=None, dummy3=None): + return None, None + self.stubs.Set(processutils, 'execute', test_execute) + + def test_get_vpp_interface_name(pci_dev): + return 'GigabitEthernet0/9/0' + + self.stubs.Set(utils, '_get_vpp_interface_name', + test_get_vpp_interface_name) + + int1 = objects.VppInterface('eth1', options="vlan-strip-offload off") + int1.pci_dev = '0000:00:09.0' + int1.hwaddr = '01:02:03:04:05:06' + int2 = objects.VppInterface('eth2') + int2.pci_dev = '0000:00:09.1' + int2.hwaddr = '01:02:03:04:05:07' + interfaces = [int1, int2] + + utils.update_vpp_mapping(interfaces) + + contents = utils.get_file_data(utils._DPDK_MAPPING_FILE) + + dpdk_test = [{'name': 'eth1', 'pci_address': '0000:00:09.0', + 'mac_address': '01:02:03:04:05:06', + 'driver': 'vfio-pci'}, + {'name': 'eth2', 'pci_address': '0000:00:09.1', + 'mac_address': '01:02:03:04:05:07', + 'driver': 'vfio-pci'}] + dpdk_map = yaml.load(contents) if contents else [] + self.assertEqual(2, len(dpdk_map)) + self.assertListEqual(dpdk_test, dpdk_map) diff --git a/os_net_config/utils.py b/os_net_config/utils.py index 98bfe99..a4dfa4b 100644 --- a/os_net_config/utils.py +++ b/os_net_config/utils.py @@ -35,11 +35,19 @@ _SYS_CLASS_NET = '/sys/class/net' # driver: vfio-pci _DPDK_MAPPING_FILE = '/var/lib/os-net-config/dpdk_mapping.yaml' +# VPP startup operational configuration file. The content of this file will +# be executed when VPP starts as if typed from CLI. +_VPP_EXEC_FILE = '/etc/vpp/vpp-exec' + class OvsDpdkBindException(ValueError): pass +class VppException(ValueError): + pass + + def write_config(filename, data): with open(filename, 'w') as f: f.write(str(data)) @@ -82,7 +90,7 @@ def interface_mac(name): return f.read().rstrip() except IOError: # If the interface is bound to a DPDK driver, get the mac address from - # the dpdk mapping file as /sys files will be removed after binding. + # the DPDK mapping file as /sys files will be removed after binding. dpdk_mac_address = _get_dpdk_mac_address(name) if dpdk_mac_address: return dpdk_mac_address @@ -187,7 +195,7 @@ def diff(filename, data): def bind_dpdk_interfaces(ifname, driver, noop): - pci_address = _get_pci_address(ifname, noop) + pci_address = get_pci_address(ifname, noop) if not noop: if pci_address: # modbprobe of the driver has to be done before binding. @@ -218,7 +226,7 @@ def bind_dpdk_interfaces(ifname, driver, noop): {'name': ifname, 'driver': driver}) -def _get_pci_address(ifname, noop): +def get_pci_address(ifname, noop): # TODO(skramaja): Validate if the given interface supports dpdk if not noop: try: @@ -241,11 +249,10 @@ def _get_pci_address(ifname, noop): # Once the interface is bound to a DPDK driver, all the references to the # interface including '/sys' and '/proc', will be removed. And there is no # way to identify the nic name after it is bound. So, the DPDK bound nic info -# is stored persistently in a file and is used to for nic numbering on -# subsequent runs of os-net-config. +# is stored persistently in _DPDK_MAPPING_FILE and is used to for nic numbering +# on subsequent runs of os-net-config. def _update_dpdk_map(ifname, pci_address, mac_address, driver): - contents = get_file_data(_DPDK_MAPPING_FILE) - dpdk_map = yaml.load(contents) if contents else [] + dpdk_map = _get_dpdk_map() for item in dpdk_map: if item['pci_address'] == pci_address: item['name'] = ifname @@ -263,9 +270,215 @@ def _update_dpdk_map(ifname, pci_address, mac_address, driver): write_yaml_config(_DPDK_MAPPING_FILE, dpdk_map) +def _get_dpdk_map(): + contents = get_file_data(_DPDK_MAPPING_FILE) + dpdk_map = yaml.load(contents) if contents else [] + return dpdk_map + + def _get_dpdk_mac_address(name): contents = get_file_data(_DPDK_MAPPING_FILE) dpdk_map = yaml.load(contents) if contents else [] for item in dpdk_map: if item['name'] == name: return item['mac_address'] + + +def restart_vpp(vpp_interfaces): + for vpp_int in vpp_interfaces: + if 'vfio-pci' in vpp_int.uio_driver: + processutils.execute('modprobe', 'vfio-pci') + logger.info('Restarting VPP') + processutils.execute('systemctl', 'restart', 'vpp') + + +def _get_vpp_interface_name(pci_addr): + """Get VPP interface name from a given PCI address + + From a running VPP instance, attempt to find the interface name from + a given PCI address of a NIC. + + VppException will be raised if pci_addr is not formatted correctly. + ProcessExecutionError will be raised if VPP interface mapped to pci_addr + is not found. + + :param pci_addr: PCI address to lookup, in the form of DDDD:BB:SS.F, where + - DDDD = Domain + - BB = Bus Number + - SS = Slot number + - F = Function + :return: VPP interface name. None if an interface is not found. + """ + if not pci_addr: + return None + + try: + processutils.execute('systemctl', 'is-active', 'vpp') + out, err = processutils.execute('vppctl', 'show', 'interfaces') + m = re.search(r':([0-9a-fA-F]{2}):([0-9a-fA-F]{2}).([0-9a-fA-F])', + pci_addr) + if m: + formatted_pci = "%x/%x/%x" % (int(m.group(1), 16), + int(m.group(2), 16), + int(m.group(3), 16)) + else: + raise VppException('Invalid PCI address format: %s' % pci_addr) + + m = re.search(r'^(\w+%s)\s+' % formatted_pci, out, re.MULTILINE) + if m: + logger.debug('VPP interface found: %s' % m.group(1)) + return m.group(1) + else: + logger.debug('Interface with pci address %s not bound to VPP' + % pci_addr) + return None + except processutils.ProcessExecutionError: + logger.debug('Interface with pci address %s not bound to vpp' % + pci_addr) + + +def generate_vpp_config(vpp_config_path, vpp_interfaces): + """Generate configuration content for VPP + + Generate interface related configuration content for VPP. Current + configuration will be preserved, with interface related configurations + updated or inserted. The config only affects 'dpdk' section of VPP config + file, and only those lines affecting interfaces, specifically, lines + containing the following: + dpdk { + ... + dev <pci_dev> {<options>} + uio-driver <uio_driver_name> + ... + } + + :param vpp_config_path: VPP Configuration file path + :param vpp_interfaces: List of VPP interface objects + :return: updated VPP config content. + """ + + data = get_file_data(vpp_config_path) + + # Add interface config to 'dpdk' section + for vpp_interface in vpp_interfaces: + if vpp_interface.pci_dev: + logger.info('vpp interface %s pci dev: %s' + % (vpp_interface.name, vpp_interface.pci_dev)) + + if vpp_interface.options: + int_cfg = '%s {%s}' % (vpp_interface.pci_dev, + vpp_interface.options) + else: + int_cfg = vpp_interface.pci_dev + + # Make sure 'dpdk' section exists in the config + if not re.search(r'^\s*dpdk\s*\{', data, re.MULTILINE): + data += "\ndpdk {\n}\n" + + # Find existing config line for the device we are trying to + # configure, the line should look like 'dev <pci_dev> ...' + # If such config line is found, we will replace the line with + # appropriate configuration, otherwise, add a new config line + # in 'dpdk' section of the config. + m = re.search(r'^\s*dev\s+%s\s*(\{[^}]*\})?\s*' + % vpp_interface.pci_dev, data, + re.IGNORECASE | re.MULTILINE) + if m: + data = re.sub(m.group(0), ' dev %s\n' % int_cfg, data) + else: + data = re.sub(r'(^\s*dpdk\s*\{)', + r'\1\n dev %s\n' % int_cfg, + data, + flags=re.MULTILINE) + + if vpp_interface.uio_driver: + # Check if there is existing uio-driver configuration, if + # found, the line will be replaced with the appropriate + # configuration, otherwise, add a new line in 'dpdk' section. + m = re.search(r'^\s*uio-driver.*$', data, re.MULTILINE) + if m: + data = re.sub(m.group(0), r' uio-driver %s' + % vpp_interface.uio_driver, data) + else: + data = re.sub(r'(dpdk\s*\{)', + r'\1\n uio-driver %s' + % vpp_interface.uio_driver, + data) + else: + logger.debug('pci address not found for interface %s, may have' + 'already been bound to vpp' % vpp_interface.name) + + # Add start up script for VPP to config. This script will be executed by + # VPP on service start. + if not re.search(r'^\s*unix\s*\{', data, re.MULTILINE): + data += "\nunix {\n}\n" + + m = re.search(r'^\s*(exec|startup-config).*$', + data, + re.IGNORECASE | re.MULTILINE) + if m: + data = re.sub(m.group(0), ' exec %s' % _VPP_EXEC_FILE, data) + else: + data = re.sub(r'(^\s*unix\s*\{)', + r'\1\n exec %s' % _VPP_EXEC_FILE, + data, + flags=re.MULTILINE) + # Make sure startup script exists to avoid VPP startup failure. + open(_VPP_EXEC_FILE, 'a').close() + + return data + + +def update_vpp_mapping(vpp_interfaces): + """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 + """ + vpp_start_cli = "" + + 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) + if not vpp_name: + restart_vpp(vpp_interfaces) + else: + break + else: + raise VppException('Interface %s with pci address %s not ' + 'bound to vpp' + % (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) + + logger.info('Updating mapping for vpp interface %s:' + 'pci_dev: %s mac address: %s uio driver: %s' + % (vpp_int.name, vpp_int.pci_dev, vpp_int.hwaddr, + 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') + if diff(_VPP_EXEC_FILE, vpp_start_cli): + write_config(_VPP_EXEC_FILE, vpp_start_cli) + restart_vpp(vpp_interfaces) |