Add basic autoscaling.
This is mainly for ppetit so he can create multiple resources with one stack. Use like this: heat create lots -f ./templates/ppetit.template --parameters="KeyName=${USER}_key;NumInstances=2" Change-Id: Ie609a1843c855953d65183a8f2d8ed49254a265f Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
This commit is contained in:
parent
a0da17b39f
commit
4d79e24097
148
heat/engine/autoscaling.py
Normal file
148
heat/engine/autoscaling.py
Normal file
@ -0,0 +1,148 @@
|
||||
# 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 eventlet
|
||||
import logging
|
||||
import json
|
||||
import os
|
||||
|
||||
from heat.common import exception
|
||||
from heat.db import api as db_api
|
||||
from heat.engine import instance
|
||||
from heat.engine.resources import Resource
|
||||
|
||||
logger = logging.getLogger('heat.engine.autoscaling')
|
||||
|
||||
|
||||
class AutoScalingGroup(Resource):
|
||||
tags_schema = {'Key': {'Type': 'String',
|
||||
'Required': True},
|
||||
'Value': {'Type': 'String',
|
||||
'Required': True}}
|
||||
properties_schema = {
|
||||
'AvailabilityZones': {'Required': True,
|
||||
'Type': 'List'},
|
||||
'LaunchConfigurationName': {'Required': True,
|
||||
'Type': 'String'},
|
||||
'MaxSize': {'Required': True,
|
||||
'Type': 'String'},
|
||||
'MinSize': {'Required': True,
|
||||
'Type': 'String'},
|
||||
'Cooldown': {'Type': 'String'},
|
||||
'DesiredCapacity': {'Type': 'String',
|
||||
'Implemented': False},
|
||||
'HealthCheckGracePeriod': {'Type': 'Integer',
|
||||
'Implemented': False},
|
||||
'HealthCheckType': {'Type': 'String',
|
||||
'AllowedValues': ['EC2', 'ELB'],
|
||||
'Implemented': False},
|
||||
'LoadBalancerNames': {'Type': 'List'},
|
||||
'Tags': {'Type': 'List',
|
||||
'Schema': tags_schema}
|
||||
}
|
||||
|
||||
def __init__(self, name, json_snippet, stack):
|
||||
super(AutoScalingGroup, self).__init__(name, json_snippet, stack)
|
||||
# instance_id is a list of resources
|
||||
|
||||
def handle_create(self):
|
||||
self.adjust(int(self.properties['MinSize']),
|
||||
adjustment_type='ExactCapacity')
|
||||
|
||||
def handle_delete(self):
|
||||
if self.instance_id is not None:
|
||||
conf = self.properties['LaunchConfigurationName']
|
||||
inst_list = self.instance_id.split(',')
|
||||
logger.debug('handle_delete %s' % str(inst_list))
|
||||
for victim in inst_list:
|
||||
logger.debug('handle_delete %s' % victim)
|
||||
inst = instance.Instance(victim,
|
||||
self.stack.t['Resources'][conf],
|
||||
self.stack)
|
||||
inst.destroy()
|
||||
|
||||
def adjust(self, adjustment, adjustment_type='ChangeInCapacity'):
|
||||
self.calculate_properties()
|
||||
|
||||
inst_list = []
|
||||
if self.instance_id is not None:
|
||||
inst_list = sorted(self.instance_id.split(','))
|
||||
|
||||
capacity = len(inst_list)
|
||||
if adjustment_type == 'ChangeInCapacity':
|
||||
new_capacity = capacity + adjustment
|
||||
elif adjustment_type == 'ExactCapacity':
|
||||
new_capacity = adjustment
|
||||
else:
|
||||
# PercentChangeInCapacity
|
||||
new_capacity = capacity + (capacity * adjustment / 100)
|
||||
|
||||
if new_capacity > int(self.properties['MaxSize']):
|
||||
logger.warn('can not exceed %s' % self.properties['MaxSize'])
|
||||
return
|
||||
if new_capacity < int(self.properties['MinSize']):
|
||||
logger.warn('can not be less than %s' % self.properties['MinSize'])
|
||||
return
|
||||
|
||||
if new_capacity == capacity:
|
||||
return
|
||||
|
||||
conf = self.properties['LaunchConfigurationName']
|
||||
if new_capacity > capacity:
|
||||
# grow
|
||||
for x in range(capacity, new_capacity):
|
||||
inst = instance.Instance('%s-%d' % (self.name, x),
|
||||
self.stack.t['Resources'][conf],
|
||||
self.stack)
|
||||
inst_list.append('%s-%d' % (self.name, x))
|
||||
self.instance_id_set(','.join(inst_list))
|
||||
inst.create()
|
||||
else:
|
||||
# shrink (kill largest numbered first)
|
||||
del_list = inst_list[:]
|
||||
for victim in reversed(del_list):
|
||||
inst = instance.Instance(victim,
|
||||
self.stack.t['Resources'][conf],
|
||||
self.stack)
|
||||
inst.destroy()
|
||||
inst_list.remove(victim)
|
||||
self.instance_id_set(','.join(inst_list))
|
||||
|
||||
|
||||
class LaunchConfiguration(Resource):
|
||||
tags_schema = {'Key': {'Type': 'String',
|
||||
'Required': True},
|
||||
'Value': {'Type': 'String',
|
||||
'Required': True}}
|
||||
properties_schema = {
|
||||
'ImageId': {'Type': 'String',
|
||||
'Required': True},
|
||||
'InstanceType': {'Type': 'String',
|
||||
'Required': True},
|
||||
'KeyName': {'Type': 'String'},
|
||||
'UserData': {'Type': 'String'},
|
||||
'SecurityGroups': {'Type': 'String'},
|
||||
'KernelId': {'Type': 'String',
|
||||
'Implemented': False},
|
||||
'RamDiskId': {'Type': 'String',
|
||||
'Implemented': False},
|
||||
'BlockDeviceMappings': {'Type': 'String',
|
||||
'Implemented': False},
|
||||
'NovaSchedulerHints': {'Type': 'List',
|
||||
'Schema': tags_schema},
|
||||
}
|
||||
|
||||
def __init__(self, name, json_snippet, stack):
|
||||
super(LaunchConfiguration, self).__init__(name, json_snippet, stack)
|
@ -20,6 +20,7 @@ Register of resource types and their mapping to Resource classes.
|
||||
|
||||
from heat.engine import resources
|
||||
|
||||
from heat.engine import autoscaling
|
||||
from heat.engine import cloud_watch
|
||||
from heat.engine import eip
|
||||
from heat.engine import instance
|
||||
@ -47,6 +48,8 @@ _resource_classes = {
|
||||
'AWS::IAM::User': user.User,
|
||||
'AWS::IAM::AccessKey': user.AccessKey,
|
||||
'HEAT::HA::Restarter': instance.Restarter,
|
||||
'AWS::AutoScaling::LaunchConfiguration': autoscaling.LaunchConfiguration,
|
||||
'AWS::AutoScaling::AutoScalingGroup': autoscaling.AutoScalingGroup,
|
||||
}
|
||||
|
||||
|
||||
|
@ -214,6 +214,12 @@ class Resource(object):
|
||||
|
||||
def instance_id_set(self, inst):
|
||||
self.instance_id = inst
|
||||
if self.id is not None:
|
||||
try:
|
||||
rs = db_api.resource_get(self.stack.context, self.id)
|
||||
rs.update_and_save({'nova_instance': self.instance_id})
|
||||
except Exception as ex:
|
||||
logger.warn('db error %s' % str(ex))
|
||||
|
||||
def _create_db(self, metadata=None):
|
||||
'''Create the resource in the database'''
|
||||
|
@ -75,8 +75,6 @@
|
||||
"ImageId": "F16-x86_64-cfntools",
|
||||
"InstanceType": { "Ref": "InstanceType" },
|
||||
"KeyName": { "Ref": "KeyName" },
|
||||
"Tags": [ {"Key": "Part", "Value": "long"},
|
||||
{"Key": "ready", "Value": "short"} ],
|
||||
"UserData": { "Fn::Base64": { "Fn::Join": ["", [
|
||||
"#!/bin/bash -v\n",
|
||||
"/opt/aws/bin/cfn-init -s ",
|
||||
|
57
templates/ppetit.template
Normal file
57
templates/ppetit.template
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
|
||||
"Description" : "Template to create multiple instances.",
|
||||
|
||||
"Parameters" : {
|
||||
"KeyName" : {
|
||||
"Description" : "Name of an existing EC2 KeyPair to enable SSH access to the instances",
|
||||
"Type" : "String"
|
||||
},
|
||||
"InstanceType" : {
|
||||
"Description" : "Instance type",
|
||||
"Type" : "String",
|
||||
"Default" : "m1.small",
|
||||
"AllowedValues" : [ "t1.micro", "m1.small", "m1.large", "m1.xlarge", "m2.xlarge", "m2.2xlarge", "m2.4xlarge", "c1.medium", "c1.xlarge", "cc1.4xlarge" ],
|
||||
"ConstraintDescription" : "must be a valid EC2 instance type."
|
||||
},
|
||||
"ImageId" : {
|
||||
"Description" : "Name of the image to use",
|
||||
"Type" : "String",
|
||||
"Default" : "F16-x86_64-cfntools"
|
||||
},
|
||||
"NumInstances": {
|
||||
"Default": "1",
|
||||
"MinValue": "1",
|
||||
"MaxValue": "100",
|
||||
"Description" : "Number of instances to create",
|
||||
"Type": "Integer"
|
||||
}
|
||||
},
|
||||
|
||||
"Resources" : {
|
||||
"JobServerGroup" : {
|
||||
"Type" : "AWS::AutoScaling::AutoScalingGroup",
|
||||
"Properties" : {
|
||||
"LaunchConfigurationName" : { "Ref" : "JobServerConfig" },
|
||||
"MinSize" : {"Ref": "NumInstances"},
|
||||
"MaxSize" : {"Ref": "NumInstances"},
|
||||
"AvailabilityZones" : { "Fn::GetAZs" : "" }
|
||||
}
|
||||
},
|
||||
|
||||
"JobServerConfig" : {
|
||||
"Type" : "AWS::AutoScaling::LaunchConfiguration",
|
||||
"Properties": {
|
||||
"ImageId" : { "Ref" : "ImageId" },
|
||||
"InstanceType" : { "Ref" : "InstanceType" },
|
||||
"KeyName" : { "Ref" : "KeyName" },
|
||||
"NovaSchedulerHints": [ {"Key": "part", "Value": "long"},
|
||||
{"Key": "ready", "Value": "short"} ],
|
||||
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
|
||||
"#!/bin/bash -v\n"
|
||||
]]}}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user