[email protected] wrote:
> From: martyntaylor <[email protected]>
>
> ---
>  src/app/models/instance_observer.rb       |   41 ++++++++++++++++-
>  src/app/models/quota.rb                   |   41 +++++++++++++++++
>  src/app/models/task.rb                    |    7 ++-
>  src/config/environment.rb                 |    1 +
>  src/spec/factories/instance.rb            |    5 +-
>  src/spec/models/instance_observer_spec.rb |   68 
> ++++++++++++++++++++++++++++-
>  src/spec/models/instance_spec.rb          |   10 ++---
>  src/spec/models/task_spec.rb              |    5 --
>  8 files changed, 159 insertions(+), 19 deletions(-)
>
> diff --git a/src/app/models/instance_observer.rb 
> b/src/app/models/instance_observer.rb
> index fa5b3d2..a7ca75d 100644
> --- a/src/app/models/instance_observer.rb
> +++ b/src/app/models/instance_observer.rb
> @@ -1,11 +1,14 @@
>  class InstanceObserver < ActiveRecord::Observer
>  
>   
We'll need to make sure everything here is free of threading/other race 
conditions with all the incrementing/decrementing 'in use' values.
> +  ACTIVE_STATES = [ Instance::STATE_NEW, Instance::STATE_PENDING, 
> Instance::STATE_RUNNING, Instance::STATE_SHUTTING_DOWN, 
> Instance::STATE_STOPPED ]
> +
>    def before_save(an_instance)
>      if an_instance.changed?
>        change = an_instance.changes['state']
>   
We might need to look at cloud_account_id too, although right now we 
only move instances from 'no account' to 'some account' -- so I'm not 
sure the logic around setting cloud accounts affects anything here.
>        if change
>          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
>    end
> @@ -26,7 +29,41 @@ class InstanceObserver < ActiveRecord::Observer
>        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
> +  end
> +
> +  def update_quota(state_from, state_to, an_instance)
> +
> +    hwp = HardwareProfile.find(an_instance.hardware_profile_id)
> +
>   
A more concise way of doing the same (and avoiding the extra database 
hit if the association has already loaded) would be:
  hwp = an_instance.hardware_profile


> +    pool = Pool.find(an_instance.pool_id)
>   
pool= an_instance.pool
> +    cloud_account = CloudAccount.find(an_instance.cloud_account_id)
> +
>   
cloud_account = an_instance.cloud_account
> +    [cloud_account, pool].each do |parent|
>   

> +      if Quota.exists?(parent.quota_id)
>   
> +        quota = Quota.find(parent.quota_id)
>   
quota = parent.quota
if quota
>   
> +        if state_to == Instance::STATE_RUNNING
> +          #TODO update running cpus
> +          quota.running_instances += 1
> +          quota.running_memory += hwp.memory
> +        elsif state_from == Instance::STATE_RUNNING
> +          #TODO update running cpus
> +          quota.running_instances -= 1
> +          quota.running_memory -= hwp.memory
> +        end
>   
if state_to == state_from then we should do nothing above here
> +
> +        if !ACTIVE_STATES.include?(state_from) && 
> ACTIVE_STATES.include?(state_to)
> +          quota.total_storage += hwp.storage
> +          quota.total_instances += 1
> +        elsif ACTIVE_STATES.include?(state_from) && 
> !ACTIVE_STATES.include?(state_to)
> +          quota.total_storage -= hwp.storage
> +          quota.total_instances -= 1
> +        end
> +
> +        quota.save!
> +      end
> +    end
> +  end
> +
>  end
>  
> -InstanceObserver.instance
> \ No newline at end of file
> +InstanceObserver.instance
> diff --git a/src/app/models/quota.rb b/src/app/models/quota.rb
> index 23f9aee..57662a7 100644
> --- a/src/app/models/quota.rb
> +++ b/src/app/models/quota.rb
> @@ -22,4 +22,45 @@
>  class Quota < ActiveRecord::Base
>    has_one :pool
>    has_one :cloud_account
> +
> +  validates_presence_of :maximum_running_instances
> +  validates_presence_of :maximum_running_memory
> +  validates_presence_of :maximum_running_cpus
> +
> +  validates_presence_of :maximum_total_storage
> +  validates_presence_of :maximum_total_instances
> +
>   
We need to be able to handle null values above. It should be possible, 
for example, to define a quota that limits #instances but not other 
parameters.

> +  validates_numericality_of :maximum_running_instances
> +  validates_numericality_of :maximum_running_memory
> +  validates_numericality_of :maximum_running_cpus
> +
> +  validates_numericality_of :maximum_total_storage
> +  validates_numericality_of :maximum_total_instances
> +
> +
> +  def can_create_instance?(instance)
> +    hwp = HardwareProfile.find(instance.hardware_profile_id)
> +
>   
hwp = instance.hardware_profile
> +    if maximum_total_instances >= (total_instances + 1) && 
> maximum_total_storage >= (total_storage + hwp.storage)
> +      return true
> +    end
> +    return false
> +  end
> +
> +  def can_start_instance?(instance)
> +    hwp = HardwareProfile.find(instance.hardware_profile_id)
>   
hwp = instance.hardware_profile
> +    # TODO Add check for CPUs
> +    if maximum_running_instances >= (running_instances + 1) && 
> maximum_running_memory >= (running_memory + hwp.memory)
> +      return true
> +    end
> +    return false
> +  end
> +
> +  def validate
> +    errors.add("maximum_running_instances", "cannot be less than the current 
> running instances") if maximum_running_instances < running_instances
> +    errors.add("maximum_running_memory", "cannot be less than the current 
> running memory") if maximum_running_memory < running_memory
> +    errors.add("maximum_running_cpus", "cannot be less than the current 
> running CPUs") if maximum_running_cpus < running_cpus
> +    errors.add("maximum_total_storage", "cannot be less than the current 
> total storage") if maximum_total_storage < total_storage
> +    errors.add("maximum_total_instances", "cannot be less than the current 
> total instances") if maximum_total_instances < total_instances
>   
> +  end
>  end
> diff --git a/src/app/models/task.rb b/src/app/models/task.rb
> index f3aaa63..feec26a 100644
> --- a/src/app/models/task.rb
> +++ b/src/app/models/task.rb
> @@ -43,7 +43,9 @@ class Task < ActiveRecord::Base
>    FAILURE_PROVIDER_CONTACT_FAILED = "provider_contact_failed"
>    FAILURE_PROVIDER_RETURNED_FAILED = "provider_returned_failed"
>  
> -  FAILURE_CODES = [FAILURE_PROVIDER_NOT_FOUND, 
> FAILURE_PROVIDER_CONTACT_FAILED, FAILURE_PROVIDER_RETURNED_FAILED]
> +  FAILURE_OVER_POOL_QUOTA = "exceeded_pool_quota"
> +
> +  FAILURE_CODES = [FAILURE_PROVIDER_NOT_FOUND, 
> FAILURE_PROVIDER_CONTACT_FAILED, FAILURE_PROVIDER_RETURNED_FAILED, 
> FAILURE_OVER_POOL_QUOTA]
>  
>    validates_inclusion_of :failure_code,
>      :in => FAILURE_CODES + [nil]
> @@ -114,7 +116,8 @@ class Task < ActiveRecord::Base
>  
>    def validate
>      errors.add("created_at", "Task started but does not have the creation 
> time set") if time_started and created_at.nil?
> -    errors.add("time_started", "Task ends but does not have the start time 
> set") if time_ended and time_started.nil?
> +    # Removed check on time_started exisiting. if time_ended does.  This can 
> now occur, when the task fails before is starts.  e.g. When Over Qutoa
> +    #errors.add("time_started", "Task ends but does not have the start time 
> set") if time_ended and time_started.nil?
>      errors.add("time_ended", "Tasks ends before it's started") unless 
> time_ended.nil? or time_started.nil? or time_ended > time_started
>      errors.add("time_started", "Tasks starts before it's created") unless 
> time_started.nil? or created_at.nil? or time_started > created_at
>    end
> diff --git a/src/config/environment.rb b/src/config/environment.rb
> index 1721126..eefd3a4 100644
> --- a/src/config/environment.rb
> +++ b/src/config/environment.rb
> @@ -77,4 +77,5 @@ Rails::Initializer.run do |config|
>    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', 
> '*.{rb,yml}')]
>    # config.i18n.default_locale = :de
>  
> +
>  end
> diff --git a/src/spec/factories/instance.rb b/src/spec/factories/instance.rb
> index 3a453c3..68225d0 100644
> --- a/src/spec/factories/instance.rb
> +++ b/src/spec/factories/instance.rb
> @@ -1,7 +1,8 @@
>  Factory.define :instance do |i|
>    i.sequence(:name) { |n| "instance#{n}" }
>    i.sequence(:external_key) { |n| "key#{n}" }
> -  i.hardware_profile_id 1
> +  i.association :hardware_profile, :factory => :hardware_profile_auto
> +  i.association :cloud_account, :factory => :mock_cloud_account
>    i.image_id 1
>    i.pool_id 1
>    i.state "running"
> @@ -13,4 +14,4 @@ end
>  
>  Factory.define :new_instance, :parent => :instance do |i|
>    i.state Instance::STATE_NEW
> -end
> \ No newline at end of file
> +end
> diff --git a/src/spec/models/instance_observer_spec.rb 
> b/src/spec/models/instance_observer_spec.rb
> index 9e3dcc7..d713cb1 100644
> --- a/src/spec/models/instance_observer_spec.rb
> +++ b/src/spec/models/instance_observer_spec.rb
> @@ -4,7 +4,15 @@ describe InstanceObserver do
>  
>    before(:each) do
>     @timestamp = Time.now
> -   @instance = Factory :new_instance
> +
> +   @cloud_account_quota = Factory :quota
> +   @cloud_account = Factory(:mock_cloud_account, :quota_id => 
> @cloud_account_quota.id)
> +
> +   @pool_quota = Factory :quota
> +   @pool = Factory(:pool, :quota_id => @pool_quota.id)
> +
> +   @hwp = Factory :mock_hwp1
> +   @instance = Factory(:new_instance, :pool => @pool, :hardware_profile => 
> @hwp, :cloud_account_id => @cloud_account.id)
>    end
>  
>    it "should set started at timestamp when instance goes to state pending" do
> @@ -55,7 +63,7 @@ describe InstanceObserver do
>      sleep(1)
>  
>      @instance.state = Instance::STATE_SHUTTING_DOWN
> -    @instance.save
> +    @instance.save!
>  
>      @instance.acc_running_time.should >= 1
>      @instance.acc_running_time.should <= 2
> @@ -86,4 +94,60 @@ describe InstanceObserver do
>      @instance.acc_stopped_time.should >= 1
>      @instance.acc_stopped_time.should <= 2
>    end
> +
> +  it "should update quota on pool and cloud account when a new instance is 
> created" do
> +    [...@cloud_account_quota, @pool_quota].each do |quota|
> +      quota = Quota.find(quota)
> +
> +      quota.total_instances.should == 1
> +      quota.total_storage.should == @hwp.storage
> +    end
> +  end
> +
> +  it "should update cloud account and pool quota when an instance goes into 
> an inactive state" do
> +    @instance.state = Instance::STATE_CREATE_FAILED
> +    @instance.save!
> +
> +    [...@cloud_account_quota, @pool_quota].each do |quota|
> +      quota = Quota.find(quota)
> +
> +      quota.total_instances.should == 0
> +      quota.total_storage.should == 0
> +    end
> +  end
> +
> +  it "should update pool and cloud account quota when an instance state goes 
> to running" do
> +    @instance.state = Instance::STATE_RUNNING
> +    @instance.save!
> +
> +    [...@cloud_account_quota, @pool_quota].each do |quota|
> +      quota = Quota.find(quota.id)
> +      quota.running_instances.should == 1
> +      quota.running_memory.should == @hwp.memory
> +      #TODO test for cpus
> +
> +      quota.total_storage.should == @hwp.storage
> +      quota.total_instances.should == 1
> +    end
> +  end
> +
> +  it "should update a pool and cloud account quota when an instance state 
> goes from running to another active state" do
> +    @instance.state = Instance::STATE_RUNNING
> +    @instance.save!
> +
> +    @instance.state = Instance::STATE_SHUTTING_DOWN
> +    @instance.save!
> +
> +    [...@cloud_account_quota, @pool_quota].each do |quota|
> +      quota = Quota.find(quota.id)
> +
> +      #TODO test for cpus
> +      quota.running_instances.should == 0
> +      quota.running_memory.should == 0
> +
> +      quota.total_storage.should == @hwp.storage
> +      quota.total_instances.should == 1
> +    end
> +  end
> +
>  end
> \ No newline at end of file
> diff --git a/src/spec/models/instance_spec.rb 
> b/src/spec/models/instance_spec.rb
> index 0eaf837..78a0158 100644
> --- a/src/spec/models/instance_spec.rb
> +++ b/src/spec/models/instance_spec.rb
> @@ -2,7 +2,9 @@ require 'spec_helper'
>  
>  describe Instance do
>    before(:each) do
> -    @instance = Factory.build(:instance)
> +    @quota = Factory :quota
> +    @pool = Factory(:pool, :quota_id => @quota.id)
> +    @instance = Factory.build(:instance, :pool_id => @pool.id)
>      @actions = ['start', 'stop']
>    end
>  
> @@ -69,11 +71,7 @@ describe Instance do
>    end
>  
>    it "should return action list" do
> -    @instance.get_action_list.should eql([])
> -
> -    InstanceTask.stub!(:valid_actions_for_instance_state).and_return(
> -        @actions)
> -    @instance.get_action_list.should eql(@actions)
> +    @instance.get_action_list.should eql(["reboot", "stop"])
>    end
>  
>    it "should be able to queue new actions" do
> diff --git a/src/spec/models/task_spec.rb b/src/spec/models/task_spec.rb
> index d392cb2..c5e7188 100644
> --- a/src/spec/models/task_spec.rb
> +++ b/src/spec/models/task_spec.rb
> @@ -43,11 +43,6 @@ describe Task do
>      @task.should_not be_valid
>    end
>  
> -  it "should have 'started' time set if it ended" do
> -    @task.attributes = @valid_attributes.except :time_started
> -    @task.should_not be_valid
> -  end
> -
>    it "should not be valid if it started before it was created" do
>      @task.attributes = @valid_attributes
>      @task.time_started = @task.created_at - 1.minute
>   

_______________________________________________
deltacloud-devel mailing list
[email protected]
https://fedorahosted.org/mailman/listinfo/deltacloud-devel

Reply via email to