oxidized 0.16.3 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cba5f31454c2e6512d70c1688f81885e82a97ad9
4
- data.tar.gz: 40b8dfe37843039c8fef5cedc443a7101c78ffa1
3
+ metadata.gz: 4293285f8095f28f9f3d38dc2d606a03a58d163b
4
+ data.tar.gz: f108592ebdce488d10f06d7ee0e5545e3e117e32
5
5
  SHA512:
6
- metadata.gz: 8ca49b2ed227d5526c2d2093c794fd67a796822f7aeb30966060ac7ff46d12446b7c54fc6438b9e54e57d442422d92dccfaf5483e71141de066dfeda5d2726cc
7
- data.tar.gz: 2df84d9068fcdebd9c633d627a189ea5eaf915c9e04dc00af03b372dfba944c5ad8e1e0508ab93968295136d69a892b9098dbcc0218e2880558bfccf04bbb119
6
+ metadata.gz: aed22b16c6223c5b27a6b68476e25aea4154fcafd104a8567f7f19c4de38c8dddcd3fe18e1fd0950bc6e01e927d6af2fed514dd8f966eb33670f2a3517c6285f
7
+ data.tar.gz: 62a21f8caa39a27c853d2d56b3b96d35f6320db5cf6743ea76dd0cb7c6562729a0b96b5cc9b83524ad6edaac960dfb07ab88c7bffa526b459248603ba0e71bfa
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ # 0 17.3
2
+ - FEATURE: "nil", "false" and "true" in source (e.g. router.db) are interpeted as nil, false, true. Empty is now always considered empty string, instead of in some cases nil and some cases empty string.
3
+ - FEATURE: support tftp as input model (@MajesticFalcon)
4
+ - FEATURE: add alvarion model (@MajesticFalcon)
5
+ - FEATURE: detect if ssh wants password terminal/CLI prompt or not
6
+ - FEATURE: node (group, model, username, password) resolution refactoring, supports wider range of use-cases
7
+ - BUGFIX: fetch for file output (@danilopopeye)
8
+ - BUGFIX: net-ssh version specification
9
+ - BUGFIX: routeros, catos, pfsense
10
+
1
11
  # 0.16.3
2
12
  - FEATURE: pfsense support (by @stokbaek)
3
13
  - BUGFIX: cumulus prompt not working with default switch configs (by @nertwork)
data/Dockerfile CHANGED
@@ -3,7 +3,7 @@ MAINTAINER Samer Abdel-Hafez <sam@arahant.net>
3
3
 
4
4
  RUN add-apt-repository ppa:brightbox/ruby-ng && \
5
5
  apt-get update && \
6
- apt-get install -y ruby2.1 ruby2.1-dev libsqlite3-dev libssl-dev pkg-config make cmake
6
+ apt-get install -y ruby2.3 ruby2.3-dev libsqlite3-dev libssl-dev pkg-config make cmake
7
7
 
8
8
  RUN gem install oxidized oxidized-web --no-ri --no-rdoc
9
9
 
data/Gemfile.lock CHANGED
@@ -1,9 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- oxidized (0.14.3)
4
+ oxidized (0.17.0)
5
5
  asetus (~> 0.1)
6
- net-ssh (>= 3.0.0, < 3.1)
6
+ net-ssh (~> 3.0.2)
7
+ net-telnet
7
8
  rugged (~> 0.21, >= 0.21.4)
8
9
  slop (~> 3.5)
9
10
 
@@ -14,16 +15,17 @@ GEM
14
15
  coderay (1.1.0)
15
16
  metaclass (0.0.4)
16
17
  method_source (0.8.2)
17
- minitest (5.8.3)
18
+ minitest (5.9.0)
18
19
  mocha (1.1.0)
19
20
  metaclass (~> 0.0.1)
20
21
  net-ssh (3.0.2)
22
+ net-telnet (0.1.1)
21
23
  pry (0.10.3)
22
24
  coderay (~> 1.1.0)
23
25
  method_source (~> 0.8.1)
24
26
  slop (~> 3.4)
25
- rake (10.4.2)
26
- rugged (0.24.0)
27
+ rake (10.5.0)
28
+ rugged (0.23.3)
27
29
  slop (3.6.0)
28
30
 
29
31
  PLATFORMS
@@ -38,4 +40,4 @@ DEPENDENCIES
38
40
  rake (~> 10.0)
39
41
 
40
42
  BUNDLED WITH
41
- 1.12.4
43
+ 1.11.2
data/README.md CHANGED
@@ -27,13 +27,16 @@ Oxidized is a network device configuration backup tool. It's a RANCID replacemen
27
27
  * [Privileged mode](#privileged-mode)
28
28
  * [Disabling SSH exec channels](#disabling-ssh-exec-channels)
29
29
  * [Source: CSV](#source-csv)
30
- * [Source: SQLite](#source-sqlite)
30
+ * [Source: SQL](#source-sql)
31
+ * [Source: SQLite](#source-sqlite)
32
+ * [Source: Mysql](#source-mysql)
31
33
  * [Source: HTTP](#source-http)
32
34
  * [Output: GIT](#output-git)
33
35
  * [Output: HTTP](#output-http)
34
36
  * [Output: File](#output-file)
35
37
  * [Output types](#output-types)
36
38
  * [Advanced Configuration](#advanced-configuration)
39
+ * [Advanced Group Configuration](#advanced-group-configuration)
37
40
  7. [Ruby API](#ruby-api)
38
41
  * [Input](#input)
39
42
  * [Output](#output)
@@ -41,99 +44,103 @@ Oxidized is a network device configuration backup tool. It's a RANCID replacemen
41
44
  * [Model](#model)
42
45
 
43
46
  # Supported OS types
47
+ * Vendor
48
+ * OS - model_name
44
49
 
45
50
  * A10 Networks
46
- * ACOS
51
+ * ACOS - acos
47
52
  * Alcatel-Lucent
48
- * AOS
49
- * AOS7
50
- * ISAM
53
+ * AOS - aos
54
+ * AOS7 - aos7
55
+ * ISAM - isam
51
56
  * Wireless
57
+ * Alvarion
58
+ * BreezeACCESS - alvarion
52
59
  * Arista
53
- * EOS
60
+ * EOS - eos
54
61
  * Arris
55
- * C4CMTS
62
+ * C4CMTS - c4cmts
56
63
  * Aruba
57
- * AOSW
64
+ * AOSW - aosw
58
65
  * Brocade
59
- * FabricOS
60
- * Ironware
61
- * NOS (Network Operating System)
62
- * Vyatta
63
- * 6910
66
+ * FabricOS - fabricos
67
+ * Ironware - ironware
68
+ * NOS (Network Operating System) - nos
69
+ * Vyatta - vyatta
70
+ * 6910 - br6910
64
71
  * Check Point
65
- * GaiaOS
72
+ * GaiaOS - gaiaos
66
73
  * Ciena
67
- * SOAS
74
+ * SOAS - saos
68
75
  * Cisco
69
- * AireOS
70
- * ASA
71
- * CatOS
72
- * IOS
73
- * IOSXR
74
- * NXOS
75
- * SMB (Nikola series)
76
+ * AireOS - aireos
77
+ * ASA - asa
78
+ * CatOS - catos
79
+ * IOS - ios
80
+ * IOSXR - iosxr
81
+ * NXOS - nxos
82
+ * SMB (Nikola series)
76
83
  * Citrix
77
- * NetScaler (Virtual Applicance)
84
+ * NetScaler (Virtual Applicance) - netscaler
78
85
  * Coriant (former Tellabs)
79
- * TMOS (8800)
80
- * 8600
86
+ * TMOS (8800) - tmos
87
+ * 8600 -
81
88
  * Cumulus
82
89
  * Linux
83
90
  * DataCom
84
91
  * DmSwitch 3000
85
92
  * DELL
86
- * PowerConnect
87
- * AOSW
93
+ * PowerConnect - powerconnect
94
+ * AOSW - aosw
88
95
  * Ericsson/Redback
89
- * IPOS (former SEOS)
96
+ * IPOS (former SEOS) - ipos
90
97
  * Extreme Networks
91
- * XOS
98
+ * XOS - xos
92
99
  * WM
93
100
  * F5
94
101
  * TMOS
95
102
  * Force10
96
- * DNOS
97
- * FTOS
103
+ * DNOS - dnos
104
+ * FTOS - ftos
98
105
  * FortiGate
99
- * FortiOS
106
+ * FortiOS - fortios
100
107
  * HP
101
- * Comware (HP A-series, H3C, 3Com)
102
- * Procurve
108
+ * Comware (HP A-series, H3C, 3Com) - comware
109
+ * Procurve - procurve
103
110
  * Huawei
104
111
  * VRP
105
112
  * Juniper
106
113
  * JunOS
107
114
  * ScreenOS (Netscreen)
108
115
  * Mellanox
109
- * MLNX-OS
116
+ * MLNX-OS - mlnxos
110
117
  * Mikrotik
111
- * RouterOS
118
+ * RouterOS - routeros
112
119
  * Motorola
113
120
  * RFS
114
121
  * MRV
115
- * MasterOS
122
+ * MasterOS - masteros
116
123
  * Netonix
117
- * WISP Switch (As Netonix)
124
+ * WISP Switch (As Netonix) - netonix
118
125
  * Nokia (formerly TiMetra, Alcatel, Alcatel-Lucent)
119
- * SR OS (TiMOS)
126
+ * SR OS (TiMOS) - timos
120
127
  * Opengear
121
- * Opengear
128
+ * Opengear - opengear
122
129
  * Palo Alto
123
- * PANOS
124
- * pfSense
130
+ * PANOS - panos
131
+ * pfSense - pfsense
125
132
  * Quanta
126
- * Quanta / VxWorks 6.6 (1.1.0.8)
133
+ * Quanta / VxWorks 6.6 (1.1.0.8) - quantaos
127
134
  * Supermicro
128
- * Supermicro
135
+ * Supermicro - supermicro
129
136
  * Ubiquiti
130
- * AirOS
131
- * Edgeos
132
- * EdgeSwitch
137
+ * AirOS - airos
138
+ * Edgeos - edgeos
139
+ * EdgeSwitch - edgeswitch
133
140
  * Watchguard
134
- * Fireware OS
141
+ * Fireware OS - firewareos
135
142
  * Zyxel
136
- * ZyNOS
143
+ * ZyNOS - zynos
137
144
 
138
145
 
139
146
  # Installation
@@ -147,7 +154,7 @@ gem install oxidized-script oxidized-web # if you don't install oxidized-web, ma
147
154
  ```
148
155
 
149
156
  ## CentOS, Oracle Linux, Red Hat Linux
150
- On CentOS 6 / RHEL 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
157
+ On CentOS 6 / RHEL 6, install Ruby greater than 1.9.3 (for Ruby 2.1.2 installation instructions see "Installing Ruby 2.1.2 using RVM"), then install Oxidized dependencies
151
158
  ```shell
152
159
  yum install cmake sqlite-devel openssl-devel libssh2-devel
153
160
  ```
@@ -254,46 +261,54 @@ rvm use --default 2.1.2
254
261
  ```
255
262
 
256
263
  # Running with Docker
257
- 1. clone git repo:
264
+
265
+ clone git repo:
258
266
 
259
267
  ```
260
- root@bla:~# git clone https://github.com/ytti/oxidized
261
- ```
262
- 2. build container locally:
268
+ git clone https://github.com/ytti/oxidized
263
269
  ```
264
- root@bla:~# docker build -q -t oxidized/oxidized:latest oxidized/
270
+
271
+ build container locally:
272
+
265
273
  ```
266
- 3. create config directory in main system:
274
+ docker build -q -t oxidized/oxidized:latest oxidized/
267
275
  ```
268
- root@bla~:# mkdir /etc/oxidized
276
+
277
+ create config directory in main system:
278
+
269
279
  ```
270
- 4. run container the first time:
280
+ mkdir /etc/oxidized
271
281
  ```
272
- root@bla:~# docker run -v /etc/oxidized:/root/.config/oxidized -p 8888:8888/tcp -t oxidized/oxidized:latest oxidized
282
+
283
+ run container the first time:
284
+ _Note: this step in only needed for creating Oxidized's configuration file and can be skipped if you already have it
285
+
273
286
  ```
274
- 5. add 'router.db' to /etc/oxidized:
287
+ docker run --rm -v /etc/oxidized:/root/.config/oxidized -p 8888:8888/tcp -t oxidized/oxidized:latest oxidized
275
288
  ```
276
- root@bla:~# vim /etc/oxidized/router.db
277
- [ ... ]
278
- root@bla:~#
289
+
290
+ create the `/etc/oxidized/router.db`
291
+
279
292
  ```
280
- 6. run container again:
293
+ vim /etc/oxidized/router.db
281
294
  ```
282
- root@bla:~# docker run -v /etc/oxidized:/root/.config/oxidized -p 8888:8888/tcp -t oxidized/oxidized:latest
283
- oxidized[1]: Oxidized starting, running as pid 1
284
- oxidized[1]: Loaded 1 nodes
285
- Puma 2.13.4 starting...
286
- * Min threads: 0, max threads: 16
287
- * Environment: development
288
- * Listening on tcp://0.0.0.0:8888
289
- ^C
290
295
 
291
- root@bla:~#
296
+ run container again:
297
+
298
+ ```
299
+ docker run -v /etc/oxidized:/root/.config/oxidized -p 8888:8888/tcp -t oxidized/oxidized:latest
300
+ oxidized[1]: Oxidized starting, running as pid 1
301
+ oxidized[1]: Loaded 1 nodes
302
+ Puma 2.13.4 starting...
303
+ * Min threads: 0, max threads: 16
304
+ * Environment: development
305
+ * Listening on tcp://0.0.0.0:8888
292
306
  ```
293
307
 
294
308
  If you want to have the config automatically reloaded (e.g. when using a http source that changes)
309
+
295
310
  ```
296
- root@bla:~# docker run -v /etc/oxidized:/root/.config/oxidized -p 8888:8888/tcp -e CONFIG_RELOAD_INTERVAL=3600 -t oxidized/oxidized:latest
311
+ docker run -v /etc/oxidized:/root/.config/oxidized -p 8888:8888/tcp -e CONFIG_RELOAD_INTERVAL=3600 -t oxidized/oxidized:latest
297
312
  ```
298
313
 
299
314
  ## Cookbook
@@ -386,6 +401,31 @@ vars_map:
386
401
  ssh_proxy: 3
387
402
  ...
388
403
  ```
404
+ ### Source: SQL
405
+ Oxidized uses the `sequel` ruby gem. You can use a variety of databases that aren't explicitly listed. For more information visit https://github.com/jeremyevans/sequel Make sure you have the correct adapter!
406
+ ### Source: MYSQL
407
+
408
+ ```sudo apt-get install libmysqlclient-dev```
409
+
410
+ The values correspond to your fields in the DB such that ip, model, etc are field names in the DB
411
+
412
+ ```
413
+ source:
414
+ default: sql
415
+ sql:
416
+ adapter: mysql2
417
+ database: oxidized
418
+ table: nodes
419
+ username: root
420
+ password: rootpass
421
+ map:
422
+ name: ip
423
+ model: model
424
+ username: username
425
+ password: password
426
+ vars_map:
427
+ enable: enable
428
+ ```
389
429
 
390
430
  ### Source: SQLite
391
431
 
@@ -572,7 +612,7 @@ rest: 10.0.0.1:8000/oxidized
572
612
 
573
613
  ### Advanced Configuration
574
614
 
575
- Below is an advanced example configuration. You will be able to (optinally) override options per device. The router.db format used is ```hostname:model:username:password:enable_password```. Hostname and model will be the only required options, all others override the global configuration sections.
615
+ Below is an advanced example configuration. You will be able to (optionally) override options per device. The router.db format used is ```hostname:model:username:password:enable_password```. Hostname and model will be the only required options, all others override the global configuration sections.
576
616
 
577
617
  ```
578
618
  ---
@@ -617,6 +657,28 @@ source:
617
657
  model_map:
618
658
  cisco: ios
619
659
  juniper: junos
660
+
661
+ ```
662
+
663
+ ### Advanced Group Configuration
664
+
665
+ For group specific credentials
666
+
667
+ ```
668
+ groups:
669
+ mikrotik:
670
+ username: admin
671
+ password: blank
672
+ ubiquiti:
673
+ username: ubnt
674
+ password: ubnt
675
+ ```
676
+ and add group mapping
677
+ ```
678
+ map:
679
+ model: 0
680
+ name: 1
681
+ group: 2
620
682
  ```
621
683
 
622
684
  # Hooks
@@ -716,7 +778,7 @@ The following objects exist in Oxidized.
716
778
  ## Input
717
779
  * gets config from nodes
718
780
  * must implement 'connect', 'get', 'cmd'
719
- * 'ssh' and 'telnet' implemented
781
+ * 'ssh', 'telnet, ftp, and tftp' implemented
720
782
 
721
783
  ## Output
722
784
  * stores config
data/Rakefile CHANGED
@@ -19,10 +19,10 @@ task :test do
19
19
  end
20
20
  end
21
21
 
22
- desc 'Install gem'
23
- task :install => :build do
24
- system "sudo -Es sh -c \'umask 022; gem install gems/#{file}\'"
25
- end
22
+ ## desc 'Install gem'
23
+ ## task :install => :build do
24
+ ## system "sudo -Es sh -c \'umask 022; gem install gems/#{file}\'"
25
+ ## end
26
26
 
27
27
  desc 'Remove gems'
28
28
  task :clean do
data/extra/rest_client.rb CHANGED
@@ -2,8 +2,30 @@ module Oxidized
2
2
  class RestClient
3
3
  require 'net/http'
4
4
  require 'json'
5
- HOST = 'localhost'
6
- PORT = 8888
5
+ require 'uri'
6
+ require 'asetus'
7
+
8
+ class Config
9
+ Root = Root = ENV['OXIDIZED_HOME'] || File.join(ENV['HOME'], '.config', 'oxidized')
10
+ end
11
+
12
+ CFGS = Asetus.new :name=>'oxidized', :load=>false, :key_to_s=>true
13
+ CFGS.default.rest = '127.0.0.1:8888'
14
+
15
+ begin
16
+ CFGS.load
17
+ rescue => error
18
+ raise InvalidConfig, "Error loading config: #{error.message}"
19
+ end
20
+
21
+ restcfg = CFGS.cfg.rest
22
+ unless restcfg.match(/^http:\/\//)
23
+ restcfg.insert(0, 'http://')
24
+ end
25
+
26
+ HOST = URI(restcfg).host
27
+ PORT = URI(restcfg).port
28
+ PATH = URI(restcfg).path
7
29
 
8
30
  class << self
9
31
  def next opt={}, host=HOST, port=PORT
@@ -18,7 +40,7 @@ module Oxidized
18
40
 
19
41
  def next opt
20
42
  data = JSON.dump opt
21
- @web.put '/node/next/' + opt[:name].to_s, data
43
+ @web.put PATH + '/node/next/' + opt[:name].to_s, data
22
44
  end
23
45
 
24
46
  end
@@ -42,7 +42,7 @@ module Oxidized
42
42
  unless @exec
43
43
  shell_open @ssh
44
44
  begin
45
- @username ? shell_login : expect(@node.prompt)
45
+ login
46
46
  rescue Timeout::Error
47
47
  raise PromptUndetect, [ @output, 'not matching configured prompt', @node.prompt ].join(' ')
48
48
  end
@@ -102,13 +102,18 @@ module Oxidized
102
102
  end
103
103
  end
104
104
 
105
- # Cisco WCS has extremely dubious SSH implementation, SSH auth is always
106
- # success, it always opens shell and then run auth in shell. I guess
107
- # they'll never support exec() :)
108
- def shell_login
109
- expect username
110
- cmd @node.auth[:username], password
111
- cmd @node.auth[:password]
105
+ # some models have SSH auth or terminal auth based on version of code
106
+ # if SSH is configured for terminal auth, we'll still try to detect prompt
107
+ def login
108
+ if @username
109
+ match = expect username, @node.prompt
110
+ if match == username
111
+ cmd @node.auth[:username], password
112
+ cmd @node.auth[:password]
113
+ end
114
+ else
115
+ expect @node.prompt
116
+ end
112
117
  end
113
118
 
114
119
  def exec state=nil
@@ -123,14 +128,18 @@ module Oxidized
123
128
  @output
124
129
  end
125
130
 
126
- def expect regexp
127
- Oxidized.logger.debug "lib/oxidized/input/ssh.rb: expecting #{regexp.inspect} at #{node.name}"
131
+ def expect *regexps
132
+ regexps = [regexps].flatten
133
+ Oxidized.logger.debug "lib/oxidized/input/ssh.rb: expecting #{regexps.inspect} at #{node.name}"
128
134
  Timeout::timeout(Oxidized.config.timeout) do
129
135
  @ssh.loop(0.1) do
130
136
  sleep 0.1
131
- not @output.match regexp
137
+ match = regexps.find { |regexp| @output.match regexp }
138
+ return match if match
139
+ true
132
140
  end
133
141
  end
134
142
  end
143
+
135
144
  end
136
145
  end
@@ -0,0 +1,41 @@
1
+ module Oxidized
2
+ require 'stringio'
3
+ require_relative 'cli'
4
+
5
+ begin
6
+ require 'net/tftp'
7
+ rescue LoadError
8
+ raise OxidizedError, 'net/tftp not found: sudo gem install net-tftp'
9
+ end
10
+
11
+ class TFTP < Input
12
+
13
+ include Input::CLI
14
+
15
+ # TFTP utilizes UDP, there is not a connection. We simply specify an IP and send/receive data.
16
+ def connect node
17
+ @node = node
18
+
19
+ @node.model.cfg['tftp'].each { |cb| instance_exec(&cb) }
20
+ @log = File.open(Oxidized::Config::Log + "/#{@node.ip}-tftp", 'w') if Oxidized.config.input.debug?
21
+ @tftp = Net::TFTP.new @node.ip
22
+ end
23
+
24
+ def cmd file
25
+ Oxidized.logger.debug "TFTP: #{file} @ #{@node.name}"
26
+ config = StringIO.new
27
+ @tftp.getbinary file, config
28
+ config.rewind
29
+ config.read
30
+ end
31
+
32
+ private
33
+
34
+ def disconnect
35
+ # TFTP uses UDP, there is no connection to close
36
+ ensure
37
+ @log.close if Oxidized.config.input.debug?
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,13 @@
1
+ class Alvarion < Oxidized::Model
2
+
3
+ # Used in Alvarion wisp equipment
4
+
5
+ # Run this command as an instance of Model so we can access node
6
+ pre do
7
+ cmd "#{node.auth[:password]}.cfg"
8
+ end
9
+
10
+
11
+ cfg :tftp {}
12
+
13
+ end
@@ -1,6 +1,6 @@
1
1
  class Catos < Oxidized::Model
2
2
 
3
- prompt /^[\w.@-]+> \(enable\) $/
3
+ prompt /^[\w.@-]+>\s?(\(enable\) )?$/
4
4
  comment '# '
5
5
 
6
6
  cmd :all do |cfg|
@@ -28,8 +28,15 @@ class Catos < Oxidized::Model
28
28
  password /^Password:/
29
29
  end
30
30
 
31
- cfg :ssh, :telnet do
31
+ cfg :telnet, :ssh do
32
32
  post_login 'set length 0'
33
+ # preferred way to handle additional passwords
34
+ if vars :enable
35
+ post_login do
36
+ send "enable\n"
37
+ cmd vars(:enable)
38
+ end
39
+ end
33
40
  pre_logout 'exit'
34
41
  end
35
42
 
@@ -2,21 +2,12 @@ class PfSense < Oxidized::Model
2
2
 
3
3
  # use other use than 'admin' user, 'admin' user cannot get ssh/exec. See issue #535
4
4
 
5
- comment '# '
6
-
7
- #add a comment in the final conf
8
- def add_comment comment
9
- "\n###### #{comment} ######\n"
10
- end
11
-
12
5
  cmd :all do |cfg|
13
6
  cfg.each_line.to_a[1..-2].join
14
7
  end
15
8
 
16
- #show the persistent configuration
17
- pre do
18
- cfg = add_comment 'Configuration'
19
- cfg += cmd 'cat /cf/conf/config.xml'
9
+ cmd 'cat /cf/conf/config.xml' do |cfg|
10
+ cfg.gsub! /\s<revision>\s*<time>\d*<\/time>\s*.*\s*.*\s*<\/revision>/, ''
20
11
  end
21
12
 
22
13
  cfg :ssh do
@@ -1,5 +1,5 @@
1
1
  class RouterOS < Oxidized::Model
2
- prompt /\[\w+@\S+\]\s?>\s?$/
2
+ prompt /\[\w+@\S+(\s?\S+)*\]\s?>\s?$/
3
3
  comment "# "
4
4
 
5
5
  cmd '/system routerboard print' do |cfg|
@@ -8,6 +8,7 @@ class RouterOS < Oxidized::Model
8
8
 
9
9
  cmd '/export' do |cfg|
10
10
  cfg.gsub! /\x1B\[([0-9]{1,3}((;[0-9]{1,3})*)?)?[m|K]/, '' # strip ANSI colours
11
+ cfg.gsub! /\\\r\n\s+/, '' # strip new line
11
12
  cfg = cfg.split("\n").select { |line| not line[/^\#\s\w{3}\/\d{2}\/\d{4}.*$/] }
12
13
  cfg.join("\n") + "\n"
13
14
  end
data/lib/oxidized/node.rb CHANGED
@@ -128,24 +128,15 @@ module Oxidized
128
128
  end
129
129
 
130
130
  def resolve_auth opt
131
- # Resolve configured username/password, give priority to group level configuration
132
- # TODO: refactor to use revised behaviour of Asetus
133
- cfg_username, cfg_password =
134
- if Oxidized.config.groups.has_key?(@group) and ['username', 'password'].all? {|e| Oxidized.config.groups[@group].has_key?(e)}
135
- [Oxidized.config.groups[@group].username, Oxidized.config.groups[@group].password]
136
- elsif ['username', 'password'].all? {|e| Oxidized.config.has_key?(e)}
137
- [Oxidized.config.username, Oxidized.config.password]
138
- else
139
- [nil, nil]
140
- end
141
- auth = {}
142
- auth[:username] = (opt[:username] or cfg_username)
143
- auth[:password] = (opt[:password] or cfg_password)
144
- auth
131
+ # Resolve configured username/password
132
+ {
133
+ username: resolve_key(:username, opt),
134
+ password: resolve_key(:password, opt),
135
+ }
145
136
  end
146
137
 
147
138
  def resolve_input opt
148
- inputs = (opt[:input] or Oxidized.config.input.default)
139
+ inputs = resolve_key :input, opt, Oxidized.config.input.default
149
140
  inputs.split(/\s*,\s*/).map do |input|
150
141
  if not Oxidized.mgr.input[input]
151
142
  Oxidized.mgr.add_input input or raise MethodNotFound, "#{input} not found for node #{ip}"
@@ -155,7 +146,7 @@ module Oxidized
155
146
  end
156
147
 
157
148
  def resolve_output opt
158
- output = (opt[:output] or Oxidized.config.output.default)
149
+ output = resolve_key :output, opt, Oxidized.config.output.default
159
150
  if not Oxidized.mgr.output[output]
160
151
  Oxidized.mgr.add_output output or raise MethodNotFound, "#{output} not found for node #{ip}"
161
152
  end
@@ -163,7 +154,7 @@ module Oxidized
163
154
  end
164
155
 
165
156
  def resolve_model opt
166
- model = (opt[:model] or Oxidized.config.model)
157
+ model = resolve_key :model, opt
167
158
  if not Oxidized.mgr.model[model]
168
159
  Oxidized.logger.debug "lib/oxidized/node.rb: Loading model #{model.inspect}"
169
160
  Oxidized.mgr.add_model model or raise ModelNotFound, "#{model} not found for node #{ip}"
@@ -187,6 +178,33 @@ module Oxidized
187
178
  end
188
179
  end
189
180
 
181
+ def resolve_key key, opt, global=nil
182
+ # resolve key, first get global, then get group then get node config
183
+ key_sym = key.to_sym
184
+ key_str = key.to_s
185
+ value = global
186
+ Oxidized.logger.debug "node.rb: resolving node key '#{key}', with passed global value of '#{value}' and node value '#{opt[key_sym]}'"
187
+
188
+ #global
189
+ if not value and Oxidized.config.has_key?(key_str)
190
+ value = Oxidized.config[key_str]
191
+ Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from global"
192
+ end
193
+
194
+ #group
195
+ if Oxidized.config.groups.has_key?(@group)
196
+ if Oxidized.config.groups[@group].has_key?(key_str)
197
+ value = Oxidized.config.groups[@group][key_str]
198
+ Oxidized.logger.debug "node.rb: setting node key '#{key}' to value '#{value}' from group"
199
+ end
200
+ end
201
+
202
+ #node
203
+ value = opt[key_sym] || value
204
+ Oxidized.logger.debug "node.rb: returning node key '#{key}' with value '#{value}'"
205
+ value
206
+ end
207
+
190
208
  def is_git? opt
191
209
  (opt[:output] || Oxidized.config.output.default) == 'git'
192
210
  end
@@ -17,7 +17,7 @@ class OxidizedFile < Output
17
17
  end
18
18
 
19
19
  def store node, outputs, opt={}
20
- file = @cfg.directory
20
+ file = File.expand_path @cfg.directory
21
21
  if opt[:group]
22
22
  file = File.join File.dirname(file), opt[:group]
23
23
  end
@@ -28,14 +28,16 @@ class OxidizedFile < Output
28
28
  end
29
29
 
30
30
  def fetch node, group
31
- cfg_dir = @cfg.directory
31
+ cfg_dir = File.expand_path @cfg.directory
32
+ node_name = node.name
33
+
32
34
  if group # group is explicitly defined by user
33
- IO.readlines File.join(cfg_dir, group, node)
35
+ IO.readlines File.join(cfg_dir, group, node_name)
34
36
  else
35
- if File.exists? File.join(cfg_dir, node) # node configuration file is stored on base directory
36
- IO.readlines File.join(cfg_dir, node)
37
+ if File.exists? File.join(cfg_dir, node_name) # node configuration file is stored on base directory
38
+ IO.readlines File.join(cfg_dir, node_name)
37
39
  else
38
- path = Dir.glob File.join(cfg_dir, '**', node) # fetch node in all groups
40
+ path = Dir.glob File.join(cfg_dir, '**', node_name) # fetch node in all groups
39
41
  return nil if path[0].nil?
40
42
  open(path[0], 'r').readlines
41
43
  end
@@ -20,7 +20,7 @@ class CSV < Source
20
20
  nodes = []
21
21
  open(File.expand_path @cfg.file).each_line do |line|
22
22
  next if line.match(/^\s*#/)
23
- data = line.chomp.split @cfg.delimiter
23
+ data = line.chomp.split(@cfg.delimiter, -1)
24
24
  next if data.empty?
25
25
  # map node parameters
26
26
  keys = {}
@@ -29,9 +29,11 @@ class CSV < Source
29
29
  end
30
30
  keys[:model] = map_model keys[:model] if keys.key? :model
31
31
 
32
- # map node specific vars, empty value is considered as nil
32
+ # map node specific vars
33
33
  vars = {}
34
- @cfg.vars_map.each { |key, position| vars[key.to_sym] = data[position].to_s.empty? ? nil : data[position] }
34
+ @cfg.vars_map.each do |key, position|
35
+ vars[key.to_sym] = node_var_interpolate data[position]
36
+ end
35
37
  keys[:vars] = vars unless vars.empty?
36
38
 
37
39
  nodes << keys
@@ -43,9 +43,11 @@ class HTTP < Source
43
43
  end
44
44
  keys[:model] = map_model keys[:model] if keys.key? :model
45
45
 
46
- # map node specific vars, empty value is considered as nil
46
+ # map node specific vars
47
47
  vars = {}
48
- @cfg.vars_map.each { |key, position| vars[key.to_sym] = line[position].to_s.empty? ? nil : line[position] }
48
+ @cfg.vars_map.each do |key, position|
49
+ vars[key.to_sym] = node_var_interpolate line[position]
50
+ end
49
51
  keys[:vars] = vars unless vars.empty?
50
52
 
51
53
  nodes << keys
@@ -1,11 +1,23 @@
1
1
  module Oxidized
2
2
  class Source
3
3
  class NoConfig < OxidizedError; end
4
+
4
5
  def initialize
5
6
  @map = (Oxidized.config.model_map or {})
6
7
  end
8
+
7
9
  def map_model model
8
10
  @map.has_key?(model) ? @map[model] : model
9
11
  end
12
+
13
+ def node_var_interpolate var
14
+ case var
15
+ when "nil" then nil
16
+ when "false" then false
17
+ when "true" then true
18
+ else var
19
+ end
20
+ end
21
+
10
22
  end
11
23
  end
@@ -31,7 +31,9 @@ class SQL < Source
31
31
 
32
32
  # map node specific vars
33
33
  vars = {}
34
- @cfg.vars_map.each { |key, sql_column| vars[key.to_sym] = node[sql_column.to_sym] }
34
+ @cfg.vars_map.each do |key, sql_column|
35
+ vars[key.to_sym] = node_var_interpolate node[sql_column.to_sym]
36
+ end
35
37
  keys[:vars] = vars unless vars.empty?
36
38
 
37
39
  nodes << keys
@@ -1,3 +1,3 @@
1
1
  module Oxidized
2
- VERSION = '0.16.3'
2
+ VERSION = '0.17.0'
3
3
  end
data/oxidized.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
21
21
  s.required_ruby_version = '>= 2.0.0'
22
22
  s.add_runtime_dependency 'asetus', '~> 0.1'
23
23
  s.add_runtime_dependency 'slop', '~> 3.5'
24
- s.add_runtime_dependency 'net-ssh', '>= 3.0.0', '<3.1'
24
+ s.add_runtime_dependency 'net-ssh', '~> 3.0.2'
25
25
  s.add_runtime_dependency 'rugged', '~> 0.21', '>= 0.21.4'
26
26
 
27
27
  if defined?(RUBY_VERSION) && RUBY_VERSION > '2.3'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oxidized
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.3
4
+ version: 0.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Saku Ytti
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2016-08-25 00:00:00.000000000 Z
13
+ date: 2016-09-28 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: asetus
@@ -44,22 +44,16 @@ dependencies:
44
44
  name: net-ssh
45
45
  requirement: !ruby/object:Gem::Requirement
46
46
  requirements:
47
- - - ">="
48
- - !ruby/object:Gem::Version
49
- version: 3.0.0
50
- - - "<"
47
+ - - "~>"
51
48
  - !ruby/object:Gem::Version
52
- version: '3.1'
49
+ version: 3.0.2
53
50
  type: :runtime
54
51
  prerelease: false
55
52
  version_requirements: !ruby/object:Gem::Requirement
56
53
  requirements:
57
- - - ">="
58
- - !ruby/object:Gem::Version
59
- version: 3.0.0
60
- - - "<"
54
+ - - "~>"
61
55
  - !ruby/object:Gem::Version
62
- version: '3.1'
56
+ version: 3.0.2
63
57
  - !ruby/object:Gem::Dependency
64
58
  name: rugged
65
59
  requirement: !ruby/object:Gem::Requirement
@@ -211,12 +205,14 @@ files:
211
205
  - lib/oxidized/input/input.rb
212
206
  - lib/oxidized/input/ssh.rb
213
207
  - lib/oxidized/input/telnet.rb
208
+ - lib/oxidized/input/tftp.rb
214
209
  - lib/oxidized/job.rb
215
210
  - lib/oxidized/jobs.rb
216
211
  - lib/oxidized/manager.rb
217
212
  - lib/oxidized/model/acos.rb
218
213
  - lib/oxidized/model/aireos.rb
219
214
  - lib/oxidized/model/airos.rb
215
+ - lib/oxidized/model/alvarion
220
216
  - lib/oxidized/model/aos.rb
221
217
  - lib/oxidized/model/aos7.rb
222
218
  - lib/oxidized/model/aosw.rb