foreman_inventory_upload 0.0.1.dev1
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/LICENSE +619 -0
- data/README.md +38 -0
- data/Rakefile +47 -0
- data/app/controllers/foreman_inventory_upload/react_controller.rb +7 -0
- data/app/controllers/foreman_inventory_upload/reports_controller.rb +23 -0
- data/app/controllers/foreman_inventory_upload/statuses_controller.rb +41 -0
- data/app/controllers/foreman_inventory_upload/uploads_controller.rb +31 -0
- data/app/helpers/foreman_inventory_upload_helper.rb +13 -0
- data/app/views/dashboard/_inventory_upload_widget.html.erb +2 -0
- data/app/views/foreman_inventory_upload/layouts/react.html.erb +16 -0
- data/app/views/scripts/uploader.sh.erb +31 -0
- data/config/routes.rb +10 -0
- data/lib/foreman_inventory_upload/async/generate_report_job.rb +29 -0
- data/lib/foreman_inventory_upload/async/progress_output.rb +61 -0
- data/lib/foreman_inventory_upload/async/queue_for_upload_job.rb +52 -0
- data/lib/foreman_inventory_upload/async/shell_process.rb +35 -0
- data/lib/foreman_inventory_upload/async/upload_report_job.rb +40 -0
- data/lib/foreman_inventory_upload/engine.rb +52 -0
- data/lib/foreman_inventory_upload/generators/archived_report.rb +53 -0
- data/lib/foreman_inventory_upload/generators/json_stream.rb +55 -0
- data/lib/foreman_inventory_upload/generators/metadata.rb +36 -0
- data/lib/foreman_inventory_upload/generators/queries.rb +52 -0
- data/lib/foreman_inventory_upload/generators/slice.rb +117 -0
- data/lib/foreman_inventory_upload/version.rb +3 -0
- data/lib/foreman_inventory_upload.rb +48 -0
- data/lib/tasks/foreman_inventory_upload_tasks.rake +37 -0
- data/lib/tasks/generator.rake +15 -0
- data/locale/Makefile +60 -0
- data/locale/en/foreman_yupana.po +19 -0
- data/locale/foreman_yupana.pot +19 -0
- data/locale/gemspec.rb +2 -0
- data/test/controllers/reports_controller_test.rb +21 -0
- data/test/controllers/statuses_controller_test.rb +26 -0
- data/test/controllers/uploads_controller_test.rb +21 -0
- data/test/factories/inventory_upload_factories.rb +88 -0
- data/test/test_plugin_helper.rb +26 -0
- data/test/unit/archived_report_generator_test.rb +61 -0
- data/test/unit/metadata_generator_test.rb +40 -0
- data/test/unit/shell_process_job_test.rb +27 -0
- data/test/unit/slice_generator_test.rb +60 -0
- metadata +149 -0
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'ForemanInventoryUpload'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
APP_RAKEFILE = File.expand_path('../test/dummy/Rakefile', __FILE__)
|
24
|
+
|
25
|
+
Bundler::GemHelper.install_tasks
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
|
29
|
+
Rake::TestTask.new(:test) do |t|
|
30
|
+
t.libs << 'lib'
|
31
|
+
t.libs << 'test'
|
32
|
+
t.pattern = 'test/**/*_test.rb'
|
33
|
+
t.verbose = false
|
34
|
+
end
|
35
|
+
|
36
|
+
task default: :test
|
37
|
+
|
38
|
+
begin
|
39
|
+
require 'rubocop/rake_task'
|
40
|
+
RuboCop::RakeTask.new
|
41
|
+
rescue => _
|
42
|
+
puts 'Rubocop not loaded.'
|
43
|
+
end
|
44
|
+
|
45
|
+
task :default do
|
46
|
+
Rake::Task['rubocop'].execute
|
47
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ForemanInventoryUpload
|
2
|
+
class ReportsController < ::ApplicationController
|
3
|
+
def last
|
4
|
+
label = ForemanInventoryUpload::Async::GenerateReportJob.output_label(params[:portal_user])
|
5
|
+
output = ForemanInventoryUpload::Async::ProgressOutput.get(label)&.full_output
|
6
|
+
|
7
|
+
render json: {
|
8
|
+
output: output
|
9
|
+
}, status: :ok
|
10
|
+
end
|
11
|
+
|
12
|
+
def generate
|
13
|
+
portal_user = params[:portal_user]
|
14
|
+
|
15
|
+
generated_file_name = File.join(ForemanInventoryUpload.base_folder, "#{portal_user}.tar.gz")
|
16
|
+
ForemanInventoryUpload::Async::GenerateReportJob.perform_later(generated_file_name, portal_user)
|
17
|
+
|
18
|
+
render json: {
|
19
|
+
action_status: 'success'
|
20
|
+
}, status: :ok
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module ForemanInventoryUpload
|
2
|
+
class StatusesController < ::ApplicationController
|
3
|
+
# override default "welcome screen behavior, since we don't have a model"
|
4
|
+
def welcome
|
5
|
+
true
|
6
|
+
end
|
7
|
+
|
8
|
+
def index
|
9
|
+
portal_users = RedhatAccess::TelemetryConfiguration
|
10
|
+
.where(enable_telemetry: true)
|
11
|
+
.distinct
|
12
|
+
.pluck(:portal_user)
|
13
|
+
|
14
|
+
statuses = Hash[
|
15
|
+
portal_users.map do |portal_user|
|
16
|
+
generate_report_status = status_for(portal_user, ForemanInventoryUpload::Async::GenerateReportJob)
|
17
|
+
upload_report_status = status_for(portal_user, ForemanInventoryUpload::Async::UploadReportJob)
|
18
|
+
|
19
|
+
[
|
20
|
+
portal_user,
|
21
|
+
{
|
22
|
+
generate_report_status: generate_report_status,
|
23
|
+
upload_report_status: upload_report_status
|
24
|
+
}
|
25
|
+
]
|
26
|
+
end
|
27
|
+
]
|
28
|
+
|
29
|
+
render json: {
|
30
|
+
statuses: statuses
|
31
|
+
}, status: :ok
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def status_for(portal_user, job_class)
|
37
|
+
label = job_class.output_label(portal_user)
|
38
|
+
ForemanInventoryUpload::Async::ProgressOutput.get(label)&.status
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ForemanInventoryUpload
|
2
|
+
class UploadsController < ::ApplicationController
|
3
|
+
def last
|
4
|
+
label = ForemanInventoryUpload::Async::UploadReportJob.output_label(params[:portal_user])
|
5
|
+
output = ForemanInventoryUpload::Async::ProgressOutput.get(label)&.full_output
|
6
|
+
|
7
|
+
render json: {
|
8
|
+
output: output
|
9
|
+
}, status: :ok
|
10
|
+
end
|
11
|
+
|
12
|
+
def download_file
|
13
|
+
filename = 'hosts_report.tar.gz'
|
14
|
+
path = Rails.root.join(ForemanInventoryUpload.uploads_folder(params[:portal_user]), filename)
|
15
|
+
unless File.exist? path
|
16
|
+
return throw_flash_error(
|
17
|
+
"Path doesn't exist: #{path}"
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
send_file path, disposition: 'attachment', filename: filename
|
22
|
+
end
|
23
|
+
|
24
|
+
def throw_flash_error(message)
|
25
|
+
process_error(
|
26
|
+
:redirect => foreman_inventory_upload_index_path,
|
27
|
+
:error_msg => message
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ForemanInventoryUploadHelper
|
2
|
+
if Foreman::Version.new.minor.to_i < 23
|
3
|
+
def webpacked_plugins_css_for(*plugin_names)
|
4
|
+
css_tags_for(select_requested_plugins(plugin_names)).join.html_safe
|
5
|
+
end
|
6
|
+
|
7
|
+
def css_tags_for(requested_plugins)
|
8
|
+
requested_plugins.map do |plugin|
|
9
|
+
stylesheet_link_tag(*webpack_asset_paths(plugin.to_s, :extension => 'css'), "data-turbolinks-track" => true)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<% content_for(:javascripts) do %>
|
2
|
+
<%= webpacked_plugins_js_for :'foreman_inventory_upload' %>
|
3
|
+
<% end %>
|
4
|
+
|
5
|
+
<% content_for(:stylesheets) do %>
|
6
|
+
<%= webpacked_plugins_css_for :'foreman_inventory_upload' %>
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
<% content_for(:content) do %>
|
10
|
+
<%= notifications %>
|
11
|
+
<div id="organization-id" data-id="<%= Organization.current.id if Organization.current %>" ></div>
|
12
|
+
<div id="user-id" data-id="<%= User.current.id if User.current %>" ></div>
|
13
|
+
<div id="ForemanInventoryUploadReactRoot"></div>
|
14
|
+
<% end %>
|
15
|
+
<%= render file: "layouts/base" %>
|
16
|
+
<%= mount_react_component('ForemanInventoryUpload', '#ForemanInventoryUploadReactRoot') %>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#! /bin/sh
|
2
|
+
|
3
|
+
DEST=<%= @upload_url %>
|
4
|
+
RH_USERNAME=<%= @rh_username %>
|
5
|
+
|
6
|
+
if [ -z "$RH_USERNAME" ]
|
7
|
+
then
|
8
|
+
IFS= read -rp "Enter username: " RH_USERNAME
|
9
|
+
fi
|
10
|
+
|
11
|
+
if [ -z "$RH_PASSWORD" ]
|
12
|
+
then
|
13
|
+
IFS= read -rsp "Enter password: " RH_PASSWORD
|
14
|
+
fi
|
15
|
+
|
16
|
+
if [ -z "$FILES" ]
|
17
|
+
then
|
18
|
+
FILES=./*.tar.gz
|
19
|
+
fi
|
20
|
+
|
21
|
+
mkdir -p done
|
22
|
+
|
23
|
+
for f in $FILES
|
24
|
+
do
|
25
|
+
curl -k -vvv -# --fail -F "file=@$f;type=application/vnd.redhat.qpc.tar+tgz" $DEST -u "$RH_USERNAME":"$RH_PASSWORD"
|
26
|
+
if [ $? -eq 0 ]; then
|
27
|
+
mv $f done/
|
28
|
+
echo "Done: $f"
|
29
|
+
fi
|
30
|
+
done
|
31
|
+
echo "Uploaded files moved to done/ folder"
|
data/config/routes.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
Rails.application.routes.draw do
|
2
|
+
namespace :foreman_inventory_upload do
|
3
|
+
get 'index', to: 'react#index'
|
4
|
+
get ':portal_user/reports/last', to: 'reports#last', constraints: { portal_user: %r{[^\/]+} }
|
5
|
+
post ':portal_user/reports', to: 'reports#generate', constraints: { portal_user: %r{[^\/]+} }
|
6
|
+
get ':portal_user/uploads/last', to: 'uploads#last', constraints: { portal_user: %r{[^\/]+} }
|
7
|
+
get ':portal_user/uploads/file', to: 'uploads#download_file', constraints: { portal_user: %r{[^\/]+} }
|
8
|
+
get 'statuses', to: 'statuses#index'
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ForemanInventoryUpload
|
2
|
+
module Async
|
3
|
+
class GenerateReportJob < ShellProcess
|
4
|
+
def self.output_label(portal_user)
|
5
|
+
"report_for_#{portal_user}"
|
6
|
+
end
|
7
|
+
|
8
|
+
def perform(result_file, portal_user)
|
9
|
+
@result_file = result_file
|
10
|
+
@portal_user = portal_user
|
11
|
+
|
12
|
+
super(GenerateReportJob.output_label(portal_user))
|
13
|
+
|
14
|
+
QueueForUploadJob.perform_later(result_file, portal_user)
|
15
|
+
end
|
16
|
+
|
17
|
+
def command
|
18
|
+
'rake foreman_inventory_upload:report:generate'
|
19
|
+
end
|
20
|
+
|
21
|
+
def env
|
22
|
+
super.merge(
|
23
|
+
'target' => @result_file,
|
24
|
+
'portal_user' => @portal_user
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module ForemanInventoryUpload
|
2
|
+
module Async
|
3
|
+
class ProgressOutput
|
4
|
+
def self.get(label)
|
5
|
+
ProgressOutput.new(label, :reader)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.register(label)
|
9
|
+
ProgressOutput.new(label, :writer)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(label, mode)
|
13
|
+
@label = label
|
14
|
+
@mode = mode
|
15
|
+
end
|
16
|
+
|
17
|
+
def buffer
|
18
|
+
@buffer ||= begin
|
19
|
+
File.open(file_name, file_mode)
|
20
|
+
rescue Errno::ENOENT
|
21
|
+
StringIO.new
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def full_output
|
26
|
+
buffer.read
|
27
|
+
end
|
28
|
+
|
29
|
+
def write_line(line)
|
30
|
+
buffer << line
|
31
|
+
buffer.fsync
|
32
|
+
end
|
33
|
+
|
34
|
+
def close
|
35
|
+
@buffer&.close
|
36
|
+
end
|
37
|
+
|
38
|
+
def status
|
39
|
+
File.read(file_name(:status))
|
40
|
+
rescue Errno::ENOENT
|
41
|
+
''
|
42
|
+
end
|
43
|
+
|
44
|
+
def status=(status)
|
45
|
+
File.atomic_write(file_name(:status)) do |status_file|
|
46
|
+
status_file.write(status)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def file_mode
|
53
|
+
@mode == :reader ? 'r' : 'w'
|
54
|
+
end
|
55
|
+
|
56
|
+
def file_name(type = 'out')
|
57
|
+
File.join(ForemanInventoryUpload.outputs_folder, "#{@label}.#{type}")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module ForemanInventoryUpload
|
2
|
+
module Async
|
3
|
+
class QueueForUploadJob < ::ApplicationJob
|
4
|
+
def perform(report_file, portal_user)
|
5
|
+
@portal_user = portal_user
|
6
|
+
logger.debug('Ensuring objects')
|
7
|
+
ensure_ouput_folder
|
8
|
+
ensure_output_script
|
9
|
+
logger.debug("Copying #{report_file} to #{uploads_folder}")
|
10
|
+
enqueued_file_name = File.join(uploads_folder, ForemanInventoryUpload.facts_archive_name)
|
11
|
+
FileUtils.mv(report_file, enqueued_file_name)
|
12
|
+
logger.debug("Done copying #{report_file} to #{enqueued_file_name}")
|
13
|
+
|
14
|
+
UploadReportJob.perform_later(enqueued_file_name, portal_user)
|
15
|
+
end
|
16
|
+
|
17
|
+
def uploads_folder
|
18
|
+
@uploads_folder ||= ForemanInventoryUpload.uploads_folder(@portal_user)
|
19
|
+
end
|
20
|
+
|
21
|
+
def script_file
|
22
|
+
@script_file ||= File.join(uploads_folder, ForemanInventoryUpload.upload_script_file)
|
23
|
+
end
|
24
|
+
|
25
|
+
def ensure_ouput_folder
|
26
|
+
FileUtils.mkdir_p(uploads_folder)
|
27
|
+
end
|
28
|
+
|
29
|
+
def ensure_output_script
|
30
|
+
return if File.exist?(script_file)
|
31
|
+
|
32
|
+
script_source = File.join(ForemanInventoryUpload::Engine.root, 'app/views/scripts/uploader.sh.erb')
|
33
|
+
|
34
|
+
template_src = Foreman::Renderer::Source::String.new(content: File.read(script_source))
|
35
|
+
scope = Foreman::Renderer::Scope::Base.new(
|
36
|
+
source: template_src,
|
37
|
+
variables: {
|
38
|
+
upload_url: ForemanInventoryUpload.upload_url,
|
39
|
+
rh_username: @portal_user
|
40
|
+
}
|
41
|
+
)
|
42
|
+
script_source = Foreman::Renderer.render(template_src, scope)
|
43
|
+
File.write(script_file, script_source)
|
44
|
+
FileUtils.chmod('+x', script_file)
|
45
|
+
end
|
46
|
+
|
47
|
+
def logger
|
48
|
+
Foreman::Logging.logger('background')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module ForemanInventoryUpload
|
4
|
+
module Async
|
5
|
+
class ShellProcess < ::ApplicationJob
|
6
|
+
def perform(instance_label)
|
7
|
+
klass_name = self.class.name
|
8
|
+
logger.debug("Starting #{klass_name} with label #{instance_label}")
|
9
|
+
progress_output = ProgressOutput.register(instance_label)
|
10
|
+
Open3.popen2e(env, command) do |_stdin, stdout_stderr, wait_thread|
|
11
|
+
progress_output.status = "Running in pid #{wait_thread.pid}"
|
12
|
+
|
13
|
+
stdout_stderr.each do |out_line|
|
14
|
+
progress_output.write_line(out_line)
|
15
|
+
end
|
16
|
+
|
17
|
+
progress_output.status = wait_thread.value.to_s
|
18
|
+
end
|
19
|
+
logger.debug("Finished job #{klass_name} with label #{instance_label}")
|
20
|
+
ensure
|
21
|
+
progress_output.close
|
22
|
+
end
|
23
|
+
|
24
|
+
def command; end
|
25
|
+
|
26
|
+
def env
|
27
|
+
{}
|
28
|
+
end
|
29
|
+
|
30
|
+
def logger
|
31
|
+
Foreman::Logging.logger('background')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module ForemanInventoryUpload
|
2
|
+
module Async
|
3
|
+
class UploadReportJob < ShellProcess
|
4
|
+
def self.output_label(portal_user)
|
5
|
+
"upload_for_#{portal_user}"
|
6
|
+
end
|
7
|
+
|
8
|
+
def perform(filename, portal_user)
|
9
|
+
@portal_user = portal_user
|
10
|
+
@filename = filename
|
11
|
+
|
12
|
+
super(UploadReportJob.output_label(portal_user))
|
13
|
+
end
|
14
|
+
|
15
|
+
def command
|
16
|
+
File.join(File.dirname(@filename), ForemanInventoryUpload.upload_script_file)
|
17
|
+
end
|
18
|
+
|
19
|
+
def env
|
20
|
+
super.merge(
|
21
|
+
'RH_USERNAME' => rh_username,
|
22
|
+
'RH_PASSWORD' => rh_password,
|
23
|
+
'FILES' => @filename
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def rh_credentials
|
28
|
+
@rh_credentials ||= RedhatAccess::TelemetryConfiguration.where(portal_user: @portal_user).last
|
29
|
+
end
|
30
|
+
|
31
|
+
def rh_username
|
32
|
+
@portal_user
|
33
|
+
end
|
34
|
+
|
35
|
+
def rh_password
|
36
|
+
rh_credentials.portal_password
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'katello'
|
2
|
+
require 'redhat_access'
|
3
|
+
|
4
|
+
module ForemanInventoryUpload
|
5
|
+
class Engine < ::Rails::Engine
|
6
|
+
engine_name 'foreman_inventory_upload'
|
7
|
+
|
8
|
+
config.autoload_paths += Dir["#{config.root}/app/controllers/concerns"]
|
9
|
+
config.autoload_paths += Dir["#{config.root}/app/helpers/concerns"]
|
10
|
+
config.autoload_paths += Dir["#{config.root}/app/models/concerns"]
|
11
|
+
config.autoload_paths += Dir["#{config.root}/app/overrides"]
|
12
|
+
config.autoload_paths += Dir["#{config.root}/lib"]
|
13
|
+
|
14
|
+
# Add any db migrations
|
15
|
+
initializer 'foreman_inventory_upload.load_app_instance_data' do |app|
|
16
|
+
ForemanInventoryUpload::Engine.paths['db/migrate'].existent.each do |path|
|
17
|
+
app.config.paths['db/migrate'] << path
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
initializer 'foreman_inventory_upload.register_plugin', :before => :finisher_hook do |_app|
|
22
|
+
Foreman::Plugin.register :foreman_inventory_upload do
|
23
|
+
requires_foreman '>= 1.20'
|
24
|
+
|
25
|
+
# Add permissions
|
26
|
+
security_block :foreman_inventory_upload do
|
27
|
+
permission :view_foreman_inventory_upload, :'foreman_inventory_upload/reports' => [:last]
|
28
|
+
end
|
29
|
+
|
30
|
+
# Add a new role called 'Discovery' if it doesn't exist
|
31
|
+
role 'ForemanInventoryUpload', [:view_foreman_inventory_upload]
|
32
|
+
|
33
|
+
# Adding a sub menu after hosts menu
|
34
|
+
sub_menu :top_menu, :foreman_inventory_upload, :caption => N_('RH Inventory'), :icon => 'fa fa-cloud-upload' do
|
35
|
+
menu :top_menu, :level1, :caption => N_('Manage'), :url_hash => { controller: :'foreman_inventory_upload/react', :action=>:index}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
rake_tasks do
|
41
|
+
Rake::Task['db:seed'].enhance do
|
42
|
+
ForemanInventoryUpload::Engine.load_seed
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
initializer 'foreman_inventory_upload.register_gettext', after: :load_config_initializers do |_app|
|
47
|
+
locale_dir = File.join(File.expand_path('../..', __dir__), 'locale')
|
48
|
+
locale_domain = 'foreman_inventory_upload'
|
49
|
+
Foreman::Gettext::Support.add_text_domain locale_domain, locale_dir
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module ForemanInventoryUpload
|
2
|
+
module Generators
|
3
|
+
class ArchivedReport
|
4
|
+
def initialize(target, logger = Rails.logger)
|
5
|
+
@target = target
|
6
|
+
@logger = logger
|
7
|
+
end
|
8
|
+
|
9
|
+
def render(portal_user)
|
10
|
+
Dir.mktmpdir do |tmpdir|
|
11
|
+
@logger.info "Started generating hosts report in #{tmpdir}"
|
12
|
+
host_batches = ForemanInventoryUpload::Generators::Queries.for_report(portal_user)
|
13
|
+
File.open(File.join(tmpdir, 'metadata.json'), 'w') do |metadata_out|
|
14
|
+
metadata_generator = ForemanInventoryUpload::Generators::Metadata.new(metadata_out)
|
15
|
+
metadata_generator.render do |inner_generator|
|
16
|
+
first = true
|
17
|
+
host_batches.each do |hosts_batch|
|
18
|
+
slice_id = Foreman.uuid
|
19
|
+
hosts_count = hosts_batch.count
|
20
|
+
@logger.info "Adding slice #{slice_id} with #{hosts_count} hosts"
|
21
|
+
generate_slice(tmpdir, slice_id, hosts_batch)
|
22
|
+
inner_generator.add_slice(slice_id, hosts_count, first)
|
23
|
+
first = false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
@logger.info 'Report generation finished'
|
28
|
+
|
29
|
+
@logger.info 'Archiving generated report'
|
30
|
+
# success = system('tar', '-zcvf', @target, '-C', tmpdir, '.')
|
31
|
+
Open3.popen2e('tar', '-zcvf', @target, '-C', tmpdir, '.') do |_in, out, wait_thr|
|
32
|
+
@logger.info("tar: #{out.read}")
|
33
|
+
|
34
|
+
if wait_thr.value.success?
|
35
|
+
@logger.info 'Report archived successfully'
|
36
|
+
else
|
37
|
+
@logger.info "Tar command failed: #{$CHILD_STATUS}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def generate_slice(tmpdir, slice_id, hosts_batch)
|
46
|
+
File.open(File.join(tmpdir, "#{slice_id}.json"), 'w') do |slice_out|
|
47
|
+
slice_generator = ForemanInventoryUpload::Generators::Slice.new(hosts_batch, slice_out, slice_id)
|
48
|
+
slice_generator.render
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module ForemanInventoryUpload
|
2
|
+
module Generators
|
3
|
+
class JsonStream
|
4
|
+
attr_reader :out
|
5
|
+
def initialize(out)
|
6
|
+
@out = out
|
7
|
+
end
|
8
|
+
|
9
|
+
def array
|
10
|
+
@out << '['
|
11
|
+
yield
|
12
|
+
@out << ']'
|
13
|
+
end
|
14
|
+
|
15
|
+
def object
|
16
|
+
@out << '{'
|
17
|
+
yield
|
18
|
+
@out << '}'
|
19
|
+
end
|
20
|
+
|
21
|
+
def comma
|
22
|
+
@out << ', '
|
23
|
+
end
|
24
|
+
|
25
|
+
def raw(string)
|
26
|
+
@out << string
|
27
|
+
end
|
28
|
+
|
29
|
+
def simple_field(name, value, last = false)
|
30
|
+
@out << "\"#{name}\": #{stringify_value(value)}#{last ? '' : ','}" unless value.nil?
|
31
|
+
end
|
32
|
+
|
33
|
+
def array_field(name, last = false, &block)
|
34
|
+
@out << "\"#{name}\": "
|
35
|
+
array(&block)
|
36
|
+
@out << ',' unless last
|
37
|
+
end
|
38
|
+
|
39
|
+
def object_field(name, last = false, &block)
|
40
|
+
@out << "\"#{name}\": "
|
41
|
+
object(&block)
|
42
|
+
@out << ',' unless last
|
43
|
+
end
|
44
|
+
|
45
|
+
def stringify_value(value)
|
46
|
+
return value if value.is_a?(Integer)
|
47
|
+
return value if value.is_a?(TrueClass)
|
48
|
+
return value if value.is_a?(FalseClass)
|
49
|
+
return value.to_json if value.is_a?(Hash)
|
50
|
+
|
51
|
+
"\"#{value}\""
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module ForemanInventoryUpload
|
2
|
+
module Generators
|
3
|
+
class Metadata
|
4
|
+
def initialize(output = [])
|
5
|
+
@stream = JsonStream.new(output)
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_slice(slice_id, hosts_count, first)
|
9
|
+
@stream.comma unless first
|
10
|
+
|
11
|
+
@stream.object_field(slice_id, :last) do
|
12
|
+
@stream.simple_field('number_hosts', hosts_count, :last)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def render(metadata = nil, &block)
|
17
|
+
render_report(metadata, &block)
|
18
|
+
@stream.out
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def render_report(metadata)
|
24
|
+
@stream.object do
|
25
|
+
@stream.simple_field('report_id', Foreman.uuid)
|
26
|
+
@stream.simple_field('host_inventory_api_version', '1.0')
|
27
|
+
@stream.simple_field('source', 'Satellite')
|
28
|
+
@stream.simple_field('source_metadata', metadata)
|
29
|
+
@stream.object_field('report_slices', :last) do
|
30
|
+
yield(self)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|