Adds labels endpoints
- labels get/put/delete endpoint Fixes Bug: 1612141 Change-Id: I21a2421b2e992827a602f3ab04b5c14a44014f96
This commit is contained in:
parent
014377ab63
commit
a27ad0899f
|
@ -77,3 +77,32 @@ class HostsData(base.Resource):
|
|||
context = request.environ.get('context')
|
||||
dbapi.hosts_data_delete(context, id, request.json)
|
||||
return None, 204, None
|
||||
|
||||
|
||||
class HostsLabels(base.Resource):
|
||||
|
||||
@base.http_codes
|
||||
def get(self, id):
|
||||
"""Get labels for given host device."""
|
||||
context = request.environ.get('context')
|
||||
host_obj = dbapi.hosts_get_by_id(context, id)
|
||||
response = {"labels": list(host_obj.labels)}
|
||||
return response, 200, None
|
||||
|
||||
@base.http_codes
|
||||
def put(self, id):
|
||||
"""
|
||||
Update existing device label entirely, or add if it does
|
||||
not exist.
|
||||
"""
|
||||
context = request.environ.get('context')
|
||||
resp = dbapi.hosts_labels_update(context, id, request.json)
|
||||
response = {"labels": list(resp.labels)}
|
||||
return response, 200, None
|
||||
|
||||
@base.http_codes
|
||||
def delete(self, id):
|
||||
"""Delete device label entirely."""
|
||||
context = request.environ.get('context')
|
||||
dbapi.hosts_labels_delete(context, id, request.json)
|
||||
return None, 204, None
|
||||
|
|
|
@ -105,6 +105,33 @@ class NetDeviceById(base.Resource):
|
|||
return None, 204, None
|
||||
|
||||
|
||||
class NetDeviceLabels(base.Resource):
|
||||
"""Controller for Netowrk Device Labels."""
|
||||
|
||||
@base.http_codes
|
||||
def get(self, id):
|
||||
"""Get labels for given network device."""
|
||||
context = request.environ.get('context')
|
||||
obj = dbapi.netdevices_get_by_id(context, id)
|
||||
response = {"labels": list(obj.labels)}
|
||||
return response, 200, None
|
||||
|
||||
@base.http_codes
|
||||
def put(self, id):
|
||||
"""Update existing device label. Adds if it does not exist."""
|
||||
context = request.environ.get('context')
|
||||
resp = dbapi.netdevices_labels_update(context, id, request.json)
|
||||
response = {"labels": list(resp.labels)}
|
||||
return response, 200, None
|
||||
|
||||
@base.http_codes
|
||||
def delete(self, id):
|
||||
"""Delete device label(s)."""
|
||||
context = request.environ.get('context')
|
||||
dbapi.netdevices_labels_delete(context, id)
|
||||
return None, 204, None
|
||||
|
||||
|
||||
class NetInterfaces(base.Resource):
|
||||
"""Controller for Netowrk Interfaces."""
|
||||
|
||||
|
|
|
@ -15,6 +15,9 @@ routes = [
|
|||
dict(resource=hosts.HostsData,
|
||||
urls=['/hosts/<id>/data'],
|
||||
endpoint='hosts_data'),
|
||||
dict(resource=hosts.HostsLabels,
|
||||
urls=['/hosts/<id>/labels'],
|
||||
endpoint='hosts_labels'),
|
||||
dict(resource=hosts.HostById,
|
||||
urls=['/hosts/<id>'],
|
||||
endpoint='hosts_id'),
|
||||
|
@ -69,4 +72,7 @@ routes = [
|
|||
dict(resource=networks.NetDeviceById,
|
||||
urls=['/netdevices/<id>'],
|
||||
endpoint='netdevices_id'),
|
||||
dict(resource=networks.NetDeviceLabels,
|
||||
urls=['/netdevices/<id>/labels'],
|
||||
endpoint='netdevices_labels'),
|
||||
]
|
||||
|
|
|
@ -20,7 +20,8 @@ DefinitionsHost = {'discriminator': 'name',
|
|||
'description': 'Parent Id of this host'},
|
||||
'device_type': {'type': 'string',
|
||||
'description': 'Type of host'},
|
||||
'labels': {'type': 'allOf',
|
||||
'labels': {'type': 'array',
|
||||
'items': 'string',
|
||||
'description': 'User defined labels'},
|
||||
'data': {'type': 'allOf',
|
||||
'description': 'User defined information'},
|
||||
|
@ -36,7 +37,8 @@ DefinitionsHostId = {'discriminator': 'name',
|
|||
'id': {'type': 'integer'},
|
||||
'cell_id': {'type': 'integer'},
|
||||
'project_id': {'type': 'integer'},
|
||||
'labels': {'type': 'allOf',
|
||||
'labels': {'type': 'array',
|
||||
'items': 'string',
|
||||
'description': 'User defined labels'},
|
||||
'data': {'type': 'allOf',
|
||||
'description': 'User defined information'},
|
||||
|
@ -76,6 +78,11 @@ DefinitionsData = {'type': 'object',
|
|||
'properties': {'key': {'type': 'string'},
|
||||
'value': {'type': 'object'}}}
|
||||
|
||||
DefinitionsLabel = {'type': 'object',
|
||||
'properties': {'labels': {
|
||||
'type': 'array',
|
||||
'items': {'type': 'string'}}}}
|
||||
|
||||
DefinitionsError = {'type': 'object',
|
||||
'properties': {'fields': {'type': 'string'},
|
||||
'message': {'type': 'string'},
|
||||
|
@ -284,6 +291,7 @@ validators = {
|
|||
'description': 'Cell id to generate inventory for'}}}
|
||||
},
|
||||
('hosts_id_data', 'PUT'): {'json': DefinitionsData},
|
||||
('hosts_labels', 'PUT'): {'json': DefinitionsLabel},
|
||||
('hosts_id', 'GET'): {
|
||||
'args': {'required': [],
|
||||
'properties': {
|
||||
|
@ -426,6 +434,7 @@ validators = {
|
|||
'default': True,
|
||||
'type': 'boolean'}}}},
|
||||
('netdevices', 'POST'): {'json': DefinitionNetDevice},
|
||||
('netdevices_labels', 'PUT'): {'json': DefinitionsLabel},
|
||||
('net_interfaces', 'GET'): {
|
||||
'args': {'required': ['device_id'],
|
||||
'properties': {
|
||||
|
@ -502,6 +511,16 @@ filters = {
|
|||
400: {'headers': None, 'schema': None},
|
||||
404: {'headers': None, 'schema': None},
|
||||
405: {'headers': None, 'schema': None}},
|
||||
('hosts_labels', 'GET'):
|
||||
{200: {'headers': None, 'schema': DefinitionsLabel},
|
||||
400: {'headers': None, 'schema': None},
|
||||
404: {'headers': None, 'schema': None},
|
||||
405: {'headers': None, 'schema': None}},
|
||||
('hosts_labels', 'PUT'):
|
||||
{200: {'headers': None, 'schema': DefinitionsLabel},
|
||||
400: {'headers': None, 'schema': None},
|
||||
404: {'headers': None, 'schema': None},
|
||||
405: {'headers': None, 'schema': None}},
|
||||
('hosts', 'POST'):
|
||||
{200: {'headers': None, 'schema': DefinitionsHost},
|
||||
400: {'headers': None, 'schema': None},
|
||||
|
@ -625,6 +644,16 @@ filters = {
|
|||
400: {'headers': None, 'schema': None},
|
||||
404: {'headers': None, 'schema': None},
|
||||
405: {'headers': None, 'schema': None}},
|
||||
('netdevices_labels', 'GET'):
|
||||
{200: {'headers': None, 'schema': DefinitionsLabel},
|
||||
400: {'headers': None, 'schema': None},
|
||||
404: {'headers': None, 'schema': None},
|
||||
405: {'headers': None, 'schema': None}},
|
||||
('netdevices_labels', 'PUT'):
|
||||
{200: {'headers': None, 'schema': DefinitionsLabel},
|
||||
400: {'headers': None, 'schema': None},
|
||||
404: {'headers': None, 'schema': None},
|
||||
405: {'headers': None, 'schema': None}},
|
||||
('networks', 'GET'):
|
||||
{200: {'headers': None,
|
||||
'schema': {'items': DefinitionNetwork, 'type': 'array'}},
|
||||
|
|
|
@ -187,6 +187,16 @@ def hosts_data_delete(context, host_id, data_key):
|
|||
return IMPL.hosts_data_delete(context, host_id, data_key)
|
||||
|
||||
|
||||
def hosts_labels_delete(context, host_id, labels):
|
||||
"""Delete existing device label(s)."""
|
||||
return IMPL.hosts_labels_delete(context, host_id, labels)
|
||||
|
||||
|
||||
def hosts_labels_update(context, host_id, labels):
|
||||
"""Update existing device label entirely."""
|
||||
return IMPL.hosts_labels_update(context, host_id, labels)
|
||||
|
||||
|
||||
# Projects
|
||||
|
||||
def projects_get_all(context):
|
||||
|
@ -283,6 +293,16 @@ def netdevices_delete(context, netdevice_id):
|
|||
return IMPL.netdevices_delete(context, netdevice_id)
|
||||
|
||||
|
||||
def netdevices_labels_delete(context, netdevice_id, labels):
|
||||
"""Delete network device labels."""
|
||||
return IMPL.netdevices_labels_delete(context, netdevice_id, labels)
|
||||
|
||||
|
||||
def netdevices_labels_update(context, netdevice_id, labels):
|
||||
"""Update network device labels."""
|
||||
return IMPL.netdevices_labels_update(context, netdevice_id, labels)
|
||||
|
||||
|
||||
def net_interfaces_get_by_device(context, device_id, filters):
|
||||
"""Get all network interfaces for the given device."""
|
||||
return IMPL.net_interfaces_get_by_device(context, device_id, filters)
|
||||
|
|
|
@ -115,6 +115,47 @@ def get_user_info(context, username):
|
|||
raise exceptions.UnknownException(message=err)
|
||||
|
||||
|
||||
def _device_labels_update(context, device_type, device_id, labels):
|
||||
"""Update labels for the given device. Add the label if it is not present
|
||||
in host labels list, otherwise do nothing."""
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
devices = with_polymorphic(models.Device, '*')
|
||||
query = model_query(context, devices, session=session,
|
||||
project_only=True)
|
||||
query = query.filter_by(type=device_type)
|
||||
query = query.filter_by(id=device_id)
|
||||
try:
|
||||
device = query.one()
|
||||
except sa_exc.NoResultFound:
|
||||
raise exceptions.NotFound()
|
||||
|
||||
device.labels.update(labels["labels"])
|
||||
device.save(session)
|
||||
return device
|
||||
|
||||
|
||||
def _device_labels_delete(context, device_type, device_id, labels):
|
||||
"""Delete labels from the device labels list if it matches
|
||||
the given label in the query, otherwise do nothing."""
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
devices = with_polymorphic(models.Device, '*')
|
||||
query = model_query(context, devices, session=session,
|
||||
project_only=True)
|
||||
query = query.filter_by(type=device_type)
|
||||
query = query.filter_by(id=device_id)
|
||||
try:
|
||||
device = query.one()
|
||||
except sa_exc.NoResultFound:
|
||||
raise exceptions.NotFound()
|
||||
|
||||
for label in labels["labels"]:
|
||||
device.labels.discard(label)
|
||||
device.save(session)
|
||||
return device
|
||||
|
||||
|
||||
def cells_get_all(context, region):
|
||||
"""Get all cells."""
|
||||
query = model_query(context, models.Cell, project_only=True)
|
||||
|
@ -454,6 +495,18 @@ def hosts_data_delete(context, host_id, data):
|
|||
return host_ref
|
||||
|
||||
|
||||
def hosts_labels_update(context, host_id, labels):
|
||||
"""Update labels for host. Add the label if it is not present
|
||||
in host labels list, otherwise do nothing."""
|
||||
return _device_labels_update(context, 'hosts', host_id, labels)
|
||||
|
||||
|
||||
def hosts_labels_delete(context, host_id, labels):
|
||||
"""Delete labels from the host labels list if it matches
|
||||
the given label in the query, otherwise do nothing."""
|
||||
return _device_labels_delete(context, 'hosts', host_id, labels)
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def projects_get_all(context):
|
||||
"""Get all the projects."""
|
||||
|
@ -674,6 +727,18 @@ def netdevices_delete(context, netdevice_id):
|
|||
query.delete()
|
||||
|
||||
|
||||
def netdevices_labels_update(context, device_id, labels):
|
||||
"""Update labels for a network device. Add the label if it is not present
|
||||
in host labels list, otherwise do nothing."""
|
||||
return _device_labels_update(context, 'net_devices', device_id, labels)
|
||||
|
||||
|
||||
def netdevices_labels_delete(context, device_id, labels):
|
||||
"""Delete labels from the network device labels list if it matches
|
||||
the given label in the query, otherwise do nothing."""
|
||||
return _device_labels_delete(context, 'net_devices', device_id, labels)
|
||||
|
||||
|
||||
def net_interfaces_get_by_device(context, device_id, filters):
|
||||
"""Get all network interfaces for the given host."""
|
||||
query = model_query(context, models.NetInterface, project_only=True)
|
||||
|
|
|
@ -115,3 +115,25 @@ class HostsDBTestCase(base.DBTestCase):
|
|||
host = dbapi.hosts_get_by_id(self.context, host_id)
|
||||
self.assertEqual(host.name, 'www.example.xyz')
|
||||
self.assertEqual(host.resolved, {'bar': 'bar2', 'foo': 'R1'})
|
||||
|
||||
def test_host_labels_create(self):
|
||||
region_id = self.make_region('region_1', foo='R1')
|
||||
host_id = self.make_host(region_id, 'www.example.xyz',
|
||||
IPAddress(u'10.1.2.101'),
|
||||
'server', bar='bar2')
|
||||
labels = {"labels": ["tom", "jerry"]}
|
||||
dbapi.hosts_labels_update(self.context, host_id, labels)
|
||||
|
||||
def test_host_labels_delete(self):
|
||||
region_id = self.make_region('region_1', foo='R1')
|
||||
host_id = self.make_host(region_id, 'www.example.xyz',
|
||||
IPAddress(u'10.1.2.101'),
|
||||
'server', bar='bar2')
|
||||
_labels = {"labels": ["tom", "jerry", "jones"]}
|
||||
dbapi.hosts_labels_update(self.context, host_id, _labels)
|
||||
host = dbapi.hosts_get_by_id(self.context, host_id)
|
||||
self.assertEqual(sorted(host.labels), sorted(_labels["labels"]))
|
||||
_dlabels = {"labels": ["tom"]}
|
||||
dbapi.hosts_labels_delete(self.context, host_id, _dlabels)
|
||||
host = dbapi.hosts_get_by_id(self.context, host_id)
|
||||
self.assertEqual(host.labels, {"jerry", "jones"})
|
||||
|
|
|
@ -108,6 +108,22 @@ class NetworkDevicesDBTestCase(base.DBTestCase):
|
|||
self.assertRaises(exceptions.NotFound, dbapi.netdevices_get_by_id,
|
||||
self.context, res.id)
|
||||
|
||||
def test_netdevice_labels_create(self):
|
||||
device = dbapi.netdevices_create(self.context, device1)
|
||||
labels = {"labels": ["tom", "jerry"]}
|
||||
dbapi.netdevices_labels_update(self.context, device.id, labels)
|
||||
|
||||
def test_netdevice_labels_delete(self):
|
||||
device = dbapi.netdevices_create(self.context, device1)
|
||||
_labels = {"labels": ["tom", "jerry"]}
|
||||
dbapi.netdevices_labels_update(self.context, device.id, _labels)
|
||||
ndevice = dbapi.netdevices_get_by_id(self.context, device.id)
|
||||
self.assertEqual(sorted(ndevice.labels), sorted(_labels["labels"]))
|
||||
_dlabels = {"labels": ["tom"]}
|
||||
dbapi.netdevices_labels_delete(self.context, ndevice.id, _dlabels)
|
||||
ndevice = dbapi.netdevices_get_by_id(self.context, ndevice.id)
|
||||
self.assertEqual(ndevice.labels, {"jerry"})
|
||||
|
||||
|
||||
class NetworkInterfacesDBTestCase(base.DBTestCase):
|
||||
|
||||
|
|
|
@ -77,6 +77,8 @@ HOST2 = Host("www.example.com", "1", "1", "192.168.1.2", "server",
|
|||
{"key1": "value1", "key2": "value2"})
|
||||
HOST3 = Host("www.example.net", "1", "2", "10.10.0.1", "server",
|
||||
{"key1": "value1", "key2": "value2"})
|
||||
HOST4 = Host("www.example.net", "1", "2", "10.10.0.1", "server",
|
||||
{"key1": "value1", "key2": "value2"}, labels=["a", "b"])
|
||||
HOSTS_LIST_R1 = [HOST1, HOST2]
|
||||
HOSTS_LIST_R2 = [HOST3]
|
||||
|
||||
|
|
|
@ -35,6 +35,14 @@ class APIV1Test(TestCase):
|
|||
resp.json = jsonutils.loads(resp.data.decode('utf-8'))
|
||||
return resp
|
||||
|
||||
def put(self, path, data, **kw):
|
||||
content = jsonutils.dumps(data)
|
||||
content_type = 'application/json'
|
||||
resp = self.client.put(path=path, content_type=content_type,
|
||||
data=content)
|
||||
resp.json = jsonutils.loads(resp.data.decode('utf-8'))
|
||||
return resp
|
||||
|
||||
def delete(self, path):
|
||||
resp = self.client.delete(path=path)
|
||||
return resp
|
||||
|
@ -216,6 +224,20 @@ class APIV1HostsIDTest(APIV1Test):
|
|||
resp = self.get('v1/hosts/1?resolved-values=false')
|
||||
self.assertEqual(resp.json["data"], expected)
|
||||
|
||||
@mock.patch.object(dbapi, 'hosts_get_by_id')
|
||||
def test_get_hosts_labels(self, mock_host):
|
||||
mock_host.return_value = fake_resources.HOST4
|
||||
resp = self.get('v1/hosts/1/labels')
|
||||
self.assertEqual(resp.json["labels"], ["a", "b"])
|
||||
|
||||
@mock.patch.object(dbapi, 'hosts_labels_update')
|
||||
def test_put_hosts_labels(self, mock_host):
|
||||
payload = {"labels": ["a", "b"]}
|
||||
mock_host.return_value = fake_resources.HOST4
|
||||
resp = self.put('v1/hosts/1/labels', data=payload)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assertEqual(resp.json, payload)
|
||||
|
||||
|
||||
class APIV1HostsTest(APIV1Test):
|
||||
@mock.patch.object(dbapi, 'hosts_get_by_region')
|
||||
|
|
Loading…
Reference in New Issue