Renamed all user-facing cases of "tenant" to "project".
This required a little more refactoring than expected because I wanted to make the url slug also say "project", which required changing the panel slug, which in turn required changing the name of the panel directory, and then it seemed silly not to make the template directory match. Fixed bug 909495. Change-Id: I9da3b544b46f507a223a44c256d0008c8674ab3b
This commit is contained in:
parent
6892653c05
commit
87e6970a54
@ -126,3 +126,16 @@ Be sure to generate the documentation before submitting a patch for review.
|
|||||||
Unexpected warnings often appear when building the documentation, and slight
|
Unexpected warnings often appear when building the documentation, and slight
|
||||||
reST syntax errors frequently cause links or cross-references not to work
|
reST syntax errors frequently cause links or cross-references not to work
|
||||||
correctly.
|
correctly.
|
||||||
|
|
||||||
|
Conventions
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Simply by convention, we have a few rules about naming:
|
||||||
|
|
||||||
|
* The term "project" is used in place of Keystone's "tenant" terminology
|
||||||
|
in all user-facing text. The term "tenant" is still used in API code to
|
||||||
|
make things more obvious for developers.
|
||||||
|
|
||||||
|
* The term "dashboard" refers to a top-level dashboard class, and "panel" to
|
||||||
|
the sub-items within a dashboard. Referring to a panel as a dashboard is
|
||||||
|
both confusing and incorrect.
|
||||||
|
@ -17,3 +17,8 @@ Panel
|
|||||||
A Python class representing a sub-navigation item (e.g. "instances")
|
A Python class representing a sub-navigation item (e.g. "instances")
|
||||||
which contains all the necessary logic (views, forms, tests, etc.) for
|
which contains all the necessary logic (views, forms, tests, etc.) for
|
||||||
that interface.
|
that interface.
|
||||||
|
|
||||||
|
Project
|
||||||
|
|
||||||
|
Used in user-facing text in place of the term "Tenant" which is Keystone's
|
||||||
|
word.
|
||||||
|
@ -31,7 +31,7 @@ LOG = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class AllocateIP(tables.LinkAction):
|
class AllocateIP(tables.LinkAction):
|
||||||
name = "allocate"
|
name = "allocate"
|
||||||
verbose_name = _("Allocate IP To Tenant")
|
verbose_name = _("Allocate IP To Project")
|
||||||
attrs = {"class": "ajax-modal btn small"}
|
attrs = {"class": "ajax-modal btn small"}
|
||||||
url = "horizon:nova:access_and_security:floating_ips:allocate"
|
url = "horizon:nova:access_and_security:floating_ips:allocate"
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ class NetworksTable(tables.DataTable):
|
|||||||
used = tables.Column('used', verbose_name=_('Used'))
|
used = tables.Column('used', verbose_name=_('Used'))
|
||||||
available = tables.Column('available', verbose_name=_('Available'))
|
available = tables.Column('available', verbose_name=_('Available'))
|
||||||
total = tables.Column('total', verbose_name=_('Total'))
|
total = tables.Column('total', verbose_name=_('Total'))
|
||||||
#tenant = tables.Column('tenant', verbose_name=_('Tenant'))
|
#tenant = tables.Column('tenant', verbose_name=_('Project'))
|
||||||
|
|
||||||
def get_object_id(self, datum):
|
def get_object_id(self, datum):
|
||||||
return datum['id']
|
return datum['id']
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<h3>{% trans "Description:" %}</h3>
|
<h3>{% trans "Description:" %}</h3>
|
||||||
<p>{% trans "Specify the details for launching an instance. Also please make note of the table below; all tenants have quotas which define the limit of resources they are allowed to provision." %}</p>
|
<p>{% trans "Specify the details for launching an instance. Also please make note of the table below; all projects have quotas which define the limit of resources they are allowed to provision." %}</p>
|
||||||
<table class="table table-striped table-bordered">
|
<table class="table table-striped table-bordered">
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Quota Name" %}</th>
|
<th>{% trans "Quota Name" %}</th>
|
||||||
|
@ -20,9 +20,9 @@ import horizon
|
|||||||
|
|
||||||
|
|
||||||
class Settings(horizon.Dashboard):
|
class Settings(horizon.Dashboard):
|
||||||
name = "Settings"
|
name = _("Settings")
|
||||||
slug = "settings"
|
slug = "settings"
|
||||||
panels = ('user', 'tenant')
|
panels = ('user', 'project')
|
||||||
default_panel = 'user'
|
default_panel = 'user'
|
||||||
nav = False
|
nav = False
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ LOG = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class DownloadOpenRCForm(forms.SelfHandlingForm):
|
class DownloadOpenRCForm(forms.SelfHandlingForm):
|
||||||
tenant = forms.ChoiceField(label=_("Select a Tenant"))
|
tenant = forms.ChoiceField(label=_("Select a Project"))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _instantiate(cls, request, *args, **kwargs):
|
def _instantiate(cls, request, *args, **kwargs):
|
||||||
@ -58,7 +58,7 @@ class DownloadOpenRCForm(forms.SelfHandlingForm):
|
|||||||
'tenant_id': data['tenant']}
|
'tenant_id': data['tenant']}
|
||||||
|
|
||||||
response = shortcuts.render(request,
|
response = shortcuts.render(request,
|
||||||
'settings/tenant/openrc.sh.template',
|
'settings/project/openrc.sh.template',
|
||||||
context,
|
context,
|
||||||
content_type="text/plain")
|
content_type="text/plain")
|
||||||
response['Content-Disposition'] = 'attachment; filename=openrc.sh'
|
response['Content-Disposition'] = 'attachment; filename=openrc.sh'
|
@ -19,8 +19,8 @@ from horizon.dashboards.settings import dashboard
|
|||||||
|
|
||||||
|
|
||||||
class TenantPanel(horizon.Panel):
|
class TenantPanel(horizon.Panel):
|
||||||
name = "Tenant Settings"
|
name = "Project Settings"
|
||||||
slug = 'tenant'
|
slug = 'project'
|
||||||
|
|
||||||
|
|
||||||
dashboard.Settings.register(TenantPanel)
|
dashboard.Settings.register(TenantPanel)
|
@ -17,5 +17,5 @@
|
|||||||
from django.conf.urls.defaults import patterns, url
|
from django.conf.urls.defaults import patterns, url
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = patterns('horizon.dashboards.settings.tenant.views',
|
urlpatterns = patterns('horizon.dashboards.settings.project.views',
|
||||||
url(r'^$', 'index', name='index'))
|
url(r'^$', 'index', name='index'))
|
@ -15,7 +15,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from django import shortcuts
|
from django import shortcuts
|
||||||
from horizon.dashboards.settings.tenant.forms import DownloadOpenRCForm
|
from .forms import DownloadOpenRCForm
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
@ -26,4 +26,4 @@ def index(request):
|
|||||||
|
|
||||||
context = {'form': form}
|
context = {'form': form}
|
||||||
|
|
||||||
return shortcuts.render(request, 'settings/tenant/settings.html', context)
|
return shortcuts.render(request, 'settings/project/settings.html', context)
|
@ -2,7 +2,7 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block form_id %}openrc_download_form{% endblock %}
|
{% block form_id %}openrc_download_form{% endblock %}
|
||||||
{% block form_action %}{% url horizon:settings:tenant:index %}{% endblock %}
|
{% block form_action %}{% url horizon:settings:project:index %}{% endblock %}
|
||||||
|
|
||||||
{% block modal_id %}language_settings_modal{% endblock %}
|
{% block modal_id %}language_settings_modal{% endblock %}
|
||||||
{% block modal-header %}{% trans "Download RC File" %}{% endblock %}
|
{% block modal-header %}{% trans "Download RC File" %}{% endblock %}
|
||||||
@ -15,12 +15,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<h3>{% trans "Description:" %}</h3>
|
<h3>{% trans "Description:" %}</h3>
|
||||||
<p>{% trans 'Download the RC file for the selected tenant, then type "source openrc" in the terminal to configure your environment to communicate with OpenStack.' %}</p>
|
<p>{% trans 'Download the RC file for the selected project, then type "source openrc" in the terminal to configure your environment to communicate with OpenStack.' %}</p>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block modal-footer %}
|
{% block modal-footer %}
|
||||||
<input class="btn primary pull-right" type="submit" value="{% trans "Download RC File" %}" />
|
<input class="btn primary pull-right" type="submit" value="{% trans "Download RC File" %}" />
|
||||||
{% if hide %}<a href="{% url horizon:settings:tenant:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>{% endif %}
|
{% if hide %}<a href="{% url horizon:settings:project:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
@ -3,9 +3,9 @@
|
|||||||
{% block title %}OpenRC Download{% endblock %}
|
{% block title %}OpenRC Download{% endblock %}
|
||||||
|
|
||||||
{% block page_header %}
|
{% block page_header %}
|
||||||
{% include "horizon/common/_page_header.html" with title=_("Tenant Settings") %}
|
{% include "horizon/common/_page_header.html" with title=_("Project Settings") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block settings_main %}
|
{% block settings_main %}
|
||||||
{% include "settings/tenant/_openrc.html" %}
|
{% include "settings/project/_openrc.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -15,7 +15,6 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from django import shortcuts
|
from django import shortcuts
|
||||||
from horizon.dashboards.settings.tenant.forms import DownloadOpenRCForm
|
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
|
@ -23,7 +23,7 @@ class Syspanel(horizon.Dashboard):
|
|||||||
name = "Admin" # Appears in navigation
|
name = "Admin" # Appears in navigation
|
||||||
slug = "syspanel"
|
slug = "syspanel"
|
||||||
panels = {_("System Panel"): ('overview', 'instances', 'services',
|
panels = {_("System Panel"): ('overview', 'instances', 'services',
|
||||||
'flavors', 'images', 'tenants', 'users',
|
'flavors', 'images', 'projects', 'users',
|
||||||
'quotas',)}
|
'quotas',)}
|
||||||
default_panel = 'overview'
|
default_panel = 'overview'
|
||||||
roles = ('admin',)
|
roles = ('admin',)
|
||||||
|
@ -52,7 +52,7 @@ class AddUser(forms.SelfHandlingForm):
|
|||||||
messages.success(request, _('Successfully added user to tenant.'))
|
messages.success(request, _('Successfully added user to tenant.'))
|
||||||
except:
|
except:
|
||||||
exceptions.handle(request, _('Unable to add user to tenant.'))
|
exceptions.handle(request, _('Unable to add user to tenant.'))
|
||||||
return shortcuts.redirect('horizon:syspanel:tenants:users',
|
return shortcuts.redirect('horizon:syspanel:projects:users',
|
||||||
tenant_id=data['tenant_id'])
|
tenant_id=data['tenant_id'])
|
||||||
|
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ class CreateTenant(forms.SelfHandlingForm):
|
|||||||
% data['name'])
|
% data['name'])
|
||||||
except:
|
except:
|
||||||
exceptions.handle(request, _('Unable to create tenant.'))
|
exceptions.handle(request, _('Unable to create tenant.'))
|
||||||
return shortcuts.redirect('horizon:syspanel:tenants:index')
|
return shortcuts.redirect('horizon:syspanel:projects:index')
|
||||||
|
|
||||||
|
|
||||||
class UpdateTenant(forms.SelfHandlingForm):
|
class UpdateTenant(forms.SelfHandlingForm):
|
||||||
@ -102,7 +102,7 @@ class UpdateTenant(forms.SelfHandlingForm):
|
|||||||
% data['name'])
|
% data['name'])
|
||||||
except:
|
except:
|
||||||
exceptions.handle(request, _('Unable to update tenant.'))
|
exceptions.handle(request, _('Unable to update tenant.'))
|
||||||
return shortcuts.redirect('horizon:syspanel:tenants:index')
|
return shortcuts.redirect('horizon:syspanel:projects:index')
|
||||||
|
|
||||||
|
|
||||||
class UpdateQuotas(forms.SelfHandlingForm):
|
class UpdateQuotas(forms.SelfHandlingForm):
|
||||||
@ -138,4 +138,4 @@ class UpdateQuotas(forms.SelfHandlingForm):
|
|||||||
% data['tenant_id'])
|
% data['tenant_id'])
|
||||||
except:
|
except:
|
||||||
exceptions.handle(request, _('Unable to update quotas.'))
|
exceptions.handle(request, _('Unable to update quotas.'))
|
||||||
return shortcuts.redirect('horizon:syspanel:tenants:index')
|
return shortcuts.redirect('horizon:syspanel:projects:index')
|
@ -23,8 +23,8 @@ from horizon.dashboards.syspanel import dashboard
|
|||||||
|
|
||||||
|
|
||||||
class Tenants(horizon.Panel):
|
class Tenants(horizon.Panel):
|
||||||
name = "Tenants"
|
name = "Projects"
|
||||||
slug = 'tenants'
|
slug = 'projects'
|
||||||
|
|
||||||
|
|
||||||
dashboard.Syspanel.register(Tenants)
|
dashboard.Syspanel.register(Tenants)
|
@ -15,39 +15,39 @@ LOG = logging.getLogger(__name__)
|
|||||||
class ModifyQuotasLink(tables.LinkAction):
|
class ModifyQuotasLink(tables.LinkAction):
|
||||||
name = "quotas"
|
name = "quotas"
|
||||||
verbose_name = _("Modify Quotas")
|
verbose_name = _("Modify Quotas")
|
||||||
url = "horizon:syspanel:tenants:quotas"
|
url = "horizon:syspanel:projects:quotas"
|
||||||
attrs = {"class": "ajax-modal"}
|
attrs = {"class": "ajax-modal"}
|
||||||
|
|
||||||
|
|
||||||
class ViewMembersLink(tables.LinkAction):
|
class ViewMembersLink(tables.LinkAction):
|
||||||
name = "users"
|
name = "users"
|
||||||
verbose_name = _("Modify Users")
|
verbose_name = _("Modify Users")
|
||||||
url = "horizon:syspanel:tenants:users"
|
url = "horizon:syspanel:projects:users"
|
||||||
|
|
||||||
|
|
||||||
class UsageLink(tables.LinkAction):
|
class UsageLink(tables.LinkAction):
|
||||||
name = "usage"
|
name = "usage"
|
||||||
verbose_name = _("View Usage")
|
verbose_name = _("View Usage")
|
||||||
url = "horizon:syspanel:tenants:usage"
|
url = "horizon:syspanel:projects:usage"
|
||||||
|
|
||||||
|
|
||||||
class EditLink(tables.LinkAction):
|
class EditLink(tables.LinkAction):
|
||||||
name = "update"
|
name = "update"
|
||||||
verbose_name = _("Edit Tenant")
|
verbose_name = _("Edit Project")
|
||||||
url = "horizon:syspanel:tenants:update"
|
url = "horizon:syspanel:projects:update"
|
||||||
attrs = {"class": "ajax-modal"}
|
attrs = {"class": "ajax-modal"}
|
||||||
|
|
||||||
|
|
||||||
class CreateLink(tables.LinkAction):
|
class CreateLink(tables.LinkAction):
|
||||||
name = "create"
|
name = "create"
|
||||||
verbose_name = _("Create New Tenant")
|
verbose_name = _("Create New Project")
|
||||||
url = "horizon:syspanel:tenants:create"
|
url = "horizon:syspanel:projects:create"
|
||||||
attrs = {"class": "ajax-modal btn small"}
|
attrs = {"class": "ajax-modal btn small"}
|
||||||
|
|
||||||
|
|
||||||
class DeleteTenantsAction(tables.DeleteAction):
|
class DeleteTenantsAction(tables.DeleteAction):
|
||||||
data_type_singular = _("Tenant")
|
data_type_singular = _("Project")
|
||||||
data_type_plural = _("Tenants")
|
data_type_plural = _("Projects")
|
||||||
|
|
||||||
def delete(self, request, obj_id):
|
def delete(self, request, obj_id):
|
||||||
api.keystone.tenant_delete(request, obj_id)
|
api.keystone.tenant_delete(request, obj_id)
|
||||||
@ -76,7 +76,7 @@ class TenantsTable(tables.DataTable):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
name = "tenants"
|
name = "tenants"
|
||||||
verbose_name = _("Tenants")
|
verbose_name = _("Projects")
|
||||||
row_actions = (EditLink, UsageLink, ViewMembersLink, ModifyQuotasLink,
|
row_actions = (EditLink, UsageLink, ViewMembersLink, ModifyQuotasLink,
|
||||||
DeleteTenantsAction)
|
DeleteTenantsAction)
|
||||||
table_actions = (TenantFilterAction, CreateLink, DeleteTenantsAction)
|
table_actions = (TenantFilterAction, CreateLink, DeleteTenantsAction)
|
||||||
@ -98,15 +98,15 @@ class RemoveUserAction(tables.BatchAction):
|
|||||||
class TenantUsersTable(UsersTable):
|
class TenantUsersTable(UsersTable):
|
||||||
class Meta:
|
class Meta:
|
||||||
name = "tenant_users"
|
name = "tenant_users"
|
||||||
verbose_name = _("Users For Tenant")
|
verbose_name = _("Users For Project")
|
||||||
table_actions = (RemoveUserAction,)
|
table_actions = (RemoveUserAction,)
|
||||||
row_actions = (RemoveUserAction,)
|
row_actions = (RemoveUserAction,)
|
||||||
|
|
||||||
|
|
||||||
class AddUserAction(tables.LinkAction):
|
class AddUserAction(tables.LinkAction):
|
||||||
name = "add_user"
|
name = "add_user"
|
||||||
verbose_name = _("Add To Tenant")
|
verbose_name = _("Add To Project")
|
||||||
url = "horizon:syspanel:tenants:add_user"
|
url = "horizon:syspanel:projects:add_user"
|
||||||
classes = ('ajax-modal',)
|
classes = ('ajax-modal',)
|
||||||
|
|
||||||
def get_link_url(self, user):
|
def get_link_url(self, user):
|
@ -22,7 +22,7 @@ from horizon import api
|
|||||||
from horizon import test
|
from horizon import test
|
||||||
|
|
||||||
|
|
||||||
INDEX_URL = reverse('horizon:syspanel:tenants:index')
|
INDEX_URL = reverse('horizon:syspanel:projects:index')
|
||||||
|
|
||||||
|
|
||||||
class FakeResource(object):
|
class FakeResource(object):
|
||||||
@ -60,7 +60,7 @@ class TenantsViewTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
res = self.client.get(INDEX_URL)
|
res = self.client.get(INDEX_URL)
|
||||||
|
|
||||||
self.assertTemplateUsed(res, 'syspanel/tenants/index.html')
|
self.assertTemplateUsed(res, 'syspanel/projects/index.html')
|
||||||
self.assertItemsEqual(res.context['table'].data, self.tenants)
|
self.assertItemsEqual(res.context['table'].data, self.tenants)
|
||||||
|
|
||||||
def test_modify_quota(self):
|
def test_modify_quota(self):
|
||||||
@ -78,7 +78,7 @@ class TenantsViewTests(test.BaseAdminViewTests):
|
|||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
url = reverse('horizon:syspanel:tenants:quotas',
|
url = reverse('horizon:syspanel:projects:quotas',
|
||||||
args=(self.TEST_TENANT,))
|
args=(self.TEST_TENANT,))
|
||||||
data = {"method": "UpdateQuotas",
|
data = {"method": "UpdateQuotas",
|
||||||
"tenant_id": self.TEST_TENANT,
|
"tenant_id": self.TEST_TENANT,
|
@ -41,7 +41,7 @@ LOG = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class IndexView(tables.DataTableView):
|
class IndexView(tables.DataTableView):
|
||||||
table_class = TenantsTable
|
table_class = TenantsTable
|
||||||
template_name = 'syspanel/tenants/index.html'
|
template_name = 'syspanel/projects/index.html'
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
tenants = []
|
tenants = []
|
||||||
@ -63,12 +63,12 @@ class IndexView(tables.DataTableView):
|
|||||||
|
|
||||||
class CreateView(forms.ModalFormView):
|
class CreateView(forms.ModalFormView):
|
||||||
form_class = CreateTenant
|
form_class = CreateTenant
|
||||||
template_name = 'syspanel/tenants/create.html'
|
template_name = 'syspanel/projects/create.html'
|
||||||
|
|
||||||
|
|
||||||
class UpdateView(forms.ModalFormView):
|
class UpdateView(forms.ModalFormView):
|
||||||
form_class = UpdateTenant
|
form_class = UpdateTenant
|
||||||
template_name = 'syspanel/tenants/update.html'
|
template_name = 'syspanel/projects/update.html'
|
||||||
context_object_name = 'tenant'
|
context_object_name = 'tenant'
|
||||||
|
|
||||||
def get_object(self, *args, **kwargs):
|
def get_object(self, *args, **kwargs):
|
||||||
@ -79,7 +79,7 @@ class UpdateView(forms.ModalFormView):
|
|||||||
LOG.exception('Error fetching tenant with id "%s"' % tenant_id)
|
LOG.exception('Error fetching tenant with id "%s"' % tenant_id)
|
||||||
messages.error(self.request, _('Unable to update tenant: %s')
|
messages.error(self.request, _('Unable to update tenant: %s')
|
||||||
% e.message)
|
% e.message)
|
||||||
raise http.Http404("Tenant with ID %s not found." % tenant_id)
|
raise http.Http404("Project with ID %s not found." % tenant_id)
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
return {'id': self.object.id,
|
return {'id': self.object.id,
|
||||||
@ -90,7 +90,7 @@ class UpdateView(forms.ModalFormView):
|
|||||||
|
|
||||||
class UsersView(tables.MultiTableView):
|
class UsersView(tables.MultiTableView):
|
||||||
table_classes = (TenantUsersTable, AddUsersTable)
|
table_classes = (TenantUsersTable, AddUsersTable)
|
||||||
template_name = 'syspanel/tenants/users.html'
|
template_name = 'syspanel/projects/users.html'
|
||||||
|
|
||||||
def get_data(self, *args, **kwargs):
|
def get_data(self, *args, **kwargs):
|
||||||
tenant_id = self.kwargs["tenant_id"]
|
tenant_id = self.kwargs["tenant_id"]
|
||||||
@ -99,7 +99,7 @@ class UsersView(tables.MultiTableView):
|
|||||||
self.all_users = api.keystone.user_list(self.request)
|
self.all_users = api.keystone.user_list(self.request)
|
||||||
self.tenant_users = api.keystone.user_list(self.request, tenant_id)
|
self.tenant_users = api.keystone.user_list(self.request, tenant_id)
|
||||||
except:
|
except:
|
||||||
redirect = reverse("horizon:syspanel:tenants:index")
|
redirect = reverse("horizon:syspanel:projects:index")
|
||||||
exceptions.handle(self.request,
|
exceptions.handle(self.request,
|
||||||
_("Unable to retrieve users."),
|
_("Unable to retrieve users."),
|
||||||
redirect=redirect)
|
redirect=redirect)
|
||||||
@ -121,7 +121,7 @@ class UsersView(tables.MultiTableView):
|
|||||||
|
|
||||||
class AddUserView(forms.ModalFormView):
|
class AddUserView(forms.ModalFormView):
|
||||||
form_class = AddUser
|
form_class = AddUser
|
||||||
template_name = 'syspanel/tenants/add_user.html'
|
template_name = 'syspanel/projects/add_user.html'
|
||||||
context_object_name = 'tenant'
|
context_object_name = 'tenant'
|
||||||
|
|
||||||
def get_object(self, *args, **kwargs):
|
def get_object(self, *args, **kwargs):
|
||||||
@ -138,7 +138,7 @@ class AddUserView(forms.ModalFormView):
|
|||||||
try:
|
try:
|
||||||
roles = api.keystone.role_list(self.request)
|
roles = api.keystone.role_list(self.request)
|
||||||
except:
|
except:
|
||||||
redirect = reverse("horizon:syspanel:tenants:users",
|
redirect = reverse("horizon:syspanel:projects:users",
|
||||||
args=(self.kwargs["tenant_id"],))
|
args=(self.kwargs["tenant_id"],))
|
||||||
exceptions.handle(self.request,
|
exceptions.handle(self.request,
|
||||||
_("Unable to retrieve roles."),
|
_("Unable to retrieve roles."),
|
||||||
@ -156,7 +156,7 @@ class AddUserView(forms.ModalFormView):
|
|||||||
|
|
||||||
class QuotasView(forms.ModalFormView):
|
class QuotasView(forms.ModalFormView):
|
||||||
form_class = UpdateQuotas
|
form_class = UpdateQuotas
|
||||||
template_name = 'syspanel/tenants/quotas.html'
|
template_name = 'syspanel/projects/quotas.html'
|
||||||
context_object_name = 'tenant'
|
context_object_name = 'tenant'
|
||||||
|
|
||||||
def get_object(self, *args, **kwargs):
|
def get_object(self, *args, **kwargs):
|
||||||
@ -181,7 +181,7 @@ class QuotasView(forms.ModalFormView):
|
|||||||
class TenantUsageView(usage.UsageView):
|
class TenantUsageView(usage.UsageView):
|
||||||
table_class = usage.TenantUsageTable
|
table_class = usage.TenantUsageTable
|
||||||
usage_class = usage.TenantUsage
|
usage_class = usage.TenantUsage
|
||||||
template_name = 'syspanel/tenants/usage.html'
|
template_name = 'syspanel/projects/usage.html'
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
super(TenantUsageView, self).get_data()
|
super(TenantUsageView, self).get_data()
|
@ -2,10 +2,10 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block form_id %}add_user_form{% endblock %}
|
{% block form_id %}add_user_form{% endblock %}
|
||||||
{% block form_action %}{% url horizon:syspanel:tenants:add_user tenant_id user_id %}{% endblock %}
|
{% block form_action %}{% url horizon:syspanel:projects:add_user tenant_id user_id %}{% endblock %}
|
||||||
|
|
||||||
{% block modal_id %}add_user_modal{% endblock %}
|
{% block modal_id %}add_user_modal{% endblock %}
|
||||||
{% block modal-header %}{% trans "Add User To Tenant" %}{% endblock %}
|
{% block modal-header %}{% trans "Add User To Project" %}{% endblock %}
|
||||||
|
|
||||||
{% block modal-body %}
|
{% block modal-body %}
|
||||||
<div class="left">
|
<div class="left">
|
||||||
@ -15,11 +15,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<h3>{% trans "Description" %}:</h3>
|
<h3>{% trans "Description" %}:</h3>
|
||||||
<p>{% trans "Select the user role for the tenant." %}</p>
|
<p>{% trans "Select the user role for the project." %}</p>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block modal-footer %}
|
{% block modal-footer %}
|
||||||
<input class="btn primary pull-right" type="submit" value="{% trans "Add" %}" />
|
<input class="btn primary pull-right" type="submit" value="{% trans "Add" %}" />
|
||||||
<a href="{% url horizon:syspanel:tenants:users tenant_id %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
<a href="{% url horizon:syspanel:projects:users tenant_id %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -2,10 +2,10 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block form_id %}create_tenant_form{% endblock %}
|
{% block form_id %}create_tenant_form{% endblock %}
|
||||||
{% block form_action %}{% url horizon:syspanel:tenants:create %}{% endblock %}
|
{% block form_action %}{% url horizon:syspanel:projects:create %}{% endblock %}
|
||||||
|
|
||||||
{% block modal_id %}create_tenant_modal{% endblock %}
|
{% block modal_id %}create_tenant_modal{% endblock %}
|
||||||
{% block modal-header %}{% trans "Create Tenant" %}{% endblock %}
|
{% block modal-header %}{% trans "Create Project" %}{% endblock %}
|
||||||
|
|
||||||
{% block modal-body %}
|
{% block modal-body %}
|
||||||
<div class="left">
|
<div class="left">
|
||||||
@ -15,11 +15,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<h3>{% trans "Description" %}:</h3>
|
<h3>{% trans "Description" %}:</h3>
|
||||||
<p>{% trans "From here you can create a new tenant (aka project) to organize users." %}</p>
|
<p>{% trans "From here you can create a new project to organize users." %}</p>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block modal-footer %}
|
{% block modal-footer %}
|
||||||
<input class="btn primary pull-right" type="submit" value="{% trans "Create Tenant" %}" />
|
<input class="btn primary pull-right" type="submit" value="{% trans "Create Project" %}" />
|
||||||
<a href="{% url horizon:syspanel:tenants:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
<a href="{% url horizon:syspanel:projects:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -2,7 +2,7 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block form_id %}quota_update_form{% endblock %}
|
{% block form_id %}quota_update_form{% endblock %}
|
||||||
{% block form_action %}{% url horizon:syspanel:tenants:quotas tenant.id %}{% endblock %}
|
{% block form_action %}{% url horizon:syspanel:projects:quotas tenant.id %}{% endblock %}
|
||||||
|
|
||||||
{% block modal-header %}{% trans "Update Quota" %}{% endblock %}
|
{% block modal-header %}{% trans "Update Quota" %}{% endblock %}
|
||||||
|
|
||||||
@ -14,11 +14,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<h3>{% trans "Description" %}:</h3>
|
<h3>{% trans "Description" %}:</h3>
|
||||||
<p>{% blocktrans with tenant_id=tenant.id %}From here you can edit quotas (max limits) for the tenant {{ tenant_id }}.{% endblocktrans %}</p>
|
<p>{% blocktrans with tenant_id=tenant.id %}From here you can edit quotas (max limits) for the project {{ tenant.name }}.{% endblocktrans %}</p>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block modal-footer %}
|
{% block modal-footer %}
|
||||||
<input class="btn primary pull-right" type="submit" value="{% trans "Update Quota" %}" />
|
<input class="btn primary pull-right" type="submit" value="{% trans "Update Quota" %}" />
|
||||||
<a href="{% url horizon:syspanel:tenants:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
<a href="{% url horizon:syspanel:projects:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -2,10 +2,10 @@
|
|||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block form_id %}{% endblock %}
|
{% block form_id %}{% endblock %}
|
||||||
{% block form_action %}{% url horizon:syspanel:tenants:update tenant.id %}{% endblock %}
|
{% block form_action %}{% url horizon:syspanel:projects:update tenant.id %}{% endblock %}
|
||||||
|
|
||||||
{% block modal_id %}update_tenant_modal{% endblock %}
|
{% block modal_id %}update_tenant_modal{% endblock %}
|
||||||
{% block modal-header %}{% trans "Update Tenant" %}{% endblock %}
|
{% block modal-header %}{% trans "Update Project" %}{% endblock %}
|
||||||
|
|
||||||
{% block modal-body %}
|
{% block modal-body %}
|
||||||
<div class="left">
|
<div class="left">
|
||||||
@ -15,11 +15,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<h3>{% trans "Description" %}:</h3>
|
<h3>{% trans "Description" %}:</h3>
|
||||||
<p>{% trans "From here you can edit a tenant." %}</p>
|
<p>{% trans "From here you can edit a project." %}</p>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block modal-footer %}
|
{% block modal-footer %}
|
||||||
<input class="btn primary pull-right" type="submit" value="{% trans "Update Tenant" %}" />
|
<input class="btn primary pull-right" type="submit" value="{% trans "Update Project" %}" />
|
||||||
<a href="{% url horizon:syspanel:tenants:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
<a href="{% url horizon:syspanel:projects:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -1,11 +1,11 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'syspanel/base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "Add User To Tenant" %}{% endblock %}
|
{% block title %}{% trans "Add User To Project" %}{% endblock %}
|
||||||
|
|
||||||
{% block page_header %}
|
{% block page_header %}
|
||||||
{% include "horizon/common/_page_header.html" with title=_("Add User To Tenant") %}
|
{% include "horizon/common/_page_header.html" with title=_("Add User To Project") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block syspanel_main %}
|
||||||
{% include 'syspanel/tenants/_add_user.html' %}
|
{% include 'syspanel/projects/_add_user.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -1,11 +1,11 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'syspanel/base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Create Tenant{% endblock %}
|
{% block title %}Create Project{% endblock %}
|
||||||
|
|
||||||
{% block page_header %}
|
{% block page_header %}
|
||||||
{% include "horizon/common/_page_header.html" with title=_("Create Tenant") %}
|
{% include "horizon/common/_page_header.html" with title=_("Create Project") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block syspanel_main %}
|
||||||
{% include 'syspanel/tenants/_create.html' %}
|
{% include 'syspanel/projects/_create.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -1,11 +1,11 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'syspanel/base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Tenants{% endblock %}
|
{% block title %}Projects{% endblock %}
|
||||||
|
|
||||||
{% block page_header %}
|
{% block page_header %}
|
||||||
{% url horizon:syspanel:tenants:index as refresh_link %}
|
{% url horizon:syspanel:projects:index as refresh_link %}
|
||||||
{# to make searchable false, just remove it from the include statement #}
|
{# to make searchable false, just remove it from the include statement #}
|
||||||
{% include "horizon/common/_page_header.html" with title=_("Tenants") refresh_link=refresh_link searchable="true" %}
|
{% include "horizon/common/_page_header.html" with title=_("Projects") refresh_link=refresh_link searchable="true" %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block syspanel_main %}
|
@ -1,11 +1,11 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'syspanel/base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Modify Tenant Quotas{% endblock %}
|
{% block title %}Modify Project Quotas{% endblock %}
|
||||||
|
|
||||||
{% block page_header %}
|
{% block page_header %}
|
||||||
{% include "horizon/common/_page_header.html" with title=_("Update Tenant") %}
|
{% include "horizon/common/_page_header.html" with title=_("Update Project") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block syspanel_main %}
|
||||||
{% include 'syspanel/tenants/_quotas.html' with form=form %}
|
{% include 'syspanel/projects/_quotas.html' with form=form %}
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -1,11 +1,11 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'syspanel/base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Update Tenant{% endblock %}
|
{% block title %}Update Project{% endblock %}
|
||||||
|
|
||||||
{% block page_header %}
|
{% block page_header %}
|
||||||
{% include "horizon/common/_page_header.html" with title=_("Update Tenant") %}
|
{% include "horizon/common/_page_header.html" with title=_("Update Project") %}
|
||||||
{% endblock page_header %}
|
{% endblock page_header %}
|
||||||
|
|
||||||
{% block syspanel_main %}
|
{% block syspanel_main %}
|
||||||
{% include 'syspanel/tenants/_update.html' %}
|
{% include 'syspanel/projects/_update.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
Can't render this file because it contains an unexpected character in line 1 and column 46.
|
@ -0,0 +1,14 @@
|
|||||||
|
{% extends 'syspanel/base.html' %}
|
||||||
|
{% load i18n sizeformat %}
|
||||||
|
{% block title %}{% trans "Project Usage Overview" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block page_header %}
|
||||||
|
<div class='page-header'>
|
||||||
|
<h2>{% trans "Project Usage" %}</h2>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block syspanel_main %}
|
||||||
|
{% include "horizon/common/_usage_summary.html" %}
|
||||||
|
{{ table.render }}
|
||||||
|
{% endblock %}
|
@ -1,10 +1,10 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
{% extends 'syspanel/base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}Tenant Users{% endblock %}
|
{% block title %}Project Users{% endblock %}
|
||||||
|
|
||||||
{% block page_header %}
|
{% block page_header %}
|
||||||
<div class='page-header'>
|
<div class='page-header'>
|
||||||
<h2>{% trans "Users for Tenant" %}: <span>{{ tenant.name }}</span></h2>
|
<h2>{% trans "Users for Project" %}: <span>{{ tenant.name }}</span></h2>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
@ -1,9 +0,0 @@
|
|||||||
{% load i18n %}
|
|
||||||
<form id="form_tenant_delete{{ tenant.id }}" class="form-tenant-delete" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
{% for hidden in form.hidden_fields %}
|
|
||||||
{{ hidden }}
|
|
||||||
{% endfor %}
|
|
||||||
<input name="tenant_id" type="hidden" value="{{ tenant.id }}" />
|
|
||||||
<input id="delete_{{ tenant.id }}" class="btn small delete" title="Tenant: {{ tenant.id }}" type="submit" value="{% trans "Delete" %}" />
|
|
||||||
</form>
|
|
@ -1,8 +0,0 @@
|
|||||||
{% extends 'syspanel/base.html' %}
|
|
||||||
{% load i18n sizeformat %}
|
|
||||||
{% block title %}{% trans "Tenant Usage Overview" %}{% endblock %}
|
|
||||||
|
|
||||||
{% block syspanel_main %}
|
|
||||||
{% include "horizon/common/_usage_summary.html" %}
|
|
||||||
{{ table.render }}
|
|
||||||
{% endblock %}
|
|
@ -14,7 +14,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<h3>{% trans "Description" %}:</h3>
|
<h3>{% trans "Description" %}:</h3>
|
||||||
<p>{% trans "From here you can create a new user and assign them to a tenant (aka project)." %}</p>
|
<p>{% trans "From here you can create a new user and assign them to a project." %}</p>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% extends "horizon/common/_modal_form.html" %}
|
{% extends "horizon/common/_modal_form.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block form_id %}update_tenant_form{% endblock %}
|
{% block form_id %}update_user_form{% endblock %}
|
||||||
{% block form_action %}{% url horizon:syspanel:users:update user.id %}{% endblock %}
|
{% block form_action %}{% url horizon:syspanel:users:update user.id %}{% endblock %}
|
||||||
|
|
||||||
{% block modal-header %}{% trans "Update User" %}{% endblock %}
|
{% block modal-header %}{% trans "Update User" %}{% endblock %}
|
||||||
@ -14,7 +14,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<h3>{% trans "Description" %}:</h3>
|
<h3>{% trans "Description" %}:</h3>
|
||||||
<p>{% trans "From here you can edit users by changing their usernames, emails, passwords, and tenants." %}</p>
|
<p>{% trans "From here you can edit the user by changing their username, email, password, and default project." %}</p>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ class CreateUserForm(BaseUserForm):
|
|||||||
password = forms.CharField(label=_("Password"),
|
password = forms.CharField(label=_("Password"),
|
||||||
widget=forms.PasswordInput(render_value=False),
|
widget=forms.PasswordInput(render_value=False),
|
||||||
required=False)
|
required=False)
|
||||||
tenant_id = forms.ChoiceField(label=_("Primary Tenant"))
|
tenant_id = forms.ChoiceField(label=_("Primary Project"))
|
||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
try:
|
try:
|
||||||
@ -92,7 +92,7 @@ class UpdateUserForm(BaseUserForm):
|
|||||||
password = forms.CharField(label=_("Password"),
|
password = forms.CharField(label=_("Password"),
|
||||||
widget=forms.PasswordInput(render_value=False),
|
widget=forms.PasswordInput(render_value=False),
|
||||||
required=False)
|
required=False)
|
||||||
tenant_id = forms.ChoiceField(label=_("Primary Tenant"))
|
tenant_id = forms.ChoiceField(label=_("Primary Project"))
|
||||||
|
|
||||||
def handle(self, request, data):
|
def handle(self, request, data):
|
||||||
updated = []
|
updated = []
|
||||||
|
@ -128,7 +128,7 @@ class UsersTable(tables.DataTable):
|
|||||||
email = tables.Column(_('email'))
|
email = tables.Column(_('email'))
|
||||||
# Default tenant is not returned from Keystone currently.
|
# Default tenant is not returned from Keystone currently.
|
||||||
#default_tenant = tables.Column(_('default_tenant'),
|
#default_tenant = tables.Column(_('default_tenant'),
|
||||||
# verbose_name="Default Tenant")
|
# verbose_name="Default Project")
|
||||||
enabled = tables.Column(_('enabled'),
|
enabled = tables.Column(_('enabled'),
|
||||||
status=True,
|
status=True,
|
||||||
status_choices=STATUS_CHOICES)
|
status_choices=STATUS_CHOICES)
|
||||||
|
@ -104,7 +104,7 @@ class HorizonTests(test.TestCase):
|
|||||||
'<Panel: Services>',
|
'<Panel: Services>',
|
||||||
'<Panel: Flavors>',
|
'<Panel: Flavors>',
|
||||||
'<Panel: Images>',
|
'<Panel: Images>',
|
||||||
'<Panel: Tenants>',
|
'<Panel: Projects>',
|
||||||
'<Panel: Users>',
|
'<Panel: Users>',
|
||||||
'<Panel: Quotas>'])
|
'<Panel: Quotas>'])
|
||||||
self.assertEqual(syspanel.get_absolute_url(), "/syspanel/")
|
self.assertEqual(syspanel.get_absolute_url(), "/syspanel/")
|
||||||
@ -120,7 +120,7 @@ class HorizonTests(test.TestCase):
|
|||||||
settings_dash.register(MyPanel)
|
settings_dash.register(MyPanel)
|
||||||
self.assertQuerysetEqual(settings_dash.get_panels(),
|
self.assertQuerysetEqual(settings_dash.get_panels(),
|
||||||
['<Panel: User Settings>',
|
['<Panel: User Settings>',
|
||||||
'<Panel: Tenant Settings>',
|
'<Panel: Project Settings>',
|
||||||
'<Panel: My Panel>'])
|
'<Panel: My Panel>'])
|
||||||
|
|
||||||
def test_panels(self):
|
def test_panels(self):
|
||||||
|
@ -23,7 +23,7 @@ class BaseUsageTable(tables.DataTable):
|
|||||||
|
|
||||||
|
|
||||||
class GlobalUsageTable(BaseUsageTable):
|
class GlobalUsageTable(BaseUsageTable):
|
||||||
tenant = tables.Column('tenant_id', verbose_name=_("Tenant ID"))
|
tenant = tables.Column('tenant_id', verbose_name=_("Project ID"))
|
||||||
disk_hours = tables.Column('disk_gb_hours',
|
disk_hours = tables.Column('disk_gb_hours',
|
||||||
verbose_name=_("Disk GB Hours"))
|
verbose_name=_("Disk GB Hours"))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user