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
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)
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
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'
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()]
def net_attr(context, name, attr, inventory_hostname=None): var_name = "%s_%s" % (name, attr) return utils.get_hostvar(context, var_name, inventory_hostname)
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
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)