Skip to content
Snippets Groups Projects
Commit ffb2d6f4 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

Test - Tools - IPM Mock Controller:

- First basic and functional version
parent 8ee31f1f
No related branches found
No related tags found
2 merge requests!142Release TeraFlowSDN 2.1,!71OFC'23 + IETF L2VPN Device Driver + Device Controllers + Multiple small improvements
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
# Mock IPM controller (implements minimal support) # Mock IPM controller (implements minimal support)
import functools, json, logging, sys, time, uuid import functools, json, logging, sys, time, uuid
from typing import Any, Dict, Optional, Tuple
from flask import Flask, jsonify, make_response, request from flask import Flask, jsonify, make_response, request
from flask_restful import Api, Resource from flask_restful import Api, Resource
...@@ -28,21 +29,57 @@ LOG_LEVEL = logging.DEBUG ...@@ -28,21 +29,57 @@ LOG_LEVEL = logging.DEBUG
CONSTELLATION = { CONSTELLATION = {
'id': 'ofc-constellation', 'id': 'ofc-constellation',
'hubModule': {'state': { 'hubModule': {'state': {
'module': {'moduleName': 'OFC HUB 1', 'trafficMode': 'L1Mode'}, 'module': {'moduleName': 'OFC HUB 1', 'trafficMode': 'L1Mode', 'capacity': 100},
'endpoints': [{'moduleIf': {'clientIfAid': 'XR-T1'}}, {'moduleIf': {'clientIfAid': 'XR-T4'}}] 'endpoints': [{'moduleIf': {'clientIfAid': 'XR-T1'}}, {'moduleIf': {'clientIfAid': 'XR-T4'}}]
}}, }},
'leafModules': [ 'leafModules': [
{'state': { {'state': {
'module': {'moduleName': 'OFC LEAF 1', 'trafficMode': 'L1Mode'}, 'module': {'moduleName': 'OFC LEAF 1', 'trafficMode': 'L1Mode', 'capacity': 100},
'endpoints': [{'moduleIf': {'clientIfAid': 'XR-T1'}}] 'endpoints': [{'moduleIf': {'clientIfAid': 'XR-T1'}}]
}}, }},
{'state': { {'state': {
'module': {'moduleName': 'OFC LEAF 2', 'trafficMode': 'L1Mode'}, 'module': {'moduleName': 'OFC LEAF 2', 'trafficMode': 'L1Mode', 'capacity': 100},
'endpoints': [{'moduleIf': {'clientIfAid': 'XR-T1'}}] 'endpoints': [{'moduleIf': {'clientIfAid': 'XR-T1'}}]
}} }}
] ]
} }
CONNECTIONS : Dict[str, Any] = dict()
STATE_NAME_TO_CONNECTION : Dict[str, str] = dict()
def select_module_state(module_name : str) -> Optional[Dict]:
hub_module_state = CONSTELLATION.get('hubModule', {}).get('state', {})
if module_name == hub_module_state.get('module', {}).get('moduleName'): return hub_module_state
for leaf_module in CONSTELLATION.get('leafModules', []):
leaf_module_state = leaf_module.get('state', {})
if module_name == leaf_module_state.get('module', {}).get('moduleName'): return leaf_module_state
return None
def select_endpoint(module_state : Dict, module_if : str) -> Optional[Dict]:
for endpoint in module_state.get('endpoints', []):
if module_if == endpoint.get('moduleIf', {}).get('clientIfAid'): return endpoint
return None
def select_module_endpoint(selector : Dict) -> Optional[Tuple[Dict, Dict]]:
selected_module_name = selector['moduleIfSelectorByModuleName']['moduleName']
selected_module_if = selector['moduleIfSelectorByModuleName']['moduleClientIfAid']
module_state = select_module_state(selected_module_name)
if module_state is None: return None
return module_state, select_endpoint(module_state, selected_module_if)
def compose_endpoint(endpoint_selector : Dict) -> Dict:
module, endpoint = select_module_endpoint(endpoint_selector['selector'])
return {
'href': '/' + str(uuid.uuid4()),
'state': {
'moduleIf': {
'moduleName': module['module']['moduleName'],
'clientIfAid': endpoint['moduleIf']['clientIfAid'],
},
'capacity': module['module']['capacity'],
}
}
logging.basicConfig(level=LOG_LEVEL, format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s") logging.basicConfig(level=LOG_LEVEL, format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s")
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
...@@ -53,59 +90,85 @@ def log_request(logger : logging.Logger, response): ...@@ -53,59 +90,85 @@ def log_request(logger : logging.Logger, response):
logger.info('%s %s %s %s %s', timestamp, request.remote_addr, request.method, request.full_path, response.status) logger.info('%s %s %s %s %s', timestamp, request.remote_addr, request.method, request.full_path, response.status)
return response return response
#class Health(Resource):
# def get(self):
# return make_response(jsonify({}), 200)
class OpenIdConnect(Resource): class OpenIdConnect(Resource):
ACCESS_TOKENS = {} ACCESS_TOKENS = {}
def post(self): def post(self):
if request.content_type != 'application/x-www-form-urlencoded': return make_response('bad content type', 400) if request.content_type != 'application/x-www-form-urlencoded': return make_response('bad content type', 400)
if request.content_length == 0: return make_response('bad content length', 400) if request.content_length == 0: return make_response('bad content length', 400)
form_request = request.form request_form = request.form
if form_request.get('client_id') != 'xr-web-client': return make_response('bad client_id', 403) if request_form.get('client_id') != 'xr-web-client': return make_response('bad client_id', 403)
if form_request.get('client_secret') != 'xr-web-client': return make_response('bad client_secret', 403) if request_form.get('client_secret') != 'xr-web-client': return make_response('bad client_secret', 403)
if form_request.get('grant_type') != 'password': return make_response('bad grant_type', 403) if request_form.get('grant_type') != 'password': return make_response('bad grant_type', 403)
if form_request.get('username') != IPM_USERNAME: return make_response('bad username', 403) if request_form.get('username') != IPM_USERNAME: return make_response('bad username', 403)
if form_request.get('password') != IPM_PASSWORD: return make_response('bad password', 403) if request_form.get('password') != IPM_PASSWORD: return make_response('bad password', 403)
access_token = OpenIdConnect.ACCESS_TOKENS.setdefault(IPM_USERNAME, uuid.uuid4()) access_token = OpenIdConnect.ACCESS_TOKENS.setdefault(IPM_USERNAME, uuid.uuid4())
reply = {'access_token': access_token, 'expires_in': 86400} reply = {'access_token': access_token, 'expires_in': 86400}
return make_response(jsonify(reply), 200) return make_response(jsonify(reply), 200)
class XrNetworks(Resource): class XrNetworks(Resource):
def get(self): def get(self):
print(str(request.args))
content = request.args.get('content')
print('content', content)
query = json.loads(request.args.get('q')) query = json.loads(request.args.get('q'))
hub_module_name = query.get('hubModule.state.module.moduleName') hub_module_name = query.get('hubModule.state.module.moduleName')
if hub_module_name != 'OFC HUB 1': return make_response('unexpected hub module', 404) if hub_module_name != 'OFC HUB 1': return make_response('unexpected hub module', 404)
print('query', query)
return make_response(jsonify([CONSTELLATION]), 200) return make_response(jsonify([CONSTELLATION]), 200)
#class Services(Resource): class XrNetworkConnections(Resource):
# def get(self): def get(self):
# services = [service for service in NETWORK_SERVICES.values()] query = json.loads(request.args.get('q'))
# return make_response(jsonify({'ietf-eth-tran-service:etht-svc': {'etht-svc-instances': services}}), 200) state_name = query.get('state.name')
# if state_name is None:
# def post(self): connections = [connection for connection in CONNECTIONS.values()]
# json_request = request.get_json() else:
# if not json_request: abort(400) connection_uuid = STATE_NAME_TO_CONNECTION.get(state_name)
# if not isinstance(json_request, dict): abort(400) if connection_uuid is None: return make_response('state name not found', 404)
# if 'etht-svc-instances' not in json_request: abort(400) connection = CONNECTIONS.get(connection_uuid)
# json_services = json_request['etht-svc-instances'] if connection is None: return make_response('connection for state name not found', 404)
# if not isinstance(json_services, list): abort(400) connections = [connection]
# if len(json_services) != 1: abort(400) return make_response(jsonify(connections), 200)
# svc_data = json_services[0]
# etht_svc_name = svc_data['etht-svc-name'] def post(self):
# NETWORK_SERVICES[etht_svc_name] = svc_data if request.content_type != 'application/json': return make_response('bad content type', 400)
# return make_response(jsonify({}), 201) if request.content_length == 0: return make_response('bad content length', 400)
request_json = request.json
#class DelServices(Resource): if not isinstance(request_json, list): return make_response('content is not list', 400)
# def delete(self, service_uuid : str): reply = []
# NETWORK_SERVICES.pop(service_uuid, None) for connection in request_json:
# return make_response(jsonify({}), 204) connection_uuid = str(uuid.uuid4())
state_name = connection['name']
if state_name is not None: STATE_NAME_TO_CONNECTION[state_name] = connection_uuid
CONNECTIONS[connection_uuid] = {
'href': '/network-connections/{:s}'.format(str(connection_uuid)),
'config': {
'implicitTransportCapacity': connection['implicitTransportCapacity']
# 'mc': ??
},
'state': {
'name': state_name,
'serviceMode': connection['serviceMode']
# 'outerVID' : ??
},
'endpoints': [
compose_endpoint(endpoint)
for endpoint in connection['endpoints']
]
}
reply.append(CONNECTIONS[connection_uuid])
return make_response(jsonify(reply), 202)
class XrNetworkConnection(Resource):
def get(self, connection_uuid : str):
connection = CONNECTIONS.get(connection_uuid)
if connection is None: return make_response('unexpected connection id', 404)
return make_response(jsonify(connection), 200)
def delete(self, connection_uuid : str):
connection = CONNECTIONS.pop(connection_uuid, None)
if connection is None: return make_response('unexpected connection id', 404)
state_name = connection['state']['name']
STATE_NAME_TO_CONNECTION.pop(state_name, None)
return make_response(jsonify({}), 202)
def main(): def main():
LOGGER.info('Starting...') LOGGER.info('Starting...')
...@@ -114,12 +177,10 @@ def main(): ...@@ -114,12 +177,10 @@ def main():
app.after_request(functools.partial(log_request, LOGGER)) app.after_request(functools.partial(log_request, LOGGER))
api = Api(app) api = Api(app)
#api.add_resource(Health, '/ietf-network:networks') api.add_resource(OpenIdConnect, '/realms/xr-cm/protocol/openid-connect/token')
api.add_resource(OpenIdConnect, '/realms/xr-cm/protocol/openid-connect/token') api.add_resource(XrNetworks, '/api/v1/xr-networks')
api.add_resource(XrNetworks, '/api/v1/xr-networks') api.add_resource(XrNetworkConnections, '/api/v1/network-connections')
#api.add_resource(Network, '/ietf-network:networks/network=<string:network_uuid>') api.add_resource(XrNetworkConnection, '/api/v1/network-connections/<string:connection_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))) LOGGER.info('Listening on {:s}...'.format(str(STR_ENDPOINT)))
app.run(debug=True, host=BIND_ADDRESS, port=BIND_PORT, ssl_context='adhoc') app.run(debug=True, host=BIND_ADDRESS, port=BIND_PORT, ssl_context='adhoc')
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment