vcloud-rest 0.3.0 → 1.0.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,428 @@
1
+ require 'vcloud-rest/vcloud/vapp_networking'
2
+
3
+ module VCloudClient
4
+ class Connection
5
+ ##
6
+ # Fetch details about a given vapp:
7
+ # - name
8
+ # - description
9
+ # - status
10
+ # - IP
11
+ # - Children VMs:
12
+ # -- IP addresses
13
+ # -- status
14
+ # -- ID
15
+ def get_vapp(vAppId)
16
+ params = {
17
+ 'method' => :get,
18
+ 'command' => "/vApp/vapp-#{vAppId}"
19
+ }
20
+
21
+ response, headers = send_request(params)
22
+
23
+ vapp_node = response.css('VApp').first
24
+ if vapp_node
25
+ name = vapp_node['name']
26
+ status = convert_vapp_status(vapp_node['status'])
27
+ end
28
+
29
+ description = response.css("Description").first
30
+ description = description.text unless description.nil?
31
+
32
+ ip = response.css('IpAddress').first
33
+ ip = ip.text unless ip.nil?
34
+
35
+ networks = response.css('NetworkConfig').reject{|n| n.attribute('networkName').text == 'none'}.
36
+ collect do |network|
37
+ net_name = network.attribute('networkName').text
38
+
39
+ gateway = network.css('Gateway')
40
+ gateway = gateway.text unless gateway.nil?
41
+
42
+ netmask = network.css('Netmask')
43
+ netmask = netmask.text unless netmask.nil?
44
+
45
+ fence_mode = network.css('FenceMode')
46
+ fence_mode = fence_mode.text unless fence_mode.nil?
47
+
48
+ parent_network = network.css('ParentNetwork')
49
+ parent_network = parent_network.attribute('name').text unless parent_network.empty?
50
+ parent_network = nil if parent_network.empty?
51
+
52
+ retain_network = network.css('RetainNetInfoAcrossDeployments')
53
+ retain_network = retain_network.text unless retain_network.nil?
54
+
55
+ # TODO: handle multiple scopes?
56
+ ipscope = {
57
+ :gateway => gateway,
58
+ :netmask => netmask,
59
+ :fence_mode => fence_mode,
60
+ :parent_network => parent_network,
61
+ :retain_network => retain_network
62
+ }
63
+
64
+ {
65
+ :name => net_name,
66
+ :scope => ipscope
67
+ }
68
+ end
69
+
70
+ vapp_snapshot = nil
71
+ response.css('SnapshotSection').each do |snapshot_section|
72
+ if snapshot_section['href'] =~ /.*\/vApp\/vapp\-/
73
+ snapshot = snapshot_section.css("Snapshot").first
74
+ if snapshot
75
+ vapp_snapshot = {
76
+ :size => snapshot['size'],
77
+ :creation_date => snapshot['created']
78
+ }
79
+ end
80
+ break
81
+ end
82
+ end
83
+
84
+ vms = response.css('Children Vm')
85
+ vms_hash = {}
86
+
87
+ vms.each do |vm|
88
+ vapp_local_id = vm.css('VAppScopedLocalId')
89
+ addresses = vm.css('rasd|Connection').collect do |n|
90
+ address = n['vcloud:ipAddress']
91
+ address = n.attributes['ipAddress'] unless address
92
+ address = address.value if address
93
+ end
94
+
95
+ vms_hash[vm['name']] = {
96
+ :addresses => addresses,
97
+ :status => convert_vapp_status(vm['status']),
98
+ :id => vm['href'].gsub(/.*\/vApp\/vm\-/, ""),
99
+ :vapp_scoped_local_id => vapp_local_id.text
100
+ }
101
+ end
102
+
103
+ { :id => vAppId, :name => name, :description => description,
104
+ :status => status, :ip => ip, :networks => networks,
105
+ :vapp_snapshot => vapp_snapshot, :vms_hash => vms_hash }
106
+ end
107
+
108
+ ##
109
+ # Friendly helper method to fetch a vApp by name
110
+ # - Organization object
111
+ # - Organization VDC Name
112
+ # - vApp name
113
+ def get_vapp_by_name(organization, vdcName, vAppName)
114
+ result = nil
115
+
116
+ get_vdc_by_name(organization, vdcName)[:vapps].each do |vapp|
117
+ if vapp[0].downcase == vAppName.downcase
118
+ result = get_vapp(vapp[1])
119
+ end
120
+ end
121
+
122
+ result
123
+ end
124
+
125
+ ##
126
+ # Delete a given vapp
127
+ # NOTE: It doesn't verify that the vapp is shutdown
128
+ def delete_vapp(vAppId)
129
+ params = {
130
+ 'method' => :delete,
131
+ 'command' => "/vApp/vapp-#{vAppId}"
132
+ }
133
+
134
+ response, headers = send_request(params)
135
+ task_id = headers[:location].gsub(/.*\/task\//, "")
136
+ task_id
137
+ end
138
+
139
+ ##
140
+ # Shutdown a given vapp
141
+ def poweroff_vapp(vAppId)
142
+ builder = Nokogiri::XML::Builder.new do |xml|
143
+ xml.UndeployVAppParams(
144
+ "xmlns" => "http://www.vmware.com/vcloud/v1.5") {
145
+ xml.UndeployPowerAction 'powerOff'
146
+ }
147
+ end
148
+
149
+ params = {
150
+ 'method' => :post,
151
+ 'command' => "/vApp/vapp-#{vAppId}/action/undeploy"
152
+ }
153
+
154
+ response, headers = send_request(params, builder.to_xml,
155
+ "application/vnd.vmware.vcloud.undeployVAppParams+xml")
156
+ task_id = headers[:location].gsub(/.*\/task\//, "")
157
+ task_id
158
+ end
159
+
160
+ ##
161
+ # Suspend a given vapp
162
+ def suspend_vapp(vAppId)
163
+ power_action(vAppId, 'suspend')
164
+ end
165
+
166
+ ##
167
+ # reboot a given vapp
168
+ # This will basically initial a guest OS reboot, and will only work if
169
+ # VMware-tools are installed on the underlying VMs.
170
+ # vShield Edge devices are not affected
171
+ def reboot_vapp(vAppId)
172
+ power_action(vAppId, 'reboot')
173
+ end
174
+
175
+ ##
176
+ # reset a given vapp
177
+ # This will basically reset the VMs within the vApp
178
+ # vShield Edge devices are not affected.
179
+ def reset_vapp(vAppId)
180
+ power_action(vAppId, 'reset')
181
+ end
182
+
183
+ ##
184
+ # Boot a given vapp
185
+ def poweron_vapp(vAppId)
186
+ power_action(vAppId, 'powerOn')
187
+ end
188
+
189
+ ##
190
+ # Create a vapp starting from a template
191
+ #
192
+ # Params:
193
+ # - vdc: the associated VDC
194
+ # - vapp_name: name of the target vapp
195
+ # - vapp_description: description of the target vapp
196
+ # - vapp_templateid: ID of the vapp template
197
+ def create_vapp_from_template(vdc, vapp_name, vapp_description, vapp_templateid, poweron=false)
198
+ builder = Nokogiri::XML::Builder.new do |xml|
199
+ xml.InstantiateVAppTemplateParams(
200
+ "xmlns" => "http://www.vmware.com/vcloud/v1.5",
201
+ "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
202
+ "xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1",
203
+ "name" => vapp_name,
204
+ "deploy" => "true",
205
+ "powerOn" => poweron) {
206
+ xml.Description vapp_description
207
+ xml.Source("href" => "#{@api_url}/vAppTemplate/#{vapp_templateid}")
208
+ }
209
+ end
210
+
211
+ params = {
212
+ "method" => :post,
213
+ "command" => "/vdc/#{vdc}/action/instantiateVAppTemplate"
214
+ }
215
+
216
+ response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml")
217
+
218
+ vapp_id = headers[:location].gsub(/.*\/vApp\/vapp\-/, "")
219
+ task = response.css("VApp Task[operationName='vdcInstantiateVapp']").first
220
+ task_id = task["href"].gsub(/.*\/task\//, "")
221
+
222
+ { :vapp_id => vapp_id, :task_id => task_id }
223
+ end
224
+
225
+ ##
226
+ # Compose a vapp using existing virtual machines
227
+ #
228
+ # Params:
229
+ # - vdc: the associated VDC
230
+ # - vapp_name: name of the target vapp
231
+ # - vapp_description: description of the target vapp
232
+ # - vm_list: hash with IDs of the VMs to be used in the composing process
233
+ # - network_config: hash of the network configuration for the vapp
234
+ def compose_vapp_from_vm(vdc, vapp_name, vapp_description, vm_list={}, network_config={})
235
+ builder = Nokogiri::XML::Builder.new do |xml|
236
+ xml.ComposeVAppParams(
237
+ "xmlns" => "http://www.vmware.com/vcloud/v1.5",
238
+ "xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1",
239
+ "name" => vapp_name) {
240
+ xml.Description vapp_description
241
+ xml.InstantiationParams {
242
+ xml.NetworkConfigSection {
243
+ xml['ovf'].Info "Configuration parameters for logical networks"
244
+ xml.NetworkConfig("networkName" => network_config[:name]) {
245
+ xml.Configuration {
246
+ xml.IpScopes {
247
+ xml.IpScope {
248
+ xml.IsInherited(network_config[:is_inherited] || "false")
249
+ xml.Gateway network_config[:gateway]
250
+ xml.Netmask network_config[:netmask]
251
+ xml.Dns1 network_config[:dns1] if network_config[:dns1]
252
+ xml.Dns2 network_config[:dns2] if network_config[:dns2]
253
+ xml.DnsSuffix network_config[:dns_suffix] if network_config[:dns_suffix]
254
+ xml.IpRanges {
255
+ xml.IpRange {
256
+ xml.StartAddress network_config[:start_address]
257
+ xml.EndAddress network_config[:end_address]
258
+ }
259
+ }
260
+ }
261
+ }
262
+ xml.ParentNetwork("href" => "#{@api_url}/network/#{network_config[:parent_network]}")
263
+ xml.FenceMode network_config[:fence_mode]
264
+
265
+ xml.Features {
266
+ xml.FirewallService {
267
+ xml.IsEnabled(network_config[:enable_firewall] || "false")
268
+ }
269
+ }
270
+ }
271
+ }
272
+ }
273
+ }
274
+ vm_list.each do |vm_name, vm_id|
275
+ xml.SourcedItem {
276
+ xml.Source("href" => "#{@api_url}/vAppTemplate/vm-#{vm_id}", "name" => vm_name)
277
+ xml.InstantiationParams {
278
+ xml.NetworkConnectionSection(
279
+ "xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1",
280
+ "type" => "application/vnd.vmware.vcloud.networkConnectionSection+xml",
281
+ "href" => "#{@api_url}/vAppTemplate/vm-#{vm_id}/networkConnectionSection/") {
282
+ xml['ovf'].Info "Network config for sourced item"
283
+ xml.PrimaryNetworkConnectionIndex "0"
284
+ xml.NetworkConnection("network" => network_config[:name]) {
285
+ xml.NetworkConnectionIndex "0"
286
+ xml.IsConnected "true"
287
+ xml.IpAddressAllocationMode(network_config[:ip_allocation_mode] || "POOL")
288
+ }
289
+ }
290
+ }
291
+ xml.NetworkAssignment("containerNetwork" => network_config[:name], "innerNetwork" => network_config[:name])
292
+ }
293
+ end
294
+ xml.AllEULAsAccepted "true"
295
+ }
296
+ end
297
+
298
+ params = {
299
+ "method" => :post,
300
+ "command" => "/vdc/#{vdc}/action/composeVApp"
301
+ }
302
+
303
+ response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.composeVAppParams+xml")
304
+
305
+ vapp_id = headers[:location].gsub(/.*\/vApp\/vapp\-/, "")
306
+
307
+ task = response.css("VApp Task[operationName='vdcComposeVapp']").first
308
+ task_id = task["href"].gsub(/.*\/task\//, "")
309
+
310
+ { :vapp_id => vapp_id, :task_id => task_id }
311
+ end
312
+
313
+ ##
314
+ # Create a new snapshot (overwrites any existing)
315
+ def create_snapshot(vappId, description="New Snapshot")
316
+ params = {
317
+ "method" => :post,
318
+ "command" => "/vApp/vapp-#{vappId}/action/createSnapshot"
319
+ }
320
+ builder = Nokogiri::XML::Builder.new do |xml|
321
+ xml.CreateSnapshotParams(
322
+ "xmlns" => "http://www.vmware.com/vcloud/v1.5") {
323
+ xml.Description description
324
+ }
325
+ end
326
+ response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.createSnapshotParams+xml")
327
+ task_id = headers[:location].gsub(/.*\/task\//, "")
328
+ task_id
329
+ end
330
+
331
+ ##
332
+ # Revert to an existing snapshot
333
+ def revert_snapshot(vappId)
334
+ params = {
335
+ "method" => :post,
336
+ "command" => "/vApp/vapp-#{vappId}/action/revertToCurrentSnapshot"
337
+ }
338
+ response, headers = send_request(params)
339
+ task_id = headers[:location].gsub(/.*\/task\//, "")
340
+ task_id
341
+ end
342
+
343
+ ##
344
+ # Clone a vapp in a given VDC to a new Vapp
345
+ def clone_vapp(vdc_id, source_vapp_id, name, deploy="true", poweron="false", linked="false", delete_source="false")
346
+ params = {
347
+ "method" => :post,
348
+ "command" => "/vdc/#{vdc_id}/action/cloneVApp"
349
+ }
350
+ builder = Nokogiri::XML::Builder.new do |xml|
351
+ xml.CloneVAppParams(
352
+ "xmlns" => "http://www.vmware.com/vcloud/v1.5",
353
+ "name" => name,
354
+ "deploy"=> deploy,
355
+ "linkedClone"=> linked,
356
+ "powerOn"=> poweron
357
+ ) {
358
+ xml.Source "href" => "#{@api_url}/vApp/vapp-#{source_vapp_id}"
359
+ xml.IsSourceDelete delete_source
360
+ }
361
+ end
362
+ response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.cloneVAppParams+xml")
363
+
364
+ vapp_id = headers[:location].gsub(/.*\/vApp\/vapp\-/, "")
365
+
366
+ task = response.css("VApp Task[operationName='vdcCopyVapp']").first
367
+ task_id = task["href"].gsub(/.*\/task\//, "")
368
+
369
+ {:vapp_id => vapp_id, :task_id => task_id}
370
+ end
371
+
372
+ # Fetch details about a given vapp template:
373
+ # - name
374
+ # - description
375
+ # - Children VMs:
376
+ # -- ID
377
+ def get_vapp_template(vAppId)
378
+ params = {
379
+ 'method' => :get,
380
+ 'command' => "/vAppTemplate/vappTemplate-#{vAppId}"
381
+ }
382
+
383
+ response, headers = send_request(params)
384
+
385
+ vapp_node = response.css('VAppTemplate').first
386
+ if vapp_node
387
+ name = vapp_node['name']
388
+ status = convert_vapp_status(vapp_node['status'])
389
+ end
390
+
391
+ description = response.css("Description").first
392
+ description = description.text unless description.nil?
393
+
394
+ ip = response.css('IpAddress').first
395
+ ip = ip.text unless ip.nil?
396
+
397
+ vms = response.css('Children Vm')
398
+ vms_hash = {}
399
+
400
+ vms.each do |vm|
401
+ vms_hash[vm['name']] = {
402
+ :id => vm['href'].gsub(/.*\/vAppTemplate\/vm\-/, "")
403
+ }
404
+ end
405
+
406
+ { :name => name, :description => description, :vms_hash => vms_hash }
407
+ end
408
+
409
+ ##
410
+ # Force a guest customization
411
+ def force_customization_vapp(vappId)
412
+ builder = Nokogiri::XML::Builder.new do |xml|
413
+ xml.DeployVAppParams(
414
+ "xmlns" => "http://www.vmware.com/vcloud/v1.5",
415
+ "forceCustomization" => "true")
416
+ end
417
+
418
+ params = {
419
+ "method" => :post,
420
+ "command" => "/vApp/vapp-#{vappId}/action/deploy"
421
+ }
422
+
423
+ response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.deployVAppParams+xml")
424
+ task_id = headers[:location].gsub(/.*\/task\//, "")
425
+ task_id
426
+ end
427
+ end
428
+ end