Feature 10476: New client commands and library to manage subscriptions
[osm/osmclient.git] / osmclient / sol005 / ns.py
index c8e2a77..a5a5c79 100644 (file)
@@ -22,65 +22,67 @@ from osmclient.common import utils
 from osmclient.common import wait as WaitForStatus
 from osmclient.common.exceptions import ClientException
 from osmclient.common.exceptions import NotFound
 from osmclient.common import wait as WaitForStatus
 from osmclient.common.exceptions import ClientException
 from osmclient.common.exceptions import NotFound
-from osmclient.common.exceptions import OsmHttpException
 import yaml
 import json
 import logging
 
 
 class Ns(object):
 import yaml
 import json
 import logging
 
 
 class Ns(object):
-
     def __init__(self, http=None, client=None):
         self._http = http
         self._client = client
     def __init__(self, http=None, client=None):
         self._http = http
         self._client = client
-        self._logger = logging.getLogger('osmclient')
-        self._apiName = '/nslcm'
-        self._apiVersion = '/v1'
-        self._apiResource = '/ns_instances_content'
-        self._apiBase = '{}{}{}'.format(self._apiName,
-                                        self._apiVersion, self._apiResource)
+        self._logger = logging.getLogger("osmclient")
+        self._apiName = "/nslcm"
+        self._apiVersion = "/v1"
+        self._apiResource = "/ns_instances_content"
+        self._apiBase = "{}{}{}".format(
+            self._apiName, self._apiVersion, self._apiResource
+        )
 
     # NS '--wait' option
 
     # NS '--wait' option
-    def _wait(self, id, deleteFlag=False):
+    def _wait(self, id, wait_time, deleteFlag=False):
         self._logger.debug("")
         # Endpoint to get operation status
         self._logger.debug("")
         # Endpoint to get operation status
-        apiUrlStatus = '{}{}{}'.format(self._apiName, self._apiVersion, '/ns_lcm_op_occs')
+        apiUrlStatus = "{}{}{}".format(
+            self._apiName, self._apiVersion, "/ns_lcm_op_occs"
+        )
         # Wait for status for NS instance creation/update/deletion
         # Wait for status for NS instance creation/update/deletion
+        if isinstance(wait_time, bool):
+            wait_time = WaitForStatus.TIMEOUT_NS_OPERATION
         WaitForStatus.wait_for_status(
         WaitForStatus.wait_for_status(
-            'NS',
+            "NS",
             str(id),
             str(id),
-            WaitForStatus.TIMEOUT_NS_OPERATION,
+            wait_time,
             apiUrlStatus,
             self._http.get2_cmd,
             apiUrlStatus,
             self._http.get2_cmd,
-            deleteFlag=deleteFlag)
+            deleteFlag=deleteFlag,
+        )
 
     def list(self, filter=None):
 
     def list(self, filter=None):
-        """Returns a list of NS
-        """
+        """Returns a list of NS"""
         self._logger.debug("")
         self._client.get_token()
         self._logger.debug("")
         self._client.get_token()
-        filter_string = ''
+        filter_string = ""
         if filter:
         if filter:
-            filter_string = '?{}'.format(filter)
-        _, resp = self._http.get2_cmd('{}{}'.format(self._apiBase,filter_string))
+            filter_string = "?{}".format(filter)
+        _, resp = self._http.get2_cmd("{}{}".format(self._apiBase, filter_string))
         if resp:
             return json.loads(resp)
         return list()
 
     def get(self, name):
         if resp:
             return json.loads(resp)
         return list()
 
     def get(self, name):
-        """Returns an NS based on name or id
-        """
+        """Returns an NS based on name or id"""
         self._logger.debug("")
         self._client.get_token()
         if utils.validate_uuid4(name):
             for ns in self.list():
         self._logger.debug("")
         self._client.get_token()
         if utils.validate_uuid4(name):
             for ns in self.list():
-                if name == ns['_id']:
+                if name == ns["_id"]:
                     return ns
         else:
             for ns in self.list():
                     return ns
         else:
             for ns in self.list():
-                if name == ns['name']:
+                if name == ns["name"]:
                     return ns
                     return ns
-        raise NotFound("ns {} not found".format(name))
+        raise NotFound("ns '{}' not found".format(name))
 
     def get_individual(self, name):
         self._logger.debug("")
 
     def get_individual(self, name):
         self._logger.debug("")
@@ -88,47 +90,80 @@ class Ns(object):
         ns_id = name
         if not utils.validate_uuid4(name):
             for ns in self.list():
         ns_id = name
         if not utils.validate_uuid4(name):
             for ns in self.list():
-                if name == ns['name']:
-                    ns_id = ns['_id']
+                if name == ns["name"]:
+                    ns_id = ns["_id"]
                     break
                     break
-        _, resp = self._http.get2_cmd('{}/{}'.format(self._apiBase, ns_id))
-        #resp = self._http.get_cmd('{}/{}/nsd_content'.format(self._apiBase, ns_id))
-        #print(yaml.safe_dump(resp))
-        if resp:
-            return json.loads(resp)
-        raise NotFound("ns {} not found".format(name))
+        try:
+            _, resp = self._http.get2_cmd("{}/{}".format(self._apiBase, ns_id))
+            # resp = self._http.get_cmd('{}/{}/nsd_content'.format(self._apiBase, ns_id))
+            # print(yaml.safe_dump(resp))
+            if resp:
+                return json.loads(resp)
+        except NotFound:
+            raise NotFound("ns '{}' not found".format(name))
+        raise NotFound("ns '{}' not found".format(name))
 
 
-    def delete(self, name, force=False, wait=False):
+    def delete(self, name, force=False, config=None, wait=False):
+        """
+        Deletes a Network Service (NS)
+        :param name: name of network service
+        :param force: set force. Direct deletion without cleaning at VIM
+        :param config: parameters of deletion, as:
+             autoremove: Bool (default True)
+             timeout_ns_terminate: int
+             skip_terminate_primitives: Bool (default False) to not exec the terminate primitives
+        :param wait: Make synchronous. Wait until deletion is completed:
+            False to not wait (by default), True to wait a standard time, or int (time to wait)
+        :return: None. Exception if fail
+        """
         self._logger.debug("")
         ns = self.get(name)
         self._logger.debug("")
         ns = self.get(name)
-        querystring = ''
+        querystring_list = []
+        querystring = ""
+        if config:
+            ns_config = yaml.safe_load(config)
+            querystring_list += ["{}={}".format(k, v) for k, v in ns_config.items()]
         if force:
         if force:
-            querystring = '?FORCE=True'
-        http_code, resp = self._http.delete_cmd('{}/{}{}'.format(self._apiBase,
-                                                 ns['_id'], querystring))
+            querystring_list.append("FORCE=True")
+        if querystring_list:
+            querystring = "?" + "&".join(querystring_list)
+        http_code, resp = self._http.delete_cmd(
+            "{}/{}{}".format(self._apiBase, ns["_id"], querystring)
+        )
+        # TODO change to use a POST self._http.post_cmd('{}/{}/terminate{}'.format(_apiBase, ns['_id'], querystring),
+        #                                               postfields_dict=ns_config)
+        # seting autoremove as True by default
         # print('HTTP CODE: {}'.format(http_code))
         # print('RESP: {}'.format(resp))
         if http_code == 202:
             if wait and resp:
                 resp = json.loads(resp)
                 # For the 'delete' operation, '_id' is used
         # print('HTTP CODE: {}'.format(http_code))
         # print('RESP: {}'.format(resp))
         if http_code == 202:
             if wait and resp:
                 resp = json.loads(resp)
                 # For the 'delete' operation, '_id' is used
-                self._wait(resp.get('_id'), deleteFlag=True)
+                self._wait(resp.get("_id"), wait, deleteFlag=True)
             else:
             else:
-                print('Deletion in progress')
+                print("Deletion in progress")
         elif http_code == 204:
         elif http_code == 204:
-            print('Deleted')
+            print("Deleted")
         else:
         else:
-            msg = ""
-            if resp:
-                try:
-                    msg = json.loads(resp)
-                except ValueError:
-                    msg = resp
-            raise OsmHttpException("failed to delete ns {} - {}".format(name, msg))
+            msg = resp or ""
+            if resp:
+                try:
+                    msg = json.loads(resp)
+                except ValueError:
+                    msg = resp
+            raise ClientException("failed to delete ns {} - {}".format(name, msg))
 
 
-    def create(self, nsd_name, nsr_name, account, config=None,
-               ssh_keys=None, description='default description',
-               admin_status='ENABLED', wait=False):
+    def create(
+        self,
+        nsd_name,
+        nsr_name,
+        account,
+        config=None,
+        ssh_keys=None,
+        description="default description",
+        admin_status="ENABLED",
+        wait=False,
+    ):
         self._logger.debug("")
         self._client.get_token()
         nsd = self._client.nsd.get(nsd_name)
         self._logger.debug("")
         self._client.get_token()
         nsd = self._client.nsd.get(nsd_name)
@@ -140,107 +175,135 @@ class Ns(object):
             self._logger.debug("")
             if vim_account_id.get(vim_account):
                 return vim_account_id[vim_account]
             self._logger.debug("")
             if vim_account_id.get(vim_account):
                 return vim_account_id[vim_account]
-
             vim = self._client.vim.get(vim_account)
             if vim is None:
                 raise NotFound("cannot find vim account '{}'".format(vim_account))
             vim = self._client.vim.get(vim_account)
             if vim is None:
                 raise NotFound("cannot find vim account '{}'".format(vim_account))
-            vim_account_id[vim_account] = vim['_id']
-            return vim['_id']
+            vim_account_id[vim_account] = vim["_id"]
+            return vim["_id"]
+
+        def get_vca_id(vim_id):
+            vim = self._client.vim.get(vim_id)
+            return vim.get("vca")
 
         def get_wim_account_id(wim_account):
             self._logger.debug("")
 
         def get_wim_account_id(wim_account):
             self._logger.debug("")
+            # wim_account can be False (boolean) to indicate not use wim account
             if not isinstance(wim_account, str):
                 return wim_account
             if wim_account_id.get(wim_account):
                 return wim_account_id[wim_account]
             if not isinstance(wim_account, str):
                 return wim_account
             if wim_account_id.get(wim_account):
                 return wim_account_id[wim_account]
-
             wim = self._client.wim.get(wim_account)
             if wim is None:
                 raise NotFound("cannot find wim account '{}'".format(wim_account))
             wim = self._client.wim.get(wim_account)
             if wim is None:
                 raise NotFound("cannot find wim account '{}'".format(wim_account))
-            wim_account_id[wim_account] = wim['_id']
-            return wim['_id']
+            wim_account_id[wim_account] = wim["_id"]
+            return wim["_id"]
 
 
+        vim_id = get_vim_account_id(account)
+        vca_id = get_vca_id(vim_id)
         ns = {}
         ns = {}
-        ns['nsdId'] = nsd['_id']
-        ns['nsName'] = nsr_name
-        ns['nsDescription'] = description
-        ns['vimAccountId'] = get_vim_account_id(account)
-        #ns['userdata'] = {}
-        #ns['userdata']['key1']='value1'
-        #ns['userdata']['key2']='value2'
+        ns["nsdId"] = nsd["_id"]
+        ns["nsName"] = nsr_name
+        ns["nsDescription"] = description
+        ns["vimAccountId"] = vim_id
+        if vca_id:
+            ns["vcaId"] = vca_id
+        # ns['userdata'] = {}
+        # ns['userdata']['key1']='value1'
+        # ns['userdata']['key2']='value2'
 
         if ssh_keys is not None:
 
         if ssh_keys is not None:
-            ns['ssh_keys'] = []
-            for pubkeyfile in ssh_keys.split(','):
-                with open(pubkeyfile, 'r') as f:
-                    ns['ssh_keys'].append(f.read())
+            ns["ssh_keys"] = []
+            for pubkeyfile in ssh_keys.split(","):
+                with open(pubkeyfile, "r") as f:
+                    ns["ssh_keys"].append(f.read())
         if config:
             ns_config = yaml.safe_load(config)
             if "vim-network-name" in ns_config:
                 ns_config["vld"] = ns_config.pop("vim-network-name")
             if "vld" in ns_config:
         if config:
             ns_config = yaml.safe_load(config)
             if "vim-network-name" in ns_config:
                 ns_config["vld"] = ns_config.pop("vim-network-name")
             if "vld" in ns_config:
+                if not isinstance(ns_config["vld"], list):
+                    raise ClientException(
+                        "Error at --config 'vld' must be a list of dictionaries"
+                    )
                 for vld in ns_config["vld"]:
                 for vld in ns_config["vld"]:
+                    if not isinstance(vld, dict):
+                        raise ClientException(
+                            "Error at --config 'vld' must be a list of dictionaries"
+                        )
                     if vld.get("vim-network-name"):
                         if isinstance(vld["vim-network-name"], dict):
                             vim_network_name_dict = {}
                     if vld.get("vim-network-name"):
                         if isinstance(vld["vim-network-name"], dict):
                             vim_network_name_dict = {}
-                            for vim_account, vim_net in list(vld["vim-network-name"].items()):
-                                vim_network_name_dict[get_vim_account_id(vim_account)] = vim_net
+                            for vim_account, vim_net in vld["vim-network-name"].items():
+                                vim_network_name_dict[
+                                    get_vim_account_id(vim_account)
+                                ] = vim_net
                             vld["vim-network-name"] = vim_network_name_dict
                     if "wim_account" in vld and vld["wim_account"] is not None:
                         vld["wimAccountId"] = get_wim_account_id(vld.pop("wim_account"))
                             vld["vim-network-name"] = vim_network_name_dict
                     if "wim_account" in vld and vld["wim_account"] is not None:
                         vld["wimAccountId"] = get_wim_account_id(vld.pop("wim_account"))
-                ns["vld"] = ns_config["vld"]
             if "vnf" in ns_config:
                 for vnf in ns_config["vnf"]:
                     if vnf.get("vim_account"):
                         vnf["vimAccountId"] = get_vim_account_id(vnf.pop("vim_account"))
             if "vnf" in ns_config:
                 for vnf in ns_config["vnf"]:
                     if vnf.get("vim_account"):
                         vnf["vimAccountId"] = get_vim_account_id(vnf.pop("vim_account"))
-                ns["vnf"] = ns_config["vnf"]
 
             if "additionalParamsForNs" in ns_config:
 
             if "additionalParamsForNs" in ns_config:
-                ns["additionalParamsForNs"] = ns_config.pop("additionalParamsForNs")
-                if not isinstance(ns["additionalParamsForNs"], dict):
-                    raise ValueError("Error at --config 'additionalParamsForNs' must be a dictionary")
+                if not isinstance(ns_config["additionalParamsForNs"], dict):
+                    raise ClientException(
+                        "Error at --config 'additionalParamsForNs' must be a dictionary"
+                    )
             if "additionalParamsForVnf" in ns_config:
             if "additionalParamsForVnf" in ns_config:
-                ns["additionalParamsForVnf"] = ns_config.pop("additionalParamsForVnf")
-                if not isinstance(ns["additionalParamsForVnf"], list):
-                    raise ValueError("Error at --config 'additionalParamsForVnf' must be a list")
-                for additional_param_vnf in ns["additionalParamsForVnf"]:
+                if not isinstance(ns_config["additionalParamsForVnf"], list):
+                    raise ClientException(
+                        "Error at --config 'additionalParamsForVnf' must be a list"
+                    )
+                for additional_param_vnf in ns_config["additionalParamsForVnf"]:
                     if not isinstance(additional_param_vnf, dict):
                     if not isinstance(additional_param_vnf, dict):
-                        raise ValueError("Error at --config 'additionalParamsForVnf' items must be dictionaries")
+                        raise ClientException(
+                            "Error at --config 'additionalParamsForVnf' items must be dictionaries"
+                        )
                     if not additional_param_vnf.get("member-vnf-index"):
                     if not additional_param_vnf.get("member-vnf-index"):
-                        raise ValueError("Error at --config 'additionalParamsForVnf' items must contain "
-                                         "'member-vnf-index'")
+                        raise ClientException(
+                            "Error at --config 'additionalParamsForVnf' items must contain "
+                            "'member-vnf-index'"
+                        )
             if "wim_account" in ns_config:
                 wim_account = ns_config.pop("wim_account")
                 if wim_account is not None:
             if "wim_account" in ns_config:
                 wim_account = ns_config.pop("wim_account")
                 if wim_account is not None:
-                    ns['wimAccountId'] = get_wim_account_id(wim_account)
-            if "timeout_ns_deploy" in ns_config:
-                ns["timeout_ns_deploy"] = ns_config.pop("timeout_ns_deploy")
+                    ns["wimAccountId"] = get_wim_account_id(wim_account)
+            # rest of parameters without any transformation or checking
+            # "timeout_ns_deploy"
+            # "placement-engine"
+            ns.update(ns_config)
 
         # print(yaml.safe_dump(ns))
         try:
 
         # print(yaml.safe_dump(ns))
         try:
-            self._apiResource = '/ns_instances_content'
-            self._apiBase = '{}{}{}'.format(self._apiName,
-                                            self._apiVersion, self._apiResource)
+            self._apiResource = "/ns_instances_content"
+            self._apiBase = "{}{}{}".format(
+                self._apiName, self._apiVersion, self._apiResource
+            )
             headers = self._client._headers
             headers = self._client._headers
-            headers['Content-Type'] = 'application/yaml'
-            http_header = ['{}: {}'.format(key,val)
-                          for (key,val) in list(headers.items())]
+            headers["Content-Type"] = "application/yaml"
+            http_header = [
+                "{}: {}".format(key, val) for (key, val) in list(headers.items())
+            ]
             self._http.set_http_header(http_header)
             self._http.set_http_header(http_header)
-            http_code, resp = self._http.post_cmd(endpoint=self._apiBase,
-                                   postfields_dict=ns)
+            http_code, resp = self._http.post_cmd(
+                endpoint=self._apiBase, postfields_dict=ns
+            )
             # print('HTTP CODE: {}'.format(http_code))
             # print('RESP: {}'.format(resp))
             # print('HTTP CODE: {}'.format(http_code))
             # print('RESP: {}'.format(resp))
-            #if http_code in (200, 201, 202, 204):
+            # if http_code in (200, 201, 202, 204):
             if resp:
                 resp = json.loads(resp)
             if resp:
                 resp = json.loads(resp)
-            if not resp or 'id' not in resp:
-                raise ClientException('unexpected response from server - {} '.format(
-                                      resp))
+            if not resp or "id" not in resp:
+                raise ClientException(
+                    "unexpected response from server - {} ".format(resp)
+                )
             if wait:
                 # Wait for status for NS instance creation
             if wait:
                 # Wait for status for NS instance creation
-                self._wait(resp.get('nslcmop_id'))
-            return resp['id']
-            #else:
+                self._wait(resp.get("nslcmop_id"), wait)
+            print(resp["id"])
+            return resp["id"]
+            # else:
             #    msg = ""
             #    if resp:
             #        try:
             #    msg = ""
             #    if resp:
             #        try:
@@ -249,111 +312,121 @@ class Ns(object):
             #            msg = resp
             #    raise ClientException(msg)
         except ClientException as exc:
             #            msg = resp
             #    raise ClientException(msg)
         except ClientException as exc:
-            message="failed to create ns: {} nsd: {}\nerror:\n{}".format(
-                    nsr_name,
-                    nsd_name,
-                    str(exc))
-            raise OsmHttpException(message)
+            message = "failed to create ns: {} nsd: {}\nerror:\n{}".format(
+                nsr_name, nsd_name, str(exc)
+            )
+            raise ClientException(message)
 
     def list_op(self, name, filter=None):
 
     def list_op(self, name, filter=None):
-        """Returns the list of operations of a NS
-        """
+        """Returns the list of operations of a NS"""
         self._logger.debug("")
         ns = self.get(name)
         try:
         self._logger.debug("")
         ns = self.get(name)
         try:
-            self._apiResource = '/ns_lcm_op_occs'
-            self._apiBase = '{}{}{}'.format(self._apiName,
-                                      self._apiVersion, self._apiResource)
-            filter_string = ''
+            self._apiResource = "/ns_lcm_op_occs"
+            self._apiBase = "{}{}{}".format(
+                self._apiName, self._apiVersion, self._apiResource
+            )
+            filter_string = ""
             if filter:
             if filter:
-                 filter_string = '&{}'.format(filter)
-            http_code, resp = self._http.get2_cmd('{}?nsInstanceId={}'.format(
-                                                       self._apiBase, ns['_id'],
-                                                       filter_string) )
-            #print('HTTP CODE: {}'.format(http_code))
-            #print('RESP: {}'.format(resp))
-            #if http_code == 200:
-            if resp:
-                resp = json.loads(resp)
-                return resp
+                filter_string = "&{}".format(filter)
+            http_code, resp = self._http.get2_cmd(
+                "{}?nsInstanceId={}{}".format(self._apiBase, ns["_id"], filter_string)
+            )
+            # print('HTTP CODE: {}'.format(http_code))
+            # print('RESP: {}'.format(resp))
+            if http_code == 200:
+                if resp:
+                    resp = json.loads(resp)
+                    return resp
+                else:
+                    raise ClientException("unexpected response from server")
             else:
             else:
-                raise ClientException('unexpected response from server')
-            #else:
-            #    msg = ""
-            #    if resp:
-            #        try:
-            #            resp = json.loads(resp)
-            #            msg = resp['detail']
-            #        except ValueError:
-            #            msg = resp
-            #    raise ClientException(msg)
+                msg = resp or ""
+                #    if resp:
+                #        try:
+                #            resp = json.loads(resp)
+                #            msg = resp['detail']
+                #        except ValueError:
+                #            msg = resp
+                raise ClientException(msg)
         except ClientException as exc:
         except ClientException as exc:
-            message="failed to get operation list of NS {}:\nerror:\n{}".format(
-                    name,
-                    str(exc))
-            raise OsmHttpException(message)
+            message = "failed to get operation list of NS {}:\nerror:\n{}".format(
+                name, str(exc)
+            )
+            raise ClientException(message)
 
     def get_op(self, operationId):
 
     def get_op(self, operationId):
-        """Returns the status of an operation
-        """
+        """Returns the status of an operation"""
         self._logger.debug("")
         self._client.get_token()
         try:
         self._logger.debug("")
         self._client.get_token()
         try:
-            self._apiResource = '/ns_lcm_op_occs'
-            self._apiBase = '{}{}{}'.format(self._apiName,
-                                      self._apiVersion, self._apiResource)
-            http_code, resp = self._http.get2_cmd('{}/{}'.format(self._apiBase, operationId))
-            #print('HTTP CODE: {}'.format(http_code))
-            #print('RESP: {}'.format(resp))
-            #if http_code == 200:
-            if resp:
-                resp = json.loads(resp)
-                return resp
+            self._apiResource = "/ns_lcm_op_occs"
+            self._apiBase = "{}{}{}".format(
+                self._apiName, self._apiVersion, self._apiResource
+            )
+            http_code, resp = self._http.get2_cmd(
+                "{}/{}".format(self._apiBase, operationId)
+            )
+            # print('HTTP CODE: {}'.format(http_code))
+            # print('RESP: {}'.format(resp))
+            if http_code == 200:
+                if resp:
+                    resp = json.loads(resp)
+                    return resp
+                else:
+                    raise ClientException("unexpected response from server")
             else:
             else:
-                raise ClientException('unexpected response from server')
-            #else:
-            #    msg = ""
-            #    if resp:
-            #        try:
-            #            resp = json.loads(resp)
-            #            msg = resp['detail']
-            #        except ValueError:
-            #            msg = resp
-            #    raise ClientException(msg)
+                msg = resp or ""
+                #    if resp:
+                #        try:
+                #            resp = json.loads(resp)
+                #            msg = resp['detail']
+                #        except ValueError:
+                #            msg = resp
+                raise ClientException(msg)
         except ClientException as exc:
         except ClientException as exc:
-            message="failed to get status of operation {}:\nerror:\n{}".format(
-                    operationId,
-                    str(exc))
-            raise OsmHttpException(message)
+            message = "failed to get status of operation {}:\nerror:\n{}".format(
+                operationId, str(exc)
+            )
+            raise ClientException(message)
 
 
-    def exec_op(self, name, op_name, op_data=None, wait=False, ):
-        """Executes an operation on a NS
-        """
+    def exec_op(
+        self,
+        name,
+        op_name,
+        op_data=None,
+        wait=False,
+    ):
+        """Executes an operation on a NS"""
         self._logger.debug("")
         ns = self.get(name)
         try:
             ns = self.get(name)
         self._logger.debug("")
         ns = self.get(name)
         try:
             ns = self.get(name)
-            self._apiResource = '/ns_instances'
-            self._apiBase = '{}{}{}'.format(self._apiName,
-                                            self._apiVersion, self._apiResource)
-            endpoint = '{}/{}/{}'.format(self._apiBase, ns['_id'], op_name)
-            #print('OP_NAME: {}'.format(op_name))
-            #print('OP_DATA: {}'.format(json.dumps(op_data)))
-            http_code, resp = self._http.post_cmd(endpoint=endpoint, postfields_dict=op_data)
-            #print('HTTP CODE: {}'.format(http_code))
-            #print('RESP: {}'.format(resp))
-            #if http_code in (200, 201, 202, 204):
+            self._apiResource = "/ns_instances"
+            self._apiBase = "{}{}{}".format(
+                self._apiName, self._apiVersion, self._apiResource
+            )
+            endpoint = "{}/{}/{}".format(self._apiBase, ns["_id"], op_name)
+            # print('OP_NAME: {}'.format(op_name))
+            # print('OP_DATA: {}'.format(json.dumps(op_data)))
+            http_code, resp = self._http.post_cmd(
+                endpoint=endpoint, postfields_dict=op_data
+            )
+            # print('HTTP CODE: {}'.format(http_code))
+            # print('RESP: {}'.format(resp))
+            # if http_code in (200, 201, 202, 204):
             if resp:
                 resp = json.loads(resp)
             if resp:
                 resp = json.loads(resp)
-            if not resp or 'id' not in resp:
-                raise ClientException('unexpected response from server - {}'.format(
-                                  resp))
+            if not resp or "id" not in resp:
+                raise ClientException(
+                    "unexpected response from server - {}".format(resp)
+                )
             if wait:
                 # Wait for status for NS instance action
                 # For the 'action' operation, 'id' is used
             if wait:
                 # Wait for status for NS instance action
                 # For the 'action' operation, 'id' is used
-                self._wait(resp.get('id'))
-            return resp['id']
-            #else:
+                self._wait(resp.get("id"), wait)
+            return resp["id"]
+            # else:
             #    msg = ""
             #    if resp:
             #        try:
             #    msg = ""
             #    if resp:
             #        try:
@@ -362,33 +435,44 @@ class Ns(object):
             #            msg = resp
             #    raise ClientException(msg)
         except ClientException as exc:
             #            msg = resp
             #    raise ClientException(msg)
         except ClientException as exc:
-            message="failed to exec operation {}:\nerror:\n{}".format(
-                    name,
-                    str(exc))
-            raise OsmHttpException(message)
+            message = "failed to exec operation {}:\nerror:\n{}".format(name, str(exc))
+            raise ClientException(message)
 
 
-    def scale_vnf(self, ns_name, vnf_name, scaling_group, scale_in, scale_out, wait=False):
-        """Scales a VNF by adding/removing VDUs
-        """
+    def scale_vnf(
+        self,
+        ns_name,
+        vnf_name,
+        scaling_group,
+        scale_in,
+        scale_out,
+        wait=False,
+        timeout=None,
+    ):
+        """Scales a VNF by adding/removing VDUs"""
         self._logger.debug("")
         self._client.get_token()
         try:
         self._logger.debug("")
         self._client.get_token()
         try:
-            op_data={}
+            op_data = {}
             op_data["scaleType"] = "SCALE_VNF"
             op_data["scaleVnfData"] = {}
             op_data["scaleType"] = "SCALE_VNF"
             op_data["scaleVnfData"] = {}
-            if scale_in:
+            if scale_in and not scale_out:
                 op_data["scaleVnfData"]["scaleVnfType"] = "SCALE_IN"
                 op_data["scaleVnfData"]["scaleVnfType"] = "SCALE_IN"
-            else:
+            elif not scale_in and scale_out:
                 op_data["scaleVnfData"]["scaleVnfType"] = "SCALE_OUT"
                 op_data["scaleVnfData"]["scaleVnfType"] = "SCALE_OUT"
+            else:
+                raise ClientException("you must set either 'scale_in' or 'scale_out'")
             op_data["scaleVnfData"]["scaleByStepData"] = {
                 "member-vnf-index": vnf_name,
                 "scaling-group-descriptor": scaling_group,
             }
             op_data["scaleVnfData"]["scaleByStepData"] = {
                 "member-vnf-index": vnf_name,
                 "scaling-group-descriptor": scaling_group,
             }
-            op_id = self.exec_op(ns_name, op_name='scale', op_data=op_data, wait=wait)
+            if timeout:
+                op_data["timeout_ns_scale"] = timeout
+            op_id = self.exec_op(ns_name, op_name="scale", op_data=op_data, wait=wait)
             print(str(op_id))
         except ClientException as exc:
             print(str(op_id))
         except ClientException as exc:
-            message="failed to scale vnf {} of ns {}:\nerror:\n{}".format(
-                    vnf_name, ns_name, str(exc))
+            message = "failed to scale vnf {} of ns {}:\nerror:\n{}".format(
+                vnf_name, ns_name, str(exc)
+            )
             raise ClientException(message)
 
     def create_alarm(self, alarm):
             raise ClientException(message)
 
     def create_alarm(self, alarm):
@@ -397,15 +481,16 @@ class Ns(object):
         data = {}
         data["create_alarm_request"] = {}
         data["create_alarm_request"]["alarm_create_request"] = alarm
         data = {}
         data["create_alarm_request"] = {}
         data["create_alarm_request"]["alarm_create_request"] = alarm
-        #try:
-        http_code, resp = self._http.post_cmd(endpoint='/test/message/alarm_request',
-                                   postfields_dict=data)
-            #print('HTTP CODE: {}'.format(http_code))
-            #print('RESP: {}'.format(resp))
-            #if http_code in (200, 201, 202, 204):
-            #resp = json.loads(resp)
-        print('Alarm created')
-            #else:
+        try:
+            http_code, resp = self._http.post_cmd(
+                endpoint="/test/message/alarm_request", postfields_dict=data
+            )
+            # print('HTTP CODE: {}'.format(http_code))
+            # print('RESP: {}'.format(resp))
+            # if http_code in (200, 201, 202, 204):
+            #     resp = json.loads(resp)
+            print("Alarm created")
+            # else:
             #    msg = ""
             #    if resp:
             #        try:
             #    msg = ""
             #    if resp:
             #        try:
@@ -414,11 +499,9 @@ class Ns(object):
             #            msg = resp
             #    raise ClientException('error: code: {}, resp: {}'.format(
             #                          http_code, msg))
             #            msg = resp
             #    raise ClientException('error: code: {}, resp: {}'.format(
             #                          http_code, msg))
-        #except ClientException as exc:
-        #    message="failed to create alarm: alarm {}\n{}".format(
-        #            alarm,
-        #            str(exc))
-        #    raise ClientException(message)
+        except ClientException as exc:
+            message = "failed to create alarm: alarm {}\n{}".format(alarm, str(exc))
+            raise ClientException(message)
 
     def delete_alarm(self, name):
         self._logger.debug("")
 
     def delete_alarm(self, name):
         self._logger.debug("")
@@ -427,15 +510,16 @@ class Ns(object):
         data["delete_alarm_request"] = {}
         data["delete_alarm_request"]["alarm_delete_request"] = {}
         data["delete_alarm_request"]["alarm_delete_request"]["alarm_uuid"] = name
         data["delete_alarm_request"] = {}
         data["delete_alarm_request"]["alarm_delete_request"] = {}
         data["delete_alarm_request"]["alarm_delete_request"]["alarm_uuid"] = name
-        #try:
-        http_code, resp = self._http.post_cmd(endpoint='/test/message/alarm_request',
-                                   postfields_dict=data)
-            #print('HTTP CODE: {}'.format(http_code))
-            #print('RESP: {}'.format(resp))
-            #if http_code in (200, 201, 202, 204):
-            #resp = json.loads(resp)
-        print('Alarm deleted')
-            #else:
+        try:
+            http_code, resp = self._http.post_cmd(
+                endpoint="/test/message/alarm_request", postfields_dict=data
+            )
+            # print('HTTP CODE: {}'.format(http_code))
+            # print('RESP: {}'.format(resp))
+            # if http_code in (200, 201, 202, 204):
+            #    resp = json.loads(resp)
+            print("Alarm deleted")
+            # else:
             #    msg = ""
             #    if resp:
             #        try:
             #    msg = ""
             #    if resp:
             #        try:
@@ -444,26 +528,78 @@ class Ns(object):
             #            msg = resp
             #    raise ClientException('error: code: {}, resp: {}'.format(
             #                          http_code, msg))
             #            msg = resp
             #    raise ClientException('error: code: {}, resp: {}'.format(
             #                          http_code, msg))
-        #except ClientException as exc:
-        #    message="failed to delete alarm: alarm {}\n{}".format(
-        #            name,
-        #            str(exc))
-        #    raise ClientException(message)
+        except ClientException as exc:
+            message = "failed to delete alarm: alarm {}\n{}".format(name, str(exc))
+            raise ClientException(message)
+
+    def get_alarm(self, project_name=None, ns_id=None, uuid=None):
+        self._client.get_token()
+        try:
+            self._apiName = "/nsfm"
+            self._apiResource = "/alarms"
+            self._apiBase = "{}{}{}".format(
+                self._apiName, self._apiVersion, self._apiResource
+            )
+            if uuid:
+                # if request is for any uuid
+                http_code, resp = self._http.get2_cmd(
+                    "{}/{}".format(self._apiBase, uuid)
+                )
+            if not uuid:
+                http_code, resp = self._http.get2_cmd(
+                    "{}/{}/{}/{}".format(self._apiBase, uuid, project_name, ns_id)
+                )
+            if http_code == 200:
+                if resp:
+                    resp = json.loads(resp)
+                    return resp
+                else:
+                    raise ClientException("unexpected response from server")
+            else:
+                msg = resp or ""
+                raise ClientException(msg)
+        except ClientException as exc:
+            message = "failed to get alarm :\nerror:\n{}".format(str(exc))
+            raise ClientException(message)
+
+    def update_alarm(self, uuid, threshold=None, is_enable=None, wait=None):
+        self._client.get_token()
+        try:
+            op_data = {}
+            op_data["uuid"] = uuid
+            op_data["threshold"] = threshold
+            op_data["is_enable"] = is_enable
+            self._apiName = "/nsfm"
+            self._apiResource = "/alarms"
+            self._apiBase = "{}{}{}".format(
+                self._apiName, self._apiVersion, self._apiResource
+            )
+            http_code, resp = self._http.patch_cmd(
+                endpoint="{}".format(self._apiBase), postfields_dict=op_data
+            )
+            if resp:
+                resp = json.loads(resp)
+            print(resp)
+            return resp
+        except ClientException as exc:
+            message = "failed to update alarm :\nerror:\n{}".format(str(exc))
+            raise ClientException(message)
 
     def export_metric(self, metric):
         self._logger.debug("")
         self._client.get_token()
         data = {}
         data["read_metric_data_request"] = metric
 
     def export_metric(self, metric):
         self._logger.debug("")
         self._client.get_token()
         data = {}
         data["read_metric_data_request"] = metric
-        #try:
-        http_code, resp = self._http.post_cmd(endpoint='/test/message/metric_request',
-                                   postfields_dict=data)
-            #print('HTTP CODE: {}'.format(http_code))
-            #print('RESP: {}'.format(resp))
-            #if http_code in (200, 201, 202, 204):
-            #resp = json.loads(resp)
-        return 'Metric exported'
-            #else:
+        try:
+            http_code, resp = self._http.post_cmd(
+                endpoint="/test/message/metric_request", postfields_dict=data
+            )
+            # print('HTTP CODE: {}'.format(http_code))
+            # print('RESP: {}'.format(resp))
+            # if http_code in (200, 201, 202, 204):
+            #    resp = json.loads(resp)
+            return "Metric exported"
+            # else:
             #    msg = ""
             #    if resp:
             #        try:
             #    msg = ""
             #    if resp:
             #        try:
@@ -472,11 +608,9 @@ class Ns(object):
             #            msg = resp
             #    raise ClientException('error: code: {}, resp: {}'.format(
             #                          http_code, msg))
             #            msg = resp
             #    raise ClientException('error: code: {}, resp: {}'.format(
             #                          http_code, msg))
-        #except ClientException as exc:
-        #    message="failed to export metric: metric {}\n{}".format(
-        #            metric,
-        #            str(exc))
-        #    raise ClientException(message)
+        except ClientException as exc:
+            message = "failed to export metric: metric {}\n{}".format(metric, str(exc))
+            raise ClientException(message)
 
     def get_field(self, ns_name, field):
         self._logger.debug("")
 
     def get_field(self, ns_name, field):
         self._logger.debug("")
@@ -489,4 +623,3 @@ class Ns(object):
             return nsr[field]
 
         raise NotFound("failed to find {} in ns {}".format(field, ns_name))
             return nsr[field]
 
         raise NotFound("failed to find {} in ns {}".format(field, ns_name))
-