Skip to content

Commit a5363ae

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Support the server side remote-console API changes"
2 parents 5d5def7 + cce7cd3 commit a5363ae

File tree

7 files changed

+236
-5
lines changed

7 files changed

+236
-5
lines changed

novaclient/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@
2525
# when client supported the max version, and bumped sequentially, otherwise
2626
# the client may break due to server side new version may include some
2727
# backward incompatible change.
28-
API_MAX_VERSION = api_versions.APIVersion("2.5")
28+
API_MAX_VERSION = api_versions.APIVersion("2.6")
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Copyright 2015 IBM Corp.
2+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
3+
# not use this file except in compliance with the License. You may obtain
4+
# a copy of the License at
5+
#
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11+
# License for the specific language governing permissions and limitations
12+
# under the License.
13+
14+
from tempest_lib import exceptions
15+
16+
from novaclient.tests.functional import base
17+
from novaclient.v2 import shell
18+
19+
20+
class TestConsolesNovaClient(base.ClientTestBase):
21+
"""Consoles functional tests."""
22+
23+
COMPUTE_API_VERSION = "2.1"
24+
25+
def _create_server(self):
26+
name = self.name_generate(prefix='server')
27+
server = self.client.servers.create(name, self.image, self.flavor)
28+
shell._poll_for_status(
29+
self.client.servers.get, server.id,
30+
'building', ['active'])
31+
self.addCleanup(server.delete)
32+
return server
33+
34+
def _test_console_get(self, command):
35+
server = self._create_server()
36+
completed_command = command % server.id
37+
self.assertRaises(exceptions.CommandFailed,
38+
self.nova, completed_command)
39+
40+
try:
41+
self.nova(completed_command)
42+
except exceptions.CommandFailed as cf:
43+
self.assertTrue('HTTP 400' in str(cf.stderr))
44+
45+
def _test_vnc_console_get(self):
46+
self._test_console_get('get-vnc-console %s novnc')
47+
48+
def _test_spice_console_get(self):
49+
self._test_console_get('get-spice-console %s spice-html5')
50+
51+
def _test_rdp_console_get(self):
52+
self._test_console_get('get-rdp-console %s rdp-html5')
53+
54+
def _test_serial_console_get(self):
55+
self._test_console_get('get-serial-console %s')
56+
57+
def test_vnc_console_get(self):
58+
self._test_vnc_console_get()
59+
60+
def test_spice_console_get(self):
61+
self._test_spice_console_get()
62+
63+
def test_rdp_console_get(self):
64+
self._test_rdp_console_get()
65+
66+
def test_serial_console_get(self):
67+
self._test_serial_console_get()
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Copyright 2015 IBM Corp.
2+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
3+
# not use this file except in compliance with the License. You may obtain
4+
# a copy of the License at
5+
#
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11+
# License for the specific language governing permissions and limitations
12+
# under the License.
13+
14+
from novaclient.tests.functional.v2.legacy import test_consoles
15+
16+
17+
class TestConsolesNovaClientV26(test_consoles.TestConsolesNovaClient):
18+
"""Consoles functional tests for >=v2.6 api microversions."""
19+
20+
COMPUTE_API_VERSION = "2.6"
21+
22+
def test_vnc_console_get(self):
23+
self._test_vnc_console_get()
24+
25+
def test_spice_console_get(self):
26+
self._test_spice_console_get()
27+
28+
def test_rdp_console_get(self):
29+
self._test_rdp_console_get()
30+
31+
def test_serial_console_get(self):
32+
self._test_serial_console_get()

novaclient/tests/unit/fixture_data/servers.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,10 @@ def setUp(self):
224224
json=self.post_servers,
225225
headers=self.json_headers)
226226

227+
self.requests.register_uri('POST', self.url('1234', 'remote-consoles'),
228+
json=self.post_servers_1234_remote_consoles,
229+
headers=self.json_headers)
230+
227231
self.requests.register_uri('POST', self.url('1234', 'action'),
228232
json=self.post_servers_1234_action,
229233
headers=self.json_headers)
@@ -387,6 +391,20 @@ def post_servers(self, request, context):
387391

388392
return {'server': body}
389393

394+
def post_servers_1234_remote_consoles(self, request, context):
395+
_body = ''
396+
body = jsonutils.loads(request.body)
397+
context.status_code = 202
398+
assert len(body.keys()) == 1
399+
assert 'remote_console' in body.keys()
400+
assert 'protocol' in body['remote_console'].keys()
401+
protocol = body['remote_console']['protocol']
402+
403+
_body = {'protocol': protocol, 'type': 'novnc',
404+
'url': 'http://example.com:6080/vnc_auto.html?token=XYZ'}
405+
406+
return {'remote_console': _body}
407+
390408
def post_servers_1234_action(self, request, context):
391409
_body = ''
392410
body = jsonutils.loads(request.body)

novaclient/tests/unit/v2/test_servers.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from oslo_serialization import jsonutils
2121
import six
2222

23+
from novaclient import api_versions
2324
from novaclient import exceptions
2425
from novaclient.tests.unit.fixture_data import client
2526
from novaclient.tests.unit.fixture_data import floatingips
@@ -794,3 +795,41 @@ def test_interface_detach(self):
794795
s = self.cs.servers.get(1234)
795796
s.interface_detach('port-id')
796797
self.assert_called('DELETE', '/servers/1234/os-interface/port-id')
798+
799+
800+
class ServersV26Test(ServersTest):
801+
def setUp(self):
802+
super(ServersV26Test, self).setUp()
803+
self.cs.api_version = api_versions.APIVersion("2.6")
804+
805+
def test_get_vnc_console(self):
806+
s = self.cs.servers.get(1234)
807+
s.get_vnc_console('fake')
808+
self.assert_called('POST', '/servers/1234/remote-consoles')
809+
810+
self.cs.servers.get_vnc_console(s, 'fake')
811+
self.assert_called('POST', '/servers/1234/remote-consoles')
812+
813+
def test_get_spice_console(self):
814+
s = self.cs.servers.get(1234)
815+
s.get_spice_console('fake')
816+
self.assert_called('POST', '/servers/1234/remote-consoles')
817+
818+
self.cs.servers.get_spice_console(s, 'fake')
819+
self.assert_called('POST', '/servers/1234/remote-consoles')
820+
821+
def test_get_serial_console(self):
822+
s = self.cs.servers.get(1234)
823+
s.get_serial_console('fake')
824+
self.assert_called('POST', '/servers/1234/remote-consoles')
825+
826+
self.cs.servers.get_serial_console(s, 'fake')
827+
self.assert_called('POST', '/servers/1234/remote-consoles')
828+
829+
def test_get_rdp_console(self):
830+
s = self.cs.servers.get(1234)
831+
s.get_rdp_console('fake')
832+
self.assert_called('POST', '/servers/1234/remote-consoles')
833+
834+
self.cs.servers.get_rdp_console(s, 'fake')
835+
self.assert_called('POST', '/servers/1234/remote-consoles')

novaclient/v2/servers.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import six
2626
from six.moves.urllib import parse
2727

28+
from novaclient import api_versions
2829
from novaclient import base
2930
from novaclient import crypto
3031
from novaclient.i18n import _
@@ -661,6 +662,7 @@ def remove_floating_ip(self, server, address):
661662
address = address.ip if hasattr(address, 'ip') else address
662663
self._action('removeFloatingIp', server, {'address': address})
663664

665+
@api_versions.wraps('2.0', '2.5')
664666
def get_vnc_console(self, server, console_type):
665667
"""
666668
Get a vnc console for an instance
@@ -672,6 +674,7 @@ def get_vnc_console(self, server, console_type):
672674
return self._action('os-getVNCConsole', server,
673675
{'type': console_type})[1]
674676

677+
@api_versions.wraps('2.0', '2.5')
675678
def get_spice_console(self, server, console_type):
676679
"""
677680
Get a spice console for an instance
@@ -683,6 +686,7 @@ def get_spice_console(self, server, console_type):
683686
return self._action('os-getSPICEConsole', server,
684687
{'type': console_type})[1]
685688

689+
@api_versions.wraps('2.0', '2.5')
686690
def get_rdp_console(self, server, console_type):
687691
"""
688692
Get a rdp console for an instance
@@ -694,6 +698,7 @@ def get_rdp_console(self, server, console_type):
694698
return self._action('os-getRDPConsole', server,
695699
{'type': console_type})[1]
696700

701+
@api_versions.wraps('2.0', '2.5')
697702
def get_serial_console(self, server, console_type):
698703
"""
699704
Get a serial console for an instance
@@ -705,6 +710,54 @@ def get_serial_console(self, server, console_type):
705710
return self._action('os-getSerialConsole', server,
706711
{'type': console_type})[1]
707712

713+
@api_versions.wraps('2.6')
714+
def get_vnc_console(self, server, console_type):
715+
"""
716+
Get a vnc console for an instance
717+
718+
:param server: The :class:`Server` (or its ID) to add an IP to.
719+
:param console_type: Type of vnc console to get ('novnc' or 'xvpvnc')
720+
"""
721+
722+
return self._console(server,
723+
{'protocol': 'vnc', 'type': console_type})[1]
724+
725+
@api_versions.wraps('2.6')
726+
def get_spice_console(self, server, console_type):
727+
"""
728+
Get a spice console for an instance
729+
730+
:param server: The :class:`Server` (or its ID) to add an IP to.
731+
:param console_type: Type of spice console to get ('spice-html5')
732+
"""
733+
734+
return self._console(server,
735+
{'protocol': 'spice', 'type': console_type})[1]
736+
737+
@api_versions.wraps('2.6')
738+
def get_rdp_console(self, server, console_type):
739+
"""
740+
Get a rdp console for an instance
741+
742+
:param server: The :class:`Server` (or its ID) to add an IP to.
743+
:param console_type: Type of rdp console to get ('rdp-html5')
744+
"""
745+
746+
return self._console(server,
747+
{'protocol': 'rdp', 'type': console_type})[1]
748+
749+
@api_versions.wraps('2.6')
750+
def get_serial_console(self, server, console_type):
751+
"""
752+
Get a serial console for an instance
753+
754+
:param server: The :class:`Server` (or its ID) to add an IP to.
755+
:param console_type: Type of serial console to get ('serial')
756+
"""
757+
758+
return self._console(server,
759+
{'protocol': 'serial', 'type': console_type})[1]
760+
708761
def get_password(self, server, private_key=None):
709762
"""
710763
Get admin password of an instance
@@ -1277,3 +1330,11 @@ def _action(self, action, server, info=None, **kwargs):
12771330
self.run_hooks('modify_body_for_action', body, **kwargs)
12781331
url = '/servers/%s/action' % base.getid(server)
12791332
return self.api.client.post(url, body=body)
1333+
1334+
def _console(self, server, info=None, **kwargs):
1335+
"""
1336+
Retrieve a console of a particular protocol -- vnc/spice/rdp/serial
1337+
"""
1338+
body = {'remote_console': info}
1339+
url = '/servers/%s/remote-consoles' % base.getid(server)
1340+
return self.api.client.post(url, body=body)

novaclient/v2/shell.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2303,6 +2303,16 @@ def do_volume_type_delete(cs, args):
23032303
cs.volume_types.delete(args.id)
23042304

23052305

2306+
@api_versions.wraps('2.0', '2.5')
2307+
def console_dict_accessor(cs, data):
2308+
return data['console']
2309+
2310+
2311+
@api_versions.wraps('2.6')
2312+
def console_dict_accessor(cs, data):
2313+
return data['remote_console']
2314+
2315+
23062316
@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
23072317
@cliutils.arg(
23082318
'console_type',
@@ -2318,7 +2328,8 @@ def __init__(self, console_dict):
23182328
self.type = console_dict['type']
23192329
self.url = console_dict['url']
23202330

2321-
utils.print_list([VNCConsole(data['console'])], ['Type', 'Url'])
2331+
utils.print_list([VNCConsole(console_dict_accessor(cs, data))],
2332+
['Type', 'Url'])
23222333

23232334

23242335
@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@@ -2336,7 +2347,8 @@ def __init__(self, console_dict):
23362347
self.type = console_dict['type']
23372348
self.url = console_dict['url']
23382349

2339-
utils.print_list([SPICEConsole(data['console'])], ['Type', 'Url'])
2350+
utils.print_list([SPICEConsole(console_dict_accessor(cs, data))],
2351+
['Type', 'Url'])
23402352

23412353

23422354
@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@@ -2354,7 +2366,8 @@ def __init__(self, console_dict):
23542366
self.type = console_dict['type']
23552367
self.url = console_dict['url']
23562368

2357-
utils.print_list([RDPConsole(data['console'])], ['Type', 'Url'])
2369+
utils.print_list([RDPConsole(console_dict_accessor(cs, data))],
2370+
['Type', 'Url'])
23582371

23592372

23602373
@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
@@ -2376,7 +2389,8 @@ def __init__(self, console_dict):
23762389
self.type = console_dict['type']
23772390
self.url = console_dict['url']
23782391

2379-
utils.print_list([SerialConsole(data['console'])], ['Type', 'Url'])
2392+
utils.print_list([SerialConsole(console_dict_accessor(cs, data))],
2393+
['Type', 'Url'])
23802394

23812395

23822396
@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))

0 commit comments

Comments
 (0)