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