deb-heat/heat/tests/test_eip.py
Thomas Herve 84640e7a7a Only raise UpdateReplace when properties change
This changes the default behavior of Resource.handle_update to only
raise UpdateReplace if the properties are different. It allows updating
DeletionPolicy cleanly without deleting the resource.

Co-Authored-By: ala.rezmerita@cloudwatt.com
Closes-Bug: #1316171
Change-Id: I1d2864dc44ff7a5b4536b26f2dba525e01e56af0
2014-05-22 18:50:22 +02:00

552 lines
19 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.
import mox
from testtools import skipIf
from heat.common import exception
from heat.common import template_format
from heat.engine import clients
from heat.engine import parser
from heat.engine.resources import eip
from heat.engine import scheduler
from heat.tests.common import HeatTestCase
from heat.tests import fakes as fakec
from heat.tests import utils
from heat.tests.v1_1 import fakes
eip_template = '''
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "EIP Test",
"Parameters" : {},
"Resources" : {
"IPAddress" : {
"Type" : "AWS::EC2::EIP",
"Properties" : {
"InstanceId" : { "Ref" : "WebServer" }
}
},
"WebServer": {
"Type": "AWS::EC2::Instance",
}
}
}
'''
eip_template_ipassoc = '''
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "EIP Test",
"Parameters" : {},
"Resources" : {
"IPAddress" : {
"Type" : "AWS::EC2::EIP"
},
"IPAssoc" : {
"Type" : "AWS::EC2::EIPAssociation",
"Properties" : {
"InstanceId" : { "Ref" : "WebServer" },
"EIP" : { "Ref" : "IPAddress" }
}
},
"WebServer": {
"Type": "AWS::EC2::Instance",
}
}
}
'''
eip_template_ipassoc2 = '''
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "EIP Test",
"Parameters" : {},
"Resources" : {
"the_eip" : {
"Type" : "AWS::EC2::EIP",
"Properties" : {
"Domain": "vpc"
}
},
"IPAssoc" : {
"Type" : "AWS::EC2::EIPAssociation",
"Properties" : {
"AllocationId" : 'fc68ea2c-b60b-4b4f-bd82-94ec81110766',
"NetworkInterfaceId" : { "Ref" : "the_nic" }
}
},
"the_vpc" : {
"Type" : "AWS::EC2::VPC",
"Properties" : {
"CidrBlock" : "10.0.0.0/16"
}
},
"the_subnet" : {
"Type" : "AWS::EC2::Subnet",
"Properties" : {
"CidrBlock" : "10.0.0.0/24",
"VpcId" : { "Ref" : "the_vpc" }
}
},
"the_nic" : {
"Type" : "AWS::EC2::NetworkInterface",
"Properties" : {
"PrivateIpAddress": "10.0.0.100",
"SubnetId": { "Ref": "the_subnet" }
}
},
}
}
'''
eip_template_ipassoc3 = '''
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "EIP Test",
"Parameters" : {},
"Resources" : {
"the_eip" : {
"Type" : "AWS::EC2::EIP",
"Properties" : {
"Domain": "vpc"
}
},
"IPAssoc" : {
"Type" : "AWS::EC2::EIPAssociation",
"Properties" : {
"AllocationId" : 'fc68ea2c-b60b-4b4f-bd82-94ec81110766',
"InstanceId" : '1fafbe59-2332-4f5f-bfa4-517b4d6c1b65'
}
}
}
}
'''
def force_networking(mode):
if mode == 'nova':
force_networking.client = clients.neutronclient
clients.neutronclient = None
if mode == 'neutron':
clients.neutronclient = force_networking.client
force_networking.client = None
class EIPTest(HeatTestCase):
def setUp(self):
# force Nova, will test Neutron below
force_networking('nova')
super(EIPTest, self).setUp()
self.fc = fakes.FakeClient()
self.m.StubOutWithMock(eip.ElasticIp, 'nova')
self.m.StubOutWithMock(eip.ElasticIpAssociation, 'nova')
self.m.StubOutWithMock(self.fc.servers, 'get')
def tearDown(self):
super(EIPTest, self).tearDown()
force_networking('neutron')
def create_eip(self, t, stack, resource_name):
rsrc = eip.ElasticIp(resource_name,
t['Resources'][resource_name],
stack)
self.assertIsNone(rsrc.validate())
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
return rsrc
def create_association(self, t, stack, resource_name):
rsrc = eip.ElasticIpAssociation(resource_name,
t['Resources'][resource_name],
stack)
self.assertIsNone(rsrc.validate())
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
return rsrc
def test_eip(self):
eip.ElasticIp.nova().MultipleTimes().AndReturn(self.fc)
self.fc.servers.get('WebServer').AndReturn(self.fc.servers.list()[0])
self.fc.servers.get('WebServer')
self.m.ReplayAll()
t = template_format.parse(eip_template)
stack = utils.parse_stack(t)
rsrc = self.create_eip(t, stack, 'IPAddress')
try:
self.assertEqual('11.0.0.1', rsrc.FnGetRefId())
rsrc.refid = None
self.assertEqual('11.0.0.1', rsrc.FnGetRefId())
self.assertEqual('1', rsrc.FnGetAtt('AllocationId'))
self.assertRaises(exception.InvalidTemplateAttribute,
rsrc.FnGetAtt, 'Foo')
finally:
scheduler.TaskRunner(rsrc.destroy)()
self.m.VerifyAll()
def test_association_eip(self):
eip.ElasticIp.nova().MultipleTimes().AndReturn(self.fc)
eip.ElasticIpAssociation.nova().MultipleTimes().AndReturn(self.fc)
self.fc.servers.get('WebServer').MultipleTimes() \
.AndReturn(self.fc.servers.list()[0])
self.m.ReplayAll()
t = template_format.parse(eip_template_ipassoc)
stack = utils.parse_stack(t)
rsrc = self.create_eip(t, stack, 'IPAddress')
association = self.create_association(t, stack, 'IPAssoc')
try:
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
self.assertEqual((association.CREATE, association.COMPLETE),
association.state)
self.assertEqual(utils.PhysName(stack.name, association.name),
association.FnGetRefId())
self.assertEqual('11.0.0.1', association.properties['EIP'])
finally:
scheduler.TaskRunner(association.delete)()
scheduler.TaskRunner(rsrc.delete)()
self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state)
self.assertEqual((association.DELETE, association.COMPLETE),
association.state)
self.m.VerifyAll()
def test_eip_with_exception(self):
self.m.StubOutWithMock(self.fc.floating_ips, 'create')
eip.ElasticIp.nova().MultipleTimes().AndReturn(self.fc)
self.fc.floating_ips.create().AndRaise(
clients.novaclient.exceptions.NotFound('fake_falure'))
self.m.ReplayAll()
t = template_format.parse(eip_template)
stack = utils.parse_stack(t)
resource_name = 'IPAddress'
rsrc = eip.ElasticIp(resource_name,
t['Resources'][resource_name],
stack)
self.assertRaises(clients.novaclient.exceptions.NotFound,
rsrc.handle_create)
self.m.VerifyAll()
def test_delete_eip_with_exception(self):
self.m.StubOutWithMock(self.fc.floating_ips, 'delete')
eip.ElasticIp.nova().MultipleTimes().AndReturn(self.fc)
self.fc.floating_ips.delete(mox.IsA(object)).AndRaise(
clients.novaclient.exceptions.NotFound('fake_falure'))
self.fc.servers.get(mox.IsA(object)).AndReturn(False)
self.m.ReplayAll()
t = template_format.parse(eip_template)
stack = utils.parse_stack(t)
resource_name = 'IPAddress'
rsrc = eip.ElasticIp(resource_name,
t['Resources'][resource_name],
stack)
rsrc.resource_id = 'fake_id'
rsrc.handle_delete()
self.m.VerifyAll()
class AllocTest(HeatTestCase):
@skipIf(clients.neutronclient is None, 'neutronclient unavailable')
def setUp(self):
super(AllocTest, self).setUp()
self.fc = fakes.FakeClient()
self.m.StubOutWithMock(eip.ElasticIp, 'nova')
self.m.StubOutWithMock(eip.ElasticIpAssociation, 'nova')
self.m.StubOutWithMock(self.fc.servers, 'get')
self.m.StubOutWithMock(parser.Stack, 'resource_by_refid')
self.m.StubOutWithMock(clients.neutronclient.Client,
'create_floatingip')
self.m.StubOutWithMock(clients.neutronclient.Client,
'show_floatingip')
self.m.StubOutWithMock(clients.neutronclient.Client,
'update_floatingip')
self.m.StubOutWithMock(clients.neutronclient.Client,
'delete_floatingip')
self.m.StubOutWithMock(clients.neutronclient.Client,
'add_gateway_router')
self.m.StubOutWithMock(clients.neutronclient.Client, 'list_networks')
self.m.StubOutWithMock(clients.neutronclient.Client, 'list_ports')
self.m.StubOutWithMock(clients.neutronclient.Client, 'list_subnets')
self.m.StubOutWithMock(clients.neutronclient.Client, 'show_network')
self.m.StubOutWithMock(clients.neutronclient.Client, 'list_routers')
self.m.StubOutWithMock(clients.neutronclient.Client,
'remove_gateway_router')
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
def mock_show_network(self):
vpc_name = utils.PhysName('test_stack', 'the_vpc')
clients.neutronclient.Client.show_network(
'aaaa-netid'
).AndReturn({"network": {
"status": "BUILD",
"subnets": [],
"name": vpc_name,
"admin_state_up": False,
"shared": False,
"tenant_id": "c1210485b2424d48804aad5d39c61b8f",
"id": "aaaa-netid"
}})
def create_eip(self, t, stack, resource_name):
rsrc = eip.ElasticIp(resource_name,
t['Resources'][resource_name],
stack)
self.assertIsNone(rsrc.validate())
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
return rsrc
def create_association(self, t, stack, resource_name):
rsrc = eip.ElasticIpAssociation(resource_name,
t['Resources'][resource_name],
stack)
self.assertIsNone(rsrc.validate())
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
return rsrc
def mock_update_floatingip(self, port='the_nic'):
clients.neutronclient.Client.update_floatingip(
'fc68ea2c-b60b-4b4f-bd82-94ec81110766',
{'floatingip': {'port_id': port}}).AndReturn(None)
def mock_create_gateway_attachment(self):
clients.neutronclient.Client.add_gateway_router(
'bbbb', {'network_id': 'eeee'}).AndReturn(None)
def mock_create_floatingip(self):
clients.neutronclient.Client.list_networks(
**{'router:external': True}).AndReturn({'networks': [{
'status': 'ACTIVE',
'subnets': [],
'name': 'nova',
'router:external': True,
'tenant_id': 'c1210485b2424d48804aad5d39c61b8f',
'admin_state_up': True,
'shared': True,
'id': 'eeee'
}]})
clients.neutronclient.Client.create_floatingip({
'floatingip': {'floating_network_id': u'eeee'}
}).AndReturn({'floatingip': {
"status": "ACTIVE",
"id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766",
"floating_ip_address": "192.168.9.3"
}})
def mock_show_floatingip(self, refid):
clients.neutronclient.Client.show_floatingip(
refid,
).AndReturn({'floatingip': {
'router_id': None,
'tenant_id': 'e936e6cd3e0b48dcb9ff853a8f253257',
'floating_network_id': 'eeee',
'fixed_ip_address': None,
'floating_ip_address': '172.24.4.227',
'port_id': None,
'id': 'ffff'
}})
def mock_delete_floatingip(self):
id = 'fc68ea2c-b60b-4b4f-bd82-94ec81110766'
clients.neutronclient.Client.delete_floatingip(id).AndReturn(None)
def mock_list_ports(self):
clients.neutronclient.Client.list_ports(id='the_nic').AndReturn(
{"ports": [{
"status": "DOWN",
"binding:host_id": "null",
"name": "wp-NIC-yu7fc7l4g5p6",
"admin_state_up": True,
"network_id": "22c26451-cf27-4d48-9031-51f5e397b84e",
"tenant_id": "ecf538ec1729478fa1f97f1bf4fdcf7b",
"binding:vif_type": "ovs",
"device_owner": "",
"binding:capabilities": {"port_filter": True},
"mac_address": "fa:16:3e:62:2d:4f",
"fixed_ips": [{"subnet_id": "mysubnetid-70ec",
"ip_address": "192.168.9.2"}],
"id": "a000228d-b40b-4124-8394-a4082ae1b76b",
"security_groups": ["5c6f529d-3186-4c36-84c0-af28b8daac7b"],
"device_id": ""
}]})
def mock_list_instance_ports(self, refid):
clients.neutronclient.Client.list_ports(device_id=refid).AndReturn(
{"ports": [{
"status": "DOWN",
"binding:host_id": "null",
"name": "wp-NIC-yu7fc7l4g5p6",
"admin_state_up": True,
"network_id": "22c26451-cf27-4d48-9031-51f5e397b84e",
"tenant_id": "ecf538ec1729478fa1f97f1bf4fdcf7b",
"binding:vif_type": "ovs",
"device_owner": "",
"binding:capabilities": {"port_filter": True},
"mac_address": "fa:16:3e:62:2d:4f",
"fixed_ips": [{"subnet_id": "mysubnetid-70ec",
"ip_address": "192.168.9.2"}],
"id": "a000228d-b40b-4124-8394-a4082ae1b76c",
"security_groups": ["5c6f529d-3186-4c36-84c0-af28b8daac7b"],
"device_id": refid
}]})
def mock_list_subnets(self):
clients.neutronclient.Client.list_subnets(
id='mysubnetid-70ec').AndReturn(
{'subnets': [{
u'name': u'wp-Subnet-pyjm7bvoi4xw',
u'enable_dhcp': True,
u'network_id': u'aaaa-netid',
u'tenant_id': u'ecf538ec1729478fa1f97f1bf4fdcf7b',
u'dns_nameservers': [],
u'allocation_pools': [{u'start': u'192.168.9.2',
u'end': u'192.168.9.254'}],
u'host_routes': [],
u'ip_version': 4,
u'gateway_ip': u'192.168.9.1',
u'cidr': u'192.168.9.0/24',
u'id': u'2c339ccd-734a-4acc-9f64-6f0dfe427e2d'
}]})
def mock_router_for_vpc(self):
vpc_name = utils.PhysName('test_stack', 'the_vpc')
clients.neutronclient.Client.list_routers(name=vpc_name).AndReturn({
"routers": [{
"status": "ACTIVE",
"external_gateway_info": {
"network_id": "zzzz",
"enable_snat": True},
"name": vpc_name,
"admin_state_up": True,
"tenant_id": "3e21026f2dc94372b105808c0e721661",
"routes": [],
"id": "bbbb"
}]
})
def mock_no_router_for_vpc(self):
vpc_name = utils.PhysName('test_stack', 'the_vpc')
clients.neutronclient.Client.list_routers(name=vpc_name).AndReturn({
"routers": []
})
def mock_keystone(self):
clients.OpenStackClients.keystone().AndReturn(
fakec.FakeKeystoneClient())
def test_neutron_eip(self):
eip.ElasticIp.nova().MultipleTimes().AndReturn(self.fc)
self.fc.servers.get('WebServer').AndReturn(self.fc.servers.list()[0])
self.fc.servers.get('WebServer')
self.m.ReplayAll()
t = template_format.parse(eip_template)
stack = utils.parse_stack(t)
rsrc = self.create_eip(t, stack, 'IPAddress')
try:
self.assertEqual('11.0.0.1', rsrc.FnGetRefId())
rsrc.refid = None
self.assertEqual('11.0.0.1', rsrc.FnGetRefId())
self.assertEqual('1', rsrc.FnGetAtt('AllocationId'))
self.assertRaises(exception.InvalidTemplateAttribute,
rsrc.FnGetAtt, 'Foo')
finally:
scheduler.TaskRunner(rsrc.destroy)()
self.m.VerifyAll()
def test_association_allocationid(self):
self.mock_keystone()
self.mock_create_gateway_attachment()
self.mock_show_network()
self.mock_router_for_vpc()
self.mock_create_floatingip()
self.mock_list_ports()
self.mock_list_subnets()
self.mock_show_floatingip('fc68ea2c-b60b-4b4f-bd82-94ec81110766')
self.mock_update_floatingip()
self.mock_update_floatingip(port=None)
self.mock_delete_floatingip()
self.m.ReplayAll()
t = template_format.parse(eip_template_ipassoc2)
stack = utils.parse_stack(t)
rsrc = self.create_eip(t, stack, 'the_eip')
association = self.create_association(t, stack, 'IPAssoc')
scheduler.TaskRunner(association.delete)()
scheduler.TaskRunner(rsrc.delete)()
self.m.VerifyAll()
def test_association_allocationid_with_instance(self):
self.mock_keystone()
self.mock_show_network()
self.mock_create_floatingip()
self.mock_list_instance_ports('1fafbe59-2332-4f5f-bfa4-517b4d6c1b65')
self.mock_list_subnets()
self.mock_no_router_for_vpc()
self.mock_update_floatingip(
port='a000228d-b40b-4124-8394-a4082ae1b76c')
self.mock_update_floatingip(port=None)
self.mock_delete_floatingip()
self.m.ReplayAll()
t = template_format.parse(eip_template_ipassoc3)
stack = utils.parse_stack(t)
rsrc = self.create_eip(t, stack, 'the_eip')
association = self.create_association(t, stack, 'IPAssoc')
scheduler.TaskRunner(association.delete)()
scheduler.TaskRunner(rsrc.delete)()
self.m.VerifyAll()