diff --git a/requirements.txt b/requirements.txt index 6323ef608..d2edd2eb1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ pbr>=0.11,<2.0 bunch jsonpatch -os-client-config>=1.0.0 +os-client-config>=1.2.0 six python-novaclient>=2.21.0 diff --git a/shade/__init__.py b/shade/__init__.py index cd71efd78..329fec2d7 100644 --- a/shade/__init__.py +++ b/shade/__init__.py @@ -238,6 +238,8 @@ class OpenStackCloud(object): self.api_versions = _get_service_values(kwargs, 'api_version') self.image_api_use_tasks = image_api_use_tasks + self.secgroup_source = kwargs.get('secgroup_source', None) + (self.verify, self.cert) = _ssl_args(verify, cacert, cert, key) self._cache = cache.make_region( @@ -804,15 +806,58 @@ class OpenStackCloud(object): "Error fetching flavor list: %s" % e) def list_security_groups(self): - try: - return meta.obj_list_to_dict( - self.manager.submitTask(_tasks.SecurityGroupList()) + # Handle neutron security groups + if self.secgroup_source == 'neutron': + # Neutron returns dicts, so no need to convert objects here. + try: + groups = self.manager.submitTask( + _tasks.NeutronSecurityGroupList())['security_groups'] + except Exception as e: + self.log.debug( + "neutron could not list security groups: {message}".format( + message=str(e)), + exc_info=True) + raise OpenStackCloudException( + "Error fetching security group list" + ) + return groups + + # Handle nova security groups + elif self.secgroup_source == 'nova': + try: + groups = meta.obj_list_to_dict( + self.manager.submitTask(_tasks.NovaSecurityGroupList()) + ) + except Exception as e: + self.log.debug( + "nova could not list security groups: {message}".format( + message=str(e)), + exc_info=True) + raise OpenStackCloudException( + "Error fetching security group list" + ) + # Make Nova data look like Neutron data. This doesn't make them + # look exactly the same, but pretty close. + return [{'id': g['id'], + 'name': g['name'], + 'description': g['description'], + 'security_group_rules': [{ + 'id': r['id'], + 'direction': 'ingress', + 'ethertype': 'IPv4', + 'port_range_min': r['from_port'], + 'port_range_max': r['to_port'], + 'protocol': r['ip_protocol'], + 'remote_ip_prefix': r['ip_range'].get('cidr', None), + 'security_group_id': r['parent_group_id'], + } for r in g['rules']] + } for g in groups] + + # Security groups not supported + else: + raise OpenStackCloudUnavailableFeature( + "Unavailable feature: security groups" ) - except Exception as e: - self.log.debug( - "security group list failed: %s" % e, exc_info=True) - raise OpenStackCloudException( - "Error fetching security group list: %s" % e) def list_servers(self): try: diff --git a/shade/_tasks.py b/shade/_tasks.py index 3dc051c0f..cc2d7ba29 100644 --- a/shade/_tasks.py +++ b/shade/_tasks.py @@ -192,7 +192,12 @@ class VolumeAttach(task_manager.Task): client.nova_client.volumes.create_server_volume(**self.args) -class SecurityGroupList(task_manager.Task): +class NeutronSecurityGroupList(task_manager.Task): + def main(self, client): + return client.neutron_client.list_security_groups() + + +class NovaSecurityGroupList(task_manager.Task): def main(self, client): return client.nova_client.security_groups.list() diff --git a/shade/exc.py b/shade/exc.py index 26f1acb51..946961d78 100644 --- a/shade/exc.py +++ b/shade/exc.py @@ -38,3 +38,7 @@ class OpenStackCloudUnavailableService(OpenStackCloudException): class OpenStackCloudUnavailableExtension(OpenStackCloudException): pass + + +class OpenStackCloudUnavailableFeature(OpenStackCloudException): + pass diff --git a/shade/tests/unit/test_shade.py b/shade/tests/unit/test_shade.py index ec4cf8218..ec3ff4c1c 100644 --- a/shade/tests/unit/test_shade.py +++ b/shade/tests/unit/test_shade.py @@ -324,6 +324,31 @@ class TestShade(base.TestCase): flavor2 = self.cloud.get_flavor(1) self.assertEquals(vanilla, flavor2) + @mock.patch.object(shade.OpenStackCloud, 'neutron_client') + @mock.patch.object(shade.OpenStackCloud, 'nova_client') + def test_list_security_groups_neutron(self, mock_nova, mock_neutron): + self.cloud.secgroup_source = 'neutron' + self.cloud.list_security_groups() + self.assertTrue(mock_neutron.list_security_groups.called) + self.assertFalse(mock_nova.security_groups.list.called) + + @mock.patch.object(shade.OpenStackCloud, 'neutron_client') + @mock.patch.object(shade.OpenStackCloud, 'nova_client') + def test_list_security_groups_nova(self, mock_nova, mock_neutron): + self.cloud.secgroup_source = 'nova' + self.cloud.list_security_groups() + self.assertFalse(mock_neutron.list_security_groups.called) + self.assertTrue(mock_nova.security_groups.list.called) + + @mock.patch.object(shade.OpenStackCloud, 'neutron_client') + @mock.patch.object(shade.OpenStackCloud, 'nova_client') + def test_list_security_groups_none(self, mock_nova, mock_neutron): + self.cloud.secgroup_source = None + self.assertRaises(shade.OpenStackCloudUnavailableFeature, + self.cloud.list_security_groups) + self.assertFalse(mock_neutron.list_security_groups.called) + self.assertFalse(mock_nova.security_groups.list.called) + class TestShadeOperator(base.TestCase):