app/models/manageiq/providers/ovirt/infra_manager/ovirt_services.rb
require 'yaml' module ManageIQ::Providers::Ovirt::InfraManager::OvirtServices class Error < StandardError; end class VmNotReadyToBoot < Error; end class V4 include Vmdb::Logging attr_reader :ext_management_system def initialize(args) require 'ovirtsdk4' @ext_management_system = args[:ems] end def username_by_href(href) ext_management_system.with_provider_connection do |connection| user = connection.system_service.users_service.user_service(uuid_from_href(href)).get "#{user.name}@#{user.domain.name}" end end def cluster_name_href(href) ext_management_system.with_provider_connection do |connection| cluster_proxy_from_href(href, connection).name end end # Provisioning def get_host_proxy(host, connection) connection.system_service.hosts_service.host_service(host.uid_ems) end def clone_completed?(args) source = args[:source] phase_context = args[:phase_context] logger = args[:logger] if source.template? vm_clone_completed?(logger, phase_context, source) else template_clone_completed?(logger, phase_context, source) end end def vm_clone_completed?(logger, phase_context, source) source.with_provider_connection do |connection| vm = vm_service_by_href(phase_context[:new_vm_ems_ref], connection).get status = vm.status logger.info("The Vm being cloned is #{status}") status == OvirtSDK4::VmStatus::DOWN end end def template_clone_completed?(logger, phase_context, source) source.with_provider_connection do |connection| template = template_service_by_href(phase_context[:new_vm_ems_ref], connection).get status = template.status logger.info("The status of the template being cloned is #{status}") status == OvirtSDK4::TemplateStatus::OK end end def destination_image_locked?(vm) vm.with_provider_object do |vm_proxy| vm_proxy.get.status == OvirtSDK4::VmStatus::IMAGE_LOCKED end end def exists_on_provider?(vm) vm.with_provider_object do |vm_proxy| vm_proxy.get true end rescue OvirtSDK4::Error false end def populate_phase_context(phase_context, vm) phase_context[:new_vm_ems_ref] = ManageIQ::Providers::Ovirt::InfraManager.make_ems_ref(vm.href) end def nics_for_vm(vm) vm.with_provider_connection do |connection| vm_proxy = connection.system_service.vms_service.vm_service(vm.uid_ems).get connection.follow_link(vm_proxy.nics) end end def cluster_find_network_by_name(href, network_name) ext_management_system.with_provider_connection do |connection| cluster_service = connection.system_service.clusters_service.cluster_service(uuid_from_href(href)) networks = cluster_service.networks_service.list networks.detect { |n| n.name == network_name } end end def configure_vnics(requested_vnics, destination_vnics, destination_cluster, destination_vm) destination_vm.with_provider_connection do |connection| nics_service = connection.system_service.vms_service.vm_service(destination_vm.uid_ems).nics_service requested_vnics.stretch!(destination_vnics).each_with_index do |requested_vnic, idx| if requested_vnic.nil? nics_service.nic_service(destination_vnics[idx].id).remove else configure_vnic_with_requested_data("nic#{idx + 1}", requested_vnic, destination_vnics[idx], nics_service, destination_cluster) end end end end def load_allowed_networks(_hosts, vlans, workflow) private_load_allowed_networks(vlans, VmOrTemplate.find(workflow.get_source_vm.id).ems_cluster.uid_ems) end def filter_allowed_hosts(_workflow, all_hosts) all_hosts end def powered_off_in_provider?(vm) vm.with_provider_object { |vm_service| vm_service.get.status } == OvirtSDK4::VmStatus::DOWN end def powered_on_in_provider?(vm) vm.with_provider_object { |vm_service| vm_service.get.status } == OvirtSDK4::VmStatus::UP end def vm_boot_from_cdrom(operation, name) operation.destination.with_provider_object do |vm_service| vm_service.start( :vm => { :os => { :boot => { :devices => [OvirtSDK4::BootDevice::CDROM] } }, :cdroms => [ { :file => { :id => name } } ] } ) end rescue OvirtSDK4::Error raise ManageIQ::Providers::Ovirt::InfraManager::OvirtServices::VmNotReadyToBoot end def detach_floppy(operation) operation.destination.with_provider_object do |vm_service| vm_service.update(:payloads => []) end end def vm_boot_from_network(operation) operation.destination.with_provider_object do |vm_service| vm_service.start( :vm => { :os => { :boot => { :devices => [ OvirtSDK4::BootDevice::NETWORK ] } } } ) end rescue OvirtSDK4::Error raise ManageIQ::Providers::Ovirt::InfraManager::OvirtServices::VmNotReadyToBoot end def remote_console_acquire_ticket(vm, userid, originating_server) ticket = nil rhevm_vm = nil vm.with_provider_object do |vm_service| ticket = vm_service.ticket rhevm_vm = vm_service.get end SystemConsole.force_vm_invalid_token(vm.id) console_args = { :user => User.find_by(:userid => userid), :vm_id => vm.id, :protocol => rhevm_vm.display.type, :secret => ticket.value, :url_secret => SecureRandom.hex, :ssl => rhevm_vm.display.secure_port.present? } host_address = rhevm_vm.display.address host_port = rhevm_vm.display.secure_port || rhevm_vm.display.port SystemConsole.launch_proxy_if_not_local(console_args, originating_server, host_address, host_port) end def native_console_connection(vm) vm.with_provider_object do |vm_service| consoles = vm_service.graphics_consoles_service.list(:current => true) return nil if consoles.empty? console = select_graphics_console(consoles) Base64.encode64(vm_service.graphics_consoles_service.console_service(console.id).remote_viewer_connection_file) end end def get_template_proxy(template, connection) TemplateProxyDecorator.new( connection.system_service.templates_service.template_service(template.uid_ems), connection, self ) end def get_vm_proxy(vm, connection) VmProxyDecorator.new(connection.system_service.vms_service.vm_service(vm.uid_ems), connection, self) end def collect_disks_by_hrefs(disks) vm_disks = [] ext_management_system.with_provider_connection do |connection| disks.each do |disk| parts = URI(disk).path.split('/') begin vm_disks << connection.system_service.storage_domains_service.storage_domain_service(parts[2]).disks_service.disk_service(parts[4]).get rescue OvirtSDK4::Error nil end end end vm_disks end def shutdown_guest(operation) operation.with_provider_object(&:shutdown) rescue OvirtSDK4::Error => err _log.error("Error while doing shutdown_guest: #{err}") end def reboot_guest(operation) operation.with_provider_object(&:reboot) rescue OvirtSDK4::Error => err _log.error("Error while doing reboot guest: #{err}") end def start_clone(source, clone_options, phase_context) source.with_provider_object do |rhevm_template| vm = rhevm_template.create_vm(clone_options) populate_phase_context(phase_context, vm) end end def make_template(source, clone_options, phase_context) source.with_provider_object do |rhevm_vm| template = rhevm_vm.make_template(clone_options) populate_phase_context(phase_context, template) end end def vm_start(vm, opts = {}) vm.with_provider_object do |rhevm_vm| rhevm_vm.start(opts) end rescue OvirtSDK4::Error => err _log.error("Error starting vm: #{err}") end def vm_stop(vm) vm.with_provider_object(&:stop) rescue OvirtSDK4::Error => err _log.error("Error stopping vm: #{err}") end def vm_suspend(vm) vm.with_provider_object(&:suspend) end Method `vm_reconfigure` has a Cognitive Complexity of 21 (exceeds 11 allowed). Consider refactoring.
Cyclomatic complexity for vm_reconfigure is too high. [13/11] def vm_reconfigure(vm, options = {}) log_header = "EMS: [#{ext_management_system.name}] #{vm.class.name}: id [#{vm.id}], name [#{vm.name}], ems_ref [#{vm.ems_ref}]" spec = options[:spec] _log.info("#{log_header} Started...") vm.with_provider_object do |vm_service| # Retrieve the current representation of the virtual machine: # mandatory for memory parameters and to check if next_run_configuration_exists vm = vm_service.get(:all_content => true) new_vm_specs = {} # Update the memory: memory = spec['memoryMB'] new_vm_specs.merge!(new_vm_memory_specs(vm, memory.megabytes)) if memory # Update the CPU: cpu_total = spec['numCPUs'] cpu_cores = spec['numCoresPerSocket'] cpu_sockets = cpu_total / (cpu_cores || vm.cpu.topology.cores) if cpu_total new_vm_specs.merge!(new_vm_cpu_specs(cpu_cores, cpu_sockets)) if cpu_cores || cpu_sockets unless new_vm_specs.empty? vm_service.update( OvirtSDK4::Vm.new( new_vm_specs ), :next_run => vm.next_run_configuration_exists ) end # Remove disks: removed_disk_specs = spec['disksRemove'] remove_vm_disks(vm_service, removed_disk_specs) if removed_disk_specs # Add disks: added_disk_specs = spec['disksAdd'] if added_disk_specs added_disk_specs[:default] = {} added_disk_specs[:default][:interface] = 'virtio_scsi' if vm.virtio_scsi.enabled add_vm_disks(vm_service, added_disk_specs) end edit_disk_specs = spec['disksEdit'] if edit_disk_specs edit_vm_disks(vm_service, edit_disk_specs) end network_specs = spec['networkAdapters'] if network_specs network_specs.each do |action, nets| send("#{action}_vm_networks", nets, vm_service) end end end _log.info("#{log_header} Completed.") end def advertised_images ext_management_system.with_provider_connection do |ems_service| query = { :search => "status=#{OvirtSDK4::DataCenterStatus::UP}" } data_centers = ems_service.system_service.data_centers_service.list(:query => query) iso_sd = nil data_centers.each do |dc| iso_sd = ems_service.follow_link(dc.storage_domains).detect do |sd| sd.type == OvirtSDK4::StorageDomainType::ISO && sd.status == OvirtSDK4::StorageDomainStatus::ACTIVE end break iso_sd if iso_sd end return [] unless iso_sd sd_service = ems_service.system_service.storage_domains_service.storage_domain_service(iso_sd.id) iso_images = sd_service.files_service.list iso_images end rescue OvirtSDK4::Error => err name = ext_management_system.try(:name) _log.error("Error Getting ISO Images on ISO Datastore on Management System <#{name}>: #{err.class.name}: #{err}") raise ManageIQ::Providers::Ovirt::InfraManager::OvirtServices::Error, err end def host_activate(host) host.with_provider_object(&:activate) end def host_deactivate(host) host.with_provider_object(&:deactivate) end class VmProxyDecorator < SimpleDelegator attr_reader :ovirt_services, :connection def initialize(vm_service, connection, ovirt_services) @obj = vm_service @connection = connection @ovirt_services = ovirt_services super(vm_service) end def update_memory_reserve!(memory_reserve_size) vm = get vm.memory_policy.guaranteed = memory_reserve_size update(vm) end def update_description!(description) vm = get vm.description = description update(vm) end def update_memory!(memory, limit) vm = get vm.memory = memory unless memory.zero? vm.memory_policy.max = limit unless limit.zero? update(vm) end def update_host_affinity!(dest_host_ems_ref) vm = get vm.placement_policy.hosts = [OvirtSDK4::Host.new(:id => ovirt_services.uuid_from_href(dest_host_ems_ref))] update(vm) end def update_cpu_topology!(cpu_hash) vm = get vm.cpu.topology = OvirtSDK4::CpuTopology.new(cpu_hash) update(vm) end def attach_floppy(filename_hash) name, content = filename_hash.first file = OvirtSDK4::File.new(:name => name, :content => content) payload = OvirtSDK4::Payload.new(:files => [file], :type => "floppy") vm = get vm.payloads ||= [] vm.payloads << payload update(vm) end # # Updates the `initialization` of the virtual machine using the given custom script. # # @param content [String] YAML text containing the cloud-init configuration. # def update_cloud_init!(content) # Do nothing if the cloud-init configuration is empty or nil: return if content.blank? # When this was implemented using version 3 of the API some of the attributes of the # initialization object were directly copied from the YAML text, ignoring the syntax of the # cloud-init files. For example, we expected to have a YAML file like this: # # host_name: myvm.example.com # user_name: root # root_password: mypass # ... # # These are *not* part of the syntax supported by cloud-init, but just values that we used # to copy to the initialization object. To preserve backwards compatibility, and to support # the existing customization scripts, we need to use the same logic that the 'ovirt' gem # used to extract these values. For more details see 'cloud_init=' and # 'converted_cloud_init' methods in the 'vm.rb' file of the 'ovirt' gem. # # This is the list of keys that need special treatment: keys = %i[ active_directory_ou authorized_ssh_keys dns_search dns_servers domain host_name input_locale nic_configurations org_name regenerate_ssh_keys root_password system_locale timezone ui_language user_locale user_name ] # Load the YAML text and check it is a hash, as otherwise we will not be able to process it # and cloud-init will not understand it either. yaml = YAML.safe_load(content) unless yaml.kind_of?(Hash) message = \ "The cloud-init configuration '#{content}' can't be parsed as hash;" \ "cloud-init would not understand it" raise MiqException::MiqProvisionError, message end # Remove from the YAML the keys that need special treatment, and add them to the hash that # will be used to create the initialization object. hash = {} keys.each do |key| value = yaml.delete(key.to_s) hash[key] = value if value end # The SDK expects a hash where keys are symbols, but we may have strings inside nested # hashes, for example for NIC configurations, so we need to convert them to symbols: hash = hash.deep_symbolize_keys # Convert the remaining YAML back to text, removing the initial '---' line that the dump # method adds, as cloud-init does not understand it. The result will be used as the custom # cloud-init script. unless yaml.empty? script = YAML.dump(yaml) script.sub!(/^---\n/, '') hash[:custom_script] = script end # Send the update to the server: update( OvirtSDK4::Vm.new( :initialization => hash ) ) end def make_template(options) templates_service = connection.system_service.templates_service cluster = ovirt_services.cluster_from_href(options[:cluster], connection) if options[:storage] storage = ovirt_services.storage_from_href(options[:storage], connection) end vm = get vm.disk_attachments = connection.follow_link(vm.disk_attachments) template = build_template_from_hash(:name => options[:name], :vm => vm, :description => options[:description], :cluster => cluster, :storage => storage) templates_service.add(template, :seal => options[:seal]) end def build_template_from_hash(args) options = { :name => args[:name], :description => args[:description], :vm => args[:vm], :cluster => args[:cluster], :storage_domain => args[:storage] && {:id => args[:storage].id} }.compact OvirtSDK4::Template.new(options) end def update_sysprep!(content) update( OvirtSDK4::Vm.new( :initialization => { :custom_script => content } ) ) end def destroy remove end def unregister remove(:detach_only => true) end end class TemplateProxyDecorator < SimpleDelegator attr_reader :connection, :ovirt_services def initialize(template_service, connection, ovirt_services) @obj = template_service @connection = connection @ovirt_services = ovirt_services super(template_service) end def create_vm(options) return create_skeletal_vm(options) if options[:clone_type] == :skeletal create_cloned_vm(options) end def create_cloned_vm(options) vms_service = connection.system_service.vms_service cluster = ovirt_services.cluster_from_href(options[:cluster], connection) template = get clone = options[:clone_type] == :full disk_attachments = build_disk_attachments(template, options[:sparse], options[:storage], options[:name], options[:disk_format]) vm = build_vm_from_hash(:name => options[:name], :template => template, :cluster => cluster, :disk_attachments => disk_attachments) vms_service.add(vm, :clone => clone) end def create_skeletal_vm(options) vms_service = connection.system_service.vms_service cluster = ovirt_services.cluster_from_href(options[:cluster], connection) template = get vm = build_vm_from_hash(:name => options[:name], :template => blank_template_sdk_obj, :cluster => cluster) vm_res = vms_service.add(vm, :clone => false) add_disk_attachments_to_vm(template, vm_res, vms_service, options) vm_res end def blank_template_sdk_obj connection.system_service.templates_service.template_service("00000000-0000-0000-0000-000000000000").get end def add_disk_attachments_to_vm(template, vm_res, vms_service, options) disk_attachments = build_skeletal_disk_attachments(template, options[:sparse], options[:storage], options[:name], options[:disk_format]) disk_attachments_service = vms_service.vm_service(vm_res.id).disk_attachments_service disk_attachments.each do |disk_attachment| disk_attachments_service.add(disk_attachment) end end def build_skeletal_disk_attachments(template, sparse, storage_href, vm_name, disk_format) disk_attachments = connection.follow_link(template.disk_attachments) apply_full_disk_details_on_diks_attachments(disk_attachments) apply_sparsity_on_disk_attachments(disk_attachments, sparse, disk_format) unless sparse.nil? apply_storage_domain_on_disk_attachments(disk_attachments, storage_href) unless storage_href.nil? apply_name_on_disk_attachments(disk_attachments, vm_name) nullify_disk_ids(disk_attachments) disk_attachments end def nullify_disk_ids(disk_attachments) disk_attachments.each do |disk_attachment| disk_attachment.disk.href = nil disk_attachment.disk.id = nil end end def apply_full_disk_details_on_diks_attachments(disk_attachments) disk_attachments.each do |disk_attachment| current_disk = disk_attachment.disk disk = connection.system_service.disks_service.disk_service(current_disk.id).get disk_attachment.disk = disk end end def build_disk_attachments(template, sparse, storage_href, vm_name, disk_format) disk_attachments = connection.follow_link(template.disk_attachments) apply_sparsity_on_disk_attachments(disk_attachments, sparse, disk_format) unless sparse.nil? apply_storage_domain_on_disk_attachments(disk_attachments, storage_href) unless storage_href.nil? apply_name_on_disk_attachments(disk_attachments, vm_name) disk_attachments end def apply_name_on_disk_attachments(disk_attachments, vm_name) disk_attachments.each_with_index { |disk_attachment, index| disk_attachment.disk.name = "#{vm_name}_Disk#{index + 1}" } end def apply_storage_domain_on_disk_attachments(disk_attachments, storage_href) return if storage_href.nil? storage_domain = ovirt_services.storage_from_href(storage_href, connection) disk_attachments.each do |disk_attachment| disk_attachment.disk.storage_domains = [storage_domain] end end def apply_sparsity_on_disk_attachments(disk_attachments, sparse, disk_format) return if sparse.nil? disk_attachments.each do |disk_attachment| disk_attachment.disk.format = disk_format disk_attachment.disk.sparse = sparse end end def build_vm_from_hash(args) vm_options = { :name => args[:name], :template => args[:template], :cluster => args[:cluster], :disk_attachments => args[:disk_attachments] }.compact OvirtSDK4::Vm.new(vm_options) end end def cluster_from_href(href, connection) connection.system_service.clusters_service.cluster_service(uuid_from_href(href)).get end def storage_from_href(href, connection) connection.system_service.storage_domains_service.storage_domain_service(uuid_from_href(href)).get end def uuid_from_href(ems_ref) URI(ems_ref).path.split('/').last end def get_mac_address_of_nic_on_requested_vlan(args) vm = args[:destination] nics = nics_for_vm(vm) find_mac_address_on_network(nics, args[:value_of_vlan_option], vm.uid_ems) end def find_mac_address_on_network(nics, vnic_profile_id, uid_ems) vnic_profile_id = parse_vnic_profile_id(vnic_profile_id, uid_ems) nic = if vnic_profile_id == '<Empty>' nics.detect do |n| n.vnic_profile.nil? end elsif vnic_profile_id == '<Template>' nics.first else nics.detect do |n| n.vnic_profile.id == vnic_profile_id end end if nics.empty? _log.warn("Cannot find a MAC address, there are no NICs") elsif nic.nil? _log.warn("Cannot find a MAC address based on vnic_profile=#{vnic_profile_id}, there is no NIC with this profile") end nic&.mac&.address end def event_fetcher ManageIQ::Providers::Ovirt::InfraManager::EventFetcher.new(ext_management_system) end def collect_external_network_providers ext_management_system.with_provider_connection do |connection| connection.system_service.openstack_network_providers_service.list end end private def select_graphics_console(consoles) # In case of multiple graphics console ('SPICE + VNC') choose the SPICE one pref_type = 'spice' console = consoles.find { |c| c.protocol.downcase == pref_type } unless console console = consoles.first _log.warn("Can't find a console of type #{pref_type}, choosing a #{console.protocol} type one") end console end # # Hot plug of virtual memory has to be done in quanta of this size. Actually this is configurable in the # engine, using the `HotPlugMemoryMultiplicationSizeMb` configuration parameter, but it is very unlikely # that it will change. # HOT_PLUG_DIMM_SIZE = 256.megabyte.freeze def cluster_proxy_from_href(href, connection) connection.system_service.clusters_service.cluster_service(uuid_from_href(href)).get end def vm_service_by_href(href, connection) vm_uuid = uuid_from_href(href) connection.system_service.vms_service.vm_service(vm_uuid) end def template_service_by_href(href, connection) template_uuid = uuid_from_href(href) connection.system_service.templates_service.template_service(template_uuid) end def new_vm_cpu_specs(cpu_cores, cpu_sockets) { :cpu => { :topology => { :cores => cpu_cores, :sockets => cpu_sockets } } } end # # Updates the amount memory of a virtual machine. # # @param vm [OvirtSDK4::Vm] The current representation of the virtual machine. # @param memory [Integer] The new amount of memory requested by the user. # @return [Hash] the new memory specs # def new_vm_memory_specs(vm, memory) # Calculate the adjusted virtual and guaranteed memory: virtual = calculate_adjusted_virtual_memory(vm, memory) guaranteed = calculate_adjusted_guaranteed_memory(vm, memory) # The required memory cannot exceed the max configured memory of the VM. Therefore, we'll increase the max # memory up to 1TB or to the required limit, to allow a successful update for the VM. # Once 'max' memory attribute will be introduced, this code should be replaced with the specified max memory. supports_max = ext_management_system.version_at_least?('4.1') max = calculate_max_memory(vm, memory) if supports_max { :memory => virtual, :memory_policy => { :guaranteed => guaranteed, :max => (max if supports_max) }.compact } end # # Adjusts the new requested virtual memory of a virtual machine so that it satisfies the constraints imposed # by the engine. # # @param vm [OvirtSDK4::Vm] The current representation of the virtual machine. # @param memory [Integer] The new amount of memory requested by the user. # @return [Integer] The amount of memory requested by the user adjusted so that it satisfies the constrains # imposed by the engine. # def calculate_adjusted_virtual_memory(vm, memory) # Initially there is no need for adjustment: adjusted = memory # If the virtual machine is running then the difference in memory has to be a multiple of 256 MiB, otherwise # the engine will not perform the hot plug of the new memory. The reason for this is that hot plugging of # memory is performed adding a new virtual DIMM to the virtual machine, and the size of the virtual DIMM # is 256 MiB. This means that we need to round the difference up to the closest multiple of 256 MiB. if vm.status == OvirtSDK4::VmStatus::UP delta = memory - vm.memory remainder = delta % HOT_PLUG_DIMM_SIZE if remainder.positive? adjustment = HOT_PLUG_DIMM_SIZE - remainder adjusted = memory + adjustment _log.info( "The change in virtual memory of virtual machine '#{vm.name}' needs to be a multiple of " \ "#{HOT_PLUG_DIMM_SIZE / 1.megabyte} MiB, so it will be adjusted to #{adjusted / 1.megabyte} MiB." ) end end # Return the adjusted memory: adjusted end # # Adjusts the guaranteed memory of a virtual machine so that it satisfies the constraints imposed by the # engine. # # @param vm [OvirtSDK4::Vm] The current representation of the virtual machine. # @param memory [Integer] The new amount of memory requested by the user (and maybe already adjusted). # @return [Integer] The amount of guaranteed memory to request so that it satisfies the constraints imposed by # the engine. # def calculate_adjusted_guaranteed_memory(vm, memory) # Get the current amount of guaranteed memory: current = vm.memory_policy.guaranteed # Initially there is no need for adjustment: adjusted = current # The engine requires that the virtual memory is bigger or equal than the guaranteed memory at any given # time. Therefore, we need to adjust the guaranteed memory so that it is the minimum of the previous # guaranteed memory and the new virtual memory. if current > memory adjusted = memory _log.info( "The guaranteed physical memory of virtual machine '#{vm.name}' needs to be less or equal than the " \ "virtual memory, so it will be adjusted to #{adjusted / 1.megabyte} MiB." ) end # Return the adjusted guaranteed memory: adjusted end # # Adjusts the max memory of a virtual machine so that it satisfies the constraints imposed by the # engine. The max memory is supported since version 4.1 and limited to 1TB according to the UI limits # defined for ovirt provider. # # @param vm [OvirtSDK4::Vm] The current representation of the virtual machine. # @param memory [Integer] The new amount of memory requested by the user. # @return [Integer] The amount of max memory to request so that it satisfies the constraints imposed by # the engine. # def calculate_max_memory(vm, memory) max = vm.memory_policy&.max || memory if memory >= 1.terabyte max = memory elsif memory > max max = [memory * 4, 1.terabyte].min end max end def edit_vm_disks(vm_service, disks_specs) disk_attachments_service = vm_service.disk_attachments_service disks_specs.each do |d| das = disk_attachments_service.attachment_service(d[:disk_name]) das.update(:disk => {:provisioned_size => d[:disk_size_in_mb].megabytes}) rescue OvirtSDK4::NotFoundError => err err_msg = "No disk with the id [#{d[:disk_name]}] is attached to the vm" _log.error("#{err_msg} | #{err}") raise ManageIQ::Providers::Ovirt::InfraManager::OvirtServices::Error, err_msg rescue OvirtSDK4::Error => err err_msg = "Error resizing disk with the id [#{d[:disk_name]}]. #{err.fault&.detail}" _log.error("#{err_msg} | #{err}") raise ManageIQ::Providers::Ovirt::InfraManager::OvirtServices::Error, err_msg end end # # Adds disks to a virtual machine. # # @param vm_service [OvirtSDK4::VmsService] The service that manages the virtual machine. # @param disk_specs [Hash] The specification of the disks to add. # def add_vm_disks(vm_service, disk_specs) storage_spec = disk_specs[:storage] default_disk_spec = disk_specs[:default] || {} attachments_service = vm_service.disk_attachments_service disk_specs[:disks].each do |disk_spec| attachment = prepare_vm_disk_attachment(default_disk_spec.merge(disk_spec), storage_spec) attachments_service.add(attachment) end end # # Prepares a disk attachment for adding a new disk to a virtual machine. # # @param disk_spec [Hash] The specification of the disk to add. # @param storage_spec [Hash] The specification of the storage to use. # def prepare_vm_disk_attachment(disk_spec, storage_spec) disk_spec = disk_spec.symbolize_keys attachment_builder = ManageIQ::Providers::Ovirt::InfraManager::DiskAttachmentBuilder.new( :size_in_mb => disk_spec[:disk_size_in_mb], :storage => storage_spec, :name => disk_spec[:disk_name], :thin_provisioned => disk_spec[:thin_provisioned], :bootable => disk_spec[:bootable], :interface => disk_spec[:interface] ) attachment_builder.disk_attachment end # # Removes disks from a virtual machine. # # @param vm_service [OvirtSDK4::VmsService] The service that manages the virtual machine. # @param disk_specs [Array<Hash>] The specifications of the disks to remove. # # TODO: Note that disk_spec['disk_name'] is actually the disks id not its name. # we should change that because it is very confusing. def remove_vm_disks(vm_service, disk_specs) attachments_service = vm_service.disk_attachments_service disk_specs.each do |disk_spec| disk_spec = disk_spec.with_indifferent_access attachment_service = attachments_service.attachment_service(disk_spec['disk_name']) attachment_service.remove(:detach_only => !disk_spec['delete_backing']) rescue OvirtSDK4::NotFoundError raise "no disk with the id #{disk_spec['disk_name']} is attached to the vm: #{vm_service.get.name}" rescue OvirtSDK4::Error raise "Failed to detach disk with the id #{disk_spec['disk_name']} from the vm: #{vm_service.get.name}, check that it exists" end end def add_vm_networks(networks, vm_service) nics_service = vm_service.nics_service with_each_network(networks) do |n| configure_vnic( :nic_name => n[:name], :vnic_profile => n[:vnic_profile_id], :nics_service => nics_service, :logger => _log ) end end def edit_vm_networks(networks, vm_service) nics_service = vm_service.nics_service with_each_network(networks) do |n| vnic = Struct.new(:id).new(n[:nic_id]) nic_service = nics_service.nic_service(vnic.id) nic_service.deactivate configure_vnic( :nic_name => n[:name], :vnic_profile => n[:vnic_profile_id], :nics_service => nics_service, :vnic => vnic, :logger => _log ) nic_service.activate end end def remove_vm_networks(networks, vm_service) with_each_network(networks) do |n| nic_service = vm_service.nics_service.nic_service(n[:nic_id]) nic_service.deactivate nic_service.remove end end def with_each_network(networks) networks.each do |n| yield n rescue OvirtSDK4::NotFoundError => err err_msg = "Error reconfiguring [#{n[:name]}]. NIC not found" _log.error("#{err_msg} | #{err}") raise ManageIQ::Providers::Ovirt::InfraManager::OvirtServices::Error, err_msg rescue OvirtSDK4::Error => err err_msg = "Error reconfiguring [#{n[:name]}]. #{err.fault&.detail}" _log.error("#{err_msg} | #{err}") raise ManageIQ::Providers::Ovirt::InfraManager::OvirtServices::Error, err_msg end end def configure_vnic_with_requested_data(name, requested_vnic, destination_vnic, nics_service, destination_cluster) requested_profile_id = parse_vnic_profile_id(requested_vnic[:network], destination_cluster.uid_ems) if requested_profile_id == '<Empty>' profile_id = nil else vnic_profile = find_vnic_profile_in_cluster(requested_profile_id, destination_cluster.uid_ems) if vnic_profile.nil? raise MiqException::MiqProvisionError, "Unable to find specified profile: <#{requested_profile_id}>" else profile_id = vnic_profile[0].id end end configure_vnic( :mac_addr => requested_vnic[:mac_address], :vnic_profile => profile_id, :nic_name => name, :interface => requested_vnic[:interface], :vnic => destination_vnic, :nics_service => nics_service, :logger => _log ) end # TODO: @borod108 consider if we can get this from local as well during provisioning def private_load_allowed_networks_from_provider(vlans, uid_ems_cluster) profiles = get_vnic_profiles_in_cluster(uid_ems_cluster) profiles.each do |profile, profile_network| vlans[profile.id] = "#{profile.name} (#{profile_network.name})" end vlans['<Empty>'] = _('<No Profile>') vlans['<Template>'] = _('<Use template nics>') end def private_load_allowed_networks(vlans, uid_ems_cluster) profiles = local_get_vnic_profiles_in_cluster(uid_ems_cluster) profiles.merge!(local_external_vnic_profiles(uid_ems_cluster)) profiles.each do |profile, profile_network| vlans[profile.uid_ems] = "#{profile.name} (#{profile_network.name})" end vlans['<Empty>'] = _('<No Profile>') vlans['<Template>'] = _('<Use template nics>') end def local_get_vnic_profiles_in_cluster(uid_ems_cluster) cluster_profiles = {} profiles = ext_management_system.distributed_virtual_lans cluster_networks = all_networks_as_switches_in_cluster(uid_ems_cluster) profiles.each do |p| profile_network = cluster_networks.detect { |n| n.id == p.switch.id } if profile_network cluster_profiles[p] = profile_network end end cluster_profiles end def local_external_vnic_profiles(uid_ems_cluster) external_profiles = {} cluster = cluster_by_uid_ems(uid_ems_cluster) datacenter = cluster.parent_datacenter profiles = datacenter.external_distributed_virtual_lans external_networks = datacenter.external_distributed_virtual_switches profiles.each do |p| profile_network = external_networks.detect { |n| n.id == p.switch.id } if profile_network external_profiles[p] = profile_network end end external_profiles end def all_networks_as_switches_in_cluster(uid_ems_cluster) cluster = cluster_by_uid_ems(uid_ems_cluster) cluster&.switches end def cluster_by_uid_ems(uid_ems) ext_management_system.ems_clusters.find_by(:uid_ems => uid_ems) end def parse_vnic_profile_id(requested_profile, uid_ems_cluster) if requested_profile.include?('(') vlans = {} private_load_allowed_networks_from_provider(vlans, uid_ems_cluster) matches = vlans.select { |_profile_id, profile_description| profile_description == requested_profile } return matches.keys[0] unless matches.empty? end requested_profile end def configure_vnic(args) mac_addr = args[:mac_addr] vnic = args[:vnic] profile_id = args[:vnic_profile] nics_service = args[:nics_service] options = { :name => args[:nic_name], :interface => args[:interface], :mac => mac_addr ? OvirtSDK4::Mac.new(:address => mac_addr) : nil, :vnic_profile => {:id => profile_id} }.delete_blanks args[:logger].info("with options: <#{options.inspect}>") if vnic nics_service.nic_service(vnic.id).update(options) else nics_service.add(OvirtSDK4::Nic.new(options)) end end def get_vnic_profiles_in_cluster(uid_ems_cluster) cluster_profiles = {} ext_management_system.with_provider_connection do |connection| profiles = connection.system_service.vnic_profiles_service.list cluster_networks = connection.system_service.clusters_service.cluster_service(uid_ems_cluster).networks_service.list profiles.each do |p| profile_network = cluster_networks.detect { |n| n.id == p.network.id } if profile_network cluster_profiles[p] = profile_network end end end cluster_profiles end def find_vnic_profile_in_cluster(profile_id, uid_ems_cluster) profiles = get_vnic_profiles_in_cluster(uid_ems_cluster) profiles.detect { |profile, _profile_network| profile.id == profile_id } end endend