Merge "Remove nova-consoleauth"
This commit is contained in:
commit
5fece42f9f
@ -17,7 +17,6 @@ import webob
|
|||||||
|
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
import nova.conf
|
import nova.conf
|
||||||
from nova.consoleauth import rpcapi as consoleauth_rpcapi
|
|
||||||
from nova import context as nova_context
|
from nova import context as nova_context
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
from nova import objects
|
from nova import objects
|
||||||
@ -27,9 +26,6 @@ CONF = nova.conf.CONF
|
|||||||
|
|
||||||
|
|
||||||
class ConsoleAuthTokensController(wsgi.Controller):
|
class ConsoleAuthTokensController(wsgi.Controller):
|
||||||
def __init__(self):
|
|
||||||
super(ConsoleAuthTokensController, self).__init__()
|
|
||||||
self._consoleauth_rpcapi = consoleauth_rpcapi.ConsoleAuthAPI()
|
|
||||||
|
|
||||||
def _show(self, req, id, rdp_only):
|
def _show(self, req, id, rdp_only):
|
||||||
"""Checks a console auth token and returns the related connect info."""
|
"""Checks a console auth token and returns the related connect info."""
|
||||||
@ -42,21 +38,19 @@ class ConsoleAuthTokensController(wsgi.Controller):
|
|||||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
connect_info = None
|
connect_info = None
|
||||||
if CONF.workarounds.enable_consoleauth:
|
|
||||||
connect_info = self._consoleauth_rpcapi.check_token(context, token)
|
results = nova_context.scatter_gather_skip_cell0(
|
||||||
else:
|
context, objects.ConsoleAuthToken.validate, token)
|
||||||
results = nova_context.scatter_gather_skip_cell0(
|
# NOTE(melwitt): Console token auths are stored in cell databases,
|
||||||
context, objects.ConsoleAuthToken.validate, token)
|
# but with only the token as a request param, we can't know which
|
||||||
# NOTE(melwitt): Console token auths are stored in cell databases,
|
# cell database contains the token's corresponding connection info.
|
||||||
# but with only the token as a request param, we can't know which
|
# So, we must query all cells for the info and we can break the
|
||||||
# cell database contains the token's corresponding connection info.
|
# loop as soon as we find a result because the token is associated
|
||||||
# So, we must query all cells for the info and we can break the
|
# with one instance, which can only be in one cell.
|
||||||
# loop as soon as we find a result because the token is associated
|
for result in results.values():
|
||||||
# with one instance, which can only be in one cell.
|
if not nova_context.is_cell_failure_sentinel(result):
|
||||||
for result in results.values():
|
connect_info = result.to_dict()
|
||||||
if not nova_context.is_cell_failure_sentinel(result):
|
break
|
||||||
connect_info = result.to_dict()
|
|
||||||
break
|
|
||||||
|
|
||||||
if not connect_info:
|
if not connect_info:
|
||||||
raise webob.exc.HTTPNotFound(explanation=_("Token not found"))
|
raise webob.exc.HTTPNotFound(explanation=_("Token not found"))
|
||||||
|
@ -53,9 +53,6 @@ class HostController(wsgi.Controller):
|
|||||||
| {'host_name': 'some.celly.host.name',
|
| {'host_name': 'some.celly.host.name',
|
||||||
| 'service': 'cells',
|
| 'service': 'cells',
|
||||||
| 'zone': 'internal'},
|
| 'zone': 'internal'},
|
||||||
| {'host_name': 'console1.host.com',
|
|
||||||
| 'service': 'consoleauth',
|
|
||||||
| 'zone': 'internal'},
|
|
||||||
| {'host_name': 'network1.host.com',
|
| {'host_name': 'network1.host.com',
|
||||||
| 'service': 'network',
|
| 'service': 'network',
|
||||||
| 'zone': 'internal'},
|
| 'zone': 'internal'},
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
# Copyright (c) 2012 OpenStack Foundation
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""VNC Console Proxy Server."""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
|
||||||
from oslo_reports import guru_meditation_report as gmr
|
|
||||||
from oslo_reports import opts as gmr_opts
|
|
||||||
|
|
||||||
import nova.conf
|
|
||||||
from nova import config
|
|
||||||
from nova.consoleauth import rpcapi
|
|
||||||
from nova import objects
|
|
||||||
from nova import service
|
|
||||||
from nova import version
|
|
||||||
|
|
||||||
CONF = nova.conf.CONF
|
|
||||||
LOG = logging.getLogger('nova.consoleauth')
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
config.parse_args(sys.argv)
|
|
||||||
logging.setup(CONF, "nova")
|
|
||||||
objects.register_all()
|
|
||||||
gmr_opts.set_defaults(CONF)
|
|
||||||
|
|
||||||
gmr.TextGuruMeditation.setup_autorun(version, conf=CONF)
|
|
||||||
|
|
||||||
LOG.warning('The nova-consoleauth service is deprecated as console token '
|
|
||||||
'authorization storage has moved from the nova-consoleauth '
|
|
||||||
'service backend to the database backend.')
|
|
||||||
|
|
||||||
server = service.Service.create(binary='nova-consoleauth',
|
|
||||||
topic=rpcapi.RPC_TOPIC)
|
|
||||||
service.serve(server)
|
|
||||||
service.wait()
|
|
@ -51,7 +51,6 @@ from nova.compute.utils import wrap_instance_event
|
|||||||
from nova.compute import vm_states
|
from nova.compute import vm_states
|
||||||
from nova import conductor
|
from nova import conductor
|
||||||
import nova.conf
|
import nova.conf
|
||||||
from nova.consoleauth import rpcapi as consoleauth_rpcapi
|
|
||||||
from nova import context as nova_context
|
from nova import context as nova_context
|
||||||
from nova import crypto
|
from nova import crypto
|
||||||
from nova.db import base
|
from nova.db import base
|
||||||
@ -259,7 +258,6 @@ class API(base.Base):
|
|||||||
self._placementclient = None # Lazy-load on first access.
|
self._placementclient = None # Lazy-load on first access.
|
||||||
self.security_group_api = (security_group_api or
|
self.security_group_api = (security_group_api or
|
||||||
openstack_driver.get_openstack_security_group_driver())
|
openstack_driver.get_openstack_security_group_driver())
|
||||||
self.consoleauth_rpcapi = consoleauth_rpcapi.ConsoleAuthAPI()
|
|
||||||
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
|
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
|
||||||
self.compute_task_api = conductor.ComputeTaskAPI()
|
self.compute_task_api = conductor.ComputeTaskAPI()
|
||||||
self.servicegroup_api = servicegroup.API()
|
self.servicegroup_api = servicegroup.API()
|
||||||
@ -2057,13 +2055,6 @@ class API(base.Base):
|
|||||||
instance.progress = 0
|
instance.progress = 0
|
||||||
instance.save()
|
instance.save()
|
||||||
|
|
||||||
if CONF.workarounds.enable_consoleauth:
|
|
||||||
# TODO(melwitt): Remove the conditions for running this line
|
|
||||||
# with cells v2, when consoleauth is no longer being used by
|
|
||||||
# cells v2, in Stein.
|
|
||||||
self.consoleauth_rpcapi.delete_tokens_for_instance(
|
|
||||||
context, instance.uuid)
|
|
||||||
|
|
||||||
if not instance.host and not may_have_ports_or_volumes:
|
if not instance.host and not may_have_ports_or_volumes:
|
||||||
try:
|
try:
|
||||||
with compute_utils.notify_about_instance_delete(
|
with compute_utils.notify_about_instance_delete(
|
||||||
@ -3789,18 +3780,6 @@ class API(base.Base):
|
|||||||
"""Get a url to an instance Console."""
|
"""Get a url to an instance Console."""
|
||||||
connect_info = self.compute_rpcapi.get_vnc_console(context,
|
connect_info = self.compute_rpcapi.get_vnc_console(context,
|
||||||
instance=instance, console_type=console_type)
|
instance=instance, console_type=console_type)
|
||||||
|
|
||||||
# TODO(melwitt): In Rocky, the compute manager puts the
|
|
||||||
# console authorization in the database in the above method.
|
|
||||||
# The following will be removed when everything has been
|
|
||||||
# converted to use the database, in Stein.
|
|
||||||
if CONF.workarounds.enable_consoleauth:
|
|
||||||
self.consoleauth_rpcapi.authorize_console(context,
|
|
||||||
connect_info['token'], console_type,
|
|
||||||
connect_info['host'], connect_info['port'],
|
|
||||||
connect_info['internal_access_path'], instance.uuid,
|
|
||||||
access_url=connect_info['access_url'])
|
|
||||||
|
|
||||||
return {'url': connect_info['access_url']}
|
return {'url': connect_info['access_url']}
|
||||||
|
|
||||||
@check_instance_host
|
@check_instance_host
|
||||||
@ -3810,17 +3789,6 @@ class API(base.Base):
|
|||||||
"""Get a url to an instance Console."""
|
"""Get a url to an instance Console."""
|
||||||
connect_info = self.compute_rpcapi.get_spice_console(context,
|
connect_info = self.compute_rpcapi.get_spice_console(context,
|
||||||
instance=instance, console_type=console_type)
|
instance=instance, console_type=console_type)
|
||||||
# TODO(melwitt): In Rocky, the compute manager puts the
|
|
||||||
# console authorization in the database in the above method.
|
|
||||||
# The following will be removed when everything has been
|
|
||||||
# converted to use the database, in Stein.
|
|
||||||
if CONF.workarounds.enable_consoleauth:
|
|
||||||
self.consoleauth_rpcapi.authorize_console(context,
|
|
||||||
connect_info['token'], console_type,
|
|
||||||
connect_info['host'], connect_info['port'],
|
|
||||||
connect_info['internal_access_path'], instance.uuid,
|
|
||||||
access_url=connect_info['access_url'])
|
|
||||||
|
|
||||||
return {'url': connect_info['access_url']}
|
return {'url': connect_info['access_url']}
|
||||||
|
|
||||||
@check_instance_host
|
@check_instance_host
|
||||||
@ -3830,17 +3798,6 @@ class API(base.Base):
|
|||||||
"""Get a url to an instance Console."""
|
"""Get a url to an instance Console."""
|
||||||
connect_info = self.compute_rpcapi.get_rdp_console(context,
|
connect_info = self.compute_rpcapi.get_rdp_console(context,
|
||||||
instance=instance, console_type=console_type)
|
instance=instance, console_type=console_type)
|
||||||
# TODO(melwitt): In Rocky, the compute manager puts the
|
|
||||||
# console authorization in the database in the above method.
|
|
||||||
# The following will be removed when everything has been
|
|
||||||
# converted to use the database, in Stein.
|
|
||||||
if CONF.workarounds.enable_consoleauth:
|
|
||||||
self.consoleauth_rpcapi.authorize_console(context,
|
|
||||||
connect_info['token'], console_type,
|
|
||||||
connect_info['host'], connect_info['port'],
|
|
||||||
connect_info['internal_access_path'], instance.uuid,
|
|
||||||
access_url=connect_info['access_url'])
|
|
||||||
|
|
||||||
return {'url': connect_info['access_url']}
|
return {'url': connect_info['access_url']}
|
||||||
|
|
||||||
@check_instance_host
|
@check_instance_host
|
||||||
@ -3850,17 +3807,6 @@ class API(base.Base):
|
|||||||
"""Get a url to a serial console."""
|
"""Get a url to a serial console."""
|
||||||
connect_info = self.compute_rpcapi.get_serial_console(context,
|
connect_info = self.compute_rpcapi.get_serial_console(context,
|
||||||
instance=instance, console_type=console_type)
|
instance=instance, console_type=console_type)
|
||||||
|
|
||||||
# TODO(melwitt): In Rocky, the compute manager puts the
|
|
||||||
# console authorization in the database in the above method.
|
|
||||||
# The following will be removed when everything has been
|
|
||||||
# converted to use the database, in Stein.
|
|
||||||
if CONF.workarounds.enable_consoleauth:
|
|
||||||
self.consoleauth_rpcapi.authorize_console(context,
|
|
||||||
connect_info['token'], console_type,
|
|
||||||
connect_info['host'], connect_info['port'],
|
|
||||||
connect_info['internal_access_path'], instance.uuid,
|
|
||||||
access_url=connect_info['access_url'])
|
|
||||||
return {'url': connect_info['access_url']}
|
return {'url': connect_info['access_url']}
|
||||||
|
|
||||||
@check_instance_host
|
@check_instance_host
|
||||||
@ -3870,16 +3816,6 @@ class API(base.Base):
|
|||||||
"""Get a url to a MKS console."""
|
"""Get a url to a MKS console."""
|
||||||
connect_info = self.compute_rpcapi.get_mks_console(context,
|
connect_info = self.compute_rpcapi.get_mks_console(context,
|
||||||
instance=instance, console_type=console_type)
|
instance=instance, console_type=console_type)
|
||||||
# TODO(melwitt): In Rocky, the compute manager puts the
|
|
||||||
# console authorization in the database in the above method.
|
|
||||||
# The following will be removed when everything has been
|
|
||||||
# converted to use the database, in Stein.
|
|
||||||
if CONF.workarounds.enable_consoleauth:
|
|
||||||
self.consoleauth_rpcapi.authorize_console(context,
|
|
||||||
connect_info['token'], console_type,
|
|
||||||
connect_info['host'], connect_info['port'],
|
|
||||||
connect_info['internal_access_path'], instance.uuid,
|
|
||||||
access_url=connect_info['access_url'])
|
|
||||||
return {'url': connect_info['access_url']}
|
return {'url': connect_info['access_url']}
|
||||||
|
|
||||||
@check_instance_host
|
@check_instance_host
|
||||||
@ -4437,15 +4373,6 @@ class API(base.Base):
|
|||||||
self._record_action_start(context, instance,
|
self._record_action_start(context, instance,
|
||||||
instance_actions.LIVE_MIGRATION)
|
instance_actions.LIVE_MIGRATION)
|
||||||
|
|
||||||
# TODO(melwitt): In Rocky, we optionally store console authorizations
|
|
||||||
# in both the consoleauth service and the database while
|
|
||||||
# we convert to using the database. Remove the condition for running
|
|
||||||
# this line with cells v2, when consoleauth is no longer being used by
|
|
||||||
# cells v2, in Stein.
|
|
||||||
if CONF.workarounds.enable_consoleauth:
|
|
||||||
self.consoleauth_rpcapi.delete_tokens_for_instance(
|
|
||||||
context, instance.uuid)
|
|
||||||
|
|
||||||
# NOTE(sbauza): Force is a boolean by the new related API version
|
# NOTE(sbauza): Force is a boolean by the new related API version
|
||||||
if force is False and host_name:
|
if force is False and host_name:
|
||||||
# Unset the host to make sure we call the scheduler
|
# Unset the host to make sure we call the scheduler
|
||||||
|
@ -1283,7 +1283,7 @@ registered in the database as an enabled service. Sometimes it can be useful
|
|||||||
to register new compute services in disabled state and then enabled them at a
|
to register new compute services in disabled state and then enabled them at a
|
||||||
later point in time. This option only sets this behavior for nova-compute
|
later point in time. This option only sets this behavior for nova-compute
|
||||||
services, it does not auto-disable other services like nova-conductor,
|
services, it does not auto-disable other services like nova-conductor,
|
||||||
nova-scheduler, nova-consoleauth, or nova-osapi_compute.
|
nova-scheduler, or nova-osapi_compute.
|
||||||
|
|
||||||
Possible values:
|
Possible values:
|
||||||
|
|
||||||
|
@ -32,10 +32,6 @@ The lifetime of a console auth token (in seconds).
|
|||||||
A console auth token is used in authorizing console access for a user.
|
A console auth token is used in authorizing console access for a user.
|
||||||
Once the auth token time to live count has elapsed, the token is
|
Once the auth token time to live count has elapsed, the token is
|
||||||
considered expired. Expired tokens are then deleted.
|
considered expired. Expired tokens are then deleted.
|
||||||
|
|
||||||
Related options:
|
|
||||||
|
|
||||||
* ``[workarounds]/enable_consoleauth``
|
|
||||||
""")
|
""")
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -108,24 +108,6 @@ Console RPC API version cap.
|
|||||||
|
|
||||||
Possible values:
|
Possible values:
|
||||||
|
|
||||||
* By default send the latest version the client knows about
|
|
||||||
* A string representing a version number in the format 'N.N';
|
|
||||||
for example, possible values might be '1.12' or '2.0'.
|
|
||||||
* An OpenStack release name, in lower case, such as 'mitaka' or
|
|
||||||
'liberty'.
|
|
||||||
"""),
|
|
||||||
cfg.StrOpt('consoleauth',
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
deprecated_since='18.0.0',
|
|
||||||
deprecated_reason="""
|
|
||||||
The nova-consoleauth service was deprecated in 18.0.0 (Rocky) and will be
|
|
||||||
removed in an upcoming release.
|
|
||||||
""",
|
|
||||||
help="""
|
|
||||||
Consoleauth RPC API version cap.
|
|
||||||
|
|
||||||
Possible values:
|
|
||||||
|
|
||||||
* By default send the latest version the client knows about
|
* By default send the latest version the client knows about
|
||||||
* A string representing a version number in the format 'N.N';
|
* A string representing a version number in the format 'N.N';
|
||||||
for example, possible values might be '1.12' or '2.0'.
|
for example, possible values might be '1.12' or '2.0'.
|
||||||
|
@ -154,38 +154,6 @@ Related options:
|
|||||||
compute service to the scheduler service.
|
compute service to the scheduler service.
|
||||||
"""),
|
"""),
|
||||||
|
|
||||||
cfg.BoolOpt(
|
|
||||||
'enable_consoleauth',
|
|
||||||
default=False,
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
deprecated_since="18.0.0",
|
|
||||||
deprecated_reason="""
|
|
||||||
This option has been added as deprecated originally because it is used
|
|
||||||
for avoiding a upgrade issue and it will not be used in the future.
|
|
||||||
See the help text for more details.
|
|
||||||
""",
|
|
||||||
help="""
|
|
||||||
Enable the consoleauth service to avoid resetting unexpired consoles.
|
|
||||||
|
|
||||||
Console token authorizations have moved from the ``nova-consoleauth`` service
|
|
||||||
to the database, so all new consoles will be supported by the database backend.
|
|
||||||
With this, consoles that existed before database backend support will be reset.
|
|
||||||
For most operators, this should be a minimal disruption as the default TTL of a
|
|
||||||
console token is 10 minutes.
|
|
||||||
|
|
||||||
Operators that have much longer token TTL configured or otherwise wish to avoid
|
|
||||||
immediately resetting all existing consoles can enable this flag to continue
|
|
||||||
using the ``nova-consoleauth`` service in addition to the database backend.
|
|
||||||
Once all of the old ``nova-consoleauth`` supported console tokens have expired,
|
|
||||||
this flag should be disabled. For example, if a deployment has configured a
|
|
||||||
token TTL of one hour, the operator may disable the flag, one hour after
|
|
||||||
deploying the new code during an upgrade.
|
|
||||||
|
|
||||||
Related options:
|
|
||||||
|
|
||||||
* ``[consoleauth]/token_ttl``
|
|
||||||
"""),
|
|
||||||
|
|
||||||
cfg.BoolOpt(
|
cfg.BoolOpt(
|
||||||
'enable_numa_live_migration',
|
'enable_numa_live_migration',
|
||||||
default=False,
|
default=False,
|
||||||
|
@ -30,7 +30,6 @@ import websockify
|
|||||||
|
|
||||||
from nova.compute import rpcapi as compute_rpcapi
|
from nova.compute import rpcapi as compute_rpcapi
|
||||||
import nova.conf
|
import nova.conf
|
||||||
from nova.consoleauth import rpcapi as consoleauth_rpcapi
|
|
||||||
from nova import context
|
from nova import context
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
@ -125,14 +124,8 @@ class NovaProxyRequestHandlerBase(object):
|
|||||||
str(port),
|
str(port),
|
||||||
console_type)
|
console_type)
|
||||||
|
|
||||||
def _get_connect_info_consoleauth(self, ctxt, token):
|
def _get_connect_info(self, ctxt, token):
|
||||||
# NOTE(PaulMurray) consoleauth check_token() validates the token
|
"""Validate the token and get the connect info."""
|
||||||
# and does an rpc to compute manager to check the console port
|
|
||||||
# is correct.
|
|
||||||
rpcapi = consoleauth_rpcapi.ConsoleAuthAPI()
|
|
||||||
return rpcapi.check_token(ctxt, token=token)
|
|
||||||
|
|
||||||
def _get_connect_info_database(self, ctxt, token):
|
|
||||||
# NOTE(PaulMurray) ConsoleAuthToken.validate validates the token.
|
# NOTE(PaulMurray) ConsoleAuthToken.validate validates the token.
|
||||||
# We call the compute manager directly to check the console port
|
# We call the compute manager directly to check the console port
|
||||||
# is correct.
|
# is correct.
|
||||||
@ -147,25 +140,6 @@ class NovaProxyRequestHandlerBase(object):
|
|||||||
|
|
||||||
return connect_info
|
return connect_info
|
||||||
|
|
||||||
def _get_connect_info(self, ctxt, token):
|
|
||||||
"""Validate the token and get the connect info."""
|
|
||||||
connect_info = None
|
|
||||||
|
|
||||||
# NOTE(melwitt): If consoleauth is enabled to aid in transitioning
|
|
||||||
# to the database backend, check it first before falling back to
|
|
||||||
# the database. Tokens that existed pre-database-backend will
|
|
||||||
# reside in the consoleauth service storage.
|
|
||||||
if CONF.workarounds.enable_consoleauth:
|
|
||||||
connect_info = self._get_connect_info_consoleauth(ctxt, token)
|
|
||||||
# If consoleauth is enabled to aid in transitioning to the database
|
|
||||||
# backend and we didn't find a token in the consoleauth service
|
|
||||||
# storage, check the database for a token because it's probably a
|
|
||||||
# post-database-backend token, which are stored in the database.
|
|
||||||
if not connect_info:
|
|
||||||
connect_info = self._get_connect_info_database(ctxt, token)
|
|
||||||
|
|
||||||
return connect_info
|
|
||||||
|
|
||||||
def new_websocket_client(self):
|
def new_websocket_client(self):
|
||||||
"""Called after a new WebSocket connection has been established."""
|
"""Called after a new WebSocket connection has been established."""
|
||||||
# Reopen the eventlet hub to make sure we don't share an epoll
|
# Reopen the eventlet hub to make sure we don't share an epoll
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# Copyright (c) 2012 OpenStack Foundation
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""Module to authenticate Consoles."""
|
|
@ -1,136 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# Copyright (c) 2012 OpenStack Foundation
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""Auth Components for Consoles."""
|
|
||||||
|
|
||||||
import time
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
|
||||||
import oslo_messaging as messaging
|
|
||||||
from oslo_serialization import jsonutils
|
|
||||||
|
|
||||||
from nova import cache_utils
|
|
||||||
from nova.compute import rpcapi as compute_rpcapi
|
|
||||||
import nova.conf
|
|
||||||
from nova import context as nova_context
|
|
||||||
from nova import manager
|
|
||||||
from nova import objects
|
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
CONF = nova.conf.CONF
|
|
||||||
|
|
||||||
|
|
||||||
class ConsoleAuthManager(manager.Manager):
|
|
||||||
"""Manages token based authentication."""
|
|
||||||
|
|
||||||
target = messaging.Target(version='2.1')
|
|
||||||
|
|
||||||
def __init__(self, scheduler_driver=None, *args, **kwargs):
|
|
||||||
super(ConsoleAuthManager, self).__init__(service_name='consoleauth',
|
|
||||||
*args, **kwargs)
|
|
||||||
self._mc = None
|
|
||||||
self._mc_instance = None
|
|
||||||
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def mc(self):
|
|
||||||
if self._mc is None:
|
|
||||||
self._mc = cache_utils.get_client(CONF.consoleauth.token_ttl)
|
|
||||||
return self._mc
|
|
||||||
|
|
||||||
@property
|
|
||||||
def mc_instance(self):
|
|
||||||
if self._mc_instance is None:
|
|
||||||
self._mc_instance = cache_utils.get_client()
|
|
||||||
return self._mc_instance
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
LOG.info('Reloading compute RPC API')
|
|
||||||
compute_rpcapi.LAST_VERSION = None
|
|
||||||
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
|
|
||||||
|
|
||||||
def _get_tokens_for_instance(self, instance_uuid):
|
|
||||||
tokens_str = self.mc_instance.get(instance_uuid.encode('UTF-8'))
|
|
||||||
if not tokens_str:
|
|
||||||
tokens = []
|
|
||||||
else:
|
|
||||||
tokens = jsonutils.loads(tokens_str)
|
|
||||||
return tokens
|
|
||||||
|
|
||||||
def authorize_console(self, context, token, console_type, host, port,
|
|
||||||
internal_access_path, instance_uuid,
|
|
||||||
access_url=None):
|
|
||||||
|
|
||||||
token_dict = {'token': token,
|
|
||||||
'instance_uuid': instance_uuid,
|
|
||||||
'console_type': console_type,
|
|
||||||
'host': host,
|
|
||||||
'port': port,
|
|
||||||
'internal_access_path': internal_access_path,
|
|
||||||
'access_url': access_url,
|
|
||||||
'last_activity_at': time.time()}
|
|
||||||
data = jsonutils.dumps(token_dict)
|
|
||||||
|
|
||||||
self.mc.set(token.encode('UTF-8'), data)
|
|
||||||
tokens = self._get_tokens_for_instance(instance_uuid)
|
|
||||||
|
|
||||||
# Remove the expired tokens from cache.
|
|
||||||
token_values = self.mc.get_multi(
|
|
||||||
[tok.encode('UTF-8') for tok in tokens])
|
|
||||||
tokens = [name for name, value in zip(tokens, token_values)
|
|
||||||
if value is not None]
|
|
||||||
tokens.append(token)
|
|
||||||
|
|
||||||
self.mc_instance.set(instance_uuid.encode('UTF-8'),
|
|
||||||
jsonutils.dumps(tokens))
|
|
||||||
|
|
||||||
LOG.info("Received Token: %(token)s, %(token_dict)s",
|
|
||||||
{'token': token, 'token_dict': token_dict})
|
|
||||||
|
|
||||||
def _validate_token(self, context, token):
|
|
||||||
instance_uuid = token['instance_uuid']
|
|
||||||
if instance_uuid is None:
|
|
||||||
return False
|
|
||||||
|
|
||||||
mapping = objects.InstanceMapping.get_by_instance_uuid(context,
|
|
||||||
instance_uuid)
|
|
||||||
with nova_context.target_cell(context, mapping.cell_mapping) as cctxt:
|
|
||||||
instance = objects.Instance.get_by_uuid(cctxt, instance_uuid)
|
|
||||||
|
|
||||||
return self.compute_rpcapi.validate_console_port(
|
|
||||||
cctxt,
|
|
||||||
instance,
|
|
||||||
token['port'],
|
|
||||||
token['console_type'])
|
|
||||||
|
|
||||||
def check_token(self, context, token):
|
|
||||||
token_str = self.mc.get(token.encode('UTF-8'))
|
|
||||||
token_valid = (token_str is not None)
|
|
||||||
LOG.info("Checking Token: %(token)s, %(token_valid)s",
|
|
||||||
{'token': token, 'token_valid': token_valid})
|
|
||||||
if token_valid:
|
|
||||||
token = jsonutils.loads(token_str)
|
|
||||||
if self._validate_token(context, token):
|
|
||||||
return token
|
|
||||||
|
|
||||||
def delete_tokens_for_instance(self, context, instance_uuid):
|
|
||||||
tokens = self._get_tokens_for_instance(instance_uuid)
|
|
||||||
if tokens:
|
|
||||||
self.mc.delete_multi(
|
|
||||||
[tok.encode('UTF-8') for tok in tokens])
|
|
||||||
self.mc_instance.delete(instance_uuid.encode('UTF-8'))
|
|
@ -1,103 +0,0 @@
|
|||||||
# Copyright 2013 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.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Client side of the consoleauth RPC API.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import oslo_messaging as messaging
|
|
||||||
|
|
||||||
import nova.conf
|
|
||||||
from nova import profiler
|
|
||||||
from nova import rpc
|
|
||||||
|
|
||||||
CONF = nova.conf.CONF
|
|
||||||
RPC_TOPIC = 'consoleauth'
|
|
||||||
|
|
||||||
|
|
||||||
@profiler.trace_cls("rpc")
|
|
||||||
class ConsoleAuthAPI(object):
|
|
||||||
'''Client side of the consoleauth rpc API.
|
|
||||||
|
|
||||||
API version history:
|
|
||||||
|
|
||||||
* 1.0 - Initial version.
|
|
||||||
* 1.1 - Added get_backdoor_port()
|
|
||||||
* 1.2 - Added instance_uuid to authorize_console, and
|
|
||||||
delete_tokens_for_instance
|
|
||||||
|
|
||||||
... Grizzly and Havana support message version 1.2. So, any changes
|
|
||||||
to existing methods in 2.x after that point should be done such that
|
|
||||||
they can handle the version_cap being set to 1.2.
|
|
||||||
|
|
||||||
* 2.0 - Major API rev for Icehouse
|
|
||||||
|
|
||||||
... Icehouse and Juno support message version 2.0. So, any changes to
|
|
||||||
existing methods in 2.x after that point should be done such that they
|
|
||||||
can handle the version_cap being set to 2.0.
|
|
||||||
|
|
||||||
* 2.1 - Added access_url to authorize_console
|
|
||||||
|
|
||||||
... Kilo, Liberty, Mitaka, Newton, and Ocata support message version
|
|
||||||
2.1. So, any changes to existing methods in 2.x after that point should
|
|
||||||
be done such that they can handle the version_cap being set to 2.1.
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
VERSION_ALIASES = {
|
|
||||||
'grizzly': '1.2',
|
|
||||||
'havana': '1.2',
|
|
||||||
'icehouse': '2.0',
|
|
||||||
'juno': '2.0',
|
|
||||||
'kilo': '2.1',
|
|
||||||
'liberty': '2.1',
|
|
||||||
'mitaka': '2.1',
|
|
||||||
'newton': '2.1',
|
|
||||||
'ocata': '2.1',
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super(ConsoleAuthAPI, self).__init__()
|
|
||||||
target = messaging.Target(topic=RPC_TOPIC, version='2.1')
|
|
||||||
version_cap = self.VERSION_ALIASES.get(CONF.upgrade_levels.consoleauth,
|
|
||||||
CONF.upgrade_levels.consoleauth)
|
|
||||||
self.client = rpc.get_client(target, version_cap=version_cap)
|
|
||||||
|
|
||||||
def authorize_console(self, ctxt, token, console_type, host, port,
|
|
||||||
internal_access_path, instance_uuid,
|
|
||||||
access_url):
|
|
||||||
# The remote side doesn't return anything, but we want to block
|
|
||||||
# until it completes.'
|
|
||||||
msg_args = dict(token=token, console_type=console_type,
|
|
||||||
host=host, port=port,
|
|
||||||
internal_access_path=internal_access_path,
|
|
||||||
instance_uuid=instance_uuid,
|
|
||||||
access_url=access_url)
|
|
||||||
version = '2.1'
|
|
||||||
if not self.client.can_send_version('2.1'):
|
|
||||||
version = '2.0'
|
|
||||||
del msg_args['access_url']
|
|
||||||
|
|
||||||
cctxt = self.client.prepare(version=version)
|
|
||||||
return cctxt.call(ctxt, 'authorize_console', **msg_args)
|
|
||||||
|
|
||||||
def check_token(self, ctxt, token):
|
|
||||||
cctxt = self.client.prepare()
|
|
||||||
return cctxt.call(ctxt, 'check_token', token=token)
|
|
||||||
|
|
||||||
def delete_tokens_for_instance(self, ctxt, instance_uuid):
|
|
||||||
cctxt = self.client.prepare()
|
|
||||||
return cctxt.cast(ctxt,
|
|
||||||
'delete_tokens_for_instance',
|
|
||||||
instance_uuid=instance_uuid)
|
|
@ -768,6 +768,8 @@ class NotificationSource(BaseNovaEnum):
|
|||||||
CONDUCTOR = 'nova-conductor'
|
CONDUCTOR = 'nova-conductor'
|
||||||
SCHEDULER = 'nova-scheduler'
|
SCHEDULER = 'nova-scheduler'
|
||||||
NETWORK = 'nova-network'
|
NETWORK = 'nova-network'
|
||||||
|
# TODO(stephenfin): Remove 'CONSOLEAUTH' when 'NotificationPublisher' is
|
||||||
|
# updated to version 3.0
|
||||||
CONSOLEAUTH = 'nova-consoleauth'
|
CONSOLEAUTH = 'nova-consoleauth'
|
||||||
# TODO(stephenfin): Remove when 'NotificationPublisher' object version is
|
# TODO(stephenfin): Remove when 'NotificationPublisher' object version is
|
||||||
# bumped to 3.0
|
# bumped to 3.0
|
||||||
|
@ -55,7 +55,6 @@ CONF = nova.conf.CONF
|
|||||||
SERVICE_MANAGERS = {
|
SERVICE_MANAGERS = {
|
||||||
'nova-compute': 'nova.compute.manager.ComputeManager',
|
'nova-compute': 'nova.compute.manager.ComputeManager',
|
||||||
'nova-console': 'nova.console.manager.ConsoleProxyManager',
|
'nova-console': 'nova.console.manager.ConsoleProxyManager',
|
||||||
'nova-consoleauth': 'nova.consoleauth.manager.ConsoleAuthManager',
|
|
||||||
'nova-conductor': 'nova.conductor.manager.ConductorManager',
|
'nova-conductor': 'nova.conductor.manager.ConductorManager',
|
||||||
'nova-metadata': 'nova.api.manager.MetadataManager',
|
'nova-metadata': 'nova.api.manager.MetadataManager',
|
||||||
'nova-scheduler': 'nova.scheduler.manager.SchedulerManager',
|
'nova-scheduler': 'nova.scheduler.manager.SchedulerManager',
|
||||||
|
@ -15,21 +15,18 @@
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
|
||||||
import ddt
|
|
||||||
import mock
|
import mock
|
||||||
import webob
|
import webob
|
||||||
|
|
||||||
from nova.api.openstack import api_version_request
|
from nova.api.openstack import api_version_request
|
||||||
from nova.api.openstack.compute import console_auth_tokens \
|
from nova.api.openstack.compute import console_auth_tokens \
|
||||||
as console_auth_tokens_v21
|
as console_auth_tokens_v21
|
||||||
from nova.consoleauth import rpcapi as consoleauth_rpcapi
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import objects
|
from nova import objects
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests.unit.api.openstack import fakes
|
from nova.tests.unit.api.openstack import fakes
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
|
||||||
class ConsoleAuthTokensExtensionTestV21(test.NoDBTestCase):
|
class ConsoleAuthTokensExtensionTestV21(test.NoDBTestCase):
|
||||||
controller_class = console_auth_tokens_v21
|
controller_class = console_auth_tokens_v21
|
||||||
|
|
||||||
@ -52,86 +49,35 @@ class ConsoleAuthTokensExtensionTestV21(test.NoDBTestCase):
|
|||||||
self.req = fakes.HTTPRequest.blank('', use_admin_context=True)
|
self.req = fakes.HTTPRequest.blank('', use_admin_context=True)
|
||||||
self.context = self.req.environ['nova.context']
|
self.context = self.req.environ['nova.context']
|
||||||
|
|
||||||
@ddt.data(True, False)
|
|
||||||
@mock.patch('nova.objects.ConsoleAuthToken.validate',
|
@mock.patch('nova.objects.ConsoleAuthToken.validate',
|
||||||
return_value=objects.ConsoleAuthToken(
|
return_value=objects.ConsoleAuthToken(
|
||||||
instance_uuid=fakes.FAKE_UUID, host='fake_host',
|
instance_uuid=fakes.FAKE_UUID, host='fake_host',
|
||||||
port='1234', internal_access_path='fake_access_path',
|
port='1234', internal_access_path='fake_access_path',
|
||||||
console_type='rdp-html5', token=fakes.FAKE_UUID))
|
console_type='rdp-html5', token=fakes.FAKE_UUID))
|
||||||
@mock.patch.object(consoleauth_rpcapi.ConsoleAuthAPI, 'check_token',
|
def test_get_console_connect_info(self, mock_validate):
|
||||||
return_value={
|
|
||||||
'instance_uuid': fakes.FAKE_UUID,
|
|
||||||
'host': 'fake_host',
|
|
||||||
'port': '1234',
|
|
||||||
'internal_access_path': 'fake_access_path',
|
|
||||||
'console_type': 'rdp-html5'})
|
|
||||||
def test_get_console_connect_info(self, enable_consoleauth,
|
|
||||||
mock_check_token, mock_validate):
|
|
||||||
self.flags(enable_consoleauth=enable_consoleauth, group='workarounds')
|
|
||||||
output = self.controller.show(self.req, fakes.FAKE_UUID)
|
output = self.controller.show(self.req, fakes.FAKE_UUID)
|
||||||
if enable_consoleauth:
|
self.assertEqual(self._EXPECTED_OUTPUT_DB, output)
|
||||||
self.assertEqual(self._EXPECTED_OUTPUT, output)
|
mock_validate.assert_called_once_with(self.context, fakes.FAKE_UUID)
|
||||||
mock_check_token.assert_called_once_with(self.context,
|
|
||||||
fakes.FAKE_UUID)
|
|
||||||
mock_validate.assert_not_called()
|
|
||||||
else:
|
|
||||||
self.assertEqual(self._EXPECTED_OUTPUT_DB, output)
|
|
||||||
mock_validate.assert_called_once_with(self.context,
|
|
||||||
fakes.FAKE_UUID)
|
|
||||||
mock_check_token.assert_not_called()
|
|
||||||
|
|
||||||
@ddt.data(True, False)
|
|
||||||
@mock.patch('nova.objects.ConsoleAuthToken.validate',
|
@mock.patch('nova.objects.ConsoleAuthToken.validate',
|
||||||
side_effect=exception.InvalidToken(token='***'))
|
side_effect=exception.InvalidToken(token='***'))
|
||||||
@mock.patch.object(consoleauth_rpcapi.ConsoleAuthAPI, 'check_token',
|
def test_get_console_connect_info_token_not_found(self, mock_validate):
|
||||||
return_value=None)
|
|
||||||
def test_get_console_connect_info_token_not_found(self, enable_consoleauth,
|
|
||||||
mock_check_token,
|
|
||||||
mock_validate):
|
|
||||||
self.flags(enable_consoleauth=enable_consoleauth, group='workarounds')
|
|
||||||
self.assertRaises(webob.exc.HTTPNotFound,
|
self.assertRaises(webob.exc.HTTPNotFound,
|
||||||
self.controller.show, self.req, fakes.FAKE_UUID)
|
self.controller.show, self.req, fakes.FAKE_UUID)
|
||||||
if enable_consoleauth:
|
mock_validate.assert_called_once_with(self.context, fakes.FAKE_UUID)
|
||||||
mock_check_token.assert_called_once_with(self.context,
|
|
||||||
fakes.FAKE_UUID)
|
|
||||||
mock_validate.assert_not_called()
|
|
||||||
else:
|
|
||||||
mock_validate.assert_called_once_with(self.context,
|
|
||||||
fakes.FAKE_UUID)
|
|
||||||
mock_check_token.assert_not_called()
|
|
||||||
|
|
||||||
@ddt.data(True, False)
|
|
||||||
@mock.patch('nova.objects.ConsoleAuthToken.validate',
|
@mock.patch('nova.objects.ConsoleAuthToken.validate',
|
||||||
return_value=objects.ConsoleAuthToken(
|
return_value=objects.ConsoleAuthToken(
|
||||||
instance_uuid=fakes.FAKE_UUID, host='fake_host',
|
instance_uuid=fakes.FAKE_UUID, host='fake_host',
|
||||||
port='1234', internal_access_path='fake_access_path',
|
port='1234', internal_access_path='fake_access_path',
|
||||||
console_type='unauthorized_console_type',
|
console_type='unauthorized_console_type',
|
||||||
token=fakes.FAKE_UUID))
|
token=fakes.FAKE_UUID))
|
||||||
@mock.patch.object(consoleauth_rpcapi.ConsoleAuthAPI, 'check_token',
|
def test_get_console_connect_info_nonrdp_console_type(self, mock_validate):
|
||||||
return_value={
|
|
||||||
'instance_uuid': fakes.FAKE_UUID,
|
|
||||||
'host': 'fake_host',
|
|
||||||
'port': '1234',
|
|
||||||
'internal_access_path': 'fake_access_path',
|
|
||||||
'console_type': 'unauthorized_console_type'})
|
|
||||||
def test_get_console_connect_info_nonrdp_console_type(self,
|
|
||||||
enable_consoleauth,
|
|
||||||
mock_check_token,
|
|
||||||
mock_validate):
|
|
||||||
self.flags(enable_consoleauth=enable_consoleauth, group='workarounds')
|
|
||||||
self.assertRaises(webob.exc.HTTPUnauthorized,
|
self.assertRaises(webob.exc.HTTPUnauthorized,
|
||||||
self.controller.show, self.req, fakes.FAKE_UUID)
|
self.controller.show, self.req, fakes.FAKE_UUID)
|
||||||
if enable_consoleauth:
|
mock_validate.assert_called_once_with(self.context, fakes.FAKE_UUID)
|
||||||
mock_check_token.assert_called_once_with(self.context,
|
|
||||||
fakes.FAKE_UUID)
|
|
||||||
mock_validate.assert_not_called()
|
|
||||||
else:
|
|
||||||
mock_validate.assert_called_once_with(self.context,
|
|
||||||
fakes.FAKE_UUID)
|
|
||||||
mock_check_token.assert_not_called()
|
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
|
||||||
class ConsoleAuthTokensExtensionTestV231(ConsoleAuthTokensExtensionTestV21):
|
class ConsoleAuthTokensExtensionTestV231(ConsoleAuthTokensExtensionTestV21):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -139,30 +85,13 @@ class ConsoleAuthTokensExtensionTestV231(ConsoleAuthTokensExtensionTestV21):
|
|||||||
self.req.api_version_request = api_version_request.APIVersionRequest(
|
self.req.api_version_request = api_version_request.APIVersionRequest(
|
||||||
'2.31')
|
'2.31')
|
||||||
|
|
||||||
@ddt.data(True, False)
|
@mock.patch('nova.objects.ConsoleAuthToken.validate',
|
||||||
@mock.patch('nova.objects.ConsoleAuthToken.validate')
|
return_value = objects.ConsoleAuthToken(
|
||||||
@mock.patch.object(consoleauth_rpcapi.ConsoleAuthAPI, 'check_token')
|
instance_uuid=fakes.FAKE_UUID, host='fake_host',
|
||||||
def test_get_console_connect_info_nonrdp_console_type(self,
|
port='1234', internal_access_path='fake_access_path',
|
||||||
enable_consoleauth,
|
console_type='webmks',
|
||||||
mock_check,
|
token=fakes.FAKE_UUID))
|
||||||
mock_validate):
|
def test_get_console_connect_info_nonrdp_console_type(self, mock_validate):
|
||||||
self.flags(enable_consoleauth=enable_consoleauth, group='workarounds')
|
|
||||||
mock_validate.return_value = objects.ConsoleAuthToken(
|
|
||||||
instance_uuid=fakes.FAKE_UUID, host='fake_host', port='1234',
|
|
||||||
internal_access_path='fake_access_path', console_type='webmks',
|
|
||||||
token=fakes.FAKE_UUID)
|
|
||||||
mock_check.return_value = {'instance_uuid': fakes.FAKE_UUID,
|
|
||||||
'host': 'fake_host',
|
|
||||||
'port': '1234',
|
|
||||||
'internal_access_path': 'fake_access_path',
|
|
||||||
'console_type': 'webmks'}
|
|
||||||
output = self.controller.show(self.req, fakes.FAKE_UUID)
|
output = self.controller.show(self.req, fakes.FAKE_UUID)
|
||||||
if enable_consoleauth:
|
self.assertEqual(self._EXPECTED_OUTPUT_DB, output)
|
||||||
self.assertEqual(self._EXPECTED_OUTPUT, output)
|
mock_validate.assert_called_once_with(self.context, fakes.FAKE_UUID)
|
||||||
mock_check.assert_called_once_with(self.context, fakes.FAKE_UUID)
|
|
||||||
mock_validate.assert_not_called()
|
|
||||||
else:
|
|
||||||
self.assertEqual(self._EXPECTED_OUTPUT_DB, output)
|
|
||||||
mock_validate.assert_called_once_with(self.context,
|
|
||||||
fakes.FAKE_UUID)
|
|
||||||
mock_check.assert_not_called()
|
|
||||||
|
@ -10153,12 +10153,8 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
self.assertRaises(exception.InvalidVolume,
|
self.assertRaises(exception.InvalidVolume,
|
||||||
self.compute_api.rescue, self.context, instance)
|
self.compute_api.rescue, self.context, instance)
|
||||||
|
|
||||||
@ddt.data(True, False)
|
|
||||||
@mock.patch.object(compute_rpcapi.ComputeAPI, 'get_vnc_console')
|
@mock.patch.object(compute_rpcapi.ComputeAPI, 'get_vnc_console')
|
||||||
@mock.patch.object(compute.consoleauth_rpcapi.ConsoleAuthAPI,
|
def test_vnc_console(self, mock_get):
|
||||||
'authorize_console')
|
|
||||||
def test_vnc_console(self, enable_consoleauth, mock_auth, mock_get):
|
|
||||||
self.flags(enable_consoleauth=enable_consoleauth, group='workarounds')
|
|
||||||
# Make sure we can a vnc console for an instance.
|
# Make sure we can a vnc console for an instance.
|
||||||
|
|
||||||
fake_instance = self._fake_instance(
|
fake_instance = self._fake_instance(
|
||||||
@ -10181,14 +10177,6 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
mock_get.assert_called_once_with(
|
mock_get.assert_called_once_with(
|
||||||
self.context, instance=fake_instance,
|
self.context, instance=fake_instance,
|
||||||
console_type=fake_console_type)
|
console_type=fake_console_type)
|
||||||
if enable_consoleauth:
|
|
||||||
mock_auth.assert_called_once_with(
|
|
||||||
self.context, 'fake_token', fake_console_type,
|
|
||||||
'fake_console_host', 'fake_console_port', 'fake_access_path',
|
|
||||||
'f3000000-0000-0000-0000-000000000000',
|
|
||||||
access_url='fake_console_url')
|
|
||||||
else:
|
|
||||||
mock_auth.assert_not_called()
|
|
||||||
|
|
||||||
def test_get_vnc_console_no_host(self):
|
def test_get_vnc_console_no_host(self):
|
||||||
instance = self._create_fake_instance_obj(params={'host': ''})
|
instance = self._create_fake_instance_obj(params={'host': ''})
|
||||||
@ -10197,12 +10185,8 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
self.compute_api.get_vnc_console,
|
self.compute_api.get_vnc_console,
|
||||||
self.context, instance, 'novnc')
|
self.context, instance, 'novnc')
|
||||||
|
|
||||||
@ddt.data(True, False)
|
|
||||||
@mock.patch.object(compute.consoleauth_rpcapi.ConsoleAuthAPI,
|
|
||||||
'authorize_console')
|
|
||||||
@mock.patch.object(compute_rpcapi.ComputeAPI, 'get_spice_console')
|
@mock.patch.object(compute_rpcapi.ComputeAPI, 'get_spice_console')
|
||||||
def test_spice_console(self, enable_consoleauth, mock_spice, mock_auth):
|
def test_spice_console(self, mock_spice):
|
||||||
self.flags(enable_consoleauth=enable_consoleauth, group='workarounds')
|
|
||||||
# Make sure we can a spice console for an instance.
|
# Make sure we can a spice console for an instance.
|
||||||
|
|
||||||
fake_instance = self._fake_instance(
|
fake_instance = self._fake_instance(
|
||||||
@ -10225,14 +10209,6 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
mock_spice.assert_called_once_with(self.context,
|
mock_spice.assert_called_once_with(self.context,
|
||||||
instance=fake_instance,
|
instance=fake_instance,
|
||||||
console_type=fake_console_type)
|
console_type=fake_console_type)
|
||||||
if enable_consoleauth:
|
|
||||||
mock_auth.assert_called_once_with(
|
|
||||||
self.context, 'fake_token', fake_console_type,
|
|
||||||
'fake_console_host', 'fake_console_port', 'fake_access_path',
|
|
||||||
'f3000000-0000-0000-0000-000000000000',
|
|
||||||
access_url='fake_console_url')
|
|
||||||
else:
|
|
||||||
mock_auth.assert_not_called()
|
|
||||||
|
|
||||||
def test_get_spice_console_no_host(self):
|
def test_get_spice_console_no_host(self):
|
||||||
instance = self._create_fake_instance_obj(params={'host': ''})
|
instance = self._create_fake_instance_obj(params={'host': ''})
|
||||||
@ -10260,12 +10236,8 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
getattr(self.compute_api, 'get_%s_console' % console_type),
|
getattr(self.compute_api, 'get_%s_console' % console_type),
|
||||||
self.context, instance, console_type)
|
self.context, instance, console_type)
|
||||||
|
|
||||||
@ddt.data(True, False)
|
|
||||||
@mock.patch.object(compute.consoleauth_rpcapi.ConsoleAuthAPI,
|
|
||||||
'authorize_console')
|
|
||||||
@mock.patch.object(compute_rpcapi.ComputeAPI, 'get_rdp_console')
|
@mock.patch.object(compute_rpcapi.ComputeAPI, 'get_rdp_console')
|
||||||
def test_rdp_console(self, enable_consoleauth, mock_rdp, mock_auth):
|
def test_rdp_console(self, mock_rdp):
|
||||||
self.flags(enable_consoleauth=enable_consoleauth, group='workarounds')
|
|
||||||
# Make sure we can a rdp console for an instance.
|
# Make sure we can a rdp console for an instance.
|
||||||
fake_instance = self._fake_instance({
|
fake_instance = self._fake_instance({
|
||||||
'uuid': 'f3000000-0000-0000-0000-000000000000',
|
'uuid': 'f3000000-0000-0000-0000-000000000000',
|
||||||
@ -10286,14 +10258,6 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
self.assertEqual(console, {'url': 'fake_console_url'})
|
self.assertEqual(console, {'url': 'fake_console_url'})
|
||||||
mock_rdp.assert_called_once_with(self.context, instance=fake_instance,
|
mock_rdp.assert_called_once_with(self.context, instance=fake_instance,
|
||||||
console_type=fake_console_type)
|
console_type=fake_console_type)
|
||||||
if enable_consoleauth:
|
|
||||||
mock_auth.assert_called_once_with(
|
|
||||||
self.context, 'fake_token', fake_console_type,
|
|
||||||
'fake_console_host', 'fake_console_port', 'fake_access_path',
|
|
||||||
'f3000000-0000-0000-0000-000000000000',
|
|
||||||
access_url='fake_console_url')
|
|
||||||
else:
|
|
||||||
mock_auth.assert_not_called()
|
|
||||||
|
|
||||||
def test_get_rdp_console_no_host(self):
|
def test_get_rdp_console_no_host(self):
|
||||||
instance = self._create_fake_instance_obj(params={'host': ''})
|
instance = self._create_fake_instance_obj(params={'host': ''})
|
||||||
@ -10317,21 +10281,8 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
'instance_uuid': fake_instance.uuid,
|
'instance_uuid': fake_instance.uuid,
|
||||||
'access_url': 'fake_access_url'}
|
'access_url': 'fake_access_url'}
|
||||||
|
|
||||||
rpcapi = compute_rpcapi.ComputeAPI
|
with mock.patch.object(self.compute_api.compute_rpcapi,
|
||||||
|
'get_serial_console', return_value=fake_connect_info):
|
||||||
with test.nested(
|
|
||||||
mock.patch.object(rpcapi, 'get_serial_console',
|
|
||||||
return_value=fake_connect_info),
|
|
||||||
mock.patch.object(self.compute_api.consoleauth_rpcapi,
|
|
||||||
'authorize_console')
|
|
||||||
) as (mock_get_serial_console, mock_authorize_console):
|
|
||||||
self.compute_api.consoleauth_rpcapi.authorize_console(
|
|
||||||
self.context, 'fake_token', fake_console_type,
|
|
||||||
'fake_serial_host', 'fake_tcp_port',
|
|
||||||
'fake_access_path',
|
|
||||||
'f3000000-0000-0000-0000-000000000000',
|
|
||||||
access_url='fake_access_url')
|
|
||||||
|
|
||||||
console = self.compute_api.get_serial_console(self.context,
|
console = self.compute_api.get_serial_console(self.context,
|
||||||
fake_instance,
|
fake_instance,
|
||||||
fake_console_type)
|
fake_console_type)
|
||||||
@ -10358,13 +10309,8 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
'instance_uuid': fake_instance.uuid,
|
'instance_uuid': fake_instance.uuid,
|
||||||
'access_url': 'fake_access_url'}
|
'access_url': 'fake_access_url'}
|
||||||
|
|
||||||
with test.nested(
|
with mock.patch.object(self.compute_api.compute_rpcapi,
|
||||||
mock.patch.object(self.compute_api.compute_rpcapi,
|
'get_mks_console', return_value=fake_connect_info):
|
||||||
'get_mks_console',
|
|
||||||
return_value=fake_connect_info),
|
|
||||||
mock.patch.object(self.compute_api.consoleauth_rpcapi,
|
|
||||||
'authorize_console')
|
|
||||||
) as (mock_get_mks_console, mock_authorize_console):
|
|
||||||
console = self.compute_api.get_mks_console(self.context,
|
console = self.compute_api.get_mks_console(self.context,
|
||||||
fake_instance,
|
fake_instance,
|
||||||
fake_console_type)
|
fake_console_type)
|
||||||
@ -11283,17 +11229,14 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
instance, instance_uuid = self._run_instance()
|
instance, instance_uuid = self._run_instance()
|
||||||
|
|
||||||
rpcapi = self.compute_api.compute_task_api
|
rpcapi = self.compute_api.compute_task_api
|
||||||
consoleauth_rpcapi = self.compute_api.consoleauth_rpcapi
|
|
||||||
fake_spec = objects.RequestSpec()
|
fake_spec = objects.RequestSpec()
|
||||||
|
|
||||||
@mock.patch.object(consoleauth_rpcapi, 'delete_tokens_for_instance')
|
|
||||||
@mock.patch.object(rpcapi, 'live_migrate_instance')
|
@mock.patch.object(rpcapi, 'live_migrate_instance')
|
||||||
@mock.patch.object(objects.ComputeNodeList, 'get_all_by_host')
|
@mock.patch.object(objects.ComputeNodeList, 'get_all_by_host')
|
||||||
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
@mock.patch.object(objects.RequestSpec, 'get_by_instance_uuid')
|
||||||
@mock.patch.object(self.compute_api, '_record_action_start')
|
@mock.patch.object(self.compute_api, '_record_action_start')
|
||||||
def do_test(record_action_start, get_by_instance_uuid,
|
def do_test(record_action_start, get_by_instance_uuid,
|
||||||
get_all_by_host, live_migrate_instance,
|
get_all_by_host, live_migrate_instance):
|
||||||
delete_tokens_for_instance):
|
|
||||||
get_by_instance_uuid.return_value = fake_spec
|
get_by_instance_uuid.return_value = fake_spec
|
||||||
get_all_by_host.return_value = objects.ComputeNodeList(
|
get_all_by_host.return_value = objects.ComputeNodeList(
|
||||||
objects=[objects.ComputeNode(
|
objects=[objects.ComputeNode(
|
||||||
@ -11318,12 +11261,6 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
disk_over_commit=True,
|
disk_over_commit=True,
|
||||||
request_spec=fake_spec, async_=False)
|
request_spec=fake_spec, async_=False)
|
||||||
|
|
||||||
if CONF.workarounds.enable_consoleauth:
|
|
||||||
delete_tokens_for_instance.assert_called_once_with(
|
|
||||||
self.context, instance.uuid)
|
|
||||||
else:
|
|
||||||
delete_tokens_for_instance.assert_not_called()
|
|
||||||
|
|
||||||
do_test()
|
do_test()
|
||||||
instance.refresh()
|
instance.refresh()
|
||||||
self.assertEqual(instance['task_state'], task_states.MIGRATING)
|
self.assertEqual(instance['task_state'], task_states.MIGRATING)
|
||||||
@ -11334,19 +11271,13 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
self.assertEqual('fake_dest_host', req_dest.host)
|
self.assertEqual('fake_dest_host', req_dest.host)
|
||||||
self.assertEqual('fake_dest_node', req_dest.node)
|
self.assertEqual('fake_dest_node', req_dest.node)
|
||||||
|
|
||||||
@ddt.data(True, False)
|
def test_live_migrate(self):
|
||||||
def test_live_migrate(self, enable_consoleauth):
|
|
||||||
self.flags(enable_consoleauth=enable_consoleauth, group='workarounds')
|
|
||||||
self._test_live_migrate()
|
self._test_live_migrate()
|
||||||
|
|
||||||
@ddt.data(True, False)
|
def test_live_migrate_with_not_forced_host(self):
|
||||||
def test_live_migrate_with_not_forced_host(self, enable_consoleauth):
|
|
||||||
self.flags(enable_consoleauth=enable_consoleauth, group='workarounds')
|
|
||||||
self._test_live_migrate(force=False)
|
self._test_live_migrate(force=False)
|
||||||
|
|
||||||
@ddt.data(True, False)
|
def test_live_migrate_with_forced_host(self):
|
||||||
def test_live_migrate_with_forced_host(self, enable_consoleauth):
|
|
||||||
self.flags(enable_consoleauth=enable_consoleauth, group='workarounds')
|
|
||||||
self._test_live_migrate(force=True)
|
self._test_live_migrate(force=True)
|
||||||
|
|
||||||
def test_fail_live_migrate_with_non_existing_destination(self):
|
def test_fail_live_migrate_with_non_existing_destination(self):
|
||||||
|
@ -37,7 +37,6 @@ from nova.compute import utils as compute_utils
|
|||||||
from nova.compute import vm_states
|
from nova.compute import vm_states
|
||||||
from nova import conductor
|
from nova import conductor
|
||||||
import nova.conf
|
import nova.conf
|
||||||
from nova.consoleauth import rpcapi as consoleauth_rpcapi
|
|
||||||
from nova import context
|
from nova import context
|
||||||
from nova.db import api as db
|
from nova.db import api as db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
@ -868,8 +867,6 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
@mock.patch.object(image_api.API, 'delete')
|
@mock.patch.object(image_api.API, 'delete')
|
||||||
@mock.patch.object(objects.InstanceMapping, 'save')
|
@mock.patch.object(objects.InstanceMapping, 'save')
|
||||||
@mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
|
@mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
|
||||||
@mock.patch.object(consoleauth_rpcapi.ConsoleAuthAPI,
|
|
||||||
'delete_tokens_for_instance')
|
|
||||||
@mock.patch.object(compute_utils,
|
@mock.patch.object(compute_utils,
|
||||||
'notify_about_instance_usage')
|
'notify_about_instance_usage')
|
||||||
@mock.patch.object(db, 'instance_destroy')
|
@mock.patch.object(db, 'instance_destroy')
|
||||||
@ -886,7 +883,7 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
def _test_delete(self, delete_type, mock_save, mock_bdm_get, mock_elevated,
|
def _test_delete(self, delete_type, mock_save, mock_bdm_get, mock_elevated,
|
||||||
mock_get_cn, mock_up, mock_record, mock_inst_update,
|
mock_get_cn, mock_up, mock_record, mock_inst_update,
|
||||||
mock_deallocate, mock_inst_meta, mock_inst_destroy,
|
mock_deallocate, mock_inst_meta, mock_inst_destroy,
|
||||||
mock_notify_legacy, mock_del_token, mock_get_inst,
|
mock_notify_legacy, mock_get_inst,
|
||||||
mock_save_im, mock_image_delete, mock_mig_get,
|
mock_save_im, mock_image_delete, mock_mig_get,
|
||||||
mock_notify, **attrs):
|
mock_notify, **attrs):
|
||||||
expected_save_calls = [mock.call()]
|
expected_save_calls = [mock.call()]
|
||||||
@ -1051,11 +1048,6 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
mock_terminate.assert_called_once_with(
|
mock_terminate.assert_called_once_with(
|
||||||
self.context, inst, [])
|
self.context, inst, [])
|
||||||
|
|
||||||
if CONF.workarounds.enable_consoleauth:
|
|
||||||
mock_del_token.assert_called_once_with(self.context, instance_uuid)
|
|
||||||
else:
|
|
||||||
mock_del_token.assert_not_called()
|
|
||||||
|
|
||||||
if is_shelved:
|
if is_shelved:
|
||||||
mock_image_delete.assert_called_once_with(self.context,
|
mock_image_delete.assert_called_once_with(self.context,
|
||||||
snapshot_id)
|
snapshot_id)
|
||||||
@ -1080,9 +1072,7 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
task_state=task_states.RESIZE_FINISH,
|
task_state=task_states.RESIZE_FINISH,
|
||||||
old_flavor=old_flavor)
|
old_flavor=old_flavor)
|
||||||
|
|
||||||
@ddt.data(True, False)
|
def test_delete_in_resized(self):
|
||||||
def test_delete_in_resized(self, enable_consoleauth):
|
|
||||||
self.flags(enable_consoleauth=enable_consoleauth, group='workarounds')
|
|
||||||
self._test_delete('delete', vm_state=vm_states.RESIZED)
|
self._test_delete('delete', vm_state=vm_states.RESIZED)
|
||||||
|
|
||||||
def test_delete_shelved(self):
|
def test_delete_shelved(self):
|
||||||
@ -1091,9 +1081,7 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
vm_state=vm_states.SHELVED,
|
vm_state=vm_states.SHELVED,
|
||||||
system_metadata=fake_sys_meta)
|
system_metadata=fake_sys_meta)
|
||||||
|
|
||||||
@ddt.data(True, False)
|
def test_delete_shelved_offloaded(self):
|
||||||
def test_delete_shelved_offloaded(self, enable_consoleauth):
|
|
||||||
self.flags(enable_consoleauth=enable_consoleauth, group='workarounds')
|
|
||||||
fake_sys_meta = {'shelved_image_id': SHELVED_IMAGE}
|
fake_sys_meta = {'shelved_image_id': SHELVED_IMAGE}
|
||||||
self._test_delete('delete',
|
self._test_delete('delete',
|
||||||
vm_state=vm_states.SHELVED_OFFLOADED,
|
vm_state=vm_states.SHELVED_OFFLOADED,
|
||||||
@ -1111,9 +1099,7 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
vm_state=vm_states.SHELVED_OFFLOADED,
|
vm_state=vm_states.SHELVED_OFFLOADED,
|
||||||
system_metadata=fake_sys_meta)
|
system_metadata=fake_sys_meta)
|
||||||
|
|
||||||
@ddt.data(True, False)
|
def test_delete_shelved_exception(self):
|
||||||
def test_delete_shelved_exception(self, enable_consoleauth):
|
|
||||||
self.flags(enable_consoleauth=enable_consoleauth, group='workarounds')
|
|
||||||
fake_sys_meta = {'shelved_image_id': SHELVED_IMAGE_EXCEPTION}
|
fake_sys_meta = {'shelved_image_id': SHELVED_IMAGE_EXCEPTION}
|
||||||
self._test_delete('delete',
|
self._test_delete('delete',
|
||||||
vm_state=vm_states.SHELVED,
|
vm_state=vm_states.SHELVED,
|
||||||
@ -1128,9 +1114,7 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
def test_delete_soft_in_resized(self):
|
def test_delete_soft_in_resized(self):
|
||||||
self._test_delete('soft_delete', vm_state=vm_states.RESIZED)
|
self._test_delete('soft_delete', vm_state=vm_states.RESIZED)
|
||||||
|
|
||||||
@ddt.data(True, False)
|
def test_delete_soft(self):
|
||||||
def test_delete_soft(self, enable_consoleauth):
|
|
||||||
self.flags(enable_consoleauth=enable_consoleauth, group='workarounds')
|
|
||||||
self._test_delete('soft_delete')
|
self._test_delete('soft_delete')
|
||||||
|
|
||||||
def test_delete_forced(self):
|
def test_delete_forced(self):
|
||||||
|
@ -66,10 +66,9 @@ class NovaProxyRequestHandlerDBTestCase(test.TestCase):
|
|||||||
@mock.patch('nova.objects.ConsoleAuthToken.validate')
|
@mock.patch('nova.objects.ConsoleAuthToken.validate')
|
||||||
@mock.patch('nova.objects.Instance.get_by_uuid')
|
@mock.patch('nova.objects.Instance.get_by_uuid')
|
||||||
@mock.patch('nova.compute.rpcapi.ComputeAPI.validate_console_port')
|
@mock.patch('nova.compute.rpcapi.ComputeAPI.validate_console_port')
|
||||||
@mock.patch('nova.consoleauth.rpcapi.ConsoleAuthAPI.check_token')
|
|
||||||
def test_new_websocket_client_db(
|
def test_new_websocket_client_db(
|
||||||
self, mock_ca_check, mock_validate_port, mock_inst_get,
|
self, mock_validate_port, mock_inst_get, mock_validate,
|
||||||
mock_validate, internal_access_path=None,
|
internal_access_path=None,
|
||||||
instance_not_found=False):
|
instance_not_found=False):
|
||||||
|
|
||||||
db_obj = self._fake_console_db(
|
db_obj = self._fake_console_db(
|
||||||
@ -113,7 +112,6 @@ class NovaProxyRequestHandlerDBTestCase(test.TestCase):
|
|||||||
mock_validate_port.assert_called_once_with(
|
mock_validate_port.assert_called_once_with(
|
||||||
ctxt, mock_inst_get.return_value, str(db_obj['port']),
|
ctxt, mock_inst_get.return_value, str(db_obj['port']),
|
||||||
db_obj['console_type'])
|
db_obj['console_type'])
|
||||||
mock_ca_check.assert_not_called()
|
|
||||||
|
|
||||||
self.wh.socket.assert_called_with('node1', 10000, connect=True)
|
self.wh.socket.assert_called_with('node1', 10000, connect=True)
|
||||||
|
|
||||||
@ -201,60 +199,6 @@ class NovaProxyRequestHandlerBaseTestCase(test.NoDBTestCase):
|
|||||||
'Host': 'example.net:6080',
|
'Host': 'example.net:6080',
|
||||||
}
|
}
|
||||||
|
|
||||||
@mock.patch('nova.consoleauth.rpcapi.ConsoleAuthAPI.check_token')
|
|
||||||
def test_new_websocket_client_enable_consoleauth(self, check_token):
|
|
||||||
self.flags(enable_consoleauth=True, group='workarounds')
|
|
||||||
|
|
||||||
check_token.return_value = {
|
|
||||||
'host': 'node1',
|
|
||||||
'port': '10000',
|
|
||||||
'console_type': 'novnc',
|
|
||||||
'access_url': 'https://example.net:6080'
|
|
||||||
}
|
|
||||||
self.wh.socket.return_value = '<socket>'
|
|
||||||
self.wh.path = "http://127.0.0.1/?%s" % self.path
|
|
||||||
self.wh.headers = self.fake_header
|
|
||||||
|
|
||||||
self.wh.new_websocket_client()
|
|
||||||
|
|
||||||
check_token.assert_called_with(mock.ANY, token="123-456-789")
|
|
||||||
self.wh.socket.assert_called_with('node1', 10000, connect=True)
|
|
||||||
self.wh.do_proxy.assert_called_with('<socket>')
|
|
||||||
|
|
||||||
@mock.patch('nova.consoleauth.rpcapi.ConsoleAuthAPI.check_token',
|
|
||||||
return_value=None)
|
|
||||||
@mock.patch('nova.console.websocketproxy.NovaProxyRequestHandlerBase.'
|
|
||||||
'_check_console_port')
|
|
||||||
@mock.patch('nova.objects.ConsoleAuthToken.validate')
|
|
||||||
def test_new_websocket_client_enable_consoleauth_fallback(self, validate,
|
|
||||||
check_port,
|
|
||||||
check_token):
|
|
||||||
# Since consoleauth is enabled, it should be called first before
|
|
||||||
# falling back to the database.
|
|
||||||
self.flags(enable_consoleauth=True, group='workarounds')
|
|
||||||
|
|
||||||
params = {
|
|
||||||
'id': 1,
|
|
||||||
'token': '123-456-789',
|
|
||||||
'instance_uuid': uuids.instance,
|
|
||||||
'host': 'node1',
|
|
||||||
'port': '10000',
|
|
||||||
'console_type': 'novnc',
|
|
||||||
'access_url_base': 'https://example.net:6080'
|
|
||||||
}
|
|
||||||
validate.return_value = objects.ConsoleAuthToken(**params)
|
|
||||||
|
|
||||||
self.wh.socket.return_value = '<socket>'
|
|
||||||
self.wh.path = "http://127.0.0.1/?%s" % self.path
|
|
||||||
self.wh.headers = self.fake_header
|
|
||||||
|
|
||||||
self.wh.new_websocket_client()
|
|
||||||
|
|
||||||
check_token.assert_called_with(mock.ANY, token="123-456-789")
|
|
||||||
validate.assert_called_with(mock.ANY, "123-456-789")
|
|
||||||
self.wh.socket.assert_called_with('node1', 10000, connect=True)
|
|
||||||
self.wh.do_proxy.assert_called_with('<socket>')
|
|
||||||
|
|
||||||
@mock.patch('nova.console.websocketproxy.NovaProxyRequestHandlerBase.'
|
@mock.patch('nova.console.websocketproxy.NovaProxyRequestHandlerBase.'
|
||||||
'_check_console_port')
|
'_check_console_port')
|
||||||
@mock.patch('nova.objects.ConsoleAuthToken.validate')
|
@mock.patch('nova.objects.ConsoleAuthToken.validate')
|
||||||
@ -441,23 +385,6 @@ class NovaProxyRequestHandlerBaseTestCase(test.NoDBTestCase):
|
|||||||
self.wh.socket.assert_called_with('node1', 10000, connect=True)
|
self.wh.socket.assert_called_with('node1', 10000, connect=True)
|
||||||
self.wh.do_proxy.assert_called_with('<socket>')
|
self.wh.do_proxy.assert_called_with('<socket>')
|
||||||
|
|
||||||
@mock.patch.object(websocketproxy, 'sys')
|
|
||||||
@mock.patch('nova.consoleauth.rpcapi.ConsoleAuthAPI.check_token')
|
|
||||||
def test_new_websocket_client_py273_special_scheme(
|
|
||||||
self, check_token, mock_sys):
|
|
||||||
mock_sys.version_info = (2, 7, 3)
|
|
||||||
check_token.return_value = {
|
|
||||||
'host': 'node1',
|
|
||||||
'port': '10000',
|
|
||||||
'console_type': 'novnc'
|
|
||||||
}
|
|
||||||
self.wh.socket.return_value = '<socket>'
|
|
||||||
self.wh.path = "ws://127.0.0.1/?%s" % self.path
|
|
||||||
self.wh.headers = self.fake_header
|
|
||||||
|
|
||||||
self.assertRaises(exception.NovaException,
|
|
||||||
self.wh.new_websocket_client)
|
|
||||||
|
|
||||||
@mock.patch('socket.getfqdn')
|
@mock.patch('socket.getfqdn')
|
||||||
def test_address_string_doesnt_do_reverse_dns_lookup(self, getfqdn):
|
def test_address_string_doesnt_do_reverse_dns_lookup(self, getfqdn):
|
||||||
request_mock = mock.MagicMock()
|
request_mock = mock.MagicMock()
|
||||||
|
@ -1,240 +0,0 @@
|
|||||||
# Copyright 2012 OpenStack Foundation
|
|
||||||
# Administrator of the National Aeronautics and Space Administration.
|
|
||||||
# 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.
|
|
||||||
"""
|
|
||||||
Tests for Consoleauth Code.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
import mock
|
|
||||||
from oslo_utils.fixture import uuidsentinel as uuids
|
|
||||||
from oslo_utils import timeutils
|
|
||||||
import six
|
|
||||||
|
|
||||||
from nova.consoleauth import manager
|
|
||||||
from nova import context
|
|
||||||
from nova import objects
|
|
||||||
from nova import test
|
|
||||||
|
|
||||||
|
|
||||||
class ConsoleauthTestCase(test.NoDBTestCase):
|
|
||||||
"""Test Case for consoleauth."""
|
|
||||||
|
|
||||||
rpcapi = 'nova.compute.rpcapi.ComputeAPI.'
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(ConsoleauthTestCase, self).setUp()
|
|
||||||
self.manager_api = self.manager = manager.ConsoleAuthManager()
|
|
||||||
self.context = context.get_admin_context()
|
|
||||||
self.instance_uuid = '00000000-0000-0000-0000-000000000000'
|
|
||||||
self.is_cells = False
|
|
||||||
|
|
||||||
def test_reset(self):
|
|
||||||
with mock.patch('nova.compute.rpcapi.ComputeAPI') as mock_rpc:
|
|
||||||
old_rpcapi = self.manager_api.compute_rpcapi
|
|
||||||
self.manager_api.reset()
|
|
||||||
mock_rpc.assert_called_once_with()
|
|
||||||
self.assertNotEqual(old_rpcapi,
|
|
||||||
self.manager_api.compute_rpcapi)
|
|
||||||
|
|
||||||
@mock.patch('nova.objects.instance.Instance.get_by_uuid')
|
|
||||||
def test_tokens_expire(self, mock_get):
|
|
||||||
mock_get.return_value = None
|
|
||||||
# NOTE(danms): Get the faked InstanceMapping from the SingleCellSimple
|
|
||||||
# fixture so we can return it from our own mock to verify
|
|
||||||
# that it was called
|
|
||||||
fake_im = objects.InstanceMapping.get_by_instance_uuid(self.context,
|
|
||||||
uuids.instance)
|
|
||||||
|
|
||||||
# Test that tokens expire correctly.
|
|
||||||
self.useFixture(test.TimeOverride())
|
|
||||||
token = u'mytok'
|
|
||||||
self.flags(token_ttl=1, group='consoleauth')
|
|
||||||
|
|
||||||
self._stub_validate_console_port(True)
|
|
||||||
|
|
||||||
self.manager_api.authorize_console(self.context, token, 'novnc',
|
|
||||||
'127.0.0.1', '8080', 'host',
|
|
||||||
self.instance_uuid)
|
|
||||||
with mock.patch('nova.objects.InstanceMapping.'
|
|
||||||
'get_by_instance_uuid') as mock_get:
|
|
||||||
mock_get.return_value = fake_im
|
|
||||||
self.assertIsNotNone(self.manager_api.check_token(self.context,
|
|
||||||
token))
|
|
||||||
timeutils.advance_time_seconds(1)
|
|
||||||
self.assertIsNone(self.manager_api.check_token(self.context,
|
|
||||||
token))
|
|
||||||
if not self.is_cells:
|
|
||||||
mock_get.assert_called_once_with(self.context,
|
|
||||||
self.instance_uuid)
|
|
||||||
|
|
||||||
def _stub_validate_console_port(self, result):
|
|
||||||
def fake_validate_console_port(self, ctxt, instance,
|
|
||||||
port, console_type):
|
|
||||||
return result
|
|
||||||
|
|
||||||
self.stub_out(self.rpcapi + 'validate_console_port',
|
|
||||||
fake_validate_console_port)
|
|
||||||
|
|
||||||
@mock.patch('nova.objects.instance.Instance.get_by_uuid')
|
|
||||||
def test_multiple_tokens_for_instance(self, mock_get):
|
|
||||||
mock_get.return_value = None
|
|
||||||
|
|
||||||
tokens = [u"token" + str(i) for i in range(10)]
|
|
||||||
|
|
||||||
self._stub_validate_console_port(True)
|
|
||||||
|
|
||||||
for token in tokens:
|
|
||||||
self.manager_api.authorize_console(self.context, token, 'novnc',
|
|
||||||
'127.0.0.1', '8080', 'host',
|
|
||||||
self.instance_uuid)
|
|
||||||
|
|
||||||
for token in tokens:
|
|
||||||
self.assertIsNotNone(
|
|
||||||
self.manager_api.check_token(self.context, token))
|
|
||||||
|
|
||||||
def test_delete_tokens_for_instance(self):
|
|
||||||
tokens = [u"token" + str(i) for i in range(10)]
|
|
||||||
for token in tokens:
|
|
||||||
self.manager_api.authorize_console(self.context, token, 'novnc',
|
|
||||||
'127.0.0.1', '8080', 'host',
|
|
||||||
self.instance_uuid)
|
|
||||||
self.manager_api.delete_tokens_for_instance(self.context,
|
|
||||||
self.instance_uuid)
|
|
||||||
stored_tokens = self.manager._get_tokens_for_instance(
|
|
||||||
self.instance_uuid)
|
|
||||||
|
|
||||||
self.assertEqual(len(stored_tokens), 0)
|
|
||||||
|
|
||||||
for token in tokens:
|
|
||||||
self.assertIsNone(
|
|
||||||
self.manager_api.check_token(self.context, token))
|
|
||||||
|
|
||||||
def test_delete_tokens_for_instance_no_tokens(self):
|
|
||||||
with test.nested(
|
|
||||||
mock.patch.object(self.manager, '_get_tokens_for_instance',
|
|
||||||
return_value=[]),
|
|
||||||
mock.patch.object(self.manager.mc, 'delete_multi'),
|
|
||||||
mock.patch.object(self.manager.mc_instance, 'delete')
|
|
||||||
) as (
|
|
||||||
mock_get_tokens, mock_delete_multi, mock_delete
|
|
||||||
):
|
|
||||||
self.manager.delete_tokens_for_instance(
|
|
||||||
self.context, self.instance_uuid)
|
|
||||||
# Since here were no tokens, we didn't try to clear anything
|
|
||||||
# from the cache.
|
|
||||||
mock_delete_multi.assert_not_called()
|
|
||||||
mock_delete.assert_called_once_with(
|
|
||||||
self.instance_uuid.encode('UTF-8'))
|
|
||||||
|
|
||||||
@mock.patch('nova.objects.instance.Instance.get_by_uuid')
|
|
||||||
def test_wrong_token_has_port(self, mock_get):
|
|
||||||
mock_get.return_value = None
|
|
||||||
|
|
||||||
token = u'mytok'
|
|
||||||
|
|
||||||
self._stub_validate_console_port(False)
|
|
||||||
|
|
||||||
self.manager_api.authorize_console(self.context, token, 'novnc',
|
|
||||||
'127.0.0.1', '8080', 'host',
|
|
||||||
instance_uuid=self.instance_uuid)
|
|
||||||
self.assertIsNone(self.manager_api.check_token(self.context, token))
|
|
||||||
|
|
||||||
def test_delete_expired_tokens(self):
|
|
||||||
self.useFixture(test.TimeOverride())
|
|
||||||
token = u'mytok'
|
|
||||||
self.flags(token_ttl=1, group='consoleauth')
|
|
||||||
|
|
||||||
self._stub_validate_console_port(True)
|
|
||||||
|
|
||||||
self.manager_api.authorize_console(self.context, token, 'novnc',
|
|
||||||
'127.0.0.1', '8080', 'host',
|
|
||||||
self.instance_uuid)
|
|
||||||
timeutils.advance_time_seconds(1)
|
|
||||||
self.assertIsNone(self.manager_api.check_token(self.context, token))
|
|
||||||
|
|
||||||
token1 = u'mytok2'
|
|
||||||
self.manager_api.authorize_console(self.context, token1, 'novnc',
|
|
||||||
'127.0.0.1', '8080', 'host',
|
|
||||||
self.instance_uuid)
|
|
||||||
stored_tokens = self.manager._get_tokens_for_instance(
|
|
||||||
self.instance_uuid)
|
|
||||||
# when trying to store token1, expired token is removed fist.
|
|
||||||
self.assertEqual(len(stored_tokens), 1)
|
|
||||||
self.assertEqual(stored_tokens[0], token1)
|
|
||||||
|
|
||||||
|
|
||||||
class ControlauthMemcacheEncodingTestCase(test.NoDBTestCase):
|
|
||||||
def setUp(self):
|
|
||||||
super(ControlauthMemcacheEncodingTestCase, self).setUp()
|
|
||||||
self.manager = manager.ConsoleAuthManager()
|
|
||||||
self.context = context.get_admin_context()
|
|
||||||
self.u_token = u"token"
|
|
||||||
self.u_instance = u"instance"
|
|
||||||
|
|
||||||
def test_authorize_console_encoding(self):
|
|
||||||
with test.nested(
|
|
||||||
mock.patch.object(self.manager.mc_instance,
|
|
||||||
'set', return_value=None),
|
|
||||||
mock.patch.object(self.manager.mc_instance,
|
|
||||||
'get', return_value='["token"]'),
|
|
||||||
mock.patch.object(self.manager.mc,
|
|
||||||
'set', return_value=None),
|
|
||||||
mock.patch.object(self.manager.mc,
|
|
||||||
'get', return_value=None),
|
|
||||||
mock.patch.object(self.manager.mc,
|
|
||||||
'get_multi', return_value=["token1"]),
|
|
||||||
) as (
|
|
||||||
mock_instance_set,
|
|
||||||
mock_instance_get,
|
|
||||||
mock_set,
|
|
||||||
mock_get,
|
|
||||||
mock_get_multi):
|
|
||||||
self.manager.authorize_console(self.context, self.u_token,
|
|
||||||
'novnc', '127.0.0.1', '8080',
|
|
||||||
'host', self.u_instance)
|
|
||||||
mock_set.assert_has_calls([mock.call(b'token', mock.ANY)])
|
|
||||||
mock_instance_get.assert_has_calls([mock.call(b'instance')])
|
|
||||||
mock_get_multi.assert_has_calls([mock.call([b'token'])])
|
|
||||||
mock_instance_set.assert_has_calls(
|
|
||||||
[mock.call(b'instance', mock.ANY)])
|
|
||||||
|
|
||||||
def test_check_token_encoding(self):
|
|
||||||
with mock.patch.object(self.manager.mc,
|
|
||||||
"get",
|
|
||||||
return_value=None) as mock_get:
|
|
||||||
self.manager.check_token(self.context, self.u_token)
|
|
||||||
mock_get.assert_called_once_with(test.MatchType(six.binary_type))
|
|
||||||
|
|
||||||
def test_delete_tokens_for_instance_encoding(self):
|
|
||||||
with test.nested(
|
|
||||||
mock.patch.object(self.manager.mc_instance,
|
|
||||||
'get', return_value='["token"]'),
|
|
||||||
mock.patch.object(self.manager.mc_instance,
|
|
||||||
'delete', return_value=True),
|
|
||||||
mock.patch.object(self.manager.mc,
|
|
||||||
'get'),
|
|
||||||
mock.patch.object(self.manager.mc,
|
|
||||||
'delete_multi', return_value=True),
|
|
||||||
) as (
|
|
||||||
mock_instance_get,
|
|
||||||
mock_instance_delete,
|
|
||||||
mock_get,
|
|
||||||
mock_delete_multi):
|
|
||||||
self.manager.delete_tokens_for_instance(self.context,
|
|
||||||
self.u_instance)
|
|
||||||
mock_instance_get.assert_has_calls([mock.call(b'instance')])
|
|
||||||
mock_instance_delete.assert_has_calls([mock.call(b'instance')])
|
|
||||||
mock_delete_multi.assert_has_calls([mock.call([b'token'])])
|
|
@ -1,90 +0,0 @@
|
|||||||
# Copyright 2013 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.
|
|
||||||
|
|
||||||
"""
|
|
||||||
Unit Tests for nova.consoleauth.rpcapi
|
|
||||||
"""
|
|
||||||
|
|
||||||
import mock
|
|
||||||
|
|
||||||
from nova.consoleauth import rpcapi as consoleauth_rpcapi
|
|
||||||
from nova import context
|
|
||||||
from nova import test
|
|
||||||
|
|
||||||
|
|
||||||
class ConsoleAuthRpcAPITestCase(test.NoDBTestCase):
|
|
||||||
DROPPED_ARG = object()
|
|
||||||
|
|
||||||
def _test_consoleauth_api(self, method, **kwargs):
|
|
||||||
do_cast = kwargs.pop('_do_cast', False)
|
|
||||||
|
|
||||||
ctxt = context.RequestContext('fake_user', 'fake_project')
|
|
||||||
|
|
||||||
rpcapi = consoleauth_rpcapi.ConsoleAuthAPI()
|
|
||||||
self.assertIsNotNone(rpcapi.client)
|
|
||||||
self.assertEqual(rpcapi.client.target.topic,
|
|
||||||
consoleauth_rpcapi.RPC_TOPIC)
|
|
||||||
|
|
||||||
orig_prepare = rpcapi.client.prepare
|
|
||||||
|
|
||||||
version = kwargs.pop('version', None)
|
|
||||||
rpc_kwargs = {k: v for k, v in kwargs.items()
|
|
||||||
if v is not self.DROPPED_ARG}
|
|
||||||
|
|
||||||
with test.nested(
|
|
||||||
mock.patch.object(rpcapi.client, 'cast' if do_cast else 'call'),
|
|
||||||
mock.patch.object(rpcapi.client, 'prepare'),
|
|
||||||
mock.patch.object(rpcapi.client, 'can_send_version'),
|
|
||||||
) as (
|
|
||||||
rpc_mock, prepare_mock, csv_mock
|
|
||||||
):
|
|
||||||
prepare_mock.return_value = rpcapi.client
|
|
||||||
rpc_mock.return_value = None if do_cast else 'foo'
|
|
||||||
|
|
||||||
def fake_csv(v):
|
|
||||||
if version:
|
|
||||||
return orig_prepare(
|
|
||||||
version_cap=version).can_send_version(version=v)
|
|
||||||
else:
|
|
||||||
return orig_prepare().can_send_version()
|
|
||||||
csv_mock.side_effect = fake_csv
|
|
||||||
|
|
||||||
retval = getattr(rpcapi, method)(ctxt, **kwargs)
|
|
||||||
self.assertEqual(retval, rpc_mock.return_value)
|
|
||||||
|
|
||||||
if version:
|
|
||||||
prepare_mock.assert_called_once_with(version=version)
|
|
||||||
else:
|
|
||||||
prepare_mock.assert_called_once_with()
|
|
||||||
rpc_mock.assert_called_once_with(ctxt, method, **rpc_kwargs)
|
|
||||||
|
|
||||||
def test_authorize_console(self):
|
|
||||||
self._test_consoleauth_api('authorize_console', token='token',
|
|
||||||
console_type='ctype', host='h', port='p',
|
|
||||||
internal_access_path='iap', instance_uuid="instance",
|
|
||||||
access_url=self.DROPPED_ARG, version='2.0')
|
|
||||||
|
|
||||||
def test_authorize_console_access_url(self):
|
|
||||||
self._test_consoleauth_api('authorize_console', token='token',
|
|
||||||
console_type='ctype', host='h', port='p',
|
|
||||||
internal_access_path='iap', instance_uuid="instance",
|
|
||||||
access_url="fake_access_url", version='2.1')
|
|
||||||
|
|
||||||
def test_check_token(self):
|
|
||||||
self._test_consoleauth_api('check_token', token='t')
|
|
||||||
|
|
||||||
def test_delete_tokens_for_instnace(self):
|
|
||||||
self._test_consoleauth_api('delete_tokens_for_instance',
|
|
||||||
_do_cast=True,
|
|
||||||
instance_uuid="instance")
|
|
@ -57,8 +57,6 @@ class TestProfiler(test.NoDBTestCase):
|
|||||||
'nova.conductor.rpcapi.ConductorAPI',
|
'nova.conductor.rpcapi.ConductorAPI',
|
||||||
'nova.console.manager.ConsoleProxyManager',
|
'nova.console.manager.ConsoleProxyManager',
|
||||||
'nova.console.rpcapi.ConsoleAPI',
|
'nova.console.rpcapi.ConsoleAPI',
|
||||||
'nova.consoleauth.manager.ConsoleAuthManager',
|
|
||||||
'nova.consoleauth.rpcapi.ConsoleAuthAPI',
|
|
||||||
'nova.image.api.API',
|
'nova.image.api.API',
|
||||||
'nova.network.api.API',
|
'nova.network.api.API',
|
||||||
'nova.network.manager.FlatDHCPManager',
|
'nova.network.manager.FlatDHCPManager',
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
The ``nova-consoleauth`` service has been deprecated since the 18.0.0
|
||||||
|
Rocky release and has now been removed. The following configuration
|
||||||
|
options have been removed:
|
||||||
|
|
||||||
|
* ``[upgrade_levels] consoleauth``
|
||||||
|
* ``[workarounds] enable_consoleauth``
|
@ -59,7 +59,6 @@ console_scripts =
|
|||||||
nova-compute = nova.cmd.compute:main
|
nova-compute = nova.cmd.compute:main
|
||||||
nova-conductor = nova.cmd.conductor:main
|
nova-conductor = nova.cmd.conductor:main
|
||||||
nova-console = nova.cmd.console:main
|
nova-console = nova.cmd.console:main
|
||||||
nova-consoleauth = nova.cmd.consoleauth:main
|
|
||||||
nova-dhcpbridge = nova.cmd.dhcpbridge:main
|
nova-dhcpbridge = nova.cmd.dhcpbridge:main
|
||||||
nova-manage = nova.cmd.manage:main
|
nova-manage = nova.cmd.manage:main
|
||||||
nova-network = nova.cmd.network:main
|
nova-network = nova.cmd.network:main
|
||||||
|
Loading…
x
Reference in New Issue
Block a user