Add "image unset" command
This patch add a command that supports unsetting image tags and properties Change-Id: I6f2cf45a61ff89da6664f3a34ae49fdd85d8c986 Closes-Bug:#1582968
This commit is contained in:
parent
9da02d14ea
commit
3e11661074
@ -499,3 +499,30 @@ Display image details
|
||||
.. describe:: <image>
|
||||
|
||||
Image to display (name or ID)
|
||||
|
||||
image unset
|
||||
-----------
|
||||
|
||||
*Only supported for Image v2*
|
||||
|
||||
Unset image tags or properties
|
||||
|
||||
.. program:: image unset
|
||||
.. code:: bash
|
||||
|
||||
os image set
|
||||
[--tag <tag>]
|
||||
[--property <property>]
|
||||
<image>
|
||||
|
||||
.. option:: --tag <tag>
|
||||
|
||||
Unset a tag on this image (repeat option to unset multiple tags)
|
||||
|
||||
.. option:: --property <property>
|
||||
|
||||
Unset a property on this image (repeat option to unset multiple properties)
|
||||
|
||||
.. describe:: <image>
|
||||
|
||||
Image to modify (name or ID)
|
||||
|
@ -65,3 +65,12 @@ class ImageTests(test.TestCase):
|
||||
self.openstack('image set --property a=b --property c=d ' + self.NAME)
|
||||
raw_output = self.openstack('image show ' + self.NAME + opts)
|
||||
self.assertEqual(self.NAME + "\na='b', c='d'\n", raw_output)
|
||||
|
||||
def test_image_unset(self):
|
||||
opts = self.get_show_opts(["name", "tags", "properties"])
|
||||
self.openstack('image set --tag 01 ' + self.NAME)
|
||||
self.openstack('image unset --tag 01 ' + self.NAME)
|
||||
# test_image_metadata has set image properties "a" and "c"
|
||||
self.openstack('image unset --property a --property c ' + self.NAME)
|
||||
raw_output = self.openstack('image show ' + self.NAME + opts)
|
||||
self.assertEqual(self.NAME + "\n\n", raw_output)
|
||||
|
@ -805,8 +805,8 @@ class SetImage(command.Command):
|
||||
|
||||
# Checks if anything that requires getting the image
|
||||
if not (kwargs or parsed_args.deactivate or parsed_args.activate):
|
||||
self.log.warning(_("No arguments specified"))
|
||||
return {}, {}
|
||||
msg = _("No arguments specified")
|
||||
raise exceptions.CommandError(msg)
|
||||
|
||||
image = utils.find_resource(
|
||||
image_client.images, parsed_args.image)
|
||||
@ -856,3 +856,88 @@ class ShowImage(command.ShowOne):
|
||||
|
||||
info = _format_image(image)
|
||||
return zip(*sorted(six.iteritems(info)))
|
||||
|
||||
|
||||
class UnsetImage(command.Command):
|
||||
"""Unset image tags and properties"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(UnsetImage, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
"image",
|
||||
metavar="<image>",
|
||||
help=_("Image to modify (name or ID)"),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--tag",
|
||||
dest="tags",
|
||||
metavar="<tag>",
|
||||
default=[],
|
||||
action='append',
|
||||
help=_("Unset a tag on this image "
|
||||
"(repeat option to set multiple tags)"),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--property",
|
||||
dest="properties",
|
||||
metavar="<property_key>",
|
||||
default=[],
|
||||
action='append',
|
||||
help=_("Unset a property on this image "
|
||||
"(repeat option to set multiple properties)"),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
image_client = self.app.client_manager.image
|
||||
image = utils.find_resource(
|
||||
image_client.images,
|
||||
parsed_args.image,
|
||||
)
|
||||
|
||||
if not (parsed_args.tags or parsed_args.properties):
|
||||
msg = _("No arguments specified")
|
||||
raise exceptions.CommandError(msg)
|
||||
|
||||
kwargs = {}
|
||||
tagret = 0
|
||||
propret = 0
|
||||
if parsed_args.tags:
|
||||
for k in parsed_args.tags:
|
||||
try:
|
||||
image_client.image_tags.delete(image.id, k)
|
||||
except Exception:
|
||||
self.log.error(_("tag unset failed,"
|
||||
" '%s' is a nonexistent tag ") % k)
|
||||
tagret += 1
|
||||
|
||||
if parsed_args.properties:
|
||||
for k in parsed_args.properties:
|
||||
try:
|
||||
assert(k in image.keys())
|
||||
except AssertionError:
|
||||
self.log.error(_("property unset failed,"
|
||||
" '%s' is a nonexistent property ") % k)
|
||||
propret += 1
|
||||
image_client.images.update(
|
||||
image.id,
|
||||
parsed_args.properties,
|
||||
**kwargs)
|
||||
|
||||
tagtotal = len(parsed_args.tags)
|
||||
proptotal = len(parsed_args.properties)
|
||||
if (tagret > 0 and propret > 0):
|
||||
msg = (_("Failed to unset %(tagret)s of %(tagtotal)s tags,"
|
||||
"Failed to unset %(propret)s of %(proptotal)s properties.")
|
||||
% {'tagret': tagret, 'tagtotal': tagtotal,
|
||||
'propret': propret, 'proptotal': proptotal})
|
||||
raise exceptions.CommandError(msg)
|
||||
elif tagret > 0:
|
||||
msg = (_("Failed to unset %(target)s of %(tagtotal)s tags.")
|
||||
% {'tagret': tagret, 'tagtotal': tagtotal})
|
||||
raise exceptions.CommandError(msg)
|
||||
elif propret > 0:
|
||||
msg = (_("Failed to unset %(propret)s of %(proptotal)s"
|
||||
" properties.")
|
||||
% {'propret': propret, 'proptotal': proptotal})
|
||||
raise exceptions.CommandError(msg)
|
||||
|
@ -157,6 +157,8 @@ class FakeImagev2Client(object):
|
||||
self.images.resource_class = fakes.FakeResource(None, {})
|
||||
self.image_members = mock.Mock()
|
||||
self.image_members.resource_class = fakes.FakeResource(None, {})
|
||||
self.image_tags = mock.Mock()
|
||||
self.image_tags.resource_class = fakes.FakeResource(None, {})
|
||||
self.auth_token = kwargs['token']
|
||||
self.management_url = kwargs['endpoint']
|
||||
|
||||
|
@ -37,6 +37,8 @@ class TestImage(image_fakes.TestImagev2):
|
||||
self.images_mock.reset_mock()
|
||||
self.image_members_mock = self.app.client_manager.image.image_members
|
||||
self.image_members_mock.reset_mock()
|
||||
self.image_tags_mock = self.app.client_manager.image.image_tags
|
||||
self.image_tags_mock.reset_mock()
|
||||
|
||||
# Get shortcut to the Mocks in identity client
|
||||
self.project_mock = self.app.client_manager.identity.projects
|
||||
@ -1184,3 +1186,94 @@ class TestImageShow(TestImage):
|
||||
|
||||
self.assertEqual(image_fakes.IMAGE_columns, columns)
|
||||
self.assertEqual(image_fakes.IMAGE_SHOW_data, data)
|
||||
|
||||
|
||||
class TestImageUnset(TestImage):
|
||||
|
||||
attrs = {}
|
||||
attrs['tags'] = ['test']
|
||||
attrs['prop'] = 'test'
|
||||
image = image_fakes.FakeImage.create_one_image(attrs)
|
||||
|
||||
def setUp(self):
|
||||
super(TestImageUnset, self).setUp()
|
||||
|
||||
# Set up the schema
|
||||
self.model = warlock.model_factory(
|
||||
image_fakes.IMAGE_schema,
|
||||
schemas.SchemaBasedModel,
|
||||
)
|
||||
|
||||
self.images_mock.get.return_value = self.image
|
||||
self.image_tags_mock.delete.return_value = self.image
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = image.UnsetImage(self.app, None)
|
||||
|
||||
def test_image_unset_tag_option(self):
|
||||
|
||||
arglist = [
|
||||
'--tag', 'test',
|
||||
self.image.id,
|
||||
]
|
||||
|
||||
verifylist = [
|
||||
('tags', ['test']),
|
||||
('image', self.image.id),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.image_tags_mock.delete.assert_called_with(
|
||||
self.image.id, 'test'
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_image_unset_property_option(self):
|
||||
|
||||
arglist = [
|
||||
'--property', 'prop',
|
||||
self.image.id,
|
||||
]
|
||||
|
||||
verifylist = [
|
||||
('properties', ['prop']),
|
||||
('image', self.image.id)
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
|
||||
kwargs = {}
|
||||
self.images_mock.update.assert_called_with(
|
||||
self.image.id,
|
||||
parsed_args.properties,
|
||||
**kwargs)
|
||||
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_image_unset_mixed_option(self):
|
||||
|
||||
arglist = [
|
||||
'--tag', 'test',
|
||||
'--property', 'prop',
|
||||
self.image.id,
|
||||
]
|
||||
|
||||
verifylist = [
|
||||
('tags', ['test']),
|
||||
('properties', ['prop']),
|
||||
('image', self.image.id)
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
|
||||
kwargs = {}
|
||||
self.images_mock.update.assert_called_with(
|
||||
self.image.id,
|
||||
parsed_args.properties,
|
||||
**kwargs)
|
||||
|
||||
self.image_tags_mock.delete.assert_called_with(
|
||||
self.image.id, 'test'
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
|
4
releasenotes/notes/bug-1582968-4d44912a033b242c.yaml
Normal file
4
releasenotes/notes/bug-1582968-4d44912a033b242c.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- Add "image unset" command.
|
||||
[Bug '1582968 <https://bugs.launchpad.net/python-openstackclient/+bug/1582968>']
|
@ -322,6 +322,7 @@ openstack.image.v2 =
|
||||
image_save = openstackclient.image.v2.image:SaveImage
|
||||
image_show = openstackclient.image.v2.image:ShowImage
|
||||
image_set = openstackclient.image.v2.image:SetImage
|
||||
image_unset = openstackclient.image.v2.image:UnsetImage
|
||||
|
||||
openstack.network.v2 =
|
||||
address_scope_create = openstackclient.network.v2.address_scope:CreateAddressScope
|
||||
|
Loading…
Reference in New Issue
Block a user