From 4c212214f7b095940bb2ccafaeba696205dbd08e Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 1 Apr 2024 13:19:00 -0400 Subject: [PATCH 001/133] return slice owner information --- .../core/apis/abc_actor_management_object.py | 5 +- fabric_cf/actor/core/apis/abc_database.py | 5 +- fabric_cf/actor/core/apis/abc_mgmt_actor.py | 4 +- .../core/manage/actor_management_object.py | 5 +- .../actor/core/manage/kafka/kafka_actor.py | 2 +- .../actor/core/manage/local/local_actor.py | 2 +- .../actor/core/plugins/db/actor_database.py | 5 +- fabric_cf/actor/db/psql_database.py | 18 +++++- .../orchestrator/core/orchestrator_handler.py | 4 +- .../orchestrator/core/response_builder.py | 4 ++ fabric_cf/orchestrator/openapi.json | 35 +++++++++++ .../controllers/slices_controller.py | 8 ++- .../swagger_server/models/slice.py | 58 ++++++++++++++++++- .../swagger_server/models/sliver.py | 58 ++++++++++++++++++- .../response/slices_controller.py | 17 ++++-- .../swagger_server/swagger/swagger.yaml | 25 ++++++++ .../test/test_slices_controller.py | 2 + 17 files changed, 232 insertions(+), 25 deletions(-) diff --git a/fabric_cf/actor/core/apis/abc_actor_management_object.py b/fabric_cf/actor/core/apis/abc_actor_management_object.py index 3e2abbbf..8bf95c68 100644 --- a/fabric_cf/actor/core/apis/abc_actor_management_object.py +++ b/fabric_cf/actor/core/apis/abc_actor_management_object.py @@ -256,7 +256,8 @@ def get_reservations(self, *, caller: AuthToken, states: List[int] = None, def get_slices(self, *, slice_id: ID, caller: AuthToken, slice_name: str = None, email: str = None, states: List[int] = None, project: str = None, limit: int = None, - offset: int = None, user_id: str = None) -> ResultSliceAvro: + offset: int = None, user_id: str = None, search: str = None, + exact_match: bool = False) -> ResultSliceAvro: """ Obtains all slices. @param slice_id slice id @@ -268,6 +269,8 @@ def get_slices(self, *, slice_id: ID, caller: AuthToken, slice_name: str = None, @param offset offset @param caller caller @param user_id user_id + @param search: search term applied + @param exact_match: Exact Match for Search term @return returns list of slices """ diff --git a/fabric_cf/actor/core/apis/abc_database.py b/fabric_cf/actor/core/apis/abc_database.py index d972f28d..452f19c0 100644 --- a/fabric_cf/actor/core/apis/abc_database.py +++ b/fabric_cf/actor/core/apis/abc_database.py @@ -194,7 +194,8 @@ def get_client_reservations(self, *, slice_id: ID = None) -> List[ABCReservation @abstractmethod def get_slices(self, *, slice_id: ID = None, slice_name: str = None, project_id: str = None, email: str = None, states: list[int] = None, oidc_sub: str = None, slc_type: List[SliceTypes] = None, - limit: int = None, offset: int = None, lease_end: datetime = None) -> List[ABCSlice] or None: + limit: int = None, offset: int = None, lease_end: datetime = None, + search: str = None, exact_match: bool = False) -> List[ABCSlice] or None: """ Retrieves the specified slices. @@ -208,6 +209,8 @@ def get_slices(self, *, slice_id: ID = None, slice_name: str = None, project_id: @param limit limit @param offset offset @param lease_end lease_end + @param search: search term applied + @param exact_match: Exact Match for Search term @return list of slices diff --git a/fabric_cf/actor/core/apis/abc_mgmt_actor.py b/fabric_cf/actor/core/apis/abc_mgmt_actor.py index eef1505a..cebc73a3 100644 --- a/fabric_cf/actor/core/apis/abc_mgmt_actor.py +++ b/fabric_cf/actor/core/apis/abc_mgmt_actor.py @@ -47,7 +47,7 @@ class ABCMgmtActor(ABCComponent): @abstractmethod def get_slices(self, *, slice_id: ID = None, slice_name: str = None, email: str = None, project: str = None, states: List[int] = None, limit: int = None, offset: int = None, - user_id: str = None) -> List[SliceAvro] or None: + user_id: str = None, search: str = None, exact_match: bool = False) -> List[SliceAvro] or None: """ Obtains all slices. @param slice_id slice id @@ -58,6 +58,8 @@ def get_slices(self, *, slice_id: ID = None, slice_name: str = None, email: str @param limit limit @param offset offset @param user_id user_id + @param search: search term applied + @param exact_match: Exact Match for Search term @return returns list of slices """ diff --git a/fabric_cf/actor/core/manage/actor_management_object.py b/fabric_cf/actor/core/manage/actor_management_object.py index e08df666..0cd98f94 100644 --- a/fabric_cf/actor/core/manage/actor_management_object.py +++ b/fabric_cf/actor/core/manage/actor_management_object.py @@ -138,7 +138,8 @@ def set_actor(self, *, actor: ABCActorMixin): def get_slices(self, *, slice_id: ID, caller: AuthToken, slice_name: str = None, email: str = None, states: List[int] = None, project: str = None, limit: int = None, - offset: int = None, user_id: str = None) -> ResultSliceAvro: + offset: int = None, user_id: str = None, search: str = None, + exact_match: bool = False) -> ResultSliceAvro: result = ResultSliceAvro() result.status = ResultAvro() @@ -152,7 +153,7 @@ def get_slices(self, *, slice_id: ID, caller: AuthToken, slice_name: str = None, try: slice_list = self.db.get_slices(slice_id=slice_id, slice_name=slice_name, email=email, states=states, project_id=project, limit=limit, offset=offset, - oidc_sub=user_id) + oidc_sub=user_id, search=search, exact_match=exact_match) except Exception as e: self.logger.error("getSlices:db access {}".format(e)) result.status.set_code(ErrorCodes.ErrorDatabaseError.value) diff --git a/fabric_cf/actor/core/manage/kafka/kafka_actor.py b/fabric_cf/actor/core/manage/kafka/kafka_actor.py index 10ede622..54119ded 100644 --- a/fabric_cf/actor/core/manage/kafka/kafka_actor.py +++ b/fabric_cf/actor/core/manage/kafka/kafka_actor.py @@ -79,7 +79,7 @@ def toggle_maintenance_mode(self, actor_guid: str, callback_topic: str, sites: L def get_slices(self, *, slice_id: ID = None, slice_name: str = None, email: str = None, project: str = None, states: List[int] = None, limit: int = None, offset: int = None, - user_id: str = None) -> List[SliceAvro] or None: + user_id: str = None, search: str = None, exact_match: bool = False) -> List[SliceAvro] or None: request = GetSlicesRequestAvro() request = self.fill_request_by_id_message(request=request, email=email, slice_id=slice_id, slice_name=slice_name, states=states) diff --git a/fabric_cf/actor/core/manage/local/local_actor.py b/fabric_cf/actor/core/manage/local/local_actor.py index c462630d..028bdcf6 100644 --- a/fabric_cf/actor/core/manage/local/local_actor.py +++ b/fabric_cf/actor/core/manage/local/local_actor.py @@ -57,7 +57,7 @@ def __init__(self, *, manager: ManagementObject, auth: AuthToken): def get_slices(self, *, slice_id: ID = None, slice_name: str = None, email: str = None, project: str = None, states: List[int] = None, limit: int = None, offset: int = None, - user_id: str = None) -> List[SliceAvro] or None: + user_id: str = None, search: str = None, exact_match: bool = False) -> List[SliceAvro] or None: self.clear_last() try: result = self.manager.get_slices(slice_id=slice_id, caller=self.auth, states=states, diff --git a/fabric_cf/actor/core/plugins/db/actor_database.py b/fabric_cf/actor/core/plugins/db/actor_database.py index 6285dc7e..97ac4ab5 100644 --- a/fabric_cf/actor/core/plugins/db/actor_database.py +++ b/fabric_cf/actor/core/plugins/db/actor_database.py @@ -201,7 +201,8 @@ def remove_slice(self, *, slice_id: ID): def get_slices(self, *, slice_id: ID = None, slice_name: str = None, project_id: str = None, email: str = None, states: list[int] = None, oidc_sub: str = None, slc_type: List[SliceTypes] = None, - limit: int = None, offset: int = None, lease_end: datetime = None) -> List[ABCSlice] or None: + limit: int = None, offset: int = None, lease_end: datetime = None, + search: str = None, exact_match: bool = False) -> List[ABCSlice] or None: result = [] try: try: @@ -212,7 +213,7 @@ def get_slices(self, *, slice_id: ID = None, slice_name: str = None, project_id: sid = str(slice_id) if slice_id is not None else None slices = self.db.get_slices(slice_id=sid, slice_name=slice_name, project_id=project_id, email=email, states=states, oidc_sub=oidc_sub, slc_type=slice_type, limit=limit, - offset=offset, lease_end=lease_end) + offset=offset, lease_end=lease_end, search=search, exact_match=exact_match) finally: if self.lock.locked(): self.lock.release() diff --git a/fabric_cf/actor/db/psql_database.py b/fabric_cf/actor/db/psql_database.py index 5d7074af..95dcbeec 100644 --- a/fabric_cf/actor/db/psql_database.py +++ b/fabric_cf/actor/db/psql_database.py @@ -30,7 +30,7 @@ from datetime import datetime, timezone from typing import List, Tuple, Dict -from sqlalchemy import create_engine, desc +from sqlalchemy import create_engine, desc, func from sqlalchemy.orm import scoped_session, sessionmaker, joinedload from fabric_cf.actor.core.common.constants import Constants @@ -99,6 +99,7 @@ def reset_db(self): session.query(Proxies).delete() session.query(Units).delete() session.query(Delegations).delete() + session.query(Components).delete() session.query(Reservations).delete() session.query(Slices).delete() session.query(ManagerObjects).delete() @@ -540,7 +541,8 @@ def create_slices_filter(*, slice_id: str = None, slice_name: str = None, projec def get_slices(self, *, slice_id: str = None, slice_name: str = None, project_id: str = None, email: str = None, states: list[int] = None, oidc_sub: str = None, slc_type: list[int] = None, limit: int = None, - offset: int = None, lease_end: datetime = None) -> List[dict]: + offset: int = None, lease_end: datetime = None, search: str = None, + exact_match: bool = False) -> List[dict]: """ Get slices for an actor @param slice_id actor id @@ -553,6 +555,8 @@ def get_slices(self, *, slice_id: str = None, slice_name: str = None, project_id @param limit limit @param offset offset @param lease_end lease_end + @param search: search term applied + @param exact_match: Exact Match for Search term @return list of slices """ result = [] @@ -563,6 +567,16 @@ def get_slices(self, *, slice_id: str = None, slice_name: str = None, project_id rows = session.query(Slices).filter_by(**filter_dict) + if search: + if exact_match: + search_term = func.lower(search) + rows = rows.filter(((func.lower(Slices.email) == search_term) | + (func.lower(Slices.oidc_claim_sub) == search_term))) + else: + rows = rows.filter( + ((Slices.email.ilike("%" + search + "%")) | + (Slices.oidc_claim_sub.ilike("%" + search + "%")))) + if lease_end is not None: rows = rows.filter(Slices.lease_end < lease_end) diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index 421e6196..865b4927 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -379,7 +379,7 @@ def get_slivers(self, *, token: str, slice_id: str, sliver_id: str = None, as_se raise e def get_slices(self, *, token: str, states: List[str], name: str, limit: int, offset: int, - as_self: bool = True) -> List[dict]: + as_self: bool = True, search: str = None, exact_match: bool = False) -> List[dict]: """ Get User Slices :param token Fabric Identity Token @@ -388,6 +388,8 @@ def get_slices(self, *, token: str, states: List[str], name: str, limit: int, of :param limit Number of slices to return :param offset Offset :param as_self flag; True - return calling user's slices otherwise, return all slices in the project + :param search: search term applied + :param exact_match: Exact Match for Search term :raises Raises an exception in case of failure :returns List of Slices on success """ diff --git a/fabric_cf/orchestrator/core/response_builder.py b/fabric_cf/orchestrator/core/response_builder.py index c9cd7365..96c16631 100644 --- a/fabric_cf/orchestrator/core/response_builder.py +++ b/fabric_cf/orchestrator/core/response_builder.py @@ -42,6 +42,8 @@ class ResponseBuilder: PROP_NAME = "name" PROP_STATE = "state" PROP_ERROR = "error" + PROP_OWNER_EMAIL = "owner_email" + PROP_OWNER_USER_ID = "owner_user_id" PROP_PROJECT_ID = "project_id" PROP_PROJECT_NAME = "project_name" PROP_MODEL = "model" @@ -121,6 +123,8 @@ def get_slice_summary(*, slice_list: List[SliceAvro], slice_model: str = None) - ResponseBuilder.PROP_NAME: s.get_slice_name(), ResponseBuilder.PROP_GRAPH_ID: s.get_graph_id(), ResponseBuilder.PROP_STATE: SliceState(s.get_state()).name, + ResponseBuilder.PROP_OWNER_EMAIL: s.get_owner().get_email(), + ResponseBuilder.PROP_OWNER_USER_ID: s.get_owner().get_oidc_sub_claim() } end_time = s.get_lease_end() if end_time is not None: diff --git a/fabric_cf/orchestrator/openapi.json b/fabric_cf/orchestrator/openapi.json index 13357fdf..a4a8b086 100644 --- a/fabric_cf/orchestrator/openapi.json +++ b/fabric_cf/orchestrator/openapi.json @@ -287,6 +287,29 @@ "type": "string" } }, + { + "name": "search", + "in": "query", + "description": "search term applied", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "string" + } + }, + { + "name": "exact_match", + "in": "query", + "description": "Exact Match for Search term", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "boolean", + "default": false + } + }, { "name": "as_self", "in": "query", @@ -2113,6 +2136,12 @@ }, "slice_id": { "type": "string" + }, + "owner_user_id": { + "type": "string" + }, + "owner_email": { + "type": "string" } } }, @@ -2175,6 +2204,12 @@ }, "sliver_id": { "type": "string" + }, + "owner_user_id": { + "type": "string" + }, + "owner_email": { + "type": "string" } } }, diff --git a/fabric_cf/orchestrator/swagger_server/controllers/slices_controller.py b/fabric_cf/orchestrator/swagger_server/controllers/slices_controller.py index 7b1dd8dc..4d0f6c8e 100644 --- a/fabric_cf/orchestrator/swagger_server/controllers/slices_controller.py +++ b/fabric_cf/orchestrator/swagger_server/controllers/slices_controller.py @@ -86,7 +86,7 @@ def slices_delete_slice_id_delete(slice_id): # noqa: E501 return rc.slices_delete_slice_id_delete(slice_id) -def slices_get(name=None, as_self=None, states=None, limit=None, offset=None): # noqa: E501 +def slices_get(name=None, search=None, exact_match=None, as_self=None, states=None, limit=None, offset=None): # noqa: E501 """Retrieve a listing of user slices Retrieve a listing of user slices. It returns list of all slices belonging to all members in a project when @@ -94,6 +94,10 @@ def slices_get(name=None, as_self=None, states=None, limit=None, offset=None): :param name: Search for Slices with the name :type name: str + :param search: search term applied + :type search: str + :param exact_match: Exact Match for Search term + :type exact_match: bool :param as_self: GET object as Self :type as_self: bool :param states: Search for Slices in the specified states @@ -105,7 +109,7 @@ def slices_get(name=None, as_self=None, states=None, limit=None, offset=None): :rtype: Slices """ - return rc.slices_get(name, states, limit, offset, as_self=as_self) + return rc.slices_get(name, states, limit, offset, as_self=as_self, search=search, exact_match=exact_match) def slices_modify_slice_id_accept_post(slice_id): # noqa: E501 diff --git a/fabric_cf/orchestrator/swagger_server/models/slice.py b/fabric_cf/orchestrator/swagger_server/models/slice.py index aaea0b63..baeb3479 100644 --- a/fabric_cf/orchestrator/swagger_server/models/slice.py +++ b/fabric_cf/orchestrator/swagger_server/models/slice.py @@ -14,7 +14,7 @@ class Slice(Model): Do not edit the class manually. """ - def __init__(self, model: str=None, lease_start_time: str=None, lease_end_time: str=None, state: str=None, project_id: str=None, project_name: str=None, graph_id: str=None, name: str=None, slice_id: str=None): # noqa: E501 + def __init__(self, model: str=None, lease_start_time: str=None, lease_end_time: str=None, state: str=None, project_id: str=None, project_name: str=None, graph_id: str=None, name: str=None, slice_id: str=None, owner_user_id: str=None, owner_email: str=None): # noqa: E501 """Slice - a model defined in Swagger :param model: The model of this Slice. # noqa: E501 @@ -35,6 +35,10 @@ def __init__(self, model: str=None, lease_start_time: str=None, lease_end_time: :type name: str :param slice_id: The slice_id of this Slice. # noqa: E501 :type slice_id: str + :param owner_user_id: The owner_user_id of this Slice. # noqa: E501 + :type owner_user_id: str + :param owner_email: The owner_email of this Slice. # noqa: E501 + :type owner_email: str """ self.swagger_types = { 'model': str, @@ -45,7 +49,9 @@ def __init__(self, model: str=None, lease_start_time: str=None, lease_end_time: 'project_name': str, 'graph_id': str, 'name': str, - 'slice_id': str + 'slice_id': str, + 'owner_user_id': str, + 'owner_email': str } self.attribute_map = { @@ -57,7 +63,9 @@ def __init__(self, model: str=None, lease_start_time: str=None, lease_end_time: 'project_name': 'project_name', 'graph_id': 'graph_id', 'name': 'name', - 'slice_id': 'slice_id' + 'slice_id': 'slice_id', + 'owner_user_id': 'owner_user_id', + 'owner_email': 'owner_email' } self._model = model self._lease_start_time = lease_start_time @@ -68,6 +76,8 @@ def __init__(self, model: str=None, lease_start_time: str=None, lease_end_time: self._graph_id = graph_id self._name = name self._slice_id = slice_id + self._owner_user_id = owner_user_id + self._owner_email = owner_email @classmethod def from_dict(cls, dikt) -> 'Slice': @@ -274,3 +284,45 @@ def slice_id(self, slice_id: str): raise ValueError("Invalid value for `slice_id`, must not be `None`") # noqa: E501 self._slice_id = slice_id + + @property + def owner_user_id(self) -> str: + """Gets the owner_user_id of this Slice. + + + :return: The owner_user_id of this Slice. + :rtype: str + """ + return self._owner_user_id + + @owner_user_id.setter + def owner_user_id(self, owner_user_id: str): + """Sets the owner_user_id of this Slice. + + + :param owner_user_id: The owner_user_id of this Slice. + :type owner_user_id: str + """ + + self._owner_user_id = owner_user_id + + @property + def owner_email(self) -> str: + """Gets the owner_email of this Slice. + + + :return: The owner_email of this Slice. + :rtype: str + """ + return self._owner_email + + @owner_email.setter + def owner_email(self, owner_email: str): + """Sets the owner_email of this Slice. + + + :param owner_email: The owner_email of this Slice. + :type owner_email: str + """ + + self._owner_email = owner_email diff --git a/fabric_cf/orchestrator/swagger_server/models/sliver.py b/fabric_cf/orchestrator/swagger_server/models/sliver.py index d2dbd079..de11dce9 100644 --- a/fabric_cf/orchestrator/swagger_server/models/sliver.py +++ b/fabric_cf/orchestrator/swagger_server/models/sliver.py @@ -14,7 +14,7 @@ class Sliver(Model): Do not edit the class manually. """ - def __init__(self, notice: str=None, sliver_type: str=None, sliver: object=None, lease_start_time: str=None, lease_end_time: str=None, state: str=None, pending_state: str=None, join_state: str=None, graph_node_id: str=None, slice_id: str=None, sliver_id: str=None): # noqa: E501 + def __init__(self, notice: str=None, sliver_type: str=None, sliver: object=None, lease_start_time: str=None, lease_end_time: str=None, state: str=None, pending_state: str=None, join_state: str=None, graph_node_id: str=None, slice_id: str=None, sliver_id: str=None, owner_user_id: str=None, owner_email: str=None): # noqa: E501 """Sliver - a model defined in Swagger :param notice: The notice of this Sliver. # noqa: E501 @@ -39,6 +39,10 @@ def __init__(self, notice: str=None, sliver_type: str=None, sliver: object=None, :type slice_id: str :param sliver_id: The sliver_id of this Sliver. # noqa: E501 :type sliver_id: str + :param owner_user_id: The owner_user_id of this Sliver. # noqa: E501 + :type owner_user_id: str + :param owner_email: The owner_email of this Sliver. # noqa: E501 + :type owner_email: str """ self.swagger_types = { 'notice': str, @@ -51,7 +55,9 @@ def __init__(self, notice: str=None, sliver_type: str=None, sliver: object=None, 'join_state': str, 'graph_node_id': str, 'slice_id': str, - 'sliver_id': str + 'sliver_id': str, + 'owner_user_id': str, + 'owner_email': str } self.attribute_map = { @@ -65,7 +71,9 @@ def __init__(self, notice: str=None, sliver_type: str=None, sliver: object=None, 'join_state': 'join_state', 'graph_node_id': 'graph_node_id', 'slice_id': 'slice_id', - 'sliver_id': 'sliver_id' + 'sliver_id': 'sliver_id', + 'owner_user_id': 'owner_user_id', + 'owner_email': 'owner_email' } self._notice = notice self._sliver_type = sliver_type @@ -78,6 +86,8 @@ def __init__(self, notice: str=None, sliver_type: str=None, sliver: object=None, self._graph_node_id = graph_node_id self._slice_id = slice_id self._sliver_id = sliver_id + self._owner_user_id = owner_user_id + self._owner_email = owner_email @classmethod def from_dict(cls, dikt) -> 'Sliver': @@ -326,3 +336,45 @@ def sliver_id(self, sliver_id: str): raise ValueError("Invalid value for `sliver_id`, must not be `None`") # noqa: E501 self._sliver_id = sliver_id + + @property + def owner_user_id(self) -> str: + """Gets the owner_user_id of this Sliver. + + + :return: The owner_user_id of this Sliver. + :rtype: str + """ + return self._owner_user_id + + @owner_user_id.setter + def owner_user_id(self, owner_user_id: str): + """Sets the owner_user_id of this Sliver. + + + :param owner_user_id: The owner_user_id of this Sliver. + :type owner_user_id: str + """ + + self._owner_user_id = owner_user_id + + @property + def owner_email(self) -> str: + """Gets the owner_email of this Sliver. + + + :return: The owner_email of this Sliver. + :rtype: str + """ + return self._owner_email + + @owner_email.setter + def owner_email(self, owner_email: str): + """Sets the owner_email of this Sliver. + + + :param owner_email: The owner_email of this Sliver. + :type owner_email: str + """ + + self._owner_email = owner_email diff --git a/fabric_cf/orchestrator/swagger_server/response/slices_controller.py b/fabric_cf/orchestrator/swagger_server/response/slices_controller.py index 17d8d0ee..4a85a318 100644 --- a/fabric_cf/orchestrator/swagger_server/response/slices_controller.py +++ b/fabric_cf/orchestrator/swagger_server/response/slices_controller.py @@ -23,6 +23,7 @@ # # # Author: Komal Thareja (kthare10@renci.org) +from typing import List from fabric_cf.orchestrator.core.exceptions import OrchestratorException from fabric_cf.orchestrator.core.orchestrator_handler import OrchestratorHandler @@ -158,21 +159,27 @@ def slices_delete_slice_id_delete(slice_id) -> Status200OkNoContent: # noqa: E5 return cors_error_response(error=e) -def slices_get(name=None, states=None, limit=None, offset=None, as_self=True) -> Slices: # noqa: E501 +def slices_get(name: str = None, search: str = None, exact_match: bool = False, + as_self: bool = True, states: List[str] = None, limit: int = 5, offset: int = 0): # noqa: E501 """Retrieve a listing of user slices - Retrieve a listing of user slices # noqa: E501 + Retrieve a listing of user slices. It returns list of all slices belonging to all members in a project when + 'as_self' is False otherwise returns only the all user's slices in a project. # noqa: E501 :param name: Search for Slices with the name :type name: str + :param search: search term applied + :type search: str + :param exact_match: Exact Match for Search term + :type exact_match: str + :param as_self: GET object as Self + :type as_self: bool :param states: Search for Slices in the specified states :type states: List[str] :param limit: maximum number of results to return per page (1 or more) :type limit: int :param offset: number of items to skip before starting to collect the result set :type offset: int - :param as_self: GET object as Self - :type as_self: bool :rtype: Slices """ @@ -182,7 +189,7 @@ def slices_get(name=None, states=None, limit=None, offset=None, as_self=True) -> try: token = get_token() slices_dict = handler.get_slices(token=token, states=states, name=name, limit=limit, offset=offset, - as_self=as_self) + as_self=as_self, search=search, exact_match=exact_match) response = Slices() response.data = [] response.type = 'slices' diff --git a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml index 89bfbccd..a316b770 100644 --- a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml +++ b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml @@ -194,6 +194,23 @@ paths: schema: minLength: 3 type: string + - name: search + in: query + description: search term applied + required: false + style: form + explode: true + schema: + type: string + - name: exact_match + in: query + description: Exact Match for Search term + required: false + style: form + explode: true + schema: + type: boolean + default: false - name: as_self in: query description: GET object as Self @@ -1433,6 +1450,10 @@ components: type: string slice_id: type: string + owner_user_id: + type: string + owner_email: + type: string slivers: type: object allOf: @@ -1472,6 +1493,10 @@ components: type: string sliver_id: type: string + owner_user_id: + type: string + owner_email: + type: string version: type: object allOf: diff --git a/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py index 80febabc..daf8f5c3 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py @@ -84,6 +84,8 @@ def test_slices_get(self): Retrieve a listing of user slices """ query_string = [('name', 'name_example'), + ('search', 'search_example'), + ('exact_match', false), ('as_self', true), ('states', 'states_example'), ('limit', 200), From 98148189846c32302537282611116e95492b22cf Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 2 Apr 2024 10:21:30 -0400 Subject: [PATCH 002/133] get slices param fix --- fabric_cf/actor/db/__init__.py | 2 +- .../controllers/slices_controller.py | 17 +++++---- .../swagger_server/swagger/swagger.yaml | 34 ++++++++--------- .../test/test_poas_controller.py | 6 +-- .../test/test_resources_controller.py | 4 +- .../test/test_slices_controller.py | 37 ++++++++++--------- .../test/test_slivers_controller.py | 8 ++-- .../test/test_version_controller.py | 3 +- 8 files changed, 58 insertions(+), 53 deletions(-) diff --git a/fabric_cf/actor/db/__init__.py b/fabric_cf/actor/db/__init__.py index b2d022c7..47c4ad61 100644 --- a/fabric_cf/actor/db/__init__.py +++ b/fabric_cf/actor/db/__init__.py @@ -25,7 +25,7 @@ # Author: Komal Thareja (kthare10@renci.org) from sqlalchemy import JSON, ForeignKey, LargeBinary, TIMESTAMP, Index -from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import declarative_base from sqlalchemy import Column, String, Integer, Sequence from sqlalchemy.orm import relationship diff --git a/fabric_cf/orchestrator/swagger_server/controllers/slices_controller.py b/fabric_cf/orchestrator/swagger_server/controllers/slices_controller.py index 4d0f6c8e..5d30aa2f 100644 --- a/fabric_cf/orchestrator/swagger_server/controllers/slices_controller.py +++ b/fabric_cf/orchestrator/swagger_server/controllers/slices_controller.py @@ -86,18 +86,12 @@ def slices_delete_slice_id_delete(slice_id): # noqa: E501 return rc.slices_delete_slice_id_delete(slice_id) -def slices_get(name=None, search=None, exact_match=None, as_self=None, states=None, limit=None, offset=None): # noqa: E501 +def slices_get(name=None, as_self=None, states=None, limit=None, offset=None, search=None, exact_match=None): # noqa: E501 """Retrieve a listing of user slices Retrieve a listing of user slices. It returns list of all slices belonging to all members in a project when 'as_self' is False otherwise returns only the all user's slices in a project. # noqa: E501 - :param name: Search for Slices with the name - :type name: str - :param search: search term applied - :type search: str - :param exact_match: Exact Match for Search term - :type exact_match: bool :param as_self: GET object as Self :type as_self: bool :param states: Search for Slices in the specified states @@ -106,10 +100,17 @@ def slices_get(name=None, search=None, exact_match=None, as_self=None, states=No :type limit: int :param offset: number of items to skip before starting to collect the result set :type offset: int + :param name: Search for Slices with the name + :type name: str + :param search: search term applied + :type search: str + :param exact_match: Exact Match for Search term + :type exact_match: bool :rtype: Slices """ - return rc.slices_get(name, states, limit, offset, as_self=as_self, search=search, exact_match=exact_match) + return rc.slices_get(name=name, states=states, limit=limit, offset=offset, as_self=as_self, + search=search, exact_match=exact_match) def slices_modify_slice_id_accept_post(slice_id): # noqa: E501 diff --git a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml index a316b770..b0b6ea19 100644 --- a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml +++ b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml @@ -194,23 +194,6 @@ paths: schema: minLength: 3 type: string - - name: search - in: query - description: search term applied - required: false - style: form - explode: true - schema: - type: string - - name: exact_match - in: query - description: Exact Match for Search term - required: false - style: form - explode: true - schema: - type: boolean - default: false - name: as_self in: query description: GET object as Self @@ -265,6 +248,23 @@ paths: type: integer format: int32 default: 0 + - name: search + in: query + description: search term applied + required: false + style: form + explode: true + schema: + type: string + - name: exact_match + in: query + description: Exact Match for Search term + required: false + style: form + explode: true + schema: + type: boolean + default: false responses: "200": description: OK diff --git a/fabric_cf/orchestrator/swagger_server/test/test_poas_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_poas_controller.py index 291c37d0..29d62e60 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_poas_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_poas_controller.py @@ -25,7 +25,7 @@ def test_poas_create_sliver_id_post(self): """ body = PoaPost() response = self.client.open( - '//poas/create/{sliver_id}'.format(sliver_id='sliver_id_example'), + '/poas/create/{sliver_id}'.format(sliver_id='sliver_id_example'), method='POST', data=json.dumps(body), content_type='application/json') @@ -42,7 +42,7 @@ def test_poas_get(self): ('limit', 200), ('offset', 1)] response = self.client.open( - '//poas/', + '/poas/', method='GET', query_string=query_string) self.assert200(response, @@ -54,7 +54,7 @@ def test_poas_poa_id_get(self): Perform an operational action on a sliver. """ response = self.client.open( - '//poas/{poa_id}'.format(poa_id='poa_id_example'), + '/poas/{poa_id}'.format(poa_id='poa_id_example'), method='GET') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) diff --git a/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py index 9e33f8e1..67f3050c 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py @@ -24,7 +24,7 @@ def test_portalresources_get(self): """ query_string = [('graph_format', 'GRAPHML')] response = self.client.open( - '//portalresources', + '/portalresources', method='GET', query_string=query_string) self.assert200(response, @@ -38,7 +38,7 @@ def test_resources_get(self): query_string = [('level', 1), ('force_refresh', false)] response = self.client.open( - '//resources', + '/resources', method='GET', query_string=query_string) self.assert200(response, diff --git a/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py index daf8f5c3..a735ab48 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py @@ -20,6 +20,7 @@ class TestSlicesController(BaseTestCase): """SlicesController integration test stubs""" + TOKEN = "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImI0MTUxNjcyMTExOTFlMmUwNWIyMmI1NGIxZDNiNzY2N2U3NjRhNzQ3NzIyMTg1ZTcyMmU1MmUxNDZmZTQzYWEiLCJ0eXAiOiJKV1QifQ.eyJhY3IiOiJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZFByb3RlY3RlZFRyYW5zcG9ydCIsImVtYWlsIjoia3RoYXJlMTBAZW1haWwudW5jLmVkdSIsImdpdmVuX25hbWUiOiJLb21hbCIsImZhbWlseV9uYW1lIjoiVGhhcmVqYSIsIm5hbWUiOiJLb21hbCBUaGFyZWphIiwiaXNzIjoiaHR0cHM6Ly9jaWxvZ29uLm9yZyIsInN1YiI6Imh0dHA6Ly9jaWxvZ29uLm9yZy9zZXJ2ZXJBL3VzZXJzLzExOTA0MTAxIiwiYXVkIjoiY2lsb2dvbjovY2xpZW50X2lkLzYxN2NlY2RkNzRlMzJiZTRkODE4Y2ExMTUxNTMxZGZmIiwianRpIjoiaHR0cHM6Ly9jaWxvZ29uLm9yZy9vYXV0aDIvaWRUb2tlbi8yNjg0N2EzODcwN2Y5YTk4Y2RmYjEwYjY5OThiNzBkLzE2ODkyNjkwNjU3OTIiLCJhdXRoX3RpbWUiOjE2ODkyNjc4MzgsImV4cCI6MTY4OTI4MzQ2OSwiaWF0IjoxNjg5MjY5MDY5LCJwcm9qZWN0cyI6W3sibmFtZSI6IkNGIFRlc3QiLCJ1dWlkIjoiMTBjMDA5NGEtYWJhZi00ZWY5LWE1MzItMmJlNTNlMmE4OTZiIiwidGFncyI6WyJDb21wb25lbnQuR1BVIiwiQ29tcG9uZW50LlN0b3JhZ2UiXSwibWVtYmVyc2hpcHMiOnsiaXNfY3JlYXRvciI6dHJ1ZSwiaXNfbWVtYmVyIjp0cnVlLCJpc19vd25lciI6dHJ1ZX19XSwicm9sZXMiOlt7ImRlc2NyaXB0aW9uIjoiQ0YgVGVzdCBhbmQgdGVzdCBhbmQgdGVzdCIsIm5hbWUiOiIxMGMwMDk0YS1hYmFmLTRlZjktYTUzMi0yYmU1M2UyYTg5NmItcGMifSx7ImRlc2NyaXB0aW9uIjoiQ0YgVGVzdCBhbmQgdGVzdCBhbmQgdGVzdCIsIm5hbWUiOiIxMGMwMDk0YS1hYmFmLTRlZjktYTUzMi0yYmU1M2UyYTg5NmItcG0ifSx7ImRlc2NyaXB0aW9uIjoiQ0YgVGVzdCBhbmQgdGVzdCBhbmQgdGVzdCIsIm5hbWUiOiIxMGMwMDk0YS1hYmFmLTRlZjktYTUzMi0yYmU1M2UyYTg5NmItcG8ifSx7ImRlc2NyaXB0aW9uIjoiRkFCUklDIFByb2plY3QiLCJuYW1lIjoiOGIzYTJlYWUtYTBjMC00NzVhLTgwN2ItZTlhZjU4MWNlNGMwLXBtIn0seyJkZXNjcmlwdGlvbiI6IlByb2plY3QtVGFncyIsIm5hbWUiOiJiOGQ2NmZiMy1lN2FkLTRkZjktYTY1Mi0yZDhlMzIzMjM2ZTUtcGMifSx7ImRlc2NyaXB0aW9uIjoiUHJvamVjdC1UYWdzIiwibmFtZSI6ImI4ZDY2ZmIzLWU3YWQtNGRmOS1hNjUyLTJkOGUzMjMyMzZlNS1wbyJ9LHsiZGVzY3JpcHRpb24iOiJBY3RpdmUgVXNlcnMgb2YgRkFCUklDIC0gaW5pdGlhbGx5IHNldCBieSBlbnJvbGxtZW50IHdvcmtmbG93IiwibmFtZSI6ImZhYnJpYy1hY3RpdmUtdXNlcnMifSx7ImRlc2NyaXB0aW9uIjoiRmFjaWxpdHkgT3BlcmF0b3JzIGZvciBGQUJSSUMiLCJuYW1lIjoiZmFjaWxpdHktb3BlcmF0b3JzIn0seyJkZXNjcmlwdGlvbiI6IiBKdXB5dGVyaHViIGFjY2VzcyAtIGJhc2VkIG9uIHByb2plY3QgcGFydGljaXBhdGlvbiIsIm5hbWUiOiJKdXB5dGVyaHViIn0seyJkZXNjcmlwdGlvbiI6IlByb2plY3QgTGVhZHMgZm9yIEZBQlJJQyIsIm5hbWUiOiJwcm9qZWN0LWxlYWRzIn1dLCJzY29wZSI6ImFsbCIsInV1aWQiOiIyMjY1YTRkYS1lMWZjLTRmZmMtYmJiNy1kODZkNTY1NzViZmYifQ.OG_m48gCzMt8vcYv947cZduJSbEEzpMuBbbP59SgfD7q1q4_kMbBg36P_SmZMaRfZ6P65hfEdRSnX1x5i4OJQkE47sw1P0Uge6klcnfORIwUyMNphNoKgUcbLRZN9Kch6xKr6JPEfnXKnTdTUMS2-cKzmgLa5w2oIH8kipKisHey7PQhwfdZhwqR8hme5Q7Gwf4O3n45jA8HQQxaO38scIOy0NajQOMPvOkQvfzWOOv79mSztepN2jlJRMGWLqdBql5kSm_xtVRk3S3SdLuS_GTKyA4zfU1-ouT-NUlE9CM86mBHy4FYki5M-cnM3Us3RI7oAxal8kAGqB2I0LY-xMkhHY6k34FCCHiFWvH4WgYmWBEWO7U8uPUq1rbapX6NluQJaSxc54UEA33B9pC_Cgkod6UzB_7g_CNmS31wajjyulCEjIBtlBWTXdmblG8PEZA0_T8DrXh7NPPxF3XrRC74d-IqQ9WiTuH6qrOrMT3ivpeZqPFdriKOXB49EREmUV1iO-STWXRG1RDBLM-lGW7dWOfgsNmlEvW49A8ZO6-T7xVYyVbsVB0XF9oRKAr3uMt_N9ZgF_BP3CKzIi5bhvr4_0AyFifgbrkaKb4qIMOj0XD-Ds1cCDBASqiIj0peal9M8BlBDDMY_80MkRxH1i-XvLRYU5s9t3Oc3p1yd4U" def test_slices_create_post(self): """Test case for slices_create_post @@ -31,7 +32,7 @@ def test_slices_create_post(self): ('ssh_key', 'ssh_key_example'), ('lease_end_time', 'lease_end_time_example')] response = self.client.open( - '//slices/create', + '/slices/create', method='POST', data=json.dumps(body), content_type='text/plain', @@ -48,7 +49,7 @@ def test_slices_creates_post(self): query_string = [('name', 'name_example'), ('lease_end_time', 'lease_end_time_example')] response = self.client.open( - '//slices/creates', + '/slices/creates', method='POST', data=json.dumps(body), content_type='application/json', @@ -62,7 +63,7 @@ def test_slices_delete_delete(self): Delete all slices for a User within a project. """ response = self.client.open( - '//slices/delete', + '/slices/delete', method='DELETE') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -73,8 +74,8 @@ def test_slices_delete_slice_id_delete(self): Delete slice. """ response = self.client.open( - '//slices/delete/{slice_id}'.format(slice_id='slice_id_example'), - method='DELETE') + '/slices/delete/{slice_id}'.format(slice_id='slice_id_example'), + method='DELETE', ) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -84,16 +85,18 @@ def test_slices_get(self): Retrieve a listing of user slices """ query_string = [('name', 'name_example'), - ('search', 'search_example'), - ('exact_match', false), - ('as_self', true), - ('states', 'states_example'), + ('as_self', True), + #('states', 'states_example'), ('limit', 200), - ('offset', 1)] + ('offset', 1), + # ('search', 'search_example'), + # ('exact_match', False), + ] response = self.client.open( - '//slices', + '/slices', method='GET', - query_string=query_string) + query_string=query_string, headers={'AUTHORIZATION': self.TOKEN} + ) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -103,7 +106,7 @@ def test_slices_modify_slice_id_accept_post(self): Accept the last modify an existing slice """ response = self.client.open( - '//slices/modify/{slice_id}/accept'.format(slice_id='slice_id_example'), + '/slices/modify/{slice_id}/accept'.format(slice_id='slice_id_example'), method='POST') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -115,7 +118,7 @@ def test_slices_modify_slice_id_put(self): """ body = 'body_example' response = self.client.open( - '//slices/modify/{slice_id}'.format(slice_id='slice_id_example'), + '/slices/modify/{slice_id}'.format(slice_id='slice_id_example'), method='PUT', data=json.dumps(body), content_type='text/plain') @@ -129,7 +132,7 @@ def test_slices_renew_slice_id_post(self): """ query_string = [('lease_end_time', 'lease_end_time_example')] response = self.client.open( - '//slices/renew/{slice_id}'.format(slice_id='slice_id_example'), + '/slices/renew/{slice_id}'.format(slice_id='slice_id_example'), method='POST', query_string=query_string) self.assert200(response, @@ -140,10 +143,10 @@ def test_slices_slice_id_get(self): slice properties """ - query_string = [('as_self', true), + query_string = [('as_self', True), ('graph_format', 'GRAPHML')] response = self.client.open( - '//slices/{slice_id}'.format(slice_id='slice_id_example'), + '/slices/{slice_id}'.format(slice_id='slice_id_example'), method='GET', query_string=query_string) self.assert200(response, diff --git a/fabric_cf/orchestrator/swagger_server/test/test_slivers_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_slivers_controller.py index ee1b58cb..24659957 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_slivers_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_slivers_controller.py @@ -23,9 +23,9 @@ def test_slivers_get(self): Retrieve a listing of user slivers """ query_string = [('slice_id', 'slice_id_example'), - ('as_self', true)] + ('as_self', True)] response = self.client.open( - '//slivers', + '/slivers', method='GET', query_string=query_string) self.assert200(response, @@ -37,9 +37,9 @@ def test_slivers_sliver_id_get(self): slivers properties """ query_string = [('slice_id', 'slice_id_example'), - ('as_self', true)] + ('as_self', True)] response = self.client.open( - '//slivers/{sliver_id}'.format(sliver_id='sliver_id_example'), + '/slivers/{sliver_id}'.format(sliver_id='sliver_id_example'), method='GET', query_string=query_string) self.assert200(response, diff --git a/fabric_cf/orchestrator/swagger_server/test/test_version_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_version_controller.py index ad073fe7..29bfa789 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_version_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_version_controller.py @@ -19,8 +19,9 @@ def test_version_get(self): Version """ response = self.client.open( - '//version', + '/version', method='GET') + print(response.data) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) From fad13fcee59a98937cfcf07f4aab1d7320ac9452 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 2 Apr 2024 16:19:38 -0400 Subject: [PATCH 003/133] user data update to not change slice state --- .../orchestrator/core/orchestrator_handler.py | 49 ++++++++++++------- .../core/orchestrator_slice_wrapper.py | 4 ++ 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index 865b4927..0b27fb6a 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -486,20 +486,25 @@ def modify_slice(self, *, token: str, slice_id: str, slice_graph: str) -> List[d FimHelper.delete_graph(graph_id=slice_obj.get_graph_id()) - slice_obj.graph_id = asm_graph.get_graph_id() - config_props = slice_obj.get_config_properties() - config_props[Constants.PROJECT_ID] = project - config_props[Constants.TAGS] = ','.join(tags) - config_props[Constants.TOKEN_HASH] = fabric_token.token_hash - slice_obj.set_config_properties(value=config_props) - - if not controller.update_slice(slice_obj=slice_obj, modify_state=True): - self.logger.error(f"Failed to update slice: {slice_id} error: {controller.get_last_error()}") - - # Enqueue the slice on the demand thread - # Demand thread is responsible for demanding the reservations - # Helps improve the create response time - self.controller_state.get_defer_thread().queue_slice(controller_slice=slice_object) + # Slice has sliver modifications - add/remove/update for slivers requiring AM updates + if slice_object.has_sliver_updates_at_authority(): + slice_obj.graph_id = asm_graph.get_graph_id() + config_props = slice_obj.get_config_properties() + config_props[Constants.PROJECT_ID] = project + config_props[Constants.TAGS] = ','.join(tags) + config_props[Constants.TOKEN_HASH] = fabric_token.token_hash + slice_obj.set_config_properties(value=config_props) + + if not controller.update_slice(slice_obj=slice_obj, modify_state=True): + self.logger.error(f"Failed to update slice: {slice_id} error: {controller.get_last_error()}") + + # Enqueue the slice on the demand thread + # Demand thread is responsible for demanding the reservations + # Helps improve the create response time + self.controller_state.get_defer_thread().queue_slice(controller_slice=slice_object) + # Sliver has meta data update + else: + self.logger.debug("Slice only has UserData updates") EventLoggerSingleton.get().log_slice_event(slice_object=slice_obj, action=ActionId.modify, topology=topology) @@ -596,16 +601,22 @@ def modify_accept(self, *, token: str, slice_id: str) -> dict: slice_obj = next(iter(slice_list)) slice_state = SliceState(slice_obj.get_state()) - if not SliceState.is_modified(state=slice_state): - self.logger.info(f"Unable to accept modify Slice# {slice_guid} that was not modified") - raise OrchestratorException(f"Unable to accept modify Slice# {slice_guid} that was not modified") + # Do not throw error if modify accept is received for a stable slice + # Just return the success with slice topology + #if not SliceState.is_modified(state=slice_state): + # self.logger.info(f"Unable to accept modify Slice# {slice_guid} that was not modified") + # raise OrchestratorException(f"Unable to accept modify Slice# {slice_guid} that was not modified") if slice_obj.get_graph_id() is None: raise OrchestratorException(f"Slice# {slice_obj} does not have graph id") - slice_topology = FimHelper.prune_graph(graph_id=slice_obj.get_graph_id()) + if not SliceState.is_modified(state=slice_state): + slice_topology = FimHelper.get_graph(graph_id=slice_obj.get_graph_id()) + # Prune the slice topology only if slice was modified + else: + slice_topology = FimHelper.prune_graph(graph_id=slice_obj.get_graph_id()) - controller.accept_update_slice(slice_id=ID(uid=slice_id)) + controller.accept_update_slice(slice_id=ID(uid=slice_id)) slice_model_str = slice_topology.serialize() return ResponseBuilder.get_slice_summary(slice_list=slice_list, slice_model=slice_model_str)[0] diff --git a/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py b/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py index 7c1dba20..978950ce 100644 --- a/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py +++ b/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py @@ -700,3 +700,7 @@ def update_topology(self, *, topology: ExperimentTopology): management_ip=sliver.management_ip, capacity_hints=sliver.capacity_hints, capacities=sliver.capacities) + + def has_sliver_updates_at_authority(self): + return len(self.computed_reservations) and len(self.computed_remove_reservations) or \ + len(self.computed_modify_reservations) or len(self.computed_modify_properties_reservations) From d7afb34d5fe0ff70ed3b79aced9c178859327da5 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 2 Apr 2024 16:30:05 -0400 Subject: [PATCH 004/133] update slice graph without changing state for only user data updates --- .../orchestrator/core/orchestrator_handler.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index 0b27fb6a..9cb7959b 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -487,17 +487,19 @@ def modify_slice(self, *, token: str, slice_id: str, slice_graph: str) -> List[d FimHelper.delete_graph(graph_id=slice_obj.get_graph_id()) # Slice has sliver modifications - add/remove/update for slivers requiring AM updates - if slice_object.has_sliver_updates_at_authority(): - slice_obj.graph_id = asm_graph.get_graph_id() - config_props = slice_obj.get_config_properties() - config_props[Constants.PROJECT_ID] = project - config_props[Constants.TAGS] = ','.join(tags) - config_props[Constants.TOKEN_HASH] = fabric_token.token_hash - slice_obj.set_config_properties(value=config_props) + modify_state = slice_object.has_sliver_updates_at_authority() - if not controller.update_slice(slice_obj=slice_obj, modify_state=True): - self.logger.error(f"Failed to update slice: {slice_id} error: {controller.get_last_error()}") + slice_obj.graph_id = asm_graph.get_graph_id() + config_props = slice_obj.get_config_properties() + config_props[Constants.PROJECT_ID] = project + config_props[Constants.TAGS] = ','.join(tags) + config_props[Constants.TOKEN_HASH] = fabric_token.token_hash + slice_obj.set_config_properties(value=config_props) + + if not controller.update_slice(slice_obj=slice_obj, modify_state=modify_state): + self.logger.error(f"Failed to update slice: {slice_id} error: {controller.get_last_error()}") + if modify_state: # Enqueue the slice on the demand thread # Demand thread is responsible for demanding the reservations # Helps improve the create response time From 78d18db052de624bac1a03d2c8315141bb799f98 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 5 Apr 2024 15:56:16 -0400 Subject: [PATCH 005/133] swagger updates for time in resources api --- fabric_cf/orchestrator/openapi.json | 62 ++++++++++++++++++- .../controllers/resources_controller.py | 12 ++-- .../swagger_server/models/slice.py | 58 ++++++++++++++++- .../swagger_server/models/sliver.py | 58 ++++++++++++++++- .../swagger_server/swagger/swagger.yaml | 44 +++++++++++++ .../test/test_resources_controller.py | 4 +- .../test/test_slices_controller.py | 2 + 7 files changed, 227 insertions(+), 13 deletions(-) diff --git a/fabric_cf/orchestrator/openapi.json b/fabric_cf/orchestrator/openapi.json index 13357fdf..47e9df31 100644 --- a/fabric_cf/orchestrator/openapi.json +++ b/fabric_cf/orchestrator/openapi.json @@ -106,6 +106,30 @@ "type": "boolean", "default": false } + }, + { + "name": "start_date", + "in": "query", + "description": "starting date to check availability from", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "string", + "example": "2023-01-01 16:20:15 +00:00" + } + }, + { + "name": "end_date", + "in": "query", + "description": "end date to check availability until", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "string", + "example": "2023-01-01 16:20:15 +00:00" + } } ], "responses": { @@ -287,6 +311,29 @@ "type": "string" } }, + { + "name": "search", + "in": "query", + "description": "search term applied", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "string" + } + }, + { + "name": "exact_match", + "in": "query", + "description": "Exact Match for Search term", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "boolean", + "default": false + } + }, { "name": "as_self", "in": "query", @@ -685,7 +732,8 @@ "style": "form", "explode": true, "schema": { - "type": "string" + "type": "string", + "example": "2023-01-01 16:20:15 +00:00" } } ], @@ -2113,6 +2161,12 @@ }, "slice_id": { "type": "string" + }, + "owner_user_id": { + "type": "string" + }, + "owner_email": { + "type": "string" } } }, @@ -2175,6 +2229,12 @@ }, "sliver_id": { "type": "string" + }, + "owner_user_id": { + "type": "string" + }, + "owner_email": { + "type": "string" } } }, diff --git a/fabric_cf/orchestrator/swagger_server/controllers/resources_controller.py b/fabric_cf/orchestrator/swagger_server/controllers/resources_controller.py index 33c1b53e..19bb4644 100644 --- a/fabric_cf/orchestrator/swagger_server/controllers/resources_controller.py +++ b/fabric_cf/orchestrator/swagger_server/controllers/resources_controller.py @@ -15,17 +15,19 @@ def portalresources_get(graph_format): # noqa: E501 return rc.portalresources_get(graph_format) -def resources_get(level, force_refresh): # noqa: E501 - """Retrieve a listing and description of available resources. By default, a cached available resource information - is returned. User can force to request the current available resources. +def resources_get(level, force_refresh, start_date=None, end_date=None): # noqa: E501 + """Retrieve a listing and description of available resources. By default, a cached available resource information is returned. User can force to request the current available resources. - Retrieve a listing and description of available resources. By default, a cached available resource information is - returned. User can force to request the current available resources. # noqa: E501 + Retrieve a listing and description of available resources. By default, a cached available resource information is returned. User can force to request the current available resources. # noqa: E501 :param level: Level of details :type level: int :param force_refresh: Force to retrieve current available resource information. :type force_refresh: bool + :param start_date: starting date to check availability from + :type start_date: str + :param end_date: end date to check availability until + :type end_date: str :rtype: Resources """ diff --git a/fabric_cf/orchestrator/swagger_server/models/slice.py b/fabric_cf/orchestrator/swagger_server/models/slice.py index aaea0b63..baeb3479 100644 --- a/fabric_cf/orchestrator/swagger_server/models/slice.py +++ b/fabric_cf/orchestrator/swagger_server/models/slice.py @@ -14,7 +14,7 @@ class Slice(Model): Do not edit the class manually. """ - def __init__(self, model: str=None, lease_start_time: str=None, lease_end_time: str=None, state: str=None, project_id: str=None, project_name: str=None, graph_id: str=None, name: str=None, slice_id: str=None): # noqa: E501 + def __init__(self, model: str=None, lease_start_time: str=None, lease_end_time: str=None, state: str=None, project_id: str=None, project_name: str=None, graph_id: str=None, name: str=None, slice_id: str=None, owner_user_id: str=None, owner_email: str=None): # noqa: E501 """Slice - a model defined in Swagger :param model: The model of this Slice. # noqa: E501 @@ -35,6 +35,10 @@ def __init__(self, model: str=None, lease_start_time: str=None, lease_end_time: :type name: str :param slice_id: The slice_id of this Slice. # noqa: E501 :type slice_id: str + :param owner_user_id: The owner_user_id of this Slice. # noqa: E501 + :type owner_user_id: str + :param owner_email: The owner_email of this Slice. # noqa: E501 + :type owner_email: str """ self.swagger_types = { 'model': str, @@ -45,7 +49,9 @@ def __init__(self, model: str=None, lease_start_time: str=None, lease_end_time: 'project_name': str, 'graph_id': str, 'name': str, - 'slice_id': str + 'slice_id': str, + 'owner_user_id': str, + 'owner_email': str } self.attribute_map = { @@ -57,7 +63,9 @@ def __init__(self, model: str=None, lease_start_time: str=None, lease_end_time: 'project_name': 'project_name', 'graph_id': 'graph_id', 'name': 'name', - 'slice_id': 'slice_id' + 'slice_id': 'slice_id', + 'owner_user_id': 'owner_user_id', + 'owner_email': 'owner_email' } self._model = model self._lease_start_time = lease_start_time @@ -68,6 +76,8 @@ def __init__(self, model: str=None, lease_start_time: str=None, lease_end_time: self._graph_id = graph_id self._name = name self._slice_id = slice_id + self._owner_user_id = owner_user_id + self._owner_email = owner_email @classmethod def from_dict(cls, dikt) -> 'Slice': @@ -274,3 +284,45 @@ def slice_id(self, slice_id: str): raise ValueError("Invalid value for `slice_id`, must not be `None`") # noqa: E501 self._slice_id = slice_id + + @property + def owner_user_id(self) -> str: + """Gets the owner_user_id of this Slice. + + + :return: The owner_user_id of this Slice. + :rtype: str + """ + return self._owner_user_id + + @owner_user_id.setter + def owner_user_id(self, owner_user_id: str): + """Sets the owner_user_id of this Slice. + + + :param owner_user_id: The owner_user_id of this Slice. + :type owner_user_id: str + """ + + self._owner_user_id = owner_user_id + + @property + def owner_email(self) -> str: + """Gets the owner_email of this Slice. + + + :return: The owner_email of this Slice. + :rtype: str + """ + return self._owner_email + + @owner_email.setter + def owner_email(self, owner_email: str): + """Sets the owner_email of this Slice. + + + :param owner_email: The owner_email of this Slice. + :type owner_email: str + """ + + self._owner_email = owner_email diff --git a/fabric_cf/orchestrator/swagger_server/models/sliver.py b/fabric_cf/orchestrator/swagger_server/models/sliver.py index d2dbd079..de11dce9 100644 --- a/fabric_cf/orchestrator/swagger_server/models/sliver.py +++ b/fabric_cf/orchestrator/swagger_server/models/sliver.py @@ -14,7 +14,7 @@ class Sliver(Model): Do not edit the class manually. """ - def __init__(self, notice: str=None, sliver_type: str=None, sliver: object=None, lease_start_time: str=None, lease_end_time: str=None, state: str=None, pending_state: str=None, join_state: str=None, graph_node_id: str=None, slice_id: str=None, sliver_id: str=None): # noqa: E501 + def __init__(self, notice: str=None, sliver_type: str=None, sliver: object=None, lease_start_time: str=None, lease_end_time: str=None, state: str=None, pending_state: str=None, join_state: str=None, graph_node_id: str=None, slice_id: str=None, sliver_id: str=None, owner_user_id: str=None, owner_email: str=None): # noqa: E501 """Sliver - a model defined in Swagger :param notice: The notice of this Sliver. # noqa: E501 @@ -39,6 +39,10 @@ def __init__(self, notice: str=None, sliver_type: str=None, sliver: object=None, :type slice_id: str :param sliver_id: The sliver_id of this Sliver. # noqa: E501 :type sliver_id: str + :param owner_user_id: The owner_user_id of this Sliver. # noqa: E501 + :type owner_user_id: str + :param owner_email: The owner_email of this Sliver. # noqa: E501 + :type owner_email: str """ self.swagger_types = { 'notice': str, @@ -51,7 +55,9 @@ def __init__(self, notice: str=None, sliver_type: str=None, sliver: object=None, 'join_state': str, 'graph_node_id': str, 'slice_id': str, - 'sliver_id': str + 'sliver_id': str, + 'owner_user_id': str, + 'owner_email': str } self.attribute_map = { @@ -65,7 +71,9 @@ def __init__(self, notice: str=None, sliver_type: str=None, sliver: object=None, 'join_state': 'join_state', 'graph_node_id': 'graph_node_id', 'slice_id': 'slice_id', - 'sliver_id': 'sliver_id' + 'sliver_id': 'sliver_id', + 'owner_user_id': 'owner_user_id', + 'owner_email': 'owner_email' } self._notice = notice self._sliver_type = sliver_type @@ -78,6 +86,8 @@ def __init__(self, notice: str=None, sliver_type: str=None, sliver: object=None, self._graph_node_id = graph_node_id self._slice_id = slice_id self._sliver_id = sliver_id + self._owner_user_id = owner_user_id + self._owner_email = owner_email @classmethod def from_dict(cls, dikt) -> 'Sliver': @@ -326,3 +336,45 @@ def sliver_id(self, sliver_id: str): raise ValueError("Invalid value for `sliver_id`, must not be `None`") # noqa: E501 self._sliver_id = sliver_id + + @property + def owner_user_id(self) -> str: + """Gets the owner_user_id of this Sliver. + + + :return: The owner_user_id of this Sliver. + :rtype: str + """ + return self._owner_user_id + + @owner_user_id.setter + def owner_user_id(self, owner_user_id: str): + """Sets the owner_user_id of this Sliver. + + + :param owner_user_id: The owner_user_id of this Sliver. + :type owner_user_id: str + """ + + self._owner_user_id = owner_user_id + + @property + def owner_email(self) -> str: + """Gets the owner_email of this Sliver. + + + :return: The owner_email of this Sliver. + :rtype: str + """ + return self._owner_email + + @owner_email.setter + def owner_email(self, owner_email: str): + """Sets the owner_email of this Sliver. + + + :param owner_email: The owner_email of this Sliver. + :type owner_email: str + """ + + self._owner_email = owner_email diff --git a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml index 89bfbccd..e496f34c 100644 --- a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml +++ b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml @@ -76,6 +76,24 @@ paths: schema: type: boolean default: false + - name: start_date + in: query + description: starting date to check availability from + required: false + style: form + explode: true + schema: + type: string + example: 2023-01-01 16:20:15 +00:00 + - name: end_date + in: query + description: end date to check availability until + required: false + style: form + explode: true + schema: + type: string + example: 2023-01-01 16:20:15 +00:00 responses: "200": description: OK @@ -194,6 +212,23 @@ paths: schema: minLength: 3 type: string + - name: search + in: query + description: search term applied + required: false + style: form + explode: true + schema: + type: string + - name: exact_match + in: query + description: Exact Match for Search term + required: false + style: form + explode: true + schema: + type: boolean + default: false - name: as_self in: query description: GET object as Self @@ -482,6 +517,7 @@ paths: explode: true schema: type: string + example: 2023-01-01 16:20:15 +00:00 requestBody: $ref: '#/components/requestBodies/payload_slices_create' responses: @@ -1433,6 +1469,10 @@ components: type: string slice_id: type: string + owner_user_id: + type: string + owner_email: + type: string slivers: type: object allOf: @@ -1472,6 +1512,10 @@ components: type: string sliver_id: type: string + owner_user_id: + type: string + owner_email: + type: string version: type: object allOf: diff --git a/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py index 9e33f8e1..814081ab 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py @@ -36,7 +36,9 @@ def test_resources_get(self): Retrieve a listing and description of available resources. By default, a cached available resource information is returned. User can force to request the current available resources. """ query_string = [('level', 1), - ('force_refresh', false)] + ('force_refresh', false), + ('start_date', 'start_date_example'), + ('end_date', 'end_date_example')] response = self.client.open( '//resources', method='GET', diff --git a/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py index 80febabc..daf8f5c3 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py @@ -84,6 +84,8 @@ def test_slices_get(self): Retrieve a listing of user slices """ query_string = [('name', 'name_example'), + ('search', 'search_example'), + ('exact_match', false), ('as_self', true), ('states', 'states_example'), ('limit', 200), From c3152e0f5f0194ca5a0a4acf869e9d0212672717 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 5 Apr 2024 18:39:28 -0400 Subject: [PATCH 006/133] initial support for time based list resources --- .../abc_client_actor_management_object.py | 5 +- fabric_cf/actor/core/apis/abc_database.py | 6 +- .../actor/core/apis/abc_mgmt_client_actor.py | 6 +- fabric_cf/actor/core/common/constants.py | 2 + fabric_cf/actor/core/core/broker_policy.py | 10 ++- .../core/manage/broker_management_object.py | 5 +- .../client_actor_management_object_helper.py | 6 +- .../manage/controller_management_object.py | 5 +- .../actor/core/manage/kafka/kafka_broker.py | 18 ++--- .../core/manage/kafka/kafka_controller.py | 4 +- .../actor/core/manage/local/local_broker.py | 4 +- .../core/manage/local/local_controller.py | 4 +- .../actor/core/plugins/db/actor_database.py | 8 +- .../policy/broker_simpler_units_policy.py | 5 +- fabric_cf/actor/db/psql_database.py | 8 +- .../plugins/broker/aggregate_bqm_plugin.py | 16 ++-- .../orchestrator/core/orchestrator_handler.py | 80 ++++++++++++------- .../controllers/resources_controller.py | 2 +- .../response/resources_controller.py | 12 ++- 19 files changed, 134 insertions(+), 72 deletions(-) diff --git a/fabric_cf/actor/core/apis/abc_client_actor_management_object.py b/fabric_cf/actor/core/apis/abc_client_actor_management_object.py index 79b03420..a3d95efd 100644 --- a/fabric_cf/actor/core/apis/abc_client_actor_management_object.py +++ b/fabric_cf/actor/core/apis/abc_client_actor_management_object.py @@ -128,7 +128,8 @@ def update_broker(self, *, broker: ProxyAvro, caller: AuthToken) -> ResultAvro: @abstractmethod def get_broker_query_model(self, *, broker: ID, caller: AuthToken, id_token: str, - level: int, graph_format: GraphFormat) -> ResultBrokerQueryModelAvro: + level: int, graph_format: GraphFormat, start: datetime = None, + end: datetime = None) -> ResultBrokerQueryModelAvro: """ Get Pool Info @param broker : broker ID @@ -136,6 +137,8 @@ def get_broker_query_model(self, *, broker: ID, caller: AuthToken, id_token: str @param id_token: str @param level: level of details @param graph_format: Graph Format + @param start: start time + @param end: end time @return pool information """ diff --git a/fabric_cf/actor/core/apis/abc_database.py b/fabric_cf/actor/core/apis/abc_database.py index 452f19c0..9be362c0 100644 --- a/fabric_cf/actor/core/apis/abc_database.py +++ b/fabric_cf/actor/core/apis/abc_database.py @@ -159,9 +159,9 @@ def update_slice(self, *, slice_object: ABCSlice): @abstractmethod def get_reservations(self, *, slice_id: ID = None, graph_node_id: str = None, project_id: str = None, - email: str = None, oidc_sub: str = None, rid: ID = None, - states: list[int] = None, site: str = None, - rsv_type: list[str] = None) -> List[ABCReservationMixin]: + email: str = None, oidc_sub: str = None, rid: ID = None, states: list[int] = None, + site: str = None, rsv_type: list[str] = None, start: datetime = None, + end: datetime = None) -> List[ABCReservationMixin]: """ Retrieves the reservations. diff --git a/fabric_cf/actor/core/apis/abc_mgmt_client_actor.py b/fabric_cf/actor/core/apis/abc_mgmt_client_actor.py index c4197a29..fef45485 100644 --- a/fabric_cf/actor/core/apis/abc_mgmt_client_actor.py +++ b/fabric_cf/actor/core/apis/abc_mgmt_client_actor.py @@ -116,14 +116,16 @@ def update_broker(self, *, broker: ProxyAvro) -> bool: """ @abstractmethod - def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, - graph_format: GraphFormat) -> BrokerQueryModelAvro: + def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, graph_format: GraphFormat, + start: datetime = None, end: datetime = None) -> BrokerQueryModelAvro: """ Obtains the resources available at the specified broker @param broker broker @param id_token identity token generated by Credmgr @param level: level of details @param graph_format: Graph Format + @param start: start time + @param end: end time @return BQM """ diff --git a/fabric_cf/actor/core/common/constants.py b/fabric_cf/actor/core/common/constants.py index 1eb3cd25..7acc91bb 100644 --- a/fabric_cf/actor/core/common/constants.py +++ b/fabric_cf/actor/core/common/constants.py @@ -210,6 +210,8 @@ class Constants: QUERY_DETAIL_LEVEL = "query.detail.level" BROKER_QUERY_MODEL = "bqm" BROKER_QUERY_MODEL_FORMAT = "bqm.format" + START = "start" + END = "end" POOL_TYPE = "neo4j" UNIT_MODIFY_PROP_MESSAGE_SUFFIX = ".message" diff --git a/fabric_cf/actor/core/core/broker_policy.py b/fabric_cf/actor/core/core/broker_policy.py index 7a5dd5d6..3b8b0723 100644 --- a/fabric_cf/actor/core/core/broker_policy.py +++ b/fabric_cf/actor/core/core/broker_policy.py @@ -25,6 +25,7 @@ # Author: Komal Thareja (kthare10@renci.org) from __future__ import annotations +from datetime import datetime from typing import TYPE_CHECKING from fim.graph.abc_property_graph import GraphFormat @@ -127,16 +128,23 @@ def get_client_id(self, *, reservation: ABCServerReservation) -> ID: return reservation.get_client_auth_token().get_guid() @staticmethod - def get_broker_query_model_query(*, level: int, bqm_format: GraphFormat = GraphFormat.GRAPHML) -> dict: + def get_broker_query_model_query(*, level: int, bqm_format: GraphFormat = GraphFormat.GRAPHML, + start: datetime = None, end: datetime = None) -> dict: """ Return dictionary representing query :param level: Graph Level :param bqm_format: Graph Format + :param start: start time + :param end: end time :return dictionary representing the query """ properties = {Constants.QUERY_ACTION: Constants.QUERY_ACTION_DISCOVER_BQM, Constants.QUERY_DETAIL_LEVEL: str(level), Constants.BROKER_QUERY_MODEL_FORMAT: str(bqm_format.value)} + if start: + properties[Constants.START] = start + if end: + properties[Constants.END] = end return properties @staticmethod diff --git a/fabric_cf/actor/core/manage/broker_management_object.py b/fabric_cf/actor/core/manage/broker_management_object.py index be4c00e3..12223e2b 100644 --- a/fabric_cf/actor/core/manage/broker_management_object.py +++ b/fabric_cf/actor/core/manage/broker_management_object.py @@ -98,9 +98,10 @@ def update_broker(self, *, broker: ProxyAvro, caller: AuthToken) -> ResultAvro: return self.client_helper.update_broker(broker=broker, caller=caller) def get_broker_query_model(self, *, broker: ID, caller: AuthToken, id_token: str, - level: int, graph_format: GraphFormat) -> ResultBrokerQueryModelAvro: + level: int, graph_format: GraphFormat, start: datetime = None, + end: datetime = None) -> ResultBrokerQueryModelAvro: return self.client_helper.get_broker_query_model(broker=broker, caller=caller, id_token=id_token, level=level, - graph_format=graph_format) + graph_format=graph_format, start=start, end=end) def add_reservation(self, *, reservation: TicketReservationAvro, caller: AuthToken) -> ResultStringAvro: return self.client_helper.add_reservation(reservation=reservation, caller=caller) diff --git a/fabric_cf/actor/core/manage/client_actor_management_object_helper.py b/fabric_cf/actor/core/manage/client_actor_management_object_helper.py index 9d38573c..a60c3d55 100644 --- a/fabric_cf/actor/core/manage/client_actor_management_object_helper.py +++ b/fabric_cf/actor/core/manage/client_actor_management_object_helper.py @@ -152,7 +152,8 @@ def update_broker(self, *, broker: ProxyAvro, caller: AuthToken) -> ResultAvro: return result def get_broker_query_model(self, *, broker: ID, caller: AuthToken, id_token: str, - level: int, graph_format: GraphFormat) -> ResultBrokerQueryModelAvro: + level: int, graph_format: GraphFormat, start: datetime = None, + end: datetime = None) -> ResultBrokerQueryModelAvro: result = ResultBrokerQueryModelAvro() result.status = ResultAvro() @@ -165,7 +166,8 @@ def get_broker_query_model(self, *, broker: ID, caller: AuthToken, id_token: str b = self.client.get_broker(guid=broker) if b is not None: - request = BrokerPolicy.get_broker_query_model_query(level=level, bqm_format=graph_format) + request = BrokerPolicy.get_broker_query_model_query(level=level, bqm_format=graph_format, + start=start, end=end) response = ManagementUtils.query(actor=self.client, actor_proxy=b, query=request) result.model = Translate.translate_to_broker_query_model(query_response=response, level=level) else: diff --git a/fabric_cf/actor/core/manage/controller_management_object.py b/fabric_cf/actor/core/manage/controller_management_object.py index 5d36c735..e6c7c3c5 100644 --- a/fabric_cf/actor/core/manage/controller_management_object.py +++ b/fabric_cf/actor/core/manage/controller_management_object.py @@ -102,9 +102,10 @@ def update_broker(self, *, broker: ProxyAvro, caller: AuthToken) -> ResultAvro: return self.client_helper.update_broker(broker=broker, caller=caller) def get_broker_query_model(self, *, broker: ID, caller: AuthToken, id_token: str, - level: int, graph_format: GraphFormat) -> ResultBrokerQueryModelAvro: + level: int, graph_format: GraphFormat, start: datetime = None, + end: datetime = None) -> ResultBrokerQueryModelAvro: return self.client_helper.get_broker_query_model(broker=broker, caller=caller, id_token=id_token, level=level, - graph_format=graph_format) + graph_format=graph_format, start=start, end=end) def add_reservation(self, *, reservation: TicketReservationAvro, caller: AuthToken) -> ResultStringAvro: return self.client_helper.add_reservation(reservation=reservation, caller=caller) diff --git a/fabric_cf/actor/core/manage/kafka/kafka_broker.py b/fabric_cf/actor/core/manage/kafka/kafka_broker.py index ca67d55d..efc2ef7e 100644 --- a/fabric_cf/actor/core/manage/kafka/kafka_broker.py +++ b/fabric_cf/actor/core/manage/kafka/kafka_broker.py @@ -79,8 +79,6 @@ def add_reservations(self, *, reservations: list) -> List[TicketReservationAvro] if status.code == 0: return response.result - return None - def demand_reservation(self, *, reservation: ReservationMng) -> bool: request = DemandReservationAvro() request.guid = str(self.management_id) @@ -110,10 +108,9 @@ def get_brokers(self, *, broker: ID = None, id_token: str = None) -> List[ProxyA if status.code == 0: return response.proxies - return None - def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, - graph_format: GraphFormat) -> BrokerQueryModelAvro: + def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, graph_format: GraphFormat, + start: datetime = None, end: datetime = None) -> BrokerQueryModelAvro: request = GetBrokerQueryModelRequestAvro() request.id_token = id_token request.guid = str(self.management_id) @@ -121,13 +118,14 @@ def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, request.message_id = str(ID()) request.callback_topic = self.callback_topic request.broker_id = str(broker) - request.level = level - request.graph_format = graph_format.value + request.set_level(value=level) + request.set_graph_format(graph_format=graph_format.value) + request.set_start(start=start) + request.set_end(end=end) status, response = self.send_request(request) if status.code == 0: return response.model - return None def extend_reservation(self, *, reservation: ID, new_end_time: datetime, sliver: BaseSliver, dependencies: List[ReservationPredecessorAvro] = None) -> bool: @@ -159,8 +157,6 @@ def claim_delegations(self, *, broker: ID, did: ID) -> DelegationAvro: if status.code == 0 and response.delegations is not None and len(response.delegations) > 0: return next(iter(response.delegations)) - return None - def reclaim_delegations(self, *, broker: ID, did: ID) -> DelegationAvro: request = ReclaimResourcesAvro() request.guid = str(self.management_id) @@ -175,8 +171,6 @@ def reclaim_delegations(self, *, broker: ID, did: ID) -> DelegationAvro: if status.code == 0 and response.delegations is not None and len(response.delegations) > 0: return next(iter(response.delegations)) - return None - def clone(self): return KafkaBroker(guid=self.management_id, kafka_topic=self.kafka_topic, diff --git a/fabric_cf/actor/core/manage/kafka/kafka_controller.py b/fabric_cf/actor/core/manage/kafka/kafka_controller.py index 4aa0c19f..f598b9dd 100644 --- a/fabric_cf/actor/core/manage/kafka/kafka_controller.py +++ b/fabric_cf/actor/core/manage/kafka/kafka_controller.py @@ -73,8 +73,8 @@ def get_brokers(self, *, broker: ID = None, id_token: str = None) -> List[ProxyA return response.proxies return None - def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, - graph_format: GraphFormat) -> BrokerQueryModelAvro: + def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, graph_format: GraphFormat, + start: datetime = None, end: datetime = None) -> BrokerQueryModelAvro: raise ManageException(Constants.NOT_IMPLEMENTED) def claim_delegations(self, *, broker: ID, did: ID) -> DelegationAvro: diff --git a/fabric_cf/actor/core/manage/local/local_broker.py b/fabric_cf/actor/core/manage/local/local_broker.py index 212d61c3..8d81cd94 100644 --- a/fabric_cf/actor/core/manage/local/local_broker.py +++ b/fabric_cf/actor/core/manage/local/local_broker.py @@ -94,8 +94,8 @@ def get_brokers(self, *, broker: ID = None, id_token: str = None) -> List[ProxyA return None - def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, - graph_format: GraphFormat) -> BrokerQueryModelAvro: + def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, graph_format: GraphFormat, + start: datetime = None, end: datetime = None) -> BrokerQueryModelAvro: self.clear_last() try: result = self.manager.get_broker_query_model(broker=broker, caller=self.auth, level=level, diff --git a/fabric_cf/actor/core/manage/local/local_controller.py b/fabric_cf/actor/core/manage/local/local_controller.py index 13889a79..2ef60a81 100644 --- a/fabric_cf/actor/core/manage/local/local_controller.py +++ b/fabric_cf/actor/core/manage/local/local_controller.py @@ -93,8 +93,8 @@ def get_brokers(self, *, broker: ID = None, id_token: str = None) -> List[ProxyA except Exception as e: self.on_exception(e=e, traceback_str=traceback.format_exc()) - def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, - graph_format: GraphFormat) -> BrokerQueryModelAvro: + def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, graph_format: GraphFormat, + start: datetime = None, end: datetime = None) -> BrokerQueryModelAvro: self.clear_last() try: result = self.manager.get_broker_query_model(broker=broker, caller=self.auth, id_token=id_token, diff --git a/fabric_cf/actor/core/plugins/db/actor_database.py b/fabric_cf/actor/core/plugins/db/actor_database.py index 97ac4ab5..b9ce02c2 100644 --- a/fabric_cf/actor/core/plugins/db/actor_database.py +++ b/fabric_cf/actor/core/plugins/db/actor_database.py @@ -470,9 +470,9 @@ def get_components(self, *, node_id: str, states: list[int], rsv_type: list[str] self.lock.release() def get_reservations(self, *, slice_id: ID = None, graph_node_id: str = None, project_id: str = None, - email: str = None, oidc_sub: str = None, rid: ID = None, - states: list[int] = None, site: str = None, - rsv_type: list[str] = None) -> List[ABCReservationMixin]: + email: str = None, oidc_sub: str = None, rid: ID = None, states: list[int] = None, + site: str = None, rsv_type: list[str] = None, start: datetime = None, + end: datetime = None) -> List[ABCReservationMixin]: result = [] try: #self.lock.acquire() @@ -480,7 +480,7 @@ def get_reservations(self, *, slice_id: ID = None, graph_node_id: str = None, pr res_id = str(rid) if rid is not None else None res_dict_list = self.db.get_reservations(slice_id=sid, graph_node_id=graph_node_id, project_id=project_id, email=email, oidc_sub=oidc_sub, rid=res_id, - states=states, site=site, rsv_type=rsv_type) + states=states, site=site, rsv_type=rsv_type, start=start, end=end) if self.lock.locked(): self.lock.release() result = self._load_reservations_from_db(res_dict_list=res_dict_list) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 7601000b..b0cb860e 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -1061,9 +1061,12 @@ def query(self, *, p: dict) -> dict: else: bqm_format = GraphFormat.GRAPHML + start = p.get(Constants.START, None) + end = p.get(Constants.END, None) + try: if self.query_cbm is not None: - graph = self.query_cbm.get_bqm(query_level=query_level) + graph = self.query_cbm.get_bqm(query_level=query_level, start=start, end=end) graph_string = None if graph is not None: graph_string = graph.serialize_graph(format=bqm_format) diff --git a/fabric_cf/actor/db/psql_database.py b/fabric_cf/actor/db/psql_database.py index 95dcbeec..04d2a5d7 100644 --- a/fabric_cf/actor/db/psql_database.py +++ b/fabric_cf/actor/db/psql_database.py @@ -784,7 +784,8 @@ def create_reservation_filter(self, *, slice_id: str = None, graph_node_id: str def get_reservations(self, *, slice_id: str = None, graph_node_id: str = None, project_id: str = None, email: str = None, oidc_sub: str = None, rid: str = None, states: list[int] = None, - category: list[int] = None, site: str = None, rsv_type: list[str] = None) -> List[dict]: + category: list[int] = None, site: str = None, rsv_type: list[str] = None, + start: datetime = None, end: datetime = None) -> List[dict]: """ Get Reservations for an actor @param slice_id slice id @@ -797,6 +798,8 @@ def get_reservations(self, *, slice_id: str = None, graph_node_id: str = None, p @param category reservation category @param site site name @param rsv_type rsv_type + @param start search for slivers with lease_end_time after start + @param end search for slivers with lease_end_time before end @return list of reservations """ @@ -817,6 +820,9 @@ def get_reservations(self, *, slice_id: str = None, graph_node_id: str = None, p if category is not None: rows = rows.filter(Reservations.rsv_category.in_(category)) + if end is not None: + rows = rows.filter(start <= Reservations.lease_end <= end) + for row in rows.all(): result.append(self.generate_dict_from_row(row=row)) except Exception as e: diff --git a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py index 7f731a52..acc73b57 100644 --- a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py +++ b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py @@ -23,7 +23,7 @@ # # # Author: Ilya Baldin (ibaldin@renci.org) - +from datetime import datetime from typing import Tuple, List, Dict from collections import defaultdict @@ -81,8 +81,8 @@ def __site_maintenance_info(self, *, site_name: str): result.finalize() return result - def __occupied_node_capacity(self, *, node_id: str) -> Tuple[Capacities, - Dict[ComponentType, Dict[str, Capacities]]]: + def __occupied_node_capacity(self, *, node_id: str, start: datetime, + end: datetime) -> Tuple[Capacities, Dict[ComponentType, Dict[str, Capacities]]]: """ Figure out the total capacity occupied in the network node and return a tuple of capacities occupied in this node and a dict of component capacities that are occupied @@ -96,7 +96,9 @@ def __occupied_node_capacity(self, *, node_id: str) -> Tuple[Capacities, # get existing reservations for this node existing_reservations = self.actor.get_plugin().get_database().get_reservations(graph_node_id=node_id, - states=states) + states=states, + start=start, + end=end) # node capacities occupied_capacities = Capacities() @@ -142,6 +144,9 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope if kwargs.get('query_level', None) is None or kwargs['query_level'] > 2: return cbm.clone_graph(new_graph_id=str(uuid.uuid4())) + start = kwargs.get('start', None) + end = kwargs.get('end', None) + # do a one-pass aggregation of servers, their components and interfaces # and some flags (e.g. PTP availability) # this includes facilities @@ -201,7 +206,8 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope allocated_comp_caps = dict() else: # query database for everything taken on this node - allocated_caps, allocated_comp_caps = self.__occupied_node_capacity(node_id=sliver.node_id) + allocated_caps, allocated_comp_caps = self.__occupied_node_capacity(node_id=sliver.node_id, + start=start, end=end) site_sliver.capacity_allocations = site_sliver.capacity_allocations + allocated_caps worker_sliver.capacity_allocations = allocated_caps diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index 9cb7959b..56aba265 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -114,7 +114,7 @@ def get_broker(self, *, controller: ABCMgmtControllerMixin) -> ID: def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, token: str = None, level: int = 10, graph_format: GraphFormat = GraphFormat.GRAPHML, - force_refresh: bool = False) -> str or None: + force_refresh: bool = False, start: datetime = None, end: datetime) -> str or None: """ Discover all the available resources by querying Broker :param controller Management Controller Object @@ -122,15 +122,19 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok :param level: level of details :param graph_format: Graph format :param force_refresh: Force fetching a fresh model from Broker + :param start: start time + :param end: end time :return str or None """ broker_query_model = None - saved_bqm = self.controller_state.get_saved_bqm(graph_format=graph_format, level=level) - if saved_bqm is not None: - if not force_refresh and not saved_bqm.can_refresh() and not saved_bqm.refresh_in_progress: - broker_query_model = saved_bqm.get_bqm() - else: - saved_bqm.start_refresh() + # Always get Fresh copy for advanced resource requests + if not start and not end: + saved_bqm = self.controller_state.get_saved_bqm(graph_format=graph_format, level=level) + if saved_bqm is not None: + if not force_refresh and not saved_bqm.can_refresh() and not saved_bqm.refresh_in_progress: + broker_query_model = saved_bqm.get_bqm() + else: + saved_bqm.start_refresh() if broker_query_model is None: broker = self.get_broker(controller=controller) @@ -139,22 +143,27 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok "Please check Orchestrator container configuration and logs.") model = controller.get_broker_query_model(broker=broker, id_token=token, level=level, - graph_format=graph_format) + graph_format=graph_format, start=start, end=end) if model is None or model.get_model() is None or model.get_model() == '': raise OrchestratorException(http_error_code=NOT_FOUND, message=f"Resource(s) not found for " f"level: {level} format: {graph_format}!") broker_query_model = model.get_model() - self.controller_state.save_bqm(bqm=broker_query_model, graph_format=graph_format, level=level) + # Do not update cache for advance requests + if not start and not end: + self.controller_state.save_bqm(bqm=broker_query_model, graph_format=graph_format, level=level) return broker_query_model - def list_resources(self, *, token: str, level: int, force_refresh: bool = False) -> dict: + def list_resources(self, *, token: str, level: int, force_refresh: bool = False, start: datetime = None, + end: datetime) -> dict: """ List Resources :param token Fabric Identity Token :param level: level of details (default set to 1) :param force_refresh: force fetching bqm from broker and override the cached model + :param start: start time + :param end: end time :raises Raises an exception in case of failure :returns Broker Query Model on success """ @@ -165,7 +174,8 @@ def list_resources(self, *, token: str, level: int, force_refresh: bool = False) self.__authorize_request(id_token=token, action_id=ActionId.query) broker_query_model = self.discover_broker_query_model(controller=controller, token=token, level=level, - force_refresh=force_refresh) + force_refresh=force_refresh, start=start, + end=end) return ResponseBuilder.get_broker_query_model_summary(bqm=broker_query_model) @@ -219,8 +229,8 @@ def create_slice(self, *, token: str, slice_name: str, slice_graph: str, ssh_key fabric_token = AccessChecker.validate_and_decode_token(token=token) project, tags, project_name = fabric_token.first_project allow_long_lived = True if Constants.SLICE_NO_LIMIT_LIFETIME in tags else False - end_time = self.__validate_lease_end_time(lease_end_time=lease_end_time, allow_long_lived=allow_long_lived, - project_id=project) + end_time = self.__compute_lease_end_time(lease_end_time=lease_end_time, allow_long_lived=allow_long_lived, + project_id=project) controller = self.controller_state.get_management_actor() self.logger.debug(f"create_slice invoked for Controller: {controller}") @@ -238,7 +248,7 @@ def create_slice(self, *, token: str, slice_name: str, slice_graph: str, ssh_key # Authorize the slice create_ts = time.time() self.__authorize_request(id_token=token, action_id=ActionId.create, resource=topology, - lease_end_time=end_time) + lease_end_time=end_time) self.logger.info(f"PDP authorize: TIME= {time.time() - create_ts:.0f}") # Check if an Active slice exists already with the same name for the user @@ -717,9 +727,9 @@ def renew_slice(self, *, token: str, slice_id: str, new_lease_end_time: str): fabric_token = AccessChecker.validate_and_decode_token(token=token) project, tags, project_name = fabric_token.first_project allow_long_lived = True if Constants.SLICE_NO_LIMIT_LIFETIME in tags else False - new_end_time = self.__validate_lease_end_time(lease_end_time=new_lease_end_time, - allow_long_lived=allow_long_lived, - project_id=project) + new_end_time = self.__compute_lease_end_time(lease_end_time=new_lease_end_time, + allow_long_lived=allow_long_lived, + project_id=project) reservations = controller.get_reservations(slice_id=slice_id) if reservations is None or len(reservations) < 1: @@ -766,8 +776,31 @@ def renew_slice(self, *, token: str, slice_id: str, new_lease_end_time: str): self.logger.error(f"Exception occurred processing renew e: {e}") raise e - def __validate_lease_end_time(self, lease_end_time: str, allow_long_lived: bool = False, - project_id: str = None) -> datetime: + @staticmethod + def validate_lease_time(lease_time: str) -> datetime: + """ + Validate Lease Time + :param lease_time: Lease Time + :return Lease Time + :raises Exception if new lease time is in past + """ + if not lease_time: + return lease_time + try: + new_end_time = datetime.strptime(lease_time, Constants.LEASE_TIME_FORMAT) + except Exception as e: + raise OrchestratorException(f"Lease End Time is not in format {Constants.LEASE_TIME_FORMAT}", + http_error_code=BAD_REQUEST) + + now = datetime.now(timezone.utc) + if new_end_time <= now: + raise OrchestratorException(f"New term end time {new_end_time} is in the past! ", + http_error_code=BAD_REQUEST) + + return new_end_time + + def __compute_lease_end_time(self, lease_end_time: str, allow_long_lived: bool = False, + project_id: str = None) -> datetime: """ Validate Lease End Time :param lease_end_time: New End Time @@ -779,16 +812,9 @@ def __validate_lease_end_time(self, lease_end_time: str, allow_long_lived: bool if lease_end_time is None: new_end_time = datetime.now(timezone.utc) + timedelta(hours=Constants.DEFAULT_LEASE_IN_HOURS) return new_end_time - try: - new_end_time = datetime.strptime(lease_end_time, Constants.LEASE_TIME_FORMAT) - except Exception as e: - raise OrchestratorException(f"Lease End Time is not in format {Constants.LEASE_TIME_FORMAT}", - http_error_code=BAD_REQUEST) + new_end_time = self.validate_lease_time(lease_time=lease_end_time) now = datetime.now(timezone.utc) - if new_end_time <= now: - raise OrchestratorException(f"New term end time {new_end_time} is in the past! ", - http_error_code=BAD_REQUEST) if allow_long_lived: default_long_lived_duration = Constants.LONG_LIVED_SLICE_TIME_WEEKS diff --git a/fabric_cf/orchestrator/swagger_server/controllers/resources_controller.py b/fabric_cf/orchestrator/swagger_server/controllers/resources_controller.py index 19bb4644..a3c417c9 100644 --- a/fabric_cf/orchestrator/swagger_server/controllers/resources_controller.py +++ b/fabric_cf/orchestrator/swagger_server/controllers/resources_controller.py @@ -31,4 +31,4 @@ def resources_get(level, force_refresh, start_date=None, end_date=None): # noqa :rtype: Resources """ - return rc.resources_get(level, force_refresh) + return rc.resources_get(level=level, force_refresh=force_refresh, start_date=start_date, end_date=end_date) diff --git a/fabric_cf/orchestrator/swagger_server/response/resources_controller.py b/fabric_cf/orchestrator/swagger_server/response/resources_controller.py index 16ce2e9c..897d07bf 100644 --- a/fabric_cf/orchestrator/swagger_server/response/resources_controller.py +++ b/fabric_cf/orchestrator/swagger_server/response/resources_controller.py @@ -64,7 +64,8 @@ def portalresources_get(graph_format) -> Resources: # noqa: E501 return cors_error_response(error=e) -def resources_get(level, force_refresh) -> Resources: # noqa: E501 +def resources_get(level: int = 1, force_refresh: bool = False, start_date: str = None, + end_date: str = None) -> Resources: # noqa: E501 """Retrieve a listing and description of available resources Retrieve a listing and description of available resources # noqa: E501 @@ -73,6 +74,10 @@ def resources_get(level, force_refresh) -> Resources: # noqa: E501 :type level: int :param force_refresh: Force to retrieve current available resource information. :type force_refresh: bool + :param start_date: starting date to check availability from + :type start_date: str + :param end_date: end date to check availability until + :type end_date: str :rtype: Resources """ @@ -81,7 +86,10 @@ def resources_get(level, force_refresh) -> Resources: # noqa: E501 received_counter.labels(GET_METHOD, RESOURCES_PATH).inc() try: token = get_token() - bqm_dict = handler.list_resources(token=token, level=level, force_refresh=force_refresh) + start = handler.validate_lease_time(lease_time=start_date) + end = handler.validate_lease_time(lease_time=end_date) + bqm_dict = handler.list_resources(token=token, level=level, force_refresh=force_refresh, + start=start, end=end) response = Resources() response.data = [Resource().from_dict(bqm_dict)] response.size = 1 From 8aca9989f84b9f2ae6f5a42ca67ed5396fc59233 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 15 Apr 2024 11:40:19 -0400 Subject: [PATCH 007/133] initial changes for list resources time based --- .gitignore | 1 + .../core/apis/abc_actor_management_object.py | 18 +++ .../actor/core/apis/abc_mgmt_client_actor.py | 17 +++ fabric_cf/actor/core/core/actor.py | 3 - fabric_cf/actor/core/core/broker_policy.py | 4 +- fabric_cf/actor/core/kernel/kernel.py | 1 + .../core/manage/actor_management_object.py | 17 +++ .../actor/core/manage/local/local_broker.py | 2 +- .../core/manage/local/local_controller.py | 15 +- .../core/policy/authority_calendar_policy.py | 38 ++--- .../policy/broker_simpler_units_policy.py | 4 + fabric_cf/actor/db/psql_database.py | 5 +- fabric_cf/actor/fim/fim_helper.py | 70 ++++++++- .../plugins/broker/aggregate_bqm_plugin.py | 27 ++-- fabric_cf/actor/test/schema/message.avsc | 135 +++++++++++++++++- fabric_cf/broker/config.broker.yaml | 2 +- fabric_cf/broker/test/test.yaml | 2 +- .../orchestrator/core/orchestrator_handler.py | 66 ++++++--- .../orchestrator/core/orchestrator_kernel.py | 5 + fabric_cf/orchestrator/openapi.json | 26 +++- .../controllers/resources_controller.py | 9 +- .../response/resources_controller.py | 12 +- .../swagger_server/swagger/swagger.yaml | 37 ++--- .../test/test_resources_controller.py | 7 +- 24 files changed, 434 insertions(+), 89 deletions(-) diff --git a/.gitignore b/.gitignore index 3a33e1c3..14aeaa9e 100644 --- a/.gitignore +++ b/.gitignore @@ -150,3 +150,4 @@ fabric_cf/actor/test/*.graphml secrets/kafkacat2-ca1-signed.pem secrets/kafkacat1-ca1-signed.pem secrets/... +neo4j/RENC.graphml diff --git a/fabric_cf/actor/core/apis/abc_actor_management_object.py b/fabric_cf/actor/core/apis/abc_actor_management_object.py index 8bf95c68..96de16b5 100644 --- a/fabric_cf/actor/core/apis/abc_actor_management_object.py +++ b/fabric_cf/actor/core/apis/abc_actor_management_object.py @@ -26,6 +26,7 @@ from __future__ import annotations from abc import abstractmethod +from datetime import datetime from typing import TYPE_CHECKING, Tuple, Dict, List from fabric_mb.message_bus.messages.poa_avro import PoaAvro @@ -38,6 +39,7 @@ from fabric_mb.message_bus.messages.result_slice_avro import ResultSliceAvro from fabric_mb.message_bus.messages.result_string_avro import ResultStringAvro from fabric_mb.message_bus.messages.slice_avro import SliceAvro +from fim.user import GraphFormat from fabric_cf.actor.core.apis.abc_management_object import ABCManagementObject from fabric_cf.actor.core.container.maintenance import Site @@ -280,4 +282,20 @@ def remove_slice(self, *, slice_id: ID, caller: AuthToken) -> ResultAvro: @param slice_id slice id @param caller caller @return true for success; false otherwise + """ + + def build_broker_query_model(self, level_0_broker_query_model: str, level: int, + graph_format: GraphFormat = GraphFormat.GRAPHML, + start: datetime = None, end: datetime = None, includes: str = None, + excludes: str = None) -> str: + """ + Build the BQM Model using current usage + @param level_0_broker_query_model Capacity Model + @param level: level of details + @param graph_format: Graph Format + @param start: start time + @param end: end time + @param includes: comma separated lists of sites to include + @param excludes: comma separated lists of sites to exclude + @return BQM """ \ No newline at end of file diff --git a/fabric_cf/actor/core/apis/abc_mgmt_client_actor.py b/fabric_cf/actor/core/apis/abc_mgmt_client_actor.py index fef45485..7762c1c3 100644 --- a/fabric_cf/actor/core/apis/abc_mgmt_client_actor.py +++ b/fabric_cf/actor/core/apis/abc_mgmt_client_actor.py @@ -129,6 +129,23 @@ def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, graph @return BQM """ + def build_broker_query_model(self, level_0_broker_query_model: str, level: int, + graph_format: GraphFormat = GraphFormat.GRAPHML, + start: datetime = None, end: datetime = None, + includes: str = None, excludes: str = None) -> str: + """ + Build the BQM Model using current usage + @param level_0_broker_query_model Capacity Model + @param level: level of details + @param graph_format: Graph Format + @param start: start time + @param end: end time + @param includes: comma separated lists of sites to include + @param excludes: comma separated lists of sites to exclude + @return BQM + """ + raise Exception("Not Implemented") + @abstractmethod def extend_reservation(self, *, reservation: ID, new_end_time: datetime, sliver: BaseSliver, dependencies: List[ReservationPredecessorAvro] = None) -> bool: diff --git a/fabric_cf/actor/core/core/actor.py b/fabric_cf/actor/core/core/actor.py index c3cde772..7b462ca5 100644 --- a/fabric_cf/actor/core/core/actor.py +++ b/fabric_cf/actor/core/core/actor.py @@ -28,9 +28,6 @@ from typing import List, Dict from fabric_cf.actor.core.common.constants import Constants -from fabric_mb.message_bus.messages.poa_avro import PoaAvro -from fabric_mb.message_bus.messages.poa_info_avro import PoaInfoAvro - from fabric_cf.actor.boot.configuration import ActorConfig from fabric_cf.actor.core.apis.abc_delegation import ABCDelegation from fabric_cf.actor.core.apis.abc_policy import ABCPolicy diff --git a/fabric_cf/actor/core/core/broker_policy.py b/fabric_cf/actor/core/core/broker_policy.py index 3b8b0723..11cae52b 100644 --- a/fabric_cf/actor/core/core/broker_policy.py +++ b/fabric_cf/actor/core/core/broker_policy.py @@ -142,9 +142,9 @@ def get_broker_query_model_query(*, level: int, bqm_format: GraphFormat = GraphF Constants.QUERY_DETAIL_LEVEL: str(level), Constants.BROKER_QUERY_MODEL_FORMAT: str(bqm_format.value)} if start: - properties[Constants.START] = start + properties[Constants.START] = start.strftime(Constants.LEASE_TIME_FORMAT) if end: - properties[Constants.END] = end + properties[Constants.END] = end.strftime(Constants.LEASE_TIME_FORMAT) return properties @staticmethod diff --git a/fabric_cf/actor/core/kernel/kernel.py b/fabric_cf/actor/core/kernel/kernel.py index 92bcdac1..6721efda 100644 --- a/fabric_cf/actor/core/kernel/kernel.py +++ b/fabric_cf/actor/core/kernel/kernel.py @@ -26,6 +26,7 @@ import threading import time import traceback + from typing import List, Dict from fabric_cf.actor.core.apis.abc_base_plugin import ABCBasePlugin diff --git a/fabric_cf/actor/core/manage/actor_management_object.py b/fabric_cf/actor/core/manage/actor_management_object.py index 0cd98f94..b61dba52 100644 --- a/fabric_cf/actor/core/manage/actor_management_object.py +++ b/fabric_cf/actor/core/manage/actor_management_object.py @@ -25,9 +25,11 @@ # Author: Komal Thareja (kthare10@renci.org) from __future__ import annotations +import traceback from datetime import datetime, timezone from typing import TYPE_CHECKING, List, Dict, Tuple +from fabric_cf.actor.fim.fim_helper import FimHelper from fabric_mb.message_bus.messages.poa_avro import PoaAvro from fabric_mb.message_bus.messages.poa_info_avro import PoaInfoAvro from fabric_mb.message_bus.messages.reservation_mng import ReservationMng @@ -40,6 +42,8 @@ from fabric_mb.message_bus.messages.result_avro import ResultAvro from fabric_mb.message_bus.messages.result_slice_avro import ResultSliceAvro from fabric_mb.message_bus.messages.slice_avro import SliceAvro +from fim.user import GraphFormat +from fim.user.topology import AdvertizedTopology from fabric_cf.actor.core.apis.abc_actor_runnable import ABCActorRunnable from fabric_cf.actor.core.common.constants import Constants, ErrorCodes @@ -860,3 +864,16 @@ def get_poas(self, *, caller: AuthToken, states: List[int] = None, result.status = ManagementObject.set_exception_details(result=result.status, e=e) return result + + def build_broker_query_model(self, level_0_broker_query_model: str, level: int, + graph_format: GraphFormat = GraphFormat.GRAPHML, + start: datetime = None, end: datetime = None, includes: str = None, + excludes: str = None) -> str: + try: + db = self.actor.get_plugin().get_database() + return FimHelper.build_broker_query_model(db=db, level_0_broker_query_model=level_0_broker_query_model, + level=level, graph_format=graph_format, start=start, + end=end, includes=includes, excludes=excludes) + except Exception as e: + self.logger.error(f"Exception occurred build_broker_query_model e: {e}") + self.logger.error(traceback.format_exc()) \ No newline at end of file diff --git a/fabric_cf/actor/core/manage/local/local_broker.py b/fabric_cf/actor/core/manage/local/local_broker.py index 8d81cd94..d8f0d581 100644 --- a/fabric_cf/actor/core/manage/local/local_broker.py +++ b/fabric_cf/actor/core/manage/local/local_broker.py @@ -100,7 +100,7 @@ def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, graph try: result = self.manager.get_broker_query_model(broker=broker, caller=self.auth, level=level, id_token=id_token, ignore_broker_check=True, - graph_format=graph_format) + graph_format=graph_format, start=start, end=end) self.last_status = result.status if result.status.get_code() == 0: diff --git a/fabric_cf/actor/core/manage/local/local_controller.py b/fabric_cf/actor/core/manage/local/local_controller.py index 2ef60a81..efe72f41 100644 --- a/fabric_cf/actor/core/manage/local/local_controller.py +++ b/fabric_cf/actor/core/manage/local/local_controller.py @@ -98,7 +98,8 @@ def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, graph self.clear_last() try: result = self.manager.get_broker_query_model(broker=broker, caller=self.auth, id_token=id_token, - level=level, graph_format=graph_format) + level=level, graph_format=graph_format, + start=start, end=end) self.last_status = result.status if result.status.get_code() == 0: @@ -106,6 +107,18 @@ def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, graph except Exception as e: self.on_exception(e=e, traceback_str=traceback.format_exc()) + def build_broker_query_model(self, level_0_broker_query_model: str, level: int, + graph_format: GraphFormat = GraphFormat.GRAPHML, + start: datetime = None, end: datetime = None, + includes: str = None, excludes: str = None) -> str: + self.clear_last() + try: + return self.manager.build_broker_query_model(level_0_broker_query_model=level_0_broker_query_model, + level=level, graph_format=graph_format, start=start, + end=end, includes=include, excludes=excludes) + except Exception as e: + self.on_exception(e=e, traceback_str=traceback.format_exc()) + def claim_delegations(self, *, broker: ID, did: ID, id_token: str = None) -> DelegationAvro: self.clear_last() try: diff --git a/fabric_cf/actor/core/policy/authority_calendar_policy.py b/fabric_cf/actor/core/policy/authority_calendar_policy.py index daa48174..73af3638 100644 --- a/fabric_cf/actor/core/policy/authority_calendar_policy.py +++ b/fabric_cf/actor/core/policy/authority_calendar_policy.py @@ -402,23 +402,27 @@ def map(self, *, reservation: ABCAuthorityReservation, node_id_to_reservations: @param node_id_to_reservations: node_id_to_reservations @throws Exception in case of error """ - assigned = self.assign_reservation(reservation=reservation, node_id_to_reservations=node_id_to_reservations) - if assigned is not None: - approved = reservation.get_requested_term() - reservation.set_approved(term=approved, approved_resources=assigned) - reservation.set_bid_pending(value=False) - node_id = assigned.get_sliver().get_node_map()[1] - - if node_id_to_reservations.get(node_id, None) is None: - node_id_to_reservations[node_id] = ReservationSet() - node_id_to_reservations[node_id].add(reservation=reservation) - else: - if not reservation.is_terminal(): - self.logger.debug(f"Deferring reservation {reservation} for the next cycle: " - f"{self.actor.get_current_cycle() + 1}") - self.reschedule(reservation=reservation) + try: + assigned = self.assign_reservation(reservation=reservation, node_id_to_reservations=node_id_to_reservations) + if assigned is not None: + approved = reservation.get_requested_term() + reservation.set_approved(term=approved, approved_resources=assigned) + reservation.set_bid_pending(value=False) + node_id = assigned.get_sliver().get_node_map()[1] + + if node_id_to_reservations.get(node_id, None) is None: + node_id_to_reservations[node_id] = ReservationSet() + node_id_to_reservations[node_id].add(reservation=reservation) + else: + if not reservation.is_terminal(): + self.logger.debug(f"Deferring reservation {reservation} for the next cycle: " + f"{self.actor.get_current_cycle() + 1}") + self.reschedule(reservation=reservation) - return node_id_to_reservations + return node_id_to_reservations + except Exception as e: + self.logger.error(f"Could not assign {e}") + reservation.fail(message=str(e)) def assign_reservation(self, *, reservation: ABCAuthorityReservation, node_id_to_reservations: dict): """ @@ -472,7 +476,7 @@ def assign_reservation(self, *, reservation: ABCAuthorityReservation, node_id_to except Exception as e: self.logger.error(traceback.format_exc()) self.logger.error(f"Could not assign {e}") - return None + raise e def configuration_complete(self, *, action: str, token: ConfigToken, out_properties: dict): super().configuration_complete(action=action, token=token, out_properties=out_properties) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index b0cb860e..7565ed10 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -1062,7 +1062,11 @@ def query(self, *, p: dict) -> dict: bqm_format = GraphFormat.GRAPHML start = p.get(Constants.START, None) + if start: + start = datetime.strptime(start, Constants.LEASE_TIME_FORMAT) end = p.get(Constants.END, None) + if end: + end = datetime.strptime(end, Constants.LEASE_TIME_FORMAT) try: if self.query_cbm is not None: diff --git a/fabric_cf/actor/db/psql_database.py b/fabric_cf/actor/db/psql_database.py index 04d2a5d7..8b832e04 100644 --- a/fabric_cf/actor/db/psql_database.py +++ b/fabric_cf/actor/db/psql_database.py @@ -820,8 +820,11 @@ def get_reservations(self, *, slice_id: str = None, graph_node_id: str = None, p if category is not None: rows = rows.filter(Reservations.rsv_category.in_(category)) + if start is not None: + rows = rows.filter(start <= Reservations.lease_end) + if end is not None: - rows = rows.filter(start <= Reservations.lease_end <= end) + rows = rows.filter(Reservations.lease_end <= end) for row in rows.all(): result.append(self.generate_dict_from_row(row=row)) diff --git a/fabric_cf/actor/fim/fim_helper.py b/fabric_cf/actor/fim/fim_helper.py index 51db98e5..db73d526 100644 --- a/fabric_cf/actor/fim/fim_helper.py +++ b/fabric_cf/actor/fim/fim_helper.py @@ -26,6 +26,7 @@ import logging import random import traceback +from datetime import datetime from typing import Tuple, List, Union from fim.graph.abc_property_graph import ABCPropertyGraph, ABCGraphImporter @@ -45,8 +46,10 @@ from fim.slivers.interface_info import InterfaceSliver, InterfaceType from fim.slivers.network_node import NodeSliver from fim.slivers.network_service import NetworkServiceSliver, ServiceType -from fim.user import ExperimentTopology, Labels, NodeType, Component, ReservationInfo +from fim.user import ExperimentTopology, NodeType, Component, ReservationInfo, Node, GraphFormat +from fim.user.composite_node import CompositeNode from fim.user.interface import Interface +from fim.user.topology import AdvertizedTopology from fabric_cf.actor.core.common.constants import Constants from fabric_cf.actor.core.kernel.reservation_states import ReservationStates @@ -611,3 +614,68 @@ def prune_graph(*, graph_id: str) -> ExperimentTopology: slice_topology.prune(reservation_state=ReservationStates.CloseFail.name) return slice_topology + + @staticmethod + def get_workers(site: CompositeNode) -> dict: + node_id_list = site.topo.graph_model.get_first_neighbor( + node_id=site.node_id, + rel=ABCPropertyGraph.REL_HAS, + node_label=ABCPropertyGraph.CLASS_NetworkNode, + ) + workers = dict() + for nid in node_id_list: + _, node_props = site.topo.graph_model.get_node_properties(node_id=nid) + n = Node( + name=node_props[ABCPropertyGraph.PROP_NAME], + node_id=nid, + topo=site.topo, + ) + if n.type != NodeType.Facility: + workers[n.name] = n + return workers + + @staticmethod + def build_broker_query_model(db, level_0_broker_query_model: str, level: int, + graph_format: GraphFormat = GraphFormat.GRAPHML, + start: datetime = None, end: datetime = None, + includes: str = None, excludes: str = None) -> str: + sites_to_include = [s.strip().upper() for s in includes.split(",")] if includes else None + sites_to_exclude = [s.strip().upper() for s in excludes.split(",")] if excludes else None + + if level_0_broker_query_model and len(level_0_broker_query_model) > 0: + from fabric_cf.actor.fim.plugins.broker.aggregate_bqm_plugin import AggregatedBQMPlugin + substrate = AdvertizedTopology() + substrate.load(graph_string=level_0_broker_query_model) + sites_to_remove = [] + + for site_name, site in substrate.sites.items(): + if sites_to_include and site_name not in sites_to_include: + sites_to_remove.append(site_name) + continue + + if sites_to_exclude and site_name in sites_to_exclude: + sites_to_remove.append(site_name) + continue + + workers = FimHelper.get_workers(site) + for w in workers.values(): + allocated_caps, allocated_comp_caps = AggregatedBQMPlugin.occupied_node_capacity( + db=db, + node_id=w.node_id, + start=start, + end=end + ) + w.set_property("capacity_allocations", allocated_caps) + # Merge allocated component capacities + for comp_type_comp_model, c in w.components.items(): + cap_allocations = c.capacity_allocations or Capacities() + ctype_caps = allocated_comp_caps.get(c.type) + if ctype_caps: + cm_caps = ctype_caps.get(c.model) + if cm_caps: + cap_allocations += cm_caps + c.set_property("capacity_allocations", cap_allocations) + + for s in sites_to_remove: + substrate.remove_node(s) + return substrate.serialize(fmt=graph_format) diff --git a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py index acc73b57..9c2e5258 100644 --- a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py +++ b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py @@ -23,8 +23,10 @@ # # # Author: Ilya Baldin (ibaldin@renci.org) +from __future__ import annotations + from datetime import datetime -from typing import Tuple, List, Dict +from typing import Tuple, Dict, TYPE_CHECKING from collections import defaultdict import uuid @@ -44,6 +46,8 @@ from fabric_cf.actor.core.kernel.reservation_states import ReservationStates +if TYPE_CHECKING: + from fabric_cf.actor.core.apis.abc_database import ABCDatabase class AggregatedBQMPlugin: """ @@ -81,8 +85,9 @@ def __site_maintenance_info(self, *, site_name: str): result.finalize() return result - def __occupied_node_capacity(self, *, node_id: str, start: datetime, - end: datetime) -> Tuple[Capacities, Dict[ComponentType, Dict[str, Capacities]]]: + @staticmethod + def occupied_node_capacity(*, db: ABCDatabase, node_id: str, start: datetime, + end: datetime) -> Tuple[Capacities, Dict[ComponentType, Dict[str, Capacities]]]: """ Figure out the total capacity occupied in the network node and return a tuple of capacities occupied in this node and a dict of component capacities that are occupied @@ -95,11 +100,7 @@ def __occupied_node_capacity(self, *, node_id: str, start: datetime, ReservationStates.Nascent.value] # get existing reservations for this node - existing_reservations = self.actor.get_plugin().get_database().get_reservations(graph_node_id=node_id, - states=states, - start=start, - end=end) - + existing_reservations = db.get_reservations(graph_node_id=node_id, states=states, start=start, end=end) # node capacities occupied_capacities = Capacities() occupied_component_capacities = defaultdict(dict) @@ -201,13 +202,15 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope worker_sliver.resource_type = NodeType.Server worker_sliver.set_site(s) worker_sliver.node_id = str(uuid.uuid4()) - if self.DEBUG_FLAG: + if self.DEBUG_FLAG or kwargs['query_level'] == 0: # for debugging and running in a test environment + # also for level 0; only return capacity information allocated_comp_caps = dict() else: + db = self.actor.get_plugin().get_database() # query database for everything taken on this node - allocated_caps, allocated_comp_caps = self.__occupied_node_capacity(node_id=sliver.node_id, - start=start, end=end) + allocated_caps, allocated_comp_caps = self.occupied_node_capacity(db=db, node_id=sliver.node_id, + start=start, end=end) site_sliver.capacity_allocations = site_sliver.capacity_allocations + allocated_caps worker_sliver.capacity_allocations = allocated_caps @@ -279,7 +282,7 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope props=site_props) # Add per worker metrics for query level 2 - if kwargs['query_level'] == 2: + if kwargs['query_level'] == 2 or kwargs['query_level'] == 0: for w in workers: # Add workers abqm.add_node(node_id=w.node_id, label=ABCPropertyGraph.CLASS_NetworkNode, diff --git a/fabric_cf/actor/test/schema/message.avsc b/fabric_cf/actor/test/schema/message.avsc index d08941b9..a6e96b09 100644 --- a/fabric_cf/actor/test/schema/message.avsc +++ b/fabric_cf/actor/test/schema/message.avsc @@ -638,6 +638,11 @@ "type": ["null", "string"], "default": null }, + { + "name": "site", + "type": ["null", "string"], + "default": null + }, { "name": "sequence", "type": ["null", "int"], @@ -817,6 +822,117 @@ "default": null } ] + }, + { + "namespace": "fabric.cf.model", + "name": "PoaRequest", + "type": "record", + "fields": [{ + "name": "name", + "type": "string" + }, + { + "name": "callback_topic", + "type": "string" + }, + { + "name": "message_id", + "type": "string" + }, + { + "name": "poa_id", + "type": "string" + }, + { + "name": "project_id", + "type": "string" + }, + { + "name": "operation", + "type": "string" + }, + { + "name": "rid", + "type": "string" + }, + { + "name": "slice_id", + "type": "string" + }, + { + "name": "sequence", + "type": "int" + }, + { + "name": "auth", + "type": "fabric.cf.model.AuthRecord" + }, + { + "name": "id_token", + "type": ["null", "string"], + "default": null + }, + { + "name": "vcpu_cpu_map", + "type": ["null", "bytes"], + "default": null + }, + { + "name": "node_set", + "type": ["null", "bytes"], + "default": null + }, + { + "name": "keys", + "type": ["null", "bytes"], + "default": null + } + ] + }, + { + "namespace": "fabric.cf.model", + "type": "record", + "name": "PoaInInfoRecord", + "fields": [ + { + "name": "operation", + "type": "string" + }, + { + "name": "poa_id", + "type": "string" + }, + { + "name": "state", + "type": "string" + }, + { + "name": "rid", + "type": "string" + }, + { + "name": "slice_id", + "type": "string" + }, + { + "name": "project_id", + "type": "string" + }, + { + "name": "error", + "type": ["null", "string"], + "default": null + }, + { + "name": "auth", + "type": "fabric.cf.model.AuthRecord" + }, + { + "name": "info", + "type": ["null", "bytes"], + "default": null + } + ] }, { "namespace": "fabric.cf.model", @@ -874,7 +990,6 @@ }], "default": null }, - { "name": "proxies", "type": ["null", { @@ -888,6 +1003,14 @@ "type": ["null", "fabric.cf.model.BrokerQueryModelRecord"], "default": null }, + { + "name": "poas", + "type": ["null", { + "type":"array", + "items" : "fabric.cf.model.PoaInInfoRecord" + }], + "default": null + }, { "name": "delegations", "type": ["null", { @@ -1073,6 +1196,16 @@ "name": "site", "type": ["null", "string"], "default": null + }, + { + "name": "start", + "type": ["null", "string"], + "default": null + }, + { + "name": "end", + "type": ["null", "string"], + "default": null } ] }, diff --git a/fabric_cf/broker/config.broker.yaml b/fabric_cf/broker/config.broker.yaml index 03fa612f..18218f74 100644 --- a/fabric_cf/broker/config.broker.yaml +++ b/fabric_cf/broker/config.broker.yaml @@ -125,7 +125,7 @@ neo4j: bqm: kafka-topic: broker-resource-usage # in seconds (default set to 2 hours) - publish-interval: 7200 + publish-interval: kafka-sasl-producer-username: kafka-sasl-producer-password: diff --git a/fabric_cf/broker/test/test.yaml b/fabric_cf/broker/test/test.yaml index 15d9f56f..ea7e31f2 100644 --- a/fabric_cf/broker/test/test.yaml +++ b/fabric_cf/broker/test/test.yaml @@ -121,7 +121,7 @@ neo4j: bqm: kafka-topic: broker-resource-usage # in seconds (default set to 2 hours) - publish-interval: 7200 + publish-interval: kafka-sasl-producer-username: kafka-sasl-producer-password: diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index 56aba265..f65b1d6b 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -33,11 +33,13 @@ from fabric_mb.message_bus.messages.poa_avro import PoaAvro from fabric_mb.message_bus.messages.reservation_mng import ReservationMng from fabric_mb.message_bus.messages.slice_avro import SliceAvro +from fim.graph.abc_property_graph import ABCPropertyGraph from fim.graph.networkx_property_graph_disjoint import NetworkXGraphImporterDisjoint from fim.slivers.base_sliver import BaseSliver from fim.slivers.network_service import NetworkServiceSliver -from fim.user import GraphFormat -from fim.user.topology import ExperimentTopology +from fim.user import GraphFormat, Node, NodeType +from fim.user.topology import ExperimentTopology, AdvertizedTopology +from fim.view_only_dict import ViewOnlyDict from fabric_cf.actor.core.common.event_logger import EventLoggerSingleton from fabric_cf.actor.core.kernel.poa import PoaStates @@ -112,9 +114,21 @@ def get_broker(self, *, controller: ABCMgmtControllerMixin) -> ID: except Exception as e: self.logger.error(f"Error occurred: {e}", stack_info=True) + def build_broker_query_model(self, controller: ABCMgmtControllerMixin, level: int, graph_format: GraphFormat = GraphFormat.GRAPHML, + start: datetime = None, end: datetime = None) -> str: + try: + saved_bqm = self.controller_state.get_saved_bqm(graph_format=GraphFormat.GRAPHML, level=0) + if saved_bqm and saved_bqm.get_bqm() and len(saved_bqm.get_bqm()): + + return "" + except Exception as e: + self.logger.error(f"Exception occurred build_broker_query_model e: {e}") + self.logger.error(traceback.format_exc()) + def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, token: str = None, level: int = 10, graph_format: GraphFormat = GraphFormat.GRAPHML, - force_refresh: bool = False, start: datetime = None, end: datetime) -> str or None: + force_refresh: bool = False, start: datetime = None, + end: datetime = None, includes: str = None, excludes: str = None) -> str or None: """ Discover all the available resources by querying Broker :param controller Management Controller Object @@ -124,6 +138,8 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok :param force_refresh: Force fetching a fresh model from Broker :param start: start time :param end: end time + :param includes: comma separated lists of sites to include + :param excludes: comma separated lists of sites to exclude :return str or None """ broker_query_model = None @@ -137,17 +153,28 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok saved_bqm.start_refresh() if broker_query_model is None: - 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, start=start, end=end) - if model is None or model.get_model() is None or model.get_model() == '': - raise OrchestratorException(http_error_code=NOT_FOUND, message=f"Resource(s) not found for " - f"level: {level} format: {graph_format}!") - broker_query_model = model.get_model() + if level != 2: + saved_bqm = self.controller_state.get_saved_bqm(graph_format=GraphFormat.GRAPHML, level=0) + if saved_bqm and saved_bqm.get_bqm() and len(saved_bqm.get_bqm()): + broker_query_model = controller.build_broker_query_model(level_0_broker_query_model=saved_bqm.get_bqm(), + level=level, graph_format=graph_format, + start=start, end=end, includes=includes, + excludes=excludes) + + # Request the model from Broker as a fallback + if not broker_query_model: + 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, start=start, end=end) + if model is None or model.get_model() is None or model.get_model() == '': + raise OrchestratorException(http_error_code=NOT_FOUND, message=f"Resource(s) not found for " + f"level: {level} format: {graph_format}!") + + broker_query_model = model.get_model() # Do not update cache for advance requests if not start and not end: @@ -156,7 +183,7 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok return broker_query_model def list_resources(self, *, token: str, level: int, force_refresh: bool = False, start: datetime = None, - end: datetime) -> dict: + end: datetime, includes: str = None, excludes: str = None) -> str: """ List Resources :param token Fabric Identity Token @@ -164,6 +191,8 @@ def list_resources(self, *, token: str, level: int, force_refresh: bool = False, :param force_refresh: force fetching bqm from broker and override the cached model :param start: start time :param end: end time + :param includes: comma separated lists of sites to include + :param excludes: comma separated lists of sites to exclude :raises Raises an exception in case of failure :returns Broker Query Model on success """ @@ -172,12 +201,10 @@ def list_resources(self, *, token: str, level: int, force_refresh: bool = False, self.logger.debug(f"list_resources invoked controller:{controller}") self.__authorize_request(id_token=token, action_id=ActionId.query) - broker_query_model = self.discover_broker_query_model(controller=controller, token=token, level=level, force_refresh=force_refresh, start=start, - end=end) - - return ResponseBuilder.get_broker_query_model_summary(bqm=broker_query_model) + end=end, includes=includes, excludes=excludes) + return broker_query_model except Exception as e: self.logger.error(traceback.format_exc()) @@ -195,7 +222,6 @@ def portal_list_resources(self, *, graph_format_str: str) -> dict: controller = self.controller_state.get_management_actor() self.logger.debug(f"portal_list_resources invoked controller:{controller}") - broker_query_model = None graph_format = self.__translate_graph_format(graph_format=graph_format_str) broker_query_model = self.discover_broker_query_model(controller=controller, level=1, graph_format=graph_format) diff --git a/fabric_cf/orchestrator/core/orchestrator_kernel.py b/fabric_cf/orchestrator/core/orchestrator_kernel.py index c644ce1a..d7134289 100644 --- a/fabric_cf/orchestrator/core/orchestrator_kernel.py +++ b/fabric_cf/orchestrator/core/orchestrator_kernel.py @@ -156,6 +156,11 @@ def start_threads(self): Start threads :return: """ + if not len(self.bqm_cache): + self.save_bqm(bqm="", graph_format=GraphFormat.GRAPHML, level=0) + saved_bqm = self.get_saved_bqm(graph_format=GraphFormat.GRAPHML, level=0) + saved_bqm.last_query_time = None + from fabric_cf.actor.core.container.globals import GlobalsSingleton GlobalsSingleton.get().get_container().register(tickable=self) diff --git a/fabric_cf/orchestrator/openapi.json b/fabric_cf/orchestrator/openapi.json index 47e9df31..61f401e5 100644 --- a/fabric_cf/orchestrator/openapi.json +++ b/fabric_cf/orchestrator/openapi.json @@ -130,6 +130,30 @@ "type": "string", "example": "2023-01-01 16:20:15 +00:00" } + }, + { + "name": "includes", + "in": "query", + "description": "comma separated lists of sites to include", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "string", + "example": "RENC,UKY" + } + }, + { + "name": "excludes", + "in": "query", + "description": "comma separated lists of sites to exclude", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "string", + "example": "SRI,LBNL" + } } ], "responses": { @@ -2405,7 +2429,7 @@ "requestBodies": { "Request": { "content": { - "text/plain": { + "application/json": { "schema": { "type": "string" } diff --git a/fabric_cf/orchestrator/swagger_server/controllers/resources_controller.py b/fabric_cf/orchestrator/swagger_server/controllers/resources_controller.py index a3c417c9..7c151fc9 100644 --- a/fabric_cf/orchestrator/swagger_server/controllers/resources_controller.py +++ b/fabric_cf/orchestrator/swagger_server/controllers/resources_controller.py @@ -15,7 +15,7 @@ def portalresources_get(graph_format): # noqa: E501 return rc.portalresources_get(graph_format) -def resources_get(level, force_refresh, start_date=None, end_date=None): # noqa: E501 +def resources_get(level, force_refresh, start_date=None, end_date=None, includes=None, excludes=None): # noqa: E501 """Retrieve a listing and description of available resources. By default, a cached available resource information is returned. User can force to request the current available resources. Retrieve a listing and description of available resources. By default, a cached available resource information is returned. User can force to request the current available resources. # noqa: E501 @@ -28,7 +28,12 @@ def resources_get(level, force_refresh, start_date=None, end_date=None): # noqa :type start_date: str :param end_date: end date to check availability until :type end_date: str + :param includes: comma separated lists of sites to include + :type includes: str + :param excludes: comma separated lists of sites to exclude + :type excludes: str :rtype: Resources """ - return rc.resources_get(level=level, force_refresh=force_refresh, start_date=start_date, end_date=end_date) + return rc.resources_get(level=level, force_refresh=force_refresh, start_date=start_date, + end_date=end_date, includes=includes, excludes=excludes) diff --git a/fabric_cf/orchestrator/swagger_server/response/resources_controller.py b/fabric_cf/orchestrator/swagger_server/response/resources_controller.py index 897d07bf..306a036d 100644 --- a/fabric_cf/orchestrator/swagger_server/response/resources_controller.py +++ b/fabric_cf/orchestrator/swagger_server/response/resources_controller.py @@ -65,7 +65,7 @@ def portalresources_get(graph_format) -> Resources: # noqa: E501 def resources_get(level: int = 1, force_refresh: bool = False, start_date: str = None, - end_date: str = None) -> Resources: # noqa: E501 + end_date: str = None, includes: str = None, excludes: str = None) -> Resources: # noqa: E501 """Retrieve a listing and description of available resources Retrieve a listing and description of available resources # noqa: E501 @@ -78,6 +78,10 @@ def resources_get(level: int = 1, force_refresh: bool = False, start_date: str = :type start_date: str :param end_date: end date to check availability until :type end_date: str + :param includes: comma separated lists of sites to include + :type includes: str + :param excludes: comma separated lists of sites to exclude + :type excludes: str :rtype: Resources """ @@ -88,10 +92,10 @@ def resources_get(level: int = 1, force_refresh: bool = False, start_date: str = token = get_token() start = handler.validate_lease_time(lease_time=start_date) end = handler.validate_lease_time(lease_time=end_date) - bqm_dict = handler.list_resources(token=token, level=level, force_refresh=force_refresh, - start=start, end=end) + model = handler.list_resources(token=token, level=level, force_refresh=force_refresh, + start=start, end=end, includes=includes, excludes=excludes) response = Resources() - response.data = [Resource().from_dict(bqm_dict)] + response.data = [Resource(model)] response.size = 1 response.type = "resources" success_counter.labels(GET_METHOD, RESOURCES_PATH).inc() diff --git a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml index b2b6b4dc..87e9381d 100644 --- a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml +++ b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml @@ -94,6 +94,24 @@ paths: schema: type: string example: 2023-01-01 16:20:15 +00:00 + - name: includes + in: query + description: comma separated lists of sites to include + required: false + style: form + explode: true + schema: + type: string + example: "RENC,UKY" + - name: excludes + in: query + description: comma separated lists of sites to exclude + required: false + style: form + explode: true + schema: + type: string + example: "SRI,LBNL" responses: "200": description: OK @@ -283,23 +301,6 @@ paths: type: integer format: int32 default: 0 - - name: search - in: query - description: search term applied - required: false - style: form - explode: true - schema: - type: string - - name: exact_match - in: query - description: Exact Match for Search term - required: false - style: form - explode: true - schema: - type: boolean - default: false responses: "200": description: OK @@ -1636,7 +1637,7 @@ components: requestBodies: Request: content: - text/plain: + application/json: schema: type: string required: true diff --git a/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py index 95672de2..fade5f63 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py @@ -24,7 +24,7 @@ def test_portalresources_get(self): """ query_string = [('graph_format', 'GRAPHML')] response = self.client.open( - '/portalresources', + '//portalresources', method='GET', query_string=query_string) self.assert200(response, @@ -38,9 +38,10 @@ def test_resources_get(self): query_string = [('level', 1), ('force_refresh', false), ('start_date', 'start_date_example'), - ('end_date', 'end_date_example')] + ('end_date', 'end_date_example'), + ('sites', 'sites_example')] response = self.client.open( - '/resources', + '//resources', method='GET', query_string=query_string) self.assert200(response, From b9ecc730963bd7577da0aa6f635f18e7bd337468 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 15 Apr 2024 17:02:26 -0400 Subject: [PATCH 008/133] use bqm plugin at oc --- fabric_cf/actor/core/core/controller.py | 9 ++++ .../core/manage/local/local_controller.py | 2 +- fabric_cf/actor/fim/fim_helper.py | 51 +++++-------------- .../plugins/broker/aggregate_bqm_plugin.py | 29 +++++++++-- fabric_cf/orchestrator/core/bqm_wrapper.py | 2 + .../orchestrator/core/orchestrator_handler.py | 16 +++--- .../orchestrator/core/orchestrator_kernel.py | 1 + 7 files changed, 58 insertions(+), 52 deletions(-) diff --git a/fabric_cf/actor/core/core/controller.py b/fabric_cf/actor/core/core/controller.py index 65f721ce..0ffcfecf 100644 --- a/fabric_cf/actor/core/core/controller.py +++ b/fabric_cf/actor/core/core/controller.py @@ -30,6 +30,8 @@ import traceback from typing import TYPE_CHECKING +from fabric_cf.actor.fim.plugins.broker.aggregate_bqm_plugin import AggregatedBQMPlugin +from fim.pluggable import PluggableRegistry, PluggableType from fim.slivers.base_sliver import BaseSliver from fabric_cf.actor.boot.configuration import ActorConfig @@ -83,6 +85,7 @@ def __init__(self, *, identity: AuthToken = None, clock: ActorClock = None): self.asm_update_thread = AsmUpdateThread(name=f"{self.get_name()}-asm-thread", logger=self.logger) self.thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=2, thread_name_prefix=self.__class__.__name__) + self.pluggable_registry = PluggableRegistry() def __getstate__(self): state = self.__dict__.copy() @@ -107,6 +110,9 @@ def __getstate__(self): del state['asm_update_thread'] del state['event_processors'] del state['thread_pool'] + if hasattr(self, 'pluggable_registry'): + del state['pluggable_registry'] + return state def __setstate__(self, state): @@ -132,12 +138,15 @@ def __setstate__(self, state): self.event_processors = {} self.thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=2, thread_name_prefix=self.__class__.__name__) + self.pluggable_registry = PluggableRegistry() def set_logger(self, logger): super(Controller, self).set_logger(logger=logger) self.asm_update_thread.set_logger(logger=logger) def start(self): + self.pluggable_registry.register_pluggable(t=PluggableType.Broker, p=AggregatedBQMPlugin, actor=self, + logger=self.logger) self.asm_update_thread.set_logger(logger=self.logger) self.asm_update_thread.start() super(Controller, self).start() diff --git a/fabric_cf/actor/core/manage/local/local_controller.py b/fabric_cf/actor/core/manage/local/local_controller.py index efe72f41..a50080df 100644 --- a/fabric_cf/actor/core/manage/local/local_controller.py +++ b/fabric_cf/actor/core/manage/local/local_controller.py @@ -115,7 +115,7 @@ def build_broker_query_model(self, level_0_broker_query_model: str, level: int, try: return self.manager.build_broker_query_model(level_0_broker_query_model=level_0_broker_query_model, level=level, graph_format=graph_format, start=start, - end=end, includes=include, excludes=excludes) + end=end, includes=includes, excludes=excludes) except Exception as e: self.on_exception(e=e, traceback_str=traceback.format_exc()) diff --git a/fabric_cf/actor/fim/fim_helper.py b/fabric_cf/actor/fim/fim_helper.py index db73d526..fc1ac20a 100644 --- a/fabric_cf/actor/fim/fim_helper.py +++ b/fabric_cf/actor/fim/fim_helper.py @@ -26,6 +26,8 @@ import logging import random import traceback +import uuid +from collections import defaultdict from datetime import datetime from typing import Tuple, List, Union @@ -36,15 +38,17 @@ from fim.graph.resources.abc_cbm import ABCCBMPropertyGraph from fim.graph.resources.neo4j_arm import Neo4jARMGraph from fim.graph.resources.neo4j_cbm import Neo4jCBMGraph, Neo4jCBMFactory +from fim.graph.resources.networkx_abqm import NetworkXAggregateBQM from fim.graph.slices.abc_asm import ABCASMPropertyGraph from fim.graph.slices.neo4j_asm import Neo4jASMFactory from fim.graph.slices.networkx_asm import NetworkxASM, NetworkXASMFactory +from fim.pluggable import PluggableRegistry, PluggableType from fim.slivers.attached_components import ComponentSliver from fim.slivers.base_sliver import BaseSliver from fim.slivers.capacities_labels import Capacities from fim.slivers.delegations import Delegations from fim.slivers.interface_info import InterfaceSliver, InterfaceType -from fim.slivers.network_node import NodeSliver +from fim.slivers.network_node import NodeSliver, CompositeNodeSliver from fim.slivers.network_service import NetworkServiceSliver, ServiceType from fim.user import ExperimentTopology, NodeType, Component, ReservationInfo, Node, GraphFormat from fim.user.composite_node import CompositeNode @@ -639,43 +643,12 @@ def build_broker_query_model(db, level_0_broker_query_model: str, level: int, graph_format: GraphFormat = GraphFormat.GRAPHML, start: datetime = None, end: datetime = None, includes: str = None, excludes: str = None) -> str: - sites_to_include = [s.strip().upper() for s in includes.split(",")] if includes else None - sites_to_exclude = [s.strip().upper() for s in excludes.split(",")] if excludes else None - if level_0_broker_query_model and len(level_0_broker_query_model) > 0: - from fabric_cf.actor.fim.plugins.broker.aggregate_bqm_plugin import AggregatedBQMPlugin - substrate = AdvertizedTopology() - substrate.load(graph_string=level_0_broker_query_model) - sites_to_remove = [] - - for site_name, site in substrate.sites.items(): - if sites_to_include and site_name not in sites_to_include: - sites_to_remove.append(site_name) - continue + cbm = FimHelper.get_neo4j_cbm_graph_from_string_direct(graph_str=level_0_broker_query_model) + substrate = cbm.get_bqm(query_level=level, start=start, end=end, includes=includes, + excludes=excludes) - if sites_to_exclude and site_name in sites_to_exclude: - sites_to_remove.append(site_name) - continue - - workers = FimHelper.get_workers(site) - for w in workers.values(): - allocated_caps, allocated_comp_caps = AggregatedBQMPlugin.occupied_node_capacity( - db=db, - node_id=w.node_id, - start=start, - end=end - ) - w.set_property("capacity_allocations", allocated_caps) - # Merge allocated component capacities - for comp_type_comp_model, c in w.components.items(): - cap_allocations = c.capacity_allocations or Capacities() - ctype_caps = allocated_comp_caps.get(c.type) - if ctype_caps: - cm_caps = ctype_caps.get(c.model) - if cm_caps: - cap_allocations += cm_caps - c.set_property("capacity_allocations", cap_allocations) - - for s in sites_to_remove: - substrate.remove_node(s) - return substrate.serialize(fmt=graph_format) + result = substrate.serialize_graph(format=graph_format) + substrate.delete_graph() + cbm.delete_graph() + return result diff --git a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py index 9c2e5258..7b1f7fce 100644 --- a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py +++ b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py @@ -49,6 +49,7 @@ if TYPE_CHECKING: from fabric_cf.actor.core.apis.abc_database import ABCDatabase + class AggregatedBQMPlugin: """ Implement a plugin for simple aggregation of CBM into BQM, transforming site @@ -145,6 +146,12 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope if kwargs.get('query_level', None) is None or kwargs['query_level'] > 2: return cbm.clone_graph(new_graph_id=str(uuid.uuid4())) + includes = kwargs.get('includes', None) + excludes = kwargs.get('excludes', None) + + sites_to_include = [s.strip().upper() for s in includes.split(",")] if includes else [] + sites_to_exclude = [s.strip().upper() for s in excludes.split(",")] if excludes else [] + start = kwargs.get('start', None) end = kwargs.get('end', None) @@ -159,14 +166,23 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope slivers_by_site[node_sliver.site].append(node_sliver) # create a new blank Aggregated BQM NetworkX graph - abqm = NetworkXAggregateBQM(graph_id=str(uuid.uuid4()), - importer=NetworkXGraphImporter(logger=self.logger), - logger=self.logger) + if kwargs['query_level'] == 0: + abqm = NetworkXAggregateBQM(graph_id=cbm.graph_id, + importer=NetworkXGraphImporter(logger=self.logger), + logger=self.logger) + else: + abqm = NetworkXAggregateBQM(graph_id=str(uuid.uuid4()), + importer=NetworkXGraphImporter(logger=self.logger), + logger=self.logger) site_to_composite_node_id = dict() site_to_ns_node_id = dict() facilities_by_site = defaultdict(list) for s, ls in slivers_by_site.items(): + if s not in sites_to_include: + continue + if s in sites_to_exclude: + continue # add up capacities and delegated capacities, skip labels for now # count up components and figure out links between site @@ -206,6 +222,7 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope # for debugging and running in a test environment # also for level 0; only return capacity information allocated_comp_caps = dict() + worker_sliver.node_id = sliver.node_id else: db = self.actor.get_plugin().get_database() # query database for everything taken on this node @@ -232,8 +249,12 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope site_sliver.capacities = site_sliver.capacities + \ delegation.get_details() worker_sliver.capacities = delegation.get_details() + # This for the case when BQM is generated from Orchestrator + else: + site_sliver.capacities += sliver.get_capacities() + worker_sliver.capacities = sliver.get_capacities() - # collect available components in lists by type and model for the site (for later aggregation) + # collect available components in lists by type and model for the site (for later aggregation) if sliver.attached_components_info is None: continue worker_sliver.attached_components_info = AttachedComponentsInfo() diff --git a/fabric_cf/orchestrator/core/bqm_wrapper.py b/fabric_cf/orchestrator/core/bqm_wrapper.py index ebd188ed..8ff87d66 100644 --- a/fabric_cf/orchestrator/core/bqm_wrapper.py +++ b/fabric_cf/orchestrator/core/bqm_wrapper.py @@ -25,6 +25,7 @@ # Author: Komal Thareja (kthare10@renci.org) from datetime import datetime, timezone +from fabric_cf.actor.fim.fim_helper import FimHelper from fim.user import GraphFormat @@ -39,6 +40,7 @@ def __init__(self): self.refresh_interval_in_seconds = 60 self.refresh_in_progress = False self.level = 1 + self.graph_id = None def set_refresh_interval(self, *, refresh_interval: int): """ diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index f65b1d6b..b28ed906 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -153,14 +153,14 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok saved_bqm.start_refresh() if broker_query_model is None: - if level != 2: - saved_bqm = self.controller_state.get_saved_bqm(graph_format=GraphFormat.GRAPHML, level=0) - if saved_bqm and saved_bqm.get_bqm() and len(saved_bqm.get_bqm()): - broker_query_model = controller.build_broker_query_model(level_0_broker_query_model=saved_bqm.get_bqm(), - level=level, graph_format=graph_format, - start=start, end=end, includes=includes, - excludes=excludes) - + ''' + saved_bqm = self.controller_state.get_saved_bqm(graph_format=GraphFormat.GRAPHML, level=0) + if saved_bqm and saved_bqm.get_bqm() and len(saved_bqm.get_bqm()): + broker_query_model = controller.build_broker_query_model(level_0_broker_query_model=saved_bqm.get_bqm(), + level=level, graph_format=graph_format, + start=start, end=end, includes=includes, + excludes=excludes) + ''' # Request the model from Broker as a fallback if not broker_query_model: broker = self.get_broker(controller=controller) diff --git a/fabric_cf/orchestrator/core/orchestrator_kernel.py b/fabric_cf/orchestrator/core/orchestrator_kernel.py index d7134289..18f59a7e 100644 --- a/fabric_cf/orchestrator/core/orchestrator_kernel.py +++ b/fabric_cf/orchestrator/core/orchestrator_kernel.py @@ -92,6 +92,7 @@ def save_bqm(self, *, bqm: str, graph_format: GraphFormat, level: int): saved_bqm.set_refresh_interval(refresh_interval=int(refresh_interval)) saved_bqm.save(bqm=bqm, graph_format=graph_format, level=level) self.bqm_cache[key] = saved_bqm + finally: self.lock.release() From 0d9eafb42bbf3c8c78e065bfe52a73d16d6fd366 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 15 Apr 2024 17:54:39 -0400 Subject: [PATCH 009/133] filter sites from list resources --- .../abc_client_actor_management_object.py | 8 ++++-- .../actor/core/apis/abc_mgmt_client_actor.py | 27 ++++++++++-------- fabric_cf/actor/core/common/constants.py | 3 ++ fabric_cf/actor/core/core/broker_policy.py | 12 ++++++-- .../client_actor_management_object_helper.py | 6 ++-- .../actor/core/manage/kafka/kafka_broker.py | 3 +- .../core/manage/kafka/kafka_controller.py | 28 ------------------- .../core/manage/local/local_controller.py | 5 ++-- .../policy/broker_simpler_units_policy.py | 6 +++- .../orchestrator/config.orchestrator.yaml | 1 + .../orchestrator/core/orchestrator_handler.py | 17 +++++------ 11 files changed, 59 insertions(+), 57 deletions(-) diff --git a/fabric_cf/actor/core/apis/abc_client_actor_management_object.py b/fabric_cf/actor/core/apis/abc_client_actor_management_object.py index a3d95efd..d7e9a14b 100644 --- a/fabric_cf/actor/core/apis/abc_client_actor_management_object.py +++ b/fabric_cf/actor/core/apis/abc_client_actor_management_object.py @@ -129,7 +129,8 @@ def update_broker(self, *, broker: ProxyAvro, caller: AuthToken) -> ResultAvro: @abstractmethod def get_broker_query_model(self, *, broker: ID, caller: AuthToken, id_token: str, level: int, graph_format: GraphFormat, start: datetime = None, - end: datetime = None) -> ResultBrokerQueryModelAvro: + end: datetime = None, includes: str = None, + excludes: str = None) -> ResultBrokerQueryModelAvro: """ Get Pool Info @param broker : broker ID @@ -139,7 +140,10 @@ def get_broker_query_model(self, *, broker: ID, caller: AuthToken, id_token: str @param graph_format: Graph Format @param start: start time @param end: end time - @return pool information + @param includes: comma separated lists of sites to include + @param excludes: comma separated lists of sites to exclude + + @return resource information """ @abstractmethod diff --git a/fabric_cf/actor/core/apis/abc_mgmt_client_actor.py b/fabric_cf/actor/core/apis/abc_mgmt_client_actor.py index 7762c1c3..6476a05a 100644 --- a/fabric_cf/actor/core/apis/abc_mgmt_client_actor.py +++ b/fabric_cf/actor/core/apis/abc_mgmt_client_actor.py @@ -29,6 +29,9 @@ from datetime import datetime from typing import TYPE_CHECKING, List +from fabric_cf.actor.core.common.exceptions import ManageException + +from fabric_cf.actor.core.common.constants import Constants from fabric_mb.message_bus.messages.delegation_avro import DelegationAvro from fabric_mb.message_bus.messages.broker_query_model_avro import BrokerQueryModelAvro from fabric_mb.message_bus.messages.reservation_predecessor_avro import ReservationPredecessorAvro @@ -49,7 +52,6 @@ class ABCMgmtClientActor(ABCComponent): """ Implements base class for Management Interface for a Client Actor """ - @abstractmethod def add_reservation(self, *, reservation: TicketReservationAvro) -> ID: """ Adds the reservation to the actor's state and returns the assigned reservation ID. @@ -58,8 +60,8 @@ def add_reservation(self, *, reservation: TicketReservationAvro) -> ID: @param reservation reservation @return null on failure, assigned reservation ID otherwise """ + raise ManageException(Constants.NOT_IMPLEMENTED) - @abstractmethod def add_reservations(self, *, reservations: List[ReservationMng])->list: """ Adds all reservations to the actor's state and returns the assigned reservation ID. @@ -69,8 +71,8 @@ def add_reservations(self, *, reservations: List[ReservationMng])->list: @param reservations reservation @return null on failure, list of assigned ReservationIDs on success. """ + raise ManageException(Constants.NOT_IMPLEMENTED) - @abstractmethod def demand_reservation_rid(self, *, rid: ID) -> bool: """ Demands the specified reservation. @@ -78,8 +80,8 @@ def demand_reservation_rid(self, *, rid: ID) -> bool: @param rid reservation id @return true for success; false otherwise """ + raise ManageException(Constants.NOT_IMPLEMENTED) - @abstractmethod def demand_reservation(self, *, reservation: ReservationMng) -> bool: """ Updates the reservation and issues a demand for it. @@ -89,6 +91,7 @@ def demand_reservation(self, *, reservation: ReservationMng) -> bool: @param reservation reservation @return true for success; false otherwise """ + raise ManageException(Constants.NOT_IMPLEMENTED) @abstractmethod def get_brokers(self, *, broker: ID = None, id_token: str = None) -> List[ProxyAvro]: @@ -114,10 +117,9 @@ def update_broker(self, *, broker: ProxyAvro) -> bool: @param broker broker @return true for sucess; false otherwise """ - - @abstractmethod def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, graph_format: GraphFormat, - start: datetime = None, end: datetime = None) -> BrokerQueryModelAvro: + start: datetime = None, end: datetime = None, + includes: str = None, excludes: str = None) -> BrokerQueryModelAvro: """ Obtains the resources available at the specified broker @param broker broker @@ -126,8 +128,11 @@ def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, graph @param graph_format: Graph Format @param start: start time @param end: end time + @param includes: comma separated lists of sites to include + @param excludes: comma separated lists of sites to exclude @return BQM """ + raise ManageException(Constants.NOT_IMPLEMENTED) def build_broker_query_model(self, level_0_broker_query_model: str, level: int, graph_format: GraphFormat = GraphFormat.GRAPHML, @@ -144,9 +149,8 @@ def build_broker_query_model(self, level_0_broker_query_model: str, level: int, @param excludes: comma separated lists of sites to exclude @return BQM """ - raise Exception("Not Implemented") + raise ManageException(Constants.NOT_IMPLEMENTED) - @abstractmethod def extend_reservation(self, *, reservation: ID, new_end_time: datetime, sliver: BaseSliver, dependencies: List[ReservationPredecessorAvro] = None) -> bool: """ @@ -157,8 +161,8 @@ def extend_reservation(self, *, reservation: ID, new_end_time: datetime, sliver: @param dependencies: Dependency reservations @return true for success and false for failure """ + raise ManageException(Constants.NOT_IMPLEMENTED) - @abstractmethod def claim_delegations(self, *, broker: ID, did: ID) -> DelegationAvro: """ Claims delegations exported by the specified broker @@ -166,8 +170,8 @@ def claim_delegations(self, *, broker: ID, did: ID) -> DelegationAvro: @param did reservation id @return reservation """ + raise ManageException(Constants.NOT_IMPLEMENTED) - @abstractmethod def reclaim_delegations(self, *, broker: ID, did: ID) -> DelegationAvro: """ Reclaim delegations exported by the specified broker @@ -175,3 +179,4 @@ def reclaim_delegations(self, *, broker: ID, did: ID) -> DelegationAvro: @param did reservation id @return reservation """ + raise ManageException(Constants.NOT_IMPLEMENTED) diff --git a/fabric_cf/actor/core/common/constants.py b/fabric_cf/actor/core/common/constants.py index 7acc91bb..7ee085ff 100644 --- a/fabric_cf/actor/core/common/constants.py +++ b/fabric_cf/actor/core/common/constants.py @@ -123,6 +123,7 @@ class Constants: PUBLISH_INTERVAL = "publish-interval" REFRESH_INTERVAL = "refresh-interval" DELEGATION = "delegation" + LOCAL_BQM = "local" PROPERTY_CLASS_NAME = "ObjectClassName" PROPERTY_MODULE_NAME = "ModuleName" @@ -213,6 +214,8 @@ class Constants: START = "start" END = "end" POOL_TYPE = "neo4j" + INCLUDES = "includes" + EXCLUDES = "excludes" UNIT_MODIFY_PROP_MESSAGE_SUFFIX = ".message" UNIT_MODIFY_PROP_CODE_SUFFIX = ".code" diff --git a/fabric_cf/actor/core/core/broker_policy.py b/fabric_cf/actor/core/core/broker_policy.py index 11cae52b..82be41b8 100644 --- a/fabric_cf/actor/core/core/broker_policy.py +++ b/fabric_cf/actor/core/core/broker_policy.py @@ -129,22 +129,30 @@ def get_client_id(self, *, reservation: ABCServerReservation) -> ID: @staticmethod def get_broker_query_model_query(*, level: int, bqm_format: GraphFormat = GraphFormat.GRAPHML, - start: datetime = None, end: datetime = None) -> dict: + start: datetime = None, end: datetime = None, + includes: str = None, excludes: str = None) -> dict: """ Return dictionary representing query :param level: Graph Level :param bqm_format: Graph Format :param start: start time :param end: end time + :param includes: comma separated lists of sites to include + :param excludes: comma separated lists of sites to exclude + :return dictionary representing the query """ properties = {Constants.QUERY_ACTION: Constants.QUERY_ACTION_DISCOVER_BQM, Constants.QUERY_DETAIL_LEVEL: str(level), - Constants.BROKER_QUERY_MODEL_FORMAT: str(bqm_format.value)} + Constants.BROKER_QUERY_MODEL_FORMAT: str(bqm_format.value),} if start: properties[Constants.START] = start.strftime(Constants.LEASE_TIME_FORMAT) if end: properties[Constants.END] = end.strftime(Constants.LEASE_TIME_FORMAT) + if includes: + properties[Constants.INCLUDES] = includes + if includes: + properties[Constants.EXCLUDES] = excludes return properties @staticmethod diff --git a/fabric_cf/actor/core/manage/client_actor_management_object_helper.py b/fabric_cf/actor/core/manage/client_actor_management_object_helper.py index a60c3d55..c40b327c 100644 --- a/fabric_cf/actor/core/manage/client_actor_management_object_helper.py +++ b/fabric_cf/actor/core/manage/client_actor_management_object_helper.py @@ -153,7 +153,8 @@ def update_broker(self, *, broker: ProxyAvro, caller: AuthToken) -> ResultAvro: def get_broker_query_model(self, *, broker: ID, caller: AuthToken, id_token: str, level: int, graph_format: GraphFormat, start: datetime = None, - end: datetime = None) -> ResultBrokerQueryModelAvro: + end: datetime = None, includes: str = None, + excludes: str = None) -> ResultBrokerQueryModelAvro: result = ResultBrokerQueryModelAvro() result.status = ResultAvro() @@ -167,7 +168,8 @@ def get_broker_query_model(self, *, broker: ID, caller: AuthToken, id_token: str b = self.client.get_broker(guid=broker) if b is not None: request = BrokerPolicy.get_broker_query_model_query(level=level, bqm_format=graph_format, - start=start, end=end) + start=start, end=end, includes=includes, + excludes=excludes) response = ManagementUtils.query(actor=self.client, actor_proxy=b, query=request) result.model = Translate.translate_to_broker_query_model(query_response=response, level=level) else: diff --git a/fabric_cf/actor/core/manage/kafka/kafka_broker.py b/fabric_cf/actor/core/manage/kafka/kafka_broker.py index efc2ef7e..39f66936 100644 --- a/fabric_cf/actor/core/manage/kafka/kafka_broker.py +++ b/fabric_cf/actor/core/manage/kafka/kafka_broker.py @@ -110,7 +110,8 @@ def get_brokers(self, *, broker: ID = None, id_token: str = None) -> List[ProxyA return response.proxies def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, graph_format: GraphFormat, - start: datetime = None, end: datetime = None) -> BrokerQueryModelAvro: + start: datetime = None, end: datetime = None, + includes: str = None, excludes: str = None) -> BrokerQueryModelAvro: request = GetBrokerQueryModelRequestAvro() request.id_token = id_token request.guid = str(self.management_id) diff --git a/fabric_cf/actor/core/manage/kafka/kafka_controller.py b/fabric_cf/actor/core/manage/kafka/kafka_controller.py index f598b9dd..6dbc0a02 100644 --- a/fabric_cf/actor/core/manage/kafka/kafka_controller.py +++ b/fabric_cf/actor/core/manage/kafka/kafka_controller.py @@ -71,17 +71,6 @@ def get_brokers(self, *, broker: ID = None, id_token: str = None) -> List[ProxyA if status.code == 0: return response.proxies - return None - - def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, graph_format: GraphFormat, - start: datetime = None, end: datetime = None) -> BrokerQueryModelAvro: - raise ManageException(Constants.NOT_IMPLEMENTED) - - def claim_delegations(self, *, broker: ID, did: ID) -> DelegationAvro: - raise ManageException(Constants.NOT_IMPLEMENTED) - - def reclaim_delegations(self, *, broker: ID, did: ID) -> DelegationAvro: - raise ManageException(Constants.NOT_IMPLEMENTED) def get_reservation_units(self, *, rid: ID, id_token: str = None) -> List[UnitAvro]: request = GetReservationUnitsRequestAvro() @@ -90,23 +79,6 @@ def get_reservation_units(self, *, rid: ID, id_token: str = None) -> List[UnitAv if status.code == 0: return response.units - return None - - def add_reservation(self, *, reservation: TicketReservationAvro) -> ID: - raise ManageException(Constants.NOT_IMPLEMENTED) - - def add_reservations(self, *, reservations: List[ReservationMng]) ->list: - raise ManageException(Constants.NOT_IMPLEMENTED) - - def demand_reservation(self, *, reservation: ReservationMng) -> bool: - raise ManageException(Constants.NOT_IMPLEMENTED) - - def demand_reservation_rid(self, *, rid: ID) -> bool: - raise ManageException(Constants.NOT_IMPLEMENTED) - - def extend_reservation(self, *, reservation: ID, new_end_time: datetime, sliver: BaseSliver, - dependencies: List[ReservationPredecessorAvro] = None) -> bool: - raise ManageException(Constants.NOT_IMPLEMENTED) def modify_reservation(self, *, rid: ID, modify_properties: dict) -> bool: raise ManageException(Constants.NOT_IMPLEMENTED) diff --git a/fabric_cf/actor/core/manage/local/local_controller.py b/fabric_cf/actor/core/manage/local/local_controller.py index a50080df..737c37a7 100644 --- a/fabric_cf/actor/core/manage/local/local_controller.py +++ b/fabric_cf/actor/core/manage/local/local_controller.py @@ -94,12 +94,13 @@ def get_brokers(self, *, broker: ID = None, id_token: str = None) -> List[ProxyA self.on_exception(e=e, traceback_str=traceback.format_exc()) def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, graph_format: GraphFormat, - start: datetime = None, end: datetime = None) -> BrokerQueryModelAvro: + start: datetime = None, end: datetime = None, + includes: str = None, excludes: str = None) -> BrokerQueryModelAvro: self.clear_last() try: result = self.manager.get_broker_query_model(broker=broker, caller=self.auth, id_token=id_token, level=level, graph_format=graph_format, - start=start, end=end) + start=start, end=end, excludes=excludes, includes=includes) self.last_status = result.status if result.status.get_code() == 0: diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 7565ed10..9243cd9b 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -1068,9 +1068,13 @@ def query(self, *, p: dict) -> dict: if end: end = datetime.strptime(end, Constants.LEASE_TIME_FORMAT) + excludes = p.get(Constants.EXCLUDES, None) + includes = p.get(Constants.INCLUDES, None) + try: if self.query_cbm is not None: - graph = self.query_cbm.get_bqm(query_level=query_level, start=start, end=end) + graph = self.query_cbm.get_bqm(query_level=query_level, start=start, end=end, includes=includes, + excludes=excludes) graph_string = None if graph is not None: graph_string = graph.serialize_graph(format=bqm_format) diff --git a/fabric_cf/orchestrator/config.orchestrator.yaml b/fabric_cf/orchestrator/config.orchestrator.yaml index 62a435f4..f3534867 100644 --- a/fabric_cf/orchestrator/config.orchestrator.yaml +++ b/fabric_cf/orchestrator/config.orchestrator.yaml @@ -107,6 +107,7 @@ container: bqm: # in seconds (default set to 300 seconds) refresh-interval: 300 + local: True time: # This section controls settings, which are generally useful diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index b28ed906..0bd4744b 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -67,6 +67,8 @@ def __init__(self): self.jwks_url = self.globals.get_config().get_oauth_config().get(Constants.PROPERTY_CONF_O_AUTH_JWKS_URL, None) self.pdp_config = self.globals.get_config().get_global_config().get_pdp_config() self.infrastructure_project_id = self.globals.get_config().get_runtime_config().get(Constants.INFRASTRUCTURE_PROJECT_ID, None) + self.local_bqm = self.globals.get_config().get_global_config().get_bqm_config().get( + Constants.LOCAL_BQM, False) def get_logger(self): """ @@ -153,14 +155,13 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok saved_bqm.start_refresh() if broker_query_model is None: - ''' - saved_bqm = self.controller_state.get_saved_bqm(graph_format=GraphFormat.GRAPHML, level=0) - if saved_bqm and saved_bqm.get_bqm() and len(saved_bqm.get_bqm()): - broker_query_model = controller.build_broker_query_model(level_0_broker_query_model=saved_bqm.get_bqm(), - level=level, graph_format=graph_format, - start=start, end=end, includes=includes, - excludes=excludes) - ''' + if self.local_bqm: + saved_bqm = self.controller_state.get_saved_bqm(graph_format=GraphFormat.GRAPHML, level=0) + if saved_bqm and saved_bqm.get_bqm() and len(saved_bqm.get_bqm()): + broker_query_model = controller.build_broker_query_model(level_0_broker_query_model=saved_bqm.get_bqm(), + level=level, graph_format=graph_format, + start=start, end=end, includes=includes, + excludes=excludes) # Request the model from Broker as a fallback if not broker_query_model: broker = self.get_broker(controller=controller) From f3c6076072f4b9abacadf26b2fecb3dcd6a4d78b Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 15 Apr 2024 18:08:08 -0400 Subject: [PATCH 010/133] filter sites from list resources --- fabric_cf/actor/core/apis/abc_mgmt_client_actor.py | 1 + fabric_cf/orchestrator/core/orchestrator_handler.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/fabric_cf/actor/core/apis/abc_mgmt_client_actor.py b/fabric_cf/actor/core/apis/abc_mgmt_client_actor.py index 6476a05a..01a3473d 100644 --- a/fabric_cf/actor/core/apis/abc_mgmt_client_actor.py +++ b/fabric_cf/actor/core/apis/abc_mgmt_client_actor.py @@ -117,6 +117,7 @@ def update_broker(self, *, broker: ProxyAvro) -> bool: @param broker broker @return true for sucess; false otherwise """ + def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, graph_format: GraphFormat, start: datetime = None, end: datetime = None, includes: str = None, excludes: str = None) -> BrokerQueryModelAvro: diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index 0bd4744b..9db671ef 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -170,7 +170,8 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok "Please check Orchestrator container configuration and logs.") model = controller.get_broker_query_model(broker=broker, id_token=token, level=level, - graph_format=graph_format, start=start, end=end) + graph_format=graph_format, start=start, end=end, + includes=includes, excludes=excludes) if model is None or model.get_model() is None or model.get_model() == '': raise OrchestratorException(http_error_code=NOT_FOUND, message=f"Resource(s) not found for " f"level: {level} format: {graph_format}!") From 7193b11c2a90486134fca006dd0ff2c2ba712e05 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 15 Apr 2024 18:26:55 -0400 Subject: [PATCH 011/133] filter sites from list resources --- fabric_cf/actor/core/manage/broker_management_object.py | 6 ++++-- fabric_cf/actor/core/manage/controller_management_object.py | 6 ++++-- fabric_cf/actor/core/manage/local/local_controller.py | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/fabric_cf/actor/core/manage/broker_management_object.py b/fabric_cf/actor/core/manage/broker_management_object.py index 12223e2b..04b95402 100644 --- a/fabric_cf/actor/core/manage/broker_management_object.py +++ b/fabric_cf/actor/core/manage/broker_management_object.py @@ -99,9 +99,11 @@ def update_broker(self, *, broker: ProxyAvro, caller: AuthToken) -> ResultAvro: def get_broker_query_model(self, *, broker: ID, caller: AuthToken, id_token: str, level: int, graph_format: GraphFormat, start: datetime = None, - end: datetime = None) -> ResultBrokerQueryModelAvro: + end: datetime = None, includes: str = None, + excludes: str = None) -> ResultBrokerQueryModelAvro: return self.client_helper.get_broker_query_model(broker=broker, caller=caller, id_token=id_token, level=level, - graph_format=graph_format, start=start, end=end) + graph_format=graph_format, start=start, end=end, + includes=includes, excludes=excludes) def add_reservation(self, *, reservation: TicketReservationAvro, caller: AuthToken) -> ResultStringAvro: return self.client_helper.add_reservation(reservation=reservation, caller=caller) diff --git a/fabric_cf/actor/core/manage/controller_management_object.py b/fabric_cf/actor/core/manage/controller_management_object.py index e6c7c3c5..ae89609d 100644 --- a/fabric_cf/actor/core/manage/controller_management_object.py +++ b/fabric_cf/actor/core/manage/controller_management_object.py @@ -103,9 +103,11 @@ def update_broker(self, *, broker: ProxyAvro, caller: AuthToken) -> ResultAvro: def get_broker_query_model(self, *, broker: ID, caller: AuthToken, id_token: str, level: int, graph_format: GraphFormat, start: datetime = None, - end: datetime = None) -> ResultBrokerQueryModelAvro: + end: datetime = None, includes: str = None, + excludes: str = None) -> ResultBrokerQueryModelAvro: return self.client_helper.get_broker_query_model(broker=broker, caller=caller, id_token=id_token, level=level, - graph_format=graph_format, start=start, end=end) + graph_format=graph_format, start=start, end=end, + includes=includes, excludes=excludes) def add_reservation(self, *, reservation: TicketReservationAvro, caller: AuthToken) -> ResultStringAvro: return self.client_helper.add_reservation(reservation=reservation, caller=caller) diff --git a/fabric_cf/actor/core/manage/local/local_controller.py b/fabric_cf/actor/core/manage/local/local_controller.py index 737c37a7..9211910b 100644 --- a/fabric_cf/actor/core/manage/local/local_controller.py +++ b/fabric_cf/actor/core/manage/local/local_controller.py @@ -106,6 +106,7 @@ def get_broker_query_model(self, *, broker: ID, id_token: str, level: int, graph if result.status.get_code() == 0: return result.model except Exception as e: + print(e) self.on_exception(e=e, traceback_str=traceback.format_exc()) def build_broker_query_model(self, level_0_broker_query_model: str, level: int, From 42b612dbdbef8422f629518831ee962f978d3e53 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 16 Apr 2024 21:13:56 -0400 Subject: [PATCH 012/133] fix include/exclude check --- fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py index 7b1f7fce..6f90ebab 100644 --- a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py +++ b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py @@ -179,9 +179,9 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope site_to_ns_node_id = dict() facilities_by_site = defaultdict(list) for s, ls in slivers_by_site.items(): - if s not in sites_to_include: + if len(sites_to_include) and s not in sites_to_include: continue - if s in sites_to_exclude: + if len(sites_to_exclude) and s in sites_to_exclude: continue # add up capacities and delegated capacities, skip labels for now # count up components and figure out links between site From 376d3f7fe85ad9c03c064fd7ee1851f2a379c96a Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 16 Apr 2024 21:41:46 -0400 Subject: [PATCH 013/133] filter links for the filtered sites --- fabric_cf/actor/core/core/broker_policy.py | 2 +- fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/fabric_cf/actor/core/core/broker_policy.py b/fabric_cf/actor/core/core/broker_policy.py index 82be41b8..85b40a1a 100644 --- a/fabric_cf/actor/core/core/broker_policy.py +++ b/fabric_cf/actor/core/core/broker_policy.py @@ -151,7 +151,7 @@ def get_broker_query_model_query(*, level: int, bqm_format: GraphFormat = GraphF properties[Constants.END] = end.strftime(Constants.LEASE_TIME_FORMAT) if includes: properties[Constants.INCLUDES] = includes - if includes: + if excludes: properties[Constants.EXCLUDES] = excludes return properties diff --git a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py index 6f90ebab..208eb5c5 100644 --- a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py +++ b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py @@ -361,6 +361,11 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope sink_site = l[4] source_cp = l[5] sink_cp = l[6] + # Exclude the sites requested to be filtered + if includes and (source_site not in includes or sink_site not in includes): + continue + if excludes and (source_site in excludes or sink_site not in excludes): + continue _, cbm_source_cp_props = cbm.get_node_properties(node_id=source_cp) _, cbm_sink_cp_props = cbm.get_node_properties(node_id=sink_cp) _, cbm_link_props = cbm.get_node_properties(node_id=link) From 451cacffa482ff795141d16771b783ac93345e70 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 16 Apr 2024 21:46:49 -0400 Subject: [PATCH 014/133] fix the filtering --- fabric_cf/orchestrator/core/orchestrator_handler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index 9db671ef..5a58116a 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -146,7 +146,7 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok """ broker_query_model = None # Always get Fresh copy for advanced resource requests - if not start and not end: + if not start and not end and not includes and not excludes: saved_bqm = self.controller_state.get_saved_bqm(graph_format=graph_format, level=level) if saved_bqm is not None: if not force_refresh and not saved_bqm.can_refresh() and not saved_bqm.refresh_in_progress: @@ -179,7 +179,7 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok broker_query_model = model.get_model() # Do not update cache for advance requests - if not start and not end: + if not start and not end and not includes and not excludes: self.controller_state.save_bqm(bqm=broker_query_model, graph_format=graph_format, level=level) return broker_query_model From 83aedbbce556b2d268c6f3316df5f3a37bffaf54 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 16 Apr 2024 22:30:39 -0400 Subject: [PATCH 015/133] update portal resources to support time based queries --- .../orchestrator/core/orchestrator_handler.py | 36 +++------- fabric_cf/orchestrator/openapi.json | 72 +++++++++++++++++++ .../controllers/resources_controller.py | 17 ++++- .../response/resources_controller.py | 22 +++++- .../swagger_server/swagger/swagger.yaml | 54 ++++++++++++++ .../test/test_resources_controller.py | 25 ++++--- pyproject.toml | 2 +- 7 files changed, 188 insertions(+), 40 deletions(-) diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index 5a58116a..97ef59c8 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -184,8 +184,9 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok return broker_query_model - def list_resources(self, *, token: str, level: int, force_refresh: bool = False, start: datetime = None, - end: datetime, includes: str = None, excludes: str = None) -> str: + def list_resources(self, *, level: int, force_refresh: bool = False, start: datetime = None, + end: datetime, includes: str = None, excludes: str = None, graph_format_str: str = None, + token: str = None, authorize: bool = True) -> str: """ List Resources :param token Fabric Identity Token @@ -195,6 +196,8 @@ def list_resources(self, *, token: str, level: int, force_refresh: bool = False, :param end: end time :param includes: comma separated lists of sites to include :param excludes: comma separated lists of sites to exclude + :param graph_format_str: Graph format + :param authorize: Authorize the request; Not authorized for Portal requests :raises Raises an exception in case of failure :returns Broker Query Model on success """ @@ -202,10 +205,14 @@ def list_resources(self, *, token: str, level: int, force_refresh: bool = False, controller = self.controller_state.get_management_actor() self.logger.debug(f"list_resources invoked controller:{controller}") - self.__authorize_request(id_token=token, action_id=ActionId.query) + graph_format = self.__translate_graph_format(graph_format=graph_format_str) if graph_format_str else None + + if authorize: + self.__authorize_request(id_token=token, action_id=ActionId.query) broker_query_model = self.discover_broker_query_model(controller=controller, token=token, level=level, force_refresh=force_refresh, start=start, - end=end, includes=includes, excludes=excludes) + end=end, includes=includes, excludes=excludes, + graph_format=graph_format) return broker_query_model except Exception as e: @@ -213,27 +220,6 @@ def list_resources(self, *, token: str, level: int, force_refresh: bool = False, self.logger.error(f"Exception occurred processing list_resources e: {e}") raise e - def portal_list_resources(self, *, graph_format_str: str) -> dict: - """ - List Resources - :param graph_format_str: Graph format - :raises Raises an exception in case of failure - :returns Broker Query Model on success - """ - try: - controller = self.controller_state.get_management_actor() - self.logger.debug(f"portal_list_resources invoked controller:{controller}") - - graph_format = self.__translate_graph_format(graph_format=graph_format_str) - broker_query_model = self.discover_broker_query_model(controller=controller, level=1, - graph_format=graph_format) - return ResponseBuilder.get_broker_query_model_summary(bqm=broker_query_model) - - except Exception as e: - self.logger.error(traceback.format_exc()) - self.logger.error(f"Exception occurred processing portal_list_resources e: {e}") - raise e - def create_slice(self, *, token: str, slice_name: str, slice_graph: str, ssh_key: str, lease_end_time: str) -> List[dict]: """ diff --git a/fabric_cf/orchestrator/openapi.json b/fabric_cf/orchestrator/openapi.json index 61f401e5..4a518f43 100644 --- a/fabric_cf/orchestrator/openapi.json +++ b/fabric_cf/orchestrator/openapi.json @@ -249,6 +249,78 @@ "CYTOSCAPE" ] } + }, + { + "name": "level", + "in": "query", + "description": "Level of details", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "force_refresh", + "in": "query", + "description": "Force to retrieve current available resource information.", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "boolean", + "default": false + } + }, + { + "name": "start_date", + "in": "query", + "description": "starting date to check availability from", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "string", + "example": "2023-01-01 16:20:15 +00:00" + } + }, + { + "name": "end_date", + "in": "query", + "description": "end date to check availability until", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "string", + "example": "2023-01-01 16:20:15 +00:00" + } + }, + { + "name": "includes", + "in": "query", + "description": "comma separated lists of sites to include", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "string", + "example": "RENC,UKY" + } + }, + { + "name": "excludes", + "in": "query", + "description": "comma separated lists of sites to exclude", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "string", + "example": "SRI,LBNL" + } } ], "responses": { diff --git a/fabric_cf/orchestrator/swagger_server/controllers/resources_controller.py b/fabric_cf/orchestrator/swagger_server/controllers/resources_controller.py index 7c151fc9..3ea4d46c 100644 --- a/fabric_cf/orchestrator/swagger_server/controllers/resources_controller.py +++ b/fabric_cf/orchestrator/swagger_server/controllers/resources_controller.py @@ -2,17 +2,30 @@ from fabric_cf.orchestrator.swagger_server.response import resources_controller as rc -def portalresources_get(graph_format): # noqa: E501 +def portalresources_get(graph_format, level=None, force_refresh=None, start_date=None, end_date=None, includes=None, excludes=None): # noqa: E501 """Retrieve a listing and description of available resources for portal Retrieve a listing and description of available resources for portal # noqa: E501 :param graph_format: graph format :type graph_format: str + :param level: Level of details + :type level: int + :param force_refresh: Force to retrieve current available resource information. + :type force_refresh: bool + :param start_date: starting date to check availability from + :type start_date: str + :param end_date: end date to check availability until + :type end_date: str + :param includes: comma separated lists of sites to include + :type includes: str + :param excludes: comma separated lists of sites to exclude + :type excludes: str :rtype: Resources """ - return rc.portalresources_get(graph_format) + return rc.portalresources_get(graph_format=graph_format, level=level, force_refresh=force_refresh, + start_date=start_date, end_date=end_date, includes=includes, excludes=excludes) def resources_get(level, force_refresh, start_date=None, end_date=None, includes=None, excludes=None): # noqa: E501 diff --git a/fabric_cf/orchestrator/swagger_server/response/resources_controller.py b/fabric_cf/orchestrator/swagger_server/response/resources_controller.py index 306a036d..942da8b7 100644 --- a/fabric_cf/orchestrator/swagger_server/response/resources_controller.py +++ b/fabric_cf/orchestrator/swagger_server/response/resources_controller.py @@ -33,13 +33,26 @@ from fabric_cf.orchestrator.swagger_server.response.utils import get_token, cors_error_response, cors_success_response -def portalresources_get(graph_format) -> Resources: # noqa: E501 +def portalresources_get(graph_format: str, level: int = 1, force_refresh: bool = False, start_date: str = None, + end_date: str = None, includes: str = None, excludes: str = None) -> Resources: # noqa: E501 """Retrieve a listing and description of available resources for portal Retrieve a listing and description of available resources for portal # noqa: E501 :param graph_format: graph format :type graph_format: str + :param level: Level of details + :type level: int + :param force_refresh: Force to retrieve current available resource information. + :type force_refresh: bool + :param start_date: starting date to check availability from + :type start_date: str + :param end_date: end date to check availability until + :type end_date: str + :param includes: comma separated lists of sites to include + :type includes: str + :param excludes: comma separated lists of sites to exclude + :type excludes: str :rtype: Resources """ @@ -47,9 +60,12 @@ def portalresources_get(graph_format) -> Resources: # noqa: E501 logger = handler.get_logger() received_counter.labels(GET_METHOD, PORTAL_RESOURCES_PATH).inc() try: - bqm_dict = handler.portal_list_resources(graph_format_str=graph_format) + start = handler.validate_lease_time(lease_time=start_date) + end = handler.validate_lease_time(lease_time=end_date) + model = handler.list_resources(graph_format_str=graph_format, level=level, force_refresh=force_refresh, + start=start, end=end, includes=includes, excludes=excludes) response = Resources() - response.data = [Resource().from_dict(bqm_dict)] + response.data = [Resource(model)] response.size = 1 response.type = "resources" success_counter.labels(GET_METHOD, PORTAL_RESOURCES_PATH).inc() diff --git a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml index 87e9381d..87907407 100644 --- a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml +++ b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml @@ -173,6 +173,60 @@ paths: - JSON_NODELINK - CYTOSCAPE default: GRAPHML + - name: level + in: query + description: Level of details + required: false + style: form + explode: true + schema: + type: integer + default: 1 + - name: force_refresh + in: query + description: Force to retrieve current available resource information. + required: false + style: form + explode: true + schema: + type: boolean + default: false + - name: start_date + in: query + description: starting date to check availability from + required: false + style: form + explode: true + schema: + type: string + example: 2023-01-01 16:20:15 +00:00 + - name: end_date + in: query + description: end date to check availability until + required: false + style: form + explode: true + schema: + type: string + example: 2023-01-01 16:20:15 +00:00 + - name: includes + in: query + description: comma separated lists of sites to include + required: false + style: form + explode: true + schema: + type: string + example: "RENC,UKY" + - name: excludes + in: query + description: comma separated lists of sites to exclude + required: false + style: form + explode: true + schema: + type: string + example: "SRI,LBNL" responses: "200": description: OK diff --git a/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py index fade5f63..7278190d 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py @@ -5,13 +5,13 @@ from flask import json from six import BytesIO -from fabric_cf.orchestrator.swagger_server.models.resources import Resources # noqa: E501 -from fabric_cf.orchestrator.swagger_server.models.status400_bad_request import Status400BadRequest # noqa: E501 -from fabric_cf.orchestrator.swagger_server.models.status401_unauthorized import Status401Unauthorized # noqa: E501 -from fabric_cf.orchestrator.swagger_server.models.status403_forbidden import Status403Forbidden # noqa: E501 -from fabric_cf.orchestrator.swagger_server.models.status404_not_found import Status404NotFound # noqa: E501 -from fabric_cf.orchestrator.swagger_server.models.status500_internal_server_error import Status500InternalServerError # noqa: E501 -from fabric_cf.orchestrator.swagger_server.test import BaseTestCase +from fabric_cf.orchestrator.fabric_cf.orchestrator.swagger_server.models.resources import Resources # noqa: E501 +from fabric_cf.orchestrator.fabric_cf.orchestrator.swagger_server.models.status400_bad_request import Status400BadRequest # noqa: E501 +from fabric_cf.orchestrator.fabric_cf.orchestrator.swagger_server.models.status401_unauthorized import Status401Unauthorized # noqa: E501 +from fabric_cf.orchestrator.fabric_cf.orchestrator.swagger_server.models.status403_forbidden import Status403Forbidden # noqa: E501 +from fabric_cf.orchestrator.fabric_cf.orchestrator.swagger_server.models.status404_not_found import Status404NotFound # noqa: E501 +from fabric_cf.orchestrator.fabric_cf.orchestrator.swagger_server.models.status500_internal_server_error import Status500InternalServerError # noqa: E501 +from fabric_cf.orchestrator.fabric_cf.orchestrator.swagger_server.test import BaseTestCase class TestResourcesController(BaseTestCase): @@ -22,7 +22,13 @@ def test_portalresources_get(self): Retrieve a listing and description of available resources for portal """ - query_string = [('graph_format', 'GRAPHML')] + query_string = [('graph_format', 'GRAPHML'), + ('level', 1), + ('force_refresh', false), + ('start_date', 'start_date_example'), + ('end_date', 'end_date_example'), + ('includes', 'includes_example'), + ('excludes', 'excludes_example')] response = self.client.open( '//portalresources', method='GET', @@ -39,7 +45,8 @@ def test_resources_get(self): ('force_refresh', false), ('start_date', 'start_date_example'), ('end_date', 'end_date_example'), - ('sites', 'sites_example')] + ('includes', 'includes_example'), + ('excludes', 'excludes_example')] response = self.client.open( '//resources', method='GET', diff --git a/pyproject.toml b/pyproject.toml index 0e8dfc14..a7f0674d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ dependencies = [ "swagger-ui-bundle==0.0.9", "PyYAML", "fabric_fss_utils==1.5.0", - "fabric-message-bus==1.6.2", + "fabric-message-bus==1.7.0b1", "fabric-fim==1.6.1", "fabric-credmgr-client==1.6.0", "ansible" From 31c10331111a097d8fbd668dbb1dda7d0d632467 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 16 Apr 2024 22:39:28 -0400 Subject: [PATCH 016/133] set default graph format to graphML --- fabric_cf/orchestrator/core/orchestrator_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index 97ef59c8..ff8c5eae 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -205,7 +205,7 @@ def list_resources(self, *, level: int, force_refresh: bool = False, start: date controller = self.controller_state.get_management_actor() self.logger.debug(f"list_resources invoked controller:{controller}") - graph_format = self.__translate_graph_format(graph_format=graph_format_str) if graph_format_str else None + graph_format = self.__translate_graph_format(graph_format=graph_format_str) if graph_format_str else GraphFormat.GRAPHML if authorize: self.__authorize_request(id_token=token, action_id=ActionId.query) From cd04bc795f7bc537e6576f6373d966dae5c57ed2 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 7 May 2024 17:46:50 -0400 Subject: [PATCH 017/133] update the state machine to handle allocated state --- fabric_cf/actor/core/apis/abc_database.py | 2 +- .../actor/core/kernel/reservation_client.py | 6 ++ .../actor/core/kernel/slice_state_machine.py | 82 ++++++++++++-- .../core/manage/actor_management_object.py | 6 +- .../actor/core/plugins/db/actor_database.py | 15 ++- .../core/policy/authority_calendar_policy.py | 10 +- .../policy/broker_simpler_units_policy.py | 65 +++++++---- .../core/policy/controller_simple_policy.py | 8 -- .../core/policy/network_node_inventory.py | 3 + fabric_cf/actor/db/__init__.py | 12 +-- fabric_cf/actor/db/psql_database.py | 28 ++++- .../orchestrator/core/orchestrator_handler.py | 48 +++++---- .../core/orchestrator_slice_wrapper.py | 6 +- .../core/reservation_converter.py | 8 +- fabric_cf/orchestrator/openapi.json | 101 ++++++++++++++++++ .../controllers/slices_controller.py | 39 ++++--- .../response/slices_controller.py | 20 ++-- .../swagger_server/swagger/swagger.yaml | 66 ++++++++++++ .../test/test_resources_controller.py | 4 +- .../test/test_slices_controller.py | 14 ++- fabric_cf/orchestrator/test/test.yaml | 1 + psql.upgrade | 51 +++++++++ 22 files changed, 496 insertions(+), 99 deletions(-) diff --git a/fabric_cf/actor/core/apis/abc_database.py b/fabric_cf/actor/core/apis/abc_database.py index 9be362c0..99c81113 100644 --- a/fabric_cf/actor/core/apis/abc_database.py +++ b/fabric_cf/actor/core/apis/abc_database.py @@ -172,7 +172,7 @@ def get_reservations(self, *, slice_id: ID = None, graph_node_id: str = None, pr @abstractmethod def get_components(self, *, node_id: str, states: list[int], rsv_type: list[str], component: str = None, - bdf: str = None) -> Dict[str, List[str]]: + bdf: str = None, start: datetime = None, end: datetime = None) -> Dict[str, List[str]]: """ Retrieves the components. diff --git a/fabric_cf/actor/core/kernel/reservation_client.py b/fabric_cf/actor/core/kernel/reservation_client.py index 1e7a6982..187b62da 100644 --- a/fabric_cf/actor/core/kernel/reservation_client.py +++ b/fabric_cf/actor/core/kernel/reservation_client.py @@ -25,6 +25,7 @@ # Author: Komal Thareja (kthare10@renci.org) from __future__ import annotations +import datetime import json import re import threading @@ -422,6 +423,10 @@ def approve_redeem(self): @return true if approved; false otherwise """ approved = True + now = datetime.datetime.now(datetime.timezone.utc) + if self.requested_term and self.requested_term.get_start_time() > now: + print("Future Reservation!") + return False for pred_state in self.redeem_predecessors.values(): if pred_state.get_reservation() is None or \ @@ -1004,6 +1009,7 @@ def probe_join_state(self): # blocked for a predecessor: see if we can get it going now. assert self.state == ReservationStates.Ticketed + print(f"KOMAL -- APPROVE REDEEM----- {self.requested_term} {self.approved_term}") if self.approve_redeem(): self.transition_with_join(prefix="unblock redeem", state=ReservationStates.Ticketed, pending=ReservationPendingStates.Redeeming, join_state=JoinState.NoJoin) diff --git a/fabric_cf/actor/core/kernel/slice_state_machine.py b/fabric_cf/actor/core/kernel/slice_state_machine.py index 3ed346f9..de19811a 100644 --- a/fabric_cf/actor/core/kernel/slice_state_machine.py +++ b/fabric_cf/actor/core/kernel/slice_state_machine.py @@ -44,6 +44,8 @@ class SliceState(Enum): Modifying = enum.auto() ModifyError = enum.auto() ModifyOK = enum.auto() + AllocatedError = enum.auto() + AllocatedOK = enum.auto() All = enum.auto() # used only for querying def __str__(self): @@ -102,6 +104,10 @@ def translate(state_name: str): return SliceState.Closing elif state_name.lower() == SliceState.Dead.name.lower(): return SliceState.Dead + elif state_name.lower() == SliceState.AllocatedOK.name.lower(): + return SliceState.Closing + elif state_name.lower() == SliceState.AllocatedError.name.lower(): + return SliceState.Dead else: return SliceState.All @@ -117,6 +123,12 @@ def is_stable(*, state) -> bool: return True return False + @staticmethod + def is_allocated(*, state) -> bool: + if state == SliceState.AllocatedOK or state == SliceState.AllocatedError: + return True + return False + @staticmethod def is_modified(*, state) -> bool: if state == SliceState.ModifyOK or state == SliceState.ModifyError: @@ -183,18 +195,20 @@ def has_state_other_than(self, *states) -> bool: class SliceStateMachine: CREATE = SliceOperation(SliceCommand.Create, SliceState.Nascent) - MODIFY = SliceOperation(SliceCommand.Modify, SliceState.StableOK, SliceState.StableError, SliceState.Configuring) + MODIFY = SliceOperation(SliceCommand.Modify, SliceState.StableOK, SliceState.StableError, SliceState.Configuring, + SliceState.AllocatedOK, SliceState.AllocatedError) MODIFY_ACCEPT = SliceOperation(SliceCommand.ModifyAccept, SliceState.ModifyOK, SliceState.ModifyError, - SliceState.Modifying) + SliceState.Modifying, SliceState.AllocatedOK, SliceState.AllocatedError) DELETE = SliceOperation(SliceCommand.Delete, SliceState.Nascent, SliceState.StableOK, SliceState.StableError, SliceState.Configuring, SliceState.Modifying, SliceState.ModifyOK, SliceState.ModifyError, - SliceState.Dead) + SliceState.Dead, SliceState.AllocatedOK, SliceState.AllocatedError) REEVALUATE = SliceOperation(SliceCommand.Reevaluate, SliceState.Nascent, SliceState.StableOK, SliceState.StableError, SliceState.Configuring, SliceState.Dead, SliceState.Closing, - SliceState.Modifying, SliceState.ModifyError, SliceState.ModifyOK) + SliceState.Modifying, SliceState.ModifyError, SliceState.ModifyOK, + SliceState.AllocatedError, SliceState.AllocatedOK) def __init__(self, *, slice_id: ID): self.slice_guid = slice_id @@ -238,9 +252,16 @@ def transition_slice(self, *, operation: SliceOperation, reservations: Reservati elif operation.command == SliceCommand.ModifyAccept: if self.state == SliceState.ModifyError: - self.state = SliceState.StableError + if self.last_state in [SliceState.AllocatedOK, SliceState.AllocatedError]: + self.state = SliceState.AllocatedError + else: + self.state = SliceState.StableError + elif self.state == SliceState.ModifyOK: - self.state = SliceState.StableOK + if self.last_state in [SliceState.AllocatedOK, SliceState.AllocatedError]: + self.state = SliceState.AllocatedOK + else: + self.state = SliceState.StableOK elif operation.command == SliceCommand.Delete: if self.state != SliceState.Dead: @@ -266,7 +287,36 @@ def transition_slice(self, *, operation: SliceOperation, reservations: Reservati if not has_error and r.get_error_message() is not None and len(r.get_error_message()) > 0: has_error = True - if self.state == SliceState.Nascent or self.state == SliceState.Configuring: + if self.state in [SliceState.Nascent, SliceState.Configuring]: + if not bins.has_state_other_than(ReservationStates.Active, ReservationStates.Closed, + ReservationStates.CloseFail): + if not has_error: + self.state = SliceState.StableOK + else: + self.state = SliceState.StableError + + if (not bins.has_state_other_than(ReservationStates.Active, ReservationStates.Failed, + ReservationStates.Closed, ReservationStates.CloseFail)) and \ + bins.has_state(s=ReservationStates.Failed): + self.state = SliceState.StableError + + if not bins.has_state_other_than(ReservationStates.Ticketed, ReservationStates.Closed, + ReservationStates.CloseFail): + if not has_error: + self.state = SliceState.AllocatedOK + else: + self.state = SliceState.AllocatedError + + if (not bins.has_state_other_than(ReservationStates.Ticketed, ReservationStates.Failed, + ReservationStates.Closed, ReservationStates.CloseFail)) and \ + bins.has_state(s=ReservationStates.Failed): + self.state = SliceState.AllocatedError + + if not bins.has_state_other_than(ReservationStates.Closed, ReservationStates.CloseWait, + ReservationStates.Failed, ReservationStates.CloseFail): + self.state = SliceState.Closing + + if self.state in [SliceState.AllocatedOK, SliceState.AllocatedError]: if not bins.has_state_other_than(ReservationStates.Active, ReservationStates.Closed, ReservationStates.CloseFail): if not has_error: @@ -296,12 +346,24 @@ def transition_slice(self, *, operation: SliceOperation, reservations: Reservati bins.has_state(s=ReservationStates.Failed): self.state = SliceState.ModifyError + if not bins.has_state_other_than(ReservationStates.Ticketed, ReservationStates.Closed, + ReservationStates.CloseFail): + if has_error: + self.state = SliceState.ModifyError + else: + self.state = SliceState.ModifyOK + + if (not bins.has_state_other_than(ReservationStates.Ticketed, ReservationStates.Failed, + ReservationStates.Closed, ReservationStates.CloseFail)) and \ + bins.has_state(s=ReservationStates.Failed): + self.state = SliceState.ModifyError + if not bins.has_state_other_than(ReservationStates.Closed, ReservationStates.CloseWait, ReservationStates.Failed, ReservationStates.CloseFail): self.state = SliceState.Closing - elif self.state == SliceState.StableError or self.state == SliceState.StableOK or \ - self.state == SliceState.ModifyError or self.state == SliceState.ModifyOK: + elif self.state in [SliceState.StableError, SliceState.StableOK, SliceState.ModifyError, + SliceState.ModifyOK, SliceState.AllocatedError, SliceState.AllocatedOK]: if not bins.has_state_other_than(ReservationStates.Closed, ReservationStates.CloseWait, ReservationStates.Failed, ReservationStates.CloseFail): self.state = SliceState.Dead @@ -326,4 +388,4 @@ def get_state(self) -> SliceState: return self.state def clear(self): - self.state = SliceState.Nascent \ No newline at end of file + self.state = SliceState.Nascent diff --git a/fabric_cf/actor/core/manage/actor_management_object.py b/fabric_cf/actor/core/manage/actor_management_object.py index b61dba52..fe3438e7 100644 --- a/fabric_cf/actor/core/manage/actor_management_object.py +++ b/fabric_cf/actor/core/manage/actor_management_object.py @@ -195,7 +195,11 @@ def add_slice(self, *, slice_obj: SliceAvro, caller: AuthToken) -> ResultStringA slice_obj_new.set_graph_id(graph_id=slice_obj.graph_id) slice_obj_new.set_config_properties(value=slice_obj.get_config_properties()) slice_obj_new.set_lease_end(lease_end=slice_obj.get_lease_end()) - slice_obj_new.set_lease_start(lease_start=datetime.now(timezone.utc)) + now = datetime.now(timezone.utc) + if slice_obj.get_lease_start() and slice_obj.get_lease_start() > now: + slice_obj.set_lease_start(lease_start=slice_obj.get_lease_start()) + else: + slice_obj_new.set_lease_start(lease_start=datetime.now(timezone.utc)) if slice_obj.get_inventory(): slice_obj_new.set_inventory(value=True) diff --git a/fabric_cf/actor/core/plugins/db/actor_database.py b/fabric_cf/actor/core/plugins/db/actor_database.py index b9ce02c2..76a86810 100644 --- a/fabric_cf/actor/core/plugins/db/actor_database.py +++ b/fabric_cf/actor/core/plugins/db/actor_database.py @@ -262,6 +262,8 @@ def add_reservation(self, *, reservation: ABCReservationMixin): if node_id and comp_id and bdf: components.append((node_id, comp_id, bdf)) + term = reservation.get_term() + self.db.add_reservation(slc_guid=str(reservation.get_slice_id()), rsv_resid=str(reservation.get_reservation_id()), rsv_category=reservation.get_category().value, @@ -271,7 +273,9 @@ def add_reservation(self, *, reservation: ABCReservationMixin): properties=properties, rsv_graph_node_id=reservation.get_graph_node_id(), oidc_claim_sub=oidc_claim_sub, email=email, site=site, rsv_type=rsv_type, - components=components) + components=components, + lease_start=term.get_start_time() if term else None, + lease_end=term.get_end_time() if term else None) self.logger.debug( "Reservation {} added to slice {}".format(reservation.get_reservation_id(), reservation.get_slice())) finally: @@ -308,6 +312,7 @@ def update_reservation(self, *, reservation: ABCReservationMixin): if node_id and comp_id and bdf: components.append((node_id, comp_id, bdf)) + term = reservation.get_term() begin = time.time() properties = pickle.dumps(reservation) diff = int(time.time() - begin) @@ -322,7 +327,9 @@ def update_reservation(self, *, reservation: ABCReservationMixin): rsv_joining=reservation.get_join_state().value, properties=properties, rsv_graph_node_id=reservation.get_graph_node_id(), - site=site, rsv_type=rsv_type, components=components) + site=site, rsv_type=rsv_type, components=components, + lease_start=term.get_start_time() if term else None, + lease_end=term.get_end_time() if term else None) diff = int(time.time() - begin) if diff > 0: self.logger.info(f"DB TIME: {diff}") @@ -459,10 +466,10 @@ def get_authority_reservations(self) -> List[ABCReservationMixin]: return result def get_components(self, *, node_id: str, states: list[int], rsv_type: list[str], component: str = None, - bdf: str = None) -> Dict[str, List[str]]: + bdf: str = None, start: datetime = None, end: datetime = None) -> Dict[str, List[str]]: try: return self.db.get_components(node_id=node_id, states=states, component=component, bdf=bdf, - rsv_type=rsv_type) + rsv_type=rsv_type, start=start, end=end) except Exception as e: self.logger.error(e) finally: diff --git a/fabric_cf/actor/core/policy/authority_calendar_policy.py b/fabric_cf/actor/core/policy/authority_calendar_policy.py index 73af3638..a17c7c9e 100644 --- a/fabric_cf/actor/core/policy/authority_calendar_policy.py +++ b/fabric_cf/actor/core/policy/authority_calendar_policy.py @@ -39,7 +39,7 @@ from fabric_cf.actor.core.common.constants import Constants from fabric_cf.actor.core.core.authority_policy import AuthorityPolicy from fabric_cf.actor.core.common.exceptions import AuthorityException -from fabric_cf.actor.core.kernel.reservation_states import ReservationStates +from fabric_cf.actor.core.kernel.reservation_states import ReservationStates, ReservationPendingStates from fabric_cf.actor.core.kernel.resource_set import ResourceSet from fabric_cf.actor.core.plugins.handlers.config_token import ConfigToken from fabric_cf.actor.core.apis.abc_resource_control import ABCResourceControl @@ -423,6 +423,7 @@ def map(self, *, reservation: ABCAuthorityReservation, node_id_to_reservations: except Exception as e: self.logger.error(f"Could not assign {e}") reservation.fail(message=str(e)) + return node_id_to_reservations def assign_reservation(self, *, reservation: ABCAuthorityReservation, node_id_to_reservations: dict): """ @@ -614,6 +615,13 @@ def get_existing_reservations(self, node_id: str, node_id_to_reservations: dict) existing_reservations = self.actor.get_plugin().get_database().get_reservations(graph_node_id=node_id, states=states) + if existing_reservations: + closing_reservations = [] + for r in existing_reservations: + if r.get_pending_state() == ReservationPendingStates.Closing: + closing_reservations.append(r) + for c in closing_reservations: + existing_reservations.remove(c) reservations_allocated_in_cycle = node_id_to_reservations.get(node_id, None) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 9243cd9b..32154344 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -28,7 +28,7 @@ import enum import threading import traceback -from datetime import datetime +from datetime import datetime, timezone from enum import Enum from typing import TYPE_CHECKING, Tuple, List, Any, Dict @@ -222,10 +222,10 @@ def register_inventory(self, *, resource_type: ResourceType, inventory: Inventor def bind(self, *, reservation: ABCBrokerReservation) -> bool: term = reservation.get_requested_term() - self.logger.info("SlottedAgent bind arrived at cycle {} requested term {}".format( - self.actor.get_current_cycle(), term)) + self.logger.info(f"SlottedAgent bind arrived at cycle {self.actor.get_current_cycle()} requested term {term}") bid_cycle = self.get_allocation(reservation=reservation) + self.logger.info(f"SlottedAgent bind assigned cycle: {bid_cycle} requested term {term}") self.calendar.add_request(reservation=reservation, cycle=bid_cycle) @@ -278,6 +278,12 @@ def get_allocation(self, *, reservation: ABCBrokerReservation) -> int: if intervals <= 0: intervals = 1 + # Hack for Advanced Scheduling; force the advanced slivers to be scheduled in next cycle + now = datetime.now(timezone.utc) + diff = (reservation.get_requested_term().get_new_start_time() - now).total_seconds() + if diff > 120: + intervals = 2 + start = self.last_allocation + (intervals * self.CALL_INTERVAL) + self.ADVANCE_TIME return start @@ -536,7 +542,7 @@ def __prune_nodes_in_maintenance(self, node_id_list: List[str], site: str, reser return node_id_list def __find_first_fit(self, node_id_list: List[str], node_id_to_reservations: dict, inv: NetworkNodeInventory, - reservation: ABCBrokerReservation) -> Tuple[str, BaseSliver, Any]: + reservation: ABCBrokerReservation, term: Term) -> Tuple[str, BaseSliver, Any]: """ Find First Available Node which can serve the reservation @param node_id_list: Candidate Nodes @@ -563,10 +569,14 @@ def __find_first_fit(self, node_id_list: List[str], node_id_to_reservations: dic self.logger.info(f"Skipping candidate node: {graph_node}") continue + print(f"KOMAL Start: {term.get_start_time()} End: {term.get_end_time()}") existing_reservations = self.get_existing_reservations(node_id=node_id, - node_id_to_reservations=node_id_to_reservations) + node_id_to_reservations=node_id_to_reservations, + start=term.get_start_time(), + end=term.get_end_time()) - existing_components = self.get_existing_components(node_id=node_id) + existing_components = self.get_existing_components(node_id=node_id, start=term.get_start_time(), + end=term.get_end_time()) delegation_id, sliver = inv.allocate(rid=reservation.get_reservation_id(), requested_sliver=requested_sliver, @@ -592,7 +602,7 @@ def __find_first_fit(self, node_id_list: List[str], node_id_to_reservations: dic return delegation_id, sliver, error_msg def __allocate_nodes(self, *, reservation: ABCBrokerReservation, inv: NetworkNodeInventory, sliver: NodeSliver, - node_id_to_reservations: dict) -> Tuple[str or None, BaseSliver, Any]: + node_id_to_reservations: dict, term: Term) -> Tuple[str or None, BaseSliver, Any]: """ Allocate Network Node Slivers @param reservation Reservation @@ -622,14 +632,14 @@ def __allocate_nodes(self, *, reservation: ABCBrokerReservation, inv: NetworkNod if self.get_algorithm_type().lower() == BrokerAllocationAlgorithm.FirstFit.name.lower(): return self.__find_first_fit(node_id_list=node_id_list, node_id_to_reservations=node_id_to_reservations, - inv=inv, reservation=reservation) + inv=inv, reservation=reservation, term=term) else: raise BrokerException(error_code=ExceptionErrorCode.NOT_SUPPORTED, msg=f"Broker currently only supports First Fit") def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: NetworkServiceSliver, - node_id_to_reservations: dict) -> Tuple[str, BaseSliver, Any]: + node_id_to_reservations: dict, term: Term) -> Tuple[str, BaseSliver, Any]: """ Allocate Network Service Slivers @param rid Reservation Id @@ -720,7 +730,9 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: msg=f"{message}") else: existing_reservations = self.get_existing_reservations(node_id=owner_ns_id, - node_id_to_reservations=node_id_to_reservations) + node_id_to_reservations=node_id_to_reservations, + start=term.get_start_time(), + end=term.get_end_time()) # Set vlan - source: (c) - only for dedicated NICs ifs = inv.allocate_ifs(requested_ns=sliver, requested_ifs=ifs, owner_ns=owner_ns, bqm_ifs=bqm_cp, existing_reservations=existing_reservations) @@ -791,7 +803,8 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: # Set the Subnet and gateway from the Owner Switch (a) existing_reservations = self.get_existing_reservations(node_id=owner_ns_id, - node_id_to_reservations=node_id_to_reservations) + node_id_to_reservations=node_id_to_reservations, + start=term.get_start_time(), end=term.get_end_time()) # Allocate VLAN for the Network Service if is_vnic: @@ -805,14 +818,14 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: self.__allocate_peered_interfaces(peered_interfaces=peered_ns_interfaces, owner_switch=owner_switch, owner_mpls=owner_mpls_ns, inv=inv, sliver=sliver, owner_ns=owner_ns, - node_id_to_reservations=node_id_to_reservations) + node_id_to_reservations=node_id_to_reservations, term=term) return delegation_id, sliver, error_msg def __allocate_peered_interfaces(self, *, peered_interfaces: List[InterfaceSliver], owner_switch: NodeSliver, inv: NetworkServiceInventory, sliver: NetworkServiceSliver, owner_mpls: NetworkServiceSliver, owner_ns: NetworkServiceSliver, - node_id_to_reservations: dict): + node_id_to_reservations: dict, term: Term): if not len(peered_interfaces): return for pfs in peered_interfaces: @@ -859,7 +872,9 @@ def __allocate_peered_interfaces(self, *, peered_interfaces: List[InterfaceSlive msg=f"Unable to find BQM interface for {pfs.get_name()}") existing_reservations = self.get_existing_reservations(node_id=owner_ns.node_id, - node_id_to_reservations=node_id_to_reservations) + node_id_to_reservations=node_id_to_reservations, + start=term.get_start_time(), + end=term.get_end_time()) pfs = inv.allocate_peered_ifs(owner_switch=owner_switch, requested_ifs=pfs, bqm_interface=bqm_interface, @@ -895,12 +910,14 @@ def ticket_inventory(self, *, reservation: ABCBrokerReservation, inv: InventoryF if isinstance(res_sliver, NodeSliver): delegation_id, sliver, error_msg = self.__allocate_nodes(reservation=reservation, inv=inv, sliver=res_sliver, - node_id_to_reservations=node_id_to_reservations) + node_id_to_reservations=node_id_to_reservations, + term=term) elif isinstance(res_sliver, NetworkServiceSliver): delegation_id, sliver, error_msg = self.__allocate_services(rid=reservation.get_reservation_id(), inv=inv, sliver=res_sliver, - node_id_to_reservations=node_id_to_reservations) + node_id_to_reservations=node_id_to_reservations, + term=term) else: self.logger.error(f'Reservation {reservation} sliver type is neither Node, nor NetworkServiceSliver') raise BrokerException(msg=f"Reservation sliver type is neither Node " @@ -1338,11 +1355,14 @@ def get_network_service_from_graph(self, *, node_id: str, finally: self.lock.release() - def get_existing_reservations(self, node_id: str, node_id_to_reservations: dict) -> List[ABCReservationMixin]: + def get_existing_reservations(self, node_id: str, node_id_to_reservations: dict, + start: datetime = None, end: datetime = None) -> List[ABCReservationMixin]: """ Get existing reservations which are served by CBM node identified by node_id :param node_id: :param node_id_to_reservations: + :param start + :param end :return: list of reservations """ states = [ReservationStates.Active.value, @@ -1352,7 +1372,9 @@ def get_existing_reservations(self, node_id: str, node_id_to_reservations: dict) # Only get Active or Ticketing reservations existing_reservations = self.actor.get_plugin().get_database().get_reservations(graph_node_id=node_id, - states=states) + states=states, + start=start, + end=end) reservations_allocated_in_cycle = node_id_to_reservations.get(node_id, None) @@ -1371,10 +1393,12 @@ def get_existing_reservations(self, node_id: str, node_id_to_reservations: dict) return existing_reservations - def get_existing_components(self, node_id: str) -> Dict[str, List[str]]: + def get_existing_components(self, node_id: str, start: datetime = None, end: datetime = None) -> Dict[str, List[str]]: """ Get existing components attached to Active/Ticketed Network Service Slivers :param node_id: + :param start: + :param end: :return: list of components """ states = [ReservationStates.Active.value, @@ -1388,7 +1412,8 @@ def get_existing_components(self, node_id: str) -> Dict[str, List[str]]: res_type.append(str(x)) # Only get Active or Ticketing reservations - return self.actor.get_plugin().get_database().get_components(node_id=node_id, rsv_type=res_type, states=states) + return self.actor.get_plugin().get_database().get_components(node_id=node_id, rsv_type=res_type, states=states, + start=start, end=end) def set_logger(self, logger): """ diff --git a/fabric_cf/actor/core/policy/controller_simple_policy.py b/fabric_cf/actor/core/policy/controller_simple_policy.py index 5a78e31c..77af98f2 100644 --- a/fabric_cf/actor/core/policy/controller_simple_policy.py +++ b/fabric_cf/actor/core/policy/controller_simple_policy.py @@ -143,14 +143,6 @@ def process_demand(self, *, cycle: int) -> ReservationSet: if demand is None: return ReservationSet() - ''' - for reservation in demand.values(): - kernel_slice = reservation.get_slice() - for slice_reservation in kernel_slice.get_reservations().values(): - self.logger.debug(f"Reservation {slice_reservation.get_reservation_id()} is in state: " - f"{slice_reservation.get_state().name} type: {type(reservation)}") - ''' - broker = self.actor.get_default_broker() for reservation in demand.values(): if reservation.get_broker() is None: diff --git a/fabric_cf/actor/core/policy/network_node_inventory.py b/fabric_cf/actor/core/policy/network_node_inventory.py index 5c4eb8f3..03f3d359 100644 --- a/fabric_cf/actor/core/policy/network_node_inventory.py +++ b/fabric_cf/actor/core/policy/network_node_inventory.py @@ -503,6 +503,9 @@ def allocate(self, *, rid: ID, requested_sliver: BaseSliver, graph_id: str, grap raise BrokerException(error_code=Constants.INVALID_ARGUMENT, msg=f"resource type: {graph_node.get_type()}") + print(f"KOMAL - existing reservations: {existing_reservations}") + print(f"KOMAL - existing reservations: {existing_components}") + delegation_id = None requested_capacities = None # For create, we need to allocate the VM diff --git a/fabric_cf/actor/db/__init__.py b/fabric_cf/actor/db/__init__.py index 47c4ad61..2f4dfcf8 100644 --- a/fabric_cf/actor/db/__init__.py +++ b/fabric_cf/actor/db/__init__.py @@ -24,7 +24,7 @@ # # Author: Komal Thareja (kthare10@renci.org) -from sqlalchemy import JSON, ForeignKey, LargeBinary, TIMESTAMP, Index +from sqlalchemy import JSON, ForeignKey, LargeBinary, Index, TIMESTAMP from sqlalchemy.orm import declarative_base from sqlalchemy import Column, String, Integer, Sequence from sqlalchemy.orm import relationship @@ -121,8 +121,8 @@ class Reservations(Base): rsv_category = Column(Integer, nullable=False) rsv_pending = Column(Integer, nullable=False) rsv_joining = Column(Integer, nullable=False) - lease_start = Column(TIMESTAMP, nullable=True) - lease_end = Column(TIMESTAMP, nullable=True) + lease_start = Column(TIMESTAMP(timezone=True), nullable=True) + lease_end = Column(TIMESTAMP(timezone=True), nullable=True) properties = Column(LargeBinary) components = relationship('Components', back_populates='reservation') @@ -148,8 +148,8 @@ class Slices(Base): slc_state = Column(Integer, nullable=False, index=True) slc_type = Column(Integer, nullable=False, index=True) slc_resource_type = Column(String) - lease_start = Column(TIMESTAMP, nullable=True) - lease_end = Column(TIMESTAMP, nullable=True) + lease_start = Column(TIMESTAMP(timezone=True), nullable=True) + lease_end = Column(TIMESTAMP(timezone=True), nullable=True) properties = Column(LargeBinary) Index('idx_slc_guid_name', slc_guid, slc_name) @@ -207,7 +207,7 @@ class Poas(Base): sliver_id = Column(String, nullable=True, index=True) state = Column(Integer, nullable=False, index=True) slice_id = Column(String, nullable=True, index=True) - last_update_time = Column(TIMESTAMP, nullable=True) + last_update_time = Column(TIMESTAMP(timezone=True), nullable=True) properties = Column(LargeBinary) Index('idx_poa_guid_email', poa_guid, email) diff --git a/fabric_cf/actor/db/psql_database.py b/fabric_cf/actor/db/psql_database.py index 8b832e04..d5417324 100644 --- a/fabric_cf/actor/db/psql_database.py +++ b/fabric_cf/actor/db/psql_database.py @@ -30,7 +30,7 @@ from datetime import datetime, timezone from typing import List, Tuple, Dict -from sqlalchemy import create_engine, desc, func +from sqlalchemy import create_engine, desc, func, and_, or_ from sqlalchemy.orm import scoped_session, sessionmaker, joinedload from fabric_cf.actor.core.common.constants import Constants @@ -820,11 +820,24 @@ def get_reservations(self, *, slice_id: str = None, graph_node_id: str = None, p if category is not None: rows = rows.filter(Reservations.rsv_category.in_(category)) + ''' if start is not None: rows = rows.filter(start <= Reservations.lease_end) if end is not None: rows = rows.filter(Reservations.lease_end <= end) + ''' + # Construct filter condition for lease_end within the given time range + if start is not None or end is not None: + lease_end_filter = True # Initialize with True to avoid NoneType comparison + if start is not None and end is not None: + lease_end_filter = and_(start <= Reservations.lease_end, Reservations.lease_end <= end) + elif start is not None: + lease_end_filter = start <= Reservations.lease_end + elif end is not None: + lease_end_filter = Reservations.lease_end <= end + + rows = rows.filter(lease_end_filter) for row in rows.all(): result.append(self.generate_dict_from_row(row=row)) @@ -834,7 +847,7 @@ def get_reservations(self, *, slice_id: str = None, graph_node_id: str = None, p return result def get_components(self, *, node_id: str, states: list[int], rsv_type: list[str], component: str = None, - bdf: str = None) -> Dict[str, List[str]]: + bdf: str = None, start: datetime = None, end: datetime = None) -> Dict[str, List[str]]: result = {} session = self.get_session() try: @@ -849,6 +862,17 @@ def get_components(self, *, node_id: str, states: list[int], rsv_type: list[str] # Use joinedload to efficiently load the associated Reservation ) + # Apply filter for lease_end within the given time range + if start is not None or end is not None: + lease_end_filter = or_( + and_(Reservations.lease_end > start, + Reservations.lease_end < end) if start is not None and end is not None else True, + Reservations.lease_end > start if start is not None else True, + Reservations.lease_end < end if end is not None else True, + Reservations.lease_end.is_(None) + ) + rows = rows.filter(lease_end_filter) + # Query Component records for reservations in the specified state and owner with the target string if component is not None and bdf is not None: rows = rows.filter(Components.component == component, diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index ff8c5eae..23487ce8 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -221,13 +221,14 @@ def list_resources(self, *, level: int, force_refresh: bool = False, start: date raise e def create_slice(self, *, token: str, slice_name: str, slice_graph: str, ssh_key: str, - lease_end_time: str) -> List[dict]: + lease_start_time: datetime = None, lease_end_time: datetime = None) -> List[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_start_time: Lease Start Time (UTC) :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 @@ -243,8 +244,9 @@ def create_slice(self, *, token: str, slice_name: str, slice_graph: str, ssh_key fabric_token = AccessChecker.validate_and_decode_token(token=token) project, tags, project_name = fabric_token.first_project allow_long_lived = True if Constants.SLICE_NO_LIMIT_LIFETIME in tags else False - end_time = self.__compute_lease_end_time(lease_end_time=lease_end_time, allow_long_lived=allow_long_lived, - project_id=project) + start_time, end_time = self.__compute_lease_end_time(lease_end_time=lease_end_time, + allow_long_lived=allow_long_lived, + project_id=project, lease_start_time=lease_start_time) controller = self.controller_state.get_management_actor() self.logger.debug(f"create_slice invoked for Controller: {controller}") @@ -294,6 +296,7 @@ def create_slice(self, *, token: str, slice_name: str, slice_graph: str, ssh_key Constants.TAGS: tags, Constants.CLAIMS_EMAIL: fabric_token.email, Constants.TOKEN_HASH: fabric_token.token_hash}) + slice_obj.set_lease_start(lease_start=start_time) slice_obj.set_lease_end(lease_end=end_time) auth = AuthAvro() auth.name = self.controller_state.get_management_actor().get_name() @@ -703,7 +706,7 @@ def get_slice_graph(self, *, token: str, slice_id: str, graph_format_str: str, a self.logger.error(f"Exception occurred processing get_slice_graph e: {e}") raise e - def renew_slice(self, *, token: str, slice_id: str, new_lease_end_time: str): + def renew_slice(self, *, token: str, slice_id: str, new_lease_end_time: datetime): """ Renew a slice :param token Fabric Identity Token @@ -741,9 +744,9 @@ def renew_slice(self, *, token: str, slice_id: str, new_lease_end_time: str): fabric_token = AccessChecker.validate_and_decode_token(token=token) project, tags, project_name = fabric_token.first_project allow_long_lived = True if Constants.SLICE_NO_LIMIT_LIFETIME in tags else False - new_end_time = self.__compute_lease_end_time(lease_end_time=new_lease_end_time, - allow_long_lived=allow_long_lived, - project_id=project) + start_time, new_end_time = self.__compute_lease_end_time(lease_end_time=new_lease_end_time, + allow_long_lived=allow_long_lived, + project_id=project) reservations = controller.get_reservations(slice_id=slice_id) if reservations is None or len(reservations) < 1: @@ -801,46 +804,49 @@ def validate_lease_time(lease_time: str) -> datetime: if not lease_time: return lease_time try: - new_end_time = datetime.strptime(lease_time, Constants.LEASE_TIME_FORMAT) + new_time = datetime.strptime(lease_time, Constants.LEASE_TIME_FORMAT) except Exception as e: - raise OrchestratorException(f"Lease End Time is not in format {Constants.LEASE_TIME_FORMAT}", + raise OrchestratorException(f"Lease Time is not in format {Constants.LEASE_TIME_FORMAT}", http_error_code=BAD_REQUEST) now = datetime.now(timezone.utc) - if new_end_time <= now: - raise OrchestratorException(f"New term end time {new_end_time} is in the past! ", + if new_time <= now: + raise OrchestratorException(f"New lease time {new_time} is in the past! ", http_error_code=BAD_REQUEST) - return new_end_time + return new_time - def __compute_lease_end_time(self, lease_end_time: str, allow_long_lived: bool = False, - project_id: str = None) -> datetime: + def __compute_lease_end_time(self, lease_end_time: datetime, allow_long_lived: bool = False, + project_id: str = None, lease_start_time: datetime = None) -> Tuple[datetime, datetime]: """ Validate Lease End Time :param lease_end_time: New End Time :param allow_long_lived: Allow long lived tokens :param project_id: Project Id + :param lease_start_time: New Start Time :return End Time :raises Exception if new end time is in past """ + base_time = datetime.now(timezone.utc) + if lease_start_time and lease_start_time > base_time: + base_time = lease_start_time if lease_end_time is None: - new_end_time = datetime.now(timezone.utc) + timedelta(hours=Constants.DEFAULT_LEASE_IN_HOURS) - return new_end_time + new_end_time = base_time + timedelta(hours=Constants.DEFAULT_LEASE_IN_HOURS) + return base_time, new_end_time - new_end_time = self.validate_lease_time(lease_time=lease_end_time) - now = datetime.now(timezone.utc) + new_end_time = lease_end_time if allow_long_lived: default_long_lived_duration = Constants.LONG_LIVED_SLICE_TIME_WEEKS else: default_long_lived_duration = Constants.DEFAULT_MAX_DURATION - if project_id not in self.infrastructure_project_id and (new_end_time - now) > default_long_lived_duration: + if project_id not in self.infrastructure_project_id and (new_end_time - base_time) > default_long_lived_duration: self.logger.info(f"New term end time {new_end_time} exceeds system default " f"{default_long_lived_duration}, setting to system default: ") - new_end_time = now + default_long_lived_duration + new_end_time = base_time + default_long_lived_duration - return new_end_time + return base_time, new_end_time @staticmethod def __translate_graph_format(*, graph_format: str) -> GraphFormat: diff --git a/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py b/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py index 978950ce..4bd09b13 100644 --- a/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py +++ b/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py @@ -336,7 +336,8 @@ def __build_ns_sliver_reservation(self, *, slice_graph: ABCASMPropertyGraph, nod 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=redeem_predecessors) + pred_list=redeem_predecessors, + start_time=self.slice_obj.get_lease_start()) if sliver.node_id not in node_res_mapping: node_res_mapping[sliver.node_id] = reservation.get_reservation_id() @@ -429,7 +430,8 @@ def __build_node_sliver_reservation(self, *, slice_graph: ABCASMPropertyGraph, # 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()) + end_time=self.slice_obj.get_lease_end(), + start_time=self.slice_obj.get_lease_start()) return reservation def __build_network_node_reservations(self, *, slice_graph: ABCASMPropertyGraph) \ diff --git a/fabric_cf/orchestrator/core/reservation_converter.py b/fabric_cf/orchestrator/core/reservation_converter.py index e7adbdde..95bf9ccd 100644 --- a/fabric_cf/orchestrator/core/reservation_converter.py +++ b/fabric_cf/orchestrator/core/reservation_converter.py @@ -45,13 +45,14 @@ def __init__(self, *, controller: ABCMgmtControllerMixin, broker: ID): self.controller = controller self.broker = broker - def generate_reservation(self, *, sliver: BaseSliver, slice_id: str, end_time: datetime, + def generate_reservation(self, *, sliver: BaseSliver, slice_id: str, end_time: datetime, start_time: datetime = None, pred_list: List[str] = None) -> LeaseReservationAvro: """ Responsible to generate reservation from the sliver :param sliver Network Service or Network Node Sliver :param slice_id Slice Id :param end_time End Time + :param start_time Start Time :param pred_list Predecessor Reservation Id List :returns list of tickets """ @@ -67,6 +68,11 @@ def generate_reservation(self, *, sliver: BaseSliver, slice_id: str, end_time: d ticket.set_units(1) ticket.set_resource_type(str(sliver.get_type())) start = datetime.now(timezone.utc) + if start_time and start_time > start: + print(f"Start_time: {start_time}") + start = start_time + print(f"KOMAL -START-- {start}") + end = start + timedelta(hours=Constants.DEFAULT_LEASE_IN_HOURS) if end_time is not None: end = end_time diff --git a/fabric_cf/orchestrator/openapi.json b/fabric_cf/orchestrator/openapi.json index 4a518f43..0e14db3c 100644 --- a/fabric_cf/orchestrator/openapi.json +++ b/fabric_cf/orchestrator/openapi.json @@ -820,6 +820,18 @@ "type": "string" } }, + { + "name": "lease_start_time", + "in": "query", + "description": "Lease End Time for the Slice", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "string", + "example": "2023-01-01 16:20:15 +00:00" + } + }, { "name": "lease_end_time", "in": "query", @@ -1186,6 +1198,95 @@ ] } }, + "/slices/redeem/{slice_id}": { + "post": { + "tags": [ + "slices" + ], + "summary": "Redeem resources for a slice requested in future via create", + "description": "Request to provision resources for a slice scheduled in advance via the create request once the schedule for the slice is current.\n", + "parameters": [ + { + "name": "slice_id", + "in": "path", + "description": "Slice identified by universally unique identifier", + "required": true, + "style": "simple", + "explode": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/status_200_ok_no_content" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/status_400_bad_request" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/status_401_unauthorized" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/status_403_forbidden" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/status_404_not_found" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/status_500_internal_server_error" + } + } + } + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, "/slices/delete/{slice_id}": { "delete": { "tags": [ diff --git a/fabric_cf/orchestrator/swagger_server/controllers/slices_controller.py b/fabric_cf/orchestrator/swagger_server/controllers/slices_controller.py index 5d30aa2f..6de0b78f 100644 --- a/fabric_cf/orchestrator/swagger_server/controllers/slices_controller.py +++ b/fabric_cf/orchestrator/swagger_server/controllers/slices_controller.py @@ -1,6 +1,5 @@ import connexion -from fabric_cf.orchestrator.swagger_server.models import SlicesPost from fabric_cf.orchestrator.swagger_server.models.slice_details import SliceDetails # noqa: E501 from fabric_cf.orchestrator.swagger_server.models.slices import Slices # noqa: E501 from fabric_cf.orchestrator.swagger_server.models.slices_post import SlicesPost # noqa: E501 @@ -33,10 +32,10 @@ def slices_create_post(body, name, ssh_key, lease_end_time=None): # noqa: E501 post_body = SlicesPost() post_body.graph_model = body.decode("utf-8") post_body.ssh_keys = [ssh_key] - return rc.slices_create_post(post_body, name, lease_end_time) + return rc.slices_create_post(body=post_body, name=name, lease_end_time=lease_end_time) -def slices_creates_post(body, name, lease_end_time=None): # noqa: E501 +def slices_creates_post(body, name, lease_start_time=None, lease_end_time=None): # noqa: E501 """Create slice Request to create slice as described in the request. Request would be a graph ML describing the requested resources. @@ -50,6 +49,8 @@ def slices_creates_post(body, name, lease_end_time=None): # noqa: E501 :type body: dict | bytes :param name: Slice Name :type name: str + :param lease_start_time: Lease End Time for the Slice + :type lease_start_time: str :param lease_end_time: Lease End Time for the Slice :type lease_end_time: str @@ -57,7 +58,7 @@ def slices_creates_post(body, name, lease_end_time=None): # noqa: E501 """ if connexion.request.is_json: body = SlicesPost.from_dict(connexion.request.get_json()) # noqa: E501 - return rc.slices_create_post(body, name, lease_end_time) + return rc.slices_create_post(body=body, name=name, lease_start_time=lease_start_time, lease_end_time=lease_end_time) def slices_delete_delete(): # noqa: E501 @@ -86,12 +87,17 @@ def slices_delete_slice_id_delete(slice_id): # noqa: E501 return rc.slices_delete_slice_id_delete(slice_id) -def slices_get(name=None, as_self=None, states=None, limit=None, offset=None, search=None, exact_match=None): # noqa: E501 +def slices_get(name=None, search=None, exact_match=None, as_self=None, states=None, limit=None, offset=None): # noqa: E501 """Retrieve a listing of user slices - Retrieve a listing of user slices. It returns list of all slices belonging to all members in a project when - 'as_self' is False otherwise returns only the all user's slices in a project. # noqa: E501 + Retrieve a listing of user slices. It returns list of all slices belonging to all members in a project when 'as_self' is False otherwise returns only the all user's slices in a project. # noqa: E501 + :param name: Search for Slices with the name + :type name: str + :param search: search term applied + :type search: str + :param exact_match: Exact Match for Search term + :type exact_match: bool :param as_self: GET object as Self :type as_self: bool :param states: Search for Slices in the specified states @@ -100,12 +106,6 @@ def slices_get(name=None, as_self=None, states=None, limit=None, offset=None, se :type limit: int :param offset: number of items to skip before starting to collect the result set :type offset: int - :param name: Search for Slices with the name - :type name: str - :param search: search term applied - :type search: str - :param exact_match: Exact Match for Search term - :type exact_match: bool :rtype: Slices """ @@ -148,6 +148,19 @@ def slices_modify_slice_id_put(body, slice_id): # noqa: E501 return rc.slices_modify_slice_id_put(body, slice_id) +def slices_redeem_slice_id_post(slice_id): # noqa: E501 + """Redeem resources for a slice requested in future via create + + Request to provision resources for a slice scheduled in advance via the create request once the schedule for the slice is current. # noqa: E501 + + :param slice_id: Slice identified by universally unique identifier + :type slice_id: str + + :rtype: Status200OkNoContent + """ + return 'do some magic!' + + def slices_renew_slice_id_post(slice_id, lease_end_time): # noqa: E501 """Renew slice diff --git a/fabric_cf/orchestrator/swagger_server/response/slices_controller.py b/fabric_cf/orchestrator/swagger_server/response/slices_controller.py index 4a85a318..a1b8e706 100644 --- a/fabric_cf/orchestrator/swagger_server/response/slices_controller.py +++ b/fabric_cf/orchestrator/swagger_server/response/slices_controller.py @@ -39,7 +39,8 @@ from fabric_cf.orchestrator.swagger_server.response.utils import get_token, cors_error_response, cors_success_response -def slices_create_post(body: SlicesPost, name, lease_end_time) -> Slivers: # noqa: E501 +def slices_create_post(body: SlicesPost, name: str, lease_start_time: str = None, + lease_end_time: str = None) -> Slivers: # noqa: E501 """Create slice Request to create slice as described in the request. Request would be a graph ML describing the requested resources. @@ -49,11 +50,13 @@ def slices_create_post(body: SlicesPost, name, lease_end_time) -> Slivers: # no resources asynchronously on the appropriate sites either now or in the future as requested. Experimenter can invoke get slice API to get the latest state of the requested resources. # noqa: E501 - :param body: - :type body: SlicesPost + :param body: Create new Slice + :type body: dict | bytes :param name: Slice Name :type name: str - :param lease_end_time: New Lease End Time for the Slice + :param lease_start_time: Lease End Time for the Slice + :type lease_start_time: str + :param lease_end_time: Lease End Time for the Slice :type lease_end_time: str :rtype: Slivers @@ -65,8 +68,12 @@ def slices_create_post(body: SlicesPost, name, lease_end_time) -> Slivers: # no try: token = get_token() ssh_key = ','.join(body.ssh_keys) + start = handler.validate_lease_time(lease_time=lease_start_time) + print(f"KOMAL --- {start} --- {lease_start_time}") + end = handler.validate_lease_time(lease_time=lease_end_time) slivers_dict = handler.create_slice(token=token, slice_name=name, slice_graph=body.graph_model, - lease_end_time=lease_end_time, ssh_key=ssh_key) + lease_start_time=start, lease_end_time=end, + ssh_key=ssh_key) response = Slivers() response.data = [] for s in slivers_dict: @@ -306,7 +313,8 @@ def slices_renew_slice_id_post(slice_id, lease_end_time) -> Status200OkNoContent try: token = get_token() - handler.renew_slice(token=token, slice_id=slice_id, new_lease_end_time=lease_end_time) + end = handler.validate_lease_time(lease_time=lease_end_time) + handler.renew_slice(token=token, slice_id=slice_id, new_lease_end_time=end) success_counter.labels(POST_METHOD, SLICES_RENEW_PATH).inc() slice_info = Status200OkNoContentData() diff --git a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml index 87907407..249c868e 100644 --- a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml +++ b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml @@ -581,6 +581,15 @@ paths: schema: minLength: 3 type: string + - name: lease_start_time + in: query + description: Lease End Time for the Slice + required: false + style: form + explode: true + schema: + type: string + example: 2023-01-01 16:20:15 +00:00 - name: lease_end_time in: query description: Lease End Time for the Slice @@ -823,6 +832,63 @@ paths: security: - bearerAuth: [] x-openapi-router-controller: fabric_cf.orchestrator.swagger_server.controllers.slices_controller + /slices/redeem/{slice_id}: + post: + tags: + - slices + summary: Redeem resources for a slice requested in future via create + description: | + Request to provision resources for a slice scheduled in advance via the create request once the schedule for the slice is current. + operationId: slices_redeem_slice_id_post + parameters: + - name: slice_id + in: path + description: Slice identified by universally unique identifier + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/status_200_ok_no_content' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/status_400_bad_request' + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/status_401_unauthorized' + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/status_403_forbidden' + "404": + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/status_404_not_found' + "500": + description: Internal Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/status_500_internal_server_error' + security: + - bearerAuth: [] + x-openapi-router-controller: fabric_cf.orchestrator.swagger_server.controllers.slices_controller /slices/delete/{slice_id}: delete: tags: diff --git a/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py index 7278190d..c971f079 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py @@ -30,7 +30,7 @@ def test_portalresources_get(self): ('includes', 'includes_example'), ('excludes', 'excludes_example')] response = self.client.open( - '//portalresources', + '/portalresources', method='GET', query_string=query_string) self.assert200(response, @@ -48,7 +48,7 @@ def test_resources_get(self): ('includes', 'includes_example'), ('excludes', 'excludes_example')] response = self.client.open( - '//resources', + '/resources', method='GET', query_string=query_string) self.assert200(response, diff --git a/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py index dfa09391..39b20a6d 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py @@ -35,7 +35,7 @@ def test_slices_create_post(self): '/slices/create', method='POST', data=json.dumps(body), - content_type='text/plain', + content_type='application/json', query_string=query_string) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -47,6 +47,7 @@ def test_slices_creates_post(self): """ body = SlicesPost() query_string = [('name', 'name_example'), + ('lease_start_time', 'lease_start_time_example'), ('lease_end_time', 'lease_end_time_example')] response = self.client.open( '/slices/creates', @@ -126,6 +127,17 @@ def test_slices_modify_slice_id_put(self): self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) + def test_slices_redeem_slice_id_post(self): + """Test case for slices_redeem_slice_id_post + + Redeem resources for a slice requested in future via create + """ + response = self.client.open( + '//slices/redeem/{slice_id}'.format(slice_id='slice_id_example'), + method='POST') + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + def test_slices_renew_slice_id_post(self): """Test case for slices_renew_slice_id_post diff --git a/fabric_cf/orchestrator/test/test.yaml b/fabric_cf/orchestrator/test/test.yaml index d6f56cae..c875f443 100644 --- a/fabric_cf/orchestrator/test/test.yaml +++ b/fabric_cf/orchestrator/test/test.yaml @@ -99,6 +99,7 @@ container: bqm: # in seconds (default set to 300 seconds) refresh-interval: 300 + local: True time: # This section controls settings, which are generally useful diff --git a/psql.upgrade b/psql.upgrade index 34c3f7b2..228eab41 100644 --- a/psql.upgrade +++ b/psql.upgrade @@ -21,3 +21,54 @@ CREATE TABLE IF NOT EXISTS "Components" ( ); ALTER TABLE "Reservations" ADD COLUMN IF NOT EXISTS components VARCHAR; + + +-- Add new columns with the TIMESTAMP WITH TIME ZONE data type +ALTER TABLE "Reservations" ADD COLUMN lease_start_with_tz TIMESTAMPTZ; +ALTER TABLE "Reservations" ADD COLUMN lease_end_with_tz TIMESTAMPTZ; + +-- Update the new columns with data from the existing columns +UPDATE "Reservations" SET lease_start_with_tz = lease_start::TIMESTAMPTZ; +UPDATE "Reservations" SET lease_end_with_tz = lease_end::TIMESTAMPTZ; + +-- Drop the existing columns +ALTER TABLE "Reservations" DROP COLUMN lease_start; +ALTER TABLE "Reservations" DROP COLUMN lease_end; + +-- Rename the new columns to the original column names +ALTER TABLE "Reservations" RENAME COLUMN lease_start_with_tz TO lease_start; +ALTER TABLE "Reservations" RENAME COLUMN lease_end_with_tz TO lease_end; + + +ALTER TABLE "Slices" ADD COLUMN IF NOT EXISTS components VARCHAR; + + +-- Add new columns with the TIMESTAMP WITH TIME ZONE data type +ALTER TABLE "Slices" ADD COLUMN lease_start_with_tz TIMESTAMPTZ; +ALTER TABLE "Slices" ADD COLUMN lease_end_with_tz TIMESTAMPTZ; + +-- Update the new columns with data from the existing columns +UPDATE "Slices" SET lease_start_with_tz = lease_start::TIMESTAMPTZ; +UPDATE "Slices" SET lease_end_with_tz = lease_end::TIMESTAMPTZ; + +-- Drop the existing columns +ALTER TABLE "Slices" DROP COLUMN lease_start; +ALTER TABLE "Slices" DROP COLUMN lease_end; + +-- Rename the new columns to the original column names +ALTER TABLE "Slices" RENAME COLUMN lease_start_with_tz TO lease_start; +ALTER TABLE "Slices" RENAME COLUMN lease_end_with_tz TO lease_end; + + + +-- Add new columns with the TIMESTAMP WITH TIME ZONE data type +ALTER TABLE "Poas" ADD COLUMN last_update_time_with_tz TIMESTAMPTZ; + +-- Update the new columns with data from the existing columns +UPDATE "Poas" SET last_update_time_with_tz = last_update_time::TIMESTAMPTZ; + +-- Drop the existing columns +ALTER TABLE "Poas" DROP COLUMN last_update_time; + +-- Rename the new columns to the original column names +ALTER TABLE "Poas" RENAME COLUMN last_update_time_with_tz TO last_update_time; From 7a73a107d11f3bc76f61ea7f9b0da9f7e87da21c Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 7 May 2024 17:57:07 -0400 Subject: [PATCH 018/133] allow deletion of allocated slices --- fabric_cf/actor/core/core/actor.py | 14 +++++---- fabric_cf/actor/core/kernel/slice.py | 22 ++++++++++++-- .../core/manage/actor_management_object.py | 3 +- fabric_cf/actor/fim/fim_helper.py | 2 +- .../orchestrator/core/orchestrator_handler.py | 30 +++++++------------ 5 files changed, 39 insertions(+), 32 deletions(-) diff --git a/fabric_cf/actor/core/core/actor.py b/fabric_cf/actor/core/core/actor.py index 7b462ca5..414f9d26 100644 --- a/fabric_cf/actor/core/core/actor.py +++ b/fabric_cf/actor/core/core/actor.py @@ -376,12 +376,14 @@ def recover(self): client_slices = self.plugin.get_database().get_slices(slc_type=[SliceTypes.ClientSlice, SliceTypes.BrokerClientSlice], states=[SliceState.Configuring.value, - SliceState.Nascent.value, - SliceState.StableOK.value, - SliceState.StableError.value, - SliceState.Modifying.value, - SliceState.ModifyOK.value, - SliceState.ModifyError.value]) + SliceState.Nascent.value, + SliceState.StableOK.value, + SliceState.StableError.value, + SliceState.Modifying.value, + SliceState.ModifyOK.value, + SliceState.ModifyError.value, + SliceState.AllocatedOK.value, + SliceState.AllocatedError.value]) self.logger.debug("Found {} client slices".format(len(client_slices))) self.recover_slices(slices=client_slices) self.logger.debug("Recovery of client slices complete") diff --git a/fabric_cf/actor/core/kernel/slice.py b/fabric_cf/actor/core/kernel/slice.py index 6816ccfd..c9d92356 100644 --- a/fabric_cf/actor/core/kernel/slice.py +++ b/fabric_cf/actor/core/kernel/slice.py @@ -289,7 +289,7 @@ def is_stable_error(self) -> bool: def is_stable(self) -> bool: state_changed, slice_state = self.transition_slice(operation=SliceStateMachine.REEVALUATE) - if slice_state == SliceState.StableError or slice_state == SliceState.StableOk: + if slice_state in [SliceState.StableError, SliceState.StableOK]: return True return False @@ -313,7 +313,23 @@ def is_modify_error(self) -> bool: def is_modified(self) -> bool: state_changed, slice_state = self.transition_slice(operation=SliceStateMachine.REEVALUATE) - if slice_state == SliceState.ModifyError or slice_state == SliceState.ModifyOK: + if slice_state in [SliceState.ModifyError, SliceState.ModifyOK]: + return True + + return False + + def is_allocated_error(self) -> bool: + state_changed, slice_state = self.transition_slice(operation=SliceStateMachine.REEVALUATE) + + if slice_state == SliceState.AllocatedError: + return True + + return False + + def is_allocated(self) -> bool: + state_changed, slice_state = self.transition_slice(operation=SliceStateMachine.REEVALUATE) + + if slice_state in [SliceState.AllocatedOK, SliceState.AllocatedError]: return True return False @@ -321,7 +337,7 @@ def is_modified(self) -> bool: def is_dead_or_closing(self) -> bool: state_changed, slice_state = self.transition_slice(operation=SliceStateMachine.REEVALUATE) - if slice_state == SliceState.Dead or slice_state == SliceState.Closing: + if slice_state in [SliceState.Dead, SliceState.Closing]: return True return False diff --git a/fabric_cf/actor/core/manage/actor_management_object.py b/fabric_cf/actor/core/manage/actor_management_object.py index fe3438e7..89fe1cda 100644 --- a/fabric_cf/actor/core/manage/actor_management_object.py +++ b/fabric_cf/actor/core/manage/actor_management_object.py @@ -874,8 +874,7 @@ def build_broker_query_model(self, level_0_broker_query_model: str, level: int, start: datetime = None, end: datetime = None, includes: str = None, excludes: str = None) -> str: try: - db = self.actor.get_plugin().get_database() - return FimHelper.build_broker_query_model(db=db, level_0_broker_query_model=level_0_broker_query_model, + return FimHelper.build_broker_query_model(level_0_broker_query_model=level_0_broker_query_model, level=level, graph_format=graph_format, start=start, end=end, includes=includes, excludes=excludes) except Exception as e: diff --git a/fabric_cf/actor/fim/fim_helper.py b/fabric_cf/actor/fim/fim_helper.py index fc1ac20a..c9998132 100644 --- a/fabric_cf/actor/fim/fim_helper.py +++ b/fabric_cf/actor/fim/fim_helper.py @@ -639,7 +639,7 @@ def get_workers(site: CompositeNode) -> dict: return workers @staticmethod - def build_broker_query_model(db, level_0_broker_query_model: str, level: int, + def build_broker_query_model(level_0_broker_query_model: str, level: int, graph_format: GraphFormat = GraphFormat.GRAPHML, start: datetime = None, end: datetime = None, includes: str = None, excludes: str = None) -> str: diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index 23487ce8..6b307ea3 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -27,19 +27,17 @@ import traceback from datetime import datetime, timedelta, timezone from http.client import NOT_FOUND, BAD_REQUEST, UNAUTHORIZED -from typing import List, Tuple +from typing import List, Tuple, Union from fabric_mb.message_bus.messages.auth_avro import AuthAvro from fabric_mb.message_bus.messages.poa_avro import PoaAvro from fabric_mb.message_bus.messages.reservation_mng import ReservationMng from fabric_mb.message_bus.messages.slice_avro import SliceAvro -from fim.graph.abc_property_graph import ABCPropertyGraph from fim.graph.networkx_property_graph_disjoint import NetworkXGraphImporterDisjoint from fim.slivers.base_sliver import BaseSliver from fim.slivers.network_service import NetworkServiceSliver -from fim.user import GraphFormat, Node, NodeType -from fim.user.topology import ExperimentTopology, AdvertizedTopology -from fim.view_only_dict import ViewOnlyDict +from fim.user import GraphFormat +from fim.user.topology import ExperimentTopology from fabric_cf.actor.core.common.event_logger import EventLoggerSingleton from fabric_cf.actor.core.kernel.poa import PoaStates @@ -116,17 +114,6 @@ def get_broker(self, *, controller: ABCMgmtControllerMixin) -> ID: except Exception as e: self.logger.error(f"Error occurred: {e}", stack_info=True) - def build_broker_query_model(self, controller: ABCMgmtControllerMixin, level: int, graph_format: GraphFormat = GraphFormat.GRAPHML, - start: datetime = None, end: datetime = None) -> str: - try: - saved_bqm = self.controller_state.get_saved_bqm(graph_format=GraphFormat.GRAPHML, level=0) - if saved_bqm and saved_bqm.get_bqm() and len(saved_bqm.get_bqm()): - - return "" - except Exception as e: - self.logger.error(f"Exception occurred build_broker_query_model e: {e}") - self.logger.error(traceback.format_exc()) - def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, token: str = None, level: int = 10, graph_format: GraphFormat = GraphFormat.GRAPHML, force_refresh: bool = False, start: datetime = None, @@ -572,7 +559,9 @@ def delete_slices(self, *, token: str, slice_id: str = None): SliceState.StableError.value, SliceState.StableOK.value, SliceState.ModifyOK.value, - SliceState.ModifyError.value] + SliceState.ModifyError.value, + SliceState.AllocatedError.value, + SliceState.AllocatedOK.value] slice_list = controller.get_slices(slice_id=slice_guid, user_id=fabric_token.uuid, project=project, states=states) @@ -589,7 +578,8 @@ def delete_slices(self, *, token: str, slice_id: str = None): self.logger.debug(f"Slice# {slice_object.get_slice_id()} already closed") continue - if not SliceState.is_stable(state=slice_state) and not SliceState.is_modified(state=slice_state): + if not SliceState.is_stable(state=slice_state) and not SliceState.is_modified(state=slice_state) and \ + not SliceState.is_allocated(state=slice_state): self.logger.info(f"Unable to delete Slice# {slice_object.get_slice_id()} that is not yet stable, " f"try again later") failed_to_delete_slice_ids.append(slice_object.get_slice_id()) @@ -794,14 +784,14 @@ def renew_slice(self, *, token: str, slice_id: str, new_lease_end_time: datetime raise e @staticmethod - def validate_lease_time(lease_time: str) -> datetime: + def validate_lease_time(lease_time: str) -> Union[datetime, None]: """ Validate Lease Time :param lease_time: Lease Time :return Lease Time :raises Exception if new lease time is in past """ - if not lease_time: + if lease_time is None: return lease_time try: new_time = datetime.strptime(lease_time, Constants.LEASE_TIME_FORMAT) From 5c4496f98aa439bacf3f534b7e52383ed8302849 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 8 May 2024 09:20:26 -0400 Subject: [PATCH 019/133] update swagger def --- fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml index 249c868e..edd0e2bb 100644 --- a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml +++ b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml @@ -330,6 +330,8 @@ paths: - Modifying - ModifyOK - ModifyError + - AllocatedOK + - AllocatedError - All - name: limit in: query From d653564b1bd8cf0636f3aae6a9202c72e44ce725 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 8 May 2024 11:25:15 -0400 Subject: [PATCH 020/133] allow force refresh for level 0 --- fabric_cf/orchestrator/core/orchestrator_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index 6b307ea3..19655eb1 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -142,7 +142,7 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok saved_bqm.start_refresh() if broker_query_model is None: - if self.local_bqm: + if self.local_bqm and level != 0: saved_bqm = self.controller_state.get_saved_bqm(graph_format=GraphFormat.GRAPHML, level=0) if saved_bqm and saved_bqm.get_bqm() and len(saved_bqm.get_bqm()): broker_query_model = controller.build_broker_query_model(level_0_broker_query_model=saved_bqm.get_bqm(), From cba5cf43072c06117ebf34b64cf6a1bb8889d65f Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 8 May 2024 11:54:23 -0400 Subject: [PATCH 021/133] fix missing location from list resources --- fabric_cf/actor/core/manage/local/local_controller.py | 2 +- fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fabric_cf/actor/core/manage/local/local_controller.py b/fabric_cf/actor/core/manage/local/local_controller.py index 9211910b..4baf8b50 100644 --- a/fabric_cf/actor/core/manage/local/local_controller.py +++ b/fabric_cf/actor/core/manage/local/local_controller.py @@ -243,4 +243,4 @@ def poa(self, *, poa: PoaAvro) -> bool: except Exception as e: self.on_exception(e=e, traceback_str=traceback.format_exc()) - return False \ No newline at end of file + return False diff --git a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py index 208eb5c5..9d4b028b 100644 --- a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py +++ b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py @@ -234,6 +234,7 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope # get the location if available if loc is None: loc = sliver.get_location() + worker_sliver.set_location(loc) # look at flags flags = sliver.get_flags() From c030f177a8a37aa59be7bb3b74c4d573d753d466 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 8 May 2024 15:07:09 -0400 Subject: [PATCH 022/133] force refresh to still fetch from broker --- fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py | 2 ++ fabric_cf/orchestrator/core/orchestrator_handler.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py index 9d4b028b..648ef126 100644 --- a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py +++ b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py @@ -241,6 +241,8 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope if flags and not ptp and flags.ptp: ptp = True + site_sliver.set_flags(Flags(ptp=ptp)) + # calculate available node capacities based on delegations if sliver.get_capacity_delegations() is not None: # CBM only has one delegation if it has one diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index 19655eb1..4c9d8323 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -142,7 +142,7 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok saved_bqm.start_refresh() if broker_query_model is None: - if self.local_bqm and level != 0: + if self.local_bqm and level != 0 and not force_refresh: saved_bqm = self.controller_state.get_saved_bqm(graph_format=GraphFormat.GRAPHML, level=0) if saved_bqm and saved_bqm.get_bqm() and len(saved_bqm.get_bqm()): broker_query_model = controller.build_broker_query_model(level_0_broker_query_model=saved_bqm.get_bqm(), From c9f043a037a5a8ea3deb85bf663e58b94106a9a5 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 9 May 2024 09:26:57 -0400 Subject: [PATCH 023/133] filter components based on time --- fabric_cf/actor/db/psql_database.py | 33 +++++++++++------------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/fabric_cf/actor/db/psql_database.py b/fabric_cf/actor/db/psql_database.py index d5417324..f0f5c5e7 100644 --- a/fabric_cf/actor/db/psql_database.py +++ b/fabric_cf/actor/db/psql_database.py @@ -820,13 +820,6 @@ def get_reservations(self, *, slice_id: str = None, graph_node_id: str = None, p if category is not None: rows = rows.filter(Reservations.rsv_category.in_(category)) - ''' - if start is not None: - rows = rows.filter(start <= Reservations.lease_end) - - if end is not None: - rows = rows.filter(Reservations.lease_end <= end) - ''' # Construct filter condition for lease_end within the given time range if start is not None or end is not None: lease_end_filter = True # Initialize with True to avoid NoneType comparison @@ -851,32 +844,30 @@ def get_components(self, *, node_id: str, states: list[int], rsv_type: list[str] result = {} session = self.get_session() try: + lease_end_filter = True # Initialize with True to avoid NoneType comparison + # Construct filter condition for lease_end within the given time range + if start is not None or end is not None: + if start is not None and end is not None: + lease_end_filter = and_(start <= Reservations.lease_end, Reservations.lease_end <= end) + elif start is not None: + lease_end_filter = start <= Reservations.lease_end + elif end is not None: + lease_end_filter = Reservations.lease_end <= end + # Query to retrieve Components based on specific Reservation types and states rows = ( session.query(Components) .join(Reservations, Components.reservation_id == Reservations.rsv_id) .filter(Reservations.rsv_type.in_(rsv_type)) .filter(Reservations.rsv_state.in_(states)) + .filter(lease_end_filter) .filter(Components.node_id == node_id) .options(joinedload(Components.reservation)) - # Use joinedload to efficiently load the associated Reservation ) - # Apply filter for lease_end within the given time range - if start is not None or end is not None: - lease_end_filter = or_( - and_(Reservations.lease_end > start, - Reservations.lease_end < end) if start is not None and end is not None else True, - Reservations.lease_end > start if start is not None else True, - Reservations.lease_end < end if end is not None else True, - Reservations.lease_end.is_(None) - ) - rows = rows.filter(lease_end_filter) - # Query Component records for reservations in the specified state and owner with the target string if component is not None and bdf is not None: - rows = rows.filter(Components.component == component, - Components.bdf == bdf) + rows = rows.filter(Components.component == component, Components.bdf == bdf) elif component is not None: rows = rows.filter(Components.component == component) elif bdf is not None: From f8217a528977c9e29682d572a61a2d2741b5067e Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 9 May 2024 10:21:09 -0400 Subject: [PATCH 024/133] fix slice start time --- fabric_cf/actor/core/kernel/reservation_client.py | 1 - .../actor/core/manage/actor_management_object.py | 11 +---------- .../actor/core/policy/broker_simpler_units_policy.py | 1 - fabric_cf/actor/core/policy/network_node_inventory.py | 3 --- fabric_cf/orchestrator/core/reservation_converter.py | 1 - .../swagger_server/response/slices_controller.py | 1 - 6 files changed, 1 insertion(+), 17 deletions(-) diff --git a/fabric_cf/actor/core/kernel/reservation_client.py b/fabric_cf/actor/core/kernel/reservation_client.py index 187b62da..f6b1d13d 100644 --- a/fabric_cf/actor/core/kernel/reservation_client.py +++ b/fabric_cf/actor/core/kernel/reservation_client.py @@ -1009,7 +1009,6 @@ def probe_join_state(self): # blocked for a predecessor: see if we can get it going now. assert self.state == ReservationStates.Ticketed - print(f"KOMAL -- APPROVE REDEEM----- {self.requested_term} {self.approved_term}") if self.approve_redeem(): self.transition_with_join(prefix="unblock redeem", state=ReservationStates.Ticketed, pending=ReservationPendingStates.Redeeming, join_state=JoinState.NoJoin) diff --git a/fabric_cf/actor/core/manage/actor_management_object.py b/fabric_cf/actor/core/manage/actor_management_object.py index 89fe1cda..8a934f1d 100644 --- a/fabric_cf/actor/core/manage/actor_management_object.py +++ b/fabric_cf/actor/core/manage/actor_management_object.py @@ -30,8 +30,6 @@ from typing import TYPE_CHECKING, List, Dict, Tuple from fabric_cf.actor.fim.fim_helper import FimHelper -from fabric_mb.message_bus.messages.poa_avro import PoaAvro -from fabric_mb.message_bus.messages.poa_info_avro import PoaInfoAvro from fabric_mb.message_bus.messages.reservation_mng import ReservationMng from fabric_mb.message_bus.messages.result_delegation_avro import ResultDelegationAvro from fabric_mb.message_bus.messages.result_poa_avro import ResultPoaAvro @@ -43,7 +41,6 @@ from fabric_mb.message_bus.messages.result_slice_avro import ResultSliceAvro from fabric_mb.message_bus.messages.slice_avro import SliceAvro from fim.user import GraphFormat -from fim.user.topology import AdvertizedTopology from fabric_cf.actor.core.apis.abc_actor_runnable import ABCActorRunnable from fabric_cf.actor.core.common.constants import Constants, ErrorCodes @@ -96,7 +93,6 @@ def save(self) -> dict: return properties def recover(self): - actor_name = None if Constants.PROPERTY_ACTOR_NAME in self.serial: actor_name = self.serial[Constants.PROPERTY_ACTOR_NAME] else: @@ -135,7 +131,6 @@ def make_local_db_object(self, *, actor: ABCActorMixin): def set_actor(self, *, actor: ABCActorMixin): if self.actor is None: self.actor = actor - #self.db = actor.get_plugin().get_database() self.logger = actor.get_logger() self.id = actor.get_guid() self.make_local_db_object(actor=actor) @@ -195,11 +190,7 @@ def add_slice(self, *, slice_obj: SliceAvro, caller: AuthToken) -> ResultStringA slice_obj_new.set_graph_id(graph_id=slice_obj.graph_id) slice_obj_new.set_config_properties(value=slice_obj.get_config_properties()) slice_obj_new.set_lease_end(lease_end=slice_obj.get_lease_end()) - now = datetime.now(timezone.utc) - if slice_obj.get_lease_start() and slice_obj.get_lease_start() > now: - slice_obj.set_lease_start(lease_start=slice_obj.get_lease_start()) - else: - slice_obj_new.set_lease_start(lease_start=datetime.now(timezone.utc)) + slice_obj_new.set_lease_start(lease_start=slice_obj.get_lease_start()) if slice_obj.get_inventory(): slice_obj_new.set_inventory(value=True) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 32154344..40c6eafa 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -569,7 +569,6 @@ def __find_first_fit(self, node_id_list: List[str], node_id_to_reservations: dic self.logger.info(f"Skipping candidate node: {graph_node}") continue - print(f"KOMAL Start: {term.get_start_time()} End: {term.get_end_time()}") existing_reservations = self.get_existing_reservations(node_id=node_id, node_id_to_reservations=node_id_to_reservations, start=term.get_start_time(), diff --git a/fabric_cf/actor/core/policy/network_node_inventory.py b/fabric_cf/actor/core/policy/network_node_inventory.py index 03f3d359..5c4eb8f3 100644 --- a/fabric_cf/actor/core/policy/network_node_inventory.py +++ b/fabric_cf/actor/core/policy/network_node_inventory.py @@ -503,9 +503,6 @@ def allocate(self, *, rid: ID, requested_sliver: BaseSliver, graph_id: str, grap raise BrokerException(error_code=Constants.INVALID_ARGUMENT, msg=f"resource type: {graph_node.get_type()}") - print(f"KOMAL - existing reservations: {existing_reservations}") - print(f"KOMAL - existing reservations: {existing_components}") - delegation_id = None requested_capacities = None # For create, we need to allocate the VM diff --git a/fabric_cf/orchestrator/core/reservation_converter.py b/fabric_cf/orchestrator/core/reservation_converter.py index 95bf9ccd..f8462b10 100644 --- a/fabric_cf/orchestrator/core/reservation_converter.py +++ b/fabric_cf/orchestrator/core/reservation_converter.py @@ -71,7 +71,6 @@ def generate_reservation(self, *, sliver: BaseSliver, slice_id: str, end_time: d if start_time and start_time > start: print(f"Start_time: {start_time}") start = start_time - print(f"KOMAL -START-- {start}") end = start + timedelta(hours=Constants.DEFAULT_LEASE_IN_HOURS) if end_time is not None: diff --git a/fabric_cf/orchestrator/swagger_server/response/slices_controller.py b/fabric_cf/orchestrator/swagger_server/response/slices_controller.py index a1b8e706..4abd9104 100644 --- a/fabric_cf/orchestrator/swagger_server/response/slices_controller.py +++ b/fabric_cf/orchestrator/swagger_server/response/slices_controller.py @@ -69,7 +69,6 @@ def slices_create_post(body: SlicesPost, name: str, lease_start_time: str = None token = get_token() ssh_key = ','.join(body.ssh_keys) start = handler.validate_lease_time(lease_time=lease_start_time) - print(f"KOMAL --- {start} --- {lease_start_time}") end = handler.validate_lease_time(lease_time=lease_end_time) slivers_dict = handler.create_slice(token=token, slice_name=name, slice_graph=body.graph_model, lease_start_time=start, lease_end_time=end, From d28e58c3d8880bf75d448164be42d6aeb31db04a Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 9 May 2024 10:51:54 -0400 Subject: [PATCH 025/133] remove print statements --- fabric_cf/actor/core/kernel/reservation_client.py | 2 +- fabric_cf/orchestrator/core/reservation_converter.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/fabric_cf/actor/core/kernel/reservation_client.py b/fabric_cf/actor/core/kernel/reservation_client.py index f6b1d13d..74f44be8 100644 --- a/fabric_cf/actor/core/kernel/reservation_client.py +++ b/fabric_cf/actor/core/kernel/reservation_client.py @@ -425,7 +425,7 @@ def approve_redeem(self): approved = True now = datetime.datetime.now(datetime.timezone.utc) if self.requested_term and self.requested_term.get_start_time() > now: - print("Future Reservation!") + self.logger.debug(f"Future Reservation : {self}!") return False for pred_state in self.redeem_predecessors.values(): diff --git a/fabric_cf/orchestrator/core/reservation_converter.py b/fabric_cf/orchestrator/core/reservation_converter.py index f8462b10..5182c2d6 100644 --- a/fabric_cf/orchestrator/core/reservation_converter.py +++ b/fabric_cf/orchestrator/core/reservation_converter.py @@ -69,7 +69,6 @@ def generate_reservation(self, *, sliver: BaseSliver, slice_id: str, end_time: d ticket.set_resource_type(str(sliver.get_type())) start = datetime.now(timezone.utc) if start_time and start_time > start: - print(f"Start_time: {start_time}") start = start_time end = start + timedelta(hours=Constants.DEFAULT_LEASE_IN_HOURS) From 6f04dd28aa881cc7fa2a4538f30f832b2fa0f021 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 10 May 2024 04:02:37 -0400 Subject: [PATCH 026/133] changes to add metrics api --- .../core/apis/abc_actor_management_object.py | 40 +++++ fabric_cf/actor/core/apis/abc_database.py | 31 ++++ fabric_cf/actor/core/apis/abc_mgmt_actor.py | 39 +++++ fabric_cf/actor/core/common/constants.py | 1 + .../core/manage/actor_management_object.py | 20 +++ .../actor/core/manage/local/local_actor.py | 25 ++- .../actor/core/plugins/db/actor_database.py | 39 +++++ fabric_cf/actor/db/__init__.py | 12 ++ fabric_cf/actor/db/psql_database.py | 80 ++++++++- .../orchestrator/config.orchestrator.yaml | 1 + .../orchestrator/core/orchestrator_handler.py | 44 ++++- fabric_cf/orchestrator/openapi.json | 156 ++++++++---------- .../controllers/metrics_controller.py | 15 ++ .../controllers/slices_controller.py | 13 -- .../swagger_server/models/__init__.py | 1 + .../swagger_server/models/metrics.py | 141 ++++++++++++++++ .../swagger_server/response/constants.py | 2 + .../swagger_server/response/cors_response.py | 4 +- .../response/metrics_controller.py | 54 ++++++ .../swagger_server/swagger/swagger.yaml | 101 +++++------- .../test/test_metrics_controller.py | 32 ++++ .../test/test_resources_controller.py | 14 +- .../test/test_slices_controller.py | 11 -- 23 files changed, 692 insertions(+), 184 deletions(-) create mode 100644 fabric_cf/orchestrator/swagger_server/controllers/metrics_controller.py create mode 100644 fabric_cf/orchestrator/swagger_server/models/metrics.py create mode 100644 fabric_cf/orchestrator/swagger_server/response/metrics_controller.py create mode 100644 fabric_cf/orchestrator/swagger_server/test/test_metrics_controller.py diff --git a/fabric_cf/actor/core/apis/abc_actor_management_object.py b/fabric_cf/actor/core/apis/abc_actor_management_object.py index 96de16b5..d7c5b33b 100644 --- a/fabric_cf/actor/core/apis/abc_actor_management_object.py +++ b/fabric_cf/actor/core/apis/abc_actor_management_object.py @@ -276,6 +276,46 @@ def get_slices(self, *, slice_id: ID, caller: AuthToken, slice_name: str = None, @return returns list of slices """ + @abstractmethod + def increment_metrics(self, *, project_id: str, oidc_sub: str, slice_count: int = 1) -> bool: + """ + Add or update metrics + + @param project_id project id + @param oidc_sub oidc sub + @param slice_count slice_count + + @return true or false + + @throws Exception in case of error + """ + + @abstractmethod + def get_metrics(self, *, project_id: str, oidc_sub: str) -> list: + """ + Get metrics + + @param project_id project id + @param oidc_sub oidc sub + + @return list of metric information + + @throws Exception in case of error + """ + + def get_slice_count(self, *, caller: AuthToken, email: str = None, states: List[int] = None, + project: str = None, user_id: str = None) -> int: + """ + Obtains Slice count matching the filter criteria. + + @param email email + @param project project id + @param states slice states + @param caller caller + @param user_id user_id + @return returns number of slices + """ + def remove_slice(self, *, slice_id: ID, caller: AuthToken) -> ResultAvro: """ Removes the specified slice diff --git a/fabric_cf/actor/core/apis/abc_database.py b/fabric_cf/actor/core/apis/abc_database.py index 99c81113..43c35673 100644 --- a/fabric_cf/actor/core/apis/abc_database.py +++ b/fabric_cf/actor/core/apis/abc_database.py @@ -217,6 +217,37 @@ def get_slices(self, *, slice_id: ID = None, slice_name: str = None, project_id: @throws Exception in case of error """ + @abstractmethod + def get_slice_count(self, *, project_id: str = None, email: str = None, states: list[int] = None, + oidc_sub: str = None, slc_type: List[SliceTypes] = None) -> List[ABCSlice] or None: + """ + Retrieves the slices count. + + @param project_id project id + @param email email + @param states states + @param oidc_sub oidc sub + @param slc_type slice type + + @return number of slices matching the filter criteria + + @throws Exception in case of error + """ + + @abstractmethod + def increment_metrics(self, *, project_id: str, oidc_sub: str, slice_count: int = 1) -> bool: + """ + Add or Update Metrics + + @param project_id project id + @param oidc_sub oidc sub + @param slice_count slice_count + + @return true or false + + @throws Exception in case of error + """ + @abstractmethod def initialize(self): """ diff --git a/fabric_cf/actor/core/apis/abc_mgmt_actor.py b/fabric_cf/actor/core/apis/abc_mgmt_actor.py index cebc73a3..4fad331f 100644 --- a/fabric_cf/actor/core/apis/abc_mgmt_actor.py +++ b/fabric_cf/actor/core/apis/abc_mgmt_actor.py @@ -63,6 +63,45 @@ def get_slices(self, *, slice_id: ID = None, slice_name: str = None, email: str @return returns list of slices """ + def increment_metrics(self, *, project_id: str, oidc_sub: str, slice_count: int = 1) -> bool: + """ + Add or update metrics + + @param project_id project id + @param oidc_sub oidc sub + @param slice_count slice_count + + @return true or false + + @throws Exception in case of error + """ + raise NotImplementedError + + def get_metrics(self, *, project_id: str, oidc_sub: str) -> list: + """ + Get metrics + + @param project_id project id + @param oidc_sub oidc sub + + @return list of metric information + + @throws Exception in case of error + """ + raise NotImplementedError + + def get_slice_count(self, *, email: str = None, project: str = None, states: List[int] = None, + user_id: str = None) -> int: + """ + Obtains slice count. + @param email email + @param project project id + @param states slice states + @param user_id user_id + @return returns list of slices + """ + raise NotImplementedError + @abstractmethod def add_slice(self, *, slice_obj: SliceAvro) -> ID: """ diff --git a/fabric_cf/actor/core/common/constants.py b/fabric_cf/actor/core/common/constants.py index 7ee085ff..a1735b4c 100644 --- a/fabric_cf/actor/core/common/constants.py +++ b/fabric_cf/actor/core/common/constants.py @@ -199,6 +199,7 @@ class Constants: STATE_FILE_LOCATION = '/tmp/fabric_actor.tmp' MAINT_PROJECT_ID = 'maint.project.id' INFRASTRUCTURE_PROJECT_ID = "infrastructure.project.id" + TOTAL_SLICE_COUNT_SEED = "total_slice_count_seed" ELASTIC_TIME = "request.elasticTime" ELASTIC_SIZE = "request.elasticSize" diff --git a/fabric_cf/actor/core/manage/actor_management_object.py b/fabric_cf/actor/core/manage/actor_management_object.py index 8a934f1d..c04bf7df 100644 --- a/fabric_cf/actor/core/manage/actor_management_object.py +++ b/fabric_cf/actor/core/manage/actor_management_object.py @@ -170,6 +170,26 @@ def get_slices(self, *, slice_id: ID, caller: AuthToken, slice_name: str = None, result.status = ManagementObject.set_exception_details(result=result.status, e=e) return result + def get_metrics(self, *, project_id: str, oidc_sub: str) -> list: + try: + return self.db.get_metrics(project_id=project_id, oidc_sub=oidc_sub) + except Exception as e: + self.logger.error("get_metrics {}".format(e)) + + def increment_metrics(self, *, project_id: str, oidc_sub: str, slice_count: int = 1) -> bool: + try: + return self.db.increment_metrics(project_id=project_id, oidc_sub=oidc_sub, slice_count=slice_count) + except Exception as e: + self.logger.error("add_or_update_metrics {}".format(e)) + + def get_slice_count(self, *, caller: AuthToken, email: str = None, states: List[int] = None, + project: str = None, user_id: str = None) -> int: + try: + return self.db.get_slice_count(email=email, states=states, project_id=project, oidc_sub=user_id) + except Exception as e: + self.logger.error("get_slice_count {}".format(e)) + return -1 + def add_slice(self, *, slice_obj: SliceAvro, caller: AuthToken) -> ResultStringAvro: result = ResultStringAvro() result.status = ResultAvro() diff --git a/fabric_cf/actor/core/manage/local/local_actor.py b/fabric_cf/actor/core/manage/local/local_actor.py index 028bdcf6..899513cd 100644 --- a/fabric_cf/actor/core/manage/local/local_actor.py +++ b/fabric_cf/actor/core/manage/local/local_actor.py @@ -72,6 +72,29 @@ def get_slices(self, *, slice_id: ID = None, slice_name: str = None, email: str return None + def increment_metrics(self, *, project_id: str, oidc_sub: str, slice_count: int = 1) -> bool: + try: + return self.manager.increment_metrics(project_id=project_id, oidc_sub=oidc_sub, slice_count=slice_count) + except Exception as e: + self.on_exception(e=e, traceback_str=traceback.format_exc()) + return False + + def get_metrics(self, *, project_id: str, oidc_sub: str) -> list: + try: + return self.manager.get_metrics(project_id=project_id, oidc_sub=oidc_sub) + except Exception as e: + self.on_exception(e=e, traceback_str=traceback.format_exc()) + + def get_slice_count(self, *, email: str = None, project: str = None, states: List[int] = None, + user_id: str = None) -> int: + try: + return self.manager.get_slice_count(caller=self.auth, states=states, email=email, project=project, + user_id=user_id) + except Exception as e: + self.on_exception(e=e, traceback_str=traceback.format_exc()) + + return -1 + def remove_slice(self, *, slice_id: ID) -> bool: self.clear_last() try: @@ -307,4 +330,4 @@ def get_poas(self, *, states: List[int] = None, slice_id: ID = None, rid: ID = N return result.poas except Exception as e: - self.on_exception(e=e, traceback_str=traceback.format_exc()) \ No newline at end of file + self.on_exception(e=e, traceback_str=traceback.format_exc()) diff --git a/fabric_cf/actor/core/plugins/db/actor_database.py b/fabric_cf/actor/core/plugins/db/actor_database.py index 76a86810..ac5137e7 100644 --- a/fabric_cf/actor/core/plugins/db/actor_database.py +++ b/fabric_cf/actor/core/plugins/db/actor_database.py @@ -230,6 +230,45 @@ def get_slices(self, *, slice_id: ID = None, slice_name: str = None, project_id: self.lock.release() return result + def increment_metrics(self, *, project_id: str, oidc_sub: str, slice_count: int = 1) -> bool: + try: + self.lock.acquire() + self.db.increment_metrics(project_id=project_id, user_id=oidc_sub, slice_count=slice_count) + return True + except Exception as e: + self.logger.error(e) + self.logger.error(traceback.format_exc()) + finally: + if self.lock.locked(): + self.lock.release() + return False + + def get_metrics(self, *, project_id: str, oidc_sub: str) -> list: + try: + return self.db.get_metrics(project_id=project_id, user_id=oidc_sub) + except Exception as e: + self.logger.error(e) + self.logger.error(traceback.format_exc()) + finally: + if self.lock.locked(): + self.lock.release() + + def get_slice_count(self, *, project_id: str = None, email: str = None, states: list[int] = None, + oidc_sub: str = None, slc_type: List[SliceTypes] = None) -> List[ABCSlice] or None: + try: + slice_type = None + if slc_type is not None: + slice_type = [x.value for x in slc_type] + return self.db.get_slice_count(project_id=project_id, email=email, + states=states, oidc_sub=oidc_sub, slc_type=slice_type) + except Exception as e: + self.logger.error(e) + self.logger.error(traceback.format_exc()) + finally: + if self.lock.locked(): + self.lock.release() + return -1 + def add_reservation(self, *, reservation: ABCReservationMixin): try: #self.lock.acquire() diff --git a/fabric_cf/actor/db/__init__.py b/fabric_cf/actor/db/__init__.py index 2f4dfcf8..8254565e 100644 --- a/fabric_cf/actor/db/__init__.py +++ b/fabric_cf/actor/db/__init__.py @@ -25,6 +25,7 @@ # Author: Komal Thareja (kthare10@renci.org) from sqlalchemy import JSON, ForeignKey, LargeBinary, Index, TIMESTAMP +from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.orm import declarative_base from sqlalchemy import Column, String, Integer, Sequence from sqlalchemy.orm import relationship @@ -92,6 +93,17 @@ class Miscellaneous(Base): properties = Column(JSON) +class Metrics(Base): + """ + Represents Metrics Database Table + """ + __tablename__ = 'Metrics' + m_id = Column(Integer, Sequence('m_id', start=1, increment=1), autoincrement=True, primary_key=True) + user_id = Column(String, nullable=False, index=True) + project_id = Column(String, nullable=False, index=True) + slice_count = Column(Integer, nullable=False) + + class Proxies(Base): """ Represents Proxies Database Table diff --git a/fabric_cf/actor/db/psql_database.py b/fabric_cf/actor/db/psql_database.py index f0f5c5e7..fcdd84d8 100644 --- a/fabric_cf/actor/db/psql_database.py +++ b/fabric_cf/actor/db/psql_database.py @@ -36,7 +36,7 @@ from fabric_cf.actor.core.common.constants import Constants from fabric_cf.actor.core.common.exceptions import DatabaseException from fabric_cf.actor.db import Base, Clients, ConfigMappings, Proxies, Units, Reservations, Slices, ManagerObjects, \ - Miscellaneous, Actors, Delegations, Sites, Poas, Components + Miscellaneous, Actors, Delegations, Sites, Poas, Components, Metrics @contextmanager @@ -539,6 +539,36 @@ def create_slices_filter(*, slice_id: str = None, slice_name: str = None, projec filter_dict['oidc_claim_sub'] = oidc_sub return filter_dict + def get_slice_count(self, *, project_id: str = None, email: str = None, states: list[int] = None, + oidc_sub: str = None, slc_type: list[int] = None) -> int: + """ + Get slices count for an actor + @param project_id project id + @param email email + @param states states + @param oidc_sub oidc claim sub + @param slc_type slice type + @return list of slices + """ + session = self.get_session() + try: + filter_dict = self.create_slices_filter(project_id=project_id, email=email, oidc_sub=oidc_sub) + + rows = session.query(Slices).filter_by(**filter_dict) + + rows = rows.order_by(desc(Slices.lease_end)) + + if states is not None: + rows = rows.filter(Slices.slc_state.in_(states)) + + if slc_type is not None: + rows = rows.filter(Slices.slc_type.in_(slc_type)) + + return rows.count() + except Exception as e: + self.logger.error(Constants.EXCEPTION_OCCURRED.format(e)) + raise e + def get_slices(self, *, slice_id: str = None, slice_name: str = None, project_id: str = None, email: str = None, states: list[int] = None, oidc_sub: str = None, slc_type: list[int] = None, limit: int = None, offset: int = None, lease_end: datetime = None, search: str = None, @@ -1575,6 +1605,54 @@ def get_poas(self, *, poa_guid: str = None, project_id: str = None, email: str = raise e return result + def increment_metrics(self, *, project_id: str, user_id: str, slice_count: int = 1): + """ + Add or Update Metrics + @param project_id: project_id + @param user_id: user_id + @param slice_count: slice_count + """ + session = self.get_session() + try: + metric_obj = session.query(Metrics).filter_by(project_id=project_id, user_id=user_id).one_or_none() + if not metric_obj: + metric_obj = Metrics(project_id=project_id, user_id=user_id, slice_count=slice_count) + session.add(metric_obj) + else: + metric_obj.slice_count += slice_count + session.commit() + except Exception as e: + session.rollback() + self.logger.error(Constants.EXCEPTION_OCCURRED.format(e)) + raise e + + def get_metrics(self, *, project_id: str = None, user_id: str = None) -> list: + """ + Get Metric count + @param project_id: project_id + @param user_id: user_id + @return entry identified by name + """ + result = [] + session = self.get_session() + try: + filter_criteria = True + # Construct filter condition + if project_id and user_id: + filter_criteria = and_(Metrics.project_id == project_id, Metrics.user_id == user_id) + elif project_id is not None: + filter_criteria = and_(Metrics.project_id == project_id) + elif user_id is not None: + filter_criteria = and_(Metrics.user_id == user_id) + rows = session.query(Metrics).filter(filter_criteria).all() + + for r in rows: + result.append(self.generate_dict_from_row(row=r)) + return result + except Exception as e: + self.logger.error(Constants.EXCEPTION_OCCURRED.format(e)) + raise e + def test(): logger = logging.getLogger('PsqlDatabase') diff --git a/fabric_cf/orchestrator/config.orchestrator.yaml b/fabric_cf/orchestrator/config.orchestrator.yaml index f3534867..60e2ec43 100644 --- a/fabric_cf/orchestrator/config.orchestrator.yaml +++ b/fabric_cf/orchestrator/config.orchestrator.yaml @@ -49,6 +49,7 @@ runtime: rpc.request.timeout.seconds: 1200 maint.project.id: 990d8a8b-7e50-4d13-a3be-0f133ffa8653 infrastructure.project.id: 4604cab7-41ff-4c1a-a935-0ca6f20cceeb + total_slice_count_seed: 0 message.max.bytes: 1048588 rpc.retries: 5 commit.batch.size: 1 diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index 4c9d8323..438db36d 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -64,7 +64,9 @@ def __init__(self): self.logger = self.globals.get_logger() self.jwks_url = self.globals.get_config().get_oauth_config().get(Constants.PROPERTY_CONF_O_AUTH_JWKS_URL, None) self.pdp_config = self.globals.get_config().get_global_config().get_pdp_config() - self.infrastructure_project_id = self.globals.get_config().get_runtime_config().get(Constants.INFRASTRUCTURE_PROJECT_ID, None) + self.config = self.globals.get_config() + self.infrastructure_project_id = self.config.get_runtime_config().get(Constants.INFRASTRUCTURE_PROJECT_ID, None) + self.total_slice_count_seed = self.config.get_runtime_config().get(Constants.TOTAL_SLICE_COUNT_SEED, 0) self.local_bqm = self.globals.get_config().get_global_config().get_bqm_config().get( Constants.LOCAL_BQM, False) @@ -333,6 +335,7 @@ def create_slice(self, *, token: str, slice_name: str, slice_graph: str, ssh_key EventLoggerSingleton.get().log_slice_event(slice_object=slice_obj, action=ActionId.create, topology=topology) + controller.increment_metrics(project_id=project, oidc_sub=fabric_token.uuid) 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: @@ -955,3 +958,42 @@ def get_poas(self, *, token: str, sliver_id: str = None, poa_id: str = None, sta self.logger.error(traceback.format_exc()) self.logger.error(f"Exception occurred processing poa e: {e}") raise e + + def get_metrics_overview(self, *, token: str): + """ + Get metrics overview + """ + try: + controller = self.controller_state.get_management_actor() + self.logger.debug(f"get_metrics_overview invoked for Controller: {controller}") + + project = None + user_id = None + # Filter based on project_id and user_id when token is provided + if token: + fabric_token = self.__authorize_request(id_token=token, action_id=ActionId.query) + projects = fabric_token.projects + if len(projects) == 1: + project, tags, project_name = fabric_token.first_project + user_id = fabric_token.uuid + + active_states = SliceState.list_values_ex_closing_dead() + active_slice_count = controller.get_slice_count(states=active_states, user_id=user_id, project=project) + non_active_metrics = controller.get_metrics(user_id=user_id, project_id=project) + total_slices = 0 + for m in non_active_metrics: + total_slices += m.get("slice_count", 0) + if not user_id and not project: + # Get Seed value from config + total_slices += self.total_slice_count_seed + result = { + "slices": { + "active_cumulative": active_slice_count, + "non_active_cumulative": total_slices + } + } + return result + except Exception as e: + self.logger.error(traceback.format_exc()) + self.logger.error(f"Exception occurred processing get_slices e: {e}") + raise e diff --git a/fabric_cf/orchestrator/openapi.json b/fabric_cf/orchestrator/openapi.json index 0e14db3c..d1598ba1 100644 --- a/fabric_cf/orchestrator/openapi.json +++ b/fabric_cf/orchestrator/openapi.json @@ -75,6 +75,54 @@ } } }, + "/metrics/overview": { + "get": { + "tags": [ + "metrics" + ], + "summary": "Control Framework metrics overview", + "description": "Control Framework metrics overview", + "operationId": "metrics_overview_get", + "parameters": [ + { + "name": "excluded_projects", + "in": "query", + "description": "List of projects to exclude from the metrics overview", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/metrics" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/status_500_internal_server_error" + } + } + } + } + } + } + }, "/resources": { "get": { "tags": [ @@ -1198,95 +1246,6 @@ ] } }, - "/slices/redeem/{slice_id}": { - "post": { - "tags": [ - "slices" - ], - "summary": "Redeem resources for a slice requested in future via create", - "description": "Request to provision resources for a slice scheduled in advance via the create request once the schedule for the slice is current.\n", - "parameters": [ - { - "name": "slice_id", - "in": "path", - "description": "Slice identified by universally unique identifier", - "required": true, - "style": "simple", - "explode": false, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/status_200_ok_no_content" - } - } - } - }, - "400": { - "description": "Bad Request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/status_400_bad_request" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/status_401_unauthorized" - } - } - } - }, - "403": { - "description": "Forbidden", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/status_403_forbidden" - } - } - } - }, - "404": { - "description": "Not Found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/status_404_not_found" - } - } - } - }, - "500": { - "description": "Internal Server Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/status_500_internal_server_error" - } - } - } - } - }, - "security": [ - { - "bearerAuth": [] - } - ] - } - }, "/slices/delete/{slice_id}": { "delete": { "tags": [ @@ -2286,6 +2245,25 @@ } } }, + "metrics": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/status_200_ok_single" + }, + { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "type": "object" + } + } + } + } + ] + }, "slices": { "type": "object", "allOf": [ diff --git a/fabric_cf/orchestrator/swagger_server/controllers/metrics_controller.py b/fabric_cf/orchestrator/swagger_server/controllers/metrics_controller.py new file mode 100644 index 00000000..1b3ff52b --- /dev/null +++ b/fabric_cf/orchestrator/swagger_server/controllers/metrics_controller.py @@ -0,0 +1,15 @@ +from fabric_cf.orchestrator.swagger_server.models.metrics import Metrics # noqa: E501 +from fabric_cf.orchestrator.swagger_server.response import metrics_controller as rc + + +def metrics_overview_get(excluded_projects=None): # noqa: E501 + """Control Framework metrics overview + + Control Framework metrics overview # noqa: E501 + + :param excluded_projects: List of projects to exclude from the metrics overview + :type excluded_projects: List[str] + + :rtype: Metrics + """ + return rc.metrics_overview_get() diff --git a/fabric_cf/orchestrator/swagger_server/controllers/slices_controller.py b/fabric_cf/orchestrator/swagger_server/controllers/slices_controller.py index 6de0b78f..ec643207 100644 --- a/fabric_cf/orchestrator/swagger_server/controllers/slices_controller.py +++ b/fabric_cf/orchestrator/swagger_server/controllers/slices_controller.py @@ -148,19 +148,6 @@ def slices_modify_slice_id_put(body, slice_id): # noqa: E501 return rc.slices_modify_slice_id_put(body, slice_id) -def slices_redeem_slice_id_post(slice_id): # noqa: E501 - """Redeem resources for a slice requested in future via create - - Request to provision resources for a slice scheduled in advance via the create request once the schedule for the slice is current. # noqa: E501 - - :param slice_id: Slice identified by universally unique identifier - :type slice_id: str - - :rtype: Status200OkNoContent - """ - return 'do some magic!' - - def slices_renew_slice_id_post(slice_id, lease_end_time): # noqa: E501 """Renew slice diff --git a/fabric_cf/orchestrator/swagger_server/models/__init__.py b/fabric_cf/orchestrator/swagger_server/models/__init__.py index a2ec9497..a43cb545 100644 --- a/fabric_cf/orchestrator/swagger_server/models/__init__.py +++ b/fabric_cf/orchestrator/swagger_server/models/__init__.py @@ -3,6 +3,7 @@ # flake8: noqa from __future__ import absolute_import # import models into model package +from fabric_cf.orchestrator.swagger_server.models.metrics import Metrics from fabric_cf.orchestrator.swagger_server.models.poa import Poa from fabric_cf.orchestrator.swagger_server.models.poa_data import PoaData from fabric_cf.orchestrator.swagger_server.models.poa_post import PoaPost diff --git a/fabric_cf/orchestrator/swagger_server/models/metrics.py b/fabric_cf/orchestrator/swagger_server/models/metrics.py new file mode 100644 index 00000000..84e52901 --- /dev/null +++ b/fabric_cf/orchestrator/swagger_server/models/metrics.py @@ -0,0 +1,141 @@ +# coding: utf-8 + +from __future__ import absolute_import +from datetime import date, datetime # noqa: F401 + +from typing import List, Dict # noqa: F401 + +from fabric_cf.orchestrator.swagger_server.models.base_model_ import Model +from fabric_cf.orchestrator.swagger_server.models.status200_ok_single import Status200OkSingle # noqa: F401,E501 +from fabric_cf.orchestrator.swagger_server import util + + +class Metrics(Model): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + def __init__(self, size: int=1, status: int=200, type: str=None, results: List[object]=None): # noqa: E501 + """Metrics - a model defined in Swagger + + :param size: The size of this Metrics. # noqa: E501 + :type size: int + :param status: The status of this Metrics. # noqa: E501 + :type status: int + :param type: The type of this Metrics. # noqa: E501 + :type type: str + :param results: The results of this Metrics. # noqa: E501 + :type results: List[object] + """ + self.swagger_types = { + 'size': int, + 'status': int, + 'type': str, + 'results': List[object] + } + + self.attribute_map = { + 'size': 'size', + 'status': 'status', + 'type': 'type', + 'results': 'results' + } + self._size = size + self._status = status + self._type = type + self._results = results + + @classmethod + def from_dict(cls, dikt) -> 'Metrics': + """Returns the dict as a model + + :param dikt: A dict. + :type: dict + :return: The metrics of this Metrics. # noqa: E501 + :rtype: Metrics + """ + return util.deserialize_model(dikt, cls) + + @property + def size(self) -> int: + """Gets the size of this Metrics. + + + :return: The size of this Metrics. + :rtype: int + """ + return self._size + + @size.setter + def size(self, size: int): + """Sets the size of this Metrics. + + + :param size: The size of this Metrics. + :type size: int + """ + + self._size = size + + @property + def status(self) -> int: + """Gets the status of this Metrics. + + + :return: The status of this Metrics. + :rtype: int + """ + return self._status + + @status.setter + def status(self, status: int): + """Sets the status of this Metrics. + + + :param status: The status of this Metrics. + :type status: int + """ + + self._status = status + + @property + def type(self) -> str: + """Gets the type of this Metrics. + + + :return: The type of this Metrics. + :rtype: str + """ + return self._type + + @type.setter + def type(self, type: str): + """Sets the type of this Metrics. + + + :param type: The type of this Metrics. + :type type: str + """ + + self._type = type + + @property + def results(self) -> List[object]: + """Gets the results of this Metrics. + + + :return: The results of this Metrics. + :rtype: List[object] + """ + return self._results + + @results.setter + def results(self, results: List[object]): + """Sets the results of this Metrics. + + + :param results: The results of this Metrics. + :type results: List[object] + """ + + self._results = results diff --git a/fabric_cf/orchestrator/swagger_server/response/constants.py b/fabric_cf/orchestrator/swagger_server/response/constants.py index 32d33c99..62d8c88a 100644 --- a/fabric_cf/orchestrator/swagger_server/response/constants.py +++ b/fabric_cf/orchestrator/swagger_server/response/constants.py @@ -50,3 +50,5 @@ POAS_GET_POA_ID_PATH = '/poas/{poa_id}' VERSIONS_PATH = '/version' + +METRICS_GET_PATH = '/metrics/overview' diff --git a/fabric_cf/orchestrator/swagger_server/response/cors_response.py b/fabric_cf/orchestrator/swagger_server/response/cors_response.py index edf309e6..8efc4ed8 100644 --- a/fabric_cf/orchestrator/swagger_server/response/cors_response.py +++ b/fabric_cf/orchestrator/swagger_server/response/cors_response.py @@ -8,7 +8,7 @@ from fabric_cf.orchestrator.swagger_server.models import Resources, Slices, Slivers, Version, Status200OkNoContent, \ SliceDetails, Status200OkNoContentData, Status400BadRequestErrors, Status400BadRequest, Status401UnauthorizedErrors, \ Status401Unauthorized, Status403ForbiddenErrors, Status403Forbidden, Status404NotFoundErrors, Status404NotFound, \ - Status500InternalServerErrorErrors, Status500InternalServerError + Status500InternalServerErrorErrors, Status500InternalServerError, Metrics _INDENT = int(os.getenv('OC_API_JSON_RESPONSE_INDENT', '4')) @@ -51,7 +51,7 @@ def cors_response(req: request, status_code: int = 200, body: object = None, x_e def cors_200(response_body: Union[Resources, Slices, SliceDetails, Slivers, Version, - Status200OkNoContent] = None) -> cors_response: + Status200OkNoContent, Metrics] = None) -> cors_response: """ Return 200 - OK """ diff --git a/fabric_cf/orchestrator/swagger_server/response/metrics_controller.py b/fabric_cf/orchestrator/swagger_server/response/metrics_controller.py new file mode 100644 index 00000000..f7a0e4cd --- /dev/null +++ b/fabric_cf/orchestrator/swagger_server/response/metrics_controller.py @@ -0,0 +1,54 @@ +from fabric_cf.orchestrator.swagger_server.response.constants import GET_METHOD, METRICS_GET_PATH + +from fabric_cf.orchestrator.swagger_server import received_counter, success_counter, failure_counter + +from fabric_cf.orchestrator.core.orchestrator_handler import OrchestratorHandler + +from fabric_cf.orchestrator.swagger_server.response.cors_response import cors_200, cors_500 + +from fabric_cf.orchestrator.swagger_server.models import Metrics + + +def metrics_overview_get() -> Metrics: # noqa: E501 + """Control Framework metrics overview + { + "results": [ + { + "last_updated": "2024-04-02 19:50:00.00+00", + "slices": { + "active_cumulative": 164, + "non_active_cumulative": 0 + } + } + ], + "size": 1, + "status": 200, + "type": "metrics.overview" + } + + :rtype: Metrics + """ + handler = OrchestratorHandler() + logger = handler.get_logger() + received_counter.labels(GET_METHOD, METRICS_GET_PATH).inc() + try: + metrics = handler.get_metrics_overview() + response = Metrics() + if metrics: + if isinstance(metrics.json_data, list): + response.results = metrics.json_data + else: + response.results = [metrics.json_data] + else: + response.results = [] + + response.size = len(response.results) + response.status = 200 + response.type = 'metrics.overview' + success_counter.labels(GET_METHOD, METRICS_GET_PATH).inc() + return cors_200(response_body=response) + except Exception as exc: + details = 'Oops! something went wrong with metrics_overview_get(): {0}'.format(exc) + logger.error(details) + failure_counter.labels(GET_METHOD, METRICS_GET_PATH).inc() + return cors_500(details=details) diff --git a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml index edd0e2bb..adef78e7 100644 --- a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml +++ b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml @@ -46,6 +46,38 @@ paths: schema: $ref: '#/components/schemas/status_500_internal_server_error' x-openapi-router-controller: fabric_cf.orchestrator.swagger_server.controllers.version_controller + /metrics/overview: + get: + tags: + - metrics + summary: Control Framework metrics overview + description: Control Framework metrics overview + operationId: metrics_overview_get + parameters: + - name: excluded_projects + in: query + description: List of projects to exclude from the metrics overview + required: false + style: form + explode: true + schema: + type: array + items: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/metrics' + "500": + description: Internal Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/status_500_internal_server_error' + x-openapi-router-controller: fabric_cf.orchestrator.swagger_server.controllers.metrics_controller /resources: get: tags: @@ -330,8 +362,6 @@ paths: - Modifying - ModifyOK - ModifyError - - AllocatedOK - - AllocatedError - All - name: limit in: query @@ -834,63 +864,6 @@ paths: security: - bearerAuth: [] x-openapi-router-controller: fabric_cf.orchestrator.swagger_server.controllers.slices_controller - /slices/redeem/{slice_id}: - post: - tags: - - slices - summary: Redeem resources for a slice requested in future via create - description: | - Request to provision resources for a slice scheduled in advance via the create request once the schedule for the slice is current. - operationId: slices_redeem_slice_id_post - parameters: - - name: slice_id - in: path - description: Slice identified by universally unique identifier - required: true - style: simple - explode: false - schema: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: '#/components/schemas/status_200_ok_no_content' - "400": - description: Bad Request - content: - application/json: - schema: - $ref: '#/components/schemas/status_400_bad_request' - "401": - description: Unauthorized - content: - application/json: - schema: - $ref: '#/components/schemas/status_401_unauthorized' - "403": - description: Forbidden - content: - application/json: - schema: - $ref: '#/components/schemas/status_403_forbidden' - "404": - description: Not Found - content: - application/json: - schema: - $ref: '#/components/schemas/status_404_not_found' - "500": - description: Internal Server Error - content: - application/json: - schema: - $ref: '#/components/schemas/status_500_internal_server_error' - security: - - bearerAuth: [] - x-openapi-router-controller: fabric_cf.orchestrator.swagger_server.controllers.slices_controller /slices/delete/{slice_id}: delete: tags: @@ -1564,6 +1537,16 @@ components: default: Internal Server Error details: type: string + metrics: + type: object + allOf: + - $ref: '#/components/schemas/status_200_ok_single' + - type: object + properties: + results: + type: array + items: + type: object slices: type: object allOf: diff --git a/fabric_cf/orchestrator/swagger_server/test/test_metrics_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_metrics_controller.py new file mode 100644 index 00000000..280396d2 --- /dev/null +++ b/fabric_cf/orchestrator/swagger_server/test/test_metrics_controller.py @@ -0,0 +1,32 @@ +# coding: utf-8 + +from __future__ import absolute_import + +from flask import json +from six import BytesIO + +from fabric_cf.orchestrator.swagger_server.models.metrics import Metrics # noqa: E501 +from fabric_cf.orchestrator.swagger_server.models.status500_internal_server_error import Status500InternalServerError # noqa: E501 +from fabric_cf.orchestrator.swagger_server.test import BaseTestCase + + +class TestMetricsController(BaseTestCase): + """MetricsController integration test stubs""" + + def test_metrics_overview_get(self): + """Test case for metrics_overview_get + + Control Framework metrics overview + """ + query_string = [('excluded_projects', 'excluded_projects_example')] + response = self.client.open( + '//metrics/overview', + method='GET', + query_string=query_string) + self.assert200(response, + 'Response body is : ' + response.data.decode('utf-8')) + + +if __name__ == '__main__': + import unittest + unittest.main() diff --git a/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py index c971f079..c640f8cf 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py @@ -5,13 +5,13 @@ from flask import json from six import BytesIO -from fabric_cf.orchestrator.fabric_cf.orchestrator.swagger_server.models.resources import Resources # noqa: E501 -from fabric_cf.orchestrator.fabric_cf.orchestrator.swagger_server.models.status400_bad_request import Status400BadRequest # noqa: E501 -from fabric_cf.orchestrator.fabric_cf.orchestrator.swagger_server.models.status401_unauthorized import Status401Unauthorized # noqa: E501 -from fabric_cf.orchestrator.fabric_cf.orchestrator.swagger_server.models.status403_forbidden import Status403Forbidden # noqa: E501 -from fabric_cf.orchestrator.fabric_cf.orchestrator.swagger_server.models.status404_not_found import Status404NotFound # noqa: E501 -from fabric_cf.orchestrator.fabric_cf.orchestrator.swagger_server.models.status500_internal_server_error import Status500InternalServerError # noqa: E501 -from fabric_cf.orchestrator.fabric_cf.orchestrator.swagger_server.test import BaseTestCase +from fabric_cf.orchestrator.swagger_server.models.resources import Resources # noqa: E501 +from fabric_cf.orchestrator.swagger_server.models.status400_bad_request import Status400BadRequest # noqa: E501 +from fabric_cf.orchestrator.swagger_server.models.status401_unauthorized import Status401Unauthorized # noqa: E501 +from fabric_cf.orchestrator.swagger_server.models.status403_forbidden import Status403Forbidden # noqa: E501 +from fabric_cf.orchestrator.swagger_server.models.status404_not_found import Status404NotFound # noqa: E501 +from fabric_cf.orchestrator.swagger_server.models.status500_internal_server_error import Status500InternalServerError # noqa: E501 +from fabric_cf.orchestrator.swagger_server.test import BaseTestCase class TestResourcesController(BaseTestCase): diff --git a/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py index 39b20a6d..be3a56da 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py @@ -127,17 +127,6 @@ def test_slices_modify_slice_id_put(self): self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) - def test_slices_redeem_slice_id_post(self): - """Test case for slices_redeem_slice_id_post - - Redeem resources for a slice requested in future via create - """ - response = self.client.open( - '//slices/redeem/{slice_id}'.format(slice_id='slice_id_example'), - method='POST') - self.assert200(response, - 'Response body is : ' + response.data.decode('utf-8')) - def test_slices_renew_slice_id_post(self): """Test case for slices_renew_slice_id_post From 1312eb82b6ed25513799ba40cc57eb1beec22d50 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 10 May 2024 04:36:36 -0400 Subject: [PATCH 027/133] handled excluded projects --- .../core/apis/abc_actor_management_object.py | 6 ++++-- fabric_cf/actor/core/apis/abc_database.py | 15 ++++++++++++++- fabric_cf/actor/core/apis/abc_mgmt_actor.py | 6 ++++-- .../core/manage/actor_management_object.py | 6 +++--- .../actor/core/manage/local/local_actor.py | 9 +++++---- .../actor/core/plugins/db/actor_database.py | 11 ++++++----- fabric_cf/actor/db/psql_database.py | 17 +++++++++++++---- .../orchestrator/core/orchestrator_handler.py | 8 +++++--- .../controllers/metrics_controller.py | 2 +- .../response/metrics_controller.py | 9 +++++++-- 10 files changed, 62 insertions(+), 27 deletions(-) diff --git a/fabric_cf/actor/core/apis/abc_actor_management_object.py b/fabric_cf/actor/core/apis/abc_actor_management_object.py index d7c5b33b..2c267445 100644 --- a/fabric_cf/actor/core/apis/abc_actor_management_object.py +++ b/fabric_cf/actor/core/apis/abc_actor_management_object.py @@ -291,12 +291,13 @@ def increment_metrics(self, *, project_id: str, oidc_sub: str, slice_count: int """ @abstractmethod - def get_metrics(self, *, project_id: str, oidc_sub: str) -> list: + def get_metrics(self, *, project_id: str, oidc_sub: str, excluded_projects: List[str] = None) -> list: """ Get metrics @param project_id project id @param oidc_sub oidc sub + @param excluded_projects excluded_projects @return list of metric information @@ -304,7 +305,7 @@ def get_metrics(self, *, project_id: str, oidc_sub: str) -> list: """ def get_slice_count(self, *, caller: AuthToken, email: str = None, states: List[int] = None, - project: str = None, user_id: str = None) -> int: + project: str = None, user_id: str = None, excluded_projects: List[str] = None) -> int: """ Obtains Slice count matching the filter criteria. @@ -313,6 +314,7 @@ def get_slice_count(self, *, caller: AuthToken, email: str = None, states: List[ @param states slice states @param caller caller @param user_id user_id + @param excluded_projects excluded_projects @return returns number of slices """ diff --git a/fabric_cf/actor/core/apis/abc_database.py b/fabric_cf/actor/core/apis/abc_database.py index 43c35673..b0451540 100644 --- a/fabric_cf/actor/core/apis/abc_database.py +++ b/fabric_cf/actor/core/apis/abc_database.py @@ -219,7 +219,8 @@ def get_slices(self, *, slice_id: ID = None, slice_name: str = None, project_id: @abstractmethod def get_slice_count(self, *, project_id: str = None, email: str = None, states: list[int] = None, - oidc_sub: str = None, slc_type: List[SliceTypes] = None) -> List[ABCSlice] or None: + oidc_sub: str = None, slc_type: List[SliceTypes] = None, + excluded_projects: List[str] = None) -> int: """ Retrieves the slices count. @@ -228,6 +229,7 @@ def get_slice_count(self, *, project_id: str = None, email: str = None, states: @param states states @param oidc_sub oidc sub @param slc_type slice type + @param excluded_projects excluded_projects @return number of slices matching the filter criteria @@ -248,6 +250,17 @@ def increment_metrics(self, *, project_id: str, oidc_sub: str, slice_count: int @throws Exception in case of error """ + @abstractmethod + def get_metrics(self, *, project_id: str, oidc_sub: str, excluded_projects: List[str] = None) -> list: + """ + Get Metrics + @param project_id: project id + @param oidc_sub: user id + @param excluded_projects: list of project ids to exclude + + @return list of metrics + """ + @abstractmethod def initialize(self): """ diff --git a/fabric_cf/actor/core/apis/abc_mgmt_actor.py b/fabric_cf/actor/core/apis/abc_mgmt_actor.py index 4fad331f..33b7012b 100644 --- a/fabric_cf/actor/core/apis/abc_mgmt_actor.py +++ b/fabric_cf/actor/core/apis/abc_mgmt_actor.py @@ -77,12 +77,13 @@ def increment_metrics(self, *, project_id: str, oidc_sub: str, slice_count: int """ raise NotImplementedError - def get_metrics(self, *, project_id: str, oidc_sub: str) -> list: + def get_metrics(self, *, project_id: str, oidc_sub: str, excluded_projects: List[str] = None) -> list: """ Get metrics @param project_id project id @param oidc_sub oidc sub + @param excluded_projects excluded_projects @return list of metric information @@ -91,13 +92,14 @@ def get_metrics(self, *, project_id: str, oidc_sub: str) -> list: raise NotImplementedError def get_slice_count(self, *, email: str = None, project: str = None, states: List[int] = None, - user_id: str = None) -> int: + user_id: str = None, excluded_projects: List[str] = None) -> int: """ Obtains slice count. @param email email @param project project id @param states slice states @param user_id user_id + @param excluded_projects excluded_projects @return returns list of slices """ raise NotImplementedError diff --git a/fabric_cf/actor/core/manage/actor_management_object.py b/fabric_cf/actor/core/manage/actor_management_object.py index c04bf7df..597ce371 100644 --- a/fabric_cf/actor/core/manage/actor_management_object.py +++ b/fabric_cf/actor/core/manage/actor_management_object.py @@ -170,9 +170,9 @@ def get_slices(self, *, slice_id: ID, caller: AuthToken, slice_name: str = None, result.status = ManagementObject.set_exception_details(result=result.status, e=e) return result - def get_metrics(self, *, project_id: str, oidc_sub: str) -> list: + def get_metrics(self, *, project_id: str, oidc_sub: str, excluded_projects: List[str] = None) -> list: try: - return self.db.get_metrics(project_id=project_id, oidc_sub=oidc_sub) + return self.db.get_metrics(project_id=project_id, oidc_sub=oidc_sub, excluded_projects=excluded_projects) except Exception as e: self.logger.error("get_metrics {}".format(e)) @@ -183,7 +183,7 @@ def increment_metrics(self, *, project_id: str, oidc_sub: str, slice_count: int self.logger.error("add_or_update_metrics {}".format(e)) def get_slice_count(self, *, caller: AuthToken, email: str = None, states: List[int] = None, - project: str = None, user_id: str = None) -> int: + project: str = None, user_id: str = None, excluded_projects: List[str] = None) -> int: try: return self.db.get_slice_count(email=email, states=states, project_id=project, oidc_sub=user_id) except Exception as e: diff --git a/fabric_cf/actor/core/manage/local/local_actor.py b/fabric_cf/actor/core/manage/local/local_actor.py index 899513cd..cdeea385 100644 --- a/fabric_cf/actor/core/manage/local/local_actor.py +++ b/fabric_cf/actor/core/manage/local/local_actor.py @@ -79,17 +79,18 @@ def increment_metrics(self, *, project_id: str, oidc_sub: str, slice_count: int self.on_exception(e=e, traceback_str=traceback.format_exc()) return False - def get_metrics(self, *, project_id: str, oidc_sub: str) -> list: + def get_metrics(self, *, project_id: str, oidc_sub: str, excluded_projects: List[str] = None) -> list: try: - return self.manager.get_metrics(project_id=project_id, oidc_sub=oidc_sub) + return self.manager.get_metrics(project_id=project_id, oidc_sub=oidc_sub, + excluded_projects=excluded_projects) except Exception as e: self.on_exception(e=e, traceback_str=traceback.format_exc()) def get_slice_count(self, *, email: str = None, project: str = None, states: List[int] = None, - user_id: str = None) -> int: + user_id: str = None, excluded_projects: List[str] = None) -> int: try: return self.manager.get_slice_count(caller=self.auth, states=states, email=email, project=project, - user_id=user_id) + user_id=user_id, excluded_projects=excluded_projects) except Exception as e: self.on_exception(e=e, traceback_str=traceback.format_exc()) diff --git a/fabric_cf/actor/core/plugins/db/actor_database.py b/fabric_cf/actor/core/plugins/db/actor_database.py index ac5137e7..f8d4381a 100644 --- a/fabric_cf/actor/core/plugins/db/actor_database.py +++ b/fabric_cf/actor/core/plugins/db/actor_database.py @@ -243,9 +243,9 @@ def increment_metrics(self, *, project_id: str, oidc_sub: str, slice_count: int self.lock.release() return False - def get_metrics(self, *, project_id: str, oidc_sub: str) -> list: + def get_metrics(self, *, project_id: str, oidc_sub: str, excluded_projects: List[str] = None) -> list: try: - return self.db.get_metrics(project_id=project_id, user_id=oidc_sub) + return self.db.get_metrics(project_id=project_id, user_id=oidc_sub, excluded_projects=excluded_projects) except Exception as e: self.logger.error(e) self.logger.error(traceback.format_exc()) @@ -254,13 +254,14 @@ def get_metrics(self, *, project_id: str, oidc_sub: str) -> list: self.lock.release() def get_slice_count(self, *, project_id: str = None, email: str = None, states: list[int] = None, - oidc_sub: str = None, slc_type: List[SliceTypes] = None) -> List[ABCSlice] or None: + oidc_sub: str = None, slc_type: List[SliceTypes] = None, + excluded_projects: List[str] = None) -> int: try: slice_type = None if slc_type is not None: slice_type = [x.value for x in slc_type] - return self.db.get_slice_count(project_id=project_id, email=email, - states=states, oidc_sub=oidc_sub, slc_type=slice_type) + return self.db.get_slice_count(project_id=project_id, email=email, states=states, oidc_sub=oidc_sub, + slc_type=slice_type, excluded_projects=excluded_projects) except Exception as e: self.logger.error(e) self.logger.error(traceback.format_exc()) diff --git a/fabric_cf/actor/db/psql_database.py b/fabric_cf/actor/db/psql_database.py index fcdd84d8..dd9bf81d 100644 --- a/fabric_cf/actor/db/psql_database.py +++ b/fabric_cf/actor/db/psql_database.py @@ -539,8 +539,8 @@ def create_slices_filter(*, slice_id: str = None, slice_name: str = None, projec filter_dict['oidc_claim_sub'] = oidc_sub return filter_dict - def get_slice_count(self, *, project_id: str = None, email: str = None, states: list[int] = None, - oidc_sub: str = None, slc_type: list[int] = None) -> int: + def get_slice_count(self, *, project_id: str = None, email: str = None, states: List[int] = None, + oidc_sub: str = None, slc_type: List[int] = None, excluded_projects: List[str]) -> int: """ Get slices count for an actor @param project_id project id @@ -548,6 +548,7 @@ def get_slice_count(self, *, project_id: str = None, email: str = None, states: @param states states @param oidc_sub oidc claim sub @param slc_type slice type + @param excluded_projects excluded_projects @return list of slices """ session = self.get_session() @@ -564,6 +565,9 @@ def get_slice_count(self, *, project_id: str = None, email: str = None, states: if slc_type is not None: rows = rows.filter(Slices.slc_type.in_(slc_type)) + if excluded_projects is not None: + rows = rows.filter(Slices.project_id.notin_(excluded_projects)) + return rows.count() except Exception as e: self.logger.error(Constants.EXCEPTION_OCCURRED.format(e)) @@ -1626,12 +1630,13 @@ def increment_metrics(self, *, project_id: str, user_id: str, slice_count: int = self.logger.error(Constants.EXCEPTION_OCCURRED.format(e)) raise e - def get_metrics(self, *, project_id: str = None, user_id: str = None) -> list: + def get_metrics(self, *, project_id: str = None, user_id: str = None, excluded_projects: List[str] = None) -> list: """ Get Metric count @param project_id: project_id @param user_id: user_id - @return entry identified by name + @param excluded_projects: excluded_projects + @return list of metrics """ result = [] session = self.get_session() @@ -1644,6 +1649,10 @@ def get_metrics(self, *, project_id: str = None, user_id: str = None) -> list: filter_criteria = and_(Metrics.project_id == project_id) elif user_id is not None: filter_criteria = and_(Metrics.user_id == user_id) + + if excluded_projects: + filter_criteria = and_(Metrics.project_id.notin_(excluded_projects)) + rows = session.query(Metrics).filter(filter_criteria).all() for r in rows: diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index 438db36d..aed7f5de 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -959,7 +959,7 @@ def get_poas(self, *, token: str, sliver_id: str = None, poa_id: str = None, sta self.logger.error(f"Exception occurred processing poa e: {e}") raise e - def get_metrics_overview(self, *, token: str): + def get_metrics_overview(self, *, token: str = None, excluded_projects: List[str] = None): """ Get metrics overview """ @@ -978,8 +978,10 @@ def get_metrics_overview(self, *, token: str): user_id = fabric_token.uuid active_states = SliceState.list_values_ex_closing_dead() - active_slice_count = controller.get_slice_count(states=active_states, user_id=user_id, project=project) - non_active_metrics = controller.get_metrics(user_id=user_id, project_id=project) + active_slice_count = controller.get_slice_count(states=active_states, user_id=user_id, project=project, + excluded_projects=excluded_projects) + non_active_metrics = controller.get_metrics(user_id=user_id, project_id=project, + excluded_projects=excluded_projects) total_slices = 0 for m in non_active_metrics: total_slices += m.get("slice_count", 0) diff --git a/fabric_cf/orchestrator/swagger_server/controllers/metrics_controller.py b/fabric_cf/orchestrator/swagger_server/controllers/metrics_controller.py index 1b3ff52b..410ec566 100644 --- a/fabric_cf/orchestrator/swagger_server/controllers/metrics_controller.py +++ b/fabric_cf/orchestrator/swagger_server/controllers/metrics_controller.py @@ -12,4 +12,4 @@ def metrics_overview_get(excluded_projects=None): # noqa: E501 :rtype: Metrics """ - return rc.metrics_overview_get() + return rc.metrics_overview_get(excluded_projects=excluded_projects) diff --git a/fabric_cf/orchestrator/swagger_server/response/metrics_controller.py b/fabric_cf/orchestrator/swagger_server/response/metrics_controller.py index f7a0e4cd..3d263f2d 100644 --- a/fabric_cf/orchestrator/swagger_server/response/metrics_controller.py +++ b/fabric_cf/orchestrator/swagger_server/response/metrics_controller.py @@ -1,3 +1,7 @@ +from typing import List + +from fabric_cf.orchestrator.swagger_server.response.utils import get_token + from fabric_cf.orchestrator.swagger_server.response.constants import GET_METHOD, METRICS_GET_PATH from fabric_cf.orchestrator.swagger_server import received_counter, success_counter, failure_counter @@ -9,7 +13,7 @@ from fabric_cf.orchestrator.swagger_server.models import Metrics -def metrics_overview_get() -> Metrics: # noqa: E501 +def metrics_overview_get(excluded_projects: List[str] = None) -> Metrics: # noqa: E501 """Control Framework metrics overview { "results": [ @@ -32,7 +36,8 @@ def metrics_overview_get() -> Metrics: # noqa: E501 logger = handler.get_logger() received_counter.labels(GET_METHOD, METRICS_GET_PATH).inc() try: - metrics = handler.get_metrics_overview() + token = get_token() + metrics = handler.get_metrics_overview(token=token, excluded_projects=excluded_projects) response = Metrics() if metrics: if isinstance(metrics.json_data, list): From b9e52225769246e4aed30f3dd581016fbe7836d3 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 10 May 2024 05:07:55 -0400 Subject: [PATCH 028/133] incorrect params --- fabric_cf/orchestrator/core/orchestrator_handler.py | 2 +- .../swagger_server/response/metrics_controller.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index aed7f5de..e3f1b5ea 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -980,7 +980,7 @@ def get_metrics_overview(self, *, token: str = None, excluded_projects: List[str active_states = SliceState.list_values_ex_closing_dead() active_slice_count = controller.get_slice_count(states=active_states, user_id=user_id, project=project, excluded_projects=excluded_projects) - non_active_metrics = controller.get_metrics(user_id=user_id, project_id=project, + non_active_metrics = controller.get_metrics(oidc_sub=user_id, project_id=project, excluded_projects=excluded_projects) total_slices = 0 for m in non_active_metrics: diff --git a/fabric_cf/orchestrator/swagger_server/response/metrics_controller.py b/fabric_cf/orchestrator/swagger_server/response/metrics_controller.py index 3d263f2d..5161a5b8 100644 --- a/fabric_cf/orchestrator/swagger_server/response/metrics_controller.py +++ b/fabric_cf/orchestrator/swagger_server/response/metrics_controller.py @@ -40,10 +40,10 @@ def metrics_overview_get(excluded_projects: List[str] = None) -> Metrics: # noq metrics = handler.get_metrics_overview(token=token, excluded_projects=excluded_projects) response = Metrics() if metrics: - if isinstance(metrics.json_data, list): - response.results = metrics.json_data + if isinstance(metrics, list): + response.results = metrics else: - response.results = [metrics.json_data] + response.results = [metrics] else: response.results = [] From a07b576c4027df2957062210ee33c32d5c9f1986 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 10 May 2024 05:12:13 -0400 Subject: [PATCH 029/133] add allocated states --- fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml index adef78e7..78e7da31 100644 --- a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml +++ b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml @@ -362,6 +362,8 @@ paths: - Modifying - ModifyOK - ModifyError + - AllocatedOK + - AllocatedError - All - name: limit in: query From 26391d75104fa6781c662a08eef9b10ee02c92e6 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 10 May 2024 05:18:18 -0400 Subject: [PATCH 030/133] only return user slice count --- fabric_cf/actor/core/plugins/db/actor_database.py | 2 +- fabric_cf/orchestrator/core/orchestrator_handler.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fabric_cf/actor/core/plugins/db/actor_database.py b/fabric_cf/actor/core/plugins/db/actor_database.py index f8d4381a..bebc0156 100644 --- a/fabric_cf/actor/core/plugins/db/actor_database.py +++ b/fabric_cf/actor/core/plugins/db/actor_database.py @@ -257,7 +257,7 @@ def get_slice_count(self, *, project_id: str = None, email: str = None, states: oidc_sub: str = None, slc_type: List[SliceTypes] = None, excluded_projects: List[str] = None) -> int: try: - slice_type = None + slice_type = [SliceTypes.ClientSlice.value] if slc_type is not None: slice_type = [x.value for x in slc_type] return self.db.get_slice_count(project_id=project_id, email=email, states=states, oidc_sub=oidc_sub, diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index e3f1b5ea..0063cb6e 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -997,5 +997,5 @@ def get_metrics_overview(self, *, token: str = None, excluded_projects: List[str return result except Exception as e: self.logger.error(traceback.format_exc()) - self.logger.error(f"Exception occurred processing get_slices e: {e}") + self.logger.error(f"Exception occurred processing get_metrics_overview e: {e}") raise e From 3e77fd7930e991d1f7a761dffad26e26c331c6a4 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 10 May 2024 05:34:39 -0400 Subject: [PATCH 031/133] fix response --- .../swagger_server/response/metrics_controller.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/fabric_cf/orchestrator/swagger_server/response/metrics_controller.py b/fabric_cf/orchestrator/swagger_server/response/metrics_controller.py index 5161a5b8..7f9f74a1 100644 --- a/fabric_cf/orchestrator/swagger_server/response/metrics_controller.py +++ b/fabric_cf/orchestrator/swagger_server/response/metrics_controller.py @@ -1,6 +1,6 @@ from typing import List -from fabric_cf.orchestrator.swagger_server.response.utils import get_token +from fabric_cf.orchestrator.swagger_server.response.utils import get_token, cors_error_response, cors_success_response from fabric_cf.orchestrator.swagger_server.response.constants import GET_METHOD, METRICS_GET_PATH @@ -8,7 +8,7 @@ from fabric_cf.orchestrator.core.orchestrator_handler import OrchestratorHandler -from fabric_cf.orchestrator.swagger_server.response.cors_response import cors_200, cors_500 +from fabric_cf.orchestrator.swagger_server.response.cors_response import cors_200 from fabric_cf.orchestrator.swagger_server.models import Metrics @@ -51,9 +51,8 @@ def metrics_overview_get(excluded_projects: List[str] = None) -> Metrics: # noq response.status = 200 response.type = 'metrics.overview' success_counter.labels(GET_METHOD, METRICS_GET_PATH).inc() - return cors_200(response_body=response) - except Exception as exc: - details = 'Oops! something went wrong with metrics_overview_get(): {0}'.format(exc) - logger.error(details) + return cors_success_response(response_body=response) + except Exception as e: + logger.exception(e) failure_counter.labels(GET_METHOD, METRICS_GET_PATH).inc() - return cors_500(details=details) + return cors_error_response(error=e) From 96945dd1bddaa978aaafcc4b9a1248843120d422 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 10 May 2024 06:08:46 -0400 Subject: [PATCH 032/133] update the upgrade --- fabric_cf/actor/db/psql_database.py | 1 + psql.upgrade | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/fabric_cf/actor/db/psql_database.py b/fabric_cf/actor/db/psql_database.py index dd9bf81d..304e4a76 100644 --- a/fabric_cf/actor/db/psql_database.py +++ b/fabric_cf/actor/db/psql_database.py @@ -107,6 +107,7 @@ def reset_db(self): session.query(Actors).delete() session.query(Sites).delete() session.query(Poas).delete() + session.query(Metrics).delete() session.commit() except Exception as e: session.rollback() diff --git a/psql.upgrade b/psql.upgrade index 228eab41..a6a6e9b9 100644 --- a/psql.upgrade +++ b/psql.upgrade @@ -72,3 +72,10 @@ ALTER TABLE "Poas" DROP COLUMN last_update_time; -- Rename the new columns to the original column names ALTER TABLE "Poas" RENAME COLUMN last_update_time_with_tz TO last_update_time; + +CREATE TABLE IF NOT EXISTS "Metrics" ( + m_id INTEGER NOT NULL DEFAULT nextval('m_id') PRIMARY KEY, + user_id VARCHAR NOT NULL, + project_id VARCHAR NOT NULL, + slice_count INTEGER NOT NULL, +); \ No newline at end of file From 9b82c829a39673a7a702022cf2bebe0b56582321 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 10 May 2024 09:26:12 -0400 Subject: [PATCH 033/133] update openapi.json --- fabric_cf/orchestrator/openapi.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fabric_cf/orchestrator/openapi.json b/fabric_cf/orchestrator/openapi.json index d1598ba1..73250593 100644 --- a/fabric_cf/orchestrator/openapi.json +++ b/fabric_cf/orchestrator/openapi.json @@ -511,6 +511,8 @@ "Modifying", "ModifyOK", "ModifyError", + "AllocatedOK", + "AllocatedError", "All" ] } From 983dfd473be23525d119f8cd4fef1658e1f97bdb Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 10 May 2024 09:43:49 -0400 Subject: [PATCH 034/133] update openapi.json --- fabric_cf/orchestrator/openapi.json | 4 ++++ fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml | 2 ++ 2 files changed, 6 insertions(+) diff --git a/fabric_cf/orchestrator/openapi.json b/fabric_cf/orchestrator/openapi.json index 73250593..718ba6cc 100644 --- a/fabric_cf/orchestrator/openapi.json +++ b/fabric_cf/orchestrator/openapi.json @@ -25,6 +25,10 @@ } ], "tags": [ + { + "name": "metrics", + "description": "Control Framework Metrics" + }, { "name": "slices", "description": "Slices in FABRIC" diff --git a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml index 78e7da31..2fd19354 100644 --- a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml +++ b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml @@ -16,6 +16,8 @@ servers: - url: https://virtserver.swaggerhub.com/kthare10/orchestrator/1.0.1 description: SwaggerHub API Auto Mocking tags: +- name: metrics + description: Control Framework Metrics - name: slices description: Slices in FABRIC - name: slivers From 038e2ffd685d139b802f8561b0dd0bda0f8a8878 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 10 May 2024 09:51:17 -0400 Subject: [PATCH 035/133] update the upgrade --- fabric_cf/orchestrator/openapi.json | 43 ++++++++++++++++++- .../swagger_server/swagger/swagger.yaml | 26 ++++++++++- .../test/test_metrics_controller.py | 4 ++ .../test/test_poas_controller.py | 6 +-- .../test/test_resources_controller.py | 4 +- .../test/test_slices_controller.py | 30 ++++++------- .../test/test_slivers_controller.py | 8 ++-- .../test/test_version_controller.py | 3 +- 8 files changed, 93 insertions(+), 31 deletions(-) diff --git a/fabric_cf/orchestrator/openapi.json b/fabric_cf/orchestrator/openapi.json index 718ba6cc..5eb1610d 100644 --- a/fabric_cf/orchestrator/openapi.json +++ b/fabric_cf/orchestrator/openapi.json @@ -85,8 +85,7 @@ "metrics" ], "summary": "Control Framework metrics overview", - "description": "Control Framework metrics overview", - "operationId": "metrics_overview_get", + "description": "Control Framework metrics overview", "parameters": [ { "name": "excluded_projects", @@ -114,6 +113,46 @@ } } }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/status_400_bad_request" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/status_401_unauthorized" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/status_403_forbidden" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/status_404_not_found" + } + } + } + }, "500": { "description": "Internal Server Error", "content": { diff --git a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml index 2fd19354..687ef854 100644 --- a/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml +++ b/fabric_cf/orchestrator/swagger_server/swagger/swagger.yaml @@ -53,7 +53,7 @@ paths: tags: - metrics summary: Control Framework metrics overview - description: Control Framework metrics overview + description: Control Framework metrics overview operationId: metrics_overview_get parameters: - name: excluded_projects @@ -73,6 +73,30 @@ paths: application/json: schema: $ref: '#/components/schemas/metrics' + "400": + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/status_400_bad_request' + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/status_401_unauthorized' + "403": + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/status_403_forbidden' + "404": + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/status_404_not_found' "500": description: Internal Server Error content: diff --git a/fabric_cf/orchestrator/swagger_server/test/test_metrics_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_metrics_controller.py index 280396d2..80552d13 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_metrics_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_metrics_controller.py @@ -6,6 +6,10 @@ from six import BytesIO from fabric_cf.orchestrator.swagger_server.models.metrics import Metrics # noqa: E501 +from fabric_cf.orchestrator.swagger_server.models.status400_bad_request import Status400BadRequest # noqa: E501 +from fabric_cf.orchestrator.swagger_server.models.status401_unauthorized import Status401Unauthorized # noqa: E501 +from fabric_cf.orchestrator.swagger_server.models.status403_forbidden import Status403Forbidden # noqa: E501 +from fabric_cf.orchestrator.swagger_server.models.status404_not_found import Status404NotFound # noqa: E501 from fabric_cf.orchestrator.swagger_server.models.status500_internal_server_error import Status500InternalServerError # noqa: E501 from fabric_cf.orchestrator.swagger_server.test import BaseTestCase diff --git a/fabric_cf/orchestrator/swagger_server/test/test_poas_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_poas_controller.py index 29d62e60..291c37d0 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_poas_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_poas_controller.py @@ -25,7 +25,7 @@ def test_poas_create_sliver_id_post(self): """ body = PoaPost() response = self.client.open( - '/poas/create/{sliver_id}'.format(sliver_id='sliver_id_example'), + '//poas/create/{sliver_id}'.format(sliver_id='sliver_id_example'), method='POST', data=json.dumps(body), content_type='application/json') @@ -42,7 +42,7 @@ def test_poas_get(self): ('limit', 200), ('offset', 1)] response = self.client.open( - '/poas/', + '//poas/', method='GET', query_string=query_string) self.assert200(response, @@ -54,7 +54,7 @@ def test_poas_poa_id_get(self): Perform an operational action on a sliver. """ response = self.client.open( - '/poas/{poa_id}'.format(poa_id='poa_id_example'), + '//poas/{poa_id}'.format(poa_id='poa_id_example'), method='GET') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) diff --git a/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py index c640f8cf..6285d36b 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_resources_controller.py @@ -30,7 +30,7 @@ def test_portalresources_get(self): ('includes', 'includes_example'), ('excludes', 'excludes_example')] response = self.client.open( - '/portalresources', + '//portalresources', method='GET', query_string=query_string) self.assert200(response, @@ -48,7 +48,7 @@ def test_resources_get(self): ('includes', 'includes_example'), ('excludes', 'excludes_example')] response = self.client.open( - '/resources', + '//resources', method='GET', query_string=query_string) self.assert200(response, diff --git a/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py index be3a56da..88edcbc9 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_slices_controller.py @@ -20,7 +20,6 @@ class TestSlicesController(BaseTestCase): """SlicesController integration test stubs""" - TOKEN = "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImI0MTUxNjcyMTExOTFlMmUwNWIyMmI1NGIxZDNiNzY2N2U3NjRhNzQ3NzIyMTg1ZTcyMmU1MmUxNDZmZTQzYWEiLCJ0eXAiOiJKV1QifQ.eyJhY3IiOiJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZFByb3RlY3RlZFRyYW5zcG9ydCIsImVtYWlsIjoia3RoYXJlMTBAZW1haWwudW5jLmVkdSIsImdpdmVuX25hbWUiOiJLb21hbCIsImZhbWlseV9uYW1lIjoiVGhhcmVqYSIsIm5hbWUiOiJLb21hbCBUaGFyZWphIiwiaXNzIjoiaHR0cHM6Ly9jaWxvZ29uLm9yZyIsInN1YiI6Imh0dHA6Ly9jaWxvZ29uLm9yZy9zZXJ2ZXJBL3VzZXJzLzExOTA0MTAxIiwiYXVkIjoiY2lsb2dvbjovY2xpZW50X2lkLzYxN2NlY2RkNzRlMzJiZTRkODE4Y2ExMTUxNTMxZGZmIiwianRpIjoiaHR0cHM6Ly9jaWxvZ29uLm9yZy9vYXV0aDIvaWRUb2tlbi8yNjg0N2EzODcwN2Y5YTk4Y2RmYjEwYjY5OThiNzBkLzE2ODkyNjkwNjU3OTIiLCJhdXRoX3RpbWUiOjE2ODkyNjc4MzgsImV4cCI6MTY4OTI4MzQ2OSwiaWF0IjoxNjg5MjY5MDY5LCJwcm9qZWN0cyI6W3sibmFtZSI6IkNGIFRlc3QiLCJ1dWlkIjoiMTBjMDA5NGEtYWJhZi00ZWY5LWE1MzItMmJlNTNlMmE4OTZiIiwidGFncyI6WyJDb21wb25lbnQuR1BVIiwiQ29tcG9uZW50LlN0b3JhZ2UiXSwibWVtYmVyc2hpcHMiOnsiaXNfY3JlYXRvciI6dHJ1ZSwiaXNfbWVtYmVyIjp0cnVlLCJpc19vd25lciI6dHJ1ZX19XSwicm9sZXMiOlt7ImRlc2NyaXB0aW9uIjoiQ0YgVGVzdCBhbmQgdGVzdCBhbmQgdGVzdCIsIm5hbWUiOiIxMGMwMDk0YS1hYmFmLTRlZjktYTUzMi0yYmU1M2UyYTg5NmItcGMifSx7ImRlc2NyaXB0aW9uIjoiQ0YgVGVzdCBhbmQgdGVzdCBhbmQgdGVzdCIsIm5hbWUiOiIxMGMwMDk0YS1hYmFmLTRlZjktYTUzMi0yYmU1M2UyYTg5NmItcG0ifSx7ImRlc2NyaXB0aW9uIjoiQ0YgVGVzdCBhbmQgdGVzdCBhbmQgdGVzdCIsIm5hbWUiOiIxMGMwMDk0YS1hYmFmLTRlZjktYTUzMi0yYmU1M2UyYTg5NmItcG8ifSx7ImRlc2NyaXB0aW9uIjoiRkFCUklDIFByb2plY3QiLCJuYW1lIjoiOGIzYTJlYWUtYTBjMC00NzVhLTgwN2ItZTlhZjU4MWNlNGMwLXBtIn0seyJkZXNjcmlwdGlvbiI6IlByb2plY3QtVGFncyIsIm5hbWUiOiJiOGQ2NmZiMy1lN2FkLTRkZjktYTY1Mi0yZDhlMzIzMjM2ZTUtcGMifSx7ImRlc2NyaXB0aW9uIjoiUHJvamVjdC1UYWdzIiwibmFtZSI6ImI4ZDY2ZmIzLWU3YWQtNGRmOS1hNjUyLTJkOGUzMjMyMzZlNS1wbyJ9LHsiZGVzY3JpcHRpb24iOiJBY3RpdmUgVXNlcnMgb2YgRkFCUklDIC0gaW5pdGlhbGx5IHNldCBieSBlbnJvbGxtZW50IHdvcmtmbG93IiwibmFtZSI6ImZhYnJpYy1hY3RpdmUtdXNlcnMifSx7ImRlc2NyaXB0aW9uIjoiRmFjaWxpdHkgT3BlcmF0b3JzIGZvciBGQUJSSUMiLCJuYW1lIjoiZmFjaWxpdHktb3BlcmF0b3JzIn0seyJkZXNjcmlwdGlvbiI6IiBKdXB5dGVyaHViIGFjY2VzcyAtIGJhc2VkIG9uIHByb2plY3QgcGFydGljaXBhdGlvbiIsIm5hbWUiOiJKdXB5dGVyaHViIn0seyJkZXNjcmlwdGlvbiI6IlByb2plY3QgTGVhZHMgZm9yIEZBQlJJQyIsIm5hbWUiOiJwcm9qZWN0LWxlYWRzIn1dLCJzY29wZSI6ImFsbCIsInV1aWQiOiIyMjY1YTRkYS1lMWZjLTRmZmMtYmJiNy1kODZkNTY1NzViZmYifQ.OG_m48gCzMt8vcYv947cZduJSbEEzpMuBbbP59SgfD7q1q4_kMbBg36P_SmZMaRfZ6P65hfEdRSnX1x5i4OJQkE47sw1P0Uge6klcnfORIwUyMNphNoKgUcbLRZN9Kch6xKr6JPEfnXKnTdTUMS2-cKzmgLa5w2oIH8kipKisHey7PQhwfdZhwqR8hme5Q7Gwf4O3n45jA8HQQxaO38scIOy0NajQOMPvOkQvfzWOOv79mSztepN2jlJRMGWLqdBql5kSm_xtVRk3S3SdLuS_GTKyA4zfU1-ouT-NUlE9CM86mBHy4FYki5M-cnM3Us3RI7oAxal8kAGqB2I0LY-xMkhHY6k34FCCHiFWvH4WgYmWBEWO7U8uPUq1rbapX6NluQJaSxc54UEA33B9pC_Cgkod6UzB_7g_CNmS31wajjyulCEjIBtlBWTXdmblG8PEZA0_T8DrXh7NPPxF3XrRC74d-IqQ9WiTuH6qrOrMT3ivpeZqPFdriKOXB49EREmUV1iO-STWXRG1RDBLM-lGW7dWOfgsNmlEvW49A8ZO6-T7xVYyVbsVB0XF9oRKAr3uMt_N9ZgF_BP3CKzIi5bhvr4_0AyFifgbrkaKb4qIMOj0XD-Ds1cCDBASqiIj0peal9M8BlBDDMY_80MkRxH1i-XvLRYU5s9t3Oc3p1yd4U" def test_slices_create_post(self): """Test case for slices_create_post @@ -32,7 +31,7 @@ def test_slices_create_post(self): ('ssh_key', 'ssh_key_example'), ('lease_end_time', 'lease_end_time_example')] response = self.client.open( - '/slices/create', + '//slices/create', method='POST', data=json.dumps(body), content_type='application/json', @@ -50,7 +49,7 @@ def test_slices_creates_post(self): ('lease_start_time', 'lease_start_time_example'), ('lease_end_time', 'lease_end_time_example')] response = self.client.open( - '/slices/creates', + '//slices/creates', method='POST', data=json.dumps(body), content_type='application/json', @@ -64,7 +63,7 @@ def test_slices_delete_delete(self): Delete all slices for a User within a project. """ response = self.client.open( - '/slices/delete', + '//slices/delete', method='DELETE') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -75,8 +74,8 @@ def test_slices_delete_slice_id_delete(self): Delete slice. """ response = self.client.open( - '/slices/delete/{slice_id}'.format(slice_id='slice_id_example'), - method='DELETE', ) + '//slices/delete/{slice_id}'.format(slice_id='slice_id_example'), + method='DELETE') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -90,15 +89,12 @@ def test_slices_get(self): ('exact_match', false), ('as_self', true), ('states', 'states_example'), - ('as_self', True), ('limit', 200), - ('offset', 1) - ] + ('offset', 1)] response = self.client.open( - '/slices', + '//slices', method='GET', - query_string=query_string, headers={'AUTHORIZATION': self.TOKEN} - ) + query_string=query_string) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -108,7 +104,7 @@ def test_slices_modify_slice_id_accept_post(self): Accept the last modify an existing slice """ response = self.client.open( - '/slices/modify/{slice_id}/accept'.format(slice_id='slice_id_example'), + '//slices/modify/{slice_id}/accept'.format(slice_id='slice_id_example'), method='POST') self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) @@ -120,7 +116,7 @@ def test_slices_modify_slice_id_put(self): """ body = 'body_example' response = self.client.open( - '/slices/modify/{slice_id}'.format(slice_id='slice_id_example'), + '//slices/modify/{slice_id}'.format(slice_id='slice_id_example'), method='PUT', data=json.dumps(body), content_type='text/plain') @@ -134,7 +130,7 @@ def test_slices_renew_slice_id_post(self): """ query_string = [('lease_end_time', 'lease_end_time_example')] response = self.client.open( - '/slices/renew/{slice_id}'.format(slice_id='slice_id_example'), + '//slices/renew/{slice_id}'.format(slice_id='slice_id_example'), method='POST', query_string=query_string) self.assert200(response, @@ -145,10 +141,10 @@ def test_slices_slice_id_get(self): slice properties """ - query_string = [('as_self', True), + query_string = [('as_self', true), ('graph_format', 'GRAPHML')] response = self.client.open( - '/slices/{slice_id}'.format(slice_id='slice_id_example'), + '//slices/{slice_id}'.format(slice_id='slice_id_example'), method='GET', query_string=query_string) self.assert200(response, diff --git a/fabric_cf/orchestrator/swagger_server/test/test_slivers_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_slivers_controller.py index 24659957..ee1b58cb 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_slivers_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_slivers_controller.py @@ -23,9 +23,9 @@ def test_slivers_get(self): Retrieve a listing of user slivers """ query_string = [('slice_id', 'slice_id_example'), - ('as_self', True)] + ('as_self', true)] response = self.client.open( - '/slivers', + '//slivers', method='GET', query_string=query_string) self.assert200(response, @@ -37,9 +37,9 @@ def test_slivers_sliver_id_get(self): slivers properties """ query_string = [('slice_id', 'slice_id_example'), - ('as_self', True)] + ('as_self', true)] response = self.client.open( - '/slivers/{sliver_id}'.format(sliver_id='sliver_id_example'), + '//slivers/{sliver_id}'.format(sliver_id='sliver_id_example'), method='GET', query_string=query_string) self.assert200(response, diff --git a/fabric_cf/orchestrator/swagger_server/test/test_version_controller.py b/fabric_cf/orchestrator/swagger_server/test/test_version_controller.py index 29bfa789..ad073fe7 100644 --- a/fabric_cf/orchestrator/swagger_server/test/test_version_controller.py +++ b/fabric_cf/orchestrator/swagger_server/test/test_version_controller.py @@ -19,9 +19,8 @@ def test_version_get(self): Version """ response = self.client.open( - '/version', + '//version', method='GET') - print(response.data) self.assert200(response, 'Response body is : ' + response.data.decode('utf-8')) From e8bb52e122854c604a202b179f84ca2fb6be4f8b Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 10 May 2024 10:01:02 -0400 Subject: [PATCH 036/133] update prometheus metrics path --- fabric_cf/orchestrator/nginx/default.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric_cf/orchestrator/nginx/default.conf b/fabric_cf/orchestrator/nginx/default.conf index 754c21c3..fb698444 100644 --- a/fabric_cf/orchestrator/nginx/default.conf +++ b/fabric_cf/orchestrator/nginx/default.conf @@ -36,7 +36,7 @@ server { proxy_pass http://orchestrator:8700; proxy_set_header Host $http_host; } - location /metrics { + location /prom/metrics { proxy_pass http://orchestrator:11000; proxy_set_header Host $http_host; } From e23c8f63e6c438ffd2f341169dc8ee167c65845b Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Sun, 12 May 2024 17:02:06 -0400 Subject: [PATCH 037/133] initial support for p4 switch provisioning --- .../core/manage/actor_management_object.py | 7 ++- .../policy/broker_simpler_units_policy.py | 15 ++++- .../actor/core/policy/network_node_control.py | 1 - .../core/policy/network_node_inventory.py | 62 +++++++++++++++++-- fabric_cf/actor/handlers/no_op_handler.py | 5 +- fabric_cf/authority/test/test.yaml | 9 ++- fabric_cf/broker/test/test.yaml | 2 +- .../core/orchestrator_slice_wrapper.py | 36 ++++++----- .../response/resources_controller.py | 2 +- pyproject.toml | 2 +- 10 files changed, 108 insertions(+), 33 deletions(-) diff --git a/fabric_cf/actor/core/manage/actor_management_object.py b/fabric_cf/actor/core/manage/actor_management_object.py index 597ce371..c8fb8dc0 100644 --- a/fabric_cf/actor/core/manage/actor_management_object.py +++ b/fabric_cf/actor/core/manage/actor_management_object.py @@ -459,10 +459,11 @@ def get_reservations(self, *, caller: AuthToken, states: List[int] = None, if res_list is not None: result.reservations = [] for r in res_list: - slice_id = r.get_slice_id() - slice_obj = self.get_slice_by_guid(guid=slice_id) + r_slice_id = r.get_slice_id() + slice_obj = self.get_slice_by_guid(guid=r_slice_id) r.restore(actor=self.actor, slice_obj=slice_obj) - rr = Converter.fill_reservation(reservation=r, full=True) + full = True if slice_id or rid else False + rr = Converter.fill_reservation(reservation=r, full=full) result.reservations.append(rr) except ReservationNotFoundException as e: self.logger.error("getReservations: {}".format(e)) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 40c6eafa..21dc39f7 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -494,6 +494,8 @@ def __candidate_nodes(self, *, sliver: NodeSliver) -> List[str]: node_props = {ABCPropertyGraphConstants.PROP_SITE: sliver.site, ABCPropertyGraphConstants.PROP_TYPE: str(NodeType.Server)} + if sliver.get_type() == NodeType.Switch: + node_props[ABCPropertyGraphConstants.PROP_TYPE] = str(NodeType.Switch) storage_components = [] # remove storage components before the check @@ -508,6 +510,17 @@ def __candidate_nodes(self, *, sliver: NodeSliver) -> List[str]: label=ABCPropertyGraphConstants.CLASS_NetworkNode, props=node_props, comps=sliver.attached_components_info) + + # Skip nodes without any delegations which would be data-switch in this case + if sliver.get_type() == NodeType.Switch: + exclude = [] + for n in result: + graph_node = self.get_network_node_from_graph(node_id=n) + if graph_node.get_capacity_delegations() is None: + exclude.append(n) + for e in exclude: + result.remove(e) + # re-add storage components if len(storage_components) > 0: for c in storage_components: @@ -903,8 +916,6 @@ def ticket_inventory(self, *, reservation: ABCBrokerReservation, inv: InventoryF # intended link (and possibly interfaces connected to it) res_sliver = rset.get_sliver() - delegation_id = None - sliver = None if isinstance(res_sliver, NodeSliver): delegation_id, sliver, error_msg = self.__allocate_nodes(reservation=reservation, inv=inv, diff --git a/fabric_cf/actor/core/policy/network_node_control.py b/fabric_cf/actor/core/policy/network_node_control.py index 478bd531..ee2473aa 100644 --- a/fabric_cf/actor/core/policy/network_node_control.py +++ b/fabric_cf/actor/core/policy/network_node_control.py @@ -219,7 +219,6 @@ def assign(self, *, reservation: ABCAuthorityReservation, delegation_name: str, 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}") current_sliver = current.get_sliver() diff = current_sliver.diff(other_sliver=requested) diff --git a/fabric_cf/actor/core/policy/network_node_inventory.py b/fabric_cf/actor/core/policy/network_node_inventory.py index 5c4eb8f3..ff636ff3 100644 --- a/fabric_cf/actor/core/policy/network_node_inventory.py +++ b/fabric_cf/actor/core/policy/network_node_inventory.py @@ -31,7 +31,7 @@ from fim.slivers.delegations import Delegations from fim.slivers.instance_catalog import InstanceCatalog from fim.slivers.interface_info import InterfaceSliver -from fim.slivers.network_node import NodeSliver +from fim.slivers.network_node import NodeSliver, NodeType from fim.slivers.network_service import NSLayer from fabric_cf.actor.core.apis.abc_reservation_mixin import ABCReservationMixin @@ -476,6 +476,49 @@ def __check_components(self, *, rid: ID, requested_components: AttachedComponent return requested_components + def __allocate_p4_switch(self, *, rid: ID, requested_sliver: NodeSliver, graph_id: str, graph_node: NodeSliver, + existing_reservations: List[ABCReservationMixin], existing_components: Dict[str, List[str]], + is_create: bool = False) -> Tuple[str, BaseSliver]: + delegation_id = None + + if not is_create: + # In case of modify, directly get delegation_id + if len(graph_node.get_capacity_delegations().get_delegation_ids()) > 0: + delegation_id = next(iter(graph_node.get_capacity_delegations().get_delegation_ids())) + + # Nothing to do, just return + return delegation_id, requested_sliver + + # For create, we need to allocate the P4 + requested_capacities = requested_sliver.get_capacities() + # Check if Capacities can be satisfied + delegation_id = self.__check_capacities(rid=rid, + requested_capacities=requested_capacities, + delegated_capacities=graph_node.get_capacity_delegations(), + existing_reservations=existing_reservations) + requested_sliver.capacity_allocations = Capacities() + requested_sliver.capacity_allocations = Capacities.update(lab=requested_capacities) + requested_sliver.label_allocations = Labels(local_name=graph_node.get_name()) + + requested_sliver.set_node_map(node_map=(graph_id, graph_node.node_id)) + requested_sliver.management_ip = graph_node.management_ip + + ''' + graph_node_ns = next(iter(graph_node.network_service_info.network_services.values())) + if requested_sliver.network_service_info: + requested_ns = next(iter(requested_sliver.network_service_info.network_services.values())) + requested_ns.set_node_map(node_map=(graph_id, graph_node_ns.node_id)) + if requested_ns.interface_info: + for ifs_name, ifs in requested_ns.interface_info.interfaces.items(): + ifs_local_name = graph_node_ns.interface_info.interfaces[ifs_name].labels.local_name + ifs.set_node_map(node_map=(graph_id, ifs_local_name)) + ''' + + self.logger.info(f"Reservation# {rid} is being served by delegation# {delegation_id} " + f"node# [{graph_id}/{graph_node.node_id}]") + + return delegation_id, requested_sliver + def allocate(self, *, rid: ID, requested_sliver: BaseSliver, graph_id: str, graph_node: BaseSliver, existing_reservations: List[ABCReservationMixin], existing_components: Dict[str, List[str]], is_create: bool = False) -> Tuple[str, BaseSliver]: @@ -492,17 +535,26 @@ def allocate(self, *, rid: ID, requested_sliver: BaseSliver, graph_id: str, grap :raises: BrokerException in case the request cannot be satisfied """ if graph_node.get_capacity_delegations() is None or rid is None: - raise BrokerException(error_code=Constants.INVALID_ARGUMENT, + raise BrokerException(error_code=ExceptionErrorCode.INVALID_ARGUMENT, msg=f"capacity_delegations is missing or reservation is None") if not isinstance(requested_sliver, NodeSliver): - raise BrokerException(error_code=Constants.INVALID_ARGUMENT, + raise BrokerException(error_code=ExceptionErrorCode.INVALID_ARGUMENT, msg=f"resource type: {requested_sliver.get_type()}") if not isinstance(graph_node, NodeSliver): - raise BrokerException(error_code=Constants.INVALID_ARGUMENT, + raise BrokerException(error_code=ExceptionErrorCode.INVALID_ARGUMENT, msg=f"resource type: {graph_node.get_type()}") + if requested_sliver.get_type() not in [NodeType.VM, NodeType.Switch]: + raise BrokerException(error_code=ExceptionErrorCode.INVALID_ARGUMENT, + msg=f"Unsupported resource type: {graph_node.get_type()}") + + if requested_sliver.get_type() == NodeType.Switch: + return self.__allocate_p4_switch(rid=rid, requested_sliver=requested_sliver, graph_id=graph_id, + graph_node=graph_node, existing_reservations=existing_reservations, + existing_components=existing_components, is_create=is_create) + delegation_id = None requested_capacities = None # For create, we need to allocate the VM @@ -547,4 +599,4 @@ def allocate(self, *, rid: ID, requested_sliver: BaseSliver, graph_id: str, grap return delegation_id, requested_sliver def free(self, *, count: int, request: dict = None, resource: dict = None) -> dict: - return + pass diff --git a/fabric_cf/actor/handlers/no_op_handler.py b/fabric_cf/actor/handlers/no_op_handler.py index 9c49cdb0..c61bcc13 100644 --- a/fabric_cf/actor/handlers/no_op_handler.py +++ b/fabric_cf/actor/handlers/no_op_handler.py @@ -28,7 +28,7 @@ from typing import Tuple from fim.slivers.attached_components import ComponentType -from fim.slivers.network_node import NodeSliver +from fim.slivers.network_node import NodeSliver, NodeType from fim.slivers.network_service import NetworkServiceSliver, ServiceType from fabric_cf.actor.core.common.constants import Constants @@ -125,7 +125,8 @@ def create(self, unit: ConfigToken) -> Tuple[dict, ConfigToken]: time.sleep(10) if isinstance(sliver, NodeSliver): - self.__process_node_sliver(sliver=sliver) + if sliver.get_type() == NodeType.VM: + self.__process_node_sliver(sliver=sliver) elif isinstance(sliver, NetworkServiceSliver): self.__process_ns_sliver(sliver=sliver) diff --git a/fabric_cf/authority/test/test.yaml b/fabric_cf/authority/test/test.yaml index 125ffea1..41a638d6 100644 --- a/fabric_cf/authority/test/test.yaml +++ b/fabric_cf/authority/test/test.yaml @@ -137,9 +137,16 @@ actor: handler: module: fabric_cf.actor.handlers.no_op_handler class: NoOpHandler + - resource: + type: Switch + label: P4 Switch AM + description: P4 Switch AM + handler: + module: fabric_cf.actor.handlers.no_op_handler + class: NoOpHandler controls: - control: - type: VM, Container, Baremetal + type: VM, Container, Baremetal, Switch module: fabric_cf.actor.core.policy.network_node_control class: NetworkNodeControl diff --git a/fabric_cf/broker/test/test.yaml b/fabric_cf/broker/test/test.yaml index ea7e31f2..68b83af0 100644 --- a/fabric_cf/broker/test/test.yaml +++ b/fabric_cf/broker/test/test.yaml @@ -138,7 +138,7 @@ actor: algorithm: FirstFit controls: - control: - type: VM, Container, Baremetal + type: VM, Container, Baremetal, Switch class: NetworkNodeInventory module: fabric_cf.actor.core.policy.network_node_inventory - control: diff --git a/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py b/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py index 4bd09b13..f3f45c04 100644 --- a/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py +++ b/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py @@ -191,9 +191,12 @@ def __validate_node_sliver(*, sliver: NodeSliver): @param sliver Node Sliver @raises exception for invalid slivers """ - if sliver.get_capacities() is None and sliver.get_capacity_hints() is None: + if sliver.get_type() == NodeType.VM and sliver.get_capacities() is None and sliver.get_capacity_hints() is None: raise OrchestratorException(message="Either Capacity or Capacity Hints must be specified!", http_error_code=BAD_REQUEST) + if sliver.get_type() == NodeType.Switch and sliver.get_capacities() is None: + raise OrchestratorException(message="Either Capacity must be specified!", + http_error_code=BAD_REQUEST) def __build_ns_sliver_reservation(self, *, slice_graph: ABCASMPropertyGraph, node_id: str, node_res_mapping: Dict[str, str]) -> Tuple[LeaseReservationAvro or None, bool]: @@ -406,26 +409,27 @@ def __build_node_sliver_reservation(self, *, slice_graph: ABCASMPropertyGraph, # Build Network Node Sliver sliver = slice_graph.build_deep_node_sliver(node_id=node_id) - if sliver.get_type() not in [NodeType.VM]: + if sliver.get_type() not in [NodeType.VM, NodeType.Switch]: return None # Validate Node Sliver self.__validate_node_sliver(sliver=sliver) - # Compute Requested Capacities from Capacity Hints - requested_capacities = sliver.get_capacities() - requested_capacity_hints = sliver.get_capacity_hints() - catalog = InstanceCatalog() - if requested_capacities is None and requested_capacity_hints is not None: - requested_capacities = catalog.get_instance_capacities( - instance_type=requested_capacity_hints.instance_type) - sliver.set_capacities(cap=requested_capacities) - - # Compute Capacity Hints from Requested Capacities - if requested_capacity_hints is None and requested_capacities is not None: - instance_type = catalog.map_capacities_to_instance(cap=requested_capacities) - requested_capacity_hints = CapacityHints(instance_type=instance_type) - sliver.set_capacity_hints(caphint=requested_capacity_hints) + if sliver.get_type() == NodeType.VM: + # Compute Requested Capacities from Capacity Hints + requested_capacities = sliver.get_capacities() + requested_capacity_hints = sliver.get_capacity_hints() + catalog = InstanceCatalog() + if requested_capacities is None and requested_capacity_hints is not None: + requested_capacities = catalog.get_instance_capacities( + instance_type=requested_capacity_hints.instance_type) + sliver.set_capacities(cap=requested_capacities) + + # Compute Capacity Hints from Requested Capacities + if requested_capacity_hints is None and requested_capacities is not None: + instance_type = catalog.map_capacities_to_instance(cap=requested_capacities) + requested_capacity_hints = CapacityHints(instance_type=instance_type) + sliver.set_capacity_hints(caphint=requested_capacity_hints) # Generate reservation for the sliver reservation = self.reservation_converter.generate_reservation(sliver=sliver, diff --git a/fabric_cf/orchestrator/swagger_server/response/resources_controller.py b/fabric_cf/orchestrator/swagger_server/response/resources_controller.py index 942da8b7..d2749c16 100644 --- a/fabric_cf/orchestrator/swagger_server/response/resources_controller.py +++ b/fabric_cf/orchestrator/swagger_server/response/resources_controller.py @@ -63,7 +63,7 @@ def portalresources_get(graph_format: str, level: int = 1, force_refresh: bool = start = handler.validate_lease_time(lease_time=start_date) end = handler.validate_lease_time(lease_time=end_date) model = handler.list_resources(graph_format_str=graph_format, level=level, force_refresh=force_refresh, - start=start, end=end, includes=includes, excludes=excludes) + start=start, end=end, includes=includes, excludes=excludes, authorize=False) response = Resources() response.data = [Resource(model)] response.size = 1 diff --git a/pyproject.toml b/pyproject.toml index a7f0674d..1e1497be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "PyYAML", "fabric_fss_utils==1.5.0", "fabric-message-bus==1.7.0b1", - "fabric-fim==1.6.1", + "fabric-fim==1.7.0b2", "fabric-credmgr-client==1.6.0", "ansible" ] From dcb73e88d420b107daa98b4d3de08c5bd261cd36 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 13 May 2024 10:09:01 -0400 Subject: [PATCH 038/133] Policy update for allocation of Network Services attached to P4 interfaces --- fabric_cf/actor/core/apis/abc_database.py | 17 +++++--- .../actor/core/kernel/reservation_client.py | 41 +++++++++++-------- .../policy/broker_simpler_units_policy.py | 35 ++++++++-------- .../core/policy/network_node_inventory.py | 33 ++++++++++----- fabric_cf/actor/db/psql_database.py | 14 +++++++ fabric_cf/actor/fim/fim_helper.py | 7 ++++ 6 files changed, 96 insertions(+), 51 deletions(-) diff --git a/fabric_cf/actor/core/apis/abc_database.py b/fabric_cf/actor/core/apis/abc_database.py index b0451540..0d64f244 100644 --- a/fabric_cf/actor/core/apis/abc_database.py +++ b/fabric_cf/actor/core/apis/abc_database.py @@ -174,11 +174,18 @@ def get_reservations(self, *, slice_id: ID = None, graph_node_id: str = None, pr def get_components(self, *, node_id: str, states: list[int], rsv_type: list[str], component: str = None, bdf: str = None, start: datetime = None, end: datetime = None) -> Dict[str, List[str]]: """ - Retrieves the components. - - @return list of components - - @throws Exception in case of error + Returns components matching the search criteria + @param node_id: Worker Node ID to which components belong + @param states: list of states used to find reservations + @param rsv_type: type of reservations + @param component: component name + @param bdf: Component's PCI address + @param start: start time + @param end: end time + + NOTE# For P4 switches; node_id=node+renc-p4-sw component=ip+192.168.11.8 bdf=p1 + + @return Dictionary with component name as the key and value as list of associated PCI addresses in use. """ @abstractmethod diff --git a/fabric_cf/actor/core/kernel/reservation_client.py b/fabric_cf/actor/core/kernel/reservation_client.py index 74f44be8..199ef6e2 100644 --- a/fabric_cf/actor/core/kernel/reservation_client.py +++ b/fabric_cf/actor/core/kernel/reservation_client.py @@ -515,24 +515,29 @@ def prepare_ticket(self, extend: bool = False): if parent_res is not None and (parent_res.is_ticketed() or parent_res.is_active()): node_sliver = parent_res.get_resources().get_sliver() - component = node_sliver.attached_components_info.get_device(name=value1) - 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() - - if component.get_model() == Constants.OPENSTACK_VNIC_MODEL: - ifs.labels = Labels.update(ifs.labels, mac=parent_labs.mac, bdf=parent_labs.bdf, - instance_parent=f"{parent_res.get_reservation_id()}-{node_sliver.get_name()}") - else: - ifs.labels = Labels.update(ifs.labels, mac=parent_labs.mac, vlan=parent_labs.vlan, - bdf=parent_labs.bdf) + # P4 Switch + if node_sliver.get_type() == NodeType.Switch: + graph_id, node_id = node_sliver.get_node_map() + ifs.set_node_map(node_map=(str(NodeType.Switch), node_id)) + else: + component = node_sliver.attached_components_info.get_device(name=value1) + 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() + + if component.get_model() == Constants.OPENSTACK_VNIC_MODEL: + ifs.labels = Labels.update(ifs.labels, mac=parent_labs.mac, bdf=parent_labs.bdf, + instance_parent=f"{parent_res.get_reservation_id()}-{node_sliver.get_name()}") + else: + ifs.labels = Labels.update(ifs.labels, mac=parent_labs.mac, vlan=parent_labs.vlan, + bdf=parent_labs.bdf) self.logger.trace(f"Updated Network Res# {self.get_reservation_id()} {sliver}") diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 21dc39f7..8b8276a1 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -515,8 +515,7 @@ def __candidate_nodes(self, *, sliver: NodeSliver) -> List[str]: if sliver.get_type() == NodeType.Switch: exclude = [] for n in result: - graph_node = self.get_network_node_from_graph(node_id=n) - if graph_node.get_capacity_delegations() is None: + if "p4" not in n: exclude.append(n) for e in exclude: result.remove(e) @@ -665,7 +664,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: error_msg = None owner_ns = None owner_ns_id = None - bqm_component = None + bqm_node = None is_vnic = False owner_mpls_ns = None owner_switch = None @@ -677,29 +676,31 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: node_map_id = self.combined_broker_model_graph_id # Fetch Network Node Id and BQM Component Id - node_id, bqm_component_id = ifs.get_node_map() + node_id, bqm_node_id = ifs.get_node_map() # Skipping the already allocated interface on a modify - #if node_id == self.combined_broker_model_graph_id: if self.combined_broker_model_graph_id in node_id: continue if node_id == str(NodeType.Facility): - bqm_component = self.get_facility_sliver(node_name=bqm_component_id) + bqm_node = self.get_facility_sliver(node_name=bqm_node_id) # Peered Interfaces are handled at the end elif node_id == str(Constants.PEERED): peered_ns_interfaces.append(ifs) continue + elif node_id == str(NodeType.Switch): + bqm_node = self.get_network_node_from_graph(node_id=bqm_node_id) + node_map_id = f"{node_map_id}:{bqm_node.get_name()}:{bqm_node_id}:{ifs.get_labels().local_name}" else: # For VM interfaces - bqm_component = self.get_component_sliver(node_id=bqm_component_id) - node_map_id = f"{node_map_id}:{node_id}:{bqm_component_id}:{ifs.get_labels().bdf}" + bqm_node = self.get_component_sliver(node_id=bqm_node_id) + node_map_id = f"{node_map_id}:{node_id}:{bqm_node_id}:{ifs.get_labels().bdf}" - if bqm_component is None: + if bqm_node is None: raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES) # Get BQM Connection Point in Site Delegation (c) - site_cp = FimHelper.get_site_interface_sliver(component=bqm_component, + site_cp = FimHelper.get_site_interface_sliver(component=bqm_node, local_name=ifs.get_labels().local_name, region=ifs.get_labels().region, device_name=ifs.get_labels().device_name) @@ -725,13 +726,13 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: owner_ns_id = owner_ns_id.replace('ipv6ext-ns', 'ipv6-ns') bqm_cp = net_cp - if bqm_component.get_type() == NodeType.Facility or \ + if bqm_node.get_type() == NodeType.Facility or \ (sliver.get_type() == ServiceType.L2Bridge and - bqm_component.get_model() == Constants.OPENSTACK_VNIC_MODEL): + bqm_node.get_model() == Constants.OPENSTACK_VNIC_MODEL): bqm_cp = site_cp - if bqm_component.get_type() == ComponentType.SharedNIC: - if bqm_component.get_model() == Constants.OPENSTACK_VNIC_MODEL: + if bqm_node.get_type() == ComponentType.SharedNIC: + if bqm_node.get_model() == Constants.OPENSTACK_VNIC_MODEL: is_vnic = True # VLAN is already set by the Orchestrator using the information from the Node Sliver Parent Reservation @@ -767,7 +768,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: # Set the NSO device-name ifs_labels = Labels.update(ifs_labels, device_name=device_name) adm_ids = owner_switch.get_structural_info().adm_graph_ids - site_adm_ids = bqm_component.get_structural_info().adm_graph_ids + site_adm_ids = bqm_node.get_structural_info().adm_graph_ids self.logger.debug(f"Owner Network Service: {owner_ns}") self.logger.debug(f"Owner Switch: {owner_switch}") @@ -775,7 +776,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: self.logger.debug(f"Owner Switch NS: {owner_switch.network_service_info.network_services.values()}") net_adm_ids = site_adm_ids - if bqm_component.get_type() != NodeType.Facility and not is_vnic: + if bqm_node.get_type() != NodeType.Facility and not is_vnic: net_adm_ids = [x for x in adm_ids if not x in site_adm_ids or site_adm_ids.remove(x)] # For sites like EDC which share switch with other sites like NCSA, # the net_adm_ids also includes delegation id from the other side, @@ -820,7 +821,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: # Allocate VLAN for the Network Service if is_vnic: - site_adm_ids = bqm_component.get_structural_info().adm_graph_ids + site_adm_ids = bqm_node.get_structural_info().adm_graph_ids delegation_id = site_adm_ids[0] inv.allocate_vnic(rid=rid, requested_ns=sliver, owner_ns=owner_ns, existing_reservations=existing_reservations) diff --git a/fabric_cf/actor/core/policy/network_node_inventory.py b/fabric_cf/actor/core/policy/network_node_inventory.py index ff636ff3..92304e8d 100644 --- a/fabric_cf/actor/core/policy/network_node_inventory.py +++ b/fabric_cf/actor/core/policy/network_node_inventory.py @@ -479,6 +479,20 @@ def __check_components(self, *, rid: ID, requested_components: AttachedComponent def __allocate_p4_switch(self, *, rid: ID, requested_sliver: NodeSliver, graph_id: str, graph_node: NodeSliver, existing_reservations: List[ABCReservationMixin], existing_components: Dict[str, List[str]], is_create: bool = False) -> Tuple[str, BaseSliver]: + """ + Allocate an extending or ticketing reservation for a P4 switch + + :param rid: reservation id of the reservation to be allocated + :param requested_sliver: requested sliver + :param graph_id: BQM graph id + :param graph_node: BQM graph node identified to serve the reservation + :param existing_components: Existing Components + :param existing_reservations: Existing Reservations served by the same BQM node + :param is_create: Indicates if this is create or modify + + :return: Tuple of Delegation Id and the Requested Sliver annotated with BQM Node Id and other properties + :raises: BrokerException in case the request cannot be satisfied + """ delegation_id = None if not is_create: @@ -489,8 +503,16 @@ def __allocate_p4_switch(self, *, rid: ID, requested_sliver: NodeSliver, graph_i # Nothing to do, just return return delegation_id, requested_sliver + # Handle allocation to account for leaked Network Services + for n in existing_components.keys(): + if n in graph_node.node_id: + raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, + msg=f"Node of type: {graph_node.get_type()} not available on site: " + f"{graph_node.get_site()}, already in use by another reservation") + # For create, we need to allocate the P4 requested_capacities = requested_sliver.get_capacities() + # Check if Capacities can be satisfied delegation_id = self.__check_capacities(rid=rid, requested_capacities=requested_capacities, @@ -503,17 +525,6 @@ def __allocate_p4_switch(self, *, rid: ID, requested_sliver: NodeSliver, graph_i requested_sliver.set_node_map(node_map=(graph_id, graph_node.node_id)) requested_sliver.management_ip = graph_node.management_ip - ''' - graph_node_ns = next(iter(graph_node.network_service_info.network_services.values())) - if requested_sliver.network_service_info: - requested_ns = next(iter(requested_sliver.network_service_info.network_services.values())) - requested_ns.set_node_map(node_map=(graph_id, graph_node_ns.node_id)) - if requested_ns.interface_info: - for ifs_name, ifs in requested_ns.interface_info.interfaces.items(): - ifs_local_name = graph_node_ns.interface_info.interfaces[ifs_name].labels.local_name - ifs.set_node_map(node_map=(graph_id, ifs_local_name)) - ''' - self.logger.info(f"Reservation# {rid} is being served by delegation# {delegation_id} " f"node# [{graph_id}/{graph_node.node_id}]") diff --git a/fabric_cf/actor/db/psql_database.py b/fabric_cf/actor/db/psql_database.py index 304e4a76..899c61d2 100644 --- a/fabric_cf/actor/db/psql_database.py +++ b/fabric_cf/actor/db/psql_database.py @@ -876,6 +876,20 @@ def get_reservations(self, *, slice_id: str = None, graph_node_id: str = None, p def get_components(self, *, node_id: str, states: list[int], rsv_type: list[str], component: str = None, bdf: str = None, start: datetime = None, end: datetime = None) -> Dict[str, List[str]]: + """ + Returns components matching the search criteria + @param node_id: Worker Node ID to which components belong + @param states: list of states used to find reservations + @param rsv_type: type of reservations + @param component: component name + @param bdf: Component's PCI address + @param start: start time + @param end: end time + + NOTE# For P4 switches; node_id=node+renc-p4-sw component=ip+192.168.11.8 bdf=p1 + + @return Dictionary with component name as the key and value as list of associated PCI addresses in use. + """ result = {} session = self.get_session() try: diff --git a/fabric_cf/actor/fim/fim_helper.py b/fabric_cf/actor/fim/fim_helper.py index c9998132..a8b5d1ca 100644 --- a/fabric_cf/actor/fim/fim_helper.py +++ b/fabric_cf/actor/fim/fim_helper.py @@ -441,6 +441,10 @@ def get_interface_sliver_mapping(ifs_node_id: str, slice_graph: ABCASMPropertyGr if peer_ifs.get_type() in [InterfaceType.DedicatedPort, InterfaceType.SharedPort]: component_name, component_id = slice_graph.get_parent(node_id=peer_ns_id, rel=ABCPropertyGraph.REL_HAS, parent=ABCPropertyGraph.CLASS_Component) + # Possibly P4 switch; parent will be a switch + if not component_name: + component_id = peer_ns_id + component_name = str(NodeType.Switch) node_name, node_id = slice_graph.get_parent(node_id=component_id, rel=ABCPropertyGraph.REL_HAS, parent=ABCPropertyGraph.CLASS_NetworkNode) @@ -535,6 +539,9 @@ def get_site_interface_sliver(*, component: ComponentSliver or NodeSliver, local """ result = None for ns in component.network_service_info.network_services.values(): + if not ns.interface_info: + continue + # Filter on region if region is not None: result = list(filter(lambda x: (region in x.labels.region), ns.interface_info.interfaces.values())) From 3b7438a2158020247d6ecd8b85b8fd76755463fb Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 13 May 2024 11:09:32 -0400 Subject: [PATCH 039/133] up versions and config updates --- fabric_cf/__init__.py | 2 +- fabric_cf/authority/config.site.am.yaml | 11 ++++++- fabric_cf/authority/docker-compose.yml | 2 +- fabric_cf/authority/switch_handler_config.yml | 33 +++++++++++++++++++ fabric_cf/broker/config.broker.yaml | 2 +- fabric_cf/broker/docker-compose.yml | 2 +- fabric_cf/orchestrator/docker-compose.yml | 2 +- 7 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 fabric_cf/authority/switch_handler_config.yml diff --git a/fabric_cf/__init__.py b/fabric_cf/__init__.py index 709fa443..5f2ae615 100644 --- a/fabric_cf/__init__.py +++ b/fabric_cf/__init__.py @@ -1,2 +1,2 @@ -__version__ = "1.6.2" +__version__ = "1.7.0b1" __VERSION__ = __version__ diff --git a/fabric_cf/authority/config.site.am.yaml b/fabric_cf/authority/config.site.am.yaml index d42a9ad6..b5e08c37 100644 --- a/fabric_cf/authority/config.site.am.yaml +++ b/fabric_cf/authority/config.site.am.yaml @@ -143,9 +143,18 @@ actor: class: VMHandler properties: config.properties.file: /etc/fabric/actor/config/vm_handler_config.yml + - resource: + type: Switch + label: Switch AM + description: Switch AM + handler: + module: fabric_am.handlers.switch_handler + class: VMHandler + properties: + config.properties.file: /etc/fabric/actor/config/switch_handler_config.yml controls: - control: - type: VM, Container, Baremetal + type: VM, Container, Baremetal, Switch module: fabric_cf.actor.core.policy.network_node_control class: NetworkNodeControl peers: diff --git a/fabric_cf/authority/docker-compose.yml b/fabric_cf/authority/docker-compose.yml index 3cecd2dc..86f8ffbf 100644 --- a/fabric_cf/authority/docker-compose.yml +++ b/fabric_cf/authority/docker-compose.yml @@ -52,7 +52,7 @@ services: network: host context: ../../../ dockerfile: Dockerfile-auth - image: authority:1.6.2 + image: authority:1.7.0 container_name: site1-am restart: always depends_on: diff --git a/fabric_cf/authority/switch_handler_config.yml b/fabric_cf/authority/switch_handler_config.yml new file mode 100644 index 00000000..616c249d --- /dev/null +++ b/fabric_cf/authority/switch_handler_config.yml @@ -0,0 +1,33 @@ +# MIT License +# +# Copyright (c) 2020 FABRIC Testbed +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# +# Author: Komal Thareja (kthare10@renci.org) +ansible: + ansible_python_interpreter: /usr/bin/python3.6 +runtime: +playbooks: + location: /etc/fabric/actor/playbooks + inventory_location: /etc/fabric/actor/playbooks/inventory + admin_ssh_key: /root/.ssh/id_rsa_nova + admin_user: rare + Switch: head_switch_provisioning.yml diff --git a/fabric_cf/broker/config.broker.yaml b/fabric_cf/broker/config.broker.yaml index 18218f74..2e841d6c 100644 --- a/fabric_cf/broker/config.broker.yaml +++ b/fabric_cf/broker/config.broker.yaml @@ -142,7 +142,7 @@ actor: algorithm: FirstFit controls: - control: - type: VM, Container, Baremetal + type: VM, Container, Baremetal, Switch class: NetworkNodeInventory module: fabric_cf.actor.core.policy.network_node_inventory - control: diff --git a/fabric_cf/broker/docker-compose.yml b/fabric_cf/broker/docker-compose.yml index dc8d1151..7df2cb2a 100644 --- a/fabric_cf/broker/docker-compose.yml +++ b/fabric_cf/broker/docker-compose.yml @@ -54,7 +54,7 @@ services: build: context: ../../../ dockerfile: Dockerfile-broker - image: broker:1.6.2 + image: broker:1.7.0 container_name: broker restart: always networks: diff --git a/fabric_cf/orchestrator/docker-compose.yml b/fabric_cf/orchestrator/docker-compose.yml index 4e69cb44..904eebbf 100644 --- a/fabric_cf/orchestrator/docker-compose.yml +++ b/fabric_cf/orchestrator/docker-compose.yml @@ -68,7 +68,7 @@ services: build: context: ../../../ dockerfile: Dockerfile-orchestrator - image: orchestrator:1.6.2 + image: orchestrator:1.7.0 container_name: orchestrator restart: always depends_on: From 31673a02512fe19ef3b9c81ec61e0d73af9af1f3 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 14 May 2024 11:27:45 -0400 Subject: [PATCH 040/133] update switch handler config and error message --- fabric_cf/actor/core/policy/broker_simpler_units_policy.py | 5 +++-- fabric_cf/authority/switch_handler_config.yml | 7 ++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 8b8276a1..41c76ee4 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -606,9 +606,10 @@ def __find_first_fit(self, node_id_list: List[str], node_id_to_reservations: dic else: raise e - if delegation_id is None and requested_sliver.labels is not None and requested_sliver.labels.instance_parent is not None: + if delegation_id is None and requested_sliver.labels is not None and \ + requested_sliver.labels.instance_parent is not None: error_msg = f"Insufficient Resources: {requested_sliver.labels.instance_parent} " \ - f"cannot serve the requested sliver" + f"cannot serve the requested sliver - {error_msg}" return delegation_id, sliver, error_msg diff --git a/fabric_cf/authority/switch_handler_config.yml b/fabric_cf/authority/switch_handler_config.yml index 616c249d..c5a3fbe4 100644 --- a/fabric_cf/authority/switch_handler_config.yml +++ b/fabric_cf/authority/switch_handler_config.yml @@ -22,12 +22,9 @@ # # # Author: Komal Thareja (kthare10@renci.org) -ansible: - ansible_python_interpreter: /usr/bin/python3.6 -runtime: playbooks: location: /etc/fabric/actor/playbooks inventory_location: /etc/fabric/actor/playbooks/inventory - admin_ssh_key: /root/.ssh/id_rsa_nova - admin_user: rare Switch: head_switch_provisioning.yml + cleanup: + ALL: head_switch_provisioning.yml \ No newline at end of file From 90af5fa5ff28646c3067bb7497bee8d2e98c54cc Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 14 May 2024 11:29:30 -0400 Subject: [PATCH 041/133] updated gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 14aeaa9e..5851e28b 100644 --- a/.gitignore +++ b/.gitignore @@ -151,3 +151,6 @@ secrets/kafkacat2-ca1-signed.pem secrets/kafkacat1-ca1-signed.pem secrets/... neo4j/RENC.graphml +neo4j/Network-dev.graphml +.DS_Store +*.log* From 44417c743a3a56cf1896d88836d0cb301630d052 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 14 May 2024 11:30:00 -0400 Subject: [PATCH 042/133] updated gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5851e28b..ed2c6c8f 100644 --- a/.gitignore +++ b/.gitignore @@ -154,3 +154,4 @@ neo4j/RENC.graphml neo4j/Network-dev.graphml .DS_Store *.log* +*.avsc From 41db93cdb5d1ac83a0f53831152196965de084dd Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 14 May 2024 11:31:48 -0400 Subject: [PATCH 043/133] up versions and dep --- Dockerfile-auth | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile-auth b/Dockerfile-auth index ec955fac..5177e79c 100644 --- a/Dockerfile-auth +++ b/Dockerfile-auth @@ -1,7 +1,7 @@ FROM python:3.11.0 MAINTAINER Komal Thareja -ARG HANDLERS_VER=1.6.3 +ARG HANDLERS_VER=1.7.0b1 RUN mkdir -p /usr/src/app WORKDIR /usr/src/app From 824f8f03e4287eb800a37c2ead395a867f3636cd Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 14 May 2024 11:38:49 -0400 Subject: [PATCH 044/133] fix the handler name in config --- fabric_cf/authority/config.site.am.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric_cf/authority/config.site.am.yaml b/fabric_cf/authority/config.site.am.yaml index b5e08c37..1d754062 100644 --- a/fabric_cf/authority/config.site.am.yaml +++ b/fabric_cf/authority/config.site.am.yaml @@ -149,7 +149,7 @@ actor: description: Switch AM handler: module: fabric_am.handlers.switch_handler - class: VMHandler + class: SwitchHandler properties: config.properties.file: /etc/fabric/actor/config/switch_handler_config.yml controls: From c54c271e43497d268191ecf7a85faf5496590a03 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 14 May 2024 12:43:33 -0400 Subject: [PATCH 045/133] add switch config to docker --- fabric_cf/authority/docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/fabric_cf/authority/docker-compose.yml b/fabric_cf/authority/docker-compose.yml index 86f8ffbf..eae01309 100644 --- a/fabric_cf/authority/docker-compose.yml +++ b/fabric_cf/authority/docker-compose.yml @@ -65,6 +65,7 @@ services: - ./arm.graphml:/etc/fabric/actor/config/neo4j/arm.graphml - ./logs/:/var/log/actor - ./vm_handler_config.yml:/etc/fabric/actor/config/vm_handler_config.yml + - ./switch_handler_config.yml:/etc/fabric/actor/config/switch_handler_config.yml - ../../../../AMHandlers/fabric_am/playbooks:/etc/fabric/actor/playbooks - ../../../../AMHandlers/fabric_am/playbooks/inventory:/etc/fabric/actor/playbooks/inventory - ~/.ssh:/root/.ssh From 625e9defa4b0c3d9eca350e0df5673ddb496d375 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 14 May 2024 12:48:28 -0400 Subject: [PATCH 046/133] install sshpass in am container --- Dockerfile-auth | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile-auth b/Dockerfile-auth index 5177e79c..2b00a9cb 100644 --- a/Dockerfile-auth +++ b/Dockerfile-auth @@ -11,6 +11,7 @@ EXPOSE 11000 RUN apt-get update RUN apt-get install cron -y +RUN apt-get install sshpass -y COPY docker-entrypoint.sh /usr/src/app/ COPY fabric_cf /usr/src/app/fabric_cf From e86cb13b1bfb33be4e93b47503113c2cbd49e981 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 28 May 2024 14:01:38 -0400 Subject: [PATCH 047/133] maintain in use vlans in Components table --- fabric_cf/actor/core/kernel/kernel.py | 2 +- .../actor/core/manage/management_utils.py | 1 - .../actor/core/plugins/db/actor_database.py | 7 +++-- .../policy/broker_simpler_units_policy.py | 5 +++- .../plugins/broker/aggregate_bqm_plugin.py | 27 +++++++++++++++++++ pyproject.toml | 2 +- 6 files changed, 38 insertions(+), 6 deletions(-) diff --git a/fabric_cf/actor/core/kernel/kernel.py b/fabric_cf/actor/core/kernel/kernel.py index 6721efda..8cd184e2 100644 --- a/fabric_cf/actor/core/kernel/kernel.py +++ b/fabric_cf/actor/core/kernel/kernel.py @@ -584,7 +584,7 @@ def __probe_pending_slices(self, *, slice_obj: ABCSlice): state_changed, slice_state = slice_obj.transition_slice(operation=SliceStateMachine.REEVALUATE) if state_changed: slice_obj.set_dirty() - if slice_state == SliceState.Closing: + if slice_state in [SliceState.Dead, SliceState.Closing]: slice_avro = Translate.translate_slice_to_avro(slice_obj=slice_obj) EventLoggerSingleton.get().log_slice_event(slice_object=slice_avro, action=ActionId.delete) self.plugin.get_database().update_slice(slice_object=slice_obj) diff --git a/fabric_cf/actor/core/manage/management_utils.py b/fabric_cf/actor/core/manage/management_utils.py index bc58a7fa..2b2e8aad 100644 --- a/fabric_cf/actor/core/manage/management_utils.py +++ b/fabric_cf/actor/core/manage/management_utils.py @@ -34,7 +34,6 @@ from fabric_cf.actor.core.common.constants import Constants from fabric_cf.actor.core.apis.abc_mgmt_container import ABCMgmtContainer from fabric_cf.actor.core.manage.local.local_container import LocalContainer -from fabric_cf.actor.core.proxies.kafka.translate import Translate from fabric_cf.actor.core.util.id import ID from fabric_cf.actor.core.util.rpc_exception import RPCException, RPCError from fabric_cf.actor.core.apis.abc_client_reservation import ABCClientReservation diff --git a/fabric_cf/actor/core/plugins/db/actor_database.py b/fabric_cf/actor/core/plugins/db/actor_database.py index bebc0156..ceef5eb9 100644 --- a/fabric_cf/actor/core/plugins/db/actor_database.py +++ b/fabric_cf/actor/core/plugins/db/actor_database.py @@ -294,8 +294,11 @@ def add_reservation(self, *, reservation: ABCReservationMixin): components = [] for interface in sliver.interface_info.interfaces.values(): graph_id_node_id_component_id, bqm_if_name = interface.get_node_map() - if ":" in graph_id_node_id_component_id: - split_string = graph_id_node_id_component_id.split(":") + if ":" in graph_id_node_id_component_id or "#" in graph_id_node_id_component_id: + if "#" in graph_id_node_id_component_id: + split_string = graph_id_node_id_component_id.split("#") + else: + split_string = graph_id_node_id_component_id.split(":") node_id = split_string[1] if len(split_string) > 1 else None comp_id = split_string[2] if len(split_string) > 2 else None bdf = ":".join(split_string[3:]) if len(split_string) > 3 else None diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 41c76ee4..bbb3df69 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -691,7 +691,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: continue elif node_id == str(NodeType.Switch): bqm_node = self.get_network_node_from_graph(node_id=bqm_node_id) - node_map_id = f"{node_map_id}:{bqm_node.get_name()}:{bqm_node_id}:{ifs.get_labels().local_name}" + node_map_id = f"{node_map_id}#{bqm_node.get_name()}#{bqm_node_id}#{ifs.get_labels().local_name}" else: # For VM interfaces bqm_node = self.get_component_sliver(node_id=bqm_node_id) @@ -801,6 +801,9 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: self.logger.error(error_msg) raise BrokerException(msg=error_msg) + if bqm_node.get_type() == NodeType.Facility: + node_map_id = f"{node_map_id}#{bqm_node.get_name()}#{ifs.get_labels().local_name}#{ifs.get_labels().vlan}" + # Update the Interface Sliver Node Map to map to (a) ifs.set_node_map(node_map=(node_map_id, bqm_cp.node_id)) #ifs.set_node_map(node_map=(self.combined_broker_model_graph_id, bqm_cp.node_id)) diff --git a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py index 648ef126..33bd51a7 100644 --- a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py +++ b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py @@ -160,10 +160,13 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope # this includes facilities nnodes = cbm.get_all_nodes_by_class(label=ABCPropertyGraph.CLASS_NetworkNode) slivers_by_site = defaultdict(list) + p4s_by_site = defaultdict(list) for n in nnodes: # build deep slivers for each advertised server, aggregate by site node_sliver = cbm.build_deep_node_sliver(node_id=n) slivers_by_site[node_sliver.site].append(node_sliver) + if node_sliver.get_type() == NodeType.Switch and "p4" in node_sliver.get_name(): + p4s_by_site[node_sliver.site].append(node_sliver) # create a new blank Aggregated BQM NetworkX graph if kwargs['query_level'] == 0: @@ -305,6 +308,30 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope abqm.add_node(node_id=site_sliver.node_id, label=ABCPropertyGraph.CLASS_CompositeNode, props=site_props) + p4s = p4s_by_site.get(site_sliver.site) + if p4s: + for p4 in p4s: + p4_sliver = NodeSliver() + p4_sliver.node_id = str(uuid.uuid4()) + p4_sliver.set_type(resource_type=NodeType.Switch) + p4_sliver.set_name(resource_name=p4.get_name()) + p4_sliver.set_site(site=p4.get_site()) + p4_sliver.capacities = Capacities() + p4_sliver.capacity_allocations = Capacities() + p4_sliver.capacities += p4.get_capacities() + # query database for everything taken on this node + db = self.actor.get_plugin().get_database() + allocated_caps, allocated_comp_caps = self.occupied_node_capacity(db=db, node_id=p4.node_id, + start=start, end=end) + if allocated_caps: + p4_sliver.capacity_allocations = p4_sliver.capacity_allocations + allocated_caps + p4_props = abqm.node_sliver_to_graph_properties_dict(p4_sliver) + node_id = p4.node_id + if kwargs['query_level'] != 0: + node_id = p4_sliver.node_id + abqm.add_node(node_id=node_id, label=ABCPropertyGraph.CLASS_NetworkNode, props=p4_props) + abqm.add_link(node_a=site_sliver.node_id, rel=ABCPropertyGraph.REL_HAS, node_b=node_id) + # Add per worker metrics for query level 2 if kwargs['query_level'] == 2 or kwargs['query_level'] == 0: for w in workers: diff --git a/pyproject.toml b/pyproject.toml index 1e1497be..1d81881b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "PyYAML", "fabric_fss_utils==1.5.0", "fabric-message-bus==1.7.0b1", - "fabric-fim==1.7.0b2", + "fabric-fim==1.7.0b3", "fabric-credmgr-client==1.6.0", "ansible" ] From c1ae33f07fb1bf32c27c779bc9e90b0e9b337ae2 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 28 May 2024 14:04:47 -0400 Subject: [PATCH 048/133] random vlan selection --- .../actor/core/policy/network_service_inventory.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fabric_cf/actor/core/policy/network_service_inventory.py b/fabric_cf/actor/core/policy/network_service_inventory.py index 4d3abc27..e01a9e60 100644 --- a/fabric_cf/actor/core/policy/network_service_inventory.py +++ b/fabric_cf/actor/core/policy/network_service_inventory.py @@ -163,8 +163,8 @@ def allocate_ifs(self, *, requested_ns: NetworkServiceSliver, requested_ifs: Int existing_reservations=existing_reservations) if requested_vlan is None: - #requested_ifs.labels.vlan = str(random.choice(vlan_range)) - requested_ifs.labels.vlan = str(vlan_range[0]) + requested_ifs.labels.vlan = str(random.choice(vlan_range)) + #requested_ifs.labels.vlan = str(vlan_range[0]) return requested_ifs if requested_vlan not in vlan_range: @@ -196,7 +196,7 @@ def allocate_ifs(self, *, requested_ns: NetworkServiceSliver, requested_ifs: Int return requested_ifs if requested_ifs.labels.vlan is None: - #requested_ifs.labels.vlan = str(random.choice(vlan_range)) + requested_ifs.labels.vlan = str(random.choice(vlan_range)) requested_ifs.labels.vlan = str(vlan_range[0]) if int(requested_ifs.labels.vlan) not in vlan_range: @@ -452,8 +452,8 @@ def allocate_peered_ifs(self, *, owner_switch: NodeSliver, available_vlans = self.__exclude_allocated_vlans(available_vlan_range=vlan_range, bqm_ifs=bqm_interface, existing_reservations=existing_reservations) - #vlan = str(random.choice(available_vlans)) - vlan = str(available_vlans[0]) + vlan = str(random.choice(available_vlans)) + #vlan = str(available_vlans[0]) ifs_labels = Labels.update(ifs_labels, vlan=vlan) requested_ifs.labels = ifs_labels From c932c49507468d8fcd58c08cb0a3525718490b06 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 28 May 2024 14:43:52 -0400 Subject: [PATCH 049/133] db update to populate components for interfaces --- fabric_cf/actor/core/plugins/db/actor_database.py | 7 +++++-- fabric_cf/actor/core/policy/broker_simpler_units_policy.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/fabric_cf/actor/core/plugins/db/actor_database.py b/fabric_cf/actor/core/plugins/db/actor_database.py index ceef5eb9..49c4c435 100644 --- a/fabric_cf/actor/core/plugins/db/actor_database.py +++ b/fabric_cf/actor/core/plugins/db/actor_database.py @@ -347,8 +347,11 @@ def update_reservation(self, *, reservation: ABCReservationMixin): components = [] for interface in sliver.interface_info.interfaces.values(): graph_id_node_id_component_id, bqm_if_name = interface.get_node_map() - if ":" in graph_id_node_id_component_id: - split_string = graph_id_node_id_component_id.split(":") + if ":" in graph_id_node_id_component_id or "#" in graph_id_node_id_component_id: + if "#" in graph_id_node_id_component_id: + split_string = graph_id_node_id_component_id.split("#") + else: + split_string = graph_id_node_id_component_id.split(":") node_id = split_string[1] if len(split_string) > 1 else None comp_id = split_string[2] if len(split_string) > 2 else None bdf = ":".join(split_string[3:]) if len(split_string) > 3 else None diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index bbb3df69..fddce51c 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -802,7 +802,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: raise BrokerException(msg=error_msg) if bqm_node.get_type() == NodeType.Facility: - node_map_id = f"{node_map_id}#{bqm_node.get_name()}#{ifs.get_labels().local_name}#{ifs.get_labels().vlan}" + node_map_id = f"{node_map_id}#{bqm_node.get_name()}#{ifs_labels.local_name}#{ifs_labels.vlan}" # Update the Interface Sliver Node Map to map to (a) ifs.set_node_map(node_map=(node_map_id, bqm_cp.node_id)) From 9957104cc63e024ec36a2911af3395bbd8a9aa9e Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 28 May 2024 16:23:49 -0400 Subject: [PATCH 050/133] returning in use vlan information --- .../policy/broker_simpler_units_policy.py | 2 +- .../plugins/broker/aggregate_bqm_plugin.py | 45 ++++++++++++++++--- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index fddce51c..545d03bc 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -802,7 +802,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: raise BrokerException(msg=error_msg) if bqm_node.get_type() == NodeType.Facility: - node_map_id = f"{node_map_id}#{bqm_node.get_name()}#{ifs_labels.local_name}#{ifs_labels.vlan}" + node_map_id = f"{node_map_id}#{bqm_node.get_name()}#{bqm_cp.node_id}#{ifs_labels.vlan}" # Update the Interface Sliver Node Map to map to (a) ifs.set_node_map(node_map=(node_map_id, bqm_cp.node_id)) diff --git a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py index 33bd51a7..61ea6643 100644 --- a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py +++ b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py @@ -26,7 +26,7 @@ from __future__ import annotations from datetime import datetime -from typing import Tuple, Dict, TYPE_CHECKING +from typing import Tuple, Dict, TYPE_CHECKING, List from collections import defaultdict import uuid @@ -36,7 +36,7 @@ from fim.graph.resources.abc_bqm import ABCBQMPropertyGraph from fim.graph.networkx_property_graph import NetworkXGraphImporter from fim.graph.resources.networkx_abqm import NetworkXAggregateBQM -from fim.slivers.capacities_labels import Capacities, Flags +from fim.slivers.capacities_labels import Capacities, Flags, Labels from fim.slivers.delegations import DelegationFormat from fim.slivers.maintenance_mode import MaintenanceInfo, MaintenanceEntry, MaintenanceState from fim.slivers.network_node import CompositeNodeSliver, NodeType, NodeSliver @@ -86,6 +86,35 @@ def __site_maintenance_info(self, *, site_name: str): result.finalize() return result + @staticmethod + def occupied_vlans(db: ABCDatabase, node_id: str, component_name: str, start: datetime = None, + end: datetime = None) -> List[str]: + """ + Get existing components attached to Active/Ticketed Network Service Slivers + :param db: + :param node_id: + :param component_name: + :param start: + :param end: + :return: list of components + """ + assert node_id is not None + states = [ReservationStates.Active.value, + ReservationStates.ActiveTicketed.value, + ReservationStates.Ticketed.value, + ReservationStates.Nascent.value] + + result = [] + res_type = [] + for x in ServiceType: + res_type.append(str(x)) + + # Only get Active or Ticketing reservations + comps = db.get_components(node_id=node_id, component=component_name, rsv_type=res_type, states=states, + start=start, end=end) + if comps is not None: + return comps.get(component_name) + @staticmethod def occupied_node_capacity(*, db: ABCDatabase, node_id: str, start: datetime, end: datetime) -> Tuple[Capacities, Dict[ComponentType, Dict[str, Capacities]]]: @@ -154,6 +183,7 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope start = kwargs.get('start', None) end = kwargs.get('end', None) + db = self.actor.get_plugin().get_database() # do a one-pass aggregation of servers, their components and interfaces # and some flags (e.g. PTP availability) @@ -227,7 +257,6 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope allocated_comp_caps = dict() worker_sliver.node_id = sliver.node_id else: - db = self.actor.get_plugin().get_database() # query database for everything taken on this node allocated_caps, allocated_comp_caps = self.occupied_node_capacity(db=db, node_id=sliver.node_id, start=start, end=end) @@ -320,7 +349,6 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope p4_sliver.capacity_allocations = Capacities() p4_sliver.capacities += p4.get_capacities() # query database for everything taken on this node - db = self.actor.get_plugin().get_database() allocated_caps, allocated_comp_caps = self.occupied_node_capacity(db=db, node_id=p4.node_id, start=start, end=end) if allocated_caps: @@ -465,6 +493,11 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope _, fac_ns_props = cbm.get_node_properties(node_id=fac_ns_node_id) _, fac_cp_props = cbm.get_node_properties(node_id=fac_cp_node_id) + allocated_vlans = self.occupied_vlans(db=db, node_id=fac_sliver.resource_name, + component_name=fac_sliver.node_id, start=start, end=end) + labels = Labels() + labels.vlan = allocated_vlans + # filter down only the needed properties then recreate the structure of facility in ABQM new_fac_props = {ABCPropertyGraph.PROP_NAME: fac_props[ABCPropertyGraph.PROP_NAME], ABCPropertyGraph.PROP_TYPE: fac_props[ABCPropertyGraph.PROP_TYPE], @@ -482,6 +515,8 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope ABCPropertyGraph.PROP_LABELS: fac_cp_props.get(ABCPropertyGraph.PROP_LABELS), ABCPropertyGraph.PROP_CAPACITIES: fac_cp_props.get(ABCPropertyGraph.PROP_CAPACITIES) } + if len(labels.vlan): + new_cp_props[ABCPropertyGraph.PROP_LABEL_ALLOCATIONS] = labels.to_dict() new_cp_props = {k: v for (k, v) in new_cp_props.items() if v} abqm.add_node(node_id=fac_cp_node_id, label=ABCPropertyGraph.CLASS_ConnectionPoint, props=new_cp_props) @@ -515,7 +550,7 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope abqm.add_node(node_id=fac_link_id, label=ABCPropertyGraph.CLASS_Link, props=new_link_props) try: - abqm.get_node_properties(node_id=fac_sp_id) + new_sp_props = abqm.get_node_properties(node_id=fac_sp_id) except PropertyGraphQueryException: # if the node doesn't exist we need to create it (it could have been created in the first pass) _, fac_sp_props = cbm.get_node_properties(node_id=fac_sp_id) From e4834a4583215af33b0adac0d8062c2c60917188 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 29 May 2024 09:32:52 -0400 Subject: [PATCH 051/133] pass all CPs for FacilityPorts --- .../plugins/broker/aggregate_bqm_plugin.py | 165 ++++++++++-------- 1 file changed, 92 insertions(+), 73 deletions(-) diff --git a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py index 61ea6643..c9c1cdba 100644 --- a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py +++ b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py @@ -113,7 +113,9 @@ def occupied_vlans(db: ABCDatabase, node_id: str, component_name: str, start: da comps = db.get_components(node_id=node_id, component=component_name, rsv_type=res_type, states=states, start=start, end=end) if comps is not None: - return comps.get(component_name) + if comps.get(component_name): + result = comps.get(component_name) + return result @staticmethod def occupied_node_capacity(*, db: ABCDatabase, node_id: str, start: datetime, @@ -473,100 +475,117 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope for s, lf in facilities_by_site.items(): # multiple facilities per site possible for fac_sliver in lf: - fac_nbs = cbm.get_first_and_second_neighbor(node_id=fac_sliver.node_id, - rel1=ABCPropertyGraph.REL_HAS, - node1_label=ABCPropertyGraph.CLASS_NetworkService, - rel2=ABCPropertyGraph.REL_CONNECTS, - node2_label=ABCPropertyGraph.CLASS_ConnectionPoint) - try: - fac_ns_node_id = fac_nbs[0][0] - fac_cp_node_id = fac_nbs[0][1] - except KeyError: + ns_list = cbm.get_first_neighbor(node_id=fac_sliver.node_id, + rel=ABCPropertyGraph.REL_HAS, + node_label=ABCPropertyGraph.CLASS_NetworkService) + + if not ns_list or not len(ns_list): if self.logger: - self.logger.warning(f'Unable to trace facility ConnectionPoint for ' + self.logger.warning(f'Unable to trace facility NetworkService for ' f'facility {fac_sliver.resource_name}, continuing') else: - print(f'Unable to trace facility ConnectionPoint for ' + print(f'Unable to trace facility NetworkService for ' f'facility {fac_sliver.resource_name}, continuing') continue - _, fac_props = cbm.get_node_properties(node_id=fac_sliver.node_id) - _, fac_ns_props = cbm.get_node_properties(node_id=fac_ns_node_id) - _, fac_cp_props = cbm.get_node_properties(node_id=fac_cp_node_id) - - allocated_vlans = self.occupied_vlans(db=db, node_id=fac_sliver.resource_name, - component_name=fac_sliver.node_id, start=start, end=end) - labels = Labels() - labels.vlan = allocated_vlans - # filter down only the needed properties then recreate the structure of facility in ABQM + _, fac_props = cbm.get_node_properties(node_id=fac_sliver.node_id) new_fac_props = {ABCPropertyGraph.PROP_NAME: fac_props[ABCPropertyGraph.PROP_NAME], ABCPropertyGraph.PROP_TYPE: fac_props[ABCPropertyGraph.PROP_TYPE], ABCPropertyGraph.PROP_SITE: s } abqm.add_node(node_id=fac_sliver.node_id, label=ABCPropertyGraph.CLASS_NetworkNode, props=new_fac_props) + + fac_ns_node_id = ns_list[0] + _, fac_ns_props = cbm.get_node_properties(node_id=fac_ns_node_id) + + # filter down only the needed properties then recreate the structure of facility in ABQM new_ns_props = {ABCPropertyGraph.PROP_NAME: fac_ns_props[ABCPropertyGraph.PROP_NAME], ABCPropertyGraph.PROP_TYPE: fac_ns_props[ABCPropertyGraph.PROP_TYPE] } + abqm.add_node(node_id=fac_ns_node_id, label=ABCPropertyGraph.CLASS_NetworkService, props=new_ns_props) - new_cp_props = {ABCPropertyGraph.PROP_NAME: fac_cp_props[ABCPropertyGraph.PROP_NAME], - ABCPropertyGraph.PROP_TYPE: fac_cp_props[ABCPropertyGraph.PROP_TYPE], - ABCPropertyGraph.PROP_LABELS: fac_cp_props.get(ABCPropertyGraph.PROP_LABELS), - ABCPropertyGraph.PROP_CAPACITIES: fac_cp_props.get(ABCPropertyGraph.PROP_CAPACITIES) - } - if len(labels.vlan): - new_cp_props[ABCPropertyGraph.PROP_LABEL_ALLOCATIONS] = labels.to_dict() - new_cp_props = {k: v for (k, v) in new_cp_props.items() if v} - abqm.add_node(node_id=fac_cp_node_id, label=ABCPropertyGraph.CLASS_ConnectionPoint, - props=new_cp_props) + abqm.add_link(node_a=fac_sliver.node_id, rel=ABCPropertyGraph.REL_HAS, node_b=fac_ns_node_id) - abqm.add_link(node_a=fac_ns_node_id, rel=ABCPropertyGraph.REL_CONNECTS, node_b=fac_cp_node_id) - - # trace the link to a switch port/ConnectionPoint and replicate them for simplicity - fac_cp_nbs = cbm.get_first_and_second_neighbor(node_id=fac_cp_node_id, - rel1=ABCPropertyGraph.REL_CONNECTS, - node1_label=ABCPropertyGraph.CLASS_Link, - rel2=ABCPropertyGraph.REL_CONNECTS, - node2_label=ABCPropertyGraph.CLASS_ConnectionPoint) - if len(fac_cp_nbs) == 0 or len(fac_cp_nbs) > 1: + + fac_ns_cp_list = cbm.get_all_ns_or_link_connection_points(link_id=ns_list[0]) + if not fac_ns_cp_list: if self.logger: - self.logger.warning(f'Unable to trace switch port from Facility port ' - f'for facility {fac_sliver.resource_name} {fac_cp_nbs}') + self.logger.warning(f'Unable to trace facility ConnectionPoint for ' + f'facility {fac_sliver.resource_name}, continuing') else: - print(f'Unable to trace switch port from Facility port ' - f'for facility {fac_sliver.resource_name} {fac_cp_nbs}') + print(f'Unable to trace facility ConnectionPoint for ' + f'facility {fac_sliver.resource_name}, continuing') continue - fac_link_id = fac_cp_nbs[0][0] - fac_sp_id = fac_cp_nbs[0][1] - - _, fac_link_props = cbm.get_node_properties(node_id=fac_link_id) - # selectively replicate link properties - new_link_props = {ABCPropertyGraph.PROP_NAME: fac_link_props[ABCPropertyGraph.PROP_NAME], - ABCPropertyGraph.PROP_TYPE: fac_link_props[ABCPropertyGraph.PROP_TYPE], - ABCPropertyGraph.PROP_LAYER: fac_link_props[ABCPropertyGraph.PROP_LAYER] - } - abqm.add_node(node_id=fac_link_id, label=ABCPropertyGraph.CLASS_Link, - props=new_link_props) - try: - new_sp_props = abqm.get_node_properties(node_id=fac_sp_id) - except PropertyGraphQueryException: - # if the node doesn't exist we need to create it (it could have been created in the first pass) - _, fac_sp_props = cbm.get_node_properties(node_id=fac_sp_id) - new_sp_props = {ABCPropertyGraph.PROP_NAME: fac_sp_props[ABCPropertyGraph.PROP_NAME], - ABCPropertyGraph.PROP_TYPE: fac_sp_props[ABCPropertyGraph.PROP_TYPE], - ABCPropertyGraph.PROP_CAPACITIES: fac_sp_props.get( - ABCPropertyGraph.PROP_CAPACITIES), - ABCPropertyGraph.PROP_LABELS: fac_sp_props.get(ABCPropertyGraph.PROP_LABELS) + for fac_cp_node_id in fac_ns_cp_list: + _, fac_cp_props = cbm.get_node_properties(node_id=fac_cp_node_id) + + allocated_vlans = self.occupied_vlans(db=db, node_id=fac_sliver.resource_name, + component_name=fac_cp_node_id, start=start, end=end) + labels = Labels() + labels.vlan = allocated_vlans + + new_cp_props = {ABCPropertyGraph.PROP_NAME: fac_cp_props[ABCPropertyGraph.PROP_NAME], + ABCPropertyGraph.PROP_TYPE: fac_cp_props[ABCPropertyGraph.PROP_TYPE], + ABCPropertyGraph.PROP_LABELS: fac_cp_props.get(ABCPropertyGraph.PROP_LABELS), + ABCPropertyGraph.PROP_CAPACITIES: fac_cp_props.get(ABCPropertyGraph.PROP_CAPACITIES) } - new_sp_props = {k: v for (k, v) in new_sp_props.items() if v} - abqm.add_node(node_id=fac_sp_id, label=ABCPropertyGraph.CLASS_ConnectionPoint, - props=new_sp_props) - - # link these together - abqm.add_link(node_a=fac_cp_node_id, rel=ABCPropertyGraph.REL_CONNECTS, node_b=fac_link_id) - abqm.add_link(node_a=fac_link_id, rel=ABCPropertyGraph.REL_CONNECTS, node_b=fac_sp_id) - abqm.add_link(node_a=fac_sp_id, rel=ABCPropertyGraph.REL_CONNECTS, node_b=site_to_ns_node_id[s]) + + if len(labels.vlan): + new_cp_props[ABCPropertyGraph.PROP_LABEL_ALLOCATIONS] = labels.to_dict() + + new_cp_props = {k: v for (k, v) in new_cp_props.items() if v} + + abqm.add_node(node_id=fac_cp_node_id, label=ABCPropertyGraph.CLASS_ConnectionPoint, + props=new_cp_props) + abqm.add_link(node_a=fac_ns_node_id, rel=ABCPropertyGraph.REL_CONNECTS, node_b=fac_cp_node_id) + + # trace the link to a switch port/ConnectionPoint and replicate them for simplicity + fac_cp_nbs = cbm.get_first_and_second_neighbor(node_id=fac_cp_node_id, + rel1=ABCPropertyGraph.REL_CONNECTS, + node1_label=ABCPropertyGraph.CLASS_Link, + rel2=ABCPropertyGraph.REL_CONNECTS, + node2_label=ABCPropertyGraph.CLASS_ConnectionPoint) + if len(fac_cp_nbs) == 0 or len(fac_cp_nbs) > 1: + if self.logger: + self.logger.warning(f'Unable to trace switch port from Facility port ' + f'for facility {fac_sliver.resource_name} {fac_cp_nbs}') + else: + print(f'Unable to trace switch port from Facility port ' + f'for facility {fac_sliver.resource_name} {fac_cp_nbs}') + continue + + fac_link_id = fac_cp_nbs[0][0] + fac_sp_id = fac_cp_nbs[0][1] + + _, fac_link_props = cbm.get_node_properties(node_id=fac_link_id) + # selectively replicate link properties + new_link_props = {ABCPropertyGraph.PROP_NAME: fac_link_props[ABCPropertyGraph.PROP_NAME], + ABCPropertyGraph.PROP_TYPE: fac_link_props[ABCPropertyGraph.PROP_TYPE], + ABCPropertyGraph.PROP_LAYER: fac_link_props[ABCPropertyGraph.PROP_LAYER] + } + abqm.add_node(node_id=fac_link_id, label=ABCPropertyGraph.CLASS_Link, + props=new_link_props) + try: + new_sp_props = abqm.get_node_properties(node_id=fac_sp_id) + except PropertyGraphQueryException: + # if the node doesn't exist we need to create it (it could have been created in the first pass) + _, fac_sp_props = cbm.get_node_properties(node_id=fac_sp_id) + new_sp_props = {ABCPropertyGraph.PROP_NAME: fac_sp_props[ABCPropertyGraph.PROP_NAME], + ABCPropertyGraph.PROP_TYPE: fac_sp_props[ABCPropertyGraph.PROP_TYPE], + ABCPropertyGraph.PROP_CAPACITIES: fac_sp_props.get( + ABCPropertyGraph.PROP_CAPACITIES), + ABCPropertyGraph.PROP_LABELS: fac_sp_props.get(ABCPropertyGraph.PROP_LABELS) + } + new_sp_props = {k: v for (k, v) in new_sp_props.items() if v} + abqm.add_node(node_id=fac_sp_id, label=ABCPropertyGraph.CLASS_ConnectionPoint, + props=new_sp_props) + + # link these together + abqm.add_link(node_a=fac_cp_node_id, rel=ABCPropertyGraph.REL_CONNECTS, node_b=fac_link_id) + abqm.add_link(node_a=fac_link_id, rel=ABCPropertyGraph.REL_CONNECTS, node_b=fac_sp_id) + abqm.add_link(node_a=fac_sp_id, rel=ABCPropertyGraph.REL_CONNECTS, node_b=site_to_ns_node_id[s]) return abqm From ab156a5a72a65ff41403b5aaecd0ba5340a709dc Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 29 May 2024 10:32:28 -0400 Subject: [PATCH 052/133] pass all CPs for FacilityPorts --- .../actor/fim/plugins/broker/aggregate_bqm_plugin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py index c9c1cdba..f9534a38 100644 --- a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py +++ b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py @@ -25,6 +25,7 @@ # Author: Ilya Baldin (ibaldin@renci.org) from __future__ import annotations +import json from datetime import datetime from typing import Tuple, Dict, TYPE_CHECKING, List from collections import defaultdict @@ -524,8 +525,6 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope allocated_vlans = self.occupied_vlans(db=db, node_id=fac_sliver.resource_name, component_name=fac_cp_node_id, start=start, end=end) - labels = Labels() - labels.vlan = allocated_vlans new_cp_props = {ABCPropertyGraph.PROP_NAME: fac_cp_props[ABCPropertyGraph.PROP_NAME], ABCPropertyGraph.PROP_TYPE: fac_cp_props[ABCPropertyGraph.PROP_TYPE], @@ -533,8 +532,9 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope ABCPropertyGraph.PROP_CAPACITIES: fac_cp_props.get(ABCPropertyGraph.PROP_CAPACITIES) } - if len(labels.vlan): - new_cp_props[ABCPropertyGraph.PROP_LABEL_ALLOCATIONS] = labels.to_dict() + if allocated_vlans and len(allocated_vlans): + labels = Labels(vlan=allocated_vlans) + new_cp_props[ABCPropertyGraph.PROP_LABEL_ALLOCATIONS] = json.dumps(labels.to_json()) new_cp_props = {k: v for (k, v) in new_cp_props.items() if v} From fa03d3746c895dad3e7bd2f2cf81d0ebf01d5788 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 29 May 2024 10:35:08 -0400 Subject: [PATCH 053/133] pass all CPs for FacilityPorts --- fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py index f9534a38..d7868675 100644 --- a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py +++ b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py @@ -533,7 +533,7 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope } if allocated_vlans and len(allocated_vlans): - labels = Labels(vlan=allocated_vlans) + labels = Labels(vlan_range=allocated_vlans) new_cp_props[ABCPropertyGraph.PROP_LABEL_ALLOCATIONS] = json.dumps(labels.to_json()) new_cp_props = {k: v for (k, v) in new_cp_props.items() if v} From 6e37fcb67f18a204dc2b31183ff864ad30a1179b Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 29 May 2024 10:38:13 -0400 Subject: [PATCH 054/133] pass all CPs for FacilityPorts --- fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py index d7868675..f9534a38 100644 --- a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py +++ b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py @@ -533,7 +533,7 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope } if allocated_vlans and len(allocated_vlans): - labels = Labels(vlan_range=allocated_vlans) + labels = Labels(vlan=allocated_vlans) new_cp_props[ABCPropertyGraph.PROP_LABEL_ALLOCATIONS] = json.dumps(labels.to_json()) new_cp_props = {k: v for (k, v) in new_cp_props.items() if v} From 53fae82e1bfdb24aaf248e887204c370b02916cf Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 29 May 2024 12:01:04 -0400 Subject: [PATCH 055/133] pass all CPs for FacilityPorts --- fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py index f9534a38..ae56229e 100644 --- a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py +++ b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py @@ -534,7 +534,7 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope if allocated_vlans and len(allocated_vlans): labels = Labels(vlan=allocated_vlans) - new_cp_props[ABCPropertyGraph.PROP_LABEL_ALLOCATIONS] = json.dumps(labels.to_json()) + new_cp_props[ABCPropertyGraph.PROP_LABEL_ALLOCATIONS] = labels.to_json() new_cp_props = {k: v for (k, v) in new_cp_props.items() if v} From 98ffbbf3265f2c819be90567ecccaa9ad19ba91b Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 31 May 2024 11:32:14 -0400 Subject: [PATCH 056/133] initial changes for ERO --- .../policy/broker_simpler_units_policy.py | 48 +++++++++++++------ .../plugins/broker/aggregate_bqm_plugin.py | 2 +- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 545d03bc..470663a1 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -41,6 +41,7 @@ from fim.slivers.interface_info import InterfaceSliver, InterfaceType from fim.slivers.network_node import NodeSliver, NodeType from fim.slivers.network_service import NetworkServiceSliver, ServiceType, NSLayer +from fim.slivers.path_info import Path from fabric_cf.actor.boot.configuration import ActorConfig from fabric_cf.actor.core.apis.abc_broker_reservation import ABCBrokerReservation @@ -671,6 +672,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: owner_switch = None peered_ns_interfaces = [] + sw_info_per_interface = {} # For each Interface Sliver; for ifs in sliver.interface_info.interfaces.values(): @@ -815,6 +817,9 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: self.logger.info(f"Allocated Interface Sliver: {ifs} delegation: {delegation_id}") + if owner_switch.get_labels() and owner_switch.get_labels().ipv4: + sw_info_per_interface[owner_switch.get_name()] = owner_switch.get_labels().ipv4 + # 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_ns_id)) @@ -837,6 +842,24 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: owner_mpls=owner_mpls_ns, inv=inv, sliver=sliver, owner_ns=owner_ns, node_id_to_reservations=node_id_to_reservations, term=term) + if sliver.ero and len(sliver.ero.get()) and len(sw_info_per_interface) == 2: + ero_hops = {} + type, path = sliver.ero.get() + for hop in path.get()[0]: + # User passes the site names; Broker maps the sites names to the respective switch IP + hop_switch = self.get_switch_sliver(site=hop) + if not hop_switch: + self.logger.error(f"Requested hop: {hop} in the ERO does not exist") + raise BrokerException(error_code=ExceptionErrorCode.INVALID_ARGUMENT, + msg=f"Requested hop: {hop} in the ERO does not exist ") + + if hop_switch.get_labels() and hop_switch.get_labels().ipv4: + ero_hops[hop] = hop_switch.get_labels().ipv4 + hop = hop_switch.get_labels().ipv4 + + # TODO check loops in the path and raise error + self.logger.info(f"Network Service ERO: {sliver.ero}") + return delegation_id, sliver, error_msg def __allocate_peered_interfaces(self, *, peered_interfaces: List[InterfaceSliver], owner_switch: NodeSliver, @@ -1275,20 +1298,17 @@ def get_switch_sliver(self, *, site: str) -> NodeSliver: """ try: self.lock.acquire() - if self.combined_broker_model is None: - return None - node_props = {ABCPropertyGraphConstants.PROP_SITE: site, - ABCPropertyGraphConstants.PROP_TYPE: str(NodeType.Switch)} - candidates = self.combined_broker_model.get_matching_nodes_with_components( - label=ABCPropertyGraphConstants.CLASS_NetworkNode, - props=node_props) - - if candidates is not None: - for c in candidates: - ns_sliver = self.combined_broker_model.build_deep_node_sliver(node_id=c) - return ns_sliver - - return None + if self.combined_broker_model: + node_props = {ABCPropertyGraphConstants.PROP_SITE: site, + ABCPropertyGraphConstants.PROP_TYPE: str(NodeType.Switch)} + candidates = self.combined_broker_model.get_matching_nodes_with_components( + label=ABCPropertyGraphConstants.CLASS_NetworkNode, + props=node_props) + + if candidates is not None: + for c in candidates: + ns_sliver = self.combined_broker_model.build_deep_node_sliver(node_id=c) + return ns_sliver finally: self.lock.release() diff --git a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py index ae56229e..ffd33efb 100644 --- a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py +++ b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py @@ -150,7 +150,7 @@ def occupied_node_capacity(*, db: ABCDatabase, node_id: str, start: datetime, allocated_sliver = reservation.get_resources().get_sliver() if allocated_sliver is not None: - occupied_capacities = occupied_capacities + allocated_sliver.get_capacities() + occupied_capacities = occupied_capacities + allocated_sliver.get_capacity_allocations() if allocated_sliver.attached_components_info is not None: for allocated_component in allocated_sliver.attached_components_info.devices.values(): From 6047d25854ee8d4f5a881241d8e691d9434afb72 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 31 May 2024 11:32:35 -0400 Subject: [PATCH 057/133] up fim version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1d81881b..c412ec24 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "PyYAML", "fabric_fss_utils==1.5.0", "fabric-message-bus==1.7.0b1", - "fabric-fim==1.7.0b3", + "fabric-fim==1.7.0b4", "fabric-credmgr-client==1.6.0", "ansible" ] From c939b97715195e00a8fc37205724fa95c91353f4 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 31 May 2024 12:10:15 -0400 Subject: [PATCH 058/133] pass fabnet loopback address in ero --- .../policy/broker_simpler_units_policy.py | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 470663a1..51fe4634 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -817,8 +817,9 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: self.logger.info(f"Allocated Interface Sliver: {ifs} delegation: {delegation_id}") - if owner_switch.get_labels() and owner_switch.get_labels().ipv4: - sw_info_per_interface[owner_switch.get_name()] = owner_switch.get_labels().ipv4 + owner_v4_service = self.get_ns_from_switch(switch=owner_switch, ns_type=ServiceType.FABNetv4) + if owner_v4_service and owner_v4_service.get_labels(): + sw_info_per_interface[owner_switch.get_name()] = owner_v4_service.get_labels().ipv4 # 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_ns_id)) @@ -844,6 +845,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: if sliver.ero and len(sliver.ero.get()) and len(sw_info_per_interface) == 2: ero_hops = {} + new_path = [] type, path = sliver.ero.get() for hop in path.get()[0]: # User passes the site names; Broker maps the sites names to the respective switch IP @@ -853,12 +855,15 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: raise BrokerException(error_code=ExceptionErrorCode.INVALID_ARGUMENT, msg=f"Requested hop: {hop} in the ERO does not exist ") - if hop_switch.get_labels() and hop_switch.get_labels().ipv4: - ero_hops[hop] = hop_switch.get_labels().ipv4 - hop = hop_switch.get_labels().ipv4 + hop_v4_service = self.get_ns_from_switch(switch=hop_switch, ns_type=ServiceType.FABNetv4) + if hop_v4_service and hop_v4_service.get_labels() and hop_v4_service.get_labels().ipv4: + sw_info_per_interface[hop_switch.get_name()] = hop_v4_service.get_labels().ipv4 + new_path.append(hop_v4_service.get_labels().ipv4) + hop = hop_v4_service.get_labels().ipv4 # TODO check loops in the path and raise error - self.logger.info(f"Network Service ERO: {sliver.ero}") + self.logger.info(f"KOMAL ---- Network Service ERO: {sliver.ero}") + self.logger.info(f"KOMAL ---- Network Service ERO NEW PATH: {new_path}") return delegation_id, sliver, error_msg @@ -1290,6 +1295,19 @@ def get_peer_node(self, *, site: str, node_type: str, node_name: str) -> NodeSli else: return self.get_switch_sliver(site=site) + @staticmethod + def get_ns_from_switch(switch: NodeSliver, ns_type: ServiceType) -> NetworkServiceSliver: + """ + Extract specific type of service from a switch + :param switch: switch + :param ns_type: type of service requested + :return Network Service + """ + if switch and switch.network_service_info: + for service_name, service in switch.network_service_info.network_services.items(): + if service.get_type() == ns_type: + return service + def get_switch_sliver(self, *, site: str) -> NodeSliver: """ Get Component Sliver from BQM From c3bd2e505b340ac31ad6decfeeb659fd60afcea4 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 3 Jun 2024 11:37:46 -0400 Subject: [PATCH 059/133] changes for adding ERO details --- .../core/policy/broker_simpler_units_policy.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 51fe4634..b3c59ed0 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -844,8 +844,9 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: node_id_to_reservations=node_id_to_reservations, term=term) if sliver.ero and len(sliver.ero.get()) and len(sw_info_per_interface) == 2: + source_end = list(sw_info_per_interface.values()) ero_hops = {} - new_path = [] + new_path = [source_end[0]] type, path = sliver.ero.get() for hop in path.get()[0]: # User passes the site names; Broker maps the sites names to the respective switch IP @@ -857,13 +858,15 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: hop_v4_service = self.get_ns_from_switch(switch=hop_switch, ns_type=ServiceType.FABNetv4) if hop_v4_service and hop_v4_service.get_labels() and hop_v4_service.get_labels().ipv4: - sw_info_per_interface[hop_switch.get_name()] = hop_v4_service.get_labels().ipv4 + ero_hops[hop_switch.get_name()] = hop_v4_service.get_labels().ipv4 new_path.append(hop_v4_service.get_labels().ipv4) - hop = hop_v4_service.get_labels().ipv4 - # TODO check loops in the path and raise error - self.logger.info(f"KOMAL ---- Network Service ERO: {sliver.ero}") - self.logger.info(f"KOMAL ---- Network Service ERO NEW PATH: {new_path}") + new_path.append(source_end[1]) + + if len(new_path): + ero_path = Path() + ero_path.set_symmetric(new_path) + sliver.ero.set(ero_path) return delegation_id, sliver, error_msg From dd62c31cb645efb592dfb38df4e65ef0f80a9b80 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 3 Jun 2024 11:45:10 -0400 Subject: [PATCH 060/133] added logs --- fabric_cf/actor/core/policy/broker_simpler_units_policy.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index b3c59ed0..801b6700 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -844,6 +844,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: node_id_to_reservations=node_id_to_reservations, term=term) if sliver.ero and len(sliver.ero.get()) and len(sw_info_per_interface) == 2: + self.logger.info(f"Requested ERO: {sliver.ero}") source_end = list(sw_info_per_interface.values()) ero_hops = {} new_path = [source_end[0]] @@ -851,6 +852,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: for hop in path.get()[0]: # User passes the site names; Broker maps the sites names to the respective switch IP hop_switch = self.get_switch_sliver(site=hop) + self.logger.debug(f"Switch information for {hop}: {hop_switch}") if not hop_switch: self.logger.error(f"Requested hop: {hop} in the ERO does not exist") raise BrokerException(error_code=ExceptionErrorCode.INVALID_ARGUMENT, @@ -858,6 +860,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: hop_v4_service = self.get_ns_from_switch(switch=hop_switch, ns_type=ServiceType.FABNetv4) if hop_v4_service and hop_v4_service.get_labels() and hop_v4_service.get_labels().ipv4: + self.logger.debug(f"Fabnetv4 information for {hop}: {hop_v4_service}") ero_hops[hop_switch.get_name()] = hop_v4_service.get_labels().ipv4 new_path.append(hop_v4_service.get_labels().ipv4) @@ -867,6 +870,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: ero_path = Path() ero_path.set_symmetric(new_path) sliver.ero.set(ero_path) + self.logger.info(f"Allocated ERO: {sliver.ero}") return delegation_id, sliver, error_msg From 8c6c0a6276300f27e6bb7a840cee8e57cc55c3d2 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 3 Jun 2024 12:04:52 -0400 Subject: [PATCH 061/133] lookup StitchNode property --- .../actor/core/policy/broker_simpler_units_policy.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 801b6700..896426f3 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -1311,21 +1311,23 @@ def get_ns_from_switch(switch: NodeSliver, ns_type: ServiceType) -> NetworkServi :return Network Service """ if switch and switch.network_service_info: - for service_name, service in switch.network_service_info.network_services.items(): + for service in switch.network_service_info.network_services.values(): if service.get_type() == ns_type: return service - def get_switch_sliver(self, *, site: str) -> NodeSliver: + def get_switch_sliver(self, *, site: str, stitch: bool = True) -> NodeSliver: """ Get Component Sliver from BQM @param site: Node Site Name + @param stitch: Flag indicating if the StitchNode is being looked up @return Facility Sliver """ try: self.lock.acquire() if self.combined_broker_model: node_props = {ABCPropertyGraphConstants.PROP_SITE: site, - ABCPropertyGraphConstants.PROP_TYPE: str(NodeType.Switch)} + ABCPropertyGraphConstants.PROP_TYPE: str(NodeType.Switch), + ABCPropertyGraphConstants.PROP_STITCH_NODE: stitch} candidates = self.combined_broker_model.get_matching_nodes_with_components( label=ABCPropertyGraphConstants.CLASS_NetworkNode, props=node_props) From ccb09eb44a3547f0b3cb24352a9f0d9d17846d68 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 3 Jun 2024 12:15:39 -0400 Subject: [PATCH 062/133] change bool to str before passing to query --- fabric_cf/actor/core/policy/broker_simpler_units_policy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 896426f3..9d05cac4 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -1327,7 +1327,7 @@ def get_switch_sliver(self, *, site: str, stitch: bool = True) -> NodeSliver: if self.combined_broker_model: node_props = {ABCPropertyGraphConstants.PROP_SITE: site, ABCPropertyGraphConstants.PROP_TYPE: str(NodeType.Switch), - ABCPropertyGraphConstants.PROP_STITCH_NODE: stitch} + ABCPropertyGraphConstants.PROP_STITCH_NODE: str(stitch)} candidates = self.combined_broker_model.get_matching_nodes_with_components( label=ABCPropertyGraphConstants.CLASS_NetworkNode, props=node_props) From 2a22cd203ed968c931395ad5793e74b44188392f Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 3 Jun 2024 14:25:36 -0400 Subject: [PATCH 063/133] add validation to check ERO hops --- .../policy/broker_simpler_units_policy.py | 26 ++++++++++++++++--- pyproject.toml | 2 +- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 9d05cac4..ad913d9b 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -846,7 +846,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: if sliver.ero and len(sliver.ero.get()) and len(sw_info_per_interface) == 2: self.logger.info(f"Requested ERO: {sliver.ero}") source_end = list(sw_info_per_interface.values()) - ero_hops = {} + ero_hops = [] new_path = [source_end[0]] type, path = sliver.ero.get() for hop in path.get()[0]: @@ -861,12 +861,16 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: hop_v4_service = self.get_ns_from_switch(switch=hop_switch, ns_type=ServiceType.FABNetv4) if hop_v4_service and hop_v4_service.get_labels() and hop_v4_service.get_labels().ipv4: self.logger.debug(f"Fabnetv4 information for {hop}: {hop_v4_service}") - ero_hops[hop_switch.get_name()] = hop_v4_service.get_labels().ipv4 + ero_hops = f"{hop_switch.node_id}-ns" new_path.append(hop_v4_service.get_labels().ipv4) new_path.append(source_end[1]) if len(new_path): + if not self.validate_requested_ero_path(source_node=source_end[0], end_node=source_end[1], + hops=ero_hops): + raise BrokerException(error_code=ExceptionErrorCode.INVALID_ARGUMENT, + msg=f"Requested ERO path: {sliver.ero} is invalid!") ero_path = Path() ero_path.set_symmetric(new_path) sliver.ero.set(ero_path) @@ -1315,6 +1319,18 @@ def get_ns_from_switch(switch: NodeSliver, ns_type: ServiceType) -> NetworkServi if service.get_type() == ns_type: return service + def validate_requested_ero_path(self, source_node: str, end_node: str, hops: List[str]) -> bool: + try: + self.lock.acquire() + if self.combined_broker_model: + path = self.combined_broker_model.get_nodes_on_path_with_hops(node_a=source_node, + node_z=end_node, hops=hops) + if len(path) and path[0] == source_node and path[-1] == end_node: + return True + finally: + self.lock.release() + return False + def get_switch_sliver(self, *, site: str, stitch: bool = True) -> NodeSliver: """ Get Component Sliver from BQM @@ -1326,14 +1342,16 @@ def get_switch_sliver(self, *, site: str, stitch: bool = True) -> NodeSliver: self.lock.acquire() if self.combined_broker_model: node_props = {ABCPropertyGraphConstants.PROP_SITE: site, - ABCPropertyGraphConstants.PROP_TYPE: str(NodeType.Switch), - ABCPropertyGraphConstants.PROP_STITCH_NODE: str(stitch)} + ABCPropertyGraphConstants.PROP_TYPE: str(NodeType.Switch)} + #ABCPropertyGraphConstants.PROP_STITCH_NODE: str(stitch).lower()} candidates = self.combined_broker_model.get_matching_nodes_with_components( label=ABCPropertyGraphConstants.CLASS_NetworkNode, props=node_props) if candidates is not None: for c in candidates: + if stitch and "p4" in c: + continue ns_sliver = self.combined_broker_model.build_deep_node_sliver(node_id=c) return ns_sliver finally: diff --git a/pyproject.toml b/pyproject.toml index c412ec24..f17ceb11 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "PyYAML", "fabric_fss_utils==1.5.0", "fabric-message-bus==1.7.0b1", - "fabric-fim==1.7.0b4", + "fabric-fim==1.7.0b5", "fabric-credmgr-client==1.6.0", "ansible" ] From d0e3d7bb4f3d556c616b66de738e8666c72a13b1 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 3 Jun 2024 14:41:16 -0400 Subject: [PATCH 064/133] pass switch node id for path validation --- .../core/policy/broker_simpler_units_policy.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index ad913d9b..6818d4f1 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -672,7 +672,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: owner_switch = None peered_ns_interfaces = [] - sw_info_per_interface = {} + ero_source_end_info = [] # For each Interface Sliver; for ifs in sliver.interface_info.interfaces.values(): @@ -819,7 +819,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: owner_v4_service = self.get_ns_from_switch(switch=owner_switch, ns_type=ServiceType.FABNetv4) if owner_v4_service and owner_v4_service.get_labels(): - sw_info_per_interface[owner_switch.get_name()] = owner_v4_service.get_labels().ipv4 + ero_source_end_info.append((owner_switch.get_name(),owner_v4_service.get_labels().ipv4)) # 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_ns_id)) @@ -843,11 +843,10 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: owner_mpls=owner_mpls_ns, inv=inv, sliver=sliver, owner_ns=owner_ns, node_id_to_reservations=node_id_to_reservations, term=term) - if sliver.ero and len(sliver.ero.get()) and len(sw_info_per_interface) == 2: + if sliver.ero and len(sliver.ero.get()) and len(ero_source_end_info) == 2: self.logger.info(f"Requested ERO: {sliver.ero}") - source_end = list(sw_info_per_interface.values()) ero_hops = [] - new_path = [source_end[0]] + new_path = [ero_source_end_info[0][1]] type, path = sliver.ero.get() for hop in path.get()[0]: # User passes the site names; Broker maps the sites names to the respective switch IP @@ -864,10 +863,11 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: ero_hops = f"{hop_switch.node_id}-ns" new_path.append(hop_v4_service.get_labels().ipv4) - new_path.append(source_end[1]) + new_path.append(ero_source_end_info[1][1]) if len(new_path): - if not self.validate_requested_ero_path(source_node=source_end[0], end_node=source_end[1], + if not self.validate_requested_ero_path(source_node=ero_source_end_info[0][0], + end_node=ero_source_end_info[1][0], hops=ero_hops): raise BrokerException(error_code=ExceptionErrorCode.INVALID_ARGUMENT, msg=f"Requested ERO path: {sliver.ero} is invalid!") @@ -1324,7 +1324,7 @@ def validate_requested_ero_path(self, source_node: str, end_node: str, hops: Lis self.lock.acquire() if self.combined_broker_model: path = self.combined_broker_model.get_nodes_on_path_with_hops(node_a=source_node, - node_z=end_node, hops=hops) + node_z=end_node, hops=hops, cut_off=200) if len(path) and path[0] == source_node and path[-1] == end_node: return True finally: From 69c847def427ec738582451bc4f48fe09cd2f7f5 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 3 Jun 2024 16:58:12 -0400 Subject: [PATCH 065/133] ero hops was overwriting the list --- fabric_cf/actor/core/policy/broker_simpler_units_policy.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 6818d4f1..f98c5c9c 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -819,7 +819,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: owner_v4_service = self.get_ns_from_switch(switch=owner_switch, ns_type=ServiceType.FABNetv4) if owner_v4_service and owner_v4_service.get_labels(): - ero_source_end_info.append((owner_switch.get_name(),owner_v4_service.get_labels().ipv4)) + ero_source_end_info.append((owner_switch.node_id, owner_v4_service.get_labels().ipv4)) # 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_ns_id)) @@ -860,7 +860,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: hop_v4_service = self.get_ns_from_switch(switch=hop_switch, ns_type=ServiceType.FABNetv4) if hop_v4_service and hop_v4_service.get_labels() and hop_v4_service.get_labels().ipv4: self.logger.debug(f"Fabnetv4 information for {hop}: {hop_v4_service}") - ero_hops = f"{hop_switch.node_id}-ns" + ero_hops.append(f"{hop_switch.node_id}-ns") new_path.append(hop_v4_service.get_labels().ipv4) new_path.append(ero_source_end_info[1][1]) @@ -1325,6 +1325,8 @@ def validate_requested_ero_path(self, source_node: str, end_node: str, hops: Lis if self.combined_broker_model: path = self.combined_broker_model.get_nodes_on_path_with_hops(node_a=source_node, node_z=end_node, hops=hops, cut_off=200) + self.logger.debug(f"Network path from source:{source_node} to end: {end_node} " + f"with hops: {hops} is path: {path}") if len(path) and path[0] == source_node and path[-1] == end_node: return True finally: From c79e344380755231e1b0b0d54e7ea0f4415fea16 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 3 Jun 2024 17:33:43 -0400 Subject: [PATCH 066/133] up fim version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f17ceb11..510b9827 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "PyYAML", "fabric_fss_utils==1.5.0", "fabric-message-bus==1.7.0b1", - "fabric-fim==1.7.0b5", + "fabric-fim==1.7.0b6", "fabric-credmgr-client==1.6.0", "ansible" ] From 82a15c7ac9f651ced6f5dacee9eb26d95cf9049b Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 4 Jun 2024 14:00:21 -0400 Subject: [PATCH 067/133] add tests for abqm --- .../plugins/broker/aggregate_bqm_plugin.py | 29 +++++++------ fabric_cf/actor/test/test_abqm.py | 41 +++++++++++++++++-- .../orchestrator/core/orchestrator_handler.py | 4 +- 3 files changed, 57 insertions(+), 17 deletions(-) diff --git a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py index ffd33efb..ae86e910 100644 --- a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py +++ b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py @@ -186,7 +186,10 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope start = kwargs.get('start', None) end = kwargs.get('end', None) - db = self.actor.get_plugin().get_database() + if not self.DEBUG_FLAG: + db = self.actor.get_plugin().get_database() + else: + db = None # do a one-pass aggregation of servers, their components and interfaces # and some flags (e.g. PTP availability) @@ -351,11 +354,12 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope p4_sliver.capacities = Capacities() p4_sliver.capacity_allocations = Capacities() p4_sliver.capacities += p4.get_capacities() - # query database for everything taken on this node - allocated_caps, allocated_comp_caps = self.occupied_node_capacity(db=db, node_id=p4.node_id, - start=start, end=end) - if allocated_caps: - p4_sliver.capacity_allocations = p4_sliver.capacity_allocations + allocated_caps + if not self.DEBUG_FLAG: + # query database for everything taken on this node + allocated_caps, allocated_comp_caps = self.occupied_node_capacity(db=db, node_id=p4.node_id, + start=start, end=end) + if allocated_caps: + p4_sliver.capacity_allocations = p4_sliver.capacity_allocations + allocated_caps p4_props = abqm.node_sliver_to_graph_properties_dict(p4_sliver) node_id = p4.node_id if kwargs['query_level'] != 0: @@ -523,18 +527,19 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope for fac_cp_node_id in fac_ns_cp_list: _, fac_cp_props = cbm.get_node_properties(node_id=fac_cp_node_id) - allocated_vlans = self.occupied_vlans(db=db, node_id=fac_sliver.resource_name, - component_name=fac_cp_node_id, start=start, end=end) - new_cp_props = {ABCPropertyGraph.PROP_NAME: fac_cp_props[ABCPropertyGraph.PROP_NAME], ABCPropertyGraph.PROP_TYPE: fac_cp_props[ABCPropertyGraph.PROP_TYPE], ABCPropertyGraph.PROP_LABELS: fac_cp_props.get(ABCPropertyGraph.PROP_LABELS), ABCPropertyGraph.PROP_CAPACITIES: fac_cp_props.get(ABCPropertyGraph.PROP_CAPACITIES) } - if allocated_vlans and len(allocated_vlans): - labels = Labels(vlan=allocated_vlans) - new_cp_props[ABCPropertyGraph.PROP_LABEL_ALLOCATIONS] = labels.to_json() + if not self.DEBUG_FLAG: + allocated_vlans = self.occupied_vlans(db=db, node_id=fac_sliver.resource_name, + component_name=fac_cp_node_id, start=start, end=end) + + if allocated_vlans and len(allocated_vlans): + labels = Labels(vlan=allocated_vlans) + new_cp_props[ABCPropertyGraph.PROP_LABEL_ALLOCATIONS] = labels.to_json() new_cp_props = {k: v for (k, v) in new_cp_props.items() if v} diff --git a/fabric_cf/actor/test/test_abqm.py b/fabric_cf/actor/test/test_abqm.py index 56a65aeb..bc9f7e4e 100644 --- a/fabric_cf/actor/test/test_abqm.py +++ b/fabric_cf/actor/test/test_abqm.py @@ -4,6 +4,8 @@ from fim.graph.neo4j_property_graph import Neo4jGraphImporter, Neo4jPropertyGraph from fim.graph.resources.neo4j_arm import Neo4jARMGraph from fim.graph.resources.neo4j_cbm import Neo4jCBMFactory, Neo4jCBMGraph +from fim.user import NodeType + from fabric_cf.actor.fim.plugins.broker.aggregate_bqm_plugin import AggregatedBQMPlugin """ @@ -28,9 +30,10 @@ class ABQM_Test(unittest.TestCase): def test_abqm(self): self.n4j_imp.delete_all_graphs() # these are produced by substrate tests - site_ads = ['../../../neo4j/Network-ad.graphml', '../../../neo4j/LBNL-ad.graphml', - '../../../neo4j/RENCI-ad.graphml', - '../../../neo4j/UKY-ad.graphml'] + site_ads = ['../../../neo4j/Network-dev.graphml', + '../../../neo4j/LBNL.graphml', + '../../../neo4j/RENC.graphml', + '../../../neo4j/UKY.graphml'] cbm = Neo4jCBMGraph(importer=self.n4j_imp) @@ -106,6 +109,38 @@ def test_abqm(self): with open('cbm.graphml', 'w') as f: f.write(cbm_string) + plain_cbm = self.n4j_imp.import_graph_from_string_direct(graph_string=abqm_level2_string) + temp = Neo4jCBMFactory.create(Neo4jPropertyGraph(graph_id=plain_cbm.graph_id, + importer=self.n4j_imp)) + + site_node_ids = {} + for s in temp.get_all_nodes_by_class_and_type(label=ABCPropertyGraph.CLASS_CompositeNode, + ntype=str(NodeType.Server)): + labels, props = temp.get_node_properties(node_id=s) + site_node_ids[props.get('Site')] = s + + ns_node_ids = {} + for s in temp.get_all_network_service_nodes(): + labels, props = temp.get_node_properties(node_id=s) + ns_node_ids[props.get('Name')] = s + + path = temp.get_nodes_on_path_with_hops(node_a=site_node_ids['UKY'], node_z=site_node_ids['LBNL'], + hops=[ns_node_ids['RENC_ns']]) + + assert(len(path) != 0) + from fim.user.topology import AdvertizedTopology + substrate = AdvertizedTopology() + substrate.load(graph_string=abqm_level2_string) + + uky_node_id = substrate.sites.get("UKY").node_id + lbnl_node_id = substrate.sites.get("LBNL").node_id + renc_ns_node_id = substrate.network_services.get("RENC_ns").node_id + + path = substrate.graph_model.get_nodes_on_path_with_hops(node_a=uky_node_id, node_z=lbnl_node_id, + hops=[renc_ns_node_id]) + + assert(len(path) != 0) + self.n4j_imp.delete_all_graphs() def test_cbm(self): diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index 0063cb6e..fcef28bf 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -135,7 +135,7 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok """ broker_query_model = None # Always get Fresh copy for advanced resource requests - if not start and not end and not includes and not excludes: + if not start and not end and not includes and not excludes and level == 0: saved_bqm = self.controller_state.get_saved_bqm(graph_format=graph_format, level=level) if saved_bqm is not None: if not force_refresh and not saved_bqm.can_refresh() and not saved_bqm.refresh_in_progress: @@ -168,7 +168,7 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok broker_query_model = model.get_model() # Do not update cache for advance requests - if not start and not end and not includes and not excludes: + if not start and not end and not includes and not excludes and level == 0: self.controller_state.save_bqm(bqm=broker_query_model, graph_format=graph_format, level=level) return broker_query_model From 5a6318a1a3e5986565a5be39d58259f25589f949 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 5 Jun 2024 10:33:47 -0400 Subject: [PATCH 068/133] changes for doing local computation for level=2 bqm --- .../actor/core/apis/abc_actor_container.py | 9 ++ .../core/apis/abc_actor_management_object.py | 1 - .../core/manage/actor_management_object.py | 2 +- fabric_cf/actor/fim/fim_helper.py | 111 +++++++++++++----- .../plugins/broker/aggregate_bqm_plugin.py | 2 +- fabric_cf/broker/test/broker.py | 39 ++++++ .../orchestrator/core/orchestrator_handler.py | 6 +- 7 files changed, 135 insertions(+), 35 deletions(-) diff --git a/fabric_cf/actor/core/apis/abc_actor_container.py b/fabric_cf/actor/core/apis/abc_actor_container.py index 9c41c7a9..dbd997b7 100644 --- a/fabric_cf/actor/core/apis/abc_actor_container.py +++ b/fabric_cf/actor/core/apis/abc_actor_container.py @@ -152,3 +152,12 @@ def remove_actor_database(self, *, actor_name: str): Remove Actor Database @params actor_name: actor name """ + + @abstractmethod + def get_actor(self) -> ABCActorMixin: + """ + Return Actor + + @return Actor + """ + pass diff --git a/fabric_cf/actor/core/apis/abc_actor_management_object.py b/fabric_cf/actor/core/apis/abc_actor_management_object.py index 2c267445..80798567 100644 --- a/fabric_cf/actor/core/apis/abc_actor_management_object.py +++ b/fabric_cf/actor/core/apis/abc_actor_management_object.py @@ -29,7 +29,6 @@ from datetime import datetime from typing import TYPE_CHECKING, Tuple, Dict, List -from fabric_mb.message_bus.messages.poa_avro import PoaAvro from fabric_mb.message_bus.messages.result_avro import ResultAvro from fabric_mb.message_bus.messages.result_delegation_avro import ResultDelegationAvro from fabric_mb.message_bus.messages.result_poa_avro import ResultPoaAvro diff --git a/fabric_cf/actor/core/manage/actor_management_object.py b/fabric_cf/actor/core/manage/actor_management_object.py index c8fb8dc0..f6e208c4 100644 --- a/fabric_cf/actor/core/manage/actor_management_object.py +++ b/fabric_cf/actor/core/manage/actor_management_object.py @@ -886,7 +886,7 @@ def build_broker_query_model(self, level_0_broker_query_model: str, level: int, start: datetime = None, end: datetime = None, includes: str = None, excludes: str = None) -> str: try: - return FimHelper.build_broker_query_model(level_0_broker_query_model=level_0_broker_query_model, + return FimHelper.build_broker_query_model(db=self.db, level_0_broker_query_model=level_0_broker_query_model, level=level, graph_format=graph_format, start=start, end=end, includes=includes, excludes=excludes) except Exception as e: diff --git a/fabric_cf/actor/fim/fim_helper.py b/fabric_cf/actor/fim/fim_helper.py index a8b5d1ca..72080e04 100644 --- a/fabric_cf/actor/fim/fim_helper.py +++ b/fabric_cf/actor/fim/fim_helper.py @@ -23,14 +23,21 @@ # # # Author: Komal Thareja (kthare10@renci.org) +from __future__ import annotations + +from collections import defaultdict +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from fabric_cf.actor.core.apis.abc_database import ABCDatabase + import logging import random -import traceback -import uuid -from collections import defaultdict from datetime import datetime from typing import Tuple, List, Union + +from fabric_cf.actor.fim.plugins.broker.aggregate_bqm_plugin import AggregatedBQMPlugin from fim.graph.abc_property_graph import ABCPropertyGraph, ABCGraphImporter from fim.graph.neo4j_property_graph import Neo4jGraphImporter, Neo4jPropertyGraph from fim.graph.networkx_property_graph import NetworkXGraphImporter @@ -38,19 +45,18 @@ from fim.graph.resources.abc_cbm import ABCCBMPropertyGraph from fim.graph.resources.neo4j_arm import Neo4jARMGraph from fim.graph.resources.neo4j_cbm import Neo4jCBMGraph, Neo4jCBMFactory -from fim.graph.resources.networkx_abqm import NetworkXAggregateBQM +from fim.graph.resources.networkx_abqm import NetworkXABQMFactory from fim.graph.slices.abc_asm import ABCASMPropertyGraph from fim.graph.slices.neo4j_asm import Neo4jASMFactory from fim.graph.slices.networkx_asm import NetworkxASM, NetworkXASMFactory -from fim.pluggable import PluggableRegistry, PluggableType from fim.slivers.attached_components import ComponentSliver from fim.slivers.base_sliver import BaseSliver from fim.slivers.capacities_labels import Capacities from fim.slivers.delegations import Delegations from fim.slivers.interface_info import InterfaceSliver, InterfaceType -from fim.slivers.network_node import NodeSliver, CompositeNodeSliver +from fim.slivers.network_node import NodeSliver from fim.slivers.network_service import NetworkServiceSliver, ServiceType -from fim.user import ExperimentTopology, NodeType, Component, ReservationInfo, Node, GraphFormat +from fim.user import ExperimentTopology, NodeType, Component, ReservationInfo, Node, GraphFormat, Labels from fim.user.composite_node import CompositeNode from fim.user.interface import Interface from fim.user.topology import AdvertizedTopology @@ -130,7 +136,7 @@ class FimHelper: """ Provides methods to load Graph Models and perform various operations on them """ - __neo4j_graph_importer = None + _neo4j_graph_importer = None @staticmethod def get_neo4j_importer(neo4j_config: dict = None) -> ABCGraphImporter: @@ -139,17 +145,17 @@ def get_neo4j_importer(neo4j_config: dict = None) -> ABCGraphImporter: :return: Neo4jGraphImporter """ logger = None - if neo4j_config is None: - from fabric_cf.actor.core.container.globals import GlobalsSingleton - neo4j_config = GlobalsSingleton.get().get_config().get_global_config().get_neo4j_config() - logger = GlobalsSingleton.get().get_logger() - - if FimHelper.__neo4j_graph_importer is None: - FimHelper.__neo4j_graph_importer = Neo4jGraphImporter(url=neo4j_config["url"], user=neo4j_config["user"], - pswd=neo4j_config["pass"], - import_host_dir=neo4j_config["import_host_dir"], - import_dir=neo4j_config["import_dir"], logger=logger) - return FimHelper.__neo4j_graph_importer + if FimHelper._neo4j_graph_importer is None: + if neo4j_config is None: + from fabric_cf.actor.core.container.globals import GlobalsSingleton + neo4j_config = GlobalsSingleton.get().get_config().get_global_config().get_neo4j_config() + logger = GlobalsSingleton.get().get_logger() + + FimHelper._neo4j_graph_importer = Neo4jGraphImporter(url=neo4j_config["url"], user=neo4j_config["user"], + pswd=neo4j_config["pass"], + import_host_dir=neo4j_config["import_host_dir"], + import_dir=neo4j_config["import_dir"], logger=logger) + return FimHelper._neo4j_graph_importer @staticmethod def get_networkx_importer(logger: logging.Logger = None) -> ABCGraphImporter: @@ -646,16 +652,63 @@ def get_workers(site: CompositeNode) -> dict: return workers @staticmethod - def build_broker_query_model(level_0_broker_query_model: str, level: int, + def build_broker_query_model(db: ABCDatabase, level_0_broker_query_model: str, level: int, graph_format: GraphFormat = GraphFormat.GRAPHML, start: datetime = None, end: datetime = None, includes: str = None, excludes: str = None) -> str: - if level_0_broker_query_model and len(level_0_broker_query_model) > 0: - cbm = FimHelper.get_neo4j_cbm_graph_from_string_direct(graph_str=level_0_broker_query_model) - substrate = cbm.get_bqm(query_level=level, start=start, end=end, includes=includes, - excludes=excludes) - - result = substrate.serialize_graph(format=graph_format) - substrate.delete_graph() - cbm.delete_graph() - return result + if level == 2: + sites_to_include = [s.strip().upper() for s in includes.split(",")] if includes else [] + sites_to_exclude = [s.strip().upper() for s in excludes.split(",")] if excludes else [] + + if level_0_broker_query_model and len(level_0_broker_query_model) > 0: + topology = AdvertizedTopology() + + nx_pgraph = topology.graph_model.importer.import_graph_from_string(graph_string=level_0_broker_query_model) + topology.graph_model = NetworkXABQMFactory.create(nx_pgraph) + + sites_to_remove = [] + + for site_name, site in topology.sites.items(): + if len(sites_to_include) and site_name not in sites_to_include: + sites_to_remove.append(site_name) + continue + + if len(sites_to_exclude) and site_name in sites_to_exclude: + sites_to_remove.append(site_name) + continue + + site_cap_alloc = Capacities() + + for child_name, child in site.children.items(): + allocated_caps, allocated_comp_caps = AggregatedBQMPlugin.occupied_node_capacity(db=db, + node_id=child.node_id, + start=start, + end=end) + site_cap_alloc += allocated_caps + child.set_property(pname="capacity_allocations", pval=allocated_caps) + + # merge allocated component capacities + for kt, v in allocated_comp_caps.items(): + for km, vcap in v.items(): + name = f"{kt}-{km}" + if child.components.get(name) is not None: + capacity_allocations = Capacities() + if child.components[name].capacity_allocations: + capacity_allocations = child.components[name].capacity_allocations + capacity_allocations += vcap + child.components[name].set_property(pname="capacity_allocations", + pval=capacity_allocations) + + for s in sites_to_remove: + topology.remove_node(s) + + for f_name, facility in topology.facilities.items(): + for if_name, interface in facility.interfaces.items(): + allocated_vlans = AggregatedBQMPlugin.occupied_vlans(db=db, node_id=f_name, + component_name=interface.node_id, + start=start, end=end) + if allocated_vlans and len(allocated_vlans): + label_allocations = Labels(vlan=allocated_vlans) + interface.set_property(pname="label_allocations", pval=label_allocations) + + return topology.serialize(fmt=graph_format) diff --git a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py index ae86e910..c265336c 100644 --- a/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py +++ b/fabric_cf/actor/fim/plugins/broker/aggregate_bqm_plugin.py @@ -533,7 +533,7 @@ def plug_produce_bqm(self, *, cbm: ABCCBMPropertyGraph, **kwargs) -> ABCBQMPrope ABCPropertyGraph.PROP_CAPACITIES: fac_cp_props.get(ABCPropertyGraph.PROP_CAPACITIES) } - if not self.DEBUG_FLAG: + if not self.DEBUG_FLAG and kwargs['query_level'] != 0: allocated_vlans = self.occupied_vlans(db=db, node_id=fac_sliver.resource_name, component_name=fac_cp_node_id, start=start, end=end) diff --git a/fabric_cf/broker/test/broker.py b/fabric_cf/broker/test/broker.py index 46e4b5fc..bf28498d 100644 --- a/fabric_cf/broker/test/broker.py +++ b/fabric_cf/broker/test/broker.py @@ -23,6 +23,7 @@ # # # Author: Komal Thareja (kthare10@renci.org) +import os import time import traceback @@ -33,6 +34,8 @@ from fabric_cf.actor.core.container.globals import Globals, GlobalsSingleton from fabric_cf.broker.core.broker_kernel import BrokerKernelSingleton +DEBUG_MODE = False + def main(): """ @@ -51,6 +54,42 @@ def main(): prometheus_port = int(runtime_config.get(Constants.PROPERTY_CONF_PROMETHEUS_REST_PORT, None)) prometheus_client.start_http_server(prometheus_port) + actor = GlobalsSingleton.get().get_container().get_actor() + policy = actor.get_policy() + + if DEBUG_MODE: + site_ads = ['../../../neo4j/Network-dev.graphml', + '../../../neo4j/LBNL.graphml', + '../../../neo4j/RENC.graphml', + '../../../neo4j/UKY.graphml', + '../../../neo4j/AL2S.graphml'] + + adm_ids = dict() + for ad in site_ads: + from fabric_cf.actor.fim.fim_helper import FimHelper + n4j_imp = FimHelper.get_neo4j_importer() + plain_neo4j = n4j_imp.import_graph_from_file_direct(graph_file=ad) + print(f"Validating ARM graph {ad}") + plain_neo4j.validate_graph() + + from fim.graph.resources.neo4j_arm import Neo4jARMGraph + from fim.graph.neo4j_property_graph import Neo4jPropertyGraph + site_arm = Neo4jARMGraph(graph=Neo4jPropertyGraph(graph_id=plain_neo4j.graph_id, + importer=n4j_imp)) + # generate a dict of ADMs from site graph ARM + site_adms = site_arm.generate_adms() + print('ADMS' + str(site_adms.keys())) + + # desired ADM is under 'primary' + site_adm = site_adms['primary'] + policy.combined_broker_model.merge_adm(adm=site_adm) + + print('Deleting ADM and ARM graphs') + for adm in site_adms.values(): + adm_ids[ad] = adm.graph_id + adm.delete_graph() + site_arm.delete_graph() + while True: time.sleep(0.0001) BrokerKernelSingleton.get().do_periodic() diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index fcef28bf..b869944c 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -135,7 +135,7 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok """ broker_query_model = None # Always get Fresh copy for advanced resource requests - if not start and not end and not includes and not excludes and level == 0: + if not start and not end and not includes and not excludes and level <= 1: saved_bqm = self.controller_state.get_saved_bqm(graph_format=graph_format, level=level) if saved_bqm is not None: if not force_refresh and not saved_bqm.can_refresh() and not saved_bqm.refresh_in_progress: @@ -144,7 +144,7 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok saved_bqm.start_refresh() if broker_query_model is None: - if self.local_bqm and level != 0 and not force_refresh: + if self.local_bqm and level == 2 and not force_refresh: saved_bqm = self.controller_state.get_saved_bqm(graph_format=GraphFormat.GRAPHML, level=0) if saved_bqm and saved_bqm.get_bqm() and len(saved_bqm.get_bqm()): broker_query_model = controller.build_broker_query_model(level_0_broker_query_model=saved_bqm.get_bqm(), @@ -168,7 +168,7 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok broker_query_model = model.get_model() # Do not update cache for advance requests - if not start and not end and not includes and not excludes and level == 0: + if not start and not end and not includes and not excludes and level <= 1: self.controller_state.save_bqm(bqm=broker_query_model, graph_format=graph_format, level=level) return broker_query_model From 50c30a8c99da65067f8c5f9f970bd778ac8168d7 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 5 Jun 2024 15:06:01 -0400 Subject: [PATCH 069/133] use caching for portal resource display --- fabric_cf/orchestrator/core/orchestrator_handler.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index b869944c..5997803c 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -135,7 +135,8 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok """ broker_query_model = None # Always get Fresh copy for advanced resource requests - if not start and not end and not includes and not excludes and level <= 1: + if not start and not end and not includes and not excludes and \ + (level <= 1 or graph_format == GraphFormat.JSON_NODELINK): saved_bqm = self.controller_state.get_saved_bqm(graph_format=graph_format, level=level) if saved_bqm is not None: if not force_refresh and not saved_bqm.can_refresh() and not saved_bqm.refresh_in_progress: @@ -168,7 +169,8 @@ def discover_broker_query_model(self, *, controller: ABCMgmtControllerMixin, tok broker_query_model = model.get_model() # Do not update cache for advance requests - if not start and not end and not includes and not excludes and level <= 1: + if not start and not end and not includes and not excludes and \ + (level <= 1 or graph_format == GraphFormat.JSON_NODELINK): self.controller_state.save_bqm(bqm=broker_query_model, graph_format=graph_format, level=level) return broker_query_model From 5170ca459bb7bb9e7599ece5430a3226b5ba2b68 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 5 Jun 2024 18:24:08 -0400 Subject: [PATCH 070/133] exclude the last ipv6 subnet for fabnet during allocation --- fabric_cf/actor/core/policy/network_service_inventory.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fabric_cf/actor/core/policy/network_service_inventory.py b/fabric_cf/actor/core/policy/network_service_inventory.py index e01a9e60..d071dc12 100644 --- a/fabric_cf/actor/core/policy/network_service_inventory.py +++ b/fabric_cf/actor/core/policy/network_service_inventory.py @@ -333,6 +333,9 @@ def allocate(self, *, rid: ID, requested_ns: NetworkServiceSliver, owner_ns: Net subnet_list = list(ip_network.subnets(new_prefix=64)) # Exclude the 1st subnet as it is reserved for control plane subnet_list.pop(0) + # https://github.com/fabric-testbed/ControlFramework/issues/376 + # Exclude the last subnet as the last subnet will be used for the FABRIC STAR Bastion Host Allocation + subnet_list.pop(-1) elif owner_ns.get_type() == ServiceType.FABNetv4: ip_network = IPv4Network(delegated_label.ipv4_subnet) From e369926df0aedd935f0b661c234c45f2da4c52c4 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 6 Jun 2024 11:24:06 -0400 Subject: [PATCH 071/133] update the config to allow different policy for different sites --- fabric_cf/actor/core/core/policy.py | 3 ++ .../policy/broker_simpler_units_policy.py | 30 +++++++++++-------- fabric_cf/broker/config.broker.yaml | 13 ++++++-- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/fabric_cf/actor/core/core/policy.py b/fabric_cf/actor/core/core/policy.py index c882b96c..a7f8e563 100644 --- a/fabric_cf/actor/core/core/policy.py +++ b/fabric_cf/actor/core/core/policy.py @@ -121,6 +121,9 @@ def initialize(self, *, config: ActorConfig): if self.clock is None: raise ActorException("Missing clock") + if config and config.get_policy() and config.get_policy().get_properties(): + self.set_properties(properties=config.get_policy().get_properties()) + self.initialized = True def internal_error(self, *, message: str): diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 7601000b..505e909b 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -26,6 +26,7 @@ from __future__ import annotations import enum +import random import threading import traceback from datetime import datetime @@ -76,6 +77,7 @@ class BrokerAllocationAlgorithm(Enum): FirstFit = enum.auto() BestFit = enum.auto() WorstFit = enum.auto() + Random = enum.auto() def __repr__(self): return self.name @@ -603,6 +605,8 @@ def __allocate_nodes(self, *, reservation: ABCBrokerReservation, inv: NetworkNod """ delegation_id = None node_id_list = self.__candidate_nodes(sliver=sliver) + if self.get_algorithm_type(site=sliver.site) == BrokerAllocationAlgorithm.Random: + random.shuffle(node_id_list) if len(node_id_list) == 0 and sliver.site not in self.combined_broker_model.get_sites(): error_msg = f'Unknown site {sliver.site} requested for {reservation}' @@ -619,14 +623,9 @@ def __allocate_nodes(self, *, reservation: ABCBrokerReservation, inv: NetworkNod self.logger.error(error_msg) return delegation_id, sliver, error_msg - if self.get_algorithm_type().lower() == BrokerAllocationAlgorithm.FirstFit.name.lower(): - return self.__find_first_fit(node_id_list=node_id_list, - node_id_to_reservations=node_id_to_reservations, - inv=inv, reservation=reservation) - - else: - raise BrokerException(error_code=ExceptionErrorCode.NOT_SUPPORTED, - msg=f"Broker currently only supports First Fit") + return self.__find_first_fit(node_id_list=node_id_list, + node_id_to_reservations=node_id_to_reservations, + inv=inv, reservation=reservation) def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: NetworkServiceSliver, node_id_to_reservations: dict) -> Tuple[str, BaseSliver, Any]: @@ -1442,12 +1441,17 @@ def unmerge_adm(self, *, graph_id: str): self.combined_broker_model.rollback(graph_id=snapshot_graph_id) raise e - def get_algorithm_type(self) -> str: + def get_algorithm_type(self, site: str) -> BrokerAllocationAlgorithm: if self.properties is not None: - algo_str = self.properties.get(Constants.ALGORITHM, None) - if algo_str is not None: - return algo_str - return BrokerAllocationAlgorithm.FirstFit.name + algorithms = self.properties.get(Constants.ALGORITHM, None) + random_algo = algorithms.get(str(BrokerAllocationAlgorithm.Random)) + if random_algo and random_algo.get('enabled') and random_algo.get('sites') and \ + site in random_algo.get('sites'): + return BrokerAllocationAlgorithm.Random + first_fit_algo = algorithms.get(BrokerAllocationAlgorithm.Random.name) + if first_fit_algo and first_fit_algo.get('enabled'): + return BrokerAllocationAlgorithm.FirstFit + return BrokerAllocationAlgorithm.FirstFit if __name__ == '__main__': diff --git a/fabric_cf/broker/config.broker.yaml b/fabric_cf/broker/config.broker.yaml index 03fa612f..0a8e9f2d 100644 --- a/fabric_cf/broker/config.broker.yaml +++ b/fabric_cf/broker/config.broker.yaml @@ -125,7 +125,7 @@ neo4j: bqm: kafka-topic: broker-resource-usage # in seconds (default set to 2 hours) - publish-interval: 7200 + publish-interval: kafka-sasl-producer-username: kafka-sasl-producer-password: @@ -139,10 +139,17 @@ actor: module: fabric_cf.actor.core.policy.broker_simpler_units_policy class: BrokerSimplerUnitsPolicy properties: - algorithm: FirstFit + algorithm: + FirstFit: # Default policy for all sites + enabled: true + Random: # Random policy for specific sites + enabled: true + sites: # Specify the sites where Random policy should be used + - EDUKY + # Add more sites as needed controls: - control: - type: VM, Container, Baremetal + type: VM, Container, Baremetal, Switch class: NetworkNodeInventory module: fabric_cf.actor.core.policy.network_node_inventory - control: From 016b03705014d22dddeb7c9d92c77e566494f121 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 6 Jun 2024 11:36:52 -0400 Subject: [PATCH 072/133] pass the term --- fabric_cf/actor/core/policy/broker_simpler_units_policy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 56d49036..4ea6c51f 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -648,7 +648,7 @@ def __allocate_nodes(self, *, reservation: ABCBrokerReservation, inv: NetworkNod return self.__find_first_fit(node_id_list=node_id_list, node_id_to_reservations=node_id_to_reservations, - inv=inv, reservation=reservation) + inv=inv, reservation=reservation, term=term) def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: NetworkServiceSliver, node_id_to_reservations: dict, term: Term) -> Tuple[str, BaseSliver, Any]: From c95f3fec90fe5c1a4a079858a675a76a22e17ad7 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 24 Jun 2024 22:00:43 -0400 Subject: [PATCH 073/133] updates for handling modify on network services with peered interfaces --- fabric_cf/actor/core/kernel/reservation_client.py | 10 ++++++++-- .../core/policy/broker_simpler_units_policy.py | 13 +++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/fabric_cf/actor/core/kernel/reservation_client.py b/fabric_cf/actor/core/kernel/reservation_client.py index 199ef6e2..060f6ed7 100644 --- a/fabric_cf/actor/core/kernel/reservation_client.py +++ b/fabric_cf/actor/core/kernel/reservation_client.py @@ -505,12 +505,18 @@ def prepare_ticket(self, extend: bool = False): parent_res = pred_state.get_reservation() if Constants.PEERED in value1: + self.logger.debug(f"KOMAL --- Node MAP:{ifs.get_node_map()} Result: {result}") if parent_res is not None: ns_sliver = parent_res.get_resources().get_sliver() # component_name contains => Peered:: al2s_ifs = ns_sliver.interface_info.interfaces.get(result[2]) - ifs.labels = Labels.update(ifs.labels, vlan=al2s_ifs.labels.vlan) - ifs.set_node_map(node_map=(Constants.PEERED, value2)) + if al2s_ifs: + ifs.labels = Labels.update(ifs.labels, vlan=al2s_ifs.labels.vlan) + ifs.set_node_map(node_map=(Constants.PEERED, value2)) + else: + msg = f"Could not determine al2s_ifs: {al2s_ifs} result: {result}" + self.logger.error(msg) + self.fail(message=msg) continue if parent_res is not None and (parent_res.is_ticketed() or parent_res.is_active()): diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 4ea6c51f..e5bb014f 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -820,6 +820,19 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: if owner_v4_service and owner_v4_service.get_labels(): ero_source_end_info.append((owner_switch.node_id, owner_v4_service.get_labels().ipv4)) + if not owner_ns: + bqm_graph_id, bqm_node_id = sliver.get_node_map() + owner_ns, owner_switch = self.get_network_service_from_graph(node_id=bqm_node_id, + parent=True) + owner_mpls_ns = None + if owner_switch: + for ns in owner_switch.network_service_info.network_services.values(): + if ServiceType.MPLS == ns.get_type(): + owner_mpls_ns = ns + break + delegation_id, delegated_label = InventoryForType.get_delegations(lab_cap_delegations= + owner_ns.get_label_delegations()) + # 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_ns_id)) From c5f77a78e2fbb75362273c2720a90f9a6886bebb Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 27 Jun 2024 12:10:52 -0400 Subject: [PATCH 074/133] fix lease end filter --- fabric_cf/actor/db/psql_database.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fabric_cf/actor/db/psql_database.py b/fabric_cf/actor/db/psql_database.py index 899c61d2..2300dc03 100644 --- a/fabric_cf/actor/db/psql_database.py +++ b/fabric_cf/actor/db/psql_database.py @@ -855,11 +855,21 @@ def get_reservations(self, *, slice_id: str = None, graph_node_id: str = None, p if category is not None: rows = rows.filter(Reservations.rsv_category.in_(category)) + # Ensure start and end are datetime objects + if start and isinstance(start, str): + start = datetime.fromisoformat(start) + if end and isinstance(end, str): + end = datetime.fromisoformat(end) + # Construct filter condition for lease_end within the given time range if start is not None or end is not None: lease_end_filter = True # Initialize with True to avoid NoneType comparison if start is not None and end is not None: - lease_end_filter = and_(start <= Reservations.lease_end, Reservations.lease_end <= end) + lease_end_filter = or_( + and_(start <= Reservations.lease_end, Reservations.lease_end <= end), + and_(start <= Reservations.lease_start, Reservations.lease_start <= end), + and_(Reservations.lease_start <= start, Reservations.lease_end >= end) + ) elif start is not None: lease_end_filter = start <= Reservations.lease_end elif end is not None: @@ -897,7 +907,11 @@ def get_components(self, *, node_id: str, states: list[int], rsv_type: list[str] # Construct filter condition for lease_end within the given time range if start is not None or end is not None: if start is not None and end is not None: - lease_end_filter = and_(start <= Reservations.lease_end, Reservations.lease_end <= end) + lease_end_filter = or_( + and_(start <= Reservations.lease_end, Reservations.lease_end <= end), + and_(start <= Reservations.lease_start, Reservations.lease_start <= end), + and_(Reservations.lease_start <= start, Reservations.lease_end >= end) + ) elif start is not None: lease_end_filter = start <= Reservations.lease_end elif end is not None: From e7939f119b091b5d0af22e2d6643ddab10a105cd Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 28 Jun 2024 00:38:29 -0400 Subject: [PATCH 075/133] two parent hop for subinterfaces --- fabric_cf/actor/fim/fim_helper.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/fabric_cf/actor/fim/fim_helper.py b/fabric_cf/actor/fim/fim_helper.py index 72080e04..fbffefc7 100644 --- a/fabric_cf/actor/fim/fim_helper.py +++ b/fabric_cf/actor/fim/fim_helper.py @@ -436,15 +436,23 @@ def get_interface_sliver_mapping(ifs_node_id: str, slice_graph: ABCASMPropertyGr raise Exception(f"More than one Peer Interface Sliver found for IFS: {ifs_node_id}!") peer_ifs = next(iter(peer_interfaces)) - peer_ns_node_name, peer_ns_id = slice_graph.get_parent(node_id=peer_ifs.node_id, - rel=ABCPropertyGraph.REL_CONNECTS, - parent=ABCPropertyGraph.CLASS_NetworkService) + if peer_ifs.get_type() == InterfaceType.SubInterface: + parent_cp_node_name, parent_cp_node_id = slice_graph.get_parent(node_id=peer_ifs.node_id, + rel=ABCPropertyGraph.REL_CONNECTS, + parent=ABCPropertyGraph.CLASS_ConnectionPoint) + peer_ns_node_name, peer_ns_id = slice_graph.get_parent(node_id=parent_cp_node_id, + rel=ABCPropertyGraph.REL_CONNECTS, + parent=ABCPropertyGraph.CLASS_NetworkService) + else: + peer_ns_node_name, peer_ns_id = slice_graph.get_parent(node_id=peer_ifs.node_id, + rel=ABCPropertyGraph.REL_CONNECTS, + parent=ABCPropertyGraph.CLASS_NetworkService) component_name = None facility = False peer_site = None - if peer_ifs.get_type() in [InterfaceType.DedicatedPort, InterfaceType.SharedPort]: + if peer_ifs.get_type() in [InterfaceType.DedicatedPort, InterfaceType.SharedPort, InterfaceType.SubInterface]: component_name, component_id = slice_graph.get_parent(node_id=peer_ns_id, rel=ABCPropertyGraph.REL_HAS, parent=ABCPropertyGraph.CLASS_Component) # Possibly P4 switch; parent will be a switch @@ -467,6 +475,7 @@ def get_interface_sliver_mapping(ifs_node_id: str, slice_graph: ABCASMPropertyGr # Peer Network Service is FABRIC L3VPN connected to a FABRIC Site # Determine the site to which AL2S Peered Interface is connected to + for ifs in peer_ns.interface_info.interfaces.values(): # Skip the peered interface if ifs.node_id == peer_ifs.node_id: From 96afac3b9a3da34f6eaef099bec6a15e8f569c5d Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 8 Jul 2024 10:35:59 -0400 Subject: [PATCH 076/133] skip subinterfaces from modify --- fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py b/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py index f3f45c04..e5684d7b 100644 --- a/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py +++ b/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py @@ -42,7 +42,7 @@ from fim.slivers.network_node import NodeSliver, NodeType from fim.slivers.network_service import NetworkServiceSliver from fim.slivers.topology_diff import WhatsModifiedFlag -from fim.user import ServiceType, ExperimentTopology +from fim.user import ServiceType, ExperimentTopology, InterfaceType from fabric_cf.actor.core.common.constants import ErrorCodes, Constants from fabric_cf.actor.core.kernel.reservation_states import ReservationPendingStates, ReservationStates @@ -539,6 +539,8 @@ def modify(self, *, new_slice_graph: ABCASMPropertyGraph) -> List[LeaseReservati # Added Interfaces for x in topology_diff.added.interfaces: + if x.type == InterfaceType.SubInterface: + continue new_sliver, parent_node_id = FimHelper.get_parent_node(graph_model=new_slice_graph, interface=x) rid = new_sliver.reservation_info.reservation_id # If corresponding sliver also has add operations; it's already in the map @@ -556,6 +558,8 @@ def modify(self, *, new_slice_graph: ABCASMPropertyGraph) -> List[LeaseReservati # Removed Interfaces for x in topology_diff.removed.interfaces: + if x.type == InterfaceType.SubInterface: + continue sliver, parent_node_id = FimHelper.get_parent_node(graph_model=existing_topology.graph_model, interface=x) rid = sliver.reservation_info.reservation_id # If corresponding sliver also has add operations; it's already in the map From 9c0f4af66ca0879cbb472c968de9bd17012dca67 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 8 Jul 2024 10:43:26 -0400 Subject: [PATCH 077/133] print sub interfaces --- fabric_cf/actor/core/util/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fabric_cf/actor/core/util/utils.py b/fabric_cf/actor/core/util/utils.py index 55ac2619..fae6d3db 100644 --- a/fabric_cf/actor/core/util/utils.py +++ b/fabric_cf/actor/core/util/utils.py @@ -68,6 +68,9 @@ def node_sliver_to_str(*, sliver: NodeSliver): if ns.interface_info is not None and ns.interface_info.interfaces is not None: for i in ns.interface_info.interfaces.values(): result += f"\nIFS: {i}" + if i.interface_info and i.interface_info.interfaces: + for c in ns.interface_info.interfaces.values(): + result += f"\nSub IFS: {c}" return result From 8b90c552f36a3f05723c280d6dec40afb1a3fa02 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 8 Jul 2024 10:48:58 -0400 Subject: [PATCH 078/133] print sub interfaces --- fabric_cf/actor/core/util/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fabric_cf/actor/core/util/utils.py b/fabric_cf/actor/core/util/utils.py index fae6d3db..90da469b 100644 --- a/fabric_cf/actor/core/util/utils.py +++ b/fabric_cf/actor/core/util/utils.py @@ -69,8 +69,8 @@ def node_sliver_to_str(*, sliver: NodeSliver): for i in ns.interface_info.interfaces.values(): result += f"\nIFS: {i}" if i.interface_info and i.interface_info.interfaces: - for c in ns.interface_info.interfaces.values(): - result += f"\nSub IFS: {c}" + for ch_ifc in i.interface_info.interfaces.values(): + result += f"\nSub IFS: {ch_ifc}" return result From 261bd80ee331cef838522e08a7ff5a09e783f2f2 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 8 Jul 2024 13:17:40 -0400 Subject: [PATCH 079/133] changes to update the vm sliver on add/remove sub interface at broker/am --- .../policy/broker_simpler_units_policy.py | 2 - .../actor/core/policy/network_node_control.py | 2 +- fabric_cf/actor/fim/fim_helper.py | 90 ++++++++++++++++--- fabric_cf/broker/test/test.yaml | 8 +- .../core/orchestrator_slice_wrapper.py | 53 +++++------ pyproject.toml | 2 +- 6 files changed, 113 insertions(+), 44 deletions(-) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index e5bb014f..353dc30e 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -1031,8 +1031,6 @@ def extend_private(self, *, reservation: ABCBrokerReservation, inv: InventoryFor sliver = current_resources.get_sliver() diff = sliver.diff(other_sliver=requested_resources.get_sliver()) - #if diff is not None and (diff.added is None or - # (len(diff.added.components) == 0 and len(diff.added.interfaces) == 0)): if diff is not None: sliver = requested_resources.get_sliver() diff --git a/fabric_cf/actor/core/policy/network_node_control.py b/fabric_cf/actor/core/policy/network_node_control.py index ee2473aa..a2e2af8e 100644 --- a/fabric_cf/actor/core/policy/network_node_control.py +++ b/fabric_cf/actor/core/policy/network_node_control.py @@ -234,7 +234,7 @@ def assign(self, *, reservation: ABCAuthorityReservation, delegation_name: str, actor_id=self.authority.get_guid(), sliver=requested, rtype=resource_type, properties=reservation.get_slice().get_config_properties()) modified = UnitSet(plugin=self.authority.get_plugin(), units={unit.reservation_id: unit}) - elif len(diff.removed.components) > 0: + elif len(diff.removed.components) > 0 or len(diff.modified.components): 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()) diff --git a/fabric_cf/actor/fim/fim_helper.py b/fabric_cf/actor/fim/fim_helper.py index fbffefc7..d29e9550 100644 --- a/fabric_cf/actor/fim/fim_helper.py +++ b/fabric_cf/actor/fim/fim_helper.py @@ -609,23 +609,85 @@ def get_owners(*, bqm: ABCCBMPropertyGraph, node_id: str, return switch, mpls_ns, requested_ns @staticmethod - def get_parent_node(*, graph_model: ABCPropertyGraph, component: Component = None, interface: Interface = None, + def get_parent_node(*, graph_model: ABCPropertyGraph, node: Union[Component, Interface], sliver: bool = True) -> Tuple[Union[NodeSliver, NetworkServiceSliver, None], str]: - node = None - if component is not None: - node_name, node_id = graph_model.get_parent(node_id=component.node_id, rel=ABCPropertyGraph.REL_HAS, - parent=ABCPropertyGraph.CLASS_NetworkNode) - if sliver: - node = graph_model.build_deep_node_sliver(node_id=node_id) - elif interface is not None: - node_name, node_id = graph_model.get_parent(node_id=interface.node_id, rel=ABCPropertyGraph.REL_CONNECTS, - parent=ABCPropertyGraph.CLASS_NetworkService) - if sliver: - node = graph_model.build_deep_ns_sliver(node_id=node_id) - else: + """ + Retrieve the parent node of a given component or interface in the graph model. + + This method determines the parent node of a specified component or interface within the provided + property graph model. It can return either a node sliver or a network service sliver based on the + type of the input node and the `sliver` flag. + + :param graph_model: The property graph model used to find parent nodes. + :type graph_model: ABCPropertyGraph + + :param node: The component or interface for which to find the parent node. + :type node: Union[Component, Interface] + + :param sliver: Flag indicating whether to build and return a sliver object for the parent node. + Defaults to True. + :type sliver: bool + + :return: A tuple containing the parent node sliver (or network service sliver) and the parent node ID. + If no parent node is found, returns (None, None). + :rtype: Tuple[Union[NodeSliver, NetworkServiceSliver, None], str] + + :raises Exception: If the `node` argument is None or is neither a Component nor an Interface. + + Example: + >>> parent_node, parent_node_id = get_parent_node(graph_model=my_graph_model, node=my_component) + >>> print(parent_node, parent_node_id) + """ + if node is None: raise Exception("Invalid Arguments - component/interface both are None") - return node, node_id + parent_node = None + parent_node_id = None + + if isinstance(node, Component): + node_name, parent_node_id = graph_model.get_parent( + node_id=node.node_id, + rel=ABCPropertyGraph.REL_HAS, + parent=ABCPropertyGraph.CLASS_NetworkNode + ) + if sliver: + parent_node = graph_model.build_deep_node_sliver(node_id=parent_node_id) + elif isinstance(node, Interface): + if node.type == InterfaceType.SubInterface: + # Get the OVS Network Service attached to Sub Interface + sub_cp_nbs = graph_model.get_first_and_second_neighbor( + node_id=node.node_id, + rel1=ABCPropertyGraph.REL_CONNECTS, + node1_label=ABCPropertyGraph.CLASS_ConnectionPoint, + rel2=ABCPropertyGraph.REL_CONNECTS, + node2_label=ABCPropertyGraph.CLASS_NetworkService + ) + if len(sub_cp_nbs) == 0: + raise Exception(f"Parent (NS-OVS) for Sub Interface: {node.name} cannot be found!") + + # Get the component and node associated with Sub Interface + sub_node = graph_model.get_first_and_second_neighbor( + node_id=sub_cp_nbs[0][1], + rel1=ABCPropertyGraph.REL_HAS, + node1_label=ABCPropertyGraph.CLASS_Component, + rel2=ABCPropertyGraph.REL_HAS, + node2_label=ABCPropertyGraph.CLASS_NetworkNode + ) + if len(sub_node) == 0: + raise Exception(f"Parent for Sub Interface: {node.name} cannot be found!") + parent_node_id = sub_node[0][1] + if sliver: + parent_node = graph_model.build_deep_node_sliver(node_id=parent_node_id) + else: + node_name, parent_node_id = graph_model.get_parent( + node_id=node.node_id, + rel=ABCPropertyGraph.REL_CONNECTS, + parent=ABCPropertyGraph.CLASS_NetworkService + ) + if sliver: + parent_node = graph_model.build_deep_ns_sliver(node_id=parent_node_id) + + return parent_node, parent_node_id @staticmethod def prune_graph(*, graph_id: str) -> ExperimentTopology: diff --git a/fabric_cf/broker/test/test.yaml b/fabric_cf/broker/test/test.yaml index 68b83af0..de894fa3 100644 --- a/fabric_cf/broker/test/test.yaml +++ b/fabric_cf/broker/test/test.yaml @@ -135,7 +135,13 @@ actor: module: fabric_cf.actor.core.policy.broker_simpler_units_policy class: BrokerSimplerUnitsPolicy properties: - algorithm: FirstFit + algorithm: + FirstFit: # Default policy for all sites + enabled: true + Random: # Random policy for specific sites + enabled: true + sites: # Specify the sites where Random policy should be used + - EDUKY controls: - control: type: VM, Container, Baremetal, Switch diff --git a/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py b/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py index e5684d7b..a8fc6865 100644 --- a/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py +++ b/fabric_cf/orchestrator/core/orchestrator_slice_wrapper.py @@ -519,7 +519,7 @@ def modify(self, *, new_slice_graph: ABCASMPropertyGraph) -> List[LeaseReservati # Add components for x in topology_diff.added.components: - sliver, parent_node_id = FimHelper.get_parent_node(graph_model=new_slice_graph, component=x) + sliver, parent_node_id = FimHelper.get_parent_node(graph_model=new_slice_graph, node=x) rid = sliver.reservation_info.reservation_id # If corresponding sliver also has add operations; it's already in the map # No need to rebuild it @@ -529,7 +529,7 @@ def modify(self, *, new_slice_graph: ABCASMPropertyGraph) -> List[LeaseReservati # Remove components for x in topology_diff.removed.components: # Grab the old sliver - sliver, parent_node_id = FimHelper.get_parent_node(graph_model=existing_topology.graph_model, component=x) + sliver, parent_node_id = FimHelper.get_parent_node(graph_model=existing_topology.graph_model, node=x) rid = sliver.reservation_info.reservation_id # If corresponding sliver also has add operations; it's already in the map # No need to rebuild it @@ -539,41 +539,44 @@ def modify(self, *, new_slice_graph: ABCASMPropertyGraph) -> List[LeaseReservati # Added Interfaces for x in topology_diff.added.interfaces: - if x.type == InterfaceType.SubInterface: - continue - new_sliver, parent_node_id = FimHelper.get_parent_node(graph_model=new_slice_graph, interface=x) + new_sliver, parent_node_id = FimHelper.get_parent_node(graph_model=new_slice_graph, node=x) rid = new_sliver.reservation_info.reservation_id # If corresponding sliver also has add operations; it's already in the map # No need to rebuild it if rid not in self.computed_modify_reservations: - new_reservation, dep_update_needed = self.__build_ns_sliver_reservation(slice_graph=new_slice_graph, - node_id=parent_node_id, - node_res_mapping=node_res_mapping) - self.computed_modify_reservations[rid] = ModifiedReservation(sliver=new_reservation.get_sliver(), - dependencies=new_reservation.redeem_processors) + if x.type == InterfaceType.SubInterface: + self.computed_modify_reservations[rid] = ModifiedReservation(sliver=new_sliver) + else: + new_reservation, dep_update_needed = self.__build_ns_sliver_reservation(slice_graph=new_slice_graph, + node_id=parent_node_id, + node_res_mapping=node_res_mapping) + self.computed_modify_reservations[rid] = ModifiedReservation(sliver=new_reservation.get_sliver(), + dependencies=new_reservation.redeem_processors) - if dep_update_needed: - ns_peered_reservations.append(new_reservation) - ns_mapping[new_reservation.sliver.node_id] = rid + if dep_update_needed: + ns_peered_reservations.append(new_reservation) + ns_mapping[new_reservation.sliver.node_id] = rid # Removed Interfaces for x in topology_diff.removed.interfaces: - if x.type == InterfaceType.SubInterface: - continue - sliver, parent_node_id = FimHelper.get_parent_node(graph_model=existing_topology.graph_model, interface=x) + sliver, parent_node_id = FimHelper.get_parent_node(graph_model=existing_topology.graph_model, node=x) rid = sliver.reservation_info.reservation_id # If corresponding sliver also has add operations; it's already in the map # No need to rebuild it if rid not in self.computed_modify_reservations: - new_reservation, dep_update_needed = self.__build_ns_sliver_reservation(slice_graph=new_slice_graph, - node_id=parent_node_id, - node_res_mapping=node_res_mapping) - self.computed_modify_reservations[rid] = ModifiedReservation(sliver=new_reservation.get_sliver(), - dependencies=new_reservation.redeem_processors) - - if dep_update_needed: - ns_peered_reservations.append(new_reservation) - ns_mapping[new_reservation.sliver.node_id] = rid + if x.type == InterfaceType.SubInterface: + new_sliver = new_slice_graph.build_deep_node_sliver(node_id=parent_node_id) + self.computed_modify_reservations[rid] = ModifiedReservation(sliver=new_sliver) + else: + new_reservation, dep_update_needed = self.__build_ns_sliver_reservation(slice_graph=new_slice_graph, + node_id=parent_node_id, + node_res_mapping=node_res_mapping) + self.computed_modify_reservations[rid] = ModifiedReservation(sliver=new_reservation.get_sliver(), + dependencies=new_reservation.redeem_processors) + + if dep_update_needed: + ns_peered_reservations.append(new_reservation) + ns_mapping[new_reservation.sliver.node_id] = rid # Remove nodes for x in topology_diff.removed.nodes: diff --git a/pyproject.toml b/pyproject.toml index 510b9827..056d31b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "PyYAML", "fabric_fss_utils==1.5.0", "fabric-message-bus==1.7.0b1", - "fabric-fim==1.7.0b6", + "fabric-fim==1.7.0b13", "fabric-credmgr-client==1.6.0", "ansible" ] From ed2753a297e4ff16cbafe679128601f8401e6428 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 9 Jul 2024 12:51:25 -0400 Subject: [PATCH 080/133] update broker policy to check allocations on renew --- fabric_cf/actor/core/apis/abc_database.py | 4 +- .../actor/core/plugins/db/actor_database.py | 32 ++- .../policy/broker_simpler_units_policy.py | 74 +++--- .../core/policy/network_node_inventory.py | 29 ++- .../core/policy/network_service_inventory.py | 242 +++++++++++++++++- fabric_cf/actor/db/psql_database.py | 13 +- fabric_cf/orchestrator/test/test.yaml | 1 + 7 files changed, 340 insertions(+), 55 deletions(-) diff --git a/fabric_cf/actor/core/apis/abc_database.py b/fabric_cf/actor/core/apis/abc_database.py index 0d64f244..d6386ad3 100644 --- a/fabric_cf/actor/core/apis/abc_database.py +++ b/fabric_cf/actor/core/apis/abc_database.py @@ -172,7 +172,8 @@ def get_reservations(self, *, slice_id: ID = None, graph_node_id: str = None, pr @abstractmethod def get_components(self, *, node_id: str, states: list[int], rsv_type: list[str], component: str = None, - bdf: str = None, start: datetime = None, end: datetime = None) -> Dict[str, List[str]]: + bdf: str = None, start: datetime = None, end: datetime = None, + excludes: List[str] = None) -> Dict[str, List[str]]: """ Returns components matching the search criteria @param node_id: Worker Node ID to which components belong @@ -182,6 +183,7 @@ def get_components(self, *, node_id: str, states: list[int], rsv_type: list[str] @param bdf: Component's PCI address @param start: start time @param end: end time + @param excludes: Excludes the list of reservations NOTE# For P4 switches; node_id=node+renc-p4-sw component=ip+192.168.11.8 bdf=p1 diff --git a/fabric_cf/actor/core/plugins/db/actor_database.py b/fabric_cf/actor/core/plugins/db/actor_database.py index 49c4c435..6dbed30d 100644 --- a/fabric_cf/actor/core/plugins/db/actor_database.py +++ b/fabric_cf/actor/core/plugins/db/actor_database.py @@ -290,6 +290,8 @@ def add_reservation(self, *, reservation: ABCReservationMixin): site = sliver.get_site() rsv_type = sliver.get_type().name from fim.slivers.network_service import NetworkServiceSliver + from fim.slivers.network_node import NodeSliver + if isinstance(sliver, NetworkServiceSliver) and sliver.interface_info: components = [] for interface in sliver.interface_info.interfaces.values(): @@ -304,6 +306,18 @@ def add_reservation(self, *, reservation: ABCReservationMixin): bdf = ":".join(split_string[3:]) if len(split_string) > 3 else None if node_id and comp_id and bdf: components.append((node_id, comp_id, bdf)) + elif isinstance(sliver, NodeSliver) and sliver.attached_components_info: + node_id = reservation.get_graph_node_id() + if node_id: + components = [] + for c in sliver.attached_components_info.devices.values(): + bqm_id, comp_id = c.get_node_map() + if c.labels and c.labels.bdf: + bdf = c.labels.bdf + if isinstance(c.labels.bdf, str): + bdf = [c.labels.bdf] + for x in bdf: + components.append((node_id, comp_id, x)) term = reservation.get_term() @@ -343,6 +357,7 @@ def update_reservation(self, *, reservation: ABCReservationMixin): site = sliver.get_site() rsv_type = sliver.get_type().name from fim.slivers.network_service import NetworkServiceSliver + from fim.slivers.network_node import NodeSliver if isinstance(sliver, NetworkServiceSliver) and sliver.interface_info: components = [] for interface in sliver.interface_info.interfaces.values(): @@ -357,6 +372,18 @@ def update_reservation(self, *, reservation: ABCReservationMixin): bdf = ":".join(split_string[3:]) if len(split_string) > 3 else None if node_id and comp_id and bdf: components.append((node_id, comp_id, bdf)) + elif isinstance(sliver, NodeSliver) and sliver.attached_components_info: + node_id = reservation.get_graph_node_id() + if node_id: + components = [] + for c in sliver.attached_components_info.devices.values(): + bqm_id, comp_id = c.get_node_map() + if c.labels and c.labels.bdf: + bdf = c.labels.bdf + if isinstance(c.labels.bdf, str): + bdf = [c.labels.bdf] + for x in bdf: + components.append((node_id, comp_id, x)) term = reservation.get_term() begin = time.time() @@ -512,10 +539,11 @@ def get_authority_reservations(self) -> List[ABCReservationMixin]: return result def get_components(self, *, node_id: str, states: list[int], rsv_type: list[str], component: str = None, - bdf: str = None, start: datetime = None, end: datetime = None) -> Dict[str, List[str]]: + bdf: str = None, start: datetime = None, end: datetime = None, + excludes: List[str] = None) -> Dict[str, List[str]]: try: return self.db.get_components(node_id=node_id, states=states, component=component, bdf=bdf, - rsv_type=rsv_type, start=start, end=end) + rsv_type=rsv_type, start=start, end=end, excludes=excludes) except Exception as e: self.logger.error(e) finally: diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 353dc30e..390f5a56 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -557,7 +557,7 @@ def __prune_nodes_in_maintenance(self, node_id_list: List[str], site: str, reser return node_id_list def __find_first_fit(self, node_id_list: List[str], node_id_to_reservations: dict, inv: NetworkNodeInventory, - reservation: ABCBrokerReservation, term: Term) -> Tuple[str, BaseSliver, Any]: + reservation: ABCBrokerReservation, term: Term, sliver: NodeSliver) -> Tuple[str, BaseSliver, Any]: """ Find First Available Node which can serve the reservation @param node_id_list: Candidate Nodes @@ -567,20 +567,18 @@ def __find_first_fit(self, node_id_list: List[str], node_id_to_reservations: dic @return tuple containing delegation id, sliver, error message if any """ delegation_id = None - sliver = None error_msg = None self.logger.debug(f"Possible candidates to serve {reservation} candidates# {node_id_list}") - requested_sliver = reservation.get_requested_resources().get_sliver() - is_create = requested_sliver.get_node_map() is None + is_create = sliver.get_node_map() is None for node_id in node_id_list: try: self.logger.debug(f"Attempting to allocate {reservation} via graph_node# {node_id}") graph_node = self.get_network_node_from_graph(node_id=node_id) - if requested_sliver.labels is not None and requested_sliver.labels.instance_parent is not None: - self.logger.info(f"Sliver {requested_sliver} is requested on worker: " - f"{requested_sliver.labels.instance_parent}") - if graph_node.get_name() != requested_sliver.labels.instance_parent: + if sliver.labels is not None and sliver.labels.instance_parent is not None: + self.logger.info(f"Sliver {sliver} is requested on worker: " + f"{sliver.labels.instance_parent}") + if graph_node.get_name() != sliver.labels.instance_parent: self.logger.info(f"Skipping candidate node: {graph_node}") continue @@ -590,10 +588,11 @@ def __find_first_fit(self, node_id_list: List[str], node_id_to_reservations: dic end=term.get_end_time()) existing_components = self.get_existing_components(node_id=node_id, start=term.get_start_time(), - end=term.get_end_time()) + end=term.get_end_time(), + excludes=[str(reservation.get_reservation_id())]) delegation_id, sliver = inv.allocate(rid=reservation.get_reservation_id(), - requested_sliver=requested_sliver, + requested_sliver=sliver, graph_id=self.combined_broker_model_graph_id, graph_node=graph_node, existing_reservations=existing_reservations, @@ -609,9 +608,9 @@ def __find_first_fit(self, node_id_list: List[str], node_id_to_reservations: dic else: raise e - if delegation_id is None and requested_sliver.labels is not None and \ - requested_sliver.labels.instance_parent is not None: - error_msg = f"Insufficient Resources: {requested_sliver.labels.instance_parent} " \ + if delegation_id is None and sliver.labels is not None and \ + sliver.labels.instance_parent is not None: + error_msg = f"Insufficient Resources: {sliver.labels.instance_parent} " \ f"cannot serve the requested sliver - {error_msg}" return delegation_id, sliver, error_msg @@ -648,7 +647,7 @@ def __allocate_nodes(self, *, reservation: ABCBrokerReservation, inv: NetworkNod return self.__find_first_fit(node_id_list=node_id_list, node_id_to_reservations=node_id_to_reservations, - inv=inv, reservation=reservation, term=term) + inv=inv, reservation=reservation, term=term, sliver=sliver) def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: NetworkServiceSliver, node_id_to_reservations: dict, term: Term) -> Tuple[str, BaseSliver, Any]: @@ -749,7 +748,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: start=term.get_start_time(), end=term.get_end_time()) # Set vlan - source: (c) - only for dedicated NICs - ifs = inv.allocate_ifs(requested_ns=sliver, requested_ifs=ifs, owner_ns=owner_ns, + ifs = inv.allocate_ifs(rid=rid, requested_ns=sliver, requested_ifs=ifs, owner_ns=owner_ns, bqm_ifs=bqm_cp, existing_reservations=existing_reservations) local_name = net_cp.get_name() @@ -833,9 +832,6 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: delegation_id, delegated_label = InventoryForType.get_delegations(lab_cap_delegations= owner_ns.get_label_delegations()) - # 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_ns_id)) - # Set the Subnet and gateway from the Owner Switch (a) existing_reservations = self.get_existing_reservations(node_id=owner_ns_id, node_id_to_reservations=node_id_to_reservations, @@ -851,7 +847,10 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: sliver = inv.allocate(rid=rid, requested_ns=sliver, owner_ns=owner_ns, existing_reservations=existing_reservations) - self.__allocate_peered_interfaces(peered_interfaces=peered_ns_interfaces, owner_switch=owner_switch, + # 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_ns_id)) + + self.__allocate_peered_interfaces(rid=rid, peered_interfaces=peered_ns_interfaces, owner_switch=owner_switch, owner_mpls=owner_mpls_ns, inv=inv, sliver=sliver, owner_ns=owner_ns, node_id_to_reservations=node_id_to_reservations, term=term) @@ -890,7 +889,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: return delegation_id, sliver, error_msg - def __allocate_peered_interfaces(self, *, peered_interfaces: List[InterfaceSliver], owner_switch: NodeSliver, + def __allocate_peered_interfaces(self, *, rid: ID, peered_interfaces: List[InterfaceSliver], owner_switch: NodeSliver, inv: NetworkServiceInventory, sliver: NetworkServiceSliver, owner_mpls: NetworkServiceSliver, owner_ns: NetworkServiceSliver, node_id_to_reservations: dict, term: Term): @@ -944,7 +943,7 @@ def __allocate_peered_interfaces(self, *, peered_interfaces: List[InterfaceSlive start=term.get_start_time(), end=term.get_end_time()) - pfs = inv.allocate_peered_ifs(owner_switch=owner_switch, requested_ifs=pfs, + pfs = inv.allocate_peered_ifs(rid=rid, owner_switch=owner_switch, requested_ifs=pfs, bqm_interface=bqm_interface, existing_reservations=existing_reservations) @@ -958,10 +957,13 @@ def __allocate_peered_interfaces(self, *, peered_interfaces: List[InterfaceSlive sliver.set_node_map(node_map=(self.combined_broker_model_graph_id, owner_ns.node_id)) def ticket_inventory(self, *, reservation: ABCBrokerReservation, inv: InventoryForType, term: Term, - node_id_to_reservations: dict) -> Tuple[bool, dict, Any]: + node_id_to_reservations: dict, extend: bool = False) -> Tuple[bool, dict, Any]: error_msg = None try: - rset = reservation.get_requested_resources() + if extend: + rset = reservation.get_resources() + else: + rset = reservation.get_requested_resources() needed = rset.get_units() # for network node slivers @@ -1034,15 +1036,17 @@ def extend_private(self, *, reservation: ABCBrokerReservation, inv: InventoryFor if diff is not None: sliver = requested_resources.get_sliver() - if diff is None or diff.added is None or \ - (len(diff.added.components) == 0 and len(diff.added.interfaces) == 0) or \ - self.__is_modify_on_openstack_vnic(sliver=sliver): + #if diff is None or diff.added is None or \ + # (len(diff.added.components) == 0 and len(diff.added.interfaces) == 0) or \ + # self.__is_modify_on_openstack_vnic(sliver=sliver): + if self.__is_modify_on_openstack_vnic(sliver=sliver): self.issue_ticket(reservation=reservation, units=needed, rtype=requested_resources.get_type(), term=term, source=reservation.get_source(), sliver=sliver) else: status, node_id_to_reservations, error_msg = self.ticket_inventory(reservation=reservation, inv=inv, term=term, - node_id_to_reservations=node_id_to_reservations) + node_id_to_reservations=node_id_to_reservations, + extend=True) if not status and not reservation.is_failed(): fail_message = f"Insufficient resources for specified start time, Failing reservation: " \ f"{reservation.get_reservation_id()}" @@ -1448,13 +1452,15 @@ def get_network_service_from_graph(self, *, node_id: str, self.lock.release() def get_existing_reservations(self, node_id: str, node_id_to_reservations: dict, - start: datetime = None, end: datetime = None) -> List[ABCReservationMixin]: + start: datetime = None, end: datetime = None, + excludes: List[str] = None) -> List[ABCReservationMixin]: """ Get existing reservations which are served by CBM node identified by node_id :param node_id: :param node_id_to_reservations: :param start :param end + :param excludes: :return: list of reservations """ states = [ReservationStates.Active.value, @@ -1466,7 +1472,8 @@ def get_existing_reservations(self, node_id: str, node_id_to_reservations: dict, existing_reservations = self.actor.get_plugin().get_database().get_reservations(graph_node_id=node_id, states=states, start=start, - end=end) + end=end, + excludes=excludes) reservations_allocated_in_cycle = node_id_to_reservations.get(node_id, None) @@ -1485,12 +1492,14 @@ def get_existing_reservations(self, node_id: str, node_id_to_reservations: dict, return existing_reservations - def get_existing_components(self, node_id: str, start: datetime = None, end: datetime = None) -> Dict[str, List[str]]: + def get_existing_components(self, node_id: str, start: datetime = None, end: datetime = None, + excludes: List[str] = None) -> Dict[str, List[str]]: """ Get existing components attached to Active/Ticketed Network Service Slivers :param node_id: :param start: :param end: + :param excludes: :return: list of components """ states = [ReservationStates.Active.value, @@ -1503,9 +1512,12 @@ def get_existing_components(self, node_id: str, start: datetime = None, end: dat for x in ServiceType: res_type.append(str(x)) + for x in NodeType: + res_type.append(str(x)) + # Only get Active or Ticketing reservations return self.actor.get_plugin().get_database().get_components(node_id=node_id, rsv_type=res_type, states=states, - start=start, end=end) + start=start, end=end, excludes=excludes) def set_logger(self, logger): """ diff --git a/fabric_cf/actor/core/policy/network_node_inventory.py b/fabric_cf/actor/core/policy/network_node_inventory.py index 92304e8d..e1a6f887 100644 --- a/fabric_cf/actor/core/policy/network_node_inventory.py +++ b/fabric_cf/actor/core/policy/network_node_inventory.py @@ -434,8 +434,19 @@ def __check_components(self, *, rid: ID, requested_components: AttachedComponent self.logger.debug(f"requested_components: {requested_components.devices.values()} for reservation# {rid}") for name, requested_component in requested_components.devices.items(): if not is_create and requested_component.get_node_map() is not None: + bqm_id, node_id = requested_component.get_node_map() + allocated_bdfs = existing_components.get(node_id) + if allocated_bdfs and requested_component.labels and requested_component.labels.bdf: + bdfs = requested_component.labels.bdf + if isinstance(requested_component.labels.bdf, str): + bdfs = [requested_component.labels.bdf] + for x in bdfs: + if x in allocated_bdfs: + raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, + msg=f"Renew failed: Component of type: {requested_component.get_model()} " + f"not available in graph node: {graph_node.node_id}") + self.logger.debug(f"==========Ignoring Allocated component: {requested_component} for modify") - # TODO exclude already allocated component to the same reservation continue self.logger.debug(f"==========Allocating component: {requested_component}") resource_type = requested_component.get_type() @@ -500,9 +511,6 @@ def __allocate_p4_switch(self, *, rid: ID, requested_sliver: NodeSliver, graph_i if len(graph_node.get_capacity_delegations().get_delegation_ids()) > 0: delegation_id = next(iter(graph_node.get_capacity_delegations().get_delegation_ids())) - # Nothing to do, just return - return delegation_id, requested_sliver - # Handle allocation to account for leaked Network Services for n in existing_components.keys(): if n in graph_node.node_id: @@ -574,17 +582,18 @@ def allocate(self, *, rid: ID, requested_sliver: BaseSliver, graph_id: str, grap requested_capacity_hints = requested_sliver.get_capacity_hints() catalog = InstanceCatalog() requested_capacities = catalog.get_instance_capacities(instance_type=requested_capacity_hints.instance_type) - - # Check if Capacities can be satisfied - delegation_id = self.__check_capacities(rid=rid, - requested_capacities=requested_capacities, - delegated_capacities=graph_node.get_capacity_delegations(), - existing_reservations=existing_reservations) else: + requested_capacities = requested_sliver.get_capacity_allocations() # In case of modify, directly get delegation_id if len(graph_node.get_capacity_delegations().get_delegation_ids()) > 0: delegation_id = next(iter(graph_node.get_capacity_delegations().get_delegation_ids())) + # Check if Capacities can be satisfied + delegation_id = self.__check_capacities(rid=rid, + requested_capacities=requested_capacities, + delegated_capacities=graph_node.get_capacity_delegations(), + existing_reservations=existing_reservations) + # Check if Components can be allocated if requested_sliver.attached_components_info is not None: requested_sliver.attached_components_info = self.__check_components( diff --git a/fabric_cf/actor/core/policy/network_service_inventory.py b/fabric_cf/actor/core/policy/network_service_inventory.py index d071dc12..bff79cd2 100644 --- a/fabric_cf/actor/core/policy/network_service_inventory.py +++ b/fabric_cf/actor/core/policy/network_service_inventory.py @@ -27,7 +27,7 @@ import random import traceback from ipaddress import IPv6Network, IPv4Network -from typing import List, Tuple +from typing import List, Tuple, Union from fim.slivers.capacities_labels import Labels from fim.slivers.gateway import Gateway @@ -63,13 +63,16 @@ def __extract_vlan_range(*, labels: Labels) -> List[int] or None: vlan_range = [int(labels.vlan)] return vlan_range - def __exclude_allocated_vlans(self, *, available_vlan_range: List[int], bqm_ifs: InterfaceSliver, + def __exclude_allocated_vlans(self, *, rid: ID, available_vlan_range: List[int], bqm_ifs: InterfaceSliver, existing_reservations: List[ABCReservationMixin]) -> List[int]: # Exclude the already allocated VLANs and subnets if existing_reservations is None: return available_vlan_range for reservation in existing_reservations: + if rid == reservation.get_reservation_id(): + continue + # For Active or Ticketed or Ticketing reservations; reduce the counts from available allocated_sliver = None if reservation.is_ticketing() and reservation.get_approved_resources() is not None: @@ -110,7 +113,7 @@ def __exclude_allocated_vlans(self, *, available_vlan_range: List[int], bqm_ifs: msg=f"No VLANs available!") return available_vlan_range - def allocate_ifs(self, *, requested_ns: NetworkServiceSliver, requested_ifs: InterfaceSliver, + def allocate_ifs(self, *, rid: ID, requested_ns: NetworkServiceSliver, requested_ifs: InterfaceSliver, owner_ns: NetworkServiceSliver, bqm_ifs: InterfaceSliver, existing_reservations: List[ABCReservationMixin]) -> InterfaceSliver: """ @@ -120,6 +123,7 @@ def allocate_ifs(self, *, requested_ns: NetworkServiceSliver, requested_ifs: Int - grab the VLAN from BQM Site specific NetworkService - exclude the VLAN already assigned to other Interface Sliver on the same port - allocate the first available VLAN to the Interface Sliver + :param rid: Reservation ID :param requested_ns: Requested NetworkService :param requested_ifs: Requested Interface Sliver :param owner_ns: BQM NetworkService identified to serve the InterfaceSliver @@ -159,7 +163,7 @@ def allocate_ifs(self, *, requested_ns: NetworkServiceSliver, requested_ifs: Int # Validate the VLANs vlan_range = self.__extract_vlan_range(labels=bqm_ifs.labels) if vlan_range is not None: - vlan_range = self.__exclude_allocated_vlans(available_vlan_range=vlan_range, bqm_ifs=bqm_ifs, + vlan_range = self.__exclude_allocated_vlans(rid=rid, available_vlan_range=vlan_range, bqm_ifs=bqm_ifs, existing_reservations=existing_reservations) if requested_vlan is None: @@ -184,12 +188,12 @@ def allocate_ifs(self, *, requested_ns: NetworkServiceSliver, requested_ifs: Int vlan_range = self.__extract_vlan_range(labels=bqm_ifs.labels) if vlan_range is not None: - vlan_range = self.__exclude_allocated_vlans(available_vlan_range=vlan_range, bqm_ifs=bqm_ifs, + vlan_range = self.__exclude_allocated_vlans(rid=rid, available_vlan_range=vlan_range, bqm_ifs=bqm_ifs, existing_reservations=existing_reservations) if bqm_ifs.get_type() != InterfaceType.FacilityPort: # Allocate the first available VLAN - #requested_ifs.labels.vlan = str(random.choice(vlan_range)) - requested_ifs.labels.vlan = str(vlan_range[0]) + requested_ifs.labels.vlan = str(random.choice(vlan_range)) + #requested_ifs.labels.vlan = str(vlan_range[0]) requested_ifs.label_allocations = Labels(vlan=requested_ifs.labels.vlan) else: if requested_ifs.labels is None: @@ -197,7 +201,7 @@ def allocate_ifs(self, *, requested_ns: NetworkServiceSliver, requested_ifs: Int if requested_ifs.labels.vlan is None: requested_ifs.labels.vlan = str(random.choice(vlan_range)) - requested_ifs.labels.vlan = str(vlan_range[0]) + #requested_ifs.labels.vlan = str(vlan_range[0]) if int(requested_ifs.labels.vlan) not in vlan_range: raise BrokerException(error_code=ExceptionErrorCode.FAILURE, @@ -208,6 +212,12 @@ def allocate_ifs(self, *, requested_ns: NetworkServiceSliver, requested_ifs: Int return requested_ifs def __allocate_ip_address_to_ifs(self, *, requested_ns: NetworkServiceSliver) -> NetworkServiceSliver: + """ + Allocate IP addresses to the interfaces of the requested network service sliver. + + :param requested_ns: The requested network service sliver. + :return: The updated network service sliver with allocated IP addresses. + """ if requested_ns.gateway is None: return requested_ns @@ -301,6 +311,210 @@ def allocate(self, *, rid: ID, requested_ns: NetworkServiceSliver, owner_ns: Net :param requested_ns: Requested NetworkService :param owner_ns: BQM Network Service identified to serve the NetworkService :param existing_reservations: Existing Reservations which also are served by the owner switch + :return: NetworkService updated with the allocated subnet for FABNetv4 and FABNetv6 services + """ + try: + if requested_ns.get_type() not in Constants.L3_SERVICES: + return requested_ns + + # Grab Label Delegations + delegation_id, delegated_label = self.get_delegations(lab_cap_delegations=owner_ns.get_label_delegations()) + + # HACK to use FabNetv6 for FabNetv6Ext as both have the same range + requested_ns_type = requested_ns.get_type() + if requested_ns_type == ServiceType.FABNetv6Ext: + requested_ns_type = ServiceType.FABNetv6 + + # Handle L3VPN type specifically + if requested_ns_type == ServiceType.L3VPN: + if requested_ns.labels is not None: + requested_ns.labels = Labels.update(requested_ns.labels, asn=delegated_label.asn) + else: + requested_ns.labels = Labels(asn=delegated_label.asn) + return requested_ns + + ip_network, subnet_list = self._generate_subnet_list(owner_ns=owner_ns, delegated_label=delegated_label) + + # Exclude the already allocated subnets + subnet_list = self._exclude_allocated_subnets(subnet_list=subnet_list, requested_ns_type=requested_ns_type, + rid=rid, existing_reservations=existing_reservations) + + # Extend Case + if requested_ns.get_node_map(): + self._can_extend(subnet_list=subnet_list, requested_ns=requested_ns) + return requested_ns + + gateway_labels = self._assign_gateway_labels(ip_network=ip_network, subnet_list=subnet_list, + requested_ns_type=requested_ns.get_type()) + + self.logger.debug(f"Gateway Labels: {gateway_labels}") + + requested_ns.gateway = Gateway(lab=gateway_labels) + + # Allocate the IP Addresses for the requested NS + requested_ns = self.__allocate_ip_address_to_ifs(requested_ns=requested_ns) + except Exception as e: + self.logger.error(f"Error in allocate_gateway_for_ns: {e}") + self.logger.error(traceback.format_exc()) + raise BrokerException(msg=f"Allocation failure for Requested Network Service: {e}") + + return requested_ns + + def _generate_subnet_list(self, *, owner_ns: NetworkServiceSliver, + delegated_label: Labels) -> Tuple[Union[IPv4Network, IPv6Network], List]: + """ + Generate the list of subnets based on the owner network service type. + + :param owner_ns: The NetworkServiceSliver representing the owner network service. + :param delegated_label: The Labels object containing the delegated subnet information. + :return: A tuple containing the IP network and the list of generated subnets. + """ + subnet_list = None + ip_network = None + if owner_ns.get_type() in Constants.L3_FABNETv6_SERVICES: + ip_network = IPv6Network(delegated_label.ipv6_subnet) + subnet_list = list(ip_network.subnets(new_prefix=64)) + # Exclude the 1st subnet as it is reserved for control plane + subnet_list.pop(0) + # Exclude the last subnet for FABRIC STAR Bastion Host Allocation + subnet_list.pop(-1) + + elif owner_ns.get_type() in [ServiceType.FABNetv4, ServiceType.FABNetv4Ext]: + ip_network = IPv4Network(delegated_label.ipv4_subnet) + if owner_ns.get_type() == ServiceType.FABNetv4: + subnet_list = list(ip_network.subnets(new_prefix=24)) + subnet_list.pop(0) + + elif owner_ns.get_type() == ServiceType.FABNetv4Ext: + subnet_list = list(ip_network.hosts()) + + self.logger.debug(f"Available Subnets: {subnet_list}") + + return ip_network, subnet_list + + def _exclude_allocated_subnets(self, *, subnet_list: List, requested_ns_type: str, rid: ID, + existing_reservations: List[ABCReservationMixin]) -> List: + """ + Exclude the subnets that are already allocated. + + :param subnet_list: A list of available subnets to be allocated. + :param requested_ns_type: The type of the requested network service. + :param rid: The reservation ID of the current request. + :param existing_reservations: A list of existing reservations that may contain allocated subnets. + :return: A list of subnets excluding those that have already been allocated. + """ + for reservation in existing_reservations: + if rid == reservation.get_reservation_id(): + continue + + allocated_sliver = self._get_allocated_sliver(reservation) + if allocated_sliver is None: + continue + + # HACK to use FabNetv6 for FabNetv6Ext as both have the same range + # Needs to be removed if FabNetv6/FabNetv6Ext are configured with different ranges + allocated_sliver_type = allocated_sliver.get_type() + if allocated_sliver_type == ServiceType.FABNetv6Ext: + allocated_sliver_type = ServiceType.FABNetv6 + # HACK End + + if allocated_sliver_type != requested_ns_type: + continue + + if allocated_sliver.get_type() == ServiceType.FABNetv4: + subnet_to_remove = IPv4Network(allocated_sliver.get_gateway().lab.ipv4_subnet) + subnet_list.remove(subnet_to_remove) + + elif allocated_sliver.get_type() == ServiceType.FABNetv4Ext: + if allocated_sliver.labels is not None and allocated_sliver.labels.ipv4 is not None: + for x in allocated_sliver.labels.ipv4: + subnet_to_remove = ipaddress.IPv4Address(x) + subnet_list.remove(subnet_to_remove) + + elif allocated_sliver.get_type() in Constants.L3_FABNETv6_SERVICES: + subnet_to_remove = IPv6Network(allocated_sliver.get_gateway().lab.ipv6_subnet) + subnet_list.remove(subnet_to_remove) + + self.logger.debug(f"Excluding already allocated subnet for reservation {reservation.get_reservation_id()}") + + return subnet_list + + def _get_allocated_sliver(self, reservation: ABCReservationMixin) -> NetworkServiceSliver: + """ + Retrieve the allocated sliver from the reservation. + + :param reservation: An instance of ABCReservationMixin representing the reservation to retrieve the sliver from. + :return: The allocated NetworkServiceSliver if available, otherwise None. + """ + if reservation.is_ticketing() and reservation.get_approved_resources() is not None: + return reservation.get_approved_resources().get_sliver() + if (reservation.is_active() or reservation.is_ticketed()) and reservation.get_resources() is not None: + return reservation.get_resources().get_sliver() + + self.logger.error("Could not find the allocated Sliver - should not reach here!") + + def _assign_gateway_labels(self, *, ip_network: Union[IPv4Network, IPv6Network], subnet_list: List, + requested_ns_type: str) -> Labels: + """ + Assign gateway labels based on the requested network service type. + + :param ip_network: The IP network from which subnets are derived, either IPv4Network or IPv6Network. + :param subnet_list: A list of subnets derived from the ip_network. + :param requested_ns_type: The type of the requested network service. + :return: Gateway labels populated with the appropriate subnet and IP address. + """ + gateway_labels = Labels() + if requested_ns_type == ServiceType.FABNetv4: + gateway_labels.ipv4_subnet = subnet_list[0].with_prefixlen + gateway_labels.ipv4 = str(list(subnet_list[0].hosts())[0]) + + elif requested_ns_type == ServiceType.FABNetv4Ext: + gateway_labels.ipv4_subnet = ip_network.with_prefixlen + gateway_labels.ipv4 = str(subnet_list[0]) + + elif requested_ns_type in Constants.L3_FABNETv6_SERVICES: + gateway_labels.ipv6_subnet = subnet_list[0].with_prefixlen + gateway_labels.ipv6 = str(next(subnet_list[0].hosts())) + + self.logger.debug(f"Allocated Gateway Labels for Network Service: {gateway_labels}") + + return gateway_labels + + def _can_extend(self, *, subnet_list: List, requested_ns: NetworkServiceSliver): + if requested_ns.get_type() == ServiceType.FABNetv4: + allocated_subnet = ipaddress.IPv4Network(requested_ns.gateway.subnet) + if allocated_subnet not in subnet_list: + raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, + msg=f"Subnet {requested_ns.gateway.subnet} for Network Service : " + f"{requested_ns.get_type()} already in use by another reservation") + elif requested_ns.get_type() == ServiceType.FABNetv4Ext: + if requested_ns.gateway.gateway in subnet_list: + raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, + msg=f"Subnet {requested_ns.gateway.subnet} for Network Service : " + f"{requested_ns.get_type()} already in use by another reservation") + + elif requested_ns.get_type() in Constants.L3_FABNETv6_SERVICES: + allocated_subnet = ipaddress.IPv6Network(requested_ns.gateway.subnet) + if allocated_subnet not in subnet_list: + raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, + msg=f"Subnet {requested_ns.gateway.subnet} for Network Service : " + f"{requested_ns.get_type()} already in use by another reservation") + + ''' + def allocate(self, *, rid: ID, requested_ns: NetworkServiceSliver, owner_ns: NetworkServiceSliver, + existing_reservations: List[ABCReservationMixin]) -> NetworkServiceSliver: + """ + Allocate Network Service Sliver (Only for L3 Service) + - grab the /17 or /48 from BQM Site specific NetworkService + - divide it into /24 or /64 subnets + - exclude the 1st subnet (reserved for control plane) + - exclude the last subnet for V6 as the last subnet will be used for the FABRIC STAR Bastion Host Allocation + - exclude the subnets already assigned to other V4/V6 NetworkService on the same owner switch + - allocate the first available subnet to the NetworkService + :param rid: Reservation ID + :param requested_ns: Requested NetworkService + :param owner_ns: BQM Network Service identified to serve the NetworkService + :param existing_reservations: Existing Reservations which also are served by the owner switch :return NetworkService updated with the allocated subnet for FABNetv4 and FABNetv6 services Return the sliver updated with the subnet """ @@ -425,11 +639,12 @@ def allocate(self, *, rid: ID, requested_ns: NetworkServiceSliver, owner_ns: Net self.logger.error(traceback.format_exc()) raise BrokerException(msg=f"Allocation failure for Requested Network Service: {e}") return requested_ns + ''' def free(self, *, count: int, request: dict = None, resource: dict = None) -> dict: pass - def allocate_peered_ifs(self, *, owner_switch: NodeSliver, + def allocate_peered_ifs(self, *, rid: ID, owner_switch: NodeSliver, requested_ifs: InterfaceSliver, bqm_interface: InterfaceSliver, existing_reservations: List[ABCReservationMixin]) -> InterfaceSliver: """ @@ -452,9 +667,16 @@ def allocate_peered_ifs(self, *, owner_switch: NodeSliver, if bqm_interface.labels.vlan_range is not None: vlan_range = self.__extract_vlan_range(labels=bqm_interface.labels) - available_vlans = self.__exclude_allocated_vlans(available_vlan_range=vlan_range, bqm_ifs=bqm_interface, + available_vlans = self.__exclude_allocated_vlans(rid=rid, available_vlan_range=vlan_range, bqm_ifs=bqm_interface, existing_reservations=existing_reservations) + # Extend case + if requested_ifs.get_node_map() and requested_ifs.labels and requested_ifs.labels.vlan: + if int(requested_ifs.labels.vlan) in available_vlans: + raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, + msg=f"VLAN {requested_ifs.labels.vlan} for Interface : " + f"{requested_ifs.get_name()} already in use by another reservation") + vlan = str(random.choice(available_vlans)) #vlan = str(available_vlans[0]) ifs_labels = Labels.update(ifs_labels, vlan=vlan) diff --git a/fabric_cf/actor/db/psql_database.py b/fabric_cf/actor/db/psql_database.py index 2300dc03..11c1c122 100644 --- a/fabric_cf/actor/db/psql_database.py +++ b/fabric_cf/actor/db/psql_database.py @@ -885,7 +885,8 @@ def get_reservations(self, *, slice_id: str = None, graph_node_id: str = None, p return result def get_components(self, *, node_id: str, states: list[int], rsv_type: list[str], component: str = None, - bdf: str = None, start: datetime = None, end: datetime = None) -> Dict[str, List[str]]: + bdf: str = None, start: datetime = None, end: datetime = None, + excludes: List[str] = None) -> Dict[str, List[str]]: """ Returns components matching the search criteria @param node_id: Worker Node ID to which components belong @@ -895,6 +896,7 @@ def get_components(self, *, node_id: str, states: list[int], rsv_type: list[str] @param bdf: Component's PCI address @param start: start time @param end: end time + @param excludes: list of the reservations ids to exclude NOTE# For P4 switches; node_id=node+renc-p4-sw component=ip+192.168.11.8 bdf=p1 @@ -928,6 +930,10 @@ def get_components(self, *, node_id: str, states: list[int], rsv_type: list[str] .options(joinedload(Components.reservation)) ) + # Add excludes filter if excludes list is not None and not empty + if excludes: + rows = rows.filter(Reservations.rsv_resid.notin_(excludes)) + # Query Component records for reservations in the specified state and owner with the target string if component is not None and bdf is not None: rows = rows.filter(Components.component == component, Components.bdf == bdf) @@ -1876,3 +1882,8 @@ def test3(): test2() #test() #test3() + + logger = logging.getLogger('PsqlDatabase') + db = PsqlDatabase(user='fabric', password='fabric', database='orchestrator', db_host='127.0.0.1:5432', + logger=logger) + comps = db.get_components(node_id="HX7LQ53") \ No newline at end of file diff --git a/fabric_cf/orchestrator/test/test.yaml b/fabric_cf/orchestrator/test/test.yaml index c875f443..f8641d73 100644 --- a/fabric_cf/orchestrator/test/test.yaml +++ b/fabric_cf/orchestrator/test/test.yaml @@ -48,6 +48,7 @@ runtime: commit.batch.size: 1 enable.auto.commit: False consumer.poll.timeout: 250 + infrastructure.project.id: 4604cab7-41ff-4c1a-a935-0ca6f20cceeb logging: ## The directory in which actor should create log files. From 35751653f072fd0f12f96d8845cf1d8299aa8978 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 9 Jul 2024 13:04:22 -0400 Subject: [PATCH 081/133] remove excludes from get_existing_reservations --- fabric_cf/actor/core/policy/broker_simpler_units_policy.py | 7 ++----- pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 390f5a56..9fc29c60 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -1452,15 +1452,13 @@ def get_network_service_from_graph(self, *, node_id: str, self.lock.release() def get_existing_reservations(self, node_id: str, node_id_to_reservations: dict, - start: datetime = None, end: datetime = None, - excludes: List[str] = None) -> List[ABCReservationMixin]: + start: datetime = None, end: datetime = None) -> List[ABCReservationMixin]: """ Get existing reservations which are served by CBM node identified by node_id :param node_id: :param node_id_to_reservations: :param start :param end - :param excludes: :return: list of reservations """ states = [ReservationStates.Active.value, @@ -1472,8 +1470,7 @@ def get_existing_reservations(self, node_id: str, node_id_to_reservations: dict, existing_reservations = self.actor.get_plugin().get_database().get_reservations(graph_node_id=node_id, states=states, start=start, - end=end, - excludes=excludes) + end=end) reservations_allocated_in_cycle = node_id_to_reservations.get(node_id, None) diff --git a/pyproject.toml b/pyproject.toml index 056d31b3..b264cbc1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "PyYAML", "fabric_fss_utils==1.5.0", "fabric-message-bus==1.7.0b1", - "fabric-fim==1.7.0b13", + "fabric-fim==1.7.0b14", "fabric-credmgr-client==1.6.0", "ansible" ] From 41faf6d9ba55e534a0dc619f952c463084d1c4d4 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 9 Jul 2024 15:48:28 -0400 Subject: [PATCH 082/133] fix check for node map in update/add res --- .../actor/core/plugins/db/actor_database.py | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/fabric_cf/actor/core/plugins/db/actor_database.py b/fabric_cf/actor/core/plugins/db/actor_database.py index 6dbed30d..e70c7b8d 100644 --- a/fabric_cf/actor/core/plugins/db/actor_database.py +++ b/fabric_cf/actor/core/plugins/db/actor_database.py @@ -311,13 +311,14 @@ def add_reservation(self, *, reservation: ABCReservationMixin): if node_id: components = [] for c in sliver.attached_components_info.devices.values(): - bqm_id, comp_id = c.get_node_map() - if c.labels and c.labels.bdf: - bdf = c.labels.bdf - if isinstance(c.labels.bdf, str): - bdf = [c.labels.bdf] - for x in bdf: - components.append((node_id, comp_id, x)) + if c.get_node_map(): + bqm_id, comp_id = c.get_node_map() + if c.labels and c.labels.bdf: + bdf = c.labels.bdf + if isinstance(c.labels.bdf, str): + bdf = [c.labels.bdf] + for x in bdf: + components.append((node_id, comp_id, x)) term = reservation.get_term() @@ -377,13 +378,14 @@ def update_reservation(self, *, reservation: ABCReservationMixin): if node_id: components = [] for c in sliver.attached_components_info.devices.values(): - bqm_id, comp_id = c.get_node_map() - if c.labels and c.labels.bdf: - bdf = c.labels.bdf - if isinstance(c.labels.bdf, str): - bdf = [c.labels.bdf] - for x in bdf: - components.append((node_id, comp_id, x)) + if c.get_node_map(): + bqm_id, comp_id = c.get_node_map() + if c.labels and c.labels.bdf: + bdf = c.labels.bdf + if isinstance(c.labels.bdf, str): + bdf = [c.labels.bdf] + for x in bdf: + components.append((node_id, comp_id, x)) term = reservation.get_term() begin = time.time() From d28b776fa9b50f898b283194400fd192f532d04e Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 9 Jul 2024 16:19:50 -0400 Subject: [PATCH 083/133] add logs --- .../core/policy/network_service_inventory.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/fabric_cf/actor/core/policy/network_service_inventory.py b/fabric_cf/actor/core/policy/network_service_inventory.py index bff79cd2..bbb4426f 100644 --- a/fabric_cf/actor/core/policy/network_service_inventory.py +++ b/fabric_cf/actor/core/policy/network_service_inventory.py @@ -422,17 +422,30 @@ def _exclude_allocated_subnets(self, *, subnet_list: List, requested_ns_type: st continue if allocated_sliver.get_type() == ServiceType.FABNetv4: - subnet_to_remove = IPv4Network(allocated_sliver.get_gateway().lab.ipv4_subnet) + subnet_to_remove = IPv4Network(allocated_sliver.get_gateway().subnet) + self.logger.debug( + f"Excluding already allocated IP4Subnet: " + f"{allocated_sliver.get_gateway().subnet}" + f" to res# {reservation.get_reservation_id()}") subnet_list.remove(subnet_to_remove) elif allocated_sliver.get_type() == ServiceType.FABNetv4Ext: if allocated_sliver.labels is not None and allocated_sliver.labels.ipv4 is not None: for x in allocated_sliver.labels.ipv4: subnet_to_remove = ipaddress.IPv4Address(x) + self.logger.debug( + f"Excluding already allocated IP4: " + f"{x}" + f" to res# {reservation.get_reservation_id()}") subnet_list.remove(subnet_to_remove) elif allocated_sliver.get_type() in Constants.L3_FABNETv6_SERVICES: - subnet_to_remove = IPv6Network(allocated_sliver.get_gateway().lab.ipv6_subnet) + subnet_to_remove = IPv6Network(allocated_sliver.get_gateway().subnet) + self.logger.debug( + f"Excluding already allocated IP6Subnet: " + f"{allocated_sliver.get_gateway().subnet}" + f" to res# {reservation.get_reservation_id()}") + subnet_list.remove(subnet_to_remove) self.logger.debug(f"Excluding already allocated subnet for reservation {reservation.get_reservation_id()}") From a725ce8988760d8c06f38263b105546aef74bd3b Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 9 Jul 2024 23:33:55 -0400 Subject: [PATCH 084/133] update owner_ns_id for extend --- fabric_cf/actor/core/policy/broker_simpler_units_policy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 9fc29c60..04eb5af0 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -823,6 +823,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: bqm_graph_id, bqm_node_id = sliver.get_node_map() owner_ns, owner_switch = self.get_network_service_from_graph(node_id=bqm_node_id, parent=True) + owner_ns_id = owner_ns_id.node_id owner_mpls_ns = None if owner_switch: for ns in owner_switch.network_service_info.network_services.values(): From 9f4ba2b5626121c420d0a46aa3481b287f92cb1b Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 9 Jul 2024 23:45:16 -0400 Subject: [PATCH 085/133] update owner_ns_id for extend --- fabric_cf/actor/core/policy/broker_simpler_units_policy.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 04eb5af0..15d8b809 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -823,7 +823,11 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: bqm_graph_id, bqm_node_id = sliver.get_node_map() owner_ns, owner_switch = self.get_network_service_from_graph(node_id=bqm_node_id, parent=True) - owner_ns_id = owner_ns_id.node_id + # Hack for IPV6Ext services + owner_ns_id = owner_ns.node_id + if 'ipv6ext-ns' in owner_ns_id: + owner_ns_id = owner_ns_id.replace('ipv6ext-ns', 'ipv6-ns') + owner_mpls_ns = None if owner_switch: for ns in owner_switch.network_service_info.network_services.values(): From 4cb84973f77396344e51b0f6dc62467ea71b1dae Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 10 Jul 2024 00:01:33 -0400 Subject: [PATCH 086/133] disable add comp for NodeSliver --- fabric_cf/actor/core/plugins/db/actor_database.py | 4 ++++ fabric_cf/actor/core/policy/broker_simpler_units_policy.py | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/fabric_cf/actor/core/plugins/db/actor_database.py b/fabric_cf/actor/core/plugins/db/actor_database.py index e70c7b8d..7409e027 100644 --- a/fabric_cf/actor/core/plugins/db/actor_database.py +++ b/fabric_cf/actor/core/plugins/db/actor_database.py @@ -306,6 +306,7 @@ def add_reservation(self, *, reservation: ABCReservationMixin): bdf = ":".join(split_string[3:]) if len(split_string) > 3 else None if node_id and comp_id and bdf: components.append((node_id, comp_id, bdf)) + ''' elif isinstance(sliver, NodeSliver) and sliver.attached_components_info: node_id = reservation.get_graph_node_id() if node_id: @@ -319,6 +320,7 @@ def add_reservation(self, *, reservation: ABCReservationMixin): bdf = [c.labels.bdf] for x in bdf: components.append((node_id, comp_id, x)) + ''' term = reservation.get_term() @@ -373,6 +375,7 @@ def update_reservation(self, *, reservation: ABCReservationMixin): bdf = ":".join(split_string[3:]) if len(split_string) > 3 else None if node_id and comp_id and bdf: components.append((node_id, comp_id, bdf)) + ''' elif isinstance(sliver, NodeSliver) and sliver.attached_components_info: node_id = reservation.get_graph_node_id() if node_id: @@ -386,6 +389,7 @@ def update_reservation(self, *, reservation: ABCReservationMixin): bdf = [c.labels.bdf] for x in bdf: components.append((node_id, comp_id, x)) + ''' term = reservation.get_term() begin = time.time() diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 15d8b809..2e9bbccb 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -588,8 +588,7 @@ def __find_first_fit(self, node_id_list: List[str], node_id_to_reservations: dic end=term.get_end_time()) existing_components = self.get_existing_components(node_id=node_id, start=term.get_start_time(), - end=term.get_end_time(), - excludes=[str(reservation.get_reservation_id())]) + end=term.get_end_time()) delegation_id, sliver = inv.allocate(rid=reservation.get_reservation_id(), requested_sliver=sliver, From b7ad6da56c4f2d9006a05fa79fbad49b110b8239 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 10 Jul 2024 00:03:44 -0400 Subject: [PATCH 087/133] disable add comp for NodeSliver --- fabric_cf/actor/core/policy/broker_simpler_units_policy.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 2e9bbccb..bd193454 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -1513,8 +1513,10 @@ def get_existing_components(self, node_id: str, start: datetime = None, end: dat for x in ServiceType: res_type.append(str(x)) + ''' for x in NodeType: res_type.append(str(x)) + ''' # Only get Active or Ticketing reservations return self.actor.get_plugin().get_database().get_components(node_id=node_id, rsv_type=res_type, states=states, From 7090a6e8a5e04adfb5fc0d9b688cd9643d89498c Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 10 Jul 2024 13:10:15 -0400 Subject: [PATCH 088/133] check existing components --- .../actor/core/plugins/db/actor_database.py | 4 --- .../policy/broker_simpler_units_policy.py | 5 ++-- .../core/policy/network_node_inventory.py | 26 ++++++++++++------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/fabric_cf/actor/core/plugins/db/actor_database.py b/fabric_cf/actor/core/plugins/db/actor_database.py index 7409e027..e70c7b8d 100644 --- a/fabric_cf/actor/core/plugins/db/actor_database.py +++ b/fabric_cf/actor/core/plugins/db/actor_database.py @@ -306,7 +306,6 @@ def add_reservation(self, *, reservation: ABCReservationMixin): bdf = ":".join(split_string[3:]) if len(split_string) > 3 else None if node_id and comp_id and bdf: components.append((node_id, comp_id, bdf)) - ''' elif isinstance(sliver, NodeSliver) and sliver.attached_components_info: node_id = reservation.get_graph_node_id() if node_id: @@ -320,7 +319,6 @@ def add_reservation(self, *, reservation: ABCReservationMixin): bdf = [c.labels.bdf] for x in bdf: components.append((node_id, comp_id, x)) - ''' term = reservation.get_term() @@ -375,7 +373,6 @@ def update_reservation(self, *, reservation: ABCReservationMixin): bdf = ":".join(split_string[3:]) if len(split_string) > 3 else None if node_id and comp_id and bdf: components.append((node_id, comp_id, bdf)) - ''' elif isinstance(sliver, NodeSliver) and sliver.attached_components_info: node_id = reservation.get_graph_node_id() if node_id: @@ -389,7 +386,6 @@ def update_reservation(self, *, reservation: ABCReservationMixin): bdf = [c.labels.bdf] for x in bdf: components.append((node_id, comp_id, x)) - ''' term = reservation.get_term() begin = time.time() diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index bd193454..15d8b809 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -588,7 +588,8 @@ def __find_first_fit(self, node_id_list: List[str], node_id_to_reservations: dic end=term.get_end_time()) existing_components = self.get_existing_components(node_id=node_id, start=term.get_start_time(), - end=term.get_end_time()) + end=term.get_end_time(), + excludes=[str(reservation.get_reservation_id())]) delegation_id, sliver = inv.allocate(rid=reservation.get_reservation_id(), requested_sliver=sliver, @@ -1513,10 +1514,8 @@ def get_existing_components(self, node_id: str, start: datetime = None, end: dat for x in ServiceType: res_type.append(str(x)) - ''' for x in NodeType: res_type.append(str(x)) - ''' # Only get Active or Ticketing reservations return self.actor.get_plugin().get_database().get_components(node_id=node_id, rsv_type=res_type, states=states, diff --git a/fabric_cf/actor/core/policy/network_node_inventory.py b/fabric_cf/actor/core/policy/network_node_inventory.py index e1a6f887..41bd5243 100644 --- a/fabric_cf/actor/core/policy/network_node_inventory.py +++ b/fabric_cf/actor/core/policy/network_node_inventory.py @@ -435,16 +435,22 @@ def __check_components(self, *, rid: ID, requested_components: AttachedComponent for name, requested_component in requested_components.devices.items(): if not is_create and requested_component.get_node_map() is not None: bqm_id, node_id = requested_component.get_node_map() - allocated_bdfs = existing_components.get(node_id) - if allocated_bdfs and requested_component.labels and requested_component.labels.bdf: - bdfs = requested_component.labels.bdf - if isinstance(requested_component.labels.bdf, str): - bdfs = [requested_component.labels.bdf] - for x in bdfs: - if x in allocated_bdfs: - raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, - msg=f"Renew failed: Component of type: {requested_component.get_model()} " - f"not available in graph node: {graph_node.node_id}") + if requested_component.get_type() == ComponentType.SharedNIC: + allocated_bdfs = existing_components.get(node_id) + if allocated_bdfs and requested_component.labels and requested_component.labels.bdf: + bdfs = requested_component.labels.bdf + if isinstance(requested_component.labels.bdf, str): + bdfs = [requested_component.labels.bdf] + for x in bdfs: + if x in allocated_bdfs: + raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, + msg=f"Renew failed: Component of type: {requested_component.get_model()} " + f"not available in graph node: {graph_node.node_id}") + else: + if node_id in existing_components.keys(): + raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, + msg=f"Renew failed: Component of type: {requested_component.get_model()} " + f"not available in graph node: {graph_node.node_id}") self.logger.debug(f"==========Ignoring Allocated component: {requested_component} for modify") continue From 7e444aa7333d6a5505602094a401df4537b9e064 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 10 Jul 2024 17:27:10 -0400 Subject: [PATCH 089/133] separate handling for extend and modify --- .../actor/core/kernel/reservation_states.py | 13 ++++++ .../policy/broker_simpler_units_policy.py | 30 ++++++++----- fabric_cf/actor/core/policy/inventory.py | 1 + .../core/policy/network_node_inventory.py | 42 +++++++++++-------- 4 files changed, 58 insertions(+), 28 deletions(-) diff --git a/fabric_cf/actor/core/kernel/reservation_states.py b/fabric_cf/actor/core/kernel/reservation_states.py index 5d471fb5..0e4b773b 100644 --- a/fabric_cf/actor/core/kernel/reservation_states.py +++ b/fabric_cf/actor/core/kernel/reservation_states.py @@ -23,9 +23,22 @@ # # # Author: Komal Thareja (kthare10@renci.org) +import enum from enum import Enum +class ReservationOperation(enum.Enum): + Create = enum.auto(), + Modify = enum.auto(), + Extend = enum.auto() + + def __repr__(self): + return self.name + + def __str__(self): + return self.name + + class ReservationStates(Enum): """ Reservation states diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 15d8b809..423c670f 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -52,7 +52,7 @@ from fabric_cf.actor.core.container.maintenance import Maintenance from fabric_cf.actor.core.delegation.resource_ticket import ResourceTicketFactory from fabric_cf.actor.core.common.exceptions import BrokerException, ExceptionErrorCode -from fabric_cf.actor.core.kernel.reservation_states import ReservationStates +from fabric_cf.actor.core.kernel.reservation_states import ReservationStates, ReservationOperation from fabric_cf.actor.core.policy.broker_calendar_policy import BrokerCalendarPolicy from fabric_cf.actor.core.policy.fifo_queue import FIFOQueue from fabric_cf.actor.core.policy.network_node_inventory import NetworkNodeInventory @@ -477,7 +477,8 @@ def ticket(self, *, reservation: ABCBrokerReservation, node_id_to_reservations: self.logger.debug(f"Inventory type: {type(inv)}") term = Term(start=start, end=end) return self.ticket_inventory(reservation=reservation, inv=inv, term=term, - node_id_to_reservations=node_id_to_reservations) + node_id_to_reservations=node_id_to_reservations, + operation=ReservationOperation.Create) else: reservation.fail(message=Constants.NO_POOL) else: @@ -557,7 +558,8 @@ def __prune_nodes_in_maintenance(self, node_id_list: List[str], site: str, reser return node_id_list def __find_first_fit(self, node_id_list: List[str], node_id_to_reservations: dict, inv: NetworkNodeInventory, - reservation: ABCBrokerReservation, term: Term, sliver: NodeSliver) -> Tuple[str, BaseSliver, Any]: + reservation: ABCBrokerReservation, term: Term, sliver: NodeSliver, + operation: ReservationOperation = ReservationOperation.Create) -> Tuple[str, BaseSliver, Any]: """ Find First Available Node which can serve the reservation @param node_id_list: Candidate Nodes @@ -569,7 +571,6 @@ def __find_first_fit(self, node_id_list: List[str], node_id_to_reservations: dic delegation_id = None error_msg = None self.logger.debug(f"Possible candidates to serve {reservation} candidates# {node_id_list}") - is_create = sliver.get_node_map() is None for node_id in node_id_list: try: self.logger.debug(f"Attempting to allocate {reservation} via graph_node# {node_id}") @@ -597,7 +598,7 @@ def __find_first_fit(self, node_id_list: List[str], node_id_to_reservations: dic graph_node=graph_node, existing_reservations=existing_reservations, existing_components=existing_components, - is_create=is_create) + operation=operation) if delegation_id is not None and sliver is not None: break @@ -616,7 +617,8 @@ def __find_first_fit(self, node_id_list: List[str], node_id_to_reservations: dic return delegation_id, sliver, error_msg def __allocate_nodes(self, *, reservation: ABCBrokerReservation, inv: NetworkNodeInventory, sliver: NodeSliver, - node_id_to_reservations: dict, term: Term) -> Tuple[str or None, BaseSliver, Any]: + node_id_to_reservations: dict, term: Term, + operation: ReservationOperation = ReservationOperation.Create) -> Tuple[str or None, BaseSliver, Any]: """ Allocate Network Node Slivers @param reservation Reservation @@ -647,7 +649,8 @@ def __allocate_nodes(self, *, reservation: ABCBrokerReservation, inv: NetworkNod return self.__find_first_fit(node_id_list=node_id_list, node_id_to_reservations=node_id_to_reservations, - inv=inv, reservation=reservation, term=term, sliver=sliver) + inv=inv, reservation=reservation, term=term, sliver=sliver, + operation=operation) def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: NetworkServiceSliver, node_id_to_reservations: dict, term: Term) -> Tuple[str, BaseSliver, Any]: @@ -962,10 +965,11 @@ def __allocate_peered_interfaces(self, *, rid: ID, peered_interfaces: List[Inter sliver.set_node_map(node_map=(self.combined_broker_model_graph_id, owner_ns.node_id)) def ticket_inventory(self, *, reservation: ABCBrokerReservation, inv: InventoryForType, term: Term, - node_id_to_reservations: dict, extend: bool = False) -> Tuple[bool, dict, Any]: + node_id_to_reservations: dict, + operation: ReservationOperation = ReservationOperation.Create) -> Tuple[bool, dict, Any]: error_msg = None try: - if extend: + if operation == ReservationOperation.Extend: rset = reservation.get_resources() else: rset = reservation.get_requested_resources() @@ -984,7 +988,8 @@ def ticket_inventory(self, *, reservation: ABCBrokerReservation, inv: InventoryF delegation_id, sliver, error_msg = self.__allocate_nodes(reservation=reservation, inv=inv, sliver=res_sliver, node_id_to_reservations=node_id_to_reservations, - term=term) + term=term, + operation=operation) elif isinstance(res_sliver, NetworkServiceSliver): delegation_id, sliver, error_msg = self.__allocate_services(rid=reservation.get_reservation_id(), @@ -1038,12 +1043,15 @@ def extend_private(self, *, reservation: ABCBrokerReservation, inv: InventoryFor sliver = current_resources.get_sliver() diff = sliver.diff(other_sliver=requested_resources.get_sliver()) + operation = ReservationOperation.Extend if diff is not None: sliver = requested_resources.get_sliver() + operation = ReservationOperation.Modify #if diff is None or diff.added is None or \ # (len(diff.added.components) == 0 and len(diff.added.interfaces) == 0) or \ # self.__is_modify_on_openstack_vnic(sliver=sliver): + if self.__is_modify_on_openstack_vnic(sliver=sliver): self.issue_ticket(reservation=reservation, units=needed, rtype=requested_resources.get_type(), term=term, source=reservation.get_source(), sliver=sliver) @@ -1051,7 +1059,7 @@ def extend_private(self, *, reservation: ABCBrokerReservation, inv: InventoryFor status, node_id_to_reservations, error_msg = self.ticket_inventory(reservation=reservation, inv=inv, term=term, node_id_to_reservations=node_id_to_reservations, - extend=True) + operation=operation) if not status and not reservation.is_failed(): fail_message = f"Insufficient resources for specified start time, Failing reservation: " \ f"{reservation.get_reservation_id()}" diff --git a/fabric_cf/actor/core/policy/inventory.py b/fabric_cf/actor/core/policy/inventory.py index 7ed0f285..f5f9621d 100644 --- a/fabric_cf/actor/core/policy/inventory.py +++ b/fabric_cf/actor/core/policy/inventory.py @@ -24,6 +24,7 @@ # # Author: Komal Thareja (kthare10@renci.org) from __future__ import annotations + from typing import TYPE_CHECKING from fabric_cf.actor.core.common.constants import Constants diff --git a/fabric_cf/actor/core/policy/network_node_inventory.py b/fabric_cf/actor/core/policy/network_node_inventory.py index 41bd5243..235e8b68 100644 --- a/fabric_cf/actor/core/policy/network_node_inventory.py +++ b/fabric_cf/actor/core/policy/network_node_inventory.py @@ -37,6 +37,7 @@ from fabric_cf.actor.core.apis.abc_reservation_mixin import ABCReservationMixin from fabric_cf.actor.core.common.constants import Constants from fabric_cf.actor.core.common.exceptions import BrokerException, ExceptionErrorCode +from fabric_cf.actor.core.kernel.reservation_states import ReservationOperation from fabric_cf.actor.core.policy.inventory_for_type import InventoryForType from fabric_cf.actor.core.util.id import ID @@ -230,13 +231,13 @@ def __update_smart_nic_labels_and_capacities(self, *, available_component: Compo def __check_component_labels_and_capacities(self, *, available_component: ComponentSliver, graph_id: str, requested_component: ComponentSliver, - is_create: bool = False) -> ComponentSliver: + operation: ReservationOperation = ReservationOperation.Create) -> ComponentSliver: """ Check if available component capacities, labels to match requested component :param available_component: available component :param graph_id: BQM graph id :param requested_component: requested component - :param is_create: is_create + :param operation: operation :return: requested component annotated with properties in case of success, None otherwise """ if requested_component.get_model() is not None and \ @@ -271,7 +272,7 @@ def __check_component_labels_and_capacities(self, *, available_component: Compon node_map = tuple([graph_id, available_component.node_id]) requested_component.set_node_map(node_map=node_map) - if requested_component.labels is None or is_create: + if requested_component.labels is None or operation == ReservationOperation.Create: requested_component.labels = Labels.update(lab=requested_component.get_label_allocations()) return requested_component @@ -391,7 +392,7 @@ def __exclude_components_for_existing_reservations(self, *, rid: ID, graph_node: def __check_components(self, *, rid: ID, requested_components: AttachedComponentsInfo, graph_id: str, graph_node: NodeSliver, existing_reservations: List[ABCReservationMixin], existing_components: Dict[str, List[str]], - is_create: bool = False) -> AttachedComponentsInfo: + operation: ReservationOperation = ReservationOperation.Create) -> AttachedComponentsInfo: """ Check if the requested capacities can be satisfied with the available capacities :param rid: reservation id of the reservation being served @@ -399,10 +400,12 @@ def __check_components(self, *, rid: ID, requested_components: AttachedComponent :param graph_id: BQM graph id :param graph_node: BQM graph node identified to serve the reservation :param existing_reservations: Existing Reservations served by the same BQM node - :param is_create: Flag indicating if this is create or modify + :param operation: Flag indicating if this is create or modify :return: Components updated with the corresponding BQM node ids :raises: BrokerException in case the request cannot be satisfied """ + self.logger.debug(f"available_components after excluding allocated components: {graph_node.attached_components_info.devices.keys()}") + self.__exclude_components_for_existing_reservations(rid=rid, graph_node=graph_node, existing_reservations=existing_reservations) @@ -433,7 +436,11 @@ def __check_components(self, *, rid: ID, requested_components: AttachedComponent self.logger.debug(f"requested_components: {requested_components.devices.values()} for reservation# {rid}") for name, requested_component in requested_components.devices.items(): - if not is_create and requested_component.get_node_map() is not None: + if operation == ReservationOperation.Modify and requested_component.get_node_map() is not None: + self.logger.debug(f"==========Ignoring Allocated component: {requested_component} for modify") + continue + + if operation == ReservationOperation.Extend and requested_component.get_node_map() is not None: bqm_id, node_id = requested_component.get_node_map() if requested_component.get_type() == ComponentType.SharedNIC: allocated_bdfs = existing_components.get(node_id) @@ -452,8 +459,9 @@ def __check_components(self, *, rid: ID, requested_components: AttachedComponent msg=f"Renew failed: Component of type: {requested_component.get_model()} " f"not available in graph node: {graph_node.node_id}") - self.logger.debug(f"==========Ignoring Allocated component: {requested_component} for modify") + self.logger.debug(f"==========Ignoring Allocated component: {requested_component} for renew") continue + self.logger.debug(f"==========Allocating component: {requested_component}") resource_type = requested_component.get_type() resource_model = requested_component.get_model() @@ -475,7 +483,7 @@ def __check_components(self, *, rid: ID, requested_components: AttachedComponent requested_component = self.__check_component_labels_and_capacities( available_component=component, graph_id=graph_id, requested_component=requested_component, - is_create=is_create) + operation=operation) if requested_component.get_node_map() is not None: self.logger.info(f"Assigning {component.node_id} to component# " @@ -495,7 +503,7 @@ def __check_components(self, *, rid: ID, requested_components: AttachedComponent def __allocate_p4_switch(self, *, rid: ID, requested_sliver: NodeSliver, graph_id: str, graph_node: NodeSliver, existing_reservations: List[ABCReservationMixin], existing_components: Dict[str, List[str]], - is_create: bool = False) -> Tuple[str, BaseSliver]: + operation: ReservationOperation = ReservationOperation.Create) -> Tuple[str, BaseSliver]: """ Allocate an extending or ticketing reservation for a P4 switch @@ -505,14 +513,14 @@ def __allocate_p4_switch(self, *, rid: ID, requested_sliver: NodeSliver, graph_i :param graph_node: BQM graph node identified to serve the reservation :param existing_components: Existing Components :param existing_reservations: Existing Reservations served by the same BQM node - :param is_create: Indicates if this is create or modify + :param operation: Indicates if this is create or modify :return: Tuple of Delegation Id and the Requested Sliver annotated with BQM Node Id and other properties :raises: BrokerException in case the request cannot be satisfied """ delegation_id = None - if not is_create: + if operation == ReservationOperation.Create: # In case of modify, directly get delegation_id if len(graph_node.get_capacity_delegations().get_delegation_ids()) > 0: delegation_id = next(iter(graph_node.get_capacity_delegations().get_delegation_ids())) @@ -546,7 +554,7 @@ def __allocate_p4_switch(self, *, rid: ID, requested_sliver: NodeSliver, graph_i def allocate(self, *, rid: ID, requested_sliver: BaseSliver, graph_id: str, graph_node: BaseSliver, existing_reservations: List[ABCReservationMixin], existing_components: Dict[str, List[str]], - is_create: bool = False) -> Tuple[str, BaseSliver]: + operation: ReservationOperation = ReservationOperation.Create) -> Tuple[str, BaseSliver]: """ Allocate an extending or ticketing reservation :param rid: reservation id of the reservation to be allocated @@ -555,7 +563,7 @@ def allocate(self, *, rid: ID, requested_sliver: BaseSliver, graph_id: str, grap :param graph_node: BQM graph node identified to serve the reservation :param existing_components: Existing Components :param existing_reservations: Existing Reservations served by the same BQM node - :param is_create: Indicates if this is create or modify + :param operation: Indicates if this is create or modify :return: Tuple of Delegation Id and the Requested Sliver annotated with BQM Node Id and other properties :raises: BrokerException in case the request cannot be satisfied """ @@ -578,12 +586,12 @@ def allocate(self, *, rid: ID, requested_sliver: BaseSliver, graph_id: str, grap if requested_sliver.get_type() == NodeType.Switch: return self.__allocate_p4_switch(rid=rid, requested_sliver=requested_sliver, graph_id=graph_id, graph_node=graph_node, existing_reservations=existing_reservations, - existing_components=existing_components, is_create=is_create) + existing_components=existing_components, operation=operation) delegation_id = None requested_capacities = None # For create, we need to allocate the VM - if is_create: + if operation == ReservationOperation.Create: # Always use requested capacities to be mapped from flavor i.e. capacity hints requested_capacity_hints = requested_sliver.get_capacity_hints() catalog = InstanceCatalog() @@ -609,10 +617,10 @@ def allocate(self, *, rid: ID, requested_sliver: BaseSliver, graph_id: str, grap graph_node=graph_node, existing_reservations=existing_reservations, existing_components=existing_components, - is_create=is_create) + operation=operation) # Do this only for create - if is_create: + if operation == ReservationOperation.Create: requested_sliver.capacity_allocations = Capacities() requested_sliver.capacity_allocations = Capacities.update(lab=requested_capacities) requested_sliver.label_allocations = Labels(instance_parent=graph_node.get_name()) From 0cf6c752b83ec4cd11795935f3d19762d598a750 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 10 Jul 2024 22:59:12 -0400 Subject: [PATCH 090/133] make renew asynchronous --- fabric_cf/actor/core/apis/abc_mgmt_actor.py | 1 - fabric_cf/actor/core/kernel/kernel.py | 6 +++++- fabric_cf/actor/core/kernel/slice_state_machine.py | 7 +++++++ .../client_actor_management_object_helper.py | 5 ++++- fabric_cf/actor/core/manage/kafka/kafka_actor.py | 1 - .../orchestrator/core/orchestrator_handler.py | 14 ++++++++++++++ 6 files changed, 30 insertions(+), 4 deletions(-) diff --git a/fabric_cf/actor/core/apis/abc_mgmt_actor.py b/fabric_cf/actor/core/apis/abc_mgmt_actor.py index 33b7012b..8a0739d9 100644 --- a/fabric_cf/actor/core/apis/abc_mgmt_actor.py +++ b/fabric_cf/actor/core/apis/abc_mgmt_actor.py @@ -29,7 +29,6 @@ from typing import TYPE_CHECKING, List, Tuple, Dict from fabric_mb.message_bus.messages.delegation_avro import DelegationAvro -from fabric_mb.message_bus.messages.poa_avro import PoaAvro from fabric_mb.message_bus.messages.poa_info_avro import PoaInfoAvro from fabric_mb.message_bus.messages.site_avro import SiteAvro diff --git a/fabric_cf/actor/core/kernel/kernel.py b/fabric_cf/actor/core/kernel/kernel.py index 8cd184e2..ec2f1b44 100644 --- a/fabric_cf/actor/core/kernel/kernel.py +++ b/fabric_cf/actor/core/kernel/kernel.py @@ -891,7 +891,11 @@ def modify_slice(self, *, slice_object: ABCSlice): if not real.is_dead_or_closing(): real.set_config_properties(value=slice_object.get_config_properties()) # Transition slice to Configuring state - real.transition_slice(operation=SliceStateMachine.MODIFY) + if slice_object.get_state() == SliceState.Modifying: + operation = SliceStateMachine.MODIFY + else: + operation = SliceStateMachine.RENEW + real.transition_slice(operation=operation) real.set_graph_id(graph_id=slice_object.get_graph_id()) real.set_dirty() self.plugin.get_database().update_slice(slice_object=real) diff --git a/fabric_cf/actor/core/kernel/slice_state_machine.py b/fabric_cf/actor/core/kernel/slice_state_machine.py index de19811a..3ab585c9 100644 --- a/fabric_cf/actor/core/kernel/slice_state_machine.py +++ b/fabric_cf/actor/core/kernel/slice_state_machine.py @@ -139,6 +139,7 @@ def is_modified(*, state) -> bool: class SliceCommand(Enum): Create = enum.auto() Modify = enum.auto() + Renew = enum.auto() Delete = enum.auto() Reevaluate = enum.auto() ModifyAccept = enum.auto() @@ -198,6 +199,9 @@ class SliceStateMachine: MODIFY = SliceOperation(SliceCommand.Modify, SliceState.StableOK, SliceState.StableError, SliceState.Configuring, SliceState.AllocatedOK, SliceState.AllocatedError) + RENEW = SliceOperation(SliceCommand.Renew, SliceState.StableOK, SliceState.StableError, SliceState.Configuring, + SliceState.AllocatedOK, SliceState.AllocatedError) + MODIFY_ACCEPT = SliceOperation(SliceCommand.ModifyAccept, SliceState.ModifyOK, SliceState.ModifyError, SliceState.Modifying, SliceState.AllocatedOK, SliceState.AllocatedError) @@ -247,6 +251,9 @@ def transition_slice(self, *, operation: SliceOperation, reservations: Reservati if operation.command == SliceCommand.Create: self.state = SliceState.Configuring + elif operation.command == SliceCommand.Renew: + self.state = SliceState.Configuring + elif operation.command == SliceCommand.Modify: self.state = SliceState.Modifying diff --git a/fabric_cf/actor/core/manage/client_actor_management_object_helper.py b/fabric_cf/actor/core/manage/client_actor_management_object_helper.py index c40b327c..662b5608 100644 --- a/fabric_cf/actor/core/manage/client_actor_management_object_helper.py +++ b/fabric_cf/actor/core/manage/client_actor_management_object_helper.py @@ -462,13 +462,16 @@ def run(self): dependencies=redeem_dep_res_list) return result - + ''' # Process Extend for Renew synchronously if new_end_time is not None: result = self.client.execute_on_actor_thread_and_wait(runnable=Runner(actor=self.client)) # Process Extend for Modify asynchronously else: self.client.execute_on_actor_thread(runnable=Runner(actor=self.client)) + ''' + # Always Process Extend asynchronously + self.client.execute_on_actor_thread(runnable=Runner(actor=self.client)) except Exception as e: self.logger.error("extend_reservation {}".format(e)) diff --git a/fabric_cf/actor/core/manage/kafka/kafka_actor.py b/fabric_cf/actor/core/manage/kafka/kafka_actor.py index 54119ded..cd605cc6 100644 --- a/fabric_cf/actor/core/manage/kafka/kafka_actor.py +++ b/fabric_cf/actor/core/manage/kafka/kafka_actor.py @@ -33,7 +33,6 @@ from fabric_mb.message_bus.messages.get_delegations_avro import GetDelegationsAvro from fabric_mb.message_bus.messages.get_sites_request_avro import GetSitesRequestAvro from fabric_mb.message_bus.messages.maintenance_request_avro import MaintenanceRequestAvro -from fabric_mb.message_bus.messages.poa_avro import PoaAvro from fabric_mb.message_bus.messages.poa_info_avro import PoaInfoAvro from fabric_mb.message_bus.messages.remove_delegation_avro import RemoveDelegationAvro from fabric_mb.message_bus.messages.reservation_state_avro import ReservationStateAvro diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index 5997803c..a700d305 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -514,6 +514,7 @@ def modify_slice(self, *, token: str, slice_id: str, slice_graph: str) -> List[d config_props[Constants.TAGS] = ','.join(tags) config_props[Constants.TOKEN_HASH] = fabric_token.token_hash slice_obj.set_config_properties(value=config_props) + slice_object.state = SliceState.Modifying if not controller.update_slice(slice_obj=slice_obj, modify_state=modify_state): self.logger.error(f"Failed to update slice: {slice_id} error: {controller.get_last_error()}") @@ -711,6 +712,7 @@ def renew_slice(self, *, token: str, slice_id: str, new_lease_end_time: datetime :return: """ failed_to_extend_rid_list = [] + extend_rid_list = [] try: controller = self.controller_state.get_management_actor() self.logger.debug(f"renew_slice invoked for Controller: {controller}") @@ -765,6 +767,9 @@ def renew_slice(self, *, token: str, slice_id: str, new_lease_end_time: datetime if new_end_time < current_end_time: raise OrchestratorException(f"Attempted new term end time is shorter than current slice end time") + if new_end_time == current_end_time: + continue + self.logger.debug(f"Extending reservation with reservation# {r.get_reservation_id()}") result = controller.extend_reservation(reservation=ID(uid=r.get_reservation_id()), new_end_time=new_end_time, @@ -772,16 +777,25 @@ def renew_slice(self, *, token: str, slice_id: str, new_lease_end_time: datetime if not result: self.logger.error(f"Error: {controller.get_last_error()}") failed_to_extend_rid_list.append(r.get_reservation_id()) + else: + extend_rid_list.append(r.get_reservation_id()) + ''' if len(failed_to_extend_rid_list) == 0: slice_object.set_lease_end(lease_end=new_end_time) if not controller.update_slice(slice_obj=slice_object): self.logger.error(f"Failed to update lease end time: {new_end_time} in Slice: {slice_object}") self.logger.error(controller.get_last_error()) + ''' if len(failed_to_extend_rid_list) > 0: raise OrchestratorException(f"Failed to extend reservation# {failed_to_extend_rid_list}") + if len(extend_rid_list): + slice_object.state = SliceState.Configuring + if not controller.update_slice(slice_obj=slice_object, modify_state=True): + self.logger.error(f"Failed to update slice: {slice_id} error: {controller.get_last_error()}") + EventLoggerSingleton.get().log_slice_event(slice_object=slice_object, action=ActionId.renew) except Exception as e: self.logger.error(traceback.format_exc()) From 31e439411b266992d60444c47315b2de70abc522 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 10 Jul 2024 23:01:11 -0400 Subject: [PATCH 091/133] make renew asynchronous --- fabric_cf/actor/core/kernel/slice_state_machine.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fabric_cf/actor/core/kernel/slice_state_machine.py b/fabric_cf/actor/core/kernel/slice_state_machine.py index 3ab585c9..fa2d7e70 100644 --- a/fabric_cf/actor/core/kernel/slice_state_machine.py +++ b/fabric_cf/actor/core/kernel/slice_state_machine.py @@ -199,8 +199,8 @@ class SliceStateMachine: MODIFY = SliceOperation(SliceCommand.Modify, SliceState.StableOK, SliceState.StableError, SliceState.Configuring, SliceState.AllocatedOK, SliceState.AllocatedError) - RENEW = SliceOperation(SliceCommand.Renew, SliceState.StableOK, SliceState.StableError, SliceState.Configuring, - SliceState.AllocatedOK, SliceState.AllocatedError) + RENEW = SliceOperation(SliceCommand.Renew, SliceState.StableOK, SliceState.StableError, SliceState.AllocatedOK, + SliceState.ModifyOK, SliceState.ModifyError, SliceState.AllocatedError) MODIFY_ACCEPT = SliceOperation(SliceCommand.ModifyAccept, SliceState.ModifyOK, SliceState.ModifyError, SliceState.Modifying, SliceState.AllocatedOK, SliceState.AllocatedError) From c58c2bb5c124e7250eefaf60e710dfbeb9dad534 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 10 Jul 2024 23:19:43 -0400 Subject: [PATCH 092/133] fix state transition --- fabric_cf/actor/core/apis/abc_actor_mixin.py | 4 +++- fabric_cf/actor/core/core/actor.py | 4 ++-- fabric_cf/actor/core/kernel/kernel.py | 5 +++-- fabric_cf/actor/core/kernel/kernel_wrapper.py | 6 ++++-- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/fabric_cf/actor/core/apis/abc_actor_mixin.py b/fabric_cf/actor/core/apis/abc_actor_mixin.py index f6183c64..68453c72 100644 --- a/fabric_cf/actor/core/apis/abc_actor_mixin.py +++ b/fabric_cf/actor/core/apis/abc_actor_mixin.py @@ -38,6 +38,7 @@ from fabric_cf.actor.core.apis.abc_tick import ABCTick if TYPE_CHECKING: + from fabric_cf.actor.core.kernel.slice_state_machine import SliceState from fabric_cf.actor.core.apis.abc_actor_event import ABCActorEvent from fabric_cf.actor.core.apis.abc_actor_proxy import ABCActorProxy from fabric_cf.actor.core.apis.abc_base_plugin import ABCBasePlugin @@ -431,12 +432,13 @@ def register_slice(self, *, slice_object: ABCSlice): """ @abstractmethod - def modify_slice(self, *, slice_object: ABCSlice): + def modify_slice(self, *, slice_object: ABCSlice, new_state: SliceState): """ Modify the slice registered with the actor. Moves the slice into Modifying State Args: slice_object: slice_object + new_state: new_state Raises: Exception in case of error """ diff --git a/fabric_cf/actor/core/core/actor.py b/fabric_cf/actor/core/core/actor.py index 414f9d26..95a79702 100644 --- a/fabric_cf/actor/core/core/actor.py +++ b/fabric_cf/actor/core/core/actor.py @@ -658,8 +658,8 @@ def register(self, *, reservation: ABCReservationMixin): def register_slice(self, *, slice_object: ABCSlice): self.wrapper.register_slice(slice_object=slice_object) - def modify_slice(self, *, slice_object: ABCSlice): - self.wrapper.modify_slice(slice_object=slice_object) + def modify_slice(self, *, slice_object: ABCSlice, new_state: SliceState): + self.wrapper.modify_slice(slice_object=slice_object, new_state=new_state) def delete_slice(self, *, slice_id: ID): self.wrapper.delete_slice(slice_id=slice_id) diff --git a/fabric_cf/actor/core/kernel/kernel.py b/fabric_cf/actor/core/kernel/kernel.py index ec2f1b44..4d15883e 100644 --- a/fabric_cf/actor/core/kernel/kernel.py +++ b/fabric_cf/actor/core/kernel/kernel.py @@ -872,10 +872,11 @@ def register_reservation(self, *, reservation: ABCReservationMixin): finally: reservation.unlock() - def modify_slice(self, *, slice_object: ABCSlice): + def modify_slice(self, *, slice_object: ABCSlice, new_state: SliceState): """ Modify the specified slice with the kernel. @param slice_object slice_object + @param new_state new_state @throws Exception in case of failure """ if slice_object is None: @@ -891,7 +892,7 @@ def modify_slice(self, *, slice_object: ABCSlice): if not real.is_dead_or_closing(): real.set_config_properties(value=slice_object.get_config_properties()) # Transition slice to Configuring state - if slice_object.get_state() == SliceState.Modifying: + if new_state == SliceState.Modifying: operation = SliceStateMachine.MODIFY else: operation = SliceStateMachine.RENEW diff --git a/fabric_cf/actor/core/kernel/kernel_wrapper.py b/fabric_cf/actor/core/kernel/kernel_wrapper.py index 86c8166b..ca01f761 100644 --- a/fabric_cf/actor/core/kernel/kernel_wrapper.py +++ b/fabric_cf/actor/core/kernel/kernel_wrapper.py @@ -28,6 +28,7 @@ from datetime import datetime from typing import List, Dict +from fabric_cf.actor.core.kernel.slice_state_machine import SliceState from fabric_mb.message_bus.messages.poa_info_avro import PoaInfoAvro from fim.slivers.base_sliver import BaseSliver @@ -769,16 +770,17 @@ def register_delegation(self, *, delegation: ABCDelegation): self.kernel.register_delegation(delegation=delegation) - def modify_slice(self, *, slice_object: ABCSlice): + def modify_slice(self, *, slice_object: ABCSlice, new_state: SliceState): """ Modify the slice registered with the kernel @param slice_object slice_object + @param new_state new_state @throws Exception in case of error """ if slice_object is None or slice_object.get_slice_id() is None or not isinstance(slice_object, ABCSlice): raise KernelException("Invalid argument {}".format(slice_object)) - self.kernel.modify_slice(slice_object=slice_object) + self.kernel.modify_slice(slice_object=slice_object, new_state=new_state) def delete_slice(self, *, slice_id: ID): """ From 2c31c26e37b2a66e96db00d57d8705233e5f6fe7 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 10 Jul 2024 23:46:39 -0400 Subject: [PATCH 093/133] fix state transition --- fabric_cf/actor/core/manage/actor_management_object.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fabric_cf/actor/core/manage/actor_management_object.py b/fabric_cf/actor/core/manage/actor_management_object.py index f6e208c4..95b59526 100644 --- a/fabric_cf/actor/core/manage/actor_management_object.py +++ b/fabric_cf/actor/core/manage/actor_management_object.py @@ -347,7 +347,8 @@ def run(self): try: if modify_state: slice_object = Translate.translate_slice(slice_avro=slice_mng) - self.actor.modify_slice(slice_object=slice_object) + from fabric_cf.actor.core.kernel.slice_state_machine import SliceState + self.actor.modify_slice(slice_object=slice_object, new_state=SliceState(slice_mng.get_state())) else: slice_obj = self.actor.get_slice(slice_id=slice_id) if slice_obj is None: From 5e55545a7ef3c6aaf5f55c379d680001f786d750 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 11 Jul 2024 00:06:58 -0400 Subject: [PATCH 094/133] fix state value --- fabric_cf/orchestrator/core/orchestrator_handler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index a700d305..1e7e13ff 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -514,7 +514,7 @@ def modify_slice(self, *, token: str, slice_id: str, slice_graph: str) -> List[d config_props[Constants.TAGS] = ','.join(tags) config_props[Constants.TOKEN_HASH] = fabric_token.token_hash slice_obj.set_config_properties(value=config_props) - slice_object.state = SliceState.Modifying + slice_object.state = SliceState.Modifying.value if not controller.update_slice(slice_obj=slice_obj, modify_state=modify_state): self.logger.error(f"Failed to update slice: {slice_id} error: {controller.get_last_error()}") @@ -792,7 +792,7 @@ def renew_slice(self, *, token: str, slice_id: str, new_lease_end_time: datetime raise OrchestratorException(f"Failed to extend reservation# {failed_to_extend_rid_list}") if len(extend_rid_list): - slice_object.state = SliceState.Configuring + slice_object.state = SliceState.Configuring.value if not controller.update_slice(slice_obj=slice_object, modify_state=True): self.logger.error(f"Failed to update slice: {slice_id} error: {controller.get_last_error()}") From bed559358d72b32ccb149cc0ad02fa9d1334b6f7 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 11 Jul 2024 00:25:27 -0400 Subject: [PATCH 095/133] fix state value --- fabric_cf/orchestrator/core/orchestrator_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index 1e7e13ff..9ea5979a 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -514,7 +514,7 @@ def modify_slice(self, *, token: str, slice_id: str, slice_graph: str) -> List[d config_props[Constants.TAGS] = ','.join(tags) config_props[Constants.TOKEN_HASH] = fabric_token.token_hash slice_obj.set_config_properties(value=config_props) - slice_object.state = SliceState.Modifying.value + slice_obj.state = SliceState.Modifying.value if not controller.update_slice(slice_obj=slice_obj, modify_state=modify_state): self.logger.error(f"Failed to update slice: {slice_id} error: {controller.get_last_error()}") From 6edb03850856bd2ed9aeb98d703c8d13e2e68b81 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 11 Jul 2024 09:43:58 -0400 Subject: [PATCH 096/133] add more debug logs --- .../actor/core/policy/network_node_inventory.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/fabric_cf/actor/core/policy/network_node_inventory.py b/fabric_cf/actor/core/policy/network_node_inventory.py index 235e8b68..b04f3b1f 100644 --- a/fabric_cf/actor/core/policy/network_node_inventory.py +++ b/fabric_cf/actor/core/policy/network_node_inventory.py @@ -404,7 +404,7 @@ def __check_components(self, *, rid: ID, requested_components: AttachedComponent :return: Components updated with the corresponding BQM node ids :raises: BrokerException in case the request cannot be satisfied """ - self.logger.debug(f"available_components after excluding allocated components: {graph_node.attached_components_info.devices.keys()}") + self.logger.debug(f"Available on {graph_node.node_id} components: {graph_node.attached_components_info.devices.keys()}") self.__exclude_components_for_existing_reservations(rid=rid, graph_node=graph_node, existing_reservations=existing_reservations) @@ -432,22 +432,27 @@ def __check_components(self, *, rid: ID, requested_components: AttachedComponent comps_to_remove.append(av) for c in comps_to_remove: + self.logger.debug(f"Excluding component: {c.get_name()}") + print(f"Excluding component: {c.get_name()}") graph_node.attached_components_info.remove_device(name=c.get_name()) self.logger.debug(f"requested_components: {requested_components.devices.values()} for reservation# {rid}") for name, requested_component in requested_components.devices.items(): if operation == ReservationOperation.Modify and requested_component.get_node_map() is not None: - self.logger.debug(f"==========Ignoring Allocated component: {requested_component} for modify") + self.logger.debug(f"Modify: Ignoring Allocated component: {requested_component}") continue if operation == ReservationOperation.Extend and requested_component.get_node_map() is not None: bqm_id, node_id = requested_component.get_node_map() + if requested_component.get_type() == ComponentType.SharedNIC: allocated_bdfs = existing_components.get(node_id) if allocated_bdfs and requested_component.labels and requested_component.labels.bdf: bdfs = requested_component.labels.bdf if isinstance(requested_component.labels.bdf, str): bdfs = [requested_component.labels.bdf] + + self.logger.debug(f"Allocated BDFs: {allocated_bdfs}") for x in bdfs: if x in allocated_bdfs: raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, @@ -459,10 +464,10 @@ def __check_components(self, *, rid: ID, requested_components: AttachedComponent msg=f"Renew failed: Component of type: {requested_component.get_model()} " f"not available in graph node: {graph_node.node_id}") - self.logger.debug(f"==========Ignoring Allocated component: {requested_component} for renew") + self.logger.debug(f"Renew: Component {requested_component} still available") continue - self.logger.debug(f"==========Allocating component: {requested_component}") + self.logger.debug(f"Create: Allocating component: {requested_component}") resource_type = requested_component.get_type() resource_model = requested_component.get_model() if resource_type == ComponentType.Storage: @@ -471,7 +476,8 @@ def __check_components(self, *, rid: ID, requested_components: AttachedComponent requested_component.label_allocations = Labels.update(lab=requested_component.get_labels()) continue available_components = graph_node.attached_components_info.get_devices_by_type(resource_type=resource_type) - self.logger.debug(f"available_components after excluding allocated components: {available_components}") + self.logger.debug(f"Available components of type: {resource_type} after excluding " + f"allocated components: {available_components}") if available_components is None or len(available_components) == 0: raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, From 0372424223a533effce17c08a33199bffb0ead98 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 11 Jul 2024 10:45:55 -0400 Subject: [PATCH 097/133] clear failure on extend --- fabric_cf/actor/core/kernel/broker_reservation.py | 1 + fabric_cf/actor/core/kernel/reservation_states.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fabric_cf/actor/core/kernel/broker_reservation.py b/fabric_cf/actor/core/kernel/broker_reservation.py index 24976d7b..ce4ed424 100644 --- a/fabric_cf/actor/core/kernel/broker_reservation.py +++ b/fabric_cf/actor/core/kernel/broker_reservation.py @@ -319,6 +319,7 @@ def probe_pending(self): self.transition(prefix="Recover from Extend Failure", state=ReservationStates.Ticketed, pending=ReservationPendingStates.None_) self.extend_failure = False + self.update_data.clear(clear_fail=True) else: if self.pending_state == ReservationPendingStates.Ticketing: # Check for a pending ticket operation that may have completed diff --git a/fabric_cf/actor/core/kernel/reservation_states.py b/fabric_cf/actor/core/kernel/reservation_states.py index 0e4b773b..a2aa8b7c 100644 --- a/fabric_cf/actor/core/kernel/reservation_states.py +++ b/fabric_cf/actor/core/kernel/reservation_states.py @@ -76,7 +76,7 @@ def translate(state_name: str): elif state_name.lower() == ReservationStates.Failed.name.lower(): return ReservationStates.Failed elif state_name.lower() == ReservationStates.CloseFail.name.lower(): - return ReservationStates.Failed + return ReservationStates.CloseFail else: return ReservationStates.Unknown From e403b1db152da5854bf0828ab0723fa1bddbd89f Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 11 Jul 2024 11:41:37 -0400 Subject: [PATCH 098/133] improve error messages --- .../core/policy/network_node_inventory.py | 6 +- .../core/policy/network_service_inventory.py | 149 +----------------- 2 files changed, 7 insertions(+), 148 deletions(-) diff --git a/fabric_cf/actor/core/policy/network_node_inventory.py b/fabric_cf/actor/core/policy/network_node_inventory.py index b04f3b1f..53015db1 100644 --- a/fabric_cf/actor/core/policy/network_node_inventory.py +++ b/fabric_cf/actor/core/policy/network_node_inventory.py @@ -456,13 +456,13 @@ def __check_components(self, *, rid: ID, requested_components: AttachedComponent for x in bdfs: if x in allocated_bdfs: raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, - msg=f"Renew failed: Component of type: {requested_component.get_model()} " - f"not available in graph node: {graph_node.node_id}") + msg=f"Renew failed: Component of type: {requested_component.get_model()} with PCI Address: {x}" + f"already in use by another reservation for node: {graph_node.node_id}") else: if node_id in existing_components.keys(): raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, msg=f"Renew failed: Component of type: {requested_component.get_model()} " - f"not available in graph node: {graph_node.node_id}") + f"already in use by another reservation for node: {graph_node.node_id}") self.logger.debug(f"Renew: Component {requested_component} still available") continue diff --git a/fabric_cf/actor/core/policy/network_service_inventory.py b/fabric_cf/actor/core/policy/network_service_inventory.py index bbb4426f..7642ba4b 100644 --- a/fabric_cf/actor/core/policy/network_service_inventory.py +++ b/fabric_cf/actor/core/policy/network_service_inventory.py @@ -498,162 +498,21 @@ def _can_extend(self, *, subnet_list: List, requested_ns: NetworkServiceSliver): allocated_subnet = ipaddress.IPv4Network(requested_ns.gateway.subnet) if allocated_subnet not in subnet_list: raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, - msg=f"Subnet {requested_ns.gateway.subnet} for Network Service : " + msg=f"Renew failed: Subnet {requested_ns.gateway.subnet} for Network Service : " f"{requested_ns.get_type()} already in use by another reservation") elif requested_ns.get_type() == ServiceType.FABNetv4Ext: if requested_ns.gateway.gateway in subnet_list: raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, - msg=f"Subnet {requested_ns.gateway.subnet} for Network Service : " + msg=f"Renew failed: Subnet {requested_ns.gateway.subnet} for Network Service : " f"{requested_ns.get_type()} already in use by another reservation") elif requested_ns.get_type() in Constants.L3_FABNETv6_SERVICES: allocated_subnet = ipaddress.IPv6Network(requested_ns.gateway.subnet) if allocated_subnet not in subnet_list: raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, - msg=f"Subnet {requested_ns.gateway.subnet} for Network Service : " + msg=f"Renew failed: Subnet {requested_ns.gateway.subnet} for Network Service : " f"{requested_ns.get_type()} already in use by another reservation") - ''' - def allocate(self, *, rid: ID, requested_ns: NetworkServiceSliver, owner_ns: NetworkServiceSliver, - existing_reservations: List[ABCReservationMixin]) -> NetworkServiceSliver: - """ - Allocate Network Service Sliver (Only for L3 Service) - - grab the /17 or /48 from BQM Site specific NetworkService - - divide it into /24 or /64 subnets - - exclude the 1st subnet (reserved for control plane) - - exclude the last subnet for V6 as the last subnet will be used for the FABRIC STAR Bastion Host Allocation - - exclude the subnets already assigned to other V4/V6 NetworkService on the same owner switch - - allocate the first available subnet to the NetworkService - :param rid: Reservation ID - :param requested_ns: Requested NetworkService - :param owner_ns: BQM Network Service identified to serve the NetworkService - :param existing_reservations: Existing Reservations which also are served by the owner switch - :return NetworkService updated with the allocated subnet for FABNetv4 and FABNetv6 services - Return the sliver updated with the subnet - """ - try: - if requested_ns.get_type() not in Constants.L3_SERVICES: - return requested_ns - - # Grab Label Delegations - delegation_id, delegated_label = self.get_delegations(lab_cap_delegations=owner_ns.get_label_delegations()) - - # HACK to use FabNetv6 for FabNetv6Ext as both have the same range - # Needs to be removed if FabNetv6/FabNetv6Ext are configured with different ranges - requested_ns_type = requested_ns.get_type() - if requested_ns_type == ServiceType.FABNetv6Ext: - requested_ns_type = ServiceType.FABNetv6 - # Hack End - - if requested_ns_type == ServiceType.L3VPN: - if requested_ns.labels is not None: - requested_ns.labels = Labels.update(requested_ns.labels, asn=delegated_label.asn) - else: - requested_ns.labels = Labels(asn=delegated_label.asn) - return requested_ns - - subnet_list = None - - # Get Subnet - if owner_ns.get_type() in Constants.L3_FABNETv6_SERVICES: - ip_network = IPv6Network(delegated_label.ipv6_subnet) - subnet_list = list(ip_network.subnets(new_prefix=64)) - # Exclude the 1st subnet as it is reserved for control plane - subnet_list.pop(0) - # https://github.com/fabric-testbed/ControlFramework/issues/376 - # Exclude the last subnet as the last subnet will be used for the FABRIC STAR Bastion Host Allocation - subnet_list.pop(-1) - - elif owner_ns.get_type() == ServiceType.FABNetv4: - ip_network = IPv4Network(delegated_label.ipv4_subnet) - subnet_list = list(ip_network.subnets(new_prefix=24)) - # Exclude the 1st subnet as it is reserved for control plane - subnet_list.pop(0) - - elif owner_ns.get_type() == ServiceType.FABNetv4Ext: - ip_network = IPv4Network(delegated_label.ipv4_subnet) - subnet_list = list(ip_network.hosts()) - - # Exclude the already allocated subnets - for reservation in existing_reservations: - if rid == reservation.get_reservation_id(): - continue - # For Active or Ticketed or Ticketing reservations; reduce the counts from available - allocated_sliver = None - if reservation.is_ticketing() and reservation.get_approved_resources() is not None: - allocated_sliver = reservation.get_approved_resources().get_sliver() - - if (reservation.is_active() or reservation.is_ticketed()) and \ - reservation.get_resources() is not None: - allocated_sliver = reservation.get_resources().get_sliver() - - self.logger.debug(f"Existing res# {reservation.get_reservation_id()} " - f"allocated: {allocated_sliver}") - - if allocated_sliver is None: - continue - - # HACK to use FabNetv6 for FabNetv6Ext as both have the same range - # Needs to be removed if FabNetv6/FabNetv6Ext are configured with different ranges - allocated_sliver_type = allocated_sliver.get_type() - if allocated_sliver_type == ServiceType.FABNetv6Ext: - allocated_sliver_type = ServiceType.FABNetv6 - # HACK End - - if allocated_sliver_type != requested_ns_type: - continue - - if allocated_sliver.get_type() == ServiceType.FABNetv4: - subnet_to_remove = IPv4Network(allocated_sliver.get_gateway().lab.ipv4_subnet) - subnet_list.remove(subnet_to_remove) - self.logger.debug( - f"Excluding already allocated IP4Subnet: " - f"{allocated_sliver.get_gateway().lab.ipv4_subnet}" - f" to res# {reservation.get_reservation_id()}") - - elif allocated_sliver.get_type() == ServiceType.FABNetv4Ext: - if allocated_sliver.labels is not None and allocated_sliver.labels.ipv4 is not None: - for x in allocated_sliver.labels.ipv4: - subnet_to_remove = ipaddress.IPv4Address(x) - subnet_list.remove(subnet_to_remove) - self.logger.debug( - f"Excluding already allocated IPv4: {x}" - f" to res# {reservation.get_reservation_id()}") - - elif allocated_sliver.get_type() in Constants.L3_FABNETv6_SERVICES: - subnet_to_remove = IPv6Network(allocated_sliver.get_gateway().lab.ipv6_subnet) - subnet_list.remove(subnet_to_remove) - self.logger.debug( - f"Excluding already allocated IPv6Subnet: " - f"{allocated_sliver.get_gateway().lab.ipv6_subnet}" - f" to res# {reservation.get_reservation_id()}") - - gateway_labels = Labels() - if requested_ns.get_type() == ServiceType.FABNetv4: - gateway_labels.ipv4_subnet = subnet_list[0].with_prefixlen - gateway_labels.ipv4 = str(list(subnet_list[0].hosts())[0]) - - elif requested_ns.get_type() == ServiceType.FABNetv4Ext: - gateway_labels.ipv4_subnet = ip_network.with_prefixlen - gateway_labels.ipv4 = str(subnet_list[0]) - - elif requested_ns.get_type() in Constants.L3_FABNETv6_SERVICES: - gateway_labels.ipv6_subnet = subnet_list[0].with_prefixlen - gateway_labels.ipv6 = str(next(subnet_list[0].hosts())) - - self.logger.debug(f"Gateway Labels: {gateway_labels}") - - requested_ns.gateway = Gateway(lab=gateway_labels) - - # Allocate the IP Addresses for the requested NS - requested_ns = self.__allocate_ip_address_to_ifs(requested_ns=requested_ns) - except Exception as e: - self.logger.error(f"Error in allocate_gateway_for_ns: {e}") - self.logger.error(traceback.format_exc()) - raise BrokerException(msg=f"Allocation failure for Requested Network Service: {e}") - return requested_ns - ''' - def free(self, *, count: int, request: dict = None, resource: dict = None) -> dict: pass @@ -687,7 +546,7 @@ def allocate_peered_ifs(self, *, rid: ID, owner_switch: NodeSliver, if requested_ifs.get_node_map() and requested_ifs.labels and requested_ifs.labels.vlan: if int(requested_ifs.labels.vlan) in available_vlans: raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, - msg=f"VLAN {requested_ifs.labels.vlan} for Interface : " + msg=f"Renew failed: VLAN {requested_ifs.labels.vlan} for Interface : " f"{requested_ifs.get_name()} already in use by another reservation") vlan = str(random.choice(available_vlans)) From 3bd0b95b6c8c27dd0fe82623441e2f420b468ca2 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 11 Jul 2024 11:43:25 -0400 Subject: [PATCH 099/133] improve error messages --- fabric_cf/actor/core/policy/broker_simpler_units_policy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 423c670f..efa19a9c 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -643,7 +643,7 @@ def __allocate_nodes(self, *, reservation: ABCBrokerReservation, inv: NetworkNod # no candidate nodes found if len(node_id_list) == 0: - error_msg = f'Insufficient resources: No candidates nodes found to serve {reservation}' + error_msg = f'Insufficient resources: No hosts available to provision the {reservation}' self.logger.error(error_msg) return delegation_id, sliver, error_msg From cbd340c98b23078b5e02417f1b8a4b8abe7f6e8a Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 11 Jul 2024 12:09:34 -0400 Subject: [PATCH 100/133] update the lease end time on renew --- fabric_cf/actor/core/apis/abc_reservation_mixin.py | 10 ++++++++++ fabric_cf/actor/core/kernel/slice.py | 13 ++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/fabric_cf/actor/core/apis/abc_reservation_mixin.py b/fabric_cf/actor/core/apis/abc_reservation_mixin.py index 72a55180..04a2a480 100644 --- a/fabric_cf/actor/core/apis/abc_reservation_mixin.py +++ b/fabric_cf/actor/core/apis/abc_reservation_mixin.py @@ -29,6 +29,8 @@ from enum import Enum from typing import TYPE_CHECKING +from fabric_cf.actor.core.time.term import Term + from fabric_cf.actor.core.apis.abc_reservation_resources import ABCReservationResources from fabric_cf.actor.core.apis.abc_reservation_status import ABCReservationStatus @@ -541,5 +543,13 @@ def poa_info(self, *, incoming: Poa): """ Process POA response + @throws Exception in case of error + """ + + @abstractmethod + def get_term(self) -> Term: + """ + Return Tem + @throws Exception in case of error """ \ No newline at end of file diff --git a/fabric_cf/actor/core/kernel/slice.py b/fabric_cf/actor/core/kernel/slice.py index c9d92356..593dda76 100644 --- a/fabric_cf/actor/core/kernel/slice.py +++ b/fabric_cf/actor/core/kernel/slice.py @@ -268,7 +268,18 @@ def is_dirty(self) -> bool: return self.dirty def transition_slice(self, *, operation: SliceOperation) -> Tuple[bool, SliceState]: - return self.state_machine.transition_slice(operation=operation, reservations=self.reservations) + status, new_state = self.state_machine.transition_slice(operation=operation, reservations=self.reservations) + if status and new_state in [SliceState.StableOK, SliceState.StableError]: + new_end = None + for r in self.reservations.values(): + term = r.get_term() + if not new_end or (term and term.get_end_time() > new_end): + new_end = term.get_end_time() + + if new_end: + self.lease_end = new_end + + return status, new_state def is_stable_ok(self) -> bool: state_changed, slice_state = self.transition_slice(operation=SliceStateMachine.REEVALUATE) From 2875431a7b907bd04daf811bb9b43393edb1001c Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 11 Jul 2024 13:03:29 -0400 Subject: [PATCH 101/133] excluding existing components to exclude itself on extend --- fabric_cf/actor/core/policy/network_node_inventory.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fabric_cf/actor/core/policy/network_node_inventory.py b/fabric_cf/actor/core/policy/network_node_inventory.py index 53015db1..f7051c37 100644 --- a/fabric_cf/actor/core/policy/network_node_inventory.py +++ b/fabric_cf/actor/core/policy/network_node_inventory.py @@ -343,7 +343,8 @@ def __exclude_allocated_component(self, *, graph_node: NodeSliver, available_com graph_node.attached_components_info.remove_device(name=available_component.get_name()) def __exclude_components_for_existing_reservations(self, *, rid: ID, graph_node: NodeSliver, - existing_reservations: List[ABCReservationMixin]) -> NodeSliver: + existing_reservations: List[ABCReservationMixin], + operation: ReservationOperation = ReservationOperation.Create) -> NodeSliver: """ Remove already assigned components to existing reservations from the candidate node @param rid reservation ID @@ -353,7 +354,8 @@ def __exclude_components_for_existing_reservations(self, *, rid: ID, graph_node: """ for reservation in existing_reservations: # Requested reservation should be skipped only when new i.e. not ticketed - if rid == reservation.get_reservation_id() and not reservation.is_ticketed(): + if rid == reservation.get_reservation_id() and \ + (operation == ReservationOperation.Extend or not reservation.is_ticketed()): continue # For Active or Ticketed or Ticketing reservations; reduce the counts from available allocated_sliver = None From ff8f5eb8cb12e2ff3efc9cc6eac6be75e57b090e Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 11 Jul 2024 13:15:10 -0400 Subject: [PATCH 102/133] excluding existing components to exclude itself on extend --- fabric_cf/actor/core/policy/network_node_inventory.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fabric_cf/actor/core/policy/network_node_inventory.py b/fabric_cf/actor/core/policy/network_node_inventory.py index f7051c37..83b2483c 100644 --- a/fabric_cf/actor/core/policy/network_node_inventory.py +++ b/fabric_cf/actor/core/policy/network_node_inventory.py @@ -409,7 +409,8 @@ def __check_components(self, *, rid: ID, requested_components: AttachedComponent self.logger.debug(f"Available on {graph_node.node_id} components: {graph_node.attached_components_info.devices.keys()}") self.__exclude_components_for_existing_reservations(rid=rid, graph_node=graph_node, - existing_reservations=existing_reservations) + existing_reservations=existing_reservations, + operation=operation) self.logger.debug(f"Excluding components connected to Network Services: {existing_components}") From a300323457b55055e3efb6cb1795ea6b50fa4d87 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 11 Jul 2024 13:24:58 -0400 Subject: [PATCH 103/133] excluding existing components to exclude itself on extend --- fabric_cf/actor/core/policy/network_node_inventory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric_cf/actor/core/policy/network_node_inventory.py b/fabric_cf/actor/core/policy/network_node_inventory.py index 83b2483c..30faf332 100644 --- a/fabric_cf/actor/core/policy/network_node_inventory.py +++ b/fabric_cf/actor/core/policy/network_node_inventory.py @@ -414,7 +414,7 @@ def __check_components(self, *, rid: ID, requested_components: AttachedComponent self.logger.debug(f"Excluding components connected to Network Services: {existing_components}") - if existing_components and len(existing_components): + if existing_components and len(existing_components) and operation != ReservationOperation.Extend: comps_to_remove = [] for av in graph_node.attached_components_info.devices.values(): # Skip if not in allocated comps attached to Network Services From e9d9785bb84b2da5b5bc6c0457117c7f7c606f3e Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 11 Jul 2024 13:40:54 -0400 Subject: [PATCH 104/133] only check for components attached to nodes in case of extend --- .../policy/broker_simpler_units_policy.py | 19 +++++++++++++------ .../core/policy/network_node_inventory.py | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index efa19a9c..674f58e3 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -588,9 +588,11 @@ def __find_first_fit(self, node_id_list: List[str], node_id_to_reservations: dic start=term.get_start_time(), end=term.get_end_time()) + include_ns = False if operation == ReservationOperation.Extend else True existing_components = self.get_existing_components(node_id=node_id, start=term.get_start_time(), end=term.get_end_time(), - excludes=[str(reservation.get_reservation_id())]) + excludes=[str(reservation.get_reservation_id())], + include_ns=include_ns) delegation_id, sliver = inv.allocate(rid=reservation.get_reservation_id(), requested_sliver=sliver, @@ -1503,13 +1505,16 @@ def get_existing_reservations(self, node_id: str, node_id_to_reservations: dict, return existing_reservations def get_existing_components(self, node_id: str, start: datetime = None, end: datetime = None, - excludes: List[str] = None) -> Dict[str, List[str]]: + excludes: List[str] = None, include_ns: bool = True, + include_node: bool = True) -> Dict[str, List[str]]: """ Get existing components attached to Active/Ticketed Network Service Slivers :param node_id: :param start: :param end: :param excludes: + :param include_node: + :param include_ns: :return: list of components """ states = [ReservationStates.Active.value, @@ -1519,11 +1524,13 @@ def get_existing_components(self, node_id: str, start: datetime = None, end: dat ReservationStates.CloseFail.value] res_type = [] - for x in ServiceType: - res_type.append(str(x)) + if include_ns: + for x in ServiceType: + res_type.append(str(x)) - for x in NodeType: - res_type.append(str(x)) + if include_node: + for x in NodeType: + res_type.append(str(x)) # Only get Active or Ticketing reservations return self.actor.get_plugin().get_database().get_components(node_id=node_id, rsv_type=res_type, states=states, diff --git a/fabric_cf/actor/core/policy/network_node_inventory.py b/fabric_cf/actor/core/policy/network_node_inventory.py index 30faf332..83b2483c 100644 --- a/fabric_cf/actor/core/policy/network_node_inventory.py +++ b/fabric_cf/actor/core/policy/network_node_inventory.py @@ -414,7 +414,7 @@ def __check_components(self, *, rid: ID, requested_components: AttachedComponent self.logger.debug(f"Excluding components connected to Network Services: {existing_components}") - if existing_components and len(existing_components) and operation != ReservationOperation.Extend: + if existing_components and len(existing_components): comps_to_remove = [] for av in graph_node.attached_components_info.devices.values(): # Skip if not in allocated comps attached to Network Services From 53114d7460d4561ad433feea698dc65094516513 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 11 Jul 2024 15:10:39 -0400 Subject: [PATCH 105/133] ns slivers going to Failed instead of Ticketed when renew fails --- .../actor/core/kernel/broker_reservation.py | 1 + .../policy/broker_simpler_units_policy.py | 458 +++++++++--------- .../core/policy/network_service_inventory.py | 2 + 3 files changed, 236 insertions(+), 225 deletions(-) diff --git a/fabric_cf/actor/core/kernel/broker_reservation.py b/fabric_cf/actor/core/kernel/broker_reservation.py index ce4ed424..8cae5047 100644 --- a/fabric_cf/actor/core/kernel/broker_reservation.py +++ b/fabric_cf/actor/core/kernel/broker_reservation.py @@ -320,6 +320,7 @@ def probe_pending(self): pending=ReservationPendingStates.None_) self.extend_failure = False self.update_data.clear(clear_fail=True) + self.error_message = "" else: if self.pending_state == ReservationPendingStates.Ticketing: # Check for a pending ticket operation that may have completed diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 674f58e3..23bc4f52 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -664,238 +664,246 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, 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_ns = None - owner_ns_id = None - bqm_node = None - is_vnic = False - owner_mpls_ns = None - owner_switch = None - - peered_ns_interfaces = [] - ero_source_end_info = [] - - # For each Interface Sliver; - for ifs in sliver.interface_info.interfaces.values(): - node_map_id = self.combined_broker_model_graph_id - - # Fetch Network Node Id and BQM Component Id - node_id, bqm_node_id = ifs.get_node_map() - - # Skipping the already allocated interface on a modify - if self.combined_broker_model_graph_id in node_id: - continue - - if node_id == str(NodeType.Facility): - bqm_node = self.get_facility_sliver(node_name=bqm_node_id) - # Peered Interfaces are handled at the end - elif node_id == str(Constants.PEERED): - peered_ns_interfaces.append(ifs) - continue - elif node_id == str(NodeType.Switch): - bqm_node = self.get_network_node_from_graph(node_id=bqm_node_id) - node_map_id = f"{node_map_id}#{bqm_node.get_name()}#{bqm_node_id}#{ifs.get_labels().local_name}" - else: - # For VM interfaces - bqm_node = self.get_component_sliver(node_id=bqm_node_id) - node_map_id = f"{node_map_id}:{node_id}:{bqm_node_id}:{ifs.get_labels().bdf}" - - if bqm_node is None: - raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES) - - # Get BQM Connection Point in Site Delegation (c) - site_cp = FimHelper.get_site_interface_sliver(component=bqm_node, - local_name=ifs.get_labels().local_name, - region=ifs.get_labels().region, - device_name=ifs.get_labels().device_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_peer_interface_sliver(site_ifs_id=site_cp.node_id, - interface_type=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): {net_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, owner_ns = self.get_owners(node_id=net_cp.node_id, - ns_type=sliver.get_type()) - - # Hack for IPV6Ext services - owner_ns_id = owner_ns.node_id - if 'ipv6ext-ns' in owner_ns_id: - owner_ns_id = owner_ns_id.replace('ipv6ext-ns', 'ipv6-ns') - - bqm_cp = net_cp - if bqm_node.get_type() == NodeType.Facility or \ - (sliver.get_type() == ServiceType.L2Bridge and - bqm_node.get_model() == Constants.OPENSTACK_VNIC_MODEL): - bqm_cp = site_cp - - if bqm_node.get_type() == ComponentType.SharedNIC: - if bqm_node.get_model() == Constants.OPENSTACK_VNIC_MODEL: - is_vnic = True - - # VLAN is already set by the Orchestrator using the information from the Node Sliver Parent Reservation - if ifs.get_labels().vlan is None and not is_vnic: - 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_ns_id, - node_id_to_reservations=node_id_to_reservations, - start=term.get_start_time(), - end=term.get_end_time()) - # Set vlan - source: (c) - only for dedicated NICs - ifs = inv.allocate_ifs(rid=rid, requested_ns=sliver, requested_ifs=ifs, owner_ns=owner_ns, - bqm_ifs=bqm_cp, existing_reservations=existing_reservations) + try: + self.logger.debug(f"Processing Network Service sliver: {sliver}") + owner_ns = None + owner_ns_id = None + bqm_node = None + is_vnic = False + owner_mpls_ns = None + owner_switch = None + + peered_ns_interfaces = [] + ero_source_end_info = [] + + # For each Interface Sliver; + for ifs in sliver.interface_info.interfaces.values(): + node_map_id = self.combined_broker_model_graph_id - local_name = net_cp.get_name() - device_name = owner_switch.get_name() + # Fetch Network Node Id and BQM Component Id + node_id, bqm_node_id = ifs.get_node_map() - if device_name == Constants.AL2S: + # Skipping the already allocated interface on a modify + if self.combined_broker_model_graph_id in node_id: + continue + + if node_id == str(NodeType.Facility): + bqm_node = self.get_facility_sliver(node_name=bqm_node_id) + # Peered Interfaces are handled at the end + elif node_id == str(Constants.PEERED): + peered_ns_interfaces.append(ifs) + continue + elif node_id == str(NodeType.Switch): + bqm_node = self.get_network_node_from_graph(node_id=bqm_node_id) + node_map_id = f"{node_map_id}#{bqm_node.get_name()}#{bqm_node_id}#{ifs.get_labels().local_name}" + else: + # For VM interfaces + bqm_node = self.get_component_sliver(node_id=bqm_node_id) + node_map_id = f"{node_map_id}:{node_id}:{bqm_node_id}:{ifs.get_labels().bdf}" + + if bqm_node is None: + raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES) + + # Get BQM Connection Point in Site Delegation (c) + site_cp = FimHelper.get_site_interface_sliver(component=bqm_node, + local_name=ifs.get_labels().local_name, + region=ifs.get_labels().region, + device_name=ifs.get_labels().device_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_peer_interface_sliver(site_ifs_id=site_cp.node_id, + interface_type=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): {net_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, owner_ns = self.get_owners(node_id=net_cp.node_id, + ns_type=sliver.get_type()) + + # Hack for IPV6Ext services + owner_ns_id = owner_ns.node_id + if 'ipv6ext-ns' in owner_ns_id: + owner_ns_id = owner_ns_id.replace('ipv6ext-ns', 'ipv6-ns') + + bqm_cp = net_cp + if bqm_node.get_type() == NodeType.Facility or \ + (sliver.get_type() == ServiceType.L2Bridge and + bqm_node.get_model() == Constants.OPENSTACK_VNIC_MODEL): + bqm_cp = site_cp + + if bqm_node.get_type() == ComponentType.SharedNIC: + if bqm_node.get_model() == Constants.OPENSTACK_VNIC_MODEL: + is_vnic = True + + # VLAN is already set by the Orchestrator using the information from the Node Sliver Parent Reservation + if ifs.get_labels().vlan is None and not is_vnic: + 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_ns_id, + node_id_to_reservations=node_id_to_reservations, + start=term.get_start_time(), + end=term.get_end_time()) + # Set vlan - source: (c) - only for dedicated NICs + ifs = inv.allocate_ifs(rid=rid, requested_ns=sliver, requested_ifs=ifs, owner_ns=owner_ns, + bqm_ifs=bqm_cp, existing_reservations=existing_reservations) + + local_name = net_cp.get_name() + device_name = owner_switch.get_name() + + if device_name == Constants.AL2S: + delegation_id, delegated_label = InventoryForType.get_delegations(lab_cap_delegations= + net_cp.get_label_delegations()) + device_name = delegated_label.device_name + local_name = delegated_label.local_name + + # local_name source: (a) + ifs_labels = ifs.get_labels() + ifs_labels = Labels.update(ifs_labels, local_name=local_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=device_name) + adm_ids = owner_switch.get_structural_info().adm_graph_ids + site_adm_ids = bqm_node.get_structural_info().adm_graph_ids + + self.logger.debug(f"Owner Network Service: {owner_ns}") + self.logger.debug(f"Owner Switch: {owner_switch}") + if owner_switch.network_service_info is not None: + self.logger.debug(f"Owner Switch NS: {owner_switch.network_service_info.network_services.values()}") + + net_adm_ids = site_adm_ids + if bqm_node.get_type() != NodeType.Facility and not is_vnic: + net_adm_ids = [x for x in adm_ids if not x in site_adm_ids or site_adm_ids.remove(x)] + # For sites like EDC which share switch with other sites like NCSA, + # the net_adm_ids also includes delegation id from the other side, + # this results in this list having more than one entry and no way for + # the code to know which delegation is from Network AM + # Using a hack here to pick the delegation id from one of the + # layer 3 network services in the owner switch + if len(net_adm_ids) > 1: + for x in owner_switch.network_service_info.network_services.values(): + if x.get_layer() == NSLayer.L2: + continue + net_adm_ids = x.get_structural_info().adm_graph_ids + break + else: + if bqm_cp.labels is not None and bqm_cp.labels.ipv4_subnet is not None: + ifs_labels = Labels.update(ifs_labels, ipv4_subnet=bqm_cp.labels.ipv4_subnet) + if bqm_cp.labels is not None and bqm_cp.labels.ipv6_subnet is not None: + ifs_labels = Labels.update(ifs_labels, ipv6_subnet=bqm_cp.labels.ipv6_subnet) + 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) + + if bqm_node.get_type() == NodeType.Facility: + node_map_id = f"{node_map_id}#{bqm_node.get_name()}#{bqm_cp.node_id}#{ifs_labels.vlan}" + + # Update the Interface Sliver Node Map to map to (a) + ifs.set_node_map(node_map=(node_map_id, bqm_cp.node_id)) + #ifs.set_node_map(node_map=(self.combined_broker_model_graph_id, bqm_cp.node_id)) + + delegation_id = net_adm_ids[0] + + ifs.labels = ifs_labels + ifs.label_allocations = Labels.update(lab=ifs_labels) + + self.logger.info(f"Allocated Interface Sliver: {ifs} delegation: {delegation_id}") + + owner_v4_service = self.get_ns_from_switch(switch=owner_switch, ns_type=ServiceType.FABNetv4) + if owner_v4_service and owner_v4_service.get_labels(): + ero_source_end_info.append((owner_switch.node_id, owner_v4_service.get_labels().ipv4)) + + if not owner_ns: + bqm_graph_id, bqm_node_id = sliver.get_node_map() + owner_ns, owner_switch = self.get_network_service_from_graph(node_id=bqm_node_id, + parent=True) + # Hack for IPV6Ext services + owner_ns_id = owner_ns.node_id + if 'ipv6ext-ns' in owner_ns_id: + owner_ns_id = owner_ns_id.replace('ipv6ext-ns', 'ipv6-ns') + + owner_mpls_ns = None + if owner_switch: + for ns in owner_switch.network_service_info.network_services.values(): + if ServiceType.MPLS == ns.get_type(): + owner_mpls_ns = ns + break delegation_id, delegated_label = InventoryForType.get_delegations(lab_cap_delegations= - net_cp.get_label_delegations()) - device_name = delegated_label.device_name - local_name = delegated_label.local_name - - # local_name source: (a) - ifs_labels = ifs.get_labels() - ifs_labels = Labels.update(ifs_labels, local_name=local_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=device_name) - adm_ids = owner_switch.get_structural_info().adm_graph_ids - site_adm_ids = bqm_node.get_structural_info().adm_graph_ids - - self.logger.debug(f"Owner Network Service: {owner_ns}") - self.logger.debug(f"Owner Switch: {owner_switch}") - if owner_switch.network_service_info is not None: - self.logger.debug(f"Owner Switch NS: {owner_switch.network_service_info.network_services.values()}") - - net_adm_ids = site_adm_ids - if bqm_node.get_type() != NodeType.Facility and not is_vnic: - net_adm_ids = [x for x in adm_ids if not x in site_adm_ids or site_adm_ids.remove(x)] - # For sites like EDC which share switch with other sites like NCSA, - # the net_adm_ids also includes delegation id from the other side, - # this results in this list having more than one entry and no way for - # the code to know which delegation is from Network AM - # Using a hack here to pick the delegation id from one of the - # layer 3 network services in the owner switch - if len(net_adm_ids) > 1: - for x in owner_switch.network_service_info.network_services.values(): - if x.get_layer() == NSLayer.L2: - continue - net_adm_ids = x.get_structural_info().adm_graph_ids - break - else: - if bqm_cp.labels is not None and bqm_cp.labels.ipv4_subnet is not None: - ifs_labels = Labels.update(ifs_labels, ipv4_subnet=bqm_cp.labels.ipv4_subnet) - if bqm_cp.labels is not None and bqm_cp.labels.ipv6_subnet is not None: - ifs_labels = Labels.update(ifs_labels, ipv6_subnet=bqm_cp.labels.ipv6_subnet) - 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) - - if bqm_node.get_type() == NodeType.Facility: - node_map_id = f"{node_map_id}#{bqm_node.get_name()}#{bqm_cp.node_id}#{ifs_labels.vlan}" - - # Update the Interface Sliver Node Map to map to (a) - ifs.set_node_map(node_map=(node_map_id, bqm_cp.node_id)) - #ifs.set_node_map(node_map=(self.combined_broker_model_graph_id, bqm_cp.node_id)) - - delegation_id = net_adm_ids[0] - - ifs.labels = ifs_labels - ifs.label_allocations = Labels.update(lab=ifs_labels) - - self.logger.info(f"Allocated Interface Sliver: {ifs} delegation: {delegation_id}") - - owner_v4_service = self.get_ns_from_switch(switch=owner_switch, ns_type=ServiceType.FABNetv4) - if owner_v4_service and owner_v4_service.get_labels(): - ero_source_end_info.append((owner_switch.node_id, owner_v4_service.get_labels().ipv4)) - - if not owner_ns: - bqm_graph_id, bqm_node_id = sliver.get_node_map() - owner_ns, owner_switch = self.get_network_service_from_graph(node_id=bqm_node_id, - parent=True) - # Hack for IPV6Ext services - owner_ns_id = owner_ns.node_id - if 'ipv6ext-ns' in owner_ns_id: - owner_ns_id = owner_ns_id.replace('ipv6ext-ns', 'ipv6-ns') + owner_ns.get_label_delegations()) - owner_mpls_ns = None - if owner_switch: - for ns in owner_switch.network_service_info.network_services.values(): - if ServiceType.MPLS == ns.get_type(): - owner_mpls_ns = ns - break - delegation_id, delegated_label = InventoryForType.get_delegations(lab_cap_delegations= - owner_ns.get_label_delegations()) - - # Set the Subnet and gateway from the Owner Switch (a) - existing_reservations = self.get_existing_reservations(node_id=owner_ns_id, - node_id_to_reservations=node_id_to_reservations, - start=term.get_start_time(), end=term.get_end_time()) - - # Allocate VLAN for the Network Service - if is_vnic: - site_adm_ids = bqm_node.get_structural_info().adm_graph_ids - delegation_id = site_adm_ids[0] - inv.allocate_vnic(rid=rid, requested_ns=sliver, owner_ns=owner_ns, - existing_reservations=existing_reservations) - else: - sliver = inv.allocate(rid=rid, requested_ns=sliver, owner_ns=owner_ns, - existing_reservations=existing_reservations) + # Set the Subnet and gateway from the Owner Switch (a) + existing_reservations = self.get_existing_reservations(node_id=owner_ns_id, + node_id_to_reservations=node_id_to_reservations, + start=term.get_start_time(), end=term.get_end_time()) - # 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_ns_id)) - - self.__allocate_peered_interfaces(rid=rid, peered_interfaces=peered_ns_interfaces, owner_switch=owner_switch, - owner_mpls=owner_mpls_ns, inv=inv, sliver=sliver, owner_ns=owner_ns, - node_id_to_reservations=node_id_to_reservations, term=term) - - if sliver.ero and len(sliver.ero.get()) and len(ero_source_end_info) == 2: - self.logger.info(f"Requested ERO: {sliver.ero}") - ero_hops = [] - new_path = [ero_source_end_info[0][1]] - type, path = sliver.ero.get() - for hop in path.get()[0]: - # User passes the site names; Broker maps the sites names to the respective switch IP - hop_switch = self.get_switch_sliver(site=hop) - self.logger.debug(f"Switch information for {hop}: {hop_switch}") - if not hop_switch: - self.logger.error(f"Requested hop: {hop} in the ERO does not exist") - raise BrokerException(error_code=ExceptionErrorCode.INVALID_ARGUMENT, - msg=f"Requested hop: {hop} in the ERO does not exist ") - - hop_v4_service = self.get_ns_from_switch(switch=hop_switch, ns_type=ServiceType.FABNetv4) - if hop_v4_service and hop_v4_service.get_labels() and hop_v4_service.get_labels().ipv4: - self.logger.debug(f"Fabnetv4 information for {hop}: {hop_v4_service}") - ero_hops.append(f"{hop_switch.node_id}-ns") - new_path.append(hop_v4_service.get_labels().ipv4) - - new_path.append(ero_source_end_info[1][1]) - - if len(new_path): - if not self.validate_requested_ero_path(source_node=ero_source_end_info[0][0], - end_node=ero_source_end_info[1][0], - hops=ero_hops): - raise BrokerException(error_code=ExceptionErrorCode.INVALID_ARGUMENT, - msg=f"Requested ERO path: {sliver.ero} is invalid!") - ero_path = Path() - ero_path.set_symmetric(new_path) - sliver.ero.set(ero_path) - self.logger.info(f"Allocated ERO: {sliver.ero}") + # Allocate VLAN for the Network Service + if is_vnic: + site_adm_ids = bqm_node.get_structural_info().adm_graph_ids + delegation_id = site_adm_ids[0] + inv.allocate_vnic(rid=rid, requested_ns=sliver, owner_ns=owner_ns, + existing_reservations=existing_reservations) + else: + sliver = inv.allocate(rid=rid, requested_ns=sliver, owner_ns=owner_ns, + existing_reservations=existing_reservations) + + # 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_ns_id)) + + self.__allocate_peered_interfaces(rid=rid, peered_interfaces=peered_ns_interfaces, owner_switch=owner_switch, + owner_mpls=owner_mpls_ns, inv=inv, sliver=sliver, owner_ns=owner_ns, + node_id_to_reservations=node_id_to_reservations, term=term) + + if sliver.ero and len(sliver.ero.get()) and len(ero_source_end_info) == 2: + self.logger.info(f"Requested ERO: {sliver.ero}") + ero_hops = [] + new_path = [ero_source_end_info[0][1]] + type, path = sliver.ero.get() + for hop in path.get()[0]: + # User passes the site names; Broker maps the sites names to the respective switch IP + hop_switch = self.get_switch_sliver(site=hop) + self.logger.debug(f"Switch information for {hop}: {hop_switch}") + if not hop_switch: + self.logger.error(f"Requested hop: {hop} in the ERO does not exist") + raise BrokerException(error_code=ExceptionErrorCode.INVALID_ARGUMENT, + msg=f"Requested hop: {hop} in the ERO does not exist ") + + hop_v4_service = self.get_ns_from_switch(switch=hop_switch, ns_type=ServiceType.FABNetv4) + if hop_v4_service and hop_v4_service.get_labels() and hop_v4_service.get_labels().ipv4: + self.logger.debug(f"Fabnetv4 information for {hop}: {hop_v4_service}") + ero_hops.append(f"{hop_switch.node_id}-ns") + new_path.append(hop_v4_service.get_labels().ipv4) + + new_path.append(ero_source_end_info[1][1]) + + if len(new_path): + if not self.validate_requested_ero_path(source_node=ero_source_end_info[0][0], + end_node=ero_source_end_info[1][0], + hops=ero_hops): + raise BrokerException(error_code=ExceptionErrorCode.INVALID_ARGUMENT, + msg=f"Requested ERO path: {sliver.ero} is invalid!") + ero_path = Path() + ero_path.set_symmetric(new_path) + sliver.ero.set(ero_path) + self.logger.info(f"Allocated ERO: {sliver.ero}") + + except BrokerException as e: + if e.error_code == ExceptionErrorCode.INSUFFICIENT_RESOURCES: + self.logger.error(f"Exception occurred: {e}") + error_msg = e.msg + else: + raise e return delegation_id, sliver, error_msg diff --git a/fabric_cf/actor/core/policy/network_service_inventory.py b/fabric_cf/actor/core/policy/network_service_inventory.py index 7642ba4b..a6ba5529 100644 --- a/fabric_cf/actor/core/policy/network_service_inventory.py +++ b/fabric_cf/actor/core/policy/network_service_inventory.py @@ -353,6 +353,8 @@ def allocate(self, *, rid: ID, requested_ns: NetworkServiceSliver, owner_ns: Net # Allocate the IP Addresses for the requested NS requested_ns = self.__allocate_ip_address_to_ifs(requested_ns=requested_ns) + except BrokerException as e: + raise e except Exception as e: self.logger.error(f"Error in allocate_gateway_for_ns: {e}") self.logger.error(traceback.format_exc()) From b2215a4909b9e61217602a46c76771f1ec1c2931 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 12 Jul 2024 09:32:42 -0400 Subject: [PATCH 106/133] set delegation to none on failure --- fabric_cf/actor/core/policy/broker_simpler_units_policy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 23bc4f52..4c31319c 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -899,6 +899,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: self.logger.info(f"Allocated ERO: {sliver.ero}") except BrokerException as e: + delegation_id = None if e.error_code == ExceptionErrorCode.INSUFFICIENT_RESOURCES: self.logger.error(f"Exception occurred: {e}") error_msg = e.msg From 34f7824d8a61711aa7fb457ed0146f8c09a33592 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 12 Jul 2024 12:23:35 -0400 Subject: [PATCH 107/133] log vm name and image --- fabric_cf/actor/core/common/event_logger.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fabric_cf/actor/core/common/event_logger.py b/fabric_cf/actor/core/common/event_logger.py index 79047fc7..15ea4fa2 100644 --- a/fabric_cf/actor/core/common/event_logger.py +++ b/fabric_cf/actor/core/common/event_logger.py @@ -29,6 +29,7 @@ from fabric_mb.message_bus.messages.slice_avro import SliceAvro from fim.logging.log_collector import LogCollector from fim.slivers.base_sliver import BaseSliver +from fim.slivers.network_node import NodeSliver from fim.user.topology import ExperimentTopology from fabric_cf.actor.core.common.constants import Constants @@ -110,6 +111,9 @@ def log_sliver_event(self, *, slice_object: SliceAvro, sliver: BaseSliver, verb: if ssh_foot_print is not None: log_message += f" keys{ssh_foot_print}" + if isinstance(sliver, NodeSliver): + log_message += f"name: {sliver.get_name()} image: {sliver.get_image_ref()}" + log_message += f" {str(lc)}" self.logger.info(log_message) From e28e3a079d681a0af6fe04cb000d3c3d70df5d9b Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 12 Jul 2024 16:02:32 -0400 Subject: [PATCH 108/133] remove subnet only when in list --- fabric_cf/actor/core/policy/network_service_inventory.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fabric_cf/actor/core/policy/network_service_inventory.py b/fabric_cf/actor/core/policy/network_service_inventory.py index a6ba5529..004d3165 100644 --- a/fabric_cf/actor/core/policy/network_service_inventory.py +++ b/fabric_cf/actor/core/policy/network_service_inventory.py @@ -429,7 +429,8 @@ def _exclude_allocated_subnets(self, *, subnet_list: List, requested_ns_type: st f"Excluding already allocated IP4Subnet: " f"{allocated_sliver.get_gateway().subnet}" f" to res# {reservation.get_reservation_id()}") - subnet_list.remove(subnet_to_remove) + if subnet_to_remove in subnet_list: + subnet_list.remove(subnet_to_remove) elif allocated_sliver.get_type() == ServiceType.FABNetv4Ext: if allocated_sliver.labels is not None and allocated_sliver.labels.ipv4 is not None: @@ -439,7 +440,8 @@ def _exclude_allocated_subnets(self, *, subnet_list: List, requested_ns_type: st f"Excluding already allocated IP4: " f"{x}" f" to res# {reservation.get_reservation_id()}") - subnet_list.remove(subnet_to_remove) + if subnet_to_remove in subnet_list: + subnet_list.remove(subnet_to_remove) elif allocated_sliver.get_type() in Constants.L3_FABNETv6_SERVICES: subnet_to_remove = IPv6Network(allocated_sliver.get_gateway().subnet) @@ -448,7 +450,8 @@ def _exclude_allocated_subnets(self, *, subnet_list: List, requested_ns_type: st f"{allocated_sliver.get_gateway().subnet}" f" to res# {reservation.get_reservation_id()}") - subnet_list.remove(subnet_to_remove) + if subnet_to_remove in subnet_list: + subnet_list.remove(subnet_to_remove) self.logger.debug(f"Excluding already allocated subnet for reservation {reservation.get_reservation_id()}") From 09b55973d0a6edb16bb9965c781968e7da504ac5 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 12 Jul 2024 16:22:12 -0400 Subject: [PATCH 109/133] fix the logging for name and image --- fabric_cf/actor/core/common/event_logger.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fabric_cf/actor/core/common/event_logger.py b/fabric_cf/actor/core/common/event_logger.py index 15ea4fa2..9247ab1a 100644 --- a/fabric_cf/actor/core/common/event_logger.py +++ b/fabric_cf/actor/core/common/event_logger.py @@ -99,7 +99,7 @@ def log_sliver_event(self, *, slice_object: SliceAvro, sliver: BaseSliver, verb: owner = slice_object.get_owner() log_message = f"CFEL Sliver event slc:{slice_object.get_slice_id()} " \ - f"slvr:{sliver.get_reservation_info().reservation_id} of " \ + f"slvr:{sliver.get_reservation_info().reservation_id}/{sliver.get_name()} of " \ f"type {sliver.get_type()} {verb} " \ f"by prj:{slice_object.get_project_id()} usr:{owner.get_oidc_sub_claim()}" \ f":{owner.get_email()}" @@ -111,11 +111,11 @@ def log_sliver_event(self, *, slice_object: SliceAvro, sliver: BaseSliver, verb: if ssh_foot_print is not None: log_message += f" keys{ssh_foot_print}" - if isinstance(sliver, NodeSliver): - log_message += f"name: {sliver.get_name()} image: {sliver.get_image_ref()}" - log_message += f" {str(lc)}" + if isinstance(sliver, NodeSliver): + log_message += f" image: {sliver.get_image_ref()}" + self.logger.info(log_message) except Exception as e: traceback.print_exc() From 806d636e26b9f3b615c02c8130cfbe6d40f667d9 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 12 Jul 2024 16:30:07 -0400 Subject: [PATCH 110/133] remove extra space --- fabric_cf/actor/core/common/event_logger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric_cf/actor/core/common/event_logger.py b/fabric_cf/actor/core/common/event_logger.py index 9247ab1a..db467b69 100644 --- a/fabric_cf/actor/core/common/event_logger.py +++ b/fabric_cf/actor/core/common/event_logger.py @@ -114,7 +114,7 @@ def log_sliver_event(self, *, slice_object: SliceAvro, sliver: BaseSliver, verb: log_message += f" {str(lc)}" if isinstance(sliver, NodeSliver): - log_message += f" image: {sliver.get_image_ref()}" + log_message += f" image:{sliver.get_image_ref()}" self.logger.info(log_message) except Exception as e: From 104331b784fcc64ceea1ee91c3795e27209d3ea0 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 12 Jul 2024 16:36:43 -0400 Subject: [PATCH 111/133] clear previous error --- fabric_cf/actor/core/kernel/reservation_client.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fabric_cf/actor/core/kernel/reservation_client.py b/fabric_cf/actor/core/kernel/reservation_client.py index 060f6ed7..daebe098 100644 --- a/fabric_cf/actor/core/kernel/reservation_client.py +++ b/fabric_cf/actor/core/kernel/reservation_client.py @@ -295,6 +295,8 @@ def absorb_ticket_update(self, *, incoming: ABCReservationMixin, update_data: Up self.logger.debug(f"Authority {self.authority} Site Authority {site_authority}") assert self.authority.get_name() == site_authority.get_name() + # Clear error message from previous Extend operations + self.error_message = "" # Remember the current term and ticket term and absorb the incoming term self.previous_term = self.term From 73d87748c8eb169e65bfc18ee2685c0d2ac31073 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 12 Jul 2024 17:56:58 -0400 Subject: [PATCH 112/133] added more debug logs --- fabric_cf/actor/core/common/event_logger.py | 2 +- .../actor/core/kernel/reservation_client.py | 30 ++++++++++++------- .../policy/broker_simpler_units_policy.py | 4 ++- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/fabric_cf/actor/core/common/event_logger.py b/fabric_cf/actor/core/common/event_logger.py index db467b69..9908fda0 100644 --- a/fabric_cf/actor/core/common/event_logger.py +++ b/fabric_cf/actor/core/common/event_logger.py @@ -113,7 +113,7 @@ def log_sliver_event(self, *, slice_object: SliceAvro, sliver: BaseSliver, verb: log_message += f" {str(lc)}" - if isinstance(sliver, NodeSliver): + if isinstance(sliver, NodeSliver) and sliver.get_image_ref(): log_message += f" image:{sliver.get_image_ref()}" self.logger.info(log_message) diff --git a/fabric_cf/actor/core/kernel/reservation_client.py b/fabric_cf/actor/core/kernel/reservation_client.py index daebe098..7e0769a9 100644 --- a/fabric_cf/actor/core/kernel/reservation_client.py +++ b/fabric_cf/actor/core/kernel/reservation_client.py @@ -295,8 +295,6 @@ def absorb_ticket_update(self, *, incoming: ABCReservationMixin, update_data: Up self.logger.debug(f"Authority {self.authority} Site Authority {site_authority}") assert self.authority.get_name() == site_authority.get_name() - # Clear error message from previous Extend operations - self.error_message = "" # Remember the current term and ticket term and absorb the incoming term self.previous_term = self.term @@ -307,6 +305,12 @@ def absorb_ticket_update(self, *, incoming: ABCReservationMixin, update_data: Up self.resources.update(reservation=self, resource_set=incoming.get_resources()) self.logger.debug("absorb_update: {}".format(incoming)) + # Clear error message from previous Extend operations + self.error_message = "" + + #if self.resources.get_sliver().reservation_info: + # self.resources.get_sliver().reservation_info.error_message = self.error_message + self.policy.update_ticket_complete(reservation=self) def accept_lease_update(self, *, incoming: ABCReservationMixin, update_data: UpdateData) -> bool: @@ -625,9 +629,9 @@ def can_renew(self) -> bool: return self.last_ticket_update.successful() - def clear_notice(self, clear_fail: bool=False): - self.last_ticket_update.clear() - self.last_lease_update.clear() + def clear_notice(self, clear_fail: bool = False): + self.last_ticket_update.clear(clear_fail=clear_fail) + self.last_lease_update.clear(clear_fail=clear_fail) def do_relinquish(self): """ @@ -882,9 +886,11 @@ def get_last_ticket_update(self) -> str: if self.last_ticket_update is not None: if self.last_ticket_update.get_message() is not None and self.last_ticket_update.get_message() != "": result += f"{self.last_ticket_update.get_message()}, " - ev = self.last_ticket_update.get_events() - if ev is not None and ev != "": - result += f"events: {ev}, " + # Include events only in case of failure + if self.last_ticket_update.is_failed(): + ev = self.last_ticket_update.get_events() + if ev is not None and ev != "": + result += f"events: {ev}, " result = result[:-2] return result @@ -893,9 +899,11 @@ def get_last_lease_update(self) -> str: if self.last_lease_update is not None: if self.last_lease_update.get_message() is not None and self.last_lease_update.get_message() != "": result += f"{self.last_lease_update.get_message()}, " - ev = self.last_lease_update.get_events() - if ev is not None and ev != "": - result += f"events: {ev}, " + # Include events only in case of failure + if self.last_lease_update.is_failed(): + ev = self.last_lease_update.get_events() + if ev is not None and ev != "": + result += f"events: {ev}, " result = result[:-2] return result diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 4c31319c..13df5065 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -905,7 +905,7 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: error_msg = e.msg else: raise e - + self.logger.debug(f"Allocate Services returning: {delegation_id} {sliver} {error_msg}") return delegation_id, sliver, error_msg def __allocate_peered_interfaces(self, *, rid: ID, peered_interfaces: List[InterfaceSliver], owner_switch: NodeSliver, @@ -1022,11 +1022,13 @@ def ticket_inventory(self, *, reservation: ABCBrokerReservation, inv: InventoryF if node_id_to_reservations.get(node_id, None) is None: node_id_to_reservations[node_id] = ReservationSet() node_id_to_reservations[node_id].add(reservation=reservation) + self.logger.debug(f"Ticket Inventory returning: True {error_msg}") return True, node_id_to_reservations, error_msg except Exception as e: self.logger.error(traceback.format_exc()) self.logger.error(e) reservation.fail(message=str(e)) + self.logger.debug(f"Ticket Inventory returning: False {error_msg}") return False, node_id_to_reservations, error_msg def __is_modify_on_openstack_vnic(self, *, sliver: BaseSliver) -> bool: From 07c9cdd18b83b049e5bc3094ea3f1cc2c33b89e1 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 12 Jul 2024 22:39:26 -0400 Subject: [PATCH 113/133] ensure update ticket is sent to orchestrator on failed extend --- fabric_cf/actor/core/kernel/broker_reservation.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fabric_cf/actor/core/kernel/broker_reservation.py b/fabric_cf/actor/core/kernel/broker_reservation.py index 8cae5047..c54538d8 100644 --- a/fabric_cf/actor/core/kernel/broker_reservation.py +++ b/fabric_cf/actor/core/kernel/broker_reservation.py @@ -561,7 +561,9 @@ def handle_failed_rpc(self, *, failed: FailedRPC): super().handle_failed_rpc(failed=failed) def fail_extend(self, *, message: str, exception: Exception = None): + self.logger.debug(f"Failed Extend: {message}") self.extend_failure = True + self.notified_failed = False super().fail(message=message, exception=exception) From 4285c58f36758e2393481badca30936c6f23dd10 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 12 Jul 2024 23:09:15 -0400 Subject: [PATCH 114/133] get reservations filtered on ip subnet or host --- .../core/apis/abc_actor_management_object.py | 6 +++- fabric_cf/actor/core/apis/abc_mgmt_actor.py | 5 +++- .../core/manage/actor_management_object.py | 5 ++-- .../actor/core/manage/kafka/kafka_actor.py | 5 ++-- .../actor/core/manage/kafka/kafka_proxy.py | 4 ++- .../actor/core/manage/local/local_actor.py | 6 ++-- .../actor/core/plugins/db/actor_database.py | 28 +++++++++++++++-- fabric_cf/actor/db/__init__.py | 2 ++ fabric_cf/actor/db/psql_database.py | 30 +++++++++++++++---- 9 files changed, 74 insertions(+), 17 deletions(-) diff --git a/fabric_cf/actor/core/apis/abc_actor_management_object.py b/fabric_cf/actor/core/apis/abc_actor_management_object.py index 80798567..9e8a807f 100644 --- a/fabric_cf/actor/core/apis/abc_actor_management_object.py +++ b/fabric_cf/actor/core/apis/abc_actor_management_object.py @@ -238,7 +238,8 @@ def get_sites(self, *, caller: AuthToken, site: str) -> ResultSitesAvro: def get_reservations(self, *, caller: AuthToken, states: List[int] = None, slice_id: ID = None, rid: ID = None, oidc_claim_sub: str = None, email: str = None, rid_list: List[str] = None, type: str = None, - site: str = None, node_id: str = None) -> ResultReservationAvro: + site: str = None, node_id: str = None, + host: str = None, ip_subnet: str = None) -> ResultReservationAvro: """ Get Reservations @param states states @@ -252,6 +253,9 @@ def get_reservations(self, *, caller: AuthToken, states: List[int] = None, @param node_id node id Obtains all reservations with error information in case of failure @param caller caller + @param host host + @param ip_subnet ip subnet + @return returns list of the reservations """ diff --git a/fabric_cf/actor/core/apis/abc_mgmt_actor.py b/fabric_cf/actor/core/apis/abc_mgmt_actor.py index 8a0739d9..daf17a32 100644 --- a/fabric_cf/actor/core/apis/abc_mgmt_actor.py +++ b/fabric_cf/actor/core/apis/abc_mgmt_actor.py @@ -150,7 +150,8 @@ def accept_update_slice(self, *, slice_id: ID) -> bool: @abstractmethod def get_reservations(self, *, states: List[int] = None, slice_id: ID = None, rid: ID = None, oidc_claim_sub: str = None, email: str = None, rid_list: List[str] = None, - type: str = None, site: str = None, node_id: str = None) -> List[ReservationMng]: + type: str = None, site: str = None, node_id: str = None, + host: str = None, ip_subnet: str = None) -> List[ReservationMng]: """ Get Reservations @param states states @@ -162,6 +163,8 @@ def get_reservations(self, *, states: List[int] = None, slice_id: ID = None, @param type type of reservations like NodeSliver/NetworkServiceSliver @param site site @param node_id node id + @param ip_subnet ip subnet + @param host host Obtains all reservations @return returns list of the reservations """ diff --git a/fabric_cf/actor/core/manage/actor_management_object.py b/fabric_cf/actor/core/manage/actor_management_object.py index 95b59526..8f304837 100644 --- a/fabric_cf/actor/core/manage/actor_management_object.py +++ b/fabric_cf/actor/core/manage/actor_management_object.py @@ -430,7 +430,8 @@ def get_sites(self, *, caller: AuthToken, site: str) -> ResultSitesAvro: def get_reservations(self, *, caller: AuthToken, states: List[int] = None, slice_id: ID = None, rid: ID = None, oidc_claim_sub: str = None, email: str = None, rid_list: List[str] = None, type: str = None, - site: str = None, node_id: str = None) -> ResultReservationAvro: + site: str = None, node_id: str = None, host: str = None, + ip_subnet: str = None) -> ResultReservationAvro: result = ResultReservationAvro() result.status = ResultAvro() @@ -450,7 +451,7 @@ def get_reservations(self, *, caller: AuthToken, states: List[int] = None, else: res_list = self.db.get_reservations(slice_id=slice_id, rid=rid, email=email, states=states, rsv_type=rsv_type, site=site, - graph_node_id=node_id) + graph_node_id=node_id, host=host, ip_subnet=ip_subnet) except Exception as e: self.logger.error("getReservations:db access {}".format(e)) result.status.set_code(ErrorCodes.ErrorDatabaseError.value) diff --git a/fabric_cf/actor/core/manage/kafka/kafka_actor.py b/fabric_cf/actor/core/manage/kafka/kafka_actor.py index cd605cc6..129d7ee5 100644 --- a/fabric_cf/actor/core/manage/kafka/kafka_actor.py +++ b/fabric_cf/actor/core/manage/kafka/kafka_actor.py @@ -131,11 +131,12 @@ def delete_slice(self, *, slice_id: ID) -> bool: def get_reservations(self, *, states: List[int] = None, slice_id: ID = None, rid: ID = None, oidc_claim_sub: str = None, email: str = None, rid_list: List[str] = None, - type: str = None, site: str = None, node_id: str = None) -> List[ReservationMng]: + type: str = None, site: str = None, node_id: str = None, + host: str = None, ip_subnet: str = None) -> List[ReservationMng]: request = GetReservationsRequestAvro() request = self.fill_request_by_id_message(request=request, slice_id=slice_id, states=states, email=email, rid=rid, - type=type, site=site) + type=type, site=site, host=host, ip_subnet=ip_subnet) status, response = self.send_request(request) if status.code == 0: diff --git a/fabric_cf/actor/core/manage/kafka/kafka_proxy.py b/fabric_cf/actor/core/manage/kafka/kafka_proxy.py index c6bc9501..a45c76cc 100644 --- a/fabric_cf/actor/core/manage/kafka/kafka_proxy.py +++ b/fabric_cf/actor/core/manage/kafka/kafka_proxy.py @@ -93,7 +93,7 @@ def get_type_id(self) -> str: def fill_request_by_id_message(self, request: RequestByIdRecord, id_token: str = None, email: str = None, slice_id: ID = None, slice_name: str = None, states: List[int] = None, rid: ID = None, delegation_id: str = None, broker_id: ID = None, type: str = None, - site: str = None): + site: str = None, host: str = None, ip_subnet: str = None): request.guid = str(self.management_id) request.auth = self.auth request.callback_topic = self.callback_topic @@ -104,6 +104,8 @@ def fill_request_by_id_message(self, request: RequestByIdRecord, id_token: str = request.delegation_id = delegation_id request.site = site request.type = type + request.ip_subnet = ip_subnet + request.host = host if slice_id is not None: request.slice_id = str(slice_id) if rid is not None: diff --git a/fabric_cf/actor/core/manage/local/local_actor.py b/fabric_cf/actor/core/manage/local/local_actor.py index cdeea385..ae80b11e 100644 --- a/fabric_cf/actor/core/manage/local/local_actor.py +++ b/fabric_cf/actor/core/manage/local/local_actor.py @@ -110,12 +110,14 @@ def remove_slice(self, *, slice_id: ID) -> bool: def get_reservations(self, *, states: List[int] = None, slice_id: ID = None, rid: ID = None, oidc_claim_sub: str = None, email: str = None, rid_list: List[str] = None, - type: str = None, site: str = None, node_id: str = None) -> List[ReservationMng]: + type: str = None, site: str = None, node_id: str = None, + host: str = None, ip_subnet: str = None) -> List[ReservationMng]: self.clear_last() try: result = self.manager.get_reservations(caller=self.auth, states=states, slice_id=slice_id, rid=rid, oidc_claim_sub=oidc_claim_sub, email=email, rid_list=rid_list, - type=type, site=site, node_id=node_id) + type=type, site=site, node_id=node_id, host=host, + ip_subnet=ip_subnet) self.last_status = result.status if result.status.get_code() == 0: diff --git a/fabric_cf/actor/core/plugins/db/actor_database.py b/fabric_cf/actor/core/plugins/db/actor_database.py index e70c7b8d..f1bfb380 100644 --- a/fabric_cf/actor/core/plugins/db/actor_database.py +++ b/fabric_cf/actor/core/plugins/db/actor_database.py @@ -285,6 +285,8 @@ def add_reservation(self, *, reservation: ABCReservationMixin): site = None rsv_type = None components = None + host = None + ip_subnet = None if reservation.get_resources() is not None and reservation.get_resources().get_sliver() is not None: sliver = reservation.get_resources().get_sliver() site = sliver.get_site() @@ -293,6 +295,9 @@ def add_reservation(self, *, reservation: ABCReservationMixin): from fim.slivers.network_node import NodeSliver if isinstance(sliver, NetworkServiceSliver) and sliver.interface_info: + if sliver.get_gateway(): + ip_subnet = sliver.get_gateway().subnet + components = [] for interface in sliver.interface_info.interfaces.values(): graph_id_node_id_component_id, bqm_if_name = interface.get_node_map() @@ -307,6 +312,12 @@ def add_reservation(self, *, reservation: ABCReservationMixin): if node_id and comp_id and bdf: components.append((node_id, comp_id, bdf)) elif isinstance(sliver, NodeSliver) and sliver.attached_components_info: + if sliver.get_labels() and sliver.get_labels().instance_parent: + host = sliver.get_labels().instance_parent + if sliver.get_label_allocations() and sliver.get_label_allocations().instance_parent: + host = sliver.get_label_allocations().instance_parent + ip_subnet = sliver.get_management_ip() + node_id = reservation.get_graph_node_id() if node_id: components = [] @@ -333,7 +344,8 @@ def add_reservation(self, *, reservation: ABCReservationMixin): oidc_claim_sub=oidc_claim_sub, email=email, site=site, rsv_type=rsv_type, components=components, lease_start=term.get_start_time() if term else None, - lease_end=term.get_end_time() if term else None) + lease_end=term.get_end_time() if term else None, + host=host, ip_subnet=ip_subnet) self.logger.debug( "Reservation {} added to slice {}".format(reservation.get_reservation_id(), reservation.get_slice())) finally: @@ -353,6 +365,9 @@ def update_reservation(self, *, reservation: ABCReservationMixin): site = None rsv_type = None components = None + ip_subnet = None + host = None + if reservation.get_resources() is not None and reservation.get_resources().get_sliver() is not None: sliver = reservation.get_resources().get_sliver() site = sliver.get_site() @@ -360,6 +375,9 @@ def update_reservation(self, *, reservation: ABCReservationMixin): from fim.slivers.network_service import NetworkServiceSliver from fim.slivers.network_node import NodeSliver if isinstance(sliver, NetworkServiceSliver) and sliver.interface_info: + if sliver.get_gateway(): + ip_subnet = sliver.get_gateway().subnet + components = [] for interface in sliver.interface_info.interfaces.values(): graph_id_node_id_component_id, bqm_if_name = interface.get_node_map() @@ -374,6 +392,11 @@ def update_reservation(self, *, reservation: ABCReservationMixin): if node_id and comp_id and bdf: components.append((node_id, comp_id, bdf)) elif isinstance(sliver, NodeSliver) and sliver.attached_components_info: + if sliver.get_labels() and sliver.get_labels().instance_parent: + host = sliver.get_labels().instance_parent + if sliver.get_label_allocations() and sliver.get_label_allocations().instance_parent: + host = sliver.get_label_allocations().instance_parent + ip_subnet = sliver.get_management_ip() node_id = reservation.get_graph_node_id() if node_id: components = [] @@ -404,7 +427,8 @@ def update_reservation(self, *, reservation: ABCReservationMixin): rsv_graph_node_id=reservation.get_graph_node_id(), site=site, rsv_type=rsv_type, components=components, lease_start=term.get_start_time() if term else None, - lease_end=term.get_end_time() if term else None) + lease_end=term.get_end_time() if term else None, + ip_subnet=ip_subnet, host=host) diff = int(time.time() - begin) if diff > 0: self.logger.info(f"DB TIME: {diff}") diff --git a/fabric_cf/actor/db/__init__.py b/fabric_cf/actor/db/__init__.py index 8254565e..f6b48c73 100644 --- a/fabric_cf/actor/db/__init__.py +++ b/fabric_cf/actor/db/__init__.py @@ -125,6 +125,8 @@ class Reservations(Base): rsv_slc_id = Column(Integer, ForeignKey(FOREIGN_KEY_SLICE_ID), index=True) rsv_resid = Column(String, nullable=False, index=True) oidc_claim_sub = Column(String, nullable=True, index=True) + host = Column(String, nullable=True, index=True) + ip_subnet = Column(String, nullable=True, index=True) email = Column(String, nullable=True, index=True) project_id = Column(String, nullable=True, index=True) site = Column(String, nullable=True, index=True) diff --git a/fabric_cf/actor/db/psql_database.py b/fabric_cf/actor/db/psql_database.py index 11c1c122..fc4cfb0b 100644 --- a/fabric_cf/actor/db/psql_database.py +++ b/fabric_cf/actor/db/psql_database.py @@ -656,7 +656,7 @@ def add_reservation(self, *, slc_guid: str, rsv_resid: str, rsv_category: int, r rsv_pending: int, rsv_joining: int, properties, lease_start: datetime = None, lease_end: datetime = None, rsv_graph_node_id: str = None, oidc_claim_sub: str = None, email: str = None, project_id: str = None, site: str = None, rsv_type: str = None, - components: List[Tuple[str, str, str]] = None): + components: List[Tuple[str, str, str]] = None, host: str = None, ip_subnet: str = None): """ Add a reservation @param slc_guid slice guid @@ -675,6 +675,8 @@ def add_reservation(self, *, slc_guid: str, rsv_resid: str, rsv_category: int, r @param site site @param rsv_type reservation type @param components list of components + @param host host + @param ip_subnet ip_subnet """ session = self.get_session() try: @@ -683,7 +685,7 @@ def add_reservation(self, *, slc_guid: str, rsv_resid: str, rsv_category: int, r rsv_state=rsv_state, rsv_pending=rsv_pending, rsv_joining=rsv_joining, lease_start=lease_start, lease_end=lease_end, properties=properties, oidc_claim_sub=oidc_claim_sub, email=email, - project_id=project_id, site=site, rsv_type=rsv_type) + project_id=project_id, site=site, rsv_type=rsv_type, host=host, ip_subnet=ip_subnet) if rsv_graph_node_id is not None: rsv_obj.rsv_graph_node_id = rsv_graph_node_id @@ -704,7 +706,8 @@ def add_reservation(self, *, slc_guid: str, rsv_resid: str, rsv_category: int, r def update_reservation(self, *, slc_guid: str, rsv_resid: str, rsv_category: int, rsv_state: int, rsv_pending: int, rsv_joining: int, properties, lease_start: datetime = None, lease_end: datetime = None, rsv_graph_node_id: str = None, site: str = None, - rsv_type: str = None, components: List[Tuple[str, str, str]] = None): + rsv_type: str = None, components: List[Tuple[str, str, str]] = None, + host: str = None, ip_subnet: str = None): """ Update a reservation @param slc_guid slice guid @@ -720,6 +723,8 @@ def update_reservation(self, *, slc_guid: str, rsv_resid: str, rsv_category: int @param site site @param rsv_type reservation type @param components list of components + @param ip_subnet ip subnet + @param host host """ session = self.get_session() try: @@ -732,6 +737,10 @@ def update_reservation(self, *, slc_guid: str, rsv_resid: str, rsv_category: int rsv_obj.properties = properties rsv_obj.lease_end = lease_end rsv_obj.lease_start = lease_start + if host: + rsv_obj.host = host + if ip_subnet: + rsv_obj.ip_subnet = ip_subnet if site is not None: rsv_obj.site = site if rsv_graph_node_id is not None: @@ -797,7 +806,8 @@ def remove_reservation(self, *, rsv_resid: str): raise e def create_reservation_filter(self, *, slice_id: str = None, graph_node_id: str = None, project_id: str = None, - email: str = None, oidc_sub: str = None, rid: str = None, site: str = None) -> dict: + email: str = None, oidc_sub: str = None, rid: str = None, site: str = None, + ip_subnet: str = None, host: str = None) -> dict: filter_dict = {} if slice_id is not None: @@ -815,12 +825,18 @@ def create_reservation_filter(self, *, slice_id: str = None, graph_node_id: str filter_dict['rsv_resid'] = rid if site is not None: filter_dict['site'] = site + if ip_subnet: + filter_dict['ip_subnet'] = ip_subnet + if host: + filter_dict['host'] = host + return filter_dict def get_reservations(self, *, slice_id: str = None, graph_node_id: str = None, project_id: str = None, email: str = None, oidc_sub: str = None, rid: str = None, states: list[int] = None, category: list[int] = None, site: str = None, rsv_type: list[str] = None, - start: datetime = None, end: datetime = None) -> List[dict]: + start: datetime = None, end: datetime = None, ip_subnet: str = None, + host: str = None) -> List[dict]: """ Get Reservations for an actor @param slice_id slice id @@ -835,6 +851,8 @@ def get_reservations(self, *, slice_id: str = None, graph_node_id: str = None, p @param rsv_type rsv_type @param start search for slivers with lease_end_time after start @param end search for slivers with lease_end_time before end + @param ip_subnet ip subnet + @param host host @return list of reservations """ @@ -843,7 +861,7 @@ def get_reservations(self, *, slice_id: str = None, graph_node_id: str = None, p try: filter_dict = self.create_reservation_filter(slice_id=slice_id, graph_node_id=graph_node_id, project_id=project_id, email=email, oidc_sub=oidc_sub, - rid=rid, site=site) + rid=rid, site=site, ip_subnet=ip_subnet, host=host) rows = session.query(Reservations).filter_by(**filter_dict) if rsv_type is not None: From cc8d38ea63eca92c82093d1abcc598a5ff6c0bb2 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 12 Jul 2024 23:18:20 -0400 Subject: [PATCH 115/133] get reservations filtered on ip subnet or host --- fabric_cf/actor/db/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fabric_cf/actor/db/__init__.py b/fabric_cf/actor/db/__init__.py index f6b48c73..11f85f9b 100644 --- a/fabric_cf/actor/db/__init__.py +++ b/fabric_cf/actor/db/__init__.py @@ -145,6 +145,8 @@ class Reservations(Base): Index('idx_resid_state', rsv_resid, rsv_state) Index('idx_slcid_state', rsv_slc_id, rsv_state) Index('idx_graph_id_res_id', rsv_graph_node_id, rsv_resid) + Index('idx_host', host) + Index('idx_ip_subnet', ip_subnet) class Slices(Base): From ff0c25f22d2ef5855ced60ae21b3c33785565bf8 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 12 Jul 2024 23:21:11 -0400 Subject: [PATCH 116/133] update upgrade script --- psql.upgrade | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/psql.upgrade b/psql.upgrade index a6a6e9b9..05603177 100644 --- a/psql.upgrade +++ b/psql.upgrade @@ -78,4 +78,10 @@ CREATE TABLE IF NOT EXISTS "Metrics" ( user_id VARCHAR NOT NULL, project_id VARCHAR NOT NULL, slice_count INTEGER NOT NULL, -); \ No newline at end of file +); + +ALTER TABLE "Reservations" ADD COLUMN host VARCHAR(255) NULL; +ALTER TABLE "Reservations" ADD COLUMN ip_subnet VARCHAR(255) NULL; + +CREATE INDEX idx_host ON "Reservations"(host); +CREATE INDEX idx_ip_subnet ON "Reservations"(ip_subnet); \ No newline at end of file From 255299537e9efe6f0f24b4ada36f1b62a1d1cbed Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Fri, 12 Jul 2024 23:30:01 -0400 Subject: [PATCH 117/133] get based on host and subnet --- fabric_cf/actor/core/apis/abc_database.py | 2 +- fabric_cf/actor/core/plugins/db/actor_database.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fabric_cf/actor/core/apis/abc_database.py b/fabric_cf/actor/core/apis/abc_database.py index d6386ad3..fd6a0245 100644 --- a/fabric_cf/actor/core/apis/abc_database.py +++ b/fabric_cf/actor/core/apis/abc_database.py @@ -161,7 +161,7 @@ def update_slice(self, *, slice_object: ABCSlice): def get_reservations(self, *, slice_id: ID = None, graph_node_id: str = None, project_id: str = None, email: str = None, oidc_sub: str = None, rid: ID = None, states: list[int] = None, site: str = None, rsv_type: list[str] = None, start: datetime = None, - end: datetime = None) -> List[ABCReservationMixin]: + end: datetime = None, ip_subnet: str = None, host: str = None) -> List[ABCReservationMixin]: """ Retrieves the reservations. diff --git a/fabric_cf/actor/core/plugins/db/actor_database.py b/fabric_cf/actor/core/plugins/db/actor_database.py index f1bfb380..e3f48ccb 100644 --- a/fabric_cf/actor/core/plugins/db/actor_database.py +++ b/fabric_cf/actor/core/plugins/db/actor_database.py @@ -579,13 +579,13 @@ def get_components(self, *, node_id: str, states: list[int], rsv_type: list[str] def get_reservations(self, *, slice_id: ID = None, graph_node_id: str = None, project_id: str = None, email: str = None, oidc_sub: str = None, rid: ID = None, states: list[int] = None, site: str = None, rsv_type: list[str] = None, start: datetime = None, - end: datetime = None) -> List[ABCReservationMixin]: + end: datetime = None, ip_subnet: str = None, host: str = None) -> List[ABCReservationMixin]: result = [] try: #self.lock.acquire() sid = str(slice_id) if slice_id is not None else None res_id = str(rid) if rid is not None else None - res_dict_list = self.db.get_reservations(slice_id=sid, graph_node_id=graph_node_id, + res_dict_list = self.db.get_reservations(slice_id=sid, graph_node_id=graph_node_id, host=host, ip_subnet=ip_subnet, project_id=project_id, email=email, oidc_sub=oidc_sub, rid=res_id, states=states, site=site, rsv_type=rsv_type, start=start, end=end) if self.lock.locked(): From 8333e53dc8b209446141e274685b265baaa02930 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Sun, 14 Jul 2024 14:48:22 -0400 Subject: [PATCH 118/133] check for allocated VLANs on extend --- .../policy/broker_simpler_units_policy.py | 64 +++++++++++++- .../core/policy/network_service_inventory.py | 83 +++++++++++-------- 2 files changed, 109 insertions(+), 38 deletions(-) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 13df5065..4e87a360 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -69,6 +69,7 @@ from fabric_cf.actor.fim.plugins.broker.aggregate_bqm_plugin import AggregatedBQMPlugin from fabric_cf.actor.core.util.resource_type import ResourceType from fabric_cf.actor.core.policy.inventory_for_type import InventoryForType +from fim.slivers.interface_info import InterfaceSliver if TYPE_CHECKING: from fabric_cf.actor.core.apis.abc_broker_mixin import ABCBrokerMixin @@ -654,14 +655,55 @@ def __allocate_nodes(self, *, reservation: ABCBrokerReservation, inv: NetworkNod inv=inv, reservation=reservation, term=term, sliver=sliver, operation=operation) + def __can_extend_interface_sliver(self, rid: ID, inv: NetworkServiceInventory, + ifs: InterfaceSliver, sliver: NetworkServiceSliver, + node_id_to_reservations: dict, term: Term): + """ + Checks if VLAN attached to an interface are assigned to any advanced reservations in this case + @param rid + @param inv + @param ifs + @param sliver + @param node_id_to_reservations + @param term + + @raises BrokerException in case VLAN is already assigned to any future sliver + """ + node_id, bqm_node_id = ifs.get_node_map() + bqm_cp = self.get_interface_sliver_from_graph(node_id=bqm_node_id) + owner_switch, owner_mpls, owner_ns = self.get_owners(node_id=bqm_node_id, ns_type=sliver.get_type()) + + # Handle IPV6Ext services + owner_ns_id = owner_ns.node_id.replace('ipv6ext-ns', + 'ipv6-ns') if 'ipv6ext-ns' in owner_ns.node_id else owner_ns.node_id + + existing_reservations = self.get_existing_reservations( + node_id=owner_ns_id, + node_id_to_reservations=node_id_to_reservations, + start=term.get_start_time(), + end=term.get_end_time(), + ) + + inv.allocate_ifs( + rid=rid, + requested_ns=sliver, + requested_ifs=ifs, + owner_ns=owner_ns, + bqm_ifs=bqm_cp, + existing_reservations=existing_reservations, + operation=ReservationOperation.Extend + ) + def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: NetworkServiceSliver, - node_id_to_reservations: dict, term: Term) -> Tuple[str, BaseSliver, Any]: + node_id_to_reservations: dict, term: Term, + operation: ReservationOperation = ReservationOperation.Create) -> Tuple[str, BaseSliver, Any]: """ Allocate Network Service Slivers @param rid Reservation Id @param inv Inventory @param sliver Requested sliver @param node_id_to_reservations + @param operation @return tuple containing delegation id, sliver, error message if any """ delegation_id = None @@ -687,6 +729,10 @@ def __allocate_services(self, *, rid: ID, inv: NetworkServiceInventory, sliver: # Skipping the already allocated interface on a modify if self.combined_broker_model_graph_id in node_id: + + if operation == ReservationOperation.Extend: + self.__can_extend_interface_sliver(rid=rid, inv=inv, ifs=ifs, sliver=sliver, + node_id_to_reservations=node_id_to_reservations, term=term) continue if node_id == str(NodeType.Facility): @@ -1006,7 +1052,7 @@ def ticket_inventory(self, *, reservation: ABCBrokerReservation, inv: InventoryF delegation_id, sliver, error_msg = self.__allocate_services(rid=reservation.get_reservation_id(), inv=inv, sliver=res_sliver, node_id_to_reservations=node_id_to_reservations, - term=term) + term=term, operation=operation) else: self.logger.error(f'Reservation {reservation} sliver type is neither Node, nor NetworkServiceSliver') raise BrokerException(msg=f"Reservation sliver type is neither Node " @@ -1440,6 +1486,20 @@ def get_owners(self, *, node_id: str, ns_type: ServiceType) -> Tuple[NodeSliver, finally: self.lock.release() + def get_interface_sliver_from_graph(self, *, node_id: str) -> InterfaceSliver or None: + """ + Get InterfaceSliver from CBM + :param node_id: + :return: + """ + try: + self.lock.acquire() + if self.combined_broker_model is None: + return None + return self.combined_broker_model.build_deep_interface_sliver(node_id=node_id) + finally: + self.lock.release() + def get_network_node_from_graph(self, *, node_id: str) -> NodeSliver or None: """ Get Node from CBM diff --git a/fabric_cf/actor/core/policy/network_service_inventory.py b/fabric_cf/actor/core/policy/network_service_inventory.py index 004d3165..9174cfd6 100644 --- a/fabric_cf/actor/core/policy/network_service_inventory.py +++ b/fabric_cf/actor/core/policy/network_service_inventory.py @@ -38,6 +38,7 @@ from fabric_cf.actor.core.apis.abc_reservation_mixin import ABCReservationMixin from fabric_cf.actor.core.common.constants import Constants from fabric_cf.actor.core.common.exceptions import BrokerException, ExceptionErrorCode +from fabric_cf.actor.core.kernel.reservation_states import ReservationOperation from fabric_cf.actor.core.policy.inventory_for_type import InventoryForType from fabric_cf.actor.core.util.id import ID @@ -115,7 +116,8 @@ def __exclude_allocated_vlans(self, *, rid: ID, available_vlan_range: List[int], def allocate_ifs(self, *, rid: ID, requested_ns: NetworkServiceSliver, requested_ifs: InterfaceSliver, owner_ns: NetworkServiceSliver, bqm_ifs: InterfaceSliver, - existing_reservations: List[ABCReservationMixin]) -> InterfaceSliver: + existing_reservations: List[ABCReservationMixin], + operation: ReservationOperation = ReservationOperation.Create) -> InterfaceSliver: """ Allocate Interface Sliver - For L2 services, validate the VLAN tag specified is within the allowed range @@ -129,13 +131,13 @@ def allocate_ifs(self, *, rid: ID, requested_ns: NetworkServiceSliver, requested :param owner_ns: BQM NetworkService identified to serve the InterfaceSliver :param bqm_ifs: BQM InterfaceSliver identified to serve the InterfaceSliver :param existing_reservations: Existing Reservations which also are served by the owner switch + :param operation: Extend/Create/Modify Operation :return Interface Sliver updated with the allocated VLAN tag for FABNetv4 and FABNetv6 services :raises Exception if vlan tag range is not in the valid range for L2 services - Return the sliver updated with the VLAN """ + requested_vlan = None if requested_ns.get_layer() == NSLayer.L2: - requested_vlan = None - if requested_ifs.labels is not None and requested_ifs.labels.vlan is not None: + if requested_ifs.labels and requested_ifs.labels.vlan: requested_vlan = int(requested_ifs.labels.vlan) # Validate the requested VLAN is in range specified on MPLS Network Service in BQM @@ -145,69 +147,78 @@ def allocate_ifs(self, *, rid: ID, requested_ns: NetworkServiceSliver, requested return requested_ifs if owner_ns.get_label_delegations() is None: - if 1 > requested_vlan > 4095: - raise BrokerException(error_code=ExceptionErrorCode.FAILURE, - msg=f"Vlan for L2 service {requested_vlan} " - f"is outside the allowed range 1-4095") - else: - return requested_ifs + if not (1 <= requested_vlan <= 4095): + raise BrokerException( + error_code=ExceptionErrorCode.FAILURE, + msg=f"Vlan for L2 service {requested_vlan} is outside the allowed range 1-4095" + ) + return requested_ifs delegation_id, delegated_label = self.get_delegations(lab_cap_delegations=owner_ns.get_label_delegations()) vlan_range = self.__extract_vlan_range(labels=delegated_label) - if vlan_range is not None and requested_vlan not in vlan_range: - raise BrokerException(error_code=ExceptionErrorCode.FAILURE, - msg=f"Vlan for L2 service {requested_vlan} is outside the available range " - f"{vlan_range}") + if vlan_range and requested_vlan not in vlan_range: + raise BrokerException( + error_code=ExceptionErrorCode.FAILURE, + msg=f"Vlan for L2 service {requested_vlan} is outside the available range {vlan_range}" + ) - # Validate the VLANs vlan_range = self.__extract_vlan_range(labels=bqm_ifs.labels) - if vlan_range is not None: - vlan_range = self.__exclude_allocated_vlans(rid=rid, available_vlan_range=vlan_range, bqm_ifs=bqm_ifs, + if vlan_range: + vlan_range = self.__exclude_allocated_vlans(rid=rid, available_vlan_range=vlan_range, + bqm_ifs=bqm_ifs, existing_reservations=existing_reservations) + if operation == ReservationOperation.Extend and requested_vlan not in vlan_range: + raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, + msg=f"Renew failed: VLAN {requested_vlan} for Interface : " + f"{requested_ifs.get_name()/bqm_ifs.node_id} already in " + f"use by another reservation") + if requested_vlan is None: requested_ifs.labels.vlan = str(random.choice(vlan_range)) - #requested_ifs.labels.vlan = str(vlan_range[0]) return requested_ifs if requested_vlan not in vlan_range: - raise BrokerException(error_code=ExceptionErrorCode.FAILURE, - msg=f"Vlan for L2 service {requested_vlan} is outside the available range " - f"{vlan_range}") - + raise BrokerException( + error_code=ExceptionErrorCode.FAILURE, + msg=f"Vlan for L2 service {requested_vlan} is outside the available range {vlan_range}" + ) else: - # Grab Label Delegations - delegation_id, delegated_label = self.get_delegations( - lab_cap_delegations=owner_ns.get_label_delegations()) + delegation_id, delegated_label = self.get_delegations(lab_cap_delegations=owner_ns.get_label_delegations()) - # Get the VLAN range if bqm_ifs.get_type() != InterfaceType.FacilityPort: vlan_range = self.__extract_vlan_range(labels=delegated_label) else: vlan_range = self.__extract_vlan_range(labels=bqm_ifs.labels) - if vlan_range is not None: - vlan_range = self.__exclude_allocated_vlans(rid=rid, available_vlan_range=vlan_range, bqm_ifs=bqm_ifs, + if vlan_range: + vlan_range = self.__exclude_allocated_vlans(rid=rid, available_vlan_range=vlan_range, + bqm_ifs=bqm_ifs, existing_reservations=existing_reservations) + + if operation == ReservationOperation.Extend and requested_vlan not in vlan_range: + raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, + msg=f"Renew failed: VLAN {requested_vlan} for Interface : " + f"{requested_ifs.get_name()/bqm_ifs.node_id} already in " + f"use by another reservation") + if bqm_ifs.get_type() != InterfaceType.FacilityPort: - # Allocate the first available VLAN requested_ifs.labels.vlan = str(random.choice(vlan_range)) - #requested_ifs.labels.vlan = str(vlan_range[0]) requested_ifs.label_allocations = Labels(vlan=requested_ifs.labels.vlan) else: - if requested_ifs.labels is None: + if not requested_ifs.labels: return requested_ifs if requested_ifs.labels.vlan is None: requested_ifs.labels.vlan = str(random.choice(vlan_range)) - #requested_ifs.labels.vlan = str(vlan_range[0]) if int(requested_ifs.labels.vlan) not in vlan_range: - raise BrokerException(error_code=ExceptionErrorCode.FAILURE, - msg=f"Vlan for L3 service {requested_ifs.labels.vlan} " - f"is outside the available range " - f"{vlan_range}") + raise BrokerException( + error_code=ExceptionErrorCode.FAILURE, + msg=f"Vlan for L3 service {requested_ifs.labels.vlan} is outside the " + f"available range {vlan_range}" + ) return requested_ifs From a2b5f78a0d4cd259a53644117885adc8f73b1b37 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Sun, 14 Jul 2024 14:57:19 -0400 Subject: [PATCH 119/133] check for allocated VLANs on extend --- fabric_cf/actor/core/plugins/db/actor_database.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fabric_cf/actor/core/plugins/db/actor_database.py b/fabric_cf/actor/core/plugins/db/actor_database.py index e3f48ccb..d2e5604a 100644 --- a/fabric_cf/actor/core/plugins/db/actor_database.py +++ b/fabric_cf/actor/core/plugins/db/actor_database.py @@ -316,7 +316,8 @@ def add_reservation(self, *, reservation: ABCReservationMixin): host = sliver.get_labels().instance_parent if sliver.get_label_allocations() and sliver.get_label_allocations().instance_parent: host = sliver.get_label_allocations().instance_parent - ip_subnet = sliver.get_management_ip() + if sliver.get_management_ip(): + ip_subnet = sliver.get_management_ip() node_id = reservation.get_graph_node_id() if node_id: From 4b0120f47e8aef7ec92dff0d4a6331ca1a3efa48 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Sun, 14 Jul 2024 15:51:13 -0400 Subject: [PATCH 120/133] extract labels from NS only for non facility port services --- .../actor/core/policy/broker_simpler_units_policy.py | 11 ++++++++--- .../actor/core/policy/network_service_inventory.py | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py index 4e87a360..bac382bb 100644 --- a/fabric_cf/actor/core/policy/broker_simpler_units_policy.py +++ b/fabric_cf/actor/core/policy/broker_simpler_units_policy.py @@ -669,16 +669,21 @@ def __can_extend_interface_sliver(self, rid: ID, inv: NetworkServiceInventory, @raises BrokerException in case VLAN is already assigned to any future sliver """ + ns_node_id, ns_bqm_node_id = sliver.get_node_map() node_id, bqm_node_id = ifs.get_node_map() bqm_cp = self.get_interface_sliver_from_graph(node_id=bqm_node_id) + self.logger.debug(f"BQM IFS: {bqm_cp}") owner_switch, owner_mpls, owner_ns = self.get_owners(node_id=bqm_node_id, ns_type=sliver.get_type()) + self.logger.debug(f"Owner SWITCH: {owner_switch}") + self.logger.debug(f"Owner MPLS: {owner_mpls}") + self.logger.debug(f"Owner NS: {owner_ns}") # Handle IPV6Ext services - owner_ns_id = owner_ns.node_id.replace('ipv6ext-ns', - 'ipv6-ns') if 'ipv6ext-ns' in owner_ns.node_id else owner_ns.node_id + ns_bqm_node_id = ns_bqm_node_id.node_id.replace('ipv6ext-ns', + 'ipv6-ns') if 'ipv6ext-ns' in ns_bqm_node_id else ns_bqm_node_id existing_reservations = self.get_existing_reservations( - node_id=owner_ns_id, + node_id=ns_bqm_node_id, node_id_to_reservations=node_id_to_reservations, start=term.get_start_time(), end=term.get_end_time(), diff --git a/fabric_cf/actor/core/policy/network_service_inventory.py b/fabric_cf/actor/core/policy/network_service_inventory.py index 9174cfd6..51dcbff5 100644 --- a/fabric_cf/actor/core/policy/network_service_inventory.py +++ b/fabric_cf/actor/core/policy/network_service_inventory.py @@ -185,9 +185,9 @@ def allocate_ifs(self, *, rid: ID, requested_ns: NetworkServiceSliver, requested msg=f"Vlan for L2 service {requested_vlan} is outside the available range {vlan_range}" ) else: - delegation_id, delegated_label = self.get_delegations(lab_cap_delegations=owner_ns.get_label_delegations()) - if bqm_ifs.get_type() != InterfaceType.FacilityPort: + delegation_id, delegated_label = self.get_delegations( + lab_cap_delegations=owner_ns.get_label_delegations()) vlan_range = self.__extract_vlan_range(labels=delegated_label) else: vlan_range = self.__extract_vlan_range(labels=bqm_ifs.labels) From 8250b51ef85d41553b1393574d234746c7b1a339 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 15 Jul 2024 10:04:30 -0400 Subject: [PATCH 121/133] format the log message --- fabric_cf/actor/core/policy/network_service_inventory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric_cf/actor/core/policy/network_service_inventory.py b/fabric_cf/actor/core/policy/network_service_inventory.py index 51dcbff5..65dd2789 100644 --- a/fabric_cf/actor/core/policy/network_service_inventory.py +++ b/fabric_cf/actor/core/policy/network_service_inventory.py @@ -200,7 +200,7 @@ def allocate_ifs(self, *, rid: ID, requested_ns: NetworkServiceSliver, requested if operation == ReservationOperation.Extend and requested_vlan not in vlan_range: raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, msg=f"Renew failed: VLAN {requested_vlan} for Interface : " - f"{requested_ifs.get_name()/bqm_ifs.node_id} already in " + f"{requested_ifs.get_name()}/{bqm_ifs.node_id} already in " f"use by another reservation") if bqm_ifs.get_type() != InterfaceType.FacilityPort: From feb876cbd7010bcdb326f813f2d7e44f6cff2f69 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 15 Jul 2024 11:21:58 -0400 Subject: [PATCH 122/133] set requested vlan variable --- .../core/policy/network_service_inventory.py | 30 +++++++++++-------- pyproject.toml | 2 +- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/fabric_cf/actor/core/policy/network_service_inventory.py b/fabric_cf/actor/core/policy/network_service_inventory.py index 65dd2789..d5456b09 100644 --- a/fabric_cf/actor/core/policy/network_service_inventory.py +++ b/fabric_cf/actor/core/policy/network_service_inventory.py @@ -136,10 +136,10 @@ def allocate_ifs(self, *, rid: ID, requested_ns: NetworkServiceSliver, requested :raises Exception if vlan tag range is not in the valid range for L2 services """ requested_vlan = None - if requested_ns.get_layer() == NSLayer.L2: - if requested_ifs.labels and requested_ifs.labels.vlan: - requested_vlan = int(requested_ifs.labels.vlan) + if requested_ifs.labels and requested_ifs.labels.vlan: + requested_vlan = int(requested_ifs.labels.vlan) + if requested_ns.get_layer() == NSLayer.L2: # Validate the requested VLAN is in range specified on MPLS Network Service in BQM # Only do this for Non FacilityPorts if bqm_ifs.get_type() != InterfaceType.FacilityPort: @@ -169,11 +169,13 @@ def allocate_ifs(self, *, rid: ID, requested_ns: NetworkServiceSliver, requested bqm_ifs=bqm_ifs, existing_reservations=existing_reservations) - if operation == ReservationOperation.Extend and requested_vlan not in vlan_range: - raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, - msg=f"Renew failed: VLAN {requested_vlan} for Interface : " - f"{requested_ifs.get_name()/bqm_ifs.node_id} already in " - f"use by another reservation") + if operation == ReservationOperation.Extend: + if requested_vlan and requested_vlan not in vlan_range: + raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, + msg=f"Renew failed: VLAN {requested_vlan} for Interface : " + f"{requested_ifs.get_name()/bqm_ifs.node_id} already in " + f"use by another reservation") + return requested_ifs if requested_vlan is None: requested_ifs.labels.vlan = str(random.choice(vlan_range)) @@ -197,11 +199,13 @@ def allocate_ifs(self, *, rid: ID, requested_ns: NetworkServiceSliver, requested bqm_ifs=bqm_ifs, existing_reservations=existing_reservations) - if operation == ReservationOperation.Extend and requested_vlan not in vlan_range: - raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, - msg=f"Renew failed: VLAN {requested_vlan} for Interface : " - f"{requested_ifs.get_name()}/{bqm_ifs.node_id} already in " - f"use by another reservation") + if operation == ReservationOperation.Extend: + if requested_vlan and requested_vlan not in vlan_range: + raise BrokerException(error_code=ExceptionErrorCode.INSUFFICIENT_RESOURCES, + msg=f"Renew failed: VLAN {requested_vlan} for Interface : " + f"{requested_ifs.get_name()}/{bqm_ifs.node_id} already in " + f"use by another reservation") + return requested_ifs if bqm_ifs.get_type() != InterfaceType.FacilityPort: requested_ifs.labels.vlan = str(random.choice(vlan_range)) diff --git a/pyproject.toml b/pyproject.toml index b264cbc1..cdbbe8e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "PyYAML", "fabric_fss_utils==1.5.0", "fabric-message-bus==1.7.0b1", - "fabric-fim==1.7.0b14", + "fabric-fim==1.7.0", "fabric-credmgr-client==1.6.0", "ansible" ] From 7e413594e34ec39d4f47647bc8de2955ac7a7f66 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 15 Jul 2024 12:52:29 -0400 Subject: [PATCH 123/133] update dependencies --- fabric_cf/__init__.py | 2 +- pyproject.toml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fabric_cf/__init__.py b/fabric_cf/__init__.py index 5f2ae615..96e7bfe8 100644 --- a/fabric_cf/__init__.py +++ b/fabric_cf/__init__.py @@ -1,2 +1,2 @@ -__version__ = "1.7.0b1" +__version__ = "1.7.0rc0" __VERSION__ = __version__ diff --git a/pyproject.toml b/pyproject.toml index cdbbe8e3..df1f98eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,10 +26,10 @@ dependencies = [ "connexion==2.14.2", "swagger-ui-bundle==0.0.9", "PyYAML", - "fabric_fss_utils==1.5.0", - "fabric-message-bus==1.7.0b1", + "fabric_fss_utils==1.5.1", + "fabric-message-bus==1.7.0rc0", "fabric-fim==1.7.0", - "fabric-credmgr-client==1.6.0", + "fabric-credmgr-client==1.6.1", "ansible" ] From f95fc1ab2ba2973e9132224b3c945b8c222c89fa Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 15 Jul 2024 18:16:25 -0400 Subject: [PATCH 124/133] update images and version --- Dockerfile-auth | 2 +- fabric_cf/authority/vm_handler_config.yml | 25 +++++++++++++---------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Dockerfile-auth b/Dockerfile-auth index 2b00a9cb..f969eb06 100644 --- a/Dockerfile-auth +++ b/Dockerfile-auth @@ -1,7 +1,7 @@ FROM python:3.11.0 MAINTAINER Komal Thareja -ARG HANDLERS_VER=1.7.0b1 +ARG HANDLERS_VER=1.7.0rc0 RUN mkdir -p /usr/src/app WORKDIR /usr/src/app diff --git a/fabric_cf/authority/vm_handler_config.yml b/fabric_cf/authority/vm_handler_config.yml index 04e1c7ba..757f88e6 100644 --- a/fabric_cf/authority/vm_handler_config.yml +++ b/fabric_cf/authority/vm_handler_config.yml @@ -34,26 +34,29 @@ runtime: #max_flavor: fabric.c4.m8.d10 images: default_centos8_stream: centos - default_centos9_stream: cloud-user - default_centos_7: centos - default_centos_8: centos - default_debian_10: debian + default_centos9_stream: cloud-user default_debian_11: debian - default_fedora_35: fedora - default_fedora_36: fedora - default_fedora_37: fedora + default_debian_12: debian + default_fedora_39: fedora + default_fedora_40: fedora + default_freebsd_13_zfs: freebsd + default_freebsd_14_zfs: freebsd + default_kali: kali + default_openbsd_7: openbsd default_rocky_8: rocky default_rocky_9: rocky - default_ubuntu_18: ubuntu default_ubuntu_20: ubuntu - default_ubuntu_21: ubuntu default_ubuntu_22: ubuntu + default_ubuntu_24: ubuntu docker_rocky_8: rocky + docker_rocky_9: rocky docker_ubuntu_20: ubuntu docker_ubuntu_22: ubuntu + attestable_bmv2_v1_ubuntu_20: ubuntu + attestable_bmv2_v2_ubuntu_20: ubuntu playbooks: - location: /etc/fabric/actor/playbooks - inventory_location: /etc/fabric/actor/playbooks/inventory + location: fabric_am/playbooks + inventory_location: fabric_am/playbooks/inventory admin_ssh_key: /root/.ssh/id_rsa_nova VM: head_vm_provisioning.yml GPU: worker_pci_provisioning.yml From 9186ec914b5def5298a74fa727401e2f759af34d Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Mon, 15 Jul 2024 22:03:35 -0400 Subject: [PATCH 125/133] update config file --- fabric_cf/authority/vm_handler_config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fabric_cf/authority/vm_handler_config.yml b/fabric_cf/authority/vm_handler_config.yml index 757f88e6..85bcaa67 100644 --- a/fabric_cf/authority/vm_handler_config.yml +++ b/fabric_cf/authority/vm_handler_config.yml @@ -55,8 +55,8 @@ runtime: attestable_bmv2_v1_ubuntu_20: ubuntu attestable_bmv2_v2_ubuntu_20: ubuntu playbooks: - location: fabric_am/playbooks - inventory_location: fabric_am/playbooks/inventory + location: /etc/fabric/actor/playbooks + inventory_location: /etc/fabric/actor/playbooks/inventory admin_ssh_key: /root/.ssh/id_rsa_nova VM: head_vm_provisioning.yml GPU: worker_pci_provisioning.yml From 65690d63710e725d72fac7db7f2a83559e1aafca Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 16 Jul 2024 10:14:18 -0400 Subject: [PATCH 126/133] type to str before saving ip --- fabric_cf/actor/core/plugins/db/actor_database.py | 5 +++-- fabric_cf/orchestrator/core/orchestrator_handler.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/fabric_cf/actor/core/plugins/db/actor_database.py b/fabric_cf/actor/core/plugins/db/actor_database.py index d2e5604a..8e5b9683 100644 --- a/fabric_cf/actor/core/plugins/db/actor_database.py +++ b/fabric_cf/actor/core/plugins/db/actor_database.py @@ -317,7 +317,7 @@ def add_reservation(self, *, reservation: ABCReservationMixin): if sliver.get_label_allocations() and sliver.get_label_allocations().instance_parent: host = sliver.get_label_allocations().instance_parent if sliver.get_management_ip(): - ip_subnet = sliver.get_management_ip() + ip_subnet = str(sliver.get_management_ip()) node_id = reservation.get_graph_node_id() if node_id: @@ -397,7 +397,8 @@ def update_reservation(self, *, reservation: ABCReservationMixin): host = sliver.get_labels().instance_parent if sliver.get_label_allocations() and sliver.get_label_allocations().instance_parent: host = sliver.get_label_allocations().instance_parent - ip_subnet = sliver.get_management_ip() + if sliver.get_management_ip(): + ip_subnet = str(sliver.get_management_ip()) node_id = reservation.get_graph_node_id() if node_id: components = [] diff --git a/fabric_cf/orchestrator/core/orchestrator_handler.py b/fabric_cf/orchestrator/core/orchestrator_handler.py index 9ea5979a..39b2c99b 100644 --- a/fabric_cf/orchestrator/core/orchestrator_handler.py +++ b/fabric_cf/orchestrator/core/orchestrator_handler.py @@ -900,7 +900,7 @@ def poa(self, *, token: str, sliver_id: str, poa: PoaAvro) -> Tuple[str, str]: rid = ID(uid=sliver_id) if sliver_id is not None else None - fabric_token = self.__authorize_request(id_token=token, action_id=ActionId.modify) + fabric_token = self.__authorize_request(id_token=token, action_id=ActionId.POA) user_id = fabric_token.uuid project, tags, project_name = fabric_token.first_project From 1156b851215c69c1dabe6b663f4b3396dfab8fa0 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Tue, 16 Jul 2024 11:53:38 -0400 Subject: [PATCH 127/133] save fip as string --- fabric_cf/actor/core/plugins/db/actor_database.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fabric_cf/actor/core/plugins/db/actor_database.py b/fabric_cf/actor/core/plugins/db/actor_database.py index 8e5b9683..c073cb35 100644 --- a/fabric_cf/actor/core/plugins/db/actor_database.py +++ b/fabric_cf/actor/core/plugins/db/actor_database.py @@ -311,7 +311,7 @@ def add_reservation(self, *, reservation: ABCReservationMixin): bdf = ":".join(split_string[3:]) if len(split_string) > 3 else None if node_id and comp_id and bdf: components.append((node_id, comp_id, bdf)) - elif isinstance(sliver, NodeSliver) and sliver.attached_components_info: + elif isinstance(sliver, NodeSliver): if sliver.get_labels() and sliver.get_labels().instance_parent: host = sliver.get_labels().instance_parent if sliver.get_label_allocations() and sliver.get_label_allocations().instance_parent: @@ -320,7 +320,7 @@ def add_reservation(self, *, reservation: ABCReservationMixin): ip_subnet = str(sliver.get_management_ip()) node_id = reservation.get_graph_node_id() - if node_id: + if node_id and sliver.attached_components_info: components = [] for c in sliver.attached_components_info.devices.values(): if c.get_node_map(): @@ -392,7 +392,7 @@ def update_reservation(self, *, reservation: ABCReservationMixin): bdf = ":".join(split_string[3:]) if len(split_string) > 3 else None if node_id and comp_id and bdf: components.append((node_id, comp_id, bdf)) - elif isinstance(sliver, NodeSliver) and sliver.attached_components_info: + elif isinstance(sliver, NodeSliver): if sliver.get_labels() and sliver.get_labels().instance_parent: host = sliver.get_labels().instance_parent if sliver.get_label_allocations() and sliver.get_label_allocations().instance_parent: @@ -400,7 +400,7 @@ def update_reservation(self, *, reservation: ABCReservationMixin): if sliver.get_management_ip(): ip_subnet = str(sliver.get_management_ip()) node_id = reservation.get_graph_node_id() - if node_id: + if node_id and sliver.attached_components_info: components = [] for c in sliver.attached_components_info.devices.values(): if c.get_node_map(): From 73bb3cddb1d2d2c1ecba617e6cb3a02eacd73b86 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Wed, 17 Jul 2024 23:06:37 -0400 Subject: [PATCH 128/133] pass ipsubnet/host for query --- .../actor/core/manage/kafka/services/kafka_actor_service.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fabric_cf/actor/core/manage/kafka/services/kafka_actor_service.py b/fabric_cf/actor/core/manage/kafka/services/kafka_actor_service.py index 15dc58d8..2d576a98 100644 --- a/fabric_cf/actor/core/manage/kafka/services/kafka_actor_service.py +++ b/fabric_cf/actor/core/manage/kafka/services/kafka_actor_service.py @@ -283,7 +283,8 @@ def get_reservations(self, *, request: GetReservationsRequestAvro) -> ResultRese result = mo.get_reservations(caller=auth, states=request.get_states(), slice_id=slice_id, rid=rid, email=request.get_email(), type=request.get_type(), - site=request.get_site()) + site=request.get_site(), ip_subnet=request.get_ip_subnet(), + host=request.get_host()) except Exception as e: result.status.set_code(ErrorCodes.ErrorInternalError.value) From 8c49346bc6975c7a212ae6fc0f45382137f326ad Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 18 Jul 2024 09:26:32 -0400 Subject: [PATCH 129/133] up the version --- fabric_cf/__init__.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fabric_cf/__init__.py b/fabric_cf/__init__.py index 96e7bfe8..1f6dc025 100644 --- a/fabric_cf/__init__.py +++ b/fabric_cf/__init__.py @@ -1,2 +1,2 @@ -__version__ = "1.7.0rc0" +__version__ = "1.7.0" __VERSION__ = __version__ diff --git a/pyproject.toml b/pyproject.toml index df1f98eb..0603574a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ dependencies = [ "swagger-ui-bundle==0.0.9", "PyYAML", "fabric_fss_utils==1.5.1", - "fabric-message-bus==1.7.0rc0", + "fabric-message-bus==1.7.0", "fabric-fim==1.7.0", "fabric-credmgr-client==1.6.1", "ansible" From 5cadecc7356ce692f91c659d53bc77e68d75f8a6 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 18 Jul 2024 09:26:42 -0400 Subject: [PATCH 130/133] up the version --- Dockerfile-auth | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile-auth b/Dockerfile-auth index f969eb06..bdc9014b 100644 --- a/Dockerfile-auth +++ b/Dockerfile-auth @@ -1,7 +1,7 @@ FROM python:3.11.0 MAINTAINER Komal Thareja -ARG HANDLERS_VER=1.7.0rc0 +ARG HANDLERS_VER=1.7.0 RUN mkdir -p /usr/src/app WORKDIR /usr/src/app From cb2f440fef82f69999f4fe7db3c9003233b6fde4 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 18 Jul 2024 09:39:19 -0400 Subject: [PATCH 131/133] update the sliver info --- .../actor/core/plugins/db/actor_database.py | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/fabric_cf/actor/core/plugins/db/actor_database.py b/fabric_cf/actor/core/plugins/db/actor_database.py index c073cb35..934eb264 100644 --- a/fabric_cf/actor/core/plugins/db/actor_database.py +++ b/fabric_cf/actor/core/plugins/db/actor_database.py @@ -287,14 +287,21 @@ def add_reservation(self, *, reservation: ABCReservationMixin): components = None host = None ip_subnet = None - if reservation.get_resources() is not None and reservation.get_resources().get_sliver() is not None: + sliver = None + from fabric_cf.actor.core.kernel.reservation_client import ReservationClient + if isinstance(reservation, ReservationClient) and reservation.get_leased_resources() and \ + reservation.get_leased_resources().get_sliver(): + sliver = reservation.get_leased_resources().get_sliver() + if not sliver and reservation.get_resources() and reservation.get_resources().get_sliver(): sliver = reservation.get_resources().get_sliver() - site = sliver.get_site() + + if sliver: rsv_type = sliver.get_type().name from fim.slivers.network_service import NetworkServiceSliver from fim.slivers.network_node import NodeSliver if isinstance(sliver, NetworkServiceSliver) and sliver.interface_info: + site = sliver.get_site() if sliver.get_gateway(): ip_subnet = sliver.get_gateway().subnet @@ -311,7 +318,9 @@ def add_reservation(self, *, reservation: ABCReservationMixin): bdf = ":".join(split_string[3:]) if len(split_string) > 3 else None if node_id and comp_id and bdf: components.append((node_id, comp_id, bdf)) + elif isinstance(sliver, NodeSliver): + site = sliver.get_site() if sliver.get_labels() and sliver.get_labels().instance_parent: host = sliver.get_labels().instance_parent if sliver.get_label_allocations() and sliver.get_label_allocations().instance_parent: @@ -368,14 +377,21 @@ def update_reservation(self, *, reservation: ABCReservationMixin): components = None ip_subnet = None host = None - - if reservation.get_resources() is not None and reservation.get_resources().get_sliver() is not None: + sliver = None + from fabric_cf.actor.core.kernel.reservation_client import ReservationClient + if isinstance(reservation, ReservationClient) and reservation.get_leased_resources() and \ + reservation.get_leased_resources().get_sliver(): + sliver = reservation.get_leased_resources().get_sliver() + if not sliver and reservation.get_resources() and reservation.get_resources().get_sliver(): sliver = reservation.get_resources().get_sliver() - site = sliver.get_site() + + if sliver: rsv_type = sliver.get_type().name from fim.slivers.network_service import NetworkServiceSliver from fim.slivers.network_node import NodeSliver if isinstance(sliver, NetworkServiceSliver) and sliver.interface_info: + site = sliver.get_site() + if sliver.get_gateway(): ip_subnet = sliver.get_gateway().subnet @@ -393,6 +409,8 @@ def update_reservation(self, *, reservation: ABCReservationMixin): if node_id and comp_id and bdf: components.append((node_id, comp_id, bdf)) elif isinstance(sliver, NodeSliver): + site = sliver.get_site() + if sliver.get_labels() and sliver.get_labels().instance_parent: host = sliver.get_labels().instance_parent if sliver.get_label_allocations() and sliver.get_label_allocations().instance_parent: From 7b88ae9354a219a71ca04e277b5b7d81cdde5018 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 18 Jul 2024 10:07:13 -0400 Subject: [PATCH 132/133] bump version --- Dockerfile-auth | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile-auth b/Dockerfile-auth index bdc9014b..302680c0 100644 --- a/Dockerfile-auth +++ b/Dockerfile-auth @@ -1,7 +1,7 @@ FROM python:3.11.0 MAINTAINER Komal Thareja -ARG HANDLERS_VER=1.7.0 +ARG HANDLERS_VER=1.7.1 RUN mkdir -p /usr/src/app WORKDIR /usr/src/app From 908add7c965d991f23a9e1bb2c0e4644d6076dd2 Mon Sep 17 00:00:00 2001 From: Komal Thareja Date: Thu, 18 Jul 2024 11:36:28 -0400 Subject: [PATCH 133/133] update nginx conf --- fabric_cf/orchestrator/nginx/default.conf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fabric_cf/orchestrator/nginx/default.conf b/fabric_cf/orchestrator/nginx/default.conf index fb698444..31cd479a 100644 --- a/fabric_cf/orchestrator/nginx/default.conf +++ b/fabric_cf/orchestrator/nginx/default.conf @@ -36,8 +36,8 @@ server { proxy_pass http://orchestrator:8700; proxy_set_header Host $http_host; } - location /prom/metrics { - proxy_pass http://orchestrator:11000; - proxy_set_header Host $http_host; - } + #location /prom/metrics { + # proxy_pass http://orchestrator:11000; + # proxy_set_header Host $http_host; + #} }