Add support for updating a bay
Users can use these commands to add/remove/update bay attribute(s): magnum bay-update <bay_id> replace <path=value> magnum bay-update <bay_id> add <path=value> magnum bay-update <bay_id> remove <path=value> For example, this command updates the node_count attribute of a bay: magnum bay-update 0aab40ba-3f48-4e6e-88a6-247be18b9711 replace node_count=1 Change-Id: If003beaca8e7e8bbdff2cf24d2810b0db9efdf41 Implements: blueprint update-node-count
This commit is contained in:
parent
104937fe38
commit
77084e6b84
|
@ -15,6 +15,11 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from magnumclient.openstack.common._i18n import _
|
||||||
|
from magnumclient.openstack.common.apiclient import exceptions as exc
|
||||||
|
|
||||||
|
|
||||||
def common_filters(marker=None, limit=None, sort_key=None, sort_dir=None):
|
def common_filters(marker=None, limit=None, sort_key=None, sort_dir=None):
|
||||||
"""Generate common filters for any list request.
|
"""Generate common filters for any list request.
|
||||||
|
@ -35,3 +40,41 @@ def common_filters(marker=None, limit=None, sort_key=None, sort_dir=None):
|
||||||
if sort_dir is not None:
|
if sort_dir is not None:
|
||||||
filters.append('sort_dir=%s' % sort_dir)
|
filters.append('sort_dir=%s' % sort_dir)
|
||||||
return filters
|
return filters
|
||||||
|
|
||||||
|
|
||||||
|
def split_and_deserialize(string):
|
||||||
|
"""Split and try to JSON deserialize a string.
|
||||||
|
|
||||||
|
Gets a string with the KEY=VALUE format, split it (using '=' as the
|
||||||
|
separator) and try to JSON deserialize the VALUE.
|
||||||
|
:returns: A tuple of (key, value).
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
key, value = string.split("=", 1)
|
||||||
|
except ValueError:
|
||||||
|
raise exc.CommandError(_('Attributes must be a list of '
|
||||||
|
'PATH=VALUE not "%s"') % string)
|
||||||
|
try:
|
||||||
|
value = json.loads(value)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return (key, value)
|
||||||
|
|
||||||
|
|
||||||
|
def args_array_to_patch(op, attributes):
|
||||||
|
patch = []
|
||||||
|
for attr in attributes:
|
||||||
|
# Sanitize
|
||||||
|
if not attr.startswith('/'):
|
||||||
|
attr = '/' + attr
|
||||||
|
if op in ['add', 'replace']:
|
||||||
|
path, value = split_and_deserialize(attr)
|
||||||
|
patch.append({'op': op, 'path': path, 'value': value})
|
||||||
|
|
||||||
|
elif op == "remove":
|
||||||
|
# For remove only the key is needed
|
||||||
|
patch.append({'op': op, 'path': attr})
|
||||||
|
else:
|
||||||
|
raise exc.CommandError(_('Unknown PATCH operation: %s') % op)
|
||||||
|
return patch
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from magnumclient.common import utils
|
from magnumclient.common import utils
|
||||||
|
from magnumclient.openstack.common.apiclient import exceptions as exc
|
||||||
from magnumclient.tests import utils as test_utils
|
from magnumclient.tests import utils as test_utils
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,3 +33,65 @@ class CommonFiltersTest(test_utils.BaseTestCase):
|
||||||
for key in ('marker', 'sort_key', 'sort_dir'):
|
for key in ('marker', 'sort_key', 'sort_dir'):
|
||||||
result = utils.common_filters(**{key: 'test'})
|
result = utils.common_filters(**{key: 'test'})
|
||||||
self.assertEqual(['%s=test' % key], result)
|
self.assertEqual(['%s=test' % key], result)
|
||||||
|
|
||||||
|
|
||||||
|
class SplitAndDeserializeTest(test_utils.BaseTestCase):
|
||||||
|
|
||||||
|
def test_split_and_deserialize(self):
|
||||||
|
ret = utils.split_and_deserialize('str=foo')
|
||||||
|
self.assertEqual(('str', 'foo'), ret)
|
||||||
|
|
||||||
|
ret = utils.split_and_deserialize('int=1')
|
||||||
|
self.assertEqual(('int', 1), ret)
|
||||||
|
|
||||||
|
ret = utils.split_and_deserialize('bool=false')
|
||||||
|
self.assertEqual(('bool', False), ret)
|
||||||
|
|
||||||
|
ret = utils.split_and_deserialize('list=[1, "foo", 2]')
|
||||||
|
self.assertEqual(('list', [1, "foo", 2]), ret)
|
||||||
|
|
||||||
|
ret = utils.split_and_deserialize('dict={"foo": 1}')
|
||||||
|
self.assertEqual(('dict', {"foo": 1}), ret)
|
||||||
|
|
||||||
|
ret = utils.split_and_deserialize('str_int="1"')
|
||||||
|
self.assertEqual(('str_int', "1"), ret)
|
||||||
|
|
||||||
|
def test_split_and_deserialize_fail(self):
|
||||||
|
self.assertRaises(exc.CommandError,
|
||||||
|
utils.split_and_deserialize, 'foo:bar')
|
||||||
|
|
||||||
|
|
||||||
|
class ArgsArrayToPatchTest(test_utils.BaseTestCase):
|
||||||
|
|
||||||
|
def test_args_array_to_patch(self):
|
||||||
|
my_args = {
|
||||||
|
'attributes': ['str=foo', 'int=1', 'bool=true',
|
||||||
|
'list=[1, 2, 3]', 'dict={"foo": "bar"}'],
|
||||||
|
'op': 'add',
|
||||||
|
}
|
||||||
|
patch = utils.args_array_to_patch(my_args['op'],
|
||||||
|
my_args['attributes'])
|
||||||
|
self.assertEqual([{'op': 'add', 'value': 'foo', 'path': '/str'},
|
||||||
|
{'op': 'add', 'value': 1, 'path': '/int'},
|
||||||
|
{'op': 'add', 'value': True, 'path': '/bool'},
|
||||||
|
{'op': 'add', 'value': [1, 2, 3], 'path': '/list'},
|
||||||
|
{'op': 'add', 'value': {"foo": "bar"},
|
||||||
|
'path': '/dict'}], patch)
|
||||||
|
|
||||||
|
def test_args_array_to_patch_format_error(self):
|
||||||
|
my_args = {
|
||||||
|
'attributes': ['foobar'],
|
||||||
|
'op': 'add',
|
||||||
|
}
|
||||||
|
self.assertRaises(exc.CommandError, utils.args_array_to_patch,
|
||||||
|
my_args['op'], my_args['attributes'])
|
||||||
|
|
||||||
|
def test_args_array_to_patch_remove(self):
|
||||||
|
my_args = {
|
||||||
|
'attributes': ['/foo', 'extra/bar'],
|
||||||
|
'op': 'remove',
|
||||||
|
}
|
||||||
|
patch = utils.args_array_to_patch(my_args['op'],
|
||||||
|
my_args['attributes'])
|
||||||
|
self.assertEqual([{'op': 'remove', 'path': '/foo'},
|
||||||
|
{'op': 'remove', 'path': '/extra/bar'}], patch)
|
||||||
|
|
|
@ -18,6 +18,7 @@ import json
|
||||||
import os.path
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from magnumclient.common import utils as magnum_utils
|
||||||
from magnumclient.openstack.common import cliutils as utils
|
from magnumclient.openstack.common import cliutils as utils
|
||||||
|
|
||||||
|
|
||||||
|
@ -102,6 +103,27 @@ def do_bay_show(cs, args):
|
||||||
_show_bay(bay)
|
_show_bay(bay)
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg('bay', metavar='<bay id>', help="UUID of bay")
|
||||||
|
@utils.arg(
|
||||||
|
'op',
|
||||||
|
metavar='<op>',
|
||||||
|
choices=['add', 'replace', 'remove'],
|
||||||
|
help="Operations: 'add', 'replace' or 'remove'")
|
||||||
|
@utils.arg(
|
||||||
|
'attributes',
|
||||||
|
metavar='<path=value>',
|
||||||
|
nargs='+',
|
||||||
|
action='append',
|
||||||
|
default=[],
|
||||||
|
help="Attributes to add/replace or remove "
|
||||||
|
"(only PATH is necessary on remove)")
|
||||||
|
def do_bay_update(cs, args):
|
||||||
|
"""Update information about the given bay."""
|
||||||
|
patch = magnum_utils.args_array_to_patch(args.op, args.attributes[0])
|
||||||
|
bay = cs.bays.update(args.bay, patch)
|
||||||
|
_show_bay(bay)
|
||||||
|
|
||||||
|
|
||||||
@utils.arg('--name',
|
@utils.arg('--name',
|
||||||
metavar='<name>',
|
metavar='<name>',
|
||||||
help='Name of the bay to create.')
|
help='Name of the bay to create.')
|
||||||
|
|
3
tox.ini
3
tox.ini
|
@ -34,3 +34,6 @@ show-source = True
|
||||||
ignore = E123,E125,H803
|
ignore = E123,E125,H803
|
||||||
builtins = _
|
builtins = _
|
||||||
exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build
|
exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build
|
||||||
|
|
||||||
|
[hacking]
|
||||||
|
import_exceptions = magnumclient.openstack.common._i18n
|
||||||
|
|
Loading…
Reference in New Issue