cisco_node_utils 1.0.1 → 1.1.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 (114) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rubocop.yml +81 -1
  4. data/.travis.yml +9 -0
  5. data/CHANGELOG.md +72 -6
  6. data/CONTRIBUTING.md +32 -7
  7. data/README.md +70 -7
  8. data/Rakefile +17 -0
  9. data/bin/check_metric_limits.rb +109 -0
  10. data/bin/git/hooks/commit-msg/enforce_style +81 -0
  11. data/bin/git/hooks/hook_lib +108 -0
  12. data/bin/git/hooks/hooks-wrapper +38 -0
  13. data/bin/git/hooks/post-flow-hotfix-start/update-version +24 -0
  14. data/bin/git/hooks/post-flow-release-finish/update-version +29 -0
  15. data/bin/git/hooks/post-flow-release-start/update-version +19 -0
  16. data/bin/git/hooks/post-merge/update-hooks +6 -0
  17. data/bin/git/hooks/post-rewrite/update-hooks +6 -0
  18. data/bin/git/hooks/pre-commit/rubocop +20 -0
  19. data/bin/git/hooks/pre-commit/validate-diffs +31 -0
  20. data/bin/git/hooks/pre-push/check-changelog +24 -0
  21. data/bin/git/hooks/pre-push/rubocop +7 -0
  22. data/bin/git/update-hooks +65 -0
  23. data/cisco_node_utils.gemspec +9 -3
  24. data/docs/README-develop-best-practices.md +404 -0
  25. data/docs/README-develop-node-utils-APIs.md +215 -365
  26. data/docs/README-maintainers.md +33 -3
  27. data/docs/template-router.rb +89 -91
  28. data/docs/template-test_router.rb +52 -55
  29. data/lib/.rubocop.yml +18 -0
  30. data/lib/cisco_node_utils.rb +2 -19
  31. data/lib/cisco_node_utils/README_YAML.md +1 -9
  32. data/lib/cisco_node_utils/bgp.rb +664 -0
  33. data/lib/cisco_node_utils/bgp_af.rb +530 -0
  34. data/lib/cisco_node_utils/bgp_neighbor.rb +425 -0
  35. data/lib/cisco_node_utils/bgp_neighbor_af.rb +709 -0
  36. data/lib/cisco_node_utils/cisco_cmn_utils.rb +59 -25
  37. data/lib/cisco_node_utils/command_reference.rb +72 -74
  38. data/lib/cisco_node_utils/command_reference_common.yaml +174 -9
  39. data/lib/cisco_node_utils/command_reference_common_bgp.yaml +535 -0
  40. data/lib/cisco_node_utils/command_reference_n7k.yaml +4 -0
  41. data/lib/cisco_node_utils/command_reference_n9k.yaml +0 -9
  42. data/lib/cisco_node_utils/configparser_lib.rb +152 -147
  43. data/lib/cisco_node_utils/dns_domain.rb +79 -0
  44. data/lib/cisco_node_utils/domain_name.rb +71 -0
  45. data/lib/cisco_node_utils/interface.rb +167 -161
  46. data/lib/cisco_node_utils/interface_ospf.rb +78 -81
  47. data/lib/cisco_node_utils/name_server.rb +64 -0
  48. data/lib/cisco_node_utils/node.rb +154 -198
  49. data/lib/cisco_node_utils/node_util.rb +61 -0
  50. data/lib/cisco_node_utils/ntp_config.rb +65 -0
  51. data/lib/cisco_node_utils/ntp_server.rb +76 -0
  52. data/lib/cisco_node_utils/platform.rb +174 -165
  53. data/lib/cisco_node_utils/radius_global.rb +146 -0
  54. data/lib/cisco_node_utils/radius_server.rb +295 -0
  55. data/lib/cisco_node_utils/router_ospf.rb +59 -63
  56. data/lib/cisco_node_utils/router_ospf_vrf.rb +226 -210
  57. data/lib/cisco_node_utils/snmpcommunity.rb +52 -58
  58. data/lib/cisco_node_utils/snmpgroup.rb +22 -23
  59. data/lib/cisco_node_utils/snmpserver.rb +99 -103
  60. data/lib/cisco_node_utils/snmpuser.rb +294 -274
  61. data/lib/cisco_node_utils/syslog_server.rb +92 -0
  62. data/lib/cisco_node_utils/syslog_settings.rb +69 -0
  63. data/lib/cisco_node_utils/tacacs_server.rb +137 -133
  64. data/lib/cisco_node_utils/tacacs_server_host.rb +84 -87
  65. data/lib/cisco_node_utils/version.rb +2 -1
  66. data/lib/cisco_node_utils/vlan.rb +28 -31
  67. data/lib/cisco_node_utils/vrf.rb +80 -0
  68. data/lib/cisco_node_utils/vtp.rb +100 -97
  69. data/lib/cisco_node_utils/yum.rb +15 -17
  70. data/tests/.rubocop.yml +15 -0
  71. data/tests/basetest.rb +81 -36
  72. data/tests/ciscotest.rb +38 -78
  73. data/{lib/cisco_node_utils → tests}/platform_info.rb +12 -8
  74. data/{lib/cisco_node_utils → tests}/platform_info.yaml +1 -1
  75. data/tests/test_bgp_af.rb +920 -0
  76. data/tests/test_bgp_neighbor.rb +403 -0
  77. data/tests/test_bgp_neighbor_af.rb +589 -0
  78. data/tests/test_command_config.rb +65 -62
  79. data/tests/test_command_reference.rb +31 -45
  80. data/tests/test_dns_domain.rb +113 -0
  81. data/tests/test_domain_name.rb +86 -0
  82. data/tests/test_interface.rb +424 -548
  83. data/tests/test_interface_ospf.rb +248 -432
  84. data/tests/test_interface_svi.rb +56 -79
  85. data/tests/test_interface_switchport.rb +196 -272
  86. data/tests/test_name_server.rb +85 -0
  87. data/tests/test_node.rb +7 -6
  88. data/tests/test_node_ext.rb +133 -186
  89. data/tests/test_ntp_config.rb +49 -0
  90. data/tests/test_ntp_server.rb +74 -0
  91. data/tests/test_platform.rb +58 -37
  92. data/tests/test_radius_global.rb +78 -0
  93. data/tests/test_radius_server.rb +185 -0
  94. data/tests/test_router_bgp.rb +838 -0
  95. data/tests/test_router_ospf.rb +49 -80
  96. data/tests/test_router_ospf_vrf.rb +274 -392
  97. data/tests/test_snmpcommunity.rb +128 -172
  98. data/tests/test_snmpgroup.rb +12 -14
  99. data/tests/test_snmpserver.rb +160 -189
  100. data/tests/test_snmpuser.rb +568 -717
  101. data/tests/test_syslog_server.rb +88 -0
  102. data/tests/test_syslog_settings.rb +54 -0
  103. data/tests/test_tacacs_server.rb +113 -148
  104. data/tests/test_tacacs_server_host.rb +108 -161
  105. data/tests/test_vlan.rb +63 -79
  106. data/tests/test_vrf.rb +92 -0
  107. data/tests/test_vtp.rb +108 -126
  108. data/tests/test_yum.rb +47 -41
  109. metadata +92 -56
  110. data/.rubocop_todo.yml +0 -293
  111. data/docs/.rubocop.yml +0 -13
  112. data/docs/template-feature.rb +0 -45
  113. data/docs/template-test_feature.rb +0 -51
  114. data/tests/test_all_cisco.rb +0 -46
@@ -16,15 +16,21 @@ Designed to work with Puppet and Chef.
16
16
  Currently supports NX-OS nodes.
17
17
  EOF
18
18
  spec.license = 'Apache-2.0'
19
+ spec.homepage = 'https://github.com/cisco/cisco-network-node-utils'
19
20
 
20
21
  spec.files = `git ls-files -z`.split("\x0")
21
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
22
+ # Files in bin/git are not executables as far as the Gem is concerned
23
+ spec.executables = []
22
24
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
23
25
  spec.require_paths = ['lib']
24
26
 
25
- spec.add_development_dependency 'minitest', '>= 2.5.1', '< 5.0.0'
27
+ spec.required_ruby_version = '>= 2.0.0'
28
+ spec.required_rubygems_version = '>= 2.1.0'
29
+
30
+ spec.add_development_dependency 'minitest', '~> 5.0'
26
31
  spec.add_development_dependency 'bundler', '~> 1.7'
27
32
  spec.add_development_dependency 'rake', '~> 10.0'
28
- spec.add_development_dependency 'rubocop', '>= 0.32'
33
+ spec.add_development_dependency 'rubocop', '= 0.34.2'
34
+ spec.add_development_dependency 'simplecov', '~> 0.9'
29
35
  spec.add_runtime_dependency 'cisco_nxapi', '~> 1.0'
30
36
  end
@@ -0,0 +1,404 @@
1
+ # Develoment Best Practices for cisco_node_utils APIs.
2
+
3
+ * [Overview](#overview)
4
+ * [YAML Development Best Practices](#ydbp)
5
+ * [Common Object Development Best Practices](#odbp)
6
+ * [MiniTest Development Best Practices](#mdbp)
7
+
8
+ ## <a name="overview">Overview</a>
9
+
10
+ This document is intended to assist in developing cisco_node_utils API's that are consistent with current best practices.
11
+
12
+
13
+ ## <a name="ydbp">YAML Development Best Practices</a>
14
+
15
+ * [Y1](#yaml1): All yaml feature entries should be kept in alphabetical order.
16
+ * [Y2](#yaml2): Use *regexp* anchors where needed for `config_get` and `config_get_token` entries.
17
+ * Y3: Avoid nested optional matches.
18
+ * [Y4](#yaml4): Use the `_template` feature when getting/setting the same property value at multiple levels.
19
+ * [Y5](#yaml5): When possible include a `default_value` that represents the system default value.
20
+ * [Y6](#yaml6): When possible, use the same `config_get` show command for all properties and document any anomalies.
21
+ * [Y7](#yaml7): Use Key-value wildcards instead of Printf-style wildcards.
22
+ * [Y8](#yaml8): Selection of `show` commands for `config_get`.
23
+
24
+
25
+
26
+ ## <a name="odbp">Common Object Development Best Practices</a>
27
+
28
+ * [CO1](#co1): Features that can be configured under the global and non-global vrfs need to account for this in the object design.
29
+ * [CO2](#co2): Make use of the equality operator allowing proper `instance1 == instance2` checks in the minitests.
30
+ * [CO3](#co3): Use `''` rather than `nil` to represent "property is absent entirely"
31
+ * [CO4](#co4): Make sure all new properites have a `getter`, `setter` and `default_getter` method.
32
+ * [CO5](#co5): Use singleton-like design for resources that cannot have multiple instances.
33
+
34
+ ## <a name="mdbp">MiniTest Development Best Practices</a>
35
+
36
+ * [MT1](#mt1): Ensure that **all new API's** have minitest coverage.
37
+ * [MT2](#mt2): Use appropriate `assert_foo` and `refute_foo` statements rather than `assert_equal`.
38
+ * [MT3](#mt3): Do not hardcode interface names.
39
+ * [MT4](#mt4): Make use of the `config` helper method for device configuration instead of `@device.cmd`.
40
+ * [MT5](#mt5): Make use of the `assert_show_match` and `refute_show_match` helper methods to validate expected outcomes in the CLI instead of `@device.cmd("show...")`.
41
+
42
+
43
+
44
+ ## YAML Best Practices:
45
+
46
+ ### <a name="yaml1">Y1: All yaml feature entries should be kept in alphabetical order.
47
+
48
+ Please keep all feature names in alphabetical order, and all options under a feature in alphabetical order as well. As YAML permits duplicate entries (in which case the last entry overrides any earlier entries), keeping a consistent order helps to prevent accidentally introducing such duplication.
49
+
50
+ Top level features in alpabetical order:
51
+
52
+ ```
53
+ aaa_authentication_login:
54
+ ...
55
+ dnsclient:
56
+ ...
57
+ interface:
58
+ ```
59
+
60
+ Options under a feature:
61
+
62
+ ```
63
+ interface:
64
+ access_vlan:
65
+ ...
66
+ all_interfaces:
67
+ ...
68
+ create:
69
+ ...
70
+ description:
71
+ ...
72
+ ```
73
+
74
+ ### <a name="yaml2">Y2: Use *regexp* anchors where needed for `config_get` and `config_get_token` entries.
75
+
76
+ Please use *regexp* anchors `^$` to ensure you match the correct feature information in the `show` output.
77
+
78
+ ```
79
+ syslog_settings:
80
+ timestamp:
81
+ config_get: "show running-config all | include '^logging timestamp'"
82
+ config_get_token: '/^logging timestamp (.*)$/'
83
+ config_set: '<state> logging timestamp <units>'
84
+ default_value: 'seconds'
85
+ ```
86
+
87
+ ### <a name="yaml3">Y3: Avoid nested optional matches.
88
+
89
+ ### <a name="yaml4">Y4: Use the `_template` feature when getting/setting the same property value at multiple levels.
90
+
91
+ Using the template below, `auto_cost` and `default_metric` can be set under `router ospf foo` and `router ospf foo; vrf blue`.
92
+
93
+ ```
94
+ ospf:
95
+ _template:
96
+ config_get: "show running ospf all"
97
+ config_get_token: '/^router ospf <name>$/'
98
+ config_get_token_append:
99
+ - '/^vrf <vrf>$/'
100
+ config_set: "router ospf <name>"
101
+ config_set_append:
102
+ - "vrf <vrf>"
103
+
104
+ auto_cost:
105
+ config_get_token_append: '/^auto-cost reference-bandwidth (\d+)\s*(\S+)?$/'
106
+ config_set_append: "auto-cost reference-bandwidth <cost> <type>"
107
+ default_value: [40, "Gbps"]
108
+
109
+ default_metric:
110
+ config_get_token_append: '/^default-metric (\d+)?$/'
111
+ config_set_append: "<state> default-metric <metric>"
112
+ default_value: 0
113
+ ```
114
+
115
+ ### <a name="yaml5">Y5: When possible include a `default_value` that represents the system default value.
116
+
117
+ Please make sure to specify a `default_value` and document properties that don't have a system default. System defaults may differ between cisco platforms making it important to define for lookup in the cisco_node_utils common object methods.
118
+
119
+
120
+ Default value for `message_digest_alg_type` is `md5`
121
+
122
+ ```
123
+ message_digest_alg_type:
124
+ config_get: 'show running interface all'
125
+ config_get_token: ['/^interface %s$/i', '/^\s*ip ospf message-digest-key \d+ (\S+)/']
126
+ default_value: 'md5'
127
+ ```
128
+
129
+ **NOTE1: Use strings rather then symbols when applicable**.
130
+
131
+ If the `default_value` differs between cisco platforms, the more specific `command_reference_[platform].yaml` file should be used.
132
+
133
+ For example, if the default value on all platforms except the n9k is `md5` then set the entry in `command_reference_common.yaml` to `md5` and set the entry in `command_reference_n9k.yaml` to it's default `sha2`.
134
+
135
+ ### <a name="yaml6">Y6: When possible, use the same `config_get` show command for all properties and document any anomalies.
136
+
137
+ All properties below use the `show run tacacs all` command except `directed_request` which is documented.
138
+
139
+ ```
140
+ tacacs_server:
141
+ deadtime:
142
+ config_get: "show run tacacs all"
143
+ config_get_token: '/^tacacs-server deadtime\s+(\d+)/'
144
+ config_set: "%s tacacs-server deadtime %d"
145
+ default_value: 0
146
+
147
+ directed_request:
148
+ # oddly, directed request must be retrieved from aaa output
149
+ config_get: "show running aaa all"
150
+ config_get_token: '/(?:no)?\s*tacacs-server directed-request/'
151
+ config_set: "%s tacacs-server directed-request"
152
+ default_value: false
153
+
154
+ encryption_type:
155
+ config_get: "show run tacacs all"
156
+ config_get_token: '/^tacacs-server key (\d+)\s+(\S+)/'
157
+ default_value: 0
158
+
159
+ encryption_password:
160
+ config_get: "show run tacacs all"
161
+ config_get_token: '/^tacacs-server key (\d+)\s+(\S+)/'
162
+ default_value: ""
163
+ ```
164
+
165
+ ### <a name="yaml7">Y7: Use Key-value wildcards instead of Printf-style wildcards.
166
+
167
+ The following approach is moderately more complex to implement but is more readable in the ruby code and is flexible enough to handle significant platform differences in CLI. It is therefore the recommended approach for new development.
168
+
169
+ **Key-value wildcards**
170
+
171
+ ```
172
+ config_set_append: "<state> log-adjacency-changes <type>"
173
+ ```
174
+
175
+ This following approach is quick to implement and concise, but less flexible - in particular it cannot handle a case where different platforms take parameters in a different order - and less readable in the ruby code.
176
+
177
+ **Printf-style wildcards**
178
+
179
+ ```
180
+ config_set_append: "%s log-adjacency-changes %s"
181
+ ```
182
+
183
+ ### <a name="yaml8">Y8: Selection of `show` commands for `config_get`.
184
+
185
+ The following commands should be preferred over `show [feature]` commands since not all `show [feature]` commands behave in the same manner across cisco platforms.
186
+
187
+ * `show running [feature] all` if available.
188
+ * `show running all` if `show running [feature] all` is *not* available.
189
+
190
+
191
+ ## Common Object Best Practices:
192
+
193
+ ### <a name="co1">CO1: Features that can be configured under the global and non-global vrfs need to account for this in the object design.
194
+
195
+ Many cisco features can be configured under the default or global vrf and also under *n* number of non-default vrfs.
196
+
197
+ The following `initialize` and `self.vrfs` methods account for configuration under `default` and `non-default vrfs`.
198
+
199
+ ```
200
+ def initialize(router, name, instantiate=true)
201
+ fail TypeError if router.nil?
202
+ fail TypeError if name.nil?
203
+ fail ArgumentError unless router.length > 0
204
+ fail ArgumentError unless name.length > 0
205
+ @router = router
206
+ @name = name
207
+ @parent = {}
208
+ if @name == 'default'
209
+ @get_args = @set_args = { name: @router }
210
+ else
211
+ @get_args = @set_args = { name: @router, vrf: @name }
212
+ end
213
+
214
+ create if instantiate
215
+ end
216
+
217
+ # Create a hash of all router ospf vrf instances
218
+ def self.vrfs
219
+ hash_final = {}
220
+ RouterOspf.routers.each do |instance|
221
+ name = instance[0]
222
+ vrf_ids = config_get('ospf', 'vrf', name: name)
223
+ hash_tmp = { name =>
224
+ { 'default' => RouterOspfVrf.new(name, 'default', false) } }
225
+ unless vrf_ids.nil?
226
+ vrf_ids.each do |vrf|
227
+ hash_tmp[name][vrf] = RouterOspfVrf.new(name, vrf, false)
228
+ end
229
+ end
230
+ hash_final.merge!(hash_tmp)
231
+ end
232
+ hash_final
233
+ end
234
+ ```
235
+
236
+ ### <a name="co2">CO2: Make use of the equality operator allowing proper `instance1 == instance2` checks in the minitests.
237
+
238
+ Having this logic defined in the common object lets the minitest easily check the specific instances.
239
+
240
+ Without this equality operator `==` only passes if they are the same instance object. With this equality operator `==` passes if they are different objects referring to the same configuration on the node.
241
+
242
+ ```
243
+ def ==(other)
244
+ (name == other.name) && (vrf == other.vrf)
245
+ end
246
+ ```
247
+
248
+ Example Usage:
249
+
250
+ ```
251
+ def test_dnsdomain_create_destroy_multiple
252
+ id1 = 'aoeu.com'
253
+ id2 = 'asdf.com'
254
+ refute_includes(Cisco::DnsDomain.dnsdomains, id1)
255
+ refute_includes(Cisco::DnsDomain.dnsdomains, id2)
256
+
257
+ ns1 = Cisco::DnsDomain.new(id1)
258
+ ns2 = Cisco::DnsDomain.new(id2)
259
+ assert_includes(Cisco::DnsDomain.dnsdomains, id1)
260
+ assert_includes(Cisco::DnsDomain.dnsdomains, id2)
261
+ assert_equal(Cisco::DnsDomain.dnsdomains[id1], ns1)
262
+ assert_equal(Cisco::DnsDomain.dnsdomains[id2], ns2)
263
+
264
+ ns1.destroy
265
+ refute_includes(Cisco::DnsDomain.dnsdomains, id1)
266
+ assert_includes(Cisco::DnsDomain.dnsdomains, id2)
267
+ ns2.destroy
268
+ refute_includes(Cisco::DnsDomain.dnsdomains, id2)
269
+ end
270
+ ```
271
+ ### <a name="co3">CO3: Use `''` rather than `nil` to represent "property is absent entirely"
272
+
273
+ Our convention is to let `''` represent 'not configured at all' rather than `nil`. For example, `interface.rb`:
274
+
275
+ ```
276
+ def vrf
277
+ vrf = config_get('interface', 'vrf', @name)
278
+ return '' if vrf.nil?
279
+ vrf.shift.strip
280
+ end
281
+
282
+ def vrf=(vrf)
283
+ fail TypeError unless vrf.is_a?(String)
284
+ if vrf.empty?
285
+ config_set('interface', 'vrf', @name, 'no', '')
286
+ else
287
+ config_set('interface', 'vrf', @name, '', vrf)
288
+ end
289
+ ```
290
+
291
+ However, if a property has a default value (it is never truly 'removed'), then we should do this instead:
292
+
293
+ ```
294
+ def access_vlan
295
+ vlan = config_get('interface', 'access_vlan', @name)
296
+ return default_access_vlan if vlan.nil?
297
+ vlan.shift.to_i
298
+ end
299
+
300
+ def access_vlan=(vlan)
301
+ config_set('interface', 'access_vlan', @name, vlan)
302
+ ```
303
+
304
+ ### <a name="co4">CO4: Make sure all new properites have a `getter`, `setter` and `default_getter` method.
305
+
306
+ In order to have a complete set of api's for each property it is important that all properties have a `getter`, `setter` and `default_getter` method.
307
+
308
+ This can be seen in the following `router_id` property.
309
+
310
+ ```
311
+ # Getter Method
312
+ def router_id
313
+ match = config_get('ospf', 'router_id', @get_args)
314
+ match.nil? ? default_router_id : match.first
315
+ end
316
+
317
+ # Setter Method
318
+ def router_id=(router_id)
319
+ if router_id == default_router_id
320
+ @set_args[:state] = 'no'
321
+ @set_args[:router_id] = ''
322
+ else
323
+ @set_args[:state] = ''
324
+ @set_args[:router_id] = router_id
325
+ end
326
+
327
+ config_set('ospf', 'router_id', @set_args)
328
+ delete_set_args_keys([:state, :router_id])
329
+ end
330
+
331
+ # Default Getter Method
332
+ def default_router_id
333
+ config_get_default('ospf', 'router_id')
334
+ end
335
+ ```
336
+
337
+ ### <a name="co5">CO5: Use singleton-like design for resources that cannot have multiple instances.
338
+
339
+ See [TacacsServer](../lib/cisco_node_utils/tacacs_server.rb) and [SnmpServer](../lib/cisco_node_utils/snmpserver.rb) for examples.
340
+
341
+ ## MiniTest Best Practices:
342
+
343
+ ### <a name="mt1">MT1: Ensure that *all new API's* have minitest coverage.
344
+
345
+ ### <a name="mt2">MT2: Use appropriate `assert_foo` and `refute_foo` statements rather than `assert_equal`.
346
+
347
+
348
+ Minitest has a bunch of different test methods that are more specific than assert_equal. See [test methods](http://docs.ruby-lang.org/en/2.1.0/MiniTest/Assertions.html) for a complete list, but here are some general guidelines:
349
+
350
+ | Instead of ... | Use ... |
351
+ | ------------------------------|:-----------------:|
352
+ | assert_equal(true, foo.bar?) | assert(foo.bar?) |
353
+ | assert_equal(false, foo.bar?) | refute(foo.bar?) |
354
+ | assert_equal(true, foo.nil?) | assert_nil(foo) |
355
+ | assert_equal(false, foo.nil?) | refute_nil(foo) |
356
+ | assert_equal(true, foo.empty?)| assert_empty(foo) |
357
+
358
+ The more specific assertions also produce more helpful failure messages if something is wrong.
359
+
360
+ ### <a name="mt3">MT3: Do not hardcode interface names.
361
+
362
+ Rather then hardcode an interface name that may or may not exist, instead use
363
+ the `interfaces[]` array.
364
+
365
+ ```
366
+ def create_interface(ifname=interfaces[0])
367
+ @default_show_command = show_cmd(ifname)
368
+ Interface.new(ifname)
369
+ end
370
+ ```
371
+
372
+ If additional interfaces are needed array index `1` and `2` may be used.
373
+
374
+ ### <a name="mt4">MT4: Make use of the `config` helper method for device configuration instead of `@device.cmd`.
375
+
376
+ For conveninence the `config` helper method has been provided for device configuration within the minitests.
377
+
378
+ ```
379
+ config('no feature ospf')
380
+ ```
381
+
382
+ ```
383
+ config('feature ospf'; 'router ospf green')
384
+ ```
385
+
386
+ ### <a name="mt5">MT5: Make use of the `assert_show_match` and `refute_show_match` helper methods to validate expected outcomes in the CLI instead of `@device.cmd("show...")`.
387
+
388
+ We have a very common pattern in minitest where we execute some show command over the telnet connection, match it against some regexp pattern, and succeed or fail based on the result. Helper methods `assert_show_match` and `refute_show_match` support this pattern.
389
+
390
+ ```
391
+ assert_show_match(command: 'show run all | no-more',
392
+ pattern: /interface port-channel 1/,
393
+ msg: 'port-channel is not present but it should be')
394
+ ```
395
+
396
+ If your `command` and/or `pattern` are the same throughout a test case or throughout a test suite, you can set the test case instance variables `@default_show_command` and/or `@default_output_pattern` which serve as defaults for these parameters:
397
+
398
+ ```
399
+ @default_show_command = 'show run interface all | include "interface" | no-more'
400
+ assert_output_match(pattern: /interface port-channel 10/)
401
+ refute_output_match(pattern: /interface port-channel 11/)
402
+ refute_output_match(pattern: /interface port-channel 12/)
403
+ assert_output_match(pattern: /interface port-channel 13/)
404
+ ```
@@ -3,258 +3,86 @@
3
3
  #### Table of Contents
4
4
 
5
5
  * [Overview](#overview)
6
- * [Start here: Clone the Repo](#clone)
7
- * [Basic Example: feature bash-shell](#simple)
8
- * [Step 1. YAML Definitions: feature bash-shell](#yaml)
9
- * [Step 2. Create the node_utils API: feature bash-shell](#api)
10
- * [Step 3. Create the Minitest: feature bash-shell](#minitest)
11
- * [Step 4. rubocop / lint: feature bash-shell](#lint)
12
- * [Advanced Example: router eigrp](#complex)
6
+ * [Before You Begin](#prerequisites)
7
+ * [Start here: Fork and Clone the Repo](#clone)
8
+ * [Example: router eigrp](#complex)
13
9
  * [Step 1. YAML Definitions: router eigrp](#comp_yaml)
14
10
  * [Step 2. Create the node_utils API: router eigrp](#comp_api)
15
11
  * [Step 3. Create the Minitest: router eigrp](#comp_minitest)
16
12
  * [Step 4. rubocop / lint: router eigrp](#comp_lint)
13
+ * [Step 5. Build and Install the gem](#comp_gem)
17
14
 
18
15
  ## <a name="overview">Overview</a>
19
16
 
20
- This document is a HowTo guide for writing new cisco node_utils APIs. The node_utils APIs act as an interface between the NX-OS CLI and an agent's resource/provider. If written properly the new API will work as a common framework for multiple providers (Puppet, Chef, etc).
17
+ This document is a HowTo guide for writing new cisco_node_utils APIs. The APIs act as an interface between the NX-OS CLI and an agent's resource/provider. If written properly the new API will work as a common framework for multiple providers (Puppet, Chef, etc). In addition to this guide, please reference the [cisco_node_utils development 'best practices' guide.](./README-develop-best-practices.md)
21
18
 
22
- There are multiple components involved when creating new resources. This document focuses on the cisco node_utils API, command reference YAML files, and minitests.
19
+ There are multiple components involved when creating new resources. This document focuses on the cisco_node_utils API, command reference YAML files, and minitests.
23
20
 
24
21
  ![1](agent_files.png)
25
22
 
26
- ## <a name="clone">Start here: Clone the Repo</a>
23
+ ## <a name="prerequisites">Before You Begin</a>
27
24
 
28
- First install the code base. Clone the cisco_node_utils repo into a workspace:
25
+ Please note: A virtual Nexus N9000/N3000 may be helpful for development and testing. Users with a valid [cisco.com](http://cisco.com) user ID can obtain a copy of a virtual Nexus N9000/N3000 by sending their [cisco.com](http://cisco.com) user ID in an email to <get-n9kv@cisco.com>. If you do not have a [cisco.com](http://cisco.com) user ID please register for one at [https://tools.cisco.com/IDREG/guestRegistration](https://tools.cisco.com/IDREG/guestRegistration)
29
26
 
30
- ```bash
31
- git clone https://github.com/cisco/cisco-network-node-utils.git
32
- ```
33
-
34
- ## <a name="simple">Basic Example: feature bash-shell</a>
35
-
36
- Writing a new node_utils API is often easier to understand through example code. The NX-OS CLI for `feature bash-shell` is a simple on / off style configuration and therefore a good candidate for a simple API:
37
-
38
- `[no] feature bash-shell`
39
-
40
- ### <a name="yaml">Step 1. YAML Definitions: feature bash-shell</a>
41
-
42
- The new API will need some basic YAML definitions. These are used with the `CommandReference` module as a way to abstract away platform CLI differences.
43
-
44
- `command_reference_common.yaml` is used for settings that are common across all platforms while other files are used for settings that are unique to a given platform. Our `feature bash-shell` example uses the same cli syntax on all platforms, thus we only need to edit the common file:
45
-
46
- `cisco_network_node_utils/lib/cisco_node_utils/command_reference_common.yaml`
47
-
48
- Four basic command_reference parameters will be defined for each resource property:
49
-
50
- 1. `config_get:` This defines the NX-OS CLI command (usually a 'show...' command) used to retrieve the property's current configuration state. Note that some commands may not be present until a feature is enabled.
51
- 2. `config_get_token:` A regexp pattern for extracting state values from the config_get output.
52
- 3. `config_set:` The NX-OS CLI configuration command(s) used to set the property configuration. May contain wildcards for variable parameters.
53
- 4. `default_value:` This is typically the "factory" default state of the property, expressed as an actual value (true, 12, "off", etc)
54
-
55
- There are additional YAML command parameters available which are not covered by this document. Please see the [README_YAML.md](../lib/cisco_node_utils/README_YAML.md) document for more information on the structure and semantics of these files.
56
-
57
- #### Example: YAML Property Definitions for feature bash-shell
58
-
59
- The `feature bash-shell` configuration is displayed with the `show running-config` command. Anchor the config_get_token regexp pattern carefully as it may match on unwanted configurations.
60
-
61
- *Note: YAML syntax has strict indentation rules. Do not use TABs.*
62
-
63
- ```
64
- bash_shell:
65
- feature:
66
- config_get: 'show running' # get current bash config state
67
- config_get_token: '/^feature bash-shell$/' # Match only 'feature bash-shell'
68
- config_set: '<state> feature bash-shell' # Config needed to enable/disable
69
- ```
70
-
71
- ### <a name="api">Step 2. cisco_node_utils API file: feature bash-shell</a>
72
-
73
- * Before creating the new API, first add a new entry: `require "cisco_node_utils/bash_shell"` to the master list of resources in:
74
-
75
- ```
76
- cisco_network_node_utils/lib/cisco_node_utils.rb
77
- ```
78
-
79
- * There are template files in /docs that may help when writing new APIs. These templates provide most of the necessary code with just a few customizations required for a new resource. Copy the `template-feature.rb` file to use as the basis for `bash_shell.rb`:
27
+ This development guide uses tools that are packaged as gems that need to be installed on your server.
80
28
 
81
29
  ```bash
82
- cp docs/template-feature.rb cisco_network_node_utils/bash_shell.rb
30
+ gem install cisco_nxapi
31
+ gem install rake
32
+ gem install rubocop
33
+ gem install simplecov
34
+ gem install minitest
83
35
  ```
84
36
 
85
- * Edit `bash_shell.rb` and substitute the placeholder text as shown here:
37
+ **NOTE:** If you are working from a server where you don't have admin/root privilages, use the following commands to install the gems and then update the `PATH` to include `~/.gem/ruby/x.x.x/bin`
86
38
 
87
39
  ```bash
88
- /X__CLASS_NAME__X/BashShell/
89
-
90
- /X__RESOURCE_NAME__X/bash_shell/
40
+ gem install --user-install cisco_nxapi
41
+ gem install --user-install rake
42
+ gem install --user-install rubocop
43
+ gem install --user-install simplecov
44
+ gem install --user-install minitest
91
45
  ```
92
46
 
93
- #### Example: bash_shell.rb API
94
-
95
- This is the completed bash_shell API based on `template-feature.rb`:
96
-
97
- ```ruby
47
+ ## <a name="clone">Start here: Fork and Clone the Repo</a>
98
48
 
99
- require File.join(File.dirname(__FILE__), 'node')
100
- module Cisco
101
- # Class name syntax will typically be the resource name in camelCase
102
- # format; for example: 'tacacs server host' becomes TacacsServerHost.
103
- class BashShell
104
- # Establish connection to node
105
- @@node = Cisco::Node.instance
106
-
107
- def feature_enable
108
- @@node.config_set('bash_shell', 'feature', { :state => '' })
109
- end
49
+ First [fork](https://help.github.com/articles/fork-a-repo) the [cisco-network-node-utils](https://github.com/cisco/cisco-network-node-utils) git repository
110
50
 
111
- def feature_disable
112
- @@node.config_set('bash_shell', 'feature', { :state => 'no' })
113
- end
114
-
115
- # Check current state of the configuration
116
- def BashShell.feature_enabled
117
- feat = @@node.config_get('bash_shell', 'feature')
118
- return (!feat.nil? and !feat.empty?)
119
- rescue Cisco::CliError => e
120
- # This cmd will syntax reject if feature is not
121
- # enabled. Just catch the reject and return false.
122
- return false if e.clierror =~ /Syntax error/
123
- raise
124
- end
125
- end
126
- end
127
- ```
128
-
129
- ### <a name="minitest">Step 3. Minitest: feature bash-shell</a>
130
-
131
- * A minitest should be created to validate the new APIs. Minitests are stored in the tests directory: `cisco_network_node_utils/tests/`
132
-
133
- * Tests may use `@device.cmd("show ...")` to access the CLI directly set up tests and validate expected outcomes. The tests directory contains many examples of how these are used.
134
-
135
- * Our minitest will be very basic since the API itself is very basic. Use `template-test_feature.rb` to create a minitest for the bash_shell resource:
51
+ Next install the code base. Clone the cisco-network-node-utils repo from your fork into a workspace:
136
52
 
137
53
  ```bash
138
- cp docs/template-test_feature.rb cisco_network_node_utils/tests/test_bash_shell.rb
54
+ git clone https://github.com/YOUR-USERNAME/cisco-network-node-utils.git
55
+ cd cisco-network-node-utils/
139
56
  ```
140
57
 
141
- * As with the API code, edit `test_bash_shell.rb` and change the placeholder names as shown:
58
+ Please note that any code commits must be associated with your github account and email address. If you intend to commit code to this repository then use the following commands to update your workspace with your credentials:
142
59
 
143
60
  ```bash
144
- /X__CLASS_NAME__X/BashShell/
145
-
146
- /X__RESOURCE_NAME__X/bash_shell/
147
-
148
- /X__CLI_NAME__X/bash-shell/
61
+ git config --global user.name "John Doe"
62
+ git config --global user.email johndoe@example.com
149
63
  ```
150
64
 
151
- #### Example: test_bash_shell.rb
152
-
153
- This is the completed `bash_shell` minitest based on `template-test_feature.rb`:
154
-
155
- ```ruby
156
- #
157
- # Minitest for BashShell class
158
- #
159
- # Copyright (c) 2014-2015 Cisco and/or its affiliates.
160
- #
161
- # Licensed under the Apache License, Version 2.0 (the "License");
162
- # you may not use this file except in compliance with the License.
163
- # You may obtain a copy of the License at
164
- #
165
- # http://www.apache.org/licenses/LICENSE-2.0
166
- #
167
- # Unless required by applicable law or agreed to in writing, software
168
- # distributed under the License is distributed on an "AS IS" BASIS,
169
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
170
- # See the License for the specific language governing permissions and
171
- # limitations under the License.
172
-
173
- require File.expand_path("../ciscotest", __FILE__)
174
- require File.expand_path("../../lib/cisco_node_utils/bash_shell", __FILE__)
175
-
176
- class TestBashShell < CiscoTestCase
177
- def setup
178
- # setup automatically runs at the beginning of each test
179
- super
180
- no_feature
181
- end
182
-
183
- def teardown
184
- # teardown automatically runs at the end of each test
185
- no_feature
186
- super
187
- end
188
-
189
- def no_feature
190
- # setup/teardown helper. Turn the feature off for a clean testbed.
191
- @device.cmd('conf t ; no feature bash-shell ; end')
192
- node.cache_flush()
193
- end
194
-
195
- def test_feature_on_off
196
- feat = BashShell.new()
197
- feat.feature_enable
198
- assert(BashShell.feature_enabled)
199
-
200
- feat.feature_disable
201
- refute(BashShell.feature_enabled)
202
- end
203
-
204
- end
205
- ```
206
-
207
-
208
- We can now run the new minitest against our NX-OS device using this syntax:
209
-
210
- ```bash
211
- ruby test_bash_shell.rb -- <node_ip_address> <user> <passwd>
212
- ```
213
- *Note. The minitest requires that the NX-OS device have 'feature nxapi' enabled. This will typically be enabled by default.*
214
-
215
- #### Example: Running bash_shell minitest
65
+ As a best practice create a topic/feature branch for your feature work using the `git branch feature/<feature_name>` command.
216
66
 
217
67
  ```bash
218
- % ruby test_bash_shell.rb -- 192.168.0.1 admin admin
219
- Run options: -v -- --seed 23392
220
-
221
- # Running tests:
222
-
223
- CiscoTestCase#test_placeholder =
224
- Ruby Version - 1.9.3
225
- Node in CiscoTestCase Class: 192.168.0.1
226
- Platform:
227
- - name - my_n9k
228
- - type - N9K-C9504
229
- - image - bootflash:///n9000-dk9.7.0.3.I2.0.509.bin
230
-
231
- 1.79 s = .
232
- TestBashShell#test_feature_on_off = 1.42 s = .
233
- TestBashShell#test_placeholder = 0.95 s = .
234
- TestCase#test_placeholder = 0.81 s = .
235
-
236
- Finished tests in 4.975186s, 0.8040 tests/s, 0.4020 assertions/s.
237
-
238
- 4 tests, 2 assertions, 0 failures, 0 errors, 0 skips
68
+ git branch feature/eigrp
69
+ git branch
70
+ * develop
71
+ feature/eigrp
239
72
  ```
240
73
 
241
- *Note. The minitest harness counts the helper methods as tests which is why the final tally shows 4 tests instead of just 2 tests.*
242
74
 
243
- ### <a name="lint">Step 4. rubocop / lint: feature bash-shell</a>
75
+ ## <a name="complex">Example: router eigrp</a>
244
76
 
245
- rubocop is a Ruby static analysis tool. Run rubocop with the --lint option to validate the new API:
77
+ Before you start working on the eigrp feature, checkout the feature branch you created earlier.
246
78
 
247
79
  ```bash
248
- % rubocop --lint bash_shell.rb
249
- Inspecting 1 file
250
- .
251
-
252
- 1 file inspected, no offenses detected
80
+ git checkout feature/eigrp
81
+ git branch
82
+ develop
83
+ * feature/eigrp
253
84
  ```
254
85
 
255
- ## <a name="complex">Advanced Example: router eigrp</a>
256
-
257
- Now that we have a basic example working we can move on to a slightly more complex cli.
258
86
  `router eigrp` requires feature enablement and supports multiple eigrp instances. It also has multiple configuration levels for vrf and address-family.
259
87
 
260
88
  For the purposes of this example we will only implement the following properties:
@@ -274,10 +102,20 @@ Example:
274
102
 
275
103
  ### <a name="comp_yaml">Step 1. YAML Definitions: router eigrp</a>
276
104
 
277
- As with the earlier example, `router eigrp` will need YAML definitions in the common file:
105
+ The new API for `router eigrp` will need some basic YAML definitions.
278
106
 
279
- `cisco_network_node_utils/lib/cisco_node_utils/command_reference_common.yaml`
107
+ `command_reference_common.yaml` is used for settings that are common across all platforms while other files are used for settings that are unique to a given platform. Our `router eigrp` example uses the same cli syntax on all platforms, thus we only need to edit the common file:
280
108
 
109
+ `lib/cisco_node_utils/command_reference_common.yaml`
110
+
111
+ Four basic command_reference parameters will be defined for each resource property:
112
+
113
+ 1. `config_get:` This defines the NX-OS CLI command (usually a 'show...' command) used to retrieve the property's current configuration state. Note that some commands may not be present until a feature is enabled.
114
+ 2. `config_get_token:` A regexp pattern for extracting state values from the config_get output.
115
+ 3. `config_set:` The NX-OS CLI configuration command(s) used to set the property configuration. May contain wildcards for variable parameters.
116
+ 4. `default_value:` This is typically the "factory" default state of the property, expressed as an actual value (true, 12, "off", etc)
117
+
118
+ There are additional YAML command parameters available which are not covered by this document. Please see the [README_YAML.md](../lib/cisco_node_utils/README_YAML.md) document for more information on the structure and semantics of these files.
281
119
  The properties in this example require additional context for their config_get_token values because they need to differentiate between different eigrp instances. Most properties will also have a default value.
282
120
 
283
121
  *Note: Eigrp also has vrf and address-family contexts. These contexts require additional coding and are beyond the scope of this document.*
@@ -317,16 +155,10 @@ eigrp:
317
155
 
318
156
  ### <a name="comp_api">Step 2. cisco_node_utils API: router eigrp</a>
319
157
 
320
- * Add a new entry: `require "cisco_node_utils/router_eigrp"` to the master list in:
321
-
322
- ```
323
- cisco_network_node_utils/lib/cisco_node_utils.rb
324
- ```
325
-
326
158
  * The `template-router.rb` file provides a basic router API that we will use as the basis for `router_eigrp.rb`:
327
159
 
328
160
  ```bash
329
- cp docs/template-router.rb cisco_network_node_utils/router_eigrp.rb
161
+ cp docs/template-router.rb lib/cisco_node_utils/router_eigrp.rb
330
162
  ```
331
163
 
332
164
  * Our new `router_eigrp.rb` requires changes from the original template. Edit `router_eigrp.rb` and change the placeholder names as shown.
@@ -347,9 +179,6 @@ cp docs/template-router.rb cisco_network_node_utils/router_eigrp.rb
347
179
  This is the completed `router_eigrp` API based on `template-router.rb`:
348
180
 
349
181
  ```ruby
350
- #
351
- # NXAPI implementation of RouterEigrp class
352
- #
353
182
  # Copyright (c) 2014-2015 Cisco and/or its affiliates.
354
183
  #
355
184
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -364,114 +193,111 @@ This is the completed `router_eigrp` API based on `template-router.rb`:
364
193
  # See the License for the specific language governing permissions and
365
194
  # limitations under the License.
366
195
 
367
- require File.join(File.dirname(__FILE__), 'node')
196
+ require_relative 'node_util'
368
197
 
369
198
  module Cisco
370
- class RouterEigrp
371
- attr_reader :name
372
-
373
- # Establish connection to node
374
- @@node = Cisco::Node.instance
375
-
376
- # name: name of the router instance
377
- # instantiate: true = create router instance
378
- def initialize(name, instantiate=true)
379
- raise ArgumentError unless name.length > 0
380
- @name = name
381
- create if instantiate
382
- end
199
+ # RouterEigrp - node utility class for EIGRP config management.
200
+ class RouterEigrp < NodeUtil
201
+ attr_reader :name
202
+
203
+ # name: name of the router instance
204
+ # instantiate: true = create router instance
205
+ def initialize(name, instantiate=true)
206
+ fail ArgumentError unless name.length > 0
207
+ @name = name
208
+ create if instantiate
209
+ end
383
210
 
384
- # Create a hash of all current router instances.
385
- def RouterEigrp.routers
386
- instances = @@node.config_get('eigrp', 'router')
387
- return {} if instances.nil?
388
- hash = {}
389
- instances.each do |name|
390
- hash[name] = RouterEigrp.new(name, false)
211
+ # Create a hash of all current router instances.
212
+ def self.routers
213
+ instances = config_get('eigrp', 'router')
214
+ return {} if instances.nil?
215
+ hash = {}
216
+ instances.each do |name|
217
+ hash[name] = RouterEigrp.new(name, false)
218
+ end
219
+ return hash
220
+ rescue Cisco::CliError => e
221
+ # CLI will syntax reject when feature is not enabled
222
+ raise unless e.clierror =~ /Syntax error/
223
+ return {}
391
224
  end
392
- return hash
393
- rescue Cisco::CliError => e
394
- # cmd will syntax reject when feature is not enabled
395
- raise unless e.clierror =~ /Syntax error/
396
- return {}
397
- end
398
225
 
399
- def feature_enabled
400
- feat = @@node.config_get('eigrp', 'feature')
401
- return (!feat.nil? and !feat.empty?)
402
- rescue Cisco::CliError => e
403
- # This cmd will syntax reject if feature is not
404
- # enabled. Just catch the reject and return false.
405
- return false if e.clierror =~ /Syntax error/
406
- raise
407
- end
226
+ def feature_enabled
227
+ feat = config_get('eigrp', 'feature')
228
+ return !(feat.nil? || feat.empty?)
229
+ rescue Cisco::CliError => e
230
+ # This cmd will syntax reject if feature is not
231
+ # enabled. Just catch the reject and return false.
232
+ return false if e.clierror =~ /Syntax error/
233
+ raise
234
+ end
408
235
 
409
- def feature_enable
410
- @@node.config_set('eigrp', 'feature', { :state => '' })
411
- end
236
+ def feature_enable
237
+ config_set('eigrp', 'feature', state: '')
238
+ end
412
239
 
413
- def feature_disable
414
- @@node.config_set('eigrp', 'feature', { :state => 'no' })
415
- end
240
+ def feature_disable
241
+ config_set('eigrp', 'feature', state: 'no')
242
+ end
416
243
 
417
- # Enable feature and create router instance
418
- def create
419
- feature_enable unless feature_enabled
420
- eigrp_router
421
- end
244
+ # Enable feature and create router instance
245
+ def create
246
+ feature_enable unless feature_enabled
247
+ eigrp_router
248
+ end
422
249
 
423
- # Destroy a router instance; disable feature on last instance
424
- def destroy
425
- ids = @@node.config_get('eigrp', 'router')
426
- return if ids.nil?
427
- if ids.size == 1
428
- feature_disable
429
- else
430
- eigrp_router('no')
250
+ # Destroy a router instance; disable feature on last instance
251
+ def destroy
252
+ ids = config_get('eigrp', 'router')
253
+ return if ids.nil?
254
+ if ids.size == 1
255
+ feature_disable
256
+ else
257
+ eigrp_router('no')
258
+ end
259
+ rescue Cisco::CliError => e
260
+ # CLI will syntax reject when feature is not enabled
261
+ raise unless e.clierror =~ /Syntax error/
431
262
  end
432
- rescue Cisco::CliError => e
433
- # cmd will syntax reject when feature is not enabled
434
- raise unless e.clierror =~ /Syntax error/
435
- end
436
263
 
437
- def eigrp_router(state='')
438
- @@node.config_set('eigrp', 'router', { :name => @name, :state => state })
439
- end
264
+ def eigrp_router(state='')
265
+ config_set('eigrp', 'router', name: @name, state: state)
266
+ end
440
267
 
441
- # ----------
442
- # PROPERTIES
443
- # ----------
268
+ # ----------
269
+ # PROPERTIES
270
+ # ----------
444
271
 
445
- # Property methods for boolean property
446
- def default_shutdown
447
- @@node.config_get_default('eigrp', 'shutdown')
448
- end
272
+ # Property methods for boolean property
273
+ def default_shutdown
274
+ config_get_default('eigrp', 'shutdown')
275
+ end
449
276
 
450
- def shutdown
451
- state = @@node.config_get('eigrp', 'shutdown', { :name => @name })
452
- state ? true : false
453
- end
277
+ def shutdown
278
+ state = config_get('eigrp', 'shutdown', name: @name)
279
+ state ? true : false
280
+ end
454
281
 
455
- def shutdown=(state)
456
- state = (state ? '' : 'no')
457
- @@node.config_set('eigrp', 'shutdown', { :name => @name, :state => state })
458
- end
282
+ def shutdown=(state)
283
+ state = (state ? '' : 'no')
284
+ config_set('eigrp', 'shutdown', name: @name, state: state)
285
+ end
459
286
 
460
- # Property methods for integer property
461
- def default_maximum_paths
462
- @@node.config_get_default('eigrp', 'maximum_paths')
463
- end
287
+ # Property methods for integer property
288
+ def default_maximum_paths
289
+ config_get_default('eigrp', 'maximum_paths')
290
+ end
464
291
 
465
- def maximum_paths
466
- val = @@node.config_get('eigrp', 'maximum_paths', { :name => @name })
467
- val.nil? ? default_maximum_paths : val.first.to_i
468
- end
292
+ def maximum_paths
293
+ val = config_get('eigrp', 'maximum_paths', name: @name)
294
+ val.nil? ? default_maximum_paths : val.first.to_i
295
+ end
469
296
 
470
- def maximum_paths=(val)
471
- @@node.config_set('eigrp', 'maximum_paths', { :name => @name, :val => val })
297
+ def maximum_paths=(val)
298
+ config_set('eigrp', 'maximum_paths', name: @name, val: val)
299
+ end
472
300
  end
473
-
474
- end
475
301
  end
476
302
  ```
477
303
 
@@ -480,7 +306,7 @@ end
480
306
  * Use `template-test_router.rb` to build the minitest for `router_eigrp.rb`:
481
307
 
482
308
  ```
483
- cp docs/template-test_router.rb cisco_network_node_utils/tests/test_router_eigrp.rb
309
+ cp docs/template-test_router.rb tests/test_router_eigrp.rb
484
310
  ```
485
311
  * As with the API code, edit `test_router_eigrp.rb` and change the placeholder names as shown:
486
312
 
@@ -504,9 +330,6 @@ cp docs/template-test_router.rb cisco_network_node_utils/tests/test_router_eig
504
330
  This is the completed `test_router_eigrp` minitest based on `template-test_router.rb`:
505
331
 
506
332
  ```ruby
507
- #
508
- # Minitest for RouterEigrp class
509
- #
510
333
  # Copyright (c) 2014-2015 Cisco and/or its affiliates.
511
334
  #
512
335
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -521,9 +344,10 @@ This is the completed `test_router_eigrp` minitest based on `template-test_route
521
344
  # See the License for the specific language governing permissions and
522
345
  # limitations under the License.
523
346
 
524
- require File.expand_path("../ciscotest", __FILE__)
525
- require File.expand_path("../../lib/cisco_node_utils/router_eigrp", __FILE__)
347
+ require_relative 'ciscotest'
348
+ require_relative '../lib/cisco_node_utils/router_eigrp'
526
349
 
350
+ # TestRouterEigrp - Minitest for RouterEigrp node utility class
527
351
  class TestRouterEigrp < CiscoTestCase
528
352
  def setup
529
353
  # setup runs at the beginning of each test
@@ -539,75 +363,75 @@ class TestRouterEigrp < CiscoTestCase
539
363
 
540
364
  def no_feature_eigrp
541
365
  # Turn the feature off for a clean test.
542
- @device.cmd("conf t ; no feature eigrp ; end")
543
- node.cache_flush()
366
+ config('no feature eigrp')
544
367
  end
545
368
 
546
369
  # TESTS
547
370
 
548
371
  def test_router_create_destroy_one
549
- id = "blue"
372
+ id = 'blue'
550
373
  rtr = RouterEigrp.new(id)
551
- s = @device.cmd("show runn | i 'router eigrp #{id}'")
552
- assert_match(s, /^router eigrp #{id}$/,
553
- "Error: failed to create router eigrp #{id}")
374
+ @default_show_command = "show runn | i 'router eigrp #{id}'")
375
+ assert_show_match(pattern: /^router eigrp #{id}$/,
376
+ msg: "failed to create router eigrp #{id}")
554
377
 
555
378
  rtr.destroy
556
- s = @device.cmd("show runn | i 'router eigrp #{id}'")
557
- refute_match(s, /^router eigrp #{id}$/,
558
- "Error: failed to destroy router eigrp #{id}")
379
+ refute_show_match(pattern: /^router eigrp #{id}$/,
380
+ msg: "failed to destroy router eigrp #{id}")
559
381
 
560
- s = @device.cmd("show runn | i 'feature eigrp'")
561
- refute_match(s, /^feature eigrp$/,
562
- "Error: failed to disable feature eigrp")
382
+ refute_show_match(command: "show runn | i 'feature eigrp'",
383
+ pattern: /^feature eigrp$/,
384
+ msg: "failed to disable feature eigrp")
563
385
  end
564
386
 
565
387
  def test_router_create_destroy_multiple
566
- id1 = "blue"
388
+ id1 = 'blue'
567
389
  rtr1 = RouterEigrp.new(id1)
568
- id2 = "red"
390
+ id2 = 'red'
569
391
  rtr2 = RouterEigrp.new(id2)
570
392
 
571
- s = @device.cmd("show runn | i 'router eigrp'")
572
- assert_match(s, /^router eigrp #{id1}$/)
573
- assert_match(s, /^router eigrp #{id2}$/)
393
+ @default_show_command = "show runn | i 'router eigrp'"
394
+
395
+ assert_show_match(pattern: /^router eigrp #{id1}$/,
396
+ msg: "failed to create router eigrp #{id1}")
397
+
398
+ assert_show_match(pattern: /^router eigrp #{id2}$/,
399
+ msg: "failed to create router eigrp #{id2}")
574
400
 
575
401
  rtr1.destroy
576
- s = @device.cmd("show runn | i 'router eigrp #{id1}'")
577
- refute_match(s, /^router eigrp #{id1}$/,
578
- "Error: failed to destroy router eigrp #{id1}")
402
+ refute_show_match(pattern: /^router eigrp #{id1}$/,
403
+ msg: "failed to destroy router eigrp #{id1}")
579
404
 
580
405
  rtr2.destroy
581
- s = @device.cmd("show runn | i 'router eigrp #{id2}'")
582
- refute_match(s, /^router eigrp #{id2}$/,
583
- "Error: failed to destroy router eigrp #{id2}")
406
+ refute_show_match(pattern: /^router eigrp #{id2}$/,
407
+ msg: "failed to destroy router eigrp #{id2}")
584
408
 
585
- s = @device.cmd("show runn | i 'feature eigrp'")
586
- refute_match(s, /^feature eigrp$/,
587
- "Error: failed to disable feature eigrp")
409
+ refute_show_match(command: "show runn | i 'feature eigrp'",
410
+ pattern: /^feature eigrp$/,
411
+ msg: "failed to disable feature eigrp")
588
412
  end
589
413
 
590
414
  def test_router_maximum_paths
591
- id = "blue"
415
+ id = 'blue'
592
416
  rtr = RouterEigrp.new(id)
593
- val = 5 # This value depends on property bounds
417
+ val = 5 # This value depends on property bounds
594
418
  rtr.maximum_paths = val
595
419
  assert_equal(rtr.maximum_paths, val, "maximum_paths is not #{val}")
596
420
 
597
421
  # Get default value from yaml
598
- val = node.config_get_default("eigrp", "maximum_paths")
422
+ val = node.config_get_default('eigrp', 'maximum_paths')
599
423
  rtr.maximum_paths = val
600
424
  assert_equal(rtr.maximum_paths, val, "maximum_paths is not #{val}")
601
425
  end
602
426
 
603
427
  def test_router_shutdown
604
- id = "blue"
428
+ id = 'blue'
605
429
  rtr = RouterEigrp.new(id)
606
430
  rtr.shutdown = true
607
- assert(rtr.shutdown, "shutdown state is not true")
431
+ assert(rtr.shutdown, 'shutdown state is not true')
608
432
 
609
433
  rtr.shutdown = false
610
- refute(rtr.shutdown, "shutdown state is not false")
434
+ refute(rtr.shutdown, 'shutdown state is not false')
611
435
  end
612
436
  end
613
437
  ```
@@ -615,22 +439,17 @@ end
615
439
  Now run the test:
616
440
 
617
441
  ```bash
618
- % ruby-1.9.3-p0 test_router_eigrp.rb -v -- 192.168.0.1 admin admin
442
+ % ruby test_router_eigrp.rb -v -- 192.168.0.1 admin admin
619
443
  Run options: -v -- --seed 56593
620
444
 
621
- # Running tests:
445
+ # Running:
622
446
 
623
- CiscoTestCase#test_placeholder =
624
- Ruby Version - 1.9.3
625
- Node in CiscoTestCase Class: 192.168.0.1
626
- Platform:
447
+ Node under test:
627
448
  - name - my_n3k
628
449
  - type - N3K-C3132Q-40GX
629
450
  - image -
630
451
 
631
452
  2.90 s = .
632
- TestCase#test_placeholder = 0.92 s = .
633
- TestRouterEigrp#test_placeholder = 0.97 s = .
634
453
  TestRouterEigrp#test_router_create_destroy_multiple = 10.77 s = .
635
454
  TestRouterEigrp#test_router_create_destroy_one = 6.14 s = .
636
455
  TestRouterEigrp#test_router_maximum_paths = 9.41 s = .
@@ -639,19 +458,50 @@ TestRouterEigrp#test_router_shutdown = 6.40 s = .
639
458
 
640
459
  Finished tests in 37.512356s, 0.1866 tests/s, 0.3199 assertions/s.
641
460
 
642
- 7 tests, 12 assertions, 0 failures, 0 errors, 0 skips
461
+ 5 tests, 12 assertions, 0 failures, 0 errors, 0 skips
643
462
  ```
644
463
 
645
- ### <a name="comp_lint">Step 4. rubocop / lint: router eigrp</a>
464
+ ### <a name="comp_lint">Step 4. rubocop: router eigrp</a>
646
465
 
647
- rubocop is a Ruby static analysis tool. Run rubocop with the --lint option to validate the new API:
466
+ rubocop is a Ruby static analysis tool. Run rubocop to validate the new code:
648
467
 
649
468
  ```bash
650
- % rubocop --lint router_eigrp.rb
651
- Inspecting 1 file
652
- .
469
+ % rubocop lib/cisco_node_utils/router_eigrp.rb tests/test_router_eigrp.rb
470
+ Inspecting 2 file
471
+ ..
472
+
473
+ 2 file inspected, no offenses detected
474
+ ```
475
+
476
+ ### <a name="comp_gem">Step 5. Build and Install the gem</a>
477
+
478
+ The final step is to build and install the gem that contains the new APIs.
479
+
480
+ Please note: `gem build` will only include files that are part of the repository. This means that new file `router_eigrp.rb` will be ignored by the build until it is added to the repo with `git add`:
653
481
 
654
- 1 file inspected, no offenses detected
482
+ ```bash
483
+ git add lib/cisco_node_utils/router_eigrp.rb
484
+ ```
485
+
486
+ From the root of the cisco-network-node-utils repository issue the following command.
487
+
488
+ ```bash
489
+ % gem build cisco_node_utils.gemspec
490
+ Successfully built RubyGem
491
+ Name: cisco_node_utils
492
+ Version: 1.0.1
493
+ File: cisco_node_utils-1.0.1.gem
494
+ ```
495
+
496
+ Copy the new gem to your NX-OS device and then install it.
497
+
498
+ ```bash
499
+ n9k#gem install --local /bootflash/cisco_node_utils-1.0.1.gem
500
+ Successfully installed cisco_node_utils-1.0.1
501
+ Parsing documentation for cisco_node_utils-1.0.1
502
+ Installing ri documentation for cisco_node_utils-1.0.1
503
+ Done installing documentation for cisco_node_utils after 2 seconds
504
+ 1 gem installed
655
505
  ```
656
506
 
657
507
  ## Conclusion