Add a persist_mapping option to the mapping file

This adds the option to permanently rewrite the configuration so the
aliases are used instead of the system name.  This is useful where
you have a variety of hardware and you want to have consistent device
naming accross all platforms - this allows you to essentially rename
the interfaces permanently so they match the abstracted nicN names.

Note, this needs to be run with --cleanup or the old (now conflicting)
configs will still be in place, and it may require a reboot before
the changes are fully applied.

Change-Id: I5af146e764b72c4beaa41c549fabff0af8802152
This commit is contained in:
Steven Hardy 2015-01-21 17:24:08 +00:00
parent 6945fe5afd
commit a4ad189558
7 changed files with 81 additions and 20 deletions

View File

@ -1,6 +1,10 @@
# This can be used with the -m option to override the # This can be used with the -m option to override the
# default mapping of the nicN aliases in configs # default mapping of the nicN aliases in configs
# The mapping can specify either a device name or a mac address # The mapping can specify either a device name or a mac address
# If --persist-mapping is specified, we write the device aliases
# config instead of the system names, e.g we actually configure
# nic1 intead of em3. This is probably best used with --cleanup
# to remove the stale configs e.g for em3
interface_mapping: interface_mapping:
nic1: em3 nic1: em3
nic2: em1 nic2: em1

View File

@ -74,6 +74,14 @@ def parse_opts(argv):
help="Cleanup unconfigured interfaces.", help="Cleanup unconfigured interfaces.",
required=False) required=False)
parser.add_argument(
'--persist-mapping',
dest="persist_mapping",
action='store_true',
help="Make aliases defined in the mapping file permanent "
"(WARNING, permanently renames nics).",
required=False)
opts = parser.parse_args(argv[1:]) opts = parser.parse_args(argv[1:])
return opts return opts
@ -139,13 +147,18 @@ def main(argv=sys.argv):
# mappings by specifying a specific nicN->name or nicN->MAC mapping # mappings by specifying a specific nicN->name or nicN->MAC mapping
if os.path.exists(opts.mapping_file): if os.path.exists(opts.mapping_file):
with open(opts.mapping_file) as cf: with open(opts.mapping_file) as cf:
iface_mapping = yaml.load(cf.read()).get("interface_mapping") iface_map = yaml.load(cf.read())
iface_mapping = iface_map.get("interface_mapping")
logger.debug('interface_mapping JSON: %s' % str(iface_mapping)) logger.debug('interface_mapping JSON: %s' % str(iface_mapping))
persist_mapping = opts.persist_mapping
logger.debug('persist_mapping: %s' % persist_mapping)
else: else:
iface_mapping = None iface_mapping = None
persist_mapping = False
for iface_json in iface_array: for iface_json in iface_array:
iface_json.update({'nic_mapping': iface_mapping}) iface_json.update({'nic_mapping': iface_mapping})
iface_json.update({'persist_mapping': persist_mapping})
obj = objects.object_from_json(iface_json) obj = objects.object_from_json(iface_json)
provider.add_object(obj) provider.add_object(obj)
files_changed = provider.apply(noop=opts.noop, cleanup=opts.cleanup) files_changed = provider.apply(noop=opts.noop, cleanup=opts.cleanup)

View File

@ -129,6 +129,9 @@ class ENINetConfig(os_net_config.NetConfig):
if interface.mtu != 1500: if interface.mtu != 1500:
data += " mtu %i\n" % interface.mtu data += " mtu %i\n" % interface.mtu
if interface.hwaddr:
raise NotImplemented("hwaddr is not implemented.")
if ovs_extra: if ovs_extra:
data += " ovs_extra %s\n" % " -- ".join(ovs_extra) data += " ovs_extra %s\n" % " -- ".join(ovs_extra)

View File

@ -142,6 +142,8 @@ class IfcfgNetConfig(os_net_config.NetConfig):
first_v6 = v6_addresses[0] first_v6 = v6_addresses[0]
data += "IPV6_AUTOCONF=no\n" data += "IPV6_AUTOCONF=no\n"
data += "IPV6ADDR=%s\n" % first_v6.ip data += "IPV6ADDR=%s\n" % first_v6.ip
if base_opt.hwaddr:
data += "HWADDR=%s\n" % base_opt.hwaddr
if ovs_extra: if ovs_extra:
data += "OVS_EXTRA=\"%s\"\n" % " -- ".join(ovs_extra) data += "OVS_EXTRA=\"%s\"\n" % " -- ".join(ovs_extra)
return data return data

View File

@ -129,12 +129,20 @@ class _BaseOpts(object):
"""Base abstraction for logical port options.""" """Base abstraction for logical port options."""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[], def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[],
routes=[], mtu=1500, primary=False, nic_mapping=None): routes=[], mtu=1500, primary=False, nic_mapping=None,
persist_mapping=False):
numbered_nic_names = _numbered_nics(nic_mapping) numbered_nic_names = _numbered_nics(nic_mapping)
self.hwaddr = None
if name in numbered_nic_names: if name in numbered_nic_names:
self.name = numbered_nic_names[name] if persist_mapping:
self.name = name
hwname = numbered_nic_names[name]
self.hwaddr = utils.interface_mac(hwname)
else:
self.name = numbered_nic_names[name]
else: else:
self.name = name self.name = name
self.mtu = mtu self.mtu = mtu
self.use_dhcp = use_dhcp self.use_dhcp = use_dhcp
self.use_dhcpv6 = use_dhcpv6 self.use_dhcpv6 = use_dhcpv6
@ -192,22 +200,25 @@ class _BaseOpts(object):
raise InvalidConfigException(msg) raise InvalidConfigException(msg)
nic_mapping = json.get('nic_mapping') nic_mapping = json.get('nic_mapping')
persist_mapping = json.get('persist_mapping')
if include_primary: if include_primary:
return (use_dhcp, use_dhcpv6, addresses, routes, mtu, primary, return (use_dhcp, use_dhcpv6, addresses, routes, mtu, primary,
nic_mapping) nic_mapping, persist_mapping)
else: else:
return (use_dhcp, use_dhcpv6, addresses, routes, mtu, return (use_dhcp, use_dhcpv6, addresses, routes, mtu,
nic_mapping) nic_mapping, persist_mapping)
class Interface(_BaseOpts): class Interface(_BaseOpts):
"""Base class for network interfaces.""" """Base class for network interfaces."""
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[], def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[],
routes=[], mtu=1500, primary=False, nic_mapping=None): routes=[], mtu=1500, primary=False, nic_mapping=None,
persist_mapping=False):
super(Interface, self).__init__(name, use_dhcp, use_dhcpv6, addresses, super(Interface, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
routes, mtu, primary, nic_mapping) routes, mtu, primary, nic_mapping,
persist_mapping)
@staticmethod @staticmethod
def from_json(json): def from_json(json):
@ -225,10 +236,11 @@ class Vlan(_BaseOpts):
def __init__(self, device, vlan_id, use_dhcp=False, use_dhcpv6=False, def __init__(self, device, vlan_id, use_dhcp=False, use_dhcpv6=False,
addresses=[], routes=[], mtu=1500, primary=False, addresses=[], routes=[], mtu=1500, primary=False,
nic_mapping=None): nic_mapping=None, persist_mapping=False):
name = 'vlan%i' % vlan_id name = 'vlan%i' % vlan_id
super(Vlan, self).__init__(name, use_dhcp, use_dhcpv6, addresses, super(Vlan, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
routes, mtu, primary, nic_mapping) routes, mtu, primary, nic_mapping,
persist_mapping)
self.vlan_id = int(vlan_id) self.vlan_id = int(vlan_id)
numbered_nic_names = _numbered_nics(nic_mapping) numbered_nic_names = _numbered_nics(nic_mapping)
@ -251,9 +263,10 @@ class OvsBridge(_BaseOpts):
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[], def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[],
routes=[], mtu=1500, members=[], ovs_options=None, routes=[], mtu=1500, members=[], ovs_options=None,
ovs_extra=[], nic_mapping=None): ovs_extra=[], nic_mapping=None, persist_mapping=False):
super(OvsBridge, self).__init__(name, use_dhcp, use_dhcpv6, addresses, super(OvsBridge, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
routes, mtu, False, nic_mapping) routes, mtu, False, nic_mapping,
persist_mapping)
self.members = members self.members = members
self.ovs_options = ovs_options self.ovs_options = ovs_options
self.ovs_extra = ovs_extra self.ovs_extra = ovs_extra
@ -272,8 +285,9 @@ class OvsBridge(_BaseOpts):
@staticmethod @staticmethod
def from_json(json): def from_json(json):
name = _get_required_field(json, 'name', 'OvsBridge') name = _get_required_field(json, 'name', 'OvsBridge')
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping (use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
) = _BaseOpts.base_opts_from_json(json, include_primary=False) persist_mapping) = _BaseOpts.base_opts_from_json(
json, include_primary=False)
ovs_options = json.get('ovs_options') ovs_options = json.get('ovs_options')
ovs_extra = json.get('ovs_extra', []) ovs_extra = json.get('ovs_extra', [])
members = [] members = []
@ -291,7 +305,8 @@ class OvsBridge(_BaseOpts):
return OvsBridge(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6, return OvsBridge(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
addresses=addresses, routes=routes, mtu=mtu, addresses=addresses, routes=routes, mtu=mtu,
members=members, ovs_options=ovs_options, members=members, ovs_options=ovs_options,
ovs_extra=ovs_extra, nic_mapping=nic_mapping) ovs_extra=ovs_extra, nic_mapping=nic_mapping,
persist_mapping=persist_mapping)
class OvsBond(_BaseOpts): class OvsBond(_BaseOpts):
@ -299,9 +314,11 @@ class OvsBond(_BaseOpts):
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[], def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=[],
routes=[], mtu=1500, primary=False, members=[], routes=[], mtu=1500, primary=False, members=[],
ovs_options=None, ovs_extra=[], nic_mapping=None): ovs_options=None, ovs_extra=[], nic_mapping=None,
persist_mapping=False):
super(OvsBond, self).__init__(name, use_dhcp, use_dhcpv6, addresses, super(OvsBond, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
routes, mtu, primary, nic_mapping) routes, mtu, primary, nic_mapping,
persist_mapping)
self.members = members self.members = members
self.ovs_options = ovs_options self.ovs_options = ovs_options
self.ovs_extra = ovs_extra self.ovs_extra = ovs_extra
@ -318,8 +335,9 @@ class OvsBond(_BaseOpts):
@staticmethod @staticmethod
def from_json(json): def from_json(json):
name = _get_required_field(json, 'name', 'OvsBond') name = _get_required_field(json, 'name', 'OvsBond')
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping (use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
) = _BaseOpts.base_opts_from_json(json, include_primary=False) persist_mapping) = _BaseOpts.base_opts_from_json(
json, include_primary=False)
ovs_options = json.get('ovs_options') ovs_options = json.get('ovs_options')
ovs_extra = json.get('ovs_extra', []) ovs_extra = json.get('ovs_extra', [])
members = [] members = []
@ -337,4 +355,5 @@ class OvsBond(_BaseOpts):
return OvsBond(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6, return OvsBond(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
addresses=addresses, routes=routes, mtu=mtu, addresses=addresses, routes=routes, mtu=mtu,
members=members, ovs_options=ovs_options, members=members, ovs_options=ovs_options,
ovs_extra=ovs_extra, nic_mapping=nic_mapping) ovs_extra=ovs_extra, nic_mapping=nic_mapping,
persist_mapping=persist_mapping)

View File

@ -36,9 +36,10 @@ class TestCase(testtools.TestCase):
super(TestCase, self).setUp() super(TestCase, self).setUp()
self.stubs = stubout.StubOutForTesting() self.stubs = stubout.StubOutForTesting()
self.stubbed_numbered_nics = {}
def dummy_numbered_nics(nic_mapping=None): def dummy_numbered_nics(nic_mapping=None):
return {} return self.stubbed_numbered_nics
if self.stub_numbered_nics: if self.stub_numbered_nics:
self.stubs.Set(objects, '_numbered_nics', dummy_numbered_nics) self.stubs.Set(objects, '_numbered_nics', dummy_numbered_nics)

View File

@ -36,6 +36,8 @@ IPADDR=192.168.1.2
NETMASK=255.255.255.0 NETMASK=255.255.255.0
""" """
_V4_IFCFG_MAPPED = _V4_IFCFG.replace('em1', 'nic1') + "HWADDR=a1:b2:c3:d4:e5\n"
_V6_IFCFG = _BASE_IFCFG + """IPV6INIT=yes _V6_IFCFG = _BASE_IFCFG + """IPV6INIT=yes
IPV6_AUTOCONF=no IPV6_AUTOCONF=no
IPV6ADDR=2001:abc:a:: IPV6ADDR=2001:abc:a::
@ -141,6 +143,23 @@ class TestIfcfgNetConfig(base.TestCase):
self.assertEqual(_V4_IFCFG, self.get_interface_config()) self.assertEqual(_V4_IFCFG, self.get_interface_config())
self.assertEqual('', self.get_route_config()) self.assertEqual('', self.get_route_config())
def test_add_interface_map_persisted(self):
def test_interface_mac(name):
macs = {'em1': 'a1:b2:c3:d4:e5'}
return macs[name]
self.stubs.Set(utils, 'interface_mac', test_interface_mac)
nic_mapping = {'nic1': 'em1'}
self.stubbed_numbered_nics = nic_mapping
v4_addr = objects.Address('192.168.1.2/24')
interface = objects.Interface('nic1', addresses=[v4_addr],
nic_mapping=nic_mapping,
persist_mapping=True)
self.assertEqual('a1:b2:c3:d4:e5', interface.hwaddr)
self.provider.add_interface(interface)
self.assertEqual(_V4_IFCFG_MAPPED, self.get_interface_config('nic1'))
self.assertEqual('', self.get_route_config('nic1'))
def test_add_interface_with_v6(self): def test_add_interface_with_v6(self):
v6_addr = objects.Address('2001:abc:a::/64') v6_addr = objects.Address('2001:abc:a::/64')
interface = objects.Interface('em1', addresses=[v6_addr]) interface = objects.Interface('em1', addresses=[v6_addr])