stackmate 0.0.4 → 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.
Files changed (66) hide show
  1. data/CHANGELOG.txt +7 -0
  2. data/README.md +25 -8
  3. data/bin/stackmate.rb +57 -53
  4. data/lib/stackmate/aws_attribs.rb +31 -31
  5. data/lib/stackmate/classmap.rb +33 -25
  6. data/lib/stackmate/client.rb +80 -0
  7. data/lib/stackmate/intrinsic_functions.rb +94 -38
  8. data/lib/stackmate/logging.rb +19 -19
  9. data/lib/stackmate/participants/cloudstack.rb +250 -165
  10. data/lib/stackmate/participants/cloudstack_affinitygroup.rb +122 -0
  11. data/lib/stackmate/participants/cloudstack_autoscalepolicy.rb +113 -0
  12. data/lib/stackmate/participants/cloudstack_autoscalevmgroup.rb +86 -0
  13. data/lib/stackmate/participants/cloudstack_autoscalevmprofile.rb +140 -0
  14. data/lib/stackmate/participants/cloudstack_condition.rb +122 -0
  15. data/lib/stackmate/participants/cloudstack_egressfirewallrule.rb +149 -0
  16. data/lib/stackmate/participants/cloudstack_firewallrule.rb +149 -0
  17. data/lib/stackmate/participants/cloudstack_globalloadbalancerrule.rb +158 -0
  18. data/lib/stackmate/participants/cloudstack_instancegroup.rb +113 -0
  19. data/lib/stackmate/participants/cloudstack_ipaddress.rb +149 -0
  20. data/lib/stackmate/participants/cloudstack_ipforwardingrule.rb +131 -0
  21. data/lib/stackmate/participants/cloudstack_iptonic.rb +95 -0
  22. data/lib/stackmate/participants/cloudstack_iso.rb +95 -0
  23. data/lib/stackmate/participants/cloudstack_lbhealthcheckpolicy.rb +140 -0
  24. data/lib/stackmate/participants/cloudstack_lbstickinesspolicy.rb +122 -0
  25. data/lib/stackmate/participants/cloudstack_loadbalancer.rb +158 -0
  26. data/lib/stackmate/participants/cloudstack_loadbalancerrule.rb +185 -0
  27. data/lib/stackmate/participants/cloudstack_network.rb +293 -0
  28. data/lib/stackmate/participants/cloudstack_networkacl.rb +176 -0
  29. data/lib/stackmate/participants/cloudstack_networkacllist.rb +104 -0
  30. data/lib/stackmate/participants/cloudstack_nictovirtualmachine.rb +104 -0
  31. data/lib/stackmate/participants/cloudstack_portforwardingrule.rb +176 -0
  32. data/lib/stackmate/participants/cloudstack_project.rb +113 -0
  33. data/lib/stackmate/participants/cloudstack_remoteaccessvpn.rb +122 -0
  34. data/lib/stackmate/participants/cloudstack_securitygroup.rb +122 -0
  35. data/lib/stackmate/participants/cloudstack_securitygroupegress.rb +185 -0
  36. data/lib/stackmate/participants/cloudstack_securitygroupingress.rb +185 -0
  37. data/lib/stackmate/participants/cloudstack_snapshot.rb +113 -0
  38. data/lib/stackmate/participants/cloudstack_snapshotpolicy.rb +122 -0
  39. data/lib/stackmate/participants/cloudstack_sshkeypair.rb +113 -0
  40. data/lib/stackmate/participants/cloudstack_staticnat.rb +113 -0
  41. data/lib/stackmate/participants/cloudstack_staticroute.rb +95 -0
  42. data/lib/stackmate/participants/cloudstack_tags.rb +113 -0
  43. data/lib/stackmate/participants/cloudstack_template.rb +212 -0
  44. data/lib/stackmate/participants/cloudstack_togloballoadbalancerrule.rb +104 -0
  45. data/lib/stackmate/participants/cloudstack_toloadbalancerrule.rb +95 -0
  46. data/lib/stackmate/participants/cloudstack_virtualmachine.rb +348 -0
  47. data/lib/stackmate/participants/cloudstack_virtualmachineops.rb +95 -0
  48. data/lib/stackmate/participants/cloudstack_vmsnapshot.rb +113 -0
  49. data/lib/stackmate/participants/cloudstack_volume.rb +176 -0
  50. data/lib/stackmate/participants/cloudstack_volumeops.rb +111 -0
  51. data/lib/stackmate/participants/cloudstack_vpc.rb +158 -0
  52. data/lib/stackmate/participants/cloudstack_vpnconnection.rb +95 -0
  53. data/lib/stackmate/participants/cloudstack_vpncustomergateway.rb +176 -0
  54. data/lib/stackmate/participants/cloudstack_vpngateway.rb +86 -0
  55. data/lib/stackmate/participants/cloudstack_vpnuser.rb +122 -0
  56. data/lib/stackmate/participants/common.rb +219 -70
  57. data/lib/stackmate/participants/stacknest.rb +6 -0
  58. data/lib/stackmate/resolver.rb +122 -0
  59. data/lib/stackmate/stack.rb +116 -60
  60. data/lib/stackmate/stack_executor.rb +99 -37
  61. data/lib/stackmate/stackpi.rb +46 -0
  62. data/lib/stackmate/version.rb +1 -1
  63. data/lib/stackmate/waitcondition_server.rb +15 -15
  64. data/lib/stackmate.rb +1 -1
  65. data/stackmate.gemspec +2 -4
  66. metadata +70 -19
@@ -1,233 +1,318 @@
1
1
  require 'json'
2
- require 'cloudstack_ruby_client'
2
+ #require 'cloudstack_ruby_client'
3
+ require 'stackmate/client'
3
4
  require 'yaml'
4
5
  require 'stackmate/logging'
5
6
  require 'stackmate/intrinsic_functions'
7
+ require 'stackmate/resolver'
8
+
6
9
 
7
10
  module StackMate
8
11
 
9
- class CloudStackApiException < StandardError
12
+ class CloudStackApiException < StandardError
10
13
  def initialize(msg)
11
- super(msg)
14
+ super(msg)
12
15
  end
13
- end
16
+ end
14
17
 
15
- class CloudStackResource < Ruote::Participant
16
- include Logging
18
+ class CloudStackResource < Ruote::Participant
19
+ include Logging
20
+ include Resolver
17
21
 
18
- attr_reader :name
22
+ attr_reader :name
19
23
 
20
- def initialize(opts)
24
+ def initialize(opts)
21
25
  @opts = opts
22
26
  @url = opts['URL'] || ENV['URL'] or raise ArgumentError.new("CloudStackResources: no URL supplied for CloudStack API")
23
27
  @apikey = opts['APIKEY'] || ENV['APIKEY'] or raise ArgumentError.new("CloudStackResources: no api key supplied for CloudStack API")
24
28
  @seckey = opts['SECKEY'] || ENV['SECKEY'] or raise ArgumentError.new("CloudStackResources: no secret key supplied for CloudStack API")
25
- @client = CloudstackRubyClient::Client.new(@url, @apikey, @seckey, false)
26
- end
29
+ #@client = CloudstackRubyClient::Client.new(@url, @apikey, @seckey, false)
30
+ @client = StackMate::CloudStackClient.new(@url, @apikey, @seckey, false)
31
+ end
27
32
 
28
- def on_workitem
29
- p workitem.participant_name
30
- reply
31
- end
33
+ def on_workitem
34
+ p workitem.participant_name
35
+ reply
36
+ end
32
37
 
33
- protected
34
-
35
- def make_request(cmd, args)
36
- begin
37
- logger.debug "Going to make request #{cmd} to CloudStack server for resource #{@name}"
38
- resp = @client.send(cmd, args)
39
- jobid = resp['jobid'] if resp
40
- resp = api_poll(jobid, 3, 3) if jobid
41
- return resp
42
- rescue => e
43
- logger.error("Failed to make request #{cmd} to CloudStack server while creating resource #{@name}")
44
- logger.error e.message + "\n " + e.backtrace.join("\n ")
45
- raise e
46
- rescue SystemExit
47
- logger.error "Rescued a SystemExit exception"
48
- raise CloudStackApiException, "Did not get 200 OK while making api call #{cmd}"
49
- end
38
+ protected
39
+
40
+ def set_tags(tags,resourceId,resourceType)
41
+ tags_hash = resolve_tags(tags,workitem)
42
+ tags_args = {}
43
+ i = 0
44
+ tags_hash.each_key do |k|
45
+ tags_args["tags[#{i}].key"] = k
46
+ tags_args["tags[#{i}].value"] = tags_hash[k]
47
+ i = i + 1
48
+ end
49
+ tags_args['resourceids'] = resourceId
50
+ tags_args['resourcetype'] = resourceType
51
+ logger.debug("Attemping to add tags for resource #{@name}")
52
+ p tags_args
53
+ result_tags = make_async_request("createTags",tags_args)
54
+ if (!(result_tags['error']==true))
55
+ workitem[@name]['tags'] = tags_hash
56
+ else
57
+ logger.error("Unable to set tags for resource #{@name}")
58
+ end
59
+ end
60
+
61
+ def make_sync_request(cmd,args)
62
+ begin
63
+ logger.debug "Going to make sync request #{cmd} to CloudStack server for resource #{@name}"
64
+ #resp = @client.send(cmd, args)
65
+ resp = @client.api_call(cmd,args)
66
+ return resp
67
+ rescue => e
68
+ logger.error("Failed to make request #{cmd} to CloudStack server while creating resource #{@name}")
69
+ logger.debug e.message + "\n " + e.backtrace.join("\n ")
70
+ raise e
71
+ rescue SystemExit
72
+ logger.error "Rescued a SystemExit exception"
73
+ raise CloudStackApiException, "Did not get 200 OK while making api call #{cmd}"
74
+ end
50
75
  end
51
-
76
+
77
+ def make_async_request(cmd, args)
78
+ begin
79
+ logger.debug "Going to make async request #{cmd} to CloudStack server for resource #{@name}"
80
+ #resp = @client.send(cmd, args)
81
+ resp = @client.api_call(cmd,args)
82
+ jobid = resp['jobid'] if resp
83
+ resp = api_poll(jobid, 3, 3) if jobid
84
+ return resp
85
+ rescue => e
86
+ logger.error("Failed to make request #{cmd} to CloudStack server while creating resource #{@name}")
87
+ logger.debug e.message + "\n " + e.backtrace.join("\n ")
88
+ raise e
89
+ rescue SystemExit
90
+ logger.error "Rescued a SystemExit exception"
91
+ raise CloudStackApiException, "Did not get 200 OK while making api call #{cmd}"
92
+ end
93
+ end
94
+
52
95
  def api_poll (jobid, num, period)
53
- i = 0
54
- loop do
96
+ i = 0
97
+ loop do
55
98
  break if i > num
56
- resp = @client.queryAsyncJobResult({'jobid' => jobid})
99
+ #resp = @client.queryAsyncJobResult({'jobid' => jobid})
100
+ resp = @client.api_call("queryAsyncJobResult",{'jobid' => jobid})
57
101
  if resp
58
- return resp['jobresult'] if resp['jobstatus'] == 1
59
- return {'jobresult' => {}} if resp['jobstatus'] == 2
102
+ return resp['jobresult'] if resp['jobstatus'] == 1
103
+ return {'error' => true} if resp['jobstatus'] == 2
60
104
  end
61
105
  sleep(period)
62
- i += 1
106
+ i += 1
63
107
  end
64
- return {}
108
+ return {}
65
109
  end
66
110
 
67
- end
68
-
69
- class CloudStackInstance < CloudStackResource
70
- def initialize(opts)
71
- super (opts)
72
- @localized = {}
73
- load_local_mappings()
74
111
  end
75
112
 
76
- def on_workitem
77
- workitem[participant_name] = {}
78
- myname = participant_name
79
- @name = myname
80
- resolved = workitem.fields['ResolvedNames']
81
- props = workitem.fields['Resources'][workitem.participant_name]['Properties']
82
- security_group_names = []
83
- props['SecurityGroups'].each do |sg|
113
+ class CloudStackInstance < CloudStackResource
114
+ def initialize(opts)
115
+ super (opts)
116
+ @localized = {}
117
+ load_local_mappings()
118
+ end
119
+
120
+ def create
121
+ workitem[participant_name] = {}
122
+ myname = participant_name
123
+ @name = myname
124
+ resolved = workitem['ResolvedNames']
125
+ props = workitem['Resources'][workitem.participant_name]['Properties']
126
+ security_group_names = []
127
+ props['SecurityGroups'].each do |sg|
84
128
  sg_name = resolved[sg['Ref']]
85
129
  security_group_names << sg_name
86
- end
87
- keypair = resolved[props['KeyName']['Ref']] if props['KeyName']
88
- userdata = nil
89
- if props['UserData']
130
+ end
131
+ keypair = resolved[props['KeyName']['Ref']] if props['KeyName']
132
+ userdata = nil
133
+ if props['UserData']
90
134
  userdata = user_data(props['UserData'], resolved)
135
+ end
136
+ templateid = image_id(props['ImageId'], resolved, workitem['Mappings'])
137
+ templateid = @localized['templates'][templateid] if @localized['templates']
138
+ svc_offer = resolved[props['InstanceType']['Ref']] #TODO fragile
139
+ svc_offer = @localized['service_offerings'][svc_offer] if @localized['service_offerings']
140
+ args = { 'serviceofferingid' => svc_offer,
141
+ 'templateid' => templateid,
142
+ 'zoneid' => default_zone_id,
143
+ 'securitygroupnames' => security_group_names.join(','),
144
+ 'displayname' => myname,
145
+ #'name' => myname
146
+ }
147
+ args['keypair'] = keypair if keypair
148
+ args['userdata'] = userdata if userdata
149
+ resultobj = make_async_request('deployVirtualMachine', args)
150
+ logger.debug("Created resource #{myname}")
151
+
152
+ logger.debug("result = #{resultobj.inspect}")
153
+ workitem[participant_name][:physical_id] = resultobj['virtualmachine']['id']
154
+ workitem[participant_name][:AvailabilityZone] = resultobj['virtualmachine']['zoneid']
155
+ ipaddress = resultobj['virtualmachine']['nic'][0]['ipaddress']
156
+ workitem[participant_name][:PrivateDnsName] = ipaddress
157
+ workitem[participant_name][:PublicDnsName] = ipaddress
158
+ workitem[participant_name][:PrivateIp] = ipaddress
159
+ workitem[participant_name][:PublicIp] = ipaddress
160
+ end
161
+
162
+ def delete
163
+ logger.info "In delete #{participant_name}"
164
+ return nil if !workitem[participant_name]
165
+ physical_id = workitem[participant_name]['physical_id']
166
+ if physical_id
167
+ args = {'id' => physical_id}
168
+ del_resp = make_async_request('destroyVirtualMachine', args)
169
+ end
170
+ end
171
+
172
+ def on_workitem
173
+ if workitem['params']['operation'] == 'create'
174
+ create
175
+ else
176
+ delete
177
+ end
178
+ reply
91
179
  end
92
- templateid = image_id(props['ImageId'], resolved, workitem.fields['Mappings'])
93
- templateid = @localized['templates'][templateid] if @localized['templates']
94
- svc_offer = resolved[props['InstanceType']['Ref']] #TODO fragile
95
- svc_offer = @localized['service_offerings'][svc_offer] if @localized['service_offerings']
96
- args = { 'serviceofferingid' => svc_offer,
97
- 'templateid' => templateid,
98
- 'zoneid' => default_zone_id,
99
- 'securitygroupnames' => security_group_names.join(','),
100
- 'displayname' => myname,
101
- #'name' => myname
102
- }
103
- args['keypair'] = keypair if keypair
104
- args['userdata'] = userdata if userdata
105
- resultobj = make_request('deployVirtualMachine', args)
106
- logger.debug("Created resource #{myname}")
107
-
108
- logger.debug("result = #{resultobj.inspect}")
109
- workitem[participant_name][:physical_id] = resultobj['virtualmachine']['id']
110
- workitem[participant_name][:AvailabilityZone] = resultobj['virtualmachine']['zoneid']
111
- ipaddress = resultobj['virtualmachine']['nic'][0]['ipaddress']
112
- workitem[participant_name][:PrivateDnsName] = ipaddress
113
- workitem[participant_name][:PublicDnsName] = ipaddress
114
- workitem[participant_name][:PrivateIp] = ipaddress
115
- workitem[participant_name][:PublicIp] = ipaddress
116
- reply
117
- end
118
180
 
119
- def user_data(datum, resolved)
181
+ def user_data(datum, resolved)
120
182
  #TODO make this more general purpose
121
183
  actual = datum['Fn::Base64']['Fn::Join']
122
184
  delim = actual[0]
123
185
  data = actual[1].map { |d|
124
- d.kind_of?(Hash) ? resolved[d['Ref']]: d
186
+ d.kind_of?(Hash) ? resolved[d['Ref']]: d
125
187
  }
126
188
  Base64.urlsafe_encode64(data.join(delim))
127
- end
189
+ end
128
190
 
129
- def load_local_mappings()
191
+ def load_local_mappings()
130
192
  begin
131
- @localized = YAML.load_file('local.yml')
193
+ @localized = YAML.load_file('local.yml')
132
194
  rescue
133
- logger.warning "Warning: Failed to load localized mappings from local.yaml\n"
195
+ logger.warning "Warning: Failed to load localized mappings from local.yaml\n"
134
196
  end
135
- end
197
+ end
136
198
 
137
- def default_zone_id
138
- if @localized['zoneid']
139
- @localized['zoneid']
199
+ def default_zone_id
200
+ if @localized['zoneid']
201
+ @localized['zoneid']
140
202
  else
141
- '1'
203
+ '1'
142
204
  end
143
- end
205
+ end
144
206
 
145
- def image_id(imgstring, resolved, mappings)
207
+ def image_id(imgstring, resolved, mappings)
146
208
  #TODO convoluted logic only handles the cases
147
209
  #ImageId : {"Ref" : "FooBar"}
148
- #ImageId : { "Fn::FindInMap" : [ "Map1", { "Ref" : "OuterKey" },
149
- # { "Fn::FindInMap" : [ "Map2", { "Ref" : "InnerKey" }, "InnerVal" ] } ] },
150
- #ImageId : { "Fn::FindInMap" : [ "Map1", { "Ref" : "Key" }, "Value" ] } ] },
210
+ #ImageId : { "Fn::FindInMap" : [ "Map1", { "Ref" : "OuterKey" },
211
+ # { "Fn::FindInMap" : [ "Map2", { "Ref" : "InnerKey" }, "InnerVal" ] } ] },
212
+ #ImageId : { "Fn::FindInMap" : [ "Map1", { "Ref" : "Key" }, "Value" ] } ] },
151
213
  if imgstring['Ref']
152
- return resolved[imgstring['Ref']]
153
- else
154
- if imgstring['Fn::FindInMap']
155
- key = resolved[imgstring['Fn::FindInMap'][1]['Ref']]
156
- #print "Key = ", key, "\n"
157
- if imgstring['Fn::FindInMap'][2]['Ref']
158
- val = resolved[imgstring['Fn::FindInMap'][2]['Ref']]
159
- #print "Val [Ref] = ", val, "\n"
160
- else
161
- if imgstring['Fn::FindInMap'][2]['Fn::FindInMap']
162
- val = image_id(imgstring['Fn::FindInMap'][2], resolved, mappings)
163
- #print "Val [FindInMap] = ", val, "\n"
164
- else
165
- val = imgstring['Fn::FindInMap'][2]
166
- end
167
- end
214
+ return resolved[imgstring['Ref']]
215
+ else
216
+ if imgstring['Fn::FindInMap']
217
+ key = resolved[imgstring['Fn::FindInMap'][1]['Ref']]
218
+ #print "Key = ", key, "\n"
219
+ if imgstring['Fn::FindInMap'][2]['Ref']
220
+ val = resolved[imgstring['Fn::FindInMap'][2]['Ref']]
221
+ #print "Val [Ref] = ", val, "\n"
222
+ else
223
+ if imgstring['Fn::FindInMap'][2]['Fn::FindInMap']
224
+ val = image_id(imgstring['Fn::FindInMap'][2], resolved, mappings)
225
+ #print "Val [FindInMap] = ", val, "\n"
226
+ else
227
+ val = imgstring['Fn::FindInMap'][2]
228
+ end
168
229
  end
169
- return mappings[imgstring['Fn::FindInMap'][0]][key][val]
230
+ end
231
+ return mappings[imgstring['Fn::FindInMap'][0]][key][val]
170
232
  end
233
+ end
234
+
171
235
  end
172
236
 
173
- end
174
-
175
-
176
- class CloudStackSecurityGroup < CloudStackResource
177
- def on_workitem
178
- myname = workitem.participant_name
179
- workitem[participant_name] = {}
180
- logger.debug("Going to create resource #{myname}")
181
- @name = myname
182
- p myname
183
- resolved = workitem.fields['ResolvedNames']
184
- props = workitem.fields['Resources'][myname]['Properties']
185
- name = workitem.fields['StackName'] + '-' + workitem.participant_name;
186
- resolved[myname] = name
187
- args = { 'name' => name,
188
- 'description' => props['GroupDescription']
189
- }
190
- sg_resp = make_request('createSecurityGroup', args)
191
- logger.debug("created resource #{myname}")
192
- props['SecurityGroupIngress'].each do |rule|
237
+
238
+ class CloudStackSecurityGroupAWS < CloudStackResource
239
+
240
+ def create
241
+ myname = workitem.participant_name
242
+ workitem[participant_name] = {}
243
+ logger.debug("Going to create resource #{myname}")
244
+ @name = myname
245
+ p myname
246
+ resolved = workitem['ResolvedNames']
247
+ props = workitem['Resources'][myname]['Properties']
248
+ name = workitem['StackName'] + '-' + workitem.participant_name;
249
+ resolved[myname] = name
250
+ args = { 'name' => name,
251
+ 'description' => props['GroupDescription']
252
+ }
253
+ sg_resp = make_sync_request('createSecurityGroup', args)
254
+ logger.debug("created resource #{myname}")
255
+ props['SecurityGroupIngress'].each do |rule|
193
256
  cidrIp = rule['CidrIp']
194
- if cidrIp.kind_of? Hash
195
- #TODO: some sort of validation
196
- cidrIpName = cidrIp['Ref']
197
- cidrIp = resolved[cidrIpName]
257
+ if cidrIp.kind_of? Hash
258
+ #TODO: some sort of validation
259
+ cidrIpName = cidrIp['Ref']
260
+ cidrIp = resolved[cidrIpName]
198
261
  end
199
262
  args = { 'securitygroupname' => name,
200
- 'startport' => rule['FromPort'],
201
- 'endport' => rule['ToPort'],
202
- 'protocol' => rule['IpProtocol'],
203
- 'cidrlist' => cidrIp
204
- }
263
+ 'startport' => rule['FromPort'],
264
+ 'endport' => rule['ToPort'],
265
+ 'protocol' => rule['IpProtocol'],
266
+ 'cidrlist' => cidrIp
267
+ }
205
268
  #TODO handle usersecuritygrouplist
206
- make_request('authorizeSecurityGroupIngress', args)
269
+ make_async_request('authorizeSecurityGroupIngress', args)
270
+ end
271
+ workitem[participant_name][:physical_id] = sg_resp['securitygroup']['id']
272
+ end
273
+
274
+ def delete
275
+ logger.info "In delete #{participant_name}"
276
+ return nil if !workitem[participant_name]
277
+ logger.info "In delete #{participant_name} #{workitem[participant_name].inspect}"
278
+ physical_id = workitem[participant_name]['physical_id']
279
+ if physical_id
280
+ args = {'id' => physical_id}
281
+ del_resp = make_sync_request('deleteSecurityGroup', args)
282
+ end
283
+ end
284
+
285
+ def on_workitem
286
+ if workitem['params']['operation'] == 'create'
287
+ create
288
+ else
289
+ delete
290
+ end
291
+ reply
207
292
  end
208
- workitem[participant_name][:physical_id] = sg_resp['securitygroup']['id']
209
- reply
210
293
  end
211
- end
212
294
 
213
295
 
214
- class CloudStackOutput < Ruote::Participant
215
- include Logging
216
- include Intrinsic
296
+ class CloudStackOutput < Ruote::Participant
297
+ include Logging
298
+ include Intrinsic
217
299
 
218
- def on_workitem
219
- logger.debug "Entering #{participant_name} "
220
- outputs = workitem.fields['Outputs']
221
- outputs.each do |key, val|
222
- v = val['Value']
223
- constructed_value = intrinsic(v, workitem)
224
- val['Value'] = constructed_value
225
- logger.debug "Output: key = #{key}, value = #{constructed_value} descr = #{val['Description']}"
300
+ def on_workitem
301
+ logger.debug "Entering #{workitem.participant_name} "
302
+ outputs = workitem['Outputs']
303
+ outputs.each do |key, val|
304
+ v = val['Value']
305
+ constructed_value = intrinsic(v, workitem)
306
+ val['Value'] = constructed_value
307
+ workitem['Outputs'][key] = constructed_value
308
+ logger.debug "Output: key = #{key}, value = #{constructed_value} descr = #{val['Description']}"
309
+ end
310
+
311
+ logger.debug "Output Done"
312
+ reply
226
313
  end
227
- logger.debug "Output Done"
228
- reply
314
+
229
315
  end
230
316
 
231
- end
232
317
 
233
- end
318
+ end
@@ -0,0 +1,122 @@
1
+ require 'stackmate/participants/cloudstack'
2
+
3
+ module StackMate
4
+ class CloudStackAffinityGroup < CloudStackResource
5
+
6
+ include Logging
7
+ include Intrinsic
8
+ include Resolver
9
+ def create
10
+ logger.debug("Creating resource #{@name}")
11
+ workitem[@name] = {}
12
+ name_cs = workitem['StackName'] + '-' + @name
13
+ args={}
14
+ begin
15
+ args['name'] = workitem['StackName'] +'-' +get_name
16
+ args['type'] = get_type
17
+ args['domainid'] = get_domainid if @props.has_key?('domainid')
18
+ args['account'] = get_account if @props.has_key?('account')
19
+ args['description'] = get_description if @props.has_key?('description')
20
+
21
+ logger.info("Creating resource #{@name} with following arguments")
22
+ p args
23
+ result_obj = make_async_request('createAffinityGroup',args)
24
+ resource_obj = result_obj['AffinityGroup'.downcase]
25
+ #doing it this way since it is easier to change later, rather than cloning whole object
26
+ resource_obj.each_key do |k|
27
+ val = resource_obj[k]
28
+ if('id'.eql?(k))
29
+ k = 'physical_id'
30
+ end
31
+ workitem[@name][k] = val
32
+ end
33
+ set_tags(@props['tags'],workitem[@name]['physical_id'],"AffinityGroup") if @props.has_key?('tags')
34
+ workitem['ResolvedNames'][@name] = name_cs
35
+ workitem['IdMap'][workitem[@name]['physical_id']] = @name
36
+
37
+ rescue NoMethodError => nme
38
+ logger.error("Create request failed for resource . Cleaning up the stack")
39
+ raise nme
40
+ rescue Exception => e
41
+ logger.error(e.message)
42
+ raise e
43
+ end
44
+
45
+ end
46
+
47
+ def delete
48
+ logger.debug("Deleting resource #{@name}")
49
+ begin
50
+ physical_id = workitem[@name]['physical_id'] if !workitem[@name].nil?
51
+ if(!physical_id.nil?)
52
+ args = {'id' => physical_id
53
+ }
54
+ result_obj = make_async_request('deleteAffinityGroup',args)
55
+ if (!(result_obj['error'] == true))
56
+ logger.info("Successfully deleted resource #{@name}")
57
+ else
58
+ logger.info("CloudStack error while deleting resource #{@name}")
59
+ end
60
+ else
61
+ logger.info("Resource not created in CloudStack. Skipping delete...")
62
+ end
63
+ rescue Exception => e
64
+ logger.error("Unable to delete resorce #{@name}")
65
+ end
66
+ end
67
+
68
+ def on_workitem
69
+ @name = workitem.participant_name
70
+ @props = workitem['Resources'][@name]['Properties']
71
+ @props.downcase_key
72
+ @resolved_names = workitem['ResolvedNames']
73
+ if workitem['params']['operation'] == 'create'
74
+ create
75
+ else
76
+ delete
77
+ end
78
+ reply
79
+ end
80
+
81
+ def get_name
82
+ resolved_name = get_resolved(@props["name"],workitem)
83
+ if resolved_name.nil? || !validate_param(resolved_name,"string")
84
+ raise "Missing mandatory parameter name for resource #{@name}"
85
+ end
86
+ resolved_name
87
+ end
88
+
89
+ def get_type
90
+ resolved_type = get_resolved(@props["type"],workitem)
91
+ if resolved_type.nil? || !validate_param(resolved_type,"string")
92
+ raise "Missing mandatory parameter type for resource #{@name}"
93
+ end
94
+ resolved_type
95
+ end
96
+
97
+ def get_domainid
98
+ resolved_domainid = get_resolved(@props['domainid'],workitem)
99
+ if resolved_domainid.nil? || !validate_param(resolved_domainid,"uuid")
100
+ raise "Malformed optional parameter domainid for resource #{@name}"
101
+ end
102
+ resolved_domainid
103
+ end
104
+
105
+ def get_account
106
+ resolved_account = get_resolved(@props['account'],workitem)
107
+ if resolved_account.nil? || !validate_param(resolved_account,"string")
108
+ raise "Malformed optional parameter account for resource #{@name}"
109
+ end
110
+ resolved_account
111
+ end
112
+
113
+ def get_description
114
+ resolved_description = get_resolved(@props['description'],workitem)
115
+ if resolved_description.nil? || !validate_param(resolved_description,"string")
116
+ raise "Malformed optional parameter description for resource #{@name}"
117
+ end
118
+ resolved_description
119
+ end
120
+ end
121
+ end
122
+