diff --git a/openstack_dashboard/api/network.py b/openstack_dashboard/api/network.py index b4ed3c35253..6f0fe330060 100644 --- a/openstack_dashboard/api/network.py +++ b/openstack_dashboard/api/network.py @@ -103,6 +103,10 @@ def security_group_delete(request, sg_id): return NetworkClient(request).secgroups.delete(sg_id) +def security_group_update(request, sg_id, name, desc): + return NetworkClient(request).secgroups.update(sg_id, name, desc) + + def security_group_rule_create(request, parent_group_id, direction, ethertype, ip_protocol, from_port, to_port, diff --git a/openstack_dashboard/api/neutron.py b/openstack_dashboard/api/neutron.py index cb0857aba67..81121f7d0b5 100644 --- a/openstack_dashboard/api/neutron.py +++ b/openstack_dashboard/api/neutron.py @@ -213,6 +213,12 @@ def create(self, name, desc): secgroup = self.client.create_security_group(body) return SecurityGroup(secgroup.get('security_group')) + def update(self, sg_id, name, desc): + body = {'security_group': {'name': name, + 'description': desc}} + secgroup = self.client.update_security_group(sg_id, body) + return SecurityGroup(secgroup.get('security_group')) + def delete(self, sg_id): self.client.delete_security_group(sg_id) diff --git a/openstack_dashboard/api/nova.py b/openstack_dashboard/api/nova.py index dad8d016fc2..2a99e029031 100644 --- a/openstack_dashboard/api/nova.py +++ b/openstack_dashboard/api/nova.py @@ -199,6 +199,10 @@ def get(self, sg_id): def create(self, name, desc): return SecurityGroup(self.client.security_groups.create(name, desc)) + def update(self, sg_id, name, desc): + return SecurityGroup(self.client.security_groups.update(sg_id, + name, desc)) + def delete(self, security_group_id): self.client.security_groups.delete(security_group_id) diff --git a/openstack_dashboard/dashboards/project/access_and_security/security_groups/forms.py b/openstack_dashboard/dashboards/project/access_and_security/security_groups/forms.py index b3c7c4da06f..a44e43fcda2 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/security_groups/forms.py +++ b/openstack_dashboard/dashboards/project/access_and_security/security_groups/forms.py @@ -66,6 +66,33 @@ def handle(self, request, data): redirect=redirect) +class UpdateGroup(forms.SelfHandlingForm): + id = forms.CharField(widget=forms.HiddenInput()) + name = forms.CharField(label=_("Name"), + error_messages={ + 'required': _('This field is required.'), + 'invalid': _("The string may only contain" + " ASCII characters and numbers.")}, + validators=[validators.validate_slug]) + description = forms.CharField(label=_("Description")) + + def handle(self, request, data): + try: + sg = api.network.security_group_update(request, + data['id'], + data['name'], + data['description']) + messages.success(request, + _('Successfully updated security group: %s') + % data['name']) + return sg + except Exception: + redirect = reverse("horizon:project:access_and_security:index") + exceptions.handle(request, + _('Unable to update security group.'), + redirect=redirect) + + class AddRule(forms.SelfHandlingForm): id = forms.CharField(widget=forms.HiddenInput()) rule_menu = forms.ChoiceField(label=_('Rule'), diff --git a/openstack_dashboard/dashboards/project/access_and_security/security_groups/tables.py b/openstack_dashboard/dashboards/project/access_and_security/security_groups/tables.py index 6295504592c..68f2021a7fc 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/security_groups/tables.py +++ b/openstack_dashboard/dashboards/project/access_and_security/security_groups/tables.py @@ -49,6 +49,18 @@ class CreateGroup(tables.LinkAction): classes = ("ajax-modal", "btn-create") +class EditGroup(tables.LinkAction): + name = "edit" + verbose_name = _("Edit Security Group") + url = "horizon:project:access_and_security:security_groups:update" + classes = ("ajax-modal", "btn-edit") + + def allowed(self, request, security_group=None): + if not security_group: + return True + return security_group.name != 'default' + + class EditRules(tables.LinkAction): name = "edit_rules" verbose_name = _("Edit Rules") @@ -67,7 +79,7 @@ class Meta: name = "security_groups" verbose_name = _("Security Groups") table_actions = (CreateGroup, DeleteGroup) - row_actions = (EditRules, DeleteGroup) + row_actions = (EditRules, EditGroup, DeleteGroup) class CreateRule(tables.LinkAction): diff --git a/openstack_dashboard/dashboards/project/access_and_security/security_groups/tests.py b/openstack_dashboard/dashboards/project/access_and_security/security_groups/tests.py index 1b14d9a6e4b..a6ed5657028 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/security_groups/tests.py +++ b/openstack_dashboard/dashboards/project/access_and_security/security_groups/tests.py @@ -55,6 +55,45 @@ def setUp(self): 'security_groups:add_rule', args=[sec_group.id]) + @test.create_stubs({api.network: ('security_group_get',)}) + def test_update_security_groups_get(self): + sec_group = self.security_groups.first() + api.network.security_group_get(IsA(http.HttpRequest), + sec_group.id).AndReturn(sec_group) + self.mox.ReplayAll() + + res = self.client.get(reverse('horizon:project:access_and_security:' + 'security_groups:update', + args=[sec_group.id])) + self.assertTemplateUsed(res, + 'project/access_and_security/security_groups/_update.html') + self.assertEqual(res.context['security_group'].name, + sec_group.name) + + @test.create_stubs({api.network: ('security_group_update', + 'security_group_get')}) + def test_update_security_groups_post(self): + sec_group = self.security_groups.get(name="other_group") + api.network.security_group_update(IsA(http.HttpRequest), + str(sec_group.id), + sec_group.name, + sec_group.description) \ + .AndReturn(sec_group) + api.network.security_group_get(IsA(http.HttpRequest), + sec_group.id).AndReturn(sec_group) + self.mox.ReplayAll() + + formData = {'method': 'UpdateGroup', + 'id': sec_group.id, + 'name': sec_group.name, + 'description': sec_group.description} + + update_url = reverse('horizon:project:access_and_security:' + 'security_groups:update', + args=[sec_group.id]) + res = self.client.post(update_url, formData) + self.assertRedirectsNoFollow(res, INDEX_URL) + def test_create_security_groups_get(self): res = self.client.get(SG_CREATE_URL) self.assertTemplateUsed(res, diff --git a/openstack_dashboard/dashboards/project/access_and_security/security_groups/urls.py b/openstack_dashboard/dashboards/project/access_and_security/security_groups/urls.py index 091be07e5e6..a963b4a2595 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/security_groups/urls.py +++ b/openstack_dashboard/dashboards/project/access_and_security/security_groups/urls.py @@ -32,5 +32,8 @@ name='detail'), url(r'^(?P[^/]+)/add_rule/$', views.AddRuleView.as_view(), - name='add_rule') + name='add_rule'), + url(r'^(?P[^/]+)/update/$', + views.UpdateView.as_view(), + name='update') ) diff --git a/openstack_dashboard/dashboards/project/access_and_security/security_groups/views.py b/openstack_dashboard/dashboards/project/access_and_security/security_groups/views.py index 1cb74f282f4..739b9df3926 100644 --- a/openstack_dashboard/dashboards/project/access_and_security/security_groups/views.py +++ b/openstack_dashboard/dashboards/project/access_and_security/security_groups/views.py @@ -68,6 +68,35 @@ def get_context_data(self, **kwargs): return context +class UpdateView(forms.ModalFormView): + form_class = project_forms.UpdateGroup + template_name = 'project/access_and_security/security_groups/update.html' + success_url = reverse_lazy('horizon:project:access_and_security:index') + + def get_object(self): + if not hasattr(self, "_object"): + sg_id = filters.get_int_or_uuid(self.kwargs['security_group_id']) + try: + self._object = api.network.security_group_get(self.request, + sg_id) + except Exception: + msg = _('Unable to retrieve security group.') + url = reverse('horizon:project:access_and_security:index') + exceptions.handle(self.request, msg, redirect=url) + return self._object + + def get_context_data(self, **kwargs): + context = super(UpdateView, self).get_context_data(**kwargs) + context["security_group"] = self.get_object() + return context + + def get_initial(self): + security_group = self.get_object() + return {'id': self.kwargs['security_group_id'], + 'name': security_group.name, + 'description': security_group.description} + + class AddRuleView(forms.ModalFormView): form_class = project_forms.AddRule template_name = 'project/access_and_security/security_groups/add_rule.html' diff --git a/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/security_groups/_update.html b/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/security_groups/_update.html new file mode 100644 index 00000000000..6ec2f9755d5 --- /dev/null +++ b/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/security_groups/_update.html @@ -0,0 +1,26 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} +{% load url from future %} + +{% block form_id %}update_security_group_form{% endblock %} +{% block form_action %}{% url 'horizon:project:access_and_security:security_groups:update' security_group.id%}{% endblock %} + +{% block modal-header %}{% trans "Edit Security Group" %}{% endblock %} +{% block modal_id %}update_security_group_modal{% endblock %} + +{% block modal-body %} +
+
+ {% include "horizon/common/_form_fields.html" %} +
+
+
+

{% trans "Description" %}:

+

{% trans "From here you can modify name and description of a security group." %}

+
+{% endblock %} + +{% block modal-footer %} + + {% trans "Cancel" %} +{% endblock %} diff --git a/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/security_groups/update.html b/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/security_groups/update.html new file mode 100644 index 00000000000..dbdcb5f449d --- /dev/null +++ b/openstack_dashboard/dashboards/project/access_and_security/templates/access_and_security/security_groups/update.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Edit Security Group" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Edit Security Group") %} +{% endblock page_header %} + +{% block main %} + {% include 'project/access_and_security/security_groups/_update.html' %} +{% endblock %} diff --git a/openstack_dashboard/test/api_tests/network_tests.py b/openstack_dashboard/test/api_tests/network_tests.py index 930f1bbaaf1..d174c862d48 100644 --- a/openstack_dashboard/test/api_tests/network_tests.py +++ b/openstack_dashboard/test/api_tests/network_tests.py @@ -232,6 +232,23 @@ def test_security_group_create(self): secgroup['description']) self._cmp_sg(secgroup, ret) + def test_security_group_update(self): + secgroup = self.api_q_secgroups.list()[1] + secgroup = copy.deepcopy(secgroup) + secgroup['name'] = 'newname' + secgroup['description'] = 'new description' + body = {'security_group': + {'name': secgroup['name'], + 'description': secgroup['description']}} + self.qclient.update_security_group(secgroup['id'], body) \ + .AndReturn({'security_group': secgroup}) + self.mox.ReplayAll() + ret = api.network.security_group_update(self.request, + secgroup['id'], + secgroup['name'], + secgroup['description']) + self._cmp_sg(secgroup, ret) + def test_security_group_delete(self): secgroup = self.api_q_secgroups.first() self.qclient.delete_security_group(secgroup['id'])