Exemplo n.º 1
0
def _get_veth_peer(context, bridge, inventory_hostname):
    """Return a veth peer name for a bridge.

    :param context: a Jinja2 Context object.
    :param bridge: name of the bridge interface into which the veth is plugged.
    :param inventory_hostname: Ansible inventory hostname.
    :returns: a veth peer name for a bridge.
    """
    prefix = utils.get_hostvar(context, 'network_patch_prefix',
                               inventory_hostname)
    suffix = utils.get_hostvar(context, 'network_patch_suffix_ovs',
                               inventory_hostname)
    return prefix + bridge + suffix
Exemplo n.º 2
0
def net_ip(context, name, inventory_hostname=None):
    ips = net_attr(context, name, 'ips', inventory_hostname)
    if ips:
        if inventory_hostname is None:
            inventory_hostname = utils.get_hostvar(context,
                                                   "inventory_hostname")
        return ips.get(inventory_hostname)
Exemplo n.º 3
0
def networkd_netdevs(context, names, inventory_hostname=None):
    """Return a dict representation of networkd NetDev configuration.

    The format is compatible with the systemd_networkd_netdev variable in the
    stackhpc.ansible_role_systemd_networkd role.

    :param context: a Jinja2 Context object.
    :param names: List of names of networks.
    :param inventory_hostname: Ansible inventory hostname.
    :returns: a dict representation of networkd NetDev configuration.
    """
    # Prefix for configuration file names.
    prefix = utils.get_hostvar(context, "networkd_prefix", inventory_hostname)

    result = {}

    # VLANs.
    for name in networks.net_select_vlan_interfaces(context, names,
                                                    inventory_hostname):
        device = networks.get_and_validate_interface(context, name,
                                                     inventory_hostname)
        netdev = _vlan_netdev(context, name, inventory_hostname)
        _add_to_result(result, prefix, device, netdev)

    # Bridges.
    for name in networks.net_select_bridges(context, names,
                                            inventory_hostname):
        device = networks.get_and_validate_interface(context, name,
                                                     inventory_hostname)
        netdev = _bridge_netdev(context, name, inventory_hostname)
        _add_to_result(result, prefix, device, netdev)

    # Bonds.
    for name in networks.net_select_bonds(context, names, inventory_hostname):
        device = networks.get_and_validate_interface(context, name,
                                                     inventory_hostname)
        netdev = _bond_netdev(context, name, inventory_hostname)
        _add_to_result(result, prefix, device, netdev)

    # Virtual Ethernet pairs.
    veths = networks.get_ovs_veths(context, names, inventory_hostname)
    for veth in veths:
        netdev = _veth_netdev(context, veth, inventory_hostname)
        device = veth['name']
        _add_to_result(result, prefix, device, netdev)

    return result
Exemplo n.º 4
0
def _net_interface_type(context, name, inventory_hostname):
    """Return a string describing the network interface type.

    Possible types include 'ether', 'bridge', 'bond'.
    """
    bridge_ports = net_bridge_ports(context, name, inventory_hostname)
    bond_slaves = net_bond_slaves(context, name, inventory_hostname)
    if bridge_ports is not None and bond_slaves is not None:
        raise errors.AnsibleFilterError(
            "Network %s on host %s has both bridge ports and bond slaves "
            "defined" % (name,
                         utils.get_hostvar(context, 'inventory_hostname',
                                           inventory_hostname)))
    if bridge_ports is None and bond_slaves is None:
        return 'ether'
    if bridge_ports is not None:
        return 'bridge'
    if bond_slaves is not None:
        return 'bond'
Exemplo n.º 5
0
def get_ovs_veths(context, names, inventory_hostname):
    """Return a list of dicts describing veth pairs to plug into Open vSwitch.

    :param context: a Jinja2 Context object.
    :param names: list of names of networks.
    :param inventory_hostname: Ansible inventory hostname.
    :returns: a list of dicts describing veth pairs. Each dict has keys 'name',
              'peer', 'bridge', and 'mtu'.
    """
    # The following networks need to be plugged into Open vSwitch:
    # * workload provisioning network
    # * workload cleaning network
    # * neutron external networks
    ironic_networks = [
        utils.get_hostvar(context, 'provision_wl_net_name',
                          inventory_hostname),
        utils.get_hostvar(context, 'cleaning_net_name', inventory_hostname),
    ]
    external_networks = utils.get_hostvar(context, 'external_net_names',
                                          inventory_hostname)
    veth_networks = ironic_networks + (external_networks or [])

    # Make a list of all bridge interfaces.
    bridges = net_select_bridges(context, names, inventory_hostname)
    bridge_interfaces = [
        net_interface(context, bridge, inventory_hostname)
        for bridge in bridges
    ]

    # Dict mapping bridge interfaces to the MTU of a connected veth pair.
    veth_mtu_map = {}
    for name in veth_networks:
        if name not in names:
            continue
        device = get_and_validate_interface(context, name, inventory_hostname)
        # When these networks are VLANs, we need to use the underlying tagged
        # interface rather than the untagged interface. We therefore strip the
        # .<vlan> suffix of the interface name. We use a union here as a single
        # tagged interface may be shared between these networks.
        vlan = net_vlan(context, name, inventory_hostname)
        if vlan:
            parent_or_device = get_vlan_parent(device, vlan)
        else:
            parent_or_device = device
        if parent_or_device in bridge_interfaces:
            # Determine the MTU as the maximum of all subinterface MTUs. Only
            # interfaces with an explicit MTU set will be taken account of. If
            # no interface has an explicit MTU set, then the corresponding veth
            # will not either.
            # Allow for the case where an MTU is not specified.
            mtu = net_mtu(context, name, inventory_hostname)
            veth_mtu_map.setdefault(parent_or_device, mtu)
            if (veth_mtu_map.get(parent_or_device) or 0) < (mtu or 0):
                veth_mtu_map[parent_or_device] = mtu

    return [{
        'name': _get_veth_interface(context, bridge, inventory_hostname),
        'peer': _get_veth_peer(context, bridge, inventory_hostname),
        'bridge': bridge,
        'mtu': mtu
    } for bridge, mtu in veth_mtu_map.items()]
Exemplo n.º 6
0
def net_attr(context, name, attr, inventory_hostname=None):
    var_name = "%s_%s" % (name, attr)
    return utils.get_hostvar(context, var_name, inventory_hostname)
Exemplo n.º 7
0
def networkd_networks(context, names, inventory_hostname=None):
    """Return a dict representation of networkd network configuration.

    The format is compatible with the systemd_networkd_network variable in the
    stackhpc.ansible_role_systemd_networkd role.

    :param context: a Jinja2 Context object.
    :param names: List of names of networks.
    :param inventory_hostname: Ansible inventory hostname.
    :returns: a dict representation of networkd network configuration.
    """
    # TODO(mgoddard): some attributes are currently not supported for
    # systemd-networkd: rules, route options, ethtool_opts, zone,
    # allowed addresses

    # Build up some useful mappings.
    bridge_port_to_bridge = {}
    bond_member_to_bond = {}
    interface_to_vlans = {}

    # List of all interfaces.
    interfaces = [
        networks.net_interface(context, name, inventory_hostname)
        for name in names
    ]

    # Map bridge ports to bridges.
    for name in networks.net_select_bridges(context, names,
                                            inventory_hostname):
        device = networks.get_and_validate_interface(context, name,
                                                     inventory_hostname)
        for port in networks.net_bridge_ports(context, name,
                                              inventory_hostname):
            bridge_port_to_bridge[port] = device

    # Map bond members to bonds.
    for name in networks.net_select_bonds(context, names, inventory_hostname):
        device = networks.get_and_validate_interface(context, name,
                                                     inventory_hostname)
        for member in networks.net_bond_slaves(context, name,
                                               inventory_hostname):
            bond_member_to_bond[member] = device

    # Map interfaces to lists of VLAN subinterfaces.
    for name in networks.net_select_vlans(context, names, inventory_hostname):
        device = networks.get_and_validate_interface(context, name,
                                                     inventory_hostname)
        vlan = networks.net_vlan(context, name, inventory_hostname)
        mtu = networks.net_mtu(context, name, inventory_hostname)
        parent = networks.get_vlan_parent(device, vlan)
        vlan_interfaces = interface_to_vlans.setdefault(parent, [])
        vlan_interfaces.append({"device": device, "mtu": mtu})

    # Prefix for configuration file names.
    prefix = utils.get_hostvar(context, "networkd_prefix", inventory_hostname)

    result = {}

    # Configured networks.
    for name in names:
        device = networks.get_and_validate_interface(context, name,
                                                     inventory_hostname)
        bridge = bridge_port_to_bridge.get(device)
        bond = bond_member_to_bond.get(device)
        vlan_interfaces = interface_to_vlans.get(device, [])
        net = _network(context, name, inventory_hostname, bridge, bond,
                       [vlan["device"] for vlan in vlan_interfaces])
        _add_to_result(result, prefix, device, net)

    # VLAN parent interfaces that are not in configured networks, bridge ports
    # or bond members.
    implied_vlan_parents = (set(interface_to_vlans) - set(interfaces) -
                            set(bridge_port_to_bridge) -
                            set(bond_member_to_bond))
    for device in implied_vlan_parents:
        vlan_interfaces = interface_to_vlans[device]
        mtu = max([vlan["mtu"] for vlan in vlan_interfaces])
        net = _vlan_parent_network(
            device, mtu, [vlan["device"] for vlan in vlan_interfaces])
        _add_to_result(result, prefix, device, net)

    # Bridge ports that are not in configured networks.
    for name in networks.net_select_bridges(context, names,
                                            inventory_hostname):
        device = networks.get_and_validate_interface(context, name,
                                                     inventory_hostname)
        bridge_ports = networks.net_bridge_ports(context, name,
                                                 inventory_hostname)
        for port in set(bridge_ports) - set(interfaces):
            vlan_interfaces = interface_to_vlans.get(port, [])
            net = _bridge_port_network(
                context, name, port, inventory_hostname,
                [vlan["device"] for vlan in vlan_interfaces])
            _add_to_result(result, prefix, port, net)

    # Bond members that are not in configured networks.
    for name in networks.net_select_bonds(context, names, inventory_hostname):
        device = networks.get_and_validate_interface(context, name,
                                                     inventory_hostname)
        bond_members = networks.net_bond_slaves(context, name,
                                                inventory_hostname)
        for member in set(bond_members) - set(interfaces):
            vlan_interfaces = interface_to_vlans.get(member, [])
            net = _bond_member_network(
                context, name, member, inventory_hostname,
                [vlan["device"] for vlan in vlan_interfaces])
            _add_to_result(result, prefix, member, net)

    # Virtual Ethernet pairs for Open vSwitch.
    veths = networks.get_ovs_veths(context, names, inventory_hostname)
    for veth in veths:
        net = _veth_network(context, veth, inventory_hostname)
        device = veth['name']
        _add_to_result(result, prefix, device, net)

        net = _veth_peer_network(context, veth, inventory_hostname)
        device = veth['peer']
        _add_to_result(result, prefix, device, net)

    return result
Exemplo n.º 8
0
def _network(context, name, inventory_hostname, bridge, bond, vlan_interfaces):
    """Return a networkd network for an interface.

    :param context: a Jinja2 Context object.
    :param name: name of the network.
    :param inventory_hostname: Ansible inventory hostname.
    :param bridge: Name of a bridge into which the interface is plugged, or
                   None.
    :param bond: Name of a bond of which the interface is a member, or None.
    :param vlan_interfaces: List of VLAN subinterfaces of the interface.
    """
    # FIXME(mgoddard): Currently does not support: ethtool_opts, zone,
    # allowed_addresses.
    device = networks.net_interface(context, name, inventory_hostname)
    ip = networks.net_ip(context, name, inventory_hostname)
    cidr = networks.net_cidr(context, name, inventory_hostname)
    gateway = networks.net_gateway(context, name, inventory_hostname)
    if ip is None:
        gateway = None
    else:
        if not cidr:
            raise errors.AnsibleFilterError(
                "No CIDR attribute configured for '%s' network but it has an "
                "IP address" % (name))
        ip = "%s/%s" % (ip, ipaddress.ip_network(cidr).prefixlen)

    mtu = networks.net_mtu(context, name, inventory_hostname)
    routes = networks.net_routes(context, name, inventory_hostname)
    rules = networks.net_rules(context, name, inventory_hostname)
    bootproto = networks.net_bootproto(context, name, inventory_hostname)
    defroute = networks.net_defroute(context, name, inventory_hostname)
    if defroute is not None:
        defroute = utils.call_bool_filter(context, defroute)
    config = [
        {
            'Match': [
                {
                    'Name': device
                },
            ]
        },
        {
            'Network': [
                {
                    'Address': ip
                },
                {
                    'Gateway': gateway
                },
                {
                    'DHCP': ('yes' if bootproto and bootproto.lower() == 'dhcp'
                             else None)
                },
                {
                    'UseGateway': ('false' if defroute is not None
                                   and not defroute else None)
                },
                {
                    'Bridge': bridge
                },
                {
                    'Bond': bond
                },
            ] + [{
                'VLAN': vlan_interface
            } for vlan_interface in vlan_interfaces]
        },
        {
            'Link': [
                {
                    'MTUBytes': mtu
                },
            ]
        },
    ]

    # NOTE(mgoddard): Systemd-networkd does not support named route tables
    # until v248. Until then, translate names to numeric IDs using the
    # network_route_tables variable.
    route_tables = utils.get_hostvar(context, "network_route_tables",
                                     inventory_hostname)
    route_tables = {table["name"]: table["id"] for table in route_tables}
    config += _network_routes(routes, route_tables)
    config += _network_rules(rules, route_tables)

    return _filter_options(config)