ManageIQ/manageiq-providers-azure

View on GitHub
app/models/manageiq/providers/azure/cloud_manager/provision/cloning.rb

Summary

Maintainability
A
0 mins
Test Coverage
C
71%
module ManageIQ::Providers::Azure::CloudManager::Provision::Cloning
  def do_clone_task_check(_clone_task_ref)
    source.with_provider_connection do |azure|
      vms      = ::Azure::Armrest::VirtualMachineService.new(azure)
      instance = vms.get(dest_name, resource_group.name)
      status   = instance.properties.provisioning_state
      return true if status == "Succeeded"
      return false, status
    end
  end

  def gather_storage_account_properties
    sas = nil

    source.with_provider_connection do |azure|
      sas = ::Azure::Armrest::StorageAccountService.new(azure)
    end

    return if sas.nil?

    begin
      image = sas.list_private_images(storage_account_resource_group).find do |img|
        img.uri == source.ems_ref
      end

      return unless image

      platform   = image.operating_system
      endpoint   = image.storage_account.properties.primary_endpoints.blob
      source_uri = image.uri

      target_uri = File.join(endpoint, "manageiq", dest_name + "_" + SecureRandom.uuid + ".vhd")
    rescue ::Azure::Armrest::ResourceNotFoundException => err
      _log.error("Error Class=#{err.class.name}, Message=#{err.message}")
    end

    return target_uri, source_uri, platform
  end

  def custom_data
    userdata_payload.encode('UTF-8').delete("\n")
  end

  def prepare_for_clone_task
    # TODO: Ideally this would be a check against source.storage or source.disks
    if source.ems_ref =~ /.+:.+:.+:.+/
      urn_keys = %w(publisher offer sku version)
      image_reference = Hash[urn_keys.zip(source.ems_ref.split(':'))]
      os, target_uri, source_uri = nil
    elsif source.ems_ref.starts_with?('/subscriptions')
      os = source.operating_system.product_name
      target_uri, source_uri = nil
      image_reference = { :id => source.ems_ref }
    else
      image_reference = nil
      target_uri, source_uri, os = gather_storage_account_properties
    end

    cloud_options =
      {
        :name       => dest_name,
        :location   => source.location,
        :properties => {
          :hardwareProfile => {
            :vmSize => instance_type.name
          },
          :osProfile       => {
            :adminUserName => options[:root_username],
            :adminPassword => root_password,
            :computerName  => dest_hostname
          },
          :storageProfile  => {
            :osDisk => {
              :createOption => 'FromImage',
              :caching      => 'ReadWrite',
              :osType       => os
            }
          }
        }
      }

    # The -1 value is set in ProvisionWorkflow to distinguish between the
    # desire for a new Public IP address vs a private IP.
    #
    if floating_ip
      nic_id = associated_nic || create_nic(true)
    else
      public_ip = options[:floating_ip_address].first == -1
      nic_id = create_nic(public_ip)
    end

    cloud_options[:properties][:networkProfile] = {:networkInterfaces => [{:id => nic_id}]}

    if target_uri
      cloud_options[:properties][:storageProfile][:osDisk][:name]  = dest_name + SecureRandom.uuid + '.vhd'
      cloud_options[:properties][:storageProfile][:osDisk][:image] = {:uri => source_uri}
      cloud_options[:properties][:storageProfile][:osDisk][:vhd]   = {:uri => target_uri}
    else
      # Default to a storage account type of "Standard_LRS" for managed images for now.
      cloud_options[:properties][:storageProfile][:osDisk][:managedDisk] = {:storageAccountType => 'Standard_LRS'}
      cloud_options[:properties][:storageProfile][:imageReference] = image_reference
    end

    cloud_options[:properties][:osProfile][:customData] = custom_data unless userdata_payload.nil?

    cloud_options
  end

  def log_clone_options(clone_options)
    dump_obj(clone_options, "#{_log.prefix} Clone Options: ", $log, :info)
    dump_obj(options, "#{_log.prefix} Prov Options:  ", $log, :info, :protected => {:path => workflow_class.encrypted_options_field_regs})
  end

  def region
    source.location
  end

  def storage_account_resource_group
    source.description.split("/").first
  end

  def storage_account_name
    source.description.split("/")[1]
  end

  def associated_nic
    floating_ip.try(:network_port).try(:ems_ref)
  end

  def create_nic(with_public_ip = true)
    source.with_provider_connection do |azure|
      nis = ::Azure::Armrest::Network::NetworkInterfaceService.new(azure)

      if with_public_ip
        ips = ::Azure::Armrest::Network::IpAddressService.new(azure)

        # Use the existing Public IP if possible. Otherwise create a new one.
        if floating_ip.try(:ems_ref)
          begin
            ip = ips.get_by_id(floating_ip.ems_ref)
          rescue ::Azure::Armrest::NotFoundException
            ip = ips.create("#{dest_name}-publicIp", resource_group.name, :location => region)
          end
        else
          ip = ips.create("#{dest_name}-publicIp", resource_group.name, :location => region)
        end

        network_options = build_nic_options(ip.id)
      else
        network_options = build_nic_options
      end

      return nis.create(dest_name, resource_group.name, network_options).id
    end
  end

  def build_nic_options(ip_id = nil)
    ip_config = {
      :name       => dest_name,
      :properties => {
        :subnet => {:id => cloud_subnet.ems_ref}
      }
    }

    ip_config[:properties][:publicIPAddress] = {:id => ip_id} if ip_id

    network_options = {
      :location   => region,
      :properties => {
        :ipConfigurations => [ip_config]
      }
    }

    network_options[:properties][:networkSecurityGroup] = {:id => security_group.ems_ref} if security_group

    network_options
  end

  def start_clone_task
    super
  rescue Azure::Armrest::BadRequestException => err
    phase_context[:exception_class]     = err.class.name
    phase_context[:exception_message]   = err.message
    phase_context[:exception_backtrace] = err.backtrace
    phase_context[:error_phase]         = phase
    signal :clone_failure_cleanup
  end

  def start_clone(clone_options)
    source.with_provider_connection do |azure|
      vms = ::Azure::Armrest::VirtualMachineService.new(azure)
      vm  = vms.create(dest_name, resource_group.name, clone_options)

      File.join(azure.subscription_id, vm.resource_group.downcase, vm.type.downcase, vm.name)
    end
  end
end