Add an External RAN NSSMF simulator 66/112866/5
authorEnbo Wang <wangenbo@huawei.com>
Fri, 18 Sep 2020 03:06:12 +0000 (11:06 +0800)
committerEnbo Wang <wangenbo@huawei.com>
Sat, 19 Sep 2020 09:25:04 +0000 (17:25 +0800)
Change-Id: I770091159dd1f953d8a567ccd2bd7f6d21f27649
Issue-ID: INT-1707
Signed-off-by: Enbo Wang <wangenbo@huawei.com>
test/mocks/ran-nssmf-simulator/README.md [new file with mode: 0644]
test/mocks/ran-nssmf-simulator/RanNssmfSimulator/AuthManager.py [new file with mode: 0644]
test/mocks/ran-nssmf-simulator/RanNssmfSimulator/MainApp.py [new file with mode: 0644]
test/mocks/ran-nssmf-simulator/RanNssmfSimulator/NssManager.py [new file with mode: 0644]
test/mocks/ran-nssmf-simulator/RanNssmfSimulator/SliceDataType.py [new file with mode: 0644]
test/mocks/ran-nssmf-simulator/RanNssmfSimulator/__init__.py [new file with mode: 0644]
test/mocks/ran-nssmf-simulator/RanNssmfSimulator/etc/auth.json [new file with mode: 0644]
test/mocks/ran-nssmf-simulator/RanNssmfSimulator/utils.py [new file with mode: 0644]
test/mocks/ran-nssmf-simulator/main.py [new file with mode: 0644]
test/mocks/ran-nssmf-simulator/requirements.txt [new file with mode: 0644]
test/mocks/ran-nssmf-simulator/setup.py [new file with mode: 0644]

diff --git a/test/mocks/ran-nssmf-simulator/README.md b/test/mocks/ran-nssmf-simulator/README.md
new file mode 100644 (file)
index 0000000..5a89587
--- /dev/null
@@ -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 (file)
index 0000000..6a52f51
--- /dev/null
@@ -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 (file)
index 0000000..6074e76
--- /dev/null
@@ -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 (file)
index 0000000..f515968
--- /dev/null
@@ -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 (file)
index 0000000..10ee2c7
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
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 (file)
index 0000000..23e9376
--- /dev/null
@@ -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 (file)
index 0000000..f0b9911
--- /dev/null
@@ -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 (file)
index 0000000..c2473de
--- /dev/null
@@ -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 (file)
index 0000000..531b5ba
--- /dev/null
@@ -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 (file)
index 0000000..bc6e20f
--- /dev/null
@@ -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'
+    ]
+)