oxidized 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/Dockerfile +11 -0
- data/README.md +161 -3
- data/extra/nagios_check_failing_nodes.rb +16 -6
- data/extra/syslog.rb +39 -11
- data/lib/oxidized/config.rb +2 -1
- data/lib/oxidized/core.rb +5 -0
- data/lib/oxidized/hook.rb +88 -0
- data/lib/oxidized/hook/exec.rb +84 -0
- data/lib/oxidized/hook/noophook.rb +9 -0
- data/lib/oxidized/input/ftp.rb +54 -0
- data/lib/oxidized/input/ssh.rb +2 -1
- data/lib/oxidized/input/telnet.rb +5 -2
- data/lib/oxidized/manager.rb +10 -1
- data/lib/oxidized/model/aosw.rb +11 -2
- data/lib/oxidized/model/edgeos.rb +27 -0
- data/lib/oxidized/model/ironware.rb +4 -3
- data/lib/oxidized/model/masteros.rb +46 -0
- data/lib/oxidized/model/routeros.rb +7 -1
- data/lib/oxidized/model/xos.rb +1 -1
- data/lib/oxidized/model/zynos.rb +12 -0
- data/lib/oxidized/node.rb +3 -0
- data/lib/oxidized/nodes.rb +1 -1
- data/lib/oxidized/output/git.rb +18 -15
- data/lib/oxidized/worker.rb +6 -0
- data/oxidized.gemspec +4 -3
- metadata +27 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92cfff2bf262414c347bf28d769b62618495327b
|
4
|
+
data.tar.gz: f82dbcaaaa017cf3359a5ffae022d73789a174c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bcb49a619bf2d17d457e800e6a4a285d9d05907d52aec1e03ad2605f210ba2e1ef1ef75ce613f538e28135d8a37f99c9f482faac4acf7e583d90b3474c538585
|
7
|
+
data.tar.gz: 2dd8a40b4a6b12cef3db4e74164533379c9a7192f1e6dab929722595f7352b85fadeb1b8514c8aad2e9c23eb1be0f7d54a70fd52b9b524198e6fa219e20d1b20
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
# 0.8.0
|
2
|
+
- FEATURE: hooks (by @aakso)
|
3
|
+
- FEATURE: MRV MasterOS support (by @kwibbly)
|
4
|
+
- FEATURE: EdgeOS support (by @laf)
|
5
|
+
- FEATURE: FTP input and Zyxel ZynOS support (by @ytti)
|
6
|
+
- FEATURE: version and diffs API For oxidized-web (by @FlorianDoublet)
|
7
|
+
- BUGFIX: aosw, ironware, routeros, xos models
|
8
|
+
- BUGFIX: crash with 0 nodes
|
9
|
+
- BUGFIX: ssh auth fail without keyboard-interactive
|
10
|
+
- Full changelog https://github.com/ytti/oxidized/compare/0.7.1...HEAD
|
11
|
+
|
1
12
|
# 0.7.0
|
2
13
|
- FEATURE: support http source (by @laf)
|
3
14
|
- FEATURE: support Palo Alto PANOS (by @rixxxx)
|
data/Dockerfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
FROM debian:latest
|
2
|
+
MAINTAINER Samer Abdel-Hafez <sam@arahant.net>
|
3
|
+
|
4
|
+
RUN apt-get update && \
|
5
|
+
apt-get install -y ruby ruby-dev libsqlite3-dev libssl-dev pkg-config make cmake
|
6
|
+
|
7
|
+
RUN gem install oxidized oxidized-web --no-ri --no-rdoc
|
8
|
+
|
9
|
+
RUN apt-get remove -y ruby-dev pkg-config make cmake
|
10
|
+
|
11
|
+
RUN apt-get -y autoremove
|
data/README.md
CHANGED
@@ -12,6 +12,7 @@ Oxidized is a network device configuration backup tool. It's a RANCID replacemen
|
|
12
12
|
* restful API to reload list of nodes (GET /reload)
|
13
13
|
* restful API to fetch configurations (/node/fetch/[NODE] or /node/fetch/group/[NODE])
|
14
14
|
* restful API to show list of nodes (GET /nodes)
|
15
|
+
* restful API to show list of version for a node (/node/version[NODE]) and diffs
|
15
16
|
|
16
17
|
[Youtube Video: Oxidized TREX 2014 presentation](http://youtu.be/kBQ_CTUuqeU#t=3h)
|
17
18
|
|
@@ -22,7 +23,8 @@ Oxidized is a network device configuration backup tool. It's a RANCID replacemen
|
|
22
23
|
* [CentOS, Oracle Linux, Red Hat Linux version 6](#centos-oracle-linux-red-hat-linux-version 6)
|
23
24
|
3. [Initial Configuration](#configuration)
|
24
25
|
4. [Installing Ruby 2.1.2 using RVM](#installing-ruby-2.1.2-using-rvm)
|
25
|
-
5. [
|
26
|
+
5. [Running with Docker](#running-with-docker)
|
27
|
+
6. [Cookbook](#cookbook)
|
26
28
|
* [Debugging](#debugging)
|
27
29
|
* [Privileged mode](#privileged-mode)
|
28
30
|
* [Source: CSV](#source-csv)
|
@@ -30,8 +32,9 @@ Oxidized is a network device configuration backup tool. It's a RANCID replacemen
|
|
30
32
|
* [Source: HTTP](#source-http)
|
31
33
|
* [Output: GIT](#output-git)
|
32
34
|
* [Output: File](#output-file)
|
35
|
+
* [Output types](#output-types)
|
33
36
|
* [Advanced Configuration](#advanced-configuration)
|
34
|
-
|
37
|
+
7. [Ruby API](#ruby-api)
|
35
38
|
* [Input](#input)
|
36
39
|
* [Output](#output)
|
37
40
|
* [Source](#source)
|
@@ -67,8 +70,10 @@ Oxidized is a network device configuration backup tool. It's a RANCID replacemen
|
|
67
70
|
* Juniper JunOS
|
68
71
|
* Juniper ScreenOS (Netscreen)
|
69
72
|
* Mikrotik RouterOS
|
73
|
+
* MRV Master-OS
|
70
74
|
* Ubiquiti AirOS
|
71
75
|
* Palo Alto PAN-OS
|
76
|
+
* Zyxel ZyNOS
|
72
77
|
|
73
78
|
|
74
79
|
# Installation
|
@@ -159,6 +164,43 @@ rvm install 2.1.2
|
|
159
164
|
rvm use --default 2.1.2
|
160
165
|
```
|
161
166
|
|
167
|
+
# Running with Docker
|
168
|
+
1. clone git repo:
|
169
|
+
|
170
|
+
```
|
171
|
+
root@bla:~# git clone https://github.com/ytti/oxidized
|
172
|
+
```
|
173
|
+
2. build container locally:
|
174
|
+
```
|
175
|
+
root@bla:~# docker build -q -t oxidized/oxidized:latest oxidized/
|
176
|
+
```
|
177
|
+
3. create config directory in main system:
|
178
|
+
```
|
179
|
+
root@bla~:# mkdir /etc/oxidized
|
180
|
+
```
|
181
|
+
4. run container the first time:
|
182
|
+
```
|
183
|
+
root@bla:~# docker run -v /etc/oxidized:/root/.config/oxidized -p 8888:8888/tcp -t oxidized/oxidized:latest oxidized
|
184
|
+
```
|
185
|
+
5. add 'router.db' to /etc/oxidized:
|
186
|
+
```
|
187
|
+
root@bla:~# vim /etc/oxidized/router.db
|
188
|
+
[ ... ]
|
189
|
+
root@bla:~#
|
190
|
+
```
|
191
|
+
6. run container again:
|
192
|
+
```
|
193
|
+
root@bla:~# docker run -v /etc/oxidized:/root/.config/oxidized -p 8888:8888/tcp -t oxidized/oxidized:latest oxidized
|
194
|
+
oxidized[1]: Oxidized starting, running as pid 1
|
195
|
+
oxidized[1]: Loaded 1 nodes
|
196
|
+
Puma 2.13.4 starting...
|
197
|
+
* Min threads: 0, max threads: 16
|
198
|
+
* Environment: development
|
199
|
+
* Listening on tcp://0.0.0.0:8888
|
200
|
+
^C
|
201
|
+
|
202
|
+
root@bla:~#
|
203
|
+
```
|
162
204
|
|
163
205
|
## Cookbook
|
164
206
|
### Debugging
|
@@ -169,7 +211,7 @@ The following example will log an active ssh session to ```/home/fisakytt/.confi
|
|
169
211
|
```
|
170
212
|
input:
|
171
213
|
default: ssh, telnet
|
172
|
-
debug:
|
214
|
+
debug: /tmp/oxidized_log_input
|
173
215
|
ssh:
|
174
216
|
secure: false
|
175
217
|
```
|
@@ -265,6 +307,52 @@ output:
|
|
265
307
|
repo: "/var/lib/oxidized/devices.git"
|
266
308
|
```
|
267
309
|
|
310
|
+
### Output types
|
311
|
+
|
312
|
+
If you prefer to have different outputs in different files and/or directories, you can easily do this by modifying the corresponding model. To change the behaviour for IOS, you would edit `lib/oxidized/model/ios.rb`.
|
313
|
+
|
314
|
+
For example, let's say you want to split out `show version` and `show inventory` into separate files in a directory called `nodiff` which your tools will not send automated diffstats for. You can apply a patch along the lines of
|
315
|
+
|
316
|
+
```
|
317
|
+
- cmd 'show version' do |cfg|
|
318
|
+
- comment cfg.lines.first
|
319
|
+
+ cmd 'show version' do |state|
|
320
|
+
+ state.type = 'nodiff'
|
321
|
+
+ state
|
322
|
+
|
323
|
+
- cmd 'show inventory' do |cfg|
|
324
|
+
- comment cfg
|
325
|
+
+ cmd 'show inventory' do |state|
|
326
|
+
+ state.type = 'nodiff'
|
327
|
+
+ state
|
328
|
+
+ end
|
329
|
+
|
330
|
+
- cmd 'show running-config' do |cfg|
|
331
|
+
- cfg = cfg.each_line.to_a[3..-1].join
|
332
|
+
- cfg.gsub! /^Current configuration : [^\n]*\n/, ''
|
333
|
+
- cfg.sub! /^(ntp clock-period).*/, '! \1'
|
334
|
+
- cfg.gsub! /^\ tunnel\ mpls\ traffic-eng\ bandwidth[^\n]*\n*(
|
335
|
+
+ cmd 'show running-config' do |state|
|
336
|
+
+ state = state.each_line.to_a[3..-1].join
|
337
|
+
+ state.gsub! /^Current configuration : [^\n]*\n/, ''
|
338
|
+
+ state.sub! /^(ntp clock-period).*/, '! \1'
|
339
|
+
+ state.gsub! /^\ tunnel\ mpls\ traffic-eng\ bandwidth[^\n]*\n*(
|
340
|
+
(?:\ [^\n]*\n*)*
|
341
|
+
tunnel\ mpls\ traffic-eng\ auto-bw)/mx, '\1'
|
342
|
+
- cfg
|
343
|
+
+ state = Oxidized::String.new state
|
344
|
+
+ state.type = 'nodiff'
|
345
|
+
+ state
|
346
|
+
```
|
347
|
+
|
348
|
+
which will result in the following layout
|
349
|
+
|
350
|
+
```
|
351
|
+
diff/$FQDN--show_running_config
|
352
|
+
nodiff/$FQDN--show_version
|
353
|
+
nodiff/$FQDN--show_inventory
|
354
|
+
```
|
355
|
+
|
268
356
|
### Advanced Configuration
|
269
357
|
|
270
358
|
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.
|
@@ -313,6 +401,57 @@ model_map:
|
|
313
401
|
juniper: junos
|
314
402
|
```
|
315
403
|
|
404
|
+
# Hooks
|
405
|
+
You can define arbitrary number of hooks that subscribe different events. The hook system is modular and different kind of hook types can be enabled.
|
406
|
+
|
407
|
+
## Configuration
|
408
|
+
Following configuration keys need to be defined for all hooks:
|
409
|
+
|
410
|
+
* `events`: which events to subscribe. Needs to be an array. See below for the list of available events.
|
411
|
+
* `type`: what hook class to use. See below for the list of available hook types.
|
412
|
+
|
413
|
+
### Events
|
414
|
+
* `node_success`: triggered when configuration is succesfully pulled from a node and right before storing the configuration.
|
415
|
+
* `node_fail`: triggered after `retries` amount of failed node pulls.
|
416
|
+
* `post_store`: triggered after node configuration is stored.
|
417
|
+
|
418
|
+
## Hook type: exec
|
419
|
+
The `exec` hook type allows users to run an arbitrary shell command or a binary when triggered.
|
420
|
+
|
421
|
+
The command is executed on a separate child process either in synchronous or asynchronous fashion. Non-zero exit values cause errors to be logged. STDOUT and STDERR are currently not collected.
|
422
|
+
|
423
|
+
Command is executed with the following environment:
|
424
|
+
```
|
425
|
+
OX_EVENT
|
426
|
+
OX_NODE_NAME
|
427
|
+
OX_NODE_FROM
|
428
|
+
OX_NODE_MSG
|
429
|
+
OX_NODE_GROUP
|
430
|
+
OX_JOB_STATUS
|
431
|
+
OX_JOB_TIME
|
432
|
+
```
|
433
|
+
|
434
|
+
Exec hook recognizes following configuration keys:
|
435
|
+
|
436
|
+
* `timeout`: hard timeout for the command execution. SIGTERM will be sent to the child process after the timeout has elapsed. Default: 60
|
437
|
+
* `async`: influences whether main thread will wait for the command execution. Set this true for long running commands so node pull is not blocked. Default: false
|
438
|
+
* `cmd`: command to run.
|
439
|
+
|
440
|
+
|
441
|
+
## Hook configuration example
|
442
|
+
```
|
443
|
+
hooks:
|
444
|
+
name_for_example_hook1:
|
445
|
+
type: exec
|
446
|
+
events: [node_success]
|
447
|
+
cmd: 'echo "Node success $OX_NODE_NAME" >> /tmp/ox_node_success.log'
|
448
|
+
name_for_example_hook2:
|
449
|
+
type: exec
|
450
|
+
events: [post_store, node_fail]
|
451
|
+
cmd: 'echo "Doing long running stuff for $OX_NODE_NAME" >> /tmp/ox_node_stuff.log; sleep 60'
|
452
|
+
async: true
|
453
|
+
timeout: 120
|
454
|
+
```
|
316
455
|
|
317
456
|
# Ruby API
|
318
457
|
|
@@ -345,3 +484,22 @@ The following objects exist in Oxidized.
|
|
345
484
|
* cfg is executed in input/output/source context
|
346
485
|
* cmd is executed in instance of model
|
347
486
|
* 'junos', 'ios', 'ironware' and 'powerconnect' implemented
|
487
|
+
|
488
|
+
|
489
|
+
# License and Copyright
|
490
|
+
|
491
|
+
Copyright 2013-2015 Saku Ytti <saku@ytti.fi>
|
492
|
+
2013-2015 Samer Abdel-Hafez <sam@arahant.net>
|
493
|
+
|
494
|
+
|
495
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
496
|
+
you may not use this file except in compliance with the License.
|
497
|
+
You may obtain a copy of the License at
|
498
|
+
|
499
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
500
|
+
|
501
|
+
Unless required by applicable law or agreed to in writing, software
|
502
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
503
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
504
|
+
See the License for the specific language governing permissions and
|
505
|
+
limitations under the License.
|
@@ -6,20 +6,30 @@ require 'open-uri'
|
|
6
6
|
require 'json'
|
7
7
|
|
8
8
|
critical = false
|
9
|
+
pending = false
|
9
10
|
critical_nodes = []
|
11
|
+
pending_nodes = []
|
10
12
|
|
11
13
|
json = JSON.load(open("http://localhost:8888/nodes.json"))
|
12
14
|
json.each do |node|
|
13
|
-
if node['last']
|
14
|
-
|
15
|
-
|
15
|
+
if not node['last'].nil?
|
16
|
+
if node['last']['status'] != 'success'
|
17
|
+
critical_nodes << node['name']
|
18
|
+
critical = true
|
19
|
+
end
|
20
|
+
else
|
21
|
+
pending_nodes << node['name']
|
22
|
+
pending = true
|
16
23
|
end
|
17
24
|
end
|
18
25
|
|
19
|
-
if
|
20
|
-
puts '
|
26
|
+
if pending
|
27
|
+
puts '[WARN] Pending backup: ' + pending_nodes.join(',')
|
28
|
+
exit 1
|
29
|
+
elsif critical
|
30
|
+
puts '[CRIT] Unable to backup: ' + critical_nodes.join(',')
|
21
31
|
exit 2
|
22
32
|
else
|
23
|
-
puts 'Backup of all nodes completed successfully.'
|
33
|
+
puts '[OK] Backup of all nodes completed successfully.'
|
24
34
|
exit 0
|
25
35
|
end
|
data/extra/syslog.rb
CHANGED
@@ -8,10 +8,18 @@
|
|
8
8
|
# set system syslog host SERVER interactive-commands notice
|
9
9
|
# set system syslog host SERVER match "^mgd\[[0-9]+\]: UI_COMMIT: .*"
|
10
10
|
|
11
|
-
# Ports < 1024 need extra privileges, use a port higher than this by
|
12
|
-
# To use the default port for syslog (514) you
|
11
|
+
# Ports < 1024 need extra privileges, use a port higher than this by setting the port option in your oxidized config file.
|
12
|
+
# To use the default port for syslog (514) you shouldn't pass an argument, but you will need to allow this with:
|
13
13
|
# sudo setcap 'cap_net_bind_service=+ep' /usr/bin/ruby
|
14
14
|
|
15
|
+
# Config options are:
|
16
|
+
# syslogd
|
17
|
+
# port (Default = 514)
|
18
|
+
# file (Default = messages)
|
19
|
+
# resolve (Default = true)
|
20
|
+
|
21
|
+
# To stop the resolution of IP's to PTR you can set resolve to false
|
22
|
+
|
15
23
|
# exit if fork ## TODO: proper daemonize
|
16
24
|
|
17
25
|
require 'socket'
|
@@ -19,26 +27,42 @@ require 'resolv'
|
|
19
27
|
require_relative 'rest_client'
|
20
28
|
|
21
29
|
module Oxidized
|
30
|
+
|
31
|
+
require 'asetus'
|
32
|
+
class Config
|
33
|
+
Root = File.join ENV['HOME'], '.config', 'oxidized'
|
34
|
+
end
|
35
|
+
|
36
|
+
CFGS = Asetus.new :name=>'oxidized', :load=>false, :key_to_s=>true
|
37
|
+
CFGS.default.syslogd.port = 514
|
38
|
+
CFGS.default.syslogd.file = 'messages'
|
39
|
+
CFGS.default.syslogd.resolve = true
|
40
|
+
|
41
|
+
begin
|
42
|
+
CFGS.load
|
43
|
+
rescue => error
|
44
|
+
raise InvalidConfig, "Error loading config: #{error.message}"
|
45
|
+
ensure
|
46
|
+
CFG = CFGS.cfg # convenienence, instead of Config.cfg.password, CFG.password
|
47
|
+
end
|
48
|
+
|
22
49
|
class SyslogMonitor
|
23
50
|
NAME_MAP = {
|
24
51
|
/(.*)\.ip\.tdc\.net/ => '\1',
|
25
52
|
/(.*)\.ip\.fi/ => '\1',
|
26
53
|
}
|
27
|
-
PORT = 514
|
28
|
-
FILE = 'messages'
|
29
54
|
MSG = {
|
30
55
|
:ios => /%SYS-(SW[0-9]+-)?5-CONFIG_I:/,
|
31
56
|
:junos => 'UI_COMMIT:',
|
32
57
|
}
|
33
58
|
|
34
59
|
class << self
|
35
|
-
def udp port=
|
36
|
-
port ||= PORT
|
60
|
+
def udp port=Oxidized::CFG.syslogd.port, listen=0
|
37
61
|
io = UDPSocket.new
|
38
62
|
io.bind listen, port
|
39
63
|
new io, :udp
|
40
64
|
end
|
41
|
-
def file syslog_file=
|
65
|
+
def file syslog_file=Oxidized::CFG.syslogd.file
|
42
66
|
io = open syslog_file, 'r'
|
43
67
|
io.seek 0, IO::SEEK_END
|
44
68
|
new io, :file
|
@@ -102,12 +126,16 @@ module Oxidized
|
|
102
126
|
end
|
103
127
|
|
104
128
|
def getname ip
|
105
|
-
|
106
|
-
|
107
|
-
|
129
|
+
if Oxidized::CFG.syslogd.resolve == false
|
130
|
+
ip
|
131
|
+
else
|
132
|
+
name = (Resolv.getname ip.to_s rescue ip)
|
133
|
+
NAME_MAP.each { |re, sub| name.sub! re, sub }
|
134
|
+
name
|
135
|
+
end
|
108
136
|
end
|
109
137
|
end
|
110
138
|
end
|
111
139
|
|
112
|
-
Oxidized::SyslogMonitor.udp
|
140
|
+
Oxidized::SyslogMonitor.udp
|
113
141
|
#Oxidized::SyslogMonitor.file '/var/log/poop'
|
data/lib/oxidized/config.rb
CHANGED
@@ -9,10 +9,11 @@ module Oxidized
|
|
9
9
|
OutputDir = File.join Directory, %w(lib oxidized output)
|
10
10
|
ModelDir = File.join Directory, %w(lib oxidized model)
|
11
11
|
SourceDir = File.join Directory, %w(lib oxidized source)
|
12
|
+
HookDir = File.join Directory, %w(lib oxidized hook)
|
12
13
|
Sleep = 1
|
13
14
|
end
|
14
15
|
class << self
|
15
|
-
attr_accessor :mgr
|
16
|
+
attr_accessor :mgr, :Hooks
|
16
17
|
end
|
17
18
|
CFGS = Asetus.new :name=>'oxidized', :load=>false, :key_to_s=>true
|
18
19
|
CFGS.default.username = 'username'
|
data/lib/oxidized/core.rb
CHANGED
@@ -6,6 +6,7 @@ module Oxidized
|
|
6
6
|
require 'oxidized/worker'
|
7
7
|
require 'oxidized/nodes'
|
8
8
|
require 'oxidized/manager'
|
9
|
+
require 'oxidized/hook'
|
9
10
|
class << self
|
10
11
|
def new *args
|
11
12
|
Core.new args
|
@@ -13,9 +14,13 @@ module Oxidized
|
|
13
14
|
end
|
14
15
|
|
15
16
|
class Core
|
17
|
+
class NoNodesFound < OxidizedError; end
|
18
|
+
|
16
19
|
def initialize args
|
17
20
|
Oxidized.mgr = Manager.new
|
21
|
+
Oxidized.Hooks = HookManager.from_config CFG
|
18
22
|
nodes = Nodes.new
|
23
|
+
raise NoNodesFound, 'source returns no usable nodes' if nodes.size == 0
|
19
24
|
@worker = Worker.new nodes
|
20
25
|
trap('HUP') { nodes.load }
|
21
26
|
if CFG.rest?
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Oxidized
|
2
|
+
class HookManager
|
3
|
+
class << self
|
4
|
+
def from_config cfg
|
5
|
+
mgr = new
|
6
|
+
cfg.hooks.each do |name,h_cfg|
|
7
|
+
h_cfg.events.each do |event|
|
8
|
+
mgr.register event.to_sym, name, h_cfg.type, h_cfg
|
9
|
+
end
|
10
|
+
end
|
11
|
+
mgr
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# HookContext is passed to each hook. It can contain anything related to the
|
16
|
+
# event in question. At least it contains the event name
|
17
|
+
class HookContext < OpenStruct; end
|
18
|
+
|
19
|
+
# RegisteredHook is a container for a Hook instance
|
20
|
+
class RegisteredHook < Struct.new(:name, :hook); end
|
21
|
+
|
22
|
+
Events = [
|
23
|
+
:node_success,
|
24
|
+
:node_fail,
|
25
|
+
:post_store,
|
26
|
+
]
|
27
|
+
attr_reader :registered_hooks
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@registered_hooks = Hash.new {|h,k| h[k] = []}
|
31
|
+
end
|
32
|
+
|
33
|
+
def register event, name, hook_type, cfg
|
34
|
+
unless Events.include? event
|
35
|
+
raise ArgumentError,
|
36
|
+
"unknown event #{event}, available: #{Events.join ','}"
|
37
|
+
end
|
38
|
+
|
39
|
+
Oxidized.mgr.add_hook hook_type
|
40
|
+
begin
|
41
|
+
hook = Oxidized.mgr.hook.fetch(hook_type).new
|
42
|
+
rescue KeyError
|
43
|
+
raise KeyError, "cannot find hook #{hook_type.inspect}"
|
44
|
+
end
|
45
|
+
|
46
|
+
hook.cfg = cfg
|
47
|
+
|
48
|
+
@registered_hooks[event] << RegisteredHook.new(name, hook)
|
49
|
+
Log.debug "Hook #{name.inspect} registered #{hook.class} for event #{event.inspect}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def handle event, **ctx_params
|
53
|
+
ctx = HookContext.new ctx_params
|
54
|
+
ctx.event = event
|
55
|
+
|
56
|
+
@registered_hooks[event].each do |r_hook|
|
57
|
+
begin
|
58
|
+
r_hook.hook.run_hook ctx
|
59
|
+
rescue => e
|
60
|
+
Log.error "Hook #{r_hook.name} (#{r_hook.hook}) failed " +
|
61
|
+
"(#{e.inspect}) for event #{event.inspect}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Hook abstract base class
|
68
|
+
class Hook
|
69
|
+
attr_accessor :cfg
|
70
|
+
|
71
|
+
def initialize
|
72
|
+
end
|
73
|
+
|
74
|
+
def cfg=(cfg)
|
75
|
+
@cfg = cfg
|
76
|
+
validate_cfg! if self.respond_to? :validate_cfg!
|
77
|
+
end
|
78
|
+
|
79
|
+
def run_hook ctx
|
80
|
+
raise NotImplementedError
|
81
|
+
end
|
82
|
+
|
83
|
+
def log(msg, level=:info)
|
84
|
+
Log.send(level, "#{self.class.name}: #{msg}")
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
class Exec < Oxidized::Hook
|
2
|
+
include Process
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
super
|
6
|
+
@timeout = 60
|
7
|
+
@async = false
|
8
|
+
end
|
9
|
+
|
10
|
+
def validate_cfg!
|
11
|
+
# Syntax check
|
12
|
+
if cfg.has_key? "timeout"
|
13
|
+
@timeout = cfg.timeout
|
14
|
+
raise "invalid timeout value" unless @timeout.is_a?(Integer) &&
|
15
|
+
@timeout > 0
|
16
|
+
end
|
17
|
+
|
18
|
+
if cfg.has_key? "async"
|
19
|
+
@async = !!cfg.async
|
20
|
+
end
|
21
|
+
|
22
|
+
if cfg.has_key? "cmd"
|
23
|
+
@cmd = cfg.cmd
|
24
|
+
raise "invalid cmd value" unless @cmd.is_a?(String) || @cmd.is_a?(Array)
|
25
|
+
end
|
26
|
+
|
27
|
+
rescue RuntimeError => e
|
28
|
+
raise ArgumentError,
|
29
|
+
"#{self.class.name}: configuration invalid: #{e.message}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def run_hook ctx
|
33
|
+
env = make_env ctx
|
34
|
+
log "Execute: #{@cmd.inspect}", :debug
|
35
|
+
th = Thread.new do
|
36
|
+
begin
|
37
|
+
run_cmd! env
|
38
|
+
rescue => e
|
39
|
+
raise e unless @async
|
40
|
+
end
|
41
|
+
end
|
42
|
+
th.join unless @async
|
43
|
+
end
|
44
|
+
|
45
|
+
def run_cmd! env
|
46
|
+
pid, status = nil, nil
|
47
|
+
Timeout.timeout(@timeout) do
|
48
|
+
pid = spawn env, @cmd , :unsetenv_others => true
|
49
|
+
pid, status = wait2 pid
|
50
|
+
unless status.exitstatus.zero?
|
51
|
+
msg = "#{@cmd.inspect} failed with exit value #{status.exitstatus}"
|
52
|
+
log msg, :error
|
53
|
+
raise msg
|
54
|
+
end
|
55
|
+
end
|
56
|
+
rescue TimeoutError
|
57
|
+
kill "TERM", pid
|
58
|
+
msg = "#{@cmd} timed out"
|
59
|
+
log msg, :error
|
60
|
+
raise TimeoutError, msg
|
61
|
+
end
|
62
|
+
|
63
|
+
def make_env ctx
|
64
|
+
env = {
|
65
|
+
"OX_EVENT" => ctx.event.to_s
|
66
|
+
}
|
67
|
+
if ctx.node
|
68
|
+
env.merge!(
|
69
|
+
"OX_NODE_NAME" => ctx.node.name.to_s,
|
70
|
+
"OX_NODE_FROM" => ctx.node.from.to_s,
|
71
|
+
"OX_NODE_MSG" => ctx.node.msg.to_s,
|
72
|
+
"OX_NODE_GROUP" => ctx.node.group.to_s,
|
73
|
+
"OX_EVENT" => ctx.event.to_s,
|
74
|
+
)
|
75
|
+
end
|
76
|
+
if ctx.job
|
77
|
+
env.merge!(
|
78
|
+
"OX_JOB_STATUS" => ctx.job.status.to_s,
|
79
|
+
"OX_JOB_TIME" => ctx.job.time.to_s,
|
80
|
+
)
|
81
|
+
end
|
82
|
+
env
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Oxidized
|
2
|
+
require 'net/ftp'
|
3
|
+
require 'timeout'
|
4
|
+
require_relative 'cli'
|
5
|
+
|
6
|
+
class FTP < Input
|
7
|
+
RescueFail = {
|
8
|
+
:debug => [
|
9
|
+
#Net::SSH::Disconnect,
|
10
|
+
],
|
11
|
+
:warn => [
|
12
|
+
#RuntimeError,
|
13
|
+
#Net::SSH::AuthenticationFailed,
|
14
|
+
],
|
15
|
+
}
|
16
|
+
include Input::CLI
|
17
|
+
|
18
|
+
def connect node
|
19
|
+
@node = node
|
20
|
+
@node.model.cfg['ftp'].each { |cb| instance_exec(&cb) }
|
21
|
+
@log = File.open(CFG.input.debug?.to_s + '-ftp', 'w') if CFG.input.debug?
|
22
|
+
@ftp = Net::FTP.new @node.ip, @node.auth[:username], @node.auth[:password]
|
23
|
+
connected?
|
24
|
+
end
|
25
|
+
|
26
|
+
def connected?
|
27
|
+
@ftp and not @ftp.closed?
|
28
|
+
end
|
29
|
+
|
30
|
+
def cmd file
|
31
|
+
Log.debug "FTP: #{file} @ #{@node.name}"
|
32
|
+
@ftp.getbinaryfile file, nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# meh not sure if this is the best way, but perhaps better than not implementing send
|
36
|
+
def send my_proc
|
37
|
+
my_proc.call
|
38
|
+
end
|
39
|
+
|
40
|
+
def output
|
41
|
+
""
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def disconnect
|
47
|
+
@ftp.close
|
48
|
+
#rescue Errno::ECONNRESET, IOError
|
49
|
+
ensure
|
50
|
+
@log.close if CFG.input.debug?
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
data/lib/oxidized/input/ssh.rb
CHANGED
@@ -21,7 +21,8 @@ module Oxidized
|
|
21
21
|
@node.model.cfg['ssh'].each { |cb| instance_exec(&cb) }
|
22
22
|
secure = CFG.input.ssh.secure
|
23
23
|
@log = File.open(CFG.input.debug?.to_s + '-ssh', 'w') if CFG.input.debug?
|
24
|
-
|
24
|
+
port = vars(:ssh_port) || 22
|
25
|
+
@ssh = Net::SSH.start @node.ip, @node.auth[:username], :port => port.to_i,
|
25
26
|
:password => @node.auth[:password], :timeout => CFG.timeout,
|
26
27
|
:paranoid => secure,
|
27
28
|
:auth_methods => %w(none publickey password keyboard-interactive),
|
@@ -10,9 +10,12 @@ module Oxidized
|
|
10
10
|
@node = node
|
11
11
|
@timeout = CFG.timeout
|
12
12
|
@node.model.cfg['telnet'].each { |cb| instance_exec(&cb) }
|
13
|
+
port = vars(:telnet_port) || 23
|
13
14
|
|
14
|
-
opt = { 'Host'
|
15
|
-
'
|
15
|
+
opt = { 'Host' => @node.ip,
|
16
|
+
'Port' => port.to_i,
|
17
|
+
'Timeout' => @timeout,
|
18
|
+
'Model' => @node.model }
|
16
19
|
opt['Output_log'] = CFG.input.debug?.to_s + '-telnet' if CFG.input.debug?
|
17
20
|
|
18
21
|
@telnet = Net::Telnet.new opt
|
data/lib/oxidized/manager.rb
CHANGED
@@ -23,12 +23,13 @@ module Oxidized
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
26
|
-
attr_reader :input, :output, :model, :source
|
26
|
+
attr_reader :input, :output, :model, :source, :hook
|
27
27
|
def initialize
|
28
28
|
@input = {}
|
29
29
|
@output = {}
|
30
30
|
@model = {}
|
31
31
|
@source = {}
|
32
|
+
@hook = {}
|
32
33
|
end
|
33
34
|
def add_input method
|
34
35
|
method = Manager.load Config::InputDir, method
|
@@ -53,5 +54,13 @@ module Oxidized
|
|
53
54
|
return false if _source.empty?
|
54
55
|
@source.merge! _source
|
55
56
|
end
|
57
|
+
def add_hook _hook
|
58
|
+
return nil if @hook.key? _hook
|
59
|
+
name = _hook
|
60
|
+
_hook = Manager.load File.join(Config::Root, 'hook'), name
|
61
|
+
_hook = Manager.load Config::HookDir, name if _hook.empty?
|
62
|
+
return false if _hook.empty?
|
63
|
+
@hook.merge! _hook
|
64
|
+
end
|
56
65
|
end
|
57
66
|
end
|
data/lib/oxidized/model/aosw.rb
CHANGED
@@ -5,7 +5,7 @@ class AOSW < Oxidized::Model
|
|
5
5
|
# Also Dell controllers
|
6
6
|
|
7
7
|
comment '# '
|
8
|
-
prompt /^\([^)]+\)
|
8
|
+
prompt /^\([^)]+\) [#>]/
|
9
9
|
|
10
10
|
cmd :all do |cfg|
|
11
11
|
cfg.each_line.to_a[1..-2].join
|
@@ -36,7 +36,16 @@ class AOSW < Oxidized::Model
|
|
36
36
|
end
|
37
37
|
|
38
38
|
cfg :telnet, :ssh do
|
39
|
+
if vars :enable
|
40
|
+
post_login do
|
41
|
+
send 'enable\n'
|
42
|
+
send vars(:enable) + '\n'
|
43
|
+
end
|
44
|
+
end
|
39
45
|
post_login 'no paging'
|
46
|
+
if vars :enable
|
47
|
+
pre_logout 'exit'
|
48
|
+
end
|
40
49
|
pre_logout 'exit'
|
41
50
|
end
|
42
51
|
|
@@ -50,7 +59,7 @@ class AOSW < Oxidized::Model
|
|
50
59
|
next if line.match /[0-9]+ (RPM|mV|C)$/
|
51
60
|
out << line.strip
|
52
61
|
end
|
53
|
-
out = out.join "\n"
|
62
|
+
out = comment out.join "\n"
|
54
63
|
out << "\n"
|
55
64
|
end
|
56
65
|
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class Edgeos < Oxidized::Model
|
2
|
+
|
3
|
+
# EdgeOS #
|
4
|
+
|
5
|
+
prompt /\@.*?\:~\$\s/
|
6
|
+
|
7
|
+
cmd :all do |cfg|
|
8
|
+
cfg = cfg.lines.to_a[1..-2].join
|
9
|
+
end
|
10
|
+
|
11
|
+
cmd :secret do |cfg|
|
12
|
+
cfg.gsub! /community (\S+) {/, 'community <hidden> {'
|
13
|
+
cfg
|
14
|
+
end
|
15
|
+
|
16
|
+
cmd 'show configuration | no-more'
|
17
|
+
|
18
|
+
cfg :telnet do
|
19
|
+
username /login:\s/
|
20
|
+
password /^Password:\s/
|
21
|
+
end
|
22
|
+
|
23
|
+
cfg :telnet, :ssh do
|
24
|
+
pre_logout 'exit'
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
class IronWare < Oxidized::Model
|
2
2
|
|
3
|
-
prompt
|
3
|
+
prompt /^.*(telnet|ssh)\@.+[>#]\s?$/i
|
4
4
|
comment '! '
|
5
5
|
|
6
6
|
#to handle pager without enable
|
@@ -26,13 +26,13 @@ class IronWare < Oxidized::Model
|
|
26
26
|
|
27
27
|
cmd 'show version' do |cfg|
|
28
28
|
cfg.gsub! /(^((.*)[Ss]ystem uptime(.*))$)/, '' #remove unwanted line system uptime
|
29
|
-
cfg.gsub! /
|
29
|
+
cfg.gsub! /[Uu]p\s?[Tt]ime is .*/,''
|
30
30
|
|
31
31
|
comment cfg
|
32
32
|
end
|
33
33
|
|
34
34
|
cmd 'show chassis' do |cfg|
|
35
|
-
cfg.
|
35
|
+
cfg.encode!("UTF-8", :invalid => :replace) #sometimes ironware returns broken encoding
|
36
36
|
cfg.gsub! /(^((.*)Current temp(.*))$)/, '' #remove unwanted lines current temperature
|
37
37
|
cfg.gsub! /Speed = [A-Z]{3} \(\d{2}\%\)/, '' #remove unwanted lines Speed Fans
|
38
38
|
cfg.gsub! /current speed is [A-Z]{3} \(\d{2}\%\)/, ''
|
@@ -71,6 +71,7 @@ class IronWare < Oxidized::Model
|
|
71
71
|
send vars(:enable) + "\n"
|
72
72
|
end
|
73
73
|
end
|
74
|
+
post_login ''
|
74
75
|
post_login 'skip-page-display'
|
75
76
|
post_login 'terminal length 0'
|
76
77
|
pre_logout 'logout'
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class MasterOS < Oxidized::Model
|
2
|
+
|
3
|
+
# MRV MasterOS model #
|
4
|
+
|
5
|
+
comment '!'
|
6
|
+
|
7
|
+
cmd :secret do |cfg|
|
8
|
+
cfg.gsub! /^(snmp-server community).*/, '\\1 <configuration removed>'
|
9
|
+
cfg.gsub! /username (\S+) password encrypted (\S+) class (\S+).*/, '<secret hidden>'
|
10
|
+
cfg
|
11
|
+
end
|
12
|
+
|
13
|
+
cmd :all do |cfg|
|
14
|
+
cfg.each_line.to_a[1..-2].join
|
15
|
+
end
|
16
|
+
|
17
|
+
cmd 'show inventory' do |cfg|
|
18
|
+
cfg = cfg.each_line.to_a[0..-2].join
|
19
|
+
comment cfg
|
20
|
+
end
|
21
|
+
|
22
|
+
cmd 'show plugins' do |cfg|
|
23
|
+
comment cfg
|
24
|
+
end
|
25
|
+
|
26
|
+
cmd 'show hw-config' do |cfg|
|
27
|
+
comment cfg
|
28
|
+
end
|
29
|
+
|
30
|
+
cmd 'show running-config' do |cfg|
|
31
|
+
cfg = cfg.each_line.to_a[3..-1].join
|
32
|
+
cfg
|
33
|
+
end
|
34
|
+
|
35
|
+
cfg :telnet, :ssh do
|
36
|
+
post_login 'no pager'
|
37
|
+
if vars :enable
|
38
|
+
post_login do
|
39
|
+
send "enable\n"
|
40
|
+
send vars(:enable) + "\n"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
pre_logout 'exit'
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class RouterOS < Oxidized::Model
|
2
|
-
prompt
|
2
|
+
prompt /\[\w+@\S+\]\s?>\s?$/
|
3
3
|
comment "# "
|
4
4
|
|
5
5
|
cmd '/system routerboard print' do |cfg|
|
@@ -7,10 +7,16 @@ class RouterOS < Oxidized::Model
|
|
7
7
|
end
|
8
8
|
|
9
9
|
cmd '/export' do |cfg|
|
10
|
+
cfg.gsub! /\x1B\[([0-9]{1,3}((;[0-9]{1,3})*)?)?[m|K]/, '' # strip ANSI colours
|
10
11
|
cfg = cfg.split("\n").select { |line| not line[/^\#\s\w{3}\/\d{2}\/\d{4}.*$/] }
|
11
12
|
cfg.join("\n") + "\n"
|
12
13
|
end
|
13
14
|
|
15
|
+
cfg :telnet do
|
16
|
+
username /^Login:/
|
17
|
+
password /^Password:/
|
18
|
+
end
|
19
|
+
|
14
20
|
cfg :ssh do
|
15
21
|
exec true
|
16
22
|
end
|
data/lib/oxidized/model/xos.rb
CHANGED
data/lib/oxidized/node.rb
CHANGED
@@ -29,6 +29,9 @@ module Oxidized
|
|
29
29
|
def run
|
30
30
|
status, config = :fail, nil
|
31
31
|
@input.each do |input|
|
32
|
+
# don't try input if model is missing config block, we may need strong config to class_name map
|
33
|
+
cfg_name = input.to_s.split('::').last.downcase
|
34
|
+
next unless @model.cfg[cfg_name] and not @model.cfg[cfg_name].empty?
|
32
35
|
@model.input = input = input.new
|
33
36
|
if config=run_input(input)
|
34
37
|
status = :success
|
data/lib/oxidized/nodes.rb
CHANGED
data/lib/oxidized/output/git.rb
CHANGED
@@ -19,6 +19,7 @@ class Git < Output
|
|
19
19
|
CFGS.save :user
|
20
20
|
raise NoConfig, 'no output git config, edit ~/.config/oxidized/config'
|
21
21
|
end
|
22
|
+
@cfg.repo = File.expand_path @cfg.repo
|
22
23
|
end
|
23
24
|
|
24
25
|
def store file, outputs, opt={}
|
@@ -50,18 +51,18 @@ class Git < Output
|
|
50
51
|
def fetch node, group
|
51
52
|
begin
|
52
53
|
repo = @cfg.repo
|
53
|
-
if group
|
54
|
-
repo = File.join File.dirname(repo), group + '.git'
|
55
|
-
end
|
54
|
+
repo = File.join File.dirname(repo), group + '.git' if group and not @cfg.single_repo?
|
56
55
|
repo = Rugged::Repository.new repo
|
57
56
|
index = repo.index
|
58
57
|
index.read_tree repo.head.target.tree unless repo.empty?
|
59
|
-
|
58
|
+
file = node
|
59
|
+
file = File.join(group, node) if group and @cfg.single_repo?
|
60
|
+
repo.read(index.get(file)[:oid]).data
|
60
61
|
rescue
|
61
62
|
'node not found'
|
62
63
|
end
|
63
64
|
end
|
64
|
-
|
65
|
+
|
65
66
|
#give a hash of all oid revision for the givin node, and the date of the commit
|
66
67
|
def version node, group
|
67
68
|
begin
|
@@ -69,7 +70,7 @@ class Git < Output
|
|
69
70
|
if group
|
70
71
|
repo = File.join File.dirname(repo), group + '.git'
|
71
72
|
end
|
72
|
-
repo = Rugged::Repository.new repo
|
73
|
+
repo = Rugged::Repository.new repo
|
73
74
|
walker = Rugged::Walker.new(repo)
|
74
75
|
walker.sorting(Rugged::SORT_DATE)
|
75
76
|
walker.push(repo.head.target)
|
@@ -78,8 +79,10 @@ class Git < Output
|
|
78
79
|
walker.each do |commit|
|
79
80
|
if commit.diff(paths: [node]).size > 0
|
80
81
|
hash = {}
|
81
|
-
hash[:date] = commit.time.to_s
|
82
|
+
hash[:date] = commit.time.to_s
|
82
83
|
hash[:oid] = commit.oid
|
84
|
+
hash[:author] = commit.author
|
85
|
+
hash[:message] = commit.message
|
83
86
|
tab[i += 1] = hash
|
84
87
|
end
|
85
88
|
end
|
@@ -89,7 +92,7 @@ class Git < Output
|
|
89
92
|
'node not found'
|
90
93
|
end
|
91
94
|
end
|
92
|
-
|
95
|
+
|
93
96
|
#give the blob of a specific revision
|
94
97
|
def get_version node, group, oid
|
95
98
|
begin
|
@@ -97,13 +100,13 @@ class Git < Output
|
|
97
100
|
if group && group != ''
|
98
101
|
repo = File.join File.dirname(repo), group + '.git'
|
99
102
|
end
|
100
|
-
repo = Rugged::Repository.new repo
|
103
|
+
repo = Rugged::Repository.new repo
|
101
104
|
repo.blob_at(oid,node).content
|
102
105
|
rescue
|
103
106
|
'version not found'
|
104
107
|
end
|
105
108
|
end
|
106
|
-
|
109
|
+
|
107
110
|
#give a hash with the patch of a diff between 2 revision and the stats (added and deleted lines)
|
108
111
|
def get_diff node, group, oid1, oid2
|
109
112
|
begin
|
@@ -112,10 +115,10 @@ class Git < Output
|
|
112
115
|
if group && group != ''
|
113
116
|
repo = File.join File.dirname(repo), group + '.git'
|
114
117
|
end
|
115
|
-
repo = Rugged::Repository.new repo
|
118
|
+
repo = Rugged::Repository.new repo
|
116
119
|
commit = repo.lookup(oid1)
|
117
|
-
#if the second revision is precised
|
118
|
-
if oid2
|
120
|
+
#if the second revision is precised
|
121
|
+
if oid2
|
119
122
|
commit_old = repo.lookup(oid2)
|
120
123
|
diff = repo.diff(commit_old, commit)
|
121
124
|
diff.each do |patch|
|
@@ -179,10 +182,10 @@ class Git < Output
|
|
179
182
|
:parents => repo.empty? ? [] : [repo.head.target].compact,
|
180
183
|
:update_ref => 'HEAD',
|
181
184
|
)
|
182
|
-
|
185
|
+
|
183
186
|
index.write
|
184
187
|
true
|
185
188
|
end
|
186
189
|
end
|
187
190
|
end
|
188
|
-
end
|
191
|
+
end
|
data/lib/oxidized/worker.rb
CHANGED
@@ -34,12 +34,16 @@ module Oxidized
|
|
34
34
|
@jobs.duration job.time
|
35
35
|
node.running = false
|
36
36
|
if job.status == :success
|
37
|
+
Oxidized.Hooks.handle :node_success, :node => node,
|
38
|
+
:job => job
|
37
39
|
msg = "update #{node.name}"
|
38
40
|
msg += " from #{node.from}" if node.from
|
39
41
|
msg += " with message '#{node.msg}'" if node.msg
|
40
42
|
if node.output.new.store node.name, job.config,
|
41
43
|
:msg => msg, :user => node.user, :group => node.group
|
42
44
|
Log.info "Configuration updated for #{node.group}/#{node.name}"
|
45
|
+
Oxidized.Hooks.handle :post_store, :node => node,
|
46
|
+
:job => job
|
43
47
|
end
|
44
48
|
node.reset
|
45
49
|
else
|
@@ -51,6 +55,8 @@ module Oxidized
|
|
51
55
|
else
|
52
56
|
msg += ", retries exhausted, giving up"
|
53
57
|
node.retry = 0
|
58
|
+
Oxidized.Hooks.handle :node_fail, :node => node,
|
59
|
+
:job => job
|
54
60
|
end
|
55
61
|
Log.warn msg
|
56
62
|
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.
|
3
|
+
s.version = '0.8.0'
|
4
4
|
s.licenses = %w( Apache-2.0 )
|
5
5
|
s.platform = Gem::Platform::RUBY
|
6
|
-
s.authors = [ 'Saku Ytti', 'Samer Abdel-Hafez' ]
|
7
|
-
s.email = %w( saku@ytti.fi sam@arahant.net )
|
6
|
+
s.authors = [ 'Saku Ytti', 'Samer Abdel-Hafez', 'Anton Aksola' ]
|
7
|
+
s.email = %w( saku@ytti.fi sam@arahant.net aakso@iki.fi)
|
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'
|
@@ -18,4 +18,5 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.add_runtime_dependency 'slop', '~> 3.5'
|
19
19
|
s.add_runtime_dependency 'net-ssh', '~> 2.8'
|
20
20
|
s.add_runtime_dependency 'rugged', '~> 0.21', '>= 0.21.4'
|
21
|
+
s.add_development_dependency 'pry', '~> 0'
|
21
22
|
end
|
metadata
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oxidized
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Saku Ytti
|
8
8
|
- Samer Abdel-Hafez
|
9
|
+
- Anton Aksola
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
|
-
date: 2015-
|
13
|
+
date: 2015-09-14 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: asetus
|
@@ -73,16 +74,32 @@ dependencies:
|
|
73
74
|
- - ">="
|
74
75
|
- !ruby/object:Gem::Version
|
75
76
|
version: 0.21.4
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: pry
|
79
|
+
requirement: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
type: :development
|
85
|
+
prerelease: false
|
86
|
+
version_requirements: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
76
91
|
description: software to fetch configuration from network devices and store them
|
77
92
|
email:
|
78
93
|
- saku@ytti.fi
|
79
94
|
- sam@arahant.net
|
95
|
+
- aakso@iki.fi
|
80
96
|
executables:
|
81
97
|
- oxidized
|
82
98
|
extensions: []
|
83
99
|
extra_rdoc_files: []
|
84
100
|
files:
|
85
101
|
- CHANGELOG.md
|
102
|
+
- Dockerfile
|
86
103
|
- Gemfile
|
87
104
|
- README.md
|
88
105
|
- Rakefile
|
@@ -100,7 +117,11 @@ files:
|
|
100
117
|
- lib/oxidized/config.rb
|
101
118
|
- lib/oxidized/config/vars.rb
|
102
119
|
- lib/oxidized/core.rb
|
120
|
+
- lib/oxidized/hook.rb
|
121
|
+
- lib/oxidized/hook/exec.rb
|
122
|
+
- lib/oxidized/hook/noophook.rb
|
103
123
|
- lib/oxidized/input/cli.rb
|
124
|
+
- lib/oxidized/input/ftp.rb
|
104
125
|
- lib/oxidized/input/input.rb
|
105
126
|
- lib/oxidized/input/ssh.rb
|
106
127
|
- lib/oxidized/input/telnet.rb
|
@@ -118,6 +139,7 @@ files:
|
|
118
139
|
- lib/oxidized/model/ciscosmb.rb
|
119
140
|
- lib/oxidized/model/comware.rb
|
120
141
|
- lib/oxidized/model/cumulus.rb
|
142
|
+
- lib/oxidized/model/edgeos.rb
|
121
143
|
- lib/oxidized/model/eos.rb
|
122
144
|
- lib/oxidized/model/fabricos.rb
|
123
145
|
- lib/oxidized/model/fortios.rb
|
@@ -127,6 +149,7 @@ files:
|
|
127
149
|
- lib/oxidized/model/ironware.rb
|
128
150
|
- lib/oxidized/model/isam.rb
|
129
151
|
- lib/oxidized/model/junos.rb
|
152
|
+
- lib/oxidized/model/masteros.rb
|
130
153
|
- lib/oxidized/model/model.rb
|
131
154
|
- lib/oxidized/model/nos.rb
|
132
155
|
- lib/oxidized/model/nxos.rb
|
@@ -140,6 +163,7 @@ files:
|
|
140
163
|
- lib/oxidized/model/vrp.rb
|
141
164
|
- lib/oxidized/model/vyatta.rb
|
142
165
|
- lib/oxidized/model/xos.rb
|
166
|
+
- lib/oxidized/model/zynos.rb
|
143
167
|
- lib/oxidized/node.rb
|
144
168
|
- lib/oxidized/node/stats.rb
|
145
169
|
- lib/oxidized/nodes.rb
|
@@ -174,7 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
174
198
|
version: '0'
|
175
199
|
requirements: []
|
176
200
|
rubyforge_project: oxidized
|
177
|
-
rubygems_version: 2.
|
201
|
+
rubygems_version: 2.2.2
|
178
202
|
signing_key:
|
179
203
|
specification_version: 4
|
180
204
|
summary: feeble attempt at rancid
|