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.
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