Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add method for restricting to accessible storages #18391

Merged
merged 2 commits into from
Jan 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/models/host.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class Host < ApplicationRecord
has_many :miq_templates, :inverse_of => :host
has_many :host_storages, :dependent => :destroy
has_many :storages, :through => :host_storages
has_many :writable_accessible_host_storages, -> { writable_accessible }, :class_name => "HostStorage"
has_many :writable_accessible_storages, :through => :writable_accessible_host_storages, :source => :storage
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good to replace the writable_storages and read_only_storages with associations using these scopes, not sure if in this PR or a followup since it isn't really related

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/cc @NickLaMuro since you modified the writable_storages method for performance, wdyt?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reference (since I had to go find it), that performance change was done here:

#17354

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, so I think my to cents with this is that you probably want to leave those methods (writable_storages and read_only_storages), and just change them to use the scopes:

 def writable_storages
    if host_storages.loaded? && host_storages.all? { |hs| hs.association(:storage).loaded? }
       host_storages.reject(&:read_only).map(&:storage)
     else
-      storages.where(:host_storages => {:read_only => [false, nil]})
+      storages.writable  # I think my pseudo code here is wrong...
    end
  end

Or, if we did create a writable_storages scope, I would suggest possibly doing the following to maintain the performance benefits of #17354 :

has_many                  :writable_host_storages, -> { writable }, :class_name => "HostStorage"
has_many                  :writable_storages, :through => :writable_host_storages, :source => :storage

# overwrite default reader method
def writable_storages
  if association(:writable_storages).loaded? # use already loaded the relationship (faster)
    association(:writable_storages).reader
  # can use an existing relationship to avoid another query and instantiation of other records (requires loop)
  elsif host_storages.loaded? && host_storages.all? { |hs| hs.association(:storage).loaded? }
    host_storages.reject(&:read_only).map(&:storage)
  else # call the relationship
    association(:writable_storages).reader
  end
end

Let me know if any of this needs a further explanation... trying to avoid a 📖

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright I'll leave it as is 😄

has_many :host_switches, :dependent => :destroy
has_many :switches, :through => :host_switches
has_many :lans, :through => :switches
Expand Down
12 changes: 12 additions & 0 deletions app/models/host_storage.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
class HostStorage < ApplicationRecord
belongs_to :host
belongs_to :storage

scope :writable, -> { where(:read_only => [nil, false]) }
scope :read_only, -> { where(:read_only => true) }

scope :accessible, -> { where(:accessible => [true, nil]) }
scope :inaccessible, -> { where(:accessible => false) }

class << self
def writable_accessible
writable.accessible
end
end
end
2 changes: 1 addition & 1 deletion app/models/miq_request_workflow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1069,7 +1069,7 @@ def allowed_storages(_options = {})
MiqPreloader.preload(hosts, :storages => {}, :host_storages => :storage)

storages = hosts.each_with_object({}) do |host, hash|
host.writable_storages.each { |s| hash[s.id] = s }
host.writable_accessible_storages.each { |s| hash[s.id] = s }
end.values
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change will help with the Provisioning UI and Automate callers to eligible_storages.

Unfortunately the vmware_best_fit_least_utilized placement method in Automate provisioning uses writable_storages directly. (See VM/Provisioning/Placement.class/methods/vmware_best_fit_least_utilized.rb)

@tinaafitz As part of fixing this BZ we should look into changing these calls to use allowed_storages instead.

selected_storage_profile_id = get_value(@values[:placement_storage_profile])
if selected_storage_profile_id
Expand Down
2 changes: 1 addition & 1 deletion app/models/vm_or_template/operations/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def raw_add_disk(disk_name, disk_size_mb, options = {})
raise _("VM has no EMS, unable to add disk") unless ext_management_system
if options[:datastore]
datastore = ext_management_system.hosts.collect do |h|
h.writable_storages.find_by(:name => options[:datastore])
h.writable_accessible_storages.find_by(:name => options[:datastore])
end.uniq.compact.first
raise _("Datastore does not exist or cannot be accessed, unable to add disk") unless datastore
end
Expand Down
16 changes: 3 additions & 13 deletions spec/models/vm_or_template/operations/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,13 @@

context "when ext_management_system exists" do
let(:vm) { FactoryBot.create(:vm_or_template, :ext_management_system => ems) }
let(:ems) { FactoryBot.create(:ext_management_system) }
let(:ems) { FactoryBot.create(:ext_management_system, :with_authentication) }
let(:storage_name) { "test_storage" }
let(:storage) { FactoryBot.create(:storage, :name => storage_name) }
let(:storages) { double("storages") }
let(:host) { double("host", :writable_storages => storages) }
let(:hosts) { [host] }

before do
allow(ems).to receive(:hosts).and_return(hosts)
end
let!(:host) { FactoryBot.create(:host, :ext_management_system => ems).tap { |h| h.host_storages.create!(:storage => storage) } }
NickLaMuro marked this conversation as resolved.
Show resolved Hide resolved

context "when storage exists" do
it "adds a disk on the storage" do
allow(storages).to receive(:find_by).with(:name => storage_name).and_return(storage)
allow(ems).to receive(:authentication_status_ok?).and_return(true)
allow(ems).to receive(:vm_add_disk)

expected_options = {
Expand All @@ -48,10 +40,8 @@

context "when storage does not exist" do
it "raises an exception when doesn't find storage by its name" do
allow(storages).to receive(:find_by).with(:name => storage_name).and_return(nil)

message = "Datastore does not exist or cannot be accessed, unable to add disk"
expect { vm.raw_add_disk(disk_name, disk_size, :datastore => storage_name) }.to raise_error(message)
expect { vm.raw_add_disk(disk_name, disk_size, :datastore => "wrong_storage_name") }.to raise_error(message)
end
end
end
Expand Down