Respect supplied arguments in novncproxy_base_url

In case an operator want to have NoVNC under a certain subpath instead
of the subdomain or custom port, they would need to instruct NoVNC
to establish WebSocket connection with a correct URI, which is passed
through `path` argument.

The implements parsing of novncproxy_base_url and appending token
to the request rather then re-writing request completely.

Implements: blueprint novnc-base-url-respect-extra-params
Change-Id: Ie900f4963a998222942c3d54a757ef1e625f7bf9
This commit is contained in:
Dmitriy Rabotyagov
2024-09-10 19:07:54 +02:00
parent 3c038645db
commit b063728254
4 changed files with 52 additions and 3 deletions

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -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.