Support setting non-string fields
Some attributes in Ironic support multiple types of data, before the CLI was treating everything as a string this patch is changing that by making the CLI to JSON deserialize the values before sending it to Ironic. Change-Id: I7d53b31ad401bc7c36841e0ece28e818cdc8976d Closes-Bug: #1403491
This commit is contained in:
parent
8a07fdffb2
commit
8910d45955
ironicclient
@ -18,6 +18,7 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import json
|
||||
|
||||
from ironicclient import exc
|
||||
from ironicclient.openstack.common import importutils
|
||||
@ -70,16 +71,32 @@ def import_versioned_module(version, submodule=None):
|
||||
return importutils.import_module(module)
|
||||
|
||||
|
||||
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_dict(kwargs, key_to_convert):
|
||||
values_to_convert = kwargs.get(key_to_convert)
|
||||
if values_to_convert:
|
||||
try:
|
||||
kwargs[key_to_convert] = dict(v.split("=", 1)
|
||||
for v in values_to_convert)
|
||||
except ValueError:
|
||||
raise exc.CommandError(
|
||||
_('%(key)s must be a list of KEY=VALUE not "%(values)s"') %
|
||||
{'key': key_to_convert, 'values': values_to_convert})
|
||||
kwargs[key_to_convert] = dict(split_and_deserialize(v)
|
||||
for v in values_to_convert)
|
||||
return kwargs
|
||||
|
||||
|
||||
@ -91,12 +108,9 @@ def args_array_to_patch(op, attributes):
|
||||
attr = '/' + attr
|
||||
|
||||
if op in ['add', 'replace']:
|
||||
try:
|
||||
path, value = attr.split("=", 1)
|
||||
patch.append({'op': op, 'path': path, 'value': value})
|
||||
except ValueError:
|
||||
raise exc.CommandError(_('Attributes must be a list of '
|
||||
'PATH=VALUE not "%s"') % attr)
|
||||
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})
|
||||
|
@ -23,31 +23,35 @@ from ironicclient.tests import utils as test_utils
|
||||
|
||||
|
||||
class UtilsTest(test_utils.BaseTestCase):
|
||||
|
||||
def test_args_array_to_dict(self):
|
||||
my_args = {
|
||||
'matching_metadata': ['metadata.key=metadata_value'],
|
||||
'matching_metadata': ['str=foo', 'int=1', 'bool=true',
|
||||
'list=[1, 2, 3]', 'dict={"foo": "bar"}'],
|
||||
'other': 'value'
|
||||
}
|
||||
cleaned_dict = utils.args_array_to_dict(my_args,
|
||||
"matching_metadata")
|
||||
self.assertEqual({
|
||||
'matching_metadata': {'metadata.key': 'metadata_value'},
|
||||
'matching_metadata': {'str': 'foo', 'int': 1, 'bool': True,
|
||||
'list': [1, 2, 3], 'dict': {'foo': 'bar'}},
|
||||
'other': 'value'
|
||||
}, cleaned_dict)
|
||||
|
||||
def test_args_array_to_patch(self):
|
||||
my_args = {
|
||||
'attributes': ['foo=bar', '/extra/bar=baz'],
|
||||
'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': 'bar',
|
||||
'path': '/foo'},
|
||||
{'op': 'add',
|
||||
'value': 'baz',
|
||||
'path': '/extra/bar'}], patch)
|
||||
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 = {
|
||||
@ -67,6 +71,29 @@ class UtilsTest(test_utils.BaseTestCase):
|
||||
self.assertEqual([{'op': 'remove', 'path': '/foo'},
|
||||
{'op': 'remove', 'path': '/extra/bar'}], patch)
|
||||
|
||||
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 CommonParamsForListTest(test_utils.BaseTestCase):
|
||||
def setUp(self):
|
||||
|
Loading…
x
Reference in New Issue
Block a user