def test_bond_conversion(self): # light testing of bond conversion and eni rendering of bond ncfg = openstack.convert_net_json(NETWORK_DATA_BOND, known_macs=KNOWN_MACS) eni_renderer = eni.Renderer() eni_renderer.render_network_state( network_state.parse_net_config_data(ncfg), target=self.tmp) with open(os.path.join(self.tmp, "etc", "network", "interfaces"), 'r') as f: eni_rendering = f.read() # Verify there are expected interfaces in the net config. interfaces = sorted( [i['name'] for i in ncfg['config'] if i['type'] in ('vlan', 'bond', 'physical')]) self.assertEqual( sorted(["oeth0", "oeth1", "bond0", "bond0.602", "bond0.612"]), interfaces) words = eni_rendering.split() # 'eth0' and 'eth1' are the ids. because their mac adresses # map to other names, we should not see them in the ENI self.assertNotIn('eth0', words) self.assertNotIn('eth1', words) # oeth0 and oeth1 are the interface names for eni. # bond0 will be generated for the bond. Each should be auto. self.assertIn("auto oeth0", eni_rendering) self.assertIn("auto oeth1", eni_rendering) self.assertIn("auto bond0", eni_rendering) # The bond should have the given mac address pos = eni_rendering.find("auto bond0") self.assertIn(BOND_MAC, eni_rendering[pos:])
def test_network_config_is_converted(self, on_first_boot): """Verify that network_data is converted and present on ds object.""" populate_dir(self.tmp, CFG_DRIVE_FILES_V2) myds = cfg_ds_from_dir(self.tmp) network_config = openstack.convert_net_json(NETWORK_DATA, known_macs=KNOWN_MACS) self.assertEqual(myds.network_config, network_config)
def test_bond_conversion(self): # light testing of bond conversion and eni rendering of bond ncfg = openstack.convert_net_json(NETWORK_DATA_BOND, known_macs=KNOWN_MACS) eni_renderer = eni.Renderer() eni_renderer.render_network_state( self.tmp, network_state.parse_net_config_data(ncfg)) with open(os.path.join(self.tmp, "etc", "network", "interfaces"), 'r') as f: eni_rendering = f.read() # Verify there are expected interfaces in the net config. interfaces = sorted( [i['name'] for i in ncfg['config'] if i['type'] in ('vlan', 'bond', 'physical')]) self.assertEqual( sorted(["oeth0", "oeth1", "bond0", "bond0.602", "bond0.612"]), interfaces) words = eni_rendering.split() # 'eth0' and 'eth1' are the ids. because their mac adresses # map to other names, we should not see them in the ENI self.assertNotIn('eth0', words) self.assertNotIn('eth1', words) # oeth0 and oeth1 are the interface names for eni. # bond0 will be generated for the bond. Each should be auto. self.assertIn("auto oeth0", eni_rendering) self.assertIn("auto oeth1", eni_rendering) self.assertIn("auto bond0", eni_rendering)
def test_network_config_conversion_dhcp6(self): """Test some ipv6 input network json and check the expected conversions.""" in_data = { 'links': [ { 'vif_id': '2ecc7709-b3f7-4448-9580-e1ec32d75bbd', 'ethernet_mac_address': 'fa:16:3e:69:b0:58', 'type': 'ovs', 'mtu': None, 'id': 'tap2ecc7709-b3' }, { 'vif_id': '2f88d109-5b57-40e6-af32-2472df09dc33', 'ethernet_mac_address': 'fa:16:3e:d4:57:ad', 'type': 'ovs', 'mtu': None, 'id': 'tap2f88d109-5b' }, ], 'networks': [ { 'link': 'tap2ecc7709-b3', 'type': 'ipv6_dhcpv6-stateless', 'network_id': '6d6357ac-0f70-4afa-8bd7-c274cc4ea235', 'id': 'network0' }, { 'link': 'tap2f88d109-5b', 'type': 'ipv6_dhcpv6-stateful', 'network_id': 'd227a9b3-6960-4d94-8976-ee5788b44f54', 'id': 'network1' }, ] } out_data = { 'version': 1, 'config': [{ 'mac_address': 'fa:16:3e:69:b0:58', 'mtu': None, 'name': 'enp0s1', 'subnets': [{ 'type': 'ipv6_dhcpv6-stateless' }], 'type': 'physical' }, { 'mac_address': 'fa:16:3e:d4:57:ad', 'mtu': None, 'name': 'enp0s2', 'subnets': [{ 'type': 'ipv6_dhcpv6-stateful' }], 'type': 'physical', 'accept-ra': True }], } conv_data = openstack.convert_net_json(in_data, known_macs=KNOWN_MACS) self.assertEqual(out_data, conv_data)
def test_conversion_with_tap(self): ncfg = openstack.convert_net_json(NETWORK_DATA_3, known_macs=KNOWN_MACS) physicals = set() for i in ncfg["config"]: if i.get("type") == "physical": physicals.add(i["name"]) self.assertEqual(physicals, set(("foo1", "foo2")))
def test_conversion_with_tap(self): ncfg = openstack.convert_net_json(NETWORK_DATA_3, known_macs=KNOWN_MACS) physicals = set() for i in ncfg['config']: if i.get('type') == "physical": physicals.add(i['name']) self.assertEqual(physicals, set(('foo1', 'foo2')))
def test_network_config_conversion_dhcp6(self): """Test some ipv6 input network json and check the expected conversions.""" in_data = { "links": [ { "vif_id": "2ecc7709-b3f7-4448-9580-e1ec32d75bbd", "ethernet_mac_address": "fa:16:3e:69:b0:58", "type": "ovs", "mtu": None, "id": "tap2ecc7709-b3", }, { "vif_id": "2f88d109-5b57-40e6-af32-2472df09dc33", "ethernet_mac_address": "fa:16:3e:d4:57:ad", "type": "ovs", "mtu": None, "id": "tap2f88d109-5b", }, ], "networks": [ { "link": "tap2ecc7709-b3", "type": "ipv6_dhcpv6-stateless", "network_id": "6d6357ac-0f70-4afa-8bd7-c274cc4ea235", "id": "network0", }, { "link": "tap2f88d109-5b", "type": "ipv6_dhcpv6-stateful", "network_id": "d227a9b3-6960-4d94-8976-ee5788b44f54", "id": "network1", }, ], } out_data = { "version": 1, "config": [ { "mac_address": "fa:16:3e:69:b0:58", "mtu": None, "name": "enp0s1", "subnets": [{"type": "ipv6_dhcpv6-stateless"}], "type": "physical", }, { "mac_address": "fa:16:3e:d4:57:ad", "mtu": None, "name": "enp0s2", "subnets": [{"type": "ipv6_dhcpv6-stateful"}], "type": "physical", "accept-ra": True, }, ], } conv_data = openstack.convert_net_json(in_data, known_macs=KNOWN_MACS) self.assertEqual(out_data, conv_data)
def test_convert_reads_system_prefers_name(self, get_interfaces_by_mac): macs = KNOWN_MACS.copy() macs.update({'fa:16:3e:05:30:fe': 'foonic1', 'fa:16:3e:69:b0:58': 'ens1'}) get_interfaces_by_mac.return_value = macs ncfg = openstack.convert_net_json(NETWORK_DATA) expected = set(['nic0', 'ens1', 'enp0s2']) found = self._getnames_in_config(ncfg) self.assertEqual(found, expected)
def populate_ds_from_read_config(cfg_ds, source, results): """Patch the DataSourceConfigDrive from the results of read_config_drive_dir hopefully in line with what it would have if cfg_ds.get_data had been successfully called""" cfg_ds.source = source cfg_ds.metadata = results.get('metadata') cfg_ds.ec2_metadata = results.get('ec2-metadata') cfg_ds.userdata_raw = results.get('userdata') cfg_ds.version = results.get('version') cfg_ds.network_json = results.get('networkdata') cfg_ds._network_config = openstack.convert_net_json( cfg_ds.network_json, known_macs=KNOWN_MACS)
def network_config(self): if self._network_config is None: if self.network_json is not None: LOG.debug("network config provided via network_json") self._network_config = openstack.convert_net_json( self.network_json, known_macs=self.known_macs) elif self.network_eni is not None: self._network_config = eni.convert_eni_data(self.network_eni) LOG.debug("network config provided via converted eni data") else: LOG.debug("no network configuration available") return self._network_config
def test_phy_types(self): """Verify the different known physical types are handled.""" # network_data.json example from # https://docs.openstack.org/nova/latest/user/metadata.html mac0 = "fa:16:3e:9c:bf:3d" net_json = { "links": [{ "ethernet_mac_address": mac0, "id": "tapcd9f6d46-4a", "mtu": None, "type": "bridge", "vif_id": "cd9f6d46-4a3a-43ab-a466-994af9db96fc", }], "networks": [{ "id": "network0", "link": "tapcd9f6d46-4a", "network_id": "99e88329-f20d-4741-9593-25bf07847b16", "type": "ipv4_dhcp", }], "services": [{ "address": "8.8.8.8", "type": "dns" }], } macs = {mac0: "eth0"} expected = { "version": 1, "config": [ { "mac_address": "fa:16:3e:9c:bf:3d", "mtu": None, "name": "eth0", "subnets": [{ "type": "dhcp4" }], "type": "physical", }, { "address": "8.8.8.8", "type": "nameserver" }, ], } for t in openstack.KNOWN_PHYSICAL_TYPES: net_json["links"][0]["type"] = t self.assertEqual( expected, openstack.convert_net_json(network_json=net_json, known_macs=macs), )
def network_config(self): if self.platform != Platforms.OS_CODE: # If deployed from template, an agent in the provisioning # environment handles networking configuration. Not cloud-init. return {'config': 'disabled', 'version': 1} if self._network_config is None: if self.network_json is not None: LOG.debug("network config provided via network_json") self._network_config = openstack.convert_net_json( self.network_json, known_macs=None) else: LOG.debug("no network configuration available.") return self._network_config
def test_openstack_rendering_samples(self): for os_sample in OS_SAMPLES: render_dir = self.tmp_dir() ex_input = os_sample['in_data'] ex_mac_addrs = os_sample['in_macs'] network_cfg = openstack.convert_net_json(ex_input, known_macs=ex_mac_addrs) ns = network_state.parse_net_config_data(network_cfg, skip_broken=False) renderer = sysconfig.Renderer() renderer.render_network_state(ns, render_dir) for fn, expected_content in os_sample.get('out_sysconfig', []): with open(os.path.join(render_dir, fn)) as fh: self.assertEqual(expected_content, fh.read())
def test_vlan(self): # light testing of vlan config conversion and eni rendering ncfg = openstack.convert_net_json(NETWORK_DATA_VLAN, known_macs=KNOWN_MACS) eni_renderer = eni.Renderer() eni_renderer.render_network_state( self.tmp, network_state.parse_net_config_data(ncfg)) with open(os.path.join(self.tmp, "etc", "network", "interfaces"), 'r') as f: eni_rendering = f.read() self.assertIn("iface enp0s1", eni_rendering) self.assertIn("address 10.0.1.5", eni_rendering) self.assertIn("auto enp0s1.602", eni_rendering)
def test_vlan(self): # light testing of vlan config conversion and eni rendering ncfg = openstack.convert_net_json(NETWORK_DATA_VLAN, known_macs=KNOWN_MACS) eni_renderer = eni.Renderer() eni_renderer.render_network_state( network_state.parse_net_config_data(ncfg), target=self.tmp) with open(os.path.join(self.tmp, "etc", "network", "interfaces"), 'r') as f: eni_rendering = f.read() self.assertIn("iface enp0s1", eni_rendering) self.assertIn("address 10.0.1.5", eni_rendering) self.assertIn("auto enp0s1.602", eni_rendering)
def test_mac_addrs_can_be_upper_case(self): # input mac addresses on rackspace may be upper case my_netdata = deepcopy(NETWORK_DATA) for link in my_netdata['links']: link['ethernet_mac_address'] = link['ethernet_mac_address'].upper() ncfg = openstack.convert_net_json(my_netdata, known_macs=KNOWN_MACS) config_name2mac = {} for n in ncfg['config']: if n['type'] == 'physical': config_name2mac[n['name']] = n['mac_address'] expected = {'nic0': 'fa:16:3e:05:30:fe', 'enp0s1': 'fa:16:3e:69:b0:58', 'enp0s2': 'fa:16:3e:d4:57:ad'} self.assertEqual(expected, config_name2mac)
def test_openstack_rendering_samples(self): tmp_dir = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, tmp_dir) render_dir = os.path.join(tmp_dir, "render") for os_sample in OS_SAMPLES: ex_input = os_sample['in_data'] ex_mac_addrs = os_sample['in_macs'] network_cfg = openstack.convert_net_json( ex_input, known_macs=ex_mac_addrs) ns = network_state.parse_net_config_data(network_cfg, skip_broken=False) renderer = sysconfig.Renderer() renderer.render_network_state(render_dir, ns) for fn, expected_content in os_sample.get('out_sysconfig', []): with open(os.path.join(render_dir, fn)) as fh: self.assertEqual(expected_content, fh.read())
def test_mac_addrs_can_be_upper_case(self): # input mac addresses on rackspace may be upper case my_netdata = deepcopy(NETWORK_DATA) for link in my_netdata["links"]: link["ethernet_mac_address"] = link["ethernet_mac_address"].upper() ncfg = openstack.convert_net_json(my_netdata, known_macs=KNOWN_MACS) config_name2mac = {} for n in ncfg["config"]: if n["type"] == "physical": config_name2mac[n["name"]] = n["mac_address"] expected = { "nic0": "fa:16:3e:05:30:fe", "enp0s1": "fa:16:3e:69:b0:58", "enp0s2": "fa:16:3e:d4:57:ad", } self.assertEqual(expected, config_name2mac)
def test_unknown_device_types_accepted(self): # If we don't recognise a link, we should treat it as physical for a # best-effort boot my_netdata = deepcopy(NETWORK_DATA) my_netdata['links'][0]['type'] = 'my-special-link-type' ncfg = openstack.convert_net_json(my_netdata, known_macs=KNOWN_MACS) config_name2mac = {} for n in ncfg['config']: if n['type'] == 'physical': config_name2mac[n['name']] = n['mac_address'] expected = {'nic0': 'fa:16:3e:05:30:fe', 'enp0s1': 'fa:16:3e:69:b0:58', 'enp0s2': 'fa:16:3e:d4:57:ad'} self.assertEqual(expected, config_name2mac) # We should, however, warn the user that we don't recognise the type self.assertIn('Unknown network_data link type (my-special-link-type)', self.logs.getvalue())
def test_conversion_with_route(self): ncfg = openstack.convert_net_json(NETWORK_DATA_2, known_macs=KNOWN_MACS) # not the best test, but see that we get a route in the # network config and that it gets rendered to an ENI file routes = [] for n in ncfg['config']: for s in n.get('subnets', []): routes.extend(s.get('routes', [])) self.assertIn( {'network': '0.0.0.0', 'netmask': '0.0.0.0', 'gateway': '2.2.2.9'}, routes) eni_renderer = eni.Renderer() eni_renderer.render_network_state( self.tmp, network_state.parse_net_config_data(ncfg)) with open(os.path.join(self.tmp, "etc", "network", "interfaces"), 'r') as f: eni_rendering = f.read() self.assertIn("route add default gw 2.2.2.9", eni_rendering)
def test_conversion_with_route(self): ncfg = openstack.convert_net_json(NETWORK_DATA_2, known_macs=KNOWN_MACS) # not the best test, but see that we get a route in the # network config and that it gets rendered to an ENI file routes = [] for n in ncfg['config']: for s in n.get('subnets', []): routes.extend(s.get('routes', [])) self.assertIn( {'network': '0.0.0.0', 'netmask': '0.0.0.0', 'gateway': '2.2.2.9'}, routes) eni_renderer = eni.Renderer() eni_renderer.render_network_state( network_state.parse_net_config_data(ncfg), target=self.tmp) with open(os.path.join(self.tmp, "etc", "network", "interfaces"), 'r') as f: eni_rendering = f.read() self.assertIn("route add default gw 2.2.2.9", eni_rendering)
def network_config(self): """Return a network config dict for rendering ENI or netplan files.""" if self._network_config != sources.UNSET: return self._network_config # Xenial, Artful and Bionic will not provide # network_config by default unless configured in /etc/cloud/cloud.cfg*. if util.is_false(self.ds_cfg.get('apply_network_config', False)): self._network_config = None return self._network_config if self.network_json == sources.UNSET: # this would happen if get_data hadn't been called. leave as UNSET LOG.warning( 'Unexpected call to network_config when network_json is None.') return None LOG.debug('network config provided via network_json') self._network_config = openstack.convert_net_json(self.network_json, known_macs=None) return self._network_config
def set_network_config(action="", id_net_name=""): if is_cloud_init_running(): return init = stages.Init() init.read_cfg() logging.setupLogging(init.cfg) use_legacy_networking = False try: openstack.convert_net_json init.distro.apply_network_config except AttributeError: use_legacy_networking = True if use_legacy_networking: # old network interfaces files in Debian format # required on Ubuntu 14.04, as cloud-init does not # know about v2 metadata. # Legacy network metadata appears only if # on compute node, in nova.conf: # [DEFAULT} # flat_injected = True net_cfg_raw = try_read_url(LEGACY_MAGIC_URL, init.distro.name) init.distro.apply_network(net_cfg_raw, bring_up=True) return if id_net_name and action == "remove": set_manual_interface(id_net_name) net_cfg_raw = try_read_url(MAGIC_URL, init.distro.name) net_cfg_raw = json.loads(net_cfg_raw) netcfg = openstack.convert_net_json(net_cfg_raw) init.distro.apply_network_config_names(netcfg) init.distro.apply_network_config(netcfg, bring_up=True) try_reset_network(init.distro.name)
def network_config(self): """Return a network config dict for rendering ENI or netplan files.""" if self._network_config != sources.UNSET: return self._network_config # RELEASE_BLOCKER: SRU to Xenial and Artful SRU should not provide # network_config by default unless configured in /etc/cloud/cloud.cfg*. # Patch Xenial and Artful before release to default to False. if util.is_false(self.ds_cfg.get("apply_network_config", True)): self._network_config = None return self._network_config if self.network_json == sources.UNSET: # this would happen if get_data hadn't been called. leave as UNSET LOG.warning( "Unexpected call to network_config when network_json is None.") return None LOG.debug("network config provided via network_json") self._network_config = openstack.convert_net_json(self.network_json, known_macs=None) return self._network_config
def network_config(self): """Return a network config dict for rendering ENI or netplan files.""" if self._network_config != sources.UNSET: return self._network_config # RELEASE_BLOCKER: SRU to Xenial and Artful SRU should not provide # network_config by default unless configured in /etc/cloud/cloud.cfg*. # Patch Xenial and Artful before release to default to False. if util.is_false(self.ds_cfg.get('apply_network_config', True)): self._network_config = None return self._network_config if self.network_json == sources.UNSET: # this would happen if get_data hadn't been called. leave as UNSET LOG.warning( 'Unexpected call to network_config when network_json is None.') return None LOG.debug('network config provided via network_json') self._network_config = openstack.convert_net_json( self.network_json, known_macs=None) return self._network_config
def test_unknown_device_types_accepted(self): # If we don't recognise a link, we should treat it as physical for a # best-effort boot my_netdata = deepcopy(NETWORK_DATA) my_netdata["links"][0]["type"] = "my-special-link-type" ncfg = openstack.convert_net_json(my_netdata, known_macs=KNOWN_MACS) config_name2mac = {} for n in ncfg["config"]: if n["type"] == "physical": config_name2mac[n["name"]] = n["mac_address"] expected = { "nic0": "fa:16:3e:05:30:fe", "enp0s1": "fa:16:3e:69:b0:58", "enp0s2": "fa:16:3e:d4:57:ad", } self.assertEqual(expected, config_name2mac) # We should, however, warn the user that we don't recognise the type self.assertIn( "Unknown network_data link type (my-special-link-type)", self.logs.getvalue(), )
def test_network_config_conversions(self): """Tests a bunch of input network json and checks the expected conversions.""" in_datas = [ NETWORK_DATA, { 'services': [{'type': 'dns', 'address': '172.19.0.12'}], 'networks': [{ 'network_id': 'dacd568d-5be6-4786-91fe-750c374b78b4', 'type': 'ipv4', 'netmask': '255.255.252.0', 'link': 'tap1a81968a-79', 'routes': [{ 'netmask': '0.0.0.0', 'network': '0.0.0.0', 'gateway': '172.19.3.254', }], 'ip_address': '172.19.1.34', 'id': 'network0', }], 'links': [{ 'type': 'bridge', 'vif_id': '1a81968a-797a-400f-8a80-567f997eb93f', 'ethernet_mac_address': 'fa:16:3e:ed:9a:59', 'id': 'tap1a81968a-79', 'mtu': None, }], }, ] out_datas = [ { 'version': 1, 'config': [ { 'subnets': [{'type': 'dhcp4'}], 'type': 'physical', 'mac_address': 'fa:16:3e:69:b0:58', 'name': 'enp0s1', 'mtu': None, }, { 'subnets': [{'type': 'dhcp4'}], 'type': 'physical', 'mac_address': 'fa:16:3e:d4:57:ad', 'name': 'enp0s2', 'mtu': None, }, { 'subnets': [{'type': 'dhcp4'}], 'type': 'physical', 'mac_address': 'fa:16:3e:05:30:fe', 'name': 'nic0', 'mtu': None, }, { 'type': 'nameserver', 'address': '199.204.44.24', }, { 'type': 'nameserver', 'address': '199.204.47.54', } ], }, { 'version': 1, 'config': [ { 'name': 'foo3', 'mac_address': 'fa:16:3e:ed:9a:59', 'mtu': None, 'type': 'physical', 'subnets': [ { 'address': '172.19.1.34', 'netmask': '255.255.252.0', 'type': 'static', 'ipv4': True, 'routes': [{ 'gateway': '172.19.3.254', 'netmask': '0.0.0.0', 'network': '0.0.0.0', }], } ] }, { 'type': 'nameserver', 'address': '172.19.0.12', } ], }, ] for in_data, out_data in zip(in_datas, out_datas): conv_data = openstack.convert_net_json(in_data, known_macs=KNOWN_MACS) self.assertEqual(out_data, conv_data)
def main(): parser = argparse.ArgumentParser() parser.add_argument("--network-data", "-p", type=open, metavar="PATH", required=True) parser.add_argument("--kind", "-k", choices=['eni', 'network_data.json', 'yaml'], required=True) parser.add_argument("-d", "--directory", metavar="PATH", help="directory to place output in", required=True) parser.add_argument("-m", "--mac", metavar="name,mac", action='append', help="interface name to mac mapping") parser.add_argument("--debug", action='store_true', help='enable debug logging to stderr.') parser.add_argument("--output-kind", "-ok", choices=['eni', 'netplan', 'sysconfig'], required=True) args = parser.parse_args() if not args.directory.endswith("/"): args.directory += "/" if not os.path.isdir(args.directory): os.makedirs(args.directory) if args.debug: log.setupBasicLogging(level=log.DEBUG) else: log.setupBasicLogging(level=log.WARN) if args.mac: known_macs = {} for item in args.mac: iface_name, iface_mac = item.split(",", 1) known_macs[iface_mac] = iface_name else: known_macs = None net_data = args.network_data.read() if args.kind == "eni": pre_ns = eni.convert_eni_data(net_data) ns = network_state.parse_net_config_data(pre_ns) elif args.kind == "yaml": pre_ns = yaml.load(net_data) if 'network' in pre_ns: pre_ns = pre_ns.get('network') if args.debug: sys.stderr.write('\n'.join( ["Input YAML", yaml.dump(pre_ns, default_flow_style=False, indent=4), ""])) ns = network_state.parse_net_config_data(pre_ns) else: pre_ns = openstack.convert_net_json( json.loads(net_data), known_macs=known_macs) ns = network_state.parse_net_config_data(pre_ns) if not ns: raise RuntimeError("No valid network_state object created from" "input data") if args.debug: sys.stderr.write('\n'.join([ "", "Internal State", yaml.dump(ns, default_flow_style=False, indent=4), ""])) if args.output_kind == "eni": r_cls = eni.Renderer elif args.output_kind == "netplan": r_cls = netplan.Renderer else: r_cls = sysconfig.Renderer r = r_cls() sys.stderr.write(''.join([ "Read input format '%s' from '%s'.\n" % ( args.kind, args.network_data.name), "Wrote output format '%s' to '%s'\n" % ( args.output_kind, args.directory)]) + "\n") r.render_network_state(network_state=ns, target=args.directory)
def test_conversion_fills_names(self): ncfg = openstack.convert_net_json(NETWORK_DATA, known_macs=KNOWN_MACS) expected = set(['nic0', 'enp0s1', 'enp0s2']) found = self._getnames_in_config(ncfg) self.assertEqual(found, expected)
def handle_args(name, args): if not args.directory.endswith("/"): args.directory += "/" if not os.path.isdir(args.directory): os.makedirs(args.directory) if args.debug: log.setupBasicLogging(level=log.DEBUG) else: log.setupBasicLogging(level=log.WARN) if args.mac: known_macs = {} for item in args.mac: iface_name, iface_mac = item.split(",", 1) known_macs[iface_mac] = iface_name else: known_macs = None net_data = args.network_data.read() if args.kind == "eni": pre_ns = eni.convert_eni_data(net_data) ns = network_state.parse_net_config_data(pre_ns) elif args.kind == "yaml": pre_ns = yaml.load(net_data) if 'network' in pre_ns: pre_ns = pre_ns.get('network') if args.debug: sys.stderr.write('\n'.join( ["Input YAML", yaml.dump(pre_ns, default_flow_style=False, indent=4), ""])) ns = network_state.parse_net_config_data(pre_ns) elif args.kind == 'network_data.json': pre_ns = openstack.convert_net_json( json.loads(net_data), known_macs=known_macs) ns = network_state.parse_net_config_data(pre_ns) elif args.kind == 'azure-imds': pre_ns = azure.parse_network_config(json.loads(net_data)) ns = network_state.parse_net_config_data(pre_ns) if not ns: raise RuntimeError("No valid network_state object created from" "input data") if args.debug: sys.stderr.write('\n'.join([ "", "Internal State", yaml.dump(ns, default_flow_style=False, indent=4), ""])) distro_cls = distros.fetch(args.distro) distro = distro_cls(args.distro, {}, None) config = {} if args.output_kind == "eni": r_cls = eni.Renderer config = distro.renderer_configs.get('eni') elif args.output_kind == "netplan": r_cls = netplan.Renderer config = distro.renderer_configs.get('netplan') else: r_cls = sysconfig.Renderer config = distro.renderer_configs.get('sysconfig') r = r_cls(config=config) sys.stderr.write(''.join([ "Read input format '%s' from '%s'.\n" % ( args.kind, args.network_data.name), "Wrote output format '%s' to '%s'\n" % ( args.output_kind, args.directory)]) + "\n") r.render_network_state(network_state=ns, target=args.directory)
def test_network_config_conversions(self): """Tests a bunch of input network json and checks the expected conversions.""" in_datas = [ NETWORK_DATA, { "services": [{"type": "dns", "address": "172.19.0.12"}], "networks": [ { "network_id": "dacd568d-5be6-4786-91fe-750c374b78b4", "type": "ipv4", "netmask": "255.255.252.0", "link": "tap1a81968a-79", "routes": [ { "netmask": "0.0.0.0", "network": "0.0.0.0", "gateway": "172.19.3.254", } ], "ip_address": "172.19.1.34", "id": "network0", } ], "links": [ { "type": "bridge", "vif_id": "1a81968a-797a-400f-8a80-567f997eb93f", "ethernet_mac_address": "fa:16:3e:ed:9a:59", "id": "tap1a81968a-79", "mtu": None, } ], }, ] out_datas = [ { "version": 1, "config": [ { "subnets": [{"type": "dhcp4"}], "type": "physical", "mac_address": "fa:16:3e:69:b0:58", "name": "enp0s1", "mtu": None, }, { "subnets": [{"type": "dhcp4"}], "type": "physical", "mac_address": "fa:16:3e:d4:57:ad", "name": "enp0s2", "mtu": None, }, { "subnets": [{"type": "dhcp4"}], "type": "physical", "mac_address": "fa:16:3e:05:30:fe", "name": "nic0", "mtu": None, }, { "type": "nameserver", "address": "199.204.44.24", }, { "type": "nameserver", "address": "199.204.47.54", }, ], }, { "version": 1, "config": [ { "name": "foo3", "mac_address": "fa:16:3e:ed:9a:59", "mtu": None, "type": "physical", "subnets": [ { "address": "172.19.1.34", "netmask": "255.255.252.0", "type": "static", "ipv4": True, "routes": [ { "gateway": "172.19.3.254", "netmask": "0.0.0.0", "network": "0.0.0.0", } ], } ], }, { "type": "nameserver", "address": "172.19.0.12", }, ], }, ] for in_data, out_data in zip(in_datas, out_datas): conv_data = openstack.convert_net_json( in_data, known_macs=KNOWN_MACS ) self.assertEqual(out_data, conv_data)
def main(): parser = argparse.ArgumentParser() parser.add_argument("--network-data", "-p", type=open, metavar="PATH", required=True) parser.add_argument("--kind", "-k", choices=['eni', 'network_data.json', 'yaml'], required=True) parser.add_argument("-d", "--directory", metavar="PATH", help="directory to place output in", required=True) parser.add_argument("-m", "--mac", metavar="name,mac", action='append', help="interface name to mac mapping") parser.add_argument("--output-kind", "-ok", choices=['eni', 'netplan', 'sysconfig'], required=True) args = parser.parse_args() if not os.path.isdir(args.directory): os.makedirs(args.directory) if args.mac: known_macs = {} for item in args.mac: iface_name, iface_mac = item.split(",", 1) known_macs[iface_mac] = iface_name else: known_macs = None net_data = args.network_data.read() if args.kind == "eni": pre_ns = eni.convert_eni_data(net_data) ns = network_state.parse_net_config_data(pre_ns) elif args.kind == "yaml": pre_ns = yaml.load(net_data) if 'network' in pre_ns: pre_ns = pre_ns.get('network') print("Input YAML") print(yaml.dump(pre_ns, default_flow_style=False, indent=4)) ns = network_state.parse_net_config_data(pre_ns) else: pre_ns = openstack.convert_net_json(json.loads(net_data), known_macs=known_macs) ns = network_state.parse_net_config_data(pre_ns) if not ns: raise RuntimeError("No valid network_state object created from" "input data") print("\nInternal State") print(yaml.dump(ns, default_flow_style=False, indent=4)) if args.output_kind == "eni": r_cls = eni.Renderer elif args.output_kind == "netplan": r_cls = netplan.Renderer else: r_cls = sysconfig.Renderer r = r_cls() r.render_network_state(ns, target=args.directory)
def handle_args(name, args): if not args.directory.endswith("/"): args.directory += "/" if not os.path.isdir(args.directory): os.makedirs(args.directory) if args.debug: log.setupBasicLogging(level=log.DEBUG) else: log.setupBasicLogging(level=log.WARN) if args.mac: known_macs = {} for item in args.mac: iface_name, iface_mac = item.split(",", 1) known_macs[iface_mac] = iface_name else: known_macs = None net_data = args.network_data.read() if args.kind == "eni": pre_ns = eni.convert_eni_data(net_data) elif args.kind == "yaml": pre_ns = safeyaml.load(net_data) if 'network' in pre_ns: pre_ns = pre_ns.get('network') if args.debug: sys.stderr.write('\n'.join( ["Input YAML", safeyaml.dumps(pre_ns), ""])) elif args.kind == 'network_data.json': pre_ns = openstack.convert_net_json( json.loads(net_data), known_macs=known_macs) elif args.kind == 'azure-imds': pre_ns = azure.parse_network_config(json.loads(net_data)) elif args.kind == 'vmware-imc': config = ovf.Config(ovf.ConfigFile(args.network_data.name)) pre_ns = ovf.get_network_config_from_conf(config, False) ns = network_state.parse_net_config_data(pre_ns) if args.debug: sys.stderr.write('\n'.join( ["", "Internal State", safeyaml.dumps(ns), ""])) distro_cls = distros.fetch(args.distro) distro = distro_cls(args.distro, {}, None) config = {} if args.output_kind == "eni": r_cls = eni.Renderer config = distro.renderer_configs.get('eni') elif args.output_kind == "netplan": r_cls = netplan.Renderer config = distro.renderer_configs.get('netplan') # don't run netplan generate/apply config['postcmds'] = False # trim leading slash config['netplan_path'] = config['netplan_path'][1:] # enable some netplan features config['features'] = ['dhcp-use-domains', 'ipv6-mtu'] elif args.output_kind == "networkd": r_cls = networkd.Renderer config = distro.renderer_configs.get('networkd') elif args.output_kind == "sysconfig": r_cls = sysconfig.Renderer config = distro.renderer_configs.get('sysconfig') else: raise RuntimeError("Invalid output_kind") r = r_cls(config=config) sys.stderr.write(''.join([ "Read input format '%s' from '%s'.\n" % ( args.kind, args.network_data.name), "Wrote output format '%s' to '%s'\n" % ( args.output_kind, args.directory)]) + "\n") r.render_network_state(network_state=ns, target=args.directory)
def handle_args(name, args): if not args.directory.endswith("/"): args.directory += "/" if not os.path.isdir(args.directory): os.makedirs(args.directory) if args.debug: log.setupBasicLogging(level=log.DEBUG) else: log.setupBasicLogging(level=log.WARN) if args.mac: known_macs = {} for item in args.mac: iface_name, iface_mac = item.split(",", 1) known_macs[iface_mac] = iface_name else: known_macs = None net_data = args.network_data.read() if args.kind == "eni": pre_ns = eni.convert_eni_data(net_data) elif args.kind == "yaml": pre_ns = yaml.load(net_data) if 'network' in pre_ns: pre_ns = pre_ns.get('network') if args.debug: sys.stderr.write('\n'.join( ["Input YAML", yaml.dump(pre_ns, default_flow_style=False, indent=4), ""])) elif args.kind == 'network_data.json': pre_ns = openstack.convert_net_json( json.loads(net_data), known_macs=known_macs) elif args.kind == 'azure-imds': pre_ns = azure.parse_network_config(json.loads(net_data)) elif args.kind == 'vmware-imc': config = ovf.Config(ovf.ConfigFile(args.network_data.name)) pre_ns = ovf.get_network_config_from_conf(config, False) ns = network_state.parse_net_config_data(pre_ns) if not ns: raise RuntimeError("No valid network_state object created from" "input data") if args.debug: sys.stderr.write('\n'.join([ "", "Internal State", yaml.dump(ns, default_flow_style=False, indent=4), ""])) distro_cls = distros.fetch(args.distro) distro = distro_cls(args.distro, {}, None) config = {} if args.output_kind == "eni": r_cls = eni.Renderer config = distro.renderer_configs.get('eni') elif args.output_kind == "netplan": r_cls = netplan.Renderer config = distro.renderer_configs.get('netplan') # don't run netplan generate/apply config['postcmds'] = False # trim leading slash config['netplan_path'] = config['netplan_path'][1:] else: r_cls = sysconfig.Renderer config = distro.renderer_configs.get('sysconfig') r = r_cls(config=config) sys.stderr.write(''.join([ "Read input format '%s' from '%s'.\n" % ( args.kind, args.network_data.name), "Wrote output format '%s' to '%s'\n" % ( args.output_kind, args.directory)]) + "\n") r.render_network_state(network_state=ns, target=args.directory)