1from datetime import datetime 2 3from boto.resultset import ResultSet 4 5 6class Stack(object): 7 def __init__(self, connection=None): 8 self.connection = connection 9 self.creation_time = None 10 self.description = None 11 self.disable_rollback = None 12 self.notification_arns = [] 13 self.outputs = [] 14 self.parameters = [] 15 self.capabilities = [] 16 self.tags = [] 17 self.stack_id = None 18 self.stack_status = None 19 self.stack_name = None 20 self.stack_name_reason = None 21 self.timeout_in_minutes = None 22 23 def startElement(self, name, attrs, connection): 24 if name == "Parameters": 25 self.parameters = ResultSet([('member', Parameter)]) 26 return self.parameters 27 elif name == "Outputs": 28 self.outputs = ResultSet([('member', Output)]) 29 return self.outputs 30 elif name == "Capabilities": 31 self.capabilities = ResultSet([('member', Capability)]) 32 return self.capabilities 33 elif name == "Tags": 34 self.tags = Tag() 35 return self.tags 36 elif name == 'NotificationARNs': 37 self.notification_arns = ResultSet([('member', NotificationARN)]) 38 return self.notification_arns 39 else: 40 return None 41 42 def endElement(self, name, value, connection): 43 if name == 'CreationTime': 44 try: 45 self.creation_time = datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ') 46 except ValueError: 47 self.creation_time = datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ') 48 elif name == "Description": 49 self.description = value 50 elif name == "DisableRollback": 51 if str(value).lower() == 'true': 52 self.disable_rollback = True 53 else: 54 self.disable_rollback = False 55 elif name == 'StackId': 56 self.stack_id = value 57 elif name == 'StackName': 58 self.stack_name = value 59 elif name == 'StackStatus': 60 self.stack_status = value 61 elif name == "StackStatusReason": 62 self.stack_status_reason = value 63 elif name == "TimeoutInMinutes": 64 self.timeout_in_minutes = int(value) 65 elif name == "member": 66 pass 67 else: 68 setattr(self, name, value) 69 70 def delete(self): 71 return self.connection.delete_stack(stack_name_or_id=self.stack_id) 72 73 def describe_events(self, next_token=None): 74 return self.connection.describe_stack_events( 75 stack_name_or_id=self.stack_id, 76 next_token=next_token 77 ) 78 79 def describe_resource(self, logical_resource_id): 80 return self.connection.describe_stack_resource( 81 stack_name_or_id=self.stack_id, 82 logical_resource_id=logical_resource_id 83 ) 84 85 def describe_resources(self, logical_resource_id=None, 86 physical_resource_id=None): 87 return self.connection.describe_stack_resources( 88 stack_name_or_id=self.stack_id, 89 logical_resource_id=logical_resource_id, 90 physical_resource_id=physical_resource_id 91 ) 92 93 def list_resources(self, next_token=None): 94 return self.connection.list_stack_resources( 95 stack_name_or_id=self.stack_id, 96 next_token=next_token 97 ) 98 99 def update(self): 100 rs = self.connection.describe_stacks(self.stack_id) 101 if len(rs) == 1 and rs[0].stack_id == self.stack_id: 102 self.__dict__.update(rs[0].__dict__) 103 else: 104 raise ValueError("%s is not a valid Stack ID or Name" % 105 self.stack_id) 106 107 def get_template(self): 108 return self.connection.get_template(stack_name_or_id=self.stack_id) 109 110 def get_policy(self): 111 """ 112 Returns the stack policy for this stack. If it has no policy 113 then, a null value is returned. 114 """ 115 return self.connection.get_stack_policy(self.stack_id) 116 117 def set_policy(self, stack_policy_body=None, stack_policy_url=None): 118 """ 119 Sets a stack policy for this stack. 120 121 :type stack_policy_body: string 122 :param stack_policy_body: Structure containing the stack policy body. 123 (For more information, go to ` Prevent Updates to Stack Resources`_ 124 in the AWS CloudFormation User Guide.) 125 You must pass `StackPolicyBody` or `StackPolicyURL`. If both are 126 passed, only `StackPolicyBody` is used. 127 128 :type stack_policy_url: string 129 :param stack_policy_url: Location of a file containing the stack 130 policy. The URL must point to a policy (max size: 16KB) located in 131 an S3 bucket in the same region as the stack. You must pass 132 `StackPolicyBody` or `StackPolicyURL`. If both are passed, only 133 `StackPolicyBody` is used. 134 """ 135 return self.connection.set_stack_policy(self.stack_id, 136 stack_policy_body=stack_policy_body, 137 stack_policy_url=stack_policy_url) 138 139 140class StackSummary(object): 141 def __init__(self, connection=None): 142 self.connection = connection 143 self.stack_id = None 144 self.stack_status = None 145 self.stack_name = None 146 self.creation_time = None 147 self.deletion_time = None 148 self.template_description = None 149 150 def startElement(self, name, attrs, connection): 151 return None 152 153 def endElement(self, name, value, connection): 154 if name == 'StackId': 155 self.stack_id = value 156 elif name == 'StackStatus': 157 self.stack_status = value 158 elif name == 'StackName': 159 self.stack_name = value 160 elif name == 'CreationTime': 161 try: 162 self.creation_time = datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ') 163 except ValueError: 164 self.creation_time = datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ') 165 elif name == "DeletionTime": 166 try: 167 self.deletion_time = datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ') 168 except ValueError: 169 self.deletion_time = datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ') 170 elif name == 'TemplateDescription': 171 self.template_description = value 172 elif name == "member": 173 pass 174 else: 175 setattr(self, name, value) 176 177 178class Parameter(object): 179 def __init__(self, connection=None): 180 self.connection = None 181 self.key = None 182 self.value = None 183 184 def startElement(self, name, attrs, connection): 185 return None 186 187 def endElement(self, name, value, connection): 188 if name == "ParameterKey": 189 self.key = value 190 elif name == "ParameterValue": 191 self.value = value 192 else: 193 setattr(self, name, value) 194 195 def __repr__(self): 196 return "Parameter:\"%s\"=\"%s\"" % (self.key, self.value) 197 198 199class Output(object): 200 def __init__(self, connection=None): 201 self.connection = connection 202 self.description = None 203 self.key = None 204 self.value = None 205 206 def startElement(self, name, attrs, connection): 207 return None 208 209 def endElement(self, name, value, connection): 210 if name == "Description": 211 self.description = value 212 elif name == "OutputKey": 213 self.key = value 214 elif name == "OutputValue": 215 self.value = value 216 else: 217 setattr(self, name, value) 218 219 def __repr__(self): 220 return "Output:\"%s\"=\"%s\"" % (self.key, self.value) 221 222 223class Capability(object): 224 def __init__(self, connection=None): 225 self.connection = None 226 self.value = None 227 228 def startElement(self, name, attrs, connection): 229 return None 230 231 def endElement(self, name, value, connection): 232 self.value = value 233 234 def __repr__(self): 235 return "Capability:\"%s\"" % (self.value) 236 237 238class Tag(dict): 239 240 def __init__(self, connection=None): 241 dict.__init__(self) 242 self.connection = connection 243 self._current_key = None 244 self._current_value = None 245 246 def startElement(self, name, attrs, connection): 247 return None 248 249 def endElement(self, name, value, connection): 250 if name == "Key": 251 self._current_key = value 252 elif name == "Value": 253 self._current_value = value 254 else: 255 setattr(self, name, value) 256 257 if self._current_key and self._current_value: 258 self[self._current_key] = self._current_value 259 self._current_key = None 260 self._current_value = None 261 262 263class NotificationARN(object): 264 def __init__(self, connection=None): 265 self.connection = None 266 self.value = None 267 268 def startElement(self, name, attrs, connection): 269 return None 270 271 def endElement(self, name, value, connection): 272 self.value = value 273 274 def __repr__(self): 275 return "NotificationARN:\"%s\"" % (self.value) 276 277 278class StackResource(object): 279 def __init__(self, connection=None): 280 self.connection = connection 281 self.description = None 282 self.logical_resource_id = None 283 self.physical_resource_id = None 284 self.resource_status = None 285 self.resource_status_reason = None 286 self.resource_type = None 287 self.stack_id = None 288 self.stack_name = None 289 self.timestamp = None 290 291 def startElement(self, name, attrs, connection): 292 return None 293 294 def endElement(self, name, value, connection): 295 if name == "Description": 296 self.description = value 297 elif name == "LogicalResourceId": 298 self.logical_resource_id = value 299 elif name == "PhysicalResourceId": 300 self.physical_resource_id = value 301 elif name == "ResourceStatus": 302 self.resource_status = value 303 elif name == "ResourceStatusReason": 304 self.resource_status_reason = value 305 elif name == "ResourceType": 306 self.resource_type = value 307 elif name == "StackId": 308 self.stack_id = value 309 elif name == "StackName": 310 self.stack_name = value 311 elif name == "Timestamp": 312 try: 313 self.timestamp = datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ') 314 except ValueError: 315 self.timestamp = datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ') 316 else: 317 setattr(self, name, value) 318 319 def __repr__(self): 320 return "StackResource:%s (%s)" % (self.logical_resource_id, 321 self.resource_type) 322 323 324class StackResourceSummary(object): 325 def __init__(self, connection=None): 326 self.connection = connection 327 self.last_updated_time = None 328 self.logical_resource_id = None 329 self.physical_resource_id = None 330 self.resource_status = None 331 self.resource_status_reason = None 332 self.resource_type = None 333 334 def startElement(self, name, attrs, connection): 335 return None 336 337 def endElement(self, name, value, connection): 338 if name == "LastUpdatedTime": 339 try: 340 self.last_updated_time = datetime.strptime( 341 value, 342 '%Y-%m-%dT%H:%M:%SZ' 343 ) 344 except ValueError: 345 self.last_updated_time = datetime.strptime( 346 value, 347 '%Y-%m-%dT%H:%M:%S.%fZ' 348 ) 349 elif name == "LogicalResourceId": 350 self.logical_resource_id = value 351 elif name == "PhysicalResourceId": 352 self.physical_resource_id = value 353 elif name == "ResourceStatus": 354 self.resource_status = value 355 elif name == "ResourceStatusReason": 356 self.resource_status_reason = value 357 elif name == "ResourceType": 358 self.resource_type = value 359 else: 360 setattr(self, name, value) 361 362 def __repr__(self): 363 return "StackResourceSummary:%s (%s)" % (self.logical_resource_id, 364 self.resource_type) 365 366 367class StackEvent(object): 368 valid_states = ("CREATE_IN_PROGRESS", "CREATE_FAILED", "CREATE_COMPLETE", 369 "DELETE_IN_PROGRESS", "DELETE_FAILED", "DELETE_COMPLETE") 370 def __init__(self, connection=None): 371 self.connection = connection 372 self.event_id = None 373 self.logical_resource_id = None 374 self.physical_resource_id = None 375 self.resource_properties = None 376 self.resource_status = None 377 self.resource_status_reason = None 378 self.resource_type = None 379 self.stack_id = None 380 self.stack_name = None 381 self.timestamp = None 382 383 def startElement(self, name, attrs, connection): 384 return None 385 386 def endElement(self, name, value, connection): 387 if name == "EventId": 388 self.event_id = value 389 elif name == "LogicalResourceId": 390 self.logical_resource_id = value 391 elif name == "PhysicalResourceId": 392 self.physical_resource_id = value 393 elif name == "ResourceProperties": 394 self.resource_properties = value 395 elif name == "ResourceStatus": 396 self.resource_status = value 397 elif name == "ResourceStatusReason": 398 self.resource_status_reason = value 399 elif name == "ResourceType": 400 self.resource_type = value 401 elif name == "StackId": 402 self.stack_id = value 403 elif name == "StackName": 404 self.stack_name = value 405 elif name == "Timestamp": 406 try: 407 self.timestamp = datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ') 408 except ValueError: 409 self.timestamp = datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ') 410 else: 411 setattr(self, name, value) 412 413 def __repr__(self): 414 return "StackEvent %s %s %s" % (self.resource_type, 415 self.logical_resource_id, self.resource_status) 416