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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +60 -0
- data/README.md +21 -9
- data/lib/vcloud-rest/connection.rb +72 -890
- data/lib/vcloud-rest/vcloud/catalog.rb +113 -0
- data/lib/vcloud-rest/vcloud/network.rb +78 -0
- data/lib/vcloud-rest/vcloud/org.rb +145 -0
- data/lib/vcloud-rest/vcloud/ovf.rb +251 -0
- data/lib/vcloud-rest/vcloud/vapp.rb +428 -0
- data/lib/vcloud-rest/vcloud/vapp_networking.rb +331 -0
- data/lib/vcloud-rest/vcloud/vdc.rb +67 -0
- data/lib/vcloud-rest/vcloud/vm.rb +396 -0
- data/lib/vcloud-rest/version.rb +3 -0
- metadata +18 -9
@@ -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
|