├── .gitignore
├── .pmtignore
├── .rubocop.yml
├── .simplecov
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Gemfile
├── LICENSE
├── README.md
├── Rakefile
├── SUPPORT.md
├── docs
├── README-agent-install.md
├── README-develop-beaker-scripts.md
├── README-test-execution.md
└── README-utilities.md
├── examples
├── beaker_test_template.rb
└── site.pp
├── files
└── models
│ └── defaults
│ ├── vrf-voip.json
│ ├── vrfs.json
│ └── vrfs.xml
├── lib
├── .rubucop.yml
├── facter
│ └── cisco_yang.rb
├── minitest
│ ├── environment_plugin.rb
│ └── log_level_plugin.rb
├── puppet
│ ├── feature
│ │ ├── json.rb
│ │ └── node_util.rb
│ ├── property
│ │ ├── yang_json.rb
│ │ ├── yang_netconf.rb
│ │ ├── yang_netconf_property.rb
│ │ └── yang_property.rb
│ ├── provider
│ │ ├── cisco_yang
│ │ │ └── cisco.rb
│ │ └── cisco_yang_netconf
│ │ │ └── cisco.rb
│ └── type
│ │ ├── cisco_yang.rb
│ │ └── cisco_yang_netconf.rb
└── util
│ ├── client.rb
│ ├── client
│ ├── client.rb
│ ├── grpc.rb
│ ├── grpc
│ │ ├── client.rb
│ │ ├── ems.proto
│ │ ├── ems.rb
│ │ └── ems_services.rb
│ ├── netconf.rb
│ ├── netconf
│ │ ├── client.rb
│ │ ├── netconf.rb
│ │ └── netconf_client.rb
│ └── utils.rb
│ ├── constants.rb
│ ├── environment.rb
│ ├── exceptions.rb
│ ├── logger.rb
│ ├── node.rb
│ ├── node_util.rb
│ ├── platform.rb
│ ├── show_running_yang.rb
│ ├── yang.rb
│ └── yang_accessor.rb
├── metadata.json
└── tests
├── .rubopcop.yml
├── beaker_tests
├── all
│ ├── cisco_yang
│ │ ├── model_tests
│ │ │ ├── test_cisco_ios_xr_infra_rsi_cfg_vrf_groups.rb
│ │ │ ├── test_cisco_ios_xr_infra_rsi_cfg_vrfs.rb
│ │ │ ├── test_cisco_ios_xr_ipv4_bgp_cfg_bgp.rb
│ │ │ └── yang
│ │ │ │ └── bgp.yang
│ │ ├── test_create_vrf.rb
│ │ ├── test_delete_properties.rb
│ │ ├── test_delete_vrf.rb
│ │ ├── test_file.rb
│ │ ├── test_merge_properties_vrf.rb
│ │ ├── test_merge_vrf.rb
│ │ ├── test_replace_properties_vrf.rb
│ │ ├── test_replace_srlg.rb
│ │ └── test_replace_vrf.rb
│ └── cisco_yang_netconf
│ │ ├── test_create_vrf.rb
│ │ ├── test_file_merge_vrf.rb
│ │ ├── test_file_replace_vrf.rb
│ │ ├── test_merge_properties_vrf.rb
│ │ ├── test_merge_vrf.rb
│ │ ├── test_replace_properties_vrf.rb
│ │ ├── test_replace_srlg.rb
│ │ └── test_replace_vrf.rb
└── lib
│ ├── utilitylib.rb
│ ├── yang_netconf_util.rb
│ └── yang_util.rb
├── init.pp
└── minitest
├── ciscotest.rb
├── platform_info.rb
├── test_grpc.rb
├── test_netconf.rb
├── test_netconf_yang.rb
├── test_netconf_yang_offline.rb
└── test_yang.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | .bundle/
2 | junit/
3 | log/
4 | pkg/
5 | Gemfile.lock
6 | *~
7 | *.swp
8 | tests/minitest/coverage/
9 | tests/minitest/run_grpc.sh
10 | tests/minitest/run_netconf.sh
11 | tests/beaker_tests/hosts.cfg
12 | hosts.cfg
13 |
--------------------------------------------------------------------------------
/.pmtignore:
--------------------------------------------------------------------------------
1 | .bundle/
2 | examples/
3 | junit/
4 | log/
5 | pkg/
6 | tests/
7 | Gemfile.lock
8 | *~
9 | *.swp
10 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | # Rubocop configuration
2 |
3 | AllCops:
4 | Exclude:
5 | - 'pkg/**/*'
6 | # Files we don't own
7 | - 'vendor/**/*'
8 | # Auto-generated files
9 | - 'lib/util/client/grpc/ems.rb'
10 | - 'lib/util/client/grpc/ems_services.rb'
11 |
12 | # Baseline code complexity metrics - we should try and reduce these over time
13 | Metrics/AbcSize:
14 | Max: 47
15 |
16 | Metrics/CyclomaticComplexity:
17 | Max: 18
18 |
19 | Metrics/ClassLength:
20 | Enabled: false
21 |
22 | Metrics/LineLength:
23 | Max: 167
24 |
25 | Metrics/MethodLength:
26 | Max: 75
27 |
28 | Metrics/ModuleLength:
29 | Enabled: false
30 |
31 | Metrics/PerceivedComplexity:
32 | Max: 20
33 |
34 | Metrics/ParameterLists:
35 | Max: 6
36 |
37 | # We like table alignment for readability
38 | Style/AlignHash:
39 | EnforcedHashRocketStyle: table
40 | EnforcedColonStyle: table
41 |
42 | # Template files have wildcard strings in class names
43 | Style/ClassAndModuleCamelCase:
44 | Exclude:
45 | - 'docs/*.rb'
46 |
47 | # Permit is_a? and kind_of? interchangeably
48 | Style/ClassCheck:
49 | Enabled: false
50 |
51 | # Template files have atypical file names on purpose
52 | Style/FileName:
53 | Exclude:
54 | - 'docs/*.rb'
55 |
56 | # As a team we like 'sprintf' rather than 'format'
57 | Style/FormatString:
58 | EnforcedStyle: sprintf
59 |
60 | # The Beaker installer script uses a number of global variables.
61 | # This is OK for now since it's a standalone script.
62 | Style/GlobalVars:
63 | Exclude:
64 | - 'utilities/installer/install_puppet.rb'
65 |
66 | # Mixed keys are ugly. Use one or the other as needed
67 | Style/HashSyntax:
68 | EnforcedStyle: ruby19_no_mixed_keys
69 |
70 | # Template files have wildcard strings in method names
71 | Style/MethodName:
72 | Exclude:
73 | - 'docs/*.rb'
74 |
75 | # To stop rubocop from complaining about overriding is_to_s
76 | Style/PredicateName:
77 | NameWhitelist:
78 | - is_a?
79 | - is_to_s
80 |
81 | # "def foo(bar=baz)" not "def foo(bar = baz)"
82 | Style/SpaceAroundEqualsInParameterDefault:
83 | EnforcedStyle: no_space
84 |
85 | # Template file has atypical variable name that rubocop doesn't like
86 | Style/SpaceAroundOperators:
87 | Exclude:
88 | - 'docs/template-__PROVIDER__-ensurabilitytest.rb'
89 |
90 | # Make it neater to extend multi-line arrays and hashes
91 | Style/TrailingComma:
92 | EnforcedStyleForMultiline: comma
93 |
94 | # Template files have atypical variable names on purpose
95 | Style/VariableName:
96 | Exclude:
97 | - 'docs/*.rb'
98 |
99 | Style/ClassAndModuleChildren:
100 | Enabled: false
101 |
--------------------------------------------------------------------------------
/.simplecov:
--------------------------------------------------------------------------------
1 | SimpleCov.root(File.expand_path('..', __FILE__))
2 | SimpleCov.coverage_dir(File.expand_path('../tests/minitest/coverage', __FILE__))
3 | SimpleCov.command_name("test:#{ENV['COV_TEST_SUITE']}")
4 | SimpleCov.merge_timeout(3600) # one hour
5 | SimpleCov.start do
6 | add_filter '/tests/'
7 | end
8 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | All notable changes to this project will be documented in this file.
3 | This project adheres to [Semantic Versioning](http://semver.org/).
4 |
5 | ## 1.0.2 - 2017-01-19
6 | ### Changed
7 | - Tweaked Puppet requirements in metadata.json
8 |
9 | ## 1.0.1 - 2017-01-18
10 | ### Changed
11 | - Added Puppet requirements to metadata.json
12 |
13 | ## 1.0.0 - 2017-01-17
14 | ### Added
15 | - Official release of the ciscoyang module, supporting configuration of IOS-XR through Cisco supported YANG data models in JSON/XML format.
16 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 | Cisco IOS XR Software is a modular and fully distributed network operating system for service provider networks. This project enables configuration and management of IOS XR by Puppet via YANG models. Contributions to this project are welcome. To ensure code quality, contributers will be requested to follow a few guidelines.
3 |
4 | ## Getting Started
5 |
6 | * Create a [GitHub account](https://github.com/signup/free)
7 | * A virtual XRv 9000 may be helpful for development and testing.
8 |
9 | ## Making Changes
10 |
11 | * Fork the repository
12 | * Pull a branch under the "develop" branch for your changes.
13 | * Make changes in your branch.
14 | * Testing
15 | * Add/update minitest test cases to validate your changes (under `tests/minitest`).
16 | * Add/update beaker test cases to validate your changes (under `tests/beaker`).
17 | * Run all the tests to ensure there was no collateral damage to existing code.
18 | * Check for unnecessary whitespace with `git diff --check`
19 | * Run `rubocop --lint` against all changed files. See [https://rubygems.org/gems/rubocop](https://rubygems.org/gems/rubocop)
20 | * Ensure that your commit messages clearly describe the problem you are trying to solve and the proposed solution.
21 |
22 | ## Submitting Changes
23 |
24 | * All contributions you submit to this project are voluntary and subject to the terms of the Apache 2.0 license.
25 | * Submit a pull request for commit approval to the "develop" branch.
26 | * A core team consisting of Cisco and Puppet Labs employees will looks at Pull Requests and provide feedback.
27 | * 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.
28 | * All code commits must be associated with your github account and email address. Before committing any code use the following commands to update your workspace with your credentials:
29 |
30 | ```bash
31 | git config --global user.name "John Doe"
32 | git config --global user.email johndoe@example.com
33 | ```
34 |
35 | # Additional Resources
36 |
37 | * [General GitHub documentation](http://help.github.com/)
38 | * [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
39 | * \#puppet-dev IRC channel on freenode.org ([Archive](https://botbot.me/freenode/puppet-dev/))
40 | * [puppet-dev mailing list](https://groups.google.com/forum/#!forum/puppet-dev)
41 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | source ENV['GEM_SOURCE'] || 'https://rubygems.org'
15 |
16 | puppet_version = ENV['PUPPET_VERSION'] || '>= 4.0'
17 | gem 'puppet', puppet_version
18 |
19 | beaker_version = ENV['BEAKER_VERSION'] || '>= 2.38.1'
20 | gem 'beaker', beaker_version
21 |
22 | facter_version = ENV['FACTER_VERSION'] || '>= 1.7.0'
23 | gem 'facter', facter_version
24 |
25 | gem 'puppetlabs_spec_helper', '>= 0.8.2'
26 | gem 'puppet-lint', '>= 1.0.0'
27 | gem 'rubocop', '= 0.35.1', require: false
28 | gem 'rake', '>= 10.5.0', require: false
29 | gem 'metadata-json-lint'
30 | gem 'grpc', '~> 0.15.0'
31 | gem 'net-telnet', '>= 0.1.1'
32 | gem 'simplecov', require: false, group: test
33 |
34 | # vim:ft=ruby
35 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | require 'rubocop/rake_task'
16 | require 'rake/testtask'
17 |
18 | task default: %w(rubocop test)
19 |
20 | RuboCop::RakeTask.new
21 |
22 | Rake::TestTask.new do |t|
23 | t.test_files = FileList['tests/minitest/test_*.rb']
24 | t.warning = true
25 | t.verbose = true
26 | t.options = '-v'
27 | end
28 |
--------------------------------------------------------------------------------
/SUPPORT.md:
--------------------------------------------------------------------------------
1 | Supported versions of this Puppet Module are available at [Puppet Forge](http://forge.puppetlabs.com/puppetlabs/ciscoyang)
2 |
3 | Please report any issues with this Puppet Module to [https://tickets.puppetlabs.com/browse/MODULES/](https://tickets.puppetlabs.com/browse/MODULES/)
4 |
--------------------------------------------------------------------------------
/docs/README-agent-install.md:
--------------------------------------------------------------------------------
1 | # Puppet Agent Installation & Setup
2 |
3 | #### Table of Contents
4 |
5 | 1. [Overview](#overview)
6 | 1. [Pre-Install Tasks](#pre-install)
7 | 1. [Puppet Agent Environment: bash-shell](#env-bs)
8 | 1. [Puppet Agent Installation, Configuration and Usage](#agent-config)
9 | 1. [References](#references)
10 |
11 | ## Overview
12 |
13 | This document describes Puppet agent installation and setup on Cisco IOS XR devices
14 |
15 | ## Pre-Install Tasks
16 |
17 | #### Platform and Software Minimum Requirements
18 |
19 | The Cisco IOS XR Puppet implementation requires open source Puppet version 4.3.2 or Puppet Enterprise 2015.3.2. Currently, Cisco IOS XRv9k version 6.1.1 and later are supported.
20 |
21 | #### Set Up the Network
22 |
23 | Ensure that you have network connectivity prior to Puppet installation. Some basic CLI configuration may be necessary.
24 |
25 | **Example:** Connectivity via GigabitEthernet interface - IOS XR
26 |
27 | See also the [Cisco IOS XR Application Hosting Configuration Guide](http://www.cisco.com/c/en/us/td/docs/iosxr/AppHosting/App-Hosting-Config-Guide.html)
28 |
29 | ~~~
30 | config
31 | !
32 | hostname xrv9k
33 | domain name mycompany.com
34 | !
35 | interface GigabitEthernet0/0/0/0
36 | ipv4 address 10.0.0.98 255.255.255.0
37 | no shutdown
38 | !
39 | router static
40 | address-family ipv4 unicast
41 | 0.0.0.0/0 GigabitEthernet0/0/0/0 10.0.0.1
42 | !
43 | tpa
44 | address-family ipv4
45 | update-source GigabitEthernet0/0/0/0
46 | !
47 | commit
48 | end
49 | ~~~
50 |
51 | #### Enable gRPC server
52 |
53 | ~~~
54 | config
55 | !
56 | grpc
57 | port 57799 # optional - default is 57400
58 | !
59 | commit
60 | end
61 | ~~~
62 |
63 | For more options, refer to the gRPC configuration guide for your specific version of XR.
64 |
65 | #### Enable Netconf SSH server
66 | ~~~
67 | config
68 | !
69 | ssh server v2
70 | ssh server netconf vrf default
71 | ! next line might be needed if you receive connection reset errors
72 | ssh server rate-limit 120
73 | netconf-yang agent
74 | ssh
75 | !
76 | commit
77 | end
78 | ~~~
79 |
80 | For more options, refer to the NETCONF configuration guide for your specific version of XR.
81 |
82 | After applying the correct config, you need to validate that there is an RSA key generated
83 | ~~~
84 | show crypto key mypubkey rsa
85 | ~~~
86 | If the above show command indicates that there is no RSA key, generate one. Use 2048 bits when prompted.
87 | ~~~
88 | crypto key generate rsa
89 | ~~~
90 |
91 | You should now be able to connect to the Netconf service via an external device, using a management interface address.
92 |
93 | #### Enable access to Netconf inside the third-party namespace
94 | To enable access to Netconf from the puppet module, you will need to enable a few loopback interfaces.
95 | Create loopback 0 and loopback 1. Give them both addresses, and use the address for loopback 1 when using netconf from inside the third-party namespace.
96 | ~~~
97 | config
98 | !
99 | interface loopback 0
100 | ipv4 address 1.1.1.1 255.255.255.255
101 | no shutdown
102 | !
103 | interface loopback 1
104 | ipv4 address 10.10.10.10 255.255.255.255
105 | no shutdown
106 | !
107 | end
108 | ~~~
109 |
110 | ## Puppet Agent Environment: bash-shell
111 |
112 | **Example:**
113 |
114 | ~~~bash
115 | xrv9k# run bash
116 | bash-4.3# ip netns exec global-vrf bash
117 | ~~~
118 |
119 | Set up DNS configuration:
120 |
121 | ~~~
122 | cat >> /etc/resolv.conf << EOF
123 | nameserver 10.0.0.202
124 | domain mycompany.com
125 | search mycompany.com
126 | EOF
127 | ~~~
128 |
129 | ## Puppet Agent Installation, Configuration, and Usage
130 |
131 | #### Install Puppet Agent
132 |
133 | If needed, configure a proxy server to gain network access to `yum.puppetlabs.com`:
134 |
135 | ~~~bash
136 | export http_proxy=http://proxy.yourdomain.com:
137 | export https_proxy=https://proxy.yourdomain.com:
138 | ~~~
139 |
140 | Import the Puppet GPG keys.
141 |
142 | ~~~
143 | rpm --import http://yum.puppetlabs.com/RPM-GPG-KEY-puppetlabs
144 | rpm --import http://yum.puppetlabs.com/RPM-GPG-KEY-reductive
145 | ~~~
146 |
147 | The recommended Puppet RPM for IOS XR is [http://yum.puppetlabs.com/puppetlabs-release-pc1-cisco-wrlinux-7.noarch.rpm](http://yum.puppetlabs.com/puppetlabs-release-pc1-cisco-wrlinux-7.noarch.rpm).
148 |
149 | Using the appropriate RPM, do:
150 |
151 | ~~~bash
152 | yum install http://yum.puppetlabs.com/puppetlabs-release-pc1-cisco-wrlinux-7.noarch.rpm
153 | yum install puppet
154 | ~~~
155 |
156 | *Note that in order to perform the above yum RPM install, you must have at least one yum repo configured.*
157 |
158 | Update PATH var:
159 |
160 | ~~~bash
161 | export PATH=/opt/puppetlabs/puppet/bin:/opt/puppetlabs/puppet/lib:$PATH
162 | ~~~
163 |
164 | #### Edit the Puppet config file:
165 |
166 | **/etc/puppetlabs/puppet/puppet.conf**
167 |
168 | This file can be used to override the default Puppet settings. At a minimum, the following settings should be used:
169 |
170 | ~~~bash
171 | [main]
172 | server = mypuppetmaster.mycompany.com
173 |
174 | [agent]
175 | pluginsync = true
176 | ignorecache = true
177 | ~~~
178 |
179 | See the following references for more puppet.conf settings:
180 |
181 | * https://docs.puppetlabs.com/puppet/latest/reference/config_important_settings.html
182 | * https://docs.puppetlabs.com/puppet/latest/reference/config_about_settings.html
183 | * https://docs.puppetlabs.com/puppet/latest/reference/config_file_main.html
184 | * https://docs.puppetlabs.com/references/latest/configuration.html
185 |
186 | #### Edit the module config file:
187 |
188 | The `ciscoyang` puppet module requires configuration through a yaml file. Two configuration file locations are supported:
189 |
190 | * `/etc/cisco_yang.yaml` (system and/or root user configuration)
191 | * `~/cisco_yang.yaml` (per-user configuration)
192 |
193 | If both files exist and are readable, configuration in the user-specific file will take precedence over the system configuration.
194 |
195 | This file specifies the host, port, username, and/or password to be used to connect to gRPC and/or NETCONF. Here is an example configuration file:
196 |
197 | ~~~bash
198 | grpc:
199 | host: 127.0.0.1
200 | port: 57400
201 | username: admin
202 | password: admin
203 |
204 | netconf:
205 | host: 10.10.10.10
206 | username: admin
207 | password: admin
208 | ~~~
209 |
210 | The `cisco_yang` puppet type uses the `grpc` configuration options and the `cisco_yang_netconf` type uses the `netconf` configuration options. While the gRPC host address can be the standard loopback address (127.0.0.1), the NETCONF host address must be the `loopback 1` address that you configured earlier.
211 |
212 | *For security purposes, it is highly recommended that access to this file be restricted to only the owning user (`chmod 0600`).*
213 |
214 | #### Install the grpc gem
215 |
216 | ~~~bash
217 | gem install grpc
218 | ~~~
219 |
220 | *grpc gem version 0.15.0 or higher is required.*
221 |
222 | #### Run the Puppet Agent
223 |
224 | ~~~bash
225 | puppet agent -t
226 | ~~~
227 |
228 | #### Service Management
229 |
230 | It may be desirable to set up automatic restart of the Puppet agent in the event of a system reset.
231 |
232 | #### Optional: bash-shell / init.d
233 |
234 | The `bash-shell` environment uses **init.d** for service management.
235 | The Puppet agent provides a generic init.d script when installed, but a slight
236 | modification is needed to ensure that Puppet runs in the correct namespace:
237 |
238 | ~~~diff
239 | --- /etc/init.d/puppet.old
240 | +++ /etc/init.d/puppet
241 | @@ -38,7 +38,7 @@
242 |
243 | start() {
244 | echo -n $"Starting puppet agent: "
245 | - daemon $daemonopts $puppetd ${PUPPET_OPTS} ${PUPPET_EXTRA_OPTS}
246 | + daemon $daemonopts ip netns exec global-vrf $puppetd ${PUPPET_OPTS} ${PUPPET_EXTRA_OPTS}
247 | RETVAL=$?
248 | echo
249 | [ $RETVAL = 0 ] && touch ${lockfile}
250 | ~~~
251 |
252 | Next, enable the puppet service to be automatically started at boot time, and optionally start it now:
253 |
254 | ~~~bash
255 | chkconfig --add puppet
256 | chkconfig --level 345 puppet on
257 |
258 | service puppet start
259 | ~~~
260 |
261 | ## References
262 |
263 | [Cisco IOS XR Application Hosting Configuration Guide](http://www.cisco.com/c/en/us/td/docs/iosxr/AppHosting/App-Hosting-Config-Guide.html)
264 |
265 | [Cisco IOS XR Data Models Configuration Guide](http://www.cisco.com/c/en/us/td/docs/iosxr/ncs5500/DataModels/b-Datamodels-cg-ncs5500/b-Datamodels-cg-ncs5500_chapter_010.html#concept_700172ED7CF44313B0D7E521B2983F32) - gRPC Server Documentation
266 |
267 |
268 | ----
269 | ~~~
270 | Copyright (c) 2016 Cisco and/or its affiliates.
271 |
272 | Licensed under the Apache License, Version 2.0 (the "License");
273 | you may not use this file except in compliance with the License.
274 | You may obtain a copy of the License at
275 |
276 | http://www.apache.org/licenses/LICENSE-2.0
277 |
278 | Unless required by applicable law or agreed to in writing, software
279 | distributed under the License is distributed on an "AS IS" BASIS,
280 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
281 | See the License for the specific language governing permissions and
282 | limitations under the License.
283 | ~~~
284 |
--------------------------------------------------------------------------------
/docs/README-develop-beaker-scripts.md:
--------------------------------------------------------------------------------
1 | # How To Create and Run Beaker Test Cases
2 |
3 | #### Table of Contents
4 |
5 | * [Overview](#overview)
6 | * [Prerequisites](#prereqs)
7 | * [Basic Example: new YANG model test](#new-yang-model)
8 | * [Completed Example](#completed-example)
9 | * [Reading YANG source from file](#yang-from-file)
10 | * [Run the test script](#run-test)
11 |
12 | ## Overview
13 |
14 | This document describes the process for writing [Beaker](https://github.com/puppetlabs/beaker/blob/master/README.md) Test Cases for the `ciscoyang` Puppet module.
15 |
16 | ## Prerequisites
17 |
18 | Refer to [README-test-execution.md](README-test-execution.md) for required setup steps for Beaker and the node(s) to be tested.
19 |
20 | ## Basic Example: new YANG model test
21 |
22 | This example will demonstrate how to add a new Beaker test for a specific YANG model and container. We've chosen the Cisco-IOS-XR-ipv4-bgp-cfg:bgp model:container for this example.
23 |
24 | * The `./examples/beaker_test_template.rb` file provides a template for simple Beaker tests; copy it into the proper test directory. It is recommended that you create a separate test file for each YANG model/container combination that you wish to test. Name your Beaker test file as `test_`, plus the YANG model filename, plus the container (all lower-case with underscores in place of other symbols). Here, the YANG model being tested is named `Cisco-IOS-XR-ipv4-bgp-cfg.yang` and the container we are testing is the `bgp` container, so the test file will be named `test_cisco_ios_xr_ipv4_bgp_cfg_bgp.rb`.
25 |
26 | ```bash
27 | cp examples/beaker_test_template.rb tests/beaker_tests/all/cisco_yang/model_tests/test_cisco_ios_xr_ipv4_bgp_cfg_bgp.rb
28 | ```
29 |
30 | * Our new `test_cisco_ios_xr_ipv4_bgp_cfg_bgp.rb` requires changes from the original template. Edit `test_cisco_ios_xr_ipv4_bgp_cfg_bgp.rb` and make any necessary changes. In particular, you will want to:
31 |
32 | 1. Update the test at line 33:
33 | * Change the test ID
34 | * Change the test description
35 | * Change the title to the target container in the YANG document
36 | * Change the source to the desired YANG configuration to test
37 |
38 | 2. Update the test ID at line 49
39 |
40 | ## Completed Example
41 |
42 | This is the completed `test_cisco_ios_xr_ipv4_bgp_cfg_bgp.rb` test file based on `template-beaker_test_template.rb`:
43 |
44 | ~~~ruby
45 | ###############################################################################
46 | # Copyright (c) 2016 Cisco and/or its affiliates.
47 | #
48 | # Licensed under the Apache License, Version 2.0 (the "License");
49 | # you may not use this file except in compliance with the License.
50 | # You may obtain a copy of the License at
51 | #
52 | # http://www.apache.org/licenses/LICENSE-2.0
53 | #
54 | # Unless required by applicable law or agreed to in writing, software
55 | # distributed under the License is distributed on an "AS IS" BASIS,
56 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
57 | # See the License for the specific language governing permissions and
58 | # limitations under the License.
59 | ###############################################################################
60 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
61 | require File.expand_path('../../util.rb', __FILE__)
62 |
63 | # Test hash top-level keys
64 | tests = {
65 | master: master,
66 | agent: agent,
67 | resource_name: 'cisco_yang',
68 | os: 'ios_xr',
69 | os_version: '6.1.1',
70 | }
71 |
72 | # skip entire file if os, version, etc. don't match
73 | skip_unless_supported(tests)
74 |
75 | # Define a test for the bgp YANG container.
76 | tests[:bgp] = {
77 | desc: 'Configure BGP',
78 | title: '{"Cisco-IOS-XR-ipv4-bgp-cfg:bgp": [null]}',
79 | manifest_props: {
80 | source: '{
81 | "Cisco-IOS-XR-ipv4-bgp-cfg:bgp":{
82 | "instance":[
83 | {
84 | "instance-name":"default",
85 | "instance-as":[
86 | {
87 | "as":0,
88 | "four-byte-as":[
89 | {
90 | "as":55,
91 | "bgp-running":[null],
92 | "default-vrf":{
93 | "global":{
94 | "nsr":false,
95 | "global-timers":{
96 | "keepalive":60,
97 | "hold-time":120
98 | },
99 | "enforce-ibgp-out-policy":[null],
100 | "global-afs":{
101 | "global-af":[
102 | {
103 | "af-name":"ipv4-multicast",
104 | "enable":[null],
105 | "update-limit-address-family":256,
106 | "ebgp":{
107 | "paths-value":32,
108 | "unequal-cost":false,
109 | "selective":false,
110 | "order-by-igp-metric":false
111 | }
112 | }
113 | ]
114 | }
115 | },
116 | "bgp-entity":{
117 | "neighbors":{
118 | "neighbor":[
119 | {
120 | "neighbor-address":"5.5.5.5",
121 | "remote-as":{
122 | "as-xx":0,
123 | "as-yy":12
124 | },
125 | "bfd-enable-modes":"default",
126 | "ebgp-multihop":{
127 | "max-hop-count":10,
128 | "mpls-deactivation":false
129 | },
130 | "description":"Neighbor A",
131 | "msg-log-out":{
132 | "msg-buf-count":10
133 | }
134 | },
135 | {
136 | "neighbor-address":"6.6.6.6",
137 | "remote-as":{
138 | "as-xx":0,
139 | "as-yy":13
140 | },
141 | "description":"Neighbor B"
142 | }
143 | ]
144 | }
145 | }
146 | }
147 | }
148 | ]
149 | }
150 | ]
151 | }
152 | ]
153 | }
154 | }',
155 | mode: 'replace',
156 | },
157 | }
158 |
159 | #################################################################
160 | # Execute the test
161 | #################################################################
162 |
163 | test_name 'Model Test' do
164 | # a simple run with pre/post clean
165 | # (reference our test above using the key)
166 | test_harness_run_clean(tests, :bgp)
167 | end
168 |
169 | # report on skipped tests
170 | skipped_tests_summary(tests)
171 | ~~~
172 |
173 | ### Reading YANG source from file
174 |
175 | As an alternative to putting the YANG source directly in the test file, you can read it from an external file like this:
176 |
177 | ~~~ruby
178 | :
179 |
180 | # Define a test for the bgp YANG container.
181 | tests[:bgp] = {
182 | desc: 'Configure BGP',
183 | title: '{"Cisco-IOS-XR-ipv4-bgp-cfg:bgp": [null]}',
184 | manifest_props: {
185 | source: File.read(File.expand_path('../yang/bgp.yang', __FILE__)),
186 | mode: 'replace',
187 | },
188 | }
189 |
190 | :
191 | ~~~
192 |
193 | **Note: You can find some example model test scripts in the `tests/beaker_tests/all/cisco_yang/model_tests` directory.**
194 |
195 | ### Run the test script
196 |
197 | Refer to [README-test-execution.md](README-test-execution.md#beaker) for information on running Beaker tests. From the `tests/beaker_tests/all` directory:
198 |
199 | ```bash
200 | beaker --hosts hosts.cfg --test cisco_yang/model_tests/test_cisco_ios_xr_ipv4_bgp_cfg_bgp.rb
201 | ```
202 |
--------------------------------------------------------------------------------
/docs/README-test-execution.md:
--------------------------------------------------------------------------------
1 | # Executing the Tests Provided for This Module
2 |
3 | ### Table of Contents
4 |
5 | * [Overview](#overview)
6 | * [Minitest Tests](#minitest)
7 | * [Edit or create a minitest config file](#minitest-config)
8 | * [Running a single minitest test](#minitest-single-test)
9 | * [Running a single testcase](#minitest-single-testcase)
10 | * [Running all minitest tests](#minitest-all)
11 | * [Beaker Tests](#beaker)
12 | * [Prerequisites](#beaker-prereqs)
13 | * [Beaker Configuration](#beaker-config)
14 | * [Running a single test](#beaker-single-test)
15 | * [Running all Beaker tests](#beaker-all)
16 |
17 | ## Overview
18 |
19 | This document describes the process for executing tests provided for the code in this Puppet module. The instructions below assume you have cloned this repository to a "test driver" workstation and will reference subdirectories under the root `cisco-yang-puppet-module` directory:
20 |
21 | ~~~
22 | $ git clone https://github.com/cisco/cisco-yang-puppet-module.git
23 | $ cd cisco-network-yang-module/tests
24 | ~~~
25 |
26 | ## Minitest Tests
27 |
28 | The test files located in the `tests/minitest` directory use [minitest](https://github.com/seattlerb/minitest/) as their test framework. In addition to the test driver workstation, these tests generally require a Cisco router, switch, or virtual machine to test against. The executable minitest files are all named `test_*.rb`.
29 |
30 | ### Edit or create a minitest config file
31 |
32 | You must create a `cisco_yang.yaml` file on the driver workstation to specify the XR node under test. Two configuration file locations are supported:
33 |
34 | * `/etc/cisco_yang.yaml` (system and/or root user configuration)
35 | * `~/cisco_yang.yaml` (per-user configuration)
36 |
37 | If both files exist and are readable, configuration in the user-specific file will take precedence over the system configuration.
38 |
39 | This file specifies the host, port, username, and/or password to be used to connect to gRPC and/or NETCONF on the XR node. Here is an example configuration file:
40 |
41 | ~~~bash
42 | grpc:
43 | host: 192.168.1.65
44 | port: 57400
45 | username: admin
46 | password: admin
47 |
48 | netconf:
49 | host: 192.168.1.65
50 | username: admin
51 | password: admin
52 | ~~~
53 |
54 | Each minitest file/class uses either the `grpc` client options or the `netconf` options (or neither, if the file contains only offline tests).
55 |
56 | ### Running a single minitest test
57 |
58 | You can execute a single test file by name:
59 |
60 | ```bash
61 | $ ruby tests/test_yang.rb
62 | $ ruby tests/test_netconf_yang.rb -v
63 | ```
64 |
65 | ### Running a single testcase
66 |
67 | You can execute a single testcase in a file using the '-n' or '--name' option:
68 |
69 | ```bash
70 | $ ruby tests/test_yang.rb -n test_merge
71 | ```
72 |
73 | ### Running all minitest tests
74 |
75 |
76 | Using rake, you can execute all tests:
77 |
78 | ```bash
79 | $ rake test
80 | ```
81 |
82 |
83 | ## Beaker Tests
84 |
85 | The test files located in the `tests/beaker_tests/all` directory use [Beaker](https://github.com/puppetlabs/beaker) as their test framework. In addition to the test driver workstation, these tests require a Puppet Master workstation and an XR Puppet Agent device. The executable Beaker Ruby files are in subdirectories and are named `test_*.rb`.
86 |
87 | ### Prerequisites
88 |
89 | * [Install Beaker](https://github.com/puppetlabs/beaker/wiki/Beaker-Installation) (release 2.38.1 or later) on the driver workstation.
90 | * [Prepare the Puppet Master](../README.md#puppet-master-setup)
91 | * [Prepare the XR Puppet Agent](README-agent-install.md)
92 |
93 | ### Beaker Configuration
94 |
95 | Under the `tests/beaker_tests/all` directory, create file named `hosts.cfg` and add the following content:
96 |
97 | *Replace the `< >` markers with specific information.*
98 |
99 | ```bash
100 | HOSTS:
101 | :
102 | roles:
103 | - agent
104 | platform: cisco_ios_xr-6-x86_64
105 | ip:
106 | ssh:
107 | auth_methods: ["password"]
108 | # SSHd for third-party network namespace (TPNNS) uses port 57722
109 | port: 57722
110 | user:
111 | password:
112 |
113 |
114 | #:
115 | # <...>
116 |
117 | #:
118 | # <...>
119 |
120 | :
121 | # Note: Only one master configuration block allowed
122 | roles:
123 | - master
124 | platform:
125 | ip:
126 | ssh:
127 | # Root user/password must be configured for master.
128 | auth_methods: ["password"]
129 | user: root
130 | password:
131 | ```
132 |
133 | Here is a sample `hosts.cfg` file where the `< >` markers have been replaced with actual data.
134 |
135 | ```bash
136 | HOSTS:
137 | xr-agent:
138 | roles:
139 | - agent
140 | platform: cisco_ios_xr-6-x86_64
141 | ip: xr-agent.domain.com
142 | ssh:
143 | auth_methods: ["password"]
144 | port: 57722
145 | user: admin
146 | password: adminpassword
147 |
148 | puppetmaster1:
149 | roles:
150 | - master
151 | platform: ubuntu-1404-x86_64
152 | ip: puppetmaster1.domain.com
153 | ssh:
154 | auth_methods: ["password"]
155 | user: root
156 | password: rootpassword
157 | ```
158 |
159 | ### Running a single test
160 |
161 | To run a single beaker test from the `tests/beaker_tests/all` directory, use the following command:
162 |
163 | ```bash
164 | beaker --hosts hosts.cfg --test all/cisco_yang/test_create_vrf.rb
165 | ```
166 |
167 | **NOTE:** This runs a `cisco_yang` test to create a vrf, but any other tests under the `tests/beaker_tests/all` directory can be run in the same manner.
168 |
169 | ### Running all Beaker tests
170 |
171 | To run all the Beaker tests under the `tests/beaker_tests/all` directory, use the `--test` parameter
172 | and specify the `all` directory:
173 |
174 | ```bash
175 | beaker --hosts hosts.cfg --test all
176 | ```
177 |
178 | You can also specify a subdirectory of Beaker tests:
179 |
180 | ```bash
181 | beaker --hosts hosts.cfg --test all/cisco_yang
182 | beaker --hosts hosts.cfg --test all/cisco_yang_netconf
183 | ```
184 |
--------------------------------------------------------------------------------
/docs/README-utilities.md:
--------------------------------------------------------------------------------
1 | # Utilities included in this module
2 |
3 | ## show_running_yang.rb
4 |
5 | This is a Ruby utility to output the current state of an XR configuration. In order
6 | to run, this utility needs access to one or more *.yang files (found in the
7 | `/pkg/yang` directory on the XR device, as well as from other sources). Usually, this
8 | would be run from the bash-shell on the XR device, but can be run remotely if
9 | applicable .yang files are available. Client connection information (host, username, etc.)
10 | is read from the [module configuration file](README-agent-install.md#module-config).
11 |
12 | Running `puppet agent -t` on XR caches the module source on the client, usually under the
13 | `/opt/puppetlabs/puppet/cache` directory. The `show_running_yang.rb` Ruby file will be
14 | in the `/opt/puppetlabs/puppet/cache/lib/util` directory, so can be executed using the
15 | following command:
16 |
17 | ```bash
18 | [xrv9k:~]$ ruby /opt/puppetlabs/puppet/cache/lib/util/show_running_yang.rb --help
19 |
20 | Usage: ruby [path]show_running_yang.rb [options] [file_or_directory_path]
21 | -m, --manifest Output config in a form suitable for inclusion in a Puppet manifest
22 | -o, --oper Retrieve operational data instead of configuration (experimental; use at own risk)
23 | -c, --client CLIENT The client to use to connect.
24 | grpc|netconf (defaults to grpc
25 | -d, --debug Enable debug-level logging
26 | -v, --verbose Enable verbose messages
27 | -h, --help Print this help
28 | ```
29 |
30 | If you find yourself running the utility often, consider creating an alias shortcut:
31 |
32 |
33 | ```bash
34 | [xrv9k:~]$ alias show_running_yang='ruby /opt/puppetlabs/puppet/cache/lib/util/show_running_yang.rb'
35 | [xrv9k:~]$ show_running_yang
36 | [xrv9k:~]$ show_running_yang -c netconf /pkg/yang/Cisco-IOS-XR-ipv4-bgp-cfg.yang
37 | ```
38 |
--------------------------------------------------------------------------------
/examples/beaker_test_template.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | resource_name: 'cisco_yang', # 'cisco_yang' or 'cisco_yang_netconf'
24 | os: 'ios_xr',
25 | os_version: '6.2.1', # indicates that this test shoule be run on version 6.2.1 or later
26 | }
27 |
28 | # skip entire file if os, version, etc. don't match
29 | skip_unless_supported(tests)
30 |
31 | # define a test (or tests)
32 | # (e.g. description, title, manifest)
33 | tests[:my_test] = { # user-defined key for this test
34 | desc: '',
35 | title: '',
36 | manifest_props: {
37 | source: '',
38 | mode: 'replace', # usually will want this to be 'replace'
39 | },
40 | }
41 |
42 | #################################################################
43 | # Execute the test
44 | #################################################################
45 |
46 | test_name 'Model Test' do
47 | # a simple run with pre/post clean
48 | # (reference our test above using the key)
49 | test_harness_run_clean(tests, :my_test) # same ID as above
50 | end
51 |
52 | # report on skipped tests
53 | skipped_tests_summary(tests)
54 |
--------------------------------------------------------------------------------
/examples/site.pp:
--------------------------------------------------------------------------------
1 | node 'default' {
2 | file { '/root/temp/vrfs.json': source => 'puppet:///modules/ciscoyang/models/defaults/vrfs.json'}
3 |
4 | # Configure two vrfs (VOIP & INTERNET)
5 | cisco_yang { '{"Cisco-IOS-XR-infra-rsi-cfg:vrfs": [null]}':
6 | ensure => present,
7 | source => '/root/temp/vrfs.json',
8 | }
9 |
10 | # other examples
11 |
12 | file { '/root/temp/vrf-voip.json': source => 'puppet:///modules/ciscoyang/models/defaults/vrf-voip.json'}
13 |
14 | # Add a single vrf (VOIP)
15 | cisco_yang { '{"Cisco-IOS-XR-infra-rsi-cfg:vrfs": [null]}':
16 | ensure => present,
17 | source => '/root/temp/vrf-voip.json'
18 | }
19 |
20 | # Remove a single vrf (VOIP)
21 | cisco_yang { '{"Cisco-IOS-XR-infra-rsi-cfg:vrfs": [null]}':
22 | ensure => absent,
23 | source => '/root/temp/vrf-voip.json',
24 | }
25 |
26 | # Remove all vrfs
27 | cisco_yang { '{"Cisco-IOS-XR-infra-rsi-cfg:vrfs": [null]}':
28 | ensure => absent
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/files/models/defaults/vrf-voip.json:
--------------------------------------------------------------------------------
1 | {
2 | "Cisco-IOS-XR-infra-rsi-cfg:vrfs":{
3 | "vrf":[
4 | {
5 | "vrf-name":"VOIP",
6 | "description":"Voice over IP",
7 | "vpn-id":{
8 | "vpn-oui":875,
9 | "vpn-index":3
10 | },
11 | "create":[
12 | null
13 | ]
14 | }
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/files/models/defaults/vrfs.json:
--------------------------------------------------------------------------------
1 | {
2 | "Cisco-IOS-XR-infra-rsi-cfg:vrfs":{
3 | "vrf":[
4 | {
5 | "vrf-name":"VOIP",
6 | "description":"Voice over IP",
7 | "vpn-id":{
8 | "vpn-oui":87,
9 | "vpn-index":3
10 | },
11 | "create":[
12 | null
13 | ]
14 | },
15 | {
16 | "vrf-name":"INTERNET",
17 | "description":"Generic external traffic",
18 | "vpn-id":{
19 | "vpn-oui":85,
20 | "vpn-index":22
21 | },
22 | "create":[
23 | null
24 | ]
25 | }
26 | ]
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/files/models/defaults/vrfs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | VOIP
4 |
5 | Voice over IP
6 |
7 | 875
8 | 3
9 |
10 |
11 |
12 | INTERNET
13 |
14 | Generic external traffic
15 |
16 | 875
17 | 22
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/lib/.rubucop.yml:
--------------------------------------------------------------------------------
1 | inherit_from: ../.rubocop.yml
2 |
3 | # Baseline code complexity metrics for the lib/ subdirectory:
4 |
5 | Metrics/AbcSize:
6 | Max: 49
7 |
8 | Metrics/CyclomaticComplexity:
9 | Max: 23
10 |
11 | Metrics/MethodLength:
12 | Max: 50
13 |
14 | Metrics/ParameterLists:
15 | Max: 10
16 |
17 | Metrics/PerceivedComplexity:
18 | Max: 24
19 |
--------------------------------------------------------------------------------
/lib/facter/cisco_yang.rb:
--------------------------------------------------------------------------------
1 | require 'facter'
2 | require File.expand_path('../../util/environment', __FILE__)
3 | # require File.expand_path('../../util/platform', __FILE__)
4 |
5 | Facter.add(:cisco_yang) do
6 | confine operatingsystem: [:ios_xr]
7 |
8 | setcode do
9 | hash = {}
10 |
11 | hash['configured_envs'] = Cisco::Util::Environment.environment_names
12 |
13 | # don't do this, for now. It's slow, so wait until we need it
14 | # Platform = Cisco::Platform
15 | # hash['system'] = Platform.system
16 | # hash['system_time'] = Platform.system_time
17 | # hash['host_name'] = Platform.host_name
18 | # hash['product_id'] = Platform.product_id
19 |
20 | hash
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/lib/minitest/environment_plugin.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | require_relative '../util/environment'
16 |
17 | # Add environment option to minitest
18 | module Minitest
19 | def self.plugin_environment_options(opts, options)
20 | opts.on('-e', '--environment NAME', 'Select environment by name') do |name|
21 | options[:environment] = name
22 | end
23 | end
24 |
25 | def self.plugin_environment_init(options)
26 | name = options[:environment]
27 | Cisco::Environment.default_environment_name = name unless name.nil?
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/lib/minitest/log_level_plugin.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | require 'logger'
16 | require_relative '../util/logger'
17 |
18 | # Add logging level option to minitest
19 | module Minitest
20 | LEVEL_ALIASES = {
21 | 'debug' => Logger::DEBUG,
22 | 'info' => Logger::INFO,
23 | 'warning' => Logger::WARN,
24 | 'error' => Logger::ERROR,
25 | }
26 | def self.plugin_log_level_options(opts, options)
27 | opts.on(
28 | '-l', '--log-level LEVEL', LEVEL_ALIASES,
29 | 'Configure logging level for tests',
30 | "(#{LEVEL_ALIASES.keys.join(', ')})"
31 | ) do |level|
32 | options[:log_level] = level
33 | end
34 | end
35 |
36 | def self.plugin_log_level_init(options)
37 | Cisco::Logger.level = options[:log_level] if options[:log_level]
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/lib/puppet/feature/json.rb:
--------------------------------------------------------------------------------
1 | require 'puppet/util/feature'
2 |
3 | # custom feature for json
4 | Puppet.features.add(:json, libs: ['json'])
5 |
--------------------------------------------------------------------------------
/lib/puppet/feature/node_util.rb:
--------------------------------------------------------------------------------
1 | require 'puppet/util/feature'
2 |
3 | # custom feature for node_util
4 | path = File.expand_path('../../../util/node_util', __FILE__)
5 | Puppet.features.add(:node_util, libs: [path])
6 |
--------------------------------------------------------------------------------
/lib/puppet/property/yang_json.rb:
--------------------------------------------------------------------------------
1 | require_relative 'yang_property'
2 |
3 | # This subclass of {YangProperty} manages YANG JSON content.
4 | #
5 | class YangJson < YangProperty
6 | # Determine if the specified value is inline YANG or a file path
7 | def inline_yang?(yang_or_file)
8 | !/^{/.match(yang_or_file).nil?
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/lib/puppet/property/yang_netconf.rb:
--------------------------------------------------------------------------------
1 | require_relative 'yang_netconf_property'
2 |
3 | # This subclass of {YangProperty} manages YANG XML Netconf content.
4 | #
5 | class YangNetconf < YangNetconfProperty
6 | # Determine if the specified value is inline YANG or a file path
7 | def inline_yang?(yang_or_file)
8 | !/^ read YANG from file #{yang_or_file}:"
20 | debug content
21 | # rescue Exception => e
22 | # puts "**************** ERROR DETECTED WHILE READING YANG FILE #{yang_or_file} ****************"
23 | # puts e.message
24 | # content = nil
25 | # end
26 | content
27 | else
28 | debug "-----> value is inline YANG: #{yang_or_file}"
29 | yang_or_file
30 | end
31 | end
32 |
33 | def should
34 | return @should_yang if @should_yang
35 |
36 | result = super
37 |
38 | # need better way to determine life-cycle stage of provider
39 | if provider && provider.respond_to?(:active?) && provider.active?
40 | @should_yang = result = yang_content(result)
41 | end
42 |
43 | result
44 | end
45 |
46 | # Determine if the "is" value is the same as the "should" value
47 | # (has the value of this property changed?)
48 | def insync?(is)
49 | replace = @resource && @resource[:mode] == :replace
50 |
51 | should_yang = should
52 |
53 | if is == :unknown
54 | # if the current config is unknown, assume configs are not in-sync
55 | insync = false
56 | else
57 | if replace
58 | insync = Cisco::Netconf.insync_for_replace(should_yang, is)
59 | else
60 | insync = Cisco::Netconf.insync_for_merge(should_yang, is)
61 | end
62 | end
63 |
64 | if insync
65 | debug '**************** IDEMPOTENT -- NO CHANGES DETECTED ****************'
66 | elsif debug '**************** IS vs SHOULD CHANGES DETECTED ****************'
67 | end
68 |
69 | insync
70 | end
71 | end
72 |
--------------------------------------------------------------------------------
/lib/puppet/property/yang_property.rb:
--------------------------------------------------------------------------------
1 | require 'puppet/property'
2 | require_relative '../../util/node_util' if Puppet.features.node_util?
3 |
4 | # This subclass of {Puppet::Property} manages YANG content.
5 | #
6 | class YangProperty < Puppet::Property
7 | # Determine if the specified value is inline YANG or a file path
8 | def inline_yang?(_yang_or_file)
9 | false
10 | end
11 |
12 | def yang_content(yang_or_file)
13 | return '' if yang_or_file.nil?
14 |
15 | # If it's not inline YANG, then assume it's a file, so read it in.
16 | if !inline_yang?(yang_or_file)
17 | # begin
18 | content = File.read(yang_or_file)
19 | debug "-----> read YANG from file #{yang_or_file}:"
20 | debug content
21 | # rescue Exception => e
22 | # puts "**************** ERROR DETECTED WHILE READING YANG FILE #{yang_or_file} ****************"
23 | # puts e.message
24 | # content = nil
25 | # end
26 | content
27 | else
28 | debug "-----> value is inline YANG: #{yang_or_file}"
29 | yang_or_file
30 | end
31 | end
32 |
33 | def should
34 | return @should_yang if @should_yang
35 |
36 | result = super
37 |
38 | # need better way to determine life-cycle stage of provider
39 | if provider && provider.respond_to?(:active?) && provider.active?
40 | @should_yang = result = yang_content(result)
41 | end
42 |
43 | result
44 | end
45 |
46 | # Determine if the "is" value is the same as the "should" value
47 | # (has the value of this property changed?)
48 | def insync?(is)
49 | replace = @resource && @resource[:mode] == :replace
50 |
51 | should_yang = should
52 |
53 | if is == :unknown
54 | # if the current config is unknown, assume configs are not in-sync
55 | insync = false
56 | else
57 | if replace
58 | insync = Cisco::Yang.insync_for_replace?(should_yang, is)
59 | else
60 | insync = Cisco::Yang.insync_for_merge?(should_yang, is)
61 | end
62 | end
63 |
64 | if insync
65 | debug '**************** IDEMPOTENT -- NO CHANGES DETECTED ****************'
66 | elsif debug '**************** IS vs SHOULD CHANGES DETECTED ****************'
67 | end
68 |
69 | insync
70 | end
71 | end
72 |
--------------------------------------------------------------------------------
/lib/puppet/provider/cisco_yang/cisco.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2015 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | require_relative '../../../util/node_util' if Puppet.features.node_util?
16 | require_relative '../../../util/yang_accessor' if Puppet.features.node_util?
17 |
18 | Puppet::Type.type(:cisco_yang).provide(:cisco) do
19 | desc 'IOS-XR configuration management via YANG.'
20 | defaultfor operatingsystem: [:ios_xr]
21 |
22 | confine feature: :json
23 | confine feature: :node_util
24 |
25 | def initialize(value={})
26 | super(value)
27 | @node = Cisco::Node.instance(Cisco::Client::GRPC)
28 | debug 'Created provider instance of cisco_yang.'
29 | end
30 |
31 | def exists?
32 | activate
33 | source && source != :absent
34 | end
35 |
36 | def create
37 | setyang(@resource[:source])
38 | end
39 |
40 | def destroy
41 | @source = nil # clear the cached value
42 | src = @resource[:source] || @resource[:target]
43 | debug '**************** REMOVING CONFIG ****************'
44 | @node.delete_yang(src)
45 | debug '**************** REMOVE SUCCESSFUL ****************'
46 | end
47 |
48 | def resource_mode
49 | @resource && @resource[:mode] == :replace ? :replace : :merge
50 | end
51 |
52 | def resource_force
53 | @resource && @resource[:force] ? true : false
54 | end
55 |
56 | # Return the current source YANG
57 | def source
58 | return @source if @source # return the cached value, if it's there
59 |
60 | if resource_force
61 | # If instructed to force the configuration, then there is no reason
62 | # to query the current configuration; just return :unknown.
63 | source_yang = :unknown
64 | else
65 | source_yang = @node.get_yang(@resource[:target])
66 |
67 | debug '**************** CURRENT CONFIG ****************'
68 | debug source_yang
69 |
70 | source_yang = :absent if !source_yang || source_yang.empty?
71 | end
72 |
73 | @source = source_yang
74 | rescue StandardError => e
75 | unless e.message =~ /unknown-namespace/ ||
76 | e.message =~ /not recognized or supported/
77 | raise
78 | end
79 | error e.message
80 | @source = nil
81 | end
82 |
83 | # Set the source YANG.
84 | def source=(value)
85 | setyang(value)
86 | end
87 |
88 | def setyang(value)
89 | @source = nil # clear the cached value
90 | debug '**************** SETTING CONFIG ****************'
91 | debug "Value: #{value}"
92 | debug "Resource Mode #{resource_mode}"
93 | if resource_mode == :replace
94 | @node.replace_yang(value)
95 | else
96 | @node.merge_yang(value)
97 | end
98 | debug '**************** SET SUCCESSFUL ****************'
99 | end
100 |
101 | def self.instances
102 | ya = Cisco::YangAccessor.new
103 | targets = ya.targets(client_class: Cisco::Client::GRPC)
104 |
105 | targets.map do |target|
106 | new(name: target)
107 | end
108 | end
109 |
110 | def activate
111 | @active = true
112 | end
113 |
114 | def active?
115 | !@active.nil?
116 | end
117 | end
118 |
--------------------------------------------------------------------------------
/lib/puppet/provider/cisco_yang_netconf/cisco.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2015 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | require_relative '../../../util/node_util' if Puppet.features.node_util?
16 | require_relative '../../../util/yang_accessor' if Puppet.features.node_util?
17 | # require 'rubygems'
18 |
19 | Puppet::Type.type(:cisco_yang_netconf).provide(:cisco) do
20 | desc 'IOS-XR configuration management via YANG.'
21 | defaultfor operatingsystem: [:ios_xr]
22 |
23 | def initialize(value={})
24 | super(value)
25 | activate
26 | @node = Cisco::Node.instance(Cisco::Client::NETCONF)
27 | end
28 |
29 | def create
30 | setyang(@resource[:source])
31 | end
32 |
33 | def destroy
34 | @source = nil # clear the cached value
35 | src = @resource[:source] || @resource[:target]
36 | debug '**************** REMOVING CONFIG ****************'
37 | debug '**************** Delete is not available. ****************'
38 | debug src
39 | # @node.delete_yang(src)
40 | debug '**************** REMOVE SUCCESSFUL ****************'
41 | end
42 |
43 | def resource_mode
44 | @resource && @resource[:mode] == :replace ? :replace : :merge
45 | end
46 |
47 | def resource_force
48 | @resource && @resource[:force] ? true : false
49 | end
50 |
51 | # Return the current source YANG
52 | def source
53 | return @source if @source # return the cached value, if it's there
54 |
55 | if resource_force
56 | # If instructed to force the configuration, then there is no reason
57 | # to query the current configuration; just return :unknown.
58 | source_yang = :unknown
59 | else
60 | source_yang = @node.get_netconf(@resource[:target])
61 |
62 | debug '**************** CURRENT CONFIG ****************'
63 | debug source_yang
64 |
65 | # somewhere someone reads source_yang as a string. This is a workaround.
66 | # source_yang = :absent if !source_yang || source_yang.empty?
67 | end
68 |
69 | @source = source_yang
70 | rescue StandardError => e
71 | raise unless e.message =~ /unknown-namespace/
72 | error e.message
73 | @source = nil
74 | end
75 |
76 | # Set the source YANG.
77 | def source=(value)
78 | setyang(value)
79 | end
80 |
81 | def setyang(value)
82 | @source = nil # clear the cached value
83 | debug '**************** SETTING CONFIG ****************'
84 | debug "Value: #{value}"
85 | debug "Resource Mode #{resource_mode}"
86 | if resource_mode == :replace
87 | @node.replace_netconf(value)
88 | else
89 | @node.merge_netconf(value)
90 | end
91 | debug '**************** SET SUCCESSFUL ****************'
92 | end
93 |
94 | def self.instances
95 | ya = Cisco::YangAccessor.new
96 | targets = ya.targets(client_class: Cisco::Client::NETCONF)
97 |
98 | targets.map do |target|
99 | new(name: target)
100 | end
101 | end
102 |
103 | def activate
104 | @active = true
105 | end
106 |
107 | def active?
108 | !@active.nil?
109 | end
110 | end
111 |
--------------------------------------------------------------------------------
/lib/puppet/type/cisco_yang.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../../property/yang_json.rb', __FILE__)
2 |
3 | Puppet::Type.newtype(:cisco_yang) do
4 | @doc = "IOS-XR configuration management via YANG.
5 |
6 | ~~~puppet
7 | cisco_yang { '':
8 | ..attributes..
9 | }
10 | ~~~
11 | `` is the title of the yang resource.
12 | Example:
13 | ~~~puppet
14 | cisco_yang { 'blue vrf':
15 | ensure => present,
16 | target => '{\"Cisco-IOS-XR-infra-rsi-cfg:vrfs\": [null]}',
17 | source => '{\"Cisco-IOS-XR-infra-rsi-cfg:vrfs\": {
18 | \"vrf\":[
19 | {
20 | \"vrf-name\":\"blue\",
21 | \"vpn-id\":{
22 | \"vpn-oui\":875,
23 | \"vpn-index\":3
24 | },
25 | \"create\":[null]
26 | }
27 | ]
28 | }
29 | }',
30 | }
31 | ~~~
32 | ~~~puppet
33 | cisco_yang { '{\"Cisco-IOS-XR-infra-rsi-cfg:vrfs\": [null]}':
34 | ensure => present,
35 | source => '{\"Cisco-IOS-XR-infra-rsi-cfg:vrfs\": {
36 | \"vrf\":[
37 | {
38 | \"vrf-name\":\"red\",
39 | \"vpn-id\":{
40 | \"vpn-oui\":875,
41 | \"vpn-index\":22
42 | },
43 | \"create\":[null]
44 | }
45 | ]
46 | }
47 | }',
48 | }
49 | ~~~
50 | ~~~puppet
51 | cisco_yang { '{\"Cisco-IOS-XR-infra-rsi-cfg:vrfs\": [null]}':
52 | ensure => absent,
53 | ~~~
54 | "
55 |
56 | ensurable
57 |
58 | newparam(:target, parent: YangJson) do
59 | isnamevar
60 | desc 'String containing the model path of the target node in YANG JSON '\
61 | 'format, or a reference to a local file containing the model path.'
62 | end
63 |
64 | newparam(:mode) do
65 | desc 'Determines the mode to use when setting configuration via '\
66 | "ensure=>present. If 'replace' is specified, the current "\
67 | 'configuration will be replaced by the configuration in the '\
68 | "'source' property. If 'merge' is specified, the configuration "\
69 | "in the 'source' property will be merged into the current "\
70 | "configuration. Valid values are 'replace' and 'merge' (which "\
71 | 'is the default.'
72 | munge(&:to_sym)
73 | newvalues(:replace, :merge)
74 | end
75 |
76 | newparam(:force) do
77 | desc 'If :true is specified, then the specified value of the source '\
78 | 'property is set on the device, regardless of the current value. '\
79 | 'If :false is specified (or no value is specified), the default '\
80 | 'behavior is to only set the configuration if it is different '\
81 | 'from the running configuration.'
82 | newvalues(:true, :false)
83 | munge do |force|
84 | force == true || force == 'true' || force == :true
85 | end
86 | end
87 |
88 | newproperty(:source, parent: YangJson) do
89 | desc 'The model data in YANG JSON format, or a reference to a local file '\
90 | 'containing the model data. This property is only used when '\
91 | 'ensure=>present is used.'
92 | end
93 |
94 | #
95 | # VALIDATIONS
96 | #
97 | validate do
98 | fail("The 'target' parameter must be set in the manifest.") if self[:target].nil?
99 | if self[:source].nil? && self[:ensure] == :present
100 | fail("The 'source' parameter must be set in the manifest when mode is replace.") if self[:mode] == :replace
101 | self[:source] = self[:target]
102 | end
103 | end
104 | end
105 |
--------------------------------------------------------------------------------
/lib/puppet/type/cisco_yang_netconf.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../../property/yang_netconf.rb', __FILE__)
2 |
3 | Puppet::Type.newtype(:cisco_yang_netconf) do
4 | @doc = "IOS-XR configuration management via YANG Netconf.
5 |
6 | ~~~puppet
7 | cisco_yang_netconf { '':
8 | ..attributes..
9 | }
10 | ~~~
11 | `` is the title of the yang resource.
12 | This example demonstrates changing the VRF table to contain only the vrf with name \"blue\".
13 | ~~~puppet
14 | cisco_yang_netconf { 'blue vrf':
15 | target => '',
16 | source => '
17 |
18 | blue
19 |
20 |
21 | 875
22 | 3
23 |
24 |
25 | ',
26 | mode => replace
27 | }
28 | ~~~
29 | This example demonstrates inserting the vrf with name \"blue\" into the table, with the values provided.
30 | ~~~puppet
31 | cisco_yang_netconf { '':
32 | source => '
33 |
34 | blue
35 |
36 |
37 | 875
38 | 3
39 |
40 |
41 | '
42 | }
43 | ~~~
44 | This example demonstrates removing the vrf with name \"red\" from the vrf table.
45 | ~~~puppet
46 | cisco_yang_netconf { '':
47 | source => '
48 |
49 | red
50 |
51 |
52 | '
53 | }
54 | ~~~
55 | "
56 |
57 | newparam(:target, parent: YangNetconf) do
58 | isnamevar
59 | desc 'XML formatted string or file location of an XML formatted string ' \
60 | 'that contains the filter text used in a netconf query.'
61 | end
62 |
63 | newparam(:mode) do
64 | desc 'Determines the mode to use when setting configuration.'\
65 | "If 'replace' is specified, the current "\
66 | 'configuration will be replaced by the configuration in the '\
67 | "'source' property. If 'merge' is specified, the configuration "\
68 | "in the 'source' property will be merged into the current "\
69 | "configuration. Valid values are 'replace' and 'merge' (which "\
70 | 'is the default.'
71 | munge(&:to_sym)
72 | newvalues(:replace, :merge)
73 | end
74 |
75 | newparam(:force) do
76 | desc 'If :true is specified, then the specified value of the source '\
77 | 'property is set on the device, regardless of the current value. '\
78 | 'If :false is specified (or no value is specified), the default '\
79 | 'behavior is to only set the configuration if it is different '\
80 | 'from the running configuration.'
81 | newvalues(:true, :false)
82 | munge do |force|
83 | force == true || force == 'true' || force == :true
84 | end
85 | end
86 |
87 | newproperty(:source, parent: YangNetconf) do
88 | desc 'The model data in YANG XML Netconf format, or a reference to a local file '\
89 | 'containing the model data.'
90 | end
91 |
92 | #
93 | # VALIDATIONS
94 | #
95 | validate do
96 | fail("The 'target' parameter must be set in the manifest.") if self[:target].nil?
97 | if self[:source].nil?
98 | fail("The 'source' parameter must be set in the manifest when mode is replace.") if self[:mode] == :replace
99 | self[:source] = self[:target]
100 | end
101 | end
102 | end
103 |
--------------------------------------------------------------------------------
/lib/util/client.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | # Namespace for Cisco-specific code
16 | module Cisco
17 | # Namespace for Cisco Client shim
18 | class Client
19 | end
20 | end
21 |
22 | require_relative 'client/client'
23 |
24 | # Try to load known extensions
25 | extensions = ['client/grpc',
26 | 'client/netconf',
27 | ]
28 | extensions.each do |ext|
29 | begin
30 | require_relative ext
31 | rescue LoadError => e
32 | # ignore missing clients, they're not always required
33 | raise unless e.message =~ /#{Regexp.escape(ext)}/
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/lib/util/client/client.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2015-2016 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | require_relative '../environment'
16 | require_relative '../exceptions'
17 | require_relative 'utils'
18 | require_relative '../constants'
19 | require_relative '../logger'
20 |
21 | include Cisco::Logger
22 |
23 | # Base class for clients of various RPC formats
24 | class Cisco::Client
25 | @@clients = [] # rubocop:disable Style/ClassVars
26 |
27 | def self.clients
28 | @@clients
29 | end
30 |
31 | # Each subclass should call this method to register itself.
32 | def self.register_client(client)
33 | @@clients << client
34 | end
35 |
36 | attr_reader :platform, :host, :port, :address, :username, :password
37 |
38 | def initialize(platform: nil,
39 | **kwargs)
40 | if self.class == Cisco::Client
41 | fail NotImplementedError, 'Cisco::Client is an abstract class. ' \
42 | "Instantiate one of #{@@clients} or use Cisco::Client.create() instead"
43 | end
44 | self.class.validate_args(**kwargs)
45 | @host = kwargs[:host]
46 | @port = kwargs[:port]
47 | @address = @port.nil? ? @host : "#{@host}:#{@port}"
48 | @username = kwargs[:username]
49 | @password = kwargs[:password]
50 | self.platform = platform
51 | end
52 |
53 | def self.validate_args(**kwargs)
54 | host = kwargs[:host]
55 | unless host.nil?
56 | fail TypeError, 'invalid address' unless host.is_a?(String)
57 | fail ArgumentError, 'empty address' if host.empty?
58 | end
59 | username = kwargs[:username]
60 | unless username.nil?
61 | fail TypeError, 'invalid username' unless username.is_a?(String)
62 | fail ArgumentError, 'empty username' if username.empty?
63 | end
64 | password = kwargs[:password]
65 | unless password.nil?
66 | fail TypeError, 'invalid password' unless password.is_a?(String)
67 | fail ArgumentError, 'empty password' if password.empty?
68 | end
69 | end
70 |
71 | def self.environment_name(client_class)
72 | client_class.name.split('::').last.downcase
73 | end
74 |
75 | def self.environment(client_class)
76 | Cisco::Util::Environment.environment(environment_name(client_class))
77 | end
78 |
79 | # Try to create an instance of the specified subclass
80 | def self.create(client_class)
81 | env = environment(client_class)
82 | env_name = environment_name(client_class)
83 |
84 | fail Cisco::ClientError, "No client environment configured for '#{env_name}'" unless env
85 |
86 | host = env[:host]
87 | port = env[:port]
88 | debug "Trying to connect to #{host}:#{port} as #{client_class}"
89 | errors = []
90 | begin
91 | client = client_class.new(**env)
92 | debug "#{client_class} connected successfully"
93 | return client
94 | rescue Cisco::ClientError, TypeError, ArgumentError => e
95 | debug "Unable to connect to #{host} as #{client_class}: #{e.message}"
96 | debug e.backtrace.join("\n ")
97 | errors << e
98 | end
99 | handle_errors(errors)
100 | end
101 |
102 | def self.handle_errors(errors)
103 | # ClientError means we tried to connect but failed,
104 | # so it's 'more significant' than input validation errors.
105 | client_errors = errors.select { |e| e.kind_of? Cisco::ClientError }
106 | if !client_errors.empty?
107 | # Reraise the specific error if just one
108 | fail client_errors[0] if client_errors.length == 1
109 | # Otherwise clump them together into a new error
110 | e_cls = client_errors[0].class
111 | unless client_errors.all? { |e| e.class == e_cls }
112 | e_cls = Cisco::ClientError
113 | end
114 | fail e_cls, ("Unable to establish any client connection:\n" +
115 | errors.each(&:message).join("\n"))
116 | elsif errors.any? { |e| e.kind_of? ArgumentError }
117 | fail ArgumentError, ("Invalid arguments:\n" +
118 | errors.each(&:message).join("\n"))
119 | elsif errors.any? { |e| e.kind_of? TypeError }
120 | fail TypeError, ("Invalid arguments:\n" +
121 | errors.each(&:message).join("\n"))
122 | end
123 | fail Cisco::ClientError, 'No client connected, but no errors were reported?'
124 | end
125 |
126 | def to_s
127 | @address.to_s
128 | end
129 |
130 | def inspect
131 | "<#{self.class} of #{@address}>"
132 | end
133 |
134 | # Configure the given state on the device.
135 | #
136 | # @param values [String, Array] Actual configuration to set
137 | # @param kwargs data-format-specific args
138 | def set(values: nil,
139 | **_kwargs)
140 | # subclasses will generally want to call Client.munge_to_array()
141 | # on values before calling super()
142 | Cisco::Logger.debug("values: #{values})") \
143 | unless values.nil? || values.empty?
144 | # to be implemented by subclasses
145 | end
146 |
147 | # Get the given state from the device.
148 | #
149 | # @param command [String] the get command to execute
150 | # @param value [String, Regexp] Specific key or regexp to look up
151 | # @param kwargs data-format-specific args
152 | # @return [String, Hash, nil] The state found, or nil if not found.
153 | def get(command: nil,
154 | **_kwargs)
155 | # subclasses will generally want to call Client.munge_to_array()
156 | # on value before calling super()
157 | Cisco::Logger.debug(" executing command:\n #{command}") \
158 | unless command.nil? || command.empty?
159 | # to be implemented by subclasses
160 | end
161 |
162 | private
163 |
164 | # Set the platform of the node managed by this client.
165 | def platform=(platform)
166 | fail ArgumentError, "unknown platform #{platform}" \
167 | unless Cisco::Util::PLATFORMS.include?(platform)
168 | @platform = platform
169 | end
170 | end
171 |
--------------------------------------------------------------------------------
/lib/util/client/grpc.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2015 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | require_relative 'client'
16 |
17 | # Fail gracefully if submodule dependencies are not met
18 | begin
19 | Cisco::Client.silence_warnings do
20 | require 'grpc'
21 | end
22 | rescue LoadError => e
23 | raise unless e.message =~ /-- grpc/
24 | # If grpc is not installed, raise an error that client understands.
25 | raise LoadError, "Unable to load client/grpc -- #{e}"
26 | end
27 |
28 | # Namespace for Cisco EMS gRPC-specific code
29 | class Cisco::Client::GRPC < Cisco::Client
30 | end
31 |
32 | # Auto-load all Ruby files in the subdirectory
33 | Dir.glob(__dir__ + '/grpc/*.rb') { |file| require file }
34 |
--------------------------------------------------------------------------------
/lib/util/client/grpc/ems.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package IOSXRExtensibleManagabilityService;
4 |
5 | service gRPCConfigOper {
6 |
7 | // Configuration related commands
8 |
9 | rpc GetConfig(ConfigGetArgs) returns(stream ConfigGetReply) {};
10 | // 6.0 Config commands do implicit commits
11 | rpc MergeConfig(ConfigArgs) returns(ConfigReply) {};
12 |
13 | rpc DeleteConfig(ConfigArgs) returns(ConfigReply) {};
14 |
15 | rpc ReplaceConfig(ConfigArgs) returns(ConfigReply) {};
16 |
17 | rpc CliConfig(CliConfigArgs) returns(CliConfigReply) {};
18 |
19 | // not implemented for 6.0
20 | rpc CommitReplace(CommitReplaceArgs) returns (CommitReplaceReply) {};
21 | // not implemented for 6.0
22 | rpc CommitConfig(CommitArgs) returns(CommitReply) {};
23 | // not implemented for 6.0
24 | rpc ConfigDiscardChanges(DiscardChangesArgs) returns(DiscardChangesReply) {};
25 |
26 |
27 | // Get only returns oper data
28 | //
29 | rpc GetOper(GetOperArgs) returns(stream GetOperReply) {};
30 | // Do we need "Get" also to give combined oper and config?
31 | }
32 |
33 | //
34 | // Should we seperate Exec from Config/Oper?
35 | //
36 |
37 | service gRPCExec {
38 | // Exec commands
39 | rpc ShowCmdTextOutput(ShowCmdArgs) returns(stream ShowCmdTextReply) {};
40 | rpc ShowCmdJSONOutput(ShowCmdArgs) returns(stream ShowCmdJSONReply) {};
41 |
42 |
43 | }
44 |
45 | message ConfigGetArgs {
46 | int64 ReqId = 1;
47 | string yangpathjson = 2;
48 | }
49 |
50 | message ConfigGetReply {
51 | int64 ResReqId = 1;
52 | string yangjson = 2;
53 | string errors = 3;
54 | }
55 |
56 | message GetOperArgs {
57 | int64 ReqId = 1;
58 | string yangpathjson = 2;
59 | }
60 |
61 | message GetOperReply {
62 | int64 ResReqId = 1;
63 | string yangjson = 2;
64 | string errors = 3;
65 | }
66 |
67 |
68 | message ConfigArgs {
69 | int64 ReqId = 1;
70 | string yangjson = 2;
71 |
72 | }
73 |
74 | message ConfigReply {
75 | int64 ResReqId = 1;
76 | string errors = 2;
77 | }
78 |
79 | message CliConfigArgs {
80 | int64 ReqId = 1;
81 | string cli = 2;
82 | }
83 |
84 | message CliConfigReply {
85 | int64 ResReqId = 1;
86 | string errors = 2;
87 | }
88 |
89 |
90 | message CommitReplaceArgs {
91 | int64 ReqId = 1;
92 | string cli = 2;
93 | string yangjson = 3;
94 | }
95 |
96 | message CommitReplaceReply {
97 | int64 ResReqId = 1;
98 | string errors = 2;
99 | }
100 |
101 | message CommitMsg {
102 | string label = 1;
103 | string comment = 2;
104 | }
105 |
106 | enum CommitResult {
107 | CHANGE = 0;
108 | NO_CHANGE = 1;
109 | FAIL = 2;
110 | }
111 |
112 | message CommitArgs {
113 | CommitMsg msg = 1;
114 | int64 ReqId = 2;
115 | }
116 |
117 | message CommitReply {
118 | CommitResult result = 1;
119 | int64 ResReqId = 2;
120 | string errors = 3;
121 | }
122 |
123 |
124 | message DiscardChangesArgs {
125 | int64 ReqId = 1;
126 | }
127 |
128 | message DiscardChangesReply {
129 | int64 ResReqId = 1;
130 | string errors = 2;
131 | }
132 |
133 |
134 | message ShowCmdArgs {
135 | int64 ReqId = 1;
136 | string cli = 2;
137 | }
138 |
139 | message ShowCmdTextReply {
140 | int64 ResReqId =1;
141 | string output = 2;
142 | string errors = 3;
143 | }
144 | message ShowCmdJSONReply {
145 | int64 ResReqId =1;
146 | string jsonoutput = 2;
147 | string errors = 3;
148 | }
149 |
--------------------------------------------------------------------------------
/lib/util/client/grpc/ems.rb:
--------------------------------------------------------------------------------
1 | # Generated by the protocol buffer compiler. DO NOT EDIT!
2 | # source: ems.proto
3 |
4 | require 'google/protobuf'
5 |
6 | Google::Protobuf::DescriptorPool.generated_pool.build do
7 | add_message "IOSXRExtensibleManagabilityService.ConfigGetArgs" do
8 | optional :ReqId, :int64, 1
9 | optional :yangpathjson, :string, 2
10 | end
11 | add_message "IOSXRExtensibleManagabilityService.ConfigGetReply" do
12 | optional :ResReqId, :int64, 1
13 | optional :yangjson, :string, 2
14 | optional :errors, :string, 3
15 | end
16 | add_message "IOSXRExtensibleManagabilityService.GetOperArgs" do
17 | optional :ReqId, :int64, 1
18 | optional :yangpathjson, :string, 2
19 | end
20 | add_message "IOSXRExtensibleManagabilityService.GetOperReply" do
21 | optional :ResReqId, :int64, 1
22 | optional :yangjson, :string, 2
23 | optional :errors, :string, 3
24 | end
25 | add_message "IOSXRExtensibleManagabilityService.ConfigArgs" do
26 | optional :ReqId, :int64, 1
27 | optional :yangjson, :string, 2
28 | end
29 | add_message "IOSXRExtensibleManagabilityService.ConfigReply" do
30 | optional :ResReqId, :int64, 1
31 | optional :errors, :string, 2
32 | end
33 | add_message "IOSXRExtensibleManagabilityService.CliConfigArgs" do
34 | optional :ReqId, :int64, 1
35 | optional :cli, :string, 2
36 | end
37 | add_message "IOSXRExtensibleManagabilityService.CliConfigReply" do
38 | optional :ResReqId, :int64, 1
39 | optional :errors, :string, 2
40 | end
41 | add_message "IOSXRExtensibleManagabilityService.CommitReplaceArgs" do
42 | optional :ReqId, :int64, 1
43 | optional :cli, :string, 2
44 | optional :yangjson, :string, 3
45 | end
46 | add_message "IOSXRExtensibleManagabilityService.CommitReplaceReply" do
47 | optional :ResReqId, :int64, 1
48 | optional :errors, :string, 2
49 | end
50 | add_message "IOSXRExtensibleManagabilityService.CommitMsg" do
51 | optional :label, :string, 1
52 | optional :comment, :string, 2
53 | end
54 | add_message "IOSXRExtensibleManagabilityService.CommitArgs" do
55 | optional :msg, :message, 1, "IOSXRExtensibleManagabilityService.CommitMsg"
56 | optional :ReqId, :int64, 2
57 | end
58 | add_message "IOSXRExtensibleManagabilityService.CommitReply" do
59 | optional :result, :enum, 1, "IOSXRExtensibleManagabilityService.CommitResult"
60 | optional :ResReqId, :int64, 2
61 | optional :errors, :string, 3
62 | end
63 | add_message "IOSXRExtensibleManagabilityService.DiscardChangesArgs" do
64 | optional :ReqId, :int64, 1
65 | end
66 | add_message "IOSXRExtensibleManagabilityService.DiscardChangesReply" do
67 | optional :ResReqId, :int64, 1
68 | optional :errors, :string, 2
69 | end
70 | add_message "IOSXRExtensibleManagabilityService.ShowCmdArgs" do
71 | optional :ReqId, :int64, 1
72 | optional :cli, :string, 2
73 | end
74 | add_message "IOSXRExtensibleManagabilityService.ShowCmdTextReply" do
75 | optional :ResReqId, :int64, 1
76 | optional :output, :string, 2
77 | optional :errors, :string, 3
78 | end
79 | add_message "IOSXRExtensibleManagabilityService.ShowCmdJSONReply" do
80 | optional :ResReqId, :int64, 1
81 | optional :jsonoutput, :string, 2
82 | optional :errors, :string, 3
83 | end
84 | add_enum "IOSXRExtensibleManagabilityService.CommitResult" do
85 | value :CHANGE, 0
86 | value :NO_CHANGE, 1
87 | value :FAIL, 2
88 | end
89 | end
90 |
91 | module IOSXRExtensibleManagabilityService
92 | ConfigGetArgs = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.ConfigGetArgs").msgclass
93 | ConfigGetReply = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.ConfigGetReply").msgclass
94 | GetOperArgs = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.GetOperArgs").msgclass
95 | GetOperReply = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.GetOperReply").msgclass
96 | ConfigArgs = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.ConfigArgs").msgclass
97 | ConfigReply = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.ConfigReply").msgclass
98 | CliConfigArgs = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.CliConfigArgs").msgclass
99 | CliConfigReply = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.CliConfigReply").msgclass
100 | CommitReplaceArgs = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.CommitReplaceArgs").msgclass
101 | CommitReplaceReply = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.CommitReplaceReply").msgclass
102 | CommitMsg = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.CommitMsg").msgclass
103 | CommitArgs = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.CommitArgs").msgclass
104 | CommitReply = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.CommitReply").msgclass
105 | DiscardChangesArgs = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.DiscardChangesArgs").msgclass
106 | DiscardChangesReply = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.DiscardChangesReply").msgclass
107 | ShowCmdArgs = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.ShowCmdArgs").msgclass
108 | ShowCmdTextReply = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.ShowCmdTextReply").msgclass
109 | ShowCmdJSONReply = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.ShowCmdJSONReply").msgclass
110 | CommitResult = Google::Protobuf::DescriptorPool.generated_pool.lookup("IOSXRExtensibleManagabilityService.CommitResult").enummodule
111 | end
112 |
--------------------------------------------------------------------------------
/lib/util/client/grpc/ems_services.rb:
--------------------------------------------------------------------------------
1 | # Generated by the protocol buffer compiler. DO NOT EDIT!
2 | # Source: ems.proto for package 'IOSXRExtensibleManagabilityService'
3 |
4 | require 'grpc'
5 | require_relative 'ems'
6 |
7 | module IOSXRExtensibleManagabilityService
8 | module GRPCConfigOper
9 |
10 | # TODO: add proto service documentation here
11 | class Service
12 |
13 | include GRPC::GenericService
14 |
15 | self.marshal_class_method = :encode
16 | self.unmarshal_class_method = :decode
17 | self.service_name = 'IOSXRExtensibleManagabilityService.gRPCConfigOper'
18 |
19 | rpc :GetConfig, ConfigGetArgs, stream(ConfigGetReply)
20 | rpc :MergeConfig, ConfigArgs, ConfigReply
21 | rpc :DeleteConfig, ConfigArgs, ConfigReply
22 | rpc :ReplaceConfig, ConfigArgs, ConfigReply
23 | rpc :CliConfig, CliConfigArgs, CliConfigReply
24 | rpc :CommitReplace, CommitReplaceArgs, CommitReplaceReply
25 | rpc :CommitConfig, CommitArgs, CommitReply
26 | rpc :ConfigDiscardChanges, DiscardChangesArgs, DiscardChangesReply
27 | rpc :GetOper, GetOperArgs, stream(GetOperReply)
28 | end
29 |
30 | Stub = Service.rpc_stub_class
31 | end
32 | module GRPCExec
33 |
34 | # TODO: add proto service documentation here
35 | class Service
36 |
37 | include GRPC::GenericService
38 |
39 | self.marshal_class_method = :encode
40 | self.unmarshal_class_method = :decode
41 | self.service_name = 'IOSXRExtensibleManagabilityService.gRPCExec'
42 |
43 | rpc :ShowCmdTextOutput, ShowCmdArgs, stream(ShowCmdTextReply)
44 | rpc :ShowCmdJSONOutput, ShowCmdArgs, stream(ShowCmdJSONReply)
45 | end
46 |
47 | Stub = Service.rpc_stub_class
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/lib/util/client/netconf.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2015 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | require_relative 'client'
16 |
17 | # Namespace for Cisco netconf-specific code
18 | class Cisco::Client::NETCONF < Cisco::Client
19 | end
20 |
21 | # Auto-load all Ruby files in the subdirectory
22 | Dir.glob(__dir__ + '/netconf/*.rb') { |file| require file }
23 |
--------------------------------------------------------------------------------
/lib/util/client/netconf/netconf.rb:
--------------------------------------------------------------------------------
1 | # June 2016, Chris Frisz
2 | #
3 | # Copyright (c) 2015-2016 Cisco and/or its affiliates.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | require_relative '../../yang'
18 | require 'rexml/document'
19 |
20 | module Cisco
21 | # Cisco module
22 | module Netconf
23 | # Netconf module performs conversion of REXML documents into
24 | # a format with types that are comparable to the output of
25 | # JSON.parse
26 | NC_BASE_1_0_NS = 'urn:ietf:params:xml:ns:netconf:base:1.0'
27 |
28 | def self.empty?(nc)
29 | !nc || nc.empty?
30 | end
31 |
32 | def self.convert_xml(xml)
33 | fail "#{xml} is not an XML document" unless xml.is_a?(REXML::Document)
34 | convert_xml_node(xml.root)
35 | end
36 |
37 | def self.convert_xml_node(node)
38 | fail "#{node} is not an XML node" unless node.is_a?(REXML::Element)
39 | out_hash = {}
40 | children = node.to_a
41 | if children.length == 1 && node.has_text?
42 | out_hash[node.name] = [children[0].value.strip]
43 | elsif !node.has_elements?
44 | out_hash[node.name] = []
45 | else
46 | out_hash[node.name] = children.map { |child| convert_xml_node(child) }
47 | end
48 | # Looking for operation=delete in the netconf:base:1.0 namespace
49 | if node.attributes.get_attribute_ns(NC_BASE_1_0_NS,
50 | 'operation').to_s == 'delete'
51 | out_hash[:delete] = :delete
52 | end
53 | out_hash
54 | end
55 |
56 | def self.convert_rexml_from_string(input)
57 | if empty?(input)
58 | out = {}
59 | else
60 | unless defined? @iw
61 | @iw = {}
62 | @iw[:ignore_whitespace_nodes] = :all
63 | end
64 | out = convert_xml(REXML::Document.new(input, @iw))
65 | end
66 | out
67 | end
68 |
69 | def self.insync_for_merge(target, current)
70 | !Yang.needs_something?(:merge,
71 | convert_rexml_from_string(target),
72 | convert_rexml_from_string(current))
73 | end
74 |
75 | def self.insync_for_replace(target, current)
76 | !Yang.needs_something?(:replace,
77 | convert_rexml_from_string(target),
78 | convert_rexml_from_string(current))
79 | end
80 | end # Netconf
81 | end # Cisco
82 |
--------------------------------------------------------------------------------
/lib/util/client/utils.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | #
3 | # January 2016, Glenn F. Matthews
4 | #
5 | # Copyright (c) 2015-2016 Cisco and/or its affiliates.
6 | #
7 | # Licensed under the Apache License, Version 2.0 (the "License");
8 | # you may not use this file except in compliance with the License.
9 | # You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing, software
14 | # distributed under the License is distributed on an "AS IS" BASIS,
15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | # See the License for the specific language governing permissions and
17 | # limitations under the License.
18 |
19 | require_relative '../constants'
20 | require_relative '../logger'
21 |
22 | # Utility methods for clients of various RPC formats
23 | class Cisco::Client
24 | # Make a best effort to convert a given input value to an Array.
25 | # Strings are split by newlines, and nil becomes an empty Array.
26 | def self.munge_to_array(val)
27 | val = [] if val.nil?
28 | val = val.split("\n") if val.is_a?(String)
29 | val
30 | end
31 |
32 | def munge_to_array(val)
33 | self.class.munge_to_array(val)
34 | end
35 |
36 | # Helper function that subclasses may use with get(data_format: :cli)
37 | # Method for working with hierarchical show command output such as
38 | # "show running-config". Searches the given multi-line string
39 | # for all matches to the given value query. If context is provided,
40 | # the matches will be filtered to only those that are located "under"
41 | # the given context sequence (as determined by indentation).
42 | #
43 | # @param cli_output [String] The body of text to search
44 | # @param context [*Regex] zero or more regular expressions defining
45 | # the parent configs to filter by.
46 | # @param value [Regex] The regular expression to match
47 | # @return [[String], nil] array of matching (sub)strings, else nil.
48 | #
49 | # @example Find all OSPF router names in the running-config
50 | # ospf_names = filter_cli(cli_output: running_cfg,
51 | # value: /^router ospf (\d+)/)
52 | #
53 | # @example Find all address-family types under the given BGP router
54 | # bgp_afs = filter_cli(cli_output: show_run_bgp,
55 | # context: [/^router bgp #{ASN}/],
56 | # value: /^address-family (.*)/)
57 | def self.filter_cli(cli_output: nil,
58 | context: nil,
59 | value: nil)
60 | return cli_output if cli_output.nil?
61 | context ||= []
62 | context.each { |filter| cli_output = find_subconfig(cli_output, filter) }
63 | return nil if cli_output.nil? || cli_output.empty?
64 | return cli_output if value.nil?
65 | value = to_regexp(value)
66 | match = cli_output.scan(value)
67 | return nil if match.empty?
68 | # find matches and return as array of String if it only does one match.
69 | # Otherwise return array of array.
70 | match.flatten! if match[0].is_a?(Array) && match[0].length == 1
71 | match
72 | end
73 |
74 | # Returns the subsection associated with the given
75 | # line of config
76 | # @param [String] the body of text to search
77 | # @param [Regex] the regex key of the config for which
78 | # to retrieve the subsection
79 | # @return [String, nil] the subsection of body, de-indented
80 | # appropriately, or nil if no such subsection exists.
81 | def self.find_subconfig(body, regexp_query)
82 | return nil if body.nil? || regexp_query.nil?
83 | regexp_query = to_regexp(regexp_query)
84 |
85 | rows = body.split("\n")
86 | match_row_index = rows.index { |row| regexp_query =~ row }
87 | return nil if match_row_index.nil?
88 |
89 | cur = match_row_index + 1
90 | subconfig = []
91 |
92 | until (/\A\s+.*/ =~ rows[cur]).nil? || cur == rows.length
93 | subconfig << rows[cur]
94 | cur += 1
95 | end
96 | return nil if subconfig.empty?
97 | # Strip an appropriate minimal amount of leading whitespace from
98 | # all lines in the subconfig
99 | min_leading = subconfig.map { |line| line[/\A */].size }.min
100 | subconfig = subconfig.map { |line| line[min_leading..-1] }
101 | subconfig.join("\n")
102 | end
103 |
104 | # Helper method for CLI getters
105 | #
106 | # Convert a string or array of strings to a Regexp or array thereof
107 | def self.to_regexp(input)
108 | if input.is_a?(Regexp)
109 | return input
110 | elsif input.is_a?(Array)
111 | return input.map { |item| to_regexp(item) }
112 | else
113 | # The string might be explicitly formatted as a regexp
114 | if input[0] == '/' && input[-1] == '/'
115 | # '/foo/' => %r{foo}
116 | return Regexp.new(input[1..-2])
117 | elsif input[0] == '/' && input[-2..-1] == '/i'
118 | # '/foo/i' => %r{foo}i
119 | return Regexp.new(input[1..-3], Regexp::IGNORECASE)
120 | else
121 | # 'foo' => %r{^foo$}i
122 | return Regexp.new("^#{input}$", Regexp::IGNORECASE)
123 | end
124 | end
125 | end
126 |
127 | # Helper method for calls into third-party code - suppresses Ruby warnings
128 | # for the given block since we have no control over that code.
129 | def self.silence_warnings(&block)
130 | warn_level = $VERBOSE
131 | $VERBOSE = nil
132 | result = block.call
133 | $VERBOSE = warn_level
134 | result
135 | end
136 | end
137 |
--------------------------------------------------------------------------------
/lib/util/constants.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | # Shared constants for the Cisco module
16 | module Cisco
17 | # Shared constants for the Cisco module
18 | module Util
19 | PLATFORMS = [
20 | # Cisco IOS XR
21 | :ios_xr
22 | ]
23 |
24 | DATA_FORMATS = [
25 | :cli, # Cisco CLI. Indentation is significant.
26 | :xml, # Netconf XML
27 | :yang_json, # YANG JSON
28 | ]
29 |
30 | NETCONF_SET_MODE = [
31 | :merge,
32 | :replace,
33 | :delete,
34 | ]
35 |
36 | YANG_SET_MODE = [
37 | :merge_config,
38 | :replace_config,
39 | :delete_config,
40 | ]
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/lib/util/environment.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | require 'yaml'
16 | require_relative 'logger'
17 |
18 | module Cisco
19 | module Util
20 | # Class representing the configuration environment
21 | class Environment
22 | @environments = {}
23 | @default_environment_name = 'default'
24 |
25 | # Autogenerate Cisco::Uitl::Environment.default_environment_name and
26 | # Cisco::Util::Environment.default_environment_name= class methods.
27 | class << self
28 | attr_accessor :default_environment_name
29 | end
30 |
31 | # We have three tiers of configuration:
32 | # 1) default (defined in this file)
33 | # 2) System-wide gem configuration in /etc/cisco_node_utils.yaml
34 | # 3) User configuration in ~/cisco_node_utils.yaml
35 |
36 | DEFAULT_ENVIRONMENT = {
37 | host: nil, # localhost by default
38 | port: nil, # only applicable to gRPC
39 | username: nil,
40 | password: nil,
41 | }
42 |
43 | def self.environments
44 | if @environments.empty?
45 | @environments = merge_config('/etc/cisco_yang.yaml',
46 | @environments)
47 | @environments = merge_config('~/cisco_yang.yaml',
48 | @environments)
49 | @environments.each do |name, config|
50 | Cisco::Logger.debug("Environment '#{name}': #{config}")
51 | end
52 | end
53 | @environments
54 | end
55 |
56 | def self.environment_names
57 | names = []
58 | environments.each do |name, _config|
59 | names << name
60 | end
61 | names
62 | end
63 |
64 | def self.merge_config(path, current_config)
65 | data = data_from_file(path)
66 | data.each do |name, config|
67 | # in case config is nil:
68 | config ||= {}
69 | # in case current_config has no entry for this name:
70 | current_config[name] ||= DEFAULT_ENVIRONMENT.clone
71 | # merge it on in!
72 | current_config[name].merge!(strings_to_symbols(config))
73 | end
74 | current_config
75 | end
76 |
77 | def self.data_from_file(path)
78 | begin
79 | path = File.expand_path(path)
80 | rescue ArgumentError => e
81 | # Can happen if path includes '~' but $HOME is not defined
82 | Cisco::Logger.debug "Failed to load #{path}: #{e}"
83 | return {}
84 | end
85 | unless File.file?(path)
86 | Cisco::Logger.debug "No file found at #{path}"
87 | return {}
88 | end
89 | unless File.readable?(path)
90 | Cisco::Logger.debug "No permissions to read #{path}"
91 | return {}
92 | end
93 | Cisco::Logger.debug "File found at #{path}"
94 | YAML.load_file(path)
95 | rescue Psych::SyntaxError => e
96 | Cisco::Logger.error("Error loading #{path}: #{e}")
97 | {}
98 | end
99 |
100 | def self.strings_to_symbols(hash)
101 | Hash[hash.map { |k, v| [k.to_sym, v] }]
102 | end
103 |
104 | def self.environment(name)
105 | name ||= @default_environment_name
106 | Cisco::Logger.debug("Getting environment '#{name}'")
107 | environments.fetch(name, nil)
108 | end
109 | end
110 | end
111 | end
112 |
--------------------------------------------------------------------------------
/lib/util/exceptions.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | # Add generic exception classes to the Cisco module
16 | # The hierarchy is:
17 | # RuntimeError
18 | # Cisco::CiscoError
19 | # Cisco::ClientError
20 | # Cisco::ConnectionRefused
21 | # Cisco::AuthenticationFailed
22 | # Cisco::RequestFailed
23 | # Cisco::CliError
24 | # Cisco::RequestNotSupported
25 | # Cisco::UnsupportedError
26 | module Cisco
27 | # Generic class for exceptions raised by this module
28 | class CiscoError < RuntimeError
29 | attr_reader :kwargs
30 |
31 | def initialize(message=nil, **kwargs)
32 | @kwargs = kwargs
33 | super(message)
34 | end
35 |
36 | def respond_to?(method_sym, include_private=false)
37 | @kwargs.key?(method_sym) ? true : super
38 | end
39 |
40 | def method_missing(method_sym, *args, **kwargs, &block)
41 | @kwargs.key?(method_sym) ? @kwargs[method_sym] : super
42 | end
43 | end
44 |
45 | # Exception class for fundamental client failures
46 | class ClientError < CiscoError
47 | end
48 |
49 | # ConnectionRefused means the server isn't listening
50 | class ConnectionRefused < ClientError
51 | end
52 |
53 | # AuthenticationFailed means we were able to connect but not login
54 | class AuthenticationFailed < ClientError
55 | end
56 |
57 | # Exception class for failures of a specific request to the client
58 | class RequestFailed < CiscoError
59 | end
60 |
61 | # Extension of RequestFailed class specifically for CLI errors
62 | class CliError < RequestFailed
63 | def initialize(message=nil,
64 | clierror: nil,
65 | rejected_input: nil,
66 | successful_input: [],
67 | **kwargs)
68 | unless message
69 | if rejected_input.is_a?(Array)
70 | if rejected_input.length > 1
71 | message = "The following commands were rejected:\n"
72 | message += " #{rejected_input.join("\n ")}\n"
73 | else
74 | message = "The command '#{rejected_input.first}' was rejected "
75 | end
76 | else
77 | message = "The command '#{rejected_input}' was rejected "
78 | end
79 | message += "with error:\n#{clierror}"
80 | end
81 | super(message,
82 | :clierror => clierror,
83 | :rejected_input => rejected_input,
84 | :successful_input => successful_input,
85 | **kwargs)
86 | end
87 | end
88 |
89 | # Extension of RequestFailed class specifically for YANG errors
90 | class YangError < RequestFailed
91 | def initialize(message=nil,
92 | error: nil,
93 | rejected_input: nil,
94 | successful_input: [],
95 | **kwargs)
96 | unless message
97 | if rejected_input.is_a?(Array)
98 | if rejected_input.length > 1
99 | message = "The following configs were rejected:\n"
100 | message += " #{rejected_input.join("\n ")}\n"
101 | else
102 | message = "The config '#{rejected_input.first}' was rejected "
103 | end
104 | else
105 | message = "The config '#{rejected_input}' was rejected "
106 | end
107 |
108 | message += "with error:\n#{error}"
109 | end
110 | super(message,
111 | :error => error,
112 | :rejected_input => rejected_input,
113 | :successful_input => successful_input,
114 | **kwargs)
115 | end
116 | end
117 |
118 | # RequestNotSupported means we made a request that was validly
119 | # constructed but includes options that are unsupported.
120 | #
121 | # An example would be requesting structured output on a CLI command
122 | # that only supports ASCII output.
123 | class RequestNotSupported < RequestFailed
124 | end
125 |
126 | # Exception class raised by CommandReference to indicate that
127 | # a particular feature/attribute is explicitly excluded on the given node.
128 | class UnsupportedError < CiscoError
129 | def initialize(feature, name, oper=nil, msg=nil)
130 | message = "Feature '#{feature}'"
131 | message += ", attribute '#{name}'" unless name.nil?
132 | message += ", operation '#{oper}'" unless oper.nil?
133 | message += ' is unsupported on this node'
134 | message += ": #{msg}" unless msg.nil?
135 | super(message, feature: feature, name: name, oper: oper)
136 | end
137 | end
138 | end
139 |
--------------------------------------------------------------------------------
/lib/util/logger.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | require 'logger'
16 |
17 | # Ensure module Cisco is defined
18 | module Cisco
19 | end
20 |
21 | # Module for logging. Will use the Puppet logging module if
22 | # available, otherwise will create a Logger to use
23 | module Cisco::Logger
24 | module_function
25 |
26 | # Figure out what provider logging utility we
27 | # should use: Puppet or Ruby Logger/STDOUT/INFO.
28 | if defined? (Puppet::Util::Logging)
29 | @@logger = Puppet # rubocop:disable Style/ClassVars
30 | def error(string)
31 | @@logger.err(string)
32 | end
33 |
34 | def warn(string)
35 | @@logger.warning(string)
36 | end
37 | else
38 | @@logger = Logger.new(STDOUT) # rubocop:disable Style/ClassVars
39 | @@logger.level = Logger::INFO
40 |
41 | def level
42 | @@logger.level
43 | end
44 |
45 | def level=(level)
46 | @@logger.level = level
47 | end
48 |
49 | def error(string)
50 | @@logger.error(string)
51 | end
52 |
53 | def warn(string)
54 | @@logger.warn(string)
55 | end
56 | end
57 |
58 | def debug(string)
59 | @@logger.debug(string)
60 | end
61 |
62 | def info(string)
63 | @@logger.info(string)
64 | end
65 | end # module
66 |
--------------------------------------------------------------------------------
/lib/util/node.rb:
--------------------------------------------------------------------------------
1 | # Cisco node helper class. Abstracts away the details of the underlying
2 | # transport (grpc/netconf) and provides various convenience methods.
3 |
4 | # Copyright (c) 2016 Cisco and/or its affiliates.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 |
18 | require_relative 'client'
19 | require_relative 'client/client'
20 | require_relative 'exceptions'
21 | require_relative 'logger'
22 |
23 | # Add node management classes and APIs to the Cisco namespace.
24 | module Cisco
25 | # class Cisco::Node
26 | # Pseudo-singleton representing the network node (switch/router) that is
27 | # running this code. The singleton is lazily instantiated, meaning that
28 | # it doesn't exist until some client requests it (with Node.instance(...))
29 | class Node
30 | @instance_hash = {}
31 | @instance = nil
32 |
33 | # Here and below are implementation details and private APIs that most
34 | # providers shouldn't need to know about or use.
35 |
36 | attr_reader :client
37 |
38 | # Return a node instance that wraps a client of the specified class.
39 | def self.instance(client_class=nil)
40 | return @instance_hash[client_class] ||= new(client_class) unless client_class.nil?
41 |
42 | # just return one of the cached instances
43 | return @instance_hash[hash.keys[0]] unless @instance_hash.empty?
44 |
45 | # no nodes currently cached, so create one
46 | debug 'Attempting to create a client (type not specified)...'
47 | env_names = Cisco::Util::Environment.environment_names
48 |
49 | Cisco::Client.clients.each do |c|
50 | client_env_name = Cisco::Client.environment_name(c)
51 | if env_names.include?(client_env_name)
52 | debug "Environment configuration found for client #{c}"
53 | return instance(c)
54 | end
55 | end
56 |
57 | error 'No clients configured'
58 | end
59 |
60 | def self.instance_exists(client_class)
61 | !@instance_hash[client_class].nil?
62 | end
63 |
64 | def initialize(client_class)
65 | @client = Cisco::Client.create(client_class)
66 | end
67 |
68 | def to_s
69 | client.to_s
70 | end
71 |
72 | def inspect
73 | "Node: client:'#{client.inspect}'"
74 | end
75 |
76 | # Send a config command to the device.
77 | # @raise [Cisco::RequestFailed] if any command is rejected by the device.
78 | def set(**kwargs)
79 | @client.set(**kwargs)
80 | end
81 |
82 | # Send a show command to the device.
83 | # @raise [Cisco::RequestFailed] if any command is rejected by the device.
84 | def get(**kwargs)
85 | @client.get(**kwargs)
86 | end
87 |
88 | # Merge the specified config with the running config on the device.
89 | # using netconf
90 | def merge_netconf(config)
91 | @client.set(values: config, mode: :merge)
92 | end
93 |
94 | # Replace the running config on the device with the specified
95 | # config using netconf client.
96 | def replace_netconf(config)
97 | @client.set(values: config, mode: :replace)
98 | end
99 |
100 | # Retrieve config from the device for the specified path using netconf.
101 | def get_netconf(xpath)
102 | @client.get(command: xpath)
103 | end
104 |
105 | # Merge the specified JSON YANG config with the running config on
106 | # the device.
107 | def merge_yang(yang)
108 | @client.set(values: yang, mode: :merge_config)
109 | end
110 |
111 | # Replace the running config on the device with the specified
112 | # JSON YANG config.
113 | def replace_yang(yang)
114 | @client.set(values: yang,
115 | mode: :replace_config)
116 | end
117 |
118 | # Delete the specified JSON YANG config from the device.
119 | def delete_yang(yang)
120 | @client.set(values: yang, mode: :delete_config)
121 | end
122 |
123 | # Retrieve JSON YANG config from the device for the specified path.
124 | def get_yang(yang_path)
125 | @client.get(command: yang_path)
126 | end
127 |
128 | # Retrieve JSON YANG operational data for the specified path.
129 | def get_yang_oper(yang_path)
130 | @client.get(command: yang_path, mode: :get_oper)
131 | end
132 |
133 | # @return [String] such as "Cisco Nexus Operating System (NX-OS) Software"
134 | def os
135 | @client.os
136 | end
137 |
138 | # @return [String] such as "6.0(2)U5(1) [build 6.0(2)U5(0.941)]"
139 | def os_version
140 | @client.os_version
141 | end
142 |
143 | # @return [String] such as "Nexus 3048 Chassis"
144 | def product_description
145 | @client.product_description
146 | end
147 |
148 | # @return [String] such as "N3K-C3048TP-1GE"
149 | def product_id
150 | @client.product_id
151 | end
152 |
153 | # @return [String] such as "V01"
154 | def product_version_id
155 | @client.product_version_id
156 | end
157 |
158 | # @return [String] such as "FOC1722R0ET"
159 | def product_serial_number
160 | @client.product_serial_number
161 | end
162 |
163 | # @return [String] such as "bxb-oa-n3k-7"
164 | def host_name
165 | @client.host_name
166 | end
167 |
168 | # @return [String] such as "example.com"
169 | def domain_name
170 | @client.domain_name
171 | end
172 |
173 | # @return [Integer] System uptime, in seconds
174 | def system_uptime
175 | @client.system_uptime
176 | end
177 |
178 | # @return [String] timestamp of last reset time
179 | def last_reset_time
180 | @client.get_last_reset_time
181 | end
182 |
183 | # @return [String] such as "Reset Requested by CLI command reload"
184 | def last_reset_reason
185 | @client.get_last_reset_reason
186 | end
187 |
188 | # @return [Float] combined user/kernel CPU utilization
189 | def system_cpu_utilization
190 | @client.system_cpu_utilization
191 | end
192 |
193 | # @return [String] such as
194 | # "bootflash:///n3000-uk9-kickstart.6.0.2.U5.0.941.bin"
195 | def boot
196 | @client.get_boot
197 | end
198 |
199 | # @return [String] such as
200 | # "bootflash:///n3000-uk9.6.0.2.U5.0.941.bin"
201 | def system
202 | @client.system
203 | end
204 | end
205 | end
206 |
--------------------------------------------------------------------------------
/lib/util/node_util.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | require_relative 'node'
16 | require_relative 'exceptions'
17 |
18 | module Cisco
19 | # NodeUtil - generic functionality for node utility subclasses to use
20 | class NodeUtil
21 | def self.node
22 | @node ||= Cisco::Node.instance
23 | end
24 |
25 | def node
26 | self.class.node
27 | end
28 |
29 | def self.client
30 | node.client
31 | end
32 |
33 | def client
34 | node.client
35 | end
36 |
37 | def self.supports?(api)
38 | client.supports?(api)
39 | end
40 |
41 | def supports?(api)
42 | client.supports?(api)
43 | end
44 |
45 | def self.platform
46 | client.platform
47 | end
48 |
49 | def platform
50 | client.platform
51 | end
52 |
53 | def get(**kwargs)
54 | node.get(**kwargs)
55 | rescue Cisco::RequestFailed => e
56 | e2 = e.class.new("[#{self}] #{e}", **e.kwargs)
57 | e2.set_backtrace(e.backtrace)
58 | raise e2
59 | end
60 |
61 | def ios_xr?
62 | platform == :ios_xr
63 | end
64 |
65 | def nexus?
66 | platform == :nexus
67 | end
68 | end
69 | end
70 |
--------------------------------------------------------------------------------
/lib/util/platform.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | require_relative 'node_util'
16 |
17 | module Cisco
18 | # Platform - class for gathering platform hardware and software information
19 | class Platform < NodeUtil
20 | # XR: 6.1.1.04I
21 | def self.image_version
22 | config_get('show_version', 'version')
23 | end
24 |
25 | # ex: 'n3500-uk9.6.0.2.A3.0.40.bin'
26 | def self.system_image
27 | config_get('show_version', 'boot_image')
28 | end
29 |
30 | def self.system_time
31 | client.system_time
32 | end
33 |
34 | def self.host_name
35 | client.host_name
36 | end
37 |
38 | def self.product_id
39 | client.product_id
40 | end
41 |
42 | def self.system
43 | client.system
44 | end
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/lib/util/show_running_yang.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 | # This is a utility to output the current state of an XR configuration.
16 | # In order to run, this utility needs access to one or more *.yang files
17 | # (found in the /pkg/yang directory on the XR box, as well as from other
18 | # sources).
19 |
20 | require 'optparse'
21 | require_relative 'yang_accessor'
22 |
23 | # Utility class to output the current state of an XR configuration.
24 |
25 | options = {}
26 | optparse = OptionParser.new do |opts|
27 | opts.banner = 'Usage: ruby [path]show_running_yang.rb [options] [file_or_directory_path]'
28 |
29 | opts.on('-m', '--manifest', 'Output config in a form suitable '\
30 | 'for inclusion in a Puppet manifest') do |_arg|
31 | options[:manifest] = true
32 | end
33 |
34 | opts.on('-o', '--oper',
35 | 'Retrieve operational data instead of configuration '\
36 | '(experimental; use at own risk)') do
37 | options[:oper] = true
38 | end
39 |
40 | opts.on('-c', '--client CLIENT', 'The client to use to connect.',
41 | 'grpc|netconf (defaults to grpc') do |client|
42 | options[:client] = client
43 | end
44 |
45 | # opts.on('-e', '--environment node', 'The node in cisco_node_utils.yaml '\
46 | # 'from which to retrieve data') do |env|
47 | # options[:environment] = env
48 | # end
49 |
50 | opts.on('-d', '--debug', 'Enable debug-level logging') do
51 | Cisco::Logger.level = Logger::DEBUG
52 | end
53 |
54 | opts.on('-v', '--verbose', 'Enable verbose messages') do
55 | options[:verbose] = true
56 | end
57 |
58 | opts.on('-h', '--help', 'Print this help') do
59 | puts optparse
60 | exit(0)
61 | end
62 | end
63 | optparse.parse!
64 |
65 | if options[:oper] && options[:manifest]
66 | STDERR.puts '!! Operational data cannot be set in a manifest, '\
67 | 'so option -m does not make sense in conjunction with -o.'
68 | exit(-1)
69 | end
70 |
71 | case options[:client]
72 | when 'netconf'
73 | options[:client_class] = Cisco::Client::NETCONF
74 | when 'grpc', nil
75 | options[:client_class] = Cisco::Client::GRPC
76 | else
77 | STDERR.puts "!! Invalid client specified: #{options[:client]}"
78 | exit(-1)
79 | end
80 |
81 | # If there is a single ARGV left, use is as the file/dir path
82 | if ARGV.length == 1
83 | options[:path] = ARGV[0]
84 | elsif ARGV.length > 1
85 | puts optparse
86 | exit(-1)
87 | end
88 |
89 | ya = Cisco::YangAccessor.new
90 | ya.process(options)
91 |
--------------------------------------------------------------------------------
/lib/util/yang.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | require 'json'
16 |
17 | module Cisco
18 | # The Yang class is a container for the needs_something? logic, which
19 | # attempts to determine if a given operation and data structures
20 | # will require setting the configuration of the remote device.
21 | class Yang
22 | # Is the specified yang string empty?
23 | def self.empty?(yang)
24 | !yang || yang.empty?
25 | end
26 |
27 | # Given a current and target YANG configuration, returns true if
28 | # the configurations are in-sync, relative to a "merge_config" action
29 | def self.insync_for_merge?(target, current)
30 | target_hash = self.empty?(target) ? {} : JSON.parse(target)
31 | current_hash = self.empty?(current) ? {} : JSON.parse(current)
32 |
33 | !needs_something?(:merge, target_hash, current_hash)
34 | end
35 |
36 | # Given a current and target YANG configuration, returns true if
37 | # the configuration are in-sync, relative to a "replace_config" action
38 | def self.insync_for_replace?(target, current)
39 | target_hash = self.empty?(target) ? {} : JSON.parse(target)
40 | current_hash = self.empty?(current) ? {} : JSON.parse(current)
41 |
42 | !needs_something?(:replace, target_hash, current_hash)
43 | end
44 |
45 | # usage:
46 | # needs_something?(op, target, run)
47 | #
48 | # op - symbol - If value is not :replace, it's assumed to be :merge.
49 | # Indicates to the function whether to check for a
50 | # possible merge vs. replace
51 | #
52 | # target - JSON - JSON tree representing target configuration
53 | #
54 | # run - JSON - JSON tree representing target configuration
55 | #
56 | #
57 | # Needs merge will determine if target and run differ
58 | # sufficiently to necessitate running the merge command.
59 | #
60 | # The logic here amounts to determining if target is a subtree
61 | # of run, with a tiny bit of domain trickiness surrounding
62 | # elements that are arrays that contain a single nil element
63 | # that is required for "creating" certain configuration elements.
64 | #
65 | # There are ultimately 3 different types of elements in a json
66 | # tree. Hashes, Arrays, and leaves. While hashes and array values
67 | # are organized with an order, the logic here ignores the order.
68 | # In fact, it specifically attempts to match assuming order
69 | # doesn't matter. This is largely to allow users some freedom
70 | # in specifying the config in the manifest. The gRPC interface
71 | # doesn't seem to care about order. If that changes, then so
72 | # should this code.
73 | #
74 | # Arrays and Hashes are compared by iterating over every element
75 | # in target, and ensuring it is within run.
76 | #
77 | # Leaves are directly compared for equality, excepting the
78 | # condition that the target leaf is in fact an array with one
79 | # element that is nil.
80 | #
81 | # Needs replace will determine if target and run differ
82 | # sufficiently to necessitate running the replace command.
83 | #
84 | # The logic is the same as merge, except when comparing
85 | # hashes, if the run hash table has elements that are not
86 | # in target, we ultimately indicate that replace is needed
87 | def self.needs_something?(op, target, run)
88 | !hash_equiv?(op, target, run)
89 | end
90 |
91 | def self.nil_array(elt)
92 | elt.nil? || (elt.is_a?(Array) && elt.length == 1 && elt[0].nil?)
93 | end
94 |
95 | def self.sub_elt(op, target, run)
96 | if target.is_a?(Hash) && run.is_a?(Hash)
97 | hash_equiv?(op, target, run)
98 | elsif target.is_a?(Array) && run.is_a?(Array)
99 | array_equiv?(op, target, run)
100 | else
101 | delete = target.is_a?(Hash) && (target[:delete] == :delete)
102 | delete || target == run || nil_array(target)
103 | end
104 | end
105 |
106 | def self.array_equiv?(op, target, run)
107 | n = target.length
108 | # We need to count the number of times that we've had a miss because
109 | # the target element was marked delete
110 | no_op_delete_count = 0
111 | loop = lambda do |i|
112 | if i == n
113 | if op == :replace
114 | # NB: We could end up with arrays that have only nil in them
115 | # sometimes, so correct for that, then normalize for the
116 | # number of elements that are no-ops because they are marked
117 | # delete
118 | rl = run.clone.delete_if(&:nil?).length
119 | tl = target.clone.delete_if(&:nil?).length - no_op_delete_count
120 | rl == tl
121 | else
122 | true
123 | end
124 | else
125 | target_elt = target[i]
126 | if target_elt.is_a?(Hash) && (target_elt[:delete] == :delete)
127 | target_elt = target_elt.clone
128 | target_elt.delete(:delete)
129 | delete = true
130 | end
131 | run_elt = run.find { |re| sub_elt(op, target_elt, re) }
132 |
133 | if run_elt.nil? && !nil_array(target_elt)
134 | if delete
135 | no_op_delete_count += 1
136 | loop.call(i + 1)
137 | else
138 | target_elt.nil?
139 | end
140 | else
141 | if delete
142 | false
143 | else
144 | loop.call(i + 1)
145 | end
146 | end
147 | end
148 | end
149 | loop.call(0)
150 | end
151 |
152 | def self.hash_equiv?(op, target, run)
153 | keys = target.keys
154 | n = keys.length
155 | loop = lambda do |i|
156 | if i == n
157 | if op == :replace
158 | run.keys.length == target.keys.length
159 | else
160 | true
161 | end
162 | else
163 | k = keys[i]
164 | run_v = run[k]
165 | target_v = target[k]
166 | if run_v.nil? && !nil_array(target_v)
167 | false
168 | else
169 | sub_elt(op, target_v, run_v) && loop.call(i + 1)
170 | end
171 | end
172 | end
173 | loop.call(0)
174 | end
175 | end
176 | end
177 |
--------------------------------------------------------------------------------
/lib/util/yang_accessor.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 | # This is a utility to output the current state of an XR configuration.
16 | # In order to run, this utility needs access to one or more *.yang files
17 | # (found in the /pkg/yang directory on the XR box, as well as from other
18 | # sources).
19 |
20 | require 'optparse'
21 | require_relative 'node_util'
22 |
23 | module Cisco
24 | # Utility class to output the current state of an XR configuration.
25 | class YangAccessor
26 | def process(options)
27 | @options = options
28 |
29 | # suppress stdout
30 | old_stdout = $stdout
31 | $stdout = StringIO.new if @options[:quiet]
32 |
33 | client # initialize the client
34 |
35 | dir_or_file = options[:path] || '/pkg/yang'
36 |
37 | file = nil
38 | dir = nil
39 |
40 | if File.exist?(dir_or_file)
41 | if File.directory?(dir_or_file)
42 | dir = dir_or_file
43 | else
44 | file = dir_or_file
45 | end
46 | else
47 | puts "Directory or file not found: #{dir_or_file}"
48 | exit(-1)
49 | end
50 |
51 | puts "File found: #{file}" if file
52 | puts "Directory found: #{dir}" if dir
53 | puts 'Searching for configuration data...' unless @options[:oper]
54 | puts 'Searching for operational data...' if @options[:oper]
55 |
56 | t1 = Time.now
57 |
58 | @files = 0
59 | @cnrs = 0
60 | @errors = 0
61 |
62 | targets = []
63 |
64 | if file
65 | targets.concat(process_file(file))
66 | @files += 1
67 | else
68 | Dir.glob(dir + '/*.yang').sort.each do |item|
69 | targets.concat(process_file(item))
70 | @files += 1
71 | end
72 | end
73 |
74 | delta = Time.now - t1
75 | puts '---------------------------------------------'
76 | puts "Files Processed: #{@files}"
77 | puts "Containers Processed: #{@cnrs}"
78 | puts "Errors: #{@errors}"
79 | puts "Time: #{delta.round(2)} seconds"
80 | puts # spacer
81 |
82 | $stdout = old_stdout
83 |
84 | targets
85 | end
86 |
87 | def targets(options)
88 | options[:parse_only] = true
89 | options[:quiet] = true
90 |
91 | process(options)
92 | end
93 |
94 | def process_file(file)
95 | @module = nil
96 | @namespace = nil
97 | @containers = {}
98 |
99 | targets = []
100 | puts "[ Processing file #{file} ]" if @options[:verbose]
101 |
102 | File.open(file) do |f|
103 | loop do
104 | break if (line = f.gets).nil?
105 | target = process_line(line, f)
106 | targets << target if target
107 | end
108 | end
109 | targets
110 | end
111 |
112 | def process_line(line, file)
113 | if @module.nil?
114 | @module = Regexp.last_match(1) if line =~ /^module (.+) {/
115 | elsif @namespace.nil?
116 | # handle split lines (move this to the process_file line
117 | # loop if more general handling is needed)
118 | until (m = line.match(/(.*)"\+$/)).nil?
119 | line2 = file.gets
120 | break if line2.nil?
121 | line2.match(/^\s*"(.*)/) do |m2|
122 | line = m[1] + m2[1]
123 | end
124 | end
125 |
126 | @namespace = Regexp.last_match(1) if line =~ /^ namespace "(.+)"/
127 | elsif line =~ /^ container (.+) {/
128 | return process_root_container(@module, @namespace, Regexp.last_match(1), file)
129 | end
130 | nil
131 | end
132 |
133 | def process_root_container(module_name, namespace, container, file)
134 | operation = :get_config
135 | loop do
136 | line = file.gets
137 | break if !line || line.strip == ''
138 | if line =~ /^ config false;/ # abort cnr if not config
139 | operation = :get_oper
140 | break
141 | end
142 | end
143 |
144 | # only output config or operational data, depending on options
145 | if @options[:oper]
146 | return if operation == :get_config
147 | else
148 | return unless operation == :get_config
149 | end
150 |
151 | # guard against duplicate containers
152 | if @containers.key?(container)
153 | puts "[ Duplicate container #{container} ]" if @options[:verbose]
154 | return
155 | end
156 |
157 | yang_target = client.yang_target(module_name, namespace, container)
158 |
159 | @containers[container] = true
160 | @cnrs += 1
161 |
162 | unless @options[:parse_only]
163 | begin
164 | puts "[ Processing container #{container}... ]"\
165 | if @options[:verbose]
166 | data = client.get(data_format: :yang_json,
167 | command: yang_target,
168 | mode: operation)
169 | if data && data.strip.length > 0
170 | puts '[ Data returned ]'\
171 | if @options[:verbose]
172 | output_data(yang_target, data)
173 | else
174 | puts '[ No data returned ]'\
175 | if @options[:verbose]
176 | end
177 | rescue Cisco::ClientError, Cisco::YangError => e
178 | @errors += 1
179 | puts "!!Error on '#{yang_target}': #{e}"
180 | debug e.backtrace
181 | puts # spacer
182 | end
183 | end
184 |
185 | yang_target
186 | end
187 |
188 | def output_data(yang_target, data)
189 | if @options[:manifest]
190 | if @options[:client_class] == Cisco::Client::GRPC
191 | puppet_type = 'cisco_yang'
192 | ensure_prop = " ensure => present,\n"
193 | else
194 | puppet_type = 'cisco_yang_netconf'
195 | ensure_prop = ''
196 | end
197 |
198 | puts " #{puppet_type} { '#{yang_target}':\n#{ensure_prop}"\
199 | " source => '#{data.chomp.gsub(/\n/, "\n ")}'\n"\
200 | ' }'
201 | else
202 | puts data
203 | end
204 | puts # spacer
205 | end
206 |
207 | def client
208 | unless @client
209 | @client = Cisco::Client.create(@options[:client_class])
210 |
211 | puts "[ Connected to client: #{@client} ]"\
212 | if @options[:verbose]
213 | end
214 | @client
215 | rescue Cisco::AuthenticationFailed
216 | abort 'Unauthorized to connect'
217 | rescue Cisco::ClientError, TypeError, ArgumentError => e
218 | abort "Error in establishing connection: #{e}"
219 | end
220 | end # YangAccessor
221 | end # Cisco
222 |
--------------------------------------------------------------------------------
/metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ciscoeng-ciscoyang",
3 | "version": "1.0.2",
4 | "author": "ciscoeng",
5 | "summary": "Cisco IOS-XR configuration management via YANG.",
6 | "license": "Apache-2.0",
7 | "source": "https://github.com/cisco/cisco-yang-puppet-module",
8 | "project_page": "https://github.com/cisco/cisco-yang-puppet-module",
9 | "issues_url": "https://github.com/cisco/cisco-yang-puppet-module/issues",
10 | "dependencies": [
11 | ],
12 | "requirements": [
13 | {
14 | "name": "pe",
15 | "version_requirement": "2015.3.x"
16 | },
17 | {
18 | "name": "puppet",
19 | "version_requirement": "4.x"
20 | }
21 | ],
22 | "data_provider": null,
23 | "tags": [
24 | "cisco",
25 | "ios-xr",
26 | "xr",
27 | "yang"
28 | ],
29 | "operatingsystem_support": [
30 | {
31 | "operatingsystem": "IOS-XR",
32 | "operatingsystemrelease": [
33 | "6.1.x", "6.2.x"
34 | ]
35 | }
36 | ]
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/tests/.rubopcop.yml:
--------------------------------------------------------------------------------
1 | inherit_from: ../.rubocop.yml
2 |
3 | # Code complexity metrics for the tests/ subdirectory
4 |
5 | Metrics/AbcSize:
6 | Max: 158 # ouch!
7 |
8 | Metrics/CyclomaticComplexity:
9 | Max: 15
10 |
11 | Metrics/MethodLength:
12 | Max: 100
13 |
14 | Metrics/PerceivedComplexity:
15 | Max: 17
16 |
17 | Metrics/LineLength:
18 | Max: 120
19 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang/model_tests/test_cisco_ios_xr_infra_rsi_cfg_vrf_groups.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../../lib/yang_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | resource_name: 'cisco_yang',
24 | os: 'ios_xr',
25 | os_version: '6.1.1',
26 | }
27 |
28 | # skip entire file if os, version, etc. don't match
29 | skip_unless_supported(tests)
30 |
31 | # define a test (or tests)
32 | # (e.g. description, title, manifest)
33 | tests[:vrf_groups] = {
34 | desc: 'Configure VRFs',
35 | title: '{"Cisco-IOS-XR-infra-rsi-cfg:vrf-groups": [null]}',
36 | manifest_props: {
37 | source: '
38 | {"Cisco-IOS-XR-infra-rsi-cfg:vrf-groups":
39 | {
40 | "vrf-group": [
41 | {
42 | "vrf-group-name": "TEST-GROUP",
43 | "enable": [null],
44 | "vrfs": {
45 | "vrf": [
46 | {
47 | "vrf-name": "ORANGE"
48 | }
49 | ]
50 | }
51 | }
52 | ]
53 | }
54 | }',
55 | mode: 'replace',
56 | },
57 | }
58 |
59 | #################################################################
60 | # Execute the test
61 | #################################################################
62 |
63 | test_name 'Model Test' do
64 | # a simple run with pre/post clean
65 | # (reference our test above using the key)
66 | test_harness_run_clean(tests, :vrf_groups)
67 | end
68 |
69 | # report on skipped tests
70 | skipped_tests_summary(tests)
71 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang/model_tests/test_cisco_ios_xr_infra_rsi_cfg_vrfs.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../../lib/yang_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | resource_name: 'cisco_yang',
24 | os: 'ios_xr',
25 | os_version: '6.1.1',
26 | }
27 |
28 | # skip entire file if os, version, etc. don't match
29 | skip_unless_supported(tests)
30 |
31 | # define a test (or tests)
32 | # (e.g. description, title, manifest)
33 | tests[:vrfs] = {
34 | desc: 'Configure VRFs',
35 | title: '{"Cisco-IOS-XR-infra-rsi-cfg:vrfs": [null]}',
36 | manifest_props: {
37 | source: '
38 | {"Cisco-IOS-XR-infra-rsi-cfg:vrfs":
39 | {
40 | "vrf":
41 | [
42 | {
43 | "vrf-name":"BLUE",
44 | "create":[null]
45 | },
46 | {
47 | "vrf-name":"GREEN",
48 | "create":[null]
49 | }
50 | ]
51 | }
52 | }',
53 | mode: 'replace',
54 | },
55 | }
56 |
57 | #################################################################
58 | # Execute the test
59 | #################################################################
60 |
61 | test_name 'Model Test' do
62 | # a simple run with pre/post clean
63 | # (reference our test above using the key)
64 | test_harness_run_clean(tests, :vrfs)
65 | end
66 |
67 | # report on skipped tests
68 | skipped_tests_summary(tests)
69 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang/model_tests/test_cisco_ios_xr_ipv4_bgp_cfg_bgp.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../../lib/yang_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | resource_name: 'cisco_yang',
24 | os: 'ios_xr',
25 | os_version: '6.1.1',
26 | }
27 |
28 | # skip entire file if os, version, etc. don't match
29 | skip_unless_supported(tests)
30 |
31 | # Define a test for the bgp YANG container.
32 | tests[:bgp] = {
33 | desc: 'Configure BGP',
34 | title: '{"Cisco-IOS-XR-ipv4-bgp-cfg:bgp": [null]}',
35 | manifest_props: {
36 | source: File.read(File.expand_path('../yang/bgp.yang', __FILE__)),
37 | mode: 'replace',
38 | },
39 | }
40 |
41 | #################################################################
42 | # Execute the test
43 | #################################################################
44 |
45 | test_name 'Model Test' do
46 | # a simple run with pre/post clean
47 | # (reference our test above using the key)
48 | test_harness_run_clean(tests, :bgp)
49 | end
50 |
51 | # report on skipped tests
52 | skipped_tests_summary(tests)
53 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang/model_tests/yang/bgp.yang:
--------------------------------------------------------------------------------
1 | {
2 | "Cisco-IOS-XR-ipv4-bgp-cfg:bgp":{
3 | "instance":[
4 | {
5 | "instance-name":"default",
6 | "instance-as":[
7 | {
8 | "as":0,
9 | "four-byte-as":[
10 | {
11 | "as":55,
12 | "bgp-running":[null],
13 | "default-vrf":{
14 | "global":{
15 | "nsr":false,
16 | "global-timers":{
17 | "keepalive":60,
18 | "hold-time":120
19 | },
20 | "enforce-ibgp-out-policy":[null],
21 | "global-afs":{
22 | "global-af":[
23 | {
24 | "af-name":"ipv4-multicast",
25 | "enable":[null],
26 | "update-limit-address-family":256,
27 | "ebgp":{
28 | "paths-value":32,
29 | "unequal-cost":false,
30 | "selective":false,
31 | "order-by-igp-metric":false
32 | }
33 | }
34 | ]
35 | }
36 | },
37 | "bgp-entity":{
38 | "neighbors":{
39 | "neighbor":[
40 | {
41 | "neighbor-address":"5.5.5.5",
42 | "remote-as":{
43 | "as-xx":0,
44 | "as-yy":12
45 | },
46 | "bfd-enable-modes":"default",
47 | "ebgp-multihop":{
48 | "max-hop-count":10,
49 | "mpls-deactivation":false
50 | },
51 | "description":"Neighbor A",
52 | "msg-log-out":{
53 | "msg-buf-count":10
54 | }
55 | },
56 | {
57 | "neighbor-address":"6.6.6.6",
58 | "remote-as":{
59 | "as-xx":0,
60 | "as-yy":13
61 | },
62 | "description":"Neighbor B"
63 | }
64 | ]
65 | }
66 | }
67 | }
68 | }
69 | ]
70 | }
71 | ]
72 | }
73 | ]
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang/test_create_vrf.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../lib/yang_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | resource_name: 'cisco_yang',
24 | }
25 | tests[:create] = CREATE
26 |
27 | skip_unless_supported(tests)
28 |
29 | step 'Setup' do
30 | resource_absent_by_title(agent, 'cisco_yang', ROOT_VRF)
31 | end
32 |
33 | teardown do
34 | resource_absent_by_title(agent, 'cisco_yang', BLUE_VRF_WO_PROPERTY)
35 | end
36 |
37 | #################################################################
38 | # TEST CASE EXECUTION
39 | #################################################################
40 | test_name 'TestCase :: VRF Present' do
41 | id = :create
42 | tests[id][:ensure] = :present
43 | test_harness_run(tests, id)
44 | skipped_tests_summary(tests)
45 | end
46 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang/test_delete_properties.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../lib/yang_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | resource_name: 'cisco_yang',
24 | }
25 | tests[:delete_property] = DELETE_PROPERTY
26 |
27 | skip_unless_supported(tests)
28 |
29 | step 'Setup' do
30 | resource = {
31 | name: 'cisco_yang',
32 | title: BLUE_VRF_W_PROPERTY12,
33 | property: 'ensure',
34 | value: 'present',
35 | }
36 | resource_set(agent, resource, 'Create a VRF BLUE with properties.')
37 | end
38 |
39 | teardown do
40 | resource_absent_by_title(agent, 'cisco_yang', BLUE_VRF_W_PROPERTY2)
41 | end
42 |
43 | #################################################################
44 | # TEST CASE EXECUTION
45 | #################################################################
46 | test_name 'TestCase :: VRF Absent' do
47 | # -------------------------------------------------------------------
48 | id = :delete_property
49 | tests[id][:ensure] = :absent
50 | test_harness_run(tests, id)
51 | skipped_tests_summary(tests)
52 | # -------------------------------------------------------------------
53 | end
54 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang/test_delete_vrf.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../lib/yang_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | resource_name: 'cisco_yang',
24 | }
25 | tests[:delete] = DELETE
26 |
27 | skip_unless_supported(tests)
28 |
29 | step 'Setup' do
30 | resource = {
31 | name: 'cisco_yang',
32 | title: BLUE_VRF_WO_PROPERTY,
33 | property: 'ensure',
34 | value: 'present',
35 | }
36 | resource_set(agent, resource, 'Create a VRF BLUE.')
37 | end
38 |
39 | teardown do
40 | resource_absent_by_title(agent, 'cisco_yang', BLUE_VRF_WO_PROPERTY)
41 | end
42 |
43 | #################################################################
44 | # TEST CASE EXECUTION
45 | #################################################################
46 | test_name 'TestCase :: VRF Absent' do
47 | # -------------------------------------------------------------------
48 | id = :delete
49 | tests[id][:ensure] = :absent
50 | test_harness_run(tests, id)
51 | skipped_tests_summary(tests)
52 | # -------------------------------------------------------------------
53 | end
54 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang/test_file.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../lib/yang_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | resource_name: 'cisco_yang',
24 | }
25 | tests[:file_merge] = FILE_MERGE
26 | tests[:replace_merge] = FILE_REPLACE
27 |
28 | skip_unless_supported(tests)
29 |
30 | def dependency_manifest(_tests, _id)
31 | setup_manifest = \
32 | "file {'/root/temp':
33 | ensure => 'directory',
34 | }
35 |
36 | file { '/root/temp/vrfs.json':
37 | source => 'puppet:///modules/ciscoyang/models/defaults/vrfs.json'
38 | }"
39 | setup_manifest
40 | end
41 |
42 | step 'Setup' do
43 | resource_absent_by_title(agent, 'cisco_yang', ROOT_VRF)
44 | end
45 |
46 | teardown do
47 | resource_absent_by_title(agent, 'cisco_yang', ROOT_VRF)
48 | resource_absent_by_title(agent, 'file', '/root/temp/vrfs.json')
49 | end
50 |
51 | #################################################################
52 | # TEST CASE EXECUTION
53 | #################################################################
54 | test_name 'TestCase :: read config from vrfs.json file' do
55 | id = :file_merge
56 | tests[id][:ensure] = :present
57 | test_harness_run(tests, id)
58 |
59 | resource_absent_by_title(agent, 'cisco_yang', ROOT_VRF)
60 |
61 | id = :replace_merge
62 | tests[id][:ensure] = :present
63 | test_harness_run(tests, id)
64 |
65 | skipped_tests_summary(tests)
66 | end
67 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang/test_merge_properties_vrf.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../lib/yang_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | resource_name: 'cisco_yang',
24 | }
25 | tests[:merge12] = MERGE12
26 |
27 | skip_unless_supported(tests)
28 |
29 | step 'Setup' do
30 | resource_absent_by_title(agent, 'cisco_yang', ROOT_VRF)
31 | resource = {
32 | name: 'cisco_yang',
33 | title: BLUE_VRF_W_PROPERTY1,
34 | property: 'ensure',
35 | value: 'present',
36 | }
37 | resource_set(agent, resource, 'Create a VRF BLUE with property vpn id.')
38 | end
39 |
40 | teardown do
41 | resource_absent_by_title(agent, 'cisco_yang', BLUE_VRF_W_PROPERTY12)
42 | end
43 |
44 | #################################################################
45 | # TEST CASE EXECUTION
46 | #################################################################
47 | test_name 'TestCase :: Merge12 VRF BLUE' do
48 | id = :merge12
49 | tests[id][:ensure] = :present
50 | test_harness_run(tests, id)
51 | skipped_tests_summary(tests)
52 | end
53 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang/test_merge_vrf.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../lib/yang_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | resource_name: 'cisco_yang',
24 | }
25 | tests[:merge] = MERGE
26 |
27 | skip_unless_supported(tests)
28 |
29 | step 'Setup' do
30 | resource_absent_by_title(agent, 'cisco_yang', ROOT_VRF)
31 | resource = {
32 | name: 'cisco_yang',
33 | title: GREEN_VRF_WO_PROPERTY,
34 | property: 'ensure',
35 | value: 'present',
36 | }
37 | resource_set(agent, resource, 'Create a VRF GREEN.')
38 | end
39 |
40 | teardown do
41 | resource_absent_by_title(agent, 'cisco_yang', ROOT_VRF)
42 | end
43 |
44 | #################################################################
45 | # TEST CASE EXECUTION
46 | #################################################################
47 | test_name 'TestCase :: Merge VRF BLUE' do
48 | id = :merge
49 | tests[id][:ensure] = :present
50 | test_harness_run(tests, id)
51 | skipped_tests_summary(tests)
52 | end
53 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang/test_replace_properties_vrf.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../lib/yang_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | resource_name: 'cisco_yang',
24 | }
25 | tests[:replace12] = REPLACE12
26 |
27 | skip_unless_supported(tests)
28 |
29 | step 'Setup' do
30 | resource_absent_by_title(agent, 'cisco_yang', ROOT_VRF)
31 | resource = {
32 | name: 'cisco_yang',
33 | title: BLUE_VRF_W_PROPERTY1,
34 | property: 'ensure',
35 | value: 'present',
36 | }
37 | resource_set(agent, resource, 'Create a VRF BLUE with properties.')
38 | end
39 |
40 | teardown do
41 | resource_absent_by_title(agent, 'cisco_yang', ROOT_VRF)
42 | end
43 |
44 | #################################################################
45 | # TEST CASE EXECUTION
46 | #################################################################
47 | test_name 'TestCase :: VRF Present' do
48 | id = :replace12
49 | tests[id][:ensure] = :present
50 | test_harness_run(tests, id)
51 | skipped_tests_summary(tests)
52 | end
53 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang/test_replace_srlg.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../lib/yang_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | resource_name: 'cisco_yang',
24 | }
25 | tests[:replace_srlg] = REPLACE_SRLG
26 | tests[:create_srlg] = CREATE_SRLG
27 |
28 | skip_unless_supported(tests)
29 |
30 | step 'Setup' do
31 | resource_absent_by_title(agent, 'cisco_yang', ROOT_SRLG)
32 | end
33 |
34 | teardown do
35 | resource_absent_by_title(agent, 'cisco_yang', ROOT_SRLG)
36 | end
37 |
38 | #################################################################
39 | # TEST CASE EXECUTION
40 | #################################################################
41 | test_name 'TestCase :: Replace GE0 by GE0 updated' do
42 | id = :create_srlg
43 | tests[id][:ensure] = :present
44 | test_harness_run(tests, id)
45 |
46 | id = :replace_srlg
47 | tests[id][:ensure] = :present
48 | test_harness_run(tests, id)
49 | skipped_tests_summary(tests)
50 | end
51 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang/test_replace_vrf.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../lib/yang_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | resource_name: 'cisco_yang',
24 | }
25 | tests[:replace] = REPLACE
26 |
27 | skip_unless_supported(tests)
28 |
29 | step 'Setup' do
30 | resource_absent_by_title(agent, 'cisco_yang', ROOT_VRF)
31 | resource = {
32 | name: 'cisco_yang',
33 | title: GREEN_VRF_WO_PROPERTY,
34 | property: 'ensure',
35 | value: 'present',
36 | }
37 | resource_set(agent, resource, 'Create a VRF GREEN.')
38 | end
39 |
40 | teardown do
41 | resource_absent_by_title(agent, 'cisco_yang', ROOT_VRF)
42 | end
43 |
44 | #################################################################
45 | # TEST CASE EXECUTION
46 | #################################################################
47 | test_name 'TestCase :: VRF Present' do
48 | id = :replace
49 | tests[id][:ensure] = :present
50 | test_harness_run(tests, id)
51 | skipped_tests_summary(tests)
52 | end
53 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang_netconf/test_create_vrf.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../lib/yang_netconf_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | ensurable: false,
24 | resource_name: 'cisco_yang_netconf',
25 | }
26 | tests[:create] = NETCONF_CREATE
27 |
28 | skip_unless_supported(tests)
29 |
30 | step 'Setup' do
31 | clear_vrf
32 | end
33 |
34 | teardown do
35 | clear_vrf
36 | end
37 |
38 | #################################################################
39 | # TEST CASE EXECUTION
40 | #################################################################
41 | test_name 'TestCase :: VRF Present' do
42 | id = :create
43 | test_harness_run(tests, id)
44 | skipped_tests_summary(tests)
45 | end
46 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang_netconf/test_file_merge_vrf.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../lib/yang_netconf_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | ensurable: false,
24 | resource_name: 'cisco_yang_netconf',
25 | }
26 | tests[:file_merge] = NETCONF_FILE_MERGE
27 |
28 | skip_unless_supported(tests)
29 |
30 | step 'Setup' do
31 | clear_vrf
32 | on(agent, puppet_resource('file', '/root/temp/', 'ensure=directory'))
33 | on(agent, puppet_resource('file', \
34 | '/root/temp/vrfs.xml', \
35 | 'source="puppet:///modules/ciscoyang/models/defaults/vrfs.xml"', \
36 | 'ensure=present'))
37 | end
38 |
39 | teardown do
40 | clear_vrf
41 | on(agent, puppet_resource('file', \
42 | '/root/temp/vrfs.xml', \
43 | 'ensure=absent'))
44 | end
45 |
46 | #################################################################
47 | # TEST CASE EXECUTION
48 | #################################################################
49 | test_name 'TestCase :: read config from vrfs.xml file and merge with current config' do
50 | id = :file_merge
51 | test_harness_run(tests, id)
52 | skipped_tests_summary(tests)
53 | end
54 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang_netconf/test_file_replace_vrf.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../lib/yang_netconf_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | ensurable: false,
24 | resource_name: 'cisco_yang_netconf',
25 | }
26 | tests[:replace_merge] = NETCONF_FILE_REPLACE
27 |
28 | skip_unless_supported(tests)
29 |
30 | step 'Setup' do
31 | clear_vrf
32 | on(agent, puppet_resource('file', '/root/temp/', 'ensure=directory'))
33 | on(agent, puppet_resource('file', \
34 | '/root/temp/vrfs.xml', \
35 | 'source="puppet:///modules/ciscoyang/models/defaults/vrfs.xml"', \
36 | 'ensure=present'))
37 | end
38 |
39 | teardown do
40 | clear_vrf
41 | on(agent, puppet_resource('file', \
42 | '/root/temp/vrfs.xml', \
43 | 'ensure=absent'))
44 | end
45 |
46 | #################################################################
47 | # TEST CASE EXECUTION
48 | #################################################################
49 | test_name 'TestCase :: read config from vrfs.xml file and replace current config with it' do
50 | id = :replace_merge
51 | test_harness_run(tests, id)
52 | skipped_tests_summary(tests)
53 | end
54 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang_netconf/test_merge_properties_vrf.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../lib/yang_netconf_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | ensurable: false,
24 | resource_name: 'cisco_yang_netconf',
25 | }
26 | tests[:merge12] = NETCONF_MERGE12
27 |
28 | skip_unless_supported(tests)
29 |
30 | step 'Setup' do
31 | clear_vrf
32 | title_string = NETCONF_BLUE_VRF_W_PROPERTY1
33 | cmd = PUPPET_BINPATH + "resource cisco_yang_netconf '#{title_string}' mode=merge"
34 | on(agent, cmd)
35 | end
36 |
37 | teardown do
38 | clear_vrf
39 | end
40 |
41 | #################################################################
42 | # TEST CASE EXECUTION
43 | #################################################################
44 | test_name 'TestCase :: Merge12 VRF BLUE' do
45 | id = :merge12
46 | test_harness_run(tests, id)
47 | skipped_tests_summary(tests)
48 | end
49 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang_netconf/test_merge_vrf.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../lib/yang_netconf_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | ensurable: false,
24 | resource_name: 'cisco_yang_netconf',
25 | }
26 | tests[:merge] = NETCONF_MERGE
27 |
28 | skip_unless_supported(tests)
29 |
30 | step 'Setup' do
31 | clear_vrf
32 | title_string = NETCONF_GREEN_VRF_WO_PROPERTY
33 | cmd = PUPPET_BINPATH + "resource cisco_yang_netconf '#{title_string}' mode=merge"
34 | on(agent, cmd)
35 | end
36 |
37 | teardown do
38 | clear_vrf
39 | end
40 |
41 | #################################################################
42 | # TEST CASE EXECUTION
43 | #################################################################
44 | test_name 'TestCase :: Merge VRF BLUE' do
45 | id = :merge
46 | test_harness_run(tests, id)
47 | skipped_tests_summary(tests)
48 | end
49 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang_netconf/test_replace_properties_vrf.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../lib/yang_netconf_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | ensurable: false,
24 | resource_name: 'cisco_yang_netconf',
25 | }
26 | tests[:replace12] = NETCONF_REPLACE12
27 |
28 | skip_unless_supported(tests)
29 |
30 | step 'Setup' do
31 | clear_vrf
32 | title_string = NETCONF_BLUE_VRF_WO_PROPERTY
33 | cmd = PUPPET_BINPATH + "resource cisco_yang_netconf '#{title_string}' mode=merge"
34 | on(agent, cmd)
35 | end
36 |
37 | teardown do
38 | clear_vrf
39 | end
40 |
41 | #################################################################
42 | # TEST CASE EXECUTION
43 | #################################################################
44 | test_name 'TestCase :: VRF Present' do
45 | id = :replace12
46 | test_harness_run(tests, id)
47 | skipped_tests_summary(tests)
48 | end
49 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang_netconf/test_replace_srlg.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../lib/yang_netconf_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | ensurable: false,
24 | resource_name: 'cisco_yang_netconf',
25 | }
26 | tests[:replace_srlg] = NETCONF_REPLACE_SRLG
27 | tests[:create_srlg] = NETCONF_CREATE_SRLG
28 |
29 | skip_unless_supported(tests)
30 |
31 | step 'Setup' do
32 | clear_srlg
33 | end
34 |
35 | teardown do
36 | clear_srlg
37 | end
38 |
39 | #################################################################
40 | # TEST CASE EXECUTION
41 | #################################################################
42 | test_name 'TestCase :: Replace GE0 by GE0 updated' do
43 | id = :create_srlg
44 | test_harness_run(tests, id)
45 |
46 | id = :replace_srlg
47 | test_harness_run(tests, id)
48 | skipped_tests_summary(tests)
49 | end
50 |
--------------------------------------------------------------------------------
/tests/beaker_tests/all/cisco_yang_netconf/test_replace_vrf.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../../lib/utilitylib.rb', __FILE__)
17 | require File.expand_path('../../../lib/yang_netconf_util.rb', __FILE__)
18 |
19 | # Test hash top-level keys
20 | tests = {
21 | master: master,
22 | agent: agent,
23 | ensurable: false,
24 | resource_name: 'cisco_yang_netconf',
25 | }
26 | tests[:replace] = NETCONF_REPLACE
27 |
28 | skip_unless_supported(tests)
29 |
30 | step 'Setup' do
31 | clear_vrf
32 | title_string = NETCONF_BLUE_VRF_WO_PROPERTY
33 | cmd = PUPPET_BINPATH + "resource cisco_yang_netconf '#{title_string}' mode=merge"
34 | on(agent, cmd)
35 |
36 | title_string = NETCONF_GREEN_VRF_WO_PROPERTY
37 | cmd = PUPPET_BINPATH + "resource cisco_yang_netconf '#{title_string}' mode=merge"
38 | on(agent, cmd)
39 | end
40 |
41 | teardown do
42 | clear_vrf
43 | end
44 |
45 | #################################################################
46 | # TEST CASE EXECUTION
47 | #################################################################
48 | test_name 'TestCase :: VRF Present' do
49 | id = :replace
50 | test_harness_run(tests, id)
51 | skipped_tests_summary(tests)
52 | end
53 |
--------------------------------------------------------------------------------
/tests/beaker_tests/lib/yang_netconf_util.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../lib/utilitylib.rb', __FILE__)
17 |
18 | NETCONF_ROOT_VRF = \
19 | ''
20 |
21 | NETCONF_BLUE_VRF_WO_PROPERTY = \
22 | '
23 |
24 | BLUE
25 |
26 |
27 | '
28 |
29 | NETCONF_BLUE_VRF_W_PROPERTY1 = \
30 | '
31 |
32 | BLUE
33 |
34 |
35 | 9
36 | 9
37 |
38 |
39 | '
40 |
41 | NETCONF_BLUE_VRF_W_PROPERTY2 = \
42 | '
43 |
44 | BLUE
45 |
46 | Generic external traffic
47 |
48 | '
49 |
50 | NETCONF_BLUE_VRF_W_PROPERTY12 = \
51 | '
52 |
53 | BLUE
54 |
55 | Generic external traffic
56 |
57 | 9
58 | 9
59 |
60 |
61 | '
62 |
63 | NETCONF_GREEN_VRF_WO_PROPERTY = \
64 | '
65 |
66 | GREEN
67 |
68 |
69 | '
70 |
71 | NETCONF_BLUE_GREEN_VRF_WO_PROPERTY = \
72 | '
73 |
74 | BLUE
75 |
76 |
77 |
78 | GREEN
79 |
80 |
81 | '
82 |
83 | NETCONF_INTERNET_VOIP_VRF = \
84 | '
85 |
86 | VOIP
87 |
88 | Voice over IP
89 |
90 | 875
91 | 3
92 |
93 |
94 |
95 | INTERNET
96 |
97 | Generic external traffic
98 |
99 | 875
100 | 22
101 |
102 |
103 | '
104 |
105 | NETCONF_ROOT_SRLG = \
106 | ''
107 |
108 | NETCONF_SRLG_GE_01 = \
109 | '
110 |
111 |
112 |
113 | GigabitEthernet0/0/0/0
114 |
115 |
116 |
117 |
118 |
119 | 10
120 |
121 |
122 | 100
123 |
124 |
125 | default
126 |
127 |
128 |
129 |
130 | 20
131 |
132 |
133 | 200
134 |
135 |
136 | default
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | 1
146 |
147 |
148 | 2
149 |
150 |
151 | default
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 | '
160 |
161 | NETCONF_SRLG_GE_01_UPDATE = \
162 | '
163 |
164 |
165 |
166 | GigabitEthernet0/0/0/0
167 |
168 |
169 |
170 |
171 |
172 | 20
173 |
174 |
175 | 200
176 |
177 |
178 | default
179 |
180 |
181 |
182 |
183 | 90
184 |
185 |
186 | 900
187 |
188 |
189 | default
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 | 1
199 |
200 |
201 | 9
202 |
203 |
204 | default
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 | '
213 |
214 | NETCONF_DELETE_SRLG = ''
215 |
216 | def clear_vrf
217 | title_string = NETCONF_ROOT_VRF
218 | cmd = PUPPET_BINPATH + "resource cisco_yang_netconf '#{title_string}' source='#{title_string}' mode=replace"
219 | on(agent, cmd)
220 | end
221 |
222 | def clear_srlg
223 | title_string = NETCONF_ROOT_SRLG
224 | cmd = PUPPET_BINPATH + "resource cisco_yang_netconf '#{title_string}' source='#{title_string}' mode=replace"
225 | on(agent, cmd)
226 | end
227 |
228 | def massage_path(str)
229 | ret = str.clone
230 | ret.delete!("\n").gsub!(/\s*{\s*/, '{').gsub!(/\s*}\s*/, '}').gsub!(/\s*:\s*/, ':')
231 | ret.gsub!(/\s*\[\s*/, '[').gsub!(/\s*\]\s*/, ']').gsub!(/\s*,\s*/, ',')
232 | end
233 |
234 | NETCONF_CREATE = {
235 | desc: 'Create VRF BLUE',
236 | title: NETCONF_ROOT_VRF,
237 | manifest_props: {
238 | source: NETCONF_BLUE_VRF_WO_PROPERTY
239 | },
240 | }
241 |
242 | NETCONF_CREATE_SRLG = {
243 | desc: 'CREATE SRLG GE0 and GE1',
244 | title: NETCONF_ROOT_SRLG,
245 | manifest_props: {
246 | source: NETCONF_SRLG_GE_01
247 | },
248 | }
249 |
250 | NETCONF_REPLACE = {
251 | desc: 'Replace VRF GREEN with BLUE',
252 | title: NETCONF_ROOT_VRF,
253 | manifest_props: {
254 | source: NETCONF_BLUE_VRF_WO_PROPERTY,
255 | mode: 'replace',
256 | },
257 | }
258 |
259 | NETCONF_REPLACE12 = {
260 | desc: 'Replace VRF BLUE with BLUE',
261 | title: NETCONF_ROOT_VRF,
262 | manifest_props: {
263 | # Replace NETCONF_BLUE_VRF_W_PROPERTY1 by NETCONF_BLUE_VRF_W_PROPERTY2.
264 | source: NETCONF_BLUE_VRF_W_PROPERTY2,
265 | mode: 'replace',
266 | },
267 | }
268 |
269 | NETCONF_MERGE = {
270 | desc: 'Merge VRF BLUE with GREEN',
271 | title: NETCONF_ROOT_VRF,
272 | manifest_props: {
273 | source: NETCONF_BLUE_VRF_WO_PROPERTY,
274 | mode: 'merge',
275 | },
276 | resource: {
277 | source: NETCONF_BLUE_GREEN_VRF_WO_PROPERTY
278 | },
279 | }
280 |
281 | NETCONF_MERGE12 = {
282 | desc: 'Merge VRF BLUE with BLUE',
283 | title: NETCONF_ROOT_VRF,
284 | manifest_props: {
285 | # merge NETCONF_BLUE_VRF_W_PROPERTY2 with existing configuration.
286 | # Expecting existing configuration to be NETCONF_BLUE_VRF_W_PROPERTY1
287 | # resulting NETCONF_BLUE_VRF_W_PROPERTY12
288 | source: NETCONF_BLUE_VRF_W_PROPERTY2,
289 | mode: 'merge',
290 | },
291 | resource: {
292 | source: NETCONF_BLUE_VRF_W_PROPERTY12
293 | },
294 | }
295 |
296 | NETCONF_REPLACE_SRLG = {
297 | desc: 'Update SRLG GE0 properties',
298 | title: NETCONF_ROOT_SRLG,
299 | manifest_props: {
300 | source: NETCONF_SRLG_GE_01_UPDATE,
301 | mode: 'replace',
302 | },
303 | }
304 |
305 | NETCONF_FILE_MERGE = {
306 | desc: 'Merge VOIP and INTERNET VRFs with current config',
307 | title: NETCONF_ROOT_VRF,
308 | manifest_props: {
309 | source: '/root/temp/vrfs.xml',
310 | mode: 'merge',
311 | },
312 | resource: {
313 | source: NETCONF_INTERNET_VOIP_VRF
314 | },
315 | }
316 |
317 | NETCONF_FILE_REPLACE = {
318 | desc: 'Replace current config by VOIP and INTERNET VRFs',
319 | title: NETCONF_ROOT_VRF,
320 | manifest_props: {
321 | source: '/root/temp/vrfs.xml',
322 | mode: 'replace',
323 | },
324 | resource: {
325 | source: NETCONF_INTERNET_VOIP_VRF
326 | },
327 | }
328 |
--------------------------------------------------------------------------------
/tests/beaker_tests/lib/yang_util.rb:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Copyright (c) 2016 Cisco and/or its affiliates.
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | ###############################################################################
16 | require File.expand_path('../../lib/utilitylib.rb', __FILE__)
17 |
18 | ROOT_VRF = \
19 | '{
20 | "Cisco-IOS-XR-infra-rsi-cfg:vrfs": [null]
21 | }'
22 |
23 | BLUE_VRF_WO_PROPERTY = \
24 | '{
25 | "Cisco-IOS-XR-infra-rsi-cfg:vrfs":
26 | {
27 | "vrf":
28 | [
29 | {
30 | "vrf-name":"BLUE",
31 | "create":[null]
32 | }
33 | ]
34 | }
35 | }'
36 |
37 | BLUE_VRF_W_PROPERTY1 = \
38 | '{
39 | "Cisco-IOS-XR-infra-rsi-cfg:vrfs":
40 | {
41 | "vrf":
42 | [
43 | {
44 | "vrf-name":"BLUE",
45 | "create":[null],
46 | "remote-route-filter-disable": [null]
47 | }
48 | ]
49 | }
50 | }'
51 |
52 | BLUE_VRF_W_PROPERTY2 = \
53 | '{
54 | "Cisco-IOS-XR-infra-rsi-cfg:vrfs":
55 | {
56 | "vrf":
57 | [
58 | {
59 | "vrf-name":"BLUE",
60 | "create":[null],
61 | "description":"Sample description"
62 | }
63 | ]
64 | }
65 | }'
66 |
67 | BLUE_VRF_W_PROPERTY12 = \
68 | '{
69 | "Cisco-IOS-XR-infra-rsi-cfg:vrfs":
70 | {
71 | "vrf":
72 | [
73 | {
74 | "vrf-name":"BLUE",
75 | "create":[null],
76 | "description":"Sample description",
77 | "remote-route-filter-disable": [null]
78 | }
79 | ]
80 | }
81 | }'
82 |
83 | GREEN_VRF_WO_PROPERTY = \
84 | '{
85 | "Cisco-IOS-XR-infra-rsi-cfg:vrfs":
86 | {
87 | "vrf":
88 | [
89 | {
90 | "vrf-name":"GREEN",
91 | "create":[null]
92 | }
93 | ]
94 | }
95 | }'
96 |
97 | BLUE_GREEN_VRF_WO_PROPERTY = \
98 | '{
99 | "Cisco-IOS-XR-infra-rsi-cfg:vrfs":
100 | {
101 | "vrf":
102 | [
103 | {
104 | "vrf-name":"BLUE",
105 | "create":[null]
106 | },
107 | {
108 | "vrf-name":"GREEN",
109 | "create":[null]
110 | }
111 | ]
112 | }
113 | }'
114 | INTERNET_VOIP_VRF = \
115 | '{
116 | "Cisco-IOS-XR-infra-rsi-cfg:vrfs":{
117 | "vrf":[
118 | {
119 | "vrf-name":"VOIP",
120 | "create":[
121 | null
122 | ],
123 | "description":"Voice over IP",
124 | "vpn-id":{
125 | "vpn-oui":87,
126 | "vpn-index":3
127 | }
128 | },
129 | {
130 | "vrf-name":"INTERNET",
131 | "create":[
132 | null
133 | ],
134 | "description":"Generic external traffic",
135 | "vpn-id":{
136 | "vpn-oui":85,
137 | "vpn-index":22
138 | }
139 | }
140 | ]
141 | }
142 | }'
143 |
144 | ROOT_SRLG = \
145 | '{
146 | "Cisco-IOS-XR-infra-rsi-cfg:srlg": [null]
147 | }'
148 |
149 | SRLG_GE_01 = \
150 | '{
151 | "Cisco-IOS-XR-infra-rsi-cfg:srlg": {
152 | "interfaces": {
153 | "interface": [
154 | {
155 | "interface-name": "GigabitEthernet0/0/0/0",
156 | "enable": [
157 | null
158 | ],
159 | "values": {
160 | "value": [
161 | {
162 | "srlg-index": 10,
163 | "srlg-value": 100,
164 | "srlg-priority": "default"
165 | },
166 | {
167 | "srlg-index": 20,
168 | "srlg-value": 200,
169 | "srlg-priority": "default"
170 | }
171 | ]
172 | },
173 | "interface-group": {
174 | "enable": [
175 | null
176 | ],
177 | "group-names": {
178 | "group-name": [
179 | {
180 | "group-name-index": 1,
181 | "group-name": "2",
182 | "srlg-priority": "default"
183 | }
184 | ]
185 | }
186 | }
187 | },
188 | {
189 | "interface-name": "GigabitEthernet0/0/0/1",
190 | "enable": [
191 | null
192 | ]
193 | }
194 | ]
195 | },
196 | "enable": [
197 | null
198 | ]
199 | }
200 | }'
201 |
202 | SRLG_GE_01_UPDATE = \
203 | '{
204 | "Cisco-IOS-XR-infra-rsi-cfg:srlg": {
205 | "interfaces": {
206 | "interface": [
207 | {
208 | "interface-name": "GigabitEthernet0/0/0/0",
209 | "enable": [
210 | null
211 | ],
212 | "values": {
213 | "value": [
214 | {
215 | "srlg-index": 80,
216 | "srlg-value": 800,
217 | "srlg-priority": "default"
218 | },
219 | {
220 | "srlg-index": 90,
221 | "srlg-value": 900,
222 | "srlg-priority": "default"
223 | }
224 | ]
225 | },
226 | "interface-group": {
227 | "enable": [
228 | null
229 | ],
230 | "group-names": {
231 | "group-name": [
232 | {
233 | "group-name-index": 1,
234 | "group-name": "2",
235 | "srlg-priority": "default"
236 | }
237 | ]
238 | }
239 | }
240 | },
241 | {
242 | "interface-name": "GigabitEthernet0/0/0/1",
243 | "enable": [
244 | null
245 | ]
246 | }
247 | ]
248 | },
249 | "enable": [
250 | null
251 | ]
252 | }
253 | }'
254 |
255 | def massage_path(str)
256 | ret = str.clone
257 | ret.delete!("\n").gsub!(/\s*{\s*/, '{').gsub!(/\s*}\s*/, '}').gsub!(/\s*:\s*/, ':')
258 | ret.gsub!(/\s*\[\s*/, '[').gsub!(/\s*\]\s*/, ']').gsub!(/\s*,\s*/, ',')
259 | end
260 |
261 | DELETE = {
262 | desc: 'Delete VRF BLUE',
263 | title: BLUE_VRF_WO_PROPERTY,
264 | manifest_props: {
265 | },
266 | }
267 |
268 | DELETE_PROPERTY = {
269 | desc: 'Delete VRF BLUE description',
270 | title: BLUE_VRF_W_PROPERTY2,
271 | manifest_props: {
272 | },
273 | }
274 |
275 | CREATE = {
276 | desc: 'Create VRF BLUE',
277 | title: ROOT_VRF,
278 | manifest_props: {
279 | source: BLUE_VRF_WO_PROPERTY
280 | },
281 | }
282 |
283 | CREATE_SRLG = {
284 | desc: 'CREATE SRLG GE0 and GE1',
285 | title: ROOT_SRLG,
286 | manifest_props: {
287 | source: SRLG_GE_01
288 | },
289 | }
290 |
291 | REPLACE = {
292 | desc: 'Replace VRF GREEN with BLUE',
293 | title: ROOT_VRF,
294 | manifest_props: {
295 | source: BLUE_VRF_WO_PROPERTY,
296 | mode: 'replace',
297 | },
298 | }
299 |
300 | REPLACE12 = {
301 | desc: 'Replace VRF BLUE with BLUE',
302 | title: ROOT_VRF,
303 | manifest_props: {
304 | # Replace BLUE_VRF_W_PROPERTY1 by BLUE_VRF_W_PROPERTY2.
305 | source: BLUE_VRF_W_PROPERTY2,
306 | mode: 'replace',
307 | },
308 | }
309 |
310 | MERGE = {
311 | desc: 'Merge VRF BLUE with GREEN',
312 | title: ROOT_VRF,
313 | manifest_props: {
314 | source: BLUE_VRF_WO_PROPERTY,
315 | mode: 'merge',
316 | },
317 | resource: {
318 | source: BLUE_GREEN_VRF_WO_PROPERTY
319 | },
320 | }
321 |
322 | MERGE12 = {
323 | desc: 'Merge VRF BLUE with BLUE',
324 | title: ROOT_VRF,
325 | manifest_props: {
326 | # merge BLUE_VRF_W_PROPERTY2 with existing configuration (BLUE_VRF_W_PROPERTY1)
327 | # resulting BLUE_VRF_W_PROPERTY12
328 | source: BLUE_VRF_W_PROPERTY2,
329 | mode: 'merge',
330 | },
331 | resource: {
332 | source: BLUE_VRF_W_PROPERTY12
333 | },
334 | }
335 |
336 | REPLACE_SRLG = {
337 | desc: 'Update SRLG GE0 properties',
338 | title: ROOT_SRLG,
339 | manifest_props: {
340 | source: SRLG_GE_01_UPDATE,
341 | mode: 'replace',
342 | },
343 | }
344 |
345 | FILE_MERGE = {
346 | desc: 'Merge VOIP and INTERNET VRFs with current config',
347 | title: ROOT_VRF,
348 | manifest_props: {
349 | source: '/root/temp/vrfs.json',
350 | mode: 'merge',
351 | },
352 | resource: {
353 | source: INTERNET_VOIP_VRF
354 | },
355 | }
356 |
357 | FILE_REPLACE = {
358 | desc: 'Replace current config by VOIP and INTERNET VRFs',
359 | title: ROOT_VRF,
360 | manifest_props: {
361 | source: '/root/temp/vrfs.json',
362 | mode: 'replace',
363 | },
364 | resource: {
365 | source: INTERNET_VOIP_VRF
366 | },
367 | }
368 |
--------------------------------------------------------------------------------
/tests/init.pp:
--------------------------------------------------------------------------------
1 | # The baseline for module testing used by Puppet Labs is that each manifest
2 | # should have a corresponding test manifest that declares that class or defined
3 | # type.
4 | #
5 | # Tests are then run by using puppet apply --noop (to check for compilation
6 | # errors and view a log of events) or by fully applying the test in a virtual
7 | # environment (to compare the resulting system state to the desired state).
8 | #
9 | # Learn more about module testing here:
10 | # http://docs.puppetlabs.com/guides/tests_smoke.html
11 | #
12 | include ciscoyang
13 |
--------------------------------------------------------------------------------
/tests/minitest/ciscotest.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2013-2016 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | # Minitest needs to have this path in order to discover our plugins
16 | $LOAD_PATH.push File.expand_path('../../../lib', __FILE__)
17 |
18 | require 'simplecov'
19 | require 'ipaddr'
20 | require 'resolv'
21 | require_relative 'platform_info'
22 | gem 'minitest', '~> 5.0'
23 | require 'minitest/autorun'
24 | require 'net/telnet'
25 | require_relative '../../lib/util/client'
26 | require_relative '../../lib/util/client/grpc/client'
27 | require_relative '../../lib/util/node'
28 | require_relative '../../lib/util/environment'
29 | require_relative '../../lib/util/logger'
30 |
31 | include Cisco
32 |
33 | # CiscoTestCase - base class for all node utility minitests
34 | class CiscoTestCase < Minitest::Test
35 | # rubocop:disable Style/ClassVars
36 | @@device_hash = {}
37 | # rubocop:enable Style/ClassVars
38 |
39 | @node = nil
40 |
41 | @client_class = nil
42 |
43 | class << self
44 | attr_accessor :client_class
45 | end
46 |
47 | def client_class
48 | self.class.client_class
49 | end
50 |
51 | def self.runnable_methods
52 | cc = client_class
53 | return super unless cc
54 | env = cc ? cc.environment(cc) : nil
55 | return super if env
56 |
57 | # no environment configured for this test suite, so skip all tests
58 | remove_method :setup if instance_methods(false).include?(:setup)
59 | remove_method :teardown if instance_methods(false).include?(:teardown)
60 | [:all_skipped]
61 | end
62 |
63 | def all_skipped
64 | skip("Skipping #{self.class}; #{client_class} is not configured/supported on this node")
65 | end
66 |
67 | def node
68 | cc = client_class
69 | assert(cc, 'No client_class defined')
70 |
71 | return nil unless cc
72 |
73 | is_new = !Node.instance_exists(cc)
74 | @node = Node.instance(cc)
75 |
76 | if is_new
77 | # Record the platform we're running on
78 | puts "\nNode under test via #{cc}:"
79 | puts " - name - #{@node.host_name}"
80 | puts " - type - #{@node.product_id}"
81 | puts " - image - #{@node.system}\n\n"
82 | end
83 |
84 | @node
85 | rescue Cisco::AuthenticationFailed
86 | abort "Unauthorized to connect as #{username}:#{password}@#{address}"
87 | rescue Cisco::ClientError, TypeError, ArgumentError => e
88 | abort "Error in establishing connection: #{e}"
89 | end
90 |
91 | def client
92 | node ? node.client : nil
93 | end
94 |
95 | def environment(default=nil)
96 | Cisco::Client.environment(client.class) || default
97 | end
98 |
99 | def device
100 | @@device_hash[node.client.class] ||= create_device
101 | end
102 |
103 | def create_device
104 | login = proc do
105 | puts "====> ciscotest.create_device - login address: #{node.client.host}, username: #{node.client.username}, object_id: #{object_id}"
106 | d = Net::Telnet.new('Host' => node.client.host,
107 | 'Timeout' => 240,
108 | # NX-OS has a space after '#', IOS XR does not
109 | 'Prompt' => /[$%#>] *\z/n,
110 | )
111 | d.login('Name' => node.client.username,
112 | 'Password' => node.client.password,
113 | # NX-OS uses 'login:' while IOS XR uses 'Username:'
114 | 'LoginPrompt' => /(?:[Ll]ogin|[Uu]sername)[: ]*\z/n,
115 | )
116 | d
117 | end
118 |
119 | begin
120 | new_device = login.call
121 | rescue Errno::ECONNRESET
122 | new_device.close
123 | puts 'Connection reset by peer? Try again'
124 | sleep 1
125 |
126 | new_device = login.call
127 | end
128 | new_device.cmd('term len 0')
129 | new_device
130 | rescue Errno::ECONNREFUSED
131 | puts 'Telnet login refused - please check that the IP address is correct'
132 | puts " and that you have configured 'telnet ipv4 server...' on the UUT"
133 | exit
134 | end
135 |
136 | def cmd_ref
137 | node.cmd_ref
138 | end
139 |
140 | def platform
141 | node.client.platform
142 | end
143 |
144 | def config_and_warn_on_match(warn_match, *args)
145 | if node.client.platform == :ios_xr
146 | result = super(warn_match, *args, 'commit')
147 | else
148 | result = super
149 | end
150 | result
151 | end
152 | end
153 |
--------------------------------------------------------------------------------
/tests/minitest/platform_info.rb:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2013-2016 Cisco and/or its affiliates.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | require 'yaml'
16 |
17 | # Class: PlatformInfo
18 | # This class reads device specific details from platform_info.yaml
19 | # These details can be used to customize unit tests for a device
20 | class PlatformInfo
21 | # Default constructor for the PlatformInfo class. This class
22 | # requires the hostname of the device on which UTs are to run
23 | # to be passed in. The constructor reads platform_info.yaml
24 | # and stores info for this device in the instance variable
25 | # @platform_info_hash
26 | #
27 | # @param[in] device_name hostname of device on which
28 | # UTs are to be run
29 | #
30 | def initialize(device_name, platform)
31 | if device_name.nil? || device_name.empty?
32 | fail 'device name must be specified in PlatformInfo constructor.'
33 | end
34 | @platform_info_hash = {}
35 |
36 | begin
37 | project_info_hash = YAML.load_file(File.join(File.dirname(__FILE__),
38 | 'platform_info.yaml'))
39 | rescue RuntimeError
40 | raise 'Error - could not open platform file - platform_info.yaml'
41 | end
42 |
43 | @platform_info_hash = project_info_hash[device_name]
44 | @platform_info_hash ||= project_info_hash['default'][platform.to_s]
45 | fail "Error - could not find #{device_name} device specific information " \
46 | 'in platform_info.yaml' if @platform_info_hash.nil?
47 | end
48 |
49 | # The following instance method will return the value associated with
50 | # the specified key from the instance variable @platform_info_hash.
51 | #
52 | # @param[in] key String value indicating the key to be searched for
53 | # in @platform_info_hash
54 | #
55 | def get_value_from_key(key)
56 | fail 'key must be specified in the method get_value_from_key' if key.nil?
57 |
58 | value = @platform_info_hash[key]
59 | fail "no value exists for the key #{key}" if value.nil?
60 |
61 | value
62 | end
63 | end
64 |
--------------------------------------------------------------------------------
/tests/minitest/test_grpc.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | #
3 | # October 2015, Glenn F. Matthews
4 | #
5 | # Copyright (c) 2015-2016 Cisco and/or its affiliates.
6 | #
7 | # Licensed under the Apache License, Version 2.0 (the "License");
8 | # you may not use this file except in compliance with the License.
9 | # You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing, software
14 | # distributed under the License is distributed on an "AS IS" BASIS,
15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | # See the License for the specific language governing permissions and
17 | # limitations under the License.
18 |
19 | require_relative 'ciscotest'
20 |
21 | # Test case for Cisco::Client::GRPC::Client class
22 | class TestGRPC < CiscoTestCase
23 | @client_class = Cisco::Client::GRPC
24 | RED_VRF = \
25 | "{\n \"Cisco-IOS-XR-infra-rsi-cfg:vrfs\": {\n \"vrf\": [\n {\n \"vrf-name\": \"RED\",\n \"create\": [\n null\n ]\n }\n ]\n }\n}\n"
26 | FOO_VRF = \
27 | "{\n \"Cisco-IOS-XR-infra-rsi-cfg:vrfs\": {\n \"vrf\": "\
28 | "[\n {\n \"vrf-name\": \"foo-should-not-be-there\",\n \"create\": [\n null\n ]\n }\n ]\n }\n}\n"
29 | ROOT_VRF = '{"Cisco-IOS-XR-infra-rsi-cfg:vrfs": [null]}'
30 | INVALID_VRF = '{"Cisco-IOS-XR-infra-rsi-cfg:invalid": [null]}'
31 |
32 | def test_auth_failure
33 | env = environment.merge(password: 'wrong password')
34 | # Cisco::Client::GRPC.new(**env)
35 | e = assert_raises Cisco::AuthenticationFailed do
36 | Cisco::Client::GRPC.new(**env)
37 | end
38 | assert_equal('gRPC client creation failure: Failed authentication',
39 | e.message)
40 | end
41 |
42 | def test_connection_failure
43 | # Failure #1: connecting to a port that's listening for a non-gRPC protocol
44 | env = environment.merge(port: '57722') # sshd
45 | e = assert_raises Cisco::ConnectionRefused do
46 | Cisco::Client::GRPC.new(**env)
47 | end
48 | assert_match('gRPC client creation failure: Connection refused: ',
49 | e.message)
50 | # Failure #2: Connecting to a port that's not listening at all
51 | env = environment.merge(port: '0')
52 | e = assert_raises Cisco::ConnectionRefused do
53 | Cisco::Client::GRPC.new(**env)
54 | end
55 | # for now, make this test a little more lenient
56 | # assert_equal('gRPC client creation failure: ' \
57 | # 'timed out during initial connection: Deadline Exceeded',
58 | # e.message)
59 | assert_match('gRPC client creation failure: ', e.message)
60 | end
61 |
62 | def test_set_string
63 | client.set(values: RED_VRF,
64 | mode: :replace_config)
65 | run = client.get(command: ROOT_VRF)
66 | assert_match(RED_VRF, run)
67 | end
68 |
69 | def test_set_invalid
70 | e = assert_raises Cisco::YangError do
71 | client.set(values: INVALID_VRF)
72 | end
73 | assert_equal("The config '{\"Cisco-IOS-XR-infra-rsi-cfg:invalid\": [null]}' was rejected with error:
74 | unknown-element: Cisco-IOS-XR-infra-rsi-cfg:ns1:invalid", e.message)
75 | assert_empty(e.successful_input)
76 | assert_equal(INVALID_VRF,
77 | e.rejected_input)
78 | end
79 |
80 | def test_get_invalid
81 | assert_raises Cisco::YangError do
82 | client.get(command: INVALID_VRF)
83 | end
84 | end
85 |
86 | def test_get_incomplete
87 | assert_raises Cisco::YangError do
88 | client.get(command: INVALID_VRF)
89 | end
90 | end
91 |
92 | def test_get_empty
93 | result = client.get(command: FOO_VRF)
94 | assert_empty(result)
95 | end
96 | end
97 |
--------------------------------------------------------------------------------
/tests/minitest/test_netconf.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | #
3 | # June 2016, Sushrut Shirole
4 | #
5 | # Copyright (c) 2016 Cisco and/or its affiliates.
6 | #
7 | # Licensed under the Apache License, Version 2.0 (the "License");
8 | # you may not use this file except in compliance with the License.
9 | # You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing, software
14 | # distributed under the License is distributed on an "AS IS" BASIS,
15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | # See the License for the specific language governing permissions and
17 | # limitations under the License.
18 |
19 | require_relative 'ciscotest'
20 |
21 | # Test case for Cisco::Client::NETCONF::Client class
22 | class TestNetconf < CiscoTestCase
23 | @client_class = Cisco::Client::NETCONF
24 |
25 | RED_VRF = \
26 | "\n \n \n red\n \n \n \n"
27 | BLUE_VRF = \
28 | "\n \n \n blue\n \n \n \n"
29 | ROOT_VRF = ''
30 | INVALID_VRF = ''
31 |
32 | def test_auth_failure
33 | env = environment({}).merge(password: 'wrong password')
34 | e = assert_raises Cisco::AuthenticationFailed do
35 | Cisco::Client::NETCONF.new(**env)
36 | end
37 | assert_equal('Netconf client creation failure: Authentication failed for user ' \
38 | + environment[:username] + '@' + environment[:host], e.message)
39 | end
40 |
41 | def test_connection_failure
42 | # Failure #1: connecting to a host that's listening for a non-Netconf protocol
43 | env = environment.merge(host: '1.1.1.1')
44 | e = assert_raises Cisco::YangError do
45 | Cisco::Client::NETCONF.new(**env)
46 | end
47 | assert_match('No route to host - connect(2)',
48 | e.message)
49 | end
50 |
51 | def test_set_string
52 | client.set(values: RED_VRF,
53 | mode: :replace)
54 | run = client.get(command: ROOT_VRF)
55 | assert_match(RED_VRF, run)
56 | end
57 |
58 | def test_set_invalid
59 | e = assert_raises Cisco::YangError do
60 | client.set(values: INVALID_VRF)
61 | end
62 | assert_equal('The config \'apply of \' was rejected with error:
64 | error-type => rpc
65 | error-tag => malformed-message
66 | error-severity => error
67 | ', e.message)
68 | assert_empty(e.successful_input)
69 | assert_equal('apply of ', e.rejected_input)
70 | end
71 |
72 | def test_get_invalid
73 | assert_raises Cisco::YangError do
74 | client.get(command: INVALID_VRF)
75 | end
76 | end
77 |
78 | def test_get_incomplete
79 | assert_raises Cisco::YangError do
80 | client.get(command: INVALID_VRF)
81 | end
82 | end
83 |
84 | def test_get_empty
85 | result = client.get(command: BLUE_VRF)
86 | assert_empty(result)
87 | end
88 | end
89 |
--------------------------------------------------------------------------------
/tests/minitest/test_netconf_yang_offline.rb:
--------------------------------------------------------------------------------
1 | require_relative 'ciscotest'
2 | require_relative '../../lib/util/client/netconf/netconf'
3 |
4 | # Two elts in Current, one will be deleted by operation=delete clause
5 | CURRENT_BLUE_RED_VRF =
6 | '
7 |
8 | blue
9 |
10 |
11 | red
12 |
13 | '
14 |
15 | TARGET_BLUE_RED_VRF =
16 | '
17 |
18 | blue
19 |
20 |
21 | red
22 |
23 | '
24 |
25 | # Two elts in Current, delete in target is a no-op. Merge should be a no-op, but replace should not
26 | CURRENT_BLUE_GREEN_VRF =
27 | '
28 |
29 | blue
30 |
31 |
32 | green
33 |
34 | '
35 |
36 | TARGET_BLUE_GREEN_VRF =
37 | '
38 |
39 | blue
40 |
41 |
42 | red
43 |
44 | '
45 |
46 | # Single elt in Current, operation=delete in target results in no-op
47 | CURRENT_DELETE_DELETED_VRF =
48 | '
49 |
50 | blue
51 | Blue description
52 |
53 |
54 | '
55 |
56 | TARGET_DELETE_DELETED_VRF =
57 | '
58 |
59 | blue
60 | Blue description
61 |
62 |
63 |
64 | red
65 | Red description
66 |
67 |
68 | '
69 |
70 | # No elts in current, operation=delete in target results in no-op
71 | CURRENT_DELETE_DELETED_VRF_2 =
72 | '
73 | '
74 |
75 | TARGET_DELETE_DELETED_VRF_2 =
76 | '
77 |
78 | red
79 |
80 | '
81 |
82 | # Simple case to validate namespace normalization
83 | CURRENT_BLUE_VRF =
84 | '
85 |
86 | blue
87 | Blue description
88 |
89 |
90 | '
91 |
92 | TARGET_BLUE_VRF =
93 | '
94 |
95 | blue
96 | Blue description
97 |
98 |
99 | '
100 |
101 | # Delete a non-container element
102 | CURRENT_BLUE_VRF_DELETE_DESCRIPTION =
103 | '
104 |
105 | blue
106 | Blue description
107 |
108 |
109 | '
110 |
111 | TARGET_BLUE_VRF_DELETE_DESCRIPTION =
112 | '
113 |
114 | blue
115 |
116 | Blue description
117 |
118 |
119 |
120 | '
121 |
122 | # TestNetconfOffline - Offline Minitest for Netconf class
123 | class TestNetconfOffline < CiscoTestCase
124 | def test_netconf_prefix_merge
125 | assert(Cisco::Netconf.insync_for_merge(TARGET_BLUE_VRF,
126 | CURRENT_BLUE_VRF),
127 | 'expected in-sync')
128 | end
129 |
130 | def test_netconf_prefix_replace
131 | assert(Cisco::Netconf.insync_for_replace(TARGET_BLUE_VRF,
132 | CURRENT_BLUE_VRF),
133 | 'expected in-sync')
134 | end
135 |
136 | def test_netconf_delete_vrf_description_merge
137 | refute(Cisco::Netconf.insync_for_merge(TARGET_BLUE_VRF_DELETE_DESCRIPTION,
138 | CURRENT_BLUE_VRF_DELETE_DESCRIPTION),
139 | 'expected not in-sync')
140 | end
141 |
142 | def test_netconf_delete_vrf_description_replace
143 | refute(Cisco::Netconf.insync_for_replace(TARGET_BLUE_VRF_DELETE_DESCRIPTION,
144 | CURRENT_BLUE_VRF_DELETE_DESCRIPTION),
145 | 'expected not in-sync')
146 | end
147 |
148 | def test_netconf_delete_red_vrf_merge
149 | refute(Cisco::Netconf.insync_for_merge(TARGET_BLUE_RED_VRF,
150 | CURRENT_BLUE_RED_VRF),
151 | 'expected not in-sync')
152 | end
153 |
154 | def test_netconf_delete_red_vrf_replace
155 | refute(Cisco::Netconf.insync_for_replace(TARGET_BLUE_RED_VRF,
156 | CURRENT_BLUE_RED_VRF),
157 | 'expected not in-sync')
158 | end
159 |
160 | def test_netconf_delete_missing_vrf_merge
161 | assert(Cisco::Netconf.insync_for_merge(TARGET_DELETE_DELETED_VRF,
162 | CURRENT_DELETE_DELETED_VRF),
163 | 'expected in-sync')
164 | end
165 |
166 | def test_netconf_delete_missing_vrf_replace
167 | assert(Cisco::Netconf.insync_for_replace(TARGET_DELETE_DELETED_VRF,
168 | CURRENT_DELETE_DELETED_VRF),
169 | 'expected in-sync')
170 | end
171 |
172 | def test_netconf_delete_missing_vrf_2_merge
173 | assert(Cisco::Netconf.insync_for_merge(TARGET_DELETE_DELETED_VRF_2,
174 | CURRENT_DELETE_DELETED_VRF_2),
175 | 'expected in-sync')
176 | end
177 |
178 | def test_netconf_delete_missing_vrf_2_replace
179 | assert(Cisco::Netconf.insync_for_replace(TARGET_DELETE_DELETED_VRF_2,
180 | CURRENT_DELETE_DELETED_VRF_2),
181 | 'expected in-sync')
182 | end
183 |
184 | def test_netconf_delete_combo_merge
185 | assert(Cisco::Netconf.insync_for_merge(TARGET_BLUE_GREEN_VRF,
186 | CURRENT_BLUE_GREEN_VRF),
187 | 'expected in-sync')
188 | end
189 |
190 | def test_netconf_delete_combo_replace
191 | refute(Cisco::Netconf.insync_for_replace(TARGET_BLUE_GREEN_VRF,
192 | CURRENT_BLUE_GREEN_VRF),
193 | 'expected not in-sync')
194 | end
195 | end
196 |
--------------------------------------------------------------------------------