Added server tags support in nova-api
Added new API microversion which allows the following: - add tag to the server - replace set of server tags with new set of tags - get information about server, including list of tags for server - get just list of tags for server - check if tag exists on a server - remove specified tag from server - remove all tags from server - search servers by tags DocImpact APIImpact Implements: blueprint tag-instances Change-Id: I9573aa52aae9f49945d8806ca5e52ada29fb087a
This commit is contained in:
parent
bfe8e7484d
commit
537df23d85
@ -0,0 +1,3 @@
|
||||
{
|
||||
"tags": ["sometag"]
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
{
|
||||
"tags": [
|
||||
"sometag"
|
||||
]
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
{
|
||||
"server": {
|
||||
"tags": [
|
||||
"sometag"
|
||||
],
|
||||
"accessIPv4": "1.2.3.4",
|
||||
"accessIPv6": "80fe::",
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"addr": "192.168.0.3",
|
||||
"OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff",
|
||||
"OS-EXT-IPS:type": "fixed",
|
||||
"version": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"created": "2012-12-02T02:11:55Z",
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostId": "c949ab4256cea23b6089b710aa2df48bf6577ed915278b62e33ad8bb",
|
||||
"id": "5046e2f2-3b33-4041-b3cf-e085f73e78e7",
|
||||
"image": {
|
||||
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/5046e2f2-3b33-4041-b3cf-e085f73e78e7",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/5046e2f2-3b33-4041-b3cf-e085f73e78e7",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"My Server Name": "Apache1"
|
||||
},
|
||||
"name": "new-server-test",
|
||||
"progress": 0,
|
||||
"status": "ACTIVE",
|
||||
"tenant_id": "6f70656e737461636b20342065766572",
|
||||
"updated": "2012-12-02T02:11:55Z",
|
||||
"key_name": null,
|
||||
"user_id": "fake",
|
||||
"locked": false,
|
||||
"description": null
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
{
|
||||
"servers": [
|
||||
{
|
||||
"accessIPv4": "1.2.3.4",
|
||||
"accessIPv6": "80fe::",
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"addr": "192.168.0.3",
|
||||
"OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff",
|
||||
"OS-EXT-IPS:type": "fixed",
|
||||
"version": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"created": "2013-09-03T04:01:32Z",
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostId": "bcf92836fc9ed4203a75cb0337afc7f917d2be504164b995c2334b25",
|
||||
"id": "f5dc173b-6804-445a-a6d8-c705dad5b5eb",
|
||||
"image": {
|
||||
"id": "70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/images/70a599e0-31e7-49b7-b260-868f441e862b",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"key_name": null,
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v2/6f70656e737461636b20342065766572/servers/f5dc173b-6804-445a-a6d8-c705dad5b5eb",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "http://openstack.example.com/6f70656e737461636b20342065766572/servers/f5dc173b-6804-445a-a6d8-c705dad5b5eb",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"My Server Name": "Apache1"
|
||||
},
|
||||
"name": "new-server-test",
|
||||
"progress": 0,
|
||||
"status": "ACTIVE",
|
||||
"tenant_id": "6f70656e737461636b20342065766572",
|
||||
"updated": "2013-09-03T04:01:32Z",
|
||||
"user_id": "fake",
|
||||
"locked": false,
|
||||
"tags": ["sometag"],
|
||||
"description": null
|
||||
}
|
||||
]
|
||||
}
|
@ -19,7 +19,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.25",
|
||||
"version": "2.26",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"version": "2.25",
|
||||
"version": "2.26",
|
||||
"min_version": "2.1",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
* 2.24 - Add API to cancel a running live migration
|
||||
* 2.25 - Make block_migration support 'auto' and remove
|
||||
disk_over_commit for os-migrateLive.
|
||||
|
||||
* 2.26 - Adds support of server tags
|
||||
"""
|
||||
|
||||
# The minimum and maximum versions of the API supported
|
||||
@ -80,7 +80,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
|
||||
# Note(cyeoh): This only applies for the v2.1 API once microversions
|
||||
# support is fully merged. It does not affect the V2 API.
|
||||
_MIN_API_VERSION = "2.1"
|
||||
_MAX_API_VERSION = "2.25"
|
||||
_MAX_API_VERSION = "2.26"
|
||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||
|
||||
|
||||
|
@ -82,7 +82,8 @@ v21_to_v2_extension_list_mapping = {
|
||||
v2_extension_suppress_list = ['servers', 'images', 'versions', 'flavors',
|
||||
'os-block-device-mapping-v1', 'os-consoles',
|
||||
'extensions', 'image-metadata', 'ips', 'limits',
|
||||
'server-metadata', 'server-migrations'
|
||||
'server-metadata', 'server-migrations',
|
||||
'os-server-tags'
|
||||
]
|
||||
|
||||
# v2.1 plugins which should appear under a different name in v2
|
||||
|
@ -35,6 +35,7 @@ def _get_tags_names(tags):
|
||||
class ServerTagsController(wsgi.Controller):
|
||||
_view_builder_class = server_tags.ViewBuilder
|
||||
|
||||
@wsgi.Controller.api_version("2.26")
|
||||
@wsgi.response(204)
|
||||
@extensions.expected_errors(404)
|
||||
def show(self, req, server_id, id):
|
||||
@ -51,6 +52,7 @@ class ServerTagsController(wsgi.Controller):
|
||||
% {'server_id': server_id, 'tag': id})
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
|
||||
@wsgi.Controller.api_version("2.26")
|
||||
@extensions.expected_errors(404)
|
||||
def index(self, req, server_id):
|
||||
context = req.environ["nova.context"]
|
||||
@ -63,6 +65,7 @@ class ServerTagsController(wsgi.Controller):
|
||||
|
||||
return {'tags': _get_tags_names(tags)}
|
||||
|
||||
@wsgi.Controller.api_version("2.26")
|
||||
@extensions.expected_errors((400, 404))
|
||||
@validation.schema(schema.update)
|
||||
def update(self, req, server_id, id, body):
|
||||
@ -109,6 +112,7 @@ class ServerTagsController(wsgi.Controller):
|
||||
req, server_id, id)
|
||||
return response
|
||||
|
||||
@wsgi.Controller.api_version("2.26")
|
||||
@extensions.expected_errors((400, 404))
|
||||
@validation.schema(schema.update_all)
|
||||
def update_all(self, req, server_id, body):
|
||||
@ -149,6 +153,7 @@ class ServerTagsController(wsgi.Controller):
|
||||
|
||||
return {'tags': _get_tags_names(tags)}
|
||||
|
||||
@wsgi.Controller.api_version("2.26")
|
||||
@wsgi.response(204)
|
||||
@extensions.expected_errors(404)
|
||||
def delete(self, req, server_id, id):
|
||||
@ -162,6 +167,7 @@ class ServerTagsController(wsgi.Controller):
|
||||
except exception.InstanceNotFound as e:
|
||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||
|
||||
@wsgi.Controller.api_version("2.26")
|
||||
@wsgi.response(204)
|
||||
@extensions.expected_errors(404)
|
||||
def delete_all(self, req, server_id):
|
||||
|
@ -45,6 +45,7 @@ from nova import objects
|
||||
from nova import utils
|
||||
|
||||
ALIAS = 'servers'
|
||||
TAG_SEARCH_FILTERS = ('tags', 'tags-any', 'not-tags', 'not-tags-any')
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.import_opt('enable_instance_password',
|
||||
@ -353,6 +354,12 @@ class ServersController(wsgi.Controller):
|
||||
msg = _("Only administrators may list deleted instances")
|
||||
raise exc.HTTPForbidden(explanation=msg)
|
||||
|
||||
if api_version_request.is_supported(req, min_version='2.26'):
|
||||
for tag_filter in TAG_SEARCH_FILTERS:
|
||||
if tag_filter in search_opts:
|
||||
search_opts[tag_filter] = search_opts[
|
||||
tag_filter].split(',')
|
||||
|
||||
# If tenant_id is passed as a search parameter this should
|
||||
# imply that all_tenants is also enabled unless explicitly
|
||||
# disabled. Note that the tenant_id parameter is filtered out
|
||||
@ -397,6 +404,9 @@ class ServersController(wsgi.Controller):
|
||||
|
||||
expected_attrs = ['pci_devices']
|
||||
if is_detail:
|
||||
if api_version_request.is_supported(req, '2.26'):
|
||||
expected_attrs.append("tags")
|
||||
|
||||
# merge our expected attrs with what the view builder needs for
|
||||
# showing details
|
||||
expected_attrs = self._view_builder.get_show_expected_attrs(
|
||||
@ -1141,6 +1151,8 @@ class ServersController(wsgi.Controller):
|
||||
'ip', 'changes-since', 'all_tenants')
|
||||
if api_version_request.is_supported(req, min_version='2.5'):
|
||||
opt_list += ('ip6',)
|
||||
if api_version_request.is_supported(req, min_version='2.26'):
|
||||
opt_list += TAG_SEARCH_FILTERS
|
||||
return opt_list
|
||||
|
||||
def _get_instance(self, context, instance_uuid):
|
||||
|
@ -313,4 +313,7 @@ class ViewBuilderV21(ViewBuilder):
|
||||
server["server"]["description"] = instance.get(
|
||||
"display_description")
|
||||
|
||||
if api_version_request.is_supported(request, min_version="2.26"):
|
||||
server["server"]["tags"] = [t.tag for t in instance.tags]
|
||||
|
||||
return server
|
||||
|
@ -218,3 +218,68 @@ user documentation.
|
||||
|
||||
Modify input parameter for ``os-migrateLive``. The block_migration will
|
||||
support 'auto' value, and disk_over_commit flag will be removed.
|
||||
|
||||
2.26
|
||||
----
|
||||
|
||||
Added support of server tags.
|
||||
|
||||
A user can create, update, delete or check existence of simple string tags
|
||||
for servers by the os-server-tags plugin.
|
||||
|
||||
The resource point for these operations is /servers/<server_id>/tags
|
||||
|
||||
A user can add a single tag to the server by sending PUT request to the
|
||||
/servers/<server_id>/tags/<tag>
|
||||
|
||||
where <tag> is any valid tag name.
|
||||
|
||||
A user can replace **all** current server tags to the new set of tags
|
||||
by sending PUT request to the /servers/<server_id>/tags. New set of tags
|
||||
must be specified in request body. This set must be in list 'tags'.
|
||||
|
||||
A user can remove specified tag from the server by sending DELETE request
|
||||
to the /servers/<server_id>/tags/<tag>
|
||||
|
||||
where <tag> is tag name which user wants to remove.
|
||||
|
||||
A user can remove **all** tags from the server by sending DELETE request
|
||||
to the /servers/<server_id>/tags
|
||||
|
||||
A user can get a set of server tags with information about server by sending
|
||||
GET request to the /servers/<server_id>
|
||||
|
||||
Request returns dictionary with information about specified server, including
|
||||
list 'tags' ::
|
||||
|
||||
{
|
||||
'id': {server_id},
|
||||
...
|
||||
'tags': ['foo', 'bar', 'baz']
|
||||
}
|
||||
|
||||
A user can get **only** a set of server tags by sending GET request to the
|
||||
/servers/<server_id>/tags
|
||||
|
||||
Response ::
|
||||
|
||||
{
|
||||
'tags': ['foo', 'bar', 'baz']
|
||||
}
|
||||
|
||||
A user can check if a tag exists or not on a server by sending
|
||||
GET /servers/{server_id}/tags/{tag}
|
||||
|
||||
Request returns `204 No Content` if tag exist on a server or `404 Not Found`
|
||||
if tag doesn't exist on a server.
|
||||
|
||||
A user can filter servers in GET /servers request by new filters:
|
||||
|
||||
* tags
|
||||
* tags-any
|
||||
* not-tags
|
||||
* not-tags-any
|
||||
|
||||
These filters can be combined. Also user can use more than one string tags
|
||||
for each filter. In this case string tags for each filter must be separated
|
||||
by comma: GET /servers?tags=red&tags-any=green,orange
|
||||
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"tags": ["%(tag)s"]
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
{
|
||||
"tags": [
|
||||
"%(tag)s"
|
||||
]
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
{
|
||||
"server": {
|
||||
"tags": [
|
||||
"%(tag)s"
|
||||
],
|
||||
"accessIPv4": "%(access_ip_v4)s",
|
||||
"accessIPv6": "%(access_ip_v6)s",
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"addr": "192.168.0.3",
|
||||
"OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff",
|
||||
"OS-EXT-IPS:type": "fixed",
|
||||
"version": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"created": "%(isotime)s",
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(compute_endpoint)s/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostId": "%(hostid)s",
|
||||
"id": "%(id)s",
|
||||
"image": {
|
||||
"id": "%(uuid)s",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(compute_endpoint)s/images/%(uuid)s",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"href": "%(versioned_compute_endpoint)s/servers/%(uuid)s",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "%(compute_endpoint)s/servers/%(uuid)s",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"My Server Name": "Apache1"
|
||||
},
|
||||
"name": "new-server-test",
|
||||
"progress": 0,
|
||||
"status": "ACTIVE",
|
||||
"tenant_id": "6f70656e737461636b20342065766572",
|
||||
"updated": "%(isotime)s",
|
||||
"key_name": null,
|
||||
"user_id": "fake",
|
||||
"locked": false,
|
||||
"description": null
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
{
|
||||
"servers": [
|
||||
{
|
||||
"accessIPv4": "%(access_ip_v4)s",
|
||||
"accessIPv6": "%(access_ip_v6)s",
|
||||
"addresses": {
|
||||
"private": [
|
||||
{
|
||||
"addr": "%(ip)s",
|
||||
"OS-EXT-IPS-MAC:mac_addr": "aa:bb:cc:dd:ee:ff",
|
||||
"OS-EXT-IPS:type": "fixed",
|
||||
"version": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"created": "%(isotime)s",
|
||||
"flavor": {
|
||||
"id": "1",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(compute_endpoint)s/flavors/1",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"hostId": "%(hostid)s",
|
||||
"id": "%(id)s",
|
||||
"image": {
|
||||
"id": "%(uuid)s",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(compute_endpoint)s/images/%(uuid)s",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
]
|
||||
},
|
||||
"key_name": null,
|
||||
"links": [
|
||||
{
|
||||
"href": "%(versioned_compute_endpoint)s/servers/%(uuid)s",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "%(compute_endpoint)s/servers/%(id)s",
|
||||
"rel": "bookmark"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"My Server Name": "Apache1"
|
||||
},
|
||||
"name": "new-server-test",
|
||||
"progress": 0,
|
||||
"status": "ACTIVE",
|
||||
"tenant_id": "6f70656e737461636b20342065766572",
|
||||
"updated": "%(isotime)s",
|
||||
"user_id": "fake",
|
||||
"locked": false,
|
||||
"tags": ["%(tag)s"],
|
||||
"description": null
|
||||
}
|
||||
]
|
||||
}
|
96
nova/tests/functional/api_sample_tests/test_server_tags.py
Normal file
96
nova/tests/functional/api_sample_tests/test_server_tags.py
Normal file
@ -0,0 +1,96 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from nova.db.sqlalchemy import models
|
||||
from nova.tests.functional.api_sample_tests import test_servers
|
||||
|
||||
TAG = 'sometag'
|
||||
|
||||
|
||||
class ServerTagsJsonTest(test_servers.ServersSampleBase):
|
||||
extension_name = 'os-server-tags'
|
||||
microversion = '2.26'
|
||||
scenarios = [('v2_26', {'api_major_version': 'v2.1'})]
|
||||
|
||||
def _get_create_subs(self):
|
||||
return {'tag': TAG}
|
||||
|
||||
def _put_server_tags(self):
|
||||
"""Verify the response status and returns the UUID of the
|
||||
newly created server with tags.
|
||||
"""
|
||||
uuid = self._post_server()
|
||||
subs = self._get_create_subs()
|
||||
response = self._do_put('servers/%s/tags' % uuid,
|
||||
'server-tags-put-all-req', subs)
|
||||
self.assertEqual(200, response.status_code)
|
||||
return uuid
|
||||
|
||||
def test_server_tags_update_all(self):
|
||||
self._put_server_tags()
|
||||
|
||||
def test_server_tags_show(self):
|
||||
uuid = self._put_server_tags()
|
||||
response = self._do_get('servers/%s/tags/%s' % (uuid, TAG))
|
||||
self.assertEqual(204, response.status_code)
|
||||
|
||||
def test_server_tags_show_with_details_information(self):
|
||||
uuid = self._put_server_tags()
|
||||
response = self._do_get('servers/%s' % uuid)
|
||||
subs = self._get_regexes()
|
||||
subs['hostid'] = '[a-f0-9]+'
|
||||
subs['tag'] = '[0-9a-zA-Z]+'
|
||||
subs['access_ip_v4'] = '1.2.3.4'
|
||||
subs['access_ip_v6'] = '80fe::'
|
||||
self._verify_response('server-tags-show-details-resp',
|
||||
subs, response, 200)
|
||||
|
||||
def test_server_tags_list_with_details_information(self):
|
||||
self._put_server_tags()
|
||||
response = self._do_get('servers/detail')
|
||||
subs = self._get_regexes()
|
||||
subs['hostid'] = '[a-f0-9]+'
|
||||
subs['tag'] = '[0-9a-zA-Z]+'
|
||||
subs['access_ip_v4'] = '1.2.3.4'
|
||||
subs['access_ip_v6'] = '80fe::'
|
||||
self._verify_response('servers-tags-details-resp', subs, response, 200)
|
||||
|
||||
def test_server_tags_index(self):
|
||||
uuid = self._put_server_tags()
|
||||
response = self._do_get('servers/%s/tags' % uuid)
|
||||
subs = self._get_regexes()
|
||||
subs['tag'] = '[0-9a-zA-Z]+'
|
||||
self._verify_response('server-tags-index-resp', subs, response, 200)
|
||||
|
||||
def test_server_tags_update(self):
|
||||
uuid = self._put_server_tags()
|
||||
tag = models.Tag()
|
||||
tag.resource_id = uuid
|
||||
tag.tag = 'OtherTag'
|
||||
response = self._do_put('servers/%s/tags/%s' % (uuid, tag.tag),
|
||||
'server-tags-put-req', {})
|
||||
self.assertEqual(201, response.status_code)
|
||||
expected_location = "%s/servers/%s/tags/%s" % (
|
||||
self._get_vers_compute_endpoint(), uuid, tag.tag)
|
||||
self.assertEqual(expected_location, response.headers['Location'])
|
||||
|
||||
def test_server_tags_delete(self):
|
||||
uuid = self._put_server_tags()
|
||||
response = self._do_delete('servers/%s/tags/%s' % (uuid, TAG))
|
||||
self.assertEqual(204, response.status_code)
|
||||
self.assertEqual('', response.content)
|
||||
|
||||
def test_server_tags_delete_all(self):
|
||||
uuid = self._put_server_tags()
|
||||
response = self._do_delete('servers/%s/tags' % uuid)
|
||||
self.assertEqual(204, response.status_code)
|
||||
self.assertEqual('', response.content)
|
@ -32,6 +32,8 @@ NON_EXISTING_UUID = '123'
|
||||
|
||||
|
||||
class ServerTagsTest(test.TestCase):
|
||||
api_version = '2.26'
|
||||
|
||||
def setUp(self):
|
||||
super(ServerTagsTest, self).setUp()
|
||||
self.controller = server_tags.ServerTagsController()
|
||||
@ -43,7 +45,7 @@ class ServerTagsTest(test.TestCase):
|
||||
return tag
|
||||
|
||||
def _get_request(self, url, method):
|
||||
request = fakes.HTTPRequest.blank(url)
|
||||
request = fakes.HTTPRequest.blank(url, version=self.api_version)
|
||||
request.method = method
|
||||
return request
|
||||
|
||||
|
@ -1462,6 +1462,65 @@ class ServersControllerTestV219(ServersControllerTest):
|
||||
self._test_list_server_detail_with_descriptions('desc1', 'desc2')
|
||||
|
||||
|
||||
class ServersControllerTestV226(ControllerTest):
|
||||
wsgi_api_version = '2.26'
|
||||
|
||||
@mock.patch.object(compute_api.API, 'get')
|
||||
def test_get_server_with_tags_by_id(self, mock_get):
|
||||
req = fakes.HTTPRequest.blank('/fake/servers/%s' % FAKE_UUID,
|
||||
version=self.wsgi_api_version)
|
||||
ctxt = req.environ['nova.context']
|
||||
|
||||
fake_server = fakes.stub_instance_obj(
|
||||
ctxt, id=2, vm_state=vm_states.ACTIVE, progress=100)
|
||||
|
||||
tags = ['tag1', 'tag2']
|
||||
tag_list = objects.TagList(objects=[
|
||||
objects.Tag(resource_id=FAKE_UUID, tag=tag)
|
||||
for tag in tags])
|
||||
|
||||
fake_server.tags = tag_list
|
||||
mock_get.return_value = fake_server
|
||||
|
||||
res_dict = self.controller.show(req, FAKE_UUID)
|
||||
|
||||
self.assertIn('tags', res_dict['server'])
|
||||
self.assertEqual(res_dict['server']['tags'], tags)
|
||||
|
||||
@mock.patch.object(compute_api.API, 'get_all')
|
||||
def _test_get_servers_allows_tag_filters(self, filter_name, mock_get_all):
|
||||
server_uuid = str(uuid.uuid4())
|
||||
req = fakes.HTTPRequest.blank('/fake/servers?%s=t1,t2' % filter_name,
|
||||
version=self.wsgi_api_version)
|
||||
ctxt = req.environ['nova.context']
|
||||
|
||||
def fake_get_all(*a, **kw):
|
||||
self.assertIsNotNone(kw['search_opts'])
|
||||
self.assertIn(filter_name, kw['search_opts'])
|
||||
self.assertEqual(kw['search_opts'][filter_name], ['t1', 't2'])
|
||||
return objects.InstanceList(
|
||||
objects=[fakes.stub_instance_obj(ctxt, uuid=server_uuid)])
|
||||
|
||||
mock_get_all.side_effect = fake_get_all
|
||||
|
||||
servers = self.controller.index(req)['servers']
|
||||
|
||||
self.assertEqual(len(servers), 1)
|
||||
self.assertEqual(servers[0]['id'], server_uuid)
|
||||
|
||||
def test_get_servers_allows_tags_filter(self):
|
||||
self._test_get_servers_allows_tag_filters('tags')
|
||||
|
||||
def test_get_servers_allows_tags_any_filter(self):
|
||||
self._test_get_servers_allows_tag_filters('tags-any')
|
||||
|
||||
def test_get_servers_allows_not_tags_filter(self):
|
||||
self._test_get_servers_allows_tag_filters('not-tags')
|
||||
|
||||
def test_get_servers_allows_not_tags_any_filter(self):
|
||||
self._test_get_servers_allows_tag_filters('not-tags-any')
|
||||
|
||||
|
||||
class ServersControllerDeleteTest(ControllerTest):
|
||||
|
||||
def setUp(self):
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- Microversion v2.26 allows to create/update/delete simple string tags.
|
||||
They can be used for filtering servers by these tags.
|
@ -142,6 +142,7 @@ nova.api.v21.extensions =
|
||||
server_metadata = nova.api.openstack.compute.server_metadata:ServerMetadata
|
||||
server_migrations = nova.api.openstack.compute.server_migrations:ServerMigrations
|
||||
server_password = nova.api.openstack.compute.server_password:ServerPassword
|
||||
server_tags = nova.api.openstack.compute.server_tags:ServerTags
|
||||
server_usage = nova.api.openstack.compute.server_usage:ServerUsage
|
||||
server_groups = nova.api.openstack.compute.server_groups:ServerGroups
|
||||
servers = nova.api.openstack.compute.servers:Servers
|
||||
|
Loading…
x
Reference in New Issue
Block a user