class VCenterDriver::Datastore

Class Datastore

Attributes

one_item[RW]

Public Class Methods

detach_and_destroy(disk, vm, disk_id, prev_ds_ref, vi_client) click to toggle source

detach disk from vCenter vm if possible, destroy the disk on FS

# File lib/datastore.rb, line 831
def self.detach_and_destroy(disk, vm, disk_id, prev_ds_ref, vi_client)
    # it's not a CDROM (CLONE=NO)
    is_cd = !(disk['CLONE'].nil? || disk['CLONE'] == 'YES')

    begin
        # Detach disk if possible (VM is reconfigured)
        # and gather vCenter info
        # Needed for poweroff machines too
        ds_ref, img_path = vm.detach_disk(disk)

        return if is_cd

        # Disk could't be detached, use OpenNebula info
        if !(ds_ref && img_path && !img_path.empty?)
            img_path = vm.disk_real_path(disk, disk_id)
            ds_ref = prev_ds_ref
        end

        # If disk was already detached we have no way to remove it
        ds = VCenterDriver::Datastore.new_from_ref(ds_ref, vi_client)

        search_params = ds.get_search_params(ds['name'],
                                             File.dirname(img_path),
                                             File.basename(img_path))

        # Perform search task and return results
        search_task = ds['browser']
                      .SearchDatastoreSubFolders_Task(search_params)
        search_task.wait_for_completion

        ds.delete_virtual_disk(img_path)
        img_dir = File.dirname(img_path)
        ds.rm_directory(img_dir) if ds.dir_empty?(img_dir)
    rescue StandardError => e
        if !e.message.start_with?('FileNotFound')
            raise e.message # Ignore FileNotFound
        end
    end
end
new(item, vi_client = nil) click to toggle source
Calls superclass method
# File lib/datastore.rb, line 370
def initialize(item, vi_client = nil)
    check_item(item, RbVmomi::VIM::Datastore)
    @vi_client = vi_client
    @item = item
    @one_item = {}

    super()
end
new_from_ref(ref, vi_client) click to toggle source

This is never cached

# File lib/datastore.rb, line 826
def self.new_from_ref(ref, vi_client)
    new(RbVmomi::VIM::Datastore.new(vi_client.vim, ref), vi_client)
end

Public Instance Methods

all_images() click to toggle source
# File lib/datastore.rb, line 685
def all_images
    images = {}
    imid = -1
    ds_id = nil
    ds_name = self['name']

    # We need OpenNebula Images and Datastores pools
    ipool = VCenterDriver::VIHelper
            .one_pool(OpenNebula::ImagePool, false)
    if ipool.respond_to?(:message)
        raise "Could not get OpenNebula ImagePool: #{pool.message}"
    end

    dpool = VCenterDriver::VIHelper
            .one_pool(OpenNebula::DatastorePool, false)
    if dpool.respond_to?(:message)
        raise "Could not get OpenNebula DatastorePool: #{pool.message}"
    end

    ds_id = @one_item['ID']

    begin
        # Create Search Spec
        search_params = get_search_params(ds_name)

        # Perform search task and return results
        search_task = self['browser']
                      .SearchDatastoreSubFolders_Task(search_params)
        search_task.wait_for_completion

        # Loop through search results
        search_task.info.result.each do |result|
            # Remove [datastore] from file path
            folderpath = ''
            size = result.folderPath.size
            if result.folderPath[-1] != ']'
                if result.folderPath[-1] != '/'
                    result.folderPath[size] = '/'
                end
                folderpath = result
                             .folderPath.sub(/^\[#{ds_name}\] /, '')
            end

            # Loop through images in result.file
            result.file.each do |image|
                image_path = ''

                # Skip not relevant files
                next unless %w[FloppyImageFileInfo
                               IsoImageFileInfo
                               VmDiskFileInfo].include? image.class.to_s

                # Get image path and name
                image_path << folderpath << image.path
                image_name = File.basename(image.path)
                                 .reverse.sub('kdmv.', '').reverse

                # Get image's type
                if image.class.to_s == 'VmDiskFileInfo'
                    image_type = 'OS'
                else
                    image_type = 'CDROM'
                end

                # Get image's size
                image_size = image.capacityKb / 1024 rescue nil
                image_size ||= image.fileSize / 1024 / 1024 rescue nil

                # Assign image prefix if known or assign default prefix
                controller = image.controllerType rescue nil
                if controller
                    if controller == 'VirtualIDEController'
                        disk_prefix = 'hd'
                    else
                        disk_prefix = 'sd'
                    end
                else
                    # Get default value for disks that
                    # are not attached to any controller
                    disk_prefix = VCenterDriver::VIHelper
                                  .get_default(
                                      'IMAGE/TEMPLATE/DEV_PREFIX'
                                  )
                end

                # Generate a crypto hash
                # this hash is used to avoid name collisions
                key = "#{image_name}#{ds_name}#{image_path}"
                import_name = VCenterDriver::VIHelper
                              .one_name(
                                  OpenNebula::ImagePool,
                                  image_name,
                                  key,
                                  ipool
                              )

                # Set template
                one_image =  "NAME=\"#{import_name}\"\n"
                one_image << "PATH=\"vcenter://#{image_path}\"\n"
                one_image << "PERSISTENT=\"NO\"\n"
                one_image << "TYPE=\"#{image_type}\"\n"
                unless CONFIG[:delete_images]
                    one_image << "VCENTER_IMPORTED=\"YES\"\n"
                end
                one_image << "DEV_PREFIX=\"#{disk_prefix}\"\n"

                # Check image hasn't already been imported
                image_found = VCenterDriver::VIHelper
                              .find_image_by(
                                  'SOURCE',
                                  OpenNebula::ImagePool,
                                  image_path,
                                  ds_id,
                                  ipool
                              )

                next if image_found

                # Add template to image array
                images[import_name] = {
                    :import_id   => imid+=1,
                    :name        => import_name,
                    :ref         => import_name,
                    :path        => image_path,
                    :size        => image_size.to_s,
                    :type        => image.class.to_s,
                    :dsid        => ds_id,
                    :one         => one_image
                }
            end
        end
    rescue StandardError => e
        raise "Could not find images. \
              Reason: #{e.message}/#{e.backtrace}"
    end
    vname = @vi_client.vc_name || ''

    { vname => images }
end
copy_virtual_disk(src_path, target_ds, target_path, new_size = nil) click to toggle source

Copy a VirtualDisk

# File lib/datastore.rb, line 414
def copy_virtual_disk(src_path, target_ds, target_path, new_size = nil)
    source_ds_name = self['name']
    target_ds_name = target_ds['name']

    leading_dirs = target_path.split('/')[0..-2]
    if !leading_dirs.empty?
        if source_ds_name == target_ds_name
            create_directory(leading_dirs.join('/'))
        else
            target_ds.create_directory(leading_dirs.join('/'))
        end
    end

    copy_params = {
        :sourceName        => "[#{source_ds_name}] #{src_path}",
        :sourceDatacenter  => obtain_dc.item
    }

    if File.extname(src_path) == '.vmdk'
        copy_params[:destName] = "[#{target_ds_name}] #{target_path}"
        obtain_vdm.CopyVirtualDisk_Task(copy_params).wait_for_completion

        if new_size
            resize_spec = {
                :name => "[#{target_ds_name}] #{target_path}",
                :datacenter => target_ds.obtain_dc.item,
                :newCapacityKb => new_size,
                :eagerZero => false
            }

            obtain_vdm.ExtendVirtualDisk_Task(
                resize_spec
            ).wait_for_completion
        end
    else
        copy_params[:destinationName] =
            "[#{target_ds_name}] #{target_path}"
        obtain_fm.CopyDatastoreFile_Task(
            copy_params
        ).wait_for_completion
    end

    target_path
end
dc_path() click to toggle source
# File lib/datastore.rb, line 603
def dc_path
    dc = obtain_dc
    p = dc.item.parent
    path = [dc.item.name]
    while p.instance_of? RbVmomi::VIM::Folder
        path.unshift(p.name)
        p = p.parent
    end
    path.delete_at(0) # The first folder is the root "Datacenters"
    path.join('/')
end
delete_file(img_name) click to toggle source
# File lib/datastore.rb, line 396
def delete_file(img_name)
    ds_name = self['name']

    begin
        obtain_fm.DeleteDatastoreFile_Task(
            :name => "[#{ds_name}] #{img_name}",
            :datacenter => obtain_dc.item
        ).wait_for_completion
    rescue StandardError => e
        # Ignore if file not found
        if !e.message.start_with?('ManagedObjectNotFound') &&
           !e.message.start_with?('FileNotFound')
            raise e
        end
    end
end
delete_virtual_disk(img_name) click to toggle source
# File lib/datastore.rb, line 379
def delete_virtual_disk(img_name)
    ds_name = self['name']

    begin
        obtain_vdm.DeleteVirtualDisk_Task(
            :name => "[#{ds_name}] #{img_name}",
            :datacenter => obtain_dc.item
        ).wait_for_completion
    rescue StandardError => e
        # Ignore if file not found
        if !e.message.start_with?('ManagedObjectNotFound') &&
           !e.message.start_with?('FileNotFound')
            raise e
        end
    end
end
descriptor?(remote_path) click to toggle source
# File lib/datastore.rb, line 644
def descriptor?(remote_path)
    url = generate_file_url(remote_path)

    rout, wout = IO.pipe
    pid = spawn(CURLBIN,
                '-I', '-k', '--noproxy', '*', '-f',
                '-b', self['_connection.cookie'],
                url,
                :out => wout,
                :err => '/dev/null')

    Process.waitpid(pid, 0)
    raise 'read image header failed' unless $?.success? # rubocop:disable Style/SpecialGlobalVars

    wout.close
    size = rout.readlines.select do |l|
        l.downcase.start_with?('content-length')
    end[0].downcase.sub('content-length: ', '')
    rout.close
    size.chomp.to_i < 4096 # If <4k, then is a descriptor
end
dir_empty?(path) click to toggle source
# File lib/datastore.rb, line 503
def dir_empty?(path)
    ds_name = self['name']

    spec = RbVmomi::VIM::HostDatastoreBrowserSearchSpec.new

    search_params = {
        'datastorePath' => "[#{ds_name}] #{path}",
        'searchSpec'    => spec
    }

    begin
        search_task = self['browser']
                      .SearchDatastoreSubFolders_Task(search_params)
        search_task.wait_for_completion
        !search_task.info.result.nil? &&
            search_task.info.result.length == 1 &&
            search_task.info.result.first.file.empty?
    rescue StandardError
        false
    end
end
download_file(source_path, target_path) click to toggle source
# File lib/datastore.rb, line 529
def download_file(source_path, target_path)
    @item.download(source_path, target_path)
end
download_to_stdout(remote_path) click to toggle source
# File lib/datastore.rb, line 633
def download_to_stdout(remote_path)
    url = generate_file_url(remote_path)
    pid = spawn(CURLBIN,
                '-k', '--noproxy', '*', '-f',
                '-b', self['_connection.cookie'],
                url)

    Process.waitpid(pid, 0)
    raise 'download failed' unless $?.success? # rubocop:disable Style/SpecialGlobalVars
end
generate_file_url(path) click to toggle source
# File lib/datastore.rb, line 615
def generate_file_url(path)
    if self['_connection.http.use_ssl?']
        protocol = 'https://'
    else
        protocol = 'http://'
    end
    hostname = self['_connection.http.address']
    port     = self['_connection.http.port']
    dcpath   = dc_path
    url_path = "folder/#{path}?dcPath=#{dcpath}&dsName=#{self['name']}"

    # This creates the vcenter file URL
    # for uploading or downloading files
    # e.g:
    url = "#{protocol}#{hostname}:#{port}/#{url_path}"
    URI.escape(url) # rubocop:disable Lint/UriEscapeUnescape
end
get_search_params(ds_name, img_path = nil, img_name = nil) click to toggle source
# File lib/datastore.rb, line 570
def get_search_params(ds_name, img_path = nil, img_name = nil)
    spec         = RbVmomi::VIM::HostDatastoreBrowserSearchSpec.new

    vmdisk_query = RbVmomi::VIM::VmDiskFileQuery.new
    vmdisk_query.details = RbVmomi::VIM::VmDiskFileQueryFlags(
        :diskType        => true,
        :capacityKb      => true,
        :hardwareVersion => true,
        :controllerType  => true
    )

    spec.query   = [vmdisk_query,
                    RbVmomi::VIM::IsoImageFileQuery.new]
    spec.details = RbVmomi::VIM::FileQueryFlags(
        :fileOwner    => true,
        :fileSize     => true,
        :fileType     => true,
        :modification => true
    )

    if img_name.nil?
        spec.matchPattern = []
    else
        spec.matchPattern = [img_name]
    end

    datastore_path = "[#{ds_name}]"
    datastore_path << " #{img_path}" unless img_path.nil?

    { 'datastorePath' => datastore_path,
                     'searchSpec'    => spec }
end
get_text_file(remote_path) click to toggle source
# File lib/datastore.rb, line 666
def get_text_file(remote_path)
    url = generate_file_url(remote_path)

    rout, wout = IO.pipe
    pid = spawn CURLBIN, '-k', '--noproxy', '*', '-f',
                '-b', self['_connection.cookie'],
                url,
                :out => wout,
                :err => '/dev/null'

    Process.waitpid(pid, 0)
    raise 'get text file failed' unless $?.success? # rubocop:disable Style/SpecialGlobalVars

    wout.close
    output = rout.readlines
    rout.close
    output
end
move_virtual_disk(disk, dest_path, dest_dsid, vi_client = nil) click to toggle source
# File lib/datastore.rb, line 459
def move_virtual_disk(disk, dest_path, dest_dsid, vi_client = nil)
    vi_client ||= @vi_client

    target_ds     = VCenterDriver::VIHelper.one_item(
        OpenNebula::Datastore,
        dest_dsid,
        false
    )
    target_ds_ref = target_ds['TEMPLATE/VCENTER_DS_REF']
    target_ds_vc  = VCenterDriver::Datastore
                    .new_from_ref(
                        target_ds_ref,
                        vi_client
                    )
    dest_name = target_ds_vc['name']

    target_ds_vc.create_directory(File.dirname(dest_path))

    dpath_ds  = "[#{dest_name}] #{dest_path}"
    orig_path = "[#{self['name']}] #{disk.path}"

    move_params = {
        :sourceName => orig_path,
        :sourceDatacenter => obtain_dc.item,
        :destName => dpath_ds,
        :force => true
    }

    obtain_vdm.MoveVirtualDisk_Task(move_params).wait_for_completion
end
rm_directory(directory) click to toggle source
# File lib/datastore.rb, line 490
def rm_directory(directory)
    ds_name = self['name']

    rm_directory_params = {
        :name                     => "[#{ds_name}] #{directory}",
        :datacenter               => obtain_dc.item
    }

    obtain_fm.DeleteDatastoreFile_Task(
        rm_directory_params
    ).wait_for_completion
end
stat(img_str) click to toggle source

Get file size for image handling

# File lib/datastore.rb, line 534
def stat(img_str)
    ds_name = self['name']
    img_path = File.dirname img_str
    img_name = File.basename img_str

    # Create Search Spec
    search_params = get_search_params(ds_name, img_path, img_name)

    # Perform search task and return results
    begin
        search_task = self['browser']
                      .SearchDatastoreSubFolders_Task(search_params)

        search_task.wait_for_completion

        # Try to get vmdk capacity as seen by VM
        size = search_task
               .info.result[0].file[0].capacityKb / 1024 rescue nil

        # Try to get file size
        size ||= search_task
                 .info
                 .result[0].file[0].fileSize / 1024 / 1024 rescue nil

        raise 'Could not get file size or capacity' if size.nil?

        size
    rescue StandardError => e
        message = "Could not find file. Reason: \"#{e.message}\"."
        if VCenterDriver::CONFIG[:debug_information]
            message += ' ' + e.backtrace.to_s
        end
        raise message
    end
end
upload_file(source_path, target_path) click to toggle source
# File lib/datastore.rb, line 525
def upload_file(source_path, target_path)
    @item.upload(target_path, source_path)
end