Merge "Revert "Remove the update default quotas feature""

This commit is contained in:
Jenkins 2014-10-02 03:08:37 +00:00 committed by Gerrit Code Review
commit ca4a772d5f
20 changed files with 494 additions and 167 deletions

View File

@ -393,6 +393,10 @@ def volume_type_list_with_qos_associations(request):
return vol_types return vol_types
def default_quota_update(request, **kwargs):
cinderclient(request).quota_classes.update(DEFAULT_QUOTA_NAME, **kwargs)
def volume_type_list(request): def volume_type_list(request):
return cinderclient(request).volume_types.list() return cinderclient(request).volume_types.list()

View File

@ -670,6 +670,10 @@ def default_quota_get(request, tenant_id):
return base.QuotaSet(novaclient(request).quotas.defaults(tenant_id)) return base.QuotaSet(novaclient(request).quotas.defaults(tenant_id))
def default_quota_update(request, **kwargs):
novaclient(request).quota_classes.update(DEFAULT_QUOTA_NAME, **kwargs)
def usage_get(request, tenant_id, start, end): def usage_get(request, tenant_id, start, end):
return NovaUsage(novaclient(request).usage.get(tenant_id, start, end)) return NovaUsage(novaclient(request).usage.get(tenant_id, start, end))

View File

@ -31,6 +31,7 @@
"volume_extension:quotas:show": [], "volume_extension:quotas:show": [],
"volume_extension:quotas:update": [["rule:admin_api"]], "volume_extension:quotas:update": [["rule:admin_api"]],
"volume_extension:quota_classes": [],
"volume_extension:volume_admin_actions:reset_status": [["rule:admin_api"]], "volume_extension:volume_admin_actions:reset_status": [["rule:admin_api"]],
"volume_extension:snapshot_admin_actions:reset_status": [["rule:admin_api"]], "volume_extension:snapshot_admin_actions:reset_status": [["rule:admin_api"]],

View File

@ -166,6 +166,8 @@
"compute_extension:v3:os-quota-sets:show": "", "compute_extension:v3:os-quota-sets:show": "",
"compute_extension:v3:os-quota-sets:update": "rule:admin_api", "compute_extension:v3:os-quota-sets:update": "rule:admin_api",
"compute_extension:v3:os-quota-sets:delete": "rule:admin_api", "compute_extension:v3:os-quota-sets:delete": "rule:admin_api",
"compute_extension:quota_classes": "",
"compute_extension:v3:os-quota-class-sets": "",
"compute_extension:rescue": "", "compute_extension:rescue": "",
"compute_extension:v3:os-rescue": "", "compute_extension:v3:os-rescue": "",
"compute_extension:security_group_default_rules": "rule:admin_api", "compute_extension:security_group_default_rules": "rule:admin_api",

View File

@ -22,7 +22,7 @@ class SystemPanels(horizon.PanelGroup):
name = _("System") name = _("System")
panels = ('overview', 'metering', 'hypervisors', 'aggregates', panels = ('overview', 'metering', 'hypervisors', 'aggregates',
'instances', 'volumes', 'flavors', 'images', 'instances', 'volumes', 'flavors', 'images',
'networks', 'routers', 'info') 'networks', 'routers', 'defaults', 'info')
class Admin(horizon.Dashboard): class Admin(horizon.Dashboard):

View File

@ -0,0 +1,27 @@
# Copyright 2013 Kylin, Inc.
#
# 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 Defaults(horizon.Panel):
name = _("Defaults")
slug = 'defaults'
dashboard.Admin.register(Defaults)

View File

@ -0,0 +1,76 @@
# Copyright 2013 Kylin, Inc.
#
# 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 _
from horizon import tables
class QuotaFilterAction(tables.FilterAction):
def filter(self, table, tenants, filter_string):
q = filter_string.lower()
def comp(tenant):
if q in tenant.name.lower():
return True
return False
return filter(comp, tenants)
class UpdateDefaultQuotas(tables.LinkAction):
name = "update_defaults"
verbose_name = _("Update Defaults")
url = "horizon:admin:defaults:update_defaults"
classes = ("ajax-modal", "btn-edit")
def get_quota_name(quota):
QUOTA_NAMES = {
'injected_file_content_bytes': _('Injected File Content Bytes'),
'injected_file_path_bytes': _('Length of Injected File Path'),
'metadata_items': _('Metadata Items'),
'cores': _('VCPUs'),
'instances': _('Instances'),
'injected_files': _('Injected Files'),
'volumes': _('Volumes'),
'snapshots': _('Volume Snapshots'),
'gigabytes': _('Total Size of Volumes and Snapshots (GB)'),
'ram': _('RAM (MB)'),
'floating_ips': _('Floating IPs'),
'security_groups': _('Security Groups'),
'security_group_rules': _('Security Group Rules'),
'key_pairs': _('Key Pairs'),
'fixed_ips': _('Fixed IPs'),
'volumes_volume_luks': _('LUKS Volumes'),
'snapshots_volume_luks': _('LUKS Volume Snapshots'),
'gigabytes_volume_luks':
_('Total Size of LUKS Volumes and Snapshots (GB)'),
'dm-crypt': _('dm-crypt'),
}
return QUOTA_NAMES.get(quota.name, quota.name.replace("_", " ").title())
class QuotasTable(tables.DataTable):
name = tables.Column(get_quota_name, verbose_name=_('Quota Name'))
limit = tables.Column("limit", verbose_name=_('Limit'))
def get_object_id(self, obj):
return obj.name
class Meta:
name = "quotas"
verbose_name = _("Quotas")
table_actions = (QuotaFilterAction, UpdateDefaultQuotas)
multi_select = False

View File

@ -0,0 +1,44 @@
# Copyright 2013 Kylin, Inc.
#
# 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 _
from horizon import exceptions
from horizon import tabs
from openstack_dashboard.usage import quotas
from openstack_dashboard.dashboards.admin.defaults import tables
class DefaultQuotasTab(tabs.TableTab):
table_classes = (tables.QuotasTable,)
name = _("Default Quotas")
slug = "quotas"
template_name = ("horizon/common/_detail_table.html")
def get_quotas_data(self):
request = self.tab_group.request
try:
data = quotas.get_default_quota_data(request)
except Exception:
data = []
exceptions.handle(self.request, _('Unable to get quota info.'))
return data
class DefaultsTabs(tabs.TabGroup):
slug = "defaults"
tabs = (DefaultQuotasTab,)
sticky = True

View File

@ -0,0 +1,15 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Defaults" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Defaults")%}
{% endblock page_header %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ tab_group.render }}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,137 @@
# Copyright 2013 Kylin, Inc.
#
# 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 # noqa
from openstack_dashboard import api
from openstack_dashboard.test import helpers as test
from openstack_dashboard.usage import quotas
INDEX_URL = reverse('horizon:admin:defaults:index')
class ServicesViewTests(test.BaseAdminViewTests):
def test_index(self):
self._test_index(neutron_enabled=True)
def test_index_with_neutron_disabled(self):
self._test_index(neutron_enabled=False)
def test_index_with_neutron_sg_disabled(self):
self._test_index(neutron_enabled=True,
neutron_sg_enabled=False)
def _test_index(self, neutron_enabled=True, neutron_sg_enabled=True):
# Neutron does not have an API for getting default system
# quotas. When not using Neutron, the floating ips quotas
# should be in the list.
self.mox.StubOutWithMock(api.nova, 'default_quota_get')
self.mox.StubOutWithMock(api.cinder, 'default_quota_get')
self.mox.StubOutWithMock(api.base, 'is_service_enabled')
if neutron_enabled:
self.mox.StubOutWithMock(api.neutron, 'is_extension_supported')
api.base.is_service_enabled(IsA(http.HttpRequest), 'volume') \
.AndReturn(True)
api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \
.MultipleTimes().AndReturn(neutron_enabled)
api.nova.default_quota_get(IsA(http.HttpRequest),
self.tenant.id).AndReturn(self.quotas.nova)
api.cinder.default_quota_get(IsA(http.HttpRequest), self.tenant.id) \
.AndReturn(self.cinder_quotas.first())
if neutron_enabled:
api.neutron.is_extension_supported(
IsA(http.HttpRequest),
'security-group').AndReturn(neutron_sg_enabled)
self.mox.ReplayAll()
res = self.client.get(INDEX_URL)
self.assertTemplateUsed(res, 'admin/defaults/index.html')
quotas_tab = res.context['tab_group'].get_tab('quotas')
expected_tabs = ['<Quota: (injected_file_content_bytes, 1)>',
'<Quota: (metadata_items, 1)>',
'<Quota: (injected_files, 1)>',
'<Quota: (gigabytes, 1000)>',
'<Quota: (ram, 10000)>',
'<Quota: (instances, 10)>',
'<Quota: (snapshots, 1)>',
'<Quota: (volumes, 1)>',
'<Quota: (cores, 10)>',
'<Quota: (floating_ips, 1)>',
'<Quota: (fixed_ips, 10)>',
'<Quota: (security_groups, 10)>',
'<Quota: (security_group_rules, 20)>']
if neutron_enabled:
expected_tabs.remove('<Quota: (floating_ips, 1)>')
expected_tabs.remove('<Quota: (fixed_ips, 10)>')
if neutron_sg_enabled:
expected_tabs.remove('<Quota: (security_groups, 10)>')
expected_tabs.remove('<Quota: (security_group_rules, 20)>')
self.assertQuerysetEqual(quotas_tab._tables['quotas'].data,
expected_tabs,
ordered=False)
class UpdateDefaultQuotasTests(test.BaseAdminViewTests):
def _get_quota_info(self, quota):
quota_data = {}
for field in (quotas.QUOTA_FIELDS + quotas.MISSING_QUOTA_FIELDS):
if field != 'fixed_ips':
limit = quota.get(field).limit or 10
quota_data[field] = int(limit)
return quota_data
@test.create_stubs({api.nova: ('default_quota_update', ),
api.cinder: ('default_quota_update', ),
quotas: ('get_default_quota_data',
'get_disabled_quotas')})
def test_update_default_quotas(self):
quota = self.quotas.first()
# init
quotas.get_disabled_quotas(IsA(http.HttpRequest)) \
.AndReturn(self.disabled_quotas.first())
quotas.get_default_quota_data(IsA(http.HttpRequest)).AndReturn(quota)
# update some fields
quota[0].limit = 123
quota[1].limit = -1
updated_quota = self._get_quota_info(quota)
# handle
nova_fields = quotas.NOVA_QUOTA_FIELDS + quotas.MISSING_QUOTA_FIELDS
nova_updated_quota = dict([(key, updated_quota[key]) for key in
nova_fields if key != 'fixed_ips'])
api.nova.default_quota_update(IsA(http.HttpRequest),
**nova_updated_quota)
cinder_updated_quota = dict([(key, updated_quota[key]) for key in
quotas.CINDER_QUOTA_FIELDS])
api.cinder.default_quota_update(IsA(http.HttpRequest),
**cinder_updated_quota)
self.mox.ReplayAll()
url = reverse('horizon:admin:defaults:update_defaults')
res = self.client.post(url, updated_quota)
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, INDEX_URL)

View File

@ -0,0 +1,24 @@
# Copyright 2013 Kylin, Inc.
#
# 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 import patterns
from django.conf.urls import url
from openstack_dashboard.dashboards.admin.defaults import views
urlpatterns = patterns('openstack_dashboard.dashboards.admin.defaults.views',
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^update_defaults$',
views.UpdateDefaultQuotasView.as_view(), name='update_defaults'))

View File

@ -0,0 +1,47 @@
# Copyright 2013 Kylin, Inc.
#
# 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 _
from horizon import tabs
from horizon import workflows
from openstack_dashboard.dashboards.admin.defaults import tabs as project_tabs
from openstack_dashboard.dashboards.admin.defaults import workflows as \
project_workflows
from openstack_dashboard.usage import quotas
class IndexView(tabs.TabbedTableView):
tab_group_class = project_tabs.DefaultsTabs
template_name = 'admin/defaults/index.html'
class UpdateDefaultQuotasView(workflows.WorkflowView):
workflow_class = project_workflows.UpdateDefaultQuotas
def get_initial(self):
initial = super(UpdateDefaultQuotasView, self).get_initial()
# get initial quota defaults
try:
quota_defaults = quotas.get_default_quota_data(self.request)
for field in (quotas.QUOTA_FIELDS + quotas.MISSING_QUOTA_FIELDS):
initial[field] = quota_defaults.get(field).limit
except Exception:
error_msg = _('Unable to retrieve default quota values.')
self.add_error_to_step(error_msg, 'update_default_quotas')
return initial

View File

@ -0,0 +1,100 @@
# Copyright 2013 Kylin, Inc.
#
# 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 _
from horizon import exceptions
from horizon import forms
from horizon import workflows
from openstack_dashboard.api import base
from openstack_dashboard.api import cinder
from openstack_dashboard.api import nova
from openstack_dashboard.usage import quotas
ALL_NOVA_QUOTA_FIELDS = quotas.NOVA_QUOTA_FIELDS + quotas.MISSING_QUOTA_FIELDS
class UpdateDefaultQuotasAction(workflows.Action):
ifcb_label = _("Injected File Content Bytes")
ifpb_label = _("Length of Injected File Path")
injected_file_content_bytes = forms.IntegerField(min_value=-1,
label=ifcb_label)
metadata_items = forms.IntegerField(min_value=-1,
label=_("Metadata Items"))
ram = forms.IntegerField(min_value=-1, label=_("RAM (MB)"))
floating_ips = forms.IntegerField(min_value=-1, label=_("Floating IPs"))
key_pairs = forms.IntegerField(min_value=-1, label=_("Key Pairs"))
injected_file_path_bytes = forms.IntegerField(min_value=-1,
label=ifpb_label)
instances = forms.IntegerField(min_value=-1, label=_("Instances"))
security_group_rules = forms.IntegerField(min_value=-1,
label=_("Security Group Rules"))
injected_files = forms.IntegerField(min_value=-1,
label=_("Injected Files"))
cores = forms.IntegerField(min_value=-1, label=_("VCPUs"))
security_groups = forms.IntegerField(min_value=-1,
label=_("Security Groups"))
gigabytes = forms.IntegerField(min_value=-1,
label=_("Total Size of Volumes and Snapshots (GB)"))
snapshots = forms.IntegerField(min_value=-1, label=_("Volume Snapshots"))
volumes = forms.IntegerField(min_value=-1, label=_("Volumes"))
def __init__(self, request, *args, **kwargs):
super(UpdateDefaultQuotasAction, self).__init__(request,
*args,
**kwargs)
disabled_quotas = quotas.get_disabled_quotas(request)
for field in disabled_quotas:
if field in self.fields:
self.fields[field].required = False
self.fields[field].widget = forms.HiddenInput()
class Meta:
name = _("Default Quotas")
slug = 'update_default_quotas'
help_text = _("From here you can update the default quotas "
"(max limits).")
class UpdateDefaultQuotasStep(workflows.Step):
action_class = UpdateDefaultQuotasAction
contributes = (quotas.QUOTA_FIELDS + quotas.MISSING_QUOTA_FIELDS)
class UpdateDefaultQuotas(workflows.Workflow):
slug = "update_default_quotas"
name = _("Update Default Quotas")
finalize_button_name = _("Update Defaults")
success_message = _('Default quotas updated.')
failure_message = _('Unable to update default quotas.')
success_url = "horizon:admin:defaults:index"
default_steps = (UpdateDefaultQuotasStep,)
def handle(self, request, data):
# Update the default quotas.
# `fixed_ips` update for quota class is not supported by novaclient
nova_data = dict([(key, data[key]) for key in ALL_NOVA_QUOTA_FIELDS
if key != 'fixed_ips'])
try:
nova.default_quota_update(request, **nova_data)
if base.is_service_enabled(request, 'volume'):
cinder_data = dict([(key, data[key]) for key in
quotas.CINDER_QUOTA_FIELDS])
cinder.default_quota_update(request, **cinder_data)
except Exception:
exceptions.handle(request, _('Unable to update default quotas.'))
return True

View File

@ -186,55 +186,3 @@ class NetworkAgentsTable(tables.DataTable):
verbose_name = _("Network Agents") verbose_name = _("Network Agents")
table_actions = (NetworkAgentsFilterAction,) table_actions = (NetworkAgentsFilterAction,)
multi_select = False multi_select = False
class QuotaFilterAction(tables.FilterAction):
def filter(self, table, tenants, filter_string):
q = filter_string.lower()
def comp(tenant):
if q in tenant.name.lower():
return True
return False
return filter(comp, tenants)
def get_quota_name(quota):
QUOTA_NAMES = {
'injected_file_content_bytes': _('Injected File Content Bytes'),
'injected_file_path_bytes': _('Length of Injected File Path'),
'metadata_items': _('Metadata Items'),
'cores': _('VCPUs'),
'instances': _('Instances'),
'injected_files': _('Injected Files'),
'volumes': _('Volumes'),
'snapshots': _('Volume Snapshots'),
'gigabytes': _('Total Size of Volumes and Snapshots (GB)'),
'ram': _('RAM (MB)'),
'floating_ips': _('Floating IPs'),
'security_groups': _('Security Groups'),
'security_group_rules': _('Security Group Rules'),
'key_pairs': _('Key Pairs'),
'fixed_ips': _('Fixed IPs'),
'volumes_volume_luks': _('LUKS Volumes'),
'snapshots_volume_luks': _('LUKS Volume Snapshots'),
'gigabytes_volume_luks':
_('Total Size of LUKS Volumes and Snapshots (GB)'),
'dm-crypt': _('dm-crypt'),
}
return QUOTA_NAMES.get(quota.name, quota.name.replace("_", " ").title())
class QuotasTable(tables.DataTable):
name = tables.Column(get_quota_name, verbose_name=_('Quota Name'))
limit = tables.Column("limit", verbose_name=_('Limit'))
def get_object_id(self, obj):
return obj.name
class Meta:
name = "quotas"
verbose_name = _("Quotas")
table_actions = (QuotaFilterAction,)
multi_select = False

View File

@ -23,7 +23,6 @@ from openstack_dashboard.api import neutron
from openstack_dashboard.api import nova from openstack_dashboard.api import nova
from openstack_dashboard.dashboards.admin.info import constants from openstack_dashboard.dashboards.admin.info import constants
from openstack_dashboard.dashboards.admin.info import tables from openstack_dashboard.dashboards.admin.info import tables
from openstack_dashboard.usage import quotas
class ServicesTab(tabs.TableTab): class ServicesTab(tabs.TableTab):
@ -103,25 +102,8 @@ class NetworkAgentsTab(tabs.TableTab):
return agents return agents
class DefaultQuotasTab(tabs.TableTab):
table_classes = (tables.QuotasTable,)
name = _("Default Quotas")
slug = "quotas"
template_name = constants.INFO_DETAIL_TEMPLATE_NAME
permissions = ('openstack.services.compute',)
def get_quotas_data(self):
request = self.tab_group.request
try:
data = quotas.get_default_quota_data(request)
except Exception:
data = []
exceptions.handle(self.request, _('Unable to get quota info.'))
return data
class SystemInfoTabs(tabs.TabGroup): class SystemInfoTabs(tabs.TabGroup):
slug = "system_info" slug = "system_info"
tabs = (ServicesTab, NovaServicesTab, CinderServicesTab, tabs = (ServicesTab, NovaServicesTab, CinderServicesTab,
NetworkAgentsTab, DefaultQuotasTab) NetworkAgentsTab)
sticky = True sticky = True

View File

@ -26,31 +26,23 @@ INDEX_URL = reverse('horizon:admin:info:index')
class SystemInfoViewTests(test.BaseAdminViewTests): class SystemInfoViewTests(test.BaseAdminViewTests):
@test.create_stubs({api.base: ('is_service_enabled',), @test.create_stubs({api.base: ('is_service_enabled',),
api.nova: ('default_quota_get', 'service_list'), api.nova: ('service_list',),
api.neutron: ('agent_list', 'is_extension_supported'), api.neutron: ('agent_list', 'is_extension_supported'),
api.cinder: ('default_quota_get', 'service_list')}) api.cinder: ('service_list',)})
def test_index(self): def test_index(self):
services = self.services.list() services = self.services.list()
api.nova.service_list(IsA(http.HttpRequest)).AndReturn(services) api.nova.service_list(IsA(http.HttpRequest)).AndReturn(services)
api.base.is_service_enabled(IsA(http.HttpRequest), IgnoreArg()) \
.MultipleTimes().AndReturn(True)
api.neutron.is_extension_supported(IsA(http.HttpRequest), api.neutron.is_extension_supported(IsA(http.HttpRequest),
'agent').AndReturn(True) 'agent').AndReturn(True)
agents = self.agents.list() agents = self.agents.list()
api.neutron.agent_list(IsA(http.HttpRequest)).AndReturn(agents) api.neutron.agent_list(IsA(http.HttpRequest)).AndReturn(agents)
api.base.is_service_enabled(IsA(http.HttpRequest), IgnoreArg()) \
.MultipleTimes().AndReturn(True)
api.nova.default_quota_get(IsA(http.HttpRequest),
IgnoreArg()).AndReturn({})
api.cinder.default_quota_get(IsA(http.HttpRequest), self.tenant.id)\
.AndReturn(self.cinder_quotas.first())
cinder_services = self.cinder_services.list() cinder_services = self.cinder_services.list()
api.cinder.service_list(IsA(http.HttpRequest)).\ api.cinder.service_list(IsA(http.HttpRequest)).\
AndReturn(cinder_services) AndReturn(cinder_services)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'security-group').AndReturn(True)
self.mox.ReplayAll() self.mox.ReplayAll()
res = self.client.get(INDEX_URL) res = self.client.get(INDEX_URL)
@ -79,25 +71,19 @@ class SystemInfoViewTests(test.BaseAdminViewTests):
self.mox.VerifyAll() self.mox.VerifyAll()
@test.create_stubs({api.base: ('is_service_enabled',), @test.create_stubs({api.base: ('is_service_enabled',),
api.cinder: ('default_quota_get', 'service_list'), api.cinder: ('service_list',),
api.nova: ('default_quota_get', 'service_list'), api.nova: ('service_list',),
api.neutron: ('agent_list', 'is_extension_supported')}) api.neutron: ('agent_list', 'is_extension_supported')})
def test_cinder_services_index(self): def test_cinder_services_index(self):
cinder_services = self.cinder_services.list() cinder_services = self.cinder_services.list()
api.nova.service_list(IsA(http.HttpRequest)).AndReturn([]) api.nova.service_list(IsA(http.HttpRequest)).AndReturn([])
api.cinder.default_quota_get(IsA(http.HttpRequest), self.tenant.id)\
.AndReturn(self.cinder_quotas.first())
api.cinder.service_list(IsA(http.HttpRequest)).\ api.cinder.service_list(IsA(http.HttpRequest)).\
AndReturn(cinder_services) AndReturn(cinder_services)
api.neutron.agent_list(IsA(http.HttpRequest)).AndReturn([]) api.neutron.agent_list(IsA(http.HttpRequest)).AndReturn([])
api.base.is_service_enabled(IsA(http.HttpRequest), IgnoreArg()) \ api.base.is_service_enabled(IsA(http.HttpRequest), IgnoreArg()) \
.MultipleTimes().AndReturn(True) .MultipleTimes().AndReturn(True)
api.nova.default_quota_get(IsA(http.HttpRequest),
IgnoreArg()).AndReturn({})
api.neutron.is_extension_supported(IsA(http.HttpRequest), api.neutron.is_extension_supported(IsA(http.HttpRequest),
'agent').AndReturn(True) 'agent').AndReturn(True)
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'security-group').AndReturn(True)
self.mox.ReplayAll() self.mox.ReplayAll()
res = self.client.get(INDEX_URL) res = self.client.get(INDEX_URL)
@ -109,73 +95,3 @@ class SystemInfoViewTests(test.BaseAdminViewTests):
['cinder_services'].data, ['cinder_services'].data,
['<Service: cinder-scheduler>', ['<Service: cinder-scheduler>',
'<Service: cinder-volume>']) '<Service: cinder-volume>'])
def test_default_quotas_index(self):
self._test_default_quotas_index(neutron_enabled=True)
def test_default_quotas_index_with_neutron_disabled(self):
self._test_default_quotas_index(neutron_enabled=False)
def test_default_quotas_index_with_neutron_sg_disabled(self):
self._test_default_quotas_index(neutron_enabled=True,
neutron_sg_enabled=False)
@test.create_stubs({api.base: ('is_service_enabled',),
api.nova: ('default_quota_get', 'service_list'),
api.cinder: ('default_quota_get', 'service_list')})
def _test_default_quotas_index(self, neutron_enabled=True,
neutron_sg_enabled=True):
# Neutron does not have an API for getting default system
# quotas. When not using Neutron, the floating ips quotas
# should be in the list.
api.base.is_service_enabled(IsA(http.HttpRequest), 'volume') \
.MultipleTimes().AndReturn(True)
api.base.is_service_enabled(IsA(http.HttpRequest), 'network') \
.MultipleTimes().AndReturn(neutron_enabled)
api.nova.service_list(IsA(http.HttpRequest)).AndReturn([])
api.nova.default_quota_get(IsA(http.HttpRequest),
self.tenant.id).AndReturn(self.quotas.nova)
api.cinder.default_quota_get(IsA(http.HttpRequest), self.tenant.id)\
.AndReturn(self.cinder_quotas.first())
api.cinder.service_list(IsA(http.HttpRequest)).AndReturn([])
if neutron_enabled:
self.mox.StubOutWithMock(api.neutron, 'agent_list')
self.mox.StubOutWithMock(api.neutron, 'is_extension_supported')
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'agent').AndReturn(True)
api.neutron.agent_list(IsA(http.HttpRequest)).AndReturn([])
api.neutron.is_extension_supported(IsA(http.HttpRequest),
'security-group').AndReturn(neutron_sg_enabled)
self.mox.ReplayAll()
res = self.client.get(INDEX_URL)
quotas_tab = res.context['tab_group'].get_tab('quotas')
expected_tabs = ['<Quota: (injected_file_content_bytes, 1)>',
'<Quota: (metadata_items, 1)>',
'<Quota: (injected_files, 1)>',
'<Quota: (gigabytes, 1000)>',
'<Quota: (ram, 10000)>',
'<Quota: (instances, 10)>',
'<Quota: (snapshots, 1)>',
'<Quota: (volumes, 1)>',
'<Quota: (cores, 10)>',
'<Quota: (floating_ips, 1)>',
'<Quota: (fixed_ips, 10)>',
'<Quota: (security_groups, 10)>',
'<Quota: (security_group_rules, 20)>']
if neutron_enabled:
expected_tabs.remove('<Quota: (floating_ips, 1)>')
expected_tabs.remove('<Quota: (fixed_ips, 10)>')
if neutron_sg_enabled:
expected_tabs.remove('<Quota: (security_groups, 10)>')
expected_tabs.remove('<Quota: (security_group_rules, 20)>')
self.assertQuerysetEqual(quotas_tab._tables['quotas'].data,
expected_tabs,
ordered=False)

View File

@ -1,9 +1,9 @@
# The name of the panel to be added to HORIZON_CONFIG. Required. # The name of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'instances' PANEL = 'defaults'
# The name of the dashboard the PANEL associated with. Required. # The name of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'admin' PANEL_DASHBOARD = 'admin'
# The name of the panel group the PANEL is associated with. # The name of the panel group the PANEL is associated with.
PANEL_GROUP = 'admin' PANEL_GROUP = 'admin'
# If set, it will update the default panel of the PANEL_DASHBOARD. # If set, it will update the default panel of the PANEL_DASHBOARD.
DEFAULT_PANEL = 'instances' DEFAULT_PANEL = 'defaults'

View File

@ -1,9 +1,9 @@
# The name of the panel to be added to HORIZON_CONFIG. Required. # The name of the panel to be added to HORIZON_CONFIG. Required.
PANEL = 'instances' PANEL = 'defaults'
# The name of the dashboard the PANEL associated with. Required. # The name of the dashboard the PANEL associated with. Required.
PANEL_DASHBOARD = 'admin' PANEL_DASHBOARD = 'admin'
# The name of the panel group the PANEL is associated with. # The name of the panel group the PANEL is associated with.
PANEL_GROUP = 'admin' PANEL_GROUP = 'admin'
# If set, it will update the default panel of the PANEL_DASHBOARD. # If set, it will update the default panel of the PANEL_DASHBOARD.
DEFAULT_PANEL = 'instances' DEFAULT_PANEL = 'defaults'

View File

@ -48,4 +48,4 @@ class PanelPluginTests(test.PluginTestCase):
def test_default_panel(self): def test_default_panel(self):
dashboard = horizon.get_dashboard("admin") dashboard = horizon.get_dashboard("admin")
self.assertEqual('instances', dashboard.default_panel) self.assertEqual('defaults', dashboard.default_panel)