cisco_node_utils 0.9.0 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c811008424906279c5c89cb82653b6367c6ee8d8
4
- data.tar.gz: 7ffc3ebadb167bb07f35edc267dd44588b5c0d41
3
+ metadata.gz: 1c5b07b6e931c42203329e0218d507395d58bf0a
4
+ data.tar.gz: a52112854a11a4aa1053010a92edf7bbe2607c35
5
5
  SHA512:
6
- metadata.gz: f40b4f8cd2f4711511458b3a02b48e8a1488cdb02d2174bf9ad66a4febb78e5915317937b3ec701ebaa128edd50cf49216fa2d572e04c792bd81fa351b9bffca
7
- data.tar.gz: 5d4dd8cfdb95ccca0ab67e3fa18d20ce1bb336dc3a625e2d73c3cc1931df0192150ae0614f7b0ddba7165941416f831926e853ddaa0de9c6b79f46ffc2c205c5
6
+ metadata.gz: 2af0852642d24be931c1700b685d236a123717496c7526dc10753725c85a62f31981017b98ce82022e2117481f88273a989e078c83fd32f54e1b2c4d65b94d04
7
+ data.tar.gz: 866dd382c29fd0cecfb9162fae84980dd35138b926a2d72ee34caad204df691d7db0eec1056a9cb2f8eb2406b97643a67b1f17958627033c41c93d7e52ad9a71
data/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ Changelog
2
+ =========
3
+
4
+ 1.0.0
5
+ -----
6
+
7
+ * Improved logic in Vtp class to handle the presence or absence of
8
+ 'feature vtp' and 'vtp domain' configuration.
9
+ * Fixed missing `default_timer_throttle_*` APIs in RouterOspfVrf class.
10
+ * Fixed idempotency and area update issues in interface_ospf class.
11
+ * Updated CliError class definition to make it easier to troubleshoot such
12
+ errors when running Puppet modules that use this gem.
13
+ * Added dotted-decimal munging for the area getter in interface_ospf.
14
+ * Added n9000_sample*.rpm to /tests for use with minitests.
15
+ * Updated yum install method to include vrf, fixes minitest issue.
16
+ * Extended cisco_interface with the following attributes:
17
+ * encapsulation dot1q
18
+ * mtu
19
+ * switchport trunk allowed and native vlans
20
+ * vrf member
21
+ * Move misc READMEs into /docs
22
+
1
23
  0.9.0
2
24
  -----
3
25
 
data/CONTRIBUTING.md CHANGED
@@ -1,31 +1,33 @@
1
- # Contributing
2
-
3
- ## Submitting an Issue
4
-
5
- Issues are tracked on GitHub (TODO).
6
-
7
- ## Developing a new Feature Provider
8
-
9
- 1. Create a new class file in `lib/cisco_node_utils/`.
10
- 2. Write the class.
11
- 3. Add the class to `lib/cisco_node_utils.rb`.
12
- 4. Create a new minitest file in `tests/`.
13
- 5. Write the minitest. We recommend subclassing the provided `CiscoTestCase`
14
- class as it provides numerous helper methods.
15
- 6. Run the minitest. `ruby test_my_feature.rb -- <NX-OS node IP> <user> <pass>`
16
- 7. Once minitest is passing, add the test to `tests/test_all_cisco.rb`.
17
- 8. Run rubocop (`rake rubocop`) and fix any failures.
18
- 9. Proceed to submit a pull request as described below.
19
-
20
- ## Submitting a Pull Request
21
-
22
- 1. Fork the code (https://github.com/cisco/cisco_node_utils/fork) (TODO)
23
- 2. Create your feature branch (`git checkout -b my-new-feature`)
24
- 3. Write your code
25
- 4. Write minitest cases to cover your new code
26
- 5. Verify that minitest passes in full (`ruby tests/test_all_cisco.rb --
27
- n3k_test.mycompany.com username password`)
28
- 6. Verify that rubocop also passes (`rake rubocop`).
29
- 7. Commit your changes (`git commit -am 'Add some feature'`)
30
- 8. Push to the branch (`git push origin my-new-feature`)
31
- 9. Create a new Pull Request
1
+ # How to contribute
2
+ Cisco Network Elements support a rich set of features to make networks robust, efficient and secure. The GitHub project [cisco-network-puppet-module](https://github.com/cisco/cisco-network-puppet-module) defines a set of Puppet resource types and providers to manage the network element. Similarly, the GitHub project [cisco-network-chef-cookbook](https://github.com/cisco/cisco-network-chef-cookbook) defines a set of Chef resources and providers for network element management. The providers defined in these projects leverage a common set of Ruby API Objects defined in this project. This object set is expected to grow with contributions from Cisco, Cisco-Partners and third-party alike. Contributions to this project are welcome. To ensure code quality, contributors will be requested to follow few guidelines.
3
+
4
+ ## Getting Started
5
+
6
+ * Create a [GitHub account](https://github.com/signup/free)
7
+ * Create a [cisco.com](http://cisco.com) account if you need access to a Network Simulator to test your code.
8
+
9
+ ## Making Changes
10
+
11
+ * Fork the repository
12
+ * Pull a branch under the "develop" branch for your changes.
13
+ * Follow all guidelines documented in [README-creating-node_utils-APIs](#README-creating-node_utils-APIs.md)
14
+ * Make changes in your branch.
15
+ * Testing
16
+ * Create a minitest for new APIs or new functionality
17
+ * Run all the tests to ensure there was no collateral damage to existing code
18
+ * Committing
19
+ * Check for unnecessary whitespace with `git diff --check` before committing.
20
+ * Run `rubocop --lint` against all changed files. See [https://rubygems.org/gems/rubocop](https://rubygems.org/gems/rubocop)
21
+ * Make sure your commit messages clearly describe the problem you are trying to solve and the proposed solution.
22
+
23
+ ## Submitting Changes
24
+
25
+ * All contributions you submit to this project are voluntary and subject to the terms of the Apache 2.0 license
26
+ * Submit a pull request to the repository
27
+ * A core team consisting of Cisco and Cisco-Partner employees will looks at Pull Request and provide feedback.
28
+ * After feedback has been given we expect responses within two weeks. After two weeks we may close the pull request if it isn't showing any activity.
29
+
30
+ # Additional Resources
31
+
32
+ * [General GitHub documentation](http://help.github.com/)
33
+ * [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
data/SUPPORT.md ADDED
@@ -0,0 +1,3 @@
1
+ Cisco Node Utils GEM is supported by Cisco Partners and Cisco Systems.
2
+
3
+ Please report any issues with this module to [https://github.com/cisco/cisco-network-node-utils/issues](https://github.com/cisco/cisco-network-node-utils/issues)
@@ -26,5 +26,5 @@ Currently supports NX-OS nodes.
26
26
  spec.add_development_dependency 'bundler', '~> 1.7'
27
27
  spec.add_development_dependency 'rake', '~> 10.0'
28
28
  spec.add_development_dependency 'rubocop', '>= 0.32'
29
- spec.add_runtime_dependency 'cisco_nxapi', '>= 0.9'
29
+ spec.add_runtime_dependency 'cisco_nxapi', '~> 1.0'
30
30
  end
data/docs/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ # Rubocop configuration
2
+
3
+ inherit_from: ../.rubocop.yml
4
+
5
+ Style/ClassAndModuleCamelCase:
6
+ Enabled: false
7
+
8
+ Style/FileName:
9
+ Enabled: false
10
+
11
+ Metrics/LineLength:
12
+ Enabled: false
13
+
@@ -0,0 +1,659 @@
1
+ # How To Create New node_utils APIs
2
+
3
+ #### Table of Contents
4
+
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)
13
+ * [Step 1. YAML Definitions: router eigrp](#comp_yaml)
14
+ * [Step 2. Create the node_utils API: router eigrp](#comp_api)
15
+ * [Step 3. Create the Minitest: router eigrp](#comp_minitest)
16
+ * [Step 4. rubocop / lint: router eigrp](#comp_lint)
17
+
18
+ ## <a name="overview">Overview</a>
19
+
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).
21
+
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.
23
+
24
+ ![1](agent_files.png)
25
+
26
+ ## <a name="clone">Start here: Clone the Repo</a>
27
+
28
+ First install the code base. Clone the cisco_node_utils repo into a workspace:
29
+
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`:
80
+
81
+ ```bash
82
+ cp docs/template-feature.rb cisco_network_node_utils/bash_shell.rb
83
+ ```
84
+
85
+ * Edit `bash_shell.rb` and substitute the placeholder text as shown here:
86
+
87
+ ```bash
88
+ /X__CLASS_NAME__X/BashShell/
89
+
90
+ /X__RESOURCE_NAME__X/bash_shell/
91
+ ```
92
+
93
+ #### Example: bash_shell.rb API
94
+
95
+ This is the completed bash_shell API based on `template-feature.rb`:
96
+
97
+ ```ruby
98
+
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
110
+
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:
136
+
137
+ ```bash
138
+ cp docs/template-test_feature.rb cisco_network_node_utils/tests/test_bash_shell.rb
139
+ ```
140
+
141
+ * As with the API code, edit `test_bash_shell.rb` and change the placeholder names as shown:
142
+
143
+ ```bash
144
+ /X__CLASS_NAME__X/BashShell/
145
+
146
+ /X__RESOURCE_NAME__X/bash_shell/
147
+
148
+ /X__CLI_NAME__X/bash-shell/
149
+ ```
150
+
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
216
+
217
+ ```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
239
+ ```
240
+
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
+
243
+ ### <a name="lint">Step 3. rubocop / lint: feature bash-shell</a>
244
+
245
+ rubocop is a Ruby static analysis tool. Run rubocop with the --lint option to validate the new API:
246
+
247
+ ```bash
248
+ % rubocop --lint bash_shell.rb
249
+ Inspecting 1 file
250
+ .
251
+
252
+ 1 file inspected, no offenses detected
253
+ ```
254
+
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
+ `router eigrp` requires feature enablement and supports multiple eigrp instances. It also has multiple configuration levels for vrf and address-family.
259
+
260
+ For the purposes of this example we will only implement the following properties:
261
+
262
+ ```bash
263
+ [no] feature eigrp (boolean)
264
+ [no] router eigrp [name] (string)
265
+ maximum-paths [n] (integer)
266
+ [no] shutdown (boolean)
267
+
268
+ Example:
269
+ feature eigrp
270
+ router eigrp Blue
271
+ maximum-paths 5
272
+ shutdown
273
+ ```
274
+
275
+ ### <a name="comp_yaml">Step 1. YAML Definitions: router eigrp</a>
276
+
277
+ As with the earlier example, `router eigrp` will need YAML definitions in the common file:
278
+
279
+ `cisco_network_node_utils/lib/cisco_node_utils/command_reference_common.yaml`
280
+
281
+ 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
+
283
+ *Note: Eigrp also has vrf and address-family contexts. These contexts require additional coding and are beyond the scope of this document.*
284
+
285
+ #### Example: YAML Property Definitions for router eigrp
286
+
287
+ *Note: The basic token definitions for multi-level commands can become long and complicated. A better solution for these commands is to use a command_reference _template: definition to simplify the configuration. The example below will use the basic syntax; see the ospf definitions in the YAML file for an example of _template: usage.*
288
+
289
+ ```yaml
290
+ eigrp:
291
+ feature:
292
+ # feature eigrp must be enabled before configuring router eigrp
293
+ config_get: 'show running eigrp all'
294
+ config_get_token: '/^feature eigrp$/'
295
+ config_set: '<state> feature eigrp'
296
+
297
+ router:
298
+ # There can be multiple eigrp instances
299
+ config_get: 'show running eigrp all' # all eigrp-related configs
300
+ config_get_token: '/^router eigrp (\S+)$/' # Match instance name
301
+ config_set: '<state> router eigrp <name>' # config to add or remove
302
+
303
+ maximum_paths:
304
+ # This is an integer property
305
+ config_get: 'show running eigrp all'
306
+ config_get_token: ['/^router eigrp <name>$/', '/^maximum-paths (\d+)/']
307
+ config_set: ['router eigrp <name>', 'maximum-paths <val>']
308
+ default_value: 8
309
+
310
+ shutdown:
311
+ # This is a boolean property
312
+ config_get: 'show running eigrp all'
313
+ config_get_token: ['/^router eigrp <name>$/', '/^shutdown$/']
314
+ config_set: ['router eigrp <name>', '<state> shutdown']
315
+ default_value: false
316
+ ```
317
+
318
+ ### <a name="comp_api">Step 2. cisco_node_utils API: router eigrp</a>
319
+
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
+ * The `template-router.rb` file provides a basic router API that we will use as the basis for `router_eigrp.rb`:
327
+
328
+ ```bash
329
+ cp docs/template-router.rb cisco_network_node_utils/router_eigrp.rb
330
+ ```
331
+
332
+ * Our new `router_eigrp.rb` requires changes from the original template. Edit `router_eigrp.rb` and change the placeholder names as shown.
333
+
334
+ ```
335
+ /X__CLASS_NAME__X/RouterEigrp/
336
+
337
+ /X__RESOURCE_NAME__X/eigrp/
338
+
339
+ /X__PROPERTY_BOOL__X/shutdown/
340
+
341
+ /X__PROPERTY_INT__X/maximum_paths/
342
+ ```
343
+
344
+ *Note that this template only provides example property methods for a few properties. Copy the example methods for additional properties as needed.*
345
+
346
+ #### Example: router_eigrp.rb
347
+ This is the completed `router_eigrp` API based on `template-router.rb`:
348
+
349
+ ```ruby
350
+ #
351
+ # NXAPI implementation of RouterEigrp class
352
+ #
353
+ # Copyright (c) 2014-2015 Cisco and/or its affiliates.
354
+ #
355
+ # Licensed under the Apache License, Version 2.0 (the "License");
356
+ # you may not use this file except in compliance with the License.
357
+ # You may obtain a copy of the License at
358
+ #
359
+ # http://www.apache.org/licenses/LICENSE-2.0
360
+ #
361
+ # Unless required by applicable law or agreed to in writing, software
362
+ # distributed under the License is distributed on an "AS IS" BASIS,
363
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
364
+ # See the License for the specific language governing permissions and
365
+ # limitations under the License.
366
+
367
+ require File.join(File.dirname(__FILE__), 'node')
368
+
369
+ 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
383
+
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)
391
+ 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
+
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
408
+
409
+ def feature_enable
410
+ @@node.config_set('eigrp', 'feature', { :state => '' })
411
+ end
412
+
413
+ def feature_disable
414
+ @@node.config_set('eigrp', 'feature', { :state => 'no' })
415
+ end
416
+
417
+ # Enable feature and create router instance
418
+ def create
419
+ feature_enable unless feature_enabled
420
+ eigrp_router
421
+ end
422
+
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')
431
+ 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
+
437
+ def eigrp_router(state='')
438
+ @@node.config_set('eigrp', 'router', { :name => @name, :state => state })
439
+ end
440
+
441
+ # ----------
442
+ # PROPERTIES
443
+ # ----------
444
+
445
+ # Property methods for boolean property
446
+ def default_shutdown
447
+ @@node.config_get_default('eigrp', 'shutdown')
448
+ end
449
+
450
+ def shutdown
451
+ state = @@node.config_get('eigrp', 'shutdown', { :name => @name })
452
+ state ? true : false
453
+ end
454
+
455
+ def shutdown=(state)
456
+ state = (state ? '' : 'no')
457
+ @@node.config_set('eigrp', 'shutdown', { :name => @name, :state => state })
458
+ end
459
+
460
+ # Property methods for integer property
461
+ def default_maximum_paths
462
+ @@node.config_get_default('eigrp', 'maximum_paths')
463
+ end
464
+
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
469
+
470
+ def maximum_paths=(val)
471
+ @@node.config_set('eigrp', 'maximum_paths', { :name => @name, :val => val })
472
+ end
473
+
474
+ end
475
+ end
476
+ ```
477
+
478
+ ### <a name="comp_api">Step 3. Minitest: router eigrp</a>
479
+
480
+ * Use `template-test_router.rb` to build the minitest for `router_eigrp.rb`:
481
+
482
+ ```
483
+ cp docs/template-test_router.rb cisco_network_node_utils/tests/test_router_eigrp.rb
484
+ ```
485
+ * As with the API code, edit `test_router_eigrp.rb` and change the placeholder names as shown:
486
+
487
+ ```
488
+ /X__CLASS_NAME__X/RouterEigrp/
489
+
490
+ /X__RESOURCE_NAME__X/eigrp/
491
+
492
+ /X__PROPERTY_BOOL__X/shutdown/
493
+
494
+ /X__PROPERTY_INT__X/maximum_paths/
495
+ ```
496
+
497
+ * At a minimum, the tests should include coverage for:
498
+ * creating & destroying a single `router eigrp` instance
499
+ * creating & destroying multiple `router eigrp` instances
500
+ * feature disablement when removing last `router eigrp`
501
+ * testing each property state
502
+
503
+ #### Example: test_router_eigrp.rb
504
+ This is the completed `test_router_eigrp` minitest based on `template-test_router.rb`:
505
+
506
+ ```ruby
507
+ #
508
+ # Minitest for RouterEigrp class
509
+ #
510
+ # Copyright (c) 2014-2015 Cisco and/or its affiliates.
511
+ #
512
+ # Licensed under the Apache License, Version 2.0 (the "License");
513
+ # you may not use this file except in compliance with the License.
514
+ # You may obtain a copy of the License at
515
+ #
516
+ # http://www.apache.org/licenses/LICENSE-2.0
517
+ #
518
+ # Unless required by applicable law or agreed to in writing, software
519
+ # distributed under the License is distributed on an "AS IS" BASIS,
520
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
521
+ # See the License for the specific language governing permissions and
522
+ # limitations under the License.
523
+
524
+ require File.expand_path("../ciscotest", __FILE__)
525
+ require File.expand_path("../../lib/cisco_node_utils/router_eigrp", __FILE__)
526
+
527
+ class TestRouterEigrp < CiscoTestCase
528
+ def setup
529
+ # setup runs at the beginning of each test
530
+ super
531
+ no_feature_eigrp
532
+ end
533
+
534
+ def teardown
535
+ # teardown runs at the end of each test
536
+ no_feature_eigrp
537
+ super
538
+ end
539
+
540
+ def no_feature_eigrp
541
+ # Turn the feature off for a clean test.
542
+ @device.cmd("conf t ; no feature eigrp ; end")
543
+ node.cache_flush()
544
+ end
545
+
546
+ # TESTS
547
+
548
+ def test_router_create_destroy_one
549
+ id = "blue"
550
+ 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}")
554
+
555
+ 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}")
559
+
560
+ s = @device.cmd("show runn | i 'feature eigrp'")
561
+ refute_match(s, /^feature eigrp$/,
562
+ "Error: failed to disable feature eigrp")
563
+ end
564
+
565
+ def test_router_create_destroy_multiple
566
+ id1 = "blue"
567
+ rtr1 = RouterEigrp.new(id1)
568
+ id2 = "red"
569
+ rtr2 = RouterEigrp.new(id2)
570
+
571
+ s = @device.cmd("show runn | i 'router eigrp'")
572
+ assert_match(s, /^router eigrp #{id1}$/)
573
+ assert_match(s, /^router eigrp #{id2}$/)
574
+
575
+ 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}")
579
+
580
+ 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}")
584
+
585
+ s = @device.cmd("show runn | i 'feature eigrp'")
586
+ refute_match(s, /^feature eigrp$/,
587
+ "Error: failed to disable feature eigrp")
588
+ end
589
+
590
+ def test_router_maximum_paths
591
+ id = "blue"
592
+ rtr = RouterEigrp.new(id)
593
+ val = 5 # This value depends on property bounds
594
+ rtr.maximum_paths = val
595
+ assert_equal(rtr.maximum_paths, val, "maximum_paths is not #{val}")
596
+
597
+ # Get default value from yaml
598
+ val = node.config_get_default("eigrp", "maximum_paths")
599
+ rtr.maximum_paths = val
600
+ assert_equal(rtr.maximum_paths, val, "maximum_paths is not #{val}")
601
+ end
602
+
603
+ def test_router_shutdown
604
+ id = "blue"
605
+ rtr = RouterEigrp.new(id)
606
+ rtr.shutdown = true
607
+ assert(rtr.shutdown, "shutdown state is not true")
608
+
609
+ rtr.shutdown = false
610
+ refute(rtr.shutdown, "shutdown state is not false")
611
+ end
612
+ end
613
+ ```
614
+
615
+ Now run the test:
616
+
617
+ ```bash
618
+ % ruby-1.9.3-p0 test_router_eigrp.rb -v -- 192.168.0.1 admin admin
619
+ Run options: -v -- --seed 56593
620
+
621
+ # Running tests:
622
+
623
+ CiscoTestCase#test_placeholder =
624
+ Ruby Version - 1.9.3
625
+ Node in CiscoTestCase Class: 192.168.0.1
626
+ Platform:
627
+ - name - my_n3k
628
+ - type - N3K-C3132Q-40GX
629
+ - image -
630
+
631
+ 2.90 s = .
632
+ TestCase#test_placeholder = 0.92 s = .
633
+ TestRouterEigrp#test_placeholder = 0.97 s = .
634
+ TestRouterEigrp#test_router_create_destroy_multiple = 10.77 s = .
635
+ TestRouterEigrp#test_router_create_destroy_one = 6.14 s = .
636
+ TestRouterEigrp#test_router_maximum_paths = 9.41 s = .
637
+ TestRouterEigrp#test_router_shutdown = 6.40 s = .
638
+
639
+
640
+ Finished tests in 37.512356s, 0.1866 tests/s, 0.3199 assertions/s.
641
+
642
+ 7 tests, 12 assertions, 0 failures, 0 errors, 0 skips
643
+ ```
644
+
645
+ ### <a name="comp_lint">Step 4. rubocop / lint: router eigrp</a>
646
+
647
+ rubocop is a Ruby static analysis tool. Run rubocop with the --lint option to validate the new API:
648
+
649
+ ```bash
650
+ % rubocop --lint router_eigrp.rb
651
+ Inspecting 1 file
652
+ .
653
+
654
+ 1 file inspected, no offenses detected
655
+ ```
656
+
657
+ ## Conclusion
658
+
659
+ This was hopefully a good introduction to writing a Cisco node_utils API. At this point you could continue adding properties or try your hand at writing Puppet or Chef provider code to utilize your new API.