def process_neo4j(self, substrate_file: str, actor_name: str) -> Dict: """ Create ARM and Inventory Slices """ from fabric_cf.actor.core.container.globals import GlobalsSingleton self.container = GlobalsSingleton.get().get_container() result = self.substrate.get_inventory_slice_manager().create_inventory_slice( slice_id=ID(), name=actor_name, rtype=ResourceType(resource_type=Constants.PROPERTY_AGGREGATE_RESOURCE_MODEL)) if result.code != InventorySliceManagerError.ErrorNone: raise AggregateResourceModelCreatorException(f"Could not create ARM: {actor_name}. error={result.code}") self.logger.debug(f"Created aggregate manager resource slice# {result.slice}") if result.slice.get_graph_id() is not None: # load the graph from Neo4j database self.logger.debug(f"Reloading an existing graph for resource slice# {result.slice}") self.arm_graph = FimHelper.get_arm_graph(graph_id=result.slice.get_graph_id()) result.slice.set_graph(graph=self.arm_graph) else: self.arm_graph = FimHelper.get_arm_graph_from_file(filename=substrate_file) result.slice.set_graph(graph=self.arm_graph) self.substrate.get_inventory_slice_manager().update_inventory_slice(slice_obj=result.slice) self.logger.debug(f"Created new graph for resource slice# {result.slice}") for r in self.resources.values(): self.logger.debug(f"Registering resource_handler for resource_type: {r.get_resource_type_label()} " f"for Actor {actor_name}") self.register_handler(resource_config=r) return self.arm_graph.generate_adms()
def update_slice_graph(self, sliver: BaseSliver, rid: str, reservation_state: str) -> BaseSliver: try: self.lock.acquire() # Update for Orchestrator for Active / Ticketed Reservations if sliver is not None and self.graph_id is not None: if sliver.reservation_info is None: sliver.reservation_info = ReservationInfo() sliver.reservation_info.reservation_id = rid sliver.reservation_info.reservation_state = reservation_state FimHelper.update_node(graph_id=self.graph_id, sliver=sliver) return sliver finally: self.lock.release()
def get_net_interface_sliver(self, *, site_ifs_id: str, itype: InterfaceType) -> InterfaceSliver: """ Get Peer Interface Sliver (child of Network Service Sliver) provided node id of Interface Sliver (child of Component Sliver) E.g: Provided Connection Point which is a child of Component i.e. renc-w3-nic2-p1, return Peer Connection Point i.e. HundredGigE 0/0/0/25.3 renc-w3-nic2-p1 => l10 <= HundredGigE 0/0/0/25.3 [Connection Point] Link [Connection Point] @param site_ifs_id Interface Sliver Id @param itype Interface Type @return Interface sliver """ try: self.lock.acquire() result = FimHelper.get_interface_sliver_by_id( ifs_node_id=site_ifs_id, graph=self.combined_broker_model, itype=itype) if len(result) != 1: raise BrokerException( msg= f"More than one Peer Interface Sliver of type {itype} found for " f"IFS: {site_ifs_id}") return next(iter(result)) finally: self.lock.release()
def check_predecessors_ticketed(self, *, reservation: TicketReservationAvro) -> bool: """ Check if all the parent reservations have been ticketed @param reservation: Reservation @return True if parent reservations are ticketed; False otherwise """ ret_val = True if isinstance(reservation, LeaseReservationAvro) and reservation.get_redeem_predecessors() is not None and \ len(reservation.get_redeem_predecessors()) > 0: state_bin = [ReservationStates.Ticketed, ReservationStates.Failed, ReservationStates.Closed, ReservationStates.CloseWait] rids = [] for r in reservation.get_redeem_predecessors(): rids.append(r.get_reservation_id()) reservations = self.controller.get_reservations(rid_list=rids) rid_res_map = {} for r in reservations: rid_res_map[r.get_reservation_id()] = r state = ReservationStates(r.get_state()) self.logger.trace(f"Parent Res# {r.get_reservation_id()} is in state# {state}") if state not in state_bin: ret_val = False break # Parent reservations have been Ticketed; Update BQM Node and Component Id in Node Map # Used by Broker to set vlan - source: (c) # local_name source: (a) # NSO device name source: (a) - need to find the owner switch of the network service in CBM # and take its .name or labels.local_name if ret_val: sliver = reservation.get_sliver() for ifs in sliver.interface_info.interfaces.values(): component_name, rid = ifs.get_node_map() parent_res = rid_res_map.get(rid) if parent_res is not None and \ ReservationStates(parent_res.get_state()) == ReservationStates.Ticketed: node_sliver = parent_res.get_sliver() component = node_sliver.attached_components_info.get_device(name=component_name) graph_id, bqm_component_id = component.get_node_map() graph_id, node_id = node_sliver.get_node_map() ifs.set_node_map(node_map=(node_id, bqm_component_id)) # For shared NICs grab the MAC & VLAN from corresponding Interface Sliver # maintained in the Parent Reservation Sliver if component.get_type() == ComponentType.SharedNIC: parent_res_ifs_sliver = FimHelper.get_site_interface_sliver(component=component, local_name=ifs.get_labels().local_name) parent_labs = parent_res_ifs_sliver.get_label_allocations() ifs.labels = Labels.update(ifs.labels, mac=parent_labs.mac, vlan=parent_labs.vlan) reservation.set_sliver(sliver=sliver) self.logger.trace(f"Res# {reservation.get_reservation_id()} {ret_val}") return ret_val
def get_slice_graph(self, *, token: str, slice_id: str, graph_format_str: str) -> dict: """ Get User Slice :param token Fabric Identity Token :param slice_id Slice Id :param graph_format_str :raises Raises an exception in case of failure :returns Slice Graph on success """ try: controller = self.controller_state.get_management_actor() self.logger.debug(f"get_slice_graph invoked for Controller: {controller}") slice_guid = None if slice_id is not None: slice_guid = ID(uid=slice_id) slice_list = controller.get_slices(id_token=token, slice_id=slice_guid) if slice_list is None or len(slice_list) == 0: if controller.get_last_error() is not None: self.logger.error(controller.get_last_error()) raise OrchestratorException(f"User# has no Slices", http_error_code=NOT_FOUND) slice_obj = next(iter(slice_list)) if slice_obj.get_graph_id() is None: raise OrchestratorException(f"Slice# {slice_obj} does not have graph id") slice_model = FimHelper.get_graph(graph_id=slice_obj.get_graph_id()) graph_format = self.__translate_graph_format(graph_format=graph_format_str) if graph_format == GraphFormat.JSON_NODELINK: slice_model_str = slice_model.serialize_graph() slice_model = FimHelper.get_networkx_graph_from_string(graph_str=slice_model_str) if slice_model is None: raise OrchestratorException(f"Slice# {slice_obj} graph could not be loaded") slice_model_str = slice_model.serialize_graph(format=graph_format) return ResponseBuilder.get_slice_summary(slice_list=slice_list, slice_id=slice_id, slice_model=slice_model_str) except Exception as e: self.logger.error(traceback.format_exc()) self.logger.error(f"Exception occurred processing get_slice_graph e: {e}") raise e
def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, token: str = None, level: int = 10, delete_graph: bool = True, ignore_validation: bool = True, graph_format: GraphFormat = GraphFormat.GRAPHML) -> Tuple[str, ABCCBMPropertyGraph or None]: """ Discover all the available resources by querying Broker :param controller Management Controller Object :param token Fabric Token :param level: level of details :param delete_graph flag indicating if the loaded graph should be deleted or not :param ignore_validation flag indicating to ignore validating the graph (only needed for ADs) :param graph_format: Graph format :return tuple of dictionary containing the BQM and ABCCBMPropertyGraph (if delete_graph = False) """ broker = self.get_broker(controller=controller) if broker is None: raise OrchestratorException("Unable to determine broker proxy for this controller. " "Please check Orchestrator container configuration and logs.") model = controller.get_broker_query_model(broker=broker, id_token=token, level=level, graph_format=graph_format) if model is None: raise OrchestratorException(f"Could not discover types: {controller.get_last_error()}") graph_str = model.get_model() try: if graph_str is not None and graph_str != '': graph = None # Load graph only when GraphML if graph_format == GraphFormat.GRAPHML: graph = FimHelper.get_neo4j_cbm_graph_from_string_direct(graph_str=graph_str, ignore_validation=ignore_validation) if delete_graph: FimHelper.delete_graph(graph_id=graph.get_graph_id()) graph = None return graph_str, graph else: raise OrchestratorException(http_error_code=NOT_FOUND, message="Resource(s) not found!") except Exception as e: self.logger.error(traceback.format_exc()) raise e
def load_aggregate_resource_model(self): if self.aggregate_resource_model_graph_id is not None: self.logger.debug( f"Loading an existing Aggregate ResourceModel Graph:" f" {self.aggregate_resource_model_graph_id}") self.aggregate_resource_model = FimHelper.get_arm_graph( graph_id=self.aggregate_resource_model_graph_id) self.logger.debug( f"Successfully loaded an existing Aggregate Resource Model Graph: " f"{self.aggregate_resource_model_graph_id}")
def get_owners(self, *, node_id: str) -> Tuple[NodeSliver, NetworkServiceSliver]: """ Get owner switch and network service of a Connection Point from BQM @param node_id Node Id of the Connection Point """ try: self.lock.acquire() return FimHelper.get_owners(bqm=self.combined_broker_model, node_id=node_id) finally: self.lock.release()
def restore(self, actor: ABCActorMixin, slice_obj: ABCSlice): self.actor = actor self.slice_object = slice_obj if actor is not None: self.logger = actor.get_logger() if self.callback is not None: self.callback.set_logger(logger=actor.get_logger()) if actor.get_type() == ActorType.Authority: self.graph = FimHelper.get_arm_graph( graph_id=str(self.dlg_graph_id)) if actor.get_policy() is not None: self.policy = actor.get_policy()
def check_and_reload_model(self, *, graph_id) -> ABCARMPropertyGraph or None: """ Reload Neo4j on model restart """ if not self.can_reload_model(): return None self.cleanup_neo4j() self.log.debug(f"Reload Neo4j database started {graph_id}") from fabric_cf.actor.fim.fim_helper import FimHelper arm_graph = FimHelper.get_arm_graph_from_file( filename=self.get_config().get_actor().get_substrate_file(), graph_id=graph_id) self.log.debug(f"Reload Neo4j database completed {graph_id}") return arm_graph
def load_combined_broker_model(self): if self.combined_broker_model_graph_id is None: self.logger.debug("Creating an empty Combined Broker Model Graph") else: self.logger.debug( f"Loading an existing Combined Broker Model Graph: {self.combined_broker_model_graph_id}" ) self.combined_broker_model = FimHelper.get_neo4j_cbm_graph( graph_id=self.combined_broker_model_graph_id) self.combined_broker_model_graph_id = self.combined_broker_model.get_graph_id( ) self.logger.debug( f"Successfully loaded an Combined Broker Model Graph: {self.combined_broker_model_graph_id}" ) self.pluggable_registry.register_pluggable(t=PluggableType.Broker, p=AggregatedBQMPlugin, actor=self.actor, logger=self.logger) self.logger.debug(f"Registered AggregateBQMPlugin")
def __allocate_services( self, *, rid: ID, inv: NetworkServiceInventory, sliver: NetworkServiceSliver, node_id_to_reservations: dict) -> Tuple[str, BaseSliver, Any]: """ Allocate Network Service Slivers @param rid Reservation Id @param inv Inventory @param sliver Requested sliver @param node_id_to_reservations @return tuple containing delegation id, sliver, error message if any """ self.logger.debug(f"Processing Network Service sliver: {sliver}") delegation_id = None error_msg = None owner_switch = None owner_mpls_ns = None # For each Interface Sliver; for ifs in sliver.interface_info.interfaces.values(): # Fetch Network Node Id and BQM Component Id node_id, bqm_component_id = ifs.get_node_map() bqm_component = self.get_component_sliver(node_id=bqm_component_id) # Get BQM Connection Point in Site Delegation (c) site_cp = FimHelper.get_site_interface_sliver( component=bqm_component, local_name=ifs.get_labels().local_name) self.logger.debug( f"Interface Sliver [Site Delegation] (C): {site_cp}") # Get BQM Peer Connection Point in Site Delegation (a) net_cp = self.get_net_interface_sliver( site_ifs_id=site_cp.node_id, itype=InterfaceType.TrunkPort) if net_cp is None: error_msg = "Peer Connection Point not found from Network AM" raise BrokerException(msg=error_msg) self.logger.debug( f"Peer Interface Sliver [Network Delegation] (A): {site_cp}") # need to find the owner switch of the network service in CBM and take it's name or labels.local_name owner_switch, owner_mpls_ns = self.get_owners( node_id=net_cp.node_id) if bqm_component.get_type() == ComponentType.SharedNIC: # VLAN is already set by the Orchestrator using the information from the Node Sliver Parent Reservation if ifs.get_labels().vlan is None: message = "Shared NIC VLAN cannot be None" self.logger.error(message) raise BrokerException( error_code=ExceptionErrorCode.FAILURE, msg=f"{message}") else: existing_reservations = self.get_existing_reservations( node_id=owner_mpls_ns.node_id, node_id_to_reservations=node_id_to_reservations) # Set vlan - source: (c) - only for dedicated NICs ifs = inv.allocate_ifs( requested_ns=sliver, requested_ifs=ifs, owner_switch=owner_switch, mpls_ns=owner_mpls_ns, bqm_ifs_id=net_cp.node_id, existing_reservations=existing_reservations) # local_name source: (a) ifs_labels = ifs.get_labels() ifs_labels = Labels.update(ifs_labels, local_name=net_cp.get_name()) # NSO device name source: (a) - need to find the owner switch of the network service in CBM # and take its name or labels.local_name # Set the NSO device-name ifs_labels = Labels.update(ifs_labels, device_name=owner_switch.get_name()) adm_ids = owner_switch.get_structural_info().adm_graph_ids site_adm_ids = bqm_component.get_structural_info().adm_graph_ids self.logger.debug(f"Owner MPLS Network Service: {owner_mpls_ns}") self.logger.debug(f"Owner Switch: {owner_switch}") self.logger.debug( f"Owner Switch: {owner_switch.network_service_info}") net_adm_ids = [ x for x in adm_ids if not x in site_adm_ids or site_adm_ids.remove(x) ] if len(net_adm_ids) != 1: error_msg = f"More than 1 or 0 Network Delegations found! net_adm_ids: {net_adm_ids}" self.logger.error(error_msg) raise BrokerException(msg=error_msg) # Update the Interface Sliver Node Map to map to (a) ifs.set_node_map(node_map=(self.combined_broker_model_graph_id, net_cp.node_id)) delegation_id = net_adm_ids[0] ifs.labels = ifs_labels self.logger.debug( f"Allocated Interface Sliver: {ifs} delegation: {delegation_id}" ) # Update the Network Service Sliver Node Map to map to parent of (a) sliver.set_node_map(node_map=(self.combined_broker_model_graph_id, owner_mpls_ns.node_id)) # Set the Subnet and gateway from the Owner Switch (a) if sliver.get_type() == ServiceType.FABNetv6 or sliver.get_type( ) == ServiceType.FABNetv4: existing_reservations = self.get_existing_reservations( node_id=owner_mpls_ns.node_id, node_id_to_reservations=node_id_to_reservations) sliver = inv.allocate(rid=rid, requested_ns=sliver, owner_switch=owner_switch, existing_reservations=existing_reservations) return delegation_id, sliver, error_msg
def assign( self, *, reservation: ABCAuthorityReservation, delegation_name: str, graph_node: BaseSliver, existing_reservations: List[ABCReservationMixin]) -> ResourceSet: """ Assign a reservation :param reservation: reservation :param delegation_name: Name of delegation serving the request :param graph_node: ARM Graph Node serving the reservation :param existing_reservations: Existing Reservations served by the same ARM node :return: ResourceSet with updated sliver annotated with properties :raises: AuthorityException in case the request cannot be satisfied """ if graph_node.capacity_delegations is None or reservation is None: raise AuthorityException(Constants.INVALID_ARGUMENT) delegated_capacities = graph_node.get_capacity_delegations() available_delegated_capacity = FimHelper.get_delegation( delegated_capacities=delegated_capacities, delegation_name=delegation_name) if available_delegated_capacity is None: raise AuthorityException( f"Allocated node {graph_node.node_id} does not have delegation: {delegation_name}" ) reservation.set_send_with_deficit(value=True) requested = reservation.get_requested_resources().get_sliver() if not isinstance(requested, NodeSliver): raise AuthorityException( f"Invalid resource type {requested.get_type()}") current = reservation.get_resources() resource_type = ResourceType(resource_type=str(requested.get_type())) gained = None lost = None if current is None: # Check if Capacities can be satisfied by Delegated Capacities self.__check_capacities( rid=reservation.get_reservation_id(), requested_capacities=requested.get_capacity_allocations(), available_capacities=available_delegated_capacity, existing_reservations=existing_reservations) # Check if Capacities can be satisfied by Capacities self.__check_capacities( rid=reservation.get_reservation_id(), requested_capacities=requested.get_capacity_allocations(), available_capacities=graph_node.get_capacities(), existing_reservations=existing_reservations) # Check components # Check if Components can be allocated if requested.attached_components_info is not None: self.__check_components( rid=reservation.get_reservation_id(), requested_components=requested.attached_components_info, graph_node=graph_node, existing_reservations=existing_reservations) self.logger.debug( f"Slice properties: {reservation.get_slice().get_config_properties()}" ) unit = Unit( rid=reservation.get_reservation_id(), slice_id=reservation.get_slice_id(), actor_id=self.authority.get_guid(), sliver=requested, rtype=resource_type, properties=reservation.get_slice().get_config_properties()) gained = UnitSet(plugin=self.authority.get_plugin(), units={unit.reservation_id: unit}) else: # FIX ME: handle modify self.logger.info( f"Extend Lease for now, no modify supported res# {reservation}" ) return current result = ResourceSet(gained=gained, lost=lost, rtype=resource_type) result.set_sliver(sliver=requested) return result
def load_graph(self, *, graph_str: str): self.graph = FimHelper.get_graph_from_string_direct( graph_str=graph_str)
def __build_network_service_reservations(self, slice_graph: ABCASMPropertyGraph, node_res_mapping: Dict[str, str]) -> List[TicketReservationAvro]: """ Build Network Service Reservations @param slice_graph Slice graph @param node_res_mapping Mapping of Network Node sliver Id to reservation Id; Needed to add dependency information in network service slivers @return list of network service reservations """ reservations = [] for ns_id in slice_graph.get_all_network_service_nodes(): # Build Network Service Sliver sliver = slice_graph.build_deep_ns_sliver(node_id=ns_id) sliver_type = sliver.get_type() # Ignore Sliver types P4,OVS and MPLS if sliver_type in self.ignorable_ns: continue # Process only the currently supported Network Sliver types L2STS, L2PTP and L2Bridge elif sliver_type in self.supported_ns: self.logger.trace(f"Network Service Sliver: {sliver}") # Processing Interface Slivers if sliver.interface_info is not None: predecessor_reservations = [] for ifs in sliver.interface_info.interfaces.values(): # Get Mapping information for Interface Sliver from ASM # i.e. [Peer IFS, Peer NS Id, Component Name, Node Id] ifs_mapping = FimHelper.get_interface_sliver_mapping(ifs_node_id=ifs.node_id, slice_graph=slice_graph) if ifs_mapping is None: raise OrchestratorException(message=f"Peer connection point not found for ifs# {ifs}", http_error_code=BAD_REQUEST) # capacities (bw in Gbps, burst size is in Mbits) source: (b) # Set Capacities ifs.set_capacities(cap=ifs_mapping.get_peer_ifs().get_capacities()) # Set Labels ifs.set_labels(lab=ifs_mapping.get_peer_ifs().get_labels()) # Save the parent component name and the parent reservation id in the Node Map parent_res_id = node_res_mapping.get(ifs_mapping.get_node_id(), None) node_map = tuple([ifs_mapping.get_component_name(), parent_res_id]) ifs.set_node_map(node_map=node_map) self.logger.trace(f"Interface Sliver: {ifs}") if parent_res_id is not None and parent_res_id not in predecessor_reservations: predecessor_reservations.append(parent_res_id) # Generate reservation for the sliver reservation = self.reservation_converter.generate_reservation(sliver=sliver, slice_id=self.slice_obj.get_slice_id(), end_time=self.slice_obj.get_lease_end(), pred_list=predecessor_reservations) reservations.append(reservation) else: raise OrchestratorException(message="Not implemented", http_error_code=BAD_REQUEST) return reservations
def create_slice(self, *, token: str, slice_name: str, slice_graph: str, ssh_key: str, lease_end_time: str) -> dict: """ Create a slice :param token Fabric Identity Token :param slice_name Slice Name :param slice_graph Slice Graph Model :param ssh_key: User ssh key :param lease_end_time: Lease End Time (UTC) :raises Raises an exception in case of failure :returns List of reservations created for the Slice on success """ if self.globals.is_maintenance_mode_on(): raise OrchestratorException(Constants.MAINTENANCE_MODE_ERROR) slice_id = None controller = None orchestrator_slice = None asm_graph = None try: end_time = self.__validate_lease_end_time(lease_end_time=lease_end_time) controller = self.controller_state.get_management_actor() self.logger.debug(f"create_slice invoked for Controller: {controller}") # Check if an Active slice exists already with the same name for the user existing_slices = controller.get_slices(id_token=token, slice_name=slice_name) if existing_slices is not None and len(existing_slices) != 0: for es in existing_slices: if es.get_state() != SliceState.Dead.value and es.get_state() != SliceState.Closing.value: raise OrchestratorException(f"Slice {slice_name} already exists") asm_graph = FimHelper.get_neo4j_asm_graph(slice_graph=slice_graph) broker = self.get_broker(controller=controller) if broker is None: raise OrchestratorException("Unable to determine broker proxy for this controller. " "Please check Orchestrator container configuration and logs.") slice_obj = SliceAvro() slice_obj.set_slice_name(slice_name) slice_obj.set_client_slice(True) slice_obj.set_description("Description") slice_obj.graph_id = asm_graph.get_graph_id() slice_obj.set_config_properties(value={Constants.USER_SSH_KEY: ssh_key}) slice_obj.set_lease_end(lease_end=end_time) self.logger.debug(f"Adding Slice {slice_name}") slice_id = controller.add_slice(slice_obj=slice_obj, id_token=token) if slice_id is None: self.logger.error(controller.get_last_error()) self.logger.error("Slice could not be added to Database") raise OrchestratorException("Slice could not be added to Database") self.logger.debug(f"Slice {slice_name}/{slice_id} added successfully") slice_obj.set_slice_id(slice_id=str(slice_id)) orchestrator_slice = OrchestratorSliceWrapper(controller=controller, broker=broker, slice_obj=slice_obj, logger=self.logger) orchestrator_slice.lock() # Create Slivers from Slice Graph; Compute Reservations from Slivers; # Add Reservations to relational database; computed_reservations = orchestrator_slice.create(slice_graph=asm_graph) # Process the Slice i.e. Demand the computed reservations i.e. Add them to the policy # Once added to the policy; Actor Tick Handler will do following asynchronously: # 1. Ticket message exchange with broker and # 2. Redeem message exchange with AM once ticket is granted by Broker self.controller_state.demand_slice(controller_slice=orchestrator_slice) # self.controller_state.get_sdt().process_slice(controller_slice=orchestrator_slice) return ResponseBuilder.get_reservation_summary(res_list=computed_reservations) except Exception as e: if slice_id is not None and controller is not None and asm_graph is not None: FimHelper.delete_graph(graph_id=asm_graph.graph_id) controller.remove_slice(slice_id=slice_id, id_token=token) self.logger.error(traceback.format_exc()) self.logger.error(f"Exception occurred processing create_slice e: {e}") raise e finally: if orchestrator_slice is not None: orchestrator_slice.unlock()