Merge "Tweak volume commands and add k=v argparse action"

This commit is contained in:
Jenkins 2013-05-30 16:25:36 +00:00 committed by Gerrit Code Review
commit f4f85a2f79
4 changed files with 223 additions and 58 deletions

View File

@ -0,0 +1,34 @@
# Copyright 2013 OpenStack Foundation
#
# 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.
#
"""argparse Custom Actions"""
import argparse
class KeyValueAction(argparse.Action):
"""A custom action to parse arguments as key=value pairs.
Ensures that dest is a dict
"""
def __call__(self, parser, namespace, values, option_string=None):
# Make sure we have an empty dict rather than None
if getattr(namespace, self.dest, None) is None:
setattr(namespace, self.dest, {})
# Add value if an assignment else remove it
if '=' in values:
getattr(namespace, self.dest, {}).update([values.split('=', 1)])
else:
getattr(namespace, self.dest, {}).pop(values, None)

View File

@ -21,6 +21,7 @@ from cliff import command
from cliff import lister from cliff import lister
from cliff import show from cliff import show
from openstackclient.common import parseractions
from openstackclient.common import utils from openstackclient.common import utils
@ -118,21 +119,22 @@ class SetVolumeType(command.Command):
help='Volume type name or ID to update', help='Volume type name or ID to update',
) )
parser.add_argument( parser.add_argument(
'meta_data', '--property',
metavar='<key=value>', metavar='<key=value>',
help='meta-data to add to volume type', action=parseractions.KeyValueAction,
help='Property to add/change for this volume type '
'(repeat option to set multiple properties)',
) )
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
self.log.debug('take_action(%s)' % parsed_args) self.log.debug('take_action(%s)' % parsed_args)
meta = dict(v.split('=') for v in parsed_args.meta_data.split(' '))
volume_client = self.app.client_manager.volume volume_client = self.app.client_manager.volume
volume_type = utils.find_resource( volume_type = utils.find_resource(
volume_client.volume_types, parsed_args.volume_type) volume_client.volume_types, parsed_args.volume_type)
volume_type.set_keys(meta) if parsed_args.property:
volume_type.set_keys(parsed_args.property)
return return
@ -148,12 +150,15 @@ class UnsetVolumeType(command.Command):
parser.add_argument( parser.add_argument(
'volume_type', 'volume_type',
metavar='<volume-type>', metavar='<volume-type>',
help='Type ID or name to update', help='Type ID or name to remove',
) )
parser.add_argument( parser.add_argument(
'meta_data', '--property',
metavar='<key>', metavar='<key>',
help='meta-data to remove from volume type (key only)', action='append',
default=[],
help='Property key to remove from volume '
'(repeat option to remove multiple properties)',
) )
return parser return parser
@ -161,12 +166,17 @@ class UnsetVolumeType(command.Command):
self.log.debug('take_action(%s)' % parsed_args) self.log.debug('take_action(%s)' % parsed_args)
volume_client = self.app.client_manager.volume volume_client = self.app.client_manager.volume
volume_type = utils.find_resource( volume_type = utils.find_resource(
volume_client.volume_types, parsed_args.volume_type) volume_client.volume_types,
parsed_args.volume_type,
key_list = [] )
key_list.append(parsed_args.meta_data)
volume_type.unset_keys(key_list)
if parsed_args.property:
volume_client.volumes.delete_metadata(
volume_type.id,
parsed_args.property,
)
else:
self.app.log.error("No changes requested\n")
return return

View File

@ -16,12 +16,12 @@
"""Volume v1 Volume action implementations""" """Volume v1 Volume action implementations"""
import logging import logging
import sys
from cliff import command from cliff import command
from cliff import lister from cliff import lister
from cliff import show from cliff import show
from openstackclient.common import parseractions
from openstackclient.common import utils from openstackclient.common import utils
@ -63,32 +63,34 @@ class CreateVolume(show.ShowOne):
parser.add_argument( parser.add_argument(
'--user-id', '--user-id',
metavar='<user-id>', metavar='<user-id>',
help='User id derived from context', help='Override user id derived from context (admin only)',
) )
parser.add_argument( parser.add_argument(
'--project-id', '--project-id',
metavar='<project-id>', metavar='<project-id>',
help='Project id derived from context', help='Override project id derived from context (admin only)',
) )
parser.add_argument( parser.add_argument(
'--availability-zone', '--availability-zone',
metavar='<availability-zone>', metavar='<availability-zone>',
help='Availability Zone to use', help='Availability zone to use',
) )
parser.add_argument( parser.add_argument(
'--property', '--property',
metavar='<key=value>', metavar='<key=value>',
help='Optional property to set on volume creation', action=parseractions.KeyValueAction,
help='Property to store for this volume '
'(repeat option to set multiple properties)',
) )
parser.add_argument( parser.add_argument(
'--image-ref', '--image',
metavar='<image-ref>', metavar='<image>',
help='reference to an image stored in glance', help='Reference to a stored image',
) )
parser.add_argument( parser.add_argument(
'--source-volid', '--source',
metavar='<source-volid>', metavar='<volume>',
help='ID of source volume to clone from', help='Source for volume clone',
) )
return parser return parser
@ -98,22 +100,25 @@ class CreateVolume(show.ShowOne):
volume_client = self.app.client_manager.volume volume_client = self.app.client_manager.volume
meta = None source_volume = None
if parsed_args.meta_data: if parsed_args.source:
meta = dict(v.split('=') for v in parsed_args.meta_data.split(' ')) source_volume = utils.find_resource(
volume_client.volumes,
parsed_args.source,
).id
volume = volume_client.volumes.create( volume = volume_client.volumes.create(
parsed_args.size, parsed_args.size,
parsed_args.snapshot_id, parsed_args.snapshot_id,
parsed_args.source_volid, source_volume,
parsed_args.name, parsed_args.name,
parsed_args.description, parsed_args.description,
parsed_args.volume_type, parsed_args.volume_type,
parsed_args.user_id, parsed_args.user_id,
parsed_args.project_id, parsed_args.project_id,
parsed_args.availability_zone, parsed_args.availability_zone,
meta, parsed_args.property,
parsed_args.image_ref parsed_args.image
) )
return zip(*sorted(volume._info.iteritems())) return zip(*sorted(volume._info.iteritems()))
@ -175,13 +180,13 @@ class ListVolume(lister.Lister):
'--all-tenants', '--all-tenants',
action='store_true', action='store_true',
default=False, default=False,
help='Display information from all tenants (Admin-only)', help='Display information from all tenants (admin only)',
) )
parser.add_argument( parser.add_argument(
'--long', '--long',
action='store_true', action='store_true',
default=False, default=False,
help='Display meta-data', help='Display properties',
) )
return parser return parser
@ -221,19 +226,25 @@ class SetVolume(command.Command):
parser.add_argument( parser.add_argument(
'volume', 'volume',
metavar='<volume>', metavar='<volume>',
help='Name or ID of volume to change') help='Name or ID of volume to change',
)
parser.add_argument( parser.add_argument(
'--name', '--name',
metavar='<new-volume-name>', metavar='<new-name>',
help='New volume name') help='New volume name',
)
parser.add_argument( parser.add_argument(
'--description', '--description',
metavar='<volume-description>', metavar='<new-description>',
help='New volume description') help='New volume description',
)
parser.add_argument( parser.add_argument(
'--meta-data', '--property',
metavar='<key=value>', metavar='<key=value>',
help='meta-data to add to volume') action=parseractions.KeyValueAction,
help='Property to add/change for this volume '
'(repeat option to set multiple properties)',
)
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
@ -241,21 +252,22 @@ class SetVolume(command.Command):
volume_client = self.app.client_manager.volume volume_client = self.app.client_manager.volume
volume = utils.find_resource(volume_client.volumes, parsed_args.volume) volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
meta = None
if parsed_args.property: if parsed_args.property:
meta = dict(v.split('=') for v in parsed_args.property.split(' ')) print "property: %s" % parsed_args.property
volume_client.volumes.set_metadata(volume.id, meta) volume_client.volumes.set_metadata(volume.id, parsed_args.property)
kwargs = {} kwargs = {}
if parsed_args.name: if parsed_args.name:
kwargs['display_name'] = parsed_args.name kwargs['display_name'] = parsed_args.name
if parsed_args.description: if parsed_args.description:
kwargs['display_description'] = parsed_args.description kwargs['display_description'] = parsed_args.description
if kwargs:
print "kwargs: %s" % kwargs
volume_client.volumes.update(volume.id, **kwargs)
if not kwargs and not parsed_args.property:
self.app.log.error("No changes requested\n")
if not kwargs and not meta:
sys.stdout.write("Volume not updated, no arguments present \n")
return
volume_client.volumes.update(volume.id, **kwargs)
return return
@ -270,7 +282,8 @@ class ShowVolume(show.ShowOne):
parser.add_argument( parser.add_argument(
'volume', 'volume',
metavar='<volume>', metavar='<volume>',
help='Name or ID of volume to display') help='Name or ID of volume to display',
)
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
@ -292,11 +305,16 @@ class UnsetVolume(command.Command):
parser.add_argument( parser.add_argument(
'volume', 'volume',
metavar='<volume>', metavar='<volume>',
help='Name or ID of volume to change') help='Name or ID of volume to change',
)
parser.add_argument( parser.add_argument(
'--meta-data', '--property',
metavar='<key>', metavar='<key>',
help='meta-data to remove from volume (key only)') action='append',
default=[],
help='Property key to remove from volume '
'(repeat to set multiple values)',
)
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
@ -305,14 +323,13 @@ class UnsetVolume(command.Command):
volume = utils.find_resource( volume = utils.find_resource(
volume_client.volumes, parsed_args.volume) volume_client.volumes, parsed_args.volume)
if not parsed_args.meta_data: if parsed_args.property:
sys.stdout.write("Volume not updated, no arguments present \n") volume_client.volumes.delete_metadata(
return volume.id,
parsed_args.property,
key_list = [] )
key_list.append(parsed_args.meta_data) else:
volume_client.volumes.delete_metadata(volume.id, key_list) self.app.log.error("No changes requested\n")
return return

View File

@ -0,0 +1,104 @@
# Copyright 2012-2013 OpenStack Foundation
#
# 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.
#
import argparse
from openstackclient.common import parseractions
from tests import utils
class TestKeyValueAction(utils.TestCase):
def test_good_values(self):
parser = argparse.ArgumentParser()
# Set up our typical usage
parser.add_argument(
'--property',
metavar='<key=value>',
action=parseractions.KeyValueAction,
help='Property to store for this volume '
'(repeat option to set multiple properties)',
)
results = parser.parse_args([
'--property', 'red=',
'--property', 'green=100%',
'--property', 'blue=50%',
])
actual = getattr(results, 'property', {})
# All should pass through unmolested
expect = {'red': '', 'green': '100%', 'blue': '50%'}
self.assertDictEqual(expect, actual)
def test_default_values(self):
parser = argparse.ArgumentParser()
# Set up our typical usage
parser.add_argument(
'--property',
metavar='<key=value>',
action=parseractions.KeyValueAction,
default={'green': '20%', 'format': '#rgb'},
help='Property to store for this volume '
'(repeat option to set multiple properties)',
)
results = parser.parse_args([
'--property', 'red=',
'--property', 'green=100%',
'--property', 'blue=50%',
])
actual = getattr(results, 'property', {})
# Verify green default is changed, format default is unchanged
expect = {'red': '', 'green': '100%', 'blue': '50%', 'format': '#rgb'}
self.assertDictEqual(expect, actual)
def test_error_values(self):
parser = argparse.ArgumentParser()
# Set up our typical usage
parser.add_argument(
'--property',
metavar='<key=value>',
action=parseractions.KeyValueAction,
default={'green': '20%', 'blue': '40%'},
help='Property to store for this volume '
'(repeat option to set multiple properties)',
)
results = parser.parse_args([
'--property', 'red',
'--property', 'green=100%',
'--property', 'blue',
])
failhere = None
actual = getattr(results, 'property', {})
# Verify non-existant red key
try:
failhere = actual['red']
except Exception as e:
self.assertTrue(type(e) == KeyError)
# Verify removal of blue key
try:
failhere = actual['blue']
except Exception as e:
self.assertTrue(type(e) == KeyError)
# There should be no red or blue
expect = {'green': '100%'}
self.assertDictEqual(expect, actual)
self.assertEqual(failhere, None)