heat/heat/tests/test_neutron_autoscaling.py

380 lines
13 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 copy
from testtools import skipIf
import mox
from oslo.config import cfg
import uuid
from heat.common import template_format
from heat.db import api as db_api
from heat.engine import clients
from heat.engine import environment
from heat.engine import parser
from heat.engine.resources import instance
from heat.engine.resources import nova_utils
from heat.engine import template
from heat.openstack.common.importutils import try_import
from heat.tests.common import HeatTestCase
from heat.tests import fakes
from heat.tests import utils
from heat.tests.v1_1 import fakes as v1fakes
neutroncli = try_import('neutronclient')
as_template = '''
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "AutoScaling Test",
"Parameters" : {
"ImageId": {"Type": "String"},
"KeyName": {"Type": "String"},
"SubnetId": {"Type": "String"}
},
"Resources" : {
"SvrGrp" : {
"Type" : "AWS::AutoScaling::AutoScalingGroup",
"Properties" : {
"AvailabilityZones" : ["nova"],
"LaunchConfigurationName" : { "Ref" : "LaunchConfig" },
"MinSize" : "1",
"MaxSize" : "5",
"DesiredCapacity": "1",
"VPCZoneIdentifier": [ { "Ref": "SubnetId" } ],
"LoadBalancerNames" : [ { "Ref" : "ElasticLoadBalancer" } ]
}
},
"myMonitor": {
"Type": "OS::Neutron::HealthMonitor",
"Properties": {
"type": "HTTP",
"delay": 3,
"max_retries": 5,
"timeout": 10
}
},
"myPool": {
"Type": "OS::Neutron::Pool",
"Properties": {
"description": "Test Pool",
"lb_method": "ROUND_ROBIN",
"monitors": [ { "Ref": "myMonitor" } ],
"name": "Test_Pool",
"protocol": "HTTP",
"subnet_id": { "Ref": "SubnetId" },
"vip": {
"description": "Test VIP",
"connection_limit": 1000,
"address": "10.0.3.121",
"protocol_port": 80,
"name": "test_vip"
}
}
},
"ElasticLoadBalancer" : {
'Type': 'OS::Neutron::LoadBalancer',
'Properties': {
'protocol_port': 8080,
'pool_id': { "Ref": "myPool" }
}
},
"LaunchConfig" : {
"Type" : "AWS::AutoScaling::LaunchConfiguration",
"Properties": {
"ImageId" : {"Ref": "ImageId"},
"InstanceType" : "bar",
}
}
}
}
'''
class AutoScalingTest(HeatTestCase):
params = {'KeyName': 'test', 'ImageId': 'foo'}
def setUp(self):
super(AutoScalingTest, self).setUp()
utils.setup_dummy_db()
self.ctx = utils.dummy_context()
self.fc = v1fakes.FakeClient()
cfg.CONF.set_default('heat_waitcondition_server_url',
'http://server.test:8000/v1/waitcondition')
self.m.StubOutWithMock(clients.OpenStackClients, 'keystone')
self.m.StubOutWithMock(clients.neutronclient.Client,
'create_health_monitor')
self.m.StubOutWithMock(clients.neutronclient.Client,
'associate_health_monitor')
self.m.StubOutWithMock(clients.neutronclient.Client, 'create_pool')
self.m.StubOutWithMock(clients.neutronclient.Client, 'create_vip')
self.m.StubOutWithMock(clients.neutronclient.Client, 'show_pool')
self.m.StubOutWithMock(clients.neutronclient.Client, 'show_vip')
self.m.StubOutWithMock(clients.neutronclient.Client, 'create_member')
self.m.StubOutWithMock(clients.neutronclient.Client, 'list_members')
self.m.StubOutWithMock(nova_utils, 'server_to_ipaddress')
self.m.StubOutWithMock(parser.Stack, 'validate')
self.m.StubOutWithMock(instance.Instance, 'handle_create')
self.m.StubOutWithMock(instance.Instance, 'check_create_complete')
def _stub_create(self, num):
parser.Stack.validate()
cookie = object()
for x in range(num):
instance.Instance.handle_create().AndReturn(cookie)
instance.Instance.check_create_complete(mox.IgnoreArg())\
.AndReturn(False)
instance.Instance.check_create_complete(mox.IgnoreArg())\
.AndReturn(True)
@skipIf(neutroncli is None, 'neutronclient unavailable')
def test_lb(self):
tmpl = template_format.parse(as_template)
network_body = {
"network": {
"id": str(uuid.uuid4()),
"name": "testnet",
"admin_state_up": True
}
}
subnet_body = {
"subnet": {
"name": "testsubnet",
"id": str(uuid.uuid4()),
"network_id": network_body['network']['id'],
"ip_version": 4,
"cidr": "10.0.3.0/24",
"allocation_pools": [
{
"start": "10.0.3.20",
"end": "10.0.3.150"
}
],
"gateway_ip": "10.0.3.1"
}
}
self.params["SubnetId"] = subnet_body['subnet']['id']
mon_block = {
'health_monitor': tmpl['Resources']['myMonitor']['Properties']
}
mon_block['health_monitor']['admin_state_up'] = True
mon_ret_block = copy.deepcopy(mon_block)
mon_ret_block['health_monitor']['id'] = str(uuid.uuid4())
mon_ret_block['health_monitor']['status'] = 'ACTIVE'
pool_block = {'pool': {}}
tmp_pool_block = tmpl['Resources']['myPool']['Properties']
for val in ['lb_method', 'protocol', 'name', 'description']:
pool_block['pool'][val] = tmp_pool_block[val]
pool_block['pool']['admin_state_up'] = True
pool_block['pool']['subnet_id'] = self.params['SubnetId']
pool_block['pool']['admin_state_up'] = True
pool_ret_block = copy.deepcopy(pool_block)
pool_ret_block['pool']['id'] = str(uuid.uuid4())
pool_ret_block['pool']['status'] = 'ACTIVE'
tmp_vip_block = tmp_pool_block.pop('vip')
vip_block = {
'vip': {
'protocol': pool_block['pool']['protocol'],
'description': tmp_vip_block['description'],
'admin_state_up': True,
'subnet_id': self.params['SubnetId'],
'connection_limit': tmp_vip_block['connection_limit'],
'pool_id': pool_ret_block['pool']['id'],
'address': tmp_vip_block['address'],
'protocol_port': tmp_vip_block['protocol_port'],
'name': tmp_vip_block['name']
}
}
vip_ret_block = copy.deepcopy(vip_block)
vip_ret_block['vip']['id'] = str(uuid.uuid4())
vip_ret_block['vip']['status'] = 'ACTIVE'
port_block = {
'port': {
'network_id': network_body['network']['id'],
'fixed_ips': [
{
'subnet_id': subnet_body['subnet']['id'],
}
],
'admin_state_up': True
}
}
port_ret_block = copy.deepcopy(port_block)
port_ret_block['port']['id'] = str(uuid.uuid4())
membera_block = {
'member': {
'protocol_port': 8080,
'pool_id': pool_ret_block['pool']['id'],
'address': '1.2.3.4'
}
}
membera_ret_block = copy.deepcopy(membera_block)
membera_ret_block['member']['id'] = str(uuid.uuid4())
memberb_block = {
'member': {
'protocol_port': 8080,
'pool_id': pool_ret_block['pool']['id'],
'address': '1.2.3.5'
}
}
memberb_ret_block = copy.deepcopy(memberb_block)
memberb_ret_block['member']['id'] = str(uuid.uuid4())
memberc_block = {
'member': {
'protocol_port': 8080,
'pool_id': pool_ret_block['pool']['id'],
'address': '1.2.3.6'
}
}
memberc_ret_block = copy.deepcopy(memberc_block)
memberc_ret_block['member']['id'] = str(uuid.uuid4())
class id_type(object):
def __init__(self, id, name):
self.id = id
self.name = name
instances = {}
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
clients.neutronclient.Client.create_health_monitor(mon_block).\
AndReturn(mon_ret_block)
clients.neutronclient.Client.create_pool(pool_block).\
AndReturn(pool_ret_block)
clients.neutronclient.Client.associate_health_monitor(
pool_ret_block['pool']['id'],
{'health_monitor': {
'id': mon_ret_block['health_monitor']['id']
}}).AndReturn(None)
clients.neutronclient.Client.create_vip(vip_block).\
AndReturn(vip_ret_block)
clients.neutronclient.Client.show_pool(pool_ret_block['pool']['id']).\
AndReturn(pool_ret_block)
clients.neutronclient.Client.show_vip(vip_ret_block['vip']['id']).\
AndReturn(vip_ret_block)
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
parser.Stack.validate()
instid = str(uuid.uuid4())
instance.Instance.handle_create().AndReturn(instid)
instance.Instance.check_create_complete(mox.IgnoreArg())\
.AndReturn(False)
instance.Instance.check_create_complete(mox.IgnoreArg())\
.AndReturn(True)
nova_utils.server_to_ipaddress(
mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn('1.2.3.4')
clients.neutronclient.Client.create_member(membera_block).\
AndReturn(membera_ret_block)
instances[instid] = membera_ret_block['member']['id']
# Start of update
clients.OpenStackClients.keystone().AndReturn(
fakes.FakeKeystoneClient())
parser.Stack.validate()
instid = str(uuid.uuid4())
instance.Instance.handle_create().AndReturn(instid)
instance.Instance.check_create_complete(mox.IgnoreArg())\
.AndReturn(False)
instance.Instance.check_create_complete(mox.IgnoreArg())\
.AndReturn(True)
instances[instid] = memberb_ret_block['member']['id']
instid = str(uuid.uuid4())
instance.Instance.handle_create().AndReturn(instid)
instance.Instance.check_create_complete(mox.IgnoreArg())\
.AndReturn(False)
instance.Instance.check_create_complete(mox.IgnoreArg())\
.AndReturn(True)
nova_utils.server_to_ipaddress(
mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn('1.2.3.5')
clients.neutronclient.Client.create_member(memberb_block).\
AndReturn(memberb_ret_block)
nova_utils.server_to_ipaddress(
mox.IgnoreArg(),
mox.IgnoreArg()).AndReturn('1.2.3.6')
clients.neutronclient.Client.create_member(memberc_block).\
AndReturn(memberc_ret_block)
self.m.ReplayAll()
# Start of stack create
env = {'parameters': self.params}
tmpl = template_format.parse(as_template)
stack = parser.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl),
environment.Environment(env))
stack.store()
stack.create()
self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
stack.state)
# Start of stack update
stack2 = parser.Stack.load(self.ctx, stack_id=stack.id)
tmpl2 = copy.deepcopy(tmpl)
tmpl2['Resources']['SvrGrp']['Properties']['DesiredCapacity'] = '3'
update_stack = parser.Stack(self.ctx, 'update_test_stack',
template.Template(tmpl2),
environment.Environment(env))
stack2.update(update_stack)
self.assertEqual((parser.Stack.UPDATE, parser.Stack.COMPLETE),
stack2.state)
members = db_api.resource_data_get_all(stack['ElasticLoadBalancer'])
self.assertEqual(3, len(members.keys()))
self.m.VerifyAll()