class ZabconCore

Public Class Methods

new() click to toggle source
# File libs/zabcon_core.rb, line 50
  def initialize()
    # This must be set first or the debug module will throw an error
    set_debug_level(env["debug"])

    env.register_notifier("debug",self.method(:set_debug_level))
    env.register_notifier("api_debug",self.method(:set_debug_api_level))

    @printer=OutputPrinter.new
    debug(5,:msg=>"Setting up help")
    CommandHelp.setup("english")

    server_login unless env["no_login"]
    env.delete("no_login") if env["no_login"]

    debug(5,:msg=>"Setting up prompt")
    @debug_prompt=false
    if env["have_tty"]
      prc=Proc.new do
        debug_part = @debug_prompt ? " #{debug_level}" : ""
        if !ZabbixServer.instance.connected?
          " #{debug_part}-> "
        else
          ZabbixServer.instance.loggedin? ? " #{debug_part}+> " : " #{debug_part}-> "
        end
      end
      @input=Readline_Input.new
      @input.set_prompt_func(prc)
    else
      @input=STDIN_Input.new
    end

###############################################################################
# Configure the history command.
###############################################################################
    zabconcore=self
    if @input.respond_to?(:history)  #does our input object have a history method?
      ZabconCommand.add_command "history" do
        set_method do  zabconcore.show_history end
        set_help_tag :history
      end
    else
      ZabconCommand.add_command "history" do
        set_method do puts "History is not supported by your version of Ruby and ReadLine" end
        set_help_tag :history
      end
    end
###############################################################################
###############################################################################

    debug(5,:msg=>"Setting up custom commands")


    if !(custom_commands=env["custom_commands"]).nil?
      if custom_commands.is_a?(String)
        load_command_path(custom_commands)
      elsif custom_commands.is_a?(Hash)
        base_path=custom_commands["base_path"]
        custom_commands.delete("base_path") if base_path
        show_load=custom_commands["show_load"]
        if show_load
          show_load=show_load.downcase
          custom_commands.delete("show_load")
          if !show_load.is_a?(String) ||
            !(show_load=="total" || show_load=="all")
            warn "Invalid value for show_load: #{custom_commands}"
            warn "Valid arguments are \"all\" and \"total\""
            env["show_load"]=nil
          else
            env["show_load"]=show_load
          end
        else
          env["show_load"]="all"
        end

        env["load_count"]=0

        custom_commands.each { |k,v|
          load_command_path(v,base_path,show_load)
        }

        puts "#{env["load_count"]} Custom command files loaded" if show_load=="total"
      else
        warn "\"#{custom_commands.to_s}\" is an invalid parameter for custom_commands"
      end
    end

    debug(5,:msg=>"Setup complete")
  end

Public Instance Methods

do_import(input) click to toggle source

TODO: Fix XML Import capability to work again. Import config from an XML file:

# File libs/zabcon_core.rb, line 341
def do_import(input)
  debug(5,:var=>input,:msg=>"args")

  input=input[0]

  begin
    xml_import = REXML::Document.new(File.new(input)).root
  rescue Errno::ENOENT
    raise ZabconError.new("Failed to open import file #{input}.",:retry=>true)
  end

  if xml_import.nil?
    raise ZabconError.new("Failed to parse import file #{input}.",:retry=>true)
  end

  p hosts=xml_import.elements.to_a("//hosts")[0]
  p hosts = hosts.elements.to_a("//host")
  if !hosts.empty?
    host = hosts[0]
  end

  # Loop for the host tags:
  while !host.nil?
    p host_params = { 'host' => host.attributes["name"],
               'port' => host.elements["port"].text }
    if host.elements["useip"].text.to_i == 0 # This is broken in Zabbix export (always 0).
      host_params['dns']=host.elements["dns"].text
    else
      host_params['ip']=host.elements["ip"].text
    end
    # Loop through the groups:
    group = host.elements['groups/']
    if !group.nil?
      group = group[1]
    end
    groupids = Array.new
    while !group.nil?
      result = @server.gethostgroupid({ 'name' => group.text })
      groupid = result[:result].to_i
      if groupid == 0
        puts "The host group " + group.text + " doesn't exist. Attempting to add it."
        result = @server.addhostgroup(['name' => group.text])
        groupid = result[:result].to_a[0][1].to_i
        if groupid == 0
          puts "The group \"" + group.text + "\" doesn't exist and couldn't be added. Terminating import."
          return
        end
      end
      groupids << groupid
      group = group.next_element
    end
    host_params['groupids'] = groupids;

    # Add the host
    result = @server.addhost(host_params)[:result]
    hostid = @server.gethost( { 'pattern' => host.attributes['name'] } )[:result].to_a[0][1]
    if result.nil? # Todo: result is nil when the host is added. I'm not sure if I buggered it up or not.
      puts "Added host " + host.attributes['name'] + ": " + hostid.to_s
    else
      puts "Failed to add host " + host.attributes['name']
    end

    # Item loop (within host loop)
    item = host.elements['items/']
    if !item.nil?
      item = item[1]
      item_params = Array.new
      appids = Array.new
      while !item.nil?
        # Application loop:
        app = item.elements['applications/']
        if !app.nil?
          app = app[1]
          if hostid != 0
            while !app.nil?
              appid = @server.getappid({'name' => app.text, 'hostid' => hostid})[:result]
              if appid == 0
                result = @server.addapp([{'name' => app.text, 'hostid' => hostid}])
                appid = result[:result].to_a[0][1].to_i
                puts "Application " + app.text + " added: " + appid.to_s
              end
              appids << appid
              app = app.next_element
            end
          else
            puts "There is no hostname associated with the application " + app.text
            puts "An application must be associated with a host. It has not been added."
          end
        end

        item_params = { 'description'           => item.elements["description"].text,
                        'key_'                  => item.attributes["key"],
                        'hostid'                => hostid,
                        'delay'                 => item.elements['delay'].text.to_s.to_i,
                        'history'               => item.elements['history'].text.to_s.to_i,
                        'status'                => item.elements['status'].text.to_s.to_i,
                        'type'                  => item.attributes['type'].to_i,
                        'snmp_community'        => item.elements['snmp_community'].text.to_s,
                        'snmp_oid'              => item.elements['snmp_oid'].text.to_s,
                        'value_type'            => item.attributes['value_type'].to_i,
                        'data_type'             => item.elements['data_type'].text.to_s.to_i,
                        'trapper_hosts'         => 'localhost',
                        'snmp_port'             => item.elements['snmp_port'].text.to_s.to_i,
                        'units'                 => item.elements['units'].text.to_s,
                        'multiplier'            => item.elements['multiplier'].text.to_s.to_i,
                        'delta'                 => item.elements['delta'].text.to_s.to_i,
                        'snmpv3_securityname'   => item.elements['snmpv3_securityname'].text.to_s,
                        'snmpv3_securitylevel'  => item.elements['snmpv3_securitylevel'].text.to_s.to_i,
                        'snmpv3_authpassphrase' => item.elements['snmpv3_authpassphrase'].text.to_s,
                        'snmpv3_privpassphrase' => item.elements['snmpv3_privpassphrase'].text.to_s,
                        'formula'               => item.elements['formula'].text.to_s.to_i,
                        'trends'                => item.elements['trends'].text.to_s.to_i,
                        'logtimefmt'            => item.elements['logtimefmt'].text.to_s,
                        'valuemapid'            => 0,
                        'delay_flex'            => item.elements['delay_flex'].text.to_s,
                        'params'                => item.elements['params'].text.to_s,
                        'ipmi_sensor'           => item.elements['ipmi_sensor'].text.to_s.to_i,
                        'applications'          => appids,
                        'templateid'            => 0 }
        added_item = @server.additem([item_params])
        puts "Added item " + item.elements["description"].text + ": " + added_item[0]
        item = item.next_element
      end # End of item loop (within host loop)
    end

    host = host.next_element
  end # End of loop for host tags

  # Trigger loop
  trigger=xml_import.elements['import/triggers']
  if !trigger.nil?
    trigger = trigger[1]
  end
  while !trigger.nil?
    trigger_params = { 'description' => trigger.elements['description'].text,
                       'type'        => trigger.elements['type'].text.to_i,
                       'expression'  => trigger.elements['expression'].text,
                       'url'         => '', # trigger.elements['url'].text,
                       'status'      => trigger.elements['status'].text.to_i,
                       'priority'    => trigger.elements['priority'].text.to_i,
                       'comments'     => 'No comments.' } # trigger.elements['comments'].text }
    result = @server.addtrigger( trigger_params )
    puts "Added trigger " + result[:result][0]['triggerid'] + ": " + trigger.elements['description'].text
    trigger = trigger.next_element
  end

  # Sysmap loop
  sysmap = xml_import.elements['import/sysmaps/']
  if !sysmap.nil?
    sysmap = sysmap[1]
  end
  while !sysmap.nil?
    sysmap_params = { 'name' => sysmap.attributes['name'],
                      'width' => sysmap.elements['width'].text.to_i,
                      'height' => sysmap.elements['height'].text.to_i,
                      'backgroundid' => sysmap.elements['backgroundid'].text.to_i,
                      'label_type' => sysmap.elements['label_type'].text.to_i,
                      'label_location' => sysmap.elements['label_location'].text.to_i }
    sysmapid = 0
    result = @server.addsysmap([sysmap_params])
    # Get sysmapid from the result code
    sysmapid = result[:result][0]['sysmapid'].to_i
    puts "Added sysmap " + sysmap.attributes['name'] + ": " + sysmapid.to_s

    if sysmapid != 0 # We must have a sysmap ID to add elements

      # Element loop (within the sysmap loop)
      element = sysmap.elements['/import/sysmaps/sysmap/elements/']
      if !element.nil?
        element = element[1]
      end
      while !element.nil?
        # Todo: change to use case.
        elementtype = element.elements['elementtype'].text.to_i
        if elementtype != ME_IMAGE
          hostid = @server.gethost( { 'pattern' => element.elements['hostname'].text } )[:result].to_a[0][1].to_i
        end
        if elementtype == ME_HOST
          elementid = hostid
        elsif elementtype == ME_TRIGGER
          elementid = @server.gettrigger({'hostids' => hostid, 'pattern' => element.elements['tdesc'].text})
          elementid = elementid[:result].to_a[0][1].to_i
        else # ME_IMAGE for now.
          elementid = 0
        end
        element_params = { 'label' => element.attributes['label'],
                           'sysmapid' => sysmapid,
                           'elementid' => elementid,
                           'elementtype' => element.elements['elementtype'].text.to_i,
                           'iconid_off' => element.elements['iconid_off'].text.to_i,
                           'iconid_on' => element.elements['iconid_on'].text.to_i,
                           'iconid_unknown' => element.elements['iconid_unknown'].text.to_i,
                           'iconid_disabled' => element.elements['iconid_disabled'].text.to_i,
                           'label_location' => element.elements['label_location'].text.to_i,
                           'x' => element.elements['x'].text.to_i,
                           'y' => element.elements['y'].text.to_i }
                           # 'url' => element.elements['url'].text }
        result = @server.addelementtosysmap([element_params])
        puts "Added map element " + element.attributes['label'] + ": " + result[:result]
        element = element.next_element
      end # End of element loop (within the sysmap loop)

      # Sysmap link loop (within the sysmap loop)
      syslink = sysmap.elements['/import/sysmaps/sysmap/sysmaplinks/']
      if !syslink.nil?
        syslink = syslink[1]
      end
      while !syslink.nil?
        # The code down to "link_params = {" is a mess and needs to be rewritten.
        # elementid = hostid or triggerid depending on element type.
        if syslink.elements['type1'].text.to_i == ME_HOST
          hostid1 = @server.gethost( { 'pattern' => syslink.elements['host1'].text } )[:result].to_a[0][1]
          selementid1 = @server.getseid({'elementid' => hostid1, 'sysmapid' => sysmapid})[:result].to_a[0][1].to_i
        elsif syslink.elements['type1'].text.to_i == ME_TRIGGER # The first element is a trigger
          hostid1 = @server.gethost( { 'pattern' => syslink.elements['host1'].text } )[:result].to_a[0][1]
          triggerid1 = @server.gettrigger({'hostids' => hostid1, 'pattern' => syslink.elements['tdesc1'].text})
          hostid1 = triggerid1[:result].to_a[0][1].to_i
          selementid1 = @server.getseid({'elementid' => hostid1, 'sysmapid' => sysmapid})[:result].to_a[0][1].to_i
        elsif syslink.elements['type1'].text.to_i == ME_IMAGE
          label = syslink.elements['label1'].text
          selementid1 = @server.getseid({'label' => label, 'sysmapid' => sysmapid})[:result].to_a[0][1].to_i
        end
        # The other end of the link:
        if syslink.elements['type2'].text.to_i == ME_HOST
          hostid2 = @server.gethost( { 'pattern' => syslink.elements['host2'].text } )[:result].to_a[0][1]
          selementid2 = @server.getseid({'elementid' => hostid2, 'sysmapid' => sysmapid})[:result].to_a[0][1].to_i
        elsif syslink.elements['type2'].text.to_i == ME_TRIGGER # The second element is a trigger
          hostid2 = @server.gethost( { 'pattern' => syslink.elements['host2'].text } )[:result].to_a[0][1]
          triggerid2 = @server.gettrigger({'hostids' => hostid2, 'pattern' => syslink.elements['tdesc2'].text})
          triggerid2 = triggerid2[:result].to_a[0][1].to_i
          selementid2 = @server.getseid({'elementid' => triggerid2, 'sysmapid' => sysmapid})[:result].to_a[0][1].to_i
        elsif syslink.elements['type2'].text.to_i == ME_IMAGE
          label = syslink.elements['label2'].text
          selementid2 = @server.getseid({'pattern' => label, 'sysmapid' => sysmapid})[:result].to_a[0][1].to_i
        end
        link_params = { 'sysmapid' => sysmapid,
                        'selementid1' => selementid1,
                        'selementid2' => selementid2,
                        'triggers' => [], # The triggers require linkid, so this is a catch 22
                        'drawtype' => syslink.elements['drawtype'].text.to_i,
                        'color' => syslink.elements['color'].text.tr('"','') }
        result = @server.addlink([link_params])
        linkid = result[:result].to_i
        puts "Link added: " + link_params.inspect
        #puts "Added map link " + linkid.to_s + " (" + syslink.elements['host1'].text + "(" +
        #     hostid1.to_s + ") <-> " + syslink.elements['host2'].text + "(" + hostid2.to_s + "))."

        if !linkid.nil? # Link triggers require the associated link
          # Sysmap link trigger loop (within the sysmap and syslink loop)
          linktrigger = syslink.elements['linktriggers/']
          if !linktrigger.nil?
            linktrigger = linktrigger[1]
          end
          i = 0
          linktrigger_params = Array.new
          while !linktrigger.nil?
            # Add hostname and tdesc field in the XML to identify the link:
            hostid = @server.gethost( { 'pattern' => linktrigger.elements['host'].text } )[:result].to_a[0][1].to_i
            triggerid = @server.gettrigger({'hostids' => hostid, 'pattern' => linktrigger.elements['tdesc'].text})
            triggerid = triggerid[:result].to_a[0][1].to_i
            if triggerid.nil?
              puts "Failed to find trigger for host " + host + " and description \"" + tdesc + "\"."
            else
              linktrigger_params[i] = { 'linkid' => linkid,
                                        'triggerid' => triggerid,
                                        'drawtype' => linktrigger.elements['drawtype'].text.to_i,
                                        'color' => linktrigger.elements['color'].text.tr('"', '') }
              i = i + 1
            end
            linktrigger = linktrigger.next_element
          end # End linktrigger loop (within sysmap and syslink loop)
            puts "Adding link trigger(s): " + linktrigger_params.inspect
            result = @server.addlinktrigger(linktrigger_params);
        end # If !linkid.nil? (linktrigger)

        syslink = syslink.next_element
      end # End syslink loop

    end # End If sysmap
    sysmap = sysmap.next_element
  end # End Sysmap loop

end
getprompt() click to toggle source
# File libs/zabcon_core.rb, line 316
def getprompt
debug_part = @debug_prompt ? " #{debug_level}" : ""

return " #{debug_part}-> " if @server.nil?
@server.login? ? " #{debug_part}+> " : " #{debug_part}-> "
end
load_command_path(path,base_path=nil,show_load=nil) click to toggle source
# File libs/zabcon_core.rb, line 191
def load_command_path(path,base_path=nil,show_load=nil)
  base_path||="~"
  load_path=nil
  path=File.expand_path(path,base_path)
  if File.file?(path)
    load_path=[path]
  elsif File.directory?(path)
    load_path=Dir.entries(path).map { |i|
      if i =~ /^\..*/
        nil
      else
        File.join(path,i)
      end
    }
    load_path.compact!
  else
    load_path=Dir[path]
  end

  return if load_path.nil? || load_path.empty?
  env["load_count"]||=0

  load_path.each { |f|
    env["load_count"]+=1
    puts "Loading custom commands from #{f}" if env["echo"] && env["show_load"]=="all"

    begin
      load f
    rescue Exception=> e
      warn "*** Error loading custom commands ***"
      warn "#{e.class.to_s}: #{e}"
      warn "Custom commands from: #{f} were not loaded."
      warn ""
      env["load_count"]-=1
    end
  }
end
server_login() click to toggle source

TODO clean up and streamline with multiple server capabilities

# File libs/zabcon_core.rb, line 140
  def server_login
    loaded_session=false
    begin
      if !env["session_file"].nil? && !env["session_file"].empty?
        path=File.expand_path(env["session_file"])
#        puts "Attempting to load previous session keys from #{env["session_file"]}" if env["echo"]
        yaml=YAML::load(File.open(path))
        if yaml["auth"]
          yaml["auth"].each {|k,v|
            if v.nil?  #cleanup for old auth file
              k="global"
              v=k
            end
            ServerCredentials.instance[k]["auth"]=v
          }
        end
        credentials=ServerCredentials.instance[env["default_server"]]
        if credentials["auth"]
          puts "Attempting to use previous key" if env["echo"]
          loaded_session=ZabbixServer.instance.use_auth(credentials)
          puts "#{env["server"]} connected"  if env["echo"]
          puts "API Version: #{ZabbixServer.instance.version}"  if env["echo"]
        end
      end
    rescue Errno::ECONNREFUSED, Errno::ENOENT, ZbxAPI_ExceptionLoginPermission
      puts "Failed to load previous session key" if env["echo"]
#      return
    end


    credentials=ServerCredentials.instance[env["default_server"]] if credentials.nil?

    if !loaded_session && !credentials["server"].nil? &&
      !credentials["username"].nil? && !credentials["password"].nil?
      puts "Found valid login credentials, attempting login"  if env["echo"]
      begin

        #
        ZabbixServer.instance.login(credentials)

      rescue ZbxAPI_ExceptionBadAuth => e
        puts e.message
      rescue ZbxAPI_ExceptionLoginPermission
        puts "Error Invalid login or no API permissions."
      rescue ZbxAPI_ExceptionBadServerUrl
        puts "Error connecting to server"   #TODO Fix message to show hostname
      end
    end

  end
set_debug(input) click to toggle source
# File libs/zabcon_core.rb, line 323
def set_debug(input)
  if input["prompt"].nil? then
    puts "This command is deprecated, please use \"set env debug=n\""
    @env["debug"]=input.keys[0].to_i
  else
    @debug_prompt=!@debug_prompt
  end
end
set_debug_api_level(value) click to toggle source
# File libs/zabcon_core.rb, line 332
def set_debug_api_level(value)
  puts "inside set_debug_api_level"
  set_facility_debug_level(:api,value)
end
start() click to toggle source
# File libs/zabcon_core.rb, line 230
  def start
    debug(5,:msg=>"Entering main zabcon start routine")
    puts "Welcome to Zabcon.  Build Number: #{REVISION}"  if env["echo"]
    puts "Use the command 'help' to get help on commands" if env["have_tty"] || env["echo"]

    begin
      catch(:exit) do
        while line=@input.get_line()
          #tokens=ExpressionTokenizer.new(line)
          tokens=CommandTokenizer.new(line)

#          tokens.delete_if {|item| item.kind==:comment }
          tokens.map!{|item|
            case item.kind
              when :comment
                item=nil
              when :variable
                var_name=item.value[1..-1]
                if var_name=="auth"
                  item.set_value(ServerCredentials.instance[env["default_server"]]["auth"])
                elsif env[var_name]
                  item.set_value(env[var_name])
                elsif vars[var_name]
                  item.set_value(vars[var_name])
                else
                  raise ZbxAPI_ParameterError.new("Unknown Variable #{item.value}")
                end
                item
              else
                item
            end
          }.compact!

          next if tokens.nil? || tokens.first.kind==:end ||
              (tokens.first.kind==:whitespace && tokens[1].kind==:end)
          debug(6, :var=>line, :msg=>"Input from user")

          commands=ZabconExecuteContainer.new(tokens)
          debug(8,:var=>commands,:msg=>"Commands tree")

          commands.execute
          @printer.print(commands.results,commands.show_params) if commands.print?

        end  # while
      end #end catch
    rescue CommandList::InvalidCommand, Command::NonFatalError,
        Command::ParameterError, ZabbixServer::ConnectionProblem,
        ZbxAPI_ExceptionVersion, ZbxAPI_ExceptionBadAuth,
        ZbxAPI_ParameterError, Command::LoginRequired => e
      puts e.message
      retry
    rescue ParseError => e  #catch the base exception class
      e.show_message
      retry if e.retry?
    rescue ZbxAPI_ExceptionLoginPermission
      puts "No login permissions"
      retry
    rescue ZbxAPI_ExceptionPermissionError
      puts "You do not have permission to perform that operation"
      retry
    rescue ZbxAPI_ExceptionBadServerUrl
      puts "A Zabbix server was not found at that url"
      retry
    rescue ZbxAPI_GeneralError => e
      puts "An error was received from the Zabbix server"
      if e.message.class==Hash
        puts "Error code: #{e.message["code"]}"
        puts "Error message: #{e.message["message"]}"
        puts "Error data: #{e.message["data"]}"
        retry
      else
        e.show_message
        retry if e.retry?
      end
    rescue ZError => e
      puts
      if e.retry?
        puts "A non-fatal error occurred."
      else
        puts "A fatal error occurred."
      end
      e.show_message
      retry if e.retry?
    end  #end of exception block
  end