smart_proxy_salt 2.1.8 → 3.1.2

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.
@@ -1,31 +1,34 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
  require 'smart_proxy_salt/api_request'
3
5
 
4
- module Proxy::Salt::Rest
5
- extend ::Proxy::Log
6
- extend ::Proxy::Util
6
+ module Proxy
7
+ module Salt
8
+ # Rest Salt API methods
9
+ module Rest
10
+ extend ::Proxy::Log
11
+ extend ::Proxy::Util
7
12
 
8
- class << self
9
- def environments_list
10
- JSON.dump(Proxy::Salt::ApiRequest.new.post('/run', :fun => 'fileserver.envs', :client => 'runner')['return'][0])
11
- end
13
+ class << self
14
+ def environments_list
15
+ JSON.dump(Proxy::Salt::ApiRequest.new.post('/run', :fun => 'fileserver.envs', :client => 'runner')['return'][0])
16
+ end
12
17
 
13
- def states_list(environment)
14
- states = []
15
- files = Proxy::Salt::ApiRequest.new.post('/run', :fun => 'fileserver.file_list',
16
- :saltenv => environment,
17
- :client => 'runner')['return'][0]
18
+ def states_list(environment)
19
+ states = []
20
+ files = Proxy::Salt::ApiRequest.new.post('/run', :fun => 'fileserver.file_list',
21
+ :saltenv => environment,
22
+ :client => 'runner')['return'][0]
18
23
 
19
- files.each do |file|
20
- if file =~ /\.sls\Z/ && file != "top.sls"
21
- states << file.gsub('.sls', '').
22
- gsub('/init', '').
23
- chomp('/').
24
- gsub('/', '.')
24
+ files.each do |file|
25
+ next unless file =~ /\.sls\Z/ && file != 'top.sls'
26
+ states << file.gsub('.sls', '').gsub('/init', '').chomp('/').tr('/', '.')
27
+ end
28
+
29
+ JSON.dump(states)
25
30
  end
26
31
  end
27
-
28
- JSON.dump(states)
29
32
  end
30
33
  end
31
34
  end
@@ -1,31 +1,37 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'smart_proxy_salt/cli'
2
4
  require 'smart_proxy_salt/rest'
3
5
 
4
- module Proxy::Salt
5
- class NotFound < RuntimeError; end
6
+ module Proxy
7
+ # SmartProxy Salt
8
+ module Salt
9
+ class NotFound < RuntimeError; end
6
10
 
7
- class Plugin < ::Proxy::Plugin
8
- plugin 'salt', Proxy::Salt::VERSION
11
+ # Implement a SmartProxy plugin
12
+ class Plugin < ::Proxy::Plugin
13
+ plugin 'salt', Proxy::Salt::VERSION
9
14
 
10
- default_settings :autosign_file => '/etc/salt/autosign.conf',
11
- :salt_command_user => 'root',
12
- :use_api => false
15
+ default_settings :autosign_file => '/etc/salt/autosign.conf',
16
+ :salt_command_user => 'root',
17
+ :use_api => false
13
18
 
14
- http_rackup_path File.expand_path('salt_http_config.ru', File.expand_path('../', __FILE__))
15
- https_rackup_path File.expand_path('salt_http_config.ru', File.expand_path('../', __FILE__))
16
- end
19
+ http_rackup_path File.expand_path('salt_http_config.ru', File.expand_path('../', __FILE__))
20
+ https_rackup_path File.expand_path('salt_http_config.ru', File.expand_path('../', __FILE__))
21
+ end
17
22
 
18
- class << self
19
- def method_missing(m, *args, &block)
20
- # Use API, if it supports it, otherwise fallback to the CLI
21
- if Proxy::Salt::Plugin.settings.use_api && Proxy::Salt::Rest.respond_to?(m)
22
- Proxy::Salt::Rest.send(m, *args, &block)
23
- elsif Proxy::Salt::CLI.respond_to?(m)
24
- Proxy::Salt::CLI.send(m, *args, &block)
25
- elsif !Proxy::Salt::Plugin.settings.use_api && Proxy::Salt::Rest.respond_to?(m)
26
- raise NotImplementedError, 'You must enable the Salt API to use this feature.'
27
- else
28
- super
23
+ class << self
24
+ def method_missing(m, *args, &block)
25
+ # Use API, if it supports it, otherwise fallback to the CLI
26
+ if Proxy::Salt::Plugin.settings.use_api && Proxy::Salt::Rest.respond_to?(m)
27
+ Proxy::Salt::Rest.send(m, *args, &block)
28
+ elsif Proxy::Salt::CLI.respond_to?(m)
29
+ Proxy::Salt::CLI.send(m, *args, &block)
30
+ elsif !Proxy::Salt::Plugin.settings.use_api && Proxy::Salt::Rest.respond_to?(m)
31
+ raise NotImplementedError.new('You must enable the Salt API to use this feature.')
32
+ else
33
+ super
34
+ end
29
35
  end
30
36
  end
31
37
  end
@@ -1,104 +1,109 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'sinatra'
2
4
  require 'smart_proxy_salt/salt'
3
5
 
4
- module Proxy::Salt
5
- class Api < ::Sinatra::Base
6
- include ::Proxy::Log
7
- helpers ::Proxy::Helpers
8
- authorize_with_ssl_client
6
+ module Proxy
7
+ module Salt
8
+ # Implement the SmartProxy API
9
+ class Api < ::Sinatra::Base
10
+ include ::Proxy::Log
11
+ helpers ::Proxy::Helpers
12
+ authorize_with_ssl_client
9
13
 
10
- post '/autosign/:host' do
11
- content_type :json
12
- begin
13
- Proxy::Salt.autosign_create(params[:host]).to_json
14
- rescue => e
15
- log_halt 406, "Failed to create autosign for #{params[:host]}: #{e}"
14
+ post '/autosign/:host' do
15
+ content_type :json
16
+ begin
17
+ Proxy::Salt.autosign_create(params[:host]).to_json
18
+ rescue Exception => e
19
+ log_halt 406, "Failed to create autosign for #{params[:host]}: #{e}"
20
+ end
16
21
  end
17
- end
18
22
 
19
- delete '/autosign/:host' do
20
- content_type :json
21
- begin
22
- Proxy::Salt.autosign_remove(params[:host]).to_json
23
- rescue Proxy::Salt::NotFound => e
24
- log_halt 404, "#{e}"
25
- rescue => e
26
- log_halt 406, "Failed to remove autosign for #{params[:host]}: #{e}"
23
+ delete '/autosign/:host' do
24
+ content_type :json
25
+ begin
26
+ Proxy::Salt.autosign_remove(params[:host]).to_json
27
+ rescue Proxy::Salt::NotFound => e
28
+ log_halt 404, e.to_s
29
+ rescue Exception => e
30
+ log_halt 406, "Failed to remove autosign for #{params[:host]}: #{e}"
31
+ end
27
32
  end
28
- end
29
33
 
30
- get '/autosign' do
31
- content_type :json
32
- begin
33
- Proxy::Salt::autosign_list.to_json
34
- rescue => e
35
- log_halt 406, "Failed to list autosign entries: #{e}"
34
+ get '/autosign' do
35
+ content_type :json
36
+ begin
37
+ Proxy::Salt.autosign_list.to_json
38
+ rescue Exception => e
39
+ log_halt 406, "Failed to list autosign entries: #{e}"
40
+ end
36
41
  end
37
- end
38
42
 
39
- get '/environments' do
40
- content_type :json
41
- begin
42
- Proxy::Salt::environments_list
43
- rescue => e
44
- log_halt 406, "Failed to list environments: #{e}"
43
+ get '/environments' do
44
+ content_type :json
45
+ begin
46
+ Proxy::Salt.environments_list
47
+ rescue Exception => e
48
+ log_halt 406, "Failed to list environments: #{e}"
49
+ end
45
50
  end
46
- end
47
51
 
48
- get '/environments/:environment' do
49
- content_type :json
50
- begin
51
- Proxy::Salt::states_list params[:environment]
52
- rescue Proxy::Salt::NotFound => e
53
- log_halt 404, "#{e}"
54
- rescue => e
55
- log_halt 406, "Failed to list states for #{params[:host]}: #{e}"
52
+ get '/environments/:environment' do
53
+ content_type :json
54
+ begin
55
+ Proxy::Salt.states_list params[:environment]
56
+ rescue Proxy::Salt::NotFound => e
57
+ log_halt 404, e.to_s
58
+ rescue Exception => e
59
+ log_halt 406, "Failed to list states for #{params[:host]}: #{e}"
60
+ end
56
61
  end
57
- end
58
62
 
59
- post '/highstate/:host' do
60
- content_type :json
61
- begin
62
- log_halt 500, "Failed salt run for #{params[:host]}: Check Log files" unless (result = Proxy::Salt.highstate(params[:host]))
63
- result
64
- rescue => e
65
- log_halt 406, "Failed salt run for #{params[:host]}: #{e}"
63
+ post '/highstate/:host' do
64
+ content_type :json
65
+ begin
66
+ log_halt 500, "Failed salt run for #{params[:host]}: Check Log files" unless (result = Proxy::Salt.highstate(params[:host]))
67
+ result
68
+ rescue Exception => e
69
+ log_halt 406, "Failed salt run for #{params[:host]}: #{e}"
70
+ end
66
71
  end
67
- end
68
72
 
69
- delete '/key/:host' do
70
- content_type :json
71
- begin
72
- Proxy::Salt.key_delete(params[:host])
73
- rescue => e
74
- log_halt 406, "Failed delete salt key for #{params[:host]}: #{e}"
73
+ delete '/key/:host' do
74
+ content_type :json
75
+ begin
76
+ Proxy::Salt.key_delete(params[:host])
77
+ rescue Exception => e
78
+ log_halt 406, "Failed delete salt key for #{params[:host]}: #{e}"
79
+ end
75
80
  end
76
- end
77
81
 
78
- post '/key/:host' do
79
- content_type :json
80
- begin
81
- Proxy::Salt::key_accept(params[:host])
82
- rescue => e
83
- log_halt 406, "Failed to accept salt key for #{params[:host]}: #{e}"
82
+ post '/key/:host' do
83
+ content_type :json
84
+ begin
85
+ Proxy::Salt.key_accept(params[:host])
86
+ rescue Exception => e
87
+ log_halt 406, "Failed to accept salt key for #{params[:host]}: #{e}"
88
+ end
84
89
  end
85
- end
86
90
 
87
- delete '/key/reject/:host' do
88
- content_type :json
89
- begin
90
- Proxy::Salt::key_reject(params[:host])
91
- rescue => e
92
- log_halt 406, "Failed to reject salt key for #{params[:host]}: #{e}"
91
+ delete '/key/reject/:host' do
92
+ content_type :json
93
+ begin
94
+ Proxy::Salt.key_reject(params[:host])
95
+ rescue Exception => e
96
+ log_halt 406, "Failed to reject salt key for #{params[:host]}: #{e}"
97
+ end
93
98
  end
94
- end
95
99
 
96
- get '/key' do
97
- content_type :json
98
- begin
99
- Proxy::Salt::key_list.to_json
100
- rescue => e
101
- log_halt 406, "Failed to list keys: #{e}"
100
+ get '/key' do
101
+ content_type :json
102
+ begin
103
+ Proxy::Salt.key_list.to_json
104
+ rescue Exception => e
105
+ log_halt 406, "Failed to list keys: #{e}"
106
+ end
102
107
  end
103
108
  end
104
109
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'smart_proxy_salt/salt_api'
2
4
  map '/salt' do
3
5
  run Proxy::Salt::Api
@@ -1,5 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Proxy
4
+ # Salt module
2
5
  module Salt
3
- VERSION = '2.1.8'
6
+ VERSION = '3.1.2'
4
7
  end
5
8
  end
@@ -0,0 +1,31 @@
1
+ # Foreman Salt Report Upload
2
+
3
+ Currently, there are two possibilites to upload the salt report to Foreman:
4
+ 1. Use /usr/sbin/upload-salt-reports which is called by a cron job every 10 minutes by default
5
+ 2. Upload the report immediately by using a Salt Reactor.
6
+
7
+ This README, handles the second option and how to configure it
8
+
9
+ ## Setup
10
+ Add the content of 'master.snippet' to '/etc/salt/master' which configures a reactor.
11
+ In case there is already a reactor configured, you need to adapt it using the options mentioned in 'master.snippet'.
12
+
13
+ In case '/srv/salt' is configured as 'file_roots' in your '/etc/salt/master' config, setup the necessary salt state file and Salt runner functions:
14
+
15
+ ```
16
+ /srv/salt/foreman_report_upload.sls
17
+ /srv/salt/_runners/foreman_report_upload.py
18
+ ```
19
+
20
+ After changing the salt master run:
21
+
22
+ ```
23
+ systemctl restart salt-master
24
+ ```
25
+
26
+ After adding the foreman_report_upload.sls and foreman_report_upload.py, run the following:
27
+
28
+ ```
29
+ salt-run saltutil.sync_all
30
+ ```
31
+
@@ -0,0 +1,3 @@
1
+ reactor:
2
+ - 'salt/job/*/ret/*':
3
+ - /srv/salt/foreman_report_upload.sls
@@ -0,0 +1,106 @@
1
+ # -*- coding: utf-8 -*-
2
+ '''
3
+ Uploads reports from the Salt job cache to Foreman
4
+ '''
5
+ from __future__ import absolute_import, print_function, unicode_literals
6
+
7
+ FOREMAN_CONFIG = '/etc/salt/foreman.yaml'
8
+
9
+ try:
10
+ from http.client import HTTPConnection, HTTPSConnection
11
+ except ImportError:
12
+ from httplib import HTTPSConnection, HTTPSConnection
13
+
14
+ import ssl
15
+ import json
16
+ import yaml
17
+ import os
18
+ import sys
19
+ import base64
20
+
21
+ # Import python libs
22
+ import logging
23
+
24
+ log = logging.getLogger(__name__)
25
+
26
+
27
+ def salt_config():
28
+ with open(FOREMAN_CONFIG, 'r') as f:
29
+ config = yaml.load(f.read())
30
+ return config
31
+
32
+
33
+ def upload(report):
34
+ config = salt_config()
35
+ headers = {'Accept': 'application/json',
36
+ 'Content-Type': 'application/json'}
37
+
38
+ if config[':proto'] == 'https':
39
+ ctx = ssl.create_default_context()
40
+ ctx.load_cert_chain(certfile=config[':ssl_cert'], keyfile=config[':ssl_key'])
41
+ if config[':ssl_ca']:
42
+ ctx.load_verify_locations(cafile=config[':ssl_ca'])
43
+ connection = HTTPSConnection(config[':host'],
44
+ port=config[':port'], context=ctx)
45
+ else:
46
+ connection = HTTPConnection(config[':host'],
47
+ port=config[':port'])
48
+ if ':username' in config and ':password' in config:
49
+ token = base64.b64encode('{}:{}'.format(config[':username'],
50
+ config[':password']))
51
+ headers['Authorization'] = 'Basic {}'.format(token)
52
+
53
+ connection.request('POST', '/salt/api/v2/jobs/upload',
54
+ json.dumps(report), headers)
55
+ response = connection.getresponse()
56
+
57
+ if response.status == 200:
58
+ info_msg = 'Success {0}: {1}'.format(report['job']['job_id'], response.read())
59
+ log.info(info_msg)
60
+ else:
61
+ log.error("Unable to upload job - aborting report upload")
62
+ log.error(response.read())
63
+
64
+
65
+ def create_report(json_str):
66
+ msg = json.loads(json_str)
67
+
68
+ if msg['fun'] == 'state.highstate':
69
+ return {'job':
70
+ {
71
+ 'result': {
72
+ msg['id']: msg['return'],
73
+ },
74
+ 'function': 'state.highstate',
75
+ 'job_id': msg['jid']
76
+ }
77
+ }
78
+ elif msg['fun'] == 'state.template_str':
79
+ for key, entry in msg['return'].items():
80
+ if key.startswith('module_') and entry['__id__'] == 'state.highstate':
81
+ return {'job':
82
+ {
83
+ 'result': {
84
+ msg['id']: entry['changes']['ret'],
85
+ },
86
+ 'function': 'state.highstate',
87
+ 'job_id': msg['jid']
88
+ }
89
+ }
90
+ raise Exception('No state.highstate found')
91
+
92
+
93
+ def now(highstate):
94
+ '''
95
+ Upload a highstate to Foreman
96
+ highstate :
97
+ dictionary containing a highstate (generated by a Salt reactor)
98
+ '''
99
+ log.debug('Upload highstate to Foreman')
100
+
101
+ try:
102
+ report = create_report(base64.decodestring(highstate))
103
+ upload(report)
104
+ except Exception as exc:
105
+ log.error('Exception encountered: %s', exc)
106
+