1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-02-08 05:57:23 +03:00

Merge remote-tracking branch 'origin/feature-2452'

This commit is contained in:
Javi Fontan 2014-06-23 11:56:26 +02:00
commit f0978952bb
5 changed files with 363 additions and 223 deletions

View File

@ -53,7 +53,8 @@ public:
CANCEL_FAILURE, /**< Sent by the VMM when a cancel action fails */
MONITOR_FAILURE, /**< Sent by the VMM when a VM has failed while active */
MONITOR_SUSPEND, /**< Sent by the VMM when a VM is paused while active */
MONITOR_DONE, /**< Sent by the VMM when a VM is not found */
MONITOR_DONE, /**< Sent by the VMM when a Host cannot be monitored*/
MONITOR_POWEROFF, /**< Sent by the VMM when a VM is not found */
PROLOG_SUCCESS, /**< Sent by the TM when the prolog phase succeeds */
PROLOG_FAILURE, /**< Sent by the TM when the prolog phase fails */
EPILOG_SUCCESS, /**< Sent by the TM when the epilog phase succeeds */
@ -197,6 +198,8 @@ private:
void monitor_done_action(int vid);
void monitor_poweroff_action(int vid);
void prolog_success_action(int vid);
void prolog_failure_action(int vid);

View File

@ -73,7 +73,7 @@ void MonitorThread::do_message()
}
// -------------------------------------------------------------------------
// Monitoring Error
// Monitoring Error. VMs running on the host are moved to UNKNOWN
// -------------------------------------------------------------------------
if (result != "SUCCESS")
{
@ -211,6 +211,10 @@ void MonitorThread::do_message()
host->unlock();
//--------------------------------------------------------------------------
// Process VM information if any. VMs not reported by the hypervisor are
// moved to the POWEROFF state.
//--------------------------------------------------------------------------
if (vm_poll)
{
set<int>::iterator its;
@ -218,7 +222,7 @@ void MonitorThread::do_message()
for (its = lost.begin(); its != lost.end(); its++)
{
lcm->trigger(LifeCycleManager::MONITOR_DONE, *its);
lcm->trigger(LifeCycleManager::MONITOR_POWEROFF, *its);
}
for (itm = found.begin(); itm != found.end(); itm++)

View File

@ -113,6 +113,10 @@ void LifeCycleManager::trigger(Actions action, int _vid)
aname = "MONITOR_DONE";
break;
case MONITOR_POWEROFF:
aname = "MONITOR_POWEROFF";
break;
case PROLOG_SUCCESS:
aname = "PROLOG_SUCCESS";
break;
@ -334,6 +338,10 @@ void LifeCycleManager::do_action(const string &action, void * arg)
{
monitor_done_action(vid);
}
else if (action == "MONITOR_POWEROFF")
{
monitor_poweroff_action(vid);
}
else if (action == "PROLOG_SUCCESS")
{
prolog_success_action(vid);

View File

@ -1058,10 +1058,10 @@ void LifeCycleManager::cancel_success_action(int vid)
//----------------------------------------------------
// POWEROFF STATE
//----------------------------------------------------
map<string, string> empty;
vm->delete_snapshots();
map<string, string> empty;
vm->update_info(0, 0, -1, -1, empty);
vmpool->update(vm);
@ -1263,6 +1263,59 @@ void LifeCycleManager::monitor_done_action(int vid)
vm->unlock();
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void LifeCycleManager::monitor_poweroff_action(int vid)
{
VirtualMachine * vm;
vm = vmpool->get(vid,true);
if ( vm == 0 )
{
return;
}
//This event should be ignored if the VM is not RUNNING
if ( vm->get_lcm_state() == VirtualMachine::RUNNING )
{
//----------------------------------------------------
// POWEROFF STATE
//----------------------------------------------------
map<string, string> empty;
time_t the_time = time(0);
Nebula& nd = Nebula::instance();
DispatchManager * dm = nd.get_dm();
vm->delete_snapshots();
vm->update_info(0, 0, -1, -1, empty);
vm->set_resched(false);
vm->set_state(VirtualMachine::SHUTDOWN_POWEROFF);
vmpool->update(vm);
vm->set_running_etime(the_time);
vm->set_etime(the_time);
vm->set_vm_info();
vm->set_reason(History::USER);
vmpool->update_history(vm);
//----------------------------------------------------
dm->trigger(DispatchManager::POWEROFF_SUCCESS,vid);
}
vm->unlock();
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

View File

@ -21,7 +21,13 @@ require 'rexml/document'
ENV['LANG']='C'
################################################################################
#
# KVM Monitor Module
#
################################################################################
module KVM
# Constants for KVM operations
CONF={
:dominfo => 'virsh --connect LIBVIRT_URI --readonly dominfo',
:list => 'virsh --connect LIBVIRT_URI --readonly list',
@ -31,118 +37,121 @@ module KVM
'LIBVIRT_URI' => 'qemu:///system'
}
def self.get_all_vm_info
vms=get_vm_info
info={}
vms.each do |name, vm|
info[name]=vm[:values]
end
info
# Execute a virsh command using the predefined command strings and URI
# @param command [Symbol] as defined in the module CONF constant
def self.virsh(command)
CONF[command].gsub('LIBVIRT_URI', CONF['LIBVIRT_URI'])
end
def self.get_vm_names
# Get the information of a single VM. In case of error the VM is reported
# as not found.
# @param vm_id [String] with the VM information
def self.get_vm_info(one_vm)
dominfo = dom_info(one_vm)
return { :state => '-' } if !dominfo
psinfo = process_info(dominfo['UUID'])
vm = Hash.new
vm[:name] = one_vm
vm[:pid] = psinfo[1]
cpu = get_cpu_info({one_vm => vm})
resident_mem = psinfo[5].to_i
max_mem = dominfo['Max memory'].split(/\s+/).first.to_i
values=Hash.new
values[:state] = get_state(dominfo['State'])
values[:usedcpu] = cpu[vm[:pid]] if cpu[vm[:pid]]
values[:usedmemory] = [resident_mem, max_mem].max
values.merge!(get_interface_statistics(one_vm))
return values
end
# Gets the information of all VMs
#
# @return [Hash, nil] Hash with the VM information or nil in case of error
def self.get_all_vm_info
vms_info = Hash.new
vms = Hash.new
text=`#{virsh(:list)}`
return [] if $?.exitstatus != 0
return nil if $?.exitstatus != 0
lines=text.split(/\n/)[2..-1]
lines = text.split(/\n/)[2..-1]
lines.map do |line|
names = lines.map do |line|
line.split(/\s+/).delete_if {|d| d.empty? }[1]
end
end
def self.process_info(uuid)
ps=`ps auxwww | grep -- '-uuid #{uuid}' | grep -v grep`
ps.split(/\s+/)
end
return vms_info if names.length == 0
def self.get_vm_info(one_vm=nil)
vms={}
names.each do |vm|
dominfo = dom_info(vm)
names=get_vm_names
if dominfo
psinfo = process_info(dominfo['UUID'])
if names.length!=0
names.each do |vm|
dominfo=dom_info(vm)
if dominfo
psinfo=process_info(dominfo['UUID'])
info= Hash.new
info={}
info[:dominfo]=dominfo
info[:psinfo]=psinfo
info[:name]=vm
info[:pid]=psinfo[1]
info[:dominfo] = dominfo
info[:psinfo] = psinfo
info[:name] = vm
info[:pid] = psinfo[1]
vms[vm]=info
end
end
cpu=get_cpu_info(vms)
vms.each do |name, vm|
if one_vm
next if name!=one_vm
end
c=cpu[vm[:pid]]
vm[:cpu]=c if c
monitor=Hash.new
ps_data=vm[:psinfo]
dominfo=vm[:dominfo]
monitor[:cpu]=vm[:cpu]
monitor[:resident_memory]=ps_data[5].to_i
monitor[:max_memory]=dominfo['Max memory'].split(/\s+/).first.to_i
monitor[:memory]=[monitor[:resident_memory], monitor[:max_memory]].max
state=dominfo['State']
monitor[:state]=get_state(state)
monitor[:cpus]=dominfo['CPU(s)']
values=Hash.new
values[:state]=monitor[:state]
values[:usedcpu]=monitor[:cpu]
values[:usedmemory]=monitor[:memory]
values.merge!(get_interface_statistics(name))
vm[:values]=values
vms[vm]=info
end
end
if one_vm
if vms[one_vm]
vms[one_vm][:values]
else
{ :state => '-' }
end
else
vms
cpu = get_cpu_info(vms)
vms.each do |name, vm|
ps_data = vm[:psinfo]
dominfo = vm[:dominfo]
resident_mem = ps_data[5].to_i
max_mem = dominfo['Max memory'].split(/\s+/).first.to_i
values = Hash.new
values[:state] = get_state(dominfo['State'])
values[:usedcpu] = cpu[vm[:pid]] if cpu[vm[:pid]]
values[:usedmemory] = [resident_mem, max_mem].max
values.merge!(get_interface_statistics(name))
vms_info[vm[:name]] = values
end
return vms_info
end
# Gathers process information from a set of VMs.
# @param vms [Hash] of vms indexed by name. Value is a hash with :pid
# @return [Hash] with ps information
def self.get_cpu_info(vms)
pids=vms.map {|name, vm| vm[:pid] }
pids = vms.map {|name, vm| vm[:pid] }
pids.compact!
cpu={}
cpu = Hash.new
pids.each_slice(20) do |slice|
data=%x{#{CONF[:top]} #{slice.join(',')}}
data = %x{#{CONF[:top]} #{slice.join(',')}}
lines=data.strip.split("\n")
block_size=lines.length/2
valid_lines=lines.last(block_size)
lines = data.strip.split("\n")
block_size = lines.length/2
valid_lines = lines.last(block_size)
first_domain = 7
valid_lines.each_with_index{ |l,i|
if l.match 'PID USER'
first_domain=i+1
@ -150,149 +159,196 @@ module KVM
end
}
domain_lines=valid_lines[first_domain..-1]
domain_lines = valid_lines[first_domain..-1]
domain_lines.each do |line|
d=line.split
cpu[d[0]]=d[8]
d = line.split
cpu[d[0]] = d[8]
end
end
cpu
end
# Process information for a KVM domain by its UUID
# @param uid [String] with user id
# @return [Array] of user processes
def self.process_info(uuid)
ps=`ps auxwww | grep -- '-uuid #{uuid}' | grep -v grep`
ps.split(/\s+/)
end
# Gets the info of a domain by its id
# @param the ID of the VM as defined in libvirt
# @return [Hash] with the output of virsh dominfo, indexed by name (Id...)
# Example execution of dominfo
# Id: 5
# Name: one-6
# UUID: 06bc1876-fc6a-4dca-b41d-d7f2093b6b59
# OS Type: hvm
# State: running
# CPU(s): 1
# CPU time: 11.1s
# Max memory: 524288 KiB
# Used memory: 524288 KiB
# Persistent: no
# Autostart: disable
# Managed save: no
# Security model: none
# Security DOI: 0
def self.dom_info(vmid)
text=`#{virsh(:dominfo)} #{vmid}`
text = `#{virsh(:dominfo)} #{vmid}`
return nil if $?.exitstatus != 0
lines=text.split(/\n/)
lines = text.split(/\n/)
hash = Hash.new
hash=Hash.new
lines.map do |line|
parts = line.split(/:\s+/)
data=lines.map do |line|
parts=line.split(/:\s+/)
hash[parts[0]]=parts[1]
hash[parts[0]] = parts[1]
end
hash
end
def self.virsh(command)
CONF[command].gsub('LIBVIRT_URI', CONF['LIBVIRT_URI'])
end
# Aggregate statics of all VM NICs
# @param the ID of the VM as defined in libvirt
# @return [Hash] with network stats, by name [symbol] :netrx, :nettx
def self.get_interface_statistics(vmid)
text = `#{virsh(:dumpxml)} #{vmid}`
def self.get_interface_names(vmid)
text=`#{virsh(:dumpxml)} #{vmid}`
return {} if $?.exitstatus != 0
doc = REXML::Document.new(text)
interfaces = Array.new
doc=REXML::Document.new(text)
interfaces = []
doc.elements.each('domain/devices/interface/target') do |ele|
interfaces << ele.attributes["dev"]
end
interfaces
end
return {} if interfaces.empty?
def self.get_interface_statistics(vmid)
interfaces=get_interface_names(vmid)
values = Hash.new
if interfaces && !interfaces.empty?
values={}
values[:netrx]=0
values[:nettx]=0
values[:netrx] = 0
values[:nettx] = 0
interfaces.each do |interface|
text=`#{virsh(:domifstat)} #{vmid} #{interface}`
interfaces.each do |interface|
text=`#{virsh(:domifstat)} #{vmid} #{interface}`
text.each_line do |line|
columns=line.split(/\s+/)
case columns[1]
next if $?.exitstatus != 0
text.each_line do |line|
columns = line.split(/\s+/)
case columns[1]
when 'rx_bytes'
values[:netrx]+=columns[2].to_i
values[:netrx] += columns[2].to_i
when 'tx_bytes'
values[:nettx]+=columns[2].to_i
end
end
end
values
else
{}
end
values
end
# Translate libvirt state to Opennebula monitor state
# @param state [String] libvirt state
# @return [String] OpenNebula state
def self.get_state(state)
case state.gsub('-', '')
when *%w{running blocked shutdown dying idle}
'a'
when 'paused'
'd'
when 'crashed'
'e'
else
'-'
when *%w{running blocked shutdown dying idle}
'a'
when 'paused'
'd'
when 'crashed'
'e'
else
'-'
end
end
end
################################################################################
#
# Xen Monitor Module
#
################################################################################
module XEN
# Default configuration variables. It can be overridden through xenrc
CONF={
'XM_POLL' => 'sudo /usr/sbin/xentop -bi2'
}
# Get the information of a single VM. In case of error the VM is reported
# as not found.
# @param vm_id [String] with the VM information
def self.get_vm_info(vm_id)
data = get_all_vm_info[vm_id]
data = get_all_vm_info
if !data
return {:STATE => 'd'}
else
return data
return data[vm_id]
end
end
# Gets the information of all VMs
#
# @return [Hash, nil] Hash with the VM information or nil in case of error
def self.get_all_vm_info
begin
text=`#{CONF['XM_POLL']}`
lines=text.strip.split("\n")
block_size=lines.length/2
valid_lines=lines.last(block_size)
begin
text = `#{CONF['XM_POLL']}`
first_domain = 4
valid_lines.each_with_index{ |l,i|
if l.match 'NAME STATE'
first_domain=i+1
break
return nil if $?.exitstatus != 0
lines = text.strip.split("\n")
block_size = lines.length/2
valid_lines = lines.last(block_size)
first_domain = 4
valid_lines.each_with_index{ |l,i|
if l.match 'NAME STATE'
first_domain=i+1
break
end
}
domain_lines = valid_lines[first_domain..-1]
domains = Hash.new
domain_lines.each do |dom|
dom_data = dom.gsub('no limit', 'no-limit').strip.split
dom_hash = Hash.new
dom_hash[:name] = dom_data[0]
dom_hash[:state] = get_state(dom_data[1])
dom_hash[:usedcpu] = dom_data[3]
dom_hash[:usedmemory] = dom_data[4]
dom_hash[:nettx] = dom_data[10].to_i * 1024
dom_hash[:netrx] = dom_data[11].to_i * 1024
domains[dom_hash[:name]] = dom_hash
end
}
domain_lines=valid_lines[first_domain..-1]
domains=Hash.new
domain_lines.each do |dom|
dom_data=dom.gsub('no limit', 'no-limit').strip.split
dom_hash=Hash.new
dom_hash[:name]=dom_data[0]
dom_hash[:state]=get_state(dom_data[1])
dom_hash[:usedcpu]=dom_data[3]
dom_hash[:usedmemory]=dom_data[4]
dom_hash[:nettx]=dom_data[10].to_i * 1024
dom_hash[:netrx]=dom_data[11].to_i * 1024
domains[dom_hash[:name]]=dom_hash
domains
rescue
STDERR.puts "Error executing #{CONF['XM_POLL']}"
nil
end
domains
rescue
STDERR.puts "Error executing #{CONF['XM_POLL']}"
nil
end
end
# Returns an OpenNebula state from the Xen status
# @param state [String] with the Xen status
# @return [String] OpenNebula monitor state
def self.get_state(state)
case state.gsub('-', '')[-1..-1]
when *%w{r b s d}
@ -307,63 +363,71 @@ module XEN
end
end
################################################################################
# Functions to interface hypervisor information
################################################################################
def select_hypervisor
hypervisor=nil
params=ARGV.clone
# Selects the hypervisor to be used based on the arguments or probe location
# This function also loads the associated configuration variables.
# @return [Module] with the hypervisor XEN, KVM
def setup_hypervisor
hypervisor = nil
params = ARGV.clone
params.each_with_index do |param, index|
case param
when '--kvm'
hypervisor=KVM
ARGV.delete_at(index)
when '--xen'
hypervisor=XEN
ARGV.delete_at(index)
when '--kvm'
hypervisor = KVM
ARGV.delete_at(index)
when '--xen'
hypervisor = XEN
ARGV.delete_at(index)
end
end
if !hypervisor
case $0
when %r{/vmm\/kvm/}
hypervisor=KVM
when %r{/vmm\/xen\d?/}
hypervisor=XEN
when %r{/vmm\/kvm/}
hypervisor=KVM
when %r{/vmm\/xen\d?/}
hypervisor=XEN
end
end
hypervisor
end
def load_vars(hypervisor)
case hypervisor.name
when 'XEN'
file='xenrc'
vars=%w{XM_POLL}
when 'KVM'
file='kvmrc'
vars=%w{LIBVIRT_URI}
else
return
when 'XEN'
file = 'xenrc'
vars = %w{XM_POLL}
when 'KVM'
file = 'kvmrc'
vars = %w{LIBVIRT_URI}
else
return nil
end
begin
env=`. #{File.dirname($0)+"/#{file}"};env`
# Load the rc variables and override the default values
begin
env = `. #{File.dirname($0)+"/#{file}"};env`
lines = env.split("\n")
lines=env.split("\n")
vars.each do |var|
lines.each do |line|
if a=line.match(/^(#{var})=(.*)$/)
hypervisor::CONF[var]=a[2]
break
vars.each do |var|
lines.each do |line|
if a = line.match(/^(#{var})=(.*)$/)
hypervisor::CONF[var] = a[2]
break
end
end
end
rescue
end
rescue
end
return hypervisor
end
# Returns an OpenNebula monitor string
# @param name [String] of the monitor metric
# @param value [String] of the monitor metric
# @return [String, nil]
def print_data(name, value)
if value
"#{name.to_s.upcase}=#{value}"
@ -372,16 +436,16 @@ def print_data(name, value)
end
end
# Puts to STDOUT a string in the form "VAL1=VAR1 VAL2=VAR2" with the monitor
# attributes of the VM
# @param hypervisor [Module]
# @param vm_id [String] with the VM ID
def print_one_vm_info(hypervisor, vm_id)
info=hypervisor.get_vm_info(vm_id)
info = hypervisor.get_vm_info(vm_id)
exit(-1) if !info
#info.merge!(get_interface_statistics(vm_id))
values=info.map do |key, value|
values = info.map do |key, value|
print_data(key, value)
end
@ -393,15 +457,20 @@ def print_all_vm_info(hypervisor)
require 'base64'
require 'zlib'
vms=hypervisor.get_all_vm_info
vms = hypervisor.get_all_vm_info
return nil if vms.nil?
compressed = Zlib::Deflate.deflate(vms.to_yaml)
compressed=Zlib::Deflate.deflate(vms.to_yaml)
puts Base64.encode64(compressed).delete("\n")
end
def print_all_vm_template(hypervisor)
vms=hypervisor.get_all_vm_info
return nil if vms.nil?
puts "VM_POLL=YES"
vms.each do |name, data|
@ -411,34 +480,37 @@ def print_all_vm_template(hypervisor)
number = name.split('-').last
end
string="VM=[\n"
string<<" ID=#{number},\n"
string<<" DEPLOY_ID=#{name},\n"
string = "VM=[\n"
string << " ID=#{number},\n"
string << " DEPLOY_ID=#{name},\n"
values=data.map do |key, value|
values = data.map do |key, value|
print_data(key, value)
end
monitor=values.zip.join(' ')
monitor = values.zip.join(' ')
string<<" POLL=\"#{monitor}\" ]"
string << " POLL=\"#{monitor}\" ]"
puts string
end
end
hypervisor=select_hypervisor
################################################################################
# MAIN PROGRAM
################################################################################
hypervisor = setup_hypervisor
if !hypervisor
STDERR.puts "Could not detect hypervisor"
exit(-1)
end
load_vars(hypervisor)
vm_id = ARGV[0]
vm_id=ARGV[0]
if vm_id=='-t'
if vm_id == '-t'
print_all_vm_template(hypervisor)
elsif vm_id
print_one_vm_info(hypervisor, vm_id)