Add some compute console operations

In order to switch OSC to SDK for managing consoles add few missing
(optimize existing) bits:
- add server.get_console_url (deprecated, but still might functional)
- add tests for server_remote_console
- add compute.create_console, which depending on the supported
  microversion invokes appropriate underlaying operation.
- add some console type validations to fail early

Change-Id: I63e752e70fb394fadf88057d71669514532cdddf
This commit is contained in:
Artem Goncharov 2020-09-11 12:23:58 +02:00
parent 1195f95016
commit ec8f810f05
7 changed files with 260 additions and 0 deletions

View File

@ -31,6 +31,7 @@ from openstack.compute.v2 import volume_attachment as _volume_attachment
from openstack.network.v2 import security_group as _sg
from openstack import proxy
from openstack import resource
from openstack import utils
class Proxy(proxy.Proxy):
@ -1488,6 +1489,49 @@ class Proxy(proxy.Proxy):
return self._create(_server_remote_console.ServerRemoteConsole,
server_id=server_id, **attrs)
def get_server_console_url(self, server, console_type):
"""Create a remote console on the server.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:param console_type: Type of the console connection.
:returns: Dictionary with console type and url
"""
server = self._get_resource(_server.Server, server)
return server.get_console_url(self, console_type)
def create_console(self, server, console_type, console_protocol=None):
"""Create a remote console on the server.
When microversion supported is higher then 2.6 remote console is
created, otherwise deprecated call to get server console is issued.
:param server: Either the ID of a server or a
:class:`~openstack.compute.v2.server.Server` instance.
:param console_type: Type of the remote console. Supported values as:
* novnc
* spice-html5
* rdp-html5
* serial
* webmks (supported after 2.8)
:param console_protocol: Optional console protocol (is respected only
after microversion 2.6).
:returns: Dictionary with console type, url and optionally protocol.
"""
server = self._get_resource(_server.Server, server)
# NOTE: novaclient supports undocumented type xcpvnc also supported
# historically by OSC. We support it, but do not document either.
if utils.supports_microversion(self, '2.6'):
console = self._create(
_server_remote_console.ServerRemoteConsole,
server_id=server.id,
type=console_type,
protocol=console_protocol)
return console.to_dict()
else:
return server.get_console_url(self, console_type)
def _get_cleanup_dependencies(self):
return {
'compute': {

View File

@ -17,6 +17,15 @@ from openstack import resource
from openstack import utils
CONSOLE_TYPE_ACTION_MAPPING = {
'novnc': 'os-getVNCConsole',
'xvpvnc': 'os-getVNCConsole',
'spice-html5': 'os-getSPICEConsole',
'rdp-html5': 'os-getRDPConsole',
'serial': 'os-getSerialConsole'
}
class Server(resource.Resource, metadata.MetadataMixin, resource.TagMixin):
resource_key = 'server'
resources_key = 'servers'
@ -475,6 +484,14 @@ class Server(resource.Resource, metadata.MetadataMixin, resource.TagMixin):
block_migration=block_migration,
disk_over_commit=disk_over_commit)
def get_console_url(self, session, console_type):
action = CONSOLE_TYPE_ACTION_MAPPING.get(console_type)
if not action:
raise ValueError("Unsupported console type")
body = {action: {'type': console_type}}
resp = self._action(session, body)
return resp.json().get('console')
def _live_migrate_30(self, session, host, force, block_migration):
microversion = '2.30'
body = {'host': None}

View File

@ -12,6 +12,17 @@
from openstack import resource
from openstack import utils
CONSOLE_TYPE_PROTOCOL_MAPPING = {
'novnc': 'vnc',
'xvpvnc': 'vnc',
'spice-html5': 'spice',
'rdp-html5': 'rdp',
'serial': 'serial',
'webmks': 'mks'
}
class ServerRemoteConsole(resource.Resource):
resource_key = 'remote_console'
@ -34,3 +45,19 @@ class ServerRemoteConsole(resource.Resource):
url = resource.Body('url')
#: The ID for the server.
server_id = resource.URI('server_id')
def create(self, session, prepend_key=True, base_path=None, **params):
if not self.protocol:
self.protocol = \
CONSOLE_TYPE_PROTOCOL_MAPPING.get(self.type)
if (
not utils.supports_microversion(session, '2.8')
and self.type == 'webmks'
):
raise ValueError('Console type webmks is not supported on '
'server side')
return super(ServerRemoteConsole, self).create(
session,
prepend_key=prepend_key,
base_path=base_path,
**params)

View File

@ -23,6 +23,7 @@ from openstack.compute.v2 import server
from openstack.compute.v2 import server_group
from openstack.compute.v2 import server_interface
from openstack.compute.v2 import server_ip
from openstack.compute.v2 import server_remote_console
from openstack.compute.v2 import service
from openstack.tests.unit import test_proxy_base
@ -564,3 +565,53 @@ class TestComputeProxy(test_proxy_base.TestProxyBase):
self.proxy.remove_security_group_from_server,
method_args=["value", {'id': 'id', 'name': 'sg'}],
expected_args=['sg'])
def test_create_server_remote_console(self):
self.verify_create(
self.proxy.create_server_remote_console,
server_remote_console.ServerRemoteConsole,
method_kwargs={"server": "test_id", "type": "fake"},
expected_kwargs={"server_id": "test_id", "type": "fake"})
def test_get_console_url(self):
self._verify(
'openstack.compute.v2.server.Server.get_console_url',
self.proxy.get_server_console_url,
method_args=["value", "console_type"],
expected_args=["console_type"])
def test_create_console(self):
with \
mock.patch('openstack.utils.supports_microversion') as smv, \
mock.patch('openstack.compute.v2._proxy.Proxy._create') as rcc, \
mock.patch('openstack.compute.v2.server.Server.get_console_url') \
as sgc:
console_fake = {
'url': 'a',
'type': 'b',
'protocol': 'c'
}
smv.return_value = False
sgc.return_value = console_fake
ret = self.proxy.create_console('fake_server', 'fake_type')
smv.assert_called_once_with(self.proxy, '2.6')
rcc.assert_not_called()
sgc.assert_called_with(self.proxy, 'fake_type')
self.assertDictEqual(console_fake, ret)
smv.reset_mock()
sgc.reset_mock()
rcc.reset_mock()
# Test server_remote_console is triggered when mv>=2.6
smv.return_value = True
rcc.return_value = server_remote_console.ServerRemoteConsole(
**console_fake)
ret = self.proxy.create_console('fake_server', 'fake_type')
smv.assert_called_once_with(self.proxy, '2.6')
sgc.assert_not_called()
rcc.assert_called_with(server_remote_console.ServerRemoteConsole,
server_id='fake_server',
type='fake_type',
protocol=None)
self.assertEqual(console_fake['url'], ret['url'])

View File

@ -803,6 +803,52 @@ class TestServer(base.TestCase):
self.sess.post.assert_called_with(
url, json=body, headers=headers, microversion=None)
def test_get_console_url(self):
sot = server.Server(**EXAMPLE)
resp = mock.Mock()
resp.body = {'console': {'a': 'b'}}
resp.json = mock.Mock(return_value=resp.body)
resp.status_code = 200
self.sess.post.return_value = resp
res = sot.get_console_url(self.sess, 'novnc')
self.sess.post.assert_called_with(
'servers/IDENTIFIER/action',
json={'os-getVNCConsole': {'type': 'novnc'}},
headers={'Accept': ''}, microversion=None)
self.assertDictEqual(resp.body['console'], res)
sot.get_console_url(self.sess, 'xvpvnc')
self.sess.post.assert_called_with(
'servers/IDENTIFIER/action',
json={'os-getVNCConsole': {'type': 'xvpvnc'}},
headers={'Accept': ''}, microversion=None)
sot.get_console_url(self.sess, 'spice-html5')
self.sess.post.assert_called_with(
'servers/IDENTIFIER/action',
json={'os-getSPICEConsole': {'type': 'spice-html5'}},
headers={'Accept': ''}, microversion=None)
sot.get_console_url(self.sess, 'rdp-html5')
self.sess.post.assert_called_with(
'servers/IDENTIFIER/action',
json={'os-getRDPConsole': {'type': 'rdp-html5'}},
headers={'Accept': ''}, microversion=None)
sot.get_console_url(self.sess, 'serial')
self.sess.post.assert_called_with(
'servers/IDENTIFIER/action',
json={'os-getSerialConsole': {'type': 'serial'}},
headers={'Accept': ''}, microversion=None)
self.assertRaises(ValueError,
sot.get_console_url,
self.sess,
'fake_type'
)
def test_live_migrate_no_force(self):
sot = server.Server(**EXAMPLE)

View File

@ -0,0 +1,71 @@
# 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.
from unittest import mock
from keystoneauth1 import adapter
from openstack.tests.unit import base
from openstack.compute.v2 import server_remote_console
IDENTIFIER = 'IDENTIFIER'
EXAMPLE = {
'protocol': 'rdp',
'type': 'rdp',
'url': 'fake'
}
class TestServerRemoteConsole(base.TestCase):
def setUp(self):
super(TestServerRemoteConsole, self).setUp()
self.sess = mock.Mock(spec=adapter.Adapter)
self.sess.default_microversion = '2.9'
self.resp = mock.Mock()
self.resp.body = None
self.resp.json = mock.Mock(return_value=self.resp.body)
self.resp.status_code = 200
self.sess = mock.Mock()
self.sess.post = mock.Mock(return_value=self.resp)
self.sess._get_connection = mock.Mock(return_value=self.cloud)
def test_basic(self):
sot = server_remote_console.ServerRemoteConsole()
self.assertEqual('remote_console', sot.resource_key)
self.assertEqual('/servers/%(server_id)s/remote-consoles',
sot.base_path)
self.assertTrue(sot.allow_create)
self.assertFalse(sot.allow_fetch)
self.assertFalse(sot.allow_commit)
self.assertFalse(sot.allow_delete)
self.assertFalse(sot.allow_list)
def test_make_it(self):
sot = server_remote_console.ServerRemoteConsole(**EXAMPLE)
self.assertEqual(EXAMPLE['url'], sot.url)
def test_create_type_mks_old(self):
sot = server_remote_console.ServerRemoteConsole(
server_id='fake_server', type='webmks')
class FakeEndpointData:
min_microversion = '2'
max_microversion = '2.5'
self.sess.get_endpoint_data.return_value = FakeEndpointData()
self.assertRaises(
ValueError,
sot.create,
self.sess
)

View File

@ -0,0 +1,4 @@
---
features:
- |
Optimizes compute server console creation by addind older get_server_console method to the server and create_console proxy method calling appropriate method depending on the suported microversion.