oxidized 0.2.3 → 0.2.4

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: cfbcf124c9f12166a20cf549f684d7b6b5edb3ab
4
- data.tar.gz: 9a317c8cc4dd290297ac00cf4888b855b1f10303
3
+ metadata.gz: 685b7ced661d1682eb3702d88419a71e17069868
4
+ data.tar.gz: 0e3194c5964e0f803d70382e0f227c25d0a574b4
5
5
  SHA512:
6
- metadata.gz: 2ad3cf531b7be26691abe59a8581be106c07b5a039c5eafec5dae3f170b28e9d7d3f8bb45cfe4516787604ddddec27b52d9ac55644e7c64ba11ffb4601e03d54
7
- data.tar.gz: 72ad3d8512564340980e742c98f70977ffc8b5f9b5212a44b194f116afd1b3915f9348df9b066a531ce89ce67313ebceb44e816ed7dad4dcb36aa50820763955
6
+ metadata.gz: 4bfa8faf73182f97fd614db4b5530e6e37bcc632b3d0380d5dfff9d6f5336c17be6dffc833b3c7a25c82a88e0841788e92f4c51340a6729eee931ceadfec0685
7
+ data.tar.gz: 5929ddbd24859dfcb219deeba837f5070a5cea4327eb9b8b0b643c09045fbf59cc82d4d1759de7dc5f1913445d91fe0e397e22d2eb9cf5b78322de5d953b25ce
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ # 0.3.0
2
+ - FEATURE: *FIXME* bunch of stuff I did for richih, docs needed
3
+
4
+ # 0.2.4
5
+ - FEATURE: Cisco SMB (Nikola series VxWorks) model by @thetamind
6
+ - FEATURE: Extreme Networks XOS model (access by sjm)
7
+ - FEATURE: Brocade NOS (Network Operating System) (access by sjm)
8
+ - BUGFIX: Match exactly to node[:name] if node[name] is an ip address.
9
+
1
10
  # 0.2.3
2
11
  - BUGFIX: rescue @ssh.close when far end closes disgracefully (ALU ISAM)
3
12
  - BUGFIX: bugfixes to models
data/README.md CHANGED
@@ -1,12 +1,38 @@
1
- # Pitch
2
- * automatically adds/removes threads to meet configured retrieval interval
3
- * restful API to move node immediately to head-of-queue (GET/POST /node/next/[NODE])
4
- * syslog udp+file example to catch config change event (ios/junos) and trigger config fetch
5
- * will signal ios/junos user who made change, which output module can (git does) use (via POST)
6
- * 'git blame' will show for each line who and when the change was made
7
- * restful API to reload list of nodes (GET /reload)
8
- * restful API to fetch configurations (/node/fetch/[NODE] or /node/fetch/group/[NODE])
9
- * restful API to show list of nodes (GET /nodes)
1
+ # Oxidized
2
+
3
+ Oxidized is a network device configration backup tool. Its a RANCID replacment!
4
+
5
+ * automatically adds/removes threads to meet configured retrieval interval
6
+ * restful API to move node immediately to head-of-queue (GET/POST /node/next/[NODE])
7
+ * syslog udp+file example to catch config change event (ios/junos) and trigger config fetch
8
+ * will signal ios/junos user who made change, which output module can (git does) use (via POST)
9
+ * 'git blame' will show for each line who and when the change was made
10
+ * restful API to reload list of nodes (GET /reload)
11
+ * restful API to fetch configurations (/node/fetch/[NODE] or /node/fetch/group/[NODE])
12
+ * restful API to show list of nodes (GET /nodes)
13
+
14
+ [Youtube Video: Oxidized TREX 2014 presentation](http://youtu.be/kBQ_CTUuqeU#t=3h)
15
+
16
+ #### Index
17
+ 1. [Supported OS Types](#supported-os-types)
18
+ 2. [Installation](#installation)
19
+ * [Debian](#debian)
20
+ * [CentOS, Oracle Linux, Red Hat Linux version 6](#centos-oracle-linux-red-hat-linux-version 6)
21
+ 3. [Initial Configuration](#configuration)
22
+ 4. [Installing Ruby 2.1.2 using RVM](#installing-ruby-2.1.2-using-rvm)
23
+ 5. [Cookbook](#cookbook)
24
+ * [Debugging](#debugging)
25
+ * [Privileged mode](#privileged-mode)
26
+ * [Source: CSV](#source-csv)
27
+ * [Source: SQLite](#source-sqlite)
28
+ * [Output: GIT](#output-git)
29
+ * [Output: File](#output-file)
30
+ * [Advanced Configuration](#advanced-configuration)
31
+ 6. [Ruby API](#ruby-api)
32
+ * [Input](#input)
33
+ * [Output](#output)
34
+ * [Source](#source)
35
+ * [Model](#model)
10
36
 
11
37
  # Supported OS types
12
38
 
@@ -19,12 +45,16 @@
19
45
  * Arista EOS
20
46
  * Brocade Fabric OS
21
47
  * Brocade Ironware
48
+ * Brocade NOS (Network Operating System)
22
49
  * Brocade Vyatta
23
50
  * Cisco AireOS
24
51
  * Cisco ASA
25
52
  * Cisco IOS
26
53
  * Cisco IOS-XR
54
+ * Cisco NXOS
55
+ * Cisco SMB (Nikola series)
27
56
  * DELL PowerConnect
57
+ * Extreme Networks XOS
28
58
  * Force10 FTOS
29
59
  * FortiGate FortiOS
30
60
  * HP ProCurve
@@ -33,159 +63,257 @@
33
63
  * Juniper ScreenOS (Netscreen)
34
64
  * Ubiquiti AirOS
35
65
 
36
- # Install
37
-
38
- * Debian
39
- 1. apt-get install ruby ruby-dev libsqlite3-dev libssl-dev
40
- 2. gem install oxidized
41
- 3. gem install oxidized-script oxidized-web # if you don't install oxidized-web, make sure you remove "rest" from your config
42
- 4. oxidized
43
- 5. vi ~/.config/oxidized/config
44
- 6. (maybe point to your rancid/router.db or copy it there)
45
- 7. oxidized
46
-
47
- * CentOS, Oracle Linux, Red Hat Linux version 6
48
- 1. Install Ruby 1.9.3 or greater
49
- * For Ruby 2.1.2 installation instructions see "Installing Ruby 2.1.2 using RVM"
50
- 2. Install Oxidized dependencies
51
- * yum install cmake sqlite-devel openssl-devel
52
- 3. Install Oxidized daemon and Web front-end from Ruby Gems
53
- * gem install oxidized
54
- * gem install oxidized-script oxidized-web
55
- 4. Start Oxidized, this will create initial configuration
56
- * oxidized
57
- 5. Edit Oxidized configuration and create device database
58
- * vi ~/.config/oxidized/config
59
- * vi ~/.config/oxidized/router.db
60
- 6. If you are using file system storage create config directory
61
- * mkdir -p ~/.config/oxidized/configs/default
62
- 7. Start Oxidized
63
- * oxidized
64
-
65
- * Installing Ruby 2.1.2 using RVM
66
- 1. Install Ruby 2.1.2 build dependencies
67
- * yum install curl gcc-c++ patch readline readline-devel zlib zlib-devel
68
- * yum install libyaml-devel libffi-devel openssl-devel make cmake
69
- * yum install bzip2 autoconf automake libtool bison iconv-devel
70
- 2. Install RVM
71
- * curl -L get.rvm.io | bash -s stable
72
- 3. Setup RVM environment
73
- * source /etc/profile.d/rvm.sh
74
- 4. Compile and install Ruby 2.1.2
75
- * rvm install 2.1.2
76
- 5. Set Ruby 2.1.2 as default
77
- * rvm use --default 2.1.2
78
-
79
- # API
80
- ## Input
81
- * gets config from nodes
82
- * must implement 'connect', 'get', 'cmd'
83
- * 'ssh' and 'telnet' implemented
84
66
 
85
- ## Output
86
- * stores config
87
- * must implement 'store' (may implement 'fetch')
88
- * 'git' and 'file' (store as flat ascii) implemented
67
+ # Installation
68
+ ## Debian
69
+ Install all required packages and gems.
70
+
71
+ ```shell
72
+ apt-get install ruby ruby-dev libsqlite3-dev libssl-dev pkg-config cmake
73
+ gem install rugged
74
+ gem install oxidized
75
+ gem install oxidized-script oxidized-web # if you don't install oxidized-web, make sure you remove "rest" from your config
76
+ ```
77
+
78
+ ## CentOS, Oracle Linux, Red Hat Linux version 6
79
+ 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
80
+ ```shell
81
+ yum install cmake sqlite-devel openssl-devel
82
+ ```
83
+
84
+ Now lets install oxidized via Rubygems:
85
+ ```shell
86
+ gem install oxidized
87
+ gem install oxidized-script oxidized-web
88
+ ```
89
+
90
+ # Configuration
91
+
92
+ Oxidized configuration is in YAML format. Configuration files are subsequently sourced from ```/etc/oxidized/config``` then ```~/.config/oxidized/config```. The hashes will be merged, this might be useful for storing source information in a system wide file and user specific configuration in the home directory (to only include a staff specific username and password). Eg. if many users are using ```oxs```, see [Oxidized::Script](https://github.com/ytti/oxidized-script).
93
+
94
+ 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.
89
95
 
90
96
  ## Source
91
- * gets list of nodes to poll
92
- * must implement 'load'
93
- * source can have 'name', 'model', 'group', 'username', 'password', 'input', 'output', 'prompt'
94
- * name - name of the devices
95
- * model - model to use ios/junos/xyz, model is loaded dynamically when needed (Also default in config file)
96
- * input - method to acquire config, loaded dynamically as needed (Also default in config file)
97
- * output - method to store config, loaded dynamically as needed (Also default in config file)
98
- * prompt - prompt used for node (Also default in config file, can be specified in model too)
99
- * 'sql' and 'csv' (supports any format with single entry per line, like router.db)
100
97
 
101
- ## Model
102
- * lists commands to gather from given device model
103
- * can use 'cmd', 'prompt', 'comment', 'cfg'
104
- * cfg is executed in input/output/source context
105
- * cmd is executed in instance of model
106
- * 'junos', 'ios', 'ironware' and 'powerconnect' implemented
98
+ Oxidized supports ```CSV``` and ```SQLite``` 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. Take a look at the [Cookbook](#cookbook) for more details.
99
+
100
+ ## Outputs
101
+
102
+ 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.
103
+
104
+ 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.
105
+
106
+ First create the directory where the CSV ```output``` is going to store device configs and start Oxidized once.
107
+ ```
108
+ mkdir ~/.config/oxidized/configs
109
+ oxidized
110
+ ```
111
+
112
+ Now tell Oxidized where it finds a list of network devices to backup configuration from. You can either use CSV or SQLite as source. To create a CVS source add the following snippet:
113
+
114
+ ```
115
+ source:
116
+ default: csv
117
+ csv:
118
+ file: ~/.config/oxidized/router.db
119
+ delimiter: !ruby/regexp /:/
120
+ map:
121
+ name: 0
122
+ model: 1
123
+ ```
124
+
125
+ Now lets create a file based device database (you might want to switch to SQLite later on). Put your routers in ```~/.config/oxidized/router.db``` (file format is compatible with rancid). Simply add an item per line:
126
+
127
+ ```
128
+ router01.example.com:ios
129
+ switch01.example.com:procurve
130
+ router02.example.com:ios
131
+ ```
132
+
133
+ Run ```oxidized``` again to take the first backups.
134
+
135
+ # Installing Ruby 2.1.2 using RVM
136
+
137
+ Install Ruby 2.1.2 build dependencies
138
+ ```
139
+ yum install curl gcc-c++ patch readline readline-devel zlib zlib-devel
140
+ yum install libyaml-devel libffi-devel openssl-devel make cmake
141
+ yum install bzip2 autoconf automake libtool bison iconv-devel
142
+ ```
143
+
144
+ Install RVM
145
+ ```
146
+ curl -L get.rvm.io | bash -s stable
147
+ ```
148
+
149
+ Setup RVM environment and compile and install Ruby 2.1.2 and set it as default
150
+ ```
151
+ source /etc/profile.d/rvm.sh
152
+ rvm install 2.1.2
153
+ rvm use --default 2.1.2
154
+ ```
107
155
 
108
- ## Media
109
- * TREX 2014 presentation - http://youtu.be/kBQ_CTUuqeU#t=3h
110
156
 
111
157
  ## Cookbook
158
+ ### Debugging
159
+ In case a model plugin doesn't work correctly (ios, procurve, etc.), you can enable live debugging of SSH/Telnet sessions. Just add a ```debug``` option, specifying a log file destination to the ```input``` section.
160
+
161
+ The following example will log an active ssh session to ```/home/fisakytt/.config/oxidized/log_input-ssh``` and telnet to ```log_input-telnet```. The file will be truncated on each consecutive ssh/telnet session, so you need to put a ```tailf``` or ```tail -f``` on that file!
162
+
163
+ ```
164
+ input:
165
+ default: ssh, telnet
166
+ debug: ~/.config/oxidized/log_input
167
+ ssh:
168
+ secure: false
169
+ ```
170
+
171
+ ### Privileged mode
172
+
173
+ To start privileged mode before pulling the configuration, Oxidized needs to send the enable command. You can globally enable this, by adding the following snippet to the global section of the configuration file.
174
+
175
+ ```
176
+ vars:
177
+ enable: S3cre7
178
+ ```
179
+
180
+ ### Source: CSV
181
+
182
+ One line per device, colon seperated.
183
+
184
+ ```
185
+ source:
186
+ default: csv
187
+ csv:
188
+ file: /var/lib/oxidized/router.db
189
+ delimiter: !ruby/regexp /:/
190
+ map:
191
+ name: 0
192
+ model: 1
193
+ username: 2
194
+ password: 3
195
+ var_map:
196
+ enable: 4
197
+ ```
198
+
199
+ ### Source: SQLite
200
+
201
+ One row per device, filtered by hostname.
112
202
 
113
- ### Configuration I use in one environment
114
203
  ```
115
- ---
116
- username: LANA
117
- password: LANAAAAAAA
118
- output:
119
- default: git
120
- git:
121
- user: Oxidized
122
- email: o@example.com
123
- repo: "/usr/local/lan/oxidized.git"
124
204
  source:
125
205
  default: sql
126
206
  sql:
127
207
  adapter: sqlite
128
- database: "/usr/local/lan/corona.db"
129
- table: device
208
+ database: "/var/lib/oxidized/devices.db"
209
+ table: devices
130
210
  map:
131
- name: ptr
211
+ name: fqdn
132
212
  model: model
213
+ username: username
214
+ password: password
215
+ var_map:
216
+ enable: enable
133
217
  ```
134
218
 
135
- ### Configuration you end up after first run
136
- If you don't configure output and source, it'll further fill them with example
137
- configs for your chosen output/source in subsequent runs
219
+ ### Output: File
220
+
221
+ Parent directory needs to be created manually, one file per device, with most recent running config.
222
+
223
+ ```
224
+ output:
225
+ file:
226
+ directory: /var/lib/oxidized/configs
227
+ ```
228
+
229
+ ### Output: Git
230
+
231
+ ```
232
+ output:
233
+ default: git
234
+ git:
235
+ user: Oxidized
236
+ email: o@example.com
237
+ repo: "/var/lib/oxidized/devices.git"
238
+ ```
239
+
240
+ ### Advanced Configuration
241
+
242
+ 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.
243
+
138
244
  ```
139
245
  ---
140
- username: username
141
- password: password
246
+ username: oxidized
247
+ password: S3cr3tx
142
248
  model: junos
143
249
  interval: 3600
144
- log: "/home/fisakytt/.config/oxidized/log"
250
+ log: ~/.config/oxidized/log
145
251
  debug: false
146
252
  threads: 30
147
- timeout: 30
253
+ timeout: 20
254
+ retries: 3
148
255
  prompt: !ruby/regexp /^([\w.@-]+[#>]\s?)$/
256
+ vars:
257
+ enable: S3cr3tx
258
+ groups: {}
149
259
  rest: 127.0.0.1:8888
150
- vars: {}
151
260
  input:
152
261
  default: ssh, telnet
262
+ debug: false
153
263
  ssh:
154
264
  secure: false
155
- output:
156
- default: git
157
- source:
158
- default: csv
159
- model_map:
160
- cisco: ios
161
- juniper: junos
162
- ```
163
-
164
- Output and Source could be:
165
- ```
166
265
  output:
167
266
  default: git
168
267
  git:
169
- user: Oxidized
170
- email: o@example.com
171
- repo: "/home/fisakytt/.config/oxidized/oxidized.git"
268
+ user: Oxidized
269
+ email: oxidized@example.com
270
+ repo: "~/.config/oxidized/oxidized.git"
172
271
  source:
173
272
  default: csv
174
273
  csv:
175
- file: "/home/fisakytt/.config/oxidized/router.db"
274
+ file: ~/.config/oxidized/router.db
176
275
  delimiter: !ruby/regexp /:/
177
276
  map:
178
277
  name: 0
179
278
  model: 1
279
+ username: 2
280
+ password: 3
281
+ var_map:
282
+ enable: 4
283
+ model_map:
284
+ cisco: ios
285
+ juniper: junos
180
286
  ```
181
- which reads nodes from rancid compatible router.db maps their model names to
182
- model names oxidized expects, stores config in git, will try ssh first then
183
- telnet, wont crash on changed ssh keys.
184
287
 
185
- Hopefully most of them are obvious, log is ignored if Syslog::Logger exists
186
- (>=2.0) and syslog is used instead.
187
288
 
188
- System wide configurations can be stored in /etc/oxidized/config, this might be
189
- useful for storing for example source information, if many users are using
190
- oxs/Oxidized::Script, which would allow user specific config only to include
191
- username+password.
289
+ # Ruby API
290
+
291
+ The following objects exist in Oxidized.
292
+
293
+ ## Input
294
+ * gets config from nodes
295
+ * must implement 'connect', 'get', 'cmd'
296
+ * 'ssh' and 'telnet' implemented
297
+
298
+ ## Output
299
+ * stores config
300
+ * must implement 'store' (may implement 'fetch')
301
+ * 'git' and 'file' (store as flat ascii) implemented
302
+
303
+ ## Source
304
+ * gets list of nodes to poll
305
+ * must implement 'load'
306
+ * source can have 'name', 'model', 'group', 'username', 'password', 'input', 'output', 'prompt'
307
+ * name - name of the devices
308
+ * model - model to use ios/junos/xyz, model is loaded dynamically when needed (Also default in config file)
309
+ * input - method to acquire config, loaded dynamically as needed (Also default in config file)
310
+ * output - method to store config, loaded dynamically as needed (Also default in config file)
311
+ * prompt - prompt used for node (Also default in config file, can be specified in model too)
312
+ * 'sql' and 'csv' (supports any format with single entry per line, like router.db)
313
+
314
+ ## Model
315
+ * lists commands to gather from given device model
316
+ * can use 'cmd', 'prompt', 'comment', 'cfg'
317
+ * cfg is executed in input/output/source context
318
+ * cmd is executed in instance of model
319
+ * 'junos', 'ios', 'ironware' and 'powerconnect' implemented
data/lib/oxidized/cli.rb CHANGED
@@ -16,11 +16,13 @@ module Oxidized
16
16
  private
17
17
 
18
18
  def initialize
19
+ Log.info "Oxidized starting, running as pid #{$$}"
19
20
  _args, @opts = parse_opts
20
21
  CFG.debug = true if @opts[:debug]
21
22
  end
22
23
 
23
24
  def crash error
25
+ Log.fatal "Oxidized crashed, crashfile written in #{Config::Crash}"
24
26
  open Config::Crash, 'w' do |file|
25
27
  file.puts '-' * 50
26
28
  file.puts Time.now.utc
@@ -51,6 +51,6 @@ module Oxidized
51
51
 
52
52
  Log.level = Logger::INFO unless CFG.debug
53
53
  raise NoConfig, 'edit ~/.config/oxidized/config' if CFGS.create
54
- Log.file = CFG.log if CFG.log
54
+ Log.file = CFG.log if CFG.log
55
55
 
56
56
  end
data/lib/oxidized/log.rb CHANGED
@@ -12,6 +12,7 @@ module Oxidized
12
12
  super target
13
13
  end
14
14
  def file= target
15
+ FileUtils.mkdir_p File.dirname(target)
15
16
  @logdev = LogDevice.new target
16
17
  end
17
18
  end
@@ -1,7 +1,8 @@
1
1
  class AOSW < Oxidized::Model
2
2
 
3
- # AOSW - Alcatel-Lucent OS - Wireless
4
- # Used in Alcatel OAW-4750 WLAN controller (Aruba)
3
+ # AOSW Aruba Wireless
4
+ # Used in Alcatel OAW-4750 WLAN controller
5
+ # Also Dell controllers
5
6
 
6
7
  comment '# '
7
8
  prompt /^\([^)]+\) #/
@@ -16,8 +17,7 @@ class AOSW < Oxidized::Model
16
17
  end
17
18
 
18
19
  cmd 'show inventory' do |cfg|
19
- cfg = cfg.each_line.take_while { |line| not line.match /Output \d Config/i }
20
- comment cfg.join
20
+ clean cfg
21
21
  end
22
22
 
23
23
  cmd 'show slots' do |cfg|
@@ -40,4 +40,18 @@ class AOSW < Oxidized::Model
40
40
  pre_logout 'exit'
41
41
  end
42
42
 
43
+ def clean cfg
44
+ out = []
45
+ cfg.each_line do |line|
46
+ # drop the temperature, fan speed and voltage, which change each run
47
+ next if line.match /Output \d Config/i
48
+ next if line.match /(Tachometers|Temperatures|Voltages)/
49
+ next if line.match /((Card|CPU) Temperature|Chassis Fan|VMON1[0-9])/
50
+ next if line.match /[0-9]+ (RPM|mV|C)$/
51
+ out << line.strip
52
+ end
53
+ out = out.join "\n"
54
+ out << "\n"
55
+ end
56
+
43
57
  end
@@ -3,7 +3,7 @@ class ASA < Oxidized::Model
3
3
  # Cisco ASA model #
4
4
  # Only SSH supported for the sake of security
5
5
 
6
- prompt /^\r*([\w]+[#>]\s?)$/
6
+ prompt /^\r*([\w.@()-\/]+[#>]\s?)$/
7
7
  comment '! '
8
8
 
9
9
  cmd :all do |cfg|
@@ -16,11 +16,10 @@ class ASA < Oxidized::Model
16
16
  cfg
17
17
  end
18
18
 
19
- cmd 'show clock' do |cfg|
20
- comment cfg
21
- end
22
-
23
19
  cmd 'show version' do |cfg|
20
+ # avoid commits due to uptime / ixo-router01 up 2 mins 28 secs / ixo-router01 up 1 days 2 hours
21
+ cfg = cfg.each_line.select { |line| not line.match /\s+up\s+\d+\s+/ }
22
+ cfg = cfg.join
24
23
  comment cfg
25
24
  end
26
25
 
@@ -0,0 +1,48 @@
1
+ class CiscoSMB < Oxidized::Model
2
+
3
+ # Cisco Small Business 200, 300, 500, and ESW2 series switches
4
+ # http://www.cisco.com/c/en/us/support/switches/small-business-300-series-managed-switches/products-release-notes-list.html
5
+
6
+ prompt /^\r?([\w.@()-]+[#>]\s?)$/
7
+ comment '! '
8
+
9
+ cmd :all do |cfg|
10
+ lines = cfg.each_line.to_a[1..-2]
11
+ # Remove \r from beginning of response
12
+ lines[0].gsub!(/^\r.*?/, '') if lines.length > 0
13
+ lines.join
14
+ end
15
+
16
+ cmd :secret do |cfg|
17
+ cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>'
18
+ cfg.gsub! /username (\S+) privilege (\d+) (\S+).*/, '<secret hidden>'
19
+ cfg
20
+ end
21
+
22
+ cmd 'show version' do |cfg|
23
+ comment cfg
24
+ end
25
+
26
+ cmd 'show inventory' do |cfg|
27
+ comment cfg
28
+ end
29
+
30
+ cmd 'show running-config' do |cfg|
31
+ cfg = cfg.each_line.to_a[0..-1].join
32
+ cfg.gsub! /^Current configuration : [^\n]*\n/, ''
33
+ cfg.sub! /^(ntp clock-period).*/, '! \1'
34
+ cfg.gsub! /^\ tunnel\ mpls\ traffic-eng\ bandwidth[^\n]*\n*(
35
+ (?:\ [^\n]*\n*)*
36
+ tunnel\ mpls\ traffic-eng\ auto-bw)/mx, '\1'
37
+ cfg
38
+ end
39
+
40
+ cfg :telnet, :ssh do
41
+ username /^User Name:/
42
+ password /^\r?Password:$/
43
+ post_login 'terminal datadump' # Disable pager
44
+ post_login 'terminal width 0'
45
+ pre_logout 'exit'
46
+ end
47
+
48
+ end
@@ -37,9 +37,9 @@ class IOS < Oxidized::Model
37
37
  end
38
38
 
39
39
  cmd 'show running-config' do |cfg|
40
- cfg = cfg.each_line.to_a[3..-1].join
40
+ cfg = cfg.each_line.to_a[3..-1]
41
+ cfg = cfg.reject { |line| line.match /^ntp clock-period / }.join
41
42
  cfg.gsub! /^Current configuration : [^\n]*\n/, ''
42
- cfg.sub! /^(ntp clock-period).*/, '! \1'
43
43
  cfg.gsub! /^\ tunnel\ mpls\ traffic-eng\ bandwidth[^\n]*\n*(
44
44
  (?:\ [^\n]*\n*)*
45
45
  tunnel\ mpls\ traffic-eng\ auto-bw)/mx, '\1'
@@ -1,3 +1,5 @@
1
+ require_relative 'outputs'
2
+
1
3
  module Oxidized
2
4
  class Model
3
5
  include Oxidized::Config::Vars
@@ -77,17 +79,16 @@ module Oxidized
77
79
  def cmd string, &block
78
80
  out = @input.cmd string
79
81
  return false unless out
80
- out = Oxidized::String.new out
81
82
  self.class.cmds[:all].each do |all_block|
82
- out = instance_exec out, string, &all_block
83
+ out = instance_exec Oxidized::String.new(out), string, &all_block
83
84
  end
84
85
  if vars :remove_secret
85
86
  self.class.cmds[:secret].each do |all_block|
86
- out = instance_exec out, string, &all_block
87
+ out = instance_exec Oxidized::String.new(out), string, &all_block
87
88
  end
88
89
  end
89
- out = instance_exec out, &block if block
90
- out
90
+ out = instance_exec Oxidized::String.new(out), &block if block
91
+ process_cmd_output out, string
91
92
  end
92
93
 
93
94
  def output
@@ -124,20 +125,20 @@ module Oxidized
124
125
  end
125
126
 
126
127
  def get
127
- data, pre = '', ''
128
+ outputs = Outputs.new
128
129
  procs = self.class.procs
129
130
  self.class.cmds[:cmd].each do |command, block|
130
131
  out = cmd command, &block
131
132
  return false unless out
132
- data << out.to_s
133
+ outputs << out
133
134
  end
134
135
  procs[:pre].each do |pre_proc|
135
- pre << instance_eval(&pre_proc).to_s
136
+ outputs.unshift Oxidized::String.new(instance_eval(&pre_proc))
136
137
  end
137
138
  procs[:post].each do |post_proc|
138
- data << instance_eval(&post_proc).to_s
139
+ outputs << Oxidized::String.new(instance_eval(&post_proc))
139
140
  end
140
- pre + data
141
+ outputs
141
142
  end
142
143
 
143
144
  def comment _comment
@@ -148,5 +149,15 @@ module Oxidized
148
149
  data
149
150
  end
150
151
 
152
+ private
153
+
154
+ def process_cmd_output output, name
155
+ if output.class != Oxidized::String
156
+ output = Oxidized::String.new output
157
+ end
158
+ output.set_cmd(name)
159
+ output
160
+ end
161
+
151
162
  end
152
163
  end
@@ -0,0 +1,45 @@
1
+ class NOS < Oxidized::Model
2
+
3
+ # Brocade Network Operating System
4
+
5
+ prompt /^(?:\e\[..h)?[\w.-]+# $/
6
+ comment '! '
7
+
8
+ cmd :all do |cfg|
9
+ cfg.each_line.to_a[1..-2].join
10
+ end
11
+
12
+ cmd 'show version' do |cfg|
13
+ comment cfg
14
+ end
15
+
16
+ cmd 'show inventory' do |cfg|
17
+ comment cfg
18
+ end
19
+
20
+ cmd 'show license' do |cfg|
21
+ comment cfg
22
+ end
23
+
24
+ cmd 'show chassis' do |cfg|
25
+ comment cfg.each_line.reject { |line| line.match /Time/ }.join
26
+ end
27
+
28
+ cfg 'show system' do |cfg|
29
+ commen cfg.each_line.reject { |line| line.match /Time/ or line.match /speed/ }
30
+ end
31
+
32
+ cmd 'show running-config'
33
+
34
+ cfg :telnet do
35
+ username /^.* login: /
36
+ username /^Password:/
37
+ end
38
+
39
+ cfg :telnet, :ssh do
40
+ post_login 'terminal length 0'
41
+ #post_login 'terminal width 0'
42
+ pre_logout 'exit'
43
+ end
44
+
45
+ end
@@ -0,0 +1,23 @@
1
+ class NXOS < Oxidized::Model
2
+
3
+ prompt /^(\r?[\w.@_()-]+[#]\s?)$/
4
+ comment '! '
5
+
6
+ cmd 'show version' do |cfg|
7
+ cfg = cfg.each_line.take_while { |line| not line.match(/uptime/i) }
8
+ comment cfg.join ""
9
+ end
10
+
11
+ cmd 'show inventory' do |cfg|
12
+ comment cfg
13
+ end
14
+
15
+ cmd 'show running-config' do |cfg|
16
+ cfg.gsub! /^!Time:[^\n]*\n/, ''
17
+ end
18
+
19
+ cfg :ssh do
20
+ post_login 'terminal length 0'
21
+ pre_logout 'exit'
22
+ end
23
+ end
@@ -0,0 +1,41 @@
1
+ module Oxidized
2
+ class Model
3
+ class Outputs
4
+
5
+ def to_cfg
6
+ type_to_str(nil)
7
+ end
8
+
9
+ def type_to_str want_type
10
+ type(want_type).map { |out| out }.join
11
+ end
12
+
13
+ def << output
14
+ @outputs << output
15
+ end
16
+
17
+ def unshift output
18
+ @outputs.unshift output
19
+ end
20
+
21
+ def all
22
+ @outputs
23
+ end
24
+
25
+ def type type
26
+ @outputs.select { |out| out.type==type }
27
+ end
28
+
29
+ def types
30
+ @outputs.map { |out| out.type }.uniq.compact
31
+ end
32
+
33
+ private
34
+
35
+ def initialize
36
+ @outputs = []
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -14,12 +14,14 @@ class PowerConnect < Oxidized::Model
14
14
  end
15
15
 
16
16
  cmd 'show version' do |cfg|
17
- comment cfg
17
+ cfg = cfg.split("\n").select { |line| not line[/Up\sTime/] }
18
+ comment cfg.join("\n") + "\n"
18
19
  end
19
20
 
20
21
  cmd 'show system' do |cfg|
21
- cfg = cfg.each_line.take_while { |line| not line.match(/uptime/i) }
22
- comment cfg.join "\n"
22
+ cfg = cfg.split("\n").select { |line| not line[/Up\sTime/] }
23
+ cfg = cfg[0..-28]<<" "
24
+ comment cfg.join("\n")
23
25
  end
24
26
 
25
27
  cmd 'show running-config'
@@ -31,8 +33,10 @@ class PowerConnect < Oxidized::Model
31
33
 
32
34
  cfg :telnet, :ssh do
33
35
  if vars :enable
34
- send "enable\n"
35
- send vars(:enable) + "\n"
36
+ post_login do
37
+ send "enable\n"
38
+ send vars(:enable) + "\n"
39
+ end
36
40
  end
37
41
 
38
42
  post_login "terminal length 0"
@@ -0,0 +1,40 @@
1
+ class XOS < Oxidized::Model
2
+
3
+ # Extreme Networks XOS
4
+
5
+ prompt /^*?[\w .-]+# $/
6
+ comment '# '
7
+
8
+ cmd :all do |cfg|
9
+ cfg.each_line.to_a[1..-2].join.rstrip
10
+ end
11
+
12
+ cmd 'show version' do |cfg|
13
+ comment cfg
14
+ end
15
+
16
+ cmd 'show diagnostics' do |cfg|
17
+ comment cfg
18
+ end
19
+
20
+ cmd 'show licenses' do |cfg|
21
+ comment cfg
22
+ end
23
+
24
+ cmd 'show switch'do |cfg|
25
+ comment cfg.each_line.reject { |line| line.match /Time:/ or line.match /boot/i }.join
26
+ end
27
+
28
+ cmd 'show configuration'
29
+
30
+ cfg :telnet do
31
+ username /^login:/
32
+ password /^passowrd:/
33
+ end
34
+
35
+ cfg :telnet, :ssh do
36
+ post_login 'disable clipaging'
37
+ pre_logout 'exit'
38
+ end
39
+
40
+ end
@@ -9,16 +9,11 @@ module Oxidized
9
9
  def load node_want=nil
10
10
  with_lock do
11
11
  new = []
12
- node_want_ip = (IPAddr.new(node_want) rescue false) if node_want
13
12
  @source = CFG.source.default
14
13
  Oxidized.mgr.add_source @source
15
14
  Oxidized.mgr.source[@source].new.load.each do |node|
16
-
17
15
  # we want to load specific node(s), not all of them
18
- if node_want
19
- next unless node_want_ip == node[:ip] or node_want.match(node[:name])
20
- end
21
-
16
+ next unless node_want? node_want, node
22
17
  begin
23
18
  _node = Node.new node
24
19
  new.push _node
@@ -28,10 +23,25 @@ module Oxidized
28
23
  Log.error "node %s is not resolvable, raised %s with message '%s'" % [node, err.class, err.message]
29
24
  end
30
25
  end
26
+ Log.info "Loaded #{size} nodes"
31
27
  size == 0 ? replace(new) : update_nodes(new)
32
28
  end
33
29
  end
34
30
 
31
+ def node_want? node_want, node
32
+ return true unless node_want
33
+ node_want_ip = (IPAddr.new(node_want) rescue false)
34
+ name_is_ip = (IPAddr.new(node[:name]) rescue false)
35
+ if name_is_ip and node_want_ip == node[:name]
36
+ true
37
+ elsif node[:ip] and node_want_ip == node[:ip]
38
+ true
39
+ elsif node_want.match node[:name]
40
+ true unless name_is_ip
41
+ end
42
+ end
43
+
44
+
35
45
  def list
36
46
  with_lock do
37
47
  map { |e| e.serialize }
@@ -14,14 +14,14 @@ class OxidizedFile < Output
14
14
  end
15
15
  end
16
16
 
17
- def store node, data, opt={}
17
+ def store node, outputs, opt={}
18
18
  file = @cfg.directory
19
19
  if opt[:group]
20
20
  file = File.join File.dirname(file), opt[:group]
21
21
  end
22
22
  FileUtils.mkdir_p file
23
23
  file = File.join file, node
24
- open(file, 'w') { |fh| fh.write data }
24
+ open(file, 'w') { |fh| fh.write outputs.to_cfg }
25
25
  end
26
26
 
27
27
  def fetch node, group
@@ -21,23 +21,32 @@ class Git < Output
21
21
  end
22
22
  end
23
23
 
24
- def store file, data, opt={}
25
- msg = opt[:msg]
26
- user = (opt[:user] or @cfg.user)
27
- email = (opt[:email] or @cfg.email)
28
- repo = @cfg.repo
29
- if opt[:group]
30
- repo = File.join File.dirname(repo), opt[:group] + '.git'
31
- end
32
- begin
33
- repo = Rugged::Repository.new repo
34
- update_repo repo, file, data, msg, user, email
35
- rescue Rugged::OSError, Rugged::RepositoryError
36
- Rugged::Repository.init_at repo, :bare
37
- retry
24
+ def store file, outputs, opt={}
25
+ @msg = opt[:msg]
26
+ @user = (opt[:user] or @cfg.user)
27
+ @email = (opt[:email] or @cfg.email)
28
+ @opt = opt
29
+ repo = @cfg.repo
30
+
31
+ outputs.types.each do |type|
32
+ type_cfg = ''
33
+ type_repo = File.join File.dirname(repo), type + '.git'
34
+ outputs.type(type).each do |output|
35
+ (type_cfg << output; next) if not output.name
36
+ type_file = file + '--' + output.name
37
+ if @cfg.type_as_directory?
38
+ type_file = type + '/' + type_file
39
+ type_repo = repo
40
+ end
41
+ update type_repo, type_file, output
42
+ end
43
+ update type_repo, file, type_cfg
38
44
  end
45
+
46
+ update repo, file, outputs.to_cfg
39
47
  end
40
48
 
49
+
41
50
  def fetch node, group
42
51
  begin
43
52
  repo = @cfg.repo
@@ -55,6 +64,18 @@ class Git < Output
55
64
 
56
65
  private
57
66
 
67
+ def update repo, file, data
68
+ return if data.empty?
69
+ if @opt[:group]
70
+ repo = File.join File.dirname(repo), @opt[:group] + '.git'
71
+ end
72
+ repo = Rugged::Repository.new repo
73
+ update_repo repo, file, data, @msg, @user, @email
74
+ rescue Rugged::OSError, Rugged::RepositoryError
75
+ Rugged::Repository.init_at repo, :bare
76
+ retry
77
+ end
78
+
58
79
  def update_repo repo, file, data, msg, user, email
59
80
  oid = repo.write data, :blob
60
81
  index = repo.index
@@ -1,5 +1,10 @@
1
1
  module Oxidized
2
2
  class Output
3
3
  class NoConfig < OxidizedError; end
4
+
5
+ def cfg_to_str cfg
6
+ cfg.select{ |h| h[:type]=='cfg' }.map{ |h| h[:data] }.join
7
+ end
8
+
4
9
  end
5
10
  end
@@ -19,6 +19,7 @@ class CSV < Source
19
19
  def load
20
20
  nodes = []
21
21
  open(@cfg.file).each_line do |line|
22
+ next if line.match /^\s*#/
22
23
  data = line.chomp.split @cfg.delimiter
23
24
  next if data.empty?
24
25
  # map node parameters
@@ -1,13 +1,32 @@
1
1
  module Oxidized
2
2
  # Used in models, contains convenience methods
3
3
  class String < String
4
+ attr_accessor :type, :cmd, :name
5
+
4
6
  # @return [Oxidized::String] copy of self with last line removed
5
7
  def cut_tail
6
8
  Oxidized::String.new each_line.to_a[0..-2].join
7
9
  end
10
+
8
11
  # @return [Oxidized::String] copy of self with first line removed
9
12
  def cut_head
10
13
  Oxidized::String.new each_line.to_a[1..-1].join
11
14
  end
15
+
16
+ # sets @cmd and @name unless @name is already set
17
+ def set_cmd command
18
+ @cmd = command
19
+ @name = @cmd.strip.gsub(/\s+/, '_') if @name == nil
20
+ end
21
+
22
+ def initialize str=''
23
+ super
24
+ if str.class == Oxidized::String
25
+ @cmd = str.cmd
26
+ @name = str.name
27
+ @type = str.type
28
+ end
29
+ end
30
+
12
31
  end
13
32
  end
data/oxidized.gemspec CHANGED
@@ -1,10 +1,10 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'oxidized'
3
- s.version = '0.2.3'
3
+ s.version = '0.2.4'
4
4
  s.licenses = %w( Apache-2.0 )
5
5
  s.platform = Gem::Platform::RUBY
6
- s.authors = [ 'Saku Ytti' ]
7
- s.email = %w( saku@ytti.fi )
6
+ s.authors = [ 'Saku Ytti', 'Samer Abdel-Hafez' ]
7
+ s.email = %w( saku@ytti.fi sam@arahant.net )
8
8
  s.homepage = 'http://github.com/ytti/oxidized'
9
9
  s.summary = 'feeble attempt at rancid'
10
10
  s.description = 'software to fetch configuration from network devices and store them'
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oxidized
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Saku Ytti
8
+ - Samer Abdel-Hafez
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2014-08-16 00:00:00.000000000 Z
12
+ date: 2015-02-02 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: asetus
@@ -55,6 +56,7 @@ dependencies:
55
56
  description: software to fetch configuration from network devices and store them
56
57
  email:
57
58
  - saku@ytti.fi
59
+ - sam@arahant.net
58
60
  executables:
59
61
  - oxidized
60
62
  extensions: []
@@ -88,6 +90,7 @@ files:
88
90
  - lib/oxidized/model/aos7.rb
89
91
  - lib/oxidized/model/aosw.rb
90
92
  - lib/oxidized/model/asa.rb
93
+ - lib/oxidized/model/ciscosmb.rb
91
94
  - lib/oxidized/model/eos.rb
92
95
  - lib/oxidized/model/fabricos.rb
93
96
  - lib/oxidized/model/fortios.rb
@@ -98,12 +101,16 @@ files:
98
101
  - lib/oxidized/model/isam.rb
99
102
  - lib/oxidized/model/junos.rb
100
103
  - lib/oxidized/model/model.rb
104
+ - lib/oxidized/model/nos.rb
105
+ - lib/oxidized/model/nxos.rb
106
+ - lib/oxidized/model/outputs.rb
101
107
  - lib/oxidized/model/powerconnect.rb
102
108
  - lib/oxidized/model/procurve.rb
103
109
  - lib/oxidized/model/screenos.rb
104
110
  - lib/oxidized/model/timos.rb
105
111
  - lib/oxidized/model/vrp.rb
106
112
  - lib/oxidized/model/vyatta.rb
113
+ - lib/oxidized/model/xos.rb
107
114
  - lib/oxidized/node.rb
108
115
  - lib/oxidized/node/stats.rb
109
116
  - lib/oxidized/nodes.rb