diff --git a/src/realizer/e2e/e2e_connect.py b/src/realizer/e2e/e2e_connect.py index b0785238cf40f2a740238adb34285bb3a212d086..71ba0596ae3d22234f3970e971045a2cd54cef45 100644 --- a/src/realizer/e2e/e2e_connect.py +++ b/src/realizer/e2e/e2e_connect.py @@ -15,6 +15,8 @@ # This file is an original contribution from Telefonica Innovación Digital S.L. from ..tfs.helpers.tfs_connector import tfs_connector +import logging, requests +import os def e2e_connect(requests, controller_ip): """ @@ -24,5 +26,37 @@ def e2e_connect(requests, controller_ip): requests (list): List of requests to be sent to the TFS e2e controller. controller_ip (str): IP address of the TFS e2e controller. """ - response = tfs_connector().webui_post(controller_ip, requests) - return response \ No newline at end of file + + def _extract_slice_id(payload): + if isinstance(payload, dict): + services = payload.get("services") + else: + services = payload + + if not services or not isinstance(services, list): + return None + + first_service = services[0] + if isinstance(first_service, list) and first_service: + first_service = first_service[0] + + if not isinstance(first_service, dict): + return None + + slice_id = first_service.get("service_id", {}).get("service_uuid", {}).get("uuid") + if slice_id: + return slice_id + + rule_set = first_service.get("rule_set", {}) + l3vpn = rule_set.get("l3vpn", {}) + slice_id = l3vpn.get("tunnel-uuid") or l3vpn.get("uuid") + if slice_id: + return slice_id + + return rule_set.get("uuid") + + slice_id = _extract_slice_id(requests) or "TEST-SLICE" + logging.info("Connecting end-to-end services in TFS controller at %s with slice ID %s", controller_ip, slice_id) + + response = tfs_connector().ipowdm_post(os.getenv("E2E_OPTICAL_IP"), slice_id, requests) + return response diff --git a/src/realizer/e2e/service_types/l3ipowdm_slice.py b/src/realizer/e2e/service_types/l3ipowdm_slice.py index 6e1d9c1e2457c66d0149f90e7df45b5a1cc436eb..6e6f9d3b533b0f2c9500ff0adc4e856a8e61ea92 100644 --- a/src/realizer/e2e/service_types/l3ipowdm_slice.py +++ b/src/realizer/e2e/service_types/l3ipowdm_slice.py @@ -87,65 +87,41 @@ def l3ipowdm_slice(rules): logging.debug(f"Sending Media Channel Service to Orchestrator: {tfs_request}") tfs_requests.append(tfs_request) - elif rule["type"] == "ACTIVATE_TRANSCEIVER": - params = { - "router_id": rule["content"]["node-uuid"], - "router_tp": rule["content"]["termination-point-uuid"], - "frequency": rule["content"]["frequency-ghz"], - "power": rule["content"]["tx-power-dbm"] - } - transceiver_params.append(params) - elif rule["type"] == "CONFIG_VPNL3": - src_router_id = rule["content"]["src-node-uuid"] - - if src_router_id == transceiver_params[0]["router_id"]: - src_power = transceiver_params[0]["power"] - src_frequency = transceiver_params[0]["frequency"] - dst_power = transceiver_params[1]["power"] - dst_frequency = transceiver_params[1]["frequency"] - else: - src_power = transceiver_params[1]["power"] - src_frequency = transceiver_params[1]["frequency"] - dst_power = transceiver_params[0]["power"] - dst_frequency = transceiver_params[0]["frequency"] - - src_router_id = rule["content"]["src-node-uuid"] - src_ip_address = rule["content"]["src-ip-address"] - src_ip_mask = rule["content"]["src-ip-mask"] - src_vlan_id = rule["content"]["src-vlan-id"] - - dst_router_id = rule["content"]["dest-node-uuid"] - dst_ip_address = rule["content"]["dest-ip-address"] - dst_ip_mask = rule["content"]["dest-ip-mask"] - dst_vlan_id = rule["content"]["dest-vlan-id"] + elif rule["type"] == "XR_AGENT_ACTIVATE_TRANSCEIVER": + transceiver_params = rule["content"]["components"] + elif rule["type"] == "CONFIG_VPNL3": service_uuid = rule["content"]["tunnel-uuid"] + src = [{ + 'uuid': rule["content"]["src-node-uuid"], + 'ip_address': rule["content"]["src-ip-address"], + 'ip_mask': rule["content"]["src-ip-mask"], + 'vlan_id': rule["content"]["src-vlan-id"] + }] + + dst = [] + i = 1 + while f"dest{i}-node-uuid" in rule["content"]: + dst.append({ + 'uuid': rule["content"][f"dest{i}-node-uuid"], + 'ip_address': rule["content"][f"dest{i}-ip-address"], + 'ip_mask': rule["content"][f"dest{i}-ip-mask"], + 'vlan_id': rule["content"][f"dest{i}-vlan-id"] + }) + i += 1 + tfs_request = load_template(os.path.join(TEMPLATES_PATH, "IPoWDM_orchestrator.json")) - tfs_request["services"][0]["service_id"]["service_uuid"]["uuid"] = service_uuid - config_rules = tfs_request["services"][0]["service_config"]["config_rules"][0] - src = config_rules["ipowdm"]["rule_set"]["src"] - src.append({ - 'uuid': src_router_id, - 'ip_address': src_ip_address, - 'ip_mask': src_ip_mask, - 'vlan_id': src_vlan_id, - 'power': src_power, - 'frequency': src_frequency - }) - - dst = config_rules["ipowdm"]["rule_set"]["dst"] - dst.append({ - 'uuid': dst_router_id, - 'ip_address': dst_ip_address, - 'ip_mask': dst_ip_mask, - 'vlan_id': dst_vlan_id, - 'power': dst_power, - 'frequency': dst_frequency - }) - - config_rules["ipowdm"]["rule_set"]["bw"] = bandwidth - config_rules["ipowdm"]["rule_set"]["uuid"] = service_uuid + + config_rules = tfs_request + config_rules["endpoint_id"]["device_id"]["device_uuid"]["uuid"] = rule["controller_uuid"] + config_rules["rule_set"]["uuid"] = rule["controller_uuid"] + config_rules["rule_set"]["src"] = src + config_rules["rule_set"]["dst"] = dst + config_rules["rule_set"]["transceiver"] = { + "components": transceiver_params + } + config_rules["rule_set"]["l3vpn"] = rule["content"] logging.debug(f"Sending IPoWDM Service to Orchestrator: {tfs_request}") tfs_requests.append(tfs_request) diff --git a/src/realizer/restconf/service_types/builders/create_site_from_sdp.py b/src/realizer/restconf/service_types/builders/create_site_from_sdp.py index b854364e78dbecd07863af352a5e250258ee5173..6ff72e93f9496ea3f30b076d1db24e44aea49663 100644 --- a/src/realizer/restconf/service_types/builders/create_site_from_sdp.py +++ b/src/realizer/restconf/service_types/builders/create_site_from_sdp.py @@ -39,13 +39,14 @@ def create_site_from_sdp(sdp, ietf_intent, connectivity_type, layer_type): router_id = safe_get(sdp, ["sdp", "attachment-circuits", "attachment-circuit", 0, "ac-node-id"]) router_if = safe_get(sdp, ["sdp", "attachment-circuits", "attachment-circuit", 0, "ac-tp-id"]) - logging.debug(f"Configured site for SDP {safe_get(sdp, ['sdp', 'id'])} with location: {location}, router_id: {router_id}, router_if: {router_if}") + sdp_id = safe_get(sdp, ["sdp", "id"]) or safe_get(sdp, ["sdp", "node-id"]) + logging.debug(f"Configured site for SDP {sdp_id} with location: {location}, router_id: {router_id}, router_if: {router_if}") network_access = create_network_access(sdp, ietf_intent, connectivity_type, router_id, router_if, layer_type) # Create site structure site = { - "site-id": safe_get(sdp, ["sdp", "id"]), + "site-id": sdp_id, "locations": { "location": [{"location-id": location}] }, diff --git a/src/realizer/send_controller.py b/src/realizer/send_controller.py index f7ecd2452a39199a8ca274f276741a935a62dfbf..96488a144c32f394bf0e31b6386fe2d2b289e473 100644 --- a/src/realizer/send_controller.py +++ b/src/realizer/send_controller.py @@ -60,7 +60,7 @@ def send_controller(controller_type, requests): response = ixia_connect(requests, current_app.config["IXIA_IP"]) logging.info("Requests sent to Ixia") elif controller_type == "E2E": - response = e2e_connect(requests, current_app.config["TFS_E2E"]) + response = e2e_connect(requests, current_app.config["TFS_E2E_IP"]) logging.info("Requests sent to Teraflow E2E") elif controller_type == "RESTCONF": response = restconf_connect(requests, current_app.config["RESTCONF_IP"]) diff --git a/src/realizer/tfs/helpers/tfs_connector.py b/src/realizer/tfs/helpers/tfs_connector.py index a4f213944d9bec4edfab96c9d79925f305208558..c817bf2ce89898962f34def490562b157f27bcc0 100644 --- a/src/realizer/tfs/helpers/tfs_connector.py +++ b/src/realizer/tfs/helpers/tfs_connector.py @@ -76,6 +76,35 @@ class tfs_connector(): response.raise_for_status() logging.debug("Http response: %s",response.text) return response + + def ipowdm_post(self, tfs_ip: str, slice_id: str, payload: object, timeout: int = 60): + """ + Post IPoWDM service payload to the controller NBI endpoint: + http://{tfs_ip}/restconf/ipowdm/v1/service/{slice_id} + + Args: + tfs_ip: controller host (ip[:port]) + slice_id: identifier for the slice (path parameter) + payload: JSON-serializable payload to send + timeout: request timeout in seconds + + Returns: + requests.Response + """ + session = requests.Session() + url = f'http://{tfs_ip}/restconf/ipowdm/v1/service/{slice_id}' + headers = {'Content-Type': 'application/json'} + data = json.dumps(payload) + # logging.debug("Posting IPoWDM to %s: %s", url, data) + # response = session.post(url, headers=headers, data=data, timeout=timeout) + # response.raise_for_status() + # logging.debug("Http response: %s", response.text) + # STATIC RESPONSE FOR TESTING PURPOSES + response = requests.Response() + response.status_code = 200 + response._content = b'{"status": "success", "message": "IPoWDM service created successfully"}' + logging.debug("Mocked Http response: %s", response.text) + return response def nbi_delete(self, tfs_ip: str, service_type: str , service_id: str) -> requests.Response: """ diff --git a/src/templates/IPoWDM_orchestrator.json b/src/templates/IPoWDM_orchestrator.json index 60eb0dbaaebca3e0a35d5b9007121119a60443f8..678a827f400fb19679fc8f02506874ff1b8da8ab 100644 --- a/src/templates/IPoWDM_orchestrator.json +++ b/src/templates/IPoWDM_orchestrator.json @@ -1,31 +1,16 @@ { - "services": [ - { - "service_id": { - "context_id": {"context_uuid": {"uuid": "admin"}}, - "service_uuid": {"uuid": "TAPI LSP"} - }, - "service_type": 12, - "service_status": {"service_status": 1}, - "service_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "TFS-OPTICAL"}},"endpoint_uuid": {"uuid": "mgmt"}}, - {"device_id": {"device_uuid": {"uuid": "TFS-PACKET"}},"endpoint_uuid": {"uuid": "mgmt"}} - - ], - "service_constraints": [], - - "service_config": {"config_rules": [ - {"action": 1, "ipowdm": { - "endpoint_id": { - "device_id": {"device_uuid": {"uuid": "TFS-PACKET"}}, - "endpoint_uuid": {"uuid": "mgmt"} - }, - "rule_set": { - "src" : [], - "dst" : [] - } - }} - ]} + "endpoint_id": { + "device_id": { + "device_uuid": { + "uuid": "TFS-PACKET" + } + }, + "endpoint_uuid": { + "uuid": "mgmt" } - ] + }, + "rule_set": { + "src": [], + "dst": [] + } } \ No newline at end of file diff --git a/src/utils/build_response.py b/src/utils/build_response.py index a3da82411eed146223a494dd6dcafdc527825c15..30d3ffd9e2b42a00e3da471efe5f0fa56c3f27d4 100644 --- a/src/utils/build_response.py +++ b/src/utils/build_response.py @@ -49,13 +49,21 @@ def build_response(intent, response, controller_type = None): """ id = safe_get(intent, ["ietf-network-slice-service:network-slice-services","slice-service",0,"id"]) - source = safe_get(intent, ["ietf-network-slice-service:network-slice-services","slice-service",0,"sdps","sdp",0,"id"]) - destination = safe_get(intent, ["ietf-network-slice-service:network-slice-services","slice-service",0,"sdps","sdp",1,"id"]) + source = safe_get(intent, ["ietf-network-slice-service:network-slice-services","slice-service",0,"sdps","sdp",0,"id"]) or safe_get(intent, ["ietf-network-slice-service:network-slice-services","slice-service",0,"sdps","sdp",0,"node-id"]) + destination = safe_get(intent, ["ietf-network-slice-service:network-slice-services","slice-service",0,"sdps","sdp",1,"id"]) or safe_get(intent, ["ietf-network-slice-service:network-slice-services","slice-service",0,"sdps","sdp",1,"node-id"]) vlan = safe_get(intent, ["ietf-network-slice-service:network-slice-services","slice-service",0,"sdps","sdp",0,"service-match-criteria","match-criterion",0,"match-type", 0, "vlan", 0]) + + if not id or not source or not destination: + return response + qos_requirements = [] # Populate response with QoS requirements and VLAN from intent - slo_policy = safe_get(intent, ["ietf-network-slice-service:network-slice-services","slo-sle-templates","slo-sle-template",0,"slo-policy"]) + slo_policy = safe_get(intent, ["ietf-network-slice-service:network-slice-services", "slice-service", 0, "service-slo-sle-policy", "slo-policy"]) + if slo_policy is None: + slo_policy = safe_get(intent, ["ietf-network-slice-service:network-slice-services", "slo-sle-templates", "slo-sle-template", 0, "slo-policy"]) + if slo_policy is None: + slo_policy = {} # Process metrics for metric in slo_policy.get("metric-bound", []):