nagios-promoo 1.1.0 → 1.2.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.
@@ -0,0 +1,179 @@
1
+ # Internal deps
2
+ require File.join(File.dirname(__FILE__), 'base_probe')
3
+
4
+ module Nagios
5
+ module Promoo
6
+ module Appdb
7
+ module Probes
8
+ # Probe for checking appliance synchronization between sites and AppDB.
9
+ #
10
+ # @author Boris Parak <parak@cesnet.cz>
11
+ class SyncProbe < Nagios::Promoo::Appdb::Probes::BaseProbe
12
+ class << self
13
+ def description
14
+ [
15
+ 'sync',
16
+ 'Run a probe checking consistency between a published VO-wide ' \
17
+ 'image list and appliances available at the site (via AppDB)'
18
+ ]
19
+ end
20
+
21
+ def options
22
+ [
23
+ [
24
+ :vo,
25
+ {
26
+ type: :string,
27
+ required: true,
28
+ desc: 'Virtual Organization name (used to select the appropriate VO-wide image list)'
29
+ }
30
+ ],
31
+ [
32
+ :token,
33
+ {
34
+ type: :string,
35
+ required: true,
36
+ desc: 'AppDB authentication token (used to access the VO-wide image list)'
37
+ }
38
+ ],
39
+ [
40
+ :warning_after,
41
+ {
42
+ type: :numeric,
43
+ default: 24,
44
+ desc: 'A number of hours after list publication when missing or outdated appliances raise WARNING'
45
+ }
46
+ ],
47
+ [
48
+ :critical_after,
49
+ {
50
+ type: :numeric,
51
+ default: 72,
52
+ desc: 'A number of hours after list publication when missing or outdated appliances raise CRITICAL'
53
+ }
54
+ ]
55
+ ]
56
+ end
57
+
58
+ def declaration
59
+ 'sync'
60
+ end
61
+
62
+ def runnable?
63
+ true
64
+ end
65
+ end
66
+
67
+ IMAGE_LIST_TEMPLATE = 'https://$$TOKEN$$:x-oauth-basic@vmcaster.appdb.egi.eu' \
68
+ '/store/vo/$$VO$$/image.list'.freeze
69
+
70
+ def run(_args = [])
71
+ @_results = { found: [], outdated: [], missing: [], expected: [] }
72
+
73
+ Timeout.timeout(options[:timeout]) { check_vmc_sync }
74
+
75
+ wrong = @_results[:missing] + @_results[:outdated]
76
+ if wrong.any?
77
+ if (@_last_update + options[:critical_after].hours) < Time.now
78
+ puts "SYNC CRITICAL - Appliance(s) #{wrong.inspect} missing " \
79
+ "or outdated in #{options[:vo].inspect} " \
80
+ "more than #{options[:critical_after]} hours after list publication [#{@_last_update}]"
81
+ exit 2
82
+ end
83
+
84
+ if (@_last_update + options[:warning_after].hours) < Time.now
85
+ puts "SYNC WARNING - Appliance(s) #{wrong.inspect} missing " \
86
+ "or outdated in #{options[:vo].inspect} " \
87
+ "more than #{options[:warning_after]} hours after list publication [#{@_last_update}]"
88
+ exit 1
89
+ end
90
+ end
91
+
92
+ puts "SYNC OK - All appliances registered in #{options[:vo].inspect} " \
93
+ "are available [#{@_results[:expected].count}]"
94
+ rescue => ex
95
+ puts "SYNC UNKNOWN - #{ex.message}"
96
+ puts ex.backtrace if options[:debug]
97
+ exit 3
98
+ end
99
+
100
+ private
101
+
102
+ def check_vmc_sync
103
+ vo_list.each do |hv_image|
104
+ mpuri_versionless = versionless_mpuri(hv_image['ad:mpuri'])
105
+ @_results[:expected] << mpuri_versionless
106
+
107
+ matching = provider_appliances.detect { |appl| appl['mp_uri'] == mpuri_versionless }
108
+ unless matching
109
+ @_results[:missing] << mpuri_versionless
110
+ next
111
+ end
112
+
113
+ @_results[:outdated] << mpuri_versionless if hv_image['hv:version'] != matching['vmiversion']
114
+ @_results[:found] << mpuri_versionless
115
+ end
116
+ end
117
+
118
+ def provider_appliances
119
+ return @_appliances if @_appliances
120
+
121
+ @_appliances = [appdb_provider['provider:image']].flatten.compact
122
+ @_appliances.keep_if { |appliance| appliance['voname'] == options[:vo] }
123
+ @_appliances.reject { |appliance| appliance['mp_uri'].blank? }
124
+
125
+ @_appliances.each do |appliance|
126
+ appliance['mp_uri'] = versionless_mpuri(appliance['mp_uri'])
127
+ end
128
+
129
+ @_appliances
130
+ end
131
+
132
+ def vo_list
133
+ return @_hv_images if @_hv_images
134
+
135
+ list = JSON.parse pkcs7_data
136
+ raise "AppDB image list #{list_url.inspect} is empty or malformed" unless list && list['hv:imagelist']
137
+
138
+ list = list['hv:imagelist']
139
+ unless DateTime.parse(list['dc:date:expires']) > Time.now
140
+ raise "AppDB image list #{list_url.inspect} has expired"
141
+ end
142
+ raise "AppDB image list #{list_url.inspect} doesn't contain images" unless list['hv:images']
143
+ @_last_update = DateTime.parse list['dc:date:created']
144
+
145
+ @_hv_images = list['hv:images'].collect { |im| im['hv:image'] }
146
+ @_hv_images.reject! { |im| im.blank? || im['ad:mpuri'].blank? }
147
+ @_hv_images
148
+ end
149
+
150
+ def pkcs7_data
151
+ content = OpenSSL::PKCS7.read_smime(retrieve_list)
152
+ content.data
153
+ end
154
+
155
+ def retrieve_list
156
+ response = HTTParty.get list_url
157
+ unless response.success?
158
+ raise 'Could not get a VO-wide image list' \
159
+ "from #{list_url.inspect} [#{response.code}]"
160
+ end
161
+ response.parsed_response
162
+ end
163
+
164
+ def list_url
165
+ IMAGE_LIST_TEMPLATE.gsub('$$TOKEN$$', options[:token]).gsub('$$VO$$', options[:vo])
166
+ end
167
+
168
+ def normalize_mpuri(mpuri)
169
+ mpuri.gsub(%r{/+$}, '')
170
+ end
171
+
172
+ def versionless_mpuri(mpuri)
173
+ normalize_mpuri(mpuri).gsub(/:\d+$/, '')
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
@@ -1,7 +1,7 @@
1
1
  module Nagios
2
2
  module Promoo
3
3
  module Appdb
4
- VERSION = "1.1.0"
4
+ VERSION = '1.2.0'.freeze
5
5
  end
6
6
  end
7
7
  end
@@ -4,29 +4,47 @@
4
4
  # Include available probe modules
5
5
  Dir.glob(File.join(File.dirname(__FILE__), '*', 'master.rb')) { |mod| require mod.chomp('.rb') }
6
6
 
7
- class Nagios::Promoo::Master < ::Thor
8
- class_option :debug, type: :boolean, desc: 'Turn on debugging mode', default: false
9
- class_option :ca_path, type: :string, desc: 'Path to a directory with CA certificates', default: '/etc/grid-security/certificates'
10
- class_option :ca_file, type: :string, desc: 'Path to a file with CA certificates'
11
- class_option :insecure, type: :boolean, desc: 'Turn on insecure mode (without SSL client validation)', default: false
12
- class_option :timeout, type: :numeric, desc: 'Timeout for all internal connections and other processes (in seconds)', default: 720
7
+ module Nagios
8
+ module Promoo
9
+ # Main class for `nagios-promoo`.
10
+ #
11
+ # @author Boris Parak <parak@cesnet.cz>
12
+ class Master < ::Thor
13
+ class_option :debug, type: :boolean, desc: 'Turn on debugging mode', default: false
14
+ class_option :ca_path,
15
+ type: :string,
16
+ desc: 'Path to a directory with CA certificates',
17
+ default: '/etc/grid-security/certificates'
18
+ class_option :ca_file, type: :string, desc: 'Path to a file with CA certificates'
19
+ class_option :insecure,
20
+ type: :boolean,
21
+ desc: 'Turn on insecure mode (without SSL client validation)',
22
+ default: false
23
+ class_option :timeout,
24
+ type: :numeric,
25
+ desc: 'Timeout for all internal connections and other processes (in seconds)',
26
+ default: 720
13
27
 
14
- desc 'opennebula PROBE', 'Run the given probe for OpenNebula'
15
- subcommand 'opennebula', Nagios::Promoo::Opennebula::Master
28
+ desc 'opennebula PROBE', 'Run the given probe for OpenNebula'
29
+ subcommand 'opennebula', Nagios::Promoo::Opennebula::Master
16
30
 
17
- desc 'occi PROBE', 'Run the given probe for OCCI'
18
- subcommand 'occi', Nagios::Promoo::Occi::Master
31
+ desc 'occi PROBE', 'Run the given probe for OCCI'
32
+ subcommand 'occi', Nagios::Promoo::Occi::Master
19
33
 
20
- desc 'appdb PROBE', 'Run the given probe for AppDB'
21
- subcommand 'appdb', Nagios::Promoo::Appdb::Master
34
+ desc 'appdb PROBE', 'Run the given probe for AppDB'
35
+ subcommand 'appdb', Nagios::Promoo::Appdb::Master
22
36
 
23
- desc 'version', 'Print PROMOO version'
24
- def version
25
- puts Nagios::Promoo::VERSION
26
- end
37
+ desc 'version', 'Print PROMOO version'
38
+ def version
39
+ puts Nagios::Promoo::VERSION
40
+ end
27
41
 
28
- class << self
29
- # Force thor to exit with a non-zero return code on failure
30
- def exit_on_failure?; true; end
42
+ class << self
43
+ # Force thor to exit with a non-zero return code on failure
44
+ def exit_on_failure?
45
+ true
46
+ end
47
+ end
48
+ end
31
49
  end
32
50
  end
@@ -4,42 +4,73 @@ require 'occi-api'
4
4
  # Internal deps
5
5
  require File.join(File.dirname(__FILE__), 'version')
6
6
 
7
- # Define modules
8
- module Nagios::Promoo::Occi; end
9
- module Nagios::Promoo::Occi::Probes; end
7
+ module Nagios
8
+ module Promoo
9
+ # Namespace for OCCI-related code.
10
+ #
11
+ # @author Boris Parak <parak@cesnet.cz>
12
+ module Occi
13
+ # Namespace for OCCI-related probes.
14
+ #
15
+ # @author Boris Parak <parak@cesnet.cz>
16
+ module Probes; end
17
+ end
18
+ end
19
+ end
20
+
10
21
  Dir.glob(File.join(File.dirname(__FILE__), 'probes', '*.rb')) { |probe| require probe.chomp('.rb') }
11
22
 
12
- class Nagios::Promoo::Occi::Master < ::Thor
13
- class << self
14
- # Hack to override the help message produced by Thor.
15
- # https://github.com/wycats/thor/issues/261#issuecomment-16880836
16
- def banner(command, namespace = nil, subcommand = nil)
17
- "#{basename} occi #{command.usage}"
18
- end
23
+ module Nagios
24
+ module Promoo
25
+ module Occi
26
+ # Master class for all OCCI probes.
27
+ #
28
+ # @author Boris Parak <parak@cesnet.cz>
29
+ class Master < ::Thor
30
+ class << self
31
+ # Hack to override the help message produced by Thor.
32
+ # https://github.com/wycats/thor/issues/261#issuecomment-16880836
33
+ def banner(command, _namespace = nil, _subcommand = nil)
34
+ "#{basename} occi #{command.usage}"
35
+ end
19
36
 
20
- def available_probes
21
- Nagios::Promoo::Occi::Probes.constants.collect { |probe| Nagios::Promoo::Occi::Probes.const_get(probe) }.reject { |probe| !probe.runnable? }
22
- end
23
- end
37
+ def available_probes
38
+ probes = Nagios::Promoo::Occi::Probes.constants.collect do |probe|
39
+ Nagios::Promoo::Occi::Probes.const_get(probe)
40
+ end
41
+ probes.select(&:runnable?)
42
+ end
43
+ end
24
44
 
25
- class_option :endpoint, type: :string, desc: 'OCCI-enabled endpoint', default: 'http://localhost:3000/'
26
- class_option :auth, type: :string, desc: 'Authentication mechanism', enum: %w(x509-voms), default: 'x509-voms'
27
- class_option :token, type: :string, desc: 'Authentication token', default: "file:///tmp/x509up_u#{`id -u`.strip}"
45
+ class_option :endpoint, type: :string, desc: 'OCCI-enabled endpoint', default: 'http://localhost:3000/'
46
+ class_option :auth,
47
+ type: :string,
48
+ desc: 'Authentication mechanism',
49
+ enum: %w[x509-voms],
50
+ default: 'x509-voms'
51
+ class_option :token,
52
+ type: :string,
53
+ desc: 'Authentication token',
54
+ default: "file:///tmp/x509up_u#{`id -u`.strip}"
28
55
 
29
- available_probes.each do |probe|
30
- desc *probe.description
31
- probe.options.each do |opt|
32
- option opt.first, opt.last
33
- end
34
- class_eval %Q^
56
+ available_probes.each do |probe|
57
+ desc(*probe.description)
58
+ probe.options.each do |opt|
59
+ option opt.first, opt.last
60
+ end
61
+
62
+ class_eval %^
35
63
  def #{probe.declaration}(*args)
36
64
  #{probe}.new(options).run(args)
37
65
  end
38
66
  ^
39
- end
67
+ end
40
68
 
41
- desc 'version', 'Print version of the OCCI probe set'
42
- def version
43
- puts Nagios::Promoo::Occi::VERSION
69
+ desc 'version', 'Print version of the OCCI probe set'
70
+ def version
71
+ puts Nagios::Promoo::Occi::VERSION
72
+ end
73
+ end
74
+ end
44
75
  end
45
76
  end
@@ -1,29 +1,42 @@
1
- class Nagios::Promoo::Occi::Probes::BaseProbe
2
- class << self
3
- def runnable?; false; end
4
- end
1
+ module Nagios
2
+ module Promoo
3
+ module Occi
4
+ module Probes
5
+ # Base probe for all OCCI-related probes.
6
+ #
7
+ # @author Boris Parak <parak@cesnet.cz>
8
+ class BaseProbe
9
+ class << self
10
+ def runnable?
11
+ false
12
+ end
13
+ end
5
14
 
6
- attr_reader :options
15
+ attr_reader :options
7
16
 
8
- def initialize(options)
9
- @options = options
10
- end
17
+ def initialize(options)
18
+ @options = options
19
+ end
11
20
 
12
- def client
13
- @_client ||= Occi::Api::Client::ClientHttp.new({
14
- :endpoint => options[:endpoint],
15
- :auth => {
16
- :type => options[:auth].gsub('-voms', ''),
17
- :user_cert => options[:token].gsub('file://', ''),
18
- :user_cert_password => nil,
19
- :ca_path => options[:ca_path],
20
- :voms => options[:auth] == 'x509-voms' ? true : false
21
- },
22
- :log => {
23
- :level => options[:debug] ? Occi::Api::Log::DEBUG : Occi::Api::Log::ERROR,
24
- :logger => nil,
25
- :out => '/dev/null',
26
- }
27
- })
21
+ def client
22
+ @_client ||= ::Occi::Api::Client::ClientHttp.new(
23
+ endpoint: options[:endpoint],
24
+ auth: {
25
+ type: options[:auth].gsub('-voms', ''),
26
+ user_cert: options[:token].gsub('file://', ''),
27
+ user_cert_password: nil,
28
+ ca_path: options[:ca_path],
29
+ voms: options[:auth] == 'x509-voms' ? true : false
30
+ },
31
+ log: {
32
+ level: options[:debug] ? ::Occi::Api::Log::DEBUG : ::Occi::Api::Log::ERROR,
33
+ logger: nil,
34
+ out: '/dev/null'
35
+ }
36
+ )
37
+ end
38
+ end
39
+ end
40
+ end
28
41
  end
29
42
  end
@@ -3,58 +3,100 @@ require File.join(File.dirname(__FILE__), 'base_probe')
3
3
  require File.join(File.dirname(__FILE__), 'kinds_probe')
4
4
  require File.join(File.dirname(__FILE__), 'mixins_probe')
5
5
 
6
- class Nagios::Promoo::Occi::Probes::CategoriesProbe < Nagios::Promoo::Occi::Probes::BaseProbe
7
- class << self
8
- def description
9
- ['categories', 'Run a probe checking for mandatory OCCI category definitions']
10
- end
6
+ module Nagios
7
+ module Promoo
8
+ module Occi
9
+ module Probes
10
+ # Probe for checking OCCI categories declared by endpoints.
11
+ #
12
+ # @author Boris Parak <parak@cesnet.cz>
13
+ class CategoriesProbe < Nagios::Promoo::Occi::Probes::BaseProbe
14
+ class << self
15
+ def description
16
+ ['categories', 'Run a probe checking for mandatory OCCI category definitions']
17
+ end
11
18
 
12
- def options
13
- [
14
- [:optional, { type: :array, default: [], desc: 'Identifiers of optional categories (optional by force)' }],
15
- [:check_location, { type: :boolean, default: false, desc: 'Verify declared REST locations for INFRA resources' }],
16
- ]
17
- end
19
+ def options
20
+ [
21
+ [
22
+ :optional,
23
+ {
24
+ type: :array, default: [],
25
+ desc: 'Identifiers of optional categories (optional by force)'
26
+ }
27
+ ],
28
+ [
29
+ :check_location,
30
+ {
31
+ type: :boolean, default: false,
32
+ desc: 'Verify declared REST locations for INFRA resources'
33
+ }
34
+ ]
35
+ ]
36
+ end
18
37
 
19
- def declaration
20
- "categories"
21
- end
38
+ def declaration
39
+ 'categories'
40
+ end
22
41
 
23
- def runnable?; true; end
24
- end
42
+ def runnable?
43
+ true
44
+ end
45
+ end
25
46
 
26
- def run(args = [])
27
- categories = all_categories
28
- categories -= options[:optional] if options[:optional]
29
-
30
- Timeout::timeout(options[:timeout]) do
31
- categories.each do |cat|
32
- fail "#{cat.inspect} is missing" unless client.model.get_by_id(cat, true)
33
- next unless options[:check_location] && Nagios::Promoo::Occi::Probes::KindsProbe::INFRA_KINDS.include?(cat)
34
-
35
- # Make sure declared locations are actually available as REST
36
- # endpoints. Failure will raise an exception, no need to do
37
- # anything here. To keep requirements reasonable, only INFRA
38
- # kinds are considered relevant for this part of the check.
39
- begin
40
- client.list(cat)
41
- rescue => err
42
- fail "Failed to verify declared REST location for #{cat.inspect} (#{err.message})"
43
- end
44
- end
45
- end
47
+ def run(_args = [])
48
+ categories = all_categories
49
+ categories -= options[:optional] if options[:optional]
46
50
 
47
- puts 'CATEGORIES OK - All specified OCCI categories were found'
48
- rescue => ex
49
- puts "CATEGORIES CRITICAL - #{ex.message}"
50
- puts ex.backtrace if options[:debug]
51
- exit 2
52
- end
51
+ Timeout.timeout(options[:timeout]) do
52
+ categories.each do |cat|
53
+ raise "#{cat.inspect} is missing" unless client.model.get_by_id(cat, true)
54
+ next unless options[:check_location] && infra_kinds.include?(cat)
55
+
56
+ # Make sure declared locations are actually available as REST
57
+ # endpoints. Failure will raise an exception, no need to do
58
+ # anything here. To keep requirements reasonable, only INFRA
59
+ # kinds are considered relevant for this part of the check.
60
+ begin
61
+ client.list(cat)
62
+ rescue => ex
63
+ raise "Failed to verify declared REST location for #{cat.inspect} (#{ex.message})"
64
+ end
65
+ end
66
+ end
53
67
 
54
- private
68
+ puts 'CATEGORIES OK - All specified OCCI categories were found'
69
+ rescue => ex
70
+ puts "CATEGORIES CRITICAL - #{ex.message}"
71
+ puts ex.backtrace if options[:debug]
72
+ exit 2
73
+ end
55
74
 
56
- def all_categories
57
- Nagios::Promoo::Occi::Probes::KindsProbe::CORE_KINDS + Nagios::Promoo::Occi::Probes::KindsProbe::INFRA_KINDS \
58
- + Nagios::Promoo::Occi::Probes::MixinsProbe::INFRA_MIXINS + Nagios::Promoo::Occi::Probes::MixinsProbe::CONTEXT_MIXINS
75
+ private
76
+
77
+ def core_kinds
78
+ Nagios::Promoo::Occi::Probes::KindsProbe::CORE_KINDS
79
+ end
80
+
81
+ def infra_kinds
82
+ Nagios::Promoo::Occi::Probes::KindsProbe::INFRA_KINDS
83
+ end
84
+
85
+ def infra_mixins
86
+ Nagios::Promoo::Occi::Probes::MixinsProbe::INFRA_MIXINS
87
+ end
88
+
89
+ def context_mixins
90
+ Nagios::Promoo::Occi::Probes::MixinsProbe::CONTEXT_MIXINS
91
+ end
92
+
93
+ def all_categories
94
+ %i[core_kinds infra_kinds infra_mixins context_mixins].reduce([]) do |memo, elm|
95
+ memo.concat send(elm)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
59
101
  end
60
102
  end