Feature 10915: SOL003 STD Support for OSM 08/11908/11
authorselvi.j <selvi.j@tataelxsi.co.in>
Thu, 21 Apr 2022 14:52:15 +0000 (14:52 +0000)
committergarciadeblas <gerardo.garciadeblas@telefonica.com>
Mon, 16 May 2022 21:23:34 +0000 (23:23 +0200)
Added implementation code for SOL003 STD support API's for VNF Lifecycle management interface

Change-Id: Ibd483a99aec24b1e7d8166d9e17a899e70b27647
Signed-off-by: selvi.j <selvi.j@tataelxsi.co.in>
osm_nbi/engine.py
osm_nbi/nbi.py
osm_nbi/osm_vnfm/__init__.py [new file with mode: 0644]
osm_nbi/osm_vnfm/base_methods.py [new file with mode: 0644]
osm_nbi/osm_vnfm/vnf_instance_actions.py [new file with mode: 0644]
osm_nbi/osm_vnfm/vnf_instances.py [new file with mode: 0644]
osm_nbi/resources_to_operations.yml
osm_nbi/tests/test_db_descriptors.py
osm_nbi/tests/test_osm_vnfm.py [new file with mode: 0644]
osm_nbi/vnf_instance_topics.py [new file with mode: 0644]

index cb27414..7afaa22 100644 (file)
@@ -52,6 +52,7 @@ from osm_nbi.instance_topics import (
     NsiTopic,
     NsiLcmOpTopic,
 )
+from osm_nbi.vnf_instance_topics import VnfInstances, VnfLcmOpTopic
 from osm_nbi.pmjobs_topics import PmJobsTopic
 from osm_nbi.subscription_topics import NslcmSubscriptionsTopic
 from base64 import b64encode
@@ -85,6 +86,8 @@ class Engine(object):
         "nsilcmops": NsiLcmOpTopic,
         "vnfpkgops": VnfPkgOpTopic,
         "nslcm_subscriptions": NslcmSubscriptionsTopic,
+        "vnf_instances": VnfInstances,
+        "vnflcmops": VnfLcmOpTopic,
         # [NEW_TOPIC]: add an entry here
         # "pm_jobs": PmJobsTopic will be added manually because it needs other parameters
     }
index 502207f..9801931 100644 (file)
@@ -477,6 +477,31 @@ valid_url_methods = {
             },
         }
     },
+    "vnflcm": {
+        "v1": {
+            "vnf_instances": {"METHODS": ("GET", "POST"),
+                              "ROLE_PERMISSION": "vnflcm_instances:",
+                              "<ID>": {"METHODS": ("GET", "DELETE"),
+                                       "ROLE_PERMISSION": "vnflcm_instances:id:",
+                                       "scale": {"METHODS": ("POST",),
+                                                 "ROLE_PERMISSION": "vnflcm_instances:id:scale:"
+                                                },
+                                       "terminate": {"METHODS": ("POST",),
+                                                     "ROLE_PERMISSION": "vnflcm_instances:id:terminate:"
+                                                    },
+                                       "instantiate": {"METHODS": ("POST",),
+                                                       "ROLE_PERMISSION": "vnflcm_instances:id:instantiate:"
+                                                      },
+                                       }
+                            },
+            "vnf_lcm_op_occs": {"METHODS": ("GET",),
+                               "ROLE_PERMISSION": "vnf_instances:opps:",
+                               "<ID>": {"METHODS": ("GET",),
+                                        "ROLE_PERMISSION": "vnf_instances:opps:id:"
+                                        },
+                               },
+        }
+    },
     "nst": {
         "v1": {
             "netslice_templates_content": {
@@ -1293,6 +1318,7 @@ class Server(object):
                 "nst",
                 "nsilcm",
                 "nspm",
+                "vnflcm",
             ):
                 raise NbiException(
                     "URL main_topic '{}' not supported".format(main_topic),
@@ -1347,6 +1373,9 @@ class Server(object):
                     engine_topic = "nslcmops"
                 if topic == "vnfrs" or topic == "vnf_instances":
                     engine_topic = "vnfrs"
+            elif main_topic == "vnflcm":
+                if topic == "vnf_lcm_op_occs":
+                    engine_topic = "vnflcmops"
             elif main_topic == "nst":
                 engine_topic = "nsts"
             elif main_topic == "nsilcm":
@@ -1504,6 +1533,13 @@ class Server(object):
                         "_links": link,
                     }
                     cherrypy.response.status = HTTPStatus.CREATED.value
+                elif topic == "vnf_instances" and item:
+                    indata["lcmOperationType"] = item
+                    indata["vnfInstanceId"] = _id
+                    _id, _ = self.engine.new_item(rollback, engine_session, "vnflcmops", indata, kwargs)
+                    self._set_location_header(main_topic, version, "vnf_lcm_op_occs", _id)
+                    outdata = {"id": _id}
+                    cherrypy.response.status = HTTPStatus.ACCEPTED.value
                 else:
                     _id, op_id = self.engine.new_item(
                         rollback,
diff --git a/osm_nbi/osm_vnfm/__init__.py b/osm_nbi/osm_vnfm/__init__.py
new file mode 100644 (file)
index 0000000..b62a185
--- /dev/null
@@ -0,0 +1,14 @@
+# Copyright 2021 K Sai Kiran (Tata Elxsi)
+#
+# 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.
diff --git a/osm_nbi/osm_vnfm/base_methods.py b/osm_nbi/osm_vnfm/base_methods.py
new file mode 100644 (file)
index 0000000..f7ca5f4
--- /dev/null
@@ -0,0 +1,29 @@
+# Copyright 2021 K Sai Kiran (Tata Elxsi)
+#
+# 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.
+
+__author__ = "K Sai Kiran <saikiran.k@tataelxsi.co.in>, Selvi Jayaraman <selvi.j@tataelxsi.co.in>"
+__date__ = "$12-June-2021 8:30:59$"
+
+
+import logging
+
+
+class BaseMethod:
+
+    def __init__(self):
+        """
+        Constructor of the base method
+        """
+        self.logger = logging.getLogger("nbi.engine")
diff --git a/osm_nbi/osm_vnfm/vnf_instance_actions.py b/osm_nbi/osm_vnfm/vnf_instance_actions.py
new file mode 100644 (file)
index 0000000..93c91c5
--- /dev/null
@@ -0,0 +1,226 @@
+# Copyright 2021 K Sai Kiran (Tata Elxsi)
+#
+# 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.
+
+__author__ = "K Sai Kiran <saikiran.k@tataelxsi.co.in>, Selvi Jayaraman <selvi.j@tataelxsi.co.in>"
+__date__ = "$12-June-2021 8:30:59$"
+
+from osm_nbi.instance_topics import NsrTopic, NsLcmOpTopic, VnfrTopic
+from .base_methods import BaseMethod
+
+
+class VnfLcmOp2NsLcmOp:
+
+    def __init__(self, db, fs, msg, auth):
+        """
+        Constructor of Vnf lcm op to Ns lcm op
+        """
+        self.new_vnf_lcmop = NewVnfLcmOp(db, fs, msg, auth)
+        self.list_vnf_lcmop = ListVnfLcmOp(db, fs, msg, auth)
+        self.show_vnf_lcmop = ShowVnfLcmOp(db, fs, msg, auth)
+
+    def new(self, rollback, session, indata=None, kwargs=None, headers=None):
+        """
+        Creates a new entry into database.
+        :param rollback: list to append created items at database in case a rollback may to be done
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param indata: data to be inserted
+        :param kwargs: used to override the indata descriptor
+        :param headers: http request headers
+        :return: _id, op_id:
+            _id: identity of the inserted data.
+             op_id: operation id if this is asynchronous, None otherwise
+        """
+        return self.new_vnf_lcmop.action(rollback, session, indata, kwargs, headers)
+
+    def list(self, session, filter_q=None, api_req=False):
+        """
+        Get a list of the Vnf Lcm Operation that matches a filter
+        :param session: contains the used login username and working project
+        :param filter_q: filter of data to be applied
+        :param api_req: True if this call is serving an external API request. False if serving internal request.
+        :return: The list, it can be empty if no one match the filter.
+        """
+        return self.list_vnf_lcmop.action(session, filter_q, api_req)
+
+    def show(self, session, _id, api_req=False):
+        """
+        Get complete information on an Vnf Lcm Operation
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param _id: server internal id
+        :param api_req: True if this call is serving an external API request. False if serving internal request.
+        :return: dictionary, raise exception if not found.
+        """
+        return self.show_vnf_lcmop.action(session, _id, api_req)
+
+
+class NewVnfLcmOp(BaseMethod):
+
+    def __init__(self, db, fs, msg, auth):
+        """
+        Constructor of new Vnf Lcm Op
+        """
+        super().__init__()
+        self.msg = msg
+        self.nslcmoptopic = NsLcmOpTopic(db, fs, msg, auth)
+        self.nsrtopic = NsrTopic(db, fs, msg, auth)
+        self.vnfrtopic = VnfrTopic(db, fs, msg, auth)
+
+    def __get_nsdid(self, session, vnf_instance_id):
+        """
+        Returns a nsd id from vnf instance id.
+        :param session: contains the used login username and working project
+        :param vnf_instance_id: id of vnf instance
+        :return: id of nsd id
+        """
+        nsr = self.nsrtopic.show(session, vnf_instance_id)
+        return nsr['nsd']['_id']
+
+    def __get_formatted_indata(self, session, indata):
+        """
+        Returns formatted data for new vnf lcm op
+        :param session: contains the used login username and working project
+        :param indata: contains information for new lcm operation.
+        :return: formatted indata for new lcm op.
+        """
+        formatted_indata = {}
+        if indata["lcmOperationType"] == "instantiate":
+            formatted_indata = {
+                "nsName": indata["vnfName"],
+                "nsDescription": indata["vnfDescription"],
+                "nsdId": self.__get_nsdid(session, indata["vnfInstanceId"]),
+                "vimAccountId": indata["vimAccountId"],
+                "nsr_id": indata["vnfInstanceId"],
+                "lcmOperationType": indata["lcmOperationType"],
+                "nsInstanceId": indata["vnfInstanceId"]
+            }
+        elif indata["lcmOperationType"] == "terminate":
+            formatted_indata = {
+                "lcmOperationType": indata["lcmOperationType"],
+                "nsInstanceId": indata["vnfInstanceId"]
+            }
+        elif indata["lcmOperationType"] == "scale":
+            formatted_indata = {
+                "lcmOperationType": indata["lcmOperationType"],
+                "nsInstanceId": indata["vnfInstanceId"],
+                "scaleType": "SCALE_VNF",
+                "scaleVnfData": {
+                    "scaleVnfType": indata["type"],
+                    "scaleByStepData": {
+                        "scaling-group-descriptor": indata["aspectId"],
+                        "member-vnf-index": indata["additionalParams"]["member-vnf-index"]
+                    }
+                }
+            }
+        elif indata["lcmOperationType"] == "action":
+            formatted_indata = {
+                "lcmOperationType": indata["lcmOperationType"],
+                "nsInstanceId": indata["vnfInstanceId"],
+                "member_vnf_index": indata["member_vnf_index"],
+                "primitive": indata["primitive"],
+                "primitive_params": indata["primitive_params"]
+            }
+        return formatted_indata
+
+    def action(self, rollback, session, indata=None, kwargs=None, headers=None):
+        """
+        Creates an new lcm operation.
+        :param rollback: list to append the created items at database in case a rollback must be done
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param indata: params to be used for the nsr
+        :param kwargs: used to override the indata
+        :param headers: http request headers
+        :return: id of new lcm operation.
+        """
+        vnfInstanceId = indata["vnfInstanceId"]
+        lcm_operation = indata["lcmOperationType"]
+        vnfr = self.vnfrtopic.show(session, vnfInstanceId)
+        indata["vnfInstanceId"] = vnfr.get("nsr-id-ref")
+        indata = self.__get_formatted_indata(session, indata)
+        op_id, _ = self.nslcmoptopic.new(rollback, session, indata, kwargs, headers)
+        return op_id, _
+
+
+class ListVnfLcmOp(BaseMethod):
+
+    def __init__(self, db, fs, msg, auth):
+        """
+        Constructor call for listing vnf lcm operations
+        """
+        super().__init__()
+        self.nslcmoptopic = NsLcmOpTopic(db, fs, msg, auth)
+        self.nsrtopic = NsrTopic(db, fs, msg, auth)
+
+    def action(self, session, filter_q=None, api_req=False):
+        """
+        To get list of vnf lcm operations that matches a filter
+        :param session: contains the used login username and working project
+        :param filter_q: filter of data to be applied
+        :param api_req: True if this call is serving an external API request. False if serving internal request.
+        :return: The list, it can be empty if no one match the filter.
+        """
+        list = []
+        records = self.nslcmoptopic.list(session, filter_q, api_req)
+        for record in records:
+            ns_id = record.get("nsInstanceId")
+            nsr = self.nsrtopic.show(session, ns_id)
+            vnfInstance_id = nsr['constituent-vnfr-ref'][0]
+            outdata = sol003_projection(record, vnfInstance_id)
+            list.append(outdata)
+        return list
+
+
+class ShowVnfLcmOp(BaseMethod):
+
+    def __init__(self, db, fs, msg, auth):
+        """
+        Constructor call for showing vnf lcm operation
+        """
+        super().__init__()
+        self.nslcmoptopic = NsLcmOpTopic(db, fs, msg, auth)
+        self.nsrtopic = NsrTopic(db, fs, msg, auth)
+
+    def action(self, session, _id, api_req=False):
+        """
+        Get complete information on an Vnf Lcm Operation.
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param _id: Vnf Lcm operation id
+        :param api_req: True if this call is serving an external API request. False if serving internal request.
+        :return: dictionary, raise exception if not found.
+        """
+        record = self.nslcmoptopic.show(session, _id, api_req)
+        ns_id = record.get("nsInstanceId")
+        nsr = self.nsrtopic.show(session, ns_id)
+        vnfinstance_id = nsr['constituent-vnfr-ref'][0]
+        outdata = sol003_projection(record, vnfinstance_id)
+        return outdata
+
+
+def sol003_projection(data, vnfinstance_id):
+    """
+    Returns SOL003 formatted data
+    :param data: contains Lcm Operation information
+    :param vnfinstance_id: id of vnf_instance
+    :return: SOL003 formatted data of vnf lcm op
+    """
+    data.pop("nsInstanceId")
+    data.pop("operationParams")
+    data.pop("links")
+    links = {
+        "self": "/osm/vnflcm/v1/vnf_lcm_op_occs/" + data["_id"],
+        "vnfInstance": "/osm/vnflcm/v1/vnf_instances/" + vnfinstance_id,
+    }
+    data["_links"] = links
+    data["vnfInstanceId"] = vnfinstance_id
+    return data
diff --git a/osm_nbi/osm_vnfm/vnf_instances.py b/osm_nbi/osm_vnfm/vnf_instances.py
new file mode 100644 (file)
index 0000000..a41f6d5
--- /dev/null
@@ -0,0 +1,309 @@
+# Copyright 2021 K Sai Kiran (Tata Elxsi)
+#
+# 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.
+
+__author__ = "K Sai Kiran <saikiran.k@tataelxsi.co.in>, Selvi Jayaraman <selvi.j@tataelxsi.co.in>"
+__date__ = "$12-June-2021 8:30:59$"
+
+from copy import deepcopy
+from osm_nbi.descriptor_topics import NsdTopic
+from .base_methods import BaseMethod
+
+from osm_nbi.instance_topics import NsrTopic, VnfrTopic
+
+
+class VnfInstances2NsInstances:
+
+    def __init__(self, db, fs, msg, auth):
+        """
+        Constructor of Vnf Instances to Ns Instances
+        """
+        self.new_vnf_instance = NewVnfInstance(db, fs, msg, auth)
+        self.list_vnf_instance = ListVnfInstance(db, fs, msg, auth)
+        self.show_vnf_instance = ShowVnfInstance(db, fs, msg, auth)
+        self.delete_vnf_instance = DeleteVnfInstance(db, fs, msg, auth)
+
+    def new(self, rollback, session, indata=None, kwargs=None, headers=None):
+        """
+        Creates a new entry into database.
+        :param rollback: list to append created items at database in case a rollback may have to be done
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param indata: data to be inserted
+        :param kwargs: used to override the indata descriptor
+        :param headers: http request headers
+        :return: _id, op_id:
+            _id: identity of the inserted data.
+             op_id: operation id if this is asynchronous, None otherwise
+        """
+        return self.new_vnf_instance.action(rollback, session, indata, kwargs, headers)
+
+    def list(self, session, filter_q=None, api_req=False):
+        """
+        Get a list of the Vnfs that match a filter
+        :param session: contains the used login username and working project
+        :param filter_q: filter of data to be applied
+        :param api_req: True if this call is serving an external API request. False if serving internal request.
+        :return: The list, it can be empty if no one match the filter.
+        """
+        return self.list_vnf_instance.action(session, filter_q, api_req)
+
+    def show(self, session, _id, api_req=False):
+        """
+        Get complete information on an Vnf
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param _id: server internal id
+        :param api_req: True if this call is serving an external API request. False if serving internal request.
+        :return: dictionary, raise exception if not found.
+        """
+        return self.show_vnf_instance.action(session, _id, api_req)
+
+    def delete(self, session, _id, dry_run=False, not_send_msg=None):
+        """
+        Delete item by its internal _id
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param _id: server internal id
+        :param dry_run: make checking but do not delete
+        :param not_send_msg: To not send message (False) or store content (list) instead
+        :return: operation id (None if there is not operation), raise exception if error or not found, conflict, ...
+        """
+        return self.delete_vnf_instance.action(session, _id, dry_run, not_send_msg)
+
+
+class NewVnfInstance(BaseMethod):
+
+    # sample ns descriptor
+    sample_nsd = {
+        "nsd": {
+            "nsd": [
+                {
+                    "df": [
+                        {
+                            "id": "default-df",
+                            "vnf-profile": [
+                                {
+                                    "id": 1,
+                                    "virtual-link-connectivity": [
+                                        {
+                                            "constituent-cpd-id": [
+                                                {"constituent-base-element-id": 1,
+                                                 "constituent-cpd-id": "eth0-ext"}
+                                            ],
+                                            "virtual-link-profile-id": "mgmtnet",
+                                        }
+                                    ],
+                                    "vnfd-id": "cirros_vnfd"
+                                }
+                            ],
+                        }
+                    ],
+                    "vnfd-id": ["cirros_vnfd"],
+                    "description": "Generated by OSM pacakage generator",
+                    "id": "cirros_2vnf_nsd",
+                    "name": "cirros_2vnf_ns",
+                    "short-name": "cirros_2vnf_ns",
+                    "vendor": "OSM",
+                    "version": "1.0",
+                }
+            ]
+        }
+    }
+
+    @staticmethod
+    def __get_formatted_indata(indata, nsd_id):
+        """
+        Create indata for nsd_id
+        :param indata: Contains unformatted data for new vnf instance
+        :param nsd_id: Id of nsd
+        :return: formatted indata for nsd_id
+        """
+        formatted_indata = deepcopy(indata)
+        formatted_indata["nsdId"] = nsd_id
+        formatted_indata["nsName"] = indata["vnfInstanceName"] + "-ns"
+        for invalid_key in ("vnfdId", "vnfInstanceName", "vnfInstanceDescription", "additionalParams"):
+            formatted_indata.pop(invalid_key)
+        return formatted_indata
+
+    def __init__(self, db, fs, msg, auth):
+        """
+        Constructor for new vnf instance
+        """
+        super().__init__()
+        self.msg = msg
+        self.nsdtopic = NsdTopic(db, fs, msg, auth)
+        self.nsrtopic = NsrTopic(db, fs, msg, auth)
+
+    def __get_vnfd(self):
+        # get vnfd from nfvo
+        pass
+
+    def __onboard_vnfd(self):
+        self.__get_vnfd()
+        pass
+
+    def __create_nsd(self, rollback, session, indata=None, kwargs=None, headers=None):
+        """
+        Creates new ns descriptor from a vnfd.
+        :param rollback: list to append the created items at database in case a rollback must be done
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param indata: params to be used for the nsr
+        :param kwargs: used to override the indata
+        :param headers: http request headers
+        :return: id of new nsd created
+        """
+        _id, *others = self.nsdtopic.new(rollback, session, {}, None, headers)
+        new_nsd = deepcopy(NewVnfInstance.sample_nsd)
+        vnf_content = {
+              "id":"default-df",
+              "vnf-profile": [
+                {
+                  "id": "1",
+                  "virtual-link-connectivity": [
+                    {
+                      "constituent-cpd-id": [
+                        {
+                          "constituent-base-element-id": "1",
+                          "constituent-cpd-id": indata["additionalParams"]["constituent-cpd-id"]
+                        }
+                      ],
+                      "virtual-link-profile-id": indata["additionalParams"]["virtual-link-profile-id"]
+                    }
+                  ],
+                  "vnfd-id": indata["vnfdId"]
+                }
+              ]
+        }
+        new_nsd["nsd"]["nsd"][0] = {
+            "description": indata["vnfInstanceDescription"],
+            "designer": "OSM",
+            "id": indata["vnfdId"] + "-ns",
+            "name": indata["vnfdId"] + "-ns",
+            "version": "1.0",
+            "df": [vnf_content, ],
+            "virtual-link-desc": indata["additionalParams"]["virtual-link-desc"],
+            "vnfd-id": [indata["vnfdId"]]
+        }
+        return _id, new_nsd
+
+    def __create_nsr(self, rollback, session, indata=None, kwargs=None, headers=None):
+        """
+        Creates an new ns record in database
+        :param rollback: list to append the created items at database in case a rollback must be done
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param indata: params to be used for the nsr
+        :param kwargs: used to override the indata
+        :param headers: http request headers
+        :return: id of new nsr
+        """
+        return self.nsrtopic.new(rollback, session, indata, kwargs, headers)
+
+    def __action_pre_processing(self, rollback, session, indata=None, kwargs=None, headers=None):
+        """
+        Pre process for creating new vnf instance
+        :param rollback: list to append the created items at database in case a rollback must be done
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param indata: params to be used for the nsr
+        :param kwargs: used to override the indata
+        :param headers: http request headers
+        :return: id nsr
+        """
+        self.__onboard_vnfd()
+        nsd_id, nsd = self.__create_nsd(rollback, session, indata, kwargs, headers)
+        self.nsdtopic.upload_content(session, nsd_id, nsd, kwargs, headers)
+        formatted_indata = NewVnfInstance.__get_formatted_indata(indata, nsd_id)
+        nsr_id, _ = self.__create_nsr(rollback, session, formatted_indata, kwargs, headers)
+        nsr = self.nsrtopic.show(session, nsr_id)
+        vnfr_id =  nsr['constituent-vnfr-ref'][0]
+        return vnfr_id, None
+
+    def action(self, rollback, session, indata=None, kwargs=None, headers=None):
+        """
+        Creates an new vnf instance
+        :param rollback: list to append the created items at database in case a rollback must be done
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param indata: params to be used for the nsr
+        :param kwargs: used to override the indata
+        :param headers: http request headers
+        :return: id of new vnf instance
+        """
+        return self.__action_pre_processing(rollback, session, indata, kwargs, headers)
+
+
+class ListVnfInstance(BaseMethod):
+
+    def __init__(self, db, fs, msg, auth):
+        """
+        Constructor call for listing vnfs
+        """
+        super().__init__()
+        self.vnfrtopic = VnfrTopic(db, fs, msg, auth)
+
+    def action(self, session, filter_q=None, api_req=False):
+        """
+        To get list of vnfs that matches a filter
+        :param session: contains the used login username and working project
+        :param filter_q: filter of data to be applied
+        :param api_req: True if this call is serving an external API request. False if serving internal request.
+        :return: The list, it can be empty if no one match the filter.
+        """
+        return self.vnfrtopic.list(session, filter_q, api_req)
+
+
+class ShowVnfInstance(BaseMethod):
+
+    def __init__(self, db, fs, msg, auth):
+        """
+        Constructor call for showing vnf lcm operation
+        """
+        super().__init__()
+        self.vnfrtopic = VnfrTopic(db, fs, msg, auth)
+
+    def action(self, session, _id, api_req=False):
+        """
+        Get complete information on an Vnf Lcm Operation.
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param _id: Vnf Lcm operation id
+        :param api_req: True if this call is serving an external API request. False if serving internal request.
+        :return: dictionary, raise exception if not found.
+        """
+        return self.vnfrtopic.show(session, _id, api_req)
+
+
+class DeleteVnfInstance(BaseMethod):
+
+    def __init__(self, db, fs, msg, auth):
+        """
+        Constructor call for deleting vnf
+        """
+        super().__init__()
+        self.msg = msg
+        self.nsrtopic = NsrTopic(db, fs, msg, auth)
+        self.nsdtopic = NsdTopic(db, fs, msg, auth)
+        self.vnfrtopic = VnfrTopic(db, fs, msg, auth)
+
+    def action(self, session, _id, dry_run=False, not_send_msg=None):
+        """
+        Delete vnf instance by its internal _id
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param _id: server internal id
+        :param dry_run: make checking but do not delete
+        :param not_send_msg: To not send message (False) or store content (list) instead
+        :return: operation id (None if there is not operation), raise exception if error or not found, conflict, ...
+        """
+        vnfInstanceId = _id
+        vnfr = self.vnfrtopic.show(session, vnfInstanceId)
+        ns_id = vnfr.get("nsr-id-ref")
+        nsr = self.nsrtopic.show(session, ns_id)
+        nsd_to_del = nsr['nsd']['_id']
+        self.nsrtopic.delete(session, ns_id, dry_run, not_send_msg)
+        return self.nsdtopic.delete(session, nsd_to_del, dry_run, not_send_msg)
index 07f6a8f..25092c7 100644 (file)
@@ -147,6 +147,12 @@ resources_to_operations:
   "GET /nslcm/v1/vnfrs/<vnfInstanceId>": "vnf_instances:id:get"
   "GET /nslcm/v1/vnf_instances/<vnfInstanceId>": "vnf_instances:id:get"
 
+  "GET /vnflcm/v1/vnf_instances/": "vnflcm_instances:get"
+  "POST /vnflcm/v1/vnf_instances/": "vnflcm_instances:post"
+
+  "GET /vnflcm/v1/vnf_instances/<vnfInstanceId>": "vnflcm_instances:id:get"
+  "DELETE /vnflcm/v1/vnf_instances/<vnfInstanceId>": "vnflcm_instances:id:delete"
+
 ################################################################################
 #################################### Tokens ####################################
 ################################################################################
index 8b0b226..ea3cdd8 100644 (file)
@@ -623,3 +623,148 @@ db_vnfrs_text = """
     vnfd-id: 7637bcf8-cf14-42dc-ad70-c66fcf1e6e77
     vnfd-ref: hackfest3charmed-vnf
 """
+
+db_vnfm_vnfd_text = """
+---
+-   _admin:
+        created: 1647529096.3635302
+        modified: 1650456936.518325
+        onboardingState: ONBOARDED
+        operationalState: ENABLED
+        projects_read:
+        - 25b5aebf-3da1-49ed-99de-1d2b4a86d6e4
+        projects_write:
+        - 25b5aebf-3da1-49ed-99de-1d2b4a86d6e4
+        storage:
+            descriptor: hackfest_basic_metrics_vnf/hackfest_basic_metrics_vnfd.yaml
+            folder: 70b47595-fafa-4f63-904b-fc3ada60eebb
+            fs: mongo
+            path: /app/storage/
+            pkg-dir: hackfest_basic_metrics_vnf
+            zipfile: package.tar.gz
+        type: vnfd
+        usageState: NOT_IN_USE
+        userDefinedData: {}
+    _id: 70b47595-fafa-4f63-904b-fc3ada60eebb
+    _links:
+        packageContent:
+            href: /vnfpkgm/v1/vnf_packages/70b47595-fafa-4f63-904b-fc3ada60eebb/package_content
+        self:
+            href: /vnfpkgm/v1/vnf_packages/70b47595-fafa-4f63-904b-fc3ada60eebb
+        vnfd:
+            href: /vnfpkgm/v1/vnf_packages/70b47595-fafa-4f63-904b-fc3ada60eebb/vnfd
+    description: A basic VNF descriptor with one VDU and VIM metrics
+    df:
+    -   id: default-df
+        instantiation-level:
+        -   id: default-instantiation-level
+            vdu-level:
+            -   number-of-instances: 1
+                vdu-id: hackfest_basic_metrics-VM
+        scaling-aspect:
+        -   aspect-delta-details:
+                deltas:
+                -   id: vdu_autoscale-delta
+                    vdu-delta:
+                    -   id: hackfest_basic_metrics-VM
+                        number-of-instances: 1
+            id: vdu_autoscale
+            max-scale-level: 1
+            name: vdu_autoscale
+            scaling-policy:
+            -   cooldown-time: 120
+                name: cpu_util_above_threshold
+                scaling-criteria:
+                -   name: cpu_util_above_threshold
+                    scale-in-relational-operation: LT
+                    scale-in-threshold: '10.0000000000'
+                    scale-out-relational-operation: GT
+                    scale-out-threshold: '60.0000000000'
+                    vnf-monitoring-param-ref: vnf_cpu_util
+                scaling-type: automatic
+                threshold-time: 10
+        vdu-profile:
+        -   id: hackfest_basic_metrics-VM
+            max-number-of-instances: 2
+            min-number-of-instances: 1
+    ext-cpd:
+    -   id: vnf-cp0-ext
+        int-cpd:
+            cpd: vdu-eth0-int
+            vdu-id: hackfest_basic_metrics-VM
+    id: hackfest_basic_metrics-vnf
+    mgmt-cp: vnf-cp0-ext
+    onboardingState: ONBOARDED
+    operationalState: ENABLED
+    product-name: hackfest_basic_metrics-vnf
+    sw-image-desc:
+    -   id: bionic
+        image: bionic
+        name: bionic
+    -   id: ubuntu18.04-aws
+        image: ubuntu/images/hvm-ssd/ubuntu-artful-17.10-amd64-server-20180509
+        name: ubuntu18.04-aws
+        vim-type: aws
+    -   id: ubuntu18.04-azure
+        image: Canonical:UbuntuServer:18.04-LTS:latest
+        name: ubuntu18.04-azure
+        vim-type: azure
+    -   id: ubuntu18.04-gcp
+        image: ubuntu-os-cloud:image-family:ubuntu-1804-lts
+        name: ubuntu18.04-gcp
+        vim-type: gcp
+    usageState: NOT_IN_USE
+    vdu:
+    -   alarm:
+        -   actions:
+                alarm:
+                -   url: https://webhook.site/b79f9bf9-4c19-429d-81ed-19be26a3d5d8
+                insufficient-data:
+                -   url: https://webhook.site/b79f9bf9-4c19-429d-81ed-19be26a3d5d8
+                ok:
+                -   url: https://webhook.site/b79f9bf9-4c19-429d-81ed-19be26a3d5d8
+            alarm-id: alarm-1
+            operation: LT
+            value: '20.0000'
+            vnf-monitoring-param-ref: vnf_cpu_util
+        alternative-sw-image-desc:
+        - ubuntu18.04-aws
+        - ubuntu18.04-azure
+        - ubuntu18.04-gcp
+        cloud-init-file: cloud-config
+        id: hackfest_basic_metrics-VM
+        int-cpd:
+        -   id: vdu-eth0-int
+            virtual-network-interface-requirement:
+            -   name: vdu-eth0
+                virtual-interface:
+                    type: PARAVIRT
+        monitoring-parameter:
+        -   id: vnf_cpu_util
+            name: vnf_cpu_util
+            performance-metric: cpu_utilization
+        -   id: vnf_memory_util
+            name: vnf_memory_util
+            performance-metric: average_memory_utilization
+        -   id: vnf_packets_sent
+            name: vnf_packets_sent
+            performance-metric: packets_sent
+        -   id: vnf_packets_received
+            name: vnf_packets_received
+            performance-metric: packets_received
+        name: hackfest_basic_metrics-VM
+        sw-image-desc: bionic
+        virtual-compute-desc: hackfest_basic_metrics-VM-compute
+        virtual-storage-desc:
+        - hackfest_basic_metrics-VM-storage
+    version: '1.0'
+    virtual-compute-desc:
+    -   id: hackfest_basic_metrics-VM-compute
+        virtual-cpu:
+            num-virtual-cpu: 1
+        virtual-memory:
+            size: 1.0
+    virtual-storage-desc:
+    -   id: hackfest_basic_metrics-VM-storage
+        size-of-storage: '10'
+"""
diff --git a/osm_nbi/tests/test_osm_vnfm.py b/osm_nbi/tests/test_osm_vnfm.py
new file mode 100644 (file)
index 0000000..a7ea8a3
--- /dev/null
@@ -0,0 +1,227 @@
+# Copyright 2021 Selvi Jayaraman (Tata Elxsi)
+#
+# 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.
+
+__author__ = "Selvi Jayaraman <selvi.j@tataelxsi.co.in>"
+
+import unittest
+from uuid import uuid4
+from unittest.mock import Mock, patch, mock_open
+from osm_common.dbmemory import DbMemory
+from osm_common.fsbase import FsBase
+from osm_common.msgbase import MsgBase
+from osm_nbi.vnf_instance_topics import VnfInstances, VnfLcmOpTopic
+from osm_nbi.instance_topics import NsrTopic
+from osm_nbi.tests.test_db_descriptors import (
+    db_vim_accounts_text,
+    db_vnfm_vnfd_text,
+    db_nsds_text,
+    db_nsrs_text,
+    db_vnfrs_text,
+    db_nslcmops_text
+)
+import yaml
+
+
+class TestVnfInstances(unittest.TestCase):
+    def setUp(self):
+        self.db = DbMemory()
+        self.fs = Mock(FsBase())
+        self.msg = Mock(MsgBase())
+        self.vnfinstances = VnfInstances(self.db, self.fs, self.msg, None)
+        self.nsrtopic = NsrTopic(self.db, self.fs, self.msg, None)
+        self.db.create_list(
+            "vim_accounts", yaml.load(db_vim_accounts_text, Loader=yaml.Loader)
+        )
+        self.db.create_list("vnfds", yaml.load(db_vnfm_vnfd_text, Loader=yaml.Loader))
+        self.vnfd = self.db.get_list("vnfds")[0]
+        self.vnfd_id = self.vnfd["id"]
+        self.vnfd_project = self.vnfd["_admin"]["projects_read"][0]
+
+        self.vim = self.db.get_list("vim_accounts")[0]
+        self.vim_id = self.vim["_id"]
+
+    @patch("osm_nbi.descriptor_topics.shutil")
+    @patch("osm_nbi.descriptor_topics.os.rename")
+    def test_create_identifier(self, mock_rename, mock_shutil):
+        session = {
+            "force": True,
+            "admin": False,
+            "public": False,
+            "project_id": [self.vnfd_project],
+            "method": "write",
+        }
+        indata = {
+            "vnfdId": self.vnfd_id,
+            "vnfInstanceName": "vnf_instance_name",
+            "vnfInstanceDescription": "vnf instance description",
+            "vimAccountId": self.vim_id,
+            "additionalParams": {
+                "virtual-link-desc": [
+                    {
+                        "id": "mgmt-net",
+                        "mgmt-network": True
+                    }
+                ],
+                "constituent-cpd-id": "vnf-cp0-ext",
+                "virtual-link-profile-id": "mgmt-net"
+            }
+        }
+        rollback = []
+        headers = {}
+        self.fs.path = ""
+        self.fs.get_params.return_value = {}
+        self.fs.file_exists.return_value = False
+        self.fs.file_open.side_effect = lambda path, mode: open(
+                "/tmp/" + str(uuid4()), "a+b"
+                )
+
+        vnfr_id, _ = self.vnfinstances.new(
+            rollback, session, indata, {}, headers={"Content-Type": []}
+        )
+        vnfr = self.db.get_one("vnfrs")
+        self.assertEqual(
+                vnfr_id,
+                vnfr["id"],
+                "Mismatch between return id and database id"
+                )
+        self.assertEqual(
+                "NOT_INSTANTIATED",
+                vnfr["_admin"]["nsState"],
+                "Database record must contain 'nsState' NOT_INSTANTIATED"
+                )
+        self.assertEqual(
+                self.vnfd_id,
+                vnfr["vnfd-ref"],
+                "vnfr record is not properly created for the given vnfd")
+
+    def test_show_vnfinstance(self):
+        session = {
+            "force": False,
+            "admin": False,
+            "public": False,
+            "project_id": [self.vnfd_project],
+            "method": "write",
+        }
+        filter_q = {}
+        self.db.create_list("vnfrs", yaml.load(db_vnfrs_text, Loader=yaml.Loader))
+        actual_vnfr = self.db.get_list("vnfrs")[0]
+        id = actual_vnfr["_id"]
+        expected_vnfr = self.vnfinstances.show(session, id, filter_q)
+        self.assertEqual(
+            actual_vnfr["_id"],
+            expected_vnfr["_id"],
+            "Mismatch between return vnfr Id and database vnfr Id"
+        )
+
+    def test_delete_vnfinstance(self):
+        session = {
+            "force": False,
+            "admin": False,
+            "public": False,
+            "project_id": [self.vnfd_project],
+            "method": "delete",
+        }
+        self.db.create_list("vnfrs", yaml.load(db_vnfrs_text, Loader=yaml.Loader))
+        self.db.create_list("nsrs", yaml.load(db_nsrs_text, Loader=yaml.Loader))
+        self.db.create_list("nsds", yaml.load(db_nsds_text, Loader=yaml.Loader))
+
+        self.vnfr = self.db.get_list("vnfrs")[0]
+        self.vnfr_id = self.vnfr["_id"]
+        self.db.set_one = self.db.set_one
+        self.db.set_one = Mock()
+
+        self.vnfinstances.delete(session, self.vnfr_id)
+        msg_args = self.msg.write.call_args[0]
+        self.assertEqual(msg_args[1], "deleted", "Wrong message action")
+
+
+class TestVnfLcmOpTopic(unittest.TestCase):
+    def setUp(self):
+        self.db = DbMemory()
+        self.fs = Mock(FsBase())
+        self.fs.get_params.return_value = {"./fake/folder"}
+        self.fs.file_open = mock_open()
+        self.msg = Mock(MsgBase())
+
+        self.vnflcmop_topic = VnfLcmOpTopic(self.db, self.fs, self.msg, None)
+        self.vnflcmop_topic.check_quota = Mock(return_value=None)  # skip quota
+
+        self.db.create_list(
+            "vim_accounts", yaml.load(db_vim_accounts_text, Loader=yaml.Loader)
+        )
+        self.db.create_list("nsds", yaml.load(db_nsds_text, Loader=yaml.Loader))
+        self.db.create_list("vnfds", yaml.load(db_vnfm_vnfd_text, Loader=yaml.Loader))
+        self.db.create_list("vnfrs", yaml.load(db_vnfrs_text, Loader=yaml.Loader))
+        self.db.create_list("nsrs", yaml.load(db_nsrs_text, Loader=yaml.Loader))
+
+        self.vnfd = self.db.get_list("vnfds")[0]
+        self.vnfd_id = self.vnfd["_id"]
+        self.vnfr = self.db.get_list("vnfrs")[0]
+        self.vnfr_id = self.vnfr["_id"]
+
+        self.vnfd_project = self.vnfd["_admin"]["projects_read"][0]
+
+        self.vim = self.db.get_list("vim_accounts")[0]
+        self.vim_id = self.vim["_id"]
+
+    def test_create_vnf_instantiate(self):
+        session = {
+            "force": False,
+            "admin": False,
+            "public": False,
+            "project_id": [self.vnfd_project],
+            "method": "write",
+        }
+        indata = {
+            "vnfInstanceId": self.vnfr_id,
+            "lcmOperationType": "instantiate",
+            "vnfName": "vnf_instance_name",
+            "vnfDescription": "vnf instance description",
+            "vnfId": self.vnfd_id,
+            "vimAccountId": self.vim_id
+        }
+        rollback = []
+        headers = {}
+        vnflcmop_id, _ = self.vnflcmop_topic.new(
+            rollback, session, indata, kwargs=None, headers=headers
+        )
+        vnflcmop_info = self.db.get_one("nslcmops")
+        self.assertEqual(
+            vnflcmop_id,
+            vnflcmop_info["_id"],
+            "Mismatch between return id and database '_id'",
+        )
+        self.assertTrue(
+            vnflcmop_info["lcmOperationType"] == "instantiate",
+            "Database record must contain 'lcmOperationType=instantiate'",
+        )
+
+    def test_show_vnflmcop(self):
+        session = {
+            "force": False,
+            "admin": False,
+            "public": False,
+            "project_id": [self.vnfd_project],
+            "method": "write",
+        }
+        self.db.create_list("nslcmops", yaml.load(db_nslcmops_text, Loader=yaml.Loader))
+        filter_q = {}
+        actual_lcmop = self.db.get_list("nslcmops")[0]
+        id = actual_lcmop["_id"]
+        vnfr = self.db.get_list("vnfrs")[0]
+        vnfr_id = vnfr["_id"]
+        vnflcmop = self.vnflcmop_topic.show(session, id, filter_q)
+        _id = vnflcmop["vnfInstanceId"]
+        self.assertEqual(_id, vnfr_id, "Mismatch between vnflcmop's vnfInstanceId and database vnfr's id")
diff --git a/osm_nbi/vnf_instance_topics.py b/osm_nbi/vnf_instance_topics.py
new file mode 100644 (file)
index 0000000..ac5fe41
--- /dev/null
@@ -0,0 +1,116 @@
+# Copyright 2021 K Sai Kiran (Tata Elxsi)
+#
+# 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.
+
+__author__ = "K Sai Kiran <saikiran.k@tataelxsi.co.in>, Selvi Jayaraman <selvi.j@tataelxsi.co.in>"
+__date__ = "$12-June-2021 8:30:59$"
+
+from osm_nbi.base_topic import BaseTopic
+from .osm_vnfm.vnf_instances import VnfInstances2NsInstances
+from .osm_vnfm.vnf_instance_actions import VnfLcmOp2NsLcmOp
+
+
+class VnfInstances(BaseTopic):
+
+    def __init__(self, db, fs, msg, auth):
+        """
+        Constructor call for vnf instance topic
+        """
+        BaseTopic.__init__(self, db, fs, msg, auth)
+        self.vnfinstances2nsinstances = VnfInstances2NsInstances(db, fs, msg, auth)
+
+    def new(self, rollback, session, indata=None, kwargs=None, headers=None):
+        """
+        Creates new vnf instance
+        :param rollback: list to append the created items at database in case a rollback must be done
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param indata: params to be used for the vnf instance
+        :param kwargs: used to override the indata descriptor
+        :param headers: http request headers
+        :return: the _id of vnf instance created at database. Or an exception.
+        """
+        return self.vnfinstances2nsinstances.new(rollback, session, indata, kwargs, headers)
+
+    def list(self, session, filter_q=None, api_req=False):
+        """
+        Get a list of the vnf instances that match a filter
+        :param session: contains the used login username and working project
+        :param filter_q: filter of data to be applied
+        :param api_req: True if this call is serving an external API request. False if serving internal request.
+        :return: The list, it can be empty if no one match the filter.
+        """
+        return self.vnfinstances2nsinstances.list(session, filter_q, api_req)
+
+    def show(self, session, _id, filter_q=None, api_req=False):
+        """
+        Get complete information on an vnf instance
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param _id: server internal id
+        :param api_req: True if this call is serving an external API request. False if serving internal request.
+        :return: dictionary, raise exception if not found.
+        """
+        return self.vnfinstances2nsinstances.show(session, _id, api_req)
+
+    def delete(self, session, _id, dry_run=False, not_send_msg=None):
+        """
+        Delete vnf instance by its internal _id
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param _id: server internal id
+        :param dry_run: make checking but do not delete
+        :param not_send_msg: To not send message (False) or store content (list) instead
+        :return: operation id (None if there is not operation), raise exception if error or not found, conflict, ...
+        """
+        return self.vnfinstances2nsinstances.delete(session, _id, dry_run, not_send_msg)
+
+
+class VnfLcmOpTopic(BaseTopic):
+
+    def __init__(self, db, fs, msg, auth):
+        """
+        Constructor call for vnf lcm op topic
+        """
+        BaseTopic.__init__(self, db, fs, msg, auth)
+        self.vnflcmop2nslcmop = VnfLcmOp2NsLcmOp(db, fs, msg, auth)
+
+    def new(self, rollback, session, indata=None, kwargs=None, headers=None):
+        """
+        Creates new vnf lcm op
+        :param rollback: list to append the created items at database in case a rollback must be done
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param indata: params to be used for the vnf instance
+        :param kwargs: used to override the indata descriptor
+        :param headers: http request headers
+        :return: the _id of vnf lcm op created at database. Or an exception.
+        """
+        return self.vnflcmop2nslcmop.new(rollback, session, indata, kwargs, headers)
+
+    def list(self, session, filter_q=None, api_req=False):
+        """
+        Get a list of the vnf lcm op that match a filter
+        :param session: contains the used login username and working project
+        :param filter_q: filter of data to be applied
+        :param api_req: True if this call is serving an external API request. False if serving internal request.
+        :return: The list, it can be empty if no one match the filter.
+        """
+        return self.vnflcmop2nslcmop.list(session, filter_q, api_req)
+
+    def show(self, session, _id, filter_q=None, api_req=False):
+        """
+        Get complete information on an vnf lcm op
+        :param session: contains "username", "admin", "force", "public", "project_id", "set_project"
+        :param _id: server internal id
+        :param api_req: True if this call is serving an external API request. False if serving internal request.
+        :return: dictionary, raise exception if not found.
+        """
+        return self.vnflcmop2nslcmop.show(session, _id, api_req)