1
0
Fork 0
mirror of https://github.com/fog/fog.git synced 2022-11-09 13:51:43 -05:00
fog--fog/lib/fog/libvirt/models/compute/server.rb
Carl Caum 5b0b1218f7 Revert 530122d
This commit reverts commit 530122d. Commit 530122d contained changes to
how the mac address for an domain's interfaces was retrieved.  The
change was made due to an broken development environment and was
unnecessary.
2012-08-03 22:12:05 -07:00

424 lines
14 KiB
Ruby

require 'fog/compute/models/server'
require 'fog/libvirt/models/compute/util/util'
require 'net/ssh/proxy/command'
module Fog
module Compute
class Libvirt
class Server < Fog::Compute::Server
include Fog::Compute::LibvirtUtil
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 :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_writer :private_key, :private_key_path, :public_key, :public_key_path, :username
# Can be created by passing in :xml => "<xml to create domain/server>"
# 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
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
@xml ||= to_xml
self.id = (persistent ? connection.define_domain(xml) : connection.create_domain(xml)).uuid
reload
rescue => e
raise Fog::Errors::Error.new("Error saving the server: #{e}")
end
def username
@username ||= 'root'
end
def start
return true if active?
connection.vm_action(uuid, :create)
reload
true
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?
connection.vm_action(uuid, :undefine)
volumes.each { |vol| vol.destroy } if options[:destroy_volumes]
true
end
def reboot
connection.vm_action(uuid, :reboot)
end
def poweroff
connection.vm_action(uuid, :destroy)
end
def shutdown
connection.vm_action(uuid, :shutdown)
end
def resume
connection.vm_action(uuid, :resume)
end
def suspend
connection.vm_action(uuid, :suspend)
end
def stopped?
state == "shutoff"
end
def ready?
state == "running"
end
#alias methods
alias :halt :poweroff
alias :stop :shutdown
alias :active? :active
def volumes
# lazy loading of volumes
@volumes ||= (@volumes_path || []).map{|path| connection.volumes.all(:path => path).first }
end
def private_ip_address
ip_address(:private)
end
def public_ip_address
ip_address(:public)
end
def private_key_path
@private_key_path ||= Fog.credentials[:private_key_path]
@private_key_path &&= File.expand_path(@private_key_path)
end
def private_key
@private_key ||= private_key_path && File.read(private_key_path)
end
def public_key_path
@public_key_path ||= Fog.credentials[:public_key_path]
@public_key_path &&= File.expand_path(@public_key_path)
end
def public_key
@public_key ||= public_key_path && File.read(public_key_path)
end
def ssh(commands)
requires :public_ip_address, :username
#requires :password, :private_key
ssh_options={}
ssh_options[:password] = password unless password.nil?
ssh_options[:key_data] = [private_key] if private_key
ssh_options[:proxy]= ssh_proxy unless ssh_proxy.nil?
Fog::SSH.new(public_ip_address, @username, ssh_options).run(commands)
end
def ssh_proxy
# if this is a direct connection, we don't need a proxy to be set.
return nil unless @connection.uri.ssh_enabled?
user_string= connection.uri.user ? "-l #{connection.uri.user}" : ""
Net::SSH::Proxy::Command.new("ssh #{user_string} #{connection.uri.host} nc %h %p")
end
# Transfers a file
def scp(local_path, remote_path, upload_options = {})
requires :public_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(public_ip_address, username, scp_options).upload(local_path, remote_path, upload_options)
end
# Sets up a new key
def setup(credentials = {})
requires :public_key, :public_ip_address, :username
credentials[:proxy]= ssh_proxy unless ssh_proxy.nil?
credentials[:password] = password unless self.password.nil?
credentails[: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(public_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(public_ip_address, username, credentials).run(commands)
end
def update_display attrs = {}
connection.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
private
attr_accessor :volumes_path
# This retrieves the ip address of the mac address
# 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(connection=connection, 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=connection.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' : connection.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 connection.uri.ssh_enabled?
# Retrieve the parts we need from the connection to setup our ssh options
user=connection.uri.user #could be nil
host=connection.uri.host
keyfile=connection.uri.keyfile
port=connection.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 connection.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 connection.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")
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]}
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) ? connection.nics.new(nic) : nic }
else
self.nics = [connection.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) ? connection.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 = connection.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[: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 = connection.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",
:iso_dir => default_iso_dir,
:network_interface_type => "network",
:network_nat_network => "default",
:network_bridge_name => "br0",
:boot_order => default_boot_order,
:display => default_display
}
end
def default_boot_order
%w[hd cdrom network]
end
def verify_boot_order order = []
if order
order.each do |b|
raise "invalid boot order, possible values are: hd, network and/or cdrom" unless default_boot_order.include?(b)
end
end
end
def default_display
{:port => '-1', :listen => '127.0.0.1', :type => 'vnc', :password => '' }
end
end
end
end
end