From f678336cd73934b930947d0219a4e51945d7afb2 Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Thu, 4 Jul 2013 12:28:34 +0200 Subject: [PATCH] adding hypervisors panel to admin dashboard This commit adds a new panel "Hypervisors" to the system panel in the admin dashboard listing all registered hypervisors hosts by using the os-hypervisors extension of the Nova API. fixes bug #1197763 Change-Id: I3f9a7858342c3592521b7fa8ecf17e2fb5af6cda --- openstack_dashboard/api/nova.py | 4 + .../dashboards/admin/dashboard.py | 2 +- .../dashboards/admin/hypervisors/__init__.py | 0 .../dashboards/admin/hypervisors/panel.py | 29 +++++++ .../dashboards/admin/hypervisors/tables.py | 75 +++++++++++++++++++ .../templates/hypervisors/index.html | 11 +++ .../dashboards/admin/hypervisors/tests.py | 34 +++++++++ .../dashboards/admin/hypervisors/urls.py | 26 +++++++ .../dashboards/admin/hypervisors/views.py | 42 +++++++++++ .../test/test_data/nova_data.py | 29 +++++++ 10 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 openstack_dashboard/dashboards/admin/hypervisors/__init__.py create mode 100644 openstack_dashboard/dashboards/admin/hypervisors/panel.py create mode 100644 openstack_dashboard/dashboards/admin/hypervisors/tables.py create mode 100644 openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/index.html create mode 100644 openstack_dashboard/dashboards/admin/hypervisors/tests.py create mode 100644 openstack_dashboard/dashboards/admin/hypervisors/urls.py create mode 100644 openstack_dashboard/dashboards/admin/hypervisors/views.py diff --git a/openstack_dashboard/api/nova.py b/openstack_dashboard/api/nova.py index e73b280eba..c9807cbdca 100644 --- a/openstack_dashboard/api/nova.py +++ b/openstack_dashboard/api/nova.py @@ -575,6 +575,10 @@ def instance_volumes_list(request, instance_id): return volumes +def hypervisor_list(request): + return novaclient(request).hypervisors.list() + + def tenant_absolute_limits(request, reserved=False): limits = novaclient(request).limits.get(reserved=reserved).absolute limits_dict = {} diff --git a/openstack_dashboard/dashboards/admin/dashboard.py b/openstack_dashboard/dashboards/admin/dashboard.py index d63da76645..49601f75aa 100644 --- a/openstack_dashboard/dashboards/admin/dashboard.py +++ b/openstack_dashboard/dashboards/admin/dashboard.py @@ -22,7 +22,7 @@ import horizon class SystemPanels(horizon.PanelGroup): slug = "admin" name = _("System Panel") - panels = ('overview', 'instances', 'volumes', 'flavors', + panels = ('overview', 'hypervisors', 'instances', 'volumes', 'flavors', 'images', 'domains', 'projects', 'users', 'groups', 'roles', 'networks', 'routers', 'info') diff --git a/openstack_dashboard/dashboards/admin/hypervisors/__init__.py b/openstack_dashboard/dashboards/admin/hypervisors/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openstack_dashboard/dashboards/admin/hypervisors/panel.py b/openstack_dashboard/dashboards/admin/hypervisors/panel.py new file mode 100644 index 0000000000..156702ef6f --- /dev/null +++ b/openstack_dashboard/dashboards/admin/hypervisors/panel.py @@ -0,0 +1,29 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 B1 Systems GmbH +# +# 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 django.utils.translation import ugettext_lazy as _ + +import horizon +from openstack_dashboard.dashboards.admin import dashboard + + +class Hypervisors(horizon.Panel): + name = _("Hypervisors") + slug = 'hypervisors' + permissions = ('openstack.roles.admin',) + + +dashboard.Admin.register(Hypervisors) diff --git a/openstack_dashboard/dashboards/admin/hypervisors/tables.py b/openstack_dashboard/dashboards/admin/hypervisors/tables.py new file mode 100644 index 0000000000..8562434b6f --- /dev/null +++ b/openstack_dashboard/dashboards/admin/hypervisors/tables.py @@ -0,0 +1,75 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 B1 Systems GmbH +# +# 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 logging + +from django.utils.translation import ugettext_lazy as _ +from horizon import tables + +LOG = logging.getLogger(__name__) + + +def get_memory(hypervisor): + return _("%s MB") % hypervisor.memory_mb + + +def get_memory_used(hypervisor): + return _("%s MB") % hypervisor.memory_mb_used + + +def get_local(hypervisor): + return _("%s GB") % hypervisor.local_gb + + +def get_local_used(hypervisor): + return _("%s GB") % hypervisor.local_gb_used + + +class AdminHypervisorsTable(tables.DataTable): + hypervisor_hostname = tables.Column("hypervisor_hostname", + verbose_name=_("Hostname")) + + hypervisor_type = tables.Column("hypervisor_type", + verbose_name=_("Type")) + + vcpus = tables.Column("vcpus", + verbose_name=_("VCPUs (total)")) + + vcpus_used = tables.Column("vcpus_used", + verbose_name=_("VCPUs (used)")) + + memory = tables.Column(get_memory, + verbose_name=_("RAM (total)"), + attrs={'data-type': 'size'}) + + memory_used = tables.Column(get_memory_used, + verbose_name=_("RAM (used)"), + attrs={'data-type': 'size'}) + + local = tables.Column(get_local, + verbose_name=_("Storage (total)"), + attrs={'data-type': 'size'}) + + local_used = tables.Column(get_local_used, + verbose_name=_("Storage (used)"), + attrs={'data-type': 'size'}) + + running_vms = tables.Column("running_vms", + verbose_name=_("Instances")) + + class Meta: + name = "hypervisors" + verbose_name = _("Hypervisors") diff --git a/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/index.html b/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/index.html new file mode 100644 index 0000000000..c5bda9c066 --- /dev/null +++ b/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/index.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Hypervisors" %}{% endblock %} + +{% block page_header %} +{% include "horizon/common/_page_header.html" with title=_("All Hypervisors") %} +{% endblock page_header %} + +{% block main %} +{{ table.render }} +{% endblock %} diff --git a/openstack_dashboard/dashboards/admin/hypervisors/tests.py b/openstack_dashboard/dashboards/admin/hypervisors/tests.py new file mode 100644 index 0000000000..dfa8144370 --- /dev/null +++ b/openstack_dashboard/dashboards/admin/hypervisors/tests.py @@ -0,0 +1,34 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 B1 Systems GmbH +# +# 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 django.core.urlresolvers import reverse +from django import http +from mox import IsA + +from openstack_dashboard import api +from openstack_dashboard.test import helpers as test + + +class HypervisorViewTest(test.BaseAdminViewTests): + @test.create_stubs({api.nova: ('hypervisor_list',)}) + def test_index(self): + hypervisors = self.hypervisors.list() + api.nova.hypervisor_list(IsA(http.HttpRequest)).AndReturn(hypervisors) + self.mox.ReplayAll() + + res = self.client.get(reverse('horizon:admin:hypervisors:index')) + self.assertTemplateUsed(res, 'admin/hypervisors/index.html') + self.assertItemsEqual(res.context['table'].data, hypervisors) diff --git a/openstack_dashboard/dashboards/admin/hypervisors/urls.py b/openstack_dashboard/dashboards/admin/hypervisors/urls.py new file mode 100644 index 0000000000..c3aedb0614 --- /dev/null +++ b/openstack_dashboard/dashboards/admin/hypervisors/urls.py @@ -0,0 +1,26 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 B1 Systems GmbH +# +# 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 django.conf.urls.defaults import patterns +from django.conf.urls.defaults import url + +from .views import AdminIndexView + + +urlpatterns = patterns( + 'openstack_dashboard.dashboards.admin.hypervisors.views', + url(r'^$', AdminIndexView.as_view(), name='index') +) diff --git a/openstack_dashboard/dashboards/admin/hypervisors/views.py b/openstack_dashboard/dashboards/admin/hypervisors/views.py new file mode 100644 index 0000000000..ad3cc6f738 --- /dev/null +++ b/openstack_dashboard/dashboards/admin/hypervisors/views.py @@ -0,0 +1,42 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2013 B1 Systems GmbH +# +# 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 logging + +from django.utils.translation import ugettext_lazy as _ + +from horizon import exceptions +from horizon import tables +from openstack_dashboard import api +from openstack_dashboard.dashboards.admin.hypervisors.tables import \ + AdminHypervisorsTable + +LOG = logging.getLogger(__name__) + + +class AdminIndexView(tables.DataTableView): + table_class = AdminHypervisorsTable + template_name = 'admin/hypervisors/index.html' + + def get_data(self): + hypervisors = [] + try: + hypervisors = api.nova.hypervisor_list(self.request) + except: + exceptions.handle(self.request, + _('Unable to retrieve hypervisor list.')) + + return hypervisors diff --git a/openstack_dashboard/test/test_data/nova_data.py b/openstack_dashboard/test/test_data/nova_data.py index 83c9f023fb..10f8c5946d 100644 --- a/openstack_dashboard/test/test_data/nova_data.py +++ b/openstack_dashboard/test/test_data/nova_data.py @@ -19,6 +19,7 @@ from novaclient.v1_1 import availability_zones from novaclient.v1_1 import certs from novaclient.v1_1 import flavors from novaclient.v1_1 import floating_ips +from novaclient.v1_1 import hypervisors from novaclient.v1_1 import keypairs from novaclient.v1_1 import quotas from novaclient.v1_1 import security_group_rules as rules @@ -162,6 +163,7 @@ def data(TEST): TEST.volume_snapshots = TestDataContainer() TEST.volume_types = TestDataContainer() TEST.availability_zones = TestDataContainer() + TEST.hypervisors = TestDataContainer() # Data return by novaclient. # It is used if API layer does data conversion. @@ -483,3 +485,30 @@ def data(TEST): {'zoneName': 'nova', 'zoneState': {'available': True}} ) ) + + # hypervisors + hypervisor_1 = hypervisors.Hypervisor(hypervisors.HypervisorManager(None), + { + "service": {"host": "devstack001", "id": 3}, + "vcpus_used": 1, + "hypervisor_type": "QEMU", + "local_gb_used": 20, + "hypervisor_hostname": "devstack001", + "memory_mb_used": 1500, + "memory_mb": 2000, + "current_workload": 0, + "vcpus": 1, + "cpu_info": '{"vendor": "Intel", "model": "core2duo",' + '"arch": "x86_64", "features": ["lahf_lm"' + ', "rdtscp"], "topology": {"cores": 1, "t' + 'hreads": 1, "sockets": 1}}', + "running_vms": 1, + "free_disk_gb": 9, + "hypervisor_version": 1002000, + "disk_available_least": 6, + "local_gb": 29, + "free_ram_mb": 500, + "id": 1 + } + ) + TEST.hypervisors.add(hypervisor_1)