Merge "Add volume_type extra_specs support to client"

This commit is contained in:
Jenkins
2012-10-31 20:24:16 +00:00
committed by Gerrit Code Review
5 changed files with 167 additions and 14 deletions

View File

@@ -17,9 +17,7 @@ class Client(object):
Then call methods on its managers::
>>> client.servers.list()
...
>>> client.flavors.list()
>>> client.volumes.list()
...
"""

View File

@@ -20,6 +20,7 @@ import os
import sys
import time
from cinderclient import exceptions
from cinderclient import utils
@@ -91,14 +92,17 @@ def _translate_volume_snapshot_keys(collection):
setattr(item, to_key, item._info[from_key])
def _extract_metadata(arg_list):
def _extract_metadata(args):
metadata = {}
for metadatum in arg_list:
assert(metadatum.find('=') > -1), "Improperly formatted metadata "\
"input (%s)" % metadatum
(key, value) = metadatum.split('=', 1)
metadata[key] = value
for metadatum in args.metadata[0]:
# unset doesn't require a val, so we have the if/else
if '=' in metadatum:
(key, value) = metadatum.split('=', 1)
else:
key = metadatum
value = None
metadata[key] = value
return metadata
@@ -219,7 +223,7 @@ def do_create(cs, args):
volume_metadata = None
if args.metadata is not None:
volume_metadata = _extract_metadata(args.metadata)
volume_metadata = _extract_metadata(args)
volume = cs.volumes.create(args.size,
args.snapshot_id,
@@ -375,7 +379,9 @@ def do_snapshot_rename(cs, args):
def _print_volume_type_list(vtypes):
utils.print_list(vtypes, ['ID', 'Name'])
#_translate_type_keys(vtypes)
formatters = {'extra_specs': _print_type_extra_specs}
utils.print_list(vtypes, ['ID', 'Name', 'extra_specs'], formatters)
@utils.service_type('volume')
@@ -387,7 +393,7 @@ def do_type_list(cs, args):
@utils.arg('name',
metavar='<name>',
help="Name of the new flavor")
help="Name of the new volume type")
@utils.service_type('volume')
def do_type_create(cs, args):
"""Create a new volume type."""
@@ -400,10 +406,35 @@ def do_type_create(cs, args):
help="Unique ID of the volume type to delete")
@utils.service_type('volume')
def do_type_delete(cs, args):
"""Delete a specific flavor"""
"""Delete a specific volume type"""
cs.volume_types.delete(args.id)
@utils.arg('vtype',
metavar='<vtype>',
help="Name or ID of the volume type")
@utils.arg('action',
metavar='<action>',
choices=['set', 'unset'],
help="Actions: 'set' or 'unset'")
@utils.arg('metadata',
metavar='<key=value>',
nargs='+',
action='append',
default=[],
help='Extra_specs to set/unset (only key is necessary on unset)')
@utils.service_type('volume')
def do_type_key(cs, args):
"Set or unset extra_spec for a volume type."""
vtype = _find_volume_type(cs, args.vtype)
keypair = _extract_metadata(args)
if args.action == 'set':
vtype.set_keys(keypair)
elif args.action == 'unset':
vtype.unset_keys(keypair.keys())
def do_endpoints(cs, args):
"""Discover endpoints that get returned from the authenticate services"""
catalog = cs.client.service_catalog.catalog
@@ -513,3 +544,15 @@ def do_rate_limits(cs, args):
limits = cs.limits.get().rate
columns = ['Verb', 'URI', 'Value', 'Remain', 'Unit', 'Next_Available']
utils.print_list(limits, columns)
def _print_type_extra_specs(vol_type):
try:
return vol_type.get_keys()
except exceptions.NotFound:
return "N/A"
def _find_volume_type(cs, vtype):
"""Get a volume type by name or ID."""
return utils.find_resource(cs.volume_types, vtype)

View File

@@ -26,7 +26,52 @@ class VolumeType(base.Resource):
A Volume Type is the type of volume to be created
"""
def __repr__(self):
return "<Volume Type: %s>" % self.name
return "<VolumeType: %s>" % self.name
def get_keys(self):
"""
Get extra specs from a volume type.
:param vol_type: The :class:`VolumeType` to get extra specs from
"""
_resp, body = self.manager.api.client.get(
"/types/%s/extra_specs" %
base.getid(self))
return body["extra_specs"]
def set_keys(self, metadata):
"""
Set extra specs on a volume type.
:param type : The :class:`VolumeType` to set extra spec on
:param metadata: A dict of key/value pairs to be set
"""
body = {'extra_specs': metadata}
return self.manager._create(
"/types/%s/extra_specs" % base.getid(self),
body,
"extra_specs",
return_raw=True)
def unset_keys(self, keys):
"""
Unset extra specs on a volue type.
:param type_id: The :class:`VolumeType` to unset extra spec on
:param keys: A list of keys to be unset
"""
# NOTE(jdg): This wasn't actually doing all of the keys before
# the return in the loop resulted in ony ONE key being unset.
# since on success the return was NONE, we'll only interrupt the loop
# and return if there's an error
result = None
for k in keys:
resp = self.manager._delete(
"/types/%s/extra_specs/%s" % (
base.getid(self), k))
if resp is not None:
return resp
class VolumeTypeManager(base.ManagerWithFind):

View File

@@ -227,3 +227,35 @@ class FakeHTTPClient(base_client.HTTPClient):
'metadata_items': [],
'volumes': 2,
'gigabytes': 1}})
#
# VolumeTypes
#
def get_types(self, **kw):
return (200, {
'volume_types': [{'id': 1,
'name': 'test-type-1',
'extra_specs':{}},
{'id': 2,
'name': 'test-type-2',
'extra_specs':{}}]})
def get_types_1(self, **kw):
return (200, {'volume_type': {'id': 1,
'name': 'test-type-1',
'extra_specs': {}}})
def post_types(self, body, **kw):
return (202, {'volume_type': {'id': 3,
'name': 'test-type-3',
'extra_specs': {}}})
def post_types_1_extra_specs(self, body, **kw):
assert body.keys() == ['extra_specs']
return (200, {'extra_specs': {'k': 'v'}})
def delete_types_1_extra_specs_k(self, **kw):
return(204, None)
def delete_types_1(self, **kw):
return (202, None)

35
tests/v1/test_types.py Normal file
View File

@@ -0,0 +1,35 @@
from cinderclient import exceptions
from cinderclient.v1 import volume_types
from tests import utils
from tests.v1 import fakes
cs = fakes.FakeClient()
class TypesTest(utils.TestCase):
def test_list_types(self):
tl = cs.volume_types.list()
cs.assert_called('GET', '/types')
for t in tl:
self.assertTrue(isinstance(t, volume_types.VolumeType))
def test_create(self):
t = cs.volume_types.create('test-type-3')
cs.assert_called('POST', '/types')
self.assertTrue(isinstance(t, volume_types.VolumeType))
def test_set_key(self):
t = cs.volume_types.get(1)
t.set_keys({'k': 'v'})
cs.assert_called('POST',
'/types/1/extra_specs',
{'extra_specs': {'k': 'v'}})
def test_unsset_keys(self):
t = cs.volume_types.get(1)
t.unset_keys(['k'])
cs.assert_called('DELETE', '/types/1/extra_specs/k')
def test_delete(self):
cs.volume_types.delete(1)
cs.assert_called('DELETE', '/types/1')