Adds Labels Support
Adds labels attribute and associated tools/tests to the magnum client. Implements: blueprint extend-client-network-attributes Change-Id: Ie8465275646b82aa9bd7a9de756c519bdd6ec0e2
This commit is contained in:
parent
dca6785af8
commit
ce8924bb43
@ -78,3 +78,32 @@ def args_array_to_patch(op, attributes):
|
||||
else:
|
||||
raise exc.CommandError(_('Unknown PATCH operation: %s') % op)
|
||||
return patch
|
||||
|
||||
|
||||
def format_labels(lbls, parse_comma=True):
|
||||
'''Reformat labels into dict of format expected by the API.'''
|
||||
|
||||
if not lbls:
|
||||
return {}
|
||||
|
||||
if parse_comma:
|
||||
# expect multiple invocations of --labels but fall back
|
||||
# to , delimited if only one --labels is specified
|
||||
if len(lbls) == 1:
|
||||
lbls = lbls[0].split(',')
|
||||
|
||||
labels = {}
|
||||
for l in lbls:
|
||||
try:
|
||||
(k, v) = l.split(('='), 1)
|
||||
except ValueError:
|
||||
raise exc.CommandError(_('labels must be a list of KEY=VALUE '
|
||||
'not %s') % l)
|
||||
if k not in labels:
|
||||
labels[k] = v
|
||||
else:
|
||||
if not isinstance(labels[k], list):
|
||||
labels[k] = [labels[k]]
|
||||
labels[k].append(v)
|
||||
|
||||
return labels
|
||||
|
@ -215,6 +215,7 @@ class TestCommandLineArgument(utils.TestCase):
|
||||
'--flavor-id test_flavor '
|
||||
'--fixed-network public '
|
||||
'--network-driver test_driver '
|
||||
'--labels key=val '
|
||||
'--master-flavor-id test_flavor '
|
||||
'--docker-volume-size 10')
|
||||
self.assertTrue(mock_create.called)
|
||||
@ -314,6 +315,39 @@ class TestCommandLineArgument(utils.TestCase):
|
||||
'--image-id test_image '
|
||||
'--coe swarm '
|
||||
'--no-proxy no_proxy ')
|
||||
|
||||
@mock.patch('magnumclient.v1.baymodels.BayModelManager.create')
|
||||
def test_baymodel_create_labels_success(self, mock_create):
|
||||
self._test_arg_success('baymodel-create '
|
||||
'--name test '
|
||||
'--labels key=val '
|
||||
'--keypair-id test_keypair '
|
||||
'--external-network-id test_net '
|
||||
'--image-id test_image '
|
||||
'--coe swarm')
|
||||
self.assertTrue(mock_create.called)
|
||||
|
||||
@mock.patch('magnumclient.v1.baymodels.BayModelManager.create')
|
||||
def test_baymodel_create_separate_labels_success(self, mock_create):
|
||||
self._test_arg_success('baymodel-create '
|
||||
'--name test '
|
||||
'--labels key1=val1 '
|
||||
'--labels key2=val2 '
|
||||
'--keypair-id test_keypair '
|
||||
'--external-network-id test_net '
|
||||
'--image-id test_image '
|
||||
'--coe swarm')
|
||||
self.assertTrue(mock_create.called)
|
||||
|
||||
@mock.patch('magnumclient.v1.baymodels.BayModelManager.create')
|
||||
def test_baymodel_create_combined_labels_success(self, mock_create):
|
||||
self._test_arg_success('baymodel-create '
|
||||
'--name test '
|
||||
'--labels key1=val1,key2=val2 '
|
||||
'--keypair-id test_keypair '
|
||||
'--external-network-id test_net '
|
||||
'--image-id test_image '
|
||||
'--coe swarm')
|
||||
self.assertTrue(mock_create.called)
|
||||
|
||||
@mock.patch('magnumclient.v1.baymodels.BayModelManager.create')
|
||||
|
@ -95,3 +95,80 @@ class ArgsArrayToPatchTest(test_utils.BaseTestCase):
|
||||
my_args['attributes'])
|
||||
self.assertEqual([{'op': 'remove', 'path': '/foo'},
|
||||
{'op': 'remove', 'path': '/extra/bar'}], patch)
|
||||
|
||||
|
||||
class FormatLabelsTest(test_utils.BaseTestCase):
|
||||
|
||||
def test_format_label_none(self):
|
||||
self.assertEqual({}, utils.format_labels(None))
|
||||
|
||||
def test_format_labels(self):
|
||||
l = utils.format_labels([
|
||||
'K1=V1,K2=V2,'
|
||||
'K3=V3,K4=V4,'
|
||||
'K5=V5'])
|
||||
self.assertEqual({'K1': 'V1',
|
||||
'K2': 'V2',
|
||||
'K3': 'V3',
|
||||
'K4': 'V4',
|
||||
'K5': 'V5'
|
||||
}, l)
|
||||
|
||||
def test_format_labels_split(self):
|
||||
l = utils.format_labels([
|
||||
'K1=V1,'
|
||||
'K2=V22222222222222222222222222222'
|
||||
'222222222222222222222222222,'
|
||||
'K3=3.3.3.3'])
|
||||
self.assertEqual({'K1': 'V1',
|
||||
'K2': 'V22222222222222222222222222222'
|
||||
'222222222222222222222222222',
|
||||
'K3': '3.3.3.3'}, l)
|
||||
|
||||
def test_format_labels_multiple(self):
|
||||
l = utils.format_labels([
|
||||
'K1=V1',
|
||||
'K2=V22222222222222222222222222222'
|
||||
'222222222222222222222222222',
|
||||
'K3=3.3.3.3'])
|
||||
self.assertEqual({'K1': 'V1',
|
||||
'K2': 'V22222222222222222222222222222'
|
||||
'222222222222222222222222222',
|
||||
'K3': '3.3.3.3'}, l)
|
||||
|
||||
def test_format_labels_multiple_colon_values(self):
|
||||
l = utils.format_labels([
|
||||
'K1=V1',
|
||||
'K2=V2,V22,V222,V2222',
|
||||
'K3=3.3.3.3'])
|
||||
self.assertEqual({'K1': 'V1',
|
||||
'K2': 'V2,V22,V222,V2222',
|
||||
'K3': '3.3.3.3'}, l)
|
||||
|
||||
def test_format_labels_parse_comma_false(self):
|
||||
l = utils.format_labels(
|
||||
['K1=V1,K2=2.2.2.2,K=V'],
|
||||
parse_comma=False)
|
||||
self.assertEqual({'K1': 'V1,K2=2.2.2.2,K=V'}, l)
|
||||
|
||||
def test_format_labels_multiple_values_per_labels(self):
|
||||
l = utils.format_labels([
|
||||
'K1=V1',
|
||||
'K1=V2'])
|
||||
self.assertIn('K1', l)
|
||||
self.assertIn('V1', l['K1'])
|
||||
self.assertIn('V2', l['K1'])
|
||||
|
||||
def test_format_label_bad_label(self):
|
||||
labels = ['K1=V1,K22.2.2.2']
|
||||
ex = self.assertRaises(exc.CommandError,
|
||||
utils.format_labels, labels)
|
||||
self.assertEqual('labels must be a list of KEY=VALUE '
|
||||
'not K22.2.2.2', str(ex))
|
||||
|
||||
def test_format_multiple_bad_label(self):
|
||||
labels = ['K1=V1', 'K22.2.2.2']
|
||||
ex = self.assertRaises(exc.CommandError,
|
||||
utils.format_labels, labels)
|
||||
self.assertEqual('labels must be a list of KEY=VALUE '
|
||||
'not K22.2.2.2', str(ex))
|
||||
|
@ -44,6 +44,7 @@ BAYMODEL1 = {'id': 123,
|
||||
'http_proxy': 'http_proxy',
|
||||
'https_proxy': 'https_proxy',
|
||||
'no_proxy': 'no_proxy',
|
||||
'labels': 'key1=val1,key11=val11',
|
||||
}
|
||||
BAYMODEL2 = {'id': 124,
|
||||
'uuid': '66666666-7777-8888-9999-000000000002',
|
||||
@ -65,6 +66,7 @@ BAYMODEL2 = {'id': 124,
|
||||
'X8vjlQUnTK0HijrbSTLxp/9kazWWraBS0AyXe6'
|
||||
'Jv0Zio4VeFrfpytB8RtAAA test1234@magnum',
|
||||
'coe': 'kubernetes',
|
||||
'labels': 'key2=val2,key22=val22',
|
||||
}
|
||||
|
||||
CREATE_BAYMODEL = copy.deepcopy(BAYMODEL1)
|
||||
@ -153,6 +155,7 @@ class BayModelManagerTest(testtools.TestCase):
|
||||
self.assertEqual(BAYMODEL1['https_proxy'], baymodel.https_proxy)
|
||||
self.assertEqual(BAYMODEL1['no_proxy'], baymodel.no_proxy)
|
||||
self.assertEqual(BAYMODEL1['network_driver'], baymodel.network_driver)
|
||||
self.assertEqual(BAYMODEL1['labels'], baymodel.labels)
|
||||
|
||||
def test_baymodel_show_by_name(self):
|
||||
baymodel = self.mgr.get(BAYMODEL1['name'])
|
||||
@ -172,6 +175,7 @@ class BayModelManagerTest(testtools.TestCase):
|
||||
self.assertEqual(BAYMODEL1['https_proxy'], baymodel.https_proxy)
|
||||
self.assertEqual(BAYMODEL1['no_proxy'], baymodel.no_proxy)
|
||||
self.assertEqual(BAYMODEL1['network_driver'], baymodel.network_driver)
|
||||
self.assertEqual(BAYMODEL1['labels'], baymodel.labels)
|
||||
|
||||
def test_baymodel_create(self):
|
||||
baymodel = self.mgr.create(**CREATE_BAYMODEL)
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
import mock
|
||||
|
||||
from magnumclient.common import utils as magnum_utils
|
||||
from magnumclient.tests import base
|
||||
from magnumclient.v1 import shell
|
||||
|
||||
@ -190,6 +191,8 @@ class ShellTest(base.TestCase):
|
||||
args.https_proxy = 'https_proxy'
|
||||
no_proxy = 'no_proxy'
|
||||
args.no_proxy = no_proxy
|
||||
labels = ['key1=val1']
|
||||
args.labels = labels
|
||||
|
||||
shell.do_baymodel_create(client_mock, args)
|
||||
client_mock.baymodels.create.assert_called_once_with(
|
||||
@ -200,7 +203,8 @@ class ShellTest(base.TestCase):
|
||||
fixed_network=fixed_network, dns_nameserver=dns_nameserver,
|
||||
ssh_authorized_key=ssh_authorized_key, coe=coe,
|
||||
http_proxy=http_proxy, https_proxy=https_proxy,
|
||||
no_proxy=no_proxy, network_driver=network_driver)
|
||||
no_proxy=no_proxy, network_driver=network_driver,
|
||||
labels=magnum_utils.format_labels(labels))
|
||||
|
||||
def test_do_baymodel_delete(self):
|
||||
client_mock = mock.MagicMock()
|
||||
@ -297,10 +301,10 @@ class ShellTest(base.TestCase):
|
||||
args.pod = pod_id
|
||||
op = 'add'
|
||||
args.op = op
|
||||
attributes = "label={'name': 'value'}"
|
||||
attributes = "labels={'name': 'value'}"
|
||||
args.attributes = attributes
|
||||
shell.magnum_utils.args_array_to_patch = mock.MagicMock()
|
||||
patch = [{'path': '/label', 'value': {'name': 'value'}, 'op': 'add'}]
|
||||
patch = [{'path': '/labels', 'value': {'name': 'value'}, 'op': 'add'}]
|
||||
shell.magnum_utils.args_array_to_patch.return_value = patch
|
||||
|
||||
shell.do_pod_update(client_mock, args)
|
||||
|
@ -17,7 +17,7 @@ from magnumclient import exceptions
|
||||
|
||||
CREATION_ATTRIBUTES = ['name', 'image_id', 'flavor_id', 'master_flavor_id',
|
||||
'keypair_id', 'external_network_id', 'fixed_network',
|
||||
'dns_nameserver', 'docker_volume_size',
|
||||
'dns_nameserver', 'docker_volume_size', 'labels',
|
||||
'ssh_authorized_key', 'coe', 'http_proxy',
|
||||
'https_proxy', 'no_proxy', 'network_driver']
|
||||
|
||||
|
@ -200,6 +200,11 @@ def do_bay_update(cs, args):
|
||||
@utils.arg('--no-proxy',
|
||||
metavar='<no-proxy>',
|
||||
help='The no_proxy address to use for nodes in bay.')
|
||||
@utils.arg('--labels', metavar='<KEY1=VALUE1,KEY2=VALUE2...>',
|
||||
action='append', default=[],
|
||||
help='Arbitrary labels in the form of key=value pairs '
|
||||
'to associate with a baymodel. '
|
||||
'May be used multiple times.')
|
||||
def do_baymodel_create(cs, args):
|
||||
"""Create a baymodel."""
|
||||
opts = {}
|
||||
@ -218,6 +223,7 @@ def do_baymodel_create(cs, args):
|
||||
opts['http_proxy'] = args.http_proxy
|
||||
opts['https_proxy'] = args.https_proxy
|
||||
opts['no_proxy'] = args.no_proxy
|
||||
opts['labels'] = magnum_utils.format_labels(args.labels)
|
||||
|
||||
baymodel = cs.baymodels.create(**opts)
|
||||
_show_baymodel(baymodel)
|
||||
|
Loading…
Reference in New Issue
Block a user