oxidized 0.7.2 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|