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.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.travis.yml +10 -0
- data/CHANGELOG.md +19 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +21 -0
- data/README.md +3 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/topological_inventory/providers/common.rb +15 -0
- data/lib/topological_inventory/providers/common/collector.rb +178 -0
- data/lib/topological_inventory/providers/common/collector/inventory_collection_storage.rb +69 -0
- data/lib/topological_inventory/providers/common/collector/inventory_collection_wrapper.rb +25 -0
- data/lib/topological_inventory/providers/common/collector/parser.rb +34 -0
- data/lib/topological_inventory/providers/common/collectors_pool.rb +135 -0
- data/lib/topological_inventory/providers/common/logging.rb +22 -0
- data/lib/topological_inventory/providers/common/operations/endpoint_client.rb +62 -0
- data/lib/topological_inventory/providers/common/operations/processor.rb +135 -0
- data/lib/topological_inventory/providers/common/operations/sources_api_client.rb +85 -0
- data/lib/topological_inventory/providers/common/operations/topology_api_client.rb +28 -0
- data/lib/topological_inventory/providers/common/save_inventory/exception.rb +14 -0
- data/lib/topological_inventory/providers/common/save_inventory/saver.rb +124 -0
- data/lib/topological_inventory/providers/common/version.rb +7 -0
- data/topological_inventory-providers-common.gemspec +35 -0
- metadata +198 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -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
|
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -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__)
|
data/bin/setup
ADDED
@@ -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
|