Merge "Adding `image delete --store
and
image import info
` commands"
This commit is contained in:
commit
7dc1276f40
@ -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.
|
||||||
|
|
@ -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 = _(
|
||||||
|
31
openstackclient/image/v2/info.py
Normal file
31
openstackclient/image/v2/info.py
Normal 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', [])),),
|
||||||
|
)
|
30
openstackclient/tests/functional/image/v2/test_info.py
Normal file
30
openstackclient/tests/functional/image/v2/test_info.py
Normal 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'])
|
@ -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()
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
43
openstackclient/tests/unit/image/v2/test_info.py
Normal file
43
openstackclient/tests/unit/image/v2/test_info.py
Normal 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()
|
@ -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.
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user