OS::Neutron::Port: Add network attribute

Adds attribute 'network' to the port resource.

Similar to the existing subnets attribute which returns
a list of all subnets. The network attribute returns
the properties of the neutron network.

Story: 1766946
Task: 18792
Change-Id: I6c667a0ff2c15aa27ca0d7943359e7f595630f87
This commit is contained in:
Harald Jensås 2018-05-17 08:32:13 +02:00
parent 42aab2095a
commit 93feb34539
3 changed files with 97 additions and 2 deletions

View File

@ -78,11 +78,12 @@ class Port(neutron.NeutronResource):
MAC_ADDRESS_ATTR, NAME_ATTR, NETWORK_ID_ATTR, SECURITY_GROUPS_ATTR,
STATUS, TENANT_ID, ALLOWED_ADDRESS_PAIRS_ATTR, SUBNETS_ATTR,
PORT_SECURITY_ENABLED_ATTR, QOS_POLICY_ATTR, DNS_ASSIGNMENT,
NETWORK_ATTR,
) = (
'admin_state_up', 'device_id', 'device_owner', 'fixed_ips',
'mac_address', 'name', 'network_id', 'security_groups',
'status', 'tenant_id', 'allowed_address_pairs', 'subnets',
'port_security_enabled', 'qos_policy_id', 'dns_assignment',
'port_security_enabled', 'qos_policy_id', 'dns_assignment', 'network',
)
properties_schema = {
@ -379,6 +380,22 @@ class Port(neutron.NeutronResource):
type=attributes.Schema.MAP,
support_status=support.SupportStatus(version='7.0.0'),
),
NETWORK_ATTR: attributes.Schema(
_("The attributes of the network owning the port. (The full list "
"of response parameters can be found in the `Openstack "
"Networking service API reference "
"<https://developer.openstack.org/api-ref/network/>`_.) The "
"following examples demonstrate some (not all) possible "
"expressions. (Obtains the network, the MTU (Maximum "
"transmission unit), the network tags and the l2_adjacency "
"property): "
"``{get_attr: [<port>, network]}``, "
"``{get_attr: [<port>, network, mtu]}``, "
"``{get_attr: [<port>, network, tags]}?``, "
"``{get_attr: [<port>, network, l2_adjacency]}``."),
type=attributes.Schema.MAP,
support_status=support.SupportStatus(version='11.0.0'),
),
}
def translation_rules(self, props):
@ -551,6 +568,13 @@ class Port(neutron.NeutronResource):
LOG.warning("Failed to fetch resource attributes: %s", ex)
return
return subnets
if name == self.NETWORK_ATTR:
try:
return self.client().show_network(
self._show_resource().get('network_id'))['network']
except Exception as ex:
LOG.warning("Failed to fetch resource attributes: %s", ex)
return
return super(Port, self)._resolve_attribute(name)
def needs_replace(self, after_props):

View File

@ -80,6 +80,8 @@ class NeutronPortTest(common.HeatTestCase):
neutronclient.Client, 'update_port')
self.subnet_show_mock = self.patchobject(
neutronclient.Client, 'show_subnet')
self.network_show_mock = self.patchobject(
neutronclient.Client, 'show_network')
self.find_mock = self.patchobject(
neutronV20, 'find_resourceid_by_name_or_id')
@ -568,13 +570,26 @@ class NeutronPortTest(common.HeatTestCase):
'end': u'10.0.0.254'}],
'gateway_ip': '10.0.0.1', 'ipv6_address_mode': None,
'ip_version': 4, 'host_routes': [],
'id': '6dd609ad-d52a-4587-b1a0-b335f76062a5'}
'id': 'd0e971a6-a6b4-4f4c-8c88-b75e9c120b7e'}
network_dict = {'name': 'test-network', 'status': 'ACTIVE',
'router:external': False,
'availability_zone_hints': [],
'availability_zones': ['nova'],
'ipv4_address_scope': None, 'description': '',
'subnets': [subnet_dict['id']],
'port_security_enabled': True,
'tenant_id': '58a61fc3992944ce971404a2ece6ff98',
'tags': [], 'ipv6_address_scope': None,
'project_id': '58a61fc3992944ce971404a2ece6ff98',
'revision_number': 4, 'admin_state_up': True,
'shared': False, 'mtu': 1450, 'id': 'net1234'}
self.find_mock.return_value = 'net1234'
self.create_mock.return_value = {'port': {
'status': 'BUILD',
'id': 'fc68ea2c-b60b-4b4f-bd82-94ec81110766'
}}
self.subnet_show_mock.return_value = {'subnet': subnet_dict}
self.network_show_mock.return_value = {'network': network_dict}
self.port_show_mock.return_value = {'port': {
'status': 'DOWN',
'name': utils.PhysName(stack.name, 'port'),
@ -616,6 +631,7 @@ class NeutronPortTest(common.HeatTestCase):
'ip_address': '10.0.0.2'}],
port.FnGetAtt('fixed_ips'))
self.assertEqual([subnet_dict], port.FnGetAtt('subnets'))
self.assertEqual(network_dict, port.FnGetAtt('network'))
self.assertRaises(exception.InvalidTemplateAttribute,
port.FnGetAtt, 'Foo')
@ -661,6 +677,48 @@ class NeutronPortTest(common.HeatTestCase):
'device_id': ''}}
)
def test_network_attribute_exception(self):
t = template_format.parse(neutron_port_template)
t['resources']['port']['properties'].pop('fixed_ips')
stack = utils.parse_stack(t)
self.find_mock.return_value = 'net1234'
self.create_mock.return_value = {'port': {
'status': 'BUILD',
'id': 'fc68ea2c-b60b-4b4f-bd82-94ec81110766'
}}
self.port_show_mock.return_value = {'port': {
'status': 'DOWN',
'name': utils.PhysName(stack.name, 'port'),
'allowed_address_pairs': [],
'admin_state_up': True,
'network_id': 'net1234',
'device_id': 'dc68eg2c-b60g-4b3f-bd82-67ec87650532',
'mac_address': 'fa:16:3e:75:67:60',
'tenant_id': '58a61fc3992944ce971404a2ece6ff98',
'security_groups': ['5b15d80c-6b70-4a1c-89c9-253538c5ade6'],
'fixed_ips': [{'subnet_id': 'd0e971a6-a6b4-4f4c-8c88-b75e9c120b7e',
'ip_address': '10.0.0.2'}]
}}
self.network_show_mock.side_effect = (qe.NeutronClientException(
'ConnectionFailed: Connection to neutron failed: Maximum '
'attempts reached'))
port = stack['port']
scheduler.TaskRunner(port.create)()
self.assertIsNone(port.FnGetAtt('network'))
log_msg = ('Failed to fetch resource attributes: ConnectionFailed: '
'Connection to neutron failed: Maximum attempts reached')
self.assertIn(log_msg, self.LOG.output)
self.create_mock.assert_called_once_with({'port': {
'network_id': u'net1234',
'name': utils.PhysName(stack.name, 'port'),
'admin_state_up': True,
'device_owner': u'network:dhcp',
'binding:vnic_type': 'normal',
'device_id': ''}}
)
def test_prepare_for_replace_port_not_created(self):
t = template_format.parse(neutron_port_template)
stack = utils.parse_stack(t)

View File

@ -0,0 +1,13 @@
---
features:
- |
Added ``network`` attribute to OS::Neutron::Port resource. The new
attribute returns the neutron network that owns the port. The following
examples demonstrate some (not all) possible expressions. (Obtains the
network, the MTU (Maximum transmission unit), the network tags and finally
the l2_adjacency property)::
{get_attr: [<port>, network]}
{get_attr: [<port>, network, mtu]}
{get_attr: [<port>, network, tags]}
{get_attr: [<port>, network, l2_adjacency]}