fix wait option when operation fails
[osm/osmclient.git] / osmclient / sol005 / ns.py
index c8e2a77..f4b5b89 100644 (file)
@@ -22,7 +22,6 @@ 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
 import yaml
 import json
 import logging
@@ -41,15 +40,17 @@ class Ns(object):
                                         self._apiVersion, self._apiResource)
 
     # NS '--wait' option
                                         self._apiVersion, self._apiResource)
 
     # 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
         apiUrlStatus = '{}{}{}'.format(self._apiName, self._apiVersion, '/ns_lcm_op_occs')
         # Wait for status for NS instance creation/update/deletion
         self._logger.debug("")
         # Endpoint to get operation status
         apiUrlStatus = '{}{}{}'.format(self._apiName, self._apiVersion, '/ns_lcm_op_occs')
         # Wait for status for NS instance creation/update/deletion
+        if isinstance(wait_time, bool):
+            wait_time = WaitForStatus.TIMEOUT_NS_OPERATION
         WaitForStatus.wait_for_status(
             'NS',
             str(id),
         WaitForStatus.wait_for_status(
             'NS',
             str(id),
-            WaitForStatus.TIMEOUT_NS_OPERATION,
+            wait_time,
             apiUrlStatus,
             self._http.get2_cmd,
             deleteFlag=deleteFlag)
             apiUrlStatus,
             self._http.get2_cmd,
             deleteFlag=deleteFlag)
@@ -80,7 +81,7 @@ class Ns(object):
             for ns in self.list():
                 if name == ns['name']:
                     return ns
             for ns in self.list():
                 if name == ns['name']:
                     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("")
@@ -91,40 +92,64 @@ class Ns(object):
                 if name == ns['name']:
                     ns_id = ns['_id']
                     break
                 if name == ns['name']:
                     ns_id = ns['_id']
                     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_list = []
         querystring = ''
         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'
+            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))
         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:
                 print('Deletion in progress')
         elif http_code == 204:
             print('Deleted')
         else:
             else:
                 print('Deletion in progress')
         elif http_code == 204:
             print('Deleted')
         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',
 
     def create(self, nsd_name, nsr_name, account, config=None,
                ssh_keys=None, description='default description',
@@ -140,7 +165,6 @@ 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))
@@ -149,11 +173,11 @@ class Ns(object):
 
         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))
@@ -179,31 +203,31 @@ class Ns(object):
             if "vim-network-name" in ns_config:
                 ns_config["vld"] = ns_config.pop("vim-network-name")
             if "vld" in ns_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 ValueError("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 ValueError("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()):
+                            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"))
                                 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"))
-                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):
+                if not isinstance(ns_config["additionalParamsForNs"], dict):
                     raise ValueError("Error at --config 'additionalParamsForNs' must be a dictionary")
             if "additionalParamsForVnf" in ns_config:
                     raise ValueError("Error at --config 'additionalParamsForNs' must be a dictionary")
             if "additionalParamsForVnf" in ns_config:
-                ns["additionalParamsForVnf"] = ns_config.pop("additionalParamsForVnf")
-                if not isinstance(ns["additionalParamsForVnf"], list):
+                if not isinstance(ns_config["additionalParamsForVnf"], list):
                     raise ValueError("Error at --config 'additionalParamsForVnf' must be a list")
                     raise ValueError("Error at --config 'additionalParamsForVnf' must be a list")
-                for additional_param_vnf in ns["additionalParamsForVnf"]:
+                for additional_param_vnf in ns_config["additionalParamsForVnf"]:
                     if not isinstance(additional_param_vnf, dict):
                         raise ValueError("Error at --config 'additionalParamsForVnf' items must be dictionaries")
                     if not additional_param_vnf.get("member-vnf-index"):
                     if not isinstance(additional_param_vnf, dict):
                         raise ValueError("Error at --config 'additionalParamsForVnf' items must be dictionaries")
                     if not additional_param_vnf.get("member-vnf-index"):
@@ -213,8 +237,10 @@ class Ns(object):
                 wim_account = ns_config.pop("wim_account")
                 if wim_account is not None:
                     ns['wimAccountId'] = get_wim_account_id(wim_account)
                 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")
+            # 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:
@@ -238,7 +264,8 @@ class Ns(object):
                                       resp))
             if wait:
                 # Wait for status for NS instance creation
                                       resp))
             if wait:
                 # Wait for status for NS instance creation
-                self._wait(resp.get('nslcmop_id'))
+                self._wait(resp.get('nslcmop_id'), wait)
+            print(resp['id'])
             return resp['id']
             #else:
             #    msg = ""
             return resp['id']
             #else:
             #    msg = ""
@@ -253,7 +280,7 @@ class Ns(object):
                     nsr_name,
                     nsd_name,
                     str(exc))
                     nsr_name,
                     nsd_name,
                     str(exc))
-            raise OsmHttpException(message)
+            raise ClientException(message)
 
     def list_op(self, name, filter=None):
         """Returns the list of operations of a NS
 
     def list_op(self, name, filter=None):
         """Returns the list of operations of a NS
@@ -272,26 +299,26 @@ class Ns(object):
                                                        filter_string) )
             #print('HTTP CODE: {}'.format(http_code))
             #print('RESP: {}'.format(resp))
                                                        filter_string) )
             #print('HTTP CODE: {}'.format(http_code))
             #print('RESP: {}'.format(resp))
-            #if http_code == 200:
-            if resp:
-                resp = json.loads(resp)
-                return 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 = ""
+                msg = resp or ""
             #    if resp:
             #        try:
             #            resp = json.loads(resp)
             #            msg = resp['detail']
             #        except ValueError:
             #            msg = resp
             #    if resp:
             #        try:
             #            resp = json.loads(resp)
             #            msg = resp['detail']
             #        except ValueError:
             #            msg = resp
-            #    raise ClientException(msg)
+                raise ClientException(msg)
         except ClientException as exc:
             message="failed to get operation list of NS {}:\nerror:\n{}".format(
                     name,
                     str(exc))
         except ClientException as exc:
             message="failed to get operation list of NS {}:\nerror:\n{}".format(
                     name,
                     str(exc))
-            raise OsmHttpException(message)
+            raise ClientException(message)
 
     def get_op(self, operationId):
         """Returns the status of an operation
 
     def get_op(self, operationId):
         """Returns the status of an operation
@@ -305,26 +332,26 @@ class Ns(object):
             http_code, resp = self._http.get2_cmd('{}/{}'.format(self._apiBase, operationId))
             #print('HTTP CODE: {}'.format(http_code))
             #print('RESP: {}'.format(resp))
             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
+            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 = ""
+                msg = resp or ""
             #    if resp:
             #        try:
             #            resp = json.loads(resp)
             #            msg = resp['detail']
             #        except ValueError:
             #            msg = resp
             #    if resp:
             #        try:
             #            resp = json.loads(resp)
             #            msg = resp['detail']
             #        except ValueError:
             #            msg = resp
-            #    raise ClientException(msg)
+                raise ClientException(msg)
         except ClientException as exc:
             message="failed to get status of operation {}:\nerror:\n{}".format(
                     operationId,
                     str(exc))
         except ClientException as exc:
             message="failed to get status of operation {}:\nerror:\n{}".format(
                     operationId,
                     str(exc))
-            raise OsmHttpException(message)
+            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
@@ -351,7 +378,7 @@ class Ns(object):
             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'))
+                self._wait(resp.get('id'), wait)
             return resp['id']
             #else:
             #    msg = ""
             return resp['id']
             #else:
             #    msg = ""
@@ -365,7 +392,7 @@ class Ns(object):
             message="failed to exec operation {}:\nerror:\n{}".format(
                     name,
                     str(exc))
             message="failed to exec operation {}:\nerror:\n{}".format(
                     name,
                     str(exc))
-            raise OsmHttpException(message)
+            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):
         """Scales a VNF by adding/removing VDUs
@@ -376,10 +403,12 @@ class Ns(object):
             op_data={}
             op_data["scaleType"] = "SCALE_VNF"
             op_data["scaleVnfData"] = {}
             op_data={}
             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,
@@ -397,14 +426,14 @@ 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)
+        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))
             #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')
+            # if http_code in (200, 201, 202, 204):
+            #     resp = json.loads(resp)
+            print('Alarm created')
             #else:
             #    msg = ""
             #    if resp:
             #else:
             #    msg = ""
             #    if resp:
@@ -414,11 +443,11 @@ 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,14 +456,14 @@ 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)
+        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))
             #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')
+            # if http_code in (200, 201, 202, 204):
+            #    resp = json.loads(resp)
+            print('Alarm deleted')
             #else:
             #    msg = ""
             #    if resp:
             #else:
             #    msg = ""
             #    if resp:
@@ -444,25 +473,25 @@ 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 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)
+        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))
             #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'
+            # if http_code in (200, 201, 202, 204):
+            #    resp = json.loads(resp)
+            return 'Metric exported'
             #else:
             #    msg = ""
             #    if resp:
             #else:
             #    msg = ""
             #    if resp:
@@ -472,11 +501,11 @@ 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("")