Merge "Move and fix security group normalization"

This commit is contained in:
Jenkins 2016-10-12 13:47:40 +00:00 committed by Gerrit Code Review
commit 3ecd9a405a
5 changed files with 128 additions and 69 deletions

View File

@ -97,6 +97,78 @@ class Normalizer(object):
new_image['is_public'] = is_public
return new_image
def _normalize_secgroups(self, groups):
"""Normalize the structure of security groups
This makes security group dicts, as returned from nova, look like the
security group dicts as returned from neutron. This does not make them
look exactly the same, but it's pretty close.
:param list groups: A list of security group dicts.
:returns: A list of normalized dicts.
"""
ret = []
for group in groups:
ret.append(self._normalize_secgroup(group))
return ret
def _normalize_secgroup(self, group):
rules = group.pop('security_group_rules', None)
if not rules and 'rules' in group:
rules = group.pop('rules')
group['security_group_rules'] = self._normalize_secgroup_rules(rules)
# neutron sets these. we don't care about it, but let's be the same
project_id = group.get('project_id', group.get('tenant_id', ''))
group['tenant_id'] = project_id
group['project_id'] = project_id
group['location'] = self.current_location
return munch.Munch(group)
def _normalize_secgroup_rules(self, rules):
"""Normalize the structure of nova security group rules
Note that nova uses -1 for non-specific port values, but neutron
represents these with None.
:param list rules: A list of security group rule dicts.
:returns: A list of normalized dicts.
"""
ret = []
for rule in rules:
ret.append(self._normalize_secgroup_rule(rule))
return ret
def _normalize_secgroup_rule(self, rule):
ret = munch.Munch()
ret['id'] = rule['id']
ret['location'] = self.current_location
ret['direction'] = rule.get('direction', 'ingress')
ret['ethertype'] = rule.get('ethertype', 'IPv4')
port_range_min = rule.get(
'port_range_min', rule.get('from_port', None))
if port_range_min == -1:
port_range_min = None
ret['port_range_min'] = port_range_min
port_range_max = rule.get(
'port_range_max', rule.get('to_port', None))
if port_range_max == -1:
port_range_max = None
ret['port_range_max'] = port_range_max
ret['protocol'] = rule.get('protocol', rule.get('ip_protocol'))
ret['remote_ip_prefix'] = rule.get(
'remote_ip_prefix', rule.get('ip_range', {}).get('cidr', None))
ret['security_group_id'] = rule.get(
'security_group_id', rule.get('parent_group_id'))
ret['remote_group_id'] = rule.get('remote_group_id')
project_id = rule.get('project_id', rule.get('tenant_id', ''))
ret['tenant_id'] = project_id
ret['project_id'] = project_id
ret['remote_group_id'] = rule.get('remote_group_id')
return ret
def _normalize_servers(self, servers):
# Here instead of _utils because we need access to region and cloud
# name from the cloud object

View File

@ -182,49 +182,6 @@ def normalize_keystone_services(services):
return meta.obj_list_to_dict(ret)
def normalize_nova_secgroups(groups):
"""Normalize the structure of nova security groups
This makes security group dicts, as returned from nova, look like the
security group dicts as returned from neutron. This does not make them
look exactly the same, but it's pretty close.
:param list groups: A list of security group dicts.
:returns: A list of normalized dicts.
"""
ret = [{'id': g['id'],
'name': g['name'],
'description': g['description'],
'security_group_rules': normalize_nova_secgroup_rules(g['rules'])
} for g in groups]
return meta.obj_list_to_dict(ret)
def normalize_nova_secgroup_rules(rules):
"""Normalize the structure of nova security group rules
Note that nova uses -1 for non-specific port values, but neutron
represents these with None.
:param list rules: A list of security group rule dicts.
:returns: A list of normalized dicts.
"""
ret = [{'id': r['id'],
'direction': 'ingress',
'ethertype': 'IPv4',
'port_range_min':
None if r['from_port'] == -1 else r['from_port'],
'port_range_max':
None if r['to_port'] == -1 else 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 rules]
return meta.obj_list_to_dict(ret)
def normalize_nova_floating_ips(ips):
"""Normalize the structure of Neutron floating IPs

View File

@ -1510,7 +1510,7 @@ class OpenStackCloud(_normalize.Normalizer):
groups = self.manager.submit_task(
_tasks.ServerListSecurityGroups(server=server['id']))
return _utils.normalize_nova_secgroups(groups)
return self._normalize_secgroups(groups)
def list_security_groups(self):
"""List all available security groups.
@ -1524,12 +1524,13 @@ class OpenStackCloud(_normalize.Normalizer):
"Unavailable feature: security groups"
)
groups = []
# Handle neutron security groups
if self._use_neutron_secgroups():
# Neutron returns dicts, so no need to convert objects here.
with _utils.neutron_exceptions(
"Error fetching security group list"):
return self.manager.submit_task(
groups = self.manager.submit_task(
_tasks.NeutronSecurityGroupList())['security_groups']
# Handle nova security groups
@ -1537,7 +1538,7 @@ class OpenStackCloud(_normalize.Normalizer):
with _utils.shade_exceptions("Error fetching security group list"):
groups = self.manager.submit_task(
_tasks.NovaSecurityGroupList())
return _utils.normalize_nova_secgroups(groups)
return self._normalize_secgroups(groups)
def list_servers(self, detailed=False):
"""List all available servers.
@ -5709,6 +5710,7 @@ class OpenStackCloud(_normalize.Normalizer):
"Unavailable feature: security groups"
)
group = None
if self._use_neutron_secgroups():
with _utils.neutron_exceptions(
"Error creating security group {0}".format(name)):
@ -5716,9 +5718,7 @@ class OpenStackCloud(_normalize.Normalizer):
_tasks.NeutronSecurityGroupCreate(
body=dict(security_group=dict(name=name,
description=description))
)
)
return group['security_group']
))['security_group']
else:
with _utils.shade_exceptions(
@ -5729,7 +5729,7 @@ class OpenStackCloud(_normalize.Normalizer):
name=name, description=description
)
)
return _utils.normalize_nova_secgroups([group])[0]
return self._normalize_secgroup(group)
def delete_security_group(self, name_or_id):
"""Delete a security group
@ -5791,9 +5791,9 @@ class OpenStackCloud(_normalize.Normalizer):
"Unavailable feature: security groups"
)
secgroup = self.get_security_group(name_or_id)
group = self.get_security_group(name_or_id)
if secgroup is None:
if group is None:
raise OpenStackCloudException(
"Security group %s not found." % name_or_id)
@ -5802,10 +5802,9 @@ class OpenStackCloud(_normalize.Normalizer):
"Error updating security group {0}".format(name_or_id)):
group = self.manager.submit_task(
_tasks.NeutronSecurityGroupUpdate(
security_group=secgroup['id'],
security_group=group['id'],
body={'security_group': kwargs})
)
return group['security_group']
)['security_group']
else:
with _utils.shade_exceptions(
@ -5813,9 +5812,9 @@ class OpenStackCloud(_normalize.Normalizer):
group=name_or_id)):
group = self.manager.submit_task(
_tasks.NovaSecurityGroupUpdate(
group=secgroup['id'], **kwargs)
group=group['id'], **kwargs)
)
return _utils.normalize_nova_secgroups([group])[0]
return self._normalize_secgroup(group)
def create_security_group_rule(self,
secgroup_name_or_id,
@ -5901,7 +5900,7 @@ class OpenStackCloud(_normalize.Normalizer):
_tasks.NeutronSecurityGroupRuleCreate(
body={'security_group_rule': rule_def})
)
return rule['security_group_rule']
return self._normalize_secgroup_rule(rule['security_group_rule'])
else:
# NOTE: Neutron accepts None for protocol. Nova does not.
@ -5944,7 +5943,7 @@ class OpenStackCloud(_normalize.Normalizer):
group_id=remote_group_id
)
)
return _utils.normalize_nova_secgroup_rules([rule])[0]
return self._normalize_secgroup_rule(rule)
def delete_security_group_rule(self, rule_id):
"""Delete a security group rule

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import mock
import testtools
from shade import _utils
@ -79,7 +80,7 @@ class TestUtils(base.TestCase):
}})
self.assertEqual([el2, el3], ret)
def test_normalize_nova_secgroups(self):
def test_normalize_secgroups(self):
nova_secgroup = dict(
id='abc123',
name='nova_secgroup',
@ -94,17 +95,38 @@ class TestUtils(base.TestCase):
id='abc123',
name='nova_secgroup',
description='A Nova security group',
tenant_id='',
project_id='',
location=dict(
region_name='RegionOne',
project=dict(
domain_name=None,
id=mock.ANY,
domain_id=None,
name='admin'),
cloud='_test_cloud_'),
security_group_rules=[
dict(id='123', direction='ingress', ethertype='IPv4',
port_range_min=80, port_range_max=81, protocol='tcp',
remote_ip_prefix='0.0.0.0/0', security_group_id='xyz123')
remote_ip_prefix='0.0.0.0/0', security_group_id='xyz123',
tenant_id='',
project_id='',
remote_group_id=None,
location=dict(
region_name='RegionOne',
project=dict(
domain_name=None,
id=mock.ANY,
domain_id=None,
name='admin'),
cloud='_test_cloud_'))
]
)
retval = _utils.normalize_nova_secgroups([nova_secgroup])[0]
retval = self.cloud._normalize_secgroup(nova_secgroup)
self.assertEqual(expected, retval)
def test_normalize_nova_secgroups_negone_port(self):
def test_normalize_secgroups_negone_port(self):
nova_secgroup = dict(
id='abc123',
name='nova_secgroup',
@ -115,11 +137,11 @@ class TestUtils(base.TestCase):
]
)
retval = _utils.normalize_nova_secgroups([nova_secgroup])[0]
retval = self.cloud._normalize_secgroup(nova_secgroup)
self.assertIsNone(retval['security_group_rules'][0]['port_range_min'])
self.assertIsNone(retval['security_group_rules'][0]['port_range_max'])
def test_normalize_nova_secgroup_rules(self):
def test_normalize_secgroup_rules(self):
nova_rules = [
dict(id='123', from_port=80, to_port=81, ip_protocol='tcp',
ip_range={'cidr': '0.0.0.0/0'}, parent_group_id='xyz123')
@ -127,9 +149,18 @@ class TestUtils(base.TestCase):
expected = [
dict(id='123', direction='ingress', ethertype='IPv4',
port_range_min=80, port_range_max=81, protocol='tcp',
remote_ip_prefix='0.0.0.0/0', security_group_id='xyz123')
remote_ip_prefix='0.0.0.0/0', security_group_id='xyz123',
tenant_id='', project_id='', remote_group_id=None,
location=dict(
region_name='RegionOne',
project=dict(
domain_name=None,
id=mock.ANY,
domain_id=None,
name='admin'),
cloud='_test_cloud_'))
]
retval = _utils.normalize_nova_secgroup_rules(nova_rules)
retval = self.cloud._normalize_secgroup_rules(nova_rules)
self.assertEqual(expected, retval)
def test_normalize_volumes_v1(self):

View File

@ -352,9 +352,9 @@ class TestSecurityGroups(base.TestCase):
secgroup_name_or_id='nova-sec-group',
direction='egress')
@mock.patch.object(shade._utils, 'normalize_nova_secgroups')
@mock.patch.object(shade.OpenStackCloud, '_normalize_secgroups')
@mock.patch.object(shade.OpenStackCloud, 'nova_client')
def test_list_server_security_groups(self, mock_nova, mock_norm):
def test_list_server_security_groups_nova(self, mock_nova, mock_norm):
self.has_neutron = False
server = dict(id='server_id')
self.cloud.list_server_security_groups(server)