diff --git a/src/tests/tools/mock_sdn_ctrl/MockMWSdnCtrl.py b/src/tests/tools/mock_sdn_ctrl/MockMWSdnCtrl.py new file mode 100644 index 0000000000000000000000000000000000000000..61eec6fe674a0348a92cb84af439c6fbf8668f28 --- /dev/null +++ b/src/tests/tools/mock_sdn_ctrl/MockMWSdnCtrl.py @@ -0,0 +1,130 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Mock MicroWave SDN controller +# ----------------------------- +# REST server implementing minimal support for: +# - IETF YANG data model for Network Topology +# Ref: https://www.rfc-editor.org/rfc/rfc8345.html +# - IETF YANG data model for Transport Network Client Signals +# Ref: https://www.ietf.org/archive/id/draft-ietf-ccamp-client-signal-yang-07.html + + +# Ref: https://blog.miguelgrinberg.com/post/running-your-flask-application-over-https +# Ref: https://blog.miguelgrinberg.com/post/designing-a-restful-api-using-flask-restful + +import functools, logging, sys, time +from flask import Flask, abort, request +from flask.json import jsonify +from flask_restful import Api, Resource + +BIND_ADDRESS = '0.0.0.0' +BIND_PORT = 8443 +BASE_URL = '/nmswebs/restconf/data' +STR_ENDPOINT = 'https://{:s}:{:s}{:s}'.format(str(BIND_ADDRESS), str(BIND_PORT), str(BASE_URL)) +LOG_LEVEL = logging.DEBUG + +NETWORK_NODES = [ + {'node-id': '172.18.0.1', 'ietf-network-topology:termination-point': [ + {'tp-id': '172.18.0.1:1', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '172.18.0.1:2', 'ietf-te-topology:te': {'name': 'antena' }}, + ]}, + {'node-id': '172.18.0.2', 'ietf-network-topology:termination-point': [ + {'tp-id': '172.18.0.2:1', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '172.18.0.2:2', 'ietf-te-topology:te': {'name': 'antena' }}, + ]}, + {'node-id': '172.18.0.3', 'ietf-network-topology:termination-point': [ + {'tp-id': '172.18.0.3:1', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '172.18.0.3:2', 'ietf-te-topology:te': {'name': 'antena' }}, + ]}, + {'node-id': '172.18.0.4', 'ietf-network-topology:termination-point': [ + {'tp-id': '172.18.0.4:1', 'ietf-te-topology:te': {'name': 'ethernet'}}, + {'tp-id': '172.18.0.4:2', 'ietf-te-topology:te': {'name': 'antena' }}, + ]} +] +NETWORK_LINKS = [ + { + 'source' : {'source-node': '172.18.0.1', 'source-tp': '172.18.0.1:2'}, + 'destination': {'dest-node' : '172.18.0.2', 'dest-tp' : '172.18.0.2:2'}, + }, + { + 'source' : {'source-node': '172.18.0.3', 'source-tp': '172.18.0.3:2'}, + 'destination': {'dest-node' : '172.18.0.4', 'dest-tp' : '172.18.0.4:2'}, + } +] +NETWORK_SERVICES = {} + + +logging.basicConfig(level=LOG_LEVEL, format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s") +LOGGER = logging.getLogger(__name__) + +logging.getLogger('werkzeug').setLevel(logging.WARNING) + +def log_request(logger : logging.Logger, response): + timestamp = time.strftime('[%Y-%b-%d %H:%M]') + logger.info('%s %s %s %s %s', timestamp, request.remote_addr, request.method, request.full_path, response.status) + return response + +class Health(Resource): + def get(self): return jsonify({}) + +class Network(Resource): + def get(self, network_uuid : str): + if network_uuid != 'SIAE-ETH-TOPOLOGY': abort(400) + network = {'node': NETWORK_NODES, 'ietf-network-topology:link': NETWORK_LINKS} + return jsonify({'ietf-network:network': network}) + +class Services(Resource): + def get(self): + services = [service for service in NETWORK_SERVICES.values()] + return jsonify({'ietf-eth-tran-service:etht-svc': {'etht-svc-instances': services}}) + + def post(self): + json_request = request.json + if not json_request: abort(400) + if not isinstance(json_request, dict): abort(400) + if 'etht-svc-instances' not in json_request: abort(400) + json_services = json_request['etht-svc-instances'] + if not isinstance(json_services, list): abort(400) + if len(json_services) != 1: abort(400) + svc_data = json_services[0] + etht_svc_name = svc_data['etht-svc-name'] + NETWORK_SERVICES[etht_svc_name] = svc_data + return jsonify({}), 201 + +class DelServices(Resource): + def delete(self, service_uuid : str): + NETWORK_SERVICES.pop(service_uuid, None) + return jsonify({}), 204 + +def main(): + LOGGER.info('Starting...') + + app = Flask(__name__) + app.after_request(functools.partial(log_request, LOGGER)) + + api = Api(app, prefix=BASE_URL) + api.add_resource(Health, '/ietf-network:networks') + api.add_resource(Network, '/ietf-network:networks/network=<string:network_uuid>') + api.add_resource(Services, '/ietf-eth-tran-service:etht-svc') + api.add_resource(DelServices, '/ietf-eth-tran-service:etht-svc/etht-svc-instances=<string:service_uuid>') + + LOGGER.info('Listening on {:s}...'.format(str(STR_ENDPOINT))) + app.run(debug=True, host=BIND_ADDRESS, port=BIND_PORT, ssl_context='adhoc') + + LOGGER.info('Bye') + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/tests/tools/mock_sdn_ctrl/microwave_deploy.sh b/src/tests/tools/mock_sdn_ctrl/microwave_deploy.sh new file mode 100644 index 0000000000000000000000000000000000000000..2da884899c84491e36d87b87c502d2773cdf7f69 --- /dev/null +++ b/src/tests/tools/mock_sdn_ctrl/microwave_deploy.sh @@ -0,0 +1,22 @@ +# Set the URL of your local Docker registry where the images will be uploaded to. +export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" + +# Set the list of components, separated by spaces, you want to build images for, and deploy. +# Supported components are: +# context device automation policy service compute monitoring webui +# interdomain slice pathcomp dlt +# dbscanserving opticalattackmitigator opticalattackdetector +# l3_attackmitigator l3_centralizedattackdetector l3_distributedattackdetector +export TFS_COMPONENTS="context device pathcomp service slice webui" + +# Set the tag you want to use for your images. +export TFS_IMAGE_TAG="dev" + +# Set the name of the Kubernetes namespace to deploy to. +export TFS_K8S_NAMESPACE="tfs" + +# Set additional manifest files to be applied after the deployment +export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml" + +# Set the neew Grafana admin password +export TFS_GRAFANA_PASSWORD="admin123+" diff --git a/src/tests/tools/mock_sdn_ctrl/network_descriptors.json b/src/tests/tools/mock_sdn_ctrl/network_descriptors.json new file mode 100644 index 0000000000000000000000000000000000000000..d42fe61dc820f69328429b33aa1a496d493d06c4 --- /dev/null +++ b/src/tests/tools/mock_sdn_ctrl/network_descriptors.json @@ -0,0 +1,117 @@ +{ + "contexts": [ + { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_ids": [], + "service_ids": [] + } + ], + "topologies": [ + { + "topology_id": {"topology_uuid": {"uuid": "admin"}, "context_id": {"context_uuid": {"uuid": "admin"}}}, + "device_ids": [], + "link_ids": [] + } + ], + "devices": [ + { + "device_id": {"device_uuid": {"uuid": "R1"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_operational_status": 2, "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"type": "copper", "uuid": "MW", "sample_types": []}, + {"type": "copper", "uuid": "R2", "sample_types": []}, + {"type": "copper", "uuid": "EXT", "sample_types": []} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R2"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_operational_status": 2, "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"type": "copper", "uuid": "MW", "sample_types": []}, + {"type": "copper", "uuid": "R1", "sample_types": []}, + {"type": "copper", "uuid": "EXT", "sample_types": []} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R3"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_operational_status": 2, "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"type": "copper", "uuid": "MW", "sample_types": []}, + {"type": "copper", "uuid": "R4", "sample_types": []}, + {"type": "copper", "uuid": "EXT", "sample_types": []} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R4"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_operational_status": 2, "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"type": "copper", "uuid": "MW", "sample_types": []}, + {"type": "copper", "uuid": "R3", "sample_types": []}, + {"type": "copper", "uuid": "EXT", "sample_types": []} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "MW"}}, "device_type": "microwave-radio-system", "device_drivers": [4], + "device_operational_status": 2, "device_endpoints": [], + "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "192.168.1.56"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "8443"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"timeout": 120}}} + ]} + } + ], + "links": [ + { + "link_id": {"link_uuid": {"uuid": "R1/R2==R2/R1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "R2"}}, + {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "R1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R3/R4==R4/R3"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "R4"}}, + {"device_id": {"device_uuid": {"uuid": "R4"}}, "endpoint_uuid": {"uuid": "R3"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R1/MW==MW/172.18.0.1:1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "MW"}}, + {"device_id": {"device_uuid": {"uuid": "MW"}}, "endpoint_uuid": {"uuid": "172.18.0.1:1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R2/MW==MW/172.18.0.2:1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "MW"}}, + {"device_id": {"device_uuid": {"uuid": "MW"}}, "endpoint_uuid": {"uuid": "172.18.0.2:1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R3/MW==MW/172.18.0.3:1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "MW"}}, + {"device_id": {"device_uuid": {"uuid": "MW"}}, "endpoint_uuid": {"uuid": "172.18.0.3:1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R4/MW==MW/172.18.0.4:1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R4"}}, "endpoint_uuid": {"uuid": "MW"}}, + {"device_id": {"device_uuid": {"uuid": "MW"}}, "endpoint_uuid": {"uuid": "172.18.0.4:1"}} + ] + } + ] +} \ No newline at end of file diff --git a/src/tests/tools/mock_sdn_ctrl/service_descriptor.json b/src/tests/tools/mock_sdn_ctrl/service_descriptor.json new file mode 100644 index 0000000000000000000000000000000000000000..3e15bed5cf100fe381da061023f3ce885c9f9bd5 --- /dev/null +++ b/src/tests/tools/mock_sdn_ctrl/service_descriptor.json @@ -0,0 +1,25 @@ +{ + "services": [ + { + "service_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "service_uuid": {"uuid": "siae-svc"} + }, + "service_type": 2, + "service_status": {"service_status": 1}, + "service_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "EXT"}}, + {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "EXT"}} + ], + "service_constraints": [ + {"custom": {"constraint_type": "bandwidth[gbps]", "constraint_value": "10.0"}}, + {"custom": {"constraint_type": "latency[ms]", "constraint_value": "15.2"}} + ], + "service_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "/settings", "resource_value": { + "vlan_id": 121 + }}} + ]} + } + ] +}