foreman_inventory_upload 0.0.1.dev1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +619 -0
  3. data/README.md +38 -0
  4. data/Rakefile +47 -0
  5. data/app/controllers/foreman_inventory_upload/react_controller.rb +7 -0
  6. data/app/controllers/foreman_inventory_upload/reports_controller.rb +23 -0
  7. data/app/controllers/foreman_inventory_upload/statuses_controller.rb +41 -0
  8. data/app/controllers/foreman_inventory_upload/uploads_controller.rb +31 -0
  9. data/app/helpers/foreman_inventory_upload_helper.rb +13 -0
  10. data/app/views/dashboard/_inventory_upload_widget.html.erb +2 -0
  11. data/app/views/foreman_inventory_upload/layouts/react.html.erb +16 -0
  12. data/app/views/scripts/uploader.sh.erb +31 -0
  13. data/config/routes.rb +10 -0
  14. data/lib/foreman_inventory_upload/async/generate_report_job.rb +29 -0
  15. data/lib/foreman_inventory_upload/async/progress_output.rb +61 -0
  16. data/lib/foreman_inventory_upload/async/queue_for_upload_job.rb +52 -0
  17. data/lib/foreman_inventory_upload/async/shell_process.rb +35 -0
  18. data/lib/foreman_inventory_upload/async/upload_report_job.rb +40 -0
  19. data/lib/foreman_inventory_upload/engine.rb +52 -0
  20. data/lib/foreman_inventory_upload/generators/archived_report.rb +53 -0
  21. data/lib/foreman_inventory_upload/generators/json_stream.rb +55 -0
  22. data/lib/foreman_inventory_upload/generators/metadata.rb +36 -0
  23. data/lib/foreman_inventory_upload/generators/queries.rb +52 -0
  24. data/lib/foreman_inventory_upload/generators/slice.rb +117 -0
  25. data/lib/foreman_inventory_upload/version.rb +3 -0
  26. data/lib/foreman_inventory_upload.rb +48 -0
  27. data/lib/tasks/foreman_inventory_upload_tasks.rake +37 -0
  28. data/lib/tasks/generator.rake +15 -0
  29. data/locale/Makefile +60 -0
  30. data/locale/en/foreman_yupana.po +19 -0
  31. data/locale/foreman_yupana.pot +19 -0
  32. data/locale/gemspec.rb +2 -0
  33. data/test/controllers/reports_controller_test.rb +21 -0
  34. data/test/controllers/statuses_controller_test.rb +26 -0
  35. data/test/controllers/uploads_controller_test.rb +21 -0
  36. data/test/factories/inventory_upload_factories.rb +88 -0
  37. data/test/test_plugin_helper.rb +26 -0
  38. data/test/unit/archived_report_generator_test.rb +61 -0
  39. data/test/unit/metadata_generator_test.rb +40 -0
  40. data/test/unit/shell_process_job_test.rb +27 -0
  41. data/test/unit/slice_generator_test.rb +60 -0
  42. 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,7 @@
1
+ module ForemanInventoryUpload
2
+ class ReactController < ::ApplicationController
3
+ def index
4
+ render 'foreman_inventory_upload/layouts/react', :layout => false
5
+ end
6
+ end
7
+ 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,2 @@
1
+ <h4 class="header ca"><%= _('ForemanInventoryUpload') %></h4>
2
+ <%= _('Widget content') %>
@@ -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