ManageIQ/manageiq-providers-azure

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

Summary

Maintainability
A
0 mins
Test Coverage
F
44%
class ManageIQ::Providers::Azure::CloudManager < ManageIQ::Providers::CloudManager
  include ManageIQ::Providers::Azure::ManagerMixin

  alias_attribute :azure_tenant_id, :uid_ems

  has_many :resource_groups, :foreign_key => :ems_id, :dependent => :destroy

  supports     :catalog
  supports     :create
  supports_not :discovery
  supports     :label_mapping
  supports     :provisioning
  supports     :regions

  supports(:events) { _('Events are not supported for this region') unless insights? }
  supports(:metrics) { _('Capacity & Utilization not supported for this region') unless insights? }
  supports(:timeline) { _('Timeline not supported for this region') unless insights? }

  before_create :ensure_managers
  before_update :ensure_managers_zone_and_provider_region

  # If the Microsoft.Insights Azure provider is not registered, then neither
  # events nor metrics are supported for that EMS.
  #
  def insights?
    !!capabilities["insights"]
  end

  def ensure_network_manager
    build_network_manager(:type => 'ManageIQ::Providers::Azure::NetworkManager') unless network_manager
  end

  def self.ems_type
    @ems_type ||= "azure".freeze
  end

  def self.description
    @description ||= "Azure".freeze
  end

  def self.default_blacklisted_event_names
    Settings.ems.ems_azure.blacklisted_event_names
  end

  def self.api_allowed_attributes
    %w(azure_tenant_id).freeze
  end

  def blacklisted_provider_types
    %r{Microsoft.Classic}
  end

  def self.hostname_required?
    false
  end

  def description
    ManageIQ::Providers::Azure::Regions.regions.dig(provider_region, :description)
  end

  def self.catalog_types
    {"azure" => N_("Azure")}
  end

  def allow_targeted_refresh?
    true
  end

  # Operations

  def vm_create_evm_snapshot(vm, options = {})
    @connection = connect(options)
    vm.provider_service(@connection)
    if vm.managed_disk?
      vm_create_evm_managed_snapshot(vm)
    else
      vm_create_evm_blob_snapshot(vm)
    end
  end

  def vm_create_evm_managed_snapshot(vm)
    snap_svc     = snapshot_service(@connection)
    snap_options = { :location   => vm.location,
                     :properties => {
                       :creationData => {
                         :createOption     => "Copy",
                         :sourceResourceId => vm.os_disk.managed_disk.id
                       }
                     } }
    _log.debug("vm=[#{vm.name}] creating SSA snapshot #{vm.ssa_snap_name}")
    begin
      ssa_snap_name  = vm.ssa_snap_name
      resource_group = vm.resource_group.name
      snap_svc.get(ssa_snap_name, resource_group) # Check if snapshot already exists
    rescue ::Azure::Armrest::NotFoundException, ::Azure::Armrest::ResourceNotFoundException => err
      begin
        # The snapshot doesn't exist, create it.
        response = snap_svc.create(ssa_snap_name, resource_group, snap_options)
        # wait a minute at a time, allowing the Job Timeout to handle long-running snapshots here
        loop do
          snap_state = snap_svc.wait(response.response_headers)
          _log.debug("Snapshot creation state = #{snap_state}")
          return ssa_snap_name if snap_state =~ /succe/i
        end
      rescue StandardError => err
        _log.error("vm=[#{vm.name}], error: #{err}")
        _log.debug { err.backtrace.join("\n") }
        raise "Error #{err} creating SSA Snapshot #{ssa_snap_name}"
      end
    end
    _log.error("SSA Snapshot #{ssa_snap_name} already exists.")
    raise "Snapshot #{ssa_snap_name} already exists. Another SSA request for this VM is in progress or a previous one failed to clean up properly."
  end

  def vm_create_evm_blob_snapshot(vm)
    _log.debug("vm=[#{vm.name}] creating SSA snapshot for #{vm.blob_uri}")
    begin
      snapshot_info = vm.storage_acct.create_blob_snapshot(vm.container, vm.blob, vm.key)
      # wait a minute at a time, allowing the Job Timeout to handle long-running snapshots here
      loop do
        snap_state = vm.storage_acct_service.wait(snapshot_info)
        _log.debug("Snapshot creation state = #{snap_state}")
        return snapshot_info[:x_ms_snapshot] if snap_state =~ /succe/i
      end
    rescue StandardError => err
      _log.error("vm=[#{vm.name}], error:#{err}")
      _log.debug { err.backtrace.join("\n") }
      raise "Error #{err} creating SSA Snapshot for #{vm.name}"
    end
  end

  def vm_delete_evm_snapshot(vm, options = {})
    @connection = connect(options)
    if vm.managed_disk?
      vm_delete_managed_snapshot(vm, options)
    else
      vm_delete_blob_snapshot(vm, options)
    end
  end

  def vm_delete_managed_snapshot(vm, _options = {})
    snap_svc = snapshot_service(@connection)
    _log.debug("vm=[#{vm.name}] deleting SSA snapshot #{vm.ssa_snap_name}")
    snap_svc.delete(vm.ssa_snap_name, vm.resource_group.name)
  rescue StandardError => err
    _log.error("vm=[#{vm.name}], error: #{err} deleting SSA snapshot #{vm.ssa_snap_name}")
    _log.debug { err.backtrace.join("\n") }
  end

  def vm_delete_blob_snapshot(vm, options = {})
    unless options[:snMor]
      _log.error("Unable to clean up SSA snapshot: Missing Snapshot Date")
      return
    end
    _log.debug("vm=[#{vm.name}] deleting SSA snapshot for #{vm.blob_uri} with date #{options[:snMor]}")
    begin
      snap_opts = { :date => options[:snMor] }
      vm.storage_acct.delete_blob(vm.container, vm.blob, vm.key, snap_opts)
    rescue StandardError => err
      _log.error("vm=[#{vm.name}], error:#{err} deleting SSA snapshot with date #{options[:snMor]}")
      _log.debug { err.backtrace.join("\n") }
    end
  end

  def snapshot_service(connection = nil)
    _log.debug("Enter")
    connection ||= connect
    ::Azure::Armrest::Storage::SnapshotService.new(connection)
  end

  def self.display_name(number = 1)
    n_('Cloud Provider (Microsoft Azure)', 'Cloud Providers (Microsoft Azure)', number)
  end

  LABEL_MAPPING_ENTITIES = {
    "VmAzure" => "ManageIQ::Providers::Azure::CloudManager::Vm"
  }.freeze

  def self.entities_for_label_mapping
    LABEL_MAPPING_ENTITIES
  end

  def self.label_mapping_prefix
    ems_type
  end
end