inventory_refresh 0.3.5 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.codeclimate.yml +25 -30
- data/.github/workflows/ci.yaml +58 -0
- data/.rubocop.yml +3 -3
- data/.rubocop_cc.yml +3 -4
- data/.rubocop_local.yml +5 -2
- data/.whitesource +3 -0
- data/CHANGELOG.md +19 -0
- data/Gemfile +10 -4
- data/README.md +1 -2
- data/Rakefile +2 -2
- data/inventory_refresh.gemspec +9 -10
- data/lib/inventory_refresh/application_record_iterator.rb +25 -12
- data/lib/inventory_refresh/graph/topological_sort.rb +24 -26
- data/lib/inventory_refresh/graph.rb +2 -2
- data/lib/inventory_refresh/inventory_collection/builder.rb +37 -15
- data/lib/inventory_refresh/inventory_collection/data_storage.rb +9 -0
- data/lib/inventory_refresh/inventory_collection/helpers/initialize_helper.rb +147 -38
- data/lib/inventory_refresh/inventory_collection/helpers/questions_helper.rb +48 -4
- data/lib/inventory_refresh/inventory_collection/index/proxy.rb +35 -3
- data/lib/inventory_refresh/inventory_collection/index/type/base.rb +8 -0
- data/lib/inventory_refresh/inventory_collection/index/type/local_db.rb +2 -0
- data/lib/inventory_refresh/inventory_collection/index/type/skeletal.rb +1 -0
- data/lib/inventory_refresh/inventory_collection/reference.rb +1 -0
- data/lib/inventory_refresh/inventory_collection/references_storage.rb +17 -0
- data/lib/inventory_refresh/inventory_collection/scanner.rb +91 -3
- data/lib/inventory_refresh/inventory_collection/serialization.rb +16 -10
- data/lib/inventory_refresh/inventory_collection.rb +122 -64
- data/lib/inventory_refresh/inventory_object.rb +74 -40
- data/lib/inventory_refresh/inventory_object_lazy.rb +17 -10
- data/lib/inventory_refresh/null_logger.rb +2 -2
- data/lib/inventory_refresh/persister.rb +43 -93
- data/lib/inventory_refresh/save_collection/base.rb +4 -2
- data/lib/inventory_refresh/save_collection/saver/base.rb +114 -15
- data/lib/inventory_refresh/save_collection/saver/batch.rb +17 -0
- data/lib/inventory_refresh/save_collection/saver/concurrent_safe_batch.rb +129 -51
- data/lib/inventory_refresh/save_collection/saver/default.rb +57 -0
- data/lib/inventory_refresh/save_collection/saver/partial_upsert_helper.rb +2 -19
- data/lib/inventory_refresh/save_collection/saver/retention_helper.rb +68 -3
- data/lib/inventory_refresh/save_collection/saver/sql_helper.rb +125 -0
- data/lib/inventory_refresh/save_collection/saver/sql_helper_update.rb +10 -6
- data/lib/inventory_refresh/save_collection/saver/sql_helper_upsert.rb +28 -16
- data/lib/inventory_refresh/save_collection/sweeper.rb +17 -93
- data/lib/inventory_refresh/save_collection/topological_sort.rb +5 -5
- data/lib/inventory_refresh/save_inventory.rb +5 -12
- data/lib/inventory_refresh/target.rb +73 -0
- data/lib/inventory_refresh/target_collection.rb +92 -0
- data/lib/inventory_refresh/version.rb +1 -1
- data/lib/inventory_refresh.rb +2 -0
- metadata +42 -39
- data/.travis.yml +0 -23
- data/lib/inventory_refresh/exception.rb +0 -8
| @@ -42,12 +42,64 @@ module InventoryRefresh | |
| 42 42 | 
             
                # Transforms InventoryObject object data into hash format with keys that are column names and resolves correct
         | 
| 43 43 | 
             
                # values of the foreign keys (even the polymorphic ones)
         | 
| 44 44 | 
             
                #
         | 
| 45 | 
            -
                # @param  | 
| 45 | 
            +
                # @param inventory_collection_scope [InventoryRefresh::InventoryCollection] parent InventoryCollection object
         | 
| 46 | 
            +
                # @return [Hash] Data in DB format
         | 
| 47 | 
            +
                def attributes(inventory_collection_scope = nil)
         | 
| 48 | 
            +
                  # We should explicitly pass a scope, since the inventory_object can be mapped to more InventoryCollections with
         | 
| 49 | 
            +
                  # different blacklist and whitelist. The generic code always passes a scope.
         | 
| 50 | 
            +
                  inventory_collection_scope ||= inventory_collection
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  attributes_for_saving = {}
         | 
| 53 | 
            +
                  # First transform the values
         | 
| 54 | 
            +
                  data.each do |key, value|
         | 
| 55 | 
            +
                    if !allowed?(inventory_collection_scope, key)
         | 
| 56 | 
            +
                      next
         | 
| 57 | 
            +
                    elsif value.kind_of?(Array) && value.any? { |x| loadable?(x) }
         | 
| 58 | 
            +
                      # Lets fill also the original data, so other InventoryObject referring to this attribute gets the right
         | 
| 59 | 
            +
                      # result
         | 
| 60 | 
            +
                      data[key] = value.compact.map(&:load).compact
         | 
| 61 | 
            +
                      # We can use built in _ids methods to assign array of ids into has_many relations. So e.g. the :key_pairs=
         | 
| 62 | 
            +
                      # relation setter will become :key_pair_ids=
         | 
| 63 | 
            +
                      attributes_for_saving["#{key.to_s.singularize}_ids".to_sym] = data[key].map(&:id).compact.uniq
         | 
| 64 | 
            +
                    elsif loadable?(value) || inventory_collection_scope.association_to_foreign_key_mapping[key]
         | 
| 65 | 
            +
                      # Lets fill also the original data, so other InventoryObject referring to this attribute gets the right
         | 
| 66 | 
            +
                      # result
         | 
| 67 | 
            +
                      data[key] = value.load if value.respond_to?(:load)
         | 
| 68 | 
            +
                      if (foreign_key = inventory_collection_scope.association_to_foreign_key_mapping[key])
         | 
| 69 | 
            +
                        # We have an association to fill, lets fill also the :key, cause some other InventoryObject can refer to it
         | 
| 70 | 
            +
                        record_id                                 = data[key].try(:id)
         | 
| 71 | 
            +
                        attributes_for_saving[foreign_key.to_sym] = record_id
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                        if (foreign_type = inventory_collection_scope.association_to_foreign_type_mapping[key])
         | 
| 74 | 
            +
                          # If we have a polymorphic association, we need to also fill a base class name, but we want to nullify it
         | 
| 75 | 
            +
                          # if record_id is missing
         | 
| 76 | 
            +
                          base_class = data[key].try(:base_class_name) || data[key].class.try(:base_class).try(:name)
         | 
| 77 | 
            +
                          attributes_for_saving[foreign_type.to_sym] = record_id ? base_class : nil
         | 
| 78 | 
            +
                        end
         | 
| 79 | 
            +
                      elsif data[key].kind_of?(::InventoryRefresh::InventoryObject)
         | 
| 80 | 
            +
                        # We have an association to fill but not an Activerecord association, so e.g. Ancestry, lets just load
         | 
| 81 | 
            +
                        # it here. This way of storing ancestry is ineffective in DB call count, but RAM friendly
         | 
| 82 | 
            +
                        attributes_for_saving[key.to_sym] = data[key].base_class_name.constantize.find_by(:id => data[key].id)
         | 
| 83 | 
            +
                      else
         | 
| 84 | 
            +
                        # We have a normal attribute to fill
         | 
| 85 | 
            +
                        attributes_for_saving[key.to_sym] = data[key]
         | 
| 86 | 
            +
                      end
         | 
| 87 | 
            +
                    else
         | 
| 88 | 
            +
                      attributes_for_saving[key.to_sym] = value
         | 
| 89 | 
            +
                    end
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  attributes_for_saving
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                # Transforms InventoryObject object data into hash format with keys that are column names and resolves correct
         | 
| 96 | 
            +
                # values of the foreign keys (even the polymorphic ones)
         | 
| 97 | 
            +
                #
         | 
| 46 98 | 
             
                # @param inventory_collection_scope [InventoryRefresh::InventoryCollection] parent InventoryCollection object
         | 
| 47 99 | 
             
                # @param all_attribute_keys [Array<Symbol>] Attribute keys we will modify based on object's data
         | 
| 48 100 | 
             
                # @param inventory_object [InventoryRefresh::InventoryObject] InventoryObject object owning these attributes
         | 
| 49 101 | 
             
                # @return [Hash] Data in DB format
         | 
| 50 | 
            -
                def  | 
| 102 | 
            +
                def attributes_with_keys(inventory_collection_scope = nil, all_attribute_keys = [], inventory_object = nil)
         | 
| 51 103 | 
             
                  # We should explicitly pass a scope, since the inventory_object can be mapped to more InventoryCollections with
         | 
| 52 104 | 
             
                  # different blacklist and whitelist. The generic code always passes a scope.
         | 
| 53 105 | 
             
                  inventory_collection_scope ||= inventory_collection
         | 
| @@ -96,8 +148,8 @@ module InventoryRefresh | |
| 96 148 | 
             
                def assign_attributes(attributes)
         | 
| 97 149 | 
             
                  attributes.each do |k, v|
         | 
| 98 150 | 
             
                    # We don't want timestamps or resource versions to be overwritten here, since those are driving the conditions
         | 
| 99 | 
            -
                    next if %i | 
| 100 | 
            -
                    next if %i | 
| 151 | 
            +
                    next if %i[resource_timestamps resource_timestamps_max resource_timestamp].include?(k)
         | 
| 152 | 
            +
                    next if %i[resource_counters resource_counters_max resource_counter].include?(k)
         | 
| 101 153 |  | 
| 102 154 | 
             
                    if data[:resource_timestamp] && attributes[:resource_timestamp]
         | 
| 103 155 | 
             
                      assign_only_newest(:resource_timestamp, :resource_timestamps, attributes, data, k, v)
         | 
| @@ -146,44 +198,14 @@ module InventoryRefresh | |
| 146 198 | 
             
                      end
         | 
| 147 199 | 
             
                    end
         | 
| 148 200 |  | 
| 149 | 
            -
                     | 
| 150 | 
            -
             | 
| 151 | 
            -
             | 
| 152 | 
            -
                       | 
| 201 | 
            +
                    next if defined_methods.include?(attr.to_sym)
         | 
| 202 | 
            +
             | 
| 203 | 
            +
                    define_method(attr) do
         | 
| 204 | 
            +
                      data[attr]
         | 
| 153 205 | 
             
                    end
         | 
| 154 206 | 
             
                  end
         | 
| 155 207 | 
             
                end
         | 
| 156 208 |  | 
| 157 | 
            -
                # Return true if the attribute is allowed to be saved into the DB
         | 
| 158 | 
            -
                #
         | 
| 159 | 
            -
                # @param inventory_collection_scope [InventoryRefresh::InventoryCollection] InventoryCollection object owning the
         | 
| 160 | 
            -
                #        attribute
         | 
| 161 | 
            -
                # @param key [Symbol] attribute name
         | 
| 162 | 
            -
                # @return true if the attribute is allowed to be saved into the DB
         | 
| 163 | 
            -
                def self.allowed?(inventory_collection_scope, key)
         | 
| 164 | 
            -
                  foreign_to_association = (inventory_collection_scope.foreign_key_to_association_mapping[key] ||
         | 
| 165 | 
            -
                    inventory_collection_scope.foreign_type_to_association_mapping[key])
         | 
| 166 | 
            -
             | 
| 167 | 
            -
                  return false if inventory_collection_scope.attributes_blacklist.present? &&
         | 
| 168 | 
            -
                    (inventory_collection_scope.attributes_blacklist.include?(key) ||
         | 
| 169 | 
            -
                      (foreign_to_association && inventory_collection_scope.attributes_blacklist.include?(foreign_to_association)))
         | 
| 170 | 
            -
             | 
| 171 | 
            -
                  return false if inventory_collection_scope.attributes_whitelist.present? &&
         | 
| 172 | 
            -
                    (!inventory_collection_scope.attributes_whitelist.include?(key) &&
         | 
| 173 | 
            -
                      (!foreign_to_association || (foreign_to_association && inventory_collection_scope.attributes_whitelist.include?(foreign_to_association))))
         | 
| 174 | 
            -
             | 
| 175 | 
            -
                  true
         | 
| 176 | 
            -
                end
         | 
| 177 | 
            -
             | 
| 178 | 
            -
                # Return true if the object is loadable, which we determine by a list of loadable classes.
         | 
| 179 | 
            -
                #
         | 
| 180 | 
            -
                # @param value [Object] object we test
         | 
| 181 | 
            -
                # @return true if the object is loadable
         | 
| 182 | 
            -
                def self.loadable?(value)
         | 
| 183 | 
            -
                  value.kind_of?(::InventoryRefresh::InventoryObjectLazy) || value.kind_of?(::InventoryRefresh::InventoryObject) ||
         | 
| 184 | 
            -
                    value.kind_of?(::InventoryRefresh::ApplicationRecordReference)
         | 
| 185 | 
            -
                end
         | 
| 186 | 
            -
             | 
| 187 209 | 
             
                private
         | 
| 188 210 |  | 
| 189 211 | 
             
                # Assigns value based on the version attributes. If versions are specified, it asigns attribute only if it's
         | 
| @@ -261,7 +283,18 @@ module InventoryRefresh | |
| 261 283 | 
             
                # @param key [Symbol] attribute name
         | 
| 262 284 | 
             
                # @return true if the attribute is allowed to be saved into the DB
         | 
| 263 285 | 
             
                def allowed?(inventory_collection_scope, key)
         | 
| 264 | 
            -
                   | 
| 286 | 
            +
                  foreign_to_association = inventory_collection_scope.foreign_key_to_association_mapping[key] ||
         | 
| 287 | 
            +
                                           inventory_collection_scope.foreign_type_to_association_mapping[key]
         | 
| 288 | 
            +
             | 
| 289 | 
            +
                  return false if inventory_collection_scope.attributes_blacklist.present? &&
         | 
| 290 | 
            +
                                  (inventory_collection_scope.attributes_blacklist.include?(key) ||
         | 
| 291 | 
            +
                                    (foreign_to_association && inventory_collection_scope.attributes_blacklist.include?(foreign_to_association)))
         | 
| 292 | 
            +
             | 
| 293 | 
            +
                  return false if inventory_collection_scope.attributes_whitelist.present? &&
         | 
| 294 | 
            +
                                  (!inventory_collection_scope.attributes_whitelist.include?(key) &&
         | 
| 295 | 
            +
                                    (!foreign_to_association || (foreign_to_association && inventory_collection_scope.attributes_whitelist.include?(foreign_to_association))))
         | 
| 296 | 
            +
             | 
| 297 | 
            +
                  true
         | 
| 265 298 | 
             
                end
         | 
| 266 299 |  | 
| 267 300 | 
             
                # Return true if the object is loadable, which we determine by a list of loadable classes.
         | 
| @@ -269,7 +302,8 @@ module InventoryRefresh | |
| 269 302 | 
             
                # @param value [Object] object we test
         | 
| 270 303 | 
             
                # @return true if the object is loadable
         | 
| 271 304 | 
             
                def loadable?(value)
         | 
| 272 | 
            -
                   | 
| 305 | 
            +
                  value.kind_of?(::InventoryRefresh::InventoryObjectLazy) || value.kind_of?(::InventoryRefresh::InventoryObject) ||
         | 
| 306 | 
            +
                    value.kind_of?(::InventoryRefresh::ApplicationRecordReference)
         | 
| 273 307 | 
             
                end
         | 
| 274 308 | 
             
              end
         | 
| 275 309 | 
             
            end
         | 
| @@ -30,6 +30,7 @@ module InventoryRefresh | |
| 30 30 |  | 
| 31 31 | 
             
                # @return [String] stringified reference
         | 
| 32 32 | 
             
                def to_s
         | 
| 33 | 
            +
                  # TODO(lsmola) do we need this method?
         | 
| 33 34 | 
             
                  stringified_reference
         | 
| 34 35 | 
             
                end
         | 
| 35 36 |  | 
| @@ -70,6 +71,10 @@ module InventoryRefresh | |
| 70 71 |  | 
| 71 72 | 
             
                # @return [Boolean] true if the key is an association on inventory_collection_scope model class
         | 
| 72 73 | 
             
                def association?(key)
         | 
| 74 | 
            +
                  # TODO(lsmola) remove this if there will be better dependency scan, probably with transitive dependencies filled
         | 
| 75 | 
            +
                  # in a second pass, then we can get rid of this hardcoded symbols. Right now we are not able to introspect these.
         | 
| 76 | 
            +
                  return true if [:parent, :genealogy_parent].include?(key)
         | 
| 77 | 
            +
             | 
| 73 78 | 
             
                  inventory_collection.dependency_attributes.key?(key) ||
         | 
| 74 79 | 
             
                    !inventory_collection.association_to_foreign_key_mapping[key].nil?
         | 
| 75 80 | 
             
                end
         | 
| @@ -95,7 +100,7 @@ module InventoryRefresh | |
| 95 100 |  | 
| 96 101 | 
             
                private
         | 
| 97 102 |  | 
| 98 | 
            -
                delegate :saved?, :saver_strategy, :skeletal_primary_index, :to => :inventory_collection
         | 
| 103 | 
            +
                delegate :parallel_safe?, :saved?, :saver_strategy, :skeletal_primary_index, :targeted?, :to => :inventory_collection
         | 
| 99 104 | 
             
                delegate :nested_secondary_index?, :primary?, :full_reference, :keys, :primary?, :to => :reference
         | 
| 100 105 |  | 
| 101 106 | 
             
                attr_writer :reference
         | 
| @@ -107,18 +112,20 @@ module InventoryRefresh | |
| 107 112 | 
             
                #
         | 
| 108 113 | 
             
                # @return [InventoryRefresh::InventoryObject, NilClass] Returns pre-created InventoryObject or nil
         | 
| 109 114 | 
             
                def skeletal_precreate!
         | 
| 115 | 
            +
                  # We can do skeletal pre-create only for strategies using unique indexes. Since this can build records out of
         | 
| 116 | 
            +
                  # the given :arel scope, we will always attempt to create the recod, so we need unique index to avoid duplication
         | 
| 117 | 
            +
                  # of records.
         | 
| 118 | 
            +
                  return unless parallel_safe?
         | 
| 110 119 | 
             
                  # Pre-create only for strategies that will be persisting data, i.e. are not saved already
         | 
| 111 120 | 
             
                  return if saved?
         | 
| 112 121 | 
             
                  # We can only do skeletal pre-create for primary index reference, since that is needed to create DB unique index
         | 
| 113 | 
            -
                   | 
| 114 | 
            -
                   | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 117 | 
            -
                  #  | 
| 118 | 
            -
                  #  | 
| 119 | 
            -
                   | 
| 120 | 
            -
                  # smartly, since having nil means we will need to use different unique index for the upsert/update query.
         | 
| 121 | 
            -
                  return if keys.any? { |x| full_reference[x].nil? }
         | 
| 122 | 
            +
                  return unless primary?
         | 
| 123 | 
            +
                  # Full reference must be present
         | 
| 124 | 
            +
                  return if full_reference.blank?
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                  # To avoid pre-creating invalid records all fields of a primary key must have value
         | 
| 127 | 
            +
                  # TODO(lsmola) for composite keys, it's still valid to have one of the keys nil, figure out how to allow this
         | 
| 128 | 
            +
                  return if keys.any? { |x| full_reference[x].blank? }
         | 
| 122 129 |  | 
| 123 130 | 
             
                  skeletal_primary_index.build(full_reference)
         | 
| 124 131 | 
             
                end
         | 
| @@ -3,21 +3,18 @@ module InventoryRefresh | |
| 3 3 | 
             
                require 'json'
         | 
| 4 4 | 
             
                require 'yaml'
         | 
| 5 5 |  | 
| 6 | 
            -
                attr_reader :manager, :collections
         | 
| 6 | 
            +
                attr_reader :manager, :target, :collections
         | 
| 7 7 |  | 
| 8 | 
            -
                attr_accessor :refresh_state_uuid, :refresh_state_part_uuid, : | 
| 9 | 
            -
                attr_accessor :persister_started_at, :persister_finished_at,
         | 
| 10 | 
            -
                              :refresh_state_part_collected_at, :refresh_state_part_sent_at,
         | 
| 11 | 
            -
                              :refresh_state_started_at, :refresh_state_sent_at, :ingress_api_sent_at
         | 
| 8 | 
            +
                attr_accessor :refresh_state_uuid, :refresh_state_part_uuid, :total_parts, :sweep_scope, :retry_count, :retry_max
         | 
| 12 9 |  | 
| 13 10 | 
             
                # @param manager [ManageIQ::Providers::BaseManager] A manager object
         | 
| 14 | 
            -
                 | 
| 11 | 
            +
                # @param target [Object] A refresh Target object
         | 
| 12 | 
            +
                def initialize(manager, target = nil)
         | 
| 15 13 | 
             
                  @manager = manager
         | 
| 14 | 
            +
                  @target  = target
         | 
| 16 15 |  | 
| 17 16 | 
             
                  @collections = {}
         | 
| 18 17 |  | 
| 19 | 
            -
                  self.persister_started_at = Time.now.utc.to_datetime.to_s
         | 
| 20 | 
            -
             | 
| 21 18 | 
             
                  initialize_inventory_collections
         | 
| 22 19 | 
             
                end
         | 
| 23 20 |  | 
| @@ -48,6 +45,9 @@ module InventoryRefresh | |
| 48 45 | 
             
                                                       &block)
         | 
| 49 46 |  | 
| 50 47 | 
             
                  builder.add_properties(extra_properties) if extra_properties.present?
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  builder.add_properties({:manager_uuids => target.try(:references, collection_name) || []}, :if_missing) if targeted?
         | 
| 50 | 
            +
             | 
| 51 51 | 
             
                  builder.evaluate_lambdas!(self)
         | 
| 52 52 |  | 
| 53 53 | 
             
                  collections[collection_name] = builder.to_inventory_collection
         | 
| @@ -66,7 +66,7 @@ module InventoryRefresh | |
| 66 66 | 
             
                # @return [InventoryRefresh::InventoryCollection] returns a defined InventoryCollection or undefined method
         | 
| 67 67 | 
             
                def method_missing(method_name, *arguments, &block)
         | 
| 68 68 | 
             
                  if inventory_collections_names.include?(method_name)
         | 
| 69 | 
            -
                     | 
| 69 | 
            +
                    define_collections_reader(method_name)
         | 
| 70 70 | 
             
                    send(method_name)
         | 
| 71 71 | 
             
                  else
         | 
| 72 72 | 
             
                    super
         | 
| @@ -96,7 +96,7 @@ module InventoryRefresh | |
| 96 96 |  | 
| 97 97 | 
             
                # Returns serialized Persisted object to JSON
         | 
| 98 98 | 
             
                # @return [String] serialized Persisted object to JSON
         | 
| 99 | 
            -
                def to_json
         | 
| 99 | 
            +
                def to_json(*_args)
         | 
| 100 100 | 
             
                  JSON.dump(to_hash)
         | 
| 101 101 | 
             
                end
         | 
| 102 102 |  | 
| @@ -104,25 +104,21 @@ module InventoryRefresh | |
| 104 104 | 
             
                def to_hash
         | 
| 105 105 | 
             
                  collections_data = collections.map do |_, collection|
         | 
| 106 106 | 
             
                    next if collection.data.blank? &&
         | 
| 107 | 
            +
                            collection.targeted_scope.primary_references.blank? &&
         | 
| 108 | 
            +
                            collection.all_manager_uuids.nil? &&
         | 
| 107 109 | 
             
                            collection.skeletal_primary_index.index_data.blank?
         | 
| 108 110 |  | 
| 109 111 | 
             
                    collection.to_hash
         | 
| 110 112 | 
             
                  end.compact
         | 
| 111 113 |  | 
| 112 114 | 
             
                  {
         | 
| 113 | 
            -
                    :refresh_state_uuid | 
| 114 | 
            -
                    :refresh_state_part_uuid | 
| 115 | 
            -
                    : | 
| 116 | 
            -
                    : | 
| 117 | 
            -
                    : | 
| 118 | 
            -
                    : | 
| 119 | 
            -
                    : | 
| 120 | 
            -
                    :refresh_time_tracking           => refresh_time_tracking,
         | 
| 121 | 
            -
                    :retry_count                     => retry_count,
         | 
| 122 | 
            -
                    :retry_max                       => retry_max,
         | 
| 123 | 
            -
                    :total_parts                     => total_parts,
         | 
| 124 | 
            -
                    :sweep_scope                     => sweep_scope_to_hash(sweep_scope),
         | 
| 125 | 
            -
                    :collections                     => collections_data,
         | 
| 115 | 
            +
                    :refresh_state_uuid      => refresh_state_uuid,
         | 
| 116 | 
            +
                    :refresh_state_part_uuid => refresh_state_part_uuid,
         | 
| 117 | 
            +
                    :retry_count             => retry_count,
         | 
| 118 | 
            +
                    :retry_max               => retry_max,
         | 
| 119 | 
            +
                    :total_parts             => total_parts,
         | 
| 120 | 
            +
                    :sweep_scope             => sweep_scope,
         | 
| 121 | 
            +
                    :collections             => collections_data,
         | 
| 126 122 | 
             
                  }
         | 
| 127 123 | 
             
                end
         | 
| 128 124 |  | 
| @@ -131,70 +127,33 @@ module InventoryRefresh | |
| 131 127 | 
             
                  #
         | 
| 132 128 | 
             
                  # @param json_data [String] input JSON data
         | 
| 133 129 | 
             
                  # @return [ManageIQ::Providers::Inventory::Persister] Persister object loaded from a passed JSON
         | 
| 134 | 
            -
                  def from_json(json_data, manager)
         | 
| 135 | 
            -
                    from_hash(JSON.parse(json_data), manager)
         | 
| 130 | 
            +
                  def from_json(json_data, manager, target = nil)
         | 
| 131 | 
            +
                    from_hash(JSON.parse(json_data), manager, target)
         | 
| 136 132 | 
             
                  end
         | 
| 137 133 |  | 
| 138 134 | 
             
                  # Returns Persister object built from serialized data
         | 
| 139 135 | 
             
                  #
         | 
| 140 136 | 
             
                  # @param persister_data [Hash] serialized Persister object in hash
         | 
| 141 137 | 
             
                  # @return [ManageIQ::Providers::Inventory::Persister] Persister object built from serialized data
         | 
| 142 | 
            -
                  def from_hash(persister_data, manager)
         | 
| 143 | 
            -
                     | 
| 138 | 
            +
                  def from_hash(persister_data, manager, target = nil)
         | 
| 139 | 
            +
                    # TODO(lsmola) we need to pass serialized targeted scope here
         | 
| 140 | 
            +
                    target ||= InventoryRefresh::TargetCollection.new(:manager => manager)
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                    new(manager, target).tap do |persister|
         | 
| 144 143 | 
             
                      persister_data['collections'].each do |collection|
         | 
| 145 144 | 
             
                        inventory_collection = persister.collections[collection['name'].try(:to_sym)]
         | 
| 146 | 
            -
                        raise "Unrecognized InventoryCollection name: #{ | 
| 145 | 
            +
                        raise "Unrecognized InventoryCollection name: #{inventory_collection}" if inventory_collection.blank?
         | 
| 147 146 |  | 
| 148 147 | 
             
                        inventory_collection.from_hash(collection, persister.collections)
         | 
| 149 148 | 
             
                      end
         | 
| 150 149 |  | 
| 151 | 
            -
                      persister.refresh_state_uuid | 
| 152 | 
            -
                      persister.refresh_state_part_uuid | 
| 153 | 
            -
                      persister. | 
| 154 | 
            -
                      persister. | 
| 155 | 
            -
                      persister. | 
| 156 | 
            -
                      persister. | 
| 157 | 
            -
                      persister.ingress_api_sent_at             = persister_data['ingress_api_sent_at']
         | 
| 158 | 
            -
                      persister.retry_count                     = persister_data['retry_count']
         | 
| 159 | 
            -
                      persister.retry_max                       = persister_data['retry_max']
         | 
| 160 | 
            -
                      persister.total_parts                     = persister_data['total_parts']
         | 
| 161 | 
            -
                      persister.sweep_scope                     = sweep_scope_from_hash(persister_data['sweep_scope'], persister.collections)
         | 
| 162 | 
            -
                    end
         | 
| 163 | 
            -
                  end
         | 
| 164 | 
            -
             | 
| 165 | 
            -
                  private
         | 
| 166 | 
            -
             | 
| 167 | 
            -
                  def assert_sweep_scope!(sweep_scope)
         | 
| 168 | 
            -
                    return unless sweep_scope
         | 
| 169 | 
            -
             | 
| 170 | 
            -
                    allowed_format_message = "Allowed format of sweep scope is Array<String> or Hash{String => Hash}, got #{sweep_scope}"
         | 
| 171 | 
            -
             | 
| 172 | 
            -
                    if sweep_scope.kind_of?(Array)
         | 
| 173 | 
            -
                      return if sweep_scope.all? { |x| x.kind_of?(String) || x.kind_of?(Symbol) }
         | 
| 174 | 
            -
             | 
| 175 | 
            -
                      raise InventoryRefresh::Exception::SweeperScopeBadFormat, allowed_format_message
         | 
| 176 | 
            -
                    elsif sweep_scope.kind_of?(Hash)
         | 
| 177 | 
            -
                      return if sweep_scope.values.all? { |x| x.kind_of?(Array) }
         | 
| 178 | 
            -
             | 
| 179 | 
            -
                      raise InventoryRefresh::Exception::SweeperScopeBadFormat, allowed_format_message
         | 
| 150 | 
            +
                      persister.refresh_state_uuid      = persister_data['refresh_state_uuid']
         | 
| 151 | 
            +
                      persister.refresh_state_part_uuid = persister_data['refresh_state_part_uuid']
         | 
| 152 | 
            +
                      persister.retry_count             = persister_data['retry_count']
         | 
| 153 | 
            +
                      persister.retry_max               = persister_data['retry_max']
         | 
| 154 | 
            +
                      persister.total_parts             = persister_data['total_parts']
         | 
| 155 | 
            +
                      persister.sweep_scope             = persister_data['sweep_scope']
         | 
| 180 156 | 
             
                    end
         | 
| 181 | 
            -
             | 
| 182 | 
            -
                    raise InventoryRefresh::Exception::SweeperScopeBadFormat, allowed_format_message
         | 
| 183 | 
            -
                  end
         | 
| 184 | 
            -
             | 
| 185 | 
            -
                  def sweep_scope_from_hash(sweep_scope, available_inventory_collections)
         | 
| 186 | 
            -
                    assert_sweep_scope!(sweep_scope)
         | 
| 187 | 
            -
             | 
| 188 | 
            -
                    return sweep_scope unless sweep_scope.kind_of?(Hash)
         | 
| 189 | 
            -
             | 
| 190 | 
            -
                    sweep_scope.each_with_object({}) do |(k, v), obj|
         | 
| 191 | 
            -
                      inventory_collection = available_inventory_collections[k.try(:to_sym)]
         | 
| 192 | 
            -
                      raise "Unrecognized InventoryCollection name: #{k}" if inventory_collection.blank?
         | 
| 193 | 
            -
             | 
| 194 | 
            -
                      serializer = InventoryRefresh::InventoryCollection::Serialization.new(inventory_collection)
         | 
| 195 | 
            -
             | 
| 196 | 
            -
                      obj[k] = serializer.sweep_scope_from_hash(v, available_inventory_collections)
         | 
| 197 | 
            -
                    end.symbolize_keys!
         | 
| 198 157 | 
             
                  end
         | 
| 199 158 | 
             
                end
         | 
| 200 159 |  | 
| @@ -226,37 +185,28 @@ module InventoryRefresh | |
| 226 185 | 
             
                  nil
         | 
| 227 186 | 
             
                end
         | 
| 228 187 |  | 
| 229 | 
            -
                def  | 
| 188 | 
            +
                def saver_strategy
         | 
| 189 | 
            +
                  :default
         | 
| 190 | 
            +
                end
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                # Persisters for targeted refresh can override to true
         | 
| 193 | 
            +
                def targeted?
         | 
| 230 194 | 
             
                  false
         | 
| 231 195 | 
             
                end
         | 
| 232 196 |  | 
| 233 | 
            -
                def  | 
| 234 | 
            -
                   | 
| 197 | 
            +
                def assert_graph_integrity?
         | 
| 198 | 
            +
                  false
         | 
| 235 199 | 
             
                end
         | 
| 236 200 |  | 
| 237 201 | 
             
                # @return [Hash] kwargs shared for all InventoryCollection objects
         | 
| 238 202 | 
             
                def shared_options
         | 
| 239 203 | 
             
                  {
         | 
| 204 | 
            +
                    :saver_strategy         => saver_strategy,
         | 
| 240 205 | 
             
                    :strategy               => strategy,
         | 
| 206 | 
            +
                    :targeted               => targeted?,
         | 
| 241 207 | 
             
                    :parent                 => manager.presence,
         | 
| 242 208 | 
             
                    :assert_graph_integrity => assert_graph_integrity?,
         | 
| 243 | 
            -
                    :use_ar_object          => use_ar_object?,
         | 
| 244 209 | 
             
                  }
         | 
| 245 210 | 
             
                end
         | 
| 246 | 
            -
             | 
| 247 | 
            -
                private
         | 
| 248 | 
            -
             | 
| 249 | 
            -
                def sweep_scope_to_hash(sweep_scope)
         | 
| 250 | 
            -
                  return sweep_scope unless sweep_scope.kind_of?(Hash)
         | 
| 251 | 
            -
             | 
| 252 | 
            -
                  sweep_scope.each_with_object({}) do |(k, v), obj|
         | 
| 253 | 
            -
                    inventory_collection = collections[k.try(:to_sym)]
         | 
| 254 | 
            -
                    raise "Unrecognized InventoryCollection name: #{k}" if inventory_collection.blank?
         | 
| 255 | 
            -
             | 
| 256 | 
            -
                    serializer = InventoryRefresh::InventoryCollection::Serialization.new(inventory_collection)
         | 
| 257 | 
            -
             | 
| 258 | 
            -
                    obj[k] = serializer.sweep_scope_to_hash(v)
         | 
| 259 | 
            -
                  end
         | 
| 260 | 
            -
                end
         | 
| 261 211 | 
             
              end
         | 
| 262 212 | 
             
            end
         | 
| @@ -1,5 +1,7 @@ | |
| 1 1 | 
             
            require "inventory_refresh/logging"
         | 
| 2 | 
            +
            require "inventory_refresh/save_collection/saver/batch"
         | 
| 2 3 | 
             
            require "inventory_refresh/save_collection/saver/concurrent_safe_batch"
         | 
| 4 | 
            +
            require "inventory_refresh/save_collection/saver/default"
         | 
| 3 5 |  | 
| 4 6 | 
             
            module InventoryRefresh::SaveCollection
         | 
| 5 7 | 
             
              class Base
         | 
| @@ -14,7 +16,7 @@ module InventoryRefresh::SaveCollection | |
| 14 16 | 
             
                    return if skip?(inventory_collection)
         | 
| 15 17 |  | 
| 16 18 | 
             
                    logger.debug("----- BEGIN ----- Saving collection #{inventory_collection} of size #{inventory_collection.size} to"\
         | 
| 17 | 
            -
                                 " the database, for the manager: '#{ems. | 
| 19 | 
            +
                                 " the database, for the manager: '#{ems.name}'...")
         | 
| 18 20 |  | 
| 19 21 | 
             
                    if inventory_collection.custom_save_block.present?
         | 
| 20 22 | 
             
                      logger.debug("Saving collection #{inventory_collection} using a custom save block")
         | 
| @@ -22,7 +24,7 @@ module InventoryRefresh::SaveCollection | |
| 22 24 | 
             
                    else
         | 
| 23 25 | 
             
                      save_inventory(inventory_collection)
         | 
| 24 26 | 
             
                    end
         | 
| 25 | 
            -
                    logger.debug("----- END ----- Saving collection #{inventory_collection}, for the manager: '#{ems. | 
| 27 | 
            +
                    logger.debug("----- END ----- Saving collection #{inventory_collection}, for the manager: '#{ems.name}'...Complete")
         | 
| 26 28 | 
             
                    inventory_collection.saved = true
         | 
| 27 29 | 
             
                  end
         | 
| 28 30 |  |