Merge "Use Resource layer for network SecurityGroups"

This commit is contained in:
Zuul 2019-06-13 12:38:38 +00:00 committed by Gerrit Code Review
commit b0501643b9
8 changed files with 159 additions and 114 deletions

View File

@ -92,13 +92,6 @@ class ComputeCloudMixin(_normalize.Normalizer):
flavors = self.list_flavors(get_extra=get_extra)
return _utils._filter_list(flavors, name_or_id, filters)
def search_security_groups(self, name_or_id=None, filters=None):
# `filters` could be a dict or a jmespath (str)
groups = self.list_security_groups(
filters=filters if isinstance(filters, dict) else None
)
return _utils._filter_list(groups, name_or_id, filters)
def search_servers(
self, name_or_id=None, filters=None, detailed=False,
all_projects=False, bare=False):

View File

@ -1156,40 +1156,6 @@ class NetworkCloudMixin(_normalize.Normalizer):
firewall_group[key + '_id'] = val
del firewall_group[key]
def list_security_groups(self, filters=None):
"""List all available security groups.
:param filters: (optional) dict of filter conditions to push down
:returns: A list of security group ``munch.Munch``.
"""
# Security groups not supported
if not self._has_secgroups():
raise exc.OpenStackCloudUnavailableFeature(
"Unavailable feature: security groups"
)
if not filters:
filters = {}
data = []
# Handle neutron security groups
if self._use_neutron_secgroups():
# Neutron returns dicts, so no need to convert objects here.
resp = self.network.get('/security-groups.json', params=filters)
data = proxy._json_response(
resp,
error_message="Error fetching security group list")
return self._normalize_secgroups(
self._get_and_munchify('security_groups', data))
# Handle nova security groups
else:
data = proxy._json_response(self.compute.get(
'/os-security-groups', params=filters))
return self._normalize_secgroups(
self._get_and_munchify('security_groups', data))
@_utils.valid_kwargs("name", "description", "shared", "default",
"project_id")
def create_qos_policy(self, **kwargs):

View File

@ -28,6 +28,45 @@ class SecurityGroupCloudMixin(_normalize.Normalizer):
def __init__(self):
self.secgroup_source = self.config.config['secgroup_source']
def search_security_groups(self, name_or_id=None, filters=None):
# `filters` could be a dict or a jmespath (str)
groups = self.list_security_groups(
filters=filters if isinstance(filters, dict) else None
)
return _utils._filter_list(groups, name_or_id, filters)
def list_security_groups(self, filters=None):
"""List all available security groups.
:param filters: (optional) dict of filter conditions to push down
:returns: A list of security group ``munch.Munch``.
"""
# Security groups not supported
if not self._has_secgroups():
raise exc.OpenStackCloudUnavailableFeature(
"Unavailable feature: security groups"
)
if not filters:
filters = {}
data = []
# Handle neutron security groups
if self._use_neutron_secgroups():
# pass filters dict to the list to filter as much as possible on
# the server side
return list(
self.network.security_groups(allow_unknown_params=True,
**filters))
# Handle nova security groups
else:
data = proxy._json_response(self.compute.get(
'/os-security-groups', params=filters))
return self._normalize_secgroups(
self._get_and_munchify('security_groups', data))
def get_security_group(self, name_or_id, filters=None):
"""Get a security group by name or ID.
@ -67,8 +106,7 @@ class SecurityGroupCloudMixin(_normalize.Normalizer):
error_message = ("Error getting security group with"
" ID {id}".format(id=id))
if self._use_neutron_secgroups():
resp = self.network.get('/security-groups/{id}'.format(id=id))
data = proxy._json_response(resp, error_message=error_message)
return self.network.get_security_group(id)
else:
data = proxy._json_response(
self.compute.get(
@ -101,20 +139,17 @@ class SecurityGroupCloudMixin(_normalize.Normalizer):
data = []
security_group_json = {
'security_group': {
'name': name, 'description': description
}}
'name': name, 'description': description
}
if project_id is not None:
security_group_json['security_group']['tenant_id'] = project_id
security_group_json['tenant_id'] = project_id
if self._use_neutron_secgroups():
data = proxy._json_response(
self.network.post(
'/security-groups.json',
json=security_group_json),
error_message="Error creating security group {0}".format(name))
return self.network.create_security_group(
**security_group_json)
else:
data = proxy._json_response(self.compute.post(
'/os-security-groups', json=security_group_json))
'/os-security-groups',
json={'security_group': security_group_json}))
return self._normalize_secgroup(
self._get_and_munchify('security_group', data))
@ -144,13 +179,8 @@ class SecurityGroupCloudMixin(_normalize.Normalizer):
return False
if self._use_neutron_secgroups():
exceptions.raise_from_response(
self.network.delete(
'/security-groups/{sg_id}.json'.format(
sg_id=secgroup['id'])),
error_message="Error deleting security group {0}".format(
name_or_id)
)
self.network.delete_security_group(
secgroup['id'], ignore_missing=False)
return True
else:
@ -183,12 +213,10 @@ class SecurityGroupCloudMixin(_normalize.Normalizer):
"Security group %s not found." % name_or_id)
if self._use_neutron_secgroups():
data = proxy._json_response(
self.network.put(
'/security-groups/{sg_id}.json'.format(sg_id=group['id']),
json={'security_group': kwargs}),
error_message="Error updating security group {0}".format(
name_or_id))
return self.network.update_security_group(
group['id'],
**kwargs
)
else:
for key in ('name', 'description'):
kwargs.setdefault(key, group[key])
@ -281,13 +309,12 @@ class SecurityGroupCloudMixin(_normalize.Normalizer):
'ethertype': ethertype
}
if project_id is not None:
rule_def['project_id'] = project_id
rule_def['tenant_id'] = project_id
data = proxy._json_response(
self.network.post(
'/security-group-rules.json',
json={'security_group_rule': rule_def}),
error_message="Error creating security group rule")
return self.network.create_security_group_rule(
**rule_def
)
else:
# NOTE: Neutron accepts None for protocol. Nova does not.
if protocol is None:
@ -355,15 +382,10 @@ class SecurityGroupCloudMixin(_normalize.Normalizer):
)
if self._use_neutron_secgroups():
try:
exceptions.raise_from_response(
self.network.delete(
'/security-group-rules/{sg_id}.json'.format(
sg_id=rule_id)),
error_message="Error deleting security group rule "
"{0}".format(rule_id))
except exc.OpenStackCloudResourceNotFound:
return False
self.network.delete_security_group_rule(
rule_id,
ignore_missing=False
)
return True
else:

View File

@ -26,8 +26,8 @@ class SecurityGroup(resource.Resource, resource.TagMixin):
allow_list = True
_query_mapping = resource.QueryParameters(
'description', 'name',
project_id='tenant_id',
'description', 'name', 'project_id', 'tenant_id', 'revision_number',
'sort_dir', 'sort_key',
**resource.TagMixin._tag_query_parameters
)
@ -39,12 +39,14 @@ class SecurityGroup(resource.Resource, resource.TagMixin):
#: The security group name.
name = resource.Body('name')
#: The ID of the project this security group is associated with.
project_id = resource.Body('tenant_id')
project_id = resource.Body('project_id')
#: Revision number of the security group. *Type: int*
revision_number = resource.Body('revision_number', type=int)
#: A list of
#: :class:`~openstack.network.v2.security_group_rule.SecurityGroupRule`
#: objects. *Type: list*
security_group_rules = resource.Body('security_group_rules', type=list)
#: The ID of the project this security group is associated with.
tenant_id = resource.Body('tenant_id')
#: Timestamp when the security group was last updated.
updated_at = resource.Body('updated_at')

View File

@ -28,8 +28,11 @@ class SecurityGroupRule(resource.Resource, resource.TagMixin):
_query_mapping = resource.QueryParameters(
'description', 'direction', 'protocol',
'remote_group_id', 'security_group_id',
'port_range_max', 'port_range_min',
'remote_ip_prefix', 'revision_number',
'project_id', 'tenant_id',
'sort_dir', 'sort_key',
ether_type='ethertype',
project_id='tenant_id',
**resource.TagMixin._tag_query_parameters
)
@ -58,7 +61,7 @@ class SecurityGroupRule(resource.Resource, resource.TagMixin):
#: attribute. If the protocol is ICMP, this value must be an ICMP type.
port_range_min = resource.Body('port_range_min', type=int)
#: The ID of the project this security group rule is associated with.
project_id = resource.Body('tenant_id')
project_id = resource.Body('project_id')
#: The protocol that is matched by the security group rule.
#: Valid values are ``null``, ``tcp``, ``udp``, and ``icmp``.
protocol = resource.Body('protocol')
@ -75,5 +78,7 @@ class SecurityGroupRule(resource.Resource, resource.TagMixin):
revision_number = resource.Body('revision_number', type=int)
#: The security group ID to associate with this security group rule.
security_group_id = resource.Body('security_group_id')
#: The ID of the project this security group rule is associated with.
tenant_id = resource.Body('tenant_id')
#: Timestamp when the security group rule was last updated.
updated_at = resource.Body('updated_at')

View File

@ -59,7 +59,7 @@ class TestSecurityGroups(base.TestCase):
dict(method='GET',
uri=self.get_mock_url(
'network', 'public',
append=['v2.0', 'security-groups.json'],
append=['v2.0', 'security-groups'],
qs_elements=["project_id=%s" % project_id]),
json={'security_groups': [neutron_grp_dict]})
])
@ -93,12 +93,13 @@ class TestSecurityGroups(base.TestCase):
dict(method='GET',
uri=self.get_mock_url(
'network', 'public',
append=['v2.0', 'security-groups.json']),
append=['v2.0', 'security-groups']),
json={'security_groups': [neutron_grp_dict]}),
dict(method='DELETE',
uri=self.get_mock_url(
'network', 'public',
append=['v2.0', 'security-groups', '%s.json' % sg_id]),
append=['v2.0', 'security-groups', '%s' % sg_id]),
status_code=200,
json={})
])
self.assertTrue(self.cloud.delete_security_group('1'))
@ -126,7 +127,7 @@ class TestSecurityGroups(base.TestCase):
dict(method='GET',
uri=self.get_mock_url(
'network', 'public',
append=['v2.0', 'security-groups.json']),
append=['v2.0', 'security-groups']),
json={'security_groups': [neutron_grp_dict]})
])
self.assertFalse(self.cloud.delete_security_group('10'))
@ -163,7 +164,7 @@ class TestSecurityGroups(base.TestCase):
dict(method='POST',
uri=self.get_mock_url(
'network', 'public',
append=['v2.0', 'security-groups.json']),
append=['v2.0', 'security-groups']),
json={'security_group': new_group},
validate=dict(
json={'security_group': {
@ -194,7 +195,7 @@ class TestSecurityGroups(base.TestCase):
dict(method='POST',
uri=self.get_mock_url(
'network', 'public',
append=['v2.0', 'security-groups.json']),
append=['v2.0', 'security-groups']),
json={'security_group': new_group},
validate=dict(
json={'security_group': {
@ -260,12 +261,12 @@ class TestSecurityGroups(base.TestCase):
dict(method='GET',
uri=self.get_mock_url(
'network', 'public',
append=['v2.0', 'security-groups.json']),
append=['v2.0', 'security-groups']),
json={'security_groups': [neutron_grp_dict]}),
dict(method='PUT',
uri=self.get_mock_url(
'network', 'public',
append=['v2.0', 'security-groups', '%s.json' % sg_id]),
append=['v2.0', 'security-groups', '%s' % sg_id]),
json={'security_group': update_return},
validate=dict(json={
'security_group': {'name': new_name}}))
@ -321,28 +322,36 @@ class TestSecurityGroups(base.TestCase):
expected_new_rule = copy.copy(expected_args)
expected_new_rule['id'] = '1234'
expected_new_rule['tenant_id'] = ''
expected_new_rule['tenant_id'] = None
expected_new_rule['project_id'] = expected_new_rule['tenant_id']
self.register_uris([
dict(method='GET',
uri=self.get_mock_url(
'network', 'public',
append=['v2.0', 'security-groups.json']),
append=['v2.0', 'security-groups']),
json={'security_groups': [neutron_grp_dict]}),
dict(method='POST',
uri=self.get_mock_url(
'network', 'public',
append=['v2.0', 'security-group-rules.json']),
append=['v2.0', 'security-group-rules']),
json={'security_group_rule': expected_new_rule},
validate=dict(json={
'security_group_rule': expected_args}))
])
new_rule = self.cloud.create_security_group_rule(
secgroup_name_or_id=neutron_grp_dict['id'], **args)
# NOTE(slaweq): don't check location and properties in new rule
new_rule.pop("location")
new_rule.pop("properties")
secgroup_name_or_id=neutron_grp_dict['id'], **args).to_dict(
original_names=True
)
# NOTE(gtema): don't check location and not relevant properties
# in new rule
new_rule.pop('created_at')
new_rule.pop('description')
new_rule.pop('location')
new_rule.pop('name')
new_rule.pop('revision_number')
new_rule.pop('tags')
new_rule.pop('updated_at')
self.assertEqual(expected_new_rule, new_rule)
self.assert_calls()
@ -363,31 +372,36 @@ class TestSecurityGroups(base.TestCase):
expected_args['port_range_min'] = None
expected_args['security_group_id'] = neutron_grp_dict['id']
expected_args['tenant_id'] = expected_args['project_id']
expected_args.pop('project_id')
expected_new_rule = copy.copy(expected_args)
expected_new_rule['id'] = '1234'
expected_new_rule['project_id'] = expected_new_rule['tenant_id']
self.register_uris([
dict(method='GET',
uri=self.get_mock_url(
'network', 'public',
append=['v2.0', 'security-groups.json']),
append=['v2.0', 'security-groups']),
json={'security_groups': [neutron_grp_dict]}),
dict(method='POST',
uri=self.get_mock_url(
'network', 'public',
append=['v2.0', 'security-group-rules.json']),
append=['v2.0', 'security-group-rules']),
json={'security_group_rule': expected_new_rule},
validate=dict(json={
'security_group_rule': expected_args}))
])
new_rule = self.cloud.create_security_group_rule(
secgroup_name_or_id=neutron_grp_dict['id'], ** args)
secgroup_name_or_id=neutron_grp_dict['id'], ** args).to_dict(
original_names=True
)
# NOTE(slaweq): don't check location and properties in new rule
new_rule.pop("location")
new_rule.pop("properties")
new_rule.pop('created_at')
new_rule.pop('description')
new_rule.pop('location')
new_rule.pop('name')
new_rule.pop('revision_number')
new_rule.pop('tags')
new_rule.pop('updated_at')
self.assertEqual(expected_new_rule, new_rule)
self.assert_calls()
@ -477,7 +491,7 @@ class TestSecurityGroups(base.TestCase):
uri=self.get_mock_url(
'network', 'public',
append=['v2.0', 'security-group-rules',
'%s.json' % rule_id]),
'%s' % rule_id]),
json={})
])
self.assertTrue(self.cloud.delete_security_group_rule(rule_id))
@ -509,7 +523,7 @@ class TestSecurityGroups(base.TestCase):
dict(method='GET',
uri=self.get_mock_url(
'network', 'public',
append=['v2.0', 'security-groups.json']),
append=['v2.0', 'security-groups']),
json={'security_groups': [neutron_grp_dict]})
])
self.assertFalse(self.cloud.delete_security_group(rule_id))
@ -618,7 +632,7 @@ class TestSecurityGroups(base.TestCase):
dict(method='GET',
uri=self.get_mock_url(
'network', 'public',
append=['v2.0', 'security-groups.json']),
append=['v2.0', 'security-groups']),
json={'security_groups': [neutron_grp_dict]}),
dict(method='POST',
uri='%s/servers/%s/action' % (fakes.COMPUTE_ENDPOINT, '1234'),
@ -674,7 +688,7 @@ class TestSecurityGroups(base.TestCase):
dict(method='GET',
uri=self.get_mock_url(
'network', 'public',
append=['v2.0', 'security-groups.json']),
append=['v2.0', 'security-groups']),
json={'security_groups': [neutron_grp_dict]}),
dict(method='POST',
uri='%s/servers/%s/action' % (fakes.COMPUTE_ENDPOINT, '1234'),
@ -729,7 +743,7 @@ class TestSecurityGroups(base.TestCase):
dict(method='GET',
uri=self.get_mock_url(
'network', 'public',
append=['v2.0', 'security-groups.json']),
append=['v2.0', 'security-groups']),
json={'security_groups': [neutron_grp_dict]})
])
self.assertFalse(self.cloud.add_server_security_groups(

View File

@ -57,6 +57,7 @@ EXAMPLE = {
'revision_number': 3,
'security_group_rules': RULES,
'tenant_id': '4',
'project_id': '4',
'updated_at': '2016-10-14T12:16:57.233772',
'tags': ['5']
}
@ -75,6 +76,22 @@ class TestSecurityGroup(base.TestCase):
self.assertTrue(sot.allow_delete)
self.assertTrue(sot.allow_list)
self.assertDictEqual({'any_tags': 'tags-any',
'description': 'description',
'limit': 'limit',
'marker': 'marker',
'name': 'name',
'not_any_tags': 'not-tags-any',
'not_tags': 'not-tags',
'project_id': 'project_id',
'revision_number': 'revision_number',
'sort_dir': 'sort_dir',
'sort_key': 'sort_key',
'tags': 'tags',
'tenant_id': 'tenant_id'
},
sot._query_mapping._mapping)
def test_make_it(self):
sot = security_group.SecurityGroup(**EXAMPLE)
self.assertEqual(EXAMPLE['created_at'], sot.created_at)
@ -85,6 +102,7 @@ class TestSecurityGroup(base.TestCase):
self.assertEqual(EXAMPLE['security_group_rules'],
sot.security_group_rules)
self.assertEqual(dict, type(sot.security_group_rules[0]))
self.assertEqual(EXAMPLE['tenant_id'], sot.project_id)
self.assertEqual(EXAMPLE['tenant_id'], sot.tenant_id)
self.assertEqual(EXAMPLE['project_id'], sot.project_id)
self.assertEqual(EXAMPLE['updated_at'], sot.updated_at)
self.assertEqual(EXAMPLE['tags'], sot.tags)

View File

@ -29,6 +29,7 @@ EXAMPLE = {
'revision_number': 9,
'security_group_id': '10',
'tenant_id': '11',
'project_id': '11',
'updated_at': '12'
}
@ -46,6 +47,29 @@ class TestSecurityGroupRule(base.TestCase):
self.assertTrue(sot.allow_delete)
self.assertTrue(sot.allow_list)
self.assertDictEqual({'any_tags': 'tags-any',
'description': 'description',
'direction': 'direction',
'ether_type': 'ethertype',
'limit': 'limit',
'marker': 'marker',
'not_any_tags': 'not-tags-any',
'not_tags': 'not-tags',
'port_range_max': 'port_range_max',
'port_range_min': 'port_range_min',
'project_id': 'project_id',
'protocol': 'protocol',
'remote_group_id': 'remote_group_id',
'remote_ip_prefix': 'remote_ip_prefix',
'revision_number': 'revision_number',
'security_group_id': 'security_group_id',
'sort_dir': 'sort_dir',
'sort_key': 'sort_key',
'tags': 'tags',
'tenant_id': 'tenant_id'
},
sot._query_mapping._mapping)
def test_make_it(self):
sot = security_group_rule.SecurityGroupRule(**EXAMPLE)
self.assertEqual(EXAMPLE['created_at'], sot.created_at)
@ -60,5 +84,6 @@ class TestSecurityGroupRule(base.TestCase):
self.assertEqual(EXAMPLE['remote_ip_prefix'], sot.remote_ip_prefix)
self.assertEqual(EXAMPLE['revision_number'], sot.revision_number)
self.assertEqual(EXAMPLE['security_group_id'], sot.security_group_id)
self.assertEqual(EXAMPLE['tenant_id'], sot.project_id)
self.assertEqual(EXAMPLE['tenant_id'], sot.tenant_id)
self.assertEqual(EXAMPLE['project_id'], sot.project_id)
self.assertEqual(EXAMPLE['updated_at'], sot.updated_at)