Added Support of microverison 2.26

This microversion allows to create/delete/update
server tags and to search servers by tags.

Implements: blueprint tag-instances

Change-Id: I66b6d4a763c507335f27a425bc3d4c2aae377c00
This commit is contained in:
Sergey Nikitin 2016-05-18 17:30:40 +03:00
parent 4187c69184
commit ff9b97b2c6
7 changed files with 331 additions and 1 deletions

View File

@ -25,4 +25,4 @@ API_MIN_VERSION = api_versions.APIVersion("2.1")
# when client supported the max version, and bumped sequentially, otherwise # when client supported the max version, and bumped sequentially, otherwise
# the client may break due to server side new version may include some # the client may break due to server side new version may include some
# backward incompatible change. # backward incompatible change.
API_MAX_VERSION = api_versions.APIVersion("2.25") API_MAX_VERSION = api_versions.APIVersion("2.26")

View File

@ -316,6 +316,50 @@ class Base(base.Fixture):
self.url(1234, 'os-server-password'), self.url(1234, 'os-server-password'),
status_code=202, status_code=202,
headers=self.json_headers) headers=self.json_headers)
#
# Server tags
#
self.requests.register_uri('GET',
self.url(1234, 'tags'),
json={'tags': ['tag1', 'tag2']},
headers=self.json_headers)
self.requests.register_uri('GET',
self.url(1234, 'tags', 'tag'),
status_code=204,
headers=self.json_headers)
self.requests.register_uri('DELETE',
self.url(1234, 'tags', 'tag'),
status_code=204,
headers=self.json_headers)
self.requests.register_uri('DELETE',
self.url(1234, 'tags'),
status_code=204,
headers=self.json_headers)
def put_server_tag(request, context):
body = jsonutils.loads(request.body)
assert body is None
context.status_code = 201
return None
self.requests.register_uri('PUT',
self.url(1234, 'tags', 'tag'),
json=put_server_tag,
headers=self.json_headers)
def put_server_tags(request, context):
body = jsonutils.loads(request.body)
assert list(body) == ['tags']
return body
self.requests.register_uri('PUT',
self.url(1234, 'tags'),
json=put_server_tags,
headers=self.json_headers)
class V1(Base): class V1(Base):

View File

@ -2221,6 +2221,24 @@ class FakeHTTPClient(base_client.HTTPClient):
def delete_servers_1234_migrations_1(self): def delete_servers_1234_migrations_1(self):
return (202, {}, None) return (202, {}, None)
def put_servers_1234_tags_tag(self, **kw):
return (201, {}, None)
def put_servers_1234_tags(self, **kw):
return (201, {}, None)
def get_servers_1234_tags(self, **kw):
return (200, {}, {'tags': ['tag1', 'tag2']})
def delete_servers_1234_tags_tag(self, **kw):
return (204, {}, None)
def delete_servers_1234_tags(self, **kw):
return (204, {}, None)
def get_servers_1234_tags_tag(self, **kw):
return (204, {}, None)
class FakeSessionClient(fakes.FakeClient, client.Client): class FakeSessionClient(fakes.FakeClient, client.Client):

View File

@ -1148,3 +1148,45 @@ class ServersV225Test(ServersV219Test):
s = self.cs.servers.get(1234) s = self.cs.servers.get(1234)
self.assertRaises(ValueError, s.live_migrate, 'hostname', self.assertRaises(ValueError, s.live_migrate, 'hostname',
'auto', 'True') 'auto', 'True')
class ServersV226Test(ServersV225Test):
def setUp(self):
super(ServersV219Test, self).setUp()
self.cs.api_version = api_versions.APIVersion("2.26")
def test_tag_list(self):
s = self.cs.servers.get(1234)
ret = s.tag_list()
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called('GET', '/servers/1234/tags')
def test_tag_delete(self):
s = self.cs.servers.get(1234)
ret = s.delete_tag('tag')
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called('DELETE', '/servers/1234/tags/tag')
def test_tag_delete_all(self):
s = self.cs.servers.get(1234)
ret = s.delete_all_tags()
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called('DELETE', '/servers/1234/tags')
def test_tag_add(self):
s = self.cs.servers.get(1234)
ret = s.add_tag('tag')
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called('PUT', '/servers/1234/tags/tag')
def test_tags_set(self):
s = self.cs.servers.get(1234)
ret = s.set_tags(['tag1', 'tag2'])
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called('PUT', '/servers/1234/tags')
def test_tag_exists(self):
s = self.cs.servers.get(1234)
ret = s.tag_exists('tag')
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called('GET', '/servers/1234/tags/tag')

View File

@ -2775,6 +2775,52 @@ class ShellTest(utils.TestCase):
self.run_command('list', api_version='2.10') self.run_command('list', api_version='2.10')
self.assert_called('GET', '/servers/detail') self.assert_called('GET', '/servers/detail')
def test_server_tag_add(self):
self.run_command('server-tag-add sample-server tag',
api_version='2.26')
self.assert_called('PUT', '/servers/1234/tags/tag', None)
def test_server_tag_set(self):
self.run_command('server-tag-set sample-server tag1 tag2',
api_version='2.26')
self.assert_called('PUT', '/servers/1234/tags',
{'tags': ['tag1', 'tag2']})
def test_server_tag_list(self):
self.run_command('server-tag-list sample-server', api_version='2.26')
self.assert_called('GET', '/servers/1234/tags')
def test_server_tag_delete(self):
self.run_command('server-tag-delete sample-server tag',
api_version='2.26')
self.assert_called('DELETE', '/servers/1234/tags/tag')
def test_server_tag_delete_all(self):
self.run_command('server-tag-delete-all sample-server',
api_version='2.26')
self.assert_called('DELETE', '/servers/1234/tags')
def test_server_tag_exists(self):
self.run_command('server-tag-exists sample-server tag',
api_version='2.26')
self.assert_called('GET', '/servers/1234/tags/tag')
def test_list_v2_26_tags(self):
self.run_command('list --tags tag1,tag2', api_version='2.26')
self.assert_called('GET', '/servers/detail?tags=tag1%2Ctag2')
def test_list_v2_26_tags_any(self):
self.run_command('list --tags-any tag1,tag2', api_version='2.26')
self.assert_called('GET', '/servers/detail?tags-any=tag1%2Ctag2')
def test_list_v2_26_not_tags(self):
self.run_command('list --not-tags tag1,tag2', api_version='2.26')
self.assert_called('GET', '/servers/detail?not-tags=tag1%2Ctag2')
def test_list_v2_26_not_tags_any(self):
self.run_command('list --not-tags-any tag1,tag2', api_version='2.26')
self.assert_called('GET', '/servers/detail?not-tags-any=tag1%2Ctag2')
class ShellWithSessionClientTest(ShellTest): class ShellWithSessionClientTest(ShellTest):

View File

@ -526,6 +526,42 @@ class Server(base.Resource):
"""Trigger crash dump in an instance""" """Trigger crash dump in an instance"""
return self.manager.trigger_crash_dump(self) return self.manager.trigger_crash_dump(self)
def tag_list(self):
"""
Get list of tags from an instance.
"""
return self.manager.tag_list(self)
def delete_tag(self, tag):
"""
Remove single tag from an instance.
"""
return self.manager.delete_tag(self, tag)
def delete_all_tags(self):
"""
Remove all tags from an instance.
"""
return self.manager.delete_all_tags(self)
def set_tags(self, tags):
"""
Set list of tags to an instance.
"""
return self.manager.set_tags(self, tags)
def add_tag(self, tag):
"""
Add single tag to an instance.
"""
return self.manager.add_tag(self, tag)
def tag_exists(self, tag):
"""
Check if an instance has specified tag.
"""
return self.manager.tag_exists(self, tag)
class NetworkInterface(base.Resource): class NetworkInterface(base.Resource):
@property @property
@ -1709,3 +1745,51 @@ class ServerManager(base.BootingManagerWithFind):
url = '/servers/%s/remote-consoles' % base.getid(server) url = '/servers/%s/remote-consoles' % base.getid(server)
resp, body = self.api.client.post(url, body=body) resp, body = self.api.client.post(url, body=body)
return self.convert_into_with_meta(body, resp) return self.convert_into_with_meta(body, resp)
@api_versions.wraps('2.26')
def tag_list(self, server):
"""
Get list of tags from an instance.
"""
resp, body = self.api.client.get(
"/servers/%s/tags" % base.getid(server))
return base.ListWithMeta(body['tags'], resp)
@api_versions.wraps('2.26')
def delete_tag(self, server, tag):
"""
Remove single tag from an instance.
"""
return self._delete("/servers/%s/tags/%s" % (base.getid(server), tag))
@api_versions.wraps('2.26')
def delete_all_tags(self, server):
"""
Remove all tags from an instance.
"""
return self._delete("/servers/%s/tags" % base.getid(server))
@api_versions.wraps('2.26')
def set_tags(self, server, tags):
"""
Set list of tags to an instance.
"""
body = {"tags": tags}
return self._update("/servers/%s/tags" % base.getid(server), body)
@api_versions.wraps('2.26')
def add_tag(self, server, tag):
"""
Add single tag to an instance.
"""
return self._update(
"/servers/%s/tags/%s" % (base.getid(server), tag), None)
@api_versions.wraps('2.26')
def tag_exists(self, server, tag):
"""
Check if an instance has specified tag.
"""
resp, body = self.api.client.get(
"/servers/%s/tags/%s" % (base.getid(server), tag))
return self.convert_into_with_meta(body, resp)

View File

@ -1461,6 +1461,45 @@ def do_image_delete(cs, args):
help=_("List only servers changed after a certain point of time." help=_("List only servers changed after a certain point of time."
"The provided time should be an ISO 8061 formatted time." "The provided time should be an ISO 8061 formatted time."
"ex 2016-03-04T06:27:59Z .")) "ex 2016-03-04T06:27:59Z ."))
@utils.arg(
'--tags',
dest='tags',
metavar='<tags>',
default=None,
help=_("The given tags must all be present for a server to be included in "
"the list result. Boolean expression in this case is 't1 AND t2'. "
"Tags must be separated by commas: --tags <tag1,tag2>"),
start_version="2.26")
@utils.arg(
'--tags-any',
dest='tags-any',
metavar='<tags-any>',
default=None,
help=_("If one of the given tags is present the server will be included "
"in the list result. Boolean expression in this case is "
"'t1 OR t2'. Tags must be separated by commas: "
"--tags-any <tag1,tag2>"),
start_version="2.26")
@utils.arg(
'--not-tags',
dest='not-tags',
metavar='<not-tags>',
default=None,
help=_("Only the servers that do not have any of the given tags will"
"be included in the list results. Boolean expression in this case "
"is 'NOT(t1 AND t2)'. Tags must be separated by commas: "
"--not-tags <tag1,tag2>"),
start_version="2.26")
@utils.arg(
'--not-tags-any',
dest='not-tags-any',
metavar='<not-tags-any>',
default=None,
help=_("Only the servers that do not have at least one of the given tags"
"will be included in the list result. Boolean expression in this "
"case is 'NOT(t1 OR t2)'. Tags must be separated by commas: "
"--not-tags-any <tag1,tag2>"),
start_version="2.26")
def do_list(cs, args): def do_list(cs, args):
"""List active servers.""" """List active servers."""
imageid = None imageid = None
@ -1488,6 +1527,10 @@ def do_list(cs, args):
'instance_name': args.instance_name, 'instance_name': args.instance_name,
'changes-since': args.changes_since} 'changes-since': args.changes_since}
for arg in ('tags', "tags-any", 'not-tags', 'not-tags-any'):
if arg in args:
search_opts[arg] = getattr(args, arg)
filters = {'flavor': lambda f: f['id'], filters = {'flavor': lambda f: f['id'],
'security_groups': utils.format_security_groups} 'security_groups': utils.format_security_groups}
@ -4956,3 +4999,56 @@ def do_virtual_interface_list(cs, args):
server = _find_server(cs, args.server) server = _find_server(cs, args.server)
interface_list = cs.virtual_interfaces.list(base.getid(server)) interface_list = cs.virtual_interfaces.list(base.getid(server))
_print_virtual_interface_list(cs, interface_list) _print_virtual_interface_list(cs, interface_list)
@api_versions.wraps("2.26")
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_server_tag_list(cs, args):
"""Get list of tags from a server."""
server = _find_server(cs, args.server)
tags = server.tag_list()
utils.print_list(tags, 'name')
@api_versions.wraps("2.26")
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('tag', metavar='<tag>', help=_('Tag to add.'))
def do_server_tag_add(cs, args):
"""Add single tag to a server."""
server = _find_server(cs, args.server)
server.add_tag(args.tag)
@api_versions.wraps("2.26")
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('tags', metavar='<tags>', nargs='+', help=_('Tag(s) to set.'))
def do_server_tag_set(cs, args):
"""Set list of tags to a server."""
server = _find_server(cs, args.server)
server.set_tags(args.tags)
@api_versions.wraps("2.26")
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('tag', metavar='<tag>', help=_('Tag to delete.'))
def do_server_tag_delete(cs, args):
"""Delete single tag from a server."""
server = _find_server(cs, args.server)
server.delete_tag(args.tag)
@api_versions.wraps("2.26")
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
def do_server_tag_delete_all(cs, args):
"""Delete all tags from a server."""
server = _find_server(cs, args.server)
server.delete_all_tags()
@api_versions.wraps("2.26")
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@utils.arg('tag', metavar='<tag>', help=_('Tag to check if it exists or not.'))
def do_server_tag_exists(cs, args):
"""Check if a server has specified tag."""
server = _find_server(cs, args.server)
server.tag_exists(args.tag)