module OpenNebula::VirtualMachineExt
Module to decorate VirtualMachine
class with additional helpers not directly exposed through the OpenNebula
XMLRPC API. The extensions include
- mp_import helper that imports a template into a marketplace
rubocop:disable Style/ClassAndModuleChildren
Public Class Methods
Source
# File lib/opennebula/virtual_machine_ext.rb, line 26 def self.extend_object(obj) if !obj.is_a?(OpenNebula::VirtualMachine) raise StandardError, "Cannot extended #{obj.class} "\ ' with MarketPlaceAppExt' end class << obj #################################################################### # Public extended interface #################################################################### #------------------------------------------------------------------- # Clones the VM's source Template, replacing the disks with live # snapshots of the current disks. The VM capacity and NICs are also # preserved # # @param name [String] Name for the new Template # @param name [true,false,nil] Optional, true to make the saved # images persistent, false make them non-persistent # # @return [Integer, OpenNebula::Error] the new Template ID in case # of success, error otherwise #------------------------------------------------------------------- REMOVE_VNET_ATTRS = ['AR_ID', 'BRIDGE', 'CLUSTER_ID', 'IP', 'MAC', 'TARGET', 'NIC_ID', 'NETWORK_ID', 'VN_MAD', 'SECURITY_GROUPS', 'VLAN_ID', 'BRIDGE_TYPE'] REMOVE_IMAGE_ATTRS = ['DEV_PREFIX', 'SOURCE', 'DRIVER', 'FORMAT', 'ORIGINAL_SIZE', 'DISK_SNAPSHOT_TOTAL_SIZE', 'IMAGE_STATE', 'SAVE', 'CLONE', 'READONLY', 'PERSISTENT', 'TARGET', 'ALLOW_ORPHANS', 'CLONE_TARGET', 'CLUSTER_ID', 'DATASTORE', 'DATASTORE_ID', 'DISK_ID', 'DISK_TYPE', 'IMAGE_ID', 'IMAGE', 'IMAGE_UNAME', 'IMAGE_UID', 'LN_TARGET', 'TM_MAD', 'TM_MAD_SYSTEM', 'OPENNEBULA_MANAGED', 'PERSISTENT_SNAPSHOTS'] def save_as_template(name, desc, opts = {}) opts = { :persistent => false, :poweroff => false, :logger => nil }.merge(opts) img_ids = [] ntid = nil logger = opts[:logger] poweron = false rc = info raise rc.message if OpenNebula.is_error?(rc) tid = self['TEMPLATE/TEMPLATE_ID'] raise 'VM has no associated template' unless valid?(tid) # -------------------------------------------------------------- # Check VM state and poweroff it if needed # -------------------------------------------------------------- if state_str != 'POWEROFF' raise 'VM must be POWEROFF' unless opts[:poweroff] logger.info 'Powering off VM' if logger poweron = true rc = poweroff raise rc.message if OpenNebula.is_error?(rc) end # -------------------------------------------------------------- # Clone the source template # -------------------------------------------------------------- vm_template = OpenNebula::Template.new_with_id(tid, @client) ntid = vm_template.clone(name) raise ntid.message if OpenNebula.is_error?(ntid) # -------------------------------------------------------------- # Replace the original template's capacity with VM values # -------------------------------------------------------------- cpu = self['TEMPLATE/CPU'] vcpu = self['TEMPLATE/VCPU'] mem = self['TEMPLATE/MEMORY'] replace = '' replace << "DESCRIPTION = \"#{desc}\"\n" if valid?(desc) replace << "CPU = #{cpu}\n" if valid?(cpu) replace << "VCPU = #{vcpu}\n" if valid?(vcpu) replace << "MEMORY = #{mem}\n" if valid?(mem) # -------------------------------------------------------------- # Process VM DISKs # -------------------------------------------------------------- logger.info 'Processing VM disks' if logger each('TEMPLATE/DISK') do |disk| # Wait for any pending operation rc = wait_state2('POWEROFF', 'LCM_INIT') raise rc.message if OpenNebula.is_error?(rc) disk_id = disk['DISK_ID'] image_id = disk['IMAGE_ID'] omng = disk['OPENNEBULA_MANAGED'] type = disk['TYPE'] raise 'Missing DISK_ID' unless valid?(disk_id) REMOVE_IMAGE_ATTRS.each do |attr| disk.delete_element(attr) end # Volatile disks cannot be saved, copy definition if !valid?(image_id) logger.info 'Adding volatile disk' if logger disk_str = disk.template_like_str('.').tr("\n", ",\n") replace << "DISK = [ #{disk_str} ]\n" next end # SIZE, TYPE is important for volatile disk, remove them now disk.delete_element('SIZE') disk.delete_element('TYPE') # CDROM disk, copy definition if type == 'CDROM' logger.info 'Adding CDROM disk' if logger replace << "DISK = [ IMAGE_ID = #{image_id}" replace << ", OPENNEBULA_MANAGED=#{omng}" if omng replace << " ]\n" next end # Regular disk, saveas it logger.info 'Adding and saving regular disk' if logger ndisk_name = "#{name}-disk-#{disk_id}" rc = disk_saveas(disk_id.to_i, ndisk_name, '', -1) raise rc.message if OpenNebula.is_error?(rc) if opts[:persistent] logger.info 'Making disk persistent' if logger nimg = OpenNebula::Image.new_with_id(rc.to_i, @client) nimg.persistent end img_ids << rc.to_i disk_tmpl = disk.template_like_str('.').tr("\n", ",\n") replace << "DISK = [ IMAGE_ID = #{rc} " replace << ", #{disk_tmpl}" unless disk_tmpl.empty? replace << " ]\n" end # -------------------------------------------------------------- # Process VM NICs # -------------------------------------------------------------- logger.info 'Processing VM NICs' if logger each('TEMPLATE/NIC') do |nic| nic_id = nic['NIC_ID'] raise 'Missing NIC_ID' unless valid?(nic_id) REMOVE_VNET_ATTRS.each do |attr| nic.delete_element(attr) end replace << 'NIC = [ ' replace << nic.template_like_str('.').tr("\n", ",\n") replace << " ] \n" end # -------------------------------------------------------------- # Extra. Required by the Sunstone Cloud View # -------------------------------------------------------------- replace << "SAVED_TEMPLATE_ID = #{tid}\n" new_tmpl = OpenNebula::Template.new_with_id(ntid, @client) logger.info 'Updating VM Template' if logger rc = new_tmpl.update(replace, true) raise rc.message if OpenNebula.is_error?(rc) # -------------------------------------------------------------- # Resume VM if needed # -------------------------------------------------------------- if poweron logger.info 'Powering on VM' if logger rc = wait_state2('POWEROFF', 'LCM_INIT') raise rc.message if OpenNebula.is_error?(rc) resume rc = wait_state2('ACTIVE', 'RUNNING') raise rc.message if OpenNebula.is_error?(rc) end ntid rescue StandardError # -------------------------------------------------------------- # Rollback. Delete the template and the images created # -------------------------------------------------------------- if ntid && !OpenNebula.is_error?(ntid) ntmpl = OpenNebula::Template.new_with_id(ntid, @client) ntmpl.delete end img_ids.each do |id| img = OpenNebula::Image.new_with_id(id, @client) img.delete end raise end #################################################################### # Private extended interface #################################################################### private #------------------------------------------------------------------- # Check an attribute is defined and valid #------------------------------------------------------------------- def valid?(att) return false if att.nil? return !att.nil? && !att.empty? if att.is_a? String !att.nil? end end end