op5util 0.1.1

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