diff --git a/nova/conf/vnc.py b/nova/conf/vnc.py index 11d262ab900e..fef2497353e6 100644 --- a/nova/conf/vnc.py +++ b/nova/conf/vnc.py @@ -75,6 +75,10 @@ instance and, by extension, the VNC sessions. If using noVNC >= 1.0.0, you should use ``vnc_lite.html`` instead of ``vnc_auto.html``. +You can also supply extra request arguments which will be passed to +the backend. This might be useful to move console URL to subpath, for example: +``http://127.0.0.1/novnc/vnc_auto.html?path=novnc`` + Related options: * novncproxy_host diff --git a/nova/objects/console_auth_token.py b/nova/objects/console_auth_token.py index 281d95549871..3be2f8f08241 100644 --- a/nova/objects/console_auth_token.py +++ b/nova/objects/console_auth_token.py @@ -75,9 +75,13 @@ class ConsoleAuthToken(base.NovaTimestampObject, base.NovaObject): # top-level 'token' query parameter was removed. The 'path' # parameter is supported in older noVNC versions, so it is # backward compatible. - qparams = {'path': '?token=%s' % self.token} - return '%s?%s' % (self.access_url_base, - urlparse.urlencode(qparams)) + parsed_base_url = urlparse.urlparse(self.access_url_base) + qparams = urlparse.parse_qs(parsed_base_url.query) + qpath = '%s?token=%s' % (qparams.get('path', [''])[0], + self.token) + qparams.update({'path': qpath}) + return parsed_base_url._replace( + query=urlparse.urlencode(qparams, doseq=True)).geturl() else: return '%s?token=%s' % (self.access_url_base, self.token) diff --git a/nova/tests/unit/objects/test_console_auth_token.py b/nova/tests/unit/objects/test_console_auth_token.py index 4fe906c41384..d7103d752cb3 100644 --- a/nova/tests/unit/objects/test_console_auth_token.py +++ b/nova/tests/unit/objects/test_console_auth_token.py @@ -99,6 +99,41 @@ class _TestConsoleAuthToken(object): def test_authorize_novnc(self): self._test_authorize('novnc') + @mock.patch('nova.db.main.api.console_auth_token_create') + def _test_access_novnc_custom_request( + self, url_base, url_expected, mock_create): + ttl = 10 + expires = timeutils.utcnow_ts() + ttl + db_dict = copy.deepcopy(fakes.fake_token_dict) + db_dict['access_url_base'] = url_base + db_dict['expires'] = expires + db_dict['console_type'] = 'novnc' + mock_create.return_value = db_dict + + obj = token_obj.ConsoleAuthToken( + context=self.context, + console_type=db_dict['console_type'], + host=fakes.fake_token_dict['host'], + port=fakes.fake_token_dict['port'], + instance_uuid=fakes.fake_token_dict['instance_uuid'], + access_url_base=url_base, + ) + + with mock.patch('uuid.uuid4', return_value=fakes.fake_token): + obj.authorize(ttl) + token_req = urlparse.quote_plus('token=%s' % fakes.fake_token) + expected_url = '%3F'.join([url_expected, token_req]) + self.assertEqual(expected_url, obj.access_url) + + def test_access_novnc_custom_path(self): + base_url = 'http://fake.url.fake/novnc/root.html?path=novnc' + self._test_access_novnc_custom_request(base_url, base_url) + + def test_access_novnc_random_req(self): + base_url = 'http://fake.url.fake/novnc/root.html?noop=true' + expected_url = 'http://fake.url.fake/novnc/root.html?noop=true&path=' + self._test_access_novnc_custom_request(base_url, expected_url) + @mock.patch('nova.db.main.api.console_auth_token_create') def test_authorize_duplicate_token(self, mock_create): mock_create.side_effect = DBDuplicateEntry() diff --git a/releasenotes/notes/novnc_respect_query-5a670b25a44e7fdc.yaml b/releasenotes/notes/novnc_respect_query-5a670b25a44e7fdc.yaml new file mode 100644 index 000000000000..05152a3f8a7b --- /dev/null +++ b/releasenotes/notes/novnc_respect_query-5a670b25a44e7fdc.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Option ``novncproxy_base_url`` does now respect supplied custom query + which might be used to move NoVNC to a subdirectory or pass an extra + argument to NoVNC.