oxidized 0.29.1 → 0.30.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/.github/workflows/codeql.yml +4 -4
- data/.github/workflows/publishdocker.yml +3 -0
- data/.github/workflows/ruby.yml +1 -1
- data/.github/workflows/stale.yml +4 -1
- data/.rubocop.yml +6 -3
- data/.rubocop_todo.yml +10 -93
- data/CHANGELOG.md +66 -4
- data/CONTRIBUTING.md +174 -0
- data/Dockerfile +12 -1
- data/README.md +18 -36
- data/bin/oxidized +2 -5
- data/docs/Hooks.md +37 -1
- data/docs/Model-Notes/APC_AOS.md +52 -0
- data/docs/Model-Notes/FSOS.md +11 -0
- data/docs/Model-Notes/FortiOS.md +28 -0
- data/docs/Model-Notes/README.md +1 -20
- data/docs/Model-Notes/XGS4600-Zyxel.md +5 -0
- data/docs/Ruby-API.md +1 -1
- data/docs/Sources.md +13 -0
- data/docs/Supported-OS-Types.md +178 -270
- data/docs/Troubleshooting.md +16 -0
- data/examples/podman-compose/Makefile +61 -0
- data/examples/podman-compose/README.md +58 -0
- data/examples/podman-compose/docker-compose.yml +21 -0
- data/examples/podman-compose/model-simulation/Dockerfile-model +13 -0
- data/examples/podman-compose/model-simulation/asternos.sh +34 -0
- data/examples/podman-compose/oxidized-config/.gitignore +8 -0
- data/examples/podman-compose/oxidized-config/config +46 -0
- data/examples/podman-compose/oxidized-config/router.db +1 -0
- data/examples/podman-compose/oxidized-ssh/README.md +14 -0
- data/extra/rest_client.rb +2 -2
- data/extra/syslog.rb +2 -2
- data/lib/oxidized/cli.rb +6 -4
- data/lib/oxidized/config.rb +17 -14
- data/lib/oxidized/core.rb +22 -3
- data/lib/oxidized/hook.rb +3 -3
- data/lib/oxidized/input/exec.rb +1 -1
- data/lib/oxidized/input/ftp.rb +2 -2
- data/lib/oxidized/input/http.rb +32 -8
- data/lib/oxidized/input/input.rb +1 -1
- data/lib/oxidized/input/scp.rb +52 -0
- data/lib/oxidized/input/ssh.rb +10 -7
- data/lib/oxidized/input/telnet.rb +3 -2
- data/lib/oxidized/input/tftp.rb +1 -1
- data/lib/oxidized/jobs.rb +11 -1
- data/lib/oxidized/manager.rb +6 -6
- data/lib/oxidized/model/acos.rb +1 -1
- data/lib/oxidized/model/addpack.rb +26 -0
- data/lib/oxidized/model/adtran.rb +5 -1
- data/lib/oxidized/model/adva.rb +2 -2
- data/lib/oxidized/model/aoscx.rb +2 -1
- data/lib/oxidized/model/apc_aos.rb +2 -1
- data/lib/oxidized/model/aricentiss.rb +7 -0
- data/lib/oxidized/model/asternos.rb +22 -0
- data/lib/oxidized/model/asyncos.rb +2 -2
- data/lib/oxidized/model/awplus.rb +2 -2
- data/lib/oxidized/model/bdcom.rb +1 -0
- data/lib/oxidized/model/c4cmts.rb +1 -2
- data/lib/oxidized/model/ciscosma.rb +1 -1
- data/lib/oxidized/model/ciscosmb.rb +6 -1
- data/lib/oxidized/model/comware.rb +2 -2
- data/lib/oxidized/model/cumulus.rb +1 -1
- data/lib/oxidized/model/dellx.rb +1 -1
- data/lib/oxidized/model/dlink.rb +4 -2
- data/lib/oxidized/model/dlinknextgen.rb +51 -0
- data/lib/oxidized/model/dnos.rb +3 -0
- data/lib/oxidized/model/edgecos.rb +1 -1
- data/lib/oxidized/model/eltex.rb +2 -0
- data/lib/oxidized/model/enterasys800.rb +1 -1
- data/lib/oxidized/model/eos.rb +1 -1
- data/lib/oxidized/model/firebrick.rb +2 -2
- data/lib/oxidized/model/firewareos.rb +1 -1
- data/lib/oxidized/model/fortios.rb +9 -2
- data/lib/oxidized/model/fsos.rb +44 -0
- data/lib/oxidized/model/ios.rb +1 -1
- data/lib/oxidized/model/iosxr.rb +2 -2
- data/lib/oxidized/model/junos.rb +3 -2
- data/lib/oxidized/model/mimosab11.rb +34 -0
- data/lib/oxidized/model/ml66.rb +33 -0
- data/lib/oxidized/model/model.rb +3 -3
- data/lib/oxidized/model/netgear.rb +1 -1
- data/lib/oxidized/model/netscaler.rb +1 -1
- data/lib/oxidized/model/nxos.rb +4 -3
- data/lib/oxidized/model/ocnos.rb +42 -0
- data/lib/oxidized/model/onefinity.rb +18 -0
- data/lib/oxidized/model/openbsd.rb +1 -1
- data/lib/oxidized/model/opengear.rb +36 -1
- data/lib/oxidized/model/opnsense.rb +1 -1
- data/lib/oxidized/model/panos.rb +2 -0
- data/lib/oxidized/model/pfsense.rb +1 -0
- data/lib/oxidized/model/procurve.rb +3 -1
- data/lib/oxidized/model/rgos.rb +33 -0
- data/lib/oxidized/model/routeros.rb +10 -8
- data/lib/oxidized/model/slxos.rb +2 -2
- data/lib/oxidized/model/sonicos.rb +18 -17
- data/lib/oxidized/model/sros.rb +3 -3
- data/lib/oxidized/model/tplink.rb +4 -3
- data/lib/oxidized/model/truenas.rb +2 -1
- data/lib/oxidized/model/vrp.rb +3 -1
- data/lib/oxidized/model/vyatta.rb +6 -0
- data/lib/oxidized/model/zynos.rb +67 -3
- data/lib/oxidized/model/zynosadsl.rb +14 -0
- data/lib/oxidized/model/zynosgs.rb +2 -0
- data/lib/oxidized/model/zynosmgs.rb +32 -0
- data/lib/oxidized/node.rb +7 -7
- data/lib/oxidized/nodes.rb +17 -12
- data/lib/oxidized/output/file.rb +1 -1
- data/lib/oxidized/output/git.rb +5 -3
- data/lib/oxidized/output/gitcrypt.rb +4 -3
- data/lib/oxidized/signals.rb +44 -0
- data/lib/oxidized/source/csv.rb +1 -1
- data/lib/oxidized/source/http.rb +26 -5
- data/lib/oxidized/source/source.rb +2 -2
- data/lib/oxidized/source/sql.rb +3 -3
- data/lib/oxidized/version.rb +2 -2
- data/lib/oxidized/worker.rb +8 -1
- data/lib/oxidized.rb +3 -2
- data/lib/refinements.rb +1 -1
- data/oxidized.gemspec +6 -3
- metadata +77 -9
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
services:
|
|
2
|
+
oxidized:
|
|
3
|
+
# Choose the image that you want to test
|
|
4
|
+
# image: docker.io/oxidized/oxidized:0.29.1
|
|
5
|
+
image: docker.io/oxidized/oxidized:latest
|
|
6
|
+
ports:
|
|
7
|
+
- 127.0.0.1:8042:8888/tcp
|
|
8
|
+
environment:
|
|
9
|
+
# Reload hosts list once per day
|
|
10
|
+
CONFIG_RELOAD_INTERVAL: 86400
|
|
11
|
+
# Needed when you push to a remote git repository
|
|
12
|
+
OXIDIZED_SSH_PASSPHRASE: xxxxPassphasexxxx
|
|
13
|
+
volumes:
|
|
14
|
+
- ./oxidized-config:/home/oxidized/.config/oxidized
|
|
15
|
+
- ./oxidized-ssh:/home/oxidized/.ssh
|
|
16
|
+
# This is a simulated network device for the example to work out of the box
|
|
17
|
+
asternos-device:
|
|
18
|
+
image: localhost/local/model
|
|
19
|
+
volumes:
|
|
20
|
+
- ./model-simulation/asternos.sh:/home/oxidized/.profile
|
|
21
|
+
- ./model-simulation/asternos.sh:/home/admin/.profile
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
FROM docker.io/phusion/baseimage:jammy-1.0.2
|
|
2
|
+
|
|
3
|
+
# Use baseimage-docker's init system.
|
|
4
|
+
CMD ["/sbin/my_init"]
|
|
5
|
+
|
|
6
|
+
# enable ssh
|
|
7
|
+
RUN rm -f /etc/service/sshd/down
|
|
8
|
+
RUN /etc/my_init.d/00_regen_ssh_host_keys.sh
|
|
9
|
+
|
|
10
|
+
# Add users to login. The password is "oxidized"
|
|
11
|
+
RUN useradd -m oxidized -p '$y$j9T$UoDYxDiE.8iBGmoaD/acn1$kVvYvoEIJdKUmIKFVBRYKLIVzmEBP1RKrCM6Vfx.V55' -s /bin/bash
|
|
12
|
+
RUN useradd -m admin -p '$y$j9T$UoDYxDiE.8iBGmoaD/acn1$kVvYvoEIJdKUmIKFVBRYKLIVzmEBP1RKrCM6Vfx.V55' -s /bin/bash
|
|
13
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# if running bash
|
|
2
|
+
if [ -n "$BASH_VERSION" ]; then
|
|
3
|
+
# include .bashrc if it exists
|
|
4
|
+
if [ -f "$HOME/.bashrc" ]; then
|
|
5
|
+
. "$HOME/.bashrc"
|
|
6
|
+
fi
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
# Display a MOTD
|
|
10
|
+
cat << EOF
|
|
11
|
+
This is the welcome message of this device
|
|
12
|
+
it is muliline
|
|
13
|
+
End of the MOTD
|
|
14
|
+
EOF
|
|
15
|
+
|
|
16
|
+
function show() {
|
|
17
|
+
if [ "$*" == "version" ]; then
|
|
18
|
+
echo "Version 1.2.3"
|
|
19
|
+
elif [ "$*" == "runningconfiguration all" ]; then
|
|
20
|
+
cat << EOF
|
|
21
|
+
! begin of the configuration
|
|
22
|
+
! this is the running config
|
|
23
|
+
!
|
|
24
|
+
I have no idea how a configuration in asternos looks like ;-)
|
|
25
|
+
!
|
|
26
|
+
! End of the Configuration
|
|
27
|
+
EOF
|
|
28
|
+
else
|
|
29
|
+
echo "command 'show $*' not implemented"
|
|
30
|
+
fi
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
PS1="asternos$"
|
|
34
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
username: oxidized
|
|
3
|
+
password: oxidized
|
|
4
|
+
resolve_dns: true
|
|
5
|
+
interval: 3600
|
|
6
|
+
use_syslog: false
|
|
7
|
+
debug: false
|
|
8
|
+
threads: 30
|
|
9
|
+
use_max_threads: true
|
|
10
|
+
timeout: 20
|
|
11
|
+
retries: 3
|
|
12
|
+
prompt: !ruby/regexp /^([\w.@-]+[#>]\s?)$/
|
|
13
|
+
rest: 127.0.0.1:8888
|
|
14
|
+
next_adds_job: false
|
|
15
|
+
vars: {}
|
|
16
|
+
groups: {}
|
|
17
|
+
group_map: {}
|
|
18
|
+
models: {}
|
|
19
|
+
pid: "~/.config/oxidized/pid"
|
|
20
|
+
crash:
|
|
21
|
+
directory: "~/.config/oxidized/crashes"
|
|
22
|
+
hostnames: false
|
|
23
|
+
stats:
|
|
24
|
+
history_size: 10
|
|
25
|
+
input:
|
|
26
|
+
default: ssh
|
|
27
|
+
debug: false
|
|
28
|
+
ssh:
|
|
29
|
+
secure: false
|
|
30
|
+
ftp:
|
|
31
|
+
passive: true
|
|
32
|
+
utf8_encoded: true
|
|
33
|
+
output:
|
|
34
|
+
default: file
|
|
35
|
+
file:
|
|
36
|
+
directory: "~/.config/oxidized/configs/"
|
|
37
|
+
source:
|
|
38
|
+
default: csv
|
|
39
|
+
csv:
|
|
40
|
+
file: "~/.config/oxidized/router.db"
|
|
41
|
+
delimiter: !ruby/regexp /:/
|
|
42
|
+
map:
|
|
43
|
+
name: 0
|
|
44
|
+
model: 1
|
|
45
|
+
ip: 2
|
|
46
|
+
gpg: false
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
asternos-device:asternos
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
This is `~/.ssh/` of the user oxidized inside the oxidized container.
|
|
2
|
+
|
|
3
|
+
## What you need here for the hook githubrepo
|
|
4
|
+
You can store the SSH key needed to access a remote Git repository here. Here is
|
|
5
|
+
an example how to generate this key.
|
|
6
|
+
```shell
|
|
7
|
+
ssh-keygen -q -t ed25519 -C "Oxidized Push Key@`hostname`" -N "YOURPASSPHRASE" -m PEM -f oxidized-key
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
You also need to store the public keys of the remote git server in known_hosts. If you do not,
|
|
11
|
+
oxidized will refuse to push to the remote Git with the error `#<Rugged::SshError: invalid or unknown remote ssh hostkey>`, see Issue #2753.
|
|
12
|
+
```shell
|
|
13
|
+
ssh-keyscan git-server.example.com > known_hosts
|
|
14
|
+
```
|
data/extra/rest_client.rb
CHANGED
|
@@ -14,8 +14,8 @@ module Oxidized
|
|
|
14
14
|
|
|
15
15
|
begin
|
|
16
16
|
CFGS.load
|
|
17
|
-
rescue StandardError =>
|
|
18
|
-
raise InvalidConfig, "Error loading config: #{
|
|
17
|
+
rescue StandardError => e
|
|
18
|
+
raise InvalidConfig, "Error loading config: #{e.message}"
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
restcfg = CFGS.cfg.rest
|
data/extra/syslog.rb
CHANGED
|
@@ -43,8 +43,8 @@ module Oxidized
|
|
|
43
43
|
|
|
44
44
|
begin
|
|
45
45
|
CFGS.load
|
|
46
|
-
rescue StandardError =>
|
|
47
|
-
raise InvalidConfig, "Error loading config: #{
|
|
46
|
+
rescue StandardError => e
|
|
47
|
+
raise InvalidConfig, "Error loading config: #{e.message}"
|
|
48
48
|
ensure
|
|
49
49
|
CFG = CFGS.cfg # convenienence, instead of Config.cfg.password, CFG.password
|
|
50
50
|
end
|
data/lib/oxidized/cli.rb
CHANGED
|
@@ -11,8 +11,8 @@ module Oxidized
|
|
|
11
11
|
begin
|
|
12
12
|
Oxidized.logger.info "Oxidized starting, running as pid #{$PROCESS_ID}"
|
|
13
13
|
Oxidized.new
|
|
14
|
-
rescue StandardError =>
|
|
15
|
-
crash
|
|
14
|
+
rescue StandardError => e
|
|
15
|
+
crash e
|
|
16
16
|
raise
|
|
17
17
|
end
|
|
18
18
|
end
|
|
@@ -29,8 +29,8 @@ module Oxidized
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def crash(error)
|
|
32
|
-
Oxidized.logger.fatal "Oxidized crashed, crashfile written in #{Config::
|
|
33
|
-
File.open Config::
|
|
32
|
+
Oxidized.logger.fatal "Oxidized crashed, crashfile written in #{Config::CRASH}"
|
|
33
|
+
File.open Config::CRASH, 'w' do |file|
|
|
34
34
|
file.puts '-' * 50
|
|
35
35
|
file.puts Time.now.utc
|
|
36
36
|
file.puts error.message + ' [' + error.class.to_s + ']'
|
|
@@ -44,6 +44,8 @@ module Oxidized
|
|
|
44
44
|
opts = Slop.parse do |opt|
|
|
45
45
|
opt.on '-d', '--debug', 'turn on debugging'
|
|
46
46
|
opt.on '--daemonize', 'Daemonize/fork the process'
|
|
47
|
+
opt.string '--home-dir', 'Oxidized home dir', default: nil
|
|
48
|
+
opt.string '--config-file', 'Oxidized config file', default: nil
|
|
47
49
|
opt.on '-h', '--help', 'show usage' do
|
|
48
50
|
puts opt
|
|
49
51
|
exit
|
data/lib/oxidized/config.rb
CHANGED
|
@@ -4,18 +4,20 @@ module Oxidized
|
|
|
4
4
|
class InvalidConfig < OxidizedError; end
|
|
5
5
|
|
|
6
6
|
class Config
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
ROOT = ENV['OXIDIZED_HOME'] || File.join(Dir.home, '.config', 'oxidized')
|
|
8
|
+
CRASH = File.join(ENV['OXIDIZED_LOGS'] || ROOT, 'crash')
|
|
9
|
+
LOG = File.join(ENV['OXIDIZED_LOGS'] || ROOT, 'logs')
|
|
10
|
+
INPUT_DIR = File.join Directory, %w[lib oxidized input]
|
|
11
|
+
OUTPUT_DIR = File.join Directory, %w[lib oxidized output]
|
|
12
|
+
MODEL_DIR = File.join Directory, %w[lib oxidized model]
|
|
13
|
+
SOURCE_DIR = File.join Directory, %w[lib oxidized source]
|
|
14
|
+
HOOK_DIR = File.join Directory, %w[lib oxidized hook]
|
|
15
|
+
SLEEP = 1
|
|
16
16
|
|
|
17
17
|
def self.load(cmd_opts = {})
|
|
18
|
-
|
|
18
|
+
usrdir = File.expand_path(cmd_opts[:home_dir] || Oxidized::Config::ROOT)
|
|
19
|
+
cfgfile = cmd_opts[:config_file] || 'config'
|
|
20
|
+
asetus = Asetus.new(name: 'oxidized', load: false, key_to_s: true, usrdir: usrdir, cfgfile: cfgfile)
|
|
19
21
|
Oxidized.asetus = asetus
|
|
20
22
|
|
|
21
23
|
asetus.default.username = 'username'
|
|
@@ -25,6 +27,7 @@ module Oxidized
|
|
|
25
27
|
asetus.default.interval = 3600
|
|
26
28
|
asetus.default.use_syslog = false
|
|
27
29
|
asetus.default.debug = false
|
|
30
|
+
asetus.default.run_once = false
|
|
28
31
|
asetus.default.threads = 30
|
|
29
32
|
asetus.default.use_max_threads = false
|
|
30
33
|
asetus.default.timeout = 20
|
|
@@ -36,9 +39,9 @@ module Oxidized
|
|
|
36
39
|
asetus.default.groups = {} # group level configuration
|
|
37
40
|
asetus.default.group_map = {} # map aliases of groups to names
|
|
38
41
|
asetus.default.models = {} # model level configuration
|
|
39
|
-
asetus.default.pid = File.join(Oxidized::Config::
|
|
42
|
+
asetus.default.pid = File.join(Oxidized::Config::ROOT, 'pid')
|
|
40
43
|
|
|
41
|
-
asetus.default.crash.directory = File.join(Oxidized::Config::
|
|
44
|
+
asetus.default.crash.directory = File.join(Oxidized::Config::ROOT, 'crashes')
|
|
42
45
|
asetus.default.crash.hostnames = false
|
|
43
46
|
|
|
44
47
|
asetus.default.stats.history_size = 10
|
|
@@ -58,8 +61,8 @@ module Oxidized
|
|
|
58
61
|
|
|
59
62
|
begin
|
|
60
63
|
asetus.load # load system+user configs, merge to Config.cfg
|
|
61
|
-
rescue StandardError =>
|
|
62
|
-
raise InvalidConfig, "Error loading config: #{
|
|
64
|
+
rescue StandardError => e
|
|
65
|
+
raise InvalidConfig, "Error loading config: #{e.message}"
|
|
63
66
|
end
|
|
64
67
|
|
|
65
68
|
raise NoConfig, 'edit ~/.config/oxidized/config' if asetus.create
|
data/lib/oxidized/core.rb
CHANGED
|
@@ -12,10 +12,18 @@ module Oxidized
|
|
|
12
12
|
Oxidized.mgr = Manager.new
|
|
13
13
|
Oxidized.hooks = HookManager.from_config(Oxidized.config)
|
|
14
14
|
nodes = Nodes.new
|
|
15
|
-
raise NoNodesFound, 'source returns no usable nodes' if nodes.
|
|
15
|
+
raise NoNodesFound, 'source returns no usable nodes' if nodes.empty?
|
|
16
16
|
|
|
17
17
|
@worker = Worker.new nodes
|
|
18
|
-
|
|
18
|
+
@need_reload = false
|
|
19
|
+
|
|
20
|
+
# If we receive a SIGHUP, queue a reload of the state
|
|
21
|
+
reload_proc = proc do
|
|
22
|
+
@need_reload = true
|
|
23
|
+
end
|
|
24
|
+
Signals.register_signal('HUP', reload_proc)
|
|
25
|
+
|
|
26
|
+
# Initialize REST API and webUI if requested
|
|
19
27
|
if Oxidized.config.rest?
|
|
20
28
|
begin
|
|
21
29
|
require 'oxidized/web'
|
|
@@ -31,9 +39,20 @@ module Oxidized
|
|
|
31
39
|
|
|
32
40
|
private
|
|
33
41
|
|
|
42
|
+
def reload
|
|
43
|
+
Oxidized.logger.info("Reloading node list and log files")
|
|
44
|
+
@worker.reload
|
|
45
|
+
Oxidized.logger.reopen
|
|
46
|
+
@need_reload = false
|
|
47
|
+
end
|
|
48
|
+
|
|
34
49
|
def run
|
|
35
50
|
Oxidized.logger.debug "lib/oxidized/core.rb: Starting the worker..."
|
|
36
|
-
|
|
51
|
+
loop do
|
|
52
|
+
reload if @need_reload
|
|
53
|
+
@worker.work
|
|
54
|
+
sleep Config::SLEEP
|
|
55
|
+
end
|
|
37
56
|
end
|
|
38
57
|
end
|
|
39
58
|
end
|
data/lib/oxidized/hook.rb
CHANGED
|
@@ -19,7 +19,7 @@ module Oxidized
|
|
|
19
19
|
# RegisteredHook is a container for a Hook instance
|
|
20
20
|
RegisteredHook = Struct.new(:name, :hook)
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
EVENTS = %i[
|
|
23
23
|
node_success
|
|
24
24
|
node_fail
|
|
25
25
|
post_store
|
|
@@ -32,9 +32,9 @@ module Oxidized
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def register(event, name, hook_type, cfg)
|
|
35
|
-
unless
|
|
35
|
+
unless EVENTS.include? event
|
|
36
36
|
raise ArgumentError,
|
|
37
|
-
"unknown event #{event}, available: #{
|
|
37
|
+
"unknown event #{event}, available: #{EVENTS.join ','}"
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
Oxidized.mgr.add_hook(hook_type) || raise("cannot load hook '#{hook_type}', not found")
|
data/lib/oxidized/input/exec.rb
CHANGED
|
@@ -6,7 +6,7 @@ module Oxidized
|
|
|
6
6
|
|
|
7
7
|
def connect(node)
|
|
8
8
|
@node = node
|
|
9
|
-
@log = File.open(Oxidized::Config::
|
|
9
|
+
@log = File.open(Oxidized::Config::LOG + "/#{@node.ip}-exec", "w") if Oxidized.config.input.debug?
|
|
10
10
|
@node.model.cfg["exec"].each { |cb| instance_exec(&cb) }
|
|
11
11
|
end
|
|
12
12
|
|
data/lib/oxidized/input/ftp.rb
CHANGED
|
@@ -4,7 +4,7 @@ module Oxidized
|
|
|
4
4
|
require_relative 'cli'
|
|
5
5
|
|
|
6
6
|
class FTP < Input
|
|
7
|
-
|
|
7
|
+
RESCUE_FAIL = {
|
|
8
8
|
debug: [
|
|
9
9
|
# Net::SSH::Disconnect,
|
|
10
10
|
],
|
|
@@ -18,7 +18,7 @@ module Oxidized
|
|
|
18
18
|
def connect(node)
|
|
19
19
|
@node = node
|
|
20
20
|
@node.model.cfg['ftp'].each { |cb| instance_exec(&cb) }
|
|
21
|
-
@log = File.open(Oxidized::Config::
|
|
21
|
+
@log = File.open(Oxidized::Config::LOG + "/#{@node.ip}-ftp", 'w') if Oxidized.config.input.debug?
|
|
22
22
|
@ftp = Net::FTP.new(@node.ip)
|
|
23
23
|
@ftp.passive = Oxidized.config.input.ftp.passive
|
|
24
24
|
@ftp.login @node.auth[:username], @node.auth[:password]
|
data/lib/oxidized/input/http.rb
CHANGED
|
@@ -12,7 +12,7 @@ module Oxidized
|
|
|
12
12
|
@username = nil
|
|
13
13
|
@password = nil
|
|
14
14
|
@headers = {}
|
|
15
|
-
@log = File.open(Oxidized::Config::
|
|
15
|
+
@log = File.open(Oxidized::Config::LOG + "/#{@node.ip}-http", "w") if Oxidized.config.input.debug?
|
|
16
16
|
@node.model.cfg["http"].each { |cb| instance_exec(&cb) }
|
|
17
17
|
|
|
18
18
|
return true unless @main_page && defined?(login)
|
|
@@ -49,18 +49,42 @@ module Oxidized
|
|
|
49
49
|
def get_http(path)
|
|
50
50
|
schema = @secure ? "https://" : "http://"
|
|
51
51
|
uri = URI("#{schema}#{@node.ip}#{path}")
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
req.add_field(header, value)
|
|
56
|
-
end
|
|
52
|
+
|
|
53
|
+
Oxidized.logger.debug "Making request to: #{uri}"
|
|
54
|
+
|
|
57
55
|
ssl_verify = Oxidized.config.input.http.ssl_verify? ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
|
|
57
|
+
res = make_request(uri, ssl_verify)
|
|
58
|
+
|
|
59
|
+
if res.code == '401' && res['www-authenticate']&.include?('Digest')
|
|
60
|
+
uri.user = @username
|
|
61
|
+
uri.password = @password
|
|
62
|
+
Oxidized.logger.debug "Server requires Digest authentication"
|
|
63
|
+
auth = Net::HTTP::DigestAuth.new.auth_header(uri, res['www-authenticate'], 'GET')
|
|
64
|
+
|
|
65
|
+
res = make_request(uri, ssl_verify, 'Authorization' => auth)
|
|
66
|
+
elsif @username && @password
|
|
67
|
+
Oxidized.logger.debug "Falling back to Basic authentication"
|
|
68
|
+
res = make_request(uri, ssl_verify, 'Authorization' => basic_auth_header)
|
|
60
69
|
end
|
|
70
|
+
|
|
71
|
+
Oxidized.logger.debug "Response code: #{res.code}"
|
|
61
72
|
res.body
|
|
62
73
|
end
|
|
63
74
|
|
|
75
|
+
def make_request(uri, ssl_verify, extra_headers = {})
|
|
76
|
+
Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https", verify_mode: ssl_verify) do |http|
|
|
77
|
+
req = Net::HTTP::Get.new(uri)
|
|
78
|
+
@headers.merge(extra_headers).each { |header, value| req.add_field(header, value) }
|
|
79
|
+
Oxidized.logger.debug "Sending request with headers: #{@headers.merge(extra_headers)}"
|
|
80
|
+
http.request(req)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def basic_auth_header
|
|
85
|
+
"Basic " + ["#{@username}:#{@password}"].pack('m').delete("\r\n")
|
|
86
|
+
end
|
|
87
|
+
|
|
64
88
|
def log(str)
|
|
65
89
|
@log&.write(str)
|
|
66
90
|
end
|
data/lib/oxidized/input/input.rb
CHANGED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Oxidized
|
|
2
|
+
require 'net/ssh'
|
|
3
|
+
require 'net/scp'
|
|
4
|
+
require 'timeout'
|
|
5
|
+
require_relative 'cli'
|
|
6
|
+
|
|
7
|
+
class SCP < Input
|
|
8
|
+
RESCUE_FAIL = {
|
|
9
|
+
debug: [
|
|
10
|
+
# Net::SSH::Disconnect,
|
|
11
|
+
],
|
|
12
|
+
warn: [
|
|
13
|
+
# RuntimeError,
|
|
14
|
+
# Net::SSH::AuthenticationFailed,
|
|
15
|
+
]
|
|
16
|
+
}.freeze
|
|
17
|
+
include Input::CLI
|
|
18
|
+
|
|
19
|
+
def connect(node)
|
|
20
|
+
@node = node
|
|
21
|
+
@node.model.cfg['scp'].each { |cb| instance_exec(&cb) }
|
|
22
|
+
@log = File.open(Oxidized::Config::LOG + "/#{@node.ip}-scp", 'w') if Oxidized.config.input.debug?
|
|
23
|
+
@ssh = Net::SSH.start(@node.ip, @node.auth[:username], password: @node.auth[:password])
|
|
24
|
+
connected?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def connected?
|
|
28
|
+
@ssh && (not @ssh.closed?)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def cmd(file)
|
|
32
|
+
Oxidized.logger.debug "SCP: #{file} @ #{@node.name}"
|
|
33
|
+
@ssh.scp.download!(file)
|
|
34
|
+
end
|
|
35
|
+
|
|
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
|
+
@ssh.close
|
|
48
|
+
ensure
|
|
49
|
+
@log.close if Oxidized.config.input.debug?
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
data/lib/oxidized/input/ssh.rb
CHANGED
|
@@ -4,7 +4,7 @@ module Oxidized
|
|
|
4
4
|
require 'timeout'
|
|
5
5
|
require 'oxidized/input/cli'
|
|
6
6
|
class SSH < Input
|
|
7
|
-
|
|
7
|
+
RESCUE_FAIL = {
|
|
8
8
|
debug: [
|
|
9
9
|
Net::SSH::Disconnect
|
|
10
10
|
],
|
|
@@ -21,7 +21,7 @@ module Oxidized
|
|
|
21
21
|
@output = ''
|
|
22
22
|
@pty_options = { term: "vt100" }
|
|
23
23
|
@node.model.cfg['ssh'].each { |cb| instance_exec(&cb) }
|
|
24
|
-
@log = File.open(Oxidized::Config::
|
|
24
|
+
@log = File.open(Oxidized::Config::LOG + "/#{@node.ip}-ssh", 'w') if Oxidized.config.input.debug?
|
|
25
25
|
|
|
26
26
|
Oxidized.logger.debug "lib/oxidized/input/ssh.rb: Connecting to #{@node.name}"
|
|
27
27
|
@ssh = Net::SSH.start(@node.ip, @node.auth[:username], make_ssh_opts)
|
|
@@ -42,11 +42,13 @@ module Oxidized
|
|
|
42
42
|
|
|
43
43
|
def cmd(cmd, expect = node.prompt)
|
|
44
44
|
Oxidized.logger.debug "lib/oxidized/input/ssh.rb #{cmd} @ #{node.name} with expect: #{expect.inspect}"
|
|
45
|
-
if @exec
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
cmd_output = if @exec
|
|
46
|
+
@ssh.exec! cmd
|
|
47
|
+
else
|
|
48
|
+
cmd_shell(cmd, expect).gsub("\r\n", "\n")
|
|
49
|
+
end
|
|
50
|
+
# Make sure we return a String
|
|
51
|
+
cmd_output.to_s
|
|
50
52
|
end
|
|
51
53
|
|
|
52
54
|
def send(data)
|
|
@@ -66,6 +68,7 @@ module Oxidized
|
|
|
66
68
|
# if disconnect does not disconnect us, give up after timeout
|
|
67
69
|
Timeout.timeout(Oxidized.config.timeout) { @ssh.loop }
|
|
68
70
|
rescue Errno::ECONNRESET, Net::SSH::Disconnect, IOError
|
|
71
|
+
# These exceptions are intented and therefore not handled here
|
|
69
72
|
ensure
|
|
70
73
|
@log.close if Oxidized.config.input.debug?
|
|
71
74
|
(@ssh.close rescue true) unless @ssh.closed?
|
|
@@ -2,7 +2,7 @@ module Oxidized
|
|
|
2
2
|
require 'net/telnet'
|
|
3
3
|
require 'oxidized/input/cli'
|
|
4
4
|
class Telnet < Input
|
|
5
|
-
|
|
5
|
+
RESCUE_FAIL = {}.freeze
|
|
6
6
|
include Input::CLI
|
|
7
7
|
attr_reader :telnet
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ module Oxidized
|
|
|
10
10
|
@node = node
|
|
11
11
|
@timeout = Oxidized.config.timeout
|
|
12
12
|
@node.model.cfg['telnet'].each { |cb| instance_exec(&cb) }
|
|
13
|
-
@log = File.open(Oxidized::Config::
|
|
13
|
+
@log = File.open(Oxidized::Config::LOG + "/#{@node.ip}-telnet", 'w') if Oxidized.config.input.debug?
|
|
14
14
|
port = vars(:telnet_port) || 23
|
|
15
15
|
|
|
16
16
|
telnet_opts = {
|
|
@@ -64,6 +64,7 @@ module Oxidized
|
|
|
64
64
|
disconnect_cli
|
|
65
65
|
@telnet.close
|
|
66
66
|
rescue Errno::ECONNRESET
|
|
67
|
+
# This exception is intented and therefore not handled here
|
|
67
68
|
ensure
|
|
68
69
|
@log.close if Oxidized.config.input.debug?
|
|
69
70
|
(@telnet.close rescue true) unless @telnet.sock.closed?
|
data/lib/oxidized/input/tftp.rb
CHANGED
|
@@ -16,7 +16,7 @@ module Oxidized
|
|
|
16
16
|
@node = node
|
|
17
17
|
|
|
18
18
|
@node.model.cfg['tftp'].each { |cb| instance_exec(&cb) }
|
|
19
|
-
@log = File.open(Oxidized::Config::
|
|
19
|
+
@log = File.open(Oxidized::Config::LOG + "/#{@node.ip}-tftp", 'w') if Oxidized.config.input.debug?
|
|
20
20
|
@tftp = Net::TFTP.new @node.ip
|
|
21
21
|
end
|
|
22
22
|
|
data/lib/oxidized/jobs.rb
CHANGED
|
@@ -44,6 +44,14 @@ module Oxidized
|
|
|
44
44
|
@want = @max if @want > @max
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
def increment
|
|
48
|
+
# Increments the job count if safe to do so, which means:
|
|
49
|
+
# a) less threads running than the total amount of nodes
|
|
50
|
+
# b) we want less than the max specified number of threads
|
|
51
|
+
|
|
52
|
+
@want = [(@want + 1), @nodes.size, @max].min
|
|
53
|
+
end
|
|
54
|
+
|
|
47
55
|
def work
|
|
48
56
|
# if a) we want less or same amount of threads as we now running
|
|
49
57
|
# and b) we want less threads running than the total amount of nodes
|
|
@@ -51,7 +59,9 @@ module Oxidized
|
|
|
51
59
|
# then we want one more thread (rationale is to fix hanging thread causing HOLB)
|
|
52
60
|
return unless @want <= size && @want < @nodes.size
|
|
53
61
|
|
|
54
|
-
|
|
62
|
+
return unless @want <= size
|
|
63
|
+
|
|
64
|
+
increment if (Time.now.utc - @last) > MAX_INTER_JOB_GAP
|
|
55
65
|
end
|
|
56
66
|
end
|
|
57
67
|
end
|
data/lib/oxidized/manager.rb
CHANGED
|
@@ -33,30 +33,30 @@ module Oxidized
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def add_input(name)
|
|
36
|
-
loader @input, Config::
|
|
36
|
+
loader @input, Config::INPUT_DIR, "input", name
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def add_output(name)
|
|
40
|
-
loader @output, Config::
|
|
40
|
+
loader @output, Config::OUTPUT_DIR, "output", name
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def add_source(name)
|
|
44
|
-
loader @source, Config::
|
|
44
|
+
loader @source, Config::SOURCE_DIR, "source", name
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
def add_model(name)
|
|
48
|
-
loader @model, Config::
|
|
48
|
+
loader @model, Config::MODEL_DIR, "model", name
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def add_hook(name)
|
|
52
|
-
loader @hook, Config::
|
|
52
|
+
loader @hook, Config::HOOK_DIR, "hook", name
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
private
|
|
56
56
|
|
|
57
57
|
# if local version of file exists, load it, else load global - return falsy value if nothing loaded
|
|
58
58
|
def loader(hash, global_dir, local_dir, name)
|
|
59
|
-
dir = File.join(Config::
|
|
59
|
+
dir = File.join(Config::ROOT, local_dir)
|
|
60
60
|
map = Manager.load(dir, name) if File.exist? File.join(dir, name + ".rb")
|
|
61
61
|
map ||= Manager.load(global_dir, name)
|
|
62
62
|
hash.merge!(map) if map
|
data/lib/oxidized/model/acos.rb
CHANGED
|
@@ -72,7 +72,7 @@ class ACOS < Oxidized::Model
|
|
|
72
72
|
out << "! partition: #{partition}"
|
|
73
73
|
arules.each do |name|
|
|
74
74
|
cmd("show aflex #{name} partition #{partition}") do |cfg|
|
|
75
|
-
content = cfg.split(
|
|
75
|
+
content = cfg.split("Content:").last.strip
|
|
76
76
|
out << "aflex create #{name}"
|
|
77
77
|
out << content
|
|
78
78
|
out << ".\n"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
class AddPack < Oxidized::Model
|
|
2
|
+
# Used in AddPack Voip, such as AP100B, AP100_G2, AP700, AP1000, AP1100F
|
|
3
|
+
|
|
4
|
+
using Refinements
|
|
5
|
+
PROMPT = /^.*[>#]\s?$/
|
|
6
|
+
|
|
7
|
+
expect /-- [Mm]ore --/ do |data, re|
|
|
8
|
+
send ' '
|
|
9
|
+
data.sub re, ''
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
prompt PROMPT
|
|
13
|
+
cmd 'enable'
|
|
14
|
+
|
|
15
|
+
cmd 'show running-config' do |cfg|
|
|
16
|
+
cfg.gsub! /^Building configuration.../, ''
|
|
17
|
+
cfg.gsub! /^*show running-config/, ''
|
|
18
|
+
cfg.gsub! PROMPT, ''
|
|
19
|
+
cfg
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
cfg :telnet do
|
|
23
|
+
username /[Ll]ogin:\s?/
|
|
24
|
+
password /[Pp]assword:\s?/
|
|
25
|
+
end
|
|
26
|
+
end
|