miasma-aws 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,86 @@
1
+ require 'miasma'
2
+
3
+ module Miasma
4
+ module Models
5
+ class AutoScale
6
+ class Aws < AutoScale
7
+
8
+ # Service name of the API
9
+ API_SERVICE = 'autoscaling'
10
+ # Supported version of the AutoScaling API
11
+ API_VERSION = '2011-01-01'
12
+
13
+ include Contrib::AwsApiCore::ApiCommon
14
+ include Contrib::AwsApiCore::RequestUtils
15
+
16
+ # Save auto scale group
17
+ #
18
+ # @param group [Models::AutoScale::Group]
19
+ # @return [Models::AutoScale::Group]
20
+ def group_save(group)
21
+ raise NotImplementedError
22
+ end
23
+
24
+ # Reload the group data from the API
25
+ #
26
+ # @param group [Models::AutoScale::Group]
27
+ # @return [Models::AutoScale::Group]
28
+ def group_reload(group)
29
+ if(group.id || group.name)
30
+ load_group_data(group)
31
+ end
32
+ group
33
+ end
34
+
35
+ # Delete auto scale group
36
+ #
37
+ # @param group [Models::AutoScale::Group]
38
+ # @return [TrueClass, FalseClass]
39
+ def group_destroy(group)
40
+ raise NotImplemented
41
+ end
42
+
43
+ # Fetch groups or update provided group data
44
+ #
45
+ # @param group [Models::AutoScale::Group]
46
+ # @return [Array<Models::AutoScale::Group>]
47
+ def load_group_data(group=nil)
48
+ params = Smash.new('Action' => 'DescribeAutoScalingGroups')
49
+ if(group)
50
+ params.merge('AutoScalingGroupNames.member.1' => group.id || group.name)
51
+ end
52
+ result = all_result_pages(nil, :body, 'DescribeAutoScalingGroupsResponse', 'DescribeAutoScalingGroupsResult', 'AutoScalingGroups', 'member') do |options|
53
+ request(
54
+ :path => '/',
55
+ :params => options.merge(params)
56
+ )
57
+ end.map do |grp|
58
+ (group || Group.new(self)).load_data(
59
+ :id => grp['AutoScalingGroupName'],
60
+ :name => grp['AutoScalingGroupName'],
61
+ :servers => [grp.get('Instances', 'member')].flatten(1).compact.map{|i|
62
+ Group::Server.new(self, :id => i['InstanceId'])
63
+ },
64
+ :minimum_size => grp['MinSize'],
65
+ :maximum_size => grp['MaxSize'],
66
+ :status => grp['Status'],
67
+ :load_balancers => [grp.get('LoadBalancerNames', 'member')].flatten(1).compact.map{|i|
68
+ Group::Balancer.new(self, :id => i, :name => i)
69
+ }
70
+ ).valid_state
71
+ end
72
+ end
73
+
74
+
75
+ # Return all auto scale groups
76
+ #
77
+ # @param options [Hash] filter
78
+ # @return [Array<Models::AutoScale::Group>]
79
+ def group_all(options={})
80
+ load_group_data
81
+ end
82
+
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,113 @@
1
+ require 'miasma'
2
+
3
+ module Miasma
4
+
5
+ module Models
6
+ class Compute
7
+ # Compute interface for AWS
8
+ class Aws < Compute
9
+
10
+ # Service name of the API
11
+ API_SERVICE = 'ec2'
12
+ # Supported version of the EC2 API
13
+ API_VERSION = '2014-06-15'
14
+
15
+ include Contrib::AwsApiCore::ApiCommon
16
+ include Contrib::AwsApiCore::RequestUtils
17
+
18
+ # @return [Smash] map state to valid internal values
19
+ SERVER_STATE_MAP = Smash.new(
20
+ 'running' => :running,
21
+ 'pending' => :pending,
22
+ 'shutting-down' => :pending,
23
+ 'terminated' => :terminated,
24
+ 'stopping' => :pending,
25
+ 'stopped' => :stopped
26
+ )
27
+
28
+ # @todo catch bad lookup and clear model
29
+ def server_reload(server)
30
+ result = request(
31
+ :path => '/',
32
+ :params => {
33
+ 'Action' => 'DescribeInstances',
34
+ 'InstanceId.1' => server.id
35
+ }
36
+ )
37
+ srv = result.get(:body, 'DescribeInstancesResponse', 'reservationSet', 'item', 'instancesSet', 'item')
38
+ server.load_data(
39
+ :id => srv[:instanceId],
40
+ :name => srv.fetch(:tagSet, :item, []).map{|tag| tag[:value] if tag.is_a?(Hash) && tag[:key] == 'Name'}.compact.first,
41
+ :image_id => srv[:imageId],
42
+ :flavor_id => srv[:instanceType],
43
+ :state => SERVER_STATE_MAP.fetch(srv.get(:instanceState, :name), :pending),
44
+ :addresses_private => [Server::Address.new(:version => 4, :address => srv[:privateIpAddress])],
45
+ :addresses_public => [Server::Address.new(:version => 4, :address => srv[:ipAddress])],
46
+ :status => srv.get(:instanceState, :name),
47
+ :key_name => srv[:keyName]
48
+ )
49
+ server.valid_state
50
+ end
51
+
52
+ def server_destroy(server)
53
+ if(server.persisted?)
54
+ result = request(
55
+ :path => '/',
56
+ :params => {
57
+ 'Action' => 'TerminateInstances',
58
+ 'InstanceId.1' => server.id
59
+ }
60
+ )
61
+ else
62
+ raise "this doesn't even exist"
63
+ end
64
+ end
65
+
66
+ def server_save(server)
67
+ unless(server.persisted?)
68
+ server.load_data(server.attributes)
69
+ result = request(
70
+ :path => '/',
71
+ :params => {
72
+ 'Action' => 'RunInstances',
73
+ 'ImageId' => server.image_id,
74
+ 'InstanceType' => server.flavor_id,
75
+ 'KeyName' => server.key_name,
76
+ 'MinCount' => 1,
77
+ 'MaxCount' => 1
78
+ }
79
+ )
80
+ server.id = result.get(:body, 'RunInstancesResponse', 'instancesSet', 'item', 'instanceId')
81
+ else
82
+ raise 'WAT DO I DO!?'
83
+ end
84
+ end
85
+
86
+ # @todo need to add auto pagination helper (as common util)
87
+ def server_all
88
+ results = all_result_pages(nil, :body, 'DescribeInstancesResponse', 'reservationSet', 'item') do |options|
89
+ request(:path => '/', :params => options.merge('Action' => 'DescribeInstances'))
90
+ end
91
+ results.map do |srv|
92
+ [srv[:instancesSet][:item]].flatten.compact.map do |srv|
93
+ Server.new(
94
+ self,
95
+ :id => srv[:instanceId],
96
+ :name => srv.fetch(:tagSet, :item, []).map{|tag| tag[:value] if tag.is_a?(Hash) && tag[:key] == 'Name'}.compact.first,
97
+ :image_id => srv[:imageId],
98
+ :flavor_id => srv[:instanceType],
99
+ :state => SERVER_STATE_MAP.fetch(srv.get(:instanceState, :name), :pending),
100
+ :addresses_private => [Server::Address.new(:version => 4, :address => srv[:privateIpAddress])],
101
+ :addresses_public => [Server::Address.new(:version => 4, :address => srv[:ipAddress])],
102
+ :status => srv.get(:instanceState, :name),
103
+ :key_name => srv[:keyName]
104
+ ).valid_state
105
+ end
106
+ end.flatten
107
+ end
108
+
109
+ end
110
+ end
111
+ end
112
+
113
+ end
@@ -0,0 +1,187 @@
1
+ require 'miasma'
2
+
3
+ module Miasma
4
+ module Models
5
+ class LoadBalancer
6
+ class Aws < LoadBalancer
7
+
8
+ include Contrib::AwsApiCore::ApiCommon
9
+ include Contrib::AwsApiCore::RequestUtils
10
+
11
+ # Service name of API
12
+ API_SERVICE = 'elasticloadbalancing'
13
+ # Supported version of the ELB API
14
+ API_VERSION = '2012-06-01'
15
+
16
+ # Save load balancer
17
+ #
18
+ # @param balancer [Models::LoadBalancer::Balancer]
19
+ # @return [Models::LoadBalancer::Balancer]
20
+ def balancer_save(balancer)
21
+ unless(balancer.persisted?)
22
+ params = Smash.new(
23
+ 'LoadBalancerName' => balancer.name
24
+ )
25
+ availability_zones.each_with_index do |az, i|
26
+ params["AvailabilityZones.member.#{i+1}"] = az
27
+ end
28
+ if(balancer.listeners)
29
+ balancer.listeners.each_with_index do |listener, i|
30
+ key = "Listeners.member.#{i + 1}"
31
+ params["#{key}.Protocol"] = listener.protocol
32
+ params["#{key}.InstanceProtocol"] = listener.instance_protocol
33
+ params["#{key}.LoadBalancerPort"] = listener.load_balancer_port
34
+ params["#{key}.InstancePort"] = listener.instance_port
35
+ if(listener.ssl_certificate_id)
36
+ params["#{key}.SSLCertificateId"] = listener.ssl_certificate_id
37
+ end
38
+ end
39
+ end
40
+ result = request(
41
+ :path => '/',
42
+ :params => params.merge(
43
+ Smash.new(
44
+ 'Action' => 'CreateLoadBalancer'
45
+ )
46
+ )
47
+ )
48
+ balancer.public_addresses = [
49
+ :address => result.get(:body, 'CreateLoadBalancerResponse', 'CreateLoadBalancerResult', 'DNSName')
50
+ ]
51
+ balancer.load_data(:id => balancer.name).valid_state
52
+ if(balancer.health_check)
53
+ balancer_health_check(balancer)
54
+ end
55
+ if(balancer.servers && !balancer.servers.empty?)
56
+ balancer_set_instances(balancer)
57
+ end
58
+ else
59
+ if(balancer.dirty?)
60
+ if(balancer.dirty?(:health_check))
61
+ balancer_health_check(balancer)
62
+ end
63
+ if(balancer.dirty?(:servers))
64
+ balancer_set_instances(balancer)
65
+ end
66
+ balancer.reload
67
+ end
68
+ balancer
69
+ end
70
+ end
71
+
72
+ # Save the load balancer health check
73
+ #
74
+ # @param balancer [Models::LoadBalancer::Balancer]
75
+ # @return [Models::LoadBalancer::Balancer]
76
+ def balancer_health_check(balancer)
77
+ balancer
78
+ end
79
+
80
+ # Save the load balancer attached servers
81
+ #
82
+ # @param balancer [Models::LoadBalancer::Balancer]
83
+ # @return [Models::LoadBalancer::Balancer]
84
+ def balancer_set_instances(balancer)
85
+ balancer
86
+ end
87
+
88
+ # Reload the balancer data from the API
89
+ #
90
+ # @param balancer [Models::LoadBalancer::Balancer]
91
+ # @return [Models::LoadBalancer::Balancer]
92
+ def balancer_reload(balancer)
93
+ if(balancer.persisted?)
94
+ load_balancer_data(balancer)
95
+ end
96
+ balancer
97
+ end
98
+
99
+ # Fetch balancers or update provided balancer data
100
+ #
101
+ # @param balancer [Models::LoadBalancer::Balancer]
102
+ # @return [Array<Models::LoadBalancer::Balancer>]
103
+ def load_balancer_data(balancer=nil)
104
+ params = Smash.new('Action' => 'DescribeLoadBalancers')
105
+ if(balancer)
106
+ params.merge('LoadBalancerNames.member.1' => balancer.id || balancer.name)
107
+ end
108
+ result = all_result_pages(nil, :body, 'DescribeLoadBalancersResponse', 'DescribeLoadBalancersResult', 'LoadBalancerDescriptions', 'member') do |options|
109
+ request(
110
+ :path => '/',
111
+ :params => options.merge(params)
112
+ )
113
+ end
114
+ result.map do |blr|
115
+ (balancer || Balancer.new(self)).load_data(
116
+ :id => blr['LoadBalancerName'],
117
+ :name => blr['LoadBalancerName'],
118
+ :state => :active,
119
+ :status => 'ACTIVE',
120
+ :created => blr['CreatedTime'],
121
+ :updated => blr['CreatedTime'],
122
+ :public_addresses => [
123
+ Balancer::Address.new(
124
+ :address => blr['DNSName'],
125
+ :version => 4
126
+ )
127
+ ],
128
+ :servers => [blr.get('Instances', 'member')].flatten(1).compact.map{|i|
129
+ Balancer::Server.new(self.api_for(:compute), :id => i['InstanceId'])
130
+ }
131
+ ).valid_state
132
+ end
133
+ end
134
+
135
+ # Delete load balancer
136
+ #
137
+ # @param balancer [Models::LoadBalancer::Balancer]
138
+ # @return [TrueClass, FalseClass]
139
+ def balancer_destroy(balancer)
140
+ if(balancer.persisted?)
141
+ request(
142
+ :path => '/',
143
+ :params => Smash.new(
144
+ 'Action' => 'DeleteLoadBalancer',
145
+ 'LoadBalancerName' => balancer.name
146
+ )
147
+ )
148
+ balancer.state = :pending
149
+ balancer.status = 'DELETE_IN_PROGRESS'
150
+ balancer.valid_state
151
+ true
152
+ else
153
+ false
154
+ end
155
+ end
156
+
157
+ # Return all load balancers
158
+ #
159
+ # @param options [Hash] filter
160
+ # @return [Array<Models::LoadBalancer::Balancer>]
161
+ def balancer_all(options={})
162
+ load_balancer_data
163
+ end
164
+
165
+ protected
166
+
167
+ # @return [Array<String>] availability zones
168
+ def availability_zones
169
+ memoize(:availability_zones) do
170
+ res = api_for(:compute).request(
171
+ :path => '/',
172
+ :params => Smash.new(
173
+ 'Action' => 'DescribeAvailabilityZones'
174
+ )
175
+ ).fetch(:body, 'DescribeAvailabilityZonesResponse', 'availabilityZoneInfo', 'item', [])
176
+ [res].flatten.compact.map do |item|
177
+ if(item['zoneState'] == 'available')
178
+ item['zoneName']
179
+ end
180
+ end.compact
181
+ end
182
+ end
183
+
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,350 @@
1
+ require 'miasma'
2
+
3
+ module Miasma
4
+ module Models
5
+ class Orchestration
6
+ class Aws < Orchestration
7
+
8
+ # Service name of the API
9
+ API_SERVICE = 'cloudformation'
10
+ # Supported version of the AutoScaling API
11
+ API_VERSION = '2010-05-15'
12
+
13
+ # Valid stack lookup states
14
+ STACK_STATES = [
15
+ "CREATE_COMPLETE", "CREATE_FAILED", "CREATE_IN_PROGRESS", "DELETE_FAILED",
16
+ "DELETE_IN_PROGRESS", "ROLLBACK_COMPLETE", "ROLLBACK_FAILED", "ROLLBACK_IN_PROGRESS",
17
+ "UPDATE_COMPLETE", "UPDATE_COMPLETE_CLEANUP_IN_PROGRESS", "UPDATE_IN_PROGRESS",
18
+ "UPDATE_ROLLBACK_COMPLETE", "UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS", "UPDATE_ROLLBACK_FAILED",
19
+ "UPDATE_ROLLBACK_IN_PROGRESS"
20
+ ]
21
+
22
+ include Contrib::AwsApiCore::ApiCommon
23
+ include Contrib::AwsApiCore::RequestUtils
24
+
25
+ # @return [Smash] external to internal resource mapping
26
+ RESOURCE_MAPPING = Smash.new(
27
+ 'AWS::EC2::Instance' => Smash.new(
28
+ :api => :compute,
29
+ :collection => :servers
30
+ ),
31
+ 'AWS::ElasticLoadBalancing::LoadBalancer' => Smash.new(
32
+ :api => :load_balancer,
33
+ :collection => :balancers
34
+ ),
35
+ 'AWS::AutoScaling::AutoScalingGroup' => Smash.new(
36
+ :api => :auto_scale,
37
+ :collection => :groups
38
+ )
39
+ )
40
+
41
+ # Fetch stacks or update provided stack data
42
+ #
43
+ # @param stack [Models::Orchestration::Stack]
44
+ # @return [Array<Models::Orchestration::Stack>]
45
+ def load_stack_data(stack=nil)
46
+ d_params = Smash.new('Action' => 'DescribeStacks')
47
+ l_params = Smash.new('Action' => 'ListStacks')
48
+ STACK_STATES.each_with_index do |state, idx|
49
+ l_params["StackStatusFilter.member.#{idx + 1}"] = state.to_s.upcase
50
+ end
51
+ if(stack)
52
+ d_params['StackName'] = stack.id
53
+ descriptions = all_result_pages(nil, :body, 'DescribeStacksResponse', 'DescribeStacksResult', 'Stacks', 'member') do |options|
54
+ request(
55
+ :path => '/',
56
+ :params => options.merge(d_params)
57
+ )
58
+ end
59
+ else
60
+ descriptions = []
61
+ end
62
+ lists = all_result_pages(nil, :body, 'ListStacksResponse', 'ListStacksResult', 'StackSummaries', 'member') do |options|
63
+ request(
64
+ :path => '/',
65
+ :params => options.merge(l_params)
66
+ )
67
+ end.map do |stk|
68
+ desc = descriptions.detect do |d_stk|
69
+ d_stk['StackId'] == stk['StackId']
70
+ end || Smash.new
71
+ stk.merge!(desc)
72
+ if(stack)
73
+ next if stack.id != stk['StackId'] && stk['StackId'].split('/')[1] != stack.id
74
+ end
75
+ new_stack = stack || Stack.new(self)
76
+ new_stack.load_data(
77
+ :id => stk['StackId'],
78
+ :name => stk['StackName'],
79
+ :capabilities => [stk.get('Capabilities', 'member')].flatten(1).compact,
80
+ :description => stk['Description'],
81
+ :created => stk['CreationTime'],
82
+ :updated => stk['LastUpdatedTime'],
83
+ :notification_topics => [stk.get('NotificationARNs', 'member')].flatten(1).compact,
84
+ :timeout_in_minutes => stk['TimeoutInMinutes'] ? stk['TimeoutInMinutes'].to_i : nil,
85
+ :status => stk['StackStatus'],
86
+ :status_reason => stk['StackStatusReason'],
87
+ :state => stk['StackStatus'].downcase.to_sym,
88
+ :template_description => stk['TemplateDescription'],
89
+ :disable_rollback => !!stk['DisableRollback'],
90
+ :outputs => [stk.get('Outputs', 'member')].flatten(1).compact.map{|o|
91
+ Smash.new(
92
+ :key => o['OutputKey'],
93
+ :value => o['OutputValue'],
94
+ :description => o['Description']
95
+ )
96
+ },
97
+ :parameters => Smash[
98
+ [stk.fetch('Parameters', 'member', [])].flatten(1).map{|param|
99
+ [param['ParameterKey'], param['ParameterValue']]
100
+ }
101
+ ]
102
+ ).valid_state
103
+ end
104
+ end
105
+
106
+ # Save the stack
107
+ #
108
+ # @param stack [Models::Orchestration::Stack]
109
+ # @return [Models::Orchestration::Stack]
110
+ def stack_save(stack)
111
+ params = Smash.new('StackName' => stack.name)
112
+ (stack.parameters || {}).each_with_index do |pair, idx|
113
+ params["Parameters.member.#{idx + 1}.ParameterKey"] = pair.first
114
+ params["Parameters.member.#{idx + 1}.ParameterValue"] = pair.last
115
+ end
116
+ (stack.capabilities || []).each_with_index do |cap, idx|
117
+ params["Capabilities.member.#{idx + 1}"] = cap
118
+ end
119
+ (stack.notification_topics || []).each_with_index do |topic, idx|
120
+ params["NotificationARNs.member.#{idx + 1}"] = topic
121
+ end
122
+ if(stack.template.empty?)
123
+ params['UsePreviousTemplate'] = true
124
+ else
125
+ params['TemplateBody'] = MultiJson.dump(stack.template)
126
+ end
127
+ if(stack.persisted?)
128
+ result = request(
129
+ :path => '/',
130
+ :method => :post,
131
+ :params => Smash.new(
132
+ 'Action' => 'UpdateStack'
133
+ ).merge(params)
134
+ )
135
+ stack
136
+ else
137
+ if(stack.timeout_in_minutes)
138
+ params['TimeoutInMinutes'] = stack.timeout_in_minutes
139
+ end
140
+ result = request(
141
+ :path => '/',
142
+ :method => :post,
143
+ :params => Smash.new(
144
+ 'Action' => 'CreateStack',
145
+ 'DisableRollback' => !!stack.disable_rollback
146
+ ).merge(params)
147
+ )
148
+ stack.id = result.get(:body, 'CreateStackResponse', 'CreateStackResult', 'StackId')
149
+ stack.valid_state
150
+ end
151
+ end
152
+
153
+ # Reload the stack data from the API
154
+ #
155
+ # @param stack [Models::Orchestration::Stack]
156
+ # @return [Models::Orchestration::Stack]
157
+ def stack_reload(stack)
158
+ if(stack.persisted?)
159
+ ustack = Stack.new(self)
160
+ ustack.id = stack.id
161
+ load_stack_data(ustack)
162
+ if(ustack.data[:name])
163
+ stack.load_data(ustack.attributes).valid_state
164
+ else
165
+ stack.status = 'DELETE_COMPLETE'
166
+ stack.state = :delete_complete
167
+ stack.valid_state
168
+ end
169
+ end
170
+ stack
171
+ end
172
+
173
+ # Delete the stack
174
+ #
175
+ # @param stack [Models::Orchestration::Stack]
176
+ # @return [TrueClass, FalseClass]
177
+ def stack_destroy(stack)
178
+ if(stack.persisted?)
179
+ request(
180
+ :path => '/',
181
+ :params => Smash.new(
182
+ 'Action' => 'DeleteStack',
183
+ 'StackName' => stack.id
184
+ )
185
+ )
186
+ true
187
+ else
188
+ false
189
+ end
190
+ end
191
+
192
+ # Fetch stack template
193
+ #
194
+ # @param stack [Stack]
195
+ # @return [Smash] stack template
196
+ def stack_template_load(stack)
197
+ if(stack.persisted?)
198
+ result = request(
199
+ :path => '/',
200
+ :params => Smash.new(
201
+ 'Action' => 'GetTemplate',
202
+ 'StackName' => stack.id
203
+ )
204
+ )
205
+ MultiJson.load(
206
+ result.get(:body, 'GetTemplateResponse', 'GetTemplateResult', 'TemplateBody')
207
+ ).to_smash
208
+ else
209
+ Smash.new
210
+ end
211
+ end
212
+
213
+ # Validate stack template
214
+ #
215
+ # @param stack [Stack]
216
+ # @return [NilClass, String] nil if valid, string error message if invalid
217
+ def stack_template_validate(stack)
218
+ begin
219
+ result = request(
220
+ :method => :post,
221
+ :path => '/',
222
+ :params => Smash.new(
223
+ 'Action' => 'ValidateTemplate',
224
+ 'TemplateBody' => MultiJson.dump(stack.template)
225
+ )
226
+ )
227
+ nil
228
+ rescue Error::ApiError::RequestError => e
229
+ MultiXml.parse(e.response.body.to_s).to_smash.get(
230
+ 'ErrorResponse', 'Error', 'Message'
231
+ )
232
+ end
233
+ end
234
+
235
+ # Return single stack
236
+ #
237
+ # @param ident [String] name or ID
238
+ # @return [Stack]
239
+ def stack_get(ident)
240
+ i = Stack.new(self)
241
+ i.id = ident
242
+ i.reload
243
+ i.name ? i : nil
244
+ end
245
+
246
+ # Return all stacks
247
+ #
248
+ # @param options [Hash] filter
249
+ # @return [Array<Models::Orchestration::Stack>]
250
+ # @todo check if we need any mappings on state set
251
+ def stack_all
252
+ load_stack_data
253
+ end
254
+
255
+ # Return all resources for stack
256
+ #
257
+ # @param stack [Models::Orchestration::Stack]
258
+ # @return [Array<Models::Orchestration::Stack::Resource>]
259
+ def resource_all(stack)
260
+ results = all_result_pages(nil, :body, 'DescribeStackResourcesResponse', 'DescribeStackResourcesResult', 'StackResources', 'member') do |options|
261
+ request(
262
+ :path => '/',
263
+ :params => options.merge(
264
+ Smash.new(
265
+ 'Action' => 'DescribeStackResources',
266
+ 'StackName' => stack.id
267
+ )
268
+ )
269
+ )
270
+ end.map do |res|
271
+ Stack::Resource.new(
272
+ stack,
273
+ :id => res['PhysicalResourceId'],
274
+ :name => res['LogicalResourceId'],
275
+ :logical_id => res['LogicalResourceId'],
276
+ :type => res['ResourceType'],
277
+ :state => res['ResourceStatus'].downcase.to_sym,
278
+ :status => res['ResourceStatus'],
279
+ :status_reason => res['ResourceStatusReason'],
280
+ :updated => res['Timestamp']
281
+ ).valid_state
282
+ end
283
+ end
284
+
285
+ # Reload the stack resource data from the API
286
+ #
287
+ # @param resource [Models::Orchestration::Stack::Resource]
288
+ # @return [Models::Orchestration::Resource]
289
+ def resource_reload(resource)
290
+ resource.stack.resources.reload
291
+ resource.stack.resources.get(resource.id)
292
+ end
293
+
294
+ # Return all events for stack
295
+ #
296
+ # @param stack [Models::Orchestration::Stack]
297
+ # @return [Array<Models::Orchestration::Stack::Event>]
298
+ def event_all(stack, evt_id=nil)
299
+ results = all_result_pages(nil, :body, 'DescribeStackEventsResponse', 'DescribeStackEventsResult', 'StackEvents', 'member') do |options|
300
+ request(
301
+ :path => '/',
302
+ :params => options.merge(
303
+ 'Action' => 'DescribeStackEvents',
304
+ 'StackName' => stack.id
305
+ )
306
+ )
307
+ end
308
+ events = results.map do |event|
309
+ Stack::Event.new(
310
+ stack,
311
+ :id => event['EventId'],
312
+ :resource_id => event['PhysicalResourceId'],
313
+ :resource_name => event['LogicalResourceId'],
314
+ :resource_logical_id => event['LogicalResourceId'],
315
+ :resource_state => event['ResourceStatus'].downcase.to_sym,
316
+ :resource_status => event['ResourceStatus'],
317
+ :resource_status_reason => event['ResourceStatusReason'],
318
+ :time => Time.parse(event['Timestamp'])
319
+ ).valid_state
320
+ end
321
+ if(evt_id)
322
+ idx = events.index{|d| e.id == evt_id}
323
+ idx = idx ? idx + 1 : 0
324
+ events.slice(idx, events.size)
325
+ else
326
+ events
327
+ end
328
+ end
329
+
330
+ # Return all new events for event collection
331
+ #
332
+ # @param events [Models::Orchestration::Stack::Events]
333
+ # @return [Array<Models::Orchestration::Stack::Event>]
334
+ def event_all_new(events)
335
+ event_all(events.stack, events.all.first.id)
336
+ end
337
+
338
+ # Reload the stack event data from the API
339
+ #
340
+ # @param resource [Models::Orchestration::Stack::Event]
341
+ # @return [Models::Orchestration::Event]
342
+ def event_reload(event)
343
+ event.stack.events.reload
344
+ event.stack.events.get(event.id)
345
+ end
346
+
347
+ end
348
+ end
349
+ end
350
+ end