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.
Files changed (236) hide show
  1. checksums.yaml +4 -4
  2. data/.coderabbit.yaml +21 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +45 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +22 -0
  5. data/.github/ISSUE_TEMPLATE/support-request.md +39 -0
  6. data/.github/workflows/codeql.yml +4 -4
  7. data/.github/workflows/publishdocker.yml +108 -13
  8. data/.github/workflows/ruby.yml +3 -12
  9. data/.github/workflows/stale.yml +6 -1
  10. data/.gitignore +2 -0
  11. data/.rubocop.yml +69 -13
  12. data/.rubocop_todo.yml +13 -143
  13. data/CHANGELOG.md +431 -4
  14. data/CONTRIBUTING.md +189 -0
  15. data/Dockerfile +89 -34
  16. data/README.md +59 -188
  17. data/Rakefile +58 -12
  18. data/bin/oxidized +2 -5
  19. data/docs/Configuration.md +382 -149
  20. data/docs/Creating-Models.md +262 -10
  21. data/docs/DeviceSimulation.md +196 -0
  22. data/docs/Docker.md +246 -0
  23. data/docs/Hooks.md +146 -51
  24. data/docs/Inputs.md +237 -0
  25. data/docs/Issues.md +123 -0
  26. data/docs/Model-Notes/APC.md +72 -0
  27. data/docs/Model-Notes/Cumulus.md +5 -0
  28. data/docs/Model-Notes/EatonNetwork.md +18 -0
  29. data/docs/Model-Notes/ExaLink.md +43 -0
  30. data/docs/Model-Notes/FSOS.md +17 -0
  31. data/docs/Model-Notes/Fortinet.md +75 -0
  32. data/docs/Model-Notes/GrandstreamHT8xx.md +8 -0
  33. data/docs/Model-Notes/HPEAruba.md +32 -0
  34. data/docs/Model-Notes/IvantiConnectSecure.md +59 -0
  35. data/docs/Model-Notes/OS6.md +10 -0
  36. data/docs/Model-Notes/README.md +1 -20
  37. data/docs/Model-Notes/RouterOS.md +28 -0
  38. data/docs/Model-Notes/SikluMHTG.md +7 -0
  39. data/docs/Model-Notes/TrueNAS.md +23 -0
  40. data/docs/Model-Notes/XGS4600-Zyxel.md +5 -0
  41. data/docs/ModelUnitTests.md +219 -0
  42. data/docs/Outputs.md +102 -5
  43. data/docs/Release.md +102 -0
  44. data/docs/Ruby-API.md +135 -2
  45. data/docs/Sources.md +34 -0
  46. data/docs/Supported-OS-Types.md +207 -270
  47. data/docs/Troubleshooting.md +43 -1
  48. data/extra/device2yaml.rb +259 -0
  49. data/extra/gitdiff-msteams.sh +32 -5
  50. data/extra/hooks/modelrules.rb +55 -0
  51. data/extra/hooks/modelrulesadvanced.rb +167 -0
  52. data/extra/hooks/srcipmap.rb +54 -0
  53. data/extra/nagios_check_failing_nodes.rb +1 -1
  54. data/extra/oxidized.runit +1 -1
  55. data/extra/rest_client.rb +5 -4
  56. data/extra/syslog.rb +11 -6
  57. data/lib/oxidized/cli/support.rb +152 -0
  58. data/lib/oxidized/cli.rb +21 -6
  59. data/lib/oxidized/config/vars.rb +33 -14
  60. data/lib/oxidized/config.rb +28 -17
  61. data/lib/oxidized/core.rb +50 -8
  62. data/lib/oxidized/hook/ciscosparkdiff.rb +11 -9
  63. data/lib/oxidized/hook/exec.rb +5 -4
  64. data/lib/oxidized/hook/githubrepo.rb +55 -18
  65. data/lib/oxidized/hook/noophook.rb +2 -2
  66. data/lib/oxidized/hook/slackdiff.rb +50 -14
  67. data/lib/oxidized/hook/xmppdiff.rb +9 -9
  68. data/lib/oxidized/hook.rb +68 -16
  69. data/lib/oxidized/input/cli.rb +8 -3
  70. data/lib/oxidized/input/debugtext.rb +40 -0
  71. data/lib/oxidized/input/debugyaml.rb +82 -0
  72. data/lib/oxidized/input/exec.rb +2 -11
  73. data/lib/oxidized/input/ftp.rb +2 -19
  74. data/lib/oxidized/input/http.rb +67 -17
  75. data/lib/oxidized/input/input.rb +35 -14
  76. data/lib/oxidized/input/scp.rb +27 -0
  77. data/lib/oxidized/input/ssh.rb +59 -83
  78. data/lib/oxidized/input/sshbase.rb +102 -0
  79. data/lib/oxidized/input/telnet.rb +20 -19
  80. data/lib/oxidized/input/tftp.rb +8 -8
  81. data/lib/oxidized/job.rb +18 -7
  82. data/lib/oxidized/jobs.rb +11 -1
  83. data/lib/oxidized/logger.rb +51 -0
  84. data/lib/oxidized/manager.rb +18 -17
  85. data/lib/oxidized/model/acos.rb +2 -1
  86. data/lib/oxidized/model/addpack.rb +26 -0
  87. data/lib/oxidized/model/adtran.rb +5 -1
  88. data/lib/oxidized/model/adva.rb +3 -3
  89. data/lib/oxidized/model/airfiber.rb +1 -1
  90. data/lib/oxidized/model/aos7.rb +11 -0
  91. data/lib/oxidized/model/aoscx.rb +37 -29
  92. data/lib/oxidized/model/aosw.rb +39 -29
  93. data/lib/oxidized/model/apc_aos.rb +7 -2
  94. data/lib/oxidized/model/apcaos.rb +39 -0
  95. data/lib/oxidized/model/aricentiss.rb +9 -2
  96. data/lib/oxidized/model/arubainstant.rb +93 -0
  97. data/lib/oxidized/model/asa.rb +11 -10
  98. data/lib/oxidized/model/asternos.rb +22 -0
  99. data/lib/oxidized/model/asyncos.rb +3 -3
  100. data/lib/oxidized/model/audiocodes.rb +2 -2
  101. data/lib/oxidized/model/awplus.rb +15 -12
  102. data/lib/oxidized/model/axos.rb +1 -1
  103. data/lib/oxidized/model/bdcom.rb +1 -0
  104. data/lib/oxidized/model/c4cmts.rb +1 -2
  105. data/lib/oxidized/model/ciscosma.rb +1 -1
  106. data/lib/oxidized/model/ciscosmb.rb +6 -1
  107. data/lib/oxidized/model/cnos.rb +13 -10
  108. data/lib/oxidized/model/comtrol.rb +1 -1
  109. data/lib/oxidized/model/comware.rb +5 -3
  110. data/lib/oxidized/model/cumulus.rb +14 -2
  111. data/lib/oxidized/model/defacto.rb +26 -0
  112. data/lib/oxidized/model/dellx.rb +1 -1
  113. data/lib/oxidized/model/dlink.rb +5 -2
  114. data/lib/oxidized/model/dlinknextgen.rb +55 -0
  115. data/lib/oxidized/model/dnos.rb +3 -0
  116. data/lib/oxidized/model/dslcommands.rb +93 -0
  117. data/lib/oxidized/model/dslsetup.rb +102 -0
  118. data/lib/oxidized/model/eatonnetwork.rb +70 -0
  119. data/lib/oxidized/model/edgecos.rb +3 -1
  120. data/lib/oxidized/model/edgeos.rb +7 -6
  121. data/lib/oxidized/model/edgeswitch.rb +3 -1
  122. data/lib/oxidized/model/efos.rb +41 -0
  123. data/lib/oxidized/model/eltex.rb +4 -2
  124. data/lib/oxidized/model/enterasys800.rb +2 -2
  125. data/lib/oxidized/model/enterprise_sonic.rb +46 -0
  126. data/lib/oxidized/model/eos.rb +3 -1
  127. data/lib/oxidized/model/exalink.rb +36 -0
  128. data/lib/oxidized/model/f5os.rb +17 -0
  129. data/lib/oxidized/model/fabricos.rb +2 -2
  130. data/lib/oxidized/model/fastiron.rb +5 -3
  131. data/lib/oxidized/model/firebrick.rb +2 -2
  132. data/lib/oxidized/model/firelinuxos.rb +13 -6
  133. data/lib/oxidized/model/firewareos.rb +10 -1
  134. data/lib/oxidized/model/fortigate.rb +160 -0
  135. data/lib/oxidized/model/fortios.rb +38 -48
  136. data/lib/oxidized/model/fsos.rb +46 -0
  137. data/lib/oxidized/model/gaiaos.rb +4 -4
  138. data/lib/oxidized/model/garderos.rb +38 -0
  139. data/lib/oxidized/model/grandstreamht8xx.rb +19 -0
  140. data/lib/oxidized/model/h3c.rb +1 -1
  141. data/lib/oxidized/model/ibos.rb +1 -0
  142. data/lib/oxidized/model/ingate.rb +47 -0
  143. data/lib/oxidized/model/ios.rb +57 -30
  144. data/lib/oxidized/model/iosxr.rb +2 -2
  145. data/lib/oxidized/model/ironware.rb +7 -5
  146. data/lib/oxidized/model/ivanti.rb +54 -0
  147. data/lib/oxidized/model/junos.rb +12 -5
  148. data/lib/oxidized/model/kornfeldos.rb +33 -0
  149. data/lib/oxidized/model/lenovonos.rb +2 -0
  150. data/lib/oxidized/model/linksyssrw.rb +3 -3
  151. data/lib/oxidized/model/linuxgeneric.rb +4 -2
  152. data/lib/oxidized/model/macros.rb +60 -0
  153. data/lib/oxidized/model/mimosab11.rb +34 -0
  154. data/lib/oxidized/model/ml66.rb +33 -0
  155. data/lib/oxidized/model/mlnxos.rb +18 -14
  156. data/lib/oxidized/model/model.rb +98 -103
  157. data/lib/oxidized/model/mtrlrfs.rb +1 -0
  158. data/lib/oxidized/model/ndms.rb +6 -0
  159. data/lib/oxidized/model/netgear.rb +14 -3
  160. data/lib/oxidized/model/netonix.rb +1 -1
  161. data/lib/oxidized/model/netscaler.rb +1 -1
  162. data/lib/oxidized/model/nodegrid.rb +1 -1
  163. data/lib/oxidized/model/nsxdfw.rb +31 -0
  164. data/lib/oxidized/model/nsxfirewall.rb +2 -1
  165. data/lib/oxidized/model/nxos.rb +20 -15
  166. data/lib/oxidized/model/ocnos.rb +42 -0
  167. data/lib/oxidized/model/onefinity.rb +18 -0
  168. data/lib/oxidized/model/openbsd.rb +1 -1
  169. data/lib/oxidized/model/opengear.rb +36 -1
  170. data/lib/oxidized/model/openwrt.rb +6 -6
  171. data/lib/oxidized/model/opnsense.rb +9 -1
  172. data/lib/oxidized/model/os6.rb +48 -0
  173. data/lib/oxidized/model/outputs.rb +5 -0
  174. data/lib/oxidized/model/panos.rb +2 -0
  175. data/lib/oxidized/model/perle.rb +41 -0
  176. data/lib/oxidized/model/pfsense.rb +9 -0
  177. data/lib/oxidized/model/powerconnect.rb +32 -11
  178. data/lib/oxidized/model/procurve.rb +6 -2
  179. data/lib/oxidized/model/qtech.rb +3 -1
  180. data/lib/oxidized/model/quantaos.rb +8 -6
  181. data/lib/oxidized/model/rgos.rb +33 -0
  182. data/lib/oxidized/model/riverbed.rb +104 -0
  183. data/lib/oxidized/model/routeros.rb +18 -11
  184. data/lib/oxidized/model/saos.rb +18 -1
  185. data/lib/oxidized/model/saos10.rb +38 -0
  186. data/lib/oxidized/model/siklu.rb +1 -1
  187. data/lib/oxidized/model/siklumhtg.rb +22 -0
  188. data/lib/oxidized/model/sixwind.rb +28 -0
  189. data/lib/oxidized/model/slxos.rb +3 -3
  190. data/lib/oxidized/model/smartbyte.rb +48 -0
  191. data/lib/oxidized/model/sonicos.rb +27 -20
  192. data/lib/oxidized/model/speedtouch.rb +1 -1
  193. data/lib/oxidized/model/sros.rb +3 -3
  194. data/lib/oxidized/model/srosmd.rb +1 -1
  195. data/lib/oxidized/model/supermicro.rb +1 -1
  196. data/lib/oxidized/model/telco.rb +1 -1
  197. data/lib/oxidized/model/tmos.rb +1 -0
  198. data/lib/oxidized/model/tnsr.rb +57 -0
  199. data/lib/oxidized/model/tplink.rb +5 -5
  200. data/lib/oxidized/model/trango.rb +4 -2
  201. data/lib/oxidized/model/truenas.rb +64 -3
  202. data/lib/oxidized/model/ucs.rb +1 -1
  203. data/lib/oxidized/model/unifiap.rb +144 -0
  204. data/lib/oxidized/model/uplinkolt.rb +46 -0
  205. data/lib/oxidized/model/voltaire.rb +1 -1
  206. data/lib/oxidized/model/voss.rb +3 -0
  207. data/lib/oxidized/model/vrp.rb +5 -1
  208. data/lib/oxidized/model/vyatta.rb +9 -3
  209. data/lib/oxidized/model/vyos.rb +37 -0
  210. data/lib/oxidized/model/xos.rb +10 -1
  211. data/lib/oxidized/model/zhoneolt.rb +4 -2
  212. data/lib/oxidized/model/zynos.rb +67 -3
  213. data/lib/oxidized/model/zynosadsl.rb +14 -0
  214. data/lib/oxidized/model/zynoscli.rb +1 -1
  215. data/lib/oxidized/model/zynosgs.rb +2 -0
  216. data/lib/oxidized/model/zynosmgs.rb +32 -0
  217. data/lib/oxidized/node.rb +99 -64
  218. data/lib/oxidized/nodes.rb +40 -19
  219. data/lib/oxidized/output/file.rb +80 -43
  220. data/lib/oxidized/output/git.rb +295 -151
  221. data/lib/oxidized/output/gitcrypt.rb +193 -185
  222. data/lib/oxidized/output/http.rb +63 -51
  223. data/lib/oxidized/output/output.rb +20 -4
  224. data/lib/oxidized/signals.rb +44 -0
  225. data/lib/oxidized/source/csv.rb +49 -49
  226. data/lib/oxidized/source/http.rb +76 -63
  227. data/lib/oxidized/source/jsonfile.rb +68 -0
  228. data/lib/oxidized/source/source.rb +73 -18
  229. data/lib/oxidized/source/sql.rb +71 -59
  230. data/lib/oxidized/version.rb +6 -4
  231. data/lib/oxidized/worker.rb +57 -29
  232. data/lib/oxidized.rb +4 -24
  233. data/lib/refinements.rb +21 -1
  234. data/oxidized.gemspec +45 -21
  235. metadata +248 -41
  236. data/lib/oxidized/model/timos.rb +0 -10
@@ -1,33 +1,96 @@
1
1
  # Creating and Extending Models
2
2
 
3
- Oxidized supports a growing list of [operating system types](Supported-OS-Types.md). Out of the box, most model implementations collect configuration data. Some implementations also include a conservative set of additional commands that collect basic device information (device make and model, software version, licensing information, ...) which are appended to the configuration as comments.
3
+ Oxidized supports a growing list of
4
+ [operating system types](Supported-OS-Types.md). Out of the box, most model
5
+ implementations collect configuration data. Some implementations also include a
6
+ conservative set of additional commands that collect basic device information
7
+ (device make and model, software version, licensing information, ...) which are
8
+ appended to the configuration as comments.
9
+
10
+ A user may wish to extend an existing model to collect the output of additional
11
+ commands. Oxidized offers smart loading of models in order to facilitate this
12
+ with ease, without the need to introduce changes to the upstream source code.
13
+
14
+ This methodology allows local site changes to be preserved during Oxidized
15
+ version updates / gem updates. It also enables convenient local development
16
+ of new models.
17
+
18
+ ## Index
19
+ - [Creating a new model](#creating-a-new-model)
20
+ - [Typical Tasks and Solutions](#typical-tasks-and-solutions)
21
+ - [Handling 'enable' mode](#handling-enable-mode)
22
+ - [Remove ANSI escape codes](#remove-ansi-escape-codes)
23
+ - [Conditional commands](#conditional-commands)
24
+ - [Extending an existing model with a new command](#extending-an-existing-model-with-a-new-command)
25
+ - [Create unit tests for the model](#create-unit-tests-for-the-model)
26
+ - [Advanced features](#advanced-features)
27
+ - [Monkey-patching blocks in existing models](#monkey-patching-blocks-in-existing-models)
28
+ - [Help](#help)
4
29
 
5
- A user may wish to extend an existing model to collect the output of additional commands. Oxidized offers smart loading of models in order to facilitate this with ease, without the need to introduce changes to the upstream source code.
30
+ ## Creating a new model
31
+ ### Use the defacto model
32
+ Before you create a new model, check the
33
+ [defacto model](/lib/oxidized/model/defacto.rb). It aims to work with a lot of
34
+ devices copying the defacto standard set by the cisco CLI:
35
+ - login per ssh or telnet
36
+ - disable pager
37
+ - execute `show running-config`
38
+ - run both `exit` and `logout`
39
+
40
+ If you want to extend the defacto model, you can define your own model file,
41
+ and inherit from `Defacto`:
42
+ ```ruby
43
+ require 'oxidized/model/defacto'
6
44
 
7
- This methodology allows local site changes to be preserved during Oxidized version updates / gem updates. It also enables convenient local development of new models.
45
+ class OxiWare < Defacto
46
+ # Redefine how to process show running-config
47
+ def process_config(cfg)
48
+ cfg.gsub(/.*service timestamps.*/, '')
49
+ end
8
50
 
9
- ## Creating a new model
51
+ # run more commands. They will be run before 'show running-config'
52
+ cmd "show cdp neighbors" do |cfg|
53
+ comment cfg
54
+ end
10
55
 
56
+ # Send "enable" password if defined in vars("enable")
57
+ macro :enable
58
+ end
59
+ ```
60
+
61
+ If your model doesn't support "show running-config", or if you prefer to avoid
62
+ the dependency of the defacto model, you will need to write a model "from
63
+ scratch" as explained in the next section.
64
+
65
+ ### Create a new model from scratch
11
66
  An Oxidized model, at minimum, requires just three elements:
12
67
 
13
68
  * A model file, this file should be placed in the ~/.config/oxidized/model directory and named after the target OS type.
14
69
  * A class defined within this file with the same name as the file itself that inherits from `Oxidized::Model`, the base model class.
15
70
  * At least one command that will be executed and the output of which will be collected by Oxidized.
16
71
 
17
- A bare-bone example for a fictional model running the OS type `rootware` could be introduced by creating the file `~/.config/oxidized/model/rootware.rb`, with the following content:
72
+ A bare-bone example for a fictional model running the OS type `rootware` could
73
+ be introduced by creating the file `~/.config/oxidized/model/rootware.rb`, with
74
+ the following content:
18
75
 
19
76
  ```ruby
20
77
  class RootWare < Oxidized::Model
21
78
  using Refinements
22
79
 
23
80
  cmd 'show complete-config'
81
+
82
+ cfg :ssh do
83
+ pre_logout 'exit'
84
+ end
85
+ end
24
86
  ```
25
87
 
26
88
  This model, as-is will:
27
89
 
28
- * Log into the device and expect the default prompt.
90
+ * Log into the device with ssh and expect the default prompt.
29
91
  * Upon matching it, execute the command `show complete-config`
30
92
  * Collect the output.
93
+ * Logout with the command `exit`
31
94
 
32
95
  It is often useful to, at minimum, define the following additional elements for any newly introduced module:
33
96
 
@@ -40,6 +103,184 @@ The API documentation contains a list of [methods](https://github.com/ytti/oxidi
40
103
 
41
104
  A more fleshed out example can be found in the `IOS` and `JunOS` models.
42
105
 
106
+ ## Typical Tasks and Solutions
107
+
108
+ ### Keep or Remove Lines Returned from a Command
109
+ To make command output cleaner, you can remove unwanted lines or keep only
110
+ specific ones.
111
+
112
+ Most devices echo the executed command on the first line and display a
113
+ prompt on the last line. To remove these for all commands, use
114
+ [cut_both](Ruby-API.md#cut_both):
115
+ ```ruby
116
+ cmd :all do |cfg|
117
+ cfg.cut_both
118
+ end
119
+ ```
120
+
121
+ You can also use the macro `clean :cut`, which does the same:
122
+ ```ruby
123
+ clean :cut
124
+ ```
125
+
126
+ If you want to keep only relevant lines, use
127
+ [keep_lines](Ruby-API.md#keep_lines):
128
+ ```ruby
129
+ cmd 'show interfaces transceiver' do |cfg|
130
+ cfg = cfg.keep_lines [
131
+ 'SFP Information',
132
+ /Vendor (Name|Serial Number)/
133
+ ]
134
+ comment cfg + "\n"
135
+ end
136
+ ```
137
+
138
+ If you want to suppress specific lines,
139
+ use [reject_lines](Ruby-API.md#reject_lines):
140
+ ```ruby
141
+ cmd 'show running-config' do |cfg|
142
+ cfg.reject_lines [
143
+ 'System Up Time',
144
+ /Current .* Time:/
145
+ ]
146
+ end
147
+ ```
148
+
149
+ ### Handling 'enable' mode
150
+ Some devices need to send an 'enable' command and an enable password.
151
+
152
+ You can use the `macro :enable` command to implement this:
153
+ ```ruby
154
+ class IOS < Oxidized::Model
155
+ using Refinements
156
+ # ... Code ...
157
+ macro :enable
158
+ end
159
+ ```
160
+
161
+ `macro :enable` takes options:
162
+ - `regex`; the regex to match the password prompt (default: `/password/i`)
163
+ - `inputs`: a symbol or a list of symbols for which inputs enable should be activated (default: %i[telnet ssh])
164
+ - `command`: the command needed to access privileged mode (default: `enable`)
165
+
166
+ If one would want to access a german linux box as root, a minimal model would be:
167
+ ```ruby
168
+ class GermanLinux < Oxidized::Model
169
+ using Refinements
170
+
171
+ prompt /^(\w.*|\W.*)[:#$] /
172
+ comment '# '
173
+
174
+ cmd "id"
175
+
176
+ cfg :ssh do
177
+ pre_logout 'exit'
178
+ pre_logout 'exit'
179
+ end
180
+
181
+ macro :enable, inputs: :ssh, command: "su -", regex: /Passwort: /
182
+ end
183
+ ```
184
+
185
+ The macro (with defaults) implements following code:
186
+ ```ruby
187
+ cfg :telnet, :ssh do
188
+ post_login do
189
+ if vars(:enable) == true
190
+ cmd "enable"
191
+ elsif vars(:enable)
192
+ cmd "enable", /password/i
193
+ cmd vars(:enable)
194
+ end
195
+ end
196
+ end
197
+ ```
198
+
199
+
200
+ ### Remove ANSI Escape Codes
201
+ Some devices produce [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#Control_Sequence_Introducer_commands)
202
+ to enhance the appearance of their output.
203
+ However, this can make prompt matching difficult and some of these ANSI escape
204
+ codes might end up in the resulting configuration.
205
+
206
+ You can remove most ANSI escape codes by inserting the following line in your
207
+ model:
208
+ ```ruby
209
+ clean :escape_codes
210
+ ```
211
+
212
+ When using clean `:escape_codes`, you don't have to worry about escape codes
213
+ in your prompt regexp, as they will be removed before the prompt detection runs.
214
+
215
+ If it doesn't work for your model, please open an issue and provide a
216
+ [device simulation file](/docs/DeviceSimulation.md) so that we can adapt the
217
+ code.
218
+
219
+ ### Conditional commands
220
+ Some times, you have to run commands depending on the output of the device or
221
+ a configured variable. For this, there are at least three solutions.
222
+
223
+ #### Nested `cmd`
224
+ You can nest `cmd` inside [`cmd` blocks](Ruby-API.md#cmd), the following example
225
+ is taken from [nxos.rb](/lib/oxidized/model/nxos.rb):
226
+ ```ruby
227
+ cmd 'show inventory all' do |cfg|
228
+ if cfg.match? /^% Invalid .* at '\^' marker\./
229
+ # 'show inventory all' isn't supported on older versions (See Issues #3657, #3779)
230
+ cfg = cmd 'show inventory'
231
+ end
232
+ comment cfg
233
+ end
234
+ ```
235
+
236
+ #### pre/post blocks
237
+ After all the [`cmd` blocks](Ruby-API.md#cmd) have been run, the [`pre`
238
+ and `post` blocks](Ruby-API.md#pre--post) are run. The following example is
239
+ taken from [junos.rb](/lib/oxidized/model/junos.rb):
240
+ ```ruby
241
+ post do
242
+ out = String.new
243
+ case @model
244
+ when 'mx960'
245
+ out << cmd('show chassis fabric reachability') { |cfg| comment cfg }
246
+ when /^(ex22|ex3[34]|ex4|ex8|qfx)/
247
+ out << cmd('show virtual-chassis') { |cfg| comment cfg }
248
+ when /^srx/
249
+ out << cmd('show chassis cluster status') do |cfg|
250
+ cfg.lines.count <= 1 && cfg.include?("error:") ? String.new : comment(cfg)
251
+ end
252
+ end
253
+ out
254
+ end
255
+ ```
256
+
257
+ In [pre/post blocks](Ruby-API.md#pre--post), you can also use dynamic generated
258
+ commands, for example in [eatonnetwok.rb](/lib/oxidized/model/eatonnetwork.rb):
259
+ ```ruby
260
+ post do
261
+ cmd "save_configuration -p #{@node.auth[:password]}"
262
+ end
263
+ ```
264
+
265
+ #### Conditional `cmd`
266
+ The `cmd "string"` method for accepts a lambda function via the `:if` argument
267
+ to execute the command only when the lambda evaluates to true.
268
+ The lambda function is evaluated at runtime in the instance context.
269
+
270
+ ```ruby
271
+ cmd 'conditional command', if: lambda {
272
+ # Use lambda when multiple lines are needed
273
+ vars("condition")
274
+ } do |cfg|
275
+ @run_second_command = "go"
276
+ comment cfg
277
+ end
278
+
279
+ cmd 'second command', if: -> { @run_second_command == "go" } do |cfg|
280
+ comment cfg
281
+ end
282
+ ```
283
+
43
284
  ## Extending an existing model with a new command
44
285
 
45
286
  The example below can be used to extend the `JunOS` model to collect the output of `show interfaces diagnostics optics` and append the output to the configuration file as a comment. This command retrieves DOM information on pluggable optics present in a `JunOS`-powered chassis.
@@ -72,7 +313,18 @@ Intuitively, it is also possible to:
72
313
  * Create a completely new model, with a new name, for a new operating system type.
73
314
  * Testing/validation of an updated model from the [Oxidized GitHub repo models](https://github.com/ytti/oxidized/tree/master/lib/oxidized/model) by placing an updated model in the proper location without disrupting the gem-supplied model files.
74
315
 
75
- ## Advanced features
316
+ ## Create Unit Tests for the Model
317
+ If you want the model to be integrated into Oxidized, you can
318
+ [submit a pull request on GitHub](https://github.com/ytti/oxidized/pulls).
319
+ This is a greatly appreciated submission, as there are probably other users
320
+ using the same network device as you are.
321
+
322
+ A good (and optional) practice for submissions is to provide a
323
+ [unit test for your model](/docs/ModelUnitTests.md). This reduces the risk that
324
+ further developments could break it, and facilitates debugging issues without
325
+ having access to a physical network device for the model.
326
+
327
+ ## Advanced feature: output type
76
328
 
77
329
  The loosely-coupled architecture of Oxidized allows for easy extensibility in more advanced use cases as well.
78
330
 
@@ -124,19 +376,19 @@ Examples:
124
376
 
125
377
  ```ruby
126
378
  cmd :secret, clear: true do
127
- ... "(new code for secret removal which replaces the existing :secret definition in the model)" ...
379
+ # ... "(new code for secret removal which replaces the existing :secret definition in the model)" ...
128
380
  end
129
381
  ```
130
382
 
131
383
  ```ruby
132
384
  cmd 'show version', clear: true do |cfg|
133
- ... "(new code for parsing 'show version', replaces the existing definition in the model)" ...
385
+ # ... "(new code for parsing 'show version', replaces the existing definition in the model)" ...
134
386
  end
135
387
  ```
136
388
 
137
389
  ```ruby
138
390
  cmd :ssh, prepend: true do
139
- ... "(code that should run first, before any code in the existing :ssh definition in the model)" ...
391
+ # ... "(code that should run first, before any code in the existing :ssh definition in the model)" ...
140
392
  end
141
393
  ```
142
394
 
@@ -0,0 +1,196 @@
1
+ # Device Simulation
2
+ Oxidized supports [150+ devices](/docs/Supported-OS-Types.md).
3
+
4
+ No developer has access to all of these devices, which makes the task of
5
+ maintaining Oxidized difficult:
6
+
7
+ - Issues can't be resolved because the developer has no access to the device.
8
+ - Further developments can produce regressions.
9
+
10
+ In order to address this, we can simulate the devices. An example of a
11
+ simulation is the [model unit tests](/spec/model), but one could also simulate a
12
+ device within an SSH server.
13
+
14
+ The simulation of devices is currently focused on SSH-based devices. This may be
15
+ extended to other inputs like Telnet or FTP in the future.
16
+
17
+ ## YAML Simulation Data
18
+ The underlying data for the simulation is a [YAML](https://yaml.org/) file in
19
+ which we store all relevant information about the device. The most important
20
+ information is the responses to the commands used in the Oxidized models.
21
+
22
+ The YAML simulation files are stored under
23
+ [/spec/model/data/](/spec/model/data/), with the naming convention
24
+ `<model>#<description>#simulation.yaml`, where `<model>` is the lowercase name
25
+ of the Oxidized model and `<description>` is the name of the test case.
26
+ `<description>` is generally formatted as `<hardware>_<software>` or
27
+ `<hardware>_<software>_<information>`.
28
+
29
+ ### Creating a YAML Simulation File with device2yaml.rb
30
+ A device does not only output the ASCII text we can see in the console.
31
+ It adds ANSI escape codes for nice colors, bold and underline, \r, and so on.
32
+ These are key factors in prompt issues, so they must be represented in the YAML
33
+ file. We use the Ruby string format with interpolations like \r, \e, and so on.
34
+ Another important point is trailing spaces at the end of lines. Some text
35
+ editors automatically remove trailing spaces, so we code them with \x20.
36
+
37
+ Although a YAML file could be written by hand, this is quite a tedious task to
38
+ catch all the extra codes and code them into YAML. This can be automated with
39
+ the Ruby script [extra/device2yaml.rb](/extra/device2yaml.rb).
40
+
41
+ `device2yaml.rb` needs Ruby and the gem
42
+ [net-ssh](https://rubygems.org/gems/net-ssh/) to run. On Debian, you can install
43
+ them with `sudo apt install ruby-net-ssh`.
44
+
45
+ Run `extra/device2yaml.rb`, the online help tells you the options.
46
+ ```
47
+ oxidized$ extra/device2yaml.rb
48
+ Missing a host to connect to...
49
+
50
+ Usages:
51
+ - device2yaml.rb [user@]host -i file [options]
52
+ - device2yaml.rb [user@]host -c "command1
53
+ command2
54
+ command3" [options]
55
+
56
+ -i and -c are mutualy exclusive, one must be specified
57
+
58
+ [options]:
59
+ -c, --commands "command list" specify the commands to be run
60
+ -i, --input file Specify an input file for commands to be run
61
+ -o, --output file Specify an output YAML-file
62
+ -t, --timeout value Specify the idle timeout beween commands (default: 5 seconds)
63
+ -e, --exec-mode Run ssh in exec mode (without tty)
64
+ -u, --unordered The YAML simulation should not enforce an order of the commands
65
+ -h, --help Print this help
66
+ ```
67
+
68
+ - `[user@]host` specifies the user and host to connect to the device. The
69
+ password will be prompted interactively by the script. If you do not specify a
70
+ user, it will use the user executing the script.
71
+ - The commands that will be run on the device must be defined in
72
+ `deviceyaml.rb`. You can give the commands online with `-c` or read them from a
73
+ file (one line per command) with `-i`. The commands should match exactly the
74
+ ones of the model (no abbreviations) and include the commands of the
75
+ `post_login` and `pre_logout` sections. When using `-c` and editing the shell
76
+ command line, `CTRL-V CTRL-J` is very useful to add a line break.
77
+ - `device2yaml.rb` waits an idle timeout after the last received data
78
+ before sending the next command. The default is 5 seconds. If your device makes
79
+ a longer pause than 5 seconds before or within a command, you will see that the
80
+ output of the command is shortened or slips into the next command in the YAML
81
+ file. You will have to change the idle timeout to a greater value to address
82
+ this.
83
+ - When run without the output argument, `device2yaml.rb` will only print the SSH
84
+ output to the standard output. You must use `-o <model#HW_SW#simulation.yaml>`
85
+ to store the collected data in a YAML file.
86
+ - If your Oxidized model uses SSH exec mode (look for `exec true` in the model),
87
+ you will have to use the option `-e` to run `device2yaml.rb` in SSH exec mode.
88
+ - The default behavior is to create a YAML file in which the commands must
89
+ appear in the order used in the Oxidized model. This is useful for simulating
90
+ devices that paginate output. To allow any order or include more commands than
91
+ the model uses, use the option `-u`. Note that the `unordered` mode may not
92
+ produce a useful YAML file when combined with user input (see
93
+ [Interactive Mode](#interactive-mode) below).
94
+
95
+ Note that `device2yaml.rb` takes some time to run because of the idle timeout of
96
+ (default) 5 seconds between each command. You can press the "Escape" key if you
97
+ know there is no more data to come for the current command (when you see the
98
+ prompt for the next command), and the script will stop waiting and directly
99
+ process the next command.
100
+
101
+
102
+ Running the script against an ios device would look like:
103
+ ```shell
104
+ extra/device2yaml.rb oxidized@r61 -c "terminal length 0
105
+ terminal width 0
106
+ show version
107
+ show vtp status
108
+ show inventory
109
+ show running-config
110
+ exit" -o spec/model/data/ios#C8200L_16.12.1#simulation.yaml
111
+ ```
112
+ ### Publishing the YAML Simulation File to Oxidized
113
+ Publishing the YAML simulation file of your device helps maintain Oxidized. This
114
+ task may take some time, and we are very grateful that you take this time for
115
+ the community!
116
+
117
+ You should pay attention to removing or replacing anything you don't want to
118
+ share with the rest of the world, for example:
119
+
120
+ - Passwords
121
+ - IP Addresses
122
+ - Serial numbers
123
+
124
+ You can also shorten the configuration if you want - we don't need 48 times the
125
+ same configuration for each interface, but it doesn't hurt either.
126
+
127
+ Take your time, this is an important task: after you have uploaded your file on
128
+ GitHub, it may be impossible to remove it.
129
+ You can use search/replace to make consistent and faster changes, for example
130
+ change the hostname everywhere.
131
+
132
+ The YAML simulation files are stored under
133
+ [/spec/model/data/](/spec/model/data/), with the naming convention
134
+ `<model>#<description>#simulation.yaml`, where `<model>` is the lowercase name
135
+ of the Oxidized model and `<description>` is the name of the test case.
136
+ `<description>` is generally formatted as `<hardware>_<software>` or
137
+ `<hardware>_<software>_<information>`.
138
+
139
+ Using a correct name for the file is important to ensure it is included in
140
+ automatic model unit tests.
141
+
142
+ Examples:
143
+
144
+ - spec/model/data/aoscx#R0X25A-6410_FL.10.10.1100#simulation.yaml
145
+ - spec/model/data/asa#5512_9.12-4-67_single-context#simulation.yaml
146
+ - spec/model/data/ios#C9200L-24P-4G_17.09.04a#simulation.yaml
147
+
148
+ When you are finished, commit and push to your forked repository on GitHub, and
149
+ submit a Pull Request. Thank you for your help!
150
+
151
+ ### Interactive Mode
152
+ The `device2yaml.rb` script is basic and sometimes needs some help, especially
153
+ when dealing with a device that sends its output page by page and requires you
154
+ to press space for the next page. `device2yaml.rb` does not know how to handle
155
+ this.
156
+
157
+ While `device2yaml.rb` is running, you can type anything on the keyboard, and it
158
+ will be sent to the remote device. So you can press space or 'n' to get the next
159
+ page.
160
+
161
+ You can also use this to enter an enable password.
162
+
163
+ Every key press will be recorded in the YAML file, so that it can be used
164
+ in the simulation afterwards, especialy for devices that paginate output. You
165
+ may need to clean the YAML file manually if you don't want some input (such
166
+ as passwords) to be included.
167
+
168
+ If you press the "Esc" key, `device2yaml.rb` will not wait for the idle timeout
169
+ and will process the next command right away.
170
+
171
+ ### YAML Format
172
+ The YAML file has two sections:
173
+ - init_prompt: describing the lines sent by the device before we can send a
174
+ command. It usually includes MOTD banners and must include the first prompt.
175
+ - commands: the commands the Oxidized model sends to the network device and
176
+ their outputs.
177
+
178
+ The outputs are multiline and use YAML block scalars (`|`), with the trailing \n
179
+ removed (`-` after `|`). The outputs include the echo of the given command and
180
+ the next prompt. Escape characters are coded in Ruby style (\n, \r...).
181
+
182
+ Here is a shortened example of a YAML file:
183
+ ```yaml
184
+ ---
185
+ init_prompt: |-
186
+ \e[4m\rLAB-R1234_Garderos#\e[m\x20
187
+ commands:
188
+ show system version: |-
189
+ show system version
190
+ grs-gwuz-armel/003_005_068 (Garderos; 2021-04-30 16:19:35)
191
+ \e[4m\rLAB-R1234_Garderos#\e[m\x20
192
+ # ...
193
+ exit: ""
194
+ ```
195
+
196
+