fog-libvirt-0.13.2/0000755000004100000410000000000014743305667014070 5ustar www-datawww-datafog-libvirt-0.13.2/minitests/0000755000004100000410000000000014743305667016107 5ustar www-datawww-datafog-libvirt-0.13.2/minitests/test_helper.rb0000644000004100000410000000065014743305667020753 0ustar www-datawww-datarequire 'minitest/autorun' require 'mocha/minitest' require 'fileutils' $: << File.join(File.dirname(__FILE__), '..', 'lib') logdir = File.join(File.dirname(__FILE__), '..', 'logs') FileUtils.mkdir_p(logdir) unless File.exist?(logdir) ENV['TMPDIR'] = 'test/tmp' FileUtils.rm_f Dir.glob 'test/tmp/*.tmp' require 'fog/libvirt' Fog.mock! Fog.credentials = { :libvirt_uri => 'test:///default', }.merge(Fog.credentials) fog-libvirt-0.13.2/minitests/server/0000755000004100000410000000000014743305667017415 5ustar www-datawww-datafog-libvirt-0.13.2/minitests/server/user_data_iso_test.rb0000644000004100000410000000501214743305667023620 0ustar www-datawww-datarequire 'test_helper' class UserDataIsoTest < Minitest::Test def setup @compute = Fog::Compute[:libvirt] @server = @compute.servers.new(:name => "test") @test_data = "test data" end def test_contains_meta_data_file @server.stubs(:system).returns(true) in_a_temp_dir do |d| @server.generate_config_iso_in_dir(d, @test_data) {|iso| assert File.exist?(File.join(d, 'meta-data')) } end end def test_contains_user_data_file @server.stubs(:system).returns(true) in_a_temp_dir do |d| @server.generate_config_iso_in_dir(d, @test_data) do |iso| assert File.exist?(File.join(d, 'user-data')) assert_equal @test_data, File.read(File.join(d, 'user-data')) end end end def test_iso_is_generated in_a_temp_dir do |d| @server.expects(:system).with(regexp_matches(/^genisoimage/)).returns(true) @server.generate_config_iso_in_dir(d, @test_data) {|iso| } end end def test_volume_is_created_during_user_data_iso_generation @server.stubs(:system).returns(true) Fog::Libvirt::Compute::Volumes.any_instance.expects(:create). with(has_entries(:name => @server.cloud_init_volume_name)). returns(@compute.volumes.new) Fog::Libvirt::Compute::Volume.any_instance.stubs(:upload_image) @server.create_user_data_iso end def test_volume_is_uploaded_during_user_data_iso_generation @server.stubs(:system).returns(true) Fog::Libvirt::Compute::Volumes.any_instance.stubs(:create).returns(@compute.volumes.new) Fog::Libvirt::Compute::Volume.any_instance.expects(:upload_image).returns(true) @server.create_user_data_iso end def test_iso_file_is_set_during_user_data_iso_generation @server.stubs(:system).returns(true) Fog::Libvirt::Compute::Volumes.any_instance.stubs(:create).returns(@compute.volumes.new) Fog::Libvirt::Compute::Volume.any_instance.stubs(:upload_image) @server.create_user_data_iso assert_equal @server.cloud_init_volume_name, @server.iso_file end def test_iso_dir_is_set_during_user_data_iso_generation @server.stubs(:system).returns(true) volume = @compute.volumes.new volume.stubs(:path).returns("/srv/libvirt/#{@server.cloud_init_volume_name}") Fog::Libvirt::Compute::Volumes.any_instance.stubs(:create).returns(volume) Fog::Libvirt::Compute::Volume.any_instance.stubs(:upload_image) @server.create_user_data_iso assert_equal '/srv/libvirt', @server.iso_dir end def in_a_temp_dir Dir.mktmpdir('test-dir') do |d| yield d end end end fog-libvirt-0.13.2/lib/0000755000004100000410000000000014743305667014636 5ustar www-datawww-datafog-libvirt-0.13.2/lib/fog/0000755000004100000410000000000014743305667015411 5ustar www-datawww-datafog-libvirt-0.13.2/lib/fog/bin/0000755000004100000410000000000014743305667016161 5ustar www-datawww-datafog-libvirt-0.13.2/lib/fog/bin/libvirt.rb0000644000004100000410000000302314743305667020157 0ustar www-datawww-datamodule Libvirt # deviates from other bin stuff to accommodate gem class << self def class_for(key) case key when :compute Fog::Libvirt::Compute else raise ArgumentError, "Unrecognized service: #{key}" end end def [](service) @@connections ||= Hash.new do |hash, key| hash[key] = case key when :compute Fog::Logger.warning("Libvirt[:compute] is not recommended, use Compute[:libvirt] for portability") Fog::Compute.new(:provider => 'Libvirt') else raise ArgumentError, "Unrecognized service: #{key.inspect}" end end @@connections[service] end def available? begin availability=true unless Gem::Specification::find_by_name("ruby-libvirt").nil? rescue Gem::LoadError availability=false rescue availability_gem=Gem.available?("ruby-libvirt") end if availability for service in services for collection in self.class_for(service).collections unless self.respond_to?(collection) self.class_eval <<-EOS, __FILE__, __LINE__ def self.#{collection} self[:#{service}].#{collection} end EOS end end end end availability end def collections services.map {|service| self[service].collections}.flatten.sort_by {|service| service.to_s} end def services Fog::Libvirt.services end end end fog-libvirt-0.13.2/lib/fog/libvirt.rb0000644000004100000410000000045714743305667017417 0ustar www-datawww-datarequire 'fog/core' require 'fog/xml' require 'fog/json' require 'libvirt' require File.expand_path('../libvirt/version', __FILE__) module Fog module Libvirt extend Fog::Provider autoload :Compute, File.expand_path('../libvirt/compute', __FILE__) service(:compute, 'Compute') end end fog-libvirt-0.13.2/lib/fog/libvirt/0000755000004100000410000000000014743305667017064 5ustar www-datawww-datafog-libvirt-0.13.2/lib/fog/libvirt/requests/0000755000004100000410000000000014743305667020737 5ustar www-datawww-datafog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/0000755000004100000410000000000014743305667022413 5ustar www-datawww-datafog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/define_domain.rb0000644000004100000410000000042714743305667025524 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def define_domain(xml) client.define_domain_xml(xml) end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/list_interfaces.rb0000644000004100000410000000242114743305667026115 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def list_interfaces(filter = { }) data=[] if filter.keys.empty? active_networks = client.list_interfaces rescue [] defined_networks = client.list_defined_interfaces rescue [] (active_networks + defined_networks).each do |ifname| data << interface_to_attributes(client.lookup_interface_by_name(ifname)) end else data = [interface_to_attributes(get_interface_by_filter(filter))] end data.compact end private # Retrieve the interface by mac or by name def get_interface_by_filter(filter) case filter.keys.first when :mac client.lookup_interface_by_mac(filter[:mac]) when :name client.lookup_interface_by_name(filter[:name]) end end def interface_to_attributes(net) return if net.nil? || net.name == 'lo' { :mac => net.mac, :name => net.name, :active => net.active? } end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/create_domain.rb0000644000004100000410000000042714743305667025535 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def create_domain(xml) client.create_domain_xml(xml) end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/clone_volume.rb0000644000004100000410000000065714743305667025437 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def clone_volume(pool_name, xml, name) vol = client.lookup_storage_pool_by_name(pool_name).lookup_volume_by_name(name) client.lookup_storage_pool_by_name(pool_name).create_vol_xml_from(xml, vol) end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/volume_action.rb0000644000004100000410000000051114743305667025601 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def volume_action(key, action, options={}) get_volume({:key => key}, true).send(action) true end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/list_domains.rb0000644000004100000410000001024114743305667025423 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def list_domains(filter = { }) data=[] if filter.key?(:uuid) data << client.lookup_domain_by_uuid(filter[:uuid]) elsif filter.key?(:name) data << client.lookup_domain_by_name(filter[:name]) else client.list_defined_domains.each { |name| data << catchLibvirtExceptions { client.lookup_domain_by_name(name) } } unless filter[:defined] == false client.list_domains.each { |id| data << catchLibvirtExceptions { client.lookup_domain_by_id(id) } } unless filter[:active] == false end data.compact.map { |d| domain_to_attributes d }.compact end # Catch Libvirt exceptions to avoid race conditions involving # concurrent libvirt operations from other processes. For example, # domains being undefined while fog-libvirt is trying to work with # domain lists. def catchLibvirtExceptions yield rescue ::Libvirt::RetrieveError, ::Libvirt::Error nil end private def domain_display xml attrs = {} [:type, :port, :password, :listen].each do |element| attrs[element] = xml_element(xml, "domain/devices/graphics",element.to_s) rescue nil end attrs.reject{|k,v| v.nil? or v == ""} end def domain_volumes xml xml_elements(xml, "domain/devices/disk/source").map do |element| element[:file] || element[:dev] || element[:name] end end def boot_order xml xml_elements(xml, "domain/os/boot", "dev") end def firmware(xml) firmware_from_loader = xml_elements(xml, "domain/os/loader", "type").first case firmware_from_loader when 'pflash' 'efi' when 'rom' 'bios' else xml_elements(xml, "domain/os", "firmware").first || 'bios' end end # we rely on the fact that the secure attribute is only present when secure boot is enabled def secure_boot_enabled?(xml) xml_elements(xml, "domain/os/loader", "secure").first == 'yes' end def domain_interfaces xml ifs = xml_elements(xml, "domain/devices/interface") ifs.map { |i| nics.new({ :type => i['type'], :mac => (i/'mac').first[:address], :network => ((i/'source').first[:network] rescue nil), :bridge => ((i/'source').first[:bridge] rescue nil), :model => ((i/'model').first[:type] rescue nil), }.reject{|k,v| v.nil?}) } end def domain_to_attributes(dom) states= %w(nostate running blocked paused shutting-down shutoff crashed pmsuspended) begin { :id => dom.uuid, :uuid => dom.uuid, :name => dom.name, :max_memory_size => dom.info.max_mem, :cputime => dom.info.cpu_time, :memory_size => dom.info.memory, :cpus => dom.info.nr_virt_cpu, :autostart => dom.autostart?, :os_type => dom.os_type, :active => dom.active?, :display => domain_display(dom.xml_desc), :boot_order => boot_order(dom.xml_desc), :nics => domain_interfaces(dom.xml_desc), :volumes_path => domain_volumes(dom.xml_desc), :state => states[dom.info.state], :firmware => firmware(dom.xml_desc), :secure_boot => secure_boot_enabled?(dom.xml_desc), } rescue ::Libvirt::RetrieveError, ::Libvirt::Error # Catch libvirt exceptions to avoid race conditions involving # concurrent libvirt operations (like from another process) return nil end end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/destroy_interface.rb0000644000004100000410000000051414743305667026451 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared #shutdown the interface def destroy_interface(uuid) client.lookup_interface_by_uuid(uuid).destroy end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/list_networks.rb0000644000004100000410000000255514743305667025656 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def list_networks(filter = { }) data=[] if filter.keys.empty? (client.list_networks + client.list_defined_networks).each do |network_name| data << network_to_attributes(client.lookup_network_by_name(network_name)) end else data = [network_to_attributes(get_network_by_filter(filter))] end data end private # Retrieve the network by uuid or name def get_network_by_filter(filter) case filter.keys.first when :uuid client.lookup_network_by_uuid(filter[:uuid]) when :name client.lookup_network_by_name(filter[:name]) end end # bridge name may not be defined in some networks, we should skip that in such case def network_to_attributes(net) return if net.nil? begin bridge_name = net.bridge_name rescue ::Libvirt::Error bridge_name = '' end { :uuid => net.uuid, :name => net.name, :bridge_name => bridge_name } end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/define_pool.rb0000644000004100000410000000043314743305667025223 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def define_pool(xml) client.define_storage_pool_xml(xml) end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/destroy_network.rb0000644000004100000410000000045014743305667026201 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def destroy_network(uuid) client.lookup_network_by_uuid(uuid).destroy end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/list_volumes.rb0000644000004100000410000000475414743305667025477 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def list_volumes(filter = { }) data = [] if filter.keys.empty? raw_volumes do |pool| pool.list_volumes.each do |volume_name| begin data << volume_to_attributes(pool.lookup_volume_by_name(volume_name)) rescue ::Libvirt::RetrieveError # Catch libvirt exceptions to avoid race conditions involving # concurrent libvirt operations (like from another process) next end end end else data << get_volume(filter) end data.compact end private def volume_to_attributes(vol) format_type = xml_element(vol.xml_desc, "/volume/target/format", "type") rescue nil # not all volumes have types, e.g. LVM return nil if format_type == "dir" begin { :pool_name => vol.pool.name, :key => vol.key, :id => vol.key, :path => vol.path, :name => vol.name, :format_type => format_type, :allocation => bytes_to_gb(vol.info.allocation), :capacity => bytes_to_gb(vol.info.capacity), } rescue ::Libvirt::RetrieveError, ::Libvirt::Error return nil # If there are issues during stat of volume file end end def bytes_to_gb bytes bytes / 1024**3 end def raw_volumes client.list_storage_pools.each do |pool_name| pool = client.lookup_storage_pool_by_name(pool_name) yield(pool) end end def get_volume filter = { }, raw = false raw_volumes do |pool| vol = case filter.keys.first when :name pool.lookup_volume_by_name(filter[:name]) rescue nil when :key pool.lookup_volume_by_key(filter[:key]) rescue nil when :path pool.lookup_volume_by_path(filter[:path]) rescue nil end if vol return raw ? vol : volume_to_attributes(vol) end end nil end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/pool_action.rb0000644000004100000410000000053214743305667025246 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def pool_action(uuid, action) pool = client.lookup_storage_pool_by_uuid uuid pool.send(action) true end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/update_display.rb0000644000004100000410000000221614743305667025750 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def update_display(options = { }) raise ArgumentError, "uuid is a required parameter" unless options.key? :uuid domain = client.lookup_domain_by_uuid(options[:uuid]) display = { } display[:type] = options[:type] || 'vnc' display[:port] = (options[:port] || -1).to_s display[:listen] = options[:listen].to_s if options[:listen] display[:passwd] = options[:password].to_s if options[:password] display[:autoport] = 'yes' if display[:port] == '-1' new_keymap = options[:keymap] || xml_elements(domain.xml_desc, "graphics", "keymap")[0] display[:keymap] = new_keymap unless new_keymap.nil? builder = Nokogiri::XML::Builder.new { graphics_ (display) } xml = Nokogiri::XML(builder.to_xml).root.to_s domain.update_device(xml, 0) # if we got no exceptions, then we're good' true end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/vm_action.rb0000644000004100000410000000055114743305667024720 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def vm_action(uuid, action, *params) domain = client.lookup_domain_by_uuid(uuid) domain.send(action, *params) true end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/libversion.rb0000644000004100000410000000040614743305667025114 0ustar www-datawww-data module Fog module Libvirt class Compute module Shared def libversion() client.libversion end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/create_volume.rb0000644000004100000410000000050614743305667025573 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def create_volume(pool_name, xml) client.lookup_storage_pool_by_name(pool_name).create_vol_xml(xml) end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/update_autostart.rb0000644000004100000410000000052314743305667026330 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def update_autostart(uuid, value) domain = client.lookup_domain_by_uuid(uuid) domain.autostart = value end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/upload_volume.rb0000644000004100000410000000145214743305667025615 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def upload_volume(pool_name, volume_name, file_path) volume = client.lookup_storage_pool_by_name(pool_name).lookup_volume_by_name(volume_name) stream = client.stream image_file = File.open(file_path, "rb") volume.upload(stream, 0, image_file.size) stream.sendall do |_opaque, n| begin r = image_file.read(n) r ? [r.length, r] : [0, ""] rescue Exception => e [-1, ""] end end stream.finish ensure image_file.close if image_file end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/get_node_info.rb0000644000004100000410000000205514743305667025541 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def get_node_info node_hash = Hash.new node_info = client.node_get_info [:model, :memory, :cpus, :mhz, :nodes, :sockets, :cores, :threads].each do |param| node_hash[param] = node_info.send(param) rescue nil end [:type, :version, :node_free_memory, :max_vcpus].each do |param| node_hash[param] = client.send(param) rescue nil end node_hash[:uri] = client.uri xml = client.sys_info rescue nil [:uuid, :manufacturer, :product, :serial].each do |attr| node_hash[attr] = node_attr(attr, xml) rescue nil end if xml node_hash[:hostname] = client.hostname [node_hash] end private def node_attr attr, xml xml_element(xml, "sysinfo/system/entry[@name='#{attr}']").strip end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/list_pools.rb0000644000004100000410000000347314743305667025136 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def list_pools(filter = { }) data=[] if filter.key?(:name) data << find_pool_by_name(filter[:name], filter[:include_inactive]) elsif filter.key?(:uuid) data << find_pool_by_uuid(filter[:uuid], filter[:include_inactive]) else (client.list_storage_pools + client.list_defined_storage_pools).each do |name| data << find_pool_by_name(name, filter[:include_inactive]) end end data.compact end private def find_pool_by_name name, include_inactive pool_to_attributes(client.lookup_storage_pool_by_name(name), include_inactive) rescue ::Libvirt::RetrieveError nil end def find_pool_by_uuid uuid, include_inactive pool_to_attributes(client.lookup_storage_pool_by_uuid(uuid), include_inactive) rescue ::Libvirt::RetrieveError nil end def pool_to_attributes(pool, include_inactive = nil) return nil unless pool.active? || include_inactive states=[:inactive, :building, :running, :degrated, :inaccessible] { :uuid => pool.uuid, :persistent => pool.persistent?, :autostart => pool.autostart?, :active => pool.active?, :name => pool.name, :allocation => pool.info.allocation, :capacity => pool.info.capacity, :num_of_volumes => pool.active? ? pool.num_of_volumes : nil, :state => states[pool.info.state] } end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/dhcp_leases.rb0000644000004100000410000000213714743305667025215 0ustar www-datawww-datarequire 'socket' module Fog module Libvirt class Compute class Real def dhcp_leases(uuid, mac, flags = 0) client.lookup_network_by_uuid(uuid).dhcp_leases(mac, flags) end end class Mock # Not implemented by the test driver def dhcp_leases(uuid, mac, flags = 0) leases1 = { 'aa:bb:cc:dd:ee:ff' => [ { 'type' => Socket::AF_INET, 'ipaddr' => '1.2.3.4', 'prefix' => 24, 'expirytime' => 5000 }, { 'type' => Socket::AF_INET, 'ipaddr' => '1.2.5.6', 'prefix' => 24, 'expirytime' => 5005 } ] } leases2 = { '99:88:77:66:55:44' => [ { 'type' => Socket::AF_INET, 'ipaddr' => '10.1.1.5', 'prefix' => 24, 'expirytime' => 50 } ] } networks = { # should match the default network from the test connection 'dd8fe884-6c02-601e-7551-cca97df1c5df' => leases1, 'fbd4ac68-cbea-4f95-86ed-22953fd92384' => leases2 } networks.dig(uuid, mac) end end end end end fog-libvirt-0.13.2/lib/fog/libvirt/requests/compute/list_pool_volumes.rb0000644000004100000410000000066614743305667026526 0ustar www-datawww-datamodule Fog module Libvirt class Compute module Shared def list_pool_volumes(uuid) pool = client.lookup_storage_pool_by_uuid uuid pool.list_volumes.map do |volume_name| volume_to_attributes(pool.lookup_volume_by_name(volume_name)) end end end class Real include Shared end class Mock include Shared end end end end fog-libvirt-0.13.2/lib/fog/libvirt/models/0000755000004100000410000000000014743305667020347 5ustar www-datawww-datafog-libvirt-0.13.2/lib/fog/libvirt/models/compute/0000755000004100000410000000000014743305667022023 5ustar www-datawww-datafog-libvirt-0.13.2/lib/fog/libvirt/models/compute/networks.rb0000644000004100000410000000061314743305667024224 0ustar www-datawww-datarequire 'fog/core/collection' require 'fog/libvirt/models/compute/network' module Fog module Libvirt class Compute class Networks < Fog::Collection model Fog::Libvirt::Compute::Network def all(filter={}) load(service.list_networks(filter)) end def get(uuid) self.all(:uuid => uuid).first end end end end end fog-libvirt-0.13.2/lib/fog/libvirt/models/compute/node.rb0000644000004100000410000000114014743305667023271 0ustar www-datawww-datarequire 'fog/core/model' module Fog module Libvirt class Compute class Node < Fog::Model identity :uuid attribute :model attribute :memory attribute :cpus attribute :mhz attribute :nodes attribute :sockets attribute :cores attribute :threads attribute :type attribute :version attribute :uri attribute :node_free_memory attribute :max_vcpus attribute :manufacturer attribute :product attribute :serial attribute :hostname end end end end fog-libvirt-0.13.2/lib/fog/libvirt/models/compute/interface.rb0000644000004100000410000000073414743305667024314 0ustar www-datawww-datarequire 'fog/core/model' module Fog module Libvirt class Compute class Interface < Fog::Model identity :name attribute :mac attribute :active def save raise Fog::Errors::Error.new('Creating a new interface is not yet implemented. Contributions welcome!') end def shutdown service.destroy_interface(mac) end def active? active end end end end end fog-libvirt-0.13.2/lib/fog/libvirt/models/compute/server.rb0000644000004100000410000005013714743305667023664 0ustar www-datawww-datarequire 'fog/compute/models/server' require 'fog/libvirt/models/compute/util/util' require 'fileutils' module Fog module Libvirt class Compute class Server < Fog::Compute::Server include Fog::Libvirt::Util attr_reader :xml identity :id, :aliases => 'uuid' attribute :cpus attribute :cputime attribute :firmware attribute :firmware_features attribute :secure_boot attribute :loader_attributes attribute :os_type attribute :memory_size attribute :max_memory_size attribute :name attribute :arch attribute :persistent attribute :domain_type attribute :uuid attribute :autostart attribute :nics attribute :volumes attribute :active attribute :boot_order attribute :display attribute :cpu attribute :hugepages attribute :guest_agent attribute :video attribute :virtio_rng attribute :state # The following attributes are only needed when creating a new vm #TODO: Add depreciation warning attr_accessor :iso_dir, :iso_file attr_accessor :network_interface_type ,:network_nat_network, :network_bridge_name attr_accessor :volume_format_type, :volume_allocation,:volume_capacity, :volume_name, :volume_pool_name, :volume_template_name, :volume_path attr_accessor :password attr_accessor :user_data # Can be created by passing in :xml => "" # or by providing :template_options => { # :name => "", :cpus => 1, :memory_size => 256 , :volume_template # } def initialize(attributes={} ) @xml = attributes.delete(:xml) verify_boot_order(attributes[:boot_order]) super defaults.merge(attributes) initialize_nics initialize_volumes @user_data = attributes.delete(:user_data) end def new? uuid.nil? end def save raise Fog::Errors::Error.new('Saving an existing server may create a duplicate') unless new? create_or_clone_volume unless xml or @volumes create_user_data_iso if user_data @xml ||= to_xml self.id = (persistent ? service.define_domain(xml) : service.create_domain(xml)).uuid reload rescue => e raise Fog::Errors::Error.new("Error saving the server: #{e}") end def start return true if active? action_status = service.vm_action(uuid, :create) reload action_status end def update_autostart(value) service.update_autostart(uuid, value) end def mac nics&.first&.mac end def disk_path volumes.first.path if volumes and volumes.first end def destroy(options={ :destroy_volumes => false, :flags => ::Libvirt::Domain::UNDEFINE_NVRAM }) poweroff unless stopped? flags = options.fetch(:flags, ::Libvirt::Domain::UNDEFINE_NVRAM) if flags.zero? service.vm_action(uuid, :undefine) else # the test driver doesn't support UNDEFINE_NVRAM if service.uri.driver == 'test' flags ^= ::Libvirt::Domain::UNDEFINE_NVRAM end service.vm_action(uuid, :undefine, flags) end volumes.each { |vol| vol.destroy } if options[:destroy_volumes] true end def reboot action_status = service.vm_action(uuid, :reboot) reload action_status end def poweroff action_status = service.vm_action(uuid, :destroy) reload action_status end def shutdown action_status = service.vm_action(uuid, :shutdown) reload action_status end def resume action_status = service.vm_action(uuid, :resume) reload action_status end def suspend action_status = service.vm_action(uuid, :suspend) reload action_status end def stopped? state == "shutoff" end def ready? state == "running" end #alias methods alias_method :halt, :poweroff alias_method :stop, :shutdown alias_method :active?, :active alias_method :autostart?, :autostart def volumes # lazy loading of volumes @volumes ||= (@volumes_path || []).map{ |path| service.volumes.all(:path => path).first }.compact end def private_ip_address ip_address(:private) end def public_ip_address ip_address(:public) end def ssh(commands) requires :ssh_ip_address, :username ssh_options={} ssh_options[:password] = password unless password.nil? ssh_options[:proxy]= ssh_proxy unless ssh_proxy.nil? super(commands, ssh_options) end def ssh_proxy begin require 'net/ssh/proxy/command' rescue LoadError Fog::Logger.warning("'net/ssh' missing, please install and try again.") exit(1) end # if this is a direct connection, we don't need a proxy to be set. return nil unless connection.uri.ssh_enabled? user_string= service.uri.user ? "-l #{service.uri.user}" : "" Net::SSH::Proxy::Command.new("ssh #{user_string} #{service.uri.host} nc %h %p") end # Transfers a file def scp(local_path, remote_path, upload_options = {}) requires :ssh_ip_address, :username scp_options = {} scp_options[:password] = password unless self.password.nil? scp_options[:key_data] = [private_key] if self.private_key scp_options[:proxy]= ssh_proxy unless self.ssh_proxy.nil? Fog::SCP.new(ssh_ip_address, username, scp_options).upload(local_path, remote_path, upload_options) end # Sets up a new key def setup(credentials = {}) requires :public_key, :ssh_ip_address, :username credentials[:proxy]= ssh_proxy unless ssh_proxy.nil? credentials[:password] = password unless self.password.nil? credentials[:key_data] = [private_key] if self.private_key commands = [ %{mkdir .ssh}, # %{passwd -l #{username}}, #Not sure if we need this here # %{echo "#{Fog::JSON.encode(attributes)}" >> ~/attributes.json} ] if public_key commands << %{echo "#{public_key}" >> ~/.ssh/authorized_keys} end # wait for domain to be ready Timeout::timeout(360) do begin Timeout::timeout(8) do Fog::SSH.new(ssh_ip_address, username, credentials.merge(:timeout => 4)).run('pwd') end rescue Errno::ECONNREFUSED sleep(2) retry rescue Net::SSH::AuthenticationFailed, Timeout::Error retry end end Fog::SSH.new(ssh_ip_address, username, credentials).run(commands) end def update_display attrs = {} service.update_display attrs.merge(:uuid => uuid) reload end # can't use deprecate method, as the value is part of the display hash def vnc_port Fog::Logger.deprecation("#{self.class} => #vnc_port is deprecated, use #display[:port] instead [light_black](#{caller.first})[/]") display[:port] end def generate_config_iso(user_data, &blk) Dir.mktmpdir('config') do |wd| generate_config_iso_in_dir(wd, user_data, &blk) end end def generate_config_iso_in_dir(dir_path, user_data, &blk) FileUtils.touch(File.join(dir_path, "meta-data")) File.open(File.join(dir_path, 'user-data'), 'w') { |f| f.write user_data } isofile = Tempfile.new(['init', '.iso']).path unless system("genisoimage -output #{isofile} -volid cidata -joliet -rock #{File.join(dir_path, 'user-data')} #{File.join(dir_path, 'meta-data')}") raise Fog::Errors::Error.new("Couldn't generate cloud-init iso disk with genisoimage.") end blk.call(isofile) end def create_user_data_iso generate_config_iso(user_data) do |iso| vol = service.volumes.create(:name => cloud_init_volume_name, :capacity => "#{File.size(iso)}b", :allocation => "0G") vol.upload_image(iso) @iso_file = cloud_init_volume_name @iso_dir = File.dirname(vol.path) if vol.path end end def cloud_init_volume_name "#{name}-cloud-init.iso" end # rubocop:disable Metrics def to_xml builder = Nokogiri::XML::Builder.new do |xml| xml.domain(:type => domain_type) do xml.name(name) xml.memory(memory_size) if hugepages xml.memoryBacking do xml.hugepages end end xml.vcpu(cpus) os_tags = {} os_tags[:firmware] = firmware if firmware == 'efi' xml.os(**os_tags) do type = xml.type(os_type, :arch => arch) type[:machine] = "q35" if ["i686", "x86_64"].include?(arch) boot_order.each do |dev| xml.boot(:dev => dev) end loader_attributes&.each do |key, value| xml.loader(key => value) end if firmware == "efi" && firmware_features&.any? xml.firmware do firmware_features.each_pair do |key, value| xml.feature(:name => key, :enabled => value) end end end end xml.features do xml.acpi xml.apic end unless cpu.empty? if cpu.dig(:model, :name) xml.cpu do xml.model(cpu.dig(:model, :name), :fallback => cpu.dig(:model, :fallback) || "allow") end else xml.cpu( :mode => cpu.fetch(:mode, "host-passthrough"), :check => cpu.fetch(:check, "none"), :migratable => cpu.fetch(:migratable, "on") ) end end xml.clock(:offset => "utc") do xml.timer(:name => "rtc", :tickpolicy => "catchup") xml.timer(:name => "pit", :tickpolicy => "delay") xml.timer(:name => "hpet", :present => "no") end xml.devices do ceph_args = read_ceph_args volumes.each_with_index do |volume, index| target_device = "vd#{('a'..'z').to_a[index]}" if ceph_args && volume.pool_name.include?(ceph_args["libvirt_ceph_pool"]) xml.disk(:type => "network", :device => "disk") do xml.driver(:name => "qemu", :type => volume.format_type, :cache => "writeback", :discard => "unmap") xml.source(:protocol => "rbd", :name => volume.path) ceph_args["monitor"]&.split(",")&.each do |monitor| xml.host(:name => monitor, :port => ceph_args["port"]) end xml.auth(:username => ceph_args["auth_username"]) do if ceph_args.key?("auth_uuid") xml.secret(:type => "ceph", :uuid => ceph_args["auth_uuid"]) else xml.secret(:type => "ceph", :usage => ceph_args["auth_usage"]) end end xml.target(:dev => target_device, :bus => ceph_args["bus_type"] == "virtio" ? "virtio" : "scsi") end else is_block = volume.path.start_with?("/dev/") xml.disk(:type => is_block ? "block" : "file", :device => "disk") do xml.driver(:name => "qemu", :type => volume.format_type) if is_block xml.source(:dev => volume.path) else xml.source(:file => volume.path) end xml.target(:dev => target_device, :bus => "virtio") end end end if iso_file xml.disk(:type => "file", :device => "cdrom") do xml.driver(:name => "qemu", :type => "raw") xml.source(:file => "#{iso_dir}/#{iso_file}") xml.target(:dev => "sda", :bus => "scsi") xml.readonly xml.address(:type => "drive", :controller => 0, :bus => 0, :unit => 0) end end nics.each do |nic| xml.interface(:type => nic.type) do xml.mac(:address => nic.mac) if nic.mac if nic.type == "bridge" xml.source(:bridge => nic.bridge) else xml.source(:network => nic.network) end xml.model(:type => nic.model) end end if guest_agent xml.channel(:type => "unix") do xml.target(:type => "virtio", :name => "org.qemu.guest_agent.0") end end xml.rng(:model => "virtio") do xml.backend(virtio_rng[:backend_path], :model => virtio_rng.fetch(:backend_model, "random")) end if arch == "s390x" xml.controller(:type => "scsi", :index => "0", :model => "virtio-scsi") xml.console(:type => "pty") do xml.target(:type => "sclp") end xml.memballoon(:model => "virtio") else xml.serial(:type => "pty") do xml.target(:port => 0) end xml.console(:type => "pty") do xml.target(:port => 0) end xml.input(:type => "tablet", :bus => "usb") xml.input(:type => "mouse", :bus => "ps2") graphics = xml.graphics(:type => display[:type]) if display[:port].empty? graphics.port = display[:port] graphics.autoport = "no" else graphics.port = -1 graphics.autoport = "yes" end graphics.listen = display[:listen] unless display[:listen].empty? graphics.passwd = display[:password] if display[:password] && !display[:password].empty? xml.video do xml.model(video) end end end end end builder.to_xml end # rubocop:enable Metrics private attr_accessor :volumes_path def read_ceph_args(path = "/etc/foreman/ceph.conf") return unless File.file?(path) args = {} File.readlines(path).each do |line| pair = line.strip.split("=") key = pair[0] value = pair[1] args[key] = value end args end # This retrieves the ip address of the mac address using dhcp_leases # It returns an array of public and private ip addresses # Currently only one ip address is returned, but in the future this could be multiple # if the server has multiple network interface def addresses(service_arg=service, options={}) ip_address = nil if (nic = self.nics&.first) net = service.networks.all(:name => nic.network).first # Assume the lease expiring last is the current IP address ip_address = net&.dhcp_leases(nic.mac)&.max_by { |lse| lse["expirytime"] }&.dig("ipaddr") end return { :public => [ip_address], :private => [ip_address] } end # Locale-friendly removal of non-alpha nums DOMAIN_CLEANUP_REGEXP = Regexp.compile('[\W_-]') def ip_address(key) addresses[key]&.first end def initialize_nics if nics nics.map! { |nic| nic.is_a?(Hash) ? service.nics.new(nic) : nic } else self.nics = [service.nics.new({:type => network_interface_type, :bridge => network_bridge_name, :network => network_nat_network})] end end def initialize_volumes if attributes[:volumes] && !attributes[:volumes].empty? @volumes = attributes[:volumes].map { |vol| vol.is_a?(Hash) ? service.volumes.new(vol) : vol } end end def create_or_clone_volume options = {:name => volume_name || default_volume_name} # Check if a disk template was specified if volume_template_name template_volume = service.volumes.all(:name => volume_template_name).first raise Fog::Errors::Error.new("Template #{volume_template_name} not found") unless template_volume begin volume = template_volume.clone("#{options[:name]}") rescue => e raise Fog::Errors::Error.new("Error creating the volume : #{e}") end else # If no template volume was given, let's create our own volume options[:pool_name] = volume_pool_name if volume_pool_name options[:format_type] = volume_format_type if volume_format_type options[:capacity] = volume_capacity if volume_capacity options[:allocation] = volume_allocation if volume_allocation begin volume = service.volumes.create(options) rescue => e raise Fog::Errors::Error.new("Error creating the volume : #{e}") end end @volumes.nil? ? @volumes = [volume] : @volumes << volume end def default_iso_dir "/var/lib/libvirt/images" end def default_volume_name "#{name}.#{volume_format_type || 'img'}" end def defaults { :persistent => true, :cpus => 1, :memory_size => 256 * 1024, :name => randomized_name, :os_type => "hvm", :arch => "x86_64", :domain_type => "kvm", :autostart => false, :iso_dir => default_iso_dir, :network_interface_type => "network", :network_nat_network => "default", :network_bridge_name => "br0", :boot_order => %w[hd cdrom network], :display => default_display, :cpu => {}, :hugepages => false, :guest_agent => true, :video => {:type => "cirrus", :vram => 9216, :heads => 1}, :virtio_rng => {}, :firmware_features => { "secure-boot" => "no" }, } end def verify_boot_order order = [] valid_boot_media = %w[cdrom fd hd network] if order order.each do |b| raise "invalid boot order, possible values are any combination of: #{valid_boot_media.join(', ')}" unless valid_boot_media.include?(b) end end end def default_display {:port => '-1', :listen => '127.0.0.1', :type => 'vnc' } end end end end end fog-libvirt-0.13.2/lib/fog/libvirt/models/compute/volumes.rb0000644000004100000410000000060614743305667024044 0ustar www-datawww-datarequire 'fog/core/collection' require 'fog/libvirt/models/compute/volume' module Fog module Libvirt class Compute class Volumes < Fog::Collection model Fog::Libvirt::Compute::Volume def all(filter = {}) load(service.list_volumes(filter)) end def get(key) self.all(:key => key).first end end end end end fog-libvirt-0.13.2/lib/fog/libvirt/models/compute/nic.rb0000644000004100000410000000225314743305667023123 0ustar www-datawww-datarequire 'fog/core/model' module Fog module Libvirt class Compute class Nic < Fog::Model identity :mac attribute :id attribute :type attribute :network attribute :bridge attribute :model attr_accessor :server TYPES = ["network", "bridge", "user"] def new? mac.nil? end def initialize attributes super defaults.merge(attributes) raise Fog::Errors::Error.new("#{type} is not a supported nic type") if new? && !TYPES.include?(type) end def save raise Fog::Errors::Error.new('Creating a new nic is not yet implemented. Contributions welcome!') #requires :server #service.attach_nic(domain , self) end def destroy raise Fog::Errors::Error.new('Destroying an interface is not yet implemented. Contributions welcome!') #requires :server ##detach the nic #service.detach_nic(domain, mac) end private def defaults { :type => "bridge", :model => "virtio" } end end end end end fog-libvirt-0.13.2/lib/fog/libvirt/models/compute/nodes.rb0000644000004100000410000000054114743305667023460 0ustar www-datawww-datarequire 'fog/core/collection' require 'fog/libvirt/models/compute/node' module Fog module Libvirt class Compute class Nodes < Fog::Collection model Fog::Libvirt::Compute::Node def all(filter={ }) load(service.get_node_info) end def get all.first end end end end end fog-libvirt-0.13.2/lib/fog/libvirt/models/compute/network.rb0000644000004100000410000000172314743305667024044 0ustar www-datawww-datarequire 'fog/core/model' require 'fog/libvirt/models/compute/util/util' module Fog module Libvirt class Compute class Network < Fog::Model include Fog::Libvirt::Util identity :uuid attribute :name attribute :bridge_name attribute :xml def initialize(attributes = {}) super end def dhcp_leases(mac, flags = 0) service.dhcp_leases(uuid, mac, flags) end def save raise Fog::Errors::Error.new('Creating a new network is not yet implemented. Contributions welcome!') end def shutdown service.destroy_network(uuid) end def to_xml builder = Nokogiri::XML::Builder.new do |xml| xml.network do xml.name(name) xml.bridge(:name => bridge_name, :stp => 'on', :delay => '0') end end builder.to_xml end end end end end fog-libvirt-0.13.2/lib/fog/libvirt/models/compute/util/0000755000004100000410000000000014743305667023000 5ustar www-datawww-datafog-libvirt-0.13.2/lib/fog/libvirt/models/compute/util/util.rb0000644000004100000410000000106714743305667024306 0ustar www-datawww-datarequire 'nokogiri' require 'securerandom' module Fog module Libvirt module Util def xml_element(xml, path, attribute=nil) xml = Nokogiri::XML(xml) attribute.nil? ? (xml/path).first.text : (xml/path).first[attribute.to_sym] end def xml_elements(xml, path, attribute=nil) xml = Nokogiri::XML(xml) attribute.nil? ? (xml/path).map : (xml/path).map{|element| element[attribute.to_sym]} end def randomized_name "fog-#{(SecureRandom.random_number*10E14).to_i.round}" end end end end fog-libvirt-0.13.2/lib/fog/libvirt/models/compute/util/uri.rb0000644000004100000410000000473414743305667024134 0ustar www-datawww-datarequire 'uri' require 'cgi' module Fog module Libvirt module Util class URI attr_reader :uri def initialize(uri) @parsed_uri=::URI.parse(uri) @uri=uri return self end # Transport will be part of the scheme # The part after the plus sign # f.i. qemu+ssh def transport scheme=@parsed_uri.scheme return nil if scheme.nil? return scheme.split(/\+/)[1] end def scheme return @parsed_uri.scheme end def driver scheme=@parsed_uri.scheme return nil if scheme.nil? return scheme.split(/\+/).first end def ssh_enabled? if remote? return transport.include?("ssh") else return false end end def remote? return !transport.nil? end def user @parsed_uri.user end def host @parsed_uri.host end def port @parsed_uri.port end def password @parsed_uri.password end def name value("name") end def command value("command") end def socket value("socket") end def keyfile value("keyfile") end def netcat value("netcat") end def no_verify? no_verify=value("no_verify") return false if no_verify.nil? if no_verify.to_s=="0" return false else return true end end def verify? return !no_verify? end def no_tty? no_tty=value("no_tty") return false if no_tty.nil? if no_tty=="0" return false else return true end end def tty? return !no_tty? end def pkipath value("pkipath") end # A libvirt URI allows you to specify extra params # http://libvirt.org/remote.html private def value(name) unless @parsed_uri.query.nil? params=CGI.parse(@parsed_uri.query) if params.key?(name) return params[name].first else return nil end else return nil end end end end end end fog-libvirt-0.13.2/lib/fog/libvirt/models/compute/servers.rb0000644000004100000410000000066514743305667024050 0ustar www-datawww-datarequire 'fog/core/collection' require 'fog/libvirt/models/compute/server' module Fog module Libvirt class Compute class Servers < Fog::Collection model Fog::Libvirt::Compute::Server def all(filter={}) load(service.list_domains(filter)) end def get(uuid) data = service.list_domains(:uuid => uuid) new data.first if data end end end end end fog-libvirt-0.13.2/lib/fog/libvirt/models/compute/nics.rb0000644000004100000410000000033614743305667023306 0ustar www-datawww-datarequire 'fog/core/collection' require 'fog/libvirt/models/compute/nic' module Fog module Libvirt class Compute class Nics < Fog::Collection model Fog::Libvirt::Compute::Nic end end end end fog-libvirt-0.13.2/lib/fog/libvirt/models/compute/interfaces.rb0000644000004100000410000000062314743305667024474 0ustar www-datawww-datarequire 'fog/core/collection' require 'fog/libvirt/models/compute/interface' module Fog module Libvirt class Compute class Interfaces < Fog::Collection model Fog::Libvirt::Compute::Interface def all(filter={}) load(service.list_interfaces(filter)) end def get(name) self.all(:name => name).first end end end end end fog-libvirt-0.13.2/lib/fog/libvirt/models/compute/pool.rb0000644000004100000410000000440114743305667023320 0ustar www-datawww-datarequire 'fog/core/model' module Fog module Libvirt class Compute class Pool < Fog::Model attr_reader :xml identity :uuid attribute :persistent attribute :autostart attribute :active attribute :name attribute :allocation attribute :capacity attribute :num_of_volumes attribute :state def initialize(attributes={} ) # Can be created by passing in XML @xml = attributes.delete(:xml) super(attributes) end def save raise Fog::Errors::Error.new('Creating a new pool requires proper xml') unless xml self.uuid = (persistent ? service.define_pool(xml) : service.create_pool(xml)).uuid reload end # Start the pool = make it active # Performs a libvirt create (= start) def start service.pool_action uuid, :create end # Stop the pool = make it non-active # Performs a libvirt destroy (= stop) def stop service.pool_action uuid, :destroy end # Shuts down the pool def shutdown stop end # Build this storage pool def build service.pool_action uuid, :build end # Destroys the storage pool def destroy # Shutdown pool if active service.pool_action uuid, :destroy if active? # If this is a persistent domain we need to undefine it service.pool_action uuid, :undefine if persistent? end # Is the pool active or not? def active? active end # Will the pool autostart or not? def auto_start? autostart end # Is the pool persistent or not? def persistent? persistent end # Retrieves the volumes of this pool def volumes service.list_pool_volumes uuid end def to_xml builder = Nokogiri::XML::Builder.new do |xml| xml.pool(:type => 'dir') do xml.name(name) xml.target do xml.path(path) end end end builder.to_xml end end end end end fog-libvirt-0.13.2/lib/fog/libvirt/models/compute/pools.rb0000644000004100000410000000060114743305667023501 0ustar www-datawww-datarequire 'fog/core/collection' require 'fog/libvirt/models/compute/pool' module Fog module Libvirt class Compute class Pools < Fog::Collection model Fog::Libvirt::Compute::Pool def all(filter = {}) load(service.list_pools(filter)) end def get(uuid) self.all(:uuid => uuid).first end end end end end fog-libvirt-0.13.2/lib/fog/libvirt/models/compute/README.md0000644000004100000410000000764114743305667023312 0ustar www-datawww-dataThis model implements the connection with a libvirt URI. A libvirt URI can either be local or remote. To learn more on the specific libvirt URI syntax see: - [http://libvirt.org/uri.html](http://libvirt.org/uri.html) - [http://libvirt.org/remote.html#Remote_URI_reference](http://libvirt.org/remote.html#Remote_URI_reference) Only ssh is supported as the transport for remote URI's. TLS is NOT supported, as we can't easily login to the server ## Dependencies - the interaction with libvirt is done through the official libvirt gem called 'ruby-libvirt'. - be aware that there is another libvirt gem called 'libvirt', which is not compatible - If this gem is not installed the models for libvirt will not be available - libvirt needs to be setup so that it can be used - for a remote ssh connection this requires to be member of the libvirt group before you can use the libvirt commands - verify if you can execute virsh command to see if you have correct access ## Libvirt on Macosx - There is a libvirt client for Macosx, available via homebrew - By default this will install things in /usr/local/somewhere - This means that also the default locations of the libvirt-socket are assumed to be in /usr/local - To check the connection you need to override your libvirt socket location in the URI - "qemu+ssh://patrick@myserver/system?socket=/var/run/libvirt/libvirt-sock" ## Ceph RBD volumes To configure Ceph RBD volumes, the file ``/etc/foreman/ceph.conf`` is used. After adding the authentication key to a libvirt secret, it can be configured as follows: ``` monitor=mon001.example.com,mon002.example.com,mon003.example.com port=6789 libvirt_ceph_pool=rbd_pool_name auth_username=libvirt auth_uuid=uuid_of_libvirt_secret bus_type=virtio ``` For more recent versions of libvirt which support using the secret by name (`usage` attribute in the `secret` tag), you can also drop `auth_uuid` and specify `auth_usage` instead. If both are specified, `auth_uuid` will be preferred for maximum compatibility. The `bus_type` can be set to `virtio` or `scsi`. If it is ommited, the default is `scsi`. ## Configuration The URI can be configured in two ways: 1) via the .fog file :default :libvirt_uri: "qemu+ssh://patrick@myserver/system?socket=/var/run/libvirt/libvirt-sock" 2) you can also pass it during creation : f=Fog::Compute.new(:provider => "Libvirt", :libvirt_uri => "qemu+ssh://patrick@myserver/system") ## IP-addresses of guests Libvirt does not provide a way to query guests for Ip-addresses. The way we solve this problem is by installing arpwatch: this watches an interface for new mac-addresses and ip-addresses requested by DHCP We query that logfile for the mac-address and can retrieve the ip-address vi /etc/rsyslog.d/30-arpwatch.conf #:msg, contains, "arpwatch:" -/var/log/arpwatch.log #& ~ if $programname =='arpwatch' then /var/log/arpwatch.log & ~ This log files needs to be readable for the users of libvirt ## SSh-ing into the guests Once we have retrieved the ip-address of the guest we can ssh into it. This works great if the URI is local. But when the URI is remote our machine can't ssh directly into the guest sometimes (due to NAT or firewall issues) Luckily libvirt over ssh requires netcat to be installed on the libvirt server. We use this to proxy our ssh requests to the guest over the ssh connection to the libvirt server. Thanks to the requirement that you need ssh login to work to a libvirt server, we can login and tunnel the ssh to the guest. ## Bridge configuration (slowness) We had noticed that sometimes it takes about 30 seconds before the server gets a DHCP response from the server. In our case it was because the new machine Mac-address was not allowed immediately by the bridge. Adding the flag 'bridge_fd 0' solved that problem. /etc/network/interfaces auto br0 iface br0 inet static address 10.247.4.13 netmask 255.255.255.0 network 10.247.4.0 broadcast 10.247.4.255 bridge_ports eth0.4 bridge_stp on bridge_maxwait 0 bridge_fd 0 fog-libvirt-0.13.2/lib/fog/libvirt/models/compute/volume.rb0000644000004100000410000001074514743305667023666 0ustar www-datawww-datarequire 'fog/core/model' require 'fog/libvirt/models/compute/util/util' module Fog module Libvirt class Compute class Volume < Fog::Model attr_reader :xml include Fog::Libvirt::Util identity :id, :aliases => 'key' attribute :pool_name attribute :key attribute :name attribute :path attribute :capacity attribute :allocation attribute :owner attribute :group attribute :format_type attribute :backing_volume # Can be created by passing in :xml => "" # A volume always belongs to a pool, :pool_name => "" # def initialize(attributes={ }) @xml = attributes.delete(:xml) super(defaults.merge(attributes)) # We need a connection to calculate the pool_name # This is why we do this after super self.pool_name ||= default_pool_name end # Takes a pool and either :xml or other settings def save requires :pool_name raise Fog::Errors::Error.new('Reserving an existing volume may create a duplicate') if key @xml ||= to_xml self.id = service.create_volume(pool_name, xml).key reload end # Destroy a volume def destroy service.volume_action key, :delete end # Wipes a volume , zeroes disk def wipe service.volume_action key, :wipe end # Clones this volume to the name provided def clone(name) new_volume = self.dup new_volume.key = nil new_volume.name = name new_volume.save new_volume.reload end def clone_volume(new_name) requires :pool_name new_volume = self.dup new_volume.key = nil new_volume.name = new_name new_volume.id = service.clone_volume(pool_name, new_volume.to_xml, self.name).key new_volume.reload end def upload_image(file_path) requires :pool_name service.upload_volume(pool_name, name, file_path) end def to_xml builder = Nokogiri::XML::Builder.new do |xml| xml.volume do xml.name(name) allocation_size, allocation_unit = split_size_unit(allocation) xml.allocation(allocation_size, :unit => allocation_unit) capacity_size, capacity_unit = split_size_unit(capacity) xml.capacity(capacity_size, :unit => capacity_unit) xml.target do xml.format(:type => format_type) xml_permissions(xml) end if backing_volume xml.backingStore do xml.path(backing_volume.path) xml.format(:type => backing_volume.format_type) xml_permissions(xml) end end end end builder.to_xml end private def xml_permissions(xml) xml.permissions do xml.owner(owner) if owner xml.group(group) if group xml.mode('0744') xml.label('virt_image_t') end end def image_suffix return "img" if format_type == "raw" format_type end def randominzed_name "#{super}.#{image_suffix}" end # Try to guess the default/first pool of no pool_name was specified def default_pool_name name = "default" return name unless (service.pools.all(:name => name)).empty? # we default to the first pool we find. first_pool = service.pools.first raise Fog::Errors::Error.new('No storage pools are defined') unless first_pool first_pool.name end def defaults { :persistent => true, :format_type => "raw", :name => randomized_name, :capacity => "10G", :allocation => "1G", :owner => nil, :group => nil, } end def split_size_unit(text) if (text.kind_of? String) && (matcher = text.match(/(\d+)(.+)/)) size = matcher[1] unit = matcher[2] else size = text.to_i unit = "G" end [size, unit] end end end end end fog-libvirt-0.13.2/lib/fog/libvirt/version.rb0000644000004100000410000000007514743305667021100 0ustar www-datawww-datamodule Fog module Libvirt VERSION = '0.13.2' end end fog-libvirt-0.13.2/lib/fog/libvirt/compute.rb0000644000004100000410000000717714743305667021101 0ustar www-datawww-datarequire 'fog/libvirt/models/compute/util/util' require 'fog/libvirt/models/compute/util/uri' module Fog module Libvirt class Compute < Fog::Service requires :libvirt_uri recognizes :libvirt_username, :libvirt_password model_path 'fog/libvirt/models/compute' model :server collection :servers model :network collection :networks model :interface collection :interfaces model :volume collection :volumes model :pool collection :pools model :node collection :nodes model :nic collection :nics request_path 'fog/libvirt/requests/compute' request :list_domains request :create_domain request :define_domain request :vm_action request :list_pools request :list_pool_volumes request :define_pool request :pool_action request :list_volumes request :volume_action request :create_volume request :upload_volume request :clone_volume request :list_networks request :destroy_network request :dhcp_leases request :list_interfaces request :destroy_interface request :get_node_info request :update_autostart request :update_display request :libversion module Shared include Fog::Libvirt::Util attr_reader :client attr_reader :uri def initialize(options={}) super() @uri = ::Fog::Libvirt::Util::URI.new(enhance_uri(options[:libvirt_uri])) # libvirt is part of the gem => ruby-libvirt begin require 'libvirt' rescue LoadError => e retry if require('rubygems') raise e.message end begin if options[:libvirt_username] and options[:libvirt_password] @client = ::Libvirt::open_auth(uri.uri, [::Libvirt::CRED_AUTHNAME, ::Libvirt::CRED_PASSPHRASE]) do |cred| case cred['type'] when ::Libvirt::CRED_AUTHNAME options[:libvirt_username] when ::Libvirt::CRED_PASSPHRASE options[:libvirt_password] end end else @client = ::Libvirt::open(uri.uri) end rescue ::Libvirt::ConnectionError raise Fog::Errors::Error.new("Error making a connection to libvirt URI #{uri.uri}:\n#{$!}") end end def terminate @client.close if @client and !@client.closed? end def enhance_uri(uri) require 'cgi' append="" # on macosx, chances are we are using libvirt through homebrew # the client will default to a socket location based on it's own location (/opt) # we conveniently point it to /var/run/libvirt/libvirt-sock # if no socket option has been specified explicitly and # if the socket exists socketpath="/var/run/libvirt/libvirt-sock" if RUBY_PLATFORM =~ /darwin/ && File.exist?(socketpath) querystring=::URI.parse(uri).query if querystring.nil? append="?socket=#{socketpath}" elsif !::CGI.parse(querystring).key?("socket") append="&socket=#{socketpath}" end end uri+append end end class Real include Shared end class Mock include Shared def enhance_uri(uri) uri = 'test:///default' unless ::URI.parse(uri).scheme == 'test' super(uri) end end end end end fog-libvirt-0.13.2/tests/0000755000004100000410000000000014743305667015232 5ustar www-datawww-datafog-libvirt-0.13.2/tests/helper.rb0000644000004100000410000000114014743305667017032 0ustar www-datawww-dataENV['FOG_RC'] = ENV['FOG_RC'] || File.expand_path('../.fog', __FILE__) ENV['FOG_CREDENTIAL'] = ENV['FOG_CREDENTIAL'] || 'default' require 'fog/libvirt' Excon.defaults.merge!(:debug_request => true, :debug_response => true) require File.expand_path(File.join(File.dirname(__FILE__), 'helpers', 'mock_helper')) # This overrides the default 600 seconds timeout during live test runs if Fog.mocking? FOG_TESTING_TIMEOUT = ENV['FOG_TEST_TIMEOUT'] || 2000 Fog.timeout = 2000 Fog::Logger.warning "Setting default fog timeout to #{Fog.timeout} seconds" else FOG_TESTING_TIMEOUT = Fog.timeout end fog-libvirt-0.13.2/tests/libvirt/0000755000004100000410000000000014743305667016705 5ustar www-datawww-datafog-libvirt-0.13.2/tests/libvirt/requests/0000755000004100000410000000000014743305667020560 5ustar www-datawww-datafog-libvirt-0.13.2/tests/libvirt/requests/compute/0000755000004100000410000000000014743305667022234 5ustar www-datawww-datafog-libvirt-0.13.2/tests/libvirt/requests/compute/create_domain_tests.rb0000644000004100000410000000132514743305667026576 0ustar www-datawww-dataShindo.tests("Fog::Compute[:libvirt] | create_domain request", 'libvirt') do compute = Fog::Compute[:libvirt] xml = compute.servers.new( :nics => [{:bridge => "br180"}]).to_xml tests("Create Domain") do response = compute.create_domain(xml) test("should be a kind of Libvirt::Domain") { response.kind_of? Libvirt::Domain} end tests("Fail Creating Domain") do begin response = compute.create_domain(xml) test("should be a kind of Libvirt::Domain") { response.kind_of? Libvirt::Domain} #mock never raise exceptions rescue => e #should raise vm name already exist exception. test("error should be a kind of Libvirt::Error") { e.kind_of? Libvirt::Error} end end end fog-libvirt-0.13.2/tests/libvirt/requests/compute/define_domain_tests.rb0000644000004100000410000000047614743305667026573 0ustar www-datawww-dataShindo.tests("Fog::Compute[:libvirt] | define_domain request", 'libvirt') do compute = Fog::Compute[:libvirt] xml = compute.servers.new().to_xml tests("Define Domain") do response = compute.define_domain(xml) test("should be a kind of Libvirt::Domain") { response.kind_of? Libvirt::Domain} end end fog-libvirt-0.13.2/tests/libvirt/requests/compute/update_display.rb0000644000004100000410000000061314743305667025570 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | update_display request', ['libvirt']) do compute = Fog::Compute[:libvirt] reconfig_target = 'f74d728a-5b62-7e2f-1f84-239aead298ca' display_spec = {:password => 'ssaaa'} tests('The response should') do response = compute.update_display(:uuid => reconfig_target).merge(display_spec) test('should be true').succeeds { response } end end fog-libvirt-0.13.2/tests/libvirt/requests/compute/update_autostart_tests.rb0000644000004100000410000000054414743305667027376 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | update_autostart request', ['libvirt']) do servers = Fog::Compute[:libvirt].servers tests('The response should') do test('should not be empty') { not servers.empty? } server = servers.first tests('should be false').succeeds { server.autostart == false } server.update_autostart(true) end end fog-libvirt-0.13.2/tests/libvirt/requests/compute/list_pools_tests.rb0000644000004100000410000000303214743305667026170 0ustar www-datawww-dataclass FakePool < Fog::Model # Fake pool object to allow exercising the internal parsing of pools # returned by the client queries identity :uuid attribute :persistent attribute :autostart attribute :active attribute :name attribute :num_of_volumes attr_reader :info class FakeInfo < Fog::Model attribute :allocation attribute :capacity attribute :state end def initialize(attributes = {}) @info = FakeInfo.new(attributes.dup.delete(:info)) super(attributes) end def active? active end def autostart? autostart end def persistent? persistent end end Shindo.tests("Fog::Compute[:libvirt] | list_pools request", 'libvirt') do compute = Fog::Compute[:libvirt] tests("Lists Pools") do response = compute.list_pools test("should be an array") { response.kind_of? Array } test("should have two pools") { response.length == 1 } end tests("Handle Inactive Pools") do inactive_pool = { :uuid => 'pool.uuid', :persistent => true, :autostart => true, :active => false, :name => 'inactive_pool1', :info => { :allocation => 123456789, :capacity => 123456789, :state => 2 # running }, :num_of_volumes => 3 } response = compute.send(:pool_to_attributes, FakePool.new(inactive_pool), true) test("should be hash of attributes") { response.kind_of? Hash } response = compute.send(:pool_to_attributes, FakePool.new(inactive_pool)) test("should be nil") { response.nil? } end end fog-libvirt-0.13.2/tests/libvirt/requests/compute/dhcp_leases_tests.rb0000644000004100000410000000112514743305667026254 0ustar www-datawww-dataShindo.tests("Fog::Compute[:libvirt] | dhcp_leases request", 'libvirt') do compute = Fog::Compute[:libvirt] tests("DHCP leases response") do response = compute.dhcp_leases("fbd4ac68-cbea-4f95-86ed-22953fd92384", "99:88:77:66:55:44", 0) test("should be an array") { response.kind_of? Array } test("should have one element") { response.length == 1 } test("should have dict elements") { response[0].kind_of? Hash } ["ipaddr", "prefix", "expirytime", "type"].each { |k| test("should have dict elements with required key #{k}") { !response[0][k].nil? } } end end fog-libvirt-0.13.2/tests/libvirt/compute_tests.rb0000644000004100000410000000134314743305667022131 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt]', ['libvirt']) do compute = Fog::Compute[:libvirt] tests("Compute collections") do %w{ servers interfaces networks nics nodes pools volumes}.each do |collection| test("it should respond to #{collection}") { compute.respond_to? collection } end end tests("Compute requests") do %w{ create_domain create_volume define_domain define_pool destroy_interface destroy_network get_node_info update_autostart list_domains list_interfaces list_networks list_pools list_pool_volumes list_volumes pool_action vm_action volume_action dhcp_leases }.each do |request| test("it should respond to #{request}") { compute.respond_to? request } end end end fog-libvirt-0.13.2/tests/libvirt/models/0000755000004100000410000000000014743305667020170 5ustar www-datawww-datafog-libvirt-0.13.2/tests/libvirt/models/compute/0000755000004100000410000000000014743305667021644 5ustar www-datawww-datafog-libvirt-0.13.2/tests/libvirt/models/compute/pool_tests.rb0000644000004100000410000000166014743305667024367 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | interface model', ['libvirt']) do pools = Fog::Compute[:libvirt].pools pool = pools.last tests('The interface model should') do tests('have the action') do test('reload') { pool.respond_to? 'reload' } end tests('have attributes') do model_attribute_hash = pool.attributes attributes = [ :uuid, :name, :persistent, :active, :autostart, :allocation, :capacity, :num_of_volumes, :state] tests("The interface model should respond to") do attributes.each do |attribute| test("#{attribute}") { pool.respond_to? attribute } end end tests("The attributes hash should have key") do attributes.each do |attribute| test("#{attribute}") { model_attribute_hash.key? attribute } end end end test('be a kind of Fog::Libvirt::Compute::Pool') { pool.kind_of? Fog::Libvirt::Compute::Pool } end end fog-libvirt-0.13.2/tests/libvirt/models/compute/pools_tests.rb0000644000004100000410000000100214743305667024540 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | pools request', ['libvirt']) do pools = Fog::Compute[:libvirt].pools tests('The pools collection') do test('should not be empty') { not pools.empty? } test('should be a kind of Fog::Libvirt::Compute::Pools') { pools.kind_of? Fog::Libvirt::Compute::Pools } tests('should be able to reload itself').succeeds { pools.reload } tests('should be able to get a model') do tests('by instance id').succeeds { pools.get pools.first.uuid } end end end fog-libvirt-0.13.2/tests/libvirt/models/compute/networks_tests.rb0000644000004100000410000000075714743305667025300 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | networks collection', ['libvirt']) do networks = Fog::Compute[:libvirt].networks tests('The networks collection') do test('should be a kind of Fog::Libvirt::Compute::Networks') { networks.kind_of? Fog::Libvirt::Compute::Networks } tests('should be able to reload itself').succeeds { networks.reload } tests('should be able to get a model') do tests('by instance id').succeeds { networks.get networks.first.uuid } end end end fog-libvirt-0.13.2/tests/libvirt/models/compute/volumes_tests.rb0000644000004100000410000000122614743305667025106 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | volumes collection', ['libvirt']) do volumes = Fog::Compute[:libvirt].volumes volumes.create(:name => 'test') tests('The volumes collection') do test('should not be empty') { not volumes.empty? } test('should be a kind of Fog::Libvirt::Compute::Volumes') { volumes.kind_of? Fog::Libvirt::Compute::Volumes } tests('should be able to reload itself').succeeds { volumes.reload } tests('should be able to get a model') do tests('by instance uuid').succeeds { volumes.get volumes.first.id } end test('filtered should be empty') { volumes.all(:name => "does-not-exist").empty? } end end fog-libvirt-0.13.2/tests/libvirt/models/compute/interfaces_tests.rb0000644000004100000410000000107714743305667025543 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | interfaces collection', ['libvirt']) do interfaces = Fog::Compute[:libvirt].interfaces tests('The interfaces collection') do test('should not be empty') { not interfaces.empty? } test('should be a kind of Fog::Libvirt::Compute::Interfaces') { interfaces.kind_of? Fog::Libvirt::Compute::Interfaces } tests('should be able to reload itself').succeeds { interfaces.reload } tests('should be able to get a model') do tests('by instance name').succeeds { interfaces.get interfaces.first.name } end end end fog-libvirt-0.13.2/tests/libvirt/models/compute/interface_tests.rb0000644000004100000410000000162714743305667025361 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | interface model', ['libvirt']) do interfaces = Fog::Compute[:libvirt].interfaces interface = interfaces.last tests('The interface model should') do tests('have the action') do test('reload') { interface.respond_to? 'reload' } end tests('have attributes') do model_attribute_hash = interface.attributes attributes = [ :name, :mac, :active] tests("The interface model should respond to") do attributes.each do |attribute| test("#{attribute}") { interface.respond_to? attribute } end end tests("The attributes hash should have key") do attributes.each do |attribute| test("#{attribute}") { model_attribute_hash.key? attribute } end end end test('be a kind of Fog::Libvirt::Compute::Interface') { interface.kind_of? Fog::Libvirt::Compute::Interface } end end fog-libvirt-0.13.2/tests/libvirt/models/compute/nic_tests.rb0000644000004100000410000000173014743305667024165 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | nic model', ['libvirt']) do server = Fog::Compute[:libvirt].servers.create(:name => Fog::Mock.random_letters(8)) nic = server.nics.first tests('The nic model should') do tests('have the action') do test('reload') { nic.respond_to? 'reload' } end tests('have attributes') do model_attribute_hash = nic.attributes attributes = [ :mac, :model, :type, :network, :bridge] tests("The nic model should respond to") do attributes.each do |attribute| test("#{attribute}") { nic.respond_to? attribute } end end tests("The attributes hash should have key") do attributes.delete(:bridge) attributes.each do |attribute| test("#{attribute}") { model_attribute_hash.key? attribute } end end end test('be a kind of Fog::Libvirt::Compute::Nic') { nic.kind_of? Fog::Libvirt::Compute::Nic } end end fog-libvirt-0.13.2/tests/libvirt/models/compute/volume_tests.rb0000644000004100000410000000336314743305667024727 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | volume model', ['libvirt']) do volume = Fog::Compute[:libvirt].volumes.create(:name => 'fog_test') tests('The volume model should') do tests('have attributes') do model_attribute_hash = volume.attributes attributes = [ :id, :pool_name, :key, :name, :path, :capacity, :allocation, :format_type] tests("The volume model should respond to") do attributes.each do |attribute| test("#{attribute}") { volume.respond_to? attribute } end end tests("The attributes hash should have key") do attributes.each do |attribute| test("#{attribute}") { model_attribute_hash.key? attribute } end end end test('be a kind of Fog::Libvirt::Compute::Volume') { volume.kind_of? Fog::Libvirt::Compute::Volume } end tests('Cloning volumes should') do test('respond to clone_volume') { volume.respond_to? :clone_volume } new_vol = volume.clone_volume('new_vol') # We'd like to test that the :name attr has changed, but it seems that's # not possible, so we can at least check the new_vol xml exists properly test('succeed') { volume.xml == new_vol.xml } end test('to_xml') do test('default') do expected = <<~VOLUME fog_test 1 10 0744 VOLUME volume.to_xml == expected end end end fog-libvirt-0.13.2/tests/libvirt/models/compute/server_tests.rb0000644000004100000410000001064014743305667024722 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | server model', ['libvirt']) do servers = Fog::Compute[:libvirt].servers # Match the mac in dhcp_leases mock nics = Fog.mock? ? [{ :type => 'network', :network => 'default', :mac => 'aa:bb:cc:dd:ee:ff' }] : nil server = servers.create(:name => Fog::Mock.random_letters(8), :nics => nics) tests('The server model should') do tests('have the action') do test('autostart') { server.respond_to? 'autostart' } test('update_autostart') { server.respond_to? 'update_autostart' } test('reload') { server.respond_to? 'reload' } %w{ start stop destroy reboot suspend }.each do |action| test(action) { server.respond_to? action } end %w{ start reboot suspend stop }.each do |action| test("#{action} returns successfully") { begin server.send(action.to_sym) rescue Libvirt::Error #libvirt error is acceptable for the above actions. true end } end end tests('have an ip_address action that') do test('returns the latest IP address lease') { server.public_ip_address() == '1.2.5.6' } end tests('have attributes') do model_attribute_hash = server.attributes attributes = [ :id, :cpus, :cputime, :firmware, :firmware_features, :secure_boot, :loader_attributes, :os_type, :memory_size, :max_memory_size, :name, :arch, :persistent, :domain_type, :uuid, :autostart, :display, :nics, :volumes, :active, :boot_order, :hugepages, :state] tests("The server model should respond to") do attributes.each do |attribute| test("#{attribute}") { server.respond_to? attribute } end end tests("The attributes hash should have key") do attributes.delete(:volumes) attributes.each do |attribute| test("#{attribute}") { model_attribute_hash.key? attribute } end end end test('can destroy') do servers.create(:name => Fog::Mock.random_letters(8)).destroy end test('be a kind of Fog::Libvirt::Compute::Server') { server.kind_of? Fog::Libvirt::Compute::Server } tests("serializes to xml") do test("without firmware") { server.to_xml.include?("") } test("with memory") { server.to_xml.match?(%r{\d+}) } test("with disk of type file") do xml = server.to_xml xml.match?(//) && xml.match?(%r{}) end test("with disk of type block") do server = Fog::Libvirt::Compute::Server.new( { :nics => [], :volumes => [ Fog::Libvirt::Compute::Volume.new({ :path => "/dev/sda", :pool_name => "dummy" }) ] } ) xml = server.to_xml xml.match?(//) && xml.match?(%r{}) end test("with q35 machine type on x86_64") { server.to_xml.match?(%r{hvm}) } end test("with efi firmware") do server = Fog::Libvirt::Compute::Server.new( { :firmware => "efi", :nics => [], :volumes => [] } ) xml = server.to_xml os_firmware = xml.include?('') secure_boot = xml.include?('') loader_attributes = !xml.include?('') os_firmware && secure_boot && loader_attributes end test("with secure boot enabled") do server = Fog::Libvirt::Compute::Server.new( { :firmware => "efi", :firmware_features => { "secure-boot" => "yes", "enrolled-keys" => "yes" }, :loader_attributes => { "secure" => "yes" }, :nics => [], :volumes => [] } ) xml = server.to_xml os_firmware = xml.include?('') secure_boot = xml.include?('') enrolled_keys = xml.include?('') loader_attributes = xml.include?('') os_firmware && secure_boot && enrolled_keys && loader_attributes end end end fog-libvirt-0.13.2/tests/libvirt/models/compute/network_tests.rb0000644000004100000410000000256514743305667025114 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | network model', ['libvirt']) do networks = Fog::Compute[:libvirt].networks network = networks.first tests('The network model should') do tests('have the action') do test('reload') { network.respond_to? 'reload' } test('dhcp_leases') { network.respond_to? 'dhcp_leases' } end tests('have a dhcp_leases action that') do test('returns an array') { network.dhcp_leases('aa:bb:cc:dd:ee:ff', 0).kind_of? Array } end tests('have attributes') do model_attribute_hash = network.attributes attributes = [ :name, :uuid, :bridge_name] tests("The network model should respond to") do attributes.each do |attribute| test("#{attribute}") { network.respond_to? attribute } end end tests("The attributes hash should have key") do attributes.each do |attribute| test("#{attribute}") { model_attribute_hash.key? attribute } end end end test('be a kind of Fog::Libvirt::Compute::Network') { network.kind_of? Fog::Libvirt::Compute::Network } end tests("to_xml") do test("default") do expected = <<~NETWORK default NETWORK network.to_xml == expected end end end fog-libvirt-0.13.2/tests/libvirt/models/compute/nics_tests.rb0000644000004100000410000000043514743305667024351 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | nics collection', ['libvirt']) do nics = Fog::Compute[:libvirt].servers.first.nics tests('The nics collection') do test('should not be empty') { not nics.empty? } test('should be a kind of Array') { nics.kind_of? Array } end end fog-libvirt-0.13.2/tests/libvirt/models/compute/servers_tests.rb0000644000004100000410000000103414743305667025102 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | servers collection', ['libvirt']) do servers = Fog::Compute[:libvirt].servers tests('The servers collection') do test('should not be empty') { not servers.empty? } test('should be a kind of Fog::Libvirt::Compute::Servers') { servers.kind_of? Fog::Libvirt::Compute::Servers } tests('should be able to reload itself').succeeds { servers.reload } tests('should be able to get a model') do tests('by instance uuid').succeeds { servers.get servers.first.id } end end end fog-libvirt-0.13.2/tests/helpers/0000755000004100000410000000000014743305667016674 5ustar www-datawww-datafog-libvirt-0.13.2/tests/helpers/formats_helper_tests.rb0000644000004100000410000000704614743305667023464 0ustar www-datawww-dataShindo.tests('test_helper', 'meta') do tests('comparing welcome data against schema') do data = { :welcome => 'Hello' } data_matches_schema(:welcome => String) { data } end tests('#data_matches_schema') do tests('when value matches schema expectation') do data_matches_schema('key' => String) { { 'key' => 'Value' } } end tests('when values within an array all match schema expectation') do data_matches_schema('key' => [Integer]) { { 'key' => [1, 2] } } end tests('when nested values match schema expectation') do data_matches_schema('key' => { :nested_key => String }) { { 'key' => { :nested_key => 'Value' } } } end tests('when collection of values all match schema expectation') do data_matches_schema([{ 'key' => String }]) { [{ 'key' => 'Value' }, { 'key' => 'Value' }] } end tests('when collection is empty although schema covers optional members') do data_matches_schema([{ 'key' => String }], :allow_optional_rules => true) { [] } end tests('when additional keys are passed and not strict') do data_matches_schema({ 'key' => String }, :allow_extra_keys => true) { { 'key' => 'Value', :extra => 'Bonus' } } end tests('when value is nil and schema expects NilClass') do data_matches_schema('key' => NilClass) { { 'key' => nil } } end tests('when value and schema match as hashes') do data_matches_schema({}) { {} } end tests('when value and schema match as arrays') do data_matches_schema([]) { [] } end tests('when value is a Time') do data_matches_schema('time' => Time) { { 'time' => Time.now } } end tests('when key is missing but value should be NilClass (#1477)') do data_matches_schema({ 'key' => NilClass }, :allow_optional_rules => true) { {} } end tests('when key is missing but value is nullable (#1477)') do data_matches_schema({ 'key' => Fog::Nullable::String }, :allow_optional_rules => true) { {} } end end tests('#formats backwards compatible changes') do tests('when value matches schema expectation') do formats('key' => String) { { 'key' => 'Value' } } end tests('when values within an array all match schema expectation') do formats('key' => [Integer]) { { 'key' => [1, 2] } } end tests('when nested values match schema expectation') do formats('key' => { :nested_key => String }) { { 'key' => { :nested_key => 'Value' } } } end tests('when collection of values all match schema expectation') do formats([{ 'key' => String }]) { [{ 'key' => 'Value' }, { 'key' => 'Value' }] } end tests('when collection is empty although schema covers optional members') do formats([{ 'key' => String }]) { [] } end tests('when additional keys are passed and not strict') do formats({ 'key' => String }, false) { { 'key' => 'Value', :extra => 'Bonus' } } end tests('when value is nil and schema expects NilClass') do formats('key' => NilClass) { { 'key' => nil } } end tests('when value and schema match as hashes') do formats({}) { {} } end tests('when value and schema match as arrays') do formats([]) { [] } end tests('when value is a Time') do formats('time' => Time) { { 'time' => Time.now } } end tests('when key is missing but value should be NilClass (#1477)') do formats('key' => NilClass) { {} } end tests('when key is missing but value is nullable (#1477)') do formats('key' => Fog::Nullable::String) { {} } end end end fog-libvirt-0.13.2/tests/helpers/succeeds_helper.rb0000644000004100000410000000023214743305667022353 0ustar www-datawww-datamodule Shindo class Tests def succeeds(&block) test('succeeds') do !instance_eval(&Proc.new(&block)).nil? end end end end fog-libvirt-0.13.2/tests/helpers/mock_helper.rb0000644000004100000410000000044014743305667021507 0ustar www-datawww-data# Use so you can run in mock mode from the command line # # FOG_MOCK=true fog if ENV["FOG_MOCK"] == "true" Fog.mock! end # if in mocked mode, fill in some fake credentials for us if Fog.mock? Fog.credentials = { :libvirt_uri => 'test:///default', }.merge(Fog.credentials) end fog-libvirt-0.13.2/tests/helpers/formats_helper.rb0000644000004100000410000000701014743305667022231 0ustar www-datawww-data# frozen_string_literal: true require 'fog/schema/data_validator' # format related hackery # allows both true.is_a?(Fog::Boolean) and false.is_a?(Fog::Boolean) # allows both nil.is_a?(Fog::Nullable::String) and ''.is_a?(Fog::Nullable::String) module Fog module Boolean; end module Nullable module Boolean; end module Integer; end module String; end module Time; end module Float; end module Hash; end module Array; end end end [FalseClass, TrueClass].each { |klass| klass.send(:include, Fog::Boolean) } [FalseClass, TrueClass, NilClass, Fog::Boolean].each { |klass| klass.send(:include, Fog::Nullable::Boolean) } [NilClass, String].each { |klass| klass.send(:include, Fog::Nullable::String) } [NilClass, Time].each { |klass| klass.send(:include, Fog::Nullable::Time) } [Integer, NilClass].each { |klass| klass.send(:include, Fog::Nullable::Integer) } [Float, NilClass].each { |klass| klass.send(:include, Fog::Nullable::Float) } [Hash, NilClass].each { |klass| klass.send(:include, Fog::Nullable::Hash) } [Array, NilClass].each { |klass| klass.send(:include, Fog::Nullable::Array) } module Shindo # Generates a Shindo test that compares a hash schema to the result # of the passed in block returning true if they match. # # The schema that is passed in is a Hash or Array of hashes that # have Classes in place of values. When checking the schema the # value should match the Class. # # Strict mode will fail if the data has additional keys. Setting # +strict+ to +false+ will allow additional keys to appear. # # @param [Hash] schema A Hash schema # @param [Hash] options Options to change validation rules # @option options [Boolean] :allow_extra_keys # If +true+ does not fail when keys are in the data that are # not specified in the schema. This allows new values to # appear in API output without breaking the check. # @option options [Boolean] :allow_optional_rules # If +true+ does not fail if extra keys are in the schema # that do not match the data. Not recommended! # @yield Data to check with schema # # @example Using in a test # Shindo.tests("comparing welcome data against schema") do # data = {:welcome => "Hello" } # data_matches_schema(:welcome => String) { data } # end # # comparing welcome data against schema # + data matches schema # # @example Example schema # { # "id" => String, # "ram" => Integer, # "disks" => [ # { # "size" => Float # } # ], # "dns_name" => Fog::Nullable::String, # "active" => Fog::Boolean, # "created" => DateTime # } # # @return [Boolean] class Tests def data_matches_schema(schema, options = {}) test('data matches schema') do validator = Fog::Schema::DataValidator.new valid = validator.validate(yield, schema, options) @message = validator.message unless valid valid end end # @deprecated #formats is deprecated. Use #data_matches_schema instead def formats(format, strict = true) test('has proper format') do if strict options = { :allow_extra_keys => false, :allow_optional_rules => true } else options = { :allow_extra_keys => true, :allow_optional_rules => true } end validator = Fog::Schema::DataValidator.new valid = validator.validate(yield, format, options) @message = validator.message unless valid valid end end end end fog-libvirt-0.13.2/Rakefile0000644000004100000410000000542014743305667015536 0ustar www-datawww-datarequire 'bundler/setup' require 'rake/testtask' require 'rubygems' require 'rubygems/package_task' require 'yard' require File.dirname(__FILE__) + '/lib/fog/libvirt' ############################################################################# # # Helper functions # ############################################################################# def name @name ||= Dir['*.gemspec'].first.split('.').first end def version Fog::Libvirt::VERSION end def gemspec_file "#{name}.gemspec" end def gem_file "#{name}-#{version}.gem" end ############################################################################# # # Standard tasks # ############################################################################# GEM_NAME = "#{name}" task :default => [:test, :minitest] Rake::TestTask.new(:minitest) do |t| t.libs << '.' t.libs << 'lib' t.libs << 'minitests' t.test_files = Dir.glob('minitests/**/*_test.rb') t.verbose = true end desc 'Run tests' task :test do mock = ENV['FOG_MOCK'] || 'true' sh("export FOG_MOCK=#{mock} && bundle exec shindont tests") end desc 'Run mocked tests' task :mock do sh("export FOG_MOCK=true && bundle exec shindont tests") end desc 'Run live tests' task :live do sh("export FOG_MOCK=false && bundle exec shindont tests") end desc "Open an irb session preloaded with this library" task :console do sh "irb -rubygems -r ./lib/fog/libvirt.rb" end ############################################################################# # # Packaging tasks # ############################################################################# task :release => ["release:prepare", "release:publish"] namespace :release do task :preflight do unless `git branch` =~ /^\* master$/ puts "You must be on the master branch to release!" exit! end if `git tag` =~ /^\* v#{version}$/ puts "Tag v#{version} already exists!" exit! end end task :prepare => :preflight do Rake::Task[:build].invoke sh "gem install pkg/#{name}-#{version}.gem" Rake::Task[:git_mark_release].invoke end task :publish do Rake::Task[:git_push_release].invoke Rake::Task[:gem_push].invoke end end task :git_mark_release do sh "git commit --allow-empty -a -m 'Release #{version}'" sh "git tag v#{version}" end task :git_push_release do sh "git push origin master" sh "git push origin v#{version}" end task :gem_push do sh "gem push pkg/#{name}-#{version}.gem" end desc "Build #{name}-#{version}.gem" task :build do sh "mkdir -p pkg" sh "gem build #{gemspec_file}" sh "mv #{gem_file} pkg" end task :gem => :build # Include Yard tasks for rake yard YARDOC_LOCATION = "doc" YARD::Rake::YardocTask.new do |t| t.files = ['lib/**/*.rb', "README"] t.options = ["--output-dir", YARDOC_LOCATION, "--title", "#{name} #{version}"] end fog-libvirt-0.13.2/LICENSE.md0000644000004100000410000000217114743305667015475 0ustar www-datawww-dataThe MIT License (MIT) Copyright (c) 2009-2014 [CONTRIBUTORS.md](https://github.com/fog/fog/blob/master/CONTRIBUTORS.md) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. fog-libvirt-0.13.2/Gemfile0000644000004100000410000000031514743305667015362 0ustar www-datawww-datasource "https://rubygems.org" group :development, :test do # This is here because gemspec doesn't support require: false gem "netrc", :require => false gem "octokit", :require => false end gemspec fog-libvirt-0.13.2/CONTRIBUTORS.md0000644000004100000410000000164714743305667016357 0ustar www-datawww-data* Amos Benari * brookemckim * Carl Caum * Carlos Sanchez * David Wittman * Dominic Cleal * Greg Sutcliffe * James Herdman * Josef Strzibny * Kevin Menard * Konstantin Haase * Kyle Rames * Lance Ivy * Ohad Levy * Patrick Debois * Paul Thornthwaite * Romain Vrignaud * Ryan Davies * Sergio Rubio * Shlomi Zadok * Steve Smith * Vincent Demeester * Wesley Beary * Tomer Brisker fog-libvirt-0.13.2/fog-libvirt.gemspec0000644000004100000410000000325214743305667017663 0ustar www-datawww-datalib = File.expand_path("../lib", __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require "fog/libvirt/version" Gem::Specification.new do |s| s.specification_version = 2 if s.respond_to? :specification_version= s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.name = "fog-libvirt" s.version = Fog::Libvirt::VERSION s.summary = "Module for the 'fog' gem to support libvirt" s.description = "This library can be used as a module for 'fog' or as standalone libvirt provider." s.authors = ["geemus (Wesley Beary)"] s.email = "geemus@gmail.com" s.homepage = "http://github.com/fog/fog-libvirt" s.license = "MIT" s.require_paths = %w[lib] s.rdoc_options = ["--charset=UTF-8"] s.extra_rdoc_files = %w[README.md] s.required_ruby_version = '>= 2.7' s.add_dependency("fog-core", ">= 1.27.4") s.add_dependency("fog-json") s.add_dependency("fog-xml", "~> 0.1.1") s.add_dependency("json") s.add_dependency('ruby-libvirt','>= 0.7.0') s.add_development_dependency("minitest", "~> 5.0") s.add_development_dependency("mocha", ">= 1.15", "< 3") s.add_development_dependency("net-ssh") s.add_development_dependency("pry") s.add_development_dependency("rake") s.add_development_dependency("rubocop") s.add_development_dependency("shindo", "~> 0.3.4") s.add_development_dependency("simplecov") s.add_development_dependency("yard") # Let's not ship dot files and gemfiles git_files = `git ls-files`.split("\n") s.files = git_files.delete_if{ |f| f =~ /^\..*/ || f =~ /^gemfiles\/*/ } s.test_files = `git ls-files -- {spec,tests}/*`.split("\n") end fog-libvirt-0.13.2/README.md0000644000004100000410000000467314743305667015361 0ustar www-datawww-data# Fog::Libvirt fog-libvirt is a libvirt provider for [fog](https://github.com/fog/fog). [![Build Status](https://github.com/fog/fog-libvirt/actions/workflows/ruby.yml/badge.svg)](https://github.com/fog/fog-libvirt/actions/workflows/ruby.yml) [![Dependency Status](https://gemnasium.com/fog/fog.png)](https://gemnasium.com/fog/fog-libvirt) [![Code Climate](https://codeclimate.com/github/fog/fog.png)](https://codeclimate.com/github/fog/fog-libvirt) [![Gem Version](https://fury-badge.herokuapp.com/rb/fog.png)](http://badge.fury.io/rb/fog-libvirt) [![Gittip](http://img.shields.io/gittip/geemus.png)](https://www.gittip.com/geemus/) ## Installation fog-libvirt can be used as a module for fog or installed separately as: ``` $ sudo gem install fog-libvirt ``` ## Usage Example REPL session: ``` >> require "fog/libvirt" => true >> compute = Fog::Compute.new(provider: :libvirt, libvirt_uri: "qemu:///session") => #, @uri="qemu:///session"... >> server = compute.servers.create(name: "test") => ], volumes=[ ], active=false, boot_order=["hd", "cdrom", "network"], display={:type=>"vnc", :port=>"-1", :listen=>"127.0.0.1"}, cpu={}, hugepages=false, guest_agent=true, virtio_rng={}, state="shutoff" > ``` See [README.md](https://github.com/fog/fog-libvirt/blob/master/lib/fog/libvirt/models/compute/README.md). ## Contributing Please refer to [CONTRIBUTING.md](https://github.com/fog/fog/blob/master/CONTRIBUTING.md). ## License Please refer to [LICENSE.md](https://github.com/fog/fog-libvirt/blob/master/LICENSE.md).