Merge "Adding `image delete --store and image import info` commands"

This commit is contained in:
Zuul 2023-08-09 19:01:35 +00:00 committed by Gerrit Code Review
commit 7dc1276f40
9 changed files with 184 additions and 7 deletions

View File

@ -18,7 +18,7 @@ image-tag-update,image set --tag <tag>,Update an image with the given tag.
image-tasks,,Get tasks associated with image. image-tasks,,Get tasks associated with image.
image-update,image set,Update an existing image. image-update,image set,Update an existing image.
image-upload,,Upload data for a specific image. image-upload,,Upload data for a specific image.
import-info,,Print import methods available from Glance. import-info,image import info,Show available import methods from Glance.
location-add,,Add a location (and related metadata) to an image. location-add,,Add a location (and related metadata) to an image.
location-delete,,Remove locations (and related metadata) from an image. location-delete,,Remove locations (and related metadata) from an image.
location-update,,Update metadata of an image's location. location-update,,Update metadata of an image's location.
@ -57,7 +57,7 @@ member-delete,image remove project,Delete image member.
member-get,,Show details of an image member member-get,,Show details of an image member
member-list,image member list,Describe sharing permissions by image. member-list,image member list,Describe sharing permissions by image.
member-update,image set --accept --reject --status,Update the status of a member for a given image. member-update,image set --accept --reject --status,Update the status of a member for a given image.
stores-delete,,Delete image from specific store. stores-delete,image delete --store,Delete image from specific store.
stores-info,,Print available backends from Glance. stores-info,,Print available backends from Glance.
task-create,WONTFIX,Create a new task. task-create,WONTFIX,Create a new task.
task-list,image task list,List tasks you can access. task-list,image task list,List tasks you can access.

1 cache-clear Clear all images from cache, queue or both.
18 image-tasks Get tasks associated with image.
19 image-update image set Update an existing image.
20 image-upload Upload data for a specific image.
21 import-info image import info Print import methods available from Glance. Show available import methods from Glance.
22 location-add Add a location (and related metadata) to an image.
23 location-delete Remove locations (and related metadata) from an image.
24 location-update Update metadata of an image's location.
57 member-get Show details of an image member
58 member-list image member list Describe sharing permissions by image.
59 member-update image set --accept --reject --status Update the status of a member for a given image.
60 stores-delete image delete --store Delete image from specific store.
61 stores-info Print available backends from Glance.
62 task-create WONTFIX Create a new task.
63 task-list image task list List tasks you can access.

View File

@ -653,6 +653,13 @@ class DeleteImage(command.Command):
nargs="+", nargs="+",
help=_("Image(s) to delete (name or ID)"), help=_("Image(s) to delete (name or ID)"),
) )
parser.add_argument(
'--store',
metavar='<STORE>',
# default=None,
dest='store',
help=_('Store to delete image(s) from.'),
)
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
@ -664,7 +671,18 @@ class DeleteImage(command.Command):
image, image,
ignore_missing=False, ignore_missing=False,
) )
image_client.delete_image(image_obj.id) except sdk_exceptions.ResourceNotFound as e:
msg = _("Unable to process request: %(e)s") % {'e': e}
raise exceptions.CommandError(msg)
try:
image_client.delete_image(
image_obj.id,
store=parsed_args.store,
ignore_missing=False,
)
except sdk_exceptions.ResourceNotFound:
msg = _("Multi Backend support not enabled.")
raise exceptions.CommandError(msg)
except Exception as e: except Exception as e:
del_result += 1 del_result += 1
msg = _( msg = _(

View File

@ -0,0 +1,31 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from osc_lib.cli import format_columns
from osc_lib.command import command
from openstackclient.i18n import _
class ImportInfo(command.ShowOne):
_description = _("Show available import methods")
def take_action(self, parsed_args):
image_client = self.app.client_manager.image
import_info = image_client.get_import_info()
import_methods = import_info.import_methods or {}
return (
('import-methods',),
(format_columns.ListColumn(import_methods.get('value', [])),),
)

View File

@ -0,0 +1,30 @@
# Copyright 2023 Red Hat.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from openstackclient.tests.functional.image import base
class InfoTests(base.BaseImageTests):
"""Functional tests for Info commands"""
def setUp(self):
super(InfoTests, self).setUp()
def tearDown(self):
super().tearDown()
def test_image_import_info(self):
output = self.openstack('image import info', parse_output=True)
self.assertIsNotNone(output['import-methods'])

View File

@ -41,6 +41,7 @@ class FakeImagev2Client:
self.deactivate_image = mock.Mock() self.deactivate_image = mock.Mock()
self.stage_image = mock.Mock() self.stage_image = mock.Mock()
self.import_image = mock.Mock() self.import_image = mock.Mock()
self.get_import_info = mock.Mock()
self.members = mock.Mock() self.members = mock.Mock()
self.add_member = mock.Mock() self.add_member = mock.Mock()

View File

@ -520,7 +520,29 @@ class TestImageDelete(TestImage):
result = self.cmd.take_action(parsed_args) result = self.cmd.take_action(parsed_args)
self.client.delete_image.assert_called_with(images[0].id) self.client.delete_image.assert_called_with(
images[0].id, store=parsed_args.store, ignore_missing=False
)
self.assertIsNone(result)
def test_image_delete_from_store(self):
images = self.setup_images_mock(count=1)
arglist = [
images[0].id,
'--store',
'store1',
]
verifylist = [('images', [images[0].id]), ('store', 'store1')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.client.find_image.side_effect = images
result = self.cmd.take_action(parsed_args)
self.client.delete_image.assert_called_with(
images[0].id, store=parsed_args.store, ignore_missing=False
)
self.assertIsNone(result) self.assertIsNone(result)
def test_image_delete_multi_images(self): def test_image_delete_multi_images(self):
@ -536,10 +558,33 @@ class TestImageDelete(TestImage):
result = self.cmd.take_action(parsed_args) result = self.cmd.take_action(parsed_args)
calls = [mock.call(i.id) for i in images] calls = [
mock.call(i.id, store=parsed_args.store, ignore_missing=False)
for i in images
]
self.client.delete_image.assert_has_calls(calls) self.client.delete_image.assert_has_calls(calls)
self.assertIsNone(result) self.assertIsNone(result)
def test_image_delete_from_store_without_multi_backend(self):
images = self.setup_images_mock(count=1)
arglist = [images[0].id, '--store', 'store1']
verifylist = [('images', [images[0].id]), ('store', 'store1')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.client.find_image.side_effect = images
self.client.delete_image.side_effect = sdk_exceptions.ResourceNotFound
exc = self.assertRaises(
exceptions.CommandError,
self.cmd.take_action,
parsed_args,
)
self.assertIn(
"Multi Backend support not enabled",
str(exc),
)
def test_image_delete_multi_images_exception(self): def test_image_delete_multi_images_exception(self):
images = image_fakes.create_images(count=2) images = image_fakes.create_images(count=2)
arglist = [ arglist = [
@ -562,7 +607,10 @@ class TestImageDelete(TestImage):
self.assertRaises( self.assertRaises(
exceptions.CommandError, self.cmd.take_action, parsed_args exceptions.CommandError, self.cmd.take_action, parsed_args
) )
calls = [mock.call(i.id) for i in images] calls = [
mock.call(i.id, store=parsed_args.store, ignore_missing=False)
for i in images
]
self.client.delete_image.assert_has_calls(calls) self.client.delete_image.assert_has_calls(calls)

View File

@ -0,0 +1,43 @@
# Copyright 2023 Red Hat.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from openstackclient.image.v2 import info
from openstackclient.tests.unit.image.v2 import fakes as info_fakes
class TestInfo(info_fakes.TestImagev2):
def setUp(self):
super().setUp()
# Get shortcuts to mocked image client
self.client = self.app.client_manager.image
class TestImportInfo(TestInfo):
import_info = info_fakes.create_one_import_info()
def setUp(self):
super().setUp()
self.client.get_import_info.return_value = self.import_info
self.cmd = info.ImportInfo(self.app, None)
def test_import_info(self):
arglist = []
parsed_args = self.check_parser(self.cmd, arglist, [])
self.cmd.take_action(parsed_args)
self.client.get_import_info.assert_called()

View File

@ -0,0 +1,6 @@
---
features:
- |
Add ``image import info`` command, allowing users to know available import
methods, and `--store` option to ``image delete``, allowing users to delete
image from particular store.

View File

@ -387,7 +387,7 @@ openstack.image.v2 =
image_stage = openstackclient.image.v2.image:StageImage image_stage = openstackclient.image.v2.image:StageImage
image_task_show = openstackclient.image.v2.task:ShowTask image_task_show = openstackclient.image.v2.task:ShowTask
image_task_list = openstackclient.image.v2.task:ListTask image_task_list = openstackclient.image.v2.task:ListTask
image_import = openstackclient.image.v2.image:ImportImage image_import_info = openstackclient.image.v2.info:ImportInfo
image_metadef_namespace_create = openstackclient.image.v2.metadef_namespaces:CreateMetadefNameSpace image_metadef_namespace_create = openstackclient.image.v2.metadef_namespaces:CreateMetadefNameSpace
image_metadef_namespace_delete = openstackclient.image.v2.metadef_namespaces:DeleteMetadefNameSpace image_metadef_namespace_delete = openstackclient.image.v2.metadef_namespaces:DeleteMetadefNameSpace