fog/fog-proxmox

View on GitHub
lib/fog/proxmox/compute/models/server.rb

Summary

Maintainability
B
5 hrs
Test Coverage
A
98%
# frozen_string_literal: true

# Copyright 2018 Tristan Robert

# This file is part of Fog::Proxmox.

# Fog::Proxmox 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.

# Fog::Proxmox 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 Fog::Proxmox. If not, see <http://www.gnu.org/licenses/>.

require 'fog/compute/models/server'
require 'fog/proxmox/helpers/disk_helper'
require 'fog/proxmox/hash'
require 'fog/proxmox/errors'

module Fog
  module Proxmox
    class Compute
      # Server model
      # rubocop:disable ClassLength
      class Server < Fog::Compute::Server
        identity  :vmid
        attribute :type
        attribute :node_id
        attribute :config
        attribute :digest
        attribute :name
        attribute :maxdisk
        attribute :disk
        attribute :diskwrite
        attribute :diskread
        attribute :uptime
        attribute :netout
        attribute :netin
        attribute :cpu
        attribute :cpus
        attribute :template
        attribute :status
        attribute :maxcpu
        attribute :mem
        attribute :maxmem
        attribute :qmpstatus
        attribute :ha
        attribute :pid
        attribute :blockstat
        attribute :balloon
        attribute :ballooninfo
        attribute :snapshots
        attribute :tasks
        attribute :vmgenid
        attribute :lock
        attribute :maxswap
        attribute :swap

        def initialize(new_attributes = {})
          prepare_service_value(new_attributes)
          Fog::Proxmox::Attributes.set_attr_and_sym('node_id', attributes, new_attributes)
          Fog::Proxmox::Attributes.set_attr_and_sym('type', attributes, new_attributes)
          Fog::Proxmox::Attributes.set_attr_and_sym('vmid', attributes, new_attributes)
          requires :node_id, :type, :vmid
          initialize_config(new_attributes)
          initialize_snapshots
          initialize_tasks
          super(new_attributes)
        end

        def to_s
          name
        end

        def container?
          type == 'lxc'
        end

        def persisted?
          service.next_vmid(vmid: vmid)
          false
        rescue Excon::Error::InternalServerError
          false
        rescue Excon::Error::BadRequest
          true
        end

        # request with async task
        def request(name, body_params = {}, path_params = {})
          requires :node_id, :type
          path = path_params.merge(node: node_id, type: type)
          task_upid = service.send(name, path, body_params)
          tasks.wait_for(task_upid)
        end

        def save(new_attributes = {})
          body_params = new_attributes.merge(vmid: vmid)
          body_params = body_params.merge(config.flatten) unless persisted?
          request(:create_server, body_params)
          reload
        end

        def update(new_attributes = {})
          if container?
            path_params = { node: node_id, type: type, vmid: vmid }
            body_params = new_attributes
            service.update_server(path_params, body_params)
          else
            request(:update_server, new_attributes, vmid: vmid)
          end
          reload
        end

        def destroy(options = {})
          request(:delete_server, options, vmid: vmid)
        end

        def action(action, options = {})
          action_known = %w[start stop resume suspend shutdown reset].include? action
          message = "Action #{action} not implemented"
          raise Fog::Errors::Error, message unless action_known

          request(:action_server, options, action: action, vmid: vmid)
          reload
        end

        def ready?
          status == 'running'
        end

        def backup(options = {})
          request(:create_backup, options.merge(vmid: vmid))
          reload
        end

        def restore(backup, options = {})
          attr_hash = if container?
                        options.merge(ostemplate: backup.volid, force: 1, restore: 1)
                      else
                        options.merge(archive: backup.volid, force: 1)
                      end
          save(attr_hash)
        end

        def clone(newid, options = {})
          request(:clone_server, options.merge(newid: newid), vmid: vmid)
          reload
        end

        def create_template(options = {})
          service.template_server({ node: node_id, type: type, vmid: vmid }, options)
          reload
        end

        def migrate(target, options = {})
          request(:migrate_server, options.merge(target: target), vmid: vmid)
        end

        def extend(disk, size, options = {})
          if container?
            request(:resize_container, options.merge(disk: disk, size: size), vmid: vmid)
          else
            service.resize_server({ vmid: vmid, node: node_id }, options.merge(disk: disk, size: size))
          end
          reload
        end

        def move(volume, storage, options = {})
          if container?
            request(:move_volume, options.merge(volume: volume, storage: storage), vmid: vmid)
          else
            request(:move_disk, options.merge(disk: volume, storage: storage), vmid: vmid)
          end
          reload
        end

        def attach(disk, options = {})
          disk_hash = Fog::Proxmox::DiskHelper.flatten(disk.merge(options: options))
          update(disk_hash)
        end

        def detach(diskid)
          update(delete: diskid)
        end

        def start_console(options = {})
          unless ready?
            raise ::Fog::Proxmox::Errors::ServiceError,
                  'Unable to start console because server not running.'
          end

          if container?
            type_console = options[:console]
            options.delete_if { |option| [:console].include? option }
            unless type_console
              raise ::Fog::Proxmox::Errors::ServiceError,
                    'Unable to start console because console container config is not set or unknown.'
            end
          else
            type_console = config.type_console
            unless type_console
              raise ::Fog::Proxmox::Errors::ServiceError,
                    'Unable to start console because VGA display server config is not set or unknown.'
            end
          end
          requires :vmid, :node_id, :type
          path_params = { node: node_id, type: type, vmid: vmid }
          body_params = options
          data = service.send(('create_' + type_console).to_sym, path_params, body_params)
          task_upid = data['upid']
          if task_upid
            task = tasks.get(task_upid)
            task.wait_for { running? }
          end
          data
        end

        def connect_vnc(options = {})
          path_params = { node: node_id, type: type, vmid: vmid }
          query_params = { port: options['port'], vncticket: options['ticket'] }
          service.get_vnc(path_params, query_params)
        end

        def backups
          list 'backup'
        end

        def images
          list 'images'
        end

        def list(content)
          storages = node.storages.list_by_content_type content
          volumes = []
          storages.each { |storage| volumes += storage.volumes.list_by_content_type_and_by_server(content, identity) }
          volumes
        end

        protected

        def initialize_config(new_attributes = {})
          options = { service: service, vmid: vmid }
          attributes[:config] = Fog::Proxmox::Compute::ServerConfig.new(options.merge(new_attributes))
        end

        private

        def initialize_snapshots
          attributes[:snapshots] =
            Fog::Proxmox::Compute::Snapshots.new(service: service, server_id: vmid, server_type: type, node_id: node_id)
        end

        def initialize_tasks
          attributes[:tasks] = Fog::Proxmox::Compute::Tasks.new(service: service, node_id: node_id).select do |task|
            task.id == vmid
          end
        end

        def node
          Fog::Proxmox::Compute::Node.new(service: service, node: node_id)
        end
      end
      # rubocop:enable ClassLength
    end
  end
end