virtual_machine.rb
1 |
# -------------------------------------------------------------------------- #
|
---|---|
2 |
# Copyright 2002-2017, OpenNebula Project, OpenNebula Systems #
|
3 |
# #
|
4 |
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
|
5 |
# not use this file except in compliance with the License. You may obtain #
|
6 |
# a copy of the License at #
|
7 |
# #
|
8 |
# http://www.apache.org/licenses/LICENSE-2.0 #
|
9 |
# #
|
10 |
# Unless required by applicable law or agreed to in writing, software #
|
11 |
# distributed under the License is distributed on an "AS IS" BASIS, #
|
12 |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
|
13 |
# See the License for the specific language governing permissions and #
|
14 |
# limitations under the License. #
|
15 |
#--------------------------------------------------------------------------- #
|
16 |
|
17 |
require 'opennebula/pool_element'
|
18 |
|
19 |
module OpenNebula |
20 |
class VirtualMachine < PoolElement |
21 |
#######################################################################
|
22 |
# Constants and Class Methods
|
23 |
#######################################################################
|
24 |
|
25 |
VM_METHODS = {
|
26 |
:info => "vm.info", |
27 |
:allocate => "vm.allocate", |
28 |
:action => "vm.action", |
29 |
:migrate => "vm.migrate", |
30 |
:deploy => "vm.deploy", |
31 |
:chown => "vm.chown", |
32 |
:chmod => "vm.chmod", |
33 |
:monitoring => "vm.monitoring", |
34 |
:attach => "vm.attach", |
35 |
:detach => "vm.detach", |
36 |
:rename => "vm.rename", |
37 |
:update => "vm.update", |
38 |
:resize => "vm.resize", |
39 |
:snapshotcreate => "vm.snapshotcreate", |
40 |
:snapshotrevert => "vm.snapshotrevert", |
41 |
:snapshotdelete => "vm.snapshotdelete", |
42 |
:attachnic => "vm.attachnic", |
43 |
:detachnic => "vm.detachnic", |
44 |
:recover => "vm.recover", |
45 |
:disksaveas => "vm.disksaveas", |
46 |
:disksnapshotcreate => "vm.disksnapshotcreate", |
47 |
:disksnapshotrevert => "vm.disksnapshotrevert", |
48 |
:disksnapshotdelete => "vm.disksnapshotdelete", |
49 |
:diskresize => "vm.diskresize", |
50 |
:updateconf => "vm.updateconf" |
51 |
} |
52 |
|
53 |
VM_STATE=%w{INIT PENDING HOLD ACTIVE STOPPED SUSPENDED DONE FAILED |
54 |
POWEROFF UNDEPLOYED CLONING CLONING_FAILURE}
|
55 |
|
56 |
LCM_STATE=%w{ |
57 |
LCM_INIT
|
58 |
PROLOG
|
59 |
BOOT
|
60 |
RUNNING
|
61 |
MIGRATE
|
62 |
SAVE_STOP
|
63 |
SAVE_SUSPEND
|
64 |
SAVE_MIGRATE
|
65 |
PROLOG_MIGRATE
|
66 |
PROLOG_RESUME
|
67 |
EPILOG_STOP
|
68 |
EPILOG
|
69 |
SHUTDOWN
|
70 |
CANCEL
|
71 |
FAILURE
|
72 |
CLEANUP_RESUBMIT
|
73 |
UNKNOWN
|
74 |
HOTPLUG
|
75 |
SHUTDOWN_POWEROFF
|
76 |
BOOT_UNKNOWN
|
77 |
BOOT_POWEROFF
|
78 |
BOOT_SUSPENDED
|
79 |
BOOT_STOPPED
|
80 |
CLEANUP_DELETE
|
81 |
HOTPLUG_SNAPSHOT
|
82 |
HOTPLUG_NIC
|
83 |
HOTPLUG_SAVEAS
|
84 |
HOTPLUG_SAVEAS_POWEROFF
|
85 |
HOTPLUG_SAVEAS_SUSPENDED
|
86 |
SHUTDOWN_UNDEPLOY
|
87 |
EPILOG_UNDEPLOY
|
88 |
PROLOG_UNDEPLOY
|
89 |
BOOT_UNDEPLOY
|
90 |
HOTPLUG_PROLOG_POWEROFF
|
91 |
HOTPLUG_EPILOG_POWEROFF
|
92 |
BOOT_MIGRATE
|
93 |
BOOT_FAILURE
|
94 |
BOOT_MIGRATE_FAILURE
|
95 |
PROLOG_MIGRATE_FAILURE
|
96 |
PROLOG_FAILURE
|
97 |
EPILOG_FAILURE
|
98 |
EPILOG_STOP_FAILURE
|
99 |
EPILOG_UNDEPLOY_FAILURE
|
100 |
PROLOG_MIGRATE_POWEROFF
|
101 |
PROLOG_MIGRATE_POWEROFF_FAILURE
|
102 |
PROLOG_MIGRATE_SUSPEND
|
103 |
PROLOG_MIGRATE_SUSPEND_FAILURE
|
104 |
BOOT_UNDEPLOY_FAILURE
|
105 |
BOOT_STOPPED_FAILURE
|
106 |
PROLOG_RESUME_FAILURE
|
107 |
PROLOG_UNDEPLOY_FAILURE
|
108 |
DISK_SNAPSHOT_POWEROFF
|
109 |
DISK_SNAPSHOT_REVERT_POWEROFF
|
110 |
DISK_SNAPSHOT_DELETE_POWEROFF
|
111 |
DISK_SNAPSHOT_SUSPENDED
|
112 |
DISK_SNAPSHOT_REVERT_SUSPENDED
|
113 |
DISK_SNAPSHOT_DELETE_SUSPENDED
|
114 |
DISK_SNAPSHOT
|
115 |
DISK_SNAPSHOT_REVERT
|
116 |
DISK_SNAPSHOT_DELETE
|
117 |
PROLOG_MIGRATE_UNKNOWN
|
118 |
PROLOG_MIGRATE_UNKNOWN_FAILURE
|
119 |
DISK_RESIZE
|
120 |
DISK_RESIZE_POWEROFF
|
121 |
DISK_RESIZE_UNDEPLOYED
|
122 |
}
|
123 |
|
124 |
SHORT_VM_STATES={
|
125 |
"INIT" => "init", |
126 |
"PENDING" => "pend", |
127 |
"HOLD" => "hold", |
128 |
"ACTIVE" => "actv", |
129 |
"STOPPED" => "stop", |
130 |
"SUSPENDED" => "susp", |
131 |
"DONE" => "done", |
132 |
"FAILED" => "fail", |
133 |
"POWEROFF" => "poff", |
134 |
"UNDEPLOYED" => "unde", |
135 |
"CLONING" => "clon", |
136 |
"CLONING_FAILURE" => "fail" |
137 |
} |
138 |
|
139 |
SHORT_LCM_STATES={
|
140 |
"PROLOG" => "prol", |
141 |
"BOOT" => "boot", |
142 |
"RUNNING" => "runn", |
143 |
"MIGRATE" => "migr", |
144 |
"SAVE_STOP" => "save", |
145 |
"SAVE_SUSPEND" => "save", |
146 |
"SAVE_MIGRATE" => "save", |
147 |
"PROLOG_MIGRATE" => "migr", |
148 |
"PROLOG_RESUME" => "prol", |
149 |
"EPILOG_STOP" => "epil", |
150 |
"EPILOG" => "epil", |
151 |
"SHUTDOWN" => "shut", |
152 |
"CANCEL" => "shut", |
153 |
"FAILURE" => "fail", |
154 |
"CLEANUP_RESUBMIT" => "clea", |
155 |
"UNKNOWN" => "unkn", |
156 |
"HOTPLUG" => "hotp", |
157 |
"SHUTDOWN_POWEROFF" => "shut", |
158 |
"BOOT_UNKNOWN" => "boot", |
159 |
"BOOT_POWEROFF" => "boot", |
160 |
"BOOT_SUSPENDED" => "boot", |
161 |
"BOOT_STOPPED" => "boot", |
162 |
"CLEANUP_DELETE" => "clea", |
163 |
"HOTPLUG_SNAPSHOT" => "snap", |
164 |
"HOTPLUG_NIC" => "hotp", |
165 |
"HOTPLUG_SAVEAS" => "hotp", |
166 |
"HOTPLUG_SAVEAS_POWEROFF" => "hotp", |
167 |
"HOTPLUG_SAVEAS_SUSPENDED" => "hotp", |
168 |
"SHUTDOWN_UNDEPLOY" => "shut", |
169 |
"EPILOG_UNDEPLOY" => "epil", |
170 |
"PROLOG_UNDEPLOY" => "prol", |
171 |
"BOOT_UNDEPLOY" => "boot", |
172 |
"HOTPLUG_PROLOG_POWEROFF" => "hotp", |
173 |
"HOTPLUG_EPILOG_POWEROFF" => "hotp", |
174 |
"BOOT_MIGRATE" => "boot", |
175 |
"BOOT_FAILURE" => "fail", |
176 |
"BOOT_MIGRATE_FAILURE" => "fail", |
177 |
"PROLOG_MIGRATE_FAILURE" => "fail", |
178 |
"PROLOG_FAILURE" => "fail", |
179 |
"EPILOG_FAILURE" => "fail", |
180 |
"EPILOG_STOP_FAILURE" => "fail", |
181 |
"EPILOG_UNDEPLOY_FAILURE" => "fail", |
182 |
"PROLOG_MIGRATE_POWEROFF" => "migr", |
183 |
"PROLOG_MIGRATE_POWEROFF_FAILURE" => "fail", |
184 |
"PROLOG_MIGRATE_SUSPEND" => "migr", |
185 |
"PROLOG_MIGRATE_SUSPEND_FAILURE" => "fail", |
186 |
"BOOT_UNDEPLOY_FAILURE" => "fail", |
187 |
"BOOT_STOPPED_FAILURE" => "fail", |
188 |
"PROLOG_RESUME_FAILURE" => "fail", |
189 |
"PROLOG_UNDEPLOY_FAILURE" => "fail", |
190 |
"DISK_SNAPSHOT_POWEROFF" => "snap", |
191 |
"DISK_SNAPSHOT_REVERT_POWEROFF" => "snap", |
192 |
"DISK_SNAPSHOT_DELETE_POWEROFF" => "snap", |
193 |
"DISK_SNAPSHOT_SUSPENDED" => "snap", |
194 |
"DISK_SNAPSHOT_REVERT_SUSPENDED"=> "snap", |
195 |
"DISK_SNAPSHOT_DELETE_SUSPENDED"=> "snap", |
196 |
"DISK_SNAPSHOT" => "snap", |
197 |
"DISK_SNAPSHOT_DELETE" => "snap", |
198 |
"PROLOG_MIGRATE_UNKNOWN" => "migr", |
199 |
"PROLOG_MIGRATE_UNKNOWN_FAILURE" => "fail", |
200 |
"DISK_RESIZE" => "drsz", |
201 |
"DISK_RESIZE_POWEROFF" => "drsz", |
202 |
"DISK_RESIZE_UNDEPLOYED" => "drsz" |
203 |
} |
204 |
|
205 |
HISTORY_ACTION=%w{none migrate live-migrate shutdown shutdown-hard |
206 |
undeploy undeploy-hard hold release stop suspend resume boot delete
|
207 |
delete-recreate reboot reboot-hard resched unresched poweroff
|
208 |
poweroff-hard disk-attach disk-detach nic-attach nic-detach
|
209 |
disk-snapshot-create disk-snapshot-delete terminate terminate-hard
|
210 |
disk-resize deploy chown chmod updateconf rename resize update
|
211 |
snapshot-resize snapshot-delete snapshot-revert disk-saveas
|
212 |
disk-snapshot-revert recover retry monitor}
|
213 |
|
214 |
EXTERNAL_IP_ATTRS = [
|
215 |
'GUEST_IP',
|
216 |
'AWS_IP_ADDRESS',
|
217 |
'AWS_PUBLIC_IP_ADDRESS',
|
218 |
'AWS_PRIVATE_IP_ADDRESS',
|
219 |
'AZ_IPADDRESS',
|
220 |
'SL_PRIMARYIPADDRESS'
|
221 |
] |
222 |
|
223 |
# VirtualMachineDriver constants
|
224 |
module Driver |
225 |
POLL_ATTRIBUTE = {
|
226 |
:memory => "MEMORY", |
227 |
:cpu => "CPU", |
228 |
:nettx => "NETTX", |
229 |
:netrx => "NETRX", |
230 |
:state => "STATE", |
231 |
:disk_size => "DISK_SIZE", |
232 |
:snapshot_size => "SNAPSHOT_SIZE" |
233 |
} |
234 |
|
235 |
VM_STATE = {
|
236 |
:active => 'a', |
237 |
:paused => 'p', |
238 |
:error => 'e', |
239 |
:deleted => 'd', |
240 |
:unknown => '-' |
241 |
} |
242 |
end
|
243 |
|
244 |
# Creates a VirtualMachine description with just its identifier
|
245 |
# this method should be used to create plain VirtualMachine objects.
|
246 |
# +id+ the id of the vm
|
247 |
#
|
248 |
# Example:
|
249 |
# vnet = VirtualMachine.new(VirtualMachine.build_xml(3),rpc_client)
|
250 |
#
|
251 |
def VirtualMachine.build_xml(pe_id=nil) |
252 |
if pe_id
|
253 |
vm_xml = "<VM><ID>#{pe_id}</ID></VM>"
|
254 |
else
|
255 |
vm_xml = "<VM></VM>"
|
256 |
end
|
257 |
|
258 |
XMLElement.build_xml(vm_xml, 'VM') |
259 |
end
|
260 |
|
261 |
def VirtualMachine.get_history_action(action) |
262 |
return HISTORY_ACTION[action.to_i] |
263 |
end
|
264 |
|
265 |
# Class constructor
|
266 |
def initialize(xml, client) |
267 |
super(xml,client)
|
268 |
end
|
269 |
|
270 |
#######################################################################
|
271 |
# XML-RPC Methods for the Virtual Machine Object
|
272 |
#######################################################################
|
273 |
|
274 |
# Retrieves the information of the given VirtualMachine.
|
275 |
def info() |
276 |
super(VM_METHODS[:info], 'VM') |
277 |
end
|
278 |
|
279 |
alias_method :info!, :info |
280 |
|
281 |
# Allocates a new VirtualMachine in OpenNebula
|
282 |
#
|
283 |
# @param description [String] A string containing the template of
|
284 |
# the VirtualMachine.
|
285 |
# @param hold [true,false] false to create the VM in pending state,
|
286 |
# true to create it on hold
|
287 |
#
|
288 |
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
289 |
# otherwise
|
290 |
def allocate(description, hold=false) |
291 |
super(VM_METHODS[:allocate], description, hold) |
292 |
end
|
293 |
|
294 |
# Replaces the template contents
|
295 |
#
|
296 |
# @param new_template [String] New template contents
|
297 |
# @param append [true, false] True to append new attributes instead of
|
298 |
# replace the whole template
|
299 |
#
|
300 |
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
301 |
# otherwise
|
302 |
def update(new_template=nil, append=false) |
303 |
super(VM_METHODS[:update], new_template, append ? 1 : 0) |
304 |
end
|
305 |
|
306 |
# Returns the <USER_TEMPLATE> element in text form
|
307 |
#
|
308 |
# @param indent [true,false] indents the resulting string, defaults to true
|
309 |
#
|
310 |
# @return [String] The USER_TEMPLATE
|
311 |
def user_template_str(indent=true) |
312 |
template_like_str('USER_TEMPLATE', indent)
|
313 |
end
|
314 |
|
315 |
# Returns the <USER_TEMPLATE> element in XML form
|
316 |
#
|
317 |
# @return [String] The USER_TEMPLATE
|
318 |
def user_template_xml |
319 |
if NOKOGIRI |
320 |
@xml.xpath('USER_TEMPLATE').to_s |
321 |
else
|
322 |
@xml.elements['USER_TEMPLATE'].to_s |
323 |
end
|
324 |
end
|
325 |
|
326 |
|
327 |
# Initiates the instance of the VM on the target host.
|
328 |
#
|
329 |
# @param host_id [Interger] The host id (hid) of the target host where
|
330 |
# the VM will be instantiated.
|
331 |
# @param enforce [true|false] If it is set to true, the host capacity
|
332 |
# will be checked, and the deployment will fail if the host is
|
333 |
# overcommited. Defaults to false
|
334 |
# @param ds_id [Integer] The System Datastore where to deploy the VM. To
|
335 |
# use the default, set it to -1
|
336 |
#
|
337 |
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
338 |
# otherwise
|
339 |
def deploy(host_id, enforce=false, ds_id=-1) |
340 |
enforce ||= false
|
341 |
ds_id ||= -1
|
342 |
|
343 |
self.info
|
344 |
|
345 |
return call(VM_METHODS[:deploy], |
346 |
@pe_id,
|
347 |
host_id.to_i, |
348 |
enforce, |
349 |
ds_id.to_i) |
350 |
end
|
351 |
|
352 |
# Shutdowns an already deployed VM
|
353 |
def terminate(hard=false) |
354 |
action(hard ? 'terminate-hard' : 'terminate') |
355 |
end
|
356 |
|
357 |
alias_method :shutdown, :terminate |
358 |
|
359 |
# Shuts down an already deployed VM, saving its state in the system DS
|
360 |
def undeploy(hard=false) |
361 |
action(hard ? 'undeploy-hard' : 'undeploy') |
362 |
end
|
363 |
|
364 |
# Powers off a running VM
|
365 |
def poweroff(hard=false) |
366 |
action(hard ? 'poweroff-hard' : 'poweroff') |
367 |
end
|
368 |
|
369 |
# Reboots an already deployed VM
|
370 |
def reboot(hard=false) |
371 |
action(hard ? 'reboot-hard' : 'reboot') |
372 |
end
|
373 |
|
374 |
# Sets a VM to hold state, scheduler will not deploy it
|
375 |
def hold |
376 |
action('hold')
|
377 |
end
|
378 |
|
379 |
# Releases a VM from hold state
|
380 |
def release |
381 |
action('release')
|
382 |
end
|
383 |
|
384 |
# Stops a running VM
|
385 |
def stop |
386 |
action('stop')
|
387 |
end
|
388 |
|
389 |
# Saves a running VM
|
390 |
def suspend |
391 |
action('suspend')
|
392 |
end
|
393 |
|
394 |
# Resumes the execution of a saved VM
|
395 |
def resume |
396 |
action('resume')
|
397 |
end
|
398 |
|
399 |
# Attaches a disk to a running VM
|
400 |
#
|
401 |
# @param disk_template [String] Template containing a DISK element
|
402 |
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
403 |
# otherwise
|
404 |
def disk_attach(disk_template) |
405 |
return call(VM_METHODS[:attach], @pe_id, disk_template) |
406 |
end
|
407 |
|
408 |
alias_method :attachdisk, :disk_attach |
409 |
|
410 |
# Detaches a disk from a running VM
|
411 |
#
|
412 |
# @param disk_id [Integer] Id of the disk to be detached
|
413 |
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
414 |
# otherwise
|
415 |
def disk_detach(disk_id) |
416 |
return call(VM_METHODS[:detach], @pe_id, disk_id) |
417 |
end
|
418 |
|
419 |
alias_method :detachdisk, :disk_detach |
420 |
|
421 |
# Attaches a NIC to a running VM
|
422 |
#
|
423 |
# @param nic_template [String] Template containing a NIC element
|
424 |
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
425 |
# otherwise
|
426 |
def nic_attach(nic_template) |
427 |
return call(VM_METHODS[:attachnic], @pe_id, nic_template) |
428 |
end
|
429 |
|
430 |
# Detaches a NIC from a running VM
|
431 |
#
|
432 |
# @param nic_id [Integer] Id of the NIC to be detached
|
433 |
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
434 |
# otherwise
|
435 |
def nic_detach(nic_id) |
436 |
return call(VM_METHODS[:detachnic], @pe_id, nic_id) |
437 |
end
|
438 |
|
439 |
# Sets the re-scheduling flag for the VM
|
440 |
def resched |
441 |
action('resched')
|
442 |
end
|
443 |
|
444 |
# Unsets the re-scheduling flag for the VM
|
445 |
def unresched |
446 |
action('unresched')
|
447 |
end
|
448 |
|
449 |
# Moves a running VM to the specified host. With live=true the
|
450 |
# migration is done withdout downtime.
|
451 |
#
|
452 |
# @param host_id [Interger] The host id (hid) of the target host where
|
453 |
# the VM will be migrated.
|
454 |
# @param live [true|false] If true the migration is done without
|
455 |
# downtime. Defaults to false
|
456 |
# @param enforce [true|false] If it is set to true, the host capacity
|
457 |
# will be checked, and the deployment will fail if the host is
|
458 |
# overcommited. Defaults to false
|
459 |
# @param ds_id [Integer] The System Datastore where to migrate the VM.
|
460 |
# To use the current one, set it to -1
|
461 |
#
|
462 |
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
463 |
# otherwise
|
464 |
def migrate(host_id, live=false, enforce=false, ds_id=-1) |
465 |
call(VM_METHODS[:migrate], @pe_id, host_id.to_i, live==true, |
466 |
enforce, ds_id.to_i) |
467 |
end
|
468 |
|
469 |
# @deprecated use {#migrate} instead
|
470 |
def live_migrate(host_id, enforce=false) |
471 |
migrate(host_id, true, enforce)
|
472 |
end
|
473 |
|
474 |
# Set the specified vm's disk to be saved as a new image
|
475 |
#
|
476 |
# @param disk_id [Integer] ID of the disk to be saved
|
477 |
# @param image_name [String] Name for the new image where the
|
478 |
# disk will be saved
|
479 |
# @param image_type [String] Type of the new image. Set to empty string
|
480 |
# to use the default type
|
481 |
# @param snap_id [Integer] ID of the snapshot to save, -1 to use the
|
482 |
# current disk image state
|
483 |
#
|
484 |
# @return [Integer, OpenNebula::Error] the new Image ID in case of
|
485 |
# success, error otherwise
|
486 |
def disk_saveas(disk_id, image_name, image_type="", snap_id=-1) |
487 |
return Error.new('ID not defined') if !@pe_id |
488 |
|
489 |
rc = @client.call(VM_METHODS[:disksaveas], |
490 |
@pe_id,
|
491 |
disk_id, |
492 |
image_name, |
493 |
image_type, |
494 |
snap_id) |
495 |
return rc
|
496 |
end
|
497 |
|
498 |
# Resize the VM
|
499 |
#
|
500 |
# @param capacity_template [String] Template containing the new capacity
|
501 |
# elements CPU, VCPU, MEMORY. If one of them is not present, or its
|
502 |
# value is 0, it will not be resized
|
503 |
# @param enforce [true|false] If it is set to true, the host capacity
|
504 |
# will be checked. This will only affect oneadmin requests, regular users
|
505 |
# resize requests will always be enforced
|
506 |
#
|
507 |
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
508 |
# otherwise
|
509 |
def resize(capacity_template, enforce) |
510 |
return call(VM_METHODS[:resize], @pe_id, capacity_template, enforce) |
511 |
end
|
512 |
|
513 |
# Changes the owner/group
|
514 |
# uid:: _Integer_ the new owner id. Set to -1 to leave the current one
|
515 |
# gid:: _Integer_ the new group id. Set to -1 to leave the current one
|
516 |
# [return] nil in case of success or an Error object
|
517 |
def chown(uid, gid) |
518 |
super(VM_METHODS[:chown], uid, gid) |
519 |
end
|
520 |
|
521 |
# Changes the permissions.
|
522 |
#
|
523 |
# @param octet [String] Permissions octed , e.g. 640
|
524 |
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
525 |
# otherwise
|
526 |
def chmod_octet(octet) |
527 |
super(VM_METHODS[:chmod], octet) |
528 |
end
|
529 |
|
530 |
# Changes the permissions.
|
531 |
# Each [Integer] argument must be 1 to allow, 0 deny, -1 do not change
|
532 |
#
|
533 |
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
534 |
# otherwise
|
535 |
def chmod(owner_u, owner_m, owner_a, group_u, group_m, group_a, other_u, |
536 |
other_m, other_a) |
537 |
super(VM_METHODS[:chmod], owner_u, owner_m, owner_a, group_u, |
538 |
group_m, group_a, other_u, other_m, other_a) |
539 |
end
|
540 |
|
541 |
# Retrieves this VM's monitoring data from OpenNebula
|
542 |
#
|
543 |
# @param [Array<String>] xpath_expressions Elements to retrieve.
|
544 |
#
|
545 |
# @return [Hash<String, Array<Array<int>>>, OpenNebula::Error] Hash with
|
546 |
# the requested xpath expressions, and an Array of 'timestamp, value'.
|
547 |
#
|
548 |
# @example
|
549 |
# vm.monitoring( ['MONITORING/CPU', 'MONITORING/NETTX'] )
|
550 |
#
|
551 |
# {
|
552 |
# "MONITORING/CPU"=>[["1435085098", "47"], ["1435085253", "5"],
|
553 |
# ["1435085410", "48"], ["1435085566", "3"], ["1435088136", "2"]],
|
554 |
# "MONITORING/NETTX"=>[["1435085098", "0"], ["1435085253", "50"],
|
555 |
# ["1435085410", "50"], ["1435085566", "50"], ["1435085723", "50"]]
|
556 |
# }
|
557 |
#
|
558 |
def monitoring(xpath_expressions) |
559 |
return super(VM_METHODS[:monitoring], 'VM', |
560 |
'LAST_POLL', xpath_expressions)
|
561 |
end
|
562 |
|
563 |
# Retrieves this VM's monitoring data from OpenNebula, in XML
|
564 |
#
|
565 |
# @return [String] VM monitoring data, in XML
|
566 |
def monitoring_xml() |
567 |
return Error.new('ID not defined') if !@pe_id |
568 |
|
569 |
return @client.call(VM_METHODS[:monitoring], @pe_id) |
570 |
end
|
571 |
|
572 |
# Renames this VM
|
573 |
#
|
574 |
# @param name [String] New name for the VM.
|
575 |
#
|
576 |
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
577 |
# otherwise
|
578 |
def rename(name) |
579 |
return call(VM_METHODS[:rename], @pe_id, name) |
580 |
end
|
581 |
|
582 |
# Creates a new VM snapshot
|
583 |
#
|
584 |
# @param name [String] Name for the snapshot.
|
585 |
#
|
586 |
# @return [Integer, OpenNebula::Error] The new snaphost ID in case
|
587 |
# of success, Error otherwise
|
588 |
def snapshot_create(name="") |
589 |
return Error.new('ID not defined') if !@pe_id |
590 |
|
591 |
name ||= ""
|
592 |
return @client.call(VM_METHODS[:snapshotcreate], @pe_id, name) |
593 |
end
|
594 |
|
595 |
# Reverts to a snapshot
|
596 |
#
|
597 |
# @param snap_id [Integer] Id of the snapshot
|
598 |
#
|
599 |
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
600 |
# otherwise
|
601 |
def snapshot_revert(snap_id) |
602 |
return call(VM_METHODS[:snapshotrevert], @pe_id, snap_id) |
603 |
end
|
604 |
|
605 |
# Deletes a VM snapshot
|
606 |
#
|
607 |
# @param snap_id [Integer] Id of the snapshot
|
608 |
#
|
609 |
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
610 |
# otherwise
|
611 |
def snapshot_delete(snap_id) |
612 |
return call(VM_METHODS[:snapshotdelete], @pe_id, snap_id) |
613 |
end
|
614 |
|
615 |
# Takes a new snapshot of a disk
|
616 |
#
|
617 |
# @param disk_id [Integer] Id of the disk
|
618 |
# @param name [String] description for the snapshot
|
619 |
#
|
620 |
# @return [Integer, OpenNebula::Error] The new snapshot ID or error
|
621 |
def disk_snapshot_create(disk_id, name) |
622 |
return call(VM_METHODS[:disksnapshotcreate], @pe_id, disk_id, name) |
623 |
end
|
624 |
|
625 |
# Reverts disk state to a previously taken snapshot
|
626 |
#
|
627 |
# @param disk_id [Integer] Id of the disk
|
628 |
# @param snap_id [Integer] Id of the snapshot
|
629 |
#
|
630 |
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
631 |
# otherwise
|
632 |
def disk_snapshot_revert(disk_id, snap_id) |
633 |
return call(VM_METHODS[:disksnapshotrevert], @pe_id, disk_id, snap_id) |
634 |
end
|
635 |
|
636 |
# Deletes a disk snapshot
|
637 |
#
|
638 |
# @param disk_id [Integer] Id of the disk
|
639 |
# @param snap_id [Integer] Id of the snapshot
|
640 |
#
|
641 |
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
642 |
# otherwise
|
643 |
def disk_snapshot_delete(disk_id, snap_id) |
644 |
return call(VM_METHODS[:disksnapshotdelete], @pe_id, disk_id, snap_id) |
645 |
end
|
646 |
|
647 |
# Changes the size of a disk
|
648 |
#
|
649 |
# @param disk_id [Integer] Id of the disk
|
650 |
# @param size [Integer] new size in MiB
|
651 |
#
|
652 |
# @return [nil, OpenNebula::Error] nil in case of success or error
|
653 |
def disk_resize(disk_id, size) |
654 |
return call(VM_METHODS[:diskresize], @pe_id, disk_id, size.to_s) |
655 |
end
|
656 |
|
657 |
# Recovers an ACTIVE VM
|
658 |
#
|
659 |
# @param result [Integer] Recover with failure (0), success (1),
|
660 |
# retry (2), delete (3), delete-recreate (4)
|
661 |
# @param result [info] Additional information needed to recover the VM
|
662 |
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
663 |
# otherwise
|
664 |
def recover(result) |
665 |
return call(VM_METHODS[:recover], @pe_id, result) |
666 |
end
|
667 |
|
668 |
# Deletes a VM from the pool
|
669 |
def delete(recreate=false) |
670 |
if recreate
|
671 |
recover(4)
|
672 |
else
|
673 |
recover(3)
|
674 |
end
|
675 |
end
|
676 |
|
677 |
# Changes the attributes of a VM in power off, failure and undeploy
|
678 |
# states
|
679 |
# @param new_conf, string describing the new attributes. Each attribute
|
680 |
# will replace the existing ones or delete it if empty. Attributes that
|
681 |
# can be updated are: INPUT/{TYPE, BUS}; RAW/{TYPE, DATA, DATA_VMX},
|
682 |
# OS/{BOOT, BOOTLOADER, ARCH, MACHINE, KERNEL, INITRD},
|
683 |
# FEATURES/{ACPI, APIC, PAE, LOCALTIME, HYPERV, GUEST_AGENT},
|
684 |
# and GRAPHICS/{TYPE, LISTEN, PASSWD, KEYMAP}
|
685 |
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
686 |
# otherwise
|
687 |
def updateconf(new_conf) |
688 |
return call(VM_METHODS[:updateconf], @pe_id, new_conf) |
689 |
end
|
690 |
|
691 |
########################################################################
|
692 |
# Helpers to get VirtualMachine information
|
693 |
########################################################################
|
694 |
|
695 |
# Returns the VM state of the VirtualMachine (numeric value)
|
696 |
def state |
697 |
self['STATE'].to_i |
698 |
end
|
699 |
|
700 |
# Returns the VM state of the VirtualMachine (string value)
|
701 |
def state_str |
702 |
VM_STATE[state]
|
703 |
end
|
704 |
|
705 |
# Returns the LCM state of the VirtualMachine (numeric value)
|
706 |
def lcm_state |
707 |
self['LCM_STATE'].to_i |
708 |
end
|
709 |
|
710 |
# Returns the LCM state of the VirtualMachine (string value)
|
711 |
def lcm_state_str |
712 |
LCM_STATE[lcm_state]
|
713 |
end
|
714 |
|
715 |
# Returns the short status string for the VirtualMachine
|
716 |
def status |
717 |
short_state_str=SHORT_VM_STATES[state_str]
|
718 |
|
719 |
if short_state_str=="actv" |
720 |
short_state_str=SHORT_LCM_STATES[lcm_state_str]
|
721 |
end
|
722 |
|
723 |
short_state_str |
724 |
end
|
725 |
|
726 |
# Returns the group identifier
|
727 |
# [return] _Integer_ the element's group ID
|
728 |
def gid |
729 |
self['GID'].to_i |
730 |
end
|
731 |
|
732 |
# Returns the deploy_id of the VirtualMachine (numeric value)
|
733 |
def deploy_id |
734 |
self['DEPLOY_ID'] |
735 |
end
|
736 |
|
737 |
# Clones the VM's source Template, replacing the disks with live snapshots
|
738 |
# of the current disks. The VM capacity and NICs are also preserved
|
739 |
#
|
740 |
# @param name [String] Name for the new Template
|
741 |
# @param name [true,false,nil] Optional, true to make the saved images
|
742 |
# persistent, false make them non-persistent
|
743 |
#
|
744 |
# @return [Integer, OpenNebula::Error] the new Template ID in case of
|
745 |
# success, error otherwise
|
746 |
REMOVE_VNET_ATTRS = %w{AR_ID BRIDGE CLUSTER_ID IP MAC TARGET NIC_ID NETWORK_ID VN_MAD SECURITY_GROUPS} |
747 |
def save_as_template(name,description, persistent=nil) |
748 |
img_ids = [] |
749 |
new_tid = nil
|
750 |
begin
|
751 |
rc = info() |
752 |
raise if OpenNebula.is_error?(rc) |
753 |
|
754 |
tid = self['TEMPLATE/TEMPLATE_ID'] |
755 |
if tid.nil? || tid.empty?
|
756 |
rc = Error.new('VM has no template to be saved') |
757 |
raise |
758 |
end
|
759 |
|
760 |
#if state_str() != "POWEROFF"
|
761 |
# rc = Error.new("VM state must be POWEROFF, "<<
|
762 |
# "current state is #{state_str()}, #{lcm_state_str()}")
|
763 |
# raise
|
764 |
#end
|
765 |
|
766 |
# Clone the source template
|
767 |
rc = OpenNebula::Template.new_with_id(tid, @client).clone(name) |
768 |
raise if OpenNebula.is_error?(rc) |
769 |
|
770 |
new_tid = rc |
771 |
|
772 |
# Replace the original template's capacity with the actual VM values
|
773 |
replace = ""
|
774 |
if !description.nil?
|
775 |
replace << "DESCRIPTION = #{description}\n"
|
776 |
end
|
777 |
cpu = self['TEMPLATE/CPU'] |
778 |
if !cpu.nil? && !cpu.empty?
|
779 |
replace << "CPU = #{cpu}\n"
|
780 |
end
|
781 |
|
782 |
vcpu = self['TEMPLATE/VCPU'] |
783 |
if !vcpu.nil? && !vcpu.empty?
|
784 |
replace << "VCPU = #{vcpu}\n"
|
785 |
end
|
786 |
|
787 |
mem = self['TEMPLATE/MEMORY'] |
788 |
if !mem.nil? && !mem.empty?
|
789 |
replace << "MEMORY = #{mem}\n"
|
790 |
end
|
791 |
|
792 |
# stefan@bit.nl disable this to allow template save as without disks
|
793 |
# self.each('TEMPLATE/DISK') do |disk|
|
794 |
# # While the previous snapshot is still in progress, we wait
|
795 |
# # indefinitely
|
796 |
# rc = info()
|
797 |
# raise if OpenNebula.is_error?(rc)
|
798 |
|
799 |
# steps = 0
|
800 |
# while lcm_state_str() == "HOTPLUG_SAVEAS_POWEROFF"
|
801 |
# if steps < 30
|
802 |
# sleep 1
|
803 |
# else
|
804 |
# sleep 15
|
805 |
# end
|
806 |
|
807 |
# rc = info()
|
808 |
# raise if OpenNebula.is_error?(rc)
|
809 |
|
810 |
# steps += 1
|
811 |
# end
|
812 |
|
813 |
# # If the VM is not busy with a previous disk snapshot, we wait
|
814 |
# # but this time with a timeout
|
815 |
# rc = wait_state("POWEROFF")
|
816 |
# raise if OpenNebula.is_error?(rc)
|
817 |
|
818 |
# disk_id = disk["DISK_ID"]
|
819 |
# if disk_id.nil? || disk_id.empty?
|
820 |
# rc = Error.new('The DISK_ID is missing from the VM template')
|
821 |
# raise
|
822 |
# end
|
823 |
|
824 |
# image_id = disk["IMAGE_ID"]
|
825 |
|
826 |
# if !image_id.nil? && !image_id.empty?
|
827 |
# rc = disk_saveas(disk_id.to_i,"#{name}-disk-#{disk_id}","",-1)
|
828 |
|
829 |
# raise if OpenNebula.is_error?(rc)
|
830 |
|
831 |
# if persistent == true
|
832 |
# OpenNebula::Image.new_with_id(rc.to_i, @client).persistent()
|
833 |
# end
|
834 |
|
835 |
# img_ids << rc.to_i
|
836 |
|
837 |
# replace << "DISK = [ IMAGE_ID = #{rc} ]\n"
|
838 |
# else
|
839 |
# # Volatile disks cannot be saved, so the definition is copied
|
840 |
# replace << self.template_like_str(
|
841 |
# "TEMPLATE", true, "DISK[DISK_ID=#{disk_id}]") << "\n"
|
842 |
# end
|
843 |
# end
|
844 |
|
845 |
self.each('TEMPLATE/NIC') do |nic| |
846 |
|
847 |
nic_id = nic["NIC_ID"]
|
848 |
if nic_id.nil? || nic_id.empty?
|
849 |
rc = Error.new('The NIC_ID is missing from the VM template') |
850 |
raise |
851 |
end
|
852 |
REMOVE_VNET_ATTRS.each do |attr| |
853 |
nic.delete_element(attr) |
854 |
end
|
855 |
|
856 |
replace << self.template_like_str(
|
857 |
"TEMPLATE", true, "NIC[#{nic}]") << "\n" |
858 |
end
|
859 |
|
860 |
# Required by the Sunstone Cloud View
|
861 |
replace << "SAVED_TEMPLATE_ID = #{tid}\n"
|
862 |
|
863 |
new_tmpl = OpenNebula::Template.new_with_id(new_tid, @client) |
864 |
|
865 |
rc = new_tmpl.update(replace, true)
|
866 |
raise if OpenNebula.is_error?(rc) |
867 |
|
868 |
return new_tid
|
869 |
|
870 |
rescue
|
871 |
# Rollback. Delete the template and the images created
|
872 |
if !new_tid.nil?
|
873 |
new_tmpl = OpenNebula::Template.new_with_id(new_tid, @client) |
874 |
new_tmpl.delete() |
875 |
end
|
876 |
|
877 |
img_ids.each do |id|
|
878 |
img = OpenNebula::Image.new_with_id(id, @client) |
879 |
img.delete() |
880 |
end
|
881 |
|
882 |
return rc
|
883 |
end
|
884 |
end
|
885 |
|
886 |
private |
887 |
def action(name) |
888 |
return Error.new('ID not defined') if !@pe_id |
889 |
|
890 |
rc = @client.call(VM_METHODS[:action], name, @pe_id) |
891 |
rc = nil if !OpenNebula.is_error?(rc) |
892 |
|
893 |
return rc
|
894 |
end
|
895 |
|
896 |
def wait_state(state, timeout=10) |
897 |
vm_state = ""
|
898 |
lcm_state = ""
|
899 |
|
900 |
timeout.times do
|
901 |
rc = info() |
902 |
return rc if OpenNebula.is_error?(rc) |
903 |
|
904 |
vm_state = state_str() |
905 |
lcm_state = lcm_state_str() |
906 |
|
907 |
if vm_state == state
|
908 |
return true |
909 |
end
|
910 |
|
911 |
sleep 1
|
912 |
end
|
913 |
|
914 |
return Error.new("Timeout expired for state #{state}. "<< |
915 |
"VM is in state #{vm_state}, #{lcm_state}")
|
916 |
end
|
917 |
|
918 |
def wait_lcm_state(state, timeout=10) |
919 |
vm_state = ""
|
920 |
lcm_state = ""
|
921 |
|
922 |
timeout.times do
|
923 |
rc = info() |
924 |
return rc if OpenNebula.is_error?(rc) |
925 |
|
926 |
vm_state = state_str() |
927 |
lcm_state = lcm_state_str() |
928 |
|
929 |
if lcm_state == state
|
930 |
return true |
931 |
end
|
932 |
|
933 |
sleep 1
|
934 |
end
|
935 |
|
936 |
return Error.new("Timeout expired for state #{state}. "<< |
937 |
"VM is in state #{vm_state}, #{lcm_state}")
|
938 |
end
|
939 |
end
|
940 |
end
|