Add the provisioning management service provider for 5G NRM CM

Issue-ID: INT-1387
Signed-off-by: Huang Cheng <duke.huangcheng@huawei.com>
Change-Id: I23bda3ec2a31569d4857b2f16b9a607c64abd9f0
diff --git a/test/mocks/prov-mns-provider/Dockerfile b/test/mocks/prov-mns-provider/Dockerfile
new file mode 100644
index 0000000..748ad48
--- /dev/null
+++ b/test/mocks/prov-mns-provider/Dockerfile
@@ -0,0 +1,13 @@
+FROM python:3.6
+
+WORKDIR /app
+
+COPY src/requirements.txt ./
+
+RUN pip install -r requirements.txt
+
+COPY src /app
+
+EXPOSE 8000
+
+CMD ["python", "ProvMnSProvider.py"]
diff --git a/test/mocks/prov-mns-provider/README.txt b/test/mocks/prov-mns-provider/README.txt
new file mode 100644
index 0000000..130d3b3
--- /dev/null
+++ b/test/mocks/prov-mns-provider/README.txt
@@ -0,0 +1,60 @@
+Python Dependence: python 3.6.x
+
+
+1. To specify the supported NRM function in DefinedNRMFunction.json
+
+
+2. To specify the HTTP server configuration info in ConfigInfo.json
+
+
+3. To specify the User info in UserInfo.json
+
+
+4. To specify the pre-set-MOI info in preSetMOI.json
+
+
+5. To run the HTTP EMS simulator: python ProvMnSProvider.py
+
+Build the image by using the command: docker build . -t prov-mns-provider
+Create the container and start the service by using the command: docker-compose up -d
+
+The default port number of ProvMnSProvider is : 8000
+
+The default username&password of ProvMnSProvider is : root&root
+
+ProvMnSProvider provdies four RESTful APIs:
+
+1. Sample PUT request to Create MOI
+   PUT /ProvisioningMnS/v1500/GNBCUCPFunction/35c369d0-2681-4225-9755-daf98fd20805
+   {
+    "data": {
+        "attributes": {
+            "pLMNId": {
+                "mnc": "01",
+                "mcc": "001"
+            },
+            "gNBId": "1",
+            "gNBIdLength": "5",
+            "gNBCUName": "gnb-01"
+        },
+        "href": "/GNBCUCPFunction/35c369d0-2681-4225-9755-daf98fd20805",
+        "class": "GNBCUCPFunction",
+        "id": "35c369d0-2681-4225-9755-daf98fd20805"
+    }
+   }
+
+2. Sample GET request to get MOI attributes
+   GET /ProvisioningMnS/v1500/GNBCUCPFunction/35c369d0-2681-4225-9755-daf98fd20805?scope=BASE_ONLY&filter=GNBCUCPFunction&fields=gNBId&fields=gNBIdLength
+
+3. Sample PATCH request to modify MOI attributes
+   PATCH /ProvisioningMnS/v1500/GNBCUCPFunction/35c369d0-2681-4225-9755-daf98fd20805?scope=BASE_ONLY&filter=GNBCUCPFunction
+   {
+    "data": {
+         "pLMNId": "xxx",
+         "gNBId": "1234",
+         "gNBIdLength": "4"
+    }
+   }
+
+4. Sample DELETE request to delete MOI
+   DELETE /ProvisioningMnS/v1500/GNBCUCPFunction/35c369d0-2681-4225-9755-daf98fd20805?scope=BASE_ONLY&filter=GNBCUCPFunction
diff --git a/test/mocks/prov-mns-provider/docker-compose.yaml b/test/mocks/prov-mns-provider/docker-compose.yaml
new file mode 100644
index 0000000..c44f21b
--- /dev/null
+++ b/test/mocks/prov-mns-provider/docker-compose.yaml
@@ -0,0 +1,9 @@
+version: '3.3'
+
+services:
+  ProvMnSProvider:
+    image: prov-mns-provider:latest
+    container_name: ProvMnSProvider
+    ports:
+    - "8000:8000"
+    restart: always
diff --git a/test/mocks/prov-mns-provider/src/ConfigInfo.json b/test/mocks/prov-mns-provider/src/ConfigInfo.json
new file mode 100644
index 0000000..e64a731
--- /dev/null
+++ b/test/mocks/prov-mns-provider/src/ConfigInfo.json
@@ -0,0 +1,5 @@
+{
+  "ipAddress": "0.0.0.0",
+  "portNumber": 8000,
+  "prefix": "/ProvisioningMnS/v1500"
+}
diff --git a/test/mocks/prov-mns-provider/src/DefinedNRMFunction.json b/test/mocks/prov-mns-provider/src/DefinedNRMFunction.json
new file mode 100644
index 0000000..8c685a4
--- /dev/null
+++ b/test/mocks/prov-mns-provider/src/DefinedNRMFunction.json
@@ -0,0 +1,11 @@
+{
+  "NRMFunction": [
+    "GNBDUFunction",
+    "GNBCUCPFunction",
+    "GNBCUUPFunction",
+    "EP_XnU",
+    "EP_NgC",
+    "EP_NgU",
+    "EP_XnC"
+  ]
+}
diff --git a/test/mocks/prov-mns-provider/src/ProvMnSProvider.py b/test/mocks/prov-mns-provider/src/ProvMnSProvider.py
new file mode 100644
index 0000000..e9d488e
--- /dev/null
+++ b/test/mocks/prov-mns-provider/src/ProvMnSProvider.py
@@ -0,0 +1,270 @@
+from  http.server import HTTPServer, BaseHTTPRequestHandler
+import re
+import json
+import base64
+from urllib.parse import urlparse, parse_qs
+
+with open("DefinedNRMFunction.json",'r') as f:
+    jsonFile = json.loads(f.read())
+SupportingFunctionList = jsonFile["NRMFunction"]
+
+with open("UserInfo.json",'r') as f:
+    UserFile = json.loads(f.read())
+
+with open("ConfigInfo.json",'r') as f:
+    ConfigFile = json.loads(f.read())
+
+with open("preSetMOI.json",'r') as f:
+    Cretaed_MOIs = json.loads(f.read())
+Cretaed_MOIs_list = Cretaed_MOIs['preSetMOI']
+
+ipAddress = ConfigFile["ipAddress"]
+portNumber = ConfigFile["portNumber"]
+prefix = ConfigFile["prefix"]
+
+username = UserFile['userName']
+password = UserFile['password']
+Auth_str = username+":"+password
+print(Auth_str)
+base64string = base64.b64encode(bytes(Auth_str,'utf-8'))
+authheader =  "Basic %s" % base64string.decode('utf-8')
+print(authheader)
+
+class ServerHTTP(BaseHTTPRequestHandler):
+    def do_GET(self):
+        path = self.path
+        print("\n**************************** NEW GET REQUEST ********************************")
+        request = urlparse(path)
+        print("the PATH of the received GET request:" + request.path)
+        pathlist = request.path.split('/')
+        if "/" + pathlist[1] + "/"+ pathlist[2] == prefix:
+            prefix_check = True
+        else:
+            prefix_check = False
+        className = pathlist[3]
+        idName = pathlist[4]
+        response = {}
+        query_params = parse_qs(request.query)
+        if self.headers['Authorization'] == authheader and prefix_check is True:
+            if className in SupportingFunctionList:
+                try:
+                    print("the value of the scope : "+ str(query_params['scope']))
+                    print("the value of the filter : "+ str(query_params['filter']))
+                    print("the value of the fields : "+ str(query_params['fields']))
+                except:
+                    print("the request body doesn't follow the standard format")
+                    response['error'] = "the request body doesn't follow the standard format"
+                    print("Fail to get MOI object: "+'/' +className+'/'+idName)
+                    self.send_response(406)
+                else:
+                    find_moi = False
+                    for MOI in Cretaed_MOIs_list:
+                        if (idName == MOI['id'] and className == MOI['class']):
+                            find_moi = True
+                            try:
+                                attributes = {}
+                                for field in query_params['fields']:
+                                    attributes[field] = MOI['attributes'][field]
+                            except:
+                                print("the createed MOI doesn't contain the required attribute")
+                                response['error'] = "the createed MOI doesn't contain the required attribute"
+                                print("Fail to get MOI object: "+'/' +className+'/'+idName)
+                                self.send_response(406)
+                            else:
+                                print("Successfully get MOI object: "+ className+'_'+idName)
+                                response = {"data":[{"href":"/"+className+"/"+idName,"class":className,"id":idName,"attributes":attributes}]}
+                                self.send_response(200)
+                    if (find_moi is False):
+                        response['error'] = {"errorInfo":"MOI does not exist"}
+                        print("Fail to get MOI object: "+'/' +className+'/'+idName)
+                        self.send_response(406)
+            else:
+                response['error'] = {"errorInfo":"MOI class not support"}
+                print("Fail to get MOI object: "+'/' +className+'/'+idName)
+                self.send_response(406)
+        else:
+            self.send_response(401)
+            if prefix_check is True:
+                response['error'] = {"errorInfo":"not Authorized"}
+            else:
+                response['error'] = {"errorInfo":"wrong prefix"}
+        self.send_header("Content-type","application/json")
+        self.end_headers()
+        buf = json.dumps(response)
+        self.wfile.write(bytes(buf,'utf-8'))
+
+    def do_PATCH(self):
+        path = self.path
+        print("\n**************************** NEW PATCH REQUEST ********************************")
+        request = urlparse(path)
+        print("the PATH of the received GET request:" + request.path)
+        pathlist = request.path.split('/')
+        if "/" + pathlist[1] + "/"+ pathlist[2] == prefix:
+            prefix_check = True
+        else:
+            prefix_check = False
+        className = pathlist[3]
+        idName = pathlist[4]
+        response = {}
+        query_params = parse_qs(request.query)
+        if self.headers['Authorization'] == authheader and prefix_check is True:
+            if className in SupportingFunctionList:
+                datas = self.rfile.read(int(self.headers['content-length']))
+                json_str = datas.decode('utf-8')
+                json_str = re.sub('\'','\"', json_str)
+                json_dict = json.loads(json_str)
+                try:
+                    print("the value of the scope : "+ str(query_params['scope']))
+                    print("the value of the filter : "+ str(query_params['filter']))
+                    print("the modified attribute values : "+json.dumps(json_dict['data']))
+                except:
+                    print("the request body doesn't follow the standard format")
+                    response['error'] = "the request body doesn't follow the standard format"
+                    print("Fail to modify MOI object: "+'/' +className+'/'+idName)
+                    self.send_response(406)
+                else:
+                    find_moi = False
+                    for MOI in Cretaed_MOIs_list:
+                        if (idName == MOI['id'] and className == MOI['class']):
+                            find_moi = True
+                            wrong_attribute = False
+                            for key, value in json_dict['data'].items():
+                                if (key in MOI['attributes']):
+                                    MOI['attributes'][key] = value
+                                else:
+                                    wrong_attribute = True
+                            if (wrong_attribute is True):
+                                print("the createed MOI doesn't contain the required attribute")
+                                response['error'] = "the createed MOI doesn't contain the required attribute"
+                                print("Fail to get modify object: "+'/' +className+'/'+idName)
+                                self.send_response(406)
+                            else:
+                                print("Successfully modify MOI object: "+ className+'_'+idName)
+                                response = {"data":[MOI]}
+                                self.send_response(200)
+                    if (find_moi is False):
+                        response['error'] = {"errorInfo":"MOI does not exist"}
+                        print("Fail to get MOI object: "+'/' +className+'/'+idName)
+                        self.send_response(406)
+            else:
+                response['error'] = {"errorInfo":"MOI class not support"}
+                print("Fail to modify MOI object: "+'/' +className+'/'+idName)
+                self.send_response(406)
+        else:
+            self.send_response(401)
+            if prefix_check is True:
+                response['error'] = {"errorInfo":"not Authorized"}
+            else:
+                response['error'] = {"errorInfo":"wrong prefix"}
+        self.send_header("Content-type","application/json")
+        self.end_headers()
+        buf = json.dumps(response)
+        self.wfile.write(bytes(buf,'utf-8'))
+
+    def do_DELETE(self):
+        path = self.path
+        print("\n**************************** NEW DELETE REQUEST ********************************")
+        request = urlparse(path)
+        print("the PATH of the received DELETE request:" + request.path)
+        pathlist = request.path.split('/')
+        if "/" + pathlist[1] + "/"+ pathlist[2] == prefix:
+            prefix_check = True
+        else:
+            prefix_check = False
+        className = pathlist[3]
+        idName = pathlist[4]
+        response = {}
+        query_params = parse_qs(request.query)
+        if self.headers['Authorization'] == authheader and prefix_check is True:
+            if className in SupportingFunctionList:
+                try:
+                    print("the value of the scope : "+ str(query_params['scope']))
+                    print("the value of the filter : "+ str(query_params['filter']))
+                except:
+                    print("the request body doesn't follow the standard format")
+                    response['error'] = "the request body doesn't follow the standard format"
+                    print("Fail to delete MOI object: "+'/' +className+'/'+idName)
+                    self.send_response(406)
+                else:
+                    find_moi = False
+                    for MOI in Cretaed_MOIs_list:
+                        if (idName == MOI['id'] and className == MOI['class']):
+                            find_moi = True
+                            Cretaed_MOIs_list.remove(MOI)
+                            print("Successfully delete MOI object: "+ className+'_'+idName)
+                            response = {"data":["/"+className+"/"+idName]}
+                            self.send_response(200)
+                    if (find_moi is False):
+                        response['error'] = {"errorInfo":"MOI does not exist"}
+                        print("Fail to delete MOI object: "+'/' +className+'/'+idName)
+                        self.send_response(406)
+            else:
+                response['error'] = {"errorInfo":"MOI class not support"}
+                print("Fail to delete MOI object: "+'/' +className+'/'+idName)
+                self.send_response(406)
+        else:
+            self.send_response(401)
+            if prefix_check is True:
+                response['error'] = {"errorInfo":"not Authorized"}
+            else:
+                response['error'] = {"errorInfo":"wrong prefix"}
+        self.send_header("Content-type","application/json")
+        self.end_headers()
+        buf = json.dumps(response)
+        self.wfile.write(bytes(buf,'utf-8'))
+
+    def do_PUT(self):
+        path = self.path
+        print("\n**************************** NEW PUT REQUEST ********************************")
+        print("the PATH of the received PUT request:" + path)
+        pathlist = path.split('/')
+        if "/" + pathlist[1] + "/"+ pathlist[2] == prefix:
+            prefix_check = True
+        else:
+            prefix_check = False
+        className = pathlist[3]
+        idName = pathlist[4]
+        response = {}
+        if self.headers['Authorization'] == authheader and prefix_check is True:
+            if className in SupportingFunctionList:
+                datas = self.rfile.read(int(self.headers['content-length']))
+                json_str = datas.decode('utf-8')
+                json_str = re.sub('\'','\"', json_str)
+                json_dict = json.loads(json_str)
+                try:
+                    print("the class of the New MOI : "+json_dict['data']['class'])
+                    print("the ID of the New MOI : "+json_dict['data']['id'])
+                    print("the href of the New MOI : "+json_dict['data']['href'])
+                    print("the attributes of the New MOI : "+json.dumps(json_dict['data']['attributes']))
+                except:
+                    print("the request body doesn't follow the standard format")
+                    response['error'] = "the request body doesn't follow the standard format"
+                    print("Fail to create MOI object: "+'/' +className+'/'+idName)
+                    self.send_response(406)
+                else:
+                    print("Successfully create MOI object: "+ className+'/'+idName)
+                    Cretaed_MOIs_list.append(json_dict['data'])
+                    response = json_dict
+                    self.send_response(201)
+                    self.send_header("Location",path)
+            else:
+                response['error'] = {"errorInfo":"MOI class not support"}
+                print("Fail to create MOI object: "+'/' +className+'/'+idName)
+                self.send_response(406)
+        else:
+            self.send_response(401)
+            if prefix_check is True:
+                response['error'] = {"errorInfo":"not Authorized"}
+            else:
+                response['error'] = {"errorInfo":"wrong prefix"}
+        self.send_header("Content-type","application/json")
+        self.end_headers()
+        buf = json.dumps(response)
+        self.wfile.write(bytes(buf,'utf-8'))
+
+def start_server(port):
+    http_server = HTTPServer((ipAddress, int(port)), ServerHTTP)
+    http_server.serve_forever()
+
+if __name__ == "__main__":
+    start_server(int(portNumber))
diff --git a/test/mocks/prov-mns-provider/src/UserInfo.json b/test/mocks/prov-mns-provider/src/UserInfo.json
new file mode 100644
index 0000000..f7f2ba2
--- /dev/null
+++ b/test/mocks/prov-mns-provider/src/UserInfo.json
@@ -0,0 +1,4 @@
+{
+  "userName": "root",
+  "password": "root"
+}
diff --git a/test/mocks/prov-mns-provider/src/preSetMOI.json b/test/mocks/prov-mns-provider/src/preSetMOI.json
new file mode 100644
index 0000000..7d78dbd
--- /dev/null
+++ b/test/mocks/prov-mns-provider/src/preSetMOI.json
@@ -0,0 +1,18 @@
+{
+  "preSetMOI": [
+    {
+      "attributes": {
+        "gNBCUName": "gnb-01",
+        "gNBId": "1",
+        "gNBIdLength": "5",
+        "pLMNId": {
+          "mcc": "001",
+          "mnc": "01"
+        }
+      },
+      "class": "GNBCUCPFunction",
+      "href": "/GNBCUCPFunction/e65d3f05-9558-4e58-aeb0-3a1eae1db742",
+      "id": "e65d3f05-9558-4e58-aeb0-3a1eae1db742"
+    }
+  ]
+}
diff --git a/test/mocks/prov-mns-provider/src/requirements.txt b/test/mocks/prov-mns-provider/src/requirements.txt
new file mode 100644
index 0000000..f622334
--- /dev/null
+++ b/test/mocks/prov-mns-provider/src/requirements.txt
@@ -0,0 +1,2 @@
+pip==20.0.2
+wheel==0.29.0