diff --git a/doc/source/topics/settings.rst b/doc/source/topics/settings.rst index 222680474b..5ff603d798 100644 --- a/doc/source/topics/settings.rst +++ b/doc/source/topics/settings.rst @@ -667,6 +667,17 @@ This setting notifies the Data Processing (Sahara) system whether or not automatic IP allocation is enabled. You would want to set this to True if you were running Nova Networking with auto_assign_floating_ip = True. +``CONSOLE_TYPE`` +------------------------------------- + +Default: ``"AUTO"`` + +This settings specifies the type of in-browser VNC console used to access the +VMs. +Valid values are ``"AUTO"``(default), ``"VNC"``, ``"SPICE"``, ``"RDP"`` and +``None``(this latest value is available in version 2014.2(Juno) to allow +deactivating the in-browser console). + Django Settings (Partial) ========================= @@ -971,3 +982,4 @@ following content:: PANEL_GROUP = 'plugin_panel_group' PANEL_GROUP_NAME = 'Plugin Panel Group' PANEL_GROUP_DASHBOARD = 'admin' + diff --git a/openstack_dashboard/dashboards/project/instances/tables.py b/openstack_dashboard/dashboards/project/instances/tables.py index 3e2a3071ed..a89a816b98 100644 --- a/openstack_dashboard/dashboards/project/instances/tables.py +++ b/openstack_dashboard/dashboards/project/instances/tables.py @@ -333,7 +333,10 @@ class ConsoleLink(tables.LinkAction): return {"project_id": project_id} def allowed(self, request, instance=None): - return instance.status in ACTIVE_STATES and not is_deleting(instance) + # We check if ConsoleLink is allowed only if settings.CONSOLE_TYPE is + # not set at all, or if it's set to any value other than None or False. + return bool(getattr(settings, 'CONSOLE_TYPE', True)) and \ + instance.status in ACTIVE_STATES and not is_deleting(instance) def get_link_url(self, datum): base_url = super(ConsoleLink, self).get_link_url(datum) diff --git a/openstack_dashboard/dashboards/project/instances/tabs.py b/openstack_dashboard/dashboards/project/instances/tabs.py index 8e0a526e66..c8a773d609 100644 --- a/openstack_dashboard/dashboards/project/instances/tabs.py +++ b/openstack_dashboard/dashboards/project/instances/tabs.py @@ -71,6 +71,11 @@ class ConsoleTab(tabs.Tab): return {'console_url': console_url, 'instance_id': instance.id} + def allowed(self, request): + # The ConsoleTab is available if settings.CONSOLE_TYPE is not set at + # all, or if it's set to any value other than None or False. + return bool(getattr(settings, 'CONSOLE_TYPE', True)) + class AuditTab(tabs.TableTab): name = _("Action Log") diff --git a/openstack_dashboard/dashboards/project/instances/tests.py b/openstack_dashboard/dashboards/project/instances/tests.py index 7df4a9891f..8584737225 100644 --- a/openstack_dashboard/dashboards/project/instances/tests.py +++ b/openstack_dashboard/dashboards/project/instances/tests.py @@ -61,7 +61,7 @@ class InstanceTests(helpers.TestCase): 'servers_update_addresses', ), }) - def test_index(self): + def _get_index(self): servers = self.servers.list() api.nova.extension_supported('AdminActions', IsA(http.HttpRequest)) \ @@ -83,7 +83,11 @@ class InstanceTests(helpers.TestCase): self.mox.ReplayAll() - res = self.client.get(INDEX_URL) + return self.client.get(INDEX_URL) + + def test_index(self): + + res = self._get_index() self.assertTemplateUsed(res, 'project/instances/index.html') @@ -239,11 +243,36 @@ class InstanceTests(helpers.TestCase): self.assertEqual(len(instances), len(servers)) self.assertContains(res, "(not found)") + def test_index_with_console_link(self): + res = self._get_index() + + instances_table = res.context['instances_table'] + instances = res.context['instances_table'].data + console_link_rendered = False + for instance in instances: + for action in instances_table.get_row_actions(instance): + if isinstance(action, tables.ConsoleLink): + console_link_rendered = True + break + if console_link_rendered: + break + self.assertTrue(console_link_rendered) + + @django.test.utils.override_settings(CONSOLE_TYPE=None) + def test_index_without_console_link(self): + res = self._get_index() + + instances_table = res.context['instances_table'] + instances = res.context['instances_table'].data + for instance in instances: + for action in instances_table.get_row_actions(instance): + self.assertNotIsInstance(action, tables.ConsoleLink) + @helpers.create_stubs({api.nova: ('server_list', - 'flavor_list', - 'server_delete',), - api.glance: ('image_list_detailed',), - api.network: ('servers_update_addresses',)}) + 'flavor_list', + 'server_delete',), + api.glance: ('image_list_detailed',), + api.network: ('servers_update_addresses',)}) def test_terminate_instance(self): servers = self.servers.list() server = servers[0] @@ -623,52 +652,56 @@ class InstanceTests(helpers.TestCase): "flavor_get"), api.network: ("server_security_groups", "servers_update_addresses")}) + def _get_instance_details(self, server, qs=None, + flavor_return=None, volumes_return=None, + security_groups_return=None, ): + + url = reverse('horizon:project:instances:detail', args=[server.id]) + if qs: + url += qs + + if flavor_return is None: + flavor_return = self.flavors.first() + + if volumes_return is None: + volumes_return = [] + + if security_groups_return is None: + security_groups_return = self.security_groups.list() + + api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server) + api.network.servers_update_addresses(IsA(http.HttpRequest), + IgnoreArg()) + api.nova.instance_volumes_list(IsA(http.HttpRequest), + server.id).AndReturn(volumes_return) + api.nova.flavor_get(IsA(http.HttpRequest), server.flavor['id']) \ + .AndReturn(flavor_return) + api.network.server_security_groups(IsA(http.HttpRequest), server.id) \ + .AndReturn(security_groups_return) + + self.mox.ReplayAll() + + return self.client.get(url) + def test_instance_details_volumes(self): server = self.servers.first() volumes = [self.volumes.list()[1]] + security_group = self.security_groups.first() - api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server) - api.network.servers_update_addresses(IsA(http.HttpRequest), - IgnoreArg()) - api.nova.instance_volumes_list(IsA(http.HttpRequest), - server.id).AndReturn(volumes) - api.nova.flavor_get(IsA(http.HttpRequest), server.flavor['id']) \ - .AndReturn(self.flavors.first()) - api.network.server_security_groups(IsA(http.HttpRequest), server.id) \ - .AndReturn(self.security_groups.list()) - - self.mox.ReplayAll() - - url = reverse('horizon:project:instances:detail', - args=[server.id]) - res = self.client.get(url) + res = self._get_instance_details(server, volumes_return=volumes, + security_groups_return=security_group) + + self.assertItemsEqual(res.context['instance'].volumes, volumes) self.assertItemsEqual(res.context['instance'].volumes, volumes) - @helpers.create_stubs({api.nova: ("server_get", - "instance_volumes_list", - "flavor_get"), - api.network: ("server_security_groups", - "servers_update_addresses")}) def test_instance_details_volume_sorting(self): server = self.servers.first() volumes = self.volumes.list()[1:3] + security_group = self.security_groups.first() - api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server) - api.network.servers_update_addresses(IsA(http.HttpRequest), - IgnoreArg()) - api.nova.instance_volumes_list(IsA(http.HttpRequest), - server.id).AndReturn(volumes) - api.nova.flavor_get(IsA(http.HttpRequest), server.flavor['id']) \ - .AndReturn(self.flavors.first()) - api.network.server_security_groups(IsA(http.HttpRequest), server.id) \ - .AndReturn(self.security_groups.list()) - - self.mox.ReplayAll() - - url = reverse('horizon:project:instances:detail', - args=[server.id]) - res = self.client.get(url) + res = self._get_instance_details(server, volumes_return=volumes, + security_groups_return=security_group) self.assertItemsEqual(res.context['instance'].volumes, volumes) self.assertEqual(res.context['instance'].volumes[0].device, @@ -676,31 +709,12 @@ class InstanceTests(helpers.TestCase): self.assertEqual(res.context['instance'].volumes[1].device, "/dev/hdk") - @helpers.create_stubs({api.nova: ("server_get", - "instance_volumes_list", - "flavor_get"), - api.network: ("server_security_groups", - "servers_update_addresses")}) def test_instance_details_metadata(self): server = self.servers.first() - api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server) - api.network.servers_update_addresses(IsA(http.HttpRequest), - IgnoreArg()) - api.nova.instance_volumes_list(IsA(http.HttpRequest), - server.id).AndReturn([]) - api.nova.flavor_get(IsA(http.HttpRequest), server.flavor['id']) \ - .AndReturn(self.flavors.first()) - api.network.server_security_groups(IsA(http.HttpRequest), server.id) \ - .AndReturn(self.security_groups.list()) - - self.mox.ReplayAll() - - url = reverse('horizon:project:instances:detail', - args=[server.id]) tg = tabs.InstanceDetailTabs(self.request, instance=server) qs = "?%s=%s" % (tg.param_name, tg.get_tab("overview").get_id()) - res = self.client.get(url + qs) + res = self._get_instance_details(server, qs) self.assertContains(res, "
keyName
", 1) self.assertContains(res, "
someMetaLabel
", 1) @@ -712,11 +726,6 @@ class InstanceTests(helpers.TestCase): # TODO(david-lyle): uncomment when fixed with Django 1.6 # self.assertContains(res, "
N/A
", 1) - @helpers.create_stubs({api.nova: ("server_get", - "instance_volumes_list", - "flavor_get"), - api.network: ("server_security_groups", - "servers_update_addresses")}) def test_instance_details_fault(self): server = self.servers.first() @@ -731,23 +740,37 @@ class InstanceTests(helpers.TestCase): "(reason=\"\")\n", "created": "2013-10-07T00:08:32Z"} - api.nova.server_get(IsA(http.HttpRequest), server.id).AndReturn(server) - api.network.servers_update_addresses(IsA(http.HttpRequest), - IgnoreArg()) - api.nova.instance_volumes_list(IsA(http.HttpRequest), - server.id).AndReturn([]) - api.nova.flavor_get(IsA(http.HttpRequest), server.flavor['id']) \ - .AndReturn(self.flavors.first()) - api.network.server_security_groups(IsA(http.HttpRequest), server.id) \ - .AndReturn(self.security_groups.list()) - - self.mox.ReplayAll() - - url = reverse('horizon:project:instances:detail', - args=[server.id]) - res = self.client.get(url) + res = self._get_instance_details(server) self.assertItemsEqual(res.context['instance'].fault, server.fault) + def test_instance_details_console_tab(self): + server = self.servers.first() + + tg = tabs.InstanceDetailTabs(self.request, instance=server) + qs = "?%s=%s" % (tg.param_name, tg.get_tab("console").get_id()) + res = self._get_instance_details(server, qs) + self.assertIn(tabs.ConsoleTab, res.context_data['tab_group'].tabs) + self.assertTemplateUsed(res, + 'project/instances/_detail_console.html') + console_tab_rendered = False + for tab in res.context_data['tab_group'].get_loaded_tabs(): + if isinstance(tab, tabs.ConsoleTab): + console_tab_rendered = True + break + self.assertTrue(console_tab_rendered) + + @django.test.utils.override_settings(CONSOLE_TYPE=None) + def test_instance_details_console_tab_deactivated(self): + server = self.servers.first() + + tg = tabs.InstanceDetailTabs(self.request, instance=server) + self.assertIsNone(tg.get_tab("console")) + res = self._get_instance_details(server) + self.assertTemplateNotUsed(res, + 'project/instances/_detail_console.html') + for tab in res.context_data['tab_group'].get_loaded_tabs(): + self.assertNotIsInstance(tab, tabs.ConsoleTab) + @helpers.create_stubs({api.nova: ('server_get',)}) def test_instance_details_exception(self): server = self.servers.first() diff --git a/openstack_dashboard/dashboards/project/network_topology/templates/network_topology/client_side/_balloon_container.html b/openstack_dashboard/dashboards/project/network_topology/templates/network_topology/client_side/_balloon_container.html index ac84dcea1e..bbf163c093 100644 --- a/openstack_dashboard/dashboards/project/network_topology/templates/network_topology/client_side/_balloon_container.html +++ b/openstack_dashboard/dashboards/project/network_topology/templates/network_topology/client_side/_balloon_container.html @@ -15,9 +15,12 @@
[[add_interface_label]]
diff --git a/openstack_dashboard/dashboards/project/network_topology/views.py b/openstack_dashboard/dashboards/project/network_topology/views.py index 07d05d9ed4..431dc3898f 100644 --- a/openstack_dashboard/dashboards/project/network_topology/views.py +++ b/openstack_dashboard/dashboards/project/network_topology/views.py @@ -112,6 +112,8 @@ class NetworkTopologyView(TemplateView): context['create_router_allowed'] = ( network_config.get('enable_router', True) and self._has_permission((("network", "create_router"),))) + context['console_type'] = getattr( + settings, 'CONSOLE_TYPE', 'AUTO') return context diff --git a/openstack_dashboard/local/local_settings.py.example b/openstack_dashboard/local/local_settings.py.example index f748dc84ea..7cd1a24718 100644 --- a/openstack_dashboard/local/local_settings.py.example +++ b/openstack_dashboard/local/local_settings.py.example @@ -46,7 +46,8 @@ TEMPLATE_DEBUG = DEBUG # OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = 'Default' # Set Console type: -# valid options would be "AUTO", "VNC", "SPICE" or "RDP" +# valid options would be "AUTO"(default), "VNC", "SPICE", "RDP" or None +# Set to None explicitly if you want to deactivate the console. # CONSOLE_TYPE = "AUTO" # Default OpenStack Dashboard configuration.