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