From 398c3de590f3cb52894b11565b545ee82b11fef0 Mon Sep 17 00:00:00 2001 From: Eric Lund Date: Fri, 21 Feb 2025 14:46:53 -0600 Subject: [PATCH] Restructure config to prepare for MAC address changes --- vtds_cluster_kvm/private/api_objects.py | 20 ++-- vtds_cluster_kvm/private/cluster.py | 27 +++-- vtds_cluster_kvm/private/config/config.yaml | 32 +++--- .../scripts/deploy_cluster_to_blade.py | 105 ++++++++++-------- 4 files changed, 105 insertions(+), 79 deletions(-) diff --git a/vtds_cluster_kvm/private/api_objects.py b/vtds_cluster_kvm/private/api_objects.py index 07f7d14..4381fc2 100644 --- a/vtds_cluster_kvm/private/api_objects.py +++ b/vtds_cluster_kvm/private/api_objects.py @@ -189,18 +189,20 @@ def __network_by_name(self, network_name): ) return self.networks_by_name[network_name] - def __l3_config(self, network_name, family): - """Get the l3_info block for the specified address family from + def __address_family(self, network_name, family): + """Get the address_family block for the specified address family from the network of the specified name. If the network doesn't - exist raise an exception. If there is no matching l3_info, + exist raise an exception. If there is no matching address_family, return None. """ network = self.__network_by_name(network_name) candidates = [ - l3_info - for _, l3_info in network.get('l3_configs', {}).items() - if l3_info.get('family', None) == family + address_family + for _, address_family in network.get( + 'address_families', {} + ).items() + if address_family.get('family', None) == family ] return candidates[0] if candidates else None @@ -212,10 +214,10 @@ def application_metadata(self, network_name): return network.get('application_metadata', {}) def ipv4_cidr(self, network_name): - l3_config = self.__l3_config(network_name, 'AF_INET') - if l3_config is None: + address_family = self.__address_family(network_name, 'AF_INET') + if address_family is None: return None - return l3_config.get('cidr', None) + return address_family.get('cidr', None) def non_cluster_network(self, network_name): network = self.__network_by_name(network_name) diff --git a/vtds_cluster_kvm/private/cluster.py b/vtds_cluster_kvm/private/cluster.py index 4caa273..7c0b57b 100644 --- a/vtds_cluster_kvm/private/cluster.py +++ b/vtds_cluster_kvm/private/cluster.py @@ -190,21 +190,21 @@ def __net_name(network): ) return netname - def __get_l3_config(self, network, family): + def __get_address_family(self, network, family): """Look up the L3 configuration for the specified address family in the specified network. """ - l3_configs = network.get('l3_configs', None) - if l3_configs is None: + address_families = network.get('address_families', None) + if address_families is None: raise ContextualError( "configuration error: network '%s' has no " - "'l3_configs' section" % self.__net_name(network) + "'address_families' section" % self.__net_name(network) ) candidates = [ - l3_config - for _, l3_config in l3_configs.items() - if l3_config.get('family', None) == family + address_family + for _, address_family in address_families.items() + if address_family.get('family', None) == family ] if not candidates: raise ContextualError( @@ -223,8 +223,8 @@ def __get_ipv4_cidr(self, network): there is none. """ - l3_config = self.__get_l3_config(network, 'AF_INET') - cidr = l3_config.get('cidr', None) + address_family = self.__get_address_family(network, 'AF_INET') + cidr = address_family.get('cidr', None) if cidr is None: raise ContextualError( "configuration error: AF_INET L3 configuration for " @@ -272,8 +272,10 @@ def __add_host_blade_net(self): ) # Connect the host_blade_network to all blades of all classes. blade_classes = virtual_blades.blade_classes() - l3_config = self.__get_l3_config(host_blade_network, 'AF_INET') - l3_config['connected_blades'] = [ + address_family = self.__get_address_family( + host_blade_network, 'AF_INET' + ) + address_family['connected_blades'] = [ { 'blade_class': blade_class, 'blade_instances': [ @@ -343,7 +345,8 @@ def __random_mac(prefix="52:54:00"): """Generate a MAC address using a specified prefix specified as a string containing colon separated hexadecimal octet values for the length of the desired prefix. By default use - the KVM reserved prefix '52:54:00'. + the KVM reserved, locally administered, unicast prefix + '52:54:00'. """ try: diff --git a/vtds_cluster_kvm/private/config/config.yaml b/vtds_cluster_kvm/private/config/config.yaml index 493c685..6aa8f73 100644 --- a/vtds_cluster_kvm/private/config/config.yaml +++ b/vtds_cluster_kvm/private/config/config.yaml @@ -46,7 +46,7 @@ cluster: # Virtual Network. Since this is a blade_local network, there is # no underlying interconnect. Set this to null. blade_interconnect: null - l3_configs: + address_families: ipv4: family: AF_INET # By default we take a /16 network from a (hopefully) low @@ -117,7 +117,22 @@ cluster: # The name of the Blade Interconnect network underlying this # Virtual Network. Set this to null for blade-local networks. blade_interconnect: base-interconnect - l3_configs: + # Connected blades lists the blades that are connected to + # the Virtual Network (by default no blade is connected). If + # you are going to have a blade provided DHCP server on one + # of the blades for this network, that blade (class and + # instance) must be a connected blade. For a blade-local + # network to exist on a blade, that blade (class and + # instance) must be in the set of connected blades. + connected_blades: + # Connected blades are grouped by blade class and + # identified by instance numbers. + - blade_class: base-blade + blade_instances: + - 0 + # Configuration for things that are specific to a given address + # family being used on the network. + address_families: ipv4: family: AF_INET cidr: 10.254.0.0/16 @@ -126,19 +141,10 @@ cluster: - 10.1.1.1 - 10.1.1.2 - 10.1.1.3 - # Connected blades lists the blades that are connected to - # the Virtual Network (by default no blade is connected). If - # you are going to have a blade provided DHCP server on one - # of the blades for this network, that blade (class and - # instance) must be a connected blade. For a blade-local - # network to exist on a blade, that blade (class and - # instance) must be in the set of connected blades. + # Connected blades are assigned IP addresses by blade class + # which are indexed by instance number. connected_blades: - # Connected blades are grouped by blade class and - # identified by instance numbers. - blade_class: base-blade - blade_instances: - - 0 # Each connected blade instance is assigned an IP # address on the Virtual Network using the following # list of blade IPs matched one-to-one with blade diff --git a/vtds_cluster_kvm/private/scripts/deploy_cluster_to_blade.py b/vtds_cluster_kvm/private/scripts/deploy_cluster_to_blade.py index 7c0ecbd..5fb5411 100644 --- a/vtds_cluster_kvm/private/scripts/deploy_cluster_to_blade.py +++ b/vtds_cluster_kvm/private/scripts/deploy_cluster_to_blade.py @@ -247,29 +247,42 @@ def connected_blade_instances(network, blade_class): network and blade class. """ - l3_config = find_l3_config(network, 'AF_INET') return [ int(blade_instance) - for blade in l3_config.get('connected_blades', []) + for blade in network.get('connected_blades', []) if blade.get('blade_class', None) == blade_class for blade_instance in blade.get('blade_instances', []) ] def connected_blade_ipv4s(network, blade_class): - """Get the list of conencted blade instance numbers for a given + """Get the list of conencted blade IP addresses for a given network and blade class. """ - l3_config = find_l3_config(network, 'AF_INET') + address_family = find_address_family(network, 'AF_INET') return [ ipv4_addr - for blade in l3_config.get('connected_blades', []) + for blade in address_family.get('connected_blades', []) if blade.get('blade_class', None) == blade_class for ipv4_addr in blade.get('blade_ips', []) ] +def connected_blade_macs(network, blade_class): + """Get the list of conencted blade IP addresses for a given + network and blade class. + + """ + address_family = find_address_family(network, 'AF_LINK') + return [ + mac + for blade in address_family.get('connected_blades', []) + if blade.get('blade_class', None) == blade_class + for mac in blade.get('blade_macs', []) + ] + + def network_blade_connected(network, blade_class, blade_instance): """Determine whether the specified network is connected to the specified instance of the specified blade class. If it is, return @@ -287,7 +300,7 @@ def network_blade_ipv4(network, blade_class, blade_instance): """ instances = connected_blade_instances(network, blade_class) ipv4s = connected_blade_ipv4s(network, blade_class) - count = len(instances) if len(instances) >= len(ipv4s) else len(ipv4s) + count = len(instances) if len(instances) <= len(ipv4s) else len(ipv4s) candidates = [ ipv4s[i] for i in range(0, count) if blade_instance == instances[i] @@ -300,7 +313,7 @@ def network_ipv4_gateway(network): specified network. If no gateway is configured, return None. """ - return find_l3_config(network, 'AF_INET').get('gateway', None) + return find_address_family(network, 'AF_INET').get('gateway', None) def is_nat_router(network, blade_class, blade_instance): @@ -322,10 +335,10 @@ def is_dhcp_server(network, blade_class, blade_instance): hosts the gateway for that network). """ - l3_config = find_l3_config(network, 'AF_INET') + address_family = find_address_family(network, 'AF_INET') candidates = [ blade - for blade in l3_config.get('connected_blades', []) + for blade in address_family.get('connected_blades', []) if blade.get('blade_class', None) == blade_class and blade.get('dhcp_server_instance', None) == blade_instance ] @@ -424,48 +437,50 @@ def find_addr_info(interface, family): return addr_infos[0] -def find_l3_config(network, family): +def find_address_family(network, family): """Find the L3 configuration for the specified address family ('family') in the provided network configuration ('network'). """ netname = net_name(network) - # There should be exactly one 'l3_config' block in the network + # There should be exactly one 'address_family' block in the network # with the specified family. - l3_configs = [ - l3_config - for _, l3_config in network.get('l3_configs', {}).items() - if l3_config.get('family', None) == family + address_families = [ + address_family + for _, address_family in network.get('address_families', {}).items() + if address_family.get('family', None) == family ] - if len(l3_configs) > 1: + if len(address_families) > 1: raise ContextualError( "configuration error: the Virtual Network named '%s' has more " - "than one %s 'l3_config' block." % (netname, family) + "than one %s 'address_family' block." % (netname, family) ) - if not l3_configs: + if not address_families: raise ContextualError( "configuration error: the Virtual Network named '%s' has " - "no %s 'l3_config' block." % (netname, family) + "no %s 'address_family' block." % (netname, family) ) - return l3_configs[0] + return address_families[0] -def network_length(l3_config, netname): - """Given an l3_config ('l3_config') from a network named 'netname' - return the network length from its 'cidr' element. +def network_length(address_family, netname): + """Given an address_family ('address_family') from a network named + 'netname' return the network length from its 'cidr' element. """ - if 'cidr' not in l3_config: + if 'cidr' not in address_family: raise ContextualError( - "configuration error: the AF_INET 'l3_config' block for the " + "configuration error: the AF_INET 'address_family' block for the " "network named '%s' has no 'cidr' configured" % netname ) - if '/' not in l3_config['cidr']: + if '/' not in address_family['cidr']: raise ContextualError( "configuration error: the AF_INET 'cidr' value '%s' for the " - "network named '%s' is malformed" % (l3_config['cidr'], netname) + "network named '%s' is malformed" % ( + address_family['cidr'], netname + ) ) - return l3_config['cidr'].split('/')[1] + return address_family['cidr'].split('/')[1] def find_blade_cidr(network, blade_class, blade_instance): @@ -476,10 +491,10 @@ def find_blade_cidr(network, blade_class, blade_instance): then return None. """ - l3_config = find_l3_config(network, "AF_INET") + address_family = find_address_family(network, "AF_INET") blade_ip = network_blade_ipv4(network, blade_class, blade_instance) return ( - '/'.join((blade_ip, network_length(l3_config, net_name(network)))) + '/'.join((blade_ip, network_length(address_family, net_name(network)))) if blade_ip is not None else None ) @@ -670,7 +685,7 @@ def install_nat_rule(network): """ dest_if = find_interconnect_interface() - cidr = find_l3_config(network, 'AF_INET')['cidr'] + cidr = find_address_family(network, 'AF_INET')['cidr'] run_cmd( 'iptables', [ @@ -1334,8 +1349,8 @@ def __make_network_interface(self, interface, network): ipv4_info = find_addr_info(interface, "AF_INET") mac_addrs = node_mac_addrs(interface) addresses = ipv4_info.get('addresses', []) - l3_config = find_l3_config(network, "AF_INET") - net_length = network_length(l3_config, netname) + address_family = find_address_family(network, "AF_INET") + net_length = network_length(address_family, netname) try: mode = ipv4_info['mode'] except KeyError as err: @@ -1617,21 +1632,21 @@ def __compose_reservations(self, interfaces): ] return reservations - def __compose_subnet(self, blade_if, l3_config, interfaces): + def __compose_subnet(self, blade_if, address_family, interfaces): """Based on a network's l3 configuration block, compose the DCP4 subnet configuration for Kea. """ pools = [ {'pool': "%s - %s" % (pool['start'], pool['end'])} - for pool in l3_config['dhcp'].get('pools', []) + for pool in address_family['dhcp'].get('pools', []) ] try: - cidr = l3_config['cidr'] + cidr = address_family['cidr'] except KeyError as err: raise ContextualError( - "configuration error: network l3_config %s " - "has no 'cidr' element" % str(l3_config) + "configuration error: network address_family %s " + "has no 'cidr' element" % str(address_family) ) from err subnet = { 'pools': pools, @@ -1640,7 +1655,7 @@ def __compose_subnet(self, blade_if, l3_config, interfaces): 'reservations': self.__compose_reservations(interfaces), 'option-data': [] } - gateway = l3_config.get('gateway', None) + gateway = address_family.get('gateway', None) if gateway: subnet['option-data'].append( { @@ -1648,7 +1663,7 @@ def __compose_subnet(self, blade_if, l3_config, interfaces): 'data': gateway, }, ) - nameservers = l3_config.get('name_servers', []) + nameservers = address_family.get('name_servers', []) if nameservers: subnet['option-data'].append( { @@ -1661,8 +1676,8 @@ def __compose_subnet(self, blade_if, l3_config, interfaces): def __compose_network(self, network): """Compose the base Kea DHCP4 configuration for the provided network and return it. A network may be a set of subnets, - based on 'l3_config' blocks, so treat each AF_INET 'l3_config' - block as its own subnet. + based on 'address_family' blocks, so treat each AF_INET + 'address_family' block as its own subnet. """ # Further filter network interfaces to get only those that @@ -1673,10 +1688,10 @@ def __compose_network(self, network): if if_network(interface) == net_name(network) ] blade_if = blade_ipv4_ifname(network) - l3_config = find_l3_config(network, 'AF_INET') + address_family = find_address_family(network, 'AF_INET') subnet = ( - self.__compose_subnet(blade_if, l3_config, interfaces) - if l3_config.get('dhcp', {}) else None + self.__compose_subnet(blade_if, address_family, interfaces) + if address_family.get('dhcp', {}) else None ) return [subnet] if subnet is not None else []