diff options
37 files changed, 884 insertions, 99 deletions
| diff --git a/CHANGELOG.md b/CHANGELOG.md index b59e03d..cea6fb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,39 @@ +# 0.14.3 +- BUGFIX: fix git when using multiple groups without single_repo + +# 0.14.2 +- BUGFIX: git expand path for all groups +- BUGFIX: git get_version, teletubbies do it again +- BUGFIX: comware, acos, procurve models + +# 0.14.1 +- BUGFIX: git get_version when groups and single_repo are used + +# 0.14.0 +- FEATURE: support supermicro swithes (by @funzoneq) +- FEATURE: support catos switches +- BUGFIX: git+groups+singlerepo (by @PANZERBARON) +- BUGFIX: asa, tmos, ironware, ios-xr +- BUGFIX: mandate net-ssh 3.0.x, don't accept 3.1 (numerous issues) + +# 0.13.1 +- BUGFIX: file permissions (Sigh...) + +# 0.13.0 +- FEATURE: http post for configs (by @jgroom33) +- FEATURE: support ericsson redbacks (by @roedie) +- FEATURE: support motorola wireless controllers (by @roadie) +- FEATURE: support citrix netscaler (by @roadie) +- FEATURE: support datacom devices (by @danilopopeye) +- FEATURE: support netonix devices +- FEATURE: support specifying ssh cipher and kex (by @roadie) +- FEATURE: rename proxy to ssh_proxy (by @roadie) +- FEATURE: support ssh keys on ssh_proxy (by @awix) +- BUGFIX: various (by @danilopopeye) +- BUGFIX: Node#repo with groups (by @danilopopeye) +- BUGFIX: githubrepohoook (by @danilopopeye) +- BUGFIX: fortios, airos, junos, xos, edgeswitch, nos, tmos, procurve, ipos models +  # 0.12.2  - BUGFIX: more MRV model fixes (by @natm) @@ -12,7 +48,7 @@  - FEATURE: EdgeSwitch support (by @doogieconsulting)  - BUGFIX: rename input debug log files  - BUGFIX: powerconnect model fixes (by @Madpilot0) -- BUGFIX: fortigate model fixes (by @ElvinEfendi)  +- BUGFIX: fortigate model fixes (by @ElvinEfendi)  - BUGFIX: various (by @mikebryant)  - BUGFIX: write SSH debug to file without buffering  - BUGFIX: fix IOS XR prompt handling @@ -31,6 +31,7 @@ Oxidized is a network device configuration backup tool. It's a RANCID replacemen      * [Source: SQLite](#source-sqlite)      * [Source: HTTP](#source-http)      * [Output: GIT](#output-git) +    * [Output: HTTP](#output-http)      * [Output: File](#output-file)      * [Output types](#output-types)      * [Advanced Configuration](#advanced-configuration) @@ -66,17 +67,25 @@ Oxidized is a network device configuration backup tool. It's a RANCID replacemen   * Cisco     * AireOS     * ASA +   * CatOS     * IOS     * IOSXR     * NXOS     * SMB (Nikola series) + * Citrix +   * NetScaler (Virtual Applicance)   * Cumulus     * Linux + * DataCom +   * DmSwitch 3000   * DELL     * PowerConnect     * AOSW + * Ericsson/Redback +   * IPOS (former SEOS)   * Extreme Networks     * XOS +   * WM   * F5     * TMOS   * Force10 @@ -96,12 +105,18 @@ Oxidized is a network device configuration backup tool. It's a RANCID replacemen     * MLNX-OS   * Mikrotik     * RouterOS + * Motorola +   * RFS   * MRV     * MasterOS + * Netonix +   * WISP Switch (As Netonix)   * Opengear     * Opengear   * Palo Alto     * PANOS + * Supermicro +   * Supermicro   * Ubiquiti     * AirOS     * Edgeos @@ -115,7 +130,7 @@ Oxidized is a network device configuration backup tool. It's a RANCID replacemen  Install all required packages and gems.  ```shell -apt-get install ruby ruby-dev libsqlite3-dev libssl-dev pkg-config cmake +apt-get install ruby ruby-dev libsqlite3-dev libssl-dev pkg-config cmake libssh2-1-dev  gem install oxidized  gem install oxidized-script oxidized-web # if you don't install oxidized-web, make sure you remove "rest" from your config  ``` @@ -123,7 +138,7 @@ gem install oxidized-script oxidized-web # if you don't install oxidized-web, ma  ## CentOS, Oracle Linux, Red Hat Linux version 6  Install Ruby 1.9.3 or greater (for Ruby 2.1.2 installation instructions see "Installing Ruby 2.1.2 using RVM"), then install Oxidized dependencies  ```shell -yum install cmake sqlite-devel openssl-devel +yum install cmake sqlite-devel openssl-devel libssh2-devel  ```  Now lets install oxidized via Rubygems: @@ -138,6 +153,21 @@ Oxidized configuration is in YAML format. Configuration files are subsequently s  To initialize a default configuration in your home directory ```~/.config/oxidized/config```, simply run ```oxidized``` once. If you don't further configure anything from the output and source sections, it'll extend the examples on a subsequent ```oxidized``` execution. This is useful to see what options for a specific source or output backend are available. +You can set the env variable `OXIDIZED_HOME` to change its home directory. + +``` +OXIDIZED_HOME=/etc/oxidized + +$ tree -L 1 /etc/oxidized +/etc/oxidized/ +├── config +├── log-router-ssh +├── log-router-telnet +├── pid +├── router.db +└── repository.git +``` +  ## Source  Oxidized supports ```CSV```, ```SQLite``` and ```HTTP``` as source backends. The CSV backend reads nodes from a rancid compatible router.db file. The SQLite backend will fire queries against a database and map certain fields to model items. The HTTP backend will fire queries against a http/https url. Take a look at the [Cookbook](#cookbook) for more details. @@ -183,7 +213,7 @@ Install Ruby 2.1.2 build dependencies  ```  yum install curl gcc-c++ patch readline readline-devel zlib zlib-devel  yum install libyaml-devel libffi-devel openssl-devel make cmake -yum install bzip2 autoconf automake libtool bison iconv-devel +yum install bzip2 autoconf automake libtool bison iconv-devel libssh2-devel  ```  Install RVM @@ -264,6 +294,30 @@ vars:     enable: S3cre7  ``` +### Removing secrets + +To strip out secrets from configurations before storing them, Oxidized needs the the remove_secrets flag. You can globally enable this by adding the following snippet to the global sections of the configuration file. + +``` +vars: +  remove_secret: true +``` + +Device models can contain substitution filters to remove potentially sensitive data from configs. + +As a partial example from ios.rb: + +```   +  cmd :secret do |cfg| +    cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>' +    (...)     +    cfg +  end +``` +The above strips out snmp community strings from your saved configs. + +**NOTE:** Removing secrets reduces the usefulness as a full configuration backup, but it may make sharing configs easier. +  ### Source: CSV  One line per device, colon seperated. @@ -285,7 +339,7 @@ source:  ### SSH Proxy Command -Oxidized can `ssh` through a proxy as well. To do so we just need to set `proxy` variable. +Oxidized can `ssh` through a proxy as well. To do so we just need to set `ssh_proxy` variable.  ```  ... @@ -294,7 +348,7 @@ map:    model: 1  vars_map:    enable: 2 -  proxy: 3 +  ssh_proxy: 3  ...  ``` @@ -356,13 +410,67 @@ output:  This uses the rugged/libgit2 interface. So you should remember that normal Git hooks will not be executed. + +For a single repositories for all devices: + +``` yaml +output: +  default: git +  git: +    user: Oxidized +    email: o@example.com +    repo: "/var/lib/oxidized/devices.git"  ``` + +And for groups repositories: + +``` yaml  output:    default: git    git:      user: Oxidized      email: o@example.com +    repo: "/var/lib/oxidized/git-repos/default.git" +``` + +Oxidized will create a repository for each group in the same directory as the `default.git`. For +example: + +``` csv +host1:ios:first +host2:nxos:second +``` + +This will generate the following repositories: + +``` bash +$ ls /var/lib/oxidized/git-repos + +default.git first.git second.git +``` + +If you would like to use groups and a single repository, you can force this with the `single_repo` config. + +``` yaml +output: +  default: git +  git: +    single_repo: true      repo: "/var/lib/oxidized/devices.git" + +``` + +### Output: Http + +POST a config to the specified URL + +``` +output: +  default: http +  http: +    user: admin +    password: changeit +    url: "http://192.168.162.50:8080/db/coll"  ```  ### Output types @@ -445,6 +553,7 @@ vars:    enable: S3cr3tx  groups: {}  rest: 127.0.0.1:8888 +pid: ~/.config/oxidized/oxidized.pid  input:    default: ssh, telnet    debug: false @@ -527,6 +636,42 @@ hooks:      timeout: 120  ``` +### githubrepo + +This hook configures the repository `remote` and _push_ the code when the specified event is triggerd. If the `username` and `password` are not provided, the `Rugged::Credentials::SshKeyFromAgent` will be used. + +`githubrepo` hook recognizes following configuration keys: + +  * `remote_repo`: the remote repository to be pushed to. +  * `username`: username for repository auth. +  * `password`: password for repository auth. +  * `publickey`: publickey for repository auth. +  * `privatekey`: privatekey for repository auth. + +When using groups repositories, each group must have its own `remote` in the `remote_repo` config. + +``` yaml +hooks: +  push_to_remote: +    remote_repo: +      routers: git@git.intranet:oxidized/routers.git +      switches: git@git.intranet:oxidized/switches.git +      firewalls: git@git.intranet:oxidized/firewalls.git +``` + + +## Hook configuration example + +``` yaml +hooks: +  push_to_remote: +    type: githubrepo +    events: [post_store] +    remote_repo: git@git.intranet:oxidized/test.git +    username: user +    password: pass +``` +  # Ruby API  The following objects exist in Oxidized. diff --git a/extra/nagios_check_failing_nodes.rb b/extra/nagios_check_failing_nodes.rb index abb34ba..abb34ba 100644..100755 --- a/extra/nagios_check_failing_nodes.rb +++ b/extra/nagios_check_failing_nodes.rb diff --git a/extra/oxidized.init b/extra/oxidized.init index 197c5b1..197c5b1 100644..100755 --- a/extra/oxidized.init +++ b/extra/oxidized.init diff --git a/extra/oxidized.init.d b/extra/oxidized.init.d new file mode 100755 index 0000000..d2fdf00 --- /dev/null +++ b/extra/oxidized.init.d @@ -0,0 +1,87 @@ +#!/bin/sh +# chkconfig: - 99 01 +# description: Oxidized - Network Device Configuration Backup Tool +# processname: /opt/ruby-2.1/bin/oxidized + +# Source function library +. /etc/rc.d/init.d/functions + +name="oxidized" +desc="Oxidized" +cmd=oxidized +args="--daemonize" +lockfile=/var/lock/subsys/$name +pidfile=/etc/oxidized/pid + +export OXIDIZED_HOME=/etc/oxidized + +# Source sysconfig configuration +[ -r /etc/sysconfig/$name ] && . /etc/sysconfig/$name + +start() { +    echo -n $"Starting $desc: " +    daemon ${cmd} ${args} +    retval=$? +    if [ $retval = 0 ] +    then +        echo_success +        touch $lockfile +    else +        echo_failure +    fi +    echo +    return $retval +} + +stop() { +    echo -n $"Stopping $desc: " +    killproc -p $pidfile +    retval=$? +    [ $retval -eq 0 ] && rm -f $lockfile +    rm -f $pidfile +    echo +    return $retval +} + +restart() { +    stop +    start +} + +reload() { +  echo -n $"Reloading config..." +  curl -s http://localhost:8888/reload?format=json -O /dev/null +  echo +} + +rh_status() { +    status -p $pidfile $cmd +} + +rh_status_q() { +    rh_status >/dev/null 2>&1 +} + +case "$1" in +    start) +        rh_status_q && exit 0 +        $1 +        ;; +    stop) +        rh_status_q || exit 0 +        $1 +        ;; +    restart) +        $1 +        ;; +    reload) +        rh_status_q || exit 0 +        $1 +        ;; +    status) +        rh_status +        ;; +    *) +        echo $"Usage: $0 {start|stop|restart|reload|status}" +        exit 2 +esac diff --git a/extra/syslog.rb b/extra/syslog.rb index e364cf9..e364cf9 100644..100755 --- a/extra/syslog.rb +++ b/extra/syslog.rb diff --git a/lib/oxidized/cli.rb b/lib/oxidized/cli.rb index 0594dcb..9a09d41 100644 --- a/lib/oxidized/cli.rb +++ b/lib/oxidized/cli.rb @@ -24,7 +24,7 @@ module Oxidized        Config.load(@opts)        Oxidized.setup_logger -      @pidfile = File.join(Oxidized::Config::Root, 'pid') +      @pidfile = File.expand_path(Oxidized.config.pid)      end      def crash error diff --git a/lib/oxidized/config.rb b/lib/oxidized/config.rb index d2d12d8..1797be6 100644 --- a/lib/oxidized/config.rb +++ b/lib/oxidized/config.rb @@ -3,7 +3,7 @@ module Oxidized    class NoConfig < OxidizedError; end    class InvalidConfig < OxidizedError; end    class Config -    Root      = File.join ENV['HOME'], '.config', 'oxidized' +    Root      = ENV['OXIDIZED_HOME'] || File.join(ENV['HOME'], '.config', 'oxidized')      Crash     = File.join Root, 'crash'      Log       = File.join Root, 'log'      InputDir  = File.join Directory, %w(lib oxidized input) @@ -27,9 +27,10 @@ module Oxidized        asetus.default.timeout       = 20        asetus.default.retries       = 3        asetus.default.prompt        = /^([\w.@-]+[#>]\s?)$/ -        asetus.default.rest          = '127.0.0.1:8888' # or false to disable +      asetus.default.rest          = '127.0.0.1:8888' # or false to disable        asetus.default.vars          = {}             # could be 'enable'=>'enablePW'        asetus.default.groups        = {}             # group level configuration +      asetus.default.pid           = File.join(Oxidized::Config::Root, 'pid')        asetus.default.input.default    = 'ssh, telnet'        asetus.default.input.debug      = false # or String for session log file diff --git a/lib/oxidized/hook/githubrepo.rb b/lib/oxidized/hook/githubrepo.rb index d10b51e..d33e54e 100644 --- a/lib/oxidized/hook/githubrepo.rb +++ b/lib/oxidized/hook/githubrepo.rb @@ -1,12 +1,12 @@  class GithubRepo < Oxidized::Hook    def validate_cfg! -    cfg.has_key?('remote_repo') or raise KeyError, 'remote_repo is required' +    raise KeyError, 'hook.remote_repo is required' unless cfg.has_key?('remote_repo')    end    def run_hook(ctx) -    repo = Rugged::Repository.new(Oxidized.config.output.git.repo) +    repo = Rugged::Repository.new(ctx.node.repo)      log "Pushing local repository(#{repo.path})..." -    remote = repo.remotes['origin'] || repo.remotes.create('origin', cfg.remote_repo) +    remote = repo.remotes['origin'] || repo.remotes.create('origin', remote_repo(ctx.node))      log "to remote: #{remote.url}"      fetch_and_merge_remote(repo) @@ -49,9 +49,21 @@ class GithubRepo < Oxidized::Hook        log "Using https auth", :debug        Rugged::Credentials::UserPassword.new(username: cfg.username, password: cfg.password)      else -      log "Using ssh auth", :debug -      Rugged::Credentials::SshKeyFromAgent.new(username: 'git') +      if cfg.has_key?('publickey') && cfg.has_key?('privatekey') +        log "Using ssh auth with key", :debug +        Rugged::Credentials::SshKey.new(username: 'git', publickey: File.expand_path(cfg.publickey), privatekey: File.expand_path(cfg.privatekey)) +      else +        log "Using ssh auth with agentforwarding", :debug +        Rugged::Credentials::SshKeyFromAgent.new(username: 'git') +      end      end    end +  def remote_repo(node) +    if node.group.nil? || cfg.remote_repo.is_a?(String) +      cfg.remote_repo +    else +      cfg.remote_repo[node.group] +    end +  end  end diff --git a/lib/oxidized/input/ssh.rb b/lib/oxidized/input/ssh.rb index 7ffdd36..fef20d6 100644 --- a/lib/oxidized/input/ssh.rb +++ b/lib/oxidized/input/ssh.rb @@ -23,8 +23,8 @@ module Oxidized        secure = Oxidized.config.input.ssh.secure        @log = File.open(Oxidized::Config::Log + "-#{@node.ip}-ssh", 'w') if Oxidized.config.input.debug?        port = vars(:ssh_port) || 22 -      if proxy_host = vars(:proxy) -        proxy =  Net::SSH::Proxy::Command.new("ssh #{proxy_host} nc %h %p") +      if proxy_host = vars(:ssh_proxy) +        proxy =  Net::SSH::Proxy::Command.new("ssh #{proxy_host} -W %h:%p")        end        ssh_opts = {          :port => port.to_i, @@ -92,7 +92,7 @@ module Oxidized            @output << data            @output = @node.model.expects @output          end -        ch.request_pty (opts={:term=>'vt100'}) do |_ch, success_pty| +        ch.request_pty (_opts={:term=>'vt100'}) 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 diff --git a/lib/oxidized/model/acos.rb b/lib/oxidized/model/acos.rb index 75fbacf..7db8b00 100644 --- a/lib/oxidized/model/acos.rb +++ b/lib/oxidized/model/acos.rb @@ -1,5 +1,5 @@  class ACOS < Oxidized::Model -	# A10 ACOS model for AX and Thunder series +  # A10 ACOS model for AX and Thunder series    comment  '! ' @@ -17,10 +17,14 @@ class ACOS < Oxidized::Model    cmd 'show running-config all-partitions'    cmd 'show aflex all-partitions' do |cfg| +    comment cfg +  end + +  cmd 'show aflex all-partitions' do |cfg|      @partitions_aflex = cfg.lines.each_with_object({}) do |l,h|        h[$1] = [] if l.match /partition: (.+)/        # only consider scripts that have passed syntax check -      h[h.keys.last] << $1 if l.match /^([\w-]+) +Check/   +      h[h.keys.last] << $1 if l.match /^([\w-]+) +Check/      end      ''    end @@ -52,18 +56,18 @@ class ACOS < Oxidized::Model      username  /login:/      password  /^Password:/    end - +      cfg :telnet, :ssh do      # preferred way to handle additional passwords -    if vars :enable -      post_login do -        send "enable\n" -        send vars(:enable) + "\n" -      end +    post_login do +      pw = vars(:enable) +      pw ||= "" +      send "enable\r\n" +      cmd pw      end      post_login 'terminal length 0'      post_login 'terminal width 0' -    pre_logout "exit\nexit\ny" +    pre_logout "exit\nexit\nY\r\n"    end  end diff --git a/lib/oxidized/model/asa.rb b/lib/oxidized/model/asa.rb index 547afd7..48e6bf4 100644 --- a/lib/oxidized/model/asa.rb +++ b/lib/oxidized/model/asa.rb @@ -18,7 +18,7 @@ class ASA < Oxidized::Model    cmd 'show version' do |cfg|      # avoid commits due to uptime / ixo-router01 up 2 mins 28 secs / ixo-router01 up 1 days 2 hours -    cfg = cfg.each_line.select { |line| not line.match /\s+up\s+\d+\s+/ } +    cfg = cfg.each_line.select { |line| not line.match /(\s+up\s+\d+\s+)|(.*days.*)/ }      cfg = cfg.join      comment cfg    end @@ -30,6 +30,22 @@ class ASA < Oxidized::Model    cmd 'more system:running-config' do |cfg|      cfg = cfg.each_line.to_a[3..-1].join      cfg.gsub! /^: [^\n]*\n/, '' +    # backup any xml referenced in the configuration. +    anyconnect_profiles = cfg.scan(Regexp.new('(\sdisk0:/.+\.xml)')).flatten +    anyconnect_profiles.each do |profile| +	cfg << (comment profile + "\n" ) +    	cmd ("more" + profile) do |xml| +	  cfg << (comment xml) +	end +    end +    # if DAP is enabled, also backup dap.xml +    if cfg.rindex(/dynamic-access-policy-record\s(?!DfltAccessPolicy)/) +   	cfg << (comment "disk0:/dap.xml\n") +        cmd "more disk0:/dap.xml" do |xml| +          cfg << (comment xml) +	  puts xml +        end +    end      cfg    end diff --git a/lib/oxidized/model/catos.rb b/lib/oxidized/model/catos.rb new file mode 100644 index 0000000..874ebbc --- /dev/null +++ b/lib/oxidized/model/catos.rb @@ -0,0 +1,36 @@ +class Catos < Oxidized::Model + +  prompt /^[\w.@-]+> \(enable\) $/ +  comment '# ' + +  cmd :all do |cfg| +    cfg.each_line.to_a[1..-2].join +  end + +  cmd 'show system' do |cfg| +    cfg = cfg.gsub /(\s+)\d+,\d+:\d+:\d+(\s+)/, '\1X\2' +    comment cfg +  end + +  cmd 'show version' do |cfg| +    cfg = cfg.gsub /\d+(K)/, 'X\1' +    cfg = cfg.gsub /^(Uptime is ).*/, '\1X' +    comment cfg +  end + +  cmd 'show conf all' do |cfg| +    cfg = cfg.sub /^(#time: ).*/, '\1X' +    cfg.each_line.drop_while { |line| not line.match /^begin/ }.join +  end + +  cfg :telnet do +    username /^Username: / +    password /^Password:/ +  end + +  cfg :ssh, :telnet do +    post_login 'set length 0' +    pre_logout 'exit' +  end + +end diff --git a/lib/oxidized/model/comware.rb b/lib/oxidized/model/comware.rb index 9b36e8b..27b70ae 100644 --- a/lib/oxidized/model/comware.rb +++ b/lib/oxidized/model/comware.rb @@ -13,6 +13,8 @@ class Comware < Oxidized::Model    cmd :all do |cfg|      #cfg.gsub! /^.*\e\[42D/, ''        # example how to handle pager +    #skip rogue ^M +    cfg = cfg.gsub /\r/, ''      cfg.each_line.to_a[1..-2].join    end diff --git a/lib/oxidized/model/datacom.rb b/lib/oxidized/model/datacom.rb new file mode 100644 index 0000000..54091ed --- /dev/null +++ b/lib/oxidized/model/datacom.rb @@ -0,0 +1,33 @@ +class DataCom < Oxidized::Model + +  comment '! ' + +  expect /^--More--\s+$/ do |data, re| +    send ' ' +    data.sub re, '' +  end + +  cmd :all do |cfg| +    cfg.each_line.to_a[1..-2].join +    cfg.cut_head.cut_tail +  end + +  cmd 'show firmware' do |cfg| +    comment cfg +  end + +  cmd 'show system' do |cfg| +    comment cfg +  end + +  cmd 'show running-config' do |cfg| +    cfg.cut_head +  end + +  cfg :telnet, :ssh do +    username /login:\s$/ +    password /^Password:\s$/ +    pre_logout 'exit' +  end + +end diff --git a/lib/oxidized/model/edgeswitch.rb b/lib/oxidized/model/edgeswitch.rb index 89a5690..7c82639 100644 --- a/lib/oxidized/model/edgeswitch.rb +++ b/lib/oxidized/model/edgeswitch.rb @@ -4,22 +4,29 @@ class EdgeSwitch < Oxidized::Model    comment '!' -  prompt /[(]\w*\s\w*[)][\s#>]*[\s#>]/ +  prompt /\(.*\)\s[#>]/    cmd 'show running-config' do |cfg| -    comment cfg.each_line.reject { |line| line.match /System Up Time.*/ or line.match /Current SNTP Synchronized Time.*/ }.join +    cfg.each_line.to_a[2..-2].reject { |line| line.match /System Up Time.*/ or line.match /Current SNTP Synchronized Time.*/ }.join    end    cfg :telnet do      username /Username:\s/ -    passsword /^Password:\s/ +    password /^Password:\s/    end    cfg :telnet, :ssh do -    post_login 'enable' -    post_login 'terminal length 0' -    pre_logout 'exit' -    pre_logout 'exit' +    post_login do +      if vars :enable +        send "enable\n" +        cmd vars(:enable) +      else +        cmd 'enable' +      end +      cmd 'terminal length 0' +    end +    pre_logout 'quit' +    pre_logout 'n'    end -end
\ No newline at end of file +end diff --git a/lib/oxidized/model/fortios.rb b/lib/oxidized/model/fortios.rb index 92add0e..aad3a6e 100644 --- a/lib/oxidized/model/fortios.rb +++ b/lib/oxidized/model/fortios.rb @@ -2,7 +2,7 @@ class FortiOS < Oxidized::Model    comment  '# ' -  prompt /^([-\w\.]+(\s[\(\w\-\.\)]+)?\~?\s?[#>]\s?)$/ +  prompt /^([-\w\.]+(\s[\(\w\-\.\)]+)?\~?\s?[#>$]\s?)$/    expect /^--More--\s$/ do |data, re|      send ' ' diff --git a/lib/oxidized/model/iosxr.rb b/lib/oxidized/model/iosxr.rb index a622b66..bf01140 100644 --- a/lib/oxidized/model/iosxr.rb +++ b/lib/oxidized/model/iosxr.rb @@ -30,7 +30,7 @@ class IOSXR < Oxidized::Model    cfg :telnet do      username /^Username:/ -    password /^Password:/ +    password /^\r?Password:/    end    cfg :telnet, :ssh do diff --git a/lib/oxidized/model/ipos.rb b/lib/oxidized/model/ipos.rb new file mode 100644 index 0000000..5efd831 --- /dev/null +++ b/lib/oxidized/model/ipos.rb @@ -0,0 +1,61 @@ +class IPOS < Oxidized::Model + +  # Ericsson SSR (IPOS) +  # Redback SE (SEOS) + +  prompt /^([\[\]\w.@-]+[#>]\s?)$/ +  comment '! ' + +  cmd 'show chassis' do |cfg| +    comment cfg.each_line.to_a[0..-2].join +  end + +  cmd 'show hardware' do |cfg| +    comment cfg.each_line.to_a[0..-2].join +  end + +  cmd 'show release' do |cfg| +    comment cfg.each_line.to_a[0..-2].join +  end + +  cmd 'show configuration' do |cfg| +    # SEOS regularly adds some odd line breaks in random places +    # when showing the config, triggering changes. +    cfg.gsub! "\r\n", "\n" + +    cfg = cfg.each_line.to_a + +    # Keeps the issued command commented but removes the uncommented "Building configuration..." +    # and "Current configuration:" lines as well as the last prompt at the end. +    cfg = cfg[4..-2].unshift comment cfg[0] + +    # Later IPOS releases add this line in addition to the usual "last changed" line. +    # It's touched regularly (as often as multiple times per minute) by the OS without actual visible config changes. +    cfg = cfg.reject { |line| line.match "Configuration last changed by system user" } + +    # Earlier IPOS releases lack the "changed by system user" line and instead overwrite +    # the single "last changed by user" line. Because the line has a timestamp it will +    # trigger constant changes if not removed. By doing so there will only be a single +    # extra change trigged after an actual config change by a user but still have the +    # real user. +    cfg = cfg.reject { |line| line.match "Configuration last changed by user '%LICM%' at" } +    cfg = cfg.reject { |line| line.match "Configuration last changed by user '<NO USER>' at" } +    cfg = cfg.reject { |line| line.match "Configuration last changed by user '' at" } + +    cfg.join +  end + +  cfg :telnet do +    username /^login:/ +    password /^\r*password:/ +  end + +  cfg :telnet, :ssh do +    post_login 'terminal length 0' +    pre_logout do +      send "exit\n" +      send "n\n" +    end +  end + +end diff --git a/lib/oxidized/model/ironware.rb b/lib/oxidized/model/ironware.rb index 55b6e57..1e8c30e 100644 --- a/lib/oxidized/model/ironware.rb +++ b/lib/oxidized/model/ironware.rb @@ -23,6 +23,7 @@ class IronWare < Oxidized::Model    cmd 'show version' do |cfg|      cfg.gsub! /(^((.*)[Ss]ystem uptime(.*))$)/, '' #remove unwanted line system uptime +    cfg.gsub! /(^((.*)[Tt]he system started at(.*))$)/, ''      cfg.gsub! /[Uu]p\s?[Tt]ime is .*/,''      comment cfg diff --git a/lib/oxidized/model/mtrlrfs.rb b/lib/oxidized/model/mtrlrfs.rb new file mode 100644 index 0000000..8baa4e9 --- /dev/null +++ b/lib/oxidized/model/mtrlrfs.rb @@ -0,0 +1,37 @@ +class Mtrlrfs < Oxidized::Model + +  # Motorola RFS/Extreme WM + +  comment  '# ' + +  cmd :all do |cfg| +    # xos inserts leading \r characters and other trailing white space. +    # this deletes extraneous \r and trailing white space. +    cfg.each_line.to_a[1..-2].map{|line|line.delete("\r").rstrip}.join("\n") + "\n" +  end + +  cmd 'show version' do |cfg| +    comment cfg +  end + +  cmd 'show licenses' do |cfg| +    comment cfg +  end + +  cmd 'show running-config' + +  cfg :telnet do +    username /^login:/ +    password /^\r*password:/ +  end + +  cfg :telnet, :ssh do +    post_login 'terminal length 0' +    pre_logout do +      send "exit\n" +      send "n\n" +    end +  end + +end + diff --git a/lib/oxidized/model/netonix.rb b/lib/oxidized/model/netonix.rb new file mode 100644 index 0000000..4624f83 --- /dev/null +++ b/lib/oxidized/model/netonix.rb @@ -0,0 +1,15 @@ +class Netonix < Oxidized::Model +  prompt /^[\w\s.@_\/:-]+#/ + +  cmd :all do |cfg| +    cfg.each_line.to_a[1..-2].join +  end + +  cmd 'cat config.json;echo' + +  cfg :ssh do +    post_login 'cmdline' +    pre_logout 'exit' +    pre_logout 'exit' +  end +end diff --git a/lib/oxidized/model/netscaler.rb b/lib/oxidized/model/netscaler.rb new file mode 100644 index 0000000..9ca66b6 --- /dev/null +++ b/lib/oxidized/model/netscaler.rb @@ -0,0 +1,24 @@ +class NetScaler < Oxidized::Model + +  prompt /^\>\s*$/ +  comment '# ' + +  cmd :all do |cfg| +    cfg.each_line.to_a[1..-3].join +  end + +  cmd 'show version' do |cfg| +    comment cfg +  end + +  cmd 'show hardware' do |cfg| +    comment cfg +  end + +  cmd 'show ns ns.conf' + +  cfg :ssh do +    pre_logout 'exit' +  end + +end diff --git a/lib/oxidized/model/nos.rb b/lib/oxidized/model/nos.rb index 18ca6a2..bd2cb0f 100644 --- a/lib/oxidized/model/nos.rb +++ b/lib/oxidized/model/nos.rb @@ -33,7 +33,7 @@ class NOS < Oxidized::Model    cfg :telnet do      username /^.* login: / -    username /^Password:/ +    password /^Password:/    end    cfg :telnet, :ssh do diff --git a/lib/oxidized/model/procurve.rb b/lib/oxidized/model/procurve.rb index 684a4b6..392f510 100644 --- a/lib/oxidized/model/procurve.rb +++ b/lib/oxidized/model/procurve.rb @@ -1,36 +1,49 @@  class Procurve < Oxidized::Model -  # FIXME: this is way too unsafe -  prompt /.*?(\w+# ).*/m +  # some models start lines with \r  +  # previous command is repeated followed by "\eE", which sometimes ends up on last line +  prompt /^\r?([\w -]+\eE)?([\w.-]+# )$/    comment  '! ' +  # replace all used vt100 control sequences +  expect /\e\[\??\d+(;\d+)*[A-Za-z]/ do |data, re| +    data.gsub re, '' +  end +    expect /Press any key to continue/ do -     send ' ' -     "" +    send ' ' +    ""    end    cmd :all do |cfg|      cfg = cfg.each_line.to_a[1..-3].join -    cfg = cfg.gsub /\r/, '' -    new_cfg = '' -    cfg.each_line do |line| -      line.sub! /^\e.*(\e.*)/, '\1'  #leave last escape -      line.sub! /\e\[24;1H/, ''      #remove last escape, is it always this? -      new_cfg << line -    end -    new_cfg +    cfg = cfg.gsub /^\r/, '' +  end + +  cmd :secret do |cfg| +    cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>' +    cfg.gsub! /^(snmp-server host).*/, '\\1 <configuration removed>' +    cfg.gsub! /^(radius-server host).*/, '\\1 <configuration removed>' +    cfg    end    cmd 'show version' do |cfg|      comment cfg    end +  # not supported on all models    cmd 'show system-information' do |cfg|      cfg = cfg.split("\n")[0..-8].join("\n")      comment cfg    end +  # not supported on all models +  cmd 'show system information' do |cfg| +    cfg = cfg.split("\n")[0..-8].join("\n") +    comment cfg +  end +    cmd 'show running-config'    cfg :telnet do diff --git a/lib/oxidized/model/supermicro.rb b/lib/oxidized/model/supermicro.rb new file mode 100644 index 0000000..361244c --- /dev/null +++ b/lib/oxidized/model/supermicro.rb @@ -0,0 +1,45 @@ +class Supermicro < Oxidized::Model +  comment  '! ' + +  cmd :secret do |cfg| +    cfg.gsub!(/password \d+ (\S+).*/, '<secret removed>') +    cfg.gsub!(/community (\S+)/, 'community <hidden>') +    cfg +  end + +  cmd :all do |cfg| +     cfg.each_line.to_a[2..-2].join +  end + +  cmd 'show running-config' + +  cmd 'show access-list tcam-utilization' do |cfg| +    comment cfg +  end + +  cmd 'show memory' do |cfg| +    comment cfg +  end + +  cmd 'show system' do |cfg| +    comment cfg +  end + +  cmd 'show version' do |cfg| +    comment cfg +  end + +  cmd 'show watchdog' do |cfg| +    comment cfg +  end + +  cfg :telnet do +    username /^Username:/ +    password /^Password:/ +  end + +  cfg :telnet, :ssh do +    post_login 'terminal length 0' +    pre_logout 'exit' +  end +end
\ No newline at end of file diff --git a/lib/oxidized/model/tmos.rb b/lib/oxidized/model/tmos.rb index 4841b98..390046d 100644 --- a/lib/oxidized/model/tmos.rb +++ b/lib/oxidized/model/tmos.rb @@ -3,18 +3,20 @@ class TMOS < Oxidized::Model    comment  '# '    cmd :secret do |cfg| -    cfg.gsub!(/password (\S+)/, 'password <secret removed>') -    cfg.gsub!(/passphrase (\S+)/, 'passphrase <secret removed>') -    cfg.gsub!(/community (\S+)/, 'community <secret removed>') -    cfg.gsub!(/community-name (\S+)/, 'community-name <secret removed>') +    cfg.gsub!(/^([\s\t]*)secret \S+/, '\1secret <secret removed>') +    cfg.gsub!(/^([\s\t]*\S*)password \S+/, '\1password <secret removed>') +    cfg.gsub!(/^([\s\t]*\S*)passphrase \S+/, '\1passphrase <secret removed>') +    cfg.gsub!(/community \S+/, 'community <secret removed>') +    cfg.gsub!(/community-name \S+/, 'community-name <secret removed>') +    cfg.gsub!(/^([\s\t]*\S*)encrypted \S+$/, '\1encrypted <secret removed>')      cfg    end -  cmd('tmsh show sys version') { |cfg| comment cfg } +  cmd('tmsh -q show sys version') { |cfg| comment cfg } -  cmd('tmsh show sys software') { |cfg| comment cfg } +  cmd('tmsh -q show sys software') { |cfg| comment cfg } -  cmd 'tmsh show sys hardware field-fmt' do |cfg| +  cmd 'tmsh -q show sys hardware field-fmt' do |cfg|      cfg.gsub!(/fan-speed (\S+)/, '')      cfg.gsub!(/temperature (\S+)/, '')      comment cfg @@ -22,25 +24,27 @@ class TMOS < Oxidized::Model    cmd('cat /config/bigip.license') { |cfg| comment cfg } -  cmd 'tmsh list' do |cfg| -    cfg.gsub!(/state (up|down)/, '') +  cmd 'tmsh -q list' do |cfg| +    cfg.gsub!(/state (up|down|checking|irule-down)/, '')      cfg.gsub!(/errors (\d+)/, '')      cfg    end -  cmd('tmsh list net route all') { |cfg| comment cfg } +  cmd('tmsh -q list net route all') { |cfg| comment cfg }    cmd('/bin/ls --full-time --color=never /config/ssl/ssl.crt') { |cfg| comment cfg }    cmd('/bin/ls --full-time --color=never /config/ssl/ssl.key') { |cfg| comment cfg } -  cmd 'tmsh show running-config sys db all-properties' do |cfg| +  cmd 'tmsh -q show running-config sys db all-properties' do |cfg|      cfg.gsub!(/sys db configsync.localconfigtime {[^}]+}/m, '')      cfg.gsub!(/sys db gtm.configtime {[^}]+}/m, '')      cfg.gsub!(/sys db ltm.configtime {[^}]+}/m, '')      comment cfg    end +  cmd('cat /config/partitions/*/bigip.conf') { |cfg| comment cfg } +    cfg :ssh do      exec true  # don't run shell, run each command in exec channel    end diff --git a/lib/oxidized/model/xos.rb b/lib/oxidized/model/xos.rb index de8ec39..6f1323f 100644 --- a/lib/oxidized/model/xos.rb +++ b/lib/oxidized/model/xos.rb @@ -36,8 +36,10 @@ class XOS < Oxidized::Model    cfg :telnet, :ssh do      post_login 'disable clipaging' -    pre_logout 'exit' -    pre_logout 'n' +    pre_logout do +      send "exit\n" +      send "n\n" +    end    end  end diff --git a/lib/oxidized/node.rb b/lib/oxidized/node.rb index 7a278a9..35bcad9 100644 --- a/lib/oxidized/node.rb +++ b/lib/oxidized/node.rb @@ -24,7 +24,7 @@ module Oxidized        @vars           = opt[:vars]        @stats          = Stats.new        @retry          = 0 -      @repo           = Oxidized.config.output.git.repo +      @repo           = resolve_repo        # model instance needs to access node instance        @model.node = self @@ -170,5 +170,15 @@ module Oxidized        Oxidized.mgr.model[model].new      end +    def resolve_repo +      remote_repo = Oxidized.config.output.git.repo + +      if Oxidized.config.output.git.single_repo? || @group.nil? || remote_repo.is_a?(String) +        remote_repo +      else +        remote_repo[@group] +      end +    end +    end  end diff --git a/lib/oxidized/output/git.rb b/lib/oxidized/output/git.rb index 8b605f6..8d9dae1 100644 --- a/lib/oxidized/output/git.rb +++ b/lib/oxidized/output/git.rb @@ -21,7 +21,14 @@ class Git < Output        Oxidized.asetus.save :user        raise NoConfig, 'no output git config, edit ~/.config/oxidized/config'      end -    @cfg.repo = File.expand_path @cfg.repo + +    if @cfg.repo.respond_to?(:each) +      @cfg.repo.each do |group, repo| +        @cfg.repo["#{group}="] = File.expand_path repo +      end +    else +      @cfg.repo = File.expand_path @cfg.repo +    end    end    def store file, outputs, opt={} @@ -70,7 +77,10 @@ class Git < Output      def version node, group        begin          repo = @cfg.repo -        if group +        path = node +        if group and @cfg.single_repo? +          path = "#{group}/#{node}" +        elsif group            repo = File.join File.dirname(repo), group + '.git'          end          repo = Rugged::Repository.new repo @@ -80,7 +90,7 @@ class Git < Output          i = -1          tab  = []          walker.each do |commit| -          if commit.diff(paths: [node]).size > 0 +          if commit.diff(paths: [path]).size > 0              hash = {}              hash[:date] = commit.time.to_s              hash[:oid] = commit.oid @@ -100,8 +110,10 @@ class Git < Output      def get_version node, group, oid        begin          repo = @cfg.repo -        if group && group != '' +        if group && group != '' && !@cfg.single_repo?            repo = File.join File.dirname(repo), group + '.git' +        elsif group && group != '' +          node = File.join group, node          end          repo = Rugged::Repository.new repo          repo.blob_at(oid,node).content @@ -115,7 +127,7 @@ class Git < Output        begin          repo = @cfg.repo          diff_commits = nil -        if group && group != '' +        if group && group != '' && !@cfg.single_repo?            repo = File.join File.dirname(repo), group + '.git'          end          repo = Rugged::Repository.new repo @@ -147,13 +159,19 @@ class Git < Output    def update repo, file, data      return if data.empty? +      if @opt[:group]        if @cfg.single_repo?          file = File.join @opt[:group], file        else -        repo = File.join File.dirname(repo), @opt[:group] + '.git' +        repo = if repo.is_a?(::String) +                 File.join File.dirname(repo), @opt[:group] + '.git' +               else +                 repo[@opt[:group]] +               end        end      end +      begin        repo = Rugged::Repository.new repo        update_repo repo, file, data, @msg, @user, @email diff --git a/lib/oxidized/output/http.rb b/lib/oxidized/output/http.rb new file mode 100644 index 0000000..13ba300 --- /dev/null +++ b/lib/oxidized/output/http.rb @@ -0,0 +1,58 @@ +module Oxidized +  class Http < Output +    attr_reader :commitref +    def initialize +      @cfg = Oxidized.config.output.http +    end + +    def setup +      if @cfg.empty? +        CFGS.user.output.http.user  = 'Oxidized' +        CFGS.user.output.http.pasword = 'secret' +        CFGS.user.output.http.url  =  'http://localhost/web-api/oxidized' +        CFGS.save :user +        raise NoConfig, 'no output http config, edit ~/.config/oxidized/config' +      end +    end +    require "net/http" +    require "uri" +    require "json" +    def store node, outputs, opt={} +      @commitref = nil +      json = JSON.pretty_generate( +          { +              'msg' => opt[:msg], +              'user' => opt[:user], +              'email' => opt[:email], +              'group' => opt[:group], +              'node' => node, +              'config' => outputs.to_cfg, +              # actually we need to also iterate outputs, for other types like in gitlab. But most people don't use 'type' functionality. +          } +      ) +      uri = URI.parse @cfg.url +      http = Net::HTTP.new uri.host, uri.port +      #http.use_ssl = true if uri.scheme = 'https' +      req = Net::HTTP::Post.new(uri.request_uri, initheader = { 'Content-Type' => 'application/json'}) +      req.basic_auth @cfg.user, @cfg.password +      req.body = json +      response = http.request req + +      case response.code.to_i +        when 200 || 201 +          Oxidized.logger.info "Configuration http backup complete for #{node}" +          p [:success] +        when (400..499) +          Oxidized.logger.info "Configuration http backup for #{node} failed status: #{response.body}" +          p [:bad_request] +        when (500..599) +          p [:server_problems] +          Oxidized.logger.info "Configuration http backup for #{node} failed status: #{response.body}" +      end + +    end + +  end +end + + diff --git a/lib/oxidized/version.rb b/lib/oxidized/version.rb index c06a14a..428cd19 100644 --- a/lib/oxidized/version.rb +++ b/lib/oxidized/version.rb @@ -1,3 +1,3 @@  module Oxidized -  VERSION = '0.12.2' +  VERSION = '0.14.3'  end diff --git a/oxidized.gemspec b/oxidized.gemspec index efa21db..9ff795c 100644 --- a/oxidized.gemspec +++ b/oxidized.gemspec @@ -21,11 +21,11 @@ Gem::Specification.new do |s|    s.required_ruby_version =           '>= 2.0.0'    s.add_runtime_dependency 'asetus',  '~> 0.1'    s.add_runtime_dependency 'slop',    '~> 3.5' -  s.add_runtime_dependency 'net-ssh', '~> 3.0', '>= 3.0.2' -  s.add_runtime_dependency 'rugged',  '~> 0.21', '>= 0.21.4' -  s.add_development_dependency 'pry', '~> 0' -  s.add_development_dependency 'bundler', '~> 1.10' -  s.add_development_dependency 'rake', '~> 10.0' +  s.add_runtime_dependency 'net-ssh', '>= 3.0.0', '<3.1' +  s.add_runtime_dependency 'rugged',  '~> 0.21',  '>= 0.21.4' +  s.add_development_dependency 'pry',      '~> 0' +  s.add_development_dependency 'bundler',  '~> 1.10' +  s.add_development_dependency 'rake',     '~> 10.0'    s.add_development_dependency 'minitest', '~> 5.8' -  s.add_development_dependency 'mocha', '~> 1.1' +  s.add_development_dependency 'mocha',    '~> 1.1'  end diff --git a/spec/cli_spec.rb b/spec/cli_spec.rb index 783d9c8..0a6c91b 100644 --- a/spec/cli_spec.rb +++ b/spec/cli_spec.rb @@ -12,7 +12,8 @@ describe Oxidized::CLI do        before { ARGV.replace [option] }        it 'prints the version and exits' do -        Oxidized::Config.expects(:load).returns(asetus) +        Oxidized::Config.expects(:load) +        Oxidized.expects(:setup_logger)          Kernel.expects(:exit)          proc { diff --git a/spec/githubrepo_spec.rb b/spec/githubrepo_spec.rb index 9ad43e9..ab5e251 100644 --- a/spec/githubrepo_spec.rb +++ b/spec/githubrepo_spec.rb @@ -2,19 +2,32 @@ require 'spec_helper'  require 'rugged'  require 'oxidized/hook/githubrepo' -describe Oxidized::Node do +describe GithubRepo do    let(:credentials) { mock() }    let(:remote) { mock() } +  let(:remotes) { mock() }    let(:repo_head) { mock() }    let(:repo) { mock() }    let(:gr) { GithubRepo.new }    before(:each) do      Oxidized.asetus = Asetus.new -    Oxidized.config.output.git.repo = 'foo.git' +    Oxidized.config.log = '/dev/null'      Oxidized.setup_logger    end +  describe '#validate_cfg!' do +    before do +      gr.expects(:respond_to?).with(:validate_cfg!).returns(false) # `cfg=` call +    end + +    it 'raise a error when `remote_repo` is not configured' do +      Oxidized.config.hooks.github_repo_hook = { type: 'githubrepo' } +      gr.cfg = Oxidized.config.hooks.github_repo_hook +      proc { gr.validate_cfg! }.must_raise(KeyError) +    end +  end +    describe "#fetch_and_merge_remote" do      before(:each) do        Oxidized.config.hooks.github_repo_hook.remote_repo = 'git@github.com:username/foo.git' @@ -68,31 +81,89 @@ describe Oxidized::Node do    end    describe "#run_hook" do -    before(:each) do -      remote.expects(:url).returns('https://github.com/username/foo.git') -      remote.expects(:push).with(['refs/heads/master'], credentials: credentials).returns(true) +    let(:group) { nil } +    let(:ctx) { OpenStruct.new(node: node) } +    let(:node) do +      Oxidized::Node.new(ip: '127.0.0.1', group: group, model: 'junos', output: 'output') +    end + +    before do        repo_head.expects(:name).twice.returns('refs/heads/master')        repo.expects(:head).twice.returns(repo_head)        repo.expects(:path).returns('foo.git') -      repo.expects(:remotes).returns({'origin' => remote})        repo.expects(:fetch).with('origin', ['refs/heads/master'], credentials: credentials).returns(Hash.new(0)) -      Rugged::Repository.expects(:new).with('foo.git').returns(repo)      end -    it "will push to the remote repository using https" do -      Oxidized.config.hooks.github_repo_hook.remote_repo = 'https://github.com/username/foo.git' -      Oxidized.config.hooks.github_repo_hook.username = 'username' -      Oxidized.config.hooks.github_repo_hook.password = 'password' -      Rugged::Credentials::UserPassword.expects(:new).with(username: 'username', password: 'password').returns(credentials) -      gr.cfg = Oxidized.config.hooks.github_repo_hook -      gr.run_hook(nil).must_equal true +    describe 'when there is only one repository and no groups' do +      before do +        Oxidized.config.output.git.repo = 'foo.git' +        remote.expects(:url).returns('https://github.com/username/foo.git') +        remote.expects(:push).with(['refs/heads/master'], credentials: credentials).returns(true) +        repo.expects(:remotes).returns({'origin' => remote}) +        Rugged::Repository.expects(:new).with('foo.git').returns(repo) +      end + +      it "will push to the remote repository using https" do +        Oxidized.config.hooks.github_repo_hook.remote_repo = 'https://github.com/username/foo.git' +        Oxidized.config.hooks.github_repo_hook.username = 'username' +        Oxidized.config.hooks.github_repo_hook.password = 'password' +        Rugged::Credentials::UserPassword.expects(:new).with(username: 'username', password: 'password').returns(credentials) +        gr.cfg = Oxidized.config.hooks.github_repo_hook +        gr.run_hook(ctx).must_equal true +      end + +      it "will push to the remote repository using ssh" do +        Oxidized.config.hooks.github_repo_hook.remote_repo = 'git@github.com:username/foo.git' +        Rugged::Credentials::SshKeyFromAgent.expects(:new).with(username: 'git').returns(credentials) +        gr.cfg = Oxidized.config.hooks.github_repo_hook +        gr.run_hook(ctx).must_equal true +      end      end -    it "will push to the remote repository using ssh" do -      Oxidized.config.hooks.github_repo_hook.remote_repo = 'git@github.com:username/foo.git' -      Rugged::Credentials::SshKeyFromAgent.expects(:new).with(username: 'git').returns(credentials) -      gr.cfg = Oxidized.config.hooks.github_repo_hook -      gr.run_hook(nil).must_equal true +    describe "when there are groups" do +      let(:group) { 'ggrroouupp' } + +      before do +        Rugged::Credentials::SshKeyFromAgent.expects(:new).with(username: 'git').returns(credentials) +        Rugged::Repository.expects(:new).with(repository).returns(repo) + +        repo.expects(:remotes).twice.returns(remotes) +        remotes.expects(:[]).with('origin').returns(nil) +        remotes.expects(:create).with('origin', create_remote).returns(remote) +        remote.expects(:url).returns('url') +        remote.expects(:push).with(['refs/heads/master'], credentials: credentials).returns(true) +      end + +      describe 'and there are several repositories' do +        let(:create_remote) { 'ggrroouupp#remote_repo' } +        let(:repository) { './ggrroouupp.git' } + +        before do +          Oxidized.config.output.git.repo.ggrroouupp = repository +          Oxidized.config.hooks.github_repo_hook.remote_repo.ggrroouupp = 'ggrroouupp#remote_repo' +        end + +        it 'will push to the node group repository' do +          gr.cfg = Oxidized.config.hooks.github_repo_hook +          gr.run_hook(ctx).must_equal true +        end +      end + +      describe 'and has a single repository' do +        let(:create_remote) { 'github_repo_hook#remote_repo' } +        let(:repository) { 'foo.git' } + +        before do +          Oxidized.config.output.git.repo = repository +          Oxidized.config.hooks.github_repo_hook.remote_repo = 'github_repo_hook#remote_repo' +          Oxidized.config.output.git.single_repo = true +        end + +        it 'will push to the correct repository' do +          gr.cfg = Oxidized.config.hooks.github_repo_hook +          gr.run_hook(ctx).must_equal true +        end +      end      end    end  end diff --git a/spec/input/ssh_spec.rb b/spec/input/ssh_spec.rb index 43c7d66..c4210f2 100644 --- a/spec/input/ssh_spec.rb +++ b/spec/input/ssh_spec.rb @@ -12,7 +12,7 @@ describe Oxidized::SSH do                                 model: 'junos',                                 username: 'alma',                                 password: 'armud', -                               vars: {proxy: 'test.com'}) +                               vars: {ssh_proxy: 'test.com'})    end @@ -25,7 +25,7 @@ describe Oxidized::SSH do        @node.expects(:model).returns(model)        proxy = mock() -      Net::SSH::Proxy::Command.expects(:new).with("ssh test.com nc %h %p").returns(proxy) +      Net::SSH::Proxy::Command.expects(:new).with("ssh test.com -W %h:%p").returns(proxy)        Net::SSH.expects(:start).with('93.184.216.34', 'alma', {:port => 22, :password => 'armud', :timeout => Oxidized.config.timeout,                                      :paranoid => Oxidized.config.input.ssh.secure, :auth_methods => ['none', 'publickey', 'password', 'keyboard-interactive'],                                      :number_of_password_prompts => 0, :proxy => proxy}) diff --git a/spec/node_spec.rb b/spec/node_spec.rb index c568463..21c6e34 100644 --- a/spec/node_spec.rb +++ b/spec/node_spec.rb @@ -41,4 +41,50 @@ describe Oxidized::Node do        status.must_equal :success      end    end + +  describe '#repo' do +    let(:group) { nil } +    let(:node) do +      Oxidized::Node.new({ +        ip: '127.0.0.1', group: group, model: 'junos' +      }) +    end + +    it 'when there are no groups' do +      Oxidized.config.output.git.repo = '/tmp/repository.git' +      node.repo.must_equal '/tmp/repository.git' +    end + +    describe 'when there are groups' do +      let(:group) { 'ggrroouupp' } + +      before do +        Oxidized.config.output.git.single_repo = single_repo +      end + +      describe 'with only one repository' do +        let(:single_repo) { true } + +        before do +          Oxidized.config.output.git.repo = '/tmp/repository.git' +        end + +        it 'should use the correct remote' do +          node.repo.must_equal '/tmp/repository.git' +        end +      end + +      describe 'with more than one repository' do +        let(:single_repo) { nil } + +        before do +          Oxidized.config.output.git.repo.ggrroouupp = '/tmp/ggrroouupp.git' +        end + +        it 'should use the correct remote' do +          node.repo.must_equal '/tmp/ggrroouupp.git' +        end +      end +    end +  end  end | 
