# frozen_string_literal: true require 'bigdecimal' require 'bigdecimal/util' require 'rbvmomi' require 'vmpooler/providers/base' module Vmpooler class PoolManager class Provider class VSphere < Vmpooler::PoolManager::Provider::Base # The connection_pool method is normally used only for testing attr_reader :connection_pool def initialize(config, logger, metrics, redis_connection_pool, name, options) super(config, logger, metrics, redis_connection_pool, name, options) task_limit = global_config[:config].nil? || global_config[:config]['task_limit'].nil? ? 10 : global_config[:config]['task_limit'].to_i # The default connection pool size is: # Whatever is biggest from: # - How many pools this provider services # - Maximum number of cloning tasks allowed # - Need at least 2 connections so that a pool can have inventory functions performed while cloning etc. default_connpool_size = [provided_pools.count, task_limit, 2].max connpool_size = provider_config['connection_pool_size'].nil? ? default_connpool_size : provider_config['connection_pool_size'].to_i # The default connection pool timeout should be quite large - 60 seconds connpool_timeout = provider_config['connection_pool_timeout'].nil? ? 60 : provider_config['connection_pool_timeout'].to_i logger.log('d', "[#{name}] ConnPool - Creating a connection pool of size #{connpool_size} with timeout #{connpool_timeout}") @connection_pool = Vmpooler::PoolManager::GenericConnectionPool.new( metrics: metrics, connpool_type: 'provider_connection_pool', connpool_provider: name, size: connpool_size, timeout: connpool_timeout ) do logger.log('d', "[#{name}] Connection Pool - Creating a connection object") # Need to wrap the vSphere connection object in another object. The generic connection pooler will preserve # the object reference for the connection, which means it cannot "reconnect" by creating an entirely new connection # object. Instead by wrapping it in a Hash, the Hash object reference itself never changes but the content of the # Hash can change, and is preserved across invocations. new_conn = connect_to_vsphere { connection: new_conn } end @provider_hosts = {} @provider_hosts_lock = Mutex.new @redis = redis_connection_pool end # name of the provider class def name 'vsphere' end def folder_configured?(folder_title, base_folder, configured_folders, allowlist) return true if allowlist&.include?(folder_title) return false unless configured_folders.keys.include?(folder_title) return false unless configured_folders[folder_title] == base_folder true end def destroy_vm_and_log(vm_name, vm_object, pool, data_ttl) try = 0 if try.nil? max_tries = 3 @redis.with_metrics do |redis| redis.multi redis.srem("vmpooler__completed__#{pool}", vm_name) redis.hdel("vmpooler__active__#{pool}", vm_name) redis.hset("vmpooler__vm__#{vm_name}", 'destroy', Time.now) # Auto-expire metadata key redis.expire("vmpooler__vm__#{vm_name}", (data_ttl * 60 * 60)) redis.exec end start = Time.now if vm_object.is_a? RbVmomi::VIM::Folder logger.log('s', "[!] [#{pool}] '#{vm_name}' is a folder, bailing on destroying") raise('Expected VM, but received a folder object') end vm_object.PowerOffVM_Task.wait_for_completion if vm_object.runtime&.powerState && vm_object.runtime.powerState == 'poweredOn' vm_object.Destroy_Task.wait_for_completion finish = format('%