fog-libvirt-0.8.0/0000755000004100000410000000000014002531725013772 5ustar www-datawww-datafog-libvirt-0.8.0/README.md0000644000004100000410000000203314002531725015247 0ustar www-datawww-data# Fog::Libvirt fog-libvirt is a libvirt provider for [fog](https://github.com/fog/fog). [![Build Status](https://secure.travis-ci.org/fog/fog.png?branch=master)](http://travis-ci.org/fog/fog-libvirt) [![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 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). fog-libvirt-0.8.0/tests/0000755000004100000410000000000014002531725015134 5ustar www-datawww-datafog-libvirt-0.8.0/tests/helper.rb0000644000004100000410000000114014002531725016734 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.8.0/tests/helpers/0000755000004100000410000000000014002531725016576 5ustar www-datawww-datafog-libvirt-0.8.0/tests/helpers/formats_helper_tests.rb0000644000004100000410000000702014002531725023356 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.8.0/tests/helpers/mock_helper.rb0000644000004100000410000000047314002531725021417 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 => 'qemu://libvirt/system', }.merge(Fog.credentials) end fog-libvirt-0.8.0/tests/helpers/formats_helper.rb0000644000004100000410000000705614002531725022145 0ustar www-datawww-datarequire "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 class Tests # 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] 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.8.0/tests/helpers/succeeds_helper.rb0000644000004100000410000000020614002531725022256 0ustar www-datawww-datamodule Shindo class Tests def succeeds test('succeeds') do !!instance_eval(&Proc.new) end end end end fog-libvirt-0.8.0/tests/libvirt/0000755000004100000410000000000014002531725016607 5ustar www-datawww-datafog-libvirt-0.8.0/tests/libvirt/requests/0000755000004100000410000000000014002531725020462 5ustar www-datawww-datafog-libvirt-0.8.0/tests/libvirt/requests/compute/0000755000004100000410000000000014002531725022136 5ustar www-datawww-datafog-libvirt-0.8.0/tests/libvirt/requests/compute/create_domain_tests.rb0000644000004100000410000000132514002531725026500 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.8.0/tests/libvirt/requests/compute/define_domain_tests.rb0000644000004100000410000000047614002531725026475 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.8.0/tests/libvirt/requests/compute/update_display.rb0000644000004100000410000000061314002531725025472 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.8.0/tests/libvirt/requests/compute/update_autostart_tests.rb0000644000004100000410000000054414002531725027300 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.8.0/tests/libvirt/requests/compute/dhcp_leases_tests.rb0000644000004100000410000000112514002531725026156 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.8.0/tests/libvirt/compute_tests.rb0000644000004100000410000000134314002531725022033 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.8.0/tests/libvirt/models/0000755000004100000410000000000014002531725020072 5ustar www-datawww-datafog-libvirt-0.8.0/tests/libvirt/models/compute/0000755000004100000410000000000014002531725021546 5ustar www-datawww-datafog-libvirt-0.8.0/tests/libvirt/models/compute/pool_tests.rb0000644000004100000410000000166014002531725024271 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.8.0/tests/libvirt/models/compute/pools_tests.rb0000644000004100000410000000102014002531725024442 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.8.0/tests/libvirt/models/compute/networks_tests.rb0000644000004100000410000000075714002531725025202 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.8.0/tests/libvirt/models/compute/server_tests.rb0000644000004100000410000000367514002531725024636 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | server model', ['libvirt']) do servers = Fog::Compute[:libvirt].servers server = servers.all.select{|v| v.name =~ /^fog/}.last 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 destroy}.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, :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('be a kind of Fog::Libvirt::Compute::Server') { server.kind_of? Fog::Libvirt::Compute::Server } end end fog-libvirt-0.8.0/tests/libvirt/models/compute/network_tests.rb0000644000004100000410000000213014002531725025002 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | network model', ['libvirt']) do networks = Fog::Compute[:libvirt].networks network = networks.last 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('99:88:77:66:55:44', 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 end fog-libvirt-0.8.0/tests/libvirt/models/compute/volume_tests.rb0000644000004100000410000000241214002531725024623 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | volume model', ['libvirt']) do volume = Fog::Compute[:libvirt].servers.all.select{|v| v.name !~ /^fog/}.first.volumes.first 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 end fog-libvirt-0.8.0/tests/libvirt/models/compute/nics_tests.rb0000644000004100000410000000043514002531725024253 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.8.0/tests/libvirt/models/compute/servers_tests.rb0000644000004100000410000000103414002531725025004 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.8.0/tests/libvirt/models/compute/volumes_tests.rb0000644000004100000410000000116314002531725025010 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | volumes collection', ['libvirt']) do volumes = Fog::Compute[:libvirt].volumes 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.8.0/tests/libvirt/models/compute/interface_tests.rb0000644000004100000410000000162714002531725025263 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.8.0/tests/libvirt/models/compute/nic_tests.rb0000644000004100000410000000170014002531725024064 0ustar www-datawww-dataShindo.tests('Fog::Compute[:libvirt] | nic model', ['libvirt']) do nic = Fog::Compute[:libvirt].servers.all.select{|v| v.name =~ /^fog/}.first.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.8.0/tests/libvirt/models/compute/interfaces_tests.rb0000644000004100000410000000107714002531725025445 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.8.0/LICENSE.md0000644000004100000410000000217114002531725015377 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.8.0/minitests/0000755000004100000410000000000014002531725016011 5ustar www-datawww-datafog-libvirt-0.8.0/minitests/server/0000755000004100000410000000000014002531725017317 5ustar www-datawww-datafog-libvirt-0.8.0/minitests/server/server_test.rb0000644000004100000410000000367114002531725022220 0ustar www-datawww-datarequire 'test_helper' class ServerTest < Minitest::Test def setup @compute = Fog::Compute[:libvirt] @server = @compute.servers.new(:name => "test") end def test_addresses_calls_compat_version_for_no_dhcp_leases_support network = Libvirt::Network.new @compute.expects(:networks).returns([network]) network.expects(:dhcp_leases).raises(NoMethodError) @server.expects(:addresses_ip_command).returns(true) @server.send(:addresses) end def test_addresses_calls_compat_version_for_older_libvirt network = Libvirt::Network.new @compute.expects(:libversion).returns(1002007) @compute.expects(:networks).returns([network]) network.expects(:dhcp_leases).returns(true) @server.expects(:addresses_ip_command).returns(true) @server.send(:addresses) end def test_addresses_calls_compat_version_for_newer_libvirt network = Libvirt::Network.new @compute.expects(:libversion).returns(1002008) @compute.expects(:networks).returns([network]) network.expects(:dhcp_leases).returns(true) @server.expects(:addresses_dhcp).returns(true) @server.send(:addresses) end def test_ssh_ip_command_success fog_ssh = MiniTest::Mock.new result = MiniTest::Mock.new result.expect(:status, 0) result.expect(:stdout, "any_ip") fog_ssh.expect(:run, [result], [String]) uri = ::Fog::Libvirt::Util::URI.new('qemu+ssh://localhost:22?keyfile=nofile') Fog::SSH.stub(:new, fog_ssh) do @server.send(:ssh_ip_command, "test command", uri) end fog_ssh.verify end def test_local_ip_command_success proc_info = MiniTest::Mock.new proc_info.expect(:each_line, "127.0.0.1") proc_info.expect(:pid, 0) status = MiniTest::Mock.new status.expect(:exitstatus, 0) Process.stubs(:waitpid2).returns([0, status]) IO.stub(:popen, true, proc_info) do @server.send(:local_ip_command, "test command") end proc_info.verify status.verify end end fog-libvirt-0.8.0/minitests/server/user_data_iso_test.rb0000644000004100000410000000514714002531725023533 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 iso_path = "iso_file_path" @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 iso_path = "iso_file_path" @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 iso_path = "iso_file_path" @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.8.0/minitests/test_helper.rb0000644000004100000410000000065714002531725020664 0ustar www-datawww-datarequire 'minitest/autorun' require 'mocha/mini_test' 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 => 'qemu://libvirt/system', }.merge(Fog.credentials) fog-libvirt-0.8.0/CONTRIBUTORS.md0000644000004100000410000000164714002531725016261 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.8.0/Rakefile0000644000004100000410000000542014002531725015440 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.8.0/lib/0000755000004100000410000000000014002531725014540 5ustar www-datawww-datafog-libvirt-0.8.0/lib/fog/0000755000004100000410000000000014002531725015313 5ustar www-datawww-datafog-libvirt-0.8.0/lib/fog/bin/0000755000004100000410000000000014002531725016063 5ustar www-datawww-datafog-libvirt-0.8.0/lib/fog/bin/libvirt.rb0000644000004100000410000000302314002531725020061 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.8.0/lib/fog/libvirt.rb0000644000004100000410000000045714002531725017321 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.8.0/lib/fog/libvirt/0000755000004100000410000000000014002531725016766 5ustar www-datawww-datafog-libvirt-0.8.0/lib/fog/libvirt/version.rb0000644000004100000410000000007414002531725021001 0ustar www-datawww-datamodule Fog module Libvirt VERSION = '0.8.0' end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/0000755000004100000410000000000014002531725020641 5ustar www-datawww-datafog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/0000755000004100000410000000000014002531725022315 5ustar www-datawww-datafog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/destroy_network.rb0000644000004100000410000000043014002531725026101 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real def destroy_network(uuid) client.lookup_network_by_uuid(uuid).destroy end end class Mock def destroy_network(uuid) true end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/list_networks.rb0000644000004100000410000000327414002531725025557 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real 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 Mock def list_networks(filters={ }) [ { :uuid => 'a29146ea-39b2-412d-8f53-239eef117a32', :name => 'net1', :bridge_name => 'virbr0' }, { :uuid => 'fbd4ac68-cbea-4f95-86ed-22953fd92384', :name => 'net2', :bridge_name => 'virbr1' } ] end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/list_domains.rb0000644000004100000410000001104414002531725025327 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real 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 end module Shared 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 vols_by_file = xml_elements(xml, "domain/devices/disk/source", "file") vols_by_name = xml_elements(xml, "domain/devices/disk/source", "name") vols = [] vols_by_file.zip(vols_by_name).each do |by_file,by_name| vols.push(by_file.nil? ? by_name : by_file) end vols end def boot_order xml xml_elements(xml, "domain/os/boot", "dev") 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) 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] } 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 Mock def list_domains(filter = { }) dom1 = mock_domain 'fog-dom1' dom2 = mock_domain 'fog-dom2' dom3 = mock_domain 'a-fog-dom3' [dom1, dom2, dom3] end def mock_domain name xml = read_xml 'domain.xml' { :id => "dom.uuid", :uuid => "dom.uuid", :name => name, :max_memory_size => 8, :cputime => 7, :memory_size => 6, :cpus => 5, :autostart => false, :os_type => "RHEL6", :active => false, :vnc_port => 5910, :boot_order => boot_order(xml), :nics => domain_interfaces(xml), :volumes_path => domain_volumes(xml), :state => 'shutoff' } end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/create_domain.rb0000644000004100000410000000042714002531725025437 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real def create_domain(xml) client.create_domain_xml(xml) end end class Mock def create_domain(xml) ::Libvirt::Domain.new() end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/vm_action.rb0000644000004100000410000000051114002531725024616 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real def vm_action(uuid, action) domain = client.lookup_domain_by_uuid(uuid) domain.send(action) true end end class Mock def vm_action(uuid, action) true end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/clone_volume.rb0000644000004100000410000000076214002531725025336 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real 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 Mock def clone_volume(pool_name, xml, name) Fog::Libvirt::Compute::Volume.new({:pool_name => pool_name, :xml => xml}) end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/list_pool_volumes.rb0000644000004100000410000000063114002531725026420 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real 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 Mock def list_pool_volumes(uuid) end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/volume_action.rb0000644000004100000410000000050514002531725025506 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real def volume_action(key, action, options={}) get_volume({:key => key}, true).send(action) true end end class Mock def volume_action(action, options={}) true end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/define_pool.rb0000644000004100000410000000036714002531725025133 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real def define_pool(xml) client.define_storage_pool_xml(xml) end end class Mock def define_pool(xml) end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/update_display.rb0000644000004100000410000000233614002531725025655 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real 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 Mock def update_display(options = { }) raise ArgumentError, "uuid is a required parameter" unless options.key? :uuid true end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/create_volume.rb0000644000004100000410000000045714002531725025502 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real def create_volume(pool_name, xml) client.lookup_storage_pool_by_name(pool_name).create_vol_xml(xml) end end class Mock def create_volume(pool_name, xml) end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/list_pools.rb0000644000004100000410000000405314002531725025033 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real def list_pools(filter = { }) data=[] if filter.key?(:name) data << find_pool_by_name(filter[:name]) elsif filter.key?(:uuid) data << find_pool_by_uuid(filter[:uuid]) else (client.list_storage_pools + client.list_defined_storage_pools).each do |name| data << find_pool_by_name(name) end end data.compact end private def pool_to_attributes(pool) 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.num_of_volumes, :state => states[pool.info.state] } end def find_pool_by_name name pool_to_attributes(client.lookup_storage_pool_by_name(name)) rescue ::Libvirt::RetrieveError nil end def find_pool_by_uuid uuid pool_to_attributes(client.lookup_storage_pool_by_uuid(uuid)) rescue ::Libvirt::RetrieveError nil end end class Mock def list_pools(filter = { }) pool1 = mock_pool 'pool1' pool2 = mock_pool 'pool1' [pool1, pool2] end def mock_pool name { :uuid => 'pool.uuid', :persistent => true, :autostart => true, :active => true, :name => name, :allocation => 123456789, :capacity => 123456789, :num_of_volumes => 3, :state => :running } end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/list_volumes.rb0000644000004100000410000000625314002531725025375 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real 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 Mock def list_volumes(filters={ }) vol1 = mock_volume 'vol1' vol2 = mock_volume 'vol2' vols = [vol1, vol2] if filters.keys.empty? return vols end key = filters.keys.first vols.select { |v| v[key] == filters[key] } end def mock_volume name { :pool_name => 'vol.pool.name', :key => "vol.#{name}", # needs to match id :id => "vol.#{name}", :path => "path/to/disk", # used by in mock_files/domain.xml :name => name, :format_type => 'raw', :allocation => 123, :capacity => 123, } end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/dhcp_leases.rb0000644000004100000410000000213614002531725025116 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 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 mock net uuid from list_networks.rb 'a29146ea-39b2-412d-8f53-239eef117a32' => leases1, 'fbd4ac68-cbea-4f95-86ed-22953fd92384' => leases2 } if !networks[uuid].nil? return networks[uuid][mac] end end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/upload_volume.rb0000644000004100000410000000144614002531725025522 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real 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 Mock def upload_volume(pool_name, volume_name, file_path) end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/destroy_interface.rb0000644000004100000410000000047614002531725026362 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real #shutdown the interface def destroy_interface(uuid) client.lookup_interface_by_uuid(uuid).destroy end end class Mock def destroy_interface(uuid) true end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/libversion.rb0000644000004100000410000000036714002531725025024 0ustar www-datawww-data module Fog module Libvirt class Compute class Real def libversion() client.libversion end end class Mock def libversion() return 1002009 end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/mock_files/0000755000004100000410000000000014002531725024430 5ustar www-datawww-datafog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/mock_files/domain.xml0000644000004100000410000000155614002531725026430 0ustar www-datawww-data fog-449765558356062 262144 1 hvm fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/get_node_info.rb0000644000004100000410000000200614002531725025437 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real 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 Mock def get_node_info end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/define_domain.rb0000644000004100000410000000042714002531725025426 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real def define_domain(xml) client.define_domain_xml(xml) end end class Mock def define_domain(xml) ::Libvirt::Domain.new() end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/pool_action.rb0000644000004100000410000000051614002531725025152 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real def pool_action(uuid, action) pool = client.lookup_storage_pool_by_uuid uuid pool.send(action) true end end class Mock def pool_action(uuid, action) true end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/list_interfaces.rb0000644000004100000410000000301014002531725026012 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real 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 Mock def list_interfaces(filters={ }) if1 = mock_interface 'if1' if2 = mock_interface 'if2' [if1, if2] end def mock_interface name { :mac => 'aa:bb:cc:dd:ee:ff', :name => name, :active => true } end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/requests/compute/update_autostart.rb0000644000004100000410000000051414002531725026232 0ustar www-datawww-datamodule Fog module Libvirt class Compute class Real def update_autostart(uuid, value) domain = client.lookup_domain_by_uuid(uuid) domain.autostart = value end end class Mock def update_autostart(uuid, value) value end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/compute.rb0000644000004100000410000000772314002531725021000 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 recognizes :libvirt_ip_command 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 end class Mock include Shared def initialize(options={}) # libvirt is part of the gem => ruby-libvirt require 'libvirt' end private def client return @client if defined?(@client) end #read mocks xml def read_xml(file_name) file_path = File.join(File.dirname(__FILE__),"requests","compute","mock_files",file_name) File.read(file_path) end end class Real include Shared attr_reader :client attr_reader :uri attr_reader :ip_command def initialize(options={}) @uri = ::Fog::Libvirt::Util::URI.new(enhance_uri(options[:libvirt_uri])) @ip_command = options[:libvirt_ip_command] # 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 if RUBY_PLATFORM =~ /darwin/ querystring=::URI.parse(uri).query if querystring.nil? append="?socket=/var/run/libvirt/libvirt-sock" else if !::CGI.parse(querystring).key?("socket") append="&socket=/var/run/libvirt/libvirt-sock" end end end uri+append end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/models/0000755000004100000410000000000014002531725020251 5ustar www-datawww-datafog-libvirt-0.8.0/lib/fog/libvirt/models/compute/0000755000004100000410000000000014002531725021725 5ustar www-datawww-datafog-libvirt-0.8.0/lib/fog/libvirt/models/compute/interface.rb0000644000004100000410000000073414002531725024216 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.8.0/lib/fog/libvirt/models/compute/README.md0000644000004100000410000000764114002531725023214 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.8.0/lib/fog/libvirt/models/compute/interfaces.rb0000644000004100000410000000062314002531725024376 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.8.0/lib/fog/libvirt/models/compute/network.rb0000644000004100000410000000127714002531725023752 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 end end end end fog-libvirt-0.8.0/lib/fog/libvirt/models/compute/volumes.rb0000644000004100000410000000060614002531725023746 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.8.0/lib/fog/libvirt/models/compute/nics.rb0000644000004100000410000000033614002531725023210 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.8.0/lib/fog/libvirt/models/compute/templates/0000755000004100000410000000000014002531725023723 5ustar www-datawww-datafog-libvirt-0.8.0/lib/fog/libvirt/models/compute/templates/network.xml.erb0000644000004100000410000000023214002531725026702 0ustar www-datawww-data <%= name %> fog-libvirt-0.8.0/lib/fog/libvirt/models/compute/templates/volume.xml.erb0000644000004100000410000000170514002531725026526 0ustar www-datawww-data <%= name %> <%= split_size_unit(allocation)[0] %> <%= split_size_unit(capacity)[0] %> <%= owner %> <%= group %> 0744 <% if backing_volume -%> <%= backing_volume.path %> <%= owner %> <%= group %> 0744 <% end -%> fog-libvirt-0.8.0/lib/fog/libvirt/models/compute/templates/pool.xml.erb0000644000004100000410000000016114002531725026163 0ustar www-datawww-data <%= name %> <%= path %> fog-libvirt-0.8.0/lib/fog/libvirt/models/compute/templates/server.xml.erb0000644000004100000410000001013614002531725026523 0ustar www-datawww-data <%= name %> <%= memory_size %> <% if hugepages -%> <% end -%> <%= cpus %> <%= os_type %> <% boot_order.each do |dev| -%> <% end -%> <% if !cpu.empty? -%> <% if cpu[:mode] -%> <% else -%> <% end -%> <% fallback = 'allow' model = '' if cpu[:model] fallback = cpu[:model][:fallback] if cpu[:model][:fallback] model = cpu[:model][:name] if cpu[:model][:name] end -%> <%= model %> <% end -%> <% args = {} if File.file?('/etc/foreman/ceph.conf') File.readlines('/etc/foreman/ceph.conf').each do |line| pair = line.strip.split("=") key = pair[0] value = pair[1] args[key] = value end end %> <% volumes.each do |vol| -%> <% if File.file?('/etc/foreman/ceph.conf') && vol.pool_name.include?(args["libvirt_ceph_pool"]) %> <% if args.key?("monitor") -%> <% args["monitor"].split(",").each do |mon| %> <% end %> <% end %> <% if args.key?("auth_uuid") -%> <% else -%> <% end -%> <% if args['bus_type'] == 'virtio' %> ' bus='virtio'/> <% else %> ' bus='scsi'/> <% end %> <% else %> <%# we need to ensure a unique target dev -%> ' bus='virtio'/> <% end %> <% end -%> <% if iso_file -%>
<% end -%> <% nics.each do |nic| -%> /> <% end -%> <% if guest_agent -%> <% end -%> <% rng_backend_model = virtio_rng[:backend_model] ? virtio_rng[:backend_model] : 'random' -%> <% if virtio_rng[:backend_path] -%> <%= virtio_rng[:backend_path] %> <% else -%> <% end -%> <% display_type = display[:type] if display[:port].empty? display_port = display[:port] autoport = 'no' else display_port = '-1' autoport = 'yes' end unless display[:listen].empty? display_listen = "listen='#{display[:listen]}'" end unless display[:password].empty? display_password = "passwd='#{display[:password]}'" end -%> <%= display_password %> /> fog-libvirt-0.8.0/lib/fog/libvirt/models/compute/util/0000755000004100000410000000000014002531725022702 5ustar www-datawww-datafog-libvirt-0.8.0/lib/fog/libvirt/models/compute/util/uri.rb0000644000004100000410000000473414002531725024036 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.8.0/lib/fog/libvirt/models/compute/util/util.rb0000644000004100000410000000166214002531725024211 0ustar www-datawww-datarequire 'nokogiri' require 'erb' require 'ostruct' 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 to_xml template_name = nil # figure out our ERB template filename erb = template_name || self.class.to_s.split("::").last.downcase path = File.join(File.dirname(__FILE__), "..", "templates", "#{erb}.xml.erb") template = File.read(path) ERB.new(template, nil, '-').result(binding) end def randomized_name "fog-#{(SecureRandom.random_number*10E14).to_i.round}" end end end end fog-libvirt-0.8.0/lib/fog/libvirt/models/compute/node.rb0000644000004100000410000000114014002531725023173 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.8.0/lib/fog/libvirt/models/compute/nic.rb0000644000004100000410000000225314002531725023025 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.8.0/lib/fog/libvirt/models/compute/servers.rb0000644000004100000410000000066514002531725023752 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.8.0/lib/fog/libvirt/models/compute/pools.rb0000644000004100000410000000060114002531725023403 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.8.0/lib/fog/libvirt/models/compute/server.rb0000644000004100000410000004414614002531725023571 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 :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 :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 if nics && nics.first end def disk_path volumes.first.path if volumes and volumes.first end def destroy(options={ :destroy_volumes => false}) poweroff unless stopped? service.vm_action(uuid, :undefine) 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 } 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 private attr_accessor :volumes_path # This tests the library version before redefining the address # method for this instance to use a method compatible with # earlier libvirt libraries, or uses the dhcp method from more # recent releases. def addresses(service_arg=service, options={}) addresses_method = self.method(:addresses_dhcp) # check if ruby-libvirt was compiled against a new enough version # that can use dhcp_leases, as otherwise it will not provide the # method dhcp_leases on any of the network objects. has_dhcp_leases = true begin service.networks.first.dhcp_leases(self.mac) rescue NoMethodError has_dhcp_leases = false rescue # assume some other odd exception. end # if ruby-libvirt not compiled with support, or remote library is # too old (must be newer than 1.2.8), then use old fallback if not has_dhcp_leases or service.libversion() < 1002008 addresses_method = self.method(:addresses_ip_command) end # replace current definition for this instance with correct one for # detected libvirt to perform check once for connection (class << self; self; end).class_eval do define_method(:addresses, addresses_method) end addresses(service_arg, options) end def ssh_ip_command(ip_command, uri) # Retrieve the parts we need from the service to setup our ssh options user=uri.user #could be nil host=uri.host keyfile=uri.keyfile port=uri.port # Setup the options ssh_options={} ssh_options[:keys]=[ keyfile ] unless keyfile.nil? ssh_options[:port]=port unless keyfile.nil? ssh_options[:paranoid]=true if uri.no_verify? begin result=Fog::SSH.new(host, user, ssh_options).run(ip_command) rescue Errno::ECONNREFUSED raise Fog::Errors::Error.new("Connection was refused to host #{host} to retrieve the ip_address for #{mac}") rescue Net::SSH::AuthenticationFailed raise Fog::Errors::Error.new("Error authenticating over ssh to host #{host} and user #{user}") end # Check for a clean exit code if result.first.status == 0 return result.first.stdout.strip else # We got a failure executing the command raise Fog::Errors::Error.new("The command #{ip_command} failed to execute with a clean exit code") end end def local_ip_command(ip_command) # Execute the ip_command locally # Initialize empty ip_address string ip_address="" IO.popen("#{ip_command}") do |p| p.each_line do |l| ip_address+=l end status=Process.waitpid2(p.pid)[1].exitstatus if status!=0 raise Fog::Errors::Error.new("The command #{ip_command} failed to execute with a clean exit code") end end #Strip any new lines from the string ip_address.chomp end # This retrieves the ip address of the mac address using ip_command # 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_ip_command(service_arg=service, options={}) mac=self.mac # Aug 24 17:34:41 juno arpwatch: new station 10.247.4.137 52:54:00:88:5a:0a eth0.4 # Aug 24 17:37:19 juno arpwatch: changed ethernet address 10.247.4.137 52:54:00:27:33:00 (52:54:00:88:5a:0a) eth0.4 # Check if another ip_command string was provided ip_command_global=service_arg.ip_command.nil? ? 'grep $mac /var/log/arpwatch.log|sed -e "s/new station//"|sed -e "s/changed ethernet address//g" |sed -e "s/reused old ethernet //" |tail -1 |cut -d ":" -f 4-| cut -d " " -f 3' : service_arg.ip_command ip_command_local=options[:ip_command].nil? ? ip_command_global : options[:ip_command] ip_command="mac=#{mac}; server_name=#{name}; "+ip_command_local ip_address=nil if service_arg.uri.ssh_enabled? ip_address=ssh_ip_command(ip_command, service_arg.uri) else # It's not ssh enabled, so we assume it is if service_arg.uri.transport=="tls" raise Fog::Errors::Error.new("TlS remote transport is not currently supported, only ssh") end ip_address=local_ip_command(ip_command) end # The Ip-address command has been run either local or remote now if ip_address=="" #The grep didn't find an ip address result" ip_address=nil else # To be sure that the command didn't return another random string # We check if the result is an actual ip-address # otherwise we return nil unless ip_address=~/^(\d{1,3}\.){3}\d{1,3}$/ raise Fog::Errors::Error.new( "The result of #{ip_command} does not have valid ip-address format\n"+ "Result was: #{ip_address}\n" ) end end return { :public => [ip_address], :private => [ip_address]} 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_dhcp(service_arg=service, options={}) mac=self.mac ip_address = nil nic = self.nics.find {|nic| nic.mac==mac} if !nic.nil? service.networks.all.each do |net| if net.name == nic.network leases = net.dhcp_leases(mac, 0) # Assume the lease expiring last is the current IP address ip_address = leases.sort_by { |lse| lse["expirytime"] }.last["ipaddr"] if !leases.empty? break end end end return { :public => [ip_address], :private => [ip_address] } end def ip_address(key) addresses[key].nil? ? nil : 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, :virtio_rng => {}, } 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', :password => '' } end end end end end fog-libvirt-0.8.0/lib/fog/libvirt/models/compute/networks.rb0000644000004100000410000000061314002531725024126 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.8.0/lib/fog/libvirt/models/compute/nodes.rb0000644000004100000410000000054114002531725023362 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.8.0/lib/fog/libvirt/models/compute/volume.rb0000644000004100000410000000660214002531725023565 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.path = service.create_volume(pool_name, xml).path 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.path = service.clone_volume(pool_name, new_volume.to_xml, self.name).path new_volume.reload end def upload_image(file_path) requires :pool_name service.upload_volume(pool_name, name, file_path) end private 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 => "0", :group => "0", } 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.8.0/lib/fog/libvirt/models/compute/pool.rb0000644000004100000410000000373614002531725023234 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 end end end end fog-libvirt-0.8.0/fog-libvirt.gemspec0000644000004100000410000000340414002531725017564 0ustar www-datawww-data# coding: utf-8 lib = 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.0.0' s.add_dependency("fog-core", ">= 1.27.4") s.add_dependency("fog-json") s.add_dependency("fog-xml", "~> 0.1.1") s.add_dependency('ruby-libvirt','>= 0.7.0') s.add_dependency("json") s.add_development_dependency("net-ssh") s.add_development_dependency("minitest", "~> 5.0") s.add_development_dependency("minitest-stub-const") s.add_development_dependency("pry") s.add_development_dependency("rake") s.add_development_dependency("rubocop") if RUBY_VERSION > "2.0" s.add_development_dependency("shindo", "~> 0.3.4") s.add_development_dependency("simplecov") s.add_development_dependency("yard") s.add_development_dependency("mocha", "~> 1.1.0") # 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.8.0/Gemfile0000644000004100000410000000031514002531725015264 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