op5util 0.1.1

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,36 @@
1
+ # rubocop:disable Lint/UnneededDisable
2
+ # rubocop:disable LineLength
3
+ # rubocop:disable AbcSize
4
+ # rubocop:disable SymbolArray
5
+ # rubocop:disable Metrics/MethodLength
6
+ # rubocop:disable HashSyntax
7
+
8
+ # Toplevel documentation
9
+ module Op5util
10
+ class NoAuthMethodError < StandardError; end
11
+ def check_authfile(file)
12
+ authline = File.open(File.expand_path(file)).readline.chomp
13
+ rescue StandardError
14
+ [nil, nil]
15
+ else
16
+ authinfo = authline.split(':')
17
+ authinfo.count == 2 ? authinfo : [nil, nil]
18
+ end
19
+
20
+ def check_auth(global_opts)
21
+ if !global_opts[:username].nil? && !global_opts[:password].nil?
22
+ true
23
+ elsif !ENV['OP5USER'].nil? && !ENV['OP5PASS'].nil?
24
+ global_opts[:username] = ENV['OP5USER']
25
+ global_opts[:password] = ENV['OP5PASS']
26
+ true
27
+ elsif File.exist?(File.expand_path(global_opts[:authfile]))
28
+ (global_opts[:username], global_opts[:password]) = check_authfile(global_opts[:authfile])
29
+ global_opts[:username].nil? ? false : true
30
+ else
31
+ raise NoAuthMethodError
32
+ end
33
+ end
34
+
35
+ module_function :check_auth, :check_authfile
36
+ end
@@ -0,0 +1,63 @@
1
+ # rubocop:disable LineLength, MethodLength, AbcSize
2
+ # Foo
3
+ module Op5util
4
+ class NoSuchHostgroupError; end
5
+ # Foo
6
+ class Monitor
7
+ def list_hostgroups(hostgroup, options)
8
+ response = self.class.get(@base_uri + 'config/hostgroup?format=json',
9
+ basic_auth: @auth, verify: false)
10
+ raise ApiError unless response.code == 200
11
+ if hostgroup.nil?
12
+ hostgroups = JSON.parse!(response.body).map { |h| h['name'] }
13
+ else
14
+ hostgroups = JSON.parse!(response.body).map { |h| h['name'] }.select { |hg| hg == hostgroup }
15
+ raise NoSuchHostgroupError if hostgroups.empty?
16
+ end
17
+ if options[:long]
18
+ list_hostgroups_with_services(hostgroups)
19
+ else
20
+ hostgroups.each { |h| puts h }
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def hostgroup_info(h)
27
+ response = self.class.get(@base_uri + "config/hostgroup/#{URI.escape(h)}?format=json",
28
+ basic_auth: @auth, verify: false)
29
+ raise ApiError unless response.code == 200
30
+ hostgroup = JSON.parse!(response.body)
31
+ hostgroup['members'].nil? ? '' : members = hostgroup['members'].join(',')
32
+ max = max_cell_width
33
+ if hostgroup['services'].nil?
34
+ services = ''
35
+ else
36
+ services = hostgroup['services'].map { |s| fold_string('"' + s['service_description'] + '"', max) }.join("\n")
37
+ end
38
+ [members, services]
39
+ end
40
+
41
+ def max_cell_width
42
+ # The magic number 15 is the size of tables cells padding + the heading 'State'
43
+ # and a an extra characters for safe layout on narrow terminals, down to
44
+ # 80 characters width tested.
45
+ require 'io/console'
46
+ (_terminal_height, terminal_width) = IO.console.winsize
47
+ ((terminal_width - 15) / 2).floor
48
+ end
49
+
50
+ def list_hostgroups_with_services(hostgroups)
51
+ max = max_cell_width
52
+ table = Terminal::Table.new do |t|
53
+ t.add_row ['Hostgroup'.blue, 'Member hosts'.blue, 'Service checks'.blue]
54
+ t.add_separator
55
+ hostgroups.each do |h|
56
+ (members, services) = hostgroup_info(h)
57
+ t.add_row [h, fold_string(members, max), services ]
58
+ end
59
+ end
60
+ puts table
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,13 @@
1
+ # rubocop:disable LineLength, MethodLength, AccessorMethodName
2
+ # Foo
3
+ module Op5util
4
+ # Foo
5
+ class Monitor
6
+ def method_template
7
+ response = self.class.get(@base_uri + 'some/path?format=json',
8
+ basic_auth: @auth, verify: false)
9
+ raise ApiError unless response.code == 200
10
+ puts 'Do something'
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,29 @@
1
+ # rubocop:disable Lint/UnneededDisable
2
+ # rubocop:disable LineLength
3
+ # rubocop:disable AbcSize
4
+ # rubocop:disable SymbolArray
5
+ # rubocop:disable Metrics/MethodLength
6
+ # rubocop:disable HashSyntax
7
+
8
+ # Top level documentation
9
+ module Op5util
10
+ require 'httparty'
11
+ class AuthenticationError < StandardError; end
12
+ class CommunicationError < StandardError; end
13
+ # Top level documentation
14
+ class Monitor
15
+ include HTTParty
16
+ #debug_output $stdout
17
+
18
+ def initialize(monitor, username, password)
19
+ @monitor = monitor
20
+ @auth = { username: username, password: password }
21
+ @base_uri = "https://#{@monitor}/api/"
22
+ url = 'status/status?format=json'
23
+ response = self.class.get(@base_uri + url, basic_auth: @auth, verify: false)
24
+ raise AuthenticationError if !response.nil? && !response.code.nil? && response.code == 401
25
+ raise CommunicationError if response.nil? || response.code.nil? || response.code != 200
26
+ self
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,72 @@
1
+ # rubocop:disable LineLength, MethodLength, AccessorMethodName
2
+ # Foo
3
+ module Op5util
4
+ # Foo
5
+ class Monitor
6
+ def schedule_checks(host, options)
7
+ schedule_host_check(host)
8
+ schedule_service_checks_for_host(host, options)
9
+ end
10
+
11
+ private
12
+
13
+ def build_host_check_body(host)
14
+ time = Time.now.to_i
15
+ { host_name: host,
16
+ check_time: time }.to_json
17
+ end
18
+
19
+ def schedule_host_check(host)
20
+ body = build_host_check_body(host)
21
+ response = self.class.post(@base_uri + 'command/SCHEDULE_HOST_CHECK?format=json',
22
+ headers: { 'Content-Type' => 'application/json' },
23
+ body: body, basic_auth: @auth, verify: false)
24
+ raise ApiError unless response.code == 200
25
+ puts 'Host check scheduled'
26
+ end
27
+
28
+ def hostgroup_services(hg)
29
+ response = self.class.get(@base_uri + "config/hostgroup/#{URI.escape(hg)}?format=json",
30
+ basic_auth: @auth, verify: false)
31
+ raise ApiError unless response.code == 200
32
+ hg_info = JSON.parse!(response.body)
33
+ hg_info['services'].nil? ? [] : hg_info['services'].map { |s| s['service_description'] }
34
+ end
35
+
36
+ # TODO: split hostgroup_info into better re-usable code
37
+ def service_description_for_host(host)
38
+ response = self.class.get(@base_uri + "config/host/#{host}?format=json",
39
+ basic_auth: @auth, verify: false)
40
+ raise ApiError unless response.code == 200
41
+ host_config = JSON.parse!(response.body)
42
+ if host_config['services'].nil?
43
+ services = []
44
+ else
45
+ services = host_config['services'].map { |s| s['service_description'] }
46
+ end
47
+ host_config['hostgroups'].each do |hg|
48
+ services += hostgroup_services(hg)
49
+ end
50
+ services.nil? ? [] : services
51
+ end
52
+
53
+ def build_service_check_body(host, service_description)
54
+ time = Time.now.to_i
55
+ { host_name: host,
56
+ check_time: time,
57
+ service_description: service_description }.to_json
58
+ end
59
+
60
+ def schedule_service_checks_for_host(host, options)
61
+ service_description_for_host(host).each do |s|
62
+ body = build_service_check_body(host, s)
63
+ response = self.class.post(@base_uri + 'command/SCHEDULE_SVC_CHECK?format=json',
64
+ headers: { 'Content-Type' => 'application/json' },
65
+ body: body, basic_auth: @auth, verify: false)
66
+ raise ApiError unless response.code == 200
67
+ puts "Service check for service \"#{s}\" scheduled" if options[:verbose]
68
+ end
69
+ puts "All services for host #{host} scheduled for check"
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,31 @@
1
+ # rubocop:disable Lint/UnneededDisable, LineLength, AbcSize, SymbolArray, MethodLength, HashSyntax
2
+ # Foo
3
+ module Op5util
4
+ # Foo
5
+ class Monitor
6
+ def schedule_downtime(host, options)
7
+ body = build_schedule_downtime_request_body(host, options)
8
+ response = self.class.post(@base_uri + 'command/SCHEDULE_HOST_DOWNTIME',
9
+ headers: { 'Content-Type' => 'application/json' },
10
+ body: body, basic_auth: @auth, verify: false)
11
+ raise ApiError unless response.code == 200
12
+ puts 'Downtime Scheduled'
13
+ end
14
+
15
+ private
16
+
17
+ def build_schedule_downtime_request_body(host, options)
18
+ start_time = Time.now.to_i + options[:wait].to_i
19
+ end_time = start_time + + options[:time].to_i * 3600 + 10
20
+ {
21
+ host_name: host.to_s,
22
+ start_time: start_time,
23
+ end_time: end_time,
24
+ fixed: 1,
25
+ trigger_id: 0,
26
+ duration: options[:time].to_i * 3600 + 10,
27
+ comment: options[:comment]
28
+ }.to_json
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,152 @@
1
+ # rubocop:disable LineLength, MethodLength, AbcSize, Lint/UselessAssignment
2
+ # Foo
3
+ module Op5util
4
+ # Foo
5
+ class Monitor
6
+ require 'colorize'
7
+ require 'terminfo'
8
+
9
+ def status_host(host, options)
10
+ full_status = JSON.parse!(get_host_status(host))
11
+ if options[:short] || (!options[:short] && !options[:long])
12
+ print_short_status(full_status)
13
+ else
14
+ print_full_status(full_status)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def print_short_status(full_status)
21
+ tot_s = full_status['num_services'].to_s
22
+ ok_s = full_status['num_services_hard_ok'].to_s
23
+ mark_ok = tot_s == ok_s
24
+ warn_s = full_status['num_services_hard_warn'].to_s
25
+ mark_warn = warn_s != '0'
26
+ crit_s = full_status['num_services_hard_crit'].to_s
27
+ mark_crit = crit_s != '0'
28
+ s = "The host #{full_status['name']} is in " + host_state_to_s(full_status['state'].to_i)
29
+ s += ' state, and it was last checked ' + pp_unixtime_ago(full_status['last_check']) + "ago.\n"
30
+ s += 'Out of ' + tot_s.blue.bold + ' services is '
31
+ s += pp_n_services(ok_s.to_i, tot_s.to_i) + ' in the '
32
+ s += mark_ok ? 'OK'.green : 'OK'.red.bold
33
+ s += + ' state, '
34
+ s += mark_warn ? warn_s.yellow.bold : warn_s
35
+ s += ' in the '
36
+ s += mark_warn ? 'WARN'.yellow.bold : 'WARN'
37
+ s += + ' state and '
38
+ s += mark_crit ? crit_s.red.bold : crit_s
39
+ s += ' in the '
40
+ s += mark_crit ? 'CRIT'.red.bold : 'CRIT' + ' state'
41
+ puts s
42
+ end
43
+
44
+ def service_state(service, full_status)
45
+ full_status['services_with_state'].each do |s|
46
+ return s[1] if s[0] == service
47
+ end
48
+ ''
49
+ end
50
+
51
+ def service_info(service, full_status)
52
+ full_status['services_with_info'].each do |s|
53
+ return s[3] if s[0] == service
54
+ end
55
+ ''
56
+ end
57
+
58
+ def print_full_status(full_status)
59
+ host_status = "The host #{full_status['name']} is "
60
+ host_status += host_state_to_s full_status['hard_state']
61
+ host_status += ', last check was done ' + pp_unixtime_ago(full_status['last_check'])
62
+ host_status += " seconds ago.\n"
63
+ puts host_status
64
+ max_field_length = max_cell_width
65
+ table = Terminal::Table.new do |t|
66
+ t.add_row ['Service'.blue, 'State'.blue, 'Info'.blue]
67
+ t.add_separator
68
+ full_status['services'].each do |service|
69
+ t.add_row [fold_string(service, max_field_length),
70
+ service_state_to_s(service_state(service, full_status)),
71
+ fold_string(service_info(service, full_status), max_field_length)]
72
+ end
73
+ end
74
+ puts table
75
+ end
76
+
77
+ def fold_string(s, width)
78
+ return nil if s.nil?
79
+ start_pos = 0
80
+ result = ''
81
+ while start_pos < s.length
82
+ cut_chars = [width, (s.length - start_pos)].min
83
+ cut_pos = start_pos + cut_chars - 1
84
+ result += s[start_pos..cut_pos]
85
+ start_pos += cut_chars
86
+ result += "\n" if start_pos < s.length
87
+ end
88
+ result
89
+ end
90
+
91
+ def pp_seconds(seconds)
92
+ retval = ''
93
+ s = seconds
94
+ if s > 86_400
95
+ retval = (s / 8600).to_s + ' days, '
96
+ s = s % 86_400
97
+ end
98
+ if s > 3600
99
+ retval += (s / 3660).to_s + ' hours, '
100
+ s = s % 3600
101
+ end
102
+ if s > 60
103
+ retval += (s / 60).to_s + ' minutes, '
104
+ s = s % 60
105
+ end
106
+ retval += (s % 60).to_s + ' seconds '
107
+ end
108
+
109
+ def pp_unixtime_ago(t)
110
+ pp_seconds(Time.now.to_i - t)
111
+ end
112
+
113
+ def pp_n_services(n, total = 0)
114
+ if n == total || ( total == 0 && n > 0 )
115
+ n.to_s.green
116
+ else
117
+ n.to_s.red.bold
118
+ end
119
+ end
120
+
121
+ def host_state_to_s(state)
122
+ case state
123
+ when 0
124
+ 'UP'.green
125
+ when 1
126
+ 'DOWN'.red.bold
127
+ else
128
+ 'UNKNOWN'.white.bold
129
+ end
130
+ end
131
+
132
+ def service_state_to_s(state)
133
+ case state
134
+ when 0
135
+ 'OK'.green
136
+ when 1
137
+ 'WARN'.yellow.bold
138
+ when 2
139
+ 'CRITICAL'.red.bold
140
+ else
141
+ 'UNKNOWN'.white.bold
142
+ end
143
+ end
144
+
145
+ def get_host_status(host)
146
+ response = self.class.get(@base_uri + "status/host/#{host}?format=json",
147
+ basic_auth: @auth, verify: false)
148
+ raise ApiError unless response.code == 200
149
+ response.body
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,88 @@
1
+ # rubocop:disable LineLength, MethodLength, AccessorMethodName
2
+ # Foo
3
+ module Op5util
4
+ require 'terminal-table'
5
+ # Foo
6
+ class Monitor
7
+ def status_summary(options)
8
+ if options[:long]
9
+ print_hosts_summary
10
+ else
11
+ print_summary
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def print_hosts_summary
18
+ response = self.class.get(@base_uri + 'status/host?format=json',
19
+ basic_auth: @auth, verify: false)
20
+ raise ApiError unless response.code == 200
21
+ hosts = JSON.parse!(response.body).map { |h| h['name'] }
22
+ table = Terminal::Table.new do |t|
23
+ t.add_row ['Host'.blue, 'Host status'.blue, 'Total Services'.blue, 'OK'.green, 'WARN'.yellow, 'CRIT'.red]
24
+ t.add_separator
25
+ hosts.each do |h|
26
+ t.add_row row_for_host(h)
27
+ end
28
+ end
29
+ puts table
30
+ end
31
+
32
+ def row_for_host(host)
33
+ full_status = JSON.parse!(get_host_status(host))
34
+ row = []
35
+ row << host
36
+ row << host_state_to_s(full_status['state'].to_i)
37
+ row << full_status['num_services'].to_s.green
38
+ row << full_status['num_services_hard_ok'].to_s.green
39
+ row << full_status['num_services_hard_warn'].to_s.yellow
40
+ row << full_status['num_services_hard_crit'].to_s.red
41
+ row
42
+ end
43
+
44
+ def print_summary
45
+ h = get_host_statuses
46
+ s = get_service_statuses
47
+ table = Terminal::Table.new do |t|
48
+ t.add_row ['', 'Total'.blue, 'OK'.green, 'Acknowledged problems'.yellow, 'Unhandled problems'.red]
49
+ t.add_separator
50
+ t.add_row ['Hosts'.blue, h[0].to_s, count_ok(h).to_s.green, h[1].to_s.yellow, h[2].to_s.red ]
51
+ t.add_row ['Services'.blue, s[0].to_s, count_ok(s).to_s.green, s[1].to_s.yellow, s[2].to_s.red ]
52
+ end
53
+ puts table
54
+ end
55
+
56
+ def get_service_statuses
57
+ service_urls = ['=%5Bservices%5D%20all',
58
+ '=%5Bservices%5D%20acknowledged%20%3D%201',
59
+ '=[services] state !%3D 0 and acknowledged %3D 0 and scheduled_downtime_depth %3D 0 and host.scheduled_downtime_depth %3D 0']
60
+ service_statuses = []
61
+ service_urls.each do |url|
62
+ response = self.class.get(@base_uri + 'filter/count?query' + url,
63
+ basic_auth: @auth, verify: false)
64
+ raise ApiError unless response.code == 200
65
+ service_statuses << JSON.parse!(response.body)['count'].to_i
66
+ end
67
+ service_statuses
68
+ end
69
+
70
+ def count_ok(a)
71
+ a[0] - a[1] - a[2]
72
+ end
73
+
74
+ def get_host_statuses
75
+ host_urls = ['=%5Bhosts%5D%20all',
76
+ '=%5Bhosts%5D%20acknowledged%20%3D%201',
77
+ '=%5Bhosts%5D%20state%20!%3D%200%20and%20acknowledged%20%3D%200%20and%20scheduled_downtime_depth%20%3D%200']
78
+ host_statuses = []
79
+ host_urls.each do |url|
80
+ response = self.class.get(@base_uri + 'filter/count?query' + url,
81
+ basic_auth: @auth, verify: false)
82
+ raise ApiError unless response.code == 200
83
+ host_statuses << JSON.parse!(response.body)['count'].to_i
84
+ end
85
+ host_statuses
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,3 @@
1
+ module Op5util
2
+ VERSION = '0.1.1'.freeze
3
+ end
data/lib/op5util.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'op5util/version.rb'
2
+ require 'op5util/check_auth.rb'
3
+ require 'op5util/monitor.rb'
4
+ require 'op5util/add_host.rb'
5
+ require 'op5util/add_hostgroups.rb'
6
+ require 'op5util/schedule_downtime.rb'
7
+ require 'op5util/status_host.rb'
8
+ require 'op5util/status_summary.rb'
9
+ require 'op5util/acknowledge_alarm.rb'
10
+ require 'op5util/list_hostgroups.rb'
11
+ require 'op5util/schedule_checks.rb'
data/op5util.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # rubocop:disable UselessAssignment, StringLiterals
2
+ require File.join([File.dirname(__FILE__), 'lib', 'op5util', 'version.rb'])
3
+ spec = Gem::Specification.new do |s|
4
+ s.name = 'op5util'
5
+ s.version = Op5util::VERSION
6
+ s.author = 'Niklas Paulsson'
7
+ s.email = 'niklasp@gmail.com'
8
+ s.platform = Gem::Platform::RUBY
9
+ s.licenses = ['MIT License']
10
+ s.homepage = 'https://github.com/np422/Op5util'
11
+ s.summary = 'A utility to do common Op5 administration from the commandline'
12
+ s.files = `git ls-files`.split("
13
+ ")
14
+ s.require_paths << 'lib'
15
+ s.has_rdoc = true
16
+ s.extra_rdoc_files = ['README.rdoc', 'op5util.rdoc']
17
+ s.rdoc_options << '--title' << 'op5util' << '--main' << 'README.rdoc'
18
+ s.bindir = 'bin'
19
+ s.executables << 'op5util'
20
+ s.add_development_dependency 'rake', '~> 0'
21
+ s.add_development_dependency 'rdoc', '~> 0'
22
+ s.add_development_dependency 'test-unit', '~> 0'
23
+ s.add_runtime_dependency('colorize', '0.8.1')
24
+ s.add_runtime_dependency('gli', '2.16.1')
25
+ s.add_runtime_dependency('httparty', '0.15.5')
26
+ s.add_runtime_dependency('terminal-table', '1.8.0')
27
+ end
data/op5util.rdoc ADDED
@@ -0,0 +1,5 @@
1
+ = op5util
2
+
3
+ Generate this with
4
+ op5util rdoc
5
+ After you have described your command line interface
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,14 @@
1
+ require 'test_helper'
2
+
3
+ class DefaultTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ end
7
+
8
+ def teardown
9
+ end
10
+
11
+ def test_the_truth
12
+ assert true
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ require 'test/unit'
2
+
3
+ # Add test libraries you want to use here, e.g. mocha
4
+
5
+ class Test::Unit::TestCase
6
+
7
+ # Add global extensions to the test case class here
8
+
9
+ end