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.
- data/CHANGELOG.txt +7 -0
- data/README.md +25 -8
- data/bin/stackmate.rb +57 -53
- data/lib/stackmate/aws_attribs.rb +31 -31
- data/lib/stackmate/classmap.rb +33 -25
- data/lib/stackmate/client.rb +80 -0
- data/lib/stackmate/intrinsic_functions.rb +94 -38
- data/lib/stackmate/logging.rb +19 -19
- data/lib/stackmate/participants/cloudstack.rb +250 -165
- data/lib/stackmate/participants/cloudstack_affinitygroup.rb +122 -0
- data/lib/stackmate/participants/cloudstack_autoscalepolicy.rb +113 -0
- data/lib/stackmate/participants/cloudstack_autoscalevmgroup.rb +86 -0
- data/lib/stackmate/participants/cloudstack_autoscalevmprofile.rb +140 -0
- data/lib/stackmate/participants/cloudstack_condition.rb +122 -0
- data/lib/stackmate/participants/cloudstack_egressfirewallrule.rb +149 -0
- data/lib/stackmate/participants/cloudstack_firewallrule.rb +149 -0
- data/lib/stackmate/participants/cloudstack_globalloadbalancerrule.rb +158 -0
- data/lib/stackmate/participants/cloudstack_instancegroup.rb +113 -0
- data/lib/stackmate/participants/cloudstack_ipaddress.rb +149 -0
- data/lib/stackmate/participants/cloudstack_ipforwardingrule.rb +131 -0
- data/lib/stackmate/participants/cloudstack_iptonic.rb +95 -0
- data/lib/stackmate/participants/cloudstack_iso.rb +95 -0
- data/lib/stackmate/participants/cloudstack_lbhealthcheckpolicy.rb +140 -0
- data/lib/stackmate/participants/cloudstack_lbstickinesspolicy.rb +122 -0
- data/lib/stackmate/participants/cloudstack_loadbalancer.rb +158 -0
- data/lib/stackmate/participants/cloudstack_loadbalancerrule.rb +185 -0
- data/lib/stackmate/participants/cloudstack_network.rb +293 -0
- data/lib/stackmate/participants/cloudstack_networkacl.rb +176 -0
- data/lib/stackmate/participants/cloudstack_networkacllist.rb +104 -0
- data/lib/stackmate/participants/cloudstack_nictovirtualmachine.rb +104 -0
- data/lib/stackmate/participants/cloudstack_portforwardingrule.rb +176 -0
- data/lib/stackmate/participants/cloudstack_project.rb +113 -0
- data/lib/stackmate/participants/cloudstack_remoteaccessvpn.rb +122 -0
- data/lib/stackmate/participants/cloudstack_securitygroup.rb +122 -0
- data/lib/stackmate/participants/cloudstack_securitygroupegress.rb +185 -0
- data/lib/stackmate/participants/cloudstack_securitygroupingress.rb +185 -0
- data/lib/stackmate/participants/cloudstack_snapshot.rb +113 -0
- data/lib/stackmate/participants/cloudstack_snapshotpolicy.rb +122 -0
- data/lib/stackmate/participants/cloudstack_sshkeypair.rb +113 -0
- data/lib/stackmate/participants/cloudstack_staticnat.rb +113 -0
- data/lib/stackmate/participants/cloudstack_staticroute.rb +95 -0
- data/lib/stackmate/participants/cloudstack_tags.rb +113 -0
- data/lib/stackmate/participants/cloudstack_template.rb +212 -0
- data/lib/stackmate/participants/cloudstack_togloballoadbalancerrule.rb +104 -0
- data/lib/stackmate/participants/cloudstack_toloadbalancerrule.rb +95 -0
- data/lib/stackmate/participants/cloudstack_virtualmachine.rb +348 -0
- data/lib/stackmate/participants/cloudstack_virtualmachineops.rb +95 -0
- data/lib/stackmate/participants/cloudstack_vmsnapshot.rb +113 -0
- data/lib/stackmate/participants/cloudstack_volume.rb +176 -0
- data/lib/stackmate/participants/cloudstack_volumeops.rb +111 -0
- data/lib/stackmate/participants/cloudstack_vpc.rb +158 -0
- data/lib/stackmate/participants/cloudstack_vpnconnection.rb +95 -0
- data/lib/stackmate/participants/cloudstack_vpncustomergateway.rb +176 -0
- data/lib/stackmate/participants/cloudstack_vpngateway.rb +86 -0
- data/lib/stackmate/participants/cloudstack_vpnuser.rb +122 -0
- data/lib/stackmate/participants/common.rb +219 -70
- data/lib/stackmate/participants/stacknest.rb +6 -0
- data/lib/stackmate/resolver.rb +122 -0
- data/lib/stackmate/stack.rb +116 -60
- data/lib/stackmate/stack_executor.rb +99 -37
- data/lib/stackmate/stackpi.rb +46 -0
- data/lib/stackmate/version.rb +1 -1
- data/lib/stackmate/waitcondition_server.rb +15 -15
- data/lib/stackmate.rb +1 -1
- data/stackmate.gemspec +2 -4
- 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
|
-
|
14
|
+
super(msg)
|
12
15
|
end
|
13
|
-
end
|
16
|
+
end
|
14
17
|
|
15
|
-
class CloudStackResource < Ruote::Participant
|
16
|
-
|
18
|
+
class CloudStackResource < Ruote::Participant
|
19
|
+
include Logging
|
20
|
+
include Resolver
|
17
21
|
|
18
|
-
|
22
|
+
attr_reader :name
|
19
23
|
|
20
|
-
|
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
|
-
|
26
|
-
|
29
|
+
#@client = CloudstackRubyClient::Client.new(@url, @apikey, @seckey, false)
|
30
|
+
@client = StackMate::CloudStackClient.new(@url, @apikey, @seckey, false)
|
31
|
+
end
|
27
32
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
33
|
+
def on_workitem
|
34
|
+
p workitem.participant_name
|
35
|
+
reply
|
36
|
+
end
|
32
37
|
|
33
|
-
|
34
|
-
|
35
|
-
def
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
59
|
-
|
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
|
-
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
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
|
-
|
186
|
+
d.kind_of?(Hash) ? resolved[d['Ref']]: d
|
125
187
|
}
|
126
188
|
Base64.urlsafe_encode64(data.join(delim))
|
127
|
-
|
189
|
+
end
|
128
190
|
|
129
|
-
|
191
|
+
def load_local_mappings()
|
130
192
|
begin
|
131
|
-
|
193
|
+
@localized = YAML.load_file('local.yml')
|
132
194
|
rescue
|
133
|
-
|
195
|
+
logger.warning "Warning: Failed to load localized mappings from local.yaml\n"
|
134
196
|
end
|
135
|
-
|
197
|
+
end
|
136
198
|
|
137
|
-
|
138
|
-
if @localized['zoneid']
|
139
|
-
|
199
|
+
def default_zone_id
|
200
|
+
if @localized['zoneid']
|
201
|
+
@localized['zoneid']
|
140
202
|
else
|
141
|
-
|
203
|
+
'1'
|
142
204
|
end
|
143
|
-
|
205
|
+
end
|
144
206
|
|
145
|
-
|
207
|
+
def image_id(imgstring, resolved, mappings)
|
146
208
|
#TODO convoluted logic only handles the cases
|
147
209
|
#ImageId : {"Ref" : "FooBar"}
|
148
|
-
#ImageId :
|
149
|
-
#
|
150
|
-
#ImageId :
|
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
|
-
|
153
|
-
else
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
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
|
-
|
230
|
+
end
|
231
|
+
return mappings[imgstring['Fn::FindInMap'][0]][key][val]
|
170
232
|
end
|
233
|
+
end
|
234
|
+
|
171
235
|
end
|
172
236
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
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?
|
195
|
-
|
196
|
-
|
197
|
-
|
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
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
263
|
+
'startport' => rule['FromPort'],
|
264
|
+
'endport' => rule['ToPort'],
|
265
|
+
'protocol' => rule['IpProtocol'],
|
266
|
+
'cidrlist' => cidrIp
|
267
|
+
}
|
205
268
|
#TODO handle usersecuritygrouplist
|
206
|
-
|
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
|
-
|
216
|
-
|
296
|
+
class CloudStackOutput < Ruote::Participant
|
297
|
+
include Logging
|
298
|
+
include Intrinsic
|
217
299
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
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
|
-
|
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
|
+
|