""" Copyright (c) 2015-2019 Wind River Systems, Inc. SPDX-License-Identifier: Apache-2.0 """ from netaddr import IPRange from controllerconfig.common.exceptions import ConfigFail from controllerconfig.common.exceptions import ValidateFail from controllerconfig.utils import is_mtu_valid from controllerconfig.utils import is_valid_vlan from controllerconfig.utils import validate_network_str from controllerconfig.utils import validate_address_str DEFAULT_CONFIG = 0 REGION_CONFIG = 1 SUBCLOUD_CONFIG = 2 MGMT_TYPE = 0 INFRA_TYPE = 1 OAM_TYPE = 2 CLUSTER_TYPE = 3 NETWORK_PREFIX_NAMES = [ ('MGMT', 'INFRA', 'OAM', 'CLUSTER'), ('CLM', 'BLS', 'CAN', 'CLUSTER') ] HOST_XML_ATTRIBUTES = ['hostname', 'personality', 'subfunctions', 'mgmt_mac', 'mgmt_ip', 'bm_ip', 'bm_type', 'bm_username', 'bm_password', 'boot_device', 'rootfs_device', 'install_output', 'console', 'vsc_controllers', 'power_on', 'location'] # Network naming types DEFAULT_NAMES = 0 HP_NAMES = 1 # well-known default domain name DEFAULT_DOMAIN_NAME = 'Default' class LogicalInterface(object): """ Represents configuration for a logical interface. """ def __init__(self): self.name = None self.mtu = None self.lag_interface = False self.lag_mode = None self.ports = None def parse_config(self, system_config, logical_interface): # Ensure logical interface config is present if not system_config.has_section(logical_interface): raise ConfigFail("Missing config for logical interface %s." % logical_interface) self.name = logical_interface # Parse/validate the MTU self.mtu = system_config.getint(logical_interface, 'INTERFACE_MTU') if not is_mtu_valid(self.mtu): raise ConfigFail("Invalid MTU value for %s. " "Valid values: 576 - 9216" % logical_interface) # Parse the ports self.ports = [_f for _f in [x.strip() for x in system_config.get(logical_interface, 'INTERFACE_PORTS').split(',')] if _f] # Parse/validate the LAG config lag_interface = system_config.get(logical_interface, 'LAG_INTERFACE') if lag_interface.lower() == 'y': self.lag_interface = True if len(self.ports) != 2: raise ConfigFail( "Invalid number of ports (%d) supplied for LAG " "interface %s" % (len(self.ports), logical_interface)) self.lag_mode = system_config.getint(logical_interface, 'LAG_MODE') if self.lag_mode < 1 or self.lag_mode > 6: raise ConfigFail( "Invalid LAG_MODE value of %d for %s. Valid values: 1-6" % (self.lag_mode, logical_interface)) elif lag_interface.lower() == 'n': if len(self.ports) > 1: raise ConfigFail( "More than one interface supplied for non-LAG " "interface %s" % logical_interface) if len(self.ports) == 0: raise ConfigFail( "No interfaces supplied for non-LAG " "interface %s" % logical_interface) else: raise ConfigFail( "Invalid LAG_INTERFACE value of %s for %s. Valid values: " "Y or N" % (lag_interface, logical_interface)) class Network(object): """ Represents configuration for a network. """ def __init__(self): self.vlan = None self.cidr = None self.multicast_cidr = None self.start_address = None self.end_address = None self.start_end_in_config = False self.floating_address = None self.address_0 = None self.address_1 = None self.dynamic_allocation = False self.gateway_address = None self.logical_interface = None def parse_config(self, system_config, config_type, network_type, min_addresses=0, multicast_addresses=0, optional=False, naming_type=DEFAULT_NAMES, logical_interface_required=True): network_prefix = NETWORK_PREFIX_NAMES[naming_type][network_type] network_name = network_prefix + '_NETWORK' if naming_type == HP_NAMES: attr_prefix = network_prefix + '_' else: attr_prefix = '' # Ensure network config is present if not system_config.has_section(network_name): if not optional: raise ConfigFail("Missing config for network %s." % network_name) else: # Optional interface - just return return # Parse/validate the VLAN if system_config.has_option(network_name, attr_prefix + 'VLAN'): self.vlan = system_config.getint(network_name, attr_prefix + 'VLAN') if self.vlan: if not is_valid_vlan(self.vlan): raise ConfigFail( "Invalid %s value of %d for %s. Valid values: 1-4094" % (attr_prefix + 'VLAN', self.vlan, network_name)) # Parse/validate the cidr cidr_str = system_config.get(network_name, attr_prefix + 'CIDR') try: self.cidr = validate_network_str( cidr_str, min_addresses) except ValidateFail as e: raise ConfigFail( "Invalid %s value of %s for %s.\nReason: %s" % (attr_prefix + 'CIDR', cidr_str, network_name, e)) # Parse/validate the multicast subnet if 0 < multicast_addresses and \ system_config.has_option(network_name, attr_prefix + 'MULTICAST_CIDR'): multicast_cidr_str = system_config.get(network_name, attr_prefix + 'MULTICAST_CIDR') try: self.multicast_cidr = validate_network_str( multicast_cidr_str, multicast_addresses, multicast=True) except ValidateFail as e: raise ConfigFail( "Invalid %s value of %s for %s.\nReason: %s" % (attr_prefix + 'MULTICAST_CIDR', multicast_cidr_str, network_name, e)) if self.cidr.version != self.multicast_cidr.version: raise ConfigFail( "Invalid %s value of %s for %s. Multicast " "subnet and network IP families must be the same." % (attr_prefix + 'MULTICAST_CIDR', multicast_cidr_str, network_name)) # Parse/validate the hardwired controller addresses floating_address_str = None address_0_str = None address_1_str = None if min_addresses == 1: if (system_config.has_option( network_name, attr_prefix + 'IP_FLOATING_ADDRESS') or system_config.has_option( network_name, attr_prefix + 'IP_UNIT_0_ADDRESS') or system_config.has_option( network_name, attr_prefix + 'IP_UNIT_1_ADDRESS') or system_config.has_option( network_name, attr_prefix + 'IP_START_ADDRESS') or system_config.has_option( network_name, attr_prefix + 'IP_END_ADDRESS')): raise ConfigFail( "Only one IP address is required for OAM " "network, use 'IP_ADDRESS' to specify the OAM IP " "address") floating_address_str = system_config.get( network_name, attr_prefix + 'IP_ADDRESS') try: self.floating_address = validate_address_str( floating_address_str, self.cidr) except ValidateFail as e: raise ConfigFail( "Invalid %s value of %s for %s.\nReason: %s" % (attr_prefix + 'IP_ADDRESS', floating_address_str, network_name, e)) self.address_0 = self.floating_address self.address_1 = self.floating_address else: if system_config.has_option( network_name, attr_prefix + 'IP_FLOATING_ADDRESS'): floating_address_str = system_config.get( network_name, attr_prefix + 'IP_FLOATING_ADDRESS') try: self.floating_address = validate_address_str( floating_address_str, self.cidr) except ValidateFail as e: raise ConfigFail( "Invalid %s value of %s for %s.\nReason: %s" % (attr_prefix + 'IP_FLOATING_ADDRESS', floating_address_str, network_name, e)) if system_config.has_option( network_name, attr_prefix + 'IP_UNIT_0_ADDRESS'): address_0_str = system_config.get( network_name, attr_prefix + 'IP_UNIT_0_ADDRESS') try: self.address_0 = validate_address_str( address_0_str, self.cidr) except ValidateFail as e: raise ConfigFail( "Invalid %s value of %s for %s.\nReason: %s" % (attr_prefix + 'IP_UNIT_0_ADDRESS', address_0_str, network_name, e)) if system_config.has_option( network_name, attr_prefix + 'IP_UNIT_1_ADDRESS'): address_1_str = system_config.get( network_name, attr_prefix + 'IP_UNIT_1_ADDRESS') try: self.address_1 = validate_address_str( address_1_str, self.cidr) except ValidateFail as e: raise ConfigFail( "Invalid %s value of %s for %s.\nReason: %s" % (attr_prefix + 'IP_UNIT_1_ADDRESS', address_1_str, network_name, e)) # Parse/validate the start/end addresses start_address_str = None end_address_str = None if system_config.has_option( network_name, attr_prefix + 'IP_START_ADDRESS'): start_address_str = system_config.get( network_name, attr_prefix + 'IP_START_ADDRESS') try: self.start_address = validate_address_str( start_address_str, self.cidr) except ValidateFail as e: raise ConfigFail( "Invalid %s value of %s for %s.\nReason: %s" % (attr_prefix + 'IP_START_ADDRESS', start_address_str, network_name, e)) if system_config.has_option( network_name, attr_prefix + 'IP_END_ADDRESS'): end_address_str = system_config.get( network_name, attr_prefix + 'IP_END_ADDRESS') try: self.end_address = validate_address_str( end_address_str, self.cidr) except ValidateFail as e: raise ConfigFail( "Invalid %s value of %s for %s.\nReason: %s " % (attr_prefix + 'IP_END_ADDRESS', end_address_str, network_name, e)) if start_address_str or end_address_str: if not end_address_str: raise ConfigFail("Missing attribute %s for %s_NETWORK" % (attr_prefix + 'IP_END_ADDRESS', network_name)) if not start_address_str: raise ConfigFail("Missing attribute %s for %s_NETWORK" % (attr_prefix + 'IP_START_ADDRESS', network_name)) if not self.start_address < self.end_address: raise ConfigFail( "Start address %s not less than end address %s for %s." % (str(self.start_address), str(self.end_address), network_name)) if not IPRange(start_address_str, end_address_str).size >= \ min_addresses: raise ConfigFail("Address range for %s must contain at " "least %d addresses." % (network_name, min_addresses)) self.start_end_in_config = True if floating_address_str or address_0_str or address_1_str: if not floating_address_str: raise ConfigFail("Missing attribute %s for %s_NETWORK" % (attr_prefix + 'IP_FLOATING_ADDRESS', network_name)) if not address_0_str: raise ConfigFail("Missing attribute %s for %s_NETWORK" % (attr_prefix + 'IP_UNIT_0_ADDRESS', network_name)) if not address_1_str: raise ConfigFail("Missing attribute %s for %s_NETWORK" % (attr_prefix + 'IP_UNIT_1_ADDRESS', network_name)) if start_address_str and floating_address_str: raise ConfigFail("Overspecified network: Can only set %s " "and %s OR %s, %s, and %s for " "%s_NETWORK" % (attr_prefix + 'IP_START_ADDRESS', attr_prefix + 'IP_END_ADDRESS', attr_prefix + 'IP_FLOATING_ADDRESS', attr_prefix + 'IP_UNIT_0_ADDRESS', attr_prefix + 'IP_UNIT_1_ADDRESS', network_name)) if config_type == DEFAULT_CONFIG: if not self.start_address: self.start_address = self.cidr[2] if not self.end_address: self.end_address = self.cidr[-2] # Parse/validate the dynamic IP address allocation if system_config.has_option(network_name, 'DYNAMIC_ALLOCATION'): dynamic_allocation = system_config.get(network_name, 'DYNAMIC_ALLOCATION') if dynamic_allocation.lower() == 'y': self.dynamic_allocation = True elif dynamic_allocation.lower() == 'n': self.dynamic_allocation = False else: raise ConfigFail( "Invalid DYNAMIC_ALLOCATION value of %s for %s. " "Valid values: Y or N" % (dynamic_allocation, network_name)) # Parse/validate the gateway (optional) if system_config.has_option(network_name, attr_prefix + 'GATEWAY'): gateway_address_str = system_config.get( network_name, attr_prefix + 'GATEWAY') try: self.gateway_address = validate_address_str( gateway_address_str, self.cidr) except ValidateFail as e: raise ConfigFail( "Invalid %s value of %s for %s.\nReason: %s" % (attr_prefix + 'GATEWAY', gateway_address_str, network_name, e)) # Parse/validate the logical interface if logical_interface_required or system_config.has_option( network_name, attr_prefix + 'LOGICAL_INTERFACE'): logical_interface_name = system_config.get( network_name, attr_prefix + 'LOGICAL_INTERFACE') self.logical_interface = LogicalInterface() self.logical_interface.parse_config(system_config, logical_interface_name)