Add overwrite option to host-label-assign
Add an overwrite option to host-label-assign. The behavior will be consistent with kubectl label --overwrite. If a label key exists for the host and no overwrite option is given the operation will be rejected. With the overwrite option provided any existing keys will be updated to the new value. Change-Id: If32dfe191f002f991d8357c05ca4ed0ca4308fb6 Closes-Bug: 1850168 Signed-off-by: David Sullivan <david.sullivan@windriver.com>
This commit is contained in:
parent
455fc28f6d
commit
b2e4d43f45
|
@ -8,6 +8,7 @@
|
|||
#
|
||||
|
||||
from cgtsclient.common import base
|
||||
from cgtsclient.v1 import options
|
||||
|
||||
|
||||
class KubernetesLabel(base.Resource):
|
||||
|
@ -34,8 +35,8 @@ class KubernetesLabelManager(base.Manager):
|
|||
except IndexError:
|
||||
return None
|
||||
|
||||
def assign(self, host_uuid, label):
|
||||
return self._create(self._path(host_uuid), label)
|
||||
def assign(self, host_uuid, label, parameters=None):
|
||||
return self._create(options.build_url(self._path(host_uuid), q=None, params=parameters), label)
|
||||
|
||||
def remove(self, uuid):
|
||||
return self._delete(self._path(uuid))
|
||||
|
|
|
@ -43,11 +43,15 @@ def do_host_label_list(cc, args):
|
|||
action='append',
|
||||
default=[],
|
||||
help="List of Kubernetes labels")
|
||||
@utils.arg('--overwrite',
|
||||
action='store_true',
|
||||
help="Allow labels to be overwritten")
|
||||
def do_host_label_assign(cc, args):
|
||||
"""Update the Kubernetes labels on a host."""
|
||||
attributes = utils.extract_keypairs(args)
|
||||
ihost = ihost_utils._find_ihost(cc, args.hostnameorid)
|
||||
new_labels = cc.label.assign(ihost.uuid, attributes)
|
||||
parameters = ["overwrite=" + str(args.overwrite)]
|
||||
new_labels = cc.label.assign(ihost.uuid, attributes, parameters)
|
||||
for p in new_labels.labels:
|
||||
uuid = p['uuid']
|
||||
if uuid is not None:
|
||||
|
|
|
@ -189,9 +189,9 @@ class LabelController(rest.RestController):
|
|||
return Label.convert_with_links(sp_label)
|
||||
|
||||
@cutils.synchronized(LOCK_NAME)
|
||||
@wsme_pecan.wsexpose(LabelCollection, types.uuid,
|
||||
@wsme_pecan.wsexpose(LabelCollection, types.uuid, types.boolean,
|
||||
body=types.apidict)
|
||||
def post(self, uuid, body):
|
||||
def post(self, uuid, overwrite=False, body=None):
|
||||
"""Assign label(s) to a host.
|
||||
"""
|
||||
if self._from_ihosts:
|
||||
|
@ -204,6 +204,21 @@ class LabelController(rest.RestController):
|
|||
|
||||
_semantic_check_worker_labels(body)
|
||||
|
||||
existing_labels = {}
|
||||
for label_key in body.keys():
|
||||
label = None
|
||||
try:
|
||||
label = pecan.request.dbapi.label_query(host.id, label_key)
|
||||
except exception.HostLabelNotFoundByKey:
|
||||
pass
|
||||
if label:
|
||||
if overwrite:
|
||||
existing_labels.update({label_key: label.uuid})
|
||||
else:
|
||||
raise wsme.exc.ClientSideError(
|
||||
_("Label %s exists for host %s. Use overwrite option to assign a new value." % (
|
||||
label_key, host.hostname)))
|
||||
|
||||
try:
|
||||
pecan.request.rpcapi.update_kubernetes_label(
|
||||
pecan.request.context,
|
||||
|
@ -223,12 +238,17 @@ class LabelController(rest.RestController):
|
|||
'label_key': key,
|
||||
'label_value': value
|
||||
}
|
||||
|
||||
try:
|
||||
new_label = pecan.request.dbapi.label_create(uuid, values)
|
||||
if existing_labels.get(key, None):
|
||||
# Update the value
|
||||
label_uuid = existing_labels.get(key)
|
||||
new_label = pecan.request.dbapi.label_update(label_uuid, {'label_value': value})
|
||||
else:
|
||||
new_label = pecan.request.dbapi.label_create(uuid, values)
|
||||
new_records.append(new_label)
|
||||
except exception.HostLabelAlreadyExists:
|
||||
pass
|
||||
# We should not be here
|
||||
raise wsme.exc.ClientSideError(_("Error creating label %s") % label_key)
|
||||
|
||||
try:
|
||||
vim_api.vim_host_update(
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import platform
|
||||
|
||||
from six.moves import http_client
|
||||
|
||||
from sysinv.common import constants
|
||||
|
@ -10,6 +12,11 @@ from sysinv.db import api as dbapi
|
|||
from sysinv.tests.api import base
|
||||
from sysinv.tests.db import utils as dbutils
|
||||
|
||||
if platform.python_version().startswith('2.7'):
|
||||
from urllib import urlencode
|
||||
else:
|
||||
from urllib.parse import urlencode
|
||||
|
||||
HEADER = {'User-Agent': 'sysinv'}
|
||||
|
||||
|
||||
|
@ -41,55 +48,112 @@ class LabelTestCase(base.FunctionalTest):
|
|||
invprovision=constants.PROVISIONED,
|
||||
)
|
||||
|
||||
def _get_path(self, path=None):
|
||||
if path:
|
||||
return '/labels/' + path
|
||||
def _get_path(self, host=None, params=None):
|
||||
if host:
|
||||
path = '/labels/' + host
|
||||
else:
|
||||
return '/labels'
|
||||
path = '/labels'
|
||||
|
||||
if params:
|
||||
path += '?' + urlencode(params)
|
||||
return path
|
||||
|
||||
|
||||
class LabelAssignTestCase(LabelTestCase):
|
||||
def setUp(self):
|
||||
super(LabelAssignTestCase, self).setUp()
|
||||
|
||||
generic_labels = {
|
||||
'apps': 'enabled',
|
||||
'foo': 'bar'
|
||||
}
|
||||
|
||||
def validate_labels(self, input_data, response_data):
|
||||
self.assertEqual(len(input_data), len(response_data))
|
||||
for label in response_data:
|
||||
label_key = label["label_key"]
|
||||
label_value = label["label_value"]
|
||||
self.assertIn(label_key, input_data.keys())
|
||||
self.assertEqual(label_value, input_data[label_key])
|
||||
|
||||
def assign_labels(self, host_uuid, input_data, parameters=None):
|
||||
response = self.post_json('%s' % self._get_path(host_uuid, parameters), input_data)
|
||||
self.assertEqual(http_client.OK, response.status_int)
|
||||
return response
|
||||
|
||||
def assign_labels_failure(self, host_uuid, input_data, parameters=None):
|
||||
response = self.post_json('%s' % self._get_path(host_uuid, parameters), input_data, expect_errors=True)
|
||||
self.assertEqual(response.content_type, 'application/json')
|
||||
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
def get_host_labels(self, host_uuid):
|
||||
response = self.get_json("/ihosts/%s/labels" % host_uuid)
|
||||
return response['labels']
|
||||
|
||||
def test_create_labels(self):
|
||||
host_uuid = self.worker.uuid
|
||||
input_data = self.generic_labels
|
||||
self.assign_labels(host_uuid, input_data)
|
||||
response_data = self.get_host_labels(host_uuid)
|
||||
self.validate_labels(input_data, response_data)
|
||||
|
||||
def test_overwrite_labels_success(self):
|
||||
host_uuid = self.worker.uuid
|
||||
input_data = self.generic_labels
|
||||
self.assign_labels(host_uuid, input_data)
|
||||
|
||||
new_input_values = {
|
||||
'apps': 'disabled',
|
||||
'foo': 'free'
|
||||
}
|
||||
self.assign_labels(host_uuid, new_input_values, parameters={'overwrite': True})
|
||||
response_data = self.get_host_labels(host_uuid)
|
||||
self.validate_labels(new_input_values, response_data)
|
||||
|
||||
def test_overwrite_labels_failure(self):
|
||||
host_uuid = self.worker.uuid
|
||||
input_data = self.generic_labels
|
||||
self.assign_labels(host_uuid, input_data)
|
||||
|
||||
new_input_values = {
|
||||
'apps': 'disabled',
|
||||
'foo': 'free'
|
||||
}
|
||||
# Default value should be overwrite=False
|
||||
self.assign_labels_failure(host_uuid, new_input_values)
|
||||
# Test explicit overwrite=False
|
||||
self.assign_labels_failure(host_uuid, new_input_values, parameters={'overwrite': False})
|
||||
|
||||
# Labels should be unchanged from initial values
|
||||
response_data = self.get_host_labels(host_uuid)
|
||||
self.validate_labels(input_data, response_data)
|
||||
|
||||
def test_create_validated_labels_success(self):
|
||||
host_uuid = self.worker.uuid
|
||||
cpu_mgr_label = {
|
||||
'kube-cpu-mgr-policy': 'static',
|
||||
}
|
||||
response = self.post_json('%s' % self._get_path(host_uuid), cpu_mgr_label)
|
||||
self.assertEqual(http_client.OK, response.status_int)
|
||||
self.assign_labels(host_uuid, cpu_mgr_label)
|
||||
topology_mgr_label = {
|
||||
'kube-topology-mgr-policy': 'restricted',
|
||||
}
|
||||
response = self.post_json('%s' % self._get_path(host_uuid), topology_mgr_label)
|
||||
self.assertEqual(http_client.OK, response.status_int)
|
||||
self.assign_labels(host_uuid, topology_mgr_label)
|
||||
|
||||
response = self.get_json("/ihosts/%s/labels" % host_uuid)
|
||||
labels = response['labels']
|
||||
self.assertEqual(2, len(labels))
|
||||
input_data = {}
|
||||
for input_label in [cpu_mgr_label, topology_mgr_label]:
|
||||
input_data.update(input_label)
|
||||
for label in labels:
|
||||
label_key = label["label_key"]
|
||||
label_value = label["label_value"]
|
||||
self.assertIn(label_key, input_data.keys())
|
||||
self.assertEqual(label_value, input_data[label_key])
|
||||
|
||||
response_data = self.get_host_labels(host_uuid)
|
||||
self.validate_labels(input_data, response_data)
|
||||
|
||||
def test_create_validated_labels_failure(self):
|
||||
host_uuid = self.worker.uuid
|
||||
cpu_mgr_label = {
|
||||
'kube-cpu-mgr-policy': 'invalid',
|
||||
}
|
||||
response = self.post_json('%s' % self._get_path(host_uuid), cpu_mgr_label, expect_errors=True)
|
||||
self.assertEqual(response.content_type, 'application/json')
|
||||
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
self.assign_labels_failure(host_uuid, cpu_mgr_label)
|
||||
topology_mgr_label = {
|
||||
'kube-topology-mgr-policy': 'invalid',
|
||||
}
|
||||
response = self.post_json('%s' % self._get_path(host_uuid), topology_mgr_label, expect_errors=True)
|
||||
self.assertEqual(response.content_type, 'application/json')
|
||||
self.assertEqual(http_client.BAD_REQUEST, response.status_int)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
self.assign_labels_failure(host_uuid, topology_mgr_label)
|
||||
|
|
Loading…
Reference in New Issue