def test_circular_dependency_results_in_an_exception(self): self.assertRaises(CircularDependency, list, sorttop({1: {2}, 2: {1}}))
def update_node_interfaces(node, interfaces, topology_hints=None, create_fabrics=True): """Update the interfaces attached to the node :param interfaces: a dict with interface details. :param topology_hints: List of dictionaries representing hints about fabric/VLAN connectivity. :param create_fabrics: If True, creates fabrics associated with each VLAN. Otherwise, creates the interfaces but does not create any links or VLANs. """ # Avoid circular imports from metadataserver.builtin_scripts.hooks import ( parse_interfaces_details, update_interface_details, ) # Get all of the current interfaces on this node. current_interfaces = { interface.id: interface for interface in node.interface_set.all().order_by("id") } # Update the interfaces in dependency order. This make sure that the # parent is created or updated before the child. The order inside # of the sorttop result is ordered so that the modification locks that # postgres grabs when updating interfaces is always in the same order. # The ensures that multiple threads can call this method at the # exact same time. Without this ordering it will deadlock because # multiple are trying to update the same items in the database in # a different order. process_order = sorttop( {name: config["parents"] for name, config in interfaces.items()}) process_order = [sorted(list(items)) for items in process_order] # Cache the neighbour discovery settings, since they will be used for # every interface on this Controller. discovery_mode = Config.objects.get_network_discovery_config() interfaces_details = parse_interfaces_details(node) for name in flatten(process_order): settings = interfaces[name] # Note: the interface that comes back from this call may be None, # if we decided not to model an interface based on what the rack # sent. interface = update_interface( node, name, settings, create_fabrics=create_fabrics, hints=topology_hints, ) if interface is not None: interface.update_discovery_state(discovery_mode, settings) if interface.type == INTERFACE_TYPE.PHYSICAL: update_interface_details(interface, interfaces_details) if interface.id in current_interfaces: del current_interfaces[interface.id] if not create_fabrics: # This could be an existing rack controller re-registering, # so don't delete interfaces during this phase. return # Remove all the interfaces that no longer exist. We do this in reverse # order so the child is deleted before the parent. deletion_order = {} for nic_id, nic in current_interfaces.items(): deletion_order[nic_id] = [ parent.id for parent in nic.parents.all() if parent.id in current_interfaces ] deletion_order = sorttop(deletion_order) deletion_order = [sorted(list(items)) for items in deletion_order] deletion_order = reversed(list(flatten(deletion_order))) for delete_id in deletion_order: if node.boot_interface_id == delete_id: node.boot_interface = None current_interfaces[delete_id].delete() node.save()
def assertSort(self, data, *batches): self.assertThat(tuple(sorttop(data)), Equals(batches))
def update_node_interfaces(node, data): """Update the interfaces attached to the node :param data: a dict containing commissioning data """ from metadataserver.builtin_scripts.hooks import ( parse_interfaces, update_interface_details, ) if "network-extra" in data: topology_hints = data["network-extra"]["hints"] monitored_interfaces = data["network-extra"]["monitored-interfaces"] address_extra = get_address_extra(data["network-extra"]["interfaces"]) else: topology_hints = None monitored_interfaces = [] address_extra = {} current_interfaces = { interface.id: interface for interface in node.interface_set.all() } # Update the interfaces in dependency order. This make sure that the # parent is created or updated before the child. The order inside # of the sorttop result is ordered so that the modification locks that # postgres grabs when updating interfaces is always in the same order. # The ensures that multiple threads can call this method at the # exact same time. Without this ordering it will deadlock because # multiple are trying to update the same items in the database in # a different order. process_order = sorttop(get_interface_dependencies(data)) process_order = [sorted(list(items)) for items in process_order] # Cache the neighbour discovery settings, since they will be used for # every interface on this Controller. discovery_mode = Config.objects.get_network_discovery_config() interfaces_details = parse_interfaces(node, data) for name in flatten(process_order): # Note: the interface that comes back from this call may be None, # if we decided not to model an interface based on what the rack # sent. interface = update_interface( node, name, data, address_extra, hints=topology_hints, ) if interface is None: continue interface.update_discovery_state(discovery_mode, name in monitored_interfaces) if interface.type == INTERFACE_TYPE.PHYSICAL: update_interface_details(interface, interfaces_details) if interface.id in current_interfaces: del current_interfaces[interface.id] # Remove all the interfaces that no longer exist. We do this in reverse # order so the child is deleted before the parent. deletion_order = {} for nic_id, nic in current_interfaces.items(): deletion_order[nic_id] = [ parent.id for parent in nic.parents.all() if parent.id in current_interfaces ] deletion_order = sorttop(deletion_order) deletion_order = [sorted(list(items)) for items in deletion_order] deletion_order = reversed(list(flatten(deletion_order))) for delete_id in deletion_order: if node.boot_interface_id == delete_id: node.boot_interface = None current_interfaces[delete_id].delete() node.save()