Allow creating a Neutron port without fixed ips

Since Newton release, Neutron supports ports without fixed ips[1].
This change introduces a new no_fixed_ips property so that we can
create an unaddressed port from Heat.

[1] https://specs.openstack.org/openstack/neutron-specs/specs/newton/unaddressed-port.html

Story: 2008554
Task: 41685
Change-Id: I6f16905155038db44c7e01dd5d34d65032dca061
This commit is contained in:
Takashi Kajinami 2021-01-25 21:31:15 +09:00
parent f4ab9d0bc0
commit 9292264aa7
3 changed files with 84 additions and 20 deletions

View File

@ -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:

View File

@ -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)

View File

@ -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.