diff --git a/heat/engine/resources/openstack/neutron/port.py b/heat/engine/resources/openstack/neutron/port.py index 82b7302be..da0087992 100644 --- a/heat/engine/resources/openstack/neutron/port.py +++ b/heat/engine/resources/openstack/neutron/port.py @@ -44,9 +44,11 @@ class Port(neutron.NeutronResource): PROPERTIES = ( NAME, NETWORK_ID, NETWORK, FIXED_IPS, SECURITY_GROUPS, REPLACEMENT_POLICY, DEVICE_ID, DEVICE_OWNER, DNS_NAME, + TAGS, ) = ( 'name', 'network_id', 'network', 'fixed_ips', 'security_groups', 'replacement_policy', 'device_id', 'device_owner', 'dns_name', + 'tags', ) EXTRA_PROPERTIES = ( @@ -209,6 +211,13 @@ class Port(neutron.NeutronResource): ], support_status=support.SupportStatus(version='7.0.0'), ), + TAGS: properties.Schema( + properties.Schema.LIST, + _('The tags to be added to the port.'), + schema=properties.Schema(properties.Schema.STRING), + update_allowed=True, + support_status=support.SupportStatus(version='9.0.0') + ), } # NOTE(prazumovsky): properties_schema has been separated because some @@ -422,6 +431,7 @@ class Port(neutron.NeutronResource): props['network_id'] = props.pop(self.NETWORK) self._prepare_port_properties(props) qos_policy = props.pop(self.QOS_POLICY, None) + tags = props.pop(self.TAGS, []) if qos_policy: props['qos_policy_id'] = self.client_plugin().get_qos_policy_id( qos_policy) @@ -429,6 +439,9 @@ class Port(neutron.NeutronResource): port = self.client().create_port({'port': props})['port'] self.resource_id_set(port['id']) + if tags: + 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] @@ -518,6 +531,11 @@ class Port(neutron.NeutronResource): qos_policy = prop_diff.pop(self.QOS_POLICY) prop_diff['qos_policy_id'] = self.client_plugin( ).get_qos_policy_id(qos_policy) if qos_policy else None + + if self.TAGS in prop_diff: + tags = prop_diff.pop(self.TAGS) + self.set_tags(tags) + self._prepare_port_properties(prop_diff, prepare_for_update=True) LOG.debug('updating port with %s', prop_diff) self.client().update_port(self.resource_id, {'port': prop_diff}) diff --git a/heat/tests/openstack/neutron/test_neutron_port.py b/heat/tests/openstack/neutron/test_neutron_port.py index b7a840e94..a417d82a7 100644 --- a/heat/tests/openstack/neutron/test_neutron_port.py +++ b/heat/tests/openstack/neutron/test_neutron_port.py @@ -288,6 +288,30 @@ class NeutronPortTest(common.HeatTestCase): }} + def test_create_with_tags(self): + t = template_format.parse(neutron_port_template) + t['resources']['port']['properties']['tags'] = ['tag1', 'tag2'] + stack = utils.parse_stack(t) + + port_prop = { + 'network_id': u'net_or_sub', + 'fixed_ips': [ + {'subnet_id': u'net_or_sub', 'ip_address': u'10.0.3.21'} + ], + 'name': utils.PhysName(stack.name, 'port'), + 'admin_state_up': True, + 'device_owner': u'network:dhcp'} + + set_tag_mock = self.patchobject(neutronclient.Client, 'replace_tag') + self._mock_create_with_props() + + port = stack['port'] + scheduler.TaskRunner(port.create)() + self.assertEqual((port.CREATE, port.COMPLETE), port.state) + self.create_mock.assert_called_once_with({'port': port_prop}) + set_tag_mock.assert_called_with('ports', port.resource_id, + {'tags': ['tag1', 'tag2']}) + def test_security_groups(self): t = template_format.parse(neutron_port_template) t['resources']['port']['properties']['security_groups'] = [ @@ -801,6 +825,7 @@ class UpdatePortTest(common.HeatTestCase): } self.patchobject(neutronclient.Client, 'list_security_groups', return_value=fake_groups_list) + set_tag_mock = self.patchobject(neutronclient.Client, 'replace_tag') props = {'network_id': u'net1234', 'name': str(utils.PhysName(stack.name, 'port')), @@ -810,6 +835,7 @@ class UpdatePortTest(common.HeatTestCase): update_props = props.copy() update_props['security_groups'] = self.secgrp update_props['value_specs'] = self.value_specs + update_props['tags'] = ['test_tag'] if self.fixed_ips: update_props['fixed_ips'] = self.fixed_ips update_props['allowed_address_pairs'] = self.addr_pair @@ -828,6 +854,8 @@ class UpdatePortTest(common.HeatTestCase): for value_spec in six.iteritems(value_specs): update_dict[value_spec[0]] = value_spec[1] + tags = update_dict.pop('tags') + # create port port = stack['port'] self.assertIsNone(scheduler.TaskRunner(port.handle_create)()) @@ -840,7 +868,8 @@ class UpdatePortTest(common.HeatTestCase): update_props)()) update_port.assset_called_once_with(update_dict) - + set_tag_mock.assert_called_with('ports', port.resource_id, + {'tags': tags}) # check, that update does not cause of Update Replace create_snippet = rsrc_defn.ResourceDefinition(port.name, port.type(), props) diff --git a/releasenotes/notes/set-tags-for-port-471155bb53436361.yaml b/releasenotes/notes/set-tags-for-port-471155bb53436361.yaml new file mode 100644 index 000000000..ce40a3979 --- /dev/null +++ b/releasenotes/notes/set-tags-for-port-471155bb53436361.yaml @@ -0,0 +1,3 @@ +--- +features: + - Allow to set or update the tags for OS::Neutron::Port resource.