diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/v1/app.py b/sysinv/cgts-client/cgts-client/cgtsclient/v1/app.py index 2a82a0667c..2b4a536283 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/v1/app.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/v1/app.py @@ -61,12 +61,13 @@ class AppManager(base.Manager): resp, body = self.api.json_request('POST', self._path() + "/update", body=data) return self.resource_class(self, body) - def remove(self, app_name): + def remove(self, app_name, force): """Uninstall the specified application :param name: app_name + :param force: True/False - cli flag/argument """ - return self._update(self._path(app_name) + '?directive=remove', + return self._update(self._path(app_name) + '?directive=remove&force=' + str(force), {'values': {}}) def abort(self, app_name): @@ -77,12 +78,13 @@ class AppManager(base.Manager): return self._update(self._path(app_name) + '?directive=abort', {'values': {}}) - def delete(self, app_name): + def delete(self, app_name, force): """Delete application data :param name: app_name + :param force: True/False - cli flag/argument """ - return self._delete(self._path(app_name)) + return self._delete(self._path(app_name) + '?force=' + str(force)) def _find_app(cc, app_name): diff --git a/sysinv/cgts-client/cgts-client/cgtsclient/v1/app_shell.py b/sysinv/cgts-client/cgts-client/cgtsclient/v1/app_shell.py index 2bc6e0fe21..1f9ad50248 100644 --- a/sysinv/cgts-client/cgts-client/cgtsclient/v1/app_shell.py +++ b/sysinv/cgts-client/cgts-client/cgtsclient/v1/app_shell.py @@ -162,10 +162,14 @@ def do_application_apply(cc, args): @utils.arg('name', metavar='', help='Name of the application to be uninstalled') +@utils.arg('-f', '--force', + action='store_true', + default=False, + help="Force a remove operation") def do_application_remove(cc, args): """Uninstall the application""" try: - response = cc.app.remove(args.name) + response = cc.app.remove(args.name, args.force) _print_application_show(response) _print_reminder_msg(args.name) except exc.HTTPNotFound: @@ -188,10 +192,14 @@ def do_application_abort(cc, args): @utils.arg('name', metavar='', help='Name of the application to be deleted') +@utils.arg('-f', '--force', + action='store_true', + default=False, + help="Force a delete operation") def do_application_delete(cc, args): """Remove the uninstalled application from the system""" try: - cc.app.delete(args.name) + cc.app.delete(args.name, args.force) print('Application %s deleted.' % args.name) except exc.HTTPNotFound: raise exc.CommandError('Application not found: %s' % args.name) diff --git a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/kube_app.py b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/kube_app.py index 11861ad8c1..902be291b6 100644 --- a/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/kube_app.py +++ b/sysinv/sysinv/sysinv/sysinv/api/controllers/v1/kube_app.py @@ -241,8 +241,8 @@ class KubeAppController(rest.RestController): return KubeApp.convert_with_links(new_app) @cutils.synchronized(LOCK_NAME) - @wsme_pecan.wsexpose(KubeApp, wtypes.text, wtypes.text, wtypes.text) - def patch(self, name, directive, values): + @wsme_pecan.wsexpose(KubeApp, wtypes.text, wtypes.text, wtypes.text, wtypes.text) + def patch(self, name, directive, values, force=None): """Install/update the specified application :param name: application name @@ -332,6 +332,13 @@ class KubeAppController(rest.RestController): constants.APP_LIFECYCLE_TYPE_SEMANTIC_CHECK, constants.APP_LIFECYCLE_TIMING_PRE, constants.APP_REMOVE_OP) + # Converting string to boolean + if force == 'True': + force = True + else: + force = False + + lifecycle_hook_info.extra = {constants.APP_LIFECYCLE_FORCE_OPERATION: force} self._app_lifecycle_actions(db_app, lifecycle_hook_info) except rpc_common.RemoteError as e: @@ -490,8 +497,8 @@ class KubeAppController(rest.RestController): return KubeApp.convert_with_links(target_app) @cutils.synchronized(LOCK_NAME) - @wsme_pecan.wsexpose(None, wtypes.text, status_code=204) - def delete(self, name): + @wsme_pecan.wsexpose(None, wtypes.text, wtypes.text, status_code=204) + def delete(self, name, force=None): """Delete the application with the given name :param name: application name @@ -516,6 +523,13 @@ class KubeAppController(rest.RestController): constants.APP_LIFECYCLE_TYPE_SEMANTIC_CHECK, constants.APP_LIFECYCLE_TIMING_PRE, constants.APP_DELETE_OP) + # Converting string to boolean + if force == 'True': + force = True + else: + force = False + + lifecycle_hook_info.extra = {constants.APP_LIFECYCLE_FORCE_OPERATION: force} self._app_lifecycle_actions(db_app, lifecycle_hook_info) except rpc_common.RemoteError as e: diff --git a/sysinv/sysinv/sysinv/sysinv/common/constants.py b/sysinv/sysinv/sysinv/sysinv/common/constants.py index 799e4a8a1e..eaca88e7f7 100644 --- a/sysinv/sysinv/sysinv/sysinv/common/constants.py +++ b/sysinv/sysinv/sysinv/sysinv/common/constants.py @@ -1589,7 +1589,7 @@ APP_LIFECYCLE_TYPE_ARMADA_REQUEST = 'armada-request' APP_LIFECYCLE_MODE_MANUAL = 'manual' APP_LIFECYCLE_MODE_AUTO = 'auto' - +APP_LIFECYCLE_FORCE_OPERATION = 'force' # Application metadata constants APP_METADATA_MAINTAIN_USER_OVERRIDES = 'maintain_user_overrides' diff --git a/sysinv/sysinv/sysinv/sysinv/helm/lifecycle_base.py b/sysinv/sysinv/sysinv/sysinv/helm/lifecycle_base.py index 7400a69945..c5231e51ca 100644 --- a/sysinv/sysinv/sysinv/sysinv/helm/lifecycle_base.py +++ b/sysinv/sysinv/sysinv/sysinv/helm/lifecycle_base.py @@ -45,8 +45,9 @@ class AppLifecycleOperator(object): # hook and raise exception.LifecycleSemanticCheckException pass # Check if operation is a delete or remove operation - elif hook_info.operation == constants.APP_DELETE_OP or \ - hook_info.operation == constants.APP_REMOVE_OP: + elif (hook_info.operation in [constants.APP_DELETE_OP, + constants.APP_REMOVE_OP]) and \ + not hook_info.extra['force']: try: # Store the forbidden operations in a list forbidden = conductor_obj.apps_metadata[