Remove 'nova-console' service, 'console' RPC API

This legacy service was only compatible with the XenServer driver and
has effectively been replaced by the noVNC console proxy service. Remove
the service. The API that provided remote access to this service,
'os-consoles', was removed in a previous change. Note that
'os-remote-consoles' is unrelated and therefore is not removed, though
it will now reject requests for XVP VNC consoles.

This was previously discussed and agreed on openstack-dev [1] and
openstack-discuss [1].

Part of blueprint remove-xvpvncproxy

[1] http://lists.openstack.org/pipermail/openstack-dev/2018-October/135413.html
[2] http://lists.openstack.org/pipermail/openstack-discuss/2019-April/005369.html

Change-Id: Ib1ff32f04b16af7981471f67c8e0bf04e6ecb6be
Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
This commit is contained in:
Stephen Finucane 2019-10-10 12:55:11 +01:00
parent 3062a1199d
commit 6537a537f9
24 changed files with 16 additions and 1063 deletions

View File

@ -31,7 +31,6 @@ redirectmatch 301 ^/nova/([^/]+)/man/nova-cells.html$ /nova/$1/cli/nova-cells.ht
# this is gone and never coming back, indicate that to the end users
redirectmatch 301 ^/nova/([^/]+)/man/nova-compute.html$ /nova/$1/cli/nova-compute.html
redirectmatch 301 ^/nova/([^/]+)/man/nova-conductor.html$ /nova/$1/cli/nova-conductor.html
redirectmatch 301 ^/nova/([^/]+)/man/nova-console.html$ /nova/$1/cli/nova-console.html
redirectmatch 301 ^/nova/([^/]+)/man/nova-dhcpbridge.html$ /nova/$1/cli/nova-dhcpbridge.html
redirectmatch 301 ^/nova/([^/]+)/man/nova-manage.html$ /nova/$1/cli/nova-manage.html
redirectmatch 301 ^/nova/([^/]+)/man/nova-network.html$ /nova/$1/cli/nova-network.html

View File

@ -87,5 +87,4 @@ deployments, but are documented for existing ones.
nova-dhcpbridge
nova-network
nova-console
nova-xvpvncproxy

View File

@ -1,54 +0,0 @@
============
nova-console
============
-------------------
Nova Console Server
-------------------
:Author: openstack@lists.openstack.org
:Copyright: OpenStack Foundation
:Manual section: 1
:Manual group: cloud computing
Synopsis
========
::
nova-console [options]
Description
===========
:program:`nova-console` is a server daemon that serves the Nova Console
service, which is a console proxy to set up multi-tenant VM console access,
e.g. with *XVP*.
.. deprecated:: 19.0.0
:program:`nova-console` is deprecated since 19.0.0 (Stein) and will be
removed in an upcoming release.
Options
=======
**General options**
Files
=====
* ``/etc/nova/nova.conf``
* ``/etc/nova/policy.json``
* ``/etc/nova/rootwrap.conf``
* ``/etc/nova/rootwrap.d/``
See Also
========
* :nova-doc:`OpenStack Nova <>`
Bugs
====
* Nova bugs are managed at `Launchpad <https://bugs.launchpad.net/nova>`__

View File

@ -86,7 +86,6 @@ _man_pages = [
('nova-api-os-compute', u'Cloud controller fabric'),
('nova-compute', u'Cloud controller fabric'),
('nova-conductor', u'Cloud controller fabric'),
('nova-console', u'Cloud controller fabric'),
('nova-dhcpbridge', u'Cloud controller fabric'),
('nova-manage', u'Cloud controller fabric'),
('nova-network', u'Cloud controller fabric'),

View File

@ -31,7 +31,6 @@
/nova/latest/man/nova-cells.html 301 /nova/latest/cli/nova-cells.html
/nova/latest/man/nova-compute.html 301 /nova/latest/cli/nova-compute.html
/nova/latest/man/nova-conductor.html 301 /nova/latest/cli/nova-conductor.html
/nova/latest/man/nova-console.html 301 /nova/latest/cli/nova-console.html
/nova/latest/man/nova-dhcpbridge.html 301 /nova/latest/cli/nova-dhcpbridge.html
/nova/latest/man/nova-manage.html 301 /nova/latest/cli/nova-manage.html
/nova/latest/man/nova-network.html 301 /nova/latest/cli/nova-network.html

View File

@ -1,51 +0,0 @@
# Copyright (c) 2010 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.
"""Starter script for Nova Console Proxy."""
import sys
from oslo_config import cfg
from oslo_log import log as logging
from oslo_reports import guru_meditation_report as gmr
from oslo_reports import opts as gmr_opts
from nova import config
from nova.console import rpcapi as console_rpcapi
from nova import objects
from nova import service
from nova import version
CONF = cfg.CONF
LOG = logging.getLogger('nova.console')
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-console service is deprecated as it is Xen '
'specific, does not function properly in a multi-cell '
'environment, and has effectively been replaced by noVNC '
'and the nova-novncproxy service.')
server = service.Service.create(binary='nova-console',
topic=console_rpcapi.RPC_TOPIC)
service.serve(server)
service.wait()

View File

@ -69,7 +69,6 @@ from nova.compute.utils import wrap_instance_event
from nova.compute import vm_states
from nova import conductor
import nova.conf
from nova.console import rpcapi as console_rpcapi
import nova.context
from nova import exception
from nova import exception_wrapper
@ -1504,6 +1503,7 @@ class ComputeManager(manager.Manager):
except exception.InstanceNotFound:
return power_state.NOSTATE
# TODO(stephenfin): Remove this once we bump the compute API to v6.0
def get_console_topic(self, context):
"""Retrieves the console host for a project on this host.
@ -1511,7 +1511,7 @@ class ComputeManager(manager.Manager):
"""
# TODO(mdragon): perhaps make this variable by console_type?
return '%s.%s' % (console_rpcapi.RPC_TOPIC, CONF.console_host)
return 'console.%s' % CONF.console_host
@wrap_exception()
def get_console_pool_info(self, context, console_type):

View File

@ -709,6 +709,7 @@ class ComputeAPI(object):
return cctxt.call(ctxt, 'get_console_pool_info',
console_type=console_type)
# TODO(stephenfin): This is no longer used and can be removed in v6.0
def get_console_topic(self, ctxt, host):
version = '5.0'
cctxt = self.router.client(ctxt).prepare(

View File

@ -96,18 +96,6 @@ Conductor RPC API version cap.
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('console',
help="""
Console RPC API version cap.
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'.

View File

@ -1,56 +0,0 @@
# Copyright (c) 2010 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.
"""Handles ConsoleProxy API requests."""
from nova.compute import rpcapi as compute_rpcapi
from nova.console import rpcapi as console_rpcapi
from nova.db import base
from nova import objects
class API(base.Base):
"""API for spinning up or down console proxy connections."""
def get_consoles(self, context, instance_uuid):
return self.db.console_get_all_by_instance(context, instance_uuid,
columns_to_join=['pool'])
def get_console(self, context, instance_uuid, console_uuid):
return self.db.console_get(context, console_uuid, instance_uuid)
def delete_console(self, context, instance_uuid, console_uuid):
console = self.db.console_get(context, console_uuid, instance_uuid)
rpcapi = console_rpcapi.ConsoleAPI(topic=console_rpcapi.RPC_TOPIC,
server=console['pool']['host'])
rpcapi.remove_console(context, console['id'])
def create_console(self, context, instance_uuid):
# NOTE(mdragon): If we wanted to return this the console info
# here, as we would need to do a call.
# They can just do an index later to fetch
# console info. I am not sure which is better
# here.
instance = objects.Instance.get_by_uuid(context, instance_uuid)
topic = self._get_console_topic(context, instance.host)
server = None
if '.' in topic:
topic, server = topic.split('.', 1)
rpcapi = console_rpcapi.ConsoleAPI(topic=topic, server=server)
rpcapi.add_console(context, instance.id)
def _get_console_topic(self, context, instance_host):
rpcapi = compute_rpcapi.ComputeAPI()
return rpcapi.get_console_topic(context, instance_host)

View File

@ -1,115 +0,0 @@
# Copyright (c) 2010 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.
"""Console Proxy Service."""
from oslo_log import log as logging
import oslo_messaging as messaging
from nova.compute import rpcapi as compute_rpcapi
import nova.conf
from nova.console import xvp
from nova import exception
from nova import manager
from nova import objects
from nova import utils
CONF = nova.conf.CONF
LOG = logging.getLogger(__name__)
class ConsoleProxyManager(manager.Manager):
"""Sets up and tears down any console proxy connections.
Needed for accessing instance consoles securely.
"""
target = messaging.Target(version='2.0')
def __init__(self, *args, **kwargs):
self.driver = xvp.XVPConsoleProxy()
super(ConsoleProxyManager, self).__init__(service_name='console',
*args, **kwargs)
self.driver.host = self.host
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
def reset(self):
LOG.info('Reloading compute RPC API')
compute_rpcapi.LAST_VERSION = None
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
def init_host(self):
self.driver.init_host()
def add_console(self, context, instance_id):
instance = objects.Instance.get_by_id(context, instance_id)
host = instance.host
name = instance.name
pool = self._get_pool_for_instance_host(context, host)
try:
console = self.db.console_get_by_pool_instance(context,
pool['id'],
instance.uuid)
except exception.NotFound:
LOG.debug('Adding console', instance=instance)
password = utils.generate_password(8)
port = self.driver.get_port(context)
console_data = {'instance_name': name,
'instance_uuid': instance.uuid,
'password': password,
'pool_id': pool['id']}
if port:
console_data['port'] = port
console = self.db.console_create(context, console_data)
self.driver.setup_console(context, console)
return console['id']
def remove_console(self, context, console_id):
try:
console = self.db.console_get(context, console_id)
except exception.NotFound:
LOG.debug('Tried to remove non-existent console '
'%(console_id)s.',
{'console_id': console_id})
return
self.db.console_delete(context, console_id)
self.driver.teardown_console(context, console)
def _get_pool_for_instance_host(self, context, instance_host):
context = context.elevated()
console_type = self.driver.console_type
try:
pool = self.db.console_pool_get_by_host_type(context,
instance_host,
self.host,
console_type)
except exception.NotFound:
# NOTE(mdragon): Right now, the only place this info exists is the
# compute worker's flagfile, at least for
# xenserver. Thus we need to ask.
pool_info = self.compute_rpcapi.get_console_pool_info(context,
instance_host, console_type)
pool_info['password'] = self.driver.fix_pool_password(
pool_info['password'])
pool_info['host'] = self.host
pool_info['public_hostname'] = \
CONF.xenserver.console_public_hostname
pool_info['console_type'] = self.driver.console_type
pool_info['compute_host'] = instance_host
pool = self.db.console_pool_create(context, pool_info)
return pool

View File

@ -1,77 +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 console RPC API.
"""
import oslo_messaging as messaging
import nova.conf
from nova import profiler
from nova import rpc
CONF = nova.conf.CONF
RPC_TOPIC = "console"
@profiler.trace_cls("rpc")
class ConsoleAPI(object):
'''Client side of the console rpc API.
API version history:
1.0 - Initial version.
1.1 - Added get_backdoor_port()
... Grizzly and Havana support message version 1.1. So, any changes to
existing methods in 1.x after that point should be done such that they
can handle the version_cap being set to 1.1.
2.0 - Major API rev for Icehouse
... Icehouse, Juno, Kilo, Liberty, Mitaka, Newton, and Ocata 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.
'''
VERSION_ALIASES = {
'grizzly': '1.1',
'havana': '1.1',
'icehouse': '2.0',
'juno': '2.0',
'kilo': '2.0',
'liberty': '2.0',
'mitaka': '2.0',
'newton': '2.0',
'ocata': '2.0',
}
def __init__(self, topic=None, server=None):
super(ConsoleAPI, self).__init__()
topic = topic if topic else RPC_TOPIC
target = messaging.Target(topic=topic, server=server, version='2.0')
version_cap = self.VERSION_ALIASES.get(CONF.upgrade_levels.console,
CONF.upgrade_levels.console)
self.client = rpc.get_client(target, version_cap=version_cap)
def add_console(self, ctxt, instance_id):
cctxt = self.client.prepare()
cctxt.cast(ctxt, 'add_console', instance_id=instance_id)
def remove_console(self, ctxt, console_id):
cctxt = self.client.prepare()
cctxt.cast(ctxt, 'remove_console', console_id=console_id)

View File

@ -1413,21 +1413,7 @@ def project_get_networks(context, project_id, associate=True):
return IMPL.project_get_networks(context, project_id, associate)
###################
def console_pool_create(context, values):
"""Create console pool."""
return IMPL.console_pool_create(context, values)
def console_pool_get_by_host_type(context, compute_host, proxy_host,
console_type):
"""Fetch a console pool for a given proxy host, compute host, and type."""
return IMPL.console_pool_get_by_host_type(context,
compute_host,
proxy_host,
console_type)
##################
def console_pool_get_all_by_host_type(context, host, console_type):
@ -1437,31 +1423,6 @@ def console_pool_get_all_by_host_type(context, host, console_type):
console_type)
def console_create(context, values):
"""Create a console."""
return IMPL.console_create(context, values)
def console_delete(context, console_id):
"""Delete a console."""
return IMPL.console_delete(context, console_id)
def console_get_by_pool_instance(context, pool_id, instance_uuid):
"""Get console entry for a given instance and pool."""
return IMPL.console_get_by_pool_instance(context, pool_id, instance_uuid)
def console_get_all_by_instance(context, instance_uuid, columns_to_join=None):
"""Get consoles for a given instance."""
return IMPL.console_get_all_by_instance(context, instance_uuid,
columns_to_join)
def console_get(context, console_id, instance_uuid=None):
"""Get a specific console (possibly on a given instance)."""
return IMPL.console_get(context, console_id, instance_uuid)
##################

View File

@ -4487,40 +4487,6 @@ def migration_migrate_to_uuid(context, count):
##################
@pick_context_manager_writer
def console_pool_create(context, values):
pool = models.ConsolePool()
pool.update(values)
try:
pool.save(context.session)
except db_exc.DBDuplicateEntry:
raise exception.ConsolePoolExists(
host=values["host"],
console_type=values["console_type"],
compute_host=values["compute_host"],
)
return pool
@pick_context_manager_reader
def console_pool_get_by_host_type(context, compute_host, host,
console_type):
result = model_query(context, models.ConsolePool, read_deleted="no").\
filter_by(host=host).\
filter_by(console_type=console_type).\
filter_by(compute_host=compute_host).\
options(joinedload('consoles')).\
first()
if not result:
raise exception.ConsolePoolNotFoundForHostType(
host=host, console_type=console_type,
compute_host=compute_host)
return result
@pick_context_manager_reader
def console_pool_get_all_by_host_type(context, host, console_type):
return model_query(context, models.ConsolePool, read_deleted="no").\
@ -4530,71 +4496,6 @@ def console_pool_get_all_by_host_type(context, host, console_type):
all()
##################
@pick_context_manager_writer
def console_create(context, values):
console = models.Console()
console.update(values)
console.save(context.session)
return console
@pick_context_manager_writer
def console_delete(context, console_id):
# NOTE(mdragon): consoles are meant to be transient.
context.session.query(models.Console).\
filter_by(id=console_id).\
delete()
@pick_context_manager_reader
def console_get_by_pool_instance(context, pool_id, instance_uuid):
result = model_query(context, models.Console, read_deleted="yes").\
filter_by(pool_id=pool_id).\
filter_by(instance_uuid=instance_uuid).\
options(joinedload('pool')).\
first()
if not result:
raise exception.ConsoleNotFoundInPoolForInstance(
pool_id=pool_id, instance_uuid=instance_uuid)
return result
@pick_context_manager_reader
def console_get_all_by_instance(context, instance_uuid, columns_to_join=None):
query = model_query(context, models.Console, read_deleted="yes").\
filter_by(instance_uuid=instance_uuid)
if columns_to_join:
for column in columns_to_join:
query = query.options(joinedload(column))
return query.all()
@pick_context_manager_reader
def console_get(context, console_id, instance_uuid=None):
query = model_query(context, models.Console, read_deleted="yes").\
filter_by(id=console_id).\
options(joinedload('pool'))
if instance_uuid is not None:
query = query.filter_by(instance_uuid=instance_uuid)
result = query.first()
if not result:
if instance_uuid:
raise exception.ConsoleNotFoundForInstance(
instance_uuid=instance_uuid)
else:
raise exception.ConsoleNotFound(console_id=console_id)
return result
########################
# User-provided metadata

View File

@ -1001,6 +1001,7 @@ class ConsolePool(BASE, NovaBase, models.SoftDeleteMixin):
compute_host = Column(String(255))
# TODO(stephenfin): Remove in V or later
class Console(BASE, NovaBase, models.SoftDeleteMixin):
"""Represents a console session for an instance."""
__tablename__ = 'consoles'

View File

@ -1203,35 +1203,10 @@ class ConsoleLogOutputException(NovaException):
"%(instance_id)s. Reason: %(reason)s")
class ConsolePoolExists(NovaException):
msg_fmt = _("Console pool with host %(host)s, console_type "
"%(console_type)s and compute_host %(compute_host)s "
"already exists.")
class ConsolePoolNotFoundForHostType(NotFound):
msg_fmt = _("Console pool of type %(console_type)s "
"for compute host %(compute_host)s "
"on proxy host %(host)s not found.")
class ConsoleNotFound(NotFound):
msg_fmt = _("Console %(console_id)s could not be found.")
class ConsoleNotFoundForInstance(ConsoleNotFound):
msg_fmt = _("Console for instance %(instance_uuid)s could not be found.")
class ConsoleNotAvailable(NotFound):
msg_fmt = _("Guest does not have a console available.")
class ConsoleNotFoundInPoolForInstance(ConsoleNotFound):
msg_fmt = _("Console for instance %(instance_uuid)s "
"in pool %(pool_id)s could not be found.")
class ConsoleTypeInvalid(Invalid):
msg_fmt = _("Invalid console type %(console_type)s")

View File

@ -796,6 +796,8 @@ class NotificationSource(BaseNovaEnum):
# TODO(stephenfin): Remove when 'NotificationPublisher' object version is
# bumped to 3.0
CELLS = 'nova-cells'
# TODO(stephenfin): Remove when 'NotificationPublisher' object version is
# bumped to 3.0
CONSOLE = 'nova-console'
METADATA = 'nova-metadata'

View File

@ -54,7 +54,6 @@ CONF = nova.conf.CONF
SERVICE_MANAGERS = {
'nova-compute': 'nova.compute.manager.ComputeManager',
'nova-console': 'nova.console.manager.ConsoleProxyManager',
'nova-conductor': 'nova.conductor.manager.ConductorManager',
'nova-metadata': 'nova.api.manager.MetadataManager',
'nova-scheduler': 'nova.scheduler.manager.SchedulerManager',

View File

@ -1,199 +0,0 @@
# Copyright (c) 2010 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 Console proxy."""
import fixtures
import mock
from nova.compute import rpcapi as compute_rpcapi
from nova.console import api as console_api
from nova.console import manager as console_manager
from nova import context
from nova.db import api as db
from nova import exception
from nova import objects
from nova import test
from nova.tests.unit import fake_instance
from nova.tests.unit import fake_xvp_console_proxy
class ConsoleTestCase(test.TestCase):
"""Test case for console proxy manager."""
def setUp(self):
super(ConsoleTestCase, self).setUp()
self.useFixture(fixtures.MonkeyPatch(
'nova.console.manager.xvp.XVPConsoleProxy',
fake_xvp_console_proxy.FakeConsoleProxy))
self.console = console_manager.ConsoleProxyManager()
self.user_id = 'fake'
self.project_id = 'fake'
self.context = context.RequestContext(self.user_id, self.project_id)
self.host = 'test_compute_host'
self.pool_info = {'address': '127.0.0.1',
'username': 'test',
'password': '1234pass'}
def test_reset(self):
with mock.patch('nova.compute.rpcapi.ComputeAPI') as mock_rpc:
old_rpcapi = self.console.compute_rpcapi
self.console.reset()
mock_rpc.assert_called_once_with()
self.assertNotEqual(old_rpcapi,
self.console.compute_rpcapi)
def _create_instance(self):
"""Create a test instance."""
inst = {}
inst['image_id'] = 1
inst['reservation_id'] = 'r-fakeres'
inst['user_id'] = self.user_id
inst['project_id'] = self.project_id
inst['instance_type_id'] = 1
inst['ami_launch_index'] = 0
return fake_instance.fake_instance_obj(self.context, **inst)
@mock.patch.object(compute_rpcapi.ComputeAPI, 'get_console_pool_info')
def test_get_pool_for_instance_host(self, mock_get):
mock_get.return_value = self.pool_info
pool = self.console._get_pool_for_instance_host(self.context,
self.host)
self.assertEqual(pool['compute_host'], self.host)
@mock.patch.object(compute_rpcapi.ComputeAPI, 'get_console_pool_info')
def test_get_pool_creates_new_pool_if_needed(self, mock_get):
mock_get.return_value = self.pool_info
self.assertRaises(exception.NotFound,
db.console_pool_get_by_host_type,
self.context,
self.host,
self.console.host,
self.console.driver.console_type)
pool = self.console._get_pool_for_instance_host(self.context,
self.host)
pool2 = db.console_pool_get_by_host_type(self.context,
self.host,
self.console.host,
self.console.driver.console_type)
self.assertEqual(pool['id'], pool2['id'])
def test_get_pool_does_not_create_new_pool_if_exists(self):
pool_info = {'address': '127.0.0.1',
'username': 'test',
'password': '1234pass',
'host': self.console.host,
'console_type': self.console.driver.console_type,
'compute_host': 'sometesthostname'}
new_pool = db.console_pool_create(self.context, pool_info)
pool = self.console._get_pool_for_instance_host(self.context,
'sometesthostname')
self.assertEqual(pool['id'], new_pool['id'])
@mock.patch.object(compute_rpcapi.ComputeAPI, 'get_console_pool_info')
@mock.patch('nova.objects.instance.Instance.get_by_id')
def test_add_console(self, mock_id, mock_get):
mock_get.return_value = self.pool_info
instance = self._create_instance()
mock_id.return_value = instance
self.console.add_console(self.context, instance.id)
pool = db.console_pool_get_by_host_type(self.context,
instance.host, self.console.host,
self.console.driver.console_type)
console_instances = [con['instance_uuid'] for con in pool['consoles']]
self.assertIn(instance.uuid, console_instances)
@mock.patch.object(compute_rpcapi.ComputeAPI, 'get_console_pool_info')
@mock.patch('nova.objects.instance.Instance.get_by_id')
def test_add_console_does_not_duplicate(self, mock_id, mock_get):
mock_get.return_value = self.pool_info
instance = self._create_instance()
mock_id.return_value = instance
cons1 = self.console.add_console(self.context, instance.id)
cons2 = self.console.add_console(self.context, instance.id)
self.assertEqual(cons1, cons2)
@mock.patch.object(compute_rpcapi.ComputeAPI, 'get_console_pool_info')
@mock.patch('nova.objects.instance.Instance.get_by_id')
def test_remove_console(self, mock_id, mock_get):
mock_get.return_value = self.pool_info
instance = self._create_instance()
mock_id.return_value = instance
console_id = self.console.add_console(self.context, instance.id)
self.console.remove_console(self.context, console_id)
self.assertRaises(exception.NotFound,
db.console_get,
self.context,
console_id)
class ConsoleAPITestCase(test.NoDBTestCase):
"""Test case for console API."""
def setUp(self):
super(ConsoleAPITestCase, self).setUp()
self.context = context.RequestContext('fake', 'fake')
self.console_api = console_api.API()
self.fake_uuid = '00000000-aaaa-bbbb-cccc-000000000000'
self.fake_instance = {
'id': 1,
'uuid': self.fake_uuid,
'host': 'fake_host'
}
self.fake_console = {
'pool': {'host': 'fake_host'},
'id': 'fake_id'
}
def _fake_db_console_get(_ctxt, _console_uuid, _instance_uuid):
return self.fake_console
self.stub_out('nova.db.api.console_get', _fake_db_console_get)
def _fake_db_console_get_all_by_instance(_ctxt, _instance_uuid,
columns_to_join):
return [self.fake_console]
self.stub_out('nova.db.api.console_get_all_by_instance',
_fake_db_console_get_all_by_instance)
def test_get_consoles(self):
console = self.console_api.get_consoles(self.context, self.fake_uuid)
self.assertEqual(console, [self.fake_console])
def test_get_console(self):
console = self.console_api.get_console(self.context, self.fake_uuid,
'fake_id')
self.assertEqual(console, self.fake_console)
@mock.patch('nova.console.rpcapi.ConsoleAPI.remove_console')
def test_delete_console(self, mock_remove):
self.console_api.delete_console(self.context, self.fake_uuid,
'fake_id')
mock_remove.assert_called_once_with(self.context, 'fake_id')
@mock.patch.object(compute_rpcapi.ComputeAPI, 'get_console_topic',
return_value='compute.fake_host')
@mock.patch.object(objects.Instance, 'get_by_uuid')
def test_create_console(self, mock_get_instance_by_uuid,
mock_get_console_topic):
mock_get_instance_by_uuid.return_value = objects.Instance(
**self.fake_instance)
self.console_api.create_console(self.context, self.fake_uuid)
mock_get_console_topic.assert_called_once_with(self.context,
'fake_host')

View File

@ -1,61 +0,0 @@
# Copyright 2012, 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.console.rpcapi
"""
import mock
from nova.console import rpcapi as console_rpcapi
from nova import context
from nova import test
class ConsoleRpcAPITestCase(test.NoDBTestCase):
def _test_console_api(self, method, rpc_method, **kwargs):
ctxt = context.RequestContext('fake_user', 'fake_project')
rpcapi = console_rpcapi.ConsoleAPI()
self.assertIsNotNone(rpcapi.client)
self.assertEqual(rpcapi.client.target.topic,
console_rpcapi.RPC_TOPIC)
orig_prepare = rpcapi.client.prepare
with test.nested(
mock.patch.object(rpcapi.client, rpc_method),
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 = 'foo' if rpc_method == 'call' else None
csv_mock.side_effect = (
lambda v: orig_prepare().can_send_version())
retval = getattr(rpcapi, method)(ctxt, **kwargs)
self.assertEqual(retval, rpc_mock.return_value)
prepare_mock.assert_called_once_with()
rpc_mock.assert_called_once_with(ctxt, method, **kwargs)
def test_add_console(self):
self._test_console_api('add_console', instance_id='i',
rpc_method='cast')
def test_remove_console(self):
self._test_console_api('remove_console', console_id='i',
rpc_method='cast')

View File

@ -3210,25 +3210,6 @@ class InstanceTestCase(test.TestCase, ModelsObjectComparatorMixin):
db.virtual_interface_create(ctxt, {'instance_uuid': uuid})
pool_values = {
'address': '192.168.10.10',
'username': 'user1',
'password': 'passwd1',
'console_type': 'type1',
'public_hostname': 'public_host1',
'host': 'host1',
'compute_host': 'compute_host1',
}
console_pool = db.console_pool_create(ctxt, pool_values)
console_values = {
'instance_name': instance['name'],
'instance_uuid': uuid,
'password': 'pass',
'port': 7878,
'pool_id': console_pool['id']
}
db.console_create(self.ctxt, console_values)
# Hard delete the instance
db.instance_destroy(ctxt, uuid, hard_delete=True)
@ -3260,10 +3241,7 @@ class InstanceTestCase(test.TestCase, ModelsObjectComparatorMixin):
_assert_instance_id_mapping(ctxt, self, uuid)
self.assertRaises(exception.InstanceNotFound,
db.instance_destroy, ctxt, uuid)
# NOTE(ttsiouts): Should these also be valid?
# instance_consoles = db.console_get_all_by_instance(ctxt, uuid)
# self.assertEqual(0, len(instance_consoles))
# Also FixedIp has the instance_uuid as a foreign key
# NOTE(ttsiouts): FixedIp has the instance_uuid as a foreign key
def test_check_instance_exists(self):
instance = self.create_instance_with_args()
@ -7916,207 +7894,6 @@ class CertificateTestCase(test.TestCase, ModelsObjectComparatorMixin):
self._assertEqualObjects(self.created[1], cert[0])
class ConsoleTestCase(test.TestCase, ModelsObjectComparatorMixin):
def setUp(self):
super(ConsoleTestCase, self).setUp()
self.ctxt = context.get_admin_context()
pools_data = [
{'address': '192.168.10.10',
'username': 'user1',
'password': 'passwd1',
'console_type': 'type1',
'public_hostname': 'public_host1',
'host': 'host1',
'compute_host': 'compute_host1',
},
{'address': '192.168.10.11',
'username': 'user2',
'password': 'passwd2',
'console_type': 'type2',
'public_hostname': 'public_host2',
'host': 'host2',
'compute_host': 'compute_host2',
},
]
self.console_pools = [db.console_pool_create(self.ctxt, val)
for val in pools_data]
instance_uuid = uuidsentinel.uuid1
db.instance_create(self.ctxt, {'uuid': instance_uuid})
self.console_data = [{'instance_name': 'name' + str(x),
'instance_uuid': instance_uuid,
'password': 'pass' + str(x),
'port': 7878 + x,
'pool_id': self.console_pools[x]['id']}
for x in range(len(pools_data))]
self.consoles = [db.console_create(self.ctxt, val)
for val in self.console_data]
def test_console_create(self):
ignored_keys = ['id', 'deleted', 'deleted_at', 'created_at',
'updated_at']
for console in self.consoles:
self.assertIsNotNone(console['id'])
self._assertEqualListsOfObjects(self.console_data, self.consoles,
ignored_keys=ignored_keys)
def test_console_get_by_id(self):
console = self.consoles[0]
console_get = db.console_get(self.ctxt, console['id'])
self._assertEqualObjects(console, console_get,
ignored_keys=['pool'])
def test_console_get_by_id_uuid(self):
console = self.consoles[0]
console_get = db.console_get(self.ctxt, console['id'],
console['instance_uuid'])
self._assertEqualObjects(console, console_get,
ignored_keys=['pool'])
def test_console_get_by_pool_instance(self):
console = self.consoles[0]
console_get = db.console_get_by_pool_instance(self.ctxt,
console['pool_id'], console['instance_uuid'])
self._assertEqualObjects(console, console_get,
ignored_keys=['pool'])
def test_console_get_all_by_instance(self):
instance_uuid = self.consoles[0]['instance_uuid']
consoles_get = db.console_get_all_by_instance(self.ctxt, instance_uuid)
self._assertEqualListsOfObjects(self.consoles, consoles_get)
def test_console_get_all_by_instance_with_pool(self):
instance_uuid = self.consoles[0]['instance_uuid']
consoles_get = db.console_get_all_by_instance(self.ctxt, instance_uuid,
columns_to_join=['pool'])
self._assertEqualListsOfObjects(self.consoles, consoles_get,
ignored_keys=['pool'])
self._assertEqualListsOfObjects([pool for pool in self.console_pools],
[c['pool'] for c in consoles_get])
def test_console_get_all_by_instance_empty(self):
consoles_get = db.console_get_all_by_instance(self.ctxt,
uuidsentinel.uuid2)
self.assertEqual(consoles_get, [])
def test_console_delete(self):
console_id = self.consoles[0]['id']
db.console_delete(self.ctxt, console_id)
self.assertRaises(exception.ConsoleNotFound, db.console_get,
self.ctxt, console_id)
def test_console_get_by_pool_instance_not_found(self):
self.assertRaises(exception.ConsoleNotFoundInPoolForInstance,
db.console_get_by_pool_instance, self.ctxt,
self.consoles[0]['pool_id'],
uuidsentinel.uuid2)
def test_console_get_not_found(self):
self.assertRaises(exception.ConsoleNotFound, db.console_get,
self.ctxt, 100500)
def test_console_get_not_found_instance(self):
self.assertRaises(exception.ConsoleNotFoundForInstance, db.console_get,
self.ctxt, self.consoles[0]['id'],
uuidsentinel.uuid2)
class ConsolePoolTestCase(test.TestCase, ModelsObjectComparatorMixin):
def setUp(self):
super(ConsolePoolTestCase, self).setUp()
self.ctxt = context.get_admin_context()
self.test_console_pool_1 = {
'address': '192.168.2.10',
'username': 'user_1',
'password': 'secret_123',
'console_type': 'type_1',
'public_hostname': 'public_hostname_123',
'host': 'localhost',
'compute_host': '127.0.0.1',
}
self.test_console_pool_2 = {
'address': '192.168.2.11',
'username': 'user_2',
'password': 'secret_1234',
'console_type': 'type_2',
'public_hostname': 'public_hostname_1234',
'host': '127.0.0.1',
'compute_host': 'localhost',
}
self.test_console_pool_3 = {
'address': '192.168.2.12',
'username': 'user_3',
'password': 'secret_12345',
'console_type': 'type_2',
'public_hostname': 'public_hostname_12345',
'host': '127.0.0.1',
'compute_host': '192.168.1.1',
}
def test_console_pool_create(self):
console_pool = db.console_pool_create(
self.ctxt, self.test_console_pool_1)
self.assertIsNotNone(console_pool.get('id'))
ignored_keys = ['deleted', 'created_at', 'updated_at',
'deleted_at', 'id']
self._assertEqualObjects(
console_pool, self.test_console_pool_1, ignored_keys)
def test_console_pool_create_duplicate(self):
db.console_pool_create(self.ctxt, self.test_console_pool_1)
self.assertRaises(exception.ConsolePoolExists, db.console_pool_create,
self.ctxt, self.test_console_pool_1)
def test_console_pool_get_by_host_type(self):
params = [
self.test_console_pool_1,
self.test_console_pool_2,
]
for p in params:
db.console_pool_create(self.ctxt, p)
ignored_keys = ['deleted', 'created_at', 'updated_at',
'deleted_at', 'id', 'consoles']
cp = self.test_console_pool_1
db_cp = db.console_pool_get_by_host_type(
self.ctxt, cp['compute_host'], cp['host'], cp['console_type']
)
self._assertEqualObjects(cp, db_cp, ignored_keys)
def test_console_pool_get_by_host_type_no_resuls(self):
self.assertRaises(
exception.ConsolePoolNotFoundForHostType,
db.console_pool_get_by_host_type, self.ctxt, 'compute_host',
'host', 'console_type')
def test_console_pool_get_all_by_host_type(self):
params = [
self.test_console_pool_1,
self.test_console_pool_2,
self.test_console_pool_3,
]
for p in params:
db.console_pool_create(self.ctxt, p)
ignored_keys = ['deleted', 'created_at', 'updated_at',
'deleted_at', 'id', 'consoles']
cp = self.test_console_pool_2
db_cp = db.console_pool_get_all_by_host_type(
self.ctxt, cp['host'], cp['console_type'])
self._assertEqualListsOfObjects(
db_cp, [self.test_console_pool_2, self.test_console_pool_3],
ignored_keys)
def test_console_pool_get_all_by_host_type_no_results(self):
res = db.console_pool_get_all_by_host_type(
self.ctxt, 'cp_host', 'cp_console_type')
self.assertEqual([], res)
class DnsdomainTestCase(test.TestCase):
def setUp(self):
@ -8455,9 +8232,6 @@ class ArchiveTestCase(test.TestCase, ModelsObjectComparatorMixin):
self.dns_domains = models.DNSDomain.__table__
self.shadow_dns_domains = sqlalchemyutils.get_table(
self.engine, "shadow_dns_domains")
self.consoles = models.Console.__table__
self.shadow_consoles = sqlalchemyutils.get_table(
self.engine, "shadow_consoles")
self.console_pools = models.ConsolePool.__table__
self.shadow_console_pools = sqlalchemyutils.get_table(
self.engine, "shadow_console_pools")
@ -8813,41 +8587,6 @@ class ArchiveTestCase(test.TestCase, ModelsObjectComparatorMixin):
'sqlite version too old for reliable SQLA foreign_keys')
self.conn.execute("PRAGMA foreign_keys = ON")
def test_archive_deleted_rows_fk_constraint(self):
# consoles.pool_id depends on console_pools.id
self._check_sqlite_version_less_than_3_7()
ins_stmt = self.console_pools.insert().values(deleted=1,
deleted_at=timeutils.utcnow())
result = self.conn.execute(ins_stmt)
id1 = result.inserted_primary_key[0]
ins_stmt = self.consoles.insert().values(deleted=1,
deleted_at=timeutils.utcnow(),
pool_id=id1)
result = self.conn.execute(ins_stmt)
result.inserted_primary_key[0]
# The first try to archive console_pools should fail, due to FK.
num = sqlalchemy_api._archive_deleted_rows_for_table(self.metadata,
"console_pools",
max_rows=None,
before=None)
self.assertEqual(num[0], 0)
# Then archiving consoles should work.
num = sqlalchemy_api._archive_deleted_rows_for_table(self.metadata,
"consoles",
max_rows=None,
before=None)
self.assertEqual(num[0], 1)
# Then archiving console_pools should work.
num = sqlalchemy_api._archive_deleted_rows_for_table(self.metadata,
"console_pools",
max_rows=None,
before=None)
self.assertEqual(num[0], 1)
self._assert_shadow_tables_empty_except(
'shadow_console_pools',
'shadow_consoles'
)
def test_archive_deleted_rows_for_migrations(self):
# migrations.instance_uuid depends on instances.uuid
self._check_sqlite_version_less_than_3_7()

View File

@ -55,8 +55,6 @@ class TestProfiler(test.NoDBTestCase):
'nova.conductor.manager.ConductorManager',
'nova.conductor.rpcapi.ComputeTaskAPI',
'nova.conductor.rpcapi.ConductorAPI',
'nova.console.manager.ConsoleProxyManager',
'nova.console.rpcapi.ConsoleAPI',
'nova.image.api.API',
'nova.network.api.API',
'nova.network.manager.FlatDHCPManager',

View File

@ -1,7 +1,13 @@
---
upgrade:
- |
The following APIs have been removed. Calling these APIs will
The ``nova-console`` service has been deprecated since the 19.0.0 Stein
release and has now been removed. The following configuration options are
therefore removed.
* ``[upgrade_levels] console``
In addition, the following APIs have been removed. Calling these APIs will
now result in a ``410 HTTPGone`` error response:
* ``POST /servers/{server_id}/consoles``
@ -9,7 +15,7 @@ upgrade:
* ``GET /servers/{server_id}/consoles/{console_id}``
* ``DELETE /servers/{server_id}/consoles/{console_id}``
In addition, the following policies are removed. These were related to the
Finally, the following policies are removed. These were related to the
removed APIs listed above and no longer had any effect:
* ``os_compute_api:os-consoles:index``

View File

@ -66,7 +66,6 @@ console_scripts =
nova-api-os-compute = nova.cmd.api_os_compute:main
nova-compute = nova.cmd.compute:main
nova-conductor = nova.cmd.conductor:main
nova-console = nova.cmd.console:main
nova-dhcpbridge = nova.cmd.dhcpbridge:main
nova-manage = nova.cmd.manage:main
nova-network = nova.cmd.network:main