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