diff --git a/doc/source/topics/settings.rst b/doc/source/topics/settings.rst index 0cb59eb8ca..2051583f99 100644 --- a/doc/source/topics/settings.rst +++ b/doc/source/topics/settings.rst @@ -369,6 +369,25 @@ Default: ``"1800"`` Specifies the timespan in seconds inactivity, until a user is considered as logged out. +``FLAVOR_EXTRA_KEYS`` +--------------------------- + +Default:: + + { + 'flavor_keys': [ + ('quota:read_bytes_sec', _('Quota: Read bytes')), + ('quota:write_bytes_sec', _('Quota: Write bytes')), + ('quota:cpu_quota', _('Quota: CPU')), + ('quota:cpu_period', _('Quota: CPU period')), + ('quota:inbound_average', _('Quota: Inbound average')), + ('quota:outbound_average', _('Quota: Outbound average')) + ] + } + +Used to customize flavor extra specs keys + + Django Settings (Partial) ========================= diff --git a/openstack_dashboard/dashboards/admin/flavors/extras/forms.py b/openstack_dashboard/dashboards/admin/flavors/extras/forms.py index 376588c09e..73089dfb15 100644 --- a/openstack_dashboard/dashboards/admin/flavors/extras/forms.py +++ b/openstack_dashboard/dashboards/admin/flavors/extras/forms.py @@ -18,6 +18,8 @@ # License for the specific language governing permissions and limitations # under the License. +from django.conf import settings +from django.forms import ValidationError # noqa from django.utils.translation import ugettext_lazy as _ from openstack_dashboard import api @@ -31,17 +33,43 @@ import re class CreateExtraSpec(forms.SelfHandlingForm): _extraspec_name_regex = re.compile(r"^[\w\.\-: ]+$", re.UNICODE) + keys = forms.ChoiceField(label=_("Keys"), + widget=forms.Select(attrs={ + 'class': 'switchable', + 'data-slug': 'keys'})) key = forms.RegexField( max_length="255", label=_("Key"), + required=False, regex=_extraspec_name_regex, error_messages={'invalid': _('Key Name may only contain letters, ' 'numbers, underscores, periods, colons, ' - 'spaces and hyphens.')}) + 'spaces and hyphens.')}, + widget=forms.TextInput(attrs={ + 'class': 'switched', + 'data-switch-on': 'keys', + 'data-keys-custom': _('Key')})) value = forms.CharField(max_length="255", label=_("Value")) flavor_id = forms.CharField(widget=forms.widgets.HiddenInput) + def __init__(self, *args, **kwargs): + super(CreateExtraSpec, self).__init__(*args, **kwargs) + key_settings = getattr(settings, 'FLAVOR_EXTRA_KEYS', {}) + key_list = key_settings.get('flavor_keys', []) + self.fields['keys'].choices = key_list + [('custom', _('Other Key'))] + + def clean(self): + cleaned_data = super(CreateExtraSpec, self).clean() + keys = cleaned_data.get('keys', None) + key = cleaned_data.get('key', None) + if keys == 'custom' and key == "": + msg = _('This field is required.') + self._errors["key"] = self.error_class([msg]) + return cleaned_data + def handle(self, request, data): + if data["keys"] != 'custom': + data['key'] = data['keys'] try: api.nova.flavor_extra_set(request, data['flavor_id'], diff --git a/openstack_dashboard/dashboards/admin/flavors/extras/tests.py b/openstack_dashboard/dashboards/admin/flavors/extras/tests.py index f897eb6274..1116ef0fc0 100644 --- a/openstack_dashboard/dashboards/admin/flavors/extras/tests.py +++ b/openstack_dashboard/dashboards/admin/flavors/extras/tests.py @@ -52,6 +52,7 @@ class FlavorExtrasTests(test.BaseAdminViewTests): self.mox.ReplayAll() data = {'flavor_id': flavor.id, + 'keys': 'custom', 'key': key_name, 'value': 'v1'} resp = self.client.post(create_url, data) @@ -59,6 +60,27 @@ class FlavorExtrasTests(test.BaseAdminViewTests): self.assertRedirectsNoFollow(resp, index_url) self.mox.UnsetStubs() + @test.create_stubs({api.nova: ('flavor_extra_set', ), }) + def test_extra_create_with_template(self): + flavor = self.flavors.first() + create_url = reverse('horizon:admin:flavors:extras:create', + args=[flavor.id]) + index_url = reverse('horizon:admin:flavors:extras:index', + args=[flavor.id]) + + # GET to display the flavor_name + api.nova.flavor_extra_set(IsA(http.HttpRequest), + flavor.id, + {'quota:read_bytes_sec': '1000'}) + self.mox.ReplayAll() + + data = {'flavor_id': flavor.id, + 'keys': 'quota:read_bytes_sec', + 'value': '1000'} + resp = self.client.post(create_url, data) + self.assertNoFormErrors(resp) + self.assertRedirectsNoFollow(resp, index_url) + @test.create_stubs({api.nova: ('flavor_get', ), }) def test_extra_create_get(self): flavor = self.flavors.first() @@ -83,6 +105,7 @@ class FlavorExtrasTests(test.BaseAdminViewTests): self.mox.ReplayAll() data = {'flavor_id': flavor.id, + 'keys': 'custom', 'key': key_name, 'value': 'v1'} diff --git a/openstack_dashboard/dashboards/admin/flavors/extras/views.py b/openstack_dashboard/dashboards/admin/flavors/extras/views.py index b77d583078..7a80a7dfa8 100644 --- a/openstack_dashboard/dashboards/admin/flavors/extras/views.py +++ b/openstack_dashboard/dashboards/admin/flavors/extras/views.py @@ -79,6 +79,11 @@ class CreateView(ExtraSpecMixin, forms.ModalFormView): class EditView(ExtraSpecMixin, forms.ModalFormView): form_class = project_forms.EditExtraSpec template_name = 'admin/flavors/extras/edit.html' + success_url = 'horizon:admin:flavors:extras:index' + + def get_success_url(self): + return reverse(self.success_url, + args=(self.kwargs['id'],)) def get_initial(self): flavor_id = self.kwargs['id'] diff --git a/openstack_dashboard/dashboards/admin/flavors/templates/flavors/extras/_edit.html b/openstack_dashboard/dashboards/admin/flavors/templates/flavors/extras/_edit.html index 50ab38b67a..3468f0a2d4 100644 --- a/openstack_dashboard/dashboards/admin/flavors/templates/flavors/extras/_edit.html +++ b/openstack_dashboard/dashboards/admin/flavors/templates/flavors/extras/_edit.html @@ -3,7 +3,7 @@ {% load url from future %} {% block form_id %}extra_spec_edit_form{% endblock %} -{% block form_action %}{% url 'horizon:admin:flavors:extras:create' flavor.id %}{% endblock %} +{% block form_action %}{% url 'horizon:admin:flavors:extras:edit' flavor.id key %}{% endblock %} {% block modal_id %}extra_spec_edit_modal{% endblock %} diff --git a/openstack_dashboard/local/local_settings.py.example b/openstack_dashboard/local/local_settings.py.example index a9c96c182e..50af3e811b 100644 --- a/openstack_dashboard/local/local_settings.py.example +++ b/openstack_dashboard/local/local_settings.py.example @@ -470,3 +470,14 @@ SECURITY_GROUP_RULES = { 'to_port': '3389', }, } + +FLAVOR_EXTRA_KEYS = { + 'flavor_keys': [ + ('quota:read_bytes_sec', _('Quota: Read bytes')), + ('quota:write_bytes_sec', _('Quota: Write bytes')), + ('quota:cpu_quota', _('Quota: CPU')), + ('quota:cpu_period', _('Quota: CPU period')), + ('quota:inbound_average', _('Quota: Inbound average')), + ('quota:outbound_average', _('Quota: Outbound average')), + ] +} diff --git a/openstack_dashboard/test/settings.py b/openstack_dashboard/test/settings.py index 04b2c43a1c..2914051061 100644 --- a/openstack_dashboard/test/settings.py +++ b/openstack_dashboard/test/settings.py @@ -12,6 +12,8 @@ import os +from django.utils.translation import ugettext_lazy as _ + from horizon.test.settings import * # noqa from horizon.utils import secret_key @@ -201,3 +203,14 @@ POLICY_FILES = { 'identity': 'keystone_policy.json', 'compute': 'nova_policy.json' } + +FLAVOR_EXTRA_KEYS = { + 'flavor_keys': [ + ('quota:read_bytes_sec', _('Quota: Read bytes')), + ('quota:write_bytes_sec', _('Quota: Write bytes')), + ('quota:cpu_quota', _('Quota: CPU')), + ('quota:cpu_period', _('Quota: CPU period')), + ('quota:inbound_average', _('Quota: Inbound average')), + ('quota:outbound_average', _('Quota: Outbound average')), + ] +}