topological_inventory-providers-common 1.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 022a317ceef535823e38291efad54d0a498905a6149e9fe75e5fc3ee036b7277
4
+ data.tar.gz: ade50e0e3fc05c34ec151673c076e9b521bd7d3426fd82480dc2c30e00406d7c
5
+ SHA512:
6
+ metadata.gz: a9e7f2af5e8affba2ae20513a0cc802aef9bdbdcefbac6ab3aff174fc4ee7cbf4fcafb3138845bcf90a9a11b8e4e363b605b549bf3cfefe90c847be842b04a63
7
+ data.tar.gz: 2c41976bc692984a032847e5b2e24fa7c30ec4c0f9da14032fc31e9dbf69da0ebad0c33d88ea08f7dee700c9a84f5e174b682073ae7a6f9e9cfb021230bbe88f
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ /bundler.d/
10
+ /Gemfile.lock
11
+
12
+ # rspec failure tracking
13
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,10 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.5.7
7
+ - 2.6.5
8
+ before_install:
9
+ - 'echo ''gem: --no-ri --no-rdoc --no-document'' > ~/.gemrc'
10
+ - gem install bundler
@@ -0,0 +1,19 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [1.0.1] - 2020-05-06
8
+ ### Changed
9
+
10
+ Add logging method to base collector #18
11
+ manageiq-loggers to 0.5.0 #19
12
+ manageiq-loggers to >= 0.4.2 #20
13
+
14
+ ## [1.0.0] - 2020-03-19
15
+ ### Initial release to rubygems.org
16
+
17
+ [Unreleased]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.1...HEAD
18
+ [1.0.1]: https://github.com/RedHatInsights/topological_inventory-providers-common/compare/v1.0.0...v1.0.1
19
+ [1.0.0]: https://github.com/RedHatInsights/topological_inventory-providers-common/releases/v1.0.0
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "https://rubygems.org"
2
+
3
+ plugin 'bundler-inject', '~> 1.1'
4
+ require File.join(Bundler::Plugin.index.load_paths("bundler-inject")[0], "bundler-inject") rescue nil
5
+
6
+ # Specify your gem's dependencies in topological_inventory-providers-common.gemspec
7
+ gemspec
8
+
9
+ gem "sources-api-client", "~> 1.0"
10
+ gem "topological_inventory-ingress_api-client", "~> 1.0"
11
+
12
+ group :development, :test do
13
+ gem 'rake', '~> 12.0.0'
14
+ gem 'pry-byebug'
15
+ end
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Martin Slemr
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,3 @@
1
+ # TopologicalInventory::Providers::Common
2
+
3
+ Common code for topological-inventory collectors and Operation workers.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "topological_inventory/providers/common"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,15 @@
1
+ require "topological_inventory/providers/common/version"
2
+ require "topological_inventory/providers/common/logging"
3
+ require "topological_inventory/providers/common/operations/processor"
4
+ require "topological_inventory/providers/common/operations/endpoint_client"
5
+ require "topological_inventory/providers/common/collectors_pool"
6
+ require "topological_inventory/providers/common/collector"
7
+
8
+ module TopologicalInventory
9
+ module Providers
10
+ module Common
11
+ class Error < StandardError; end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,178 @@
1
+ require "active_support/inflector"
2
+ require "concurrent"
3
+ require "topological_inventory-ingress_api-client"
4
+ require "topological_inventory/providers/common/collector/inventory_collection_storage"
5
+ require "topological_inventory/providers/common/collector/inventory_collection_wrapper"
6
+ require "topological_inventory/providers/common/collector/parser"
7
+ require "topological_inventory/providers/common/save_inventory/saver"
8
+
9
+ module TopologicalInventory
10
+ module Providers
11
+ module Common
12
+ class Collector
13
+ # @param poll_time [Integer] Waiting between collecting loops. Irrelevant for standalone_mode: true
14
+ # @param standalone_mode [Boolean] T/F if collector is created by collectors_pool
15
+ def initialize(source, default_limit: 1_000, poll_time: 30, standalone_mode: true)
16
+ self.collector_threads = Concurrent::Map.new
17
+ self.finished = Concurrent::AtomicBoolean.new(false)
18
+ self.poll_time = poll_time
19
+ self.limits = Hash.new(default_limit)
20
+ self.queue = Queue.new
21
+ self.source = source
22
+ self.standalone_mode = standalone_mode
23
+ end
24
+
25
+ def collect!
26
+ start_collector_threads
27
+
28
+ until finished? do
29
+ ensure_collector_threads
30
+
31
+ notices = []
32
+ notices << queue.pop until queue.empty?
33
+
34
+ targeted_refresh(notices) unless notices.empty?
35
+
36
+ standalone_mode ? sleep(poll_time) : stop
37
+ end
38
+ end
39
+
40
+ def stop
41
+ finished.value = true
42
+ end
43
+
44
+ protected
45
+
46
+ attr_accessor :collector_threads, :finished, :limits,
47
+ :poll_time, :queue, :source, :standalone_mode
48
+
49
+ def finished?
50
+ finished.value
51
+ end
52
+
53
+ def entity_types
54
+ endpoint_types.flat_map { |endpoint| send("#{endpoint}_entity_types") }
55
+ end
56
+
57
+ # Should be overriden by subclass
58
+ # Entity types collected from endpoints
59
+ def endpoint_types
60
+ %w()
61
+ end
62
+
63
+ def start_collector_threads
64
+ entity_types.each do |entity_type|
65
+ next if collector_threads[entity_type]&.alive?
66
+
67
+ collector_threads[entity_type] = start_collector_thread(entity_type)
68
+ end
69
+ end
70
+
71
+ def ensure_collector_threads
72
+ start_collector_threads
73
+ end
74
+
75
+ def start_collector_thread(entity_type)
76
+ connection = connection_for_entity_type(entity_type)
77
+ return if connection.nil?
78
+
79
+ Thread.new do
80
+ collector_thread(connection, entity_type)
81
+ end
82
+ end
83
+
84
+ # Connection to endpoint for each entity type
85
+ def connection_for_entity_type(_entity_type)
86
+ raise NotImplementedError
87
+ end
88
+
89
+ # Thread's main for collecting one entity type's data
90
+ def collector_thread(_connection, _entity_type)
91
+ raise NotImplementedError
92
+ end
93
+
94
+ # @optional
95
+ # Listen to notices from threads
96
+ def targeted_refresh(notices)
97
+ end
98
+
99
+ # @param refresh_state_part_collected_at [Time] when this payload is collected (for [Core]:RefreshStatePart)
100
+ # @param refresh_state_part_sent_at [Time] when this payload is sent (for [Core]:RefreshStatePart)
101
+ def save_inventory(collections,
102
+ inventory_name,
103
+ schema,
104
+ refresh_state_uuid = nil,
105
+ refresh_state_part_uuid = nil,
106
+ refresh_state_part_collected_at = nil,
107
+ refresh_state_part_sent_at = Time.now.utc)
108
+ return 0 if collections.empty?
109
+
110
+ SaveInventory::Saver.new(:client => ingress_api_client, :logger => logger).save(
111
+ :inventory => TopologicalInventoryIngressApiClient::Inventory.new(
112
+ :name => inventory_name,
113
+ :schema => TopologicalInventoryIngressApiClient::Schema.new(:name => schema),
114
+ :source => source,
115
+ :collections => collections,
116
+ :refresh_state_uuid => refresh_state_uuid,
117
+ :refresh_state_part_uuid => refresh_state_part_uuid,
118
+ :refresh_state_part_collected_at => refresh_state_part_collected_at,
119
+ :refresh_state_part_sent_at => refresh_state_part_sent_at
120
+ )
121
+ )
122
+ rescue => e
123
+ response_body = e.response_body if e.respond_to? :response_body
124
+ response_headers = e.response_headers if e.respond_to? :response_headers
125
+ logger.error("Error when sending payload to Ingress API. Error message: #{e.message}. Body: #{response_body}. Header: #{response_headers}")
126
+ raise e
127
+ end
128
+
129
+ # @param refresh_state_started_at [Time] when collecting of this entity type is started (for [Core]:RefreshState)
130
+ # @param refresh_state_sent_at [Time] when this payload is sent (for [Core]:RefreshState)
131
+ def sweep_inventory(inventory_name,
132
+ schema,
133
+ refresh_state_uuid,
134
+ total_parts,
135
+ sweep_scope,
136
+ refresh_state_started_at = nil,
137
+ refresh_state_sent_at = Time.now.utc)
138
+ return if !total_parts || sweep_scope.empty?
139
+
140
+ SaveInventory::Saver.new(:client => ingress_api_client, :logger => logger).save(
141
+ :inventory => TopologicalInventoryIngressApiClient::Inventory.new(
142
+ :name => inventory_name,
143
+ :schema => TopologicalInventoryIngressApiClient::Schema.new(:name => schema),
144
+ :source => source,
145
+ :collections => [],
146
+ :refresh_state_uuid => refresh_state_uuid,
147
+ :total_parts => total_parts,
148
+ :sweep_scope => sweep_scope,
149
+ :refresh_state_started_at => refresh_state_started_at,
150
+ :refresh_state_sent_at => refresh_state_sent_at
151
+ )
152
+ )
153
+ rescue => e
154
+ response_body = e.response_body if e.respond_to? :response_body
155
+ response_headers = e.response_headers if e.respond_to? :response_headers
156
+ logger.error("Error when sending payload to Ingress API. Error message: #{e.message}. Body: #{response_body}. Header: #{response_headers}")
157
+ raise e
158
+ end
159
+
160
+ def inventory_name
161
+ "Default"
162
+ end
163
+
164
+ def schema_name
165
+ "Default"
166
+ end
167
+
168
+ def ingress_api_client
169
+ TopologicalInventoryIngressApiClient::DefaultApi.new
170
+ end
171
+
172
+ def log_external_url(url)
173
+ logger.info("[EXTERNAL URL] #{url}")
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,69 @@
1
+ module TopologicalInventory
2
+ module Providers
3
+ module Common
4
+ class Collector
5
+ class InventoryCollectionStorage
6
+ attr_accessor :data
7
+
8
+ delegate :values, :to => :data
9
+
10
+ def initialize
11
+ @data = {}
12
+ end
13
+
14
+ def add_collection(name, overwrite: true)
15
+ return @data[name] if !@data[name].nil? && !overwrite
16
+
17
+ if ingress_api_model_exists?(name)
18
+ @data[name] ||= InventoryCollectionWrapper.new(:name => name)
19
+ else
20
+ raise NameError, "TopologicalInventoryIngressApiClient::#{name.to_s.classify} doesn't exist"
21
+ end
22
+ end
23
+
24
+ # Creates collection automatically
25
+ def [](name)
26
+ add_collection(name, :overwrite => false)
27
+ end
28
+
29
+ # @return [Array<Symbol>] array of InventoryCollection object names of the persister
30
+ def inventory_collections_names
31
+ @data.keys
32
+ end
33
+
34
+ def method_missing(method_name, *arguments, &block)
35
+ add_collection(method_name, :overwrite => false) # init collection if not exist
36
+
37
+ if inventory_collections_names.include?(method_name)
38
+ self.class.define_collections_reader(method_name)
39
+ send(method_name)
40
+ else
41
+ super
42
+ end
43
+ end
44
+
45
+ # @return [Boolean] true if InventoryCollection with passed method_name name is defined
46
+ def respond_to_missing?(method_name, _include_private = false)
47
+ ingress_api_model_exists?(method_name) || super
48
+ end
49
+
50
+ protected
51
+
52
+ def ingress_api_model_exists?(method_name)
53
+ class_name = "TopologicalInventoryIngressApiClient::#{method_name.to_s.classify}"
54
+
55
+ # nil test is not enough due to sometimes weird namespace autoloading
56
+ class_name.safe_constantize.to_s == class_name
57
+ end
58
+
59
+ # Defines a new attr reader returning InventoryCollection object
60
+ def self.define_collections_reader(collection_key)
61
+ define_method(collection_key) do
62
+ add_collection(collection_key, :overwrite => false)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,25 @@
1
+ module TopologicalInventory
2
+ module Providers
3
+ module Common
4
+ class Collector
5
+ class InventoryCollectionWrapper < TopologicalInventoryIngressApiClient::InventoryCollection
6
+ def initialize(name:)
7
+ super(:name => name, :data => [])
8
+ end
9
+
10
+ def build(properties)
11
+ obj = get_model.new(properties)
12
+ data << obj
13
+ obj
14
+ end
15
+
16
+ protected
17
+
18
+ def get_model
19
+ "TopologicalInventoryIngressApiClient::#{name.to_s.classify}".constantize
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,34 @@
1
+ require "active_support/inflector"
2
+
3
+ module TopologicalInventory
4
+ module Providers
5
+ module Common
6
+ class Collector
7
+ class Parser
8
+ attr_accessor :collections, :resource_timestamp
9
+
10
+ delegate :add_collection, :to => :collections
11
+
12
+ def initialize
13
+ @collections = InventoryCollectionStorage.new
14
+
15
+ self.resource_timestamp = Time.now.utc
16
+ end
17
+
18
+ def lazy_find(collection, reference, ref: :manager_ref)
19
+ return if reference.kind_of?(String) && reference.blank?
20
+
21
+ # Don't make lazy link if all reference values are blank
22
+ return if reference.kind_of?(Hash) && reference.values.select { |val| val.to_s.present? }.blank?
23
+
24
+ TopologicalInventoryIngressApiClient::InventoryObjectLazy.new(
25
+ :inventory_collection_name => collection,
26
+ :reference => reference,
27
+ :ref => ref,
28
+ )
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end