vcloud-rest 0.3.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,396 @@
|
|
1
|
+
module VCloudClient
|
2
|
+
class Connection
|
3
|
+
##
|
4
|
+
# Retrieve information (i.e., memory and CPUs)
|
5
|
+
def get_vm_info(vmid)
|
6
|
+
params = {
|
7
|
+
'method' => :get,
|
8
|
+
'command' => "/vApp/vm-#{vmid}/virtualHardwareSection"
|
9
|
+
}
|
10
|
+
|
11
|
+
response, headers = send_request(params)
|
12
|
+
|
13
|
+
result = {}
|
14
|
+
response.css("ovf|Item [vcloud|href]").each do |item|
|
15
|
+
item_name = item.attribute('href').text.gsub(/.*\/vApp\/vm\-(\w+(-?))+\/virtualHardwareSection\//, "")
|
16
|
+
name = item.css("rasd|ElementName")
|
17
|
+
name = name.text unless name.nil?
|
18
|
+
|
19
|
+
description = item.css("rasd|Description")
|
20
|
+
description = description.text unless description.nil?
|
21
|
+
|
22
|
+
result[item_name] = {
|
23
|
+
:name => name,
|
24
|
+
:description => description
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
result
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Retrieve information about Disks
|
33
|
+
def get_vm_disk_info(vmid)
|
34
|
+
response, headers = __get_disk_info(vmid)
|
35
|
+
|
36
|
+
disks = []
|
37
|
+
response.css("Item").each do |entry|
|
38
|
+
# Pick only entries with node "HostResource"
|
39
|
+
resource = entry.css("rasd|HostResource").first
|
40
|
+
next unless resource
|
41
|
+
|
42
|
+
name = entry.css("rasd|ElementName").first
|
43
|
+
name = name.text unless name.nil?
|
44
|
+
capacity = resource.attribute("capacity").text
|
45
|
+
|
46
|
+
disks << {
|
47
|
+
:name => name,
|
48
|
+
:capacity => "#{capacity} MB"
|
49
|
+
}
|
50
|
+
end
|
51
|
+
disks
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Set information about Disks
|
56
|
+
#
|
57
|
+
# Disks can be added, deleted or modified
|
58
|
+
def set_vm_disk_info(vmid, disk_info={})
|
59
|
+
get_response, headers = __get_disk_info(vmid)
|
60
|
+
|
61
|
+
if disk_info[:add]
|
62
|
+
data = add_disk(get_response, disk_info)
|
63
|
+
else
|
64
|
+
data = edit_disk(get_response, disk_info)
|
65
|
+
end
|
66
|
+
|
67
|
+
params = {
|
68
|
+
'method' => :put,
|
69
|
+
'command' => "/vApp/vm-#{vmid}/virtualHardwareSection/disks"
|
70
|
+
}
|
71
|
+
put_response, headers = send_request(params, data, "application/vnd.vmware.vcloud.rasdItemsList+xml")
|
72
|
+
|
73
|
+
task_id = headers[:location].gsub(/.*\/task\//, "")
|
74
|
+
task_id
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Set VM CPUs
|
79
|
+
def set_vm_cpus(vmid, cpu_number)
|
80
|
+
params = {
|
81
|
+
'method' => :get,
|
82
|
+
'command' => "/vApp/vm-#{vmid}/virtualHardwareSection/cpu"
|
83
|
+
}
|
84
|
+
|
85
|
+
get_response, headers = send_request(params)
|
86
|
+
|
87
|
+
# Change attributes from the previous invocation
|
88
|
+
get_response.css("rasd|ElementName").first.content = "#{cpu_number} virtual CPU(s)"
|
89
|
+
get_response.css("rasd|VirtualQuantity").first.content = cpu_number
|
90
|
+
|
91
|
+
params['method'] = :put
|
92
|
+
put_response, headers = send_request(params, get_response.to_xml, "application/vnd.vmware.vcloud.rasdItem+xml")
|
93
|
+
|
94
|
+
task_id = headers[:location].gsub(/.*\/task\//, "")
|
95
|
+
task_id
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Set VM RAM
|
100
|
+
def set_vm_ram(vmid, memory_size)
|
101
|
+
params = {
|
102
|
+
'method' => :get,
|
103
|
+
'command' => "/vApp/vm-#{vmid}/virtualHardwareSection/memory"
|
104
|
+
}
|
105
|
+
|
106
|
+
get_response, headers = send_request(params)
|
107
|
+
|
108
|
+
# Change attributes from the previous invocation
|
109
|
+
get_response.css("rasd|ElementName").first.content = "#{memory_size} MB of memory"
|
110
|
+
get_response.css("rasd|VirtualQuantity").first.content = memory_size
|
111
|
+
|
112
|
+
params['method'] = :put
|
113
|
+
put_response, headers = send_request(params, get_response.to_xml, "application/vnd.vmware.vcloud.rasdItem+xml")
|
114
|
+
|
115
|
+
task_id = headers[:location].gsub(/.*\/task\//, "")
|
116
|
+
task_id
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# Set VM Network Config
|
121
|
+
def set_vm_network_config(vmid, network_name, config={})
|
122
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
123
|
+
xml.NetworkConnectionSection(
|
124
|
+
"xmlns" => "http://www.vmware.com/vcloud/v1.5",
|
125
|
+
"xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1") {
|
126
|
+
xml['ovf'].Info "VM Network configuration"
|
127
|
+
xml.PrimaryNetworkConnectionIndex(config[:primary_index] || 0)
|
128
|
+
xml.NetworkConnection("network" => network_name, "needsCustomization" => true) {
|
129
|
+
xml.NetworkConnectionIndex(config[:network_index] || 0)
|
130
|
+
xml.IpAddress config[:ip] if config[:ip]
|
131
|
+
xml.IsConnected(config[:is_connected] || true)
|
132
|
+
xml.IpAddressAllocationMode config[:ip_allocation_mode] if config[:ip_allocation_mode]
|
133
|
+
}
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
params = {
|
138
|
+
'method' => :put,
|
139
|
+
'command' => "/vApp/vm-#{vmid}/networkConnectionSection"
|
140
|
+
}
|
141
|
+
|
142
|
+
response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.networkConnectionSection+xml")
|
143
|
+
|
144
|
+
task_id = headers[:location].gsub(/.*\/task\//, "")
|
145
|
+
task_id
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
##
|
150
|
+
# Set VM Guest Customization Config
|
151
|
+
def set_vm_guest_customization(vmid, computer_name, config={})
|
152
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
153
|
+
xml.GuestCustomizationSection(
|
154
|
+
"xmlns" => "http://www.vmware.com/vcloud/v1.5",
|
155
|
+
"xmlns:ovf" => "http://schemas.dmtf.org/ovf/envelope/1") {
|
156
|
+
xml['ovf'].Info "VM Guest Customization configuration"
|
157
|
+
xml.Enabled config[:enabled] if config[:enabled]
|
158
|
+
xml.AdminPasswordEnabled config[:admin_passwd_enabled] if config[:admin_passwd_enabled]
|
159
|
+
xml.AdminPassword config[:admin_passwd] if config[:admin_passwd]
|
160
|
+
xml.CustomizationScript config[:customization_script] if config[:customization_script]
|
161
|
+
xml.ComputerName computer_name
|
162
|
+
}
|
163
|
+
end
|
164
|
+
|
165
|
+
params = {
|
166
|
+
'method' => :put,
|
167
|
+
'command' => "/vApp/vm-#{vmid}/guestCustomizationSection"
|
168
|
+
}
|
169
|
+
|
170
|
+
response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.guestCustomizationSection+xml")
|
171
|
+
|
172
|
+
task_id = headers[:location].gsub(/.*\/task\//, "")
|
173
|
+
task_id
|
174
|
+
end
|
175
|
+
|
176
|
+
##
|
177
|
+
# Force a guest customization
|
178
|
+
def force_customization_vm(vmId)
|
179
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
180
|
+
xml.DeployVAppParams(
|
181
|
+
"xmlns" => "http://www.vmware.com/vcloud/v1.5",
|
182
|
+
"forceCustomization" => "true")
|
183
|
+
end
|
184
|
+
|
185
|
+
params = {
|
186
|
+
"method" => :post,
|
187
|
+
"command" => "/vApp/vm-#{vmId}/action/deploy"
|
188
|
+
}
|
189
|
+
|
190
|
+
response, headers = send_request(params, builder.to_xml, "application/vnd.vmware.vcloud.deployVAppParams+xml")
|
191
|
+
task_id = headers[:location].gsub(/.*\/task\//, "")
|
192
|
+
task_id
|
193
|
+
end
|
194
|
+
|
195
|
+
def rename_vm(vmId, name)
|
196
|
+
params = {
|
197
|
+
'method' => :get,
|
198
|
+
'command' => "/vApp/vm-#{vmId}"
|
199
|
+
}
|
200
|
+
|
201
|
+
response, headers = send_request(params)
|
202
|
+
response.css('Vm').attribute("name").content = name
|
203
|
+
|
204
|
+
params['method'] = :put
|
205
|
+
response, headers = send_request(params, response.to_xml,
|
206
|
+
"application/vnd.vmware.vcloud.vm+xml")
|
207
|
+
task_id = headers[:location].gsub(/.*\/task\//, "")
|
208
|
+
task_id
|
209
|
+
end
|
210
|
+
|
211
|
+
##
|
212
|
+
# Fetch details about a given VM
|
213
|
+
def get_vm(vmId)
|
214
|
+
params = {
|
215
|
+
'method' => :get,
|
216
|
+
'command' => "/vApp/vm-#{vmId}"
|
217
|
+
}
|
218
|
+
|
219
|
+
response, headers = send_request(params)
|
220
|
+
|
221
|
+
vm_name = response.css('Vm').attribute("name")
|
222
|
+
vm_name = vm_name.text unless vm_name.nil?
|
223
|
+
|
224
|
+
status = convert_vapp_status(response.css('Vm').attribute("status").text)
|
225
|
+
|
226
|
+
os_desc = response.css('ovf|OperatingSystemSection ovf|Description').first.text
|
227
|
+
|
228
|
+
networks = {}
|
229
|
+
response.css('NetworkConnection').each do |network|
|
230
|
+
ip = network.css('IpAddress').first
|
231
|
+
ip = ip.text if ip
|
232
|
+
|
233
|
+
external_ip = network.css('ExternalIpAddress').first
|
234
|
+
external_ip = external_ip.text if external_ip
|
235
|
+
|
236
|
+
networks[network['network']] = {
|
237
|
+
:index => network.css('NetworkConnectionIndex').first.text,
|
238
|
+
:ip => ip,
|
239
|
+
:external_ip => external_ip,
|
240
|
+
:is_connected => network.css('IsConnected').first.text,
|
241
|
+
:mac_address => network.css('MACAddress').first.text,
|
242
|
+
:ip_allocation_mode => network.css('IpAddressAllocationMode').first.text
|
243
|
+
}
|
244
|
+
end
|
245
|
+
|
246
|
+
admin_password = response.css('GuestCustomizationSection AdminPassword').first
|
247
|
+
admin_password = admin_password.text if admin_password
|
248
|
+
|
249
|
+
guest_customizations = {
|
250
|
+
:enabled => response.css('GuestCustomizationSection Enabled').first.text,
|
251
|
+
:admin_passwd_enabled => response.css('GuestCustomizationSection AdminPasswordEnabled').first.text,
|
252
|
+
:admin_passwd_auto => response.css('GuestCustomizationSection AdminPasswordAuto').first.text,
|
253
|
+
:admin_passwd => admin_password,
|
254
|
+
:reset_passwd_required => response.css('GuestCustomizationSection ResetPasswordRequired').first.text,
|
255
|
+
:computer_name => response.css('GuestCustomizationSection ComputerName').first.text
|
256
|
+
}
|
257
|
+
|
258
|
+
{ :id => vmId,
|
259
|
+
:vm_name => vm_name, :os_desc => os_desc, :networks => networks,
|
260
|
+
:guest_customizations => guest_customizations, :status => status
|
261
|
+
}
|
262
|
+
end
|
263
|
+
|
264
|
+
##
|
265
|
+
# Friendly helper method to fetch a vApp by name
|
266
|
+
# - Organization object
|
267
|
+
# - Organization VDC Name
|
268
|
+
# - vApp Name
|
269
|
+
# - VM Name
|
270
|
+
def get_vm_by_name(organization, vdcName, vAppName, vmName)
|
271
|
+
result = nil
|
272
|
+
|
273
|
+
get_vapp_by_name(organization, vdcName, vAppName)[:vms_hash].each do |key, values|
|
274
|
+
if key.downcase == vmName.downcase
|
275
|
+
result = get_vm(values[:id])
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
result
|
280
|
+
end
|
281
|
+
|
282
|
+
##
|
283
|
+
# Shutdown a given vm
|
284
|
+
def poweroff_vm(vmId)
|
285
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
286
|
+
xml.UndeployVAppParams(
|
287
|
+
"xmlns" => "http://www.vmware.com/vcloud/v1.5") {
|
288
|
+
xml.UndeployPowerAction 'powerOff'
|
289
|
+
}
|
290
|
+
end
|
291
|
+
|
292
|
+
params = {
|
293
|
+
'method' => :post,
|
294
|
+
'command' => "/vApp/vm-#{vmId}/action/undeploy"
|
295
|
+
}
|
296
|
+
|
297
|
+
response, headers = send_request(params, builder.to_xml,
|
298
|
+
"application/vnd.vmware.vcloud.undeployVAppParams+xml")
|
299
|
+
task_id = headers[:location].gsub(/.*\/task\//, "")
|
300
|
+
task_id
|
301
|
+
end
|
302
|
+
|
303
|
+
##
|
304
|
+
# Suspend a given vm
|
305
|
+
def suspend_vm(vmId)
|
306
|
+
power_action(vmId, 'suspend', :vm)
|
307
|
+
end
|
308
|
+
|
309
|
+
##
|
310
|
+
# reboot a given vm
|
311
|
+
# This will basically initial a guest OS reboot, and will only work if
|
312
|
+
# VMware-tools are installed on the underlying VMs.
|
313
|
+
# vShield Edge devices are not affected
|
314
|
+
def reboot_vm(vmId)
|
315
|
+
power_action(vmId, 'reboot', :vm)
|
316
|
+
end
|
317
|
+
|
318
|
+
##
|
319
|
+
# reset a given vm
|
320
|
+
def reset_vm(vmId)
|
321
|
+
power_action(vmId, 'reset', :vm)
|
322
|
+
end
|
323
|
+
|
324
|
+
##
|
325
|
+
# Boot a given vm
|
326
|
+
def poweron_vm(vmId)
|
327
|
+
power_action(vmId, 'powerOn', :vm)
|
328
|
+
end
|
329
|
+
|
330
|
+
private
|
331
|
+
def add_disk(source_xml, disk_info)
|
332
|
+
disks_count = source_xml.css("Item").css("rasd|HostResource").count
|
333
|
+
|
334
|
+
# FIXME: This is a hack, but dealing with nokogiri APIs can be quite
|
335
|
+
# frustrating sometimes...
|
336
|
+
sibling = source_xml.css("Item").first
|
337
|
+
new_disk = Nokogiri::XML::Node.new "PLACEHOLDER", sibling.parent
|
338
|
+
sibling.add_next_sibling(new_disk)
|
339
|
+
result = source_xml.to_xml
|
340
|
+
|
341
|
+
result.gsub("<PLACEHOLDER/>", """
|
342
|
+
<Item>
|
343
|
+
<rasd:AddressOnParent>#{disks_count}</rasd:AddressOnParent>
|
344
|
+
<rasd:Description>Hard disk</rasd:Description>
|
345
|
+
<rasd:ElementName>Hard disk #{disks_count + 1}</rasd:ElementName>
|
346
|
+
<rasd:HostResource
|
347
|
+
xmlns:ns12=\"http://www.vmware.com/vcloud/v1.5\"
|
348
|
+
ns12:capacity=\"#{disk_info[:disk_size]}\"
|
349
|
+
ns12:busSubType=\"lsilogic\"
|
350
|
+
ns12:busType=\"6\"/>
|
351
|
+
<rasd:InstanceID>200#{disks_count}</rasd:InstanceID>
|
352
|
+
<rasd:Parent>1</rasd:Parent>
|
353
|
+
<rasd:ResourceType>17</rasd:ResourceType>
|
354
|
+
</Item>""")
|
355
|
+
end
|
356
|
+
|
357
|
+
def edit_disk(source_xml, disk_info)
|
358
|
+
changed = false
|
359
|
+
|
360
|
+
source_xml.css("Item").each do |entry|
|
361
|
+
# Pick only entries with node "HostResource"
|
362
|
+
resource = entry.css("rasd|HostResource").first
|
363
|
+
next unless resource
|
364
|
+
|
365
|
+
name = entry.css("rasd|ElementName").first
|
366
|
+
name = name.text unless name.nil?
|
367
|
+
next unless name == disk_info[:disk_name]
|
368
|
+
|
369
|
+
changed = true
|
370
|
+
|
371
|
+
if disk_info[:delete]
|
372
|
+
entry.remove
|
373
|
+
else
|
374
|
+
# Set disk size
|
375
|
+
resource.attribute("capacity").content = disk_info[:disk_size]
|
376
|
+
end
|
377
|
+
break
|
378
|
+
end
|
379
|
+
|
380
|
+
unless changed
|
381
|
+
@logger.warn "Disk #{disk_info[:disk_name]} not found."
|
382
|
+
raise WrongItemIDError, "Disk #{disk_info[:disk_name]} not found."
|
383
|
+
end
|
384
|
+
source_xml.to_xml
|
385
|
+
end
|
386
|
+
|
387
|
+
def __get_disk_info(vmid)
|
388
|
+
params = {
|
389
|
+
'method' => :get,
|
390
|
+
'command' => "/vApp/vm-#{vmid}/virtualHardwareSection/disks"
|
391
|
+
}
|
392
|
+
|
393
|
+
send_request(params)
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vcloud-rest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stefano Tortarolo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-11-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.
|
19
|
+
version: 1.5.10
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.
|
26
|
+
version: 1.5.10
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rest-client
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ~>
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 1.
|
61
|
+
version: 1.2.0
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ~>
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 1.
|
68
|
+
version: 1.2.0
|
69
69
|
description: Ruby bindings to create, list and manage vCloud servers
|
70
70
|
email:
|
71
71
|
- stefano.tortarolo@gmail.com
|
@@ -77,6 +77,15 @@ files:
|
|
77
77
|
- README.md
|
78
78
|
- LICENSE
|
79
79
|
- lib/vcloud-rest/connection.rb
|
80
|
+
- lib/vcloud-rest/vcloud/catalog.rb
|
81
|
+
- lib/vcloud-rest/vcloud/network.rb
|
82
|
+
- lib/vcloud-rest/vcloud/org.rb
|
83
|
+
- lib/vcloud-rest/vcloud/ovf.rb
|
84
|
+
- lib/vcloud-rest/vcloud/vapp.rb
|
85
|
+
- lib/vcloud-rest/vcloud/vapp_networking.rb
|
86
|
+
- lib/vcloud-rest/vcloud/vdc.rb
|
87
|
+
- lib/vcloud-rest/vcloud/vm.rb
|
88
|
+
- lib/vcloud-rest/version.rb
|
80
89
|
homepage: https://github.com/astratto/vcloud-rest
|
81
90
|
licenses:
|
82
91
|
- Apache 2.0
|
@@ -97,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
97
106
|
version: '0'
|
98
107
|
requirements: []
|
99
108
|
rubyforge_project:
|
100
|
-
rubygems_version: 2.
|
109
|
+
rubygems_version: 2.1.11
|
101
110
|
signing_key:
|
102
111
|
specification_version: 4
|
103
112
|
summary: Unofficial ruby bindings for VMWare vCloud's API
|