Merge "Use Placement API along with the hypervisor stats"
This commit is contained in:
commit
108c8c2043
@ -38,6 +38,7 @@ from openstack_dashboard.api import keystone
|
||||
from openstack_dashboard.api import network
|
||||
from openstack_dashboard.api import neutron
|
||||
from openstack_dashboard.api import nova
|
||||
from openstack_dashboard.api import placement
|
||||
from openstack_dashboard.api import swift
|
||||
|
||||
|
||||
@ -49,5 +50,6 @@ __all__ = [
|
||||
"network",
|
||||
"neutron",
|
||||
"nova",
|
||||
"placement",
|
||||
"swift",
|
||||
]
|
||||
|
110
openstack_dashboard/api/placement.py
Normal file
110
openstack_dashboard/api/placement.py
Normal file
@ -0,0 +1,110 @@
|
||||
# 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 keystoneauth1 import adapter
|
||||
from keystoneauth1 import identity
|
||||
from keystoneauth1 import session
|
||||
|
||||
from openstack_dashboard.api import base
|
||||
|
||||
from horizon.utils.memoized import memoized
|
||||
|
||||
|
||||
class Adapter(adapter.LegacyJsonAdapter):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.api_version = kwargs.pop('api_version', None)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def request(self, url, method, **kwargs):
|
||||
kwargs.setdefault('headers', kwargs.get('headers', {}))
|
||||
if self.api_version is not None:
|
||||
kwargs['headers']['OpenStack-API-Version'] = self.api_version
|
||||
resp, body = super().request(url, method, **kwargs)
|
||||
return resp, body
|
||||
|
||||
|
||||
@memoized
|
||||
def make_adapter(request):
|
||||
auth = identity.Token(
|
||||
auth_url=base.url_for(request, 'identity'),
|
||||
token=request.user.token.id,
|
||||
project_id=request.user.project_id,
|
||||
project_name=request.user.project_name,
|
||||
project_domain_name=request.user.domain_id,
|
||||
)
|
||||
return Adapter(session.Session(auth=auth), api_version="placement 1.6")
|
||||
|
||||
|
||||
def _get_json(request, path):
|
||||
adapter = make_adapter(request)
|
||||
uri = base.url_for(request, 'placement') + path
|
||||
response, body = adapter.get(uri)
|
||||
return response.json()
|
||||
|
||||
|
||||
def get_versions(request):
|
||||
versions = _get_json(request, '/')
|
||||
return versions
|
||||
|
||||
|
||||
def resource_providers(request):
|
||||
providers = _get_json(request, '/resource_providers')
|
||||
return providers['resource_providers']
|
||||
|
||||
|
||||
def get_providers_uuids(request):
|
||||
providers = resource_providers(request)
|
||||
return [p['uuid'] for p in providers]
|
||||
|
||||
|
||||
def resource_provider_inventories(request, uuid):
|
||||
return _get_json(
|
||||
request, f'/resource_providers/{uuid}/inventories')['inventories']
|
||||
|
||||
|
||||
def resource_provider_usages(request, uuid):
|
||||
return _get_json(request, f'/resource_providers/{uuid}/usages')['usages']
|
||||
|
||||
|
||||
def resource_provider_aggregates(request, uuid):
|
||||
return _get_json(
|
||||
request, f'/resource_providers/{uuid}/aggregates')['aggregates']
|
||||
|
||||
|
||||
def resource_provider_traits(request, uuid):
|
||||
return _get_json(request, f'/resource_providers/{uuid}/traits')['traits']
|
||||
|
||||
|
||||
def get_providers(request):
|
||||
providers = resource_providers(request)
|
||||
for p in providers:
|
||||
inventories = resource_provider_inventories(request, p['uuid'])
|
||||
usages = resource_provider_usages(request, p['uuid'])
|
||||
vcpus = inventories.get('VCPU')
|
||||
pcpus = inventories.get('PCPU')
|
||||
p['inventories'] = inventories
|
||||
p['usages'] = usages
|
||||
p['aggregates'] = resource_provider_aggregates(request, p['uuid'])
|
||||
p['traits'] = resource_provider_traits(request, p['uuid'])
|
||||
p['vcpus_used'] = usages.get('VCPU')
|
||||
p['vcpus_reserved'] = vcpus['reserved'] if vcpus is not None else None
|
||||
p['vcpus'] = vcpus['total'] if vcpus is not None else None
|
||||
p['pcpus_used'] = usages.get('PCPU')
|
||||
p['pcpus_reserved'] = pcpus['reserved'] if pcpus is not None else None
|
||||
p['pcpus'] = pcpus['total'] if pcpus is not None else None
|
||||
p['memory_mb_used'] = usages['MEMORY_MB']
|
||||
p['memory_mb_reserved'] = inventories['MEMORY_MB']['reserved']
|
||||
p['memory_mb'] = inventories['MEMORY_MB']['total']
|
||||
p['disk_gb_used'] = usages['DISK_GB']
|
||||
p['disk_gb_reserved'] = inventories['DISK_GB']['reserved']
|
||||
p['disk_gb'] = inventories['DISK_GB']['total']
|
||||
return providers
|
@ -22,36 +22,24 @@ class AdminHypervisorsTable(tables.DataTable):
|
||||
hostname = tables.WrappingColumn("hypervisor_hostname",
|
||||
link="horizon:admin:hypervisors:detail",
|
||||
verbose_name=_("Hostname"))
|
||||
|
||||
hypervisor_type = tables.Column("hypervisor_type",
|
||||
verbose_name=_("Type"))
|
||||
|
||||
vcpus_used = tables.Column("vcpus_used",
|
||||
verbose_name=_("VCPUs (used)"))
|
||||
|
||||
vcpus = tables.Column("vcpus",
|
||||
verbose_name=_("VCPUs (total)"))
|
||||
|
||||
memory_used = tables.Column('memory_mb_used',
|
||||
verbose_name=_("RAM (used)"),
|
||||
attrs={'data-type': 'size'},
|
||||
filters=(sizeformat.mb_float_format,))
|
||||
|
||||
memory = tables.Column('memory_mb',
|
||||
verbose_name=_("RAM (total)"),
|
||||
attrs={'data-type': 'size'},
|
||||
filters=(sizeformat.mb_float_format,))
|
||||
|
||||
local_used = tables.Column('local_gb_used',
|
||||
verbose_name=_("Local Storage (used)"),
|
||||
attrs={'data-type': 'size'},
|
||||
filters=(sizeformat.diskgbformat,))
|
||||
|
||||
local = tables.Column('local_gb',
|
||||
verbose_name=_("Local Storage (total)"),
|
||||
attrs={'data-type': 'size'},
|
||||
filters=(sizeformat.diskgbformat,))
|
||||
|
||||
running_vms = tables.Column("running_vms",
|
||||
verbose_name=_("Instances"))
|
||||
|
||||
@ -78,3 +66,51 @@ class AdminHypervisorInstancesTable(tables.DataTable):
|
||||
class Meta(object):
|
||||
name = "hypervisor_instances"
|
||||
verbose_name = _("Hypervisor Instances")
|
||||
|
||||
|
||||
class AdminProvidersTable(tables.DataTable):
|
||||
name = tables.WrappingColumn("name",
|
||||
verbose_name=_("Resource Provider Name"))
|
||||
vcpus_used = tables.Column("vcpus_used",
|
||||
verbose_name=_("VCPUs (used)"))
|
||||
vcpus_reserved = tables.Column("vcpus_reserved",
|
||||
verbose_name=_("VCPUs (reserved)"))
|
||||
vcpus = tables.Column("vcpus",
|
||||
verbose_name=_("VCPUs (total)"))
|
||||
pcpus_used = tables.Column("pcpus_used",
|
||||
verbose_name=_("PCPUs (used)"))
|
||||
pcpus_reserved = tables.Column("pcpus_reserved",
|
||||
verbose_name=_("PCPUs (reserved)"))
|
||||
pcpus = tables.Column("pcpus",
|
||||
verbose_name=_("PCPUs (total)"))
|
||||
memory_used = tables.Column('memory_mb_used',
|
||||
verbose_name=_("RAM (used)"),
|
||||
attrs={'data-type': 'size'},
|
||||
filters=(sizeformat.mb_float_format,))
|
||||
memory_reserved = tables.Column('memory_mb_reserved',
|
||||
verbose_name=_("RAM (reserved)"),
|
||||
attrs={'data-type': 'size'},
|
||||
filters=(sizeformat.mb_float_format,))
|
||||
memory = tables.Column('memory_mb',
|
||||
verbose_name=_("RAM (total)"),
|
||||
attrs={'data-type': 'size'},
|
||||
filters=(sizeformat.mb_float_format,))
|
||||
disk_used = tables.Column('disk_gb_used',
|
||||
verbose_name=_("Storage (used)"),
|
||||
attrs={'data-type': 'size'},
|
||||
filters=(sizeformat.diskgbformat,))
|
||||
disk_reserved = tables.Column('disk_gb_reserved',
|
||||
verbose_name=_("Storage (reserved)"),
|
||||
attrs={'data-type': 'size'},
|
||||
filters=(sizeformat.diskgbformat,))
|
||||
disk = tables.Column('disk_gb',
|
||||
verbose_name=_("Storage (total)"),
|
||||
attrs={'data-type': 'size'},
|
||||
filters=(sizeformat.diskgbformat,))
|
||||
|
||||
def get_object_id(self, provider):
|
||||
return provider['uuid']
|
||||
|
||||
class Meta(object):
|
||||
name = "providers"
|
||||
verbose_name = _("Resource Providers")
|
||||
|
@ -17,6 +17,7 @@ from horizon import tabs
|
||||
from horizon.utils import functions as utils
|
||||
|
||||
from openstack_dashboard.api import nova
|
||||
from openstack_dashboard.api import placement
|
||||
from openstack_dashboard.dashboards.admin.hypervisors.compute \
|
||||
import tabs as cmp_tabs
|
||||
from openstack_dashboard.dashboards.admin.hypervisors import tables
|
||||
@ -40,7 +41,24 @@ class HypervisorTab(tabs.TableTab):
|
||||
return hypervisors
|
||||
|
||||
|
||||
class ProviderTab(tabs.TableTab):
|
||||
table_classes = (tables.AdminProvidersTable,)
|
||||
name = _("Resource Provider")
|
||||
slug = "provider"
|
||||
template_name = "horizon/common/_detail_table.html"
|
||||
|
||||
def get_providers_data(self):
|
||||
providers = []
|
||||
try:
|
||||
providers = placement.get_providers(self.request)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve providers information.'))
|
||||
|
||||
return providers
|
||||
|
||||
|
||||
class HypervisorHostTabs(tabs.TabGroup):
|
||||
slug = "hypervisor_info"
|
||||
tabs = (HypervisorTab, cmp_tabs.ComputeHostTab)
|
||||
tabs = (HypervisorTab, cmp_tabs.ComputeHostTab, ProviderTab)
|
||||
sticky = True
|
||||
|
@ -6,14 +6,6 @@
|
||||
{% block main %}
|
||||
<div class="quota-dynamic">
|
||||
<h3>{% trans "Hypervisor Summary" %}</h3>
|
||||
<div class="col-sm-4 d3_quota_bar">
|
||||
<div class="pie-chart-usage" data-used="{% widthratio stats.vcpus_used stats.vcpus 100 %}"></div>
|
||||
<div class="h5">{% trans "VCPU Usage" %}</div>
|
||||
<div class="h6">
|
||||
{% blocktrans with used=stats.vcpus_used|intcomma available=stats.vcpus|intcomma %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4 d3_quota_bar">
|
||||
<div class="pie-chart-usage" data-used="{% widthratio stats.memory_mb_used stats.memory_mb 100 %}"></div>
|
||||
<div class="h5">{% trans "Memory Usage" %}</div>
|
||||
@ -30,6 +22,47 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="quota-dynamic">
|
||||
<h3>{% trans "Resource Providers Summary" %}</h3>
|
||||
{% for provider in providers %}
|
||||
<h4>{{ provider.name }}</h4>
|
||||
<div class="col-sm-4 d3_quota_bar col-lg-3 col-md-2 col-xs-4">
|
||||
<div class="pie-chart-usage" data-used="{% widthratio provider.usages.VCPU provider.inventories.VCPU.total 100 %}"></div>
|
||||
<div class="h5">{% trans "VCPU Usage" %}</div>
|
||||
{% if provider.inventories.VCPU != None %}
|
||||
<div class="h6">
|
||||
{% blocktrans with used=provider.usages.VCPU|intcomma available=provider.inventories.VCPU.total|intcomma %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4 d3_quota_bar col-lg-3 col-md-2 col-xs-4">
|
||||
<div class="pie-chart-usage" data-used="{% widthratio provider.usages.PCPU provider.inventories.PCPU.total 100 %}"></div>
|
||||
<div class="h5">{% trans "PCPU Usage" %}</div>
|
||||
{% if provider.inventories.PCPU != None %}
|
||||
<div class="h6">
|
||||
{% blocktrans with used=provider.usages.PCPU|intcomma available=provider.inventories.PCPU.total|intcomma %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4 d3_quota_bar col-lg-3 col-md-2 col-xs-4">
|
||||
<div class="pie-chart-usage" data-used="{% widthratio provider.usages.MEMORY_MB provider.inventories.MEMORY_MB.total 100 %}"></div>
|
||||
<div class="h5">{% trans "Memory Usage" %}</div>
|
||||
<div class="h6">
|
||||
{% blocktrans with used=provider.usages.MEMORY_MB|mb_float_format available=provider.inventories.MEMORY_MB.total|mb_float_format %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4 d3_quota_bar col-lg-3 col-md-2 col-xs-4">
|
||||
<div class="pie-chart-usage" data-used="{% widthratio provider.usages.DISK_GB provider.inventories.DISK_GB.total 100 %}"></div>
|
||||
<div class="h5">{% trans "Local Disk Usage" %}</div>
|
||||
<div class="h6">
|
||||
{% blocktrans with used=provider.usages.DISK_GB|diskgbformat available=provider.inventories.DISK_GB.total|diskgbformat %}Used <span> {{ used }} </span> of <span> {{ available }} </span>{% endblocktrans %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
|
@ -76,7 +76,7 @@ class HypervisorViewTest(test.BaseAdminViewTests):
|
||||
self.mock_service_list.side_effect = self.exceptions.nova
|
||||
|
||||
resp = self.client.get(reverse('horizon:admin:hypervisors:index'))
|
||||
self.assertMessageCount(resp, error=1, warning=0)
|
||||
self.assertMessageCount(resp, error=2, warning=0)
|
||||
|
||||
self.mock_hypervisor_list.assert_called_once_with(
|
||||
test.IsHttpRequest())
|
||||
|
@ -37,7 +37,11 @@ class AdminIndexView(tabs.TabbedTableView):
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve hypervisor statistics.'))
|
||||
|
||||
try:
|
||||
context["providers"] = api.placement.get_providers(self.request)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve providers statistics.'))
|
||||
return context
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user