foreman_salt 1.0.0 → 1.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3958e07e3ba51a52ed71666da0a710088a5f33f6
4
- data.tar.gz: df6214da1df13e4ec5eea3026c5237528d88deea
3
+ metadata.gz: 763554c758fd70adb3a4feef13ceb720c6711caa
4
+ data.tar.gz: 0b86628a2277d95a26cc75bf6ef45070361c35d0
5
5
  SHA512:
6
- metadata.gz: 7d6dc7e5d6b97d0fc09ebc34df7d6172ceab5e052d779ec907fca754fd6edea1ce82df6e5c56a135dc7db91147b4953aa643b4afec3ab05dd4b1dd59b29c04d0
7
- data.tar.gz: b30c5d22dfd590fc398f26893ba1910452e9fc21b0fceca6974217c24fc58cb57142c0b586fd832ceedaeec9115f9553a5c4c88a8203662d2b8a9e39d6eb8250
6
+ metadata.gz: 8d874dcf278f82b3b5bce1da5373bc37ccd758a5ee3ab9cccca65bc7cd8e0518a74a4cb9580d47e9d3187b4b2191e9b5d4b69b102cb420b4815719344eae35de
7
+ data.tar.gz: 5fd385f82b6dd1a8f9fef3d8061e221e127123176afeda78ea07b1075092b62ac7cab162d8b143a60f4bb6f88c991657fcf3c5511602977b825fa1aa9127d37e
@@ -0,0 +1,65 @@
1
+ require 'uri'
2
+
3
+ module ForemanSalt
4
+ module Api
5
+ module V2
6
+ class JobsController < ::Api::V2::BaseController
7
+ include ::Api::Version2
8
+ include ::Foreman::Controller::SmartProxyAuth
9
+ include ForemanSalt::Concerns::SmartProxyAuthExtensions
10
+
11
+ add_puppetmaster_filters :upload
12
+
13
+ resource_description do
14
+ api_base_url "/salt/api"
15
+ end
16
+
17
+ def_param_group :job do
18
+ param :job, Hash, :required => true, :action_aware => true do
19
+ param :job_id, Integer, :required => true, :desc => N_("JID")
20
+ param :function, String, :required => true, :desc => N_("Function")
21
+ param :result, Hash, :required => true, :desc => N_("Result")
22
+ end
23
+ end
24
+
25
+ api :POST, '/upload/', N_('Upload a Job')
26
+ param_group :job, :as => :upload
27
+
28
+ def upload
29
+ Rails.logger.info("Processing job #{params[:job][:job_id]} from Salt.")
30
+ case params[:job][:function]
31
+ when 'state.highstate'
32
+ # Dynflowize the action if we can, otherwise we'll do it live
33
+ if defined? ForemanTasks
34
+ task = ForemanTasks.async_task(::Actions::ForemanSalt::ReportImport, params[:job], detected_proxy.try(:id))
35
+ render :json => {:task_id => task.id}
36
+ else
37
+ reports = ForemanSalt::ReportImporter.import(params[:job][:result], detected_proxy.try(:id))
38
+ render :json => {:message => "Imported #{reports.count} new reports."}
39
+ end
40
+ else
41
+ render :json => {:message => 'Unsupported function'}, :status => :unprocessable_entity
42
+ end
43
+ rescue ::Foreman::Exception => e
44
+ render :json => {:message => e.to_s}, :status => :unprocessable_entity
45
+ end
46
+
47
+ def resource_class
48
+ ::Report
49
+ end
50
+
51
+ private
52
+
53
+ def action_permission
54
+ case params[:action]
55
+ when 'upload'
56
+ :create
57
+ else
58
+ super
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+
@@ -0,0 +1,29 @@
1
+ module Actions
2
+ module ForemanSalt
3
+ if defined? ForemanTasks
4
+ class ReportImport < Actions::EntryAction
5
+
6
+ def resource_locks
7
+ :report_import
8
+ end
9
+
10
+ def plan(job, proxy_id)
11
+ plan_self(:job_id => job[:job_id], :report => job[:result], :proxy_id => proxy_id)
12
+ end
13
+
14
+ def run
15
+ ::User.as_anonymous_admin do
16
+ reports = ::ForemanSalt::ReportImporter.import(input[:report], input[:proxy_id])
17
+
18
+ output[:state] = {:message => "Imported #{reports.count} new reports"}
19
+ output[:hosts] = reports.map { |report| report.host.name }
20
+ end
21
+ end
22
+
23
+ def humanized_name
24
+ _("Process Highstate Report: #{input[:job_id]}")
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -51,7 +51,7 @@ module ForemanSalt
51
51
 
52
52
  def set_hostgroup_defaults_with_salt_proxy
53
53
  return unless hostgroup
54
- assign_hostgroup_attributes(%w{salt_proxy_id})
54
+ assign_hostgroup_attributes(%w{salt_proxy_id salt_environment_id})
55
55
  set_hostgroup_defaults_without_salt_proxy
56
56
  end
57
57
 
@@ -0,0 +1,152 @@
1
+ module ForemanSalt
2
+ class ReportImporter
3
+ delegate :logger, :to => :Rails
4
+ attr_reader :report
5
+
6
+ def self.import(raw, proxy_id = nil)
7
+ raise ::Foreman::Exception.new(_('Invalid report')) unless raw.is_a?(Hash)
8
+ raw.map do |host, report|
9
+ importer = ForemanSalt::ReportImporter.new(host, report, proxy_id)
10
+ importer.import
11
+ importer.report
12
+ end
13
+ end
14
+
15
+ def initialize(host, raw, proxy_id = nil)
16
+ @host = find_or_create_host(host)
17
+ @raw = raw
18
+ @proxy_id = proxy_id
19
+ end
20
+
21
+ def import
22
+ logger.info "processing report for #{@host}"
23
+ logger.debug { "Report: #{@raw.inspect}" }
24
+
25
+ if @host.new_record? && !Setting[:create_new_host_when_report_is_uploaded]
26
+ logger.info("skipping report for #{@host} as its an unknown host and create_new_host_when_report_is_uploaded setting is disabled")
27
+ return Report.new
28
+ end
29
+
30
+ @host.salt_proxy_id ||= @proxy_id
31
+ @host.last_report = start_time
32
+
33
+ if @raw.is_a? Array
34
+ process_failures # If Salt sends us only an array, it's a list of fatal failures
35
+ else
36
+ process_normal
37
+ end
38
+
39
+ @host.save(:validate => false)
40
+ logger.info("Imported report for #{@host} in #{(Time.now - start_time).round(2)} seconds")
41
+ end
42
+
43
+ private
44
+
45
+ def find_or_create_host(host)
46
+ @host ||= Host::Base.find_by_name(host)
47
+
48
+ unless @host
49
+ new = Host::Managed.new(:name => host)
50
+ new.save(:validate => false)
51
+ @host = new
52
+ end
53
+
54
+ @host
55
+ end
56
+
57
+ def import_log_messages
58
+ @raw.each do |resource, result|
59
+ level = if result['changes'].blank? && result['result']
60
+ :info
61
+ elsif result['result'] == false
62
+ :err
63
+ else
64
+ :notice
65
+ end
66
+
67
+ source = Source.find_or_create(resource)
68
+
69
+ message = if result['changes']['diff']
70
+ result['changes']['diff']
71
+ elsif !result['comment'].blank?
72
+ result['comment']
73
+ else
74
+ 'No message available'
75
+ end
76
+
77
+ message = Message.find_or_create(message)
78
+ Log.create(:message_id => message.id, :source_id => source.id, :report => @report, :level => level)
79
+ end
80
+ end
81
+
82
+ def calculate_metrics
83
+ success = 0
84
+ failed = 0
85
+ changed = 0
86
+ restarted = 0
87
+ restarted_failed = 0
88
+
89
+ time = {}
90
+
91
+ @raw.each do |resource, result|
92
+ next unless result.is_a? Hash
93
+
94
+ if result['result'] == true
95
+ success += 1
96
+ if resource.match(/^service_/) && result['comment'].include?('restarted')
97
+ restarted += 1
98
+ elsif !result['changes'].blank?
99
+ changed += 1
100
+ end
101
+ elsif result['result'] == false
102
+ if resource.match(/^service_/) && result['comment'].include?('restarted')
103
+ restarted_failed += 1
104
+ else
105
+ failed += 1
106
+ end
107
+ end
108
+
109
+ time[resource] = result['duration'] if result['duration']
110
+ end
111
+
112
+ time[:total] = time.values.inject(&:+)
113
+ events = {:total => changed + failed + restarted + restarted_failed, :success => success + restarted, :failure => failed + restarted_failed}
114
+
115
+ changes = {:total => changed + restarted}
116
+
117
+ resources = {'total' => @raw.size, 'applied' => changed, 'restarted' => restarted, 'failed' => failed,
118
+ 'failed_restarts' => restarted_failed, 'skipped' => 0, 'scheduled' => 0}
119
+
120
+ {:events => events, :resources => resources, :changes => changes, :time => time}
121
+ end
122
+
123
+ def process_normal
124
+ metrics = calculate_metrics
125
+ status = ReportStatusCalculator.new(:counters => metrics[:resources].slice(*::Report::METRIC)).calculate
126
+
127
+ @host.puppet_status = status
128
+
129
+ @report = Report.new(:host => @host, :reported_at => start_time, :status => status, :metrics => metrics)
130
+ return @report unless @report.save
131
+ import_log_messages
132
+ end
133
+
134
+
135
+ def process_failures
136
+ status = ReportStatusCalculator.new({:counters => {'failed' => @raw.size}}).calculate
137
+ @report = Report.create(:host => @host, :reported_at => Time.now, :status => status, :metrics => {})
138
+
139
+ @host.puppet_status = status
140
+
141
+ source = Source.find_or_create('Salt')
142
+ @raw.each do |failure|
143
+ message = Message.find_or_create(failure)
144
+ Log.create(:message_id => message.id, :source_id => source.id, :report => @report, :level => :err)
145
+ end
146
+ end
147
+
148
+ def start_time
149
+ @start_time ||= Time.now
150
+ end
151
+ end
152
+ end
data/config/routes.rb CHANGED
@@ -14,6 +14,10 @@ Rails.application.routes.draw do
14
14
  get 'auto_complete_search'
15
15
  end
16
16
  end
17
+
18
+ scope :api, :defaults => {:format => 'json'}, :constraints => ApiConstraints.new(:version => 2) do
19
+ match 'api/v2/jobs/upload' => 'foreman_salt/api/v2/jobs#upload', :via => :post
20
+ end
17
21
  end
18
22
 
19
23
  constraints(:smart_proxy_id => /[^\/]+/) do
data/lib/foreman_salt.rb CHANGED
@@ -1,4 +1,10 @@
1
- require "foreman_salt/engine"
1
+ require 'foreman_salt/engine'
2
+
3
+ begin
4
+ require 'foreman-tasks'
5
+ rescue LoadError
6
+ # Foreman Tasks isn't available
7
+ end
2
8
 
3
9
  module ForemanSalt
4
10
  end
@@ -10,6 +10,12 @@ module ForemanSalt
10
10
  config.autoload_paths += Dir["#{config.root}/app/services"]
11
11
  config.autoload_paths += Dir["#{config.root}/app/lib"]
12
12
 
13
+ if defined? ForemanTasks
14
+ initializer "foreman_salt.require_dynflow", :before => "foreman_tasks.initialize_dynflow" do |app|
15
+ ForemanTasks.dynflow.require!
16
+ end
17
+ end
18
+
13
19
  # Add any db migrations
14
20
  initializer "foreman_salt.load_app_instance_data" do |app|
15
21
  app.config.paths['db/migrate'] += ForemanSalt::Engine.paths['db/migrate'].existent
@@ -66,6 +72,10 @@ module ForemanSalt
66
72
  permission :view_smart_proxies_salt_autosign, {:'foreman_salt/salt_autosign' => [:index]}, :resource_type => "SmartProxy"
67
73
  end
68
74
 
75
+ security_block :api do |map|
76
+ permission :create_reports, {:'foreman_salt/api/v2/jobs' => [:upload]}, :resource_type => "Report"
77
+ end
78
+
69
79
  role "Salt admin", [:saltrun_hosts, :create_salt_modules, :view_salt_modules, :edit_salt_modules, :destroy_salt_modules,
70
80
  :view_smart_proxies_salt_keys, :destroy_smart_proxies_salt_keys, :edit_smart_proxies_salt_keys,
71
81
  :create_smart_proxies_salt_autosign, :view_smart_proxies_salt_autosign, :destroy_smart_proxies_salt_autosign,
@@ -1,3 +1,3 @@
1
1
  module ForemanSalt
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -0,0 +1,196 @@
1
+ {
2
+ "saltclient713.example.com": {
3
+ "pkg_|-postfix_|-postfix_|-installed": {
4
+ "comment": "Package postfix is already installed.",
5
+ "name": "postfix",
6
+ "start_time": "00:18:06.518702",
7
+ "result": true,
8
+ "duration": 1.575,
9
+ "__run_num__": 13,
10
+ "changes": {
11
+ },
12
+ "retcode": 2
13
+ },
14
+ "file_|-mysql_config_|-/etc/my.cnf_|-managed": {
15
+ "comment": "File /etc/my.cnf updated",
16
+ "name": "/etc/my.cnf",
17
+ "start_time": "00:18:00.901791",
18
+ "result": true,
19
+ "duration": 978.938,
20
+ "__run_num__": 6,
21
+ "changes": {
22
+ "diff": "--- \n+++ \n@@ -1,10 +1,39 @@\n-[mysqld]\n-datadir=/var/lib/mysql\n-socket=/var/lib/mysql/mysql.sock\n-user=mysql\n-# Disabling symbolic-links is recommended to prevent assorted security risks\n-symbolic-links=0\n+# DO NOT CHANGE THIS FILE!\n+# This config is generated by SALTSTACK\n+# and all change will be overrided on next salt call\n+\n+\n+[myisampack]\n+\n+[mysqlimport]\n+\n+[mysqlcheck]\n \n [mysqld_safe]\n-log-error=/var/log/mysqld.log\n-pid-file=/var/run/mysqld/mysqld.pid\n+log-error = /var/log/mysqld.log\n+pid-file = /var/run/mysqld/mysqld.pid\n+\n+[mysqladmin]\n+\n+[mysqlshow]\n+\n+[myisamchk]\n+\n+[client]\n+\n+[mysqlhotcopy]\n+\n+[mysqldump]\n+\n+[mysql]\n+\n+[mysqld]\n+bind-address = 127.0.0.1\n+datadir = /var/lib/mysql\n+port = 3306\n+socket = /var/lib/mysql/mysql.sock\n+symbolic-links = 0\n+user = mysql\n+\n+[isamchk]\n+\n"
23
+ },
24
+ "retcode": 2
25
+ },
26
+ "mysql_database_|-mysql remove test database_|-test_|-absent": {
27
+ "comment": "Database test has been removed",
28
+ "name": "test",
29
+ "start_time": "00:18:06.511324",
30
+ "result": true,
31
+ "duration": 7.266,
32
+ "__run_num__": 12,
33
+ "changes": {
34
+ "test": "Absent"
35
+ },
36
+ "retcode": 2
37
+ },
38
+ "service_|-postfix_|-postfix_|-running": {
39
+ "comment": "Service postfix is already enabled, and is in the desired state",
40
+ "name": "postfix",
41
+ "start_time": "00:18:06.520605",
42
+ "result": true,
43
+ "duration": 53.025,
44
+ "__run_num__": 14,
45
+ "changes": {
46
+ }
47
+ },
48
+ "mysql_user_|-mysql_delete_anonymous_user_saltclient713.example.com_|-_|-absent": {
49
+ "comment": "An exception occurred in this state: Traceback (most recent call last):\n File \"/usr/lib/python2.6/site-packages/salt/state.py\", line 1533, in call\n **cdata['kwargs'])\n File \"/usr/lib/python2.6/site-packages/salt/states/mysql_user.py\", line 239, in absent\n if __salt__['mysql.user_exists'](name, host, **connection_args):\n File \"/usr/lib/python2.6/site-packages/salt/modules/mysql.py\", line 1023, in user_exists\n dbc = _connect(**connection_args)\n File \"/usr/lib/python2.6/site-packages/salt/modules/mysql.py\", line 287, in _connect\n dbc = MySQLdb.connect(**connargs)\n File \"/usr/lib64/python2.6/site-packages/MySQLdb/__init__.py\", line 81, in Connect\n return Connection(*args, **kwargs)\n File \"/usr/lib64/python2.6/site-packages/MySQLdb/connections.py\", line 187, in __init__\n super(Connection, self).__init__(*args, **kwargs2)\nTypeError: connect() argument 3 must be string, not int\n",
50
+ "name": "",
51
+ "start_time": "00:18:06.510447",
52
+ "result": false,
53
+ "duration": 0.752,
54
+ "__run_num__": 11,
55
+ "changes": {
56
+ }
57
+ },
58
+ "mysql_user_|-mysql_delete_anonymous_user_localhost_|-_|-absent": {
59
+ "comment": "An exception occurred in this state: Traceback (most recent call last):\n File \"/usr/lib/python2.6/site-packages/salt/state.py\", line 1533, in call\n **cdata['kwargs'])\n File \"/usr/lib/python2.6/site-packages/salt/states/mysql_user.py\", line 239, in absent\n if __salt__['mysql.user_exists'](name, host, **connection_args):\n File \"/usr/lib/python2.6/site-packages/salt/modules/mysql.py\", line 1023, in user_exists\n dbc = _connect(**connection_args)\n File \"/usr/lib/python2.6/site-packages/salt/modules/mysql.py\", line 287, in _connect\n dbc = MySQLdb.connect(**connargs)\n File \"/usr/lib64/python2.6/site-packages/MySQLdb/__init__.py\", line 81, in Connect\n return Connection(*args, **kwargs)\n File \"/usr/lib64/python2.6/site-packages/MySQLdb/connections.py\", line 187, in __init__\n super(Connection, self).__init__(*args, **kwargs2)\nTypeError: connect() argument 3 must be string, not int\n",
60
+ "_stamp": "2014-11-16T01:18:06.783560",
61
+ "return": "Error: mysql_user.absent",
62
+ "name": "",
63
+ "success": false,
64
+ "start_time": "00:18:06.488962",
65
+ "jid": "20141116011706024672",
66
+ "duration": 19.798,
67
+ "result": false,
68
+ "fun": "state.highstate",
69
+ "__run_num__": 9,
70
+ "changes": {
71
+ },
72
+ "id": "saltclient713.example.com",
73
+ "retcode": 2
74
+ },
75
+ "service_|-mysqld_|-mysqld_|-running": {
76
+ "comment": "Service mysqld has been enabled, and is running",
77
+ "name": "mysqld",
78
+ "start_time": "00:18:01.881541",
79
+ "result": true,
80
+ "duration": 4566.262,
81
+ "__run_num__": 7,
82
+ "changes": {
83
+ "mysqld": true
84
+ }
85
+ },
86
+ "mysql_user_|-mysql_delete_anonymous_user_localhost.localdomain_|-_|-absent": {
87
+ "comment": "An exception occurred in this state: Traceback (most recent call last):\n File \"/usr/lib/python2.6/site-packages/salt/state.py\", line 1533, in call\n **cdata['kwargs'])\n File \"/usr/lib/python2.6/site-packages/salt/states/mysql_user.py\", line 239, in absent\n if __salt__['mysql.user_exists'](name, host, **connection_args):\n File \"/usr/lib/python2.6/site-packages/salt/modules/mysql.py\", line 1023, in user_exists\n dbc = _connect(**connection_args)\n File \"/usr/lib/python2.6/site-packages/salt/modules/mysql.py\", line 287, in _connect\n dbc = MySQLdb.connect(**connargs)\n File \"/usr/lib64/python2.6/site-packages/MySQLdb/__init__.py\", line 81, in Connect\n return Connection(*args, **kwargs)\n File \"/usr/lib64/python2.6/site-packages/MySQLdb/connections.py\", line 187, in __init__\n super(Connection, self).__init__(*args, **kwargs2)\nTypeError: connect() argument 3 must be string, not int\n",
88
+ "name": "",
89
+ "start_time": "00:18:06.509230",
90
+ "result": false,
91
+ "duration": 0.8,
92
+ "__run_num__": 10,
93
+ "changes": {
94
+ }
95
+ },
96
+ "pkg_|-mysqld_|-mysql-server_|-installed": {
97
+ "comment": "The following packages were installed/updated: mysql-server.",
98
+ "name": "mysql-server",
99
+ "start_time": "00:17:41.058929",
100
+ "result": true,
101
+ "duration": 19842.654,
102
+ "__run_num__": 5,
103
+ "changes": {
104
+ "perl-DBI": {
105
+ "new": "1.609-4.el6",
106
+ "old": ""
107
+ },
108
+ "perl-DBD-MySQL": {
109
+ "new": "4.013-3.el6",
110
+ "old": ""
111
+ },
112
+ "mysql-server": {
113
+ "new": "5.1.73-3.el6_5",
114
+ "old": ""
115
+ },
116
+ "mysql": {
117
+ "new": "5.1.73-3.el6_5",
118
+ "old": ""
119
+ }
120
+ }
121
+ },
122
+ "cmd_|-mysql_root_password_|-mysqladmin --user root password '434510094'_|-run": {
123
+ "comment": "Command \"mysqladmin --user root password '434510094'\" run",
124
+ "name": "mysqladmin --user root password '434510094'",
125
+ "start_time": "00:18:06.448367",
126
+ "result": true,
127
+ "duration": 39.196,
128
+ "__run_num__": 8,
129
+ "changes": {
130
+ "pid": 2709,
131
+ "retcode": 0,
132
+ "stderr": "",
133
+ "stdout": ""
134
+ }
135
+ },
136
+ "pkg_|-mysql_python_|-MySQL-python_|-installed": {
137
+ "comment": "The following packages were installed/updated: MySQL-python.",
138
+ "name": "MySQL-python",
139
+ "start_time": "00:17:14.059753",
140
+ "result": true,
141
+ "duration": 26997.765,
142
+ "__run_num__": 4,
143
+ "changes": {
144
+ "MySQL-python": {
145
+ "new": "1.2.3-0.3.c1.1.el6",
146
+ "old": ""
147
+ }
148
+ },
149
+ "retcode": 2
150
+ },
151
+ "file_|-enable_epel_|-/etc/yum.repos.d/epel.repo_|-sed": {
152
+ "comment": "sed ran without error",
153
+ "name": "/etc/yum.repos.d/epel.repo",
154
+ "start_time": "00:17:14.024416",
155
+ "result": true,
156
+ "duration": 35.098,
157
+ "__run_num__": 3,
158
+ "changes": {
159
+ "diff": "--- \n+++ \n@@ -12,7 +12,7 @@\n #baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch/debug\n mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-debug-6&arch=$basearch\n failovermethod=priority\n-enabled=0\n+enabled=1\n gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6\n gpgcheck=1\n \n@@ -21,6 +21,6 @@\n #baseurl=http://download.fedoraproject.org/pub/epel/6/SRPMS\n mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-source-6&arch=$basearch\n failovermethod=priority\n-enabled=0\n+enabled=1\n gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6\n gpgcheck=1\n"
160
+ }
161
+ },
162
+ "pkg_|-epel_release_|-epel_release_|-installed": {
163
+ "comment": "All specified packages are already installed.",
164
+ "name": "epel_release",
165
+ "start_time": "00:17:13.178528",
166
+ "result": true,
167
+ "duration": 845.684,
168
+ "__run_num__": 2,
169
+ "changes": {
170
+ }
171
+ },
172
+ "file_|-/etc/motd_|-/etc/motd_|-managed": {
173
+ "comment": "File /etc/motd updated",
174
+ "name": "/etc/motd",
175
+ "start_time": "00:17:11.971247",
176
+ "result": true,
177
+ "duration": 163.144,
178
+ "__run_num__": 0,
179
+ "changes": {
180
+ "diff": "--- \n+++ \n@@ -1,0 +1,7 @@\n+_| _| _| _| _|\n+_|_|_| _|_|_|_| _|_|_| _|_|_|\n+_| _| _| _| _| _| _| _| _|\n+_| _| _| _| _| _| _| _| _|\n+_|_|_| _| _|_| _|_|_| _| _| _|\n+\n+saltclient713.example.com\n"
181
+ }
182
+ },
183
+ "file_|-install_pubkey_|-/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL_|-managed": {
184
+ "comment": "File /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL updated",
185
+ "name": "/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL",
186
+ "start_time": "00:17:12.134782",
187
+ "result": true,
188
+ "duration": 1042.983,
189
+ "__run_num__": 1,
190
+ "changes": {
191
+ "diff": "New file",
192
+ "mode": "0644"
193
+ }
194
+ }
195
+ }
196
+ }
@@ -0,0 +1,32 @@
1
+ require 'test_plugin_helper'
2
+
3
+ module ForemanSalt
4
+ class ReportImporterTest < ActiveSupport::TestCase
5
+ setup do
6
+ User.current = User.find_by_login 'admin'
7
+ Setting[:create_new_host_when_facts_are_uploaded] = true
8
+
9
+ @report = JSON.parse(File.read(File.join(Engine.root, 'test', 'unit', 'highstate.json')))
10
+
11
+ @host = 'saltclient713.example.com'
12
+ end
13
+
14
+ test 'importing report creates a host' do
15
+ refute Host.find_by_name(@host)
16
+ ForemanSalt::ReportImporter.import(@report)
17
+ assert Host.find_by_name(@host)
18
+ end
19
+
20
+ test "importing report updates host status" do
21
+ ForemanSalt::ReportImporter.import(@report)
22
+ assert_equal Host.find_by_name(@host).host_status, 'Error'
23
+ end
24
+
25
+ test 'importing report has correct status' do
26
+ ForemanSalt::ReportImporter.import(@report)
27
+ status = Host.find_by_name(@host).reports.last.status
28
+ assert_equal status['applied'], 9
29
+ assert_equal status['failed'], 3
30
+ end
31
+ end
32
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_salt
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Benjamin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-11 00:00:00.000000000 Z
11
+ date: 2014-11-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: deface
@@ -52,6 +52,7 @@ files:
52
52
  - app/views/foreman_salt/salt_autosign/new.html.erb
53
53
  - app/views/foreman_salt/salt_autosign/_form.html.erb
54
54
  - app/services/foreman_salt/smart_proxies/salt_keys.rb
55
+ - app/services/foreman_salt/report_importer.rb
55
56
  - app/services/foreman_salt/fact_importer.rb
56
57
  - app/models/foreman_salt/salt_module.rb
57
58
  - app/models/foreman_salt/concerns/hostgroup_extensions.rb
@@ -67,8 +68,10 @@ files:
67
68
  - app/controllers/foreman_salt/concerns/smart_proxy_auth_extensions.rb
68
69
  - app/controllers/foreman_salt/concerns/hosts_controller_extensions.rb
69
70
  - app/controllers/foreman_salt/concerns/unattended_controller_extensions.rb
71
+ - app/controllers/foreman_salt/api/v2/jobs_controller.rb
70
72
  - app/controllers/foreman_salt/salt_environments_controller.rb
71
73
  - app/lib/proxy_api/salt.rb
74
+ - app/lib/actions/foreman_salt/report_import.rb
72
75
  - config/routes.rb
73
76
  - db/seeds.d/75-salt-seeds.rb
74
77
  - db/migrate/20140920232200_create_salt_environments.rb
@@ -90,8 +93,10 @@ files:
90
93
  - test/unit/grains_centos.json
91
94
  - test/unit/host_extensions_test.rb
92
95
  - test/unit/salt_modules_test.rb
96
+ - test/unit/highstate.json
93
97
  - test/unit/grains_importer_test.rb
94
98
  - test/unit/hostgroup_extensions_test.rb
99
+ - test/unit/report_importer_test.rb
95
100
  - test/test_plugin_helper.rb
96
101
  - test/factories/foreman_salt_factories.rb
97
102
  - test/functional/hosts_controller_test.rb
@@ -128,8 +133,10 @@ test_files:
128
133
  - test/unit/grains_centos.json
129
134
  - test/unit/host_extensions_test.rb
130
135
  - test/unit/salt_modules_test.rb
136
+ - test/unit/highstate.json
131
137
  - test/unit/grains_importer_test.rb
132
138
  - test/unit/hostgroup_extensions_test.rb
139
+ - test/unit/report_importer_test.rb
133
140
  - test/test_plugin_helper.rb
134
141
  - test/factories/foreman_salt_factories.rb
135
142
  - test/functional/hosts_controller_test.rb