aeolusproject/conductor

View on GitHub
src/app/models/instance_observer.rb

Summary

Maintainability
C
1 day
Test Coverage
#
#   Copyright 2011 Red Hat, Inc.
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

class InstanceObserver < ActiveRecord::Observer

  ACTIVE_STATES = [ Instance::STATE_PENDING, Instance::STATE_RUNNING, Instance::STATE_SHUTTING_DOWN, Instance::STATE_STOPPED ]
  RUNNING_STATES = [ Instance::STATE_PENDING, Instance::STATE_RUNNING, Instance::STATE_SHUTTING_DOWN ]

  def before_save(an_instance)
    if an_instance.changed? and change = an_instance.changes['state']
      update_state_timestamps(change[1], an_instance)
      update_accumulative_state_time(change[0], an_instance)
      update_quota(change[0], change[1], an_instance)
    end
  end

  def update_state_timestamps(state_to, an_instance)
    case state_to
      when Instance::STATE_PENDING then an_instance.time_last_pending = Time.now
      when Instance::STATE_RUNNING then an_instance.time_last_running = Time.now
      when Instance::STATE_SHUTTING_DOWN then an_instance.time_last_shutting_down = Time.now
      when Instance::STATE_STOPPED then an_instance.time_last_stopped = Time.now
    end
  end

  def update_accumulative_state_time(state_from, an_instance)
    case state_from
      when Instance::STATE_PENDING then an_instance.acc_pending_time += Time.now - an_instance.time_last_pending
      when Instance::STATE_RUNNING then an_instance.acc_running_time += Time.now - an_instance.time_last_running
      when Instance::STATE_SHUTTING_DOWN then an_instance.acc_shutting_down_time += Time.now - an_instance.time_last_shutting_down
      when Instance::STATE_STOPPED then an_instance.acc_stopped_time += Time.now - an_instance.time_last_stopped
    end
  end

  def update_quota(state_from, state_to, an_instance)
    pool = an_instance.pool
    pool_family = pool.pool_family
    user = an_instance.owner
    provider_account = an_instance.provider_account

    [provider_account, pool_family, pool, user].each do |parent|
      if parent
        # Since pool and pool_family are related, updating one can cause the other to become stale
        parent.reload if [Pool, ProviderAccount].include? parent.class
        quota = parent.quota
        if quota
          if !RUNNING_STATES.include?(state_from) && RUNNING_STATES.include?(state_to)
            quota.running_instances += 1
          elsif RUNNING_STATES.include?(state_from) && !RUNNING_STATES.include?(state_to)
            quota.running_instances -= 1
          end
          quota.transaction do
            quota.lock!
            if !RUNNING_STATES.include?(state_from) && RUNNING_STATES.include?(state_to)
              quota.running_instances += 1
            elsif RUNNING_STATES.include?(state_from) && !RUNNING_STATES.include?(state_to)
              quota.running_instances -= 1
            end

            if !ACTIVE_STATES.include?(state_from) && ACTIVE_STATES.include?(state_to)
              quota.total_instances += 1
            elsif ACTIVE_STATES.include?(state_from) && !ACTIVE_STATES.include?(state_to)
              quota.total_instances -= 1
            end
            quota.save!
           end
        end
      end
    end
  end

  def after_create(instance)
    event = Event.new(:source => instance, :event_time => instance.created_at,
                      :summary => "created")
    event.save!
  end

  def after_update(instance)
    # This can get stale, so reload it -- if it exists
    instance.deployment.reload if instance.deployment
    if instance.state_changed?
      event = Event.new(:source => instance, :event_time => DateTime.now,
                        :summary => "state changed to #{instance.state}",
                        :change_hash => instance.changes, :status_code => instance.state)
      event.save!
      if instance.state == Instance::STATE_RUNNING && instance.deployment
        event2 = Event.new(:source => instance.deployment, :event_time => DateTime.now,
                        :status_code => "first_running", :summary => "1st instance is running",
                        :change_hash => instance.changes) if instance.first_running?
        event3 = Event.new(:source => instance.deployment, :event_time => DateTime.now,
                        :status_code => "all_running", :summary => "All instances are running",
                        :change_hash => instance.changes) if instance.deployment.all_instances_running?
        event2.save! if event2
        event3.save! if event3
      elsif (instance.state == Instance::STATE_STOPPED || instance.state == Instance::STATE_ERROR) && instance.deployment
        if instance.deployment.any_instance_running? && !(instance.deployment.events.empty?)
          event4 = Event.new(:source => instance.deployment, :event_time => DateTime.now,
                             :status_code => "some_stopped", :summary => "Some instances are stopped",
                             :change_hash => instance.changes) if instance.deployment.events.lifetime.last{|e|e.status_code == :all_running}
        else
          event4 = Event.new(:source => instance.deployment, :event_time => DateTime.now,
                             :status_code => "all_stopped", :summary => "All instances are stopped",
                             :change_hash => instance.changes)
        end
        event4.save! if event4
      elsif instance.state == Instance::STATE_VANISHED
        Event.create!(
          :source => instance, :event_time => Time.now,
          :status_code => 'vanished', :summary => "Instance disappeared from cloud provider"
        )
      end
      instance.deployment.update_state(instance) if instance.deployment

    end
  end

  def after_save(instance)
    if instance.state_changed? and instance.state == Instance::STATE_STOPPED
      instance.instance_key.destroy if instance.instance_key

      if instance.deployment and
        instance.deployment.scheduled_for_deletion and
        instance.deployment.destroyable?

        instance.deployment.destroy
      end
    end
  end

end

InstanceObserver.instance