smart_proxy_salt 2.1.8 → 3.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+