diff options
Diffstat (limited to 'lib/oxidized')
33 files changed, 696 insertions, 88 deletions
| diff --git a/lib/oxidized/hook/awssns.rb b/lib/oxidized/hook/awssns.rb new file mode 100644 index 0000000..dbc2d47 --- /dev/null +++ b/lib/oxidized/hook/awssns.rb @@ -0,0 +1,27 @@ +require 'aws-sdk' + +class AwsSns < Oxidized::Hook +  def validate_cfg! +    raise KeyError, 'hook.region is required' unless cfg.has_key?('region') +    raise KeyError, 'hook.topic_arn is required' unless cfg.has_key?('topic_arn') +  end + +  def run_hook(ctx) +    sns = Aws::SNS::Resource.new(region: cfg.region) +    topic = sns.topic(cfg.topic_arn) +    message = { +      :event => ctx.event.to_s +    } +    if ctx.node +      message.merge!( +        :group => ctx.node.group.to_s, +        :model => ctx.node.model.class.name.to_s.downcase, +        :node => ctx.node.name.to_s +      ) +    end +    topic.publish({ +      message: message.to_json +    }) +  end + +end diff --git a/lib/oxidized/input/ssh.rb b/lib/oxidized/input/ssh.rb index cd12167..9a5c508 100644 --- a/lib/oxidized/input/ssh.rb +++ b/lib/oxidized/input/ssh.rb @@ -17,8 +17,9 @@ module Oxidized      class NoShell < OxidizedError; end      def connect node -      @node       = node -      @output     = '' +      @node        = node +      @output      = '' +      @pty_options = { term: "vt100" }        @node.model.cfg['ssh'].each { |cb| instance_exec(&cb) }        secure = Oxidized.config.input.ssh.secure        @log = File.open(Oxidized::Config::Log + "/#{@node.ip}-ssh", 'w') if Oxidized.config.input.debug? @@ -32,9 +33,10 @@ module Oxidized          :paranoid => secure,          :auth_methods => %w(none publickey password keyboard-interactive),          :number_of_password_prompts => 0, -        :proxy => proxy +        :proxy => proxy,        } -      ssh_opts[:kex] = vars(:ssh_kex).split(/,\s*/) if vars(:ssh_kex) +      ssh_opts[:keys] = vars(:ssh_keys).is_a?(Array) ? vars(:ssh_keys) : [vars(:ssh_keys)] if vars(:ssh_keys) +      ssh_opts[:kex]  = vars(:ssh_kex).split(/,\s*/) if vars(:ssh_kex)        ssh_opts[:encryption] = vars(:ssh_encryption).split(/,\s*/) if vars(:ssh_encryption)        Oxidized.logger.debug "lib/oxidized/input/ssh.rb: Connecting to #{@node.name}" @@ -42,7 +44,7 @@ module Oxidized        unless @exec          shell_open @ssh          begin -          @username ? shell_login : expect(@node.prompt) +          login          rescue Timeout::Error            raise PromptUndetect, [ @output, 'not matching configured prompt', @node.prompt ].join(' ')          end @@ -71,6 +73,10 @@ module Oxidized        @output      end +    def pty_options hash +      @pty_options = @pty_options.merge hash +    end +      private      def disconnect @@ -93,7 +99,7 @@ module Oxidized            @output << data            @output = @node.model.expects @output          end -        ch.request_pty (_opts={:term=>'vt100'}) do |_ch, success_pty| +        ch.request_pty (@pty_options) do |_ch, success_pty|            raise NoShell, "Can't get PTY" unless success_pty            ch.send_channel_request 'shell' do |_ch, success_shell|              raise NoShell, "Can't get shell" unless success_shell @@ -102,13 +108,18 @@ module Oxidized        end      end -    # Cisco WCS has extremely dubious SSH implementation, SSH auth is always -    # success, it always opens shell and then run auth in shell. I guess -    # they'll never support exec() :) -    def shell_login -      expect username -      cmd @node.auth[:username], password -      cmd @node.auth[:password] +    # some models have SSH auth or terminal auth based on version of code +    # if SSH is configured for terminal auth, we'll still try to detect prompt +    def login +      if @username +        match = expect username, @node.prompt +        if match == username +          cmd @node.auth[:username], password +          cmd @node.auth[:password] +        end +      else +        expect @node.prompt +      end      end      def exec state=nil @@ -123,14 +134,18 @@ module Oxidized        @output      end -    def expect regexp -      Oxidized.logger.debug "lib/oxidized/input/ssh.rb: expecting #{regexp.inspect} at #{node.name}" +    def expect *regexps +      regexps = [regexps].flatten +      Oxidized.logger.debug "lib/oxidized/input/ssh.rb: expecting #{regexps.inspect} at #{node.name}"        Timeout::timeout(Oxidized.config.timeout) do          @ssh.loop(0.1) do            sleep 0.1 -          not @output.match regexp +          match = regexps.find { |regexp| @output.match regexp } +          return match if match +          true          end        end      end +    end  end diff --git a/lib/oxidized/input/tftp.rb b/lib/oxidized/input/tftp.rb new file mode 100644 index 0000000..78164d0 --- /dev/null +++ b/lib/oxidized/input/tftp.rb @@ -0,0 +1,41 @@ +module Oxidized +  require 'stringio' +  require_relative 'cli' +   +  begin +    require 'net/tftp' +  rescue LoadError +    raise OxidizedError, 'net/tftp not found: sudo gem install net-tftp' +  end +   +  class TFTP < Input +     +    include Input::CLI +     +    # TFTP utilizes UDP, there is not a connection. We simply specify an IP and send/receive data. +    def connect node +      @node       = node + +      @node.model.cfg['tftp'].each { |cb| instance_exec(&cb) } +      @log = File.open(Oxidized::Config::Log + "/#{@node.ip}-tftp", 'w') if Oxidized.config.input.debug? +      @tftp = Net::TFTP.new @node.ip +    end + +    def cmd file +      Oxidized.logger.debug "TFTP: #{file} @ #{@node.name}" +      config = StringIO.new +      @tftp.getbinary file, config +      config.rewind +      config.read +    end +     +    private +     +    def disconnect +      # TFTP uses UDP, there is no connection to close +    ensure +      @log.close if Oxidized.config.input.debug? +    end + +  end +end diff --git a/lib/oxidized/model/acos.rb b/lib/oxidized/model/acos.rb index bb9846e..47649a2 100644 --- a/lib/oxidized/model/acos.rb +++ b/lib/oxidized/model/acos.rb @@ -6,6 +6,13 @@ class ACOS < Oxidized::Model    ##ACOS prompt changes depending on the state of the device    prompt /^([-\w.\/:?\[\]\(\)]+[#>]\s?)$/ +  cmd :secret do |cfg| +    cfg.gsub!(/community read encrypted (\S+)/, 'community read encrypted <hidden>') # snmp +    cfg.gsub!(/secret encrypted (\S+)/, 'secret encrypted <hidden>') # tacacs-server +    cfg.gsub!(/password encrypted (\S+)/, 'password encrypted <hidden>') # user +    cfg +  end +    cmd 'show version' do |cfg|      cfg.gsub! /\s(Last configuration saved at).*/, ' \\1 <removed>'      cfg.gsub! /\s(Memory).*/, ' \\1 <removed>' @@ -22,11 +29,20 @@ class ACOS < Oxidized::Model      comment cfg    end +  cmd 'show partition-config all' do |cfg| +     cfg.gsub! /(Current configuration).*/, '\\1 <removed>' +     cfg.gsub! /(Configuration last updated at).*/, '\\1 <removed>' +     cfg.gsub! /(Configuration last saved at).*/, '\\1 <removed>' +     cfg.gsub! /(Configuration last synchronized at).*/, '\\1 <removed>' +     cfg +  end   +    cmd 'show running-config all-partitions' do |cfg|       cfg.gsub! /(Current configuration).*/, '\\1 <removed>'       cfg.gsub! /(Configuration last updated at).*/, '\\1 <removed>'       cfg.gsub! /(Configuration last saved at).*/, '\\1 <removed>'       cfg.gsub! /(Configuration last synchronized at).*/, '\\1 <removed>' +     cfg    end    cmd 'show aflex all-partitions' do |cfg| diff --git a/lib/oxidized/model/alvarion.rb b/lib/oxidized/model/alvarion.rb new file mode 100644 index 0000000..3c762de --- /dev/null +++ b/lib/oxidized/model/alvarion.rb @@ -0,0 +1,13 @@ +class Alvarion < Oxidized::Model + +  # Used in Alvarion wisp equipment + +  # Run this command as an instance of Model so we can access node +  pre do +    cmd "#{node.auth[:password]}.cfg" +  end + + +  cfg :tftp {} + +end diff --git a/lib/oxidized/model/aosw.rb b/lib/oxidized/model/aosw.rb index 394561f..11d8442 100644 --- a/lib/oxidized/model/aosw.rb +++ b/lib/oxidized/model/aosw.rb @@ -28,19 +28,21 @@ class AOSW < Oxidized::Model    cmd 'show version' do |cfg|      cfg = cfg.each_line.select { |line| not line.match /Switch uptime/i } -    comment cfg.join +    rstrip_cfg comment cfg.join    end    cmd 'show inventory' do |cfg| -    clean cfg +    rstrip_cfg clean cfg    end    cmd 'show slots' do |cfg| -    comment cfg +    rstrip_cfg comment cfg    end +    cmd 'show license' do |cfg| -    comment cfg +    rstrip_cfg comment cfg    end +    cmd 'show running-config' do |cfg|      out = []      cfg.each_line do |line| @@ -60,8 +62,8 @@ class AOSW < Oxidized::Model    cfg :telnet, :ssh do      if vars :enable        post_login do -        send 'enable\n' -        send vars(:enable) + '\n' +        send "enable\n" +        cmd vars(:enable)        end      end      post_login 'no paging' @@ -72,6 +74,15 @@ class AOSW < Oxidized::Model      pre_logout 'exit'    end +  def rstrip_cfg cfg +    out = [] +    cfg.each_line do |line| +      out << line.rstrip +    end +    out = out.join "\n" +    out << "\n" +  end +    def clean cfg      out = []      cfg.each_line do |line| diff --git a/lib/oxidized/model/apc_aos.rb b/lib/oxidized/model/apc_aos.rb new file mode 100644 index 0000000..530d436 --- /dev/null +++ b/lib/oxidized/model/apc_aos.rb @@ -0,0 +1,11 @@ +class Apc_aos < Oxidized::Model + +  cmd 'config.ini' do |cfg| +    cfg.gsub! /^; Configuration file\, generated on.*/, '' +  end + +  cfg :ftp do +  end + +end + diff --git a/lib/oxidized/model/asa.rb b/lib/oxidized/model/asa.rb index a41348e..df30059 100644 --- a/lib/oxidized/model/asa.rb +++ b/lib/oxidized/model/asa.rb @@ -15,7 +15,7 @@ class ASA < Oxidized::Model      cfg.gsub! /username (\S+) password (\S+) (.*)/, 'username \1 password <secret hidden> \3'      cfg.gsub! /ikev2 pre-shared-key (\S+)/, 'ikev2 pre-shared-key <secret hidden>'      cfg.gsub! /ikev2 (remote|local)-authentication pre-shared-key (\S+)/, 'ikev2 \1-authentication pre-shared-key <secret hidden>' -    cfg.gsub! /^(aaa-server TACACS\+ \(\S+\) host.*\n\skey) \S+$/m, '\1 <secret hidden>' +    cfg.gsub! /^(aaa-server TACACS\+? \(\S+\) host.*\n\skey) \S+$/mi, '\1 <secret hidden>'      cfg    end diff --git a/lib/oxidized/model/casa.rb b/lib/oxidized/model/casa.rb new file mode 100644 index 0000000..e85c904 --- /dev/null +++ b/lib/oxidized/model/casa.rb @@ -0,0 +1,46 @@ +class Casa < Oxidized::Model +  # Casa Systems CMTS + +  prompt /^([\w.@()-]+[#>]\s?)$/ +  comment '! ' + +  cmd :secret do |cfg| +    cfg.gsub! /^(snmp community) \S+/, '\\1 <configuration removed>' +    cfg.gsub! /^(snmp comm-tbl) \S+ \S+/, '\\1 <removed> <removed>' +    cfg.gsub! /^(console-password encrypted) \S+/, '\\1 <secret hidden>' +    cfg.gsub! /^(password encrypted) \S+/, '\\1 <secret hidden>' +    cfg.gsub! /^(tacacs-server key) \S+/, '\\1 <secret hidden>' +    cfg +  end + +  cmd :all do |cfg| +    cfg.each_line.to_a[1..-2].join +  end + +  cmd 'show system' do |cfg| +    comment cfg.each_line.reject { |line| line.match /^\s+System (Time|Uptime): / }.join +  end + +  cmd 'show version' do |cfg| +    comment cfg +  end + +  cmd 'show run' + +  cfg :telnet do +    username /^Username:/ +    password /^Password:/ +  end + +  cfg :telnet, :ssh do +    post_login 'page-off' +    # preferred way to handle additional passwords +    if vars :enable +      post_login do +        send "enable\n" +        cmd vars(:enable) +      end +    end +    pre_logout 'logout' +  end +end diff --git a/lib/oxidized/model/catos.rb b/lib/oxidized/model/catos.rb index 874ebbc..bac9eec 100644 --- a/lib/oxidized/model/catos.rb +++ b/lib/oxidized/model/catos.rb @@ -1,6 +1,6 @@  class Catos < Oxidized::Model -  prompt /^[\w.@-]+> \(enable\) $/ +  prompt /^[\w.@-]+>\s?(\(enable\) )?$/    comment '# '    cmd :all do |cfg| @@ -28,8 +28,15 @@ class Catos < Oxidized::Model      password /^Password:/    end -  cfg :ssh, :telnet do +  cfg :telnet, :ssh do      post_login 'set length 0' +    # preferred way to handle additional passwords +    if vars :enable +      post_login do +        send "enable\n" +        cmd vars(:enable) +      end +    end      pre_logout 'exit'    end diff --git a/lib/oxidized/model/ciscosmb.rb b/lib/oxidized/model/ciscosmb.rb index 3ef9a85..e5501d5 100644 --- a/lib/oxidized/model/ciscosmb.rb +++ b/lib/oxidized/model/ciscosmb.rb @@ -33,14 +33,13 @@ class CiscoSMB < Oxidized::Model      cfg    end -  cfg :telnet do -    username /^User Name:/ -    password /^\r?Password:$/ -  end -    cfg :telnet, :ssh do +    username /^User ?[nN]ame:/ +    password /^\r?Password:$/      post_login 'terminal datadump' # Disable pager      post_login 'terminal width 0' +    post_login 'terminal len 0' +    pre_logout 'exit' #exit returns to previous priv level, no way to quit from exec(#)      pre_logout 'exit'    end diff --git a/lib/oxidized/model/dlink.rb b/lib/oxidized/model/dlink.rb new file mode 100644 index 0000000..5756bad --- /dev/null +++ b/lib/oxidized/model/dlink.rb @@ -0,0 +1,36 @@ +class Dlink < Oxidized::Model +  # D-LINK Switches + +  prompt /^(\r*[\w.@():-]+[#>]\s?)$/ +  comment '# ' + +  cmd :secret do |cfg| +    cfg.gsub! /^(create snmp community) \S+/, '\\1 <removed>' +    cfg.gsub! /^(create snmp group) \S+/, '\\1 <removed>' +    cfg +  end + +  cmd :all do |cfg| +    cfg.each_line.to_a[2..-2].map{|line|line.delete("\r").rstrip}.join("\n") + "\n" +  end + +  cmd 'show switch' do |cfg| +    comment cfg +  end + +  cmd 'show vlan' do |cfg| +    comment cfg +  end + +  cmd 'show config current' + +  cfg :telnet do +    username /\r*username:/ +    password /\r*password:/ +  end + +  cfg :telnet, :ssh do +    post_login 'disable clipaging' +    pre_logout 'logout' +  end +end diff --git a/lib/oxidized/model/dnos.rb b/lib/oxidized/model/dnos.rb index 1c31aad..a44630e 100644 --- a/lib/oxidized/model/dnos.rb +++ b/lib/oxidized/model/dnos.rb @@ -33,15 +33,16 @@ class DNOS  < Oxidized::Model    end    cfg :telnet, :ssh do -    post_login 'terminal length 0' -    post_login 'terminal width 0'      if vars :enable        post_login do          send "enable\n" -        send vars(:enable) + "\n" +        cmd vars(:enable)        end      end +    post_login 'terminal length 0' +    post_login 'terminal width 0'      pre_logout 'exit' +    pre_logout 'exit'        end  end diff --git a/lib/oxidized/model/eos.rb b/lib/oxidized/model/eos.rb index 75da0fa..a9f3ff3 100644 --- a/lib/oxidized/model/eos.rb +++ b/lib/oxidized/model/eos.rb @@ -22,7 +22,7 @@ class EOS < Oxidized::Model      comment cfg    end -  cmd 'show running-config | no-more' do |cfg| +  cmd 'show running-config | no-more | exclude ! Time:' do |cfg|      cfg    end diff --git a/lib/oxidized/model/fiberdriver.rb b/lib/oxidized/model/fiberdriver.rb new file mode 100644 index 0000000..8f8eb07 --- /dev/null +++ b/lib/oxidized/model/fiberdriver.rb @@ -0,0 +1,21 @@ +class FiberDriver < Oxidized::Model +  prompt /\w+#/ +  comment "! " + +  cmd :all do |cfg| +    cfg.each_line.to_a[1..-2].join +  end +  cmd 'show inventory' do |cfg| +    comment cfg +  end + +  cmd "show running-config" do |cfg| +    cfg.each_line.to_a[3..-1].join +  end + +  cfg :ssh do +    post_login 'terminal length 0' +    post_login 'terminal width 512' +    pre_logout 'exit' +  end +end diff --git a/lib/oxidized/model/fujitsupy.rb b/lib/oxidized/model/fujitsupy.rb new file mode 100644 index 0000000..20a78dd --- /dev/null +++ b/lib/oxidized/model/fujitsupy.rb @@ -0,0 +1,42 @@ +class FujitsuPY < Oxidized::Model + +  prompt /^(\([\w.-]*\)\s#|^\S+\#\s)$/ +  comment  '! ' + +  cmd :all do |cfg| +    cfg.each_line.to_a[1..-2].join +  end + +# 1Gbe switch +  cmd 'show version' do |cfg| +    cfg.gsub! /^(<ERROR> : 2 : format error)$/, '' +    comment cfg +  end + +# 10Gbe switch +  cmd 'show system information' do |cfg| +    cfg.gsub! /^Current-time : [\w\s:]*$/, '' +    cfg.gsub! /^(\s{33}\^)$/, '' +    cfg.gsub! /^(\% Invalid input detected at '\^' marker.)$/, '' +    comment cfg +  end + +  cmd 'show running-config' do |cfg| +    cfg +  end + +  cfg :telnet do +    username /^Username:/ +    password /^Password:/ +  end + +  cfg :telnet, :ssh do +    post_login 'no pager' +    post_login 'terminal pager disable' +    pre_logout do +      send "quit\n" +      send "n\n" +    end +  end + +end diff --git a/lib/oxidized/model/hatteras.rb b/lib/oxidized/model/hatteras.rb new file mode 100644 index 0000000..4192cbc --- /dev/null +++ b/lib/oxidized/model/hatteras.rb @@ -0,0 +1,52 @@ +class Hatteras < Oxidized::Model +  # Hatteras Networks + +  prompt /^(\r?[\w.@()-]+[#>]\s?)$/ +  comment '# ' + +  expect /WARNING: System configuration changes will be lost when the device restarts./ do |data, re| +    send "y\r" +    data.sub re, '' +  end + + +  cmd :secret do |cfg| +    cfg.gsub! /^(community) \S+/, '\\1 "<configuration removed>"' +    cfg.gsub! /^(communityString) "\S+"/, '\\1 "<configuration removed>"' +    cfg.gsub! /^(key) "\S+"/, '\\1 "<secret hidden>"' +    cfg +  end + +  cmd :all do |cfg| +    cfg.each_line.to_a[1..-2].join +  end + +  cmd "show switch\r" do |cfg| +    cfg = cfg.each_line.reject { |line| line.match /Switch uptime|Switch temperature|Last reset reason/ or +                                          line.match /TermCpuUtil|^\s+\^$|ERROR: Bad command/ }.join +    comment cfg +  end + +  cmd "show card\r" do |cfg| +    cfg = cfg.each_line.reject { |line| line.match /Card uptime|Card temperature|Last reset reason/ or +                                          line.match /TermCpuUtil|^\s+\^$|ERROR: Bad command/ }.join +    comment cfg +  end + +  cmd "show sfp *\r" do |cfg| +    comment cfg +  end + +  cmd "show config run\r" do |cfg| +    cfg +  end + +  cfg :telnet do +    username /^Login:/ +    password /^Password:/ +  end + +  cfg :telnet, :ssh do +    pre_logout "logout\r" +  end +end diff --git a/lib/oxidized/model/hpebladesystem.rb b/lib/oxidized/model/hpebladesystem.rb new file mode 100644 index 0000000..5e34de8 --- /dev/null +++ b/lib/oxidized/model/hpebladesystem.rb @@ -0,0 +1,83 @@ +class HPEBladeSystem < Oxidized::Model +  # HPE Onboard Administrator + +  prompt /.*> / +  comment '# ' + +  expect /^\s*--More--\s+.*$/ do |data, re| +     send ' ' +     data.sub re, '' +  end + +  cmd :all do |cfg| +    cfg = cfg.delete("\r").each_line.to_a[0..-1].map{|line|line.rstrip}.join("\n") + "\n" +    cfg.each_line.to_a[0..-2].join +  end + +  cmd :secret do |cfg| +    cfg.gsub! /^(SET SNMP COMMUNITY (READ|WRITE)).*/, '\\1 <configuration removed>' +    cfg +  end + +  cmd 'show oa info' do |cfg| +    comment cfg +  end +   +  cmd 'show oa network' do |cfg| +    comment cfg +  end + +  cmd 'show oa certificate' do |cfg| +    comment cfg +  end +   +  cmd 'show sshfingerprint' do |cfg| +    comment cfg +  end +   +  cmd 'show fru' do |cfg| +    comment cfg +  end +   +  cmd 'show network' do |cfg| +    comment cfg +  end + +  cmd 'show vlan' do |cfg| +    comment cfg +  end + +  cmd 'show rack name' do |cfg| +    comment cfg +  end + +  cmd 'show server list' do |cfg| +    comment cfg +  end +   +  cmd 'show server names' do |cfg| +    comment cfg +  end +   +  cmd 'show server port map all' do |cfg| +    comment cfg +  end +   +  cmd 'show server info all' do |cfg| +    comment cfg +  end + +  cmd 'show config' do |cfg| +    cfg.gsub! /^#(Generated on:) .*$/, '\\1 <removed>' +    cfg.gsub /^\s+/, '' +  end +   +  cfg :telnet do +    username /\slogin:/ +    password /^Password: / +  end +  +   cfg :telnet, :ssh do +     pre_logout "exit" +   end +end diff --git a/lib/oxidized/model/ios.rb b/lib/oxidized/model/ios.rb index 1f099c8..3cbe0f0 100644 --- a/lib/oxidized/model/ios.rb +++ b/lib/oxidized/model/ios.rb @@ -26,7 +26,9 @@ class IOS < Oxidized::Model      cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>'      cfg.gsub! /username (\S+) privilege (\d+) (\S+).*/, '<secret hidden>'      cfg.gsub! /^username \S+ password \d \S+/, '<secret hidden>' +    cfg.gsub! /^username \S+ secret \d \S+/, '<secret hidden>'      cfg.gsub! /^enable password \d \S+/, '<secret hidden>' +    cfg.gsub! /^enable secret \d \S+/, '<secret hidden>'      cfg.gsub! /wpa-psk ascii \d \S+/, '<secret hidden>'      cfg.gsub! /^tacacs-server key \d \S+/, '<secret hidden>'      cfg diff --git a/lib/oxidized/model/ironware.rb b/lib/oxidized/model/ironware.rb index 1e8c30e..db341d1 100644 --- a/lib/oxidized/model/ironware.rb +++ b/lib/oxidized/model/ironware.rb @@ -2,14 +2,14 @@ class IronWare < Oxidized::Model    prompt /^.*(telnet|ssh)\@.+[>#]\s?$/i    comment  '! ' -   +    #to handle pager without enable    #expect /^((.*)--More--(.*))$/ do |data, re|    #  send ' '    #  data.sub re, ''    #end -   +    #to remove backspace (if handle pager without enable)    #expect /^((.*)[\b](.*))$/ do |data, re|    #  data.sub re, '' @@ -44,14 +44,14 @@ class IronWare < Oxidized::Model        out << sc.rest        cfg = out      end -     +      comment cfg    end -   +    cmd 'show flash' do |cfg|      comment cfg    end -   +    cmd 'show module' do |cfg|      cfg.gsub! /^((Invalid input)|(Type \?)).*$/, '' # some ironware devices are fixed config      comment cfg @@ -74,7 +74,7 @@ class IronWare < Oxidized::Model      if vars :enable        post_login do          send "enable\r\n" -        send vars(:enable) + "\r\n" +        cmd vars(:enable)        end      end      post_login '' diff --git a/lib/oxidized/model/pfsense.rb b/lib/oxidized/model/pfsense.rb index cd6885c..c02c0d0 100644 --- a/lib/oxidized/model/pfsense.rb +++ b/lib/oxidized/model/pfsense.rb @@ -1,20 +1,14 @@  class PfSense < Oxidized::Model -   -  comment  '# ' -   -  #add a comment in the final conf -  def add_comment comment -    "\n###### #{comment} ######\n"  -  end +  # use other use than 'admin' user, 'admin' user cannot get ssh/exec. See issue #535 +      cmd :all do |cfg|      cfg.each_line.to_a[1..-2].join    end -  #show the persistent configuration -  pre do -    cfg = add_comment 'Configuration' -    cfg += cmd 'cat /cf/conf/config.xml'     +  cmd 'cat /cf/conf/config.xml' do |cfg| +    cfg.gsub! /\s<revision>\s*.*\s*<time>\d*<\/time>\s*.*\s*<\/revision>/, '' +    cfg    end    cfg :ssh do diff --git a/lib/oxidized/model/planet.rb b/lib/oxidized/model/planet.rb new file mode 100644 index 0000000..05a369a --- /dev/null +++ b/lib/oxidized/model/planet.rb @@ -0,0 +1,83 @@ +class Planet < Oxidized::Model + +  prompt /^\r?([\w.@()-]+[#>]\s?)$/ +  comment  '! ' + +  # example how to handle pager +  #expect /^\s--More--\s+.*$/ do |data, re| +  # send ' ' +  # data.sub re, '' +  #end + +  # non-preferred way to handle additional PW prompt +  #expect /^[\w.]+>$/ do |data| +  #  send "enable\n" +  #  send vars(:enable) + "\n" +  #  data +  #end + +  cmd :all do |cfg| +    #cfg.gsub! /\cH+\s{8}/, ''         # example how to handle pager +    #cfg.gsub! /\cH+/, ''              # example how to handle pager +    cfg.each_line.to_a[1..-2].join +  end + +  cmd :secret do |cfg| +    cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>' +    cfg.gsub! /username (\S+) privilege (\d+) (\S+).*/, '<secret hidden>' +    cfg.gsub! /^username \S+ password \d \S+/, '<secret hidden>' +    cfg.gsub! /^enable password \d \S+/, '<secret hidden>' +    cfg.gsub! /wpa-psk ascii \d \S+/, '<secret hidden>' +    cfg.gsub! /^tacacs-server key \d \S+/, '<secret hidden>' +    cfg +  end + +  cmd 'show version' do |cfg| +    cfg.gsub! "\n\r", "\n" +    @planetgs = true if cfg.match /^System Name\w*:\w*GS-.*$/ +    @planetsgs = true if cfg.match /SGS-(.*) Device, Compiled on .*$/ + +    cfg = cfg.each_line.to_a[0...-2] + +   # Strip system time and system uptime from planet gs switches +    cfg = cfg.reject { |line| line.match /System Time\s*:.*/ } +    cfg = cfg.reject { |line| line.match /System Uptime\s*:.*/ } + +    comment cfg.join +  end + + +  cmd 'show running-config' do |cfg| +    cfg.gsub! "\n\r", "\n" +    cfg = cfg.each_line.to_a + +    cfg = cfg.reject { |line| line.match "Building configuration..." } +   +    if @planetsgs +      cfg << cmd('show transceiver detail | include transceiver detail information|found|Type|length|Nominal|wavelength|Base information') do |cfg| +        comment cfg +      end +    end + +    cfg.join +  end +   + +  cfg :telnet do +    username /^Username:/ +    password /^Password:/ +  end + +  cfg :telnet, :ssh do +    post_login 'terminal length 0' +    # preferred way to handle additional passwords +    if vars :enable +      post_login do +        send "enable\n" +        cmd vars(:enable) +      end +    end +    pre_logout 'exit' +  end + +end diff --git a/lib/oxidized/model/powerconnect.rb b/lib/oxidized/model/powerconnect.rb index f0fa3df..ac36c26 100644 --- a/lib/oxidized/model/powerconnect.rb +++ b/lib/oxidized/model/powerconnect.rb @@ -39,7 +39,7 @@ class PowerConnect < Oxidized::Model      if vars :enable        post_login do          send "enable\n" -        send vars(:enable) + "\n" +        cmd vars(:enable)        end      end diff --git a/lib/oxidized/model/procurve.rb b/lib/oxidized/model/procurve.rb index da792e6..c117df3 100644 --- a/lib/oxidized/model/procurve.rb +++ b/lib/oxidized/model/procurve.rb @@ -57,4 +57,8 @@ class Procurve < Oxidized::Model      pre_logout "logout\ny\nn"    end +  cfg :ssh do +    pty_options({ chars_wide: 1000 }) +  end +  end diff --git a/lib/oxidized/model/routeros.rb b/lib/oxidized/model/routeros.rb index 4822500..a92ad5e 100644 --- a/lib/oxidized/model/routeros.rb +++ b/lib/oxidized/model/routeros.rb @@ -1,5 +1,5 @@  class RouterOS < Oxidized::Model -  prompt /\[\w+@\S+\]\s?>\s?$/ +  prompt /\[\w+@\S+(\s?\S+)*\]\s?>\s?$/    comment "# "    cmd '/system routerboard print' do |cfg| @@ -8,6 +8,7 @@ class RouterOS < Oxidized::Model    cmd '/export' do |cfg|      cfg.gsub! /\x1B\[([0-9]{1,3}((;[0-9]{1,3})*)?)?[m|K]/, '' # strip ANSI colours +    cfg.gsub! /\\\r\n\s+/, ''   # strip new line      cfg = cfg.split("\n").select { |line| not line[/^\#\s\w{3}\/\d{2}\/\d{4}.*$/] }      cfg.join("\n") + "\n"    end diff --git a/lib/oxidized/model/trango.rb b/lib/oxidized/model/trango.rb new file mode 100644 index 0000000..b2aa1e7 --- /dev/null +++ b/lib/oxidized/model/trango.rb @@ -0,0 +1,62 @@ +class Trango < Oxidized::Model +  # take a Trangolink sysinfo output and turn it into a configuration file +   +  prompt /^#>\s?/ +  comment  '# ' + +  cmd 'sysinfo' do |cfg| +    out = [] +    comments = [] +    cfg.each_line do |line| +      if line.match /\[Opmode\] (off|on) \[Default Opmode\] (off|on)/ +        out << "opmode " + Regexp.last_match[1] +        out << "defaultopmode " + Regexp.last_match[2] +      end +      if line.match /\[Tx Power\] ([\-\d]+) dBm/ +        out << "power " + Regexp.last_match[1] +      end +      if line.match /\[Active Channel\] (\d+) (v|h)/ +        out << "freq " + Regexp.last_match[1] + ' ' + Regexp.last_match[2] +      end +      if line.match /\[Peer ID\] ([A-F0-9]+)/ +        out << "peerid " + Regexp.last_match[1] +      end +      if line.match /\[Unit Type\] (\S+)/ +        out << "utype " + Regexp.last_match[1] +      end +      if line.match /\[(Hardware Version|Firmware Version|Model|S\/N)\] (\S+)/ +        comments << '# ' + Regexp.last_match[1] + ': ' + Regexp.last_match[2] +      end +      if line.match /\[Remarks\] (\S+)/ +        out << "remarks " + Regexp.last_match[1] +      end +      if line.match /\[RSSI LED\] (on|off)/ +        out << "rssiled " + Regexp.last_match[1] +      end +      if line.match /\[Speed\] (\d+) Mbps/ +        speed = Regexp.last_match[1] +      end +      if line.match /\[Tx MIR\] (\d+) Kbps/ +        out << "mir ".concat(Regexp.last_match[1]) +      end +      if line.match /\[Auto Rate Shift\] (on|off)/ +        out << "autorateshift ".concat(Regexp.last_match[1]) +        if Regexp.last_match[1].eql? 'off' +          out << "speed $speed" +        end +      end +      if line.match /\[IP\] (\S+) \[Subnet Mask\] (\S+) \[Gateway\] (\S+)/ +        out << "ipconfig " + Regexp.last_match[1] + ' ' + +                              Regexp.last_match[2] + ' ' + +                              Regexp.last_match[3] +      end +    end  +    comments.push(*out).join "\n" +  end  + +  cfg :telnet do +    password /Password:/ +    pre_logout 'exit' +  end +  +end diff --git a/lib/oxidized/node.rb b/lib/oxidized/node.rb index f2b125a..b13ce0e 100644 --- a/lib/oxidized/node.rb +++ b/lib/oxidized/node.rb @@ -128,24 +128,15 @@ module Oxidized      end      def resolve_auth opt -      # Resolve configured username/password, give priority to group level configuration -      # TODO: refactor to use revised behaviour of Asetus -      cfg_username, cfg_password = -        if Oxidized.config.groups.has_key?(@group) and ['username', 'password'].all? {|e| Oxidized.config.groups[@group].has_key?(e)} -          [Oxidized.config.groups[@group].username, Oxidized.config.groups[@group].password] -        elsif ['username', 'password'].all? {|e| Oxidized.config.has_key?(e)} -          [Oxidized.config.username, Oxidized.config.password] -        else -          [nil, nil] -        end -      auth = {} -      auth[:username] = (opt[:username] or cfg_username) -      auth[:password] = (opt[:password] or cfg_password) -      auth +      # Resolve configured username/password +      { +        username:       resolve_key(:username, opt), +        password:       resolve_key(:password, opt), +      }      end      def resolve_input opt -      inputs = (opt[:input]  or Oxidized.config.input.default) +      inputs = resolve_key :input, opt, Oxidized.config.input.default        inputs.split(/\s*,\s*/).map do |input|          if not Oxidized.mgr.input[input]            Oxidized.mgr.add_input input or raise MethodNotFound, "#{input} not found for node #{ip}" @@ -155,7 +146,7 @@ module Oxidized      end      def resolve_output opt -      output = (opt[:output] or Oxidized.config.output.default) +      output = resolve_key :output, opt, Oxidized.config.output.default        if not Oxidized.mgr.output[output]          Oxidized.mgr.add_output output or raise MethodNotFound, "#{output} not found for node #{ip}"        end @@ -163,7 +154,7 @@ module Oxidized      end      def resolve_model opt -      model = (opt[:model] or Oxidized.config.model) +      model = resolve_key :model, opt        if not Oxidized.mgr.model[model]          Oxidized.logger.debug "lib/oxidized/node.rb: Loading model #{model.inspect}"          Oxidized.mgr.add_model model or raise ModelNotFound, "#{model} not found for node #{ip}" @@ -187,6 +178,33 @@ module Oxidized        end      end +    def resolve_key key, opt, global=nil +      # resolve key, first get global, then get group then get node config +      key_sym = key.to_sym +      key_str = key.to_s +      value   = global +      Oxidized.logger.debug "node.rb: resolving node key '#{key}', with passed global value of '#{value}' and node value '#{opt[key_sym]}'" + +      #global +      if not value and Oxidized.config.has_key?(key_str) +        value = Oxidized.config[key_str] +        Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from global" +      end + +      #group +      if Oxidized.config.groups.has_key?(@group) +        if Oxidized.config.groups[@group].has_key?(key_str) +          value = Oxidized.config.groups[@group][key_str] +          Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from group" +        end +      end + +      #node +      value = opt[key_sym] || value +      Oxidized.logger.debug "node.rb: returning node key '#{key}' with value '#{value}'" +      value +    end +      def is_git? opt        (opt[:output] || Oxidized.config.output.default) == 'git'      end diff --git a/lib/oxidized/output/file.rb b/lib/oxidized/output/file.rb index bb13827..45f72e1 100644 --- a/lib/oxidized/output/file.rb +++ b/lib/oxidized/output/file.rb @@ -17,7 +17,7 @@ class OxidizedFile < Output    end    def store node, outputs, opt={} -    file = @cfg.directory +    file = File.expand_path @cfg.directory      if opt[:group]        file = File.join File.dirname(file), opt[:group]      end @@ -28,18 +28,22 @@ class OxidizedFile < Output    end    def fetch node, group -    cfg_dir = @cfg.directory +    cfg_dir   = File.expand_path @cfg.directory +    node_name = node.name +      if group # group is explicitly defined by user -      IO.readlines File.join(cfg_dir, group, node) +      cfg_dir = File.join File.dirname(cfg_dir), group +      File.read File.join(cfg_dir, node_name)      else -      if File.exists? File.join(cfg_dir, node) # node configuration file is stored on base directory -        IO.readlines File.join(cfg_dir, node) +      if File.exists? File.join(cfg_dir, node_name) # node configuration file is stored on base directory +        File.read File.join(cfg_dir, node_name)        else -        path = Dir.glob File.join(cfg_dir, '**', node) # fetch node in all groups -        return nil if path[0].nil? -        open(path[0], 'r').readlines +        path = Dir.glob(File.join(File.dirname(cfg_dir), '**', node_name)).first # fetch node in all groups +        File.read path        end      end +  rescue Errno::ENOENT +    return nil    end    def version node, group diff --git a/lib/oxidized/source/csv.rb b/lib/oxidized/source/csv.rb index a0ce848..d498e0b 100644 --- a/lib/oxidized/source/csv.rb +++ b/lib/oxidized/source/csv.rb @@ -20,18 +20,20 @@ class CSV < Source      nodes = []      open(File.expand_path @cfg.file).each_line do |line|        next if line.match(/^\s*#/) -      data  = line.chomp.split @cfg.delimiter +      data  = line.chomp.split(@cfg.delimiter, -1)        next if data.empty?        # map node parameters        keys = {}        @cfg.map.each do |key, position| -        keys[key.to_sym] = data[position] +        keys[key.to_sym] = node_var_interpolate data[position]        end        keys[:model] = map_model keys[:model] if keys.key? :model -      # map node specific vars, empty value is considered as nil +      # map node specific vars        vars = {} -      @cfg.vars_map.each { |key, position| vars[key.to_sym] = data[position].to_s.empty? ? nil : data[position] } +      @cfg.vars_map.each do |key, position| +        vars[key.to_sym] = node_var_interpolate data[position] +      end        keys[:vars] = vars unless vars.empty?        nodes << keys diff --git a/lib/oxidized/source/http.rb b/lib/oxidized/source/http.rb index 93361a2..4fd388b 100644 --- a/lib/oxidized/source/http.rb +++ b/lib/oxidized/source/http.rb @@ -20,6 +20,7 @@ class HTTP < Source      uri = URI.parse(@cfg.url)      http = Net::HTTP.new(uri.host, uri.port)      http.use_ssl = true if uri.scheme == 'https' +    http.verify_mode = OpenSSL::SSL::VERIFY_NONE unless @cfg.secure      # map headers      headers = {} @@ -39,13 +40,15 @@ class HTTP < Source        # map node parameters        keys = {}        @cfg.map.each do |key, position| -        keys[key.to_sym] = line[position] +        keys[key.to_sym] = node_var_interpolate line[position]        end        keys[:model] = map_model keys[:model] if keys.key? :model -      # map node specific vars, empty value is considered as nil +      # map node specific vars        vars = {} -      @cfg.vars_map.each { |key, position| vars[key.to_sym] = line[position].to_s.empty? ? nil : line[position] } +      @cfg.vars_map.each do |key, position| +        vars[key.to_sym] = node_var_interpolate line[position] +      end        keys[:vars] = vars unless vars.empty?        nodes << keys diff --git a/lib/oxidized/source/source.rb b/lib/oxidized/source/source.rb index 7862dd1..9b8bc94 100644 --- a/lib/oxidized/source/source.rb +++ b/lib/oxidized/source/source.rb @@ -1,11 +1,23 @@  module Oxidized    class Source      class NoConfig < OxidizedError; end +      def initialize        @map = (Oxidized.config.model_map or {})      end +      def map_model model        @map.has_key?(model) ? @map[model] : model      end + +    def node_var_interpolate var +       case var +       when "nil"   then nil +       when "false" then false +       when "true"  then true +       else var +       end +    end +    end  end diff --git a/lib/oxidized/source/sql.rb b/lib/oxidized/source/sql.rb index fc1caa8..13fc39b 100644 --- a/lib/oxidized/source/sql.rb +++ b/lib/oxidized/source/sql.rb @@ -26,12 +26,14 @@ class SQL < Source      query.each do |node|        # map node parameters        keys = {} -      @cfg.map.each { |key, sql_column| keys[key.to_sym] = node[sql_column.to_sym] } +      @cfg.map.each { |key, sql_column| keys[key.to_sym] = node_var_interpolate node[sql_column.to_sym] }        keys[:model] = map_model keys[:model] if keys.key? :model        # map node specific vars        vars = {} -      @cfg.vars_map.each { |key, sql_column| vars[key.to_sym] = node[sql_column.to_sym] } +      @cfg.vars_map.each do |key, sql_column| +        vars[key.to_sym] = node_var_interpolate node[sql_column.to_sym] +      end        keys[:vars] = vars unless vars.empty?        nodes << keys diff --git a/lib/oxidized/version.rb b/lib/oxidized/version.rb index 54defae..073aae9 100644 --- a/lib/oxidized/version.rb +++ b/lib/oxidized/version.rb @@ -1,3 +1,3 @@  module Oxidized -  VERSION = '0.16.3' +  VERSION = '0.19.0'  end | 
