# frozen_string_literal: true # Copyright 2019 Tristan Robert # This file is part of ForemanFogProxmox. # ForemanFogProxmox is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # ForemanFogProxmox is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with ForemanFogProxmox. If not, see . require 'fog/proxmox/helpers/disk_helper' require 'foreman_fog_proxmox/hash_collection' module ForemanFogProxmox module ProxmoxVolumes include ProxmoxVmHelper def delete_volume(vm, id, volume_attributes) logger.info(format(_('vm %s delete volume %s'), vmid: vm.identity, volume_id: id)) vm.detach(id) return unless volume_type?(volume_attributes, 'hard_disk') device = Fog::Proxmox::DiskHelper.extract_device(id) vm.detach('unused' + device.to_s) end def volume_options(vm, id, volume_attributes) options = {} options.store(:mp, volume_attributes['mp']) if vm.container? && id != 'rootfs' options.store(:cache, volume_attributes['cache']) unless vm.container? options end def update_volume_required?(old_volume_attributes, new_volume_attributes) old_h = ForemanFogProxmox::HashCollection.new_hash_reject_empty_values(old_volume_attributes) new_h = ForemanFogProxmox::HashCollection.new_hash_reject_empty_values(new_volume_attributes) new_h = ForemanFogProxmox::HashCollection.new_hash_reject_keys(new_h, ['cdrom', 'cloudinit', 'storage_type']) !ForemanFogProxmox::HashCollection.equals?(old_h.with_indifferent_access, new_h.with_indifferent_access) end def update_cdrom(vm, disk, volume_attributes) new_disk = { id: disk.id } if ['none', 'cdrom'].include?(volume_attributes[:cdrom]) new_disk[:volid] = volume_attributes[:cdrom] else new_disk[:storage] = volume_attributes[:storage] new_disk[:volid] = volume_attributes[:volid] end vm.attach(new_disk, {}) end def extend_volume(vm, id, diff_size) extension = '+' + (diff_size / GIGA).to_s + 'G' logger.info(format(_('vm %s extend volume %s to %s'), vmid: vm.identity, volume_id: id, extension: extension)) vm.extend(id, extension) end def move_volume(id, vm, new_storage) logger.info(format(_('vm %s move volume %s into %s'), vmid: vm.identity, volume_id: id, new_storage: new_storage)) vm.move(id, new_storage) end def update_options(disk, vm, volume_attributes) options = volume_options(vm, disk.id, volume_attributes) if volume_type?(volume_attributes, 'hard_disk') logger.info(format(_('vm %s update volume %s to %s'), vmid: vm.identity, volume_id: disk.id, options: options)) new_disk = { id: disk.id } new_disk[:volid] = disk.volid vm.attach(new_disk, options) end def update_volume(vm, disk, volume_attributes) id = disk.id if volume_type?(volume_attributes, 'cdrom') update_cdrom(vm, disk, volume_attributes) elsif volume_type?(volume_attributes, 'hard_disk') diff_size = volume_attributes['size'].to_i - disk.size if volume_attributes['size'] && disk.size raise ::Foreman::Exception, format(_('Unable to shrink %s size. Proxmox allows only increasing size.'), id: id) unless diff_size >= 0 new_storage = volume_attributes['storage'] if diff_size > 0 extend_volume(vm, id, diff_size) elsif disk.storage != new_storage move_volume(id, vm, new_storage) else update_options(disk, vm, volume_attributes) end end end def volume_exists?(vm, volume_attributes) vm.attributes.key?(volume_attributes['id']) end def volume_to_delete?(volume_attributes) volume_attributes['_delete'].blank? ? false : Foreman::Cast.to_bool(volume_attributes['_delete']) end def extract_id(vm, volume_attributes) id = '' if volume_exists?(vm, volume_attributes) id = volume_attributes['id'] else device = vm.container? ? 'mp' : volume_attributes['controller'] id = volume_type?(volume_attributes, 'cdrom') ? 'ide2' : device + volume_attributes['device'] end id end def add_volume(vm, id, volume_attributes) disk_attributes = { id: id } if volume_type?(volume_attributes, 'hard_disk') options = volume_options(vm, id, volume_attributes) disk_attributes[:storage] = volume_attributes['storage'] disk_attributes[:size] = (volume_attributes['size'].to_i / GIGA).to_s elsif volume_type?(volume_attributes, 'cdrom') disk_attributes[:volid] = volume_attributes[:iso] elsif volume_type?(volume_attributes, 'cloud_init') disk_attributes[:storage] = volume_attributes['storage'] disk_attributes[:volid] = "#{volume_attributes['storage']}:cloudinit" end logger.info(format(_('vm %s add volume %s'), vmid: vm.identity, volume_id: id)) logger.debug(format(_('add_volume(%s) disk_attributes=%s'), vmid: vm.identity, disk_attributes: disk_attributes)) vm.attach(disk_attributes, options) end def save_volume(vm, volume_attributes) logger.debug(format(_('save_volume(%s) volume_attributes=%s'), vmid: vm.identity, volume_attributes: volume_attributes)) id = extract_id(vm, volume_attributes) if volume_exists?(vm, volume_attributes) if volume_to_delete?(volume_attributes) delete_volume(vm, id, volume_attributes) else disk = vm.config.disks.get(id) update_volume(vm, disk, volume_attributes) if update_volume_required?(disk.attributes, volume_attributes) end else add_volume(vm, id, volume_attributes) end end end end