1# Copyright (c) 2009-2011 Reza Lotun http://reza.lotun.name/ 2# 3# Permission is hereby granted, free of charge, to any person obtaining a 4# copy of this software and associated documentation files (the 5# "Software"), to deal in the Software without restriction, including 6# without limitation the rights to use, copy, modify, merge, publish, dis- 7# tribute, sublicense, and/or sell copies of the Software, and to permit 8# persons to whom the Software is furnished to do so, subject to the fol- 9# lowing conditions: 10# 11# The above copyright notice and this permission notice shall be included 12# in all copies or substantial portions of the Software. 13# 14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- 16# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 17# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20# IN THE SOFTWARE. 21 22from boto.ec2.elb.listelement import ListElement 23from boto.resultset import ResultSet 24from boto.ec2.autoscale.launchconfig import LaunchConfiguration 25from boto.ec2.autoscale.request import Request 26from boto.ec2.autoscale.instance import Instance 27from boto.ec2.autoscale.tag import Tag 28 29 30class ProcessType(object): 31 def __init__(self, connection=None): 32 self.connection = connection 33 self.process_name = None 34 35 def __repr__(self): 36 return 'ProcessType(%s)' % self.process_name 37 38 def startElement(self, name, attrs, connection): 39 pass 40 41 def endElement(self, name, value, connection): 42 if name == 'ProcessName': 43 self.process_name = value 44 45 46class SuspendedProcess(object): 47 def __init__(self, connection=None): 48 self.connection = connection 49 self.process_name = None 50 self.reason = None 51 52 def __repr__(self): 53 return 'SuspendedProcess(%s, %s)' % (self.process_name, self.reason) 54 55 def startElement(self, name, attrs, connection): 56 pass 57 58 def endElement(self, name, value, connection): 59 if name == 'ProcessName': 60 self.process_name = value 61 elif name == 'SuspensionReason': 62 self.reason = value 63 64 65class EnabledMetric(object): 66 def __init__(self, connection=None, metric=None, granularity=None): 67 self.connection = connection 68 self.metric = metric 69 self.granularity = granularity 70 71 def __repr__(self): 72 return 'EnabledMetric(%s, %s)' % (self.metric, self.granularity) 73 74 def startElement(self, name, attrs, connection): 75 pass 76 77 def endElement(self, name, value, connection): 78 if name == 'Granularity': 79 self.granularity = value 80 elif name == 'Metric': 81 self.metric = value 82 83 84class TerminationPolicies(list): 85 86 def startElement(self, name, attrs, connection): 87 pass 88 89 def endElement(self, name, value, connection): 90 if name == 'member': 91 self.append(value) 92 93 94class AutoScalingGroup(object): 95 def __init__(self, connection=None, name=None, 96 launch_config=None, availability_zones=None, 97 load_balancers=None, default_cooldown=None, 98 health_check_type=None, health_check_period=None, 99 placement_group=None, vpc_zone_identifier=None, 100 desired_capacity=None, min_size=None, max_size=None, 101 tags=None, termination_policies=None, instance_id=None, 102 **kwargs): 103 """ 104 Creates a new AutoScalingGroup with the specified name. 105 106 You must not have already used up your entire quota of 107 AutoScalingGroups in order for this call to be successful. Once the 108 creation request is completed, the AutoScalingGroup is ready to be 109 used in other calls. 110 111 :type name: str 112 :param name: Name of autoscaling group (required). 113 114 :type availability_zones: list 115 :param availability_zones: List of availability zones (required). 116 117 :type default_cooldown: int 118 :param default_cooldown: Number of seconds after a Scaling Activity 119 completes before any further scaling activities can start. 120 121 :type desired_capacity: int 122 :param desired_capacity: The desired capacity for the group. 123 124 :type health_check_period: str 125 :param health_check_period: Length of time in seconds after a new 126 EC2 instance comes into service that Auto Scaling starts 127 checking its health. 128 129 :type health_check_type: str 130 :param health_check_type: The service you want the health status from, 131 Amazon EC2 or Elastic Load Balancer. 132 133 :type launch_config: str or LaunchConfiguration 134 :param launch_config: Name of launch configuration (required). 135 136 :type load_balancers: list 137 :param load_balancers: List of load balancers. 138 139 :type max_size: int 140 :param max_size: Maximum size of group (required). 141 142 :type min_size: int 143 :param min_size: Minimum size of group (required). 144 145 :type placement_group: str 146 :param placement_group: Physical location of your cluster placement 147 group created in Amazon EC2. 148 149 :type vpc_zone_identifier: str or list 150 :param vpc_zone_identifier: A comma-separated string or python list of 151 the subnet identifiers of the Virtual Private Cloud. 152 153 :type tags: list 154 :param tags: List of :class:`boto.ec2.autoscale.tag.Tag`s 155 156 :type termination_policies: list 157 :param termination_policies: A list of termination policies. Valid values 158 are: "OldestInstance", "NewestInstance", "OldestLaunchConfiguration", 159 "ClosestToNextInstanceHour", "Default". If no value is specified, 160 the "Default" value is used. 161 162 :type instance_id: str 163 :param instance_id: The ID of the Amazon EC2 instance you want to use 164 to create the Auto Scaling group. 165 166 :rtype: :class:`boto.ec2.autoscale.group.AutoScalingGroup` 167 :return: An autoscale group. 168 """ 169 self.name = name or kwargs.get('group_name') # backwards compat 170 self.connection = connection 171 self.min_size = int(min_size) if min_size is not None else None 172 self.max_size = int(max_size) if max_size is not None else None 173 self.created_time = None 174 # backwards compatibility 175 default_cooldown = default_cooldown or kwargs.get('cooldown') 176 if default_cooldown is not None: 177 default_cooldown = int(default_cooldown) 178 self.default_cooldown = default_cooldown 179 self.launch_config_name = launch_config 180 if launch_config and isinstance(launch_config, LaunchConfiguration): 181 self.launch_config_name = launch_config.name 182 self.desired_capacity = desired_capacity 183 lbs = load_balancers or [] 184 self.load_balancers = ListElement(lbs) 185 zones = availability_zones or [] 186 self.availability_zones = ListElement(zones) 187 self.health_check_period = health_check_period 188 self.health_check_type = health_check_type 189 self.placement_group = placement_group 190 self.autoscaling_group_arn = None 191 if type(vpc_zone_identifier) is list: 192 vpc_zone_identifier = ','.join(vpc_zone_identifier) 193 self.vpc_zone_identifier = vpc_zone_identifier 194 self.instances = None 195 self.tags = tags or None 196 termination_policies = termination_policies or [] 197 self.termination_policies = ListElement(termination_policies) 198 self.instance_id = instance_id 199 200 # backwards compatible access to 'cooldown' param 201 def _get_cooldown(self): 202 return self.default_cooldown 203 204 def _set_cooldown(self, val): 205 self.default_cooldown = val 206 207 cooldown = property(_get_cooldown, _set_cooldown) 208 209 def __repr__(self): 210 return 'AutoScaleGroup<%s>' % self.name 211 212 def startElement(self, name, attrs, connection): 213 if name == 'Instances': 214 self.instances = ResultSet([('member', Instance)]) 215 return self.instances 216 elif name == 'LoadBalancerNames': 217 return self.load_balancers 218 elif name == 'AvailabilityZones': 219 return self.availability_zones 220 elif name == 'EnabledMetrics': 221 self.enabled_metrics = ResultSet([('member', EnabledMetric)]) 222 return self.enabled_metrics 223 elif name == 'SuspendedProcesses': 224 self.suspended_processes = ResultSet([('member', SuspendedProcess)]) 225 return self.suspended_processes 226 elif name == 'Tags': 227 self.tags = ResultSet([('member', Tag)]) 228 return self.tags 229 elif name == 'TerminationPolicies': 230 return self.termination_policies 231 else: 232 return 233 234 def endElement(self, name, value, connection): 235 if name == 'MinSize': 236 self.min_size = int(value) 237 elif name == 'AutoScalingGroupARN': 238 self.autoscaling_group_arn = value 239 elif name == 'CreatedTime': 240 self.created_time = value 241 elif name == 'DefaultCooldown': 242 self.default_cooldown = int(value) 243 elif name == 'LaunchConfigurationName': 244 self.launch_config_name = value 245 elif name == 'DesiredCapacity': 246 self.desired_capacity = int(value) 247 elif name == 'MaxSize': 248 self.max_size = int(value) 249 elif name == 'AutoScalingGroupName': 250 self.name = value 251 elif name == 'PlacementGroup': 252 self.placement_group = value 253 elif name == 'HealthCheckGracePeriod': 254 try: 255 self.health_check_period = int(value) 256 except ValueError: 257 self.health_check_period = None 258 elif name == 'HealthCheckType': 259 self.health_check_type = value 260 elif name == 'VPCZoneIdentifier': 261 self.vpc_zone_identifier = value 262 elif name == 'InstanceId': 263 self.instance_id = value 264 else: 265 setattr(self, name, value) 266 267 def set_capacity(self, capacity): 268 """ 269 Set the desired capacity for the group. 270 """ 271 params = {'AutoScalingGroupName': self.name, 272 'DesiredCapacity': capacity} 273 req = self.connection.get_object('SetDesiredCapacity', params, 274 Request) 275 self.connection.last_request = req 276 return req 277 278 def update(self): 279 """ 280 Sync local changes with AutoScaling group. 281 """ 282 return self.connection._update_group('UpdateAutoScalingGroup', self) 283 284 def shutdown_instances(self): 285 """ 286 Convenience method which shuts down all instances associated with 287 this group. 288 """ 289 self.min_size = 0 290 self.max_size = 0 291 self.desired_capacity = 0 292 self.update() 293 294 def delete(self, force_delete=False): 295 """ 296 Delete this auto-scaling group if no instances attached or no 297 scaling activities in progress. 298 """ 299 return self.connection.delete_auto_scaling_group(self.name, 300 force_delete) 301 302 def get_activities(self, activity_ids=None, max_records=50): 303 """ 304 Get all activies for this group. 305 """ 306 return self.connection.get_all_activities(self, activity_ids, 307 max_records) 308 309 def put_notification_configuration(self, topic, notification_types): 310 """ 311 Configures an Auto Scaling group to send notifications when 312 specified events take place. Valid notification types are: 313 'autoscaling:EC2_INSTANCE_LAUNCH', 314 'autoscaling:EC2_INSTANCE_LAUNCH_ERROR', 315 'autoscaling:EC2_INSTANCE_TERMINATE', 316 'autoscaling:EC2_INSTANCE_TERMINATE_ERROR', 317 'autoscaling:TEST_NOTIFICATION' 318 """ 319 return self.connection.put_notification_configuration(self, 320 topic, 321 notification_types) 322 323 def delete_notification_configuration(self, topic): 324 """ 325 Deletes notifications created by put_notification_configuration. 326 """ 327 return self.connection.delete_notification_configuration(self, topic) 328 329 def suspend_processes(self, scaling_processes=None): 330 """ 331 Suspends Auto Scaling processes for an Auto Scaling group. 332 """ 333 return self.connection.suspend_processes(self.name, scaling_processes) 334 335 def resume_processes(self, scaling_processes=None): 336 """ 337 Resumes Auto Scaling processes for an Auto Scaling group. 338 """ 339 return self.connection.resume_processes(self.name, scaling_processes) 340 341 342class AutoScalingGroupMetric(object): 343 def __init__(self, connection=None): 344 345 self.connection = connection 346 self.metric = None 347 self.granularity = None 348 349 def __repr__(self): 350 return 'AutoScalingGroupMetric:%s' % self.metric 351 352 def startElement(self, name, attrs, connection): 353 return 354 355 def endElement(self, name, value, connection): 356 if name == 'Metric': 357 self.metric = value 358 elif name == 'Granularity': 359 self.granularity = value 360 else: 361 setattr(self, name, value) 362