oxidized 0.29.1 → 0.37.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/.coderabbit.yaml +21 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +45 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
- data/.github/ISSUE_TEMPLATE/support-request.md +39 -0
- data/.github/workflows/codeql.yml +4 -4
- data/.github/workflows/publishdocker.yml +108 -13
- data/.github/workflows/ruby.yml +3 -12
- data/.github/workflows/stale.yml +6 -1
- data/.gitignore +2 -0
- data/.rubocop.yml +69 -13
- data/.rubocop_todo.yml +13 -143
- data/CHANGELOG.md +431 -4
- data/CONTRIBUTING.md +189 -0
- data/Dockerfile +89 -34
- data/README.md +59 -188
- data/Rakefile +58 -12
- data/bin/oxidized +2 -5
- data/docs/Configuration.md +382 -149
- data/docs/Creating-Models.md +262 -10
- data/docs/DeviceSimulation.md +196 -0
- data/docs/Docker.md +246 -0
- data/docs/Hooks.md +146 -51
- data/docs/Inputs.md +237 -0
- data/docs/Issues.md +123 -0
- data/docs/Model-Notes/APC.md +72 -0
- data/docs/Model-Notes/Cumulus.md +5 -0
- data/docs/Model-Notes/EatonNetwork.md +18 -0
- data/docs/Model-Notes/ExaLink.md +43 -0
- data/docs/Model-Notes/FSOS.md +17 -0
- data/docs/Model-Notes/Fortinet.md +75 -0
- data/docs/Model-Notes/GrandstreamHT8xx.md +8 -0
- data/docs/Model-Notes/HPEAruba.md +32 -0
- data/docs/Model-Notes/IvantiConnectSecure.md +59 -0
- data/docs/Model-Notes/OS6.md +10 -0
- data/docs/Model-Notes/README.md +1 -20
- data/docs/Model-Notes/RouterOS.md +28 -0
- data/docs/Model-Notes/SikluMHTG.md +7 -0
- data/docs/Model-Notes/TrueNAS.md +23 -0
- data/docs/Model-Notes/XGS4600-Zyxel.md +5 -0
- data/docs/ModelUnitTests.md +219 -0
- data/docs/Outputs.md +102 -5
- data/docs/Release.md +102 -0
- data/docs/Ruby-API.md +135 -2
- data/docs/Sources.md +34 -0
- data/docs/Supported-OS-Types.md +207 -270
- data/docs/Troubleshooting.md +43 -1
- data/extra/device2yaml.rb +259 -0
- data/extra/gitdiff-msteams.sh +32 -5
- data/extra/hooks/modelrules.rb +55 -0
- data/extra/hooks/modelrulesadvanced.rb +167 -0
- data/extra/hooks/srcipmap.rb +54 -0
- data/extra/nagios_check_failing_nodes.rb +1 -1
- data/extra/oxidized.runit +1 -1
- data/extra/rest_client.rb +5 -4
- data/extra/syslog.rb +11 -6
- data/lib/oxidized/cli/support.rb +152 -0
- data/lib/oxidized/cli.rb +21 -6
- data/lib/oxidized/config/vars.rb +33 -14
- data/lib/oxidized/config.rb +28 -17
- data/lib/oxidized/core.rb +50 -8
- data/lib/oxidized/hook/ciscosparkdiff.rb +11 -9
- data/lib/oxidized/hook/exec.rb +5 -4
- data/lib/oxidized/hook/githubrepo.rb +55 -18
- data/lib/oxidized/hook/noophook.rb +2 -2
- data/lib/oxidized/hook/slackdiff.rb +50 -14
- data/lib/oxidized/hook/xmppdiff.rb +9 -9
- data/lib/oxidized/hook.rb +68 -16
- data/lib/oxidized/input/cli.rb +8 -3
- data/lib/oxidized/input/debugtext.rb +40 -0
- data/lib/oxidized/input/debugyaml.rb +82 -0
- data/lib/oxidized/input/exec.rb +2 -11
- data/lib/oxidized/input/ftp.rb +2 -19
- data/lib/oxidized/input/http.rb +67 -17
- data/lib/oxidized/input/input.rb +35 -14
- data/lib/oxidized/input/scp.rb +27 -0
- data/lib/oxidized/input/ssh.rb +59 -83
- data/lib/oxidized/input/sshbase.rb +102 -0
- data/lib/oxidized/input/telnet.rb +20 -19
- data/lib/oxidized/input/tftp.rb +8 -8
- data/lib/oxidized/job.rb +18 -7
- data/lib/oxidized/jobs.rb +11 -1
- data/lib/oxidized/logger.rb +51 -0
- data/lib/oxidized/manager.rb +18 -17
- data/lib/oxidized/model/acos.rb +2 -1
- data/lib/oxidized/model/addpack.rb +26 -0
- data/lib/oxidized/model/adtran.rb +5 -1
- data/lib/oxidized/model/adva.rb +3 -3
- data/lib/oxidized/model/airfiber.rb +1 -1
- data/lib/oxidized/model/aos7.rb +11 -0
- data/lib/oxidized/model/aoscx.rb +37 -29
- data/lib/oxidized/model/aosw.rb +39 -29
- data/lib/oxidized/model/apc_aos.rb +7 -2
- data/lib/oxidized/model/apcaos.rb +39 -0
- data/lib/oxidized/model/aricentiss.rb +9 -2
- data/lib/oxidized/model/arubainstant.rb +93 -0
- data/lib/oxidized/model/asa.rb +11 -10
- data/lib/oxidized/model/asternos.rb +22 -0
- data/lib/oxidized/model/asyncos.rb +3 -3
- data/lib/oxidized/model/audiocodes.rb +2 -2
- data/lib/oxidized/model/awplus.rb +15 -12
- data/lib/oxidized/model/axos.rb +1 -1
- 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/cnos.rb +13 -10
- data/lib/oxidized/model/comtrol.rb +1 -1
- data/lib/oxidized/model/comware.rb +5 -3
- data/lib/oxidized/model/cumulus.rb +14 -2
- data/lib/oxidized/model/defacto.rb +26 -0
- data/lib/oxidized/model/dellx.rb +1 -1
- data/lib/oxidized/model/dlink.rb +5 -2
- data/lib/oxidized/model/dlinknextgen.rb +55 -0
- data/lib/oxidized/model/dnos.rb +3 -0
- data/lib/oxidized/model/dslcommands.rb +93 -0
- data/lib/oxidized/model/dslsetup.rb +102 -0
- data/lib/oxidized/model/eatonnetwork.rb +70 -0
- data/lib/oxidized/model/edgecos.rb +3 -1
- data/lib/oxidized/model/edgeos.rb +7 -6
- data/lib/oxidized/model/edgeswitch.rb +3 -1
- data/lib/oxidized/model/efos.rb +41 -0
- data/lib/oxidized/model/eltex.rb +4 -2
- data/lib/oxidized/model/enterasys800.rb +2 -2
- data/lib/oxidized/model/enterprise_sonic.rb +46 -0
- data/lib/oxidized/model/eos.rb +3 -1
- data/lib/oxidized/model/exalink.rb +36 -0
- data/lib/oxidized/model/f5os.rb +17 -0
- data/lib/oxidized/model/fabricos.rb +2 -2
- data/lib/oxidized/model/fastiron.rb +5 -3
- data/lib/oxidized/model/firebrick.rb +2 -2
- data/lib/oxidized/model/firelinuxos.rb +13 -6
- data/lib/oxidized/model/firewareos.rb +10 -1
- data/lib/oxidized/model/fortigate.rb +160 -0
- data/lib/oxidized/model/fortios.rb +38 -48
- data/lib/oxidized/model/fsos.rb +46 -0
- data/lib/oxidized/model/gaiaos.rb +4 -4
- data/lib/oxidized/model/garderos.rb +38 -0
- data/lib/oxidized/model/grandstreamht8xx.rb +19 -0
- data/lib/oxidized/model/h3c.rb +1 -1
- data/lib/oxidized/model/ibos.rb +1 -0
- data/lib/oxidized/model/ingate.rb +47 -0
- data/lib/oxidized/model/ios.rb +57 -30
- data/lib/oxidized/model/iosxr.rb +2 -2
- data/lib/oxidized/model/ironware.rb +7 -5
- data/lib/oxidized/model/ivanti.rb +54 -0
- data/lib/oxidized/model/junos.rb +12 -5
- data/lib/oxidized/model/kornfeldos.rb +33 -0
- data/lib/oxidized/model/lenovonos.rb +2 -0
- data/lib/oxidized/model/linksyssrw.rb +3 -3
- data/lib/oxidized/model/linuxgeneric.rb +4 -2
- data/lib/oxidized/model/macros.rb +60 -0
- data/lib/oxidized/model/mimosab11.rb +34 -0
- data/lib/oxidized/model/ml66.rb +33 -0
- data/lib/oxidized/model/mlnxos.rb +18 -14
- data/lib/oxidized/model/model.rb +98 -103
- data/lib/oxidized/model/mtrlrfs.rb +1 -0
- data/lib/oxidized/model/ndms.rb +6 -0
- data/lib/oxidized/model/netgear.rb +14 -3
- data/lib/oxidized/model/netonix.rb +1 -1
- data/lib/oxidized/model/netscaler.rb +1 -1
- data/lib/oxidized/model/nodegrid.rb +1 -1
- data/lib/oxidized/model/nsxdfw.rb +31 -0
- data/lib/oxidized/model/nsxfirewall.rb +2 -1
- data/lib/oxidized/model/nxos.rb +20 -15
- 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/openwrt.rb +6 -6
- data/lib/oxidized/model/opnsense.rb +9 -1
- data/lib/oxidized/model/os6.rb +48 -0
- data/lib/oxidized/model/outputs.rb +5 -0
- data/lib/oxidized/model/panos.rb +2 -0
- data/lib/oxidized/model/perle.rb +41 -0
- data/lib/oxidized/model/pfsense.rb +9 -0
- data/lib/oxidized/model/powerconnect.rb +32 -11
- data/lib/oxidized/model/procurve.rb +6 -2
- data/lib/oxidized/model/qtech.rb +3 -1
- data/lib/oxidized/model/quantaos.rb +8 -6
- data/lib/oxidized/model/rgos.rb +33 -0
- data/lib/oxidized/model/riverbed.rb +104 -0
- data/lib/oxidized/model/routeros.rb +18 -11
- data/lib/oxidized/model/saos.rb +18 -1
- data/lib/oxidized/model/saos10.rb +38 -0
- data/lib/oxidized/model/siklu.rb +1 -1
- data/lib/oxidized/model/siklumhtg.rb +22 -0
- data/lib/oxidized/model/sixwind.rb +28 -0
- data/lib/oxidized/model/slxos.rb +3 -3
- data/lib/oxidized/model/smartbyte.rb +48 -0
- data/lib/oxidized/model/sonicos.rb +27 -20
- data/lib/oxidized/model/speedtouch.rb +1 -1
- data/lib/oxidized/model/sros.rb +3 -3
- data/lib/oxidized/model/srosmd.rb +1 -1
- data/lib/oxidized/model/supermicro.rb +1 -1
- data/lib/oxidized/model/telco.rb +1 -1
- data/lib/oxidized/model/tmos.rb +1 -0
- data/lib/oxidized/model/tnsr.rb +57 -0
- data/lib/oxidized/model/tplink.rb +5 -5
- data/lib/oxidized/model/trango.rb +4 -2
- data/lib/oxidized/model/truenas.rb +64 -3
- data/lib/oxidized/model/ucs.rb +1 -1
- data/lib/oxidized/model/unifiap.rb +144 -0
- data/lib/oxidized/model/uplinkolt.rb +46 -0
- data/lib/oxidized/model/voltaire.rb +1 -1
- data/lib/oxidized/model/voss.rb +3 -0
- data/lib/oxidized/model/vrp.rb +5 -1
- data/lib/oxidized/model/vyatta.rb +9 -3
- data/lib/oxidized/model/vyos.rb +37 -0
- data/lib/oxidized/model/xos.rb +10 -1
- data/lib/oxidized/model/zhoneolt.rb +4 -2
- data/lib/oxidized/model/zynos.rb +67 -3
- data/lib/oxidized/model/zynosadsl.rb +14 -0
- data/lib/oxidized/model/zynoscli.rb +1 -1
- data/lib/oxidized/model/zynosgs.rb +2 -0
- data/lib/oxidized/model/zynosmgs.rb +32 -0
- data/lib/oxidized/node.rb +99 -64
- data/lib/oxidized/nodes.rb +40 -19
- data/lib/oxidized/output/file.rb +80 -43
- data/lib/oxidized/output/git.rb +295 -151
- data/lib/oxidized/output/gitcrypt.rb +193 -185
- data/lib/oxidized/output/http.rb +63 -51
- data/lib/oxidized/output/output.rb +20 -4
- data/lib/oxidized/signals.rb +44 -0
- data/lib/oxidized/source/csv.rb +49 -49
- data/lib/oxidized/source/http.rb +76 -63
- data/lib/oxidized/source/jsonfile.rb +68 -0
- data/lib/oxidized/source/source.rb +73 -18
- data/lib/oxidized/source/sql.rb +71 -59
- data/lib/oxidized/version.rb +6 -4
- data/lib/oxidized/worker.rb +57 -29
- data/lib/oxidized.rb +4 -24
- data/lib/refinements.rb +21 -1
- data/oxidized.gemspec +45 -21
- metadata +248 -41
- data/lib/oxidized/model/timos.rb +0 -10
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
class SmartByte < Oxidized::Model
|
|
2
|
+
using Refinements
|
|
3
|
+
|
|
4
|
+
comment '! '
|
|
5
|
+
|
|
6
|
+
cmd :secret do |cfg|
|
|
7
|
+
cfg.gsub!(/group (\S+) v2c/, 'group <hidden> v2c')
|
|
8
|
+
cfg.gsub!(/community (\S+)/, 'community <hidden>')
|
|
9
|
+
cfg
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
cmd :all do |cfg|
|
|
13
|
+
cfg = cfg.delete("\r")
|
|
14
|
+
cfg.cut_tail
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
cmd 'show running-config'
|
|
18
|
+
|
|
19
|
+
cmd 'show version' do |cfg|
|
|
20
|
+
comment cfg
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
cmd 'show interface optical-transceiver info' do |cfg|
|
|
24
|
+
cfg.gsub! /^\|Transceiver current alarm information: \|\s+\+-------------------------------------------------\+.*?\s+\+-------------------------------------------------\+\s+/m, ''
|
|
25
|
+
comment cfg
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
cmd 'show power' do |cfg|
|
|
29
|
+
comment cfg
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
cfg :telnet do
|
|
33
|
+
username /^.*? login:/
|
|
34
|
+
password /^Password:/
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
cfg :telnet, :ssh do
|
|
38
|
+
post_login do
|
|
39
|
+
if vars(:enable)
|
|
40
|
+
cmd "enable", /^[pP]assword:/
|
|
41
|
+
cmd vars(:enable)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
post_login 'terminal length 0'
|
|
46
|
+
pre_logout 'exit'
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -3,28 +3,35 @@ class SonicOS < Oxidized::Model
|
|
|
3
3
|
|
|
4
4
|
# Applies to Sonicwall NSA series firewalls
|
|
5
5
|
|
|
6
|
-
prompt /^\w
|
|
7
|
-
comment
|
|
6
|
+
prompt /^\w+@[\w-]+>\(?.+\)?\s?/
|
|
7
|
+
comment '! '
|
|
8
|
+
|
|
9
|
+
# Accept policiy message (see Issue #3339). Tested on 6.5 and 7.1
|
|
10
|
+
expect /Accept The Policy Banner \(yes\)\?\r\nyes: $/ do |data, re|
|
|
11
|
+
send "yes\n"
|
|
12
|
+
data.sub re, ''
|
|
13
|
+
end
|
|
8
14
|
|
|
9
15
|
cmd :all do |cfg|
|
|
10
16
|
cfg.each_line.to_a[1..-2].join
|
|
11
17
|
end
|
|
12
18
|
|
|
13
19
|
cmd :secret do |cfg|
|
|
14
|
-
cfg.gsub! /cli ftp password default \d
|
|
15
|
-
cfg.gsub! /secret \d
|
|
16
|
-
cfg.gsub! /shared-secret \d
|
|
17
|
-
cfg.gsub! /password \d
|
|
18
|
-
cfg.gsub! /passphrase password \d
|
|
19
|
-
cfg.gsub! /bind-password \d
|
|
20
|
-
cfg.gsub! /authentication sha1 \d
|
|
21
|
-
cfg.gsub! /encryption aes \d
|
|
22
|
-
cfg.gsub! /smtp-pass \d
|
|
23
|
-
cfg.gsub! /pop-pass \d
|
|
24
|
-
cfg.gsub! /sslvpn password \d
|
|
25
|
-
cfg.gsub! /administrator password \d
|
|
26
|
-
cfg.gsub! /ftp password \d
|
|
27
|
-
cfg.gsub! /shared-key \d
|
|
20
|
+
cfg.gsub! /cli ftp password default \d,(\S+)/, 'cli ftp password default <secret hidden> \2'
|
|
21
|
+
cfg.gsub! /secret \d,(\S+)/, 'secret <secret hidden> \2'
|
|
22
|
+
cfg.gsub! /shared-secret \d,(\S+)/, 'shared-secret <secret hidden> \2'
|
|
23
|
+
cfg.gsub! /password \d,(\S+)/, 'password <secret hidden> \2'
|
|
24
|
+
cfg.gsub! /passphrase password \d,(\S+)/, 'passphrase password <secret hidden> \2'
|
|
25
|
+
cfg.gsub! /bind-password \d,(\S+)/, 'bind-password <secret hidden> \2'
|
|
26
|
+
cfg.gsub! /authentication sha1 \d,(\S+)/, 'authentication sha1 <secret hidden> \2'
|
|
27
|
+
cfg.gsub! /encryption aes \d,(\S+)/, 'encryption aes <secret hidden> \2'
|
|
28
|
+
cfg.gsub! /smtp-pass \d,(\S+)/, 'smtp-pass <secret hidden> \2'
|
|
29
|
+
cfg.gsub! /pop-pass \d,(\S+)/, 'pop-pass <secret hidden> \2'
|
|
30
|
+
cfg.gsub! /sslvpn password \d,(\S+)/, 'sslvpn password <secret hidden> \2'
|
|
31
|
+
cfg.gsub! /administrator password \d,(\S+)/, 'administrator password <secret hidden> \2'
|
|
32
|
+
cfg.gsub! /ftp password \d,(\S+)/, 'ftp password <secret hidden> \2'
|
|
33
|
+
cfg.gsub! /shared-key \d,(\S+)/, 'shared-key <secret hidden> \2'
|
|
34
|
+
cfg.gsub! /wpa passphrase \d,(\S+)/, 'wpa passphrase <secret hidden> \2'
|
|
28
35
|
cfg
|
|
29
36
|
end
|
|
30
37
|
|
|
@@ -46,10 +53,10 @@ class SonicOS < Oxidized::Model
|
|
|
46
53
|
def clean(cfg)
|
|
47
54
|
out = []
|
|
48
55
|
cfg.each_line do |line|
|
|
49
|
-
next if line =~ /date \d{4}
|
|
50
|
-
next if line =~ /time \d{2}
|
|
51
|
-
next if line =~ /system-time
|
|
52
|
-
next if line =~ /system-uptime "(
|
|
56
|
+
next if line =~ /date \d{4}:\d{2}:\d{2}/
|
|
57
|
+
next if line =~ /time \d{2}:\d{2}:\d{2}/
|
|
58
|
+
next if line =~ /system-time "\d{2}\/\d{2}\/\d{4} \d{2}:\d{2}:\d{2}.\d+"/
|
|
59
|
+
next if line =~ /system-uptime "(?:\s+up\s+\d+\s+|\d+ \w+(?:, \d+ \w+)*)"/
|
|
53
60
|
next if line =~ /checksum \d+/
|
|
54
61
|
|
|
55
62
|
line = line[1..-1] if line[0] == "\r"
|
data/lib/oxidized/model/sros.rb
CHANGED
|
@@ -32,7 +32,7 @@ class SROS < Oxidized::Model
|
|
|
32
32
|
#
|
|
33
33
|
# Strip uptime.
|
|
34
34
|
#
|
|
35
|
-
cfg.
|
|
35
|
+
cfg.gsub! /^System Up Time.*$/, ''
|
|
36
36
|
comment cfg
|
|
37
37
|
end
|
|
38
38
|
|
|
@@ -75,14 +75,14 @@ class SROS < Oxidized::Model
|
|
|
75
75
|
#
|
|
76
76
|
# Show the running persistent indices.
|
|
77
77
|
#
|
|
78
|
-
cmd
|
|
78
|
+
cmd "admin display-config index\n" do |cfg|
|
|
79
79
|
comment cfg
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
#
|
|
83
83
|
# Show the running configuration.
|
|
84
84
|
#
|
|
85
|
-
cmd
|
|
85
|
+
cmd "admin display-config\n" do |cfg|
|
|
86
86
|
cfg
|
|
87
87
|
end
|
|
88
88
|
|
data/lib/oxidized/model/telco.rb
CHANGED
data/lib/oxidized/model/tmos.rb
CHANGED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
class TNSR < Oxidized::Model
|
|
2
|
+
using Refinements
|
|
3
|
+
|
|
4
|
+
# Netgate TNSR #
|
|
5
|
+
|
|
6
|
+
# prompt examples
|
|
7
|
+
# https://docs.netgate.com/tnsr/en/latest/basics/working-cli.html#command-prompt
|
|
8
|
+
# "<hostname> tnsr<(mode)># " <-- with trailing whitespace
|
|
9
|
+
# tnsr-dev-25-02 tnsr#
|
|
10
|
+
# tnsr-dev-25-02 tnsr(config)# <-- after "configure terminal", but this is not important for oxidized
|
|
11
|
+
# prompt from debug log
|
|
12
|
+
# received "Welcome to Netgate TNSR Version: 25.02-2\r\n\r\n\t* Documentation: https://docs.netgate.com/tnsr/en/latest/\r\n\t* Support: https://www.netgate.com/support\r\n\r\n _ __________\r\n _| |_ _ __ | ___ _ __ |\r\n|_ __| '_ \\ |/ __| '__||\r\n | |_| | | ||\\__ \\ | |\r\n \\__|_| |_|||___/_| |\r\n |__________|\r\n\r\n\r\n"
|
|
13
|
+
# received "\r\nVersion: 25.02-2\r\n\r\nFor information see 'show documentation'\r\n\r\n"
|
|
14
|
+
# received "\rbgp01 tnsr# "
|
|
15
|
+
# when the --More-- pager is the last line of paged output, the prompt contains \x08\x20\x08 orphans on the begin
|
|
16
|
+
# on older version (23.06) --More-- pager is sometimes misplaced
|
|
17
|
+
# you can look for context into spec simulation:
|
|
18
|
+
# spec/model/data/tnsr#TNSR_24.10-3_short-config#simulation.yaml
|
|
19
|
+
# spec/model/data/tnsr#TNSR_25.02-2_long-config-and-pager-at-last-line#simulation.yaml
|
|
20
|
+
# spec/model/data/tnsr#TNSR_23.06-3_with-misplaced-pager#simulation.yaml
|
|
21
|
+
prompt /^((\x08{8}\x20{8}\x08{8})?\r?[\w-]+\stnsr#\s?)$/
|
|
22
|
+
|
|
23
|
+
comment '! '
|
|
24
|
+
|
|
25
|
+
expect /^--More--|--More--$/ do |data, re|
|
|
26
|
+
send ' '
|
|
27
|
+
data.sub re, ''
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
cmd :all do |cfg|
|
|
31
|
+
# remove orphans \r
|
|
32
|
+
cfg.gsub! /^\r+(.+)/, '\1'
|
|
33
|
+
# handle pager orphans ^H^H^H^H^H^H^H^H ^H^H^H^H^H^H^H^H
|
|
34
|
+
cfg.gsub! /\x08{8}\x20{8}\x08{8}/, ''
|
|
35
|
+
cfg.cut_both
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
cmd :secret do |cfg|
|
|
39
|
+
cfg.gsub! /(password( \d+)?) (\S+).*/, '\\1 <secret hidden>'
|
|
40
|
+
cfg.gsub! /^(snmp community community-name )\S+ (.*)?/, '\\1 <configuration removed> \\2'
|
|
41
|
+
cfg
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
cmd 'show version all' do |cfg|
|
|
45
|
+
# for older tnsr versions
|
|
46
|
+
cfg = cmd('show version') if cfg.to_s =~ /^CLI syntax error:.+Unknown command$/
|
|
47
|
+
comment cfg
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
cmd 'show configuration running cli' do |cfg|
|
|
51
|
+
cfg
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
cfg :telnet, :ssh do
|
|
55
|
+
pre_logout 'exit'
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -48,17 +48,17 @@ class TPLink < Oxidized::Model
|
|
|
48
48
|
lines[0..lines.index("end\n")].join
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
+
macro :enable, regex: /^[pP]assword:/
|
|
52
|
+
|
|
51
53
|
cfg :telnet, :ssh do
|
|
52
54
|
username /^User ?[nN]ame:/
|
|
53
55
|
password /^\r?Password:/
|
|
56
|
+
newline "\r\n"
|
|
54
57
|
end
|
|
55
58
|
|
|
56
59
|
cfg :telnet, :ssh do
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
send "enable\r"
|
|
60
|
-
cmd vars(:enable)
|
|
61
|
-
end
|
|
60
|
+
post_login do
|
|
61
|
+
cmd 'terminal length 0'
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
pre_logout do
|
|
@@ -14,11 +14,13 @@ class Trango < Oxidized::Model
|
|
|
14
14
|
out << ("opmode " + Regexp.last_match[1])
|
|
15
15
|
out << ("defaultopmode " + Regexp.last_match[2])
|
|
16
16
|
end
|
|
17
|
-
out << ("power " + Regexp.last_match[1]) if line =~ /\[Tx Power\] ([
|
|
17
|
+
out << ("power " + Regexp.last_match[1]) if line =~ /\[Tx Power\] ([-\d]+) dBm/
|
|
18
18
|
out << ("freq " + Regexp.last_match[1] + ' ' + Regexp.last_match[2]) if line =~ /\[Active Channel\] (\d+) (v|h)/
|
|
19
19
|
out << ("peerid " + Regexp.last_match[1]) if line =~ /\[Peer ID\] ([A-F0-9]+)/
|
|
20
20
|
out << ("utype " + Regexp.last_match[1]) if line =~ /\[Unit Type\] (\S+)/
|
|
21
|
-
|
|
21
|
+
if line =~ /\[(Hardware Version|Firmware Version|Model|S\/N)\] (\S+)/
|
|
22
|
+
comments << ('# ' + Regexp.last_match[1] + ': ' + Regexp.last_match[2])
|
|
23
|
+
end
|
|
22
24
|
out << ("remarks " + Regexp.last_match[1]) if line =~ /\[Remarks\] (\S+)/
|
|
23
25
|
out << ("rssiled " + Regexp.last_match[1]) if line =~ /\[RSSI LED\] (on|off)/
|
|
24
26
|
speed = Regexp.last_match[1] if line =~ /\[Speed\] (\d+) Mbps/
|
|
@@ -5,13 +5,74 @@ class TrueNAS < Oxidized::Model
|
|
|
5
5
|
|
|
6
6
|
cmd('uname -a') { |cfg| comment cfg }
|
|
7
7
|
cmd('cat /etc/version') { |cfg| comment cfg }
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
# for TrueNAS SCALE machines, make sure the user you use to connect can run
|
|
10
|
+
# this command, or if needed, with passwordless sudo. Try putting this in
|
|
11
|
+
# /etc/sudoers
|
|
12
|
+
# oxidized ALL=(ALL) NOPASSWD: /usr/bin/find /mnt/.ix-apps/app_configs *, /usr/bin/sqlite3 -readonly file\:/data/freenas-v1.db *
|
|
13
|
+
|
|
14
|
+
cmd("sqlite3 -readonly 'file:/data/freenas-v1.db' .dump") do |cfg|
|
|
15
|
+
if cfg.include? "Error: unable to open database"
|
|
16
|
+
# retry with sudo
|
|
17
|
+
cfg = cmd("sudo sqlite3 -readonly 'file:/data/freenas-v1.db' .dump")
|
|
18
|
+
end
|
|
9
19
|
cfg.lines.reject do |line|
|
|
10
|
-
line.match(/^INSERT INTO storage_replication /) ||
|
|
11
|
-
line.match(/^INSERT INTO system_alert /)
|
|
20
|
+
line.match(/^INSERT INTO storage_replication /) || # ignore storage_replication because repl_status field changes on every run
|
|
21
|
+
line.match(/^INSERT INTO system_alert /) || # ignore system alerts in db
|
|
22
|
+
line.match(/^INSERT INTO sqlite_sequence VALUES\('system_alert',/) || # ignore system alerts in db
|
|
23
|
+
line.match(/^INSERT INTO tasks_cloudsync /) # ignore cloudsync tasks because job field changes on every run
|
|
12
24
|
end.join
|
|
13
25
|
end
|
|
14
26
|
|
|
27
|
+
post do
|
|
28
|
+
filter_column("storage_replication", "repl_state")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
post do
|
|
32
|
+
filter_column("tasks_cloudsync", "job")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
post do
|
|
36
|
+
collect_ixapps_configurations
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def filter_column(table_name, column_name)
|
|
40
|
+
sqlite_cmd = "sqlite3 -readonly 'file:/data/freenas-v1.db'"
|
|
41
|
+
|
|
42
|
+
# This SQL command will create a SELECT query with all columns except the one we want to skip.
|
|
43
|
+
generate_select_cmd = "select 'select ' || group_concat(name,', ') || ' FROM #{table_name};' FROM pragma_table_info('#{table_name}') WHERE name != '#{column_name}';"
|
|
44
|
+
|
|
45
|
+
select_stmt = cmd("#{sqlite_cmd} \"#{generate_select_cmd}\"")
|
|
46
|
+
if select_stmt.include? "Error: unable to open database"
|
|
47
|
+
# retry with sudo
|
|
48
|
+
sqlite_cmd = "sudo #{sqlite_cmd}"
|
|
49
|
+
select_stmt = cmd("#{sqlite_cmd} \"#{generate_select_cmd}\"")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
insert_cmds = "-header '.mode insert #{table_name}' '#{select_stmt}'"
|
|
53
|
+
cmd("#{sqlite_cmd} #{insert_cmds}") do |cfg|
|
|
54
|
+
if cfg.include? "INSERT"
|
|
55
|
+
# Don't add a COMMIT; if the query came up with no rows
|
|
56
|
+
cfg + "COMMIT;\n"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def collect_ixapps_configurations
|
|
62
|
+
config_command = <<~'CMDEND'
|
|
63
|
+
if [ -d /mnt/.ix-apps ];
|
|
64
|
+
then
|
|
65
|
+
sudo find /mnt/.ix-apps/app_configs
|
|
66
|
+
\( -name "app.yaml" -or -name "user_config.yaml" -or -name "metadata.yaml" \)
|
|
67
|
+
-printf "\n\n#######################\n# %p\n#######################\n"
|
|
68
|
+
-exec cat {} \; ;
|
|
69
|
+
else
|
|
70
|
+
echo "# No Apps configuration found in /mnt/.ix-apps";
|
|
71
|
+
fi
|
|
72
|
+
CMDEND
|
|
73
|
+
cmd(config_command.gsub("\n", ""))
|
|
74
|
+
end
|
|
75
|
+
|
|
15
76
|
cfg :ssh do
|
|
16
77
|
exec true # don't run shell, run each command in exec channel
|
|
17
78
|
end
|
data/lib/oxidized/model/ucs.rb
CHANGED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
class Unifiap < Oxidized::Model
|
|
2
|
+
using Refinements
|
|
3
|
+
|
|
4
|
+
# Ubiquiti Unifi AP circa 6.x
|
|
5
|
+
# Should also work for unfi switches, and airOS, maybe they could be combined.
|
|
6
|
+
# Since it relies on exec channels, because the interactive session wouldn't
|
|
7
|
+
# capture all of the system.cfg output, you can't use telnet with this model.
|
|
8
|
+
|
|
9
|
+
# Sometimes there's a handy info command that summarizes some device attributes,
|
|
10
|
+
# but it doesn't seem to be available in exec mode. So we try to build up a similar
|
|
11
|
+
# list by extracting tidbits from various places. AirOS doesn't have some of these
|
|
12
|
+
# files, so we # may have to fall back on other commands, or locations.
|
|
13
|
+
|
|
14
|
+
# First get the board model
|
|
15
|
+
cmd 'head -4 /etc/board.info' do |cfg|
|
|
16
|
+
@model = Regexp.last_match(1) if cfg =~ /board\.name=(\S+)/i
|
|
17
|
+
""
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# and version
|
|
21
|
+
cmd 'cat /etc/version' do |cfg|
|
|
22
|
+
@version = Regexp.last_match(1) if cfg =~ /(\S+)$/i
|
|
23
|
+
""
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Now the Mac address
|
|
27
|
+
cmd 'ifconfig eth0' do |cfg|
|
|
28
|
+
@mac = Regexp.last_match(1) if cfg =~ /eth0\s+Link encap:Ethernet\s+HWaddr\s+(\w+:\w+:\w+:\w+:\w+:\w+)/i
|
|
29
|
+
""
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Next see if we can get our IP and host name out of /etc/hosts
|
|
33
|
+
cmd 'cat /etc/hosts' do |cfg|
|
|
34
|
+
cfg = cfg.split("\n").reject do |line|
|
|
35
|
+
line[/^\s*(127|0000:0000:0000:0000:0000:0000:0000:0001|0:0:0:0:0:0:0:1|::1)/]
|
|
36
|
+
end
|
|
37
|
+
cfg.select do |line|
|
|
38
|
+
if (match = line.match(/(\d+\.\d+\.\d+\.\d+)\s+(\S+)/))
|
|
39
|
+
@ip, @hostname = match.captures
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
""
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# We check here to see if we succeeded with /etc/hosts. If not, then we try again with ifconfig, and /tmp/system.cfg
|
|
46
|
+
cmd 'echo' do
|
|
47
|
+
unless @ip
|
|
48
|
+
cmd 'ifconfig br0' do |cfg|
|
|
49
|
+
@ip = Regexp.last_match(1) if cfg =~ /inet addr:\s*(\d+\.\d+\.\d+\.\d+)/i
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
unless @ip
|
|
53
|
+
cmd 'ifconfig eth0' do |cfg|
|
|
54
|
+
@ip = Regexp.last_match(1) if cfg =~ /inet addr:\s*(\d+\.\d+\.\d+\.\d+)/i
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
unless @hostname
|
|
60
|
+
cmd 'cat /tmp/system.cfg' do |cfg|
|
|
61
|
+
@hostname = Regexp.last_match(1) if cfg =~ /resolv.host.1.name=(\S+)/i
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
""
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Check if ntpclient is running
|
|
68
|
+
cmd 'ps wwww' do |cfg|
|
|
69
|
+
@ntpserver = Regexp.last_match(1) if cfg =~ /bin\/ntpclient.+-h\s*(\S+)/i
|
|
70
|
+
""
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# If it's a Unifi device it may have NTP health indication
|
|
74
|
+
# If there are other places that Ubiquiti puts these status files, add them here.
|
|
75
|
+
cmd '[ -e /tmp/run/ntp.ready ] || [ -e /var/run/ntp.ready ] && echo "File(s) exist(s)" || echo "No such file"' do |cfg|
|
|
76
|
+
if cfg =~ /No such file/i
|
|
77
|
+
if @ntpserver
|
|
78
|
+
# Ok, now lets try getting the skew from the output of ntpclient
|
|
79
|
+
cmd "ntpclient -d -n -c 2 -i0 -h #{@ntpserver}" do |ntp_out|
|
|
80
|
+
@skew = ntpskew(ntp_out)
|
|
81
|
+
end
|
|
82
|
+
@sync = !@skew.nil? && @skew.to_f.abs < 1e6 ? "Synchronized" : "FAIL"
|
|
83
|
+
end
|
|
84
|
+
else
|
|
85
|
+
@ntpserver = true
|
|
86
|
+
@sync = "Synchronized"
|
|
87
|
+
end
|
|
88
|
+
""
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Now we can display it all as a banner
|
|
92
|
+
cmd 'echo' do
|
|
93
|
+
out = []
|
|
94
|
+
out << "*************************"
|
|
95
|
+
out << "Model: #{@model}"
|
|
96
|
+
out << "Version: #{@version}"
|
|
97
|
+
out << "MAC Address: #{@mac}"
|
|
98
|
+
out << "IP Address: #{@ip}"
|
|
99
|
+
out << "Hostname: #{@hostname}"
|
|
100
|
+
out << "NTP: #{@sync}" if @ntpserver
|
|
101
|
+
out << "*************************"
|
|
102
|
+
comment out.join("\n") + "\n"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Followed by the board info
|
|
106
|
+
cmd 'cat /etc/board.info' do |cfg|
|
|
107
|
+
cfg = "#\n# Board Info:\n#\n" + cfg
|
|
108
|
+
comment cfg
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Lastly the system config
|
|
112
|
+
cmd 'cat /tmp/system.cfg' do |cfg|
|
|
113
|
+
cfg = "#\n# System Config:\n#\n" + cfg
|
|
114
|
+
cfg + "\n"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
cmd :secret do |cfg|
|
|
118
|
+
cfg.gsub! /^((?:users|snmp\.(?:user|community))\.\d+\.password)=.+/, "# \\1=<hidden>"
|
|
119
|
+
cfg
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
cfg :ssh do
|
|
123
|
+
exec true # Don't run shell, run each command in exec channel
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# NTPskew: Return the skew in micro seconds from the ntpclient output
|
|
127
|
+
def ntpskew(cfg)
|
|
128
|
+
index = skew = nil
|
|
129
|
+
|
|
130
|
+
cfg.each_line do |line|
|
|
131
|
+
# Look for the header just before the stats line, and find which number is skew
|
|
132
|
+
if line.match(/^\s*[a-z]+\s+[a-z]+\s+[a-z]+\s+[a-z]+/i)
|
|
133
|
+
words = line.split
|
|
134
|
+
index = words.map(&:downcase).index("skew")
|
|
135
|
+
end
|
|
136
|
+
# Now look for the single stats line and grab the skew
|
|
137
|
+
if !index.nil? && line.match(/^\s*[\d.]+\s+[\d.]+\s+[\d.]+\s+[\d.]+/)
|
|
138
|
+
numbers = line.split
|
|
139
|
+
skew = numbers[index]
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
skew
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
class UPLINKOLT < Oxidized::Model
|
|
2
|
+
prompt /^([\w.@()-]+[#>]\s?)$/
|
|
3
|
+
comment '! '
|
|
4
|
+
|
|
5
|
+
cmd :all do |cfg|
|
|
6
|
+
cfg.gsub! /^% Invalid input detected at '\^' marker\.$|^\s+\^$/, ''
|
|
7
|
+
cfg.gsub!(/^show running-config$/, '')
|
|
8
|
+
cfg.gsub!(/^.*\s*#\s*$/, '')
|
|
9
|
+
# Remove leading and trailing whitespace
|
|
10
|
+
cfg.strip!
|
|
11
|
+
# Remove empty lines
|
|
12
|
+
cfg.gsub!(/^\s*$/, '')
|
|
13
|
+
cfg
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
cmd 'configure terminal' do
|
|
17
|
+
# Enter configure terminal mode
|
|
18
|
+
cmd 'show version' do |cfg|
|
|
19
|
+
cfg.gsub! /^show version/, ''
|
|
20
|
+
comment cfg
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
cmd 'show running-config' do |cfg|
|
|
25
|
+
cfg.gsub! /^Current configuration:/, ''
|
|
26
|
+
cfg
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
cfg :telnet, :ssh do
|
|
30
|
+
username /^Login:/i
|
|
31
|
+
password /^Password:/i
|
|
32
|
+
# preferred way to handle additional passwords
|
|
33
|
+
post_login do
|
|
34
|
+
if vars(:enable) == true
|
|
35
|
+
cmd "enable"
|
|
36
|
+
elsif vars(:enable)
|
|
37
|
+
cmd "enable", /^[pP]assword:/
|
|
38
|
+
cmd vars(:enable)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
post_login 'terminal length 0'
|
|
42
|
+
pre_logout 'exit'
|
|
43
|
+
pre_logout 'disable'
|
|
44
|
+
pre_logout 'exit'
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/oxidized/model/voss.rb
CHANGED
|
@@ -23,6 +23,9 @@ class Voss < Oxidized::Model
|
|
|
23
23
|
cfg.gsub! /(^((.*)Last Vlan Change(.*):(.*))$)/, 'removed Last Vlan Change'
|
|
24
24
|
cfg.gsub! /(^((.*)Temperature(.*):(.*))$)/, 'removed Temperature'
|
|
25
25
|
cfg.gsub! /(^((.*)Total Power Usage(.*):(.*))$)/, 'removed Total Power Usage'
|
|
26
|
+
cfg.gsub! /(^((.*)(Tray \d+ Fan \d+)\s+(\w+)\s+(\w+)\s+(.*))$)/, '\3\4 \5 [removed RPM values]'
|
|
27
|
+
cfg.gsub! /(^((.*)Command Execution Time: (.*))$)/, 'Command Execution Time [removed]'
|
|
28
|
+
cfg.gsub! /(^((.*)(Ambient\s+\d+|CPU|INTERNAL\s+MAC|SODIMM)\s+\d+\s+\d+(.*))$)/, '\3\4 [removed temperature readings]'
|
|
26
29
|
comment "#{cfg}\n"
|
|
27
30
|
end
|
|
28
31
|
|
data/lib/oxidized/model/vrp.rb
CHANGED
|
@@ -27,15 +27,19 @@ class VRP < Oxidized::Model
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
cmd 'display version' do |cfg|
|
|
30
|
-
cfg = cfg.each_line.reject
|
|
30
|
+
cfg = cfg.each_line.reject do |l|
|
|
31
|
+
l.match /uptime|^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d(\.\d\d\d)? ?(\+\d\d:\d\d)?$/
|
|
32
|
+
end.join
|
|
31
33
|
comment cfg
|
|
32
34
|
end
|
|
33
35
|
|
|
34
36
|
cmd 'display device' do |cfg|
|
|
37
|
+
cfg = cfg.each_line.reject { |l| l.match /^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d(\.\d\d\d)? ?(\+\d\d:\d\d)?$/ }.join
|
|
35
38
|
comment cfg
|
|
36
39
|
end
|
|
37
40
|
|
|
38
41
|
cmd 'display current-configuration all' do |cfg|
|
|
42
|
+
cfg = cfg.each_line.reject { |l| l.match /^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d(\.\d\d\d)? ?(\+\d\d:\d\d)?$/ }.join
|
|
39
43
|
cfg
|
|
40
44
|
end
|
|
41
45
|
end
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
class Vyatta < Oxidized::Model
|
|
2
2
|
using Refinements
|
|
3
3
|
|
|
4
|
-
# Brocade Vyatta
|
|
4
|
+
# Brocade Vyatta model #
|
|
5
5
|
|
|
6
|
-
prompt
|
|
6
|
+
prompt /@.*(:~\$|>)\s/
|
|
7
7
|
|
|
8
8
|
cmd :all do |cfg|
|
|
9
9
|
cfg.lines.to_a[1..-2].join
|
|
@@ -14,10 +14,16 @@ class Vyatta < Oxidized::Model
|
|
|
14
14
|
cfg.gsub! /plaintext-password (\S+).*/, 'plaintext-password <secret removed>'
|
|
15
15
|
cfg.gsub! /password (\S+).*/, 'password <secret removed>'
|
|
16
16
|
cfg.gsub! /pre-shared-secret (\S+).*/, 'pre-shared-secret <secret removed>'
|
|
17
|
-
cfg.gsub! /community (\S+)
|
|
17
|
+
cfg.gsub! /community (\S+)/, 'community <hidden>'
|
|
18
|
+
cfg.gsub! /private-key (\S+).*/, 'private-key <secret removed>'
|
|
19
|
+
cfg.gsub! /preshared-key (\S+).*/, 'preshared-key <secret removed>'
|
|
18
20
|
cfg
|
|
19
21
|
end
|
|
20
22
|
|
|
23
|
+
cmd 'show version' do |cfg|
|
|
24
|
+
comment cfg
|
|
25
|
+
end
|
|
26
|
+
|
|
21
27
|
cmd 'show configuration commands | no-more'
|
|
22
28
|
|
|
23
29
|
cfg :telnet do
|