diff --git a/openstack_dashboard/api/nova.py b/openstack_dashboard/api/nova.py index a74135e67d..6c8afc293b 100644 --- a/openstack_dashboard/api/nova.py +++ b/openstack_dashboard/api/nova.py @@ -65,6 +65,13 @@ class SPICEConsole(base.APIDictWrapper): _attrs = ['url', 'type'] +class RDPConsole(base.APIDictWrapper): + """Wrapper for the "console" dictionary returned by the + novaclient.servers.get_rdp_console method. + """ + _attrs = ['url', 'type'] + + class Server(base.APIResourceWrapper): """Simple wrapper around novaclient.server.Server @@ -389,6 +396,11 @@ def server_spice_console(request, instance_id, console_type='spice-html5'): instance_id, console_type)['console']) +def server_rdp_console(request, instance_id, console_type='rdp-html5'): + return RDPConsole(novaclient(request).servers.get_rdp_console( + instance_id, console_type)['console']) + + def flavor_create(request, name, memory, vcpu, disk, flavorid='auto', ephemeral=0, swap=0, metadata=None, is_public=True): flavor = novaclient(request).flavors.create(name, memory, vcpu, disk, diff --git a/openstack_dashboard/dashboards/admin/instances/urls.py b/openstack_dashboard/dashboards/admin/instances/urls.py index a81901ee48..9ab2268f8f 100644 --- a/openstack_dashboard/dashboards/admin/instances/urls.py +++ b/openstack_dashboard/dashboards/admin/instances/urls.py @@ -34,6 +34,7 @@ urlpatterns = patterns('openstack_dashboard.dashboards.admin.instances.views', url(INSTANCES % 'console', 'console', name='console'), url(INSTANCES % 'vnc', 'vnc', name='vnc'), url(INSTANCES % 'spice', 'spice', name='spice'), + url(INSTANCES % 'rdp', 'rdp', name='rdp'), url(INSTANCES % 'live_migrate', views.LiveMigrateView.as_view(), name='live_migrate'), ) diff --git a/openstack_dashboard/dashboards/admin/instances/views.py b/openstack_dashboard/dashboards/admin/instances/views.py index 5ef276fded..54f41ce1b4 100644 --- a/openstack_dashboard/dashboards/admin/instances/views.py +++ b/openstack_dashboard/dashboards/admin/instances/views.py @@ -54,6 +54,11 @@ def spice(args, **kvargs): return views.spice(args, **kvargs) +# re-use rdp from project.instances.views to make reflection work +def rdp(args, **kvargs): + return views.rdp(args, **kvargs) + + class AdminUpdateView(views.UpdateView): workflow_class = update_instance.AdminUpdateInstance diff --git a/openstack_dashboard/dashboards/project/instances/tabs.py b/openstack_dashboard/dashboards/project/instances/tabs.py index 4caa7d4be3..f6d8d4686f 100644 --- a/openstack_dashboard/dashboards/project/instances/tabs.py +++ b/openstack_dashboard/dashboards/project/instances/tabs.py @@ -79,7 +79,15 @@ class ConsoleTab(tabs.Tab): getattr(instance, "name", ""), instance.id) except Exception: - console_url = None + try: + console = api.nova.server_rdp_console(request, + instance.id) + console_url = "%s&title=%s(%s)" % ( + console.url, + getattr(instance, "name", ""), + instance.id) + except Exception: + console_url = None elif console_type == 'VNC': try: console = api.nova.server_vnc_console(request, instance.id) @@ -98,6 +106,15 @@ class ConsoleTab(tabs.Tab): instance.id) except Exception: console_url = None + elif console_type == 'RDP': + try: + console = api.nova.server_rdp_console(request, instance.id) + console_url = "%s&title=%s(%s)" % ( + console.url, + getattr(instance, "name", ""), + instance.id) + except Exception: + console_url = None else: console_url = None diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py index c137d4a121..209e30c0c6 100644 --- a/openstack_dashboard/dashboards/project/instances/tests.py +++ b/openstack_dashboard/dashboards/project/instances/tests.py @@ -865,6 +865,42 @@ class InstanceTests(test.TestCase): self.assertRedirectsNoFollow(res, INDEX_URL) + def test_instance_rdp(self): + server = self.servers.first() + CONSOLE_OUTPUT = '/rdpserver' + + console_mock = self.mox.CreateMock(api.nova.RDPConsole) + console_mock.url = CONSOLE_OUTPUT + + self.mox.StubOutWithMock(api.nova, 'server_rdp_console') + self.mox.StubOutWithMock(api.nova, 'server_get') + api.nova.server_get(IsA(http.HttpRequest), server.id) \ + .AndReturn(server) + api.nova.server_rdp_console(IgnoreArg(), server.id) \ + .AndReturn(console_mock) + self.mox.ReplayAll() + + url = reverse('horizon:project:instances:rdp', + args=[server.id]) + res = self.client.get(url) + redirect = CONSOLE_OUTPUT + '&title=%s(1)' % server.name + self.assertRedirectsNoFollow(res, redirect) + + @test.create_stubs({api.nova: ('server_rdp_console',)}) + def test_instance_rdp_exception(self): + server = self.servers.first() + + api.nova.server_rdp_console(IsA(http.HttpRequest), server.id) \ + .AndRaise(self.exceptions.nova) + + self.mox.ReplayAll() + + url = reverse('horizon:project:instances:rdp', + args=[server.id]) + res = self.client.get(url) + + self.assertRedirectsNoFollow(res, INDEX_URL) + @test.create_stubs({api.nova: ('server_get', 'snapshot_create', 'server_list', diff --git a/openstack_dashboard/dashboards/project/instances/urls.py b/openstack_dashboard/dashboards/project/instances/urls.py index 5cbf1b6fee..28ea320ea2 100644 --- a/openstack_dashboard/dashboards/project/instances/urls.py +++ b/openstack_dashboard/dashboards/project/instances/urls.py @@ -38,5 +38,6 @@ urlpatterns = patterns(VIEW_MOD, url(INSTANCES % 'console', 'console', name='console'), url(INSTANCES % 'vnc', 'vnc', name='vnc'), url(INSTANCES % 'spice', 'spice', name='spice'), + url(INSTANCES % 'rdp', 'rdp', name='rdp'), url(INSTANCES % 'resize', views.ResizeView.as_view(), name='resize'), ) diff --git a/openstack_dashboard/dashboards/project/instances/views.py b/openstack_dashboard/dashboards/project/instances/views.py index 569d01b7d5..c999da94df 100644 --- a/openstack_dashboard/dashboards/project/instances/views.py +++ b/openstack_dashboard/dashboards/project/instances/views.py @@ -173,6 +173,18 @@ def spice(request, instance_id): exceptions.handle(request, msg, redirect=redirect) +def rdp(request, instance_id): + try: + console = api.nova.server_rdp_console(request, instance_id) + instance = api.nova.server_get(request, instance_id) + return shortcuts.redirect(console.url + + ("&title=%s(%s)" % (instance.name, instance_id))) + except Exception: + redirect = reverse("horizon:project:instances:index") + msg = _('Unable to get RDP console for instance "%s".') % instance_id + exceptions.handle(request, msg, redirect=redirect) + + class UpdateView(workflows.WorkflowView): workflow_class = project_workflows.UpdateInstance success_url = reverse_lazy("horizon:project:instances:index") diff --git a/openstack_dashboard/local/local_settings.py.example b/openstack_dashboard/local/local_settings.py.example index 50af3e811b..ea14588511 100644 --- a/openstack_dashboard/local/local_settings.py.example +++ b/openstack_dashboard/local/local_settings.py.example @@ -45,7 +45,7 @@ TEMPLATE_DEBUG = DEBUG # OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = 'Default' # Set Console type: -# valid options would be "AUTO", "VNC" or "SPICE" +# valid options would be "AUTO", "VNC", "SPICE" or "RDP" # CONSOLE_TYPE = "AUTO" # Default OpenStack Dashboard configuration. diff --git a/openstack_dashboard/test/api_tests/nova_tests.py b/openstack_dashboard/test/api_tests/nova_tests.py index 203f263716..ba44a87a48 100644 --- a/openstack_dashboard/test/api_tests/nova_tests.py +++ b/openstack_dashboard/test/api_tests/nova_tests.py @@ -107,6 +107,22 @@ class ComputeApiTests(test.APITestCase): console_type) self.assertIsInstance(ret_val, api.nova.SPICEConsole) + def test_server_rdp_console(self): + server = self.servers.first() + console = self.servers.rdp_console_data + console_type = console["console"]["type"] + + novaclient = self.stub_novaclient() + novaclient.servers = self.mox.CreateMockAnything() + novaclient.servers.get_rdp_console(server.id, + console_type).AndReturn(console) + self.mox.ReplayAll() + + ret_val = api.nova.server_rdp_console(self.request, + server.id, + console_type) + self.assertIsInstance(ret_val, api.nova.RDPConsole) + def test_server_list(self): servers = self.servers.list() diff --git a/openstack_dashboard/test/test_data/nova_data.py b/openstack_dashboard/test/test_data/nova_data.py index e15b28876d..9a2fd7ee69 100644 --- a/openstack_dashboard/test/test_data/nova_data.py +++ b/openstack_dashboard/test/test_data/nova_data.py @@ -442,6 +442,10 @@ def data(TEST): console = {u'console': {u'url': u'http://example.com:6080/spice_auto.html', u'type': u'spice'}} TEST.servers.spice_console_data = console + # RDP Console Data + console = {u'console': {u'url': u'http://example.com:6080/rdp_auto.html', + u'type': u'rdp'}} + TEST.servers.rdp_console_data = console # Floating IPs def generate_fip(conf):