zabcon 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/libs/printer.rb ADDED
@@ -0,0 +1,383 @@
1
+ #GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.html
2
+ #Zabbix CLI Tool and associated files
3
+ #Copyright (C) 2009,2010 Andrew Nelson nelsonab(at)red-tux(dot)net
4
+ #
5
+ #This program is free software; you can redistribute it and/or
6
+ #modify it under the terms of the GNU General Public License
7
+ #as published by the Free Software Foundation; either version 2
8
+ #of the License, or (at your option) any later version.
9
+ #
10
+ #This program is distributed in the hope that it will be useful,
11
+ #but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ #GNU General Public License for more details.
14
+ #
15
+ #You should have received a copy of the GNU General Public License
16
+ #along with this program; if not, write to the Free Software
17
+ #Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
+
19
+
20
+ ##########################################
21
+ # Subversion information
22
+ # $Id: printer.rb 258 2010-12-28 22:49:21Z nelsonab $
23
+ # $Revision: 258 $
24
+ ##########################################
25
+
26
+ require 'libs/zdebug'
27
+ require 'libs/zabcon_globals'
28
+
29
+ if RUBY_PLATFORM =~ /.*?mswin.*?/
30
+ require 'Win32API'
31
+
32
+ Kbhit = Win32API.new("crtdll", "_kbhit", 'V', 'L')
33
+ Getch = Win32API.new("crtdll", "_getch", 'V', 'L')
34
+
35
+ def getch
36
+ Getch.call
37
+ end
38
+
39
+ def kbhit
40
+ Kbhit.call
41
+ end
42
+ end
43
+
44
+ require 'highline/system_extensions'
45
+
46
+ class OutputPrinter
47
+
48
+ include ZDebug
49
+ include HighLine::SystemExtensions
50
+
51
+ NILCHAR="--"
52
+
53
+ # attr_accessor :sheight
54
+
55
+ # Class initializer
56
+ # Interactive mode will not be implemented for a while so the variable
57
+ # is more of a place holder for now. Interactive
58
+ def initialize(interactive=true)
59
+ @swidth=80 # screen width
60
+ @interactive=interactive # mode for output
61
+ @lines=0 # how many lines have been displayed thus far?
62
+
63
+ # Check the environment variables to see if screen height has been set
64
+ EnvVars.instance["sheight"]=25 if EnvVars.instance["sheight"].nil?
65
+ end
66
+
67
+ def hash_width(item)
68
+ w=0
69
+ item.each do |value, index|
70
+ w+= value.length + index.length + 6 # 6 is for " => " and ", "
71
+ end
72
+ w-=2 # subtract out last comma and space
73
+ return w
74
+ end
75
+
76
+ def array_width(item)
77
+ w=0
78
+ item.each do |value|
79
+ w+=value.length + 2 # 2 is for ", "
80
+ end
81
+ w-=2 # remove last comma and space
82
+ return w
83
+ end
84
+
85
+ def getitemwidth(item)
86
+ retval=0
87
+ return NILCHAR.length if item.nil?
88
+ case item.class.to_s
89
+ when "String"
90
+ retval=item.length
91
+ when "Fixnum"
92
+ retval=item.to_s.length
93
+ when "Float"
94
+ retval=item.to_s.length
95
+ when "Hash"
96
+ retval=hash_width(item)
97
+ when "Array"
98
+ retval=array_width(item)
99
+ else
100
+ p item
101
+ raise "getitemwidth - item.class: #{item.class} not supported"
102
+ end
103
+ retval
104
+ end
105
+
106
+ # determines the max col width for each colum
107
+ # may need to be optimized in the future
108
+ # possible optimization may include randomly iterating through list for large lists
109
+ # if dataset is an array headers is ignored, also an integer is returned not an array
110
+ # of widths
111
+ def getcolwidth(dataset,headers=nil)
112
+ if dataset.class==Array then
113
+ widths=headers.collect {|x| 0} # setup our resultant array of widths
114
+
115
+ # check the widths for the headers
116
+ headers.each_with_index { |value, index| widths[index] = value.length }
117
+
118
+ if (dataset.length>0) and (dataset[0].class!=Hash) then
119
+ width=0
120
+ dataset.each do |item|
121
+ w=getitemwidth(item)
122
+ widths[0] = widths[0]<w ? w : widths[0] # 0 because there's one column
123
+ end
124
+ return widths
125
+ elsif dataset[0].class==Hash then
126
+ raise "getcolwidth headers are nil" if headers.nil?
127
+ dataset.each do |row|
128
+ headers.each_with_index do |value, index|
129
+ width=getitemwidth(row[value])
130
+ val= widths[index] # storing value for efficiency, next statement might have two of this call
131
+ widths[index]= val < width ? width : val
132
+ end
133
+ end
134
+
135
+ return widths
136
+ else
137
+ raise "getcolwidth Unknown internal data type"
138
+ end
139
+ else
140
+ raise "getcolwidth - dataset type not supported: #{dataset.class}" # need to raise an error
141
+ end
142
+ end
143
+
144
+ def format_hash_for_print(item)
145
+ s = ""
146
+ item.each do |value, index|
147
+ s << ", " if !s.empty?
148
+ s << index << " => " << value
149
+ end
150
+ return s
151
+ end
152
+
153
+ def format_for_print(item)
154
+ if item.nil? || item==[]
155
+ return NILCHAR
156
+ else
157
+ case item.class.to_s
158
+ when "Hash"
159
+ return format_hash_for_print(item)
160
+ else
161
+ return item
162
+ end
163
+ end
164
+ end
165
+
166
+ # Pause output function
167
+ # This function will pause output after n lines have been printed
168
+ # n is defined by the lines parameter
169
+ # If interactive output has been disabled pause will not stop
170
+ # after n lines have been printed
171
+ # If @lines is set to -1 a side effect is created where pause is disabled.
172
+ def pause? (lines=1)
173
+ if @interactive and EnvVars.instance["sheight"]>0 and (@lines>-1) then
174
+ @lines += lines
175
+ if @lines>=(EnvVars.instance["sheight"]-1) then
176
+ pause_msg = "Pause, q to quit, a to stop pausing output"
177
+ Kernel.print pause_msg
178
+ if RUBY_PLATFORM =~ /.*?mswin.*?/
179
+ while kbhit==0
180
+ sleep 0.3
181
+ # putc('.')
182
+ end
183
+ chr=getch
184
+ puts chr
185
+ else
186
+ begin
187
+ chr=get_character
188
+ rescue Interrupt # trap ctrl-c and create side effect to behave like "q" was pressed
189
+ chr=113
190
+ end
191
+
192
+ # erase characters on the current line, and move the cursor left the size of pause_msg
193
+ Kernel.print "\033[2K\033[#{pause_msg.length}D"
194
+
195
+ if (chr==113) or (chr==81) then # 113="q" 81="Q"
196
+ raise "quit"
197
+ end
198
+ if (chr==65) or (chr==97) then # 65="A" 97="a
199
+ @lines=-1
200
+ end
201
+ end
202
+ @lines= (@lines==-1) ? -1:0 # if we set @lines to -1 make sure the side effect propagates
203
+ end
204
+ end
205
+ end
206
+
207
+ def printline(widths)
208
+ output="+"
209
+ widths.each { |width| output+="-"+("-"*width)+"-+" }
210
+ pause? 1
211
+ puts output
212
+ end
213
+
214
+ #Prints the table header
215
+ #header: Array of strings in the print order for the table header
216
+ #order: Array of strings denoting output order
217
+ #widths: (optional) Array of numbers denoting the width of each field
218
+ #separator: (optional) Separator character
219
+ def printheader(header, widths=nil, separator=",")
220
+ if widths.nil?
221
+ output=""
222
+ header.each do |value|
223
+ output+="#{value}#{separator}"
224
+ end
225
+ separator.length.times {output.chop!}
226
+ else
227
+ output="|"
228
+ header.each_with_index do |value, index|
229
+ output+=" %-#{widths[index]}s |" % value
230
+ end
231
+ end
232
+ puts output
233
+ pause? 1
234
+ end
235
+
236
+ #Requires 2 arguments and 2 optional arguments
237
+ #row: The Row of data
238
+ #order: An array of field names with the order in which they are to be printed
239
+ #Optional arguments
240
+ #widths: An array denoting the width of each field, if nul a table separated by separator will be printed
241
+ #separator: the separator character to be used
242
+ def printrow(row, order, widths=nil, separator=',')
243
+ if widths.nil?
244
+ output=""
245
+ order.each_with_index do |value, index|
246
+ output+="#{row[value]}#{separator}"
247
+ end
248
+ separator.length.times { output.chop! } #remove the last separator
249
+ puts output
250
+ else
251
+ output="|"
252
+ order.each_with_index do |value, index|
253
+ output+=" %-#{widths[index]}s |" % format_for_print(row[value])
254
+ end
255
+ puts output
256
+ end
257
+ pause? 1
258
+ end
259
+
260
+ def print_array(dataset,cols)
261
+ debug(6,dataset,"dataset",150)
262
+ debug(6,cols,"cols",50)
263
+ count=0
264
+ type=dataset[:class]
265
+ results=dataset[:result]
266
+
267
+ debug(6,type,"Array type")
268
+
269
+ puts "#{dataset[:class].to_s.capitalize} result set" if EnvVars.instance["echo"]
270
+
271
+ if results.length==0
272
+ puts "Result set empty"
273
+ elsif results[0].class==Hash then
274
+ debug(7,"Results type is Hash")
275
+ header=[]
276
+ if cols.nil? then
277
+ case type
278
+ when :user
279
+ header=["userid","alias"]
280
+ when :host
281
+ header=["hostid","host"]
282
+ when :item
283
+ header=["itemid","description","key_"]
284
+ when :hostgroup
285
+ header=["groupid","name"]
286
+ when :hostgroupid
287
+ header=["name", "groupid", "internal"]
288
+ when :raw
289
+ header=results[0].keys
290
+ when nil
291
+ header=results[0].keys
292
+ end
293
+ elsif cols.class==Array
294
+ header=cols
295
+ elsif cols=="all" then
296
+ puts "all cols"
297
+ results[0].each_key { |key| header<<key }
298
+ else
299
+ header=cols.split(',')
300
+ end
301
+
302
+ debug(6,header,"header")
303
+
304
+ widths=getcolwidth(results,header)
305
+
306
+ if EnvVars.instance["table_output"]
307
+ if EnvVars.instance["table_header"]
308
+ printline(widths)
309
+ printheader(header,widths)
310
+ end
311
+ printline(widths)
312
+ results.each { |row| printrow(row,header,widths) }
313
+ printline(widths)
314
+ puts "#{results.length} rows total"
315
+ else
316
+ printheader(header,nil,EnvVars.instance["table_separator"]) if EnvVars.instance["table_header"]
317
+ results.each { | row| printrow(row,header,nil,EnvVars.instance["table_separator"]) }
318
+ end
319
+
320
+
321
+ else
322
+ debug(7,"Results type is not Hash, assuming array")
323
+ widths = getcolwidth(results,["id"]) # always returns an array of widths
324
+
325
+ printline(widths) # hacking parameters to overload functions
326
+ printheader(["id"],widths)
327
+ printline(widths)
328
+
329
+ results.each { |item| printrow({"id"=>item},["id"],widths) }
330
+ printline(widths)
331
+ puts "#{results.length} rows total"
332
+ end
333
+ end
334
+
335
+ def print_hash(dataset,cols)
336
+ puts "Hash object printing not implemented, here is the raw result"
337
+ p dataset
338
+ end
339
+
340
+
341
+ def print(dataset,cols)
342
+ begin
343
+ debug(6,dataset,"Dataset",200)
344
+ debug(6,cols,"Cols",40)
345
+ @lines=0
346
+ if !cols #cols==nil
347
+ cols_to_show=nil
348
+ else
349
+ cols_to_show=cols.empty? ? nil : cols[:show]
350
+ end
351
+
352
+ puts dataset[:message] if dataset[:message]
353
+
354
+ # p dataset[:result].class
355
+ if dataset[:result].class==Array then
356
+ print_array(dataset,cols_to_show)
357
+ elsif dataset[:result].class==Hash then
358
+ print_hash(dataset,cols_to_show)
359
+ elsif dataset[:result].class!=NilClass then
360
+ puts "Unknown object received by the print routint"
361
+ puts "Class type: #{dataset[:result].class}"
362
+ puts "Data:"
363
+ p dataset[:result]
364
+ end
365
+ rescue TypeError
366
+ puts "***********************************************************"
367
+ puts "Whoops!"
368
+ puts "Looks like we got some data we didn't know how to print."
369
+ puts "This may be worth submitting as a bug. If you submit a bug"
370
+ puts "report be sure to include this output and the command you"
371
+ puts "executed to get this message. http://trac.red-tux.net"
372
+ puts "data received:"
373
+ p dataset
374
+ rescue RuntimeError => e
375
+ if e.message=="quit" then
376
+ puts "Output stopped"
377
+ else
378
+ raise e
379
+ end
380
+ end
381
+ end
382
+
383
+ end
@@ -0,0 +1,742 @@
1
+ #!/usr/bin/ruby
2
+
3
+ #GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.html
4
+ #Zabbix CLI Tool and associated files
5
+ #Copyright (C) 2009,2010 Andrew Nelson nelsonab(at)red-tux(dot)net
6
+ #
7
+ #This program is free software; you can redistribute it and/or
8
+ #modify it under the terms of the GNU General Public License
9
+ #as published by the Free Software Foundation; either version 2
10
+ #of the License, or (at your option) any later version.
11
+ #
12
+ #This program is distributed in the hope that it will be useful,
13
+ #but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ #GNU General Public License for more details.
16
+ #
17
+ #You should have received a copy of the GNU General Public License
18
+ #along with this program; if not, write to the Free Software
19
+ #Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
+
21
+ ##########################################
22
+ # Subversion information
23
+ # $Id: $
24
+ # $Revision: 258 $
25
+ ##########################################
26
+
27
+ require 'parseconfig'
28
+ require 'ostruct'
29
+ require 'rexml/document'
30
+ require 'libs/zbxcliserver'
31
+ require 'libs/printer'
32
+ require 'libs/zdebug'
33
+ require 'libs/input'
34
+ require 'libs/defines'
35
+ require 'libs/command_tree'
36
+ require 'libs/argument_processor'
37
+ require 'libs/command_help'
38
+ require 'libs/zabcon_globals'
39
+
40
+
41
+ class ZabconCore
42
+
43
+ include ZDebug
44
+
45
+ def initialize()
46
+ @env = EnvVars.instance # make it easier to call the global EnvVars singleton
47
+
48
+ # This must be set first or the debug module will throw an error
49
+ set_debug_level(@env["debug"])
50
+
51
+ @env.register_notifier("debug",self.method(:set_debug_level))
52
+ @env.register_notifier("api_debug",self.method(:set_debug_api_level))
53
+
54
+ @server = nil
55
+ @callbacks={}
56
+ @printer=OutputPrinter.new
57
+ @commands=nil
58
+ @setvars={}
59
+ debug(5,"Setting up help")
60
+ @cmd_help=CommandHelp.new("english") # Setup help functions, determine default language to use
61
+ debug(5,"Setting up ArgumentProcessor")
62
+ @arg_processor=ArgumentProcessor.new # Need to instantiate for debug routines
63
+
64
+ if !@env["server"].nil? and !@env["username"].nil? and !@env["password"].nil? then
65
+ puts "Found valid login credentials, attempting login" if @env["echo"]
66
+ begin
67
+ do_login({:server=>@env["server"], :username=>@env["username"],:password=>@env["password"]})
68
+ rescue ZbxAPI_ExceptionLoginPermission
69
+ puts "Error Invalid login or no API permissions."
70
+ end
71
+ end
72
+
73
+ debug(5,"Setting up prompt")
74
+ @debug_prompt=false
75
+ if @env["have_tty"]
76
+ prc=Proc.new do
77
+ debug_part = @debug_prompt ? " #{debug_level}" : ""
78
+ if @server.nil?
79
+ " #{debug_part}-> "
80
+ else
81
+ @server.login? ? " #{debug_part}+> " : " #{debug_part}-> "
82
+ end
83
+ end
84
+ @input=Readline_Input.new
85
+ @input.set_prompt_func(prc)
86
+ else
87
+ @input=STDIN_Input.new
88
+ end
89
+ debug(5,"Setup complete")
90
+ end
91
+
92
+ # Argument logged in is used to determine which set of commands to load. If loggedin is true then commands which
93
+ # require a valid login are loaded
94
+ def setupcommands(loggedin)
95
+ debug(5,loggedin,"Starting setupcommands (loggedin)")
96
+
97
+ @commands=Parser.new(@arg_processor.default)
98
+
99
+ no_cmd=nil
100
+ no_args=nil
101
+ no_help=nil
102
+ no_verify=nil
103
+
104
+ login_required = lambda {
105
+ debug(6,"Lambda 'login_required'")
106
+ puts "Login required"
107
+ }
108
+
109
+ # parameters for insert: insert_path, command, commandproc, arguments=[], helpproc=nil, verify_func=nil, options
110
+
111
+ # These commands do not require a valid login
112
+ @commands.insert "", "quit", :exit
113
+ @commands.insert "", "exit", :exit
114
+ @commands.insert "", "help", :help,no_args,no_help,@arg_processor.help, :suppress_printer
115
+
116
+ @commands.insert "", "hisotry", self.method(:do_history),no_args,no_help,no_verify,:suppress_printer
117
+ @commands.insert "", "info", self.method(:do_info),no_args,no_help,no_verify,:suppress_printer
118
+ @commands.insert "", "load", no_cmd,no_args,no_help,no_verify,:suppress_printer
119
+ @commands.insert "", "login", self.method(:do_login),nil,nil,@arg_processor.method(:login), :suppress_printer
120
+ @commands.insert "", "set", no_cmd
121
+ @commands.insert "", "show", no_cmd
122
+ @commands.insert "", "unset", no_cmd
123
+ @commands.insert "load", "config", @env.method(:load_config),no_args,no_help,no_verify,:suppress_printer
124
+ @commands.insert "set", "debug", self.method(:set_debug),no_args,no_help,no_verify,:suppress_printer
125
+ @commands.insert "set", "lines", self.method(:set_lines),no_args,no_help,no_verify,:suppress_printer
126
+ @commands.insert "set", "pause", self.method(:set_pause),no_args,no_help,no_verify,:suppress_printer
127
+ @commands.insert "set", "var", self.method(:set_var), no_args, no_help, @arg_processor.method(:simple_processor),:suppress_printer
128
+ @commands.insert "set", "env", self.method(:set_env), no_args, no_help, @arg_processor.method(:simple_processor), :suppress_printer
129
+ @commands.insert "show", "var", self.method(:show_var), no_args, no_help, @arg_processor.method(:array_processor), :suppress_printer
130
+ @commands.insert "show", "env", self.method(:show_env), no_args, no_help, @arg_processor.method(:array_processor), :suppress_printer
131
+ @commands.insert "unset", "var", self.method(:unset_var), no_args, no_help, @arg_processor.method(:array_processor), :suppress_printer
132
+
133
+ if loggedin then
134
+ debug(5,"Inserting commands which require login")
135
+ # This command tree is for a valid login
136
+ @commands.insert "", "raw", no_cmd
137
+ @commands.insert "", "add", no_cmd
138
+ @commands.insert "", "delete", no_cmd
139
+ @commands.insert "", "get", no_cmd, no_args, @cmd_help.method(:get)
140
+ @commands.insert "", "import", self.method(:do_import),no_args,no_help,no_verify
141
+ @commands.insert "", "update", no_cmd
142
+
143
+ @commands.insert "add", "app", @server.method(:addapp),no_args,no_help,no_verify
144
+ @commands.insert "add app", "id", @server.method(:getappid),no_args,no_help,no_verify
145
+ @commands.insert "add", "host", @server.method(:addhost), no_args, @cmd_help.method(:add_host), @arg_processor.method(:add_host), :server => @server
146
+ @commands.insert "add host", "group", @server.method(:addhostgroup),no_args,no_help,no_verify
147
+ @commands.insert "add", "item", @server.method(:additem), no_args, @cmd_help.method(:add_item), @arg_processor.method(:add_item)
148
+ @commands.insert "add", "link", @server.method(:addlink),no_args,no_help,no_verify
149
+ @commands.insert "add link", "trigger", @server.method(:addlinktrigger),no_args,no_help,no_verify
150
+ @commands.insert "add", "sysmap", @server.method(:addsysmap),no_args,no_help,no_verify
151
+ @commands.insert "add sysmap", "element", @server.method(:addelementtosysmap),no_args,no_help,no_verify
152
+ @commands.insert "add", "trigger", @server.method(:addtrigger),no_args,no_help,no_verify
153
+ @commands.insert "add", "user", @server.method(:adduser), no_args, @cmd_help.method(:add_user), @arg_processor.method(:add_user)
154
+ @commands.insert "add user", "media", @server.method(:addusermedia),no_args,@cmd_help.method(:add_user_media),no_verify
155
+
156
+ @commands.insert "get", "app", @server.method(:getapp), no_args, no_help, @arg_processor.default_get
157
+ @commands.insert "get", "host", @server.method(:gethost), no_args, no_help, @arg_processor.default_get
158
+ @commands.insert "get host", "group", @server.method(:gethostgroup), no_args, no_help, @arg_processor.default_get
159
+ @commands.insert "get host group", "id", @server.method(:gethostgroupid), no_args, no_help, @arg_processor.method(:get_group_id)
160
+ @commands.insert "get", "item", @server.method(:getitem),
161
+ ['itemids','hostids','groupids', 'triggerids','applicationids','status','templated_items','editable','count','pattern','limit','order', 'show'],
162
+ @cmd_help.method(:get_item), @arg_processor.default_get
163
+ @commands.insert "get", "seid", @server.method(:getseid), no_args, no_help, @arg_processor.default_get
164
+ @commands.insert "get", "trigger", @server.method(:gettrigger), no_args, no_help, @arg_processor.default_get
165
+ @commands.insert "get", "user", @server.method(:getuser),['show'], @cmd_help.method(:get_user), @arg_processor.method(:get_user)
166
+
167
+ @commands.insert "delete", "user", @server.method(:deleteuser), ['id'], @cmd_help.method(:delete_user), @arg_processor.method(:delete_user)
168
+ @commands.insert "delete", "host", @server.method(:deletehost), no_args, @cmd_help.method(:delete_host), @arg_processor.method(:delete_host)
169
+ @commands.insert "delete", "item", @server.method(:deleteitem), ['itemid'], @cmd_help.method(:delete_item), @arg_processor.default
170
+
171
+ @commands.insert "raw", "api", @server.method(:raw_api), no_args, @cmd_help.method(:raw_api), @arg_processor.method(:raw_api)
172
+ @commands.insert "raw", "json", @server.method(:raw_json), no_args, @cmd_help.method(:raw_json), @arg_processor.method(:raw_processor)
173
+
174
+ @commands.insert "update", "user", @server.method(:updateuser), no_args, no_help, no_verify
175
+ else
176
+ debug(5,"Inserting commands which do not require login")
177
+ # This command tree is for no login
178
+ @commands.insert "", "add", no_cmd
179
+ @commands.insert "", "delete", no_cmd
180
+ @commands.insert "", "get", no_cmd
181
+ @commands.insert "", "import", no_cmd,no_args,no_help
182
+ @commands.insert "", "update", no_cmd
183
+
184
+ @commands.insert "add", "app", login_required,no_args,no_help
185
+ @commands.insert "add app", "id", login_required,no_args,no_help
186
+ @commands.insert "add", "host", login_required, no_args, @cmd_help.method(:add_host)
187
+ @commands.insert "add host", "group", login_required,no_args,no_help
188
+ @commands.insert "add", "item", login_required, no_args, no_help
189
+ @commands.insert "add", "link", login_required,no_args,no_help
190
+ @commands.insert "add link", "trigger", login_required,no_args,no_help
191
+ @commands.insert "add", "sysmap", login_required,no_args,no_help
192
+ @commands.insert "add sysmap", "element", login_required,no_args,no_help
193
+ @commands.insert "add", "trigger", login_required,no_args,no_help
194
+ @commands.insert "add", "user", login_required, no_args, @cmd_help.method(:add_user)
195
+ @commands.insert "add user", "media", login_required,no_args,no_help
196
+
197
+ @commands.insert "get", "app", login_required, no_args, no_help
198
+ @commands.insert "get", "host", login_required, no_args, no_help
199
+ @commands.insert "get host", "group", login_required, no_args, no_help
200
+ @commands.insert "get host group", "id", login_required, no_args, no_help
201
+ @commands.insert "get", "item", login_required, no_args, no_help
202
+ @commands.insert "get", "seid", login_required, no_args, no_help
203
+ @commands.insert "get", "trigger", login_required, no_args, no_help
204
+ @commands.insert "get", "user", login_required,no_args, @cmd_help.method(:get_user)
205
+
206
+ @commands.insert "delete", "user", login_required, no_args, @cmd_help.method(:delete_user)
207
+ @commands.insert "delete", "host", login_required, no_args, @cmd_help.method(:delete_host)
208
+
209
+ @commands.insert "update", "user", login_required, no_args, no_help
210
+ end
211
+ end
212
+
213
+ def start
214
+ debug(5,"Entering main zabcon start routine")
215
+ puts "Welcome to Zabcon." if @env["echo"]
216
+ puts "Use the command 'help' to get help on commands" if @env["have_tty"] || @env["echo"]
217
+
218
+ setupcommands(!@server.nil?) # If we don't have a valid server we're not logged in'
219
+ begin
220
+ while line=@input.get_line()
221
+ line=@arg_processor.strip_comments(line) # use the argument processor's comment stripper'
222
+ next if line.nil?
223
+ next if line.strip.length==0 # don't bother parsing an empty line'
224
+ debug(6, line, "Input from user")
225
+
226
+ # this statement calls the command tree parser and sets up rhash
227
+ # for later use and function calls
228
+ rhash=@commands.parse(line, @setvars)
229
+
230
+ debug(6, rhash, "Results from parse")
231
+
232
+ next if rhash.nil?
233
+ case rhash[:proc]
234
+ when :exit
235
+ break
236
+ when :help
237
+ @cmd_help.help(@commands,line)
238
+ else
239
+ if !rhash[:proc].nil?
240
+ debug(4,rhash,"Calling function",250)
241
+ results=rhash[:proc].call(rhash[:api_params])
242
+ printing = rhash[:options].nil? ? true : rhash[:options][:suppress_printer].nil? ? true : false
243
+ @printer.print(results,rhash[:show_params]) if !results.nil? if printing
244
+ end
245
+ end # case
246
+
247
+ end # while
248
+ rescue ParseError => e #catch the base exception class
249
+ e.show_message
250
+ retry if e.retry?
251
+ rescue ZbxAPI_ExceptionVersion => e
252
+ puts e
253
+ retry # We will allow for graceful recover from Version exceptions
254
+ rescue ZbxAPI_ExceptionLoginPermission
255
+ puts "No login permissions"
256
+ retry
257
+ rescue ZbxAPI_ExceptionPermissionError
258
+ puts "You do not have permission to perform that operation"
259
+ retry
260
+ rescue ZbxAPI_GeneralError => e
261
+ puts "An error was received from the Zabbix server"
262
+ if e.message.class==Hash
263
+ puts "Error code: #{e.message["code"]}"
264
+ puts "Error message: #{e.message["message"]}"
265
+ puts "Error data: #{e.message["data"]}"
266
+ retry
267
+ else
268
+ e.show_message
269
+ # ddputs "Error: #{e.message}"
270
+ retry if e.retry?
271
+ end
272
+ rescue ZError => e
273
+ puts
274
+ if e.retry?
275
+ puts "A non-fatal error occurred."
276
+ else
277
+ puts "A fatal error occurred."
278
+ end
279
+ e.show_message
280
+ e.show_backtrace
281
+ retry if e.retry?
282
+ end #end of exception block
283
+ end # def
284
+
285
+ def getprompt
286
+ debug_part = @debug_prompt ? " #{debug_level}" : ""
287
+ if @server.nil?
288
+ return " #{debug_part}-> "
289
+ end
290
+ return @server.login? ? " #{debug_part}+> " : " #{debug_part}-> "
291
+ end
292
+
293
+ def do_history(input)
294
+ history = @input.history.to_a
295
+ history.each_index do |index|
296
+ puts "#{index}: #{history[index]}"
297
+ end
298
+ end
299
+
300
+ # set_debug is for the callback to set the debug level
301
+ # todo
302
+ # This command is now deprecated for "set env debug=n"
303
+ def set_debug(input)
304
+ if input["prompt"].nil? then
305
+ puts "This command is deprecated, please use \"set env debug=n\""
306
+ @env["debug"]=input.keys[0].to_i
307
+ else
308
+ @debug_prompt=!@debug_prompt
309
+ end
310
+ end
311
+
312
+ def set_debug_api_level(value)
313
+ puts "inside set_debug_api_level"
314
+ set_facility_debug_level(:api,value)
315
+ end
316
+
317
+ def set_lines(input)
318
+ @printer.sheight=input.keys[0].to_i
319
+ end
320
+
321
+ def set_pause(input)
322
+ if input.nil? then
323
+ puts "set pause requires either Off or On"
324
+ return
325
+ end
326
+
327
+ if input.keys[0].upcase=="OFF"
328
+ @printer.sheight=@printer.sheight.abs*(-1)
329
+ elsif input.keys[0].upcase=="ON"
330
+ @printer.sheight=@printer.sheight.abs
331
+ else
332
+ puts "set pause requires either Off or On"
333
+ end
334
+ @printer.sheight = 24 if @printer.sheight==0
335
+ end
336
+
337
+ def set_var(input)
338
+ debug(6,input)
339
+ input.each {|key,val|
340
+ GlobalVars.instance[key]=val
341
+ puts "#{key} : #{val.inspect}"
342
+ }
343
+ end
344
+
345
+ def show_var(input)
346
+ if input.empty?
347
+ if GlobalVars.instance.empty?
348
+ puts "No variables defined"
349
+ else
350
+ GlobalVars.instance.each { |key,val|
351
+ puts "#{key} : #{val.inspect}"
352
+ }
353
+ end
354
+ else
355
+ input.each { |item|
356
+ if GlobalVars.instance[item].nil?
357
+ puts "#{item} *** Not Defined ***"
358
+ else
359
+ puts "#{item} : #{GlobalVars.instance[item].inspect}"
360
+ end
361
+ }
362
+ end
363
+ end
364
+
365
+ def unset_var(input)
366
+ if input.empty?
367
+ puts "No variables given to unset"
368
+ else
369
+ input.each {|item|
370
+ if GlobalVars.instance[item].nil?
371
+ puts "#{item} *** Not Defined ***"
372
+ else
373
+ GlobalVars.instance.delete(item)
374
+ puts "#{item} Deleted"
375
+ end
376
+ }
377
+ end
378
+ end
379
+
380
+ def set_env(input)
381
+ input.each{|key,val|
382
+ @env[key]=val
383
+ puts "#{key} : #{val.inspect}"
384
+ }
385
+ end
386
+
387
+ def show_env(input)
388
+ if input.empty?
389
+ if @env.empty?
390
+ puts "No variables defined"
391
+ else
392
+ @env.each { |key,val|
393
+ puts "#{key} : #{val.inspect}"
394
+ }
395
+ end
396
+ else
397
+ input.each { |item|
398
+ if @env[item].nil?
399
+ puts "#{item} *** Not Defined ***"
400
+ else
401
+ puts "#{item} : #{@env[item].inspect}"
402
+ end
403
+ }
404
+ end
405
+ end
406
+
407
+ # Load a configuration stored in a file
408
+ # many things can be passed in
409
+ # 1) an OpenStruct list of command line parameters which will overload the parameters
410
+ # stored in the file
411
+ # 2) A Hash with a key :filename
412
+ # 3) If nil or empty the class variable @conffile will be used
413
+
414
+ def do_login(params)
415
+ url = params[:server]
416
+ username = params[:username]
417
+ password = params[:password]
418
+
419
+ begin
420
+ @server = ZbxCliServer.new(url,username,password,debug_level)
421
+ puts "#{url} connected" if @env["echo"]
422
+ puts "API Version: #{@server.version}" if @env["echo"]
423
+
424
+ setupcommands(true)
425
+ return true
426
+ rescue ZbxAPI_ExceptionBadAuth
427
+ puts "Login error, incorrect login information"
428
+ puts "Server: #{url} User: #{username} password: #{password}" # will need to remove password in later versions
429
+ return false
430
+ rescue ZbxAPI_ExceptionBadServerUrl
431
+ puts "Login error, unable to connect to host or bad host name: '#{url}'"
432
+ # rescue ZbxAPI_ExceptionConnectionRefused
433
+ # puts "Server refused connection, is url correct?"
434
+ end
435
+ end
436
+
437
+ def do_info(input)
438
+ puts "Current settings"
439
+ puts "Server"
440
+ if @server.nil?
441
+ puts "Not connected"
442
+ else
443
+ puts " Server Name: %s" % @server.server_url
444
+ puts " Username: %-15s Password: %-12s" % [@server.user, Array.new(@server.password.length,'*')]
445
+ end
446
+ puts "Display"
447
+ puts " Current screen length #{@env["sheight"]}"
448
+ puts "Other"
449
+ puts " Debug level %d" % @env["debug"]
450
+ end
451
+
452
+ #
453
+ # Import config from an XML file:
454
+ #
455
+ def do_import(input)
456
+ if input.nil?
457
+ puts "Run requires a file name as argument."
458
+ return
459
+ end
460
+
461
+ begin
462
+ xml_import = REXML::Document.new File.new(input[:filename])
463
+ rescue Errno::ENOENT
464
+ puts "Failed to open import file #{input[:filename]}."
465
+ return
466
+ end
467
+
468
+ if xml_import.nil?
469
+ puts "Failed to parse import file #{input[:filename]}."
470
+ return
471
+ end
472
+
473
+ host=xml_import.elements['import/hosts']
474
+ if !host.nil?
475
+ host = host[1]
476
+ end
477
+
478
+ # Loop for the host tags:
479
+ while !host.nil?
480
+ host_params = { 'host' => host.attributes["name"],
481
+ 'port' => host.elements["port"].text }
482
+ if host.elements["useip"].text.to_i == 0 # This is broken in Zabbix export (always 0).
483
+ host_params['dns']=host.elements["dns"].text
484
+ else
485
+ host_params['ip']=host.elements["ip"].text
486
+ end
487
+ # Loop through the groups:
488
+ group = host.elements['groups/']
489
+ if !group.nil?
490
+ group = group[1]
491
+ end
492
+ groupids = Array.new
493
+ while !group.nil?
494
+ result = @server.gethostgroupid({ 'name' => group.text })
495
+ groupid = result[:result].to_i
496
+ if groupid == 0
497
+ puts "The host group " + group.text + " doesn't exist. Attempting to add it."
498
+ result = @server.addhostgroup(['name' => group.text])
499
+ groupid = result[:result].to_a[0][1].to_i
500
+ if groupid == 0
501
+ puts "The group \"" + group.text + "\" doesn't exist and couldn't be added. Terminating import."
502
+ return
503
+ end
504
+ end
505
+ groupids << groupid
506
+ group = group.next_element
507
+ end
508
+ host_params['groupids'] = groupids;
509
+
510
+ # Add the host
511
+ result = @server.addhost(host_params)[:result]
512
+ hostid = @server.gethost( { 'pattern' => host.attributes['name'] } )[:result].to_a[0][1]
513
+ if result.nil? # Todo: result is nil when the host is added. I'm not sure if I buggered it up or not.
514
+ puts "Added host " + host.attributes['name'] + ": " + hostid.to_s
515
+ else
516
+ puts "Failed to add host " + host.attributes['name']
517
+ end
518
+
519
+ # Item loop (within host loop)
520
+ item = host.elements['items/']
521
+ if !item.nil?
522
+ item = item[1]
523
+ item_params = Array.new
524
+ appids = Array.new
525
+ while !item.nil?
526
+ # Application loop:
527
+ app = item.elements['applications/']
528
+ if !app.nil?
529
+ app = app[1]
530
+ if hostid != 0
531
+ while !app.nil?
532
+ appid = @server.getappid({'name' => app.text, 'hostid' => hostid})[:result]
533
+ if appid == 0
534
+ result = @server.addapp([{'name' => app.text, 'hostid' => hostid}])
535
+ appid = result[:result].to_a[0][1].to_i
536
+ puts "Application " + app.text + " added: " + appid.to_s
537
+ end
538
+ appids << appid
539
+ app = app.next_element
540
+ end
541
+ else
542
+ puts "There is no hostname associated with the application " + app.text
543
+ puts "An application must be associated with a host. It has not been added."
544
+ end
545
+ end
546
+
547
+ item_params = { 'description' => item.elements["description"].text,
548
+ 'key_' => item.attributes["key"],
549
+ 'hostid' => hostid,
550
+ 'delay' => item.elements['delay'].text.to_s.to_i,
551
+ 'history' => item.elements['history'].text.to_s.to_i,
552
+ 'status' => item.elements['status'].text.to_s.to_i,
553
+ 'type' => item.attributes['type'].to_i,
554
+ 'snmp_community' => item.elements['snmp_community'].text.to_s,
555
+ 'snmp_oid' => item.elements['snmp_oid'].text.to_s,
556
+ 'value_type' => item.attributes['value_type'].to_i,
557
+ 'data_type' => item.elements['data_type'].text.to_s.to_i,
558
+ 'trapper_hosts' => 'localhost',
559
+ 'snmp_port' => item.elements['snmp_port'].text.to_s.to_i,
560
+ 'units' => item.elements['units'].text.to_s,
561
+ 'multiplier' => item.elements['multiplier'].text.to_s.to_i,
562
+ 'delta' => item.elements['delta'].text.to_s.to_i,
563
+ 'snmpv3_securityname' => item.elements['snmpv3_securityname'].text.to_s,
564
+ 'snmpv3_securitylevel' => item.elements['snmpv3_securitylevel'].text.to_s.to_i,
565
+ 'snmpv3_authpassphrase' => item.elements['snmpv3_authpassphrase'].text.to_s,
566
+ 'snmpv3_privpassphrase' => item.elements['snmpv3_privpassphrase'].text.to_s,
567
+ 'formula' => item.elements['formula'].text.to_s.to_i,
568
+ 'trends' => item.elements['trends'].text.to_s.to_i,
569
+ 'logtimefmt' => item.elements['logtimefmt'].text.to_s,
570
+ 'valuemapid' => 0,
571
+ 'delay_flex' => item.elements['delay_flex'].text.to_s,
572
+ 'params' => item.elements['params'].text.to_s,
573
+ 'ipmi_sensor' => item.elements['ipmi_sensor'].text.to_s.to_i,
574
+ 'applications' => appids,
575
+ 'templateid' => 0 }
576
+ added_item = @server.additem([item_params])
577
+ puts "Added item " + item.elements["description"].text + ": " + added_item[0]
578
+ item = item.next_element
579
+ end # End of item loop (within host loop)
580
+ end
581
+
582
+ host = host.next_element
583
+ end # End of loop for host tags
584
+
585
+ # Trigger loop
586
+ trigger=xml_import.elements['import/triggers']
587
+ if !trigger.nil?
588
+ trigger = trigger[1]
589
+ end
590
+ while !trigger.nil?
591
+ trigger_params = { 'description' => trigger.elements['description'].text,
592
+ 'type' => trigger.elements['type'].text.to_i,
593
+ 'expression' => trigger.elements['expression'].text,
594
+ 'url' => '', # trigger.elements['url'].text,
595
+ 'status' => trigger.elements['status'].text.to_i,
596
+ 'priority' => trigger.elements['priority'].text.to_i,
597
+ 'comments' => 'No comments.' } # trigger.elements['comments'].text }
598
+ result = @server.addtrigger( trigger_params )
599
+ puts "Added trigger " + result[:result][0]['triggerid'] + ": " + trigger.elements['description'].text
600
+ trigger = trigger.next_element
601
+ end
602
+
603
+ # Sysmap loop
604
+ sysmap = xml_import.elements['import/sysmaps/']
605
+ if !sysmap.nil?
606
+ sysmap = sysmap[1]
607
+ end
608
+ while !sysmap.nil?
609
+ sysmap_params = { 'name' => sysmap.attributes['name'],
610
+ 'width' => sysmap.elements['width'].text.to_i,
611
+ 'height' => sysmap.elements['height'].text.to_i,
612
+ 'backgroundid' => sysmap.elements['backgroundid'].text.to_i,
613
+ 'label_type' => sysmap.elements['label_type'].text.to_i,
614
+ 'label_location' => sysmap.elements['label_location'].text.to_i }
615
+ sysmapid = 0
616
+ result = @server.addsysmap([sysmap_params])
617
+ # Get sysmapid from the result code
618
+ sysmapid = result[:result][0]['sysmapid'].to_i
619
+ puts "Added sysmap " + sysmap.attributes['name'] + ": " + sysmapid.to_s
620
+
621
+ if sysmapid != 0 # We must have a sysmap ID to add elements
622
+
623
+ # Element loop (within the sysmap loop)
624
+ element = sysmap.elements['/import/sysmaps/sysmap/elements/']
625
+ if !element.nil?
626
+ element = element[1]
627
+ end
628
+ while !element.nil?
629
+ # Todo: change to use case.
630
+ elementtype = element.elements['elementtype'].text.to_i
631
+ if elementtype != ME_IMAGE
632
+ hostid = @server.gethost( { 'pattern' => element.elements['hostname'].text } )[:result].to_a[0][1].to_i
633
+ end
634
+ if elementtype == ME_HOST
635
+ elementid = hostid
636
+ elsif elementtype == ME_TRIGGER
637
+ elementid = @server.gettrigger({'hostids' => hostid, 'pattern' => element.elements['tdesc'].text})
638
+ elementid = elementid[:result].to_a[0][1].to_i
639
+ else # ME_IMAGE for now.
640
+ elementid = 0
641
+ end
642
+ element_params = { 'label' => element.attributes['label'],
643
+ 'sysmapid' => sysmapid,
644
+ 'elementid' => elementid,
645
+ 'elementtype' => element.elements['elementtype'].text.to_i,
646
+ 'iconid_off' => element.elements['iconid_off'].text.to_i,
647
+ 'iconid_on' => element.elements['iconid_on'].text.to_i,
648
+ 'iconid_unknown' => element.elements['iconid_unknown'].text.to_i,
649
+ 'iconid_disabled' => element.elements['iconid_disabled'].text.to_i,
650
+ 'label_location' => element.elements['label_location'].text.to_i,
651
+ 'x' => element.elements['x'].text.to_i,
652
+ 'y' => element.elements['y'].text.to_i }
653
+ # 'url' => element.elements['url'].text }
654
+ result = @server.addelementtosysmap([element_params])
655
+ puts "Added map element " + element.attributes['label'] + ": " + result[:result]
656
+ element = element.next_element
657
+ end # End of element loop (within the sysmap loop)
658
+
659
+ # Sysmap link loop (within the sysmap loop)
660
+ syslink = sysmap.elements['/import/sysmaps/sysmap/sysmaplinks/']
661
+ if !syslink.nil?
662
+ syslink = syslink[1]
663
+ end
664
+ while !syslink.nil?
665
+ # The code down to "link_params = {" is a mess and needs to be rewritten.
666
+ # elementid = hostid or triggerid depending on element type.
667
+ if syslink.elements['type1'].text.to_i == ME_HOST
668
+ hostid1 = @server.gethost( { 'pattern' => syslink.elements['host1'].text } )[:result].to_a[0][1]
669
+ selementid1 = @server.getseid({'elementid' => hostid1, 'sysmapid' => sysmapid})[:result].to_a[0][1].to_i
670
+ elsif syslink.elements['type1'].text.to_i == ME_TRIGGER # The first element is a trigger
671
+ hostid1 = @server.gethost( { 'pattern' => syslink.elements['host1'].text } )[:result].to_a[0][1]
672
+ triggerid1 = @server.gettrigger({'hostids' => hostid1, 'pattern' => syslink.elements['tdesc1'].text})
673
+ hostid1 = triggerid1[:result].to_a[0][1].to_i
674
+ selementid1 = @server.getseid({'elementid' => hostid1, 'sysmapid' => sysmapid})[:result].to_a[0][1].to_i
675
+ elsif syslink.elements['type1'].text.to_i == ME_IMAGE
676
+ label = syslink.elements['label1'].text
677
+ selementid1 = @server.getseid({'label' => label, 'sysmapid' => sysmapid})[:result].to_a[0][1].to_i
678
+ end
679
+ # The other end of the link:
680
+ if syslink.elements['type2'].text.to_i == ME_HOST
681
+ hostid2 = @server.gethost( { 'pattern' => syslink.elements['host2'].text } )[:result].to_a[0][1]
682
+ selementid2 = @server.getseid({'elementid' => hostid2, 'sysmapid' => sysmapid})[:result].to_a[0][1].to_i
683
+ elsif syslink.elements['type2'].text.to_i == ME_TRIGGER # The second element is a trigger
684
+ hostid2 = @server.gethost( { 'pattern' => syslink.elements['host2'].text } )[:result].to_a[0][1]
685
+ triggerid2 = @server.gettrigger({'hostids' => hostid2, 'pattern' => syslink.elements['tdesc2'].text})
686
+ triggerid2 = triggerid2[:result].to_a[0][1].to_i
687
+ selementid2 = @server.getseid({'elementid' => triggerid2, 'sysmapid' => sysmapid})[:result].to_a[0][1].to_i
688
+ elsif syslink.elements['type2'].text.to_i == ME_IMAGE
689
+ label = syslink.elements['label2'].text
690
+ selementid2 = @server.getseid({'pattern' => label, 'sysmapid' => sysmapid})[:result].to_a[0][1].to_i
691
+ end
692
+ link_params = { 'sysmapid' => sysmapid,
693
+ 'selementid1' => selementid1,
694
+ 'selementid2' => selementid2,
695
+ 'triggers' => [], # The triggers require linkid, so this is a catch 22
696
+ 'drawtype' => syslink.elements['drawtype'].text.to_i,
697
+ 'color' => syslink.elements['color'].text.tr('"','') }
698
+ result = @server.addlink([link_params])
699
+ linkid = result[:result].to_i
700
+ puts "Link added: " + link_params.inspect
701
+ #puts "Added map link " + linkid.to_s + " (" + syslink.elements['host1'].text + "(" +
702
+ # hostid1.to_s + ") <-> " + syslink.elements['host2'].text + "(" + hostid2.to_s + "))."
703
+
704
+ if !linkid.nil? # Link triggers require the associated link
705
+ # Sysmap link trigger loop (within the sysmap and syslink loop)
706
+ linktrigger = syslink.elements['linktriggers/']
707
+ if !linktrigger.nil?
708
+ linktrigger = linktrigger[1]
709
+ end
710
+ i = 0
711
+ linktrigger_params = Array.new
712
+ while !linktrigger.nil?
713
+ # Add hostname and tdesc field in the XML to identify the link:
714
+ hostid = @server.gethost( { 'pattern' => linktrigger.elements['host'].text } )[:result].to_a[0][1].to_i
715
+ triggerid = @server.gettrigger({'hostids' => hostid, 'pattern' => linktrigger.elements['tdesc'].text})
716
+ triggerid = triggerid[:result].to_a[0][1].to_i
717
+ if triggerid.nil?
718
+ puts "Failed to find trigger for host " + host + " and description \"" + tdesc + "\"."
719
+ else
720
+ linktrigger_params[i] = { 'linkid' => linkid,
721
+ 'triggerid' => triggerid,
722
+ 'drawtype' => linktrigger.elements['drawtype'].text.to_i,
723
+ 'color' => linktrigger.elements['color'].text.tr('"', '') }
724
+ i = i + 1
725
+ end
726
+ linktrigger = linktrigger.next_element
727
+ end # End linktrigger loop (within sysmap and syslink loop)
728
+ puts "Adding link trigger(s): " + linktrigger_params.inspect
729
+ result = @server.addlinktrigger(linktrigger_params);
730
+ end # If !linkid.nil? (linktrigger)
731
+
732
+ syslink = syslink.next_element
733
+ end # End syslink loop
734
+
735
+ end # End If sysmap
736
+ sysmap = sysmap.next_element
737
+ end # End Sysmap loop
738
+
739
+ end # end do_import
740
+
741
+ end #end class
742
+