Steve Linabery
2009-Jun-24 16:38 UTC
[Ovirt-devel] [PATCH server] Vm state change auditing/accounting
Adds VmStateChangeEvent class and uses VmObserver to track/audit state changes for Vm objects. Callbacks in VmObserver also update the new total_uptime and total_uptime_timestamp attributes of Vm class, which allows easy calculation of time spent in running states for user resource use accounting. --- src/app/models/vm.rb | 34 +++++++++++ src/app/models/vm_observer.rb | 96 ++++++++++++++++++++++++++++++ src/app/models/vm_state_change_event.rb | 22 +++++++ src/config/environment.rb | 2 +- src/db/migrate/039_add_vm_state_audit.rb | 49 +++++++++++++++ 5 files changed, 202 insertions(+), 1 deletions(-) create mode 100644 src/app/models/vm_observer.rb create mode 100644 src/app/models/vm_state_change_event.rb create mode 100644 src/db/migrate/039_add_vm_state_audit.rb diff --git a/src/app/models/vm.rb b/src/app/models/vm.rb index c1be4a6..a45342e 100644 --- a/src/app/models/vm.rb +++ b/src/app/models/vm.rb @@ -41,6 +41,14 @@ class Vm < ActiveRecord::Base :order => 'time_started DESC', :dependent => :destroy + + has_many :vm_state_change_events, + :order => 'created_at' do + def previous_state_with_type(state_type) + find(:first, :conditions=> { :to_state => state_type }, :order=> 'created_at DESC') + end + end + alias history vm_host_histories validates_presence_of :uuid, :description, :num_vcpus_allocated, @@ -138,6 +146,25 @@ class Vm < ActiveRecord::Base STATE_CREATE_FAILED = "create_failed" STATE_INVALID = "invalid" + ALL_STATES = [STATE_PENDING, + STATE_CREATING, + STATE_RUNNING, + STATE_UNREACHABLE, + STATE_POWERING_OFF, + STATE_STOPPING, + STATE_STOPPED, + STATE_STARTING, + STATE_SUSPENDING, + STATE_SUSPENDED, + STATE_RESUMING, + STATE_SAVING, + STATE_SAVED, + STATE_RESTORING, + STATE_MIGRATING, + STATE_CREATE_FAILED, + STATE_INVALID] + + DESTROYABLE_STATES = [STATE_PENDING, STATE_STOPPED, STATE_CREATE_FAILED, @@ -176,6 +203,13 @@ class Vm < ActiveRecord::Base validates_inclusion_of :state, :in => EFFECTIVE_STATE.keys + def get_calculated_uptime + if VmObserver::AUDIT_RUNNING_STATES.include?(state) + total_uptime_timestamp ? total_uptime + (Time.now - total_uptime_timestamp) : 0 + else + total_uptime + end + end def get_vm_pool vm_resource_pool diff --git a/src/app/models/vm_observer.rb b/src/app/models/vm_observer.rb new file mode 100644 index 0000000..914fd35 --- /dev/null +++ b/src/app/models/vm_observer.rb @@ -0,0 +1,96 @@ +# +# Copyright (C) 2008 Red Hat, Inc. +# Written by Steve Linabery <slinabery at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +class VmObserver < ActiveRecord::Observer + + AUDIT_RUNNING_STATES = [Vm::STATE_RUNNING, + Vm::STATE_POWERING_OFF, + Vm::STATE_STOPPING, + Vm::STATE_STARTING, + Vm::STATE_RESUMING, + Vm::STATE_SAVING, + Vm::STATE_RESTORING, + Vm::STATE_MIGRATING] + + AUDIT_NON_RUNNING_STATES = Vm::ALL_STATES - AUDIT_RUNNING_STATES - [Vm::STATE_UNREACHABLE] + + def after_save(a_vm) + if a_vm.changed? + change = a_vm.changes['state'] + if change + event = VmStateChangeEvent.new({ :vm => a_vm, + :from_state => change[0], + :to_state => change[1]}) + event.save! + end + end + end + + def before_save(a_vm) + if a_vm.changed? + change = a_vm.changes['state'] + if change + #When going from a non-running state to a running state, update the + #total uptime timestamp to indicate the start of the running period. + + if AUDIT_NON_RUNNING_STATES.include?(change[0]) && + AUDIT_RUNNING_STATES.include?(change[1]) + a_vm.total_uptime_timestamp = Time.now + end + + #When going from a running state to anything but a running state, + #add the time since the last timestamp to the total uptime, and then + #update the total uptime timestamp. + #Note that this also matches the transition to unreachable + + if AUDIT_RUNNING_STATES.include?(change[0]) && + !AUDIT_RUNNING_STATES.include?(change[1]) + a_vm.total_uptime = a_vm.total_uptime + + (Time.now - a_vm.total_uptime_timestamp) + a_vm.total_uptime_timestamp = Time.now + end + + + #Leaving the unreachable state for a running state is a special case. + + if change[0] == Vm::STATE_UNREACHABLE && + AUDIT_RUNNING_STATES.include?(change[1]) + + #We need to know from what state the Vm most recently entered the + #unreachable state + prev = a_vm.vm_state_change_events.previous_state_with_type(Vm::STATE_UNREACHABLE) + + if prev + #If it entered unreachable from a running state, then we consider + #the time spent in unreachable as running time. Add the time + #spent in unreachable to total uptime, and update the timestamp. + if AUDIT_RUNNING_STATES.include?(prev.from_state) + a_vm.total_uptime = a_vm.total_uptime + + (Time.now - a_vm.total_uptime_timestamp) + a_vm.total_uptime_timestamp = Time.now + end + end + end + + end + end + end +end + +VmObserver.instance diff --git a/src/app/models/vm_state_change_event.rb b/src/app/models/vm_state_change_event.rb new file mode 100644 index 0000000..033e9da --- /dev/null +++ b/src/app/models/vm_state_change_event.rb @@ -0,0 +1,22 @@ +# +# Copyright (C) 2008 Red Hat, Inc. +# Written by Steve Linabery <slinabery at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +class VmStateChangeEvent < ActiveRecord::Base + belongs_to :vm +end diff --git a/src/config/environment.rb b/src/config/environment.rb index 4014613..98a2fbb 100644 --- a/src/config/environment.rb +++ b/src/config/environment.rb @@ -82,7 +82,7 @@ Rails::Initializer.run do |config| # Activate observers that should always be running # config.active_record.observers = :cacher, :garbage_collector - config.active_record.observers = :host_observer + config.active_record.observers = :host_observer, :vm_observer end # Add new inflection rules using the following format diff --git a/src/db/migrate/039_add_vm_state_audit.rb b/src/db/migrate/039_add_vm_state_audit.rb new file mode 100644 index 0000000..f16fb8b --- /dev/null +++ b/src/db/migrate/039_add_vm_state_audit.rb @@ -0,0 +1,49 @@ +# +# Copyright (C) 2009 Red Hat, Inc. +# Written by Steve Linabery <slinabery at redhat.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. A copy of the GNU General Public License is +# also available at http://www.gnu.org/copyleft/gpl.html. + +class AddVmStateAudit < ActiveRecord::Migration + def self.up + add_column :vms, :total_uptime, :integer, :default => 0 + add_column :vms, :total_uptime_timestamp, :timestamp + + create_table :vm_state_change_events do |t| + t.timestamp :created_at + t.integer :vm_id + t.string :from_state + t.string :to_state + t.integer :lock_version, :default => 0 + end + + Vm.transaction do + Vm.find(:all).each do |vm| + event = VmStateChangeEvent.new(:vm_id => vm.id, + :to_state => vm.state + ) + event.save! + end + end + end + + def self.down + remove_column :vms, :total_uptime + remove_column :vms, :total_uptime_timestamp + + drop_table :vm_state_change_events + end +end -- 1.6.0.6
Scott Seago
2009-Jun-29 13:18 UTC
[Ovirt-devel] [PATCH server] Vm state change auditing/accounting
Steve Linabery wrote:> Adds VmStateChangeEvent class and uses VmObserver to track/audit state > changes for Vm objects. Callbacks in VmObserver also update the new > total_uptime and total_uptime_timestamp attributes of Vm class, which > allows easy calculation of time spent in running states for user > resource use accounting. > --- >ACK with the addition of those minor fixes we discussed in IRC. My patch will be out shortly -- one more snag to work through. Scott
Possibly Parallel Threads
- [PATCH server] REVISED Vm state change auditing/accounting
- [PATCH server] UI for accumulated uptime for VMs.
- [PATCH server] UI for accumulated uptime for VMs. (revised2)
- [PATCH server] UI for accumulated uptime for VMs. (revised)
- [PATCH server] UI for accumulated uptime for VMs. (revised3)