Support flavor extra specs in nova client
Add flavor extra specs so that user can list/set/unset extra specs in nova client blueprint extra-specs-in-nova-client Change-Id: I6ad7293e29764648c79943c4d05f3a09931af411 Signed-off-by: Yunhong, Jiang <yunhong.jiang@intel.com>
This commit is contained in:
parent
247b53022b
commit
3dd0393fbb
@ -4,6 +4,7 @@ Flavor interface.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from novaclient import base
|
from novaclient import base
|
||||||
|
from novaclient import exceptions
|
||||||
|
|
||||||
|
|
||||||
class Flavor(base.Resource):
|
class Flavor(base.Resource):
|
||||||
@ -29,6 +30,43 @@ class Flavor(base.Resource):
|
|||||||
"""
|
"""
|
||||||
return self._info.get("os-flavor-access:is_public", 'N/A')
|
return self._info.get("os-flavor-access:is_public", 'N/A')
|
||||||
|
|
||||||
|
def get_keys(self):
|
||||||
|
"""
|
||||||
|
Get extra specs from a flavor.
|
||||||
|
|
||||||
|
:param flavor: The :class:`Flavor` to get extra specs from
|
||||||
|
"""
|
||||||
|
_resp, body = self.manager.api.client.get(
|
||||||
|
"/flavors/%s/os-extra_specs" %
|
||||||
|
base.getid(self))
|
||||||
|
return body["extra_specs"]
|
||||||
|
|
||||||
|
def set_keys(self, metadata):
|
||||||
|
"""
|
||||||
|
Set extra specs on a flavor.
|
||||||
|
|
||||||
|
:param flavor: The :class:`Flavor` to set extra spec on
|
||||||
|
:param metadata: A dict of key/value pairs to be set
|
||||||
|
"""
|
||||||
|
body = {'extra_specs': metadata}
|
||||||
|
return self.manager._create(
|
||||||
|
"/flavors/%s/os-extra_specs" % base.getid(self),
|
||||||
|
body,
|
||||||
|
"extra_specs",
|
||||||
|
return_raw=True)
|
||||||
|
|
||||||
|
def unset_keys(self, keys):
|
||||||
|
"""
|
||||||
|
Unset extra specs on a flavor.
|
||||||
|
|
||||||
|
:param flavor: The :class:`Flavor` to unset extra spec on
|
||||||
|
:param keys: A list of keys to be unset
|
||||||
|
"""
|
||||||
|
for k in keys:
|
||||||
|
return self.manager._delete(
|
||||||
|
"/flavors/%s/os-extra_specs/%s" % (
|
||||||
|
base.getid(self), k))
|
||||||
|
|
||||||
|
|
||||||
class FlavorManager(base.ManagerWithFind):
|
class FlavorManager(base.ManagerWithFind):
|
||||||
"""
|
"""
|
||||||
|
@ -314,8 +314,16 @@ def _translate_flavor_keys(collection):
|
|||||||
setattr(item, to_key, item._info[from_key])
|
setattr(item, to_key, item._info[from_key])
|
||||||
|
|
||||||
|
|
||||||
def _print_flavor_list(flavors):
|
def _print_flavor_extra_specs(flavor):
|
||||||
|
try:
|
||||||
|
return flavor.get_keys()
|
||||||
|
except exceptions.NotFound:
|
||||||
|
return "N/A"
|
||||||
|
|
||||||
|
|
||||||
|
def _print_flavor_list(cs, flavors):
|
||||||
_translate_flavor_keys(flavors)
|
_translate_flavor_keys(flavors)
|
||||||
|
formatters = {'extra_specs': _print_flavor_extra_specs}
|
||||||
utils.print_list(flavors, [
|
utils.print_list(flavors, [
|
||||||
'ID',
|
'ID',
|
||||||
'Name',
|
'Name',
|
||||||
@ -325,7 +333,8 @@ def _print_flavor_list(flavors):
|
|||||||
'Swap',
|
'Swap',
|
||||||
'VCPUs',
|
'VCPUs',
|
||||||
'RXTX_Factor',
|
'RXTX_Factor',
|
||||||
'Is_Public'])
|
'Is_Public',
|
||||||
|
'extra_specs'], formatters)
|
||||||
|
|
||||||
|
|
||||||
def do_flavor_list(cs, _args):
|
def do_flavor_list(cs, _args):
|
||||||
@ -334,7 +343,7 @@ def do_flavor_list(cs, _args):
|
|||||||
for flavor in flavors:
|
for flavor in flavors:
|
||||||
# int needed for numerical sort
|
# int needed for numerical sort
|
||||||
flavor.id = int(flavor.id)
|
flavor.id = int(flavor.id)
|
||||||
_print_flavor_list(flavors)
|
_print_flavor_list(cs, flavors)
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('id',
|
@utils.arg('id',
|
||||||
@ -351,7 +360,7 @@ def do_flavor_delete(cs, args):
|
|||||||
def do_flavor_show(cs, args):
|
def do_flavor_show(cs, args):
|
||||||
"""Show details about the given flavor."""
|
"""Show details about the given flavor."""
|
||||||
flavor = _find_flavor(cs, args.flavor)
|
flavor = _find_flavor(cs, args.flavor)
|
||||||
_print_flavor(flavor)
|
_print_flavor(cs, flavor)
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('name',
|
@utils.arg('name',
|
||||||
@ -391,7 +400,31 @@ def do_flavor_create(cs, args):
|
|||||||
f = cs.flavors.create(args.name, args.ram, args.vcpus, args.disk, args.id,
|
f = cs.flavors.create(args.name, args.ram, args.vcpus, args.disk, args.id,
|
||||||
args.ephemeral, args.swap, args.rxtx_factor,
|
args.ephemeral, args.swap, args.rxtx_factor,
|
||||||
args.is_public)
|
args.is_public)
|
||||||
_print_flavor_list([f])
|
_print_flavor_list(cs, [f])
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg('flavor',
|
||||||
|
metavar='<flavor>',
|
||||||
|
help="Name or ID of flavor")
|
||||||
|
@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)')
|
||||||
|
def do_flavor_key(cs, args):
|
||||||
|
"""Set or unset extra_spec for a flavor."""
|
||||||
|
flavor = _find_flavor(cs, args.flavor)
|
||||||
|
keypair = _extract_metadata(args)
|
||||||
|
|
||||||
|
if args.action == 'set':
|
||||||
|
flavor.set_keys(keypair)
|
||||||
|
elif args.action == 'unset':
|
||||||
|
flavor.unset_keys(keypair.keys())
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('--flavor',
|
@utils.arg('--flavor',
|
||||||
@ -530,10 +563,11 @@ def _print_image(image):
|
|||||||
utils.print_dict(info)
|
utils.print_dict(info)
|
||||||
|
|
||||||
|
|
||||||
def _print_flavor(flavor):
|
def _print_flavor(cs, flavor):
|
||||||
info = flavor._info.copy()
|
info = flavor._info.copy()
|
||||||
# ignore links, we don't need to present those
|
# ignore links, we don't need to present those
|
||||||
info.pop('links')
|
info.pop('links')
|
||||||
|
info.update({"extra_specs": _print_flavor_extra_specs(flavor)})
|
||||||
utils.print_dict(info)
|
utils.print_dict(info)
|
||||||
|
|
||||||
|
|
||||||
|
@ -400,6 +400,24 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||||||
def post_flavors(self, body, **kw):
|
def post_flavors(self, body, **kw):
|
||||||
return (202, {'flavor': self.get_flavors_detail()[1]['flavors'][0]})
|
return (202, {'flavor': self.get_flavors_detail()[1]['flavors'][0]})
|
||||||
|
|
||||||
|
def get_flavors_1_os_extra_specs(self, **kw):
|
||||||
|
return (200,
|
||||||
|
{'extra_specs': {"k1": "v1"}})
|
||||||
|
|
||||||
|
def get_flavors_2_os_extra_specs(self, **kw):
|
||||||
|
return (200,
|
||||||
|
{'extra_specs': {"k2": "v2"}})
|
||||||
|
|
||||||
|
def post_flavors_1_os_extra_specs(self, body, **kw):
|
||||||
|
assert body.keys() == ['extra_specs']
|
||||||
|
fakes.assert_has_keys(body['extra_specs'],
|
||||||
|
required=['k1'])
|
||||||
|
return (200,
|
||||||
|
{'extra_specs': {"k1": "v1"}})
|
||||||
|
|
||||||
|
def delete_flavors_1_os_extra_specs_k1(self, **kw):
|
||||||
|
return (204, None)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Flavor access
|
# Flavor access
|
||||||
#
|
#
|
||||||
|
@ -91,3 +91,14 @@ class FlavorsTest(utils.TestCase):
|
|||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
cs.flavors.delete("flavordelete")
|
cs.flavors.delete("flavordelete")
|
||||||
cs.assert_called('DELETE', '/flavors/flavordelete')
|
cs.assert_called('DELETE', '/flavors/flavordelete')
|
||||||
|
|
||||||
|
def test_set_keys(self):
|
||||||
|
f = cs.flavors.get(1)
|
||||||
|
f.set_keys({'k1': 'v1'})
|
||||||
|
cs.assert_called('POST', '/flavors/1/os-extra_specs',
|
||||||
|
{"extra_specs": {'k1': 'v1'}})
|
||||||
|
|
||||||
|
def test_unset_keys(self):
|
||||||
|
f = cs.flavors.get(1)
|
||||||
|
f.unset_keys(['k1'])
|
||||||
|
cs.assert_called('DELETE', '/flavors/1/os-extra_specs/k1')
|
||||||
|
@ -167,11 +167,12 @@ class ShellTest(utils.TestCase):
|
|||||||
|
|
||||||
def test_flavor_list(self):
|
def test_flavor_list(self):
|
||||||
self.run_command('flavor-list')
|
self.run_command('flavor-list')
|
||||||
|
self.assert_called('GET', '/flavors/2/os-extra_specs')
|
||||||
self.assert_called_anytime('GET', '/flavors/detail')
|
self.assert_called_anytime('GET', '/flavors/detail')
|
||||||
|
|
||||||
def test_flavor_show(self):
|
def test_flavor_show(self):
|
||||||
self.run_command('flavor-show 1')
|
self.run_command('flavor-show 1')
|
||||||
self.assert_called('GET', '/flavors/1')
|
self.assert_called_anytime('GET', '/flavors/1')
|
||||||
|
|
||||||
def test_image_show(self):
|
def test_image_show(self):
|
||||||
self.run_command('image-show 1')
|
self.run_command('image-show 1')
|
||||||
@ -389,8 +390,9 @@ class ShellTest(utils.TestCase):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.assert_called('POST', '/flavors', body, pos=-2)
|
self.assert_called('POST', '/flavors', pos=-3)
|
||||||
self.assert_called('GET', '/flavors/1')
|
self.assert_called('GET', '/flavors/1', pos=-2)
|
||||||
|
self.assert_called('GET', '/flavors/1/os-extra_specs', pos=-1)
|
||||||
|
|
||||||
def test_aggregate_list(self):
|
def test_aggregate_list(self):
|
||||||
self.run_command('aggregate-list')
|
self.run_command('aggregate-list')
|
||||||
|
Loading…
Reference in New Issue
Block a user