summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml3
-rw-r--r--Gemfile.lock41
-rw-r--r--README.md4
-rw-r--r--Rakefile16
-rwxr-xr-xbin/console9
-rwxr-xr-xbin/oxidized2
-rw-r--r--lib/oxidized.rb47
-rw-r--r--lib/oxidized/cli.rb58
-rw-r--r--lib/oxidized/config.rb84
-rw-r--r--lib/oxidized/config/vars.rb8
-rw-r--r--lib/oxidized/core.rb17
-rw-r--r--lib/oxidized/hook.rb8
-rw-r--r--lib/oxidized/hook/githubrepo.rb57
-rw-r--r--lib/oxidized/input/cli.rb3
-rw-r--r--lib/oxidized/input/ftp.rb6
-rw-r--r--lib/oxidized/input/ssh.rb18
-rw-r--r--lib/oxidized/input/telnet.rb6
-rw-r--r--lib/oxidized/job.rb6
-rw-r--r--lib/oxidized/log.rb22
-rw-r--r--lib/oxidized/model/junos.rb18
-rw-r--r--lib/oxidized/model/model.rb5
-rw-r--r--lib/oxidized/node.rb30
-rw-r--r--lib/oxidized/nodes.rb24
-rw-r--r--lib/oxidized/output/file.rb6
-rw-r--r--lib/oxidized/output/git.rb10
-rw-r--r--lib/oxidized/source/csv.rb14
-rw-r--r--lib/oxidized/source/http.rb2
-rw-r--r--lib/oxidized/source/source.rb2
-rw-r--r--lib/oxidized/source/sql.rb14
-rw-r--r--lib/oxidized/string.rb2
-rw-r--r--lib/oxidized/version.rb3
-rw-r--r--lib/oxidized/worker.rb14
-rw-r--r--oxidized.gemspec15
-rw-r--r--spec/githubrepo_spec.rb98
-rw-r--r--spec/node_spec.rb44
-rw-r--r--spec/nodes_spec.rb35
-rw-r--r--spec/spec_helper.rb14
37 files changed, 559 insertions, 206 deletions
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..3734051
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,3 @@
+language: ruby
+rvm:
+ - 2.1.6
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000..b46ca2f
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,41 @@
+PATH
+ remote: .
+ specs:
+ oxidized (0.9.0)
+ asetus (~> 0.1)
+ net-ssh (~> 3.0, >= 3.0.2)
+ rugged (~> 0.21, >= 0.21.4)
+ slop (~> 3.5)
+
+GEM
+ remote: https://rubygems.org/
+ specs:
+ asetus (0.3.0)
+ coderay (1.1.0)
+ metaclass (0.0.4)
+ method_source (0.8.2)
+ minitest (5.8.3)
+ mocha (1.1.0)
+ metaclass (~> 0.0.1)
+ net-ssh (3.0.2)
+ pry (0.10.3)
+ coderay (~> 1.1.0)
+ method_source (~> 0.8.1)
+ slop (~> 3.4)
+ rake (10.4.2)
+ rugged (0.23.3)
+ slop (3.6.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ bundler (~> 1.10)
+ minitest (~> 5.8)
+ mocha (~> 1.1)
+ oxidized!
+ pry (~> 0)
+ rake (~> 10.0)
+
+BUNDLED WITH
+ 1.11.2
diff --git a/README.md b/README.md
index f2116de..b5cd435 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Oxidized
+# Oxidized [![Build Status](https://travis-ci.org/Shopify/oxidized.svg)](https://travis-ci.org/Shopify/oxidized)
[![Gem Version](https://badge.fury.io/rb/oxidized.svg)](http://badge.fury.io/rb/oxidized)
@@ -140,7 +140,7 @@ Oxidized supports ```CSV```, ```SQLite``` and ```HTTP``` as source backends. The
Possible outputs are either ```file``` or ```git```. The file backend takes a destination directory as argument and will keep a file per device, with most recent running version of a device. The GIT backend (recommended) will initialize an empty GIT repository in the specified path and create a new commit on every configuration change. Take a look at the [Cookbook](#cookbook) for more details.
-Maps define how to map a model's fields to model [model fields](https://github.com/ytti/oxidized/tree/master/lib/oxidized/model). Most of the settings should be self explanatory, log is ignored if Syslog::Logger exists (>=2.0) and syslog is used instead.
+Maps define how to map a model's fields to model [model fields](https://github.com/ytti/oxidized/tree/master/lib/oxidized/model). Most of the settings should be self explanatory, log is ignored if `use_syslog`(requires Ruby >= 2.0) is set to `true`.
First create the directory where the CSV ```output``` is going to store device configs and start Oxidized once.
```
diff --git a/Rakefile b/Rakefile
index 3d85377..3a1be5e 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,10 +1,5 @@
-begin
- require 'rake/testtask'
- require 'bundler'
- # Bundler.setup
-rescue LoadError
- warn 'bundler missing'
-end
+require 'bundler/gem_tasks'
+require 'rake/testtask'
gemspec = eval(File.read(Dir['*.gemspec'].first))
file = [gemspec.name, gemspec.version].join('-') + '.gem'
@@ -17,8 +12,9 @@ end
desc 'Run minitest'
task :test do
Rake::TestTask.new do |t|
- t.libs.push "lib"
- t.test_files = FileList['spec/*_spec.rb']
+ t.libs << 'spec'
+ t.test_files = FileList['spec/**/*_spec.rb']
+ t.warning = true
t.verbose = true
end
end
@@ -49,3 +45,5 @@ desc 'Push to rubygems'
task :push => :tag do
system "gem push gems/#{file}"
end
+
+task default: :test
diff --git a/bin/console b/bin/console
new file mode 100755
index 0000000..0419027
--- /dev/null
+++ b/bin/console
@@ -0,0 +1,9 @@
+#!/usr/bin/env ruby
+
+lib = File.expand_path('../../lib', __FILE__)
+$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
+
+require 'oxidized'
+require 'pry'
+
+Pry.start
diff --git a/bin/oxidized b/bin/oxidized
index e5d9bec..2695a52 100755
--- a/bin/oxidized
+++ b/bin/oxidized
@@ -9,5 +9,5 @@ begin
Oxidized::CLI.new.run
rescue => error
warn "#{error}"
- raise if Oxidized::CFG.debug
+ raise if Oxidized.config.debug
end
diff --git a/lib/oxidized.rb b/lib/oxidized.rb
index 61948ff..e92224a 100644
--- a/lib/oxidized.rb
+++ b/lib/oxidized.rb
@@ -1,6 +1,49 @@
module Oxidized
class OxidizedError < StandardError; end
- Encoding.default_external = 'UTF-8'
- Directory = File.expand_path File.join File.dirname(__FILE__), '../'
+
+ Directory = File.expand_path(File.join(File.dirname(__FILE__), '../'))
+
+ require 'oxidized/string'
+ require 'oxidized/config'
+ require 'oxidized/config/vars'
+ require 'oxidized/worker'
+ require 'oxidized/nodes'
+ require 'oxidized/manager'
+ require 'oxidized/hook'
require 'oxidized/core'
+
+ def self.asetus
+ @@asetus
+ end
+
+ def self.asetus=(val)
+ @@asetus = val
+ end
+
+ def self.config
+ asetus.cfg
+ end
+
+ def self.logger
+ @@logger
+ end
+
+ def self.logger=(val)
+ @@logger = val
+ end
+
+ def self.setup_logger
+ self.logger = if config.has_key?('use_syslog') && config.use_syslog
+ require 'syslog/logger'
+ Syslog::Logger.new('oxidized')
+ else
+ require 'logger'
+ if config.has_key?('log')
+ Logger.new(config.log)
+ else
+ Logger.new(STDERR)
+ end
+ end
+ logger.level = Logger::INFO unless config.debug
+ end
end
diff --git a/lib/oxidized/cli.rb b/lib/oxidized/cli.rb
index c66ec8d..15d20c5 100644
--- a/lib/oxidized/cli.rb
+++ b/lib/oxidized/cli.rb
@@ -1,11 +1,14 @@
module Oxidized
class CLI
- require 'oxidized'
require 'slop'
+ require 'oxidized'
def run
+ check_pid
Process.daemon if @opts[:daemonize]
+ write_pid
begin
+ Oxidized.logger.info "Oxidized starting, running as pid #{$$}"
Oxidized.new
rescue => error
crash error
@@ -16,13 +19,16 @@ module Oxidized
private
def initialize
- Log.info "Oxidized starting, running as pid #{$$}"
_args, @opts = parse_opts
- CFG.debug = true if @opts[:debug]
+
+ Config.load(@opts)
+ Oxidized.setup_logger
+
+ @pidfile = File.expand_path("pid")
end
def crash error
- Log.fatal "Oxidized crashed, crashfile written in #{Config::Crash}"
+ Oxidized.logger.fatal "Oxidized crashed, crashfile written in #{Config::Crash}"
open Config::Crash, 'w' do |file|
file.puts '-' * 50
file.puts Time.now.utc
@@ -40,5 +46,49 @@ module Oxidized
end
[opts.parse!, opts]
end
+
+ def pidfile
+ @pidfile
+ end
+
+ def pidfile?
+ !!pidfile
+ end
+
+ def write_pid
+ if pidfile?
+ begin
+ File.open(pidfile, ::File::CREAT | ::File::EXCL | ::File::WRONLY){|f| f.write("#{Process.pid}") }
+ at_exit { File.delete(pidfile) if File.exists?(pidfile) }
+ rescue Errno::EEXIST
+ check_pid
+ retry
+ end
+ end
+ end
+
+ def check_pid
+ if pidfile?
+ case pid_status(pidfile)
+ when :running, :not_owned
+ puts "A server is already running. Check #{pidfile}"
+ exit(1)
+ when :dead
+ File.delete(pidfile)
+ end
+ end
+ end
+
+ def pid_status(pidfile)
+ return :exited unless File.exists?(pidfile)
+ pid = ::File.read(pidfile).to_i
+ return :dead if pid == 0
+ Process.kill(0, pid)
+ :running
+ rescue Errno::ESRCH
+ :dead
+ rescue Errno::EPERM
+ :not_owned
+ end
end
end
diff --git a/lib/oxidized/config.rb b/lib/oxidized/config.rb
index f45004a..c850059 100644
--- a/lib/oxidized/config.rb
+++ b/lib/oxidized/config.rb
@@ -11,47 +11,53 @@ module Oxidized
SourceDir = File.join Directory, %w(lib oxidized source)
HookDir = File.join Directory, %w(lib oxidized hook)
Sleep = 1
+
+ def self.load(cmd_opts={})
+ asetus = Asetus.new(name: 'oxidized', load: false, key_to_s: true)
+ Oxidized.asetus = asetus
+
+ asetus.default.username = 'username'
+ asetus.default.password = 'password'
+ asetus.default.model = 'junos'
+ asetus.default.interval = 3600
+ asetus.default.use_syslog = false
+ asetus.default.debug = false
+ asetus.default.threads = 30
+ 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.vars = {} # could be 'enable'=>'enablePW'
+ asetus.default.groups = {} # group level configuration
+
+ asetus.default.input.default = 'ssh, telnet'
+ asetus.default.input.debug = false # or String for session log file
+ asetus.default.input.ssh.secure = false # complain about changed certs
+
+ asetus.default.output.default = 'file' # file, git
+ asetus.default.source.default = 'csv' # csv, sql
+
+ asetus.default.model_map = {
+ 'cisco' => 'ios',
+ 'juniper' => 'junos',
+ }
+
+ begin
+ asetus.load # load system+user configs, merge to Config.cfg
+ rescue => error
+ raise InvalidConfig, "Error loading config: #{error.message}"
+ end
+
+ raise NoConfig, 'edit ~/.config/oxidized/config' if asetus.create
+
+ # override if comand line flag given
+ asetus.cfg.debug = cmd_opts[:debug] if cmd_opts[:debug]
+
+ asetus
+ end
end
+
class << self
attr_accessor :mgr, :Hooks
end
- CFGS = Asetus.new :name=>'oxidized', :load=>false, :key_to_s=>true
- CFGS.default.username = 'username'
- CFGS.default.password = 'password'
- CFGS.default.model = 'junos'
- CFGS.default.interval = 3600
- CFGS.default.log = File.join Config::Root, 'log'
- CFGS.default.debug = false
- CFGS.default.threads = 30
- CFGS.default.timeout = 20
- CFGS.default.retries = 3
- CFGS.default.prompt = /^([\w.@-]+[#>]\s?)$/
- CFGS.default.rest = '127.0.0.1:8888' # or false to disable
- CFGS.default.vars = {} # could be 'enable'=>'enablePW'
- CFGS.default.groups = {} # group level configuration
-
- CFGS.default.input.default = 'ssh, telnet'
- CFGS.default.input.debug = false # or String for session log file
- CFGS.default.input.ssh.secure = false # complain about changed certs
-
- CFGS.default.output.default = 'file' # file, git
- CFGS.default.source.default = 'csv' # csv, sql
-
- CFGS.default.model_map = {
- 'cisco' => 'ios',
- 'juniper' => 'junos',
- }
-
- begin
- CFGS.load # load system+user configs, merge to Config.cfg
- rescue => error
- raise InvalidConfig, "Error loading config: #{error.message}"
- ensure
- CFG = CFGS.cfg # convenienence, instead of Config.cfg.password, CFG.password
- end
-
- Log.level = Logger::INFO unless CFG.debug
- raise NoConfig, 'edit ~/.config/oxidized/config' if CFGS.create
- Log.file = CFG.log if CFG.log
-
end
diff --git a/lib/oxidized/config/vars.rb b/lib/oxidized/config/vars.rb
index b0471f2..09f9781 100644
--- a/lib/oxidized/config/vars.rb
+++ b/lib/oxidized/config/vars.rb
@@ -3,12 +3,12 @@ module Oxidized::Config::Vars
# nil values will be ignored
def vars name
r = @node.vars[name] unless @node.vars.nil?
- if Oxidized::CFG.groups.has_key?(@node.group)
- if Oxidized::CFG.groups[@node.group].vars.has_key?(name.to_s)
- r ||= Oxidized::CFG.groups[@node.group].vars[name.to_s]
+ if Oxidized.config.groups.has_key?(@node.group)
+ if Oxidized.config.groups[@node.group].vars.has_key?(name.to_s)
+ r ||= Oxidized.config.groups[@node.group].vars[name.to_s]
end
end
- r ||= Oxidized::CFG.vars[name.to_s] if Oxidized::CFG.vars.has_key?(name.to_s)
+ r ||= Oxidized.config.vars[name.to_s] if Oxidized.config.vars.has_key?(name.to_s)
r
end
end
diff --git a/lib/oxidized/core.rb b/lib/oxidized/core.rb
index 6e7a352..d57c5cb 100644
--- a/lib/oxidized/core.rb
+++ b/lib/oxidized/core.rb
@@ -1,12 +1,4 @@
module Oxidized
- require 'oxidized/log'
- require 'oxidized/string'
- require 'oxidized/config'
- require 'oxidized/config/vars'
- require 'oxidized/worker'
- require 'oxidized/nodes'
- require 'oxidized/manager'
- require 'oxidized/hook'
class << self
def new *args
Core.new args
@@ -18,18 +10,19 @@ module Oxidized
def initialize args
Oxidized.mgr = Manager.new
- Oxidized.Hooks = HookManager.from_config CFG
+ Oxidized.Hooks = HookManager.from_config(Oxidized.config)
nodes = Nodes.new
raise NoNodesFound, 'source returns no usable nodes' if nodes.size == 0
@worker = Worker.new nodes
trap('HUP') { nodes.load }
- if CFG.rest?
+ if Oxidized.config.rest?
begin
require 'oxidized/web'
rescue LoadError
- raise OxidizedError, 'oxidized-web not found: sudo gem install oxidized-web - or disable web support by setting "rest: false" in your configuration'
+ raise OxidizedError, 'oxidized-web not found: sudo gem install oxidized-web - \
+ or disable web support by setting "rest: false" in your configuration'
end
- @rest = API::Web.new nodes, CFG.rest
+ @rest = API::Web.new nodes, Oxidized.config.rest
@rest.run
end
run
diff --git a/lib/oxidized/hook.rb b/lib/oxidized/hook.rb
index 7f1942b..029688d 100644
--- a/lib/oxidized/hook.rb
+++ b/lib/oxidized/hook.rb
@@ -46,7 +46,7 @@ class HookManager
hook.cfg = cfg
@registered_hooks[event] << RegisteredHook.new(name, hook)
- Log.debug "Hook #{name.inspect} registered #{hook.class} for event #{event.inspect}"
+ Oxidized.logger.debug "Hook #{name.inspect} registered #{hook.class} for event #{event.inspect}"
end
def handle event, ctx_params={}
@@ -57,7 +57,7 @@ class HookManager
begin
r_hook.hook.run_hook ctx
rescue => e
- Log.error "Hook #{r_hook.name} (#{r_hook.hook}) failed " +
+ Oxidized.logger.error "Hook #{r_hook.name} (#{r_hook.hook}) failed " +
"(#{e.inspect}) for event #{event.inspect}"
end
end
@@ -66,7 +66,7 @@ end
# Hook abstract base class
class Hook
- attr_accessor :cfg
+ attr_reader :cfg
def initialize
end
@@ -81,7 +81,7 @@ class Hook
end
def log(msg, level=:info)
- Log.send(level, "#{self.class.name}: #{msg}")
+ Oxidized.logger.send(level, "#{self.class.name}: #{msg}")
end
end
diff --git a/lib/oxidized/hook/githubrepo.rb b/lib/oxidized/hook/githubrepo.rb
new file mode 100644
index 0000000..d10b51e
--- /dev/null
+++ b/lib/oxidized/hook/githubrepo.rb
@@ -0,0 +1,57 @@
+class GithubRepo < Oxidized::Hook
+ def validate_cfg!
+ cfg.has_key?('remote_repo') or raise KeyError, 'remote_repo is required'
+ end
+
+ def run_hook(ctx)
+ repo = Rugged::Repository.new(Oxidized.config.output.git.repo)
+ log "Pushing local repository(#{repo.path})..."
+ remote = repo.remotes['origin'] || repo.remotes.create('origin', cfg.remote_repo)
+ log "to remote: #{remote.url}"
+
+ fetch_and_merge_remote(repo)
+
+ remote.push([repo.head.name], credentials: credentials)
+ end
+
+ def fetch_and_merge_remote(repo)
+ result = repo.fetch('origin', [repo.head.name], credentials: credentials)
+ log result.inspect, :debug
+
+ unless result[:total_deltas] > 0
+ log "nothing recieved after fetch", :debug
+ return
+ end
+
+ their_branch = repo.branches["origin/master"]
+
+ log "merging fetched branch #{their_branch.name}", :debug
+
+ merge_index = repo.merge_commits(repo.head.target_id, their_branch.target_id)
+
+ if merge_index.conflicts?
+ log("Conflicts detected, skipping Rugged::Commit.create", :warn)
+ return
+ end
+
+ Rugged::Commit.create(repo, {
+ parents: [repo.head.target, their_branch.target],
+ tree: merge_index.write_tree(repo),
+ message: "Merge remote-tracking branch '#{their_branch.name}'",
+ update_ref: "HEAD"
+ })
+ end
+
+ private
+
+ def credentials
+ @credentials ||= if cfg.has_key?('username') && cfg.has_key?('password')
+ 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')
+ end
+ end
+
+end
diff --git a/lib/oxidized/input/cli.rb b/lib/oxidized/input/cli.rb
index 22d188c..731b459 100644
--- a/lib/oxidized/input/cli.rb
+++ b/lib/oxidized/input/cli.rb
@@ -1,6 +1,7 @@
module Oxidized
class Input
module CLI
+ attr_reader :node
def initialize
@post_login = []
@@ -10,7 +11,7 @@ module Oxidized
def get
connect_cli
- d = @node.model.get
+ d = node.model.get
disconnect
d
end
diff --git a/lib/oxidized/input/ftp.rb b/lib/oxidized/input/ftp.rb
index 70db60c..93cdb38 100644
--- a/lib/oxidized/input/ftp.rb
+++ b/lib/oxidized/input/ftp.rb
@@ -18,7 +18,7 @@ module Oxidized
def connect node
@node = node
@node.model.cfg['ftp'].each { |cb| instance_exec(&cb) }
- @log = File.open(Oxidized::Config::Crash + "-#{@node.ip}-ftp", 'w') if CFG.input.debug?
+ @log = File.open(Oxidized::Config::Crash + "-#{@node.ip}-ftp", 'w') if Oxidized.config.input.debug?
@ftp = Net::FTP.new @node.ip, @node.auth[:username], @node.auth[:password]
connected?
end
@@ -28,7 +28,7 @@ module Oxidized
end
def cmd file
- Log.debug "FTP: #{file} @ #{@node.name}"
+ Oxidized.logger.debug "FTP: #{file} @ #{@node.name}"
@ftp.getbinaryfile file, nil
end
@@ -47,7 +47,7 @@ module Oxidized
@ftp.close
#rescue Errno::ECONNRESET, IOError
ensure
- @log.close if CFG.input.debug?
+ @log.close if Oxidized.config.input.debug?
end
end
diff --git a/lib/oxidized/input/ssh.rb b/lib/oxidized/input/ssh.rb
index 21fb02c..476a786 100644
--- a/lib/oxidized/input/ssh.rb
+++ b/lib/oxidized/input/ssh.rb
@@ -19,11 +19,11 @@ module Oxidized
@node = node
@output = ''
@node.model.cfg['ssh'].each { |cb| instance_exec(&cb) }
- secure = CFG.input.ssh.secure
- @log = File.open(Oxidized::Config::Crash + "-#{@node.ip}-ssh", 'w') if CFG.input.debug?
+ secure = Oxidized.config.input.ssh.secure
+ @log = File.open(Oxidized::Config::Crash + "-#{@node.ip}-ssh", 'w') if Oxidized.config.input.debug?
port = vars(:ssh_port) || 22
@ssh = Net::SSH.start @node.ip, @node.auth[:username], :port => port.to_i,
- :password => @node.auth[:password], :timeout => CFG.timeout,
+ :password => @node.auth[:password], :timeout => Oxidized.config.timeout,
:paranoid => secure,
:auth_methods => %w(none publickey password keyboard-interactive),
:number_of_password_prompts => 0
@@ -42,8 +42,8 @@ module Oxidized
@ssh and not @ssh.closed?
end
- def cmd cmd, expect=@node.prompt
- Log.debug "SSH: #{cmd} @ #{@node.name}"
+ def cmd cmd, expect=node.prompt
+ Oxidized.logger.debug "SSH: #{cmd} @ #{node.name}"
if @exec
@ssh.exec! cmd
else
@@ -64,17 +64,17 @@ module Oxidized
def disconnect
disconnect_cli
# if disconnect does not disconnect us, give up after timeout
- Timeout::timeout(CFG.timeout) { @ssh.loop }
+ Timeout::timeout(Oxidized.config.timeout) { @ssh.loop }
rescue Errno::ECONNRESET, Net::SSH::Disconnect, IOError
ensure
- @log.close if CFG.input.debug?
+ @log.close if Oxidized.config.input.debug?
(@ssh.close rescue true) unless @ssh.closed?
end
def shell_open ssh
@ses = ssh.open_channel do |ch|
ch.on_data do |_ch, data|
- @log.print data if CFG.input.debug?
+ @log.print data if Oxidized.config.input.debug?
@output << data
@output = @node.model.expects @output
end
@@ -109,7 +109,7 @@ module Oxidized
end
def expect regexp
- Timeout::timeout(CFG.timeout) do
+ Timeout::timeout(Oxidized.config.timeout) do
@ssh.loop(0.1) do
sleep 0.1
not @output.match regexp
diff --git a/lib/oxidized/input/telnet.rb b/lib/oxidized/input/telnet.rb
index bf0140c..e9fd7d9 100644
--- a/lib/oxidized/input/telnet.rb
+++ b/lib/oxidized/input/telnet.rb
@@ -8,7 +8,7 @@ module Oxidized
def connect node
@node = node
- @timeout = CFG.timeout
+ @timeout = Oxidized.config.timeout
@node.model.cfg['telnet'].each { |cb| instance_exec(&cb) }
port = vars(:telnet_port) || 23
@@ -16,7 +16,7 @@ module Oxidized
'Port' => port.to_i,
'Timeout' => @timeout,
'Model' => @node.model }
- opt['Output_log'] = Oxidized::Config::Crash + "-#{@node.ip}-telnet" if CFG.input.debug?
+ opt['Output_log'] = Oxidized::Config::Crash + "-#{@node.ip}-telnet" if Oxidized.config.input.debug?
@telnet = Net::Telnet.new opt
if @node.auth[:username] and @node.auth[:username].length > 0
@@ -37,7 +37,7 @@ module Oxidized
end
def cmd cmd, expect=@node.prompt
- Log.debug "Telnet: #{cmd} @#{@node.name}"
+ Oxidized.logger.debug "Telnet: #{cmd} @#{@node.name}"
args = { 'String' => cmd }
args.merge!({ 'Match' => expect, 'Timeout' => @timeout }) if expect
@telnet.cmd args
diff --git a/lib/oxidized/job.rb b/lib/oxidized/job.rb
index 6fb60a8..0a4a24b 100644
--- a/lib/oxidized/job.rb
+++ b/lib/oxidized/job.rb
@@ -1,11 +1,11 @@
module Oxidized
class Job < Thread
attr_reader :start, :end, :status, :time, :node, :config
- def initialize node
+ def initialize(node)
@node = node
@start = Time.now.utc
- super do |node|
- @status, @config = node.run
+ super do
+ @status, @config = @node.run
@end = Time.now.utc
@time = @end - @start
end
diff --git a/lib/oxidized/log.rb b/lib/oxidized/log.rb
deleted file mode 100644
index e9ae1b4..0000000
--- a/lib/oxidized/log.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-module Oxidized
-
- begin
- require 'syslog/logger'
- Log = Syslog::Logger.new 'oxidized'
- Log.define_singleton_method(:file=){|arg|}
- rescue LoadError
- # 1.9.3 has no love for syslog
- require 'logger'
- class Logger < Logger
- def initialize target=STDOUT
- super target
- end
- def file= target
- FileUtils.mkdir_p File.dirname(target)
- @logdev = LogDevice.new target
- end
- end
- Log = Logger.new
- end
-
-end
diff --git a/lib/oxidized/model/junos.rb b/lib/oxidized/model/junos.rb
index 0e921d2..bb56481 100644
--- a/lib/oxidized/model/junos.rb
+++ b/lib/oxidized/model/junos.rb
@@ -3,7 +3,7 @@ class JunOS < Oxidized::Model
comment '# '
def telnet
- @input.class.to_s.match /Telnet/
+ @input.class.to_s.match(/Telnet/)
end
cmd :all do |cfg|
@@ -12,16 +12,16 @@ class JunOS < Oxidized::Model
cfg.lines.map { |line| line.rstrip }.join("\n") + "\n"
end
- cmd :secret do |cfg|
- cfg.gsub! /encrypted-password (\S+).*/, '<secret removed>'
- cfg.gsub! /community (\S+) {/, 'community <hidden> {'
+ cmd :secret do |cfg|
+ cfg.gsub!(/encrypted-password (\S+).*/, '<secret removed>')
+ cfg.gsub!(/community (\S+) {/, 'community <hidden> {')
cfg
end
cmd 'show configuration | display omit'
cmd 'show version' do |cfg|
- @model = $1 if cfg.match /^Model: (\S+)/
+ @model = $1 if cfg.match(/^Model: (\S+)/)
comment cfg
end
@@ -34,13 +34,11 @@ class JunOS < Oxidized::Model
out
end
- cmd 'show chassis hardware' do |cfg|
- comment cfg
- end
+ cmd('show chassis hardware') { |cfg| comment cfg }
cfg :telnet do
- username /^login:/
- password /^Password:/
+ username(/^login:/)
+ password(/^Password:/)
end
cfg :ssh do
diff --git a/lib/oxidized/model/model.rb b/lib/oxidized/model/model.rb
index e0e3941..ca851e3 100644
--- a/lib/oxidized/model/model.rb
+++ b/lib/oxidized/model/model.rb
@@ -11,7 +11,8 @@ module Oxidized
klass.instance_variable_set '@cfg', Hash.new { |h,k| h[k] = [] }
klass.instance_variable_set '@procs', Hash.new { |h,k| h[k] = [] }
klass.instance_variable_set '@expect', []
- klass.const_set :CFG, CFG
+ klass.instance_variable_set '@comment', nil
+ klass.instance_variable_set '@prompt', nil
end
def comment _comment='# '
return @comment if @comment
@@ -78,7 +79,7 @@ module Oxidized
attr_accessor :input, :node
def cmd string, &block
- out = @input.cmd string
+ out = "====================== #{string} ======================\n" + @input.cmd(string)
return false unless out
self.class.cmds[:all].each do |all_block|
out = instance_exec Oxidized::String.new(out), string, &all_block
diff --git a/lib/oxidized/node.rb b/lib/oxidized/node.rb
index df5b830..7a278a9 100644
--- a/lib/oxidized/node.rb
+++ b/lib/oxidized/node.rb
@@ -9,7 +9,7 @@ module Oxidized
attr_accessor :running, :user, :msg, :from, :stats, :retry
alias :running? :running
def initialize opt
- if CFG.debug == true or opt[:debug] == true
+ if Oxidized.config.debug == true or opt[:debug] == true
puts 'resolving DNS for %s...' % opt[:name]
end
@name = opt[:name]
@@ -24,7 +24,7 @@ module Oxidized
@vars = opt[:vars]
@stats = Stats.new
@retry = 0
- @repo = CFG.output.git.repo
+ @repo = Oxidized.config.output.git.repo
# model instance needs to access node instance
@model.node = self
@@ -58,9 +58,7 @@ module Oxidized
end
end
begin
- if input.connect self
- input.get
- end
+ input.connect(self) and input.get
rescue *rescue_fail.keys => err
resc = ''
if not level = rescue_fail[err.class]
@@ -68,7 +66,7 @@ module Oxidized
level = rescue_fail[resc]
resc = " (rescued #{resc})"
end
- Log.send(level, '%s raised %s%s with msg "%s"' % [self.ip, err.class, resc, err.message])
+ Oxidized.logger.send(level, '%s raised %s%s with msg "%s"' % [self.ip, err.class, resc, err.message])
return false
rescue => err
file = Oxidized::Config::Crash + '.' + self.ip.to_s
@@ -78,7 +76,7 @@ module Oxidized
fh.puts '-' * 50
fh.puts err.backtrace
end
- Log.error '%s raised %s with msg "%s", %s saved' % [self.ip, err.class, err.message, file]
+ Oxidized.logger.error '%s raised %s with msg "%s", %s saved' % [self.ip, err.class, err.message, file]
return false
end
end
@@ -126,19 +124,17 @@ module Oxidized
private
def resolve_prompt opt
- prompt = opt[:prompt]
- prompt ||= @model.prompt
- prompt ||= CFG.prompt
+ opt[:prompt] || @model.prompt || Oxidized.config.prompt
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 CFG.groups.has_key?(@group) and ['username', 'password'].all? {|e| CFG.groups[@group].has_key?(e)}
- [CFG.groups[@group].username, CFG.groups[@group].password]
- elsif ['username', 'password'].all? {|e| CFG.has_key?(e)}
- [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
@@ -149,7 +145,7 @@ module Oxidized
end
def resolve_input opt
- inputs = (opt[:input] or CFG.input.default)
+ inputs = (opt[:input] or 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}"
@@ -159,7 +155,7 @@ module Oxidized
end
def resolve_output opt
- output = (opt[:output] or CFG.output.default)
+ output = (opt[:output] or 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
@@ -167,7 +163,7 @@ module Oxidized
end
def resolve_model opt
- model = (opt[:model] or CFG.model)
+ model = (opt[:model] or Oxidized.config.model)
if not Oxidized.mgr.model[model]
Oxidized.mgr.add_model model or raise ModelNotFound, "#{model} not found for node #{ip}"
end
diff --git a/lib/oxidized/nodes.rb b/lib/oxidized/nodes.rb
index cb2ce7b..0c02d7a 100644
--- a/lib/oxidized/nodes.rb
+++ b/lib/oxidized/nodes.rb
@@ -1,15 +1,15 @@
module Oxidized
- require 'ipaddr'
- require 'oxidized/node'
- class Oxidized::NotSupported < OxidizedError; end
- class Oxidized::NodeNotFound < OxidizedError; end
+ require 'ipaddr'
+ require 'oxidized/node'
+ class Oxidized::NotSupported < OxidizedError; end
+ class Oxidized::NodeNotFound < OxidizedError; end
class Nodes < Array
attr_accessor :source
alias :put :unshift
def load node_want=nil
with_lock do
new = []
- @source = CFG.source.default
+ @source = Oxidized.config.source.default
Oxidized.mgr.add_source @source
Oxidized.mgr.source[@source].new.load.each do |node|
# we want to load specific node(s), not all of them
@@ -18,13 +18,13 @@ module Oxidized
_node = Node.new node
new.push _node
rescue ModelNotFound => err
- Log.error "node %s raised %s with message '%s'" % [node, err.class, err.message]
+ Oxidized.logger.error "node %s raised %s with message '%s'" % [node, err.class, err.message]
rescue Resolv::ResolvError => err
- Log.error "node %s is not resolvable, raised %s with message '%s'" % [node, err.class, err.message]
+ Oxidized.logger.error "node %s is not resolvable, raised %s with message '%s'" % [node, err.class, err.message]
end
end
size == 0 ? replace(new) : update_nodes(new)
- Log.info "Loaded #{size} nodes"
+ Oxidized.logger.info "Loaded #{size} nodes"
end
end
@@ -148,9 +148,9 @@ module Oxidized
end
end
end
-
+
public
-
+
def version node, group
with_lock do
i = find_node_index node
@@ -159,7 +159,7 @@ module Oxidized
output.version node, group
end
end
-
+
def get_version node, group, oid
with_lock do
i = find_node_index node
@@ -168,7 +168,7 @@ module Oxidized
output.get_version node, group, oid
end
end
-
+
def get_diff node, group, oid1, oid2
with_lock do
i = find_node_index node
diff --git a/lib/oxidized/output/file.rb b/lib/oxidized/output/file.rb
index eb915c3..bb13827 100644
--- a/lib/oxidized/output/file.rb
+++ b/lib/oxidized/output/file.rb
@@ -5,13 +5,13 @@ class OxidizedFile < Output
attr_reader :commitref
def initialize
- @cfg = CFG.output.file
+ @cfg = Oxidized.config.output.file
end
def setup
if @cfg.empty?
- CFGS.user.output.file.directory = File.join(Config::Root, 'configs')
- CFGS.save :user
+ Oxidized.asetus.user.output.file.directory = File.join(Config::Root, 'configs')
+ Oxidized.asetus.save :user
raise NoConfig, 'no output file config, edit ~/.config/oxidized/config'
end
end
diff --git a/lib/oxidized/output/git.rb b/lib/oxidized/output/git.rb
index fff3941..8b605f6 100644
--- a/lib/oxidized/output/git.rb
+++ b/lib/oxidized/output/git.rb
@@ -10,15 +10,15 @@ class Git < Output
attr_reader :commitref
def initialize
- @cfg = CFG.output.git
+ @cfg = Oxidized.config.output.git
end
def setup
if @cfg.empty?
- CFGS.user.output.git.user = 'Oxidized'
- CFGS.user.output.git.email = 'o@example.com'
- CFGS.user.output.git.repo = File.join(Config::Root, 'oxidized.git')
- CFGS.save :user
+ Oxidized.asetus.user.output.git.user = 'Oxidized'
+ Oxidized.asetus.user.output.git.email = 'o@example.com'
+ Oxidized.asetus.user.output.git.repo = File.join(Config::Root, 'oxidized.git')
+ Oxidized.asetus.save :user
raise NoConfig, 'no output git config, edit ~/.config/oxidized/config'
end
@cfg.repo = File.expand_path @cfg.repo
diff --git a/lib/oxidized/source/csv.rb b/lib/oxidized/source/csv.rb
index 5064e5e..a0ce848 100644
--- a/lib/oxidized/source/csv.rb
+++ b/lib/oxidized/source/csv.rb
@@ -1,17 +1,17 @@
module Oxidized
class CSV < Source
def initialize
- @cfg = CFG.source.csv
+ @cfg = Oxidized.config.source.csv
super
end
def setup
if @cfg.empty?
- CFGS.user.source.csv.file = File.join(Config::Root, 'router.db')
- CFGS.user.source.csv.delimiter = /:/
- CFGS.user.source.csv.map.name = 0
- CFGS.user.source.csv.map.model = 1
- CFGS.save :user
+ Oxidized.asetus.user.source.csv.file = File.join(Config::Root, 'router.db')
+ Oxidized.asetus.user.source.csv.delimiter = /:/
+ Oxidized.asetus.user.source.csv.map.name = 0
+ Oxidized.asetus.user.source.csv.map.model = 1
+ Oxidized.asetus.save :user
raise NoConfig, 'no source csv config, edit ~/.config/oxidized/config'
end
end
@@ -19,7 +19,7 @@ class CSV < Source
def load
nodes = []
open(File.expand_path @cfg.file).each_line do |line|
- next if line.match /^\s*#/
+ next if line.match(/^\s*#/)
data = line.chomp.split @cfg.delimiter
next if data.empty?
# map node parameters
diff --git a/lib/oxidized/source/http.rb b/lib/oxidized/source/http.rb
index d2e3ea6..6e765cf 100644
--- a/lib/oxidized/source/http.rb
+++ b/lib/oxidized/source/http.rb
@@ -1,7 +1,7 @@
module Oxidized
class HTTP < Source
def initialize
- @cfg = CFG.source.http
+ @cfg = Oxidized.config.source.http
super
end
diff --git a/lib/oxidized/source/source.rb b/lib/oxidized/source/source.rb
index 3c1f255..7862dd1 100644
--- a/lib/oxidized/source/source.rb
+++ b/lib/oxidized/source/source.rb
@@ -2,7 +2,7 @@ module Oxidized
class Source
class NoConfig < OxidizedError; end
def initialize
- @map = (CFG.model_map or {})
+ @map = (Oxidized.config.model_map or {})
end
def map_model model
@map.has_key?(model) ? @map[model] : model
diff --git a/lib/oxidized/source/sql.rb b/lib/oxidized/source/sql.rb
index cd9ef4a..fc1caa8 100644
--- a/lib/oxidized/source/sql.rb
+++ b/lib/oxidized/source/sql.rb
@@ -8,12 +8,12 @@ class SQL < Source
def setup
if @cfg.empty?
- CFGS.user.source.sql.adapter = 'sqlite'
- CFGS.user.source.sql.database = File.join(Config::Root, 'sqlite.db')
- CFGS.user.source.sql.table = 'devices'
- CFGS.user.source.sql.map.name = 'name'
- CFGS.user.source.sql.map.model = 'rancid'
- CFGS.save :user
+ Oxidized.asetus.user.source.sql.adapter = 'sqlite'
+ Oxidized.asetus.user.source.sql.database = File.join(Config::Root, 'sqlite.db')
+ Oxidized.asetus.user.source.sql.table = 'devices'
+ Oxidized.asetus.user.source.sql.map.name = 'name'
+ Oxidized.asetus.user.source.sql.map.model = 'rancid'
+ Oxidized.asetus.save :user
raise NoConfig, 'no source sql config, edit ~/.config/oxidized/config'
end
end
@@ -44,7 +44,7 @@ class SQL < Source
def initialize
super
- @cfg = CFG.source.sql
+ @cfg = Oxidized.config.source.sql
end
def connect
diff --git a/lib/oxidized/string.rb b/lib/oxidized/string.rb
index 4bdfbf2..8bcb082 100644
--- a/lib/oxidized/string.rb
+++ b/lib/oxidized/string.rb
@@ -16,7 +16,7 @@ module Oxidized
# sets @cmd and @name unless @name is already set
def set_cmd command
@cmd = command
- @name = @cmd.strip.gsub(/\s+/, '_') if @name == nil
+ @name ||= @cmd.strip.gsub(/\s+/, '_')
end
def initialize str=''
diff --git a/lib/oxidized/version.rb b/lib/oxidized/version.rb
new file mode 100644
index 0000000..38772b7
--- /dev/null
+++ b/lib/oxidized/version.rb
@@ -0,0 +1,3 @@
+module Oxidized
+ VERSION = '0.9.0'
+end
diff --git a/lib/oxidized/worker.rb b/lib/oxidized/worker.rb
index c886a5b..324dcb5 100644
--- a/lib/oxidized/worker.rb
+++ b/lib/oxidized/worker.rb
@@ -4,7 +4,7 @@ module Oxidized
class Worker
def initialize nodes
@nodes = nodes
- @jobs = Jobs.new CFG.threads, CFG.interval, @nodes
+ @jobs = Jobs.new(Oxidized.config.threads, Oxidized.config.interval, @nodes)
Thread.abort_on_exception = true
end
@@ -14,11 +14,11 @@ module Oxidized
ended.each { |job| process job }
@jobs.work
while @jobs.size < @jobs.want
- Log.debug "Jobs #{@jobs.size}, Want: #{@jobs.want}"
+ Oxidized.logger.debug "Jobs #{@jobs.size}, Want: #{@jobs.want}"
# ask for next node in queue non destructive way
nextnode = @nodes.first
unless nextnode.last.nil?
- break if nextnode.last.end + CFG.interval > Time.now.utc
+ break if nextnode.last.end + Oxidized.config.interval > Time.now.utc
end
# shift nodes and get the next node
node = @nodes.get
@@ -42,7 +42,7 @@ module Oxidized
output = node.output.new
if output.store node.name, job.config,
:msg => msg, :user => node.user, :group => node.group
- Log.info "Configuration updated for #{node.group}/#{node.name}"
+ Oxidized.logger.info "Configuration updated for #{node.group}/#{node.name}"
Oxidized.Hooks.handle :post_store, :node => node,
:job => job,
:commitref => output.commitref
@@ -50,7 +50,7 @@ module Oxidized
node.reset
else
msg = "#{node.name} status #{job.status}"
- if node.retry < CFG.retries
+ if node.retry < Oxidized.config.retries
node.retry += 1
msg += ", retry attempt #{node.retry}"
@nodes.next node.name
@@ -60,10 +60,10 @@ module Oxidized
Oxidized.Hooks.handle :node_fail, :node => node,
:job => job
end
- Log.warn msg
+ Oxidized.logger.warn msg
end
rescue NodeNotFound
- Log.warn "#{node.name} not found, removed while collecting?"
+ Oxidized.logger.warn "#{node.name} not found, removed while collecting?"
end
end
diff --git a/oxidized.gemspec b/oxidized.gemspec
index eef386a..808d508 100644
--- a/oxidized.gemspec
+++ b/oxidized.gemspec
@@ -1,6 +1,11 @@
+# coding: utf-8
+lib = File.expand_path('../lib', __FILE__)
+$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
+require 'oxidized/version'
+
Gem::Specification.new do |s|
s.name = 'oxidized'
- s.version = '0.9.0'
+ s.version = Oxidized::VERSION
s.licenses = %w( Apache-2.0 )
s.platform = Gem::Platform::RUBY
s.authors = [ 'Saku Ytti', 'Samer Abdel-Hafez', 'Anton Aksola' ]
@@ -9,14 +14,18 @@ Gem::Specification.new do |s|
s.summary = 'feeble attempt at rancid'
s.description = 'software to fetch configuration from network devices and store them'
s.rubyforge_project = s.name
- s.files = `git ls-files`.split("\n")
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
s.executables = %w( oxidized )
s.require_path = 'lib'
s.required_ruby_version = '>= 1.9.3'
s.add_runtime_dependency 'asetus', '~> 0.1'
s.add_runtime_dependency 'slop', '~> 3.5'
- s.add_runtime_dependency 'net-ssh', '~> 2.9', '>= 2.9.3'
+ 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_development_dependency 'minitest', '~> 5.8'
+ s.add_development_dependency 'mocha', '~> 1.1'
end
diff --git a/spec/githubrepo_spec.rb b/spec/githubrepo_spec.rb
new file mode 100644
index 0000000..9ad43e9
--- /dev/null
+++ b/spec/githubrepo_spec.rb
@@ -0,0 +1,98 @@
+require 'spec_helper'
+require 'rugged'
+require 'oxidized/hook/githubrepo'
+
+describe Oxidized::Node do
+ let(:credentials) { mock() }
+ let(:remote) { 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.setup_logger
+ end
+
+ describe "#fetch_and_merge_remote" do
+ before(:each) 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)
+ repo_head.expects(:name).returns('refs/heads/master')
+ gr.cfg = Oxidized.config.hooks.github_repo_hook
+ end
+
+ it "should not try to merge when there is no update in remote branch" do
+ repo.expects(:fetch).with('origin', ['refs/heads/master'], credentials: credentials).returns(Hash.new(0))
+ repo.expects(:branches).never
+ repo.expects(:head).returns(repo_head)
+ gr.fetch_and_merge_remote(repo).must_equal nil
+ end
+ describe "when there is update considering conflicts" do
+ let(:merge_index) { mock() }
+ let(:their_branch) { mock() }
+
+ before(:each) do
+ repo.expects(:fetch).with('origin', ['refs/heads/master'], credentials: credentials).returns({total_deltas: 1})
+ their_branch.expects(:target_id).returns(1)
+ repo_head.expects(:target_id).returns(2)
+ repo.expects(:merge_commits).with(2, 1).returns(merge_index)
+ repo.expects(:branches).returns({"origin/master" => their_branch})
+ end
+
+ it "should not try merging when there's conflict" do
+ repo.expects(:head).twice.returns(repo_head)
+ their_branch.expects(:name).returns("origin/master")
+ merge_index.expects(:conflicts?).returns(true)
+ Rugged::Commit.expects(:create).never
+ gr.fetch_and_merge_remote(repo).must_equal nil
+ end
+
+ it "should merge when there is no conflict" do
+ repo.expects(:head).times(3).returns(repo_head)
+ their_branch.expects(:target).returns("their_target")
+ their_branch.expects(:name).twice.returns("origin/master")
+ repo_head.expects(:target).returns("our_target")
+ merge_index.expects(:write_tree).with(repo).returns("tree")
+ merge_index.expects(:conflicts?).returns(false)
+ Rugged::Commit.expects(:create).with(repo, {
+ parents: ["our_target", "their_target"],
+ tree: "tree",
+ message: "Merge remote-tracking branch 'origin/master'",
+ update_ref: "HEAD"
+ }).returns(1)
+ gr.fetch_and_merge_remote(repo).must_equal 1
+ end
+ end
+ 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)
+ 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
+ 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
+ end
+ end
+end
diff --git a/spec/node_spec.rb b/spec/node_spec.rb
new file mode 100644
index 0000000..c568463
--- /dev/null
+++ b/spec/node_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+describe Oxidized::Node do
+ before(:each) do
+ Oxidized.stubs(:asetus).returns(Asetus.new)
+
+ Oxidized::Node.any_instance.stubs(:resolve_output)
+ @node = Oxidized::Node.new(name: 'example.com',
+ input: 'ssh',
+ output: 'git',
+ model: 'junos',
+ username: 'alma',
+ password: 'armud',
+ prompt: 'test_prompt')
+
+ end
+
+ describe '#new' do
+ it 'should resolve input' do
+ @node.input[0].to_s.split('::')[1].must_equal 'SSH'
+ end
+ it 'should resolve model' do
+ @node.model.class.must_equal JunOS
+ end
+ it 'should resolve username' do
+ @node.auth[:username].must_equal 'alma'
+ end
+ it 'should resolve password' do
+ @node.auth[:password].must_equal 'armud'
+ end
+ it 'should require prompt' do
+ @node.prompt.must_equal 'test_prompt'
+ end
+ end
+
+ describe '#run' do
+ it 'should fetch the configuration' do
+ stub_oxidized_ssh
+
+ status, _ = @node.run
+ status.must_equal :success
+ end
+ end
+end
diff --git a/spec/nodes_spec.rb b/spec/nodes_spec.rb
index 80ed300..5f2ef95 100644
--- a/spec/nodes_spec.rb
+++ b/spec/nodes_spec.rb
@@ -1,33 +1,44 @@
-require 'oxidized'
-Oxidized.mgr = Oxidized::Manager.new
+require 'spec_helper'
describe Oxidized::Nodes do
before(:each) do
+ Resolv.any_instance.stubs(:getaddress)
+ Oxidized.stubs(:asetus).returns(Asetus.new)
+ opts = {
+ input: 'ssh',
+ output: 'git',
+ model: 'junos',
+ username: 'alma',
+ password: 'armud',
+ prompt: 'test_prompt'
+ }
+
+ Oxidized::Node.any_instance.stubs(:resolve_output)
@nodes_org = %w(ltt-pe1.hel kes2-rr1.tku tor-peer1.oul
- hal-p2.tre sav-gr1-sw1.kuo psl-sec-pe1.hel).map { |e| Oxidized::Node.new(:name=>e) }
- @nodes = Oxidized::Nodes.new @nodes_org.dup
+ hal-p2.tre sav-gr1-sw1.kuo psl-sec-pe1.hel).map { |e| Oxidized::Node.new(opts.merge(name: e)) }
+ @node = @nodes_org.delete_at(0)
+ @nodes = Oxidized::Nodes.new(nodes: @nodes_org.dup)
end
describe '#put' do
it 'adds node to top of queue' do
- node = Oxidized::Node.new(:name=>'kst-p1.sto')
- @nodes.put node
- expect(@nodes).to eq [node] + @nodes_org
+ @nodes.put @node
+ @nodes.must_equal [@node] + @nodes_org
end
end
describe '#get' do
it 'returns node from top of queue' do
- expect(@nodes.get).to eq @nodes_org.first
+ @nodes.get.must_equal @nodes_org.first
end
it 'moves node from top to bottom' do
@nodes.get
- expect(@nodes).to end_with [@nodes_org.first]
+ @nodes.last.must_equal @nodes_org.first
end
it 'does not change node count' do
before = @nodes.size
@nodes.get
- expect(before).to eq @nodes.size
+ before.must_equal @nodes.size
end
end
@@ -35,12 +46,12 @@ describe Oxidized::Nodes do
it 'moves node to top of queue' do
node = @nodes[3]
@nodes.next node.name
- expect(@nodes).to start_with [node]
+ @nodes.first.must_equal node
end
it 'does not change node count' do
before = @nodes.size
@nodes.next @nodes[3].name
- expect(before).to eq @nodes.size
+ before.must_equal @nodes.size
end
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 0000000..28eb9d4
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,14 @@
+require 'minitest/autorun'
+require 'mocha/mini_test'
+require 'oxidized'
+
+Oxidized.mgr = Oxidized::Manager.new
+
+def stub_oxidized_ssh
+ Oxidized::SSH.any_instance.stubs(:connect).returns(true)
+ Oxidized::SSH.any_instance.stubs(:node).returns(@node)
+ Oxidized::SSH.any_instance.expects(:cmd).at_least(1).returns("this is a command output\nModel: mx960")
+ Oxidized::SSH.any_instance.stubs(:connect_cli).returns(true)
+ Oxidized::SSH.any_instance.stubs(:disconnect).returns(true)
+ Oxidized::SSH.any_instance.stubs(:disconnect_cli).returns(true)
+end