diff --git a/doc/api_samples/all_extensions/extensions-get-resp.json b/doc/api_samples/all_extensions/extensions-get-resp.json index 078ba1545d83..f59780c51101 100644 --- a/doc/api_samples/all_extensions/extensions-get-resp.json +++ b/doc/api_samples/all_extensions/extensions-get-resp.json @@ -639,6 +639,14 @@ "name": "ExtendedServicesDelete", "namespace": "http://docs.openstack.org/compute/ext/extended_services_delete/api/v2", "updated": "2013-12-10T00:00:00+00:00" + }, + { + "alias": "os-console-auth-tokens", + "description": "Console token authentication support.", + "links": [], + "name": "ConsoleAuthTokens", + "namespace": "http://docs.openstack.org/compute/ext/consoles-auth-tokens/api/v2", + "updated": "2013-08-13T00:00:00+00:00" } ] } diff --git a/doc/api_samples/all_extensions/extensions-get-resp.xml b/doc/api_samples/all_extensions/extensions-get-resp.xml index 6fcacf603ba0..627c28eefc6d 100644 --- a/doc/api_samples/all_extensions/extensions-get-resp.xml +++ b/doc/api_samples/all_extensions/extensions-get-resp.xml @@ -260,4 +260,7 @@ Services deletion support. + + Console token authentication support. + diff --git a/doc/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.json b/doc/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.json new file mode 100644 index 000000000000..07e93c9b8457 --- /dev/null +++ b/doc/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.json @@ -0,0 +1,8 @@ +{ + "console": { + "instance_uuid": "b48316c5-71e8-45e4-9884-6c78055b9b13", + "host": "localhost", + "port": 5900, + "internal_access_path": "51af38c3-555e-4884-a314-6c8cdde37444" + } +} diff --git a/doc/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.xml b/doc/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.xml new file mode 100644 index 000000000000..18ab81e4c33e --- /dev/null +++ b/doc/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.xml @@ -0,0 +1,7 @@ + + + b48316c5-71e8-45e4-9884-6c78055b9b13 + localhost + 5900 + 51af38c3-555e-4884-a314-6c8cdde37444 + diff --git a/doc/api_samples/os-console-auth-tokens/get-rdp-console-post-req.json b/doc/api_samples/os-console-auth-tokens/get-rdp-console-post-req.json new file mode 100644 index 000000000000..00956b90e4a0 --- /dev/null +++ b/doc/api_samples/os-console-auth-tokens/get-rdp-console-post-req.json @@ -0,0 +1,5 @@ +{ + "os-getRDPConsole": { + "type": "rdp-html5" + } +} diff --git a/doc/api_samples/os-console-auth-tokens/get-rdp-console-post-req.xml b/doc/api_samples/os-console-auth-tokens/get-rdp-console-post-req.xml new file mode 100644 index 000000000000..16cf28832d70 --- /dev/null +++ b/doc/api_samples/os-console-auth-tokens/get-rdp-console-post-req.xml @@ -0,0 +1,2 @@ + + diff --git a/doc/api_samples/os-console-auth-tokens/server-post-req.json b/doc/api_samples/os-console-auth-tokens/server-post-req.json new file mode 100644 index 000000000000..09366b4c934e --- /dev/null +++ b/doc/api_samples/os-console-auth-tokens/server-post-req.json @@ -0,0 +1,16 @@ +{ + "server" : { + "name" : "new-server-test", + "imageRef" : "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b", + "flavorRef" : "http://openstack.example.com/openstack/flavors/1", + "metadata" : { + "My Server Name" : "Apache1" + }, + "personality" : [ + { + "path" : "/etc/banner.txt", + "contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBpdCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5kIGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVsc2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4gQnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRoZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlvdSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vyc2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6b25zLiINCg0KLVJpY2hhcmQgQmFjaA==" + } + ] + } +} diff --git a/doc/api_samples/os-console-auth-tokens/server-post-req.xml b/doc/api_samples/os-console-auth-tokens/server-post-req.xml new file mode 100644 index 000000000000..077dd7618cf4 --- /dev/null +++ b/doc/api_samples/os-console-auth-tokens/server-post-req.xml @@ -0,0 +1,19 @@ + + + + Apache1 + + + + ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp + dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k + IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs + c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g + QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo + ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv + dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy + c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 + b25zLiINCg0KLVJpY2hhcmQgQmFjaA== + + + diff --git a/doc/api_samples/os-console-auth-tokens/server-post-resp.json b/doc/api_samples/os-console-auth-tokens/server-post-resp.json new file mode 100644 index 000000000000..db9ed3d6d1ed --- /dev/null +++ b/doc/api_samples/os-console-auth-tokens/server-post-resp.json @@ -0,0 +1,16 @@ +{ + "server": { + "adminPass": "MVk5HPrazHcG", + "id": "5bbcc3c4-1da2-4437-a48a-66f15b1b13f9", + "links": [ + { + "href": "http://openstack.example.com/v2/openstack/servers/5bbcc3c4-1da2-4437-a48a-66f15b1b13f9", + "rel": "self" + }, + { + "href": "http://openstack.example.com/openstack/servers/5bbcc3c4-1da2-4437-a48a-66f15b1b13f9", + "rel": "bookmark" + } + ] + } +} diff --git a/doc/api_samples/os-console-auth-tokens/server-post-resp.xml b/doc/api_samples/os-console-auth-tokens/server-post-resp.xml new file mode 100644 index 000000000000..68f0933c7710 --- /dev/null +++ b/doc/api_samples/os-console-auth-tokens/server-post-resp.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/doc/v3/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.json b/doc/v3/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.json new file mode 100644 index 000000000000..07e93c9b8457 --- /dev/null +++ b/doc/v3/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.json @@ -0,0 +1,8 @@ +{ + "console": { + "instance_uuid": "b48316c5-71e8-45e4-9884-6c78055b9b13", + "host": "localhost", + "port": 5900, + "internal_access_path": "51af38c3-555e-4884-a314-6c8cdde37444" + } +} diff --git a/doc/v3/api_samples/os-console-auth-tokens/get-rdp-console-post-req.json b/doc/v3/api_samples/os-console-auth-tokens/get-rdp-console-post-req.json new file mode 100644 index 000000000000..075d3b28a861 --- /dev/null +++ b/doc/v3/api_samples/os-console-auth-tokens/get-rdp-console-post-req.json @@ -0,0 +1,5 @@ +{ + "get_rdp_console": { + "type": "rdp-html5" + } +} diff --git a/doc/v3/api_samples/os-console-auth-tokens/server-post-req.json b/doc/v3/api_samples/os-console-auth-tokens/server-post-req.json new file mode 100644 index 000000000000..2eedab6147cf --- /dev/null +++ b/doc/v3/api_samples/os-console-auth-tokens/server-post-req.json @@ -0,0 +1,10 @@ +{ + "server" : { + "name" : "new-server-test", + "image_ref" : "http://glance.openstack.example.com/images/70a599e0-31e7-49b7-b260-868f441e862b", + "flavor_ref" : "http://openstack.example.com/flavors/1", + "metadata" : { + "My Server Name" : "Apache1" + } + } +} diff --git a/doc/v3/api_samples/os-console-auth-tokens/server-post-resp.json b/doc/v3/api_samples/os-console-auth-tokens/server-post-resp.json new file mode 100644 index 000000000000..3d22d59aa60a --- /dev/null +++ b/doc/v3/api_samples/os-console-auth-tokens/server-post-resp.json @@ -0,0 +1,16 @@ +{ + "server": { + "admin_password": "Kwg5tff6KiUU", + "id": "8619225c-67c8-424f-9b46-cec5bad137a2", + "links": [ + { + "href": "http://openstack.example.com/v3/servers/8619225c-67c8-424f-9b46-cec5bad137a2", + "rel": "self" + }, + { + "href": "http://openstack.example.com/servers/8619225c-67c8-424f-9b46-cec5bad137a2", + "rel": "bookmark" + } + ] + } +} diff --git a/etc/nova/policy.json b/etc/nova/policy.json index 75e5719c38cc..27adf30744a7 100644 --- a/etc/nova/policy.json +++ b/etc/nova/policy.json @@ -256,6 +256,8 @@ "compute_extension:v3:os-migrations:discoverable": "", "compute_extension:os-assisted-volume-snapshots:create": "rule:admin_api", "compute_extension:os-assisted-volume-snapshots:delete": "rule:admin_api", + "compute_extension:console_auth_tokens": "rule:admin_api", + "compute_extension:v3:os-console-auth-tokens": "rule:admin_api", "volume:create": "", diff --git a/nova/api/openstack/compute/contrib/console_auth_tokens.py b/nova/api/openstack/compute/contrib/console_auth_tokens.py new file mode 100644 index 000000000000..c6aa9e07a5c2 --- /dev/null +++ b/nova/api/openstack/compute/contrib/console_auth_tokens.py @@ -0,0 +1,68 @@ +# Copyright 2013 Cloudbase Solutions Srl +# All Rights Reserved. +# +# 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. + +import webob + +from nova.api.openstack import extensions +from nova.api.openstack import wsgi +from nova.consoleauth import rpcapi as consoleauth_rpcapi +from nova.openstack.common.gettextutils import _ + + +authorize = extensions.extension_authorizer('compute', 'console_auth_tokens') + + +class ConsoleAuthTokensController(wsgi.Controller): + def __init__(self, *args, **kwargs): + self._consoleauth_rpcapi = consoleauth_rpcapi.ConsoleAuthAPI() + super(ConsoleAuthTokensController, self).__init__(*args, **kwargs) + + def show(self, req, id): + """Checks a console auth token and returns the related connect info.""" + context = req.environ['nova.context'] + authorize(context) + + token = id + connect_info = self._consoleauth_rpcapi.check_token(context, token) + if not connect_info: + raise webob.exc.HTTPNotFound(explanation=_("Token not found")) + + console_type = connect_info.get('console_type') + # This is currently required only for RDP consoles + if console_type != "rdp-html5": + raise webob.exc.HTTPUnauthorized( + explanation=_("The requested console type details are not " + "accessible")) + + return {'console': + dict([(i, connect_info[i]) + for i in ['instance_uuid', 'host', 'port', + 'internal_access_path'] + if i in connect_info])} + + +class Console_auth_tokens(extensions.ExtensionDescriptor): + """Console token authentication support.""" + name = "ConsoleAuthTokens" + alias = "os-console-auth-tokens" + namespace = ("http://docs.openstack.org/compute/ext/" + "consoles-auth-tokens/api/v2") + updated = "2013-08-13T00:00:00+00:00" + + def get_resources(self): + controller = ConsoleAuthTokensController() + ext = extensions.ResourceExtension('os-console-auth-tokens', + controller) + return [ext] diff --git a/nova/api/openstack/compute/plugins/v3/console_auth_tokens.py b/nova/api/openstack/compute/plugins/v3/console_auth_tokens.py new file mode 100644 index 000000000000..2aedcd801008 --- /dev/null +++ b/nova/api/openstack/compute/plugins/v3/console_auth_tokens.py @@ -0,0 +1,75 @@ +# Copyright 2013 Cloudbase Solutions Srl +# All Rights Reserved. +# +# 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. + +import webob + +from nova.api.openstack import extensions +from nova.api.openstack import wsgi +from nova.consoleauth import rpcapi as consoleauth_rpcapi +from nova.openstack.common.gettextutils import _ + +ALIAS = "os-console-auth-tokens" +authorize = extensions.extension_authorizer('compute', 'v3:' + ALIAS) + + +class ConsoleAuthTokensController(wsgi.Controller): + def __init__(self, *args, **kwargs): + self._consoleauth_rpcapi = consoleauth_rpcapi.ConsoleAuthAPI() + super(ConsoleAuthTokensController, self).__init__(*args, **kwargs) + + @extensions.expected_errors((400, 401, 404)) + def show(self, req, id): + """Checks a console auth token and returns the related connect info.""" + context = req.environ['nova.context'] + authorize(context) + + token = id + if not token: + msg = _("token not provided") + raise webob.exc.HTTPBadRequest(explanation=msg) + + connect_info = self._consoleauth_rpcapi.check_token(context, token) + if not connect_info: + raise webob.exc.HTTPNotFound(explanation=_("Token not found")) + + console_type = connect_info.get('console_type') + # This is currently required only for RDP consoles + if console_type != "rdp-html5": + raise webob.exc.HTTPUnauthorized( + explanation=_("The requested console type details are not " + "accessible")) + + return {'console': + dict([(i, connect_info[i]) + for i in ['instance_uuid', 'host', 'port', + 'internal_access_path'] + if i in connect_info])} + + +class ConsoleAuthTokens(extensions.V3APIExtensionBase): + """Console token authentication support.""" + name = "ConsoleAuthTokens" + alias = ALIAS + namespace = "http://docs.openstack.org/compute/ext/%s/api/v3" % ALIAS + version = 1 + + def get_resources(self): + controller = ConsoleAuthTokensController() + ext = extensions.ResourceExtension('os-console-auth-tokens', + controller) + return [ext] + + def get_controller_extensions(self): + return [] diff --git a/nova/tests/api/openstack/compute/contrib/test_console_auth_tokens.py b/nova/tests/api/openstack/compute/contrib/test_console_auth_tokens.py new file mode 100644 index 000000000000..33782414326d --- /dev/null +++ b/nova/tests/api/openstack/compute/contrib/test_console_auth_tokens.py @@ -0,0 +1,104 @@ +# Copyright 2013 Cloudbase Solutions Srl +# All Rights Reserved. +# +# 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. + +import webob + +from oslo.config import cfg + +from nova.consoleauth import rpcapi as consoleauth_rpcapi +from nova import context +from nova.openstack.common import jsonutils +from nova import test +from nova.tests.api.openstack import fakes + +CONF = cfg.CONF +CONF.import_opt('osapi_compute_ext_list', 'nova.api.openstack.compute.contrib') + +_FAKE_CONNECT_INFO = {'instance_uuid': 'fake_instance_uuid', + 'host': 'fake_host', + 'port': 'fake_port', + 'internal_access_path': 'fake_access_path', + 'console_type': 'rdp-html5'} + + +def _fake_check_token(self, context, token): + return _FAKE_CONNECT_INFO + + +def _fake_check_token_not_found(self, context, token): + return None + + +def _fake_check_token_unauthorized(self, context, token): + connect_info = _FAKE_CONNECT_INFO + connect_info['console_type'] = 'unauthorized_console_type' + return connect_info + + +class ConsoleAuthTokensExtensionTest(test.TestCase): + + _FAKE_URL = '/v2/fake/os-console-auth-tokens/1' + + _EXPECTED_OUTPUT = {'console': {'instance_uuid': 'fake_instance_uuid', + 'host': 'fake_host', + 'port': 'fake_port', + 'internal_access_path': + 'fake_access_path'}} + + def setUp(self): + super(ConsoleAuthTokensExtensionTest, self).setUp() + self.stubs.Set(consoleauth_rpcapi.ConsoleAuthAPI, 'check_token', + _fake_check_token) + self.flags( + osapi_compute_extension=[ + 'nova.api.openstack.compute.contrib.select_extensions'], + osapi_compute_ext_list=['Console_auth_tokens']) + + ctxt = self._get_admin_context() + self.app = fakes.wsgi_app(init_only=('os-console-auth-tokens',), + fake_auth_context=ctxt) + + def _get_admin_context(self): + ctxt = context.get_admin_context() + ctxt.user_id = 'fake' + ctxt.project_id = 'fake' + return ctxt + + def _create_request(self): + req = webob.Request.blank(self._FAKE_URL) + req.method = "GET" + req.headers["content-type"] = "application/json" + return req + + def test_get_console_connect_info(self): + req = self._create_request() + res = req.get_response(self.app) + self.assertEqual(200, res.status_int) + output = jsonutils.loads(res.body) + self.assertEqual(self._EXPECTED_OUTPUT, output) + + def test_get_console_connect_info_token_not_found(self): + self.stubs.Set(consoleauth_rpcapi.ConsoleAuthAPI, 'check_token', + _fake_check_token_not_found) + req = self._create_request() + res = req.get_response(self.app) + self.assertEqual(404, res.status_int) + + def test_get_console_connect_info_unauthorized_console_type(self): + self.stubs.Set(consoleauth_rpcapi.ConsoleAuthAPI, 'check_token', + _fake_check_token_unauthorized) + req = self._create_request() + res = req.get_response(self.app) + self.assertEqual(401, res.status_int) diff --git a/nova/tests/api/openstack/compute/plugins/v3/test_console_auth_tokens.py b/nova/tests/api/openstack/compute/plugins/v3/test_console_auth_tokens.py new file mode 100644 index 000000000000..3a91903d0501 --- /dev/null +++ b/nova/tests/api/openstack/compute/plugins/v3/test_console_auth_tokens.py @@ -0,0 +1,94 @@ +# Copyright 2013 Cloudbase Solutions Srl +# All Rights Reserved. +# +# 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.consoleauth import rpcapi as consoleauth_rpcapi +from nova import context +from nova.openstack.common import jsonutils +from nova import test +from nova.tests.api.openstack import fakes + + +_FAKE_CONNECT_INFO = {'instance_uuid': 'fake_instance_uuid', + 'host': 'fake_host', + 'port': 'fake_port', + 'internal_access_path': 'fake_access_path', + 'console_type': 'rdp-html5'} + + +def _fake_check_token(self, context, token): + return _FAKE_CONNECT_INFO + + +def _fake_check_token_not_found(self, context, token): + return None + + +def _fake_check_token_unauthorized(self, context, token): + connect_info = _FAKE_CONNECT_INFO + connect_info['console_type'] = 'unauthorized_console_type' + return connect_info + + +class ConsoleAuthTokensExtensionTest(test.TestCase): + + _FAKE_URL = '/v3/os-console-auth-tokens/1' + + _EXPECTED_OUTPUT = {'console': {'instance_uuid': 'fake_instance_uuid', + 'host': 'fake_host', + 'port': 'fake_port', + 'internal_access_path': + 'fake_access_path'}} + + def setUp(self): + super(ConsoleAuthTokensExtensionTest, self).setUp() + self.stubs.Set(consoleauth_rpcapi.ConsoleAuthAPI, 'check_token', + _fake_check_token) + + ctxt = self._get_admin_context() + self.app = fakes.wsgi_app_v3(init_only=('os-console-auth-tokens'), + fake_auth_context=ctxt) + + def _get_admin_context(self): + ctxt = context.get_admin_context() + ctxt.user_id = 'fake' + ctxt.project_id = 'fake' + return ctxt + + def _create_request(self): + req = fakes.HTTPRequestV3.blank(self._FAKE_URL) + req.method = "GET" + req.headers["content-type"] = "application/json" + return req + + def test_get_console_connect_info(self): + req = self._create_request() + res = req.get_response(self.app) + self.assertEqual(200, res.status_int) + output = jsonutils.loads(res.body) + self.assertEqual(self._EXPECTED_OUTPUT, output) + + def test_get_console_connect_info_token_not_found(self): + self.stubs.Set(consoleauth_rpcapi.ConsoleAuthAPI, 'check_token', + _fake_check_token_not_found) + req = self._create_request() + res = req.get_response(self.app) + self.assertEqual(404, res.status_int) + + def test_get_console_connect_info_unauthorized_console_type(self): + self.stubs.Set(consoleauth_rpcapi.ConsoleAuthAPI, 'check_token', + _fake_check_token_unauthorized) + req = self._create_request() + res = req.get_response(self.app) + self.assertEqual(401, res.status_int) diff --git a/nova/tests/fake_policy.py b/nova/tests/fake_policy.py index a868c3626389..0c4934786d3a 100644 --- a/nova/tests/fake_policy.py +++ b/nova/tests/fake_policy.py @@ -294,6 +294,8 @@ policy_data = """ "compute_extension:v3:os-migrations:index": "is_admin:True", "compute_extension:os-assisted-volume-snapshots:create": "", "compute_extension:os-assisted-volume-snapshots:delete": "", + "compute_extension:console_auth_tokens": "is_admin:True", + "compute_extension:v3:os-console-auth-tokens": "is_admin:True", "volume:create": "", "volume:get": "", diff --git a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl index c1f5ae3f9c0b..0a7a3446ad4b 100644 --- a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl +++ b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl @@ -104,6 +104,14 @@ "namespace": "http://docs.openstack.org/compute/ext/server_usage/api/v1.1", "updated": "%(timestamp)s" }, + { + "alias": "os-console-auth-tokens", + "description": "%(text)s", + "links": [], + "name": "ConsoleAuthTokens", + "namespace": "http://docs.openstack.org/compute/ext/consoles-auth-tokens/api/v2", + "updated": "%(timestamp)s" + }, { "alias": "OS-SCH-HNT", "description": "%(text)s", diff --git a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl index ba17766d5806..e53475681f54 100644 --- a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl +++ b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl @@ -239,4 +239,7 @@ %(text)s + + %(text)s + diff --git a/nova/tests/integrated/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.json.tpl b/nova/tests/integrated/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.json.tpl new file mode 100644 index 000000000000..f5be11801e0d --- /dev/null +++ b/nova/tests/integrated/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.json.tpl @@ -0,0 +1,8 @@ +{ + "console": { + "instance_uuid": "%(id)s", + "host": "%(host)s", + "port": %(port)s, + "internal_access_path": "%(internal_access_path)s" + } +} diff --git a/nova/tests/integrated/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.xml.tpl b/nova/tests/integrated/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.xml.tpl new file mode 100644 index 000000000000..de81f08fe86f --- /dev/null +++ b/nova/tests/integrated/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.xml.tpl @@ -0,0 +1,7 @@ + + + %(id)s + %(host)s + %(port)s + %(internal_access_path)s + diff --git a/nova/tests/integrated/api_samples/os-console-auth-tokens/get-rdp-console-post-req.json.tpl b/nova/tests/integrated/api_samples/os-console-auth-tokens/get-rdp-console-post-req.json.tpl new file mode 100644 index 000000000000..00956b90e4a0 --- /dev/null +++ b/nova/tests/integrated/api_samples/os-console-auth-tokens/get-rdp-console-post-req.json.tpl @@ -0,0 +1,5 @@ +{ + "os-getRDPConsole": { + "type": "rdp-html5" + } +} diff --git a/nova/tests/integrated/api_samples/os-console-auth-tokens/get-rdp-console-post-req.xml.tpl b/nova/tests/integrated/api_samples/os-console-auth-tokens/get-rdp-console-post-req.xml.tpl new file mode 100644 index 000000000000..b761d78b6752 --- /dev/null +++ b/nova/tests/integrated/api_samples/os-console-auth-tokens/get-rdp-console-post-req.xml.tpl @@ -0,0 +1,4 @@ + + + rdp-html5 + diff --git a/nova/tests/integrated/api_samples/os-console-auth-tokens/server-post-req.json.tpl b/nova/tests/integrated/api_samples/os-console-auth-tokens/server-post-req.json.tpl new file mode 100644 index 000000000000..d3916d1aa68a --- /dev/null +++ b/nova/tests/integrated/api_samples/os-console-auth-tokens/server-post-req.json.tpl @@ -0,0 +1,16 @@ +{ + "server" : { + "name" : "new-server-test", + "imageRef" : "%(host)s/openstack/images/%(image_id)s", + "flavorRef" : "%(host)s/openstack/flavors/1", + "metadata" : { + "My Server Name" : "Apache1" + }, + "personality" : [ + { + "path" : "/etc/banner.txt", + "contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBpdCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5kIGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVsc2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4gQnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRoZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlvdSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vyc2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6b25zLiINCg0KLVJpY2hhcmQgQmFjaA==" + } + ] + } +} diff --git a/nova/tests/integrated/api_samples/os-console-auth-tokens/server-post-req.xml.tpl b/nova/tests/integrated/api_samples/os-console-auth-tokens/server-post-req.xml.tpl new file mode 100644 index 000000000000..f92614984242 --- /dev/null +++ b/nova/tests/integrated/api_samples/os-console-auth-tokens/server-post-req.xml.tpl @@ -0,0 +1,19 @@ + + + + Apache1 + + + + ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp + dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k + IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs + c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g + QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo + ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv + dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy + c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6 + b25zLiINCg0KLVJpY2hhcmQgQmFjaA== + + + diff --git a/nova/tests/integrated/api_samples/os-console-auth-tokens/server-post-resp.json.tpl b/nova/tests/integrated/api_samples/os-console-auth-tokens/server-post-resp.json.tpl new file mode 100644 index 000000000000..d5f030c8730b --- /dev/null +++ b/nova/tests/integrated/api_samples/os-console-auth-tokens/server-post-resp.json.tpl @@ -0,0 +1,16 @@ +{ + "server": { + "adminPass": "%(password)s", + "id": "%(id)s", + "links": [ + { + "href": "%(host)s/v2/openstack/servers/%(uuid)s", + "rel": "self" + }, + { + "href": "%(host)s/openstack/servers/%(uuid)s", + "rel": "bookmark" + } + ] + } +} diff --git a/nova/tests/integrated/api_samples/os-console-auth-tokens/server-post-resp.xml.tpl b/nova/tests/integrated/api_samples/os-console-auth-tokens/server-post-resp.xml.tpl new file mode 100644 index 000000000000..3bb13e69bd6d --- /dev/null +++ b/nova/tests/integrated/api_samples/os-console-auth-tokens/server-post-resp.xml.tpl @@ -0,0 +1,6 @@ + + + + + + diff --git a/nova/tests/integrated/test_api_samples.py b/nova/tests/integrated/test_api_samples.py index 1efde855a5c5..81439bd4ae80 100644 --- a/nova/tests/integrated/test_api_samples.py +++ b/nova/tests/integrated/test_api_samples.py @@ -19,6 +19,7 @@ import datetime import inspect import json import os +import re import urllib import uuid as uuid_lib @@ -2037,6 +2038,46 @@ class ConsolesSampleXmlTests(ConsolesSampleJsonTests): ctype = 'xml' +class ConsoleAuthTokensSampleJsonTests(ServersSampleBase): + extends_name = ("nova.api.openstack.compute.contrib.consoles.Consoles") + extension_name = ("nova.api.openstack.compute.contrib.console_auth_tokens." + "Console_auth_tokens") + + def _get_console_url(self, data): + return json.loads(data)["console"]["url"] + + def _get_console_token(self, uuid): + response = self._do_post('servers/%s/action' % uuid, + 'get-rdp-console-post-req', + {'action': 'os-getRDPConsole'}) + + url = self._get_console_url(response.read()) + return re.match('.+?token=([^&]+)', url).groups()[0] + + def test_get_console_connect_info(self): + self.flags(enabled=True, group='rdp') + + uuid = self._post_server() + token = self._get_console_token(uuid) + + response = self._do_get('os-console-auth-tokens/%s' % token) + + subs = self._get_regexes() + subs["uuid"] = uuid + subs["host"] = r"[\w\.\-]+" + subs["port"] = "[0-9]+" + subs["internal_access_path"] = ".*" + self._verify_response('get-console-connect-info-get-resp', subs, + response, 200) + + +class ConsoleAuthTokensSampleXmlTests(ConsoleAuthTokensSampleJsonTests): + ctype = 'xml' + + def _get_console_url(self, data): + return etree.fromstring(data).find('url').text + + class DeferredDeleteSampleJsonTests(ServersSampleBase): extension_name = ("nova.api.openstack.compute.contrib" ".deferred_delete.Deferred_delete") diff --git a/nova/tests/integrated/v3/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.json.tpl b/nova/tests/integrated/v3/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.json.tpl new file mode 100644 index 000000000000..f5be11801e0d --- /dev/null +++ b/nova/tests/integrated/v3/api_samples/os-console-auth-tokens/get-console-connect-info-get-resp.json.tpl @@ -0,0 +1,8 @@ +{ + "console": { + "instance_uuid": "%(id)s", + "host": "%(host)s", + "port": %(port)s, + "internal_access_path": "%(internal_access_path)s" + } +} diff --git a/nova/tests/integrated/v3/api_samples/os-console-auth-tokens/get-rdp-console-post-req.json.tpl b/nova/tests/integrated/v3/api_samples/os-console-auth-tokens/get-rdp-console-post-req.json.tpl new file mode 100644 index 000000000000..075d3b28a861 --- /dev/null +++ b/nova/tests/integrated/v3/api_samples/os-console-auth-tokens/get-rdp-console-post-req.json.tpl @@ -0,0 +1,5 @@ +{ + "get_rdp_console": { + "type": "rdp-html5" + } +} diff --git a/nova/tests/integrated/v3/api_samples/os-console-auth-tokens/server-post-req.json.tpl b/nova/tests/integrated/v3/api_samples/os-console-auth-tokens/server-post-req.json.tpl new file mode 100644 index 000000000000..d9a7537dfb34 --- /dev/null +++ b/nova/tests/integrated/v3/api_samples/os-console-auth-tokens/server-post-req.json.tpl @@ -0,0 +1,10 @@ +{ + "server" : { + "name" : "new-server-test", + "image_ref" : "%(glance_host)s/images/%(image_id)s", + "flavor_ref" : "%(host)s/flavors/1", + "metadata" : { + "My Server Name" : "Apache1" + } + } +} diff --git a/nova/tests/integrated/v3/api_samples/os-console-auth-tokens/server-post-resp.json.tpl b/nova/tests/integrated/v3/api_samples/os-console-auth-tokens/server-post-resp.json.tpl new file mode 100644 index 000000000000..eb3f76ebe6d3 --- /dev/null +++ b/nova/tests/integrated/v3/api_samples/os-console-auth-tokens/server-post-resp.json.tpl @@ -0,0 +1,16 @@ +{ + "server": { + "admin_password": "%(password)s", + "id": "%(id)s", + "links": [ + { + "href": "%(host)s/v3/servers/%(uuid)s", + "rel": "self" + }, + { + "href": "%(host)s/servers/%(uuid)s", + "rel": "bookmark" + } + ] + } +} diff --git a/nova/tests/integrated/v3/test_console_auth_tokens.py b/nova/tests/integrated/v3/test_console_auth_tokens.py new file mode 100644 index 000000000000..a7cc228a5719 --- /dev/null +++ b/nova/tests/integrated/v3/test_console_auth_tokens.py @@ -0,0 +1,50 @@ +# Copyright 2013 Cloudbase Solutions Srl +# +# 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. + +import json +import re + +from nova.tests.integrated.v3 import test_servers + + +class ConsoleAuthTokensSampleJsonTests(test_servers.ServersSampleBase): + extension_name = "os-console-auth-tokens" + extra_extensions_to_load = ["os-remote-consoles"] + + def _get_console_url(self, data): + return json.loads(data)["console"]["url"] + + def _get_console_token(self, uuid): + response = self._do_post('servers/%s/action' % uuid, + 'get-rdp-console-post-req', + {'action': 'os-getRDPConsole'}) + + url = self._get_console_url(response.read()) + return re.match('.+?token=([^&]+)', url).groups()[0] + + def test_get_console_connect_info(self): + self.flags(enabled=True, group='rdp') + + uuid = self._post_server() + token = self._get_console_token(uuid) + + response = self._do_get('os-console-auth-tokens/%s' % token) + + subs = self._get_regexes() + subs["uuid"] = uuid + subs["host"] = r"[\w\.\-]+" + subs["port"] = "[0-9]+" + subs["internal_access_path"] = ".*" + self._verify_response('get-console-connect-info-get-resp', subs, + response, 200) diff --git a/setup.cfg b/setup.cfg index ebcc2441f7a7..f2efee8c2613 100644 --- a/setup.cfg +++ b/setup.cfg @@ -67,6 +67,7 @@ nova.api.v3.extensions = cells = nova.api.openstack.compute.plugins.v3.cells:Cells certificates = nova.api.openstack.compute.plugins.v3.certificates:Certificates config_drive = nova.api.openstack.compute.plugins.v3.config_drive:ConfigDrive + console_auth_tokens = nova.api.openstack.compute.plugins.v3.console_auth_tokens:ConsoleAuthTokens console_output = nova.api.openstack.compute.plugins.v3.console_output:ConsoleOutput consoles = nova.api.openstack.compute.plugins.v3.consoles:Consoles deferred_delete = nova.api.openstack.compute.plugins.v3.deferred_delete:DeferredDelete