def read_nodes(nodes_filename): """ Convert a list of node names into a list of IP addresses. :param nodes_filename: Name of the YAML file containing the IP addresses :type nodes_filename: str :return: Tuple (List of IP addresses, Locator bits, uSID ID bits) :rtype: tuple :raises NodeNotFoundError: Node name not found in the mapping file :raises InvalidConfigurationError: The mapping file is not a valid YAML file """ # Read the mapping from the file with open(nodes_filename, 'r') as nodes_file: nodes = yaml.safe_load(nodes_file) # Validate the IP addresses for addr in [node['grpc_ip'] for node in nodes['nodes'].values()]: if not utils.validate_ipv6_address(addr): logger.error('Invalid IPv6 address %s in %s', addr, nodes_filename) raise InvalidConfigurationError # Validate the SIDs for sid in [node['uN'] for node in nodes['nodes'].values()]: if not utils.validate_ipv6_address(sid): logger.error('Invalid SID %s in %s', sid, nodes_filename) raise InvalidConfigurationError # Validate the forwarding engine for fwd_engine in [node['fwd_engine'] for node in nodes['nodes'].values()]: if fwd_engine not in ['linux', 'vpp', 'p4']: logger.error('Invalid forwarding engine %s in %s', fwd_engine, nodes_filename) raise InvalidConfigurationError # Get the #bits of the locator locator_bits = nodes.get('locator_bits') # Validate #bits for the SID Locator if locator_bits is not None and \ (int(locator_bits) < 0 or int(locator_bits) > 128): raise InvalidConfigurationError # Get the #bits of the uSID identifier usid_id_bits = nodes.get('usid_id_bits') # Validate #bits for the uSID ID if usid_id_bits is not None and \ (int(usid_id_bits) < 0 or int(usid_id_bits) > 128): raise InvalidConfigurationError if locator_bits is not None and usid_id_bits is not None and \ int(usid_id_bits) + int(locator_bits) > 128: raise InvalidConfigurationError # Enforce case-sensitivity for node in nodes['nodes'].values(): nodes['nodes'][node['name']]['grpc_ip'] = node['grpc_ip'].lower() nodes['nodes'][node['name']]['uN'] = node['uN'].lower() # Return the nodes list return nodes['nodes'], locator_bits, usid_id_bits
def print_node_to_addr_mapping(nodes_filename): """ This function reads a YAML file containing the mapping of node names to IP addresses and pretty print it :param node_to_addr_filename: Name of the YAML file containing the mapping of node names to IP addresses :type node_to_addr_filename: str """ # Read the mapping from the file with open(nodes_filename, 'r') as nodes_file: nodes = yaml.safe_load(nodes_file) # Validate the IP addresses for addr in [node['grpc_ip'] for node in nodes['nodes'].values()]: if not utils.validate_ipv6_address(addr): logger.error('Invalid IPv6 address %s in %s', addr, nodes_filename) raise InvalidConfigurationError # Validate the SIDs for sid in [node['uN'] for node in nodes['nodes'].values()]: if not utils.validate_ipv6_address(sid): logger.error('Invalid SID %s in %s', sid, nodes_filename) raise InvalidConfigurationError # Validate the forwarding engine for fwd_engine in [node['fwd_engine'] for node in nodes['nodes'].values()]: if fwd_engine not in ['linux', 'vpp', 'p4']: logger.error('Invalid forwarding engine %s in %s', fwd_engine, nodes_filename) raise InvalidConfigurationError # Get the #bits of the locator locator_bits = nodes.get('locator_bits') # Validate #bits for the SID Locator if locator_bits is not None and \ (int(locator_bits) < 0 or int(locator_bits) > 128): raise InvalidConfigurationError # Get the #bits of the uSID identifier usid_id_bits = nodes.get('usid_id_bits') # Validate #bits for the uSID ID if usid_id_bits is not None and \ (int(usid_id_bits) < 0 or int(usid_id_bits) > 128): raise InvalidConfigurationError if locator_bits is not None and usid_id_bits is not None and \ int(usid_id_bits) + int(locator_bits) > 128: raise InvalidConfigurationError print('\nList of available devices:') pprint.PrettyPrinter(indent=4).pprint(list(nodes['nodes'].keys())) print()
def encode_intermediate_node(node, locator): """ Get a dict-representation of a node (intermediate node of the path), starting from gRPC IP and port, uDT sid, forwarding engine and locator. For the intermediate nodes, we don't need uDT, forwarding engine. gRPC IP and gRPC address. :param node: Node identifier. This could be a name, a SID (IPv6 address) or a number (uSID identifier). :type node: str :param locator: Locator part of the SIDs (e.g. fcbb:bbbb::). :type locator: str :return: Dict representation of the node. The dict has the following fields: - name - grpc_ip (set to None) - grpc_port (set to None) - uN - uDT (set to None) - fwd_engine (set to None) :rtype: dict :raises InvalidConfigurationError: If the node params are invalid. """ # Validate params # # Validate locator if locator is None: logger.error('locator is mandatory for node %s', node) raise InvalidConfigurationError # # Compute uN SID starting from the provided node identifier # Node identifier can be expressed as SID (an IPv6 address) or a # uSID identifier. If it is a uSID identifier, we need to convert it # to a SID. un = node # Node identifier is a integer, we need to convert it to a SID (IPv6 # address) if validate_usid_id(node): un = usid_id_to_usid(node, locator) # If the node is expressed as IPv6 address or uSID identifier, encode it # Otherwise (if the node is expressed as node name), we return None and we # expect to find the node info in the nodes configuration. if utils.validate_ipv6_address(node) or validate_usid_id(node): return { 'name': node, 'grpc_ip': None, # Useless for intermediate nodes 'grpc_port': None, # Useless for intermediate nodes 'uN': un, 'uDT': None, # Useless for intermediate nodes 'fwd_engine': None # Useless for intermediate nodes } # 'Node' is a name. Return None. return None
def fill_nodes_info(nodes_info, nodes, l_grpc_ip=None, l_grpc_port=None, l_fwd_engine=None, r_grpc_ip=None, r_grpc_port=None, r_fwd_engine=None, decap_sid=None, locator=None): """ Fill 'nodes_info' dict with the nodes containined in the 'nodes' list. :param nodes_info: Dict containined the nodes information where to add the nodes. :type nodes_info: dict :param nodes: List of nodes. Each node can be expressed as SID (IPv6 address), a uSID identifier (integer) or a name. :type nodes: list :param l_grpc_ip: gRPC address of the left node in the path. :type l_grpc_ip: str, optional :param l_grpc_port: Port number of the gRPC server on the left node of the path. :type l_grpc_port: str, optional :param l_fwd_engine: Forwarding engine to be used on the left node of the path (e.g. Linux or VPP). :type l_fwd_engine: str, optional :param r_grpc_ip: gRPC address of the right node in the path. :type r_grpc_ip: str, optional :param r_grpc_port: Port number of the gRPC server on the right node of the path. :type r_grpc_port: str, optional :param r_fwd_engine: Forwarding engine to be used on the right node of the path (e.g. Linux or VPP). :type r_fwd_engine: str, optional :param decap_sid: Decap SID. This could be a SID (IPv6 address) or a uSID identifier (an integer). :type decap_sid: str, optional :param locator: Locator part of the SIDs (e.g. fcbb:bbbb::). :type locator: str, optional :raises InvalidConfigurationError: If the node params are invalid. """ # Convert decap SID to uDT udt = None if decap_sid is not None: # Locator is required if locator is None: logger.error('locator is mandatory') raise InvalidConfigurationError # Check if decap SID is expressed as a SID (IPv6 address) # or a uSID identifier (an integer) if not utils.validate_ipv6_address(decap_sid): # Integer, we need to convert it to a SID (IPv6 address) udt = usid_id_to_usid(decap_sid, locator) else: # IPv6 address udt = decap_sid # Encode left node # # A node could be expressed as an integer, an IPv6 address (SID) # or a name node = encode_endpoint_node( node=nodes[0], grpc_ip=l_grpc_ip, grpc_port=l_grpc_port, udt=udt, fwd_engine=l_fwd_engine, locator=locator ) # If we received a node info dict, we add it to the # nodes info dictionary if node is not None: nodes_info[nodes[0]] = node # Encode right node # # A node could be expressed as an integer, an IPv6 address (SID) # or a name node = encode_endpoint_node( node=nodes[-1], grpc_ip=r_grpc_ip, grpc_port=r_grpc_port, udt=udt, fwd_engine=r_fwd_engine, locator=locator ) # If we received a node info dict, we add it to the # nodes info dictionary if node is not None: nodes_info[nodes[-1]] = node # Encode intermediate nodes # For the intermediate nodes, we don't need forwarding engine, # uDT, gRPC IP and port for node_name in nodes[1:-1]: # Encode the node node = encode_intermediate_node( node=node_name, locator=locator ) # If we received a node info dict, we add it to the # nodes info dictionary if node is not None: nodes_info[node_name] = node
def encode_endpoint_node(node, grpc_ip, grpc_port, fwd_engine, locator, udt=None): """ Get a dict-representation of a node (endpoint of the path), starting from gRPC IP and port, uDT sid, forwarding engine and locator. :param node: Node identifier. This could be a name, a SID (IPv6 address) or a number (uSID identifier). :type node: str :param grpc_ip: gRPC IP address of the node. :type grpc_ip: str :param grpc_port: Port number of the gRPC server. :type grpc_port: int :param udt: uDT SID of the node, used for the decap operation. If not provided, the uDT SID is not added to the SID list. :type udt: str, optional :param fwd_engine: Forwarding engine to be used (e.g. Linux or VPP). :type fwd_engine: str :param locator: Locator part of the SIDs (e.g. fcbb:bbbb::). :type locator: str :return: Dict representation of the node. The dict has the following fields: - name - grpc_ip - grpc_port - uN - uDT - fwd_engine :rtype: dict :raises InvalidConfigurationError: If the node params are invalid. """ # Validation checks # # Validate gRPC address if grpc_ip is None: logger.error('grpc_ip is mandatory for node %s', node) raise InvalidConfigurationError # Validate gRPC port if grpc_port is None: logger.error('grpc_port is mandatory for node %s', node) raise InvalidConfigurationError # Validate forwarding engine if fwd_engine is None: logger.error('grpcfwd_engine_ip is mandatory for node %s', node) raise InvalidConfigurationError # Validate locator if locator is None: logger.error('locator is mandatory for node %s', node) raise InvalidConfigurationError # # Compute uN SID starting from the provided node identifier # Node identifier can be expressed as SID (an IPv6 address) or a # uSID identifier. If it is a uSID identifier, we need to convert it # to a SID. un = node if validate_usid_id(node): # Node identifier is a integer, we need to convert it to a SID (IPv6 # address) un = usid_id_to_usid(node, locator) # If the node is expressed as IPv6 address or uSID identifier, encode it # Otherwise (if the node is expressed as node name), we return None and we # expect to find the node info in the nodes configuration. if utils.validate_ipv6_address(node) or validate_usid_id(node): # Return the dict return { 'name': node, 'grpc_ip': grpc_ip, 'grpc_port': grpc_port, 'uN': un, 'uDT': udt, 'fwd_engine': fwd_engine } # 'Node' is a name. Return None. return None