Note that this is not part of the condor infrastructure -- it's an interim solution to handle scheduling across providers until the full scheduling capabilities are put into use with Condor integration.
The full list of provider accounts is filtered by realm, hardware profile, and image -- so only those accounts that map validly to the "front-end/engine" realm/hwp/image choices of the user are considered. After this, we filter out those cloud accounts that do not have the available capacity to schedule the instance creation request.
As we do not yet have a UI to map images and HWPs from different providers to the same front-end image or HWP, multi-provider scheduling is only possible across accounts in the same provider, or across providers after manually mapping equivalent images and HWPs.
For a simple test case with exactly two providers in the AR database with the same set of available images and HWPs (this will only be true for two mock providers, or if you've set up two separate provider objects pointing to the same driver -- or two drivers pointing to the same cloud impl), the following should take your separately-mapped resources and map them together on the front end:
Provider.last.images.each do |image| if image.aggregator_images[0].provider_images.size < 2 image.aggregator_images[0].delete image.aggregator_images = [Provider.first.images.find_by_external_key(image.external_key).aggregator_images[0]] image.save end agg_image = image.aggregator_images[0] agg_image.name = image.name agg_image.external_key = image.external_key agg_image.save end
Provider.last.hardware_profiles.each do |hardware_profile| if hardware_profile.aggregator_hardware_profiles[0].provider_hardware_profiles.size < 2 hardware_profile.aggregator_hardware_profiles[0].delete hardware_profile.aggregator_hardware_profiles = [Provider.first.hardware_profiles.find_by_external_key(hardware_profile.external_key).aggregator_hardware_profiles[0]] hardware_profile.save end agg_hwp = hardware_profile.aggregator_hardware_profiles[0] agg_hwp.name = hardware_profile.name agg_hwp.external_key = hardware_profile.external_key agg_hwp.save end
Signed-off-by: Scott Seago sseago@redhat.com --- src/app/models/instance.rb | 14 +++--- src/app/util/taskomatic.rb | 77 ++++++++++++++++++++++++---------- src/app/views/instance/new.html.erb | 2 +- 3 files changed, 62 insertions(+), 31 deletions(-)
diff --git a/src/app/models/instance.rb b/src/app/models/instance.rb index 21a5cef..56019ff 100644 --- a/src/app/models/instance.rb +++ b/src/app/models/instance.rb @@ -45,8 +45,6 @@ class Instance < ActiveRecord::Base validates_uniqueness_of :name, :scope => :pool_id validates_length_of :name, :maximum => 1024
- # FIXME: for now, hardware profile is required, realm is optional, although for RHEV-M, - # hardware profile may be optional too validates_presence_of :hardware_profile_id validates_presence_of :image_id
@@ -114,11 +112,13 @@ class Instance < ActiveRecord::Base end
def front_end_realm=(realm_name) - provider_name, tmpstr = realm_name.split(Realm::AGGREGATOR_REALM_PROVIDER_DELIMITER,2) - account_name, realm_name = tmpstr.split(Realm::AGGREGATOR_REALM_ACCOUNT_DELIMITER,2) - provider = Provider.find_by_name(provider_name) - self.cloud_account = provider.cloud_accounts.find_by_username(account_name) - self.realm = provider.realms.find_by_name(realm_name) unless realm_name.nil? + unless realm_name.nil? or realm_name.empty? + provider_name, tmpstr = realm_name.split(Realm::AGGREGATOR_REALM_PROVIDER_DELIMITER,2) + account_name, realm_name = tmpstr.split(Realm::AGGREGATOR_REALM_ACCOUNT_DELIMITER,2) + provider = Provider.find_by_name(provider_name) + self.cloud_account = provider.cloud_accounts.find_by_username(account_name) + self.realm = provider.realms.find_by_name(realm_name) unless realm_name.nil? + end end
# Returns the total time that this instance has been in the state diff --git a/src/app/util/taskomatic.rb b/src/app/util/taskomatic.rb index 3277c4d..08da3d2 100644 --- a/src/app/util/taskomatic.rb +++ b/src/app/util/taskomatic.rb @@ -50,40 +50,71 @@ class Taskomatic # can go to state running. # TODO Modify this to handle generic case for providers pool = @task.instance.pool - cloud_account = @task.instance.cloud_account
- [pool, cloud_account].each do |parent| - quota = parent.quota - if quota - if !quota.can_create_instance?(@task.instance) || !quota.can_start_instance?(@task.instance) - @task.state = Task::STATE_FAILED - @task.instance.state = Instance::STATE_CREATE_FAILED + #find matching cloud account + if @task.instance.cloud_account + cloud_accounts = [@task.instance.cloud_account] + else + # FIXME: this provides a predictable order -- scheduler will eventually + # do something entirely different to determine preference + cloud_accounts = CloudAccount.find(:all, :order => 'created_at') + end
- if parent.class == Pool - @task.failure_code = Task::FAILURE_OVER_POOL_QUOTA - elsif parent.class == CloudAccount - @task.failure_code = Task::FAILURE_OVER_CLOUD_ACCOUNT_QUOTA - end + if @task.instance.image.provider_image? + image_providers = Set.new([@task.instance.image]) + else + image_providers = Set.new(@task.instance.image.provider_images. + collect { |image| image.provider}) + end
- @task.save! - @task.instance.save! + if @task.instance.hardware_profile.provider_hardware_profile? + hwp_providers = Set.new([@task.instance.hardware_profile]) + else + hwp_providers = Set.new(@task.instance.hardware_profile.provider_hardware_profiles. + collect { |hwp| hwp.provider }) + end
- return @task - end - end + matching_providers = hwp_providers & image_providers + cloud_accounts.delete_if do |acct| + !matching_providers.include?(acct.provider) or + (acct.quota and + (!acct.quota.can_create_instance?(@task.instance) or + !acct.quota.can_start_instance?(@task.instance))) + end + + if pool.quota and + (!pool.quota.can_create_instance?(@task.instance) or + !pool.quota.can_start_instance?(@task.instance)) + @task.failure_code = Task::FAILURE_OVER_POOL_QUOTA + end + @task.failure_code = Task::FAILURE_PROVIDER_NOT_FOUND if cloud_accounts.empty? + + unless @task.failure_code.nil? + @task.state = Task::STATE_FAILED + @task.instance.state = Instance::STATE_CREATE_FAILED + @task.save! + @task.instance.save! + return @task end
begin + # take first matching cloud account + @task.instance.cloud_account = cloud_accounts[0] client = @task.instance.cloud_account.connect realm = @task.instance.realm.external_key rescue nil
# Map aggregator HWP/image to back-end provider HWP/image in instance unless @task.instance.image.provider_image? - @task.instance.image = @task.instance.image.provider_images[0] + @task.instance.image = @task.instance.image.provider_images. + find(:first, :conditions => {:provider_id => + @task.instance.cloud_account.provider_id}) end
unless @task.instance.hardware_profile.provider_hardware_profile? - @task.instance.hardware_profile = @task.instance.hardware_profile.provider_hardware_profiles[0] + @task.instance.hardware_profile = @task.instance.hardware_profile. + provider_hardware_profiles. + find(:first, :conditions => {:provider_id => + @task.instance.cloud_account.provider_id}) end
@task.state = Task::STATE_PENDING diff --git a/src/app/views/instance/new.html.erb b/src/app/views/instance/new.html.erb index 894d1b6..74096a2 100644 --- a/src/app/views/instance/new.html.erb +++ b/src/app/views/instance/new.html.erb @@ -55,7 +55,7 @@ <% end %> <% if @instance.pool and @instance.pool.realms.size > 0 %> <li><label>Realm<span>Choose a realm</span></label> - <%= select("instance", "front_end_realm", @instance.pool.realms, { :include_blank => false }) %> + <%= select("instance", "front_end_realm", @instance.pool.realms, { :include_blank => true }) %> </li> <% end %> </ul>
deltacloud-devel@lists.fedorahosted.org