diff --git a/lib/fog/libvirt/compute.rb b/lib/fog/libvirt/compute.rb index 59a916f..fd989be 100644 --- a/lib/fog/libvirt/compute.rb +++ b/lib/fog/libvirt/compute.rb @@ -40,6 +40,7 @@ class Libvirt < Fog::Service request :clone_volume request :list_networks request :destroy_network + request :dhcp_leases request :list_interfaces request :destroy_interface request :get_node_info diff --git a/lib/fog/libvirt/models/compute/network.rb b/lib/fog/libvirt/models/compute/network.rb index 6a80247..e241b69 100644 --- a/lib/fog/libvirt/models/compute/network.rb +++ b/lib/fog/libvirt/models/compute/network.rb @@ -16,6 +16,10 @@ 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 diff --git a/lib/fog/libvirt/models/compute/server.rb b/lib/fog/libvirt/models/compute/server.rb index 6a3e758..9dd8c1e 100644 --- a/lib/fog/libvirt/models/compute/server.rb +++ b/lib/fog/libvirt/models/compute/server.rb @@ -263,88 +263,20 @@ def cloud_init_volume_name def addresses(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? - - # Retrieve the parts we need from the service to setup our ssh options - user=service_arg.uri.user #could be nil - host=service_arg.uri.host - keyfile=service_arg.uri.keyfile - port=service_arg.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 service_arg.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 - ip_address=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 - - 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 - - # 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") + ip_address = nil + nic = self.nics.find {|nic| nic.mac==mac} + if !nic.nil? + Fog::Compute[:libvirt].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 - - #Strip any new lines from the string - ip_address=ip_address.chomp - 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]} + return { :public => [ip_address], :private => [ip_address] } end def ip_address(key) diff --git a/lib/fog/libvirt/requests/compute/dhcp_leases.rb b/lib/fog/libvirt/requests/compute/dhcp_leases.rb new file mode 100644 index 0000000..539c853 --- /dev/null +++ b/lib/fog/libvirt/requests/compute/dhcp_leases.rb @@ -0,0 +1,37 @@ +require 'socket' + +module Fog + module Compute + class Libvirt + 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 diff --git a/lib/fog/libvirt/requests/compute/list_networks.rb b/lib/fog/libvirt/requests/compute/list_networks.rb index 5f55c2a..d2669b4 100644 --- a/lib/fog/libvirt/requests/compute/list_networks.rb +++ b/lib/fog/libvirt/requests/compute/list_networks.rb @@ -37,17 +37,17 @@ def network_to_attributes(net) class Mock def list_networks(filters={ }) - net1 = mock_network 'net1' - net2 = mock_network 'net2' - [net1, net2] - end - - def mock_network name - { - :uuid => 'net.uuid', - :name => name, - :bridge_name => 'net.bridge_name' - } + [ { + :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 diff --git a/tests/libvirt/compute_tests.rb b/tests/libvirt/compute_tests.rb index ae78b9a..aca0db1 100644 --- a/tests/libvirt/compute_tests.rb +++ b/tests/libvirt/compute_tests.rb @@ -10,7 +10,8 @@ tests("Compute requests") do %w{ create_domain create_volume define_domain define_pool destroy_interface destroy_network get_node_info list_domains - list_interfaces list_networks list_pools list_pool_volumes list_volumes pool_action vm_action volume_action }.each do |request| + 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 diff --git a/tests/libvirt/models/compute/network_tests.rb b/tests/libvirt/models/compute/network_tests.rb index 155a598..ec4633f 100644 --- a/tests/libvirt/models/compute/network_tests.rb +++ b/tests/libvirt/models/compute/network_tests.rb @@ -6,6 +6,10 @@ 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 diff --git a/tests/libvirt/models/compute/server_tests.rb b/tests/libvirt/models/compute/server_tests.rb index ac5d035..8b9b41a 100644 --- a/tests/libvirt/models/compute/server_tests.rb +++ b/tests/libvirt/models/compute/server_tests.rb @@ -20,6 +20,9 @@ } 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, diff --git a/tests/libvirt/requests/compute/dhcp_leases_tests.rb b/tests/libvirt/requests/compute/dhcp_leases_tests.rb new file mode 100644 index 0000000..78f5f36 --- /dev/null +++ b/tests/libvirt/requests/compute/dhcp_leases_tests.rb @@ -0,0 +1,15 @@ +Shindo.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