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:
Daneyon Hansen 2015-08-20 17:22:49 +00:00
parent dca6785af8
commit ce8924bb43
7 changed files with 158 additions and 4 deletions

View File

@ -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

View File

@ -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')

View File

@ -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))

View File

@ -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)

View File

@ -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)

View File

@ -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']

View File

@ -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)