252 lines
10 KiB
Python
252 lines
10 KiB
Python
# Copyright 2016 Cloudbase Solutions Srl
|
|
# 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.
|
|
|
|
import mock
|
|
from nova import exception
|
|
|
|
from compute_hyperv.nova import constants
|
|
from compute_hyperv.nova import pathutils
|
|
from compute_hyperv.nova import serialconsolehandler
|
|
from compute_hyperv.nova import serialproxy
|
|
from compute_hyperv.tests.unit import test_base
|
|
|
|
|
|
class SerialConsoleHandlerTestCase(test_base.HyperVBaseTestCase):
|
|
|
|
_autospec_classes = [
|
|
pathutils.PathUtils,
|
|
]
|
|
|
|
def setUp(self):
|
|
super(SerialConsoleHandlerTestCase, self).setUp()
|
|
|
|
pathutils.PathUtils.return_value.mock_get_log_paths.return_value = [
|
|
mock.sentinel.log_path]
|
|
|
|
self._consolehandler = serialconsolehandler.SerialConsoleHandler(
|
|
mock.sentinel.instance_name)
|
|
self._consolehandler._log_path = mock.sentinel.log_path
|
|
|
|
@mock.patch.object(serialconsolehandler.SerialConsoleHandler,
|
|
'_setup_handlers')
|
|
def test_start(self, mock_setup_handlers):
|
|
mock_workers = [mock.Mock(), mock.Mock()]
|
|
self._consolehandler._workers = mock_workers
|
|
|
|
self._consolehandler.start()
|
|
|
|
mock_setup_handlers.assert_called_once_with()
|
|
for worker in mock_workers:
|
|
worker.start.assert_called_once_with()
|
|
|
|
@mock.patch('nova.console.serial.release_port')
|
|
def test_stop(self, mock_release_port):
|
|
mock_serial_proxy = mock.Mock()
|
|
mock_workers = [mock_serial_proxy, mock.Mock()]
|
|
|
|
self._consolehandler._serial_proxy = mock_serial_proxy
|
|
self._consolehandler._listen_host = mock.sentinel.host
|
|
self._consolehandler._listen_port = mock.sentinel.port
|
|
self._consolehandler._workers = mock_workers
|
|
|
|
self._consolehandler.stop()
|
|
|
|
mock_release_port.assert_called_once_with(mock.sentinel.host,
|
|
mock.sentinel.port)
|
|
for worker in mock_workers:
|
|
worker.stop.assert_called_once_with()
|
|
|
|
@mock.patch.object(serialconsolehandler.SerialConsoleHandler,
|
|
'_setup_named_pipe_handlers')
|
|
@mock.patch.object(serialconsolehandler.SerialConsoleHandler,
|
|
'_setup_serial_proxy_handler')
|
|
def _test_setup_handlers(self, mock_setup_proxy, mock_setup_pipe_handlers,
|
|
serial_console_enabled=True):
|
|
self.flags(enabled=serial_console_enabled, group='serial_console')
|
|
|
|
self._consolehandler._setup_handlers()
|
|
|
|
self.assertEqual(serial_console_enabled, mock_setup_proxy.called)
|
|
mock_setup_pipe_handlers.assert_called_once_with()
|
|
|
|
def test_setup_handlers(self):
|
|
self._test_setup_handlers()
|
|
|
|
def test_setup_handlers_console_disabled(self):
|
|
self._test_setup_handlers(serial_console_enabled=False)
|
|
|
|
@mock.patch.object(serialproxy, 'SerialProxy')
|
|
@mock.patch('nova.console.serial.acquire_port')
|
|
@mock.patch.object(serialconsolehandler.threading, 'Event')
|
|
@mock.patch.object(serialconsolehandler.ioutils, 'IOQueue')
|
|
def test_setup_serial_proxy_handler(self, mock_io_queue, mock_event,
|
|
mock_acquire_port,
|
|
mock_serial_proxy_class):
|
|
mock_input_queue = mock.sentinel.input_queue
|
|
mock_output_queue = mock.sentinel.output_queue
|
|
mock_client_connected = mock_event.return_value
|
|
mock_io_queue.side_effect = [mock_input_queue, mock_output_queue]
|
|
mock_serial_proxy = mock_serial_proxy_class.return_value
|
|
|
|
mock_acquire_port.return_value = mock.sentinel.port
|
|
self.flags(proxyclient_address='127.0.0.3',
|
|
group='serial_console')
|
|
|
|
self._consolehandler._setup_serial_proxy_handler()
|
|
|
|
mock_serial_proxy_class.assert_called_once_with(
|
|
mock.sentinel.instance_name,
|
|
'127.0.0.3', mock.sentinel.port,
|
|
mock_input_queue,
|
|
mock_output_queue,
|
|
mock_client_connected)
|
|
|
|
self.assertIn(mock_serial_proxy, self._consolehandler._workers)
|
|
|
|
@mock.patch.object(serialconsolehandler.SerialConsoleHandler,
|
|
'_get_named_pipe_handler')
|
|
@mock.patch.object(serialconsolehandler.SerialConsoleHandler,
|
|
'_get_vm_serial_port_mapping')
|
|
def _mock_setup_named_pipe_handlers(self, mock_get_port_mapping,
|
|
mock_get_pipe_handler,
|
|
serial_port_mapping=None):
|
|
mock_get_port_mapping.return_value = serial_port_mapping
|
|
|
|
self._consolehandler._setup_named_pipe_handlers()
|
|
|
|
expected_workers = [mock_get_pipe_handler.return_value
|
|
for port in serial_port_mapping]
|
|
|
|
self.assertEqual(expected_workers, self._consolehandler._workers)
|
|
|
|
return mock_get_pipe_handler
|
|
|
|
def test_setup_ro_pipe_handler(self):
|
|
serial_port_mapping = {
|
|
constants.SERIAL_PORT_TYPE_RW: mock.sentinel.pipe_path
|
|
}
|
|
|
|
mock_get_handler = self._mock_setup_named_pipe_handlers(
|
|
serial_port_mapping=serial_port_mapping)
|
|
|
|
mock_get_handler.assert_called_once_with(
|
|
mock.sentinel.pipe_path,
|
|
pipe_type=constants.SERIAL_PORT_TYPE_RW,
|
|
enable_logging=True)
|
|
|
|
def test_setup_pipe_handlers(self):
|
|
serial_port_mapping = {
|
|
constants.SERIAL_PORT_TYPE_RO: mock.sentinel.ro_pipe_path,
|
|
constants.SERIAL_PORT_TYPE_RW: mock.sentinel.rw_pipe_path
|
|
}
|
|
|
|
mock_get_handler = self._mock_setup_named_pipe_handlers(
|
|
serial_port_mapping=serial_port_mapping)
|
|
|
|
expected_calls = [mock.call(mock.sentinel.ro_pipe_path,
|
|
pipe_type=constants.SERIAL_PORT_TYPE_RO,
|
|
enable_logging=True),
|
|
mock.call(mock.sentinel.rw_pipe_path,
|
|
pipe_type=constants.SERIAL_PORT_TYPE_RW,
|
|
enable_logging=False)]
|
|
mock_get_handler.assert_has_calls(expected_calls, any_order=True)
|
|
|
|
@mock.patch.object(serialconsolehandler.utilsfactory,
|
|
'get_named_pipe_handler')
|
|
def _test_get_named_pipe_handler(self, mock_get_pipe_handler,
|
|
pipe_type=None, enable_logging=False):
|
|
expected_args = {}
|
|
|
|
if pipe_type == constants.SERIAL_PORT_TYPE_RW:
|
|
self._consolehandler._input_queue = mock.sentinel.input_queue
|
|
self._consolehandler._output_queue = mock.sentinel.output_queue
|
|
self._consolehandler._client_connected = (
|
|
mock.sentinel.connect_event)
|
|
expected_args.update({
|
|
'input_queue': mock.sentinel.input_queue,
|
|
'output_queue': mock.sentinel.output_queue,
|
|
'connect_event': mock.sentinel.connect_event})
|
|
|
|
if enable_logging:
|
|
expected_args['log_file'] = mock.sentinel.log_path
|
|
|
|
ret_val = self._consolehandler._get_named_pipe_handler(
|
|
mock.sentinel.pipe_path, pipe_type, enable_logging)
|
|
|
|
self.assertEqual(mock_get_pipe_handler.return_value, ret_val)
|
|
mock_get_pipe_handler.assert_called_once_with(
|
|
mock.sentinel.pipe_path,
|
|
**expected_args)
|
|
|
|
def test_get_ro_named_pipe_handler(self):
|
|
self._test_get_named_pipe_handler(
|
|
pipe_type=constants.SERIAL_PORT_TYPE_RO,
|
|
enable_logging=True)
|
|
|
|
def test_get_rw_named_pipe_handler(self):
|
|
self._test_get_named_pipe_handler(
|
|
pipe_type=constants.SERIAL_PORT_TYPE_RW,
|
|
enable_logging=False)
|
|
|
|
def _mock_get_port_connections(self, port_connections):
|
|
get_port_connections = (
|
|
self._consolehandler._vmutils.get_vm_serial_port_connections)
|
|
get_port_connections.return_value = port_connections
|
|
|
|
def test_get_vm_serial_port_mapping_having_tagged_pipes(self):
|
|
ro_pipe_path = 'fake_pipe_ro'
|
|
rw_pipe_path = 'fake_pipe_rw'
|
|
self._mock_get_port_connections([ro_pipe_path, rw_pipe_path])
|
|
|
|
ret_val = self._consolehandler._get_vm_serial_port_mapping()
|
|
|
|
expected_mapping = {
|
|
constants.SERIAL_PORT_TYPE_RO: ro_pipe_path,
|
|
constants.SERIAL_PORT_TYPE_RW: rw_pipe_path
|
|
}
|
|
|
|
self.assertEqual(expected_mapping, ret_val)
|
|
|
|
def test_get_vm_serial_port_mapping_untagged_pipe(self):
|
|
pipe_path = 'fake_pipe_path'
|
|
self._mock_get_port_connections([pipe_path])
|
|
|
|
ret_val = self._consolehandler._get_vm_serial_port_mapping()
|
|
|
|
expected_mapping = {constants.SERIAL_PORT_TYPE_RW: pipe_path}
|
|
self.assertEqual(expected_mapping, ret_val)
|
|
|
|
def test_get_vm_serial_port_mapping_exception(self):
|
|
self._mock_get_port_connections([])
|
|
self.assertRaises(exception.NovaException,
|
|
self._consolehandler._get_vm_serial_port_mapping)
|
|
|
|
@mock.patch('nova.console.type.ConsoleSerial')
|
|
def test_get_serial_console(self, mock_serial_console):
|
|
self.flags(enabled=True, group='serial_console')
|
|
self._consolehandler._listen_host = mock.sentinel.host
|
|
self._consolehandler._listen_port = mock.sentinel.port
|
|
|
|
ret_val = self._consolehandler.get_serial_console()
|
|
self.assertEqual(mock_serial_console.return_value, ret_val)
|
|
mock_serial_console.assert_called_once_with(
|
|
host=mock.sentinel.host,
|
|
port=mock.sentinel.port)
|
|
|
|
def test_get_serial_console_disabled(self):
|
|
self.flags(enabled=False, group='serial_console')
|
|
self.assertRaises(exception.ConsoleTypeUnavailable,
|
|
self._consolehandler.get_serial_console)
|