diff --git a/muranodashboard/dynamic_ui/fields.py b/muranodashboard/dynamic_ui/fields.py index 0347d5e69..91d0e5db9 100644 --- a/muranodashboard/dynamic_ui/fields.py +++ b/muranodashboard/dynamic_ui/fields.py @@ -29,6 +29,7 @@ from horizon import exceptions from horizon import forms as hz_forms from horizon import messages from openstack_dashboard.api import glance +from openstack_dashboard.api import network from openstack_dashboard.api import nova from oslo_log import log as logging from oslo_log import versionutils @@ -390,7 +391,7 @@ class FlavorChoiceField(ChoiceField): class KeyPairChoiceField(DynamicChoiceField): - " This widget allows to select keypair for VMs " + """This widget allows to select keypair for VMs""" @with_request def update(self, request, **kwargs): self.choices = [('', _('No keypair'))] @@ -400,6 +401,20 @@ class KeyPairChoiceField(DynamicChoiceField): self.choices.append((keypair.name, keypair.name)) +class SecurityGroupChoiceField(DynamicChoiceField): + """This widget allows to select a security group for VMs""" + @with_request + def update(self, request, **kwargs): + self.choices = [('', _('Application default security group'))] + # TODO(pbourke): remove sorted when supported natively in Horizon + # (https://bugs.launchpad.net/horizon/+bug/1692972) + for secgroup in sorted( + network.security_group_list(request), + key=lambda e: e.name_or_id): + if not secgroup.name_or_id.startswith('murano--'): + self.choices.append((secgroup.name_or_id, secgroup.name_or_id)) + + # NOTE(kzaitsev): for transform to work correctly on horizon SelectWidget # Choice has to be non-string class Choice(object): diff --git a/muranodashboard/dynamic_ui/forms.py b/muranodashboard/dynamic_ui/forms.py index 1eb992e67..cf824d36e 100644 --- a/muranodashboard/dynamic_ui/forms.py +++ b/muranodashboard/dynamic_ui/forms.py @@ -50,7 +50,8 @@ TYPES.update({ 'network': fields.NetworkChoiceField, 'text': (fields.CharField, forms.Textarea), 'choice': fields.ChoiceField, - 'floatingip': fields.FloatingIpBooleanField + 'floatingip': fields.FloatingIpBooleanField, + 'securitygroup': fields.SecurityGroupChoiceField }) KEYPAIR_IMPORT_URL = "horizon:project:key_pairs:import" diff --git a/muranodashboard/tests/unit/dynamic_ui/test_fields.py b/muranodashboard/tests/unit/dynamic_ui/test_fields.py index 2e342503d..5333769e1 100644 --- a/muranodashboard/tests/unit/dynamic_ui/test_fields.py +++ b/muranodashboard/tests/unit/dynamic_ui/test_fields.py @@ -576,6 +576,31 @@ class TestKeyPairChoiceField(testtools.TestCase): sorted(key_pair_choice_field.choices)) +class TestSecurityGroupChoiceField(testtools.TestCase): + + def setUp(self): + super(TestSecurityGroupChoiceField, self).setUp() + self.request = {'request': mock.Mock()} + self.addCleanup(mock.patch.stopall) + + @mock.patch.object(fields, 'network') + def test_update(self, mock_network): + mock_network.security_group_list.return_value = [ + mock.Mock(name_or_id='foo'), + mock.Mock(name_or_id='bar') + ] + security_group_choice_field = fields.SecurityGroupChoiceField() + security_group_choice_field.choices = [] + security_group_choice_field.update(self.request) + + expected_choices = [ + ('', _('Application default security group')), + ('foo', 'foo'), ('bar', 'bar') + ] + self.assertEqual(sorted(expected_choices), + sorted(security_group_choice_field.choices)) + + class TestImageChoiceField(testtools.TestCase): def setUp(self):