[Ovirt-devel] [PATCH server] REVISED Vm state change auditing/accounting

Steve Linabery slinabery at redhat.com
Mon Jun 29 16:46:25 UTC 2009


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 |   51 ++++++++++++++++
 5 files changed, 204 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 f445219..1a57a14 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..e9b5076
--- /dev/null
+++ b/src/db/migrate/039_add_vm_state_audit.rb
@@ -0,0 +1,51 @@
+#
+# 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!
+        vm.total_uptime = 0
+        vm.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




More information about the ovirt-devel mailing list