Skip to content

Commit

Permalink
Merge pull request #11 from dcbw/ipaddr-libvirt
Browse files Browse the repository at this point in the history
Retrieve VM IP address using libvirt
  • Loading branch information
plribeiro3000 committed Mar 3, 2016
2 parents 505675f + 21d1b71 commit c85553e
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 90 deletions.
1 change: 1 addition & 0 deletions lib/fog/libvirt/compute.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions lib/fog/libvirt/models/compute/network.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
88 changes: 10 additions & 78 deletions lib/fog/libvirt/models/compute/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
37 changes: 37 additions & 0 deletions lib/fog/libvirt/requests/compute/dhcp_leases.rb
Original file line number Diff line number Diff line change
@@ -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
22 changes: 11 additions & 11 deletions lib/fog/libvirt/requests/compute/list_networks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion tests/libvirt/compute_tests.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions tests/libvirt/models/compute/network_tests.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions tests/libvirt/models/compute/server_tests.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
15 changes: 15 additions & 0 deletions tests/libvirt/requests/compute/dhcp_leases_tests.rb
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit c85553e

Please sign in to comment.