diff --git a/heat/engine/resources/openstack/neutron/port.py b/heat/engine/resources/openstack/neutron/port.py index b7a01d40b8..a06fbf3590 100644 --- a/heat/engine/resources/openstack/neutron/port.py +++ b/heat/engine/resources/openstack/neutron/port.py @@ -14,6 +14,7 @@ from oslo_log import log as logging from oslo_serialization import jsonutils +from heat.common import exception from heat.common.i18n import _ from heat.engine import attributes from heat.engine import constraints @@ -53,11 +54,11 @@ class Port(neutron.NeutronResource): EXTRA_PROPERTIES = ( VALUE_SPECS, ADMIN_STATE_UP, MAC_ADDRESS, ALLOWED_ADDRESS_PAIRS, VNIC_TYPE, QOS_POLICY, - PORT_SECURITY_ENABLED, PROPAGATE_UPLINK_STATUS, + PORT_SECURITY_ENABLED, PROPAGATE_UPLINK_STATUS, NO_FIXED_IPS, ) = ( 'value_specs', 'admin_state_up', 'mac_address', 'allowed_address_pairs', 'binding:vnic_type', 'qos_policy', - 'port_security_enabled', 'propagate_uplink_status', + 'port_security_enabled', 'propagate_uplink_status', 'no_fixed_ips', ) _FIXED_IP_KEYS = ( @@ -313,6 +314,13 @@ class Port(neutron.NeutronResource): update_allowed=True, support_status=support.SupportStatus(version='15.0.0') ), + NO_FIXED_IPS: properties.Schema( + properties.Schema.BOOLEAN, + _('Flag to disable all fixed ips on the port.'), + update_allowed=True, + support_status=support.SupportStatus(version='16.0.0'), + default=False + ), } # Need to update properties_schema with other properties before @@ -442,6 +450,14 @@ class Port(neutron.NeutronResource): ) ] + def validate(self): + super(Port, self).validate() + fixed_ips = self.properties.get(self.FIXED_IPS) + no_fixed_ips = self.properties.get(self.NO_FIXED_IPS, False) + if fixed_ips and no_fixed_ips: + raise exception.ResourcePropertyConflict(self.FIXED_IPS, + self.NO_FIXED_IPS) + def add_dependencies(self, deps): super(Port, self).add_dependencies(deps) # Depend on any Subnet in this template with the same @@ -480,24 +496,28 @@ class Port(neutron.NeutronResource): self.set_tags(tags) def _prepare_port_properties(self, props, prepare_for_update=False): - if self.FIXED_IPS in props: - fixed_ips = props[self.FIXED_IPS] - if fixed_ips: - for fixed_ip in fixed_ips: - for key, value in list(fixed_ip.items()): - if value is None: - fixed_ip.pop(key) - if self.FIXED_IP_SUBNET in fixed_ip: - fixed_ip[ - 'subnet_id'] = fixed_ip.pop(self.FIXED_IP_SUBNET) - else: - # Passing empty list would have created a port without - # fixed_ips during CREATE and released the existing - # fixed_ips during UPDATE (default neutron behaviour). - # However, for backward compatibility we will let neutron - # assign ip for CREATE and leave the assigned ips during - # UPDATE by not passing it. ref bug #1538473. - del props[self.FIXED_IPS] + if not props.pop(self.NO_FIXED_IPS, False): + if self.FIXED_IPS in props: + fixed_ips = props[self.FIXED_IPS] + if fixed_ips: + for fixed_ip in fixed_ips: + for key, value in list(fixed_ip.items()): + if value is None: + fixed_ip.pop(key) + if self.FIXED_IP_SUBNET in fixed_ip: + fixed_ip['subnet_id'] = \ + fixed_ip.pop(self.FIXED_IP_SUBNET) + else: + # Passing empty list would have created a port without + # fixed_ips during CREATE and released the existing + # fixed_ips during UPDATE (default neutron behaviour). + # However, for backward compatibility we will let neutron + # assign ip for CREATE and leave the assigned ips during + # UPDATE by not passing it. ref bug #1538473. + del props[self.FIXED_IPS] + else: + props[self.FIXED_IPS] = [] + # delete empty MAC addresses so that Neutron validation code # wouldn't fail as it not accepts Nones if self.ALLOWED_ADDRESS_PAIRS in props: diff --git a/heat/tests/openstack/neutron/test_neutron_port.py b/heat/tests/openstack/neutron/test_neutron_port.py index 4e022e1ed1..04244f52e6 100644 --- a/heat/tests/openstack/neutron/test_neutron_port.py +++ b/heat/tests/openstack/neutron/test_neutron_port.py @@ -56,6 +56,17 @@ resources: ''' +neutron_port_with_no_fixed_ips_template = ''' +heat_template_version: 2015-04-30 +description: Template to test port Neutron resource +resources: + port: + type: OS::Neutron::Port + properties: + network: abcd1234 + no_fixed_ips: true +''' + neutron_port_security_template = ''' heat_template_version: 2015-04-30 description: Template to test port Neutron resource @@ -224,6 +235,34 @@ class NeutronPortTest(common.HeatTestCase): 'device_owner': '' }}) + def test_no_fixed_ips(self): + t = template_format.parse(neutron_port_with_no_fixed_ips_template) + stack = utils.parse_stack(t) + + self.find_mock.return_value = 'abcd1234' + + self.create_mock.return_value = {'port': { + "status": "BUILD", + "id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766" + }} + + self.port_show_mock.return_value = {'port': { + "status": "ACTIVE", + "id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766", + }} + + port = stack['port'] + scheduler.TaskRunner(port.create)() + self.create_mock.assert_called_once_with({'port': { + 'network_id': u'abcd1234', + 'name': utils.PhysName(stack.name, 'port'), + 'fixed_ips': [], + 'admin_state_up': True, + 'binding:vnic_type': 'normal', + 'device_id': '', + 'device_owner': '' + }}) + def test_port_security_enabled(self): t = template_format.parse(neutron_port_security_template) stack = utils.parse_stack(t) diff --git a/releasenotes/notes/neutron-port-without-fixed-ips-e3a771d106224628.yaml b/releasenotes/notes/neutron-port-without-fixed-ips-e3a771d106224628.yaml new file mode 100644 index 0000000000..7235850ed6 --- /dev/null +++ b/releasenotes/notes/neutron-port-without-fixed-ips-e3a771d106224628.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Now the ``OS::Neutron::Port`` type supports the ``no_fixed_ips`` property, + which allows users to create a network port without any fixed ips.