summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsunyajing <yajing.sun@easystack.cn>2016-05-28 11:01:22 +0800
committersunyajing <yajing.sun@easystack.cn>2016-05-28 16:38:22 +0800
commit3e11661074e1a7e051e0ebff5800f8f1aac85153 (patch)
tree8714135039224e6e1cc338cbd75dd038e8022655
parent9da02d14eadc39da6f97b3df095af8b0c452a5b4 (diff)
downloadpython-openstackclient-3e11661074e1a7e051e0ebff5800f8f1aac85153.tar.gz
Add "image unset" command
This patch add a command that supports unsetting image tags and properties Change-Id: I6f2cf45a61ff89da6664f3a34ae49fdd85d8c986 Closes-Bug:#1582968
-rw-r--r--doc/source/command-objects/image.rst27
-rw-r--r--functional/tests/image/v2/test_image.py9
-rw-r--r--openstackclient/image/v2/image.py89
-rw-r--r--openstackclient/tests/image/v2/fakes.py2
-rw-r--r--openstackclient/tests/image/v2/test_image.py93
-rw-r--r--releasenotes/notes/bug-1582968-4d44912a033b242c.yaml4
-rw-r--r--setup.cfg1
7 files changed, 223 insertions, 2 deletions
diff --git a/doc/source/command-objects/image.rst b/doc/source/command-objects/image.rst
index 942c03d5..d6451af7 100644
--- a/doc/source/command-objects/image.rst
+++ b/doc/source/command-objects/image.rst
@@ -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)
diff --git a/functional/tests/image/v2/test_image.py b/functional/tests/image/v2/test_image.py
index f0ebc116..6a33ad88 100644
--- a/functional/tests/image/v2/test_image.py
+++ b/functional/tests/image/v2/test_image.py
@@ -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)
diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py
index a9c0f1fd..a81f092c 100644
--- a/openstackclient/image/v2/image.py
+++ b/openstackclient/image/v2/image.py
@@ -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)
diff --git a/openstackclient/tests/image/v2/fakes.py b/openstackclient/tests/image/v2/fakes.py
index a662a585..24aaec51 100644
--- a/openstackclient/tests/image/v2/fakes.py
+++ b/openstackclient/tests/image/v2/fakes.py
@@ -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']
diff --git a/openstackclient/tests/image/v2/test_image.py b/openstackclient/tests/image/v2/test_image.py
index beebdef9..ca20d83d 100644
--- a/openstackclient/tests/image/v2/test_image.py
+++ b/openstackclient/tests/image/v2/test_image.py
@@ -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)
diff --git a/releasenotes/notes/bug-1582968-4d44912a033b242c.yaml b/releasenotes/notes/bug-1582968-4d44912a033b242c.yaml
new file mode 100644
index 00000000..9d9794e3
--- /dev/null
+++ b/releasenotes/notes/bug-1582968-4d44912a033b242c.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - Add "image unset" command.
+ [Bug '1582968 <https://bugs.launchpad.net/python-openstackclient/+bug/1582968>']
diff --git a/setup.cfg b/setup.cfg
index a62f5d25..a2da7048 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -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