diff --git a/releasenotes/notes/add-force-delete-to-osc-dfff1db4da937415.yaml b/releasenotes/notes/add-force-delete-to-osc-dfff1db4da937415.yaml new file mode 100644 index 00000000..41effc12 --- /dev/null +++ b/releasenotes/notes/add-force-delete-to-osc-dfff1db4da937415.yaml @@ -0,0 +1,10 @@ +--- +features: + - | + The command ``trove force-delete`` is now available + to use in the python-openstackclient CLI as ``openstack database + instance force delete`` + - | + The command ``trove cluster-force-delete`` is now available + to use in the python-openstackclient CLI as ``openstack database + instance cluster force delete`` diff --git a/setup.cfg b/setup.cfg index bd27e3fd..05509c98 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,6 +37,7 @@ openstack.database.v1 = database_backup_show = troveclient.osc.v1.database_backups:ShowDatabaseBackup database_cluster_create = troveclient.osc.v1.database_clusters:CreateDatabaseCluster database_cluster_delete = troveclient.osc.v1.database_clusters:DeleteDatabaseCluster + database_cluster_force_delete = troveclient.osc.v1.database_clusters:ForceDeleteDatabaseCluster database_cluster_list = troveclient.osc.v1.database_clusters:ListDatabaseClusters database_cluster_list_instances = troveclient.osc.v1.database_clusters:ListDatabaseClusterInstances database_cluster_reset_status = troveclient.osc.v1.database_clusters:ResetDatabaseClusterStatus @@ -57,6 +58,7 @@ openstack.database.v1 = database_flavor_show = troveclient.osc.v1.database_flavors:ShowDatabaseFlavor database_instance_create = troveclient.osc.v1.database_instances:CreateDatabaseInstance database_instance_delete = troveclient.osc.v1.database_instances:DeleteDatabaseInstance + database_instance_force_delete = troveclient.osc.v1.database_instances:ForceDeleteDatabaseInstance database_instance_list = troveclient.osc.v1.database_instances:ListDatabaseInstances database_instance_reset_status = troveclient.osc.v1.database_instances:ResetDatabaseInstanceStatus database_instance_resize_flavor = troveclient.osc.v1.database_instances:ResizeDatabaseInstanceFlavor diff --git a/troveclient/osc/v1/database_clusters.py b/troveclient/osc/v1/database_clusters.py index 997d79c8..b9e19182 100644 --- a/troveclient/osc/v1/database_clusters.py +++ b/troveclient/osc/v1/database_clusters.py @@ -258,3 +258,29 @@ class UpgradeDatabaseCluster(command.Command): cluster = utils.find_resource(database_clusters, parsed_args.cluster) database_clusters.upgrade(cluster, parsed_args.datastore_version) + + +class ForceDeleteDatabaseCluster(command.Command): + + _description = _("Force delete a cluster.") + + def get_parser(self, prog_name): + parser = super(ForceDeleteDatabaseCluster, self).get_parser(prog_name) + parser.add_argument( + 'cluster', + metavar='', + help=_('ID or name of the cluster.'), + ) + return parser + + def take_action(self, parsed_args): + database_clusters = self.app.client_manager.database.clusters + cluster = utils.find_resource(database_clusters, + parsed_args.cluster) + database_clusters.reset_status(cluster) + try: + database_clusters.delete(cluster) + except Exception as e: + msg = (_("Failed to delete cluster %(cluster)s: %(e)s") + % {'cluster': parsed_args.cluster, 'e': e}) + raise exceptions.CommandError(msg) diff --git a/troveclient/osc/v1/database_instances.py b/troveclient/osc/v1/database_instances.py index 60b5e986..0af4f368 100644 --- a/troveclient/osc/v1/database_instances.py +++ b/troveclient/osc/v1/database_instances.py @@ -475,3 +475,30 @@ class ResizeDatabaseInstanceVolume(command.Command): instance = osc_utils.find_resource(db_instances, parsed_args.instance) db_instances.resize_volume(instance, parsed_args.size) + + +class ForceDeleteDatabaseInstance(command.Command): + + _description = _("Force delete an instance.") + + def get_parser(self, prog_name): + parser = (super(ForceDeleteDatabaseInstance, self) + .get_parser(prog_name)) + parser.add_argument( + 'instance', + metavar='', + help=_('ID or name of the instance'), + ) + return parser + + def take_action(self, parsed_args): + db_instances = self.app.client_manager.database.instances + instance = osc_utils.find_resource(db_instances, + parsed_args.instance) + db_instances.reset_status(instance) + try: + db_instances.delete(instance) + except Exception as e: + msg = (_("Failed to delete instance %(instance)s: %(e)s") + % {'instance': parsed_args.instance, 'e': e}) + raise exceptions.CommandError(msg) diff --git a/troveclient/tests/osc/v1/test_database_clusters.py b/troveclient/tests/osc/v1/test_database_clusters.py index 4130da6d..70fc9c30 100644 --- a/troveclient/tests/osc/v1/test_database_clusters.py +++ b/troveclient/tests/osc/v1/test_database_clusters.py @@ -205,3 +205,31 @@ class TestDatabaseClusterUpgrade(TestClusters): self.cluster_client.upgrade.assert_called_with('cluster1', 'datastore_version1') self.assertIsNone(result) + + +class TestDatabaseClusterForceDelete(TestClusters): + + def setUp(self): + super(TestDatabaseClusterForceDelete, self).setUp() + self.cmd = database_clusters.ForceDeleteDatabaseCluster(self.app, None) + + @mock.patch.object(utils, 'find_resource') + def test_cluster_force_delete(self, mock_find): + args = ['cluster1'] + mock_find.return_value = args[0] + parsed_args = self.check_parser(self.cmd, args, []) + result = self.cmd.take_action(parsed_args) + self.cluster_client.reset_status.assert_called_with('cluster1') + self.cluster_client.delete.assert_called_with('cluster1') + self.assertIsNone(result) + + @mock.patch.object(utils, 'find_resource') + def test_cluster_force_delete_with_exception(self, mock_find): + args = ['fakecluster'] + parsed_args = self.check_parser(self.cmd, args, []) + mock_find.return_value = args[0] + + self.cluster_client.delete.side_effect = exceptions.CommandError + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args) diff --git a/troveclient/tests/osc/v1/test_database_instances.py b/troveclient/tests/osc/v1/test_database_instances.py index 0f20fb3c..66171a7f 100644 --- a/troveclient/tests/osc/v1/test_database_instances.py +++ b/troveclient/tests/osc/v1/test_database_instances.py @@ -236,3 +236,32 @@ class TestDatabaseInstanceResizeVolume(TestInstances): self.instance_client.resize_volume.assert_called_with('instance1', 5) self.assertIsNone(result) + + +class TestDatabaseInstanceForceDelete(TestInstances): + + def setUp(self): + super(TestDatabaseInstanceForceDelete, self).setUp() + self.cmd = (database_instances + .ForceDeleteDatabaseInstance(self.app, None)) + + @mock.patch.object(utils, 'find_resource') + def test_instance_force_delete(self, mock_find): + args = ['instance1'] + mock_find.return_value = args[0] + parsed_args = self.check_parser(self.cmd, args, []) + result = self.cmd.take_action(parsed_args) + self.instance_client.reset_status.assert_called_with('instance1') + self.instance_client.delete.assert_called_with('instance1') + self.assertIsNone(result) + + @mock.patch.object(utils, 'find_resource') + def test_instance_force_delete_with_exception(self, mock_find): + args = ['fakeinstance'] + parsed_args = self.check_parser(self.cmd, args, []) + mock_find.return_value = args[0] + + self.instance_client.delete.side_effect = exceptions.CommandError + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args)