nagios-promoo 1.1.0 → 1.2.0

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