diff --git a/src/sunstone/routes/vcenter.rb b/src/sunstone/routes/vcenter.rb index ce9bffcc98..ec1b71ea4c 100644 --- a/src/sunstone/routes/vcenter.rb +++ b/src/sunstone/routes/vcenter.rb @@ -99,7 +99,6 @@ get '/vcenter/templates' do error 404, error.to_json end - #ctemplates = templates.select{|t| t[:host] == params[:name]} [200, templates.to_json] rescue Exception => e logger.error("[vCenter] " + e.message) @@ -108,6 +107,50 @@ get '/vcenter/templates' do end end +get '/vcenter/template/:vcenter_ref' do + begin + t = {} + t[:one] = "" + + template = VCenterDriver::VirtualMachine.new_from_ref(params[:vcenter_ref], vcenter_client) + + vc_uuid = vcenter_client.vim.serviceContent.about.instanceUuid + dpool = VCenterDriver::VIHelper.one_pool(OpenNebula::DatastorePool) + ipool = VCenterDriver::VIHelper.one_pool(OpenNebula::ImagePool) + npool = VCenterDriver::VIHelper.one_pool(OpenNebula::VirtualNetworkPool) + + # Create images or get disks information for template + error, template_disks = template.import_vcenter_disks(vc_uuid, dpool, ipool) + + if !error.empty? + msg = error + logger.error("[vCenter] " + msg) + error = Error.new(msg) + error 404, error.to_json + end + + t[:one] << template_disks + + # Create images or get nics information for template + error, template_nics = template.import_vcenter_nics(vc_uuid, npool) + + if !error.empty? + msg = error + logger.error("[vCenter] " + msg) + error = Error.new(msg) + error 404, error.to_json + end + + t[:one] << template_nics + + [200, t.to_json] + rescue Exception => e + logger.error("[vCenter] " + e.message) + error = Error.new(e.message) + error 403, error.to_json + end +end + get '/vcenter/networks' do begin dc_folder = VCenterDriver::DatacenterFolder.new(vcenter_client) diff --git a/src/vmm_mad/remotes/lib/vcenter_driver/datacenter.rb b/src/vmm_mad/remotes/lib/vcenter_driver/datacenter.rb index 96e878e192..1f41bd8087 100644 --- a/src/vmm_mad/remotes/lib/vcenter_driver/datacenter.rb +++ b/src/vmm_mad/remotes/lib/vcenter_driver/datacenter.rb @@ -236,6 +236,21 @@ class DatacenterFolder template_ccr = template['runtime.host.parent'] + # Check if template has nics or disks to be imported later + has_nics_and_disks = false + + template["config.hardware.device"].each do |device| + if VCenterDriver::Storage.is_disk_or_iso?(device) + has_nics_and_disks = true + break + end + + if VCenterDriver::Network.is_nic?(device) + has_nics_and_disks = true + break + end + end + #Get resource pools rp_cache = {} if !rp_cache[template_ccr.name.to_s] @@ -256,6 +271,7 @@ class DatacenterFolder rp = rp_cache[template_ccr.name.to_s] object = template.to_one_template(template, + has_nics_and_disks, rp, rp_list, vcenter_uuid) diff --git a/src/vmm_mad/remotes/lib/vcenter_driver/datastore.rb b/src/vmm_mad/remotes/lib/vcenter_driver/datastore.rb index 8c4e944a59..379defeae0 100644 --- a/src/vmm_mad/remotes/lib/vcenter_driver/datastore.rb +++ b/src/vmm_mad/remotes/lib/vcenter_driver/datastore.rb @@ -112,6 +112,13 @@ class Storage return element end + # Checks if a RbVmomi::VIM::VirtualDevice is a disk or an iso file + def self.is_disk_or_iso?(device) + is_disk = !(device.class.ancestors.index(RbVmomi::VIM::VirtualDisk)).nil? + is_iso = device.backing.is_a? RbVmomi::VIM::VirtualCdromIsoBackingInfo + is_disk || is_iso + end + def monitor summary = self['summary'] diff --git a/src/vmm_mad/remotes/lib/vcenter_driver/importer.rb b/src/vmm_mad/remotes/lib/vcenter_driver/importer.rb index a7667d939e..491aefb92a 100644 --- a/src/vmm_mad/remotes/lib/vcenter_driver/importer.rb +++ b/src/vmm_mad/remotes/lib/vcenter_driver/importer.rb @@ -119,12 +119,12 @@ def self.import_templates(con_ops, options) template = t[:template] - error, template_disks_and_nics = template.import_vcenter_disks(vc_uuid, + error, template_disks = template.import_vcenter_disks(vc_uuid, dpool, ipool) if error.empty? - t[:one] << template_disks_and_nics + t[:one] << template_disks else STDOUT.puts error next diff --git a/src/vmm_mad/remotes/lib/vcenter_driver/network.rb b/src/vmm_mad/remotes/lib/vcenter_driver/network.rb index 37674be8e8..5baaeb20d2 100644 --- a/src/vmm_mad/remotes/lib/vcenter_driver/network.rb +++ b/src/vmm_mad/remotes/lib/vcenter_driver/network.rb @@ -61,6 +61,12 @@ class Network @item = item end + # Checks if a RbVmomi::VIM::VirtualDevice is a network interface + def self.is_nic?(device) + !device.class.ancestors.index(RbVmomi::VIM::VirtualEthernetCard).nil? + end + + def self.to_one_template(network_name, network_ref, network_type, ccr_ref, ccr_name, vcenter_uuid) diff --git a/src/vmm_mad/remotes/lib/vcenter_driver/virtual_machine.rb b/src/vmm_mad/remotes/lib/vcenter_driver/virtual_machine.rb index 818847a54a..a9aa3b797d 100644 --- a/src/vmm_mad/remotes/lib/vcenter_driver/virtual_machine.rb +++ b/src/vmm_mad/remotes/lib/vcenter_driver/virtual_machine.rb @@ -59,6 +59,22 @@ class VirtualMachine def initialize(item=nil, vi_client=nil) @item = item @vi_client = vi_client + @locking = true + end + + # Locking function. Similar to flock + def lock + if @locking + @locking_file = File.open("/tmp/vcenter-importer-lock","w") + @locking_file.flock(File::LOCK_EX) + end + end + + # Unlock driver execution mutex + def unlock + if @locking + @locking_file.close + end end ############################################################################ @@ -1351,51 +1367,25 @@ class VirtualMachine disk_info = "" error = "" - ccr_ref = self["runtime.host.parent._ref"] + begin + lock #Lock import operation, to avoid concurrent creation of images - #Get disks and info required - vc_disks = get_vcenter_disks + ccr_ref = self["runtime.host.parent._ref"] - # Track allocated images - allocated_images = [] + #Get disks and info required + vc_disks = get_vcenter_disks - vc_disks.each do |disk| + # Track allocated images + allocated_images = [] - datastore_found = VCenterDriver::Storage.get_one_image_ds_by_ref_and_ccr(disk[:datastore]._ref, - ccr_ref, - vc_uuid, - dpool) - if datastore_found.nil? - error = " Error datastore #{disk[:datastore].name}: has to be imported first as an image datastore!\n" + vc_disks.each do |disk| - #Rollback delete disk images - allocated_images.each do |i| - i.delete - end - - break - end - - image_import = VCenterDriver::Datastore.get_image_import_template(disk[:datastore].name, - disk[:path], - disk[:type], ipool) - #Image is already in the datastore - if image_import[:one] - # This is the disk info - disk_info << "DISK=[\n" - disk_info << "IMAGE_ID=\"#{image_import[:one]["ID"]}\",\n" - disk_info << "OPENNEBULA_MANAGED=\"NO\"\n" - disk_info << "]\n" - elsif !image_import[:template].empty? - # Then the image is created as it's not in the datastore - one_i = VCenterDriver::VIHelper.new_one_item(OpenNebula::Image) - - allocated_images << one_i - - rc = one_i.allocate(image_import[:template], datastore_found['ID'].to_i) - - if ::OpenNebula.is_error?(rc) - error = " Error creating disk from template: #{rc.message}. Cannot import the template\n" + datastore_found = VCenterDriver::Storage.get_one_image_ds_by_ref_and_ccr(disk[:datastore]._ref, + ccr_ref, + vc_uuid, + dpool) + if datastore_found.nil? + error = " Error datastore #{disk[:datastore].name}: has to be imported first as an image datastore!\n" #Rollback delete disk images allocated_images.each do |i| @@ -1405,14 +1395,49 @@ class VirtualMachine break end - #Add info for One template - one_i.info - disk_info << "DISK=[\n" - disk_info << "IMAGE_ID=\"#{one_i["ID"]}\",\n" - disk_info << "IMAGE_UNAME=\"#{one_i["UNAME"]}\",\n" - disk_info << "OPENNEBULA_MANAGED=\"NO\"\n" - disk_info << "]\n" + image_import = VCenterDriver::Datastore.get_image_import_template(disk[:datastore].name, + disk[:path], + disk[:type], ipool) + #Image is already in the datastore + if image_import[:one] + # This is the disk info + disk_info << "DISK=[\n" + disk_info << "IMAGE_ID=\"#{image_import[:one]["ID"]}\",\n" + disk_info << "OPENNEBULA_MANAGED=\"NO\"\n" + disk_info << "]\n" + elsif !image_import[:template].empty? + # Then the image is created as it's not in the datastore + one_i = VCenterDriver::VIHelper.new_one_item(OpenNebula::Image) + + allocated_images << one_i + + rc = one_i.allocate(image_import[:template], datastore_found['ID'].to_i) + + if ::OpenNebula.is_error?(rc) + error = " Error creating disk from template: #{rc.message}. Cannot import the template\n" + + #Rollback delete disk images + allocated_images.each do |i| + i.delete + end + + break + end + + #Add info for One template + one_i.info + disk_info << "DISK=[\n" + disk_info << "IMAGE_ID=\"#{one_i["ID"]}\",\n" + disk_info << "IMAGE_UNAME=\"#{one_i["UNAME"]}\",\n" + disk_info << "OPENNEBULA_MANAGED=\"NO\"\n" + disk_info << "]\n" + end end + + rescue Exception => e + error = "There was an error trying to create an image for disk in vcenter template. Reason: #{e.message}" + ensure + unlock end return error, disk_info @@ -1479,69 +1504,78 @@ class VirtualMachine nic_info = "" error = "" - ccr_ref = self["runtime.host.parent._ref"] - ccr_name = self["runtime.host.parent.name"] + begin + lock #Lock import operation, to avoid concurrent creation of images - #Get disks and info required - vc_nics = get_vcenter_nics + ccr_ref = self["runtime.host.parent._ref"] + ccr_name = self["runtime.host.parent.name"] - # Track allocated networks - allocated_networks = [] + #Get disks and info required + vc_nics = get_vcenter_nics - vc_nics.each do |nic| + # Track allocated networks + allocated_networks = [] - network_found = VCenterDriver::Network.get_one_vnet_ds_by_ref_and_ccr(nic[:net_ref], - ccr_ref, - vc_uuid, - npool) - #Network is already in the datastore - if network_found - # This is the existing nic info - nic_info << "NIC=[\n" - nic_info << "NETWORK_ID=\"#{network_found["ID"]}\",\n" - nic_info << "OPENNEBULA_MANAGED=\"NO\"\n" - nic_info << "]\n" - else - # Then the network has to be created as it's not in OpenNebula - one_vn = VCenterDriver::VIHelper.new_one_item(OpenNebula::VirtualNetwork) + vc_nics.each do |nic| - allocated_networks << one_vn + network_found = VCenterDriver::Network.get_one_vnet_ds_by_ref_and_ccr(nic[:net_ref], + ccr_ref, + vc_uuid, + npool) + #Network is already in the datastore + if network_found + # This is the existing nic info + nic_info << "NIC=[\n" + nic_info << "NETWORK_ID=\"#{network_found["ID"]}\",\n" + nic_info << "OPENNEBULA_MANAGED=\"NO\"\n" + nic_info << "]\n" + else + # Then the network has to be created as it's not in OpenNebula + one_vn = VCenterDriver::VIHelper.new_one_item(OpenNebula::VirtualNetwork) - one_vnet = VCenterDriver::Network.to_one_template(nic[:net_name], - nic[:net_ref], - nic[:pg_type], - ccr_ref, - ccr_name, - vc_uuid) + allocated_networks << one_vn - # By default add an ethernet range to network size 255 - ar_str = "" - ar_str << "AR=[\n" - ar_str << "TYPE=\"ETHER\",\n" - ar_str << "SIZE=\"255\"\n" - ar_str << "]\n" - one_vnet[:one] << ar_str + one_vnet = VCenterDriver::Network.to_one_template(nic[:net_name], + nic[:net_ref], + nic[:pg_type], + ccr_ref, + ccr_name, + vc_uuid) - rc = one_vn.allocate(one_vnet[:one]) + # By default add an ethernet range to network size 255 + ar_str = "" + ar_str << "AR=[\n" + ar_str << "TYPE=\"ETHER\",\n" + ar_str << "SIZE=\"255\"\n" + ar_str << "]\n" + one_vnet[:one] << ar_str - if ::OpenNebula.is_error?(rc) - error = " Error creating virtual network from template: #{rc.message}. Cannot import the template\n" + rc = one_vn.allocate(one_vnet[:one]) - #Rollback, delete virtual networks - allocated_networks.each do |n| - n.delete + if ::OpenNebula.is_error?(rc) + error = " Error creating virtual network from template: #{rc.message}. Cannot import the template\n" + + #Rollback, delete virtual networks + allocated_networks.each do |n| + n.delete + end + + break end - break + #Add info for One template + one_vn.info + nic_info << "NIC=[\n" + nic_info << "NETWORK_ID=\"#{one_vn["ID"]}\",\n" + nic_info << "OPENNEBULA_MANAGED=\"NO\"\n" + nic_info << "]\n" end - - #Add info for One template - one_vn.info - nic_info << "NIC=[\n" - nic_info << "NETWORK_ID=\"#{one_vn["ID"]}\",\n" - nic_info << "OPENNEBULA_MANAGED=\"NO\"\n" - nic_info << "]\n" end + + rescue Exception => e + error = "There was an error trying to create a virtual network for network in vcenter template. Reason: #{e.message}" + ensure + unlock end return error, nic_info @@ -1880,7 +1914,7 @@ class VirtualMachine return str end - def to_one_template(template, rp, rp_list, vcenter_uuid) + def to_one_template(template, has_nics_and_disks, rp, rp_list, vcenter_uuid) template_name = template['name'] template_ref = template['_ref'] @@ -1898,6 +1932,7 @@ class VirtualMachine one_tmp[:rp] = rp one_tmp[:rp_list] = rp_list one_tmp[:template] = template + one_tmp[:import_disks_and_nics] = has_nics_and_disks return one_tmp end