heat/heat/tests/test_eip.py

552 lines
18 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.resources import eip
from heat.engine import clients
from heat.engine import resource
from heat.engine import scheduler
from heat.engine import parser
from heat.tests.common import HeatTestCase
from heat.tests.v1_1 import fakes
from heat.tests import fakes as fakec
from heat.tests import utils
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')
utils.setup_dummy_db()
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(resource.UpdateReplace,
rsrc.handle_update, {}, {}, {})
self.assertRaises(exception.InvalidTemplateAttribute,
rsrc.FnGetAtt, 'Foo')
finally:
scheduler.TaskRunner(rsrc.destroy)()
self.m.VerifyAll()
def test_association_eip(self):
eip.ElasticIp.nova().AndReturn(self.fc)
eip.ElasticIp.nova().AndReturn(self.fc)
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')
# TODO(sbaker), figure out why this is an empty string
#self.assertEqual('', association.FnGetRefId())
scheduler.TaskRunner(association.delete)()
scheduler.TaskRunner(rsrc.delete)()
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')
utils.setup_dummy_db()
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(resource.UpdateReplace,
rsrc.handle_update, {}, {}, {})
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()