diff options
author | Enbo Wang <wangenbo@huawei.com> | 2020-09-18 11:06:12 +0800 |
---|---|---|
committer | Enbo Wang <wangenbo@huawei.com> | 2020-09-19 17:25:04 +0800 |
commit | b764f221db38dd0d0fb3bad908e5d1329721730d (patch) | |
tree | 0a60d0bc8a3f9b6ac0a941a955ba0007fe57cd06 | |
parent | edbe50b80ce489b5ef9f30c9cd72f2dbcf2032d9 (diff) |
Add an External RAN NSSMF simulator
Change-Id: I770091159dd1f953d8a567ccd2bd7f6d21f27649
Issue-ID: INT-1707
Signed-off-by: Enbo Wang <wangenbo@huawei.com>
11 files changed, 470 insertions, 0 deletions
diff --git a/test/mocks/ran-nssmf-simulator/README.md b/test/mocks/ran-nssmf-simulator/README.md new file mode 100644 index 000000000..5a8958740 --- /dev/null +++ b/test/mocks/ran-nssmf-simulator/README.md @@ -0,0 +1,19 @@ +# External RAN NSSMF Simulator for Network Slicing Use Case + +There are two options to run the simulator: + +## Option 1. Directly run it in the current directory: + +``` +1. pip3 install -r requirements.txt + +2. python3 main.py +``` + +## Option 2. Install it using setuptools, and run it in any directory: + +``` +1. python3 setup.py install --user + +2. python3 -m RanNssmfSimulator.MainApp +``` diff --git a/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/AuthManager.py b/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/AuthManager.py new file mode 100644 index 000000000..6a52f516f --- /dev/null +++ b/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/AuthManager.py @@ -0,0 +1,127 @@ +# ============LICENSE_START======================================================= +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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. +# ============LICENSE_END========================================================= + +""" + Used to get and check Access Token by SO NSSMF adapter. +""" + +import json +import uuid +import time +import sched +import threading +from schematics.types import StringType +from schematics.models import Model + +from .utils import getLogger, AUTH_DB, TOKEN_EXPIRES_TIME, TOKEN_CLEAN_TIME + + +logger = getLogger("AuthManager") +lock = threading.Lock() + + +class AuthRequest(Model): + grantType = StringType(required=True) + userName = StringType(required=True) + value = StringType(required=True) + + +class AuthInfo(object): + def __init__(self, authRequest, expires): + self.authRequest = authRequest + self.expiredTime = int(time.time()) + expires * 60 + + +class AuthError(ValueError): + pass + + +class TokenError(ValueError): + pass + + +_AUTH_TOKEN = {} + + +def cleanExpiredToken(): + s = sched.scheduler(time.time, time.sleep) + + def doCleanExpiredToken(): + current_time = int(time.time()) + + expiredTokens = [] + for authToken in _AUTH_TOKEN: + if current_time > _AUTH_TOKEN[authToken].expiredTime: + expiredTokens.append(authToken) + logger.debug("Auth token %s is expired and will be deleted" % authToken) + + with lock: + for authToken in expiredTokens: + del _AUTH_TOKEN[authToken] + + s.enter(TOKEN_CLEAN_TIME, 1, doCleanExpiredToken) + + s.enter(TOKEN_CLEAN_TIME, 1, doCleanExpiredToken) + + s.run() + + +def checkAuth(authRequest): + with open(AUTH_DB) as f: + authDB = json.load(f) + + if authRequest["grantType"].lower() != "password": + raise AuthError("Unsupported grantType %s" % authRequest["grantType"]) + + for authItem in authDB: + if authItem["userName"].lower() == authRequest["userName"].lower() \ + and authItem["value"] == authRequest["value"]: + break + else: + raise AuthError("userName or password is error") + + +def generateAuthToken(authRequest): + token = uuid.uuid4().hex + with lock: + _AUTH_TOKEN[token] = AuthInfo(authRequest, TOKEN_EXPIRES_TIME) + + return { + "accessToken": token, + "expires": TOKEN_EXPIRES_TIME + } + + +def checkAuthToken(requestHeaders): + authToken = requestHeaders.get("X-Auth-Token") + logger.debug("X-Auth-Token: %s" % authToken) + + if not authToken: + raise TokenError("Auth token is missing") + + if authToken not in _AUTH_TOKEN: + raise TokenError("Auth token is error") + + current_time = int(time.time()) + if current_time > _AUTH_TOKEN[authToken].expiredTime: + raise TokenError("Auth token is expired") + + +def startAuthManagerJob(): + cleanThread = threading.Thread(target=cleanExpiredToken) + cleanThread.daemon = True + + cleanThread.start() diff --git a/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/MainApp.py b/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/MainApp.py new file mode 100644 index 000000000..6074e7634 --- /dev/null +++ b/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/MainApp.py @@ -0,0 +1,107 @@ +#! /usr/bin/python3 + +# ============LICENSE_START======================================================= +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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. +# ============LICENSE_END========================================================= + +import json +from flask import Flask, request, Response +from schematics.exceptions import DataError + +from .utils import REST_PORT, LOGGING_LEVEL +from .SliceDataType import AllocateNssi, DeAllocateNssi +from . import AuthManager +from . import NssManager + + +app = Flask(__name__) +app.logger.setLevel(LOGGING_LEVEL) + + +@app.errorhandler(DataError) +def handleRequestException(e): + app.logger.error(e) + response = Response() + response.status_code = 400 + return response + + +@app.errorhandler(AuthManager.AuthError) +def handleAuthException(e): + app.logger.error(e) + response = Response() + response.status_code = 400 + return response + + +@app.errorhandler(AuthManager.TokenError) +def handleAuthException(e): + app.logger.error(e) + response = Response() + response.status_code = 401 + return response + + +@app.errorhandler(NssManager.NssError) +def handleNssException(e): + app.logger.error(e) + response = Response() + response.status_code = 400 + return response + + +@app.route("/api/rest/securityManagement/v1/oauth/token", methods=['POST']) +def handleAuthToken(): + """ + Used to get Access Token by SO NSSMF adapter. + """ + app.logger.debug("Receive request:\n%s" % json.dumps(request.json, indent=2)) + + AuthManager.AuthRequest(request.json).validate() + AuthManager.checkAuth(request.json) + + return AuthManager.generateAuthToken(request.json), 201 + + +@app.route("/ObjectManagement/NSS/SliceProfiles", methods=['POST']) +def handleAllocateNssi(): + AuthManager.checkAuthToken(request.headers) + + app.logger.info("Receive AllocateNssi request:\n%s" % json.dumps(request.json, indent=2)) + + AllocateNssi(request.json).validate() + + return NssManager.allocateNssi(request.json), 200 + + +@app.route("/ObjectManagement/NSS/SliceProfiles/<string:sliceProfileId>", methods=['DELETE']) +def handleDeallocateNssi(sliceProfileId): + AuthManager.checkAuthToken(request.headers) + + app.logger.info("Receive DeallocateNssi request for sliceProfileId %s:\n%s" + % (sliceProfileId, json.dumps(request.json, indent=2))) + + DeAllocateNssi(request.json).validate() + + return NssManager.deallocateNssi(sliceProfileId, request.json), 200 + + +def main(): + AuthManager.startAuthManagerJob() + app.run("0.0.0.0", REST_PORT, False, ssl_context="adhoc") + + +if __name__ == '__main__': + main() diff --git a/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/NssManager.py b/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/NssManager.py new file mode 100644 index 000000000..f515968f1 --- /dev/null +++ b/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/NssManager.py @@ -0,0 +1,48 @@ +# ============LICENSE_START======================================================= +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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. +# ============LICENSE_END========================================================= + +import uuid + +from .utils import getLogger + + +logger = getLogger("NssManager") + + +class NssError(ValueError): + pass + + +def allocateNssi(requestBody): + sliceProfile = requestBody["attributeListIn"] + sliceProfileId = sliceProfile["sliceProfileId"] + + nSSId = uuid.uuid4().hex + + responseBody = { + "attributeListOut": {}, + "href": nSSId + } + + logger.info("Allocate NSSI for sliceProfileId %s success, nSSId: %s" % (sliceProfileId, nSSId)) + return responseBody + + +def deallocateNssi(sliceProfileId, requestBody): + nSSId = requestBody["nSSId"] + + logger.info("Deallocate NSSI for sliceProfileId %s success, nSSId: %s" % (sliceProfileId, nSSId)) + return "" diff --git a/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/SliceDataType.py b/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/SliceDataType.py new file mode 100644 index 000000000..10ee2c765 --- /dev/null +++ b/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/SliceDataType.py @@ -0,0 +1,59 @@ +# ============LICENSE_START======================================================= +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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. +# ============LICENSE_END========================================================= + +from schematics.types import BaseType, StringType, IntType, LongType +from schematics.types.compound import ModelType, ListType, DictType +from schematics.models import Model + + +class PerfReqEmbb(Model): + expDataRateDL = IntType() + expDataRateUL = IntType() + areaTrafficCapDL = IntType() + areaTrafficCapUL = IntType() + overallUserDensity = IntType() + activityFactor = IntType() + + +class PerfReqUrllc(Model): + """TODO""" + pass + + +class PerfReq(Model): + perfReqEmbb = ModelType(PerfReqEmbb) + # perfReqUrllc = ModelType(PerfReqUrllc) + perfReqUrllc = DictType(BaseType) + + +class SliceProfile(Model): + sliceProfileId = StringType(required=True) + sNSSAIList = ListType(StringType(required=True)) + pLMNIdList = ListType(StringType(required=True)) + perfReq = ModelType(PerfReq, required=True) + maxNumberofUEs = LongType() + coverageAreaTAList = ListType(IntType()) + latency = IntType() + uEMobilityLevel = StringType() + resourceSharingLevel = StringType() + + +class AllocateNssi(Model): + attributeListIn = ModelType(SliceProfile) + + +class DeAllocateNssi(Model): + nSSId = StringType(required=True) diff --git a/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/__init__.py b/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/__init__.py diff --git a/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/etc/auth.json b/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/etc/auth.json new file mode 100644 index 000000000..23e9376e0 --- /dev/null +++ b/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/etc/auth.json @@ -0,0 +1,7 @@ +[ + { + "grantType": "password", + "userName": "admin", + "value": "123456" + } +] diff --git a/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/utils.py b/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/utils.py new file mode 100644 index 000000000..f0b99119a --- /dev/null +++ b/test/mocks/ran-nssmf-simulator/RanNssmfSimulator/utils.py @@ -0,0 +1,44 @@ +# ============LICENSE_START======================================================= +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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. +# ============LICENSE_END========================================================= + +import os +import logging + +REST_PORT = int(os.getenv("RAN_NSSMF_REST_PORT", "8443")) +LOGGING_LEVEL = os.getenv("RAN_NSSMF_LOGGING_LEVEL", "INFO") + +TOKEN_EXPIRES_TIME = int(os.getenv("RAN_NSSMF_TOKEN_EXPIRES_TIME", "30")) +TOKEN_CLEAN_TIME = int(os.getenv("RAN_NSSMF_TOKEN_CLEAN_TIME", "180")) + +MAIN_DIR = os.path.dirname(os.path.abspath(__file__)) +AUTH_DB_FILE = os.path.join(MAIN_DIR, "etc", "auth.json") + +AUTH_DB = os.getenv("RAN_NSSMF_AUTH_DB", AUTH_DB_FILE) + + +LOGGER_FORMAT = "[%(asctime)-15s] %(levelname)s in %(name)s: %(message)s" + + +def getLogger(name, level=LOGGING_LEVEL, fmt=LOGGER_FORMAT): + logger = logging.getLogger(name) + logger.setLevel(level) + + formatter = logging.Formatter(fmt) + cmd_handler = logging.StreamHandler() + cmd_handler.setFormatter(formatter) + logger.addHandler(cmd_handler) + + return logger diff --git a/test/mocks/ran-nssmf-simulator/main.py b/test/mocks/ran-nssmf-simulator/main.py new file mode 100644 index 000000000..c2473de09 --- /dev/null +++ b/test/mocks/ran-nssmf-simulator/main.py @@ -0,0 +1,23 @@ +#! /usr/bin/python3 + +# ============LICENSE_START======================================================= +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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. +# ============LICENSE_END========================================================= + +from RanNssmfSimulator.MainApp import main + + +if __name__ == '__main__': + main() diff --git a/test/mocks/ran-nssmf-simulator/requirements.txt b/test/mocks/ran-nssmf-simulator/requirements.txt new file mode 100644 index 000000000..531b5ba93 --- /dev/null +++ b/test/mocks/ran-nssmf-simulator/requirements.txt @@ -0,0 +1,2 @@ +Flask +schematics diff --git a/test/mocks/ran-nssmf-simulator/setup.py b/test/mocks/ran-nssmf-simulator/setup.py new file mode 100644 index 000000000..bc6e20f85 --- /dev/null +++ b/test/mocks/ran-nssmf-simulator/setup.py @@ -0,0 +1,34 @@ +#! /usr/bin/python3 + +# ============LICENSE_START======================================================= +# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. +# ================================================================================ +# 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. +# ============LICENSE_END========================================================= + +from setuptools import setup, find_packages + +setup( + name="RanNssmfSimulator", + version="0.1.0", + description="RAN NSSMF Simulator", + license="Apache License, Version 2.0", + packages=find_packages(), + data_files=[ + ('RanNssmfSimulator/etc', ['RanNssmfSimulator/etc/auth.json']) + ], + install_requires=[ + 'Flask', + 'schematics' + ] +) |