Source code for ironic.common.vnc
# Copyright 2025 Red Hat, Inc.
#
# 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.
"""Functions for VNC graphical console drivers and novncproxy"""
import datetime
import secrets
from urllib import parse
from oslo_utils import timeutils
from ironic.common import exception
from ironic.conf import CONF
[docs]
def novnc_authorize(node):
"""Create and save a console token
A random token is created and is stored in the node
``driver_internal_info` along with creation time.
This is called by graphical console drivers when ``start_console`` is
called.
:param node: the node object
:returns: an authorized token
"""
token = secrets.token_urlsafe()
node.set_driver_internal_info('novnc_secret_token', token)
node.timestamp_driver_internal_info('novnc_secret_token_created')
node.save()
return token
[docs]
def novnc_unauthorize(node):
"""Clear any existing console token
:param node: the node object
"""
node.del_driver_internal_info('novnc_secret_token')
node.del_driver_internal_info('novnc_secret_token_created')
node.save()
[docs]
def novnc_validate(node, token):
"""Validate the token.
:param node: the node object
:param token: the token for the authorization
:returns: The ConsoleAuthToken object if valid
The token is valid if the token is in the database and the expires
time has not passed.
"""
if not token:
raise exception.NotAuthorized()
node_token = node.driver_internal_info.get('novnc_secret_token')
if not node_token:
# missing token info for node
raise exception.NotAuthorized()
if token != node_token:
# token doesn't match
raise exception.NotAuthorized()
if token_valid_until(node) < timeutils.utcnow():
# token has expired
raise exception.NotAuthorized()
[docs]
def token_valid_until(node):
"""Calculate when the token will expire
:param node: the node object
:returns: a datetime object representing expiration time
:raises: NotAuthorized if no timestamp is stored in the node
"""
token_created = node.driver_internal_info.get('novnc_secret_token_created')
if not token_created:
# missing token created timestamp for node
raise exception.NotAuthorized()
timeout = CONF.vnc.token_timeout
created_dt = datetime.datetime.strptime(token_created,
'%Y-%m-%dT%H:%M:%S.%f')
time_delta = datetime.timedelta(seconds=timeout)
return created_dt + time_delta
[docs]
def get_console(node):
"""Get the type and connection information about the console
:param node: the node object
:returns: A dict containing keys 'type', 'url'
"""
uuid = node.uuid
base_url = CONF.vnc.public_url
token = node.driver_internal_info.get('novnc_secret_token')
path = parse.quote(f"websockify?node={uuid}&token={token}")
url = f"{base_url}?path={path}"
return {'type': 'vnc', 'url': url}