Ejemplo n.º 1
0
def convert_ec2_metadata_network_config(network_md, macs_to_nics=None,
                                        fallback_nic=None):
    """Convert ec2 metadata to network config version 1 data dict.

    @param: network_md: 'network' portion of EC2 metadata.
       generally formed as {"interfaces": {"macs": {}} where
       'macs' is a dictionary with mac address as key and contents like:
       {"device-number": "0", "interface-id": "...", "local-ipv4s": ...}
    @param: macs_to_nics: Optional dict of mac addresses and nic names. If
       not provided, get_interfaces_by_mac is called to get it from the OS.
    @param: fallback_nic: Optionally provide the primary nic interface name.
       This nic will be guaranteed to minimally have a dhcp4 configuration.

    @return A dict of network config version 1 based on the metadata and macs.
    """
    netcfg = {'version': 1, 'config': []}
    if not macs_to_nics:
        macs_to_nics = net.get_interfaces_by_mac()
    macs_metadata = network_md['interfaces']['macs']
    for mac, nic_name in macs_to_nics.items():
        nic_metadata = macs_metadata.get(mac)
        if not nic_metadata:
            continue  # Not a physical nic represented in metadata
        nic_cfg = {'type': 'physical', 'name': nic_name, 'subnets': []}
        nic_cfg['mac_address'] = mac
        if (nic_name == fallback_nic or nic_metadata.get('public-ipv4s') or
                nic_metadata.get('local-ipv4s')):
            nic_cfg['subnets'].append({'type': 'dhcp4'})
        if nic_metadata.get('ipv6s'):
            nic_cfg['subnets'].append({'type': 'dhcp6'})
        netcfg['config'].append(nic_cfg)
    return netcfg
Ejemplo n.º 2
0
 def generate_fallback_config(self):
     nconf = {'config': [], 'version': 1}
     for mac, name in net.get_interfaces_by_mac().items():
         nconf['config'].append(
             {'type': 'physical', 'name': name,
              'mac_address': mac, 'subnets': [{'type': 'dhcp'}]})
     return nconf
Ejemplo n.º 3
0
def convert_ec2_metadata_network_config(network_md, macs_to_nics=None,
                                        fallback_nic=None):
    """Convert ec2 metadata to network config version 1 data dict.

    @param: network_md: 'network' portion of EC2 metadata.
       generally formed as {"interfaces": {"macs": {}} where
       'macs' is a dictionary with mac address as key and contents like:
       {"device-number": "0", "interface-id": "...", "local-ipv4s": ...}
    @param: macs_to_nics: Optional dict of mac addresses and nic names. If
       not provided, get_interfaces_by_mac is called to get it from the OS.
    @param: fallback_nic: Optionally provide the primary nic interface name.
       This nic will be guaranteed to minimally have a dhcp4 configuration.

    @return A dict of network config version 1 based on the metadata and macs.
    """
    netcfg = {'version': 1, 'config': []}
    if not macs_to_nics:
        macs_to_nics = net.get_interfaces_by_mac()
    macs_metadata = network_md['interfaces']['macs']
    for mac, nic_name in macs_to_nics.items():
        nic_metadata = macs_metadata.get(mac)
        if not nic_metadata:
            continue  # Not a physical nic represented in metadata
        nic_cfg = {'type': 'physical', 'name': nic_name, 'subnets': []}
        nic_cfg['mac_address'] = mac
        if (nic_name == fallback_nic or nic_metadata.get('public-ipv4s') or
                nic_metadata.get('local-ipv4s')):
            nic_cfg['subnets'].append({'type': 'dhcp4'})
        if nic_metadata.get('ipv6s'):
            nic_cfg['subnets'].append({'type': 'dhcp6'})
        netcfg['config'].append(nic_cfg)
    return netcfg
Ejemplo n.º 4
0
 def test_get_interfaces_by_mac(self, mock_is_FreeBSD, mock_subp):
     mock_is_FreeBSD.return_value = True
     mock_subp.return_value = (SAMPLE_FREEBSD_IFCONFIG_OUT, 0)
     a = net.get_interfaces_by_mac()
     assert a == {
         '52:54:00:50:b7:0d': 'vtnet0',
         '80:00:73:63:5c:48': 're0.33',
         '02:14:39:0e:25:00': 'bridge0',
         '02:ff:60:8c:f3:72': 'vnet0:11'
     }
Ejemplo n.º 5
0
 def generate_fallback_config(self):
     nconf = {"config": [], "version": 1}
     for mac, name in net.get_interfaces_by_mac().items():
         nconf["config"].append({
             "type": "physical",
             "name": name,
             "mac_address": mac,
             "subnets": [{
                 "type": "dhcp"
             }],
         })
     return nconf
Ejemplo n.º 6
0
    def _ifconfig_entries(self, settings):
        ifname_by_mac = net.get_interfaces_by_mac()
        for interface in settings.iter_interfaces():
            device_name = interface.get("name")
            device_mac = interface.get("mac_address")
            if device_name and re.match(r"^lo\d+$", device_name):
                continue
            if device_mac not in ifname_by_mac:
                LOG.info("Cannot find any device with MAC %s", device_mac)
            elif device_mac and device_name:
                cur_name = ifname_by_mac[device_mac]
                if cur_name != device_name:
                    LOG.info(
                        "netif service will rename interface %s to %s",
                        cur_name,
                        device_name,
                    )
                    try:
                        self.rename_interface(cur_name, device_name)
                    except NotImplementedError:
                        LOG.error(
                            "Interface renaming is not supported on this OS"
                        )
                        device_name = cur_name

            else:
                device_name = ifname_by_mac[device_mac]

            LOG.info("Configuring interface %s", device_name)

            self.interface_configurations[device_name] = "DHCP"

            for subnet in interface.get("subnets", []):
                if subnet.get("type") == "static":
                    if not subnet.get("netmask"):
                        LOG.debug(
                            "Skipping IP %s, because there is no netmask",
                            subnet.get("address"),
                        )
                        continue
                    LOG.debug(
                        "Configuring dev %s with %s / %s",
                        device_name,
                        subnet.get("address"),
                        subnet.get("netmask"),
                    )

                    self.interface_configurations[device_name] = {
                        "address": subnet.get("address"),
                        "netmask": subnet.get("netmask"),
                        "mtu": subnet.get("mtu") or interface.get("mtu"),
                    }
Ejemplo n.º 7
0
def _ensure_netfailover_safe(network_config):
    """
    Search network config physical interfaces to see if any of them are
    a netfailover master.  If found, we prevent matching by MAC as the other
    failover devices have the same MAC but need to be ignored.

    Note: we rely on cloudinit.net changes which prevent netfailover devices
    from being present in the provided network config.  For more details about
    netfailover devices, refer to cloudinit.net module.

    :param network_config
       A v1 or v2 network config dict with the primary NIC, and possibly
       secondary nic configured.  This dict will be mutated.

    """
    # ignore anything that's not an actual network-config
    if "version" not in network_config:
        return

    if network_config["version"] not in [1, 2]:
        LOG.debug(
            "Ignoring unknown network config version: %s",
            network_config["version"],
        )
        return

    mac_to_name = get_interfaces_by_mac()
    if network_config["version"] == 1:
        for cfg in [c for c in network_config["config"] if "type" in c]:
            if cfg["type"] == "physical":
                if "mac_address" in cfg:
                    mac = cfg["mac_address"]
                    cur_name = mac_to_name.get(mac)
                    if not cur_name:
                        continue
                    elif is_netfail_master(cur_name):
                        del cfg["mac_address"]

    elif network_config["version"] == 2:
        for _, cfg in network_config.get("ethernets", {}).items():
            if "match" in cfg:
                macaddr = cfg.get("match", {}).get("macaddress")
                if macaddr:
                    cur_name = mac_to_name.get(macaddr)
                    if not cur_name:
                        continue
                    elif is_netfail_master(cur_name):
                        del cfg["match"]["macaddress"]
                        del cfg["set-name"]
                        cfg["match"]["name"] = cur_name
Ejemplo n.º 8
0
    def _ifconfig_entries(self, settings, target=None):
        ifname_by_mac = net.get_interfaces_by_mac()
        for interface in settings.iter_interfaces():
            device_name = interface.get("name")
            device_mac = interface.get("mac_address")
            if device_name and re.match(r'^lo\d+$', device_name):
                continue
            if device_mac not in ifname_by_mac:
                LOG.info('Cannot find any device with MAC %s', device_mac)
            elif device_mac and device_name:
                cur_name = ifname_by_mac[device_mac]
                if cur_name != device_name:
                    LOG.info('netif service will rename interface %s to %s',
                             cur_name, device_name)
                    try:
                        self.rename_interface(cur_name, device_name)
                    except NotImplementedError:
                        LOG.error((
                            'Interface renaming is '
                            'not supported on this OS'))
                        device_name = cur_name

            else:
                device_name = ifname_by_mac[device_mac]

            LOG.info('Configuring interface %s', device_name)

            self.interface_configurations[device_name] = 'DHCP'

            for subnet in interface.get("subnets", []):
                if subnet.get('type') == 'static':
                    if not subnet.get('netmask'):
                        LOG.debug(
                            'Skipping IP %s, because there is no netmask',
                            subnet.get('address')
                        )
                        continue
                    LOG.debug('Configuring dev %s with %s / %s', device_name,
                              subnet.get('address'), subnet.get('netmask'))

                    self.interface_configurations[device_name] = {
                        'address': subnet.get('address'),
                        'netmask': subnet.get('netmask'),
                        'mtu': subnet.get('mtu'),
                    }
Ejemplo n.º 9
0
    def _write_ifconfig_entries(self, settings, target=None):
        ifname_by_mac = net.get_interfaces_by_mac()
        for interface in settings.iter_interfaces():
            device_name = interface.get("name")
            device_mac = interface.get("mac_address")
            if device_name and re.match(r'^lo\d+$', device_name):
                continue
            if device_mac not in ifname_by_mac:
                LOG.info('Cannot find any device with MAC %s', device_mac)
            elif device_mac and device_name:
                cur_name = ifname_by_mac[device_mac]
                if cur_name != device_name:
                    LOG.info('netif service will rename interface %s to %s',
                             cur_name, device_name)
                    self._update_rc_conf(
                        {'ifconfig_%s_name' % cur_name: device_name},
                        target=target)
            else:
                device_name = ifname_by_mac[device_mac]

            LOG.info('Configuring interface %s', device_name)
            ifconfig = 'DHCP'  # default

            for subnet in interface.get("subnets", []):
                if ifconfig != 'DHCP':
                    LOG.info('The FreeBSD provider only set the first subnet.')
                    break
                if subnet.get('type') == 'static':
                    if not subnet.get('netmask'):
                        LOG.debug(
                            'Skipping IP %s, because there is no netmask',
                            subnet.get('address'))
                        continue
                    LOG.debug('Configuring dev %s with %s / %s', device_name,
                              subnet.get('address'), subnet.get('netmask'))
                    # Configure an ipv4 address.
                    ifconfig = (subnet.get('address') + ' netmask ' +
                                subnet.get('netmask'))

            if ifconfig == 'DHCP':
                self.dhcp_interfaces.append(device_name)
            self._update_rc_conf({'ifconfig_' + device_name: ifconfig},
                                 target=target)
Ejemplo n.º 10
0
def convert_net_json(network_json=None, known_macs=None):
    """Return a dictionary of network_config by parsing provided
       OpenStack ConfigDrive NetworkData json format

    OpenStack network_data.json provides a 3 element dictionary
      - "links" (links are network devices, physical or virtual)
      - "networks" (networks are ip network configurations for one or more
                    links)
      -  services (non-ip services, like dns)

    networks and links are combined via network items referencing specific
    links via a 'link_id' which maps to a links 'id' field.

    To convert this format to network_config yaml, we first iterate over the
    links and then walk the network list to determine if any of the networks
    utilize the current link; if so we generate a subnet entry for the device

    We also need to map network_data.json fields to network_config fields. For
    example, the network_data links 'id' field is equivalent to network_config
    'name' field for devices.  We apply more of this mapping to the various
    link types that we encounter.

    There are additional fields that are populated in the network_data.json
    from OpenStack that are not relevant to network_config yaml, so we
    enumerate a dictionary of valid keys for network_yaml and apply filtering
    to drop these superflous keys from the network_config yaml.
    """
    if network_json is None:
        return None

    # dict of network_config key for filtering network_json
    valid_keys = {
        'physical': [
            'name',
            'type',
            'mac_address',
            'subnets',
            'params',
            'mtu',
        ],
        'subnet': [
            'type',
            'address',
            'netmask',
            'broadcast',
            'metric',
            'gateway',
            'pointopoint',
            'scope',
            'dns_nameservers',
            'dns_search',
            'routes',
        ],
    }

    links = network_json.get('links', [])
    networks = network_json.get('networks', [])
    services = network_json.get('services', [])

    config = []
    for link in links:
        subnets = []
        cfg = {k: v for k, v in link.items()
               if k in valid_keys['physical']}
        # 'name' is not in openstack spec yet, but we will support it if it is
        # present.  The 'id' in the spec is currently implemented as the host
        # nic's name, meaning something like 'tap-adfasdffd'.  We do not want
        # to name guest devices with such ugly names.
        if 'name' in link:
            cfg['name'] = link['name']

        for network in [n for n in networks
                        if n['link'] == link['id']]:
            subnet = {k: v for k, v in network.items()
                      if k in valid_keys['subnet']}
            if 'dhcp' in network['type']:
                t = 'dhcp6' if network['type'].startswith('ipv6') else 'dhcp4'
                subnet.update({
                    'type': t,
                })
            else:
                subnet.update({
                    'type': 'static',
                    'address': network.get('ip_address'),
                })
            if network['type'] == 'ipv4':
                subnet['ipv4'] = True
            if network['type'] == 'ipv6':
                subnet['ipv6'] = True
            subnets.append(subnet)
        cfg.update({'subnets': subnets})
        if link['type'] in ['ethernet', 'vif', 'ovs', 'phy', 'bridge']:
            cfg.update({
                'type': 'physical',
                'mac_address': link['ethernet_mac_address']})
        elif link['type'] in ['bond']:
            params = {}
            for k, v in link.items():
                if k == 'bond_links':
                    continue
                elif k.startswith('bond'):
                    params.update({k: v})
            cfg.update({
                'bond_interfaces': copy.deepcopy(link['bond_links']),
                'params': params,
            })
        elif link['type'] in ['vlan']:
            cfg.update({
                'name': "%s.%s" % (link['vlan_link'],
                                   link['vlan_id']),
                'vlan_link': link['vlan_link'],
                'vlan_id': link['vlan_id'],
                'mac_address': link['vlan_mac_address'],
            })
        else:
            raise ValueError(
                'Unknown network_data link type: %s' % link['type'])

        config.append(cfg)

    need_names = [d for d in config
                  if d.get('type') == 'physical' and 'name' not in d]

    if need_names:
        if known_macs is None:
            known_macs = net.get_interfaces_by_mac()

        for d in need_names:
            mac = d.get('mac_address')
            if not mac:
                raise ValueError("No mac_address or name entry for %s" % d)
            if mac not in known_macs:
                raise ValueError("Unable to find a system nic for %s" % d)
            d['name'] = known_macs[mac]

    for service in services:
        cfg = service
        cfg.update({'type': 'nameserver'})
        config.append(cfg)

    return {'version': 1, 'config': config}
Ejemplo n.º 11
0
 def get_interfaces_by_mac(self) -> dict:
     return net.get_interfaces_by_mac()
Ejemplo n.º 12
0
    def _add_network_config_from_opc_imds(self):
        """Generate secondary NIC config from IMDS and merge it.

        The primary NIC configuration should not be modified based on the IMDS
        values, as it should continue to be configured for DHCP.  As such, this
        uses the instance's network config dict which is expected to have the
        primary NIC configuration already present.
        It will mutate the network config to include the secondary VNICs.

        :raises:
            Exceptions are not handled within this function.  Likely
            exceptions are KeyError/IndexError
            (if the IMDS returns valid JSON with unexpected contents).
        """
        if self._vnics_data is None:
            LOG.warning("Secondary NIC data is UNSET but should not be")
            return

        if 'nicIndex' in self._vnics_data[0]:
            # TODO: Once configure_secondary_nics defaults to True, lower the
            # level of this log message.  (Currently, if we're running this
            # code at all, someone has explicitly opted-in to secondary
            # VNIC configuration, so we should warn them that it didn't
            # happen.  Once it's default, this would be emitted on every Bare
            # Metal Machine launch, which means INFO or DEBUG would be more
            # appropriate.)
            LOG.warning(
                'VNIC metadata indicates this is a bare metal machine; '
                'skipping secondary VNIC configuration.')
            return

        interfaces_by_mac = get_interfaces_by_mac()

        for vnic_dict in self._vnics_data[1:]:
            # We skip the first entry in the response because the primary
            # interface is already configured by iSCSI boot; applying
            # configuration from the IMDS is not required.
            mac_address = vnic_dict['macAddr'].lower()
            if mac_address not in interfaces_by_mac:
                LOG.debug('Interface with MAC %s not found; skipping',
                          mac_address)
                continue
            name = interfaces_by_mac[mac_address]

            if self._network_config['version'] == 1:
                subnet = {
                    'type': 'static',
                    'address': vnic_dict['privateIp'],
                }
                self._network_config['config'].append({
                    'name': name,
                    'type': 'physical',
                    'mac_address': mac_address,
                    'mtu': MTU,
                    'subnets': [subnet],
                })
            elif self._network_config['version'] == 2:
                self._network_config['ethernets'][name] = {
                    'addresses': [vnic_dict['privateIp']],
                    'mtu': MTU,
                    'dhcp4': False,
                    'dhcp6': False,
                    'match': {
                        'macaddress': mac_address
                    }
                }
Ejemplo n.º 13
0
    def _ifconfig_entries(self, settings, target=None):
        ifname_by_mac = net.get_interfaces_by_mac()
        for interface in settings.iter_interfaces():
            device_name = interface.get("name")
            device_mac = interface.get("mac_address")
            if device_name and re.match(r'^lo\d+$', device_name):
                continue
            if device_mac not in ifname_by_mac:
                LOG.info('Cannot find any device with MAC %s', device_mac)
            elif device_mac and device_name:
                cur_name = ifname_by_mac[device_mac]
                if cur_name != device_name:
                    LOG.info('netif service will rename interface %s to %s',
                             cur_name, device_name)
                    try:
                        self.rename_interface(cur_name, device_name)
                    except NotImplementedError:
                        LOG.error(('Interface renaming is '
                                   'not supported on this OS'))
                        device_name = cur_name

            else:
                device_name = ifname_by_mac[device_mac]

            LOG.info('Configuring interface %s', device_name)

            self.interface_configurations[device_name] = 'DHCP'

            alias_count = -1
            alias_count_ipv6 = -1

            for subnet in interface.get("subnets", []):
                if subnet.get('type') == 'static6':
                    if not subnet.get('prefix'):
                        LOG.debug('Skipping IP %s, because there is no prefix',
                                  subnet)
                        # not sure how to handle this. is our ipv6 implementation
                        # correct or not? ip/64 or do we need to pass in the
                        # netmask for ipv6 interfaces
                    LOG.debug('Configuring dev %s with %s / %s', device_name,
                              subnet.get('address'), subnet.get('prefix'))

                    # If we have added in a config for this interface,
                    # add this subnet in as an alias
                    if alias_count_ipv6 > -1:
                        name = f'{device_name}_ipv6_alias{alias_count_ipv6}'
                    else:
                        name = f'{device_name}_ipv6'
                    alias_count_ipv6 += 1

                    self.interface_configurations[name] = {
                        'address': subnet.get('address'),
                        'prefixlen': subnet.get('prefix'),
                        'type': 'ipv6',
                    }
                elif subnet.get('type') == 'static':
                    if not subnet.get('netmask'):
                        LOG.debug(
                            'Skipping IP %s, because there is no netmask',
                            subnet.get('address'))
                        continue
                    LOG.debug('Configuring dev %s with %s / %s', device_name,
                              subnet.get('address'), subnet.get('netmask'))

                    # If we have added in a config for this interface,
                    # add this subnet in as an alias
                    if alias_count > -1:
                        name = f'{device_name}_alias{alias_count}'
                    else:
                        name = device_name
                    alias_count += 1

                    self.interface_configurations[name] = {
                        'address': subnet.get('address'),
                        'netmask': subnet.get('netmask'),
                        'type': 'ipv4',
                    }
Ejemplo n.º 14
0
    def _add_network_config_from_opc_imds(self, set_primary: bool = False):
        """Generate primary and/or secondary NIC config from IMDS and merge it.

        It will mutate the network config to include the secondary VNICs.

        :param set_primary: If True set primary interface.
        :raises:
            Exceptions are not handled within this function.  Likely
            exceptions are KeyError/IndexError
            (if the IMDS returns valid JSON with unexpected contents).
        """
        if self._vnics_data is None:
            LOG.warning("NIC data is UNSET but should not be")
            return

        if not set_primary and ("nicIndex" in self._vnics_data[0]):
            # TODO: Once configure_secondary_nics defaults to True, lower the
            # level of this log message.  (Currently, if we're running this
            # code at all, someone has explicitly opted-in to secondary
            # VNIC configuration, so we should warn them that it didn't
            # happen.  Once it's default, this would be emitted on every Bare
            # Metal Machine launch, which means INFO or DEBUG would be more
            # appropriate.)
            LOG.warning(
                "VNIC metadata indicates this is a bare metal machine; "
                "skipping secondary VNIC configuration.")
            return

        interfaces_by_mac = get_interfaces_by_mac()

        vnics_data = self._vnics_data if set_primary else self._vnics_data[1:]

        for vnic_dict in vnics_data:
            mac_address = vnic_dict["macAddr"].lower()
            if mac_address not in interfaces_by_mac:
                LOG.warning(
                    "Interface with MAC %s not found; skipping",
                    mac_address,
                )
                continue
            name = interfaces_by_mac[mac_address]

            if self._network_config["version"] == 1:
                subnet = {
                    "type": "static",
                    "address": vnic_dict["privateIp"],
                }
                self._network_config["config"].append({
                    "name": name,
                    "type": "physical",
                    "mac_address": mac_address,
                    "mtu": MTU,
                    "subnets": [subnet],
                })
            elif self._network_config["version"] == 2:
                self._network_config["ethernets"][name] = {
                    "addresses": [vnic_dict["privateIp"]],
                    "mtu": MTU,
                    "dhcp4": False,
                    "dhcp6": False,
                    "match": {
                        "macaddress": mac_address
                    },
                }
def convert_network_configuration(config, dns_servers):
    """Convert the DigitalOcean Network description into Cloud-init's netconfig
    format.

    Example JSON:
     {'public': [
           {'mac': '04:01:58:27:7f:01',
            'ipv4': {'gateway': '45.55.32.1',
                     'netmask': '255.255.224.0',
                     'ip_address': '45.55.50.93'},
            'anchor_ipv4': {
                     'gateway': '10.17.0.1',
                     'netmask': '255.255.0.0',
                     'ip_address': '10.17.0.9'},
            'type': 'public',
            'ipv6': {'gateway': '....',
                     'ip_address': '....',
                     'cidr': 64}}
        ],
       'private': [
           {'mac': '04:01:58:27:7f:02',
            'ipv4': {'gateway': '10.132.0.1',
                     'netmask': '255.255.0.0',
                     'ip_address': '10.132.75.35'},
            'type': 'private'}
        ]
     }
    """
    def _get_subnet_part(pcfg):
        subpart = {
            "type": "static",
            "control": "auto",
            "address": pcfg.get("ip_address"),
            "gateway": pcfg.get("gateway"),
        }

        if ":" in pcfg.get("ip_address"):
            subpart["address"] = "{0}/{1}".format(pcfg.get("ip_address"),
                                                  pcfg.get("cidr"))
        else:
            subpart["netmask"] = pcfg.get("netmask")

        return subpart

    nic_configs = []
    macs_to_nics = cloudnet.get_interfaces_by_mac()
    LOG.debug("nic mapping: %s", macs_to_nics)

    for n in config:
        nic = config[n][0]
        LOG.debug("considering %s", nic)

        mac_address = nic.get("mac")
        if mac_address not in macs_to_nics:
            raise RuntimeError(
                "Did not find network interface on system "
                "with mac '%s'. Cannot apply configuration: %s" %
                (mac_address, nic))

        sysfs_name = macs_to_nics.get(mac_address)
        nic_type = nic.get("type", "unknown")

        if_name = NIC_MAP.get(nic_type, sysfs_name)
        if if_name != sysfs_name:
            LOG.debug(
                "Found %s interface '%s' on '%s', assigned name of '%s'",
                nic_type,
                mac_address,
                sysfs_name,
                if_name,
            )
        else:
            msg = ("Found interface '%s' on '%s', which is not a public "
                   "or private interface. Using default system naming.")
            LOG.debug(msg, mac_address, sysfs_name)

        ncfg = {
            "type": "physical",
            "mac_address": mac_address,
            "name": if_name,
        }

        subnets = []
        for netdef in ("ipv4", "ipv6", "anchor_ipv4", "anchor_ipv6"):
            raw_subnet = nic.get(netdef, None)
            if not raw_subnet:
                continue

            sub_part = _get_subnet_part(raw_subnet)
            if nic_type != "public" or "anchor" in netdef:
                del sub_part["gateway"]

            subnets.append(sub_part)

        ncfg["subnets"] = subnets
        nic_configs.append(ncfg)
        LOG.debug("nic '%s' configuration: %s", if_name, ncfg)

    if dns_servers:
        LOG.debug("added dns servers: %s", dns_servers)
        nic_configs.append({"type": "nameserver", "address": dns_servers})

    return {"version": 1, "config": nic_configs}
Ejemplo n.º 16
0
 def get_interfaces_by_mac(self) -> dict:
     return net.get_interfaces_by_mac(
         blacklist_drivers=self.blacklist_drivers)
Ejemplo n.º 17
0
def convert_network_configuration(config, dns_servers):
    """Convert the DigitalOcean Network description into Cloud-init's netconfig
       format.

       Example JSON:
        {'public': [
              {'mac': '04:01:58:27:7f:01',
               'ipv4': {'gateway': '45.55.32.1',
                        'netmask': '255.255.224.0',
                        'ip_address': '45.55.50.93'},
               'anchor_ipv4': {
                        'gateway': '10.17.0.1',
                        'netmask': '255.255.0.0',
                        'ip_address': '10.17.0.9'},
               'type': 'public',
               'ipv6': {'gateway': '....',
                        'ip_address': '....',
                        'cidr': 64}}
           ],
          'private': [
              {'mac': '04:01:58:27:7f:02',
               'ipv4': {'gateway': '10.132.0.1',
                        'netmask': '255.255.0.0',
                        'ip_address': '10.132.75.35'},
               'type': 'private'}
           ]
        }
    """

    def _get_subnet_part(pcfg, nameservers=None):
        subpart = {'type': 'static',
                   'control': 'auto',
                   'address': pcfg.get('ip_address'),
                   'gateway': pcfg.get('gateway')}

        if nameservers:
            subpart['dns_nameservers'] = nameservers

        if ":" in pcfg.get('ip_address'):
            subpart['address'] = "{0}/{1}".format(pcfg.get('ip_address'),
                                                  pcfg.get('cidr'))
        else:
            subpart['netmask'] = pcfg.get('netmask')

        return subpart

    all_nics = []
    for k in ('public', 'private'):
        if k in config:
            all_nics.extend(config[k])

    macs_to_nics = cloudnet.get_interfaces_by_mac()
    nic_configs = []

    for nic in all_nics:

        mac_address = nic.get('mac')
        sysfs_name = macs_to_nics.get(mac_address)
        nic_type = nic.get('type', 'unknown')
        # Note: the entry 'public' above contains a list, but
        # the list will only ever have one nic inside it per digital ocean.
        # If it ever had more than one nic, then this code would
        # assign all 'public' the same name.
        if_name = NIC_MAP.get(nic_type, sysfs_name)

        LOG.debug("mapped %s interface to %s, assigning name of %s",
                  mac_address, sysfs_name, if_name)

        ncfg = {'type': 'physical',
                'mac_address': mac_address,
                'name': if_name}

        subnets = []
        for netdef in ('ipv4', 'ipv6', 'anchor_ipv4', 'anchor_ipv6'):
            raw_subnet = nic.get(netdef, None)
            if not raw_subnet:
                continue

            sub_part = _get_subnet_part(raw_subnet)
            if nic_type == 'public' and 'anchor' not in netdef:
                # add DNS resolvers to the public interfaces only
                sub_part = _get_subnet_part(raw_subnet, dns_servers)
            else:
                # remove the gateway any non-public interfaces
                if 'gateway' in sub_part:
                    del sub_part['gateway']

            subnets.append(sub_part)

        ncfg['subnets'] = subnets
        nic_configs.append(ncfg)
        LOG.debug("nic '%s' configuration: %s", if_name, ncfg)

    return {'version': 1, 'config': nic_configs}
Ejemplo n.º 18
0
def convert_net_json(network_json=None, known_macs=None):
    """Return a dictionary of network_config by parsing provided
       OpenStack ConfigDrive NetworkData json format

    OpenStack network_data.json provides a 3 element dictionary
      - "links" (links are network devices, physical or virtual)
      - "networks" (networks are ip network configurations for one or more
                    links)
      -  services (non-ip services, like dns)

    networks and links are combined via network items referencing specific
    links via a 'link_id' which maps to a links 'id' field.

    To convert this format to network_config yaml, we first iterate over the
    links and then walk the network list to determine if any of the networks
    utilize the current link; if so we generate a subnet entry for the device

    We also need to map network_data.json fields to network_config fields. For
    example, the network_data links 'id' field is equivalent to network_config
    'name' field for devices.  We apply more of this mapping to the various
    link types that we encounter.

    There are additional fields that are populated in the network_data.json
    from OpenStack that are not relevant to network_config yaml, so we
    enumerate a dictionary of valid keys for network_yaml and apply filtering
    to drop these superflous keys from the network_config yaml.
    """
    if network_json is None:
        return None

    # dict of network_config key for filtering network_json
    valid_keys = {
        "physical": [
            "name",
            "type",
            "mac_address",
            "subnets",
            "params",
            "mtu",
        ],
        "subnet": [
            "type",
            "address",
            "netmask",
            "broadcast",
            "metric",
            "gateway",
            "pointopoint",
            "scope",
            "dns_nameservers",
            "dns_search",
            "routes",
        ],
    }

    links = network_json.get("links", [])
    networks = network_json.get("networks", [])
    services = network_json.get("services", [])

    link_updates = []
    link_id_info = {}
    bond_name_fmt = "bond%d"
    bond_number = 0
    config = []
    for link in links:
        subnets = []
        cfg = dict(
            (k, v) for k, v in link.items() if k in valid_keys["physical"])
        # 'name' is not in openstack spec yet, but we will support it if it is
        # present.  The 'id' in the spec is currently implemented as the host
        # nic's name, meaning something like 'tap-adfasdffd'.  We do not want
        # to name guest devices with such ugly names.
        if "name" in link:
            cfg["name"] = link["name"]

        link_mac_addr = None
        if link.get("ethernet_mac_address"):
            link_mac_addr = link.get("ethernet_mac_address").lower()
            link_id_info[link["id"]] = link_mac_addr

        curinfo = {
            "name": cfg.get("name"),
            "mac": link_mac_addr,
            "id": link["id"],
            "type": link["type"],
        }

        for network in [n for n in networks if n["link"] == link["id"]]:
            subnet = dict((k, v) for k, v in network.items()
                          if k in valid_keys["subnet"])

            if network["type"] == "ipv4_dhcp":
                subnet.update({"type": "dhcp4"})
            elif network["type"] == "ipv6_dhcp":
                subnet.update({"type": "dhcp6"})
            elif network["type"] in [
                    "ipv6_slaac",
                    "ipv6_dhcpv6-stateless",
                    "ipv6_dhcpv6-stateful",
            ]:
                subnet.update({"type": network["type"]})
            elif network["type"] in ["ipv4", "static"]:
                subnet.update({
                    "type": "static",
                    "address": network.get("ip_address"),
                })
            elif network["type"] in ["ipv6", "static6"]:
                cfg.update({"accept-ra": False})
                subnet.update({
                    "type": "static6",
                    "address": network.get("ip_address"),
                })

            # Enable accept_ra for stateful and legacy ipv6_dhcp types
            if network["type"] in ["ipv6_dhcpv6-stateful", "ipv6_dhcp"]:
                cfg.update({"accept-ra": True})

            if network["type"] == "ipv4":
                subnet["ipv4"] = True
            if network["type"] == "ipv6":
                subnet["ipv6"] = True
            subnets.append(subnet)
        cfg.update({"subnets": subnets})
        if link["type"] in ["bond"]:
            params = {}
            if link_mac_addr:
                params["mac_address"] = link_mac_addr
            for k, v in link.items():
                if k == "bond_links":
                    continue
                elif k.startswith("bond"):
                    params.update({k: v})

            # openstack does not provide a name for the bond.
            # they do provide an 'id', but that is possibly non-sensical.
            # so we just create our own name.
            link_name = bond_name_fmt % bond_number
            bond_number += 1

            # bond_links reference links by their id, but we need to add
            # to the network config by their nic name.
            # store that in bond_links_needed, and update these later.
            link_updates.append((
                cfg,
                "bond_interfaces",
                "%s",
                copy.deepcopy(link["bond_links"]),
            ))
            cfg.update({"params": params, "name": link_name})

            curinfo["name"] = link_name
        elif link["type"] in ["vlan"]:
            name = "%s.%s" % (link["vlan_link"], link["vlan_id"])
            cfg.update({
                "name": name,
                "vlan_id": link["vlan_id"],
                "mac_address": link["vlan_mac_address"],
            })
            link_updates.append((cfg, "vlan_link", "%s", link["vlan_link"]))
            link_updates.append(
                (cfg, "name", "%%s.%s" % link["vlan_id"], link["vlan_link"]))
            curinfo.update({"mac": link["vlan_mac_address"], "name": name})
        else:
            if link["type"] not in KNOWN_PHYSICAL_TYPES:
                LOG.warning(
                    "Unknown network_data link type (%s); treating as"
                    " physical",
                    link["type"],
                )
            cfg.update({"type": "physical", "mac_address": link_mac_addr})

        config.append(cfg)
        link_id_info[curinfo["id"]] = curinfo

    need_names = [
        d for d in config if d.get("type") == "physical" and "name" not in d
    ]

    if need_names or link_updates:
        if known_macs is None:
            known_macs = net.get_interfaces_by_mac()

        # go through and fill out the link_id_info with names
        for _link_id, info in link_id_info.items():
            if info.get("name"):
                continue
            if info.get("mac") in known_macs:
                info["name"] = known_macs[info["mac"]]

        for d in need_names:
            mac = d.get("mac_address")
            if not mac:
                raise ValueError("No mac_address or name entry for %s" % d)
            if mac not in known_macs:
                raise ValueError("Unable to find a system nic for %s" % d)
            d["name"] = known_macs[mac]

        for cfg, key, fmt, targets in link_updates:
            if isinstance(targets, (list, tuple)):
                cfg[key] = [
                    fmt % link_id_info[target]["name"] for target in targets
                ]
            else:
                cfg[key] = fmt % link_id_info[targets]["name"]

    # Infiniband interfaces may be referenced in network_data.json by a 6 byte
    # Ethernet MAC-style address, and we use that address to look up the
    # interface name above. Now ensure that the hardware address is set to the
    # full 20 byte address.
    ib_known_hwaddrs = net.get_ib_hwaddrs_by_interface()
    if ib_known_hwaddrs:
        for cfg in config:
            if cfg["name"] in ib_known_hwaddrs:
                cfg["mac_address"] = ib_known_hwaddrs[cfg["name"]]
                cfg["type"] = "infiniband"

    for service in services:
        cfg = service
        cfg.update({"type": "nameserver"})
        config.append(cfg)

    return {"version": 1, "config": config}
Ejemplo n.º 19
0
def _add_network_config_from_opc_imds(network_config):
    """
    Fetch data from Oracle's IMDS, generate secondary NIC config, merge it.

    The primary NIC configuration should not be modified based on the IMDS
    values, as it should continue to be configured for DHCP.  As such, this
    takes an existing network_config dict which is expected to have the primary
    NIC configuration already present.  It will mutate the given dict to
    include the secondary VNICs.

    :param network_config:
        A v1 network config dict with the primary NIC already configured.  This
        dict will be mutated.

    :raises:
        Exceptions are not handled within this function.  Likely exceptions are
        those raised by url_helper.readurl (if communicating with the IMDS
        fails), ValueError/JSONDecodeError (if the IMDS returns invalid JSON),
        and KeyError/IndexError (if the IMDS returns valid JSON with unexpected
        contents).
    """
    resp = readurl(VNIC_METADATA_URL)
    vnics = json.loads(str(resp))

    if 'nicIndex' in vnics[0]:
        # TODO: Once configure_secondary_nics defaults to True, lower the level
        # of this log message.  (Currently, if we're running this code at all,
        # someone has explicitly opted-in to secondary VNIC configuration, so
        # we should warn them that it didn't happen.  Once it's default, this
        # would be emitted on every Bare Metal Machine launch, which means INFO
        # or DEBUG would be more appropriate.)
        LOG.warning(
            'VNIC metadata indicates this is a bare metal machine; skipping'
            ' secondary VNIC configuration.')
        return

    interfaces_by_mac = get_interfaces_by_mac()

    for vnic_dict in vnics[1:]:
        # We skip the first entry in the response because the primary interface
        # is already configured by iSCSI boot; applying configuration from the
        # IMDS is not required.
        mac_address = vnic_dict['macAddr'].lower()
        if mac_address not in interfaces_by_mac:
            LOG.debug('Interface with MAC %s not found; skipping', mac_address)
            continue
        name = interfaces_by_mac[mac_address]
        subnet = {
            'type': 'static',
            'address': vnic_dict['privateIp'],
            'netmask': vnic_dict['subnetCidrBlock'].split('/')[1],
            'gateway': vnic_dict['virtualRouterIp'],
            'control': 'manual',
        }
        network_config['config'].append({
            'name': name,
            'type': 'physical',
            'mac_address': mac_address,
            'mtu': MTU,
            'subnets': [subnet],
        })
Ejemplo n.º 20
0
    def handle_ethernets(self, command):
        """
        ethernets:
          eno1:
            match:
              macaddress: 00:11:22:33:44:55
              driver: hv_netsvc
            wakeonlan: true
            dhcp4: true
            dhcp6: false
            addresses:
              - 192.168.14.2/24
              - 2001:1::1/64
            gateway4: 192.168.14.1
            gateway6: 2001:1::2
            nameservers:
              search: [foo.local, bar.local]
              addresses: [8.8.8.8, 8.8.4.4]
          lom:
            match:
              driver: ixgbe
            set-name: lom1
            dhcp6: true
            accept-ra: true
          switchports:
            match:
              name: enp2*
            mtu: 1280

        command = {
            'type': 'physical',
            'mac_address': 'c0:d6:9f:2c:e8:80',
            'name': 'eth0',
            'subnets': [
                {'type': 'dhcp4'}
             ]
        }
        """

        # Get the interfaces by MAC address to update an interface's
        # device name to the name of the device that matches a provided
        # MAC address when the set-name directive is not present.
        #
        # Please see https://bugs.launchpad.net/cloud-init/+bug/1855945
        # for more information.
        ifaces_by_mac = get_interfaces_by_mac()

        for eth, cfg in command.items():
            phy_cmd = {
                "type": "physical",
            }
            match = cfg.get("match", {})
            mac_address = match.get("macaddress", None)
            if not mac_address:
                LOG.debug(
                    'NetworkState Version2: missing "macaddress" info '
                    "in config entry: %s: %s",
                    eth,
                    str(cfg),
                )
            phy_cmd["mac_address"] = mac_address

            # Determine the name of the interface by using one of the
            # following in the order they are listed:
            #   * set-name
            #   * interface name looked up by mac
            #   * value of "eth" key from this loop
            name = eth
            set_name = cfg.get("set-name", None)
            if set_name:
                name = set_name
            elif mac_address and ifaces_by_mac:
                lcase_mac_address = mac_address.lower()
                for iface_mac, iface_name in ifaces_by_mac.items():
                    if lcase_mac_address == iface_mac.lower():
                        name = iface_name
                        break
            phy_cmd["name"] = name

            driver = match.get("driver", None)
            if driver:
                phy_cmd["params"] = {"driver": driver}
            for key in ["mtu", "match", "wakeonlan", "accept-ra"]:
                if key in cfg:
                    phy_cmd[key] = cfg[key]

            subnets = self._v2_to_v1_ipcfg(cfg)
            if len(subnets) > 0:
                phy_cmd.update({"subnets": subnets})

            LOG.debug("v2(ethernets) -> v1(physical):\n%s", phy_cmd)
            self.handle_physical(phy_cmd)
Ejemplo n.º 21
0
def convert_to_network_config_v1(config):
    """
    Convert the UpCloud network metadata description into
    Cloud-init's version 1 netconfig format.

    Example JSON:
    {
      "interfaces": [
        {
          "index": 1,
          "ip_addresses": [
            {
              "address": "94.237.105.53",
              "dhcp": true,
              "dns": [
                "94.237.127.9",
                "94.237.40.9"
              ],
              "family": "IPv4",
              "floating": false,
              "gateway": "94.237.104.1",
              "network": "94.237.104.0/22"
            },
            {
              "address": "94.237.105.50",
              "dhcp": false,
              "dns": [],
              "family": "IPv4",
              "floating": true,
              "gateway": "",
              "network": "94.237.105.50/32"
            }
          ],
          "mac": "32:d5:ba:4a:36:e7",
          "network_id": "031457f4-0f8c-483c-96f2-eccede02909c",
          "type": "public"
        },
        {
          "index": 2,
          "ip_addresses": [
            {
              "address": "10.6.3.27",
              "dhcp": true,
              "dns": [],
              "family": "IPv4",
              "floating": false,
              "gateway": "10.6.0.1",
              "network": "10.6.0.0/22"
            }
          ],
          "mac": "32:d5:ba:4a:84:cc",
          "network_id": "03d82553-5bea-4132-b29a-e1cf67ec2dd1",
          "type": "utility"
        },
        {
          "index": 3,
          "ip_addresses": [
            {
              "address": "2a04:3545:1000:720:38d6:baff:fe4a:63e7",
              "dhcp": true,
              "dns": [
                "2a04:3540:53::1",
                "2a04:3544:53::1"
              ],
              "family": "IPv6",
              "floating": false,
              "gateway": "2a04:3545:1000:720::1",
              "network": "2a04:3545:1000:720::/64"
            }
          ],
          "mac": "32:d5:ba:4a:63:e7",
          "network_id": "03000000-0000-4000-8046-000000000000",
          "type": "public"
        },
        {
          "index": 4,
          "ip_addresses": [
            {
              "address": "172.30.1.10",
              "dhcp": true,
              "dns": [],
              "family": "IPv4",
              "floating": false,
              "gateway": "172.30.1.1",
              "network": "172.30.1.0/24"
            }
          ],
          "mac": "32:d5:ba:4a:8a:e1",
          "network_id": "035a0a4a-77b4-4de5-820d-189fc8135714",
          "type": "private"
        }
      ],
      "dns": [
        "94.237.127.9",
        "94.237.40.9"
      ]
    }
    """
    def _get_subnet_config(ip_addr, dns):
        if ip_addr.get("dhcp"):
            dhcp_type = "dhcp"
            if ip_addr.get("family") == "IPv6":
                # UpCloud currently passes IPv6 addresses via
                # StateLess Address Auto Configuration (SLAAC)
                dhcp_type = "ipv6_dhcpv6-stateless"
            return {"type": dhcp_type}

        static_type = "static"
        if ip_addr.get("family") == "IPv6":
            static_type = "static6"
        subpart = {
            "type": static_type,
            "control": "auto",
            "address": ip_addr.get("address"),
        }

        if ip_addr.get("gateway"):
            subpart["gateway"] = ip_addr.get("gateway")

        if "/" in ip_addr.get("network"):
            subpart["netmask"] = ip_addr.get("network").split("/")[1]

        if dns != ip_addr.get("dns") and ip_addr.get("dns"):
            subpart["dns_nameservers"] = ip_addr.get("dns")

        return subpart

    nic_configs = []
    macs_to_interfaces = cloudnet.get_interfaces_by_mac()
    LOG.debug("NIC mapping: %s", macs_to_interfaces)

    for raw_iface in config.get("interfaces"):
        LOG.debug("Considering %s", raw_iface)

        mac_address = raw_iface.get("mac")
        if mac_address not in macs_to_interfaces:
            raise RuntimeError(
                "Did not find network interface on system "
                "with mac '%s'. Cannot apply configuration: %s" %
                (mac_address, raw_iface))

        iface_type = raw_iface.get("type")
        sysfs_name = macs_to_interfaces.get(mac_address)

        LOG.debug(
            "Found %s interface '%s' with address '%s' (index %d)",
            iface_type,
            sysfs_name,
            mac_address,
            raw_iface.get("index"),
        )

        interface = {
            "type": "physical",
            "name": sysfs_name,
            "mac_address": mac_address
        }

        subnets = []
        for ip_address in raw_iface.get("ip_addresses"):
            sub_part = _get_subnet_config(ip_address, config.get("dns"))
            subnets.append(sub_part)

        interface["subnets"] = subnets
        nic_configs.append(interface)

    if config.get("dns"):
        LOG.debug("Setting DNS nameservers to %s", config.get("dns"))
        nic_configs.append({
            "type": "nameserver",
            "address": config.get("dns")
        })

    return {"version": 1, "config": nic_configs}
Ejemplo n.º 22
0
def convert_net_json(network_json=None, known_macs=None):
    """Return a dictionary of network_config by parsing provided
       OpenStack ConfigDrive NetworkData json format

    OpenStack network_data.json provides a 3 element dictionary
      - "links" (links are network devices, physical or virtual)
      - "networks" (networks are ip network configurations for one or more
                    links)
      -  services (non-ip services, like dns)

    networks and links are combined via network items referencing specific
    links via a 'link_id' which maps to a links 'id' field.

    To convert this format to network_config yaml, we first iterate over the
    links and then walk the network list to determine if any of the networks
    utilize the current link; if so we generate a subnet entry for the device

    We also need to map network_data.json fields to network_config fields. For
    example, the network_data links 'id' field is equivalent to network_config
    'name' field for devices.  We apply more of this mapping to the various
    link types that we encounter.

    There are additional fields that are populated in the network_data.json
    from OpenStack that are not relevant to network_config yaml, so we
    enumerate a dictionary of valid keys for network_yaml and apply filtering
    to drop these superflous keys from the network_config yaml.
    """
    if network_json is None:
        return None

    # dict of network_config key for filtering network_json
    valid_keys = {
        'physical': [
            'name',
            'type',
            'mac_address',
            'subnets',
            'params',
            'mtu',
        ],
        'subnet': [
            'type',
            'address',
            'netmask',
            'broadcast',
            'metric',
            'gateway',
            'pointopoint',
            'scope',
            'dns_nameservers',
            'dns_search',
            'routes',
        ],
    }

    links = network_json.get('links', [])
    networks = network_json.get('networks', [])
    services = network_json.get('services', [])

    link_updates = []
    link_id_info = {}
    bond_name_fmt = "bond%d"
    bond_number = 0
    config = []
    for link in links:
        subnets = []
        cfg = dict((k, v) for k, v in link.items()
                   if k in valid_keys['physical'])
        # 'name' is not in openstack spec yet, but we will support it if it is
        # present.  The 'id' in the spec is currently implemented as the host
        # nic's name, meaning something like 'tap-adfasdffd'.  We do not want
        # to name guest devices with such ugly names.
        if 'name' in link:
            cfg['name'] = link['name']

        link_mac_addr = None
        if link.get('ethernet_mac_address'):
            link_mac_addr = link.get('ethernet_mac_address').lower()
            link_id_info[link['id']] = link_mac_addr

        curinfo = {'name': cfg.get('name'), 'mac': link_mac_addr,
                   'id': link['id'], 'type': link['type']}

        for network in [n for n in networks
                        if n['link'] == link['id']]:
            subnet = dict((k, v) for k, v in network.items()
                          if k in valid_keys['subnet'])
            if 'dhcp' in network['type']:
                t = 'dhcp6' if network['type'].startswith('ipv6') else 'dhcp4'
                subnet.update({
                    'type': t,
                })
            else:
                subnet.update({
                    'type': 'static',
                    'address': network.get('ip_address'),
                })
            if network['type'] == 'ipv4':
                subnet['ipv4'] = True
            if network['type'] == 'ipv6':
                subnet['ipv6'] = True
            subnets.append(subnet)
        cfg.update({'subnets': subnets})
        if link['type'] in PHYSICAL_TYPES:
            cfg.update({'type': 'physical', 'mac_address': link_mac_addr})
        elif link['type'] in ['bond']:
            params = {}
            for k, v in link.items():
                if k == 'bond_links':
                    continue
                elif k.startswith('bond'):
                    params.update({k: v})

            # openstack does not provide a name for the bond.
            # they do provide an 'id', but that is possibly non-sensical.
            # so we just create our own name.
            link_name = bond_name_fmt % bond_number
            bond_number += 1

            # bond_links reference links by their id, but we need to add
            # to the network config by their nic name.
            # store that in bond_links_needed, and update these later.
            link_updates.append(
                (cfg, 'bond_interfaces', '%s',
                 copy.deepcopy(link['bond_links']))
            )
            cfg.update({'params': params, 'name': link_name})

            curinfo['name'] = link_name
        elif link['type'] in ['vlan']:
            name = "%s.%s" % (link['vlan_link'], link['vlan_id'])
            cfg.update({
                'name': name,
                'vlan_id': link['vlan_id'],
                'mac_address': link['vlan_mac_address'],
            })
            link_updates.append((cfg, 'vlan_link', '%s', link['vlan_link']))
            link_updates.append((cfg, 'name', "%%s.%s" % link['vlan_id'],
                                 link['vlan_link']))
            curinfo.update({'mac': link['vlan_mac_address'],
                            'name': name})
        else:
            raise ValueError(
                'Unknown network_data link type: %s' % link['type'])

        config.append(cfg)
        link_id_info[curinfo['id']] = curinfo

    need_names = [d for d in config
                  if d.get('type') == 'physical' and 'name' not in d]

    if need_names or link_updates:
        if known_macs is None:
            known_macs = net.get_interfaces_by_mac()

        # go through and fill out the link_id_info with names
        for link_id, info in link_id_info.items():
            if info.get('name'):
                continue
            if info.get('mac') in known_macs:
                info['name'] = known_macs[info['mac']]

        for d in need_names:
            mac = d.get('mac_address')
            if not mac:
                raise ValueError("No mac_address or name entry for %s" % d)
            if mac not in known_macs:
                raise ValueError("Unable to find a system nic for %s" % d)
            d['name'] = known_macs[mac]

        for cfg, key, fmt, target in link_updates:
            if isinstance(target, (list, tuple)):
                cfg[key] = [fmt % link_id_info[l]['name'] for l in target]
            else:
                cfg[key] = fmt % link_id_info[target]['name']

    for service in services:
        cfg = service
        cfg.update({'type': 'nameserver'})
        config.append(cfg)

    return {'version': 1, 'config': config}
Ejemplo n.º 23
0
def convert_net_json(network_json=None, known_macs=None):
    """Return a dictionary of network_config by parsing provided
       OpenStack ConfigDrive NetworkData json format

    OpenStack network_data.json provides a 3 element dictionary
      - "links" (links are network devices, physical or virtual)
      - "networks" (networks are ip network configurations for one or more
                    links)
      -  services (non-ip services, like dns)

    networks and links are combined via network items referencing specific
    links via a 'link_id' which maps to a links 'id' field.

    To convert this format to network_config yaml, we first iterate over the
    links and then walk the network list to determine if any of the networks
    utilize the current link; if so we generate a subnet entry for the device

    We also need to map network_data.json fields to network_config fields. For
    example, the network_data links 'id' field is equivalent to network_config
    'name' field for devices.  We apply more of this mapping to the various
    link types that we encounter.

    There are additional fields that are populated in the network_data.json
    from OpenStack that are not relevant to network_config yaml, so we
    enumerate a dictionary of valid keys for network_yaml and apply filtering
    to drop these superflous keys from the network_config yaml.
    """
    if network_json is None:
        return None

    # dict of network_config key for filtering network_json
    valid_keys = {
        'physical': [
            'name',
            'type',
            'mac_address',
            'subnets',
            'params',
            'mtu',
        ],
        'subnet': [
            'type',
            'address',
            'netmask',
            'broadcast',
            'metric',
            'gateway',
            'pointopoint',
            'scope',
            'dns_nameservers',
            'dns_search',
            'routes',
        ],
    }

    links = network_json.get('links', [])
    networks = network_json.get('networks', [])
    services = network_json.get('services', [])

    link_updates = []
    link_id_info = {}
    bond_name_fmt = "bond%d"
    bond_number = 0
    config = []
    for link in links:
        subnets = []
        cfg = dict(
            (k, v) for k, v in link.items() if k in valid_keys['physical'])
        # 'name' is not in openstack spec yet, but we will support it if it is
        # present.  The 'id' in the spec is currently implemented as the host
        # nic's name, meaning something like 'tap-adfasdffd'.  We do not want
        # to name guest devices with such ugly names.
        if 'name' in link:
            cfg['name'] = link['name']

        link_mac_addr = None
        if link.get('ethernet_mac_address'):
            link_mac_addr = link.get('ethernet_mac_address').lower()
            link_id_info[link['id']] = link_mac_addr

        curinfo = {
            'name': cfg.get('name'),
            'mac': link_mac_addr,
            'id': link['id'],
            'type': link['type']
        }

        for network in [n for n in networks if n['link'] == link['id']]:
            subnet = dict((k, v) for k, v in network.items()
                          if k in valid_keys['subnet'])
            if 'dhcp' in network['type']:
                t = (network['type']
                     if network['type'].startswith('ipv6') else 'dhcp4')
                subnet.update({
                    'type': t,
                })
            else:
                subnet.update({
                    'type': 'static',
                    'address': network.get('ip_address'),
                })
            if network['type'] == 'ipv4':
                subnet['ipv4'] = True
            if network['type'] == 'ipv6':
                subnet['ipv6'] = True
            subnets.append(subnet)
        cfg.update({'subnets': subnets})
        if link['type'] in ['bond']:
            params = {}
            if link_mac_addr:
                params['mac_address'] = link_mac_addr
            for k, v in link.items():
                if k == 'bond_links':
                    continue
                elif k.startswith('bond'):
                    params.update({k: v})

            # openstack does not provide a name for the bond.
            # they do provide an 'id', but that is possibly non-sensical.
            # so we just create our own name.
            link_name = bond_name_fmt % bond_number
            bond_number += 1

            # bond_links reference links by their id, but we need to add
            # to the network config by their nic name.
            # store that in bond_links_needed, and update these later.
            link_updates.append((cfg, 'bond_interfaces', '%s',
                                 copy.deepcopy(link['bond_links'])))
            cfg.update({'params': params, 'name': link_name})

            curinfo['name'] = link_name
        elif link['type'] in ['vlan']:
            name = "%s.%s" % (link['vlan_link'], link['vlan_id'])
            cfg.update({
                'name': name,
                'vlan_id': link['vlan_id'],
                'mac_address': link['vlan_mac_address'],
            })
            link_updates.append((cfg, 'vlan_link', '%s', link['vlan_link']))
            link_updates.append(
                (cfg, 'name', "%%s.%s" % link['vlan_id'], link['vlan_link']))
            curinfo.update({'mac': link['vlan_mac_address'], 'name': name})
        else:
            if link['type'] not in KNOWN_PHYSICAL_TYPES:
                LOG.warning(
                    'Unknown network_data link type (%s); treating as'
                    ' physical', link['type'])
            cfg.update({'type': 'physical', 'mac_address': link_mac_addr})

        config.append(cfg)
        link_id_info[curinfo['id']] = curinfo

    need_names = [
        d for d in config if d.get('type') == 'physical' and 'name' not in d
    ]

    if need_names or link_updates:
        if known_macs is None:
            known_macs = net.get_interfaces_by_mac()

        # go through and fill out the link_id_info with names
        for _link_id, info in link_id_info.items():
            if info.get('name'):
                continue
            if info.get('mac') in known_macs:
                info['name'] = known_macs[info['mac']]

        for d in need_names:
            mac = d.get('mac_address')
            if not mac:
                raise ValueError("No mac_address or name entry for %s" % d)
            if mac not in known_macs:
                raise ValueError("Unable to find a system nic for %s" % d)
            d['name'] = known_macs[mac]

        for cfg, key, fmt, target in link_updates:
            if isinstance(target, (list, tuple)):
                cfg[key] = [fmt % link_id_info[l]['name'] for l in target]
            else:
                cfg[key] = fmt % link_id_info[target]['name']

    # Infiniband interfaces may be referenced in network_data.json by a 6 byte
    # Ethernet MAC-style address, and we use that address to look up the
    # interface name above. Now ensure that the hardware address is set to the
    # full 20 byte address.
    ib_known_hwaddrs = net.get_ib_hwaddrs_by_interface()
    if ib_known_hwaddrs:
        for cfg in config:
            if cfg['name'] in ib_known_hwaddrs:
                cfg['mac_address'] = ib_known_hwaddrs[cfg['name']]
                cfg['type'] = 'infiniband'

    for service in services:
        cfg = service
        cfg.update({'type': 'nameserver'})
        config.append(cfg)

    return {'version': 1, 'config': config}
Ejemplo n.º 24
0
    def network_config(self):
        """Configure the networking. This needs to be done each boot, since
           the IP information may have changed due to snapshot and/or
           migration.
        """

        if self._network_config:
            return self._network_config

        interfaces = self.metadata.get('interfaces')

        if not interfaces:
            raise Exception("Unable to get meta-data from server....")

        # Convert Vultr network configuration to cloudinit.net format

        #    Example JSON:
        #    [
        #     {
        #         "ipv4": {
        #             "additional": [
        #                 {
        #                     "address": "192.0.2.3",
        #                     "netmask": "255.255.255.0"
        #                 }
        #             ],
        #             "address": "192.0.2.2",
        #             "gateway": "192.0.2.1",
        #             "netmask": "255.255.255.0"
        #         },
        #         "ipv6": {
        #             "additional": [
        #                 {
        #                     "network": "2001:0db8:0:2::",
        #                     "prefix": "64"
        #                 }
        #             ],
        #             "address": "2001:0db8:0:1:5428:d5ff:fe28:1910",
        #             "network": "2001:0db8:0:1::",
        #             "prefix": "64"
        #         },
        #         "mac": "00:00:00:00:00:00",
        #         "network-type": "public"
        #     },
        #     ......
        # ]

        nic_configs = []
        macs_to_nics = cloudnet.get_interfaces_by_mac()
        LOG.debug("nic mapping: %s", macs_to_nics)

        config = []
        for vultr_ip_dict in interfaces:
            mac = vultr_ip_dict["mac"]

            if mac not in macs_to_nics:
                raise ValueError("Did not find network interface on system "
                        "with mac '%s'. Cannot apply configuration: %s"
                        % (mac_address, nic))
            if_name = macs_to_nics[mac]  # if_name = string 'eth0', ...
            if_config= {
                    'type': 'physical',
                    'mac_address': mac,
                    'name': if_name,
                    'subnets': [{
                        'type': 'dhcp',
                        'control': 'auto',
                        }
                     ]
            }
            config.append(if_config)

            LOG.debug("nic '%s' configuration: %s", if_name, if_config)

        LOG.debug("added dns servers: %s", self.dns_servers)
        config.append({'type': 'nameserver', 'address': self.dns_servers})

        return {'version': 1, 'config': config}
Ejemplo n.º 25
0
def get_physical_nics_by_mac():
    devs = net.get_interfaces_by_mac()
    return dict([(m, n) for m, n in devs.items() if net.is_physical(n)])
Ejemplo n.º 26
0
def convert_net_json(network_json=None, known_macs=None):
    """Return a dictionary of network_config by parsing provided
       OpenStack ConfigDrive NetworkData json format

    OpenStack network_data.json provides a 3 element dictionary
      - "links" (links are network devices, physical or virtual)
      - "networks" (networks are ip network configurations for one or more
                    links)
      -  services (non-ip services, like dns)

    networks and links are combined via network items referencing specific
    links via a 'link_id' which maps to a links 'id' field.

    To convert this format to network_config yaml, we first iterate over the
    links and then walk the network list to determine if any of the networks
    utilize the current link; if so we generate a subnet entry for the device

    We also need to map network_data.json fields to network_config fields. For
    example, the network_data links 'id' field is equivalent to network_config
    'name' field for devices.  We apply more of this mapping to the various
    link types that we encounter.

    There are additional fields that are populated in the network_data.json
    from OpenStack that are not relevant to network_config yaml, so we
    enumerate a dictionary of valid keys for network_yaml and apply filtering
    to drop these superflous keys from the network_config yaml.
    """
    if network_json is None:
        return None

    # dict of network_config key for filtering network_json
    valid_keys = {
        "physical": ["name", "type", "mac_address", "subnets", "params", "mtu"],
        "subnet": [
            "type",
            "address",
            "netmask",
            "broadcast",
            "metric",
            "gateway",
            "pointopoint",
            "scope",
            "dns_nameservers",
            "dns_search",
            "routes",
        ],
    }

    links = network_json.get("links", [])
    networks = network_json.get("networks", [])
    services = network_json.get("services", [])

    config = []
    for link in links:
        subnets = []
        cfg = dict((k, v) for k, v in link.items() if k in valid_keys["physical"])
        # 'name' is not in openstack spec yet, but we will support it if it is
        # present.  The 'id' in the spec is currently implemented as the host
        # nic's name, meaning something like 'tap-adfasdffd'.  We do not want
        # to name guest devices with such ugly names.
        if "name" in link:
            cfg["name"] = link["name"]

        for network in [n for n in networks if n["link"] == link["id"]]:
            subnet = dict((k, v) for k, v in network.items() if k in valid_keys["subnet"])
            if "dhcp" in network["type"]:
                t = "dhcp6" if network["type"].startswith("ipv6") else "dhcp4"
                subnet.update({"type": t})
            else:
                subnet.update({"type": "static", "address": network.get("ip_address")})
            if network["type"] == "ipv4":
                subnet["ipv4"] = True
            if network["type"] == "ipv6":
                subnet["ipv6"] = True
            subnets.append(subnet)
        cfg.update({"subnets": subnets})
        if link["type"] in ["ethernet", "vif", "ovs", "phy", "bridge"]:
            cfg.update({"type": "physical", "mac_address": link["ethernet_mac_address"]})
        elif link["type"] in ["bond"]:
            params = {}
            for k, v in link.items():
                if k == "bond_links":
                    continue
                elif k.startswith("bond"):
                    params.update({k: v})
            cfg.update({"bond_interfaces": copy.deepcopy(link["bond_links"]), "params": params})
        elif link["type"] in ["vlan"]:
            cfg.update(
                {
                    "name": "%s.%s" % (link["vlan_link"], link["vlan_id"]),
                    "vlan_link": link["vlan_link"],
                    "vlan_id": link["vlan_id"],
                    "mac_address": link["vlan_mac_address"],
                }
            )
        else:
            raise ValueError("Unknown network_data link type: %s" % link["type"])

        config.append(cfg)

    need_names = [d for d in config if d.get("type") == "physical" and "name" not in d]

    if need_names:
        if known_macs is None:
            known_macs = net.get_interfaces_by_mac()

        for d in need_names:
            mac = d.get("mac_address")
            if not mac:
                raise ValueError("No mac_address or name entry for %s" % d)
            if mac not in known_macs:
                raise ValueError("Unable to find a system nic for %s" % d)
            d["name"] = known_macs[mac]

    for service in services:
        cfg = service
        cfg.update({"type": "nameserver"})
        config.append(cfg)

    return {"version": 1, "config": config}
def get_physical_nics_by_mac(distro):
    devs = net.get_interfaces_by_mac()
    return dict(
        [(m, n) for m, n in devs.items() if distro.networking.is_physical(n)]
    )
Ejemplo n.º 28
0
def convert_ec2_metadata_network_config(network_md,
                                        macs_to_nics=None,
                                        fallback_nic=None,
                                        full_network_config=True):
    """Convert ec2 metadata to network config version 2 data dict.

    @param: network_md: 'network' portion of EC2 metadata.
       generally formed as {"interfaces": {"macs": {}} where
       'macs' is a dictionary with mac address as key and contents like:
       {"device-number": "0", "interface-id": "...", "local-ipv4s": ...}
    @param: macs_to_nics: Optional dict of mac addresses and nic names. If
       not provided, get_interfaces_by_mac is called to get it from the OS.
    @param: fallback_nic: Optionally provide the primary nic interface name.
       This nic will be guaranteed to minimally have a dhcp4 configuration.
    @param: full_network_config: Boolean set True to configure all networking
       presented by IMDS. This includes rendering secondary IPv4 and IPv6
       addresses on all NICs and rendering network config on secondary NICs.
       If False, only the primary nic will be configured and only with dhcp
       (IPv4/IPv6).

    @return A dict of network config version 2 based on the metadata and macs.
    """
    netcfg = {'version': 2, 'ethernets': {}}
    if not macs_to_nics:
        macs_to_nics = net.get_interfaces_by_mac()
    macs_metadata = network_md['interfaces']['macs']

    if not full_network_config:
        for mac, nic_name in macs_to_nics.items():
            if nic_name == fallback_nic:
                break
        dev_config = {
            'dhcp4': True,
            'dhcp6': False,
            'match': {
                'macaddress': mac.lower()
            },
            'set-name': nic_name
        }
        nic_metadata = macs_metadata.get(mac)
        if nic_metadata.get('ipv6s'):  # Any IPv6 addresses configured
            dev_config['dhcp6'] = True
        netcfg['ethernets'][nic_name] = dev_config
        return netcfg
    # Apply network config for all nics and any secondary IPv4/v6 addresses
    nic_idx = 1
    for mac, nic_name in sorted(macs_to_nics.items()):
        nic_metadata = macs_metadata.get(mac)
        if not nic_metadata:
            continue  # Not a physical nic represented in metadata
        dhcp_override = {'route-metric': nic_idx * 100}
        nic_idx += 1
        dev_config = {
            'dhcp4': True,
            'dhcp4-overrides': dhcp_override,
            'dhcp6': False,
            'match': {
                'macaddress': mac.lower()
            },
            'set-name': nic_name
        }
        if nic_metadata.get('ipv6s'):  # Any IPv6 addresses configured
            dev_config['dhcp6'] = True
            dev_config['dhcp6-overrides'] = dhcp_override
        dev_config['addresses'] = get_secondary_addresses(nic_metadata, mac)
        if not dev_config['addresses']:
            dev_config.pop('addresses')  # Since we found none configured
        netcfg['ethernets'][nic_name] = dev_config
    # Remove route-metric dhcp overrides if only one nic configured
    if len(netcfg['ethernets']) == 1:
        for nic_name in netcfg['ethernets'].keys():
            netcfg['ethernets'][nic_name].pop('dhcp4-overrides')
            netcfg['ethernets'][nic_name].pop('dhcp6-overrides', None)
    return netcfg
Ejemplo n.º 29
0
def convert_ec2_metadata_network_config(network_md,
                                        macs_to_nics=None,
                                        fallback_nic=None,
                                        full_network_config=True):
    """Convert ec2 metadata to network config version 2 data dict.

    @param: network_md: 'network' portion of EC2 metadata.
       generally formed as {"interfaces": {"macs": {}} where
       'macs' is a dictionary with mac address as key and contents like:
       {"device-number": "0", "interface-id": "...", "local-ipv4s": ...}
    @param: macs_to_nics: Optional dict of mac addresses and nic names. If
       not provided, get_interfaces_by_mac is called to get it from the OS.
    @param: fallback_nic: Optionally provide the primary nic interface name.
       This nic will be guaranteed to minimally have a dhcp4 configuration.
    @param: full_network_config: Boolean set True to configure all networking
       presented by IMDS. This includes rendering secondary IPv4 and IPv6
       addresses on all NICs and rendering network config on secondary NICs.
       If False, only the primary nic will be configured and only with dhcp
       (IPv4/IPv6).

    @return A dict of network config version 2 based on the metadata and macs.
    """
    netcfg = {"version": 2, "ethernets": {}}
    if not macs_to_nics:
        macs_to_nics = net.get_interfaces_by_mac()
    macs_metadata = network_md["interfaces"]["macs"]

    if not full_network_config:
        for mac, nic_name in macs_to_nics.items():
            if nic_name == fallback_nic:
                break
        dev_config = {
            "dhcp4": True,
            "dhcp6": False,
            "match": {
                "match": nicname
            },
            "set-name": nic_name,
        }
        nic_metadata = macs_metadata.get(mac)
        if nic_metadata.get("ipv6s"):  # Any IPv6 addresses configured
            dev_config["dhcp6"] = True
        netcfg["ethernets"][nic_name] = dev_config
        return netcfg
    # Apply network config for all nics and any secondary IPv4/v6 addresses
    nic_idx = 0
    for mac, nic_name in sorted(macs_to_nics.items()):
        nic_metadata = macs_metadata.get(mac)
        if not nic_metadata:
            continue  # Not a physical nic represented in metadata
        # device-number is zero-indexed, we want it 1-indexed for the
        # multiplication on the following line
        nic_idx = int(nic_metadata.get("device-number", nic_idx)) + 1
        dhcp_override = {"route-metric": nic_idx * 100}
        dev_config = {
            "dhcp4": True,
            "dhcp4-overrides": dhcp_override,
            "dhcp6": False,
            "match": {
                "name": nic_name
            },
            "set-name": nic_name,
        }
        if nic_metadata.get("ipv6s"):  # Any IPv6 addresses configured
            dev_config["dhcp6"] = True
            dev_config["dhcp6-overrides"] = dhcp_override
        dev_config["addresses"] = get_secondary_addresses(nic_metadata, mac)
        if not dev_config["addresses"]:
            dev_config.pop("addresses")  # Since we found none configured
        netcfg["ethernets"][nic_name] = dev_config
    # Remove route-metric dhcp overrides if only one nic configured
    if len(netcfg["ethernets"]) == 1:
        for nic_name in netcfg["ethernets"].keys():
            netcfg["ethernets"][nic_name].pop("dhcp4-overrides")
            netcfg["ethernets"][nic_name].pop("dhcp6-overrides", None)
    return netcfg
Ejemplo n.º 30
0
def convert_network_configuration(config, dns_servers):
    """Convert the DigitalOcean Network description into Cloud-init's netconfig
       format.

       Example JSON:
        {'public': [
              {'mac': '04:01:58:27:7f:01',
               'ipv4': {'gateway': '45.55.32.1',
                        'netmask': '255.255.224.0',
                        'ip_address': '45.55.50.93'},
               'anchor_ipv4': {
                        'gateway': '10.17.0.1',
                        'netmask': '255.255.0.0',
                        'ip_address': '10.17.0.9'},
               'type': 'public',
               'ipv6': {'gateway': '....',
                        'ip_address': '....',
                        'cidr': 64}}
           ],
          'private': [
              {'mac': '04:01:58:27:7f:02',
               'ipv4': {'gateway': '10.132.0.1',
                        'netmask': '255.255.0.0',
                        'ip_address': '10.132.75.35'},
               'type': 'private'}
           ]
        }
    """

    def _get_subnet_part(pcfg):
        subpart = {'type': 'static',
                   'control': 'auto',
                   'address': pcfg.get('ip_address'),
                   'gateway': pcfg.get('gateway')}

        if ":" in pcfg.get('ip_address'):
            subpart['address'] = "{0}/{1}".format(pcfg.get('ip_address'),
                                                  pcfg.get('cidr'))
        else:
            subpart['netmask'] = pcfg.get('netmask')

        return subpart

    nic_configs = []
    macs_to_nics = cloudnet.get_interfaces_by_mac()
    LOG.debug("nic mapping: %s", macs_to_nics)

    for n in config:
        nic = config[n][0]
        LOG.debug("considering %s", nic)

        mac_address = nic.get('mac')
        if mac_address not in macs_to_nics:
            raise RuntimeError("Did not find network interface on system "
                               "with mac '%s'. Cannot apply configuration: %s"
                               % (mac_address, nic))

        sysfs_name = macs_to_nics.get(mac_address)
        nic_type = nic.get('type', 'unknown')

        if_name = NIC_MAP.get(nic_type, sysfs_name)
        if if_name != sysfs_name:
            LOG.debug("Found %s interface '%s' on '%s', assigned name of '%s'",
                      nic_type, mac_address, sysfs_name, if_name)
        else:
            msg = ("Found interface '%s' on '%s', which is not a public "
                   "or private interface. Using default system naming.")
            LOG.debug(msg, mac_address, sysfs_name)

        ncfg = {'type': 'physical',
                'mac_address': mac_address,
                'name': if_name}

        subnets = []
        for netdef in ('ipv4', 'ipv6', 'anchor_ipv4', 'anchor_ipv6'):
            raw_subnet = nic.get(netdef, None)
            if not raw_subnet:
                continue

            sub_part = _get_subnet_part(raw_subnet)
            if nic_type != "public" or "anchor" in netdef:
                del sub_part['gateway']

            subnets.append(sub_part)

        ncfg['subnets'] = subnets
        nic_configs.append(ncfg)
        LOG.debug("nic '%s' configuration: %s", if_name, ncfg)

    if dns_servers:
        LOG.debug("added dns servers: %s", dns_servers)
        nic_configs.append({'type': 'nameserver', 'address': dns_servers})

    return {'version': 1, 'config': nic_configs}
Ejemplo n.º 31
0
def convert_network_configuration(config, dns_servers):
    """Convert the DigitalOcean Network description into Cloud-init's netconfig
       format.

       Example JSON:
        {'public': [
              {'mac': '04:01:58:27:7f:01',
               'ipv4': {'gateway': '45.55.32.1',
                        'netmask': '255.255.224.0',
                        'ip_address': '45.55.50.93'},
               'anchor_ipv4': {
                        'gateway': '10.17.0.1',
                        'netmask': '255.255.0.0',
                        'ip_address': '10.17.0.9'},
               'type': 'public',
               'ipv6': {'gateway': '....',
                        'ip_address': '....',
                        'cidr': 64}}
           ],
          'private': [
              {'mac': '04:01:58:27:7f:02',
               'ipv4': {'gateway': '10.132.0.1',
                        'netmask': '255.255.0.0',
                        'ip_address': '10.132.75.35'},
               'type': 'private'}
           ]
        }
    """
    def _get_subnet_part(pcfg, nameservers=None):
        subpart = {
            'type': 'static',
            'control': 'auto',
            'address': pcfg.get('ip_address'),
            'gateway': pcfg.get('gateway')
        }

        if nameservers:
            subpart['dns_nameservers'] = nameservers

        if ":" in pcfg.get('ip_address'):
            subpart['address'] = "{0}/{1}".format(pcfg.get('ip_address'),
                                                  pcfg.get('cidr'))
        else:
            subpart['netmask'] = pcfg.get('netmask')

        return subpart

    all_nics = []
    for k in ('public', 'private'):
        if k in config:
            all_nics.extend(config[k])

    macs_to_nics = cloudnet.get_interfaces_by_mac()
    nic_configs = []

    for nic in all_nics:

        mac_address = nic.get('mac')
        sysfs_name = macs_to_nics.get(mac_address)
        nic_type = nic.get('type', 'unknown')
        # Note: the entry 'public' above contains a list, but
        # the list will only ever have one nic inside it per digital ocean.
        # If it ever had more than one nic, then this code would
        # assign all 'public' the same name.
        if_name = NIC_MAP.get(nic_type, sysfs_name)

        LOG.debug("mapped %s interface to %s, assigning name of %s",
                  mac_address, sysfs_name, if_name)

        ncfg = {
            'type': 'physical',
            'mac_address': mac_address,
            'name': if_name
        }

        subnets = []
        for netdef in ('ipv4', 'ipv6', 'anchor_ipv4', 'anchor_ipv6'):
            raw_subnet = nic.get(netdef, None)
            if not raw_subnet:
                continue

            sub_part = _get_subnet_part(raw_subnet)
            if nic_type == 'public' and 'anchor' not in netdef:
                # add DNS resolvers to the public interfaces only
                sub_part = _get_subnet_part(raw_subnet, dns_servers)
            else:
                # remove the gateway any non-public interfaces
                if 'gateway' in sub_part:
                    del sub_part['gateway']

            subnets.append(sub_part)

        ncfg['subnets'] = subnets
        nic_configs.append(ncfg)
        LOG.debug("nic '%s' configuration: %s", if_name, ncfg)

    return {'version': 1, 'config': nic_configs}
Ejemplo n.º 32
0
def get_interface_map():
    return net.get_interfaces_by_mac()