heat/heat/tests/openstack/neutron/test_neutron_port.py

1165 lines
46 KiB
Python

#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
from neutronclient.common import exceptions as qe
from neutronclient.neutron import v2_0 as neutronV20
from neutronclient.v2_0 import client as neutronclient
from oslo_serialization import jsonutils
from heat.common import exception
from heat.common import template_format
from heat.engine import resource
from heat.engine import rsrc_defn
from heat.engine import scheduler
from heat.tests import common
from heat.tests import utils
neutron_port_template = '''
heat_template_version: 2015-04-30
description: Template to test port Neutron resource
resources:
port:
type: OS::Neutron::Port
properties:
network: net1234
fixed_ips:
- subnet: sub1234
ip_address: 10.0.3.21
device_owner: network:dhcp
'''
neutron_port_with_address_pair_template = '''
heat_template_version: 2015-04-30
description: Template to test port Neutron resource
resources:
port:
type: OS::Neutron::Port
properties:
network: abcd1234
allowed_address_pairs:
- ip_address: 10.0.3.21/8
mac_address: 00-B0-D0-86-BB-F7
'''
neutron_port_security_template = '''
heat_template_version: 2015-04-30
description: Template to test port Neutron resource
resources:
port:
type: OS::Neutron::Port
properties:
network: abcd1234
port_security_enabled: False
'''
neutron_port_propagate_ul_status_template = '''
heat_template_version: 2015-04-30
description: Template to test port Neutron resource
resources:
port:
type: OS::Neutron::Port
properties:
network: abcd1234
propagate_uplink_status: True
'''
class NeutronPortTest(common.HeatTestCase):
def setUp(self):
super(NeutronPortTest, self).setUp()
self.create_mock = self.patchobject(
neutronclient.Client, 'create_port')
self.port_show_mock = self.patchobject(
neutronclient.Client, 'show_port')
self.update_mock = self.patchobject(
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')
def test_missing_network(self):
t = template_format.parse(neutron_port_template)
t['resources']['port']['properties'] = {}
stack = utils.parse_stack(t)
port = stack['port']
self.assertRaises(exception.StackValidationFailed, port.validate)
def test_missing_subnet_id(self):
t = template_format.parse(neutron_port_template)
t['resources']['port']['properties']['fixed_ips'][0].pop('subnet')
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": "ACTIVE",
"id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766"}}
port = stack['port']
scheduler.TaskRunner(port.create)()
self.assertEqual((port.CREATE, port.COMPLETE), port.state)
self.create_mock.assert_called_once_with({'port': {
'network_id': u'net1234',
'fixed_ips': [
{'ip_address': u'10.0.3.21'}
],
'name': utils.PhysName(stack.name, 'port'),
'admin_state_up': True,
'device_owner': u'network:dhcp',
'binding:vnic_type': 'normal',
'device_id': ''
}})
self.port_show_mock.assert_called_once_with(
'fc68ea2c-b60b-4b4f-bd82-94ec81110766')
def test_missing_ip_address(self):
t = template_format.parse(neutron_port_template)
t['resources']['port']['properties']['fixed_ips'][0].pop('ip_address')
stack = utils.parse_stack(t)
self.find_mock.return_value = 'net_or_sub'
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.assertEqual((port.CREATE, port.COMPLETE), port.state)
self.create_mock.assert_called_once_with({'port': {
'network_id': u'net_or_sub',
'fixed_ips': [
{'subnet_id': u'net_or_sub'}
],
'name': utils.PhysName(stack.name, 'port'),
'admin_state_up': True,
'device_owner': u'network:dhcp',
'binding:vnic_type': 'normal',
'device_id': ''
}})
self.port_show_mock.assert_called_once_with(
'fc68ea2c-b60b-4b4f-bd82-94ec81110766')
def test_missing_fixed_ips(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": "ACTIVE",
"id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766",
"fixed_ips": {
"subnet_id": "d0e971a6-a6b4-4f4c-8c88-b75e9c120b7e",
"ip_address": "10.0.0.2"
}
}}
port = stack['port']
scheduler.TaskRunner(port.create)()
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_allowed_address_pair(self):
t = template_format.parse(neutron_port_with_address_pair_template)
t['resources']['port']['properties'][
'allowed_address_pairs'][0]['ip_address'] = '10.0.3.21'
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',
'allowed_address_pairs': [{
'ip_address': u'10.0.3.21',
'mac_address': u'00-B0-D0-86-BB-F7'
}],
'name': utils.PhysName(stack.name, 'port'),
'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)
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',
'port_security_enabled': False,
'name': utils.PhysName(stack.name, 'port'),
'admin_state_up': True,
'binding:vnic_type': 'normal',
'device_id': '',
'device_owner': ''
}})
def test_port_propagate_uplink_status(self):
t = template_format.parse(neutron_port_propagate_ul_status_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',
'propagate_uplink_status': True,
'name': utils.PhysName(stack.name, 'port'),
'admin_state_up': True,
'binding:vnic_type': 'normal',
'device_id': '',
'device_owner': ''
}})
def test_missing_mac_address(self):
t = template_format.parse(neutron_port_with_address_pair_template)
t['resources']['port']['properties']['allowed_address_pairs'][0].pop(
'mac_address'
)
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',
'allowed_address_pairs': [{
'ip_address': u'10.0.3.21/8',
}],
'name': utils.PhysName(stack.name, 'port'),
'admin_state_up': True,
'binding:vnic_type': 'normal',
'device_owner': '',
'device_id': ''}})
def test_ip_address_is_cidr(self):
t = template_format.parse(neutron_port_with_address_pair_template)
t['resources']['port']['properties'][
'allowed_address_pairs'][0]['ip_address'] = '10.0.3.0/24'
stack = utils.parse_stack(t)
self.find_mock.return_value = 'abcd1234'
self.create_mock.return_value = {'port': {
"status": "BUILD",
"id": "2e00180a-ff9d-42c4-b701-a0606b243447"
}}
self.port_show_mock.return_value = {'port': {
"status": "ACTIVE",
"id": "2e00180a-ff9d-42c4-b701-a0606b243447"
}}
port = stack['port']
scheduler.TaskRunner(port.create)()
self.create_mock.assert_called_once_with({'port': {
'network_id': u'abcd1234',
'allowed_address_pairs': [{
'ip_address': u'10.0.3.0/24',
'mac_address': u'00-B0-D0-86-BB-F7'
}],
'name': utils.PhysName(stack.name, 'port'),
'admin_state_up': True,
'binding:vnic_type': 'normal',
'device_owner': '',
'device_id': ''
}})
def _mock_create_with_props(self):
self.find_mock.return_value = 'net_or_sub'
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",
"dns_assignment": {
"hostname": "my-vm",
"ip_address": "10.0.0.15",
"fqdn": "my-vm.openstack.org."}
}}
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',
'binding:vnic_type': 'normal',
'device_id': ''
}
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'] = [
'8a2f582a-e1cd-480f-b85d-b02631c10656',
'024613dc-b489-4478-b46f-ada462738740']
stack = utils.parse_stack(t)
port_prop = {
'network_id': u'net_or_sub',
'security_groups': ['8a2f582a-e1cd-480f-b85d-b02631c10656',
'024613dc-b489-4478-b46f-ada462738740'],
'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',
'binding:vnic_type': 'normal',
'device_id': ''
}
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})
def test_port_with_dns_name(self):
t = template_format.parse(neutron_port_template)
t['resources']['port']['properties']['dns_name'] = 'myvm'
stack = utils.parse_stack(t)
port_prop = {
'network_id': u'net_or_sub',
'dns_name': 'myvm',
'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',
'binding:vnic_type': 'normal',
'device_id': ''
}
self._mock_create_with_props()
port = stack['port']
scheduler.TaskRunner(port.create)()
self.assertEqual('my-vm.openstack.org.',
port.FnGetAtt('dns_assignment')['fqdn'])
self.assertEqual((port.CREATE, port.COMPLETE), port.state)
self.create_mock.assert_called_once_with({'port': port_prop})
def test_security_groups_empty_list(self):
t = template_format.parse(neutron_port_template)
t['resources']['port']['properties']['security_groups'] = []
stack = utils.parse_stack(t)
port_prop = {
'network_id': u'net_or_sub',
'security_groups': [],
'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',
'binding:vnic_type': 'normal',
'device_id': ''
}
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})
def test_update_failed_port_no_replace(self):
t = template_format.parse(neutron_port_template)
stack = utils.parse_stack(t)
port = stack['port']
port.resource_id = 'r_id'
port.state_set(port.CREATE, port.FAILED)
new_props = port.properties.data.copy()
new_props['name'] = 'new_one'
self.find_mock.return_value = 'net_or_sub'
self.port_show_mock.return_value = {'port': {
"status": "ACTIVE",
"id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766",
"fixed_ips": {
"subnet_id": "d0e971a6-a6b4-4f4c-8c88-b75e9c120b7e",
"ip_address": "10.0.3.21"}}}
update_snippet = rsrc_defn.ResourceDefinition(port.name, port.type(),
new_props)
scheduler.TaskRunner(port.update, update_snippet)()
self.assertEqual((port.UPDATE, port.COMPLETE), port.state)
self.assertEqual(1, self.update_mock.call_count)
def test_port_needs_update(self):
t = template_format.parse(neutron_port_template)
t['resources']['port']['properties'].pop('fixed_ips')
stack = utils.parse_stack(t)
props = {'network_id': u'net1234',
'name': utils.PhysName(stack.name, 'port'),
'admin_state_up': True,
'device_owner': u'network:dhcp',
'device_id': '',
'binding:vnic_type': 'normal'}
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": "ACTIVE",
"id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766",
"fixed_ips": {
"subnet_id": "d0e971a6-a6b4-4f4c-8c88-b75e9c120b7e",
"ip_address": "10.0.0.2"
}
}}
# create port
port = stack['port']
scheduler.TaskRunner(port.create)()
self.create_mock.assert_called_once_with({'port': props})
new_props = props.copy()
# test always replace
new_props['replacement_policy'] = 'REPLACE_ALWAYS'
new_props['network'] = new_props.pop('network_id')
update_snippet = rsrc_defn.ResourceDefinition(port.name, port.type(),
new_props)
self.assertRaises(resource.UpdateReplace, port._needs_update,
update_snippet, port.frozen_definition(),
new_props, port.properties, None)
# test deferring to Resource._needs_update
new_props['replacement_policy'] = 'AUTO'
update_snippet = rsrc_defn.ResourceDefinition(port.name, port.type(),
new_props)
self.assertTrue(port._needs_update(update_snippet,
port.frozen_definition(),
new_props, port.properties, None))
def test_port_needs_update_network(self):
net1 = '9cfe6c74-c105-4906-9a1f-81d9064e9bca'
net2 = '0064eec9-5681-4ba7-a745-6f8e32db9503'
props = {'network_id': net1,
'name': 'test_port',
'device_owner': u'network:dhcp',
'binding:vnic_type': 'normal',
'device_id': ''
}
create_kwargs = props.copy()
create_kwargs['admin_state_up'] = True
self.find_mock.side_effect = [net1] * 8 + [net2] * 2 + [net1]
self.create_mock.return_value = {'port': {
"status": "ACTIVE",
"id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766"
}}
self.port_show_mock.return_value = {'port': {
"status": "ACTIVE",
"id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766",
"fixed_ips": {
"subnet_id": "d0e971a6-a6b4-4f4c-8c88-b75e9c120b7e",
"ip_address": "10.0.0.2"
}
}}
# create port with network_id
tmpl = neutron_port_template.replace(
'network: net1234',
'network_id: 9cfe6c74-c105-4906-9a1f-81d9064e9bca')
t = template_format.parse(tmpl)
t['resources']['port']['properties'].pop('fixed_ips')
t['resources']['port']['properties']['name'] = 'test_port'
stack = utils.parse_stack(t)
port = stack['port']
scheduler.TaskRunner(port.create)()
self.assertEqual((port.CREATE, port.COMPLETE), port.state)
self.create_mock.assert_called_once_with({'port': create_kwargs})
# Switch from network_id=ID to network=ID (no replace)
new_props = props.copy()
new_props['network'] = new_props.pop('network_id')
update_snippet = rsrc_defn.ResourceDefinition(port.name, port.type(),
new_props)
scheduler.TaskRunner(port.update, update_snippet)()
self.assertEqual((port.UPDATE, port.COMPLETE), port.state)
self.assertEqual(0, self.update_mock.call_count)
# Switch from network=ID to network=NAME (no replace)
new_props['network'] = 'net1234'
update_snippet = rsrc_defn.ResourceDefinition(port.name, port.type(),
new_props)
scheduler.TaskRunner(port.update, update_snippet)()
self.assertEqual((port.UPDATE, port.COMPLETE), port.state)
self.assertEqual(0, self.update_mock.call_count)
# Switch to a different network (replace)
new_props['network'] = 'net5678'
update_snippet = rsrc_defn.ResourceDefinition(port.name, port.type(),
new_props)
updater = scheduler.TaskRunner(port.update, update_snippet)
self.assertRaises(resource.UpdateReplace, updater)
self.assertEqual(11, self.find_mock.call_count)
def test_get_port_attributes(self):
t = template_format.parse(neutron_port_template)
t['resources']['port']['properties'].pop('fixed_ips')
stack = utils.parse_stack(t)
subnet_dict = {'name': 'test-subnet', 'enable_dhcp': True,
'network_id': 'net1234', 'dns_nameservers': [],
'tenant_id': '58a61fc3992944ce971404a2ece6ff98',
'ipv6_ra_mode': None, 'cidr': '10.0.0.0/24',
'allocation_pools': [{'start': '10.0.0.2',
'end': u'10.0.0.254'}],
'gateway_ip': '10.0.0.1', 'ipv6_address_mode': None,
'ip_version': 4, 'host_routes': [],
'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,
'propagate_uplink_status': 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'),
'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'}]
}}
port = stack['port']
scheduler.TaskRunner(port.create)()
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': ''
}})
self.assertEqual('DOWN', port.FnGetAtt('status'))
self.assertEqual([], port.FnGetAtt('allowed_address_pairs'))
self.assertTrue(port.FnGetAtt('admin_state_up'))
self.assertEqual('net1234', port.FnGetAtt('network_id'))
self.assertEqual('fa:16:3e:75:67:60', port.FnGetAtt('mac_address'))
self.assertEqual(utils.PhysName(stack.name, 'port'),
port.FnGetAtt('name'))
self.assertEqual('dc68eg2c-b60g-4b3f-bd82-67ec87650532',
port.FnGetAtt('device_id'))
self.assertEqual('58a61fc3992944ce971404a2ece6ff98',
port.FnGetAtt('tenant_id'))
self.assertEqual(['5b15d80c-6b70-4a1c-89c9-253538c5ade6'],
port.FnGetAtt('security_groups'))
self.assertEqual([{'subnet_id': 'd0e971a6-a6b4-4f4c-8c88-b75e9c120b7e',
'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')
def test_subnet_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.subnet_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('subnets'))
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_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)
port = stack['port']
port._show_resource = mock.Mock()
port.data_set = mock.Mock()
n_client = mock.Mock()
port.client = mock.Mock(return_value=n_client)
self.assertIsNone(port.resource_id)
# execute prepare_for_replace
port.prepare_for_replace()
# check, if the port is not created, do nothing in
# prepare_for_replace()
self.assertFalse(port._show_resource.called)
self.assertFalse(port.data_set.called)
self.assertFalse(n_client.update_port.called)
def test_prepare_for_replace_port_not_found(self):
t = template_format.parse(neutron_port_template)
stack = utils.parse_stack(t)
port = stack['port']
port.resource_id = 'test_res_id'
port._show_resource = mock.Mock(side_effect=qe.NotFound)
port.data_set = mock.Mock()
n_client = mock.Mock()
port.client = mock.Mock(return_value=n_client)
# execute prepare_for_replace
port.prepare_for_replace()
# check, if the port is not found, do nothing in
# prepare_for_replace()
self.assertTrue(port._show_resource.called)
self.assertFalse(port.data_set.called)
self.assertFalse(n_client.update_port.called)
def test_prepare_for_replace_port(self):
t = template_format.parse(neutron_port_template)
stack = utils.parse_stack(t)
port = stack['port']
port.resource_id = 'test_res_id'
_value = {
'fixed_ips': {
'subnet_id': 'test_subnet',
'ip_address': '42.42.42.42'
}
}
port._show_resource = mock.Mock(return_value=_value)
port.data_set = mock.Mock()
n_client = mock.Mock()
port.client = mock.Mock(return_value=n_client)
# execute prepare_for_replace
port.prepare_for_replace()
# check, that data was stored
port.data_set.assert_called_once_with(
'port_fip', jsonutils.dumps(_value.get('fixed_ips')))
# check, that port was updated and ip was removed
expected_props = {'port': {'fixed_ips': []}}
n_client.update_port.assert_called_once_with('test_res_id',
expected_props)
def test_restore_prev_rsrc(self):
t = template_format.parse(neutron_port_template)
stack = utils.parse_stack(t)
new_port = stack['port']
new_port.resource_id = 'new_res_id'
# mock backup stack to return only one mocked old_port
old_port = mock.Mock()
new_port.stack._backup_stack = mock.Mock()
new_port.stack._backup_stack().resources.get.return_value = old_port
old_port.resource_id = 'old_res_id'
_value = {
'subnet_id': 'test_subnet',
'ip_address': '42.42.42.42'
}
old_port.data = mock.Mock(
return_value={'port_fip': jsonutils.dumps(_value)})
n_client = mock.Mock()
new_port.client = mock.Mock(return_value=n_client)
# execute restore_prev_rsrc
new_port.restore_prev_rsrc()
# check, that ports were updated: old port get ip and
# same ip was removed from old port
expected_new_props = {'port': {'fixed_ips': []}}
expected_old_props = {'port': {'fixed_ips': _value}}
n_client.update_port.assert_has_calls([
mock.call('new_res_id', expected_new_props),
mock.call('old_res_id', expected_old_props)])
def test_restore_prev_rsrc_convergence(self):
t = template_format.parse(neutron_port_template)
stack = utils.parse_stack(t)
stack.store()
# mock resource from previous template
prev_rsrc = stack['port']
prev_rsrc.resource_id = 'prev-rsrc'
# store in db
prev_rsrc.state_set(prev_rsrc.UPDATE, prev_rsrc.COMPLETE)
# mock resource from existing template and store in db
existing_rsrc = stack['port']
existing_rsrc.current_template_id = stack.t.id
existing_rsrc.resource_id = 'existing-rsrc'
existing_rsrc.state_set(existing_rsrc.UPDATE, existing_rsrc.COMPLETE)
# mock previous resource was replaced by existing resource
prev_rsrc.replaced_by = existing_rsrc.id
_value = {
'subnet_id': 'test_subnet',
'ip_address': '42.42.42.42'
}
prev_rsrc._data = {'port_fip': jsonutils.dumps(_value)}
n_client = mock.Mock()
prev_rsrc.client = mock.Mock(return_value=n_client)
# execute restore_prev_rsrc
prev_rsrc.restore_prev_rsrc(convergence=True)
expected_existing_props = {'port': {'fixed_ips': []}}
expected_prev_props = {'port': {'fixed_ips': _value}}
n_client.update_port.assert_has_calls([
mock.call(existing_rsrc.resource_id, expected_existing_props),
mock.call(prev_rsrc.resource_id, expected_prev_props)])
def test_port_get_live_state(self):
t = template_format.parse(neutron_port_template)
t['resources']['port']['properties']['value_specs'] = {
'binding:vif_type': 'test'}
stack = utils.parse_stack(t)
port = stack['port']
resp = {'port': {
'status': 'DOWN',
'binding:host_id': '',
'name': 'flip-port-xjbal77qope3',
'allowed_address_pairs': [],
'admin_state_up': True,
'network_id': 'd6859535-efef-4184-b236-e5fcae856e0f',
'dns_name': '',
'extra_dhcp_opts': [],
'mac_address': 'fa:16:3e:fe:64:79',
'qos_policy_id': 'some',
'dns_assignment': [],
'binding:vif_details': {},
'binding:vif_type': 'unbound',
'device_owner': '',
'tenant_id': '30f466e3d14b4251853899f9c26e2b66',
'binding:profile': {},
'port_security_enabled': True,
'propagate_uplink_status': True,
'binding:vnic_type': 'normal',
'fixed_ips': [
{'subnet_id': '02d9608f-8f30-4611-ad02-69855c82457f',
'ip_address': '10.0.3.4'}],
'id': '829bf5c1-b59c-40ad-80e3-ea15a93879f3',
'security_groups': ['c276247f-50fd-4289-862a-80fb81a55de1'],
'device_id': ''}
}
port.client().show_port = mock.MagicMock(return_value=resp)
port.resource_id = '1234'
port._data = {}
port.data_set = mock.Mock()
reality = port.get_live_state(port.properties)
expected = {
'allowed_address_pairs': [],
'admin_state_up': True,
'device_owner': '',
'port_security_enabled': True,
'propagate_uplink_status': True,
'binding:vnic_type': 'normal',
'fixed_ips': [
{'subnet': '02d9608f-8f30-4611-ad02-69855c82457f',
'ip_address': '10.0.3.4'}],
'security_groups': ['c276247f-50fd-4289-862a-80fb81a55de1'],
'device_id': '',
'dns_name': '',
'qos_policy': 'some',
'value_specs': {'binding:vif_type': 'unbound'}
}
self.assertEqual(set(expected.keys()), set(reality.keys()))
for key in expected:
self.assertEqual(expected[key], reality[key])
class UpdatePortTest(common.HeatTestCase):
scenarios = [
('with_secgrp', dict(secgrp=['8a2f582a-e1cd-480f-b85d-b02631c10656'],
name='test',
value_specs={},
fixed_ips=None,
addr_pair=None,
vnic_type=None)),
('with_no_name', dict(secgrp=['8a2f582a-e1cd-480f-b85d-b02631c10656'],
orig_name='original',
name=None,
value_specs={},
fixed_ips=None,
addr_pair=None,
vnic_type=None)),
('with_empty_values', dict(secgrp=[],
name='test',
value_specs={},
fixed_ips=[],
addr_pair=[],
vnic_type=None)),
('with_fixed_ips', dict(secgrp=None,
value_specs={},
fixed_ips=[
{"subnet_id": "d0e971a6-a6b4-4f4c",
"ip_address": "10.0.0.2"}],
addr_pair=None,
vnic_type=None)),
('with_addr_pair', dict(secgrp=None,
value_specs={},
fixed_ips=None,
addr_pair=[{'ip_address': '10.0.3.21',
'mac_address': '00-B0-D0-86'}],
vnic_type=None)),
('with_value_specs', dict(secgrp=None,
value_specs={'binding:vnic_type': 'direct'},
fixed_ips=None,
addr_pair=None,
vnic_type=None)),
('normal_vnic', dict(secgrp=None,
value_specs={},
fixed_ips=None,
addr_pair=None,
vnic_type='normal')),
('direct_vnic', dict(secgrp=None,
value_specs={},
fixed_ips=None,
addr_pair=None,
vnic_type='direct')),
('physical_direct_vnic', dict(secgrp=None,
value_specs={},
fixed_ips=None,
addr_pair=None,
vnic_type='direct-physical')),
('baremetal_vnic', dict(secgrp=None,
value_specs={},
fixed_ips=None,
addr_pair=None,
vnic_type='baremetal')),
('virtio_forwarder_vnic', dict(secgrp=None,
value_specs={},
fixed_ips=None,
addr_pair=None,
vnic_type='virtio-forwarder')),
('with_all', dict(secgrp=['8a2f582a-e1cd-480f-b85d-b02631c10656'],
value_specs={},
fixed_ips=[
{"subnet_id": "d0e971a6-a6b4-4f4c",
"ip_address": "10.0.0.2"}],
addr_pair=[{'ip_address': '10.0.3.21',
'mac_address': '00-B0-D0-86-BB-F7'}],
vnic_type='normal')),
]
def test_update_port(self):
t = template_format.parse(neutron_port_template)
create_name = getattr(self, 'orig_name', None)
if create_name is not None:
t['resources']['port']['properties']['name'] = create_name
stack = utils.parse_stack(t)
def res_id(client, resource, name_or_id, cmd_resource=None):
return {
'network': 'net1234',
'subnet': 'sub1234',
}[resource]
self.patchobject(neutronV20, 'find_resourceid_by_name_or_id',
side_effect=res_id)
create_port_result = {
'port': {
"status": "BUILD",
"id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766"
}
}
show_port_result = {
'port': {
"status": "ACTIVE",
"id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766",
"fixed_ips": {
"subnet_id": "d0e971a6-a6b4-4f4c-8c88-b75e9c120b7e",
"ip_address": "10.0.0.2"
}
}
}
create_port = self.patchobject(neutronclient.Client, 'create_port',
return_value=create_port_result)
update_port = self.patchobject(neutronclient.Client, 'update_port')
show_port = self.patchobject(neutronclient.Client, 'show_port',
return_value=show_port_result)
fake_groups_list = {
'security_groups': [
{
'tenant_id': 'dc4b074874244f7693dd65583733a758',
'id': '0389f747-7785-4757-b7bb-2ab07e4b09c3',
'name': 'default',
'security_group_rules': [],
'description': 'no protocol'
}
]
}
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',
'fixed_ips': [{'subnet_id': 'sub1234',
'ip_address': '10.0.3.21'}],
'name': (create_name if create_name is not None else
utils.PhysName(stack.name, 'port')),
'admin_state_up': True,
'device_owner': u'network:dhcp',
'device_id': '',
'binding:vnic_type': 'normal'}
update_props = props.copy()
update_props['name'] = getattr(self, 'name', create_name)
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
update_props['binding:vnic_type'] = self.vnic_type
update_dict = update_props.copy()
if update_props['allowed_address_pairs'] is None:
update_dict['allowed_address_pairs'] = []
if update_props['security_groups'] is None:
update_dict['security_groups'] = [
'0389f747-7785-4757-b7bb-2ab07e4b09c3',
]
if update_props['name'] is None:
update_dict['name'] = utils.PhysName(stack.name, 'port')
value_specs = update_dict.pop('value_specs')
if value_specs:
for value_spec in value_specs.items():
update_dict[value_spec[0]] = value_spec[1]
tags = update_dict.pop('tags')
# create port
port = stack['port']
self.assertIsNone(scheduler.TaskRunner(port.create)())
create_port.assert_called_once_with({'port': props})
# update port
update_snippet = rsrc_defn.ResourceDefinition(port.name, port.type(),
update_props)
self.assertIsNone(scheduler.TaskRunner(port.handle_update,
update_snippet, {},
update_props)())
update_port.assert_called_once_with(
'fc68ea2c-b60b-4b4f-bd82-94ec81110766', {'port': 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)
after_props, before_props = port._prepare_update_props(update_snippet,
create_snippet)
self.assertIsNotNone(
port.update_template_diff_properties(after_props, before_props))
# With fixed_ips removed
scheduler.TaskRunner(port.handle_update, update_snippet,
{}, {'fixed_ips': None})()
# update with empty prop_diff
scheduler.TaskRunner(port.handle_update, update_snippet, {}, {})()
self.assertEqual(1, update_port.call_count)
show_port.assert_called_with('fc68ea2c-b60b-4b4f-bd82-94ec81110766')