diff --git a/doc/source/devref/abstraction.rst b/doc/source/devref/abstraction.rst index e08f885e7..0c47f44da 100644 --- a/doc/source/devref/abstraction.rst +++ b/doc/source/devref/abstraction.rst @@ -382,6 +382,12 @@ The following defines the mapping to Neutron resources using attribute extension 'port_id': {'allow_post': True, 'allow_put': False, 'validate': {'type:uuid_or_none': None}, 'is_visible': True, 'default': None}, + 'fixed_ips': {'allow_post': True, 'allow_put': True, + 'default': attr.ATTR_NOT_SPECIFIED, + 'convert_list_to': attr.convert_kvp_list_to_dict, + 'validate': {'type:fixed_ips': None}, + 'enforce_policy': True, + 'is_visible': True}, }, gp.POLICY_TARGET_GROUPS: { 'subnets': {'allow_post': True, 'allow_put': True, diff --git a/gbpservice/neutron/extensions/group_policy_mapping.py b/gbpservice/neutron/extensions/group_policy_mapping.py index 7d1d39bbd..6a680ed44 100644 --- a/gbpservice/neutron/extensions/group_policy_mapping.py +++ b/gbpservice/neutron/extensions/group_policy_mapping.py @@ -21,6 +21,12 @@ EXTENDED_ATTRIBUTES_2_0 = { 'port_id': {'allow_post': True, 'allow_put': False, 'validate': {'type:uuid_or_none': None}, 'is_visible': True, 'default': None}, + 'fixed_ips': {'allow_post': True, 'allow_put': True, + 'default': attr.ATTR_NOT_SPECIFIED, + 'convert_list_to': attr.convert_kvp_list_to_dict, + 'validate': {'type:fixed_ips': None}, + 'enforce_policy': True, + 'is_visible': True}, }, gp.POLICY_TARGET_GROUPS: { 'subnets': {'allow_post': True, 'allow_put': True, diff --git a/gbpservice/neutron/services/grouppolicy/plugin.py b/gbpservice/neutron/services/grouppolicy/plugin.py index f8d8b9d97..c08ec41f5 100644 --- a/gbpservice/neutron/services/grouppolicy/plugin.py +++ b/gbpservice/neutron/services/grouppolicy/plugin.py @@ -351,8 +351,18 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin): else: return result + def _add_fixed_ips_to_port_attributes(self, policy_target): + if 'fixed_ips' in policy_target['policy_target'] and ( + policy_target['policy_target']['fixed_ips'] is not ( + nattr.ATTR_NOT_SPECIFIED)): + port_attributes = {'fixed_ips': policy_target[ + 'policy_target']['fixed_ips']} + policy_target['policy_target'].update( + {'port_attributes': port_attributes}) + @log.log def create_policy_target(self, context, policy_target): + self._add_fixed_ips_to_port_attributes(policy_target) session = context.session with session.begin(subtransactions=True): result = super(GroupPolicyPlugin, @@ -382,6 +392,7 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin): @log.log def update_policy_target(self, context, policy_target_id, policy_target): + self._add_fixed_ips_to_port_attributes(policy_target) session = context.session with session.begin(subtransactions=True): original_policy_target = self.get_policy_target(context, diff --git a/gbpservice/neutron/tests/unit/services/grouppolicy/test_grouppolicy_plugin.py b/gbpservice/neutron/tests/unit/services/grouppolicy/test_grouppolicy_plugin.py index 8489657c8..3d747e25e 100644 --- a/gbpservice/neutron/tests/unit/services/grouppolicy/test_grouppolicy_plugin.py +++ b/gbpservice/neutron/tests/unit/services/grouppolicy/test_grouppolicy_plugin.py @@ -972,4 +972,5 @@ class TestGroupPolicyPluginGroupResources( class TestGroupPolicyPluginMappedGroupResourceAttrs( GroupPolicyPluginTestCase, tgpmdb.TestMappedGroupResourceAttrs): + pass diff --git a/gbpservice/neutron/tests/unit/services/grouppolicy/test_resource_mapping.py b/gbpservice/neutron/tests/unit/services/grouppolicy/test_resource_mapping.py index 53719b742..483be5834 100644 --- a/gbpservice/neutron/tests/unit/services/grouppolicy/test_resource_mapping.py +++ b/gbpservice/neutron/tests/unit/services/grouppolicy/test_resource_mapping.py @@ -571,6 +571,29 @@ class TestPolicyTarget(ResourceMappingTestCase, TestClusterIdMixin): res = req.get_response(self.api) self.assertEqual(res.status_int, webob.exc.HTTPNotFound.code) + def test_create_policy_target_with_fixed_ip(self): + l3p = self.create_l3_policy(name="l3p1", ip_pool='10.0.0.0/8') + l3p_id = l3p['l3_policy']['id'] + l2p = self.create_l2_policy(name="l2p1", l3_policy_id=l3p_id) + l2p_id = l2p['l2_policy']['id'] + network_id = l2p['l2_policy']['network_id'] + req = self.new_show_request('networks', network_id) + network = self.deserialize(self.fmt, req.get_response(self.api)) + with self.subnet(network=network, cidr='10.10.1.0/24') as subnet1: + fixed_ips = [{'subnet_id': subnet1['subnet']['id'], + 'ip_address': '10.10.1.5'}] + ptg = self.create_policy_target_group( + l2_policy_id=l2p_id, + subnets=[subnet1['subnet']['id']])['policy_target_group'] + pt = self.create_policy_target( + policy_target_group_id=ptg['id'], + fixed_ips=fixed_ips)['policy_target'] + port = self._get_object('ports', pt['port_id'], + self.api)['port'] + self.assertEqual(1, len(port['fixed_ips'])) + ip = port['fixed_ips'][0]['ip_address'] + self.assertEqual('10.10.1.5', ip) + def test_explicit_port_lifecycle(self): # Create policy_target group. ptg = self.create_policy_target_group(name="ptg1") diff --git a/gbpservice/neutron/tests/unit/test_extension_group_policy.py b/gbpservice/neutron/tests/unit/test_extension_group_policy.py index c450d5ede..d0ae08228 100644 --- a/gbpservice/neutron/tests/unit/test_extension_group_policy.py +++ b/gbpservice/neutron/tests/unit/test_extension_group_policy.py @@ -14,6 +14,7 @@ import copy import re import mock +from neutron.api.v2 import attributes as nattr from neutron.common import constants as n_consts from neutron.plugins.common import constants from neutron.tests import base @@ -84,12 +85,19 @@ class GroupPolicyExtensionTestCase(test_extensions_base.ExtensionTestCase): res = self.api.post(_get_path(POLICY_TARGETS_URI, fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt) - self.instance.create_policy_target.assert_called_once_with( - mock.ANY, policy_target=default_data) self.assertEqual(exc.HTTPCreated.code, res.status_int) res = self.deserialize(res) self.assertIn('policy_target', res) + if 'fixed_ips' in expected_value and ( + str(res['policy_target']['fixed_ips']) == + str(nattr.ATTR_NOT_SPECIFIED)): + default_data['policy_target']['fixed_ips'] = ( + nattr.ATTR_NOT_SPECIFIED) + expected_value['fixed_ips'] = res['policy_target']['fixed_ips'] + self.assertEqual(expected_value, res['policy_target']) + self.instance.create_policy_target.assert_called_once_with( + mock.ANY, policy_target=default_data) def test_create_policy_target_with_defaults(self): policy_target_id = _uuid() diff --git a/gbpservice/neutron/tests/unit/test_extension_group_policy_mapping.py b/gbpservice/neutron/tests/unit/test_extension_group_policy_mapping.py index bc58191bc..bdfd92c9d 100644 --- a/gbpservice/neutron/tests/unit/test_extension_group_policy_mapping.py +++ b/gbpservice/neutron/tests/unit/test_extension_group_policy_mapping.py @@ -10,6 +10,9 @@ # License for the specific language governing permissions and limitations # under the License. +import copy + +from neutron.api.v2 import attributes as nattr from neutron.plugins.common import constants from gbpservice.neutron.extensions import group_policy as gp @@ -61,6 +64,16 @@ class GroupPolicyMappingExtTestCase(tgp.GroupPolicyExtensionTestCase): def get_create_policy_target_attrs(self): attrs = cm.get_create_policy_target_attrs() attrs.update({'port_id': tgp._uuid()}) + fixed_ips = [{'subnet_id': '00000000-ffff-ffff-ffff-000000000000', + 'ip_address': '11.1.1.1'}] + attrs.update({'fixed_ips': fixed_ips}) + return attrs + + def get_update_policy_target_attrs(self): + attrs = cm.get_update_policy_target_attrs() + fixed_ips = [{'subnet_id': '00000000-ffff-ffff-ffff-000000000000', + 'ip_address': '11.1.1.1'}] + attrs.update({'fixed_ips': fixed_ips}) return attrs def get_create_policy_target_group_default_attrs(self): @@ -112,3 +125,16 @@ class GroupPolicyMappingExtTestCase(tgp.GroupPolicyExtensionTestCase): attrs = cm.get_create_external_segment_attrs() attrs.update({'subnet_id': tgp._uuid()}) return attrs + + def test_create_policy_target_with_defaults(self): + policy_target_id = tgp._uuid() + data = {'policy_target': {'policy_target_group_id': tgp._uuid(), + 'tenant_id': tgp._uuid()}} + default_attrs = self.get_create_policy_target_default_attrs() + default_data = copy.copy(data) + default_data['policy_target'].update(default_attrs) + expected_value = dict(default_data['policy_target']) + expected_value['id'] = policy_target_id + expected_value['fixed_ips'] = nattr.ATTR_NOT_SPECIFIED + + self._test_create_policy_target(data, expected_value, default_data) diff --git a/gbpservice/tests/contrib/devstack/exercises/gbp.sh b/gbpservice/tests/contrib/devstack/exercises/gbp.sh index f6cdddadb..1186b1438 100755 --- a/gbpservice/tests/contrib/devstack/exercises/gbp.sh +++ b/gbpservice/tests/contrib/devstack/exercises/gbp.sh @@ -115,6 +115,14 @@ gbp policy-target-delete web-pt-1 gbp policy-target-delete client-pt-1 gbp policy-target-delete client-pt-2 +# The following tests fixed IP assignment for PTs +WEB_SUBNET=$(gbp ptg-show web | grep subnets | awk '{print $4}') +WEB_GW_IP=$(neutron subnet-show $WEB_SUBNET | grep gateway_ip | awk '{print $4}') +FIXED_IP="${WEB_GW_IP}0" +gbp pt-create --policy-target-group web --fixed-ip subnet_id=$WEB_SUBNET,ip_address=$FIXED_IP pt-fixed-ip +neutron port-show pt_pt-fixed-ip +gbp pt-delete pt-fixed-ip + gbp group-delete web gbp group-delete client-1 gbp group-delete client-2