diff --git a/openstackclient/common/project_cleanup.py b/openstackclient/common/project_cleanup.py index 32a1fa7a30..fdd5b3b7ff 100644 --- a/openstackclient/common/project_cleanup.py +++ b/openstackclient/common/project_cleanup.py @@ -79,6 +79,12 @@ class ProjectCleanup(command.Command): metavar='', help=_('Only delete resources updated before the given time'), ) + parser.add_argument( + '--skip-resource', + metavar='', + help='Skip cleanup of specific resource (repeat if necessary)', + action='append', + ) identity_common.add_project_domain_option_to_parser(parser) return parser @@ -114,7 +120,10 @@ class ProjectCleanup(command.Command): filters['updated_at'] = parsed_args.updated_before project_connect.project_cleanup( - dry_run=True, status_queue=status_queue, filters=filters + dry_run=True, + status_queue=status_queue, + filters=filters, + skip_resources=parsed_args.skip_resource, ) data = [] diff --git a/openstackclient/tests/unit/common/test_project_cleanup.py b/openstackclient/tests/unit/common/test_project_cleanup.py index 64ecf3ef96..fc09bbfdd5 100644 --- a/openstackclient/tests/unit/common/test_project_cleanup.py +++ b/openstackclient/tests/unit/common/test_project_cleanup.py @@ -84,7 +84,12 @@ class TestProjectCleanup(TestProjectCleanupBase): filters = {'created_at': '2200-01-01', 'updated_at': '2200-01-02'} calls = [ - mock.call(dry_run=True, status_queue=mock.ANY, filters=filters), + mock.call( + dry_run=True, + status_queue=mock.ANY, + filters=filters, + skip_resources=None, + ), mock.call(dry_run=False, status_queue=mock.ANY, filters=filters), ] self.project_cleanup_mock.assert_has_calls(calls) @@ -110,7 +115,12 @@ class TestProjectCleanup(TestProjectCleanupBase): self.sdk_connect_as_project_mock.assert_called_with(self.project) calls = [ - mock.call(dry_run=True, status_queue=mock.ANY, filters={}), + mock.call( + dry_run=True, + status_queue=mock.ANY, + filters={}, + skip_resources=None, + ), mock.call(dry_run=False, status_queue=mock.ANY, filters={}), ] self.project_cleanup_mock.assert_has_calls(calls) @@ -135,7 +145,12 @@ class TestProjectCleanup(TestProjectCleanupBase): self.sdk_connect_as_project_mock.assert_called_with(self.project) calls = [ - mock.call(dry_run=True, status_queue=mock.ANY, filters={}), + mock.call( + dry_run=True, + status_queue=mock.ANY, + filters={}, + skip_resources=None, + ), mock.call(dry_run=False, status_queue=mock.ANY, filters={}), ] self.project_cleanup_mock.assert_has_calls(calls) @@ -160,7 +175,12 @@ class TestProjectCleanup(TestProjectCleanupBase): self.sdk_connect_as_project_mock.assert_called_with(self.project) calls = [ - mock.call(dry_run=True, status_queue=mock.ANY, filters={}), + mock.call( + dry_run=True, + status_queue=mock.ANY, + filters={}, + skip_resources=None, + ), ] self.project_cleanup_mock.assert_has_calls(calls) @@ -184,7 +204,10 @@ class TestProjectCleanup(TestProjectCleanupBase): self.sdk_connect_as_project_mock.assert_called_with(self.project) self.project_cleanup_mock.assert_called_once_with( - dry_run=True, status_queue=mock.ANY, filters={} + dry_run=True, + status_queue=mock.ANY, + filters={}, + skip_resources=None, ) self.assertIsNone(result) @@ -208,7 +231,42 @@ class TestProjectCleanup(TestProjectCleanupBase): self.sdk_connect_as_project_mock.assert_not_called() calls = [ - mock.call(dry_run=True, status_queue=mock.ANY, filters={}), + mock.call( + dry_run=True, + status_queue=mock.ANY, + filters={}, + skip_resources=None, + ), + mock.call(dry_run=False, status_queue=mock.ANY, filters={}), + ] + self.project_cleanup_mock.assert_has_calls(calls) + + self.assertIsNone(result) + + def test_project_cleanup_with_skip_resource(self): + skip_resource = "block_storage.backup" + arglist = [ + '--project', + self.project.id, + '--skip-resource', + skip_resource, + ] + verifylist = [('skip_resource', [skip_resource])] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = None + + with mock.patch('sys.stdin', StringIO('y')): + result = self.cmd.take_action(parsed_args) + + self.sdk_connect_as_project_mock.assert_called_with(self.project) + + calls = [ + mock.call( + dry_run=True, + status_queue=mock.ANY, + filters={}, + skip_resources=[skip_resource], + ), mock.call(dry_run=False, status_queue=mock.ANY, filters={}), ] self.project_cleanup_mock.assert_has_calls(calls) diff --git a/releasenotes/notes/project-cleanup-skip-resource-option-4f80db0d8cf36fdb.yaml b/releasenotes/notes/project-cleanup-skip-resource-option-4f80db0d8cf36fdb.yaml new file mode 100644 index 0000000000..ee1ef89dea --- /dev/null +++ b/releasenotes/notes/project-cleanup-skip-resource-option-4f80db0d8cf36fdb.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + A new option ``--skip-resource`` has been added to the + ``project cleanup`` command. This allows to exclude + certain resources from project cleanups, e. g. + ``--skip-resource "block_storage.backup"`` to keep + Cinder backups. diff --git a/requirements.txt b/requirements.txt index 458fb41195..bc95056282 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0 cryptography>=2.7 # BSD/Apache-2.0 cliff>=3.5.0 # Apache-2.0 iso8601>=0.1.11 # MIT -openstacksdk>=0.103.0 # Apache-2.0 +openstacksdk>=1.4.0 # Apache-2.0 osc-lib>=2.3.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0