From: martyntaylor <[email protected]>

---
 src/app/models/instance_observer.rb       |   42 ++++++++++++++-
 src/app/models/quota.rb                   |   49 ++++++++++++++++++
 src/app/models/task.rb                    |    7 ++-
 src/config/environment.rb                 |    1 +
 src/spec/factories/hardware_profile.rb    |    2 +-
 src/spec/factories/instance.rb            |    5 +-
 src/spec/factories/quota.rb               |   15 +++++
 src/spec/models/instance_observer_spec.rb |   80 ++++++++++++++++++++++++++++-
 src/spec/models/instance_spec.rb          |   10 ++--
 src/spec/models/task_spec.rb              |    5 --
 10 files changed, 196 insertions(+), 20 deletions(-)
 create mode 100644 src/spec/factories/quota.rb

diff --git a/src/app/models/instance_observer.rb 
b/src/app/models/instance_observer.rb
index fa5b3d2..52adccc 100644
--- a/src/app/models/instance_observer.rb
+++ b/src/app/models/instance_observer.rb
@@ -1,11 +1,14 @@
 class InstanceObserver < ActiveRecord::Observer
 
+  ACTIVE_STATES = [ 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']
       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,42 @@ 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 = an_instance.hardware_profile
+    pool = an_instance.pool
+    cloud_account = an_instance.cloud_account
+
+    [cloud_account, pool].each do |parent|
+      quota = parent.quota
+      if quota
+        if state_to == Instance::STATE_RUNNING
+          quota.running_instances += 1
+          quota.running_memory =  quota.running_memory.to_f + 
hwp.memory.value.to_f
+          quota.running_cpus = quota.running_cpus.to_f + hwp.cpu.value.to_f
+        elsif state_from == Instance::STATE_RUNNING
+          quota.running_instances -= 1
+          quota.running_memory = quota.running_memory.to_f - 
hwp.memory.value.to_f
+          quota.running_cpus = quota.running_cpus.to_f - hwp.cpu.value.to_f
+        end
+
+        if state_from != nil
+          if !ACTIVE_STATES.include?(state_from) && 
ACTIVE_STATES.include?(state_to)
+            quota.total_storage = quota.total_storage.to_f + 
hwp.storage.value.to_f
+            quota.total_instances += 1
+          elsif ACTIVE_STATES.include?(state_from) && 
!ACTIVE_STATES.include?(state_to)
+            quota.total_storage = quota.total_storage.to_f - 
hwp.storage.value.to_f
+            quota.total_instances -= 1
+          end
+       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..43ab9a2 100644
--- a/src/app/models/quota.rb
+++ b/src/app/models/quota.rb
@@ -22,4 +22,53 @@
 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
+
+  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)
+    # TODO Fix: When this returns failed, instance gets deleted at some point 
from database.  It should be kept for audit
+    hwp = instance.hardware_profile
+
+    potential_total_storage = total_storage.to_f + hwp.storage.value.to_f
+    potential_total_instances = total_instances + 1
+
+    if maximum_total_instances >= potential_total_instances && 
maximum_total_storage.to_f >= potential_total_storage.to_f
+      return true
+    end
+    return false
+  end
+
+  def can_start_instance?(instance)
+    hwp = instance.hardware_profile
+
+    potential_running_instances = running_instances + 1
+    potential_running_memory = running_memory.to_f + hwp.memory.value.to_f
+    potential_running_cpus = running_cpus.to_f + hwp.cpu.value.to_f
+
+    if maximum_running_instances >= potential_running_instances && 
maximum_running_memory.to_f >= potential_running_memory && 
maximum_running_cpus.to_f >= potential_running_cpus
+      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.to_f < running_memory.to_f
+    errors.add("maximum_running_cpus", "cannot be less than the current 
running CPUs") if maximum_running_cpus.to_f < running_cpus.to_f
+    errors.add("maximum_total_storage", "cannot be less than the current total 
storage") if maximum_total_storage.to_f < total_storage.to_f
+    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/hardware_profile.rb 
b/src/spec/factories/hardware_profile.rb
index 9b19f01..4e6a85f 100644
--- a/src/spec/factories/hardware_profile.rb
+++ b/src/spec/factories/hardware_profile.rb
@@ -7,7 +7,7 @@ Factory.define :mock_hwp1, :parent => :hardware_profile do |p|
   p.storage { |p| p.association(:mock_hwp1_storage) }
   p.cpu { |p| p.association(:mock_hwp1_cpu) }
   p.architecture { |p| p.association(:mock_hwp1_arch) }
-  p.external_key 'mock_hwp1_key'
+  p.sequence(:external_key) { |n| "mock_hwp1_key#{n}" }
 end
 
 Factory.define :mock_hwp2, :parent => :hardware_profile do |p|
diff --git a/src/spec/factories/instance.rb b/src/spec/factories/instance.rb
index 3a453c3..f73a691 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 => :mock_hwp1
+  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/factories/quota.rb b/src/spec/factories/quota.rb
new file mode 100644
index 0000000..3f519ca
--- /dev/null
+++ b/src/spec/factories/quota.rb
@@ -0,0 +1,15 @@
+Factory.define :quota do |f|
+  f.maximum_running_instances 10
+  f.maximum_running_memory "10240"
+  f.maximum_running_cpus 20
+  f.maximum_total_instances 15
+  f.maximum_total_storage "8500"
+end
+
+Factory.define :full_quota, :parent => :quota do |f|
+  f.running_instances 10
+  f.running_memory "10240"
+  f.running_cpus 20
+  f.total_instances 15
+  f.total_storage "8500"
+end
\ No newline at end of file
diff --git a/src/spec/models/instance_observer_spec.rb 
b/src/spec/models/instance_observer_spec.rb
index 9e3dcc7..6bcb4d0 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,72 @@ describe InstanceObserver do
     @instance.acc_stopped_time.should >= 1
     @instance.acc_stopped_time.should <= 2
   end
+
+  it "should not update quota on pool and cloud account when an instance is 
state new" do
+    [...@cloud_account_quota, @pool_quota].each do |quota|
+      quota = Quota.find(quota)
+
+      quota.total_instances.should == 0
+      quota.total_storage.to_f.should == 0.to_f
+    end
+  end
+
+  it "should update quota on pool and cloud account when an instance fgoes to 
state pending" do
+    [...@cloud_account_quota, @pool_quota].each do |quota|
+      @instance.state = Instance::STATE_PENDING
+      @instance.save
+
+      quota = Quota.find(quota)
+
+      quota.total_instances.should == 1
+      quota.total_storage.to_f.should == @hwp.storage.value.to_f
+    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.to_f.should == 0.to_f
+    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.to_f.should == @hwp.memory.value.to_f
+      quota.running_cpus.to_f.should == @hwp.cpu.value.to_f
+
+      quota.total_storage.to_f.should == @hwp.storage.value.to_f
+      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.to_f.should == 0.to_f
+
+      quota.total_storage.to_f.should == @hwp.storage.value.to_f
+      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
-- 
1.6.6.1

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

Reply via email to