From ef69fa86694c1d92e4f2e9bdb295f76641f8e7fc Mon Sep 17 00:00:00 2001 From: Jonathan Tripathy Date: Thu, 7 Apr 2016 15:34:06 +0100 Subject: [PATCH 001/203] Added IOS XR support for tacacs_server --- README.md | 4 ++-- lib/puppet/provider/tacacs_server/cisco.rb | 22 ++++++++++++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f780632ff..7b17718b4 100644 --- a/README.md +++ b/README.md @@ -197,7 +197,7 @@ The following table indicates which providers are supported on each platform. As | [tacacs](#type-tacacs) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | | [tacacs_global](#type-tacacs_global) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | | [tacacs_server_group](#type-tacacs_server_group) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | -| [tacacs_server](#type-tacacs_server) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | +| [tacacs_server](#type-tacacs_server) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ## Resource Reference @@ -3982,7 +3982,7 @@ Number of seconds before the timeout period ends | N6k | unsupported | unsupported | | N7k | unsupported | unsupported | | N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | +| IOS XR | TODO | TODO | ##### `ensure` Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. diff --git a/lib/puppet/provider/tacacs_server/cisco.rb b/lib/puppet/provider/tacacs_server/cisco.rb index dc85498cc..d1edca277 100644 --- a/lib/puppet/provider/tacacs_server/cisco.rb +++ b/lib/puppet/provider/tacacs_server/cisco.rb @@ -105,6 +105,14 @@ def create @property_flush[:ensure] = :present end + def create_new + if Facter.value('operatingsystem').eql?('ios_xr') + @tacacs_server = Cisco::TacacsServerHost.new(@resource[:name], true, @resource[:port]) + else + @tacacs_server = Cisco::TacacsServerHost.new(@resource[:name], true) + end + end + def destroy @property_flush[:ensure] = :absent end @@ -133,9 +141,19 @@ def flush @tacacs_server = nil @property_hash[:ensure] = :absent else - if @property_hash.empty? + # On IOS XR, if the port values change, the entity has to be re-created as the ports + # form part of the uniquiness of the item on the device. This is opposed to using + # the setters on other platforms for the changing of port values. + if @property_hash.empty? || + (Facter.value('operatingsystem').eql?('ios_xr') && + @resource[:port] != @tacacs_server.port.to_i) + # create a new Tacacs Server - @tacacs_server = Cisco::TacacsServerHost.new(@resource[:name]) + create_new + end + + if Facter.value('operatingsystem').eql?('ios_xr') + TACACS_SERVER_PROPS.delete(:port) end TACACS_SERVER_PROPS.each do |puppet_prop, cisco_prop| From 8c47fa8d5bb175add08b3c14a7a40ad878b4635e Mon Sep 17 00:00:00 2001 From: Jonathan Tripathy Date: Thu, 7 Apr 2016 15:38:14 +0100 Subject: [PATCH 002/203] Updated CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afd37b3f1..db2a38e13 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `cisco_stp_global` type and provider. #### NetDev Resources -* +* Added IOS XR support for tacacs_server ### Added - Extended `cisco_interface` with the following attributes: From 549a28c852eca9cd8c7840cbcf4b1fe7fc352ef8 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Thu, 7 Apr 2016 10:46:03 -0400 Subject: [PATCH 003/203] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db2a38e13..6eca0ff93 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `cisco_stp_global` type and provider. #### NetDev Resources -* Added IOS XR support for tacacs_server +* Extended `tacacs_server` with support for ios_xr platform ### Added - Extended `cisco_interface` with the following attributes: From b0ebc7c2ce5d75499fbfacb292984fc90f1eb425 Mon Sep 17 00:00:00 2001 From: Jonathan Tripathy Date: Thu, 7 Apr 2016 15:58:53 +0100 Subject: [PATCH 004/203] Added support on IOS XR for tacacs_server_group --- CHANGELOG.md | 1 + README.md | 6 +++--- .../tacacs_server_group_provider_defaults.rb | 10 +++------- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6eca0ff93..a8a1a0b33 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). #### NetDev Resources * Extended `tacacs_server` with support for ios_xr platform +* Extended `tacacs_server_group` with support for ios_xr platform ### Added - Extended `cisco_interface` with the following attributes: diff --git a/README.md b/README.md index 7b17718b4..158391514 100644 --- a/README.md +++ b/README.md @@ -185,7 +185,7 @@ The following table indicates which providers are supported on each platform. As | [network_snmp](#type-network_snmp) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | | [radius](#type-radius) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | | [radius_global](#type-radius_global) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | -| [radius_server_group](#type-tacacs_server_group) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | +| [radius_server_group](#type-radius_server_group) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | | [radius_server](#type-radius_server) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅* | * [caveats](#radius_server-caveats) | | [search_domain](#type-search_domain) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | | [snmp_community](#type-snmp_community) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | @@ -196,7 +196,7 @@ The following table indicates which providers are supported on each platform. As | [syslog_setting](#type-syslog_setting) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | | [tacacs](#type-tacacs) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | | [tacacs_global](#type-tacacs_global) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | -| [tacacs_server_group](#type-tacacs_server_group) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | +| [tacacs_server_group](#type-tacacs_server_group) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | | [tacacs_server](#type-tacacs_server) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | @@ -4014,7 +4014,7 @@ Number of seconds before the timeout period ends | N6k | unsupported | unsupported | | N7k | unsupported | unsupported | | N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | +| IOS XR | TODO | TODO | #### Parameters diff --git a/tests/beaker_tests/tacacs_server_group/tacacs_server_group_provider_defaults.rb b/tests/beaker_tests/tacacs_server_group/tacacs_server_group_provider_defaults.rb index d09c8f0c4..2320f302b 100644 --- a/tests/beaker_tests/tacacs_server_group/tacacs_server_group_provider_defaults.rb +++ b/tests/beaker_tests/tacacs_server_group/tacacs_server_group_provider_defaults.rb @@ -65,13 +65,9 @@ resource_absent_cleanup(agent, 'tacacs_server') resource_absent_cleanup(agent, 'tacacs_server_group') - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, TacacsServerGroupLib.create_tacacs_server_setup) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = get_namespace_cmd(agent, PUPPET_BINPATH + - 'agent -t', options) - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) + command_config(agent, 'tacacs-server host 2.2.2.2') + command_config(agent, 'tacacs-server host 3.3.3.3') + command_config(agent, 'tacacs-server host 2004::44') logger.info('Setup switch for provider') end From 0112c9d6c3891f3a532dd53f5c5dbc0c495a8793 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Fri, 8 Apr 2016 07:59:45 -0400 Subject: [PATCH 005/203] remove sync damage from demo_bridge --- examples/cisco/demo_bridge_domain.pp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/examples/cisco/demo_bridge_domain.pp b/examples/cisco/demo_bridge_domain.pp index af4280293..3ac087204 100644 --- a/examples/cisco/demo_bridge_domain.pp +++ b/examples/cisco/demo_bridge_domain.pp @@ -1,10 +1,6 @@ # Manifest to demo cisco_bridge_domain provider # -<<<<<<< HEAD # Copyright (c) 2016 Cisco and/or its affiliates. -======= -# Copyright (c) 2016 Cisco and/or its affiliates. ->>>>>>> 0df39e4b0f44d6684f044d91f5420594388d3940 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From 067ddb088540ae88645bbf40f38d62fda4df597e Mon Sep 17 00:00:00 2001 From: Jonathan Tripathy Date: Tue, 12 Apr 2016 11:51:18 +0100 Subject: [PATCH 006/203] Refactored network_interface provider. Updated tests and code to support IOS XR. --- .../provider/network_interface/cisco.rb | 60 ++++++++++++- .../network_interface_provider_defaults.rb | 85 +++++++++---------- .../netdev_stdlib/network_interfacelib.rb | 14 ++- 3 files changed, 110 insertions(+), 49 deletions(-) diff --git a/lib/puppet/provider/network_interface/cisco.rb b/lib/puppet/provider/network_interface/cisco.rb index 57085707f..2448e225b 100644 --- a/lib/puppet/provider/network_interface/cisco.rb +++ b/lib/puppet/provider/network_interface/cisco.rb @@ -14,11 +14,31 @@ # See the License for the specific language governing permissions and # limitations under the License. -Puppet::Type.type(:network_interface).provide(:cisco, parent: Puppet::Type.type(:cisco_interface).provider(:cisco)) do +require 'cisco_node_utils' if Puppet.features.cisco_node_utils? +begin + require 'puppet_x/cisco/autogen' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'autogen.rb')) +end + +Puppet::Type.type(:network_interface).provide(:cisco) do @doc = 'network INTERFACE' + confine feature: :cisco_node_utils + defaultfor operatingsystem: [:nexus, :ios_xr] + mk_resource_methods + UNSUPPORTED_PROPS_IOS_XR = [:speed, :duplex] + + def initialize(value={}) + super(value) + @interface = Cisco::Interface.interfaces[@property_hash[:name]] + @property_flush = {} + end + def self.instances interfaces = [] Cisco::Interface.interfaces.each do |interface_name, i| @@ -37,7 +57,45 @@ def self.instances interfaces end + def self.prefetch(resources) + interfaces = instances + + resources.keys.each do |id| + provider = interfaces.find { |instance| instance.name == id } + resources[id].provider = provider unless provider.nil? + end + end # self.prefetch + + def exists? + (@property_hash[:ensure] == :present) + end + + def create + @property_flush[:ensure] = :present + end + + def destroy + @property_flush[:ensure] = :absent + end + + def instance_name + interface + end + + def validate + return unless Facter.value('operatingsystem').eql?('ios_xr') + + invalid = [] + UNSUPPORTED_PROPS_IOS_XR.each do |prop| + invalid << prop if @resource[prop] + end + + fail ArgumentError, "This provider does not support the following properties on this platform: #{invalid}" unless invalid.empty? + end + def flush + validate + if @property_flush[:ensure] == :absent @interface.destroy @interface = nil diff --git a/tests/beaker_tests/netdev_stdlib/network_interface_provider_defaults.rb b/tests/beaker_tests/netdev_stdlib/network_interface_provider_defaults.rb index 33f334bc0..7110de172 100644 --- a/tests/beaker_tests/netdev_stdlib/network_interface_provider_defaults.rb +++ b/tests/beaker_tests/netdev_stdlib/network_interface_provider_defaults.rb @@ -65,35 +65,37 @@ test_name "TestCase :: #{testheader}" do # @step [Step] Sets up switch for provider test. step 'TestStep :: Setup switch for provider test' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, NetworkInterfaceLib.create_defaults) + unless operating_system == 'ios_xr' + # Expected exit_code is 0 since this is a bash shell cmd. + on(master, NetworkInterfaceLib.create_defaults) - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - # Or expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = get_namespace_cmd(agent, PUPPET_BINPATH + - 'agent -t', options) - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) + # Expected exit_code is 0 since this is a puppet agent cmd with no change. + # Or expected exit_code is 2 since this is a puppet agent cmd with change. + cmd_str = get_namespace_cmd(agent, PUPPET_BINPATH + + 'agent -t', options) + on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config interface eth1/4 all') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [ - /mtu 1500/, - /speed auto/, - /duplex auto/, - ], - false, self, logger) - end + # Expected exit_code is 0 since this is a vegas shell cmd. + # Flag is set to true to check for absence of RegExp pattern in stdout. + cmd_str = get_vshell_cmd('show running-config interface eth1/4 all') + on(agent, cmd_str) do + search_pattern_in_output(stdout, + [ + /mtu 1500/, + /speed auto/, + /duplex auto/, + ], + false, self, logger) + end - logger.info("Setup switch for provider test :: #{result}") + logger.info("Setup switch for provider test :: #{result}") + end end # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource present manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. - on(master, NetworkInterfaceLib.create_non_defaults) + on(master, NetworkInterfaceLib.create_non_defaults(operating_system)) # Expected exit_code is 2 since this is a puppet agent cmd with change. cmd_str = get_namespace_cmd(agent, PUPPET_BINPATH + @@ -103,36 +105,29 @@ logger.info("Get resource present manifest from master :: #{result}") end - # @step [Step] Checks network_interface resource on agent using resource cmd. - step 'TestStep :: Check network_interface resource presence on agent' do + step 'TestStep :: Check interface instance presence on agent' do + if operating_system == 'ios_xr' + interface_name = 'gigabitethernet0/0/0/0' + else + interface_name = 'ethernet1/4' + end + # Expected exit_code is 0 since this is a puppet resource cmd. # Flag is set to false to check for presence of RegExp pattern in stdout. cmd_str = get_namespace_cmd(agent, PUPPET_BINPATH + - "resource network_interface 'ethernet1/4'", options) + "resource network_interface #{interface_name}", options) + on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'mtu' => '1500', - 'speed' => '100m', - 'description' => 'foo', - 'duplex' => 'full' }, - false, self, logger) - end + search_pattern_in_output(stdout, { 'mtu' => '1500' }, + false, self, logger) unless operating_system == 'ios_xr' - logger.info("Check network_interface resource presence on agent :: #{result}") - end + search_pattern_in_output(stdout, { 'speed' => '100' }, + false, self, logger) unless operating_system == 'ios_xr' - step 'TestStep :: Check interface instance presence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config interface eth1/4 all') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [ - /mtu 1500/, - /speed 100/, - /duplex full/, - /description foo/, - ], + search_pattern_in_output(stdout, { 'duplex' => 'full' }, + false, self, logger) unless operating_system == 'ios_xr' + + search_pattern_in_output(stdout, { 'description' => 'foo' }, false, self, logger) end diff --git a/tests/beaker_tests/netdev_stdlib/network_interfacelib.rb b/tests/beaker_tests/netdev_stdlib/network_interfacelib.rb index 6f6c48841..35b6dd470 100644 --- a/tests/beaker_tests/netdev_stdlib/network_interfacelib.rb +++ b/tests/beaker_tests/netdev_stdlib/network_interfacelib.rb @@ -49,8 +49,8 @@ def self.create_defaults manifest_str end - def self.create_non_defaults - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} + def self.create_non_defaults(type) + nxos_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { network_interface { 'ethernet1/4': speed => '100m', @@ -59,6 +59,14 @@ def self.create_non_defaults } } EOF" - manifest_str + + ios_xr_str = "cat <#{PUPPETMASTER_MANIFESTPATH} +node default { + network_interface { 'gigabitethernet0/0/0/0': + description => 'foo', + } +} +EOF" + type == 'ios_xr' ? ios_xr_str : nxos_str end end From 0f0b81a3e9cae8643425d904971c15f551c9ce6b Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Fri, 29 Apr 2016 13:10:56 -0400 Subject: [PATCH 007/203] Matrix updates for overlay_global & network_trunk --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1caf0909e..91e88d89d 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ The following table indicates which providers are supported on each platform. As | [cisco_ospf](#type-cisco_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | [cisco_ospf_vrf](#type-cisco_ospf_vrf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | ✅ = Supported
❌ = Unsupported
:heavy_minus_sign: = Not Applicable | N9k | N30xx | N31xx | N56xx | N6k | N7k | N8k | IOS XR | Caveats | -| [cisco_overlay_global](#type-cisco_overlay_global) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | ❌ | ❌ | ❌ | ✅ | :heavy_minus_sign: | +| [cisco_overlay_global](#type-cisco_overlay_global) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | ✅ | ✅ | ✅ | :heavy_minus_sign: | | [cisco_pim](#type-cisco_pim) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | [cisco_pim_rp_address](#type-cisco_pim_rp_address) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | [cisco_pim_grouplist](#type-cisco_pim_grouplist) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | @@ -182,7 +182,7 @@ The following table indicates which providers are supported on each platform. As | [network_dns](#type-network_dns) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [network_interface](#type-network_interface) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | [network_snmp](#type-network_snmp) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [network_trunk](#type-network_trunk) | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | +| [network_trunk](#type-network_trunk) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | [network_vlan](#type-network_vlan) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | | [ntp_config](#type-ntp_config) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [ntp_server](#type-ntp_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | @@ -2459,9 +2459,9 @@ Also configures anycast gateway MAC of the switch. | N9k | 7.0(3)I2(1) | 1.2.0 | | N30xx | not applicable | not applicable | | N31xx | not applicable | not applicable | -| N56xx | unsupported | unsupported | -| N6k | unsupported | unsupported | -| N7k | unsupported | unsupported | +| N56xx | 7.3(0)N1(1) | 1.3.0 | +| N6k | 7.3(0)N1(1) | 1.3.0 | +| N7k | 7.3(0)D1(1) | 1.3.0 | | N8k | 7.0(3)F1(1) | 1.3.0 | | IOS XR | not applicable | not applicable | From 4b144d4a6daf8e4fc7df939297251fe7247049b5 Mon Sep 17 00:00:00 2001 From: Garrett Honeycutt Date: Sat, 30 Apr 2016 16:09:29 -0400 Subject: [PATCH 008/203] domain and search options are mutually exclusive http://man7.org/linux/man-pages/man5/resolv.conf.5.html --- docs/README-agent-install.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/README-agent-install.md b/docs/README-agent-install.md index 198484329..03ab3581c 100644 --- a/docs/README-agent-install.md +++ b/docs/README-agent-install.md @@ -181,7 +181,6 @@ Regardless of OS, set up DNS configuration: cat >> /etc/resolv.conf << EOF nameserver 10.0.0.202 domain mycompany.com -search mycompany.com EOF ~~~ @@ -272,7 +271,6 @@ echo 'n3k' > /etc/hostname cat >> /etc/resolv.conf << EOF nameserver 10.0.0.202 domain mycompany.com -search mycompany.com EOF ~~~ @@ -419,7 +417,6 @@ echo 'n7k' > /etc/hostname cat >> /etc/resolv.conf << EOF nameserver 10.0.0.202 domain mycompany.com -search mycompany.com EOF ~~~ From 3cab58545fa495b323944b69d478f4816da48d47 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Wed, 4 May 2016 14:15:29 -0400 Subject: [PATCH 009/203] Remove source_interface_hold_down_time for 8k --- tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb b/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb index ca1c593ce..6aa79b3bf 100644 --- a/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb +++ b/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb @@ -62,7 +62,7 @@ def unsupported_properties(*) unprops = [] - unprops << :source_interface_hold_down_time unless platform[/n(8|9)k/] + unprops << :source_interface_hold_down_time unless platform[/n(9)k/] unprops end From 24f4ca63af7aa5fba980a08148c43daf3e646c47 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Fri, 20 May 2016 13:59:41 -0400 Subject: [PATCH 010/203] Collapse release_1.3.2 to develop --- CHANGELOG.md | 27 + README.md | 122 ++- examples/cisco/demo_bgp.pp | 65 +- examples/cisco/demo_interface.pp | 59 +- examples/cisco/demo_vlan.pp | 18 +- .../provider/cisco_command_config/cisco.rb | 8 + lib/puppet/provider/cisco_interface/cisco.rb | 141 +-- lib/puppet/provider/cisco_vlan/cisco.rb | 18 +- lib/puppet/type/cisco_interface.rb | 405 +++++--- lib/puppet/type/cisco_vlan.rb | 93 +- lib/puppet_x/cisco/autogen.rb | 31 + lib/puppet_x/cisco/cmnutils.rb | 147 ++- metadata.json | 2 +- tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb | 9 + .../cisco_interface/interfacelib.rb | 68 ++ .../cisco_interface/test_interface.rb | 963 ------------------ .../cisco_interface/test_interface_L2.rb | 138 +++ .../cisco_interface/test_interface_L3.rb | 166 +++ .../cisco_interface/test_interface_bdi.rb | 87 ++ .../test_interface_capabilities.rb | 113 +- .../test_interface_private_vlan.rb | 223 ++++ .../cisco_interface/test_interface_stp.rb | 86 +- .../cisco_interface/test_interface_svi.rb | 112 ++ .../test_interface_vlan_mapping.rb | 114 +++ .../cisco_interface/test_vlan_mapping.rb | 190 ---- .../accessvlan_provider_defaults.rb | 186 ---- .../accessvlan_provider_negatives.rb | 260 ----- .../accessvlan_provider_nondefaults.rb | 186 ---- .../beaker_tests/cisco_vlan/accessvlanlib.rb | 253 ----- .../cisco_vlan/test_private_vlan.rb | 93 ++ tests/beaker_tests/cisco_vlan/test_vlan.rb | 133 +++ .../vlan/extendedvlan_provider_defaults.rb | 144 --- .../vlan/extendedvlan_provider_negatives.rb | 160 --- .../vlan/extendedvlan_provider_nondefaults.rb | 143 --- .../vlan/standardvlan_provider_defaults.rb | 149 --- .../vlan/standardvlan_provider_negatives.rb | 160 --- .../vlan/standardvlan_provider_nondefaults.rb | 150 --- .../cisco_vlan/vlan/test_private_vlan.rb | 124 --- tests/beaker_tests/cisco_vlan/vlan/vlanlib.rb | 304 ------ tests/beaker_tests/lib/utilitylib.rb | 114 ++- 40 files changed, 2155 insertions(+), 3809 deletions(-) create mode 100755 tests/beaker_tests/cisco_interface/interfacelib.rb delete mode 100755 tests/beaker_tests/cisco_interface/test_interface.rb create mode 100755 tests/beaker_tests/cisco_interface/test_interface_L2.rb create mode 100755 tests/beaker_tests/cisco_interface/test_interface_L3.rb create mode 100755 tests/beaker_tests/cisco_interface/test_interface_bdi.rb create mode 100755 tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb create mode 100755 tests/beaker_tests/cisco_interface/test_interface_svi.rb create mode 100644 tests/beaker_tests/cisco_interface/test_interface_vlan_mapping.rb delete mode 100644 tests/beaker_tests/cisco_interface/test_vlan_mapping.rb delete mode 100644 tests/beaker_tests/cisco_vlan/accessvlan_provider_defaults.rb delete mode 100644 tests/beaker_tests/cisco_vlan/accessvlan_provider_negatives.rb delete mode 100644 tests/beaker_tests/cisco_vlan/accessvlan_provider_nondefaults.rb delete mode 100644 tests/beaker_tests/cisco_vlan/accessvlanlib.rb create mode 100644 tests/beaker_tests/cisco_vlan/test_private_vlan.rb create mode 100644 tests/beaker_tests/cisco_vlan/test_vlan.rb delete mode 100644 tests/beaker_tests/cisco_vlan/vlan/extendedvlan_provider_defaults.rb delete mode 100644 tests/beaker_tests/cisco_vlan/vlan/extendedvlan_provider_negatives.rb delete mode 100644 tests/beaker_tests/cisco_vlan/vlan/extendedvlan_provider_nondefaults.rb delete mode 100644 tests/beaker_tests/cisco_vlan/vlan/standardvlan_provider_defaults.rb delete mode 100644 tests/beaker_tests/cisco_vlan/vlan/standardvlan_provider_negatives.rb delete mode 100644 tests/beaker_tests/cisco_vlan/vlan/standardvlan_provider_nondefaults.rb delete mode 100644 tests/beaker_tests/cisco_vlan/vlan/test_private_vlan.rb delete mode 100644 tests/beaker_tests/cisco_vlan/vlan/vlanlib.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 767a338a3..82a57b36b 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,33 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Changed +## [1.3.2] - 2016-05-20 + +### Added +- Extended `cisco_bgp_af` to include l2vpn/evpn address-family support + +- Deprecated `cisco_interface` 'private-vlan' properties and replaced with new methods. The deprecated properties will be removed with release 2.0.0. The old -> new properties are: + +| Old Name | New Name(s) | +|:---|:---:| +| `private_vlan_mapping` | `pvlan_mapping` +| `switchport_mode_private_vlan_host` | `switchport_pvlan_host`, `switchport_pvlan_promiscuous`, +| `switchport_mode_private_vlan_host_association` | `switchport_pvlan_host_association` +| `switchport_mode_private_vlan_host_promiscous` | `switchport_pvlan_mapping` +| `switchport_mode_private_vlan_trunk_promiscuous`| `switchport_pvlan_trunk_promiscuous` +| `switchport_mode_private_vlan_trunk_secondary` | `switchport_pvlan_trunk_secondary` +| `switchport_private_vlan_association_trunk` | `switchport_pvlan_trunk_association` +| `switchport_private_vlan_mapping_trunk` | `switchport_pvlan_mapping_trunk` +| `switchport_private_vlan_trunk_allowed_vlan` | `switchport_pvlan_trunk_allowed_vlan` +| `switchport_private_vlan_trunk_native_vlan` | `switchport_pvlan_trunk_native_vlan` + +- Deprecated `cisco_vlan` 'private-vlan' properties and replaced with new methods. The deprecated properties will be removed with release 2.0.0. The old -> new properties are: + +| Old Name | New Name | +|:---|:---:| +| `private_vlan_association` | `pvlan_association` +| `private_vlan_type` | `pvlan_type` + ## [1.3.1] - 2016-05-06 ### New feature support diff --git a/README.md b/README.md index ac77081d2..ad882caeb 100644 --- a/README.md +++ b/README.md @@ -1052,6 +1052,7 @@ Manages configuration of a BGP Address-family instance. |----------|:------------------:|:----------------------:| | N9k | 7.0(3)I2(1) | 1.1.0 | | N3k | 7.0(3)I2(1) | 1.1.0 | +| N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | @@ -1059,8 +1060,9 @@ Manages configuration of a BGP Address-family instance. | Property | Caveat Description | |:--------|:-------------| -| `additional_paths_install` | Not supported on N3k, N9k | -| `advertise_l2vpn_evpn` | Not supported on N3k, N6k | +| `additional_paths_install` | Not supported on N3k, N9k | +| `advertise_l2vpn_evpn` | Not supported on N3k, N6k | +| address-family `l2vpn/evpn` | Module Minimum Version 1.3.2
OS Minimum Version 7.0(3)I3(1)
Not supported on N3k | #### Parameters @@ -1690,9 +1692,20 @@ Manages a Cisco Network Interface. Any resource dependency should be run before | Property | Caveat Description | |:---------|:-------------| -| `svi_autostate` | Only supported on N3k,N7k,N9k | -| `vlan_mapping` | Only supported on N7k | -| `vlan_mapping_enable` | Only supported on N7k | +| `pvlan_mapping` | Not supported on N8k | +| `switchport_pvlan_host` | Not supported on N8k | +| `switchport_pvlan_host_association | Not supported on N8k | +| `switchport_pvlan_mapping` | Not supported on N8k | +| `switchport_pvlan_mapping_trunk` | Not supported on N3k,N8k | +| `switchport_pvlan_promiscuous` | Not supported on N8k | +| `switchport_pvlan_trunk_allowed_vlan` | Not supported on N8k | +| `switchport_pvlan_trunk_association` | Not supported on N3k,N8k | +| `switchport_pvlan_trunk_native_vlan` | Not supported on N8k | +| `switchport_pvlan_trunk_promiscuous` | Not supported on N3k,N8k | +| `switchport_pvlan_trunk_secondary` | Not supported on N3k,N8k | +| `svi_autostate` | Only supported on N3k,N7k,N9k | +| `vlan_mapping` | Only supported on N7k | +| `vlan_mapping_enable` | Only supported on N7k | #### Parameters @@ -1739,35 +1752,66 @@ interface. Valid value is an integer. ##### `switchport_autostate_exclude` Exclude this port for the SVI link calculation. Valid values are 'true', 'false', and 'default'. -##### `private_vlan_mapping` -Private vlan mapping for interface vlan. List of secondary vlans associated to the interface vlan primary. +##### `pvlan_mapping` +Maps secondary VLANs to the VLAN interface of a primary VLAN. Valid inputs are a String containing a range of secondary vlans or keyword 'default'. -##### `switchport_mode_private_vlan_host` -Switchport host mode for private vlan. This a L2 access port. There are two modes: host and promiscous. +Example: `pvlan_mapping => '3-4,6'` -##### `switchport_mode_private_vlan_host_association` -This configuration specify which vlans are associated on this port. Host mode only support a pair of vlans: primary and secondary. Valid values are an array of ["primary_vlan", "secondary_vlan"] pairs. - -##### `switchport_mode_private_vlan_host_promisc` -This configuration specify which vlans are associated on this port. Promiscous mode only support a pair of vlans: primary and secondaries. Valid values are an array of ["primary_vlan", "secondary_vlan"] pairs. +##### `switchport_pvlan_host` +Configures a Layer 2 interface as a private VLAN host port. Valid values are 'true', 'false', and 'default' + +##### `switchport_pvlan_host_association` +Associates the Layer 2 host port with the primary and secondary VLANs of a private VLAN. Valid inputs are: An array containing the primary and secondary vlans, or keyword 'default'. + +Example: `switchport_pvlan_host_association => ['44', '144']` + +##### `switchport_pvlan_mapping` +Associates the specified port with a primary VLAN and a selected list of secondary VLANs. Valid inputs are an array containing both the primary vlan and a range of secondary vlans, or keyword 'default'. + +Example: `switchport_pvlan_mapping => ['44', '3-4,6']` + +##### `switchport_pvlan_mapping_trunk` +Maps the promiscuous trunk port with the primary VLAN and a selected list of associated secondary VLANs. Valid inputs are: An array containing both the primary vlan and a range of secondary vlans, a nested array if there are multiple mappings, or keyword 'default'. + +Examples: + +``` + switchport_pvlan_mapping_trunk => [['44', '3-4,6'], ['99', '199']] + + -or- + + switchport_pvlan_mapping_trunk => ['44', '3-4,6'] +``` + +##### `switchport_pvlan_trunk_allowed_vlan` +Sets the allowed VLANs for the private VLAN isolated trunk interface. Valid values are a String range of vlans or keyword 'default'. -##### `switchport_mode_private_vlan_trunk_promiscuous` -Switchport trunk promisc mode for private vlan. This a L2 trunk port capable of carrying multiple primary vlans. +Example: `switchport_pvlan_trunk_allowed_vlan => '3-4,6'` -##### `switchport_mode_private_vlan_trunk_secondary` -Switchport trunk secondary mode for private vlan. This a L2 trunk port capable of carrying multiple secondary vlans. +##### `switchport_pvlan_trunk_association` +Associates the Layer 2 isolated trunk port with the primary and secondary VLANs of private VLANs. Valid inputs are: An array containing an association of primary and secondary vlans, a nested array if there are multiple associations, or the keyword 'default'. -#### `switchport_private_vlan_association_trunk` -This configuration specify which vlans are associated on this trunk secondary port. Pair of distinguish vlans in the form of primary and secondary are accepted per entry. Valid values are an array of ["primary_vlan", "secondary_vlan"] pairs. +Examples: -#### `switchport_private_vlan_mapping_trunk` -This configuration specify which vlans are associated on this trunk promisc port. Pair of distinguish vlans in the form of primary vlan and secondary vlans (single or range) are accepted per entry. Valid values are an array of ["primary_vlan", "secondary_vlan"] pairs. +``` +switchport_pvlan_trunk_association => [['44', '244'], ['45', '245']] -#### `switchport_private_vlan_trunk_allowed_vlan` -This configuration specify which private vlans are associated on this trunk port. Valid values are an array of ["vlan"]. + -or- -#### `switchport_private_vlan_trunk_native_vlan` -This configuration specify the native vlan as a private vlan. Valid values are integers. +switchport_pvlan_trunk_association => ['44', '244'] +``` + +##### `switchport_pvlan_trunk_native_vlan` +Sets the native VLAN for the 802.1Q trunk. Valid values are Integer, String, or keyword 'default'. + +##### `switchport_pvlan_promiscuous` +Configures a Layer 2 interface as a private VLAN promiscuous port. Valid values are 'true', 'false', and 'default'. + +##### `switchport_pvlan_trunk_promiscuous` +Configures a Layer 2 interface as a private VLAN promiscuous trunk port. Valid values are 'true', 'false', and 'default'. + +##### `switchport_pvlan_trunk_secondary` +Configures a Layer 2 interface as a private VLAN isolated trunk port. Valid values are 'true', 'false', and 'default'. ##### `switchport_trunk_allowed_vlan` The allowed VLANs for the specified Ethernet interface. Valid values are @@ -2876,8 +2920,10 @@ Manages a Cisco VLAN. | Property | Caveat Description | |:--------|:-------------| -| `fabric_control` | Only supported on N7k (support added in ciscopuppet 1.3.0) | -| `mode` | Only supported on N5k,N6k,N7k | +| `fabric_control` | Only supported on N7k (support added in ciscopuppet 1.3.0) | +| `mode` | Only supported on N5k,N6k,N7k | +| `pvlan_type` | Not supported on N8k | +| `pvlan_association` | Not supported on N8k | #### Parameters @@ -2904,12 +2950,20 @@ State of the VLAN. Valid values are 'active', 'suspend', and keyword 'default'. Whether or not the vlan is shutdown. Valid values are 'true', 'false' and keyword 'default'. -##### `private_vlan_type` -The private vlan type. Valid values are 'primary', 'isolated' and 'community'. - -##### `private_vlan_association` -Associate the secondary vlanis to the primary vlan. Valid values are integer like 5,10-12. - +##### `pvlan_type` +The private vlan type. Valid values are: 'primary', 'isolated', 'community' or 'default'. + +##### `pvlan_association` +Associates the secondary vlan(s) to the primary vlan. Valid values are an Array or String of vlan ranges, or keyword 'default'. + +Examples: + +``` +pvlan_associate => ['2-5, 9'] + -or- +pvlan_associate => '2-5, 9' +``` + ##### `fabric_control` Specifies this vlan as the fabric control vlan. Only one bridge-domain or VLAN can be configured as fabric-control. Valid values are true, false. diff --git a/examples/cisco/demo_bgp.pp b/examples/cisco/demo_bgp.pp index c4b9cd293..5d92298ee 100644 --- a/examples/cisco/demo_bgp.pp +++ b/examples/cisco/demo_bgp.pp @@ -111,16 +111,17 @@ default => undef } - cisco_bgp { '55.77 blue': - ensure => present, - - confederation_id => $confederation_id, - confederation_peers => $confederation_peers, - enforce_first_as => true, - log_neighbor_changes => true, - timer_bgp_keepalive => '60', - timer_bgp_holdtime => '120', - route_distinguisher => auto, + if platform_get() != 'n3k' { + cisco_bgp { '55.77 blue': + ensure => present, + + confederation_id => $confederation_id, + confederation_peers => $confederation_peers, + log_neighbor_changes => true, + timer_bgp_keepalive => '60', + timer_bgp_holdtime => '120', + route_distinguisher => auto, + } } # --------------------------------------------------------------------------# @@ -130,8 +131,8 @@ $ipv4_redistribute = [['eigrp 1', 'e_rtmap_29'], ['ospf 3', 'o_rtmap']] $ipv4_injectmap = [['nyc', 'sfo'], ['sjc', 'sfo', 'copy-attributes']] - $additional_paths_install = $operatingsystem ? { - 'nexus' => true, + $additional_paths_install = platform_get() ? { + /(n5k|n6k|n7k)/ => true, default => undef } $dampen_igp_metric = $operatingsystem ? { @@ -188,6 +189,29 @@ redistribute => $ipv4_redistribute, } + if platform_get() != 'n3k' { + cisco_bgp_af { '55.77 default l2vpn evpn': + ensure => present, + #asn => 55.77, + #vrf => 'default', + #afi => 'l2vpn', + #safi => 'evpn', + # Properties + + # dampening_routemap is mutually exclusive with + # dampening_half_time, reuse_time, suppress_time + # and max_suppress_time. + # + dampening_state => true, + dampening_half_time => 1, + dampening_reuse_time => 2, + dampening_suppress_time => 3, + dampening_max_suppress_time => 4, + #dampening_routemap => default, + next_hop_route_map => 'RouteMap', + } + } + # --------------------------------------------------------------------------# # Configure Address Family IPv6 Unicast # # --------------------------------------------------------------------------# @@ -361,8 +385,8 @@ if $operatingsystem == 'ios_xr' { cisco_bgp_neighbor { '55.77 default 1.1.1.1': - ensure => present, - remote_as => 2, + ensure => present, + remote_as => 2, } } @@ -370,7 +394,8 @@ ensure => present, # Properties - allowas_in => 'default', + # allowas_in should be true if allowas_in_max is specified + allowas_in => true, allowas_in_max => 5, default_originate_route_map => $default_originate_route_map, max_prefix_limit => 100, @@ -392,8 +417,11 @@ # Configure Neighbor-level Address Family IPv4 Unicast (non-default vrf) # --------------------------------------------------------------------------# - cisco_bgp_af { '55.77 default vpnv4 unicast': - ensure => present, + # TBD: vpnv4 support will be added for I4 images + if platform_get() != 'n3k' { + cisco_bgp_af { '55.77 default vpnv4 unicast': + ensure => present, + } } cisco_bgp_af { '55.77 blue ipv4 unicast': ensure => present, @@ -403,7 +431,8 @@ ensure => present, # Properties - allowas_in => 'default', + # allowas_in should be true if allowas_in_max is specified + allowas_in => true, allowas_in_max => 5, default_originate_route_map => $default_originate_route_map, as_override => true, diff --git a/examples/cisco/demo_interface.pp b/examples/cisco/demo_interface.pp index 825147d3c..01f7d9350 100755 --- a/examples/cisco/demo_interface.pp +++ b/examples/cisco/demo_interface.pp @@ -108,24 +108,57 @@ warning('This platform does not support cisco_bridge_domain') } - # For private vlan + # Private-vlan if platform_get() =~ /n(3|5|6|7|9)k/ { - cisco_vlan { '445': - ensure => present, - private_vlan_type => 'isolated', + cisco_vlan { '12': pvlan_type => 'community' } + cisco_vlan { '2': pvlan_type => 'primary', pvlan_association => '12' } + + cisco_interface { 'Ethernet1/6': + description => 'Private-vlan Host Port', + switchport_pvlan_host => true, + switchport_pvlan_host_association => [2, 12], + } + + cisco_vlan { '13': pvlan_type => 'isolated' } + cisco_vlan { '14': pvlan_type => 'isolated' } + cisco_vlan { '3': pvlan_type => 'primary', pvlan_association => '13' } + cisco_vlan { '4': pvlan_type => 'primary', pvlan_association => '14' } + + cisco_vlan { '15': pvlan_type => 'community' } + cisco_vlan { '5': pvlan_type => 'primary', pvlan_association => '15' } + + cisco_vlan { '17': pvlan_type => 'community' } + cisco_vlan { '27': pvlan_type => 'community' } + cisco_vlan { '37': pvlan_type => 'community' } + cisco_vlan { '7': pvlan_type => 'primary', pvlan_association => '17,27,37' } + + # Ethernet1/7 platform checks + $trunk_secondary = platform_get() ? { + /(n3k)/ => undef, + default => true } - cisco_vlan { '444': - ensure => present, - private_vlan_type => 'primary', - private_vlan_association => ['445'], + $trunk_assoc = platform_get() ? { + /(n3k)/ => undef, + default => [[3, 13], [4, 14]] } - cisco_interface { 'Ethernet1/6': - description => 'private_vlan_host_port', - switchport_mode_private_vlan_host => 'host', - switchport_mode_private_vlan_host_association => ['444', '445'], + $trunk_map = platform_get() ? { + /(n3k)/ => undef, + default => [['5', '15'], ['7', '17,27,37']] + } + cisco_interface { 'Ethernet1/7': + description => 'Private-vlan Trunk Port', + switchport_pvlan_trunk_secondary => $trunk_secondary, + switchport_pvlan_trunk_allowed_vlan => '106,102-103,105', + switchport_pvlan_trunk_association => $trunk_assoc, + switchport_pvlan_trunk_native_vlan => 42, + switchport_pvlan_mapping_trunk => $trunk_map, + } + cisco_interface { 'vlan29': + description => 'SVI Private-vlan Mapping', + pvlan_mapping => '108-109', } } else { - warning('This platform does not support the private vlan feature') + warning('This platform does not support the private-vlan feature') } # Requires F3 or newer linecards diff --git a/examples/cisco/demo_vlan.pp b/examples/cisco/demo_vlan.pp index 571ef4b8c..749212d0b 100644 --- a/examples/cisco/demo_vlan.pp +++ b/examples/cisco/demo_vlan.pp @@ -24,19 +24,19 @@ default => undef } cisco_vlan { '220': - ensure => present, - mapped_vni => $mapped_vni, - vlan_name => 'newtest', - shutdown => true, - state => 'active', - fabric_control => $fabric_control + ensure => present, + mapped_vni => $mapped_vni, + vlan_name => 'newtest', + shutdown => true, + state => 'active', + fabric_control => $fabric_control } # For private vlan if platform_get() =~ /n(3|5|6|7|9)k/ { cisco_vlan { '333': - ensure => present, - private_vlan_type => 'primary', - private_vlan_association => ['334,336-339'], + ensure => present, + pvlan_type => 'primary', + pvlan_association => ['334,336-339'], } } else { diff --git a/lib/puppet/provider/cisco_command_config/cisco.rb b/lib/puppet/provider/cisco_command_config/cisco.rb index 624068659..362ae3251 100644 --- a/lib/puppet/provider/cisco_command_config/cisco.rb +++ b/lib/puppet/provider/cisco_command_config/cisco.rb @@ -44,6 +44,14 @@ def command # Compare full manifest config to running-config. existing_str = manifest_hash.compare_with(running_hash) debug "Existing:\n>#{existing_str}<" + # Note: 'existing_str' may sometimes include confusing (but normal) contents + # if the 'manifest_hash' contains a 'no' command that is not present in + # running-config. For example, manifest_hash contains 'no foo bar'; the + # compare_with() logic does not find 'no foo bar' so it will strip the 'no' + # and search for 'foo bar' (in the proper context); that will not be found + # so it will add 'no foo bar' to 'existing_str', which allows the logic + # below to exclude 'no foo bar' from 'min_config_hash'. + manifest_config_str = Cisco::ConfigParser::Configuration.config_hash_to_str( manifest_hash.configuration) diff --git a/lib/puppet/provider/cisco_interface/cisco.rb b/lib/puppet/provider/cisco_interface/cisco.rb index 51c4bee27..3dd445300 100644 --- a/lib/puppet/provider/cisco_interface/cisco.rb +++ b/lib/puppet/provider/cisco_interface/cisco.rb @@ -31,13 +31,16 @@ mk_resource_methods - # Property symbol arrays for method auto-generation. There are separate arrays - # because the boolean-based methods are processed slightly different. - # Note: switchport_mode should always process first to evaluate L2 vs L3. - # Note: vrf should be the first L3 property to process. The AutoGen vrf - # setter is not used. + # Property symbol arrays for getter/setter method auto-generation. Separate + # arrays are used to classify different property behaviors and generate + # appropriate dynamic methods. Note that some auto-generated methods may be + # overridden within this file; e.g. the vrf getter is auto-generated but the + # vrf setter is overridden with an explicit method. + # Please maintain the properties in alpha order except where noted. INTF_NON_BOOL_PROPS = [ + # Note: :switchport_mode must always process first to evaluate L2 vs L3 :switchport_mode, + # Note: :vrf must be the first L3 property to process :vrf, :access_vlan, :description, @@ -61,10 +64,10 @@ :stp_link_type, :stp_port_priority, :stp_port_type, - :switchport_mode_private_vlan_host, :switchport_trunk_allowed_vlan, :switchport_trunk_native_vlan, - :switchport_private_vlan_trunk_native_vlan, + :switchport_pvlan_trunk_native_vlan, + :switchport_pvlan_trunk_allowed_vlan, :vlan_mapping, :vpc_id, ] @@ -77,8 +80,10 @@ :negotiate_auto, :shutdown, :switchport_autostate_exclude, - :switchport_mode_private_vlan_trunk_promiscuous, - :switchport_mode_private_vlan_trunk_secondary, + :switchport_pvlan_host, + :switchport_pvlan_promiscuous, + :switchport_pvlan_trunk_promiscuous, + :switchport_pvlan_trunk_secondary, :switchport_vtp, :svi_autostate, :svi_management, @@ -86,33 +91,73 @@ :vpc_peer_link, ] INTF_ARRAY_FLAT_PROPS = [ - :private_vlan_mapping, + :pvlan_mapping, :stp_mst_cost, :stp_mst_port_priority, :stp_vlan_cost, :stp_vlan_port_priority, + :switchport_pvlan_host_association, + :switchport_pvlan_mapping, + ] + + INTF_ARRAY_NESTED_PROPS = [ + :switchport_pvlan_mapping_trunk, + :switchport_pvlan_trunk_association, + ] + + # TBD: These DEPRECATED arrays will be removed with release 2.0.0 + DEPRECATED_INTF_FLAT = [ + :private_vlan_mapping, + # Replaced by: pvlan_mapping :switchport_mode_private_vlan_host_association, + # Replaced by: switchport_pvlan_host_association :switchport_mode_private_vlan_host_promisc, + # Replaced by: switchport_pvlan_mapping, + :switchport_private_vlan_trunk_allowed_vlan, + # Replaced by: switchport_pvlan_trunk_allowed_vlan, :switchport_private_vlan_association_trunk, + # Replaced by: switchport_pvlan_trunk_association :switchport_private_vlan_mapping_trunk, + # Replaced by: switchport_pvlan_mapping_trunk + ] + DEPRECATED_INTF_BOOL = [ + :switchport_mode_private_vlan_trunk_promiscuous, + # Replaced by: switchport_pvlan_trunk_promiscuous, + :switchport_mode_private_vlan_trunk_secondary, + # Replaced by: switchport_pvlan_trunk_secondary, + ] + DEPRECATED_INTF_NON_BOOL = [ + :switchport_mode_private_vlan_host, + # Replaced by: switchport_pvlan_host, :switchport_private_vlan_trunk_allowed_vlan, + # Replaced by: switchport_pvlan_trunk_allowed_vlan, + :switchport_private_vlan_trunk_native_vlan, + # Replaced by: switchport_pvlan_trunk_native_vlan, ] - INTF_ALL_PROPS = INTF_NON_BOOL_PROPS + INTF_BOOL_PROPS + INTF_ARRAY_FLAT_PROPS + INTF_ARRAY_FLAT_PROPS.concat(DEPRECATED_INTF_FLAT) + INTF_BOOL_PROPS.concat(DEPRECATED_INTF_BOOL) + INTF_NON_BOOL_PROPS.concat(DEPRECATED_INTF_NON_BOOL) + # End DEPRECATED - PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@interface', + PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@nu', INTF_NON_BOOL_PROPS) - PuppetX::Cisco::AutoGen.mk_puppet_methods(:bool, self, '@interface', + PuppetX::Cisco::AutoGen.mk_puppet_methods(:bool, self, '@nu', INTF_BOOL_PROPS) - PuppetX::Cisco::AutoGen.mk_puppet_methods(:array_flat, self, '@interface', + PuppetX::Cisco::AutoGen.mk_puppet_methods(:array_flat, self, '@nu', INTF_ARRAY_FLAT_PROPS) + PuppetX::Cisco::AutoGen.mk_puppet_methods(:array_nested, self, '@nu', + INTF_ARRAY_NESTED_PROPS) + + INTF_NON_BOOL_PROPS.concat(INTF_ARRAY_FLAT_PROPS + INTF_ARRAY_NESTED_PROPS) + INTF_ALL_PROPS = INTF_NON_BOOL_PROPS + INTF_BOOL_PROPS def initialize(value={}) super(value) - @interface = Cisco::Interface.interfaces[@property_hash[:name]] + @nu = Cisco::Interface.interfaces[@property_hash[:name]] @property_flush = {} end - def self.properties_get(interface_name, intf) + def self.properties_get(interface_name, nu_obj) debug "Checking instance, #{interface_name}." current_state = { interface: interface_name, @@ -121,35 +166,26 @@ def self.properties_get(interface_name, intf) } # Call node_utils getter for each property INTF_NON_BOOL_PROPS.each do |prop| - current_state[prop] = intf.send(prop) + current_state[prop] = nu_obj.send(prop) end INTF_BOOL_PROPS.each do |prop| - val = intf.send(prop) + val = nu_obj.send(prop) if val.nil? current_state[prop] = nil else current_state[prop] = val ? :true : :false end end - INTF_ARRAY_FLAT_PROPS.each do |prop| - current_state[prop] = intf.send(prop) - end - # nested array properties - current_state[:vlan_mapping] = intf.vlan_mapping - current_state[:stp_mst_cost] = intf.stp_mst_cost - current_state[:stp_mst_port_priority] = intf.stp_mst_port_priority - current_state[:stp_vlan_cost] = intf.stp_vlan_cost - current_state[:stp_vlan_port_priority] = intf.stp_vlan_port_priority new(current_state) end # self.properties_get def self.instances interfaces = [] - Cisco::Interface.interfaces.each do |interface_name, intf| + Cisco::Interface.interfaces.each do |interface_name, nu_obj| begin # Not allowed to create an interface for mgmt0 or MgmtEth0/* next if interface_name.match(/mgmt/i) - interfaces << properties_get(interface_name, intf) + interfaces << properties_get(interface_name, nu_obj) end end interfaces @@ -184,8 +220,8 @@ def properties_set(new_interface=false) next unless @resource[prop] send("#{prop}=", @resource[prop]) if new_interface unless @property_flush[prop].nil? - @interface.send("#{prop}=", @property_flush[prop]) if - @interface.respond_to?("#{prop}=") + @nu.send("#{prop}=", @property_flush[prop]) if + @nu.respond_to?("#{prop}=") end end ipv4_addr_mask_set @@ -206,17 +242,17 @@ def ipv4_addr_mask_configure(secondary=false) @resource[v4_addr_prop] == :default if @resource[v4_addr_prop] == :default - addr = @interface.default_ipv4_address + addr = @nu.default_ipv4_address else addr = @resource[v4_addr_prop] end if @resource[v4_mask_prop] == :default - mask = @interface.default_ipv4_netmask_length + mask = @nu.default_ipv4_netmask_length else mask = @resource[v4_mask_prop] end - @interface.ipv4_addr_mask_set(addr, mask, secondary) + @nu.ipv4_addr_mask_set(addr, mask, secondary) end def ipv4_addr_mask_set @@ -232,28 +268,28 @@ def ipv4_addr_mask_set end def stp_mst_cost=(should_list) - should_list = @interface.default_stp_mst_cost if should_list[0] == :default + should_list = @nu.default_stp_mst_cost if should_list[0] == :default # check for overlapping arrays in should_list PuppetX::Cisco::Utils.fail_array_overlap(should_list) @property_flush[:stp_mst_cost] = should_list end def stp_mst_port_priority=(should_list) - should_list = @interface.default_stp_mst_port_priority if should_list[0] == :default + should_list = @nu.default_stp_mst_port_priority if should_list[0] == :default # check for overlapping arrays in should_list PuppetX::Cisco::Utils.fail_array_overlap(should_list) @property_flush[:stp_mst_port_priority] = should_list end def stp_vlan_cost=(should_list) - should_list = @interface.default_stp_vlan_cost if should_list[0] == :default + should_list = @nu.default_stp_vlan_cost if should_list[0] == :default # check for overlapping arrays in should_list PuppetX::Cisco::Utils.fail_array_overlap(should_list) @property_flush[:stp_vlan_cost] = should_list end def stp_vlan_port_priority=(should_list) - should_list = @interface.default_stp_vlan_port_priority if should_list[0] == :default + should_list = @nu.default_stp_vlan_port_priority if should_list[0] == :default # check for overlapping arrays in should_list PuppetX::Cisco::Utils.fail_array_overlap(should_list) @property_flush[:stp_vlan_port_priority] = should_list @@ -262,7 +298,7 @@ def stp_vlan_port_priority=(should_list) def vlan_mapping return @property_hash[:vlan_mapping] if @resource[:vlan_mapping].nil? if @resource[:vlan_mapping][0] == :default && - @property_hash[:vlan_mapping] == @interface.default_vlan_mapping + @property_hash[:vlan_mapping] == @nu.default_vlan_mapping return [:default] else @property_hash[:vlan_mapping] @@ -270,13 +306,13 @@ def vlan_mapping end def vlan_mapping=(should_list) - should_list = @interface.default_vlan_mapping if should_list[0] == :default + should_list = @nu.default_vlan_mapping if should_list[0] == :default @property_flush[:vlan_mapping] = should_list end # override vrf setter def vrf=(val) - val = @interface.default_vrf if val == :default + val = @nu.default_vrf if val == :default @property_flush[:vrf] = val # flush other L3 properties because vrf will wipe them out @@ -294,30 +330,15 @@ def vrf=(val) def flush if @property_flush[:ensure] == :absent - @interface.destroy - @interface = nil + @nu.destroy + @nu = nil else # Create/Update - if @interface.nil? + if @nu.nil? new_interface = true - @interface = Cisco::Interface.new(@resource[:interface]) + @nu = Cisco::Interface.new(@resource[:interface]) end properties_set(new_interface) end - puts_config end - - def puts_config - if @interface.nil? - info "Interface=#{@resource[:interface]} is absent." - return - end - - # Dump all current properties for this interface - current = sprintf("\n%30s: %s", 'interface', @interface.name) - INTF_ALL_PROPS.each do |prop| - current.concat(sprintf("\n%30s: %s", prop, @interface.send(prop))) - end - debug current - end # puts_config end # Puppet::Type diff --git a/lib/puppet/provider/cisco_vlan/cisco.rb b/lib/puppet/provider/cisco_vlan/cisco.rb index bf0edd4e1..ab9e0b48e 100644 --- a/lib/puppet/provider/cisco_vlan/cisco.rb +++ b/lib/puppet/provider/cisco_vlan/cisco.rb @@ -31,10 +31,24 @@ mk_resource_methods - VLAN_ARRAY_FLAT_PROPS = [:private_vlan_association] + VLAN_ARRAY_FLAT_PROPS = [:pvlan_association] VLAN_NON_BOOL_PROPS = [:mapped_vni, :mode, :state, - :private_vlan_type, :vlan_name] + :pvlan_type, :vlan_name] VLAN_BOOL_PROPS = [:fabric_control, :shutdown] + + # TBD: These DEPRECATED arrays will be removed with release 2.0.0 + DEPRECATED_VLAN_ARRAY_FLAT = [ + :private_vlan_association + # Replaced by: pvlan_association + ] + DEPRECATED_VLAN_NON_BOOL = [ + :private_vlan_type + # Replaced by: pvlan_type + ] + VLAN_ARRAY_FLAT_PROPS.concat(DEPRECATED_VLAN_ARRAY_FLAT) + VLAN_NON_BOOL_PROPS.concat(DEPRECATED_VLAN_NON_BOOL) + # End DEPRECATED + VLAN_ALL_PROPS = VLAN_ARRAY_FLAT_PROPS + VLAN_NON_BOOL_PROPS + VLAN_BOOL_PROPS PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@vlan', diff --git a/lib/puppet/type/cisco_interface.rb b/lib/puppet/type/cisco_interface.rb index 96dbe1e5b..fa999cafb 100755 --- a/lib/puppet/type/cisco_interface.rb +++ b/lib/puppet/type/cisco_interface.rb @@ -25,31 +25,31 @@ end Puppet::Type.newtype(:cisco_interface) do - @doc = "Manages a Cisco Network Interface. + @doc = %(Manages a Cisco Network Interface. - Any resource dependency should be run before the interface resource. + Resource dependencies should process before the interface resource. - cisco_interface {\"\": + cisco_interface {'': ..attributes.. } is the complete name of the interface. - Example: - cisco_interface {\"Ethernet1/15\": + Examples: + cisco_interface {'ethernet1/15': shutdown => false, - description => \"switched port\", + description => 'switched port', switchport_mode => access, access_vlan => 2, switchport_autostate_exclude => true, switchport_vtp => true, } - cisco_interface { \"Ethernet1/16\" : + cisco_interface { 'ethernet1/16' : shutdown => true, - description => \"routed port\", - ipv4_address => \"192.168.1.1\", + description => 'routed port', + ipv4_address => '192.168.1.1', ipv4_netmask_length => 24, - ipv4_address_secondary => \"192.168.2.1\", + ipv4_address_secondary => '192.168.2.1', ipv4_netmask_length_secondary => 24, ipv4_forwarding => true, ipv4_redirects => true, @@ -57,7 +57,7 @@ ipv4_pim_sparse_mode => true, negotiate_auto => true, } - cisco_interface { \"Ethernet1/17\" : + cisco_interface { 'ethernet1/17' : stp_bpdufilter => 'enable', stp_bpduguard => 'enable', stp_cost => 2000, @@ -70,74 +70,48 @@ stp_vlan_cost => [[1-4,6,8-12, 1000], [1000, 2568]], stp_vlan_port_priority => [[1-11,20-33, 64], [1111, 160], } - cisco_interface { \"Ethernet9/1\" : + cisco_interface { 'ethernet9/1' : switchport_mode => 'trunk', vlan_mapping_enable => 'false', vlan_mapping => [[20, 21], [30, 31]], } - cisco_interface { \"loopback42\" : - description => \"logical interface\", + cisco_interface { 'loopback42' : + description => 'logical interface', shutdown => true, } - cisco_interface { \"loopback43\" : + cisco_interface { 'loopback43' : ensure => absent, # ensure will create or destroy a logical interface. If not specified # then the default behavior is to attempt to create the interface. } - cisco_interface {\"Vlan98\": + cisco_interface {'Vlan98': shutdown => true, - description => \"svi interface\", + description => 'svi interface', ipv4_arp_timeout => 300, svi_autostate => true, svi_management => true, } - #Private vlan config example - cisco_interface { \"Ethernet8/1\" : - switchport_mode_private_vlan_host => 'host', - switchport_mode_private_vlan_host_association => ['10', '11'], + cisco_interface { 'ethernet8/1' : + description => 'Private-vlan host', + switchport_pvlan_host => 'host', + switchport_pvlan_host_association => ['10', '11'], } - cisco_interface { \"Ethernet8/1\" : - switchport_mode_private_vlan_host => 'promiscuous', - switchport_mode_private_vlan_host_promisc=> ['10', '11'], + cisco_interface { 'ethernet8/1' : + description => 'Private-vlan trunk', + switchport_pvlan_trunk_promiscuous => true, + switchport_pvlan_trunk_association => [['14', '114'], ['15', '115']], + switchport_pvlan_trunk_allowed_vlan => '88-91,94', + switchport_pvlan_trunk_native_vlan => 12, } - cisco_interface { \"Ethernet8/1\" : - switchport_mode_private_vlan_trunk_promiscuous => true, - switchport_private_vlan_mapping_trunk => ['10', '11'], + cisco_interface {'Vlan98': + pvlan_mapping => '10-11,13', } - cisco_interface { \"Ethernet8/1\" : - switchport_mode_private_vlan_trunk_secondary => true, - switchport_private_vlan_association_trunk => ['10', '11'], - } - cisco_interface { \"Ethernet8/1\" : - switchport_private_vlan_trunk_allowed_vlan => ['10-11'], - } - cisco_interface { \"Ethernet8/1\" : - switchport_private_vlan_trunk_native_vlan => 10, - } - cisco_interface {\"Vlan98\": - private_vlan_mapping => ['10-11'], - }" + ) ################### # Resource Naming # ################### - # Parse out the title to fill in the attributes in these - # patterns. These attributes can be overwritten later. - def self.title_patterns - identity = ->(x) { x } - patterns = [] - - # Below pattern matches both parts of the full composite name. - patterns << [ - /^(\S+)/, - [ - [:interface, identity] - ], - ] - patterns - end - newparam(:interface, namevar: :true) do desc 'Name of the interface on the network element. Valid values are string.' @@ -728,9 +702,201 @@ def is_to_s(value) # private vlan attributes # ########################### - newproperty(:switchport_mode_private_vlan_host) do - desc 'Switchport private host mode of the interface.' + # ------------------------ + newproperty(:pvlan_mapping, array_matching: :all) do + inputs = + 'Valid inputs are a String containing a range of secondary vlans, '\ + "example: '3-4,6'; or keyword 'default'" + desc 'Maps secondary VLANs to the VLAN interface of a primary VLAN. ' + inputs + + validate do |value| + fail inputs unless value.to_s.delete(' ')[/^(default|[-,\d]+)$/] + end + + munge do |value| + value.to_s[/default/] ? :default : value.to_s.delete(' ') + end + end + + # ------------------------ + newproperty(:switchport_pvlan_host) do + inputs = "Valid values are 'true', 'false', and 'default'." + desc 'Configures a Layer 2 interface as a private VLAN host port. ' + inputs + newvalues(:true, :false, :default) + end + + # ------------------------ + newproperty(:switchport_pvlan_host_association, array_matching: :all) do + inputs = + 'Valid inputs are: An array containing the primary and secondary vlans: '\ + "e.g.: ['44', '144']; or keyword 'default'" + desc 'Associates the Layer 2 host port with the primary and secondary '\ + 'VLANs of a private VLAN. ' + inputs + + validate do |value| + fail inputs unless value.to_s.delete(' ')[/^(default|\d+)$/] + end + + munge { |value| value.to_s[/default/] ? :default : value.to_s.delete(' ') } + end + + # ------------------------ + newproperty(:switchport_pvlan_mapping, array_matching: :all) do + inputs = + 'Valid inputs are an array containing both the primary vlan and a '\ + "range of secondary vlans, example: ['44', '3-4,6'] or keyword 'default'" + desc 'Associates the specified port with a primary VLAN and a selected '\ + 'list of secondary VLANs. ' + inputs + + validate do |value| + fail inputs unless value.to_s.delete(' ')[/^(default|[-,\d]+)$/] + end + + munge do |value| + if value.to_s[/default/] + :default + else + PuppetX::Cisco::Utils.normalize_range_string(value) + end + end + end + + # ------------------------ + newproperty(:switchport_pvlan_mapping_trunk, array_matching: :all) do + inputs = %( + Valid inputs are: An array containing both the primary vlan and a range + of secondary vlans: ['44', '3-4,6']; a nested array if there are multiple + mappings: [['44', '3-4,6'], ['99', '199']]; or the keyword 'default') + desc 'Maps the promiscuous trunk port with the primary VLAN and a selected'\ + 'list of associated secondary VLANs. ' + inputs + + validate do |value| + if value.is_a?(Array) + pri, range = value.map { |x| x.to_s.delete(' ') } + fail inputs unless pri[/^\d+$/] && range[/^[-,\d]+$/] + elsif value.is_a?(String) + fail inputs unless value.delete(' ')[/^(default|[-,\d]+)$/] + end + end + + munge do |value| + if value.is_a?(Array) + pri, range = value + [pri.to_s, PuppetX::Cisco::Utils.normalize_range_string(range)] + elsif value.to_s[/default/] + :default + else + PuppetX::Cisco::Utils.normalize_range_string(value) + end + end + + # Override puppet's insync method to check for array equality + def insync?(is) + is.flatten! if should[0].is_a?(String) # non-nested arrays + (is.size == should.size && is.sort == should.sort) + end + end + + # ------------------------ + newproperty(:switchport_pvlan_trunk_allowed_vlan) do + inputs = "Valid values are a String range of vlans: e.g. '3-4,6'; "\ + "or keyword 'default'." + desc 'Sets the allowed VLANs for the private VLAN isolated trunk '\ + 'interface. ' + inputs + + validate do |value| + fail inputs unless value.delete(' ')[/^(default|[-,\d]+)$/] + end + + munge do |value| + if value.to_s[/default/] + :default + else + PuppetX::Cisco::Utils.normalize_range_string(value) + end + end + end + # ------------------------ + newproperty(:switchport_pvlan_trunk_association, array_matching: :all) do + inputs = %( + Valid inputs are: An array containing an association of primary and + secondary vlans: e.g. ['44', '244']; a nested array if there are multiple + associations: [['44', '244'], ['45', '245']]; or the keyword 'default') + desc 'Associates the Layer 2 isolated trunk port with the primary and '\ + 'secondary VLANs of private VLANs. ' + inputs + + validate do |value| + if value.is_a?(Array) + pri, sec = value.map { |x| x.to_s.delete(' ') } + fail inputs unless pri[/^\d+$/] && sec[/^\d+$/] + elsif value.is_a?(String) + fail inputs unless value.delete(' ')[/^(default|[-,\d]+)$/] + end + end + + munge do |value| + if value.is_a?(Array) + pri, sec = value + [pri.to_s, sec.to_s] + elsif value.to_s[/default/] + :default + else + value.to_s + end + end + + # Override puppet's insync method to check for array equality + def insync?(is) + is.flatten! if should[0].is_a?(String) # non-nested arrays + (is.size == should.size && is.sort == should.sort) + end + end + + # ------------------------ + newproperty(:switchport_pvlan_trunk_native_vlan) do + inputs = "Valid values are Integer, String, or keyword 'default'." + desc 'Sets the native VLAN for the 802.1Q trunk. ' + inputs + + validate do |value| + fail inputs unless value.to_s.delete(' ')[/^(default|\d+)$/] + end + + munge { |value| value.to_s[/default/] ? :default : value.to_s.delete(' ') } + end + + # ------------------------ + newproperty(:switchport_pvlan_promiscuous) do + inputs = "Valid values are 'true', 'false', and 'default'." + desc 'Configures a Layer 2 interface as a private VLAN promiscuous '\ + 'port. ' + inputs + newvalues(:true, :false, :default) + end + + # ------------------------ + newproperty(:switchport_pvlan_trunk_promiscuous) do + inputs = "Valid values are 'true', 'false', and 'default'." + desc 'Configures a Layer 2 interface as a private VLAN promiscuous '\ + 'trunk port. ' + inputs + newvalues(:true, :false, :default) + end + + # ------------------------ + newproperty(:switchport_pvlan_trunk_secondary) do + inputs = "Valid values are 'true', 'false', and 'default'." + desc 'Configures a Layer 2 interface as a private VLAN isolated '\ + 'trunk port. ' + inputs + newvalues(:true, :false, :default) + end + + ############################################################################# + # # + # DEPRECATED PROPERTIES Start # + # # + ############################################################################# + + newproperty(:switchport_mode_private_vlan_host) do + desc %(## -DEPRECATED- ## Property. Replace with: 'switchport_pvlan_host' and 'switchport_pvlan_promiscuous') newvalues( :host, :promiscuous, @@ -738,56 +904,40 @@ def is_to_s(value) end # property switchport_mode_private_vlan_host newproperty(:switchport_mode_private_vlan_host_association, array_matching: :all) do - format = '["primary_vlan", "secondary_vlan"]' - desc "An array of #{format} pairs. "\ - "Valid values match format #{format}. "\ - 'primary_vlan and secondary_vlan are integers.' - match_error = "must be of format #{format}. "\ - 'primary_vlan and secondary_vlan must be specified as integers.' - + dep = %(## -DEPRECATED- ## Property. Replace with: 'switchport_pvlan_host_association') + desc dep validate do |value| - fail "Vlan '#{value}' #{match_error}" unless + fail dep unless /^(\d+)$/.match(value.to_s).to_s == value.to_s || value == 'default' || value == :default end - munge do |value| value == 'default' ? :default : value.to_s.gsub(/\s+/, '') end - def insync?(is) (is.size == should.flatten.size && is.sort == should.flatten.sort) end end # property switchport_mode_private_vlan_host_association newproperty(:switchport_mode_private_vlan_host_promisc, array_matching: :all) do - format = '["primary_vlan", "secondary_vlan"]' - desc "An array of #{format} pairs. "\ - "Valid values match format #{format}. "\ - 'primary_vlan and secondary_vlan are integers.' - match_error = "must be of format #{format}. "\ - 'primary_vlan and secondary_vlan must be specified as integers.' - + dep = %(## -DEPRECATED- ## Property. Replace with: 'switchport_pvlan_mapping') + desc dep validate do |value| - fail "Vlan '#{value}' #{match_error}" unless - value.kind_of? String - fail "Vlan '#{value}' #{match_error}" unless + fail dep unless /^(\s*\d+\s*[-,\d\s]*\d+\s*)$/.match(value).to_s == value || value == 'default' || value == :default end - munge do |value| value == 'default' ? :default : value.to_s.gsub(/\s+/, '') end - def insync?(is) (is.size == should.flatten.size && is.sort == should.flatten.sort) end end # switchport_mode_private_vlan_host_promisc newproperty(:switchport_mode_private_vlan_trunk_promiscuous) do - desc 'Switchport private trunk promisc mode for the interface.' - + dep = %(## -DEPRECATED- ## Property. Replace with: 'switchport_pvlan_trunk_promiscuous') + desc dep newvalues( :true, :false, @@ -795,8 +945,8 @@ def insync?(is) end # property switchport_mode_private_vlan_trunk_promiscuous newproperty(:switchport_mode_private_vlan_trunk_secondary) do - desc 'Switchport private trunk secondary mode for the interface.' - + dep = %(## -DEPRECATED- ## Property. Replace with: 'switchport_pvlan_trunk_secondary') + desc dep newvalues( :true, :false, @@ -804,24 +954,16 @@ def insync?(is) end # property switchport_mode_private_vlan_trunk_secondary newproperty(:switchport_private_vlan_association_trunk, array_matching: :all) do - format = '["primary_vlan", "secondary_vlan"]' - desc "An array of #{format} pairs. "\ - "Valid values match format #{format}. "\ - 'primary_vlan and secondary_vlan are integers.' - match_error = "Input must be of #{format}. "\ - 'primary_vlan and secondary_vlan must be specified as integers. '\ - "Ex ['10', '20']" - + dep = %(## -DEPRECATED- ## Property. Replace with: 'switchport_pvlan_trunk_association') + desc dep validate do |value| - fail "Vlan '#{value}' #{match_error}" unless + fail dep unless /^(\s*\d+\s*)$/.match(value).to_s == value || value == 'default' || value == :default end - munge do |value| value == 'default' ? :default : value.to_s.gsub(/\s+/, '') end - def insync?(is) return true if should == [:default] && is == [:default] pair = should.join(' ') @@ -830,26 +972,16 @@ def insync?(is) end # switchport_private_vlan_association_trunk newproperty(:switchport_private_vlan_mapping_trunk, array_matching: :all) do - format = '["primary_vlan", "secondary_vlan"]' - desc "An array of #{format} pairs. "\ - "Valid values match format #{format}. "\ - 'primary_vlan and secondary_vlan are integers.' - - match_error = "Input must be of format #{format}. "\ - 'primary_vlan and secondary_vlan must be specified as integers.'\ - " Ex ['10', '20'], ['10', '20-30']"\ - " or ['10', '20,24']" - + dep = %(## -DEPRECATED- ## Property. Replace with: 'switchport_pvlan_mapping_trunk') + desc dep validate do |value| - fail "Vlan '#{value}' #{match_error}" unless + fail dep unless /^(\s*\d+\s*[-,\d\s]*\d+\s*)$/.match(value).to_s == value || value == 'default' || value == :default end - munge do |value| value == 'default' ? :default : value.to_s.gsub(/\s+/, '') end - def insync?(is) return true if should == [:default] && is == [:default] pair = should.join(' ') @@ -858,25 +990,16 @@ def insync?(is) end # switchport_private_vlan_mapping_trunk newproperty(:switchport_private_vlan_trunk_allowed_vlan, array_matching: :all) do - format = '["vlans"]' - desc "An array of #{format}. "\ - "Valid values match format #{format} with vlans as integers." - match_error = "must be of format #{format}. "\ - 'vlans must be specified as integers.'\ - " Ex ['10'], ['20-30'] or ['20,24']" - + dep = %(## -DEPRECATED- ## Property. Replace with: 'switchport_pvlan_trunk_allowed_vlan') + desc dep validate do |value| - fail "Vlan '#{value}' #{match_error}" unless - value.kind_of? String - fail "Vlan '#{value}' #{match_error}" unless + fail dep unless /^(\s*\d+\s*[-,\d\s]*\d+\s*)$/.match(value).to_s == value || value == 'default' || value == :default end - munge do |value| value == 'default' ? :default : value.to_s.gsub(/\s+/, '') end - def insync?(is) return true if should == [:default] && is == [:default] return false if should == [:default] @@ -886,46 +1009,40 @@ def insync?(is) end # switchport_private_vlan_trunk_allowed_vlan newproperty(:switchport_private_vlan_trunk_native_vlan) do - format = '' - desc 'The private native vlan. '\ - "Valid values match format #{format} with vlan as integer" - match_error = "must be of format #{format}. "\ - 'vlan must be specified as integer. Ex 10 or 20' - + dep = %(## -DEPRECATED- ## Property. Replace with: 'switchport_pvlan_trunk_allowed_vlan') + desc dep validate do |value| - fail "Vlan '#{value}' #{match_error}" unless + fail dep unless /^(\d+)$/.match(value.to_s).to_s == value.to_s || value == 'default' || value == :default end - munge do |value| value == 'default' ? :default : Integer(value) end end # switchport_private_vlan_trunk_native_vlan newproperty(:private_vlan_mapping, array_matching: :all) do - format = '["vlans"]' - desc "An array of #{format}. "\ - "Valid values match format #{format} with vlans as integer" - match_error = "must be of format #{format}. "\ - 'vlans must be specified as integers. '\ - "Ex ['10'], ['20-30'] or ['20,24']" - + dep = %(## -DEPRECATED- ## Property. Replace with: 'pvlan_mapping') + desc dep validate do |value| - fail "Vlan '#{value}' #{match_error}" unless + fail dep unless /^(\s*\d+\s*[-,\d\s]*\d+\s*)$/.match(value).to_s == value || value == 'default' || value == :default end - munge do |value| value == 'default' ? :default : value.gsub(/\s+/, '') end - def insync?(is) (is.size == should.flatten.size && is.sort == should.flatten.sort) end end # private_vlan_mapping + ############################################################################# + # # + # DEPRECATED PROPERTIES End # + # # + ############################################################################# + ################ # Autorequires # ################ @@ -938,9 +1055,11 @@ def insync?(is) reqs end # autorequire vlan - autorequire(:cisco_vtp) do |rel_catalog| - reqs = [] - reqs << rel_catalog.catalog.resource('Cisco_vtp') - reqs # return - end # autorequire vtp + # TBD: Remove? + # autorequire(:cisco_vtp) do |rel_catalog| + # reqs = [] + # reqs << rel_catalog.catalog.resource('Cisco_vtp') + # reqs # return + # rel_catalog.catalog.resource('Cisco_vtp', 'default') + # end # autorequire vtp end # Puppet::Type.newtype diff --git a/lib/puppet/type/cisco_vlan.rb b/lib/puppet/type/cisco_vlan.rb index 4a3a43bf8..ebf418db2 100644 --- a/lib/puppet/type/cisco_vlan.rb +++ b/lib/puppet/type/cisco_vlan.rb @@ -40,8 +40,8 @@ mapped_vni => 20000, state => 'active', shutdown => 'true', - private_vlan_type => 'primary', - private_vlan_association => ['101-104'] + pvlan_type => 'primary', + pvlan_association => ['101-104'] fabric_control => 'true', }" @@ -142,7 +142,7 @@ def self.title_patterns :default) end # property shutdown - newproperty(:private_vlan_type) do + newproperty(:pvlan_type) do desc 'The private vlan type for VLAN. Valid values are string primary, isolated, community' @@ -169,17 +169,19 @@ def self.title_patterns end # rescue value end - end # property private_vlan_type + end # property pvlan_type - newproperty(:private_vlan_association, array_matching: :all) do - desc 'The private association for the primary vlan.'\ - "Valid values match format ['vlans']." + newproperty(:pvlan_association, array_matching: :all) do + valid = %( + Valid values are an Array or String of vlan ranges, or keyword 'default'. + Examples: ['2-5, 9'] or '2-5, 9' + ) + desc "The private-vlan association for the primary vlan. #{valid}" - match_error = "must be of format ['vlans'] with vlans as integer" validate do |value| - fail "Vlan '#{value}' #{match_error}" unless - /^(\s*\d+\s*[-,\d\s]*\d+\s*)$/.match(value).to_s == value || - value == 'default' || value == :default + fail "Invalid input: #{value}\n#{valid}" unless + value == 'default' || + /^(\s*\d+\s*[-,\d\s]*\d+\s*)$/.match(value).to_s == value end munge do |value| @@ -187,15 +189,11 @@ def self.title_patterns end def insync?(is) - return true if should == [:default] && is == [:default] - return false if should == [:default] - # For pvlan association we need to massage the should value - # since the returned is value is a flat array of vlans. - result = PuppetX::Cisco::PvlanUtils.prepare_list(should[0]) - - (is.size == result.size && is.sort == result.sort) + # Summarize the manifest ranges before comparing. + normal = PuppetX::Cisco::Utils.normalize_range_array(should) + (is.size == normal.size && is.sort == normal.sort) end - end # property private_vlan_association + end # property pvlan_association newproperty(:fabric_control) do desc %(Specifies this VLAN as the fabric control VLAN. Only one bridge-domain or VLAN can be configured as fabric-control. @@ -206,4 +204,61 @@ def insync?(is) :false, :default) end # property fabric_control + + ############################################################################# + # # + # DEPRECATED PROPERTIES Start # + # # + ############################################################################# + + newproperty(:private_vlan_type) do + dep = %(## -DEPRECATED- ## Property. Replace with: 'pvlan_type') + desc dep + valid_input = %w(primary isolated community) + + validate do |value| + fail dep unless value.kind_of? String + + unless valid_input.include?(value) || + value == 'default' || value == :default + fail dep + end + end + + munge do |value| + begin + value = :default if value == 'default' + value = String(value) unless value == :default + rescue + raise 'Type is not a valid string.' + end # rescue + value + end + end # property private_vlan_type + + newproperty(:private_vlan_association, array_matching: :all) do + dep = %(## -DEPRECATED- ## Property. Replace with: 'pvlan_association') + desc dep + validate do |value| + fail dep unless + value == 'default' || + /^(\s*\d+\s*[-,\d\s]*\d+\s*)$/.match(value).to_s == value + end + + munge do |value| + value == 'default' ? :default : value.to_s.gsub(/\s+/, '') + end + + def insync?(is) + # Summarize the manifest ranges before comparing. + normal = PuppetX::Cisco::Utils.normalize_range_array(should) + (is.size == normal.size && is.sort == normal.sort) + end + end # property private_vlan_association + + ############################################################################# + # # + # DEPRECATED PROPERTIES End # + # # + ############################################################################# end # Puppet::Type.newtype diff --git a/lib/puppet_x/cisco/autogen.rb b/lib/puppet_x/cisco/autogen.rb index ad801b60f..c57b47a40 100644 --- a/lib/puppet_x/cisco/autogen.rb +++ b/lib/puppet_x/cisco/autogen.rb @@ -19,6 +19,7 @@ module PuppetX module Cisco # PuppetX::Cisco::AutoGen - automatically generate getter/setter methods + # rubocop:disable Metrics/ClassLength class AutoGen # "nu_name" refers to node_utils def self.mk_puppet_methods(mtype, klass, nu_name, props) @@ -26,6 +27,10 @@ def self.mk_puppet_methods(mtype, klass, nu_name, props) when :array_flat mk_puppet_getters_array_flat(klass, nu_name, props) mk_puppet_setters_array_flat(klass, nu_name, props) + when :array_nested + # The *nested* array getters are identical to flat array getters + mk_puppet_getters_array_flat(klass, nu_name, props) + mk_puppet_setters_array_nested(klass, nu_name, props) when :non_bool mk_puppet_getters_non_bool(klass, nu_name, props) mk_puppet_setters_non_bool(klass, nu_name, props) @@ -88,6 +93,32 @@ def self.mk_puppet_setters_array_flat(klass, nu_name, props) end end # mk_puppet_setters_array_flat + # Note: There is no mk_puppet_setters_array_nested() method + + # Auto-generator for puppet nested array-based SETTER methods + # These properties expect a nested array but optionally support a string + # of space-separated values in the manifest. + def self.mk_puppet_setters_array_nested(klass, nu_name, props) + props.each do |prop| + klass.instance_eval do + # def foo_array_nested=(val) + # fail '@property_flush not defined' if + # @property_flush.nil? + # val = @nu.default_foo_array_nested if + # val[0] == :default (index 0) + # @property_flush[:foo_array_nested] = val (no flatten) + # end + define_method("#{prop}=") do |val| + fail '@property_flush not defined' if + instance_variable_get(:@property_flush).nil? + val = instance_variable_get(nu_name).send("default_#{prop}") if + val[0] == :default + @property_flush[prop] = val + end + end + end + end + # Auto-generator for puppet non-boolean-based GETTER methods def self.mk_puppet_getters_non_bool(klass, nu_name, props) props.each do |prop| diff --git a/lib/puppet_x/cisco/cmnutils.rb b/lib/puppet_x/cisco/cmnutils.rb index bb3d38afa..03a45c5a8 100644 --- a/lib/puppet_x/cisco/cmnutils.rb +++ b/lib/puppet_x/cisco/cmnutils.rb @@ -19,6 +19,7 @@ module PuppetX module Cisco # PuppetX::Cisco::Utils: - Common helper methods shared by any Type/Provider + # rubocop:disable Metrics/ClassLength class Utils require 'ipaddr' # Helper utility method for ip/prefix format networks. @@ -45,6 +46,119 @@ def self.flush_boolean?(prop) prop.is_a?(TrueClass) || prop.is_a?(FalseClass) end + # normalize_range_array + # + # Given a list of ranges, merge any overlapping ranges and normalize the + # them as a string that can be used directly on the switch. + # + # Note: The ranges are converted to ruby ranges for easy merging, + # then converted back to a cli-syntax ranges. + # + # Accepts an array or string: + # ["2-5", "9", "4-6"] -or- '2-5, 9, 4-6' -or- ["2-5, 9, 4-6"] + # Returns a merged and ordered range: + # ["2-6", "9"] + # + def self.normalize_range_array(range, type=:array) + return range if range.nil? || range.empty? + + # This step is puppet only + return range if range[0] == :default + + # Handle string within an array: ["2-5, 9, 4-6"] to '2-5, 9, 4-6' + range = range.shift if range.is_a?(Array) && range.length == 1 + + # Handle string only: '2-5, 9, 4-6' to ["2-5", "9", "4-6"] + range = range.split(',') if range.is_a?(String) + + # Convert to ruby-syntax ranges + range = dash_range_to_ruby_range(range) + + # Sort & Merge + merged = merge_range(range) + + # Convert back to cli dash-syntax + ruby_range_to_dash_range(merged, type) + end + + def self.normalize_range_string(range) + range = range.to_s + return normalize_range_array(range, :string) if range[/[-,]/] + range + end + + # Convert a cli-dash-syntax range to ruby-range. This is useful for + # preparing inputs to merge_range(). + # + # Inputs an array or string of dash-syntax ranges -> returns an array + # of ruby ranges. + # + # Accepts an array or string: ["2-5", "9", "4-6"] or '2-5, 9, 4-6' + # Returns an array of ranges: [2..5, 9..9, 4..6] + # + def self.dash_range_to_ruby_range(range) + range = range.split(',') if range.is_a?(String) + # [["45", "7-8"], ["46", "9,10"]] + range.map! do |rng| + if rng[/-/] + # '2-5' -> 2..5 + rng.split('-').inject { |a, e| a.to_i..e.to_i } + else + # '9' -> 9..9 + rng.to_i..rng.to_i + end + end + range + end + + # Convert a ruby-range to cli-dash-syntax. + # + # Inputs an array of ruby ranges -> returns an array or string of + # dash-syntax ranges. + # + # when (:array) [2..6, 9..9] -> ['2-6', '9'] + # + # when (:string) [2..6, 9..9] -> '2-6, 9' + # + def self.ruby_range_to_dash_range(range, type=:array) + range.map! do |r| + if r.first == r.last + # 9..9 -> '9' + r.first.to_s + else + # 2..6 -> '2-6' + r.first.to_s + '-' + r.last.to_s + end + end + return range.join(',') if type == :string + range + end + + # Merge overlapping ranges. + # + # Inputs an array of ruby ranges: [2..5, 9..9, 4..6] + # Returns an array of merged ruby ranges: [2..6, 9..9] + # + def self.merge_range(range) + # sort to lowest range 'first' values: + # [2..5, 9..9, 4..6] -> [2..5, 4..6, 9..9] + range = range.sort_by(&:first) + + *merged = range.shift + range.each do |r| + lastr = merged[-1] + if lastr.last >= r.first - 1 + merged[-1] = lastr.first..[r.last, lastr.last].max + else + merged.push(r) + end + end + merged + end # merge_range + + # TBD: Investigate replacing fail_array_overlap() and range_summarize() + # with above methods. + # Helper utility for checking if arrays are overlapping in a # give list. # For ex: if the list has '2-10,32,42,44-89' and '11-33' @@ -124,38 +238,7 @@ def self.range_summarize(range_str, sort=true) ranges.join(',').gsub('..', '-') end end # class Utils - - # PuppetX::Cisco::PvlanUtils - Common BGP methods used by BGP Types/Providers - class PvlanUtils - # This api is used by private vlan to prepare the massage the input. - # The input can be in the following formats for vlans: - # 10-12,14. Prepare_list api is transforming this input into a flat array. - # In the example above the returned array will be 10, 11, 12, 13. Prepare - # list is first splitting the input on ',' and the than expanding the vlan - # range element like 10-12 into a flat array. The final result will - # be a flat array. - - def self.prepare_list(input) - return [] if input.nil? || input.empty? - result = [] - input.gsub!('-', '..') - input.gsub!(/\s+/, '') - new_list = input.split(',') - new_list.each do |member| - if member.include?('..') - elema = member.split('..').map { |d| Integer(d) } - elema.sort! - tr = elema[0]..elema[1] - tr.to_a.each do |item| - result.push(item.to_s) - end - else - result.push(member) - end - end - result - end - end + # rubocop:enable Metrics/ClassLength # PuppetX::Cisco::BgpUtil - Common BGP methods used by BGP Types/Providers class BgpUtils diff --git a/metadata.json b/metadata.json index bf97c827d..33880bae7 100644 --- a/metadata.json +++ b/metadata.json @@ -1,6 +1,6 @@ { "name": "puppetlabs-ciscopuppet", - "version": "1.3.1", + "version": "1.3.2", "author": "cisco", "summary": "Cisco Puppet providers and types for NX-OS devices", "license": "Apache-2.0", diff --git a/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb b/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb index cd32429eb..ef40938b4 100644 --- a/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb +++ b/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb @@ -185,6 +185,14 @@ resource: { 'ensure' => 'present' }, } +tests[:l2vpn_evpn] = { + desc: '3.1 Address-family l2evpn/evpn', + operating_system: 'nexus', + platform: 'n(5|6|7|8|9)k', + title_pattern: '2 default l2vpn evpn', + resource: { 'ensure' => 'present' }, +} + # Overridden to properly handle dependencies for this test file. def dependency_manifest(tests, id) extra_config = '' @@ -299,6 +307,7 @@ def test_harness_bgp_af_run(tests, id) test_harness_run(tests, :title_patterns_1) test_harness_run(tests, :title_patterns_2) test_harness_run(tests, :title_patterns_3) + test_harness_run(tests, :l2vpn_evpn) unless nexus_i2_image # ----------------------------------- resource_absent_cleanup(agent, 'cisco_bgp') diff --git a/tests/beaker_tests/cisco_interface/interfacelib.rb b/tests/beaker_tests/cisco_interface/interfacelib.rb new file mode 100755 index 000000000..01c9d8f4f --- /dev/null +++ b/tests/beaker_tests/cisco_interface/interfacelib.rb @@ -0,0 +1,68 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# Interface test library +############################################################################### +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# This method overrides the method in utilitylib.rb to set up dependencies +# for interface tests. +def test_harness_dependencies(tests, id) + logger.info(' * Process test_harness_dependencies (interfacelib)') + + # System-level switchport dependencies + if operating_system == 'nexus' + config_system_default_switchport?(tests, id) + config_system_default_switchport_shutdown?(tests, id) + end + + # Misc dependencies + config_acl?(tests, id) + config_anycast_gateway_mac?(tests, id) + config_bridge_domain?(tests, id) + + # Various Cleanups + return unless tests[id][:preclean_intf] + intf = tests[id][:title_pattern] + if intf[/ethernet/i] + interface_cleanup(agent, intf) + else + remove_interface(agent, intf) + end +end + +# A global 'anycast gateway mac' is required for some SVI properties +# TBD: This may not be required for all platforms +def config_anycast_gateway_mac?(tests, _id) + return unless tests.key?(:anycast_gateway_mac) + agent = tests[:agent] + + # TBD: Consider creating a resource_get to avoid vsh calls + on(agent, get_vshell_cmd('show runn fabric forwarding'), pty: true) + + unless stdout[/anycast-gateway-mac/] + mac = { + name: 'cisco_overlay_global', + title: 'default', + property: 'anycast_gateway_mac', + value: '1.1.1', + } + resource_set(agent, mac, + "fabric forwarding anycast-gateway-mac #{mac[:value]}") + end + + # Delete the key to prevent having to set this for every test case + tests.delete(:anycast_gateway_mac) +end diff --git a/tests/beaker_tests/cisco_interface/test_interface.rb b/tests/beaker_tests/cisco_interface/test_interface.rb deleted file mode 100755 index f0c76616d..000000000 --- a/tests/beaker_tests/cisco_interface/test_interface.rb +++ /dev/null @@ -1,963 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2016 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# test_interface.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet cisco_interface testcase for Puppet Agent on -# Nexus and IOS XR devices. -# The test case assumes the following prerequisites are already satisfied: -# - Host configuration file contains agent and master information. -# - SSH is enabled on the agent node. -# - Puppet master/server is started. -# - Puppet agent certificate has been signed on the Puppet master/server. -# -# The following exit_codes are validated for Puppet and Bash shell commands. -# -# Bash Shell Commands: -# 0 - successful command execution -# > 0 - failed command execution. -# -# Puppet Commands: -# 0 - no changes have occurred -# 1 - errors have occurred, -# 2 - changes have occurred -# 4 - failures have occurred and -# 6 - changes and failures have occurred. -# -# NOTE: 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# -# The test cases use RegExp pattern matching on stdout or output IO -# instance attributes to verify resource properties. -# -############################################################################### - -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -# ----------------------------- -# Common settings and variables -# ----------------------------- -testheader = 'Resource cisco_interface' - -# The 'tests' hash is used to define all of the test data values and expected -# results. It is also used to pass optional flags to the test methods when -# necessary. - -# 'tests' hash -# Top-level keys set by caller: -# tests[:master] - the master object -# tests[:agent] - the agent object -# tests[:operating_system] - a regexp pattern to match against supported OS. -# This key can be overridden by a -# tests[id][:operating_system] key -# tests[:platform] - a regexp pattern to match against supported platforms. -# This key can be overridden by a tests[id][:platform] key -# -tests = { - master: master, - agent: agent, - svi_name: 'vlan13', - bdi_name: 'bdi100', - resource_name: 'cisco_interface', -} - -tests_2 = { - master: master, - agent: agent, - resource_name: 'cisco_vlan', - operating_system: 'nexus', - platform: 'n(3|5|6|7|9)k', -} - -# tests[id] keys set by caller and used by test_harness_common: -# -# tests[id] keys set by caller: -# tests[id][:operating_system] - a regexp pattern to match against supported OS. -# This key overrides a tests[:operating_system] key -# tests[id][:platform] - a regexp pattern to match against supported platforms. -# This key overrides a tests[:platform] key -# tests[id][:desc] - a string to use with logs & debugs -# tests[id][:manifest] - the complete manifest, as used by test_harness_common -# tests[id][:resource] - a hash of expected states, used by test_resource -# tests[id][:resource_cmd] - 'puppet resource' command to use with test_resource -# tests[id][:ensure] - (Optional) set to :present or :absent before calling -# tests[id][:code] - (Optional) override the default exit code in some tests. -# -# These keys are local use only and not used by test_harness_common: -# -# tests[id][:manifest_props] - This is essentially a master list of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the list -# tests[id][:resource_props] - This is essentially a master hash of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the hash -# tests[id][:sys_def_switchport] - (Optional) Specifies state of 'system default switchport' -# tests[id][:sys_def_sw_shut] - (Optional) Specifies state of 'system default switchport shutdown'. -# This is only meaningful for L2 interfaces. -# tests[id][:title_pattern] - (Optional) defines the manifest title. -# -tests['L3_default'] = { - desc: '1.1 (L3) Default Properties', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: false, - manifest_props: { - description: 'default', - ipv4_proxy_arp: 'default', - ipv4_redirects: 'default', - mtu: 'default', - shutdown: 'default', - vrf: 'default', - }, - resource: { - # 'description' => nil, - 'ipv4_proxy_arp' => 'false', - 'ipv4_redirects' => operating_system == 'nexus' ? 'true' : 'false', - 'mtu' => operating_system == 'nexus' ? '1500' : '1514', - 'shutdown' => 'false', - # 'vrf' => nil, - }, -} - -tests['L3_default_nexus'] = { - desc: '1.2 (L3) Default Properties - Nexus Specific', - operating_system: 'nexus', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: false, - manifest_props: { - duplex: 'default', - ipv4_forwarding: 'default', - ipv4_pim_sparse_mode: 'default', - }, - resource: { - 'duplex' => 'auto', - 'ipv4_forwarding' => 'false', - 'ipv4_pim_sparse_mode' => 'false', - }, -} - -# Note: This test should follow the L3_default test as it requires an -# L3 parent interface and this makes it easy to set up. -tests['L3_sub_int'] = { - desc: '1.3 (L3) Sub-interface', - intf_type: 'dot1q', - manifest_props: { - encapsulation_dot1q: 30 - }, - resource: { - 'encapsulation_dot1q' => '30' - }, -} - -tests['L3_misc'] = { - desc: '1.4 (L3) Misc Properties', - intf_type: 'ethernet', - sys_def_switchport: false, - manifest_props: { - description: 'Configured with Puppet', - shutdown: true, - ipv4_address: '1.1.1.1', - ipv4_netmask_length: 31, - ipv4_address_secondary: '2.2.2.2', - ipv4_netmask_length_secondary: 31, - ipv4_proxy_arp: true, - ipv4_redirects: operating_system == 'nexus' ? false : true, - vrf: 'test1', - }, - resource: { - 'description' => 'Configured with Puppet', - 'shutdown' => 'true', - 'ipv4_address' => '1.1.1.1', - 'ipv4_netmask_length' => '31', - 'ipv4_address_secondary' => '2.2.2.2', - 'ipv4_netmask_length_secondary' => '31', - 'ipv4_proxy_arp' => 'true', - 'ipv4_redirects' => operating_system == 'nexus' ? 'false' : 'true', - 'vrf' => 'test1', - }, -} - -tests['L3_misc_nexus'] = { - desc: '1.5 (L3) Misc Properties - Nexus specific', - operating_system: 'nexus', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: false, - manifest_props: { - switchport_mode: 'disabled', - ipv4_forwarding: true, - ipv4_pim_sparse_mode: true, - }, - resource: { - 'switchport_mode' => 'disabled', - 'ipv4_forwarding' => 'true', - 'ipv4_pim_sparse_mode' => 'true', - }, -} - -tests['L3_ACL'] = { - desc: '1.6 (L3) ACL Properties', - # TODO: this requires cisco_acl support before we can enable it for IOS XR - operating_system: 'nexus', - intf_type: 'ethernet', - sys_def_switchport: false, - acl: { - 'v4_in' => 'ipv4', - 'v4_out' => 'ipv4', - 'v6_in' => 'ipv6', - 'v6_out' => 'ipv6', - }, - manifest_props: { - switchport_mode: 'disabled', - ipv4_acl_in: 'v4_in', - ipv4_acl_out: 'v4_out', - ipv6_acl_in: 'v6_in', - ipv6_acl_out: 'v6_out', - }, - resource: { - 'ipv4_acl_in' => 'v4_in', - 'ipv4_acl_out' => 'v4_out', - 'ipv6_acl_in' => 'v6_in', - 'ipv6_acl_out' => 'v6_out', - }, -} - -tests['L2_access_default'] = { - desc: '2.1 (L2) Access Default', - operating_system: 'nexus', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: true, - sys_def_sw_shut: true, - manifest_props: { - shutdown: 'default', - switchport_mode: 'access', - }, - resource: { - 'shutdown' => 'true', - 'switchport_mode' => 'access', - }, -} - -tests['L2_access'] = { - desc: '2.2 (L2) Access Properties', - operating_system: 'nexus', - intf_type: 'ethernet', - sys_def_switchport: true, - sys_def_sw_shut: true, - manifest_props: { - access_vlan: '128', - shutdown: 'false', - switchport_mode: 'access', - - }, - resource: { - 'access_vlan' => '128', - 'shutdown' => 'false', - 'switchport_mode' => 'access', - }, -} - -tests['L2_trunk_default'] = { - desc: '3.1 (L2) Trunk Default', - operating_system: 'nexus', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: true, - sys_def_sw_shut: true, - manifest_props: { - shutdown: 'default', - switchport_mode: 'trunk', - switchport_trunk_allowed_vlan: 'default', - switchport_trunk_native_vlan: 'default', - - }, - resource: { - 'shutdown' => 'true', - 'switchport_mode' => 'trunk', - 'switchport_trunk_allowed_vlan' => '1-4094', - 'switchport_trunk_native_vlan' => '1', - }, -} - -tests['L2_trunk'] = { - desc: '3.2 (L2) Trunk', - operating_system: 'nexus', - intf_type: 'ethernet', - sys_def_switchport: true, - manifest_props: { - shutdown: 'false', - switchport_mode: 'trunk', - switchport_trunk_allowed_vlan: '30, 40, 31-33, 100', - switchport_trunk_native_vlan: '20', - switchport_vtp: 'false', - - }, - resource: { - 'shutdown' => 'false', - 'switchport_mode' => 'trunk', - 'switchport_trunk_allowed_vlan' => '30-33,40,100', - 'switchport_trunk_native_vlan' => '20', - }, -} - -tests['SVI_default'] = { - desc: '4.1 (SVI) Default Properties', - operating_system: 'nexus', - intf_type: 'vlan', - manifest_props: { - svi_management: 'default' - }, - resource: { - 'svi_management' => 'false' - }, -} - -tests['SVI'] = { - desc: '4.2 (SVI) Non Default Properties', - operating_system: 'nexus', - intf_type: 'vlan', - manifest_props: { - svi_management: 'true' - }, - resource: { - 'svi_management' => 'true' - }, -} - -# Fabric Forwarding Anycast Gateway -if platform[/n9k/] - tests['SVI_default'][:manifest_props][:fabric_forwarding_anycast_gateway] = 'default' - tests['SVI'][:manifest_props][:fabric_forwarding_anycast_gateway] = 'true' - tests['SVI'][:resource][:fabric_forwarding_anycast_gateway] = 'true' -end - -tests['SVI_autostate_default'] = { - desc: '4.3 (SVI) Default SVI Autostate Property', - operating_system: 'nexus', - platform: 'n(3|7|9)k', - intf_type: 'vlan', - manifest_props: { - svi_autostate: 'default' - }, - resource: { - 'svi_autostate' => 'true' - }, -} - -tests['SVI_autostate'] = { - desc: '4.4 (SVI) Non Default SVI Autostate Property', - operating_system: 'nexus', - platform: 'n(3|7|9)k', - intf_type: 'vlan', - manifest_props: { - svi_autostate: 'false' - }, - resource: { - 'svi_autostate' => 'false' - }, -} - -tests[:auto] = { - desc: '5.1 Misc. Auto Value Properties', - operating_system: 'nexus', - intf_type: 'ethernet', - sys_def_switchport: false, - code: [0, 2], - manifest_props: { - switchport_mode: 'disabled', - # duplex and speed are defined by interface_pre_check - }, -} - -tests[:non_default] = { - desc: '5.2 Misc. Non-default Value Properties', - operating_system: 'nexus', - intf_type: 'ethernet', - sys_def_switchport: false, - manifest_props: { - switchport_mode: 'disabled', - # duplex, speed, and mtu are defined by interface_pre_check - }, -} - -tests_2[:primary] = { - desc: '6.1 configure pvlan primary type', - title_pattern: '100', - manifest_props: { - private_vlan_type: 'primary' - }, -} - -tests_2[:community] = { - desc: '6.2 configure pvlan primary type', - title_pattern: '101', - manifest_props: { - private_vlan_type: 'community' - }, -} -tests_2[:isolated] = { - desc: '6.3 configure pvlan isolated type', - title_pattern: '102', - manifest_props: { - private_vlan_type: 'isolated' - }, -} -tests_2[:community_2] = { - desc: '6.4 configure pvlan community type', - title_pattern: '103', - manifest_props: { - private_vlan_type: 'community' - }, -} - -tests_2[:community_3] = { - desc: '6.5 configure pvlan isolated type', - title_pattern: '104', - manifest_props: { - private_vlan_type: 'community' - }, -} -tests_2[:community_4] = { - desc: '6.6 configure pvlan community type', - title_pattern: '105', - manifest_props: { - private_vlan_type: 'community' - }, -} -vlan_assoc = %w(101 102 103 104 105) -tests_2[:association] = { - desc: '6.7 configured private vlan association', - title_pattern: '100', - manifest_props: { - private_vlan_association: ['101-105'] - }, - resource: { - 'private_vlan_association' => "#{vlan_assoc}" - }, -} - -switchport_modes = [ - :host, - :promiscuous, -] - -tests['pvlan_host_port'] = { - desc: '6.9 Pvlan host port config', - operating_system: 'nexus', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: true, - manifest_props: { - switchport_mode_private_vlan_host: switchport_modes[0], - switchport_mode_private_vlan_host_association: %w(100 102), - }, - resource: { - 'switchport_mode_private_vlan_host' => "#{switchport_modes[0]}", - 'switchport_mode_private_vlan_host_association' => "['100', '102']", - }, -} - -tests['pvlan_promisc_port'] = { - desc: '6.10 Pvlan promisc port config', - operating_system: 'nexus', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: true, - manifest_props: { - switchport_mode_private_vlan_host: switchport_modes[1], - switchport_mode_private_vlan_host_promisc: ['100', '101-103'], - }, - resource: { - 'switchport_mode_private_vlan_host' => "#{switchport_modes[1]}", - 'switchport_mode_private_vlan_host_promisc' => "['100', '101-103']", - }, -} -tests['pvlan_trunk_promisc_port'] = { - desc: '6.11 Pvlan trunk promisc port config', - operating_system: 'nexus', - platform: 'n(5|6|7|9)k', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: true, - manifest_props: { - switchport_mode_private_vlan_trunk_promiscuous: true, - switchport_private_vlan_mapping_trunk: ['100', '101,104-105'], - }, - resource: { - 'switchport_mode_private_vlan_trunk_promiscuous' => 'true', - 'switchport_private_vlan_mapping_trunk' => "['100 101,104-105']", - }, -} - -tests['pvlan_trunk_sec_port'] = { - desc: '6.12 Pvlan trunk sec port config', - operating_system: 'nexus', - platform: 'n(5|6|7|9)k', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: true, - manifest_props: { - switchport_mode_private_vlan_trunk_secondary: true, - switchport_private_vlan_association_trunk: %w(100 102), - }, - resource: { - 'switchport_mode_private_vlan_trunk_secondary' => 'true', - 'switchport_private_vlan_association_trunk' => "['100 102']", - }, -} - -vlan_assoc = %w(100,102-103,105) -tests['pvlan_trunk_allow_vlan'] = { - desc: '6.13 Pvlan trunk allow vlans config', - operating_system: 'nexus', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: true, - manifest_props: { - switchport_private_vlan_trunk_allowed_vlan: vlan_assoc - }, - resource: { - 'switchport_private_vlan_trunk_allowed_vlan' => "['100', '102-103', '105']" - }, -} - -tests['pvlan_trunk_native_vlan'] = { - desc: '6.14 Pvlan trunk native vlan config', - operating_system: 'nexus', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: true, - manifest_props: { - switchport_private_vlan_trunk_native_vlan: 100 - }, - resource: { - 'switchport_private_vlan_trunk_native_vlan' => '100' - }, -} - -vlan_assoc = %w(102-103) -tests['pvlan_mapping_svi'] = { - desc: '6.15 Pvlan vlan mapping for svi', - operating_system: 'nexus', - intf_type: 'vlan', - manifest_props: { - private_vlan_mapping: vlan_assoc - }, - resource: { - 'private_vlan_mapping' => "['102-103']" - }, -} - -tests['pvlan_host_port_association_default'] = { - desc: '6.16 Pvlan host port association default config', - operating_system: 'nexus', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: true, - manifest_props: { - switchport_mode_private_vlan_host_association: 'default' - }, - resource: { - }, -} - -tests['pvlan_promisc_port_association_default'] = { - desc: '6.17 Pvlan promisc port association default config', - operating_system: 'nexus', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: true, - manifest_props: { - switchport_mode_private_vlan_host_promisc: 'default' - }, - resource: { - }, -} - -tests['pvlan_trunk_promisc_port_default'] = { - desc: '6.18 Pvlan trunk promisc port default config', - operating_system: 'nexus', - platform: 'n(5|6|7|9)k', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: true, - manifest_props: { - switchport_mode_private_vlan_trunk_promiscuous: 'default' - }, - resource: { - }, -} - -tests['pvlan_trunk_sec_port_default'] = { - desc: '6.19 Pvlan trunk sec port default config', - operating_system: 'nexus', - platform: 'n(5|6|7|9)k', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: true, - manifest_props: { - switchport_mode_private_vlan_trunk_secondary: 'default' - }, - resource: { - }, -} - -tests['pvlan_trunk_allow_vlan_default'] = { - desc: '6.20 Pvlan trunk vlan allow port default config', - operating_system: 'nexus', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: true, - manifest_props: { - switchport_private_vlan_trunk_allowed_vlan: 'default' - }, - resource: { - }, -} - -tests['pvlan_host_port_default'] = { - desc: '6.21 Pvlan host port default config', - operating_system: 'nexus', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: true, - manifest_props: { - switchport_mode_private_vlan_host: 'disabled' - }, - resource: { - }, -} - -tests['pvlan_promisc_port_default'] = { - desc: '6.22 Pvlan promisc port default config', - operating_system: 'nexus', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: true, - manifest_props: { - switchport_mode_private_vlan_host: 'disabled' - }, - resource: { - }, -} - -tests['switchport_private_vlan_trunk_native_vlan_default'] = { - desc: '6.23 Pvlan trunk native vlan default config', - operating_system: 'nexus', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: true, - manifest_props: { - switchport_private_vlan_trunk_native_vlan: 'default' - }, - resource: { - }, -} - -tests['switchport_private_vlan_mapping_trunk_default'] = { - desc: '6.24 Pvlan trunk promisc association default config', - operating_system: 'nexus', - platform: 'n(5|6|7|9)k', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: true, - manifest_props: { - switchport_private_vlan_mapping_trunk: 'default' - }, - resource: { - }, -} -tests['switchport_private_vlan_association_trunk_default'] = { - desc: '6.25 Pvlan trunk secondary association default config', - operating_system: 'nexus', - platform: 'n(5|6|7|9)k', - intf_type: 'ethernet', - preclean: true, - sys_def_switchport: true, - manifest_props: { - switchport_private_vlan_association_trunk: 'default' - }, - resource: { - }, -} - -tests['private_vlan_mapping_svi_default'] = { - desc: '6.26 Pvlan svi association default config', - operating_system: 'nexus', - intf_type: 'vlan', - manifest_props: { - private_vlan_mapping: 'default' - }, - resource: { - }, -} - -tests['BDI_non_default'] = { - desc: '7.1 (BDI) Non Default BDI Properties', - operating_system: 'nexus', - platform: 'n(7)k', - intf_type: 'bdi', - manifest_props: { - ipv4_address: '10.10.10.1', - ipv4_netmask_length: '24', - shutdown: 'false', - vrf: 'test1', - }, - resource: { - 'ipv4_address' => '10.10.10.1', - 'ipv4_netmask_length' => '24', - 'shutdown' => 'false', - 'vrf' => 'test1', - }, -} - -resource_cisco_overlay_global = { - name: 'cisco_overlay_global', - title: 'default', - property: 'anycast_gateway_mac', - value: '1.1.1', -} - -# Create actual manifest for a given test scenario. -def build_manifest_interface(tests, id) - manifest = prop_hash_to_manifest(tests[id][:manifest_props]) - if tests[id][:ensure] == :absent - state = 'ensure => absent,' - tests[id][:resource] = { 'ensure' => 'absent' } - else - state = 'ensure => present,' - end - create_interface_title(tests, id) - - tests[id][:manifest] = "cat <#{PUPPETMASTER_MANIFESTPATH} - \nnode default { - cisco_interface { '#{tests[id][:title_pattern]}': - #{state}\n#{manifest} - }\n}\nEOF" - - cmd = PUPPET_BINPATH + - "resource cisco_interface '#{tests[id][:title_pattern]}'" - tests[id][:resource_cmd] = cmd -end - -def interface_pre_check(tests) # rubocop:disable Metrics/AbcSize - # Discover a usable test interface - intf = find_interface(tests, :auto) - tests[:auto][:title_pattern] = intf - tests[:non_default][:title_pattern] = intf - - # Clean the test interface - system_default_switchport(agent, false) - interface_cleanup(agent, intf, 'Initial Cleanup') - - # Get the capabilities and update the caps list with any add'l test values - caps = interface_capabilities(agent, intf) - - if caps.empty? - tests[:skipped] ||= [] - tests[:skipped] << tests[:auto][:desc] - tests[:skipped] << tests[:non_default][:desc] - return false - end - - caps['Speed'] += ',auto' unless caps['Speed']['auto'] - caps['Duplex'] += ',auto' unless caps['Duplex']['auto'] - caps['MTU'] = '1600' - - # Create a probe hash to pre-test the properties - probe = { - cmd: PUPPET_BINPATH + 'resource cisco_interface ', - intf: intf, - caps: caps, - probe_props: %w(Speed Duplex MTU), - } - caps = interface_probe(tests, probe)[:caps] - - # Fixup the test manifests with usable values - spd = caps['Speed'] - dup = caps['Duplex'] - mtu = caps['MTU'] - - tests[:auto][:manifest_props][:negotiate_auto] = 'true' unless platform[/n7k/] - tests[:auto][:manifest_props][:duplex] = 'auto' if dup.delete('auto') - tests[:auto][:manifest_props][:speed] = 'auto' if spd.delete('auto') - - tests[:non_default][:manifest_props][:duplex] = dup.shift unless dup.empty? - tests[:non_default][:manifest_props][:speed] = spd.shift unless spd.empty? - tests[:non_default][:manifest_props][:mtu] = mtu.shift unless mtu.empty? - - # Cannot turn off auto-negotiate for speeds 10G+ - non_default_speed = tests[:non_default][:manifest_props][:speed] - tests[:non_default][:manifest_props][:negotiate_auto] = 'false' unless - platform[/n7k/] || non_default_speed.to_i >= 10_000 - - logger.info "\n Pre-Check :non_default hash: #{tests[:non_default]}"\ - "\n Pre-Check :auto hash: #{tests[:auto]}" - interface_cleanup(agent, intf, 'Post-Pre-Check Cleanup') - true -end - -# Helper for 'system default switchport' -def sys_def_switchport?(tests, id) - return unless tests[id].key?(:sys_def_switchport) - - state = tests[id][:sys_def_switchport] - # cached state - return if tests[:sys_def_switchport] == state - - system_default_switchport(agent, state) - # cache for later tests - tests[:sys_def_switchport] = state -end - -# Helper for 'system default switchport shutdown' -def sys_def_switchport_shutdown?(tests, id) - return unless tests[id].key?(:sys_def_sw_shut) - - state = tests[id][:sys_def_sw_shut] - # cached state - return if tests[:sys_def_sw_shut] == state - - system_default_switchport_shutdown(agent, state) - # cache for later tests - tests[:sys_def_sw_shut] = state -end - -# Helper for setting up ACL dependencies -def acl?(tests, id) - tests[id][:acl].each { |acl, afi| config_acl(agent, afi, acl, true) } if - tests[id][:acl] -end - -# Wrapper for interface specific settings prior to calling the -# common test_harness. -def test_harness_interface(tests, id) - return unless platform_supports_test(tests, id) - - tests[id][:ensure] = :present if tests[id][:ensure].nil? - - # Build the manifest for this test - build_manifest_interface(tests, id) - - # Set up system default switchport - sys_def_switchport?(tests, id) - sys_def_switchport_shutdown?(tests, id) - - # Set up ACL - acl?(tests, id) - - tests[id][:code] = [0, 2] - - interface_cleanup(agent, tests[id][:title_pattern]) if tests[id][:preclean] - - test_harness_common(tests, id) - tests[id][:ensure] = nil -end - -################################################################# -# TEST CASE EXECUTION -################################################################# -test_name "TestCase :: #{testheader}" do - # ------------------------------------------------------------------- - logger.info("\n#{'-' * 60}\nSection 1. (L3) Property Testing") - test_harness_interface(tests, 'L3_default') - test_harness_interface(tests, 'L3_default_nexus') - test_harness_interface(tests, 'L3_sub_int') - test_harness_interface(tests, 'L3_misc') - test_harness_interface(tests, 'L3_misc_nexus') - test_harness_interface(tests, 'L3_ACL') - - # ------------------------------------------------------------------- - logger.info("\n#{'-' * 60}\nSection 2. (L2) Access Property Testing") - test_harness_interface(tests, 'L2_access_default') - test_harness_interface(tests, 'L2_access') - - # ------------------------------------------------------------------- - logger.info("\n#{'-' * 60}\nSection 3. (L2) Trunk Property Testing") - test_harness_interface(tests, 'L2_trunk_default') - test_harness_interface(tests, 'L2_trunk') - - # ------------------------------------------------------------------- - logger.info("\n#{'-' * 60}\nSection 4. (SVI) Property Testing") - if platform_supports_test(tests, 'SVI') - resource_set(agent, resource_cisco_overlay_global, - 'Overlay Global mac setup') - end - interface_cleanup(agent, tests[:svi_name]) - test_harness_interface(tests, 'SVI_default') - test_harness_interface(tests, 'SVI') - test_harness_interface(tests, 'SVI_autostate_default') - test_harness_interface(tests, 'SVI_autostate') - - ### ------------------------------------------------------------------- - logger.info("\n#{'-' * 60}\nSection 5. Negotiate Auto, MTU, Speed, Duplex") - if interface_pre_check(tests) - test_harness_run(tests, :auto) - test_harness_run(tests, :non_default) - else - msg = 'Could not find interface capabilities' - logger.error("\n#{tests[:auto][:desc]} :: auto :: SKIP" \ - "\n#{msg}") - logger.error("\n#{tests[:non_default][:desc]} :: non_default :: SKIP" \ - "\n#{msg}") - end - # ------------------------------------------------------------------- - logger.info("\n#{'-' * 60}\nSection 6. Private vlan Property Testing") - resource_absent_cleanup(agent, 'cisco_vlan', 'private-vlan CLEANUP :: ') - test_harness_run(tests_2, :primary) - test_harness_run(tests_2, :community) - test_harness_run(tests_2, :isolated) - test_harness_run(tests_2, :community_2) - test_harness_run(tests_2, :community_3) - test_harness_run(tests_2, :community_4) - test_harness_run(tests_2, :association) - test_harness_interface(tests, 'pvlan_host_port') - test_harness_interface(tests, 'pvlan_promisc_port') - test_harness_interface(tests, 'pvlan_trunk_promisc_port') - test_harness_interface(tests, 'pvlan_trunk_sec_port') - test_harness_interface(tests, 'pvlan_trunk_allow_vlan') - test_harness_interface(tests, 'pvlan_trunk_native_vlan') - test_harness_interface(tests, 'pvlan_host_port_association_default') - test_harness_interface(tests, 'pvlan_promisc_port_association_default') - test_harness_interface(tests, 'pvlan_trunk_promisc_port_default') - test_harness_interface(tests, 'pvlan_trunk_sec_port_default') - test_harness_interface(tests, 'pvlan_trunk_allow_vlan_default') - test_harness_interface(tests, 'pvlan_host_port_default') - test_harness_interface(tests, 'pvlan_promisc_port_default') - test_harness_interface(tests, 'switchport_private_vlan_trunk_native_vlan_default') - test_harness_interface(tests, 'switchport_private_vlan_mapping_trunk_default') - test_harness_interface(tests, 'switchport_private_vlan_association_trunk_default') - interface_cleanup(agent, tests[:svi_name]) - test_harness_interface(tests, 'private_vlan_mapping_svi_default') - test_harness_interface(tests, 'pvlan_mapping_svi') - interface_cleanup(agent, tests[:svi_name]) - - # ------------------------------------------------------------------- - if platform_supports_test(tests, 'BDI_non_default') - logger.info("\n#{'-' * 60}\nSection 7. BDI Property Testing") - bd = tests[:bdi_name][/(\d+)/] - config_bridge_domain(agent, bd) - test_harness_interface(tests, 'BDI_non_default') - end - # ------------------------------------------------------------------- - resource_absent_cleanup(agent, 'cisco_vlan', 'private-vlan CLEANUP :: ') - interface_cleanup(agent, tests[:ethernet]) if tests[:ethernet] - skipped_tests_summary(tests) -end -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_interface/test_interface_L2.rb b/tests/beaker_tests/cisco_interface/test_interface_L2.rb new file mode 100755 index 000000000..5d4478c6e --- /dev/null +++ b/tests/beaker_tests/cisco_interface/test_interface_L2.rb @@ -0,0 +1,138 @@ +# rubocop:disable Style/FileName +############################################################################### +# Copyright (c) 2014-2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### +# +# 'test_interface_L2' primarily tests layer 2 interface properties. +# +############################################################################### +require File.expand_path('../interfacelib.rb', __FILE__) + +# Test hash top-level keys +tests = { + agent: agent, + master: master, + intf_type: 'ethernet', + operating_system: 'nexus', + resource_name: 'cisco_interface', +} + +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + +# Find a usable interface for this test +intf = find_interface(tests) + +# TBD: Consider adding switchport_mode => 'default' tests. + +# Test hash test cases +tests[:default_access] = { + desc: "1.1 Default 'access' Properties", + title_pattern: intf, + code: [0], + preclean_intf: true, + sys_def_switchport: true, + sys_def_sw_shut: true, + manifest_props: { + shutdown: 'default', + switchport_autostate_exclude: 'default', + switchport_mode: 'access', + }, + resource: { + shutdown: 'true', + switchport_autostate_exclude: 'false', + switchport_mode: 'access', + }, +} + +tests[:non_default_access] = { + desc: "1.2 Non Default 'access' Properties", + title_pattern: intf, + sys_def_switchport: true, + sys_def_sw_shut: true, + manifest_props: { + access_vlan: '128', + shutdown: 'false', + switchport_autostate_exclude: 'true', + switchport_mode: 'access', + }, +} + +tests[:default_trunk] = { + desc: "2.1 Default 'trunk' Properties", + title_pattern: intf, + preclean_intf: true, + sys_def_switchport: true, + sys_def_sw_shut: true, + manifest_props: { + shutdown: 'default', + switchport_autostate_exclude: 'default', + switchport_mode: 'trunk', + switchport_trunk_allowed_vlan: 'default', + switchport_trunk_native_vlan: 'default', + + }, + resource: { + shutdown: 'true', + switchport_autostate_exclude: 'false', + switchport_mode: 'trunk', + switchport_trunk_allowed_vlan: '1-4094', + switchport_trunk_native_vlan: '1', + }, +} + +tests[:non_default_trunk] = { + desc: "2.2 Non Default 'trunk' Properties", + title_pattern: intf, + preclean_intf: true, + sys_def_switchport: true, + sys_def_sw_shut: true, + manifest_props: { + shutdown: 'false', + switchport_autostate_exclude: 'true', + switchport_mode: 'trunk', + switchport_trunk_allowed_vlan: '30-33,40,100', + switchport_trunk_native_vlan: '20', + switchport_vtp: 'false', + }, +} + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. 'access' Property Testing") + test_harness_run(tests, :default_access) + test_harness_run(tests, :non_default_access) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. 'trunk' Property Testing") + test_harness_run(tests, :default_trunk) + test_harness_run(tests, :non_default_trunk) + + # ------------------------------------------------------------------- + interface_cleanup(agent, intf) +end + +logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_interface/test_interface_L3.rb b/tests/beaker_tests/cisco_interface/test_interface_L3.rb new file mode 100755 index 000000000..7d452b780 --- /dev/null +++ b/tests/beaker_tests/cisco_interface/test_interface_L3.rb @@ -0,0 +1,166 @@ +# rubocop:disable Style/FileName +############################################################################### +# Copyright (c) 2014-2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### +# +# 'test_interface_L3' primarily tests layer 3 interface properties. +# +############################################################################### +require File.expand_path('../interfacelib.rb', __FILE__) + +# Test hash top-level keys +tests = { + agent: agent, + master: master, + intf_type: 'ethernet', + resource_name: 'cisco_interface', +} + +# Find a usable interface for this test +intf = find_interface(tests) + +# Test hash test cases +tests[:default] = { + desc: '1.1 Default Properties', + title_pattern: intf, + code: [0], + preclean_intf: true, + sys_def_switchport: false, + manifest_props: { + description: 'default', + duplex: 'default', + ipv4_forwarding: 'default', + ipv4_pim_sparse_mode: 'default', + ipv4_proxy_arp: 'default', + ipv4_redirects: 'default', + mtu: 'default', + shutdown: 'default', + vrf: 'default', + }, + resource: { + duplex: 'auto', + ipv4_forwarding: 'false', + ipv4_pim_sparse_mode: 'false', + ipv4_proxy_arp: 'false', + ipv4_redirects: operating_system == 'nexus' ? 'true' : 'false', + mtu: operating_system == 'nexus' ? '1500' : '1514', + shutdown: 'false', + }, +} + +tests[:non_default] = { + desc: '2.1 Non Default Properties', + title_pattern: intf, + sys_def_switchport: false, + manifest_props: { + description: 'Configured with Puppet', + shutdown: true, + ipv4_address: '1.1.1.1', + ipv4_netmask_length: 31, + ipv4_address_secondary: '2.2.2.2', + ipv4_netmask_length_secondary: 31, + ipv4_pim_sparse_mode: true, + ipv4_proxy_arp: true, + ipv4_redirects: operating_system == 'nexus' ? false : true, + switchport_mode: 'disabled', + vrf: 'test1', + }, +} + +tests[:acl] = { + desc: '2.2 ACL Properties', + title_pattern: intf, + operating_system: 'nexus', + sys_def_switchport: false, + manifest_props: { + switchport_mode: 'disabled', + ipv4_acl_in: 'v4_in', + ipv4_acl_out: 'v4_out', + ipv6_acl_in: 'v6_in', + ipv6_acl_out: 'v6_out', + }, + # ACLs must exist on some platforms + acl: { + 'v4_in' => 'ipv4', + 'v4_out' => 'ipv4', + 'v6_in' => 'ipv6', + 'v6_out' => 'ipv6', + }, +} + +# Note: This test should follow the default test as it requires an +# L3 parent interface and this makes it easy to set up. +tests[:dot1q] = { + desc: '2.3 dot1q Sub-interface', + title_pattern: "#{intf}.1", + manifest_props: { encapsulation_dot1q: 30 }, +} + +# This test should be run last since it will break ip addressing properties. +# Note that any tests that follow need to preclean. +tests[:ip_forwarding] = { + desc: '2.4 IP forwarding', + title_pattern: intf, + preclean_intf: true, + sys_def_switchport: false, + manifest_props: { ipv4_forwarding: true }, +} + +def unsupported_properties(_tests, id) + unprops = [] + + if operating_system == 'ios_xr' + unprops << + :duplex << + :ipv4_forwarding << + :ipv4_pim_sparse_mode << + :switchport_mode + end + + # TBD: shutdown has unpredictable behavior. Needs investigation. + unprops << :shutdown if id == :default + + unprops +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + test_harness_run(tests, :default) + test_harness_run(tests, :dot1q) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + test_harness_run(tests, :non_default) + test_harness_run(tests, :acl) + test_harness_run(tests, :ip_forwarding) + + # ------------------------------------------------------------------- + interface_cleanup(agent, intf) + skipped_tests_summary(tests) +end + +logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_interface/test_interface_bdi.rb b/tests/beaker_tests/cisco_interface/test_interface_bdi.rb new file mode 100755 index 000000000..e850e2eba --- /dev/null +++ b/tests/beaker_tests/cisco_interface/test_interface_bdi.rb @@ -0,0 +1,87 @@ +############################################################################### +# Copyright (c) 2014-2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### +# +# 'test_interface_bdi' primarily tests BDI interface properties. +# +############################################################################### +require File.expand_path('../interfacelib.rb', __FILE__) + +# Test hash top-level keys +tests = { + agent: agent, + master: master, + operating_system: 'nexus', + platform: 'n(7)k', + resource_name: 'cisco_interface', + bridge_domain: '100', +} + +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + +# Assign a test interface. +intf = 'bdi100' + +# Test hash test cases +tests[:default] = { + desc: '1.1 Default Properties', + title_pattern: intf, + preclean_intf: true, + manifest_props: { + shutdown: 'default' + }, + resource: { + 'shutdown' => 'true' + }, +} + +tests[:non_default] = { + desc: '2.1 Non Default Properties', + title_pattern: intf, + manifest_props: { + ipv4_address: '10.10.10.1', + ipv4_netmask_length: '24', + shutdown: 'false', + vrf: 'test1', + }, +} + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + test_harness_run(tests, :default) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + test_harness_run(tests, :non_default) + + # ------------------------------------------------------------------- + remove_interface(agent, intf) + remove_all_vlans(agent) +end + +logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_interface/test_interface_capabilities.rb b/tests/beaker_tests/cisco_interface/test_interface_capabilities.rb index 422fabd1d..69a7b04a1 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_capabilities.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_capabilities.rb @@ -21,17 +21,103 @@ # - A description of the 'tests' hash and its usage # ############################################################################### -require File.expand_path('../../lib/utilitylib.rb', __FILE__) +# +# 'test_interface_capabilities' tests platform/linecard variable interface +# properties such as speed, duplex, mtu, negotiate. +# +############################################################################### +require File.expand_path('../interfacelib.rb', __FILE__) # Test hash top-level keys tests = { agent: agent, master: master, + ensurable: false, intf_type: 'ethernet', operating_system: 'nexus', - resource_name: 'cisco_interface_capabilities', + resource_name: 'cisco_interface', +} + +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + +# Find a usable interface for this test +intf = find_interface(tests) + +# Test hash test cases +tests[:default] = { + desc: '1.1 Default Properties', + title_pattern: intf, + code: [0, 2], + manifest_props: { + switchport_mode: 'disabled', + # interface_pre_check() will define add'l properties + }, } +tests[:non_default] = { + desc: '1.2 Misc. Non Default Properties', + title_pattern: intf, + manifest_props: { + switchport_mode: 'disabled', + # interface_pre_check() will define add'l properties + }, +} + +# This method will probe the test interface to determine testable values. +def interface_pre_check(tests, intf) # rubocop:disable Metrics/AbcSize + # Clean the test interface + system_default_switchport(agent, false) + interface_cleanup(agent, intf, 'Initial Cleanup') + + # Get the capabilities and update the caps list with any add'l test values + caps = interface_capabilities(agent, intf) + + if caps.empty? + tests[:skipped] ||= [] + tests[:skipped] << tests[:default][:desc] + tests[:skipped] << tests[:non_default][:desc] + return false + end + + caps['Speed'] += ',auto' unless caps['Speed']['auto'] + caps['Duplex'] += ',auto' unless caps['Duplex']['auto'] + caps['MTU'] = '1600' + + # Create a probe hash to pre-test the properties + probe = { + cmd: PUPPET_BINPATH + 'resource cisco_interface ', + intf: intf, + caps: caps, + probe_props: %w(Speed Duplex MTU), + } + caps = interface_probe(tests, probe)[:caps] + + # Fixup the test manifests with usable values + spd = caps['Speed'] + dup = caps['Duplex'] + mtu = caps['MTU'] + + tests[:default][:manifest_props][:negotiate_auto] = 'true' unless + platform[/n7k/] + tests[:default][:manifest_props][:duplex] = 'auto' if dup.delete('auto') + tests[:default][:manifest_props][:speed] = 'auto' if spd.delete('auto') + + tests[:non_default][:manifest_props][:duplex] = dup.shift unless dup.empty? + tests[:non_default][:manifest_props][:speed] = spd.shift unless spd.empty? + tests[:non_default][:manifest_props][:mtu] = mtu.shift unless mtu.empty? + + # Cannot turn off auto-negotiate for speeds 10G+ + non_default_speed = tests[:non_default][:manifest_props][:speed] + tests[:non_default][:manifest_props][:negotiate_auto] = 'false' unless + platform[/n7k/] || non_default_speed.to_i >= 10_000 + + logger.info("\n Pre-Check :default hash: #{tests[:default]}"\ + "\n Pre-Check :non_default hash: #{tests[:non_default]}") + interface_cleanup(agent, intf, 'Post-Pre-Check Cleanup') + true +end + def parse_capabilities(agent, cmd) on(agent, cmd) caps = {} @@ -45,12 +131,21 @@ def parse_capabilities(agent, cmd) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - skip_unless_supported(tests) - # ------------------------------------------------------------------- - logger.info("\n#{'-' * 60}\n1.1 Test puppet resource vs vsh results") + logger.info("\n#{'-' * 60}\nSection 1. Platform/Linecard Variable Properties") + + if interface_pre_check(tests, intf) + test_harness_run(tests, :default) + test_harness_run(tests, :non_default) + else + msg = 'Could not find interface capabilities' + logger.error("\n#{tests[:default][:desc]} :: default :: SKIP\n#{msg}") + logger.error("\n#{tests[:non_default][:desc]} :: non_def :: SKIP\n#{msg}") + end - intf = find_interface(tests) + # ------------------------------------------------------------------- + # Section 2 & 3 test the cisco_interface_capabilities provider itself. + logger.info("\n#{'-' * 60}\nSection 2. Test puppet resource vs vsh results") vsh_cmd = get_vshell_cmd("show interface #{intf} capabilities") vsh_caps = parse_capabilities(agent, vsh_cmd) @@ -62,12 +157,16 @@ def parse_capabilities(agent, cmd) vsh_caps == resource_caps # ------------------------------------------------------------------- - logger.info("\n#{'-' * 60}\n1.2 Test with utilitylib helper results") + logger.info("\n#{'-' * 60}\nSection 3. Test with utilitylib helper results") util_caps = interface_capabilities(agent, intf) vsh_caps.keys.each do |k| next if vsh_caps[k] == util_caps[k] fail_test('utilitylib helper results mismatch with vsh') end + + # ------------------------------------------------------------------- + interface_cleanup(agent, intf) end + logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb b/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb new file mode 100755 index 000000000..67643a13c --- /dev/null +++ b/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb @@ -0,0 +1,223 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### +# +# 'test_interface_private_vlan' tests private-vlan interface properties. +# +############################################################################### +require File.expand_path('../interfacelib.rb', __FILE__) + +# Test hash top-level keys +tests = { + agent: agent, + master: master, + intf_type: 'ethernet', + operating_system: 'nexus', + platform: 'n(3|5|6|7|9)k', + resource_name: 'cisco_interface', +} + +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + +# Assign a test interface. +intf = find_interface(tests) + +# Test hash test cases +tests[:default] = { + desc: '1.1 Default Properties', + title_pattern: intf, + code: [0], + preclean_intf: true, + sys_def_switchport: true, + manifest_props: { + switchport_pvlan_host: 'default', + switchport_pvlan_host_association: 'default', + switchport_pvlan_mapping: 'default', + switchport_pvlan_mapping_trunk: 'default', + switchport_pvlan_promiscuous: 'default', + switchport_pvlan_trunk_native_vlan: 'default', + switchport_pvlan_trunk_allowed_vlan: 'default', + switchport_pvlan_trunk_association: 'default', + switchport_pvlan_trunk_promiscuous: 'default', + switchport_pvlan_trunk_secondary: 'default', + }, + resource: { + switchport_pvlan_host: 'false', + # switchport_pvlan_host_association: nil, + # switchport_pvlan_mapping: nil, + # switchport_pvlan_mapping_trunk: nil, + switchport_pvlan_promiscuous: 'false', + switchport_pvlan_trunk_native_vlan: '1', + switchport_pvlan_trunk_allowed_vlan: 'none', + # switchport_pvlan_trunk_association: nil, + switchport_pvlan_trunk_promiscuous: 'false', + switchport_pvlan_trunk_secondary: 'false', + }, +} + +tests[:host] = { + desc: '2.1 Host', + title_pattern: intf, + preclean_intf: true, + manifest_props: { + switchport_pvlan_host: true, + switchport_pvlan_host_association: %w(2 12), + switchport_pvlan_trunk_allowed_vlan: '6, 5, 2, 12', + switchport_pvlan_trunk_native_vlan: 42, + }, + resource: { + switchport_pvlan_host: 'true', + switchport_pvlan_host_association: %w(2 12), + switchport_pvlan_trunk_allowed_vlan: '2,5-6,12', + switchport_pvlan_trunk_native_vlan: '42', + }, + dependency: %( + cisco_vlan { '12': pvlan_type => 'community' } + cisco_vlan { '2': pvlan_type => 'primary', pvlan_association => '12' } + ), +} + +tests[:promiscuous] = { + desc: '2.2 Promiscuous', + title_pattern: intf, + manifest_props: { + switchport_pvlan_promiscuous: true + }, +} + +tests[:trunk_secondary] = { + desc: '3.1 Trunk Secondary', + platform: 'n(5|6|7|9)k', + title_pattern: intf, + preclean_intf: true, + manifest_props: { + switchport_pvlan_trunk_association: [%w(4 14), %w(3 13)], + switchport_pvlan_trunk_secondary: true, + switchport_pvlan_mapping_trunk: [%w(7 17,27,37), %w(5 15)], + }, + resource: { + switchport_pvlan_trunk_association: [%w(3 13), %w(4 14)], + switchport_pvlan_trunk_secondary: 'true', + switchport_pvlan_mapping_trunk: [%w(5 15), %w(7 17,27,37)], + }, + dependency: %( + cisco_vlan { '13': pvlan_type => 'isolated' } + cisco_vlan { '14': pvlan_type => 'isolated' } + cisco_vlan { '3': pvlan_type => 'primary', pvlan_association => '13' } + cisco_vlan { '4': pvlan_type => 'primary', pvlan_association => '14' } + + cisco_vlan { '15': pvlan_type => 'community' } + cisco_vlan { '5': pvlan_type => 'primary', pvlan_association => '15' } + + cisco_vlan { '17': pvlan_type => 'community' } + cisco_vlan { '27': pvlan_type => 'community' } + cisco_vlan { '37': pvlan_type => 'community' } + cisco_vlan { '7': pvlan_type => 'primary', pvlan_association => '17,27,37'} + ), +} + +tests[:trunk_promiscuous] = { + desc: '3.2 Trunk Promiscuous', + platform: 'n(5|6|7|9)k', + title_pattern: intf, + manifest_props: { + switchport_pvlan_trunk_promiscuous: true + }, +} + +svi = 'vlan13' +tests[:svi_mapping] = { + desc: '4.1 SVI Private-Vlan Mapping', + platform: 'n(5|6|7|9)k', + title_pattern: svi, + preclean_intf: true, + manifest_props: { + pvlan_mapping: %w(108-109) + }, +} + +# This method overrides utilitylib.rb:unsupported_properties() +def unsupported_properties(_tests, _id) + unprops = [] + if platform[/n3k/] + unprops << + :switchport_pvlan_mapping_trunk << + :switchport_pvlan_trunk_association << + :switchport_pvlan_trunk_promiscuous << + :switchport_pvlan_trunk_secondary + end + unprops +end + +def vtp_cleanup(agent) + return unless platform[/n6k/] + logger.info("\n#{'-' * 60}\nVTP cleanup") + resource_set(agent, %w(cisco_vtp default ensure absent)) +end + +# CSCuz58517 workaround: 'private-vlan association trunk' doesn't get +# removed by 'default interface' on some platforms. +def pvlan_assoc_cleanup(agent, intf) + logger.info("\n#{'-' * 60}\nPrivate-vlan cleanup") + resource_set(agent, ['cisco_interface', intf, 'switchport_mode', 'disabled']) +end + +# This method overrides utilitylib.rb:dependency_manifest() +def dependency_manifest(tests, id) + tests[id][:dependency] if tests[id][:dependency] +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + vtp_cleanup(agent) + pvlan_assoc_cleanup(agent, intf) + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Defaults") + test_harness_run(tests, :default) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. Port Mode") + test_harness_run(tests, :host) + test_harness_run(tests, :promiscuous) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 3. Trunk Mode") + test_harness_run(tests, :trunk_secondary) + pvlan_assoc_cleanup(agent, intf) + test_harness_run(tests, :trunk_promiscuous) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 4. SVI Mapping") + test_harness_run(tests, :svi_mapping) + + # ------------------------------------------------------------------- + pvlan_assoc_cleanup(agent, intf) + interface_cleanup(agent, intf) + remove_interface(agent, svi) + skipped_tests_summary(tests) +end + +logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_interface/test_interface_stp.rb b/tests/beaker_tests/cisco_interface/test_interface_stp.rb index 017d8f826..03f2e5c1e 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_stp.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_stp.rb @@ -21,21 +21,33 @@ # - A description of the 'tests' hash and its usage # ############################################################################### -require File.expand_path('../../lib/utilitylib.rb', __FILE__) +# +# 'test_interface_stp' primarily tests STP interface properties. +# +############################################################################### +require File.expand_path('../interfacelib.rb', __FILE__) # Test hash top-level keys tests = { - master: master, agent: agent, + master: master, + intf_type: 'ethernet', operating_system: 'nexus', resource_name: 'cisco_interface', } +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + +# Find a usable interface for this test +intf = find_interface(tests) + # Test hash test cases tests[:default] = { desc: '1.1 Default Properties', - intf_type: 'ethernet', - preclean: 'cisco_interface', + title_pattern: intf, + preclean_intf: true, + code: [0], manifest_props: { stp_bpdufilter: 'default', stp_bpduguard: 'default', @@ -64,15 +76,13 @@ }, } -# Non-default Tests. NOTE: [:resource] = [:manifest_props] for all non-default - stp_mst_cost_ndp = Array[%w(0,2-4,6,8-12 1000), %w(1000 2568)] stp_mst_port_priority_ndp = Array[%w(0,2-11,20-33 64), %w(1111 160)] stp_vlan_cost_ndp = Array[%w(1-4,6,8-12 1000), %w(1000 2568)] stp_vlan_port_priority_ndp = Array[%w(1-11,20-33 64), %w(1111 160)] tests[:non_default] = { desc: '2.1 Non Defaults', - intf_type: 'ethernet', + title_pattern: intf, manifest_props: { switchport_mode: 'trunk', stp_bpdufilter: 'enable', @@ -88,6 +98,14 @@ stp_vlan_port_priority: stp_vlan_port_priority_ndp, }, resource: { + switchport_mode: 'trunk', + stp_bpdufilter: 'enable', + stp_bpduguard: 'enable', + stp_cost: '2000', + stp_guard: 'loop', + stp_link_type: 'shared', + stp_port_priority: '64', + stp_port_type: 'network', stp_mst_cost: "#{stp_mst_cost_ndp}", stp_mst_port_priority: "#{stp_mst_port_priority_ndp}", stp_vlan_cost: "#{stp_vlan_cost_ndp}", @@ -95,65 +113,21 @@ }, } -# Create actual manifest for a given test scenario. -def build_manifest_interface(tests, id) - manifest = prop_hash_to_manifest(tests[id][:manifest_props]) - if tests[id][:ensure] == :absent - state = 'ensure => absent,' - tests[id][:resource] = { 'ensure' => 'absent' } - else - state = 'ensure => present,' - end - create_interface_title(tests, id) - - tests[id][:manifest] = "cat <#{PUPPETMASTER_MANIFESTPATH} - \nnode default { - cisco_interface { '#{tests[id][:title_pattern]}': - #{state}\n#{manifest} - }\n}\nEOF" - - cmd = PUPPET_BINPATH + - "resource cisco_interface '#{tests[id][:title_pattern]}'" - tests[id][:resource_cmd] = cmd -end - -# Wrapper for interface specific settings prior to calling the -# common test_harness. -def test_harness_interface(tests, id) - return unless platform_supports_test(tests, id) - - tests[id][:ensure] = :present if tests[id][:ensure].nil? - - # Build the manifest for this test - build_manifest_interface(tests, id) - - tests[id][:code] = [0, 2] - - interface_cleanup(agent, tests[id][:title_pattern]) if tests[id][:preclean] - - test_harness_common(tests, id) - tests[id][:ensure] = nil -end - ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + resource_absent_cleanup(agent, 'cisco_bridge_domain') + # ------------------------------------------------------------------- - device = platform - logger.info("#### This device is of type: #{device} #####") - resource_absent_cleanup(agent, 'cisco_bridge_domain', - 'bridge-domain CLEANUP :: ') logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") - - test_harness_interface(tests, :default) + test_harness_run(tests, :default) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + test_harness_run(tests, :non_default) - test_harness_interface(tests, :non_default) - - skipped_tests_summary(tests) + interface_cleanup(agent, intf) end logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_interface/test_interface_svi.rb b/tests/beaker_tests/cisco_interface/test_interface_svi.rb new file mode 100755 index 000000000..c21cd4ddd --- /dev/null +++ b/tests/beaker_tests/cisco_interface/test_interface_svi.rb @@ -0,0 +1,112 @@ +############################################################################### +# Copyright (c) 2014-2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### +# +# 'test_interface_svi' primarily tests SVI interface properties. +# +############################################################################### +require File.expand_path('../interfacelib.rb', __FILE__) + +# Test hash top-level keys +tests = { + agent: agent, + master: master, + operating_system: 'nexus', + resource_name: 'cisco_interface', + anycast_gateway_mac: true, +} + +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + +# Assign a test interface. +intf = 'vlan13' + +# Test hash test cases +tests[:default_mgmt] = { + desc: "1.1 Default 'mgmt'", + title_pattern: intf, + preclean_intf: true, + manifest_props: { + svi_management: 'default' + }, + resource: { + svi_management: 'false' + }, +} + +tests[:non_default_mgmt] = { + desc: "1.2 Non Default 'mgmt'", + title_pattern: intf, + manifest_props: { + svi_management: 'true', + fabric_forwarding_anycast_gateway: 'true', + }, +} + +tests[:default_autostate] = { + platform: 'n(3|7|9)k', + desc: "2.1 Default 'autostate'", + title_pattern: intf, + preclean_intf: true, + manifest_props: { + svi_autostate: 'default' + }, + resource: { + svi_autostate: 'true' + }, +} + +tests[:non_default_autostate] = { + platform: 'n(3|7|9)k', + desc: "2.1 Non Default 'autostate'", + title_pattern: intf, + preclean_intf: true, + manifest_props: { + svi_autostate: 'false' + }, +} + +def unsupported_properties(*) + unprops = [] + unprops << :fabric_forwarding_anycast_gateway unless platform[/n9k/] + unprops +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + test_harness_run(tests, :default_mgmt) + test_harness_run(tests, :non_default_mgmt) + test_harness_run(tests, :default_autostate) + test_harness_run(tests, :non_default_autostate) + + # ------------------------------------------------------------------- + remove_interface(agent, intf) + skipped_tests_summary(tests) +end + +logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_interface/test_interface_vlan_mapping.rb b/tests/beaker_tests/cisco_interface/test_interface_vlan_mapping.rb new file mode 100644 index 000000000..449adf569 --- /dev/null +++ b/tests/beaker_tests/cisco_interface/test_interface_vlan_mapping.rb @@ -0,0 +1,114 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### +# +# 'test_interface_vlan_mapping' tests vlan-mapping interface properties. +# +# **************************************** +# ** IMPORTANT ADDITIONAL PREREQUISITES ** +# **************************************** +# +# The vlan_mapping properties are "Multi-Tenancy Full" properties which +# currently have limited platform and linecard support. This test script will +# look for these requirements and fail if they are not present: +# +# - VDC support +# - F3 linecard +# +# This test will need to be updated as the product matures. +# +############################################################################### +require File.expand_path('../interfacelib.rb', __FILE__) + +tests = { + agent: agent, + master: master, + operating_system: 'nexus', + platform: 'n7k', + resource_name: 'cisco_interface', + intf_type: 'ethernet', + bridge_domain: '100', + switchport_mode: 'trunk', + # On N7k, feature vni requires solely F3 cards in the vdc + vdc_limit_module: 'f3', +} + +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + +# Assign a test interface. +if platform[/n7k/] + setup_mt_full_env(tests, self) + # Use test interface discovered by setup_mt_full_env(). + intf = tests[:intf] +else + intf = find_interface(tests) +end + +# Test hash test cases +tests[:default] = { + desc: '1.1 Default Properties', + title_pattern: intf, + # :preclean_intf not needed since setup_mt_full_env will clean intf + code: [0], + manifest_props: { + vlan_mapping_enable: 'default', + vlan_mapping: 'default', + }, + resource: { + vlan_mapping_enable: 'true', + # 'vlan_mapping' is nil when default + }, +} + +vlan_maps = Array[%w(20 21), %w(30 31)] +tests[:non_default] = { + desc: '2.1 Non Default Properties', + title_pattern: intf, + manifest_props: { + vlan_mapping_enable: 'false', + vlan_mapping: vlan_maps, + }, + resource: { + vlan_mapping_enable: 'false', + vlan_mapping: "#{vlan_maps}", + }, +} + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + # ----------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + test_harness_run(tests, :default) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + test_harness_run(tests, :non_default) + + # ------------------------------------------------------------------- + interface_cleanup(agent, intf) +end + +logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_interface/test_vlan_mapping.rb b/tests/beaker_tests/cisco_interface/test_vlan_mapping.rb deleted file mode 100644 index f44cf3516..000000000 --- a/tests/beaker_tests/cisco_interface/test_vlan_mapping.rb +++ /dev/null @@ -1,190 +0,0 @@ -############################################################################### -# Copyright (c) 2016 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# test_vlan_mapping.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet Interface resource testcase of vlan_mapping properties, -# for use with Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# - Host configuration file contains agent and master information. -# - SSH is enabled on the N9K Agent. -# - Puppet master/server is started. -# - Puppet agent certificate has been signed on the Puppet master/server. -# -############################################################################### -# -# **************************************** -# ** IMPORTANT ADDITIONAL PREREQUISITES ** -# **************************************** -# -# The vlan_mapping properties are "Multi-Tenancy Full" properties which -# currently have limited platform and linecard support. This test script will -# look for these requirements and fail if they are not present: -# -# - VDC support -# - F3 linecard -# -# This test will need to be updated as the product matures. -# -############################################################################### -# -# TestCase: -# --------- -# This resource test verifies default values for all properties. -# -# The following exit_codes are validated for Puppet, Vegas shell and -# Bash shell commands. -# -# Vegas and Bash Shell Commands: -# 0 - successful command execution -# > 0 - failed command execution. -# -# Puppet Commands: -# 0 - no changes have occurred -# 1 - errors have occurred, -# 2 - changes have occurred -# 4 - failures have occurred and -# 6 - changes and failures have occurred. -# -# NOTE: 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# -# The test cases use RegExp pattern matching on stdout or output IO -# instance attributes to verify resource properties. -# -############################################################################### -require File.expand_path('../../lib/utilitylib.rb', __FILE__) - -# ----------------------------- -# Common settings and variables -# ----------------------------- -testheader = 'Resource cisco_interface: vlan_mapping properties' - -# The 'tests' hash is used to define all of the test data values and expected -# results. It is also used to pass optional flags to the test methods when -# necessary. - -# 'tests' hash -# Top-level keys set by caller: -# tests[:master] - the master object -# tests[:agent] - the agent object -# tests[:bridge_domain] - the bridge-domain configuration -# tests[:switchport_mode] - the interface switchport mode type -# -tests = { - master: master, - agent: agent, - operating_system: 'nexus', - resource_name: 'cisco_interface', - bridge_domain: '199', - switchport_mode: 'trunk', - # On N7k, feature vni requires solely F3 cards in the vdc - vdc_limit_module: 'f3', -} - -# tests[id] keys set by caller and used by test_harness_common: -# -# tests[id] keys set by caller: -# tests[id][:desc] - a string to use with logs & debugs -# tests[id][:manifest] - the complete manifest, as used by test_harness_common -# tests[id][:resource] - a hash of expected states, used by test_resource -# tests[id][:resource_cmd] - 'puppet resource' command to use with test_resource -# tests[id][:ensure] - (Optional) set to :present or :absent before calling -# tests[id][:code] - (Optional) override the default exit code in some tests. -# - -tests['default_properties'] = { - desc: '1.1 Default Properties', - manifest_props: { - vlan_mapping_enable: 'default', - vlan_mapping: 'default', - }, - resource: { - 'vlan_mapping_enable' => 'true', - # 'vlan_mapping' is nil when default - }, -} - -vlan_maps = [%w(20 21), %w(30 31)] -tests['non_default_properties'] = { - desc: '2.1 Non Default Properties', - manifest_props: { - vlan_mapping_enable: 'false', - vlan_mapping: vlan_maps, - }, - resource: { - 'vlan_mapping_enable' => 'false', - 'vlan_mapping' => "#{vlan_maps}", - }, -} - -################################################################# -# HELPER FUNCTIONS -################################################################# - -def build_manifest_vlan_mapping(tests, id) - intf = tests[:intf] - tests[id][:manifest] = "cat <#{PUPPETMASTER_MANIFESTPATH} - node default { - cisco_interface { '#{intf}':\n - switchport_mode => '#{tests[:switchport_mode]}', - #{prop_hash_to_manifest(tests[id][:manifest_props])} - }\n }\nEOF" - - cmd = PUPPET_BINPATH + "resource cisco_interface '#{intf}'" - tests[id][:resource_cmd] = cmd -end - -def test_harness_vlan_mapping(tests, id) - tests[id][:ensure] = :present if tests[id][:ensure].nil? - - # Build the manifest for this test - build_manifest_vlan_mapping(tests, id) - - # Workaround for (ioctl) facter bug on n7k *** - tests[id][:code] = [0, 2] if platform[/n7k/] - - test_harness_common(tests, id) - tests[id][:ensure] = nil -end - -################################################################# -# TEST CASE EXECUTION -################################################################# -test_name "TestCase :: #{testheader}" do - # ------------------------------------------------------------------- - logger.info("\n#{'-' * 60}\nSection 0. Testbed Initialization") - # ------------------------------------------------------------------- - setup_mt_full_env(tests, self) - - # ----------------------------------- - logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") - id = 'default_properties' - test_harness_vlan_mapping(tests, id) - - tests[id][:ensure] = :absent - test_harness_vlan_mapping(tests, id) - tests[id][:ensure] = :present - - # ------------------------------------------------------------------- - logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") - test_harness_vlan_mapping(tests, 'non_default_properties') - - interface_cleanup(agent, tests[:intf], 'Post-test cleanup: ') -end -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_vlan/accessvlan_provider_defaults.rb b/tests/beaker_tests/cisco_vlan/accessvlan_provider_defaults.rb deleted file mode 100644 index ea3bf64d5..000000000 --- a/tests/beaker_tests/cisco_vlan/accessvlan_provider_defaults.rb +++ /dev/null @@ -1,186 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# AccessVlan-Provider-Defaults.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet ACCESSVLAN resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a ACCESSVLAN resource test that tests for default values for -# access_vlan, description, ipv4_proxy_arp, ipv4_redirects, -# negotiate_auto, shutdown, switchport_autostate_exclude, -# switchport_mode and switchport_vtp attributes of a -# cisco_interface resource when created with 'ensure' => 'present'. -# Access Standard VLANs are in the VLAN ID range 2..1005. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-4 deal with cisco_interface resource creation and its -# verification using Puppet Agent and the switch running-config. -# Steps 5-7 deal with cisco_interface resource deletion and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and AccessVlanLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../accessvlanlib.rb', __FILE__) - -result = 'PASS' -testheader = 'ACCESSVLAN Resource :: All Attributes Defaults' - -# Local tests hash and helper method used to dynamically find an available -# interface for tests that require an interface. -tests = { intf_type: 'ethernet', agent: agent, testheader: testheader } -def find_ospf_interface(tests) - if tests[:ethernet] - intf = tests[:ethernet] - else - intf = find_interface(tests) - # cache for later tests - tests[:ethernet] = intf - end - intf -end -int = find_ospf_interface(tests) - -# Cleanup commands for 'system default switchport [default]' -cmd1 = 'no system default switchport' -cmd2 = cmd1 + ' shutdown' - -# @test_name [TestCase] Executes defaults testcase for ACCESSVLAN Resource. -test_name "TestCase :: #{testheader}" do - resource_absent_cleanup(agent, 'cisco_vlan', 'VLAN CLEAN :: ') - - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, AccessVlanLib.create_accessvlan_manifest_absent(int)) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - # Or expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - - # Ensure that 'system default switchport [default]' is set property before - # the test starts. - command_config(agent, cmd1, cmd1) - command_config(agent, cmd2, cmd2) - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource present manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, AccessVlanLib.create_accessvlan_manifest_present(int)) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource present manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_interface resource on agent using resource cmd. - step 'TestStep :: Check cisco_interface resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_interface '#{int}'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'access_vlan' => '128', - 'ipv4_proxy_arp' => 'false', - 'ipv4_redirects' => 'true', - # 'negotiate_auto' => 'true', # TBD: Needs plat awareness - 'shutdown' => 'false', - 'switchport_autostate_exclude' => 'false', - 'switchport_mode' => 'access', - 'switchport_vtp' => 'false' }, - false, self, logger) - end - - logger.info("Check cisco_interface resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource absent manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, AccessVlanLib.create_accessvlan_manifest_absent(int)) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource absent manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_interface resource on agent using resource cmd. - step 'TestStep :: Check cisco_interface resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Presence of AccessVLAN 1 implies absence of AccessVLAN 128. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_interface '#{int}'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'access_vlan' => '1', - 'ipv4_proxy_arp' => 'false', - 'ipv4_redirects' => 'true', - # 'negotiate_auto' => 'true', # TBD: Needs plat awareness - 'shutdown' => 'false', - 'switchport_autostate_exclude' => 'false', - 'switchport_mode' => 'access', - 'switchport_vtp' => 'false' }, - false, self, logger) - end - - logger.info("Check cisco_interface resource absence on agent :: #{result}") - end - - # Ensure that 'system default switchport [default]' is set property before - # after the test ends. - command_config(agent, cmd1, cmd1) - command_config(agent, cmd2, cmd2) - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_vlan/accessvlan_provider_negatives.rb b/tests/beaker_tests/cisco_vlan/accessvlan_provider_negatives.rb deleted file mode 100644 index c4db02758..000000000 --- a/tests/beaker_tests/cisco_vlan/accessvlan_provider_negatives.rb +++ /dev/null @@ -1,260 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# AccessVlan-Provider-Negatives.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet ACCESSVLAN resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a ACCESSVLAN resource test that tests for negative values for -# access_vlan, description, ipv4_proxy_arp, ipv4_redirects, -# negotiate_auto, shutdown, switchport_autostate_exclude, -# switchport_mode and switchport_vtp attributes of a -# cisco_interface resource when created with 'ensure' => 'present'. -# Access Standard VLANs are in the VLAN ID range 2..1005. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# The next set of teststeps deal with attribute negative tests and their -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and AccessVlanLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../accessvlanlib.rb', __FILE__) - -result = 'PASS' -testheader = 'ACCESSVLAN Resource :: All Attributes Negatives' - -# Local tests hash and helper method used to dynamically find an available -# interface for tests that require an interface. -tests = { intf_type: 'ethernet', agent: agent, testheader: testheader } -def find_ospf_interface(tests) - if tests[:ethernet] - intf = tests[:ethernet] - else - intf = find_interface(tests) - # cache for later tests - tests[:ethernet] = intf - end - intf -end -int = find_ospf_interface(tests) - -# @test_name [TestCase] Executes negatives testcase for ACCESSVLAN Resource. -test_name "TestCase :: #{testheader}" do - resource_absent_cleanup(agent, 'cisco_vlan', 'VLAN CLEAN :: ') - - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, AccessVlanLib.create_accessvlan_manifest_absent(int)) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - # Or expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, AccessVlanLib.create_accessvlan_manifest_ipv4proxyarp_negative(int)) - - # Expected exit_code is 1 since this is a puppet agent cmd with error. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [1]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_interface resource on agent using resource cmd. - step 'TestStep :: Check cisco_interface resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_interface '#{int}'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ipv4_proxy_arp' => AccessVlanLib::IPV4PROXYARP_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_interface resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, AccessVlanLib.create_accessvlan_manifest_ipv4redir_negative(int)) - - # Expected exit_code is 1 since this is a puppet agent cmd with error. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [1]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_interface resource on agent using resource cmd. - step 'TestStep :: Check cisco_interface resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_interface '#{int}'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ipv4_redirects' => AccessVlanLib::IPV4REDIRECTS_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_interface resource presence on agent :: #{result}") - end - - # Negotiate Auto: TBD: Needs plat awareness - # @step [Step] Requests manifest from the master server to the agent. - # step 'TestStep :: Get negative test resource manifest from master' do - # # Expected exit_code is 0 since this is a bash shell cmd. - # on(master, AccessVlanLib.create_accessvlan_manifest_negoauto_negative(int)) - # - # # Expected exit_code is 1 since this is a puppet agent cmd with error. - # cmd_str = PUPPET_BINPATH + 'agent -t' - # on(agent, cmd_str, acceptable_exit_codes: [1]) - # - # logger.info("Get negative test resource manifest from master :: #{result}") - # end - - # Negotiate Auto: TBD: Needs plat awareness - # @step [Step] Checks cisco_interface resource on agent using resource cmd. - # step 'TestStep :: Check cisco_interface resource absence on agent' do - # # Expected exit_code is 0 since this is a puppet resource cmd. - # # Flag is set to true to check for absence of RegExp pattern in stdout. - # cmd_str = PUPPET_BINPATH + "resource cisco_interface '#{int}'" - # on(agent, cmd_str) do - # search_pattern_in_output(stdout, - # { 'negotiate_auto' => AccessVlanLib::NEGOTIATEAUTO_NEGATIVE }, - # true, self, logger) - # end - # - # logger.info("Check cisco_interface resource presence on agent :: #{result}") - # end - - # Negotiate Auto: TBD: Needs plat awareness - # @step [Step] Requests manifest from the master server to the agent. - # step 'TestStep :: Get negative test resource manifest from master' do - # # Expected exit_code is 0 since this is a bash shell cmd. - # on(master, AccessVlanLib.create_accessvlan_manifest_shutdown_negative(int)) - # - # # Expected exit_code is 1 since this is a puppet agent cmd with error. - # cmd_str = PUPPET_BINPATH + 'agent -t' - # on(agent, cmd_str, acceptable_exit_codes: [1]) - # - # logger.info("Get negative test resource manifest from master :: #{result}") - # end - - # @step [Step] Checks cisco_interface resource on agent using resource cmd. - step 'TestStep :: Check cisco_interface resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_interface '#{int}'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'shutdown' => AccessVlanLib::SHUTDOWN_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_interface resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, AccessVlanLib.create_accessvlan_manifest_autostate_negative(int)) - - # Expected exit_code is 1 since this is a puppet agent cmd with error. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [1]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_interface resource on agent using resource cmd. - step 'TestStep :: Check cisco_interface resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_interface '#{int}'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'switchport_autostate_exclude' => AccessVlanLib::AUTOSTATE_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_interface resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, AccessVlanLib.create_accessvlan_manifest_vtp_negative(int)) - - # Expected exit_code is 1 since this is a puppet agent cmd with error. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [1]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_interface resource on agent using resource cmd. - step 'TestStep :: Check cisco_interface resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_interface '#{int}'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'switchport_vtp' => AccessVlanLib::SWITCHPORTVTP_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_interface resource presence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_vlan/accessvlan_provider_nondefaults.rb b/tests/beaker_tests/cisco_vlan/accessvlan_provider_nondefaults.rb deleted file mode 100644 index 7f31eb90b..000000000 --- a/tests/beaker_tests/cisco_vlan/accessvlan_provider_nondefaults.rb +++ /dev/null @@ -1,186 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# AccessVlan-Provider-NonDefaults.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet ACCESSVLAN resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a ACCESSVLAN resource test that tests for nondefault values for -# access_vlan, description, ipv4_proxy_arp, ipv4_redirects, -# negotiate_auto, shutdown, switchport_autostate_exclude, -# switchport_mode and switchport_vtp attributes of a -# cisco_interface resource when created with 'ensure' => 'present'. -# Access Standard VLANs are in the VLAN ID range 2..1005. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-4 deal with cisco_interface resource creation and its -# verification using Puppet Agent and the switch running-config. -# Steps 5-7 deal with cisco_interface resource deletion and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and AccessVlanLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../accessvlanlib.rb', __FILE__) - -result = 'PASS' -testheader = 'ACCESSVLAN Resource :: All Attributes NonDefaults' - -# Local tests hash and helper method used to dynamically find an available -# interface for tests that require an interface. -tests = { intf_type: 'ethernet', agent: agent, testheader: testheader } -def find_ospf_interface(tests) - if tests[:ethernet] - intf = tests[:ethernet] - else - intf = find_interface(tests) - # cache for later tests - tests[:ethernet] = intf - end - intf -end -int = find_ospf_interface(tests) - -# Cleanup commands for 'system default switchport [default]' -cmd1 = 'no system default switchport' -cmd2 = cmd1 + ' shutdown' - -# @test_name [TestCase] Executes nondefaults testcase for ACCESSVLAN Resource. -test_name "TestCase :: #{testheader}" do - resource_absent_cleanup(agent, 'cisco_vlan', 'VLAN CLEAN :: ') - - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, AccessVlanLib.create_accessvlan_manifest_absent(int)) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - # Or expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - - # Ensure that 'system default switchport' [default] is set property before - # the test starts. - command_config(agent, cmd1, cmd1) - command_config(agent, cmd2, cmd2) - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource nondefaults manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, AccessVlanLib.create_accessvlan_manifest_nondefaults(int)) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource nondefaults manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_interface resource on agent using resource cmd. - step 'TestStep :: Check cisco_interface resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_interface '#{int}'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'access_vlan' => '128', - 'ipv4_proxy_arp' => 'false', - 'ipv4_redirects' => 'true', - # 'negotiate_auto' => 'true', # TBD: Needs plat awareness - 'shutdown' => 'true', - 'switchport_autostate_exclude' => 'false', - 'switchport_mode' => 'access', - 'switchport_vtp' => 'false' }, - false, self, logger) - end - - logger.info("Check cisco_interface resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource absent manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, AccessVlanLib.create_accessvlan_manifest_absent(int)) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource absent manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_interface resource on agent using resource cmd. - step 'TestStep :: Check cisco_interface resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Presence of AccessVLAN 1 implies absence of AccessVLAN 128. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_interface '#{int}'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'access_vlan' => '1', - 'ipv4_proxy_arp' => 'false', - 'ipv4_redirects' => 'true', - # 'negotiate_auto' => 'true', # TBD: Needs plat awareness - 'shutdown' => 'false', - 'switchport_autostate_exclude' => 'false', - 'switchport_mode' => 'access', - 'switchport_vtp' => 'false' }, - false, self, logger) - end - - logger.info("Check cisco_interface resource absence on agent :: #{result}") - end - - # Ensure that 'system default switchport [default]' is set property before - # after the test ends. - command_config(agent, cmd1, cmd1) - command_config(agent, cmd2, cmd2) - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_vlan/accessvlanlib.rb b/tests/beaker_tests/cisco_vlan/accessvlanlib.rb deleted file mode 100644 index b1620bf89..000000000 --- a/tests/beaker_tests/cisco_vlan/accessvlanlib.rb +++ /dev/null @@ -1,253 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### - -# Require UtilityLib.rb path. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) - -# ACCESSVLAN Utility Library: -# --------------------------- -# accessvlanlib.rb -# -# This is the utility library for the ACCESSVLAN provider Beaker test cases that -# contains the common methods used across the ACCESSVLAN testsuite's cases. The -# library is implemented as a module with related methods and constants defined -# inside it for use as a namespace. All of the methods are defined as module -# methods. -# -# Every Beaker ACCESSVLAN test case that runs an instance of Beaker::TestCase -# requires AccessVlanLib module. -# -# The module has a single set of methods: -# A. Methods to create manifests for cisco_interface Puppet provider test cases. -module AccessVlanLib - # Group of Constants used in negative tests for ACCESSVLAN provider. - DESCRIPTION_NEGATIVE = '' - IPV4PROXYARP_NEGATIVE = 'invalid' - IPV4REDIRECTS_NEGATIVE = 'invalid' - NEGOTIATEAUTO_NEGATIVE = 'invalid' - SHUTDOWN_NEGATIVE = 'invalid' - AUTOSTATE_NEGATIVE = 'invalid' - SWITCHPORTVTP_NEGATIVE = 'invalid' - - # A. Methods to create manifests for cisco_interface Puppet provider test cases. - - # Method to create a manifest for AccessVLAN resource attribute 'ensure' where - # 'ensure' is set to present. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_accessvlan_manifest_present(intf) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_vlan { '128': - ensure => present, - shutdown => 'default', - state => 'default', - } - cisco_interface { '#{intf}': - ensure => present, - access_vlan => '128', - description => 'default', - ipv4_proxy_arp => 'default', - ipv4_redirects => 'default', - #negotiate_auto => 'default', # TBD: Needs plat awareness - shutdown => 'default', - switchport_autostate_exclude => 'default', - switchport_mode => 'access', - switchport_vtp => 'default', - } -} -EOF" - manifest_str - end - - # Method to create a manifest for AccessVLAN resource attribute 'ensure' where - # 'ensure' is set to absent. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_accessvlan_manifest_absent(intf) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_vlan { '128': - ensure => absent, - } - cisco_interface { '#{intf}': - ensure => present, - access_vlan => '1', - description => 'default', - ipv4_proxy_arp => 'default', - ipv4_redirects => 'default', - #negotiate_auto => 'default', # TBD: Needs plat awareness - shutdown => 'default', - switchport_autostate_exclude => 'default', - switchport_mode => 'access', - switchport_vtp => 'default', - } -} -EOF" - manifest_str - end - - # Method to create a manifest for AccessVLAN resource attributes: - # access_vlan, description, ipv4_proxy_arp, ipv4_redirects, - # negotiate_auto, shutdown, switchport_autostate_exclude, switchport_mode - # and switchport_vtp. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_accessvlan_manifest_nondefaults(intf) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_vlan { '128': - ensure => present, - shutdown => 'default', - state => 'default', - } - cisco_interface { '#{intf}': - ensure => present, - access_vlan => '128', - description => 'Configured with puppet', - ipv4_proxy_arp => 'default', - ipv4_redirects => 'default', - #negotiate_auto => 'default', # TBD: Needs plat awareness - shutdown => 'true', - switchport_autostate_exclude => 'default', - switchport_mode => 'access', - switchport_vtp => 'default', - } -} -EOF" - manifest_str - end - - # Method to create a manifest for AccessVLAN resource attribute 'ipv4_proxy_arp'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_accessvlan_manifest_ipv4proxyarp_negative(intf) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_vlan { '128': - ensure => present, - state => 'default', - } - cisco_interface { '#{intf}': - ensure => present, - access_vlan => '128', - ipv4_proxy_arp => #{AccessVlanLib::IPV4PROXYARP_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for AccessVLAN resource attribute 'ipv4_redirects'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_accessvlan_manifest_ipv4redir_negative(intf) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_vlan { '128': - ensure => present, - state => 'default', - } - cisco_interface { '#{intf}': - ensure => present, - access_vlan => '128', - ipv4_redirects => #{AccessVlanLib::IPV4REDIRECTS_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for AccessVLAN resource attribute 'negotiate_auto'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_accessvlan_manifest_negoauto_negative(intf) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_vlan { '128': - ensure => present, - state => 'default', - } - cisco_interface { '#{intf}': - ensure => present, - access_vlan => '128', - #negotiate_auto => #{AccessVlanLib::NEGOTIATEAUTO_NEGATIVE}, # TBD: Needs plat awareness - } -} -EOF" - manifest_str - end - - # Method to create a manifest for AccessVLAN resource attribute 'shutdown'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_accessvlan_manifest_shutdown_negative(intf) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_vlan { '128': - ensure => present, - state => 'default', - } - cisco_interface { '#{intf}': - ensure => present, - access_vlan => '128', - shutdown => #{AccessVlanLib::SHUTDOWN_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for AccessVLAN resource attribute 'autostate'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_accessvlan_manifest_autostate_negative(intf) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_vlan { '128': - ensure => present, - state => 'default', - } - cisco_interface { '#{intf}': - ensure => present, - access_vlan => '128', - switchport_autostate_exclude => #{AccessVlanLib::AUTOSTATE_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for AccessVLAN resource attribute 'switchport_vtp'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_accessvlan_manifest_vtp_negative(intf) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_vlan { '128': - ensure => present, - state => 'default', - } - cisco_interface { '#{intf}': - ensure => present, - access_vlan => '128', - switchport_vtp => #{AccessVlanLib::SWITCHPORTVTP_NEGATIVE}, - } -} -EOF" - manifest_str - end -end diff --git a/tests/beaker_tests/cisco_vlan/test_private_vlan.rb b/tests/beaker_tests/cisco_vlan/test_private_vlan.rb new file mode 100644 index 000000000..7630d88da --- /dev/null +++ b/tests/beaker_tests/cisco_vlan/test_private_vlan.rb @@ -0,0 +1,93 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### +# +# 'test_private_vlan' tests *VLAN* related private_vlan properties. +# +# (See 'test_interface_private_vlan' for interface-related private-vlan tests) +# +############################################################################### +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# Test hash top-level keys +tests = { + agent: agent, + master: master, + resource_name: 'cisco_vlan', + operating_system: 'nexus', + platform: 'n(3|5|6|7|9)k', +} + +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + +tests[:primary] = { + desc: '1.1 Primary', + title_pattern: '100', + manifest_props: { + pvlan_type: 'primary', + pvlan_association: '101, 102, 98-99, 105', + }, + resource: { + pvlan_type: 'primary', + pvlan_association: "['98-99', '101-102', '105']", + }, +} + +tests[:community] = { + desc: '1.2 Community', + title_pattern: '100', + manifest_props: { + pvlan_type: 'community' + }, +} + +tests[:isolated] = { + desc: '1.3 Isolated', + title_pattern: '100', + manifest_props: { + pvlan_type: 'isolated' + }, +} + +# This method overrides the method in utilitylib.rb to set up dependencies +# for interface tests. +def test_harness_dependencies(*) + logger.info(' * Process test_harness_dependencies (test_private_vlan)') + remove_all_vlans(agent) +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Property Testing") + test_harness_run(tests, :primary) + test_harness_run(tests, :community) + test_harness_run(tests, :isolated) + + remove_all_vlans(agent) +end + +logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_vlan/test_vlan.rb b/tests/beaker_tests/cisco_vlan/test_vlan.rb new file mode 100644 index 000000000..ae9251211 --- /dev/null +++ b/tests/beaker_tests/cisco_vlan/test_vlan.rb @@ -0,0 +1,133 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### +# +# 'test_vlan' tests standard and extended vlan properties. +# +# (See 'test_interface_private_vlan' for interface-related private-vlan tests) +# +############################################################################### +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# Test hash top-level keys +tests = { + agent: agent, + master: master, + resource_name: 'cisco_vlan', + operating_system: 'nexus', +} + +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + +tests[:default_standard] = { + desc: '1.1 Default standard vlan properties', + title_pattern: '128', + manifest_props: { + fabric_control: 'default', + mapped_vni: 'default', + shutdown: 'default', + state: 'default', + # vlan_name: Does not support default but resource displays default name. + }, + resource: { + fabric_control: 'false', + shutdown: 'false', + state: 'active', + vlan_name: 'VLAN0128', + }, +} + +tests[:non_default_standard] = { + desc: '1.2 Non Default standard vlan properties', + title_pattern: '128', + manifest_props: { + fabric_control: 'true', + mapped_vni: '128000', + shutdown: 'true', + state: 'suspend', + vlan_name: 'Standard_Configured_By_Puppet', + }, +} + +tests[:default_extended] = { + desc: '1.3 Default extended vlan properties', + title_pattern: '2400', + manifest_props: { + fabric_control: 'default', + mapped_vni: 'default', + shutdown: 'default', + state: 'default', + # vlan_name: Does not support default but resource displays default name. + }, + resource: { + fabric_control: 'false', + shutdown: 'false', + state: 'active', + vlan_name: 'VLAN2400', + }, +} + +tests[:non_default_extended] = { + desc: '1.4 Non Default extended vlan properties', + title_pattern: '2400', + manifest_props: { + fabric_control: 'true', + mapped_vni: '4096', + shutdown: 'false', + state: 'suspend', + vlan_name: 'Extended_Configured_By_Puppet', + }, +} +# State cannot be modified for extended vlans on N5k and N6k platforms. +tests[:non_default_extended][:manifest_props].delete(:state) if platform[/n(5|6)k/] + +def unsupported_properties(_tests, _id) + unprops = [] + + unprops << :mapped_vni if platform[/n7k/] + + unprops << :fabric_control unless platform[/n7k/] + + unprops +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + remove_all_vlans(agent) + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Property Testing") + test_harness_run(tests, :default_standard) + test_harness_run(tests, :non_default_standard) + + # Cleanup between standard and extended vlan tests. + remove_all_vlans(agent) + test_harness_run(tests, :default_extended) + test_harness_run(tests, :non_default_extended) + + remove_all_vlans(agent) +end + +logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_vlan/vlan/extendedvlan_provider_defaults.rb b/tests/beaker_tests/cisco_vlan/vlan/extendedvlan_provider_defaults.rb deleted file mode 100644 index f4da6d653..000000000 --- a/tests/beaker_tests/cisco_vlan/vlan/extendedvlan_provider_defaults.rb +++ /dev/null @@ -1,144 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# ExtendedVlan-Provider-Defaults.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet EXTVLAN resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a EXTVLAN resource test that tests for default values for -# vlan_name, state and shutdown attributes of a -# cisco_vlan resource when created with 'ensure' => 'present'. -# Extended VLANs are in the VLAN ID range 1006..3967. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-4 deal with cisco_vlan resource creation and its -# verification using Puppet Agent and the switch running-config. -# Steps 5-7 deal with cisco_vlan resource deletion and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and VlanLib.rb paths. -require File.expand_path('../../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../vlanlib.rb', __FILE__) - -result = 'PASS' -testheader = 'EXTVLAN Resource :: All Attributes Defaults' - -# @test_name [TestCase] Executes defaults testcase for EXTVLAN Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - resource_absent_cleanup(agent, 'cisco_bridge_domain', - 'bridge-domain CLEANUP :: ') - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_extvlan_manifest_absent) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - # Or expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource present manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_extvlan_manifest_present(platform.match('n9k'))) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource present manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_vlan resource on agent using resource cmd. - step 'TestStep :: Check cisco_vlan resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_vlan '2400'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'shutdown' => 'false', - 'state' => 'active', - 'vlan_name' => 'VLAN2400' }, - false, self, logger) - end - - logger.info("Check cisco_vlan resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource absent manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_extvlan_manifest_absent) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource absent manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_vlan resource on agent using resource cmd. - step 'TestStep :: Check cisco_vlan resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_vlan '2400'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'shutdown' => 'false', - 'state' => 'active', - 'vlan_name' => 'VLAN2400' }, - true, self, logger) - end - - logger.info("Check cisco_vlan resource absence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_vlan/vlan/extendedvlan_provider_negatives.rb b/tests/beaker_tests/cisco_vlan/vlan/extendedvlan_provider_negatives.rb deleted file mode 100644 index 1f8905c9c..000000000 --- a/tests/beaker_tests/cisco_vlan/vlan/extendedvlan_provider_negatives.rb +++ /dev/null @@ -1,160 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# ExtendedVlan-Provider-Negatives.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet EXTVLAN resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a EXTVLAN resource test that tests for negative values for -# vlan_name, state and shutdown attributes of a -# cisco_vlan resource when created with 'ensure' => 'present'. -# Extended VLANs are in the VLAN ID range 1006..3967. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# The next set of teststeps deal with attribute negative tests and their -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and VlanLib.rb paths. -require File.expand_path('../../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../vlanlib.rb', __FILE__) - -result = 'PASS' -testheader = 'EXTVLAN Resource :: All Attributes Negatives' - -# @test_name [TestCase] Executes negatives testcase for EXTVLAN Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_extvlan_manifest_absent) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - # Or expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_extvlan_manifest_vlanname_negative) - - # Expected exit_code is 1 since this is a puppet agent cmd with error. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [1]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_vlan resource on agent using resource cmd. - step 'TestStep :: Check cisco_vlan resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_vlan '2400'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'vlan_name' => VlanLib::VLANNAME_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_vlan resource absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_extvlan_manifest_state_negative) - - # Expected exit_code is 1 since this is a puppet agent cmd with error. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [1]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_vlan resource on agent using resource cmd. - step 'TestStep :: Check cisco_vlan resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_vlan '2400'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'state' => VlanLib::STATE_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_vlan resource absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_extvlan_manifest_shutdown_negative) - - # Expected exit_code is 1 since this is a puppet agent cmd with error. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [1]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_vlan resource on agent using resource cmd. - step 'TestStep :: Check cisco_vlan resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_vlan '2400'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'shutdown' => VlanLib::SHUTDOWN_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_vlan resource absence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_vlan/vlan/extendedvlan_provider_nondefaults.rb b/tests/beaker_tests/cisco_vlan/vlan/extendedvlan_provider_nondefaults.rb deleted file mode 100644 index a38d8126a..000000000 --- a/tests/beaker_tests/cisco_vlan/vlan/extendedvlan_provider_nondefaults.rb +++ /dev/null @@ -1,143 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# ExtendedVlan-Provider-NonDefaults.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet EXTVLAN resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a EXTVLAN resource test that tests for nondefault values for -# vlan_name, state and shutdown attributes of a -# cisco_vlan resource when created with 'ensure' => 'present'. -# Extended VLANs are in the VLAN ID range 1006..3967. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-4 deal with cisco_vlan resource creation and its -# verification using Puppet Agent and the switch running-config. -# Steps 5-7 deal with cisco_vlan resource deletion and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and VlanLib.rb paths. -require File.expand_path('../../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../vlanlib.rb', __FILE__) - -result = 'PASS' -testheader = 'EXTVLAN Resource :: All Attributes NonDefaults' - -# @test_name [TestCase] Executes nondefaults testcase for EXTVLAN Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - resource_absent_cleanup(agent, 'cisco_bridge_domain', - 'bridge-domain CLEANUP :: ') - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_extvlan_manifest_absent) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - # Or expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource nondefaults manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_extvlan_manifest_nondefaults(platform.match('n9k'))) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource nondefaults manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_vlan resource on agent using resource cmd. - step 'TestStep :: Check cisco_vlan resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_vlan '2400'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'mapped_vni' => ('24000' if platform.match('n9k')), - 'shutdown' => 'false', - 'vlan_name' => 'DESCR-VLAN2400' }.reject { |_k, v| v.nil? }, - false, self, logger) - end - - logger.info("Check cisco_vlan resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource absent manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_extvlan_manifest_absent) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource absent manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_vlan resource on agent using resource cmd. - step 'TestStep :: Check cisco_vlan resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_vlan '2400'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'shutdown' => 'false', - 'vlan_name' => 'DESCR-VLAN2400' }, - true, self, logger) - end - - logger.info("Check cisco_vlan resource absence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_vlan/vlan/standardvlan_provider_defaults.rb b/tests/beaker_tests/cisco_vlan/vlan/standardvlan_provider_defaults.rb deleted file mode 100644 index fe04c168a..000000000 --- a/tests/beaker_tests/cisco_vlan/vlan/standardvlan_provider_defaults.rb +++ /dev/null @@ -1,149 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# StandardVlan-Provider-Defaults.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet STDVLAN resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a STDVLAN resource test that tests for default values for -# vlan_name, state and shutdown attributes of a -# cisco_vlan resource when created with 'ensure' => 'present'. -# Standard VLANs are in the VLAN ID range 2..1005. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-4 deal with cisco_vlan resource creation and its -# verification using Puppet Agent and the switch running-config. -# Steps 5-7 deal with cisco_vlan resource deletion and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and VlanLib.rb paths. -require File.expand_path('../../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../vlanlib.rb', __FILE__) - -result = 'PASS' -testheader = 'STDVLAN Resource :: All Attributes Defaults' -test_properties = { - mapped_vni: platform.match('n9k'), - fabric_control: platform.match('n7k'), -} - -# @test_name [TestCase] Executes defaults testcase for STDVLAN Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - resource_absent_cleanup(agent, 'cisco_bridge_domain', - 'bridge-domain CLEANUP :: ') - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_stdvlan_manifest_absent) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - # Or expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource present manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_stdvlan_manifest_present(test_properties)) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource present manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_vlan resource on agent using resource cmd. - step 'TestStep :: Check cisco_vlan resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_vlan '128'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'shutdown' => 'false', - 'state' => 'active', - 'vlan_name' => 'VLAN0128', - 'fabric_control' => ('false' if test_properties[:fabric_control]) }.reject { |_k, v| v.nil? }, - false, self, logger) - end - - logger.info("Check cisco_vlan resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource absent manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_stdvlan_manifest_absent) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource absent manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_vlan resource on agent using resource cmd. - step 'TestStep :: Check cisco_vlan resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_vlan '128'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'shutdown' => 'false', - 'state' => 'active', - 'vlan_name' => 'VLAN0128' }, - true, self, logger) - end - - logger.info("Check cisco_vlan resource absence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_vlan/vlan/standardvlan_provider_negatives.rb b/tests/beaker_tests/cisco_vlan/vlan/standardvlan_provider_negatives.rb deleted file mode 100644 index a541c39ab..000000000 --- a/tests/beaker_tests/cisco_vlan/vlan/standardvlan_provider_negatives.rb +++ /dev/null @@ -1,160 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# StandardVlan-Provider-Negatives.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet STDVLAN resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a STDVLAN resource test that tests for negative values for -# vlan_name, state and shutdown attributes of a -# cisco_vlan resource when created with 'ensure' => 'present'. -# Standard VLANs are in the VLAN ID range 2..1005. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# The next set of teststeps deal with attribute negative tests and their -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and VlanLib.rb paths. -require File.expand_path('../../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../vlanlib.rb', __FILE__) - -result = 'PASS' -testheader = 'STDVLAN Resource :: All Attributes Negatives' - -# @test_name [TestCase] Executes negatives testcase for STDVLAN Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_stdvlan_manifest_absent) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - # Or expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_stdvlan_manifest_vlanname_negative) - - # Expected exit_code is 1 since this is a puppet agent cmd with error. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [1]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_vlan resource on agent using resource cmd. - step 'TestStep :: Check cisco_vlan resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_vlan '128'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'vlan_name' => VlanLib::VLANNAME_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_vlan resource absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_stdvlan_manifest_state_negative) - - # Expected exit_code is 1 since this is a puppet agent cmd with error. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [1]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_vlan resource on agent using resource cmd. - step 'TestStep :: Check cisco_vlan resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_vlan '128'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'state' => VlanLib::STATE_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_vlan resource absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_stdvlan_manifest_shutdown_negative) - - # Expected exit_code is 1 since this is a puppet agent cmd with error. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [1]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_vlan resource on agent using resource cmd. - step 'TestStep :: Check cisco_vlan resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_vlan '128'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'shutdown' => VlanLib::SHUTDOWN_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_vlan resource absence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_vlan/vlan/standardvlan_provider_nondefaults.rb b/tests/beaker_tests/cisco_vlan/vlan/standardvlan_provider_nondefaults.rb deleted file mode 100644 index 587fab025..000000000 --- a/tests/beaker_tests/cisco_vlan/vlan/standardvlan_provider_nondefaults.rb +++ /dev/null @@ -1,150 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# StandardVlan-Provider-NonDefaults.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet STDVLAN resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a STDVLAN resource test that tests for nondefault values for -# vlan_name, state and shutdown attributes of a -# cisco_vlan resource when created with 'ensure' => 'present'. -# Standard VLANs are in the VLAN ID range 2..1005. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-4 deal with cisco_vlan resource creation and its -# verification using Puppet Agent and the switch running-config. -# Steps 5-7 deal with cisco_vlan resource deletion and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and VlanLib.rb paths. -require File.expand_path('../../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../vlanlib.rb', __FILE__) - -result = 'PASS' -testheader = 'STDVLAN Resource :: All Attributes NonDefaults' -test_properties = { - mapped_vni: platform.match('n9k'), - fabric_control: platform.match('n7k'), -} - -# @test_name [TestCase] Executes nondefaults testcase for STDVLAN Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - resource_absent_cleanup(agent, 'cisco_bridge_domain', - 'bridge-domain CLEANUP :: ') - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_stdvlan_manifest_absent) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - # Or expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource nondefaults manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_stdvlan_manifest_nondefaults(test_properties)) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource nondefaults manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_vlan resource on agent using resource cmd. - step 'TestStep :: Check cisco_vlan resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_vlan '128'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'mapped_vni' => ('128000' if test_properties[:mapped_vni]), - 'shutdown' => 'true', - 'state' => 'suspend', - 'vlan_name' => 'DESCR-VLAN0128', - 'fabric_control' => ('true' if test_properties[:fabric_control]) }.reject { |_k, v| v.nil? }, - false, self, logger) - end - - logger.info("Check cisco_vlan resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource absent manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, VlanLib.create_stdvlan_manifest_absent) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource absent manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_vlan resource on agent using resource cmd. - step 'TestStep :: Check cisco_vlan resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_vlan '128'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'shutdown' => 'true', - 'state' => 'suspend', - 'vlan_name' => 'DESCR-VLAN0128' }, - true, self, logger) - end - - logger.info("Check cisco_vlan resource absence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_vlan/vlan/test_private_vlan.rb b/tests/beaker_tests/cisco_vlan/vlan/test_private_vlan.rb deleted file mode 100644 index d82dbba17..000000000 --- a/tests/beaker_tests/cisco_vlan/vlan/test_private_vlan.rb +++ /dev/null @@ -1,124 +0,0 @@ -############################################################################### -# Copyright (c) 2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### - -require File.expand_path('../../../lib/utilitylib.rb', __FILE__) - -# ----------------------------- -# Common settings and variables -# ----------------------------- -testheader = 'Resource private_vlan' - -# Top-level keys set by caller: -tests = { - master: master, - agent: agent, - resource_name: 'cisco_vlan', - operating_system: 'nexus', - platform: 'n(3|5|6|7|9)k', -} - -tests[:primary] = { - desc: '2.1 configure pvlan primary type', - title_pattern: '100', - manifest_props: { - private_vlan_type: 'primary' - }, -} - -tests[:community] = { - desc: '2.2 change type: primary to community', - title_pattern: '100', - manifest_props: { - private_vlan_type: 'community' - }, -} - -tests[:isolated] = { - desc: '2.3 change type: community to isolated', - title_pattern: '100', - manifest_props: { - private_vlan_type: 'isolated' - }, -} - -tests[:isolated_101] = { - desc: '2.4 configured isolated vlan', - title_pattern: '101', - manifest_props: { - private_vlan_type: 'isolated' - }, -} - -tests[:community_102] = { - desc: '2.5 configured community vlan', - title_pattern: '102', - manifest_props: { - private_vlan_type: 'community' - }, -} - -vlan_assoc = %w(99 101 102 105) -tests[:association] = { - desc: '2.6 configured private vlan association', - title_pattern: '100', - manifest_props: { - private_vlan_association: ['99,101-102,105'] - }, - resource: { - 'private_vlan_association' => "#{vlan_assoc}" - }, -} - -tests[:association_default] = { - desc: '2.7 private vlan association default', - title_pattern: '100', - manifest_props: { - private_vlan_association: 'default' - }, - resource: { - }, -} - -tests[:type_default] = { - desc: '2.8 private vlan type default', - title_pattern: '100', - manifest_props: { - private_vlan_type: 'default' - }, - resource: { - }, -} - -################################################################# -# TEST CASE EXECUTION -################################################################# -test_name "TestCase :: #{testheader}" do - # ------------------------------------------------------------------- - logger.info("\n#{'-' * 60}\nSection 1. Property Testing") - resource_absent_cleanup(agent, 'cisco_vlan', 'private-vlan CLEANUP :: ') - test_harness_run(tests, :primary) - test_harness_run(tests, :community) - test_harness_run(tests, :isolated) - test_harness_run(tests, :primary) - test_harness_run(tests, :isolated_101) - test_harness_run(tests, :community_102) - test_harness_run(tests, :association) - test_harness_run(tests, :association_default) - test_harness_run(tests, :type_default) - resource_absent_cleanup(agent, 'cisco_vlan', 'private-vlan CLEANUP :: ') -end - -logger.info('TestCase :: # {testheader} :: End') diff --git a/tests/beaker_tests/cisco_vlan/vlan/vlanlib.rb b/tests/beaker_tests/cisco_vlan/vlan/vlanlib.rb deleted file mode 100644 index 514525d24..000000000 --- a/tests/beaker_tests/cisco_vlan/vlan/vlanlib.rb +++ /dev/null @@ -1,304 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### - -# Require UtilityLib.rb path. -require File.expand_path('../../../lib/utilitylib.rb', __FILE__) - -# VLAN Utility Library: -# --------------------- -# vlanlib.rb -# -# This is the utility library for the VLAN provider Beaker test cases that -# contains the common methods used across the VLAN testsuite's cases. The -# library is implemented as a module with related methods and constants defined -# inside it for use as a namespace. All of the methods are defined as module -# methods. -# -# Every Beaker VLAN test case that runs an instance of Beaker::TestCase -# requires VlanLib module. -# -# The module has a single set of methods: -# A. Methods to create manifests for cisco_vlan Puppet provider test cases. -module VlanLib - # Group of Constants used in negative tests for VLAN provider. - VLANNAME_NEGATIVE = '' - STATE_NEGATIVE = 'invalid' - SHUTDOWN_NEGATIVE = 'invalid' - - # A. Methods to create manifests for cisco_vlan Puppet provider test cases. - - # Method to create a manifest for StandardVLAN resource attribute 'ensure' where - # 'ensure' is set to present. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_stdvlan_manifest_present(test_properties) - if test_properties[:mapped_vni] - manifest = " - cisco_vlan { '128': - ensure => present, - state => 'default', - mapped_vni => 'default', - shutdown => 'default', - }" - else - if test_properties[:fabric_control] - manifest = " - cisco_vlan { '128': - ensure => present, - state => 'default', - shutdown => 'default', - fabric_control => 'default', - }" - else - manifest = " - cisco_vlan { '128': - ensure => present, - state => 'default', - shutdown => 'default', - }" - end - end - - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - #{manifest} -} -EOF" - manifest_str - end - - # Method to create a manifest for StandardVLAN resource attribute 'ensure' where - # 'ensure' is set to absent. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_stdvlan_manifest_absent - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_vlan { '128': - ensure => absent, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for StandardVLAN resource attributes: - # vlan_name, state and shutdown. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_stdvlan_manifest_nondefaults(test_properties) - if test_properties[:mapped_vni] - manifest = " - cisco_vlan { '128': - ensure => present, - vlan_name => 'DESCR-VLAN0128', - state => 'suspend', - mapped_vni => '128000', - shutdown => 'true', - }" - else - if test_properties[:fabric_control] - manifest = " - cisco_vlan { '128': - ensure => present, - vlan_name => 'DESCR-VLAN0128', - state => 'suspend', - shutdown => 'true', - fabric_control => 'true', - }" - else - manifest = " - cisco_vlan { '128': - ensure => present, - vlan_name => 'DESCR-VLAN0128', - state => 'suspend', - shutdown => 'true', - }" - end - end - - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - #{manifest} -} -EOF" - manifest_str - end - - # Method to create a manifest for StandardVLAN resource attribute 'vlan_name'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_stdvlan_manifest_vlanname_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_vlan { '128': - ensure => present, - vlan_name => #{VlanLib::VLANNAME_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for StandardVLAN resource attribute 'state'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_stdvlan_manifest_state_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_vlan { '128': - ensure => present, - state => #{VlanLib::STATE_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for StandardVLAN resource attribute 'shutdown'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_stdvlan_manifest_shutdown_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_vlan { '128': - ensure => present, - shutdown => #{VlanLib::SHUTDOWN_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for ExtendedVLAN resource attribute 'ensure' where - # 'ensure' is set to present. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_extvlan_manifest_present(test_mapped_vni) - if test_mapped_vni - manifest = " - cisco_vlan { '2400': - ensure => present, - state => 'default', - mapped_vni => 'default', - shutdown => 'default', - }" - else - manifest = " - cisco_vlan { '2400': - ensure => present, - state => 'default', - shutdown => 'default', - }" - end - - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - #{manifest} -} -EOF" - manifest_str - end - - # Method to create a manifest for ExtendedVLAN resource attribute 'ensure' where - # 'ensure' is set to absent. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_extvlan_manifest_absent - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_vlan { '2400': - ensure => absent, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for ExtendedVLAN resource attributes: - # vlan_name and state. - # Extended VLANs cannot be shutdown. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_extvlan_manifest_nondefaults(test_mapped_vni) - if test_mapped_vni - manifest = " - cisco_vlan { '2400': - ensure => present, - mapped_vni => '24000', - vlan_name => 'DESCR-VLAN2400', - }" - else - manifest = " - cisco_vlan { '2400': - ensure => present, - vlan_name => 'DESCR-VLAN2400', - }" - end - - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - #{manifest} -} -EOF" - manifest_str - end - - # Method to create a manifest for ExtendedVLAN resource attribute 'vlan_name'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_extvlan_manifest_vlanname_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_vlan { '2400': - ensure => present, - vlan_name => #{VlanLib::VLANNAME_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for ExtendedVLAN resource attribute 'state'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_extvlan_manifest_state_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_vlan { '2400': - ensure => present, - state => #{VlanLib::STATE_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for ExtendedVLAN resource attribute 'shutdown'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_extvlan_manifest_shutdown_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_vlan { '2400': - ensure => present, - shutdown => #{VlanLib::SHUTDOWN_NEGATIVE}, - } -} -EOF" - manifest_str - end -end diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 5675ceb1e..791b9e1c5 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -49,40 +49,6 @@ # These methods are defined outside of a module so that # they can access the Beaker DSL API's. -# cisco_interface uses the interface name as the title. -# Find an available interface and create an appropriate title. -def create_interface_title(tests, id) - return tests[id][:title_pattern] if tests[id][:title_pattern] - - # Prefer specific test key over the all tests key - type = tests[id][:intf_type] || tests[:intf_type] - case type - when /ethernet/i - if tests[:ethernet] - intf = tests[:ethernet] - else - intf = find_interface(tests, id) - # cache for later tests - tests[:ethernet] = intf - end - when /dot1q/ - if tests[:ethernet] - intf = "#{tests[:ethernet]}.1" - else - intf = find_interface(tests, id) - # cache for later tests - tests[:ethernet] = intf - intf = "#{intf}.1" unless intf.nil? - end - when /vlan/ - intf = tests[:svi_name] - when /bdi/ - intf = tests[:bdi_name] - end - logger.info("\nUsing interface: #{intf}") - tests[id][:title_pattern] = intf -end - # Method to return the Vegas shell command string for a NXOS CLI command. # @param nxosclistr [String] The NXOS CLI command string to execute on host. # @result vshellcmd [String] Returns 'vsh -c ' command string. @@ -307,6 +273,7 @@ def resource_absent_cleanup(agent, res_name, stepinfo='absent clean') when /cisco_snmp_user/ next if title[/devops/i] when /cisco_vlan/ + # TBD: Per-vlan cleanup is too slow. Consider 'no vlan 1-4095' next if title == '1' when /cisco_vrf/ next if title[/management/] @@ -340,9 +307,9 @@ def resource_titles(agent, res_name, action=:find) end # Helper to configure switchport mode -def config_switchport_mode(agent, mode, stepinfo='switchport mode: ') +def config_switchport_mode(agent, intf, mode, stepinfo='switchport mode: ') step "TestStep :: #{stepinfo}" do - cmd = "switchport ; switchport mode #{mode}" + cmd = "interface #{intf} ; switchport ; switchport mode #{mode}" command_config(agent, cmd, cmd) end end @@ -357,6 +324,19 @@ def system_default_switchport(agent, state=false, end end +# Helper for checking/setting 'system default switchport' +def config_system_default_switchport?(tests, id) + return unless tests[id].key?(:sys_def_switchport) + + state = tests[id][:sys_def_switchport] + # cached state + return if tests[:sys_def_switchport] == state + + system_default_switchport(agent, state) + # cache for later tests + tests[:sys_def_switchport] = state +end + # Helper to toggle 'system default switchport shutdown' def system_default_switchport_shutdown(agent, state=false, stepinfo='system default switchport shutdown') @@ -367,6 +347,19 @@ def system_default_switchport_shutdown(agent, state=false, end end +# Helper for checking/setting 'system default switchport shutdown' +def config_system_default_switchport_shutdown?(tests, id) + return unless tests[id].key?(:sys_def_sw_shut) + + state = tests[id][:sys_def_sw_shut] + # cached state + return if tests[:sys_def_sw_shut] == state + + system_default_switchport_shutdown(agent, state) + # cache for later tests + tests[:sys_def_sw_shut] = state +end + # Helper for creating / removing an ACL def config_acl(agent, afi, acl, state, stepinfo='ACL:') step "TestStep :: #{stepinfo}" do @@ -378,6 +371,12 @@ def config_acl(agent, afi, acl, state, stepinfo='ACL:') end end +# Helper for checking/setting ACLs +def config_acl?(tests, id) + tests[id][:acl].each { |acl, afi| config_acl(agent, afi, acl, true) } if + tests[id][:acl] +end + # Helper for creating / removing bridge-domain configs # 1. Remove any existing bridge-domain config unless it contains our test_bd # 2. Remove vlan with test_bd id @@ -412,6 +411,20 @@ def config_bridge_domain(agent, test_bd, stepinfo='bridge-domain config:') end end +def config_bridge_domain?(tests, _id) + return unless platform[/n7k/] && tests.key?(:bridge_domain) + + bd = tests[:bridge_domain] + agent = tests[:agent] + on(agent, get_vshell_cmd('show runn bridge-domain'), pty: true) + + config_bridge_domain(agent, bd) unless + stdout.match(Regexp.new("^bridge-domain #{bd}")) + + # Delete the key to prevent having to set this for every test case + tests.delete(:bridge_domain) +end + # Helper for creating / removing encap profile vni (global) configs def config_encap_profile_vni_global(agent, cmd, stepinfo='encap profile vni global:') @@ -422,7 +435,7 @@ def config_encap_profile_vni_global(agent, cmd, # Helper to nuke a single interface. This is needed to remove all # configurations from the interface. -def interface_cleanup(agent, intf, stepinfo='Pre Clean:') +def interface_cleanup(agent, intf, stepinfo='Interface Clean:') step "TestStep :: #{stepinfo}" do cmd = "resource cisco_command_config 'interface_cleanup' "\ "command='default interface #{intf}'" @@ -661,7 +674,7 @@ def supported_property_hash(tests, id, property_hash) # - Cleans resource def test_harness_run(tests, id) return unless platform_supports_test(tests, id) - + logger.info("\n * Process test_harness_run") tests[id][:ensure] = :present if tests[id][:ensure].nil? # Build the manifest for this test @@ -693,6 +706,7 @@ def setup_mt_full_env(tests, testcase) # MT-full tests require a specific linecard. Search for a compatible # module and enable it. + logger.info('Process setup_mt_full_env') testheader = tests[:resource_name] mod = tests[:vdc_limit_module] mod = 'f3' if mod.nil? @@ -730,9 +744,9 @@ def setup_mt_full_env(tests, testcase) "Unable to allocate interface '#{intf}' to VDC") end - interface_cleanup(tests[:agent], intf) + interface_cleanup(agent, intf) - config_switchport_mode(agent, tests[:switchport_mode]) if + config_switchport_mode(agent, intf, tests[:switchport_mode]) if tests[:switchport_mode] config_bridge_domain(agent, tests[:bridge_domain]) if @@ -797,8 +811,13 @@ def command_config(agent, cmd, msg='') # Helper to set properties using the puppet resource command. def resource_set(agent, resource, msg='') logger.info("\n#{msg}") - cmd = "resource #{resource[:name]} '#{resource[:title]}' " \ - "#{resource[:property]}='#{resource[:value]}'" + if resource.is_a?(Array) + cmd = + "resource %s '%s' %s='%s'" % resource # rubocop:disable Style/FormatString + else + cmd = "resource #{resource[:name]} '#{resource[:title]}' "\ + "#{resource[:property]}='#{resource[:value]}'" + end cmd = PUPPET_BINPATH + cmd on(agent, cmd, acceptable_exit_codes: [0, 2]) end @@ -1020,7 +1039,7 @@ def skip_unless_supported(tests) pattern = tests[:platform] return false if pattern.nil? || platform.match(tests[:platform]) msg = "Skipping all tests; '#{tests[:resource_name]}' "\ - 'is unsupported on this node' + '(or test file) is not supported on this node' banner = '#' * msg.length raise_skip_exception("\n#{banner}\n#{msg}\n#{banner}\n", self) end @@ -1036,6 +1055,7 @@ def skipped_tests_summary(tests) # TBD: This needs to be more selective when used with modular platforms, # particularly to ignore L2-only F2 cards on N7k. +# TBD: Remove the :intf_type requirement & change this to find_ethernet() # # Find a test interface on the agent. # Callers should include the following hash keys: @@ -1043,6 +1063,7 @@ def skipped_tests_summary(tests) # [:intf_type] # [:resource_name] def find_interface(tests, id=nil, skipcheck=true) + logger.info("\n#{'-' * 60}\n Find a suitable interface\n#{'-' * 60}") # Prefer specific test key over the all tests key if id type = tests[id][:intf_type] || tests[:intf_type] @@ -1062,6 +1083,7 @@ def find_interface(tests, id=nil, skipcheck=true) msg = 'Unable to find suitable interface module for this test.' prereq_skip(tests[:resource_name], self, msg) end + logger.info("\n * Found #{intf}") intf end @@ -1211,6 +1233,12 @@ def debug_probe(probe, msg) logger.info("\n #{msg}: #{dbg}") end +# Remove a single dynamic interface (Vlan, Loopback, Port-channel, etc). +def remove_interface(agent, intf) + cmd = " no interface #{intf.capitalize}" + command_config(agent, cmd, cmd) +end + def remove_all_vlans(agent, stepinfo='Remove all vlans & bridge-domains') step "\n--------\n * TestStep :: #{stepinfo}" do resource_absent_cleanup(agent, 'cisco_bridge_domain', 'bridge domains') From 527eccdfef05d98f519acab5f62169242ac1731c Mon Sep 17 00:00:00 2001 From: saichint Date: Wed, 1 Jun 2016 12:31:41 -0700 Subject: [PATCH 011/203] cisco_bfd_global: new properties (#334) * manifest * type file add * provider file * array types * beaker * add loopback int * remove name * fix manifest * documentation * comment * Review comments * Fix README * Fix README --- CHANGELOG.md | 2 + README.md | 91 ++++++++ examples/cisco/demo_bfd.pp | 101 +++++++++ examples/demo_all_cisco.pp | 1 + lib/puppet/provider/cisco_bfd_global/cisco.rb | 143 +++++++++++++ lib/puppet/type/cisco_bfd_global.rb | 195 ++++++++++++++++++ .../cisco_bfd_global/test_bfd_global.rb | 174 ++++++++++++++++ 7 files changed, 707 insertions(+) create mode 100644 examples/cisco/demo_bfd.pp create mode 100644 lib/puppet/provider/cisco_bfd_global/cisco.rb create mode 100644 lib/puppet/type/cisco_bfd_global.rb create mode 100644 tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 82a57b36b..392cb5f49 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### New feature support +#### Cisco Resources +- `cisco_bfd_global` type and provider. ### Added diff --git a/README.md b/README.md index 700195a7e..d14d4def7 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ A note about support for specific platform models: | [cisco_acl](#type-cisco_acl) | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_ace](#type-cisco_ace) | ✅ | ✅ | ✅* | ✅* | ✅* | \*[caveats](#cisco_ace-caveats) | | [cisco_command_config](#type-cisco_command_config) | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_bfd_global](#type-cisco_bfd_global) | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_bfd_global-caveats) | | [cisco_bgp](#type-cisco_bgp) | ✅ | ✅ | ✅* | ✅* | ✅* | \*[caveats](#cisco_bgp-caveats) | | [cisco_bgp_af](#type-cisco_bgp_af) | ✅* | ✅* | ✅ | ✅* | ✅ | \*[caveats](#cisco_bgp_af-caveats) | | [cisco_bgp_neighbor](#type-cisco_bgp_neighbor) | ✅ | ✅ | ✅ | ✅ | ✅ | @@ -232,6 +233,9 @@ The following resources include cisco types and providers along with cisco provi * [`cisco_ace`](#type-cisco_ace) * [`cisco_acl`](#type-cisco_acl) +* BFD Types + * [`cisco_bfd_global`](#type-cisco_bfd_global) + * BGP Types * [`cisco_vrf`](#type-cisco_vrf) * [`cisco_vrf_af`](#type-cisco_vrf_af) @@ -354,6 +358,7 @@ The following resources include cisco types and providers along with cisco provi * [`cisco_aaa_group_tacacs`](#type-cisco_aaa_group_tacacs) * [`cisco_acl`](#type-cisco_acl) * [`cisco_ace`](#type-cisco_ace) +* [`cisco_bfd_global`](#type-cisco_bfd_global) * [`cisco_bgp`](#type-cisco_bgp) * [`cisco_bgp_af`](#type-cisco_bgp_af) * [`cisco_bgp_neighbor`](#type-cisco_bgp_neighbor) @@ -882,6 +887,92 @@ Allows matching based on Time-To-Live (TTL) value. Valid values are type Integer |:-- | `ttl => '128'` +-- +### Type: cisco_bfd_global + +Manages configuration of a BFD (Bidirectional Forwarding Detection) instance. + +| Platform | OS Minimum Version | Module Minimum Version | +|----------|:------------------:|:----------------------:| +| N9k | 7.0(3)I3(1) | 1.4.0 | +| N3k | 7.0(3)I3(1) | 1.4.0 | +| N5k | 7.3(0)N1(1) | 1.4.0 | +| N6k | 7.3(0)N1(1) | 1.4.0 | +| N7k | 7.3(0)D1(1) | 1.4.0 | +| N8k | 7.3(0)F1(1) | 1.4.0 | + +####
Caveats + +| Property | Caveat Description | +|:--------|:-------------| +| `echo_rx_interval` | Not supported on N5k, N6k | +| `fabricpath_interval` | Not supported on N3k, N8k, N9k | +| `fabricpath_slow_timer` | Not supported on N3k, N8k, N9k | +| `fabricpath_vlan` | Not supported on N3k, N8k, N9k | +| `interval` | Not supported on N8k, N9k | +| `ipv4_echo_rx_interval` | Not supported on N5k, N6k | +| `ipv4_interval` | Not supported on N5k, N6k | +| `ipv4_slow_timer` | Not supported on N5k, N6k | +| `ipv6_echo_rx_interval` | Not supported on N5k, N6k | +| `ipv6_interval` | Not supported on N5k, N6k | +| `ipv6_slow_timer` | Not supported on N5k, N6k | +| `startup_timer` | Not supported on N5k, N6k, N7k | + +#### Parameters + +##### `ensure` +Determines whether the config should be present or not on the device. Valid values are 'present' and 'absent'. + +##### `echo_interface` +Loopback interface used for echo frames. Valid values are String, and 'default'. + +##### `echo_rx_interval` +Echo receive interval in milliseconds. Valid values are integer, and 'default'. + +##### `fabricpath_interval` +BFD fabricpath interval. Valid values are an array of [fabricpath_interval, fabricpath_min_rx, fabricpath_multiplier] or 'default'. + +Example: `fabricpath_interval => [100, 120, 4]` + +##### `fabricpath_slow_timer` +BFD fabricpath slow rate timer in milliseconds. Valid values are integer, and 'default'. + +##### `fabricpath_vlan` +BFD fabricpath control vlan. Valid values are integer, and 'default'. + +##### `interval` +BFD interval. Valid values are an array of [interval, min_rx, multiplier] or 'default'. + +Example: `interval => [100, 120, 4]` + +##### `ipv4_echo_rx_interval` +IPv4 session echo receive interval in milliseconds. Valid values are integer, and 'default'. + +##### `ipv4_interval` +BFD IPv4 session interval. Valid values are an array of [ipv4_interval, ipv4_min_rx, ipv4_multiplier] or 'default'. + +Example: `ipv4_interval => [100, 120, 4]` + +##### `ipv4_slow_timer` +BFD IPv4 session slow rate timer in milliseconds. Valid values are integer, and 'default'. + +##### `ipv6_echo_rx_interval` +IPv6 session echo receive interval in milliseconds. Valid values are integer, and 'default'. + +##### `ipv6_interval` +BFD IPv6 session interval. Valid values are an array of [ipv6_interval, ipv6_min_rx, ipv6_multiplier] or 'default'. + +Example: `ipv6_interval => [100, 120, 4]` + +##### `ipv6_slow_timer` +BFD IPv6 session slow rate timer in milliseconds. Valid values are integer, and 'default'. + +##### `slow_timer` +BFD slow rate timer in milliseconds. Valid values are integer, and 'default'. + +##### `startup_timer` +BFD delayed startup timer in seconds. Valid values are integer, and 'default'. + -- ### Type: cisco_bgp diff --git a/examples/cisco/demo_bfd.pp b/examples/cisco/demo_bfd.pp new file mode 100644 index 000000000..a57ba85c5 --- /dev/null +++ b/examples/cisco/demo_bfd.pp @@ -0,0 +1,101 @@ +# Manifest to demo cisco_interface provider +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +class ciscopuppet::cisco::demo_bfd { + + $echo_rx_interval = platform_get() ? { + /(n3k|n7k|n8k|n9k)/ => 300, + default => undef + } + + $fabricpath_interval = platform_get() ? { + /(n5k|n6k|n7k)/ => ['750', '350', '35'], + default => undef + } + + $fabricpath_slow_timer = platform_get() ? { + /(n5k|n6k|n7k)/ => 15000, + default => undef + } + + $fabricpath_vlan = platform_get() ? { + /(n5k|n6k|n7k)/ => 100, + default => undef + } + + # TBD: this is due to a bug on n8k and n9k + $interval = platform_get() ? { + /(n3k|n5k|n6k|n7k)/ => ['100', '100', '25'], + default => undef + } + + $ipv4_echo_rx_interval = platform_get() ? { + /(n3k|n7k|n8k|n9k)/ => 100, + default => undef + } + + $ipv4_interval = platform_get() ? { + /(n3k|n7k|n8k|n9k)/ => ['200', '200', '50'], + default => undef + } + + $ipv4_slow_timer = platform_get() ? { + /(n3k|n7k|n8k|n9k)/ => 10000, + default => undef + } + + $ipv6_echo_rx_interval = platform_get() ? { + /(n3k|n7k|n8k|n9k)/ => 200, + default => undef + } + + $ipv6_interval = platform_get() ? { + /(n3k|n7k|n8k|n9k)/ => ['500', '500', '30'], + default => undef + } + + $ipv6_slow_timer = platform_get() ? { + /(n3k|n7k|n8k|n9k)/ => 25000, + default => undef + } + + $startup_timer = platform_get() ? { + /(n3k|n8k|n9k)/ => 25, + default => undef + } + + cisco_command_config { 'loopback': + command => 'interface loopback10', + } + + cisco_bfd_global { 'default': + ensure => 'present', + echo_interface => 'loopback10', + echo_rx_interval => $echo_rx_interval, + fabricpath_interval => $fabricpath_interval, + fabricpath_slow_timer => $fabricpath_slow_timer, + fabricpath_vlan => $fabricpath_vlan, + interval => $interval, + ipv4_echo_rx_interval => $ipv4_echo_rx_interval, + ipv4_interval => $ipv4_interval, + ipv4_slow_timer => $ipv4_slow_timer, + ipv6_echo_rx_interval => $ipv6_echo_rx_interval, + ipv6_interval => $ipv6_interval, + ipv6_slow_timer => $ipv6_slow_timer, + slow_timer => 5000, + startup_timer => $startup_timer, + } +} diff --git a/examples/demo_all_cisco.pp b/examples/demo_all_cisco.pp index ec6c079eb..eaf033008 100644 --- a/examples/demo_all_cisco.pp +++ b/examples/demo_all_cisco.pp @@ -28,6 +28,7 @@ include ciscopuppet::cisco::demo_aaa include ciscopuppet::cisco::demo_acl + include ciscopuppet::cisco::demo_bfd include ciscopuppet::cisco::demo_bgp include ciscopuppet::cisco::demo_command_config include ciscopuppet::cisco::demo_evpn diff --git a/lib/puppet/provider/cisco_bfd_global/cisco.rb b/lib/puppet/provider/cisco_bfd_global/cisco.rb new file mode 100644 index 000000000..06face201 --- /dev/null +++ b/lib/puppet/provider/cisco_bfd_global/cisco.rb @@ -0,0 +1,143 @@ +# The Cisco provider for cisco_bfd_global +# +# May, 2016 +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'cisco_node_utils' if Puppet.features.cisco_node_utils? +begin + require 'puppet_x/cisco/autogen' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'autogen.rb')) +end + +Puppet::Type.type(:cisco_bfd_global).provide(:cisco) do + desc 'The Cisco bfd_global provider.' + + confine feature: :cisco_node_utils + defaultfor operatingsystem: :nexus + + mk_resource_methods + + BFD_GLOBAL_NON_BOOL_PROPS = [ + :echo_interface, + :echo_rx_interval, + :fabricpath_slow_timer, + :fabricpath_vlan, + :ipv4_echo_rx_interval, + :ipv4_slow_timer, + :ipv6_echo_rx_interval, + :ipv6_slow_timer, + :slow_timer, + :startup_timer, + ] + + BFD_GLOBAL_ARRAY_FLAT_PROPS = [ + :fabricpath_interval, + :interval, + :ipv4_interval, + :ipv6_interval, + ] + + BFD_GLOBAL_ALL_PROPS = BFD_GLOBAL_ARRAY_FLAT_PROPS + BFD_GLOBAL_NON_BOOL_PROPS + + PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@nu', + BFD_GLOBAL_NON_BOOL_PROPS) + PuppetX::Cisco::AutoGen.mk_puppet_methods(:array_flat, self, '@nu', + BFD_GLOBAL_ARRAY_FLAT_PROPS) + + def initialize(value={}) + super(value) + @nu = Cisco::BfdGlobal.new + @property_flush = {} + end + + def self.properties_get(nu_obj) + current_state = { + name: 'default', + ensure: :present, + } + + # Call node_utils getter for each property + BFD_GLOBAL_NON_BOOL_PROPS.each do |prop| + current_state[prop] = nu_obj.send(prop) + end + BFD_GLOBAL_ARRAY_FLAT_PROPS.each do |prop| + current_state[prop] = nu_obj.send(prop) + end + # nested array properties + current_state[:fabricpath_interval] = nu_obj.fabricpath_interval + current_state[:interval] = nu_obj.interval + current_state[:ipv4_interval] = nu_obj.ipv4_interval + current_state[:ipv6_interval] = nu_obj.ipv6_interval + new(current_state) + end # self.properties_get + + def self.instances + globals = [] + return globals unless Cisco::Feature.bfd_enabled? + bfd = Cisco::BfdGlobal.new + globals << properties_get(bfd) + globals + end + + def self.prefetch(resources) + resources.values.first.provider = instances.first unless instances.first.nil? + end # self.prefetch + + def exists? + (@property_hash[:ensure] == :present) + end + + def create + @property_flush[:ensure] = :present + end + + def destroy + @property_flush[:ensure] = :absent + end + + def instance_name + name + end + + def properties_set(new_global=false) + BFD_GLOBAL_ALL_PROPS.each do |prop| + next unless @resource[prop] + send("#{prop}=", @resource[prop]) if new_global + unless @property_flush[prop].nil? + @nu.send("#{prop}=", @property_flush[prop]) if + @nu.respond_to?("#{prop}=") + end + end + end + + def flush + if @property_flush[:ensure] == :absent + @nu.destroy + @nu = nil + else + # Create/Update + new_global = false + if @nu.nil? + new_global = true + @nu = Cisco::BfdGlobal.new + end + properties_set(new_global) + end + end +end # Puppet::Type diff --git a/lib/puppet/type/cisco_bfd_global.rb b/lib/puppet/type/cisco_bfd_global.rb new file mode 100644 index 000000000..79a058ff8 --- /dev/null +++ b/lib/puppet/type/cisco_bfd_global.rb @@ -0,0 +1,195 @@ +# Manages the Cisco Bfd Global configuration resource. +# +# May 2016 +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Puppet::Type.newtype(:cisco_bfd_global) do + @doc = " + Manages the Cisco Bfd Global configuration resource. + cisco_bfd_global {'default': + ..attributes.. + } + 'default' is only acceptable name for this global config object. + Example: + cisco_bfd_global { 'default': + ensure => 'present', + echo_interface => 'loopback10', + echo_rx_interval => 300, + fabricpath_interval => ['750', '350', '45'], + fabricpath_slow_timer => 15000, + fabricpath_vlan => 100, + interval => ['100', '100', '25'], + ipv4_echo_rx_interval => 100, + ipv4_interval => ['200', '200', '50'], + ipv4_slow_timer => 10000, + ipv6_echo_rx_interval => 200, + ipv6_interval => ['500', '500', '30'], + ipv6_slow_timer => 25000, + slow_timer => 5000, + startup_timer => 25, + } + " + + ensurable + + ################### + # Resource Naming # + ################### + + newparam(:name, namevar: :true) do + inputs = "The name of the bfd_global instance. Valid values are 'default' only" + desc inputs + + validate { |name| error inputs unless name == 'default' } + end # param id + + ############## + # Attributes # + ############## + + newproperty(:echo_interface) do + desc "Loopback interface used for echo frames. Valid values are + string (e.g. 'loopback42'), or keyword 'default'." + + munge do |value| + value = :default if value == 'default' + value + end + end # property echo_interface + + newproperty(:echo_rx_interval) do + desc "Echo receive interval in msec. Valid values are + integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property echo_rx_interval + + newproperty(:fabricpath_interval, array_matching: :all) do + desc "Valid values are an array of [fabricpath_interval, fabricpath_min_rx, fabricpath_multiplier] + or keyword 'default'" + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge { |value| value == 'default' ? :default : value } + end # property fabricpath_interval + + newproperty(:fabricpath_slow_timer) do + desc "Fabricpath slow rate timer in msec. Valid values are + integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property fabricpath_slow_timer + + newproperty(:fabricpath_vlan) do + desc "Fabricpath control vlan. Valid values are + integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property fabricpath_vlan + + newproperty(:interval, array_matching: :all) do + desc "Valid values are an array of [interval, min_rx, multiplier] + or keyword 'default'" + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge { |value| value == 'default' ? :default : value } + end # property interval + + newproperty(:ipv4_echo_rx_interval) do + desc "Ipv4 session echo receive interval in msec. Valid values are + integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property ipv4_echo_rx_interval + + newproperty(:ipv4_interval, array_matching: :all) do + desc "Valid values are an array of [ipv4_interval, ipv4_min_rx, ipv4_multiplier] + or keyword 'default'" + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge { |value| value == 'default' ? :default : value } + end # property ipv4_interval + + newproperty(:ipv4_slow_timer) do + desc "Ipv4 session slow rate timer in msec. Valid values are + integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property ipv4_slow_timer + + newproperty(:ipv6_echo_rx_interval) do + desc "Ipv6 session echo receive interval in msec. Valid values are + integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property ipv6_echo_rx_interval + + newproperty(:ipv6_interval, array_matching: :all) do + desc "Valid values are an array of [ipv6_interval, ipv6_min_rx, ipv6_multiplier] + or keyword 'default'" + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge { |value| value == 'default' ? :default : value } + end # property ipv6_interval + + newproperty(:ipv6_slow_timer) do + desc "Ipv6 session slow rate timer in msec. Valid values are + integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property ipv6_slow_timer + + newproperty(:slow_timer) do + desc "Slow rate timer in msec. Valid values are + integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property slow_timer + + newproperty(:startup_timer) do + desc "Delayed startup timer in sec. Valid values are + integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property startup_timer +end diff --git a/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb b/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb new file mode 100644 index 000000000..1132ceba8 --- /dev/null +++ b/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb @@ -0,0 +1,174 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### + +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# Test hash top-level keys +tests = { + master: master, + agent: agent, + operating_system: 'nexus', + resource_name: 'cisco_bfd_global', +} + +default_interval_plat_1 = %w(50 50 3) +default_interval_plat_2 = %w(250 250 3) +non_default_interval = %w(100 100 25) +non_default_ipv4_interval = %w(200 200 50) +non_default_ipv6_interval = %w(500 500 30) +non_default_fabricpath_interval = %w(750 350 35) + +# Test hash test cases +tests[:default] = { + desc: '1.1 Defaults', + title_pattern: 'default', + preclean: 'cisco_bfd_global', + manifest_props: { + echo_interface: 'default', + echo_rx_interval: 'default', + fabricpath_interval: 'default', + fabricpath_slow_timer: 'default', + fabricpath_vlan: 'default', + interval: 'default', + ipv4_echo_rx_interval: 'default', + ipv4_interval: 'default', + ipv4_slow_timer: 'default', + ipv6_echo_rx_interval: 'default', + ipv6_interval: 'default', + ipv6_slow_timer: 'default', + slow_timer: 'default', + startup_timer: 'default', + }, + code: [0, 2], + resource: { + echo_interface: 'false', + echo_rx_interval: 50, + fabricpath_interval: default_interval_plat_1, + fabricpath_slow_timer: 2000, + fabricpath_vlan: 1, + interval: default_interval_plat_1, + ipv4_echo_rx_interval: 50, + ipv4_interval: default_interval_plat_1, + ipv4_slow_timer: 2000, + ipv6_echo_rx_interval: 50, + ipv6_interval: default_interval_plat_1, + ipv6_slow_timer: 2000, + slow_timer: 2000, + startup_timer: 5, + }, +} + +# Per platform default values +resource = { + n3k: { + echo_rx_interval: 250, + interval: default_interval_plat_2, + ipv4_echo_rx_interval: 250, + ipv4_interval: default_interval_plat_2, + ipv6_echo_rx_interval: 250, + ipv6_interval: default_interval_plat_2, + } +} + +tests[:default][:resource].merge!(resource[:n3k]) if platform[/n3k/] + +# Non-default Tests. NOTE: [:resource] = [:manifest_props] for all non-default + +tests[:non_default] = { + desc: '2.1 Non Defaults', + title_pattern: 'default', + preclean: 'cisco_bfd_global', + manifest_props: { + echo_interface: 'loopback10', + echo_rx_interval: 300, + fabricpath_interval: non_default_fabricpath_interval, + fabricpath_slow_timer: 15_000, + fabricpath_vlan: 100, + interval: non_default_interval, + ipv4_echo_rx_interval: 100, + ipv4_interval: non_default_ipv4_interval, + ipv4_slow_timer: 10_000, + ipv6_echo_rx_interval: 200, + ipv6_interval: non_default_ipv6_interval, + ipv6_slow_timer: 25_000, + slow_timer: 5000, + startup_timer: 25, + }, +} + +def unsupported_properties(_tests, _id) + unprops = [] + if platform[/n(3|8|9)k/] + unprops << + :fabricpath_interval << + :fabricpath_slow_timer << + :fabricpath_vlan + + elsif platform[/n(5|6)k/] + unprops << + :echo_rx_interval << + :ipv4_echo_rx_interval << + :ipv4_interval << + :ipv4_slow_timer << + :ipv6_echo_rx_interval << + :ipv6_interval << + :ipv6_slow_timer << + :startup_timer + + elsif platform[/n7k/] + unprops << + :startup_timer + end + + # TBD: this is due to nxos bug on n8k and n9k + unprops << :interval if platform[/n(8|9)k/] + + unprops +end + +# Overridden to properly handle dependencies for this test file. +def test_harness_dependencies(_tests, id) + return unless id[/non_default/] + cmd = 'interface loopback 10' + command_config(agent, cmd, cmd) +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + # ------------------------------------------------------------------- + device = platform + logger.info("#### This device is of type: #{device} #####") + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + + test_harness_run(tests, :default) + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + + test_harness_run(tests, :non_default) + resource_absent_cleanup(agent, 'cisco_bfd_global') +end + +logger.info("TestCase :: #{tests[:resource_name]} :: End") From ac0a0ea15a8a35b02fee6501df8c4ae1db8ace0f Mon Sep 17 00:00:00 2001 From: tphoney Date: Fri, 3 Jun 2016 17:23:40 +0100 Subject: [PATCH 012/203] network_snmp ios_xr does not support global enable --- lib/puppet/provider/network_snmp/cisco.rb | 7 ++++++- .../network_snmp/network_snmp_provider_defaults.rb | 9 +++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/puppet/provider/network_snmp/cisco.rb b/lib/puppet/provider/network_snmp/cisco.rb index b7ed4aa67..16379b7f9 100644 --- a/lib/puppet/provider/network_snmp/cisco.rb +++ b/lib/puppet/provider/network_snmp/cisco.rb @@ -54,6 +54,9 @@ def self.properties_get location: network_snmp.location.empty? ? 'unset' : network_snmp.location, } + if Facter.value('operatingsystem').eql?('ios_xr') + current_state[:enable] = true + end new(current_state) end # self.properties_get @@ -94,7 +97,9 @@ def validate def flush validate - + if Facter.value('operatingsystem').eql?('ios_xr') + NETWORK_SNMP_PROPS.delete(:enable) + end NETWORK_SNMP_PROPS.each do |puppet_prop, cisco_prop| if @resource[puppet_prop] @network_snmp.send("#{cisco_prop}=", munge_flush(@resource[puppet_prop])) \ diff --git a/tests/beaker_tests/network_snmp/network_snmp_provider_defaults.rb b/tests/beaker_tests/network_snmp/network_snmp_provider_defaults.rb index 8aeede2bc..44e5953ff 100644 --- a/tests/beaker_tests/network_snmp/network_snmp_provider_defaults.rb +++ b/tests/beaker_tests/network_snmp/network_snmp_provider_defaults.rb @@ -118,8 +118,13 @@ # Flag is set to false to check for presence of RegExp pattern in stdout. cmd_str = PUPPET_BINPATH + 'resource network_snmp default' on(agent, cmd_str) do - search_pattern_in_output(stdout, { 'enable' => 'false' }, - false, self, logger) + if operating_system == 'ios_xr' + search_pattern_in_output(stdout, { 'enable' => 'true' }, + false, self, logger) + else + search_pattern_in_output(stdout, { 'enable' => 'false' }, + false, self, logger) + end search_pattern_in_output(stdout, { 'contact' => 'unset' }, false, self, logger) search_pattern_in_output(stdout, { 'location' => 'unset' }, From 969aa5bcabe9b3af10deac4258765749ed0b2d43 Mon Sep 17 00:00:00 2001 From: saichint Date: Fri, 3 Jun 2016 12:13:23 -0700 Subject: [PATCH 013/203] bgp_neighbor: add bfd (#340) * manifest * type file add * provider file * array types * beaker * add loopback int * remove name * fix manifest * documentation * comment * Review comments * Fix README * Fix README * add bfd to bgp_neighbor * bfd prop * documentation --- CHANGELOG.md | 1 + README.md | 7 ++++++- examples/cisco/demo_bgp.pp | 6 ++++++ lib/puppet/provider/cisco_bgp_neighbor/cisco.rb | 1 + lib/puppet/type/cisco_bgp_neighbor.rb | 7 +++++++ tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb | 4 ++++ 6 files changed, 25 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 392cb5f49..94913b32f 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `cisco_bfd_global` type and provider. ### Added +- Extended `cisco_bgp_neighbor` to include bfd support ### Removed diff --git a/README.md b/README.md index d14d4def7..bac734129 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ A note about support for specific platform models: | [cisco_bfd_global](#type-cisco_bfd_global) | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_bfd_global-caveats) | | [cisco_bgp](#type-cisco_bgp) | ✅ | ✅ | ✅* | ✅* | ✅* | \*[caveats](#cisco_bgp-caveats) | | [cisco_bgp_af](#type-cisco_bgp_af) | ✅* | ✅* | ✅ | ✅* | ✅ | \*[caveats](#cisco_bgp_af-caveats) | -| [cisco_bgp_neighbor](#type-cisco_bgp_neighbor) | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_bgp_neighbor](#type-cisco_bgp_neighbor) | ✅ | ✅ | ✅* | ✅* | ✅ |\*[caveats](#cisco_bgp_neighbor-caveats) | [cisco_bgp_neighbor_af](#type-cisco_bgp_neighbor_af) | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_bridge_domain](#type-cisco_bridge_domain) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | | [cisco_bridge_domain_vni](#type-cisco_bridge_domain_vni) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | @@ -1321,12 +1321,14 @@ Manages configuration of a BGP Neighbor. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.3(0)F1(1) | 1.3.2 | #### Caveats | Property | Caveat Description | |:--------|:-------------| | `log_neighbor_changes` | Not supported on N5k, N6k, N7k | +| `bfd` | Not supported on N5k, N6k for IPv6 peers (support added in ciscopuppet 1.4.0) | #### Parameters @@ -1347,6 +1349,9 @@ Neighbor Identifier. Required. Valid values are string. Neighbors may use IPv4 o ##### `description` Description of the neighbor. Valid value is string. +##### `bfd` +Configure whether or not to enable bidirectional forwarding detection. Valid values are true, false and keyword 'default'. + ##### `connected_check` Configure whether or not to check for directly connected peer. Valid values are true and false. diff --git a/examples/cisco/demo_bgp.pp b/examples/cisco/demo_bgp.pp index 5d92298ee..42e11428b 100644 --- a/examples/cisco/demo_bgp.pp +++ b/examples/cisco/demo_bgp.pp @@ -276,6 +276,10 @@ # Configure BGP IPv4 Neighbors #---------------------------------------------------------------------------# + $bfd = $operatingsystem ? { + 'nexus' => true, + default => undef + } $capability_negotiation = $operatingsystem ? { 'nexus' => true, default => undef @@ -303,6 +307,7 @@ #Properties description => 'my description', + bfd => $bfd, connected_check => true, capability_negotiation => $capability_negotiation, dynamic_capability => $dynamic_capability, @@ -325,6 +330,7 @@ #Properties description => 'my description', + bfd => $bfd, connected_check => true, capability_negotiation => $capability_negotiation, dynamic_capability => $dynamic_capability, diff --git a/lib/puppet/provider/cisco_bgp_neighbor/cisco.rb b/lib/puppet/provider/cisco_bgp_neighbor/cisco.rb index 8e894ec5d..d64f6b538 100644 --- a/lib/puppet/provider/cisco_bgp_neighbor/cisco.rb +++ b/lib/puppet/provider/cisco_bgp_neighbor/cisco.rb @@ -50,6 +50,7 @@ :update_source, ] BGP_NBR_BOOL_PROPS = [ + :bfd, :connected_check, :capability_negotiation, :dynamic_capability, diff --git a/lib/puppet/type/cisco_bgp_neighbor.rb b/lib/puppet/type/cisco_bgp_neighbor.rb index dfe4395a9..1e26744ca 100644 --- a/lib/puppet/type/cisco_bgp_neighbor.rb +++ b/lib/puppet/type/cisco_bgp_neighbor.rb @@ -45,6 +45,7 @@ vrf => 'default', neighbor => '10.1.1.1', description => 'my descritpion', + bfd => true, connected_check => true, capability_negotiation => true, dynamic_capability => true, @@ -186,6 +187,12 @@ def name desc 'Description of the neighbor. Valid value is string.' end + newproperty(:bfd) do + desc "Enable bidirectional forwarding detection capability. Valid + values are true, false or default" + newvalues(:true, :false, :default) + end + newproperty(:connected_check) do desc "Configure whether or not to check for directly connected peer. Valid values are true or false" diff --git a/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb b/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb index 2b24b386d..f180827ff 100644 --- a/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb +++ b/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb @@ -37,6 +37,7 @@ title_pattern: '2 default 1.1.1.1', preclean: 'cisco_bgp', manifest_props: { + bfd: 'default', ebgp_multihop: 'default', local_as: 'default', low_memory_exempt: 'default', @@ -46,6 +47,7 @@ timers_holdtime: 'default', }, resource: { + 'bfd' => 'false', 'ebgp_multihop' => 'false', 'local_as' => '0', 'log_neighbor_changes' => 'inherit', @@ -65,6 +67,7 @@ title_pattern: '2 default 1.1.1.1', manifest_props: { description: 'tested by beaker', + bfd: 'true', connected_check: 'true', capability_negotiation: 'true', dynamic_capability: 'true', @@ -120,6 +123,7 @@ def unsupported_properties(_tests, _id) if operating_system == 'ios_xr' # IOS-XR does not support these properties unprops << + :bfd << :capability_negotiation << :dynamic_capability << :log_neighbor_changes << From 4be51d3041245956d916350e3c9cd4b79e54a1b4 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Fri, 3 Jun 2016 15:20:08 -0400 Subject: [PATCH 014/203] Fix BFD caveat --- README.md | 163 +++++++++++++++++++++++++++--------------------------- 1 file changed, 82 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index bac734129..02d44fc1a 100644 --- a/README.md +++ b/README.md @@ -66,10 +66,10 @@ The Puppet Agent requires installation and setup on each device. Agent setup can ### `cisco_node_utils` Ruby gem -This module has dependencies on the [`cisco_node_utils`](https://rubygems.org/gems/cisco_node_utils) ruby gem. After installing the Puppet Agent software, use Puppet's built-in [`Package`](https://github.com/cisco/cisco-network-puppet-module/blob/master/examples/install.pp#L17) provider to install the gem. - -A helper class [`ciscopuppet::install`](https://github.com/cisco/cisco-network-puppet-module/blob/master/examples/demo_all_cisco.pp#L19) is provided in the examples subdirectory of this module. Simply add an `include ciscopuppet::install` statement at the beginning of the manifest to install the latest `cisco_node_utils` gem from rubygems.org. Including the aforementioned class with [`additional parameters`](https://github.com/cisco/cisco-network-puppet-module/blob/master/examples/demo_all_cisco.pp#L24) overrides the default rubygems.org repository with a custom repository. - +This module has dependencies on the [`cisco_node_utils`](https://rubygems.org/gems/cisco_node_utils) ruby gem. After installing the Puppet Agent software, use Puppet's built-in [`Package`](https://github.com/cisco/cisco-network-puppet-module/blob/master/examples/install.pp#L17) provider to install the gem. + +A helper class [`ciscopuppet::install`](https://github.com/cisco/cisco-network-puppet-module/blob/master/examples/demo_all_cisco.pp#L19) is provided in the examples subdirectory of this module. Simply add an `include ciscopuppet::install` statement at the beginning of the manifest to install the latest `cisco_node_utils` gem from rubygems.org. Including the aforementioned class with [`additional parameters`](https://github.com/cisco/cisco-network-puppet-module/blob/master/examples/demo_all_cisco.pp#L24) overrides the default rubygems.org repository with a custom repository. + For Puppet Agents running within the GuestShell or OAC environment, the installed GEM remains persistent across system reloads, however, agents running in the NX-OS bash-shell environment will automatically download and reinstall the GEM after a system reload. ## Usage @@ -1328,7 +1328,8 @@ Manages configuration of a BGP Neighbor. | Property | Caveat Description | |:--------|:-------------| | `log_neighbor_changes` | Not supported on N5k, N6k, N7k | -| `bfd` | Not supported on N5k, N6k for IPv6 peers (support added in ciscopuppet 1.4.0) | +| `bfd` | (ciscopuppet v1.4.0) BFD support added for all platforms | +| `bfd` on IPv6 | Not supported on N5k, N6k | #### Parameters @@ -1350,7 +1351,7 @@ Neighbor Identifier. Required. Valid values are string. Neighbors may use IPv4 o Description of the neighbor. Valid value is string. ##### `bfd` -Configure whether or not to enable bidirectional forwarding detection. Valid values are true, false and keyword 'default'. +Enable Bidirectional Forwarding Detection (BFD). Valid values are true, false and keyword 'default'. ##### `connected_check` Configure whether or not to check for directly connected peer. Valid values are true and false. @@ -1784,20 +1785,20 @@ Manages a Cisco Network Interface. Any resource dependency should be run before | Property | Caveat Description | |:---------|:-------------| -| `pvlan_mapping` | Not supported on N8k | -| `switchport_pvlan_host` | Not supported on N8k | -| `switchport_pvlan_host_association | Not supported on N8k | -| `switchport_pvlan_mapping` | Not supported on N8k | -| `switchport_pvlan_mapping_trunk` | Not supported on N3k,N8k | -| `switchport_pvlan_promiscuous` | Not supported on N8k | -| `switchport_pvlan_trunk_allowed_vlan` | Not supported on N8k | -| `switchport_pvlan_trunk_association` | Not supported on N3k,N8k | -| `switchport_pvlan_trunk_native_vlan` | Not supported on N8k | -| `switchport_pvlan_trunk_promiscuous` | Not supported on N3k,N8k | -| `switchport_pvlan_trunk_secondary` | Not supported on N3k,N8k | -| `svi_autostate` | Only supported on N3k,N7k,N9k | -| `vlan_mapping` | Only supported on N7k | -| `vlan_mapping_enable` | Only supported on N7k | +| `pvlan_mapping` | Not supported on N8k | +| `switchport_pvlan_host` | Not supported on N8k | +| `switchport_pvlan_host_association | Not supported on N8k | +| `switchport_pvlan_mapping` | Not supported on N8k | +| `switchport_pvlan_mapping_trunk` | Not supported on N3k,N8k | +| `switchport_pvlan_promiscuous` | Not supported on N8k | +| `switchport_pvlan_trunk_allowed_vlan` | Not supported on N8k | +| `switchport_pvlan_trunk_association` | Not supported on N3k,N8k | +| `switchport_pvlan_trunk_native_vlan` | Not supported on N8k | +| `switchport_pvlan_trunk_promiscuous` | Not supported on N3k,N8k | +| `switchport_pvlan_trunk_secondary` | Not supported on N3k,N8k | +| `svi_autostate` | Only supported on N3k,N7k,N9k | +| `vlan_mapping` | Only supported on N7k | +| `vlan_mapping_enable` | Only supported on N7k | #### Parameters @@ -1844,67 +1845,67 @@ interface. Valid value is an integer. ##### `switchport_autostate_exclude` Exclude this port for the SVI link calculation. Valid values are 'true', 'false', and 'default'. -##### `pvlan_mapping` -Maps secondary VLANs to the VLAN interface of a primary VLAN. Valid inputs are a String containing a range of secondary vlans or keyword 'default'. - -Example: `pvlan_mapping => '3-4,6'` - -##### `switchport_pvlan_host` -Configures a Layer 2 interface as a private VLAN host port. Valid values are 'true', 'false', and 'default' - -##### `switchport_pvlan_host_association` -Associates the Layer 2 host port with the primary and secondary VLANs of a private VLAN. Valid inputs are: An array containing the primary and secondary vlans, or keyword 'default'. - -Example: `switchport_pvlan_host_association => ['44', '144']` - -##### `switchport_pvlan_mapping` -Associates the specified port with a primary VLAN and a selected list of secondary VLANs. Valid inputs are an array containing both the primary vlan and a range of secondary vlans, or keyword 'default'. - -Example: `switchport_pvlan_mapping => ['44', '3-4,6']` - -##### `switchport_pvlan_mapping_trunk` -Maps the promiscuous trunk port with the primary VLAN and a selected list of associated secondary VLANs. Valid inputs are: An array containing both the primary vlan and a range of secondary vlans, a nested array if there are multiple mappings, or keyword 'default'. - -Examples: - -``` - switchport_pvlan_mapping_trunk => [['44', '3-4,6'], ['99', '199']] - - -or- - - switchport_pvlan_mapping_trunk => ['44', '3-4,6'] -``` - -##### `switchport_pvlan_trunk_allowed_vlan` -Sets the allowed VLANs for the private VLAN isolated trunk interface. Valid values are a String range of vlans or keyword 'default'. - -Example: `switchport_pvlan_trunk_allowed_vlan => '3-4,6'` - -##### `switchport_pvlan_trunk_association` -Associates the Layer 2 isolated trunk port with the primary and secondary VLANs of private VLANs. Valid inputs are: An array containing an association of primary and secondary vlans, a nested array if there are multiple associations, or the keyword 'default'. - -Examples: - -``` -switchport_pvlan_trunk_association => [['44', '244'], ['45', '245']] - - -or- - -switchport_pvlan_trunk_association => ['44', '244'] -``` - -##### `switchport_pvlan_trunk_native_vlan` -Sets the native VLAN for the 802.1Q trunk. Valid values are Integer, String, or keyword 'default'. - -##### `switchport_pvlan_promiscuous` -Configures a Layer 2 interface as a private VLAN promiscuous port. Valid values are 'true', 'false', and 'default'. - -##### `switchport_pvlan_trunk_promiscuous` -Configures a Layer 2 interface as a private VLAN promiscuous trunk port. Valid values are 'true', 'false', and 'default'. - -##### `switchport_pvlan_trunk_secondary` -Configures a Layer 2 interface as a private VLAN isolated trunk port. Valid values are 'true', 'false', and 'default'. - +##### `pvlan_mapping` +Maps secondary VLANs to the VLAN interface of a primary VLAN. Valid inputs are a String containing a range of secondary vlans or keyword 'default'. + +Example: `pvlan_mapping => '3-4,6'` + +##### `switchport_pvlan_host` +Configures a Layer 2 interface as a private VLAN host port. Valid values are 'true', 'false', and 'default' + +##### `switchport_pvlan_host_association` +Associates the Layer 2 host port with the primary and secondary VLANs of a private VLAN. Valid inputs are: An array containing the primary and secondary vlans, or keyword 'default'. + +Example: `switchport_pvlan_host_association => ['44', '144']` + +##### `switchport_pvlan_mapping` +Associates the specified port with a primary VLAN and a selected list of secondary VLANs. Valid inputs are an array containing both the primary vlan and a range of secondary vlans, or keyword 'default'. + +Example: `switchport_pvlan_mapping => ['44', '3-4,6']` + +##### `switchport_pvlan_mapping_trunk` +Maps the promiscuous trunk port with the primary VLAN and a selected list of associated secondary VLANs. Valid inputs are: An array containing both the primary vlan and a range of secondary vlans, a nested array if there are multiple mappings, or keyword 'default'. + +Examples: + +``` + switchport_pvlan_mapping_trunk => [['44', '3-4,6'], ['99', '199']] + + -or- + + switchport_pvlan_mapping_trunk => ['44', '3-4,6'] +``` + +##### `switchport_pvlan_trunk_allowed_vlan` +Sets the allowed VLANs for the private VLAN isolated trunk interface. Valid values are a String range of vlans or keyword 'default'. + +Example: `switchport_pvlan_trunk_allowed_vlan => '3-4,6'` + +##### `switchport_pvlan_trunk_association` +Associates the Layer 2 isolated trunk port with the primary and secondary VLANs of private VLANs. Valid inputs are: An array containing an association of primary and secondary vlans, a nested array if there are multiple associations, or the keyword 'default'. + +Examples: + +``` +switchport_pvlan_trunk_association => [['44', '244'], ['45', '245']] + + -or- + +switchport_pvlan_trunk_association => ['44', '244'] +``` + +##### `switchport_pvlan_trunk_native_vlan` +Sets the native VLAN for the 802.1Q trunk. Valid values are Integer, String, or keyword 'default'. + +##### `switchport_pvlan_promiscuous` +Configures a Layer 2 interface as a private VLAN promiscuous port. Valid values are 'true', 'false', and 'default'. + +##### `switchport_pvlan_trunk_promiscuous` +Configures a Layer 2 interface as a private VLAN promiscuous trunk port. Valid values are 'true', 'false', and 'default'. + +##### `switchport_pvlan_trunk_secondary` +Configures a Layer 2 interface as a private VLAN isolated trunk port. Valid values are 'true', 'false', and 'default'. + ##### `switchport_trunk_allowed_vlan` The allowed VLANs for the specified Ethernet interface. Valid values are string, keyword 'default'. From c75775622ef4c7cc09775ebbc08029727d4db487 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Fri, 3 Jun 2016 15:22:50 -0400 Subject: [PATCH 015/203] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94913b32f..d277e99ad 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `cisco_bfd_global` type and provider. ### Added -- Extended `cisco_bgp_neighbor` to include bfd support +- Extend cisco_bgp_neighbor with attributes: + - `bfd` ### Removed From 3d9efa814dbe83746c8379a419bccb03d048c605 Mon Sep 17 00:00:00 2001 From: TP Honey Date: Wed, 8 Jun 2016 15:49:20 +0100 Subject: [PATCH 016/203] search_domain, tests platform agnostic (#343) --- .../search_domain_provider_defaults.rb | 40 ++++--------------- 1 file changed, 7 insertions(+), 33 deletions(-) diff --git a/tests/beaker_tests/search_domain/search_domain_provider_defaults.rb b/tests/beaker_tests/search_domain/search_domain_provider_defaults.rb index 945828773..3626d160f 100644 --- a/tests/beaker_tests/search_domain/search_domain_provider_defaults.rb +++ b/tests/beaker_tests/search_domain/search_domain_provider_defaults.rb @@ -65,9 +65,13 @@ def dns_clean(agent) # These resources currently do not support ensure=absent; they can use # resource_titles above if they're ever updated. - on(agent, get_vshell_cmd('show run | i domain-list|name-server')) - stdout.scan(/ip domain-list \S+|ip name-server .*/).each do |cli| - command_config(agent, "no #{cli}", "removing #{cli}") + if operating_system == 'ios_xr' + command_config(agent, 'no domain name', 'unsetting domain name') + else + on(agent, get_vshell_cmd('show run | i domain-list|name-server')) + stdout.scan(/ip domain-list \S+|ip name-server .*/).each do |cli| + command_config(agent, "no #{cli}", "removing #{cli}") + end end end @@ -103,21 +107,6 @@ def dns_clean(agent) logger.info("Check search_domain resource presence on agent :: #{result}") end - # @step [Step] Checks search_domain instance on agent using switch show cli - # cmds. - step 'TestStep :: Check search_domain instance presence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config all') - - on(agent, cmd_str) do - search_pattern_in_output(stdout, [/domain-name test\.xyz/], - false, self, logger) - end - - logger.info("Check search_domain instance presence on agent :: #{result}") - end - # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource absent manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. @@ -143,21 +132,6 @@ def dns_clean(agent) logger.info("Check search_domain resource absence on agent :: #{result}") end - # @step [Step] Checks search_domain instance on agent using switch show cli - # cmds. - step 'TestStep :: Check search_domain instance absence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config all') - - on(agent, cmd_str) do - search_pattern_in_output(stdout, [/domain-name test\.xyz/], - true, self, logger) - end - - logger.info("Check search_domain instance absence on agent :: #{result}") - end - step 'TestStep :: Testbed post-test cleanup' do dns_clean(agent) resource_absent_cleanup(agent, 'cisco_vrf') From 3a98d1cab827df1d9656dc9bc7ec395089ab9468 Mon Sep 17 00:00:00 2001 From: tphoney Date: Tue, 31 May 2016 16:05:24 +0100 Subject: [PATCH 017/203] modificatins for snmp_notification on IOS_XR --- .../provider/snmp_notification/cisco.rb | 3 ++ .../snmp_notification_provider_defaults.rb | 35 +++++++++------- .../netdev_stdlib/snmp_notificationlib.rb | 42 +++++++++++++++---- 3 files changed, 56 insertions(+), 24 deletions(-) diff --git a/lib/puppet/provider/snmp_notification/cisco.rb b/lib/puppet/provider/snmp_notification/cisco.rb index 6eef76701..cd70ab672 100644 --- a/lib/puppet/provider/snmp_notification/cisco.rb +++ b/lib/puppet/provider/snmp_notification/cisco.rb @@ -55,6 +55,9 @@ def self.prefetch(resources) end def flush + if @snmpnotification.nil? + @snmpnotification = Cisco::SnmpNotification.new(@resource[:name]) + end enable = @resource[:enable] == :true ? true : false @snmpnotification.enable = enable if @resource[:enable] end diff --git a/tests/beaker_tests/netdev_stdlib/snmp_notification_provider_defaults.rb b/tests/beaker_tests/netdev_stdlib/snmp_notification_provider_defaults.rb index 4dcd49f6b..9e0de5a0b 100644 --- a/tests/beaker_tests/netdev_stdlib/snmp_notification_provider_defaults.rb +++ b/tests/beaker_tests/netdev_stdlib/snmp_notification_provider_defaults.rb @@ -66,7 +66,7 @@ logger.info('Setup switch for provider') # Make sure radius server is not configured before test starts. - on(master, SnmpNotificationLib.create_absent) + on(master, SnmpNotificationLib.create_absent(operating_system)) # Expected exit_code is 0,2 since server may or may not be configured. cmd_str = PUPPET_BINPATH + 'agent -t' @@ -75,7 +75,7 @@ step 'TestStep :: Setup switch for provider test' do # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpNotificationLib.create_defaults) + on(master, SnmpNotificationLib.create_defaults(operating_system)) # Expected exit_code is 0 since this is a puppet agent cmd with no change. # Or expected exit_code is 2 since this is a puppet agent cmd with change. @@ -86,23 +86,24 @@ end # @step [Step] Checks snmp_notification resource on agent using resource cmd. - step 'TestStep :: Check snmp_notification resource absent on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource snmp_notification 'aaa server-state-change'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'enable' => 'false' }, - false, self, logger) + unless operating_system == 'ios_xr' + step 'TestStep :: Check snmp_notification resource absent on agent' do + # Expected exit_code is 0 since this is a puppet resource cmd. + # Flag is set to false to check for presence of RegExp pattern in stdout. + cmd_str = PUPPET_BINPATH + "resource snmp_notification 'aaa server-state-change'" + on(agent, cmd_str) do + search_pattern_in_output(stdout, + { 'enable' => 'false' }, + false, self, logger) + end + + logger.info("Check snmp_notification resource absent on agent :: #{result}") end - - logger.info("Check snmp_notification resource absent on agent :: #{result}") end - # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource present manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpNotificationLib.create_non_defaults) + on(master, SnmpNotificationLib.create_non_defaults(operating_system)) # Expected exit_code is 2 since this is a puppet agent cmd with change. cmd_str = PUPPET_BINPATH + 'agent -t' @@ -115,7 +116,11 @@ step 'TestStep :: Check snmp_notification resource presence on agent' do # Expected exit_code is 0 since this is a puppet resource cmd. # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource snmp_notification 'aaa server-state-change'" + if operating_system == 'ios_xr' + cmd_str = PUPPET_BINPATH + "resource snmp_notification 'ntp'" + else + cmd_str = PUPPET_BINPATH + "resource snmp_notification 'aaa server-state-change'" + end on(agent, cmd_str) do search_pattern_in_output(stdout, { 'enable' => 'true' }, diff --git a/tests/beaker_tests/netdev_stdlib/snmp_notificationlib.rb b/tests/beaker_tests/netdev_stdlib/snmp_notificationlib.rb index ae9cb005d..852ef397c 100644 --- a/tests/beaker_tests/netdev_stdlib/snmp_notificationlib.rb +++ b/tests/beaker_tests/netdev_stdlib/snmp_notificationlib.rb @@ -37,36 +37,60 @@ module SnmpNotificationLib # A. Methods to create manifests for snmp_notification Puppet provider test cases. - def self.create_absent - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} + def self.create_absent(type) + nxos_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { snmp_notification { 'aaa server-state-change': enable => false, } } EOF" - manifest_str + + ios_xr_str = "cat <#{PUPPETMASTER_MANIFESTPATH} +node default { + snmp_notification { 'ntp': + enable => false, + } +} +EOF" + type == 'ios_xr' ? ios_xr_str : nxos_str end - def self.create_defaults - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} + def self.create_defaults(type) + nxos_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { snmp_notification { 'aaa server-state-change': enable => false, } } EOF" - manifest_str + + ios_xr_str = "cat <#{PUPPETMASTER_MANIFESTPATH} +node default { + snmp_notification { 'ntp': + enable => false, + } +} +EOF" + type == 'ios_xr' ? ios_xr_str : nxos_str end - def self.create_non_defaults - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} + def self.create_non_defaults(type) + nxos_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { snmp_notification { 'aaa server-state-change': enable => true, } } EOF" - manifest_str + + ios_xr_str = "cat <#{PUPPETMASTER_MANIFESTPATH} +node default { + snmp_notification { 'ntp': + enable => true, + } +} +EOF" + type == 'ios_xr' ? ios_xr_str : nxos_str end end From 79388a81f6f1f2a5be215b3084d5a04ca0b83ec9 Mon Sep 17 00:00:00 2001 From: TP Honey Date: Fri, 10 Jun 2016 15:45:24 +0100 Subject: [PATCH 018/203] test fixes for ios_xr snmp_notification_receiver (#338) --- ...notification_receiver_provider_defaults.rb | 55 ++++++++++++------- .../snmp_notification_receiverlib.rb | 44 ++++++++++----- 2 files changed, 64 insertions(+), 35 deletions(-) diff --git a/tests/beaker_tests/snmp_notification_receiver/snmp_notification_receiver_provider_defaults.rb b/tests/beaker_tests/snmp_notification_receiver/snmp_notification_receiver_provider_defaults.rb index 5c1b6ff9e..a122abcbf 100644 --- a/tests/beaker_tests/snmp_notification_receiver/snmp_notification_receiver_provider_defaults.rb +++ b/tests/beaker_tests/snmp_notification_receiver/snmp_notification_receiver_provider_defaults.rb @@ -53,6 +53,12 @@ result = 'PASS' testheader = 'snmp_notification_receiver Resource :: All Attributes Defaults' +if operating_system == 'ios_xr' + interface = 'gigabitethernet0/0/0/1' +else + interface = 'ethernet1/4' +end + # @test_name [TestCase] Executes defaults testcase for snmp_notification_receiver Resource. test_name "TestCase :: #{testheader}" do # @step [Step] Sets up switch for provider test. @@ -64,8 +70,10 @@ resource_absent_cleanup(agent, 'snmp_notification_receiver', \ 'Remove snmp_notification_receiver') - add_vrf = get_vshell_cmd('conf t ; vrf context red') - on(agent, add_vrf, acceptable_exit_codes: [0, 2]) + if operating_system == 'nexus' + add_vrf = get_vshell_cmd('conf t ; vrf context red') + on(agent, add_vrf, acceptable_exit_codes: [0, 2]) + end logger.info('Setup switch for provider') end @@ -73,7 +81,7 @@ # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource present manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpNotificationReceiverLib.create_snmp_notification_receiver_manifest_present_v3) + on(master, SnmpNotificationReceiverLib.create_snmp_notification_receiver_manifest_present_v3(operating_system)) # Expected exit_code is 2 since this is a puppet agent cmd with change. cmd_str = PUPPET_BINPATH + 'agent -t' @@ -90,8 +98,13 @@ on(agent, cmd_str) do search_pattern_in_output(stdout, { 'ensure' => 'present' }, false, self, logger) - search_pattern_in_output(stdout, { 'source_interface' => 'ethernet1/3' }, - false, self, logger) + if operating_system == 'ios_xr' + search_pattern_in_output(stdout, { 'source_interface' => 'gigabitethernet0/0/0/0' }, + false, self, logger) + else + search_pattern_in_output(stdout, { 'source_interface' => 'ethernet1/3' }, + false, self, logger) + end search_pattern_in_output(stdout, { 'port' => '47' }, false, self, logger) search_pattern_in_output(stdout, { 'type' => 'traps' }, @@ -112,7 +125,7 @@ # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource present (with changes)manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpNotificationReceiverLib.create_snmp_notification_receiver_manifest_present_change_v3) + on(master, SnmpNotificationReceiverLib.create_snmp_notification_receiver_manifest_present_change_v3(interface)) # Expected exit_code is 2 since this is a puppet agent cmd with change. cmd_str = PUPPET_BINPATH + 'agent -t' @@ -129,7 +142,7 @@ on(agent, cmd_str) do search_pattern_in_output(stdout, { 'ensure' => 'present' }, false, self, logger) - search_pattern_in_output(stdout, { 'source_interface' => 'ethernet1/4' }, + search_pattern_in_output(stdout, { 'source_interface' => interface }, false, self, logger) search_pattern_in_output(stdout, { 'port' => '47' }, false, self, logger) @@ -151,7 +164,7 @@ # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource present (with changes)manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpNotificationReceiverLib.create_snmp_notification_receiver_manifest_present_change_v3_2) + on(master, SnmpNotificationReceiverLib.create_snmp_notification_receiver_manifest_present_change_v3_2(interface)) # Expected exit_code is 2 since this is a puppet agent cmd with change. cmd_str = PUPPET_BINPATH + 'agent -t' @@ -168,7 +181,7 @@ on(agent, cmd_str) do search_pattern_in_output(stdout, { 'ensure' => 'present' }, false, self, logger) - search_pattern_in_output(stdout, { 'source_interface' => 'ethernet1/4' }, + search_pattern_in_output(stdout, { 'source_interface' => interface }, false, self, logger) search_pattern_in_output(stdout, { 'port' => '47' }, false, self, logger) @@ -190,7 +203,7 @@ # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource present (with changes)manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpNotificationReceiverLib.create_snmp_notification_receiver_manifest_present_change_v3_3) + on(master, SnmpNotificationReceiverLib.create_snmp_notification_receiver_manifest_present_change_v3_3(interface)) # Expected exit_code is 2 since this is a puppet agent cmd with change. cmd_str = PUPPET_BINPATH + 'agent -t' @@ -207,7 +220,7 @@ on(agent, cmd_str) do search_pattern_in_output(stdout, { 'ensure' => 'present' }, false, self, logger) - search_pattern_in_output(stdout, { 'source_interface' => 'ethernet1/4' }, + search_pattern_in_output(stdout, { 'source_interface' => interface }, false, self, logger) search_pattern_in_output(stdout, { 'port' => '47' }, false, self, logger) @@ -229,7 +242,7 @@ # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource present (with changes)manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpNotificationReceiverLib.create_snmp_notification_receiver_manifest_present_v2) + on(master, SnmpNotificationReceiverLib.create_snmp_notification_receiver_manifest_present_v2(interface)) # Expected exit_code is 2 since this is a puppet agent cmd with change. cmd_str = PUPPET_BINPATH + 'agent -t' @@ -246,7 +259,7 @@ on(agent, cmd_str) do search_pattern_in_output(stdout, { 'ensure' => 'present' }, false, self, logger) - search_pattern_in_output(stdout, { 'source_interface' => 'ethernet1/4' }, + search_pattern_in_output(stdout, { 'source_interface' => interface }, false, self, logger) search_pattern_in_output(stdout, { 'port' => '47' }, false, self, logger) @@ -266,7 +279,7 @@ # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource present (with changes)manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpNotificationReceiverLib.create_snmp_notification_receiver_manifest_present_change_v2) + on(master, SnmpNotificationReceiverLib.create_snmp_notification_receiver_manifest_present_change_v2(interface)) # Expected exit_code is 2 since this is a puppet agent cmd with change. cmd_str = PUPPET_BINPATH + 'agent -t' @@ -283,7 +296,7 @@ on(agent, cmd_str) do search_pattern_in_output(stdout, { 'ensure' => 'present' }, false, self, logger) - search_pattern_in_output(stdout, { 'source_interface' => 'ethernet1/4' }, + search_pattern_in_output(stdout, { 'source_interface' => interface }, false, self, logger) search_pattern_in_output(stdout, { 'port' => '47' }, false, self, logger) @@ -303,7 +316,7 @@ # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource present (with changes)manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpNotificationReceiverLib.create_snmp_notification_receiver_manifest_present_v1) + on(master, SnmpNotificationReceiverLib.create_snmp_notification_receiver_manifest_present_v1(interface)) # Expected exit_code is 2 since this is a puppet agent cmd with change. cmd_str = PUPPET_BINPATH + 'agent -t' @@ -320,15 +333,17 @@ on(agent, cmd_str) do search_pattern_in_output(stdout, { 'ensure' => 'present' }, false, self, logger) - search_pattern_in_output(stdout, { 'source_interface' => 'ethernet1/4' }, + search_pattern_in_output(stdout, { 'source_interface' => interface }, false, self, logger) + if operating_system == 'nexus' + search_pattern_in_output(stdout, { 'version' => 'v1' }, + false, self, logger) + end search_pattern_in_output(stdout, { 'port' => '47' }, false, self, logger) - search_pattern_in_output(stdout, { 'type' => 'traps' }, - false, self, logger) search_pattern_in_output(stdout, { 'username' => 'ab' }, false, self, logger) - search_pattern_in_output(stdout, { 'version' => 'v1' }, + search_pattern_in_output(stdout, { 'type' => 'traps' }, false, self, logger) search_pattern_in_output(stdout, { 'vrf' => 'red' }, false, self, logger) diff --git a/tests/beaker_tests/snmp_notification_receiver/snmp_notification_receiverlib.rb b/tests/beaker_tests/snmp_notification_receiver/snmp_notification_receiverlib.rb index 5f0700c8c..a9bd89aca 100644 --- a/tests/beaker_tests/snmp_notification_receiver/snmp_notification_receiverlib.rb +++ b/tests/beaker_tests/snmp_notification_receiver/snmp_notification_receiverlib.rb @@ -44,8 +44,22 @@ module SnmpNotificationReceiverLib # where 'ensure' is set to present. # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_snmp_notification_receiver_manifest_present_v3 - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} + def self.create_snmp_notification_receiver_manifest_present_v3(type) + ios_xr_str = "cat <#{PUPPETMASTER_MANIFESTPATH} +node default { + snmp_notification_receiver { '2.3.4.5': + ensure => 'present', + source_interface => 'gigabitethernet0/0/0/0', + port => '47', + type => 'traps', + username => 'jj', + version => 'v3', + vrf => 'red', + security => 'priv', + } +} +EOF" + nxos_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { snmp_notification_receiver { '2.3.4.5': ensure => 'present', @@ -59,19 +73,19 @@ def self.create_snmp_notification_receiver_manifest_present_v3 } } EOF" - manifest_str + type == 'ios_xr' ? ios_xr_str : nxos_str end # Method to create a manifest for snmp_notification_receiver resource attribute 'ensure' # where 'ensure' is set to present, and a few changes made from above. # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_snmp_notification_receiver_manifest_present_change_v3 + def self.create_snmp_notification_receiver_manifest_present_change_v3(interface) manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { snmp_notification_receiver { '2.3.4.5': ensure => 'present', - source_interface => 'ethernet1/4', + source_interface => '#{interface}', port => '47', type => 'traps', username => 'ab', @@ -88,12 +102,12 @@ def self.create_snmp_notification_receiver_manifest_present_change_v3 # where 'ensure' is set to present, and a few changes made from above. # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_snmp_notification_receiver_manifest_present_change_v3_2 + def self.create_snmp_notification_receiver_manifest_present_change_v3_2(interface) manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { snmp_notification_receiver { '2.3.4.5': ensure => 'present', - source_interface => 'ethernet1/4', + source_interface => '#{interface}', port => '47', type => 'traps', username => 'ab', @@ -110,12 +124,12 @@ def self.create_snmp_notification_receiver_manifest_present_change_v3_2 # where 'ensure' is set to present, and a few changes made from above. # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_snmp_notification_receiver_manifest_present_change_v3_3 + def self.create_snmp_notification_receiver_manifest_present_change_v3_3(interface) manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { snmp_notification_receiver { '2.3.4.5': ensure => 'present', - source_interface => 'ethernet1/4', + source_interface => '#{interface}', port => '47', type => 'informs', username => 'ab', @@ -132,12 +146,12 @@ def self.create_snmp_notification_receiver_manifest_present_change_v3_3 # where 'ensure' is set to present, and a few changes made from above. # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_snmp_notification_receiver_manifest_present_v2 + def self.create_snmp_notification_receiver_manifest_present_v2(interface) manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { snmp_notification_receiver { '2.3.4.5': ensure => 'present', - source_interface => 'ethernet1/4', + source_interface => '#{interface}', port => '47', type => 'traps', username => 'ab', @@ -153,12 +167,12 @@ def self.create_snmp_notification_receiver_manifest_present_v2 # where 'ensure' is set to present, and a few changes made from above. # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_snmp_notification_receiver_manifest_present_change_v2 + def self.create_snmp_notification_receiver_manifest_present_change_v2(interface) manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { snmp_notification_receiver { '2.3.4.5': ensure => 'present', - source_interface => 'ethernet1/4', + source_interface => '#{interface}', port => '47', type => 'informs', username => 'ab', @@ -174,12 +188,12 @@ def self.create_snmp_notification_receiver_manifest_present_change_v2 # where 'ensure' is set to present, and a few changes made from above. # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_snmp_notification_receiver_manifest_present_v1 + def self.create_snmp_notification_receiver_manifest_present_v1(interface) manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { snmp_notification_receiver { '2.3.4.5': ensure => 'present', - source_interface => 'ethernet1/4', + source_interface => '#{interface}', port => '47', type => 'traps', username => 'ab', From 351404dec35063033c053689c1b9ea2bc862f5d1 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Fri, 10 Jun 2016 10:49:50 -0400 Subject: [PATCH 019/203] Add test_get / test_set to command_config (#348) * Add test_get / test_set to command_config * Needed a way for beaker to query / set config that didn't have explicit resource support (e.g. disabling a feature) * test_get is a simple get-only property that does a show running; callers set a filter to parse what they need: `puppet resource cisco_command_config 'cc' test_get='incl feature'` * test_set is a simple set-only property: `puppet resource cisco_command_config 'cc' test_set='no feature foo'` * Tested on n3,n5,n7,n9 * Bad regex in bkr test service_provider_nondefaults.rb * Add beaker test_get / test_set * config_find_remove() uses test_get to search for existing configs, then test_set to remove it. * Fix the fabricpath tests which ran into an issue on n5k when cleaning up 'feature nv overlay' with command_config: Puppet resource removes the config but then immediately does a 'show runn' which gets a SAP timeout on the device itself and the test errors. Using the test_set setter instead of regular command_config gets around this problem because it's a "set-only" with a dummy test_set getter. * Tested on n5,n7,n9 * test_get: add debugs, comments * tested on n5 --- .../provider/cisco_command_config/cisco.rb | 36 ++++++++++++++ lib/puppet/type/cisco_command_config.rb | 33 +++++++++++++ .../test_command_config.rb | 23 +++++++++ .../test_fabricpath_global.rb | 5 +- .../test_fabricpath_topology.rb | 5 +- .../service_provider_nondefaults.rb | 2 +- tests/beaker_tests/lib/utilitylib.rb | 47 ++++++++++++++++++- 7 files changed, 147 insertions(+), 4 deletions(-) diff --git a/lib/puppet/provider/cisco_command_config/cisco.rb b/lib/puppet/provider/cisco_command_config/cisco.rb index 362ae3251..75ab5e7c7 100644 --- a/lib/puppet/provider/cisco_command_config/cisco.rb +++ b/lib/puppet/provider/cisco_command_config/cisco.rb @@ -85,4 +85,40 @@ def command=(cmds) info "Successfully updated:\n#{e.previous.join("\n")}" unless e.previous.empty? raise end # command= + + def test_get + # This method is for beaker use only. It allows beaker to retrieve any + # configuration it needs from the device using puppet resource. Callers + # must pass a filter string to test_get. + # Example usage: + # puppet resource cisco_command_config 'cc' test_get='incl feature' + cmd = 'show running-config all' + cmd << " | #{@resource[:test_get]}" if @resource[:test_get] + + output = @node.get(command: cmd) + debug "@node.get output:\n#{output}" + "\n" + output unless output.nil? + end + + def test_get=(noop) + # This is a dummy "setter" for test_get(), which is a get-only property. + # This dummy method is necessary to keep Puppet from raising an error or + # displaying noise. + end + + def test_set + # This is a dummy "getter" for test_set=(), which is a set-only property. + # This dummy method is necessary to keep Puppet from raising an error or + # displaying noise. + end + + def test_set=(cmds) + # This method is for beaker use only. It allows beaker to set simple raw + # configuration using puppet resource. + # Example usage: + # puppet resource cisco_command_config 'cc' test_set='no feature foo' + return if cmds.empty? + output = @node.set(values: cmds) + debug "@node.set output:\n#{output}" unless output.nil? + end end # Puppet::Type diff --git a/lib/puppet/type/cisco_command_config.rb b/lib/puppet/type/cisco_command_config.rb index 1ed000fb2..88bf9596f 100644 --- a/lib/puppet/type/cisco_command_config.rb +++ b/lib/puppet/type/cisco_command_config.rb @@ -106,4 +106,37 @@ def self.title_patterns value.gsub!(/^(#{indent_level})/, '') # remove extra indentation end # validate end # property command + + newproperty(:test_get) do + desc %( + This is a test-only property for beaker use. It allows beaker to retrieve + any configuration it needs from the device using puppet resource. Callers + must pass a filter string to test_get. + Example usage: + puppet resource cisco_command_config 'cc' test_get='incl feature' + ) + + def insync?(*) + # This is a "get-only" property so insync? is overridden to prevent + # puppet from displaying the following notice: + # Notice: /Cisco_command_config[c]/test_get: test_get changed '' to '' + true + end + end + + newproperty(:test_set) do + desc %( + This is a test-only property for beaker use. It allows beaker to set + simple raw configuration using puppet resource. + Example usage: + puppet resource cisco_command_config 'cc' test_set='no feature foo' + ) + + munge do |value| + value << "\n" + value.gsub!(/^\s*$\n/, '') + indent_level = value.match(/\A\s*/) + value.gsub!(/^(#{indent_level})/, '') # remove extra indentation + end + end end # Puppet::Type.newtype diff --git a/tests/beaker_tests/cisco_command_config/test_command_config.rb b/tests/beaker_tests/cisco_command_config/test_command_config.rb index 53f206d11..cb05abfc5 100644 --- a/tests/beaker_tests/cisco_command_config/test_command_config.rb +++ b/tests/beaker_tests/cisco_command_config/test_command_config.rb @@ -222,6 +222,27 @@ # HELPER FUNCTIONS ################################################################# +def test_set_get + stepinfo = 'Test test_get/test_set properties' + logger.info("\n#{'-' * 60}\n#{stepinfo}") + cmd_prefix = PUPPET_BINPATH + "resource cisco_command_config 'cc' " + + logger.info('* cleanup testbed') + on(agent, cmd_prefix + "test_set='no interface loopback42'") + + logger.info('* create config') + on(agent, cmd_prefix + "test_set='interface loopback42'") + + logger.info('* check config') + on(agent, cmd_prefix + "test_get='incl loopback42'") + fail_test("TestStep :: set/get :: FAIL\nstdout:\n#{stdout}") unless + stdout[/^interface loopback42/] + + logger.info('* cleanup testbed') + on(agent, cmd_prefix + "test_set='no interface loopback42'") + logger.info("#{stepinfo} :: PASS\n#{'-' * 60}\n") +end + # Full command string for puppet resource command used to verify # the configuration applied by the cisco_command_config resource. def puppet_resource_cmd(tests, id) @@ -290,6 +311,8 @@ def test_harness_cisco_command_config(tests, id) id = 'configure_loopback_interface' tests[id][:desc] = '1.5 Apply INTERFACE Config' test_harness_cisco_command_config(tests, id) + + test_set_get end logger.info('TestCase :: # {testheader} :: End') diff --git a/tests/beaker_tests/cisco_fabricpath_global/test_fabricpath_global.rb b/tests/beaker_tests/cisco_fabricpath_global/test_fabricpath_global.rb index 382ad90f5..47c9ab907 100644 --- a/tests/beaker_tests/cisco_fabricpath_global/test_fabricpath_global.rb +++ b/tests/beaker_tests/cisco_fabricpath_global/test_fabricpath_global.rb @@ -239,8 +239,11 @@ def test_harness_fabricpath_global(tests, id) end def testbed_cleanup(agent) + logger.info("\n* testbed cleanup") + cmds = ['feature nv overlay', 'feature-set fabricpath'] + config_find_remove(agent, cmds, 'incl ^feature') + remove_all_vlans(agent) - resource_absent_cleanup(agent, 'cisco_fabricpath_global') end ################################################################# diff --git a/tests/beaker_tests/cisco_fabricpath_topology/test_fabricpath_topology.rb b/tests/beaker_tests/cisco_fabricpath_topology/test_fabricpath_topology.rb index ecde409b9..72ac46d15 100644 --- a/tests/beaker_tests/cisco_fabricpath_topology/test_fabricpath_topology.rb +++ b/tests/beaker_tests/cisco_fabricpath_topology/test_fabricpath_topology.rb @@ -159,8 +159,11 @@ def test_harness_fabricpath_topology(tests, id) end def testbed_cleanup(agent) + logger.info("\n* testbed cleanup") + cmds = ['feature nv overlay', 'feature-set fabricpath'] + config_find_remove(agent, cmds, 'incl ^feature') + remove_all_vlans(agent) - resource_absent_cleanup(agent, 'cisco_fabricpath_topology') end ################################################################# diff --git a/tests/beaker_tests/file_service_package/service_provider_nondefaults.rb b/tests/beaker_tests/file_service_package/service_provider_nondefaults.rb index 0679a5879..d67806c74 100644 --- a/tests/beaker_tests/file_service_package/service_provider_nondefaults.rb +++ b/tests/beaker_tests/file_service_package/service_provider_nondefaults.rb @@ -64,7 +64,7 @@ # @test_name [TestCase] Executes nondefaults testcase for SERVICE Resource. test_name "TestCase :: #{testheader}" do raise_skip_exception('Not supported for OAC platforms', self) if - platform[/'n5|6|7|k'/] + platform[/n(5|6|7)k/] # @step [Step] Sets up switch for provider test. step 'TestStep :: Setup switch for provider test' do # Expected exit_code is 0 since this is a bash shell cmd. diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 791b9e1c5..4b7fdefc7 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -800,6 +800,48 @@ def setup_fabricpath_env(tests, testcase) end # rubocop:enable Metrics/AbcSize +# Given a configuration command (or array of commands), search the device +# and remove any configs that match. Optionally include a show command filter. +# +# Example: +# config_find_remove(agent, 'interface loopback42') +# config_find_remove(agent, ['interface loopback42', 'feature foo']) +# config_find_remove(agent, ['feature foo', 'feature bar'], 'incl ^feature') +# +def config_find_remove(agent, find=[], filter='incl .*') + find = [find] if find.is_a?(String) + remove = [] + current = test_get(agent, filter) + + find.each do |cfg| + remove << "no #{cfg}" if current.match(Regexp.new("^#{cfg}")) + end + return if remove.empty? + + # Clean up all configs with one call + logger.info(' * Remove existing config') + test_set(agent, remove.join(' ; ')) +end + +# Get raw configuration from the device using command_config's test_get. +# test_get does a 'show runn' but requires a filter. +# Example: +# test_get(agent, 'incl ^vlan') +def test_get(agent, filter) + cmd_prefix = PUPPET_BINPATH + "resource cisco_command_config 'cc' " + on(agent, cmd_prefix + "test_get='#{filter}'") + stdout +end + +# Add arbitrary configurations using command_config's test_set property. +# Example: +# test_set(agent, 'no feature foo ; no feature bar') +def test_set(agent, cmd) + logger.info(cmd) + cmd_prefix = PUPPET_BINPATH + "resource cisco_command_config 'cc' " + on(agent, cmd_prefix + "test_set='#{cmd}'") +end + # Helper for command_config calls def command_config(agent, cmd, msg='') logger.info("\n#{msg}") @@ -810,7 +852,7 @@ def command_config(agent, cmd, msg='') # Helper to set properties using the puppet resource command. def resource_set(agent, resource, msg='') - logger.info("\n#{msg}") + logger.info("\nresource_set: #{msg}") if resource.is_a?(Array) cmd = "resource %s '%s' %s='%s'" % resource # rubocop:disable Style/FormatString @@ -1240,6 +1282,9 @@ def remove_interface(agent, intf) end def remove_all_vlans(agent, stepinfo='Remove all vlans & bridge-domains') + # TBD: Modify this cleanup to use faster test_get / test_set: + # test_get('i ^vlan|^bridge') + # test_set('no vlan ; no bridge ; system bridge-domain none') step "\n--------\n * TestStep :: #{stepinfo}" do resource_absent_cleanup(agent, 'cisco_bridge_domain', 'bridge domains') cmd = 'system bridge-domain none' From 6338dbded6c6e2196dbf7fba6f90a4528cd848a7 Mon Sep 17 00:00:00 2001 From: "JT (Jonny)" Date: Fri, 10 Jun 2016 16:23:09 +0100 Subject: [PATCH 020/203] Skipped tacacs provider test on IOS XR (#350) --- lib/puppet/provider/tacacs/cisco.rb | 6 ++++++ tests/beaker_tests/tacacs/tacacs_provider_defaults.rb | 2 ++ 2 files changed, 8 insertions(+) diff --git a/lib/puppet/provider/tacacs/cisco.rb b/lib/puppet/provider/tacacs/cisco.rb index d164f45c6..e9f309627 100644 --- a/lib/puppet/provider/tacacs/cisco.rb +++ b/lib/puppet/provider/tacacs/cisco.rb @@ -49,6 +49,9 @@ def self.get_properties(name) end # self.get_properties def self.instances + fail ArgumentError, + 'This provider is not supported on IOS XR' if Facter.value('operatingsystem').eql?('ios_xr') + tacacs = [] tacacs << get_properties('default') tacacs @@ -64,6 +67,9 @@ def self.prefetch(resources) end # self.prefetch def validate + fail ArgumentError, + 'This provider is not supported on IOS XR' if Facter.value('operatingsystem').eql?('ios_xr') + fail ArgumentError, "This provider only supports a namevar of 'default'" unless @resource[:name].to_s == 'default' end diff --git a/tests/beaker_tests/tacacs/tacacs_provider_defaults.rb b/tests/beaker_tests/tacacs/tacacs_provider_defaults.rb index b86586dd2..3f1efd94d 100644 --- a/tests/beaker_tests/tacacs/tacacs_provider_defaults.rb +++ b/tests/beaker_tests/tacacs/tacacs_provider_defaults.rb @@ -60,6 +60,8 @@ # @test_name [TestCase] Executes defaults testcase for tacacs Resource. test_name "TestCase :: #{testheader}" do + raise_skip_exception('Not supported on IOS XR', self) if operating_system == 'ios_xr' + # @step [Step] Sets up switch for provider test. step 'TestStep :: Setup switch for provider' do on(master, TacacsLib.create_tacacs_manifest_change_disabled) From 6df7a427859dc2f50964128b84a74e05d13c61d5 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Tue, 14 Jun 2016 09:33:35 -0400 Subject: [PATCH 021/203] Refactor tests: aaa_group_tacacs (#351) * Converted to new test syntax * Tested on n9-i4 --- .../aaagroup_provider_defaults.rb | 141 ------------ .../aaagroup_provider_negatives.rb | 200 ------------------ .../aaagroup_provider_nondefaults.rb | 147 ------------- .../cisco_aaa_group_tacacs/aaagrouplib.rb | 166 --------------- .../test_aaa_group_tacacs.rb | 101 +++++++++ 5 files changed, 101 insertions(+), 654 deletions(-) delete mode 100644 tests/beaker_tests/cisco_aaa_group_tacacs/aaagroup_provider_defaults.rb delete mode 100644 tests/beaker_tests/cisco_aaa_group_tacacs/aaagroup_provider_negatives.rb delete mode 100644 tests/beaker_tests/cisco_aaa_group_tacacs/aaagroup_provider_nondefaults.rb delete mode 100644 tests/beaker_tests/cisco_aaa_group_tacacs/aaagrouplib.rb create mode 100644 tests/beaker_tests/cisco_aaa_group_tacacs/test_aaa_group_tacacs.rb diff --git a/tests/beaker_tests/cisco_aaa_group_tacacs/aaagroup_provider_defaults.rb b/tests/beaker_tests/cisco_aaa_group_tacacs/aaagroup_provider_defaults.rb deleted file mode 100644 index c947a5b98..000000000 --- a/tests/beaker_tests/cisco_aaa_group_tacacs/aaagroup_provider_defaults.rb +++ /dev/null @@ -1,141 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# AaaGroup-Provider-Defaults.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet AAAGROUP resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a AAAGROUPUNITY resource test that tests for default values for -# ensure, group and acl attributes of a -# cisco_aaa_group_tacacs resource when created with 'ensure' => 'present'. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-4 deal with cisco_aaa_group_tacacs_resource creation and its -# verification using Puppet Agent and the switch running-config. -# Steps 5-7 deal with cisco_aaa_group_tacacs resource deletion and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and AaaGroupLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../aaagrouplib.rb', __FILE__) -# Require TacacsServer because vsh can't enable/disable feature tacacs -require File.expand_path('../../cisco_tacacs_server/tacacsserverlib.rb', __FILE__) - -result = 'PASS' -testheader = 'AAAGROUP Resource :: All Attributes Defaults' - -# @test_name [TestCase] Executes defaults testcase for AAAGROUP Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, TacacsServerLib.create_tacacsserver_absent) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - # Or expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource present manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, AaaGroupLib.create_aaagroup_manifest_present) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource present manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_aaa_group resource on agent using resource cmd. - step 'TestStep :: Check cisco_aaa_group resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_aaa_group_tacacs 'test'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'deadtime' => '0', - 'vrf_name' => 'default' }, - false, self, logger) - end - - logger.info("Check cisco_aaa_group resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource absent manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, AaaGroupLib.create_aaagroup_manifest_absent) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource absent manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_aaa_group resource on agent using resource cmd. - step 'TestStep :: Check cisco_aaa_group resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_aaa_group_tacacs 'test'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'deadtime' => '0', - 'vrf_name' => 'default' }, - true, self, logger) - end - - logger.info("Check cisco_aaa_group resource absence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_aaa_group_tacacs/aaagroup_provider_negatives.rb b/tests/beaker_tests/cisco_aaa_group_tacacs/aaagroup_provider_negatives.rb deleted file mode 100644 index 330991c24..000000000 --- a/tests/beaker_tests/cisco_aaa_group_tacacs/aaagroup_provider_negatives.rb +++ /dev/null @@ -1,200 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# -------------- -# AaaGroup-Provider-Negatives.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet AAAGROUP resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a AAAGROUP resource test that tests for negative values for -# ensure, group and acl attributes of a -# cisco_aaa_group_tacacs resource when created with 'ensure' => 'present'. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# The next set of teststeps deal with attribute negative tests and their -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and AaaGroupLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../aaagrouplib.rb', __FILE__) -# Require TacacsServer because vsh can't enable/disable feature tacacs -require File.expand_path('../../cisco_tacacs_server/tacacsserverlib.rb', __FILE__) - -result = 'PASS' -testheader = 'AAAGROUP Resource :: All Attributes Negatives' - -# @test_name [TestCase] Executes negatives testcase for AAAGROUP Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, TacacsServerLib.create_tacacsserver_absent) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - # Or expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, TacacsServerLib.create_tacacsserver_present) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, AaaGroupLib.create_aaagroup_manifest_deadtime_negative) - - # Expected exit_code is 1 since this is a puppet agent cmd with error. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [1]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_aaa_group resource on agent using resource cmd. - step 'TestStep :: Check cisco_aaa_group resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_aaa_group_tacacs 'test'" - on(agent, cmd_str) do - search_pattern_in_output( - stdout, - { 'deadtime' => AaaGroupLib::DEADTIME_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_aaa_group resource absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, AaaGroupLib.create_aaagroup_manifest_vrf_name_negative) - - # Expected exit_code is 1 since this is a puppet agent cmd with error. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [1]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_aaa_group resource on agent using resource cmd. - step 'TestStep :: Check cisco_aaa_group resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_aaa_group_tacacs 'test'" - on(agent, cmd_str) do - search_pattern_in_output( - stdout, - { 'vrf_name' => AaaGroupLib::VRF_NAME_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_aaa_group resource absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, - AaaGroupLib.create_aaagroup_manifest_source_interface_negative) - - # Expected exit_code is 1 since this is a puppet agent cmd with error. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [1]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_aaa_group resource on agent using resource cmd. - step 'TestStep :: Check cisco_aaa_group resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_aaa_group_tacacs 'test'" - on(agent, cmd_str) do - search_pattern_in_output( - stdout, - { 'source_interface' => AaaGroupLib::SOURCE_INTERFACE_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_aaa_group resource absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, - AaaGroupLib.create_aaagroup_manifest_server_hosts_negative) - - # Expected exit_code is 1 since this is a puppet agent cmd with error. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [1]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_aaa_group resource on agent using resource cmd. - step 'TestStep :: Check cisco_aaa_group resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_aaa_group_tacacs 'test'" - on(agent, cmd_str) do - search_pattern_in_output( - stdout, - { 'server_hosts' => AaaGroupLib::SERVER_HOSTS_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_aaa_group resource absence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_aaa_group_tacacs/aaagroup_provider_nondefaults.rb b/tests/beaker_tests/cisco_aaa_group_tacacs/aaagroup_provider_nondefaults.rb deleted file mode 100644 index 4bc6c8814..000000000 --- a/tests/beaker_tests/cisco_aaa_group_tacacs/aaagroup_provider_nondefaults.rb +++ /dev/null @@ -1,147 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# AaaGroup-Provider-NonDefaults.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet AAAGROUP resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a AAAGROUP resource test that tests for nondefault values for -# ensure, group and acl attributes of a -# cisco_aaa_group_tacacs resource when created with 'ensure' => 'present'. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-4 deal with cisco_aaa_group_tacacs_resource creation and its -# verification using Puppet Agent and the switch running-config. -# Steps 5-7 deal with cisco_aaa_group_tacacs resource deletion and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and AaaGroupLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../aaagrouplib.rb', __FILE__) -# Require TacacsServer because vsh can't enable/disable feature tacacs -require File.expand_path('../../cisco_tacacs_server/tacacsserverlib.rb', __FILE__) - -result = 'PASS' -testheader = 'AAAGROUP Resource :: All Attributes NonDefaults' - -# @test_name [TestCase] Executes nondefaults testcase for AAAGROUP Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, TacacsServerLib.create_tacacsserver_absent) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - # Or expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource nondefaults manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, AaaGroupLib.create_aaagroup_manifest_nondefaults) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource nondefaults manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_aaa_group resource on agent using resource cmd. - step 'TestStep :: Check cisco_aaa_group resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_aaa_group_tacacs 'test'" - on(agent, cmd_str) do - search_pattern_in_output( - stdout, - { 'ensure' => 'present', - 'deadtime' => '30', - 'vrf_name' => 'blue', - 'source_interface' => 'ethernet1/1', - 'server_hosts' => "\\['testhost', '1.1.1.1'\\]" }, - false, self, logger) - end - - logger.info("Check cisco_aaa_group resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource absent manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, AaaGroupLib.create_aaagroup_manifest_absent) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource absent manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_aaa_group resource on agent using resource cmd. - step 'TestStep :: Check cisco_aaa_group resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_aaa_group_tacacs 'test'" - on(agent, cmd_str) do - search_pattern_in_output( - stdout, - { 'ensure' => 'present', - 'deadtime' => '30', - 'vrf_name' => 'blue', - 'source_interface' => 'ethernet1/1', - 'server_hosts' => "\\['testhost', '1.1.1.1'\\]" }, - true, self, logger) - end - - logger.info("Check cisco_aaa_group resource absence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_aaa_group_tacacs/aaagrouplib.rb b/tests/beaker_tests/cisco_aaa_group_tacacs/aaagrouplib.rb deleted file mode 100644 index c3c173e33..000000000 --- a/tests/beaker_tests/cisco_aaa_group_tacacs/aaagrouplib.rb +++ /dev/null @@ -1,166 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### - -# Require UtilityLib.rb path. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) - -# AAAGROUP Utility Library: -# ------------------------- -# aaagrouplib.rb -# -# This is the utility library for the AAAGROUP provider Beaker test cases that -# contains the common methods used across the AAAGROUP testsuite's cases. The -# library is implemented as a module with related methods and constants defined -# inside it for use as a namespace. All of the methods are defined as module -# methods. -# -# Every Beaker AAAGROUP test case that runs an instance of Beaker::TestCase -# requires AaaGroupLib module. -# -# The module has a single set of methods: -# A. Methods to create manifests for cisco_aaa_group Puppet provider test cases. -module AaaGroupLib - # Group of Constants used in negative tests for AAAGROUP provider. - DEADTIME_NEGATIVE = '' - VRF_NAME_NEGATIVE = '' - SOURCE_INTERFACE_NEGATIVE = '' - SERVER_HOSTS_NEGATIVE = '' - - # A. Methods to create manifests for cisco_aaa_group Puppet provider test cases. - - # Method to create a manifest for AAAGROUP resource attribute 'ensure' where - # 'ensure' is set to present. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_aaagroup_manifest_present - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_aaa_group_tacacs { 'test': - ensure => present, - deadtime => 'default', - vrf_name => 'default', - source_interface => 'default', - server_hosts => 'default', - } -} -EOF" - manifest_str - end - - # Method to create a manifest for AAAGROUP resource attribute 'ensure' where - # 'ensure' is set to absent. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_aaagroup_manifest_absent - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_aaa_group_tacacs { 'test': - ensure => absent, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for AAAGROUP resource attributes: - # deadtime, vrf_name, source_interface, and server_hosts. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_aaagroup_manifest_nondefaults - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_tacacs_server { 'default': - ensure => present, - } - cisco_tacacs_server_host { 'testhost': - ensure => present, - } - cisco_tacacs_server_host { '1.1.1.1': - ensure => present, - } - cisco_aaa_group_tacacs { 'test': - ensure => present, - deadtime => '30', - vrf_name => 'blue', - source_interface => 'Ethernet1/1', - server_hosts => ['testhost', '1.1.1.1'], - require => Cisco_tacacs_server_host['testhost', '1.1.1.1'], - } -} -EOF" - manifest_str - end - - # Method to create a manifest for AAAGROUP resource attribute 'deadtime'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_aaagroup_manifest_deadtime_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_aaa_group_tacacs { 'test': - ensure => present, - deadtime => #{AaaGroupLib::DEADTIME_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for AAAGROUP resource attribute 'vrf_name'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_aaagroup_manifest_vrf_name_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_aaa_group_tacacs { 'test': - ensure => present, - vrf_name => #{AaaGroupLib::VRF_NAME_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for AAAGROUP attribute 'source_interface'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_aaagroup_manifest_source_interface_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_aaa_group_tacacs { 'test': - ensure => present, - source_interface => #{AaaGroupLib::SOURCE_INTERFACE_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for AAAGROUP attribute 'server_hosts'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_aaagroup_manifest_server_hosts_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_aaa_group_tacacs { 'test': - ensure => present, - server_hosts => #{AaaGroupLib::SERVER_HOSTS_NEGATIVE}, - } -} -EOF" - manifest_str - end -end diff --git a/tests/beaker_tests/cisco_aaa_group_tacacs/test_aaa_group_tacacs.rb b/tests/beaker_tests/cisco_aaa_group_tacacs/test_aaa_group_tacacs.rb new file mode 100644 index 000000000..ca92527a3 --- /dev/null +++ b/tests/beaker_tests/cisco_aaa_group_tacacs/test_aaa_group_tacacs.rb @@ -0,0 +1,101 @@ +############################################################################### +# Copyright (c) 2014-2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# Test hash top-level keys +tests = { + agent: agent, + master: master, + resource_name: 'cisco_aaa_group_tacacs', +} + +# Test hash test cases +tests[:default] = { + desc: '1.1 Default', + title_pattern: 'beaker', + preclean: 'cisco_aaa_group_tacacs', + manifest_props: { + deadtime: 'default', + source_interface: 'default', + server_hosts: 'default', + vrf_name: 'default', + }, + resource: { + deadtime: '0', + # source_interface: nil, + # server_hosts: nil, + vrf_name: 'default', + }, +} + +# Test hash test cases +tests[:non_default] = { + desc: '2.1 Non Default', + title_pattern: 'bkr_grp', + manifest_props: { + deadtime: '30', + source_interface: 'loopback42', + server_hosts: ['bkrhost', '1.1.1.1'], + vrf_name: 'beaker', + }, +} + +# Overridden to properly handle dependencies for this test file. +def dependency_manifest(_tests, id) + dep = '' + if id == :non_default + dep = %( + cisco_tacacs_server { 'default': ensure => present } + cisco_tacacs_server_host { 'bkrhost': ensure => present } + cisco_tacacs_server_host { '1.1.1.1': ensure => present } + ) + end + logger.info("\n * dependency_manifest\n#{dep}") + dep +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + id = :default + test_harness_run(tests, id) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + + id = :non_default + test_harness_run(tests, id) + tests[id][:ensure] = :absent + test_harness_run(tests, id) + + # ------------------------------------------------------------------- + resource_absent_cleanup(agent, 'cisco_aaa_group_tacacs') + resource_absent_cleanup(agent, 'cisco_tacacs_server') + resource_absent_cleanup(agent, 'cisco_tacacs_server_host') +end + +logger.info("TestCase :: #{tests[:resource_name]} :: End") From e4ada13ec530b6c73aed42d12920d6f835dc4217 Mon Sep 17 00:00:00 2001 From: saichint Date: Thu, 16 Jun 2016 05:09:26 -0700 Subject: [PATCH 022/203] Add cisco_ospf_area provider (#352) This PR is for cisco_ospf_area provider. The properties do not include nssa and its associated properties. They will be added as part of another user story. Since this provider becomes absent when all properties are set to default, cisco_pim model is followed for beaker tests. All demo_manifests and beaker tests passed on all platforms. Squashed commits: * first commit * fix issues * remove comment * Fix stub * fix area to ipaddr or int * small error * beaker tests * documentation * review comments * Update README.md * Review comments about stub --- CHANGELOG.md | 1 + README.md | 76 +++++++ examples/cisco/demo_ospf.pp | 22 ++ lib/puppet/provider/cisco_ospf_area/cisco.rb | 158 +++++++++++++++ lib/puppet/type/cisco_ospf_area.rb | 188 ++++++++++++++++++ .../cisco_ospf_area/test_ospf_area.rb | 119 +++++++++++ 6 files changed, 564 insertions(+) create mode 100644 lib/puppet/provider/cisco_ospf_area/cisco.rb create mode 100644 lib/puppet/type/cisco_ospf_area.rb create mode 100644 tests/beaker_tests/cisco_ospf_area/test_ospf_area.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index d277e99ad..db3564ef3 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### New feature support #### Cisco Resources - `cisco_bfd_global` type and provider. +- `cisco_ospf_area` type and provider. ### Added - Extend cisco_bgp_neighbor with attributes: diff --git a/README.md b/README.md index 02d44fc1a..3d54f93f8 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,7 @@ A note about support for specific platform models: | [cisco_itd_device_group_node](#type-cisco_itd_device_group_node) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | | [cisco_itd_service](#type-cisco_itd_service) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | \*[caveats](#cisco_itd_service-caveats) | | [cisco_ospf](#type-cisco_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_ospf_area](#type-cisco_ospf_area) | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_ospf_vrf](#type-cisco_ospf_vrf) | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅ = Supported
:heavy_minus_sign: = Not Applicable | N9k | N3k | N5k | N6k | N7k | Caveats | | [cisco_overlay_global](#type-cisco_overlay_global) | ✅ | :heavy_minus_sign: | ✅ | ✅ | ✅ | @@ -283,6 +284,7 @@ The following resources include cisco types and providers along with cisco provi * OSPF Types * [`cisco_vrf`](#type-cisco_vrf) * [`cisco_ospf`](#type-cisco_ospf) + * [`cisco_ospf_area`](#type-cisco_ospf_area) * [`cisco_ospf_vrf`](#type-cisco_ospf_vrf) * [`cisco_interface_ospf`](#type-cisco_interface_ospf) @@ -378,6 +380,7 @@ The following resources include cisco types and providers along with cisco provi * [`cisco_itd_device_group_node`](#type-cisco_itd_device_group_node) * [`cisco_itd_service`](#type-cisco_itd_service) * [`cisco_ospf`](#type-cisco_ospf) +* [`cisco_ospf_area`](#type-cisco_ospf_area) * [`cisco_ospf_vrf`](#type-cisco_ospf_vrf) * [`cisco_overlay_global`](#type-cisco_overlay_global) * [`cisco_pim`](#type-cisco_pim) @@ -2402,6 +2405,79 @@ and 'absent'. ##### `ospf` Name of the ospf router. Valid value is a string. +-- +### Type: cisco_ospf_area + +Manages an area for an OSPF router. + +| Platform | OS Minimum Version | Module Minimum Version | +|----------|:------------------:|:----------------------:| +| N9k | 7.0(3)I3(1) | 1.4.0 | +| N3k | 7.0(3)I3(1) | 1.4.0 | +| N5k | 7.3(0)N1(1) | 1.4.0 | +| N6k | 7.3(0)N1(1) | 1.4.0 | +| N7k | 7.3(0)D1(1) | 1.4.0 | +| N8k | 7.3(0)F1(1) | 1.4.0 | + +#### Example Usage + +```puppet +cisco_ospf_area { 'my_ospf_instance default 10': + ensure => 'present', + range => [['10.3.0.0/16', 'not_advertise', '23'], + ['10.3.3.0/24', '450'] + ], +} + +cisco_ospf_area { 'my_ospf_instance my_vrf 1.1.1.1': + ensure => 'present', + authentication => 'md5', + default_cost => 1000, + filter_list_in => 'fin', + filter_list_out => 'fout', + stub => true, +} +``` + +#### Parameters + + +| Example Parameter Usage | +|:--|:-- +|`cisco_ospf_area { ' ':` +|`cisco_ospf_area { '1 my_vrf 10':` +|`cisco_ospf_area { 'my_ospf default 10.1.1.1':` + +##### `ensure` +Determines whether the config should be present or not on the device. Valid values are 'present' and 'absent'. + +##### `authentication` +Enables authentication for the area. Valid values are 'clear_text', 'md5' or 'default'. + +##### `default_cost` +Default_cost for default summary Link-State Advertisement (LSA). Valid values are integer or keyword 'default'. + +##### `filter_list_in` +This is a route-map for filtering networks sent to this area. Valid values are string or keyword 'default'. + +##### `filter_list_out` +This is a route-map for filtering networks sent from this area. Valid values are string or keyword 'default'. + +##### `range` +Summarizes routes at an area boundary. Optionally sets the area range status to DoNotAdvertise as well as setting per-summary cost values. Valid values are a nested array of [summary_address, 'not_advertise', cost], or keyword 'default'. The summary-address is mandatory. + +Example: `range => [['10.3.0.0/16', 'not_advertise', '23'], + ['10.3.0.0/32', 'not_advertise'], + ['10.3.0.1/32'], + ['10.3.3.0/24', '450']]` + +##### `stub` +Defines the area as a stub area. Valid values are true, false or keyword 'default'. This property is not necessary when the `stub_no_summary` property is set to true, which also defines the area as a stub area. + + +##### `stub_no_summary` +Stub areas flood summary LSAs. This property disables summary flooding into the area. This property can be used in place of the `stub` property or in conjunction with it. Valid values are true, false or keyword 'default'. + -- ### Type: cisco_ospf_vrf diff --git a/examples/cisco/demo_ospf.pp b/examples/cisco/demo_ospf.pp index 6b066123a..7b5d572db 100644 --- a/examples/cisco/demo_ospf.pp +++ b/examples/cisco/demo_ospf.pp @@ -64,4 +64,26 @@ timer_throttle_spf_max => '5700', timer_throttle_spf_start => '277', } + + cisco_ospf_area { 'dark_blue default 1.1.1.1': + ensure => 'present', + authentication => 'md5', + default_cost => 1000, + filter_list_in => 'fin', + filter_list_out => 'fout', + range => [['10.3.0.0/16', 'not_advertise', '23'], ['10.3.3.0/24', '450']], + stub => true, + stub_no_summary => true, + } + + cisco_ospf_area { 'dark_blue vrf1 1450': + ensure => 'present', + authentication => 'clear_text', + default_cost => 5555, + filter_list_in => 'fin', + filter_list_out => 'fout', + range => [['10.3.0.0/16', '4989'], ['10.3.1.1/32']], + stub => true, + stub_no_summary => false, + } } diff --git a/lib/puppet/provider/cisco_ospf_area/cisco.rb b/lib/puppet/provider/cisco_ospf_area/cisco.rb new file mode 100644 index 000000000..18eef74d0 --- /dev/null +++ b/lib/puppet/provider/cisco_ospf_area/cisco.rb @@ -0,0 +1,158 @@ +# June, 2016 +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'cisco_node_utils' if Puppet.features.cisco_node_utils? +begin + require 'puppet_x/cisco/autogen' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'autogen.rb')) +end + +Puppet::Type.type(:cisco_ospf_area).provide(:cisco) do + desc 'The Cisco OSPF area provider.' + + confine feature: :cisco_node_utils + defaultfor operatingsystem: :nexus + + mk_resource_methods + + OSPF_AREA_NON_BOOL_PROPS = [ + :authentication, + :default_cost, + :filter_list_in, + :filter_list_out, + ] + OSPF_AREA_BOOL_PROPS = [ + :stub, + :stub_no_summary, + ] + OSPF_AREA_ARRAY_NESTED_PROPS = [ + :range + ] + + OSPF_AREA_ALL_PROPS = OSPF_AREA_NON_BOOL_PROPS + OSPF_AREA_BOOL_PROPS + + OSPF_AREA_ARRAY_NESTED_PROPS + + PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@nu', + OSPF_AREA_NON_BOOL_PROPS) + PuppetX::Cisco::AutoGen.mk_puppet_methods(:bool, self, '@nu', + OSPF_AREA_BOOL_PROPS) + PuppetX::Cisco::AutoGen.mk_puppet_methods(:array_nested, self, '@nu', + OSPF_AREA_ARRAY_NESTED_PROPS) + + def initialize(value={}) + super(value) + ospf = @property_hash[:ospf] + vrf = @property_hash[:vrf] + area = @property_hash[:area] + @nu = Cisco::RouterOspfArea.areas[ospf][vrf][area] unless + ospf.nil? || vrf.nil? || area.nil? + @property_flush = {} + end + + def self.properties_get(ospf, vrf, area, nu_obj) + debug "Checking ospf instance, #{ospf} #{vrf} #{area}" + current_state = { + name: "#{ospf} #{vrf} #{area}", + ospf: ospf, + vrf: vrf, + area: area, + ensure: :present, + } + # Call node_utils getter for each property + (OSPF_AREA_NON_BOOL_PROPS + OSPF_AREA_ARRAY_NESTED_PROPS).each do |prop| + current_state[prop] = nu_obj.send(prop) + end + OSPF_AREA_BOOL_PROPS.each do |prop| + val = nu_obj.send(prop) + if val.nil? + current_state[prop] = nil + else + current_state[prop] = val ? :true : :false + end + end + new(current_state) + end # self.properties_get + + def self.instances + area_instances = [] + Cisco::RouterOspfArea.areas.each do |ospf, vrfs| + vrfs.each do |vrf, areas| + areas.each do |area, nu_obj| + area_instances << properties_get(ospf, vrf, area, nu_obj) + end + end + end + area_instances + end # self.instances + + def self.prefetch(resources) + area_instances = instances + resources.keys.each do |id| + provider = area_instances.find do |ai| + ai.ospf.to_s == resources[id][:ospf].to_s && + ai.vrf.to_s == resources[id][:vrf].to_s && + ai.area.to_s == resources[id][:area].to_s + end + resources[id].provider = provider unless provider.nil? + end + end # self.prefetch + + def exists? + @property_hash[:ensure] == :present + end + + def create + @property_flush[:ensure] = :present + end + + def destroy + @property_flush[:ensure] = :absent + end + + def instance_name + name + end + + def properties_set(new_area=false) + OSPF_AREA_ALL_PROPS.each do |prop| + next unless @resource[prop] + send("#{prop}=", @resource[prop]) if new_area + unless @property_flush[prop].nil? + @nu.send("#{prop}=", @property_flush[prop]) if + @nu.respond_to?("#{prop}=") + end + end + end + + def flush + if @property_flush[:ensure] == :absent + @nu.destroy + @nu = nil + else + # Create/Update + new_area = false + if @nu.nil? + new_area = true + @nu = Cisco::RouterOspfArea.new(@resource[:ospf], @resource[:vrf], + @resource[:area]) + end + properties_set(new_area) + end + end +end diff --git a/lib/puppet/type/cisco_ospf_area.rb b/lib/puppet/type/cisco_ospf_area.rb new file mode 100644 index 000000000..f03188e31 --- /dev/null +++ b/lib/puppet/type/cisco_ospf_area.rb @@ -0,0 +1,188 @@ +# Manages the Cisco OSPF area configuration resource. +# +# June 2016 +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Puppet::Type.newtype(:cisco_ospf_area) do + @doc = "Manages an area for an OSPF router. + + cisco_ospf_area {\" \": + ..attributes.. + } + + is the name of the ospf router instance. + is the name of the ospf vrf. + is the name of the ospf area instance. + + Example: + cisco_ospf_area {'myrouter vrf1 1.1.1.1': + ensure => 'present', + authentication => 'md5', + default_cost => 1000, + filter_list_in => 'fin', + filter_list_out => 'fout', + range => [['10.3.0.0/16', true, '23'], + ['10.3.3.0/24', false, '450']], + stub_no_summary => true, + } + " + + ensurable + + ################### + # Resource Naming # + ################### + + # Parse out the title to fill in the attributes in these + # patterns. These attributes can be overwritten later. + def self.title_patterns + identity = ->(x) { x } + patterns = [] + + # Below pattern matches both parts of the full composite name. + patterns << [ + /^(\S+) (\S+) (\S+)$/, + [ + [:ospf, identity], + [:vrf, identity], + [:area, identity], + ], + ] + patterns + end + + # Overwrites name method. Original method simply returns self[:name], + # which is no longer valid or complete. + # Would not have failed, but just return nothing useful. + def name + "#{self[:ospf]} #{self[:vrf]} #{self[:area]}" + end + + newparam(:name) do + desc 'Name of cisco_ospf_area, not used, but needed for puppet' + end + + newparam(:area, namevar: true) do + desc 'Name of the resource instance. Valid values are string.' + munge do |value| + value = IPAddr.new(value.to_i, Socket::AF_INET) unless + value[/\./] + value + end + end # param area + + newparam(:vrf, namevar: true) do + desc "Name of the vrf instance. Valid values are string. The + name 'default' is a valid VRF." + end # param vrf + + newparam(:ospf, namevar: true) do + desc 'Name of the ospf instance. Valid values are string.' + end # param ospf + + ############## + # Attributes # + ############## + + newproperty(:authentication) do + desc 'Enable authentication for the area.' + + newvalues(:clear_text, :md5, :default) + end # property authentication + + newproperty(:default_cost) do + desc "default_cost for default summary Link-State Advertisement (LSA). + Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property default_cost + + newproperty(:filter_list_in) do + desc "This is a route-map for filtering networks sent to this area. + Valid values are string, keyword 'default'. " + + munge { |value| value == 'default' ? :default : value } + end # property filter_list_in + + newproperty(:filter_list_out) do + desc "This is a route-map for filtering networks sent from this area. + Valid values are string, keyword 'default'. " + + munge { |value| value == 'default' ? :default : value } + end # property filter_list_out + + newproperty(:range, array_matching: :all) do + format = '[[summary_address, not_advertise, cost], [sa, na, co]]' + desc 'An array of [summary_address, not_advertise, cost] pairs. '\ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + # override should_to_s and is_to_s for nested arrays + # to get clean output in the puppet notice like + # range changed '[]' to '[["10.3.0.0/16", "not_advertise", "23"], + # ["10.3.3.0/24", "450"]] + # instead of + # range changed [] to '10.3.0.0/16 not_advertise 23 10.3.3.0/24 450' + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + fail("Value must match format #{format}") unless value.is_a?(Array) + value + end + end + end # property range + + newproperty(:stub) do + desc 'Defines the area as a stub area. This property is not necessary + when the `stub_no_summary` property is set to true, which also + defines the area as a stub area.' + + newvalues(:true, :false, :default) + end # property stub + + newproperty(:stub_no_summary) do + desc 'Stub areas flood summary LSAs. This property disables summary + flooding into the area. This property can be used in place of + the `stub` property or in conjunction with it.' + + newvalues(:true, :false, :default) + end # property stub_no_summary + + validate do + # validate that stub cannot be false when + # stub_no_summary is true only if both + # properties are given in the manifest + return if + self[:stub_no_summary].nil? || self[:stub].nil? + fail ArgumentError, + 'stub MUST be true when stub_no_summary is true' if + self[:stub_no_summary] == :true && self[:stub] != :true + end +end diff --git a/tests/beaker_tests/cisco_ospf_area/test_ospf_area.rb b/tests/beaker_tests/cisco_ospf_area/test_ospf_area.rb new file mode 100644 index 000000000..14f9b581b --- /dev/null +++ b/tests/beaker_tests/cisco_ospf_area/test_ospf_area.rb @@ -0,0 +1,119 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### + +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# Test hash top-level keys +tests = { + master: master, + agent: agent, + operating_system: 'nexus', + resource_name: 'cisco_ospf_area', +} + +# Test hash test cases + +rarray1 = Array[['10.3.0.0/16', 'not_advertise', '23'], ['10.3.3.0/24', '450']] +rarray2 = Array[['10.3.0.0/16', '4989'], ['10.3.1.1/32']] + +# Non-default Tests. NOTE: [:resource] = [:manifest_props] for all non-default +tests[:non_default_1] = { + desc: '1.1 Non_Defaults', + title_pattern: 'dark_blue default 1.1.1.1', + preclean: 'cisco_ospf', + manifest_props: { + authentication: 'md5', + default_cost: 1000, + filter_list_in: 'filter_in', + filter_list_out: 'filter_out', + range: rarray1, + stub_no_summary: 'true', + }, + resource: { + range: "#{rarray1}" + }, +} + +tests[:non_default_2] = { + desc: '1.2 Non_Defaults', + title_pattern: 'dark_blue vrf1 2.2.2.2', + manifest_props: { + authentication: 'clear_text', + default_cost: 4444, + filter_list_in: 'fin', + filter_list_out: 'fout', + range: rarray2, + stub: 'true', + stub_no_summary: 'false', + }, + resource: { + range: "#{rarray2}" + }, +} + +tests[:non_default_3] = { + desc: '1.3 Non_Defaults', + title_pattern: 'dark_blue vrf1 3.3.3.3', + manifest_props: { + stub: 'true' + }, +} + +tests[:non_default_4] = { + desc: '1.4 Non_Defaults', + title_pattern: 'dark_blue vrf1 3.3.3.3', + manifest_props: { + stub_no_summary: 'true' + }, +} + +tests[:non_default_5] = { + desc: '1.5 Non_Defaults', + title_pattern: 'dark_blue vrf1 3.3.3.3', + manifest_props: { + stub_no_summary: 'false' + }, + resource: { + stub: 'true' + }, +} + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + # ------------------------------------------------------------------- + device = platform + logger.info("#### This device is of type: #{device} #####") + logger.info("\n#{'-' * 60}\nSection Non Default Property Testing") + + test_harness_run(tests, :non_default_1) + test_harness_run(tests, :non_default_2) + test_harness_run(tests, :non_default_3) + test_harness_run(tests, :non_default_4) + test_harness_run(tests, :non_default_5) + resource_absent_cleanup(agent, 'cisco_ospf') +end + +logger.info("TestCase :: #{tests[:resource_name]} :: End") From fe5822a15d4692cd01dec4d50212070285adedf6 Mon Sep 17 00:00:00 2001 From: "JT (Jonny)" Date: Thu, 16 Jun 2016 19:05:29 +0100 Subject: [PATCH 023/203] Added IOS XR support for snmp_user (#353) --- examples/netdev/demo_snmp.pp | 21 ++++++-- lib/puppet/provider/snmp_user/cisco.rb | 52 +++++++++++++++---- .../snmp_user/snmp_user_provider_defaults.rb | 47 +++++++++++++---- tests/beaker_tests/snmp_user/snmp_userlib.rb | 42 ++++++++++++--- 4 files changed, 131 insertions(+), 31 deletions(-) diff --git a/examples/netdev/demo_snmp.pp b/examples/netdev/demo_snmp.pp index d8e6ed82a..d33badbe6 100644 --- a/examples/netdev/demo_snmp.pp +++ b/examples/netdev/demo_snmp.pp @@ -29,14 +29,29 @@ acl => 'testcomacl', } + $password = $operatingsystem ? { + 'nexus' => '0x7e5030ffd26d7e1b366a9041e9c63c94', + default => '0307530A080824414B' + } + + $private_key = $operatingsystem ? { + 'nexus' => '0xcc012f26b3384d4b3da979bff48b4ffe', + default => '12491D42475E5A' + } + + $localized_key = $operatingsystem ? { + 'ios_xr' => undef, + default => true + } + snmp_user { 'test_snmp_user': ensure => present, roles => ['network-operator'], auth => 'md5', - password => '0x7e5030ffd26d7e1b366a9041e9c63c94', + password => $password, privacy => 'aes128', - private_key => '0xcc012f26b3384d4b3da979bff48b4ffe', - localized_key => true, + private_key => $private_key, + localized_key => $localized_key, } snmp_notification { 'vtp vlandelete': diff --git a/lib/puppet/provider/snmp_user/cisco.rb b/lib/puppet/provider/snmp_user/cisco.rb index e07e80415..3530d0479 100644 --- a/lib/puppet/provider/snmp_user/cisco.rb +++ b/lib/puppet/provider/snmp_user/cisco.rb @@ -20,7 +20,7 @@ desc 'The Cisco provider for snmp_user.' confine feature: :cisco_node_utils - defaultfor operatingsystem: :nexus + defaultfor operatingsystem: [:nexus, :ios_xr] mk_resource_methods @@ -46,13 +46,19 @@ def self.properties_get(snmpuser_name, v) current_state = { ensure: :present, name: snmpuser_name, - engine_id: v.engine_id, - roles: v.groups, auth: v.auth_protocol, password: v.auth_password, privacy: v.priv_protocol, private_key: v.priv_password, + roles: v.groups, } + + if Facter.value('operatingsystem').eql?('ios_xr') + current_state[:version] = v.version + else + current_state[:engine_id] = v.engine_id + end + new(current_state) end # self.properties_get @@ -86,7 +92,7 @@ def destroy @property_flush[:ensure] = :absent end - def validate + def validate # rubocop:disable Metrics/CyclomaticComplexity unless @resource[:auth] invalid = [] REQUIRES_AUTH_PROPS.each do |prop| @@ -105,22 +111,46 @@ def validate "The 'private_key' property must be set when specifying 'privacy'" \ if @resource[:private_key].nil? && @resource[:privacy] - fail ArgumentError, - "The 'engine_id' and 'roles' properties are mutually exclusive" \ - if @resource[:engine_id] && @resource[:roles] - fail ArgumentError, "The 'enforce_privacy' property is not supported by this provider" \ if @resource[:enforce_privacy] + + if Facter.value('operatingsystem').eql?('ios_xr') + fail ArgumentError, + "The 'engine_id' property is not supported on this platform" \ + if @resource[:engine_id] + + invalid = [] + [:roles, :version].each do |prop| + invalid << prop unless @resource[prop] + end + fail ArgumentError, + "You must specify the following properties on this platform: #{invalid}" \ + unless invalid.empty? + + fail ArgumentError, + 'This paltform only supports a single role per user' \ + if @resource[:roles].length > 1 + + if @resource[:localized_key] && @resource[:localized_key] == :false + fail ArgumentError, + 'This provider only supports providing encrypted passwords on this platform.' + end + else + fail ArgumentError, + "The 'engine_id' and 'roles' properties are mutually exclusive" \ + if @resource[:engine_id] && @resource[:roles] + end end def flush - validate @snmpuser.destroy if @snmpuser @snmpuser = nil return if @property_flush[:ensure] == :absent + validate + if @resource[:localized_key].eql?(:true) localized_key = true else @@ -137,6 +167,8 @@ def flush @resource[:privacy] || @property_hash[:privacy] || :none, @resource[:private_key] || @property_hash[:private_key] || '', localized_key, - @resource[:engine_id] || '') + @resource[:engine_id] || '', + true, + @resource[:version] || nil) end end # Puppet::Type diff --git a/tests/beaker_tests/snmp_user/snmp_user_provider_defaults.rb b/tests/beaker_tests/snmp_user/snmp_user_provider_defaults.rb index b0e1e17ad..f70f91e97 100644 --- a/tests/beaker_tests/snmp_user/snmp_user_provider_defaults.rb +++ b/tests/beaker_tests/snmp_user/snmp_user_provider_defaults.rb @@ -68,7 +68,7 @@ # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource present manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpUserLib.create_snmp_user_manifest_present) + on(master, SnmpUserLib.create_snmp_user_manifest_present(operating_system)) # Expected exit_code is 2 since this is a puppet agent cmd with change. cmd_str = PUPPET_BINPATH + 'agent -t' @@ -89,12 +89,25 @@ false, self, logger) search_pattern_in_output(stdout, { 'auth' => 'md5' }, false, self, logger) - search_pattern_in_output(stdout, { 'password' => '0x7e5030ffd26d7e1b366a9041e9c63c94' }, - false, self, logger) + + if operating_system == 'ios_xr' + search_pattern_in_output(stdout, { 'password' => '0307530A080824414B' }, + false, self, logger) + else + search_pattern_in_output(stdout, { 'password' => '0x7e5030ffd26d7e1b366a9041e9c63c94' }, + false, self, logger) + end + search_pattern_in_output(stdout, { 'privacy' => 'aes128' }, false, self, logger) - search_pattern_in_output(stdout, { 'private_key' => '0xcc012f26b3384d4b3da979bff48b4ffe' }, - false, self, logger) + + if operating_system == 'ios_xr' + search_pattern_in_output(stdout, { 'private_key' => '12491D42475E59' }, + false, self, logger) + else + search_pattern_in_output(stdout, { 'private_key' => '0xcc012f26b3384d4b3da979bff48b4ffe' }, + false, self, logger) + end end logger.info("Check snmp_user resource presence on agent :: #{result}") @@ -103,7 +116,7 @@ # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource present (with changes)manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpUserLib.create_snmp_user_manifest_present_change) + on(master, SnmpUserLib.create_snmp_user_manifest_present_change(operating_system)) # Expected exit_code is 2 since this is a puppet agent cmd with change. cmd_str = PUPPET_BINPATH + 'agent -t' @@ -121,15 +134,27 @@ search_pattern_in_output(stdout, { 'ensure' => 'present' }, false, self, logger) search_pattern_in_output(stdout, { 'engine_id' => '128:0:0:9:3:8:0:39:34:152:217' }, - false, self, logger) + false, self, logger) unless operating_system == 'ios_xr' search_pattern_in_output(stdout, { 'auth' => 'sha' }, false, self, logger) - search_pattern_in_output(stdout, { 'password' => '0x7e5030ffd26d7e1b366a9041e9c63c94' }, - false, self, logger) + + if operating_system == 'ios_xr' + search_pattern_in_output(stdout, { 'password' => '0307530A080824414B' }, + false, self, logger) + else + search_pattern_in_output(stdout, { 'password' => '0x7e5030ffd26d7e1b366a9041e9c63c94' }, + false, self, logger) + end + search_pattern_in_output(stdout, { 'privacy' => 'des' }, false, self, logger) - search_pattern_in_output(stdout, { 'private_key' => '0xcc012f26b3384d4b3da979bff48b4ffe' }, - false, self, logger) + if operating_system == 'ios_xr' + search_pattern_in_output(stdout, { 'private_key' => '12491D42475E59' }, + false, self, logger) + else + search_pattern_in_output(stdout, { 'private_key' => '0xcc012f26b3384d4b3da979bff48b4ffe' }, + false, self, logger) + end end logger.info("Check snmp_user resource presence on agent :: #{result}") diff --git a/tests/beaker_tests/snmp_user/snmp_userlib.rb b/tests/beaker_tests/snmp_user/snmp_userlib.rb index 9b96a4bd7..5ec3aaeb9 100644 --- a/tests/beaker_tests/snmp_user/snmp_userlib.rb +++ b/tests/beaker_tests/snmp_user/snmp_userlib.rb @@ -44,8 +44,8 @@ module SnmpUserLib # where 'ensure' is set to present. # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_snmp_user_manifest_present - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} + def self.create_snmp_user_manifest_present(type) + nxos_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { snmp_user { 'test_snmp_user': ensure => present, @@ -58,20 +58,34 @@ def self.create_snmp_user_manifest_present } } EOF" - manifest_str + + ios_xr_str = "cat <#{PUPPETMASTER_MANIFESTPATH} +node default { + snmp_user { 'test_snmp_user': + ensure => present, + roles => ['network-operator'], + version => 'v3', + auth => 'md5', + password => '0307530A080824414B', + privacy => 'aes128', + private_key => '12491D42475E59', + } +} +EOF" + type == 'ios_xr' ? ios_xr_str : nxos_str end # Method to create a manifest for snmp_user resource attribute 'ensure' # where 'ensure' is set to present, and a few changes made from above. # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_snmp_user_manifest_present_change - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} + def self.create_snmp_user_manifest_present_change(type) + nxos_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { snmp_user { 'test_snmp_user': ensure => present, auth => 'sha', - password => '0x7e5030ffd26d7e1b366a9041e9c63c94', + password => '0307530A080824414B', privacy => 'des', private_key => '0xcc012f26b3384d4b3da979bff48b4ffe', localized_key => true, @@ -79,7 +93,21 @@ def self.create_snmp_user_manifest_present_change } } EOF" - manifest_str + + ios_xr_str = "cat <#{PUPPETMASTER_MANIFESTPATH} +node default { + snmp_user { 'test_snmp_user': + ensure => present, + roles => ['network-operator'], + version => 'v3', + auth => 'sha', + password => '0307530A080824414B', + privacy => 'des', + private_key => '12491D42475E59', + } +} +EOF" + type == 'ios_xr' ? ios_xr_str : nxos_str end # Method to create a manifest for snmp_user resource attribute 'ensure' From 5f3983930afb3efa53630566d8c35e772ebf0f89 Mon Sep 17 00:00:00 2001 From: saichint Date: Thu, 23 Jun 2016 05:03:04 -0700 Subject: [PATCH 024/203] Add nssa properties to ospf_area provider (#354) * first commit * fix issues * remove comment * Fix stub * fix area to ipaddr or int * small error * beaker tests * documentation * review comments * Update README.md * Review comments about stub * nssa * small correction * name changes * fix manifest * fix nssa_set * include cmnutils * beaker test added for nssa * typo fix * documentation * fix docmentation * fix README * Fix review comments --- README.md | 43 +++++- examples/cisco/demo_ospf.pp | 12 +- lib/puppet/provider/cisco_ospf_area/cisco.rb | 42 +++++- lib/puppet/type/cisco_ospf_area.rb | 124 ++++++++++++++++-- .../cisco_ospf_area/test_ospf_area.rb | 16 ++- 5 files changed, 218 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 3d54f93f8..e26562719 100644 --- a/README.md +++ b/README.md @@ -2437,6 +2437,16 @@ cisco_ospf_area { 'my_ospf_instance my_vrf 1.1.1.1': filter_list_out => 'fout', stub => true, } + +cisco_ospf_area { 'my_ospf_instance my_vrf 1000': + ensure => 'present', + nssa => true, + nssa_default_originate => true, + nssa_no_redistribution => true, + nssa_no_summary => true, + nssa_route_map => 'rmap', + nssa_translate_type7 => 'always', +} ``` #### Parameters @@ -2452,7 +2462,7 @@ cisco_ospf_area { 'my_ospf_instance my_vrf 1.1.1.1': Determines whether the config should be present or not on the device. Valid values are 'present' and 'absent'. ##### `authentication` -Enables authentication for the area. Valid values are 'clear_text', 'md5' or 'default'. +Enables authentication for the area. Valid values are 'cleartext', 'md5' or 'default'. ##### `default_cost` Default_cost for default summary Link-State Advertisement (LSA). Valid values are integer or keyword 'default'. @@ -2463,6 +2473,33 @@ This is a route-map for filtering networks sent to this area. Valid values are s ##### `filter_list_out` This is a route-map for filtering networks sent from this area. Valid values are string or keyword 'default'. +##### `nssa` +This property defines the area as NSSA (not so stubby area). Valid values are true, false or keyword 'default'. This property is mutually exclusive with `stub` and `stub_no_summary`. + +##### `nssa_default_originate` +Generates an NSSA External (type 7) LSA for use as a default route to the external autonomous system. Valid values are true, false or keyword 'default'. + + +##### `nssa_no_redistribution` +Disable redistribution within the NSSA. Valid values are true, false or keyword 'default'. + +##### `nssa_no_summary` +Disables summary LSA flooding within the NSSA. Valid values are true, false or keyword 'default'. + +##### `nssa_route_map` +Controls distribution of the default route. This property can only be used when the `nssa_default_originate` property is set to true. Valid values are String (the route-map name) or keyword 'default'. + +##### `nssa_translate_type7` +Translates NSSA external (type 7) LSAs to standard external (type 5) LSAs for use outside the NSSA. Valid values are one of the following keyword strings: + +Keyword | Description +|:--|:-- +|`always` | Always translate +|`suppress_fa` | Forwarding Address Suppression +|`always_suppress_fa` | Always translate & use Forwarding Address Suppression +|`never` | Never translate +|`default` | Translation is not configured + ##### `range` Summarizes routes at an area boundary. Optionally sets the area range status to DoNotAdvertise as well as setting per-summary cost values. Valid values are a nested array of [summary_address, 'not_advertise', cost], or keyword 'default'. The summary-address is mandatory. @@ -2472,11 +2509,11 @@ Example: `range => [['10.3.0.0/16', 'not_advertise', '23'], ['10.3.3.0/24', '450']]` ##### `stub` -Defines the area as a stub area. Valid values are true, false or keyword 'default'. This property is not necessary when the `stub_no_summary` property is set to true, which also defines the area as a stub area. +Defines the area as a stub area. Valid values are true, false or keyword 'default'. This property is not necessary when the `stub_no_summary` property is set to true, which also defines the area as a stub area. This property is mutually exclusive with `nssa`. ##### `stub_no_summary` -Stub areas flood summary LSAs. This property disables summary flooding into the area. This property can be used in place of the `stub` property or in conjunction with it. Valid values are true, false or keyword 'default'. +Stub areas flood summary LSAs. This property disables summary flooding into the area. This property can be used in place of the `stub` property or in conjunction with it. Valid values are true, false or keyword 'default'. This property is mutually exclusive with `nssa`. -- ### Type: cisco_ospf_vrf diff --git a/examples/cisco/demo_ospf.pp b/examples/cisco/demo_ospf.pp index 7b5d572db..bc90eaaa1 100644 --- a/examples/cisco/demo_ospf.pp +++ b/examples/cisco/demo_ospf.pp @@ -78,7 +78,7 @@ cisco_ospf_area { 'dark_blue vrf1 1450': ensure => 'present', - authentication => 'clear_text', + authentication => 'cleartext', default_cost => 5555, filter_list_in => 'fin', filter_list_out => 'fout', @@ -86,4 +86,14 @@ stub => true, stub_no_summary => false, } + + cisco_ospf_area { 'dark_blue vrf2 5000': + ensure => 'present', + nssa => true, + nssa_default_originate => true, + nssa_no_redistribution => true, + nssa_no_summary => true, + nssa_route_map => 'aaa', + nssa_translate_type7 => 'supress_fa', + } } diff --git a/lib/puppet/provider/cisco_ospf_area/cisco.rb b/lib/puppet/provider/cisco_ospf_area/cisco.rb index 18eef74d0..9ff3e3cac 100644 --- a/lib/puppet/provider/cisco_ospf_area/cisco.rb +++ b/lib/puppet/provider/cisco_ospf_area/cisco.rb @@ -23,6 +23,13 @@ 'puppet_x', 'cisco', 'autogen.rb')) end +begin + require 'puppet_x/cisco/cmnutils' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'cmnutils.rb')) +end Puppet::Type.type(:cisco_ospf_area).provide(:cisco) do desc 'The Cisco OSPF area provider.' @@ -36,8 +43,14 @@ :default_cost, :filter_list_in, :filter_list_out, + :nssa_route_map, + :nssa_translate_type7, ] OSPF_AREA_BOOL_PROPS = [ + :nssa, # :nssa should process before other nssa properties + :nssa_default_originate, + :nssa_no_redistribution, + :nssa_no_summary, :stub, :stub_no_summary, ] @@ -45,7 +58,7 @@ :range ] - OSPF_AREA_ALL_PROPS = OSPF_AREA_NON_BOOL_PROPS + OSPF_AREA_BOOL_PROPS + + OSPF_AREA_ALL_PROPS = OSPF_AREA_BOOL_PROPS + OSPF_AREA_NON_BOOL_PROPS + OSPF_AREA_ARRAY_NESTED_PROPS PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@nu', @@ -138,6 +151,33 @@ def properties_set(new_area=false) @nu.respond_to?("#{prop}=") end end + # custom setters which require one-shot multi-param setters + nssa_set + end + + def nssa_set + attrs = {} + vars = [ + :nssa, + :nssa_route_map, + :nssa_default_originate, + :nssa_no_redistribution, + :nssa_no_summary, + ] + if vars.any? { |p| @property_flush.key?(p) } + # At least one var has changed, get all vals from manifest + vars.each do |p| + val = @resource[p] + if val == :default + val = @nu.send("default_#{p}") + else + val = PuppetX::Cisco::Utils.bool_sym_to_s(val) + end + next if val == false || val.to_s.empty? + attrs[p] = val + end + end + @nu.nssa_set(attrs) end def flush diff --git a/lib/puppet/type/cisco_ospf_area.rb b/lib/puppet/type/cisco_ospf_area.rb index f03188e31..359e2a194 100644 --- a/lib/puppet/type/cisco_ospf_area.rb +++ b/lib/puppet/type/cisco_ospf_area.rb @@ -27,16 +27,26 @@ is the name of the ospf vrf. is the name of the ospf area instance. - Example: + Examples: cisco_ospf_area {'myrouter vrf1 1.1.1.1': - ensure => 'present', - authentication => 'md5', - default_cost => 1000, - filter_list_in => 'fin', - filter_list_out => 'fout', - range => [['10.3.0.0/16', true, '23'], - ['10.3.3.0/24', false, '450']], - stub_no_summary => true, + ensure => 'present', + authentication => 'md5', + default_cost => 1000, + filter_list_in => 'fin', + filter_list_out => 'fout', + range => [['10.3.0.0/16', true, '23'], + ['10.3.3.0/24', false, '450']], + stub_no_summary => true, + } + + cisco_ospf_area {'myrouter vrf2 2002': + ensure => 'present', + nssa => true, + nssa_default_originate => true, + nssa_no_redistribution => true, + nssa_no_summary => true, + nssa_route_map => 'rmap', + nssa_translate_type7 => 'always_supress_fa', } " @@ -100,7 +110,7 @@ def name newproperty(:authentication) do desc 'Enable authentication for the area.' - newvalues(:clear_text, :md5, :default) + newvalues(:cleartext, :md5, :default) end # property authentication newproperty(:default_cost) do @@ -124,6 +134,48 @@ def name munge { |value| value == 'default' ? :default : value } end # property filter_list_out + newproperty(:nssa) do + desc 'Defines the area as NSSA (not so stubby area). This is + mutually exclusive with stub and stub_no_summary.' + + newvalues(:true, :false, :default) + end # property nssa + + newproperty(:nssa_default_originate) do + desc 'Generates an NSSA External (type 7) LSA for use as + a default route to the external autonomous system.' + + newvalues(:true, :false, :default) + end # property nssa_default_originate + + newproperty(:nssa_no_redistribution) do + desc 'Disable redistribution within the NSSA.' + + newvalues(:true, :false, :default) + end # property nssa_no_redistribution + + newproperty(:nssa_no_summary) do + desc 'Disables summary LSA flooding within the NSSA.' + + newvalues(:true, :false, :default) + end # property nssa_no_summary + + newproperty(:nssa_route_map) do + desc "Controls distribution of the default route. This + property can only be used when the + `nssa_default_originate` property is set to true. + Valid values are string, keyword 'default'. " + + munge { |value| value == 'default' ? :default : value } + end # property nssa_route_map + + newproperty(:nssa_translate_type7) do + desc 'Translates NSSA external (type 7) LSAs to standard + external (type 5) LSAs for use outside the NSSA.' + + newvalues(:always, :always_supress_fa, :never, :supress_fa, :default) + end # property nssa_translate_type7 + newproperty(:range, array_matching: :all) do format = '[[summary_address, not_advertise, cost], [sa, na, co]]' desc 'An array of [summary_address, not_advertise, cost] pairs. '\ @@ -162,7 +214,8 @@ def is_to_s(value) newproperty(:stub) do desc 'Defines the area as a stub area. This property is not necessary when the `stub_no_summary` property is set to true, which also - defines the area as a stub area.' + defines the area as a stub area. This is mutually exclusive with + nssa' newvalues(:true, :false, :default) end # property stub @@ -170,12 +223,13 @@ def is_to_s(value) newproperty(:stub_no_summary) do desc 'Stub areas flood summary LSAs. This property disables summary flooding into the area. This property can be used in place of - the `stub` property or in conjunction with it.' + the `stub` property or in conjunction with it. This is mutually + exclusive with nssa' newvalues(:true, :false, :default) end # property stub_no_summary - validate do + def check_stub_params # validate that stub cannot be false when # stub_no_summary is true only if both # properties are given in the manifest @@ -185,4 +239,48 @@ def is_to_s(value) 'stub MUST be true when stub_no_summary is true' if self[:stub_no_summary] == :true && self[:stub] != :true end + + def check_stub_nssa + # validate that stub and nssa are not enabled at the + # same time + fail ArgumentError, + 'stub and nssa cannot be enabled at the same time' if + (self[:stub_no_summary] == :true || self[:stub] == :true) && + self[:nssa] == :true + end + + def check_nssa_defaults + # validate that all nssa properties are default when + # nssa is default + return if self[:nssa] == :true + # validate that route_map is not enabled when + # default_information_originate is false + vars = [ + :nssa_default_originate, + :nssa_no_redistribution, + :nssa_no_summary, + :nssa_route_map, + ] + vars.each do |p| + fail ArgumentError, + 'All nssa params should be default when nssa is disabled' unless + self[p].nil? || self[p] == :default || self[p] == :false || self[p] == '' + end + end + + def check_nssa_route_map + return if self[:nssa_default_originate].nil? && + self[:nssa_route_map].nil? + fail ArgumentError, + 'nssa_route_map MUST be default when nssa_default_originate is default' if + self[:nssa_route_map] != :default && self[:nssa_route_map] != '' && + self[:nssa_default_originate] != :true + end + + validate do + check_stub_nssa + check_nssa_defaults + check_nssa_route_map + check_stub_params + end end diff --git a/tests/beaker_tests/cisco_ospf_area/test_ospf_area.rb b/tests/beaker_tests/cisco_ospf_area/test_ospf_area.rb index 14f9b581b..0c5a9e0fc 100644 --- a/tests/beaker_tests/cisco_ospf_area/test_ospf_area.rb +++ b/tests/beaker_tests/cisco_ospf_area/test_ospf_area.rb @@ -59,7 +59,7 @@ desc: '1.2 Non_Defaults', title_pattern: 'dark_blue vrf1 2.2.2.2', manifest_props: { - authentication: 'clear_text', + authentication: 'cleartext', default_cost: 4444, filter_list_in: 'fin', filter_list_out: 'fout', @@ -99,6 +99,19 @@ }, } +tests[:non_default_6] = { + desc: '1.6 Non_Defaults', + title_pattern: 'dark_blue vrf2 4.4.4.4', + manifest_props: { + nssa: 'true', + nssa_default_originate: 'true', + nssa_no_redistribution: 'true', + nssa_no_summary: 'true', + nssa_route_map: 'rmap', + nssa_translate_type7: 'always_supress_fa', + }, +} + ################################################################# # TEST CASE EXECUTION ################################################################# @@ -113,6 +126,7 @@ test_harness_run(tests, :non_default_3) test_harness_run(tests, :non_default_4) test_harness_run(tests, :non_default_5) + test_harness_run(tests, :non_default_6) resource_absent_cleanup(agent, 'cisco_ospf') end From 3ee0e3b874a2b64cf4abffb8bf22ca4ca0d50368 Mon Sep 17 00:00:00 2001 From: TP Honey Date: Tue, 28 Jun 2016 20:01:58 +0100 Subject: [PATCH 025/203] tacacs_global changes for ios_xr (#342) --- lib/puppet/provider/tacacs_global/cisco.rb | 77 ++++++++----------- .../tacacs_global_provider_defaults.rb | 51 +----------- .../tacacs_global/tacacs_globallib.rb | 28 ++----- 3 files changed, 43 insertions(+), 113 deletions(-) diff --git a/lib/puppet/provider/tacacs_global/cisco.rb b/lib/puppet/provider/tacacs_global/cisco.rb index 5a546f5e2..0d2e7471c 100644 --- a/lib/puppet/provider/tacacs_global/cisco.rb +++ b/lib/puppet/provider/tacacs_global/cisco.rb @@ -1,4 +1,4 @@ -# October, 2015 +# June, 2016 # # Copyright (c) 2014-2016 Cisco and/or its affiliates. # @@ -15,6 +15,13 @@ # limitations under the License. require 'cisco_node_utils' if Puppet.features.cisco_node_utils? +begin + require 'puppet_x/cisco/autogen' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'autogen.rb')) +end Puppet::Type.type(:tacacs_global).provide(:cisco) do desc 'The Cisco provider for tacacs_global.' @@ -24,37 +31,37 @@ mk_resource_methods - # The following properties are only valid when 'enable' is set to true - ENABLED_ONLY_PROPS = [:timeout, :key, :key_format] + TACACS_GLOBAL_PROPS = { + timeout: :timeout + } def initialize(value={}) super(value) - @tacacs_global = Cisco::TacacsServer.new(false) + @tacacs_global = Cisco::TacacsGlobal.tacacs_global['default'] @property_flush = {} debug 'Created provider instance of tacacs_global' end - def self.get_properties(name) - debug "Checking instance, TacacsServer #{name}" + def self.get_properties(name, v) + debug "Checking instance, TacacsGlobal #{name}" - v = Cisco::TacacsServer.new(false) - - current_state = {} - current_state[:name] = name - current_state[:enable] = Cisco::TacacsServer.enabled ? :true : :false - - if current_state[:enable].eql?(:true) - current_state[:timeout] = v.timeout - current_state[:key] = v.encryption_password - current_state[:key_format] = v.encryption_type - end + current_state = { + ensure: :present, + name: v.name, + timeout: v.timeout ? v.timeout : -1, + key: v.key ? v.key : 'unset', + key_format: v.key_format ? v.key_format : -1, + } new(current_state) end # self.get_properties def self.instances tacacsglobal = [] - tacacsglobal << get_properties('default') + Cisco::TacacsGlobal.tacacs_global.each do |name, v| + tacacsglobal << get_properties(name, v) + end + tacacsglobal end @@ -84,44 +91,24 @@ def munge_flush(val) def validate fail ArgumentError, "This provider only supports a namevar of 'default'" unless @resource[:name].to_s == 'default' - fail ArgumentError, - "This provider does not support the 'retransmit_count' property." if @resource[:retransmit_count] - - if @resource[:enable] && @resource[:enable].eql?(:false) - invalid = [] - ENABLED_ONLY_PROPS.each do |prop| - invalid << prop if @resource[prop] - end - fail ArgumentError, "The 'enable' property must be set to true when specifying the following properties: #{invalid}" unless invalid.empty? - else - fail ArgumentError, - "The 'key' property must be set when specifying 'key_format'." if @resource[:key_format] && !resource[:key] - fail ArgumentError, - "The 'key_format' property must be set when specifying 'key'." if !@resource[:key_format] && resource[:key] - end + "This provider does not support the 'enable' property." if @resource[:enable] + fail ArgumentError, + "The 'key' property must be set when specifying 'key_format'." if @resource[:key_format] && !resource[:key] end def exists? - true + (@property_hash[:ensure] == :present) end def flush validate - # Handle enable setting - if @resource[:enable] - if @resource[:enable].eql?(:true) - @tacacs_global.enable - elsif @resource[:enable].eql?(:false) - @tacacs_global.destroy + TACACS_GLOBAL_PROPS.each do |puppet_prop, cisco_prop| + if @resource[puppet_prop] && @tacacs_global.respond_to?("#{cisco_prop}=") + @tacacs_global.send("#{cisco_prop}=", munge_flush(@resource[puppet_prop])) end end - - # Handle timeout setting - @tacacs_global.timeout = @resource[:timeout] if @resource[:timeout] - - # Handle key and keyformat setting @tacacs_global.send('encryption_key_set', munge_flush(@resource[:key_format]), @resource[:key]) if @resource[:key] end end # Puppet::Type diff --git a/tests/beaker_tests/tacacs_global/tacacs_global_provider_defaults.rb b/tests/beaker_tests/tacacs_global/tacacs_global_provider_defaults.rb index 7f89864ca..ed8dd17b3 100644 --- a/tests/beaker_tests/tacacs_global/tacacs_global_provider_defaults.rb +++ b/tests/beaker_tests/tacacs_global/tacacs_global_provider_defaults.rb @@ -53,7 +53,6 @@ # Require UtilityLib.rb and SnmpGroupLib.rb paths. require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../../tacacs/tacacslib.rb', __FILE__) require File.expand_path('../tacacs_globallib.rb', __FILE__) result = 'PASS' @@ -61,21 +60,14 @@ # @test_name [TestCase] Executes defaults testcase for tacacs_global Resource. test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider' do - on(master, TacacsLib.create_tacacs_manifest_change_disabled) - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - end - # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource present manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. on(master, TacacsGlobalLib.create_tacacs_global_manifest) - # Expected exit_code is 2 since this is a puppet agent cmd with change. + # Expected exit_code is 0 or 2 depending on the state of the device. cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) + on(agent, cmd_str, acceptable_exit_codes: [0, 2]) logger.info("Get resource present manifest from master :: #{result}") end @@ -90,8 +82,6 @@ false, self, logger) search_pattern_in_output(stdout, { 'key_format' => '7' }, false, self, logger) - search_pattern_in_output(stdout, { 'enable' => 'true' }, - false, self, logger) search_pattern_in_output(stdout, { 'timeout' => '2' }, false, self, logger) end @@ -100,7 +90,7 @@ end # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource present (with changes)manifest from master' do + step 'TestStep :: Get resource present (with changes) manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. on(master, TacacsGlobalLib.create_tacacs_global_manifest_change) @@ -121,46 +111,13 @@ false, self, logger) search_pattern_in_output(stdout, { 'key_format' => '7' }, false, self, logger) - search_pattern_in_output(stdout, { 'enable' => 'true' }, - false, self, logger) - search_pattern_in_output(stdout, { 'timeout' => '3' }, + search_pattern_in_output(stdout, { 'timeout' => '1' }, false, self, logger) end logger.info("Check tacacs_global resource presence on agent :: #{result}") end - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource present (with changes)manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, TacacsGlobalLib.create_tacacs_global_manifest_change_disabled) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource present manifest from master :: #{result}") - end - - # @step [Step] Checks tacacs_global resource on agent using resource cmd. - step 'TestStep :: Check tacacs_global resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + 'resource tacacs_global default' - on(agent, cmd_str) do - search_pattern_in_output(stdout, { 'enable' => 'false' }, - false, self, logger) - end - - logger.info("Check tacacs_global resource presence on agent :: #{result}") - end - - step 'TestStep :: Cleanup' do - on(master, TacacsLib.create_tacacs_manifest_change_disabled) - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - end - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. raise_passfail_exception(result, testheader, self, logger) end diff --git a/tests/beaker_tests/tacacs_global/tacacs_globallib.rb b/tests/beaker_tests/tacacs_global/tacacs_globallib.rb index a3bb4c101..56e9073e0 100644 --- a/tests/beaker_tests/tacacs_global/tacacs_globallib.rb +++ b/tests/beaker_tests/tacacs_global/tacacs_globallib.rb @@ -18,13 +18,13 @@ # tacacs_globallib.rb # # This is the utility library for the Tacacs Global provider Beaker test cases -# that contains the common methods used across the Radius Server testsuite's +# that contains the common methods used across the Tacacs Server testsuite's # cases. The library is implemented as a module with related methods and # constants defined inside it for use as a namespace. All of the methods are # defined as module methods. # -# Every Beaker Radius Server test case that runs an instance of Beaker::TestCase -# requires RadiusSettingLib module. +# Every Beaker Tacacs Server test case that runs an instance of Beaker::TestCase +# requires TacacsSettingLib module. # # The module has a single set of methods: # A. Methods to create manifests for tacacs_global Puppet provider test cases. @@ -35,6 +35,9 @@ # A library to assist testing tacacs_global resource module TacacsGlobalLib + # Group of Constants used in negative tests for tacacs_global provider. + ENSURE_NEGATIVE = 'unknown' + # A. Methods to create manifests for tacacs_global Puppet provider test cases. # Method to create a manifest for tacacs_global @@ -44,7 +47,6 @@ def self.create_tacacs_global_manifest manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { tacacs_global { 'default': - enable => true, key => '44444444', key_format => '7', timeout => '2', @@ -62,25 +64,9 @@ def self.create_tacacs_global_manifest_change manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { tacacs_global { 'default': - enable => true, key => '44444444', key_format => '7', - timeout => '3', - } -} -EOF" - manifest_str - end - - # Method to create a manifest for tacacs_global resource - # with a few properties removed made from above. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_tacacs_global_manifest_change_disabled - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - tacacs_global { 'default': - enable => false, + timeout => '1', } } EOF" From 6bf232867642941c4d586c81efe71e3ad183c4af Mon Sep 17 00:00:00 2001 From: saichint Date: Thu, 30 Jun 2016 12:07:35 -0700 Subject: [PATCH 026/203] router_ospf_area_vlink: new provider (#355) Adds virtual-link support --- CHANGELOG.md | 1 + README.md | 86 ++++++- examples/cisco/demo_ospf.pp | 17 ++ .../provider/cisco_ospf_area_vlink/cisco.rb | 166 +++++++++++++ lib/puppet/type/cisco_ospf_area_vlink.rb | 235 ++++++++++++++++++ .../test_ospf_area_vlink.rb | 109 ++++++++ tests/beaker_tests/lib/utilitylib.rb | 1 + 7 files changed, 613 insertions(+), 2 deletions(-) create mode 100644 lib/puppet/provider/cisco_ospf_area_vlink/cisco.rb create mode 100644 lib/puppet/type/cisco_ospf_area_vlink.rb create mode 100644 tests/beaker_tests/cisco_ospf_area_vlink/test_ospf_area_vlink.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index db3564ef3..dd29f3819 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). #### Cisco Resources - `cisco_bfd_global` type and provider. - `cisco_ospf_area` type and provider. +- `cisco_ospf_area_vlink` type and provider. ### Added - Extend cisco_bgp_neighbor with attributes: diff --git a/README.md b/README.md index e26562719..111769fc5 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,7 @@ A note about support for specific platform models: | [cisco_itd_service](#type-cisco_itd_service) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | \*[caveats](#cisco_itd_service-caveats) | | [cisco_ospf](#type-cisco_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_ospf_area](#type-cisco_ospf_area) | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_ospf_area_vlink](#type-cisco_ospf_area_vlink) | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_ospf_vrf](#type-cisco_ospf_vrf) | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅ = Supported
:heavy_minus_sign: = Not Applicable | N9k | N3k | N5k | N6k | N7k | Caveats | | [cisco_overlay_global](#type-cisco_overlay_global) | ✅ | :heavy_minus_sign: | ✅ | ✅ | ✅ | @@ -285,6 +286,7 @@ The following resources include cisco types and providers along with cisco provi * [`cisco_vrf`](#type-cisco_vrf) * [`cisco_ospf`](#type-cisco_ospf) * [`cisco_ospf_area`](#type-cisco_ospf_area) + * [`cisco_ospf_area_vlink`](#type-cisco_ospf_area_vlink) * [`cisco_ospf_vrf`](#type-cisco_ospf_vrf) * [`cisco_interface_ospf`](#type-cisco_interface_ospf) @@ -381,6 +383,7 @@ The following resources include cisco types and providers along with cisco provi * [`cisco_itd_service`](#type-cisco_itd_service) * [`cisco_ospf`](#type-cisco_ospf) * [`cisco_ospf_area`](#type-cisco_ospf_area) +* [`cisco_ospf_area_vlink`](#type-cisco_ospf_area_vlink) * [`cisco_ospf_vrf`](#type-cisco_ospf_vrf) * [`cisco_overlay_global`](#type-cisco_overlay_global) * [`cisco_pim`](#type-cisco_pim) @@ -2451,7 +2454,6 @@ cisco_ospf_area { 'my_ospf_instance my_vrf 1000': #### Parameters - | Example Parameter Usage | |:--|:-- |`cisco_ospf_area { ' ':` @@ -2479,7 +2481,6 @@ This property defines the area as NSSA (not so stubby area). Valid values are tr ##### `nssa_default_originate` Generates an NSSA External (type 7) LSA for use as a default route to the external autonomous system. Valid values are true, false or keyword 'default'. - ##### `nssa_no_redistribution` Disable redistribution within the NSSA. Valid values are true, false or keyword 'default'. @@ -2515,6 +2516,87 @@ Defines the area as a stub area. Valid values are true, false or keyword 'defaul ##### `stub_no_summary` Stub areas flood summary LSAs. This property disables summary flooding into the area. This property can be used in place of the `stub` property or in conjunction with it. Valid values are true, false or keyword 'default'. This property is mutually exclusive with `nssa`. +-- +### Type: cisco_ospf_area_vlink + +Manages an area virtual link for an OSPF router. + +| Platform | OS Minimum Version | Module Minimum Version | +|----------|:------------------:|:----------------------:| +| N9k | 7.0(3)I3(1) | 1.4.0 | +| N3k | 7.0(3)I3(1) | 1.4.0 | +| N5k | 7.3(0)N1(1) | 1.4.0 | +| N6k | 7.3(0)N1(1) | 1.4.0 | +| N7k | 7.3(0)D1(1) | 1.4.0 | +| N8k | 7.3(0)F1(1) | 1.4.0 | + +#### Example Usage + +```puppet +cisco_ospf_area_vlink { 'my_ospf_instance default 10 1.1.1.1': + ensure => 'present', + auth_key_chain => 'keyChain', + authentication => 'md5', + authentication_key_encryption_type => cisco_type_7, + authentication_key_password => '98765432109876543210', + dead_interval => 500, + hello_interval => 2000, + message_digest_algorithm_type => 'md5', + message_digest_encryption_type => cisco_type_7, + message_digest_key_id => 123, + message_digest_password => '12345678901234567890', + retransmit_interval => 777, + transmit_delay => 333, +} +``` + +#### Parameters + +| Example Parameter Usage | +|:--|:-- +|`cisco_ospf_area_vlink { ' ':` +|`cisco_ospf_area_vlink { '1 my_vrf 10 1.1.1.1':` +|`cisco_ospf_area_vlink { 'my_ospf default 10.1.1.1 2.2.2.2':` + +##### `ensure` +Determines whether the config should be present or not on the device. Valid values are 'present' and 'absent'. + +##### `auth_key_chain` +Authentication password key chain name. Valid values are string, or 'default'. + +##### `authentication` +Enables authentication for the virtual link. Valid values are 'cleartext', 'md5', 'null', or 'default'. + +##### `authentication_key_encryption_type` +Specifies the scheme used for encrypting authentication_key_password. Valid values are 'cleartext', '3des' or 'cisco_type_7' encryption, and 'default', which defaults to 'cleartext'. + +##### `authentication_key_password` +Specifies the authentication_key password. Valid value is a string, or 'default'. + +##### `dead_interval` +Time in seconds that a neighbor waits for a Hello packet before declaring the local router as dead and tearing down adjacencies. Valid values are integer, keyword 'default'. + +##### `hello_interval` +Time in seconds between successive Hello packets. Valid values are integer, keyword 'default'. + +##### `message_digest_algorithm_type` +Algorithm used for authentication among neighboring routers within an area virtual link. Valid values are 'md5' and keyword 'default'. + +##### `message_digest_encryption_type` +Specifies the scheme used for encrypting message_digest_password. Valid values are 'cleartext', '3des' or 'cisco_type_7' encryption, and 'default', which defaults to 'cleartext'. + +##### `message_digest_key_id` +md5 authentication key id. Valid values are integer. + +##### `message_digest_password` +Specifies the message_digest password. Valid value is a string. + +##### `retransmit_interval` +Estimated time in seconds between successive LSAs. Valid values are integer, keyword 'default'. + +##### `transmit_delay` +Estimated time in seconds to transmit an LSA to a neighbor. Valid values are integer, keyword 'default'. + -- ### Type: cisco_ospf_vrf diff --git a/examples/cisco/demo_ospf.pp b/examples/cisco/demo_ospf.pp index bc90eaaa1..09b574048 100644 --- a/examples/cisco/demo_ospf.pp +++ b/examples/cisco/demo_ospf.pp @@ -96,4 +96,21 @@ nssa_route_map => 'aaa', nssa_translate_type7 => 'supress_fa', } + + $auth_password = '3109a60f51374a0d' + cisco_ospf_area_vlink { 'dark_blue vrf2 12345 1.1.1.1': + ensure => 'present', + auth_key_chain => 'myKeyChain', + authentication => md5, + authentication_key_encryption_type => '3des', + authentication_key_password => $auth_password, + dead_interval => 500, + hello_interval => 2000, + message_digest_algorithm_type => md5, + message_digest_encryption_type => cisco_type_7, + message_digest_key_id => 39, + message_digest_password => $md_password, + retransmit_interval => 10000, + transmit_delay => 400, + } } diff --git a/lib/puppet/provider/cisco_ospf_area_vlink/cisco.rb b/lib/puppet/provider/cisco_ospf_area_vlink/cisco.rb new file mode 100644 index 000000000..8d5f4fa37 --- /dev/null +++ b/lib/puppet/provider/cisco_ospf_area_vlink/cisco.rb @@ -0,0 +1,166 @@ +# June, 2016 +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'cisco_node_utils' if Puppet.features.cisco_node_utils? +begin + require 'puppet_x/cisco/autogen' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'autogen.rb')) +end +Puppet::Type.type(:cisco_ospf_area_vlink).provide(:cisco) do + desc 'The Cisco OSPF area virtual link provider.' + + confine feature: :cisco_node_utils + defaultfor operatingsystem: :nexus + + mk_resource_methods + + OSPF_AREA_VLINK_NON_BOOL_PROPS = [ + :auth_key_chain, + :authentication, + :authentication_key_encryption_type, + :authentication_key_password, + :dead_interval, + :hello_interval, + :message_digest_algorithm_type, + :message_digest_encryption_type, + :message_digest_key_id, + :message_digest_password, + :retransmit_interval, + :transmit_delay, + ] + + OSPF_AREA_VLINK_ALL_PROPS = OSPF_AREA_VLINK_NON_BOOL_PROPS + + PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@nu', + OSPF_AREA_VLINK_NON_BOOL_PROPS) + + def initialize(value={}) + super(value) + ospf = @property_hash[:ospf] + vrf = @property_hash[:vrf] + area = @property_hash[:area] + vlink = @property_hash[:vlink] + @nu = Cisco::RouterOspfAreaVirtualLink.virtual_links[ospf][vrf][area][vlink] unless + ospf.nil? || vrf.nil? || area.nil? || vlink.nil? + @property_flush = {} + end + + def self.properties_get(ospf, vrf, area, vlink, nu_obj) + debug "Checking vlink instance, #{ospf} #{vrf} #{area} #{vlink}" + current_state = { + name: "#{ospf} #{vrf} #{area} #{vlink}", + ospf: ospf, + vrf: vrf, + area: area, + vlink: vlink, + ensure: :present, + } + + # Call node_utils getter for each property + (OSPF_AREA_VLINK_NON_BOOL_PROPS).each do |prop| + current_state[prop] = nu_obj.send(prop) + end + new(current_state) + end # self.properties_get + + def self.instances + vlink_instances = [] + Cisco::RouterOspfAreaVirtualLink.virtual_links.each do |ospf, vrfs| + vrfs.each do |vrf, areas| + areas.each do |area, vlinks| + vlinks.each do |vlink, nu_obj| + vlink_instances << properties_get(ospf, vrf, area, vlink, nu_obj) + end + end + end + end + vlink_instances + end # self.instances + + def self.prefetch(resources) + vlink_instances = instances + resources.keys.each do |id| + provider = vlink_instances.find do |vli| + vli.ospf.to_s == resources[id][:ospf].to_s && + vli.vrf.to_s == resources[id][:vrf].to_s && + vli.area.to_s == resources[id][:area].to_s && + vli.vlink.to_s == resources[id][:vlink].to_s + end + resources[id].provider = provider unless provider.nil? + end + end # self.prefetch + + def exists? + @property_hash[:ensure] == :present + end + + def create + @property_flush[:ensure] = :present + end + + def destroy + @property_flush[:ensure] = :absent + end + + def properties_set(new_vlink=false) + OSPF_AREA_VLINK_ALL_PROPS.each do |prop| + next unless @resource[prop] + send("#{prop}=", @resource[prop]) if new_vlink + unless @property_flush[prop].nil? + @nu.send("#{prop}=", @property_flush[prop]) if + @nu.respond_to?("#{prop}=") + end + end + # custom setters which require one-shot multi-param setters + authentication_key_set + message_digest_key_set + end + + def authentication_key_set + pw = @property_flush[:authentication_key_password] ? @property_flush[:authentication_key_password] : @nu.authentication_key_password + enctype = @property_flush[:authentication_key_encryption_type] ? @property_flush[:authentication_key_encryption_type] : @nu.authentication_key_encryption_type + @nu.authentication_key_set(enctype, pw) + end + + def message_digest_key_set + key = @property_flush[:message_digest_key_id] ? @property_flush[:message_digest_key_id] : @nu.message_digest_key_id + pw = @property_flush[:message_digest_password] ? @property_flush[:message_digest_password] : @nu.message_digest_password + algtype = @property_flush[:message_digest_algorithm_type] ? @property_flush[:message_digest_algorithm_type] : @nu.message_digest_algorithm_type + enctype = @property_flush[:message_digest_encryption_type] ? @property_flush[:message_digest_encryption_type] : @nu.message_digest_encryption_type + @nu.message_digest_key_set(key, algtype.to_s, enctype, pw) + end + + def flush + if @property_flush[:ensure] == :absent + @nu.destroy + @nu = nil + else + # Create/Update + new_vlink = false + if @nu.nil? + new_vlink = true + @nu = Cisco::RouterOspfAreaVirtualLink.new(@resource[:ospf], + @resource[:vrf], + @resource[:area], + @resource[:vlink]) + end + properties_set(new_vlink) + end + end +end diff --git a/lib/puppet/type/cisco_ospf_area_vlink.rb b/lib/puppet/type/cisco_ospf_area_vlink.rb new file mode 100644 index 000000000..1b9ba03a4 --- /dev/null +++ b/lib/puppet/type/cisco_ospf_area_vlink.rb @@ -0,0 +1,235 @@ +# Manages the Cisco OSPF area virtual-link configuration resource. +# +# June 2016 +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Puppet::Type.newtype(:cisco_ospf_area_vlink) do + @doc = "Manages an area virtual_link for an OSPF router. + + cisco_ospf_area_vlink {\" \": + ..attributes.. + } + + is the name of the ospf router instance. + is the name of the ospf vrf. + is the name of the ospf area instance. + is the name of the virtual_link instance. + + Examples: + cisco_ospf_area_vlink {'myrouter vrf1 1.1.1.1 8.8.8.8': + ensure => 'present', + auth_key_chain => 'keyChain', + authentication => 'md5', + authentication_key_encryption_type => cisco_type_7, + authentication_key_password => '98765432109876543210', + dead_interval => 500, + hello_interval => 2000, + message_digest_algorithm_type => 'md5', + message_digest_encryption_type => cisco_type_7, + message_digest_key_id => 123, + message_digest_password => '12345678901234567890', + retransmit_interval => 777, + transmit_delay => 333, + } + " + + ensurable + + ################### + # Resource Naming # + ################### + + # Parse out the title to fill in the attributes in these + # patterns. These attributes can be overwritten later. + def self.title_patterns + identity = ->(x) { x } + patterns = [] + + # Below pattern matches both parts of the full composite name. + patterns << [ + /^(\S+) (\S+) (\S+) (\S+)$/, + [ + [:ospf, identity], + [:vrf, identity], + [:area, identity], + [:vlink, identity], + ], + ] + patterns + end + + # Overwrites name method. Original method simply returns self[:name], + # which is no longer valid or complete. + # Would not have failed, but just return nothing useful. + def name + "#{self[:ospf]} #{self[:vrf]} #{self[:area]} #{self[:vlink]}" + end + + newparam(:name) do + desc 'Name of cisco_ospf_area_vlink, not used, but needed for puppet' + end + + newparam(:vlink, namevar: true) do + desc 'Name of the virtual_link instance. Valid values are string.' + end # param vlink + + newparam(:area, namevar: true) do + desc 'Name of the resource instance. Valid values are string.' + munge do |value| + value = IPAddr.new(value.to_i, Socket::AF_INET) unless + value[/\./] + value + end + end # param area + + newparam(:vrf, namevar: true) do + desc "Name of the vrf instance. Valid values are string. The + name 'default' is a valid VRF." + end # param vrf + + newparam(:ospf, namevar: true) do + desc 'Name of the ospf instance. Valid values are string.' + end # param ospf + + ############## + # Attributes # + ############## + + newproperty(:auth_key_chain) do + desc "Authentication password key chain name. Valid + values are string, keyword 'default'. " + + munge { |value| value == 'default' ? :default : value } + end # property auth_key_chain + + newproperty(:authentication) do + desc 'Enable authentication for the virtual_link.' + + newvalues(:cleartext, :md5, :null, :default) + end # property authentication + + newproperty(:authentication_key_encryption_type) do + desc "Specifies the scheme used for encrypting + authentication key password. Valid values are + 'cleartext', '3des' or 'cisco_type_7' encryption, + and 'default', which defaults to 'cleartext'." + + munge(&:to_sym) + newvalues(:cleartext, :'3des', :cisco_type_7, :default) + end # property authentication_key_encryption_type + + newproperty(:authentication_key_password) do + desc "Specifies the authentication key password. Valid values are + string, keyword 'default'" + + munge { |value| value == 'default' ? :default : value } + end # property authentication_key_password + + newproperty(:dead_interval) do + desc "Sets the time in seconds that a neighbor waits for a Hello packet + before declaring the local router as dead and tearing down + adjacencies. Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property dead_interval + + newproperty(:hello_interval) do + desc "Sets the time in seconds between successive Hello packets. + Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property hello_interval + + newproperty(:message_digest_algorithm_type) do + desc "Algorithm used for authentication among neighboring routers + within an area virtual link. Valid values are 'md5', + keyword 'default'." + + newvalues(:md5, :default) + end # property message_digest_algorithm_type + + newproperty(:message_digest_encryption_type) do + desc "Specifies the scheme used for encrypting + message digest password. Valid values are + 'cleartext', '3des' or 'cisco_type_7' encryption, + and 'default', which defaults to 'cleartext'." + + munge(&:to_sym) + newvalues(:cleartext, :'3des', :cisco_type_7, :default) + end # property message_digest_encryption_type + + newproperty(:message_digest_key_id) do + desc 'md5 authentication key id. Valid values are integer.' + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property message_digest_key_id + + newproperty(:message_digest_password) do + desc 'Specifies the message digest password. Valid values are + string.' + + munge { |value| value == 'default' ? :default : value } + end # property message_digest_password + + newproperty(:retransmit_interval) do + desc "Sets the estimated time in seconds between successive LSAs. + Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property retransmit_interval + + newproperty(:transmit_delay) do + desc "Sets the estimated time in seconds to transmit an LSA to + a neighbor. Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property transmit_delay + + def check_authentication + return unless self[:authentication_key_password] == :default || + self[:authentication_key_password] == '' + var = :authentication_key_encryption_type + fail ArgumentError, + 'authentication_key_encryption_type MUST be default when authentication_key_password is default' unless + self[var].nil? || self[var] == :default || self[var] == :cleartext + end + + def check_message_digest + return if self[:message_digest_password].nil? + if self[:message_digest_password] == :default || + self[:message_digest_password] == '' + vars = [ + :message_digest_algorithm_type, + :message_digest_encryption_type, + :message_digest_key_id, + ] + vars.each do |p| + fail ArgumentError, + 'All message_digest params should be default when message_digest_password is default' unless + self[p].nil? || self[p] == :default || self[p] == :cleartext + end + else + fail ArgumentError, + 'message_digest_key_id cannot be default when message_digest_password is not default' if + self[:message_digest_key_id].nil? || self[:message_digest_key_id] == :default + end + end + + validate do + check_authentication + check_message_digest + end +end diff --git a/tests/beaker_tests/cisco_ospf_area_vlink/test_ospf_area_vlink.rb b/tests/beaker_tests/cisco_ospf_area_vlink/test_ospf_area_vlink.rb new file mode 100644 index 000000000..45bad9a2c --- /dev/null +++ b/tests/beaker_tests/cisco_ospf_area_vlink/test_ospf_area_vlink.rb @@ -0,0 +1,109 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### + +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# Test hash top-level keys +tests = { + master: master, + agent: agent, + operating_system: 'nexus', + resource_name: 'cisco_ospf_area_vlink', +} + +# Test hash test cases +tests[:default] = { + desc: '1.1 Defaults', + title_pattern: 'dark_blue default 1.1.1.1 2.2.2.2', + preclean: 'cisco_ospf', + manifest_props: { + auth_key_chain: 'default', + authentication: 'default', + authentication_key_encryption_type: 'default', + authentication_key_password: 'default', + dead_interval: 'default', + hello_interval: 'default', + message_digest_algorithm_type: 'default', + message_digest_encryption_type: 'default', + message_digest_key_id: 'default', + message_digest_password: 'default', + retransmit_interval: 'default', + transmit_delay: 'default', + }, + code: [0, 2], + resource: { + authentication_key_encryption_type: 'cleartext', + dead_interval: 40, + hello_interval: 10, + message_digest_algorithm_type: 'md5', + message_digest_encryption_type: 'cleartext', + message_digest_key_id: 0, + retransmit_interval: 5, + transmit_delay: 1, + }, +} + +# Non-default Tests. NOTE: [:resource] = [:manifest_props] for all non-default + +tests[:non_default] = { + desc: '2.1 Non Defaults', + title_pattern: 'dark_blue default 1.1.1.1 2.2.2.2', + preclean: 'cisco_ospf', + manifest_props: { + auth_key_chain: 'testKeyChain', + authentication: 'md5', + authentication_key_encryption_type: '3des', + authentication_key_password: '3109a60f51374a0d', + dead_interval: '500', + hello_interval: '2000', + message_digest_algorithm_type: 'md5', + message_digest_encryption_type: 'cisco_type_7', + message_digest_key_id: '82', + message_digest_password: '046E1803362E595C260E0B240619050A2D', + retransmit_interval: '1000', + transmit_delay: '400', + }, +} + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + test_harness_run(tests, :default) + + id = :default + tests[id][:ensure] = :absent + tests[id].delete(:preclean) + test_harness_run(tests, id) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + + test_harness_run(tests, :non_default) + resource_absent_cleanup(agent, 'cisco_ospf') +end + +logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 4b7fdefc7..bcbeec40c 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1066,6 +1066,7 @@ def platform_supports_test(tests, id) logger.error("\n#{tests[id][:desc]} :: #{id} :: SKIP") logger.error("Platform type does not match testcase platform regexp: /#{plat}/") else + platform return true end tests[:skipped] ||= [] From 345993cd5b07749dcfbdf8740cf1136864404bc2 Mon Sep 17 00:00:00 2001 From: saichint Date: Thu, 7 Jul 2016 12:04:26 -0700 Subject: [PATCH 027/203] Fixes for cisco_interface_ospf provider (#357) * Fix interface_ospf provider and beaker * doc * doc * review comments --- CHANGELOG.md | 1 + README.md | 19 +- .../provider/cisco_interface_ospf/cisco.rb | 117 ++++----- lib/puppet/type/cisco_interface_ospf.rb | 92 +------ .../ospfintf_provider_defaults.rb | 171 ------------- .../ospfintf_provider_negatives.rb | 236 ----------------- .../ospfintf_provider_nondefaults.rb | 198 --------------- .../cisco_interface_ospf/ospfintflib.rb | 238 ------------------ .../test_interface_ospf.rb | 127 ++++++++++ 9 files changed, 200 insertions(+), 999 deletions(-) delete mode 100644 tests/beaker_tests/cisco_interface_ospf/ospfintf_provider_defaults.rb delete mode 100644 tests/beaker_tests/cisco_interface_ospf/ospfintf_provider_negatives.rb delete mode 100644 tests/beaker_tests/cisco_interface_ospf/ospfintf_provider_nondefaults.rb delete mode 100644 tests/beaker_tests/cisco_interface_ospf/ospfintflib.rb create mode 100644 tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index dd29f3819..291690e54 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Removed ### Changed +- `cisco_interface_ospf` type and provider so that the properties accept 'default' keyword. ## [1.3.2] - 2016-05-20 diff --git a/README.md b/README.md index 111769fc5..838e1a20f 100644 --- a/README.md +++ b/README.md @@ -2115,6 +2115,15 @@ Shutdown state of the interface service vni. Valid values are 'true', 'false', o ### Type: cisco_interface_ospf Manages configuration of an OSPF interface instance. +| Platform | OS Minimum Version | Module Minimum Version | +|----------|:------------------:|:----------------------:| +| N9k | 7.0(3)I2(1) | 1.2.0 | +| N3k | 7.0(3)I2(1) | 1.2.0 | +| N5k | 7.3(0)N1(1) | 1.2.0 | +| N6k | 7.3(0)N1(1) | 1.2.0 | +| N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.3(0)F1(1) | 1.2.0 | + #### Parameters ##### `ensure` @@ -2128,7 +2137,7 @@ Name of this cisco_interface resource. Valid value is a string. Name of the cisco_ospf resource. Valid value is a string. ##### `cost` -The cost associated with this cisco_interface_ospf instance. Valid value is an integer. +The cost associated with this cisco_interface_ospf instance. Valid value is an integer or the keyword 'default'. ##### `hello_interval` The hello_interval associated with this cisco_interface_ospf instance. Time @@ -2143,17 +2152,17 @@ adjacencies. Valid values are an integer or the keyword 'default'. ##### `passive_interface` Passive interface associated with the cisco_interface_ospf instance. Setting to true will prevent this interface from receiving HELLO packets. -Valid values are 'true' and 'false'. +Valid values are 'true' and 'false' or the keyword 'default'. ##### `message_digest` Enables or disables the usage of message digest authentication. -Valid values are 'true' and 'false'. +Valid values are 'true' and 'false' or the keyword 'default'. ##### `message_digest_key_id` md5 authentication key-id associated with the cisco_interface_ospf instance. If this is present in the manifest, message_digest_encryption_type, message_digest_algorithm_type and message_digest_password are mandatory. -Valid value is an integer. +Valid value is an integer or the keyword 'default'. ##### `message_digest_algorithm_type` Algorithm used for authentication among neighboring routers within an area. @@ -2165,7 +2174,7 @@ Valid values are 'cleartext', '3des' or 'cisco_type_7' encryption, and 'default', which defaults to 'cleartext'. ##### `message_digest_password` -Specifies the message_digest password. Valid value is a string. +Specifies the message_digest password. Valid value is a string or the keyword 'default'. ##### `area` *Required*. Ospf area associated with this cisco_interface_ospf instance. Valid values are a string, formatted as an IP address (i.e. "0.0.0.0") or as an integer. diff --git a/lib/puppet/provider/cisco_interface_ospf/cisco.rb b/lib/puppet/provider/cisco_interface_ospf/cisco.rb index c0b169bba..7a0cfac05 100644 --- a/lib/puppet/provider/cisco_interface_ospf/cisco.rb +++ b/lib/puppet/provider/cisco_interface_ospf/cisco.rb @@ -31,29 +31,28 @@ mk_resource_methods - # Getter properties that need to generate the getter functions - GETTER_PROPS = [ - :hello_interval, :dead_interval + INTF_OSPF_NON_BOOL_PROPS = [ + :area, + :cost, + :hello_interval, + :dead_interval, + :message_digest_key_id, + :message_digest_algorithm_type, + :message_digest_encryption_type, + :message_digest_password, ] - # Setter properties - SETTER_NON_BOOL_PROPS = [ - :cost, :hello_interval, :dead_interval, :message_digest_key_id, - :message_digest_password, :area + INTF_OSPF_BOOL_PROPS = [ + :passive_interface, + :message_digest, ] - SETTER_BOOL_PROPS = [ - :passive_interface, :message_digest - ] - - ALL_SETTER_PROPS = SETTER_NON_BOOL_PROPS + SETTER_BOOL_PROPS + INTF_OSPF_ALL_PROPS = INTF_OSPF_NON_BOOL_PROPS + INTF_OSPF_BOOL_PROPS - PuppetX::Cisco::AutoGen.mk_puppet_getters_non_bool(self, '@interface_ospf', - GETTER_PROPS) - PuppetX::Cisco::AutoGen.mk_puppet_setters_non_bool(self, '@interface_ospf', - SETTER_NON_BOOL_PROPS) - PuppetX::Cisco::AutoGen.mk_puppet_setters_bool(self, '@interface_ospf', - SETTER_BOOL_PROPS) + PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@interface_ospf', + INTF_OSPF_NON_BOOL_PROPS) + PuppetX::Cisco::AutoGen.mk_puppet_methods(:bool, self, '@interface_ospf', + INTF_OSPF_BOOL_PROPS) def initialize(value={}) super(value) @@ -70,15 +69,17 @@ def self.properties_get(interface_name, interface_ospf) } # Call node_utils getter for each property - ALL_SETTER_PROPS.each do |prop| + INTF_OSPF_NON_BOOL_PROPS.each do |prop| current_state[prop] = interface_ospf.send(prop) end - - current_state[:passive_interface] = - current_state[:passive_interface].to_s.to_sym - current_state[:message_digest] = - current_state[:message_digest].to_s.to_sym - + INTF_OSPF_BOOL_PROPS.each do |prop| + val = interface_ospf.send(prop) + if val.nil? + current_state[prop] = nil + else + current_state[prop] = val ? :true : :false + end + end new(current_state) end # self.properties_get @@ -117,65 +118,39 @@ def destroy end def properties_set(new_instance=false) - ALL_SETTER_PROPS.each do |prop| - send("#{prop}=", @resource[prop]) if new_instance && @resource[prop] + INTF_OSPF_ALL_PROPS.each do |prop| + next unless @resource[prop] + send("#{prop}=", @resource[prop]) if new_instance unless @property_flush[prop].nil? @interface_ospf.send("#{prop}=", @property_flush[prop]) if @interface_ospf.respond_to?("#{prop}=") end end + # custom setters which require one-shot multi-param setters + message_digest_key_set + end - if @property_flush[:message_digest_key_id].nil? - should_message_digest_key_id = @interface_ospf.message_digest_key_id - else - should_message_digest_key_id = @property_flush[:message_digest_key_id] - end - - if @property_flush[:message_digest_password].nil? - should_message_digest_password = @interface_ospf.message_digest_password - else - should_message_digest_password = @property_flush[:message_digest_password] - end - - # should_message_digest_password could still be nil if not configured on box - should_message_digest_password = '' if should_message_digest_password.nil? - - if @resource[:message_digest_algorithm_type].nil? - should_message_digest_algorithm_type = @interface_ospf.message_digest_algorithm_type - else - should_message_digest_algorithm_type = @resource[:message_digest_algorithm_type] - end - - if @resource[:message_digest_encryption_type].nil? - should_message_digest_encryption_type = @interface_ospf.message_digest_encryption_type - else - should_message_digest_encryption_type = @resource[:message_digest_encryption_type] - end - - return unless (new_instance && - (should_message_digest_key_id != - @interface_ospf.default_message_digest_key_id)) || - (@property_flush[:message_digest_key_id] && !new_instance) || - (@property_flush[:message_digest_password] && !new_instance) - - @interface_ospf.message_digest_key_set( - should_message_digest_key_id, - should_message_digest_algorithm_type.to_s, - should_message_digest_encryption_type, - should_message_digest_password) + def message_digest_key_set + key = @property_flush[:message_digest_key_id] ? @property_flush[:message_digest_key_id] : @interface_ospf.message_digest_key_id + pw = @property_flush[:message_digest_password] ? @property_flush[:message_digest_password] : @interface_ospf.message_digest_password + algtype = @property_flush[:message_digest_algorithm_type] ? @property_flush[:message_digest_algorithm_type] : @interface_ospf.message_digest_algorithm_type + enctype = @property_flush[:message_digest_encryption_type] ? @property_flush[:message_digest_encryption_type] : @interface_ospf.message_digest_encryption_type + @interface_ospf.message_digest_key_set(key, algtype.to_s, enctype, pw) end def flush if @property_flush[:ensure] == :absent @interface_ospf.destroy @interface_ospf = nil - elsif @property_flush[:ensure] == :present - @interface_ospf = Cisco::InterfaceOspf.new(@resource[:interface], - @resource[:ospf], - @resource[:area]) - properties_set(true) else - properties_set + new_instance = false + if @interface_ospf.nil? + new_instance = true + @interface_ospf = Cisco::InterfaceOspf.new(@resource[:interface], + @resource[:ospf], + @resource[:area]) + end + properties_set(new_instance) end end end diff --git a/lib/puppet/type/cisco_interface_ospf.rb b/lib/puppet/type/cisco_interface_ospf.rb index b919df640..1f60043ce 100644 --- a/lib/puppet/type/cisco_interface_ospf.rb +++ b/lib/puppet/type/cisco_interface_ospf.rb @@ -85,16 +85,9 @@ def name newproperty(:cost) do desc "The cost associated with this cisco_interface_ospf - instance. Valid values are integer." + instance. Valid values are integer, keyword 'default'" - munge do |value| - begin - value = Integer(value) - rescue - raise 'cost property must be an integer.' - end - value - end + munge { |value| value == 'default' ? :default : Integer(value) } end newproperty(:hello_interval) do @@ -102,15 +95,7 @@ def name instance. Time between sending successive hello packets. Valid values are integer, keyword 'default'." - munge do |value| - begin - value = :default if value == 'default' - value = Integer(value) unless value == :default - rescue - raise 'hello_interval property must be an integer.' - end - value - end + munge { |value| value == 'default' ? :default : Integer(value) } end newproperty(:dead_interval) do @@ -119,15 +104,7 @@ def name packet before tearing down adjacencies. Valid values are integer, keyword 'default'." - munge do |value| - begin - value = :default if value == 'default' - value = Integer(value) unless value == :default - rescue - raise 'dead_interval property must be an integer.' - end - value - end + munge { |value| value == 'default' ? :default : Integer(value) } end newproperty(:passive_interface) do @@ -135,14 +112,14 @@ def name instance. Setting to true will prevent this interface from receiving HELLO packets." - newvalues(:true, :false) + newvalues(:true, :false, :default) end newproperty(:message_digest) do desc "Enables or disables the usage of message digest authentication. " - newvalues(:true, :false) + newvalues(:true, :false, :default) end newproperty(:message_digest_key_id) do @@ -152,36 +129,26 @@ def name message_digest_algorithm_type and message_digest_password are mandatory. Valid values are integer." - munge do |value| - begin - value = Integer(value) - rescue - raise "message_digest_key_id provided in the manifest - #{value} is not a valid integer." - end - value - end + munge { |value| value == 'default' ? :default : Integer(value) } end - newparam(:message_digest_algorithm_type) do + newproperty(:message_digest_algorithm_type) do desc "Algorithm used for authentication among neighboring routers within an area. Keyword: 'default'" - munge do |value| - value = :md5 if value == 'default' - value.to_sym - end newvalues(:md5, :default) end - newparam(:message_digest_encryption_type) do + newproperty(:message_digest_encryption_type) do desc "Specifies the scheme used for encrypting message_digest_password. Valid values are 'cleartext', '3des' or 'cisco_type_7' encryption, and 'default', which defaults to 'cleartext'." + munge(&:to_sym) newvalues(:clear, :cleartext, - :"3des", + :'3des', :cisco_type_7, :encrypted, :default) @@ -203,10 +170,7 @@ def name newproperty(:message_digest_password) do desc 'Specifies the message_digest password. Valid values are string.' - validate do |message_digest_password| - fail("message_digest_password - #{message_digest_password} should be a string") \ - unless message_digest_password.nil? || message_digest_password.kind_of?(String) - end + munge { |value| value == 'default' ? :default : value } end newproperty(:area) do @@ -274,36 +238,4 @@ def name fail 'passive_interface value cannot be set on loopback interfaces' end end - - ################ - # Autorequires # - ################ - - # Autorequire cisco_interface; do not fail if it is not present in the manifest - autorequire(:cisco_interface) do |rel_catalog| - reqs = [] - - interface_title = self[:interface] - - dep = rel_catalog.catalog.resource('cisco_interface', interface_title) - - info "Cisco_interface[#{interface_title}] was not found in catalog. " \ - 'Will obtain from device.' if dep.nil? - reqs << dep - reqs - end - - # Autorequire cisco_ospf; do not fail if it is not present in the manifest - autorequire(:cisco_ospf) do |rel_catalog| - reqs = [] - - ospf_title = self[:ospf] - - dep = rel_catalog.catalog.resource('cisco_ospf', ospf_title) - - info "Cisco_ospf[#{ospf_title}] was not found in catalog. " \ - 'Will obtain from device.' if dep.nil? - reqs << dep - reqs - end end diff --git a/tests/beaker_tests/cisco_interface_ospf/ospfintf_provider_defaults.rb b/tests/beaker_tests/cisco_interface_ospf/ospfintf_provider_defaults.rb deleted file mode 100644 index a743e5b1e..000000000 --- a/tests/beaker_tests/cisco_interface_ospf/ospfintf_provider_defaults.rb +++ /dev/null @@ -1,171 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# OspfIntf-Provider-Defaults.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet OSPFINTF resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a OSPFINTF resource test that tests for default values for -# cost, dead_interval, hello_interval, message_digest, message_digest_key_id -# and passive_interface attributes of a -# cisco_interface_ospf resource when created with 'ensure' => 'present'. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-4 deal with cisco_interface_ospf resource creation and its -# verification using Puppet Agent and the switch running-config. -# Steps 5-7 deal with cisco_interface_ospf resource deletion and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and OspfIntfLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../ospfintflib.rb', __FILE__) - -result = 'PASS' -testheader = 'OSPFINTF Resource :: All Attributes Defaults' - -# Local tests hash and helper method used to dynamically find an available -# interface for cisco_interface_ospf tests. -tests = { - intf_type: 'ethernet', - agent: agent, - resource_name: 'cisco_interface_ospf', -} -def find_ospf_interface(tests) - if tests[:ethernet] - intf = tests[:ethernet] - else - intf = find_interface(tests) - # cache for later tests - tests[:ethernet] = intf - end - intf -end -interface = find_ospf_interface(tests) - -# @test_name [TestCase] Executes defaults testcase for OSPFINTF Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - resource_absent_cleanup(agent, 'cisco_interface_ospf', - 'Setup switch for cisco_interface_ospf provider test') - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource present manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfIntfLib.create_ospfintf_manifest_present(interface)) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource present manifest from master :: #{result}") - end - - # @step [Step] Test idempotence by running the same manifest - step 'TestStep :: Test idempotence by running the same manifest' do - # Expected exit_code is 0 since there should not be any changes this time - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0]) - - logger.info("Test idempotence by running the same manifest :: #{result}") - end - - # @step [Step] Checks cisco_intf_ospf resource on agent using resource cmd. - step 'TestStep :: Check cisco_intf_ospf resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_interface_ospf '#{interface} test'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'cost' => '1', - 'dead_interval' => '40', - 'hello_interval' => '10', - 'message_digest' => 'false', - 'message_digest_key_id' => '0', - 'passive_interface' => 'false' }, - false, self, logger) - end - - logger.info("Check cisco_intf_ospf resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource absent manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfIntfLib.create_ospfintf_manifest_absent(interface)) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource absent manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_intf_ospf resource on agent using resource cmd. - step 'TestStep :: Check cisco_intf_ospf resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_interface_ospf '#{interface} test'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'cost' => '1', - 'dead_interval' => '40', - 'hello_interval' => '10', - 'message_digest' => 'false', - 'message_digest_key_id' => '0', - 'passive_interface' => 'false' }, - true, self, logger) - end - - logger.info("Check cisco_intf_ospf resource absence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_interface_ospf/ospfintf_provider_negatives.rb b/tests/beaker_tests/cisco_interface_ospf/ospfintf_provider_negatives.rb deleted file mode 100644 index 2281d8bce..000000000 --- a/tests/beaker_tests/cisco_interface_ospf/ospfintf_provider_negatives.rb +++ /dev/null @@ -1,236 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# OspfIntf-Provider-Negatives.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet OSPFINTF resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a OSPFINTF resource test that tests for negative values for -# cost, dead_interval, hello_interval, message_digest, message_digest_key_id -# and passive_interface attributes of a -# cisco_interface_ospf resource when created with 'ensure' => 'present'. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# The next set of teststeps deal with attribute negative tests and their -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and OspfIntfLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../ospfintflib.rb', __FILE__) - -result = 'PASS' -testheader = 'OSPFINTF Resource :: All Attributes Negatives' - -# Local tests hash and helper method used to dynamically find an available -# interface for cisco_interface_ospf tests. -tests = { - intf_type: 'ethernet', - agent: agent, - resource_name: 'cisco_interface_ospf', -} -def find_ospf_interface(tests) - if tests[:ethernet] - intf = tests[:ethernet] - else - intf = find_interface(tests) - # cache for later tests - tests[:ethernet] = intf - end - intf -end -interface = find_ospf_interface(tests) - -# @test_name [TestCase] Executes negatives testcase for OSPFINTF Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - resource_absent_cleanup(agent, 'cisco_interface_ospf', - 'Setup switch for cisco_interface_ospf provider test') - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfIntfLib.create_ospfintf_manifest_cost_negative(interface)) - - # Expected exit_code is 6 since this is a puppet agent cmd with failure. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [6]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_intf_ospf resource on agent using resource cmd. - step 'TestStep :: Check cisco_intf_ospf resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_interface_ospf '#{interface} test'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'cost' => OspfIntfLib::COST_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_intf_ospf resource absence on agent :: #{result}") - end - - # @step [Step] Checks ospfintf instance on agent using switch show cli cmds. - step 'TestStep :: Check ospfintf instance absence on agent' do - # Cleanup partially configured resource. - cmd_str = PUPPET_BINPATH + "resource cisco_interface_ospf '#{interface} test' ensure=absent" - on(agent, cmd_str) - - logger.info("Check ospfintf instance absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfIntfLib.create_ospfintf_manifest_hellointerval_negative(interface)) - - # Expected exit_code is 6 since this is a puppet agent cmd with failure. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [6]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_intf_ospf resource on agent using resource cmd. - step 'TestStep :: Check cisco_intf_ospf resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_interface_ospf '#{interface} test'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'hello_interval' => OspfIntfLib::HELLOINTERVAL_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_intf_ospf resource absence on agent :: #{result}") - end - - # @step [Step] Checks ospfintf instance on agent using switch show cli cmds. - step 'TestStep :: Check ospfintf instance absence on agent' do - # Cleanup partially configured resource. - cmd_str = PUPPET_BINPATH + "resource cisco_interface_ospf '#{interface} test' ensure=absent" - on(agent, cmd_str) - - logger.info("Check ospfintf instance absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfIntfLib.create_ospfintf_manifest_deadinterval_negative(interface)) - - # Expected exit_code is 6 since this is a puppet agent cmd with failure. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [6]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_intf_ospf resource on agent using resource cmd. - step 'TestStep :: Check cisco_intf_ospf resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_interface_ospf '#{interface} test'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'dead_interval' => OspfIntfLib::DEADINTERVAL_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_intf_ospf resource absence on agent :: #{result}") - end - - # @step [Step] Checks ospfintf instance on agent using switch show cli cmds. - step 'TestStep :: Check ospfintf instance absence on agent' do - # Cleanup partially configured resource. - cmd_str = PUPPET_BINPATH + "resource cisco_interface_ospf '#{interface} test' ensure=absent" - on(agent, cmd_str) - - logger.info("Check ospfintf instance absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfIntfLib.create_ospfintf_manifest_passiveintf_negative(interface)) - - # Expected exit_code is 1 since this is a puppet agent cmd with error. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [1]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_intf_ospf resource on agent using resource cmd. - step 'TestStep :: Check cisco_intf_ospf resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_interface_ospf '#{interface} test'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'passive_interface' => OspfIntfLib::PASSIVEINTF_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_intf_ospf resource absence on agent :: #{result}") - end - - # @step [Step] Checks ospfintf instance on agent using switch show cli cmds. - step 'TestStep :: Check ospfintf instance absence on agent' do - # Cleanup partially configured resource. - cmd_str = PUPPET_BINPATH + "resource cisco_interface_ospf '#{interface} test' ensure=absent" - on(agent, cmd_str) - - logger.info("Check ospfintf instance absence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_interface_ospf/ospfintf_provider_nondefaults.rb b/tests/beaker_tests/cisco_interface_ospf/ospfintf_provider_nondefaults.rb deleted file mode 100644 index 0487ea1b4..000000000 --- a/tests/beaker_tests/cisco_interface_ospf/ospfintf_provider_nondefaults.rb +++ /dev/null @@ -1,198 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# OspfIntf-Provider-NonDefaults.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet OSPFINTF resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a OSPFINTF resource test that tests for nondefault values for -# cost, dead_interval, hello_interval, message_digest, message_digest_key_id -# and passive_interface attributes of a -# cisco_interface_ospf resource when created with 'ensure' => 'present'. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-4 deal with cisco_interface_ospf resource creation and its -# verification using Puppet Agent and the switch running-config. -# Steps 5-7 deal with cisco_interface_ospf resource deletion and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and OspfIntfLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../ospfintflib.rb', __FILE__) - -result = 'PASS' -testheader = 'OSPFINTF Resource :: All Attributes NonDefaults' - -# Local tests hash and helper method used to dynamically find an available -# interface for cisco_interface_ospf tests. -tests = { - intf_type: 'ethernet', - agent: agent, - resource_name: 'cisco_interface_ospf', -} -def find_ospf_interface(tests) - if tests[:ethernet] - intf = tests[:ethernet] - else - intf = find_interface(tests) - # cache for later tests - tests[:ethernet] = intf - end - intf -end -interface = find_ospf_interface(tests) - -# @test_name [TestCase] Executes nondefaults testcase for OSPFINTF Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - resource_absent_cleanup(agent, 'cisco_interface_ospf', - 'Setup switch for cisco_ospf provider test') - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource nondefaults manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfIntfLib.create_ospfintf_manifest_nondefaults(interface)) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource nondefaults manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_intf_ospf resource on agent using resource cmd. - step 'TestStep :: Check cisco_intf_ospf resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_interface_ospf '#{interface} test'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'cost' => '100', - 'dead_interval' => '80', - 'hello_interval' => '20', - 'message_digest' => 'false', - 'message_digest_key_id' => '0', - 'passive_interface' => 'true' }, - false, self, logger) - end - - logger.info("Check cisco_intf_ospf resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource absent manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfIntfLib.create_ospfintf_manifest_absent(interface)) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource absent manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_intf_ospf resource on agent using resource cmd. - step 'TestStep :: Check cisco_intf_ospf resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_interface_ospf '#{interface} test'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'cost' => '100', - 'dead_interval' => '80', - 'hello_interval' => '20', - 'message_digest' => 'false', - 'message_digest_key_id' => '0', - 'passive_interface' => 'true' }, - true, self, logger) - end - - logger.info("Check cisco_intf_ospf resource absence on agent :: #{result}") - end - - # @step [Step] Verify ability to configure and update ospf area - step 'TestStep :: Check ospfintf area change on agent' do - area1 = '0.0.0.5' - area2 = '0.0.0.6' - on(master, OspfIntfLib.create_ospfintf_area_manifest(area1, interface)) - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - # check for successfull configured area - cmd_str = PUPPET_BINPATH + "resource cisco_interface_ospf '#{interface} test'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'area' => "#{area1}" }, - false, self, logger) - end - # update area to area2 - on(master, OspfIntfLib.create_ospfintf_area_manifest(area2, interface)) - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - # check for successfull configured area - cmd_str = PUPPET_BINPATH + "resource cisco_interface_ospf '#{interface} test'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'area' => "#{area2}" }, - false, self, logger) - end - # cleanup - cmd_str = PUPPET_BINPATH + "resource cisco_interface_ospf '#{interface} test' ensure=absent" - on(agent, cmd_str) - - logger.info("Check ospfintf area change on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_interface_ospf/ospfintflib.rb b/tests/beaker_tests/cisco_interface_ospf/ospfintflib.rb deleted file mode 100644 index 5d2c64721..000000000 --- a/tests/beaker_tests/cisco_interface_ospf/ospfintflib.rb +++ /dev/null @@ -1,238 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### - -# Require UtilityLib.rb path. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) - -# OSPFVRF Utility Library: -# ------------------------ -# ospfintflib.rb -# -# This is the utility library for the OSPFINTF provider Beaker test cases that -# contains the common methods used across the OSPFINTF testsuite's cases. The -# library is implemented as a module with related methods and constants defined -# inside it for use as a namespace. All of the methods are defined as module -# methods. -# -# Every Beaker OSPFINTF test case that runs an instance of Beaker::TestCase -# requires OspfIntfLib module. -# -# The module has a single set of methods: -# A. Methods to create manifests for cisco_intf_ospf Puppet provider test cases. -module OspfIntfLib - # Group of Constants used in negative tests for OSPFINTF provider. - COST_NEGATIVE = '-1' - HELLOINTERVAL_NEGATIVE = '-1' - DEADINTERVAL_NEGATIVE = '-1' - PASSIVEINTF_NEGATIVE = 'invalid' - - # A. Methods to create manifests for cisco_intf_ospf Puppet provider test cases. - - # Method to create a manifest for OSPFINTF resource attribute 'ensure' where - # 'ensure' is set to present. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ospfintf_manifest_present(intf) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_ospf { 'test': - ensure => present, - } - - cisco_interface { '#{intf}': - switchport_mode => disabled, - } - - cisco_interface_ospf { \"#{intf} test\": - ensure => present, - area => '1', - cost => '1', - hello_interval => 'default', - dead_interval => 'default', - passive_interface => 'false', - } -} -EOF" - manifest_str - end - - # Method to configure the given area inside a manifest for ensure present - # @param area is used to set the area for the manifest - # @param intf is used to optionally specify the interface to use - # @result manifest_str is the newly constructed manifest - def self.create_ospfintf_area_manifest(area, intf) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_ospf { 'test': - ensure => present, - } - - cisco_interface { '#{intf}': - switchport_mode => disabled, - } - - cisco_interface_ospf { '#{intf} test': - ensure => present, - area => \"#{area}\", - } -} -EOF" - manifest_str - end - - # Method to create a manifest for OSPFINTF resource attribute 'ensure' where - # 'ensure' is set to absent. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ospfintf_manifest_absent(intf) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_interface_ospf { '#{intf} test': - ensure => absent, - } - - cisco_ospf { 'test': - ensure => absent, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for OSPFINTF resource attributes: - # ensure, cost, dead_interval, hello_interval, area and passive_interface. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ospfintf_manifest_nondefaults(intf) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_ospf { 'test': - ensure => present, - } - - cisco_interface { '#{intf}': - switchport_mode => disabled, - } - - cisco_interface_ospf { '#{intf} test': - ensure => present, - area => '100', - cost => '100', - hello_interval => '20', - dead_interval => '80', - passive_interface => 'true', - } -} -EOF" - manifest_str - end - - # Method to create a manifest for OSPFINTF resource attribute 'cost'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ospfintf_manifest_cost_negative(intf) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_ospf { 'test': - ensure => present, - } - - cisco_interface { '#{intf}': - switchport_mode => disabled, - } - - cisco_interface_ospf { '#{intf} test': - ensure => present, - area => 1, - cost => #{OspfIntfLib::COST_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for OSPFINTF resource attribute 'hello_interval'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ospfintf_manifest_hellointerval_negative(intf) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_ospf { 'test': - ensure => present, - } - - cisco_interface { '#{intf}': - switchport_mode => disabled, - } - - cisco_interface_ospf { '#{intf} test': - ensure => present, - area => '1', - hello_interval => #{OspfIntfLib::HELLOINTERVAL_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for OSPFINTF resource attribute 'dead_interval'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ospfintf_manifest_deadinterval_negative(intf) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_ospf { 'test': - ensure => present, - } - - cisco_interface { '#{intf}': - switchport_mode => disabled, - } - - cisco_interface_ospf { '#{intf} test': - ensure => present, - area => '1', - dead_interval => #{OspfIntfLib::DEADINTERVAL_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for OSPFINTF resource attribute 'passive_intf'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ospfintf_manifest_passiveintf_negative(intf) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_ospf { 'test': - ensure => present, - } - - cisco_interface { '#{intf}': - switchport_mode => disabled, - } - - cisco_interface_ospf { '#{intf} test': - ensure => present, - area => '1', - passive_interface => #{OspfIntfLib::PASSIVEINTF_NEGATIVE}, - } -} -EOF" - manifest_str - end -end diff --git a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb new file mode 100644 index 000000000..253c5757d --- /dev/null +++ b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb @@ -0,0 +1,127 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### + +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# Test hash top-level keys +tests = { + master: master, + agent: agent, + intf_type: 'ethernet', + operating_system: 'nexus', + resource_name: 'cisco_interface_ospf', +} + +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + +# Find a usable interface for this test +@intf = find_interface(tests) +tp = @intf + ' Sample' + +# Test hash test cases +tests[:default] = { + desc: '1.1 Defaults', + title_pattern: tp, + preclean_intf: true, + manifest_props: { + area: 200, + cost: 'default', + dead_interval: 'default', + hello_interval: 'default', + message_digest: 'default', + message_digest_key_id: 'default', + message_digest_algorithm_type: 'default', + message_digest_encryption_type: 'default', + message_digest_password: 'default', + passive_interface: 'default', + }, + code: [0, 2], + resource: { + area: '0.0.0.200', + cost: 0, + dead_interval: 40, + hello_interval: 10, + message_digest: 'false', + message_digest_key_id: 0, + message_digest_algorithm_type: 'md5', + message_digest_encryption_type: 'cleartext', + passive_interface: 'false', + }, +} + +# Non-default Tests. NOTE: [:resource] = [:manifest_props] for all non-default + +tests[:non_default] = { + desc: '2.1 Non Defaults', + title_pattern: tp, + preclean_intf: true, + manifest_props: { + area: 200, + cost: '200', + dead_interval: '200', + hello_interval: '200', + message_digest: 'true', + message_digest_key_id: 27, + message_digest_algorithm_type: 'md5', + message_digest_encryption_type: 'cisco_type_7', + message_digest_password: '046E1803362E595C260E0B240619050A2D', + passive_interface: 'true', + }, + resource: { + area: '0.0.0.200' + }, +} + +# Overridden to properly handle dependencies for this test file. +def test_harness_dependencies(_tests, id) + return unless id == :default + cmd = [ + 'feature ospf ; router ospf Sample', + "interface #{@intf} ; no switchport", + ].join(' ; ') + command_config(agent, cmd, cmd) +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + test_harness_run(tests, :default) + + id = :default + tests[id][:ensure] = :absent + tests[id].delete(:preclean) + test_harness_run(tests, id) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + + test_harness_run(tests, :non_default) + interface_cleanup(agent, @intf) +end + +logger.info("TestCase :: #{tests[:resource_name]} :: End") From e3866e4821e92e3ce9e68fde951a2b8398a1137a Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Tue, 12 Jul 2016 08:24:47 -0400 Subject: [PATCH 028/203] minor vrf name change * to avoid annoying conflict with 'management' --- tests/beaker_tests/cisco_bgp_neighbor_af/test_bgpneighboraf.rb | 2 +- tests/beaker_tests/cisco_pim/test_pim_grouplist.rb | 2 +- tests/beaker_tests/cisco_pim/test_pim_rp_address.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/beaker_tests/cisco_bgp_neighbor_af/test_bgpneighboraf.rb b/tests/beaker_tests/cisco_bgp_neighbor_af/test_bgpneighboraf.rb index e6a8ca468..27efd4626 100644 --- a/tests/beaker_tests/cisco_bgp_neighbor_af/test_bgpneighboraf.rb +++ b/tests/beaker_tests/cisco_bgp_neighbor_af/test_bgpneighboraf.rb @@ -307,7 +307,7 @@ def dependency_manifest(tests, id) tests[:title_patterns_4] = { desc: 'T.4 Title Pattern', - title_pattern: '11.4 magenta 1.1.1.1 ipv4', + title_pattern: '11.4 orange 1.1.1.1 ipv4', title_params: { safi: 'unicast' }, resource: { 'ensure' => 'present' }, } diff --git a/tests/beaker_tests/cisco_pim/test_pim_grouplist.rb b/tests/beaker_tests/cisco_pim/test_pim_grouplist.rb index cac28183d..2d90d17fc 100644 --- a/tests/beaker_tests/cisco_pim/test_pim_grouplist.rb +++ b/tests/beaker_tests/cisco_pim/test_pim_grouplist.rb @@ -56,7 +56,7 @@ tests[:title_patterns_4] = { desc: 'T.4 Title Pattern', - title_pattern: 'ipv4 magenta 1.1.1.1', + title_pattern: 'ipv4 orange 1.1.1.1', title_params: { group: '224.0.0.0/8' }, resource: { 'ensure' => 'present' }, } diff --git a/tests/beaker_tests/cisco_pim/test_pim_rp_address.rb b/tests/beaker_tests/cisco_pim/test_pim_rp_address.rb index 83ffb9fb8..0a46328cb 100644 --- a/tests/beaker_tests/cisco_pim/test_pim_rp_address.rb +++ b/tests/beaker_tests/cisco_pim/test_pim_rp_address.rb @@ -55,7 +55,7 @@ tests[:title_patterns_4] = { desc: 'T.4 Title Pattern', - title_pattern: 'ipv4 magenta 1.1.1.1', + title_pattern: 'ipv4 orange 1.1.1.1', resource: { 'ensure' => 'present' }, } From d6389098f1c43ae9564312641bc919c212a8f1a7 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Fri, 15 Jul 2016 10:03:25 -0400 Subject: [PATCH 029/203] command_config: undefined method 'previous' (#358) * Symptom: Simple cc manifest with a faulty line of config: cisco_command_config { 'interface': command => ' interface loopback42 ipv6 pim foo ' } When puppet agent runs the cc provider drops into a rescue which should first print out the parts of the config that were successfully processed and then do a raise, but instead we see: ``` Error: undefined method 'previous' for # ``` * Analysis: `client.rb` was refactored a few months ago, wherein the `previous` hash key was renamed to `successful_input`. The cc providers (both puppet & chef) never got updated with the new key name. * Ref: https://github.com/cisco/cisco-network-node-utils/blob/develop/lib/cisco_node_utils/client/nxapi/client.rb#L285 * Testing: ``` Linux# puppet agent -t --debug ... Info: Cisco_command_config[interface](provider=cisco): Successfully updated: interface loopback42 Error: The command 'ipv6 pim foo' was rejected with error: % Invalid command Error: /Stage[main]/Main/Node[default]/Cisco_command_config[interface]/command: change from to interface loopback42 ipv6 pim foo failed: The command 'ipv6 pim foo' was rejected with error: % Invalid command ``` --- lib/puppet/provider/cisco_command_config/cisco.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/puppet/provider/cisco_command_config/cisco.rb b/lib/puppet/provider/cisco_command_config/cisco.rb index 75ab5e7c7..212a02c3b 100644 --- a/lib/puppet/provider/cisco_command_config/cisco.rb +++ b/lib/puppet/provider/cisco_command_config/cisco.rb @@ -82,7 +82,9 @@ def command=(cmds) rescue Cisco::CliError => e # Tell the user what succeeded, then fail with the actual failure. - info "Successfully updated:\n#{e.previous.join("\n")}" unless e.previous.empty? + unless e.successful_input.empty? + info "Successfully updated:\n#{e.successful_input.join("\n")}" + end raise end # command= From b90a9cd36a216ffc1c28b67c912e8dd1a2fa4a67 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Tue, 2 Aug 2016 13:55:38 -0400 Subject: [PATCH 030/203] Merge release_1.3.2 --> develop (#365) * Add note on existing_str * Refactor test_interface.rb into multiple files * This test was trying to do too much; it became too large and unwieldy to use and troubleshoot. * Refactor to use new test syntax (test_harness_run) * File changes: * deleted: test_interface.rb * new file: interfacelib.rb * new file: test_interface_L2.rb * new file: test_interface_L3.rb * new file: test_interface_bdi.rb * modified: test_interface_capabilities.rb * modified: test_interface_stp.rb * new file: test_interface_svi.rb * new file: test_interface_private_vlan.rb * renamed test_vlan_mapping.rb to test_interface_vlan_mapping.rb * Only tested on n7k and n9k so far but there's a lot here and I wanted to get some eyes on the changes * Platform tweaks for test_interface* * test_interface: add skip_unless_supported plat checks * Minor change to bail out of the test immediately if the platform does not apply * test_interface_private_vlan: fix trunk assoc cleanup * Found a bug on n9k where 'private-vlan association trunk' does not get cleaned up by 'default interface', which breaks this beaker test * Created a workaround cleanup method that does 'no switchport' * Tested on N9k-I3 * test_private_vlan: refactor, remove redundancies * Refactored to use new test syntax; removed unnecessary/redundant tests and some that didn't test anything * This test suffers from the same idempotency bug as the interface private-vlan code; it will require the same kind of fix * Tested on n9k-I3, N7 * Bump version number. * Add testing for switchport_autostate_exclude * Remove old and redudant access_vlan tests * Remove old style beaker tests * normalize_range_array for puppet This is the puppet counter-part to NU. The notes below are copied from: https://github.com/cisco/cisco-network-node-utils/pull/400 The main difference between these is a default check in Utils.normalize_range_array and that there's no Utils.dash_range_to_elements since that's not needed in puppet. * The motivation for this change was finding yet another array normalizer in the private vlan code so these changes are initially meant to address fixes there. IMO our range summarize methods were getting a bit long and difficult to maintain; these new methods will hopefully simplify things somewhat. * The main problem these methods are trying to solve is reconciling manifest inputs with getter results when there are overlapping ranges; e.g.: * '2-5, 9, 4-6' needs to be ['2-6', '9'] * ['2', '3', '4-6'] needs to be ['2-6'] * ['2', '3', '4'] needs to be ['2-4'] * It's fairly easy to merge the ranges if they are actual ruby ranges instead of the dash-syntax ranges, so the new methods just convert to ruby ranges, merge, and convert back. * Tested on n9k. Additional testing coming on the other platforms, as well well as demo, Readme, etc. * bgp_af l2vpn * Fix demo_bgp * Fix additional_paths_install * New beaker style vlan tests * l2vpn/evpn support * l2vpn/evpn support * exclude n3k * review comments * Review comments * cisco_interface: deprecate private_vlan* * private_vlan refactor * cisco_interface: private_vlan refactor * Deprecated the private_vlan properties that were released with 1.3.0 and created new pvlan properties in their place. * This is the companion to NU PR: https://github.com/cisco/cisco-network-node-utils/pull/406 * Updated Type, Provider, Beaker, Demo, README, and CHANGELOG. * Currently only tested on N7, N9-i3. Additional testing in prog. * readme and changelog * readme and changelog * Update README.md * pvlan beaker fixups for 6k * The 6k cli is strict about requiring vlan associations before allowing interface vlan association; therefore I added the dependencies to the test. I need to rerun the test on all the platforms to see if the platform check needs to be tweaked. * Only tested on n6 so far * Minor demo_interface changes * Added some private-vlan dependency resources * test_interface_private_vlan: n3k platform fixes * demo manifest as well * tested on n3-i4,n9-i2,n5,n9-i3,n7 * test_interface_stp needs vlan cleanup * tested on n9-i3 * Re-add n8k doc references * Fix some N8k references * Add N8k back to support table * Remove extra property * Fix spacing in rotate property * anycast_gateway platform checks * Restrict the anycast test to n9k only * Tested on n3,n7,n9-i2,n9-i3 * test_interface_service_vni script fixes * Some cleanup / setup was insufficient * :non_default was not getting run * This is only supported on n7k; tested on n7k. * test_vrf: f3 dependencies * N7k needs an f3-only vdc for route_distinguisher * tested on n7 * test_vrf_af: add f3 dependency for 7k * tested on n7k * Update README-agent-install.md * Update README.md * test_vlan/test_private_vlan: add f3 deps * Tested on n7 * test_vxlan_vtep*: add f3 deps * Tested on n7 * Refactor package provider to use rpm - qip (#328) * Replace regexp with rpm -qip * patching demo updates * Updated fail message * Update demo_patching.pp * Add back rpm filename regexp support (#329) * Add back rpm filename regexp support * Remove debugs * Update README.md * interface svi fix * Remove explicit anycast config * anycast-gateway-mac is a required dependency; a recent commit added an explicit call to the test file to set this up; BUT we already had existing code for doing this that was part of interfacelib.rb * The interfacelib code was returning early because of /unless/if/ * Tested on n9-i4 * test_interface_L2 should reset sys_def_switchport * This test sets the system default switchport state to true but it should reset it to false when it's finished. * Tested on n9-i4 * test_vxlan_vtep_vni: suppress_uuc broken * There was a platform exception that tried to add this property using the new test syntax; however this test file uses an older syntax. * Added an nv overlay hw support check (my n6k does not support nv overlay) * Tested on n6 * README-agent-install: doc cleanup (#330) * README-agent-install: doc cleanup * This doc has had a lot of misc updates in the past so I tried to streamline the content somewhat and organize the sections into discrete steps. * A couple of things that need a closer look: * The RPM table may need updating to reflect the GS fix coming out any day * The auto-install section for cisco_node_utils gem is a little weak. I'm not sure if I referenced the correct pp file and used the right directory target. * README-agent-install.md: Cleanup node-utils section * README-agent-install.md: fix broken links * Review comments addressed * All addressed except for the GS RPM comment. * README-agent-install: GS RPM v1.5 tested * Add markdown syntax flags * README.md cleanup for 1.3.2 (#335) * Move Document Workflow Map to Documentation Guide table * README: Move Limitations into Platform Support * Add Legend & tables * README: Update Usage section * README: Move platform support section after resource ref tables * README.md: minor text change * URL change: /develop/master/ * Addressed remaining PR comments * /develop/master/ URL fixup * test_vdc: fix for non-F3 testbeds (#336) * test_vdc: fix for non-F3 testbeds * Updated this test to work with non-F3 N7k testbeds. * Initially set the VDC module-type to default state * Then set module-type to F3 and test * There's no great way to know which way to leave the module-type when this test completes, so: * Reset module-type to default for non-F3 testbeds since leaving it as F3 means the device won't have any usable interfaces * Noop for F3 testbeds so that it leaves their F3 still usable * Tested on n6, n7-non-F3 * beaker tests: add F3 dependencies * vdc, bridge-domain tests * tested on n6, n7-non-F3 * Fix vxlan_vtep_vni suppress_uuc prop (#341) * Readme.md changes for itd_service (#346) * itd_service pre requisite * cleanup * cleanup * review comment * review comment * review comment * Add n8k reference to CHANGELOG * command_config: undefined method 'previous' (#358) * Symptom: Simple cc manifest with a faulty line of config: cisco_command_config { 'interface': command => ' interface loopback42 ipv6 pim foo ' } When puppet agent runs the cc provider drops into a rescue which should first print out the parts of the config that were successfully processed and then do a raise, but instead we see: ``` Error: undefined method 'previous' for # ``` * Analysis: `client.rb` was refactored a few months ago, wherein the `previous` hash key was renamed to `successful_input`. The cc providers (both puppet & chef) never got updated with the new key name. * Ref: https://github.com/cisco/cisco-network-node-utils/blob/develop/lib/cisco_node_utils/client/nxapi/client.rb#L285 * Testing: ``` Linux# puppet agent -t --debug ... Info: Cisco_command_config[interface](provider=cisco): Successfully updated: interface loopback42 Error: The command 'ipv6 pim foo' was rejected with error: % Invalid command Error: /Stage[main]/Main/Node[default]/Cisco_command_config[interface]/command: change from to interface loopback42 ipv6 pim foo failed: The command 'ipv6 pim foo' was rejected with error: % Invalid command ``` * New overlay_global behaviors (#360) * DME changes for l2rib properties resulted in removal of the 'default' keyword from the cli: `l2rib dup-host-mac-detection default` * Prior to this change, 'default' was the only way to reset the host_moves & timeout properties back to default; there was/is no 'no' command, so the only way to reset it now is to re-enter the command with the default values. * This change necessitates changes to the setter, minitest, and beaker. I also changed the default values to actual integer values instead of empty strings. * Tested minitest & beaker for: n9-edev,n9-I4,n5,n6. Skips for n3 (normal) and n7 (requires F3 card). * Remove vsh code (#361) * Fix cisco_aaa* 'TACACS+ group delete denied, group is in use' issue (#362) * Added setup and cleanup steps * Add back needed autorequires * Added cleanup and teardown calls * Refactor setup/cleanup * Remove debug causing beaker tests to error out * Fix cisco_tacacs* 'TACACS+ group delete denied, group is in use' (#363) * Added setup and cleanup steps * Add back needed autorequires * Added cleanup and teardown calls * Refactor setup/cleanup * Remove debug causing beaker tests to error out * Add setup/cleanup steps * Fix netdev tacacs* 'TACACS+ group delete denied, group is in use' (#364) * Added setup and cleanup steps * Add back needed autorequires * Added cleanup and teardown calls * Refactor setup/cleanup * Remove debug causing beaker tests to error out * Add setup/cleanup steps * Add setup/cleanup steps * Fixed merge issue --- .gitignore | 1 + CHANGELOG.md | 3 - README.md | 488 ++++++++++------- docs/README-agent-install.md | 506 +++++++++--------- docs/puppet_outline.png | Bin 65611 -> 61653 bytes examples/cisco/demo_patching.pp | 13 +- ...le-n8k_EOR-1.0.0-7.0.3.F1.1.lib32_nxos.rpm | Bin 0 -> 210991 bytes ...e-n9k_EOR-1.0.0-7.0.3.I4.1.lib32_n9000.rpm | Bin 0 -> 210969 bytes .../provider/cisco_tacacs_server/cisco.rb | 1 - .../provider/cisco_vxlan_vtep_vni/cisco.rb | 3 +- lib/puppet/provider/package/cisco.rb | 80 ++- .../cisco_aaa_authorization_login_exec_svc.rb | 21 + lib/puppet/type/cisco_encapsulation.rb | 27 +- .../test_aaalogincfgsvc_defaults.rb | 13 +- .../test_aaalogincfgsvc_negative.rb | 10 +- .../test_aaalogincfgsvc_nondefaults.rb | 19 +- .../test_aaaloginexecsvc_defaults.rb | 14 +- .../test_aaaloginexecsvc_negative.rb | 10 +- .../test_aaaloginexecsvc_nondefaults.rb | 19 +- .../cisco_bridge_domain/test_bridge_domain.rb | 3 + .../test_bridge_domain_vni.rb | 38 +- .../cisco_encapsulation/test_encapsulation.rb | 8 +- .../cisco_interface/interfacelib.rb | 21 +- .../cisco_interface/test_interface_L2.rb | 1 + .../cisco_interface/test_interface_stp.rb | 3 +- .../cisco_interface/test_interface_svi.rb | 31 +- .../test_interface_service_vni.rb | 62 +-- .../test_overlay_global.rb | 31 +- .../snmpserver_provider_defaults.rb | 34 -- .../snmpserver_provider_nondefaults.rb | 51 -- .../tacacsserverhost_provider_defaults.rb | 8 + .../tacacsserverhost_provider_negatives.rb | 8 + .../tacacsserverhost_provider_nondefaults.rb | 8 + tests/beaker_tests/cisco_vdc/test_vdc.rb | 33 +- .../cisco_vlan/test_private_vlan.rb | 15 + tests/beaker_tests/cisco_vlan/test_vlan.rb | 14 + tests/beaker_tests/cisco_vrf/test_vrf.rb | 16 +- .../beaker_tests/cisco_vrf_af/test_vrf_af.rb | 32 +- .../cisco_vxlan_vtep/test_vxlan_vtep.rb | 19 +- .../test_vxlan_vtep_vni.rb | 48 +- tests/beaker_tests/lib/utilitylib.rb | 11 + .../tacacs_server_provider_defaults.rb | 17 +- .../tacacs_server_group_provider_defaults.rb | 26 +- 43 files changed, 1000 insertions(+), 766 deletions(-) create mode 100644 files/nxos.sample-n8k_EOR-1.0.0-7.0.3.F1.1.lib32_nxos.rpm create mode 100644 files/nxos.sample-n9k_EOR-1.0.0-7.0.3.I4.1.lib32_n9000.rpm diff --git a/.gitignore b/.gitignore index ff4bea7de..f26e04d0b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .bundle/ +coverage/ junit/ log/ pkg/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 16e84f711..9fd1f216f 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,6 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added - Extend cisco_bgp_neighbor with attributes: - `bfd` - - Extended `cisco_bgp_af` to include l2vpn/evpn address-family support - Deprecated `cisco_interface` 'private-vlan' properties and replaced with new methods. The deprecated properties will be removed with release 2.0.0. The old -> new properties are: @@ -38,8 +37,6 @@ This project adheres to [Semantic Versioning](http://semver.org/). | `private_vlan_association` | `pvlan_association` | `private_vlan_type` | `pvlan_type` -### Removed - ### Changed - `cisco_interface_ospf` type and provider so that the properties accept 'default' keyword. diff --git a/README.md b/README.md index 838e1a20f..935d29a8e 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,100 @@ # ciscopuppet -##### Documentation Workflow Map - -This workflow map aids *users*, *developers* and *maintainers* of the ciscopuppet project in selecting the appropriate document(s) for their task. - -* User Guides - * [README-agent-install.md](https://github.com/cisco/cisco-network-puppet-module/blob/master/docs/README-agent-install.md) : Agent Installation and Configuration Guide - * [README-beaker-agent-install.md](https://github.com/cisco/cisco-network-puppet-module/blob/master/docs/README-beaker-agent-install.md) : Automated Agent Installation and Configuration - * [README-package-provider.md](https://github.com/cisco/cisco-network-puppet-module/blob/master/docs/README-package-provider.md) : Cisco Nexus Package Management using the Package Provider - * [README-example-manifest.md](https://github.com/cisco/cisco-network-puppet-module/blob/master/examples/README.md) : Example Demo Manifest User Guide - * The remainder of this document is aimed at end users -* Developer Guides - * [CONTRIBUTING.md](https://github.com/cisco/cisco-network-puppet-module/blob/master/CONTRIBUTING.md) : Contribution guidelines - * [README-develop-types-providers.md](https://github.com/cisco/cisco-network-puppet-module/blob/master/docs/README-develop-types-providers.md) : Developing new ciscopuppet Types and Providers - * [README-develop-beaker-scripts.md](https://github.com/cisco/cisco-network-puppet-module/blob/master/docs/README-develop-beaker-scripts.md) : Developing new beaker test scripts for ciscopuppet -* Maintainers Guides - * [README-maintainers.md](https://github.com/cisco/cisco-network-puppet-module/blob/master/docs/README-maintainers.md) : Guidelines for core maintainers of the ciscopuppet project - * All developer guides apply to maintainers as well - -Please see [Learning Resources](#learning-resources) for additional references. - --- #### Table of Contents -1. [Overview](#overview) 1. [Module Description](#module-description) 1. [Setup](#setup) -1. [Usage](#usage) -1. [Platform Support](#platform-support) - * [Provider Support Across Platforms](#provider-support-across-platforms) +1. [Example Manifests](#example-manifests) 1. [Resource Reference](#resource-reference) * [Resource Type Catalog (by Technology)](#resource-by-tech) * [Resource Type Catalog (by Name)](#resource-by-name) -1. [Limitations - OS compatibility, etc.](#limitations) -1. [Cisco OS Differences](#cisco-os-differences) -1. [Learning Resources](#learning-resources) + * [Resource Platform Support Matrix](#resource-platform-support-matrix) +1. [Documentation Guide](#documentation-guide) + +## Module Description + +The ciscopuppet module allows a network administrator to manage Cisco Network Elements using Puppet. This module bundles a set of Puppet Types, Providers, Beaker Tests, Sample Manifests and Installation Tools for effective network management. The resources and capabilities provided by this Puppet Module will grow with contributions from Cisco, Puppet Labs and the open source community. + +The Cisco Network Elements and Operating Systems managed by this Puppet Module are continuously expanding. See [Resource Platform Support Matrix](#resource-platform-support-matrix) for a list of currently supported hardware and software. + +This GitHub repository contains the latest version of the ciscopuppet module source code. Supported versions of the ciscopuppet module are available at Puppet Forge. Please refer to [SUPPORT.md][MAINT-2] for additional details. + +##### Dependencies + +The `ciscopuppet` module has a dependency on the [`cisco_node_utils`](https://rubygems.org/gems/cisco_node_utils) ruby gem. See the **Setup** section that follows for more information on `cisco_node_utils`. + +##### Contributing + +Contributions to the `ciscopuppet` module are welcome. See [CONTRIBUTING.md][DEV-1] for guidelines. + +## Setup + +#### Puppet Master + +The `ciscopuppet` module must be installed on the Puppet Master server. + +```bash +puppet module install puppetlabs-ciscopuppet +``` + +For more information on Puppet module installation see [Puppet Labs: Installing Modules](https://docs.puppetlabs.com/puppet/latest/reference/modules_installing.html) + +##### The `puppetlabs-netdev_stdlib` module + +PuppetLabs provides NetDev resource support for Cisco Nexus devices with their [`puppetlabs-netdev-stdlib`](https://forge.puppet.com/puppetlabs/netdev_stdlib) module. Installing the `ciscopuppet` module automatically installs both the `ciscopuppet` and `netdev_stdlib` modules. + +#### Puppet Agent + +The Puppet Agent requires installation and setup on each device. Agent setup can be performed as a manual process or it may be automated. For more information please see the [README-agent-install.md][USER-1] document for detailed instructions on agent installation and configuration on Cisco Nexus devices. + +##### The `cisco_node_utils` Ruby Gem +The [`cisco_node_utils`](https://rubygems.org/gems/cisco_node_utils) ruby gem is a required component of the `ciscopuppet` module. This gem contains platform APIs for interfacing between Cisco CLI and Puppet agent resources. The gem can be automatically installed by Puppet agent by simply using the [`ciscopuppet::install`](https://github.com/cisco/cisco-network-puppet-module/blob/master/examples/demo_all_cisco.pp#L19) helper class, or it can be installed manually. +##### Automatic Gem Install Using `ciscopuppet::install` -## Overview +* The `ciscopuppet::install` class is defined in the `install.pp` file in the `examples` subdirectory. Copy this file into the `manifests` directory as shown: -The ciscopuppet module allows a network administrator to manage Cisco Network Elements using Puppet. This module bundles a set of Puppet Types, providers, Beaker Tests, Sample Manifests and Installation Tools for effective network management. The resources and capabilities provided by this Puppet Module will grow with contributions from Cisco, Puppet Labs and the open source community. +~~~bash +cd /etc/puppetlabs/code/environments/production/modules/ciscopuppet/ +cp examples/install.pp manifests/ +~~~ -The Cisco Network Elements and Operating Systems managed by this Puppet Module are continuously expanding. Please refer to the [Limitations](#limitations) section for details on currently supported hardware and software. -The Limitations section also provides details on compatible Puppet Agent and Puppet Master versions. +* Next, update `site.pp` to use the install class -This GitHub repository contains the latest version of the ciscopuppet module source code. Supported versions of the ciscopuppet module are available at Puppet Forge. Please refer to [SUPPORT.md](SUPPORT.md) for additional details. +**Example** -Contributions to this Puppet Module are welcome. Guidelines on contributions to the module are captured in [CONTRIBUTING.md](CONTRIBUTING.md) +~~~puppet +node 'default' { + include ciscopuppet::install +} +~~~ -## Module Description +The preceding configuration will cause the next `puppet agent` run to automatically download the current `cisco_node_utils` gem from and install it on the node. -This module enables management of supported Cisco Network Elements using Puppet. This module enhances the Puppet DSL by introducing new Puppet Types and Providers capable of managing network elements. +##### Optional Parameters for `ciscopuppet::install` -The set of supported network element platforms is continuously expanding. Please refer to the [Limitations](#limitations) section for a list of currently supported platforms. + * Override the default rubygems repository to use a custom repository + * Provide a proxy server -## Setup +**Example** -### Puppet Master +~~~puppet +node 'default' { + class {'ciscopuppet::install': + repo => 'http://gemserver.domain.com:8808', + proxy => 'http://proxy.domain.com:8080', + } +} +~~~ -The `ciscopuppet` module must be installed on the Puppet Master server. Please see [Puppet Labs: Installing Modules](https://docs.puppetlabs.com/puppet/latest/reference/modules_installing.html) for general information on Puppet module installation. +##### Gem Persistence -### Puppet Agent -The Puppet Agent requires installation and setup on each device. Agent setup can be performed as a manual process or it may be automated. For more information please see the [README-agent-install.md](docs/README-agent-install.md) document for detailed instructions on agent installation and configuration on Cisco Nexus devices. +Once installed, the GEM will remain persistent across system reloads within the Guestshell or OAC environments; however, the bash-shell environment does not share this persistent behavior, in which case the `ciscopuppet::install` helper class automatically downloads and re-installs the gem after each system reload. -### `cisco_node_utils` Ruby gem +See [General Documentation](#general-documentation) for information on Guestshell and OAC. + +## Example Manifests This module has dependencies on the [`cisco_node_utils`](https://rubygems.org/gems/cisco_node_utils) ruby gem. After installing the Puppet Agent software, use Puppet's built-in [`Package`](https://github.com/cisco/cisco-network-puppet-module/blob/master/examples/install.pp#L17) provider to install the gem. @@ -72,16 +102,15 @@ A helper class [`ciscopuppet::install`](https://github.com/cisco/cisco-network-p For Puppet Agents running within the GuestShell or OAC environment, the installed GEM remains persistent across system reloads, however, agents running in the NX-OS bash-shell environment will automatically download and reinstall the GEM after a system reload. -## Usage +##### OSPF Example Manifest -The following example shows how to use ciscopuppet to configure ospf on a -Cisco Nexus switch. +The following example demonstrates how to define a manifest that uses `ciscopuppet` to configure OSPF on a Cisco Nexus switch. Three resource types are used to define an OSPF instance, basic OSPF router settings, and OSPF interface settings: -Three types are needed to add OSPF support on an interface: cisco_ospf, -cisco_ospf_vrf, and cisco_interface_ospf. +* [`cisco_ospf`](https://github.com/cisco/cisco-network-puppet-module/tree/master#type-cisco_ospf) +* [`cisco_ospf_vrf`](https://github.com/cisco/cisco-network-puppet-module/tree/master#type-cisco_ospf_vrf) +* [`cisco_interface_ospf`](https://github.com/cisco/cisco-network-puppet-module/tree/master#type-cisco_interface_ospf) -First, to configure cisco_ospf to enable ospf on the device, add the -following type in the manifest: +The first manifest type should define the router instance using `cisco_ospf`. The title '`Sample`' becomes the router instance name. ~~~puppet cisco_ospf {"Sample": @@ -89,8 +118,7 @@ cisco_ospf {"Sample": } ~~~ -Then put the ospf router under a VRF, and add the corresponding OSPF configuration. -If the configuration is global, use 'default' as the VRF name. +The next type to define is `cisco_ospf_vrf`. The title includes the OSPF router instance name and the VRF name. Note that a non-VRF configuration uses 'default' as the VRF name. ~~~puppet cisco_ospf_vrf {"Sample default": @@ -100,7 +128,7 @@ cisco_ospf_vrf {"Sample default": } ~~~ -Finally apply the ospf into an interface: +Finally, define the OSPF interface settings. The title here includes the Interface name and the OSPF router instance name. ~~~puppet cisco_interface_ospf {"Ethernet1/2 Sample": @@ -110,111 +138,6 @@ cisco_interface_ospf {"Ethernet1/2 Sample": } ~~~ -## Platform Support - -### Provider Support Across Platforms - -The Nexus family of switches support various hardware and software features depending on the model and version. The following table will guide you through the provider support matrix. - -✅ = Supported - * The provider has been validated to work on the platform family. An asterisk '*' indicates that some provider properties may have software/hardware limitations, caveats, or noted behaviors. Click on the associated caveat link for more information. - -:heavy_minus_sign: = Not Applicable - * The provider is not supported at all on the platform because of hardware or software limitations. - -A note about support for specific platform models: - - * "**N9k**" support includes all N9xxx models. - * "**N3k**" support includes N30xx and N31xx models only. **_The N35xx model is not supported_.** - * "**N5k**" support includes N56xx models only. **_The N50xx and N55xx models are not supported at this time_.** - * "**N6k**" support includes all N6xxx models. - * "**N7k**" support includes all N7xxx models. - -| ✅ = Supported
:heavy_minus_sign: = Not Applicable | N9k | N3k | N5k | N6k | N7k | Caveats | -|:---|:---:|:---:|:---:|:---:|:---:|:---:| -| [cisco_aaa_
authentication_login](#type-cisco_aaa_authentication_login) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_aaa_
authorization_login_cfg_svc](#type-cisco_aaa_authorization_login_cfg_svc) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_aaa_
authorization_login_exec_svc](#type-cisco_aaa_authorization_login_exec_svc) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_aaa_group_tacacs](#type-cisco_aaa_group_tacacs) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_acl](#type-cisco_acl) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_ace](#type-cisco_ace) | ✅ | ✅ | ✅* | ✅* | ✅* | \*[caveats](#cisco_ace-caveats) | -| [cisco_command_config](#type-cisco_command_config) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_bfd_global](#type-cisco_bfd_global) | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_bfd_global-caveats) | -| [cisco_bgp](#type-cisco_bgp) | ✅ | ✅ | ✅* | ✅* | ✅* | \*[caveats](#cisco_bgp-caveats) | -| [cisco_bgp_af](#type-cisco_bgp_af) | ✅* | ✅* | ✅ | ✅* | ✅ | \*[caveats](#cisco_bgp_af-caveats) | -| [cisco_bgp_neighbor](#type-cisco_bgp_neighbor) | ✅ | ✅ | ✅* | ✅* | ✅ |\*[caveats](#cisco_bgp_neighbor-caveats) -| [cisco_bgp_neighbor_af](#type-cisco_bgp_neighbor_af) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_bridge_domain](#type-cisco_bridge_domain) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | -| [cisco_bridge_domain_vni](#type-cisco_bridge_domain_vni) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | -| [cisco_encapsulation](#type-cisco_encapsulation) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | -| [cisco_evpn_vni](#type-cisco_evpn_vni) | ✅ | :heavy_minus_sign: | ✅ | ✅ | ✅ | \*[caveats](#cisco_evpn_vni-caveats) | -| [cisco_fabricpath_global](#type-cisco_fabricpath_global) | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | ✅ | ✅* | \*[caveats](#cisco_fabricpath_global-caveats) | -| [cisco_fabricpath_topology](#type-cisco_fabricpath_topology) | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | ✅ | ✅ | -| [cisco_interface](#type-cisco_interface) | ✅ | ✅ | ✅* | ✅* | ✅ | \*[caveats](#cisco_interface-caveats) | -| [cisco_interface_channel_group](#type-cisco_interface_channel_group) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_interface_ospf](#type-cisco_interface_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_interface_portchannel](#type-cisco_interface_portchannel) | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_interface_portchannel-caveats) | -| [cisco_interface_service_vni](#type-cisco_interface_service_vni) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | -| [cisco_itd_device_group](#type-cisco_itd_device_group) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | -| [cisco_itd_device_group_node](#type-cisco_itd_device_group_node) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | -| [cisco_itd_service](#type-cisco_itd_service) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | \*[caveats](#cisco_itd_service-caveats) | -| [cisco_ospf](#type-cisco_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_ospf_area](#type-cisco_ospf_area) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_ospf_area_vlink](#type-cisco_ospf_area_vlink) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_ospf_vrf](#type-cisco_ospf_vrf) | ✅ | ✅ | ✅ | ✅ | ✅ | -| ✅ = Supported
:heavy_minus_sign: = Not Applicable | N9k | N3k | N5k | N6k | N7k | Caveats | -| [cisco_overlay_global](#type-cisco_overlay_global) | ✅ | :heavy_minus_sign: | ✅ | ✅ | ✅ | -| [cisco_pim](#type-cisco_pim) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_pim_rp_address](#type-cisco_pim_rp_address) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_pim_grouplist](#type-cisco_pim_grouplist) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_portchannel_global](#type-cisco_portchannel_global) | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_portchannel_global-caveats) | -| [cisco_stp_global](#type-cisco_stp_global) | ✅* | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_stp_global-caveats) | -| [cisco_snmp_community](#type-cisco_snmp_community) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_snmp_group](#type-cisco_snmp_group) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_snmp_server](#type-cisco_snmp_server) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_snmp_user](#type-cisco_snmp_user) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_tacacs_server](#type-cisco_tacacs_server) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_tacacs_server_host](#type-cisco_tacacs_server_host) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_vdc](#type-cisco_vdc) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | -| [cisco_vlan](#type-cisco_vlan) | ✅* | ✅* | ✅ | ✅ | ✅ | \*[caveats](#cisco_vlan-caveats) | -| [cisco_vpc_domain](#type-cisco_vpc_domain) | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_vpc_domain-caveats) | -| [cisco_vrf](#type-cisco_vrf) | ✅ | ✅* | ✅ | ✅ | ✅ | \*[caveats](#cisco_vrf-caveats) | -| [cisco_vrf_af](#type-cisco_vrf_af) | ✅ | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_vrf_af-caveats) | -| [cisco_vtp](#type-cisco_vtp) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_vxlan_vtep](#type-cisco_vxlan_vtep) | ✅ | :heavy_minus_sign: | ✅ | ✅ | ✅ | -| [cisco_vxlan_vtep_vni](#type-cisco_vxlan_vtep_vni) | ✅ | :heavy_minus_sign: | ✅ | ✅ | ✅ | - -##### NetDev Providers - -| ✅ = Supported
:heavy_minus_sign: = Not Applicable | N9k | N3k | N5k | N6k | N7k | Caveats | -|:---|:---:|:---:|:---:|:---:|:---:|:---:| -| [domain_name](#type-domain_name) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [name_server](#type-name_server) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [network_dns](#type-network_dns) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [network_interface](#type-network_interface) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [network_snmp](#type-network_snmp) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [network_trunk](#type-network_trunk) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [network_vlan](#type-network_vlan) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [ntp_config](#type-ntp_config) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [ntp_server](#type-ntp_server) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [port_channel](#type-port_channel) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [radius](#type-radius) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [radius_global](#type-radius_global) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [radius_server_group](#type-tacacs_server_group) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [radius_server](#type-radius_server) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [search_domain](#type-search_domain) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [snmp_community](#type-snmp_community) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [snmp_notification](#type-snmp_notification) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [snmp_notification_receiver](#type-snmp_notification_receiver) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [snmp_user](#type-snmp_user) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [syslog_server](#type-syslog_server) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [syslog_setting](#type-syslog_setting) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [tacacs](#type-tacacs) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [tacacs_global](#type-tacacs_global) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [tacacs_server](#type-tacacs_server) | ✅ | ✅ | ✅ | ✅ | ✅ | -| [tacacs_server_group](#type-tacacs_server_group) | ✅ | ✅ | ✅ | ✅ | ✅ | - --- ## Resource Reference The following resources include cisco types and providers along with cisco provider support for netdev stdlib types. Installing the `ciscopuppet` module will install both the `ciscopuppet` and `netdev_stdlib` modules. @@ -268,7 +191,7 @@ The following resources include cisco types and providers along with cisco provi * [`cisco_interface_service_vni`](#type-cisco_interface_service_vni) * [`network_interface (netdev_stdlib)`](#type-network_interface) -* Itd Types +* ITD (Intelligent Traffic Director) Types * [`cisco_itd_device_group`](#type-cisco_itd_device_group) * [`cisco_itd_device_group_node`](#type-cisco_itd_device_group_node) * [`cisco_itd_service`](#type-cisco_itd_service) @@ -435,6 +358,112 @@ The following resources include cisco types and providers along with cisco provi * [`tacacs_server_group`](#type-tacacs_server_group) * [`tacacs_server`](#type-tacacs_server) +### Resource Platform Support Matrix + +The Nexus family of switches support various hardware and software features depending on the model and version. The following table will guide you through the provider support matrix. + +**Platform Models** + +Platform | Description | Environments +:--|:--|:-- +**N9k** | Support includes all N9xxx models | bash-shell, guestshell +**N3k** | Support includes N30xx and N31xx models only.
The N35xx model is not supported. | bash-shell, guestshell +**N5k** | Support includes N56xx models only.
The N50xx and N55xx models are not supported at this time. | Open Agent Container (OAC) +**N6k** | Support includes all N6xxx models | Open Agent Container (OAC) +**N7k** | Support includes all N7xxx models | Open Agent Container (OAC) +**N8k** | Support includes all N8xxx models | bash-shell, guestshell + + +**Matrix Legend** + +Symbol | Meaning | Description +:--|:--|:-- +✅ | Supported | The provider has been validated to work on the platform.
An asterisk '*' indicates that some provider properties may have software or hardware limitations, caveats, or other noted behaviors.
Click on the associated caveat link for more information. +:heavy_minus_sign: | Not Applicable | The provider is not supported on the platform because of hardware or software limitations. + +**Support Matrix** + +| ✅ = Supported
:heavy_minus_sign: = Not Applicable | N9k | N3k | N5k | N6k | N7k | N8k | Caveats | +|:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| [cisco_aaa_
authentication_login](#type-cisco_aaa_authentication_login) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_aaa_
authorization_login_cfg_svc](#type-cisco_aaa_authorization_login_cfg_svc) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_aaa_
authorization_login_exec_svc](#type-cisco_aaa_authorization_login_exec_svc) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_aaa_group_tacacs](#type-cisco_aaa_group_tacacs) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_acl](#type-cisco_acl) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_ace](#type-cisco_ace) | ✅ | ✅ | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_ace-caveats) | +| [cisco_command_config](#type-cisco_command_config) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_bgp](#type-cisco_bgp) | ✅ | ✅ | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_bgp-caveats) | +| [cisco_bgp_af](#type-cisco_bgp_af) | ✅* | ✅* | ✅ | ✅* | ✅ | ✅ | \*[caveats](#cisco_bgp_af-caveats) | +| [cisco_bgp_neighbor](#type-cisco_bgp_neighbor) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_bgp_neighbor_af](#type-cisco_bgp_neighbor_af) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_bridge_domain](#type-cisco_bridge_domain) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | +| [cisco_bridge_domain_vni](#type-cisco_bridge_domain_vni) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | +| [cisco_encapsulation](#type-cisco_encapsulation) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | +| [cisco_evpn_vni](#type-cisco_evpn_vni) | ✅ | :heavy_minus_sign: | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_evpn_vni-caveats) | +| [cisco_fabricpath_global](#type-cisco_fabricpath_global) | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | ✅ | ✅* | :heavy_minus_sign: | \*[caveats](#cisco_fabricpath_global-caveats) | +| [cisco_fabricpath_topology](#type-cisco_fabricpath_topology) | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | ✅ | ✅ | :heavy_minus_sign: | +| [cisco_interface](#type-cisco_interface) | ✅ | ✅ | ✅* | ✅* | ✅ | ✅ | \*[caveats](#cisco_interface-caveats) | +| [cisco_interface_channel_group](#type-cisco_interface_channel_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_interface_ospf](#type-cisco_interface_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_interface_portchannel](#type-cisco_interface_portchannel) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_interface_portchannel-caveats) | +| [cisco_interface_service_vni](#type-cisco_interface_service_vni) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | +| [cisco_itd_device_group](#type-cisco_itd_device_group) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | +| [cisco_itd_device_group_node](#type-cisco_itd_device_group_node) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | +| [cisco_itd_service](#type-cisco_itd_service) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | \*[caveats](#cisco_itd_service-caveats) | +| [cisco_ospf](#type-cisco_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_ospf_vrf](#type-cisco_ospf_vrf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| ✅ = Supported
:heavy_minus_sign: = Not Applicable | N9k | N3k | N5k | N6k | N7k | N8k | Caveats | +| [cisco_overlay_global](#type-cisco_overlay_global) | ✅ | :heavy_minus_sign: | ✅ | ✅ | ✅ | ✅ | +| [cisco_pim](#type-cisco_pim) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_pim_rp_address](#type-cisco_pim_rp_address) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_pim_grouplist](#type-cisco_pim_grouplist) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_portchannel_global](#type-cisco_portchannel_global) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_portchannel_global-caveats) | +| [cisco_stp_global](#type-cisco_stp_global) | ✅* | ✅* | ✅* | ✅* | ✅ | ✅ | \*[caveats](#cisco_stp_global-caveats) | +| [cisco_snmp_community](#type-cisco_snmp_community) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_snmp_group](#type-cisco_snmp_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_snmp_server](#type-cisco_snmp_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_snmp_user](#type-cisco_snmp_user) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_tacacs_server](#type-cisco_tacacs_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_tacacs_server_host](#type-cisco_tacacs_server_host) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_vdc](#type-cisco_vdc) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | +| [cisco_vlan](#type-cisco_vlan) | ✅* | ✅* | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_vlan-caveats) | +| [cisco_vpc_domain](#type-cisco_vpc_domain) | ✅* | ✅* | ✅* | ✅* | ✅* | :heavy_minus_sign: | \*[caveats](#cisco_vpc_domain-caveats) | +| [cisco_vrf](#type-cisco_vrf) | ✅ | ✅* | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_vrf-caveats) | +| [cisco_vrf_af](#type-cisco_vrf_af) | ✅ | ✅* | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_vrf_af-caveats) | +| [cisco_vtp](#type-cisco_vtp) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_vxlan_vtep](#type-cisco_vxlan_vtep) | ✅ | :heavy_minus_sign: | ✅ | ✅ | ✅ | ✅ | +| [cisco_vxlan_vtep_vni](#type-cisco_vxlan_vtep_vni) | ✅ | :heavy_minus_sign: | ✅ | ✅ | ✅ | ✅ | + +##### NetDev Providers + +| ✅ = Supported
:heavy_minus_sign: = Not Applicable | N9k | N3k | N5k | N6k | N7k | N8k | Caveats | +|:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| [domain_name](#type-domain_name) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [name_server](#type-name_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [network_dns](#type-network_dns) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [network_interface](#type-network_interface) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [network_snmp](#type-network_snmp) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [network_trunk](#type-network_trunk) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [network_vlan](#type-network_vlan) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [ntp_config](#type-ntp_config) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [ntp_server](#type-ntp_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [port_channel](#type-port_channel) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [radius](#type-radius) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [radius_global](#type-radius_global) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [radius_server_group](#type-tacacs_server_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [radius_server](#type-radius_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [search_domain](#type-search_domain) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [snmp_community](#type-snmp_community) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [snmp_notification](#type-snmp_notification) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [snmp_notification_receiver](#type-snmp_notification_receiver) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [snmp_user](#type-snmp_user) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [syslog_server](#type-syslog_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [syslog_setting](#type-syslog_setting) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [tacacs](#type-tacacs) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [tacacs_global](#type-tacacs_global) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [tacacs_server](#type-tacacs_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [tacacs_server_group](#type-tacacs_server_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | + -- ### Cisco Resource Type Details @@ -452,6 +481,7 @@ Allows execution of configuration commands. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -483,6 +513,7 @@ Manages AAA Authentication Login configuration. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -516,6 +547,7 @@ Manages configuration for Authorization Login Config Service. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -543,6 +575,7 @@ Manages configuration for Authorization Login Exec Service. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -570,6 +603,7 @@ Manages configuration for a TACACS+ server group. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -1148,12 +1182,13 @@ Manages configuration of a BGP Address-family instance. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Caveats | Property | Caveat Description | |:--------|:-------------| -| `additional_paths_install` | Not supported on N3k, N9k | +| `additional_paths_install` | Not supported on N3k, N8k, N9k | | `advertise_l2vpn_evpn` | Not supported on N3k, N6k | | address-family `l2vpn/evpn` | Module Minimum Version 1.3.2
OS Minimum Version 7.0(3)I3(1)
Not supported on N3k | @@ -1429,6 +1464,7 @@ Manages configuration of a BGP Neighbor Address-family instance. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Caveats @@ -1554,6 +1590,7 @@ Manages a cisco Bridge-Domain | N5k | not applicable | not applicable | | N6k | not applicable | not applicable | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | not applicable | not applicable | #### Parameters @@ -1583,6 +1620,7 @@ Creates a Virtual Network Identifier member (VNI) mapping for cisco Bridge-Domai | N5k | not applicable | not applicable | | N6k | not applicable | not applicable | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | not applicable | not applicable | #### Parameters @@ -1606,6 +1644,7 @@ Manages a Global VNI Encapsulation profile | N5k | not applicable | not applicable | | N6k | not applicable | not applicable | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | not applicable | not applicable | #### Parameters @@ -1632,6 +1671,7 @@ Manages Cisco Ethernet Virtual Private Network (EVPN) VXLAN Network Identifier ( | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Caveats @@ -1761,6 +1801,7 @@ Manages a Cisco fabricpath Topology | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | not applicable | not applicable | #### Parameters @@ -1786,6 +1827,7 @@ Manages a Cisco Network Interface. Any resource dependency should be run before | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Caveats @@ -2055,6 +2097,7 @@ Manages a Cisco Network Interface Channel-group. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -2089,6 +2132,7 @@ Manages a Cisco Network Interface Service VNI. | N5k | not applicable | not applicable | | N6k | not applicable | not applicable | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | not applicable | not applicable | #### Parameters @@ -2233,6 +2277,7 @@ Manages configuration of ITD (Intelligent Traffic Director) device group | N5k | not applicable | not applicable | | N6k | not applicable | not applicable | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | not applicable | not applicable | #### Parameters @@ -2275,6 +2320,7 @@ Manages configuration of ITD (Intelligent Traffic Director) device group node | N5k | not applicable | not applicable | | N6k | not applicable | not applicable | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | not applicable | not applicable | #### Parameters @@ -2331,6 +2377,7 @@ Manages configuration of ITD (Intelligent Traffic Director) service. | Property | Caveat Description | |:--------|:-------------| +| | This provider requires the following commands to be applied as prerequisites using the [cisco_command_config](https://github.com/cisco/cisco-network-puppet-module/blob/master/README.md#type-cisco_command_config) provider.

  cisco_command_config { 'prerequisites':
    command => "
      feature pbr
      feature sla sender
      feature sla responder
      ip sla responder
    "
  }| | `nat_destination` | Supported only on N7k | | `peer_local` | Supported only on N9k | | `peer_vdc` | Supported only on N7k | @@ -2407,6 +2454,7 @@ Manages configuration of an ospf instance. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -2618,6 +2666,7 @@ Manages a VRF for an OSPF router. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -2683,6 +2732,7 @@ Also configures anycast gateway MAC of the switch. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -2715,6 +2765,7 @@ Manages configuration of an Protocol Independent Multicast (PIM) instance. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -2740,6 +2791,7 @@ Manages configuration of an Protocol Independent Multicast (PIM) static route pr | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -2766,6 +2818,7 @@ Manages configuration of an Protocol Independent Multicast (PIM) static route pr | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -2789,6 +2842,7 @@ Manages configuration of a portchannel global parameters | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Caveats @@ -2801,7 +2855,7 @@ Manages configuration of a portchannel global parameters | `concatenation` | Supported only on N9k | | `hash_poly` | Supported only on N5k, N6k | | `resilient`
`symmetry` | Supported only on N3k, N9k | -| `rotate` | Supported only on N7k, N9k | +| `rotate` | Supported only on N7k, N8k, N9k | #### Parameters @@ -2960,6 +3014,7 @@ Manages an SNMP community on a Cisco SNMP server. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -2993,6 +3048,7 @@ of group; thus this provider utility does not create snmp groups and only report | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3015,6 +3071,7 @@ cisco_snmp_server. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3059,6 +3116,7 @@ Manages an SNMP user on an cisco SNMP server. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3107,6 +3165,7 @@ instance of the cisco_tacacs_server. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3149,6 +3208,7 @@ Configures Cisco TACACS+ server hosts. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3185,6 +3245,7 @@ Manages a Cisco VDC (Virtual Device Context). | N5k | not applicable | not applicable | | N6k | not applicable | not applicable | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | not applicable | not applicable | #### Parameters @@ -3275,6 +3336,7 @@ Manages the virtual Port Channel (vPC) domain configuration of a Cisco device. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Caveats @@ -3387,6 +3449,7 @@ device. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Caveats @@ -3456,6 +3519,7 @@ Manages Cisco Virtual Routing and Forwarding (VRF) Address-Family configuration. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Caveats @@ -3538,6 +3602,7 @@ There can only be one instance of the cisco_vtp. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3571,6 +3636,7 @@ Creates a VXLAN Network Virtualization Endpoint (NVE) overlay interface that ter | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3603,6 +3669,7 @@ Creates a Virtual Network Identifier member (VNI) for an NVE overlay interface. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3648,6 +3715,7 @@ Configure the domain name of the device | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3667,6 +3735,7 @@ Domain name of the device. Valid value is a string. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3686,6 +3755,7 @@ Hostname or address of the DNS server. Valid value is a string. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3716,6 +3786,7 @@ Manages a puppet netdev_stdlib Network Interface. Any resource dependency should | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3747,6 +3818,7 @@ interface. Valid value is an integer. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3774,6 +3846,7 @@ Manages a puppet netdev_stdlib Network Trunk. It should be noted that while the | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3809,6 +3882,7 @@ Manages a puppet netdev_stdlib Network Vlan. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3834,6 +3908,7 @@ The name of the VLAN. Valid value is a string. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3853,6 +3928,7 @@ Source interface for the NTP server. Valid value is a string. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3872,6 +3948,7 @@ Hostname or IPv4/IPv6 address of the NTP server. Valid value is a string. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3900,6 +3977,7 @@ Name of the port channel. eg port-channel100. Valid value is a string. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3919,6 +3997,7 @@ Enable or disable radius functionality. Valid values are 'true' or 'false'. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -3995,6 +4074,7 @@ Encryption key format [0-7]. Valid value is an integer. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -4013,6 +4093,7 @@ Configure the search domain of the device. Note that this type is functionally e | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -4034,6 +4115,7 @@ Manages an SNMP community on a Cisco SNMP server. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -4061,6 +4143,7 @@ Manages an SNMP notification on a Cisco SNMP server. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -4080,6 +4163,7 @@ Manages an SNMP notification receiver on an cisco SNMP server. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -4123,6 +4207,7 @@ Manages an SNMP user on an cisco SNMP server. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -4166,6 +4251,7 @@ format (in case of true) or cleartext (in case of false). Valid values are 'true | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -4191,6 +4277,7 @@ Interface to send syslog data from, e.g. "management". Valid value is a string. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -4210,6 +4297,7 @@ The unit of measurement for log time values. Valid values are 'seconds' and 'mi | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -4226,6 +4314,7 @@ Enable or disable radius functionality [true|false] | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters @@ -4251,6 +4340,7 @@ Number of seconds before the timeout period ends | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | ##### `ensure` Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. @@ -4280,45 +4370,55 @@ Number of seconds before the timeout period ends | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | +| N8k | 7.0(3)F1(1) | 1.3.2 | #### Parameters ##### `servers` Array of servers associated with this group. -## Limitations - -Minimum Requirements: -* Cisco NX-OS: - * Open source Puppet version 4.0+ or Puppet Enterprise 2015.2+ - * Cisco Nexus N9k, OS Version 7.0(3)I2(1), Environments: Bash-shell, Guestshell - * Cisco Nexus N3k, OS Version 7.0(3)I2(1), Environments: Bash-shell, Guestshell - * Cisco Nexus N5k, OS Version 7.3(0)N1(1), Environments: Open Agent Container (OAC) - * Cisco Nexus N6k, OS Version 7.3(0)N1(1), Environments: Open Agent Container (OAC) - * Cisco Nexus N7k, OS Version 7.3(0)D1(1), Environments: Open Agent Container (OAC) - -## Learning Resources - -* Puppet - * [https://learn.puppetlabs.com/](https://learn.puppetlabs.com/) - * [https://en.wikipedia.org/wiki/Puppet_(software)](https://en.wikipedia.org/wiki/Puppet_(software)) -* Markdown (for editing documentation) - * [https://help.github.com/articles/markdown-basics/](https://help.github.com/articles/markdown-basics/) -* Ruby - * [https://en.wikipedia.org/wiki/Ruby_(programming_language)](https://en.wikipedia.org/wiki/Ruby_(programming_language)) - * [https://www.codecademy.com/tracks/ruby](https://www.codecademy.com/tracks/ruby) - * [https://rubymonk.com/](https://rubymonk.com/) - * [https://www.codeschool.com/paths/ruby](https://www.codeschool.com/paths/ruby) -* Ruby Gems - * [http://guides.rubygems.org/](http://guides.rubygems.org/) - * [https://en.wikipedia.org/wiki/RubyGems](https://en.wikipedia.org/wiki/RubyGems) -* YAML - * [https://en.wikipedia.org/wiki/YAML](https://en.wikipedia.org/wiki/YAML) - * [http://www.yaml.org/start.html](http://www.yaml.org/start.html) -* Yum - * [https://en.wikipedia.org/wiki/Yellowdog_Updater,_Modified](https://en.wikipedia.org/wiki/Yellowdog_Updater,_Modified) - * [https://www.centos.org/docs/5/html/yum/](https://www.centos.org/docs/5/html/yum/) - * [http://www.linuxcommand.org/man_pages](http://www.linuxcommand.org/man_pages/yum8.html) +## Documentation Guide + +The following table groups **ciscopuppet** documentation based on the intended audience. + +Audience | ciscopuppet Documentation | +:--:|:--| +User | [README.md][USER-0] : (This document)
[README-agent-install.md][USER-1] : Agent Installation and Configuration Guide
[README-beaker-agent-install.md][USER-2] : Automated Agent Installation and Configuration
[README-package-provider.md][USER-3] : Cisco Nexus Package Management
[README-example-manifest.md][USER-4] : Example Demo Manifest User Guide +Developer | [CONTRIBUTING.md][DEV-1] : Contribution guidelines
[README-develop-types-providers.md][DEV-2] : Developing new ciscopuppet Types & Providers
[README-develop-beaker-scripts.md][DEV-3] : Developing new beaker test scripts for ciscopuppet +Maintainer | [README-maintainers.md][MAINT-1] : Guidelines for core maintainers of the ciscopuppet project
*(Developer guides apply to Maintainers as well)* + +[USER-0]: https://github.com/cisco/cisco-network-puppet-module/blob/master/README.md +[USER-1]: https://github.com/cisco/cisco-network-puppet-module/blob/master/docs/README-agent-install.md +[USER-2]: https://github.com/cisco/cisco-network-puppet-module/blob/master/docs/README-beaker-agent-install.md +[USER-3]: https://github.com/cisco/cisco-network-puppet-module/blob/master/docs/README-package-provider.md +[USER-4]: https://github.com/cisco/cisco-network-puppet-module/blob/master/examples/README.md + +[DEV-1]: https://github.com/cisco/cisco-network-puppet-module/blob/master/CONTRIBUTING.md +[DEV-2]: https://github.com/cisco/cisco-network-puppet-module/blob/master/docs/README-develop-types-providers.md +[DEV-3]: https://github.com/cisco/cisco-network-puppet-module/blob/master/docs/README-develop-beaker-scripts.md + +[MAINT-1]: https://github.com/cisco/cisco-network-puppet-module/blob/master/docs/README-maintainers.md +[MAINT-2]: https://github.com/cisco/cisco-network-puppet-module/blob/master/SUPPORT.md + +##### General Documentation + +Topic | Resources | +:---------|:--| +Puppet |
+Guestshell | [N9k Programmability Guide][GS_9K] +Markdown
(*editor*) | +N5k,N6k OAC
(open agent container) | [N5k,N6k Programmability Guide][OAC_5K_DOC] +N7k OAC
(open agent container) | [N7k Programmability Guide][OAC_7K_DOC] +Ruby |


+Ruby Gems |
+YAML |
+Yum |

+ +[GS_9K]: http://www.cisco.com/c/en/us/td/docs/switches/datacenter/nexus9000/sw/6-x/programmability/guide/b_Cisco_Nexus_9000_Series_NX-OS_Programmability_Guide/b_Cisco_Nexus_9000_Series_NX-OS_Programmability_Guide_chapter_01010.html + +[OAC_5K_DOC]: http://www.cisco.com/c/en/us/td/docs/switches/datacenter/nexus5000/sw/programmability/guide/b_Cisco_Nexus_5K6K_Series_NX-OS_Programmability_Guide/b_Cisco_Nexus_5K6K_Series_NX-OS_Programmability_Guide_chapter_01001.html + +[OAC_7K_DOC]: http://www.cisco.com/c/en/us/td/docs/switches/datacenter/nexus7000/sw/programmability/guide/b_Cisco_Nexus_7000_Series_NX-OS_Programmability_Guide/b_Cisco_Nexus_7000_Series_NX-OS_Programmability_Guide_chapter_01001.html ## License diff --git a/docs/README-agent-install.md b/docs/README-agent-install.md index 542704a30..59b761426 100644 --- a/docs/README-agent-install.md +++ b/docs/README-agent-install.md @@ -1,81 +1,83 @@ # Puppet Agent Installation & Setup: Cisco Nexus +-- #### Table of Contents 1. [Overview](#overview) -1. [Pre-Install Tasks](#pre-install) -1. [Puppet Agent Environment: bash-shell](#env-bs) -1. [Puppet Agent Environment: guestshell](#env-gs) -1. [Puppet Agent Environment: open agent container (OAC)](#env-oac) -1. [Puppet Agent Installation, Configuration and Usage](#agent-config) -1. [Optional: Guestshell & High Availability (HA) Platforms](#ha) -1. [Optional: Puppet Agent Persistence](#persistence) -1. [Optional: Automated Installation Options](#auto-install) -1. [References](#references) +1. [Pre-Install Tasks](#pre-install-tasks) +1. [Agent Environments](#env-bs) + * [bash-shell](#env-bs) + * [guestshell](#env-gs) + * [open agent container (OAC)](#env-oac) +1. [Agent Installation, Configuration and Usage](#agent-config) +1. Optional Setups + * [Guestshell on High Availability (HA) Platforms](#ha) + * [Agent Persistence](#persistence) +[References](#references) + +[How to get a virtual Nexus N9k](#VIRT_9K) + +-- ## Overview -This document describes Puppet agent installation and setup on Cisco Nexus switches. These instructions focus on manual setup. See the [Automated Installation](#auto-install) section for documentation regarding alternative installation methods. +This document describes Puppet agent installation and setup on Cisco Nexus switches. These instructions focus on manual setup. ![1](puppet_outline.png) -## Pre-Install Tasks +See [References](#references) for alternative installation methods. + +## Pre-Install Tasks -#### Platform and Software Minimum Requirements +#### *Step 1. Platform / Software Minimum Requirements* -* The Cisco NX-OS Puppet implementation requires open source Puppet version 4.0 or Puppet Enterprise 2015.2 -* The Cisco IOS XR Puppet implementation requires open source Puppet version 4.3.2 or Puppet Enterprise 2015.3.2 -* The following table lists supported platforms and OS versions. +Puppet Versions | +:--| +Open Source Puppet 4.0 | +Puppet Enterprise 2015.2 | +
-Platform | OS | OS Version | --------------------|--------|----------------------| -Cisco Nexus N9k | NX-OS | 7.0(3)I2(1) and later +Supported Platforms | OS | OS Version | +:-------------------|-------|----------------------| +Cisco Nexus N9k
*[How to get a virtual Nexus N9k](#VIRT_9K)* | NX-OS | 7.0(3)I2(1) and later Cisco Nexus N3k | NX-OS | 7.0(3)I2(1) and later Cisco Nexus N5k | NX-OS | 7.3(0)N1(1) and later Cisco Nexus N6k | NX-OS | 7.3(0)N1(1) and later Cisco Nexus N7k | NX-OS | 7.3(0)D1(1) and later +Cisco Nexus N8k | NX-OS | 7.0(3)F1(1) and later +
-Please note: A virtual Nexus N9000/N3000 may be helpful for development and testing. Users with a valid [cisco.com](http://cisco.com) user ID can obtain a copy of a virtual Nexus N9000/N3000 by sending their [cisco.com](http://cisco.com) user ID in an email to . If you do not have a [cisco.com](http://cisco.com) user ID please register for one at [https://tools.cisco.com/IDREG/guestRegistration](https://tools.cisco.com/IDREG/guestRegistration) -#### Disk space +Resource| Recommended| | +:--|:--:|:--| +Disk | **400 MB** | Minimum free space before installing Puppet agent | -400MB of free disk space on bootflash is recommended before installing the -Puppet agent software. +
-#### Environment +#### *Step 2. Choose an environment for running Puppet Agent* -NX-OS supports three possible environments for running third party software: -`bash-shell`, `guestshell` and the `open agent container (OAC)`. +NX-OS Environment | Supported Platforms | | +:--|:--:|:--| +`bash-shell` | N3k, N8k, N9k | This is the native WRL Linux environment underlying NX-OS. It is disabled by default on NX-OS. | +`guestshell` | N3k, N8k, N9k | This is a secure Linux container environment running CentOS. It is enabled by default in most platforms that support it. | +`open agent`
`container (OAC)` | N5k, N6k, N7k | This is a 32-bit CentOS-based container created specifically for running Puppet Agent software. | -Environment | Supported Platforms | ------------------------------|------------------------------------------| -`bash-shell` | Cisco Nexus N3k, N9k | -`guestshell` | Cisco Nexus N3k, N9k | -`open agent container (OAC)` | Cisco Nexus N5k, N6k, N7k | +* *OAC containers are created for specific platforms and must be downloaded from Cisco (see [OAC Download](#env-oac)). The OAC must be installed before the Puppet Agent can be installed.* -You may run Puppet from either `bash-shell` or `guestshell` on supported platforms but not from both at the same time. +* *Running Puppet Agent from multiple environments simultaneously is not supported* -* `bash-shell` - * This is the native WRL Linux environment underlying NX-OS. It is disabled by default on NX-OS. -* `guestshell` - * This is a secure Linux container environment running CentOS. It is enabled by default in most platforms that support it. -* `open agent container` - * This is a 32-bit CentOS-based container created specifically for running Puppet Agent software. - * OAC containers are created for specific platforms and must be downloaded from Cisco. - * The OAC must be installed before the Puppet Agent can be installed. -#### Set Up the Network +#### *Step 3. Network Connectivity* -Ensure that you have network connectivity prior to Puppet installation. Some basic CLI configuration may be necessary. - -**Example:** Connectivity via management interface - Nexus +* Ensure that IP reachability exists between the agent node and the Puppet Server. Note that connectivity via the management interface is in a separate VRF context which requires some additional configuration. +* Configure NTP to ensure that the agent node time is in sync with the Puppet Server. _Note: The management interface exists in a separate VRF context and requires additional configuration as shown._ +**Example:** Nexus CLI Configuration for connectivity via management interface + ~~~ config term - ntp server 10.0.0.201 use-vrf management - vrf context management ip name-server 10.0.0.202 ip domain-name mycompany.com @@ -84,16 +86,16 @@ config term interface mgmt0 vrf member management ip address 10.0.0.99/24 + + ntp server 10.0.0.201 use-vrf management end ~~~ -## Puppet Agent Environment: bash-shell +## Agent Environment Setup: bash-shell -This section is only necessary if Puppet will run from the `bash-shell`. +This section is only required when running Puppet from the `bash-shell`. -### (NX-OS only) Enable the bash-shell - -The `bash-shell` is disabled by default. Enable it with the feature configuration command. +#### *Step 1. Enable the bash-shell* ~~~ config term @@ -101,55 +103,42 @@ config term end ~~~ -### Set up the bash-shell environment - -Enter the `bash-shell` environment and become root: +#### *Step 2. Set up the bash-shell environment* -**Example:** Nexus +Use `run bash` to enter the `bash-shell` environment, then become root.
*Optional:* Use `ip netns` to switch namespaces to the `management` vrf if connectivity is via management interface. ~~~bash n3k# run bash -bash-4.2$ bash-4.2$ sudo su - -~~~ -If you're using the management interface, you must next switch to the management namespace: - -~~~bash -ip netns exec management bash +bash-4.2$ ip netns exec management bash ~~~ -Regardless of OS, set up DNS configuration: - +#### *Optional: Add DNS configuration* ~~~ -cat >> /etc/resolv.conf << EOF +bash-4.2$ cat >> /etc/resolv.conf << EOF nameserver 10.0.0.202 domain mycompany.com EOF ~~~ -*Please note: The current NX-OS bash-shell implementation does not automatically persist the entire linux filesystem. This means that certain files such as `/etc/resolv.conf` will not automatically be persistent after system reloads. Please execute `copy running-config startup-config` from the NX-OS cli after any changes to /etc/resolv.conf to ensure that it is persistent. This command can also be executed directly from the bash-shell using the vsh tool as shown:* +_A Note on Persistence_: The current NX-OS bash-shell implementation does not automatically save the entire linux filesystem. This means that certain files such as `/etc/resolv.conf` will not automatically be persistent after system reloads. Please execute `copy running-config startup-config` from the NX-OS cli after any changes to /etc/resolv.conf, which will trigger a save. This command can also be executed directly from the bash-shell with vsh: `vsh -c 'copy running-config startup-config'` -``` -vsh -c 'copy running-config startup-config' -``` +## Agent Environment Setup: guestshell -## Puppet Agent Environment: guestshell +This section is only required when running Puppet from the `guestshell`. -This section is only necessary if Puppet will run from the `guestshell`. +#### *Step 1. Enable the guestshell* -#### Set Up NX-OS +The `guestshell` container environment is enabled by default on most platforms; however, the default disk and memory resources allotted to guestshell are typically too small to support Puppet agent requirements. The resource limits may be increased with the NX-OS CLI `guestshell resize` commands as shown below. -The `guestshell` container environment is enabled by default on most platforms; however, the default disk and memory resources allocated to the guestshell container might be too small to support Puppet agent requirements. These resource limits can be increased with the NX-OS CLI `guestshell resize` commands as shown below. +Resource| Recommended| +:--|:--:| +Disk | **400 MB** | +Memory | **300 MB** | -The recommended minimum values are currently: - -~~~bash - Disk : 400MB - Memory : 300MB -~~~ - -Use the `show guestshell detail` command to display the current state of the guestshell: +

+`show guestshell detail` displays the current resource limits: ~~~ n3k# show guestshell detail @@ -162,314 +151,316 @@ Virtual service guestshell+ detail ~~~ -To resize the guestshell filesystem, use the `guestshell resize rootfs` command. To resize the guestshell memory allocation, use the `guestshell resize memory` command. These commands can be executed even when the guestshell is not yet enabled. Note that the resize command does not take effect until after the guestshell container is (re)started with the `guestshell reboot` or `guestshell enable` command. +

+`guestshell resize rootfs` sets disk size limits while `guestshell resize memory` sets memory limits. The resize commands do not take effect until after the guestshell container is (re)started by `guestshell reboot` or `guestshell enable`. -**Example.** Guestshell is currently enabled. Resize guestshell filesystem to 400MB and memory to 300MB: +**Example.** Allocate resources for guestshell by setting new limits to 400MB disk and 300MB memory. ~~~ -n3k# guestshell resize rootfs ? - <158-600> New root filesystem size (in MB) - n3k# guestshell resize rootfs 400 -Note: Please disable/enable or reboot the Guest shell for root filesystem to be resized - n3k# guestshell resize memory 300 -Note: Please disable/enable or reboot the Guest shell for system memory to be resized n3k# guestshell reboot -Access to the guest shell will be temporarily disabled while it reboots. Are you sure you want to reboot the guest shell? (y/n) [n] y ~~~ -**Example.** Guestshell is currently disabled. Resize guestshell filesystem to 400MB and memory to 300MB: +#### *Step 2. Set Up Guestshell Network* -~~~ -n3k# guestshell resize rootfs 400 -Note: Root filesystem will be resized on Guest shell enable +The `guestshell` is an independent CentOS container that does not inherit settings from NX-OS. -n3k# guestshell resize memory 300 -Note: System memory will be resized on Guest shell enable +* Use `guestshell` enter the guestshell environment, then become root. +* *Optional:* Use `chvrf` to specify a vrf namespace; e.g. `sudo chvrf management` -n3k# guestshell enable -~~~ +~~~bash +n3k# guestshell -See [References](#references) for more guestshell documentation. +[guestshell@guestshell ~]$ sudo su - # Optional: sudo chvrf management +[root@guestshell guestshell]# +~~~ -#### Set Up Guestshell Network +#### *Optional: Set up hostname* -The `guestshell` is an independent CentOS container that doesn't inherit settings from NX-OS; thus it requires additional network configuration. +This step is only needed if `certname` will not be specified in `puppet.conf`. ~~~bash -# Enter the guestshell environment using the 'guestshell' command -guestshell - -# If using the management interface, you must enter the management namespace -sudo su - -chvrf management +[root@guestshell guestshell]# hostname n3k -# Set up hostname and DNS configuration -hostname n3k +[root@guestshell guestshell]# echo 'n3k' > /etc/hostname +~~~ -echo 'n3k' > /etc/hostname +#### *Optional: Add DNS configuration* -cat >> /etc/resolv.conf << EOF +~~~bash +[root@guestshell guestshell]# cat >> /etc/resolv.conf << EOF nameserver 10.0.0.202 domain mycompany.com EOF ~~~ -## Puppet Agent Environment: open agent container (OAC) +See [References](#references) for `guestshell` documentation. -This section is only necessary if Puppet will run from the `open agent container`. +## Agent Environment Setup: open agent container (OAC) -#### Set Up NX-OS +This section is only required when running Puppet from the `open agent container`. -Download the `OAC` `oac.1.0.0.ova` file. +#### *Step 1. Download the OAC ova file* | Platform | OAC Download Link | -|----------|-------------------| -| Nexus N7k | [Download Link](https://software.cisco.com/download/release.html?i=!y&mdfid=283748960&softwareid=282088129&release=7.3%280%29D1%281%29&os=)| -| Nexus N5k and N6k | [Download Link](https://software.cisco.com/download/release.html?i=!y&mdfid=284360574&softwareid=282088130&release=7.3%280%29N1%281%29&os=)| +:----------|-------------------| +N7k | [N7k OAC file](https://software.cisco.com/download/release.html?i=!y&mdfid=283748960&softwareid=282088129&release=7.3%280%29D1%281%29&os=) | +N5k, N6k | [N5k N6k OAC file](https://software.cisco.com/download/release.html?i=!y&mdfid=284360574&softwareid=282088130&release=7.3%280%29N1%281%29&os=) | Copy the `ova` file to the `bootflash:` device. ~~~ -n7k# dir bootflash: | inc oac.1.0.0.ova +n7k# dir bootflash: 45424640 Feb 12 19:37:40 2016 oac.1.0.0.ova ~~~ -Use the `show virtual-service global` command to display available resources for the `OAC` Virtual Service. +#### *Step 2. Install and Activate the OAC* + +##### Check Resources + +Display container resources with `show virtual-service global`.
+The OAC will require **400 MB** of disk space on bootflash. Remove unnecessary files if insufficient space is available. ~~~ -n7k# show virtual-service global -... +n7k# show virtual-service global + ... Resource virtualization limits: Name Quota Committed Available ----------------------------------------------------------------------- system CPU (%) 6 0 6 memory (MB) 2304 0 2304 bootflash (MB) 600 0 600 - -n7k# ~~~ -The recommended minimum values are currently: - -~~~bash - memory : 256MB - bootflash : 400MB -~~~ +##### Installation -**NOTE:** If insufficent `bootflash:` resources are available, remove unneeded files from `bootflash:` to free up space. - -Install the `OAC` Virtual Service using the `virtual-service install` command: -`virtual-service install name oac package bootflash:oac.1.0.0.ova` +Install the OAC using the `virtual-service install` exec command. ~~~ -n7k# virtual-service install name oac package bootflash:oac.1.0.0.ova -Note: Installing package 'bootflash:/oac.1.0.0.ova' for virtual service 'oac'. Once the install has finished, the VM may be activated. Use 'show virtual-service list' for progress. +n7k# virtual-service install name oac package bootflash:oac.1.0.0.ova -n7k# 2016 Feb 12 19:51:14 n7k %$ VDC-1 %$ %VMAN-2-INSTALL_STATE: Successfully installed virtual service 'oac' +Note: Installing package 'bootflash:/oac.1.0.0.ova' for virtual service 'oac'. +Once the install has finished, the VM may be activated. +Use 'show virtual-service list' for progress. + %$ VDC-1 %$ %VMAN-2-INSTALL_STATE: Successfully installed virtual service 'oac' n7k# show virtual-service list - -Virtual Service List: - -Name Status Package Name ------------------------------------------------------------------------ oac Installed oac.1.0.0.ova - -n7k# ~~~ -Activate the OAC using the `virtual-service` configuration command: +##### Activation -~~~ -n7k# config t -Enter configuration commands, one per line. End with CNTL/Z. -n7k(config)# virtual-service oac -n7k(config-virt-serv)# activate -Note: Activating virtual-service 'oac', this might take a few minutes. Use 'show virtual-service list' for progress. -n7k(config-virt-serv)# -n7k(config-virt-serv)# end -n7k# -n7k# 2016 Feb 12 19:55:06 n7k %$ VDC-1 %$ %VMAN-2-ACTIVATION_STATE: Successfully activated virtual service 'oac' -~~~ - -You may verify activation by using the `show virtual-service list` command: +Activate the `virtual-service` using configuration mode. ~~~ -n7k# show virtual-service list +n7k# config t +n7k(config)# virtual-service oac +n7k(config-virt-serv)# activate -Virtual Service List: +Note: Activating virtual-service 'oac', this might take a few minutes. +Use 'show virtual-service list' for progress. + %$ VDC-1 %$ %VMAN-2-ACTIVATION_STATE: Successfully activated virtual service 'oac' -Name Status Package Name ------------------------------------------------------------------------ +n7k# show virtual-service list oac Activated oac.1.0.0.ova - -n7k# ~~~ -Open a console session to the `OAC` using the `virtual-service connect` command: +#### *Step 3. Set Up the OAC Network* + +The OAC is an independent CentOS container that does not inherit settings from NX-OS. -`virtual-service connect name oac console` +* Use `virtual-service connect` to enter the OAC environment. *The OAC default userid / password is `root / oac`. You are required to change the password on initial login.* -*note: The OAC's root password is initially set to `oac`. You are required to change it on initial login.* +* *Optional:* Use `chvrf` to specify a vrf namespace; e.g. `chvrf management` ~~~ -n7k# virtual-service connect name oac console +n7k# virtual-service connect name oac console Connecting to virtual-service. Exit using ^c^c^c -Trying 127.1.1.5... -Connected to 127.1.1.5. -Escape character is '^]'. - - -CentOS release 6.7 (Final) -Kernel 2.6.99.99 on an x86_64 localhost login: root -Password: +Password: oac You are required to change your password immediately (root enforced) Changing password for root. -(current) UNIX password: -New password: -Retype new password: -[root@localhost ~]# +[root@localhost ~]# +[root@localhost ~]# chvrf management ~~~ -See [References](#references) for more OAC documentation. - -#### Set Up OAC Network - -The `open agent container` is an independent CentOS container that doesn't inherit settings from NX-OS; thus it requires additional network configuration. This configuration will be applied inside the `OAC` container. +#### *Optional: Set up hostname* -Connect to the OAC console, then: +This step is only needed if `certname` will not be specified in `puppet.conf`. ~~~bash +[root@guestshell guestshell]# hostname n3k -# First become root: -sudo su - - -# Enter the management namespace if your device uses the management interface for connectivity. -chvrf management - -# Set up hostname and DNS configuration -hostname n7k +[root@guestshell guestshell]# echo 'n3k' > /etc/hostname +~~~ -echo 'n7k' > /etc/hostname +#### *Optional: Add DNS configuration* -cat >> /etc/resolv.conf << EOF +~~~bash +[root@n7k ~]# cat >> /etc/resolv.conf << EOF nameserver 10.0.0.202 domain mycompany.com EOF ~~~ +See [References](#references) for OAC documentation. + ## Puppet Agent Installation, Configuration, and Usage This section is common to `bash-shell`, `guestshell` and the `open agent container`. -#### Install Puppet Agent +#### *Step 1. Select and Install the Puppet Agent RPM* -If needed, configure a proxy server to gain network access to `yum.puppetlabs.com`: +* Optional: Define proxy server variables to allow network access to `yum.puppetlabs.com`: ~~~bash export http_proxy=http://proxy.yourdomain.com: export https_proxy=https://proxy.yourdomain.com: ~~~ +
-Import the Puppet GPG keys. +* Import the Puppet GPG keys ~~~ rpm --import http://yum.puppetlabs.com/RPM-GPG-KEY-puppetlabs rpm --import http://yum.puppetlabs.com/RPM-GPG-KEY-reductive ~~~ +
+ +* Select the appropriate Puppet RPM for your agent environment -The recommended Puppet RPM varies by environment: -* NX-OS: - * `bash-shell`: Use [http://yum.puppetlabs.com/puppetlabs-release-pc1-cisco-wrlinux-5.noarch.rpm](http://yum.puppetlabs.com/puppetlabs-release-pc1-cisco-wrlinux-5.noarch.rpm) - * `guestshell`: Due to issue https://tickets.puppetlabs.com/browse/FM-5490, please install version `1.5.2` of the puppet agent RPM inside the `guestshell`. - * Once this issue is resolved use [http://yum.puppetlabs.com/puppetlabs-release-pc1-el-7.noarch.rpm](http://yum.puppetlabs.com/puppetlabs-release-pc1-el-7.noarch.rpm) - * `open agent container (OAC)`: Use [http://yum.puppetlabs.com/puppetlabs-release-pc1-el-6.noarch.rpm](http://yum.puppetlabs.com/puppetlabs-release-pc1-el-6.noarch.rpm) +Environment | RPM | +:--|:--| +`bash-shell` | | +`guestshell` | | +`open agent`
`container (OAC)` | [http://yum.puppetlabs.com/puppetlabs-release-pc1-el-6.noarch.rpm](http://yum.puppetlabs.com/puppetlabs-release-pc1-el-6.noarch.rpm) | -Using the appropriate RPM for your environment as described above, do: +
+ +* Install the RPM (`$PUPPET_RPM` is the URL from the preceding table) ~~~bash yum install $PUPPET_RPM yum install puppet ~~~ -Update PATH var: +* Update the PATH variable ~~~bash export PATH=/opt/puppetlabs/puppet/bin:/opt/puppetlabs/puppet/lib:$PATH ~~~ -#### Install the cisco_node_utils gem +
+#### *Step 2. Configure* `/etc/puppetlabs/puppet/puppet.conf` + +Add your Puppet Server name to the configuration file. +*Optional:* Use `certname` to specify the agent node's ID. This is only needed if `hostname` has not been set. + +~~~bash +[main] + server = mypuppetmaster.mycompany.com + certname = this_node.mycompany.com +~~~ +
-If you wish to make use of the types and providers in this module, you will need to install (and perhaps configure) the [`cisco_node_utils`](https://rubygems.org/gems/cisco_node_utils) Ruby gem. Refer to [README-gem-install.md](README-gem-install.md) for detailed steps. +#### *Step 3. The `cisco_node_utils` Gem* -#### Edit the Puppet config file: +The [`cisco_node_utils`](https://rubygems.org/gems/cisco_node_utils) ruby gem is a required component of the `ciscopuppet` module. This gem contains platform APIs for interfacing between Cisco CLI and Puppet agent resources. The gem can be automatically installed by Puppet agent by simply using the [`ciscopuppet::install`](https://github.com/cisco/cisco-network-puppet-module/blob/master/examples/demo_all_cisco.pp#L19) helper class, or it can be installed manually. -**/etc/puppetlabs/puppet/puppet.conf** +##### Automatic Gem Install Using `ciscopuppet::install` -This file can be used to override the default Puppet settings. At a minimum, the following settings should be used: +* The `ciscopuppet::install` class is defined in the `install.pp` file in the `examples` subdirectory. Copy this file into the `manifests` directory as shown: ~~~bash -[main] - server = mypuppetmaster.mycompany.com +cd /etc/puppetlabs/code/environments/production/modules/ciscopuppet/ +cp examples/install.pp manifests/ +~~~ + +* Next, update `site.pp` to use the install class + +**Example** + +~~~puppet +node 'default' { + include ciscopuppet::install +} +~~~ + +The preceding configuration will cause the next `puppet agent` run to automatically download the current `cisco_node_utils` gem from and install it on the node. + +##### Optional Parameters for `ciscopuppet::install` -[agent] - pluginsync = true - ignorecache = true + * Override the default rubygems repository to use a custom repository + * Provide a proxy server + +**Example** + +~~~puppet +node default + class {'ciscopuppet::install': + repo => 'http://gemserver.domain.com:8808', + proxy => 'http://proxy.domain.com:8080', + } +end ~~~ -See the following references for more puppet.conf settings: +##### Gem Persistence -* https://docs.puppetlabs.com/puppet/latest/reference/config_important_settings.html -* https://docs.puppetlabs.com/puppet/latest/reference/config_about_settings.html -* https://docs.puppetlabs.com/puppet/latest/reference/config_file_main.html -* https://docs.puppetlabs.com/references/latest/configuration.html +Once installed, the GEM will remain persistent across system reloads within the Guestshell or OAC environments; however, the bash-shell environment does not share this persistent behavior, in which case the `ciscopuppet::install` helper class automatically downloads and re-installs the gem after each system reload. -#### Run the Puppet Agent +* The gem can also be manually installed on the agent node + +~~~ +gem install cisco_node_utils +~~~ +
+ +#### *Step 4. Run Puppet Agent* + +Executing the `puppet agent` command (with no arguments) will start the puppet agent process with the default runinterval of 30 minutes. Use the `-t` option to run puppet agent in test mode, which runs the agent a single time and stops. ~~~bash puppet agent -t ~~~ +
-## Guestshell & High Availability (HA) Platforms +## Optional Setup: Guestshell on High Availability (HA) Platforms -Optional. This section discusses `guestshell` usage on HA platforms. This section does not apply to the bash-shell environment, open agent container (OAC) environment or to single-sup platforms. +This section discusses `guestshell` usage on HA platforms. This section does not apply to the bash-shell environment, open agent container (OAC) environment or to single-sup platforms. The `guestshell` container does not automatically sync filesystem changes from the active processor to the standby processor. This means that Puppet installation files and related file changes performed in the earlier steps will not be present on the standby until they are manually synced with the following NX-OS exec command: ~~~ guestshell sync ~~~ +
-## Puppet Agent Persistence - -Optional. This section discusses Puppet agent persistence after system restarts. +## Optional Setup: Puppet Agent Persistence -1. [Service Management in bash-shell using init.d](#svc-mgmt-bs) -2. [Service Management in guestshell using systemd](#svc-mgmt-gs) -3. Not supported for the open agent container (OAC) +This section discusses Puppet agent persistence after system restarts. #### Service Management It may be desirable to set up automatic restart of the Puppet agent in the event of a system reset. The bash and guestshell environments use different methods to achieve this. -#### Optional: bash-shell / init.d +* [Service Management in bash-shell using init.d](#svc-mgmt-bs) +* [Service Management in guestshell using systemd](#svc-mgmt-gs) +* The open agent container (OAC) does not officially support agent persistence. +
-The `bash-shell` environment uses **init.d** for service management. -The Puppet agent provides a generic init.d script when installed, but a slight -modification is needed to ensure that Puppet runs in the correct namespace: +#### Service Management in bash-shell using init.d + +The `bash-shell` environment uses **init.d** for service management. The Puppet agent provides a generic init.d script when installed, but a slight modification as shown below is needed for nodes that run Puppet in the management (or other vrf) namespace: -**Nexus** ~~~diff --- /etc/init.d/puppet.old +++ /etc/init.d/puppet @@ -38,7 +38,7 @@ - + start() { echo -n $"Starting puppet agent: " - daemon $daemonopts $puppetd ${PUPPET_OPTS} ${PUPPET_EXTRA_OPTS} @@ -479,7 +470,7 @@ modification is needed to ensure that Puppet runs in the correct namespace: [ $RETVAL = 0 ] && touch ${lockfile} ~~~ -Next, in any case, enable the puppet service to be automatically started at boot time, and optionally start it now: +Next, enable the puppet service to be automatically started at boot time, and optionally start it now: ~~~bash chkconfig --add puppet @@ -487,12 +478,11 @@ chkconfig --level 345 puppet on service puppet start ~~~ +
-#### Optional: guestshell / systemd +#### Service Management in guestshell using systemd -The `guestshell` environment uses **systemd** for service management. -The Puppet agent provides a generic systemd script when installed, but a slight modification -is needed to ensure that Puppet runs in the management namespace: +The `guestshell` environment uses **systemd** for service management. The Puppet agent provides a generic systemd script when installed, but a slight modification as shown below is needed for nodes that run puppet in the management (or other vrf) namespace: ~~~diff --- /usr/lib/systemd/system/puppet.service.old @@ -504,34 +494,45 @@ is needed to ensure that Puppet runs in the management namespace: -ExecStart=/opt/puppetlabs/puppet/bin/puppet agent $PUPPET_EXTRA_OPTS --no-daemonize +ExecStart=/bin/nsenter --net=/var/run/netns/management /opt/puppetlabs/puppet/bin/puppet agent $PUPPET_EXTRA_OPTS --no-daemonize KillMode=process - + [Install] ~~~ -Now enable your Puppet systemd service (the `enable` command adds it to systemd for autostarting the next time you boot) and optionally start it. +Next, enable your Puppet systemd service (the `enable` command adds it to systemd for autostarting the next time you boot) and optionally start it now: ~~~bash - systemctl enable my_puppet systemctl start my_puppet - ~~~ -## Automated Installation Options +## References -[Beaker](README-beaker-agent-install.md) - Installing and Configuring Puppet Agent Using the Beaker Tool +Reference | Description +:--|:--| +[Automated Puppet Agent Installation](README-beaker-agent-install.md) | Using Beaker tools to install & configure Puppet Agent +[Cisco Nexus Puppet Modules](../README.md) | Types, Providers, Utilities +[Guestshell][GS_9K] | Guestshell Container Programmability Guide +[N5k, N6k Open Agent Container (OAC)][OAC_5K_DOC] | N5k, N6k Programmability Guide +[N7k Open Agent Container (OAC)][OAC_7K_DOC] | N7k Programmability Guide +[Puppet Agent Configuration Reference][PUP_CR] | `puppet.conf` settings -## References +[GS_9K]: http://www.cisco.com/c/en/us/td/docs/switches/datacenter/nexus9000/sw/6-x/programmability/guide/b_Cisco_Nexus_9000_Series_NX-OS_Programmability_Guide/b_Cisco_Nexus_9000_Series_NX-OS_Programmability_Guide_chapter_01010.html + +[OAC_5K_DOC]: http://www.cisco.com/c/en/us/td/docs/switches/datacenter/nexus5000/sw/programmability/guide/b_Cisco_Nexus_5K6K_Series_NX-OS_Programmability_Guide/b_Cisco_Nexus_5K6K_Series_NX-OS_Programmability_Guide_chapter_01001.html + +[OAC_5K_OVA]: https://software.cisco.com/download/release.html?i=!y&mdfid=284360574&softwareid=282088130&release=7.3%280%29N1%281%29&os= -[Cisco Nexus Puppet Modules](../README.md) - Types, Providers, Utilities +[OAC_7K_DOC]: http://www.cisco.com/c/en/us/td/docs/switches/datacenter/nexus7000/sw/programmability/guide/b_Cisco_Nexus_7000_Series_NX-OS_Programmability_Guide/b_Cisco_Nexus_7000_Series_NX-OS_Programmability_Guide_chapter_01001.html -[Cisco Nexus 9000 Programmability Guide](http://www.cisco.com/c/en/us/td/docs/switches/datacenter/nexus9000/sw/6-x/programmability/guide/b_Cisco_Nexus_9000_Series_NX-OS_Programmability_Guide/b_Cisco_Nexus_9000_Series_NX-OS_Programmability_Guide_chapter_01010.html) - Guestshell Documentation +[OAC_7K_OVA]: https://software.cisco.com/download/release.html?i=!y&mdfid=283748960&softwareid=282088129&release=7.3%280%29D1%281%29&os= -[Cisco Nexus 5000 and 6000 Programmability Guide](http://www.cisco.com/c/en/us/td/docs/switches/datacenter/nexus5000/sw/programmability/guide/b_Cisco_Nexus_5K6K_Series_NX-OS_Programmability_Guide/b_Cisco_Nexus_5K6K_Series_NX-OS_Programmability_Guide_chapter_01001.html) - Open Agent Container Documentation +[PUP_CR]: https://docs.puppetlabs.com/references/latest/configuration.html -[Cisco Nexus 7000 Programmability Guide](http://www.cisco.com/c/en/us/td/docs/switches/datacenter/nexus7000/sw/programmability/guide/b_Cisco_Nexus_7000_Series_NX-OS_Programmability_Guide/b_Cisco_Nexus_7000_Series_NX-OS_Programmability_Guide_chapter_01001.html) - Open Agent Container Documentation +## How to get a virtual Nexus N9k +A virtual Nexus N9k may be helpful for development and testing. To obtain a virtual N9k, first register for a [cisco.com](http://cisco.com) userid at , then send the userid in an email to . + +## License ----- ~~~ Copyright (c) 2014-2016 Cisco and/or its affiliates. @@ -547,3 +548,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ~~~ + diff --git a/docs/puppet_outline.png b/docs/puppet_outline.png index 58b63da37f9adeba60312662961db209fa9af725..fdf528516b7058f5d72ab195938a45f13ba29372 100644 GIT binary patch literal 61653 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 MHGWesRJ92FKU2M~2><{9 literal 65611 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 QWZ<8ssxG`x*(~7y0QNSn$p8QV diff --git a/examples/cisco/demo_patching.pp b/examples/cisco/demo_patching.pp index 9091e4da5..4a10f1619 100644 --- a/examples/cisco/demo_patching.pp +++ b/examples/cisco/demo_patching.pp @@ -45,9 +45,20 @@ case $image_ver { '7.0(3)I2(1)': { $ciscoPatchName = 'n9000_sample-1.0.0-7.0.3.x86_64.rpm' + $ciscoPackageName = 'n9000_sample' } '7.0(3)I3(1)': { $ciscoPatchName = 'CSCuxdublin-1.0.0-7.0.3.I3.1.lib32_n9000.rpm' + $ciscoPackageName = 'CSCuxdublin' + } + '7.0(3)I4(1)': { + $ciscoPatchName = 'nxos.sample-n9k_EOR-1.0.0-7.0.3.I4.1.lib32_n9000.rpm' + $ciscoPackageName = 'nxos.sample-n9k_EOR' + } + '7.0(3)F1(1)': { + # TBD: Update with real fretta_camden release RPM when ready. + $ciscoPatchName = 'nxos.sample-n8k_EOR-1.0.0-7.0.3.F1.1.lib32_nxos.rpm' + $ciscoPackageName = 'nxos.sample-n8k_EOR' } default: { fail("\n## NO COMPATIBLE SAMPLE PATCH RPMs AVAILABLE FOR THIS IMAGE: ${image_ver}\n") } @@ -64,7 +75,7 @@ } $settings = {'target' => 'host'} - package { 'n9000_sample': + package { $ciscoPackageName : ensure => present, provider => 'cisco', source => $ciscoPatchFile, diff --git a/files/nxos.sample-n8k_EOR-1.0.0-7.0.3.F1.1.lib32_nxos.rpm b/files/nxos.sample-n8k_EOR-1.0.0-7.0.3.F1.1.lib32_nxos.rpm new file mode 100644 index 0000000000000000000000000000000000000000..ea654f21d00b4b72a2ee27ec68fe9a66c2c4d14e GIT binary patch literal 210991 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

mS9? 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

at-ncYb1Gr*C)Ae)#v>o+eZH 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 b>pMCdk$?8&rU`;$i2@EYz9JI4MSJdl#!%ya literal 0 HcmV?d00001 diff --git a/files/nxos.sample-n9k_EOR-1.0.0-7.0.3.I4.1.lib32_n9000.rpm b/files/nxos.sample-n9k_EOR-1.0.0-7.0.3.I4.1.lib32_n9000.rpm new file mode 100644 index 0000000000000000000000000000000000000000..4ef2bdd5b58f87d8b8a6143911ff3f8e17f8389e GIT binary patch literal 210969 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

eyF`>MgkgyQ6S4$SNJfiu$`f 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

Ysd~{Yx*9YOmqtpDf&voTMYp>5f+Vh)53vK0W8O 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%0D~=(1b75D}Tv@D&~G-XG>WUYp*e^sZc&w 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

+ETWQ6`it_m4^_Ny%CJ({&T?_)0KV9Kmg`P% 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

pIHgDJ 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 X<=t_pF_I|`B=Kxp@@Q%uQj+~21;C|< literal 0 HcmV?d00001 diff --git a/lib/puppet/provider/cisco_tacacs_server/cisco.rb b/lib/puppet/provider/cisco_tacacs_server/cisco.rb index 265cb1db1..0d79c5ae2 100644 --- a/lib/puppet/provider/cisco_tacacs_server/cisco.rb +++ b/lib/puppet/provider/cisco_tacacs_server/cisco.rb @@ -142,7 +142,6 @@ def directed_request=(should_value) def deadtime if @resource[:deadtime] == :default && @property_hash[:deadtime] == Cisco::TacacsServer.default_deadtime - debug "Default value is #{deadtime_value}." deadtime = :default else deadtime = @property_hash[:deadtime] diff --git a/lib/puppet/provider/cisco_vxlan_vtep_vni/cisco.rb b/lib/puppet/provider/cisco_vxlan_vtep_vni/cisco.rb index d3d28d2bf..ebfa7cc3b 100644 --- a/lib/puppet/provider/cisco_vxlan_vtep_vni/cisco.rb +++ b/lib/puppet/provider/cisco_vxlan_vtep_vni/cisco.rb @@ -42,7 +42,8 @@ ] VXLAN_VTEP_VNI_BOOL_PROPS = [ - :suppress_arp + :suppress_arp, + :suppress_uuc, ] VXLAN_VTEP_VNI_ALL_PROPS = diff --git a/lib/puppet/provider/package/cisco.rb b/lib/puppet/provider/package/cisco.rb index 8972617fc..980f461c2 100644 --- a/lib/puppet/provider/package/cisco.rb +++ b/lib/puppet/provider/package/cisco.rb @@ -74,21 +74,55 @@ def properties end end - # set resource properties in a consistent way: - # [name] should contain the simple package name - # [source] should use ios-style file path - # [platform] stores arch, if parsable from [source] - # package_settings[version] stores version if parsable from [source] - # if [source] isn't supplied, it's assumed [name] already exists in the - # local repository - def normalize_resource - # ex: chef-12.0.0alpha.2+20150319.git.1.b6f-1.el5.x86_64.rpm - name_ver_arch_regex = /^([\w\-\+]+)-(\d+\..*)\.(\w{4,})(?:\.rpm)?$/ + def decompose_metadata(pkg) + # Sample output from 'rpm -qip' command + # + # Name : nxos.sample-n9k_EOR + # Version : 1.0.0 + # Release : 7.0.3.I4.1 + # Architecture: lib32_n9000 + # Install Date: (not installed) + # Group : Patch-RPM/swid-inseor-system/restart/none + # Size : 452807 + # License : Cisco proprietary + # Signature : (none) + # Source RPM : nxos.sample-n9k_EOR-1.0.0-7.0.3.I4.1.src.rpm + # Build Date : Mon May 23 18:18:44 2016 + # Build Host : rtp-ads-432 + # Relocations : (not relocatable) + # Packager : Wind River + # URL : http://cisco.com/ + # Summary : This is patch for sample-n9k_EOR + # Description : + # This is a patch for sample-n9k_EOR.The build type is final. + pkg = pkg[/bootflash/] ? pkg : "/bootflash/#{pkg}" + rpm_data = rpm('-qip', pkg) + n_re = /Name(?:\s+ )?:\s+(\S+)/ + v_re = /Version(?:\s+ )?:\s+(\S+)/ + r_re = /Release(?:\s+ )?:\s+(\S+)/ + a_re = /Architecture(?:\s+ )?:\s+(\S+)/ + name = n_re.match(rpm_data) ? Regexp.last_match(1) : nil + ver = v_re.match(rpm_data) ? Regexp.last_match(1) : nil + rel = r_re.match(rpm_data) ? Regexp.last_match(1) : nil + arch = a_re.match(rpm_data) ? Regexp.last_match(1) : nil + + fail "Unable to parse rpm data from #{pkg}\n#{rpm_data}" if + [name, ver, rel, arch].include?(nil) + + @resource[:name] = name + @resource[:package_settings]['version'] = "#{ver}-#{rel}" + @resource[:platform] = arch + end - # ex n9000-dk9.LIBPROCMIBREST-1.0.0-7.0.3.x86_64.rpm + def decompose_package_name + # RPM filename patterns. + # TBD: Pattern 1 and 2 are likely dead code but will only remove + # in Cisco Puppet release 1.4.0 when this fact is established. + # 1) chef-12.0.0alpha.2+20150319.git.1.b6f-1.el5.x86_64.rpm + name_ver_arch_regex = /^([\w\-\+]+)-(\d+\..*)\.(\w{4,})(?:\.rpm)?$/ + # 2) n9000-dk9.LIBPROCMIBREST-1.0.0-7.0.3.x86_64.rpm name_var_arch_regex_nx = /^(.*)-([\d\.]+-[\d\.]+)\.(\w{4,})\.rpm$/ - - # ex: b+z-ip2.x64_64 + # 3) b+z-ip2.x64_64 name_arch_regex = /^([\w\-\+]+)\.(\w+)$/ if @resource[:name] =~ name_arch_regex @@ -112,8 +146,26 @@ def normalize_resource @resource.fail 'Could not parse name|version|arch from source: ' \ "#{@resource[:source]}" end + end + + # set resource properties in a consistent way: + # [name] should contain the simple package name + # [source] should use ios-style file path + # [platform] stores architecture + # package_settings[version] stores version-release + # if [source] isn't supplied, it's assumed [name] already exists in the + # local repository + def normalize_resource + if @resource[:source] + decompose_metadata(@resource[:source]) + elsif @resource[:name][/\.rpm/] + decompose_metadata(@resource[:name]) + else + decompose_package_name + end + # replace linux path with ios-style path - @resource[:source].gsub!(%r{^/([^/]+)/}, '\1:') + @resource[:source].gsub!(%r{^/([^/]+)/}, '\1:') if @resource[:source] end # helper to retrieve version info for installed package diff --git a/lib/puppet/type/cisco_aaa_authorization_login_exec_svc.rb b/lib/puppet/type/cisco_aaa_authorization_login_exec_svc.rb index aeee8e5dd..87b1118da 100644 --- a/lib/puppet/type/cisco_aaa_authorization_login_exec_svc.rb +++ b/lib/puppet/type/cisco_aaa_authorization_login_exec_svc.rb @@ -95,4 +95,25 @@ def in_sync?(is) newvalues(:local, :unselected, :default) end + + ################ + # Autorequires # + ################ + + # At a minimum, tacacs_server must be enabled to use these features + autorequire(:cisco_tacacs_server) do |rel_catalog| + rel_catalog.catalog.resource('Cisco_tacacs_server', 'default') + end + + # Autorequire all cisco_aaa_group_tacacs associated with this service + autorequire(:cisco_aaa_group_tacacs) do |rel_catalog| + groups = [] + if self[:groups] + self[:groups].flatten.each do |group| + groups << rel_catalog.catalog.resource('Cisco_aaa_group_tacacs', + "#{group}") + end + end + groups + end end diff --git a/lib/puppet/type/cisco_encapsulation.rb b/lib/puppet/type/cisco_encapsulation.rb index 8b9aebceb..8979aaacb 100644 --- a/lib/puppet/type/cisco_encapsulation.rb +++ b/lib/puppet/type/cisco_encapsulation.rb @@ -56,28 +56,21 @@ ensurable newproperty(:dot1q_map, array_matching: :all) do - format = '[dot1q vlans, vnis]' - desc %(The encapsulation profile dot1q vlan-to-vni mapping. - Valid values are a mapping Array of the format: #{format}, - or keyword 'default'. - Example: - dot1q_map => ['100-110,150', '5000-5010,6000'] - or - dot1q_map => ['101-110,151-160', '5000-5020'] - ) + inputs = %( + Valid values are the keyword 'default', or a mapping Array of the format: + [dot1q_vlans, vnis]. Example: + dot1q_map => ['100-110,150', '5000-5010,6000'] + ) + desc 'The encapsulation profile dot1q vlan-to-vni mapping.' + inputs validate do |value| - fail 'Values in dot1q list are not of integer type' unless - /^[\d\s,-]*$/.match(value) || value[/default/] + fail inputs unless value.delete(' ')[/^(default|[\d,-]+)$/] end munge do |value| - return :default if value == 'default' - - value = value.gsub(/\s/, '') - value = PuppetX::Cisco::Utils.range_summarize(value.to_s, false) - value - end # munge + return :default if value[/default/] + PuppetX::Cisco::Utils.range_summarize(value.delete(' '), false) + end # Override puppet's insync method, which checks whether current value is # equal to value specified in manifest. Make sure puppet considers diff --git a/tests/beaker_tests/cisco_aaa_login_cfg_svc/test_aaalogincfgsvc_defaults.rb b/tests/beaker_tests/cisco_aaa_login_cfg_svc/test_aaalogincfgsvc_defaults.rb index 30baba404..797332b85 100644 --- a/tests/beaker_tests/cisco_aaa_login_cfg_svc/test_aaalogincfgsvc_defaults.rb +++ b/tests/beaker_tests/cisco_aaa_login_cfg_svc/test_aaalogincfgsvc_defaults.rb @@ -84,12 +84,15 @@ def create_aaalogincfgsvc_defaults(tests, id, title, string=false) tests[id][:resource_cmd] = resource_cmd_str end +def cleanup + logger.info('Testcase Cleanup:') + resource_absent_cleanup(agent, 'cisco_aaa_authorization_login_cfg_svc') + command_config(agent, 'no feature tacacs+') +end + test_name "TestCase :: #{testheader}" do - stepinfo = 'Setup switch for provider test' - resource_absent_cleanup(agent, - 'cisco_aaa_authorization_login_cfg_svc', - stepinfo) - logger.info("TestStep :: #{stepinfo} :: #{result}") + cleanup + teardown { cleanup } tests[id] = {} %w(default console).each do |title| diff --git a/tests/beaker_tests/cisco_aaa_login_cfg_svc/test_aaalogincfgsvc_negative.rb b/tests/beaker_tests/cisco_aaa_login_cfg_svc/test_aaalogincfgsvc_negative.rb index d6ff8e907..7f754a2ab 100644 --- a/tests/beaker_tests/cisco_aaa_login_cfg_svc/test_aaalogincfgsvc_negative.rb +++ b/tests/beaker_tests/cisco_aaa_login_cfg_svc/test_aaalogincfgsvc_negative.rb @@ -62,9 +62,17 @@ :agent => agent, } +def cleanup + logger.info('Testcase Cleanup:') + resource_absent_cleanup(agent, 'cisco_aaa_authorization_login_cfg_svc') + command_config(agent, 'no feature tacacs+') +end + test_name "TestCase :: #{testheader}" do logger.info('Test Invalid Title Pattern') - resource_absent_cleanup(agent, 'cisco_aaa_authorization_login_cfg_svc') + + cleanup + teardown { cleanup } id = 'invalid_name' tests[id] = { diff --git a/tests/beaker_tests/cisco_aaa_login_cfg_svc/test_aaalogincfgsvc_nondefaults.rb b/tests/beaker_tests/cisco_aaa_login_cfg_svc/test_aaalogincfgsvc_nondefaults.rb index fdd4295ef..5f053a476 100644 --- a/tests/beaker_tests/cisco_aaa_login_cfg_svc/test_aaalogincfgsvc_nondefaults.rb +++ b/tests/beaker_tests/cisco_aaa_login_cfg_svc/test_aaalogincfgsvc_nondefaults.rb @@ -64,12 +64,17 @@ :agent => agent, } +def cleanup + logger.info('Testcase Cleanup:') + resource_absent_cleanup(agent, 'cisco_aaa_authorization_login_cfg_svc') + resource_absent_cleanup(agent, 'cisco_aaa_group_tacacs') + resource_absent_cleanup(agent, 'cisco_tacacs_server_host') + command_config(agent, 'no feature tacacs+') +end + test_name "TestCase :: #{testheader}" do - stepinfo = 'Setup switch for provider test' - resource_absent_cleanup(agent, - 'cisco_aaa_authorization_login_cfg_svc', - stepinfo) - logger.info("TestStep :: #{stepinfo} :: #{result}") + cleanup + teardown { cleanup } tests[id] = {} %w(console default).each do |title| @@ -117,10 +122,6 @@ test_resource(tests, id) end - resource_absent_cleanup(agent, - 'cisco_aaa_authorization_login_cfg_svc', - stepinfo) - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. raise_passfail_exception(result, testheader, self, logger) end diff --git a/tests/beaker_tests/cisco_aaa_login_exec_svc/test_aaaloginexecsvc_defaults.rb b/tests/beaker_tests/cisco_aaa_login_exec_svc/test_aaaloginexecsvc_defaults.rb index 98c2fd31c..a9ab71293 100644 --- a/tests/beaker_tests/cisco_aaa_login_exec_svc/test_aaaloginexecsvc_defaults.rb +++ b/tests/beaker_tests/cisco_aaa_login_exec_svc/test_aaaloginexecsvc_defaults.rb @@ -84,12 +84,15 @@ def create_aaaloginexecsvc_defaults(tests, id, title, string=false) tests[id][:resource_cmd] = resource_cmd_str end +def cleanup + logger.info('Testcase Cleanup:') + resource_absent_cleanup(agent, 'cisco_aaa_authorization_login_exec_svc') + command_config(agent, 'no feature tacacs+') +end + test_name "TestCase :: #{testheader}" do - stepinfo = 'Setup switch for provider test' - resource_absent_cleanup(agent, - 'cisco_aaa_authorization_login_exec_svc', - stepinfo) - logger.info("TestStep :: #{stepinfo} :: #{result}") + cleanup + teardown { cleanup } tests[id] = {} %w(default console).each do |title| @@ -126,6 +129,7 @@ def create_aaaloginexecsvc_defaults(tests, id, title, string=false) tests[id][:desc] = '1.5 Verify resource absent on agent' test_resource(tests, id) end + # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. raise_passfail_exception(result, testheader, self, logger) end diff --git a/tests/beaker_tests/cisco_aaa_login_exec_svc/test_aaaloginexecsvc_negative.rb b/tests/beaker_tests/cisco_aaa_login_exec_svc/test_aaaloginexecsvc_negative.rb index 497ab9cce..3f605db01 100644 --- a/tests/beaker_tests/cisco_aaa_login_exec_svc/test_aaaloginexecsvc_negative.rb +++ b/tests/beaker_tests/cisco_aaa_login_exec_svc/test_aaaloginexecsvc_negative.rb @@ -62,9 +62,17 @@ :agent => agent, } +def cleanup + logger.info('Testcase Cleanup:') + resource_absent_cleanup(agent, 'cisco_aaa_authorization_login_exec_svc') + command_config(agent, 'no feature tacacs+') +end + test_name "TestCase :: #{testheader}" do logger.info('Test Invalid Title Pattern') - resource_absent_cleanup(agent, 'cisco_aaa_authorization_login_exec_svc') + + cleanup + teardown { cleanup } id = 'invalid_name' tests[id] = { diff --git a/tests/beaker_tests/cisco_aaa_login_exec_svc/test_aaaloginexecsvc_nondefaults.rb b/tests/beaker_tests/cisco_aaa_login_exec_svc/test_aaaloginexecsvc_nondefaults.rb index 1bf7fc1b8..65c603437 100644 --- a/tests/beaker_tests/cisco_aaa_login_exec_svc/test_aaaloginexecsvc_nondefaults.rb +++ b/tests/beaker_tests/cisco_aaa_login_exec_svc/test_aaaloginexecsvc_nondefaults.rb @@ -64,12 +64,17 @@ :agent => agent, } +def cleanup + logger.info('Testcase Cleanup:') + resource_absent_cleanup(agent, 'cisco_aaa_authorization_login_exec_svc') + resource_absent_cleanup(agent, 'cisco_aaa_group_tacacs') + resource_absent_cleanup(agent, 'cisco_tacacs_server_host') + command_config(agent, 'no feature tacacs+') +end + test_name "TestCase :: #{testheader}" do - stepinfo = 'Setup switch for provider test' - resource_absent_cleanup(agent, - 'cisco_aaa_authorization_login_exec_svc', - stepinfo) - logger.info("TestStep :: #{stepinfo} :: #{result}") + cleanup + teardown { cleanup } tests[id] = {} %w(console default).each do |title| @@ -117,10 +122,6 @@ test_resource(tests, id) end - resource_absent_cleanup(agent, - 'cisco_aaa_authorization_login_exec_svc', - stepinfo) - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. raise_passfail_exception(result, testheader, self, logger) end diff --git a/tests/beaker_tests/cisco_bridge_domain/test_bridge_domain.rb b/tests/beaker_tests/cisco_bridge_domain/test_bridge_domain.rb index 8b1e0e336..cbb9f5035 100644 --- a/tests/beaker_tests/cisco_bridge_domain/test_bridge_domain.rb +++ b/tests/beaker_tests/cisco_bridge_domain/test_bridge_domain.rb @@ -29,6 +29,9 @@ platform: 'n7k', } +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + tests[:non_default] = { desc: "1.1 Non Default Properties 'change name, state and fabric_control", title_pattern: '100', diff --git a/tests/beaker_tests/cisco_bridge_domain/test_bridge_domain_vni.rb b/tests/beaker_tests/cisco_bridge_domain/test_bridge_domain_vni.rb index 8a6e47403..c6f93b248 100644 --- a/tests/beaker_tests/cisco_bridge_domain/test_bridge_domain_vni.rb +++ b/tests/beaker_tests/cisco_bridge_domain/test_bridge_domain_vni.rb @@ -23,13 +23,16 @@ # Top-level keys set by caller: tests = { - master: master, agent: agent, - resource_name: 'cisco_bridge_domain_vni', - platform: 'n7k', + master: master, operating_system: 'nexus', + platform: 'n7k', + resource_name: 'cisco_bridge_domain_vni', } +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + tests[:default] = { title_pattern: '100-110', manifest_props: { @@ -37,36 +40,43 @@ }, } -tests[:non_default_properties_change_member_vni] = { - desc: '2.1 Non Default Properties for ordered member vni', +tests[:non_default] = { + desc: '2.1 Non Default', title_pattern: '100-110', manifest_props: { member_vni: '5100-5105,6050,7000-7001,5050,8000' }, } -tests[:non_default_properties_random_member_vni] = { - desc: '3.1 Non Default Properties for random member vni', +tests[:non_default_random] = { + desc: '2.2 Non Default Properties for random member vni', title_pattern: '100-105,150,200-203', manifest_props: { member_vni: '5100-5105,6050,7000-7001,5050,8000' }, } +def test_harness_dependencies(_tests, id) + return unless id == :non_default + + # feature vni requires F3 linecards + limit_resource_module_type_set(default_vdc_name, 'f3') +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{testheader}" do # ------------------------------------------------------------------- - logger.info("\n#{'-' * 60}\nSection 1. Non Default Property Testing") + logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + + test_harness_run(tests, :non_default) + resource_absent_cleanup(agent, 'cisco_bridge_domain_vni') - id = :non_default_properties_change_member_vni - test_harness_run(tests, id) - resource_absent_cleanup(agent, 'cisco_bridge_domain_vni', 'bridge-domain CLEANUP :: ') + test_harness_run(tests, :non_default_random) + resource_absent_cleanup(agent, 'cisco_bridge_domain_vni') - id = :non_default_properties_random_member_vni - test_harness_run(tests, id) - resource_absent_cleanup(agent, 'cisco_bridge_domain_vni', 'bridge-domain CLEANUP :: ') + teardown_vdc end logger.info('TestCase :: # {testheader} :: End') diff --git a/tests/beaker_tests/cisco_encapsulation/test_encapsulation.rb b/tests/beaker_tests/cisco_encapsulation/test_encapsulation.rb index e2867af1c..09d213c19 100644 --- a/tests/beaker_tests/cisco_encapsulation/test_encapsulation.rb +++ b/tests/beaker_tests/cisco_encapsulation/test_encapsulation.rb @@ -32,6 +32,9 @@ resource_name: 'cisco_encapsulation', } +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + # Test hash test cases tests[:default] = { desc: '1.1 Default Properties', @@ -65,11 +68,8 @@ def dependency_manifest(*) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - skip_unless_supported(tests) - # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") - id = :default test_harness_run(tests, id) @@ -78,7 +78,7 @@ def dependency_manifest(*) test_harness_run(tests, id) # ------------------------------------------------------------------- - logger.info("\n#{'-' * 60}\nSection 1. Non Default Property Testing") + logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") test_harness_run(tests, :non_default) # ------------------------------------------------------------------- diff --git a/tests/beaker_tests/cisco_interface/interfacelib.rb b/tests/beaker_tests/cisco_interface/interfacelib.rb index 01c9d8f4f..5c7f27cde 100755 --- a/tests/beaker_tests/cisco_interface/interfacelib.rb +++ b/tests/beaker_tests/cisco_interface/interfacelib.rb @@ -44,25 +44,14 @@ def test_harness_dependencies(tests, id) end # A global 'anycast gateway mac' is required for some SVI properties -# TBD: This may not be required for all platforms -def config_anycast_gateway_mac?(tests, _id) - return unless tests.key?(:anycast_gateway_mac) +def config_anycast_gateway_mac?(tests, id) + return unless tests[id].key?(:anycast_gateway_mac) agent = tests[:agent] # TBD: Consider creating a resource_get to avoid vsh calls on(agent, get_vshell_cmd('show runn fabric forwarding'), pty: true) - unless stdout[/anycast-gateway-mac/] - mac = { - name: 'cisco_overlay_global', - title: 'default', - property: 'anycast_gateway_mac', - value: '1.1.1', - } - resource_set(agent, mac, - "fabric forwarding anycast-gateway-mac #{mac[:value]}") - end - - # Delete the key to prevent having to set this for every test case - tests.delete(:anycast_gateway_mac) + return if stdout[/anycast-gateway-mac/] + cmd = ['cisco_overlay_global', 'default', 'anycast_gateway_mac', '1.1.1'] + resource_set(agent, cmd, 'fabric forwarding anycast-gateway-mac 1.1.1') end diff --git a/tests/beaker_tests/cisco_interface/test_interface_L2.rb b/tests/beaker_tests/cisco_interface/test_interface_L2.rb index 5d4478c6e..e1c4ba521 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_L2.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_L2.rb @@ -133,6 +133,7 @@ # ------------------------------------------------------------------- interface_cleanup(agent, intf) + system_default_switchport(agent, false) end logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_interface/test_interface_stp.rb b/tests/beaker_tests/cisco_interface/test_interface_stp.rb index 03f2e5c1e..da522363d 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_stp.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_stp.rb @@ -117,7 +117,7 @@ # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - resource_absent_cleanup(agent, 'cisco_bridge_domain') + remove_all_vlans(agent) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") @@ -127,6 +127,7 @@ logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") test_harness_run(tests, :non_default) + remove_all_vlans(agent) interface_cleanup(agent, intf) end diff --git a/tests/beaker_tests/cisco_interface/test_interface_svi.rb b/tests/beaker_tests/cisco_interface/test_interface_svi.rb index c21cd4ddd..5cfbda087 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_svi.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_svi.rb @@ -29,11 +29,10 @@ # Test hash top-level keys tests = { - agent: agent, - master: master, - operating_system: 'nexus', - resource_name: 'cisco_interface', - anycast_gateway_mac: true, + agent: agent, + master: master, + operating_system: 'nexus', + resource_name: 'cisco_interface', } # Skip -ALL- tests if a top-level platform/os key exludes this platform @@ -59,8 +58,7 @@ desc: "1.2 Non Default 'mgmt'", title_pattern: intf, manifest_props: { - svi_management: 'true', - fabric_forwarding_anycast_gateway: 'true', + svi_management: 'true' }, } @@ -87,23 +85,30 @@ }, } -def unsupported_properties(*) - unprops = [] - unprops << :fabric_forwarding_anycast_gateway unless platform[/n9k/] - unprops -end +tests[:anycast] = { + desc: '3.1 Anycast Gateway', + title_pattern: intf, + platform: 'n9k', + anycast_gateway_mac: true, + manifest_props: { + fabric_forwarding_anycast_gateway: 'true' + }, +} ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do # ------------------------------------------------------------------- - logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + logger.info("\n#{'-' * 60}\nSection 1. Property Testing") test_harness_run(tests, :default_mgmt) test_harness_run(tests, :non_default_mgmt) + test_harness_run(tests, :default_autostate) test_harness_run(tests, :non_default_autostate) + test_harness_run(tests, :anycast) + # ------------------------------------------------------------------- remove_interface(agent, intf) skipped_tests_summary(tests) diff --git a/tests/beaker_tests/cisco_interface_service_vni/test_interface_service_vni.rb b/tests/beaker_tests/cisco_interface_service_vni/test_interface_service_vni.rb index 789839837..b5f7a7d56 100644 --- a/tests/beaker_tests/cisco_interface_service_vni/test_interface_service_vni.rb +++ b/tests/beaker_tests/cisco_interface_service_vni/test_interface_service_vni.rb @@ -30,75 +30,75 @@ operating_system: 'nexus', platform: 'n7k', resource_name: 'cisco_interface_service_vni', - sid: 22, } +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + +if (intf = mt_full_interface) + tests[:intf] = intf +else + prereq_skip(nil, self, 'MT-full tests require F3 or compatible line module') +end + # Test hash test cases tests[:default] = { - desc: '1.1 Default Properties', - manifest_props: { + desc: '1.1 Default Properties', + title_pattern: "#{intf} 22", + sys_def_switchport: false, + manifest_props: { encapsulation_profile_vni: 'default', shutdown: 'default', }, - resource: { - # 'encapsulation_profile_vni' is nil - 'shutdown' => 'true' + resource: { + # encapsulation_profile_vni: nil + shutdown: 'true' }, } tests[:non_default] = { desc: '2.1 Non Default Properties', + title_pattern: "#{intf} 22", manifest_props: { encapsulation_profile_vni: 'vni_500_5000', shutdown: 'false', }, } -# TEST PRE-REQUISITES -# - F3 linecard assigned to admin vdc -# - Global encap profile vni config -def dependency_manifest(*) - " +def dependency_manifest(tests, id) + return unless id == :default + dep = %( cisco_vdc { '#{default_vdc_name}': - ensure => present, + # Must be f3-only limit_resource_module_type => 'f3', } - - cisco_encapsulation { 'vni_500_5000': - dot1q_map => #{Array['500', '5000']} + cisco_encapsulation {'vni_500_5000': + dot1q_map => ['500', '5000'], } - " + cisco_interface{ '#{tests[:intf]}': + switchport_mode => 'disabled', + } + ) + logger.info("\n * dependency_manifest\n#{dep}") + dep end ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - skip_unless_supported(tests) - if (intf = mt_full_interface).nil? - prereq_skip(nil, self, - 'MT-full tests require F3 or compatible line module') - end - # Clean up any stale pre-req configs that might conflict with our test resource_absent_cleanup(agent, 'cisco_encapsulation') interface_cleanup(agent, intf) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") - - id = :default - tests[id][:title_pattern] = "#{intf} #{tests[:sid]}" - test_harness_run(tests, id) - - tests[id][:ensure] = :absent - tests[id].delete(:preclean) - test_harness_run(tests, id) + test_harness_run(tests, :default) + test_harness_run(tests, :non_default) # ----------------------------------- resource_absent_cleanup(agent, 'cisco_interface_service_vni') resource_absent_cleanup(agent, 'cisco_encapsulation') - interface_cleanup(agent, intf, 'Post-test cleanup: ') end logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb b/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb index 69e041a84..c0d4c488a 100755 --- a/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb +++ b/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb @@ -76,6 +76,9 @@ platform: 'n(5|6|7|8|9)k', } +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + # tests[id] keys set by caller and used by test_harness_common: # # tests[id] keys set by caller: @@ -99,17 +102,9 @@ # :title_pattern will be set to 'id'. # -tests['preclean'] = { - manifest_props: '', - code: [0], - resource_props: { - # These properties always exist - 'dup_host_mac_detection_host_moves' => '5', - 'dup_host_mac_detection_timeout' => '180', - }, -} - tests['default_properties'] = { + # Feature disablement does not reset the detection properties on some images + code: [0, 2], manifest_props: " dup_host_ip_addr_detection_host_moves => 'default', dup_host_ip_addr_detection_timeout => 'default', @@ -182,19 +177,17 @@ def test_harness_overlay_global(tests, id) test_idempotence(tests, id) end +def testbed_clean(agent) + # TBD: config_find_remove(agent, 'nv overlay evpn', 'incl ^nv') + command_config(agent, 'no nv overlay evpn', 'no nv overlay evpn') +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{testheader}" do # ------------- - id = 'preclean' - tests[id][:desc] = 'Preclean' - if platform_supports_test(tests, id) - command_config(agent, 'no nv overlay evpn', 'no nv overlay evpn') - command_config(agent, 'l2rib dup-host-mac-detection default', - 'l2rib dup-host-mac-detection default') - end - test_harness_overlay_global(tests, id) + testbed_clean(agent) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") @@ -208,6 +201,8 @@ def test_harness_overlay_global(tests, id) id = 'non_default_properties' tests[id][:desc] = '2.1 Non Default Properties' test_harness_overlay_global(tests, id) + + testbed_clean(agent) end logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_snmp_server/snmpserver_provider_defaults.rb b/tests/beaker_tests/cisco_snmp_server/snmpserver_provider_defaults.rb index c818e6929..a12857f98 100644 --- a/tests/beaker_tests/cisco_snmp_server/snmpserver_provider_defaults.rb +++ b/tests/beaker_tests/cisco_snmp_server/snmpserver_provider_defaults.rb @@ -62,26 +62,6 @@ # @test_name [TestCase] Executes defaults testcase for SNMPSERVER Resource. test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpServerLib.create_snmpserver_manifest_defaults) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - # Or expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config snmp all') - on(agent, cmd_str) do - SnmpServerLib.match_default_cli(stdout, self, logger) - end - - logger.info("Setup switch for provider test :: #{result}") - end - # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource defaults manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. @@ -112,20 +92,6 @@ logger.info("Check cisco_snmp_server resource presence on agent :: #{result}") end - # @step [Step] Checks snmpserver instance on agent using switch show cli cmds. - step 'TestStep :: Check snmpserver instance presence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config snmp') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/snmp-server packetsize 1500/], - false, self, logger) - end - - logger.info("Check snmpserver instance presence on agent :: #{result}") - end - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. raise_passfail_exception(result, testheader, self, logger) end diff --git a/tests/beaker_tests/cisco_snmp_server/snmpserver_provider_nondefaults.rb b/tests/beaker_tests/cisco_snmp_server/snmpserver_provider_nondefaults.rb index 882cb2a5e..7a79763e5 100644 --- a/tests/beaker_tests/cisco_snmp_server/snmpserver_provider_nondefaults.rb +++ b/tests/beaker_tests/cisco_snmp_server/snmpserver_provider_nondefaults.rb @@ -64,26 +64,6 @@ # @test_name [TestCase] Executes nondefaults testcase for SNMPSERVER Resource. test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpServerLib.create_snmpserver_manifest_defaults) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - # Or expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config snmp all') - on(agent, cmd_str) do - SnmpServerLib.match_default_cli(stdout, self, logger) - end - - logger.info("Setup switch for provider test :: #{result}") - end - # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource nondefaults manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. @@ -116,23 +96,6 @@ logger.info("Check cisco_snmp_server resource presence on agent :: #{result}") end - # @step [Step] Checks snmpserver instance on agent using switch show cli cmds. - step 'TestStep :: Check snmpserver instance presence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config snmp') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/snmp-server aaa-user cache-timeout 1000/, - /snmp-server packetsize 2500/, - /snmp-server contact user1/, - /snmp-server location rtp/], - false, self, logger) - end - - logger.info("Check snmpserver instance presence on agent :: #{result}") - end - # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource defaults manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. @@ -163,20 +126,6 @@ logger.info("Check cisco_snmp_server resource presence on agent :: #{result}") end - # @step [Step] Checks snmpserver instance on agent using switch show cli cmds. - step 'TestStep :: Check snmpserver instance presence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config snmp') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/snmp-server packetsize 1500/], - false, self, logger) - end - - logger.info("Check snmpserver instance presence on agent :: #{result}") - end - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. raise_passfail_exception(result, testheader, self, logger) end diff --git a/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_defaults.rb b/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_defaults.rb index 4ef84fa1a..7c02ce526 100644 --- a/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_defaults.rb +++ b/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_defaults.rb @@ -62,8 +62,16 @@ result = 'PASS' testheader = 'TACACSSERVERHOST Resource :: All Attributes Defaults' +def cleanup + logger.info('Testcase Cleanup:') + command_config(agent, 'no feature tacacs+') +end + # @test_name [TestCase] Executes defaults testcase for TACACSSERVERHOST. test_name "TestCase :: #{testheader}" do + cleanup + teardown { cleanup } + # @step [Step] Sets up switch for provider test. step 'TestStep :: Setup switch for provider test' do # Expected exit_code is 0 since this is a bash shell cmd. diff --git a/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_negatives.rb b/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_negatives.rb index 96e3d89f2..86f1b63d9 100644 --- a/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_negatives.rb +++ b/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_negatives.rb @@ -60,8 +60,16 @@ result = 'PASS' testheader = 'TACACSSERVERHOST Resource :: All Attributes Negatives' +def cleanup + logger.info('Testcase Cleanup:') + command_config(agent, 'no feature tacacs+') +end + # @test_name [TestCase] Executes negatives testcase for TACACSSERVERHOST. test_name "TestCase :: #{testheader}" do + cleanup + teardown { cleanup } + # @step [Step] Sets up switch for provider test. step 'TestStep :: Setup switch for provider test' do # Expected exit_code is 0 since this is a bash shell cmd. diff --git a/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_nondefaults.rb b/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_nondefaults.rb index 754170b52..9fa379450 100644 --- a/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_nondefaults.rb +++ b/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_nondefaults.rb @@ -62,8 +62,16 @@ result = 'PASS' testheader = 'TACACSSERVERHOST Resource :: All Attributes NonDefaults' +def cleanup + logger.info('Testcase Cleanup:') + command_config(agent, 'no feature tacacs+') +end + # @test_name [TestCase] Executes nondefaults testcase for TACACSSERVERHOST. test_name "TestCase :: #{testheader}" do + cleanup + teardown { cleanup } + # @step [Step] Sets up switch for provider test. step 'TestStep :: Setup switch for provider test' do # Expected exit_code is 0 since this is a bash shell cmd. diff --git a/tests/beaker_tests/cisco_vdc/test_vdc.rb b/tests/beaker_tests/cisco_vdc/test_vdc.rb index b205801be..e55ca1bd1 100644 --- a/tests/beaker_tests/cisco_vdc/test_vdc.rb +++ b/tests/beaker_tests/cisco_vdc/test_vdc.rb @@ -27,12 +27,14 @@ tests = { agent: agent, master: master, - mod_type: 'f3', operating_system: 'nexus', platform: 'n7k', resource_name: 'cisco_vdc', } +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + # Test hash test cases tests[:non_default] = { desc: '1.1 non default properties', @@ -41,38 +43,23 @@ # This property does not have a meaningful default state because the module # types depend on which linecards are installed. Simply set the list to # a single common mod type and ensure that is the only type shown. - manifest_props: { limit_resource_module_type: tests[:mod_type] }, + manifest_props: { limit_resource_module_type: 'f3' }, } -def current_module_type - cmd = PUPPET_BINPATH + 'resource cisco_vdc' - # sample output: - # limit_resource_module_type => 'f3' - out = on(agent, cmd, pty: true).stdout[/limit_resource_module_type => '(.*)'/] - type = out.nil? ? '' : Regexp.last_match[1] - logger.info("\nCurrent module type: '#{type}'\n") - type +def test_harness_dependencies(_tests, id) + return unless id == :non_default + + # Set module-type to default value + limit_resource_module_type_set(default_vdc_name, nil) end ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - skip_unless_supported(tests) - - # initial setup - orig_type = current_module_type - if orig_type == tests[:mod_type] - logger.info("\nReset module type to default\n") - limit_resource_module_type_set(default_vdc_name, nil, true) - end - # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Non Default Property Testing") test_harness_run(tests, :non_default) - - # Restore original testbed settings - logger.info("\nRestore module type to '#{orig_type}'\n") - limit_resource_module_type_set(default_vdc_name, orig_type) + teardown_vdc end logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_vlan/test_private_vlan.rb b/tests/beaker_tests/cisco_vlan/test_private_vlan.rb index 7630d88da..869f08cb2 100644 --- a/tests/beaker_tests/cisco_vlan/test_private_vlan.rb +++ b/tests/beaker_tests/cisco_vlan/test_private_vlan.rb @@ -44,6 +44,7 @@ tests[:primary] = { desc: '1.1 Primary', title_pattern: '100', + preclean: 'cisco_vlan', manifest_props: { pvlan_type: 'primary', pvlan_association: '101, 102, 98-99, 105', @@ -77,6 +78,20 @@ def test_harness_dependencies(*) remove_all_vlans(agent) end +# Overridden to properly handle dependencies for this test file. +def dependency_manifest(_tests, _id) + dep = '' + if platform[/n7k/] + dep = %( + cisco_vdc { '#{default_vdc_name}': + # Must be f3-only + limit_resource_module_type => 'f3', + }) + end + logger.info("\n * dependency_manifest\n#{dep}") + dep +end + ################################################################# # TEST CASE EXECUTION ################################################################# diff --git a/tests/beaker_tests/cisco_vlan/test_vlan.rb b/tests/beaker_tests/cisco_vlan/test_vlan.rb index ae9251211..2d1ac03f5 100644 --- a/tests/beaker_tests/cisco_vlan/test_vlan.rb +++ b/tests/beaker_tests/cisco_vlan/test_vlan.rb @@ -112,6 +112,20 @@ def unsupported_properties(_tests, _id) unprops end +# Overridden to properly handle dependencies for this test file. +def dependency_manifest(_tests, _id) + dep = '' + if platform[/n7k/] + dep = %( + cisco_vdc { '#{default_vdc_name}': + # Must be f3-only + limit_resource_module_type => 'f3', + }) + end + logger.info("\n * dependency_manifest\n#{dep}") + dep +end + ################################################################# # TEST CASE EXECUTION ################################################################# diff --git a/tests/beaker_tests/cisco_vrf/test_vrf.rb b/tests/beaker_tests/cisco_vrf/test_vrf.rb index f1831b6ee..c7747a635 100644 --- a/tests/beaker_tests/cisco_vrf/test_vrf.rb +++ b/tests/beaker_tests/cisco_vrf/test_vrf.rb @@ -88,13 +88,19 @@ def unsupported_properties(_tests, _id) # Overridden to properly handle dependencies for this test file. def dependency_manifest(_tests, _id) if operating_system == 'nexus' - '' + dep = '' + if platform[/n7k/] + dep = %( + cisco_vdc { '#{default_vdc_name}': + # Must be f3-only + limit_resource_module_type => 'f3', + }) + end else - "cisco_command_config { 'interface_config': - command => ' - interface Loopback100' - }" + dep = %( cisco_interface {'loopback100': ensure => 'present' } ) end + logger.info("\n * dependency_manifest\n#{dep}") + dep end ################################################################# diff --git a/tests/beaker_tests/cisco_vrf_af/test_vrf_af.rb b/tests/beaker_tests/cisco_vrf_af/test_vrf_af.rb index 5d2864f10..a833e3ba2 100644 --- a/tests/beaker_tests/cisco_vrf_af/test_vrf_af.rb +++ b/tests/beaker_tests/cisco_vrf_af/test_vrf_af.rb @@ -124,21 +124,29 @@ def unsupported_properties(_tests, _id) # Overridden to properly handle dependencies for this test file. def dependency_manifest(tests, id) if operating_system == 'nexus' - "cisco_command_config { 'policy_config': - command => ' - route-map abc permit 10' - }" + dep = %( + cisco_command_config { 'policy_config': + command => 'route-map abc permit 10' + } ) + if platform[/n7k/] + dep += %( + cisco_vdc { '#{default_vdc_name}': + # Must be f3-only + limit_resource_module_type => 'f3', + }) + end else t = puppet_resource_title_pattern_munge(tests, id) - "cisco_vrf { '#{t[:vrf]}': - ensure => present, - } - cisco_command_config { 'policy_config': - command => ' - route-policy abc - end-policy' - }" + dep = %( + cisco_vrf { '#{t[:vrf]}': ensure => present } + cisco_command_config { 'policy_config': + command => ' + route-policy abc + end-policy' + } ) end + logger.info("\n * dependency_manifest\n#{dep}") + dep end ################################################################# diff --git a/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb b/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb index 6aa79b3bf..962bff123 100644 --- a/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb +++ b/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb @@ -32,6 +32,9 @@ resource_name: 'cisco_vxlan_vtep', } +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + # Test hash test cases tests[:default] = { title_pattern: 'nve1', @@ -76,12 +79,24 @@ def test_harness_dependencies(*) command_config(agent, cmd, cmd) end +# Overridden to properly handle dependencies for this test file. +def dependency_manifest(_tests, _id) + dep = '' + if platform[/n7k/] + dep = %( + cisco_vdc { '#{default_vdc_name}': + # Must be f3-only + limit_resource_module_type => 'f3', + }) + end + logger.info("\n * dependency_manifest\n#{dep}") + dep +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - skip_unless_supported(tests) - # ----------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") id = :default diff --git a/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb b/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb index 71018eb2b..764650662 100644 --- a/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb +++ b/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb @@ -76,6 +76,9 @@ resource_name: 'cisco_vxlan_vtep_vni', } +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + # tests[id] keys set by caller and used by test_harness_common: # # tests[id] keys set by caller: @@ -129,8 +132,9 @@ # Suppress Unknown Unicast if platform[/n(5|6)k/] - tests['default_properties_multicast_group'][:manifest_props][:suppress_uuc] = 'default' - tests['default_properties_multicast_group'][:resource_props][:suppress_uuc] = 'false' + tests['default_properties_multicast_group'][:manifest_props] += + " suppress_uuc => 'default'," + tests['default_properties_multicast_group'][:resource_props]['suppress_uuc'] = 'false' end tests['ingress_replication_static_peer_list_empty'] = { @@ -273,6 +277,25 @@ def puppet_resource_cmd PUPPET_BINPATH + 'resource cisco_vxlan_vtep_vni' end +def test_harness_dependencies(*) + return unless platform[/n(5|6)k/] + skip_if_nv_overlay_rejected(agent) +end + +# Overridden to properly handle dependencies for this test file. +def dependency_manifest(*) + dep = '' + if platform[/n7k/] + dep = %( + cisco_vdc { '#{default_vdc_name}': + # Must be f3-only + limit_resource_module_type => 'f3', + }) + end + logger.info("\n * dependency_manifest\n#{dep}") + dep +end + def build_manifest_cisco_vxlan_vtep_vni(tests, id) if tests[id][:ensure] == :absent state = 'ensure => absent,' @@ -290,6 +313,7 @@ def build_manifest_cisco_vxlan_vtep_vni(tests, id) # cisco_vxlan_vtep_vni needs cisco_vxlan_vtep as a prerequisite. tests[id][:manifest] = "cat <#{PUPPETMASTER_MANIFESTPATH} node 'default' { + #{dependency_manifest} cisco_vxlan_vtep {'nve1': ensure => present, host_reachability => 'evpn', @@ -306,6 +330,8 @@ def build_manifest_cisco_vxlan_vtep_vni(tests, id) def test_harness_cisco_vxlan_vtep_vni(tests, id) return unless platform_supports_test(tests, id) + test_harness_dependencies(tests, id) + tests[id][:ensure] = :present if tests[id][:ensure].nil? tests[id][:resource_cmd] = puppet_resource_cmd tests[id][:desc] += " [ensure => #{tests[id][:ensure]}]" @@ -323,8 +349,6 @@ def test_harness_cisco_vxlan_vtep_vni(tests, id) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{testheader}" do - skip_unless_supported(tests) - #------------------------------------------------------------------- resource_absent_cleanup(agent, 'cisco_vxlan_vtep_vni', 'Setup switch for cisco_vxlan_vtep_vni provider test') @@ -374,7 +398,7 @@ def test_harness_cisco_vxlan_vtep_vni(tests, id) tests[id][:desc] = '2.6 Multicast Group' test_harness_cisco_vxlan_vtep_vni(tests, id) - # TBD - The following tests will generate the following error. + # TBD - The suppress_arp tests will generate the following error. # ERROR: Please configure TCAM region... Configuring the TCAM region # requires a switch reboot. These tests will remain commented out # until we can design a solution. @@ -387,13 +411,13 @@ def test_harness_cisco_vxlan_vtep_vni(tests, id) # tests[id][:desc] = '2.8 Suppress ARP' # test_harness_cisco_vxlan_vtep_vni(tests, id) - # id = 'suppress_uuc_true' - # tests[id][:desc] = '2.9 Suppress Unknown Unicast' - # test_harness_cisco_vxlan_vtep_vni(tests, id) - # - # id = 'suppress_uuc_false' - # tests[id][:desc] = '2.10 Suppress Unknown Unicast' - # test_harness_cisco_vxlan_vtep_vni(tests, id) + id = 'suppress_uuc_true' + tests[id][:desc] = '2.9 Suppress Unknown Unicast' + test_harness_cisco_vxlan_vtep_vni(tests, id) + + id = 'suppress_uuc_false' + tests[id][:desc] = '2.10 Suppress Unknown Unicast' + test_harness_cisco_vxlan_vtep_vni(tests, id) resource_absent_cleanup(agent, 'cisco_vxlan_vtep_vni', 'Setup switch for cisco_vxlan_vtep_vni provider test') diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index bcbeec40c..0b97cceb6 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -963,6 +963,17 @@ def vdc_allocate_interface_set(vdc, intf) on(agent, cmd, pty: true) end +# VDC post-test cleanup +def teardown_vdc + logger.info("\n* Teardown VDC") + + # Testbeds without F3 cards should be set back to their default state; + # failure to do so will leave the testbed without usable interfaces. + # Assume that F3 testbeds should be left with module-type set to F3. + limit_resource_module_type_set(default_vdc_name, nil) unless + mt_full_interface +end + # Facter command builder helper method def facter_cmd(cmd) FACTER_BINPATH + cmd diff --git a/tests/beaker_tests/tacacs_server/tacacs_server_provider_defaults.rb b/tests/beaker_tests/tacacs_server/tacacs_server_provider_defaults.rb index 15cee056b..4b302bee8 100644 --- a/tests/beaker_tests/tacacs_server/tacacs_server_provider_defaults.rb +++ b/tests/beaker_tests/tacacs_server/tacacs_server_provider_defaults.rb @@ -53,13 +53,16 @@ result = 'PASS' testheader = 'tacacs_server Resource :: All Attributes Defaults' +def cleanup + logger.info('Testcase Cleanup:') + resource_absent_cleanup(agent, 'tacacs_server') + command_config(agent, 'no feature tacacs+') +end + # @test_name [TestCase] Executes defaults testcase for tacacs_server Resource. test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider' do - resource_absent_cleanup(agent, 'tacacs_server') - logger.info('Setup switch for provider') - end + cleanup + teardown { cleanup } # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource present manifest from master' do @@ -210,10 +213,6 @@ logger.info("Check tacacs_server resource presence on agent :: #{result}") end - step 'TestStep :: Cleanup' do - resource_absent_cleanup(agent, 'tacacs_server') - end - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. raise_passfail_exception(result, testheader, self, logger) end diff --git a/tests/beaker_tests/tacacs_server_group/tacacs_server_group_provider_defaults.rb b/tests/beaker_tests/tacacs_server_group/tacacs_server_group_provider_defaults.rb index 14dac7b21..ddb3cf216 100644 --- a/tests/beaker_tests/tacacs_server_group/tacacs_server_group_provider_defaults.rb +++ b/tests/beaker_tests/tacacs_server_group/tacacs_server_group_provider_defaults.rb @@ -58,17 +58,32 @@ result = 'PASS' testheader = 'tacacs_server_group Resource :: All Attributes Defaults' +def cleanup + logger.info('Testcase Cleanup:') + resource_absent_cleanup(agent, 'tacacs_server') + resource_absent_cleanup(agent, 'tacacs_server_group') + resource_absent_cleanup(agent, 'tacacs_server_group') + command_config(agent, 'no feature tacacs+') +end + # @test_name [TestCase] Executes defaults testcase for tacacs_server_group Resource. test_name "TestCase :: #{testheader}" do + cleanup + teardown { cleanup } + # @step [Step] Sets up switch for provider test. step 'TestStep :: Setup switch for provider' do - resource_absent_cleanup(agent, 'tacacs_server') - resource_absent_cleanup(agent, 'tacacs_server_group') - command_config(agent, 'tacacs-server host 2.2.2.2') command_config(agent, 'tacacs-server host 3.3.3.3') command_config(agent, 'tacacs-server host 2004::44') + # Expected exit_code is 0 since this is a bash shell cmd. + on(master, TacacsServerGroupLib.create_tacacs_server_setup) + + # Expected exit_code is 2 since this is a puppet agent cmd with change. + cmd_str = PUPPET_BINPATH + 'agent -t' + on(agent, cmd_str, acceptable_exit_codes: [0, 2]) + logger.info('Setup switch for provider') end @@ -178,11 +193,6 @@ logger.info("Check tacacs_server_group resource presence on agent :: #{result}") end - step 'TestStep :: Cleanup' do - resource_absent_cleanup(agent, 'tacacs_server') - resource_absent_cleanup(agent, 'tacacs_server_group') - end - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. raise_passfail_exception(result, testheader, self, logger) end From 4a8ff74c0f3df9eb8949328ac0edcebac547409e Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Wed, 3 Aug 2016 10:34:57 -0400 Subject: [PATCH 031/203] Add noop beaker test * This is a dummy test for use during CI development --- tests/beaker_tests/lib/noop.rb | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tests/beaker_tests/lib/noop.rb diff --git a/tests/beaker_tests/lib/noop.rb b/tests/beaker_tests/lib/noop.rb new file mode 100644 index 000000000..2c6863e60 --- /dev/null +++ b/tests/beaker_tests/lib/noop.rb @@ -0,0 +1,3 @@ +# Dummy test for CI development +logger.info('TestCase :: NOOP :: START') +logger.info('TestCase :: NOOP :: END') From 660cd6a888e18477b3b6cd6b8d5df1696642dbf4 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Wed, 3 Aug 2016 10:45:26 -0400 Subject: [PATCH 032/203] Move noop.rb to base dir --- tests/beaker_tests/{lib => }/noop.rb | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/beaker_tests/{lib => }/noop.rb (100%) diff --git a/tests/beaker_tests/lib/noop.rb b/tests/beaker_tests/noop.rb similarity index 100% rename from tests/beaker_tests/lib/noop.rb rename to tests/beaker_tests/noop.rb From 6228a0fef4e43026fc4dba18c0ac39a2232f7bc1 Mon Sep 17 00:00:00 2001 From: Rob Gries Date: Fri, 5 Aug 2016 09:34:05 -0500 Subject: [PATCH 033/203] Munge events value 'false' to 'size_disable' (#366) Due to recent platform changes, the CLI for `event_history events size disable` now results in the config `no event-history events`. This change munges the manifest value of `false` to `size_disable`. --- lib/puppet/type/cisco_bgp.rb | 1 + tests/beaker_tests/cisco_bgp/test_bgp.rb | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/puppet/type/cisco_bgp.rb b/lib/puppet/type/cisco_bgp.rb index 9a1ed87f3..6e5f8337b 100644 --- a/lib/puppet/type/cisco_bgp.rb +++ b/lib/puppet/type/cisco_bgp.rb @@ -377,6 +377,7 @@ def insync?(is) munge do |value| value = 'size_small' if value == 'true' + value = 'size_disable' if value == 'false' value.to_sym end diff --git a/tests/beaker_tests/cisco_bgp/test_bgp.rb b/tests/beaker_tests/cisco_bgp/test_bgp.rb index 9a4946025..64ed5e835 100644 --- a/tests/beaker_tests/cisco_bgp/test_bgp.rb +++ b/tests/beaker_tests/cisco_bgp/test_bgp.rb @@ -235,6 +235,10 @@ def unsupported_properties(tests, id) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown do + resource_absent_cleanup(agent, 'cisco_bgp') + end + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") @@ -263,9 +267,6 @@ def unsupported_properties(tests, id) logger.info("\n#{'-' * 60}\nSection 3. Title Pattern Testing") test_harness_run(tests, :title_patterns_1) test_harness_run(tests, :title_patterns_2) - - # ------------------------------------------------------------------- - resource_absent_cleanup(agent, 'cisco_bgp') end logger.info("TestCase :: #{tests[:resource_name]} :: End") From 86933e3da98c824adca9357cdd3d7c2ddf922f6a Mon Sep 17 00:00:00 2001 From: saichint Date: Fri, 5 Aug 2016 07:43:06 -0700 Subject: [PATCH 034/203] Ospf bfd for interfaces (#367) * Fix interface_ospf provider and beaker * doc * doc * review comments * bfd added to router ospf vrf * bfd_per_link added to interface_portchannel * add bfd and network to int ospf * add bfd echo to intf * changelog * doc --- CHANGELOG.md | 9 + README.md | 18 +- examples/cisco/demo_interface.pp | 2 + examples/cisco/demo_ospf.pp | 4 + examples/cisco/demo_portchannel.pp | 1 + lib/puppet/provider/cisco_interface/cisco.rb | 1 + .../provider/cisco_interface_ospf/cisco.rb | 2 + .../cisco_interface_portchannel/cisco.rb | 1 + lib/puppet/provider/cisco_ospf_vrf/cisco.rb | 26 +- lib/puppet/type/cisco_interface.rb | 6 + lib/puppet/type/cisco_interface_ospf.rb | 14 + .../type/cisco_interface_portchannel.rb | 7 + lib/puppet/type/cisco_ospf_vrf.rb | 7 + .../cisco_interface/test_interface_L3.rb | 3 + .../test_interface_ospf.rb | 4 + .../test_interface_portchannel.rb | 12 + .../ospfvrf_provider_defaults.rb | 186 ----------- .../ospfvrf_provider_negatives.rb | 291 ------------------ .../ospfvrf_provider_nondefaults.rb | 186 ----------- .../beaker_tests/cisco_ospf_vrf/ospfvrflib.rb | 235 -------------- .../cisco_ospf_vrf/test_ospf_vrf.rb | 156 ++++++++++ 21 files changed, 267 insertions(+), 904 deletions(-) delete mode 100644 tests/beaker_tests/cisco_ospf_vrf/ospfvrf_provider_defaults.rb delete mode 100644 tests/beaker_tests/cisco_ospf_vrf/ospfvrf_provider_negatives.rb delete mode 100644 tests/beaker_tests/cisco_ospf_vrf/ospfvrf_provider_nondefaults.rb delete mode 100644 tests/beaker_tests/cisco_ospf_vrf/ospfvrflib.rb create mode 100644 tests/beaker_tests/cisco_ospf_vrf/test_ospf_vrf.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fd1f216f..644ea8deb 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,15 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `cisco_ospf_area_vlink` type and provider. ### Added +- Extend cisco_interface with attributes: + - `bfd_echo` +- Extend cisco_interface_ospf with attributes: + - `bfd` + - `network_type` +- Extend cisco_interface_portchannel with attributes: + - `bfd_per_link` +- Extend cisco_ospf_vrf with attributes: + - `bfd` - Extend cisco_bgp_neighbor with attributes: - `bfd` - Extended `cisco_bgp_af` to include l2vpn/evpn address-family support diff --git a/README.md b/README.md index 935d29a8e..e676514e6 100644 --- a/README.md +++ b/README.md @@ -1835,7 +1835,7 @@ Manages a Cisco Network Interface. Any resource dependency should be run before |:---------|:-------------| | `pvlan_mapping` | Not supported on N8k | | `switchport_pvlan_host` | Not supported on N8k | -| `switchport_pvlan_host_association | Not supported on N8k | +| `switchport_pvlan_host_association` | Not supported on N8k | | `switchport_pvlan_mapping` | Not supported on N8k | | `switchport_pvlan_mapping_trunk` | Not supported on N3k,N8k | | `switchport_pvlan_promiscuous` | Not supported on N8k | @@ -1861,6 +1861,10 @@ Name of the interface on the network element. Valid value is a string. #### Properties +###### `bfd_echo` +Enables bfd echo function for all address families. Valid values are 'true', 'false', and +'default'. This property is not applicable for loopback interfaces. + ###### `description` Description of the interface. Valid values are a string or the keyword 'default'. @@ -2180,6 +2184,9 @@ Name of this cisco_interface resource. Valid value is a string. ##### `ospf` Name of the cisco_ospf resource. Valid value is a string. +###### `bfd` +Enables bfd at interface level. This overrides the bfd variable set at the ospf router level. Valid values are 'true', 'false', or 'default'. + ##### `cost` The cost associated with this cisco_interface_ospf instance. Valid value is an integer or the keyword 'default'. @@ -2220,6 +2227,9 @@ Valid values are 'cleartext', '3des' or 'cisco_type_7' encryption, and ##### `message_digest_password` Specifies the message_digest password. Valid value is a string or the keyword 'default'. +##### `network_type` +Specifies the network type of this interface. Valid values are 'broadcast', 'p2p' or the keyword 'default'. 'broadcast' type is not applicable on loopback interfaces. + ##### `area` *Required*. Ospf area associated with this cisco_interface_ospf instance. Valid values are a string, formatted as an IP address (i.e. "0.0.0.0") or as an integer. @@ -2247,6 +2257,9 @@ Manages configuration of a portchannel interface instance. ##### `ensure` Determine whether the config should be present or not. Valid values are 'present' and 'absent'. +##### `bfd_per_link` +Enables BFD sessions on each port-channel link. Valid values are true, false or 'default'. + ##### `lacp_graceful_convergence` port-channel lacp graceful convergence. Valid values are true, false or 'default'. @@ -2683,6 +2696,9 @@ Name of the ospf instance. Valid value is a string. ##### `router_id` Router Identifier (ID) of the OSPF router VRF instance. Valid values are a string or the keyword 'default'. +##### `bfd` +Enables bfd on all the OSPF interfaces on this router. The individual interfaces can override this. Valid values are true, false or keyword 'default' + ##### `default_metric` Specify the default Metric value. Valid values are an integer or the keyword 'default'. diff --git a/examples/cisco/demo_interface.pp b/examples/cisco/demo_interface.pp index 01f7d9350..6b355c1af 100755 --- a/examples/cisco/demo_interface.pp +++ b/examples/cisco/demo_interface.pp @@ -39,6 +39,7 @@ cisco_interface { 'Ethernet1/1' : shutdown => true, switchport_mode => disabled, + bfd_echo => false, description => 'managed by puppet', ipv4_address => '192.168.55.5', ipv4_netmask_length => 24, @@ -84,6 +85,7 @@ } cisco_interface { 'Vlan22': + bfd_echo => false, svi_autostate => $svi_autostate, svi_management => true, ipv4_arp_timeout => 300, diff --git a/examples/cisco/demo_ospf.pp b/examples/cisco/demo_ospf.pp index 09b574048..82aa18257 100644 --- a/examples/cisco/demo_ospf.pp +++ b/examples/cisco/demo_ospf.pp @@ -28,6 +28,7 @@ cisco_interface_ospf { 'Ethernet1/4 Sample': ensure => present, area => 200, + bfd => true, cost => '200', hello_interval => 'default', dead_interval => '200', @@ -36,12 +37,14 @@ message_digest_algorithm_type => md5, message_digest_encryption_type => cisco_type_7, message_digest_password => $md_password, + network_type => 'p2p', passive_interface => true, } cisco_ospf_vrf { 'dark_blue default': ensure => 'present', auto_cost => '45000', + bfd => true, default_metric => '5', log_adjacency => 'detail', timer_throttle_lsa_hold => '5500', @@ -55,6 +58,7 @@ cisco_ospf_vrf { 'dark_blue vrf1': ensure => 'present', auto_cost => '46000', + bfd => true, default_metric => '10', log_adjacency => 'log', timer_throttle_lsa_hold => '5600', diff --git a/examples/cisco/demo_portchannel.pp b/examples/cisco/demo_portchannel.pp index cadda3644..76fe86a75 100644 --- a/examples/cisco/demo_portchannel.pp +++ b/examples/cisco/demo_portchannel.pp @@ -68,6 +68,7 @@ cisco_interface_portchannel {'port-channel100': ensure => 'present', + bfd_per_link => true, lacp_graceful_convergence => false, lacp_max_bundle => 10, lacp_min_links => 2, diff --git a/lib/puppet/provider/cisco_interface/cisco.rb b/lib/puppet/provider/cisco_interface/cisco.rb index 3dd445300..e7c4dd7c9 100644 --- a/lib/puppet/provider/cisco_interface/cisco.rb +++ b/lib/puppet/provider/cisco_interface/cisco.rb @@ -72,6 +72,7 @@ :vpc_id, ] INTF_BOOL_PROPS = [ + :bfd_echo, :fabric_forwarding_anycast_gateway, :ipv4_forwarding, :ipv4_pim_sparse_mode, diff --git a/lib/puppet/provider/cisco_interface_ospf/cisco.rb b/lib/puppet/provider/cisco_interface_ospf/cisco.rb index 7a0cfac05..5f3178b02 100644 --- a/lib/puppet/provider/cisco_interface_ospf/cisco.rb +++ b/lib/puppet/provider/cisco_interface_ospf/cisco.rb @@ -40,10 +40,12 @@ :message_digest_algorithm_type, :message_digest_encryption_type, :message_digest_password, + :network_type, ] INTF_OSPF_BOOL_PROPS = [ :passive_interface, + :bfd, :message_digest, ] diff --git a/lib/puppet/provider/cisco_interface_portchannel/cisco.rb b/lib/puppet/provider/cisco_interface_portchannel/cisco.rb index debed6294..a44103adc 100644 --- a/lib/puppet/provider/cisco_interface_portchannel/cisco.rb +++ b/lib/puppet/provider/cisco_interface_portchannel/cisco.rb @@ -37,6 +37,7 @@ :port_hash_distribution, ] INTF_PC_BOOL_PROPS = [ + :bfd_per_link, :lacp_graceful_convergence, :lacp_suspend_individual, :port_load_defer, diff --git a/lib/puppet/provider/cisco_ospf_vrf/cisco.rb b/lib/puppet/provider/cisco_ospf_vrf/cisco.rb index e9c4c7919..41ee11063 100644 --- a/lib/puppet/provider/cisco_ospf_vrf/cisco.rb +++ b/lib/puppet/provider/cisco_ospf_vrf/cisco.rb @@ -32,15 +32,23 @@ mk_resource_methods # Property symbol array for method auto-generation. - OSPF_VRF_PROPS = [ + OSPF_VRF_NON_BOOL_PROPS = [ :default_metric, :log_adjacency, :router_id, :timer_throttle_lsa_start, :timer_throttle_lsa_hold, :timer_throttle_lsa_max, :timer_throttle_spf_start, :timer_throttle_spf_hold, :timer_throttle_spf_max ] + OSPF_VRF_BOOL_PROPS = [ + :bfd + ] + + OSPF_VRF_ALL_PROPS = OSPF_VRF_NON_BOOL_PROPS + OSPF_VRF_BOOL_PROPS + PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@vrf', - OSPF_VRF_PROPS) + OSPF_VRF_NON_BOOL_PROPS) + PuppetX::Cisco::AutoGen.mk_puppet_methods(:bool, self, '@vrf', + OSPF_VRF_BOOL_PROPS) def initialize(value={}) super(value) @@ -59,9 +67,17 @@ def self.properties_get(ospf, name, vrf) ensure: :present, } # Call node_utils getter for each property - OSPF_VRF_PROPS.each do |prop| + OSPF_VRF_NON_BOOL_PROPS.each do |prop| current_state[prop] = vrf.send(prop) end + OSPF_VRF_BOOL_PROPS.each do |prop| + val = vrf.send(prop) + if val.nil? + current_state[prop] = nil + else + current_state[prop] = val ? :true : :false + end + end # Special Cases # Display cost_value in MBPS cost_value, cost_type = vrf.auto_cost @@ -106,7 +122,7 @@ def destroy end def properties_set(new_vrf=false) - OSPF_VRF_PROPS.each do |prop| + OSPF_VRF_ALL_PROPS.each do |prop| next unless @resource[prop] send("#{prop}=", @resource[prop]) if new_vrf unless @property_flush[prop].nil? @@ -219,7 +235,7 @@ def puts_config # Dump all current properties for this interface current = sprintf("\n%30s: %s", 'vrf', @vrf.name) - OSPF_VRF_PROPS.each do |prop| + OSPF_VRF_ALL_PROPS.each do |prop| current.concat(sprintf("\n%30s: %s", prop, @vrf.send(prop))) end debug current diff --git a/lib/puppet/type/cisco_interface.rb b/lib/puppet/type/cisco_interface.rb index 9a629b3c2..afd638c13 100755 --- a/lib/puppet/type/cisco_interface.rb +++ b/lib/puppet/type/cisco_interface.rb @@ -130,6 +130,12 @@ ensurable + newproperty(:bfd_echo) do + desc 'Enables bfd echo function for all address families.' + + newvalues(:true, :false, :default) + end # property bfd_echo + newproperty(:description) do desc "Description of the interface. Valid values are string, keyword 'default'." diff --git a/lib/puppet/type/cisco_interface_ospf.rb b/lib/puppet/type/cisco_interface_ospf.rb index 8189eec97..fe5dff162 100644 --- a/lib/puppet/type/cisco_interface_ospf.rb +++ b/lib/puppet/type/cisco_interface_ospf.rb @@ -31,6 +31,7 @@ cisco_interface_ospf {\"Ethernet1/8 green\": ensure => present, area => \"0.0.0.0\", + bfd => true, cost => 10, hello_interval => 10, dead_interval => 40, @@ -40,6 +41,7 @@ message_digest_algorithm_type => md5, message_digest_encryption_type => \"clear\", message_digest_password => \"xxxxx\", + network_type => 'p2p', }" ensurable @@ -82,6 +84,12 @@ def name desc 'dummy paramenter to support puppet resource command' end + newproperty(:bfd) do + desc 'Enable bfd on this interface.' + + newvalues(:true, :false, :default) + end + newproperty(:cost) do desc "The cost associated with this cisco_interface_ospf instance. Valid values are integer, keyword 'default'" @@ -172,6 +180,12 @@ def name munge { |value| value == 'default' ? :default : value } end + newproperty(:network_type) do + desc 'Network type of this interface.' + + newvalues(:broadcast, :p2p, :default) + end + newproperty(:area) do desc "Ospf area associated with this cisco_interface_ospf instance. Valid values are string, formatted as an IP address diff --git a/lib/puppet/type/cisco_interface_portchannel.rb b/lib/puppet/type/cisco_interface_portchannel.rb index e57dce01e..d06e28bad 100644 --- a/lib/puppet/type/cisco_interface_portchannel.rb +++ b/lib/puppet/type/cisco_interface_portchannel.rb @@ -29,6 +29,7 @@ Example: cisco_interface_portchannel {"port-channel100": ensure => 'present', + bfd_per_link => true, lacp_graceful_convergence => false, lacp_max_bundle => 10, lacp_min_links => 2, @@ -70,6 +71,12 @@ def self.title_patterns ensurable + newproperty(:bfd_per_link) do + desc 'Enable BFD sessions on each port-channel link.' + + newvalues(:true, :false, :default) + end # property bfd_per_link + newproperty(:lacp_graceful_convergence) do desc "port-channel lacp graceful convergence. Disable this only with lacp ports connected to Non-Nexus peer. Disabling this with Nexus peer diff --git a/lib/puppet/type/cisco_ospf_vrf.rb b/lib/puppet/type/cisco_ospf_vrf.rb index a75806571..de6833238 100644 --- a/lib/puppet/type/cisco_ospf_vrf.rb +++ b/lib/puppet/type/cisco_ospf_vrf.rb @@ -31,6 +31,7 @@ cisco_ospf_vrf {\"green test\": ensure => present, router_id => \"192.168.1.1\", + bfd => true, default_metric => 2, log_adjancency => log, timer_throttle_lsa_start => 0, @@ -111,6 +112,12 @@ def name end end # property router id + newproperty(:bfd) do + desc 'Enable bfd on all the OSPF interfaces on this router' + + newvalues(:true, :false, :default) + end # property bfd + newproperty(:default_metric) do desc "Specify the default Metric value. Valid values are integer, keyword 'default'." diff --git a/tests/beaker_tests/cisco_interface/test_interface_L3.rb b/tests/beaker_tests/cisco_interface/test_interface_L3.rb index 7d452b780..76fb68556 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_L3.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_L3.rb @@ -47,6 +47,7 @@ preclean_intf: true, sys_def_switchport: false, manifest_props: { + bfd_echo: 'default', description: 'default', duplex: 'default', ipv4_forwarding: 'default', @@ -73,6 +74,7 @@ title_pattern: intf, sys_def_switchport: false, manifest_props: { + bfd_echo: false, description: 'Configured with Puppet', shutdown: true, ipv4_address: '1.1.1.1', @@ -131,6 +133,7 @@ def unsupported_properties(_tests, id) if operating_system == 'ios_xr' unprops << + :bfd_echo << :duplex << :ipv4_forwarding << :ipv4_pim_sparse_mode << diff --git a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb index 253c5757d..a2ddc31d4 100644 --- a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb +++ b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb @@ -47,6 +47,7 @@ preclean_intf: true, manifest_props: { area: 200, + bfd: 'default', cost: 'default', dead_interval: 'default', hello_interval: 'default', @@ -55,6 +56,7 @@ message_digest_algorithm_type: 'default', message_digest_encryption_type: 'default', message_digest_password: 'default', + network_type: 'default', passive_interface: 'default', }, code: [0, 2], @@ -79,6 +81,7 @@ preclean_intf: true, manifest_props: { area: 200, + bfd: 'true', cost: '200', dead_interval: '200', hello_interval: '200', @@ -87,6 +90,7 @@ message_digest_algorithm_type: 'md5', message_digest_encryption_type: 'cisco_type_7', message_digest_password: '046E1803362E595C260E0B240619050A2D', + network_type: 'p2p', passive_interface: 'true', }, resource: { diff --git a/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb b/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb index 700793961..050ca9b4a 100755 --- a/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb +++ b/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb @@ -103,6 +103,7 @@ tests['default_properties_asym'] = { title_pattern: 'port-channel100', manifest_props: " + bfd_per_link => 'default', lacp_graceful_convergence => 'default', lacp_max_bundle => 'default', lacp_min_links => 'default', @@ -112,6 +113,7 @@ ", code: [0, 2], resource_props: { + 'bfd_per_link' => 'false', 'lacp_graceful_convergence' => 'true', 'lacp_max_bundle' => '16', 'lacp_min_links' => '1', @@ -124,6 +126,7 @@ tests['non_default_properties_asym'] = { title_pattern: 'port-channel100', manifest_props: " + bfd_per_link => 'true', lacp_graceful_convergence => 'false', lacp_max_bundle => '10', lacp_min_links => '3', @@ -132,6 +135,7 @@ port_load_defer => 'true', ", resource_props: { + 'bfd_per_link' => 'true', 'lacp_graceful_convergence' => 'false', 'lacp_max_bundle' => '10', 'lacp_min_links' => '3', @@ -144,6 +148,7 @@ tests['default_properties_sym'] = { title_pattern: 'port-channel100', manifest_props: " + bfd_per_link => 'default', lacp_graceful_convergence => 'default', lacp_max_bundle => 'default', lacp_min_links => 'default', @@ -153,6 +158,7 @@ ", code: [0, 2], resource_props: { + 'bfd_per_link' => 'false', 'lacp_graceful_convergence' => 'true', 'lacp_max_bundle' => '32', 'lacp_min_links' => '1', @@ -165,6 +171,7 @@ tests['non_default_properties_sym'] = { title_pattern: 'port-channel100', manifest_props: " + bfd_per_link => 'true', lacp_graceful_convergence => 'false', lacp_max_bundle => '10', lacp_min_links => '3', @@ -173,6 +180,7 @@ port_load_defer => 'true', ", resource_props: { + 'bfd_per_link' => 'true', 'lacp_graceful_convergence' => 'false', 'lacp_max_bundle' => '10', 'lacp_min_links' => '3', @@ -185,6 +193,7 @@ tests['default_properties_eth'] = { title_pattern: 'port-channel100', manifest_props: " + bfd_per_link => 'default', lacp_graceful_convergence => 'default', lacp_max_bundle => 'default', lacp_min_links => 'default', @@ -192,6 +201,7 @@ ", code: [0, 2], resource_props: { + 'bfd_per_link' => 'false', 'lacp_graceful_convergence' => 'true', 'lacp_max_bundle' => '16', 'lacp_min_links' => '1', @@ -202,12 +212,14 @@ tests['non_default_properties_eth'] = { title_pattern: 'port-channel100', manifest_props: " + bfd_per_link => 'true', lacp_graceful_convergence => 'false', lacp_max_bundle => '10', lacp_min_links => '3', lacp_suspend_individual => 'false', ", resource_props: { + 'bfd_per_link' => 'true', 'lacp_graceful_convergence' => 'false', 'lacp_max_bundle' => '10', 'lacp_min_links' => '3', diff --git a/tests/beaker_tests/cisco_ospf_vrf/ospfvrf_provider_defaults.rb b/tests/beaker_tests/cisco_ospf_vrf/ospfvrf_provider_defaults.rb deleted file mode 100644 index 6c81ec913..000000000 --- a/tests/beaker_tests/cisco_ospf_vrf/ospfvrf_provider_defaults.rb +++ /dev/null @@ -1,186 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# OspfVrf-Provider-Defaults.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet OSPFVRF resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a OSPFVRF resource test that tests for default values for -# auto_cost, default_metric, log_adjacency, timer_throttle_lsa_hold, -# timer_throttle_lsa_max, timer_throttle_lsa_start, timer_throttle_spf_hold, -# timer_throttle_spf_max and timer_throttle_spf_start attributes of a -# cisco_ospf_vrf resource when created with 'ensure' => 'present'. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-4 deal with cisco_ospf_vrf resource creation and its -# verification using Puppet Agent and the switch running-config. -# Steps 5-7 deal with cisco_ospf_vrf resource deletion and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and OspfVrfLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../ospfvrflib.rb', __FILE__) - -result = 'PASS' -testheader = 'OSPFVRF Resource :: All Attributes Defaults' - -# @test_name [TestCase] Executes defaults testcase for OSPFVRF Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - resource_absent_cleanup(agent, 'cisco_ospf_vrf', - 'Setup switch for cisco_ospf_vrf provider test') - - logger.info("TestStep :: Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource present manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfVrfLib.create_ospfvrf_manifest_present) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource present manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_ospf_vrf resource on agent using resource cmd. - step 'TestStep :: Check cisco_ospf_vrf resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_ospf_vrf 'test default'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'auto_cost' => '40000', - 'default_metric' => '0', - 'log_adjacency' => 'none', - 'timer_throttle_lsa_hold' => '5000', - 'timer_throttle_lsa_max' => '5000', - 'timer_throttle_lsa_start' => '0', - 'timer_throttle_spf_hold' => '1000', - 'timer_throttle_spf_max' => '5000', - 'timer_throttle_spf_start' => '200' }, - false, self, logger) - end - - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_ospf_vrf 'test green'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'auto_cost' => '40000', - 'default_metric' => '0', - 'log_adjacency' => 'none', - 'timer_throttle_lsa_hold' => '5000', - 'timer_throttle_lsa_max' => '5000', - 'timer_throttle_lsa_start' => '0', - 'timer_throttle_spf_hold' => '1000', - 'timer_throttle_spf_max' => '5000', - 'timer_throttle_spf_start' => '200' }, - false, self, logger) - end - - logger.info("Check cisco_ospf_vrf resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource absent manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfVrfLib.create_ospfvrf_manifest_absent) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource absent manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_ospf_vrf resource on agent using resource cmd. - step 'TestStep :: Check cisco_ospf_vrf resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_ospf_vrf 'test default'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'auto_cost' => '40000', - 'default_metric' => '0', - 'log_adjacency' => 'none', - 'timer_throttle_lsa_hold' => '5000', - 'timer_throttle_lsa_max' => '5000', - 'timer_throttle_lsa_start' => '0', - 'timer_throttle_spf_hold' => '1000', - 'timer_throttle_spf_max' => '5000', - 'timer_throttle_spf_start' => '200' }, - true, self, logger) - end - - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_ospf_vrf 'test green'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'auto_cost' => '40000', - 'default_metric' => '0', - 'log_adjacency' => 'none', - 'timer_throttle_lsa_hold' => '5000', - 'timer_throttle_lsa_max' => '5000', - 'timer_throttle_lsa_start' => '0', - 'timer_throttle_spf_hold' => '1000', - 'timer_throttle_spf_max' => '5000', - 'timer_throttle_spf_start' => '200' }, - true, self, logger) - end - - logger.info("Check cisco_ospf_vrf resource absence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_ospf_vrf/ospfvrf_provider_negatives.rb b/tests/beaker_tests/cisco_ospf_vrf/ospfvrf_provider_negatives.rb deleted file mode 100644 index a8a28c5c8..000000000 --- a/tests/beaker_tests/cisco_ospf_vrf/ospfvrf_provider_negatives.rb +++ /dev/null @@ -1,291 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# OspfVrf-Provider-Negatives.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet OSPFVRF resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a OSPFVRF resource test that tests for negative values for -# auto_cost, default_metric, log_adjacency, timer_throttle_lsa_hold, -# timer_throttle_lsa_max, timer_throttle_lsa_start, timer_throttle_spf_hold, -# timer_throttle_spf_max and timer_throttle_spf_start attributes of a -# cisco_ospf_vrf resource when created with 'ensure' => 'present'. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# The next set of teststeps deal with attribute negative tests and their -# verification using Puppet Agent and the switch running-config. -# -# Steps 2-4 deal with cisco_ospf resource creation and its -# verification using Puppet Agent and the switch running-config. -# Steps 5-7 deal with cisco_ospf resource deletion and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and OspfVrfLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../ospfvrflib.rb', __FILE__) - -result = 'PASS' -testheader = 'OSPFVRF Resource :: All Attributes Negatives' - -# @test_name [TestCase] Executes negatives testcase for OSPFVRF Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - resource_absent_cleanup(agent, 'cisco_ospf_vrf', - 'Setup switch for cisco_ospf_vrf provider test') - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfVrfLib.create_ospfvrf_manifest_autocost_negative) - - # Expected exit_code is 6 since this is a puppet agent cmd with failures. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [6]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_ospf_vrf resource on agent using resource cmd. - step 'TestStep :: Check cisco_ospf_vrf resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_ospf_vrf 'test green'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'auto_cost' => OspfVrfLib::AUTOCOST_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_ospf_vrf resource absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfVrfLib.create_ospfvrf_manifest_defaultmetric_negative) - - # Expected exit_code is 6 since this is a puppet agent cmd with failures. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [6]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_ospf_vrf resource on agent using resource cmd. - step 'TestStep :: Check cisco_ospf_vrf resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_ospf_vrf 'test green'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'default_metric' => OspfVrfLib::DEFAULTMETRIC_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_ospf_vrf resource absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfVrfLib.create_ospfvrf_manifest_lsahold_negative) - - # Expected exit_code is 6 since this is a puppet agent cmd with failures. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [6]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_ospf_vrf resource on agent using resource cmd. - step 'TestStep :: Check cisco_ospf_vrf resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_ospf_vrf 'test green'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'timer_throttle_lsa_hold' => OspfVrfLib::LSAHOLD_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_ospf_vrf resource absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfVrfLib.create_ospfvrf_manifest_lsamax_negative) - - # Expected exit_code is 6 since this is a puppet agent cmd with failures. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [6]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_ospf_vrf resource on agent using resource cmd. - step 'TestStep :: Check cisco_ospf_vrf resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_ospf_vrf 'test green'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'timer_throttle_lsa_max' => OspfVrfLib::LSAMAX_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_ospf_vrf resource absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfVrfLib.create_ospfvrf_manifest_lsastart_negative) - - # Expected exit_code is 6 since this is a puppet agent cmd with failures. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [6]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_ospf_vrf resource on agent using resource cmd. - step 'TestStep :: Check cisco_ospf_vrf resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_ospf_vrf 'test green'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'timer_throttle_lsa_start' => OspfVrfLib::LSASTART_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_ospf_vrf resource absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfVrfLib.create_ospfvrf_manifest_spfhold_negative) - - # Expected exit_code is 6 since this is a puppet agent cmd with failures. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [6]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_ospf_vrf resource on agent using resource cmd. - step 'TestStep :: Check cisco_ospf_vrf resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_ospf_vrf 'test green'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'timer_throttle_spf_hold' => OspfVrfLib::SPFHOLD_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_ospf_vrf resource absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfVrfLib.create_ospfvrf_manifest_spfmax_negative) - - # Expected exit_code is 6 since this is a puppet agent cmd with failures. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [6]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_ospf_vrf resource on agent using resource cmd. - step 'TestStep :: Check cisco_ospf_vrf resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_ospf_vrf 'test green'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'timer_throttle_spf_max' => OspfVrfLib::SPFMAX_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_ospf_vrf resource absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfVrfLib.create_ospfvrf_manifest_spfstart_negative) - - # Expected exit_code is 6 since this is a puppet agent cmd with failures. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [6]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_ospf_vrf resource on agent using resource cmd. - step 'TestStep :: Check cisco_ospf_vrf resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_ospf_vrf 'test green'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'timer_throttle_spf_start' => OspfVrfLib::SPFSTART_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_ospf_vrf resource absence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_ospf_vrf/ospfvrf_provider_nondefaults.rb b/tests/beaker_tests/cisco_ospf_vrf/ospfvrf_provider_nondefaults.rb deleted file mode 100644 index 7d7bdba54..000000000 --- a/tests/beaker_tests/cisco_ospf_vrf/ospfvrf_provider_nondefaults.rb +++ /dev/null @@ -1,186 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# OspfVrf-Provider-NonDefaults.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet OSPFVRF resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a OSPFVRF resource test that tests for nondefault values for -# auto_cost, default_metric, log_adjacency, timer_throttle_lsa_hold, -# timer_throttle_lsa_max, timer_throttle_lsa_start, timer_throttle_spf_hold, -# timer_throttle_spf_max and timer_throttle_spf_start attributes of a -# cisco_ospf_vrf resource when created with 'ensure' => 'present'. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-4 deal with cisco_ospf_vrf resource creation and its -# verification using Puppet Agent and the switch running-config. -# Steps 5-7 deal with cisco_ospf_vrf resource deletion and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and OspfVrfLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../ospfvrflib.rb', __FILE__) - -result = 'PASS' -testheader = 'OSPFVRF Resource :: All Attributes NonDefaults' - -# @test_name [TestCase] Executes nondefaults testcase for OSPFVRF Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - resource_absent_cleanup(agent, 'cisco_ospf_vrf', - 'Setup switch for cisco_ospf_vrf provider test') - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource nondefaults manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfVrfLib.create_ospfvrf_manifest_nondefaults) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource nondefaults manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_ospf_vrf resource on agent using resource cmd. - step 'TestStep :: Check cisco_ospf_vrf resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_ospf_vrf 'test default'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'auto_cost' => '40000', - 'default_metric' => '0', - 'log_adjacency' => 'none', - 'timer_throttle_lsa_hold' => '5000', - 'timer_throttle_lsa_max' => '5000', - 'timer_throttle_lsa_start' => '0', - 'timer_throttle_spf_hold' => '1000', - 'timer_throttle_spf_max' => '5000', - 'timer_throttle_spf_start' => '200' }, - false, self, logger) - end - - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_ospf_vrf 'test green'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'auto_cost' => '80000', - 'default_metric' => '1', - 'log_adjacency' => 'log', - 'timer_throttle_lsa_hold' => '2000', - 'timer_throttle_lsa_max' => '10000', - 'timer_throttle_lsa_start' => '1', - 'timer_throttle_spf_hold' => '2000', - 'timer_throttle_spf_max' => '10000', - 'timer_throttle_spf_start' => '400' }, - false, self, logger) - end - - logger.info("Check cisco_ospf_vrf resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource absent manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, OspfVrfLib.create_ospfvrf_manifest_absent) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource absent manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_ospf_vrf resource on agent using resource cmd. - step 'TestStep :: Check cisco_ospf_vrf resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_ospf_vrf 'test default'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'auto_cost' => '40000', - 'default_metric' => '0', - 'log_adjacency' => 'none', - 'timer_throttle_lsa_hold' => '5000', - 'timer_throttle_lsa_max' => '5000', - 'timer_throttle_lsa_start' => '0', - 'timer_throttle_spf_hold' => '1000', - 'timer_throttle_spf_max' => '5000', - 'timer_throttle_spf_start' => '200' }, - true, self, logger) - end - - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource cisco_ospf_vrf 'test green'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'auto_cost' => '80000', - 'default_metric' => '1', - 'log_adjacency' => 'log', - 'timer_throttle_lsa_hold' => '2000', - 'timer_throttle_lsa_max' => '10000', - 'timer_throttle_lsa_start' => '1', - 'timer_throttle_spf_hold' => '2000', - 'timer_throttle_spf_max' => '10000', - 'timer_throttle_spf_start' => '400' }, - true, self, logger) - end - - logger.info("Check cisco_ospf_vrf resource absence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_ospf_vrf/ospfvrflib.rb b/tests/beaker_tests/cisco_ospf_vrf/ospfvrflib.rb deleted file mode 100644 index 72ca55fe5..000000000 --- a/tests/beaker_tests/cisco_ospf_vrf/ospfvrflib.rb +++ /dev/null @@ -1,235 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### - -# Require UtilityLib.rb path. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) - -# OSPFVRF Utility Library: -# ------------------------ -# ospfvrflib.rb -# -# This is the utility library for the OSPFVRF provider Beaker test cases that -# contains the common methods used across the OSPFVRF testsuite's cases. The -# library is implemented as a module with related methods and constants defined -# inside it for use as a namespace. All of the methods are defined as module -# methods. -# -# Every Beaker OSPFVRF test case that runs an instance of Beaker::TestCase -# requires OspfVrfLib module. -# -# The module has a single set of methods: -# A. Methods to create manifests for cisco_ospf_vrf Puppet provider test cases. -module OspfVrfLib - # Group of Constants used in negative tests for OSPFVRF provider. - AUTOCOST_NEGATIVE = '100000000' - DEFAULTMETRIC_NEGATIVE = '-1' - LSAHOLD_NEGATIVE = '-1' - LSAMAX_NEGATIVE = '-1' - LSASTART_NEGATIVE = '-1' - SPFHOLD_NEGATIVE = '-1' - SPFMAX_NEGATIVE = '-1' - SPFSTART_NEGATIVE = '-1' - - # A. Methods to create manifests for cisco_ospf_vrf Puppet provider test cases. - - # Method to create a manifest for OSPFVRF resource attribute 'ensure' where - # 'ensure' is set to present. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ospfvrf_manifest_present - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_ospf_vrf { 'test green': - ensure => present, - auto_cost => 'default', - default_metric => 'default', - log_adjacency => 'default', - timer_throttle_lsa_hold => 'default', - timer_throttle_lsa_max => 'default', - timer_throttle_lsa_start => 'default', - timer_throttle_spf_hold => 'default', - timer_throttle_spf_max => 'default', - timer_throttle_spf_start => 'default', - } -} -EOF" - manifest_str - end - - # Method to create a manifest for OSPFVRF resource attribute 'ensure' where - # 'ensure' is set to absent. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ospfvrf_manifest_absent - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_ospf_vrf { 'test green': - ensure => absent, - } - cisco_ospf { 'test': - ensure => absent, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for OSPFVRF resource attributes: - # ensure, auto_cost, default_metric, log_adjacency, timer_throttle_lsa_hold, - # timer_throttle_lsa_max, timer_throttle_lsa_start, timer_throttle_spf_hold, - # timer_throttle_spf_max and timer_throttle_spf_start. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ospfvrf_manifest_nondefaults - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_ospf_vrf { 'test green': - ensure => present, - auto_cost => '80000', - default_metric => '1', - log_adjacency => 'log', - timer_throttle_lsa_hold => '2000', - timer_throttle_lsa_max => '10000', - timer_throttle_lsa_start => '1', - timer_throttle_spf_hold => '2000', - timer_throttle_spf_max => '10000', - timer_throttle_spf_start => '400', - } -} -EOF" - manifest_str - end - - # Method to create a manifest for OSPFVRF resource attribute 'autocost'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ospfvrf_manifest_autocost_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_ospf_vrf { 'test green': - ensure => present, - auto_cost => #{OspfVrfLib::AUTOCOST_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for OSPFVRF resource attribute 'default_metric'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ospfvrf_manifest_defaultmetric_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_ospf_vrf { 'test green': - ensure => present, - default_metric => #{OspfVrfLib::DEFAULTMETRIC_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for OSPFVRF resource attribute 'lsahold'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ospfvrf_manifest_lsahold_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_ospf_vrf { 'test green': - ensure => present, - timer_throttle_lsa_hold => #{OspfVrfLib::LSAHOLD_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for OSPFVRF resource attribute 'lsamax'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ospfvrf_manifest_lsamax_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_ospf_vrf { 'test green': - ensure => present, - timer_throttle_lsa_max => #{OspfVrfLib::LSAMAX_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for OSPFVRF resource attribute 'lsastart'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ospfvrf_manifest_lsastart_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_ospf_vrf { 'test green': - ensure => present, - timer_throttle_lsa_start => #{OspfVrfLib::LSASTART_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for OSPFVRF resource attribute 'spfhold'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ospfvrf_manifest_spfhold_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_ospf_vrf { 'test green': - ensure => present, - timer_throttle_spf_hold => #{OspfVrfLib::SPFHOLD_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for OSPFVRF resource attribute 'spfmax'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ospfvrf_manifest_spfmax_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_ospf_vrf { 'test green': - ensure => present, - timer_throttle_spf_max => #{OspfVrfLib::SPFMAX_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for OSPFVRF resource attribute 'spfstart'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ospfvrf_manifest_spfstart_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_ospf_vrf { 'test green': - ensure => present, - timer_throttle_spf_start => #{OspfVrfLib::SPFSTART_NEGATIVE}, - } -} -EOF" - manifest_str - end -end diff --git a/tests/beaker_tests/cisco_ospf_vrf/test_ospf_vrf.rb b/tests/beaker_tests/cisco_ospf_vrf/test_ospf_vrf.rb new file mode 100644 index 000000000..a4e0f798e --- /dev/null +++ b/tests/beaker_tests/cisco_ospf_vrf/test_ospf_vrf.rb @@ -0,0 +1,156 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### + +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# Test hash top-level keys +tests = { + master: master, + agent: agent, + operating_system: 'nexus', + resource_name: 'cisco_ospf_vrf', +} + +# Test hash test cases +tests[:default_1] = { + desc: '1.1 Defaults', + title_pattern: 'test default', + preclean: 'cisco_ospf', + manifest_props: { + auto_cost: 'default', + bfd: 'default', + default_metric: 'default', + log_adjacency: 'default', + timer_throttle_lsa_hold: 'default', + timer_throttle_lsa_max: 'default', + timer_throttle_lsa_start: 'default', + timer_throttle_spf_hold: 'default', + timer_throttle_spf_max: 'default', + timer_throttle_spf_start: 'default', + }, + code: [0, 2], + resource: { + auto_cost: '40000', + bfd: 'false', + default_metric: '0', + log_adjacency: 'none', + timer_throttle_lsa_hold: '5000', + timer_throttle_lsa_max: '5000', + timer_throttle_lsa_start: '0', + timer_throttle_spf_hold: '1000', + timer_throttle_spf_max: '5000', + timer_throttle_spf_start: '200', + }, +} + +# Test hash test cases +tests[:default_2] = { + desc: '1.2 Defaults', + title_pattern: 'test green', + manifest_props: { + auto_cost: 'default', + bfd: 'default', + default_metric: 'default', + log_adjacency: 'default', + timer_throttle_lsa_hold: 'default', + timer_throttle_lsa_max: 'default', + timer_throttle_lsa_start: 'default', + timer_throttle_spf_hold: 'default', + timer_throttle_spf_max: 'default', + timer_throttle_spf_start: 'default', + }, + code: [0, 2], + resource: { + auto_cost: '40000', + bfd: 'false', + default_metric: '0', + log_adjacency: 'none', + timer_throttle_lsa_hold: '5000', + timer_throttle_lsa_max: '5000', + timer_throttle_lsa_start: '0', + timer_throttle_spf_hold: '1000', + timer_throttle_spf_max: '5000', + timer_throttle_spf_start: '200', + }, +} + +# Non-default Tests. NOTE: [:resource] = [:manifest_props] for all non-default + +tests[:non_default_1] = { + desc: '2.1 Non Defaults', + title_pattern: 'test default', + manifest_props: { + auto_cost: '80000', + bfd: 'true', + default_metric: '1', + log_adjacency: 'log', + timer_throttle_lsa_hold: '2000', + timer_throttle_lsa_max: '10000', + timer_throttle_lsa_start: '1', + timer_throttle_spf_hold: '2000', + timer_throttle_spf_max: '10000', + timer_throttle_spf_start: '400', + }, +} + +tests[:non_default_2] = { + desc: '2.2 Non Defaults', + title_pattern: 'test green', + manifest_props: { + auto_cost: '70000', + bfd: 'true', + default_metric: '2', + log_adjacency: 'log', + timer_throttle_lsa_hold: '1500', + timer_throttle_lsa_max: '11000', + timer_throttle_lsa_start: '1', + timer_throttle_spf_hold: '2200', + timer_throttle_spf_max: '11000', + timer_throttle_spf_start: '430', + }, +} + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + test_harness_run(tests, :default_1) + test_harness_run(tests, :default_2) + + id = :default_2 + tests[id][:ensure] = :absent + tests[id].delete(:preclean) + test_harness_run(tests, id) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + + test_harness_run(tests, :non_default_1) + test_harness_run(tests, :non_default_2) + resource_absent_cleanup(agent, 'cisco_ospf') +end + +logger.info("TestCase :: #{tests[:resource_name]} :: End") From 7f0341c34f1a10ca1cb2a04e9aaf00aab936d232 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Fri, 5 Aug 2016 14:30:02 -0400 Subject: [PATCH 035/203] Remove get_vshell_cmd calls (#368) --- .../tacacsserver_provider_defaults.rb | 39 --------- .../tacacsserver_provider_negatives.rb | 79 ------------------- .../tacacsserver_provider_nondefaults.rb | 43 ---------- .../tacacsserverhost_provider_defaults.rb | 37 --------- .../tacacsserverhost_provider_negatives.rb | 65 --------------- .../tacacsserverhost_provider_nondefaults.rb | 37 --------- 6 files changed, 300 deletions(-) diff --git a/tests/beaker_tests/cisco_tacacs_server/tacacsserver_provider_defaults.rb b/tests/beaker_tests/cisco_tacacs_server/tacacsserver_provider_defaults.rb index 674a40f58..3ac2a3297 100644 --- a/tests/beaker_tests/cisco_tacacs_server/tacacsserver_provider_defaults.rb +++ b/tests/beaker_tests/cisco_tacacs_server/tacacsserver_provider_defaults.rb @@ -74,15 +74,6 @@ cmd_str = PUPPET_BINPATH + 'agent -t' on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - # Expected exit_code is 16 since this is a vegas shell cmd with exec error. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str, acceptable_exit_codes: [16]) do - search_pattern_in_output(stdout, - [/feature tacacs\+/], - true, self, logger) - end - logger.info("Setup switch for provider test :: #{result}") end @@ -116,21 +107,6 @@ logger.info("Check cisco_tacacs_server presence on agent :: #{result}") end - # @step [Step] Checks tacacsserver instance on agent using show cli cmds. - step 'TestStep :: Check tacacsserver instance presence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/feature tacacs\+/, - /tacacs\-server key 7/], - false, self, logger) - end - - logger.info("Check tacacsserver instance presence on agent :: #{result}") - end - # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource absent manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. @@ -161,21 +137,6 @@ logger.info("Check cisco_tacacs_server absence on agent :: #{result}") end - # @step [Step] Checks tacacsserver instance on agent using show cli cmds. - step 'TestStep :: Check tacacsserver instance absence on agent' do - # Expected exit_code is 16 since this is a vegas shell cmd with exec error. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str, acceptable_exit_codes: [16]) do - search_pattern_in_output(stdout, - [/feature tacacs\+/, - /tacacs\-server key 7/], - true, self, logger) - end - - logger.info("Check tacacsserver instance absence on agent :: #{result}") - end - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. raise_passfail_exception(result, testheader, self, logger) end diff --git a/tests/beaker_tests/cisco_tacacs_server/tacacsserver_provider_negatives.rb b/tests/beaker_tests/cisco_tacacs_server/tacacsserver_provider_negatives.rb index 422446ecb..deb4b5895 100644 --- a/tests/beaker_tests/cisco_tacacs_server/tacacsserver_provider_negatives.rb +++ b/tests/beaker_tests/cisco_tacacs_server/tacacsserver_provider_negatives.rb @@ -72,15 +72,6 @@ cmd_str = PUPPET_BINPATH + 'agent -t' on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - # Expected exit_code is 16 since this is a vegas shell cmd with exec error. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str, acceptable_exit_codes: [16]) do - search_pattern_in_output(stdout, - [/feature tacacs\+/], - true, self, logger) - end - logger.info("Setup switch for provider test :: #{result}") end @@ -110,20 +101,6 @@ logger.info("Check cisco_tacacs_server absence on agent :: #{result}") end - # @step [Step] Checks tacacsserver instance on agent using show cli cmds. - step 'TestStep :: Check tacacsserver instance absence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/tacacs\-server timeout #{TacacsServerLib::TIMEOUT_NEGATIVE}/], - true, self, logger) - end - - logger.info("Check tacacsserver instance absence on agent :: #{result}") - end - # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get negative test resource manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. @@ -150,20 +127,6 @@ logger.info("Check cisco_tacacs_server absence on agent :: #{result}") end - # @step [Step] Checks tacacsserver instance on agent using show cli cmds. - step 'TestStep :: Check tacacsserver instance absence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/tacacs\-server deadtime #{TacacsServerLib::DEADTIME_NEGATIVE}/], - true, self, logger) - end - - logger.info("Check tacacsserver instance absence on agent :: #{result}") - end - # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get negative test resource manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. @@ -190,20 +153,6 @@ logger.info("Check cisco_tacacs_server absence on agent :: #{result}") end - # @step [Step] Checks tacacsserver instance on agent using show cli cmds. - step 'TestStep :: Check tacacsserver instance absence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/tacacs\-server key #{TacacsServerLib::ENCRYPTYPE_NEGATIVE}/], - true, self, logger) - end - - logger.info("Check tacacsserver instance absence on agent :: #{result}") - end - # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get negative test resource manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. @@ -230,20 +179,6 @@ logger.info("Check cisco_tacacs_server absence on agent :: #{result}") end - # @step [Step] Checks tacacsserver instance on agent using show cli cmds. - step 'TestStep :: Check tacacsserver instance absence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/tacacs\-server key 7 #{TacacsServerLib::ENCRYPPASSWD_NEGATIVE}/], - true, self, logger) - end - - logger.info("Check tacacsserver instance absence on agent :: #{result}") - end - # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get negative test resource manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. @@ -270,20 +205,6 @@ logger.info("Check cisco_tacacs_server absence on agent :: #{result}") end - # @step [Step] Checks tacacsserver instance on agent using show cli cmds. - step 'TestStep :: Check tacacsserver instance absence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/ip tacacs source\-interface #{TacacsServerLib::SOURCEINTF_NEGATIVE}/], - true, self, logger) - end - - logger.info("Check tacacsserver instance absence on agent :: #{result}") - end - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. raise_passfail_exception(result, testheader, self, logger) end diff --git a/tests/beaker_tests/cisco_tacacs_server/tacacsserver_provider_nondefaults.rb b/tests/beaker_tests/cisco_tacacs_server/tacacsserver_provider_nondefaults.rb index 3253f67e6..e92b025c4 100644 --- a/tests/beaker_tests/cisco_tacacs_server/tacacsserver_provider_nondefaults.rb +++ b/tests/beaker_tests/cisco_tacacs_server/tacacsserver_provider_nondefaults.rb @@ -74,15 +74,6 @@ cmd_str = PUPPET_BINPATH + 'agent -t' on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - # Expected exit_code is 16 since this is a vegas shell cmd with exec error. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str, acceptable_exit_codes: [16]) do - search_pattern_in_output(stdout, - [/feature tacacs\+/], - true, self, logger) - end - logger.info("Setup switch for provider test :: #{result}") end @@ -117,23 +108,6 @@ logger.info("Check cisco_tacacs_server presence on agent :: #{result}") end - # @step [Step] Checks tacacsserver instance on agent using show cli cmds. - step 'TestStep :: Check tacacsserver instance presence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/feature tacacs\+/, - /tacacs\-server key 7 "WXYZ12"/, - %r{ip tacacs source-interface Ethernet1/4}, - /tacacs\-server timeout 50/], - false, self, logger) - end - - logger.info("Check tacacsserver instance presence on agent :: #{result}") - end - # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource absent manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. @@ -165,23 +139,6 @@ logger.info("Check cisco_tacacs_server absence on agent :: #{result}") end - # @step [Step] Checks tacacsserver instance on agent using show cli cmds. - step 'TestStep :: Check tacacsserver instance absence on agent' do - # Expected exit_code is 16 since this is a vegas shell cmd with exec error. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str, acceptable_exit_codes: [16]) do - search_pattern_in_output(stdout, - [/feature tacacs\+/, - /tacacs\-server key 7 "WXYZ12"/, - %r{ip tacacs source-interface Ethernet1/4}, - /tacacs\-server timeout 50/], - true, self, logger) - end - - logger.info("Check tacacsserver instance absence on agent :: #{result}") - end - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. raise_passfail_exception(result, testheader, self, logger) end diff --git a/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_defaults.rb b/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_defaults.rb index 7c02ce526..148620008 100644 --- a/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_defaults.rb +++ b/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_defaults.rb @@ -82,15 +82,6 @@ def cleanup cmd_str = PUPPET_BINPATH + 'agent -t' on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - # Expected exit_code is 16 since this is a vegas shell cmd with exec error. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str, acceptable_exit_codes: [16]) do - search_pattern_in_output(stdout, - [/feature tacacs\+/], - true, self, logger) - end - logger.info("Setup switch for provider test :: #{result}") end @@ -122,20 +113,6 @@ def cleanup logger.info("Check cisco_tacacs_server_host presence on agent :: #{result}") end - # @step [Step] Checks tacacsserverhost instance on agent using show cli cmds. - step 'TestStep :: Check tacacsserverhost instance presence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/tacacs\-server host samplehost1/], - false, self, logger) - end - - logger.info("Check tacacsserverhost instance presence on agent :: #{result}") - end - # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource absent manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. @@ -164,20 +141,6 @@ def cleanup logger.info("Check cisco_tacacs_server_host absence on agent :: #{result}") end - # @step [Step] Checks tacacsserverhost instance on agent using show cli cmds. - step 'TestStep :: Check tacacsserverhost instance absence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/tacacs\-server host samplehost1/], - true, self, logger) - end - - logger.info("Check tacacsserverhost instance absence on agent :: #{result}") - end - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. raise_passfail_exception(result, testheader, self, logger) end diff --git a/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_negatives.rb b/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_negatives.rb index 86f1b63d9..9ba14bba6 100644 --- a/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_negatives.rb +++ b/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_negatives.rb @@ -80,15 +80,6 @@ def cleanup cmd_str = PUPPET_BINPATH + 'agent -t' on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - # Expected exit_code is 16 since this is a vegas shell cmd with exec error. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str, acceptable_exit_codes: [16]) do - search_pattern_in_output(stdout, - [/feature tacacs\+/], - true, self, logger) - end - logger.info("Setup switch for provider test :: #{result}") end @@ -118,20 +109,6 @@ def cleanup logger.info("Check cisco_tacacs_server_host absence on agent :: #{result}") end - # @step [Step] Checks tacacsserverhost instance on agent using show cli cmds. - step 'TestStep :: Check tacacsserverhost instance absence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/timeout #{TacacsServerHostLib::TIMEOUT_NEGATIVE}/], - true, self, logger) - end - - logger.info("Check tacacsserverhost instance absence on agent :: #{result}") - end - # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get negative test resource manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. @@ -158,20 +135,6 @@ def cleanup logger.info("Check cisco_tacacs_server_host absence on agent :: #{result}") end - # @step [Step] Checks tacacsserverhost instance on agent using show cli cmds. - step 'TestStep :: Check tacacsserverhost instance absence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/port #{TacacsServerHostLib::PORT_NEGATIVE}/], - true, self, logger) - end - - logger.info("Check tacacsserverhost instance absence on agent :: #{result}") - end - # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get negative test resource manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. @@ -198,20 +161,6 @@ def cleanup logger.info("Check cisco_tacacs_server_host absence on agent :: #{result}") end - # @step [Step] Checks tacacsserverhost instance on agent using show cli cmds. - step 'TestStep :: Check tacacsserverhost instance absence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/samplehost1 key #{TacacsServerHostLib::ENCRYPTYPE_NEGATIVE}/], - true, self, logger) - end - - logger.info("Check tacacsserverhost instance absence on agent :: #{result}") - end - # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get negative test resource manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. @@ -238,20 +187,6 @@ def cleanup logger.info("Check cisco_tacacs_server_host absence on agent :: #{result}") end - # @step [Step] Checks tacacsserverhost instance on agent using show cli cmds. - step 'TestStep :: Check tacacsserverhost instance absence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/samplehost1 key 7 #{TacacsServerHostLib::ENCRYPPASSWD_NEGATIVE}/], - true, self, logger) - end - - logger.info("Check tacacsserverhost instance absence on agent :: #{result}") - end - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. raise_passfail_exception(result, testheader, self, logger) end diff --git a/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_nondefaults.rb b/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_nondefaults.rb index 9fa379450..56c03e3ae 100644 --- a/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_nondefaults.rb +++ b/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_nondefaults.rb @@ -82,15 +82,6 @@ def cleanup cmd_str = PUPPET_BINPATH + 'agent -t' on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - # Expected exit_code is 16 since this is a vegas shell cmd with exec error. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str, acceptable_exit_codes: [16]) do - search_pattern_in_output(stdout, - [/feature tacacs\+/], - true, self, logger) - end - logger.info("Setup switch for provider test :: #{result}") end @@ -123,20 +114,6 @@ def cleanup logger.info("Check cisco_tacacs_server_host presence on agent :: #{result}") end - # @step [Step] Checks tacacsserverhost instance on agent using show cli cmds. - step 'TestStep :: Check tacacsserverhost instance presence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/tacacs\-server host samplehost1 key 7 "test123" port 90 timeout 39/], - false, self, logger) - end - - logger.info("Check tacacsserverhost instance presence on agent :: #{result}") - end - # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource absent manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. @@ -166,20 +143,6 @@ def cleanup logger.info("Check cisco_tacacs_server_host absence on agent :: #{result}") end - # @step [Step] Checks tacacsserverhost instance on agent using show cli cmds. - step 'TestStep :: Check tacacsserverhost instance absence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config tacacs') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/tacacs\-server host samplehost1 key 7 "test123" port 90 timeout 39/], - true, self, logger) - end - - logger.info("Check tacacsserverhost instance absence on agent :: #{result}") - end - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. raise_passfail_exception(result, testheader, self, logger) end From 511f2eba49bf3c500c3181349ae3d8c77230bc87 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Fri, 5 Aug 2016 16:24:51 -0400 Subject: [PATCH 036/203] L3 PIM-on-VRF-intf beaker fix (#369) * ip pim sparse-mode was failing to nvgen during the test * Factors: a) the intf is in vrf 'test1'; and, b) 'test1' has not been instantiated yet * Normally these forward-references are not a problem but the new DME-based PIM cli is silently rejecting it because the vrf is not instantiated * For puppet it's not unreasonable to require a pre-instantiated vrf, and since this change only affects our beaker test I'm just going to update the test. The PIM team did say they would reconsider raising an error for this, or better yet just create the pim cli object regardless of vrf-instantiation state. * Also fixed a cleanup issue for dot1q sub-ints. * Tested on N9-I5 --- .../cisco_interface/test_interface_L3.rb | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/tests/beaker_tests/cisco_interface/test_interface_L3.rb b/tests/beaker_tests/cisco_interface/test_interface_L3.rb index 76fb68556..8323a4421 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_L3.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_L3.rb @@ -38,6 +38,7 @@ # Find a usable interface for this test intf = find_interface(tests) +dot1q = intf + '.1' # Test hash test cases tests[:default] = { @@ -69,6 +70,14 @@ }, } +# Note: This test should follow the default test as it requires an +# L3 parent interface and this makes it easy to set up. +tests[:dot1q] = { + desc: '1.2 dot1q Sub-interface', + title_pattern: dot1q, + manifest_props: { encapsulation_dot1q: 30 }, +} + tests[:non_default] = { desc: '2.1 Non Default Properties', title_pattern: intf, @@ -110,14 +119,6 @@ }, } -# Note: This test should follow the default test as it requires an -# L3 parent interface and this makes it easy to set up. -tests[:dot1q] = { - desc: '2.3 dot1q Sub-interface', - title_pattern: "#{intf}.1", - manifest_props: { encapsulation_dot1q: 30 }, -} - # This test should be run last since it will break ip addressing properties. # Note that any tests that follow need to preclean. tests[:ip_forwarding] = { @@ -146,6 +147,17 @@ def unsupported_properties(_tests, id) unprops end +# Overridden to properly handle dependencies for this test file. +def dependency_manifest(_tests, id) + return unless id == :non_default + # Though not required on most platforms, the test vrf context should be + # instantiated prior to configuring settings on a vrf interface. The new + # DME-based cli's (PIM, etc) may fail otherwise. + dep = %( cisco_vrf { 'test1': description => 'Puppet test vrf' } ) + logger.info("\n * dependency_manifest\n#{dep}") + dep +end + ################################################################# # TEST CASE EXECUTION ################################################################# @@ -153,6 +165,8 @@ def unsupported_properties(_tests, id) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") test_harness_run(tests, :default) + + interface_cleanup(agent, dot1q) test_harness_run(tests, :dot1q) # ------------------------------------------------------------------- From 959c940440ab8090f1f22ce991e13746c023e05a Mon Sep 17 00:00:00 2001 From: saichint Date: Wed, 10 Aug 2016 05:09:15 -0700 Subject: [PATCH 037/203] Add Ospf properties for interfaces (#371) * Fix interface_ospf provider and beaker * doc * doc * review comments * add mtu, pri, shut and tx delay to int ospf * documentation --- CHANGELOG.md | 4 +++ README.md | 12 ++++++++ examples/cisco/demo_ospf.pp | 4 +++ .../provider/cisco_interface_ospf/cisco.rb | 4 +++ lib/puppet/type/cisco_interface_ospf.rb | 30 +++++++++++++++++++ .../test_interface_ospf.rb | 12 ++++++++ 6 files changed, 66 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 644ea8deb..4b4129687 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `bfd_echo` - Extend cisco_interface_ospf with attributes: - `bfd` + - `mtu_ignore` - `network_type` + - `priority` + - `shutdown` + - `transmit_delay` - Extend cisco_interface_portchannel with attributes: - `bfd_per_link` - Extend cisco_ospf_vrf with attributes: diff --git a/README.md b/README.md index e676514e6..d7b73f095 100644 --- a/README.md +++ b/README.md @@ -2227,9 +2227,21 @@ Valid values are 'cleartext', '3des' or 'cisco_type_7' encryption, and ##### `message_digest_password` Specifies the message_digest password. Valid value is a string or the keyword 'default'. +###### `mtu_ignore` +Disables OSPF MTU mismatch detection. Valid values are 'true', 'false', or 'default'. + ##### `network_type` Specifies the network type of this interface. Valid values are 'broadcast', 'p2p' or the keyword 'default'. 'broadcast' type is not applicable on loopback interfaces. +##### `priority` +The router priority associated with this cisco_interface_ospf instance. Valid values are an integer or the keyword 'default'. + +###### `shutdown` +Shuts down ospf on this interface. Valid values are 'true', 'false', or 'default'. + +##### `transmit_delay` +Packet transmission delay in seconds. Valid values are an integer or the keyword 'default'. + ##### `area` *Required*. Ospf area associated with this cisco_interface_ospf instance. Valid values are a string, formatted as an IP address (i.e. "0.0.0.0") or as an integer. diff --git a/examples/cisco/demo_ospf.pp b/examples/cisco/demo_ospf.pp index 82aa18257..7c405998c 100644 --- a/examples/cisco/demo_ospf.pp +++ b/examples/cisco/demo_ospf.pp @@ -37,8 +37,12 @@ message_digest_algorithm_type => md5, message_digest_encryption_type => cisco_type_7, message_digest_password => $md_password, + mtu_ignore => true, network_type => 'p2p', passive_interface => true, + priority => 100, + shutdown => true, + transmit_delay => 300, } cisco_ospf_vrf { 'dark_blue default': diff --git a/lib/puppet/provider/cisco_interface_ospf/cisco.rb b/lib/puppet/provider/cisco_interface_ospf/cisco.rb index 5f3178b02..613404f41 100644 --- a/lib/puppet/provider/cisco_interface_ospf/cisco.rb +++ b/lib/puppet/provider/cisco_interface_ospf/cisco.rb @@ -41,12 +41,16 @@ :message_digest_encryption_type, :message_digest_password, :network_type, + :priority, + :transmit_delay, ] INTF_OSPF_BOOL_PROPS = [ :passive_interface, :bfd, :message_digest, + :mtu_ignore, + :shutdown, ] INTF_OSPF_ALL_PROPS = INTF_OSPF_NON_BOOL_PROPS + INTF_OSPF_BOOL_PROPS diff --git a/lib/puppet/type/cisco_interface_ospf.rb b/lib/puppet/type/cisco_interface_ospf.rb index fe5dff162..f709b9d55 100644 --- a/lib/puppet/type/cisco_interface_ospf.rb +++ b/lib/puppet/type/cisco_interface_ospf.rb @@ -41,7 +41,11 @@ message_digest_algorithm_type => md5, message_digest_encryption_type => \"clear\", message_digest_password => \"xxxxx\", + mtu_ignore => true, network_type => 'p2p', + priority => 100, + shutdown => true, + transmit_delay => 300, }" ensurable @@ -180,12 +184,38 @@ def name munge { |value| value == 'default' ? :default : value } end + newproperty(:mtu_ignore) do + desc 'Disables OSPF MTU mismatch detection.' + + newvalues(:true, :false, :default) + end + newproperty(:network_type) do desc 'Network type of this interface.' newvalues(:broadcast, :p2p, :default) end + newproperty(:priority) do + desc "The router priority associated with this cisco_interface_ospf + instance. Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end + + newproperty(:shutdown) do + desc 'Shuts down ospf on this interface.' + + newvalues(:true, :false, :default) + end + + newproperty(:transmit_delay) do + desc "Packet transmission delay in seconds. Valid values are + integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end + newproperty(:area) do desc "Ospf area associated with this cisco_interface_ospf instance. Valid values are string, formatted as an IP address diff --git a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb index a2ddc31d4..46e9ebaf7 100644 --- a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb +++ b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb @@ -56,8 +56,12 @@ message_digest_algorithm_type: 'default', message_digest_encryption_type: 'default', message_digest_password: 'default', + mtu_ignore: 'default', network_type: 'default', passive_interface: 'default', + priority: 'default', + shutdown: 'default', + transmit_delay: 'default', }, code: [0, 2], resource: { @@ -69,7 +73,11 @@ message_digest_key_id: 0, message_digest_algorithm_type: 'md5', message_digest_encryption_type: 'cleartext', + mtu_ignore: 'false', passive_interface: 'false', + priority: 1, + shutdown: 'false', + transmit_delay: 1, }, } @@ -90,8 +98,12 @@ message_digest_algorithm_type: 'md5', message_digest_encryption_type: 'cisco_type_7', message_digest_password: '046E1803362E595C260E0B240619050A2D', + mtu_ignore: 'true', network_type: 'p2p', passive_interface: 'true', + priority: '200', + shutdown: 'true', + transmit_delay: '400', }, resource: { area: '0.0.0.200' From 7f94510901b7f3351ab5259207051c794afd0518 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Wed, 10 Aug 2016 11:38:33 -0400 Subject: [PATCH 038/203] Fix beaker test bug (#372) --- .../network_snmp/network_snmp_provider_defaults.rb | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tests/beaker_tests/network_snmp/network_snmp_provider_defaults.rb b/tests/beaker_tests/network_snmp/network_snmp_provider_defaults.rb index 44e5953ff..74bda4c60 100644 --- a/tests/beaker_tests/network_snmp/network_snmp_provider_defaults.rb +++ b/tests/beaker_tests/network_snmp/network_snmp_provider_defaults.rb @@ -116,15 +116,10 @@ step 'TestStep :: Check network_snmp resource presence on agent' do # Expected exit_code is 0 since this is a puppet resource cmd. # Flag is set to false to check for presence of RegExp pattern in stdout. + enable_pat = operating_system == 'ios_xr' ? { 'enable' => 'true' } : { 'enable' => 'false' } cmd_str = PUPPET_BINPATH + 'resource network_snmp default' on(agent, cmd_str) do - if operating_system == 'ios_xr' - search_pattern_in_output(stdout, { 'enable' => 'true' }, - false, self, logger) - else - search_pattern_in_output(stdout, { 'enable' => 'false' }, - false, self, logger) - end + search_pattern_in_output(stdout, enable_pat, false, self, logger) search_pattern_in_output(stdout, { 'contact' => 'unset' }, false, self, logger) search_pattern_in_output(stdout, { 'location' => 'unset' }, From eff9e26e26b081f576025f02087f25aa1ca3b8ef Mon Sep 17 00:00:00 2001 From: saichint Date: Fri, 12 Aug 2016 04:37:38 -0700 Subject: [PATCH 039/203] bug fix for interface_portchannel beaker test (#373) * Fix interface_ospf provider and beaker * doc * doc * review comments * add mtu, pri, shut and tx delay to int ospf * documentation * bug fix --- .../cisco_interface_portchannel/test_interface_portchannel.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb b/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb index 050ca9b4a..aff98885f 100755 --- a/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb +++ b/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb @@ -296,6 +296,7 @@ def test_harness_interface_portchannel(tests, id) id = 'default_properties_sym' end + system_default_switchport(agent, false) tests[id][:desc] = '1.1 Default Properties' test_harness_interface_portchannel(tests, id) From 98308bc35267d62e299fd841f0e0135be4c3f638 Mon Sep 17 00:00:00 2001 From: mikewiebe Date: Fri, 12 Aug 2016 15:49:13 -0400 Subject: [PATCH 040/203] Removed, no longer needed --- .../snmpserver_provider_negatives.rb | 289 ------------------ 1 file changed, 289 deletions(-) delete mode 100644 tests/beaker_tests/cisco_snmp_server/snmpserver_provider_negatives.rb diff --git a/tests/beaker_tests/cisco_snmp_server/snmpserver_provider_negatives.rb b/tests/beaker_tests/cisco_snmp_server/snmpserver_provider_negatives.rb deleted file mode 100644 index de2c41b94..000000000 --- a/tests/beaker_tests/cisco_snmp_server/snmpserver_provider_negatives.rb +++ /dev/null @@ -1,289 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# SnmpServer-Provider-Negatives.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet SNMPSERVER resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a SNMPSERVER resource test that tests for negative values for -# aaa_user_cache_timeout, global_enforce_priv, packet_size, protocol, -# tcp_session_auth, contact and location attributes of a -# cisco_snmp_server resource. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# The next set of teststeps deal with attribute negative tests and their -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and SnmpServerLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../snmpserverlib.rb', __FILE__) - -result = 'PASS' -testheader = 'SNMPSERVER Resource :: All Attributes Negatives' - -# @test_name [TestCase] Executes negatives testcase for SNMPSERVER Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpServerLib.create_snmpserver_manifest_defaults) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - # Or expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config snmp all') - on(agent, cmd_str) do - SnmpServerLib.match_default_cli(stdout, self, logger) - end - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpServerLib.create_snmpserver_manifest_packetsize_negative) - - # Expected exit_code is 4 since this is a puppet agent cmd with failure. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [4]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_snmp_server resource on agent using resource cmd. - step 'TestStep :: Check cisco_snmp_server resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + 'resource cisco_snmp_server' - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'packet_size' => SnmpServerLib::PACKETSIZE_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_snmp_server resource absence on agent :: #{result}") - end - - # @step [Step] Checks snmpserver instance on agent using switch show cli cmds. - step 'TestStep :: Check snmpserver instance absence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config snmp') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/snmp-server packetsize #{SnmpServerLib::PACKETSIZE_NEGATIVE}/], - true, self, logger) - end - - logger.info("Check snmpserver instance absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpServerLib.create_snmpserver_manifest_aaatimeout_negative) - - # Expected exit_code is 4 since this is a puppet agent cmd with failure. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [4]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_snmp_server resource on agent using resource cmd. - step 'TestStep :: Check cisco_snmp_server resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + 'resource cisco_snmp_server' - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'aaa_user_cache_timeout' => SnmpServerLib::AAATIMEOUT_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_snmp_server resource absence on agent :: #{result}") - end - - # @step [Step] Checks snmpserver instance on agent using switch show cli cmds. - step 'TestStep :: Check snmpserver instance absence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config snmp') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/snmp-server aaa-user cache-timeout #{SnmpServerLib::AAATIMEOUT_NEGATIVE}/], - true, self, logger) - end - - logger.info("Check snmpserver instance absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpServerLib.create_snmpserver_manifest_tcpauth_negative) - - # Expected exit_code is 1 since this is a puppet agent cmd with error. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [1]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_snmp_server resource on agent using resource cmd. - step 'TestStep :: Check cisco_snmp_server resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + 'resource cisco_snmp_server' - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'tcp_session_auth' => SnmpServerLib::TCPAUTH_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_snmp_server resource absence on agent :: #{result}") - end - - # @step [Step] Checks snmpserver instance on agent using switch show cli cmds. - step 'TestStep :: Check snmpserver instance absence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config snmp') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/snmp-server tcp_session_auth #{SnmpServerLib::TCPAUTH_NEGATIVE}/], - true, self, logger) - end - - logger.info("Check snmpserver instance absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpServerLib.create_snmpserver_manifest_protocol_negative) - - # Expected exit_code is 1 since this is a puppet agent cmd with error. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [1]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_snmp_server resource on agent using resource cmd. - step 'TestStep :: Check cisco_snmp_server resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + 'resource cisco_snmp_server' - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'protocol' => SnmpServerLib::PROTOCOL_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_snmp_server resource absence on agent :: #{result}") - end - - # @step [Step] Checks snmpserver instance on agent using switch show cli cmds. - step 'TestStep :: Check snmpserver instance absence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config snmp') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/snmp-server protocol #{SnmpServerLib::PROTOCOL_NEGATIVE}/], - true, self, logger) - end - - logger.info("Check snmpserver instance absence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get negative test resource manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpServerLib.create_snmpserver_manifest_globalpriv_negative) - - # Expected exit_code is 1 since this is a puppet agent cmd with error. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [1]) - - logger.info("Get negative test resource manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_snmp_server resource on agent using resource cmd. - step 'TestStep :: Check cisco_snmp_server resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + 'resource cisco_snmp_server' - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'global_enforce_priv' => SnmpServerLib::GLOBALPRIV_NEGATIVE }, - true, self, logger) - end - - logger.info("Check cisco_snmp_server resource absence on agent :: #{result}") - end - - # @step [Step] Checks snmpserver instance on agent using switch show cli cmds. - step 'TestStep :: Check snmpserver instance absence on agent' do - # Expected exit_code is 0 since this is a vegas shell cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = get_vshell_cmd('show running-config snmp') - on(agent, cmd_str) do - search_pattern_in_output(stdout, - [/snmp-server global_enforce_priv #{SnmpServerLib::GLOBALPRIV_NEGATIVE}/], - true, self, logger) - end - - logger.info("Check snmpserver instance absence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") From f48e4d311c725713a1bba653a54af9ed694bfd8a Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Fri, 12 Aug 2016 16:43:02 -0400 Subject: [PATCH 041/203] Remove broken snmp_user XR support (#374) --- examples/netdev/demo_snmp.pp | 21 ++------ lib/puppet/provider/snmp_user/cisco.rb | 52 ++++--------------- .../snmp_user/snmp_user_provider_defaults.rb | 47 ++++------------- tests/beaker_tests/snmp_user/snmp_userlib.rb | 42 +++------------ 4 files changed, 31 insertions(+), 131 deletions(-) diff --git a/examples/netdev/demo_snmp.pp b/examples/netdev/demo_snmp.pp index d33badbe6..d8e6ed82a 100644 --- a/examples/netdev/demo_snmp.pp +++ b/examples/netdev/demo_snmp.pp @@ -29,29 +29,14 @@ acl => 'testcomacl', } - $password = $operatingsystem ? { - 'nexus' => '0x7e5030ffd26d7e1b366a9041e9c63c94', - default => '0307530A080824414B' - } - - $private_key = $operatingsystem ? { - 'nexus' => '0xcc012f26b3384d4b3da979bff48b4ffe', - default => '12491D42475E5A' - } - - $localized_key = $operatingsystem ? { - 'ios_xr' => undef, - default => true - } - snmp_user { 'test_snmp_user': ensure => present, roles => ['network-operator'], auth => 'md5', - password => $password, + password => '0x7e5030ffd26d7e1b366a9041e9c63c94', privacy => 'aes128', - private_key => $private_key, - localized_key => $localized_key, + private_key => '0xcc012f26b3384d4b3da979bff48b4ffe', + localized_key => true, } snmp_notification { 'vtp vlandelete': diff --git a/lib/puppet/provider/snmp_user/cisco.rb b/lib/puppet/provider/snmp_user/cisco.rb index 3530d0479..e07e80415 100644 --- a/lib/puppet/provider/snmp_user/cisco.rb +++ b/lib/puppet/provider/snmp_user/cisco.rb @@ -20,7 +20,7 @@ desc 'The Cisco provider for snmp_user.' confine feature: :cisco_node_utils - defaultfor operatingsystem: [:nexus, :ios_xr] + defaultfor operatingsystem: :nexus mk_resource_methods @@ -46,19 +46,13 @@ def self.properties_get(snmpuser_name, v) current_state = { ensure: :present, name: snmpuser_name, + engine_id: v.engine_id, + roles: v.groups, auth: v.auth_protocol, password: v.auth_password, privacy: v.priv_protocol, private_key: v.priv_password, - roles: v.groups, } - - if Facter.value('operatingsystem').eql?('ios_xr') - current_state[:version] = v.version - else - current_state[:engine_id] = v.engine_id - end - new(current_state) end # self.properties_get @@ -92,7 +86,7 @@ def destroy @property_flush[:ensure] = :absent end - def validate # rubocop:disable Metrics/CyclomaticComplexity + def validate unless @resource[:auth] invalid = [] REQUIRES_AUTH_PROPS.each do |prop| @@ -111,46 +105,22 @@ def validate # rubocop:disable Metrics/CyclomaticComplexity "The 'private_key' property must be set when specifying 'privacy'" \ if @resource[:private_key].nil? && @resource[:privacy] + fail ArgumentError, + "The 'engine_id' and 'roles' properties are mutually exclusive" \ + if @resource[:engine_id] && @resource[:roles] + fail ArgumentError, "The 'enforce_privacy' property is not supported by this provider" \ if @resource[:enforce_privacy] - - if Facter.value('operatingsystem').eql?('ios_xr') - fail ArgumentError, - "The 'engine_id' property is not supported on this platform" \ - if @resource[:engine_id] - - invalid = [] - [:roles, :version].each do |prop| - invalid << prop unless @resource[prop] - end - fail ArgumentError, - "You must specify the following properties on this platform: #{invalid}" \ - unless invalid.empty? - - fail ArgumentError, - 'This paltform only supports a single role per user' \ - if @resource[:roles].length > 1 - - if @resource[:localized_key] && @resource[:localized_key] == :false - fail ArgumentError, - 'This provider only supports providing encrypted passwords on this platform.' - end - else - fail ArgumentError, - "The 'engine_id' and 'roles' properties are mutually exclusive" \ - if @resource[:engine_id] && @resource[:roles] - end end def flush + validate @snmpuser.destroy if @snmpuser @snmpuser = nil return if @property_flush[:ensure] == :absent - validate - if @resource[:localized_key].eql?(:true) localized_key = true else @@ -167,8 +137,6 @@ def flush @resource[:privacy] || @property_hash[:privacy] || :none, @resource[:private_key] || @property_hash[:private_key] || '', localized_key, - @resource[:engine_id] || '', - true, - @resource[:version] || nil) + @resource[:engine_id] || '') end end # Puppet::Type diff --git a/tests/beaker_tests/snmp_user/snmp_user_provider_defaults.rb b/tests/beaker_tests/snmp_user/snmp_user_provider_defaults.rb index f70f91e97..b0e1e17ad 100644 --- a/tests/beaker_tests/snmp_user/snmp_user_provider_defaults.rb +++ b/tests/beaker_tests/snmp_user/snmp_user_provider_defaults.rb @@ -68,7 +68,7 @@ # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource present manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpUserLib.create_snmp_user_manifest_present(operating_system)) + on(master, SnmpUserLib.create_snmp_user_manifest_present) # Expected exit_code is 2 since this is a puppet agent cmd with change. cmd_str = PUPPET_BINPATH + 'agent -t' @@ -89,25 +89,12 @@ false, self, logger) search_pattern_in_output(stdout, { 'auth' => 'md5' }, false, self, logger) - - if operating_system == 'ios_xr' - search_pattern_in_output(stdout, { 'password' => '0307530A080824414B' }, - false, self, logger) - else - search_pattern_in_output(stdout, { 'password' => '0x7e5030ffd26d7e1b366a9041e9c63c94' }, - false, self, logger) - end - + search_pattern_in_output(stdout, { 'password' => '0x7e5030ffd26d7e1b366a9041e9c63c94' }, + false, self, logger) search_pattern_in_output(stdout, { 'privacy' => 'aes128' }, false, self, logger) - - if operating_system == 'ios_xr' - search_pattern_in_output(stdout, { 'private_key' => '12491D42475E59' }, - false, self, logger) - else - search_pattern_in_output(stdout, { 'private_key' => '0xcc012f26b3384d4b3da979bff48b4ffe' }, - false, self, logger) - end + search_pattern_in_output(stdout, { 'private_key' => '0xcc012f26b3384d4b3da979bff48b4ffe' }, + false, self, logger) end logger.info("Check snmp_user resource presence on agent :: #{result}") @@ -116,7 +103,7 @@ # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource present (with changes)manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpUserLib.create_snmp_user_manifest_present_change(operating_system)) + on(master, SnmpUserLib.create_snmp_user_manifest_present_change) # Expected exit_code is 2 since this is a puppet agent cmd with change. cmd_str = PUPPET_BINPATH + 'agent -t' @@ -134,27 +121,15 @@ search_pattern_in_output(stdout, { 'ensure' => 'present' }, false, self, logger) search_pattern_in_output(stdout, { 'engine_id' => '128:0:0:9:3:8:0:39:34:152:217' }, - false, self, logger) unless operating_system == 'ios_xr' + false, self, logger) search_pattern_in_output(stdout, { 'auth' => 'sha' }, false, self, logger) - - if operating_system == 'ios_xr' - search_pattern_in_output(stdout, { 'password' => '0307530A080824414B' }, - false, self, logger) - else - search_pattern_in_output(stdout, { 'password' => '0x7e5030ffd26d7e1b366a9041e9c63c94' }, - false, self, logger) - end - + search_pattern_in_output(stdout, { 'password' => '0x7e5030ffd26d7e1b366a9041e9c63c94' }, + false, self, logger) search_pattern_in_output(stdout, { 'privacy' => 'des' }, false, self, logger) - if operating_system == 'ios_xr' - search_pattern_in_output(stdout, { 'private_key' => '12491D42475E59' }, - false, self, logger) - else - search_pattern_in_output(stdout, { 'private_key' => '0xcc012f26b3384d4b3da979bff48b4ffe' }, - false, self, logger) - end + search_pattern_in_output(stdout, { 'private_key' => '0xcc012f26b3384d4b3da979bff48b4ffe' }, + false, self, logger) end logger.info("Check snmp_user resource presence on agent :: #{result}") diff --git a/tests/beaker_tests/snmp_user/snmp_userlib.rb b/tests/beaker_tests/snmp_user/snmp_userlib.rb index 5ec3aaeb9..9b96a4bd7 100644 --- a/tests/beaker_tests/snmp_user/snmp_userlib.rb +++ b/tests/beaker_tests/snmp_user/snmp_userlib.rb @@ -44,8 +44,8 @@ module SnmpUserLib # where 'ensure' is set to present. # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_snmp_user_manifest_present(type) - nxos_str = "cat <#{PUPPETMASTER_MANIFESTPATH} + def self.create_snmp_user_manifest_present + manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { snmp_user { 'test_snmp_user': ensure => present, @@ -58,34 +58,20 @@ def self.create_snmp_user_manifest_present(type) } } EOF" - - ios_xr_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - snmp_user { 'test_snmp_user': - ensure => present, - roles => ['network-operator'], - version => 'v3', - auth => 'md5', - password => '0307530A080824414B', - privacy => 'aes128', - private_key => '12491D42475E59', - } -} -EOF" - type == 'ios_xr' ? ios_xr_str : nxos_str + manifest_str end # Method to create a manifest for snmp_user resource attribute 'ensure' # where 'ensure' is set to present, and a few changes made from above. # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_snmp_user_manifest_present_change(type) - nxos_str = "cat <#{PUPPETMASTER_MANIFESTPATH} + def self.create_snmp_user_manifest_present_change + manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { snmp_user { 'test_snmp_user': ensure => present, auth => 'sha', - password => '0307530A080824414B', + password => '0x7e5030ffd26d7e1b366a9041e9c63c94', privacy => 'des', private_key => '0xcc012f26b3384d4b3da979bff48b4ffe', localized_key => true, @@ -93,21 +79,7 @@ def self.create_snmp_user_manifest_present_change(type) } } EOF" - - ios_xr_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - snmp_user { 'test_snmp_user': - ensure => present, - roles => ['network-operator'], - version => 'v3', - auth => 'sha', - password => '0307530A080824414B', - privacy => 'des', - private_key => '12491D42475E59', - } -} -EOF" - type == 'ios_xr' ? ios_xr_str : nxos_str + manifest_str end # Method to create a manifest for snmp_user resource attribute 'ensure' From 85fc9dba953b92095b9d9ddeeb20a511297be229 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 15 Aug 2016 09:38:12 -0400 Subject: [PATCH 042/203] Add agent_probe to utility.lib (#375) * `feature vn-segment-vlan-based` is not supported on some n3k's * Added a simple helper method to test for h/w support; the result is then used by `unsupported_properties` to update the test cases * Tested on n3k(3048), n7k, n9k --- tests/beaker_tests/cisco_vlan/test_vlan.rb | 12 ++++++++++-- tests/beaker_tests/lib/utilitylib.rb | 8 ++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/beaker_tests/cisco_vlan/test_vlan.rb b/tests/beaker_tests/cisco_vlan/test_vlan.rb index 2d1ac03f5..e083743e6 100644 --- a/tests/beaker_tests/cisco_vlan/test_vlan.rb +++ b/tests/beaker_tests/cisco_vlan/test_vlan.rb @@ -102,13 +102,21 @@ # State cannot be modified for extended vlans on N5k and N6k platforms. tests[:non_default_extended][:manifest_props].delete(:state) if platform[/n(5|6)k/] -def unsupported_properties(_tests, _id) +if platform[/n3k/] + tests[:vn_segment_unsupported] = + resource_probe(agent, + 'cisco_vlan 128 mapped_vni=128000', + 'Hardware is not capable of supporting vn-segment-vlan-based feature') +end + +def unsupported_properties(tests, _id) unprops = [] - unprops << :mapped_vni if platform[/n7k/] + unprops << :mapped_vni if platform[/n7k/] || tests[:vn_segment_unsupported] unprops << :fabric_control unless platform[/n7k/] + logger.info(" unprops: #{unprops}") unless unprops.empty? unprops end diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 0b97cceb6..d0c426a7b 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1293,6 +1293,14 @@ def remove_interface(agent, intf) command_config(agent, cmd, cmd) end +# Issue a command on the agent and check stdout for a pattern. +# Useful for checking if hardware supports properties, etc. +def resource_probe(agent, cmd, pattern) + cmd = PUPPET_BINPATH + "resource #{cmd}" + on(agent, cmd, acceptable_exit_codes: [0, 2, 1], pty: true) + stdout.match(pattern) ? true : false +end + def remove_all_vlans(agent, stepinfo='Remove all vlans & bridge-domains') # TBD: Modify this cleanup to use faster test_get / test_set: # test_get('i ^vlan|^bridge') From 670392c367d400540c1537952c729207312d742f Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 15 Aug 2016 10:55:56 -0400 Subject: [PATCH 043/203] Add f3 utility for n7 bkr tests (#376) * Tested on n7k --- .../test_interface_vlan_mapping.rb | 3 ++ tests/beaker_tests/cisco_vlan/test_vlan.rb | 16 ++-------- tests/beaker_tests/lib/utilitylib.rb | 29 ++++++++++++++++++- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/tests/beaker_tests/cisco_interface/test_interface_vlan_mapping.rb b/tests/beaker_tests/cisco_interface/test_interface_vlan_mapping.rb index 449adf569..5af626be4 100644 --- a/tests/beaker_tests/cisco_interface/test_interface_vlan_mapping.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_vlan_mapping.rb @@ -58,6 +58,9 @@ # Assign a test interface. if platform[/n7k/] + unless mt_full_interface + prereq_skip(nil, self, 'Test requires F3 or compatible line module') + end setup_mt_full_env(tests, self) # Use test interface discovered by setup_mt_full_env(). intf = tests[:intf] diff --git a/tests/beaker_tests/cisco_vlan/test_vlan.rb b/tests/beaker_tests/cisco_vlan/test_vlan.rb index e083743e6..5e3139537 100644 --- a/tests/beaker_tests/cisco_vlan/test_vlan.rb +++ b/tests/beaker_tests/cisco_vlan/test_vlan.rb @@ -120,24 +120,11 @@ def unsupported_properties(tests, _id) unprops end -# Overridden to properly handle dependencies for this test file. -def dependency_manifest(_tests, _id) - dep = '' - if platform[/n7k/] - dep = %( - cisco_vdc { '#{default_vdc_name}': - # Must be f3-only - limit_resource_module_type => 'f3', - }) - end - logger.info("\n * dependency_manifest\n#{dep}") - dep -end - ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + vdc_limit_f3_no_intf_needed(:set) remove_all_vlans(agent) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Property Testing") @@ -150,6 +137,7 @@ def dependency_manifest(_tests, _id) test_harness_run(tests, :non_default_extended) remove_all_vlans(agent) + vdc_limit_f3_no_intf_needed(:clear) end logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index d0c426a7b..0ea38c816 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -887,7 +887,7 @@ def skip_if_nv_overlay_rejected(agent) def mt_full_interface # Search for F3 card on device, create an interface name if found cmd = get_vshell_cmd('sh mod') - out = on(agent, cmd, pty: true).stdout[/^(\d+)\s.*N7K-F3/] + out = on(agent, cmd, pty: true).stdout[/^(\d+)\s.*N7[K7]-F3/] slot = out.nil? ? nil : Regexp.last_match[1] "ethernet#{slot}/1" unless slot.nil? end @@ -1301,6 +1301,33 @@ def resource_probe(agent, cmd, pattern) stdout.match(pattern) ? true : false end +def vdc_limit_f3_no_intf_needed(action=:set) + # This is a special-use method for N7Ks that don't have a physical F3. + # 1) There are some features that refuse to load unless the VDC is + # limited to F3 only, but they will actually load if the config is + # present, despite the fact that there are no physical F3s. + # 2) We have some tests that need these features but don't need interfaces. + # + # action = :set (enable limit F3 config), :clear (default limit config) + # + # The limit config should be removed after testing if the device does not + # have an actual F3. + return unless platform[/n7k/] + case action + when :set + # limit_resource_module_type => 'f3', + cmd = PUPPET_BINPATH + "resource cisco_vdc '#{default_vdc_name}' " + out = on(agent, cmd, pty: true).stdout[/limit_resource.*'(f3)'/] + mods = out.nil? ? nil : Regexp.last_match[1] + return if mods == 'f3' + cmd += "limit_resource_module_type='f3'" + on(agent, cmd, pty: true).stdout[/limit_resource.*'(f3)'/] + + when :clear + teardown_vdc + end +end + def remove_all_vlans(agent, stepinfo='Remove all vlans & bridge-domains') # TBD: Modify this cleanup to use faster test_get / test_set: # test_get('i ^vlan|^bridge') From 917640f8c99a52cc96fa87bb63152b8b438773fc Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 15 Aug 2016 16:07:30 -0400 Subject: [PATCH 044/203] Fix half/full in test_duplex (#377) * For some 3k, need to munge 'half/full' into csv-style 'half,full' to play nice with interface_capabilities * This fix is a little trickier than the NU fix because the output from puppet resource is 'raw' whereas the NU output is a pre-processed hash of capabilities; thus we have to be careful not to munge the vsh or puppet resource output. * Tested on n3048 --- .../test_interface_capabilities.rb | 17 ++++++++++------- tests/beaker_tests/lib/utilitylib.rb | 1 + 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/beaker_tests/cisco_interface/test_interface_capabilities.rb b/tests/beaker_tests/cisco_interface/test_interface_capabilities.rb index 69a7b04a1..89a48a031 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_capabilities.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_capabilities.rb @@ -122,8 +122,8 @@ def parse_capabilities(agent, cmd) on(agent, cmd) caps = {} caps['Speed'] = Regexp.last_match[1] if stdout[/Speed:\s+([\w,]+)/] - caps['Duplex'] = Regexp.last_match[1] if stdout[/Duplex:\s+([\w,-]+)/] - logger.debug("\ncapabilities hash: #{caps}") + caps['Duplex'] = Regexp.last_match[1] if stdout[%r{Duplex:\s+([\w/,-]+)}] + logger.info("\ncapabilities hash: #{caps}") caps end @@ -131,6 +131,7 @@ def parse_capabilities(agent, cmd) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown { interface_cleanup(agent, intf) } # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Platform/Linecard Variable Properties") @@ -148,25 +149,27 @@ def parse_capabilities(agent, cmd) logger.info("\n#{'-' * 60}\nSection 2. Test puppet resource vs vsh results") vsh_cmd = get_vshell_cmd("show interface #{intf} capabilities") + # Note: vsh output is 'raw' command output (as opposed to the processed hash) vsh_caps = parse_capabilities(agent, vsh_cmd) resource_cmd = PUPPET_BINPATH + "resource cisco_interface_capabilities '#{intf}'" resource_caps = parse_capabilities(agent, resource_cmd) - fail_test('puppet resource mismatch with vsh :: FAIL') unless - vsh_caps == resource_caps + unless vsh_caps == resource_caps + logger.error("vsh_caps: #{vsh_caps}, resource_caps: #{resource_caps}") + fail_test('puppet resource mismatch with vsh :: FAIL') + end # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 3. Test with utilitylib helper results") util_caps = interface_capabilities(agent, intf) vsh_caps.keys.each do |k| + next if k[/Duplex/] && vsh_caps[k][%r{half/full}] # noise next if vsh_caps[k] == util_caps[k] + logger.error("vsh_caps[#{k}]=#{vsh_caps[k]}, util_caps[#{k}]=#{util_caps[k]}") fail_test('utilitylib helper results mismatch with vsh') end - - # ------------------------------------------------------------------- - interface_cleanup(agent, intf) end logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 0ea38c816..57a6098b0 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1202,6 +1202,7 @@ def interface_capabilities(agent, intf) k.gsub!(/ \(.*\)/, '') # Remove any parenthetical text from key k.strip! v.strip! + v.gsub!(%r{half/full}, 'half,full') if k[/Duplex/] hash[k] = v end hash From 2e5b10b160da547f17e608f3966fddb4f9b3afa6 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Tue, 16 Aug 2016 09:08:33 -0400 Subject: [PATCH 045/203] Update service provider beaker test to work in guestshell (#378) * Select service based on OS family * Update fail_test message --- .../file_service_package/filesvcpkglib.rb | 14 ++++++------- .../service_provider_nondefaults.rb | 20 ++++++++++++++----- tests/beaker_tests/lib/utilitylib.rb | 6 ++++++ 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/tests/beaker_tests/file_service_package/filesvcpkglib.rb b/tests/beaker_tests/file_service_package/filesvcpkglib.rb index f799661a7..03188d0eb 100644 --- a/tests/beaker_tests/file_service_package/filesvcpkglib.rb +++ b/tests/beaker_tests/file_service_package/filesvcpkglib.rb @@ -33,8 +33,6 @@ # The module has a single set of methods: # A. Methods to create manifests for file, service and pkg Puppet test cases. module FileSvcPkgLib - TEST_SERVICE = 'bootlogd' - # A. Methods to create manifests for file, service and pkg Puppet test cases. # Method to create a manifest for FILE resource attributes: @@ -78,11 +76,11 @@ def self.create_file_manifest_absent # name, ensure and enable. # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_service_manifest_nondefaults + def self.create_service_manifest_nondefaults(service) manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { - service { '#{TEST_SERVICE}': - name => '#{TEST_SERVICE}', + service { '#{service}': + name => '#{service}', ensure => 'running', enable => 'true', } @@ -95,11 +93,11 @@ def self.create_service_manifest_nondefaults # 'ensure' is set to stopped. # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_service_manifest_stopped + def self.create_service_manifest_stopped(service) manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { - service { '#{TEST_SERVICE}': - name => '#{TEST_SERVICE}', + service { '#{service}': + name => '#{service}', ensure => 'stopped', } } diff --git a/tests/beaker_tests/file_service_package/service_provider_nondefaults.rb b/tests/beaker_tests/file_service_package/service_provider_nondefaults.rb index d67806c74..77c329cc1 100644 --- a/tests/beaker_tests/file_service_package/service_provider_nondefaults.rb +++ b/tests/beaker_tests/file_service_package/service_provider_nondefaults.rb @@ -61,6 +61,16 @@ result = 'PASS' testheader = 'SERVICE Resource :: All Attributes NonDefaults' +# Identify service for testing based on os family. +case os_family +when 'cisco-wrlinux' + os_service = 'bootlogd' +when 'RedHat' + os_service = 'systemd-tmpfiles-clean.timer' +else + fail_test("Unable to identify/set service for OS family: #{os_family}") +end + # @test_name [TestCase] Executes nondefaults testcase for SERVICE Resource. test_name "TestCase :: #{testheader}" do raise_skip_exception('Not supported for OAC platforms', self) if @@ -68,7 +78,7 @@ # @step [Step] Sets up switch for provider test. step 'TestStep :: Setup switch for provider test' do # Expected exit_code is 0 since this is a bash shell cmd. - on(master, FileSvcPkgLib.create_service_manifest_stopped) + on(master, FileSvcPkgLib.create_service_manifest_stopped(os_service)) # Expected exit_code is 2 since this is a puppet agent cmd with change. # Or expected exit_code is 0 since this is a puppet agent cmd with no change. @@ -81,7 +91,7 @@ # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource nondefaults manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. - on(master, FileSvcPkgLib.create_service_manifest_nondefaults) + on(master, FileSvcPkgLib.create_service_manifest_nondefaults(os_service)) # Expected exit_code is 2 since this is a puppet agent cmd with change. cmd_str = PUPPET_BINPATH + 'agent -t' @@ -94,7 +104,7 @@ step 'TestStep :: Check service resource presence on agent' do # Expected exit_code is 0 since this is a puppet resource cmd. # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource service '#{FileSvcPkgLib::TEST_SERVICE}'" + cmd_str = PUPPET_BINPATH + "resource service '#{os_service}'" on(agent, cmd_str) do search_pattern_in_output(stdout, { 'ensure' => 'running' }, @@ -107,7 +117,7 @@ # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource stopped manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. - on(master, FileSvcPkgLib.create_service_manifest_stopped) + on(master, FileSvcPkgLib.create_service_manifest_stopped(os_service)) # Expected exit_code is 2 since this is a puppet agent cmd with change. cmd_str = PUPPET_BINPATH + 'agent -t' @@ -122,7 +132,7 @@ step 'TestStep :: Check service resource absence on agent' do # Expected exit_code is 0 since this is a puppet resource cmd. # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource service '#{FileSvcPkgLib::TEST_SERVICE}'" + cmd_str = PUPPET_BINPATH + "resource service '#{os_service}'" on(agent, cmd_str) do search_pattern_in_output(stdout, { 'ensure' => 'running' }, diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 57a6098b0..9366f2cc7 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -987,6 +987,12 @@ def operating_system @cisco_os = on(agent, facter_cmd('os.name')).stdout.chomp end +@os_family = nil +def os_family + return @os_family unless @os_family.nil? + @os_family = on(agent, facter_cmd('os.family')).stdout.chomp +end + # Used to cache the cisco hardware type @cisco_hardware = nil # Use facter to return cisco hardware type From 515d63367a63a4325daca0d746c24c62a0d297e1 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Tue, 16 Aug 2016 10:48:37 -0400 Subject: [PATCH 046/203] Add teardowns to beaker (#379) --- tests/beaker_tests/cisco_interface/test_interface_L2.rb | 9 +++++---- tests/beaker_tests/cisco_vlan/test_vlan.rb | 7 ++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/beaker_tests/cisco_interface/test_interface_L2.rb b/tests/beaker_tests/cisco_interface/test_interface_L2.rb index e1c4ba521..3f70c79cf 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_L2.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_L2.rb @@ -121,6 +121,11 @@ # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown do + interface_cleanup(agent, intf) + system_default_switchport(agent, false) + end + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. 'access' Property Testing") test_harness_run(tests, :default_access) @@ -130,10 +135,6 @@ logger.info("\n#{'-' * 60}\nSection 1. 'trunk' Property Testing") test_harness_run(tests, :default_trunk) test_harness_run(tests, :non_default_trunk) - - # ------------------------------------------------------------------- - interface_cleanup(agent, intf) - system_default_switchport(agent, false) end logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_vlan/test_vlan.rb b/tests/beaker_tests/cisco_vlan/test_vlan.rb index 5e3139537..68838a7e4 100644 --- a/tests/beaker_tests/cisco_vlan/test_vlan.rb +++ b/tests/beaker_tests/cisco_vlan/test_vlan.rb @@ -124,6 +124,10 @@ def unsupported_properties(tests, _id) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown do + remove_all_vlans(agent) + vdc_limit_f3_no_intf_needed(:clear) + end vdc_limit_f3_no_intf_needed(:set) remove_all_vlans(agent) # ------------------------------------------------------------------- @@ -135,9 +139,6 @@ def unsupported_properties(tests, _id) remove_all_vlans(agent) test_harness_run(tests, :default_extended) test_harness_run(tests, :non_default_extended) - - remove_all_vlans(agent) - vdc_limit_f3_no_intf_needed(:clear) end logger.info("TestCase :: #{tests[:resource_name]} :: End") From bf5f69b44994983ed279545f4420197322ffde79 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Tue, 16 Aug 2016 14:40:44 -0400 Subject: [PATCH 047/203] f3 handling for test_vrf (#380) * f3 handling for test_vrf * N7k setup/cleanup when missing F3 cards * New `image?` method for checking image version pattern; used as test for unprops * New `remove_all_vrfs` method that uses `test_get`/`test_set`; this is much faster than removing vrfs one at a time * Remove :description image check * Remove redundant cleanup --- tests/beaker_tests/cisco_vrf/test_vrf.rb | 28 ++++++++++-------------- tests/beaker_tests/lib/utilitylib.rb | 17 +++++++++++++- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/tests/beaker_tests/cisco_vrf/test_vrf.rb b/tests/beaker_tests/cisco_vrf/test_vrf.rb index c7747a635..3e4813da8 100644 --- a/tests/beaker_tests/cisco_vrf/test_vrf.rb +++ b/tests/beaker_tests/cisco_vrf/test_vrf.rb @@ -75,6 +75,7 @@ def unsupported_properties(_tests, _id) unprops << :vni unless platform[/n9k/] unprops << :route_distinguisher if nexus_i2_image + # unprops << :description if image?[/7.3.0.D1.1/] # CSCuy36637 else unprops << @@ -82,23 +83,14 @@ def unsupported_properties(_tests, _id) :shutdown << :vni end + logger.info(" unprops: #{unprops}") unless unprops.empty? unprops end # Overridden to properly handle dependencies for this test file. -def dependency_manifest(_tests, _id) - if operating_system == 'nexus' - dep = '' - if platform[/n7k/] - dep = %( - cisco_vdc { '#{default_vdc_name}': - # Must be f3-only - limit_resource_module_type => 'f3', - }) - end - else - dep = %( cisco_interface {'loopback100': ensure => 'present' } ) - end +def dependency_manifest(_tests, id) + return unless id[/non_default/] + dep = %( cisco_interface {'loopback100': ensure => 'present' } ) logger.info("\n * dependency_manifest\n#{dep}") dep end @@ -107,7 +99,12 @@ def dependency_manifest(_tests, _id) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - resource_absent_cleanup(agent, 'cisco_vrf', 'VRF CLEAN :: ') + teardown do + remove_all_vrfs(agent) + vdc_limit_f3_no_intf_needed(:clear) + end + remove_all_vrfs(agent) + vdc_limit_f3_no_intf_needed(:set) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") @@ -120,9 +117,6 @@ def dependency_manifest(_tests, _id) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") test_harness_run(tests, :non_default) - - # ------------------------------------------------------------------- - resource_absent_cleanup(agent, 'cisco_vrf', 'VRF CLEAN :: ') skipped_tests_summary(tests) end logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 9366f2cc7..1cb017495 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -829,7 +829,7 @@ def config_find_remove(agent, find=[], filter='incl .*') # test_get(agent, 'incl ^vlan') def test_get(agent, filter) cmd_prefix = PUPPET_BINPATH + "resource cisco_command_config 'cc' " - on(agent, cmd_prefix + "test_get='#{filter}'") + on(agent, cmd_prefix + "test_get=\"#{filter}\"") stdout end @@ -837,6 +837,7 @@ def test_get(agent, filter) # Example: # test_set(agent, 'no feature foo ; no feature bar') def test_set(agent, cmd) + return if cmd.empty? logger.info(cmd) cmd_prefix = PUPPET_BINPATH + "resource cisco_command_config 'cc' " on(agent, cmd_prefix + "test_set='#{cmd}'") @@ -1047,6 +1048,14 @@ def platform @cisco_hardware end +# Check if image matches pattern +@cached_img = nil +def image?(reset_cache=false) + return @cached_img unless @cached_img.nil? || reset_cache + on(agent, facter_cmd('-p cisco.images.system_image')) + @cached_img = stdout.nil? ? '' : stdout +end + # Check if this image is an I2 image @i2_image = nil # Cache the lookup result def nexus_i2_image @@ -1346,3 +1355,9 @@ def remove_all_vlans(agent, stepinfo='Remove all vlans & bridge-domains') resource_absent_cleanup(agent, 'cisco_vlan', 'vlans') end end + +def remove_all_vrfs(agent) + found = test_get(agent, "incl 'vrf context' | excl management").split("\n") + found.map! { |cmd| "no #{cmd}" if cmd[/^vrf context/] } + test_set(agent, found.compact.join(' ; ')) +end From d448cb957b7ff59a04671255dce0c5bc668a92c6 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Wed, 17 Aug 2016 10:35:11 -0400 Subject: [PATCH 048/203] f3 setup/teardown for vrf_af --- .../beaker_tests/cisco_vrf_af/test_vrf_af.rb | 19 +++++++++---------- tests/beaker_tests/lib/utilitylib.rb | 1 + 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/beaker_tests/cisco_vrf_af/test_vrf_af.rb b/tests/beaker_tests/cisco_vrf_af/test_vrf_af.rb index a833e3ba2..228342561 100644 --- a/tests/beaker_tests/cisco_vrf_af/test_vrf_af.rb +++ b/tests/beaker_tests/cisco_vrf_af/test_vrf_af.rb @@ -33,7 +33,6 @@ # Test hash test cases tests[:default] = { desc: 'Default Properties, vrf-af', - preclean: 'cisco_vrf', title_pattern: 'blue ipv4 unicast', manifest_props: { route_target_both_auto: 'default', @@ -74,7 +73,6 @@ tests[:title_patterns_1] = { desc: 'T.1 Title Pattern', - preclean: 'cisco_vrf', title_pattern: 'new_york', title_params: { vrf: 'red', afi: 'ipv4', safi: 'unicast' }, manifest_props: {}, @@ -124,17 +122,11 @@ def unsupported_properties(_tests, _id) # Overridden to properly handle dependencies for this test file. def dependency_manifest(tests, id) if operating_system == 'nexus' + return unless id[/non_default/] dep = %( cisco_command_config { 'policy_config': command => 'route-map abc permit 10' } ) - if platform[/n7k/] - dep += %( - cisco_vdc { '#{default_vdc_name}': - # Must be f3-only - limit_resource_module_type => 'f3', - }) - end else t = puppet_resource_title_pattern_munge(tests, id) dep = %( @@ -153,6 +145,13 @@ def dependency_manifest(tests, id) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown do + remove_all_vrfs(agent) + vdc_limit_f3_no_intf_needed(:clear) + end + remove_all_vrfs(agent) + vdc_limit_f3_no_intf_needed(:set) + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") id = :default @@ -168,12 +167,12 @@ def dependency_manifest(tests, id) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 3. Title Pattern Testing") + remove_all_vrfs(agent) test_harness_run(tests, :title_patterns_1) test_harness_run(tests, :title_patterns_2) test_harness_run(tests, :title_patterns_3) # ----------------------------------- - resource_absent_cleanup(agent, 'cisco_vrf') skipped_tests_summary(tests) end logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 1cb017495..52a4a11e4 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1340,6 +1340,7 @@ def vdc_limit_f3_no_intf_needed(action=:set) on(agent, cmd, pty: true).stdout[/limit_resource.*'(f3)'/] when :clear + # Reset to default only if no physical F3 is present teardown_vdc end end From 5ea5b1f80458757771ef4a7391d85d8e882ec189 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Wed, 17 Aug 2016 10:51:02 -0400 Subject: [PATCH 049/203] f3 setup/teardown bridge-domain --- .../test_bridge_domain_vni.rb | 17 ++++++----------- tests/beaker_tests/lib/utilitylib.rb | 6 +++--- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/tests/beaker_tests/cisco_bridge_domain/test_bridge_domain_vni.rb b/tests/beaker_tests/cisco_bridge_domain/test_bridge_domain_vni.rb index c6f93b248..4466b5e33 100644 --- a/tests/beaker_tests/cisco_bridge_domain/test_bridge_domain_vni.rb +++ b/tests/beaker_tests/cisco_bridge_domain/test_bridge_domain_vni.rb @@ -56,17 +56,16 @@ }, } -def test_harness_dependencies(_tests, id) - return unless id == :non_default - - # feature vni requires F3 linecards - limit_resource_module_type_set(default_vdc_name, 'f3') -end - ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{testheader}" do + teardown do + remove_all_vlans(agent) + vdc_limit_f3_no_intf_needed(:clear) + end + remove_all_vlans(agent) + vdc_limit_f3_no_intf_needed(:set) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") @@ -74,9 +73,5 @@ def test_harness_dependencies(_tests, id) resource_absent_cleanup(agent, 'cisco_bridge_domain_vni') test_harness_run(tests, :non_default_random) - resource_absent_cleanup(agent, 'cisco_bridge_domain_vni') - - teardown_vdc end - logger.info('TestCase :: # {testheader} :: End') diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 52a4a11e4..686fac8d5 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -966,13 +966,13 @@ def vdc_allocate_interface_set(vdc, intf) # VDC post-test cleanup def teardown_vdc - logger.info("\n* Teardown VDC") + return unless mt_full_interface # Testbeds without F3 cards should be set back to their default state; # failure to do so will leave the testbed without usable interfaces. # Assume that F3 testbeds should be left with module-type set to F3. - limit_resource_module_type_set(default_vdc_name, nil) unless - mt_full_interface + logger.info("\n* Teardown VDC: Reset limit-resource module-type") + limit_resource_module_type_set(default_vdc_name, nil) end # Facter command builder helper method From 9a1bd8b81d20392f4abd0a69f8a5a68d21fb25f7 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Wed, 17 Aug 2016 11:03:22 -0400 Subject: [PATCH 050/203] f3 setup/teardown for encap --- .../cisco_encapsulation/test_encapsulation.rb | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/tests/beaker_tests/cisco_encapsulation/test_encapsulation.rb b/tests/beaker_tests/cisco_encapsulation/test_encapsulation.rb index 09d213c19..f0a957c6c 100644 --- a/tests/beaker_tests/cisco_encapsulation/test_encapsulation.rb +++ b/tests/beaker_tests/cisco_encapsulation/test_encapsulation.rb @@ -53,21 +53,15 @@ }, } -# TEST PRE-REQUISITES -# - F3 linecard assigned to admin vdc -def dependency_manifest(*) - " - cisco_vdc { '#{default_vdc_name}': - ensure => present, - limit_resource_module_type => 'f3', - } - " -end - ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown do + vdc_limit_f3_no_intf_needed(:clear) + end + vdc_limit_f3_no_intf_needed(:set) + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") id = :default @@ -80,8 +74,5 @@ def dependency_manifest(*) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") test_harness_run(tests, :non_default) - - # ------------------------------------------------------------------- - resource_absent_cleanup(agent, 'cisco_encapsulation') end logger.info("TestCase :: #{tests[:resource_name]} :: End") From f9bdc8759fc4d7123c37db2c719d30ff5f934d0f Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Wed, 17 Aug 2016 11:15:31 -0400 Subject: [PATCH 051/203] bkr setup/teardown cleanups --- .../cisco_interface/test_interface_vlan_mapping.rb | 4 +++- tests/beaker_tests/lib/utilitylib.rb | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/beaker_tests/cisco_interface/test_interface_vlan_mapping.rb b/tests/beaker_tests/cisco_interface/test_interface_vlan_mapping.rb index 5af626be4..636757f58 100644 --- a/tests/beaker_tests/cisco_interface/test_interface_vlan_mapping.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_vlan_mapping.rb @@ -57,6 +57,7 @@ skip_unless_supported(tests) # Assign a test interface. +intf = '' if platform[/n7k/] unless mt_full_interface prereq_skip(nil, self, 'Test requires F3 or compatible line module') @@ -102,6 +103,8 @@ # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown { interface_cleanup(agent, intf) } + # ----------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") test_harness_run(tests, :default) @@ -111,7 +114,6 @@ test_harness_run(tests, :non_default) # ------------------------------------------------------------------- - interface_cleanup(agent, intf) end logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 686fac8d5..a7b7e368c 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -436,6 +436,7 @@ def config_encap_profile_vni_global(agent, cmd, # Helper to nuke a single interface. This is needed to remove all # configurations from the interface. def interface_cleanup(agent, intf, stepinfo='Interface Clean:') + return if intf.empty? step "TestStep :: #{stepinfo}" do cmd = "resource cisco_command_config 'interface_cleanup' "\ "command='default interface #{intf}'" @@ -966,7 +967,7 @@ def vdc_allocate_interface_set(vdc, intf) # VDC post-test cleanup def teardown_vdc - return unless mt_full_interface + return if mt_full_interface # Testbeds without F3 cards should be set back to their default state; # failure to do so will leave the testbed without usable interfaces. From e0d0c1821b151b02078f6af6663b8772a1243c49 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Wed, 17 Aug 2016 11:28:42 -0400 Subject: [PATCH 052/203] f3 setup/teardown for interface_svi --- tests/beaker_tests/cisco_bgp/test_bgp.rb | 4 +--- .../cisco_encapsulation/test_encapsulation.rb | 4 +--- tests/beaker_tests/cisco_interface/test_interface_svi.rb | 8 ++++++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/beaker_tests/cisco_bgp/test_bgp.rb b/tests/beaker_tests/cisco_bgp/test_bgp.rb index 64ed5e835..c61f9dd37 100644 --- a/tests/beaker_tests/cisco_bgp/test_bgp.rb +++ b/tests/beaker_tests/cisco_bgp/test_bgp.rb @@ -235,9 +235,7 @@ def unsupported_properties(tests, id) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - teardown do - resource_absent_cleanup(agent, 'cisco_bgp') - end + teardown { resource_absent_cleanup(agent, 'cisco_bgp') } # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") diff --git a/tests/beaker_tests/cisco_encapsulation/test_encapsulation.rb b/tests/beaker_tests/cisco_encapsulation/test_encapsulation.rb index f0a957c6c..bc35bd2ca 100644 --- a/tests/beaker_tests/cisco_encapsulation/test_encapsulation.rb +++ b/tests/beaker_tests/cisco_encapsulation/test_encapsulation.rb @@ -57,9 +57,7 @@ # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - teardown do - vdc_limit_f3_no_intf_needed(:clear) - end + teardown { vdc_limit_f3_no_intf_needed(:clear) } vdc_limit_f3_no_intf_needed(:set) # ------------------------------------------------------------------- diff --git a/tests/beaker_tests/cisco_interface/test_interface_svi.rb b/tests/beaker_tests/cisco_interface/test_interface_svi.rb index 5cfbda087..391cd3f4f 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_svi.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_svi.rb @@ -45,7 +45,6 @@ tests[:default_mgmt] = { desc: "1.1 Default 'mgmt'", title_pattern: intf, - preclean_intf: true, manifest_props: { svi_management: 'default' }, @@ -99,6 +98,12 @@ # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown do + vdc_limit_f3_no_intf_needed(:clear) + remove_interface(agent, intf) + end + remove_interface(agent, intf) + vdc_limit_f3_no_intf_needed(:set) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Property Testing") test_harness_run(tests, :default_mgmt) @@ -110,7 +115,6 @@ test_harness_run(tests, :anycast) # ------------------------------------------------------------------- - remove_interface(agent, intf) skipped_tests_summary(tests) end From d515b873306ee4a7f902baeff981ed87ae4ef2b2 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Wed, 17 Aug 2016 11:45:42 -0400 Subject: [PATCH 053/203] f3 setup/teardown for service_vni --- .../test_interface_vlan_mapping.rb | 11 +++-------- .../test_interface_service_vni.rb | 16 ++++++---------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/tests/beaker_tests/cisco_interface/test_interface_vlan_mapping.rb b/tests/beaker_tests/cisco_interface/test_interface_vlan_mapping.rb index 636757f58..42cbe43b1 100644 --- a/tests/beaker_tests/cisco_interface/test_interface_vlan_mapping.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_vlan_mapping.rb @@ -57,16 +57,11 @@ skip_unless_supported(tests) # Assign a test interface. -intf = '' -if platform[/n7k/] - unless mt_full_interface - prereq_skip(nil, self, 'Test requires F3 or compatible line module') - end +if (intf = mt_full_interface) + tests[:intf] = intf setup_mt_full_env(tests, self) - # Use test interface discovered by setup_mt_full_env(). - intf = tests[:intf] else - intf = find_interface(tests) + prereq_skip(nil, self, 'Test requires F3 or compatible line module') end # Test hash test cases diff --git a/tests/beaker_tests/cisco_interface_service_vni/test_interface_service_vni.rb b/tests/beaker_tests/cisco_interface_service_vni/test_interface_service_vni.rb index b5f7a7d56..aaa100cb1 100644 --- a/tests/beaker_tests/cisco_interface_service_vni/test_interface_service_vni.rb +++ b/tests/beaker_tests/cisco_interface_service_vni/test_interface_service_vni.rb @@ -37,6 +37,7 @@ if (intf = mt_full_interface) tests[:intf] = intf + setup_mt_full_env(tests, self) else prereq_skip(nil, self, 'MT-full tests require F3 or compatible line module') end @@ -68,10 +69,6 @@ def dependency_manifest(tests, id) return unless id == :default dep = %( - cisco_vdc { '#{default_vdc_name}': - # Must be f3-only - limit_resource_module_type => 'f3', - } cisco_encapsulation {'vni_500_5000': dot1q_map => ['500', '5000'], } @@ -87,7 +84,11 @@ def dependency_manifest(tests, id) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - # Clean up any stale pre-req configs that might conflict with our test + teardown do + resource_absent_cleanup(agent, 'cisco_interface_service_vni') + resource_absent_cleanup(agent, 'cisco_encapsulation') + interface_cleanup(agent, intf) + end resource_absent_cleanup(agent, 'cisco_encapsulation') interface_cleanup(agent, intf) @@ -95,10 +96,5 @@ def dependency_manifest(tests, id) logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") test_harness_run(tests, :default) test_harness_run(tests, :non_default) - - # ----------------------------------- - resource_absent_cleanup(agent, 'cisco_interface_service_vni') - resource_absent_cleanup(agent, 'cisco_encapsulation') - interface_cleanup(agent, intf, 'Post-test cleanup: ') end logger.info("TestCase :: #{tests[:resource_name]} :: End") From 8865b26ac31e0577243f78af7c134dbfd5a7a5a1 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Wed, 17 Aug 2016 11:55:28 -0400 Subject: [PATCH 054/203] f3 setup/teardown test_private_vlan --- tests/beaker_tests/cisco_vdc/test_vdc.rb | 3 +- .../cisco_vlan/test_private_vlan.rb | 33 ++++++------------- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/tests/beaker_tests/cisco_vdc/test_vdc.rb b/tests/beaker_tests/cisco_vdc/test_vdc.rb index e55ca1bd1..6da899fa6 100644 --- a/tests/beaker_tests/cisco_vdc/test_vdc.rb +++ b/tests/beaker_tests/cisco_vdc/test_vdc.rb @@ -57,9 +57,10 @@ def test_harness_dependencies(_tests, id) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown { vdc_limit_f3_no_intf_needed(:clear) } + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Non Default Property Testing") test_harness_run(tests, :non_default) - teardown_vdc end logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_vlan/test_private_vlan.rb b/tests/beaker_tests/cisco_vlan/test_private_vlan.rb index 869f08cb2..fefdd66be 100644 --- a/tests/beaker_tests/cisco_vlan/test_private_vlan.rb +++ b/tests/beaker_tests/cisco_vlan/test_private_vlan.rb @@ -44,7 +44,6 @@ tests[:primary] = { desc: '1.1 Primary', title_pattern: '100', - preclean: 'cisco_vlan', manifest_props: { pvlan_type: 'primary', pvlan_association: '101, 102, 98-99, 105', @@ -71,38 +70,26 @@ }, } -# This method overrides the method in utilitylib.rb to set up dependencies -# for interface tests. -def test_harness_dependencies(*) - logger.info(' * Process test_harness_dependencies (test_private_vlan)') - remove_all_vlans(agent) -end - -# Overridden to properly handle dependencies for this test file. -def dependency_manifest(_tests, _id) - dep = '' - if platform[/n7k/] - dep = %( - cisco_vdc { '#{default_vdc_name}': - # Must be f3-only - limit_resource_module_type => 'f3', - }) - end - logger.info("\n * dependency_manifest\n#{dep}") - dep -end - ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown do + remove_all_vlans(agent) + vdc_limit_f3_no_intf_needed(:clear) + end + remove_all_vlans(agent) + vdc_limit_f3_no_intf_needed(:set) + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Property Testing") test_harness_run(tests, :primary) + + remove_all_vlans(agent) test_harness_run(tests, :community) - test_harness_run(tests, :isolated) remove_all_vlans(agent) + test_harness_run(tests, :isolated) end logger.info("TestCase :: #{tests[:resource_name]} :: End") From 9a9879065dc3e996f02f645701006ee24fce0261 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Wed, 17 Aug 2016 12:51:34 -0400 Subject: [PATCH 055/203] f3 setup/teardown for vxlan_vtep* --- .../cisco_vxlan_vtep/test_vxlan_vtep.rb | 25 +++++----------- .../test_vxlan_vtep_vni.rb | 29 +++++-------------- 2 files changed, 14 insertions(+), 40 deletions(-) diff --git a/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb b/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb index 962bff123..8646290b8 100644 --- a/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb +++ b/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb @@ -38,7 +38,6 @@ # Test hash test cases tests[:default] = { title_pattern: 'nve1', - preclean: 'cisco_vxlan_vtep', manifest_props: { description: 'default', host_reachability: 'default', @@ -79,37 +78,27 @@ def test_harness_dependencies(*) command_config(agent, cmd, cmd) end -# Overridden to properly handle dependencies for this test file. -def dependency_manifest(_tests, _id) - dep = '' - if platform[/n7k/] - dep = %( - cisco_vdc { '#{default_vdc_name}': - # Must be f3-only - limit_resource_module_type => 'f3', - }) - end - logger.info("\n * dependency_manifest\n#{dep}") - dep -end - ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown do + resource_absent_cleanup(agent, 'cisco_vxlan_vtep') + vdc_limit_f3_no_intf_needed(:clear) + end + resource_absent_cleanup(agent, 'cisco_vxlan_vtep') + vdc_limit_f3_no_intf_needed(:set) + # ----------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") id = :default test_harness_run(tests, id) tests[id][:ensure] = :absent - tests[id].delete(:preclean) test_harness_run(tests, id) # ----------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") test_harness_run(tests, :non_default) - - resource_absent_cleanup(agent, 'cisco_vxlan_vtep') end logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb b/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb index 764650662..a38fcffb4 100644 --- a/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb +++ b/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb @@ -282,20 +282,6 @@ def test_harness_dependencies(*) skip_if_nv_overlay_rejected(agent) end -# Overridden to properly handle dependencies for this test file. -def dependency_manifest(*) - dep = '' - if platform[/n7k/] - dep = %( - cisco_vdc { '#{default_vdc_name}': - # Must be f3-only - limit_resource_module_type => 'f3', - }) - end - logger.info("\n * dependency_manifest\n#{dep}") - dep -end - def build_manifest_cisco_vxlan_vtep_vni(tests, id) if tests[id][:ensure] == :absent state = 'ensure => absent,' @@ -313,7 +299,6 @@ def build_manifest_cisco_vxlan_vtep_vni(tests, id) # cisco_vxlan_vtep_vni needs cisco_vxlan_vtep as a prerequisite. tests[id][:manifest] = "cat <#{PUPPETMASTER_MANIFESTPATH} node 'default' { - #{dependency_manifest} cisco_vxlan_vtep {'nve1': ensure => present, host_reachability => 'evpn', @@ -349,12 +334,15 @@ def test_harness_cisco_vxlan_vtep_vni(tests, id) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{testheader}" do - #------------------------------------------------------------------- - resource_absent_cleanup(agent, 'cisco_vxlan_vtep_vni', - 'Setup switch for cisco_vxlan_vtep_vni provider test') - logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + teardown do + resource_absent_cleanup(agent, 'cisco_vxlan_vtep_vni') + vdc_limit_f3_no_intf_needed(:clear) + end + resource_absent_cleanup(agent, 'cisco_vxlan_vtep_vni') + vdc_limit_f3_no_intf_needed(:set) # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") id = 'default_properties_ingress_replication' tests[id][:desc] = '1.1 Default Properties Ingress Replication' test_harness_cisco_vxlan_vtep_vni(tests, id) @@ -418,9 +406,6 @@ def test_harness_cisco_vxlan_vtep_vni(tests, id) id = 'suppress_uuc_false' tests[id][:desc] = '2.10 Suppress Unknown Unicast' test_harness_cisco_vxlan_vtep_vni(tests, id) - - resource_absent_cleanup(agent, 'cisco_vxlan_vtep_vni', - 'Setup switch for cisco_vxlan_vtep_vni provider test') end logger.info('TestCase :: # {testheader} :: End') From 23a16c3f543872e7362413a0722b23c4328d507f Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Wed, 17 Aug 2016 14:32:54 -0400 Subject: [PATCH 056/203] f3 setup/teardown for fabricpath_topology --- .../test_fabricpath_topology.rb | 182 ++++-------------- 1 file changed, 34 insertions(+), 148 deletions(-) diff --git a/tests/beaker_tests/cisco_fabricpath_topology/test_fabricpath_topology.rb b/tests/beaker_tests/cisco_fabricpath_topology/test_fabricpath_topology.rb index 72ac46d15..537cad691 100644 --- a/tests/beaker_tests/cisco_fabricpath_topology/test_fabricpath_topology.rb +++ b/tests/beaker_tests/cisco_fabricpath_topology/test_fabricpath_topology.rb @@ -13,179 +13,65 @@ # See the License for the specific language governing permissions and # limitations under the License. ############################################################################### -# TestCase Name: -# ------------- -# test-fabricpath_topology.rb # -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet fabricpath_topology resource testcase for Puppet Agent on -# Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# - Host configuration file contains agent and master information. -# - SSH is enabled on the N9K Agent. -# - Puppet master/server is started. -# - Puppet agent certificate has been signed on the Puppet master/server. -# -# TestCase: -# --------- -# This Tunnel resource test verifies default values for all properties. -# -# The following exit_codes are validated for Puppet, Vegas shell and -# Bash shell commands. -# -# Vegas and Bash Shell Commands: -# 0 - successful command execution -# > 0 - failed command execution. -# -# Puppet Commands: -# 0 - no changes have occurred -# 1 - errors have occurred, -# 2 - changes have occurred -# 4 - failures have occurred and -# 6 - changes and failures have occurred. -# -# NOTE: 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# -# The test cases use RegExp pattern matching on stdout or output IO -# instance attributes to verify resource properties. +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage # ############################################################################### - require File.expand_path('../../lib/utilitylib.rb', __FILE__) -# ----------------------------- -# Common settings and variables -# ----------------------------- -testheader = 'Resource cisco_fabricpath_topology' - -# The 'tests' hash is used to define all of the test data values and expected -# results. It is also used to pass optional flags to the test methods when -# necessary. - -# 'tests' hash -# Top-level keys set by caller: -# tests[:master] - the master object -# tests[:agent] - the agent object -# +# Test hash top-level keys tests = { - master: master, - agent: agent, - platform: 'n(5|6|7)k', + agent: agent, + master: master, + platform: 'n(5|6|7)k', + resource_name: 'cisco_fabricpath_topology', + operating_system: 'nexus', } -# tests[id] keys set by caller and used by test_harness_common: -# -# tests[id] keys set by caller: -# tests[id][:platform] - a regexp pattern to match against supported platforms. -# This key overrides a tests[:platform] key -# tests[id][:desc] - a string to use with logs & debugs -# tests[id][:manifest] - the complete manifest, as used by test_harness_common -# tests[id][:resource] - a hash of expected states, used by test_resource -# tests[id][:resource_cmd] - 'puppet resource' command to use with test_resource -# tests[id][:ensure] - (Optional) set to :present or :absent before calling -# tests[id][:code] - (Optional) override the default exit code in some tests. -# -# These keys are local use only and not used by test_harness_common: -# -# tests[id][:manifest_props] - This is essentially a master list of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the list -# tests[id][:resource_props] - This is essentially a master hash of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the hash -# tests[id][:title_pattern] - (Optional) defines the manifest title. -# Can be used with :af for mixed title/af testing. If mixing, :af values will -# be merged with title values and override any duplicates. If omitted, -# :title_pattern will be set to 'id'. -# -# member_vlans property below is intentionally mixed to test range_summarize() -# -tests['non_default_properties'] = { +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + +tests[:non_default] = { + desc: '2.1 Non Default Properties', title_pattern: '10', - manifest_props: " - member_vlans => '10-20, 30, 14, 31, 100-110', - topo_name => 'Topo-1', - ", - resource_props: { - 'member_vlans' => '10-20,30-31,100-110', - 'topo_name' => 'Topo-1', + manifest_props: { + member_vlans: '10-20, 30, 14, 31, 100-110', + topo_name: 'Topo-1', + }, + resource: { + member_vlans: '10-20,30-31,100-110', + topo_name: 'Topo-1', }, } -################################################################# -# HELPER FUNCTIONS -################################################################# - -# Full command string for puppet resource command -def puppet_resource_cmd - PUPPET_BINPATH + 'resource cisco_fabricpath_topology' -end - -def build_manifest_fabricpath_topology(tests, id) - if tests[id][:ensure] == :absent - state = 'ensure => absent,' - tests[id][:resource] = {} - else - state = 'ensure => present,' - manifest = tests[id][:manifest_props] - tests[id][:resource] = tests[id][:resource_props] - end - - tests[id][:title_pattern] = id if tests[id][:title_pattern].nil? - logger.debug("build_manifest_fabricpath_topology :: title_pattern:\n" + - tests[id][:title_pattern]) - tests[id][:manifest] = "cat <#{PUPPETMASTER_MANIFESTPATH} - node 'default' { - cisco_fabricpath_topology { '#{tests[id][:title_pattern]}': - #{state} - #{manifest} - } - } -EOF" -end - -def test_harness_fabricpath_topology(tests, id) - return unless platform_supports_test(tests, id) - - tests[id][:resource_cmd] = puppet_resource_cmd - - # Build the manifest for this test - build_manifest_fabricpath_topology(tests, id) - - test_harness_common(tests, id) - - tests[id][:ensure] = nil -end - def testbed_cleanup(agent) - logger.info("\n* testbed cleanup") cmds = ['feature nv overlay', 'feature-set fabricpath'] config_find_remove(agent, cmds, 'incl ^feature') - remove_all_vlans(agent) end ################################################################# # TEST CASE EXECUTION ################################################################# -test_name "TestCase :: #{testheader}" do +test_name "TestCase :: #{tests[:resource_name]}" do + teardown do + vdc_limit_f3_no_intf_needed(:clear) + testbed_cleanup(agent) + end testbed_cleanup(agent) + vdc_limit_f3_no_intf_needed(:set) - logger.info("\n#{'-' * 60}\nSection 0. Testbed Initialization") - setup_fabricpath_env(tests, self) - + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Non Default Property Testing") - id = 'non_default_properties' - tests[id][:desc] = '1.1 Non Default Properties' - test_harness_fabricpath_topology(tests, id) + id = :non_default + test_harness_run(tests, id) - tests[id][:desc] = '1.2 Non Default Properties (absent)' tests[id][:ensure] = :absent - test_harness_fabricpath_topology(tests, id) - - testbed_cleanup(agent) + test_harness_run(tests, id) end - -logger.info('TestCase :: # {testheader} :: End') +logger.info("TestCase :: #{tests[:resource_name]} :: End") From bfecb4a72a96a0483c20cb5f9e5ff9d5afe53a20 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Wed, 17 Aug 2016 15:26:30 -0400 Subject: [PATCH 057/203] setup/teardown cleanups for cisco_itd_device_group_node --- .../test_itd_device_group_node.rb | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/beaker_tests/cisco_itd_device_group_node/test_itd_device_group_node.rb b/tests/beaker_tests/cisco_itd_device_group_node/test_itd_device_group_node.rb index be466d212..a37f63023 100644 --- a/tests/beaker_tests/cisco_itd_device_group_node/test_itd_device_group_node.rb +++ b/tests/beaker_tests/cisco_itd_device_group_node/test_itd_device_group_node.rb @@ -32,11 +32,14 @@ resource_name: 'cisco_itd_device_group_node', } +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) +skip_nexus_i2_image(tests) + # Test hash test cases tests[:default] = { desc: '1.1 Defaults', title_pattern: 'icmpGroup 1.1.1.1', - preclean: 'cisco_itd_device_group_node', manifest_props: { hot_standby: 'default', node_type: 'ip', @@ -135,10 +138,14 @@ def test_harness_dependencies(_tests, _id) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown do + resource_absent_cleanup(agent, 'cisco_itd_device_group_node') + resource_absent_cleanup(agent, 'cisco_itd_device_group') + end + resource_absent_cleanup(agent, 'cisco_itd_device_group_node') + resource_absent_cleanup(agent, 'cisco_itd_device_group') + # ------------------------------------------------------------------- - device = platform - logger.info("#### This device is of type: #{device} #####") - skip_nexus_i2_image(tests) logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") test_harness_run(tests, :default) @@ -155,9 +162,8 @@ def test_harness_dependencies(_tests, _id) test_harness_run(tests, :non_default_dns) test_harness_run(tests, :non_default_tcp) test_harness_run(tests, :non_default_udp) - resource_absent_cleanup(agent, 'cisco_itd_device_group_node') - resource_absent_cleanup(agent, 'cisco_itd_device_group') + + # ------------------------------------------------------------------- skipped_tests_summary(tests) end - logger.info("TestCase :: #{tests[:resource_name]} :: End") From 9d016460e5174a1757d5b85899c7cf7933d817a0 Mon Sep 17 00:00:00 2001 From: saichint Date: Thu, 18 Aug 2016 13:28:47 -0700 Subject: [PATCH 058/203] beaker fix for resilient and symmetry non support on n3k (#381) * Fix interface_ospf provider and beaker * doc * doc * review comments * add mtu, pri, shut and tx delay to int ospf * documentation * bug fix * Fix for symmetry and resilient no support on n3k * review comment * rewview comments --- .../cisco_portchannel_global/cisco.rb | 5 +- .../test_portchannel_global.rb | 713 +++++++----------- 2 files changed, 267 insertions(+), 451 deletions(-) diff --git a/lib/puppet/provider/cisco_portchannel_global/cisco.rb b/lib/puppet/provider/cisco_portchannel_global/cisco.rb index 7bfc617bf..85288b799 100644 --- a/lib/puppet/provider/cisco_portchannel_global/cisco.rb +++ b/lib/puppet/provider/cisco_portchannel_global/cisco.rb @@ -187,11 +187,10 @@ def port_channel_load_balance_sym_concat_rot_set def check_port_channel_load_balance_no_rotate_all fail ArgumentError, 'Invalid arguments present in manifest' if @resource[:asymmetric] || @resource[:concatenation] || - @resource[:rotate] + @resource[:rotate] || @resource[:hash_poly] fail ArgumentError, 'Required arguments not present in manifest' unless @resource[:bundle_hash] && - @resource[:bundle_select] && - @resource[:symmetry] + @resource[:bundle_select] end def port_channel_load_balance_no_rotate_set diff --git a/tests/beaker_tests/cisco_portchannel_global/test_portchannel_global.rb b/tests/beaker_tests/cisco_portchannel_global/test_portchannel_global.rb index d5b64d118..91fd397be 100755 --- a/tests/beaker_tests/cisco_portchannel_global/test_portchannel_global.rb +++ b/tests/beaker_tests/cisco_portchannel_global/test_portchannel_global.rb @@ -12,509 +12,326 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# test-portchannel_global.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet portchannel_global resource testcase for Puppet Agent -# on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# - Host configuration file contains agent and master information. -# - SSH is enabled on the N9K Agent. -# - Puppet master/server is started. -# - Puppet agent certificate has been signed on the Puppet master/server. -# -# TestCase: -# --------- -# This portchannel_global resource test verifies default values for all -# properties. -# -# The following exit_codes are validated for Puppet, Vegas shell and -# Bash shell commands. # -# Vegas and Bash Shell Commands: -# 0 - successful command execution -# > 0 - failed command execution. -# -# Puppet Commands: -# 0 - no changes have occurred -# 1 - errors have occurred, -# 2 - changes have occurred -# 4 - failures have occurred and -# 6 - changes and failures have occurred. -# -# NOTE: 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# -# The test cases use RegExp pattern matching on stdout or output IO -# instance attributes to verify resource properties. +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage # ############################################################################### require File.expand_path('../../lib/utilitylib.rb', __FILE__) -# ----------------------------- -# Common settings and variables -# ----------------------------- -testheader = 'Resource cisco_portchannel_global' - -# Define PUPPETMASTER_MANIFESTPATH. - -# The 'tests' hash is used to define all of the test data values and expected -# results. It is also used to pass optional flags to the test methods when -# necessary. - -# 'tests' hash -# Top-level keys set by caller: -# tests[:master] - the master object -# tests[:agent] - the agent object -# +# Test hash top-level keys tests = { - master: master, - agent: agent, + master: master, + agent: agent, + operating_system: 'nexus', + resource_name: 'cisco_portchannel_global', + ensurable: false, } -# tests[id] keys set by caller and used by test_harness_common: -# -# tests[id] keys set by caller: -# tests[id][:desc] - a string to use with logs & debugs -# tests[id][:manifest] - the complete manifest, as used by test_harness_common -# tests[id][:resource] - a hash of expected states, used by test_resource -# tests[id][:resource_cmd] - 'puppet resource' command to use with test_resource -# tests[id][:ensure] - (Optional) set to :present or :absent before calling -# tests[id][:code] - (Optional) override the default exit code in some tests. -# -# These keys are local use only and not used by test_harness_common: -# -# tests[id][:manifest_props] - This is essentially a master list of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the list -# tests[id][:resource_props] - This is essentially a master hash of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the hash -# tests[id][:title_pattern] - (Optional) defines the manifest title. -# Can be used with :af for mixed title/af testing. If mixing, :af values will -# be merged with title values and override any duplicates. If omitted, -# :title_pattern will be set to 'id'. -# - -tests['default_properties_asym'] = { +tests[:default] = { + desc: '1.1 Defaults', title_pattern: 'default', - manifest_props: " - asymmetric => 'default', - bundle_hash => 'default', - bundle_select => 'default', - hash_distribution => 'default', - load_defer => 'default', - rotate => 'default', - ", - code: [0, 2], - resource_props: { - 'asymmetric' => 'false', - 'bundle_hash' => 'ip', - 'bundle_select' => 'src-dst', - 'hash_distribution' => 'adaptive', - 'load_defer' => '120', - 'rotate' => '0', - }, -} - -tests['non_default_properties_asym'] = { - title_pattern: 'default', - manifest_props: " - asymmetric => 'true', - bundle_hash => 'ip-l4port', - bundle_select => 'dst', - hash_distribution => 'fixed', - load_defer => '1000', - rotate => '4', - ", - resource_props: { - 'asymmetric' => 'true', - 'bundle_hash' => 'ip-l4port', - 'bundle_select' => 'dst', - 'hash_distribution' => 'fixed', - 'load_defer' => '1000', - 'rotate' => '4', + manifest_props: { + asymmetric: 'default', + bundle_hash: 'default', + bundle_select: 'default', + concatenation: 'default', + hash_distribution: 'default', + hash_poly: 'CRC10b', + load_defer: 'default', + resilient: 'default', + rotate: 'default', + symmetry: 'default', }, -} - -tests['default_properties_eth'] = { - title_pattern: 'default', - manifest_props: " - bundle_hash => 'default', - bundle_select => 'default', - hash_poly => 'CRC10b', - ", code: [0, 2], - resource_props: { - 'bundle_hash' => 'ip', - 'bundle_select' => 'src-dst', - 'hash_poly' => 'CRC10b', + resource: { + asymmetric: 'false', + bundle_hash: 'ip', + bundle_select: 'src-dst', + concatenation: 'false', + hash_distribution: 'adaptive', + hash_poly: 'CRC10b', + load_defer: '120', + resilient: 'false', + rotate: '0', + symmetry: 'false', }, } -tests['non_default_properties_eth'] = { - title_pattern: 'default', - manifest_props: " - bundle_hash => 'mac', - bundle_select => 'dst', - hash_poly => 'CRC10c', - ", - resource_props: { - 'bundle_hash' => 'mac', - 'bundle_select' => 'dst', - 'hash_poly' => 'CRC10c', +# Per platform default values +resource = { + n3k: { + resilient: 'true' }, -} - -tests['default_properties_sym'] = { - title_pattern: 'default', - manifest_props: " - bundle_hash => 'default', - bundle_select => 'default', - concatenation => 'default', - resilient => 'default', - rotate => 'default', - symmetry => 'default', - ", - code: [0, 2], - resource_props: { - 'bundle_hash' => 'ip-l4port', - 'bundle_select' => 'src-dst', - 'concatenation' => 'false', - 'resilient' => 'false', - 'rotate' => '0', - 'symmetry' => 'false', + n89k: { + bundle_hash: 'ip-l4port' }, } -tests['non_default_properties_sym'] = { - title_pattern: 'default', - manifest_props: " - bundle_hash => 'ip', - bundle_select => 'src-dst', - concatenation => 'true', - resilient => 'true', - rotate => '4', - symmetry => 'true', - ", - resource_props: { - 'bundle_hash' => 'ip', - 'bundle_select' => 'src-dst', - 'concatenation' => 'true', - 'resilient' => 'true', - 'rotate' => '4', - 'symmetry' => 'true', - }, -} +tests[:default][:resource].merge!(resource[:n3k]) if platform[/n3k/] +tests[:default][:resource].merge!(resource[:n89k]) if platform[/n(8|9)k/] -tests['default_properties_no_hash'] = { +tests[:non_default] = { + desc: '2.1 Non Defaults', title_pattern: 'default', - manifest_props: " - bundle_hash => 'default', - bundle_select => 'default', - rotate => 'default', - ", - code: [0, 2], - resource_props: { - 'bundle_hash' => 'ip-l4port', - 'bundle_select' => 'src-dst', - 'rotate' => '0', + manifest_props: { + asymmetric: 'true', + bundle_hash: 'ip-l4port', + bundle_select: 'dst', + concatenation: 'true', + hash_distribution: 'fixed', + hash_poly: 'CRC10c', + load_defer: '1000', + resilient: 'true', + rotate: '4', + symmetry: 'true', }, } -tests['non_default_properties_no_hash'] = { - title_pattern: 'default', - manifest_props: " - bundle_hash => 'ip', - bundle_select => 'dst', - rotate => '4', - ", - resource_props: { - 'bundle_hash' => 'ip', - 'bundle_select' => 'dst', - 'rotate' => '4', +# Per platform non default values +manifest_non = { + n3k: { + bundle_hash: 'ip-only', + bundle_select: 'src-dst', + resilient: 'false', }, -} - -tests['default_properties_no_rotate'] = { - title_pattern: 'default', - manifest_props: " - bundle_hash => 'default', - bundle_select => 'default', - resilient => 'default', - symmetry => 'default', - ", - code: [0, 2], - resource_props: { - 'bundle_hash' => 'ip', - 'bundle_select' => 'src-dst', - 'resilient' => 'true', - 'symmetry' => 'false', + n56k: { + bundle_hash: 'mac' }, -} - -tests['non_default_properties_no_rotate'] = { - title_pattern: 'default', - manifest_props: " - bundle_hash => 'ip-only', - bundle_select => 'src-dst', - resilient => 'false', - symmetry => 'true', - ", - resource_props: { - 'bundle_hash' => 'ip-only', - 'bundle_select' => 'src-dst', - 'resilient' => 'false', - 'symmetry' => 'true', + n8k: { + bundle_hash: 'ip' + }, + n9k: { + bundle_hash: 'ip', + bundle_select: 'src-dst', }, } -################################################################# -# HELPER FUNCTIONS -################################################################# - -# Full command string for puppet resource command -def puppet_resource_cmd - PUPPET_BINPATH + 'resource cisco_portchannel_global' -end - -def build_manifest_portchannel_global(tests, id) - manifest = tests[id][:manifest_props] - tests[id][:resource] = tests[id][:resource_props] - - tests[id][:title_pattern] = id if tests[id][:title_pattern].nil? - logger.debug( - "build_manifest_portchannel_global :: title_pattern:\n" + - tests[id][:title_pattern]) - tests[id][:manifest] = "cat <#{PUPPETMASTER_MANIFESTPATH} - node 'default' { - cisco_portchannel_global { 'default': - #{manifest} - } - } -EOF" +tests[:non_default][:manifest_props].merge!(manifest_non[:n3k]) if platform[/n3k/] +tests[:non_default][:manifest_props].merge!(manifest_non[:n56k]) if platform[/n(5|6)k/] +tests[:non_default][:manifest_props].merge!(manifest_non[:n8k]) if platform[/n8k/] +tests[:non_default][:manifest_props].merge!(manifest_non[:n9k]) if platform[/n9k/] + +def unsupported_properties(tests, _id) + unprops = [] + if platform[/n7k/] + unprops << + :concatenation << + :hash_poly << + :resilient << + :symmetry + elsif platform[/n(5|6)k/] + unprops << + :asymmetric << + :concatenation << + :hash_distribution << + :load_defer << + :resilient << + :rotate << + :symmetry + elsif platform[/n3k/] + unprops << + :asymmetric << + :concatenation << + :hash_distribution << + :hash_poly << + :load_defer << + :rotate + elsif platform[/n8k/] + unprops << + :asymmetric << + :concatenation << + :hash_distribution << + :hash_poly << + :load_defer << + :resilient << + :symmetry + elsif platform[/n9k/] + unprops << + :asymmetric << + :hash_distribution << + :hash_poly << + :load_defer + end + unprops << :resilient << :symmetry if + tests[:resilient_unsupported] + unprops end -def test_harness_portchannel_global(tests, id) - tests[id][:resource_cmd] = puppet_resource_cmd - - # Build the manifest for this test - build_manifest_portchannel_global(tests, id) - - # test_harness_common(tests, id) - test_manifest(tests, id) - test_resource(tests, id) - test_idempotence(tests, id) - - tests[id][:ensure] = nil +if platform[/n3k/] + tests[:resilient_unsupported] = + resource_probe(agent, + 'cisco_portchannel_global default resilient=true', + 'ERROR: This feature is not supported on this platform.') end ################################################################# # TEST CASE EXECUTION ################################################################# -test_name "TestCase :: #{testheader}" do +test_name "TestCase :: #{tests[:resource_name]}" do # ------------------------------------------------------------------- device = platform - logger.info("#### This device is of type: #{device} #####") - logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") - - case device - when /n7k/ - id = 'default_properties_asym' - when /n5k|n6k/ - id = 'default_properties_eth' - when /n9k/ - id = 'default_properties_sym' - when /n8k/ - id = 'default_properties_no_hash' - when /n3k/ - id = 'default_properties_no_rotate' + teardown do + test_manifest(tests, :default) end + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") - tests[id][:desc] = '1.1 Default Properties' - test_harness_portchannel_global(tests, id) + test_harness_run(tests, :default) # no absent test for portchannel_global # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + id = :non_default + test_harness_run(tests, id) + mhash = tests[id][:manifest_props] + rhash = tests[id][:resource] if device == 'n7k' - id = 'non_default_properties_asym' - tests[id][:desc] = '2.1 Non Default Properties' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.2 Non Default Properties' - tests['non_default_properties_asym'][:manifest_props]['ip-l4port'] = 'ip-l4port-vlan' - tests['non_default_properties_asym'][:resource_props]['bundle_hash'] = 'ip-l4port-vlan' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.3 Non Default Properties' - tests['non_default_properties_asym'][:manifest_props]['ip-l4port-vlan'] = 'ip-vlan' - tests['non_default_properties_asym'][:resource_props]['bundle_hash'] = 'ip-vlan' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.4 Non Default Properties' - tests['non_default_properties_asym'][:manifest_props]['ip-vlan'] = 'l4port' - tests['non_default_properties_asym'][:resource_props]['bundle_hash'] = 'l4port' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.5 Non Default Properties' - tests['non_default_properties_asym'][:manifest_props]['l4port'] = 'mac' - tests['non_default_properties_asym'][:resource_props]['bundle_hash'] = 'mac' - tests['non_default_properties_asym'][:manifest_props]['dst'] = 'src' - tests['non_default_properties_asym'][:resource_props]['bundle_select'] = 'src' - test_harness_portchannel_global(tests, id) + tests[id][:desc] = '2.2 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'ip-l4port-vlan' + test_harness_run(tests, id) + + tests[id][:desc] = '2.3 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'ip-vlan' + test_harness_run(tests, id) + + tests[id][:desc] = '2.4 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'l4port' + test_harness_run(tests, id) + + tests[id][:desc] = '2.5 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'mac' + mhash[:bundle_select] = rhash[:bundle_select] = 'src' + test_harness_run(tests, id) elsif device == 'n5k' || device == 'n6k' - id = 'non_default_properties_eth' - tests[id][:desc] = '2.1 Non Default Properties' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.2 Non Default Properties' - tests['non_default_properties_eth'][:manifest_props]['mac'] = 'port' - tests['non_default_properties_eth'][:resource_props]['bundle_hash'] = 'port' - tests['non_default_properties_eth'][:manifest_props]['dst'] = 'src' - tests['non_default_properties_eth'][:resource_props]['bundle_select'] = 'src' - tests['non_default_properties_eth'][:manifest_props]['CRC10c'] = 'CRC10a' if device == 'n6k' - tests['non_default_properties_eth'][:resource_props]['hash_poly'] = 'CRC10a' if device == 'n6k' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.3 Non Default Properties' - tests['non_default_properties_eth'][:manifest_props]['port'] = 'port-only' - tests['non_default_properties_eth'][:resource_props]['bundle_hash'] = 'port-only' - tests['non_default_properties_eth'][:manifest_props]['src'] = 'src-dst' - tests['non_default_properties_eth'][:resource_props]['bundle_select'] = 'src-dst' - tests['non_default_properties_eth'][:manifest_props]['CRC10a'] = 'CRC10d' if device == 'n6k' - tests['non_default_properties_eth'][:manifest_props]['CRC10c'] = 'CRC10d' if device == 'n5k' - tests['non_default_properties_eth'][:resource_props]['hash_poly'] = 'CRC10d' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.4 Non Default Properties' - tests['non_default_properties_eth'][:manifest_props]['port-only'] = 'ip-only' - tests['non_default_properties_eth'][:resource_props]['bundle_hash'] = 'ip-only' - test_harness_portchannel_global(tests, id) + tests[id][:desc] = '2.2 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'port' + mhash[:bundle_select] = rhash[:bundle_select] = 'src' + mhash[:hash_poly] = rhash[:hash_poly] = 'CRC10a' if device == 'n6k' + test_harness_run(tests, id) + + tests[id][:desc] = '2.3 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'port-only' + mhash[:bundle_select] = rhash[:bundle_select] = 'src-dst' + mhash[:hash_poly] = rhash[:hash_poly] = 'CRC10d' + test_harness_run(tests, id) + + tests[id][:desc] = '2.4 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'ip-only' + test_harness_run(tests, id) elsif device == 'n9k' - id = 'non_default_properties_sym' - tests[id][:desc] = '2.1 Non Default Properties' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.2 Non Default Properties' - tests['non_default_properties_sym'][:manifest_props]['true'] = 'false' - tests['non_default_properties_sym'][:manifest_props]['true'] = 'false' - tests['non_default_properties_sym'][:manifest_props]['true'] = 'false' - tests['non_default_properties_sym'][:manifest_props]['4'] = '0' - tests['non_default_properties_sym'][:manifest_props]['ip'] = 'ip-l4port-vlan' - tests['non_default_properties_sym'][:resource_props]['bundle_hash'] = 'ip-l4port-vlan' - tests['non_default_properties_sym'][:manifest_props]['src-dst'] = 'src' - tests['non_default_properties_sym'][:resource_props]['bundle_select'] = 'src' - tests['non_default_properties_sym'][:resource_props]['concatenation'] = 'false' - tests['non_default_properties_sym'][:resource_props]['resilient'] = 'false' - tests['non_default_properties_sym'][:resource_props]['symmetry'] = 'false' - tests['non_default_properties_sym'][:resource_props]['rotate'] = '0' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.3 Non Default Properties' - tests['non_default_properties_sym'][:manifest_props]['ip-l4port-vlan'] = 'ip-vlan' - tests['non_default_properties_sym'][:resource_props]['bundle_hash'] = 'ip-vlan' - tests['non_default_properties_sym'][:manifest_props]['src'] = 'dst' - tests['non_default_properties_sym'][:resource_props]['bundle_select'] = 'dst' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.4 Non Default Properties' - tests['non_default_properties_sym'][:manifest_props]['ip-vlan'] = 'l4port' - tests['non_default_properties_sym'][:resource_props]['bundle_hash'] = 'l4port' - tests['non_default_properties_sym'][:manifest_props]['dst'] = 'src-dst' - tests['non_default_properties_sym'][:resource_props]['bundle_select'] = 'src-dst' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.5 Non Default Properties' - tests['non_default_properties_sym'][:manifest_props]['l4port'] = 'mac' - tests['non_default_properties_sym'][:resource_props]['bundle_hash'] = 'mac' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.6 Non Default Properties' - tests['non_default_properties_sym'][:manifest_props]['mac'] = 'ip-gre' - tests['non_default_properties_sym'][:resource_props]['bundle_hash'] = 'ip-gre' - test_harness_portchannel_global(tests, id) + tests[id][:desc] = '2.2 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'ip-l4port-vlan' + mhash[:bundle_select] = rhash[:bundle_select] = 'src' + mhash[:rotate] = rhash[:rotate] = '0' + mhash[:symmetry] = rhash[:symmetry] = 'false' + mhash[:concatenation] = rhash[:concatenation] = 'false' + mhash[:resilient] = rhash[:resilient] = 'false' + test_harness_run(tests, id) + + tests[id][:desc] = '2.3 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'ip-vlan' + mhash[:bundle_select] = rhash[:bundle_select] = 'dst' + test_harness_run(tests, id) + + tests[id][:desc] = '2.4 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'l4port' + mhash[:bundle_select] = rhash[:bundle_select] = 'src-dst' + test_harness_run(tests, id) + + tests[id][:desc] = '2.5 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'mac' + test_harness_run(tests, id) + + tests[id][:desc] = '2.6 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'ip-gre' + test_harness_run(tests, id) elsif device == 'n8k' - id = 'non_default_properties_no_hash' - tests[id][:desc] = '2.1 Non Default Properties' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.2 Non Default Properties' - tests['non_default_properties_no_hash'][:manifest_props]['ip'] = 'ip-l4port-vlan' - tests['non_default_properties_no_hash'][:resource_props]['bundle_hash'] = 'ip-l4port-vlan' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.3 Non Default Properties' - tests['non_default_properties_no_hash'][:manifest_props]['ip-l4port-vlan'] = 'ip-vlan' - tests['non_default_properties_no_hash'][:resource_props]['bundle_hash'] = 'ip-vlan' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.4 Non Default Properties' - tests['non_default_properties_no_hash'][:manifest_props]['ip-vlan'] = 'l4port' - tests['non_default_properties_no_hash'][:resource_props]['bundle_hash'] = 'l4port' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.5 Non Default Properties' - tests['non_default_properties_no_hash'][:manifest_props]['l4port'] = 'mac' - tests['non_default_properties_no_hash'][:resource_props]['bundle_hash'] = 'mac' - tests['non_default_properties_no_hash'][:manifest_props]['dst'] = 'src' - tests['non_default_properties_no_hash'][:resource_props]['bundle_select'] = 'src' - test_harness_portchannel_global(tests, id) + tests[id][:desc] = '2.2 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'ip-l4port-vlan' + test_harness_run(tests, id) + + tests[id][:desc] = '2.3 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'ip-vlan' + test_harness_run(tests, id) + + tests[id][:desc] = '2.4 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'l4port' + test_harness_run(tests, id) + + tests[id][:desc] = '2.5 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'mac' + mhash[:bundle_select] = rhash[:bundle_select] = 'src' + test_harness_run(tests, id) + elsif device == 'n3k' - id = 'non_default_properties_no_rotate' - tests[id][:desc] = '2.1 Non Default Properties' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.2 Non Default Properties' - tests['non_default_properties_no_rotate'][:manifest_props]['true'] = 'false' - tests['non_default_properties_no_rotate'][:manifest_props]['false'] = 'true' - tests['non_default_properties_no_rotate'][:manifest_props]['ip-only'] = 'ip-gre' - tests['non_default_properties_no_rotate'][:resource_props]['bundle_hash'] = 'ip-gre' - tests['non_default_properties_no_rotate'][:manifest_props]['src-dst'] = 'src' - tests['non_default_properties_no_rotate'][:resource_props]['bundle_select'] = 'src' - tests['non_default_properties_no_rotate'][:resource_props]['resilient'] = 'true' - tests['non_default_properties_no_rotate'][:resource_props]['symmetry'] = 'false' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.3 Non Default Properties' - tests['non_default_properties_no_rotate'][:manifest_props]['ip-gre'] = 'mac' - tests['non_default_properties_no_rotate'][:resource_props]['bundle_hash'] = 'mac' - tests['non_default_properties_no_rotate'][:manifest_props]['src'] = 'dst' - tests['non_default_properties_no_rotate'][:resource_props]['bundle_select'] = 'dst' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.4 Non Default Properties' - tests['non_default_properties_no_rotate'][:manifest_props]['mac'] = 'port' - tests['non_default_properties_no_rotate'][:resource_props]['bundle_hash'] = 'port' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.5 Non Default Properties' - tests['non_default_properties_no_rotate'][:manifest_props]['port'] = 'port-only' - tests['non_default_properties_no_rotate'][:resource_props]['bundle_hash'] = 'port-only' - tests['non_default_properties_no_rotate'][:manifest_props]['dst'] = 'src-dst' - tests['non_default_properties_no_rotate'][:resource_props]['bundle_select'] = 'src-dst' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.6 Non Default Properties' - tests['non_default_properties_no_rotate'][:manifest_props]['port-only'] = 'ip-gre' - tests['non_default_properties_no_rotate'][:resource_props]['bundle_hash'] = 'ip-gre' - test_harness_portchannel_global(tests, id) - - tests[id][:desc] = '2.7 Non Default Properties' - tests['non_default_properties_no_rotate'][:manifest_props]['src-dst'] = 'dst' - tests['non_default_properties_no_rotate'][:resource_props]['bundle_select'] = 'dst' - test_harness_portchannel_global(tests, id) + if tests[:resilient_unsupported] + tests[id][:desc] = '2.2 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'ip-gre' + mhash[:bundle_select] = rhash[:bundle_select] = 'src' + test_harness_run(tests, id) + + tests[id][:desc] = '2.3 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'mac' + mhash[:bundle_select] = rhash[:bundle_select] = 'dst' + test_harness_run(tests, id) + + tests[id][:desc] = '2.4 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'port' + test_harness_run(tests, id) + + tests[id][:desc] = '2.5 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'port-only' + mhash[:bundle_select] = rhash[:bundle_select] = 'src-dst' + test_harness_run(tests, id) + + tests[id][:desc] = '2.6 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'ip-gre' + test_harness_run(tests, id) + + tests[id][:desc] = '2.7 Non Defaults' + mhash[:bundle_select] = rhash[:bundle_select] = 'dst' + test_harness_run(tests, id) + else + tests[id][:desc] = '2.2 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'ip-gre' + mhash[:bundle_select] = rhash[:bundle_select] = 'src' + mhash[:resilient] = rhash[:resilient] = 'true' + mhash[:symmetry] = rhash[:symmetry] = 'false' + test_harness_run(tests, id) + + tests[id][:desc] = '2.3 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'mac' + mhash[:bundle_select] = rhash[:bundle_select] = 'dst' + test_harness_run(tests, id) + + tests[id][:desc] = '2.4 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'port' + test_harness_run(tests, id) + + tests[id][:desc] = '2.5 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'port-only' + mhash[:bundle_select] = rhash[:bundle_select] = 'src-dst' + test_harness_run(tests, id) + + tests[id][:desc] = '2.6 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'ip-gre' + test_harness_run(tests, id) + + tests[id][:desc] = '2.7 Non Defaults' + mhash[:bundle_select] = rhash[:bundle_select] = 'dst' + test_harness_run(tests, id) + end end # no absent test for portchannel_global end -logger.info("TestCase :: #{testheader} :: End") +logger.info("TestCase :: #{tests[:resource_name]} :: End") From 0328eee4bdba03565c454daa8bb22356a7b9662a Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Fri, 19 Aug 2016 17:17:16 -0400 Subject: [PATCH 059/203] setup/teardown for bfd_global --- .../beaker_tests/cisco_bfd_global/test_bfd_global.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb b/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb index 1132ceba8..a941f033a 100644 --- a/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb +++ b/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb @@ -43,7 +43,6 @@ tests[:default] = { desc: '1.1 Defaults', title_pattern: 'default', - preclean: 'cisco_bfd_global', manifest_props: { echo_interface: 'default', echo_rx_interval: 'default', @@ -158,17 +157,17 @@ def test_harness_dependencies(_tests, id) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - # ------------------------------------------------------------------- + teardown { resource_absent_cleanup(agent, 'cisco_bfd_global') } + resource_absent_cleanup(agent, 'cisco_bfd_global') + device = platform logger.info("#### This device is of type: #{device} #####") + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") - test_harness_run(tests, :default) + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") - test_harness_run(tests, :non_default) - resource_absent_cleanup(agent, 'cisco_bfd_global') end - logger.info("TestCase :: #{tests[:resource_name]} :: End") From 011369794ab5d2b666c1bf8e569eb59bbb343ed3 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Fri, 19 Aug 2016 17:26:15 -0400 Subject: [PATCH 060/203] setup/teardown bridge_domain --- .../cisco_bridge_domain/test_bridge_domain.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/beaker_tests/cisco_bridge_domain/test_bridge_domain.rb b/tests/beaker_tests/cisco_bridge_domain/test_bridge_domain.rb index cbb9f5035..a11e0783b 100644 --- a/tests/beaker_tests/cisco_bridge_domain/test_bridge_domain.rb +++ b/tests/beaker_tests/cisco_bridge_domain/test_bridge_domain.rb @@ -33,20 +33,20 @@ skip_unless_supported(tests) tests[:non_default] = { - desc: "1.1 Non Default Properties 'change name, state and fabric_control", + desc: "1.1 Non Default 'change name, state and fabric_control", title_pattern: '100', manifest_props: { - bd_name: 'PepsiCo', + bd_name: 'my_bd', fabric_control: true, shutdown: true, }, } tests[:non_default_change_state] = { - desc: "1.2 Non Default Properties 'change state of previous bridge-domain'", + desc: "1.2 Non Default 'change state of previous bridge-domain'", title_pattern: '100', manifest_props: { - bd_name: 'PepsiCo', + bd_name: 'my_bd', fabric_control: true, shutdown: false, }, @@ -56,11 +56,12 @@ # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{testheader}" do + teardown { remove_all_vlans(agent) } + remove_all_vlans(agent) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Non Default Property Testing") test_harness_run(tests, :non_default) test_harness_run(tests, :non_default_change_state) - resource_absent_cleanup(agent, 'cisco_bridge_domain', 'bridge-domain CLEANUP :: ') end logger.info('TestCase :: # {testheader} :: End') From f8ff399ab31f7b61e05ea84c1bb20cd473bb6c7d Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Fri, 19 Aug 2016 17:31:15 -0400 Subject: [PATCH 061/203] setup/teardown acl --- tests/beaker_tests/cisco_acl/test_ace.rb | 7 ++++--- tests/beaker_tests/cisco_acl/test_acl.rb | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/beaker_tests/cisco_acl/test_ace.rb b/tests/beaker_tests/cisco_acl/test_ace.rb index b47ee0ff3..9dcf523dd 100644 --- a/tests/beaker_tests/cisco_acl/test_ace.rb +++ b/tests/beaker_tests/cisco_acl/test_ace.rb @@ -32,7 +32,6 @@ # Test hash test cases tests[:seq_5_remark] = { - preclean: 'cisco_acl', title_pattern: 'ipv4 beaker 5', manifest_props: { # 'remark' is a standalone property @@ -139,6 +138,10 @@ def unsupported_properties(tests, id) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown { resource_absent_cleanup(agent, 'cisco_acl') } + resource_absent_cleanup(agent, 'cisco_acl') + + # --------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. ACE Testing") test_harness_run(tests, :seq_5_remark) @@ -149,8 +152,6 @@ def unsupported_properties(tests, id) test_harness_run(tests, :seq_30_v4) # --------------------------------------------------------- - resource_absent_cleanup(agent, 'cisco_acl', 'ACL CLEANUP :: ') skipped_tests_summary(tests) end - logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_acl/test_acl.rb b/tests/beaker_tests/cisco_acl/test_acl.rb index cf647b38b..80ff6a6e9 100644 --- a/tests/beaker_tests/cisco_acl/test_acl.rb +++ b/tests/beaker_tests/cisco_acl/test_acl.rb @@ -34,7 +34,6 @@ # Test hash test cases tests[:default] = { desc: '1.1 Default tests', - preclean: 'cisco_acl', title_pattern: 'ipv4 beaker', manifest_props: { fragments: 'default' @@ -94,6 +93,9 @@ def unsupported_properties(_tests, _id) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown { resource_absent_cleanup(agent, 'cisco_acl') } + resource_absent_cleanup(agent, 'cisco_acl') + # ------------------------------------ logger.info("\n#{'-' * 60}\nSection 1. ACL Testing") id = :default @@ -106,16 +108,14 @@ def unsupported_properties(_tests, _id) tests[id][:title_pattern] = 'ipv6 beaker6' test_harness_run(tests, id) + resource_absent_cleanup(agent, 'cisco_acl') # ------------------------------------ - resource_absent_cleanup(agent, 'cisco_acl', 'ACL CLEANUP :: ') logger.info("\n#{'-' * 60}\nSection 2. Title Pattern Testing") test_harness_run(tests, :title_patterns_1) test_harness_run(tests, :title_patterns_2) test_harness_run(tests, :title_patterns_3) # --------------------------------------------------------- - resource_absent_cleanup(agent, 'cisco_acl', 'ACL CLEANUP :: ') skipped_tests_summary(tests) end - logger.info("TestCase :: #{tests[:resource_name]} :: End") From 38cbd4e58845aac32f44d89a0a1cb7deca2ff70d Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 07:58:34 -0400 Subject: [PATCH 062/203] setup/teardown bgp_af --- tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb b/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb index ef40938b4..276a0c851 100644 --- a/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb +++ b/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb @@ -34,7 +34,6 @@ # Test hash test cases tests[:default] = { desc: '1.1 Default Properties', - preclean: 'cisco_bgp', title_pattern: '2 default ipv4 unicast', manifest_props: { additional_paths_install: 'default', @@ -285,14 +284,15 @@ def test_harness_bgp_af_run(tests, id) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + teardown { resource_absent_cleanup(agent, 'cisco_bgp') } + resource_absent_cleanup(agent, 'cisco_bgp') # ----------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") id = :default test_harness_run(tests, id) tests[id][:ensure] = :absent - tests[id].delete(:preclean) test_harness_run(tests, id) test_harness_run(tests, :default_dampening_routemap) @@ -308,8 +308,5 @@ def test_harness_bgp_af_run(tests, id) test_harness_run(tests, :title_patterns_2) test_harness_run(tests, :title_patterns_3) test_harness_run(tests, :l2vpn_evpn) unless nexus_i2_image - - # ----------------------------------- - resource_absent_cleanup(agent, 'cisco_bgp') end logger.info("TestCase :: #{tests[:resource_name]} :: End") From 03e97a9097cfb66545e677022684ca867dbd097e Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 08:15:40 -0400 Subject: [PATCH 063/203] setup/teardown cisco_pim --- tests/beaker_tests/cisco_pim/test_pim.rb | 7 ++++--- tests/beaker_tests/cisco_pim/test_pim_grouplist.rb | 7 +++---- tests/beaker_tests/cisco_pim/test_pim_rp_address.rb | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/beaker_tests/cisco_pim/test_pim.rb b/tests/beaker_tests/cisco_pim/test_pim.rb index 4a2aae39f..56cffef0b 100644 --- a/tests/beaker_tests/cisco_pim/test_pim.rb +++ b/tests/beaker_tests/cisco_pim/test_pim.rb @@ -33,7 +33,6 @@ # Test hash test cases tests[:non_def_S1] = { desc: ' 1.1 Non default properties', - preclean: 'cisco_pim', title_pattern: 'ipv4 red', manifest_props: { ssm_range: '224.0.0.0/8' @@ -75,15 +74,17 @@ # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown { resource_absent_cleanup(agent, 'cisco_pim') } + resource_absent_cleanup(agent, 'cisco_pim') + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Non Default Property Testing") test_harness_run(tests, :non_def_S1) test_harness_run(tests, :non_def_S2) + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Title Pattern Testing") test_harness_run(tests, :title_patterns_1) test_harness_run(tests, :title_patterns_2) - # ----------------------------------- - resource_absent_cleanup(agent, 'cisco_pim') end logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_pim/test_pim_grouplist.rb b/tests/beaker_tests/cisco_pim/test_pim_grouplist.rb index 2d90d17fc..53a3c8ea3 100644 --- a/tests/beaker_tests/cisco_pim/test_pim_grouplist.rb +++ b/tests/beaker_tests/cisco_pim/test_pim_grouplist.rb @@ -33,7 +33,6 @@ # Test hash test cases tests[:title_patterns_1] = { desc: 'T.1 Title Pattern', - preclean: 'cisco_pim_grouplist', title_pattern: 'new_york', title_params: { afi: 'ipv4', vrf: 'red', rp_addr: '1.1.1.1', group: '224.0.0.0/8' }, @@ -71,6 +70,9 @@ # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown { resource_absent_cleanup(agent, 'cisco_pim_grouplist') } + resource_absent_cleanup(agent, 'cisco_pim_grouplist') + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Title Pattern Testing") test_harness_run(tests, :title_patterns_1) @@ -78,8 +80,5 @@ test_harness_run(tests, :title_patterns_3) test_harness_run(tests, :title_patterns_4) test_harness_run(tests, :title_patterns_5) - - # ----------------------------------- - resource_absent_cleanup(agent, 'cisco_pim_grouplist') end logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_pim/test_pim_rp_address.rb b/tests/beaker_tests/cisco_pim/test_pim_rp_address.rb index 0a46328cb..f19baa8c3 100644 --- a/tests/beaker_tests/cisco_pim/test_pim_rp_address.rb +++ b/tests/beaker_tests/cisco_pim/test_pim_rp_address.rb @@ -33,7 +33,6 @@ # Test hash test cases tests[:title_patterns_1] = { desc: 'T.1 Title Pattern', - preclean: 'cisco_pim_rp_address', title_pattern: 'new_york', title_params: { afi: 'ipv4', vrf: 'red', rp_addr: '1.1.1.1' }, resource: { 'ensure' => 'present' }, @@ -63,13 +62,14 @@ # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown { resource_absent_cleanup(agent, 'cisco_pim_rp_address') } + resource_absent_cleanup(agent, 'cisco_pim_rp_address') + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Title Pattern Testing") test_harness_run(tests, :title_patterns_1) test_harness_run(tests, :title_patterns_2) test_harness_run(tests, :title_patterns_3) test_harness_run(tests, :title_patterns_4) - # ------------------------------------------------------------------- - resource_absent_cleanup(agent, 'cisco_pim_rp_address') end logger.info("TestCase :: #{tests[:resource_name]} :: End") From 8ca5cf0893abcbf15825364fec4def024b21b34c Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 09:08:47 -0400 Subject: [PATCH 064/203] Convert test_fabricpath_global to new syntax --- .../test_fabricpath_global.rb | 311 +++++------------- 1 file changed, 78 insertions(+), 233 deletions(-) diff --git a/tests/beaker_tests/cisco_fabricpath_global/test_fabricpath_global.rb b/tests/beaker_tests/cisco_fabricpath_global/test_fabricpath_global.rb index 47c9ab907..2b8d67ba9 100644 --- a/tests/beaker_tests/cisco_fabricpath_global/test_fabricpath_global.rb +++ b/tests/beaker_tests/cisco_fabricpath_global/test_fabricpath_global.rb @@ -13,103 +13,38 @@ # See the License for the specific language governing permissions and # limitations under the License. ############################################################################### -# TestCase Name: -# ------------- -# test-fabricpath_global.rb # -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet fabricpath_global resource testcase for Puppet Agent on -# Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# - Host configuration file contains agent and master information. -# - SSH is enabled on the N9K Agent. -# - Puppet master/server is started. -# - Puppet agent certificate has been signed on the Puppet master/server. -# -# TestCase: -# --------- -# This Tunnel resource test verifies default values for all properties. -# -# The following exit_codes are validated for Puppet, Vegas shell and -# Bash shell commands. -# -# Vegas and Bash Shell Commands: -# 0 - successful command execution -# > 0 - failed command execution. -# -# Puppet Commands: -# 0 - no changes have occurred -# 1 - errors have occurred, -# 2 - changes have occurred -# 4 - failures have occurred and -# 6 - changes and failures have occurred. -# -# NOTE: 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# -# The test cases use RegExp pattern matching on stdout or output IO -# instance attributes to verify resource properties. +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage # ############################################################################### - require File.expand_path('../../lib/utilitylib.rb', __FILE__) -# ----------------------------- -# Common settings and variables -# ----------------------------- -testheader = 'Resource cisco_fabricpath_global' - -# The 'tests' hash is used to define all of the test data values and expected -# results. It is also used to pass optional flags to the test methods when -# necessary. - -# 'tests' hash -# Top-level keys set by caller: -# tests[:master] - the master object -# tests[:agent] - the agent object -# tests = { - master: master, - agent: agent, - platform: 'n(5|6|7)k', + agent: agent, + master: master, + platform: 'n(5|6|7)k', + resource_name: 'cisco_fabricpath_global', } -# tests[id] keys set by caller and used by test_harness_common: -# -# tests[id] keys set by caller: -# tests[id][:platform] - a regexp pattern to match against supported platforms. -# This key overrides a tests[:platform] key -# tests[id][:desc] - a string to use with logs & debugs -# tests[id][:manifest] - the complete manifest, as used by test_harness_common -# tests[id][:resource] - a hash of expected states, used by test_resource -# tests[id][:resource_cmd] - 'puppet resource' command to use with test_resource -# tests[id][:ensure] - (Optional) set to :present or :absent before calling -# tests[id][:code] - (Optional) override the default exit code in some tests. -# -# These keys are local use only and not used by test_harness_common: -# -# tests[id][:manifest_props] - This is essentially a master list of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the list -# tests[id][:resource_props] - This is essentially a master hash of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the hash -# tests[id][:title_pattern] - (Optional) defines the manifest title. -# Can be used with :af for mixed title/af testing. If mixing, :af values will -# be merged with title values and override any duplicates. If omitted, -# :title_pattern will be set to 'id'. -# -tests['default_properties'] = { +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + +tests[:default] = { + desc: '1.1 Default Properties', title_pattern: 'default', - manifest_props: " - allocate_delay => 'default', - graceful_merge => 'default', - linkup_delay => 'default', - loadbalance_unicast_layer => 'default', - loadbalance_unicast_has_vlan => 'default', - transition_delay => 'default', - ", - resource_props: { + manifest_props: { + allocate_delay: 'default', + graceful_merge: 'default', + linkup_delay: 'default', + loadbalance_unicast_layer: 'default', + loadbalance_unicast_has_vlan: 'default', + transition_delay: 'default', + }, + resource: { 'allocate_delay' => '10', 'graceful_merge' => 'enable', 'linkup_delay' => '10', @@ -119,20 +54,21 @@ }, } -tests['default_properties_exclusive'] = { +tests[:default_exclusive] = { + desc: '1.2 Default Properties (Exclusive)', title_pattern: 'default', platform: 'n7k', - manifest_props: " - aggregate_multicast_routes => 'default', - linkup_delay_always => 'default', - linkup_delay_enable => 'default', - loadbalance_algorithm => 'default', - loadbalance_multicast_has_vlan => 'default', - mode => 'default', - ttl_multicast => 'default', - ttl_unicast => 'default', - ", - resource_props: { + manifest_props: { + aggregate_multicast_routes: 'default', + linkup_delay_always: 'default', + linkup_delay_enable: 'default', + loadbalance_algorithm: 'default', + loadbalance_multicast_has_vlan: 'default', + mode: 'default', + ttl_multicast: 'default', + ttl_unicast: 'default', + }, + resource: { 'aggregate_multicast_routes' => 'false', 'linkup_delay_always' => 'false', 'linkup_delay_enable' => 'true', @@ -144,158 +80,67 @@ }, } -tests['non_default_properties'] = { +tests[:non_default] = { + desc: '2.1 Non Default Properties', title_pattern: 'default', - manifest_props: " - allocate_delay => '30', - graceful_merge => 'disable', - linkup_delay => '20', - loadbalance_algorithm => 'source', - loadbalance_unicast_layer => 'layer4', - loadbalance_unicast_has_vlan => 'true', - switch_id => '100', - transition_delay => '25', - ", - resource_props: { - 'allocate_delay' => '30', - 'graceful_merge' => 'disable', - 'linkup_delay' => '20', - 'loadbalance_algorithm' => 'source', - 'loadbalance_unicast_layer' => 'layer4', - 'loadbalance_unicast_has_vlan' => 'true', - 'switch_id' => '100', - 'transition_delay' => '25', + manifest_props: { + allocate_delay: '30', + graceful_merge: 'disable', + linkup_delay: '20', + loadbalance_algorithm: 'source', + loadbalance_unicast_layer: 'layer4', + loadbalance_unicast_has_vlan: 'true', + switch_id: '100', + transition_delay: '25', }, } -tests['non_default_properties_exclusive'] = { +tests[:non_default_exclusive] = { + desc: '2.2 Non Default Properties (Exclusive)', title_pattern: 'default', platform: 'n7k', - manifest_props: " - aggregate_multicast_routes => 'true', - linkup_delay_always => 'false', - linkup_delay_enable => 'false', - loadbalance_multicast_rotate => '3', - loadbalance_multicast_has_vlan => 'true', - loadbalance_unicast_rotate => '5', - ttl_multicast => '20', - ttl_unicast => '20', - ", - resource_props: { - 'aggregate_multicast_routes' => 'true', - 'linkup_delay_always' => 'false', - 'linkup_delay_enable' => 'false', - 'loadbalance_multicast_rotate' => '3', - 'loadbalance_multicast_has_vlan' => 'true', - 'loadbalance_unicast_rotate' => '5', - 'ttl_multicast' => '20', - 'ttl_unicast' => '20', + manifest_props: { + aggregate_multicast_routes: 'true', + linkup_delay_always: 'false', + linkup_delay_enable: 'false', + loadbalance_multicast_rotate: '3', + loadbalance_multicast_has_vlan: 'true', + loadbalance_unicast_rotate: '5', + ttl_multicast: '20', + ttl_unicast: '20', }, } -################################################################# -# HELPER FUNCTIONS -################################################################# - -# Full command string for puppet resource command -def puppet_resource_cmd - PUPPET_BINPATH + 'resource cisco_fabricpath_global' -end - -def build_manifest_fabricpath_global(tests, id) - if tests[id][:ensure] == :absent - state = 'ensure => absent,' - tests[id][:resource] = {} - else - state = 'ensure => present,' - manifest = tests[id][:manifest_props] - tests[id][:resource] = tests[id][:resource_props] - end - - tests[id][:title_pattern] = id if tests[id][:title_pattern].nil? - logger.debug("build_manifest_fabricpath_global :: title_pattern:\n" + - tests[id][:title_pattern]) - tests[id][:manifest] = "cat <#{PUPPETMASTER_MANIFESTPATH} - node 'default' { - cisco_fabricpath_global { '#{tests[id][:title_pattern]}': - #{state} - #{manifest} - } - } -EOF" -end - -def test_harness_fabricpath_global(tests, id) - return unless platform_supports_test(tests, id) - - tests[id][:resource_cmd] = puppet_resource_cmd - - # Build the manifest for this test - build_manifest_fabricpath_global(tests, id) - - test_harness_common(tests, id) - - tests[id][:ensure] = nil -end - def testbed_cleanup(agent) - logger.info("\n* testbed cleanup") cmds = ['feature nv overlay', 'feature-set fabricpath'] config_find_remove(agent, cmds, 'incl ^feature') - remove_all_vlans(agent) end ################################################################# # TEST CASE EXECUTION ################################################################# -test_name "TestCase :: #{testheader}" do +test_name "TestCase :: #{tests[:resource_name]}" do + teardown do + testbed_cleanup(agent) + vdc_limit_f3_no_intf_needed(:clear) + end + vdc_limit_f3_no_intf_needed(:set) testbed_cleanup(agent) - logger.info("\n#{'-' * 60}\nSection 0. Testbed Initialization") - setup_fabricpath_env(tests, self) - + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") - - id = 'default_properties' - tests[id][:desc] = '1.1 Default Properties' - test_harness_fabricpath_global(tests, id) - - tests[id][:desc] = '1.2 Default Properties (absent)' - tests[id][:ensure] = :absent - test_harness_fabricpath_global(tests, id) - - logger.info("\n#{'-' * 60}\nSection 2. Default Property Testing exclusive") - - id = 'default_properties_exclusive' - tests[id][:desc] = '2.1 Default Properties exclusive to Platform' - test_harness_fabricpath_global(tests, id) - - tests[id][:desc] = '2.2 Default Properties exclusive to Platform (absent)' - tests[id][:ensure] = :absent - test_harness_fabricpath_global(tests, id) - - logger.info("\n#{'-' * 60}\nSection 3. Non Default Property Testing") - - id = 'non_default_properties' - tests[id][:desc] = '3.1 Non Default Properties' - test_harness_fabricpath_global(tests, id) - - tests[id][:desc] = '3.2 Non Default Properties (absent)' - tests[id][:ensure] = :absent - test_harness_fabricpath_global(tests, id) - - logger.info("\n#{'-' * 60}\nSection 4. Non Default Property Testing excl") - - id = 'default_properties_exclusive' - tests[id][:desc] = '4.1 Non Default Properties exclusive to Platform' - test_harness_fabricpath_global(tests, id) - - tests[id][:desc] = '4.2 Non Default Properties exclusive to Platform (abs)' - tests[id][:ensure] = :absent - test_harness_fabricpath_global(tests, id) - - testbed_cleanup(agent) + test_harness_run(tests, :default) + tests[:default][:ensure] = :absent + test_harness_run(tests, :default) + + test_harness_run(tests, :default_exclusive) + tests[:default_exclusive][:ensure] = :absent + test_harness_run(tests, :default_exclusive) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + test_harness_run(tests, :non_default) + test_harness_run(tests, :non_default_exclusive) end - -logger.info('TestCase :: # {testheader} :: End') +logger.info("TestCase :: #{tests[:resource_name]} :: End") From 12b55ae7721edebbff5d542e75ff81b4eaf0080c Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 09:11:10 -0400 Subject: [PATCH 065/203] Setup/teardown test_interface_L3 --- tests/beaker_tests/cisco_interface/test_interface_L3.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/beaker_tests/cisco_interface/test_interface_L3.rb b/tests/beaker_tests/cisco_interface/test_interface_L3.rb index 8323a4421..4c81b1ad9 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_L3.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_L3.rb @@ -45,7 +45,6 @@ desc: '1.1 Default Properties', title_pattern: intf, code: [0], - preclean_intf: true, sys_def_switchport: false, manifest_props: { bfd_echo: 'default', @@ -162,6 +161,9 @@ def dependency_manifest(_tests, id) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown { interface_cleanup(agent, intf) } + interface_cleanup(agent, intf) + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") test_harness_run(tests, :default) @@ -176,7 +178,6 @@ def dependency_manifest(_tests, id) test_harness_run(tests, :ip_forwarding) # ------------------------------------------------------------------- - interface_cleanup(agent, intf) skipped_tests_summary(tests) end From ed686ad0963d14685cbf871a518f1a160ebe8cec Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 09:19:39 -0400 Subject: [PATCH 066/203] setup/teardown test_interface_private_vlan --- .../cisco_interface/test_interface_private_vlan.rb | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb b/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb index 67643a13c..079505727 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb @@ -48,7 +48,6 @@ desc: '1.1 Default Properties', title_pattern: intf, code: [0], - preclean_intf: true, sys_def_switchport: true, manifest_props: { switchport_pvlan_host: 'default', @@ -192,8 +191,17 @@ def dependency_manifest(tests, id) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown do + vtp_cleanup(agent) + pvlan_assoc_cleanup(agent, intf) + interface_cleanup(agent, intf) + remove_interface(agent, svi) + end vtp_cleanup(agent) pvlan_assoc_cleanup(agent, intf) + interface_cleanup(agent, intf) + remove_interface(agent, svi) + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Defaults") test_harness_run(tests, :default) @@ -214,9 +222,6 @@ def dependency_manifest(tests, id) test_harness_run(tests, :svi_mapping) # ------------------------------------------------------------------- - pvlan_assoc_cleanup(agent, intf) - interface_cleanup(agent, intf) - remove_interface(agent, svi) skipped_tests_summary(tests) end From 564c517dc730fc6692ee7536c50d44d3338d62eb Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 09:41:12 -0400 Subject: [PATCH 067/203] Convert test_interface_channel_group to new syntax --- .../test_interface_channel_group.rb | 152 +++--------------- 1 file changed, 21 insertions(+), 131 deletions(-) diff --git a/tests/beaker_tests/cisco_interface_channel_group/test_interface_channel_group.rb b/tests/beaker_tests/cisco_interface_channel_group/test_interface_channel_group.rb index d534f5ab5..45c92e53b 100644 --- a/tests/beaker_tests/cisco_interface_channel_group/test_interface_channel_group.rb +++ b/tests/beaker_tests/cisco_interface_channel_group/test_interface_channel_group.rb @@ -13,88 +13,28 @@ # See the License for the specific language governing permissions and # limitations under the License. ############################################################################### -# TestCase Name: -# ------------- -# test_interface_channel_group.rb # -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet cisco_interface_channel_group testcase for Puppet Agent on -# Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# - Host configuration file contains agent and master information. -# - SSH is enabled on the N9K Agent. -# - Puppet master/server is started. -# - Puppet agent certificate has been signed on the Puppet master/server. -# -# The following exit_codes are validated for Puppet, Vegas shell and -# Bash shell commands. -# -# Vegas and Bash Shell Commands: -# 0 - successful command execution -# > 0 - failed command execution. -# -# Puppet Commands: -# 0 - no changes have occurred -# 1 - errors have occurred, -# 2 - changes have occurred -# 4 - failures have occurred and -# 6 - changes and failures have occurred. -# -# NOTE: 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# -# The test cases use RegExp pattern matching on stdout or output IO -# instance attributes to verify resource properties. +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage # ############################################################################### - require File.expand_path('../../lib/utilitylib.rb', __FILE__) -# ----------------------------- -# Common settings and variables -# ----------------------------- -testheader = 'Resource cisco_interface_channel_group' - -# The 'tests' hash is used to define all of the test data values and expected -# results. It is also used to pass optional flags to the test methods when -# necessary. -# 'tests' hash -# Top-level keys set by caller: -# tests[:master] - the master object -# tests[:agent] - the agent object -# tests[:platform] - a regexp pattern to match against supported platforms. -# This key can be overridden by a tests[id][:platform] key -# tests = { - master: master, - agent: agent, - intf_type: 'ethernet', + agent: agent, + master: master, + intf_type: 'ethernet', + resource_name: 'cisco_interface_channel_group', } -# tests[id] keys set by caller and used by test_harness_common: -# -# tests[id] keys set by caller: -# tests[id][:platform] - a regexp pattern to match against supported platforms. -# This key overrides a tests[:platform] key -# tests[id][:desc] - a string to use with logs & debugs -# tests[id][:manifest] - the complete manifest, as used by test_harness_common -# tests[id][:resource] - a hash of expected states, used by test_resource -# tests[id][:resource_cmd] - 'puppet resource' command to use with test_resource -# tests[id][:ensure] - (Optional) set to :present or :absent before calling -# tests[id][:code] - (Optional) override the default exit code in some tests. -# -# These keys are local use only and not used by test_harness_common: -# -# tests[id][:manifest_props] - This is essentially a master list of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the list -# tests[id][:resource_props] - This is essentially a master hash of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the hash -# tests[id][:title_pattern] - (Optional) defines the manifest title. -# -tests['default_properties'] = { +intf = find_interface(tests) + +tests[:default] = { desc: '1.1 Default Properties', + title_pattern: intf, code: [0, 2], manifest_props: { channel_group: 'default', @@ -107,8 +47,9 @@ }, } -tests['non_default_properties'] = { +tests[:non_default] = { desc: '2.1 Non Default Properties commands', + title_pattern: intf, manifest_props: { channel_group: 201, description: 'chan group desc', @@ -121,70 +62,19 @@ }, } -def find_int_channel_group_intf(tests) - if tests[:ethernet] - intf = tests[:ethernet] - else - intf = find_interface(tests) - # cache for later tests - tests[:ethernet] = intf - end - logger.info("\nUsing interface: #{intf}") - intf -end - -# Create actual manifest for a given test scenario. -def build_manifest_interface_channel_group(tests, id) - manifest = prop_hash_to_manifest(tests[id][:manifest_props]) - if tests[id][:ensure] == :absent - state = 'ensure => absent,' - tests[id][:resource] = { 'ensure' => 'absent' } - else - state = 'ensure => present,' - end - - tests[id][:title_pattern] = find_int_channel_group_intf(tests) - tests[id][:manifest] = "cat <#{PUPPETMASTER_MANIFESTPATH} - \nnode default { - cisco_interface_channel_group { '#{tests[id][:title_pattern]}': - #{state}\n#{manifest} - }\n}\nEOF" - - cmd = PUPPET_BINPATH + - "resource cisco_interface_channel_group '#{tests[id][:title_pattern]}'" - tests[id][:resource_cmd] = cmd -end - -# Wrapper for interface_channel_group specific settings prior to calling the -# common test_harness. -def test_harness_interface_channel_group(tests, id) - return unless platform_supports_test(tests, id) - - tests[id][:ensure] = :present if tests[id][:ensure].nil? - - # Build the manifest for this test - build_manifest_interface_channel_group(tests, id) - - test_harness_common(tests, id) - tests[id][:ensure] = nil -end - ################################################################# # TEST CASE EXECUTION ################################################################# -test_name "TestCase :: #{testheader}" do - interface_cleanup(agent, find_int_channel_group_intf(tests)) +test_name "TestCase :: #{tests[:resource_name]}" do + teardown { interface_cleanup(agent, intf) } + interface_cleanup(agent, intf) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") - id = 'default_properties' - test_harness_interface_channel_group(tests, id) + test_harness_run(tests, :default) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") - test_harness_interface_channel_group(tests, 'non_default_properties') - - # ------------------------------------------------------------------- - interface_cleanup(agent, find_int_channel_group_intf(tests)) + test_harness_run(tests, :non_default) end -logger.info("TestCase :: #{testheader} :: End") +logger.info("TestCase :: #{tests[:resource_name]} :: End") From 42075b4ae201965233caa1fcddb6a6b5fb1a4c78 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 09:58:31 -0400 Subject: [PATCH 068/203] Convert test_interface_portchannel to new syntax --- .../test_interface_portchannel.rb | 335 +++++------------- 1 file changed, 90 insertions(+), 245 deletions(-) diff --git a/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb b/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb index aff98885f..66acd6d1e 100755 --- a/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb +++ b/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb @@ -13,106 +13,37 @@ # See the License for the specific language governing permissions and # limitations under the License. ############################################################################### -# TestCase Name: -# ------------- -# test-interface_portchannel.rb # -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet interface_portchannel resource testcase for Puppet Agent -# on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# - Host configuration file contains agent and master information. -# - SSH is enabled on the N9K Agent. -# - Puppet master/server is started. -# - Puppet agent certificate has been signed on the Puppet master/server. -# -# TestCase: -# --------- -# This interface_portchannel resource test verifies default values for all -# properties. -# -# The following exit_codes are validated for Puppet, Vegas shell and -# Bash shell commands. -# -# Vegas and Bash Shell Commands: -# 0 - successful command execution -# > 0 - failed command execution. -# -# Puppet Commands: -# 0 - no changes have occurred -# 1 - errors have occurred, -# 2 - changes have occurred -# 4 - failures have occurred and -# 6 - changes and failures have occurred. -# -# NOTE: 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# -# The test cases use RegExp pattern matching on stdout or output IO -# instance attributes to verify resource properties. +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage # ############################################################################### - require File.expand_path('../../lib/utilitylib.rb', __FILE__) -# ----------------------------- -# Common settings and variables -# ----------------------------- -testheader = 'Resource cisco_interface_portchannel' - -# Define PUPPETMASTER_MANIFESTPATH. - -# The 'tests' hash is used to define all of the test data values and expected -# results. It is also used to pass optional flags to the test methods when -# necessary. - -# 'tests' hash -# Top-level keys set by caller: -# tests[:master] - the master object -# tests[:agent] - the agent object -# tests = { - master: master, - agent: agent, + agent: agent, + master: master, + resource_name: 'cisco_interface_portchannel', } -# tests[id] keys set by caller and used by test_harness_common: -# -# tests[id] keys set by caller: -# tests[id][:desc] - a string to use with logs & debugs -# tests[id][:manifest] - the complete manifest, as used by test_harness_common -# tests[id][:resource] - a hash of expected states, used by test_resource -# tests[id][:resource_cmd] - 'puppet resource' command to use with test_resource -# tests[id][:ensure] - (Optional) set to :present or :absent before calling -# tests[id][:code] - (Optional) override the default exit code in some tests. -# -# These keys are local use only and not used by test_harness_common: -# -# tests[id][:manifest_props] - This is essentially a master list of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the list -# tests[id][:resource_props] - This is essentially a master hash of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the hash -# tests[id][:title_pattern] - (Optional) defines the manifest title. -# Can be used with :af for mixed title/af testing. If mixing, :af values will -# be merged with title values and override any duplicates. If omitted, -# :title_pattern will be set to 'id'. -# - -tests['default_properties_asym'] = { +tests[:default_asym] = { + desc: '1.1 Default Properties (asym)', title_pattern: 'port-channel100', - manifest_props: " - bfd_per_link => 'default', - lacp_graceful_convergence => 'default', - lacp_max_bundle => 'default', - lacp_min_links => 'default', - lacp_suspend_individual => 'default', - port_hash_distribution => 'default', - port_load_defer => 'default', - ", + platform: 'n7k', code: [0, 2], - resource_props: { + manifest_props: { + bfd_per_link: 'default', + lacp_graceful_convergence: 'default', + lacp_max_bundle: 'default', + lacp_min_links: 'default', + lacp_suspend_individual: 'default', + port_hash_distribution: 'default', + port_load_defer: 'default', + }, + resource: { 'bfd_per_link' => 'false', 'lacp_graceful_convergence' => 'true', 'lacp_max_bundle' => '16', @@ -123,41 +54,36 @@ }, } -tests['non_default_properties_asym'] = { +tests[:non_default_asym] = { + desc: '2.1 Non Default Properties (asym)', + platform: 'n7k', title_pattern: 'port-channel100', - manifest_props: " - bfd_per_link => 'true', - lacp_graceful_convergence => 'false', - lacp_max_bundle => '10', - lacp_min_links => '3', - lacp_suspend_individual => 'false', - port_hash_distribution => 'fixed', - port_load_defer => 'true', - ", - resource_props: { - 'bfd_per_link' => 'true', - 'lacp_graceful_convergence' => 'false', - 'lacp_max_bundle' => '10', - 'lacp_min_links' => '3', - 'lacp_suspend_individual' => 'false', - 'port_hash_distribution' => 'fixed', - 'port_load_defer' => 'true', + manifest_props: { + bfd_per_link: 'true', + lacp_graceful_convergence: 'false', + lacp_max_bundle: '10', + lacp_min_links: '3', + lacp_suspend_individual: 'false', + port_hash_distribution: 'fixed', + port_load_defer: 'true', }, } -tests['default_properties_sym'] = { +tests[:default_sym] = { + desc: '1.2 Default Properties (sym)', title_pattern: 'port-channel100', - manifest_props: " - bfd_per_link => 'default', - lacp_graceful_convergence => 'default', - lacp_max_bundle => 'default', - lacp_min_links => 'default', - lacp_suspend_individual => 'default', - port_hash_distribution => 'default', - port_load_defer => 'default', - ", + platform: 'n(3|8|9)k', code: [0, 2], - resource_props: { + manifest_props: { + bfd_per_link: 'default', + lacp_graceful_convergence: 'default', + lacp_max_bundle: 'default', + lacp_min_links: 'default', + lacp_suspend_individual: 'default', + port_hash_distribution: 'default', + port_load_defer: 'default', + }, + resource: { 'bfd_per_link' => 'false', 'lacp_graceful_convergence' => 'true', 'lacp_max_bundle' => '32', @@ -168,39 +94,34 @@ }, } -tests['non_default_properties_sym'] = { +tests[:non_default_sym] = { + desc: '2.2 Non Default Properties (sym)', title_pattern: 'port-channel100', - manifest_props: " - bfd_per_link => 'true', - lacp_graceful_convergence => 'false', - lacp_max_bundle => '10', - lacp_min_links => '3', - lacp_suspend_individual => 'false', - port_hash_distribution => 'fixed', - port_load_defer => 'true', - ", - resource_props: { - 'bfd_per_link' => 'true', - 'lacp_graceful_convergence' => 'false', - 'lacp_max_bundle' => '10', - 'lacp_min_links' => '3', - 'lacp_suspend_individual' => 'false', - 'port_hash_distribution' => 'fixed', - 'port_load_defer' => 'true', + platform: 'n(3|8|9)k', + manifest_props: { + bfd_per_link: 'true', + lacp_graceful_convergence: 'false', + lacp_max_bundle: '10', + lacp_min_links: '3', + lacp_suspend_individual: 'false', + port_hash_distribution: 'fixed', + port_load_defer: 'true', }, } -tests['default_properties_eth'] = { +tests[:default_eth] = { + desc: '1.3 Default Properties (eth)', title_pattern: 'port-channel100', - manifest_props: " - bfd_per_link => 'default', - lacp_graceful_convergence => 'default', - lacp_max_bundle => 'default', - lacp_min_links => 'default', - lacp_suspend_individual => 'default', - ", + platform: 'n(5|6)k', code: [0, 2], - resource_props: { + manifest_props: { + bfd_per_link: 'default', + lacp_graceful_convergence: 'default', + lacp_max_bundle: 'default', + lacp_min_links: 'default', + lacp_suspend_individual: 'default', + }, + resource: { 'bfd_per_link' => 'false', 'lacp_graceful_convergence' => 'true', 'lacp_max_bundle' => '16', @@ -209,117 +130,41 @@ }, } -tests['non_default_properties_eth'] = { +tests[:non_default_eth] = { + desc: '2.3 Non Default Properties (eth)', + platform: 'n(5|6)k', title_pattern: 'port-channel100', - manifest_props: " - bfd_per_link => 'true', - lacp_graceful_convergence => 'false', - lacp_max_bundle => '10', - lacp_min_links => '3', - lacp_suspend_individual => 'false', - ", - resource_props: { - 'bfd_per_link' => 'true', - 'lacp_graceful_convergence' => 'false', - 'lacp_max_bundle' => '10', - 'lacp_min_links' => '3', - 'lacp_suspend_individual' => 'false', + manifest_props: { + bfd_per_link: 'true', + lacp_graceful_convergence: 'false', + lacp_max_bundle: '10', + lacp_min_links: '3', + lacp_suspend_individual: 'false', }, } -################################################################# -# HELPER FUNCTIONS -################################################################# - -# Full command string for puppet resource command -def puppet_resource_cmd - PUPPET_BINPATH + 'resource cisco_interface_portchannel port-channel100' -end - -def build_manifest_interface_portchannel(tests, id) - if tests[id][:ensure] == :absent - state = 'ensure => absent,' - tests[id][:resource] = {} - else - state = 'ensure => present,' - manifest = tests[id][:manifest_props] - tests[id][:resource] = tests[id][:resource_props] - end - - tests[id][:title_pattern] = id if tests[id][:title_pattern].nil? - logger.debug( - "build_manifest_interface_portchannel :: title_pattern:\n" + - tests[id][:title_pattern]) - tests[id][:manifest] = "cat <#{PUPPETMASTER_MANIFESTPATH} - node 'default' { - cisco_interface_portchannel { 'port-channel100': - #{state} - #{manifest} - } - } -EOF" -end - -def test_harness_interface_portchannel(tests, id) - tests[id][:ensure] = :present if tests[id][:ensure].nil? - tests[id][:resource_cmd] = puppet_resource_cmd - tests[id][:desc] += " [ensure => #{tests[id][:ensure]}]" - - # Build the manifest for this test - build_manifest_interface_portchannel(tests, id) - - # test_harness_common(tests, id) - test_manifest(tests, id) - test_resource(tests, id) - test_idempotence(tests, id) - - tests[id][:ensure] = nil -end - ################################################################# # TEST CASE EXECUTION ################################################################# -test_name "TestCase :: #{testheader}" do +test_name "TestCase :: #{tests[:resource_name]}" do + teardown { resource_absent_cleanup(agent, 'cisco_interface_portchannel') } + resource_absent_cleanup(agent, 'cisco_interface_portchannel') + system_default_switchport(agent, false) + # ------------------------------------------------------------------- - device = platform - logger.info("#### This device is of type: #{device} #####") - resource_absent_cleanup(agent, 'cisco_interface_portchannel', - 'Setup switch for interface_portchannel provider test') logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") - case device - when /n7k/ - id = 'default_properties_asym' - when /n5k|n6k/ - id = 'default_properties_eth' - when /n3k|n8k|n9k/ - id = 'default_properties_sym' + [:default_asym, :default_sym, :default_eth].each do |id| + next unless platform_supports_test(tests, id) + test_harness_run(tests, id) + tests[id][:ensure] = :absent + test_harness_run(tests, id) end - system_default_switchport(agent, false) - tests[id][:desc] = '1.1 Default Properties' - test_harness_interface_portchannel(tests, id) - - tests[id][:desc] = '1.2 Default Properties' - tests[id][:ensure] = :absent - test_harness_interface_portchannel(tests, id) - # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") - case device - when /n7k/ - id = 'non_default_properties_asym' - when /n5k|n6k/ - id = 'non_default_properties_eth' - when /n3k|n8k|n9k/ - id = 'non_default_properties_sym' - end - tests[id][:desc] = '2.1 Non Default Properties' - test_harness_interface_portchannel(tests, id) - - tests[id][:desc] = '2.2 Non Default Properties (absent)' - tests[id][:ensure] = :absent - test_harness_interface_portchannel(tests, id) + test_harness_run(tests, :non_default_asym) + test_harness_run(tests, :non_default_sym) + test_harness_run(tests, :non_default_eth) end - -logger.info("TestCase :: #{testheader} :: End") +logger.info("TestCase :: #{tests[:resource_name]} :: End") From da0871df10ee51c64fd0bb8016282adffa6a90f9 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 10:24:57 -0400 Subject: [PATCH 069/203] setup/teardown for test_itd_service --- .../cisco_itd_service/test_itd_service.rb | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/tests/beaker_tests/cisco_itd_service/test_itd_service.rb b/tests/beaker_tests/cisco_itd_service/test_itd_service.rb index a4df9add3..f3d9acf61 100644 --- a/tests/beaker_tests/cisco_itd_service/test_itd_service.rb +++ b/tests/beaker_tests/cisco_itd_service/test_itd_service.rb @@ -33,6 +33,10 @@ resource_name: 'cisco_itd_service', } +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) +skip_nexus_i2_image(tests) + def find_ingress_ethernet_interface(tests) if tests[:ethernet] intf = tests[:ethernet] @@ -44,17 +48,14 @@ def find_ingress_ethernet_interface(tests) # cache for later tests tests[:ethernet] = intf end - logger.info("\nUsing interface: #{intf}") intf end - -@ing_eth_int = find_ingress_ethernet_interface(tests) +@ingress_eth_int = find_ingress_ethernet_interface(tests) # Test hash test cases tests[:default] = { desc: '1.1 Common Defaults', title_pattern: 'myService', - preclean: 'cisco_itd_service', manifest_props: { access_list: 'default', device_group: 'default', @@ -105,7 +106,7 @@ def find_ingress_ethernet_interface(tests) }, } -ing_intf = [['vlan 2', '4.4.4.4'], [@ing_eth_int, '5.5.5.5'], ['port-channel 100', '6.6.6.6']] +ingress_intf = [['vlan 2', '4.4.4.4'], [@ingress_eth_int, '5.5.5.5'], ['port-channel 100', '6.6.6.6']] vip = ['ip 3.3.3.3 255.0.0.0 tcp 500 advertise enable'] pv = %w(myVdc1 pvservice) @@ -120,7 +121,7 @@ def find_ingress_ethernet_interface(tests) device_group: 'udpGroup', exclude_access_list: 'eap', fail_action: 'true', - ingress_interface: ing_intf, + ingress_interface: ingress_intf, load_bal_buckets: '16', load_bal_enable: 'true', load_bal_mask_pos: '5', @@ -140,7 +141,7 @@ def find_ingress_ethernet_interface(tests) preclean: 'cisco_itd_service', manifest_props: { device_group: 'udpGroup', - ingress_interface: ing_intf, + ingress_interface: ingress_intf, load_bal_buckets: '32', load_bal_enable: 'true', load_bal_mask_pos: '10', @@ -160,7 +161,7 @@ def find_ingress_ethernet_interface(tests) preclean: 'cisco_itd_service', manifest_props: { device_group: 'udpGroup', - ingress_interface: ing_intf, + ingress_interface: ingress_intf, load_bal_buckets: '32', load_bal_enable: 'true', load_bal_mask_pos: '10', @@ -177,7 +178,7 @@ def find_ingress_ethernet_interface(tests) preclean: 'cisco_itd_service', manifest_props: { device_group: 'udpGroup', - ingress_interface: [[@ing_eth_int, '2.2.2.2']], + ingress_interface: [[@ingress_eth_int, '2.2.2.2']], shutdown: 'false', }, } @@ -187,7 +188,7 @@ def find_ingress_ethernet_interface(tests) title_pattern: 'myService', manifest_props: { device_group: 'udpGroup', - ingress_interface: [[@ing_eth_int, '3.3.3.3']], + ingress_interface: [[@ingress_eth_int, '3.3.3.3']], shutdown: 'true', }, } @@ -197,7 +198,7 @@ def find_ingress_ethernet_interface(tests) title_pattern: 'myService', manifest_props: { device_group: 'udpGroup', - ingress_interface: [[@ing_eth_int, '4.4.4.4']], + ingress_interface: [[@ingress_eth_int, '4.4.4.4']], shutdown: 'true', }, } @@ -207,22 +208,32 @@ def find_ingress_ethernet_interface(tests) title_pattern: 'myService', manifest_props: { device_group: 'udpGroup', - ingress_interface: [[@ing_eth_int, '5.5.5.5']], + ingress_interface: [[@ingress_eth_int, '5.5.5.5']], shutdown: 'false', }, } +def cleanup(agent) + cmds = ['no feature itd', + 'no ip access-list iap', + 'no ip access-list eap', + 'no vlan 2', + 'no interface port-channel 100', + 'no feature interface-vlan', + 'no feature itd', + ] + test_set(agent, cmds) + interface_cleanup(agent, @ingress_eth_int) +end + # Overridden to properly handle dependencies for this test file. def test_harness_dependencies(_tests, id) return if id[/non_default_shut_/] - - resource_absent_cleanup(agent, 'cisco_itd_service') - resource_absent_cleanup(agent, 'cisco_itd_device_group_node') - resource_absent_cleanup(agent, 'cisco_itd_device_group') + cleanup(agent) cmd = [ 'ip access-list iap ; ip access-list eap', - "interface #{@ing_eth_int} ; no switchport", + "interface #{@ingress_eth_int} ; no switchport", 'feature interface-vlan', 'vlan 2 ; interface vlan 2', 'interface port-channel 100 ; no switchport', @@ -235,10 +246,10 @@ def test_harness_dependencies(_tests, id) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(agent) } + cleanup(agent) + # ------------------------------------------------------------------- - device = platform - logger.info("#### This device is of type: #{device} #####") - skip_nexus_i2_image(tests) logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") test_harness_run(tests, :default) @@ -246,8 +257,8 @@ def test_harness_dependencies(_tests, id) test_harness_run(tests, :default_plat_2) id = :default + tests[id][:desc] = '1.4 Common Defaults (absent)' tests[id][:ensure] = :absent - tests[id].delete(:preclean) test_harness_run(tests, id) # ------------------------------------------------------------------- @@ -260,10 +271,8 @@ def test_harness_dependencies(_tests, id) test_harness_run(tests, :non_default_shut_2) test_harness_run(tests, :non_default_shut_3) test_harness_run(tests, :non_default_shut_4) - resource_absent_cleanup(agent, 'cisco_itd_service') - resource_absent_cleanup(agent, 'cisco_itd_device_group_node') - resource_absent_cleanup(agent, 'cisco_itd_device_group') + + # ------------------------------------------------------------------- skipped_tests_summary(tests) end - logger.info("TestCase :: #{tests[:resource_name]} :: End") From 290968b5096dc9f86902c627a7e983a22ee32cb1 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 10:39:31 -0400 Subject: [PATCH 070/203] setup/teardown cisco_ospf_area* --- .../cisco_ospf_area/test_ospf_area.rb | 13 +++++++------ .../cisco_ospf_area_vlink/test_ospf_area_vlink.rb | 15 ++++++++------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/tests/beaker_tests/cisco_ospf_area/test_ospf_area.rb b/tests/beaker_tests/cisco_ospf_area/test_ospf_area.rb index 0c5a9e0fc..c1f960c42 100644 --- a/tests/beaker_tests/cisco_ospf_area/test_ospf_area.rb +++ b/tests/beaker_tests/cisco_ospf_area/test_ospf_area.rb @@ -21,7 +21,6 @@ # - A description of the 'tests' hash and its usage # ############################################################################### - require File.expand_path('../../lib/utilitylib.rb', __FILE__) # Test hash top-level keys @@ -41,7 +40,6 @@ tests[:non_default_1] = { desc: '1.1 Non_Defaults', title_pattern: 'dark_blue default 1.1.1.1', - preclean: 'cisco_ospf', manifest_props: { authentication: 'md5', default_cost: 1000, @@ -112,13 +110,18 @@ }, } +def cleanup(agent) + test_set(agent, 'no feature ospf') +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(agent) } + cleanup(agent) + # ------------------------------------------------------------------- - device = platform - logger.info("#### This device is of type: #{device} #####") logger.info("\n#{'-' * 60}\nSection Non Default Property Testing") test_harness_run(tests, :non_default_1) @@ -127,7 +130,5 @@ test_harness_run(tests, :non_default_4) test_harness_run(tests, :non_default_5) test_harness_run(tests, :non_default_6) - resource_absent_cleanup(agent, 'cisco_ospf') end - logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_ospf_area_vlink/test_ospf_area_vlink.rb b/tests/beaker_tests/cisco_ospf_area_vlink/test_ospf_area_vlink.rb index 45bad9a2c..6ee7c3a88 100644 --- a/tests/beaker_tests/cisco_ospf_area_vlink/test_ospf_area_vlink.rb +++ b/tests/beaker_tests/cisco_ospf_area_vlink/test_ospf_area_vlink.rb @@ -21,7 +21,6 @@ # - A description of the 'tests' hash and its usage # ############################################################################### - require File.expand_path('../../lib/utilitylib.rb', __FILE__) # Test hash top-level keys @@ -36,7 +35,6 @@ tests[:default] = { desc: '1.1 Defaults', title_pattern: 'dark_blue default 1.1.1.1 2.2.2.2', - preclean: 'cisco_ospf', manifest_props: { auth_key_chain: 'default', authentication: 'default', @@ -64,12 +62,9 @@ }, } -# Non-default Tests. NOTE: [:resource] = [:manifest_props] for all non-default - tests[:non_default] = { desc: '2.1 Non Defaults', title_pattern: 'dark_blue default 1.1.1.1 2.2.2.2', - preclean: 'cisco_ospf', manifest_props: { auth_key_chain: 'testKeyChain', authentication: 'md5', @@ -86,24 +81,30 @@ }, } +def cleanup(agent) + test_set(agent, 'no feature ospf') +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(agent) } + cleanup(agent) + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") test_harness_run(tests, :default) id = :default tests[id][:ensure] = :absent - tests[id].delete(:preclean) test_harness_run(tests, id) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + cleanup(agent) test_harness_run(tests, :non_default) - resource_absent_cleanup(agent, 'cisco_ospf') end logger.info("TestCase :: #{tests[:resource_name]} :: End") From 49207062af484d56a31c99708b7dcf47104cda51 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 11:06:40 -0400 Subject: [PATCH 071/203] setup/teardown cisco_interface_ospf --- .../test_interface_ospf.rb | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb index 46e9ebaf7..2a86624ba 100644 --- a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb +++ b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb @@ -21,7 +21,6 @@ # - A description of the 'tests' hash and its usage # ############################################################################### - require File.expand_path('../../lib/utilitylib.rb', __FILE__) # Test hash top-level keys @@ -37,13 +36,12 @@ skip_unless_supported(tests) # Find a usable interface for this test -@intf = find_interface(tests) -tp = @intf + ' Sample' +intf = find_interface(tests) # Test hash test cases tests[:default] = { desc: '1.1 Defaults', - title_pattern: tp, + title_pattern: "#{intf} Sample", preclean_intf: true, manifest_props: { area: 200, @@ -81,11 +79,9 @@ }, } -# Non-default Tests. NOTE: [:resource] = [:manifest_props] for all non-default - tests[:non_default] = { desc: '2.1 Non Defaults', - title_pattern: tp, + title_pattern: "#{intf} Sample", preclean_intf: true, manifest_props: { area: 200, @@ -110,34 +106,37 @@ }, } -# Overridden to properly handle dependencies for this test file. -def test_harness_dependencies(_tests, id) +def dependency_manifest(_tests, id) return unless id == :default - cmd = [ - 'feature ospf ; router ospf Sample', - "interface #{@intf} ; no switchport", - ].join(' ; ') - command_config(agent, cmd, cmd) + " + cisco_ospf { 'sample': + ensure => present + } + " +end + +def cleanup(agent, intf) + test_set(agent, 'no feature ospf') + interface_cleanup(agent, intf) end ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(agent, intf) } + cleanup(agent, intf) + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") test_harness_run(tests, :default) id = :default tests[id][:ensure] = :absent - tests[id].delete(:preclean) test_harness_run(tests, id) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") - test_harness_run(tests, :non_default) - interface_cleanup(agent, @intf) end - logger.info("TestCase :: #{tests[:resource_name]} :: End") From 8f6088f5b5eb8ab1119e04f540e4e705d5b59e54 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 11:07:48 -0400 Subject: [PATCH 072/203] setup/teardown cisco_ospf_vrf --- tests/beaker_tests/cisco_ospf_vrf/test_ospf_vrf.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/beaker_tests/cisco_ospf_vrf/test_ospf_vrf.rb b/tests/beaker_tests/cisco_ospf_vrf/test_ospf_vrf.rb index a4e0f798e..e5cf6f64d 100644 --- a/tests/beaker_tests/cisco_ospf_vrf/test_ospf_vrf.rb +++ b/tests/beaker_tests/cisco_ospf_vrf/test_ospf_vrf.rb @@ -36,7 +36,6 @@ tests[:default_1] = { desc: '1.1 Defaults', title_pattern: 'test default', - preclean: 'cisco_ospf', manifest_props: { auto_cost: 'default', bfd: 'default', @@ -131,10 +130,17 @@ }, } +def cleanup(agent) + test_set(agent, 'no feature ospf') +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(agent) } + cleanup(agent) + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") test_harness_run(tests, :default_1) @@ -142,7 +148,6 @@ id = :default_2 tests[id][:ensure] = :absent - tests[id].delete(:preclean) test_harness_run(tests, id) # ------------------------------------------------------------------- @@ -150,7 +155,6 @@ test_harness_run(tests, :non_default_1) test_harness_run(tests, :non_default_2) - resource_absent_cleanup(agent, 'cisco_ospf') end logger.info("TestCase :: #{tests[:resource_name]} :: End") From 8fc1190c0b3f056bfc433f0a4b018db5b5a2e4c4 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 12:03:53 -0400 Subject: [PATCH 073/203] Bad cleanup test_itd_service --- tests/beaker_tests/cisco_itd_service/test_itd_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/beaker_tests/cisco_itd_service/test_itd_service.rb b/tests/beaker_tests/cisco_itd_service/test_itd_service.rb index f3d9acf61..aaee66b10 100644 --- a/tests/beaker_tests/cisco_itd_service/test_itd_service.rb +++ b/tests/beaker_tests/cisco_itd_service/test_itd_service.rb @@ -221,7 +221,7 @@ def cleanup(agent) 'no interface port-channel 100', 'no feature interface-vlan', 'no feature itd', - ] + ].join(' ; ') test_set(agent, cmds) interface_cleanup(agent, @ingress_eth_int) end From 9525c68e4d13e78fb920cf44dbbfd87421d88d70 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 12:35:04 -0400 Subject: [PATCH 074/203] Convert test_command_config to new syntax --- .../test_command_config.rb | 339 +++++++----------- 1 file changed, 120 insertions(+), 219 deletions(-) diff --git a/tests/beaker_tests/cisco_command_config/test_command_config.rb b/tests/beaker_tests/cisco_command_config/test_command_config.rb index cb05abfc5..49933c313 100644 --- a/tests/beaker_tests/cisco_command_config/test_command_config.rb +++ b/tests/beaker_tests/cisco_command_config/test_command_config.rb @@ -13,113 +13,57 @@ # See the License for the specific language governing permissions and # limitations under the License. ############################################################################### -# TestCase Name: -# ------------- -# test_command_config.rb # -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet cisco_command_config resource testcase for Puppet Agent on -# Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# - Host configuration file contains agent and master information. -# - SSH is enabled on the N9K Agent. -# - Puppet master/server is started. -# - Puppet agent certificate has been signed on the Puppet master/server. -# -# TestCase: -# --------- -# The following exit_codes are validated for Puppet, Vegas shell and -# Bash shell commands. -# -# Vegas and Bash Shell Commands: -# 0 - successful command execution -# > 0 - failed command execution. -# -# Puppet Commands: -# 0 - no changes have occurred -# 1 - errors have occurred, -# 2 - changes have occurred -# 4 - failures have occurred and -# 6 - changes and failures have occurred. -# -# NOTE: 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# -# The test cases use RegExp pattern matching on stdout or output IO -# instance attributes to verify resource properties. +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage # ############################################################################### - require File.expand_path('../../lib/utilitylib.rb', __FILE__) -# ----------------------------- -# Common settings and variables -# ----------------------------- -testheader = 'Resource cisco_command_config' - -# The 'tests' hash is used to define all of the test data values and expected -# results. It is also used to pass optional flags to the test methods when -# necessary. - -# 'tests' hash -# Top-level keys set by caller: -# tests[:master] - the master object -# tests[:agent] - the agent object -# +# Test hash top-level keys tests = { - master: master, - agent: agent, + agent: agent, + master: master, + ensurable: false, + operating_system: 'nexus', + resource_name: 'cisco_command_config', } -# tests[id] keys set by caller and used by test_harness_common: -# -# tests[id] keys set by caller: -# tests[id][:desc] - a string to use with logs & debugs -# tests[id][:manifest] - the complete manifest, as used by test_harness_common -# tests[id][:resource] - a hash of expected states, used by test_resource -# tests[id][:resource_cmd] - 'puppet resource' command to use with test_resource -# tests[id][:code] - (Optional) override the default exit code in some tests. -# -# These keys are local use only and not used by test_harness_common: -# -# tests[id][:manifest_props] - This is essentially a master list of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the list -# tests[id][:resource_props] - This is essentially a master hash of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the hash -# tests[id][:puppet_resource] - Puppet resource used to verify the configuration -# applied by the cisco_command_config resource. -# -tests['configure_bgp'] = { - puppet_resource: 'cisco_bgp', - platform: 'n(3|5|6|7|8|9)k', - # Command indentation is very important! - # Make sure config appears exactly how it nvgens on the node. - manifest_props: " - command => ' - feature bgp - router bgp 55 - shutdown - router-id 192.55.55.55 - cluster-id 172.5.5.5 - timers bgp 33 190 - timers bestpath-limit 44 always - graceful-restart-helper - graceful-restart restart-time 55 - graceful-restart stalepath-time 55 - confederation identifier 50 - confederation peers 327686 327685 200608 5000 6000 32 43 - bestpath as-path multipath-relax - bestpath cost-community ignore - bestpath compare-routerid - bestpath med confed - bestpath med non-deterministic - bestpath always-compare-med - suppress-fib-pending - log-neighbor-changes', - ", - resource_props: { +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + +tests[:bgp] = { + desc: '1.1 BGP', + manifest_props: { + # Command indentation is very important! + # Make sure config appears exactly how it nvgens on the node. + command: ' +feature bgp +router bgp 55 + shutdown + router-id 192.55.55.55 + cluster-id 172.5.5.5 + timers bgp 33 190 + timers bestpath-limit 44 always + graceful-restart-helper + graceful-restart restart-time 55 + graceful-restart stalepath-time 55 + confederation identifier 50 + confederation peers 327686 327685 200608 5000 6000 32 43 + bestpath as-path multipath-relax + bestpath cost-community ignore + bestpath compare-routerid + bestpath med confed + bestpath med non-deterministic + bestpath always-compare-med + suppress-fib-pending + log-neighbor-changes + ' + }, + resource: { 'ensure' => 'present', 'bestpath_always_compare_med' => 'true', 'bestpath_aspath_multipath_relax' => 'true', @@ -144,19 +88,19 @@ }, } -tests['configure_bgp_af'] = { - puppet_resource: 'cisco_bgp_af', - platform: 'n(3|5|6|7|8|9)k', - # Command indentation is very important! - # Make sure config appears exactly how it nvgens on the node. - manifest_props: " - command => ' - feature bgp - router bgp 55 - address-family ipv4 unicast - dampening', - ", - resource_props: { +tests[:bgp_af] = { + desc: '1.2 BGP_AF', + manifest_props: { + # Command indentation is very important! + # Make sure config appears exactly how it nvgens on the node. + command: ' +feature bgp +router bgp 55 + address-family ipv4 unicast + dampening + ' + }, + resource: { 'ensure' => 'present', 'dampening_state' => 'true', 'dampening_half_time' => '15', @@ -166,153 +110,110 @@ }, } -tests['configure_bgp_neighbor'] = { - puppet_resource: 'cisco_bgp_neighbor', - platform: 'n(3|5|6|7|8|9)k', - # Command indentation is very important! - # Make sure config appears exactly how it nvgens on the node. - manifest_props: " - command => ' - feature bgp - router bgp 55 - neighbor 1.1.1.1 - timers 90 270', - ", - resource_props: { +tests[:bgp_neighbor] = { + desc: '1.3 BGP_NEIGHBOR', + manifest_props: { + # Command indentation is very important! + # Make sure config appears exactly how it nvgens on the node. + command: ' +feature bgp +router bgp 55 + neighbor 1.1.1.1 + timers 90 270 + ' + }, + resource: { 'ensure' => 'present', 'timers_keepalive' => '90', 'timers_holdtime' => '270', }, } -tests['configure_bgp_neighbor_af'] = { - puppet_resource: 'cisco_bgp_neighbor_af', - platform: 'n(3|5|6|7|8|9)k', - # Command indentation is very important! - # Make sure config appears exactly how it nvgens on the node. - manifest_props: " - command => ' - feature bgp - router bgp 55 - neighbor 1.1.1.1 - address-family ipv6 unicast - capability additional-paths send', - ", - resource_props: { +tests[:bgp_neighbor_af] = { + desc: '1.4 BGP_NEIGHBOR_AF', + manifest_props: { + # Command indentation is very important! + # Make sure config appears exactly how it nvgens on the node. + command: ' +feature bgp +router bgp 55 + neighbor 1.1.1.1 + address-family ipv6 unicast + capability additional-paths send + ' + }, + resource: { 'ensure' => 'present', 'additional_paths_send' => 'enable', }, } -tests['configure_loopback_interface'] = { - puppet_resource: 'cisco_interface', +tests[:loopback] = { + desc: '1.5 LOOPBACK', # Command indentation is very important! # Make sure config appears exactly how it nvgens on the switch. - manifest_props: " - command => ' - interface loopback1 - description configured_by_puppet', - ", - resource_props: { + manifest_props: { + command: ' +interface loopback1 + description configured_by_puppet + ' + }, + resource: { 'description' => 'configured_by_puppet' }, } -################################################################# -# HELPER FUNCTIONS -################################################################# - def test_set_get stepinfo = 'Test test_get/test_set properties' logger.info("\n#{'-' * 60}\n#{stepinfo}") cmd_prefix = PUPPET_BINPATH + "resource cisco_command_config 'cc' " - logger.info('* cleanup testbed') - on(agent, cmd_prefix + "test_set='no interface loopback42'") - logger.info('* create config') - on(agent, cmd_prefix + "test_set='interface loopback42'") + on(agent, cmd_prefix + "test_set='interface loopback1'") logger.info('* check config') - on(agent, cmd_prefix + "test_get='incl loopback42'") + on(agent, cmd_prefix + "test_get='incl loopback1'") fail_test("TestStep :: set/get :: FAIL\nstdout:\n#{stdout}") unless - stdout[/^interface loopback42/] + stdout[/^interface loopback1/] - logger.info('* cleanup testbed') - on(agent, cmd_prefix + "test_set='no interface loopback42'") logger.info("#{stepinfo} :: PASS\n#{'-' * 60}\n") end -# Full command string for puppet resource command used to verify -# the configuration applied by the cisco_command_config resource. -def puppet_resource_cmd(tests, id) - PUPPET_BINPATH + "resource #{tests[id][:puppet_resource]}" +def cleanup(agent) + test_set(agent, 'no feature bgp ; no interface loopback1') end -def build_manifest_cisco_command_config(tests, id) - manifest = tests[id][:manifest_props] - tests[id][:resource] = tests[id][:resource_props] - - tests[id][:title_pattern] = id if tests[id][:title_pattern].nil? - logger.debug("build_manifest_cisco_command_config :: title_pattern:\n" + - tests[id][:title_pattern]) - tests[id][:manifest] = "cat <#{PUPPETMASTER_MANIFESTPATH} - node 'default' { - cisco_command_config { '#{tests[id][:title_pattern]}': - #{manifest} - } - } -EOF" -end - -def test_harness_cisco_command_config(tests, id) - return unless platform_supports_test(tests, id) - - tests[id][:resource_cmd] = puppet_resource_cmd(tests, id) +def test_harness_run_cc(tests, id, res_cmd) + tests[:resource_name] = 'cisco_command_config' + create_manifest_and_resource(tests, id) + test_manifest(tests, id) + test_idempotence(tests, id) - # Build the manifest for this test - build_manifest_cisco_command_config(tests, id) - - test_harness_common(tests, id) - - tests[id][:ensure] = nil + # Command_config can only "set" configs, it can't check them with + # puppet resource, so use res_cmd to do that part of the test + tests[id][:resource_cmd] = PUPPET_BINPATH + 'resource ' + res_cmd + test_resource(tests, id) end ################################################################# # TEST CASE EXECUTION ################################################################# -test_name "TestCase :: #{testheader}" do - # ------------------------------------------------------------------- - logger.info("\n#{'-' * 60}\nApply cisco_command_config test.") - - # Cleanup any existing resources. - tests.keys.each do |id| - resource_absent_cleanup(agent, tests[id][:puppet_resource]) unless - tests[id][:puppet_resource].nil? - end - - # ----------------------------------- - id = 'configure_bgp' - tests[id][:desc] = '1.1 Apply BGP Config' - test_harness_cisco_command_config(tests, id) +test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(agent) } + cleanup(agent) - id = 'configure_bgp_af' - tests[id][:desc] = '1.2 Apply BGP AF Config' - test_harness_cisco_command_config(tests, id) - - id = 'configure_bgp_neighbor' - tests[id][:desc] = '1.3 Apply BGP Neighbor Config' - test_harness_cisco_command_config(tests, id) - - id = 'configure_bgp_neighbor_af' - tests[id][:desc] = '1.4 Apply BGP Neighbor AF Config' - test_harness_cisco_command_config(tests, id) + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Basic multi-level configs") - id = 'configure_loopback_interface' - tests[id][:desc] = '1.5 Apply INTERFACE Config' - test_harness_cisco_command_config(tests, id) + test_harness_run_cc(tests, :bgp, 'cisco_bgp') + test_harness_run_cc(tests, :bgp_af, 'cisco_bgp_af') + test_harness_run_cc(tests, :bgp_neighbor, 'cisco_bgp_neighbor') + test_harness_run_cc(tests, :bgp_neighbor_af, 'cisco_bgp_neighbor_af') + test_harness_run_cc(tests, :loopback, 'cisco_interface') + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. test_set / test_get") + cleanup(agent) test_set_get end - -logger.info('TestCase :: # {testheader} :: End') +logger.info("TestCase :: #{tests[:resource_name]} :: End") From 3943cf4475bce722e5e2cacb22ecf33418429504 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 14:27:16 -0400 Subject: [PATCH 075/203] better cleanup for bfd --- tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb b/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb index a941f033a..a61ab521d 100644 --- a/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb +++ b/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb @@ -153,15 +153,17 @@ def test_harness_dependencies(_tests, id) command_config(agent, cmd, cmd) end +def cleanup(agent) + test_set(agent, 'no feature bfd') +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - teardown { resource_absent_cleanup(agent, 'cisco_bfd_global') } - resource_absent_cleanup(agent, 'cisco_bfd_global') + teardown { cleanup(agent) } + cleanup(agent) - device = platform - logger.info("#### This device is of type: #{device} #####") # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") test_harness_run(tests, :default) From fd030acf45fa0fc3222ee9763694234fe4bf4036 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 14:27:48 -0400 Subject: [PATCH 076/203] setup/teardown for test_bgp --- tests/beaker_tests/cisco_bgp/test_bgp.rb | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/beaker_tests/cisco_bgp/test_bgp.rb b/tests/beaker_tests/cisco_bgp/test_bgp.rb index c61f9dd37..b53bf09f3 100644 --- a/tests/beaker_tests/cisco_bgp/test_bgp.rb +++ b/tests/beaker_tests/cisco_bgp/test_bgp.rb @@ -27,8 +27,8 @@ # Test hash top-level keys asn = '1' tests = { - master: master, agent: agent, + master: master, asn: asn, resource_name: 'cisco_bgp', } @@ -37,7 +37,6 @@ tests[:default] = { desc: '1.1 Default Properties', title_pattern: "#{asn} default", - preclean: 'cisco_bgp', manifest_props: { bestpath_always_compare_med: 'default', bestpath_aspath_multipath_relax: 'default', @@ -151,7 +150,6 @@ tests[:title_patterns_1] = { desc: 'T.1 Title Pattern', - preclean: 'cisco_bgp', title_pattern: 'new_york', title_params: { asn: '11.4', vrf: 'red' }, resource: { 'ensure' => 'present' }, @@ -231,11 +229,20 @@ def unsupported_properties(tests, id) unprops end +def cleanup(agent) + if operating_system == 'nexus' + test_set(agent, 'no feature bgp') + else + resource_absent_cleanup(agent, 'cisco_bgp') + end +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - teardown { resource_absent_cleanup(agent, 'cisco_bgp') } + teardown { cleanup(agent) } + cleanup(agent) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") @@ -246,12 +253,11 @@ def unsupported_properties(tests, id) # test removal of bgp instance tests[id][:ensure] = :absent - tests[id].delete(:preclean) test_harness_run(tests, id) # now test the defaults under a non-default vrf + cleanup(agent) tests[id][:ensure] = :present - tests[id][:preclean] = 'cisco_bgp' test_harness_bgp_vrf(tests, id, 'blue') # ------------------------------------------------------------------- @@ -263,8 +269,8 @@ def unsupported_properties(tests, id) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 3. Title Pattern Testing") + cleanup(agent) test_harness_run(tests, :title_patterns_1) test_harness_run(tests, :title_patterns_2) end - logger.info("TestCase :: #{tests[:resource_name]} :: End") From d9ce12a0aeb84587d7c6cfde85988234dde0edce Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 14:36:47 -0400 Subject: [PATCH 077/203] setup/teardown test_bgpaf --- tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb b/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb index 276a0c851..b71c09706 100644 --- a/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb +++ b/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb @@ -26,8 +26,8 @@ # Test hash top-level keys tests = { - master: master, agent: agent, + master: master, resource_name: 'cisco_bgp_af', } @@ -108,7 +108,6 @@ # tests[:non_default] = { desc: '2.1 Non Default Properties', - preclean: 'cisco_bgp', title_pattern: '2 default ipv4 unicast', manifest_props: { additional_paths_install: 'true', @@ -164,7 +163,6 @@ tests[:title_patterns_1] = { desc: 'T.1 Title Pattern', - preclean: 'cisco_bgp', title_pattern: 'new_york', title_params: { asn: '2', vrf: 'red', afi: 'ipv4', safi: 'unicast' }, resource: { 'ensure' => 'present' }, @@ -280,12 +278,20 @@ def test_harness_bgp_af_run(tests, id) test_harness_bgp_vrf(tests, id, 'blue') # test in non-default vrf end +def cleanup(agent) + if operating_system == 'nexus' + test_set(agent, 'no feature bgp') + else + resource_absent_cleanup(agent, 'cisco_bgp') + end +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - teardown { resource_absent_cleanup(agent, 'cisco_bgp') } - resource_absent_cleanup(agent, 'cisco_bgp') + teardown { cleanup(agent) } + cleanup(agent) # ----------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") @@ -298,12 +304,14 @@ def test_harness_bgp_af_run(tests, id) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + cleanup(agent) test_harness_bgp_af_run(tests, :non_default) test_harness_bgp_af_run(tests, :non_default_arrays) test_harness_bgp_af_run(tests, :non_default_dampening_routemap) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 3. Title Pattern Testing") + cleanup(agent) test_harness_run(tests, :title_patterns_1) test_harness_run(tests, :title_patterns_2) test_harness_run(tests, :title_patterns_3) From df89e56e1ba407c68459eab26fc41c17e3dad1b0 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 14:40:29 -0400 Subject: [PATCH 078/203] better cleanup for pim --- tests/beaker_tests/cisco_pim/test_pim.rb | 10 +++++++--- tests/beaker_tests/cisco_pim/test_pim_grouplist.rb | 8 ++++++-- tests/beaker_tests/cisco_pim/test_pim_rp_address.rb | 8 ++++++-- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/tests/beaker_tests/cisco_pim/test_pim.rb b/tests/beaker_tests/cisco_pim/test_pim.rb index 56cffef0b..c6e5812e7 100644 --- a/tests/beaker_tests/cisco_pim/test_pim.rb +++ b/tests/beaker_tests/cisco_pim/test_pim.rb @@ -55,7 +55,6 @@ # tests[:title_patterns_1] = { desc: 'T.1 Title Pattern', - preclean: 'cisco_pim', title_pattern: 'new_york', title_params: { afi: 'ipv4', vrf: 'red' }, manifest_props: { ssm_range: '224.0.0.0/8 225.0.0.0/8' }, @@ -70,12 +69,16 @@ resource: { 'ensure' => 'present' }, } +def cleanup(agent) + test_set(agent, 'no feature pim') +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - teardown { resource_absent_cleanup(agent, 'cisco_pim') } - resource_absent_cleanup(agent, 'cisco_pim') + teardown { cleanup(agent) } + cleanup(agent) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Non Default Property Testing") @@ -84,6 +87,7 @@ # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Title Pattern Testing") + cleanup(agent) test_harness_run(tests, :title_patterns_1) test_harness_run(tests, :title_patterns_2) end diff --git a/tests/beaker_tests/cisco_pim/test_pim_grouplist.rb b/tests/beaker_tests/cisco_pim/test_pim_grouplist.rb index 53a3c8ea3..38f9f5f5b 100644 --- a/tests/beaker_tests/cisco_pim/test_pim_grouplist.rb +++ b/tests/beaker_tests/cisco_pim/test_pim_grouplist.rb @@ -66,12 +66,16 @@ resource: { 'ensure' => 'present' }, } +def cleanup(agent) + test_set(agent, 'no feature pim') +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - teardown { resource_absent_cleanup(agent, 'cisco_pim_grouplist') } - resource_absent_cleanup(agent, 'cisco_pim_grouplist') + teardown { cleanup(agent) } + cleanup(agent) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Title Pattern Testing") diff --git a/tests/beaker_tests/cisco_pim/test_pim_rp_address.rb b/tests/beaker_tests/cisco_pim/test_pim_rp_address.rb index f19baa8c3..0d7759744 100644 --- a/tests/beaker_tests/cisco_pim/test_pim_rp_address.rb +++ b/tests/beaker_tests/cisco_pim/test_pim_rp_address.rb @@ -58,12 +58,16 @@ resource: { 'ensure' => 'present' }, } +def cleanup(agent) + test_set(agent, 'no feature pim') +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - teardown { resource_absent_cleanup(agent, 'cisco_pim_rp_address') } - resource_absent_cleanup(agent, 'cisco_pim_rp_address') + teardown { cleanup(agent) } + cleanup(agent) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Title Pattern Testing") From f0f8b99e2731dc78f1a535e6d4d02d3045a57f14 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 14:44:16 -0400 Subject: [PATCH 079/203] setup/teardown test_bgpneighbor --- .../cisco_bgp_neighbor/test_bgpneighbor.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb b/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb index f180827ff..5bc7a9bca 100644 --- a/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb +++ b/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb @@ -35,7 +35,6 @@ tests[:default] = { desc: '1.1 Default Properties', title_pattern: '2 default 1.1.1.1', - preclean: 'cisco_bgp', manifest_props: { bfd: 'default', ebgp_multihop: 'default', @@ -96,7 +95,6 @@ tests[:title_patterns_1] = { desc: 'T.1 Title Pattern', - preclean: 'cisco_bgp', title_pattern: 'new_york', title_params: { asn: '11.4', vrf: 'red', neighbor: '1.1.1.1' }, resource: { 'ensure' => 'present' }, @@ -138,17 +136,27 @@ def unsupported_properties(_tests, _id) unprops end +def cleanup(agent) + if operating_system == 'nexus' + test_set(agent, 'no feature bgp ; no feature bfd') + else + resource_absent_cleanup(agent, 'cisco_bgp') + end +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(agent) } + cleanup(agent) + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") test_harness_run(tests, :default) # test removal of bgp neighbor instance tests[:default][:ensure] = :absent - tests[:default].delete(:preclean) test_harness_run(tests, :default) # now test the defaults under a non-default vrf @@ -166,12 +174,12 @@ def unsupported_properties(_tests, _id) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 3. Title Pattern Testing") + cleanup(agent) test_harness_run(tests, :title_patterns_1) test_harness_run(tests, :title_patterns_2) test_harness_run(tests, :title_patterns_3) # ----------------------------------- - resource_absent_cleanup(agent, 'cisco_bgp') skipped_tests_summary(tests) end logger.info("TestCase :: #{tests[:resource_name]} :: End") From 11d5afb5897706649e57029805b53ad97737f207 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 14:56:20 -0400 Subject: [PATCH 080/203] Better remove_all_vlans --- tests/beaker_tests/cisco_vlan/test_private_vlan.rb | 8 ++++++-- tests/beaker_tests/lib/utilitylib.rb | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/beaker_tests/cisco_vlan/test_private_vlan.rb b/tests/beaker_tests/cisco_vlan/test_private_vlan.rb index fefdd66be..f6ccab424 100644 --- a/tests/beaker_tests/cisco_vlan/test_private_vlan.rb +++ b/tests/beaker_tests/cisco_vlan/test_private_vlan.rb @@ -70,15 +70,19 @@ }, } +def cleanup(agent) + remove_all_vlans(agent) +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do teardown do - remove_all_vlans(agent) + cleanup(agent) vdc_limit_f3_no_intf_needed(:clear) end - remove_all_vlans(agent) + cleanup(agent) vdc_limit_f3_no_intf_needed(:set) # ------------------------------------------------------------------- diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index a7b7e368c..0e65fe261 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1354,7 +1354,8 @@ def remove_all_vlans(agent, stepinfo='Remove all vlans & bridge-domains') resource_absent_cleanup(agent, 'cisco_bridge_domain', 'bridge domains') cmd = 'system bridge-domain none' command_config(agent, cmd, cmd) - resource_absent_cleanup(agent, 'cisco_vlan', 'vlans') + test_set(agent, 'no feature private-vlan') + test_set(agent, 'no vlan 2-3967') end end From 56ffbc9429eba5bbb144394d8ab87f6a621f79b9 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 14:58:36 -0400 Subject: [PATCH 081/203] setup/teardown test_bgpneighboraf --- .../cisco_bgp_neighbor_af/test_bgpneighboraf.rb | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/beaker_tests/cisco_bgp_neighbor_af/test_bgpneighboraf.rb b/tests/beaker_tests/cisco_bgp_neighbor_af/test_bgpneighboraf.rb index 27efd4626..bd138e756 100644 --- a/tests/beaker_tests/cisco_bgp_neighbor_af/test_bgpneighboraf.rb +++ b/tests/beaker_tests/cisco_bgp_neighbor_af/test_bgpneighboraf.rb @@ -109,7 +109,6 @@ def dependency_manifest(tests, id) tests[:default] = { desc: '1.1 Default Properties', title_pattern: '2 default 1.1.1.1 ipv4 unicast', - preclean: 'cisco_bgp', manifest_props: { allowas_in: 'default', allowas_in_max: 'default', @@ -312,16 +311,26 @@ def dependency_manifest(tests, id) resource: { 'ensure' => 'present' }, } +def cleanup(agent) + if operating_system == 'nexus' + test_set(agent, 'no feature bgp') + else + resource_absent_cleanup(agent, 'cisco_bgp') + end +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(agent) } + cleanup(agent) + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") test_harness_run(tests, :default) tests[:default][:ensure] = :absent - tests[:default].delete(:preclean) test_harness_run(tests, :default) # ------------------------------------------------------------------- @@ -383,7 +392,6 @@ def dependency_manifest(tests, id) test_harness_run(tests, :title_patterns_4) # ----------------------------------- - resource_absent_cleanup(agent, 'cisco_bgp') skipped_tests_summary(tests) end logger.info("TestCase :: #{tests[:resource_name]} :: End") From 3443ef5cc7d2201d669527baad0cacae9af98d85 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 15:00:29 -0400 Subject: [PATCH 082/203] ospf_vrf cleanup should remove feature bfd --- tests/beaker_tests/cisco_ospf_vrf/test_ospf_vrf.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/beaker_tests/cisco_ospf_vrf/test_ospf_vrf.rb b/tests/beaker_tests/cisco_ospf_vrf/test_ospf_vrf.rb index e5cf6f64d..e9f2bff8f 100644 --- a/tests/beaker_tests/cisco_ospf_vrf/test_ospf_vrf.rb +++ b/tests/beaker_tests/cisco_ospf_vrf/test_ospf_vrf.rb @@ -131,7 +131,7 @@ } def cleanup(agent) - test_set(agent, 'no feature ospf') + test_set(agent, 'no feature ospf ; no feature bfd') end ################################################################# From 2d5e1b93f5387d63a8f379e4d9f4620b45c7ae67 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 15:08:19 -0400 Subject: [PATCH 083/203] setup/teardown test_stp_global --- tests/beaker_tests/cisco_stp_global/test_stp_global.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/beaker_tests/cisco_stp_global/test_stp_global.rb b/tests/beaker_tests/cisco_stp_global/test_stp_global.rb index 688e8018d..aadaa2cfe 100644 --- a/tests/beaker_tests/cisco_stp_global/test_stp_global.rb +++ b/tests/beaker_tests/cisco_stp_global/test_stp_global.rb @@ -262,8 +262,9 @@ def test_harness_dependencies(_tests, id) # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - # ------------------------------------------------------------------- + teardown { remove_all_vlans(agent) } remove_all_vlans(agent) + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") test_harness_run(tests, :default) @@ -271,6 +272,7 @@ def test_harness_dependencies(_tests, id) test_harness_run(tests, :default_mst) test_harness_run(tests, :default_plat_1) test_harness_run(tests, :default_plat_2) + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") @@ -279,8 +281,7 @@ def test_harness_dependencies(_tests, id) test_harness_run(tests, :non_default_plat_1) test_harness_run(tests, :non_default_plat_2) - remove_all_vlans(agent) + # ------------------------------------------------------------------- skipped_tests_summary(tests) end - logger.info("TestCase :: #{tests[:resource_name]} :: End") From 7ccf8aa71d11d046af5143953ed97deb00b0ece0 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 15:11:49 -0400 Subject: [PATCH 084/203] setup/teardown test_bgpneighbor* --- .../test_bgpneighbor_password.rb | 13 ++++++++++--- .../test_bgpneighbor_transport_passive_mode.rb | 14 ++++++++++---- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor_password.rb b/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor_password.rb index 8bbe0162d..7ed0f5089 100644 --- a/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor_password.rb +++ b/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor_password.rb @@ -61,8 +61,17 @@ resource_name: 'cisco_bgp_neighbor', } +def cleanup(agent) + if operating_system == 'nexus' + test_set(agent, 'no feature bgp') + else + resource_absent_cleanup(agent, 'cisco_bgp') + end +end + test_name "TestCase :: #{tests[:resource_name]} - #{id}" do - resource_absent_cleanup(agent, 'cisco_bgp') + teardown { cleanup(agent) } + cleanup(agent) os = operating_system vrf = 'red' @@ -122,8 +131,6 @@ test_resource(tests, id, true) end - resource_absent_cleanup(agent, 'cisco_bgp') - skipped_tests_summary(tests) end diff --git a/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor_transport_passive_mode.rb b/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor_transport_passive_mode.rb index 5698f67fc..ffff5099a 100644 --- a/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor_transport_passive_mode.rb +++ b/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor_transport_passive_mode.rb @@ -59,8 +59,17 @@ resource_name: 'cisco_bgp_neighbor', } +def cleanup(agent) + if operating_system == 'nexus' + test_set(agent, 'no feature bgp') + else + resource_absent_cleanup(agent, 'cisco_bgp') + end +end + test_name "TestCase :: #{tests[:resource_name]} - #{id}" do - resource_absent_cleanup(agent, 'cisco_bgp') + teardown { cleanup(agent) } + cleanup(agent) os = on(agent, facter_cmd('-p os.name')).stdout.chomp vrf = 'red' @@ -96,9 +105,6 @@ test_manifest(tests, id) test_resource(tests, id) - resource_absent_cleanup(agent, 'cisco_bgp') - skipped_tests_summary(tests) end - logger.info("TestCase :: #{tests[:resource_name]} - #{id} :: End") From 05d3d7180958d08edb57d1dab71b4859f7e996bc Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 15:17:03 -0400 Subject: [PATCH 085/203] better cleanup test_interface_L3 --- .../beaker_tests/cisco_interface/test_interface_L3.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/beaker_tests/cisco_interface/test_interface_L3.rb b/tests/beaker_tests/cisco_interface/test_interface_L3.rb index 4c81b1ad9..1855fc277 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_L3.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_L3.rb @@ -157,12 +157,19 @@ def dependency_manifest(_tests, id) dep end +def cleanup(agent, intf, dot1q) + test_set(agent, 'no feature bfd ; no feature pim') + test_set(agent, "no interface #{dot1q}") + remove_all_vrfs(agent) + interface_cleanup(agent, intf) +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - teardown { interface_cleanup(agent, intf) } - interface_cleanup(agent, intf) + teardown { cleanup(agent, intf, dot1q) } + cleanup(agent, intf, dot1q) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") From 995e403c219ac55eb418e14cf9a6bcddddec7c27 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 15:19:23 -0400 Subject: [PATCH 086/203] better cleanup test_interface_ospf --- tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb index 2a86624ba..1bd30adf6 100644 --- a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb +++ b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb @@ -116,7 +116,7 @@ def dependency_manifest(_tests, id) end def cleanup(agent, intf) - test_set(agent, 'no feature ospf') + test_set(agent, 'no feature ospf ; no feature bfd') interface_cleanup(agent, intf) end From 7687beb2a9eeb582ad4ede37b18ee128aa84e02b Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 15:22:01 -0400 Subject: [PATCH 087/203] setup/teardown test_interface_portchannel --- .../test_interface_portchannel.rb | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb b/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb index 66acd6d1e..1c238e364 100755 --- a/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb +++ b/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb @@ -29,9 +29,11 @@ resource_name: 'cisco_interface_portchannel', } +intf = 'port-channel100' + tests[:default_asym] = { desc: '1.1 Default Properties (asym)', - title_pattern: 'port-channel100', + title_pattern: intf, platform: 'n7k', code: [0, 2], manifest_props: { @@ -57,7 +59,7 @@ tests[:non_default_asym] = { desc: '2.1 Non Default Properties (asym)', platform: 'n7k', - title_pattern: 'port-channel100', + title_pattern: intf, manifest_props: { bfd_per_link: 'true', lacp_graceful_convergence: 'false', @@ -71,7 +73,7 @@ tests[:default_sym] = { desc: '1.2 Default Properties (sym)', - title_pattern: 'port-channel100', + title_pattern: intf, platform: 'n(3|8|9)k', code: [0, 2], manifest_props: { @@ -96,7 +98,7 @@ tests[:non_default_sym] = { desc: '2.2 Non Default Properties (sym)', - title_pattern: 'port-channel100', + title_pattern: intf, platform: 'n(3|8|9)k', manifest_props: { bfd_per_link: 'true', @@ -111,7 +113,7 @@ tests[:default_eth] = { desc: '1.3 Default Properties (eth)', - title_pattern: 'port-channel100', + title_pattern: intf, platform: 'n(5|6)k', code: [0, 2], manifest_props: { @@ -133,7 +135,7 @@ tests[:non_default_eth] = { desc: '2.3 Non Default Properties (eth)', platform: 'n(5|6)k', - title_pattern: 'port-channel100', + title_pattern: intf, manifest_props: { bfd_per_link: 'true', lacp_graceful_convergence: 'false', @@ -143,12 +145,17 @@ }, } +def cleanup(agent, intf) + test_set(agent, "no feature bfd ; no interface #{intf}") + resource_absent_cleanup(agent, 'cisco_interface_portchannel') +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - teardown { resource_absent_cleanup(agent, 'cisco_interface_portchannel') } - resource_absent_cleanup(agent, 'cisco_interface_portchannel') + teardown { cleanup(agent, intf) } + cleanup(agent, intf) system_default_switchport(agent, false) # ------------------------------------------------------------------- From 3fe8f20800fe1e0d28d95a8b7bbc1bd96539d216 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 15:26:22 -0400 Subject: [PATCH 088/203] setup/teardown test_interface_stp --- .../cisco_interface/test_interface_stp.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/beaker_tests/cisco_interface/test_interface_stp.rb b/tests/beaker_tests/cisco_interface/test_interface_stp.rb index da522363d..91c90d925 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_stp.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_stp.rb @@ -46,7 +46,6 @@ tests[:default] = { desc: '1.1 Default Properties', title_pattern: intf, - preclean_intf: true, code: [0], manifest_props: { stp_bpdufilter: 'default', @@ -113,11 +112,17 @@ }, } +def cleanup(agent, intf) + remove_all_vlans(agent) + interface_cleanup(agent, intf) +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - remove_all_vlans(agent) + teardown { cleanup(agent, intf) } + cleanup(agent, intf) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") @@ -126,9 +131,5 @@ # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") test_harness_run(tests, :non_default) - - remove_all_vlans(agent) - interface_cleanup(agent, intf) end - logger.info("TestCase :: #{tests[:resource_name]} :: End") From a2815c49ce01b30c8d437d71cb3b2e42ecf680ff Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 15:30:31 -0400 Subject: [PATCH 089/203] setup/teardown test_interface_bdi --- .../cisco_interface/test_interface_bdi.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/beaker_tests/cisco_interface/test_interface_bdi.rb b/tests/beaker_tests/cisco_interface/test_interface_bdi.rb index e850e2eba..bbf3bf01b 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_bdi.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_bdi.rb @@ -67,10 +67,18 @@ }, } +def cleanup(agent, intf) + remove_interface(agent, intf) + remove_all_vlans(agent) +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(agent, intf) } + cleanup(agent, intf) + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") test_harness_run(tests, :default) @@ -78,10 +86,6 @@ # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") test_harness_run(tests, :non_default) - - # ------------------------------------------------------------------- - remove_interface(agent, intf) - remove_all_vlans(agent) end logger.info("TestCase :: #{tests[:resource_name]} :: End") From 430f0d86386921c11f23e1000444cef498c5a0a9 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 15:33:05 -0400 Subject: [PATCH 090/203] setup/teardown test_evpn_vni --- .../cisco_evpn_vni/test_evpn_vni.rb | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/beaker_tests/cisco_evpn_vni/test_evpn_vni.rb b/tests/beaker_tests/cisco_evpn_vni/test_evpn_vni.rb index c65576c7a..916a7aef2 100644 --- a/tests/beaker_tests/cisco_evpn_vni/test_evpn_vni.rb +++ b/tests/beaker_tests/cisco_evpn_vni/test_evpn_vni.rb @@ -31,10 +31,12 @@ resource_name: 'cisco_evpn_vni', } +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + # Test hash test cases tests[:default] = { desc: '1.1 Default Properties', - preclean: 'cisco_evpn_vni', title_pattern: '4096', manifest_props: { route_distinguisher: 'default', @@ -74,26 +76,27 @@ def unsupported_properties(*) unprops end +def cleanup(agent) + resource_absent_cleanup(agent, 'cisco_evpn_vni') +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - skip_unless_supported(tests) + teardown { cleanup(agent) } + cleanup(agent) - logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") # ----------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") id = :default test_harness_run(tests, id) tests[id][:ensure] = :absent - tests[id].delete(:preclean) test_harness_run(tests, id) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") test_harness_run(tests, :non_default) - - # ----------------------------------- - resource_absent_cleanup(agent, 'cisco_evpn_vni') end logger.info("TestCase :: #{tests[:resource_name]} :: End") From 3576fcf98b059b8c17f6cab0600412d8ec637945 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 15:57:22 -0400 Subject: [PATCH 091/203] Convert test_vpc_domain to new syntax --- .../cisco_vpc_domain/test_vpc_domain.rb | 106 ++++-------------- tests/beaker_tests/lib/utilitylib.rb | 2 + 2 files changed, 23 insertions(+), 85 deletions(-) diff --git a/tests/beaker_tests/cisco_vpc_domain/test_vpc_domain.rb b/tests/beaker_tests/cisco_vpc_domain/test_vpc_domain.rb index 51451423b..e524eba50 100644 --- a/tests/beaker_tests/cisco_vpc_domain/test_vpc_domain.rb +++ b/tests/beaker_tests/cisco_vpc_domain/test_vpc_domain.rb @@ -13,64 +13,16 @@ # See the License for the specific language governing permissions and # limitations under the License. ############################################################################### -# TestCase Name: -# ------------- -# test_vpc_domain.rb # -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet cisco_vpc_domain resource testset for Puppet Agent -# on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# - Host configuration file contains agent and master information. -# - SSH is enabled on the Nexus Switch Agent. -# - Puppet master/server is started. -# - Puppet agent certificate has been signed on the Puppet master/server. -# -# TestCase: -# --------- -# This portchannel_global resource test verifies default values for all -# properties. -# -# The following exit_codes are validated for Puppet, Vegas shell and -# Bash shell commands. -# -# Vegas and Bash Shell Commands: -# 0 - successful command execution -# > 0 - failed command execution. -# -# Puppet Commands: -# 0 - no changes have occurred -# 1 - errors have occurred, -# 2 - changes have occurred -# 4 - failures have occurred and -# 6 - changes and failures have occurred. -# -# NOTE: 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# -# The test cases use RegExp pattern matching on stdout or output IO -# instance attributes to verify resource properties. +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage # ############################################################################### - require File.expand_path('../../lib/utilitylib.rb', __FILE__) -# ----------------------------- -# Common settings and variables -# ----------------------------- -testheader = 'Resource cisco_vpc_domain' - -# Define PUPPETMASTER_MANIFESTPATH. - -# The 'tests' hash is used to define all of the test data values and expected -# results. It is also used to pass optional flags to the test methods when -# necessary. - -# 'tests' hash -# Top-level keys set by caller: -# tests[:master] - the master object -# tests[:agent] - the agent object -# tests = { master: master, agent: agent, @@ -79,29 +31,8 @@ platform: 'n(3|6|7|9)k', } -# tests[id] keys set by caller and used by test_harness_common: -# -# tests[id] keys set by caller: -# tests[id][:desc] - a string to use with logs & debugs -# tests[id][:manifest] - the complete manifest, as used by test_harness_common -# tests[id][:resource] - a hash of expected states, used by test_resource -# tests[id][:resource_cmd] - 'puppet resource' command to use with test_resource -# tests[id][:ensure] - (Optional) set to :present or :absent before calling -# tests[id][:code] - (Optional) override the default exit code in some tests. -# -# These keys are local use only and not used by test_harness_common: -# -# tests[id][:manifest_props] - This is essentially a master list of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the list -# tests[id][:resource_props] - This is essentially a master hash of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the hash -# tests[id][:title_pattern] - (Optional) defines the manifest title. -# Can be used with :af for mixed title/af testing. If mixing, :af values will -# be merged with title values and override any duplicates. If omitted, -# :title_pattern will be set to 'id'. -# +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) tests[:default_properties] = { title_pattern: '200', @@ -251,17 +182,22 @@ }, } +def cleanup(agent) + remove_all_vlans(agent) + resource_absent_cleanup(agent, 'cisco_vpc_domain') +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown do + cleanup(agent) + vdc_limit_f3_no_intf_needed(:clear) + end + cleanup(agent) + # ------------------------------------------------------------------- - resource_absent_cleanup(agent, 'cisco_bridge_domain', - 'bridge-domain CLEANUP :: ') - resource_absent_cleanup(agent, 'cisco_vpc_domain', - 'Setup for cisco_vpc_domain provider test') - device = platform - logger.info("#### This device is of type: #{device} #####") logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") test_harness_run(tests, :default_properties) @@ -285,7 +221,8 @@ # ------------------------------------------------------------------------ logger.info("\n#{'-' * 60}\nSection 3. vPC+ Non Default Property Testing") # Need to setup fabricapth env for vPC+ - setup_fabricpath_env(tests, self) + # setup_fabricpath_env(tests, self) + vdc_limit_f3_no_intf_needed(:set) test_harness_run(tests, :vpc_plus_non_default_properties_n7k) # Resource absent test @@ -294,5 +231,4 @@ skipped_tests_summary(tests) end - -logger.info("TestCase :: #{testheader} :: End") +logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 0e65fe261..b919406ed 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1338,6 +1338,7 @@ def vdc_limit_f3_no_intf_needed(action=:set) mods = out.nil? ? nil : Regexp.last_match[1] return if mods == 'f3' cmd += "limit_resource_module_type='f3'" + logger.info("\n* Setup VDC: #{cmd}") on(agent, cmd, pty: true).stdout[/limit_resource.*'(f3)'/] when :clear @@ -1354,6 +1355,7 @@ def remove_all_vlans(agent, stepinfo='Remove all vlans & bridge-domains') resource_absent_cleanup(agent, 'cisco_bridge_domain', 'bridge domains') cmd = 'system bridge-domain none' command_config(agent, cmd, cmd) + test_set(agent, 'no feature interface-vlan') test_set(agent, 'no feature private-vlan') test_set(agent, 'no vlan 2-3967') end From bfa1b390bfcc4884eacb851340bf5243ca0f0b77 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 15:59:14 -0400 Subject: [PATCH 092/203] setup/teardown test_itd_device_group --- .../test_itd_device_group.rb | 23 +++++++++++-------- .../cisco_itd_service/test_itd_service.rb | 3 +-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/tests/beaker_tests/cisco_itd_device_group/test_itd_device_group.rb b/tests/beaker_tests/cisco_itd_device_group/test_itd_device_group.rb index 249488462..6af754a03 100644 --- a/tests/beaker_tests/cisco_itd_device_group/test_itd_device_group.rb +++ b/tests/beaker_tests/cisco_itd_device_group/test_itd_device_group.rb @@ -21,7 +21,6 @@ # - A description of the 'tests' hash and its usage # ############################################################################### - require File.expand_path('../../lib/utilitylib.rb', __FILE__) # Test hash top-level keys @@ -32,11 +31,14 @@ resource_name: 'cisco_itd_device_group', } +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) +skip_nexus_i2_image(tests) + # Test hash test cases tests[:default] = { desc: '1.1 Defaults', title_pattern: 'icmpGroup', - preclean: 'cisco_itd_device_group', manifest_props: { probe_type: 'default' }, @@ -125,33 +127,34 @@ }, } +def cleanup(agent) + test_set(agent, 'no feature itd') +end + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(agent) } + cleanup(agent) + # ------------------------------------------------------------------- - device = platform - logger.info("#### This device is of type: #{device} #####") - skip_nexus_i2_image(tests) logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") - test_harness_run(tests, :default) test_harness_run(tests, :default_icmp) id = :default tests[id][:ensure] = :absent - tests[id].delete(:preclean) test_harness_run(tests, id) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") - test_harness_run(tests, :non_default_icmp) test_harness_run(tests, :non_default_dns) test_harness_run(tests, :non_default_tcp) test_harness_run(tests, :non_default_udp) - resource_absent_cleanup(agent, 'cisco_itd_device_group') + + # ------------------------------------------------------------------- skipped_tests_summary(tests) end - logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_itd_service/test_itd_service.rb b/tests/beaker_tests/cisco_itd_service/test_itd_service.rb index aaee66b10..63388e8f1 100644 --- a/tests/beaker_tests/cisco_itd_service/test_itd_service.rb +++ b/tests/beaker_tests/cisco_itd_service/test_itd_service.rb @@ -214,8 +214,7 @@ def find_ingress_ethernet_interface(tests) } def cleanup(agent) - cmds = ['no feature itd', - 'no ip access-list iap', + cmds = ['no ip access-list iap', 'no ip access-list eap', 'no vlan 2', 'no interface port-channel 100', From bfd19dd660cce343988b825a14ecaf69b59cc083 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Mon, 22 Aug 2016 16:17:06 -0400 Subject: [PATCH 093/203] Convert test_overlay_global to new syntax --- .../test_overlay_global.rb | 203 ++++-------------- 1 file changed, 45 insertions(+), 158 deletions(-) diff --git a/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb b/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb index c0d4c488a..9098cf65e 100755 --- a/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb +++ b/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2015 Cisco and/or its affiliates. +# Copyright (c) 2015-2016 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,106 +13,40 @@ # See the License for the specific language governing permissions and # limitations under the License. ############################################################################### -# TestCase Name: -# ------------- -# test-overlay_global.rb # -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet overlay_global resource testcase for Puppet Agent on -# Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# - Host configuration file contains agent and master information. -# - SSH is enabled on the N9K Agent. -# - Puppet master/server is started. -# - Puppet agent certificate has been signed on the Puppet master/server. -# -# TestCase: -# --------- -# This overlay_global resource test verifies default values for all properties. -# -# The following exit_codes are validated for Puppet, Vegas shell and -# Bash shell commands. -# -# Vegas and Bash Shell Commands: -# 0 - successful command execution -# > 0 - failed command execution. -# -# Puppet Commands: -# 0 - no changes have occurred -# 1 - errors have occurred, -# 2 - changes have occurred -# 4 - failures have occurred and -# 6 - changes and failures have occurred. -# -# NOTE: 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# -# The test cases use RegExp pattern matching on stdout or output IO -# instance attributes to verify resource properties. +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage # ############################################################################### - require File.expand_path('../../lib/utilitylib.rb', __FILE__) -# ----------------------------- -# Common settings and variables -# ----------------------------- -testheader = 'Resource cisco_overlay_global' - -# Define PUPPETMASTER_MANIFESTPATH. - -# The 'tests' hash is used to define all of the test data values and expected -# results. It is also used to pass optional flags to the test methods when -# necessary. - -# 'tests' hash -# Top-level keys set by caller: -# tests[:master] - the master object -# tests[:agent] - the agent object -# tests = { - master: master, - agent: agent, - platform: 'n(5|6|7|8|9)k', + agent: agent, + master: master, + platform: 'n(5|6|7|8|9)k', + resource_name: 'cisco_overlay_global', + ensurable: false, } # Skip -ALL- tests if a top-level platform/os key exludes this platform skip_unless_supported(tests) -# tests[id] keys set by caller and used by test_harness_common: -# -# tests[id] keys set by caller: -# tests[id][:desc] - a string to use with logs & debugs -# tests[id][:manifest] - the complete manifest, as used by test_harness_common -# tests[id][:resource] - a hash of expected states, used by test_resource -# tests[id][:resource_cmd] - 'puppet resource' command to use with test_resource -# tests[id][:code] - (Optional) override the default exit code in some tests. -# -# These keys are local use only and not used by test_harness_common: -# -# tests[id][:manifest_props] - This is essentially a master list of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the list -# tests[id][:resource_props] - This is essentially a master hash of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the hash -# tests[id][:title_pattern] - (Optional) defines the manifest title. -# Can be used with :af for mixed title/af testing. If mixing, :af values will -# be merged with title values and override any duplicates. If omitted, -# :title_pattern will be set to 'id'. -# - -tests['default_properties'] = { +tests[:default] = { + desc: '1.1 Default Properties', + title_pattern: 'default', # Feature disablement does not reset the detection properties on some images code: [0, 2], - manifest_props: " - dup_host_ip_addr_detection_host_moves => 'default', - dup_host_ip_addr_detection_timeout => 'default', - anycast_gateway_mac => 'default', - dup_host_mac_detection_host_moves => 'default', - dup_host_mac_detection_timeout => 'default', - ", - resource_props: { + manifest_props: { + dup_host_ip_addr_detection_host_moves: 'default', + dup_host_ip_addr_detection_timeout: 'default', + anycast_gateway_mac: 'default', + dup_host_mac_detection_host_moves: 'default', + dup_host_mac_detection_timeout: 'default', + }, + resource: { 'dup_host_ip_addr_detection_host_moves' => '5', 'dup_host_ip_addr_detection_timeout' => '180', 'dup_host_mac_detection_host_moves' => '5', @@ -120,15 +54,17 @@ }, } -tests['non_default_properties'] = { - manifest_props: " - anycast_gateway_mac => '1234.3456.5678', - dup_host_ip_addr_detection_host_moves => '200', - dup_host_ip_addr_detection_timeout => '20', - dup_host_mac_detection_host_moves => '200', - dup_host_mac_detection_timeout => '20', - ", - resource_props: { +tests[:non_default] = { + desc: '2.1 Non Default Properties', + title_pattern: 'default', + manifest_props: { + anycast_gateway_mac: '1234.3456.5678', + dup_host_ip_addr_detection_host_moves: '200', + dup_host_ip_addr_detection_timeout: '20', + dup_host_mac_detection_host_moves: '200', + dup_host_mac_detection_timeout: '20', + }, + resource: { 'anycast_gateway_mac' => '1234.3456.5678', 'dup_host_ip_addr_detection_host_moves' => '200', 'dup_host_ip_addr_detection_timeout' => '20', @@ -137,72 +73,23 @@ }, } -################################################################# -# HELPER FUNCTIONS -################################################################# - -# Full command string for puppet resource command -def puppet_resource_cmd - PUPPET_BINPATH + 'resource cisco_overlay_global' -end - -def build_manifest_overlay_global(tests, id) - manifest = tests[id][:manifest_props] - tests[id][:resource] = tests[id][:resource_props] - - tests[id][:title_pattern] = id if tests[id][:title_pattern].nil? - logger.debug("build_manifest_overlay_global :: title_pattern:\n" + - tests[id][:title_pattern]) - tests[id][:manifest] = "cat <#{PUPPETMASTER_MANIFESTPATH} - node 'default' { - cisco_overlay_global { 'default': - #{manifest} - } - } -EOF" -end - -def test_harness_overlay_global(tests, id) - return unless platform_supports_test(tests, id) - tests[id][:resource_cmd] = puppet_resource_cmd - - # Build the manifest for this test - build_manifest_overlay_global(tests, id) - - # For full test support of properties use test_harness_common; as an - # alternative use direct calls to individual test_* wrapper methods: - # test_harness_common(tests, id) - test_manifest(tests, id) - test_resource(tests, id) - test_idempotence(tests, id) -end - -def testbed_clean(agent) - # TBD: config_find_remove(agent, 'nv overlay evpn', 'incl ^nv') - command_config(agent, 'no nv overlay evpn', 'no nv overlay evpn') +def cleanup(agent) + config_find_remove(agent, 'nv overlay evpn', 'incl ^nv') end ################################################################# # TEST CASE EXECUTION ################################################################# -test_name "TestCase :: #{testheader}" do - # ------------- - testbed_clean(agent) - - # ------------------------------------------------------------------- - logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") - - id = 'default_properties' - tests[id][:desc] = '1.1 Default Properties' - test_harness_overlay_global(tests, id) +test_name "TestCase :: #{tests[:resource_name]}" do + teardown do + cleanup(agent) + vdc_limit_f3_no_intf_needed(:clear) + end + cleanup(agent) + vdc_limit_f3_no_intf_needed(:set) # ------------------------------------------------------------------- - logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") - id = 'non_default_properties' - tests[id][:desc] = '2.1 Non Default Properties' - test_harness_overlay_global(tests, id) - - testbed_clean(agent) + test_harness_run(tests, :default) + test_harness_run(tests, :non_default) end - -logger.info("TestCase :: #{testheader} :: End") +logger.info("TestCase :: #{tests[:resource_name]} :: End") From 3ee60085fc93ee458785f8fe22336d552a7904d0 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Wed, 24 Aug 2016 09:20:33 -0400 Subject: [PATCH 094/203] Rel140/refactor package tests (#382) * Refactor beaker package tests * Remove unused code * Rubocop fix * Uncomment filename * Add ensure_prop_override setting * Rubocop fix --- .../file_service_package/filesvcpkglib.rb | 38 ------ .../package_provider_nondefaults_2.rb | 123 ------------------ .../file_service_package/test_package.rb | 86 ++++++++++++ tests/beaker_tests/lib/utilitylib.rb | 14 +- 4 files changed, 98 insertions(+), 163 deletions(-) delete mode 100644 tests/beaker_tests/file_service_package/package_provider_nondefaults_2.rb create mode 100755 tests/beaker_tests/file_service_package/test_package.rb diff --git a/tests/beaker_tests/file_service_package/filesvcpkglib.rb b/tests/beaker_tests/file_service_package/filesvcpkglib.rb index 03188d0eb..7e7f10f7a 100644 --- a/tests/beaker_tests/file_service_package/filesvcpkglib.rb +++ b/tests/beaker_tests/file_service_package/filesvcpkglib.rb @@ -135,44 +135,6 @@ def self.create_package_curl_manifest_latest ensure => latest, } } -EOF" - manifest_str - end - - # Method to create a manifest for PKG resource attribute 'ensure' where - # 'ensure' is set to present. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_package_sample_manifest_present - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - package { 'n9000_sample.x86_64': - name => 'n9000_sample-1.0.0-7.0.3.x86_64.rpm', - ensure => present, - provider => 'cisco', - source => '/bootflash/n9000_sample-1.0.0-7.0.3.x86_64.rpm', - package_settings => {'target' => 'host'}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for PKG resource attribute 'ensure' where - # 'ensure' is set to absent. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_package_sample_manifest_absent - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - package { 'n9000_sample.x86_64': - name => 'n9000_sample-1.0.0-7.0.3.x86_64.rpm', - ensure => absent, - provider => 'cisco', - source => '/bootflash/n9000_sample-1.0.0-7.0.3.x86_64.rpm', - package_settings => {'target' => 'host'}, - } -} EOF" manifest_str end diff --git a/tests/beaker_tests/file_service_package/package_provider_nondefaults_2.rb b/tests/beaker_tests/file_service_package/package_provider_nondefaults_2.rb deleted file mode 100644 index fc93889c1..000000000 --- a/tests/beaker_tests/file_service_package/package_provider_nondefaults_2.rb +++ /dev/null @@ -1,123 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# -------------- -# Package-Provider-NonDefaults-2.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet PACKAGE resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a PACKAGE resource test that tests for nondefault values for -# name, ensure, provider and source attributes of a -# package resource when installed with 'ensure' => 'present'. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-3 deal with package resource installation and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and FileSvcPkgLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../filesvcpkglib.rb', __FILE__) - -result = 'PASS' -testheader = 'PACKAGE Resource :: All Attributes NonDefaults' - -skipmsg = "\n\n*** WARNING ***\nThis test case relies on patches that are " \ - "built for specific image versions.\nMake sure the patch being tested is " \ - 'compatible with the current image and then comment out this ' \ - "raise_skip_exception call to run the test.\n*** WARNING ***\n" -raise_skip_exception(skipmsg, self) - -# @test_name [TestCase] Executes nondefaults testcase for PACKAGE Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - cmd_str = - get_vshell_cmd('dir bootflash:n9000_sample-1.0.0-7.0.3.x86_64.rpm') - on(agent, cmd_str, acceptable_exit_codes: [0]) - - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, FileSvcPkgLib.create_package_sample_manifest_absent) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - # No change would imply that Sample package is uninstalled prior to test. - # Or expected exit_code is 2 since this is a puppet agent cmd with change. - # Change would imply that Sample package is installed prior to test. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource present manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, FileSvcPkgLib.create_package_sample_manifest_present) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - # Change would imply that Sample package is uninstalled prior to test and - # installed after test. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource present manifest from master :: #{result}") - end - - # @step [Step] Checks package resource on agent using resource cmd. - step 'TestStep :: Check package resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - # Sample package state should not be purged. - cmd_str = PUPPET_BINPATH + "resource package 'n9000_sample'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'purged' }, - true, self, logger) - end - - logger.info("Check package resource presence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/file_service_package/test_package.rb b/tests/beaker_tests/file_service_package/test_package.rb new file mode 100755 index 000000000..f21744b87 --- /dev/null +++ b/tests/beaker_tests/file_service_package/test_package.rb @@ -0,0 +1,86 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +case image? +when /7.0.3.I2.1/ + filename = 'n9000_sample-1.0.0-7.0.3.x86_64.rpm' + name = 'n9000_sample' + version = '1.0.0-7.0.3' +when /7.0.3.I3.1/ + filename = 'CSCuxdublin-1.0.0-7.0.3.I3.1.lib32_n9000.rpm' + name = 'CSCuxdublin' + version = '1.0.0-7.0.3.I3.1' +when /7.0.3.I4.1/ + filename = 'nxos.sample-n9k_EOR-1.0.0-7.0.3.I4.1.lib32_n9000.rpm' + name = 'nxos.sample-n9k_EOR' + version = '1.0.0-7.0.3.I4.1' +when /7.0.3.I5/ + filename = 'nxos.sample-n9k_EOR-1.0.0-7.0.3.I5.1.lib32_n9000.rpm' + name = 'nxos.sample-n9k_EOR' + version = '1.0.0-7.0.3.I5.1' +else + raise_skip_exception("No patch specified for image #{image?}", self) +end + +unless resource_present?(agent, 'file', "/bootflash/#{filename}") + raise_skip_exception("RPM file /bootflash/#{filename} not found", self) +end + +tests = { + agent: agent, + master: master, + platform: 'n(3|8|9)k', + resource_name: 'package', +} + +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + +tests[:yum_patch] = { + desc: "1.1 Apply sample patch to image #{image?}", + title_pattern: name, + ensure_prop_override: true, + manifest_props: { + name: filename, + provider: 'cisco', + source: "/bootflash/#{filename}", + package_settings: { 'target' => 'host' }, + }, + resource: { + 'ensure' => version + }, +} + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + teardown { resource_absent_by_title(agent, 'package', name) } + resource_absent_by_title(agent, 'package', name) + + # ------------------------------------------------------------------- + test_harness_run(tests, :yum_patch) +end +logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index b919406ed..012b3be0e 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -306,6 +306,13 @@ def resource_titles(agent, res_name, action=:find) titles end +# Helper to determine if a resource is present +def resource_present?(agent, name, title) + cmd = PUPPET_BINPATH + "resource #{name} #{title}" + result = on(agent, cmd).stdout + result[/ensure => 'absent'/] ? false : true +end + # Helper to configure switchport mode def config_switchport_mode(agent, intf, mode, stepinfo='switchport mode: ') step "TestStep :: #{stepinfo}" do @@ -592,8 +599,11 @@ def create_manifest_and_resource(tests, id) tests[id][:resource] = { 'ensure' => 'absent' } else state = 'ensure => present,' unless tests[:ensurable] == false - tests[id][:resource]['ensure'] = nil unless - tests[id][:resource].nil? || tests[:ensurable] == false + + unless tests[id][:ensure_prop_override] + tests[id][:resource]['ensure'] = nil unless + tests[id][:resource].nil? || tests[:ensurable] == false + end manifest_props = tests[id][:manifest_props] if manifest_props From 59411c0e5be6e292e9550bcbb178624b5dac33e7 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Wed, 24 Aug 2016 13:49:07 -0400 Subject: [PATCH 095/203] Fix test_itd_service * The cleanup in the dependency manifest was turning off 'feature itd' but that needs to be re-enabled in order to configure the itd device-group dependency --- .../cisco_itd_service/test_itd_service.rb | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/tests/beaker_tests/cisco_itd_service/test_itd_service.rb b/tests/beaker_tests/cisco_itd_service/test_itd_service.rb index 63388e8f1..e85d0e488 100644 --- a/tests/beaker_tests/cisco_itd_service/test_itd_service.rb +++ b/tests/beaker_tests/cisco_itd_service/test_itd_service.rb @@ -78,7 +78,6 @@ def find_ingress_ethernet_interface(tests) desc: '1.2 Defaults for n7k', platform: 'n7k', title_pattern: 'myService', - preclean: 'cisco_itd_service', manifest_props: { device_group: 'udpGroup', ingress_interface: 'default', @@ -95,7 +94,6 @@ def find_ingress_ethernet_interface(tests) desc: '1.3 Defaults for n9k', platform: 'n9k', title_pattern: 'myService', - preclean: 'cisco_itd_service', manifest_props: { fail_action: 'default', peer_local: 'default', @@ -110,12 +108,9 @@ def find_ingress_ethernet_interface(tests) vip = ['ip 3.3.3.3 255.0.0.0 tcp 500 advertise enable'] pv = %w(myVdc1 pvservice) -# Non-default Tests. NOTE: [:resource] = [:manifest_props] for all non-default - tests[:non_default] = { desc: '2.1 Common Non Defaults', title_pattern: 'myService', - preclean: 'cisco_itd_service', manifest_props: { access_list: 'iap', device_group: 'udpGroup', @@ -138,7 +133,6 @@ def find_ingress_ethernet_interface(tests) desc: '2.2 Non Defaults for n7k', platform: 'n7k', title_pattern: 'myService', - preclean: 'cisco_itd_service', manifest_props: { device_group: 'udpGroup', ingress_interface: ingress_intf, @@ -158,7 +152,6 @@ def find_ingress_ethernet_interface(tests) desc: '2.3 Non Defaults for n9k', platform: 'n9k', title_pattern: 'myService', - preclean: 'cisco_itd_service', manifest_props: { device_group: 'udpGroup', ingress_interface: ingress_intf, @@ -175,7 +168,6 @@ def find_ingress_ethernet_interface(tests) tests[:non_default_shut] = { desc: '3.1 Common create service and turn it on', title_pattern: 'myService', - preclean: 'cisco_itd_service', manifest_props: { device_group: 'udpGroup', ingress_interface: [[@ingress_eth_int, '2.2.2.2']], @@ -226,19 +218,19 @@ def cleanup(agent) end # Overridden to properly handle dependencies for this test file. -def test_harness_dependencies(_tests, id) - return if id[/non_default_shut_/] +def test_harness_dependencies(_tests, _id) cleanup(agent) cmd = [ + 'feature itd', + 'feature interface-vlan', 'ip access-list iap ; ip access-list eap', "interface #{@ingress_eth_int} ; no switchport", - 'feature interface-vlan', 'vlan 2 ; interface vlan 2', 'interface port-channel 100 ; no switchport', 'itd device-group udpGroup ; node ip 1.1.1.1', ].join(' ; ') - command_config(agent, cmd, cmd) + test_set(agent, cmd) end ################################################################# From 34a3017eda14ef9f56ba2e44825c9a7ede7c2526 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Wed, 24 Aug 2016 15:44:00 -0400 Subject: [PATCH 096/203] beaker: Remove raise_skip call from summary checker --- tests/beaker_tests/lib/utilitylib.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 012b3be0e..7ed68d970 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1130,7 +1130,10 @@ def skipped_tests_summary(tests) tests[:skipped].each do |desc| logger.error(sprintf('%-40s :: SKIP', desc)) end - raise_skip_exception(tests[:resource_name], self) + # There are many tests now that skip a sub-test or two while the majority + # are still processed. We prefer to see the overall result as Pass instead + # of skip so skip the raise below. + # raise_skip_exception(tests[:resource_name], self) end # TBD: This needs to be more selective when used with modular platforms, From 70623b0bbedb57246a5cad81f0e0be35253c6420 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Thu, 25 Aug 2016 15:43:30 -0400 Subject: [PATCH 097/203] test_interface_ospf dependency fix --- .../cisco_interface_ospf/test_interface_ospf.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb index 1bd30adf6..fb2026142 100644 --- a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb +++ b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb @@ -106,13 +106,9 @@ }, } -def dependency_manifest(_tests, id) +def test_harness_dependencies(_tests, id) return unless id == :default - " - cisco_ospf { 'sample': - ensure => present - } - " + test_set(agent, 'feature ospf') end def cleanup(agent, intf) From f1905a24fe1f4e3f9d512b5b6b263ec6fe6a30f3 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Thu, 25 Aug 2016 16:52:43 -0400 Subject: [PATCH 098/203] Add :array opt to test_get bkr utility --- tests/beaker_tests/lib/utilitylib.rb | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 7ed68d970..c66fa844b 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -838,10 +838,21 @@ def config_find_remove(agent, find=[], filter='incl .*') # test_get does a 'show runn' but requires a filter. # Example: # test_get(agent, 'incl ^vlan') -def test_get(agent, filter) +# +# opt = :raw, return raw output from puppet resource command +# opt = :array, return array of test_get property data only +def test_get(agent, filter, opt=:raw) cmd_prefix = PUPPET_BINPATH + "resource cisco_command_config 'cc' " on(agent, cmd_prefix + "test_get=\"#{filter}\"") - stdout + case opt + when :raw + return stdout + when :array + # clean up the output and return as array of commands + # stdout: " cisco_command_config { 'cc':\n test_get => '\n foo\n bar\n',\n}" + # array: [' foo', ' bar'] + stdout.split("\n")[2..-3] if stdout + end end # Add arbitrary configurations using command_config's test_set property. From c3ab4f65865c50756d6f9c206b316a4f44175b4b Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Mon, 29 Aug 2016 12:23:38 -0400 Subject: [PATCH 099/203] Enable yum package testing in guestshell (#384) * Initial gs package test support * Minor refactoring * Add camden i2.1 patch details * Use specific yum patch for camden fcs * Separate native and gs test flow * Remove ensure absent in manifest_props * Enable additional releases * I5 patch fix --- .../file_service_package/test_package.rb | 67 ++++++++++++++----- tests/beaker_tests/lib/utilitylib.rb | 55 +++++++++++++++ 2 files changed, 106 insertions(+), 16 deletions(-) diff --git a/tests/beaker_tests/file_service_package/test_package.rb b/tests/beaker_tests/file_service_package/test_package.rb index f21744b87..391b13453 100755 --- a/tests/beaker_tests/file_service_package/test_package.rb +++ b/tests/beaker_tests/file_service_package/test_package.rb @@ -23,25 +23,31 @@ ############################################################################### require File.expand_path('../../lib/utilitylib.rb', __FILE__) +name = 'nxos.sample-n9k_EOR' case image? when /7.0.3.I2.1/ - filename = 'n9000_sample-1.0.0-7.0.3.x86_64.rpm' + # Version 7.0.3.I2.1 needs this specific patch. Attempts to build + # new patches for this version with the patch tool don't work. name = 'n9000_sample' + filename = 'n9000_sample-1.0.0-7.0.3.x86_64.rpm' version = '1.0.0-7.0.3' +when /7.0.3.I2.2e/ + filename = 'nxos.sample-n9k_EOR-1.0.0-7.0.3.I2.2e.lib32_n9000.rpm' + version = '1.0.0-7.0.3.I2.2e' when /7.0.3.I3.1/ - filename = 'CSCuxdublin-1.0.0-7.0.3.I3.1.lib32_n9000.rpm' - name = 'CSCuxdublin' + filename = 'nxos.sample-n9k_EOR-1.0.0-7.0.3.I3.1.lib32_n9000.rpm' version = '1.0.0-7.0.3.I3.1' when /7.0.3.I4.1/ filename = 'nxos.sample-n9k_EOR-1.0.0-7.0.3.I4.1.lib32_n9000.rpm' - name = 'nxos.sample-n9k_EOR' version = '1.0.0-7.0.3.I4.1' +when /7.0.3.I4.2/ + filename = 'nxos.sample-n9k_EOR-1.0.0-7.0.3.I4.2.lib32_n9000.rpm' + version = '1.0.0-7.0.3.I4.2' when /7.0.3.I5/ filename = 'nxos.sample-n9k_EOR-1.0.0-7.0.3.I5.1.lib32_n9000.rpm' - name = 'nxos.sample-n9k_EOR' version = '1.0.0-7.0.3.I5.1' else - raise_skip_exception("No patch specified for image #{image?}", self) + raise_skip_exception("No patch available for image #{image?}", self) end unless resource_present?(agent, 'file', "/bootflash/#{filename}") @@ -58,29 +64,58 @@ # Skip -ALL- tests if a top-level platform/os key exludes this platform skip_unless_supported(tests) -tests[:yum_patch] = { - desc: "1.1 Apply sample patch to image #{image?}", - title_pattern: name, - ensure_prop_override: true, - manifest_props: { +tests[:yum_patch_install] = { + desc: "1.1 Apply sample patch to image #{image?}", + title_pattern: name, + manifest_props: { name: filename, provider: 'cisco', source: "/bootflash/#{filename}", package_settings: { 'target' => 'host' }, }, - resource: { + resource: { 'ensure' => version }, } +tests[:yum_patch_remove] = { + desc: '1.2 Remove sample patch', + code: [0, 2], + ensure: :absent, + title_pattern: name, + manifest_props: { + name: filename, + provider: 'cisco', + package_settings: { 'target' => 'host' }, + }, +} + ################################################################# # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - teardown { resource_absent_by_title(agent, 'package', name) } - resource_absent_by_title(agent, 'package', name) + if virtual == 'lxc' + # The puppet resource command cannot be used in the guestshell + # to query patches that are applied to the host. This test will + # call explicit api's to test the following for guestshell: + # 1) Apply manifest. + # 2) Verify patch is applied on the host. + # 3) Idempotence Test. + create_package_manifest_resource(tests, :yum_patch_install) + create_package_manifest_resource(tests, :yum_patch_remove) + + teardown { test_manifest(tests, :yum_patch_remove) } + test_manifest(tests, :yum_patch_remove) + + test_manifest(tests, :yum_patch_install) + test_patch_version(tests, :yum_patch_install, name, version) + test_idempotence(tests, :yum_patch_install) + else + teardown { resource_absent_by_title(agent, 'package', name) } + resource_absent_by_title(agent, 'package', name) - # ------------------------------------------------------------------- - test_harness_run(tests, :yum_patch) + tests[:yum_patch_install][:ensure_prop_override] = true + test_harness_run(tests, :yum_patch_install) + end end logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index c66fa844b..05c8347dc 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -633,6 +633,31 @@ def create_manifest_and_resource(tests, id) true end +# Special create manifest/resource method for yum packages only. +def create_package_manifest_resource(tests, id) + puppet_resource_cmd_from_params(tests, id) + state = '' + manifest = '' + if tests[id][:ensure] == :absent + state = 'ensure => absent,' + else + state = 'ensure => present,' + end + + manifest_props = tests[id][:manifest_props] + manifest += prop_hash_to_manifest(manifest_props) + tests[id][:resource] = manifest_props + + tests[id][:manifest] = "cat <#{PUPPETMASTER_MANIFESTPATH} + \nnode default { + #{dependency_manifest(tests, id)} + #{tests[:resource_name]} { '#{tests[id][:title_pattern]}': + #{state}\n#{manifest} + }\n}\nEOF" + + true +end + # test_harness_dependencies # # This method is used for additional testbed setup beyond the basics @@ -1016,6 +1041,12 @@ def os_family @os_family = on(agent, facter_cmd('os.family')).stdout.chomp end +@virtual = nil +def virtual + return @virtual unless @virtual.nil? + @virtual = on(agent, facter_cmd('virtual')).stdout.chomp +end + # Used to cache the cisco hardware type @cisco_hardware = nil # Use facter to return cisco hardware type @@ -1390,3 +1421,27 @@ def remove_all_vrfs(agent) found.map! { |cmd| "no #{cmd}" if cmd[/^vrf context/] } test_set(agent, found.compact.join(' ; ')) end + +# Return yum patch version from host +def get_patch_version(name) + cmd = get_vshell_cmd("show install packages | inc #{name}") + # Sample Output: + # nxos.sample-n9k_EOR.lib32_n9000 1.0.0-7.0.3.I5.1 @patching + out = on(agent, cmd, pty: true).stdout[/\S+\s+(\S+).*@patching/] + out.nil? ? nil : Regexp.last_match[1] +end + +# Test yum version +def test_patch_version(tests, id, name, ver) + stepinfo = format_stepinfo(tests, id, 'YUM PACKAGE VERSION') + step "TestStep :: #{stepinfo}" do + logger.debug("test_yum_version :: #{ver}") + iv = get_patch_version(name) + if iv == ver + logger.info("#{stepinfo} :: PASS") + else + msg = "Installed version: #{iv}, does not match expected ver: #{ver}" + fail_test("TestStep :: #{msg} :: FAIL") + end + end +end From 78e3e5d82f89d3e28ee526b9add498cc6027cba5 Mon Sep 17 00:00:00 2001 From: mikewiebe Date: Mon, 29 Aug 2016 21:23:02 -0400 Subject: [PATCH 100/203] Use single workflow for gs and native --- .../file_service_package/test_package.rb | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/tests/beaker_tests/file_service_package/test_package.rb b/tests/beaker_tests/file_service_package/test_package.rb index 391b13453..f38e8459e 100755 --- a/tests/beaker_tests/file_service_package/test_package.rb +++ b/tests/beaker_tests/file_service_package/test_package.rb @@ -94,28 +94,21 @@ # TEST CASE EXECUTION ################################################################# test_name "TestCase :: #{tests[:resource_name]}" do - if virtual == 'lxc' - # The puppet resource command cannot be used in the guestshell - # to query patches that are applied to the host. This test will - # call explicit api's to test the following for guestshell: - # 1) Apply manifest. - # 2) Verify patch is applied on the host. - # 3) Idempotence Test. - create_package_manifest_resource(tests, :yum_patch_install) - create_package_manifest_resource(tests, :yum_patch_remove) + # The puppet resource command cannot be used in the guestshell + # to query patches that are applied to the host. This test will + # call explicit api's to test the following for both the native + # and guestshell workflow: + # 1) Apply manifest. + # 2) Verify patch is applied on the host. + # 3) Idempotence Test. + create_package_manifest_resource(tests, :yum_patch_install) + create_package_manifest_resource(tests, :yum_patch_remove) - teardown { test_manifest(tests, :yum_patch_remove) } - test_manifest(tests, :yum_patch_remove) + teardown { test_manifest(tests, :yum_patch_remove) } + test_manifest(tests, :yum_patch_remove) - test_manifest(tests, :yum_patch_install) - test_patch_version(tests, :yum_patch_install, name, version) - test_idempotence(tests, :yum_patch_install) - else - teardown { resource_absent_by_title(agent, 'package', name) } - resource_absent_by_title(agent, 'package', name) - - tests[:yum_patch_install][:ensure_prop_override] = true - test_harness_run(tests, :yum_patch_install) - end + test_manifest(tests, :yum_patch_install) + test_patch_version(tests, :yum_patch_install, name, version) + test_idempotence(tests, :yum_patch_install) end logger.info("TestCase :: #{tests[:resource_name]} :: End") From 375c2a103c857c14d5baee17612f68e4b391e5ba Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Tue, 30 Aug 2016 11:16:02 -0400 Subject: [PATCH 101/203] Convert test_snmp_server to new syntax (#385) * Convert test_snmp_server to new syntax * packetsize was failing on n7k so I just rewrote the test to use the new format * tested on n7k,n9k-I5 * Fix pktsize cleanup --- .../snmpserver_provider_defaults.rb | 99 ---------- .../snmpserver_provider_nondefaults.rb | 133 ------------- .../cisco_snmp_server/snmpserverlib.rb | 182 ------------------ .../cisco_snmp_server/test_snmp_server.rb | 104 ++++++++++ 4 files changed, 104 insertions(+), 414 deletions(-) delete mode 100644 tests/beaker_tests/cisco_snmp_server/snmpserver_provider_defaults.rb delete mode 100644 tests/beaker_tests/cisco_snmp_server/snmpserver_provider_nondefaults.rb delete mode 100644 tests/beaker_tests/cisco_snmp_server/snmpserverlib.rb create mode 100644 tests/beaker_tests/cisco_snmp_server/test_snmp_server.rb diff --git a/tests/beaker_tests/cisco_snmp_server/snmpserver_provider_defaults.rb b/tests/beaker_tests/cisco_snmp_server/snmpserver_provider_defaults.rb deleted file mode 100644 index a12857f98..000000000 --- a/tests/beaker_tests/cisco_snmp_server/snmpserver_provider_defaults.rb +++ /dev/null @@ -1,99 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# SnmpServer-Provider-Defaults.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet SNMPSERVER resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a SNMPSERVER resource test that tests for default values for -# aaa_user_cache_timeout, global_enforce_priv, packet_size, protocol, -# tcp_session_auth, contact and location attributes of a -# cisco_snmp_server resource. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-4 deal with cisco_snmp_server_resource defaults and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and SnmpServerLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../snmpserverlib.rb', __FILE__) - -result = 'PASS' -testheader = 'SNMPSERVER Resource :: All Attributes Defaults' - -# @test_name [TestCase] Executes defaults testcase for SNMPSERVER Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource defaults manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpServerLib.create_snmpserver_manifest_defaults) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str) - - logger.info("Get resource defaults manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_snmp_server resource on agent using resource cmd. - step 'TestStep :: Check cisco_snmp_server resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + 'resource cisco_snmp_server' - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'aaa_user_cache_timeout' => '3600', - 'global_enforce_priv' => 'false', - 'packet_size' => '1500', - 'protocol' => 'true', - 'tcp_session_auth' => 'true' }, - false, self, logger) - end - - logger.info("Check cisco_snmp_server resource presence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_snmp_server/snmpserver_provider_nondefaults.rb b/tests/beaker_tests/cisco_snmp_server/snmpserver_provider_nondefaults.rb deleted file mode 100644 index 7a79763e5..000000000 --- a/tests/beaker_tests/cisco_snmp_server/snmpserver_provider_nondefaults.rb +++ /dev/null @@ -1,133 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# ------------- -# SnmpServer-Provider-NonDefaults.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet SNMPSERVER resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a SNMPSERVER resource test that tests for nondefault values for -# aaa_user_cache_timeout, global_enforce_priv, packet_size, protocol, -# tcp_session_auth, contact and location attributes of a -# cisco_snmp_server resource. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-4 deal with cisco_snmp_server_resource change and its -# verification using Puppet Agent and the switch running-config. -# Steps 5-7 deal with cisco_snmp_server_resource defaults and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and SnmpServerLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../snmpserverlib.rb', __FILE__) - -result = 'PASS' -testheader = 'SNMPSERVER Resource :: All Attributes NonDefaults' - -# @test_name [TestCase] Executes nondefaults testcase for SNMPSERVER Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource nondefaults manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpServerLib.create_snmpserver_manifest_nondefaults) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource nondefaults manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_snmp_server resource on agent using resource cmd. - step 'TestStep :: Check cisco_snmp_server resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + 'resource cisco_snmp_server' - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'aaa_user_cache_timeout' => '1000', - 'global_enforce_priv' => 'true', - 'packet_size' => '2500', - 'protocol' => 'false', - 'tcp_session_auth' => 'false', - 'contact' => 'user1', - 'location' => 'rtp' }, - false, self, logger) - end - - logger.info("Check cisco_snmp_server resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource defaults manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SnmpServerLib.create_snmpserver_manifest_defaults) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource defaults manifest from master :: #{result}") - end - - # @step [Step] Checks cisco_snmp_server resource on agent using resource cmd. - step 'TestStep :: Check cisco_snmp_server resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + 'resource cisco_snmp_server' - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'aaa_user_cache_timeout' => '3600', - 'global_enforce_priv' => 'false', - 'packet_size' => '1500', - 'protocol' => 'true', - 'tcp_session_auth' => 'true' }, - false, self, logger) - end - - logger.info("Check cisco_snmp_server resource presence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/cisco_snmp_server/snmpserverlib.rb b/tests/beaker_tests/cisco_snmp_server/snmpserverlib.rb deleted file mode 100644 index 5e34cf830..000000000 --- a/tests/beaker_tests/cisco_snmp_server/snmpserverlib.rb +++ /dev/null @@ -1,182 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### - -# Require UtilityLib.rb path. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) - -# SNMPSERVER Utility Library: -# --------------------------- -# snmpserverlib.rb -# -# This is the utility library for the SNMPSERVER provider Beaker test cases that -# contains the common methods used across the SNMPSERVER testsuite's cases. The -# library is implemented as a module with related methods and constants defined -# inside it for use as a namespace. All of the methods are defined as module -# methods. -# -# Every Beaker SNMPSERVER test case that runs an instance of Beaker::TestCase -# requires SnmpServerLib module. -# -# The module has a single set of methods: -# A. Methods to create manifests for cisco_snmp_server Puppet provider test cases. -module SnmpServerLib - # Group of Constants used in negative tests for SNMPSERVER provider. - PACKETSIZE_NEGATIVE = '-1' - AAATIMEOUT_NEGATIVE = '-1' - TCPAUTH_NEGATIVE = 'unknown' - PROTOCOL_NEGATIVE = 'unknown' - GLOBALPRIV_NEGATIVE = 'unknown' - - # A. Methods to create manifests for cisco_snmp_server Puppet provider test cases. - - # Method to create a manifest for SNMPSERVER resource attributes: - # aaa_user_cache_timeout, global_enforce_priv, packet_size, - # protocol, tcp_session_auth, contact and location. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_snmpserver_manifest_defaults - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_snmp_server { 'default': - packet_size => 'default', - aaa_user_cache_timeout => 'default', - tcp_session_auth => 'default', - protocol => 'default', - global_enforce_priv => 'default', - contact => 'default', - location => 'default', - } -} -EOF" - manifest_str - end - - # Helper method for comparing all of the snmpserver properties against their - # expected default settings. - def self.match_default_cli(output, testcase, logger) - # Match strings - ['snmp-server aaa-user cache-timeout 3600', - 'snmp-server aaa-user cache-timeout 3600', - 'snmp-server packetsize 1500', - 'snmp-server protocol enable', - 'snmp-server tcp-session auth', - 'snmp-server globalEnforcePriv', - ].each do |pattern| - search_pattern_in_output(output, [/#{Regexp.quote(pattern)}/], - false, testcase, logger) - end - - # Refute strings - ['snmp-server contact', - 'snmp-server location', - ].each do |pattern| - search_pattern_in_output(output, [/#{Regexp.quote(pattern)}/], - true, testcase, logger) - end - end - - # Method to create a manifest for SNMPSERVER resource attributes: - # aaa_user_cache_timeout, global_enforce_priv, packet_size, - # protocol, tcp_session_auth, contact and location. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_snmpserver_manifest_nondefaults - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_snmp_server { 'default': - packet_size => 2500, - aaa_user_cache_timeout => 1000, - tcp_session_auth => false, - protocol => false, - global_enforce_priv => true, - contact => 'user1', - location => 'rtp', - } -} -EOF" - manifest_str - end - - # Method to create a manifest for SNMPSERVER resource attribute 'packet_size'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_snmpserver_manifest_packetsize_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_snmp_server { 'test': - packet_size => #{SnmpServerLib::PACKETSIZE_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for SNMPSERVER resource attribute 'aaa_cache_timeout'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_snmpserver_manifest_aaatimeout_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_snmp_server { 'test': - aaa_user_cache_timeout => #{SnmpServerLib::AAATIMEOUT_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for SNMPSERVER resource attribute 'tcp_session_auth'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_snmpserver_manifest_tcpauth_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_snmp_server { 'test': - tcp_session_auth => #{SnmpServerLib::TCPAUTH_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for SNMPSERVER resource attribute 'protocol'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_snmpserver_manifest_protocol_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_snmp_server { 'test': - protocol => #{SnmpServerLib::PROTOCOL_NEGATIVE}, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for SNMPSERVER resource attribute 'global_priv'. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_snmpserver_manifest_globalpriv_negative - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - cisco_snmp_server { 'test': - global_enforce_priv => #{SnmpServerLib::GLOBALPRIV_NEGATIVE}, - } -} -EOF" - manifest_str - end -end diff --git a/tests/beaker_tests/cisco_snmp_server/test_snmp_server.rb b/tests/beaker_tests/cisco_snmp_server/test_snmp_server.rb new file mode 100644 index 000000000..7ffcb827b --- /dev/null +++ b/tests/beaker_tests/cisco_snmp_server/test_snmp_server.rb @@ -0,0 +1,104 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# Test hash top-level keys +tests = { + agent: agent, + master: master, + ensurable: false, + resource_name: 'cisco_snmp_server', +} + +@def_pkt_size = platform[/n(3|8|9)k/] ? '1500' : '0' + +# Test hash test cases +tests[:default] = { + desc: '1.1 Default Properties', + code: [0], + title_pattern: 'default', + manifest_props: { + aaa_user_cache_timeout: 'default', + contact: 'default', + global_enforce_priv: 'default', + location: 'default', + packet_size: 'default', + protocol: 'default', + tcp_session_auth: 'default', + }, + resource: { + 'aaa_user_cache_timeout' => '3600', + # 'contact' => n/a, + 'global_enforce_priv' => 'false', + # 'location' => n/a, + 'packet_size' => @def_pkt_size, + 'protocol' => 'true', + 'tcp_session_auth' => 'true', + }, +} + +tests[:non_default] = { + desc: '2.1 Non Default Properties', + title_pattern: 'default', + manifest_props: { + aaa_user_cache_timeout: 1000, + contact: 'beaker', + global_enforce_priv: true, + location: 'beaker', + packet_size: 2500, + protocol: false, + tcp_session_auth: false, + }, +} + +def cleanup(agent) + # Restore testbed to default state + cmds = [ + 'no snmp-server aaa-user cache-timeout 3600', + 'no snmp-server contact', + 'no snmp-server globalEnforcePriv', + 'no snmp-server location', + 'no snmp-server packetsize 3600', + 'snmp-server protocol enable', + 'snmp-server tcp-session', + ].join(' ; ') + test_set(agent, cmds) +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(agent) } + cleanup(agent) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + test_harness_run(tests, :default) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + test_harness_run(tests, :non_default) +end +logger.info("TestCase :: #{tests[:resource_name]} :: End") From 9ab0bd22f95b7efcbe644213afc8e32bdca6bf0f Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Wed, 31 Aug 2016 08:15:40 -0400 Subject: [PATCH 102/203] Rename test_package.rb -> test_package_patch.rb --- .../{test_package.rb => test_package_patch.rb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/beaker_tests/file_service_package/{test_package.rb => test_package_patch.rb} (100%) diff --git a/tests/beaker_tests/file_service_package/test_package.rb b/tests/beaker_tests/file_service_package/test_package_patch.rb similarity index 100% rename from tests/beaker_tests/file_service_package/test_package.rb rename to tests/beaker_tests/file_service_package/test_package_patch.rb From 2641c7a1c821b9b512510ad3a2952f5dbfe7c0fd Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Thu, 1 Sep 2016 16:33:24 -0400 Subject: [PATCH 103/203] Converted the old style tests to new syntax. (#386) Most of these are straightforward changes. The package test is complicated on native since third-party rpms require setting up a yum repo, but I set up the test to do the repo init via puppet so we actually get a bit more coverage this way. * Tested on N5,N7,N9-I5 using native/GS/OAC --- files/demo-one-1.0-1.x86_64.rpm | Bin 0 -> 7385 bytes .../file_provider_nondefaults.rb | 145 ----------------- .../file_service_package/filesvcpkglib.rb | 141 ----------------- .../package_provider_nondefaults_1.rb | 108 ------------- .../service_provider_nondefaults.rb | 149 ------------------ .../file_service_package/test_file.rb | 75 +++++++++ .../file_service_package/test_package.rb | 124 +++++++++++++++ .../file_service_package/test_service.rb | 76 +++++++++ tests/beaker_tests/lib/utilitylib.rb | 5 - 9 files changed, 275 insertions(+), 548 deletions(-) create mode 100755 files/demo-one-1.0-1.x86_64.rpm delete mode 100644 tests/beaker_tests/file_service_package/file_provider_nondefaults.rb delete mode 100644 tests/beaker_tests/file_service_package/filesvcpkglib.rb delete mode 100644 tests/beaker_tests/file_service_package/package_provider_nondefaults_1.rb delete mode 100644 tests/beaker_tests/file_service_package/service_provider_nondefaults.rb create mode 100755 tests/beaker_tests/file_service_package/test_file.rb create mode 100755 tests/beaker_tests/file_service_package/test_package.rb create mode 100755 tests/beaker_tests/file_service_package/test_service.rb diff --git a/files/demo-one-1.0-1.x86_64.rpm b/files/demo-one-1.0-1.x86_64.rpm new file mode 100755 index 0000000000000000000000000000000000000000..a78e5a63433d8844a4a736ec94ed52d149012675 GIT binary patch literal 7385 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 'present'. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-3 deal with file resource creation and its -# verification using Puppet Agent and the switch running-config. -# Steps 4-5 deal with file resource deletion and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and FileSvcPkgLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../filesvcpkglib.rb', __FILE__) - -result = 'PASS' -testheader = 'FILE Resource :: All Attributes NonDefaults' - -# @test_name [TestCase] Executes nondefaults testcase for FILE Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, FileSvcPkgLib.create_file_manifest_absent) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - # Or expected exit_code is 0 since this is a puppet agent cmd with no change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource nondefaults manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, FileSvcPkgLib.create_file_manifest_nondefaults) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource nondefaults manifest from master :: #{result}") - end - - # @step [Step] Checks file resource on agent using resource cmd. - step 'TestStep :: Check file resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource file '/tmp/testfile.txt'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'file', - 'content' => '{md5}[0-9a-fA-F]*', - 'group' => '0', - 'mode' => '0664', - 'owner' => '0', - 'type' => 'file' }, - false, self, logger) - end - - logger.info("Check file resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource absent manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, FileSvcPkgLib.create_file_manifest_absent) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource absent manifest from master :: #{result}") - end - - # @step [Step] Checks file resource on agent using resource cmd. - step 'TestStep :: Check file resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource file '/tmp/testfile.txt'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'file', - 'content' => '{md5}[0-9a-fA-F]*', - 'group' => '0', - 'mode' => '0664', - 'owner' => '0', - 'type' => 'file' }, - true, self, logger) - end - - logger.info("Check file resource absence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/file_service_package/filesvcpkglib.rb b/tests/beaker_tests/file_service_package/filesvcpkglib.rb deleted file mode 100644 index 7e7f10f7a..000000000 --- a/tests/beaker_tests/file_service_package/filesvcpkglib.rb +++ /dev/null @@ -1,141 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### - -# Require UtilityLib.rb path. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) - -# FILESVCPKG Utility Library: -# ---------------------------- -# filesvcpkglib.rb -# -# This is the utility library for the FILESVCPKG provider Beaker test cases that -# contains the common methods used across the FILESVCPKG testsuite's cases. The -# library is implemented as a module with related methods and constants defined -# inside it for use as a namespace. All of the methods are defined as module -# methods. -# -# Every Beaker FILESVCPKG test case that runs an instance of Beaker::TestCase -# requires FileSvcPkgLib module. -# -# The module has a single set of methods: -# A. Methods to create manifests for file, service and pkg Puppet test cases. -module FileSvcPkgLib - # A. Methods to create manifests for file, service and pkg Puppet test cases. - - # Method to create a manifest for FILE resource attributes: - # path, ensure, content, checksum, mode, owner and provider. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_file_manifest_nondefaults - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - file { 'testfile.txt': - path => '/tmp/testfile.txt', - ensure => present, - content => 'These are the contents of the file.', - checksum => 'sha256', - mode => 'ug+rw', - owner => 'root', - provider => 'posix', - } -} -EOF" - manifest_str - end - - # Method to create a manifest for FILE resource attribute 'ensure' where - # 'ensure' is set to absent. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_file_manifest_absent - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - file { 'testfile.txt': - path => '/tmp/testfile.txt', - ensure => absent, - } -} -EOF" - manifest_str - end - - # Method to create a manifest for SVC resource attributes: - # name, ensure and enable. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_service_manifest_nondefaults(service) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - service { '#{service}': - name => '#{service}', - ensure => 'running', - enable => 'true', - } -} -EOF" - manifest_str - end - - # Method to create a manifest for SVC resource attribute 'ensure' where - # 'ensure' is set to stopped. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_service_manifest_stopped(service) - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - service { '#{service}': - name => '#{service}', - ensure => 'stopped', - } -} -EOF" - manifest_str - end - - # Method to create a manifest for PKG resource attribute 'ensure' where - # 'ensure' is set to installed. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_package_curl_manifest_installed - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - package { 'curl': - name => 'curl', - ensure => installed, - provider => 'yum', - allow_virtual => 'false', - } -} -EOF" - manifest_str - end - - # Method to create a manifest for PKG resource attribute 'ensure' where - # 'ensure' is set to latest. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_package_curl_manifest_latest - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { - package { 'curl': - name => 'curl', - ensure => latest, - } -} -EOF" - manifest_str - end -end diff --git a/tests/beaker_tests/file_service_package/package_provider_nondefaults_1.rb b/tests/beaker_tests/file_service_package/package_provider_nondefaults_1.rb deleted file mode 100644 index 6ee78ad7b..000000000 --- a/tests/beaker_tests/file_service_package/package_provider_nondefaults_1.rb +++ /dev/null @@ -1,108 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# -------------- -# Package-Provider-NonDefaults-1.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet PACKAGE resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a PACKAGE resource test that tests for nondefault values for -# name, ensure, provider and allow_virtual attributes of a -# package resource when installed with 'ensure' => 'installed'. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-3 deal with package resource installation and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and FileSvcPkgLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../filesvcpkglib.rb', __FILE__) - -result = 'PASS' -testheader = 'PACKAGE Resource :: All Attributes NonDefaults' - -# @test_name [TestCase] Executes nondefaults testcase for PACKAGE Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, FileSvcPkgLib.create_package_curl_manifest_latest) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - # No change would imply that curl package is installed prior to test. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0]) - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource installed manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, FileSvcPkgLib.create_package_curl_manifest_installed) - - # Expected exit_code is 0 since this is a puppet agent cmd with no change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0]) - - logger.info("Get resource installed manifest from master :: #{result}") - end - - # @step [Step] Checks package resource on agent using resource cmd. - step 'TestStep :: Check package resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - # The Curl package state should not be purged. - cmd_str = PUPPET_BINPATH + "resource package 'curl'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'purged' }, - true, self, logger) - end - - logger.info("Check package resource presence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/file_service_package/service_provider_nondefaults.rb b/tests/beaker_tests/file_service_package/service_provider_nondefaults.rb deleted file mode 100644 index 77c329cc1..000000000 --- a/tests/beaker_tests/file_service_package/service_provider_nondefaults.rb +++ /dev/null @@ -1,149 +0,0 @@ -############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# TestCase Name: -# -------------- -# Service-Provider-NonDefaults.rb -# -# TestCase Prerequisites: -# ----------------------- -# This is a Puppet SERVICE resource testcase for Puppet Agent on Nexus devices. -# The test case assumes the following prerequisites are already satisfied: -# A. Populating the HOSTS configuration file with the agent and master -# information. -# B. Enabling SSH connection prerequisites on the N9K switch based Agent. -# C. Starting of Puppet master server on master. -# D. Sending to and signing of Puppet agent certificate request on master. -# -# TestCase: -# --------- -# This is a SERVICE resource test that tests for nondefault values for -# name, ensure and enable attributes of a -# service resource when ran with 'ensure' => 'running'. -# -# There are 2 sections to the testcase: Setup, group of teststeps. -# The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-3 deal with service resource running and its -# verification using Puppet Agent and the switch running-config. -# Steps 4-5 deal with service resource stopping and its -# verification using Puppet Agent and the switch running-config. -# -# The testcode checks for exit_codes from Puppet Agent, Vegas shell and -# Bash shell command executions. For Vegas shell and Bash shell command -# string executions, this is the exit_code convention: -# 0 - successful command execution, > 0 - failed command execution. -# For Puppet Agent command string executions, this is the exit_code convention: -# 0 - no changes have occurred, 1 - errors have occurred, -# 2 - changes have occurred, 4 - failures have occurred and -# 6 - changes and failures have occurred. -# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. -# The testcode also uses RegExp pattern matching on stdout or output IO -# instance attributes of Result object from on() method invocation. -# -############################################################################### - -# Require UtilityLib.rb and FileSvcPkgLib.rb paths. -require File.expand_path('../../lib/utilitylib.rb', __FILE__) -require File.expand_path('../filesvcpkglib.rb', __FILE__) - -result = 'PASS' -testheader = 'SERVICE Resource :: All Attributes NonDefaults' - -# Identify service for testing based on os family. -case os_family -when 'cisco-wrlinux' - os_service = 'bootlogd' -when 'RedHat' - os_service = 'systemd-tmpfiles-clean.timer' -else - fail_test("Unable to identify/set service for OS family: #{os_family}") -end - -# @test_name [TestCase] Executes nondefaults testcase for SERVICE Resource. -test_name "TestCase :: #{testheader}" do - raise_skip_exception('Not supported for OAC platforms', self) if - platform[/n(5|6|7)k/] - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider test' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, FileSvcPkgLib.create_service_manifest_stopped(os_service)) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - # Or expected exit_code is 0 since this is a puppet agent cmd with no change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) - - logger.info("Setup switch for provider test :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource nondefaults manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, FileSvcPkgLib.create_service_manifest_nondefaults(os_service)) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource nondefaults manifest from master :: #{result}") - end - - # @step [Step] Checks service resource on agent using resource cmd. - step 'TestStep :: Check service resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource service '#{os_service}'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'running' }, - false, self, logger) - end - - logger.info("Check service resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource stopped manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, FileSvcPkgLib.create_service_manifest_stopped(os_service)) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - logger.info('Pause 5 seconds to allow state transition') - sleep 5 - - logger.info("Get resource stopped manifest from master :: #{result}") - end - - # @step [Step] Checks service resource on agent using resource cmd. - step 'TestStep :: Check service resource absence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to true to check for absence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + "resource service '#{os_service}'" - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'running' }, - true, self, logger) - end - - logger.info("Check service resource absence on agent :: #{result}") - end - - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. - raise_passfail_exception(result, testheader, self, logger) -end - -logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/file_service_package/test_file.rb b/tests/beaker_tests/file_service_package/test_file.rb new file mode 100755 index 000000000..d73d3361a --- /dev/null +++ b/tests/beaker_tests/file_service_package/test_file.rb @@ -0,0 +1,75 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +tests = { + agent: agent, + master: master, + resource_name: 'file', +} + +testfile = '/tmp/testfile.txt' + +tests[:testfile] = { + title_pattern: testfile, + manifest_props: { + path: testfile, + content: 'This is a puppet/beaker test file', + checksum: 'sha256', + mode: 'ug+rw', + owner: 'root', + provider: 'posix', + }, + resource: { + 'ensure' => 'file', + 'content' => '{md5}[0-9a-fA-F]*', + 'group' => '0', + 'mode' => '0664', + 'owner' => '0', + 'type' => 'file', + }, +} + +def cleanup(agent, testfile) + on(agent, "rm -f #{testfile}", acceptable_error_codes: [0, 2]) +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(agent, testfile) } + cleanup(agent, testfile) + + # ---------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Create & Destroy") + id = :testfile + tests[id][:desc] = '1.1 Create test file' + test_harness_run(tests, id) + + tests[id][:desc] = '1.2 Remove test file' + tests[id][:ensure] = :absent + test_harness_run(tests, id) +end +logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/file_service_package/test_package.rb b/tests/beaker_tests/file_service_package/test_package.rb new file mode 100755 index 000000000..c3a02a1b5 --- /dev/null +++ b/tests/beaker_tests/file_service_package/test_package.rb @@ -0,0 +1,124 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +tests = { + agent: agent, + master: master, + ensurable: false, + resource_name: 'package', +} + +# Native mode third-party rpm's require repo setup +tests[:env] = os_family[/cisco-wrlinux/] ? :native : :centos + +tests[:centos] = { + # GS/OAC-only test. Use dos2unix rpm from the wild. + # dos2unix is a tiny rpm that is not normally present + title_pattern: 'dos2unix', + manifest_props: { + name: 'dos2unix', + ensure: 'installed', + provider: 'yum', + allow_virtual: 'false', + }, + resource: { 'ensure' => '[0-9]+' }, +} + +tests[:native] = { + # Native-only test. Use local repo for native testing. + title_pattern: 'demo-one', + manifest_props: { + name: 'demo-one', + ensure: 'installed', + provider: 'yum', + allow_virtual: 'false', + }, + resource: { 'ensure' => '[0-9]+' }, +} + +def dependency_manifest(_tests, id) + return unless id == :native + dep = %( + # Create local repo files + file { '/tmp/beaker-repo' : + ensure => 'directory', + } + file { '/tmp/beaker-repo/demo-one-1.0-1.x86_64.rpm' : + ensure => present, + source => 'puppet:///modules/ciscopuppet/demo-one-1.0-1.x86_64.rpm', + } + exec { 'createrepo': + command => '/usr/bin/createrepo /tmp/beaker-repo', + unless => '/bin/ls /tmp/beaker-repo/repodata 2>/dev/null', + } + + yumrepo { 'beaker': + # Create beaker repo config on switch + baseurl => 'file:///tmp/beaker-repo', + descr => "Beaker test rpms", + enabled => 1, + gpgcheck => 0, + cost => 505, + } + ) + logger.info("\n * dependency_manifest\n#{dep}") + dep +end + +def cleanup(tests, id) + agent = tests[:agent] + # Remove the test rpm from the switch + puppet_resource_cmd_from_params(tests, id) + cmd = tests[id][:resource_cmd] + ' ensure=purged' + logger.info("Cleanup: #{cmd}") + on(agent, cmd, acceptable_error_codes: [0, 2]) + + return unless id == :native + # Remove any local repo cruft + on(agent, 'rm -f /etc/yum/repos.d/beaker.repo', acceptable_error_codes: [0, 2]) + on(agent, 'rm -rf /tmp/beaker-repo/', acceptable_error_codes: [0, 2]) +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + id = tests[:env] + teardown { cleanup(tests, id) } + cleanup(tests, id) + + # ---------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Install") + tests[id][:desc] = '1.1 Install RPM' + test_harness_run(tests, id) + + # ---------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. Uninstall") + tests[id][:desc] = '1.2 Uninstall RPM' + tests[id][:manifest_props][:ensure] = 'purged' + tests[id][:resource] = { 'ensure' => 'purged' } + test_harness_run(tests, id) +end +logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/file_service_package/test_service.rb b/tests/beaker_tests/file_service_package/test_service.rb new file mode 100755 index 000000000..b7aad9076 --- /dev/null +++ b/tests/beaker_tests/file_service_package/test_service.rb @@ -0,0 +1,76 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +tests = { + agent: agent, + master: master, + ensurable: false, + resource_name: 'service', +} + +os_service = 'mcollective' + +tests[:service_start] = { + desc: "1.1 Start Service '#{os_service}'", + title_pattern: os_service, + manifest_props: { + name: os_service, + ensure: 'running', + enable: 'true', + }, + resource: { 'ensure' => 'running' }, +} + +tests[:service_stop] = { + desc: "1.2 Stop Service '#{os_service}'", + title_pattern: os_service, + manifest_props: { + name: os_service, + ensure: 'stopped', + enable: 'false', + }, + resource: { 'ensure' => 'stopped' }, +} + +def cleanup(tests) + puppet_resource_cmd_from_params(tests, :service_stop) + cmd = tests[:service_stop][:resource_cmd] + ' ensure=stopped enable=false' + logger.info("Cleanup: #{cmd}") + on(tests[:agent], cmd, acceptable_error_codes: [0, 2]) +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(tests) } + cleanup(tests) + + # ---------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Service Tests") + test_harness_run(tests, :service_start) + test_harness_run(tests, :service_stop) +end +logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 05c8347dc..098ebeaa8 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -600,11 +600,6 @@ def create_manifest_and_resource(tests, id) else state = 'ensure => present,' unless tests[:ensurable] == false - unless tests[id][:ensure_prop_override] - tests[id][:resource]['ensure'] = nil unless - tests[id][:resource].nil? || tests[:ensurable] == false - end - manifest_props = tests[id][:manifest_props] if manifest_props manifest_props = supported_property_hash(tests, id, manifest_props) From 48755fb4445870bf66daa23aca4924c89271b534 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Fri, 2 Sep 2016 16:27:38 -0400 Subject: [PATCH 104/203] utilitylib: Restore tests[id][:resource]['ensure'] I pulled this out by mistake with this commit: https://github.com/cisco/cisco-network-puppet-module/commit/2641c7a1c821b9b512510ad3a2952f5dbfe7c0fd#diff-e0883d3270eb11e5d50fcdc5c4f83003L603 --- tests/beaker_tests/lib/utilitylib.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 098ebeaa8..823d6d794 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -600,6 +600,9 @@ def create_manifest_and_resource(tests, id) else state = 'ensure => present,' unless tests[:ensurable] == false + tests[id][:resource]['ensure'] = nil unless + tests[id][:resource].nil? || tests[:ensurable] == false + manifest_props = tests[id][:manifest_props] if manifest_props manifest_props = supported_property_hash(tests, id, manifest_props) From 5af209152e24d8ae5a88ca8bbdbddab61a560384 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Tue, 6 Sep 2016 10:48:42 -0400 Subject: [PATCH 105/203] test_interface_ospf: Fix process dependency * A previous test cleanup removed 'router ospf Sample' from the harness dependencies; now the nightly if failing when it tries to configure an ospf config on an interface * Although the router config is not necessary for the interface config, the failure occurs because the ospf process is still starting up (ospf startup is slow on 5k/6k/8k). The ospf provider already has a 'wait_for_process_initialized' method to handle this slow start, while the interface_ospf provider does not. This is only an issue during testing so I do not propose adding the wait_for method to interface_ospf. * Tested on n5,n7,n9 --- tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb index fb2026142..8d9c57bed 100644 --- a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb +++ b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb @@ -42,7 +42,6 @@ tests[:default] = { desc: '1.1 Defaults', title_pattern: "#{intf} Sample", - preclean_intf: true, manifest_props: { area: 200, bfd: 'default', @@ -108,7 +107,7 @@ def test_harness_dependencies(_tests, id) return unless id == :default - test_set(agent, 'feature ospf') + test_set(agent, 'feature ospf ; router ospf Sample') end def cleanup(agent, intf) From e1deeadc4c1c17f1167a0ee575723e1ffc57369e Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Tue, 6 Sep 2016 11:58:17 -0400 Subject: [PATCH 106/203] test_interface_ospf: Fix process dependency (#388) * A previous test cleanup removed 'router ospf Sample' from the harness dependencies; now the nightly if failing when it tries to configure an ospf config on an interface * Although the router config is not necessary for the interface config, the failure occurs because the ospf process is still starting up (ospf startup is slow on 5k/6k/8k). The ospf provider already has a 'wait_for_process_initialized' method to handle this slow start, while the interface_ospf provider does not. This is only an issue during testing so I do not propose adding the wait_for method to interface_ospf. * Tested on n5,n7,n9 --- tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb index fb2026142..8d9c57bed 100644 --- a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb +++ b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb @@ -42,7 +42,6 @@ tests[:default] = { desc: '1.1 Defaults', title_pattern: "#{intf} Sample", - preclean_intf: true, manifest_props: { area: 200, bfd: 'default', @@ -108,7 +107,7 @@ def test_harness_dependencies(_tests, id) return unless id == :default - test_set(agent, 'feature ospf') + test_set(agent, 'feature ospf ; router ospf Sample') end def cleanup(agent, intf) From 31c34f33c778f0d53d4bd9cd73e7d27f0fa702d3 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Tue, 6 Sep 2016 12:32:17 -0400 Subject: [PATCH 107/203] Add log.error to beaker search_pattern * When test_resource fails it just displays what it's expecting to find but it doesn't show what the current output was (which often helps with troubleshooting). This just includes the output when there's a failure. --- tests/beaker_tests/lib/utilitylib.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 823d6d794..774f42e50 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -98,6 +98,7 @@ def search_pattern_in_output(output, patarr, inverse, testcase,\ if match logger.debug("TestStep :: #{match_kind}Match #{pattern} :: PASS") else + logger.error("stdout:\n--\n#{stdout}\n--") testcase.fail_test("TestStep :: #{match_kind}Match #{pattern} :: FAIL") end end From 1c3607f4758c02f1164b5ff944cb5d6098ea837e Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Tue, 6 Sep 2016 13:34:00 -0400 Subject: [PATCH 108/203] Add titles for bgp vrf beaker tests --- tests/beaker_tests/cisco_bgp/test_bgp.rb | 2 ++ tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/beaker_tests/cisco_bgp/test_bgp.rb b/tests/beaker_tests/cisco_bgp/test_bgp.rb index b53bf09f3..25bc37bb5 100644 --- a/tests/beaker_tests/cisco_bgp/test_bgp.rb +++ b/tests/beaker_tests/cisco_bgp/test_bgp.rb @@ -258,6 +258,7 @@ def cleanup(agent) # now test the defaults under a non-default vrf cleanup(agent) tests[id][:ensure] = :present + tests[id][:desc] = '1.1.a. Default Properties (vrf blue)' test_harness_bgp_vrf(tests, id, 'blue') # ------------------------------------------------------------------- @@ -265,6 +266,7 @@ def cleanup(agent) id = :non_default test_harness_run(tests, id) + tests[id][:desc] = '2.1.a. Default Properties (vrf blue)' test_harness_bgp_vrf(tests, id, 'blue') # ------------------------------------------------------------------- diff --git a/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb b/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb index 5bc7a9bca..a95d2395f 100644 --- a/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb +++ b/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb @@ -160,6 +160,7 @@ def cleanup(agent) test_harness_run(tests, :default) # now test the defaults under a non-default vrf + tests[id][:desc] = '1.1.a. Default Properties (vrf blue)' tests[:default][:ensure] = :present tests[:default][:preclean] = 'cisco_bgp_neighbor' test_harness_bgp_vrf(tests, :default, 'blue') @@ -167,6 +168,7 @@ def cleanup(agent) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") test_harness_run(tests, :non_default) + tests[id][:desc] = '2.1.a. Non Default Properties (vrf blue)' test_harness_bgp_vrf(tests, :non_default, 'blue') test_harness_run(tests, :non_def_local_remote_as) From 0dd6b11f2e94f0bafb8c21d80bcda6337a95ea62 Mon Sep 17 00:00:00 2001 From: Rob Gries Date: Wed, 7 Sep 2016 10:55:37 -0400 Subject: [PATCH 109/203] Suppress fib pending fix (#387) * Generalize the method for checking nexus images * Fix suppress fib pending for non-I5 images * Clean up image check and post-merge leftovers --- README.md | 1 + tests/beaker_tests/cisco_bgp/test_bgp.rb | 5 ++--- tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb | 2 +- .../cisco_evpn_vni/test_evpn_vni.rb | 2 +- tests/beaker_tests/cisco_vrf/test_vrf.rb | 2 +- tests/beaker_tests/lib/utilitylib.rb | 20 +++++++++---------- 6 files changed, 15 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index d7b73f095..9df8a8c0a 100644 --- a/README.md +++ b/README.md @@ -1034,6 +1034,7 @@ Manages configuration of a BGP instance. | `disable_policy_batching_ipv6` | Not supported on N5k, N6k, N7k | | `neighbor_down_fib_accelerate` | Not supported on N5k, N6k, N7k | | `reconnect_interval` | Not supported on N5k, N6k, N7k | +| `suppress_fib_pending` | Idempotence supported only on I5 images | #### Parameters diff --git a/tests/beaker_tests/cisco_bgp/test_bgp.rb b/tests/beaker_tests/cisco_bgp/test_bgp.rb index 25bc37bb5..721dd1d59 100644 --- a/tests/beaker_tests/cisco_bgp/test_bgp.rb +++ b/tests/beaker_tests/cisco_bgp/test_bgp.rb @@ -64,7 +64,6 @@ nsr: 'default', reconnect_interval: 'default', shutdown: 'default', - suppress_fib_pending: 'default', timer_bestpath_limit: 'default', timer_bestpath_limit_always: 'default', timer_bgp_holdtime: 'default', @@ -97,7 +96,6 @@ 'nsr' => 'false', 'reconnect_interval' => '60', 'shutdown' => 'false', - 'suppress_fib_pending' => 'false', 'timer_bestpath_limit' => '300', 'timer_bestpath_limit_always' => 'false', 'timer_bgp_holdtime' => '180', @@ -215,7 +213,8 @@ def unsupported_properties(tests, id) :event_history_periodic << :fast_external_fallover << :flush_routes << - :neighbor_down_fib_accelerate + :neighbor_down_fib_accelerate << + :suppress_fib_pending end if platform[/n(5|6|7)k/] diff --git a/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb b/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb index b71c09706..2799b3067 100644 --- a/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb +++ b/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb @@ -315,6 +315,6 @@ def cleanup(agent) test_harness_run(tests, :title_patterns_1) test_harness_run(tests, :title_patterns_2) test_harness_run(tests, :title_patterns_3) - test_harness_run(tests, :l2vpn_evpn) unless nexus_i2_image + test_harness_run(tests, :l2vpn_evpn) unless nexus_image['I2'] end logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_evpn_vni/test_evpn_vni.rb b/tests/beaker_tests/cisco_evpn_vni/test_evpn_vni.rb index 916a7aef2..bb9e65164 100644 --- a/tests/beaker_tests/cisco_evpn_vni/test_evpn_vni.rb +++ b/tests/beaker_tests/cisco_evpn_vni/test_evpn_vni.rb @@ -72,7 +72,7 @@ # The harness will remove any "unprops" from the manifests. def unsupported_properties(*) unprops = [] - unprops << :route_target_both if nexus_i2_image + unprops << :route_target_both if nexus_image['I2'] unprops end diff --git a/tests/beaker_tests/cisco_vrf/test_vrf.rb b/tests/beaker_tests/cisco_vrf/test_vrf.rb index 3e4813da8..7b8f2f112 100644 --- a/tests/beaker_tests/cisco_vrf/test_vrf.rb +++ b/tests/beaker_tests/cisco_vrf/test_vrf.rb @@ -74,7 +74,7 @@ def unsupported_properties(_tests, _id) :vpn_id unprops << :vni unless platform[/n9k/] - unprops << :route_distinguisher if nexus_i2_image + unprops << :route_distinguisher if nexus_image['I2'] # unprops << :description if image?[/7.3.0.D1.1/] # CSCuy36637 else diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 774f42e50..295e6d441 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1108,21 +1108,19 @@ def image?(reset_cache=false) @cached_img = stdout.nil? ? '' : stdout end -# Check if this image is an I2 image -@i2_image = nil # Cache the lookup result -def nexus_i2_image - return @i2_image unless @i2_image.nil? - on(agent, facter_cmd('-p cisco.images.system_image')) - @i2_image = stdout[/7.0.3.I2/] ? true : false - @i2_image +@image = nil # Cache the lookup result +def nexus_image + facter_opt = '-p cisco.images.system_image' + image_regexp = /[A-Z]+\d+\.\d+/ + @image ||= on(agent, facter_cmd(facter_opt)).output[image_regexp] end -# This is a skip-all-testcases-if-I2-image check. +# On match will skip all testcases # Do not use this for skipping individual properties. -def skip_nexus_i2_image(tests) - return unless nexus_i2_image +def skip_nexus_image(image, tests) + return unless nexus_image[image] msg = "Skipping all tests; '#{tests[:resource_name]}' "\ - 'is not supported on 7.0.3(I2) images' + "is not supported on #{image} images" banner = '#' * msg.length raise_skip_exception("\n#{banner}\n#{msg}\n#{banner}\n", self) end From 0b1f365369627e653905f59f42ca3fa9e572957f Mon Sep 17 00:00:00 2001 From: saichint Date: Wed, 7 Sep 2016 16:27:37 -0700 Subject: [PATCH 110/203] cisco_interface enhancement (#389) * Fix interface_ospf provider and beaker * doc * doc * review comments * add mtu, pri, shut and tx delay to int ospf * documentation * bug fix * Fix for symmetry and resilient no support on n3k * review comment * rewview comments * dhcp relay and storm control -- not tested yet * beaker test * fix non default test * removed storm control broadcast and multicast for n7k * fix munging * demo manifest changes * cleanup manifests * dhcp and l3 are merged * merge L2 and storm control * docs * review comments --- CHANGELOG.md | 11 ++ README.md | 45 +++++- examples/cisco/demo_interface.pp | 34 ++++ lib/puppet/provider/cisco_interface/cisco.rb | 11 ++ lib/puppet/type/cisco_interface.rb | 151 ++++++++++++++++++ .../cisco_interface/test_interface_L2.rb | 17 ++ .../cisco_interface/test_interface_L3.rb | 99 ++++++++---- 7 files changed, 338 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b4129687..88effae34 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,17 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added - Extend cisco_interface with attributes: - `bfd_echo` + - `ipv4_dhcp_relay_addr` + - `ipv4_dhcp_relay_info_trust` + - `ipv4_dhcp_relay_src_addr_hsrp` + - `ipv4_dhcp_relay_src_intf` + - `ipv4_dhcp_relay_subnet_broadcast` + - `ipv4_dhcp_smart_relay` + - `ipv6_dhcp_relay_addr` + - `ipv6_dhcp_relay_src_intf` + - `storm_control_broadcast` + - `storm_control_multicast` + - `storm_control_unicast` - Extend cisco_interface_ospf with attributes: - `bfd` - `mtu_ignore` diff --git a/README.md b/README.md index 9df8a8c0a..8e635dfb1 100644 --- a/README.md +++ b/README.md @@ -402,7 +402,7 @@ Symbol | Meaning | Description | [cisco_evpn_vni](#type-cisco_evpn_vni) | ✅ | :heavy_minus_sign: | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_evpn_vni-caveats) | | [cisco_fabricpath_global](#type-cisco_fabricpath_global) | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | ✅ | ✅* | :heavy_minus_sign: | \*[caveats](#cisco_fabricpath_global-caveats) | | [cisco_fabricpath_topology](#type-cisco_fabricpath_topology) | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | ✅ | ✅ | :heavy_minus_sign: | -| [cisco_interface](#type-cisco_interface) | ✅ | ✅ | ✅* | ✅* | ✅ | ✅ | \*[caveats](#cisco_interface-caveats) | +| [cisco_interface](#type-cisco_interface) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_interface-caveats) | | [cisco_interface_channel_group](#type-cisco_interface_channel_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_interface_ospf](#type-cisco_interface_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_interface_portchannel](#type-cisco_interface_portchannel) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_interface_portchannel-caveats) | @@ -1834,6 +1834,10 @@ Manages a Cisco Network Interface. Any resource dependency should be run before | Property | Caveat Description | |:---------|:-------------| +| `ipv4_dhcp_relay_info_trust` | Not supported on N5k,N6k | +| `ipv4_dhcp_relay_src_addr_hsrp` | Not supported on N3k,N8k,N9k | +| `storm_control_broadcast` | Not supported on N7k | +| `storm_control_multicast` | Not supported on N7k | | `pvlan_mapping` | Not supported on N8k | | `switchport_pvlan_host` | Not supported on N8k | | `switchport_pvlan_host_association` | Not supported on N8k | @@ -1974,6 +1978,15 @@ and 'default'. Enable/Disable negotiate auto on the interface. Valid values are 'true', 'false', and 'default'. +##### `storm_control_broadcast` +Allowed broadcast traffic level. Valid values are a string representing the broadcast level or keyword 'default'. + +##### `storm_control_multicast` +Allowed multicast traffic level. Valid values are a string representing the multicast level or keyword 'default'. + +##### `storm_control_unicast` +Allowed unicast traffic level. Valid values are a string representing the unicast level or keyword 'default'. + ##### L3 interface config attributes ###### `ipv4_acl_in` @@ -2023,6 +2036,36 @@ Applies an ipv6 access list on the interface in the ingress direction. An access ###### `ipv6_acl_out` Applies an ipv6 access list on the interface in the egress direction. An access-list should be present on the network device prior to this configuration. Valid values are string, keyword 'default'. +###### `ipv4_dhcp_relay_addr` +This property is an array of dhcp relay addresses. Valid values are an array specifying the dhcp relay addresses or keyword 'default'; e.g.: + +``` +ipv4_dhcp_relay_addr => ['1.1.1.1', '2.2.2.2'] +``` +###### `ipv4_dhcp_relay_info_trust` +Enable/Disable relay trust on the interface. Valid values are 'true', 'false', and 'default'. + +###### `ipv4_dhcp_relay_src_addr_hsrp` +Enable/Disable virtual IP instead of SVI address on the interface. Valid values are 'true', 'false', and 'default'. + +###### `ipv4_dhcp_relay_src_intf` +Source interface for the DHCPV4 relay. Valid values are string, keyword 'default'. + +###### `ipv4_dhcp_relay_info_trust` +Enable/Disable DHCP relay subnet-broadcast on the interface. Valid values are 'true', 'false', and 'default'. + +###### `ipv4_dhcp_smart_relay` +Enable/Disable DHCP smart relay on the interface. Valid values are 'true', 'false', and 'default'. + +###### `ipv6_dhcp_relay_addr` +This property is an array of ipv6 dhcp relay addresses. Valid values are an array specifying the ipv6 dhcp relay addresses or keyword 'default'; e.g.: + +``` +ipv6_dhcp_relay_addr => ['2000::11', '2001::22'] +``` +###### `ipv6_dhcp_relay_src_intf` +Source interface for the DHCPV6 relay. Valid values are string, keyword 'default'. + ###### `vlan_mapping` This property is a nested array of [original_vlan, translated_vlan] pairs. Valid values are an array specifying the mapped vlans or keyword 'default'; e.g.: diff --git a/examples/cisco/demo_interface.pp b/examples/cisco/demo_interface.pp index 6b355c1af..77e740140 100755 --- a/examples/cisco/demo_interface.pp +++ b/examples/cisco/demo_interface.pp @@ -36,6 +36,16 @@ ensure => 'present', } + $ipv4_dhcp_relay_info_trust = platform_get() ? { + /(n3k|n7k|n8k|n9k)/ => true, + default => undef + } + + $ipv4_dhcp_relay_src_addr_hsrp = platform_get() ? { + /(n5k|n6k|n7k)/ => true, + default => undef + } + cisco_interface { 'Ethernet1/1' : shutdown => true, switchport_mode => disabled, @@ -73,10 +83,34 @@ switchport_mode => access, } + cisco_interface { 'Ethernet1/4': + switchport_mode => disabled, + ipv4_dhcp_relay_addr => ['1.1.1.1', '2.2.2.2'], + ipv4_dhcp_relay_info_trust => $ipv4_dhcp_relay_info_trust, + ipv4_dhcp_relay_src_addr_hsrp => $ipv4_dhcp_relay_src_addr_hsrp, + ipv4_dhcp_relay_src_intf => 'port-channel 100', + ipv4_dhcp_relay_subnet_broadcast => true, + ipv4_dhcp_smart_relay => true, + ipv6_dhcp_relay_addr => ['2000::11', '2001::22'], + ipv6_dhcp_relay_src_intf => 'ethernet 2/2', + } + $storm_control_broadcast = platform_get() ? { + /(n3k|n5k|n6k|n8k|n9k)/ => '77.77', + default => undef + } + + $storm_control_multicast = platform_get() ? { + /(n3k|n5k|n6k|n8k|n9k)/ => '22.22', + default => undef + } + cisco_interface { 'Ethernet1/5': switchport_mode => trunk, switchport_trunk_allowed_vlan => '30, 29, 31-33, 100', switchport_trunk_native_vlan => 40, + storm_control_broadcast => $storm_control_broadcast, + storm_control_multicast => $storm_control_multicast, + storm_control_unicast => '33.33', } $svi_autostate = platform_get() ? { diff --git a/lib/puppet/provider/cisco_interface/cisco.rb b/lib/puppet/provider/cisco_interface/cisco.rb index e7c4dd7c9..d6e8cfbf1 100644 --- a/lib/puppet/provider/cisco_interface/cisco.rb +++ b/lib/puppet/provider/cisco_interface/cisco.rb @@ -52,11 +52,16 @@ :ipv4_address_secondary, :ipv4_netmask_length_secondary, :ipv4_arp_timeout, + :ipv4_dhcp_relay_src_intf, :ipv6_acl_in, :ipv6_acl_out, + :ipv6_dhcp_relay_src_intf, :mtu, :speed, :duplex, + :storm_control_broadcast, + :storm_control_multicast, + :storm_control_unicast, :stp_bpdufilter, :stp_bpduguard, :stp_cost, @@ -78,6 +83,10 @@ :ipv4_pim_sparse_mode, :ipv4_proxy_arp, :ipv4_redirects, + :ipv4_dhcp_relay_info_trust, + :ipv4_dhcp_relay_src_addr_hsrp, + :ipv4_dhcp_relay_subnet_broadcast, + :ipv4_dhcp_smart_relay, :negotiate_auto, :shutdown, :switchport_autostate_exclude, @@ -93,6 +102,8 @@ ] INTF_ARRAY_FLAT_PROPS = [ :pvlan_mapping, + :ipv4_dhcp_relay_addr, + :ipv6_dhcp_relay_addr, :stp_mst_cost, :stp_mst_port_priority, :stp_vlan_cost, diff --git a/lib/puppet/type/cisco_interface.rb b/lib/puppet/type/cisco_interface.rb index afd638c13..0c40300c2 100755 --- a/lib/puppet/type/cisco_interface.rb +++ b/lib/puppet/type/cisco_interface.rb @@ -56,6 +56,14 @@ ipv4_proxy_arp => true, ipv4_pim_sparse_mode => true, negotiate_auto => true, + ipv4_dhcp_relay_addr => ['1.1.1.1', '2.2.2.2'], + ipv4_dhcp_relay_info_trust => true, + ipv4_dhcp_relay_src_addr_hsrp => true, + ipv4_dhcp_relay_src_intf => 'port-channel 100', + ipv4_dhcp_relay_subnet_broadcast => true, + ipv4_dhcp_smart_relay => true, + ipv6_dhcp_relay_addr => ['2000::11', '2001::22'], + ipv6_dhcp_relay_src_intf => 'ethernet 2/2', } cisco_interface { 'ethernet1/17' : stp_bpdufilter => 'enable', @@ -72,6 +80,9 @@ } cisco_interface { 'ethernet9/1' : switchport_mode => 'trunk', + storm_control_broadcast => '77.77', + storm_control_multicast => '22.22', + storm_control_unicast => '33.33', vlan_mapping_enable => 'false', vlan_mapping => [[20, 21], [30, 31]], } @@ -1042,4 +1053,144 @@ def insync?(is) (is.size == should.flatten.size && is.sort == should.flatten.sort) end end # private_vlan_mapping + + ############################ + # dhcp relay attributes # + ############################ + + newproperty(:ipv4_dhcp_relay_addr, array_matching: :all) do + format = '[addr1, addr2]' + desc 'An array of ipv4 addresses'\ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + value = :default if value == 'default' + value + end + end + end # property ipv4_dhcp_relay_addr + + newproperty(:ipv4_dhcp_relay_info_trust) do + desc 'Enables relay trust on this interface.' + + newvalues(:true, :false, :default) + end # property ipv4_dhcp_relay_info_trust + + newproperty(:ipv4_dhcp_relay_src_addr_hsrp) do + desc 'Enables Virtual IP instead of SVI address' + + newvalues(:true, :false, :default) + end # property ipv4_dhcp_relay_src_addr_hsrp + + newproperty(:ipv4_dhcp_relay_src_intf) do + desc "Source interface for the DHCPV4 relay. Valid values + are string, keyword 'default'. " + + munge do |value| + value = value.downcase.delete(' ') + value = :default if value == 'default' + value + end + end # property ipv4_dhcp_relay_src_intf + + newproperty(:ipv4_dhcp_relay_subnet_broadcast) do + desc 'Enables DHCP relay subnet-broadcast on this interface.' + + newvalues(:true, :false, :default) + end # property ipv4_dhcp_relay_subnet_broadcast + + newproperty(:ipv4_dhcp_smart_relay) do + desc 'Enables DHCP smart relay on this interface.' + + newvalues(:true, :false, :default) + end # property ipv4_dhcp_smart_relay + + newproperty(:ipv6_dhcp_relay_addr, array_matching: :all) do + format = '[addr1, addr2]' + desc 'An array of ipv6 addresses'\ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + value = :default if value == 'default' + value + end + end + end # property ipv6_dhcp_relay_addr + + newproperty(:ipv6_dhcp_relay_src_intf) do + desc "Source interface for the DHCPV6 relay. Valid values + are string, keyword 'default'. " + + munge do |value| + value = value.downcase.delete(' ') + value = :default if value == 'default' + value + end + end # property ipv6_dhcp_relay_src_intf + + ############################ + # storm control attributes # + ############################ + + newproperty(:storm_control_broadcast) do + desc "Allowed broadcast traffic level. Valid values + are string, keyword 'default'. " + + munge do |value| + value = :default if value == 'default' + value + end + end # property storm_control_broadcast + + newproperty(:storm_control_multicast) do + desc "Allowed multicast traffic level. Valid values + are string, keyword 'default'. " + + munge do |value| + value = :default if value == 'default' + value + end + end # property storm_control_multicast + + newproperty(:storm_control_unicast) do + desc "Allowed unicast traffic level. Valid values + are string, keyword 'default'. " + + munge do |value| + value = :default if value == 'default' + value + end + end # property storm_control_unicast end # Puppet::Type.newtype diff --git a/tests/beaker_tests/cisco_interface/test_interface_L2.rb b/tests/beaker_tests/cisco_interface/test_interface_L2.rb index 3f70c79cf..31209b8f6 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_L2.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_L2.rb @@ -86,6 +86,9 @@ sys_def_sw_shut: true, manifest_props: { shutdown: 'default', + storm_control_broadcast: 'default', + storm_control_multicast: 'default', + storm_control_unicast: 'default', switchport_autostate_exclude: 'default', switchport_mode: 'trunk', switchport_trunk_allowed_vlan: 'default', @@ -94,6 +97,9 @@ }, resource: { shutdown: 'true', + storm_control_broadcast: '100.00', + storm_control_multicast: '100.00', + storm_control_unicast: '100.00', switchport_autostate_exclude: 'false', switchport_mode: 'trunk', switchport_trunk_allowed_vlan: '1-4094', @@ -109,6 +115,9 @@ sys_def_sw_shut: true, manifest_props: { shutdown: 'false', + storm_control_broadcast: '22.22', + storm_control_multicast: '44.44', + storm_control_unicast: '66.66', switchport_autostate_exclude: 'true', switchport_mode: 'trunk', switchport_trunk_allowed_vlan: '30-33,40,100', @@ -117,6 +126,14 @@ }, } +def unsupported_properties(_tests, _id) + unprops = [] + unprops << + :storm_control_broadcast << + :storm_control_multicast if platform == 'n7k' + unprops +end + ################################################################# # TEST CASE EXECUTION ################################################################# diff --git a/tests/beaker_tests/cisco_interface/test_interface_L3.rb b/tests/beaker_tests/cisco_interface/test_interface_L3.rb index 1855fc277..90f00f078 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_L3.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_L3.rb @@ -47,25 +47,39 @@ code: [0], sys_def_switchport: false, manifest_props: { - bfd_echo: 'default', - description: 'default', - duplex: 'default', - ipv4_forwarding: 'default', - ipv4_pim_sparse_mode: 'default', - ipv4_proxy_arp: 'default', - ipv4_redirects: 'default', - mtu: 'default', - shutdown: 'default', - vrf: 'default', + bfd_echo: 'default', + description: 'default', + duplex: 'default', + ipv4_forwarding: 'default', + ipv4_pim_sparse_mode: 'default', + ipv4_proxy_arp: 'default', + ipv4_redirects: 'default', + ipv4_dhcp_relay_addr: 'default', + ipv4_dhcp_relay_info_trust: 'default', + ipv4_dhcp_relay_src_addr_hsrp: 'default', + ipv4_dhcp_relay_src_intf: 'default', + ipv4_dhcp_relay_subnet_broadcast: 'default', + ipv4_dhcp_smart_relay: 'default', + ipv6_dhcp_relay_addr: 'default', + ipv6_dhcp_relay_src_intf: 'default', + mtu: 'default', + shutdown: 'default', + vrf: 'default', }, resource: { - duplex: 'auto', - ipv4_forwarding: 'false', - ipv4_pim_sparse_mode: 'false', - ipv4_proxy_arp: 'false', - ipv4_redirects: operating_system == 'nexus' ? 'true' : 'false', - mtu: operating_system == 'nexus' ? '1500' : '1514', - shutdown: 'false', + duplex: 'auto', + ipv4_forwarding: 'false', + ipv4_pim_sparse_mode: 'false', + ipv4_proxy_arp: 'false', + ipv4_redirects: operating_system == 'nexus' ? 'true' : 'false', + ipv4_dhcp_relay_info_trust: 'false', + ipv4_dhcp_relay_src_addr_hsrp: 'false', + ipv4_dhcp_relay_src_intf: 'false', + ipv4_dhcp_relay_subnet_broadcast: 'false', + ipv4_dhcp_smart_relay: 'false', + ipv6_dhcp_relay_src_intf: 'false', + mtu: operating_system == 'nexus' ? '1500' : '1514', + shutdown: 'false', }, } @@ -77,23 +91,34 @@ manifest_props: { encapsulation_dot1q: 30 }, } +v4_relay = ['1.1.1.1', '2.2.2.2', '3.3.3.3'] +v6_relay = ['2000::11', '2001::22', '2001::12'] + tests[:non_default] = { desc: '2.1 Non Default Properties', title_pattern: intf, sys_def_switchport: false, manifest_props: { - bfd_echo: false, - description: 'Configured with Puppet', - shutdown: true, - ipv4_address: '1.1.1.1', - ipv4_netmask_length: 31, - ipv4_address_secondary: '2.2.2.2', - ipv4_netmask_length_secondary: 31, - ipv4_pim_sparse_mode: true, - ipv4_proxy_arp: true, - ipv4_redirects: operating_system == 'nexus' ? false : true, - switchport_mode: 'disabled', - vrf: 'test1', + bfd_echo: false, + description: 'Configured with Puppet', + shutdown: true, + ipv4_address: '1.1.1.1', + ipv4_netmask_length: 31, + ipv4_address_secondary: '2.2.2.2', + ipv4_netmask_length_secondary: 31, + ipv4_pim_sparse_mode: true, + ipv4_proxy_arp: true, + ipv4_redirects: operating_system == 'nexus' ? false : true, + ipv4_dhcp_relay_addr: v4_relay, + ipv4_dhcp_relay_info_trust: 'true', + ipv4_dhcp_relay_src_addr_hsrp: 'true', + ipv4_dhcp_relay_src_intf: 'loopback1', + ipv4_dhcp_relay_subnet_broadcast: 'true', + ipv4_dhcp_smart_relay: 'true', + ipv6_dhcp_relay_addr: v6_relay, + ipv6_dhcp_relay_src_intf: 'ethernet1/1', + switchport_mode: 'disabled', + vrf: 'test1', }, } @@ -137,9 +162,25 @@ def unsupported_properties(_tests, id) :duplex << :ipv4_forwarding << :ipv4_pim_sparse_mode << + :ipv4_dhcp_relay_addr << + :ipv4_dhcp_relay_info_trust << + :ipv4_dhcp_relay_src_addr_hsrp << + :ipv4_dhcp_relay_src_intf << + :ipv4_dhcp_relay_subnet_broadcast << + :ipv4_dhcp_smart_relay << + :ipv6_dhcp_relay_addr << + :ipv6_dhcp_relay_src_intf << :switchport_mode end + if platform[/n(3|8|9)k/] + unprops << + :ipv4_dhcp_relay_src_addr_hsrp + elsif platform[/n(5|6)k/] + unprops << + :ipv4_dhcp_relay_info_trust + end + # TBD: shutdown has unpredictable behavior. Needs investigation. unprops << :shutdown if id == :default From 6469ff67570c222a0ec24abc4630e7e27d3f3167 Mon Sep 17 00:00:00 2001 From: Chris Van Heuveln Date: Fri, 9 Sep 2016 11:59:38 -0400 Subject: [PATCH 111/203] Add'l cleanup for test_interface_ospf --- .../cisco_interface_ospf/test_interface_ospf.rb | 12 +++++++----- tests/beaker_tests/lib/utilitylib.rb | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb index 8d9c57bed..c07ea947d 100644 --- a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb +++ b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb @@ -40,9 +40,10 @@ # Test hash test cases tests[:default] = { - desc: '1.1 Defaults', - title_pattern: "#{intf} Sample", - manifest_props: { + desc: '1.1 Defaults', + title_pattern: "#{intf} Sample", + sys_def_switchport: false, + manifest_props: { area: 200, bfd: 'default', cost: 'default', @@ -60,8 +61,8 @@ shutdown: 'default', transmit_delay: 'default', }, - code: [0, 2], - resource: { + code: [0, 2], + resource: { area: '0.0.0.200', cost: 0, dead_interval: 40, @@ -111,6 +112,7 @@ def test_harness_dependencies(_tests, id) end def cleanup(agent, intf) + logger.error(test_get(agent, 'incl .*')) # DEBUGGING ONLY - REMOVE test_set(agent, 'no feature ospf ; no feature bfd') interface_cleanup(agent, intf) end diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 295e6d441..5c3e81f47 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -325,7 +325,7 @@ def config_switchport_mode(agent, intf, mode, stepinfo='switchport mode: ') # Helper to toggle 'system default switchport' def system_default_switchport(agent, state=false, stepinfo='system default switchport') - step "TestStep :: #{stepinfo}" do + step "TestStep :: #{stepinfo} (state: #{state})" do state = state ? ' ' : 'no ' cmd = "#{state}system default switchport" command_config(agent, cmd, cmd) From 137899764e647381fdc4f6a3643248fabca69a8a Mon Sep 17 00:00:00 2001 From: mikewiebe Date: Fri, 9 Sep 2016 15:01:03 -0400 Subject: [PATCH 112/203] Fix id bug --- tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb b/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb index a95d2395f..0421eca60 100644 --- a/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb +++ b/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb @@ -160,7 +160,7 @@ def cleanup(agent) test_harness_run(tests, :default) # now test the defaults under a non-default vrf - tests[id][:desc] = '1.1.a. Default Properties (vrf blue)' + tests[:default][:desc] = '1.1.a. Default Properties (vrf blue)' tests[:default][:ensure] = :present tests[:default][:preclean] = 'cisco_bgp_neighbor' test_harness_bgp_vrf(tests, :default, 'blue') @@ -168,7 +168,7 @@ def cleanup(agent) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") test_harness_run(tests, :non_default) - tests[id][:desc] = '2.1.a. Non Default Properties (vrf blue)' + tests[:non_default][:desc] = '2.1.a. Non Default Properties (vrf blue)' test_harness_bgp_vrf(tests, :non_default, 'blue') test_harness_run(tests, :non_def_local_remote_as) From a168c599fbd220bf88c40b72fb225f0a2cec7d6d Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Fri, 9 Sep 2016 16:37:46 -0400 Subject: [PATCH 113/203] Replace skip_nexus_i2_image() with skip_nexus_image() api call (#390) --- .../cisco_itd_device_group/test_itd_device_group.rb | 2 +- .../cisco_itd_device_group_node/test_itd_device_group_node.rb | 2 +- tests/beaker_tests/cisco_itd_service/test_itd_service.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/beaker_tests/cisco_itd_device_group/test_itd_device_group.rb b/tests/beaker_tests/cisco_itd_device_group/test_itd_device_group.rb index 6af754a03..8471b15f9 100644 --- a/tests/beaker_tests/cisco_itd_device_group/test_itd_device_group.rb +++ b/tests/beaker_tests/cisco_itd_device_group/test_itd_device_group.rb @@ -33,7 +33,7 @@ # Skip -ALL- tests if a top-level platform/os key exludes this platform skip_unless_supported(tests) -skip_nexus_i2_image(tests) +skip_nexus_image('I2', tests) # Test hash test cases tests[:default] = { diff --git a/tests/beaker_tests/cisco_itd_device_group_node/test_itd_device_group_node.rb b/tests/beaker_tests/cisco_itd_device_group_node/test_itd_device_group_node.rb index a37f63023..41b70d07a 100644 --- a/tests/beaker_tests/cisco_itd_device_group_node/test_itd_device_group_node.rb +++ b/tests/beaker_tests/cisco_itd_device_group_node/test_itd_device_group_node.rb @@ -34,7 +34,7 @@ # Skip -ALL- tests if a top-level platform/os key exludes this platform skip_unless_supported(tests) -skip_nexus_i2_image(tests) +skip_nexus_image('I2', tests) # Test hash test cases tests[:default] = { diff --git a/tests/beaker_tests/cisco_itd_service/test_itd_service.rb b/tests/beaker_tests/cisco_itd_service/test_itd_service.rb index e85d0e488..f1d11f4be 100644 --- a/tests/beaker_tests/cisco_itd_service/test_itd_service.rb +++ b/tests/beaker_tests/cisco_itd_service/test_itd_service.rb @@ -35,7 +35,7 @@ # Skip -ALL- tests if a top-level platform/os key exludes this platform skip_unless_supported(tests) -skip_nexus_i2_image(tests) +skip_nexus_image('I2', tests) def find_ingress_ethernet_interface(tests) if tests[:ethernet] From 5943ba157394e70bef8c0276e97d012afaa8ffcb Mon Sep 17 00:00:00 2001 From: mikewiebe Date: Wed, 14 Sep 2016 13:53:50 -0400 Subject: [PATCH 114/203] Skip dns provider test instead of error --- .../netdev_stdlib/network_dns_provider_nondefaults.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/beaker_tests/netdev_stdlib/network_dns_provider_nondefaults.rb b/tests/beaker_tests/netdev_stdlib/network_dns_provider_nondefaults.rb index 516adee5a..ad6ee2184 100644 --- a/tests/beaker_tests/netdev_stdlib/network_dns_provider_nondefaults.rb +++ b/tests/beaker_tests/netdev_stdlib/network_dns_provider_nondefaults.rb @@ -107,7 +107,8 @@ def dns_clean(agent) *** WARNING WARNING WARNING *** ***************************************************************** *****************************************************************" - fail dns_warning if dns_warning + # fail dns_warning if dns_warning + raise_skip_exception(dns_warning, self) if dns_warning ############ # Start Test From 1179a774f5e5e460905cc209d5dacea36f051839 Mon Sep 17 00:00:00 2001 From: mikewiebe Date: Wed, 14 Sep 2016 13:55:32 -0400 Subject: [PATCH 115/203] Remove commented out call to fail api --- .../netdev_stdlib/network_dns_provider_nondefaults.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/beaker_tests/netdev_stdlib/network_dns_provider_nondefaults.rb b/tests/beaker_tests/netdev_stdlib/network_dns_provider_nondefaults.rb index ad6ee2184..84487023d 100644 --- a/tests/beaker_tests/netdev_stdlib/network_dns_provider_nondefaults.rb +++ b/tests/beaker_tests/netdev_stdlib/network_dns_provider_nondefaults.rb @@ -107,7 +107,6 @@ def dns_clean(agent) *** WARNING WARNING WARNING *** ***************************************************************** *****************************************************************" - # fail dns_warning if dns_warning raise_skip_exception(dns_warning, self) if dns_warning ############ From e54e7e46b97883194aa23201e0ce5d54c69a8ff4 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Thu, 15 Sep 2016 09:36:29 -0400 Subject: [PATCH 116/203] Update test_harness_dependencies for test_interface_ospf.rb (#393) * Fixed rubocop errors * Remove debug code --- .../cisco_interface_ospf/test_interface_ospf.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb index c07ea947d..35e7ab392 100644 --- a/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb +++ b/tests/beaker_tests/cisco_interface_ospf/test_interface_ospf.rb @@ -106,13 +106,15 @@ }, } -def test_harness_dependencies(_tests, id) +def test_harness_dependencies(tests, id) return unless id == :default test_set(agent, 'feature ospf ; router ospf Sample') + # System-level switchport dependencies + config_system_default_switchport?(tests, id) + config_system_default_switchport_shutdown?(tests, id) end def cleanup(agent, intf) - logger.error(test_get(agent, 'incl .*')) # DEBUGGING ONLY - REMOVE test_set(agent, 'no feature ospf ; no feature bfd') interface_cleanup(agent, intf) end From d155ee49070c662dc19c5ba342b1f2df84569767 Mon Sep 17 00:00:00 2001 From: saichint Date: Fri, 16 Sep 2016 08:13:18 -0700 Subject: [PATCH 117/203] Dhcp Relay Global (#392) * Fix interface_ospf provider and beaker * doc * doc * review comments * add mtu, pri, shut and tx delay to int ospf * documentation * bug fix * Fix for symmetry and resilient no support on n3k * review comment * rewview comments * dhcp relay and storm control -- not tested yet * beaker test * fix non default test * removed storm control broadcast and multicast for n7k * fix munging * demo manifest changes * cleanup manifests * dhcp and l3 are merged * merge L2 and storm control * docs * review comments * dhcp_relay_global provider * beaker * fix beaker * Fix provider code for removal of name * documentation * typo in beaker * fix doc * fix doc * fix doc for version --- CHANGELOG.md | 1 + README.md | 78 ++++++++ examples/cisco/demo_dhcp_relay_global.pp | 66 +++++++ examples/demo_all_cisco.pp | 1 + .../provider/cisco_dhcp_relay_global/cisco.rb | 131 +++++++++++++ lib/puppet/type/cisco_dhcp_relay_global.rb | 166 +++++++++++++++++ .../test_dhcp_relay_global.rb | 175 ++++++++++++++++++ 7 files changed, 618 insertions(+) create mode 100644 examples/cisco/demo_dhcp_relay_global.pp create mode 100644 lib/puppet/provider/cisco_dhcp_relay_global/cisco.rb create mode 100644 lib/puppet/type/cisco_dhcp_relay_global.rb create mode 100644 tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 88effae34..76ed883fc 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### New feature support #### Cisco Resources - `cisco_bfd_global` type and provider. +- `cisco_dhcp_relay_global` type and provider. - `cisco_ospf_area` type and provider. - `cisco_ospf_area_vlink` type and provider. diff --git a/README.md b/README.md index 8e635dfb1..71c697d29 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,9 @@ The following resources include cisco types and providers along with cisco provi * [`cisco_bridge_domain`](#type-cisco_bridge_domain) * [`cisco_bridge_domain_vni`](#type-cisco_bridge_domain_vni) +* DHCP Types + * [`cisco_dhcp_relay_global`](#type-cisco_dhcp_relay_global) + * Domain Types * [`domain_name (netdev_stdlib)`](#type-domain_name) * [`name_server (netdev_stdlib)`](#type-name_server) @@ -292,6 +295,7 @@ The following resources include cisco types and providers along with cisco provi * [`cisco_bgp_neighbor_af`](#type-cisco_bgp_neighbor_af) * [`cisco_bridge_domain`](#type-cisco_bridge_domain) * [`cisco_bridge_domain_vni`](#type-cisco_bridge_domain_vni) +* [`cisco_dhcp_relay_global`](#type-cisco_dhcp_relay_global) * [`cisco_encapsulation`](#type-cisco_encapsulation) * [`cisco_evpn_vni`](#type-cisco_evpn_vni) * [`cisco_fabricpath_global`](#type-cisco_fabricpath_global) @@ -391,6 +395,7 @@ Symbol | Meaning | Description | [cisco_aaa_group_tacacs](#type-cisco_aaa_group_tacacs) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_acl](#type-cisco_acl) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_ace](#type-cisco_ace) | ✅ | ✅ | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_ace-caveats) | +| [cisco_bfd_global](#type-cisco_bfd_global) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_bfd_global-caveats) | | [cisco_command_config](#type-cisco_command_config) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_bgp](#type-cisco_bgp) | ✅ | ✅ | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_bgp-caveats) | | [cisco_bgp_af](#type-cisco_bgp_af) | ✅* | ✅* | ✅ | ✅* | ✅ | ✅ | \*[caveats](#cisco_bgp_af-caveats) | @@ -398,6 +403,7 @@ Symbol | Meaning | Description | [cisco_bgp_neighbor_af](#type-cisco_bgp_neighbor_af) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_bridge_domain](#type-cisco_bridge_domain) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | | [cisco_bridge_domain_vni](#type-cisco_bridge_domain_vni) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | +| [cisco_dhcp_relay_global](#type-cisco_dhcp_relay_global) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_dhcp_relay_global-caveats) | [cisco_encapsulation](#type-cisco_encapsulation) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | | [cisco_evpn_vni](#type-cisco_evpn_vni) | ✅ | :heavy_minus_sign: | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_evpn_vni-caveats) | | [cisco_fabricpath_global](#type-cisco_fabricpath_global) | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | ✅ | ✅* | :heavy_minus_sign: | \*[caveats](#cisco_fabricpath_global-caveats) | @@ -1634,6 +1640,78 @@ The bridge-domain ID. Valid values are one or range of integers. ##### `member_vni` The Virtual Network Identifier (VNI) id that is mapped to the VLAN. Valid values are one or range of integers +-- +### Type: cisco_dhcp_relay_global + +Manages configuration of a DHCP relay global configuration. + +| Platform | OS Minimum Version | Module Minimum Version | +|----------|:------------------:|:----------------------:| +| N9k | 7.0(3)I2(2e) | 1.4.0 | +| N3k | 7.0(3)I2(2e) | 1.4.0 | +| N5k | 7.3(0)N1(1) | 1.4.0 | +| N6k | 7.3(0)N1(1) | 1.4.0 | +| N7k | 7.3(0)D1(1) | 1.4.0 | +| N8k | 7.3(0)F1(1) | 1.4.0 | + +#### Caveats + +| Property | Caveat Description | +|:--------|:-------------| +| `ipv4_information_option_trust` | Not supported on N5k, N6k | +| `ipv4_information_trust_all` | Not supported on N5k, N6k | +| `ipv4_src_addr_hsrp` | Not supported on N3k, N8k, N9k | +| `ipv4_sub_option_circuit_id_custom` | Not supported on N7k, N8k and supported on N3k and N9k running images I3 and above | +| `ipv4_sub_option_circuit_id_string` | Only supported on N3k | +| `ipv6_option_cisco` | Not supported on N5k, N6k | + +#### Parameters + +##### `ipv4_information_option` +Enables inserting relay information in BOOTREQUEST. Valid values are true, false, 'default'. + +##### `ipv4_information_option_trust` +Enables relay trust functionality on the system. Valid values are true, false, 'default'. + +##### `ipv4_information_option_vpn` +Enables relay support across VRFs. Valid values are true, false, 'default'. + +##### `ipv4_information_trust_all` +Enables relay trust on all the interfaces. Valid values are true, false, 'default'. + +##### `ipv4_relay` +Enables DHCP relay agent. Valid values are true, false, 'default'. + +##### `ipv4_smart_relay` +Enables DHCP smart relay. Valid values are true, false, 'default'. + +##### `ipv4_src_addr_hsrp` +Enables Virtual IP instead of SVI address. Valid values are true, false, 'default'. + +##### `ipv4_src_intf` +Source interface for the DHCPV4 relay. Valid values are string, keyword 'default'. + +##### `ipv4_sub_option_circuit_id_custom` +Enables circuit id customized to include vlan id, slot and port info. Valid values are true, false, 'default'. + +##### `ipv4_sub_option_circuit_id_string` +Specifies suboption format type string. Valid values are string, keyword 'default'. + +##### `ipv4_sub_option_cisco` +Enables cisco propritery suboptions. Valid values are true, false, 'default'. + +##### `ipv6_option_cisco` +Enables cisco propritery suboptions for DHCPV6. Valid values are true, false, 'default'. + +##### `ipv6_option_vpn` +Enables DHCPv6 relay support across VRFs. Valid values are true, false, 'default'. + +##### `ipv6_relay` +Enables DHCPv6 relay agent. Valid values are true, false, 'default'. + +##### `ipv6_src_intf` +Source interface for the DHCPV6 relay. Valid values are string, keyword 'default'. + -- ### Type: cisco_encapsulation Manages a Global VNI Encapsulation profile diff --git a/examples/cisco/demo_dhcp_relay_global.pp b/examples/cisco/demo_dhcp_relay_global.pp new file mode 100644 index 000000000..a8b6b6766 --- /dev/null +++ b/examples/cisco/demo_dhcp_relay_global.pp @@ -0,0 +1,66 @@ +# Manifest to demo cisco_dhcp_relay_global provider +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +class ciscopuppet::cisco::demo_dhcp_relay_global { + + $ipv4_information_option_trust = platform_get() ? { + /(n3k|n7k|n8k|n9k)/ => true, + default => undef + } + + $ipv4_information_trust_all = platform_get() ? { + /(n3k|n7k|n8k|n9k)/ => true, + default => undef + } + + $ipv4_src_addr_hsrp = platform_get() ? { + /(n5k|n6k|n7k)/ => true, + default => undef + } + + $ipv4_sub_option_circuit_id_custom = platform_get() ? { + /(n3k|n5k|n6k|n9k)/ => true, + default => undef + } + + $ipv4_sub_option_circuit_id_string = platform_get() ? { + 'n3k' => '%p%p', + default => undef + } + + $ipv6_option_cisco = platform_get() ? { + /(n3k|n7k|n8k|n9k)/ => true, + default => undef + } + + cisco_dhcp_relay_global { 'default': + ipv4_information_option => true, + ipv4_information_option_trust => $ipv4_information_option_trust, + ipv4_information_option_vpn => true, + ipv4_information_trust_all => $ipv4_information_trust_all, + ipv4_relay => true, + ipv4_smart_relay => true, + ipv4_src_addr_hsrp => $ipv4_src_addr_hsrp, + ipv4_src_intf => 'port-channel100', + ipv4_sub_option_circuit_id_custom => $ipv4_sub_option_circuit_id_custom, + ipv4_sub_option_circuit_id_string => $ipv4_sub_option_circuit_id_string, + ipv4_sub_option_cisco => true, + ipv6_option_cisco => $ipv6_option_cisco, + ipv6_option_vpn => true, + ipv6_relay => true, + ipv6_src_intf => 'loopback1', + } +} diff --git a/examples/demo_all_cisco.pp b/examples/demo_all_cisco.pp index eaf033008..6cbaf8be2 100644 --- a/examples/demo_all_cisco.pp +++ b/examples/demo_all_cisco.pp @@ -31,6 +31,7 @@ include ciscopuppet::cisco::demo_bfd include ciscopuppet::cisco::demo_bgp include ciscopuppet::cisco::demo_command_config + include ciscopuppet::cisco::demo_dhcp_relay_global include ciscopuppet::cisco::demo_evpn include ciscopuppet::cisco::demo_fabricpath include ciscopuppet::cisco::demo_interface diff --git a/lib/puppet/provider/cisco_dhcp_relay_global/cisco.rb b/lib/puppet/provider/cisco_dhcp_relay_global/cisco.rb new file mode 100644 index 000000000..55e125cb2 --- /dev/null +++ b/lib/puppet/provider/cisco_dhcp_relay_global/cisco.rb @@ -0,0 +1,131 @@ +# The Cisco provider for cisco_stp_global +# +# September, 2016 +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'cisco_node_utils' if Puppet.features.cisco_node_utils? +begin + require 'puppet_x/cisco/autogen' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'autogen.rb')) +end + +Puppet::Type.type(:cisco_dhcp_relay_global).provide(:cisco) do + desc 'New Cisco provider for Dhcp Relay Global' + + confine feature: :cisco_node_utils + defaultfor operatingsystem: :nexus + + mk_resource_methods + + DHCP_RELAY_GLOBAL_NON_BOOL_PROPS = [ + :ipv4_src_intf, + :ipv4_sub_option_circuit_id_string, + :ipv6_src_intf, + ] + DHCP_RELAY_GLOBAL_BOOL_PROPS = [ + :ipv4_information_option, + :ipv4_information_option_trust, + :ipv4_information_option_vpn, + :ipv4_information_trust_all, + :ipv4_relay, + :ipv4_smart_relay, + :ipv4_src_addr_hsrp, + :ipv4_sub_option_circuit_id_custom, + :ipv4_sub_option_cisco, + :ipv6_option_cisco, + :ipv6_option_vpn, + :ipv6_relay, + ] + DHCP_RELAY_GLOBAL_ALL_PROPS = DHCP_RELAY_GLOBAL_NON_BOOL_PROPS + + DHCP_RELAY_GLOBAL_BOOL_PROPS + + PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@nu', + DHCP_RELAY_GLOBAL_NON_BOOL_PROPS) + PuppetX::Cisco::AutoGen.mk_puppet_methods(:bool, self, '@nu', + DHCP_RELAY_GLOBAL_BOOL_PROPS) + + def initialize(value={}) + super(value) + @nu = Cisco::DhcpRelayGlobal.new + @property_flush = {} + debug 'Created provider instance of cisco_dhcp_relay_global' + end + + def self.properties_get(global_id, nu_obj) + debug "Checking instance, global #{global_id}" + current_state = { + name: global_id + } + + # Call node_utils getter for each property + DHCP_RELAY_GLOBAL_NON_BOOL_PROPS.each do |prop| + current_state[prop] = nu_obj.send(prop) + end + DHCP_RELAY_GLOBAL_BOOL_PROPS.each do |prop| + val = nu_obj.send(prop) + if val.nil? + current_state[prop] = nil + else + current_state[prop] = val ? :true : :false + end + end + new(current_state) + end # self.properties_get + + def self.instances + globals = [] + Cisco::DhcpRelayGlobal.globals.each do |global_id, nu_obj| + globals << properties_get(global_id, nu_obj) + end + globals + end + + def self.prefetch(resources) + globals = instances + + resources.keys.each do |id| + provider = globals.find { |nu_obj| nu_obj.instance_name == id } + resources[id].provider = provider unless provider.nil? + end + end # self.prefetch + + def instance_name + name + end + + def properties_set(new_drg=false) + DHCP_RELAY_GLOBAL_ALL_PROPS.each do |prop| + next unless @resource[prop] + send("#{prop}=", @resource[prop]) if new_drg + unless @property_flush[prop].nil? + @nu.send("#{prop}=", @property_flush[prop]) if + @nu.respond_to?("#{prop}=") + end + end + end + + def flush + new_drg = false + if @nu.nil? + new_drg = true + @nu = Cisco::DhcpRelayGlobal.new + end + properties_set(new_drg) + end +end # Puppet::Type diff --git a/lib/puppet/type/cisco_dhcp_relay_global.rb b/lib/puppet/type/cisco_dhcp_relay_global.rb new file mode 100644 index 000000000..6ec90b0b8 --- /dev/null +++ b/lib/puppet/type/cisco_dhcp_relay_global.rb @@ -0,0 +1,166 @@ +# Manages the Cisco Spanning-tree Global configuration resource. +# +# September 2016 +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Puppet::Type.newtype(:cisco_dhcp_relay_global) do + @doc = " + Manages the Cisco Dhcp Relay Global configuration resource. + cisco_dhcp_relay_global {'default': + ..attributes.. + } + 'default' is only acceptable name for this global config object. + + Example: + cisco_dhcp_relay_global { 'default': + ipv4_information_option => true, + ipv4_information_option_trust => true, + ipv4_information_option_vpn => true, + ipv4_information_trust_all => true, + ipv4_relay => true, + ipv4_smart_relay => true, + ipv4_src_addr_hsrp => true, + ipv4_src_intf => 'port-channel200', + ipv4_sub_option_circuit_id_custom => true, + ipv4_sub_option_circuit_id_string => '%p%p', + ipv4_sub_option_cisco => true, + ipv6_option_cisco => true, + ipv6_option_vpn => true, + ipv6_relay => true, + ipv6_src_intf => 'vlan2', + } + " + ################### + # Resource Naming # + ################### + + newparam(:name, namevar: :true) do + desc 'ID of the dhcp_relay global config. Valid values are default.' + + validate do |inst_name| + fail "only acceptable name is 'default'" if inst_name != 'default' + end + end # param id + + ############## + # Attributes # + ############## + + newproperty(:ipv4_information_option) do + desc 'Enables inserting relay information in BOOTREQUEST' + + newvalues(:true, :false, :default) + end # property ipv4_information_option + + newproperty(:ipv4_information_option_trust) do + desc 'Enables relay trust functionality on the system' + + newvalues(:true, :false, :default) + end # property ipv4_information_option_trust + + newproperty(:ipv4_information_option_vpn) do + desc 'Enables relay support across VRFs' + + newvalues(:true, :false, :default) + end # property ipv4_information_option_vpn + + newproperty(:ipv4_information_trust_all) do + desc 'Enables relay trust on all the interfaces' + + newvalues(:true, :false, :default) + end # property ipv4_information_trust_all + + newproperty(:ipv4_relay) do + desc 'Enables DHCP relay agent' + + newvalues(:true, :false, :default) + end # property ipv4_relay + + newproperty(:ipv4_smart_relay) do + desc 'Enables DHCP smart relay' + + newvalues(:true, :false, :default) + end # property ipv4_smart_relay + + newproperty(:ipv4_src_addr_hsrp) do + desc 'Enables Virtual IP instead of SVI address' + + newvalues(:true, :false, :default) + end # property ipv4_src_addr_hsrp + + newproperty(:ipv4_src_intf) do + desc "Source interface for the DHCPV4 relay. Valid values + are string, keyword 'default'. " + + munge do |value| + value = value.downcase.delete(' ') + value = :default if value == 'default' + value + end + end # property ipv4_src_intf + + newproperty(:ipv4_sub_option_circuit_id_custom) do + desc 'Enables circuit id customized to include vlan id, slot and port info' + + newvalues(:true, :false, :default) + end # property ipv4_sub_option_circuit_id_custom + + newproperty(:ipv4_sub_option_circuit_id_string) do + desc "Specifies suboption format type string. Valid values + are string, keyword 'default'. " + + munge do |value| + value = value.strip + value = :default if value == 'default' + value + end + end # property ipv4_sub_option_circuit_id_string + + newproperty(:ipv4_sub_option_cisco) do + desc 'Enables cisco propritery suboptions' + + newvalues(:true, :false, :default) + end # property ipv4_sub_option_cisco + + newproperty(:ipv6_option_cisco) do + desc 'Enables cisco propritery suboptions for DHCPV6' + + newvalues(:true, :false, :default) + end # property ipv6_option_cisco + + newproperty(:ipv6_option_vpn) do + desc 'Enables DHCPv6 relay support across VRFs' + + newvalues(:true, :false, :default) + end # property ipv6_option_vpn + + newproperty(:ipv6_relay) do + desc 'Enables DHCPv6 relay agent' + + newvalues(:true, :false, :default) + end # property ipv6_relay + + newproperty(:ipv6_src_intf) do + desc "Source interface for the DHCPV6 relay. Valid values + are string, keyword 'default'. " + + munge do |value| + value = value.downcase.delete(' ') + value = :default if value == 'default' + value + end + end # property ipv6_src_intf +end # Puppet::Type.newtype diff --git a/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb b/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb new file mode 100644 index 000000000..45a24ec78 --- /dev/null +++ b/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb @@ -0,0 +1,175 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### + +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# Test hash top-level keys +tests = { + master: master, + agent: agent, + operating_system: 'nexus', + resource_name: 'cisco_dhcp_relay_global', + ensurable: false, +} + +skip_unless_supported(tests) + +# Test hash test cases +tests[:default] = { + desc: '1.1 Default Properties', + title_pattern: 'default', + manifest_props: { + ipv4_information_option: 'default', + ipv4_information_option_trust: 'default', + ipv4_information_option_vpn: 'default', + ipv4_information_trust_all: 'default', + ipv4_relay: 'default', + ipv4_smart_relay: 'default', + ipv4_src_addr_hsrp: 'default', + ipv4_src_intf: 'default', + ipv4_sub_option_circuit_id_custom: 'default', + ipv4_sub_option_circuit_id_string: 'default', + ipv4_sub_option_cisco: 'default', + ipv6_option_cisco: 'default', + ipv6_option_vpn: 'default', + ipv6_relay: 'default', + ipv6_src_intf: 'default', + }, + code: [0, 2], + resource: { + ipv4_information_option: 'false', + ipv4_information_option_trust: 'false', + ipv4_information_option_vpn: 'false', + ipv4_information_trust_all: 'false', + ipv4_relay: 'true', + ipv4_smart_relay: 'false', + ipv4_src_addr_hsrp: 'false', + ipv4_src_intf: 'false', + ipv4_sub_option_circuit_id_custom: 'false', + ipv4_sub_option_circuit_id_string: 'false', + ipv4_sub_option_cisco: 'false', + ipv6_option_cisco: 'false', + ipv6_option_vpn: 'false', + ipv6_relay: 'true', + ipv6_src_intf: 'false', + }, +} + +# Per platform default values +resource = { + n56k: { + ipv4_relay: 'false', + ipv6_relay: 'false', + } +} + +tests[:default][:resource].merge!(resource[:n56k]) if platform[/n(5|6)k/] + +tests[:non_default] = { + desc: '2.1 Non Defaults', + title_pattern: 'default', + manifest_props: { + ipv4_information_option: 'true', + ipv4_information_option_trust: 'true', + ipv4_information_option_vpn: 'true', + ipv4_information_trust_all: 'true', + ipv4_relay: 'false', + ipv4_smart_relay: 'true', + ipv4_src_addr_hsrp: 'true', + ipv4_src_intf: 'port-channel200', + ipv4_sub_option_circuit_id_custom: 'true', + ipv4_sub_option_circuit_id_string: 'WORD', + ipv4_sub_option_cisco: 'true', + ipv6_option_cisco: 'true', + ipv6_option_vpn: 'true', + ipv6_relay: 'false', + ipv6_src_intf: 'loopback1', + }, +} + +# Per platform non default values +manifest = { + n56k: { + ipv4_relay: 'true', + ipv6_relay: 'true', + } +} + +tests[:non_default][:manifest_props].merge!(manifest[:n56k]) if platform[/n(5|6)k/] + +def unsupported_properties(_tests, _id) + unprops = [] + if platform[/n3k/] + unprops << + :ipv4_src_addr_hsrp + elsif platform[/n(5|6)k/] + unprops << + :ipv4_information_option_trust << + :ipv4_information_trust_all << + :ipv4_sub_option_circuit_id_string << + :ipv6_option_cisco + elsif platform[/n7k/] + unprops << + :ipv4_sub_option_circuit_id_custom << + :ipv4_sub_option_circuit_id_string + elsif platform[/n8k/] + unprops << + :ipv4_src_addr_hsrp << + :ipv4_sub_option_circuit_id_custom << + :ipv4_sub_option_circuit_id_string + elsif platform[/n9k/] + unprops << + :ipv4_src_addr_hsrp << + :ipv4_sub_option_circuit_id_string + end + unprops << :ipv4_sub_option_circuit_id_custom if nexus_image['I2'] + unprops +end + +def cleanup(agent) + test_set(agent, 'no feature dhcp') +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(agent) } + cleanup(agent) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + test_harness_run(tests, :default) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + + cleanup(agent) + test_harness_run(tests, :non_default) + + # ------------------------------------------------------------------- + skipped_tests_summary(tests) +end + +logger.info("TestCase :: #{tests[:resource_name]} :: End") From 471ddd8e78da3f8e248aa9bc2b58270ecf8e119c Mon Sep 17 00:00:00 2001 From: mikewiebe Date: Fri, 16 Sep 2016 15:42:51 -0400 Subject: [PATCH 118/203] Remove packet_size prop testing for I2 and I3 images --- tests/beaker_tests/cisco_snmp_server/test_snmp_server.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/beaker_tests/cisco_snmp_server/test_snmp_server.rb b/tests/beaker_tests/cisco_snmp_server/test_snmp_server.rb index 7ffcb827b..723aeb133 100644 --- a/tests/beaker_tests/cisco_snmp_server/test_snmp_server.rb +++ b/tests/beaker_tests/cisco_snmp_server/test_snmp_server.rb @@ -86,6 +86,12 @@ def cleanup(agent) test_set(agent, cmds) end +def unsupported_properties(*) + unprops = [] + unprops << :packet_size if image?[/7.0.3.I2|I3/] # CSCuz14217 + unprops +end + ################################################################# # TEST CASE EXECUTION ################################################################# From 2f1d60e358b7009bfedcf6175357603c9e115831 Mon Sep 17 00:00:00 2001 From: saichint Date: Mon, 19 Sep 2016 11:30:14 -0700 Subject: [PATCH 119/203] cisco_interface beaker test (#394) * Fix interface_ospf provider and beaker * doc * doc * review comments * add mtu, pri, shut and tx delay to int ospf * documentation * bug fix * Fix for symmetry and resilient no support on n3k * review comment * rewview comments * dhcp relay and storm control -- not tested yet * beaker test * fix non default test * removed storm control broadcast and multicast for n7k * fix munging * demo manifest changes * cleanup manifests * dhcp and l3 are merged * merge L2 and storm control * docs * review comments * dhcp_relay_global provider * beaker * fix beaker * Fix provider code for removal of name * documentation * typo in beaker * fix doc * fix doc * fix doc for version * fix beaker test for intf --- tests/beaker_tests/cisco_interface/test_interface_L2.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/beaker_tests/cisco_interface/test_interface_L2.rb b/tests/beaker_tests/cisco_interface/test_interface_L2.rb index 31209b8f6..db385aaa0 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_L2.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_L2.rb @@ -49,7 +49,7 @@ tests[:default_access] = { desc: "1.1 Default 'access' Properties", title_pattern: intf, - code: [0], + code: [0, 2], preclean_intf: true, sys_def_switchport: true, sys_def_sw_shut: true, From 7252d70b542eb2110906ed92759104a4731ebe95 Mon Sep 17 00:00:00 2001 From: saichint Date: Thu, 29 Sep 2016 10:27:15 -0700 Subject: [PATCH 120/203] event_history_periodic changes for bgp (#395) * Fix interface_ospf provider and beaker * doc * doc * review comments * add mtu, pri, shut and tx delay to int ospf * documentation * bug fix * Fix for symmetry and resilient no support on n3k * review comment * rewview comments * dhcp relay and storm control -- not tested yet * beaker test * fix non default test * removed storm control broadcast and multicast for n7k * fix munging * demo manifest changes * cleanup manifests * dhcp and l3 are merged * merge L2 and storm control * docs * review comments * dhcp_relay_global provider * beaker * fix beaker * Fix provider code for removal of name * documentation * typo in beaker * fix doc * fix doc * fix doc for version * fix beaker test for intf * fix bgp for event_history * move facter to provider * beaker for event_history_periodic * documentation * review comments * review comments --- README.md | 11 ++++---- lib/puppet/provider/cisco_bgp/cisco.rb | 22 +++++++++++++++ lib/puppet/type/cisco_bgp.rb | 34 +++++++----------------- tests/beaker_tests/cisco_bgp/test_bgp.rb | 14 ++++++++-- 4 files changed, 50 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 71c697d29..cfc978ba8 100644 --- a/README.md +++ b/README.md @@ -1038,9 +1038,10 @@ Manages configuration of a BGP instance. |:--------|:-------------| | `disable_policy_batching_ipv4` | Not supported on N5k, N6k, N7k | | `disable_policy_batching_ipv6` | Not supported on N5k, N6k, N7k | +| `event_history_periodic ` | default value is 'false' for N3|9k on 7.0(3)I5(1) and later images | | `neighbor_down_fib_accelerate` | Not supported on N5k, N6k, N7k | | `reconnect_interval` | Not supported on N5k, N6k, N7k | -| `suppress_fib_pending` | Idempotence supported only on I5 images | +| `suppress_fib_pending` | Idempotence supported only on 7.0(3)I5(1) and later images N3|9k | #### Parameters @@ -1098,16 +1099,16 @@ Enable/Disable the batching evaluation of prefix advertisements to all peers wit Enable/Disable enforces the neighbor autonomous system to be the first AS number listed in the AS path attribute for eBGP. Valid values are 'true', 'false', and 'default'. On NX-OS, this property is only supported in the global BGP context. ##### `event_history_cli` -Enable/Disable cli event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. +Enable/Disable/specify size of cli event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. Size can also be specified in bytes. ##### `event_history_detail` -Enable/Disable detail event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. +Enable/Disable/specify size of detail event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. Size can also be specified in bytes. ##### `event_history_events` -Enable/Disable event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. +Enable/Disable/specify size of event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. Size can also be specified in bytes. ##### `event_history_periodic` -Enable/Disable periodic event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. +Enable/Disable/specify size of periodic event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. Size can also be specified in bytes. ##### `fast_external_fallover` Enable/Disable immediately reset the session if the link to a directly connected BGP peer goes down. Valid values are 'true', 'false', and 'default'. On NX-OS, this property is only supported in the global BGP context. diff --git a/lib/puppet/provider/cisco_bgp/cisco.rb b/lib/puppet/provider/cisco_bgp/cisco.rb index e19f7d651..a22847956 100644 --- a/lib/puppet/provider/cisco_bgp/cisco.rb +++ b/lib/puppet/provider/cisco_bgp/cisco.rb @@ -209,6 +209,28 @@ def timer_bestpath_limit_set @bgp_vrf.timer_bestpath_limit_set(limit, always) end + def legacy_image? + fd = Facter.value('cisco') + image = fd['images']['system_image'] + pid = fd['inventory']['chassis']['pid'] + image[/7.0.3.I2|I3|I4/] || pid[/N(5|6|7|8)/] + end + + def event_history_periodic + return 'default' if @property_hash[:event_history_periodic] == @bgp_vrf.default_event_history_periodic && + resource[:event_history_periodic] == 'default' + return 'true' if @property_hash[:event_history_periodic] == 'size_small' && + resource[:event_history_periodic] == 'true' && legacy_image? + @property_hash[:event_history_periodic] + end + + def event_history_periodic=(should_value) + should_value = @bgp_vrf.default_event_history_periodic if should_value == 'default' + should_value = 'size_small' if should_value == 'true' && legacy_image? + should_value = should_value.to_sym unless should_value =~ /\A\d+\z/ + @property_flush[:event_history_periodic] = should_value + end + # confederation_peers requires a custom getter and setter because we are # working with arrays. When the manifest entry is set to default, # puppet creates an array with the symbol default. [:default]. diff --git a/lib/puppet/type/cisco_bgp.rb b/lib/puppet/type/cisco_bgp.rb index 6e5f8337b..20e4c4848 100644 --- a/lib/puppet/type/cisco_bgp.rb +++ b/lib/puppet/type/cisco_bgp.rb @@ -347,55 +347,41 @@ def insync?(is) newproperty(:event_history_cli) do desc "event_history_cli state. Valid values are True, False, size_small, - size_medium, size_large, size_disable or 'default'" + size_medium, size_large, size_disable, size in bytes or 'default'" munge do |value| value = 'size_small' if value == 'true' - value.to_sym + value = value.to_sym unless value =~ /\A\d+\z/ + value end - - newvalues(:true, :false, :default, - :size_small, :size_medium, :size_large, :size_disable) end # property event_history_cli newproperty(:event_history_detail) do desc "event_history_detail state. Valid values are True, False, size_small, - size_medium, size_large, size_disable or 'default'" + size_medium, size_large, size_disable, size in bytes or 'default'" munge do |value| value = 'size_disable' if value == 'true' - value.to_sym + value = value.to_sym unless value =~ /\A\d+\z/ + value end - - newvalues(:true, :false, :default, - :size_small, :size_medium, :size_large, :size_disable) end # property event_history_detail newproperty(:event_history_events) do desc "event_history_events state. Valid values are True, False, size_small, - size_medium, size_large, size_disable or 'default'" + size_medium, size_large, size_disable, size in bytes or 'default'" munge do |value| value = 'size_small' if value == 'true' value = 'size_disable' if value == 'false' - value.to_sym + value = value.to_sym unless value =~ /\A\d+\z/ + value end - - newvalues(:true, :false, :default, - :size_small, :size_medium, :size_large, :size_disable) end # property event_history_events newproperty(:event_history_periodic) do desc "event_history_periodic state. Valid values are True, False, size_small, - size_medium, size_large, size_disable or 'default'" - - munge do |value| - value = 'size_small' if value == 'true' - value.to_sym - end - - newvalues(:true, :false, :default, - :size_small, :size_medium, :size_large, :size_disable) + size_medium, size_large, size_disable, size in bytes or 'default'" end # property event_history_periodic newproperty(:fast_external_fallover) do diff --git a/tests/beaker_tests/cisco_bgp/test_bgp.rb b/tests/beaker_tests/cisco_bgp/test_bgp.rb index 721dd1d59..d052f36dd 100644 --- a/tests/beaker_tests/cisco_bgp/test_bgp.rb +++ b/tests/beaker_tests/cisco_bgp/test_bgp.rb @@ -82,7 +82,7 @@ 'event_history_cli' => 'size_small', 'event_history_detail' => 'size_disable', 'event_history_events' => 'size_small', - 'event_history_periodic' => 'size_small', + 'event_history_periodic' => 'false', 'fast_external_fallover' => 'true', 'flush_routes' => 'false', 'graceful_restart' => 'true', @@ -103,6 +103,16 @@ }, } +# older_version default value +resource = { + legacy: { + 'event_history_periodic' => 'size_small' + } +} + +tests[:default][:resource].merge!(resource[:legacy]) if + nexus_image[/I2|I3|I4/] || platform[/n5|n6|n7|n8/] + # Non-default Tests. NOTE: [:resource] = [:manifest_props] for all non-default tests[:non_default] = { desc: '2.1 Non Defaults', @@ -123,7 +133,7 @@ event_history_cli: 'size_medium', event_history_detail: 'size_large', event_history_events: 'size_disable', - event_history_periodic: 'false', + event_history_periodic: '100000', fast_external_fallover: 'false', flush_routes: 'true', graceful_restart: 'false', From 71fac93408c7e13e668c093dc00529389519ab3d Mon Sep 17 00:00:00 2001 From: saichint Date: Sat, 8 Oct 2016 13:33:21 -0700 Subject: [PATCH 121/203] bgp event_history (#400) * fix bgp event-history * beaker changes * doc and manifest * review comments * manifest review comment --- CHANGELOG.md | 3 + README.md | 9 +++ examples/cisco/demo_bgp.pp | 18 +++++ lib/puppet/provider/cisco_bgp/cisco.rb | 92 ++++++++++++++++++++++-- lib/puppet/type/cisco_bgp.rb | 28 +++++--- tests/beaker_tests/cisco_bgp/test_bgp.rb | 90 ++++++++++++++--------- 6 files changed, 188 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 775274f73..3bb64725c 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### New feature support ### Added +- Extend cisco_bgp with attributes: + - `event_history_errors` + - `event_history_objstore` ### Changed diff --git a/README.md b/README.md index cfc978ba8..c5c78560c 100644 --- a/README.md +++ b/README.md @@ -1038,6 +1038,9 @@ Manages configuration of a BGP instance. |:--------|:-------------| | `disable_policy_batching_ipv4` | Not supported on N5k, N6k, N7k | | `disable_policy_batching_ipv6` | Not supported on N5k, N6k, N7k | +| `event_history_errors ` | supported on N3|9k on 7.0(3)I5(1) and later images | +| `event_history_events ` | default value is 'large' for N3|9k on 7.0(3)I5(1) and later images | +| `event_history_objstore ` | supported on N3|9k on 7.0(3)I5(1) and later images | | `event_history_periodic ` | default value is 'false' for N3|9k on 7.0(3)I5(1) and later images | | `neighbor_down_fib_accelerate` | Not supported on N5k, N6k, N7k | | `reconnect_interval` | Not supported on N5k, N6k, N7k | @@ -1104,9 +1107,15 @@ Enable/Disable/specify size of cli event history buffer. Valid values are 'true' ##### `event_history_detail` Enable/Disable/specify size of detail event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. Size can also be specified in bytes. +##### `event_history_errors` +Enable/Disable/specify size of error history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. Size can also be specified in bytes. + ##### `event_history_events` Enable/Disable/specify size of event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. Size can also be specified in bytes. +##### `event_history_objstore` +Enable/Disable/specify size of objstore history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. Size can also be specified in bytes. + ##### `event_history_periodic` Enable/Disable/specify size of periodic event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. Size can also be specified in bytes. diff --git a/examples/cisco/demo_bgp.pp b/examples/cisco/demo_bgp.pp index 42e11428b..dcd792fb2 100644 --- a/examples/cisco/demo_bgp.pp +++ b/examples/cisco/demo_bgp.pp @@ -58,6 +58,22 @@ default => undef } + $event_history_errors = platform_get() ? { + /(n3k|n9k)/ => $facts['cisco']['images']['system_image'] ? { + /(I2|I3|I4)/ => undef, + default => 'size_small' + }, + default => undef + } + + $event_history_objstore = platform_get() ? { + /(n3k|n9k)/ => $facts['cisco']['images']['system_image'] ? { + /(I2|I3|I4)/ => undef, + default => 'size_small' + }, + default => undef + } + cisco_bgp { '55.77 default': ensure => present, @@ -73,7 +89,9 @@ enforce_first_as => false, event_history_cli => 'size_small', event_history_detail => 'size_medium', + event_history_errors => $event_history_errors, event_history_events => 'size_large', + event_history_objstore => $event_history_objstore, event_history_periodic => 'size_disable', maxas_limit => $maxas_limit, suppress_fib_pending => $suppress_fib_pending, diff --git a/lib/puppet/provider/cisco_bgp/cisco.rb b/lib/puppet/provider/cisco_bgp/cisco.rb index a22847956..7690ced5c 100644 --- a/lib/puppet/provider/cisco_bgp/cisco.rb +++ b/lib/puppet/provider/cisco_bgp/cisco.rb @@ -41,7 +41,9 @@ :confederation_peers, :event_history_cli, :event_history_detail, + :event_history_errors, :event_history_events, + :event_history_objstore, :event_history_periodic, :disable_policy_batching_ipv4, :disable_policy_batching_ipv6, @@ -216,17 +218,95 @@ def legacy_image? image[/7.0.3.I2|I3|I4/] || pid[/N(5|6|7|8)/] end + def event_history_default?(prop) + @property_hash[prop.to_sym] == @bgp_vrf.send("default_#{prop}") + end + + def event_history_false?(prop) + @property_hash[prop.to_sym] == 'false' + end + + def event_history_cli + case resource[:event_history_cli] + when 'default' + return 'default' if event_history_default?('event_history_cli') + when 'true' + return 'true' if event_history_default?('event_history_cli') + when 'size_disable' + return 'size_disable' if + event_history_false?('event_history_cli') && !legacy_image? + end + @property_hash[:event_history_cli] + end + + def event_history_cli=(should_value) + should_value = @bgp_vrf.default_event_history_cli if + should_value == 'default' || should_value == 'true' + should_value = should_value.to_sym unless should_value =~ /\A\d+\z/ + @property_flush[:event_history_cli] = should_value + end + + def event_history_detail + case resource[:event_history_detail] + when 'default' + return 'default' if event_history_default?('event_history_detail') + when 'size_disable' + return 'size_disable' if + event_history_default?('event_history_detail') && !legacy_image? + end + @property_hash[:event_history_detail] + end + + def event_history_detail=(should_value) + should_value = @bgp_vrf.default_event_history_detail if + should_value == 'default' + should_value = @bgp_vrf.default_event_history_detail if + should_value == 'size_disable' && !legacy_image? + should_value = should_value.to_sym unless should_value =~ /\A\d+\z/ + @property_flush[:event_history_detail] = should_value + end + + def event_history_events + case resource[:event_history_events] + when 'default' + return 'default' if event_history_default?('event_history_events') + when 'true' + return 'true' if event_history_default?('event_history_events') + when 'size_disable' + return 'size_disable' if event_history_false?('event_history_events') + end + @property_hash[:event_history_events] + end + + def event_history_events=(should_value) + should_value = @bgp_vrf.default_event_history_events if + should_value == 'default' || should_value == 'true' + should_value = 'false' if should_value == 'size_disable' && !legacy_image? + should_value = should_value.to_sym unless should_value =~ /\A\d+\z/ + @property_flush[:event_history_events] = should_value + end + def event_history_periodic - return 'default' if @property_hash[:event_history_periodic] == @bgp_vrf.default_event_history_periodic && - resource[:event_history_periodic] == 'default' - return 'true' if @property_hash[:event_history_periodic] == 'size_small' && - resource[:event_history_periodic] == 'true' && legacy_image? + case resource[:event_history_periodic] + when 'default' + return 'default' if event_history_default?('event_history_periodic') + when 'true' + return 'true' if event_history_default?('event_history_periodic') && + legacy_image? + when 'size_disable' + return 'size_disable' if + event_history_default?('event_history_periodic') && !legacy_image? + end @property_hash[:event_history_periodic] end def event_history_periodic=(should_value) - should_value = @bgp_vrf.default_event_history_periodic if should_value == 'default' - should_value = 'size_small' if should_value == 'true' && legacy_image? + should_value = @bgp_vrf.default_event_history_periodic if + should_value == 'default' + should_value = @bgp_vrf.default_event_history_periodic if + should_value == 'true' && legacy_image? + should_value = @bgp_vrf.default_event_history_periodic if + should_value == 'size_disable' && !legacy_image? should_value = should_value.to_sym unless should_value =~ /\A\d+\z/ @property_flush[:event_history_periodic] = should_value end diff --git a/lib/puppet/type/cisco_bgp.rb b/lib/puppet/type/cisco_bgp.rb index 20e4c4848..d13bf9730 100644 --- a/lib/puppet/type/cisco_bgp.rb +++ b/lib/puppet/type/cisco_bgp.rb @@ -53,7 +53,9 @@ enforce_first_as => true, event_history_cli => 'true', event_history_detail => 'small', + event_history_errors => 'large', event_history_events => 'large', + event_history_objstore => 'medium', event_history_periodic => 'disable', fast_external_fallover => true, flush_routes => false, @@ -348,36 +350,40 @@ def insync?(is) newproperty(:event_history_cli) do desc "event_history_cli state. Valid values are True, False, size_small, size_medium, size_large, size_disable, size in bytes or 'default'" - - munge do |value| - value = 'size_small' if value == 'true' - value = value.to_sym unless value =~ /\A\d+\z/ - value - end end # property event_history_cli newproperty(:event_history_detail) do desc "event_history_detail state. Valid values are True, False, size_small, size_medium, size_large, size_disable, size in bytes or 'default'" + end # property event_history_detail + + newproperty(:event_history_errors) do + desc "event_history_errors state. Valid values are True, False, size_small, + size_medium, size_large, size_disable, size in bytes or 'default'" munge do |value| - value = 'size_disable' if value == 'true' + value = 'size_medium' if value == 'true' + value = 'false' if value == 'size_disable' value = value.to_sym unless value =~ /\A\d+\z/ value end - end # property event_history_detail + end # property event_history_errors newproperty(:event_history_events) do desc "event_history_events state. Valid values are True, False, size_small, size_medium, size_large, size_disable, size in bytes or 'default'" + end # property event_history_events + + newproperty(:event_history_objstore) do + desc "event_history_objstore state. Valid values are True, False, size_small, + size_medium, size_large, size_disable, size in bytes or 'default'" munge do |value| - value = 'size_small' if value == 'true' - value = 'size_disable' if value == 'false' + value = 'false' if value == 'size_disable' value = value.to_sym unless value =~ /\A\d+\z/ value end - end # property event_history_events + end # property event_history_objstore newproperty(:event_history_periodic) do desc "event_history_periodic state. Valid values are True, False, size_small, diff --git a/tests/beaker_tests/cisco_bgp/test_bgp.rb b/tests/beaker_tests/cisco_bgp/test_bgp.rb index d052f36dd..322fafba5 100644 --- a/tests/beaker_tests/cisco_bgp/test_bgp.rb +++ b/tests/beaker_tests/cisco_bgp/test_bgp.rb @@ -49,7 +49,9 @@ enforce_first_as: 'default', event_history_cli: 'default', event_history_detail: 'default', + event_history_errors: 'default', event_history_events: 'default', + event_history_objstore: 'default', event_history_periodic: 'default', fast_external_fallover: 'default', flush_routes: 'default', @@ -80,8 +82,10 @@ 'disable_policy_batching' => 'false', 'enforce_first_as' => 'true', 'event_history_cli' => 'size_small', - 'event_history_detail' => 'size_disable', - 'event_history_events' => 'size_small', + 'event_history_detail' => 'false', + 'event_history_errors' => 'size_medium', + 'event_history_events' => 'size_large', + 'event_history_objstore' => 'false', 'event_history_periodic' => 'false', 'fast_external_fallover' => 'true', 'flush_routes' => 'false', @@ -106,7 +110,8 @@ # older_version default value resource = { legacy: { - 'event_history_periodic' => 'size_small' + 'event_history_events' => 'size_small', + 'event_history_periodic' => 'size_small', } } @@ -132,7 +137,9 @@ enforce_first_as: 'false', event_history_cli: 'size_medium', event_history_detail: 'size_large', - event_history_events: 'size_disable', + event_history_errors: 'size_small', + event_history_events: 'size_medium', + event_history_objstore: 'size_large', event_history_periodic: '100000', fast_external_fallover: 'false', flush_routes: 'true', @@ -170,6 +177,43 @@ resource: { 'ensure' => 'present' }, } +def unsupp_prop_xr(tests, id) + unprops = [] + vrf = vrf(tests[id]) + + unprops << + :bestpath_med_non_deterministic << + :disable_policy_batching << + :event_history_cli << + :event_history_detail << + :event_history_events << + :event_history_periodic << + :flush_routes << + :graceful_restart_helper << + :isolate << + :log_neighbor_changes << + :maxas_limit << + :neighbor_down_fib_accelerate << + :shutdown << + :suppress_fib_pending << + :timer_bestpath_limit << + :timer_bestpath_limit_always + + if vrf != 'default' + # IOS-XR does not support these properties under a non-default vrf + unprops << + :bestpath_med_confed << + :cluster_id << + :confederation_id << + :confederation_peers << + :graceful_restart << + :graceful_restart_timers_restart << + :graceful_restart_timers_stalepath_time << + :nsr + end + unprops +end + # Overridden to properly handle unsupported properties for this test file. def unsupported_properties(tests, id) unprops = [] @@ -177,41 +221,15 @@ def unsupported_properties(tests, id) vrf = vrf(tests[id]) if operating_system == 'ios_xr' - # IOS-XR does not support these properties - unprops << - :bestpath_med_non_deterministic << - :disable_policy_batching << - :event_history_cli << - :event_history_detail << - :event_history_events << - :event_history_periodic << - :flush_routes << - :graceful_restart_helper << - :isolate << - :log_neighbor_changes << - :maxas_limit << - :neighbor_down_fib_accelerate << - :shutdown << - :suppress_fib_pending << - :timer_bestpath_limit << - :timer_bestpath_limit_always - - if vrf != 'default' - # IOS-XR does not support these properties under a non-default vrf - unprops << - :bestpath_med_confed << - :cluster_id << - :confederation_id << - :confederation_peers << - :graceful_restart << - :graceful_restart_timers_restart << - :graceful_restart_timers_stalepath_time << - :nsr - end + unprops << unsupp_prop_xr(tests, id) else # NX-OS does not support these properties unprops << :nsr + unprops << + :event_history_errors << + :event_history_objstore if nexus_image[/I2|I3|I4/] || platform[/n5|n6|n7|n8/] + if vrf != 'default' # NX-OS does not support these properties under a non-default vrf unprops << @@ -219,7 +237,9 @@ def unsupported_properties(tests, id) :enforce_first_as << :event_history_cli << :event_history_detail << + :event_history_errors << :event_history_events << + :event_history_objstore << :event_history_periodic << :fast_external_fallover << :flush_routes << From 840aee417f2b23ba66e786fd36e756c58828d25a Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Tue, 18 Oct 2016 16:59:51 -0400 Subject: [PATCH 122/203] Rel141/lacp suspend (#401) * Fix lacp_suspend_individual property issue in evergreen * Remove unneeded test_set call in test_interface_portchannel.rb * Update README.me with lacp_suspend_individual caveat * Futher clarify caveat for lacp_suspend_individual property * Futher clarify caveat for lacp_suspend_individual property * Test non-default value for n3k --- README.md | 1 + .../cisco_interface_portchannel/test_interface_portchannel.rb | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c5c78560c..44470c6fe 100644 --- a/README.md +++ b/README.md @@ -2395,6 +2395,7 @@ Manages configuration of a portchannel interface instance. | Property | Caveat Description | |:--------|:-------------| | `port_hash_distribution `
`port_load_defer ` | Not supported on N5k, N6k | +| `lacp_suspend_individual` | **WARNING:** On N9k, the portchannel interface must be shutdown before the property can be set. This provider automatically shuts the interface down if needed.
The interface is automatically restored to the original state after the property is set. | #### Parameters diff --git a/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb b/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb index 1c238e364..4c23b53ce 100755 --- a/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb +++ b/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb @@ -90,7 +90,7 @@ 'lacp_graceful_convergence' => 'true', 'lacp_max_bundle' => '32', 'lacp_min_links' => '1', - 'lacp_suspend_individual' => 'true', + 'lacp_suspend_individual' => platform[/n3k/] ? 'false' : 'true', 'port_hash_distribution' => 'false', 'port_load_defer' => 'false', }, @@ -105,7 +105,7 @@ lacp_graceful_convergence: 'false', lacp_max_bundle: '10', lacp_min_links: '3', - lacp_suspend_individual: 'false', + lacp_suspend_individual: platform[/n3k/] ? 'true' : 'false', port_hash_distribution: 'fixed', port_load_defer: 'true', }, From d2f4b55cc5c321435a00c4768c3b49647d9b37b2 Mon Sep 17 00:00:00 2001 From: saichint Date: Thu, 20 Oct 2016 13:42:48 -0700 Subject: [PATCH 123/203] fix vrf beaker (#402) --- tests/beaker_tests/cisco_vrf/test_vrf.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/beaker_tests/cisco_vrf/test_vrf.rb b/tests/beaker_tests/cisco_vrf/test_vrf.rb index 7b8f2f112..0bcf39953 100644 --- a/tests/beaker_tests/cisco_vrf/test_vrf.rb +++ b/tests/beaker_tests/cisco_vrf/test_vrf.rb @@ -75,7 +75,7 @@ def unsupported_properties(_tests, _id) unprops << :vni unless platform[/n9k/] unprops << :route_distinguisher if nexus_image['I2'] - # unprops << :description if image?[/7.3.0.D1.1/] # CSCuy36637 + unprops << :description if image?[/7.3.0.D1.1|7.3.0.N1.1/] # CSCuy36637 else unprops << From 7f0c158b666bda8d54c81ec6d7c2cdf0b7bc71f0 Mon Sep 17 00:00:00 2001 From: mikewiebe Date: Mon, 24 Oct 2016 13:52:29 -0400 Subject: [PATCH 124/203] Remove comment that no longer applied with the latest node utils gem --- lib/puppet/provider/cisco_command_config/cisco.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/puppet/provider/cisco_command_config/cisco.rb b/lib/puppet/provider/cisco_command_config/cisco.rb index 212a02c3b..780af6ca3 100644 --- a/lib/puppet/provider/cisco_command_config/cisco.rb +++ b/lib/puppet/provider/cisco_command_config/cisco.rb @@ -44,13 +44,6 @@ def command # Compare full manifest config to running-config. existing_str = manifest_hash.compare_with(running_hash) debug "Existing:\n>#{existing_str}<" - # Note: 'existing_str' may sometimes include confusing (but normal) contents - # if the 'manifest_hash' contains a 'no' command that is not present in - # running-config. For example, manifest_hash contains 'no foo bar'; the - # compare_with() logic does not find 'no foo bar' so it will strip the 'no' - # and search for 'foo bar' (in the proper context); that will not be found - # so it will add 'no foo bar' to 'existing_str', which allows the logic - # below to exclude 'no foo bar' from 'min_config_hash'. manifest_config_str = Cisco::ConfigParser::Configuration.config_hash_to_str( From 018764b02426a4d69b473054c3eca9c4dabe0b38 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Mon, 31 Oct 2016 09:51:41 -0400 Subject: [PATCH 125/203] Scrub N8k References --- docs/README-agent-install.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/README-agent-install.md b/docs/README-agent-install.md index c80e54c3d..a825a633a 100644 --- a/docs/README-agent-install.md +++ b/docs/README-agent-install.md @@ -44,7 +44,6 @@ Cisco Nexus N3k | NX-OS | 7.0(3)I2(1) and later Cisco Nexus N5k | NX-OS | 7.3(0)N1(1) and later Cisco Nexus N6k | NX-OS | 7.3(0)N1(1) and later Cisco Nexus N7k | NX-OS | 7.3(0)D1(1) and later -Cisco Nexus N8k | NX-OS | 7.0(3)F1(1) and later
@@ -58,8 +57,8 @@ Disk | **400 MB** | Minimum free space before installing Puppet agent | NX-OS Environment | Supported Platforms | | :--|:--:|:--| -`bash-shell` | N3k, N8k, N9k | This is the native WRL Linux environment underlying NX-OS. It is disabled by default on NX-OS. | -`guestshell` | N3k, N8k, N9k | This is a secure Linux container environment running CentOS. It is enabled by default in most platforms that support it. | +`bash-shell` | N3k, N9k | This is the native WRL Linux environment underlying NX-OS. It is disabled by default on NX-OS. | +`guestshell` | N3k, N9k | This is a secure Linux container environment running CentOS. It is enabled by default in most platforms that support it. | `open agent`
`container (OAC)` | N5k, N6k, N7k | This is a 32-bit CentOS-based container created specifically for running Puppet Agent software. | * *OAC containers are created for specific platforms and must be downloaded from Cisco (see [OAC Download](#env-oac)). The OAC must be installed before the Puppet Agent can be installed.* From fd0dbc1e9f543ccdb359ac89e32a648bdec03187 Mon Sep 17 00:00:00 2001 From: saichint Date: Fri, 4 Nov 2016 06:38:27 -0700 Subject: [PATCH 126/203] hsrp_global and interface hsrp (#404) * hsrp global new provider * hsrp interface * beaker of intf_hsrp * move int_hsrp to int * documentation --- CHANGELOG.md | 9 ++ README.md | 60 +++++++++ examples/cisco/demo_hsrp.pp | 43 +++++++ examples/demo_all_cisco.pp | 1 + .../provider/cisco_hsrp_global/cisco.rb | 117 ++++++++++++++++++ lib/puppet/provider/cisco_interface/cisco.rb | 6 + lib/puppet/type/cisco_hsrp_global.rb | 65 ++++++++++ lib/puppet/type/cisco_interface.rb | 51 ++++++++ .../cisco_hsrp/test_hsrp_global.rb | 91 ++++++++++++++ .../cisco_hsrp/test_interface_hsrp.rb | 111 +++++++++++++++++ 10 files changed, 554 insertions(+) create mode 100644 examples/cisco/demo_hsrp.pp create mode 100644 lib/puppet/provider/cisco_hsrp_global/cisco.rb create mode 100644 lib/puppet/type/cisco_hsrp_global.rb create mode 100644 tests/beaker_tests/cisco_hsrp/test_hsrp_global.rb create mode 100644 tests/beaker_tests/cisco_hsrp/test_interface_hsrp.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index d1615a364..de03776ea 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,17 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### New feature support +#### Cisco Resources +- `cisco_hsrp_global` type and provider. ### Added +- Extend cisco_interface with attributes: + - `hsrp_bfd` + - `hsrp_delay_minimum` + - `hsrp_delay_reload` + - `hsrp_mac_refresh` + - `hsrp_use_bia` + - `hsrp_version` ### Changed diff --git a/README.md b/README.md index 036cd222a..dca2ae47d 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,9 @@ The following resources include cisco types and providers along with cisco provi * [`cisco_fabricpath_global`](#type-cisco_fabricpath_global) * [`cisco_fabricpath_topology`](#type-cisco_fabricpath_topology) +* HSRP Types + * [`cisco_hsrp_global`](#type-cisco_hsrp_global) + * Interface Types * [`cisco_interface`](#type-cisco_interface) * [`cisco_interface_channel_group`](#type-cisco_interface_channel_group) @@ -300,6 +303,7 @@ The following resources include cisco types and providers along with cisco provi * [`cisco_evpn_vni`](#type-cisco_evpn_vni) * [`cisco_fabricpath_global`](#type-cisco_fabricpath_global) * [`cisco_fabricpath_topology`](#type-cisco_fabricpath_topology) +* [`cisco_hsrp_global`](#type-cisco_hsrp_global) * [`cisco_interface`](#type-cisco_interface) * [`cisco_interface_channel_group`](#type-cisco_interface_channel_group) * [`cisco_interface_ospf`](#type-cisco_interface_ospf) @@ -409,6 +413,7 @@ Symbol | Meaning | Description | [cisco_evpn_vni](#type-cisco_evpn_vni) | ✅ | :heavy_minus_sign: | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_evpn_vni-caveats) | | [cisco_fabricpath_global](#type-cisco_fabricpath_global) | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | ✅ | ✅* | :heavy_minus_sign: | \*[caveats](#cisco_fabricpath_global-caveats) | | [cisco_fabricpath_topology](#type-cisco_fabricpath_topology) | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | ✅ | ✅ | :heavy_minus_sign: | +| [cisco_hsrp_global](#type-cisco_hsrp_global) | ✅ | ✅* | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_hsrp_global-caveats) | | [cisco_interface](#type-cisco_interface) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_interface-caveats) | | [cisco_interface_channel_group](#type-cisco_interface_channel_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_interface_ospf](#type-cisco_interface_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | @@ -1905,6 +1910,35 @@ ID of the VLAN(s) tha are members of this topology. Valid values are integer/int ##### `topo_name` Descriptive name of the topology. Valid values are string +-- +### Type: cisco_hsrp_global + +Manages Cisco Hot Standby Router Protocol (HSRP) global parameters. + +| Platform | OS Minimum Version | Module Minimum Version | +|----------|:------------------:|:----------------------:| +| N9k | 7.0(3)I2(1) | 1.5.0 | +| N3k | 7.0(3)I2(1) | 1.5.0 | +| N5k | 7.3(0)N1(1) | 1.5.0 | +| N6k | 7.3(0)N1(1) | 1.5.0 | +| N7k | 7.3(0)D1(1) | 1.5.0 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | + +#### Caveats + +| Property | Caveat Description | +|:---------|:-------------| +| `bfd_all_intf` | Not supported on N3k | + +#### Parameters + +##### `bfd_all_intf` +Enables BFD for all HSRP sessions on all interfaces. Valid values are 'true', 'false', and +'default'. + +##### `extended_hold` +Configures extended hold on global timers. Valid values are integer, keyword 'default'. + -- ### Type: cisco_interface @@ -1941,6 +1975,12 @@ Manages a Cisco Network Interface. Any resource dependency should be run before | `svi_autostate` | Only supported on N3k,N7k,N9k | | `vlan_mapping` | Only supported on N7k | | `vlan_mapping_enable` | Only supported on N7k | +| `hsrp_bfd` | Not supported on N5k,N6k,N7k
Minimum puppet module version 1.5.0 | +| `hsrp_delay_minimum` | Not supported on N5k,N6k,N7k
Minimum puppet module version 1.5.0 | +| `hsrp_delay_reload` | Not supported on N5k,N6k,N7k
Minimum puppet module version 1.5.0 | +| `hsrp_mac_refresh` | Not supported on N5k,N6k,N7k
Minimum puppet module version 1.5.0 | +| `hsrp_use_bia` | Not supported on N5k,N6k,N7k
Minimum puppet module version 1.5.0 | +| `hsrp_version` | Not supported on N5k,N6k,N7k
Minimum puppet module version 1.5.0 | #### Parameters @@ -2222,6 +2262,26 @@ Enable/Disable autostate on the SVI interface. Valid values are 'true', ###### `svi_management` Enable/Disable management on the SVI interface. Valid values are 'true', 'false', and 'default'. +##### HSRP config attributes + +##### `hsrp_bfd` +Enable HSRP BFD on this interface. Valid values are true, false or 'default'. + +##### `hsrp_delay_minimum` +HSRP intialization minimim delay in seconds. Valid values are integer, keyword 'default' + +##### `hsrp_delay_reload` +HSRP intialization delay after reload in seconds. Valid values are integer, keyword 'default' + +##### `hsrp_mac_refresh` +HSRP mac refresh time in seconds. Valid values are integer, keyword 'default' + +##### `hsrp_use_bia` +HSRP uses this interface's burned in address. Valid values are 'use_bia', 'use_bia_intf' or 'default'. 'use_bia' uses interface's burned in address. 'use_bia_intf' will increase the scope and applies this configuration to all groups on this interface. + +##### `hsrp_version` +HSRP version for this interface. Valid values are integer, keyword 'default'. + -- ### Type: cisco_interface_channel_group diff --git a/examples/cisco/demo_hsrp.pp b/examples/cisco/demo_hsrp.pp new file mode 100644 index 000000000..8747c65a7 --- /dev/null +++ b/examples/cisco/demo_hsrp.pp @@ -0,0 +1,43 @@ +# Manifest to demo cisco_hsrp_global provider +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +class ciscopuppet::cisco::demo_hsrp { + + $bfd_all_intf = platform_get() ? { + /(n5k|n6k|n7k|n8k|n9k)/ => true, + default => undef + } + + cisco_hsrp_global { 'default': + bfd_all_intf => $bfd_all_intf, + extended_hold => 200, + } + + if platform_get() =~ /n(3|9)k/ { + cisco_interface { 'port-channel100': + ensure => 'present', + hsrp_bfd => true, + hsrp_delay_minimum => 200, + hsrp_delay_reload => 300, + hsrp_mac_refresh => 400, + hsrp_use_bia => 'use_bia_intf', + hsrp_version => 2, + } + } else { + warning('This platform does not support interface hsrp properties') + } +} + diff --git a/examples/demo_all_cisco.pp b/examples/demo_all_cisco.pp index 6cbaf8be2..a152f91bf 100644 --- a/examples/demo_all_cisco.pp +++ b/examples/demo_all_cisco.pp @@ -34,6 +34,7 @@ include ciscopuppet::cisco::demo_dhcp_relay_global include ciscopuppet::cisco::demo_evpn include ciscopuppet::cisco::demo_fabricpath + include ciscopuppet::cisco::demo_hsrp include ciscopuppet::cisco::demo_interface #include ciscopuppet::cisco::demo_interface_service_vni include ciscopuppet::cisco::demo_itd diff --git a/lib/puppet/provider/cisco_hsrp_global/cisco.rb b/lib/puppet/provider/cisco_hsrp_global/cisco.rb new file mode 100644 index 000000000..de2c7e143 --- /dev/null +++ b/lib/puppet/provider/cisco_hsrp_global/cisco.rb @@ -0,0 +1,117 @@ +# The Cisco provider for cisco_hsrp_global +# +# October, 2016 +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'cisco_node_utils' if Puppet.features.cisco_node_utils? +begin + require 'puppet_x/cisco/autogen' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'autogen.rb')) +end + +Puppet::Type.type(:cisco_hsrp_global).provide(:cisco) do + desc 'New Cisco provider for Hsrp Global' + + confine feature: :cisco_node_utils + defaultfor operatingsystem: :nexus + + mk_resource_methods + + HSRP_GLOBAL_NON_BOOL_PROPS = [ + :extended_hold + ] + HSRP_GLOBAL_BOOL_PROPS = [ + :bfd_all_intf + ] + HSRP_GLOBAL_ALL_PROPS = HSRP_GLOBAL_NON_BOOL_PROPS + HSRP_GLOBAL_BOOL_PROPS + + PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@nu', + HSRP_GLOBAL_NON_BOOL_PROPS) + PuppetX::Cisco::AutoGen.mk_puppet_methods(:bool, self, '@nu', + HSRP_GLOBAL_BOOL_PROPS) + + def initialize(value={}) + super(value) + @nu = Cisco::HsrpGlobal.new + @property_flush = {} + debug 'Created provider instance of cisco_hsrp_global' + end + + def self.properties_get(global_id, nu_obj) + debug "Checking instance, global #{global_id}" + current_state = { + name: global_id + } + + # Call node_utils getter for each property + HSRP_GLOBAL_NON_BOOL_PROPS.each do |prop| + current_state[prop] = nu_obj.send(prop) + end + HSRP_GLOBAL_BOOL_PROPS.each do |prop| + val = nu_obj.send(prop) + if val.nil? + current_state[prop] = nil + else + current_state[prop] = val ? :true : :false + end + end + new(current_state) + end # self.properties_get + + def self.instances + globals = [] + Cisco::HsrpGlobal.globals.each do |global_id, nu_obj| + globals << properties_get(global_id, nu_obj) + end + globals + end + + def self.prefetch(resources) + globals = instances + + resources.keys.each do |id| + provider = globals.find { |nu_obj| nu_obj.instance_name == id } + resources[id].provider = provider unless provider.nil? + end + end # self.prefetch + + def instance_name + name + end + + def properties_set(new_hg=false) + HSRP_GLOBAL_ALL_PROPS.each do |prop| + next unless @resource[prop] + send("#{prop}=", @resource[prop]) if new_hg + unless @property_flush[prop].nil? + @nu.send("#{prop}=", @property_flush[prop]) if + @nu.respond_to?("#{prop}=") + end + end + end + + def flush + new_hg = false + if @nu.nil? + new_hg = true + @nu = Cisco::HsrpGlobal.new + end + properties_set(new_hg) + end +end # Puppet::Type diff --git a/lib/puppet/provider/cisco_interface/cisco.rb b/lib/puppet/provider/cisco_interface/cisco.rb index d6e8cfbf1..4a659344f 100644 --- a/lib/puppet/provider/cisco_interface/cisco.rb +++ b/lib/puppet/provider/cisco_interface/cisco.rb @@ -45,6 +45,11 @@ :access_vlan, :description, :encapsulation_dot1q, + :hsrp_delay_minimum, + :hsrp_delay_reload, + :hsrp_mac_refresh, + :hsrp_use_bia, + :hsrp_version, :ipv4_acl_in, :ipv4_acl_out, :ipv4_address, @@ -79,6 +84,7 @@ INTF_BOOL_PROPS = [ :bfd_echo, :fabric_forwarding_anycast_gateway, + :hsrp_bfd, :ipv4_forwarding, :ipv4_pim_sparse_mode, :ipv4_proxy_arp, diff --git a/lib/puppet/type/cisco_hsrp_global.rb b/lib/puppet/type/cisco_hsrp_global.rb new file mode 100644 index 000000000..8efa04e7b --- /dev/null +++ b/lib/puppet/type/cisco_hsrp_global.rb @@ -0,0 +1,65 @@ +# Manages the Cisco Hsrp Global configuration resource. +# +# October 2016 +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Puppet::Type.newtype(:cisco_hsrp_global) do + @doc = " + Manages the Cisco Hsrp Global configuration resource. + cisco_hsrp_global {'default': + ..attributes.. + } + 'default' is only acceptable name for this global config object. + + Example: + cisco_hsrp_global { 'default': + bfd_all_intf => true, + extended_hold => 200, + } + " + ################### + # Resource Naming # + ################### + + newparam(:name, namevar: :true) do + desc 'ID of the hsrp global config. Valid values are default.' + + validate do |inst_name| + fail "only acceptable name is 'default'" if inst_name != 'default' + end + end # param id + + ############## + # Attributes # + ############## + + newproperty(:bfd_all_intf) do + desc 'Enables BFD for all HSRP sessions on all interfaces' + + newvalues(:true, :false, :default) + end # property bfd_all_intf + + newproperty(:extended_hold) do + desc "Configures extended hold on global timers. Valid values + are integer, keyword 'default'" + + munge do |value| + value = value.to_s + value = :default if value == 'default' + value + end + end # property extended_hold +end # Puppet::Type.newtype diff --git a/lib/puppet/type/cisco_interface.rb b/lib/puppet/type/cisco_interface.rb index 0c40300c2..069ed6690 100755 --- a/lib/puppet/type/cisco_interface.rb +++ b/lib/puppet/type/cisco_interface.rb @@ -78,6 +78,14 @@ stp_vlan_cost => [[1-4,6,8-12, 1000], [1000, 2568]], stp_vlan_port_priority => [[1-11,20-33, 64], [1111, 160], } + cisco_interface { 'ethernet1/18' : + hsrp_bfd => true, + hsrp_delay_minimum => 222, + hsrp_delay_reload => 10, + hsrp_mac_refresh => 555, + hsrp_use_bia => 'use_bia', + hsrp_version => 2, + } cisco_interface { 'ethernet9/1' : switchport_mode => 'trunk', storm_control_broadcast => '77.77', @@ -1193,4 +1201,47 @@ def is_to_s(value) value end end # property storm_control_unicast + + ############################ + # hsrp attributes # + ############################ + + newproperty(:hsrp_bfd) do + desc 'Enable HSRP BFD on this interface.' + + newvalues(:true, :false, :default) + end # property hsrp_bfd + + newproperty(:hsrp_delay_minimum) do + desc "Hsrp intialization minimim delay in sec. Valid values are + integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property hsrp_delay_minimum + + newproperty(:hsrp_delay_reload) do + desc "Hsrp intialization delay after reload in sec. Valid values are + integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property hsrp_delay_reload + + newproperty(:hsrp_mac_refresh) do + desc "Hsrp mac refresh time in sec. Valid values are + integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property hsrp_mac_refresh + + newproperty(:hsrp_use_bia) do + desc 'Hsrp uses interface burned in address' + + newvalues(:use_bia, :use_bia_intf, :default) + end # property hsrp_use_bia + + newproperty(:hsrp_version) do + desc "Hsrp version. Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property hsrp_version end # Puppet::Type.newtype diff --git a/tests/beaker_tests/cisco_hsrp/test_hsrp_global.rb b/tests/beaker_tests/cisco_hsrp/test_hsrp_global.rb new file mode 100644 index 000000000..4d3721340 --- /dev/null +++ b/tests/beaker_tests/cisco_hsrp/test_hsrp_global.rb @@ -0,0 +1,91 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### + +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# Test hash top-level keys +tests = { + master: master, + agent: agent, + operating_system: 'nexus', + resource_name: 'cisco_hsrp_global', + ensurable: false, +} + +skip_unless_supported(tests) + +# Test hash test cases +tests[:default] = { + desc: '1.1 Default Properties', + title_pattern: 'default', + manifest_props: { + bfd_all_intf: 'default', + extended_hold: 'default', + }, + code: [0, 2], + resource: { + bfd_all_intf: 'false', + extended_hold: 'false', + }, +} + +tests[:non_default] = { + desc: '2.1 Non Defaults', + title_pattern: 'default', + manifest_props: { + bfd_all_intf: true, + extended_hold: 222, + }, +} + +def unsupported_properties(_tests, _id) + unprops = [] + unprops << :bfd_all_intf if platform[/n3k/] + unprops +end + +def cleanup(agent) + test_set(agent, 'no feature hsrp') +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(agent) } + cleanup(agent) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + test_harness_run(tests, :default) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + + cleanup(agent) + test_harness_run(tests, :non_default) + # ------------------------------------------------------------------- +end + +logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_hsrp/test_interface_hsrp.rb b/tests/beaker_tests/cisco_hsrp/test_interface_hsrp.rb new file mode 100644 index 000000000..234b82bbf --- /dev/null +++ b/tests/beaker_tests/cisco_hsrp/test_interface_hsrp.rb @@ -0,0 +1,111 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### + +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# Test hash top-level keys +tests = { + master: master, + agent: agent, + intf_type: 'port-channel', + platform: 'n(3|9)k', + resource_name: 'cisco_interface', +} + +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + +# Find a usable interface for this test +@intf = 'port-channel100' + +# Test hash test cases +tests[:default] = { + desc: '1.1 Default properties', + title_pattern: @intf, + sys_def_switchport: false, + manifest_props: { + switchport_mode: 'disabled', + hsrp_bfd: 'default', + hsrp_delay_minimum: 'default', + hsrp_delay_reload: 'default', + hsrp_mac_refresh: 'default', + hsrp_use_bia: 'default', + hsrp_version: 'default', + }, + code: [0, 2], + resource: { + hsrp_bfd: 'false', + hsrp_delay_minimum: 0, + hsrp_delay_reload: 0, + hsrp_mac_refresh: 'false', + hsrp_use_bia: 'false', + hsrp_version: 1, + }, +} + +tests[:non_default] = { + desc: '2.1 Non Default properties', + title_pattern: @intf, + manifest_props: { + switchport_mode: 'disabled', + hsrp_bfd: 'true', + hsrp_delay_minimum: 100, + hsrp_delay_reload: 200, + hsrp_mac_refresh: 350, + hsrp_use_bia: 'use_bia_intf', + hsrp_version: 2, + }, +} + +def cleanup(agent) + cmd = 'no feature hsrp' + test_set(agent, cmd) + interface_cleanup(agent, @intf) +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(agent) } + cleanup(agent) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + + test_harness_run(tests, :default) + + id = :default + tests[id][:desc] = '1.2 Common Defaults (absent)' + tests[id][:ensure] = :absent + test_harness_run(tests, id) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + + test_harness_run(tests, :non_default) + # ------------------------------------------------------------------- + skipped_tests_summary(tests) +end +logger.info("TestCase :: #{tests[:resource_name]} :: End") From 557a8b0427969a3fafecbff4141cff15cef7316f Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Thu, 17 Nov 2016 12:03:47 -0500 Subject: [PATCH 127/203] Rel150/n8k n9kf rebranding (#406) * N8k -> N9K-F changes * Fix puppet module function name * Remove old README doc * Update N9k-F Module Version * Additional N8k -> N9K-F updates * Refactor platform_fretta function to use os_release fact * Update platform_fretta function comments * Fix image version check * Skip privte vlan tests on n9k-f * Skip cisco_itd tests on n9k-f * Skip private vlan tests on n9k-f * Skip cisco_vpc tests on n9k-f * Anchor n9k platform check * Update platform regexp to use correct packet size for fretta --- README-pre-1.3.0.md | 4251 ----------------- README.md | 118 +- examples/cisco/demo_acl.pp | 28 +- examples/cisco/demo_bfd.pp | 18 +- examples/cisco/demo_dhcp_relay_global.pp | 6 +- examples/cisco/demo_hsrp.pp | 2 +- examples/cisco/demo_interface.pp | 6 +- examples/cisco/demo_portchannel.pp | 6 +- .../parser/functions/platform_fretta.rb | 36 + lib/puppet/parser/functions/platform_get.rb | 14 +- .../cisco_bfd_global/test_bfd_global.rb | 6 +- tests/beaker_tests/cisco_bgp/test_bgp.rb | 4 +- tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb | 4 +- .../test_dhcp_relay_global.rb | 2 +- .../cisco_evpn_vni/test_evpn_vni.rb | 2 +- .../cisco_interface/test_interface_L3.rb | 2 +- .../test_interface_private_vlan.rb | 4 +- .../test_interface_portchannel.rb | 4 +- .../test_itd_device_group.rb | 4 +- .../test_itd_device_group_node.rb | 4 +- .../cisco_itd_service/test_itd_service.rb | 4 +- .../test_overlay_global.rb | 2 +- .../test_portchannel_global.rb | 16 +- .../cisco_snmp_server/test_snmp_server.rb | 4 +- .../cisco_vlan/test_private_vlan.rb | 4 +- .../cisco_vpc_domain/test_vpc_domain.rb | 4 +- .../cisco_vxlan_vtep/test_vxlan_vtep.rb | 2 +- .../test_vxlan_vtep_vni.rb | 14 +- .../test_package_patch.rb | 13 +- tests/beaker_tests/lib/utilitylib.rb | 10 +- 30 files changed, 195 insertions(+), 4399 deletions(-) delete mode 100644 README-pre-1.3.0.md create mode 100644 lib/puppet/parser/functions/platform_fretta.rb diff --git a/README-pre-1.3.0.md b/README-pre-1.3.0.md deleted file mode 100644 index 91e88d89d..000000000 --- a/README-pre-1.3.0.md +++ /dev/null @@ -1,4251 +0,0 @@ -# ciscopuppet - -##### Documentation Workflow Map - -This workflow map aids *users*, *developers* and *maintainers* of the ciscopuppet project in selecting the appropriate document(s) for their task. - -* User Guides - * [README-agent-install.md](https://github.com/cisco/cisco-network-puppet-module/blob/develop/docs/README-agent-install.md) : Agent Installation and Configuration Guide - * [README-beaker-agent-install.md](https://github.com/cisco/cisco-network-puppet-module/blob/develop/docs/README-beaker-agent-install.md) : Automated Agent Installation and Configuration - * [README-package-provider.md](https://github.com/cisco/cisco-network-puppet-module/blob/develop/docs/README-package-provider.md) : Cisco Nexus Package Management using the Package Provider - * [README-example-manifest.md](https://github.com/cisco/cisco-network-puppet-module/blob/develop/examples/README.md) : Example Demo Manifest User Guide - * The remainder of this document is aimed at end users -* Developer Guides - * [CONTRIBUTING.md](https://github.com/cisco/cisco-network-puppet-module/blob/develop/CONTRIBUTING.md) : Contribution guidelines - * [README-develop-types-providers.md](https://github.com/cisco/cisco-network-puppet-module/blob/develop/docs/README-develop-types-providers.md) : Developing new ciscopuppet Types and Providers - * [README-develop-beaker-scripts.md](https://github.com/cisco/cisco-network-puppet-module/blob/develop/docs/README-develop-beaker-scripts.md) : Developing new beaker test scripts for ciscopuppet -* Maintainers Guides - * [README-maintainers.md](https://github.com/cisco/cisco-network-puppet-module/blob/develop/docs/README-maintainers.md) : Guidelines for core maintainers of the ciscopuppet project - * All developer guides apply to maintainers as well - -Please see [Learning Resources](#learning-resources) for additional references. - --- -#### Table of Contents - -1. [Overview](#overview) -1. [Module Description](#module-description) -1. [Setup](#setup) -1. [Usage](#usage) -1. [Platform Support](#platform-support) - * [Provider Support Across Platforms](#provider-support-across-platforms) -1. [Resource Reference](#resource-reference) - * [Resource Type Catalog (by Technology)](#resource-by-tech) - * [Resource Type Catalog (by Name)](#resource-by-name) -1. [Limitations - OS compatibility, etc.](#limitations) -1. [Cisco OS Differences](#cisco-os-differences) -1. [Learning Resources](#learning-resources) - - - -## Overview - -The ciscopuppet module allows a network administrator to manage Cisco Network Elements using Puppet. This module bundles a set of Puppet Types, providers, Beaker Tests, Sample Manifests and Installation Tools for effective network management. The resources and capabilities provided by this Puppet Module will grow with contributions from Cisco, Puppet Labs and the open source community. - -The Cisco Network Elements and Operating Systems managed by this Puppet Module are continuously expanding. Please refer to the [Limitations](#limitations) section for details on currently supported hardware and software. -The Limitations section also provides details on compatible Puppet Agent and Puppet Master versions. - -This GitHub repository contains the latest version of the ciscopuppet module source code. Supported versions of the ciscopuppet module are available at Puppet Forge. Please refer to [SUPPORT.md](SUPPORT.md) for additional details. - -Contributions to this Puppet Module are welcome. Guidelines on contributions to the module are captured in [CONTRIBUTING.md](CONTRIBUTING.md) - -## Module Description - -This module enables management of supported Cisco Network Elements using Puppet. This module enhances the Puppet DSL by introducing new Puppet Types and Providers capable of managing network elements. - -The set of supported network element platforms is continuously expanding. Please refer to the [Limitations](#limitations) section for a list of currently supported platforms. - -## Setup - -### Puppet Master - -The `ciscopuppet` module must be installed on the Puppet Master server. Please see [Puppet Labs: Installing Modules](https://docs.puppetlabs.com/puppet/latest/reference/modules_installing.html) for general information on Puppet module installation. - -### Puppet Agent -The Puppet Agent requires installation and setup on each device. Agent setup can be performed as a manual process or it may be automated. For more information please see the [README-agent-install.md](docs/README-agent-install.md) document for detailed instructions on agent installation and configuration on Cisco Nexus and IOS XR devices. - -#### Artifacts - -As noted in the agent installation guide, these are the current RPM versions for use with ciscopuppet: -* NX-OS: - * `bash-shell`: Use [http://yum.puppetlabs.com/puppetlabs-release-pc1-cisco-wrlinux-5.noarch.rpm](http://yum.puppetlabs.com/puppetlabs-release-pc1-cisco-wrlinux-5.noarch.rpm) - * `guestshell`: Use [http://yum.puppetlabs.com/puppetlabs-release-pc1-el-7.noarch.rpm](http://yum.puppetlabs.com/puppetlabs-release-pc1-el-7.noarch.rpm) - * `open agent container (OAC)`: Use [http://yum.puppetlabs.com/puppetlabs-release-pc1-el-6.noarch.rpm](http://yum.puppetlabs.com/puppetlabs-release-pc1-el-6.noarch.rpm) -* IOS XR: - * Native: Use [http://yum.puppetlabs.com/puppetlabs-release-pc1-cisco-wrlinux-7.noarch.rpm](http://yum.puppetlabs.com/puppetlabs-release-pc1-cisco-wrlinux-7.noarch.rpm) - -### `cisco_node_utils` Ruby gem - -This module has dependencies on the [`cisco_node_utils`](https://rubygems.org/gems/cisco_node_utils) ruby gem. After installing the Puppet Agent software you will then need to install (and possibly configure) the gem on the agent device. See [README-gem-install.md](docs/README-gem-install.md) for detailed instructions. - -## Usage - -The following example shows how to use ciscopuppet to configure ospf on a -Cisco Nexus switch. - -Three types are needed to add OSPF support on an interface: cisco_ospf, -cisco_ospf_vrf, and cisco_interface_ospf. - -First, to configure cisco_ospf to enable ospf on the device, add the -following type in the manifest: - -~~~puppet -cisco_ospf {"Sample": - ensure => present, -} -~~~ - -Then put the ospf router under a VRF, and add the corresponding OSPF configuration. -If the configuration is global, use 'default' as the VRF name. - -~~~puppet -cisco_ospf_vrf {"Sample default": - ensure => 'present', - default_metric => '5', - auto_cost => '46000', -} -~~~ - -Finally apply the ospf into an interface: - -~~~puppet -cisco_interface_ospf {"Ethernet1/2 Sample": - ensure => present, - area => 200, - cost => "200", -} -~~~ - -## Platform Support - -### Provider Support Across Platforms - -The following table indicates which providers are supported on each platform. As platforms are added to the support list they may indicate `Unsupported` for some providers that have not completed the test validation process at the time of this release. Some providers will show caveats for a platform if there are limitations on usage, such as with unsupported properties or hardware limitations. -##### Cisco Providers -| ✅ = Supported
❌ = Unsupported
:heavy_minus_sign: = Not Applicable | N9k | N30xx | N31xx | N56xx | N6k | N7k | N8k | IOS XR | Caveats | -|:---|:---:|:-----:|:-----:|:-----:|:---:|:---:|:---:|:---:|:---:| -| [cisco_aaa_authentication_login](#type-cisco_aaa_authentication_login) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [cisco_aaa_authorization_login_cfg_svc](#type-cisco_aaa_authorization_login_cfg_svc) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [cisco_aaa_authorization_login_exec_svc](#type-cisco_aaa_authorization_login_exec_svc) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [cisco_aaa_group_tacacs](#type-cisco_aaa_group_tacacs) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [cisco_acl](#type-cisco_acl) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [cisco_ace](#type-cisco_ace) | ✅ | ✅ | ✅ | ✅* | ✅* | ✅* | ✅ | ❌ | * [caveats](#cisco_ace-caveats) | -| [cisco_command_config](#type-cisco_command_config) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_bgp](#type-cisco_bgp) | ✅ | ✅ | ✅ | ✅* | ✅* | ✅* | ✅ | ✅ | * [caveats](#cisco_bgp-caveats) | -| [cisco_bgp_af](#type-cisco_bgp_af) | ✅* | ✅* | ✅ | ✅ | ✅* | ✅ | ✅ | ✅ | * [caveats](#cisco_bgp_af-caveats) | -| [cisco_bgp_neighbor](#type-cisco_bgp_neighbor) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_bgp_neighbor_af](#type-cisco_bgp_neighbor_af) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_bridge_domain](#type-cisco_bridge_domain) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | -| [cisco_bridge_domain_vni](#type-cisco_bridge_domain_vni) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | -| [cisco_encapsulation](#type-cisco_encapsulation) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | -| [cisco_evpn_vni](#type-cisco_evpn_vni) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | ✅ | ✅ | ✅ | :heavy_minus_sign: | * [caveats](#cisco_evpn_vni-caveats) | -| [cisco_fabricpath_global](#type-cisco_fabricpath_global) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | ✅ | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | * [caveats](#cisco_fabricpath_global-caveats) | -| [cisco_fabricpath_topology](#type-cisco_fabricpath_topology) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | ✅ | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | -| [cisco_interface](#type-cisco_interface) | ✅ | ✅ | ✅ | ✅* | ✅* | ✅ | ✅ | ✅ | * [caveats](#cisco_interface-caveats) | -| [cisco_interface_channel_group](#type-cisco_interface_channel_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [cisco_interface_ospf](#type-cisco_interface_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [cisco_interface_portchannel](#type-cisco_interface_portchannel) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | ✅ | ❌ | * [caveats](#cisco_interface_portchannel-caveats) | -| [cisco_interface_service_vni](#type-cisco_interface_service_vni) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | -| [cisco_itd_device_group](#type-cisco_itd_device_group) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | -| [cisco_itd_device_group_node](#type-cisco_itd_device_group_node) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | -| [cisco_itd_service](#type-cisco_itd_service) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | * [caveats](#cisco_itd_service-caveats) | -| [cisco_ospf](#type-cisco_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [cisco_ospf_vrf](#type-cisco_ospf_vrf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| ✅ = Supported
❌ = Unsupported
:heavy_minus_sign: = Not Applicable | N9k | N30xx | N31xx | N56xx | N6k | N7k | N8k | IOS XR | Caveats | -| [cisco_overlay_global](#type-cisco_overlay_global) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | ✅ | ✅ | ✅ | :heavy_minus_sign: | -| [cisco_pim](#type-cisco_pim) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [cisco_pim_rp_address](#type-cisco_pim_rp_address) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [cisco_pim_grouplist](#type-cisco_pim_grouplist) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [cisco_portchannel_global](#type-cisco_portchannel_global) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | ❌ | * [caveats](#cisco_portchannel_global-caveats) | -| [cisco_stp_global](#type-cisco_stp_global) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅ | ✅ | ❌ | * [caveats](#cisco_stp_global-caveats) | -| [cisco_snmp_community](#type-cisco_snmp_community) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [cisco_snmp_group](#type-cisco_snmp_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [cisco_snmp_server](#type-cisco_snmp_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [cisco_snmp_user](#type-cisco_snmp_user) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [cisco_tacacs_server](#type-cisco_tacacs_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [cisco_tacacs_server_host](#type-cisco_tacacs_server_host) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [cisco_vdc](#type-cisco_vdc) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | -| [cisco_vlan](#type-cisco_vlan) | ✅* | ✅* | ✅* | ✅ | ✅ | ✅ | ✅ | ❌ | * [caveats](#cisco_vlan-caveats) | -| [cisco_vpc_domain](#type-cisco_vpc_domain) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | ❌ | ❌ | * [caveats](#cisco_vpc_domain-caveats) | -| [cisco_vrf](#type-cisco_vrf) | ✅ | ✅* | ✅* | ✅ | ✅ | ✅ | ✅ | ✅* | * [caveats](#cisco_vrf-caveats) | -| [cisco_vrf_af](#type-cisco_vrf_af) | ✅ | ✅* | ✅* | ✅* | ✅* | ✅* | ✅ | ✅* | * [caveats](#cisco_vrf_af-caveats) | -| [cisco_vtp](#type-cisco_vtp) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [cisco_vxlan_vtep](#type-cisco_vxlan_vtep) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | ✅ | ✅ | ✅ | :heavy_minus_sign: | -| [cisco_vxlan_vtep_vni](#type-cisco_vxlan_vtep_vni) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | ✅ | ✅ | ✅ | :heavy_minus_sign: | - -##### NetDev Providers - -| ✅ = Supported
❌ = Unsupported
:heavy_minus_sign: = Not Applicable | N9k | N30xx | N31xx | N56xx | N6k | N7k | N8k | IOS XR | -|:---|:---:|:-----:|:-----:|:-----:|:---:|:---:|:---:|:---:| -| [domain_name](#type-domain_name) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [name_server](#type-name_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [network_dns](#type-network_dns) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [network_interface](#type-network_interface) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [network_snmp](#type-network_snmp) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [network_trunk](#type-network_trunk) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [network_vlan](#type-network_vlan) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [ntp_config](#type-ntp_config) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [ntp_server](#type-ntp_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [port_channel](#type-port_channel) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [radius](#type-radius) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [radius_global](#type-radius_global) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [radius_server](#type-radius_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅* | * [caveats](#radius_server-caveats) | -| [radius_server_group](#type-tacacs_server_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [search_domain](#type-search_domain) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [snmp_community](#type-snmp_community) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [snmp_notification](#type-snmp_notification) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [snmp_notification_receiver](#type-snmp_notification_receiver) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [snmp_user](#type-snmp_user) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [syslog_server](#type-syslog_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [syslog_setting](#type-syslog_setting) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [tacacs](#type-tacacs) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [tacacs_global](#type-tacacs_global) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [tacacs_server](#type-tacacs_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | -| [tacacs_server_group](#type-tacacs_server_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | - - -## Resource Reference - -The following resources include cisco types and providers along with cisco provider support for netdev stdlib types. Installing the `ciscopuppet` module will install both the `ciscopuppet` and `netdev_stdlib` modules. - -### Resource Type Catalog (by Technology) - -* Miscellaneous Types - * [`cisco_command_config`](#type-cisco_command_config) - * [`cisco_vdc`](#type-cisco_vdc) - -* AAA Types - * [`cisco_aaa_authentication_login`](#type-cisco_aaa_authentication_login) - * [`cisco_aaa_authorization_login_cfg_svc`](#type-cisco_aaa_authorization_login_cfg_svc) - * [`cisco_aaa_authorization_login_exec_svc`](#type-cisco_aaa_authorization_login_exec_svc) - * [`cisco_aaa_group_tacacs`](#type-cisco_aaa_group_tacacs) - -* ACL Types - * [`cisco_ace`](#type-cisco_ace) - * [`cisco_acl`](#type-cisco_acl) - -* BGP Types - * [`cisco_vrf`](#type-cisco_vrf) - * [`cisco_vrf_af`](#type-cisco_vrf_af) - * [`cisco_bgp`](#type-cisco_bgp) - * [`cisco_bgp_af`](#type-cisco_bgp_af) - * [`cisco_bgp_neighbor`](#type-cisco_bgp_neighbor) - * [`cisco_bgp_neighbor_af`](#type-cisco_bgp_neighbor_af) - -* Bridge_Domain Types - * [`cisco_bridge_domain`](#type-cisco_bridge_domain) - * [`cisco_bridge_domain_vni`](#type-cisco_bridge_domain_vni) - -* Domain Types - * [`domain_name (netdev_stdlib)`](#type-domain_name) - * [`name_server (netdev_stdlib)`](#type-name_server) - * [`network_dns (netdev_stdlib)`](#type-network_dns) - * [`search_domain (netdev_stdlib)`](#type-search_domain) - -* Fabricpath Types - * [`cisco_fabricpath_global`](#type-cisco_fabricpath_global) - * [`cisco_fabricpath_topology`](#type-cisco_fabricpath_topology) - -* Interface Types - * [`cisco_interface`](#type-cisco_interface) - * [`cisco_interface_channel_group`](#type-cisco_interface_channel_group) - * [`cisco_interface_ospf`](#type-cisco_interface_ospf) - * [`cisco_interface_portchannel`](#type-cisco_interface_portchannel) - * [`cisco_interface_service_vni`](#type-cisco_interface_service_vni) - * [`network_interface (netdev_stdlib)`](#type-network_interface) - -* Itd Types - * [`cisco_itd_device_group`](#type-cisco_itd_device_group) - * [`cisco_itd_device_group_node`](#type-cisco_itd_device_group_node) - * [`cisco_itd_service`](#type-cisco_itd_service) - -* Multicast Types - * [`cisco_pim`](#type-cisco_pim) - * [`cisco_pim_grouplist`](#type-cisco_pim_grouplist) - * [`cisco_pim_rp_address`](#type-cisco_pim_rp_address) - -* NTP Types - * [`ntp_config (netdev_stdlib)`](#type-ntp_config) - * [`ntp_server (netdev_stdlib)`](#type-ntp_server) - -* OSPF Types - * [`cisco_vrf`](#type-cisco_vrf) - * [`cisco_ospf`](#type-cisco_ospf) - * [`cisco_ospf_vrf`](#type-cisco_ospf_vrf) - * [`cisco_interface_ospf`](#type-cisco_interface_ospf) - -* Portchannel Types - * [`cisco_interface_channel_group`](#type-cisco_interface_channel_group) - * [`cisco_interface_portchannel`](#type-cisco_interface_portchannel) - * [`cisco_portchannel_global`](#type-cisco_portchannel_global) - * [`port_channel (netdev_stdlib)`](#type-port_channel) - -* RADIUS Types - * [`radius (netdev_stdlib)`](#type-radius) - * [`radius_global (netdev_stdlib)`](#type-radius_global) - * [`radius_server (netdev_stdlib)`](#type-radius_server) - * [`radius_server_group (netdev_stdlib)`](#type-radius_server_group) - -* STP Types - * [`cisco_stp_global`](#type-cisco_stp_global) - -* SNMP Types - * [`cisco_snmp_community`](#type-cisco_snmp_community) - * [`cisco_snmp_group`](#type-cisco_snmp_group) - * [`cisco_snmp_server`](#type-cisco_snmp_server) - * [`cisco_snmp_user`](#type-cisco_snmp_user) - * [`network_snmp (netdev_stdlib)`](#type-network_snmp) - * [`snmp_community (netdev_stdlib)`](#type-snmp_community) - * [`snmp_notification (netdev_stdlib)`](#type-snmp_notification) - * [`snmp_notification_receiver (netdev_stdlib)`](#type-snmp_notification_receiver) - * [`snmp_user (netdev_stdlib)`](#type-snmp_user) - -* SYSLOG Types - * [`syslog_server (netdev_stdlib)`](#type-syslog_server) - * [`syslog_setting (netdev_stdlib)`](#type-syslog_setting) - -* TACACS Types - * [`cisco_tacacs_server`](#type-cisco_tacacs_server) - * [`cisco_tacacs_server_host`](#type-cisco_tacacs_server_host) - * [`tacacs (netdev_stdlib)`](#type-tacacs) - * [`tacacs_global (netdev_stdlib)`](#type-tacacs_global) - * [`tacacs_server (netdev_stdlib)`](#type-tacacs_server) - * [`tacacs_server_group (netdev_stdlib)`](#type-tacacs_server_group) - -* VLAN Types - * [`cisco_vlan`](#type-cisco_vlan) - * [`cisco_vtp`](#type-cisco_vtp) - * [`network_trunk (netdev_stdlib)`](#type-network_trunk) - * [`network_vlan (netdev_stdlib)`](#type-network_vlan) - -* VPC Types - * [`cisco_vpc_domain`](#type-cisco_vpc_domain) - -* VRF Types - * [`cisco_vrf`](#type-cisco_vrf) - * [`cisco_vrf_af`](#type-cisco_vrf_af) - -* VNI Types - * [`cisco_interface_service_vni`](#type-cisco_interface_service_vni) - * [`cisco_vni`](#type-cisco_vni) - * [`cisco_encapsulation`](#type-cisco_encapsulation) - -* VXLAN Types - * [`cisco_evpn_vni`](#type-cisco_evpn_vni) - * [`cisco_overlay_global`](#type-cisco_overlay_global) - * [`cisco_vxlan_vtep`](#type-cisco_vxlan_vtep) - * [`cisco_vxlan_vtep_vni`](#type-cisco_vxlan_vtep_vni) - --- -### Cisco Resource Type Catalog (by Name) - -* [`cisco_command_config`](#type-cisco_command_config) -* [`cisco_aaa_authentication_login`](#type-cisco_aaa_authentication_login) -* [`cisco_aaa_authorization_login_cfg_svc`](#type-cisco_aaa_authorization_login_cfg_svc) -* [`cisco_aaa_authorization_login_exec_svc`](#type-cisco_aaa_authorization_login_exec_svc) -* [`cisco_aaa_group_tacacs`](#type-cisco_aaa_group_tacacs) -* [`cisco_acl`](#type-cisco_acl) -* [`cisco_ace`](#type-cisco_ace) -* [`cisco_bgp`](#type-cisco_bgp) -* [`cisco_bgp_af`](#type-cisco_bgp_af) -* [`cisco_bgp_neighbor`](#type-cisco_bgp_neighbor) -* [`cisco_bgp_neighbor_af`](#type-cisco_bgp_neighbor_af) -* [`cisco_bridge_domain`](#type-cisco_bridge_domain) -* [`cisco_bridge_domain_vni`](#type-cisco_bridge_domain_vni) -* [`cisco_encapsulation`](#type-cisco_encapsulation) -* [`cisco_evpn_vni`](#type-cisco_evpn_vni) -* [`cisco_fabricpath_global`](#type-cisco_fabricpath_global) -* [`cisco_fabricpath_topology`](#type-cisco_fabricpath_topology) -* [`cisco_interface`](#type-cisco_interface) -* [`cisco_interface_channel_group`](#type-cisco_interface_channel_group) -* [`cisco_interface_ospf`](#type-cisco_interface_ospf) -* [`cisco_interface_portchannel`](#type-cisco_interface_portchannel) -* [`cisco_interface_service_vni`](#type-cisco_interface_service_vni) -* [`cisco_itd_device_group`](#type-cisco_itd_device_group) -* [`cisco_itd_device_group_node`](#type-cisco_itd_device_group_node) -* [`cisco_itd_service`](#type-cisco_itd_service) -* [`cisco_ospf`](#type-cisco_ospf) -* [`cisco_ospf_vrf`](#type-cisco_ospf_vrf) -* [`cisco_overlay_global`](#type-cisco_overlay_global) -* [`cisco_pim`](#type-cisco_pim) -* [`cisco_pim_grouplist`](#type-cisco_pim_grouplist) -* [`cisco_pim_rp_address`](#type-cisco_pim_rp_address) -* [`cisco_portchannel_global`](#type-cisco_portchannel_global) -* [`cisco_stp_global`](#type-cisco_stp_global) -* [`cisco_snmp_community`](#type-cisco_snmp_community) -* [`cisco_snmp_group`](#type-cisco_snmp_group) -* [`cisco_snmp_server`](#type-cisco_snmp_server) -* [`cisco_snmp_user`](#type-cisco_snmp_user) -* [`cisco_tacacs_server`](#type-cisco_tacacs_server) -* [`cisco_tacacs_server_host`](#type-cisco_tacacs_server_host) -* [`cisco_vdc`](#type-cisco_vdc) -* [`cisco_vlan`](#type-cisco_vlan) -* [`cisco_vpc_domain`](#type-cisco_vpc_domain) -* [`cisco_vni`](#type-cisco_vni) -* [`cisco_vrf`](#type-cisco_vrf) -* [`cisco_vrf_af`](#type-cisco_vrf_af) -* [`cisco_vtp`](#type-cisco_vtp) -* [`cisco_vxlan_vtep`](#type-cisco_vxlan_vtep) -* [`cisco_vxlan_vtep_vni`](#type-cisco_vxlan_vtep_vni) - -### NetDev StdLib Resource Type Catalog (by Name) - -* [`domain_name`](#type-domain_name) -* [`name_server`](#type-name_server) -* [`network_dns`](#type-network_dns) -* [`network_interface`](#type-network_interface) -* [`network_snmp`](#type-network_snmp) -* [`network_trunk`](#type-network_trunk) -* [`network_vlan`](#type-network_vlan) -* [`ntp_config`](#type-ntp_config) -* [`ntp_server`](#type-ntp_server) -* [`port_channel`](#type-port_channel) -* [`radius`](#type-radius) -* [`radius_global`](#type-radius_global) -* [`radius_server_group`](#type-radius_server_group) -* [`radius_server`](#type-radius_server) -* [`search_domain`](#type-search_domain) -* [`snmp_community`](#type-snmp_community) -* [`snmp_notification`](#type-snmp_notification) -* [`snmp_notification_receiver`](#type-snmp_notification_receiver) -* [`snmp_user`](#type-snmp_user) -* [`syslog_server`](#type-syslog_server) -* [`syslog_setting`](#type-syslog_setting) -* [`tacacs`](#type-tacacs) -* [`tacacs_global`](#type-tacacs_global) -* [`tacacs_server_group`](#type-tacacs_server_group) -* [`tacacs_server`](#type-tacacs_server) - --- -### Cisco Resource Type Details - -The following resources are listed alphabetically. - --- -### Type: cisco_command_config - -Allows execution of configuration commands. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.0.1 | -| N30xx | 7.0(3)I2(1) | 1.0.1 | -| N31xx | 7.0(3)I2(1) | 1.0.1 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Parameters - -##### `command` - -Configuration command(s) to be applied to the network element. Valid values -are string. - -This provider allows raw configurations to be managed by Puppet. It serves as a stopgap until specialized types are created. It has the following limitations: - -* The input message buffer is limited to 500KB. Large configurations are often easier to debug if broken up into multiple smaller resource blocks. -* The cisco_command_config configuration block must use the same syntax as displayed by the `show running-config` command on the switch. In some cases, configuration commands that omit optional keywords when entered may actually appear with a different syntax when displayed by `show running-config`; for example, some access-list entries may be configured without a sequence number but yet an implicit sequence number is created regardless. This then creates an idempotency problem because there is a mismatch between `show running-config` and the manifest. The solution in this case is for the manifest to include explicit sequence numbers for the affected access-list entries. -* Order is important. Some dependent commands may fail if their associated `feature` configuration is not enabled first. Use Puppet's `before`, `after`, or `require` keywords to establish dependencies between blocks. -* Indentation counts! It implies sub-mode configuration. Use the switch's running-config as a guide and do not indent configurations that are not normally indented. Do not use tabs to indent. -* Inline comments must be prefixed by '!' or '#'. -* Negating a submode will also remove configuratons under that submode, without having to specify every submode config statement: `no router ospf RED` removes all configuration under router ospf RED. -* Syntax does not auto-complete: use `Ethernet1/1`, not `Eth1/1`. -* If a CLI command is rejected during configuration, the resource will abort at that point and will not issue any remaining CLI. For this reason, we recommend limiting the scope of each instance of this resource. - --- -### Type: cisco_aaa_authentication_login - -Manages AAA Authentication Login configuration. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `name` -The name of the AAA Authentication Login instance. Must be 'default' - -##### `ascii_authentication` -Enable/disable ascii_authentication for AAA Authentication Login. Valid values are true, false, keyword 'default' - -##### `chap` -Enable/disable chap for AAA Authentication Login. - -##### `error_display` -Enable/disable error_display for AAA Authentication Login. - -##### `mschap` -Enable/disable mschap for AAA Authentication Login. - -##### `mschapv2` -Enable/disable mschapv2 for AAA Authentication Login. - --- -### Type: cisco_aaa_authorization_login_cfg_svc - -Manages configuration for Authorization Login Config Service. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `ensure` -Determines whether the config should be present or not on the device. Valid values are 'present' and 'absent'. - -##### `name` -Name of the config login service. Valid values are 'console' or 'default'. - -##### `groups` -Tacacs+ groups configured for this service. Valid values are an array of strings, keyword 'default'. - -##### `method` -Authentication methods on this device. Valid values are 'local', 'unselected', 'default'. - --- -### Type: cisco_aaa_authorization_login_exec_svc - -Manages configuration for Authorization Login Exec Service. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `ensure` -Determines whether the config should be present or not on the device. Valid values are 'present' and 'absent'. - -##### `name` -Name of the exec login service. Valid values are 'console' or 'default'. - -##### `groups` -Tacacs+ groups configured for this service. Valid values are an array of strings, keyword 'default'. - -##### `method` -Authentication methods on this device. Valid values are 'local', 'unselected', 'default'. - --- -### Type: cisco_aaa_group_tacacs - -Manages configuration for a TACACS+ server group. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `ensure` -Determines whether the config should be present or not on the device. Valid values are 'present' and 'absent'. - -##### `group` -Name of the aaa group TACACS instance. Valid values are string. - -##### `deadtime` -Deadtime interval for this TACACS+ server group. Valid values are integer, in minutes, keyword 'default' - -##### `server_hosts` -An array of TACACS+ server hosts associated with this TACACS+ server group. Valid values are an array, or the keyword 'default'. - -##### `source_interface` -Source interface for TACACS+ servers in this TACACS+ server group Valid values are string, keyword 'default'. - -##### `vrf_name` -Specifies the virtual routing and forwarding instance (VRF) to use to contact this TACACS server group. Valid values are string, the keyword 'default'. - --- -### Type: cisco_acl - -Manages configuration of a Access Control List (ACL) instance. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Caveats - -| Property | Caveat Description | -|:--------|:-------------| -| `fragments` | Not supported on N56xx, N6k | - -#### Parameters - -##### `ensure` -Determines whether the config should be present or not on the device. Valid values are 'present' and 'absent'. - -##### `afi` -Address Family Identifier (AFI). Required. Valid values are ipv4 and ipv6. - -##### `acl_name` -Name of the acl instance. Valid values are string. - -##### `stats_per_entry` -Enable/disable Statistics Per Entry for ACL. Valid values are true, false, keyword 'default'. - -##### `fragments` -Permit or deny Fragments for ACL. Valid values are 'permit-all' and 'deny-all' - --- -### Type: cisco_ace - -Manages configuration of an Access Control List (ACL) Access Control Entry (ACE) instance. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Caveats - -| Property | Caveat Description | -|:--------|:-------------| -| `http_method` | ipv4 only
Not supported on N56xx, N6k, N7k | -| `packet_length` | Not supported on N56xx, N6k | -| `precedence` | ipv4 only | -| `redirect` | ipv4 only
Not supported on N56xx, N6k, N7k | -| `time_range` | Not supported on N56xx, N6k | -| `ttl` | Not supported on N56xx, N6k, N7k | -| `tcp_option_length` | ipv4 only
Not supported on N56xx, N6k, N7k | - -#### Example Usage - -```puppet -cisco_ace { 'ipv4 my_acl 42': - ensure => 'present', - remark => 'East Branch', - action => 'permit', - proto => 'tcp', - src_addr => '10.0.0.0/8', - src_port => 'eq 40', - dst_addr => 'any', - dst_port => 'neq 80', - - dscp => 'af11', - established => 'true', - log => 'true', - packet_length => 'range 512 1024' - precedence => 'flash', - redirect => 'Ethernet1/2,Port-Channel42', - tcp_flags => 'ack psh', - time_range => 'my_time_range', - ttl => '128', -} - -cisco_ace { 'ipv6 my_v6_acl 42': - ensure => 'present', - remark => 'East Branch', - action => 'permit', - proto => 'tcp', - src_addr => '1:1::1/128', - dst_addr => 'any', -} -``` - -#### Parameters - -| Example Parameter Usage -|:-- -| `cisco_ace { ' ':` -| `cisco_ace { 'ipv4 my_acl 42':` - -##### `afi` -Address Family Identifier (AFI). Required. Valid values are ipv4 and ipv6. - -##### `acl_name` -Access Control List (ACL) name. Required. Valid values are type String. - -##### `seqno` -Access Control Entry (ACE) Sequence Number. Required. Valid values are type Integer. - -##### `ensure` -Determines whether the config should be present or not on the device. Valid values are 'present' and 'absent'. - -#### Properties - -##### `action` -The action to perform with this ACE. Valid values are keywords `permit` or `deny`. - -| Example -|:-- -| `action => 'permit'` - -##### `dscp` -Allows matching by Differentiated Services Code Point (DSCP) value. Valid values are type String, which must be one of the following forms: - -* A numeric dscp value -* One of the dscp keyword names - * `af11` `af12` `af13` `af21` `af22` `af23` `af31` `af32` `af33` `af41` `af42` `af43` - * `cs1` `cs2` `cs3` `cs4` `cs5` `cs6` `cs7` - * `ef` - * `default` - -| Example -|:-- -| `dscp => 'af11'` - -##### `dst_addr` -The Destination Address to match against. This property shares the same syntax as `src_addr`. Valid values are type String, which must be one of the following forms: - -* An IPv4/IPv6 address or subnet -* The keyword `host` and a host address -* The keyword `addrgroup` and its object group name -* The keyword `any` - -| Examples -|:-- -| `dst_addr => '10.0.0.0/8'` -| `dst_addr => 'host 10.0.0.1'` -| `dst_addr => '128:1::/64'` -| `dst_addr => 'addrgroup my_addrgroup'` -| `dst_addr => 'any'` - -See [`src_addr`](#src_addr). - -##### `dst_port` -The TCP or UDP Destination Port to match against. This property shares the same syntax as `src_port`. Valid values are type String, which must be one of the following forms: - -* A comparison operator (`eq`, `neq`, `lt`, `gt`) and value -* The keyword `range` and a range value -* The keyword `portgroup` and its object group name - -| Examples -|:-- -| `dst_port => 'neq 40'` -| `dst_port => 'range 68 69'` -| `dst_port => 'portgroup my_portgroup'` - -See [`src_port`](#src_port). - -##### `established` -Allows matching against TCP Established connections. Valid values are true or false. - -| Example -|:-- -| `established => true` - -##### `http_method` -(ipv4 only) Allows matching based on http-method. Valid values are String, which must be one of the following forms: - -* A numeric http-method value -* One of the http-method keyword names - * `connect` `delete` `get` `head` `post` `put` `trace` - -| Examples -|:-- -| `http_method => 'post'` - -##### `log` -Enables logging for the ACE. Valid values are true or false. - -| Examples -|:-- -| `'log' => true` - -##### `packet_length` -Allows matching based on Layer 3 Packet Length. Valid values are type String, which must be one of the following forms: - -* A comparison operator (`eq`, `neq`, `lt`, `gt`) and value -* The keyword `range` and range values - -| Examples -|:-- -| `packet_length => 'gt 512'` -| `packet_length => 'range 512 1024'` - -##### `precedence` -(ipv4 only) Allows matching by precedence value. Valid values are String, which must be one of the following forms: - -* A numeric precedence value -* One of the precedence keyword names - * `critical` `flash` `flash-override` `immediate` `internet` `network` `priority` `routine` - -| Example -|:-- -| `precedence => 'flash'` - -##### `proto` -The protocol to match against. Valid values are String or Integer. Examples are: `tcp`, `udp`, `ip`, `6`. - -| Example -|:-- -| `proto => 'tcp'` - -##### `redirect` -(ipv4 only) Allows for redirecting traffic to one or more interfaces. This property is only useful with VLAN ACL (VACL) applications. Valid values are a String containing a list of interface names. - -| Examples -|:-- -| `redirect => 'Ethernet1/1'` -| `redirect => 'Ethernet1/2,Port-Channel42'` - -##### `remark` -This is a Remark description for the ACL or ACE. Valid values are string. - -| Example -|:-- -| `remark => 'East Branch'` - -##### `src_addr` -The Source Address to match against. Valid values are type String, which must be one of the following forms: - -* An IPv4/IPv6 address or subnet -* The keyword `host` and a host address -* The keyword `addrgroup` and its object group name -* The keyword `any` - -| Examples -|:-- -| `src_addr => '10.0.0.0/8'` -| `src_addr => 'host 10.0.0.1'` -| `src_addr => '128:1::/64'` -| `src_addr => 'addrgroup my_addrgroup'` -| `src_addr => 'any'` - -See [`dst_addr`](#dst_addr). - -##### `src_port` -The TCP or UDP Source Port to match against. Valid values are type String, which must be one of the following forms: - -* A comparison operator (`eq`, `neq`, `lt`, `gt`) and value -* The keyword `range` and range values -* The keyword `portgroup` and its object group name - -| Examples -|:-- -| `src_port => 'neq 40'` -| `src_port => 'range 68 69'` -| `src_port => 'portgroup my_portgroup'` - -See [`dst_port`](#dst_port). - -##### `tcp_flags` -The TCP flags or control bits. Valid values are a String of some or all of flags: `urg`, `ack`, `psh`, `rst`, `syn`, or `fin`. - -| Example -|:-- -| `tcp_flags => 'ack psh'` - -##### `tcp_option_length` -(ipv4 only) Allows matching on TCP options length. Valid values are type Integer or String, which must be a multiple of 4 in the range 0-40. - -| Examples -|:-- -| `tcp_option_length => '0'` -| `tcp_option_length => '36'` - -##### `time_range` -Allows matching by Time Range. Valid values are String, which references a `time-range` name. - -| Example -|:-- -| `time_range => 'my_time_range'` - - -##### `ttl` -Allows matching based on Time-To-Live (TTL) value. Valid values are type Integer or String. - -| Example -|:-- -| `ttl => '128'` - --- -### Type: cisco_bgp - -Manages configuration of a BGP instance. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.1.0 | -| N30xx | 7.0(3)I2(1) | 1.1.0 | -| N31xx | 7.0(3)I2(1) | 1.1.0 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Caveats - -| Property | Caveat Description | -|:--------|:-------------| -| `bestpath_med_confed` | Only supported in global BGP context in IOS XR | -| `bestpath_med_non_deterministic` | Not supported on IOS XR | -| `cluster_id` | Only supported in global BGP context in IOS XR | -| `confederation_id` | Only supported in global BGP context in IOS XR | -| `confederation_peers` | Only supported in global BGP context in IOS XR | -| `disable_policy_batching` | Not supported on IOS XR | -| `disable_policy_batching_ipv4` | Not supported on N56xx, N6k, N7k, IOS XR | -| `disable_policy_batching_ipv6` | Not supported on N56xx, N6k, N7k, IOS XR | -| `enforce_first_as` | Only supported in global BGP context in NX-OS | -| `event_history_cli` | Not supported on IOS XR | -| `event_history_detail` | Not supported on IOS XR | -| `event_history_events` | Not supported on IOS XR | -| `event_history_periodic` | Not supported on IOS XR | -| `fast_external_fallover` | Only supported in global BGP context in NX-OS | -| `flush_routes` | Only supported in global BGP context in NX-OS. Not supported on IOS XR | -| `graceful_restart` | Only supported in global BGP context in IOS XR | -| `graceful_restart_helper` | Not supported on IOS XR | -| `graceful_restart_timers_restart` | Only supported in global BGP context in IOS XR | -| `graceful_restart_timers_stalepath_time` | Only supported in global BGP context in IOS XR | -| `isolate` | Not supported on IOS XR | -| `maxas_limit` | Not supported on IOS XR | -| `neighbor_down_fib_accelerate` | Not supported on N56xx, N6k, N7k, IOS XR | -| `nsr` | Only supported on IOS XR. Not supported on NX-OS | -| `reconnect_interval` | Not supported on N56xx, N6k, N7k, IOS XR | -| `shutdown` | Not supported on IOS XR | -| `suppress_fib_pending` | Not supported on IOS XR | -| `timer_bestpath_limit` | Not supported on IOS XR | -| `timer_bestpath_limit_always` | Not supported on IOS XR | - -#### Parameters - -##### `ensure` -Determines whether the config should be present or not on the device. Valid values are 'present' and 'absent'. - -##### `asn` -BGP autonomous system number. Valid values are String, Integer in ASPLAIN or ASDOT notation. - -##### `vrf` -Name of the resource instance. Valid values are string. The name 'default' is a valid VRF representing the global bgp. - -#### Properties - -##### `bestpath_always_compare_med` -Enable/Disable MED comparison on paths from different autonomous systems. Valid values are 'true', 'false', and 'default'. - -##### `bestpath_aspath_multipath_relax` -Enable/Disable load sharing across the providers with different (but equal-length) AS paths. Valid values are 'true', 'false', and 'default' - -##### `bestpath_compare_routerid` -Enable/Disable comparison of router IDs for identical eBGP paths. Valid values are 'true', 'false', and 'default' - -##### `bestpath_cost_community_ignore` -Enable/Disable Ignores the cost community for BGP best-path calculations. Valid values are 'true', 'false', and 'default' - -##### `bestpath_med_confed` -Enable/Disable enforcement of bestpath to do a MED comparison only between paths originated within a confederation. Valid values are 'true', 'false', and 'default'. On IOS XR, this property is only supported in the global BGP context. - -##### `bestpath_med_missing_as_worst` -Enable/Disable assigns the value of infinity to received routes that do not carry the MED attribute, making these routes the least desirable. Valid values are 'true', 'false', and 'default'. - -##### `bestpath_med_non_deterministic` -Enable/Disable deterministic selection of the best MED path from among the paths from the same autonomous system. Valid values are 'true', 'false', and 'default'. This property is not supported on IOS XR. - -##### `cluster_id` -Route Reflector Cluster-ID. Valid values are String, keyword 'default'. On IOS XR, this property is only supported in the global BGP context. - -##### `confederation_id` -Routing domain confederation AS. Valid values are String, keyword 'default'. On IOS XR, this property is only supported in the global BGP context. - -##### `confederation_peers` -AS confederation parameters. Valid values are String, keyword 'default'. On IOS XR, this property is only supported in the global BGP context. - -##### `disable_policy_batching` -Enable/Disable the batching evaluation of prefix advertisements to all peers. Valid values are 'true', 'false', and 'default'. This property is not supported on IOS XR. - -##### `disable_policy_batching_ipv4` -Enable/Disable the batching evaluation of prefix advertisements to all peers with prefix list. Valid values are String, keyword 'default'. This property is not supported on IOS XR. - -##### `disable_policy_batching_ipv6` -Enable/Disable the batching evaluation of prefix advertisements to all peers with prefix list. Valid values are String, keyword 'default'. This property is not supported on IOS XR. - -##### `enforce_first_as` -Enable/Disable enforces the neighbor autonomous system to be the first AS number listed in the AS path attribute for eBGP. Valid values are 'true', 'false', and 'default'. On NX-OS, this property is only supported in the global BGP context. - -##### `event_history_cli` -Enable/Disable cli event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. This property is not supported on IOS XR. - -##### `event_history_detail` -Enable/Disable detail event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. This property is not supported on IOS XR. - -##### `event_history_events` -Enable/Disable event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. This property is not supported on IOS XR. - -##### `event_history_periodic` -Enable/Disable periodic event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. This property is not supported on IOS XR. - -##### `fast_external_fallover` -Enable/Disable immediately reset the session if the link to a directly connected BGP peer goes down. Valid values are 'true', 'false', and 'default'. On NX-OS, this property is only supported in the global BGP context. - -##### `flush_routes` -Enable/Disable flush routes in RIB upon controlled restart. Valid values are 'true', 'false', and 'default'. On NX-OS, this property is only supported in the global BGP context. This property is not supported on IOS XR. - -##### `graceful_restart` -Enable/Disable graceful restart. Valid values are 'true', 'false', and 'default'. On IOS XR, this property is only supported in the global BGP context. - -##### `graceful_restart_helper` -Enable/Disable graceful restart helper mode. Valid values are 'true', 'false', and 'default'. This property is not supported on IOS XR. - -##### `graceful_restart_timers_restart` -Set maximum time for a restart sent to the BGP peer. Valid values are Integer, keyword 'default'. On IOS XR, this property is only supported in the global BGP context. - -##### `graceful_restart_timers_stalepath_time` -Set maximum time that BGP keeps the stale routes from the restarting BGP peer. Valid values are Integer, keyword 'default'. On IOS XR, this property is only supported in the global BGP context. - -##### `isolate` -Enable/Disable isolate this router from BGP perspective. Valid values are 'true', 'false', and 'default'. This property is not supported on IOS XR. - -##### `log_neighbor_changes` -Enable/Disable message logging for neighbor up/down event. Valid values are 'true', 'false', and 'default' - -##### `maxas_limit` -Specify Maximum number of AS numbers allowed in the AS-path attribute. Valid values are integers between 1 and 512, or keyword 'default' to disable this property. This property is not supported on IOS XR. - -##### `neighbor_down_fib_accelerate` -Enable/Disable handle BGP neighbor down event, due to various reasons. Valid values are 'true', 'false', and 'default'. This property is not supported on IOS XR. - -##### `nsr` -Enable/Disable Non-Stop Routing (NSR). Valid values are 'true', 'false', and 'default'. This property is not supported on Nexus. - -##### `reconnect_interval` -The BGP reconnection interval for dropped sessions. Valid values are Integer or keyword 'default'. - - -##### `route_distinguisher` -VPN Route Distinguisher (RD). The RD is combined with the IPv4 or IPv6 prefix learned by the PE router to create a globally unique address. Valid values are a String in one of the route-distinguisher formats (ASN2:NN, ASN4:NN, or IPV4:NN); the keyword 'auto', or the keyword 'default'. - -*Please note:* The `route_distinguisher` property is typically configured within the VRF context configuration on most platforms (including NXOS) but it is tightly coupled to bgp and therefore configured within the BGP configuration on some platforms (XR for example). For this reason the `route_distinguisher` property has support (with limitations) in both `cisco_vrf` and `cisco_bgp` providers: - -* `cisco_bgp`: The property is fully supported on both NXOS and XR. -* `cisco_vrf`: The property is only supported on NXOS. See: [cisco_vrf: route_distinguisher](#vrf_rd) - -*IMPORTANT: Choose only one provider to configure the `route_distinguisher` property on a given device. Using both providers simultaneously on the same device may have unpredictable results.* - -##### `router_id` -Router Identifier (ID) of the BGP router VRF instance. Valid values are string, and keyword 'default'. - -##### `shutdown` -Administratively shutdown the BGP protocol. Valid values are 'true', 'false', and 'default'. This property is not supported on IOS XR. - -##### `suppress_fib_pending` -Enable/Disable advertise only routes programmed in hardware to peers. Valid values are 'true', 'false', and 'default'. This property is not supported on IOS XR. - -##### `timer_bestpath_limit` -Specify timeout for the first best path after a restart, in seconds. Valid values are Integer, keyword 'default'. This property is not supported on IOS XR. - -##### `timer_bestpath_limit_always` -Enable/Disable update-delay-always option. Valid values are 'true', 'false', and 'default'. This property is not supported on IOS XR. - -##### `timer_bgp_hold` -Set bgp hold timer. Valid values are Integer, keyword 'default'. - -##### `timer_bgp_keepalive` -Set bgp keepalive timer. Valid values are Integer, keyword 'default'. - --- -### Type: cisco_bgp_af - -Manages configuration of a BGP Address-family instance. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.1.0 | -| N30xx | 7.0(3)I2(1) | 1.1.0 | -| N31xx | 7.0(3)I2(1) | 1.1.0 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Caveats - -| Property | Caveat Description | -|:--------|:-------------| -| `additional_paths_install` | Not supported on N9k, N30xx, N31xx, N8k, IOS XR | -| `advertise_l2vpn_evpn` | Not supported on N30xx, N31xx, N6k, IOS XR | -| `client_to_client` | Only supported in global BGP context in IOS XR | -| `dampen_igp_metric` | Not supported on IOS XR | -| `dampening_state` (and dependent properties `dampening_half_time`, `dampening_max_suppress_time`, `dampening_reuse_time`, `dampening_routemap`, `dampening_suppress_time`) | Only supported in global BGP context in IOS XR | -| `default_information_originate` | Not supported on IOS XR | -| `default_metric` | Not supported on IOS XR | -| `inject_map` | Not supported on IOS XR | -| `next_hop_route_map` | Only supported in global BGP context in IOS XR | -| `suppress_inactive` | Not supported on IOS XR | -| `table_map_filter` | Not supported on IOS XR | - -#### Parameters - -###### `ensure` -Determine whether the interface config should be present or not. Valid values - are 'present' and 'absent'. - -##### `asn` -BGP autonomous system number. Required. Valid values are String, Integer in ASPLAIN or ASDOT notation. - -##### `vrf` -VRF name. Required. Valid values are string. The name 'default' is a valid VRF representing the global bgp. - -##### `afi` -Address Family Identifier (AFI). Required. Valid values for Nexus and IOS XR are `ipv4`, `ipv6`, `vpnv4`, `vpnv6` and `l2vpn`. - -##### `safi` -Sub Address Family Identifier (SAFI). Required. Valid values are `unicast`, `multicast` and `evpn`. - -#### Properties - -##### `additional_paths_install` -Install a backup path into the forwarding table and provide prefix 'independent convergence (PIC) in case of a PE-CE link failure. Valid values are true, false, or 'default'. This property is not supported on IOS XR. - -##### `additional_paths_receive` -Enables the receive capability of additional paths for all of the neighbors under this address family for which the capability has not been disabled. Valid values are true, false, or 'default' - -##### `additional_paths_selection` -Configures the capability of selecting additional paths for a prefix. Valid values are a string defining the name of the [route-map](#cisco-os-differences). - -##### `additional_paths_send` -Enables the send capability of additional paths for all of the neighbors under this address family for which the capability has not been disabled. Valid values are true, false, or 'default' - -##### `advertise_l2vpn_evpn` -Advertise evpn routes. Valid values are true and false. This property is not supported on IOS XR. - -##### `client_to_client` -Configure client-to-client route reflection. Valid values are true and false. On IOS XR, this property is only supported in the global BGP context. - -##### `dampen_igp_metric` -Specify dampen value for IGP metric-related changes, in seconds. Valid values are Integer, keyword 'default'. This property is not supported on IOS XR. - -##### `dampening_state` -Enable/disable route-flap dampening. Valid values are true, false or 'default'. On IOS XR, this property is only supported in the global BGP context. - -##### `dampening_half_time` -Specify decay half-life in minutes for route-flap dampening. Valid values are Integer, keyword 'default'. On IOS XR, this property is only supported in the global BGP context. - -##### `dampening_max_suppress_time` -Specify max suppress time for route-flap dampening stable route. Valid values are Integer, keyword 'default'. On IOS XR, this property is only supported in the global BGP context. - -##### `dampening_reuse_time` -Specify route reuse time for route-flap dampening. Valid values are Integer, keyword 'default'. On IOS XR, this property is only supported in the global BGP context. - -##### `dampening_routemap` -Specify [route-map](#cisco-os-differences) for route-flap dampening. Valid values are a string defining the name of the route-map. On IOS XR, this property is only supported in the global BGP context. - -##### `dampening_suppress_time` -Specify route suppress time for route-flap dampening. Valid values are Integer, keyword 'default'. On IOS XR, this property is only supported in the global BGP context. - -##### Dampening Properties -Note: dampening_routemap is mutually exclusive with dampening_half_time, reuse_time, suppress_time and max_suppress_time. - -##### `default_information_originate` -`default-information originate`. Valid values are true and false. This property is not supported on IOS XR. - -##### `default_metric` -Sets default metrics for routes redistributed into BGP. Valid values are Integer or keyword 'default'. This property is not supported on IOS XR. - -##### `distance_ebgp` -Sets the administrative distance for eBGP routes. Valid values are Integer or keyword 'default'. - -##### `distance_ibgp` -Sets the administrative distance for iBGP routes. Valid values are Integer or keyword 'default'. - -##### `distance_local` -Sets the administrative distance for local BGP routes. Valid values are Integer or keyword 'default'. - -##### `inject_map` -An array of route-map names which will specify prefixes to inject. Each array entry must first specify the inject-map name, secondly an exist-map name, and optionally the `copy-attributes` keyword which indicates that attributes should be copied from the aggregate. This property is not supported on IOS XR. - -For example, the following array will create three separate inject-maps for `lax_inject_map`, `nyc_inject_map` (with copy-attributes), and `fsd_exist_map`: - -```ruby -[ - ['lax_inject_map', 'lax_exist_map'], - ['nyc_inject_map', 'nyc_exist_map', 'copy-attributes'], - ['fsd_inject_map', 'fsd_exist_map'] -] -``` - -##### `maximum_paths` -Configures the maximum number of equal-cost paths for load sharing. Valid value is an integer in the range 1-64. Default value is 1. - -##### `maximum_paths_ibgp` -Configures the maximum number of ibgp equal-cost paths for load sharing. Valid value is an integer in the range 1-64. Default value is 1. - -##### `networks` -Networks to configure. Valid value is a list of network prefixes to advertise. The list must be in the form of an array. Each entry in the array must include a prefix address and an optional [route-map](#cisco-os-differences). - -Example: IPv4 Networks Array - -```ruby -[ - ['10.0.0.0/16', 'routemap_LA'], - ['192.168.1.1', 'Chicago'], - ['192.168.2.0/24], - ['192.168.3.0/24', 'routemap_NYC'] -] -``` - -Example: IPv6 Networks Array - -```ruby -[ - ['10::0/64', 'routemap_LA'], - ['192:168::1', 'Chicago'], - ['192:168::/32] -] -``` - -##### `next_hop_route_map` -Configure a [route-map](#cisco-os-differences) for valid nexthops. Valid values are a string defining the name of the route-map. On IOS XR, this property is only supported in the global BGP context. - -##### `redistribute` -A list of redistribute directives. Multiple redistribute entries are allowed. The list must be in the form of a nested array: the first entry of each array defines the source-protocol to redistribute from; the second entry defines a [route-map](#cisco-os-differences) name. A route-map is highly advised but may be optional on some platforms, in which case it may be omitted from the array list. - -Example: Platform requiring route-maps - -```ruby -redistribute => [['direct', 'rm_direct'], - ['lisp', 'rm_lisp'], - ['static', 'rm_static'], - ['eigrp 1', 'rm_eigrp'], - ['isis 2', 'rm_isis'], - ['ospf 3', 'rm_ospf'], - ['rip 4', 'rm_rip']] -``` -Example: Platform with optional route-maps - -```ruby -redistribute => [['direct'], - ['lisp', 'rm_lisp'], - ['static'], - ['eigrp 1', 'rm_eigrp'], - ['isis 2', 'rm_isis'], - ['ospf 3', 'rm_ospf'], - ['rip 4']] -``` - -##### `suppress_inactive` -Advertises only active routes to peers. Valid values are true, false, or 'default'. This property is not supported on IOS XR. - -##### `table_map` -Apply table-map to filter routes downloaded into URIB. Valid values are a string. - -##### `table_map_filter` -Filters routes rejected by the route-map and does not download them to the RIB. Valid values are true, false, or 'default'. This property is not supported on IOS XR. - --- -### Type: cisco_bgp_neighbor - -Manages configuration of a BGP Neighbor. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.1.0 | -| N30xx | 7.0(3)I2(1) | 1.1.0 | -| N31xx | 7.0(3)I2(1) | 1.1.0 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Caveats - -| Property | Caveat Description | -|:--------|:-------------| -| `capability_negotiation` | Not supported on IOS XR | -| `dynamic_capability` | Not supported on IOS XR | -| `log_neighbor_changes` | Not supported on N56xx, N6k, N7k, IOS XR | -| `low_memory_exempt` | Not supported on IOS XR | -| `maximum_peers` | Not supported on IOS XR | -| `neighbor` | ip/prefix format is not supported on IOS XR | -| `password_type` | Set of valid values differs between NX-OS and IOS XR | -| `remove_private_as` | Not supported on IOS XR | - -#### Parameters - -###### `ensure` -Determine whether the neighbor config should be present or not. Valid values are 'present' and 'absent'. - -##### `asn` -BGP autonomous system number. Required. Valid values are String, Integer in ASPLAIN or ASDOT notation. - -##### `vrf` -VRF name. Required. Valid values are string. The name 'default' is a valid VRF representing the global bgp. - -##### `neighbor` -Neighbor Identifier. Required. Valid values are string. Neighbors may use IPv4 or IPv6 notation, with or without prefix length. Specifying ip/prefix format is not supported on IOS XR. - -#### Properties - -##### `description` -Description of the neighbor. Valid value is string. - -##### `connected_check` -Configure whether or not to check for directly connected peer. Valid values are true and false. - -##### `capability_negotiation` -Configure whether or not to negotiate capability with this neighbor. Valid values are true and false. This property is not supported on IOS XR. - -##### `dynamic_capability` -Configure whether or not to enable dynamic capability. Valid values are true and false. This property is not supported on IOS XR. - -##### `ebgp_multihop` -Specify multihop TTL for a remote peer. Valid values are integers between 2 and 255, or keyword 'default' to disable this property. - -##### `local_as` -Specify the local-as number for the eBGP neighbor. Valid values are String or Integer in ASPLAIN or ASDOT notation, or 'default', which means not to configure it. - -##### `log_neighbor_changes` -Specify whether or not to enable log messages for neighbor up/down event. Valid values are 'enable', to enable it, 'disable' to disable it, or 'inherit' to use the configuration in the cisco_bgp type. This property is not supported on IOS XR. - -##### `low_memory_exempt` -Specify whether or not to shut down this neighbor under memory pressure. Valid values are 'true' to exempt the neighbor from being shutdown, 'false' to shut it down, or 'default' to perform the default shutdown behavior. This property is not supported on IOS XR. - -##### `maximum_peers` -Specify Maximum number of peers for this neighbor prefix. Valid values are between 1 and 1000, or 'default', which does not impose the limit. This attribute can only be configured if neighbor is in 'ip/prefix' format, and is therefore not supported on IOS XR. - -##### `password` -Specify the password for neighbor. Valid value is string. - -##### `password_type` -Specify the encryption type the password will use. Valid values for Nexus are 'cleartext', '3des' or 'cisco_type_7' encryption, and 'default', which defaults to 'cleartext'. Valid values for IOS XR are 'cleartext', 'md5', and 'default', which also defaults to 'cleartext'. - -##### `remote_as` -Specify Autonomous System Number of the neighbor. Valid values are String or Integer in ASPLAIN or ASDOT notation, or 'default', which means not to configure it. This property is required on IOS XR. - -##### `remove_private_as` -Specify the config to remove private AS number from outbound updates. Valid values are 'enable' to enable this config, 'disable' to disable this config, 'all' to remove all private AS number, or 'replace-as', to replace the private AS number. This property is not supported on IOS XR. - -##### `shutdown` -Configure to administratively shutdown this neighbor. Valid values are true and false. - -##### `suppress_4_byte_as` -Configure to suppress 4-byte AS Capability. Valid values are 'true', 'false', and 'default', which sets to the default 'false' value. - -##### `timers_keepalive` -Specify keepalive timer value. Valid values are integers between 0 and 3600 in terms of seconds, or 'default', which is 60. - -##### `timers_holdtime` -Specify holdtime timer value. Valid values are integers between 0 and 3600 in terms of seconds, or 'default', which is 180. - -##### `transport_passive_mode` -Specify whether BGP sessions can be established from incoming or outgoing TCP connection requests (or both). Valid values for IOS XR are 'active_only' (allow outgoing only), 'passive_only' (allow incoming only), 'both', 'clear' (clears this property) and 'default', which defaults to 'clear'. Valid values for Nexus are 'passive_only', 'both', 'clear' and 'default', which defaults to 'clear'. This property can only be configured when the neighbor is in 'ip' address format without prefix length. This property and the transport_passive_only property are mutually exclusive. - -##### `transport_passive_only` -Specify whether or not to only allow passive connection setup. Valid values are 'true', 'false', and 'default', which defaults to 'false'. This property can only be configured when the neighbor is in 'ip' address format without prefix length. This property and the transport_passive_mode property are mutually exclusive. - -##### `update_source` -Specify source interface of BGP session and updates. Valid value is a string of the interface name. - --- -### Type: cisco_bgp_neighbor_af - -Manages configuration of a BGP Neighbor Address-family instance. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.1.0 | -| N30xx | 7.0(3)I2(1) | 1.1.0 | -| N31xx | 7.0(3)I2(1) | 1.1.0 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Caveats - -| Property | Caveat Description | -|:--------|:-------------| -| `additional_paths_receive` | Not supported on IOS XR | -| `additional_paths_send` | Not supported on IOS XR | -| `advertise_map_exist` | Not supported on IOS XR | -| `advertise_map_non_exist` | Not supported on IOS XR | -| `default_originate_route_map` | Not supported on IOS XR | -| `disable_peer_as_check` | Not supported on IOS XR | -| `filter_list_in` | Not supported on IOS XR | -| `filter_list_out` | Not supported on IOS XR | -| `next_hop_third_party` | Not supported on IOS XR | -| `prefix_list_in` | Not supported on IOS XR | -| `prefix_list_out` | Not supported on IOS XR | -| `soo` | Not supported on IOS XR | -| `suppress_inactive` | Not supported on IOS XR | -| `unsuppress_map` | Not supported on IOS XR | - -#### Parameters - -###### `ensure` -Determine whether the neighbor address family config should be present or not. -Valid values are 'present' and 'absent'. - -##### `asn` -BGP autonomous system number. Required. Valid values are String, Integer in ASPLAIN or -ASDOT notation. - -##### `vrf` -VRF name. Required. Valid values are string. The name 'default' is a valid VRF representing the global bgp. - -##### `neighbor` -Neighbor Identifier. Required. Valid values are string. Neighbors may use IPv4 or IPv6 notation, with or without a subnet mask. - -##### `afi` -Neighbor Address Family Identifier (AFI). Required. Valid values are string. Valid neighbor AFIs are `ipv4`, `ipv6`, `vpnv4`, `vpnv6` and `l2vpn`. Note that some AFI/SAFI address-families may not be supported with some neighbors; e.g. an ipv6 neighbor may not support an ipv4 multicast address-family. - -##### `safi` -Neighbor Sub Address Family Identifier (SAFI). Required. Valid values are string. Valid neighbor SAFIs are `unicast`, `multicast` and `evpn`. Note that some AFI/SAFI address-families may not be supported with some neighbors; e.g. an ipv6 neighbor may not support an ipv4 multicast address-family. - -#### Properties - -##### `additional_paths_receive` -`capability additional-paths receive`. Valid values are `enable` for basic command enablement; `disable` for disabling the command at the neighbor_af level (it adds the `disable` keyword to the basic command); and `inherit` to remove the command at this level (the command value is inherited from a higher BGP layer). This property is not supported on IOS XR. - -##### `additional_paths_send` -`capability additional-paths send`. Valid values are `enable` for basic command enablement; `disable` for disabling the command at the neighbor_af level (it adds the `disable` keyword to the basic command); and `inherit` to remove the command at this level (the command value is inherited from a higher BGP layer). This property is not supported on IOS XR. - -##### `advertise_map_exist` -Conditional route advertisement. This property requires two route maps: an advertise-map and an exist-map. Valid values are an array specifying both the advertise-map name and the exist-map name, or simply 'default'; e.g. `['my_advertise_map', 'my_exist_map']`. This command is mutually exclusive with the advertise_map_non_exist property. This property is not supported on IOS XR. - -##### `advertise_map_non_exist` -Conditional route advertisement. This property requires two route maps: an advertise-map and a non-exist-map. Valid values are an array specifying both the advertise-map name and the non-exist-map name, or simply 'default'; e.g. `['my_advertise_map', 'my_non_exist_map']`. This command is mutually exclusive with the advertise_map_exist property. This property is not supported on IOS XR. - -##### `allowas_in` -`allowas-in`. Valid values are true, false, or an integer value, which enables the command with a specific max-occurrences value. Related: `allowas_in_max`. - -##### `allowas_in_max` -Optional max-occurrences value for `allowas_in`. Valid values are an integer value or 'default'. Can be used independently or in conjunction with `allowas_in`. - -##### `as_override` -`as-override`. Valid values are true, false, or 'default'. - -##### `default_originate` -`default-originate`. Valid values are True, False, or 'default'. Related: `default_originate_route_map`. - -##### `default_originate_route_map` -Optional [route-map](#cisco-os-differences) for the `default_originate` property. Can be used independently or in conjunction with `default_originate`. Valid values are a string defining a route-map name, or 'default'. - -##### `filter_list_in` -Valid values are a string defining a filter-list name, or 'default'. This property is not supported on IOS XR. - -##### `filter_list_out` -Valid values are a string defining a filter-list name, or 'default'. This property is not supported on IOS XR. - -##### `max_prefix_limit` -`maximum-prefix` limit value. Valid values are an integer value or 'default'. Related: `max_prefix_threshold`, `max_prefix_interval`, and `max_prefix_warning`. - -##### `max_prefix_interval` -Optional restart interval. Valid values are an integer value or 'default'. Requires `max_prefix_limit`. - -##### `max_prefix_threshold` -Optional threshold percentage at which to generate a warning. Valid values are an integer value or 'default'. Requires `max_prefix_limit`. - -##### `max_prefix_warning` -Optional warning-only keyword. Valid values are True, False, or 'default'. Requires `max_prefix_limit`. - -##### `next_hop_self` -`next-hop-self`. Valid values are True, False, or 'default'. - -##### `next_hop_third_party` -`next-hop-third-party`. Valid values are True, False, or 'default'. This property is not supported on IOS XR. - -##### `prefix_list_in` -Valid values are a string defining a prefix-list name, or 'default'. This property is not supported on IOS XR. - -##### `prefix_list_out` -Valid values are a string defining a prefix-list name, or 'default'. This property is not supported on IOS XR. - -##### `route_map_in` -Valid values are a string defining a [route-map](#cisco-os-differences) name, or 'default'. - -##### `route_map_out` -Valid values are a string defining a [route-map](#cisco-os-differences) name, or 'default'. - -##### `route_reflector_client` -`route-reflector-client`. Valid values are True, False, or 'default'. - -##### `send_community` -`send-community` attribute. Valid values are 'none', 'both', 'extended', 'standard', or 'default'. - -##### `soft_reconfiguration_in` -`soft-reconfiguration inbound`. Valid values are `enable` for basic command enablement; `always` to add the `always` keyword to the basic command; and `inherit` to remove the command at this level (the command value is inherited from a higher BGP layer). - -##### `soo` -Site-of-origin. Valid values are a string defining a VPN extcommunity or 'default'. This property is not supported on IOS XR. - -##### `suppress_inactive` -`suppress-inactive` Valid values are True, False, or 'default'. This property is not supported on IOS XR. - -##### `unsuppress_map` -`unsuppress-map`. Valid values are a string defining a route-map name or 'default'. This property is not supported on IOS XR. - -##### `weight` -`weight` value. Valid values are an integer value or 'default'. - --- -### Type: cisco_bridge_domain -Manages a cisco Bridge-Domain - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | not applicable | not applicable | -| N30xx | not applicable | not applicable | -| N31xx | not applicable | not applicable | -| N56xx | not applicable | not applicable | -| N6k | not applicable | not applicable | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | not applicable | not applicable | -| IOS XR | not applicable | not applicable | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. - -##### `bd` -ID of the Bridge Domain. Valid values are integer. - -##### `bd_name` -The bridge-domain name. Valid values are String or keyword 'default'. - -##### `shutdown` -Specifies the shutdown state of the bridge-domain. Valid values are true, false, 'default'. - -##### `fabric_control` -Specifies this bridge-domain as the fabric control bridge-domain. Only one bridge-domain or VLAN can be configured as fabric-control. Valid values are true, false. - --- -### Type: cisco_bridge_domain_vni -Creates a Virtual Network Identifier member (VNI) mapping for cisco Bridge-Domain. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | not applicable | not applicable | -| N30xx | not applicable | not applicable | -| N31xx | not applicable | not applicable | -| N56xx | not applicable | not applicable | -| N6k | not applicable | not applicable | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | not applicable | not applicable | -| IOS XR | not applicable | not applicable | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. - -##### `bd` -The bridge-domain ID. Valid values are one or range of integers. - -##### `member_vni` -The Virtual Network Identifier (VNI) id that is mapped to the VLAN. Valid values are one or range of integers - --- -### Type: cisco_encapsulation -Manages a Global VNI Encapsulation profile - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | not applicable | not applicable | -| N30xx | not applicable | not applicable | -| N31xx | not applicable | not applicable | -| N56xx | not applicable | not applicable | -| N6k | not applicable | not applicable | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | not applicable | not applicable | -| IOS XR | not applicable | not applicable | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. - -##### `encap` -Profile name of the Encapsulation. Valid values are String only. - -#### Properties - -##### `dot1q_map` -The encapsulation profile dot1q vlan-to-vni mapping. Valid values are an array of [vlans, vnis] pairs. - --- -### Type: cisco_evpn_vni - -Manages Cisco Ethernet Virtual Private Network (EVPN) VXLAN Network Identifier (VNI) configurations of a Cisco device. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I3(1) | 1.3.0 | -| N30xx | not applicable | not applicable | -| N31xx | not applicable | not applicable | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | not applicable | not applicable | - -#### Caveats - -| Property | Caveat Description | -|:---------|:-------------| -| `route_target_both` | Supported on most Nexus platforms but usage is *discouraged*. See `route_target_both` below. | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. Default value is 'present'. - -##### `vni` -The EVPN VXLAN Network Identifier. Valid values are Integer. - -#### Properties - -##### `route_distinguisher` - -The VPN Route Distinguisher (RD). The RD is combined with the IPv4 or IPv6 prefix learned by the PE router to create a globally unique address. Valid values are a String in one of the route-distinguisher formats (ASN2:NN, ASN4:NN, or IPV4:NN); the keyword 'auto', or the keyword 'default'. - -##### `route_target_both` - -Enables/Disables route-target settings for both import and export target communities using a single property. Valid values are an Array or space-separated String of extended communities, or the keywords 'auto' or 'default'." - -*Caveat*: The `route_target_both` property is discouraged due to the inconsistent behavior of the property across Nexus platforms and image versions. The 'both' keyword has a transformative behavior on some platforms/versions in which it creates two cli configurations: one for import targets, a second for export targets, while the 'both' command itself may not appear at all. When the 'both' keyword does not appear in the configuration it causes an idempotency problem for puppet. For this reason it is recommended to use explicit 'route_target_export' and 'route_target_import' properties instead of `route_target_both`. - -##### `route_target_import` - -Sets the route-target 'import' extended communities. Valid values are an Array or space-separated String of extended communities, or the keywords 'auto' or 'default'. - -route_target Examples: - -route_target_import => ['1.2.3.4:5', '33:55'] -route_target_export => '4:4 66:66' - -##### `route_target_export` - -Sets the route-target 'export' extended communities. Valid values are an Array or space-separated String of extended communities, or the keywords 'auto' or 'default'. - --- -### Type: cisco_fabricpath_global -Manages Cisco fabricpath global parameters. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | not applicable | not applicable | -| N30xx | not applicable | not applicable | -| N31xx | not applicable | not applicable | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | not applicable | not applicable | -| IOS XR | not applicable | not applicable | - -#### Caveats - -| Property | Caveat Description | -|:---------|:-------------| -| `loadbalance_multicast_has_vlan` | Supported only on N7k | -| `loadbalance_multicast_rotate` | Supported only on N7k | -| `ttl_multicast` | Supported only on N7k | -| `ttl_unicast` | Supported only on N7k | - -#### Parameters - -##### `name` -ID of the fabricpath global config. The only valid value is keyword 'default'. - -##### `aggregate_multicast_routes` -Aggregate Multicast Routes on same tree in the topology. Valid values are true/false and keyword 'default'. Default value: false. - -##### `allocate_delay` -Fabricpath Timers Allocate Delay in seconds. Valid values are integers from 1..1200 and keyword 'default'. Default value: 10. - -##### `graceful_merge` -Graceful merge for conflicting switch-id or FTAG allocation. Valid values are enable/disable and keyword 'default'. Default value: true. - -##### `linkup_delay` -Fabricpath Timers Link-up Delay in seconds. Valid values are integers from 1..1200 and keyword 'default'. Default value: 10. - -##### `loadbalance_algorithm` -Fabricpath ECMP loadbalancing alogorithm. Valid values are 'destination', 'source', 'source-destination', 'symmetric' and the keyword 'default'. Default is symmetric for Nexus 7000 series and source-destination for others. - -##### `loadbalance_multicast_has_vlan` -Multicast Loadbalance flow parameters - include vlan or not. Valid values are true or false and keyword 'default'. Default value: true. - -##### `loadbalance_multicast_rotate` -Multicast Loadbalance flow parameters - rotate amount in bytes. Valid values are integer in range 0..15 and keyword 'default'. Default value: 1. - -##### `loadbalance_unicast_has_vlan` -Unicast Loadbalance flow parameters - include vlan or not. Valid values are true/false and keyword 'default'. Default value: 1. - -##### `loadbalance_unicast_layer` -Unicast Loadbalance flow parameters - layer. Valid values are : layer2, layer3, -layer4, mixed, and keyword 'default'. Default value: mixed. - -##### `loadbalance_unicast_rotate` -Unicast Loadbalance flow parameters - rotate amount in bytes. Valid values are Integers in range 0..15 and keyword 'default'. Default value: 1. - -##### `linkup_delay_always` -Fabricpath Timers Link-up delay always. This configuration introduces a linkup delay always whether the link is administratively brought up or whether it is restored after events such as a module reload. Valid values are true/false. Default: true. - -##### `linkup_delay_enable` -Fabricpath Timers Link-up delay enable. Valid values are true/false and keyword 'default'. Default value: true. - -##### `mode` -Mode of operation of this switch w.r.t to segmentation. Valid values are normal/transit and keyword 'default'. Default: normal. - -##### `switch_id` -The fabricpath switch_id. This parameter can be used to over-ride the automatically assigned switch-id for this switch. Valid values are integers from 1..4094. - -##### `transition_delay` -Fabricpath Timers Transition Delay in seconds. Valid values are integers from 1..1200 and keyword 'default'. Default value: 10. - -##### `ttl_multicast` -Fabricpath Multicast TTL value. Valid values are integers from 1..64 and keyword 'default'. Default value: 32. - -##### `ttl_unicast` -Fabricpath Unicast TTL value. Valid values are integers from 1..64 and keyword 'default'. Default value: 32. - --- -### Type: cisco_fabricpath_topology -Manages a Cisco fabricpath Topology - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | not applicable | not applicable | -| N30xx | not applicable | not applicable | -| N31xx | not applicable | not applicable | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | not applicable | not applicable | -| IOS XR | not applicable | not applicable | - -#### Parameters - -##### `topo_id` -ID of the fabricpath topology. Valid values are integers in the range 1-63. -Value of 0 is reserved for default topology. - -##### `member_vlans` -ID of the VLAN(s) tha are members of this topology. Valid values are integer/integer ranges. - -##### `topo_name` -Descriptive name of the topology. Valid values are string - --- -### Type: cisco_interface - -Manages a Cisco Network Interface. Any resource dependency should be run before the interface resource. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.0.1 | -| N30xx | 7.0(3)I2(1) | 1.0.1 | -| N31xx | 7.0(3)I2(1) | 1.0.1 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Caveats - -| Property | Caveat Description | -|:---------|:-------------| -| `access_vlan` | Not supported on IOS XR | -| `duplex` | Not supported on IOS XR | -| `fabric_forwarding_anycast_gateway` | Not supported on IOS XR | -| `ipv4_arp_timeout` | Not supported on IOS XR | -| `ipv4_forwarding` | Not supported on IOS XR | -| `ipv4_pim_sparse_mode` | Not supported on IOS XR | -| `negotiate_auto` | Not supported on IOS XR | -| `speed` | Not supported on IOS XR | -| `stp_bpdufilter` | Not supported on IOS XR | -| `stp_bpduguard` | Not supported on IOS XR | -| `stp_cost` | Not supported on IOS XR | -| `stp_guard` | Not supported on IOS XR | -| `stp_link_type` | Not supported on IOS XR | -| `stp_port_priority` | Not supported on IOS XR | -| `stp_port_type` | Not supported on IOS XR | -| `stp_mst_cost` | Not supported on IOS XR | -| `stp_mst_port_priority` | Not supported on IOS XR | -| `stp_vlan_cost` | Not supported on IOS XR | -| `stp_vlan_port_priority` | Not supported on IOS XR | -| `svi_autostate` | Not supported on N56xx, N6k, IOS XR | -| `svi_management` | Not supported on IOS XR | -| `switchport` | Not supported on IOS XR | -| `switchport_autostate_exclude` | Not supported on IOS XR | -| `switchport_mode` | Not supported on IOS XR | -| `switchport_trunk_allowed_vlan` | Not supported on IOS XR | -| `switchport_trunk_native_vlan` | Not supported on IOS XR | -| `switchport_vtp` | Not supported on IOS XR | -| `vlan_mapping` | Not supported on N9k, N3k, N56xx, N6k, IOS XR | -| `vlan_mapping_enable` | Not supported on IOS XR | - -#### Parameters - -##### Basic interface config attributes - -###### `ensure` -Determine whether the interface config should be present or not. Valid values -are 'present' and 'absent'. - -###### `interface` -Name of the interface on the network element. Valid value is a string. - -#### Properties - -###### `description` -Description of the interface. Valid values are a string or the keyword 'default'. - -###### `duplex` -Duplex of the interface. Valid values are 'full', and 'auto'. This property is not supported on IOS XR. - -###### `speed` -Speed of the interface. Valid values are 100, 1000, 10000, 40000, 1000000, and 'auto'. This property is not supported on IOS XR. - -###### `shutdown` -Shutdown state of the interface. Valid values are 'true', 'false', and -'default'. - -###### `switchport_mode` -Switchport mode of the interface. Interfaces that support `switchport_mode` may default to layer 2 or layer 3 depending on platform, interface type, or the `system default switchport` setting. An interface may be explicitly set to Layer 3 by setting `switchport_mode` to 'disabled'. Valid values are 'disabled', 'access', 'tunnel', 'fex_fabric', 'trunk', 'fabricpath' and 'default'. This property is not supported on IOS XR. - -##### L2 interface config attributes - -###### `access_vlan` -The VLAN ID assigned to the interface. Valid values are an integer or the keyword 'default'. This property is not supported on IOS XR. - -##### `encapsulation_dot1q` -Enable IEEE 802.1Q encapsulation of traffic on a specified subinterface. -Valid values are integer, keyword 'default'. - -##### `mtu` -Maximum Trasnmission Unit size for frames received and sent on the specified -interface. Valid value is an integer. - -##### `switchport_autostate_exclude` -Exclude this port for the SVI link calculation. Valid values are 'true', 'false', and 'default'. This property is not supported on IOS XR. - -##### `switchport_trunk_allowed_vlan` -The allowed VLANs for the specified Ethernet interface. Valid values are -string, keyword 'default'. This property is not supported on IOS XR. - -##### `switchport_trunk_native_vlan` -The Native VLAN assigned to the switch port. Valid values are integer, keyword 'default'. This property is not supported on IOS XR. - -###### `switchport_vtp` -Enable or disable VTP on the interface. Valid values are 'true', 'false', -and 'default'. This property is not supported on IOS XR. - -###### `negotiate_auto` -Enable/Disable negotiate auto on the interface. Valid values are 'true', -'false', and 'default'. This property is not supported on IOS XR. - -##### L3 interface config attributes - -###### `ipv4_acl_in` -Applies an ipv4 access list on the interface in the ingress direction. An access-list should be present on the network device prior to this configuration. Valid values are string, keyword 'default'. - -###### `ipv4_acl_out` -Applies an ipv4 access list on the interface in the egress direction. An access-list should be present on the network device prior to this configuration. Valid values are string, keyword 'default'. - -###### `ipv4_pim_sparse_mode` -Enables or disables ipv4 pim sparse mode on the interface. Valid values are 'true', 'false', and 'default'. This property is not supported on IOS XR. - -###### `ipv4_proxy_arp` -Enables or disables proxy arp on the interface. Valid values are 'true', 'false', and 'default'. - -###### `ipv4_address` -IP address of the interface. Valid values are a string of ipv4 address or the -keyword 'default'. - -###### `ipv4_netmask_length` -Network mask length of the IP address on the interface. Valid values are -integer and keyword 'default'. - -###### `ipv4_address_secondary` -Secondary IP address of the interface. Valid values are a string of ipv4 address or the keyword 'default'. - -###### `ipv4_netmask_length_secondary` -Network mask length of the secondary IP address on the interface. Valid values are integer and keyword 'default'. - -###### `ipv4_arp_timeout` -Address Resolution Protocol (ARP) timeout value. Valid values are integer and keyword 'default'. Currently only supported on vlan interfaces. This property is not supported on IOS XR as IOS XR does not support vlan interfaces. - -###### `ipv4_forwarding` -IP forwarding state. Valid values are string or keyword 'default'. This property is not supported on IOS XR. - -###### `ipv4_pim_sparse_mode` -Enables or disables ipv4 pim sparse mode on the interface. Valid values are 'true', 'false', and 'default'. - -###### `ipv4_proxy_arp` -Enables or disables proxy arp on the interface. Valid values are 'true', 'false', and 'default'. - -###### `ipv4_redirects` -Enables or disables sending of IP redirect messages. Valid values are 'true', 'false', and 'default'. - -###### `ipv6_acl_in` -Applies an ipv6 access list on the interface in the ingress direction. An access-list should be present on the network device prior to this configuration. Valid values are string, keyword 'default'. - -###### `ipv6_acl_out` -Applies an ipv6 access list on the interface in the egress direction. An access-list should be present on the network device prior to this configuration. Valid values are string, keyword 'default'. - -###### `vlan_mapping` -This property is a nested array of [original_vlan, translated_vlan] pairs. Valid values are an array specifying the mapped vlans or keyword 'default'; e.g.: - -``` -vlan_mapping => [[20, 21], [30, 31]] -``` - -This property is not supported on IOS XR. - -###### `vlan_mapping_enable` -Allows disablement of vlan_mapping on a given interface. Valid values are 'true', 'false', and 'default'. This property is not supported on IOS XR. - -###### `vpc_id` -Configure the vPC ID on this interface to make it a vPC link. The peer switch should configure a corresponding interface with the same vPC ID in order for the downstream device to add these links as part of the same port-channel. The vpc_id can generally be configured only on interfaces which are themselves port-channels (usually a single member port-channel). However, on the Nexus 7000 series a physical port can be configured as a vPC link. Valid values are integers in the range 1..4096. By default, interface is not configured with any vpc_id. - -###### `vpc_peer_link` -Configure this port-channel interface to be a vPC peer-link. A vPC peer-link is essential to the working of the vPC complex, not only for establishing the peer connectivity for control message exchange, but also for providing redundancy when vPC links fail. Valid values are 'true' or 'false'. Default value: false. - -###### `vrf` -VRF member of the interface. Valid values are a string or the keyword 'default'. - -##### STP config attributes - -##### `stp_bpdufilter` -Enable/Disable BPDU (Bridge Protocol Data Unit) filter for this interface. Valid values are enable, disable or 'default'. - -##### `stp_bpduguard` -Enable/Disable BPDU (Bridge Protocol Data Unit) guard for this interface. Valid values are enable, disable or 'default'. - -##### `stp_cost` -Path cost. Valid values are integer, 'auto' or 'default'. - -##### `stp_guard` -Guard mode. Valid values are loop, none, root or 'default'. - -##### `stp_link_type` -Link type. Valid values are auto, shared, point-to-point or 'default'. - -##### `stp_mst_cost` -Mst cost. Valid values are an array of [mst_range, cost] pairs or 'default'. - -##### `stp_mst_port_priority` -Mst port priority. Valid values are an array of [mst_range, port_priority] pairs or 'default'. - -##### `stp_port_priority` -Port priority. Valid values are integer or 'default'. - -##### `stp_port_type` -Port type. Valid values are edge, network, normal, edge_trunk or 'default'. - -##### `stp_vlan_cost` -Vlan path cost. Valid values are an array of [vlan_range, cost] pairs or 'default'. - -##### `stp_vlan_port_priority` -Vlan port priority. Valid values are an array of [vlan_range, port_priority] pairs or 'default'. - -##### SVI interface config attributes - -###### `fabric_forwarding_anycast_gateway` -Associate SVI with anycast gateway under VLAN configuration mode. The `cisco_overlay_global` `anycast_gateway_mac` must be set before setting this property. -Valid values are 'true', 'false', and 'default'. - -###### `svi_autostate` -Enable/Disable autostate on the SVI interface. Valid values are 'true', -'false', and 'default'. This property is not supported on IOS XR. - -###### `svi_management` -Enable/Disable management on the SVI interface. Valid values are 'true', 'false', and 'default'. This property is not supported on IOS XR. - --- -### Type: cisco_interface_channel_group - -Manages a Cisco Network Interface Channel-group. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | unsupported | unsupported | -| N6k | unsupported | unsupported | -| N7k | unsupported | unsupported | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Parameters - -##### Basic interface channel-group config attributes - -###### `ensure` -Determine whether the interface config should be present or not. Valid values are 'present' and 'absent'. - -###### `interface` -Name of the interface where the service resides. Valid value is a string. - -###### `channel_group` -channel_group is an aggregation of multiple physical interfaces that creates a logical interface. Valid values are 1 to 4096 and 'default'. - -Note: On some platforms a normal side-effect of adding the channel-group property is that an independent port-channel interface will be created; however, removing the channel-group configuration by itself will not also remove the port-channel interface. Therefore, the port-channel interface itself may be explicitly removed by using the `cisco_interface` provider with `ensure => absent`. - -###### `description` -Description of the interface. Valid values are a string or the keyword 'default'. - -###### `shutdown` -Shutdown state of the interface. Valid values are 'true', 'false', and 'default'. - --- -### Type: cisco_interface_service_vni - -Manages a Cisco Network Interface Service VNI. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | not applicable | not applicable | -| N30xx | not applicable | not applicable | -| N31xx | not applicable | not applicable | -| N56xx | not applicable | not applicable | -| N6k | not applicable | not applicable | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | not applicable | not applicable | -| IOS XR | not applicable | not applicable | - -#### Parameters - -##### Basic interface service vni config attributes - -###### `ensure` -Determine whether the interface config should be present or not. Valid values are 'present' and 'absent'. - -###### `interface` -Name of the interface where the service resides. Valid value is a string. - -###### `sid` -The Service ID number. Valid value is an Integer. - -#### Properties - -###### `encapsulation_profile_vni` -The VNI Encapsulation Profile Name. Valid values are String or the keyword 'default' - -###### `shutdown` -Shutdown state of the interface service vni. Valid values are 'true', 'false', or 'default'. - --- -### Type: cisco_interface_ospf -Manages configuration of an OSPF interface instance. - -#### Parameters - -##### `ensure` -Determine whether the config should be present or not. Valid values are -'present' and 'absent'. - -##### `interface` -Name of this cisco_interface resource. Valid value is a string. - -##### `ospf` -Name of the cisco_ospf resource. Valid value is a string. - -##### `cost` -The cost associated with this cisco_interface_ospf instance. Valid value is an integer. - -##### `hello_interval` -The hello_interval associated with this cisco_interface_ospf instance. Time -between sending successive hello packets. Valid values are an integer or the -keyword 'default'. - -##### `dead_interval` -The dead_interval associated with the cisco_interface_ospf instance. -Time interval an ospf neighbor waits for a hello packet before tearing down -adjacencies. Valid values are an integer or the keyword 'default'. - -##### `passive_interface` -Passive interface associated with the cisco_interface_ospf instance. Setting -to true will prevent this interface from receiving HELLO packets. -Valid values are 'true' and 'false'. - -##### `message_digest` -Enables or disables the usage of message digest authentication. -Valid values are 'true' and 'false'. - -##### `message_digest_key_id` -md5 authentication key-id associated with the cisco_interface_ospf instance. -If this is present in the manifest, message_digest_encryption_type, -message_digest_algorithm_type and message_digest_password are mandatory. -Valid value is an integer. - -##### `message_digest_algorithm_type` -Algorithm used for authentication among neighboring routers within an area. -Valid values are 'md5' and keyword 'default'. - -##### `message_digest_encryption_type` -Specifies the scheme used for encrypting message_digest_password. -Valid values are 'cleartext', '3des' or 'cisco_type_7' encryption, and -'default', which defaults to 'cleartext'. - -##### `message_digest_password` -Specifies the message_digest password. Valid value is a string. - -##### `area` -*Required*. Ospf area associated with this cisco_interface_ospf instance. Valid values are a string, formatted as an IP address (i.e. "0.0.0.0") or as an integer. - --- -### Type: cisco_interface_portchannel - -Manages configuration of a portchannel interface instance. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Caveats - -| Property | Caveat Description | -|:--------|:-------------| -| `port_hash_distribution `
`port_load_defer ` | Not supported on N56xx, N6k | - -#### Parameters - -##### `ensure` -Determine whether the config should be present or not. Valid values are 'present' and 'absent'. - -##### `lacp_graceful_convergence` -port-channel lacp graceful convergence. Valid values are true, false or 'default'. - -##### `lacp_max_bundle` -port-channel max-bundle. Valid values are Integer, keyword 'default'. - -##### `lacp_min_links` -port-channel min-links. Valid values are Integer, keyword 'default'. - -##### `lacp_suspend_individual` -lacp port-channel state. Valid values are true and false or 'default'. - -##### `port_hash_distribution` -port-channel per port hash-distribution. Valid values are 'adaptive', 'fixed' or the keyword 'default'. This property is not supported on (Nexus 5|6k) - -##### `port_load_defer` -port-channel per port load-defer. Valid values are true, false or 'default'. This property is not supported on (Nexus 5|6k) - --- -### Type: cisco_itd_device_group - -Manages configuration of ITD (Intelligent Traffic Director) device group - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I3(1) | 1.3.0 | -| N30xx | not applicable | not applicable | -| N31xx | not applicable | not applicable | -| N56xx | not applicable | not applicable | -| N6k | not applicable | not applicable | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | not applicable | not applicable | -| IOS XR | not applicable | not applicable | - -#### Parameters - -##### `ensure` -Determine whether the config should be present or not. Valid values are 'present' and 'absent'. - -##### `probe_control` -Enable control protocol for probe. Valid values are true, false or 'default'. This is applicable only when the probe type is 'tcp' or 'udp' - -##### `probe_dns_host` -Host name or target address when the probe type is 'dns'. Valid values are String. - -##### `probe_frequency` -Probe frequency in seconds. Valid values are Integer, keyword 'default'. - -##### `probe_port` -Probe port number when the type is 'tcp' or 'udp'. Valid values are Integer. - -##### `probe_retry_down` -Probe retry count when the node goes down. Valid values are Integer, keyword 'default'. - -##### `probe_retry_up` -Probe retry count when the node comes back up. Valid values are Integer, keyword 'default'. - -##### `probe_timeout` -Probe timeout in seconds. Valid values are Integer, keyword 'default'. - -##### `probe_type` -Probe type. Valid values are 'icmp', 'dns', 'tcp', 'udp' or keyword 'default'. - --- -### Type: cisco_itd_device_group_node - -Manages configuration of ITD (Intelligent Traffic Director) device group node - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I3(1) | 1.3.0 | -| N30xx | not applicable | not applicable | -| N31xx | not applicable | not applicable | -| N56xx | not applicable | not applicable | -| N6k | not applicable | not applicable | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | not applicable | not applicable | -| IOS XR | not applicable | not applicable | - -#### Parameters - -##### `ensure` -Determine whether the config should be present or not. Valid values are 'present' and 'absent'. - -##### `hot_standby` -Change mode of this node as hot-standby. Valid values are true, false or 'default'. - -##### `node_type` -Type of the device group node. Valid values are ip and IPv6. It defaults to ip if not specified. IPv6 is not supported for N9k. - -##### `probe_control` -Enable control protocol for probe. Valid values are true, false or 'default'. This is applicable only when the probe type is 'tcp' or 'udp' - -##### `probe_dns_host` -Host name or target address when the probe type is 'dns'. Valid values are String. - -##### `probe_frequency` -Probe frequency in seconds. Valid values are Integer, keyword 'default'. - -##### `probe_port` -Probe port number when the type is 'tcp' or 'udp'. Valid values are Integer. - -##### `probe_retry_down` -Probe retry count when the node goes down. Valid values are Integer, keyword 'default'. - -##### `probe_retry_up` -Probe retry count when the node comes back up. Valid values are Integer, keyword 'default'. - -##### `probe_timeout` -Probe timeout in seconds. Valid values are Integer, keyword 'default'. - -##### `probe_type` -Probe type. Valid values are 'icmp', 'dns', 'tcp', 'udp' or keyword 'default'. - -##### `weight` -Weight for traffic distribution. Valid values are Integer, keyword 'default'. - --- -### Type: cisco_itd_service - -Manages configuration of ITD (Intelligent Traffic Director) service. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I3(1) | 1.3.0 | -| N30xx | not applicable | not applicable | -| N31xx | not applicable | not applicable | -| N56xx | not applicable | not applicable | -| N6k | not applicable | not applicable | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | not applicable | not applicable | -| IOS XR | not applicable | not applicable | - -#### Caveats - -| Property | Caveat Description | -|:--------|:-------------| -| `nat_destination` | Supported only on N7k | -| `peer_local` | Supported only on N9k | -| `peer_vdc` | Supported only on N7k | -| `vrf` | vrf cannot be removed as an attribute to the service so this is not going to be supported in this release | - -#### Parameters - -##### `ensure` -Determine whether the config should be present or not. Valid values are 'present' and 'absent'. - -##### `access_list` -ITD access-list name. Valid values are String or 'default'. - -##### `device_group` -Device group name where this service belongs. Valid values are String or 'default'. - -##### `exclude_access_list` -ITD exclude-access-list name. Valid values are String or 'default'. - -##### `fail_action` -ITD failaction to reassign node. This enables traffic on failed nodes to be reassigned to the first available active node. Valid values are true, false or 'default'. - -##### `ingress_interface` -Ingress interface. Valid values are an array of `[interface, next-hop]` pairs or 'default'. - -##### `load_bal_enable` -Enable or disable load balance. Valid values are true, false or 'default'. - -##### `load_bal_buckets` -Buckets for traffic distribution (in powers of 2). Valid values are Integer, or keyword 'default'. - -##### `load_bal_mask_pos` -Loadbalance mask position. Valid values are Integer, keyword 'default'. - -##### `load_bal_method_bundle_select` -Loadbalance bundle select. Valid values are 'src, 'dst' or keyword 'default'. - -##### `load_bal_method_bundle_hash` -Loadbalance bundle hash. Valid values are 'ip, 'ip-l4port' or keyword 'default'. - -##### `load_bal_method_proto` -Loadbalance protocol. This is valid only when the bundle hash is 'ip-l4port'. Valid values are 'tcp, 'udp' or keyword 'default'. - -##### `load_bal_method_start_port` -Starting port in range (to match only packets in the range of port numbers). This is valid only when the bundle hash is 'ip-l4port'. Valid values are Integer, keyword 'default'. - -##### `load_bal_method_end_port` -Ending port in range (to match only packets in the range of port numbers). This is valid only when the bundle hash is 'ip-l4port'. Valid values are Integer, keyword 'default'. - -##### `nat_destination` -Destination NAT. Valid values are true, false or 'default'. - -##### `peer_local` -Peer involved in sandwich mode. Valid values are String or 'default'. - -##### `peer_vdc` -Peer involved in sandwich mode. Valid values are an array of `[vdc, service]` or 'default'. - -##### `shutdown` -Whether or not the service is shutdown. Valid values are 'true', 'false' and -keyword 'default'. - -##### `virtual_ip` -Virtual ip configuration. Valid values are an array of Strings or 'default'. - --- -### Type: cisco_ospf - -Manages configuration of an ospf instance. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.0.1 | -| N30xx | 7.0(3)I2(1) | 1.0.1 | -| N31xx | 7.0(3)I2(1) | 1.0.1 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `ensure` -Determine if the config should be present or not. Valid values are 'present', -and 'absent'. - -##### `ospf` -Name of the ospf router. Valid value is a string. - --- -### Type: cisco_ospf_vrf - -Manages a VRF for an OSPF router. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.0.1 | -| N30xx | 7.0(3)I2(1) | 1.0.1 | -| N31xx | 7.0(3)I2(1) | 1.0.1 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `ensure` -Determines whether the config should be present or not on the device. Valid values are 'present' and 'absent'. - -##### `vrf` -Name of the resource instance. Valid value is a string. The name 'default' is -a valid VRF representing the global ospf. - -##### `ospf` -Name of the ospf instance. Valid value is a string. - -##### `router_id` -Router Identifier (ID) of the OSPF router VRF instance. Valid values are a string or the keyword 'default'. - -##### `default_metric` -Specify the default Metric value. Valid values are an integer or the keyword -'default'. - -##### `log_adjacency` -Controls the level of log messages generated whenever a neighbor changes state. -Valid values are 'log', 'detail', 'none', and 'default'. - -##### `timer_throttle_lsa_start` -Specify the start interval for rate-limiting Link-State Advertisement (LSA) -generation. Valid values are an integer, in milliseconds, or the keyword 'default'. - -##### `timer_throttle_lsa_hold` -Specifies the hold interval for rate-limiting Link-State Advertisement (LSA) -generation. Valid values are an integer, in milliseconds, or the keyword 'default'. - -##### `timer_throttle_lsa_max` -Specifies the max interval for rate-limiting Link-State Advertisement (LSA) -generation. -Valid values are an integer, in milliseconds, or the keyword 'default'. - -##### `timer_throttle_spf_start` -Specify initial Shortest Path First (SPF) schedule delay. -Valid values are an integer, in milliseconds, or the keyword 'default'. - -##### `timer_throttle_spf_hold` -Specify minimum hold time between Shortest Path First (SPF) calculations. -Valid values are an integer, in milliseconds, or the keyword 'default'. - -##### `timer_throttle_spf_max` -Specify the maximum wait time between Shortest Path First (SPF) calculations. -Valid values are an integer, in milliseconds, or the keyword 'default'. - -##### `auto_cost` -Specifies the reference bandwidth used to assign OSPF cost. -Valid values are an integer, in Mbps, or the keyword 'default'. - --- -### Type: cisco_overlay_global -Handles the detection of duplicate IP or MAC addresses based on the number of moves in a given time-interval (seconds). -Also configures anycast gateway MAC of the switch. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | not applicable | not applicable | -| N31xx | not applicable | not applicable | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | not applicable | not applicable | - -#### Parameters - -##### `name` -Instance of cisco_overlay_global, only allow the value 'default' - -##### `anycast_gateway_mac` -Anycast gateway mac of the switch - -##### `dup_host_ip_addr_detection_host_moves` -The number of host moves allowed in n seconds. The range is 1 to 1000 moves; default is 5 moves. - -##### `dup_host_ip_addr_detection_timeout` -The duplicate detection timeout in seconds for the number of host moves. The range is 2 to 36000 seconds; default is 180 seconds. - -##### `dup_host_mac_detection_host_moves` -The number of host moves allowed in n seconds. The range is 1 to 1000 moves; default is 5 moves. - -##### `dup_host_mac_detection_timeout` -The duplicate detection timeout in seconds for the number of host moves. The range is 2 to 36000 seconds; default is 180 seconds. - --- -### Type: cisco_pim -Manages configuration of an Protocol Independent Multicast (PIM) instance. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `afi` -Address Family Identifier (AFI). Required. Valid value is ipv4. - -##### `vrf` -Name of the resource instance. Required. Valid values are string. The name 'default' is a valid VRF representing the global vrf. - -#### Properties - -##### `ssm_range` -Configure group ranges for Source Specific Multicast (SSM). Valid values are multicast addresses or the keyword ‘none’. - --- -### Type: cisco_pim_grouplist -Manages configuration of an Protocol Independent Multicast (PIM) static route processor (RP) address for a multicast group range. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `afi` -Address Family Identifier (AFI). Required. Valid values are ipv4 and ipv6. - -##### `vrf` -Name of the resource instance. Required. Valid values are string. The name 'default' is a valid VRF representing the global vrf. - -##### `rp_addr` -IP address of a router which is the route processor (RP) for a group range.. Required. Valid values are unicast addresses. - -##### `group` -Specifies a group range for a static route processor (RP) address. Required. Valid values are multicast addresses. - --- -### Type: cisco_pim_rp_address -Manages configuration of an Protocol Independent Multicast (PIM) static route processor (RP) address instance. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `afi` -Address Family Identifier (AFI). Required. Valid values are ipv4 and ipv6. - -##### `vrf` -Name of the resource instance. Required. Valid values are string. The name 'default' is a valid VRF representing the global vrf. - -##### `rp_addr` -Configures a Protocol Independent Multicast (PIM) static route processor (RP) address. Required. Valid values are unicast addresses. - --- -### Type: cisco_portchannel_global -Manages configuration of a portchannel global parameters - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.3.0 | -| N31xx | 7.0(3)I2(1) | 1.3.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Caveats - -| Property | Caveat Description | -|:--------|:-------------| -| `hash_poly` | Supported only on N56xx, N6k | -| `asymmetric`
`hash_distribution`
`load_defer` | Supported only on N7k | -| `concatenation` | Supported only on N9k| -| `resilient`
`symmetry` | Supported only on N30xx, N31xx, N9k | -| `rotate` | Supported only on N7k, N8k, N9k | -| `bundle_hash` | 'port', 'ip-only', 'port-only' are only supported on N30xx, N31xx, N56xx, N6k. 'ip-gre' is only supported on N30xx, N31xx, N9k. 'ip-l4port', 'ip-l4port-vlan', 'ip-vlan', 'l4port' are only supported on N9k, N7k. | - -#### Parameters - -##### `asymmetric` -port-channel asymmetric hash. Valid values are true, false or 'default'. - -##### `bundle_hash` -port-channel bundle hash. Valid values are 'ip', 'ip-l4port', 'ip-l4port-vlan', 'ip-vlan', 'l4port', 'mac', 'port', 'ip-only', 'port-only', 'ip-gre' or 'default'. - -##### `bundle_select` -port-channel bundle select. Valid values are 'src', 'dst', 'src-dst' or 'default'. - -##### `concatenation` -port-channel concatenation enable or disable. Valid values are true, false or 'default'. - -##### `hash_distribution` -port-channel hash-distribution. Valid values are 'adaptive', 'fixed' or the keyword 'default'. - -##### `hash_poly` -port-channel hash-polynomial. Valid values are 'CRC10a', 'CRC10b', 'CRC10c' or 'CRC10d'. Note: This property does not support the keyword 'default'. - -##### `load_defer` -port-channel load-defer time interval. Valid values are integer or 'default'. - -##### `resilient` -port-channel resilient mode. Valid values are true, false or 'default'. - -##### `rotate` -port-channel hash input offset. Valid values are integer or 'default'. - -##### `symmetry` -port-channel symmetry hash. Valid values are true, false or 'default'. - --- -### Type: cisco_stp_global -Manages spanning tree global parameters - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.3.0 | -| N30xx | 7.0(3)I2(1) | 1.3.0 | -| N31xx | 7.0(3)I2(1) | 1.3.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Caveats - -| Property | Caveat Description | -|:--------|:-------------| -| `bd_designated_priority` | Supported only on N7k | -| `bd_forward_time` | Supported only on N7k | -| `bd_hello_time` | Supported only on N7k | -| `bd_max_age` | Supported only on N7k | -| `bd_priority` | Supported only on N7k | -| `bd_root_priority` | Supported only on N7k | -| `domain` | Supported only on N56k, N6k, N7k | -| `fcoe` | Supported only on N9k | - -#### Parameters - -##### `bd_designated_priority` -Designated bridge priority. Valid values are an array of [bd_range, designated_priority] pairs or 'default'. - -##### `bd_forward_time` -Forward delay. Valid values are an array of [bd_range, forward_time] pairs or 'default'. - -##### `bd_hello_time` -Hello interval. Valid values are an array of [bd_range, hello_time] pairs or 'default'. - -##### `bd_max_age` -Max age interval. Valid values are an array of [bd_range, max_age] pairs or 'default'. - -##### `bd_priority` -Bridge priority. Valid values are an array of [bd_range, priority] pairs or 'default'. - -##### `bd_root_priority` -Root bridge priority. Valid values are an array of [bd_range, root_priority] pairs or 'default'. - -##### `bpdufilter` -Edge port (portfast) bpdu filter. Valid values are true, false or 'default'. - -##### `bpduguard` -Edge port (portfast) bpdu guard. Valid values are true, false or 'default'. - -##### `bridge_assurance` -Bridge Assurance on all network ports. Valid values are true, false or 'default'. - -##### `domain` -Domain. Valid values are integer or 'default'. - -##### `fcoe` -Spanning tree protocol for FCoE VLAN. Valid values are true, false or 'default'. - -##### `loopguard` -Enable loopguard by default on all ports. Valid values are true, false or 'default'. - -##### `mode` -Operating mode. Valid values are mst, rapid-pvst or 'default'. - -##### `mst_designated_priority` -Designated priority for multiple spanning tree configuration. Valid values are an array of [mst_range, designated_priority] pairs or 'default' - -##### `mst_hello_time` -Hello interval for multiple spanning tree configuration. Valid values are integer or 'default'. - -##### `mst_inst_vlan_map` -Map vlans to an MST instance. Valid values are an array of [mst_instance, vlan_range] pairs or 'default' - -##### `mst_max_age` -Max age interval for multiple spanning tree configuration. Valid values are integer or 'default'. - -##### `mst_max_hops` -Max hops for multiple spanning tree configuration. Valid values are integer or 'default' - -##### `mst_name` -Name for multiple spanning tree configuration. Valid values are String or 'default' - -##### `mst_priority` -Priority for multiple spanning tree configuration. Valid values are an array of [mst_range, priority] pairs or 'default' - -##### `mst_revision` -Configuration revision number for multiple spanning tree configuration. Valid values are String or 'default' - -##### `mst_root_priority` -Root priority for multiple spanning tree configuration. Valid values are an array of [mst_range, root_priority] pairs or 'default' - -##### `pathcost` -Pathcost option. Valid values are long, short or 'default'. - -##### `vlan_designated_priority` -Designated priority for vlan. Valid values are an array of [vlan_range, designated_priority] pairs or 'default' - -##### `vlan_forward_time` -Forward delay for vlan. Valid values are an array of [vlan_range, forward_time] pairs or 'default' - -##### `vlan_hello_time` -Hello interval for vlan. Valid values are an array of [vlan_range, hello_time] pairs or 'default' - -##### `vlan_max_age` -Max age interval for vlan. Valid values are an array of [vlan_range, max_age] pairs or 'default' - -##### `vlan_priority` -Priority for vlan. Valid values are an array of [vlan_range, priority] pairs or 'default' - -##### `vlan_root_priority` -Root priority for vlan. Valid values are an array of [vlan_range, root_priority] pairs or 'default' - --- -### Type: cisco_snmp_community -Manages an SNMP community on a Cisco SNMP server. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.0.1 | -| N30xx | 7.0(3)I2(1) | 1.0.1 | -| N31xx | 7.0(3)I2(1) | 1.0.1 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `ensure` -Determine whether the config should be present or not on the device. Valid -values are 'present' and 'absent'. - -##### `community` -Name of the SNMP community. Valid value is a string. - -##### `group` -Group that the SNMP community belongs to. Valid values are a string or the -keyword 'default'. - -##### `acl` -Assigns an Access Control List (ACL) to an SNMP community to filter SNMP -requests. Valid values are a string or the keyword 'default'. - --- -### Type: cisco_snmp_group - -Manages a Cisco SNMP Group on a Cisco SNMP Server. - -The term 'group' is a standard SNMP term, but in NXOS role it serves the purpose -of group; thus this provider utility does not create snmp groups and only reports group (role) existence. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.0.1 | -| N30xx | 7.0(3)I2(1) | 1.0.1 | -| N31xx | 7.0(3)I2(1) | 1.0.1 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `ensure` -Determines whether the config should be present on the device or not. Valid -values are 'present', and 'absent'. - -##### `group` -Name of the snmp group. Valid value is a string. - --- -### Type: cisco_snmp_server -Manages a Cisco SNMP Server. There can only be one instance of the -cisco_snmp_server. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.0.1 | -| N30xx | 7.0(3)I2(1) | 1.0.1 | -| N31xx | 7.0(3)I2(1) | 1.0.1 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `name` -The name of the SNMP Server instance. Only 'default' is accepted as a valid -name. - -##### `location` -SNMP location (sysLocation). Valid values are a string or the keyword 'default'. - -##### `contact` -SNMP system contact (sysContact). Valid values are a string or the keyword -'default'. - -##### `aaa_user_cache_timeout` -Configures how long the AAA synchronized user configuration stays in the local -cache. Valid values are an integer or the keyword 'default'. - -##### `packet_size` -Size of SNMP packet. Valid values are an integer, in bytes, or the keyword 'default'. - -##### `global_enforce_priv` -Enable/disable SNMP message encryption for all users. Valid values are 'true', -'false', and 'default'. - -##### `protocol` -Enable/disable SNMP protocol. Valid values are 'true', 'false', and 'default'. - -##### `tcp_session_auth` -Enable/disable a one time authentication for SNMP over TCP session. -Valid values are 'true', 'false', and 'default'. - --- -### Type: cisco_snmp_user - -Manages an SNMP user on an cisco SNMP server. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.0.1 | -| N30xx | 7.0(3)I2(1) | 1.0.1 | -| N31xx | 7.0(3)I2(1) | 1.0.1 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `ensure` -Determines whether the config should be present or not on the device. Valid -values are 'present', and 'absent'. - -##### `user` -Name of the SNMP user. Valid value is a string. - -##### `engine_id` -Engine ID of the SNMP user. Valid values are empty string or 5 to 32 octets -seprated by colon. - -##### `groups` -Groups that the SNMP user belongs to. Valid value is a string. - -##### `auth_protocol` -Authentication protocol for the SNMP user. Valid values are 'md5', 'sha', -and 'none'. - -##### `auth_password` -Authentication password for the SNMP user. Valid value is string. - -##### `priv_protocol` -Privacy protocol for the SNMP user. Valid values are 'aes128', 'des', and -'none'. - -##### `priv_password` -Privacy password for SNMP user. Valid value is a string. - -##### `localized_key` -Specifies whether the passwords specified in manifest are in localized key -format (in case of true) or cleartext (in case of false). Valid values are 'true', and 'false'. - --- -### Type: cisco_tacacs_server - -Manages a Cisco TACACS+ Server global configuration. There can only be one -instance of the cisco_tacacs_server. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.0.1 | -| N30xx | 7.0(3)I2(1) | 1.0.1 | -| N31xx | 7.0(3)I2(1) | 1.0.1 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `name` -Instance of the tacacs_server, only allows the value 'default'. - -##### `timeout` -Global timeout interval for TACACS+ servers. Valid value is an integer, -in seconds, or the keyword 'default'. - -##### `directed_request` -Allows users to specify a TACACS+ server to send the authentication request -when logging in. Valid values are 'true', and 'false'. - -##### `deadtime` -Specifies the global deadtime interval for TACACS+ servers. Valid values are -Integer, in minutes, and keyword 'default'. - -##### `encryption_type` -Specifies the global preshared key type for TACACS+ servers. -Valid values are 'clear', 'encrypted', 'none', and 'default'. - -##### `encryption_password` -Specifies the global TACACS+ servers preshared key password. Valid values are -string, and keyword 'default'. - -##### `source_interface` -Global source interface for all TACACS+ server groups configured on the device. -Valid values are string, and keyword 'default'. - --- -### Type: cisco_tacacs_server_host - -Configures Cisco TACACS+ server hosts. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.0.1 | -| N30xx | 7.0(3)I2(1) | 1.0.1 | -| N31xx | 7.0(3)I2(1) | 1.0.1 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid -values are 'present' and 'absent'. - -##### `host` -Name of the tacacs_server_host instance. Valid value is a string. - -##### `port` -Server port for the host. Valid values are an integer or the keyword 'default'. - -##### `timeout` -Timeout interval for the host. Valid values are an integer, in seconds, or the -keyword 'default'. - -##### `encryption_type` -Specifies a preshared key for the host. Valid values are 'clear', 'encrypted', -'none', and keyword 'default'. - -##### `encryption_password` -"Specifies the preshared key password for the host. Valid value is a string. - --- -### Type: cisco_vdc - -Manages a Cisco VDC (Virtual Device Context). - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | not applicable | not applicable | -| N30xx | not applicable | not applicable | -| N31xx | not applicable | not applicable | -| N56xx | not applicable | not applicable | -| N6k | not applicable | not applicable | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | not applicable | not applicable | -| IOS XR | not applicable | not applicable | - -#### Parameters - -##### `name` -Name of the VDC. Valid value is a String or optional keyword 'default' when referencing the default VDC. -*The current implementation restricts changes to the default VDC*. - -##### `ensure` -Determines whether the config should be present or not. Valid values are 'present' and 'absent'. - -#### Properties - -##### `limit_resource_module_type` -This command restricts the allowed module-types in a given VDC. Valid values are String or keyword 'default'. - --- -### Type: cisco_vlan - -Manages a Cisco VLAN. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.0.1 | -| N30xx | 7.0(3)I2(1) | 1.0.1 | -| N31xx | 7.0(3)I2(1) | 1.0.1 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Caveats - -| Property | Caveat Description | -|:--------|:-------------| -| `mode` | Not supported on N9k, N30xx, N31xx | -| `fabric_control` | Supported on N7k with module minimum version as 1.3.0 | - -#### Parameters - -##### `vlan` -ID of the Virtual LAN. Valid value is an integer. - -##### `ensure` -Determines whether the config should be present or not. Valid values are 'present' and 'absent'. - -##### `mapped_vni` -The Virtual Network Identifier (VNI) id that is mapped to the VLAN. Valid values are integer and keyword 'default'. - -##### `mode` -Determines mode of the VLAN. Valid values are 'CE', 'fabricpath' and -keyword 'default'. - -##### `vlan_name` -The name of the VLAN. Valid values are a string or the keyword 'default'. - -##### `state` -State of the VLAN. Valid values are 'active', 'suspend', and keyword 'default'. - -##### `shutdown` -Whether or not the vlan is shutdown. Valid values are 'true', 'false' and -keyword 'default'. - -##### `fabric_control` -Specifies this vlan as the fabric control vlan. Only one bridge-domain or VLAN can be configured as fabric-control. Valid values are true, false. - --- -### Type: cisco_vpc_domain -Manages the virtual Port Channel (vPC) domain configuration of a Cisco device. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Caveats - -| Property | Caveat Description | -|:--------|:-------------| -| `auto_recovery` | Not supported on N56xx, N6k | -| `fabricpath_emulated_switch_id` | Not supported on N31xx, N9k, N56xx, N6k | -| `fabricpath_multicast_load_balance` | Not supported on N31xx, N9k, N56xx, N6k | -| `layer3_peer_routing` | Not supported on N9k, N30xx, N31xx, N56xx | -| `peer_gateway_exclude_vlan` | Not supported on N9k, N30xx, N31xx | -| `port_channel_limit` | Not supported on N31xx, N9k, N56xx, N6k | -| `self_isolation` | Not supported on N9k, N56xx, N6k | -| `shutdown` | Not supported on N9k, N30xx, N31xx | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. - -##### `domain` -vPC domain ID. Valid values are integer in the range 1-1000. There is no default value, this is a 'name' parameter. - -##### `auto_recovery` -Auto Recovery enable or disable if peer is non-operational. Valid values are true, false or default. This parameter is available only on Nexus 7000 series. Default value: true. - -##### `auto_recovery_reload_delay` -Delay (in secs) before peer is assumed dead before attempting to recover vPCs. Valid values are integers in the range 240..3600. Default value: 240. - -##### `delay_restore` -Delay (in secs) after peer link is restored to bring up vPCs. Valid values are integers in the range 1..3600. Default vlaue: 30. - -##### `delay_restore_interface_vlan` -Delay (in secs) after peer link is restored to bring up Interface VLANs or Interface BDs. Valid values are integers in the -range 1..3600. Default value: 10. - -##### `dual_active_exclude_interface_vlan_bridge_domain` -Interface VLANs or BDs to exclude from suspension when dual-active. Valid value is a string of integer ranges from 1..4095. There is no default value. - -##### `fabricpath_emulated_switch_id` -Configure a fabricpath switch_Id to enable vPC+ mode. This is also known as the Emulated switch-id. Valid values are integers in the range 1..4095. There is no default value. - -##### `fabricpath_multicast_load_balance` -In vPC+ mode, enable or disable the fabricpath multicast load balance. This loadbalances the Designated Forwarder selection for multicast traffic. Valid values are true, false or default - -##### `graceful_consistency_check` -Graceful conistency check . Valid values are true, false or default. Default value: true. - -##### `layer3_peer_routing` -Enable or Disable Layer3 peer routing. Valid values are true/false or default. Default value: false. - -##### `peer_keepalive_dest` -Destination IPV4 address of the peer where Peer Keep-alives are terminated. Valid values are IPV4 unicast address. There is no default value. - -##### `peer_keepalive_hold_timeout` -Peer keep-alive hold timeout in secs. Valid Values are integers in the range 3..10. Default value: 3. - -##### `peer_keepalive_interval` -Peer keep-alive interval in millisecs. Valid Values are integers in the range 400..10000. Default value: 1000. - -##### `peer_keepalive_interval_timeout` -Peer keep-alive interval timeout. Valid Values are integers in the range 3..20. Default value: 5. - -##### `peer_keepalive_precedence` -Peer keep-alive precedence. Valid Values are integers in the range 0..7. Default value: 6. - -##### `peer_keepalive_src` -Source IPV4 address of this switch where Peer Keep-alives are Sourced. Valid values are IPV4 unicast address. There is no default value. - -##### `peer_keepalive_udp_port` -Peer keep-alive udp port used for hellos. Valid Values are integers in the range 1024..65000. Default value: 3200. - -##### `peer_keepalive_vrf` -Peer keep-alive VRF. Valid Values are string. There is no default value. - -##### `peer_gateway` -Enable or Disable Layer3 forwarding for packets with peer gateway-mac. Valid values are true/false or default. Default: false. - -##### `peer_gateway_exclude_vlan` -Interface vlans to exclude from peer gateway functionality. Valid value is a string of integer ranges from 1..4095. This parameter is available only in Nexus 5000, Nexus 6000 and Nexus 7000 series. There is no default value. - -##### `port_channel_limit` -In vPC+ mode, enable or disable the port channel scale limit of -244 vPCs. Valid values are true, false or default - -##### `role_priority` -Priority to be used during vPC role selection of primary vs secondary. Valid values are integers in the range 1..65535. Default value: 32667. - -##### `self_isolation` -Enable or Disable self-isolation function for vPC. Valid values are true, false or default. This parameter is available only in Nexus 7000 series. Default value: false. - -##### `shutdown` -Whether or not the vPC domain is shutdown. This property is not avialable on Nexus 9000 and Nexus 3000 series. Default value: false. - -##### `system_mac` -vPC system mac. Valid values are in mac addresses format. There is no default value. - -##### `system_priority` -vPC system priority. Valid values are integers in the range 1..65535. Default value: 32667. - --- -### Type: cisco_vrf - -Manages Cisco Virtual Routing and Forwarding (VRF) configuration of a Cisco -device. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Caveats - -| Property | Caveat Description | -|------------------------------|----------------------------------| -| mhost_ipv4_default_interface | Only supported on IOS XR | -| mhost_ipv6_default_interface | Only supported on IOS XR | -| remote_route_filtering | Only supported on IOS XR | -| route_distinguisher | Only supported on N3k and N9k | -| shutdown | Only supported on N3k and N9k | -| vni | Only supported on N9k | -| vpn_id | Only supported on IOS XR | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid -values are 'present' and 'absent'. Default value is 'present'. - -##### `name` -Name of the VRF. Valid value is a string of non-whitespace characters. It is -not case-sensitive and overrides the title of the type. - -#### Properties - -##### `description` -Description of the VRF. Valid value is string. - -##### `mhost_ipv4_default_interface` -Specify multicast ipv4 host default interface. Valid value will be a valid interface or the keyword 'default'. - -##### `mhost_ipv6_default_interface` -Specify multicast ipv6 host default interface. Valid value will be a valid interface or the keyword 'default'. - -##### `remote_route_filtering` -Enable/disable remote route filtering. Valid value will be true, false or the keyword 'default'. - - -##### `route_distinguisher` -VPN Route Distinguisher (RD). The RD is combined with the IPv4 or IPv6 prefix learned by the PE router to create a globally unique address. Valid values are a String in one of the route-distinguisher formats (ASN2:NN, ASN4:NN, or IPV4:NN); the keyword 'auto', or the keyword 'default'. - -*Please note:* The `route_distinguisher` property is typically configured within the VRF context configuration on most platforms (including NXOS) but it is tightly coupled to bgp and therefore configured within the BGP configuration on some platforms (XR for example). For this reason the `route_distinguisher` property has support (with limitations) in both `cisco_vrf` and `cisco_bgp` providers: - -* `cisco_bgp`: The property is fully supported on both NXOS and XR. See: [cisco_bgp: route_distinguisher](#bgp_rd) -* `cisco_vrf`: The property is only supported on NXOS. - -*IMPORTANT: Choose only one provider to configure the `route_distinguisher` property on a given device. Using both providers simultaneously on the same device may have unpredictable results.* - -##### `shutdown` -Shutdown state of the VRF. Valid values are 'true', 'false', and 'default'. - -##### `vni` -Specify virtual network identifier. Valid values are Integer or keyword 'default'. - -##### `vpn_id` -Specify vpn_id. Valid values are <0-ffffff>:<0-ffffffff> or keyword 'default'. - --- -### Type: cisco_vrf_af - -Manages Cisco Virtual Routing and Forwarding (VRF) Address-Family configuration. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.2.0 | -| N6k | 7.3(0)N1(1) | 1.2.0 | -| N7k | 7.3(0)D1(1) | 1.2.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Caveats - -| Property | Caveat Description | -|-------------------------------|--------------------------------------| -| route_target_both_auto | Not supported on N3k, IOS XR | -| route_target_both_auto_evpn | Not supported on N3k, IOS XR | -| route_target_export_evpn | Not supported on N3k, IOS XR | -| route_target_export_stitching | Not supported on Nexus | -| route_target_import_evpn | Not supported on N3k, IOS XR | -| route_target_import_stitching | Not supported on Nexus | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid -values are 'present' and 'absent'. Default value is 'present'. - -##### `name` -Name of the VRF. Required. Valid value is a string of non-whitespace characters. It is -not case-sensitive and overrides the title of the type. - -##### `afi` -Address-Family Identifier (AFI). Required. Valid values are 'ipv4' or 'ipv6'. - -##### `safi` -Sub Address-Family Identifier (SAFI). Required. Valid values are `unicast` or `multicast`. -*`multicast` is not supported on some platforms.* - -#### Properties - -##### `route policy export` -Set route-policy(IOS XR) or map(nexus) export name. Valid value is string or keyword 'default'. - -##### `route policy import` -Set route-policy(IOS XR) or map(nexus) import name. Valid value is string or keyword 'default'. - -##### `route target both auto` -Enable/Disable the route-target 'auto' setting for both import and export target communities. Valid values are true, false, or 'default'. - -##### `route target both auto evpn` -(EVPN only) Enable/Disable the EVPN route-target 'auto' setting for both import and export target communities. Valid values are true, false, or 'default'. - -##### `route_target_import` -Sets the route-target import extended communities. Valid values are an Array or space-separated String of extended communities, or the keyword 'default'. - -route_target Examples: - -~~~puppet -route_target_import => ['1.2.3.4:5', '33:55'] -route_target_export => '4:4 66:66' -route_target_export_evpn => '5:5' -~~~ - -##### `route_target_import_evpn` -(EVPN only) Sets the route-target import extended communities for EVPN. Valid values are an Array or space-separated String of extended communities, or the keyword 'default'. - -##### `route_target_import_stitching` -(Stitching only) Sets the route-target import extended communities for stitching. Valid values are an Array or space-separated String of extended communities, or the keyword 'default'. - -##### `route_target_export` -Sets the route-target export extended communities. Valid values are an Array or space-separated String of extended communities, or the keyword 'default'. - -##### `route_target_export_evpn` -(EVPN only) Sets the route-target export extended communities for EVPN. Valid values are an Array or space-separated String of extended communities, or the keyword 'default'. - -##### `route_target_export_stitching` -(Stitching only) Sets the route-target export extended communities for stitching. Valid values are an Array or space-separated String of extended communities, or the keyword 'default'. - --- -### Type: cisco_vtp - -Manages the VTP (VLAN Trunking Protocol) configuration of a Cisco device. -There can only be one instance of the cisco_vtp. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.0.1 | -| N30xx | 7.0(3)I2(1) | 1.0.1 | -| N31xx | 7.0(3)I2(1) | 1.0.1 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid -values are 'present' and 'absent'. - -##### `name` -Instance of vtp, only allow the value 'default' - -##### `domain` -*Required.* VTP administrative domain. Valid value is a string. - -##### `version` -Version for the VTP domain. Valid values are an integer or the keyword 'default'. - -##### `file_name` -VTP file name. Valid values are a string or the keyword 'default'. - -##### `password` -Password for the VTP domain. Valid values are a string or the keyword 'default'. - --- -### Type: cisco_vxlan_vtep -Creates a VXLAN Network Virtualization Endpoint (NVE) overlay interface that terminates VXLAN tunnels. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | not applicable | not applicable | -| N31xx | not applicable | not applicable | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | not applicable | not applicable | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. - -##### `description` -Description of the NVE interface. Valid values are string, or keyword 'default'. - -##### `host_reachability` -Specify mechanism for host reachability advertisement. Valid values are 'evpn', 'flood' or keyword 'default'. - -##### `shutdown` -Administratively shutdown the NVE interface. Valid values are true, false or keyword 'default'. - -##### `source_interface` -Specify the loopback interface whose IP address should be used for the NVE interface. Valid values are string or keyword 'default'. - -##### `source_interface_hold_down_time` -Suppresses advertisement of the NVE loopback address until the overlay has converged. Valid values are Integer or keyword 'default'. - --- -### Type: cisco_vxlan_vtep_vni -Creates a Virtual Network Identifier member (VNI) for an NVE overlay interface. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | not applicable | not applicable | -| N31xx | not applicable | not applicable | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | not applicable | not applicable | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. - -##### `interface` -Name of the nve interface on the network element. Valid values are string. - -##### `vni` -ID of the Virtual Network Identifier. Valid values are integer. - -##### `assoc_vrf` -This attribute is used to identify and separate processing VNIs that are associated with a VRF and used for routing. The VRF and VNI specified with this command must match the configuration of the VNI under the VRF. Valid values are true or false. - -##### `ingress_replication` -Specifies mechanism for host reachability advertisement. Valid values are 'bgp', 'static', or 'default'. - -##### `multicast_group` -The multicast group (range) of the VNI. Valid values are string and keyword 'default'. - -##### `peer_list` -Set the ingress-replication static peer list. Valid values are an Array, a space-separated String of ip addresses, or the keyword 'default'. - -##### `suppress_arp` -Suppress arp under layer 2 VNI. Valid values are true, false, or 'default'. - --- -### NetDev StdLib Resource Type Details - -The following resources are listed alphabetically. - --- - -### Type: domain_name - -Configure the domain name of the device - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.1.0 | -| N30xx | 7.0(3)I2(1) | 1.1.0 | -| N31xx | 7.0(3)I2(1) | 1.1.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. - -##### `name` -Domain name of the device. Valid value is a string. - --- -### Type: name_server - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. - -##### `name` -Hostname or address of the DNS server. Valid value is a string. - --- -### Type: network_dns - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.1.0 | -| N30xx | 7.0(3)I2(1) | 1.1.0 | -| N31xx | 7.0(3)I2(1) | 1.1.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. - -##### `name` -Name, generally "settings", not used to manage the resource. Valid value is a string. - -##### `domain` -Default domain name to append to the device hostname. Valid value is a string. - -##### `search` -Array of DNS suffixes to search for FQDN entries. Valid value is an array of strings. - -##### `servers` -Array of DNS servers to use for name resolution. Valid value is an array of strings. - --- -### Type: `network_interface` - -Manages a puppet netdev_stdlib Network Interface. Any resource dependency should be run before the interface resource. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -###### `name` -Name of the interface on the network element. Valid value is a string. - -#### Properties - -###### `description` -Description of the interface. Valid values are a string or the keyword 'default'. - -###### `duplex` -Duplex of the interface. Valid values are 'full', and 'auto'. - -###### `speed` -Speed of the interface. Valid values are 100m, 1g, 10g, 40g, 100g, and 'auto'. - -##### `mtu` -Maximum Trasnmission Unit size for frames received and sent on the specified -interface. Valid value is an integer. - --- -### Type: network_snmp - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.1.0 | -| N30xx | 7.0(3)I2(1) | 1.1.0 | -| N31xx | 7.0(3)I2(1) | 1.1.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `name` -Name of the Puppet resource, not used to manage the device. Valid value is a string. - -##### `enable` -Enable or disable SNMP functionality. Valid values are 'true' or 'false'. - -##### `contact` -Contact name for this device. Valid value is a string. - -##### `location` -Location of this device. Valid value is a string. - --- -### Type: `network_trunk` - -Manages a puppet netdev_stdlib Network Trunk. It should be noted that while the NetDev stdlib has certain specified accepted parameters these may not be applicable to different network devices. For example, certain Cisco devices only use dot1q encapsulation, and therefore other values will cause errors. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -###### `name` -The switch interface name. Valid value is a string. - -#### Properties - -###### `encapsulation` -The vlan-tagging encapsulation protocol, usually dot1q. Valid values are 'dot1q', 'isl', 'negotiate' and 'none'. Cisco devices use dot1q encapsulation. - -###### `mode` -The L2 interface mode, enables or disables trunking. Valid values are 'access', 'trunk', 'dynamic_auto', and 'dynamic_desirable'. The mode on a Cisco device will always be 'trunk'. - -###### `untagged_vlan` -VLAN used for untagged VLAN traffic. a.k.a Native VLAN. Values must be in range of 1 to 4095. - -###### `tagged_vlans` -Array of VLAN names used for tagged packets. Values must be in range of 1 to 4095. - -###### `pruned_vlans` -Array of VLAN ID numbers used for VLAN pruning. Values must be in range of 1 to 4095. Cisco do not implement the concept of pruned vlans. - --- -### Type: network_vlan - -Manages a puppet netdev_stdlib Network Vlan. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. - -###### `id` -ID of the Virtual LAN. Valid value is a string. - -###### `shutdown` -Whether or not the vlan is shutdown. Valid values are 'true' or 'false'. - -###### `vlan_name` -The name of the VLAN. Valid value is a string. - --- -### Type: ntp_config - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.1.0 | -| N30xx | 7.0(3)I2(1) | 1.1.0 | -| N31xx | 7.0(3)I2(1) | 1.1.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Parameters - -##### `name` -Resource name, not used to configure the device. Valid value is a string. - -##### `source_interface` -Source interface for the NTP server. Valid value is a string. - --- -### Type: ntp_server - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.1.0 | -| N30xx | 7.0(3)I2(1) | 1.1.0 | -| N31xx | 7.0(3)I2(1) | 1.1.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. - -##### `name` -Hostname or IPv4/IPv6 address of the NTP server. Valid value is a string. - --- -### Type: port_channel - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. - -##### `id` -Channel group ID. eg 100. Valid value is an integer. - -##### `interfaces` -Array of Physical Interfaces that are part of the port channel. An array of valid interface names. - -##### `minimum_links` -Number of active links required for port channel to be up. Valid value is an integer. - -##### `name` -Name of the port channel. eg port-channel100. Valid value is a string. - --- -### Type: radius - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.1.0 | -| N30xx | 7.0(3)I2(1) | 1.1.0 | -| N31xx | 7.0(3)I2(1) | 1.1.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `name` -Resource name, not used to manage the device. Valid value is a string. - -##### `enable` -Enable or disable radius functionality. Valid values are 'true' or 'false'. - --- -### Type: radius_global - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.1.0 | -| N30xx | 7.0(3)I2(1) | 1.1.0 | -| N31xx | 7.0(3)I2(1) | 1.1.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Parameters - -##### `name` -Resource identifier, not used to manage the device. Valid value is a string. - -##### `timeout` -Number of seconds before the timeout period ends. Valid value is an integer. - -##### `retransmit_count` -Number of times to retransmit. Valid value is an integer. - -##### `key` -Encryption key (plaintext or in hash form depending on key_format). Valid value is a string. - -##### `key_format` -Encryption key format [0-7]. Valid value is an integer. - --- -### Type: radius_server - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.1.0 | -| N30xx | 7.0(3)I2(1) | 1.1.0 | -| N31xx | 7.0(3)I2(1) | 1.1.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Caveats - -| Property | Caveat Description | -|:--------|:-------------| -| `accouting_only` | Not supported in Cisco IOS XR | -| `authentication_only` | Not supported in Cisco IOS XR | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. - -##### `name` -IPv4/IPv6 address of the radius server. Valid value is a string. - -##### `auth_port` -Port number to use for authentication. Valid value is an integer. - -##### `acct_port` -Port number to use for accounting. Valid value is an integer. - -##### `timeout` -Number of seconds before the timeout period ends. Valid value is an integer. - -##### `retransmit_count` -Number of times to retransmit. Valid value is an integer. - -##### `accouting_only` -Enable this server for accounting only. Valid values are 'true' or 'false'. Not supported on IOS XR. - -##### `authentication_only` -Enable this server for authentication only. Valid values are 'true' or 'false'. Not supported on IOS XR. - -##### `key` -Encryption key (plaintext or in hash form depending on key_format). Valid value is a string. - -##### `key_format` -Encryption key format [0-7]. Valid value is an integer. - --- -### Type: radius_server_group - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Parameters - -##### `servers` -Array of servers associated with this group. - --- -### Type: search_domain - -Configure the search domain of the device. Note that this type is functionally equivalent to the netdev_stdlib domain_name type. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. - -##### `name` -Search domain of the device. Valid value is a string. - -- -### Type: snmp_community - -Manages an SNMP community on a Cisco SNMP server. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `ensure` -Determine whether the config should be present or not on the device. Valid -values are 'present' and 'absent'. - -##### `group` -Group that the SNMP community belongs to. Valid values are a string or the -keyword 'default'. - -##### `acl` -Assigns an Access Control List (ACL) to an SNMP community to filter SNMP -requests. Valid values are a string or the keyword 'default'. - --- -### Type: snmp_notification - -Manages an SNMP notification on a Cisco SNMP server. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `enable` -Determine whether the trap should be on or off. Valid -values are true and false. - --- -### Type: snmp_notification_receiver - -Manages an SNMP notification receiver on an cisco SNMP server. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `ensure` -Determines whether the config should be present or not on the device. Valid -values are 'present', and 'absent'. - -##### `name` -IP address of the SNMP user. Valid value is a string. - -##### `port` -SNMP UDP port number - -##### `username` -Username to use for SNMPv3 privacy and authentication. This is the community string for SNMPv1 and v2. - -##### `version` -SNMP version [v1|v2|v3] - -##### `type` -The type of receiver [traps|informs]. - -##### `security` -SNMPv3 security mode [auto|noauth|priv]. - -##### `vrf` -Interface to send SNMP data from, e.g. "management" - -##### `source_interface` -Source interface to send SNMP data from, e.g. "ethernet 2/1". - --- -### Type: snmp_user - -Manages an SNMP user on an cisco SNMP server. - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `ensure` -Determines whether the config should be present or not on the device. Valid -values are 'present', and 'absent'. - -##### `name` -Name of the SNMP user. Valid value is a string. - -##### `engine_id` -Engine ID of the SNMP user. Valid values are empty string or 5 to 32 octets -seprated by colon. - -##### `roles` -Groups that the SNMP user belongs to. Valid value is a string. - -##### `auth` -Authentication protocol for the SNMP user. Valid values are 'md5' and 'sha'. - -##### `password` -Authentication password for the SNMP user. Valid value is string. - -##### `privacy` -Privacy protocol for the SNMP user. Valid values are 'aes128' and 'des'. - -##### `private_key` -Privacy password for SNMP user. Valid value is a string. - -##### `localized_key` -Specifies whether the passwords specified in manifest are in localized key -format (in case of true) or cleartext (in case of false). Valid values are 'true', and 'false'. - --- -### Type: syslog_server - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.1.0 | -| N30xx | 7.0(3)I2(1) | 1.1.0 | -| N31xx | 7.0(3)I2(1) | 1.1.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Parameters - -##### `ensure` -Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. - -##### `name` -Hostname or IPv4/IPv6 address of the Syslog server. Valid value is a string. - -##### `serverity_level` -Syslog severity level to log. Valid value is an integer. - -##### `vrf` -Interface to send syslog data from, e.g. "management". Valid value is a string. - --- -### Type: syslog_setting - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.1.0 | -| N30xx | 7.0(3)I2(1) | 1.1.0 | -| N31xx | 7.0(3)I2(1) | 1.1.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `name` -Hostname or address of the Syslog server. Valid value is a string. - -##### `time_stamp_units` -The unit of measurement for log time values. Valid values are 'seconds' and 'milliseconds'. - --- -### Type: tacacs - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `enable` -Enable or disable radius functionality [true|false] - --- -### Type: tacacs_global - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | unsupported | unsupported | - -#### Parameters - -##### `enable` -Enable or disable radius functionality [true|false] - -##### `key` -Encryption key (plaintext or in hash form depending on key_format) - -##### `key_format` -Encryption key format [0-7] - -##### `timeout` -Number of seconds before the timeout period ends - --- -### Type: tacacs_server - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -##### `ensure` -Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. - -##### `key` -Encryption key (plaintext or in hash form depending on key_format) - -##### `key_format` -Encryption key format [0-7] - -##### `name` -Hostname or IPv4/IPv6 address of the Syslog server. Valid value is a string. - -##### `port` -The port of the tacacs server. - -##### `timeout` -Number of seconds before the timeout period ends - --- -### Type: tacacs_server_group - -| Platform | OS Minimum Version | Module Minimum Version | -|----------|:------------------:|:----------------------:| -| N9k | 7.0(3)I2(1) | 1.2.0 | -| N30xx | 7.0(3)I2(1) | 1.2.0 | -| N31xx | 7.0(3)I2(1) | 1.2.0 | -| N56xx | 7.3(0)N1(1) | 1.3.0 | -| N6k | 7.3(0)N1(1) | 1.3.0 | -| N7k | 7.3(0)D1(1) | 1.3.0 | -| N8k | 7.0(3)F1(1) | 1.3.0 | -| IOS XR | TODO | TODO | - -#### Parameters - -##### `servers` -Array of servers associated with this group. - -## Limitations - -Minimum Requirements: -* Cisco NX-OS: - * Open source Puppet version 4.0+ or Puppet Enterprise 2015.2+ - * Cisco Nexus 31xx, OS Version 7.0(3)I2(1), Environments: Bash-shell, Guestshell - * Cisco Nexus 30xx, OS Version 7.0(3)I2(1), Environments: Bash-shell, Guestshell - * Cisco Nexus 85xx, OS Version 7.0(3)F1(1), Environments: Bash-shell, Guestshell - * Cisco Nexus 95xx, OS Version 7.0(3)I2(1), Environments: Bash-shell, Guestshell - * Cisco Nexus 93xx, OS Version 7.0(3)I2(1), Environments: Bash-shell, Guestshell - * Cisco Nexus 56xx, OS Version 7.3(0)N1(1), Environments: Open Agent Container (OAC) - * Cisco Nexus 60xx, OS Version 7.3(0)N1(1), Environments: Open Agent Container (OAC) - * Cisco Nexus 7xxx, OS Version 7.3(0)D1(1), Environments: Open Agent Container (OAC) -* Cisco IOS XR: - * Open source Puppet version 4.3.2+ or Puppet Enterprise 2015.3.2+ - * Cisco IOS XRv 9000, OS Version TODO, Environments: native (Bash-shell) - * Cisco Network Convergence System (NCS) 55xx, OS Version TODO, Environments: native (Bash-shell) - -## Cisco OS Differences - -There are some differences between NX-OS and IOS-XR as described below: - -* Route-Map vs Route-Policy - * Nexus uses route-maps in some commands, this is a string reference to a route-map defined elsewhere in the configuration. - * XR uses route-policies instead. Similar to Nexus, this is a string reference to a route-policy defined elsewhere. Under XR, a policy must be defined before it is referenced. - -## Learning Resources - -* Puppet - * [https://learn.puppetlabs.com/](https://learn.puppetlabs.com/) - * [https://en.wikipedia.org/wiki/Puppet_(software)](https://en.wikipedia.org/wiki/Puppet_(software)) -* Markdown (for editing documentation) - * [https://help.github.com/articles/markdown-basics/](https://help.github.com/articles/markdown-basics/) -* Ruby - * [https://en.wikipedia.org/wiki/Ruby_(programming_language)](https://en.wikipedia.org/wiki/Ruby_(programming_language)) - * [https://www.codecademy.com/tracks/ruby](https://www.codecademy.com/tracks/ruby) - * [https://rubymonk.com/](https://rubymonk.com/) - * [https://www.codeschool.com/paths/ruby](https://www.codeschool.com/paths/ruby) -* Ruby Gems - * [http://guides.rubygems.org/](http://guides.rubygems.org/) - * [https://en.wikipedia.org/wiki/RubyGems](https://en.wikipedia.org/wiki/RubyGems) -* YAML - * [https://en.wikipedia.org/wiki/YAML](https://en.wikipedia.org/wiki/YAML) - * [http://www.yaml.org/start.html](http://www.yaml.org/start.html) -* Yum - * [https://en.wikipedia.org/wiki/Yellowdog_Updater,_Modified](https://en.wikipedia.org/wiki/Yellowdog_Updater,_Modified) - * [https://www.centos.org/docs/5/html/yum/](https://www.centos.org/docs/5/html/yum/) - * [http://www.linuxcommand.org/man_pages](http://www.linuxcommand.org/man_pages/yum8.html) - -## License - -~~~text -Copyright (c) 2014-2016 Cisco and/or its affiliates. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -~~~ diff --git a/README.md b/README.md index dca2ae47d..6217e4088 100644 --- a/README.md +++ b/README.md @@ -493,7 +493,7 @@ Allows execution of configuration commands. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -525,7 +525,7 @@ Manages AAA Authentication Login configuration. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -559,7 +559,7 @@ Manages configuration for Authorization Login Config Service. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -587,7 +587,7 @@ Manages configuration for Authorization Login Exec Service. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -615,7 +615,7 @@ Manages configuration for a TACACS+ server group. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -951,7 +951,7 @@ Manages configuration of a BFD (Bidirectional Forwarding Detection) instance. | N5k | 7.3(0)N1(1) | 1.4.0 | | N6k | 7.3(0)N1(1) | 1.4.0 | | N7k | 7.3(0)D1(1) | 1.4.0 | -| N9k-F | 7.3(0)F1(1) | 1.4.0 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Caveats @@ -1205,7 +1205,7 @@ Manages configuration of a BGP Address-family instance. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Caveats @@ -1385,7 +1385,7 @@ Manages configuration of a BGP Neighbor. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.3(0)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Caveats @@ -1487,7 +1487,7 @@ Manages configuration of a BGP Neighbor Address-family instance. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Caveats @@ -1668,7 +1668,7 @@ Manages configuration of a DHCP relay global configuration. | N5k | 7.3(0)N1(1) | 1.4.0 | | N6k | 7.3(0)N1(1) | 1.4.0 | | N7k | 7.3(0)D1(1) | 1.4.0 | -| N9k-F | 7.3(0)F1(1) | 1.4.0 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Caveats @@ -1766,7 +1766,7 @@ Manages Cisco Ethernet Virtual Private Network (EVPN) VXLAN Network Identifier ( | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Caveats @@ -1951,7 +1951,7 @@ Manages a Cisco Network Interface. Any resource dependency should be run before | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Caveats @@ -2294,7 +2294,7 @@ Manages a Cisco Network Interface Channel-group. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -2363,7 +2363,7 @@ Manages configuration of an OSPF interface instance. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.3(0)F1(1) | 1.2.0 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -2673,7 +2673,7 @@ Manages configuration of an ospf instance. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -2696,7 +2696,7 @@ Manages an area for an OSPF router. | N5k | 7.3(0)N1(1) | 1.4.0 | | N6k | 7.3(0)N1(1) | 1.4.0 | | N7k | 7.3(0)D1(1) | 1.4.0 | -| N9k-F | 7.3(0)F1(1) | 1.4.0 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Example Usage @@ -2804,7 +2804,7 @@ Manages an area virtual link for an OSPF router. | N5k | 7.3(0)N1(1) | 1.4.0 | | N6k | 7.3(0)N1(1) | 1.4.0 | | N7k | 7.3(0)D1(1) | 1.4.0 | -| N9k-F | 7.3(0)F1(1) | 1.4.0 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Example Usage @@ -2885,7 +2885,7 @@ Manages a VRF for an OSPF router. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -2954,7 +2954,7 @@ Also configures anycast gateway MAC of the switch. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -2987,7 +2987,7 @@ Manages configuration of an Protocol Independent Multicast (PIM) instance. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -3013,7 +3013,7 @@ Manages configuration of an Protocol Independent Multicast (PIM) static route pr | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -3040,7 +3040,7 @@ Manages configuration of an Protocol Independent Multicast (PIM) static route pr | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -3064,7 +3064,7 @@ Manages configuration of a portchannel global parameters | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Caveats @@ -3236,7 +3236,7 @@ Manages an SNMP community on a Cisco SNMP server. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -3270,7 +3270,7 @@ of group; thus this provider utility does not create snmp groups and only report | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -3293,7 +3293,7 @@ cisco_snmp_server. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -3338,7 +3338,7 @@ Manages an SNMP user on an cisco SNMP server. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -3387,7 +3387,7 @@ instance of the cisco_tacacs_server. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -3430,7 +3430,7 @@ Configures Cisco TACACS+ server hosts. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -3558,7 +3558,7 @@ Manages the virtual Port Channel (vPC) domain configuration of a Cisco device. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Caveats @@ -3671,7 +3671,7 @@ device. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Caveats @@ -3741,7 +3741,7 @@ Manages Cisco Virtual Routing and Forwarding (VRF) Address-Family configuration. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Caveats @@ -3824,7 +3824,7 @@ There can only be one instance of the cisco_vtp. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -3858,7 +3858,7 @@ Creates a VXLAN Network Virtualization Endpoint (NVE) overlay interface that ter | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -3891,7 +3891,7 @@ Creates a Virtual Network Identifier member (VNI) for an NVE overlay interface. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -3937,7 +3937,7 @@ Configure the domain name of the device | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -3957,7 +3957,7 @@ Domain name of the device. Valid value is a string. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -3977,7 +3977,7 @@ Hostname or address of the DNS server. Valid value is a string. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4008,7 +4008,7 @@ Manages a puppet netdev_stdlib Network Interface. Any resource dependency should | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4040,7 +4040,7 @@ interface. Valid value is an integer. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4068,7 +4068,7 @@ Manages a puppet netdev_stdlib Network Trunk. It should be noted that while the | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4104,7 +4104,7 @@ Manages a puppet netdev_stdlib Network Vlan. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4130,7 +4130,7 @@ The name of the VLAN. Valid value is a string. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4150,7 +4150,7 @@ Source interface for the NTP server. Valid value is a string. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4170,7 +4170,7 @@ Hostname or IPv4/IPv6 address of the NTP server. Valid value is a string. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4199,7 +4199,7 @@ Name of the port channel. eg port-channel100. Valid value is a string. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4219,7 +4219,7 @@ Enable or disable radius functionality. Valid values are 'true' or 'false'. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4296,7 +4296,7 @@ Encryption key format [0-7]. Valid value is an integer. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4315,7 +4315,7 @@ Configure the search domain of the device. Note that this type is functionally e | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4337,7 +4337,7 @@ Manages an SNMP community on a Cisco SNMP server. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4365,7 +4365,7 @@ Manages an SNMP notification on a Cisco SNMP server. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4385,7 +4385,7 @@ Manages an SNMP notification receiver on an cisco SNMP server. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4429,7 +4429,7 @@ Manages an SNMP user on an cisco SNMP server. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4473,7 +4473,7 @@ format (in case of true) or cleartext (in case of false). Valid values are 'true | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4499,7 +4499,7 @@ Interface to send syslog data from, e.g. "management". Valid value is a string. | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4519,7 +4519,7 @@ The unit of measurement for log time values. Valid values are 'seconds' and 'mi | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4536,7 +4536,7 @@ Enable or disable radius functionality [true|false] | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters @@ -4562,7 +4562,7 @@ Number of seconds before the timeout period ends | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | ##### `ensure` Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. @@ -4592,7 +4592,7 @@ Number of seconds before the timeout period ends | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | -| N9k-F | 7.0(3)F1(1) | 1.3.2 | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | #### Parameters diff --git a/examples/cisco/demo_acl.pp b/examples/cisco/demo_acl.pp index 21a589e72..3ee69a59d 100644 --- a/examples/cisco/demo_acl.pp +++ b/examples/cisco/demo_acl.pp @@ -17,8 +17,8 @@ class ciscopuppet::cisco::demo_acl { $fragments = platform_get() ? { - /(n3k|n7k|n8k|n9k)/ => 'permit', - default => undef + /(n3k|n7k|n9k-f|n9k)/ => 'permit', + default => undef } cisco_acl { 'ipv4 my_ipv4_acl': @@ -38,33 +38,33 @@ } $http_method = platform_get() ? { - /(n3k|n8k|n9k)/ => 'post', - default => undef + /(n3k|n9k-f|n9k)/ => 'post', + default => undef } $packet_length = platform_get() ? { - /(n3k|n7k|n8k|n9k)/ => 'range 80 1000', - default => undef + /(n3k|n7k|n9k-f|n9k)/ => 'range 80 1000', + default => undef } $redirect = platform_get() ? { - /(n3k|n8k|n9k)/ => 'Ethernet1/1,Ethernet1/2,port-channel1', - default => undef + /(n3k|n9k-f|n9k)/ => 'Ethernet1/1,Ethernet1/2,port-channel1', + default => undef } $tcp_option_length = platform_get() ? { - /(n3k|n8k|n9k)/ => '20', - default => undef + /(n3k|n9k-f|n9k)/ => '20', + default => undef } $time_range = platform_get() ? { - /(n3k|n7k|n8k|n9k)/ => 'my_range', - default => undef + /(n3k|n7k|n9k-f|n9k)/ => 'my_range', + default => undef } $ttl = platform_get() ? { - /(n3k|n8k|n9k)/ => '153', - default => undef + /(n3k|n9k-f|n9k)/ => '153', + default => undef } cisco_ace { 'ipv4 my_ipv4_acl 20': diff --git a/examples/cisco/demo_bfd.pp b/examples/cisco/demo_bfd.pp index a57ba85c5..bcbdcd1e2 100644 --- a/examples/cisco/demo_bfd.pp +++ b/examples/cisco/demo_bfd.pp @@ -17,7 +17,7 @@ class ciscopuppet::cisco::demo_bfd { $echo_rx_interval = platform_get() ? { - /(n3k|n7k|n8k|n9k)/ => 300, + /(n3k|n7k|n9k-f|n9k)/ => 300, default => undef } @@ -36,44 +36,44 @@ default => undef } - # TBD: this is due to a bug on n8k and n9k + # TBD: this is due to a bug on n9k-f and n9k $interval = platform_get() ? { /(n3k|n5k|n6k|n7k)/ => ['100', '100', '25'], default => undef } $ipv4_echo_rx_interval = platform_get() ? { - /(n3k|n7k|n8k|n9k)/ => 100, + /(n3k|n7k|n9k-f|n9k)/ => 100, default => undef } $ipv4_interval = platform_get() ? { - /(n3k|n7k|n8k|n9k)/ => ['200', '200', '50'], + /(n3k|n7k|n9k-f|n9k)/ => ['200', '200', '50'], default => undef } $ipv4_slow_timer = platform_get() ? { - /(n3k|n7k|n8k|n9k)/ => 10000, + /(n3k|n7k|n9k-f|n9k)/ => 10000, default => undef } $ipv6_echo_rx_interval = platform_get() ? { - /(n3k|n7k|n8k|n9k)/ => 200, + /(n3k|n7k|n9k-f|n9k)/ => 200, default => undef } $ipv6_interval = platform_get() ? { - /(n3k|n7k|n8k|n9k)/ => ['500', '500', '30'], + /(n3k|n7k|n9k-f|n9k)/ => ['500', '500', '30'], default => undef } $ipv6_slow_timer = platform_get() ? { - /(n3k|n7k|n8k|n9k)/ => 25000, + /(n3k|n7k|n9k-f|n9k)/ => 25000, default => undef } $startup_timer = platform_get() ? { - /(n3k|n8k|n9k)/ => 25, + /(n3k|n9k-f|n9k)/ => 25, default => undef } diff --git a/examples/cisco/demo_dhcp_relay_global.pp b/examples/cisco/demo_dhcp_relay_global.pp index a8b6b6766..3e26e6e4a 100644 --- a/examples/cisco/demo_dhcp_relay_global.pp +++ b/examples/cisco/demo_dhcp_relay_global.pp @@ -17,12 +17,12 @@ class ciscopuppet::cisco::demo_dhcp_relay_global { $ipv4_information_option_trust = platform_get() ? { - /(n3k|n7k|n8k|n9k)/ => true, + /(n3k|n7k|n9k-f|n9k)/ => true, default => undef } $ipv4_information_trust_all = platform_get() ? { - /(n3k|n7k|n8k|n9k)/ => true, + /(n3k|n7k|n9k-f|n9k)/ => true, default => undef } @@ -42,7 +42,7 @@ } $ipv6_option_cisco = platform_get() ? { - /(n3k|n7k|n8k|n9k)/ => true, + /(n3k|n7k|n9k-f|n9k)/ => true, default => undef } diff --git a/examples/cisco/demo_hsrp.pp b/examples/cisco/demo_hsrp.pp index 8747c65a7..58fa5d8ce 100644 --- a/examples/cisco/demo_hsrp.pp +++ b/examples/cisco/demo_hsrp.pp @@ -17,7 +17,7 @@ class ciscopuppet::cisco::demo_hsrp { $bfd_all_intf = platform_get() ? { - /(n5k|n6k|n7k|n8k|n9k)/ => true, + /(n5k|n6k|n7k|n9k-f|n9k)/ => true, default => undef } diff --git a/examples/cisco/demo_interface.pp b/examples/cisco/demo_interface.pp index 77e740140..2b0b3a260 100755 --- a/examples/cisco/demo_interface.pp +++ b/examples/cisco/demo_interface.pp @@ -37,7 +37,7 @@ } $ipv4_dhcp_relay_info_trust = platform_get() ? { - /(n3k|n7k|n8k|n9k)/ => true, + /(n3k|n7k|n9k-f|n9k)/ => true, default => undef } @@ -95,12 +95,12 @@ ipv6_dhcp_relay_src_intf => 'ethernet 2/2', } $storm_control_broadcast = platform_get() ? { - /(n3k|n5k|n6k|n8k|n9k)/ => '77.77', + /(n3k|n5k|n6k|n9k-f|n9k)/ => '77.77', default => undef } $storm_control_multicast = platform_get() ? { - /(n3k|n5k|n6k|n8k|n9k)/ => '22.22', + /(n3k|n5k|n6k|n9k-f|n9k)/ => '22.22', default => undef } diff --git a/examples/cisco/demo_portchannel.pp b/examples/cisco/demo_portchannel.pp index 76fe86a75..3b65bc927 100644 --- a/examples/cisco/demo_portchannel.pp +++ b/examples/cisco/demo_portchannel.pp @@ -42,12 +42,12 @@ } $port_hash_distribution = platform_get() ? { - /(n3k|n7k|n8k|n9k)/ => 'adaptive', + /(n3k|n7k|n9k-f|n9k)/ => 'adaptive', default => undef } $port_load_defer = platform_get() ? { - /(n3k|n7k|n8k|n9k)/ => true, + /(n3k|n7k|n9k-f|n9k)/ => true, default => undef } @@ -57,7 +57,7 @@ } $rotate = platform_get() ? { - /(n7k|n8k|n9k)/ => '4', + /(n7k|n9k-f|n9k)/ => '4', default => undef } diff --git a/lib/puppet/parser/functions/platform_fretta.rb b/lib/puppet/parser/functions/platform_fretta.rb new file mode 100644 index 000000000..9ffa337cc --- /dev/null +++ b/lib/puppet/parser/functions/platform_fretta.rb @@ -0,0 +1,36 @@ +# +# Cisco platform_fretta puppet manifest function. +# +# November 2016, Michael G Wiebe +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module Puppet + module Parser + # Function platform_fretta. + # Searches facter os.release.full and returns true if the + # version matches the fretta image version. + # + module Functions + newfunction(:platform_fretta, type: :rvalue) do |_args| + data = lookupvar('os') + return '' if data.nil? + + pat = '7.0\(3\)F' + data['release']['full'].match(pat) ? true : false + end + end + end +end diff --git a/lib/puppet/parser/functions/platform_get.rb b/lib/puppet/parser/functions/platform_get.rb index 9bb6702f2..b0076978b 100644 --- a/lib/puppet/parser/functions/platform_get.rb +++ b/lib/puppet/parser/functions/platform_get.rb @@ -39,14 +39,12 @@ module Functions cisco_hardware = 'n6k' when /Nexus\s?7\d\d\d/ cisco_hardware = 'n7k' - when /Nexus\s?8\d\d\d/ - cisco_hardware = 'n8k' - when /NX-OSv8K/ - cisco_hardware = 'n8k' - when /Nexus\s?9\d\d\d/ - cisco_hardware = 'n9k' - when /NX-OSv Chassis/ - cisco_hardware = 'n9k' + when /(Nexus\s?9\d\d\d|NX-OSv Chassis)/ + if function_platform_fretta([]) + cisco_hardware = 'n9k-f' + else + cisco_hardware = 'n9k' + end else fail Puppet::ParseError, "Unrecognized platform type: #{pi}\n#{__FILE__}" end diff --git a/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb b/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb index a61ab521d..e9495129b 100644 --- a/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb +++ b/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb @@ -118,7 +118,7 @@ def unsupported_properties(_tests, _id) unprops = [] - if platform[/n(3|8|9)k/] + if platform[/n(3|9)k/] unprops << :fabricpath_interval << :fabricpath_slow_timer << @@ -140,8 +140,8 @@ def unsupported_properties(_tests, _id) :startup_timer end - # TBD: this is due to nxos bug on n8k and n9k - unprops << :interval if platform[/n(8|9)k/] + # TBD: this is due to nxos bug on n9k-f and n9k + unprops << :interval if platform[/n9k/] unprops end diff --git a/tests/beaker_tests/cisco_bgp/test_bgp.rb b/tests/beaker_tests/cisco_bgp/test_bgp.rb index 322fafba5..02d778840 100644 --- a/tests/beaker_tests/cisco_bgp/test_bgp.rb +++ b/tests/beaker_tests/cisco_bgp/test_bgp.rb @@ -116,7 +116,7 @@ } tests[:default][:resource].merge!(resource[:legacy]) if - nexus_image[/I2|I3|I4/] || platform[/n5|n6|n7|n8/] + nexus_image[/I2|I3|I4/] || platform[/n5|n6|n7|n9k-f/] # Non-default Tests. NOTE: [:resource] = [:manifest_props] for all non-default tests[:non_default] = { @@ -228,7 +228,7 @@ def unsupported_properties(tests, id) unprops << :event_history_errors << - :event_history_objstore if nexus_image[/I2|I3|I4/] || platform[/n5|n6|n7|n8/] + :event_history_objstore if nexus_image[/I2|I3|I4/] || platform[/n5|n6|n7|n9k-f/] if vrf != 'default' # NX-OS does not support these properties under a non-default vrf diff --git a/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb b/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb index 2799b3067..de67a733a 100644 --- a/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb +++ b/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb @@ -185,7 +185,7 @@ tests[:l2vpn_evpn] = { desc: '3.1 Address-family l2evpn/evpn', operating_system: 'nexus', - platform: 'n(5|6|7|8|9)k', + platform: 'n(5|6|7|9)k', title_pattern: '2 default l2vpn evpn', resource: { 'ensure' => 'present' }, } @@ -268,7 +268,7 @@ def unsupported_properties(tests, id) unprops << :advertise_l2vpn_evpn if vrf == 'default' || platform[/n(3|6)k/] - unprops << :additional_paths_install if platform[/n(3|8|9)k/] + unprops << :additional_paths_install if platform[/n(3|9)k/] end unprops end diff --git a/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb b/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb index 45a24ec78..aac0514bf 100644 --- a/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb +++ b/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb @@ -133,7 +133,7 @@ def unsupported_properties(_tests, _id) unprops << :ipv4_sub_option_circuit_id_custom << :ipv4_sub_option_circuit_id_string - elsif platform[/n8k/] + elsif platform[/n9k-f/] unprops << :ipv4_src_addr_hsrp << :ipv4_sub_option_circuit_id_custom << diff --git a/tests/beaker_tests/cisco_evpn_vni/test_evpn_vni.rb b/tests/beaker_tests/cisco_evpn_vni/test_evpn_vni.rb index bb9e65164..871371dff 100644 --- a/tests/beaker_tests/cisco_evpn_vni/test_evpn_vni.rb +++ b/tests/beaker_tests/cisco_evpn_vni/test_evpn_vni.rb @@ -27,7 +27,7 @@ tests = { agent: agent, master: master, - platform: 'n(5|6|7|8|9)k', + platform: 'n(5|6|7|9)k', resource_name: 'cisco_evpn_vni', } diff --git a/tests/beaker_tests/cisco_interface/test_interface_L3.rb b/tests/beaker_tests/cisco_interface/test_interface_L3.rb index 90f00f078..a04a63298 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_L3.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_L3.rb @@ -173,7 +173,7 @@ def unsupported_properties(_tests, id) :switchport_mode end - if platform[/n(3|8|9)k/] + if platform[/n(3|9)k/] unprops << :ipv4_dhcp_relay_src_addr_hsrp elsif platform[/n(5|6)k/] diff --git a/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb b/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb index 079505727..99edba0eb 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb @@ -28,12 +28,14 @@ require File.expand_path('../interfacelib.rb', __FILE__) # Test hash top-level keys +# The platform: key below must use an end of string anchor '$' in order to +# distinguish between 'n9k' and 'n9k-f' platform flavors. tests = { agent: agent, master: master, intf_type: 'ethernet', operating_system: 'nexus', - platform: 'n(3|5|6|7|9)k', + platform: 'n(3|5|6|7|9)k$', resource_name: 'cisco_interface', } diff --git a/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb b/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb index 4c23b53ce..10b08633e 100755 --- a/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb +++ b/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb @@ -74,7 +74,7 @@ tests[:default_sym] = { desc: '1.2 Default Properties (sym)', title_pattern: intf, - platform: 'n(3|8|9)k', + platform: 'n(3|9)k', code: [0, 2], manifest_props: { bfd_per_link: 'default', @@ -99,7 +99,7 @@ tests[:non_default_sym] = { desc: '2.2 Non Default Properties (sym)', title_pattern: intf, - platform: 'n(3|8|9)k', + platform: 'n(3|9)k', manifest_props: { bfd_per_link: 'true', lacp_graceful_convergence: 'false', diff --git a/tests/beaker_tests/cisco_itd_device_group/test_itd_device_group.rb b/tests/beaker_tests/cisco_itd_device_group/test_itd_device_group.rb index 8471b15f9..865fa0bf0 100644 --- a/tests/beaker_tests/cisco_itd_device_group/test_itd_device_group.rb +++ b/tests/beaker_tests/cisco_itd_device_group/test_itd_device_group.rb @@ -24,10 +24,12 @@ require File.expand_path('../../lib/utilitylib.rb', __FILE__) # Test hash top-level keys +# The platform: key below must use an end of string anchor '$' in order to +# distinguish between 'n9k' and 'n9k-f' platform flavors. tests = { master: master, agent: agent, - platform: 'n(7|9)k', + platform: 'n(7|9)k$', resource_name: 'cisco_itd_device_group', } diff --git a/tests/beaker_tests/cisco_itd_device_group_node/test_itd_device_group_node.rb b/tests/beaker_tests/cisco_itd_device_group_node/test_itd_device_group_node.rb index 41b70d07a..6b4c8457e 100644 --- a/tests/beaker_tests/cisco_itd_device_group_node/test_itd_device_group_node.rb +++ b/tests/beaker_tests/cisco_itd_device_group_node/test_itd_device_group_node.rb @@ -25,10 +25,12 @@ require File.expand_path('../../lib/utilitylib.rb', __FILE__) # Test hash top-level keys +# The platform: key below must use an end of string anchor '$' in order to +# distinguish between 'n9k' and 'n9k-f' platform flavors. tests = { master: master, agent: agent, - platform: 'n(7|9)k', + platform: 'n(7|9)k$', resource_name: 'cisco_itd_device_group_node', } diff --git a/tests/beaker_tests/cisco_itd_service/test_itd_service.rb b/tests/beaker_tests/cisco_itd_service/test_itd_service.rb index f1d11f4be..1cac6cad4 100644 --- a/tests/beaker_tests/cisco_itd_service/test_itd_service.rb +++ b/tests/beaker_tests/cisco_itd_service/test_itd_service.rb @@ -25,11 +25,13 @@ require File.expand_path('../../lib/utilitylib.rb', __FILE__) # Test hash top-level keys +# The platform: key below must use an end of string anchor '$' in order to +# distinguish between 'n9k' and 'n9k-f' platform flavors. tests = { master: master, agent: agent, intf_type: 'ethernet', - platform: 'n(7|9)k', + platform: 'n(7|9)k$', resource_name: 'cisco_itd_service', } diff --git a/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb b/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb index 9098cf65e..e1aeb9f4e 100755 --- a/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb +++ b/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb @@ -26,7 +26,7 @@ tests = { agent: agent, master: master, - platform: 'n(5|6|7|8|9)k', + platform: 'n(5|6|7|9)k', resource_name: 'cisco_overlay_global', ensurable: false, } diff --git a/tests/beaker_tests/cisco_portchannel_global/test_portchannel_global.rb b/tests/beaker_tests/cisco_portchannel_global/test_portchannel_global.rb index 91fd397be..79d49233a 100755 --- a/tests/beaker_tests/cisco_portchannel_global/test_portchannel_global.rb +++ b/tests/beaker_tests/cisco_portchannel_global/test_portchannel_global.rb @@ -64,16 +64,16 @@ # Per platform default values resource = { - n3k: { + n3k: { resilient: 'true' }, - n89k: { + n9k: { bundle_hash: 'ip-l4port' }, } tests[:default][:resource].merge!(resource[:n3k]) if platform[/n3k/] -tests[:default][:resource].merge!(resource[:n89k]) if platform[/n(8|9)k/] +tests[:default][:resource].merge!(resource[:n9k]) if platform[/n9k/] tests[:non_default] = { desc: '2.1 Non Defaults', @@ -102,7 +102,7 @@ n56k: { bundle_hash: 'mac' }, - n8k: { + n9kf: { bundle_hash: 'ip' }, n9k: { @@ -113,8 +113,8 @@ tests[:non_default][:manifest_props].merge!(manifest_non[:n3k]) if platform[/n3k/] tests[:non_default][:manifest_props].merge!(manifest_non[:n56k]) if platform[/n(5|6)k/] -tests[:non_default][:manifest_props].merge!(manifest_non[:n8k]) if platform[/n8k/] -tests[:non_default][:manifest_props].merge!(manifest_non[:n9k]) if platform[/n9k/] +tests[:non_default][:manifest_props].merge!(manifest_non[:n9kf]) if platform[/n9k-f/] +tests[:non_default][:manifest_props].merge!(manifest_non[:n9k]) if platform[/n9k$/] def unsupported_properties(tests, _id) unprops = [] @@ -141,7 +141,7 @@ def unsupported_properties(tests, _id) :hash_poly << :load_defer << :rotate - elsif platform[/n8k/] + elsif platform[/n9k-f/] unprops << :asymmetric << :concatenation << @@ -253,7 +253,7 @@ def unsupported_properties(tests, _id) mhash[:bundle_hash] = rhash[:bundle_hash] = 'ip-gre' test_harness_run(tests, id) - elsif device == 'n8k' + elsif device == 'n9k-f' tests[id][:desc] = '2.2 Non Defaults' mhash[:bundle_hash] = rhash[:bundle_hash] = 'ip-l4port-vlan' test_harness_run(tests, id) diff --git a/tests/beaker_tests/cisco_snmp_server/test_snmp_server.rb b/tests/beaker_tests/cisco_snmp_server/test_snmp_server.rb index 723aeb133..71376e2ee 100644 --- a/tests/beaker_tests/cisco_snmp_server/test_snmp_server.rb +++ b/tests/beaker_tests/cisco_snmp_server/test_snmp_server.rb @@ -31,7 +31,9 @@ resource_name: 'cisco_snmp_server', } -@def_pkt_size = platform[/n(3|8|9)k/] ? '1500' : '0' +# Add an anchor to the platform regexp below so that +# it only matches non-fretta n9k platforms. +@def_pkt_size = platform[/n(3|9)k$/] ? '1500' : '0' # Test hash test cases tests[:default] = { diff --git a/tests/beaker_tests/cisco_vlan/test_private_vlan.rb b/tests/beaker_tests/cisco_vlan/test_private_vlan.rb index f6ccab424..104b75199 100644 --- a/tests/beaker_tests/cisco_vlan/test_private_vlan.rb +++ b/tests/beaker_tests/cisco_vlan/test_private_vlan.rb @@ -30,12 +30,14 @@ require File.expand_path('../../lib/utilitylib.rb', __FILE__) # Test hash top-level keys +# The platform: key below must use an end of string anchor '$' in order to +# distinguish between 'n9k' and 'n9k-f' platform flavors. tests = { agent: agent, master: master, resource_name: 'cisco_vlan', operating_system: 'nexus', - platform: 'n(3|5|6|7|9)k', + platform: 'n(3|5|6|7|9)k$', } # Skip -ALL- tests if a top-level platform/os key exludes this platform diff --git a/tests/beaker_tests/cisco_vpc_domain/test_vpc_domain.rb b/tests/beaker_tests/cisco_vpc_domain/test_vpc_domain.rb index e524eba50..9505a04e4 100644 --- a/tests/beaker_tests/cisco_vpc_domain/test_vpc_domain.rb +++ b/tests/beaker_tests/cisco_vpc_domain/test_vpc_domain.rb @@ -23,12 +23,14 @@ ############################################################################### require File.expand_path('../../lib/utilitylib.rb', __FILE__) +# The platform: key below must use an end of string anchor '$' in order to +# distinguish between 'n9k' and 'n9k-f' platform flavors. tests = { master: master, agent: agent, operating_system: 'nexus', resource_name: 'cisco_vpc_domain', - platform: 'n(3|6|7|9)k', + platform: 'n(3|6|7|9)k$', } # Skip -ALL- tests if a top-level platform/os key exludes this platform diff --git a/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb b/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb index 8646290b8..2f24ba57e 100644 --- a/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb +++ b/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb @@ -28,7 +28,7 @@ agent: agent, master: master, operating_system: 'nexus', - platform: 'n(5|6|7|8|9)k', + platform: 'n(5|6|7|9)k', resource_name: 'cisco_vxlan_vtep', } diff --git a/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb b/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb index a38fcffb4..330667432 100644 --- a/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb +++ b/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb @@ -72,7 +72,7 @@ tests = { master: master, agent: agent, - platform: 'n(5|6|7|8|9)k', + platform: 'n(5|6|7|9)k', resource_name: 'cisco_vxlan_vtep_vni', } @@ -107,7 +107,7 @@ # tests['default_properties_ingress_replication'] = { title_pattern: 'nve1 10000', - platform: 'n(8|9)k', + platform: 'n9k', manifest_props: " ingress_replication => 'default', suppress_arp => 'default', @@ -139,7 +139,7 @@ tests['ingress_replication_static_peer_list_empty'] = { title_pattern: 'nve1 10000', - platform: 'n(8|9)k', + platform: 'n9k', manifest_props: " ingress_replication => 'static', peer_list => [], @@ -154,7 +154,7 @@ tests['peer_list'] = { title_pattern: 'nve1 10000', - platform: 'n(8|9)k', + platform: 'n9k', manifest_props: " ingress_replication => 'static', peer_list => ['1.1.1.1', '2.2.2.2', '3.3.3.3'], @@ -169,7 +169,7 @@ tests['peer_list_change_add'] = { title_pattern: 'nve1 10000', - platform: 'n(8|9)k', + platform: 'n9k', manifest_props: " ingress_replication => 'static', peer_list => ['1.1.1.1', '6.6.6.6', '3.3.3.3', '4.4.4.4'], @@ -184,7 +184,7 @@ tests['peer_list_default'] = { title_pattern: 'nve1 10000', - platform: 'n(8|9)k', + platform: 'n9k', manifest_props: " ingress_replication => 'static', peer_list => 'default', @@ -199,7 +199,7 @@ tests['ingress_replication_bgp'] = { title_pattern: 'nve1 10000', - platform: 'n(8|9)k', + platform: 'n9k', manifest_props: " ingress_replication => 'bgp', suppress_arp => 'default' diff --git a/tests/beaker_tests/file_service_package/test_package_patch.rb b/tests/beaker_tests/file_service_package/test_package_patch.rb index f38e8459e..37ee70904 100755 --- a/tests/beaker_tests/file_service_package/test_package_patch.rb +++ b/tests/beaker_tests/file_service_package/test_package_patch.rb @@ -44,8 +44,13 @@ filename = 'nxos.sample-n9k_EOR-1.0.0-7.0.3.I4.2.lib32_n9000.rpm' version = '1.0.0-7.0.3.I4.2' when /7.0.3.I5/ - filename = 'nxos.sample-n9k_EOR-1.0.0-7.0.3.I5.1.lib32_n9000.rpm' + name = 'nxos.sample-n9k_ALL' + filename = 'nxos.sample-n9k_ALL-1.0.0-7.0.3.I5.1.lib32_n9000.rpm' version = '1.0.0-7.0.3.I5.1' +when /7.0.3.F1/ + name = 'nxos.sample-n8k_EOR' + filename = 'nxos.sample-n8k_EOR-1.0.0-7.0.3.F1.1.lib32_nxos.rpm' + version = '1.0.0-7.0.3.F1.1' else raise_skip_exception("No patch available for image #{image?}", self) end @@ -57,7 +62,7 @@ tests = { agent: agent, master: master, - platform: 'n(3|8|9)k', + platform: 'n(3|9)k', resource_name: 'package', } @@ -65,7 +70,7 @@ skip_unless_supported(tests) tests[:yum_patch_install] = { - desc: "1.1 Apply sample patch to image #{image?}", + desc: "1.1 Apply sample patch #{name} to image #{image?}", title_pattern: name, manifest_props: { name: filename, @@ -79,7 +84,7 @@ } tests[:yum_patch_remove] = { - desc: '1.2 Remove sample patch', + desc: "1.2 Remove sample patch #{name}", code: [0, 2], ensure: :absent, title_pattern: name, diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 5c3e81f47..b574f5f61 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1083,14 +1083,8 @@ def platform @cisco_hardware = 'n6k' when /Nexus\s?7\d\d\d/ @cisco_hardware = 'n7k' - when /Nexus\s?8\d\d\d/ - @cisco_hardware = 'n8k' - when /NX-OSv8K/ - @cisco_hardware = 'n8k' - when /Nexus\s?9\d\d\d/ - @cisco_hardware = 'n9k' - when /NX-OSv Chassis/ - @cisco_hardware = 'n9k' + when /(Nexus\s?9\d\d\d|NX-OSv Chassis)/ + @cisco_hardware = image?[/7.0.3.F/] ? 'n9k-f' : 'n9k' when /XRv9K/i @cisco_hardware = 'xrv9k' else From b237d4c9f5152eb53bf047dab8f3f50382c8d46b Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Thu, 17 Nov 2016 14:50:36 -0500 Subject: [PATCH 128/203] Rel150/product tag (#407) * Add product_tag helper method * Fix bug in product_tag api --- lib/puppet_x/cisco/cmnutils.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/puppet_x/cisco/cmnutils.rb b/lib/puppet_x/cisco/cmnutils.rb index 03a45c5a8..d0f32a0f5 100644 --- a/lib/puppet_x/cisco/cmnutils.rb +++ b/lib/puppet_x/cisco/cmnutils.rb @@ -237,6 +237,25 @@ def self.range_summarize(range_str, sort=true) end ranges.join(',').gsub('..', '-') end + + def self.product_tag + data = Facter.value('cisco') + case data['inventory']['chassis']['pid'] + when /N3/ + tag = 'n3k' + when /N5/ + tag = 'n5k' + when /N6/ + tag = 'n6k' + when /N7/ + tag = 'n7k' + when /N9/ + tag = data['images']['system_image'][/7.0.3.F/] ? 'n9k-f' : 'n9k' + else + fail "Unrecognized product_id: #{data['inventory']['chassis']['pid']}" + end + tag + end end # class Utils # rubocop:enable Metrics/ClassLength From 17cae09cfcd6d52fffc2b3593bd5a4264cd5d36e Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Mon, 21 Nov 2016 12:40:00 -0500 Subject: [PATCH 129/203] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index de03776ea..af2728c32 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `hsrp_mac_refresh` - `hsrp_use_bia` - `hsrp_version` +* Added support for Cisco NX-OS software release `7.3(0)F1(1)` ### Changed From f378a7cf8c5fe4dc1677f532daca7bedd467a575 Mon Sep 17 00:00:00 2001 From: saichint Date: Tue, 22 Nov 2016 13:43:01 -0800 Subject: [PATCH 130/203] Interface HSRP group (#405) * hsrp global new provider * hsrp interface * beaker of intf_hsrp * move int_hsrp to int * documentation * hsrp group fixes * fix array issue * add validations * demo manifest * beaker * documentation * n8k to n9k-f * review comments * fix event_history for fretta * fixes for review comments * readme change * more comments --- CHANGELOG.md | 1 + README.md | 93 +++++ examples/cisco/demo_bgp.pp | 4 +- examples/cisco/demo_hsrp.pp | 52 ++- lib/puppet/provider/cisco_bgp/cisco.rb | 15 +- .../cisco_interface_hsrp_group/cisco.rb | 243 +++++++++++ lib/puppet/type/cisco_interface_hsrp_group.rb | 377 ++++++++++++++++++ .../cisco_hsrp/test_interface_hsrp_group.rb | 254 ++++++++++++ 8 files changed, 1033 insertions(+), 6 deletions(-) create mode 100644 lib/puppet/provider/cisco_interface_hsrp_group/cisco.rb create mode 100644 lib/puppet/type/cisco_interface_hsrp_group.rb create mode 100644 tests/beaker_tests/cisco_hsrp/test_interface_hsrp_group.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index af2728c32..d105c43d8 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### New feature support #### Cisco Resources - `cisco_hsrp_global` type and provider. +- `cisco_interface_hsrp_group` type and provider. ### Added - Extend cisco_interface with attributes: diff --git a/README.md b/README.md index 6217e4088..fba71786c 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,7 @@ The following resources include cisco types and providers along with cisco provi * HSRP Types * [`cisco_hsrp_global`](#type-cisco_hsrp_global) + * [`cisco_interface_hsrp_group`](#type-cisco_interface_hsrp_group) * Interface Types * [`cisco_interface`](#type-cisco_interface) @@ -306,6 +307,7 @@ The following resources include cisco types and providers along with cisco provi * [`cisco_hsrp_global`](#type-cisco_hsrp_global) * [`cisco_interface`](#type-cisco_interface) * [`cisco_interface_channel_group`](#type-cisco_interface_channel_group) +* [`cisco_interface_hsrp_group`](#type-cisco_interface_hsrp_group) * [`cisco_interface_ospf`](#type-cisco_interface_ospf) * [`cisco_interface_portchannel`](#type-cisco_interface_portchannel) * [`cisco_interface_service_vni`](#type-cisco_interface_service_vni) @@ -416,6 +418,7 @@ Symbol | Meaning | Description | [cisco_hsrp_global](#type-cisco_hsrp_global) | ✅ | ✅* | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_hsrp_global-caveats) | | [cisco_interface](#type-cisco_interface) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_interface-caveats) | | [cisco_interface_channel_group](#type-cisco_interface_channel_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_interface_hsrp_group](#type-cisco_interface_hsrp_group) | ✅ | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | | [cisco_interface_ospf](#type-cisco_interface_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_interface_portchannel](#type-cisco_interface_portchannel) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_interface_portchannel-caveats) | | [cisco_interface_service_vni](#type-cisco_interface_service_vni) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | @@ -2317,6 +2320,96 @@ Description of the interface. Valid values are a string or the keyword 'default' ###### `shutdown` Shutdown state of the interface. Valid values are 'true', 'false', and 'default'. +-- +### Type: cisco_interface_hsrp_group + +Manages a Cisco Network Interface HSRP group. + +| Platform | OS Minimum Version | Module Minimum Version | +|----------|:------------------:|:----------------------:| +| N9k | 7.0(3)I2(1) | 1.5.0 | +| N3k | 7.0(3)I2(1) | 1.5.0 | +| N5k | not applicable | not applicable | +| N6k | not applicable | not applicable | +| N7k | not applicable | not applicable | +| N9k-F | 7.0(3)F1(1) | 1.5.0 | + +#### Parameters + +##### Basic interface channel-group config attributes + +###### `ensure` +Determine whether the interface hsrp group config should be present or not. Valid values are 'present' and 'absent'. + +###### `authentication_auth_type` +Authentication type for the HSRP group. Valid values are 'cleartext', 'md5', keyword 'default'. + +###### `authentication_compatibility` +Turn on compatibility mode for MD5 type-7 authentication. Valid values are 'true', 'false', keyword 'default'. + +###### `authentication_enc_type` +Scheme used for encrypting authentication key string. Valid values are 'clear', 'encrypted', keyword 'default'. + +###### `authentication_key_type` +Authentication key type. Valid values are 'key-chain', 'key-string', keyword 'default'. + +###### `authentication_string` +Specifies password or key chain name or key string name. Valid values are string, keyword 'default'. + +###### `authentication_timeout` +Specifies authentication timeout. Valid values are integer, keyword 'default'. + +###### `ipv4_enable` +Enables HSRP ipv4. Valid values are 'true', 'false', keyword 'default'. + +###### `ipv4_vip` +Sets HSRP IPv4 virtual IP addressing name. Valid values are string, keyword 'default'. + +###### `ipv6_autoconfig` +Obtains ipv6 address using autoconfiguration. Valid values are 'true', 'false', keyword 'default'. + +###### `ipv6_vip` +Enables HSRP IPv6 and sets an array of virtual IPv6 addresses. Valid values are array of ipv6 addresses, keyword 'default'. + +###### `mac_addr` +Virtual mac address. Valid values are string specifying the mac address, keyword 'default'. + +###### `group_name` +Redundancy name string. Valid values are string, keyword 'default'. + +###### `preempt` +Overthrows lower priority Active routers. Valid values are 'true', 'false', keyword 'default'. + +###### `preempt_delay_minimum` +Specifies amount of time to wait before pre-empting. Valid values are integer, keyword 'default'. + +###### `preempt_delay_reload` +Specifies time to wait after reload. Valid values are integer, keyword 'default'. + +###### `preempt_delay_sync` +Specifies time to wait for IP redundancy clients. Valid values are integer, keyword 'default'. + +###### `priority` +Sets priority value for this interface hsrp group. Valid values are integer, keyword 'default'. + +###### `priority_forward_thresh_lower` +Sets priority forwarding lower threshold value. Valid values are integer, keyword 'default'. + +###### `priority_forward_thresh_upper` +Sets priority forwarding upper threshold value. Valid values are integer, keyword 'default'. + +###### `timers_hello_msec` +Specify hello interval in milliseconds. Valid values are 'true', 'false', keyword 'default'. + +###### `timers_hold_msec` +Specify hold interval in milliseconds. Valid values are 'true', 'false', keyword 'default'. + +###### `timers_hello` +Sets hello interval. Valid values are integer, keyword 'default'. + +###### `timers_hold` +Sets hold interval. Valid values are integer, keyword 'default'. + -- ### Type: cisco_interface_service_vni diff --git a/examples/cisco/demo_bgp.pp b/examples/cisco/demo_bgp.pp index dcd792fb2..1bcf59fdd 100644 --- a/examples/cisco/demo_bgp.pp +++ b/examples/cisco/demo_bgp.pp @@ -59,7 +59,7 @@ } $event_history_errors = platform_get() ? { - /(n3k|n9k)/ => $facts['cisco']['images']['system_image'] ? { + /(n3k|n9k$)/ => $facts['cisco']['images']['system_image'] ? { /(I2|I3|I4)/ => undef, default => 'size_small' }, @@ -67,7 +67,7 @@ } $event_history_objstore = platform_get() ? { - /(n3k|n9k)/ => $facts['cisco']['images']['system_image'] ? { + /(n3k|n9k$)/ => $facts['cisco']['images']['system_image'] ? { /(I2|I3|I4)/ => undef, default => 'size_small' }, diff --git a/examples/cisco/demo_hsrp.pp b/examples/cisco/demo_hsrp.pp index 58fa5d8ce..56d365aeb 100644 --- a/examples/cisco/demo_hsrp.pp +++ b/examples/cisco/demo_hsrp.pp @@ -17,7 +17,7 @@ class ciscopuppet::cisco::demo_hsrp { $bfd_all_intf = platform_get() ? { - /(n5k|n6k|n7k|n9k-f|n9k)/ => true, + /(n5k|n6k|n7k|n9k)/ => true, default => undef } @@ -26,9 +26,10 @@ extended_hold => 200, } - if platform_get() =~ /n(3|9)k/ { + if platform_get() =~ /(n3k|n9k)/ { cisco_interface { 'port-channel100': ensure => 'present', + switchport_mode => 'disabled', hsrp_bfd => true, hsrp_delay_minimum => 200, hsrp_delay_reload => 300, @@ -36,6 +37,53 @@ hsrp_use_bia => 'use_bia_intf', hsrp_version => 2, } + + cisco_interface { 'port-channel200': + ensure => 'present', + switchport_mode => 'disabled', + hsrp_delay_minimum => 50, + hsrp_delay_reload => 100, + hsrp_mac_refresh => 300, + hsrp_version => 2, + } + + cisco_interface_hsrp_group { 'port-channel100 2 ipv6': + ensure => 'present', + authentication_auth_type => 'md5', + authentication_string => '12345678901234567890', + authentication_key_type => 'key-string', + authentication_enc_type => 'encrypted', + authentication_compatibility => true, + authentication_timeout => 200, + ipv6_vip => ['2000::11', '2001::22'], + ipv6_autoconfig => true, + group_name => 'MyHsrp', + preempt => true, + preempt_delay_minimum => '100', + preempt_delay_reload => '100', + preempt_delay_sync => '100', + priority => '45', + priority_forward_thresh_lower => '10', + priority_forward_thresh_upper => '40', + timers_hello_msec => true, + timers_hold_msec => true, + timers_hello => 300, + timers_hold => 1000, + } + + cisco_interface_hsrp_group { 'port-channel100 2 ipv4': + ensure => 'present', + authentication_auth_type => 'cleartext', + authentication_string => 'MyPass', + ipv4_enable => true, + ipv4_vip => '2.2.2.2', + } + + cisco_interface_hsrp_group { 'port-channel200 50 ipv4': + ensure => 'present', + ipv4_enable => true, + mac_addr => '00:00:11:11:22:22', + } } else { warning('This platform does not support interface hsrp properties') } diff --git a/lib/puppet/provider/cisco_bgp/cisco.rb b/lib/puppet/provider/cisco_bgp/cisco.rb index 7690ced5c..f38fb4520 100644 --- a/lib/puppet/provider/cisco_bgp/cisco.rb +++ b/lib/puppet/provider/cisco_bgp/cisco.rb @@ -23,6 +23,13 @@ 'puppet_x', 'cisco', 'autogen.rb')) end +begin + require 'puppet_x/cisco/cmnutils' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'cmnutils.rb')) +end Puppet::Type.type(:cisco_bgp).provide(:cisco) do desc 'The cisco bgp provider.' @@ -212,10 +219,10 @@ def timer_bestpath_limit_set end def legacy_image? + utils = PuppetX::Cisco::Utils fd = Facter.value('cisco') image = fd['images']['system_image'] - pid = fd['inventory']['chassis']['pid'] - image[/7.0.3.I2|I3|I4/] || pid[/N(5|6|7|8)/] + image[/7.0.3.I2|I3|I4/] || utils.product_tag[/n(5k|6k|7k|9k-f)/] end def event_history_default?(prop) @@ -274,6 +281,9 @@ def event_history_events return 'true' if event_history_default?('event_history_events') when 'size_disable' return 'size_disable' if event_history_false?('event_history_events') + when 'size_large' + return 'size_large' if event_history_default?('event_history_events') && + !legacy_image? end @property_hash[:event_history_events] end @@ -282,6 +292,7 @@ def event_history_events=(should_value) should_value = @bgp_vrf.default_event_history_events if should_value == 'default' || should_value == 'true' should_value = 'false' if should_value == 'size_disable' && !legacy_image? + should_value = 'true' if should_value == 'size_large' && !legacy_image? should_value = should_value.to_sym unless should_value =~ /\A\d+\z/ @property_flush[:event_history_events] = should_value end diff --git a/lib/puppet/provider/cisco_interface_hsrp_group/cisco.rb b/lib/puppet/provider/cisco_interface_hsrp_group/cisco.rb new file mode 100644 index 000000000..42a6725a2 --- /dev/null +++ b/lib/puppet/provider/cisco_interface_hsrp_group/cisco.rb @@ -0,0 +1,243 @@ +# November, 2016 +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'cisco_node_utils' if Puppet.features.cisco_node_utils? +begin + require 'puppet_x/cisco/autogen' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'autogen.rb')) +end + +begin + require 'puppet_x/cisco/cmnutils' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'cmnutils.rb')) +end +Puppet::Type.type(:cisco_interface_hsrp_group).provide(:cisco) do + desc 'The Cisco interface hsrp group provider.' + + confine feature: :cisco_node_utils + defaultfor operatingsystem: :nexus + + mk_resource_methods + + INTERFACE_HSRP_GROUP_NON_BOOL_PROPS = [ + :authentication_auth_type, + :authentication_enc_type, + :authentication_key_type, + :authentication_string, + :authentication_timeout, + :ipv4_vip, + :mac_addr, + :group_name, + :preempt_delay_minimum, + :preempt_delay_reload, + :preempt_delay_sync, + :priority, + :priority_forward_thresh_lower, + :priority_forward_thresh_upper, + :timers_hello, + :timers_hold, + ] + + INTERFACE_HSRP_GROUP_BOOL_PROPS = [ + :authentication_compatibility, + :ipv4_enable, + :ipv6_autoconfig, + :preempt, + :timers_hello_msec, + :timers_hold_msec, + ] + + INTERFACE_HSRP_GROUP_ARRAY_FLAT_PROPS = [ + :ipv6_vip + ] + + INTERFACE_HSRP_GROUP_ALL_PROPS = INTERFACE_HSRP_GROUP_BOOL_PROPS + + INTERFACE_HSRP_GROUP_NON_BOOL_PROPS + + INTERFACE_HSRP_GROUP_ARRAY_FLAT_PROPS + + PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@nu', + INTERFACE_HSRP_GROUP_NON_BOOL_PROPS) + PuppetX::Cisco::AutoGen.mk_puppet_methods(:bool, self, '@nu', + INTERFACE_HSRP_GROUP_BOOL_PROPS) + PuppetX::Cisco::AutoGen.mk_puppet_methods(:array_flat, self, '@nu', + INTERFACE_HSRP_GROUP_ARRAY_FLAT_PROPS) + + def initialize(value={}) + super(value) + interface = @property_hash[:interface] + group = @property_hash[:group] + iptype = @property_hash[:iptype] + @nu = Cisco::InterfaceHsrpGroup.groups[interface][group][iptype] unless + interface.nil? || group.nil? || iptype.nil? + @property_flush = {} + end + + def self.properties_get(interface, group, iptype, nu_obj) + debug "Checking hsrp group instance, #{interface} #{group} #{iptype}" + current_state = { + name: "#{interface} #{group} #{iptype}", + interface: interface, + group: group, + iptype: iptype, + ensure: :present, + } + + # Call node_utils getter for each property + (INTERFACE_HSRP_GROUP_NON_BOOL_PROPS).each do |prop| + current_state[prop] = nu_obj.send(prop) + end + INTERFACE_HSRP_GROUP_ARRAY_FLAT_PROPS.each do |prop| + current_state[prop] = nu_obj.send(prop) + end + INTERFACE_HSRP_GROUP_BOOL_PROPS.each do |prop| + val = nu_obj.send(prop) + if val.nil? + current_state[prop] = nil + else + current_state[prop] = val ? :true : :false + end + end + new(current_state) + end # self.properties_get + + def self.instances + hg_instances = [] + Cisco::InterfaceHsrpGroup.groups.each do |interface, groups| + groups.each do |group, iptypes| + iptypes.each do |iptype, nu_obj| + hg_instances << properties_get(interface, group, iptype, nu_obj) + end + end + end + hg_instances + end # self.instances + + def self.prefetch(resources) + hg_instances = instances + resources.keys.each do |id| + provider = hg_instances.find do |hgi| + hgi.interface.to_s == resources[id][:interface].to_s && + hgi.group.to_s == resources[id][:group].to_s && + hgi.iptype.to_s == resources[id][:iptype].to_s + end + resources[id].provider = provider unless provider.nil? + end + end + + def exists? + @property_hash[:ensure] == :present + end + + def create + @property_flush[:ensure] = :present + end + + def destroy + @property_flush[:ensure] = :absent + end + + def properties_set(new_hg=false) + INTERFACE_HSRP_GROUP_ALL_PROPS.each do |prop| + next unless @resource[prop] + send("#{prop}=", @resource[prop]) if new_hg + unless @property_flush[prop].nil? + @nu.send("#{prop}=", @property_flush[prop]) if + @nu.respond_to?("#{prop}=") + end + end + # custom setters which require one-shot multi-param setters + authentication_set + ipv4_vip_set + preempt_set + priority_set + timers_set + end + + def authentication_set + attrs = {} + vars = [ + :authentication_auth_type, + :authentication_enc_type, + :authentication_key_type, + :authentication_string, + :authentication_timeout, + :authentication_compatibility, + ] + return unless vars.any? { |p| @property_flush.key?(p) } + # At least one var has changed, get all vals from manifest + vars.each do |p| + if @resource[p] == :default + attrs[p] = @nu.send("default_#{p}") + else + attrs[p] = @resource[p] + attrs[p] = PuppetX::Cisco::Utils.bool_sym_to_s(attrs[p]) + end + end + @nu.authentication_set(attrs) + end + + def ipv4_vip_set + enable = PuppetX::Cisco::Utils.flush_boolean?(@property_flush[:ipv4_enable]) ? @property_flush[:ipv4_enable] : @nu.ipv4_enable + vip = @property_flush[:ipv4_vip] ? @property_flush[:ipv4_vip] : @nu.ipv4_vip + @nu.ipv4_vip_set(enable, vip) + end + + def preempt_set + min = @property_flush[:preempt_delay_minimum] ? @property_flush[:preempt_delay_minimum] : @nu.preempt_delay_minimum + rel = @property_flush[:preempt_delay_reload] ? @property_flush[:preempt_delay_reload] : @nu.preempt_delay_reload + sync = @property_flush[:preempt_delay_sync] ? @property_flush[:preempt_delay_sync] : @nu.preempt_delay_sync + pree = PuppetX::Cisco::Utils.flush_boolean?(@property_flush[:preempt]) ? @property_flush[:preempt] : @nu.preempt + @nu.preempt_set(pree, min, rel, sync) + end + + def priority_set + pri = @property_flush[:priority] ? @property_flush[:priority] : @nu.priority + low = @property_flush[:priority_forward_thresh_lower] ? @property_flush[:priority_forward_thresh_lower] : @nu.priority_forward_thresh_lower + up = @property_flush[:priority_forward_thresh_upper] ? @property_flush[:priority_forward_thresh_upper] : @nu.priority_forward_thresh_upper + @nu.priority_level_set(pri, low, up) + end + + def timers_set + hello = @property_flush[:timers_hello] ? @property_flush[:timers_hello] : @nu.timers_hello + hold = @property_flush[:timers_hold] ? @property_flush[:timers_hold] : @nu.timers_hold + hem = PuppetX::Cisco::Utils.flush_boolean?(@property_flush[:timers_hello_msec]) ? @property_flush[:timers_hello_msec] : @nu.timers_hello_msec + hom = PuppetX::Cisco::Utils.flush_boolean?(@property_flush[:timers_hold_msec]) ? @property_flush[:timers_hold_msec] : @nu.timers_hold_msec + @nu.timers_set(hem, hello, hom, hold) + end + + def flush + if @property_flush[:ensure] == :absent + @nu.destroy + @nu = nil + else + # Create/Update + new_hg = false + if @nu.nil? + new_hg = true + @nu = Cisco::InterfaceHsrpGroup.new(@resource[:interface], + @resource[:group], + @resource[:iptype]) + end + properties_set(new_hg) + end + end +end diff --git a/lib/puppet/type/cisco_interface_hsrp_group.rb b/lib/puppet/type/cisco_interface_hsrp_group.rb new file mode 100644 index 000000000..31bcbdc44 --- /dev/null +++ b/lib/puppet/type/cisco_interface_hsrp_group.rb @@ -0,0 +1,377 @@ +# Manages the Cisco OSPF area virtual-link configuration resource. +# +# November 2016 +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Puppet::Type.newtype(:cisco_interface_hsrp_group) do + @doc = "Manages an interface hsrp group. + + cisco_interface_hsrp_group {\" \": + ..attributes.. + } + + is the name of the interface. + is the group id. + is ipv4 or ipv6. + + Examples: + cisco_interface_hsrp_group {'Ethernet1/1 100 ipv4': + ensure => 'present', + authentication_auth_type => 'md5', + authentication_compatibility => 'default', + authentication_enc_type => 'default', + authentication_key_type => 'key-chain', + authentication_string => 'MyKeyChain', + authentication_timeout => 'default', + ipv4_enable => true, + ipv4_vip => '10.10.10.10', + ipv6_vip => 'default', + ipv6_autoconfig => 'default', + mac_addr => '00:00:11:11:22:22', + group_name => 'MyHsrpGroup', + preempt => true, + preempt_delay_minimum => 100, + preempt_delay_reload => 200, + preempt_delay_sync => 300, + priority => 50, + priority_forward_thresh_lower => 20, + priority_forward_thresh_upper => 30, + timers_hello => 500, + timers_hello_msec => true, + timers_hold => 1500, + timers_hold_msec => true, + } + " + + ensurable + + ################### + # Resource Naming # + ################### + + # Parse out the title to fill in the attributes in these + # patterns. These attributes can be overwritten later. + def self.title_patterns + identity = ->(x) { x } + patterns = [] + + # Below pattern matches both parts of the full composite name. + patterns << [ + /^(\S+) (\d+) (\S+)$/, + [ + [:interface, identity], + [:group, identity], + [:iptype, identity], + ], + ] + patterns + end + + # Overwrites name method. Original method simply returns self[:name], + # which is no longer valid or complete. + # Would not have failed, but just return nothing useful. + def name + "#{self[:interface]} #{self[:group]} #{self[:iptype]}" + end + + newparam(:name) do + desc 'Name of cisco_interface_hsrp_group, not used, but needed for puppet' + end + + newparam(:iptype, namevar: true) do + desc 'Ip Type. Valid values are ipv4 or ipv6.' + munge(&:to_s) + newvalues(:ipv4, :ipv6) + end # param iptype + + newparam(:group, namevar: true) do + desc 'HSRP group ID. Valid values are integer.' + end # param group + + newparam(:interface, namevar: true) do + desc 'Name of the interface instance. Valid values are string.' + end # param interface + + ############## + # Attributes # + ############## + + newproperty(:authentication_auth_type) do + desc 'Authentication type' + + newvalues(:cleartext, :md5, :default) + end # property authentication_auth_type + + newproperty(:authentication_compatibility) do + desc 'Operate in compatibility mode for MD5 type-7 authentication. + Valid only for key-string' + + newvalues(:true, :false, :default) + end # property authentication_compatibility + + newproperty(:authentication_enc_type) do + desc 'Scheme used for encrypting authentication key string.' + + munge do |value| + value = '0' if value == 'clear' + value = '7' if value == 'encrypted' + value.to_sym + end + + newvalues(:clear, :encrypted, :default) + end # property authentication_enc_type + + newproperty(:authentication_key_type) do + desc 'Authentication key type' + + newvalues(:'key-chain', :'key-string', :default) + end # property authentication_key_type + + newproperty(:authentication_string) do + desc "Specifies password or key chain name or key string name. Valid + values are string, keyword 'default'" + + munge { |value| value == 'default' ? :default : value } + end # property authentication_string + + newproperty(:authentication_timeout) do + desc "Specifies authentication timeout. Valid only for key-string. + Valid values are integer, keyword 'default'" + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property authentication_timeout + + newproperty(:ipv4_enable) do + desc 'Enables HSRP ipv4' + + newvalues(:true, :false, :default) + end # property ipv4_enable + + newproperty(:ipv4_vip) do + desc "Sets HSRP IPv4 virtual IP addressing name. Valid + values are string, keyword 'default'" + + munge { |value| value == 'default' ? :default : value } + end # property ipv4_vip + + newproperty(:ipv6_vip, array_matching: :all) do + desc "Enables HSRP IPv6 and sets an array of virtual IPv6 addresses. + Valid values are array of ipv6 addresses, keyword 'default'" + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + slist = [] + should.each do |elem| + slist << elem unless elem[1] == 'default' + end + (is.size == slist.size && is.sort == slist.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property ipv6_vip + + newproperty(:ipv6_autoconfig) do + desc 'Obtains ipv6 address using autoconfiguration' + + newvalues(:true, :false, :default) + end # property ipv6_autoconfig + + newproperty(:mac_addr) do + desc "Virtual mac address. Valid values are string, keyword 'default'" + munge { |value| value == 'default' ? :default : value } + end # property mac_addr + + newproperty(:group_name) do + desc "Redundancy name string. Valid values are string, keyword 'default'" + + munge { |value| value == 'default' ? :default : value } + end # property group_name + + newproperty(:preempt) do + desc 'Overthrow lower priority Active routers' + + newvalues(:true, :false, :default) + end # property preempt + + newproperty(:preempt_delay_minimum) do + desc "Specifies time to wait at least this long before pre-empting. + Valid values are integer, keyword 'default'" + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property preempt_delay_minimum + + newproperty(:preempt_delay_reload) do + desc "Specifies time to wait after reload. + Valid values are integer, keyword 'default'" + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property preempt_delay_reload + + newproperty(:preempt_delay_sync) do + desc "Specifies time to wait for IP redundancy clients. + Valid values are integer, keyword 'default'" + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property preempt_delay_sync + + newproperty(:priority) do + desc "Sets Priority value for this hsrp group. + Valid values are integer, keyword 'default'" + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property priority + + newproperty(:priority_forward_thresh_lower) do + desc "Sets Priority forwarding lower threshold value. + Valid values are integer, keyword 'default'" + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property priority_forward_thresh_lower + + newproperty(:priority_forward_thresh_upper) do + desc "Sets Priority forwarding upper threshold value. + Valid values are integer, keyword 'default'" + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property priority_forward_thresh_upper + + newproperty(:timers_hello_msec) do + desc 'Specify hello interval in milliseconds' + + newvalues(:true, :false, :default) + end # property timers_hello_msec + + newproperty(:timers_hold_msec) do + desc 'Specify hold interval in milliseconds' + + newvalues(:true, :false, :default) + end # property timers_hold_msec + + newproperty(:timers_hello) do + desc "Sets hello interval. Valid values are integer, keyword 'default'" + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property timers_hello + + newproperty(:timers_hold) do + desc "Sets hold interval. Valid values are integer, keyword 'default'" + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property timers_hold + + def my_atype + self[:authentication_auth_type].nil? || + self[:authentication_auth_type] == :default || + self[:authentication_auth_type] == :cleartext + end + + def my_enc + self[:authentication_enc_type].nil? || + self[:authentication_enc_type] == :default || + self[:authentication_enc_type] == :'0' + end + + def my_key + self[:authentication_key_type].nil? || + self[:authentication_key_type] == :default || + self[:authentication_key_type] == :'key-chain' + end + + def my_compat + self[:authentication_compatibility].nil? || + self[:authentication_compatibility] == :default || + self[:authentication_compatibility] == :false + end + + def my_str + self[:authentication_string].nil? || + self[:authentication_string] == :default || + self[:authentication_string].empty? + end + + def my_timeout + self[:authentication_timeout].nil? || + self[:authentication_timeout] == :default || + self[:authentication_timeout].zero? + end + + def check_auth_str + return unless my_str + fail ArgumentError, 'Authentication properties auth_type, enc_type, key_type, compatibility, timeout MUST be default when authentication_string is default' unless + my_atype && my_compat && my_enc && my_key && my_timeout + end + + def check_auth_type + return unless my_atype + fail ArgumentError, 'Authentication properties enc_type, key_type, compatibility, timeout MUST be default when authentication_auth_type is default' unless + my_compat && my_enc && my_key && my_timeout + end + + def check_auth_key + return unless my_key + fail ArgumentError, 'Authentication properties enc_type, compatibility, timeout MUST be default when authentication_key_type is default' unless + my_compat && my_enc && my_timeout + end + + def check_ipv4 + ena = self[:ipv4_enable].nil? || self[:ipv4_enable] == :default || self[:ipv4_enable] == :false + vip = self[:ipv4_vip].nil? || self[:ipv4_vip] == :default || self[:ipv4_vip] == '' + fail ArgumentError, 'ipv4 parameters MUST be default for ipv6 type' if self[:iptype] == 'ipv6' && (!ena || !vip) + return unless ena + fail ArgumentError, 'ipv4_enable MUST be default when ipv4_vip is default' unless vip + end + + def check_ipv6 + return if self[:iptype] == 'ipv6' + auto = self[:ipv6_autoconfig].nil? || self[:ipv6_autoconfig] == :default || self[:ipv6_autoconfig] == :false + vip = self[:ipv6_vip].nil? || self[:ipv6_vip] == :default || self[:ipv6_vip].empty? + fail ArgumentError, 'ipv6 parameters MUST be default for ipv4 type' if !auto || !vip + end + + def check_preempt + pre = self[:preempt].nil? || self[:preempt] == :default || self[:preempt] == :false + return unless pre + min = self[:preempt_delay_minimum].nil? || self[:preempt_delay_minimum] == :default || self[:preempt_delay_minimum].zero? + rel = self[:preempt_delay_reload].nil? || self[:preempt_delay_reload] == :default || self[:preempt_delay_reload].zero? + sync = self[:preempt_delay_sync].nil? || self[:preempt_delay_sync] == :default || self[:preempt_delay_sync].zero? + fail ArgumentError, 'Preempt properties delay_minimum, delay_reload, delay_sync MUST be default when preempt is default' unless + min && rel && sync + end + + validate do + check_auth_str + check_auth_type + check_auth_key + check_ipv4 + check_ipv6 + check_preempt + end +end diff --git a/tests/beaker_tests/cisco_hsrp/test_interface_hsrp_group.rb b/tests/beaker_tests/cisco_hsrp/test_interface_hsrp_group.rb new file mode 100644 index 000000000..c7985c790 --- /dev/null +++ b/tests/beaker_tests/cisco_hsrp/test_interface_hsrp_group.rb @@ -0,0 +1,254 @@ +############################################################################### +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### + +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# Test hash top-level keys +tests = { + master: master, + agent: agent, + intf_type: 'port-channel', + platform: 'n(3|9)k', + resource_name: 'cisco_interface_hsrp_group', +} + +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + +# Test hash test cases +tests[:default_v4] = { + desc: '1.1 IPv4 Default properties', + title_pattern: 'port-channel100 500 ipv4', + sys_def_switchport: false, + manifest_props: { + authentication_auth_type: 'default', + authentication_compatibility: 'default', + authentication_enc_type: 'default', + authentication_key_type: 'default', + authentication_string: 'default', + authentication_timeout: 'default', + group_name: 'default', + ipv4_enable: 'default', + ipv4_vip: 'default', + mac_addr: 'default', + preempt: 'default', + preempt_delay_minimum: 'default', + preempt_delay_reload: 'default', + preempt_delay_sync: 'default', + priority: 'default', + priority_forward_thresh_lower: 'default', + priority_forward_thresh_upper: 'default', + timers_hello: 'default', + timers_hello_msec: 'default', + timers_hold: 'default', + timers_hold_msec: 'default', + }, + code: [0, 2], + resource: { + authentication_auth_type: 'cleartext', + authentication_compatibility: 'false', + authentication_enc_type: '0', + authentication_key_type: 'key-chain', + authentication_timeout: 0, + group_name: 'false', + ipv4_enable: 'false', + mac_addr: 'false', + preempt: 'false', + preempt_delay_minimum: 0, + preempt_delay_reload: 0, + preempt_delay_sync: 0, + priority: 100, + timers_hello: 3, + timers_hello_msec: 'false', + timers_hold: 10, + timers_hold_msec: 'false', + }, +} + +tests[:default_v6] = { + desc: '1.2 IPv6 Default properties', + title_pattern: 'port-channel200 510 ipv6', + sys_def_switchport: false, + manifest_props: { + authentication_auth_type: 'default', + authentication_compatibility: 'default', + authentication_enc_type: 'default', + authentication_key_type: 'default', + authentication_string: 'default', + authentication_timeout: 'default', + group_name: 'default', + ipv6_autoconfig: 'default', + ipv6_vip: 'default', + mac_addr: 'default', + preempt: 'default', + preempt_delay_minimum: 'default', + preempt_delay_reload: 'default', + preempt_delay_sync: 'default', + priority: 'default', + priority_forward_thresh_lower: 'default', + priority_forward_thresh_upper: 'default', + timers_hello: 'default', + timers_hello_msec: 'default', + timers_hold: 'default', + timers_hold_msec: 'default', + }, + code: [0, 2], + resource: { + authentication_auth_type: 'cleartext', + authentication_compatibility: 'false', + authentication_enc_type: '0', + authentication_key_type: 'key-chain', + authentication_timeout: 0, + group_name: 'false', + ipv6_autoconfig: 'false', + mac_addr: 'false', + preempt: 'false', + preempt_delay_minimum: 0, + preempt_delay_reload: 0, + preempt_delay_sync: 0, + priority: 100, + timers_hello: 3, + timers_hello_msec: 'false', + timers_hold: 10, + timers_hold_msec: 'false', + }, +} + +tests[:non_default_v4] = { + desc: '2.1 IPv4 Non Default properties', + title_pattern: 'port-channel100 500 ipv4', + sys_def_switchport: false, + manifest_props: { + authentication_auth_type: 'md5', + authentication_compatibility: 'true', + authentication_enc_type: 'encrypted', + authentication_key_type: 'key-string', + authentication_string: '12345678901234567890', + authentication_timeout: 200, + group_name: 'MyHsrpv4', + ipv4_enable: 'true', + ipv4_vip: '2.2.2.2', + mac_addr: '00:00:11:11:22:22', + preempt: 'true', + preempt_delay_minimum: '100', + preempt_delay_reload: '200', + preempt_delay_sync: '300', + priority: '45', + priority_forward_thresh_lower: '10', + priority_forward_thresh_upper: '40', + timers_hello: 300, + timers_hello_msec: 'true', + timers_hold: 1000, + timers_hold_msec: 'true', + }, + resource: { + authentication_enc_type: '7' + }, +} + +tests[:non_default_v6] = { + desc: '2.2 IPv6 Non Default properties', + title_pattern: 'port-channel200 510 ipv6', + sys_def_switchport: false, + manifest_props: { + authentication_auth_type: 'md5', + authentication_compatibility: 'true', + authentication_enc_type: 'encrypted', + authentication_key_type: 'key-string', + authentication_string: '12345678901234567890', + authentication_timeout: 200, + group_name: 'MyHsrpv6', + ipv6_autoconfig: 'true', + ipv6_vip: ['2000::11', '2001::22'], + mac_addr: '00:00:11:11:22:22', + preempt: 'true', + preempt_delay_minimum: '100', + preempt_delay_reload: '200', + preempt_delay_sync: '300', + priority: '45', + priority_forward_thresh_lower: '10', + priority_forward_thresh_upper: '40', + timers_hello: 300, + timers_hello_msec: 'true', + timers_hold: 1000, + timers_hold_msec: 'true', + }, + resource: { + authentication_enc_type: '7' + }, +} + +def cleanup(agent) + cmd = ['no interface port-channel 100', + 'no interface port-channel 200', + 'no feature hsrp', + ].join(' ; ') + test_set(agent, cmd) +end + +# Overridden to properly handle dependencies for this test file. +def test_harness_dependencies(_tests, _id) + cleanup(agent) + + cmd = [ + 'feature hsrp', + 'interface port-channel100 ; no switchport ; hsrp version 2', + 'interface port-channel200 ; no switchport ; hsrp version 2', + ].join(' ; ') + test_set(agent, cmd) +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(agent) } + cleanup(agent) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + + test_harness_run(tests, :default_v4) + test_harness_run(tests, :default_v6) + + id = :default_v4 + tests[id][:desc] = '1.3 IPv4 Defaults (absent)' + tests[id][:ensure] = :absent + test_harness_run(tests, id) + + id = :default_v6 + tests[id][:desc] = '1.4 IPv6 Defaults (absent)' + tests[id][:ensure] = :absent + test_harness_run(tests, id) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + + test_harness_run(tests, :non_default_v4) + test_harness_run(tests, :non_default_v6) + + # ------------------------------------------------------------------- + skipped_tests_summary(tests) +end +logger.info("TestCase :: #{tests[:resource_name]} :: End") From c21878d5fd35892b7849d06746477949365c4f13 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Thu, 1 Dec 2016 14:31:37 -0500 Subject: [PATCH 131/203] Rel150/atherton fretta fixes (#409) * Handle atherton nxapi double quotes * Handle atherton nxapi double quotes * Handle atherton nxapi double quotes for tacacs * Make regexp in nexus_image method more flexible --- .../tacacsserver_provider_nondefaults.rb | 40 +++++----- .../tacacsserverhost_provider_nondefaults.rb | 32 ++++---- tests/beaker_tests/lib/utilitylib.rb | 21 ++++- .../radius_global_provider_defaults.rb | 40 +++++----- .../radius_server_provider_defaults.rb | 80 +++++++++---------- .../tacacs_global_provider_defaults.rb | 32 ++++---- .../tacacs_server_provider_defaults.rb | 72 ++++++++--------- 7 files changed, 167 insertions(+), 150 deletions(-) diff --git a/tests/beaker_tests/cisco_tacacs_server/tacacsserver_provider_nondefaults.rb b/tests/beaker_tests/cisco_tacacs_server/tacacsserver_provider_nondefaults.rb index e92b025c4..d13a2d979 100644 --- a/tests/beaker_tests/cisco_tacacs_server/tacacsserver_provider_nondefaults.rb +++ b/tests/beaker_tests/cisco_tacacs_server/tacacsserver_provider_nondefaults.rb @@ -94,16 +94,16 @@ # Expected exit_code is 0 since this is a puppet resource cmd. # Flag is set to false to check for presence of RegExp pattern in stdout. cmd_str = PUPPET_BINPATH + 'resource cisco_tacacs_server' - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'timeout' => '50', - 'deadtime' => '0', - 'encryption_password' => 'WXYZ12', - 'directed_request' => 'false', - 'source_interface' => 'Ethernet1/4' }, - false, self, logger) - end + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, + { 'ensure' => 'present', + 'timeout' => '50', + 'deadtime' => '0', + 'encryption_password' => add_quotes('WXYZ12'), + 'directed_request' => 'false', + 'source_interface' => 'Ethernet1/4' }, + false, self, logger) logger.info("Check cisco_tacacs_server presence on agent :: #{result}") end @@ -125,16 +125,16 @@ # Expected exit_code is 0 since this is a puppet resource cmd. # Flag is set to true to check for absence of RegExp pattern in stdout. cmd_str = PUPPET_BINPATH + 'resource cisco_tacacs_server' - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'timeout' => '50', - 'deadtime' => '0', - 'encryption_password' => 'WXYZ12', - 'directed_request' => 'false', - 'source_interface' => 'Ethernet1/4' }, - true, self, logger) - end + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, + { 'ensure' => 'present', + 'timeout' => '50', + 'deadtime' => '0', + 'encryption_password' => add_quotes('WXYZ12'), + 'directed_request' => 'false', + 'source_interface' => 'Ethernet1/4' }, + true, self, logger) logger.info("Check cisco_tacacs_server absence on agent :: #{result}") end diff --git a/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_nondefaults.rb b/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_nondefaults.rb index 56c03e3ae..0d9aabbec 100644 --- a/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_nondefaults.rb +++ b/tests/beaker_tests/cisco_tacacs_server_host/tacacsserverhost_provider_nondefaults.rb @@ -102,14 +102,14 @@ def cleanup # Expected exit_code is 0 since this is a puppet resource cmd. # Flag is set to false to check for presence of RegExp pattern in stdout. cmd_str = PUPPET_BINPATH + 'resource cisco_tacacs_server_host' - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'port' => '90', - 'timeout' => '39', - 'encryption_password' => 'test123' }, - false, self, logger) - end + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, + { 'ensure' => 'present', + 'port' => '90', + 'timeout' => '39', + 'encryption_password' => add_quotes('test123') }, + false, self, logger) logger.info("Check cisco_tacacs_server_host presence on agent :: #{result}") end @@ -131,14 +131,14 @@ def cleanup # Expected exit_code is 0 since this is a puppet resource cmd. # Flag is set to true to check for absence of RegExp pattern in stdout. cmd_str = PUPPET_BINPATH + 'resource cisco_tacacs_server_host' - on(agent, cmd_str) do - search_pattern_in_output(stdout, - { 'ensure' => 'present', - 'port' => '90', - 'timeout' => '39', - 'encryption_password' => 'test123' }, - true, self, logger) - end + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, + { 'ensure' => 'present', + 'port' => '90', + 'timeout' => '39', + 'encryption_password' => add_quotes('test123') }, + true, self, logger) logger.info("Check cisco_tacacs_server_host absence on agent :: #{result}") end diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index b574f5f61..8b3480155 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1105,8 +1105,9 @@ def image?(reset_cache=false) @image = nil # Cache the lookup result def nexus_image facter_opt = '-p cisco.images.system_image' - image_regexp = /[A-Z]+\d+\.\d+/ - @image ||= on(agent, facter_cmd(facter_opt)).output[image_regexp] + image_regexp = /.*\.(\S+\.\S+)\.bin/ + data = on(agent, facter_cmd(facter_opt)).output + @image ||= image_regexp.match(data)[1] end # On match will skip all testcases @@ -1436,3 +1437,19 @@ def test_patch_version(tests, id, name, ver) end end end + +# Add double quotes to string. +# +# Helper method to add a double quote to the beginning +# and end of a string. +# +# Nxapi adds an escape character to config lines that +# nvgen in this way in some but not all nxos releases. +# +# Input: String (Example 'foo') +# Returns: String with double quotes: (Example: '"foo"' +# +def add_quotes(string) + string = "\"#{string}\"" if image?[/8.0/] + string +end diff --git a/tests/beaker_tests/radius_global/radius_global_provider_defaults.rb b/tests/beaker_tests/radius_global/radius_global_provider_defaults.rb index d6d4f37b5..5dbc0ab18 100644 --- a/tests/beaker_tests/radius_global/radius_global_provider_defaults.rb +++ b/tests/beaker_tests/radius_global/radius_global_provider_defaults.rb @@ -82,16 +82,16 @@ # Expected exit_code is 0 since this is a puppet resource cmd. # Flag is set to false to check for presence of RegExp pattern in stdout. cmd_str = PUPPET_BINPATH + 'resource radius_global default' - on(agent, cmd_str) do - search_pattern_in_output(stdout, { 'key' => '44444444' }, - false, self, logger) - search_pattern_in_output(stdout, { 'key_format' => '7' }, - false, self, logger) - search_pattern_in_output(stdout, { 'retransmit_count' => '4' }, - false, self, logger) - search_pattern_in_output(stdout, { 'timeout' => '2' }, - false, self, logger) - end + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, { 'key' => add_quotes('44444444') }, + false, self, logger) + search_pattern_in_output(output, { 'key_format' => '7' }, + false, self, logger) + search_pattern_in_output(output, { 'retransmit_count' => '4' }, + false, self, logger) + search_pattern_in_output(output, { 'timeout' => '2' }, + false, self, logger) logger.info("Check radius_global resource presence on agent :: #{result}") end @@ -113,16 +113,16 @@ # Expected exit_code is 0 since this is a puppet resource cmd. # Flag is set to false to check for presence of RegExp pattern in stdout. cmd_str = PUPPET_BINPATH + 'resource radius_global default' - on(agent, cmd_str) do - search_pattern_in_output(stdout, { 'key' => '44444444' }, - false, self, logger) - search_pattern_in_output(stdout, { 'key_format' => '7' }, - false, self, logger) - search_pattern_in_output(stdout, { 'retransmit_count' => '3' }, - false, self, logger) - search_pattern_in_output(stdout, { 'timeout' => '1' }, - false, self, logger) - end + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, { 'key' => add_quotes('44444444') }, + false, self, logger) + search_pattern_in_output(output, { 'key_format' => '7' }, + false, self, logger) + search_pattern_in_output(output, { 'retransmit_count' => '3' }, + false, self, logger) + search_pattern_in_output(output, { 'timeout' => '1' }, + false, self, logger) logger.info("Check radius_global resource presence on agent :: #{result}") end diff --git a/tests/beaker_tests/radius_server/radius_server_provider_defaults.rb b/tests/beaker_tests/radius_server/radius_server_provider_defaults.rb index 6a37c6b67..393ef355e 100644 --- a/tests/beaker_tests/radius_server/radius_server_provider_defaults.rb +++ b/tests/beaker_tests/radius_server/radius_server_provider_defaults.rb @@ -83,26 +83,26 @@ # Expected exit_code is 0 since this is a puppet resource cmd. # Flag is set to false to check for presence of RegExp pattern in stdout. cmd_str = PUPPET_BINPATH + 'resource radius_server 8.8.8.8' - on(agent, cmd_str) do - search_pattern_in_output(stdout, { 'ensure' => 'present' }, - false, self, logger) - search_pattern_in_output(stdout, { 'accounting_only' => 'true' }, - false, self, logger) unless operating_system == 'ios_xr' - search_pattern_in_output(stdout, { 'acct_port' => '66' }, - false, self, logger) - search_pattern_in_output(stdout, { 'auth_port' => '77' }, - false, self, logger) - search_pattern_in_output(stdout, { 'authentication_only' => 'true' }, - false, self, logger) unless operating_system == 'ios_xr' - search_pattern_in_output(stdout, { 'key' => '44444444' }, - false, self, logger) - search_pattern_in_output(stdout, { 'key_format' => '7' }, - false, self, logger) - search_pattern_in_output(stdout, { 'retransmit_count' => '4' }, - false, self, logger) - search_pattern_in_output(stdout, { 'timeout' => '2' }, - false, self, logger) - end + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, { 'ensure' => 'present' }, + false, self, logger) + search_pattern_in_output(output, { 'accounting_only' => 'true' }, + false, self, logger) unless operating_system == 'ios_xr' + search_pattern_in_output(output, { 'acct_port' => '66' }, + false, self, logger) + search_pattern_in_output(output, { 'auth_port' => '77' }, + false, self, logger) + search_pattern_in_output(output, { 'authentication_only' => 'true' }, + false, self, logger) unless operating_system == 'ios_xr' + search_pattern_in_output(output, { 'key' => add_quotes('44444444') }, + false, self, logger) + search_pattern_in_output(output, { 'key_format' => '7' }, + false, self, logger) + search_pattern_in_output(output, { 'retransmit_count' => '4' }, + false, self, logger) + search_pattern_in_output(output, { 'timeout' => '2' }, + false, self, logger) logger.info("Check radius_server resource presence on agent :: #{result}") end @@ -190,26 +190,26 @@ # Expected exit_code is 0 since this is a puppet resource cmd. # Flag is set to false to check for presence of RegExp pattern in stdout. cmd_str = PUPPET_BINPATH + 'resource radius_server 2003::7' - on(agent, cmd_str) do - search_pattern_in_output(stdout, { 'ensure' => 'present' }, - false, self, logger) - search_pattern_in_output(stdout, { 'accounting_only' => 'true' }, - false, self, logger) unless operating_system == 'ios_xr' - search_pattern_in_output(stdout, { 'acct_port' => '66' }, - false, self, logger) - search_pattern_in_output(stdout, { 'auth_port' => '77' }, - false, self, logger) - search_pattern_in_output(stdout, { 'authentication_only' => 'true' }, - false, self, logger) unless operating_system == 'ios_xr' - search_pattern_in_output(stdout, { 'key' => '44444444' }, - false, self, logger) - search_pattern_in_output(stdout, { 'key_format' => '7' }, - false, self, logger) - search_pattern_in_output(stdout, { 'retransmit_count' => '4' }, - false, self, logger) - search_pattern_in_output(stdout, { 'timeout' => '2' }, - false, self, logger) - end + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, { 'ensure' => 'present' }, + false, self, logger) + search_pattern_in_output(output, { 'accounting_only' => 'true' }, + false, self, logger) unless operating_system == 'ios_xr' + search_pattern_in_output(output, { 'acct_port' => '66' }, + false, self, logger) + search_pattern_in_output(output, { 'auth_port' => '77' }, + false, self, logger) + search_pattern_in_output(output, { 'authentication_only' => 'true' }, + false, self, logger) unless operating_system == 'ios_xr' + search_pattern_in_output(output, { 'key' => add_quotes('44444444') }, + false, self, logger) + search_pattern_in_output(output, { 'key_format' => '7' }, + false, self, logger) + search_pattern_in_output(output, { 'retransmit_count' => '4' }, + false, self, logger) + search_pattern_in_output(output, { 'timeout' => '2' }, + false, self, logger) logger.info("Check radius_server resource presence on agent :: #{result}") end diff --git a/tests/beaker_tests/tacacs_global/tacacs_global_provider_defaults.rb b/tests/beaker_tests/tacacs_global/tacacs_global_provider_defaults.rb index ed8dd17b3..4acdfef1d 100644 --- a/tests/beaker_tests/tacacs_global/tacacs_global_provider_defaults.rb +++ b/tests/beaker_tests/tacacs_global/tacacs_global_provider_defaults.rb @@ -77,14 +77,14 @@ # Expected exit_code is 0 since this is a puppet resource cmd. # Flag is set to false to check for presence of RegExp pattern in stdout. cmd_str = PUPPET_BINPATH + 'resource tacacs_global default' - on(agent, cmd_str) do - search_pattern_in_output(stdout, { 'key' => '44444444' }, - false, self, logger) - search_pattern_in_output(stdout, { 'key_format' => '7' }, - false, self, logger) - search_pattern_in_output(stdout, { 'timeout' => '2' }, - false, self, logger) - end + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, { 'key' => add_quotes('44444444') }, + false, self, logger) + search_pattern_in_output(output, { 'key_format' => '7' }, + false, self, logger) + search_pattern_in_output(output, { 'timeout' => '2' }, + false, self, logger) logger.info("Check tacacs_global resource presence on agent :: #{result}") end @@ -106,14 +106,14 @@ # Expected exit_code is 0 since this is a puppet resource cmd. # Flag is set to false to check for presence of RegExp pattern in stdout. cmd_str = PUPPET_BINPATH + 'resource tacacs_global default' - on(agent, cmd_str) do - search_pattern_in_output(stdout, { 'key' => '44444444' }, - false, self, logger) - search_pattern_in_output(stdout, { 'key_format' => '7' }, - false, self, logger) - search_pattern_in_output(stdout, { 'timeout' => '1' }, - false, self, logger) - end + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, { 'key' => add_quotes('44444444') }, + false, self, logger) + search_pattern_in_output(output, { 'key_format' => '7' }, + false, self, logger) + search_pattern_in_output(output, { 'timeout' => '1' }, + false, self, logger) logger.info("Check tacacs_global resource presence on agent :: #{result}") end diff --git a/tests/beaker_tests/tacacs_server/tacacs_server_provider_defaults.rb b/tests/beaker_tests/tacacs_server/tacacs_server_provider_defaults.rb index 4b302bee8..bacef804f 100644 --- a/tests/beaker_tests/tacacs_server/tacacs_server_provider_defaults.rb +++ b/tests/beaker_tests/tacacs_server/tacacs_server_provider_defaults.rb @@ -81,18 +81,18 @@ def cleanup # Expected exit_code is 0 since this is a puppet resource cmd. # Flag is set to false to check for presence of RegExp pattern in stdout. cmd_str = PUPPET_BINPATH + 'resource tacacs_server 8.8.8.8' - on(agent, cmd_str) do - search_pattern_in_output(stdout, { 'ensure' => 'present' }, - false, self, logger) - search_pattern_in_output(stdout, { 'key' => '44444444' }, - false, self, logger) - search_pattern_in_output(stdout, { 'key_format' => '7' }, - false, self, logger) - search_pattern_in_output(stdout, { 'port' => '48' }, - false, self, logger) - search_pattern_in_output(stdout, { 'timeout' => '2' }, - false, self, logger) - end + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, { 'ensure' => 'present' }, + false, self, logger) + search_pattern_in_output(output, { 'key' => add_quotes('44444444') }, + false, self, logger) + search_pattern_in_output(output, { 'key_format' => '7' }, + false, self, logger) + search_pattern_in_output(output, { 'port' => '48' }, + false, self, logger) + search_pattern_in_output(output, { 'timeout' => '2' }, + false, self, logger) logger.info("Check tacacs_server resource presence on agent :: #{result}") end @@ -114,18 +114,18 @@ def cleanup # Expected exit_code is 0 since this is a puppet resource cmd. # Flag is set to false to check for presence of RegExp pattern in stdout. cmd_str = PUPPET_BINPATH + 'resource tacacs_server 8.8.8.8' - on(agent, cmd_str) do - search_pattern_in_output(stdout, { 'ensure' => 'present' }, - false, self, logger) - search_pattern_in_output(stdout, { 'key' => '44444444' }, - false, self, logger) - search_pattern_in_output(stdout, { 'key_format' => '7' }, - false, self, logger) - search_pattern_in_output(stdout, { 'port' => '47' }, - false, self, logger) - search_pattern_in_output(stdout, { 'timeout' => '3' }, - false, self, logger) - end + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, { 'ensure' => 'present' }, + false, self, logger) + search_pattern_in_output(output, { 'key' => add_quotes('44444444') }, + false, self, logger) + search_pattern_in_output(output, { 'key_format' => '7' }, + false, self, logger) + search_pattern_in_output(output, { 'port' => '47' }, + false, self, logger) + search_pattern_in_output(output, { 'timeout' => '3' }, + false, self, logger) logger.info("Check tacacs_server resource presence on agent :: #{result}") end @@ -172,18 +172,18 @@ def cleanup # Expected exit_code is 0 since this is a puppet resource cmd. # Flag is set to false to check for presence of RegExp pattern in stdout. cmd_str = PUPPET_BINPATH + 'resource tacacs_server 2020::20' - on(agent, cmd_str) do - search_pattern_in_output(stdout, { 'ensure' => 'present' }, - false, self, logger) - search_pattern_in_output(stdout, { 'key' => '44444444' }, - false, self, logger) - search_pattern_in_output(stdout, { 'key_format' => '7' }, - false, self, logger) - search_pattern_in_output(stdout, { 'port' => '48' }, - false, self, logger) - search_pattern_in_output(stdout, { 'timeout' => '2' }, - false, self, logger) - end + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, { 'ensure' => 'present' }, + false, self, logger) + search_pattern_in_output(output, { 'key' => add_quotes('44444444') }, + false, self, logger) + search_pattern_in_output(output, { 'key_format' => '7' }, + false, self, logger) + search_pattern_in_output(output, { 'port' => '48' }, + false, self, logger) + search_pattern_in_output(output, { 'timeout' => '2' }, + false, self, logger) logger.info("Check tacacs_server resource presence on agent :: #{result}") end From 80a7467f226aa61c6ce329dd78a24e0e7a5b8271 Mon Sep 17 00:00:00 2001 From: saichint Date: Fri, 2 Dec 2016 07:17:41 -0800 Subject: [PATCH 132/203] pim and interface bfd (#410) * pim bfd * doc --- CHANGELOG.md | 5 +++++ README.md | 15 ++++++++++++++- examples/cisco/demo_interface.pp | 1 + examples/cisco/demo_pim.pp | 3 ++- lib/puppet/provider/cisco_interface/cisco.rb | 1 + lib/puppet/provider/cisco_pim/cisco.rb | 9 +++++++++ lib/puppet/type/cisco_interface.rb | 7 +++++++ lib/puppet/type/cisco_pim.rb | 18 ++++++++++++++---- .../cisco_interface/test_interface_L3.rb | 4 ++++ tests/beaker_tests/cisco_pim/test_pim.rb | 10 ++++++---- 10 files changed, 63 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d105c43d8..dbae716d1 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `hsrp_mac_refresh` - `hsrp_use_bia` - `hsrp_version` + - `pim_bfd` + +- Extend cisco_pim with attributes: + - `bfd` + * Added support for Cisco NX-OS software release `7.3(0)F1(1)` ### Changed diff --git a/README.md b/README.md index fba71786c..aa911fdc5 100644 --- a/README.md +++ b/README.md @@ -429,7 +429,7 @@ Symbol | Meaning | Description | [cisco_ospf_vrf](#type-cisco_ospf_vrf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅ = Supported
:heavy_minus_sign: = Not Applicable | N9k | N3k | N5k | N6k | N7k | N9k-F | Caveats | | [cisco_overlay_global](#type-cisco_overlay_global) | ✅ | :heavy_minus_sign: | ✅ | ✅ | ✅ | ✅ | -| [cisco_pim](#type-cisco_pim) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_pim](#type-cisco_pim) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_pim-caveats) | | [cisco_pim_rp_address](#type-cisco_pim_rp_address) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_pim_grouplist](#type-cisco_pim_grouplist) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_portchannel_global](#type-cisco_portchannel_global) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_portchannel_global-caveats) | @@ -1984,6 +1984,7 @@ Manages a Cisco Network Interface. Any resource dependency should be run before | `hsrp_mac_refresh` | Not supported on N5k,N6k,N7k
Minimum puppet module version 1.5.0 | | `hsrp_use_bia` | Not supported on N5k,N6k,N7k
Minimum puppet module version 1.5.0 | | `hsrp_version` | Not supported on N5k,N6k,N7k
Minimum puppet module version 1.5.0 | +| `pim_bfd` | Minimum puppet module version 1.5.0 | #### Parameters @@ -2198,6 +2199,9 @@ ipv6_dhcp_relay_addr => ['2000::11', '2001::22'] ###### `ipv6_dhcp_relay_src_intf` Source interface for the DHCPV6 relay. Valid values are string, keyword 'default'. +###### `pim_bfd` +Enables PIM BFD on the interface. Valid values are 'true', 'false', and 'default'. + ###### `vlan_mapping` This property is a nested array of [original_vlan, translated_vlan] pairs. Valid values are an array specifying the mapped vlans or keyword 'default'; e.g.: @@ -3082,6 +3086,12 @@ Manages configuration of an Protocol Independent Multicast (PIM) instance. | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +#### Caveats + +| Property | Caveat Description | +|:---------|:-------------| +| `bfd` | Minimum puppet module version 1.5.0 | + #### Parameters ##### `afi` @@ -3092,6 +3102,9 @@ Name of the resource instance. Required. Valid values are string. The name 'defa #### Properties +##### `bfd` +Enables BFD for all PIM interfaces in the current VRF. Valid values are true, false or 'default'. + ##### `ssm_range` Configure group ranges for Source Specific Multicast (SSM). Valid values are multicast addresses or the keyword ‘none’. diff --git a/examples/cisco/demo_interface.pp b/examples/cisco/demo_interface.pp index 2b0b3a260..f78c79e22 100755 --- a/examples/cisco/demo_interface.pp +++ b/examples/cisco/demo_interface.pp @@ -66,6 +66,7 @@ ipv4_acl_out => 'v4acl2', ipv6_acl_in => 'v6acl1', ipv6_acl_out => 'v6acl2', + pim_bfd => true, } cisco_interface { 'Ethernet1/1.1': diff --git a/examples/cisco/demo_pim.pp b/examples/cisco/demo_pim.pp index c570e95ed..2518e289d 100644 --- a/examples/cisco/demo_pim.pp +++ b/examples/cisco/demo_pim.pp @@ -19,7 +19,8 @@ cisco_pim { 'ipv4' : ensure => present, vrf => 'default', - ssm_range => '224.0.0.0/8 225.0.0.0/8' + ssm_range => '224.0.0.0/8 225.0.0.0/8', + bfd => true } cisco_pim_rp_address { 'ipv4' : diff --git a/lib/puppet/provider/cisco_interface/cisco.rb b/lib/puppet/provider/cisco_interface/cisco.rb index 4a659344f..553ebbfaf 100644 --- a/lib/puppet/provider/cisco_interface/cisco.rb +++ b/lib/puppet/provider/cisco_interface/cisco.rb @@ -94,6 +94,7 @@ :ipv4_dhcp_relay_subnet_broadcast, :ipv4_dhcp_smart_relay, :negotiate_auto, + :pim_bfd, :shutdown, :switchport_autostate_exclude, :switchport_pvlan_host, diff --git a/lib/puppet/provider/cisco_pim/cisco.rb b/lib/puppet/provider/cisco_pim/cisco.rb index 733a9dc4d..e922b2d97 100644 --- a/lib/puppet/provider/cisco_pim/cisco.rb +++ b/lib/puppet/provider/cisco_pim/cisco.rb @@ -33,6 +33,7 @@ ] PIM_BOOL_PROPS = [ + :bfd ] PIM_ALL_PROPS = PIM_NON_BOOL_PROPS + PIM_BOOL_PROPS @@ -62,6 +63,14 @@ def self.properties_get(afi, vrf, nu_obj) PIM_NON_BOOL_PROPS.each do |prop| current_state[prop] = nu_obj.send(prop) end + PIM_BOOL_PROPS.each do |prop| + val = nu_obj.send(prop) + if val.nil? + current_state[prop] = nil + else + current_state[prop] = val ? :true : :false + end + end new(current_state) end # self.properties_get diff --git a/lib/puppet/type/cisco_interface.rb b/lib/puppet/type/cisco_interface.rb index 069ed6690..375ea1d18 100755 --- a/lib/puppet/type/cisco_interface.rb +++ b/lib/puppet/type/cisco_interface.rb @@ -64,6 +64,7 @@ ipv4_dhcp_smart_relay => true, ipv6_dhcp_relay_addr => ['2000::11', '2001::22'], ipv6_dhcp_relay_src_intf => 'ethernet 2/2', + pim_bfd => true, } cisco_interface { 'ethernet1/17' : stp_bpdufilter => 'enable', @@ -288,6 +289,12 @@ # Begin L3 interface config attributes # ######################################## + newproperty(:pim_bfd) do + desc 'Enables pim BFD on this interface.' + + newvalues(:true, :false, :default) + end # property pim_bfd + newproperty(:ipv4_pim_sparse_mode) do desc ' Enables or disables ipv4 pim sparse mode '\ 'on the interface.' diff --git a/lib/puppet/type/cisco_pim.rb b/lib/puppet/type/cisco_pim.rb index 0eb404367..5038108f1 100644 --- a/lib/puppet/type/cisco_pim.rb +++ b/lib/puppet/type/cisco_pim.rb @@ -43,7 +43,8 @@ ~~~puppet cisco_pim { 'ipv4 red' : ensure => present, - ssm_range => '224.0.0.0/8 225.0.0.0/8' + ssm_range => '224.0.0.0/8 225.0.0.0/8', + bfd => true } ~~~ @@ -54,7 +55,8 @@ ensure => present, afi => 'ipv4', vrf => 'default', - ssm_range => default + ssm_range => default, + bfd => true } ~~~ @@ -62,14 +64,16 @@ cisco_pim { 'ipv4' : ensure => present, vrf => 'default', - ssm_range => '224.0.0.0/8 225.0.0.0/8' + ssm_range => '224.0.0.0/8 225.0.0.0/8', + bfd => true } ~~~ ~~~puppet cisco_pim { 'ipv4 blue' : ensure => present, - ssm_range => '224.0.0.0/8 225.0.0.0/8' + ssm_range => '224.0.0.0/8 225.0.0.0/8', + bfd => true } ~~~ " @@ -134,6 +138,12 @@ def name # Definition of properties. # --------------------------------------------------------------- + newproperty(:bfd) do + desc 'Enables BFD for all PIM interfaces in the VRF.' + + newvalues(:true, :false, :default) + end # property bfd + newproperty(:ssm_range) do desc "Sets the ssm range for Pim. Valid values are space-separated String diff --git a/tests/beaker_tests/cisco_interface/test_interface_L3.rb b/tests/beaker_tests/cisco_interface/test_interface_L3.rb index a04a63298..c5e15701b 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_L3.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_L3.rb @@ -62,6 +62,7 @@ ipv4_dhcp_smart_relay: 'default', ipv6_dhcp_relay_addr: 'default', ipv6_dhcp_relay_src_intf: 'default', + pim_bfd: 'default', mtu: 'default', shutdown: 'default', vrf: 'default', @@ -78,6 +79,7 @@ ipv4_dhcp_relay_subnet_broadcast: 'false', ipv4_dhcp_smart_relay: 'false', ipv6_dhcp_relay_src_intf: 'false', + pim_bfd: 'false', mtu: operating_system == 'nexus' ? '1500' : '1514', shutdown: 'false', }, @@ -117,6 +119,7 @@ ipv4_dhcp_smart_relay: 'true', ipv6_dhcp_relay_addr: v6_relay, ipv6_dhcp_relay_src_intf: 'ethernet1/1', + pim_bfd: true, switchport_mode: 'disabled', vrf: 'test1', }, @@ -170,6 +173,7 @@ def unsupported_properties(_tests, id) :ipv4_dhcp_smart_relay << :ipv6_dhcp_relay_addr << :ipv6_dhcp_relay_src_intf << + :pim_bfd << :switchport_mode end diff --git a/tests/beaker_tests/cisco_pim/test_pim.rb b/tests/beaker_tests/cisco_pim/test_pim.rb index c6e5812e7..d5bab0555 100644 --- a/tests/beaker_tests/cisco_pim/test_pim.rb +++ b/tests/beaker_tests/cisco_pim/test_pim.rb @@ -25,9 +25,10 @@ # Test hash top-level keys tests = { - master: master, - agent: agent, - resource_name: 'cisco_pim', + master: master, + agent: agent, + operating_system: 'nexus', + resource_name: 'cisco_pim', } # Test hash test cases @@ -35,7 +36,8 @@ desc: ' 1.1 Non default properties', title_pattern: 'ipv4 red', manifest_props: { - ssm_range: '224.0.0.0/8' + bfd: true, + ssm_range: '224.0.0.0/8', }, } From e142f3b2b395f13679da1553f0aa658f9093c1bd Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Mon, 5 Dec 2016 10:55:49 -0500 Subject: [PATCH 133/203] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbae716d1..6a92baabd 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Extend cisco_pim with attributes: - `bfd` -* Added support for Cisco NX-OS software release `7.3(0)F1(1)` +* Added support for Cisco NX-OS software releases `7.3(0)F1(1)` and `8.0(1)` ### Changed From 4ad43bbd54f0d4cde7b62deccb5bb8471d600f3b Mon Sep 17 00:00:00 2001 From: saichint Date: Thu, 8 Dec 2016 12:31:44 -0800 Subject: [PATCH 134/203] n7k atherton hsrp (#411) * pim bfd * doc * fix n7k atherton hsrp * review comments for doc * review comments for doc --- README.md | 24 ++++++++++++------- examples/cisco/demo_hsrp.pp | 19 +++++++++++++-- .../cisco_hsrp/test_interface_hsrp.rb | 3 ++- .../cisco_hsrp/test_interface_hsrp_group.rb | 6 +++-- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index aa911fdc5..d2d193d0f 100644 --- a/README.md +++ b/README.md @@ -418,7 +418,7 @@ Symbol | Meaning | Description | [cisco_hsrp_global](#type-cisco_hsrp_global) | ✅ | ✅* | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_hsrp_global-caveats) | | [cisco_interface](#type-cisco_interface) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_interface-caveats) | | [cisco_interface_channel_group](#type-cisco_interface_channel_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_interface_hsrp_group](#type-cisco_interface_hsrp_group) | ✅ | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | +| [cisco_interface_hsrp_group](#type-cisco_interface_hsrp_group) | ✅ | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | ✅* | ✅ | \*[caveats](#cisco_interface_hsrp_group-caveats) | | [cisco_interface_ospf](#type-cisco_interface_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_interface_portchannel](#type-cisco_interface_portchannel) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_interface_portchannel-caveats) | | [cisco_interface_service_vni](#type-cisco_interface_service_vni) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | @@ -1978,12 +1978,12 @@ Manages a Cisco Network Interface. Any resource dependency should be run before | `svi_autostate` | Only supported on N3k,N7k,N9k | | `vlan_mapping` | Only supported on N7k | | `vlan_mapping_enable` | Only supported on N7k | -| `hsrp_bfd` | Not supported on N5k,N6k,N7k
Minimum puppet module version 1.5.0 | -| `hsrp_delay_minimum` | Not supported on N5k,N6k,N7k
Minimum puppet module version 1.5.0 | -| `hsrp_delay_reload` | Not supported on N5k,N6k,N7k
Minimum puppet module version 1.5.0 | -| `hsrp_mac_refresh` | Not supported on N5k,N6k,N7k
Minimum puppet module version 1.5.0 | -| `hsrp_use_bia` | Not supported on N5k,N6k,N7k
Minimum puppet module version 1.5.0 | -| `hsrp_version` | Not supported on N5k,N6k,N7k
Minimum puppet module version 1.5.0 | +| `hsrp_bfd` | Not supported on N5k,N6k
Minimum puppet module version 1.5.0
Supported in OS Version 8.0 and later on N7k | +| `hsrp_delay_minimum` | Not supported on N5k,N6k
Minimum puppet module version 1.5.0
Supported in OS Version 8.0 and later on N7k | +| `hsrp_delay_reload` | Not supported on N5k,N6k
Minimum puppet module version 1.5.0
Supported in OS Version 8.0 and later on N7k | +| `hsrp_mac_refresh` | Not supported on N5k,N6k
Minimum puppet module version 1.5.0
Supported in OS Version 8.0 and later on N7k | +| `hsrp_use_bia` | Not supported on N5k,N6k
Minimum puppet module version 1.5.0
Supported in OS Version 8.0 and later on N7k | +| `hsrp_version` | Not supported on N5k,N6k
Minimum puppet module version 1.5.0
Supported in OS Version 8.0 and later on N7k | | `pim_bfd` | Minimum puppet module version 1.5.0 | #### Parameters @@ -2335,12 +2335,18 @@ Manages a Cisco Network Interface HSRP group. | N3k | 7.0(3)I2(1) | 1.5.0 | | N5k | not applicable | not applicable | | N6k | not applicable | not applicable | -| N7k | not applicable | not applicable | +| N7k | 8.0 | 1.5.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +#### Caveats + +| Property | Caveat Description | +|:---------|:-------------| +| `all properties` | Supported in OS Version 8.0 and later on N7k | + #### Parameters -##### Basic interface channel-group config attributes +##### Interface hsrp-group config attributes ###### `ensure` Determine whether the interface hsrp group config should be present or not. Valid values are 'present' and 'absent'. diff --git a/examples/cisco/demo_hsrp.pp b/examples/cisco/demo_hsrp.pp index 56d365aeb..3c6484884 100644 --- a/examples/cisco/demo_hsrp.pp +++ b/examples/cisco/demo_hsrp.pp @@ -26,7 +26,16 @@ extended_hold => 200, } - if platform_get() =~ /(n3k|n9k)/ { + $n7k_ath = platform_get() ? { + 'n7k' => $facts['cisco']['images']['system_image'] ? { + /(8.0)/ => true, + default => false + }, + /(n3k|n9k)/ => true, + default => false + } + + if $n7k_ath { cisco_interface { 'port-channel100': ensure => 'present', switchport_mode => 'disabled', @@ -47,6 +56,12 @@ hsrp_version => 2, } + cisco_command_config { 'ipv6-addr': + command => " + interface Po100 + ipv6 address 2000::01/64 + " + } cisco_interface_hsrp_group { 'port-channel100 2 ipv6': ensure => 'present', authentication_auth_type => 'md5', @@ -55,7 +70,7 @@ authentication_enc_type => 'encrypted', authentication_compatibility => true, authentication_timeout => 200, - ipv6_vip => ['2000::11', '2001::22'], + ipv6_vip => ['2000::11', '2000::22'], ipv6_autoconfig => true, group_name => 'MyHsrp', preempt => true, diff --git a/tests/beaker_tests/cisco_hsrp/test_interface_hsrp.rb b/tests/beaker_tests/cisco_hsrp/test_interface_hsrp.rb index 234b82bbf..81a217488 100644 --- a/tests/beaker_tests/cisco_hsrp/test_interface_hsrp.rb +++ b/tests/beaker_tests/cisco_hsrp/test_interface_hsrp.rb @@ -29,12 +29,13 @@ master: master, agent: agent, intf_type: 'port-channel', - platform: 'n(3|9)k', + platform: 'n(3|7|9)k', resource_name: 'cisco_interface', } # Skip -ALL- tests if a top-level platform/os key exludes this platform skip_unless_supported(tests) +skip_nexus_image('D1', tests) # Find a usable interface for this test @intf = 'port-channel100' diff --git a/tests/beaker_tests/cisco_hsrp/test_interface_hsrp_group.rb b/tests/beaker_tests/cisco_hsrp/test_interface_hsrp_group.rb index c7985c790..a03bc87d0 100644 --- a/tests/beaker_tests/cisco_hsrp/test_interface_hsrp_group.rb +++ b/tests/beaker_tests/cisco_hsrp/test_interface_hsrp_group.rb @@ -29,12 +29,13 @@ master: master, agent: agent, intf_type: 'port-channel', - platform: 'n(3|9)k', + platform: 'n(3|7|9)k', resource_name: 'cisco_interface_hsrp_group', } # Skip -ALL- tests if a top-level platform/os key exludes this platform skip_unless_supported(tests) +skip_nexus_image('D1', tests) # Test hash test cases tests[:default_v4] = { @@ -180,7 +181,7 @@ authentication_timeout: 200, group_name: 'MyHsrpv6', ipv6_autoconfig: 'true', - ipv6_vip: ['2000::11', '2001::22'], + ipv6_vip: ['2000::11', '2000::22'], mac_addr: '00:00:11:11:22:22', preempt: 'true', preempt_delay_minimum: '100', @@ -215,6 +216,7 @@ def test_harness_dependencies(_tests, _id) 'feature hsrp', 'interface port-channel100 ; no switchport ; hsrp version 2', 'interface port-channel200 ; no switchport ; hsrp version 2', + 'interface port-channel200 ; ipv6 address 2000::01/64', ].join(' ; ') test_set(agent, cmd) end From 84327f2edc883a1b85d5220c5cf245382c12e281 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Tue, 24 Jan 2017 09:23:22 -0500 Subject: [PATCH 135/203] Add ('--') separator for /bin/nsenter example. Resolves https://github.com/cisco/cisco-network-puppet-module/issues/413 --- docs/README-agent-install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README-agent-install.md b/docs/README-agent-install.md index 87a2f28c9..7d977e272 100644 --- a/docs/README-agent-install.md +++ b/docs/README-agent-install.md @@ -493,7 +493,7 @@ The `guestshell` environment uses **systemd** for service management. The Puppet EnvironmentFile=-/etc/sysconfig/puppet EnvironmentFile=-/etc/default/puppet -ExecStart=/opt/puppetlabs/puppet/bin/puppet agent $PUPPET_EXTRA_OPTS --no-daemonize -+ExecStart=/bin/nsenter --net=/var/run/netns/management /opt/puppetlabs/puppet/bin/puppet agent $PUPPET_EXTRA_OPTS --no-daemonize ++ExecStart=/bin/nsenter --net=/var/run/netns/management -- /opt/puppetlabs/puppet/bin/puppet agent $PUPPET_EXTRA_OPTS --no-daemonize KillMode=process [Install] From 440e4e36b1d89325f1b3135ecae953979ce207e3 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Tue, 24 Jan 2017 09:25:50 -0500 Subject: [PATCH 136/203] Update Copyright date --- docs/README-agent-install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README-agent-install.md b/docs/README-agent-install.md index 7d977e272..d58184bb4 100644 --- a/docs/README-agent-install.md +++ b/docs/README-agent-install.md @@ -535,7 +535,7 @@ A virtual Nexus N9k may be helpful for development and testing. To obtain a virt ## License ~~~ -Copyright (c) 2014-2016 Cisco and/or its affiliates. +Copyright (c) 2014-2017 Cisco and/or its affiliates. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 8181e43687c2bed53c7206060a5c22c92a968970 Mon Sep 17 00:00:00 2001 From: saichint Date: Tue, 24 Jan 2017 11:29:41 -0800 Subject: [PATCH 137/203] fix the case sensitive issue for int_hsrp_group (#416) --- lib/puppet/type/cisco_interface_hsrp_group.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/puppet/type/cisco_interface_hsrp_group.rb b/lib/puppet/type/cisco_interface_hsrp_group.rb index 31bcbdc44..17733787a 100644 --- a/lib/puppet/type/cisco_interface_hsrp_group.rb +++ b/lib/puppet/type/cisco_interface_hsrp_group.rb @@ -103,6 +103,7 @@ def name newparam(:interface, namevar: true) do desc 'Name of the interface instance. Valid values are string.' + munge(&:downcase) end # param interface ############## From 98d17a7dba20ef496cf7e5b359d3a153a4a205dd Mon Sep 17 00:00:00 2001 From: saichint Date: Tue, 7 Feb 2017 11:39:25 -0800 Subject: [PATCH 138/203] cisco route map (#418) * pim bfd * doc * fix n7k atherton hsrp * review comments for doc * review comments for doc * partial new code * rest of the code * manifest * fix get issue * fix few errors in manifest * fix manifest * fix few issues * fix next-hop sets * fix manifest * fix few issues * fix vlan string munge * beaker tests * fix manifest * fix beaker * fix case sensitivity for int_hsrp_group name * remove downcase from provider * fixes for fretta * doc * remove 2 props from I4 code * review comments * review comments * fix doc * review comments * add validate for multicast props --- CHANGELOG.md | 2 + README.md | 429 +++++ examples/cisco/demo_route_map.pp | 401 ++++ examples/demo_all_cisco.pp | 1 + lib/puppet/provider/cisco_route_map/cisco.rb | 557 ++++++ lib/puppet/type/cisco_route_map.rb | 1659 +++++++++++++++++ .../cisco_route_map/test_route_map.rb | 575 ++++++ 7 files changed, 3624 insertions(+) create mode 100644 examples/cisco/demo_route_map.pp create mode 100644 lib/puppet/provider/cisco_route_map/cisco.rb create mode 100644 lib/puppet/type/cisco_route_map.rb create mode 100644 tests/beaker_tests/cisco_route_map/test_route_map.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index ca87a50c0..2688f7838 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### New feature support +#### Cisco Resources +- `cisco_route_map` type and provider. ### Added diff --git a/README.md b/README.md index d2d193d0f..6a659a7b1 100644 --- a/README.md +++ b/README.md @@ -232,6 +232,9 @@ The following resources include cisco types and providers along with cisco provi * [`radius_server (netdev_stdlib)`](#type-radius_server) * [`radius_server_group (netdev_stdlib)`](#type-radius_server_group) +* RouteMap Types + * [`cisco_route_map`](#type-cisco_route_map) + * STP Types * [`cisco_stp_global`](#type-cisco_stp_global) @@ -323,6 +326,7 @@ The following resources include cisco types and providers along with cisco provi * [`cisco_pim_grouplist`](#type-cisco_pim_grouplist) * [`cisco_pim_rp_address`](#type-cisco_pim_rp_address) * [`cisco_portchannel_global`](#type-cisco_portchannel_global) +* [`cisco_route_map`](#type-cisco_route_map) * [`cisco_stp_global`](#type-cisco_stp_global) * [`cisco_snmp_community`](#type-cisco_snmp_community) * [`cisco_snmp_group`](#type-cisco_snmp_group) @@ -433,6 +437,7 @@ Symbol | Meaning | Description | [cisco_pim_rp_address](#type-cisco_pim_rp_address) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_pim_grouplist](#type-cisco_pim_grouplist) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_portchannel_global](#type-cisco_portchannel_global) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_portchannel_global-caveats) | +| [cisco_route_map](#type-cisco_route_map) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_route_map-caveats) | | [cisco_stp_global](#type-cisco_stp_global) | ✅* | ✅* | ✅* | ✅* | ✅ | ✅ | \*[caveats](#cisco_stp_global-caveats) | | [cisco_snmp_community](#type-cisco_snmp_community) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_snmp_group](#type-cisco_snmp_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | @@ -3223,6 +3228,430 @@ port-channel hash input offset. Valid values are integer or 'default'. ##### `symmetry` port-channel symmetry hash. Valid values are true, false or 'default'. +-- +### Type: cisco_route_map + +Manages a Cisco Route Map. + +| Platform | OS Minimum Version | Module Minimum Version | +|----------|:------------------:|:----------------------:| +| N9k | 7.0(3)I2(1) | 1.6.0 | +| N3k | 7.0(3)I2(1) | 1.6.0 | +| N5k | 7.3(0)N1(1) | 1.6.0 | +| N6k | 7.3(0)N1(1) | 1.6.0 | +| N7k | 7.3(0)D1(1) | 1.6.0 | +| N9k-F | 7.0(3)F1(1) | 1.6.0 | + +#### Caveats + +| Property | Caveat Description | +|:---------|:-------------| +| `match_evpn_route_type_1` | Not supported on N3k,N9k-F,N9k | +| `match_evpn_route_type_2_all` | Not supported on N3k,N9k-F,N9k | +| `match_evpn_route_type_2_mac_ip` | Not supported on N3k,N9k-F,N9k | +| `match_evpn_route_type_2_mac_only` | Not supported on N3k,N9k-F,N9k | +| `match_evpn_route_type_3` | Not supported on N3k,N9k-F,N9k | +| `match_evpn_route_type_4` | Not supported on N3k,N9k-F,N9k | +| `match_evpn_route_type_5` | Not supported on N3k,N9k-F,N9k | +| `match_evpn_route_type_6` | Not supported on N3k,N9k-F,N9k | +| `match_evpn_route_type_all` | Not supported on N3k,N9k-F,N9k | +| `match_length` | Not supported on N3k,N9k-F,N9k | +| `match_mac_list` | Not supported on N3k,N9k-F,N9k | +| `match_metric` | Not supported on N9k-F | +| `match_ospf_area` | Not supported on N5k,N6k,N7k,N9k-F
Supported in OS version 7.0(3)I5.1 and later on N3k, N9k | +| `match_vlan` | Not supported on N3k,N9k-F,N9k | +| `set_extcommunity_4bytes_additive` | Not supported on N9k-F | +| `set_extcommunity_4bytes_non_transitive` | Not supported on N9k-F | +| `set_extcommunity_4bytes_transitive` | Not supported on N9k-F | +| `set_extcommunity_cost_igp` | Not supported on N9k-F | +| `set_extcommunity_cost_pre_bestpath` | Not supported on N9k-F | +| `set_extcommunity_rt_additive` | Not supported on N9k-F | +| `set_extcommunity_rt_asn` | Not supported on N9k-F,N9k | +| `set_forwarding_addr` | Not supported on N9k-F | +| `set_ipv4_default_next_hop` | Not supported on N5k,N6k,N9k-F,N9k | +| `set_ipv4_default_next_hop_load_share` | Not supported on N5k,N6k,N9k-F,N9k | +| `set_ipv4_next_hop` | Not supported on N9k-F | +| `set_ipv4_next_hop_load_share` | Not supported on N5k,N6k,N9k-F
Supported in OS Version 7.0(3)I5.1 and later on N9k | +| `set_ipv4_next_hop_redist` | Supported on N5k,N6k,N7k,N9k-F
Supported in OS Version 7.0(3)I5.1 and later on N3k,N9k | +| `set_ipv4_precedence` | Not supported on N9k-F | +| `set_ipv4_prefix` | Not supported on N5k,N6k,N9k-F | +OS Version 7.0(3)I5.1 and later on N3k,N9k | +| `set_ipv6_default_next_hop` | Not supported on N5k,N6k,N9k-F,N9k | +| `set_ipv6_default_next_hop_load_share` | Not supported on N5k,N6k,N9k-F,N9k | +| `set_ipv6_next_hop` | Not supported on N9k-F | +| `set_ipv6_next_hop_load_share` | Not supported on N5k,N6k,N9k-F
Supported in OS Version 7.0(3)I5.1 and later on N9k | +| `set_ipv6_next_hop_redist` | Supported on N5k,N6k,N7k,N9k-F
Supported in OS Version 7.0(3)I5.1 and later on N3k,N9k | +| `set_ipv6_prefix` | Not supported on N5k,N6k,N9k-F | +| `set_vrf` | Supported on N7k | + + +| Example Parameter Usage | +|:-- +|`match_as_number { ', -, ..':` +|`match_as_number { '['3', '22-34', '38', '101-110', '120']':` + +##### Basic interface config attributes + +###### `ensure` +Determine whether the route map config should be present or not. Valid values +are 'present' and 'absent'. + + +##### `description` +Description of the route-map. Valid values are string, or keyword 'default' + +##### `match_as_number` +Match BGP peer AS number. Valid values are an array of ranges or keyword 'default' + + +##### `match_as_number_as_path_list` +Match BGP AS path list. Valid values are an array of list names or keyword 'default' + +##### `match_community` +Match BGP community list. Valid values are an array of communities or keyword 'default' + +##### `match_community_exact_match` +Enable exact matching of communities. Valid values 'true', 'false' or keyword 'default' + +##### `match_evpn_route_type_1` +Enable match BGP EVPN route type-1. Valid values are 'true', 'false' or keyword 'default' + +##### `match_evpn_route_type_2_all` +Enable match all BGP EVPN route in type-2. Valid values are 'true', false or keyword 'default' + +##### `match_evpn_route_type_2_mac_ip` +Enable match mac-ip BGP EVPN route in type-2. Valid values are 'true', 'false' or keyword 'default' + +##### `match_evpn_route_type_2_mac_only` +Enable match mac-only BGP EVPN route in type-2. Valid values are 'true', 'false' or keyword 'default' + +##### `match_evpn_route_type_3` +Enable match BGP EVPN route type-3. Valid values are 'true', 'false' or keyword 'default' + +##### `match_evpn_route_type_4` +Enable match BGP EVPN route type-4. Valid values are 'true', 'false' or keyword 'default' + +##### `match_evpn_route_type_5` +Enable match BGP EVPN route type-5. Valid values are 'true', 'false' or keyword 'default' + +##### `match_evpn_route_type_6` +Enable match BGP EVPN route type-6. Valid values are 'true', 'false' or keyword 'default' + +##### `match_evpn_route_type_all` +Enable match BGP EVPN route type 1-6. Valid values are 'true', 'false' or keyword 'default' + +##### `match_ext_community` +Match BGP extended community list. Valid values are an array of extended communities or keyword 'default' + +##### `match_ext_community_exact_match` +Enable exact matching of extended communities. Valid values are 'true', 'false' or keyword 'default' + +##### `match_interface` +Match first hop interface of route. Valid values are array of interfaces or keyword 'default' + +##### `match_ipv4_addr_access_list` +Match IPv4 access-list name. Valid values are String or keyword 'default' + +##### `match_ipv4_addr_prefix_list` +Match entries of prefix-lists for IPv4. Valid values are array of prefixes or keyword 'default' + +##### `match_ipv4_multicast_enable` +Enable match IPv4 multicast. This property should be set to 'true' before setting any IPv4 multicast properties. Valid values are are 'true', 'false' or keyword 'default' + +##### `match_ipv4_multicast_group_addr` +Match IPv4 multicast group prefix. Valid values are string, or keyword 'default' + +##### `match_ipv4_multicast_group_range_begin_addr` +Match IPv4 multicast group address begin range. Valid values are string, or keyword 'default' + +##### `match_ipv4_multicast_group_range_end_addr` +Match IPv4 multicast group address end range. Valid values are string, or keyword 'default' + +##### `match_ipv4_multicast_rp_addr` +Match IPv4 multicast rendezvous prefix. Valid values are string, or keyword 'default' + +##### `match_ipv4_multicast_rp_type` +Match IPv4 multicast rendezvous point type. Valid values are 'ASM', 'Bidir' or keyword 'default' + +##### `match_ipv4_multicast_src_addr` +Match IPv4 multicast source prefix. Valid values are string or keyword 'default' + +##### `match_ipv4_next_hop_prefix_list` +Match entries of prefix-lists for next-hop address of route for IPv4. Valid values are an array of prefixes or keyword 'default' + +##### `match_ipv4_route_src_prefix_list` +Match entries of prefix-lists for advertising source address of route for IPv4. Valid values are an array of prefixes or keyword 'default' + +##### `match_ipv6_addr_access_list` +Match IPv6 access-list name. Valid values are string or keyword 'default' + +##### `match_ipv6_addr_prefix_list` +Match entries of prefix-lists for IPv6. Valid values are array of prefixes or keyword 'default' + +##### `match_ipv6_multicast_enable` +Enable match IPv6 multicast. This property should be set to 'true' before setting any IPv6 multicast properties. Valid values are 'true', 'false' or keyword 'default' + +##### `match_ipv6_multicast_group_addr` +Match IPv6 multicast group prefix. Valid values are string, or keyword 'default' + +##### `match_ipv6_multicast_group_range_begin_addr` +Match IPv6 multicast group address begin range. Valid values are string, or keyword 'default' + +##### `match_ipv6_multicast_group_range_end_addr` +Match IPv6 multicast group address end range. Valid values are string, or keyword 'default' + +##### `match_ipv6_multicast_rp_addr` +Match IPv6 multicast rendezvous prefix. Valid values are string, or keyword 'default' + +##### `match_ipv6_multicast_rp_type` +Match IPv6 multicast rendezvous point type. Valid values are 'ASM', 'Bidir' or keyword 'default' + +##### `match_ipv6_multicast_src_addr` +Match IPv6 multicast source prefix. Valid values are string or keyword 'default' + +##### `match_ipv6_next_hop_prefix_list` +Match entries of prefix-lists for next-hop address of route for IPv6. Valid values are array of prefixes or keyword 'default' + +##### `match_ipv6_route_src_prefix_list` +Match entries of prefix-lists for advertising source address of route for IPv6. Valid values are array of prefixes or keyword 'default' + +##### `match_length` +Match packet length. Valid values are array of minimum and maximum lengths or keyword 'default' + +##### `match_mac_list` +Match entries of mac-lists. Valid values are array of mac list names or keyword 'default' + +##### `match_metric` +Match metric of route. Valid values are array of [metric, deviation] pairs or keyword 'default' + +##### `match_ospf_area` +Match entries of ospf area IDs. Valid values are array of ids or keyword 'default' + +##### `match_route_type_external` +Enable match external route type (BGP, EIGRP and OSPF type 1/2). Valid values are 'true', 'false' or keyword 'default' + +##### `match_route_type_inter_area` +Enable match OSPF inter area type. Valid values are 'true', 'false' or keyword 'default' + +##### `match_route_type_internal` +Enable match OSPF inter area type (OSPF intra/inter area). Valid values are 'true', 'false' or keyword 'default' + +##### `match_route_type_intra_area` +Enable match OSPF intra area route. Valid values are 'true', 'false' or keyword 'default' + +##### `match_route_type_level_1` +Enable match IS-IS level-1 route. Valid values are 'true', 'false' or keyword 'default' + +##### `match_route_type_level_2` +Enable match IS-IS level-2 route. Valid values are 'true', 'false' or keyword 'default' + +##### `match_route_type_local` +Enable match locally generated route. Valid values are 'true', 'false' or keyword 'default' + +##### `match_route_type_nssa_external` +Enable match nssa-external route (OSPF type 1/2). Valid values are 'true', 'false' or keyword 'default' + +##### `match_route_type_type_1` +Enable match OSPF external type 1 route. Valid values are 'true', 'false' or keyword 'default' + +##### `match_route_type_type_2` +Enable match OSPF external type 2 route. Valid values are 'true', 'false' or keyword 'default' + +##### `match_src_proto` +Match source protocol. Valid values are array of protocols or keyword 'default' + +##### `match_tag` +Match tag of route. Valid values are array of tags or keyword 'default' + +##### `match_vlan` +Match VLAN Id. Valid values are array of string of VLAN ranges or keyword 'default' + +##### `set_as_path_prepend` +Prepend string for a BGP AS-path attribute. Valid values are array of AS numbers or keyword 'default' + +##### `set_as_path_prepend_last_as` +Number of last-AS prepends. Valid values are integer or keyword 'default' + +##### `set_as_path_tag` +Set the tag as an AS-path attribute. Valid values are 'true', 'false' or keyword 'default' + +##### `set_comm_list` +Set BGP community list (for deletion). Valid values are String or keyword 'default' + +##### `set_community_additive` +Add to existing BGP community. Valid values are 'true', 'false' or keyword 'default' + +##### `set_community_asn` +Set community number. Valid values are array of AS numbers or keyword 'default' + +##### `set_community_internet` +Set Internet community. Valid values are 'true', 'false' or keyword 'default' + +##### `set_community_local_as` +Do not send outside local AS. Valid values are 'true', 'false' or keyword 'default' + +##### `set_community_no_advtertise` +Do not advertise to any peer. Valid values are 'true', 'false' or keyword 'default' + +##### `set_community_no_export` +Do not export to next AS. Valid values are 'true', 'false' or keyword 'default' + +##### `set_community_none` +Set no community attribute. Valid values are 'true', 'false' or keyword 'default' + +##### `set_dampening_half_life` +Set half-life time for the penalty of BGP route flap dampening. Valid values are integer or keyword 'default' + +##### `set_dampening_max_duation` +Set maximum duration to suppress a stable route of BGP route flap dampening. Valid values are integer or keyword 'default' + +##### `set_dampening_reuse` +Set penalty to start reusing a route of BGP route flap dampening. Valid values are integer or keyword 'default' + +##### `set_dampening_suppress` +Set penalty to start suppressing a route of BGP route flap dampening. Valid values are integer or keyword 'default' + +##### `set_distance_igp_ebgp` +Set administrative distance for IGP or EBGP routes. Valid values are integer or keyword 'default' + +##### `set_distance_internal` +Set administrative distance for internal routes. Valid values are integer or keyword 'default' + +##### `set_distance_local` +Set administrative distance for local routes. Valid values are integer or keyword 'default' + +##### `set_extcomm_list` +Set BGP extended community list (for deletion). Valid values are string or keyword 'default' + +##### `set_extcommunity_4bytes_additive` +Add to existing generic extcommunity. Valid values are 'true', 'false' or keyword 'default' + +##### `set_extcommunity_4bytes_non_transitive` +Set non-transitive extended community. Valid values are array of communities, or keyword 'default' + +##### `set_extcommunity_4bytes_none` +Set no extcommunity generic attribute. Valid values are 'true', 'false' or keyword 'default' + +##### `set_extcommunity_4bytes_transitive` +Set transitive extended community. Valid values are array of communities, or keyword 'default' + +##### `set_extcommunity_cost_igp` +Compare following IGP cost comparison. Valid values are array of [communityId, cost] pairs or keyword 'default' + +##### `set_extcommunity_cost_pre_bestpath` +Compare before all other steps in bestpath calculation. Valid values are array of [communityId, cost] pairs or keyword 'default' + +##### `set_extcommunity_rt_additive` +Set add to existing route target extcommunity. Valid values are 'true', 'false' or keyword 'default' + +##### `set_extcommunity_rt_asn` +Set community number. Valid values are array of AS numbers or keyword 'default' + +##### `set_forwarding_addr` +Set the forwarding address. Valid values are 'true', 'false' or keyword 'default' + +##### `set_interface` +Set output interface. Valid values are 'Null0' or keyword 'default' + +##### `set_ipv4_default_next_hop` +Set default next-hop IPv4 address. Valid values are array of next hops or keyword 'default' + +##### `set_ipv4_default_next_hop_load_share` +Enable default IPv4 next-hop load-sharing. Valid values are 'true', 'false' or keyword 'default' + +##### `set_ipv4_next_hop` +Set next-hop IPv4 address. Valid values are array of next hops or keyword 'default' + +##### `set_ipv4_next_hop_load_share` +Enable IPv4 next-hop load-sharing. Valid values are 'true', 'false' or keyword 'default' + +##### `set_ipv4_next_hop_peer_addr` +Enable IPv4 next-hop peer address. Valid values are 'true', 'false' or keyword 'default' + +##### `set_ipv4_next_hop_redist` +Enable IPv4 next-hop unchanged address during redistribution. Valid values are 'true', 'false' or keyword 'default' + +##### `set_ipv4_next_hop_unchanged` +Enable IPv4 next-hop unchanged address. Valid values are 'true', 'false' or keyword 'default' + +##### `set_ipv4_precedence` +Set IPv4 precedence field. Valid values are 'critical', 'flash', 'flash-override', 'immediate', 'internet', 'network', 'priority', 'routine' or keyword 'default' + +##### `set_ipv4_prefix` +Set IPv4 prefix-list. Valid values are string or keyword 'default' + +##### `set_ipv6_default_next_hop` +Set default next-hop IPv6 address. Valid values are array of next hops or keyword 'default' + +##### `set_ipv6_default_next_hop_load_share` +Enable default IPv6 next-hop load-sharing. Valid values are 'true', 'false' or keyword 'default' + +##### `set_ipv6_next_hop` +Set next-hop IPv6 address. Valid values are array of next hops or keyword 'default' + +##### `set_ipv6_next_hop_load_share` +Enable IPv6 next-hop load-sharing. Valid values are 'true', 'false' or keyword 'default' + +##### `set_ipv6_next_hop_peer_addr` +Enable IPv6 next-hop peer address. Valid values are 'true', 'false' or keyword 'default' + +##### `set_ipv6_next_hop_redist` +Enable IPv6 next-hop unchanged address during redistribution. Valid values are 'true', 'false' or keyword 'default' + +##### `set_ipv6_next_hop_unchanged` +Enable IPv6 next-hop unchanged address. Valid values are 'true', 'false' or keyword 'default' + +##### `set_ipv6_precedence` +Set IPv6 precedence field. Valid values are 'critical', 'flash', 'flash-override', 'immediate', 'internet', 'network', 'priority', 'routine' or keyword 'default' + +##### `set_ipv6_prefix` +Set IPv6 prefix-list. Valid values are string or keyword 'default' + +##### `set_level` +Set where to import route. Valid values are 'level-1', 'level-1-2', 'level-2' or keyword 'default' + +##### `set_local_preference` +Set BGP local preference path attribute. Valid values are integer or keyword 'default' + +##### `set_metric_additive` +Set add to metric. Valid values are 'true', 'false' or keyword 'default' + +##### `set_metric_bandwidth` +Set metric value or Bandwidth in kbps. Valid values are integer or keyword 'default' + +##### `set_metric_delay` +Set IGRP delay metric. Valid values are integer or keyword 'default' + +##### `set_metric_effective_bandwidth` +Set IGRP Effective bandwidth metric. Valid values are integer or keyword 'default' + +##### `set_metric_mtu` +Set IGRP MTU of the path. Valid values are integer or keyword 'default' + +##### `set_metric_reliability` +Set IGRP reliability metric. Valid values are integer or keyword 'default' + +##### `set_metric_type` +Set type of metric for destination routing protocol. Valid values are 'external, 'internal', 'type-1, 'type-2, or keyword 'default' + +##### `set_nssa_only` +Set OSPF NSSA Areas. Valid values are 'true, 'false' or keyword 'default' + +##### `set_origin` +Set BGP origin code. Valid values are 'egp, 'igp', 'incomplete', or keyword 'default' + +##### `set_path_selection` +Set path selection criteria for BGP. Valid values are 'true, 'false' or keyword 'default' + +##### `set_tag` +Set tag value for destination routing protocol. Valid values are integer or keyword 'default' + +##### `set_vrf` +Set the VRF for next-hop resolution. Valid values are string or keyword 'default' + +##### `set_weight` +Set BGP weight for routing table. Valid values are integer or keyword 'default' + -- ### Type: cisco_stp_global Manages spanning tree global parameters diff --git a/examples/cisco/demo_route_map.pp b/examples/cisco/demo_route_map.pp new file mode 100644 index 000000000..bb2773761 --- /dev/null +++ b/examples/cisco/demo_route_map.pp @@ -0,0 +1,401 @@ +# Manifest to demo cisco_interface provider +# +# Copyright (c) 2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +class ciscopuppet::cisco::demo_route_map { + + $match_evpn_route_type_1 = platform_get() ? { + /(n5k|n6k|n7k)/ => true, + default => undef + } + + $match_evpn_route_type_2_all = platform_get() ? { + /(n5k|n6k|n7k)/ => true, + default => undef + } + + $match_evpn_route_type_2_mac_ip = platform_get() ? { + /(n5k|n6k|n7k)/ => true, + default => undef + } + + $match_evpn_route_type_2_mac_only = platform_get() ? { + /(n5k|n6k|n7k)/ => true, + default => undef + } + + $match_evpn_route_type_3 = platform_get() ? { + /(n5k|n6k|n7k)/ => true, + default => undef + } + + $match_evpn_route_type_4 = platform_get() ? { + /(n5k|n6k|n7k)/ => true, + default => undef + } + + $match_evpn_route_type_5 = platform_get() ? { + /(n5k|n6k|n7k)/ => true, + default => undef + } + + $match_evpn_route_type_6 = platform_get() ? { + /(n5k|n6k|n7k)/ => true, + default => undef + } + + $match_evpn_route_type_all = platform_get() ? { + /(n5k|n6k|n7k)/ => true, + default => undef + } + + $match_length = platform_get() ? { + /(n5k|n6k|n7k)/ => ['45', '345'], + default => undef + } + + $match_mac_list = platform_get() ? { + /(n5k|n6k|n7k)/ => ['mac1', 'listmac'], + default => undef + } + + $match_ospf_area = platform_get() ? { + /(n3k|n9k$)/ => $facts['cisco']['images']['system_image'] ? { + /(I2|I3|I4)/ => undef, + default => ['10', '7', '222'] + }, + default => undef + } + + $match_vlan = platform_get() ? { + /(n5k|n6k|n7k)/ => '32, 45-200, 300-350, 400-453', + default => undef + } + + $set_ipv4_default_next_hop = platform_get() ? { + /(n3k|n7k)/ => ['1.1.1.1', '2.2.2.2'], + default => undef + } + + $set_ipv4_default_next_hop_load_share = platform_get() ? { + /(n3k|n7k)/ => true, + default => undef + } + + $set_ipv4_next_hop_load_share = platform_get() ? { + /(n3k|n9k$)/ => $facts['cisco']['images']['system_image'] ? { + /(I2|I3|I4)/ => undef, + default => true + }, + 'n7k' => true, + default => undef + } + + $set_ipv4_prefix = platform_get() ? { + /(n3k|n7k|n9k)/ => 'abcdef', + default => undef + } + + $set_ipv6_default_next_hop = platform_get() ? { + /(n3k|n7k)/ => ['2000::1', '2000::11', '2000::22'], + default => undef + } + + $set_ipv6_default_next_hop_load_share = platform_get() ? { + /(n3k|n7k)/ => true, + default => undef + } + + $set_ipv4_next_hop_redist = platform_get() ? { + /(n3k|n9k$)/ => $facts['cisco']['images']['system_image'] ? { + /(I2|I3|I4)/ => undef, + default => true + }, + default => true + } + + $set_ipv6_next_hop_redist = platform_get() ? { + /(n3k|n9k$)/ => $facts['cisco']['images']['system_image'] ? { + /(I2|I3|I4)/ => undef, + default => true + }, + default => true + } + + $set_ipv6_next_hop_load_share = platform_get() ? { + /(n3k|n9k$)/ => $facts['cisco']['images']['system_image'] ? { + /(I2|I3|I4)/ => undef, + default => true + }, + 'n7k' => true, + default => undef + } + + $set_ipv6_prefix = platform_get() ? { + /(n3k|n7k|n9k)/ => 'wxyz', + default => undef + } + + $set_extcommunity_rt_asn = platform_get() ? { + /(n3k|n5k|n6k|n7k)/ => ['11:22', '33:44', '12.22.22.22:12', '123.256:543'], + default => undef + } + + $set_vrf = platform_get() ? { + 'n7k' => 'igp', + default => undef + } + + if platform_get() =~ /n(3|5|6|7|9)k$/ { + cisco_route_map {'MyRouteMap1 123 permit': + ensure => 'present', + description => 'Testing', + match_as_number => ['3', '22-34', '38', '101-110'], + match_as_number_as_path_list => ['abc', 'xyz', 'pqr'], + match_community => ['public', 'private'], + match_community_exact_match => true, + match_evpn_route_type_1 => $match_evpn_route_type_1, + match_evpn_route_type_2_all => $match_evpn_route_type_2_all, + match_evpn_route_type_2_mac_ip => $match_evpn_route_type_2_mac_ip, + match_evpn_route_type_2_mac_only => $match_evpn_route_type_2_mac_only, + match_evpn_route_type_3 => $match_evpn_route_type_3, + match_evpn_route_type_4 => $match_evpn_route_type_4, + match_evpn_route_type_5 => $match_evpn_route_type_5, + match_evpn_route_type_6 => $match_evpn_route_type_6, + match_evpn_route_type_all => $match_evpn_route_type_all, + match_ext_community => ['epublic', 'eprivate'], + match_ext_community_exact_match => true, + match_interface => ['ethernet1/1', 'loopback2', 'mgmt0', 'null0', 'port-channel10'], + match_ipv4_addr_access_list => 'access', + match_ipv4_addr_prefix_list => ['p1', 'p7', 'pre5'], + match_ipv4_multicast_enable => true, + match_ipv4_multicast_src_addr => '242.1.1.1/32', + match_ipv4_multicast_group_addr => '239.2.2.2/32', + match_ipv4_multicast_rp_addr => '242.1.1.1/32', + match_ipv4_multicast_rp_type => 'ASM', + match_ipv4_next_hop_prefix_list => ['nh5', 'nh1', 'nh42'], + match_ipv4_route_src_prefix_list => ['rs2', 'rs22', 'pre15'], + match_ipv6_multicast_enable => true, + match_ipv6_multicast_src_addr => '2001::348:0:0/96', + match_ipv6_multicast_group_addr => 'ff0e::2:101:0:0/96', + match_ipv6_multicast_rp_addr => '2001::348:0:0/96', + match_ipv6_multicast_rp_type => 'ASM', + match_ipv6_next_hop_prefix_list => ['nhv6', 'v6nh1', 'nhv42'], + match_ipv6_route_src_prefix_list => ['rsv6', 'rs22v6', 'prev6'], + match_mac_list => $match_mac_list, + match_metric => [['1', '0'], ['8', '0'], ['224', '9'], ['23', '0'], ['5', '8'], ['6', '0']], + match_ospf_area => $match_ospf_area, + match_route_type_external => true, + match_route_type_inter_area => true, + match_route_type_internal => true, + match_route_type_intra_area => true, + match_route_type_level_1 => true, + match_route_type_level_2 => true, + match_route_type_local => true, + match_route_type_nssa_external => true, + match_route_type_type_1 => true, + match_route_type_type_2 => true, + match_src_proto => ['tcp', 'udp', 'igmp'], + match_tag => ['5', '342', '28', '3221'], + match_vlan => $match_vlan, + set_as_path_prepend => ['55.77', '12', '45.3'], + set_as_path_prepend_last_as => 1, + set_as_path_tag => true, + set_comm_list => 'abc', + set_community_additive => true, + set_community_asn => ['11:22', '33:44', '123:11'], + set_community_internet => true, + set_community_local_as => true, + set_community_no_advtertise => true, + set_community_no_export => true, + set_community_none => false, + set_dampening_half_life => 6, + set_dampening_max_duation => 55, + set_dampening_reuse => 22, + set_dampening_suppress => 44, + set_distance_igp_ebgp => 1, + set_distance_internal => 2, + set_distance_local => 3, + set_extcomm_list => 'xyz', + set_extcommunity_4bytes_additive => true, + set_extcommunity_4bytes_non_transitive => ['21:42', '43:22', '59:17'], + set_extcommunity_4bytes_transitive => ['11:22', '33:44', '66:77'], + set_extcommunity_cost_igp => [['0', '23'], ['3', '33'], ['100', '10954']], + set_extcommunity_cost_pre_bestpath => [['23', '999'], ['88', '482'], ['120', '2323']], + set_extcommunity_rt_additive => true, + set_extcommunity_rt_asn => $set_extcommunity_rt_asn, + set_forwarding_addr => true, + set_ipv4_next_hop => ['3.3.3.3', '4.4.4.4'], + set_ipv4_next_hop_load_share => $set_ipv4_next_hop_load_share, + set_ipv4_precedence => 'critical', + set_ipv4_prefix => $set_ipv4_prefix, + set_ipv6_next_hop => ['2000::1', '2000::11', '2000::22'], + set_ipv6_next_hop_load_share => $set_ipv6_next_hop_load_share, + set_ipv6_prefix => $set_ipv6_prefix, + set_level => 'level-1', + set_local_preference => 100, + set_metric_additive => false, + set_metric_bandwidth => 44, + set_metric_delay => 55, + set_metric_reliability => 66, + set_metric_effective_bandwidth => 77, + set_metric_mtu => 88, + set_metric_type => 'external', + set_nssa_only => true, + set_origin => 'egp', + set_path_selection => true, + set_tag => 101, + set_weight => 222, + } + } + + if platform_get() =~ /n9k-f/ { + cisco_route_map {'MyRouteMap1 123 permit': + ensure => 'present', + description => 'Testing', + match_as_number => ['3', '22-34', '38', '101-110'], + match_as_number_as_path_list => ['abc', 'xyz', 'pqr'], + match_community => ['public', 'private'], + match_community_exact_match => true, + match_evpn_route_type_1 => $match_evpn_route_type_1, + match_evpn_route_type_2_all => $match_evpn_route_type_2_all, + match_evpn_route_type_2_mac_ip => $match_evpn_route_type_2_mac_ip, + match_evpn_route_type_2_mac_only => $match_evpn_route_type_2_mac_only, + match_evpn_route_type_3 => $match_evpn_route_type_3, + match_evpn_route_type_4 => $match_evpn_route_type_4, + match_evpn_route_type_5 => $match_evpn_route_type_5, + match_evpn_route_type_6 => $match_evpn_route_type_6, + match_evpn_route_type_all => $match_evpn_route_type_all, + match_ext_community => ['epublic', 'eprivate'], + match_ext_community_exact_match => true, + match_interface => ['ethernet1/1', 'loopback2', 'mgmt0', 'null0', 'port-channel10'], + match_ipv4_addr_access_list => 'access', + match_ipv4_addr_prefix_list => ['p1', 'p7', 'pre5'], + match_ipv4_multicast_enable => true, + match_ipv4_multicast_src_addr => '242.1.1.1/32', + match_ipv4_multicast_group_addr => '239.2.2.2/32', + match_ipv4_multicast_rp_addr => '242.1.1.1/32', + match_ipv4_multicast_rp_type => 'ASM', + match_ipv4_next_hop_prefix_list => ['nh5', 'nh1', 'nh42'], + match_ipv4_route_src_prefix_list => ['rs2', 'rs22', 'pre15'], + match_ipv6_multicast_enable => true, + match_ipv6_multicast_src_addr => '2001::348:0:0/96', + match_ipv6_multicast_group_addr => 'ff0e::2:101:0:0/96', + match_ipv6_multicast_rp_addr => '2001::348:0:0/96', + match_ipv6_multicast_rp_type => 'ASM', + match_ipv6_next_hop_prefix_list => ['nhv6', 'v6nh1', 'nhv42'], + match_ipv6_route_src_prefix_list => ['rsv6', 'rs22v6', 'prev6'], + match_mac_list => $match_mac_list, + match_ospf_area => $match_ospf_area, + match_route_type_external => true, + match_route_type_inter_area => true, + match_route_type_internal => true, + match_route_type_intra_area => true, + match_route_type_level_1 => true, + match_route_type_level_2 => true, + match_route_type_local => true, + match_route_type_nssa_external => true, + match_route_type_type_1 => true, + match_route_type_type_2 => true, + match_src_proto => ['tcp', 'udp', 'igmp'], + match_tag => ['5', '342', '28', '3221'], + match_vlan => $match_vlan, + set_as_path_prepend => ['55.77', '12', '45.3'], + set_as_path_prepend_last_as => 1, + set_as_path_tag => true, + set_comm_list => 'abc', + set_community_additive => true, + set_community_asn => ['11:22', '33:44', '123:11'], + set_community_internet => true, + set_community_local_as => true, + set_community_no_advtertise => true, + set_community_no_export => true, + set_community_none => false, + set_dampening_half_life => 6, + set_dampening_max_duation => 55, + set_dampening_reuse => 22, + set_dampening_suppress => 44, + set_distance_igp_ebgp => 1, + set_distance_internal => 2, + set_distance_local => 3, + set_extcomm_list => 'xyz', + set_level => 'level-1', + set_local_preference => 100, + set_metric_additive => false, + set_metric_bandwidth => 44, + set_metric_delay => 55, + set_metric_reliability => 66, + set_metric_effective_bandwidth => 77, + set_metric_mtu => 88, + set_metric_type => 'external', + set_nssa_only => true, + set_origin => 'egp', + set_path_selection => true, + set_tag => 101, + set_weight => 222, + } + } + + cisco_route_map {'MyRouteMap2 149 deny': + ensure => 'present', + match_ipv6_addr_prefix_list => ['pv6', 'pv67', 'prev6'], + match_ipv4_multicast_enable => true, + match_ipv4_multicast_src_addr => '242.1.1.1/32', + match_ipv4_multicast_group_range_begin_addr => '239.1.1.1', + match_ipv4_multicast_group_range_end_addr => '239.2.2.2', + match_ipv4_multicast_rp_addr => '242.1.1.1/32', + match_ipv4_multicast_rp_type => 'Bidir', + match_ipv6_addr_access_list => 'v6access', + match_ipv6_multicast_enable => true, + match_ipv6_multicast_src_addr => '2001::348:0:0/96', + match_ipv6_multicast_group_range_begin_addr => 'ff01::', + match_ipv6_multicast_group_range_end_addr => 'ff02::', + match_ipv6_multicast_rp_addr => '2001::348:0:0/96', + match_ipv6_multicast_rp_type => 'Bidir', + set_community_none => true, + set_extcommunity_4bytes_none => true, + set_ipv4_default_next_hop => $set_ipv4_default_next_hop, + set_ipv4_default_next_hop_load_share => $set_ipv4_default_next_hop_load_share, + set_ipv4_next_hop_peer_addr => true, + set_ipv6_precedence => 'flash', + set_level => 'level-1-2', + set_metric_additive => true, + set_metric_bandwidth => 33, + set_metric_type => 'type-2', + set_origin => 'incomplete', + } + + cisco_route_map {'MyRouteMap3 159 deny': + ensure => 'present', + set_ipv6_default_next_hop => $set_ipv6_default_next_hop, + set_ipv6_default_next_hop_load_share => $set_ipv6_default_next_hop_load_share, + set_ipv6_next_hop_peer_addr => true, + } + + cisco_route_map {'MyRouteMap4 200 permit': + ensure => 'present', + set_interface => 'Null0', + set_ipv4_next_hop_redist => $set_ipv4_next_hop_redist, + set_ipv6_next_hop_redist => $set_ipv6_next_hop_redist, + set_ipv6_next_hop_unchanged => true, + set_ipv4_next_hop_unchanged => true, + } + + cisco_route_map {'MyRouteMap5 199 deny': + ensure => 'present', + match_length => $match_length, + set_vrf => $set_vrf, + } +} diff --git a/examples/demo_all_cisco.pp b/examples/demo_all_cisco.pp index a152f91bf..b4b57b74c 100644 --- a/examples/demo_all_cisco.pp +++ b/examples/demo_all_cisco.pp @@ -42,6 +42,7 @@ include ciscopuppet::cisco::demo_patching include ciscopuppet::cisco::demo_pim include ciscopuppet::cisco::demo_portchannel + include ciscopuppet::cisco::demo_route_map include ciscopuppet::cisco::demo_snmp #stp_bd and stp_vlan are exclusive, so comment one of them #include ciscopuppet::cisco::demo_stp_bd diff --git a/lib/puppet/provider/cisco_route_map/cisco.rb b/lib/puppet/provider/cisco_route_map/cisco.rb new file mode 100644 index 000000000..bcfd06f31 --- /dev/null +++ b/lib/puppet/provider/cisco_route_map/cisco.rb @@ -0,0 +1,557 @@ +# January, 2017 +# +# Copyright (c) 2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'cisco_node_utils' if Puppet.features.cisco_node_utils? +begin + require 'puppet_x/cisco/autogen' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'autogen.rb')) +end + +begin + require 'puppet_x/cisco/cmnutils' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'cmnutils.rb')) +end +Puppet::Type.type(:cisco_route_map).provide(:cisco) do + desc 'The Cisco route map provider.' + + confine feature: :cisco_node_utils + defaultfor operatingsystem: :nexus + + mk_resource_methods + + ROUTE_MAP_NON_BOOL_PROPS = [ + :description, + :match_ipv4_addr_access_list, + :match_ipv4_multicast_group_addr, + :match_ipv4_multicast_group_range_begin_addr, + :match_ipv4_multicast_group_range_end_addr, + :match_ipv4_multicast_rp_addr, + :match_ipv4_multicast_rp_type, + :match_ipv4_multicast_src_addr, + :match_ipv6_addr_access_list, + :match_ipv6_multicast_group_addr, + :match_ipv6_multicast_group_range_begin_addr, + :match_ipv6_multicast_group_range_end_addr, + :match_ipv6_multicast_rp_addr, + :match_ipv6_multicast_rp_type, + :match_ipv6_multicast_src_addr, + :match_vlan, + :set_as_path_prepend_last_as, + :set_comm_list, + :set_dampening_half_life, + :set_dampening_max_duation, + :set_dampening_reuse, + :set_dampening_suppress, + :set_distance_igp_ebgp, + :set_distance_internal, + :set_distance_local, + :set_extcomm_list, + :set_interface, + :set_ipv4_precedence, + :set_ipv4_prefix, + :set_ipv6_precedence, + :set_ipv6_prefix, + :set_level, + :set_local_preference, + :set_metric_bandwidth, + :set_metric_delay, + :set_metric_reliability, + :set_metric_effective_bandwidth, + :set_metric_mtu, + :set_metric_type, + :set_origin, + :set_tag, + :set_vrf, + :set_weight, + ] + + ROUTE_MAP_BOOL_PROPS = [ + :match_community_exact_match, + :match_evpn_route_type_1, + :match_evpn_route_type_2_all, + :match_evpn_route_type_2_mac_ip, + :match_evpn_route_type_2_mac_only, + :match_evpn_route_type_3, + :match_evpn_route_type_4, + :match_evpn_route_type_5, + :match_evpn_route_type_6, + :match_evpn_route_type_all, + :match_ext_community_exact_match, + :match_ipv4_multicast_enable, + :match_ipv6_multicast_enable, + :match_route_type_external, + :match_route_type_inter_area, + :match_route_type_internal, + :match_route_type_intra_area, + :match_route_type_level_1, + :match_route_type_level_2, + :match_route_type_local, + :match_route_type_nssa_external, + :match_route_type_type_1, + :match_route_type_type_2, + :set_as_path_tag, + :set_community_additive, + :set_community_internet, + :set_community_local_as, + :set_community_no_advtertise, + :set_community_no_export, + :set_community_none, + :set_extcommunity_4bytes_additive, + :set_extcommunity_4bytes_none, + :set_extcommunity_rt_additive, + :set_forwarding_addr, + :set_ipv4_default_next_hop_load_share, + :set_ipv4_next_hop_load_share, + :set_ipv4_next_hop_peer_addr, + :set_ipv4_next_hop_redist, + :set_ipv4_next_hop_unchanged, + :set_ipv6_default_next_hop_load_share, + :set_ipv6_next_hop_load_share, + :set_ipv6_next_hop_peer_addr, + :set_ipv6_next_hop_redist, + :set_ipv6_next_hop_unchanged, + :set_metric_additive, + :set_nssa_only, + :set_path_selection, + ] + + ROUTE_MAP_ARRAY_FLAT_PROPS = [ + :match_as_number, + :match_as_number_as_path_list, + :match_community, + :match_ext_community, + :match_interface, + :match_ipv4_addr_prefix_list, + :match_ipv4_next_hop_prefix_list, + :match_ipv4_route_src_prefix_list, + :match_ipv6_addr_prefix_list, + :match_ipv6_next_hop_prefix_list, + :match_ipv6_route_src_prefix_list, + :match_length, + :match_mac_list, + :match_ospf_area, + :match_src_proto, + :match_tag, + :set_as_path_prepend, + :set_community_asn, + :set_extcommunity_4bytes_non_transitive, + :set_extcommunity_4bytes_transitive, + :set_extcommunity_rt_asn, + :set_ipv4_default_next_hop, + :set_ipv4_next_hop, + :set_ipv6_default_next_hop, + :set_ipv6_next_hop, + ] + + ROUTE_MAP_ARRAY_NESTED_PROPS = [ + :match_metric, + :set_extcommunity_cost_igp, + :set_extcommunity_cost_pre_bestpath, + ] + + ROUTE_MAP_ALL_PROPS = ROUTE_MAP_NON_BOOL_PROPS + + ROUTE_MAP_ARRAY_FLAT_PROPS + + ROUTE_MAP_ARRAY_NESTED_PROPS + + ROUTE_MAP_BOOL_PROPS + + PuppetX::Cisco::AutoGen.mk_puppet_methods(:bool, self, '@nu', + ROUTE_MAP_BOOL_PROPS) + PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@nu', + ROUTE_MAP_NON_BOOL_PROPS) + PuppetX::Cisco::AutoGen.mk_puppet_methods(:array_flat, self, '@nu', + ROUTE_MAP_ARRAY_FLAT_PROPS) + PuppetX::Cisco::AutoGen.mk_puppet_methods(:array_nested, self, '@nu', + ROUTE_MAP_ARRAY_NESTED_PROPS) + + def initialize(value={}) + super(value) + rmname = @property_hash[:rmname] + sequence = @property_hash[:sequence] + action = @property_hash[:action] + @nu = Cisco::RouteMap.maps[rmname][sequence][action] unless + rmname.nil? || sequence.nil? || action.nil? + @property_flush = {} + end + + def self.properties_get(rmname, sequence, action, nu_obj) + debug "Checking route map instance, #{rmname} #{sequence} #{action}" + current_state = { + name: "#{rmname} #{sequence} #{action}", + rmname: rmname, + sequence: sequence, + action: action, + ensure: :present, + } + + # Call node_utils getter for each property + (ROUTE_MAP_NON_BOOL_PROPS).each do |prop| + current_state[prop] = nu_obj.send(prop) + end + ROUTE_MAP_ARRAY_FLAT_PROPS.each do |prop| + current_state[prop] = nu_obj.send(prop) + end + ROUTE_MAP_ARRAY_NESTED_PROPS.each do |prop| + current_state[prop] = nu_obj.send(prop) + end + ROUTE_MAP_BOOL_PROPS.each do |prop| + val = nu_obj.send(prop) + if val.nil? + current_state[prop] = nil + else + current_state[prop] = val ? :true : :false + end + end + new(current_state) + end # self.properties_get + + def self.instances + rm_instances = [] + Cisco::RouteMap.maps.each do |rmname, sequences| + sequences.each do |sequence, actions| + actions.each do |action, nu_obj| + rm_instances << properties_get(rmname, sequence, action, nu_obj) + end + end + end + rm_instances + end # self.instances + + def self.prefetch(resources) + rm_instances = instances + resources.keys.each do |id| + provider = rm_instances.find do |rmi| + rmi.rmname.to_s == resources[id][:rmname].to_s && + rmi.sequence.to_s == resources[id][:sequence].to_s && + rmi.action.to_s == resources[id][:action].to_s + end + resources[id].provider = provider unless provider.nil? + end + end + + def exists? + @property_hash[:ensure] == :present + end + + def create + @property_flush[:ensure] = :present + end + + def destroy + @property_flush[:ensure] = :absent + end + + def properties_set(new_rm=false) + ROUTE_MAP_ALL_PROPS.each do |prop| + next unless @resource[prop] + send("#{prop}=", @resource[prop]) if new_rm + unless @property_flush[prop].nil? + @nu.send("#{prop}=", @property_flush[prop]) if + @nu.respond_to?("#{prop}=") + end + end + # custom setters which require one-shot multi-param setters + match_community_set + match_ext_community_set + match_ipv4_multicast_set + match_ipv6_multicast_set + match_ip_addr_access_list_set + match_ip_addr_prefix_list_set + match_route_type_set + set_dampening_set + set_distance_set + set_community_set + set_extcommunity_4bytes_set + set_extcommunity_rt_set + set_extcommunity_cost_set + set_ip_next_hop_set + set_ip_precedence_set + set_metric_set + end + + def match_ip_addr_access_list_set + pf = @property_flush[:match_ipv4_addr_access_list] + v4 = pf.nil? ? @nu.match_ipv4_addr_access_list : pf + pf = @property_flush[:match_ipv6_addr_access_list] + v6 = pf.nil? ? @nu.match_ipv6_addr_access_list : pf + @nu.match_ip_addr_access_list(v4, v6) + end + + def match_ip_addr_prefix_list_set + pf = @property_flush[:match_ipv4_addr_prefix_list] + v4 = pf.nil? ? @nu.match_ipv4_addr_prefix_list : pf + pf = @property_flush[:match_ipv6_addr_prefix_list] + v6 = pf.nil? ? @nu.match_ipv6_addr_prefix_list : pf + @nu.match_ip_addr_prefix_list(v4, v6) + end + + def match_community_set + comm = @property_flush[:match_community] ? @property_flush[:match_community] : @nu.match_community + pf = @property_flush[:match_community_exact_match] + exact = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.match_community_exact_match + @nu.match_community_set(comm, exact) + end + + def match_ext_community_set + comm = @property_flush[:match_ext_community] ? @property_flush[:match_ext_community] : @nu.match_ext_community + pf = @property_flush[:match_ext_community_exact_match] + exact = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.match_ext_community_exact_match + @nu.match_ext_community_set(comm, exact) + end + + def match_set_helper(properties, setter) + return unless properties.any? { |p| @property_flush.key?(p) } + attrs = {} + # At least one var has changed, get all vals from manifest + properties.each do |p| + if @resource[p] == :default + attrs[p] = @nu.send("default_#{p}") + else + attrs[p] = @resource[p] + attrs[p] = PuppetX::Cisco::Utils.bool_sym_to_s(attrs[p]) + end + end + @nu.send(setter, *[attrs]) + end + + def match_ipv4_multicast_set + properties = [ + :match_ipv4_multicast_src_addr, + :match_ipv4_multicast_group_addr, + :match_ipv4_multicast_group_range_begin_addr, + :match_ipv4_multicast_group_range_end_addr, + :match_ipv4_multicast_rp_addr, + :match_ipv4_multicast_rp_type, + :match_ipv4_multicast_enable, + ] + match_set_helper(properties, 'match_ipv4_multicast_set') + end + + def match_ipv6_multicast_set + properties = [ + :match_ipv6_multicast_src_addr, + :match_ipv6_multicast_group_addr, + :match_ipv6_multicast_group_range_begin_addr, + :match_ipv6_multicast_group_range_end_addr, + :match_ipv6_multicast_rp_addr, + :match_ipv6_multicast_rp_type, + :match_ipv6_multicast_enable, + ] + match_set_helper(properties, 'match_ipv6_multicast_set') + end + + def match_route_type_set + properties = [ + :match_route_type_external, + :match_route_type_inter_area, + :match_route_type_internal, + :match_route_type_intra_area, + :match_route_type_level_1, + :match_route_type_level_2, + :match_route_type_local, + :match_route_type_nssa_external, + :match_route_type_type_1, + :match_route_type_type_2, + ] + match_set_helper(properties, 'match_route_type_set') + end + + def set_ip_precedence_set + pf = @property_flush[:set_ipv4_precedence] + v4 = pf.nil? ? @nu.set_ipv4_precedence : pf + pf = @property_flush[:set_ipv6_precedence] + v6 = pf.nil? ? @nu.set_ipv6_precedence : pf + @nu.set_ip_precedence(v4, v6) + end + + def set_metric_set + pf = @property_flush[:set_metric_additive] + plus = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_metric_additive + bw = @property_flush[:set_metric_bandwidth].nil? ? @nu.set_metric_bandwidth : @property_flush[:set_metric_bandwidth] + del = @property_flush[:set_metric_delay].nil? ? @nu.set_metric_delay : @property_flush[:set_metric_delay] + rel = @property_flush[:set_metric_reliability].nil? ? @nu.set_metric_reliability : @property_flush[:set_metric_reliability] + pf = @property_flush[:set_metric_effective_bandwidth] + ebw = pf.nil? ? @nu.set_metric_effective_bandwidth : pf + mtu = @property_flush[:set_metric_mtu].nil? ? @nu.set_metric_mtu : @property_flush[:set_metric_mtu] + @nu.set_metric_set(plus, bw, del, rel, ebw, mtu) + end + + def set_dampening_set + hl = @property_flush[:set_dampening_half_life].nil? ? @nu.set_dampening_half_life : @property_flush[:set_dampening_half_life] + md = @property_flush[:set_dampening_max_duation].nil? ? @nu.set_dampening_max_duation : @property_flush[:set_dampening_max_duation] + re = @property_flush[:set_dampening_reuse].nil? ? @nu.set_dampening_reuse : @property_flush[:set_dampening_reuse] + sup = @property_flush[:set_dampening_suppress].nil? ? @nu.set_dampening_suppress : @property_flush[:set_dampening_suppress] + @nu.set_dampening_set(hl, re, sup, md) + end + + def set_distance_set + igp = @property_flush[:set_distance_igp_ebgp].nil? ? @nu.set_distance_igp_ebgp : @property_flush[:set_distance_igp_ebgp] + int = @property_flush[:set_distance_internal].nil? ? @nu.set_distance_internal : @property_flush[:set_distance_internal] + loc = @property_flush[:set_distance_local].nil? ? @nu.set_distance_local : @property_flush[:set_distance_local] + @nu.set_distance_set(igp, int, loc) + end + + def set_ipv4_default_next_hop_set + nh = @property_flush[:set_ipv4_default_next_hop] ? @property_flush[:set_ipv4_default_next_hop] : @nu.set_ipv4_default_next_hop + pf = @property_flush[:set_ipv4_default_next_hop_load_share] + ls = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_ipv4_default_next_hop_load_share + @nu.set_ipv4_default_next_hop_set(nh, ls) + end + + def set_ipv4_next_hop_set + nh = @property_flush[:set_ipv4_next_hop] ? @property_flush[:set_ipv4_next_hop] : @nu.set_ipv4_next_hop + pf = @property_flush[:set_ipv4_next_hop_load_share] + ls = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_ipv4_next_hop_load_share + @nu.set_ipv4_next_hop_set(nh, ls) + end + + def set_ipv6_default_next_hop_set + nh = @property_flush[:set_ipv6_default_next_hop] ? @property_flush[:set_ipv6_default_next_hop] : @nu.set_ipv6_default_next_hop + pf = @property_flush[:set_ipv6_default_next_hop_load_share] + ls = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_ipv6_default_next_hop_load_share + @nu.set_ipv6_default_next_hop_set(nh, ls) + end + + def set_ipv6_next_hop_set + nh = @property_flush[:set_ipv6_next_hop] ? @property_flush[:set_ipv6_next_hop] : @nu.set_ipv6_next_hop + pf = @property_flush[:set_ipv6_next_hop_load_share] + ls = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_ipv6_next_hop_load_share + @nu.set_ipv6_next_hop_set(nh, ls) + end + + def set_community_set + pf = @property_flush[:set_community_none] + none = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_community_none + pf = @property_flush[:set_community_no_advtertise] + noadv = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_community_no_advtertise + pf = @property_flush[:set_community_no_export] + noexp = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_community_no_export + pf = @property_flush[:set_community_additive] + add = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_community_additive + pf = @property_flush[:set_community_local_as] + local = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_community_local_as + pf = @property_flush[:set_community_internet] + inter = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_community_internet + asn = @property_flush[:set_community_asn] ? @property_flush[:set_community_asn] : @nu.set_community_asn + @nu.set_community_set(none, noadv, noexp, add, local, inter, asn) + end + + def set_extcommunity_4bytes_set + pf = @property_flush[:set_extcommunity_4bytes_none] + none = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_extcommunity_4bytes_none + pf = @property_flush[:set_extcommunity_4bytes_transitive] + tr = pf ? pf : @nu.set_extcommunity_4bytes_transitive + pf = @property_flush[:set_extcommunity_4bytes_non_transitive] + ntr = pf ? pf : @nu.set_extcommunity_4bytes_non_transitive + pf = @property_flush[:set_extcommunity_4bytes_additive] + add = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_extcommunity_4bytes_additive + @nu.set_extcommunity_4bytes_set(none, tr, ntr, add) + end + + def set_extcommunity_rt_set + pf = @property_flush[:set_extcommunity_rt_asn] + asn = pf ? pf : @nu.set_extcommunity_rt_asn + pf = @property_flush[:set_extcommunity_rt_additive] + add = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_extcommunity_rt_additive + @nu.set_extcommunity_rt_set(asn, add) + end + + def set_extcommunity_cost_set + pf = @property_flush[:set_extcommunity_cost_igp] + igp = pf ? pf : @nu.set_extcommunity_cost_igp + pf = @property_flush[:set_extcommunity_cost_pre_bestpath] + pre = pf ? pf : @nu.set_extcommunity_cost_pre_bestpath + @nu.set_extcommunity_cost_set(igp, pre) + end + + def legacy_image? + fd = Facter.value('cisco') + image = fd['images']['system_image'] + image[/7.0.3.I2|I3|I4/] + end + + def v4_ip_next_hop(attrs) + pf = @property_flush[:set_ipv4_default_next_hop] + attrs[:v4dnh] = pf ? pf : @nu.set_ipv4_default_next_hop + pf = @property_flush[:set_ipv4_default_next_hop_load_share] + attrs[:v4dls] = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_ipv4_default_next_hop_load_share + pf = @property_flush[:set_ipv4_next_hop] + attrs[:v4nh] = pf ? pf : @nu.set_ipv4_next_hop + pf = @property_flush[:set_ipv4_next_hop_load_share] + attrs[:v4ls] = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_ipv4_next_hop_load_share + pf = @property_flush[:set_ipv4_next_hop_peer_addr] + attrs[:v4peer] = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_ipv4_next_hop_peer_addr + pf = @property_flush[:set_ipv4_next_hop_redist] + if legacy_image? + attrs[:v4red] = nil + else + attrs[:v4red] = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_ipv4_next_hop_redist + end + pf = @property_flush[:set_ipv4_next_hop_unchanged] + attrs[:v4unc] = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_ipv4_next_hop_unchanged + end + + def v6_ip_next_hop(attrs) + pf = @property_flush[:set_ipv6_default_next_hop] + attrs[:v6dnh] = pf ? pf : @nu.set_ipv6_default_next_hop + pf = @property_flush[:set_ipv6_default_next_hop_load_share] + attrs[:v6dls] = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_ipv6_default_next_hop_load_share + pf = @property_flush[:set_ipv6_next_hop] + attrs[:v6nh] = pf ? pf : @nu.set_ipv6_next_hop + pf = @property_flush[:set_ipv6_next_hop_load_share] + attrs[:v6ls] = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_ipv6_next_hop_load_share + pf = @property_flush[:set_ipv6_next_hop_peer_addr] + attrs[:v6peer] = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_ipv6_next_hop_peer_addr + pf = @property_flush[:set_ipv6_next_hop_redist] + if legacy_image? + attrs[:v4red] = nil + else + attrs[:v6red] = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_ipv6_next_hop_redist + end + pf = @property_flush[:set_ipv6_next_hop_unchanged] + attrs[:v6unc] = PuppetX::Cisco::Utils.flush_boolean?(pf) ? pf : @nu.set_ipv6_next_hop_unchanged + end + + def set_ip_next_hop_set + attrs = {} + attrs[:intf] = @property_flush[:set_interface] ? @property_flush[:set_interface] : @nu.set_interface + v4_ip_next_hop(attrs) + v6_ip_next_hop(attrs) + @nu.set_ip_next_hop_set(attrs) + end + + def flush + if @property_flush[:ensure] == :absent + @nu.destroy + @nu = nil + else + # Create/Update + new_rm = false + if @nu.nil? + new_rm = true + @nu = Cisco::RouteMap.new(@resource[:rmname], + @resource[:sequence], + @resource[:action]) + end + properties_set(new_rm) + end + end +end diff --git a/lib/puppet/type/cisco_route_map.rb b/lib/puppet/type/cisco_route_map.rb new file mode 100644 index 000000000..0257acb73 --- /dev/null +++ b/lib/puppet/type/cisco_route_map.rb @@ -0,0 +1,1659 @@ +# Manages the Cisco route map configuration resource. +# +# January 2017 +# +# Copyright (c) 2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Puppet::Type.newtype(:cisco_route_map) do + @doc = "Manages a route map. + + cisco_route_map {\" \": + ..attributes.. + } + + is the name of the route map. + is sequence to insert/delete existing route-map entry + is permit or deny. + + Examples: + cisco_route_map {'MyRouteMap1 123 permit': + ensure => 'present', + description => 'Testing', + match_as_number => ['3', '22-34', '38'], + match_as_number_as_path_list => ['abc', 'xyz', 'pqr'], + match_community => ['public', 'private'], + match_community_exact_match => true, + match_evpn_route_type_1 => true, + match_evpn_route_type_2_all => true, + match_evpn_route_type_2_mac_ip => true, + match_evpn_route_type_2_mac_only => true, + match_evpn_route_type_3 => true, + match_evpn_route_type_4 => true, + match_evpn_route_type_5 => true, + match_evpn_route_type_6 => true, + match_evpn_route_type_all => true, + match_ext_community => ['epublic', 'eprivate'], + match_ext_community_exact_match => true, + match_interface => ['loopback2', 'mgmt0'], + match_ipv4_addr_access_list => 'access1', + match_ipv4_addr_prefix_list => ['p1', 'p7', 'pre5'], + match_ipv4_multicast_enable => true, + match_ipv4_multicast_src_addr => '242.1.1.1/32', + match_ipv4_multicast_group_addr => '239.2.2.2/32', + match_ipv4_multicast_group_range_begin_addr => default, + match_ipv4_multicast_group_range_end_addr => default, + match_ipv4_multicast_rp_addr => '242.1.1.1/32', + match_ipv4_multicast_rp_type => 'ASM', + match_ipv4_next_hop_prefix_list => ['nh5', 'nh1', 'nh42'], + match_ipv4_route_src_prefix_list => ['rs2', 'rs22', 'pre15'], + match_ipv6_addr_access_list => 'v6access', + match_ipv6_addr_prefix_list => ['pv6', 'pv67', 'prev6'], + match_ipv6_multicast_enable => true, + match_ipv6_multicast_src_addr => '2001::348:0:0/96', + match_ipv6_multicast_group_addr => 'ff0e::2:101:0:0/96', + match_ipv6_multicast_group_range_begin_addr => default, + match_ipv6_multicast_group_range_end_addr => default, + match_ipv6_multicast_rp_addr => '2001::348:0:0/96', + match_ipv6_multicast_rp_type => 'ASM', + match_ipv6_next_hop_prefix_list => ['nhv6', 'v6nh1', 'nhv42'], + match_ipv6_route_src_prefix_list => ['rsv6', 'rs22v6', 'prev6'], + match_length => ['45', '345'], + match_mac_list => ['mac1', 'listmac'], + match_metric => [['8', '0'], ['224', '9']] + match_ospf_area => ['10', '7', '222'], + match_route_type_external => true, + match_route_type_inter_area => true, + match_route_type_internal => true, + match_route_type_intra_area => true, + match_route_type_level_1 => true, + match_route_type_level_2 => true, + match_route_type_local => true, + match_route_type_nssa_external => true, + match_route_type_type_1 => true, + match_route_type_type_2 => true, + match_src_proto => ['tcp', 'udp', 'igmp'], + match_tag => ['5', '342', '28', '3221'], + match_vlan => '32, 45-200, 300-399, 402', + set_as_path_prepend => ['55.77', '12', '45.3'], + set_as_path_prepend_last_as => 1, + set_as_path_tag => true, + set_comm_list => 'abc', + set_community_additive => true, + set_community_asn => ['11:22', '33:44', '123:11'], + set_community_internet => true, + set_community_local_as => true, + set_community_no_advtertise => true, + set_community_no_export => true, + set_community_none => false, + set_dampening_half_life => 6, + set_dampening_max_duation => 55, + set_dampening_reuse => 22, + set_dampening_suppress => 44, + set_distance_igp_ebgp => 44, + set_dampening_suppress => 44, + set_dampening_suppress => 1, + set_distance_internal => 2, + set_distance_local => 3, + set_extcomm_list => 'xyz', + set_extcommunity_4bytes_additive => true, + set_extcommunity_4bytes_non_transitive => ['21:42', '43:22', '59:17'], + set_extcommunity_4bytes_transitive => ['11:22', '33:44', '66:77'], + set_extcommunity_cost_igp => [[0, 23], [3, 33]], + set_extcommunity_cost_pre_bestpath => [[23, 999], [88, 482]], + set_extcommunity_rt_additive => true, + set_extcommunity_rt_asn => ['11:22', '123.256:543'], + set_forwarding_addr => true, + set_interface => 'Null0', + set_ipv4_default_next_hop => ['1.1.1.1', '2.2.2.2'], + set_ipv4_default_next_hop_load_share => true, + set_ipv4_next_hop => ['3.3.3.3', '4.4.4.4'], + set_ipv4_next_hop_load_share => true, + set_ipv4_next_hop_peer_addr => true, + set_ipv4_next_hop_redist => true, + set_ipv4_next_hop_unchanged => true, + set_ipv4_precedence => 'critical', + set_ipv4_prefix => 'abcdef', + set_ipv6_default_next_hop => ['2000::1', '2000::11'], + set_ipv6_default_next_hop_load_share => true, + set_ipv6_next_hop => ['2000::1', '2000::11'], + set_ipv6_next_hop_load_share => true, + set_ipv6_next_hop_peer_addr => true, + set_ipv6_next_hop_redist => true, + set_ipv6_next_hop_unchanged => true, + set_ipv6_precedence => 'network', + set_ipv6_prefix => 'wxyz', + set_level => 'level-1', + set_local_preference => 100, + set_metric_additive => false, + set_metric_bandwidth => 44, + set_metric_delay => 55, + set_metric_reliability => 66, + set_metric_effective_bandwidth => 77, + set_metric_mtu => 88, + set_metric_type => 'external', + set_nssa_only => true, + set_origin => 'egp', + set_path_selection => true, + set_tag => 101, + set_vrf => 'igp', + set_weight => 222, + } + " + + ensurable + + ################### + # Resource Naming # + ################### + + # Parse out the title to fill in the attributes in these + # patterns. These attributes can be overwritten later. + def self.title_patterns + identity = ->(x) { x } + patterns = [] + + # Below pattern matches both parts of the full composite name. + patterns << [ + /^(\S+) (\d+) (\S+)$/, + [ + [:rmname, identity], + [:sequence, identity], + [:action, identity], + ], + ] + patterns + end + + # Overwrites name method. Original method simply returns self[:name], + # which is no longer valid or complete. + # Would not have failed, but just return nothing useful. + def name + "#{self[:rmname]} #{self[:sequence]} #{self[:action]}" + end + + newparam(:name) do + desc 'Name of cisco_route_map, not used, but needed for puppet' + end + + newparam(:rmname, namevar: true) do + desc 'Name of the route map instance. Valid values are string.' + end # param rmname + + newparam(:sequence, namevar: true) do + desc "Sequence to insert/delete existing route-map entry. + Valid values are integer." + end # param sequence + + newparam(:action, namevar: true) do + desc 'Action for set oprtations. Valid values are permit or deny.' + munge(&:to_s) + newvalues(:permit, :deny) + end # param action + + ############## + # Attributes # + ############## + + newproperty(:description) do + desc "Description of the route-map. Valid values are string, + keyword 'default'" + + munge do |value| + value = :default if value == 'default' + value + end + end # property description + + newproperty(:match_as_number, array_matching: :all) do + format = '[range1, range2]' + desc 'Match BGP peer AS number. An array of [range1, range2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property match_as_number + + newproperty(:match_as_number_as_path_list, array_matching: :all) do + format = '[list1, list2]' + desc 'Match BGP AS path list. An array of [list1, list2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property match_as_number_as_path_list + + newproperty(:match_community, array_matching: :all) do + format = '[comm1, comm2]' + desc 'Match BGP community list. An array of [comm1, comm2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property match_community + + newproperty(:match_community_exact_match) do + desc 'Enable exact matching of communities' + + newvalues(:true, :false, :default) + end # property match_community_exact_match + + newproperty(:match_evpn_route_type_1) do + desc 'Enable match BGP EVPN route type-1' + + newvalues(:true, :false, :default) + end # property match_evpn_route_type_1 + + newproperty(:match_evpn_route_type_2_all) do + desc 'Enable match all BGP EVPN route in type-2' + + newvalues(:true, :false, :default) + end # property match_evpn_route_type_2_all + + newproperty(:match_evpn_route_type_2_mac_ip) do + desc 'Enable match mac-ip BGP EVPN route in type-2' + + newvalues(:true, :false, :default) + end # property match_evpn_route_type_2_mac_ip + + newproperty(:match_evpn_route_type_2_mac_only) do + desc 'Enable match mac-only BGP EVPN route in type-2' + + newvalues(:true, :false, :default) + end # property match_evpn_route_type_2_mac_only + + newproperty(:match_evpn_route_type_3) do + desc 'Enable match BGP EVPN route type-3' + + newvalues(:true, :false, :default) + end # property match_evpn_route_type_3 + + newproperty(:match_evpn_route_type_4) do + desc 'Enable match BGP EVPN route type-4' + + newvalues(:true, :false, :default) + end # property match_evpn_route_type_4 + + newproperty(:match_evpn_route_type_5) do + desc 'Enable match BGP EVPN route type-5' + + newvalues(:true, :false, :default) + end # property match_evpn_route_type_5 + + newproperty(:match_evpn_route_type_6) do + desc 'Enable match BGP EVPN route type-6' + + newvalues(:true, :false, :default) + end # property match_evpn_route_type_6 + + newproperty(:match_evpn_route_type_all) do + desc 'Enable match BGP EVPN route type 1-6' + + newvalues(:true, :false, :default) + end # property match_evpn_route_type_all + + newproperty(:match_ext_community, array_matching: :all) do + format = '[ecomm1, ecomm2]' + desc 'Match BGP extended community list. An array of [ecomm1, ecomm2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property match_ext_community + + newproperty(:match_ext_community_exact_match) do + desc 'Enable exact matching of extended communities' + + newvalues(:true, :false, :default) + end # property match_ext_community_exact_match + + newproperty(:match_interface, array_matching: :all) do + format = '[int1, int2]' + desc 'Match first hop interface of route. An array of [int1, int2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property match_interface + + newproperty(:match_ipv4_addr_access_list) do + desc "IPv4 access-list name. Valid values are string, + keyword 'default'" + + munge do |value| + value = :default if value == 'default' + value + end + end # property match_ipv4_addr_access_list + + newproperty(:match_ipv4_addr_prefix_list, array_matching: :all) do + format = '[pf1, pf2]' + desc 'Match entries of prefix-lists for IPv4. An array of [pf1, pf2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property match_ipv4_addr_prefix_list + + newproperty(:match_ipv4_multicast_enable) do + desc 'Enable match IPv4 multicast' + + newvalues(:true, :false, :default) + end # property match_ipv4_multicast_enable + + newproperty(:match_ipv4_multicast_group_addr) do + desc "Match IPv4 multicast group prefix. Valid values are string, + keyword 'default'" + + munge do |value| + value = :default if value == 'default' + value + end + end # property match_ipv4_multicast_group_addr + + newproperty(:match_ipv4_multicast_group_range_begin_addr) do + desc "Match IPv4 multicast group address begin range. + Valid values are string, keyword 'default'" + + munge do |value| + value = :default if value == 'default' + value + end + end # property match_ipv4_multicast_group_range_begin_addr + + newproperty(:match_ipv4_multicast_group_range_end_addr) do + desc "Match IPv4 multicast group address end range. + Valid values are string, keyword 'default'" + + munge do |value| + value = :default if value == 'default' + value + end + end # property match_ipv4_multicast_group_range_end_addr + + newproperty(:match_ipv4_multicast_rp_addr) do + desc "Match IPv4 multicast rendezvous prefix. Valid values are string, + keyword 'default'" + + munge do |value| + value = :default if value == 'default' + value + end + end # property match_ipv4_multicast_rp_addr + + newproperty(:match_ipv4_multicast_rp_type) do + desc 'Match IPv4 multicast rendezvous point type' + + newvalues(:ASM, :Bidir, :default) + end # property match_ipv4_multicast_rp_type + + newproperty(:match_ipv4_multicast_src_addr) do + desc "Match IPv4 multicast source prefix. Valid values are string, + keyword 'default'" + + munge do |value| + value = :default if value == 'default' + value + end + end # property match_ipv4_multicast_src_addr + + newproperty(:match_ipv4_next_hop_prefix_list, array_matching: :all) do + format = '[pf1, pf2]' + desc 'Match entries of prefix-lists for next-hop address of route for IPv4. An array of [pf1, pf2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property match_ipv4_next_hop_prefix_list + + newproperty(:match_ipv4_route_src_prefix_list, array_matching: :all) do + format = '[pf1, pf2]' + desc 'Match entries of prefix-lists for advertising source address of route for IPv4. An array of [pf1, pf2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property match_ipv4_route_src_prefix_list + + newproperty(:match_ipv6_addr_access_list) do + desc "IPv6 access-list name. Valid values are string, + keyword 'default'" + + munge do |value| + value = :default if value == 'default' + value + end + end # property match_ipv6_addr_access_list + + newproperty(:match_ipv6_addr_prefix_list, array_matching: :all) do + format = '[pf1, pf2]' + desc 'Match entries of prefix-lists for IPv6. An array of [pf1, pf2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property match_ipv6_addr_prefix_list + + newproperty(:match_ipv6_multicast_enable) do + desc 'Enable match IPv6 multicast' + + newvalues(:true, :false, :default) + end # property match_ipv6_multicast_enable + + newproperty(:match_ipv6_multicast_group_addr) do + desc "Match IPv6 multicast group prefix. Valid values are string, + keyword 'default'" + + munge do |value| + value = :default if value == 'default' + value + end + end # property match_ipv6_multicast_group_addr + + newproperty(:match_ipv6_multicast_group_range_begin_addr) do + desc "Match IPv6 multicast group address begin range. + Valid values are string, keyword 'default'" + + munge do |value| + value = :default if value == 'default' + value + end + end # property match_ipv6_multicast_group_range_begin_addr + + newproperty(:match_ipv6_multicast_group_range_end_addr) do + desc "Match IPv6 multicast group address end range. + Valid values are string, keyword 'default'" + + munge do |value| + value = :default if value == 'default' + value + end + end # property match_ipv6_multicast_group_range_end_addr + + newproperty(:match_ipv6_multicast_rp_addr) do + desc "Match IPv6 multicast rendezvous prefix. Valid values are string, + keyword 'default'" + + munge do |value| + value = :default if value == 'default' + value + end + end # property match_ipv6_multicast_rp_addr + + newproperty(:match_ipv6_multicast_rp_type) do + desc 'Match IPv6 multicast rendezvous point type' + + newvalues(:ASM, :Bidir, :default) + end # property match_ipv6_multicast_rp_type + + newproperty(:match_ipv6_multicast_src_addr) do + desc "Match IPv6 multicast source prefix. Valid values are string, + keyword 'default'" + + munge do |value| + value = :default if value == 'default' + value + end + end # property match_ipv6_multicast_src_addr + + newproperty(:match_ipv6_next_hop_prefix_list, array_matching: :all) do + format = '[pf1, pf2]' + desc 'Match entries of prefix-lists for next-hop address of route for IPv6. An array of [pf1, pf2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property match_ipv6_next_hop_prefix_list + + newproperty(:match_ipv6_route_src_prefix_list, array_matching: :all) do + format = '[pf1, pf2]' + desc 'Match entries of prefix-lists for advertising source address of route for IPv6. An array of [pf1, pf2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property match_ipv6_route_src_prefix_list + + newproperty(:match_length, array_matching: :all) do + format = '[minlen, maxlen]' + desc 'Match packet length. An array of [minlen, maxlen]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property match_length + + newproperty(:match_mac_list, array_matching: :all) do + format = '[list1, list2]' + desc 'Match entries of mac-lists. An array of [list1, list2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property match_mac_list + + newproperty(:match_metric, array_matching: :all) do + format = '[[metric, deviation], [met, dev]]' + desc 'An array of [metric, deviation] pairs. '\ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + fail("Value must match format #{format}") unless value.is_a?(Array) + value + end + end + end # property match_metric + + newproperty(:match_ospf_area, array_matching: :all) do + format = '[area1, area2]' + desc 'Match entries of ospf area IDs. An array of [area1, area2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property match_ospf_area + + newproperty(:match_route_type_external) do + desc 'Enable match external route type (BGP, EIGRP and OSPF type 1/2)' + + newvalues(:true, :false, :default) + end # property match_route_type_external + + newproperty(:match_route_type_inter_area) do + desc 'Enable match OSPF inter area type' + + newvalues(:true, :false, :default) + end # property match_route_type_inter_area + + newproperty(:match_route_type_internal) do + desc 'Enable match OSPF inter area type (OSPF intra/inter area)' + + newvalues(:true, :false, :default) + end # property match_route_type_internal + + newproperty(:match_route_type_intra_area) do + desc 'Enable match OSPF intra area route' + + newvalues(:true, :false, :default) + end # property match_route_type_intra_area + + newproperty(:match_route_type_level_1) do + desc 'Enable match IS-IS level-1 route' + + newvalues(:true, :false, :default) + end # property match_route_type_level_1 + + newproperty(:match_route_type_level_2) do + desc 'Enable match IS-IS level-2 route' + + newvalues(:true, :false, :default) + end # property match_route_type_level_2 + + newproperty(:match_route_type_local) do + desc 'Enable match locally generated route' + + newvalues(:true, :false, :default) + end # property match_route_type_local + + newproperty(:match_route_type_nssa_external) do + desc 'Enable match nssa-external route (OSPF type 1/2)' + + newvalues(:true, :false, :default) + end # property match_route_type_nssa_external + + newproperty(:match_route_type_type_1) do + desc 'Enable match OSPF external type 1 route' + + newvalues(:true, :false, :default) + end # property match_route_type_type_1 + + newproperty(:match_route_type_type_2) do + desc 'Enable match OSPF external type 2 route' + + newvalues(:true, :false, :default) + end # property match_route_type_type_2 + + newproperty(:match_src_proto, array_matching: :all) do + format = '[pr1, pr2]' + desc 'Match source protocol. An array of [pr1, pr2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property match_src_proto + + newproperty(:match_tag, array_matching: :all) do + format = '[tag1, tag2]' + desc 'Match tag of route. An array of [tag1, tag2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property match_tag + + newproperty(:match_vlan) do + desc "Match Vlan ID. Valid values are string, + keyword 'default'" + + munge do |value| + value = value == 'default' ? :default : PuppetX::Cisco::Utils.range_summarize(value) + value = value.gsub(',', ', ') unless value == :default + value + end + end # property match_vlan + + newproperty(:set_as_path_prepend, array_matching: :all) do + format = '[asn1, asn2]' + desc 'Prepend string for a BGP AS-path attribute. An array of [asn1, asn2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property set_as_path_prepend + + newproperty(:set_as_path_prepend_last_as) do + desc "Number of last-AS prepends. Valid values are + integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property set_as_path_prepend_last_as + + newproperty(:set_as_path_tag) do + desc 'Set the tag as an AS-path attribute' + + newvalues(:true, :false, :default) + end # property set_as_path_tag + + newproperty(:set_comm_list) do + desc "Set BGP community list (for deletion). Valid values are string, + keyword 'default'" + + munge do |value| + value = :default if value == 'default' + value + end + end # property set_comm_list + + newproperty(:set_community_additive) do + desc 'Add to existing BGP community' + + newvalues(:true, :false, :default) + end # property set_community_additive + + newproperty(:set_community_asn, array_matching: :all) do + format = '[asn1, asn2]' + desc 'Set community number. An array of [asn1, asn2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property set_community_asn + + newproperty(:set_community_internet) do + desc 'Set Internet community' + + newvalues(:true, :false, :default) + end # property set_community_internet + + newproperty(:set_community_local_as) do + desc 'Do not send outside local AS' + + newvalues(:true, :false, :default) + end # property set_community_local_as + + newproperty(:set_community_no_advtertise) do + desc 'Do not advertise to any peer' + + newvalues(:true, :false, :default) + end # property set_community_no_advtertise + + newproperty(:set_community_no_export) do + desc 'Do not export to next AS' + + newvalues(:true, :false, :default) + end # property set_community_no_export + + newproperty(:set_community_none) do + desc 'Set no community attribute' + + newvalues(:true, :false, :default) + end # property set_community_none + + newproperty(:set_dampening_half_life) do + desc "Set half-life time for the penalty of BGP route flap dampening. + Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property set_dampening_half_life + + newproperty(:set_dampening_max_duation) do + desc "Set maximum duration to suppress a stable route of BGP route + flap dampening. Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property set_dampening_max_duation + + newproperty(:set_dampening_reuse) do + desc "Set penalty to start reusing a route of BGP route flap dampening. + Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property set_dampening_reuse + + newproperty(:set_dampening_suppress) do + desc "Set penalty to start suppressing a route of BGP route + flap dampening. Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property set_dampening_suppress + + newproperty(:set_distance_igp_ebgp) do + desc "Set administrative distance for IGP or EBGP routes. + Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property set_distance_igp_ebgp + + newproperty(:set_distance_internal) do + desc "Set administrative distance for internal routes. + Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property set_distance_internal + + newproperty(:set_distance_local) do + desc "Set administrative distance for local routes. + Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property set_distance_local + + newproperty(:set_extcomm_list) do + desc "Set BGP extended community list (for deletion). Valid values are string, + keyword 'default'" + + munge do |value| + value = :default if value == 'default' + value + end + end # property set_extcomm_list + + newproperty(:set_extcommunity_4bytes_additive) do + desc 'Add to existing generic extcommunity' + + newvalues(:true, :false, :default) + end # property set_extcommunity_4bytes_additive + + newproperty(:set_extcommunity_4bytes_non_transitive, array_matching: :all) do + format = '[nt1, nt2]' + desc 'Set non-transitive extended community. An array of [nt1, nt2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property set_extcommunity_4bytes_non_transitive + + newproperty(:set_extcommunity_4bytes_none) do + desc 'Set no extcommunity generic attribute' + + newvalues(:true, :false, :default) + end # property set_extcommunity_4bytes_none + + newproperty(:set_extcommunity_4bytes_transitive, array_matching: :all) do + format = '[tr1, tr2]' + desc 'Set transitive extended community. An array of [tr1, tr2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property set_extcommunity_4bytes_transitive + + newproperty(:set_extcommunity_cost_igp, array_matching: :all) do + format = '[[communityId, cost], [cid, co]]' + desc 'An array of [communityId, cost] pairs. '\ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + fail("Value must match format #{format}") unless value.is_a?(Array) + value + end + end + end # property set_extcommunity_cost_igp + + newproperty(:set_extcommunity_cost_pre_bestpath, array_matching: :all) do + format = '[[communityId, cost], [cid, co]]' + desc 'An array of [communityId, cost] pairs. '\ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + fail("Value must match format #{format}") unless value.is_a?(Array) + value + end + end + end # property set_extcommunity_cost_pre_bestpath + + newproperty(:set_extcommunity_rt_additive) do + desc 'Set add to existing route target extcommunity' + + newvalues(:true, :false, :default) + end # property set_extcommunity_rt_additive + + newproperty(:set_extcommunity_rt_asn, array_matching: :all) do + format = '[asn1, asn2]' + desc 'Set community number. An array of [asn1, asn2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property set_extcommunity_rt_asn + + newproperty(:set_forwarding_addr) do + desc 'Set the forwarding address' + + newvalues(:true, :false, :default) + end # property set_forwarding_addr + + newproperty(:set_interface) do + desc 'Set output interface' + + newvalues(:Null0, :default) + end # property set_interface + + newproperty(:set_ipv4_default_next_hop, array_matching: :all) do + format = '[dnh1, dnh2]' + desc 'Set default next-hop IPv4 address. An array of [dnh1, dnh2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property set_ipv4_default_next_hop + + newproperty(:set_ipv4_default_next_hop_load_share) do + desc 'Enable default IPv4 next-hop load-sharing' + + newvalues(:true, :false, :default) + end # property set_ipv4_default_next_hop_load_share + + newproperty(:set_ipv4_next_hop, array_matching: :all) do + format = '[nh1, nh2]' + desc 'Set default next-hop ip address. An array of [nh1, nh2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property set_ipv4_next_hop + + newproperty(:set_ipv4_next_hop_load_share) do + desc 'Enable IPv4 next-hop load-sharing' + + newvalues(:true, :false, :default) + end # property set_ipv4_next_hop_load_share + + newproperty(:set_ipv4_next_hop_peer_addr) do + desc 'Enable IPv4 next-hop peer address' + + newvalues(:true, :false, :default) + end # property set_ipv4_next_hop_peer_addr + + newproperty(:set_ipv4_next_hop_redist) do + desc 'Enable IPv4 next-hop unchanged address during redistribution' + + newvalues(:true, :false, :default) + end # property set_ipv4_next_hop_redist + + newproperty(:set_ipv4_next_hop_unchanged) do + desc 'Enable IPv4 next-hop unchanged address' + + newvalues(:true, :false, :default) + end # property set_ipv4_next_hop_unchanged + + newproperty(:set_ipv4_precedence) do + desc 'Set precedence field' + + newvalues(:critical, :flash, :'flash-override', :immediate, + :internet, :network, :priority, :routine, :default) + end # property set_ipv4_precedence + + newproperty(:set_ipv4_prefix) do + desc "Set IPv4 prefix-list. Valid values are string, + keyword 'default'" + + munge do |value| + value = :default if value == 'default' + value + end + end # property set_ipv4_prefix + + newproperty(:set_ipv6_default_next_hop, array_matching: :all) do + format = '[nh1, nh2]' + desc 'Set default next-hop IPv6 address. An array of [nh1, nh2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property set_ipv6_default_next_hop + + newproperty(:set_ipv6_default_next_hop_load_share) do + desc 'Enable default IPv6 next-hop load-sharing' + + newvalues(:true, :false, :default) + end # property set_ipv6_default_next_hop_load_share + + newproperty(:set_ipv6_next_hop, array_matching: :all) do + format = '[dnh1, dnh2]' + desc 'Set default next-hop ip address. An array of [dnh1, dnh2 and so on]' \ + "Valid values match format #{format}." + + # Override puppet's insync method, which checks whether current value is + # equal to value specified in manifest. Make sure puppet considers + # 2 arrays with same elements but in different order as equal. + def insync?(is) + (is.size == should.size && is.sort == should.sort) + end + + def should_to_s(value) + value.inspect + end + + def is_to_s(value) + value.inspect + end + + munge do |value| + begin + return value = :default if value == 'default' + value + end + end + end # property set_ipv6_next_hop + + newproperty(:set_ipv6_next_hop_load_share) do + desc 'Enable IPv6 next-hop load-sharing' + + newvalues(:true, :false, :default) + end # property set_ipv6_next_hop_load_share + + newproperty(:set_ipv6_next_hop_peer_addr) do + desc 'Enable IPv6 next-hop peer address' + + newvalues(:true, :false, :default) + end # property set_ipv6_next_hop_peer_addr + + newproperty(:set_ipv6_next_hop_redist) do + desc 'Enable IPv6 next-hop unchanged address during redistribution' + + newvalues(:true, :false, :default) + end # property set_ipv6_next_hop_redist + + newproperty(:set_ipv6_next_hop_unchanged) do + desc 'Enable IPv6 next-hop unchanged address' + + newvalues(:true, :false, :default) + end # property set_ipv6_next_hop_unchanged + + newproperty(:set_ipv6_precedence) do + desc 'Set precedence field' + + newvalues(:critical, :flash, :'flash-override', :immediate, + :internet, :network, :priority, :routine, :default) + end # property set_ipv6_precedence + + newproperty(:set_ipv6_prefix) do + desc "Set IPv6 prefix-list. Valid values are string, + keyword 'default'" + + munge do |value| + value = :default if value == 'default' + value + end + end # property set_ipv6_prefix + + newproperty(:set_level) do + desc 'Set where to import route' + + newvalues(:'level-1', :'level-1-2', :'level-2', :default) + end # property set_level + + newproperty(:set_local_preference) do + desc "Set BGP local preference path attribute. + Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property set_local_preference + + newproperty(:set_metric_additive) do + desc 'Set add to metric' + + newvalues(:true, :false, :default) + end # property set_metric_additive + + newproperty(:set_metric_bandwidth) do + desc "Set metric value or Bandwidth in kbps. + Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property set_metric_bandwidth + + newproperty(:set_metric_delay) do + desc "Set IGRP delay metric. + Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property set_metric_delay + + newproperty(:set_metric_effective_bandwidth) do + desc "Set IGRP Effective bandwidth metric. + Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property set_metric_effective_bandwidth + + newproperty(:set_metric_mtu) do + desc "Set IGRP MTU of the path. + Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property set_metric_mtu + + newproperty(:set_metric_reliability) do + desc "Set IGRP reliability metric. + Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property set_metric_reliability + + newproperty(:set_metric_type) do + desc 'Set type of metric for destination routing protocol' + + newvalues(:external, :internal, :'type-1', :'type-2', :default) + end # property set_metric_type + + newproperty(:set_nssa_only) do + desc 'Set OSPF NSSA Areas' + + newvalues(:true, :false, :default) + end # property set_nssa_only + + newproperty(:set_origin) do + desc 'Set BGP origin code' + + newvalues(:egp, :igp, :incomplete, :default) + end # property set_origin + + newproperty(:set_path_selection) do + desc 'Set path selection criteria for BGP' + + newvalues(:true, :false, :default) + end # property set_path_selection + + newproperty(:set_tag) do + desc "Set tag value for destination routing protocol. + Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property set_tag + + newproperty(:set_vrf) do + desc "Set the VRF for next-hop resolution. Valid values are string, + keyword 'default'" + + munge do |value| + value = :default if value == 'default' + value + end + end # property set_vrf + + newproperty(:set_weight) do + desc "Set BGP weight for routing table. + Valid values are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property set_weight + + def cmprop(prop) + self[prop].nil? || self[prop].empty? || self[prop] == :default + end + + def check_match_ipv4_multicast + return unless self[:match_ipv4_multicast_enable] + fail ArgumentError, 'At least one of the ipv4 multicast properties MUST be non default' if + cmprop(:match_ipv4_multicast_src_addr) && + cmprop(:match_ipv4_multicast_group_addr) && + cmprop(:match_ipv4_multicast_rp_addr) && + cmprop(:match_ipv4_multicast_group_range_begin_addr) && + cmprop(:match_ipv4_multicast_group_range_end_addr) + end + + def check_match_ipv6_multicast + return unless self[:match_ipv6_multicast_enable] + fail ArgumentError, 'At least one of the ipv6 multicast properties MUST be non default' if + cmprop(:match_ipv6_multicast_src_addr) && + cmprop(:match_ipv6_multicast_group_addr) && + cmprop(:match_ipv6_multicast_rp_addr) && + cmprop(:match_ipv6_multicast_group_range_begin_addr) && + cmprop(:match_ipv6_multicast_group_range_end_addr) + end + + validate do + check_match_ipv4_multicast + check_match_ipv6_multicast + end +end diff --git a/tests/beaker_tests/cisco_route_map/test_route_map.rb b/tests/beaker_tests/cisco_route_map/test_route_map.rb new file mode 100644 index 000000000..67bebd483 --- /dev/null +++ b/tests/beaker_tests/cisco_route_map/test_route_map.rb @@ -0,0 +1,575 @@ +############################################################################### +# Copyright (c) 2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### + +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# Test hash top-level keys +tests = { + master: master, + agent: agent, + operating_system: 'nexus', + resource_name: 'cisco_route_map', +} + +skip_unless_supported(tests) + +# Test hash test cases +tests[:default] = { + desc: '1.1 Defaults', + title_pattern: 'rm1 123 permit', + manifest_props: { + description: 'default', + match_as_number: 'default', + match_as_number_as_path_list: 'default', + match_community: 'default', + match_community_exact_match: 'default', + match_evpn_route_type_1: 'default', + match_evpn_route_type_2_all: 'default', + match_evpn_route_type_2_mac_ip: 'default', + match_evpn_route_type_2_mac_only: 'default', + match_evpn_route_type_3: 'default', + match_evpn_route_type_4: 'default', + match_evpn_route_type_5: 'default', + match_evpn_route_type_6: 'default', + match_evpn_route_type_all: 'default', + match_ext_community: 'default', + match_ext_community_exact_match: 'default', + match_interface: 'default', + match_ipv4_addr_access_list: 'default', + match_ipv4_addr_prefix_list: 'default', + match_ipv4_multicast_enable: 'default', + match_ipv4_multicast_group_addr: 'default', + match_ipv4_multicast_group_range_begin_addr: 'default', + match_ipv4_multicast_group_range_end_addr: 'default', + match_ipv4_multicast_rp_addr: 'default', + match_ipv4_multicast_rp_type: 'default', + match_ipv4_multicast_src_addr: 'default', + match_ipv4_next_hop_prefix_list: 'default', + match_ipv4_route_src_prefix_list: 'default', + match_ipv6_addr_access_list: 'default', + match_ipv6_addr_prefix_list: 'default', + match_ipv6_multicast_enable: 'default', + match_ipv6_multicast_group_addr: 'default', + match_ipv6_multicast_group_range_begin_addr: 'default', + match_ipv6_multicast_group_range_end_addr: 'default', + match_ipv6_multicast_rp_addr: 'default', + match_ipv6_multicast_rp_type: 'default', + match_ipv6_multicast_src_addr: 'default', + match_ipv6_next_hop_prefix_list: 'default', + match_ipv6_route_src_prefix_list: 'default', + match_length: 'default', + match_mac_list: 'default', + match_metric: 'default', + match_ospf_area: 'default', + match_route_type_external: 'default', + match_route_type_inter_area: 'default', + match_route_type_internal: 'default', + match_route_type_intra_area: 'default', + match_route_type_level_1: 'default', + match_route_type_level_2: 'default', + match_route_type_local: 'default', + match_route_type_nssa_external: 'default', + match_route_type_type_1: 'default', + match_route_type_type_2: 'default', + match_src_proto: 'default', + match_tag: 'default', + match_vlan: 'default', + set_as_path_prepend: 'default', + set_as_path_prepend_last_as: 'default', + set_as_path_tag: 'default', + set_comm_list: 'default', + set_community_additive: 'default', + set_community_asn: 'default', + set_community_internet: 'default', + set_community_local_as: 'default', + set_community_no_advtertise: 'default', + set_community_no_export: 'default', + set_community_none: 'default', + set_dampening_half_life: 'default', + set_dampening_max_duation: 'default', + set_dampening_reuse: 'default', + set_dampening_suppress: 'default', + set_distance_igp_ebgp: 'default', + set_distance_internal: 'default', + set_distance_local: 'default', + set_extcomm_list: 'default', + set_extcommunity_4bytes_additive: 'default', + set_extcommunity_4bytes_non_transitive: 'default', + set_extcommunity_4bytes_none: 'default', + set_extcommunity_4bytes_transitive: 'default', + set_extcommunity_cost_igp: 'default', + set_extcommunity_cost_pre_bestpath: 'default', + set_extcommunity_rt_additive: 'default', + set_extcommunity_rt_asn: 'default', + set_forwarding_addr: 'default', + set_interface: 'default', + set_ipv4_default_next_hop: 'default', + set_ipv4_default_next_hop_load_share: 'default', + set_ipv4_next_hop: 'default', + set_ipv4_next_hop_load_share: 'default', + set_ipv4_next_hop_peer_addr: 'default', + set_ipv4_next_hop_redist: 'default', + set_ipv4_next_hop_unchanged: 'default', + set_ipv4_precedence: 'default', + set_ipv4_prefix: 'default', + set_ipv6_default_next_hop: 'default', + set_ipv6_default_next_hop_load_share: 'default', + set_ipv6_next_hop: 'default', + set_ipv6_next_hop_load_share: 'default', + set_ipv6_next_hop_peer_addr: 'default', + set_ipv6_next_hop_redist: 'default', + set_ipv6_next_hop_unchanged: 'default', + set_ipv6_precedence: 'default', + set_ipv6_prefix: 'default', + set_level: 'default', + set_local_preference: 'default', + set_metric_additive: 'default', + set_metric_bandwidth: 'default', + set_metric_delay: 'default', + set_metric_effective_bandwidth: 'default', + set_metric_mtu: 'default', + set_metric_reliability: 'default', + set_metric_type: 'default', + set_nssa_only: 'default', + set_origin: 'default', + set_path_selection: 'default', + set_tag: 'default', + set_vrf: 'default', + set_weight: 'default', + }, + code: [0, 2], + resource: { + description: 'false', + match_community_exact_match: 'false', + match_evpn_route_type_1: 'false', + match_evpn_route_type_2_all: 'false', + match_evpn_route_type_2_mac_ip: 'false', + match_evpn_route_type_2_mac_only: 'false', + match_evpn_route_type_3: 'false', + match_evpn_route_type_4: 'false', + match_evpn_route_type_5: 'false', + match_evpn_route_type_6: 'false', + match_evpn_route_type_all: 'false', + match_ext_community_exact_match: 'false', + match_ipv4_addr_access_list: 'false', + match_ipv4_multicast_enable: 'false', + match_ipv6_addr_access_list: 'false', + match_ipv6_multicast_enable: 'false', + match_route_type_external: 'false', + match_route_type_inter_area: 'false', + match_route_type_internal: 'false', + match_route_type_intra_area: 'false', + match_route_type_level_1: 'false', + match_route_type_level_2: 'false', + match_route_type_local: 'false', + match_route_type_nssa_external: 'false', + match_route_type_type_1: 'false', + match_route_type_type_2: 'false', + set_as_path_prepend_last_as: 'false', + set_as_path_tag: 'false', + set_comm_list: 'false', + set_community_additive: 'false', + set_community_internet: 'false', + set_community_local_as: 'false', + set_community_no_advtertise: 'false', + set_community_no_export: 'false', + set_community_none: 'false', + set_dampening_half_life: 'false', + set_dampening_max_duation: 'false', + set_dampening_reuse: 'false', + set_dampening_suppress: 'false', + set_distance_igp_ebgp: 'false', + set_distance_internal: 'false', + set_distance_local: 'false', + set_extcomm_list: 'false', + set_extcommunity_4bytes_additive: 'false', + set_extcommunity_4bytes_none: 'false', + set_extcommunity_rt_additive: 'false', + set_forwarding_addr: 'false', + set_interface: 'false', + set_ipv4_default_next_hop_load_share: 'false', + set_ipv4_next_hop_load_share: 'false', + set_ipv4_next_hop_peer_addr: 'false', + set_ipv4_next_hop_redist: 'false', + set_ipv4_next_hop_unchanged: 'false', + set_ipv4_precedence: 'false', + set_ipv4_prefix: 'false', + set_ipv6_default_next_hop_load_share: 'false', + set_ipv6_next_hop_load_share: 'false', + set_ipv6_next_hop_peer_addr: 'false', + set_ipv6_next_hop_redist: 'false', + set_ipv6_next_hop_unchanged: 'false', + set_ipv6_precedence: 'false', + set_ipv6_prefix: 'false', + set_level: 'false', + set_local_preference: 'false', + set_metric_additive: 'false', + set_metric_bandwidth: 'false', + set_metric_delay: 'false', + set_metric_effective_bandwidth: 'false', + set_metric_mtu: 'false', + set_metric_reliability: 'false', + set_metric_type: 'false', + set_nssa_only: 'false', + set_origin: 'false', + set_path_selection: 'false', + set_tag: 'false', + set_vrf: 'false', + set_weight: 'false', + }, +} + +tests[:non_default_1] = { + desc: '2.1 Non Defaults 1', + title_pattern: 'rm1 123 permit', + manifest_props: { + description: 'map1', + match_as_number: ['3', '22-34', '38', '101-110'], + match_as_number_as_path_list: %w(abc xyz pqr), + match_community: %w(public private), + match_community_exact_match: 'true', + match_evpn_route_type_1: 'true', + match_evpn_route_type_2_all: 'true', + match_evpn_route_type_2_mac_ip: 'true', + match_evpn_route_type_2_mac_only: 'true', + match_evpn_route_type_3: 'true', + match_evpn_route_type_4: 'true', + match_evpn_route_type_5: 'true', + match_evpn_route_type_6: 'true', + match_evpn_route_type_all: 'true', + match_ext_community: %w(epublic eprivate), + match_ext_community_exact_match: 'true', + match_interface: %w(loopback2 mgmt0 null0), + match_ipv4_addr_access_list: 'access', + match_ipv4_addr_prefix_list: %w(p1 p7 pre5), + match_ipv4_multicast_enable: 'true', + match_ipv4_multicast_group_addr: '239.2.2.2/32', + match_ipv4_multicast_rp_addr: '242.1.1.1/32', + match_ipv4_multicast_rp_type: 'ASM', + match_ipv4_multicast_src_addr: '242.1.1.1/32', + match_ipv4_next_hop_prefix_list: %w(nh5 nh1 nh42), + match_ipv4_route_src_prefix_list: %w(rs2 rs22 pre15), + match_ipv6_multicast_enable: 'true', + match_ipv6_multicast_group_addr: 'ff0e::2:101:0:0/96', + match_ipv6_multicast_rp_addr: '2001::348:0:0/96', + match_ipv6_multicast_rp_type: 'ASM', + match_ipv6_multicast_src_addr: '2001::348:0:0/96', + match_ipv6_next_hop_prefix_list: %w(nhv6 v6nh1 nhv42), + match_ipv6_route_src_prefix_list: %w(rsv6 rs22v6 prev6), + match_mac_list: %w(mac1 listmac), + match_metric: [%w(1 0), %w(8 0), %w(224 9), %w(23 0), %w(5 8), %w(6 0)], + match_ospf_area: %w(10 7 222), + match_route_type_external: 'true', + match_route_type_inter_area: 'true', + match_route_type_internal: 'true', + match_route_type_intra_area: 'true', + match_route_type_level_1: 'true', + match_route_type_level_2: 'true', + match_route_type_local: 'true', + match_route_type_nssa_external: 'true', + match_route_type_type_1: 'true', + match_route_type_type_2: 'true', + match_src_proto: %w(tcp udp igmp), + match_tag: %w(5 342 28 3221), + match_vlan: '32, 45-200, 300-399, 402', + set_as_path_prepend: ['55.77', '12', '45.3'], + set_as_path_prepend_last_as: 1, + set_as_path_tag: 'true', + set_comm_list: 'abc', + set_community_additive: 'true', + set_community_asn: ['11:22', '33:44', '123:11'], + set_community_internet: 'true', + set_community_local_as: 'true', + set_community_no_advtertise: 'true', + set_community_no_export: 'true', + set_dampening_half_life: 6, + set_dampening_max_duation: 55, + set_dampening_reuse: 22, + set_dampening_suppress: 44, + set_distance_igp_ebgp: 1, + set_distance_internal: 2, + set_distance_local: 3, + set_extcomm_list: 'xyz', + set_extcommunity_4bytes_additive: 'true', + set_extcommunity_4bytes_non_transitive: ['21:42', '43:22', '59:17'], + set_extcommunity_4bytes_transitive: ['11:22', '33:44', '66:77'], + set_extcommunity_cost_igp: [%w(0 23), %w(3 33), %w(100 10954)], + set_extcommunity_cost_pre_bestpath: [%w(23 999), %w(88 482), %w(120 2323)], + set_extcommunity_rt_additive: 'true', + set_extcommunity_rt_asn: ['11:22', '33:44', '12.22.22.22:12', '123.256:543'], + set_forwarding_addr: 'true', + set_ipv4_next_hop: ['3.3.3.3', '4.4.4.4'], + set_ipv4_next_hop_load_share: 'true', + set_ipv4_precedence: 'critical', + set_ipv4_prefix: 'abcdef', + set_ipv6_next_hop: ['2000::1', '2000::11', '2000::22'], + set_ipv6_next_hop_load_share: 'true', + set_ipv6_prefix: 'wxyz', + set_level: 'level-1', + set_local_preference: 100, + set_metric_additive: 'false', + set_metric_bandwidth: 44, + set_metric_delay: 55, + set_metric_effective_bandwidth: 77, + set_metric_mtu: 88, + set_metric_reliability: 66, + set_metric_type: 'external', + set_nssa_only: 'true', + set_origin: 'egp', + set_path_selection: 'true', + set_tag: 101, + set_weight: 222, + }, +} + +tests[:non_default_2] = { + desc: '2.2 Non Defaults 2', + title_pattern: 'rm2 149 deny', + manifest_props: { + match_ipv6_addr_prefix_list: %w(pv6 pv67 prev6), + match_ipv4_multicast_enable: 'true', + match_ipv4_multicast_src_addr: '242.1.1.1/32', + match_ipv4_multicast_group_range_begin_addr: '239.1.1.1', + match_ipv4_multicast_group_range_end_addr: '239.2.2.2', + match_ipv4_multicast_rp_addr: '242.1.1.1/32', + match_ipv4_multicast_rp_type: 'Bidir', + match_ipv6_addr_access_list: 'v6access', + match_ipv6_multicast_enable: 'true', + match_ipv6_multicast_src_addr: '2001::348:0:0/96', + match_ipv6_multicast_group_range_begin_addr: 'ff01::', + match_ipv6_multicast_group_range_end_addr: 'ff02::', + match_ipv6_multicast_rp_addr: '2001::348:0:0/96', + match_ipv6_multicast_rp_type: 'Bidir', + set_community_none: 'true', + set_extcommunity_4bytes_none: 'true', + set_ipv4_default_next_hop: ['1.1.1.1', '2.2.2.2'], + set_ipv4_default_next_hop_load_share: 'true', + set_ipv4_next_hop_peer_addr: 'true', + set_ipv6_precedence: 'flash', + set_level: 'level-1-2', + set_metric_additive: 'true', + set_metric_bandwidth: 33, + set_metric_type: 'type-2', + set_origin: 'incomplete', + }, +} + +tests[:non_default_3] = { + desc: '2.3 Non Defaults 3', + title_pattern: 'rm3 159 deny', + manifest_props: { + set_ipv6_default_next_hop: ['2000::1', '2000::11', '2000::22'], + set_ipv6_default_next_hop_load_share: 'true', + set_ipv6_next_hop_peer_addr: 'true', + }, +} + +tests[:non_default_4] = { + desc: '2.4 Non Defaults 4', + title_pattern: 'rm4 200 permit', + manifest_props: { + set_interface: 'Null0', + set_ipv4_next_hop_redist: 'true', + set_ipv6_next_hop_redist: 'true', + set_ipv4_next_hop_unchanged: 'true', + set_ipv6_next_hop_unchanged: 'true', + }, +} + +tests[:non_default_5] = { + desc: '2.5 Non Defaults 5', + title_pattern: 'rm5 199 deny', + manifest_props: { + match_length: %w(45 345), + set_vrf: 'igp', + }, +} + +def unsupp_n3k + im = nexus_image + unprops = [] + unprops << + :match_evpn_route_type_1 << + :match_evpn_route_type_2_all << + :match_evpn_route_type_2_mac_ip << + :match_evpn_route_type_2_mac_only << + :match_evpn_route_type_3 << + :match_evpn_route_type_4 << + :match_evpn_route_type_5 << + :match_evpn_route_type_6 << + :match_evpn_route_type_all << + :match_length << + :match_mac_list << + :match_vlan << + :set_vrf + unprops << + :match_ospf_area << + :set_ipv4_next_hop_redist << + :set_ipv6_next_hop_redist if im[/(I2|I3|I4)/] + unprops +end + +def unsupp_n56k + unprops = [] + unprops << + :match_ospf_area << + :set_ipv4_default_next_hop << + :set_ipv4_default_next_hop_load_share << + :set_ipv4_next_hop_load_share << + :set_ipv4_prefix << + :set_ipv6_default_next_hop << + :set_ipv6_default_next_hop_load_share << + :set_ipv6_next_hop_load_share << + :set_ipv6_prefix << + :set_vrf + unprops +end + +def unsupp_n7k + unprops = [] + unprops << + :match_ospf_area + unprops +end + +def unsupp_n9k + im = nexus_image + unprops = [] + unprops << + :match_evpn_route_type_1 << + :match_evpn_route_type_2_all << + :match_evpn_route_type_2_mac_ip << + :match_evpn_route_type_2_mac_only << + :match_evpn_route_type_3 << + :match_evpn_route_type_4 << + :match_evpn_route_type_5 << + :match_evpn_route_type_6 << + :match_evpn_route_type_all << + :match_length << + :match_mac_list << + :match_vlan << + :set_ipv4_default_next_hop << + :set_ipv4_default_next_hop_load_share << + :set_ipv6_default_next_hop << + :set_ipv6_default_next_hop_load_share << + :set_extcommunity_rt_asn << + :set_vrf + unprops << + :set_ipv4_next_hop_load_share << + :set_ipv6_next_hop_load_share << + :match_ospf_area << + :set_ipv4_next_hop_redist << + :set_ipv6_next_hop_redist if im[/(I2|I3|I4)/] + unprops << :match_metric if im['I4'] + unprops +end + +def unsupp_n9kf + unprops = [] + unprops << + :match_evpn_route_type_1 << + :match_evpn_route_type_2_all << + :match_evpn_route_type_2_mac_ip << + :match_evpn_route_type_2_mac_only << + :match_evpn_route_type_3 << + :match_evpn_route_type_4 << + :match_evpn_route_type_5 << + :match_evpn_route_type_6 << + :match_evpn_route_type_all << + :match_length << + :match_mac_list << + :match_metric << + :match_ospf_area << + :match_vlan << + :set_extcommunity_4bytes_additive << + :set_extcommunity_4bytes_non_transitive << + :set_extcommunity_4bytes_transitive << + :set_extcommunity_cost_igp << + :set_extcommunity_cost_pre_bestpath << + :set_extcommunity_rt_additive << + :set_extcommunity_rt_asn << + :set_forwarding_addr << + :set_ipv4_default_next_hop << + :set_ipv4_default_next_hop_load_share << + :set_ipv6_default_next_hop << + :set_ipv6_default_next_hop_load_share << + :set_ipv4_next_hop << + :set_ipv4_next_hop_load_share << + :set_ipv4_precedence << + :set_ipv4_prefix << + :set_ipv6_next_hop << + :set_ipv6_next_hop_load_share << + :set_ipv6_prefix << + :set_vrf + unprops +end + +def unsupported_properties(_tests, _id) + if platform[/n3k/] + unsupp_n3k + elsif platform[/n(5|6)k/] + unsupp_n56k + elsif platform[/n7k/] + unsupp_n7k + elsif platform[/n9k$/] + unsupp_n9k + elsif platform[/n9k-f/] + unsupp_n9kf + end +end + +def cleanup(agent) + test_set(agent, 'no route-map rm1') + test_set(agent, 'no route-map rm2') + test_set(agent, 'no route-map rm3') + test_set(agent, 'no route-map rm4') + test_set(agent, 'no route-map rm5') +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(agent) } + cleanup(agent) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + test_harness_run(tests, :default) + + id = :default + tests[id][:ensure] = :absent + test_harness_run(tests, id) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + + test_harness_run(tests, :non_default_1) + test_harness_run(tests, :non_default_2) + test_harness_run(tests, :non_default_3) + test_harness_run(tests, :non_default_4) + test_harness_run(tests, :non_default_5) +end + +logger.info("TestCase :: #{tests[:resource_name]} :: End") From f466aa2427c498a2d7fd0ba402a08bd4638fa404 Mon Sep 17 00:00:00 2001 From: saichint Date: Tue, 7 Feb 2017 11:54:03 -0800 Subject: [PATCH 139/203] route-map provider (#421) * pim bfd * doc * fix n7k atherton hsrp * review comments for doc * review comments for doc * partial new code * rest of the code * manifest * fix get issue * fix few errors in manifest * fix manifest * fix few issues * fix next-hop sets * fix manifest * fix few issues * fix vlan string munge * beaker tests * fix manifest * fix beaker * fix case sensitivity for int_hsrp_group name * remove downcase from provider * fixes for fretta * doc * remove 2 props from I4 code * review comments * review comments * fix doc * review comments * add validate for multicast props * add validate for multicast props --- lib/puppet/type/cisco_route_map.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/puppet/type/cisco_route_map.rb b/lib/puppet/type/cisco_route_map.rb index 0257acb73..7122180d0 100644 --- a/lib/puppet/type/cisco_route_map.rb +++ b/lib/puppet/type/cisco_route_map.rb @@ -1633,7 +1633,7 @@ def cmprop(prop) end def check_match_ipv4_multicast - return unless self[:match_ipv4_multicast_enable] + return if cmprop(:match_ipv4_multicast_enable) fail ArgumentError, 'At least one of the ipv4 multicast properties MUST be non default' if cmprop(:match_ipv4_multicast_src_addr) && cmprop(:match_ipv4_multicast_group_addr) && @@ -1643,7 +1643,7 @@ def check_match_ipv4_multicast end def check_match_ipv6_multicast - return unless self[:match_ipv6_multicast_enable] + return if cmprop(:match_ipv6_multicast_enable) fail ArgumentError, 'At least one of the ipv6 multicast properties MUST be non default' if cmprop(:match_ipv6_multicast_src_addr) && cmprop(:match_ipv6_multicast_group_addr) && From 8773215cd1d3065413557fa65f5350601f2d6a04 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Thu, 9 Feb 2017 14:23:00 -0500 Subject: [PATCH 140/203] Fix Chrun: console and vty config #417 (#422) * Strip \r characters * Add testcases to validate config with ctrl chars * Remove puts and add inspect to debugs * Add cleanup before next test * Refactor ctrl character pattern and test * Remove puts statements --- .../provider/cisco_command_config/cisco.rb | 11 +++++---- .../test_command_config.rb | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/puppet/provider/cisco_command_config/cisco.rb b/lib/puppet/provider/cisco_command_config/cisco.rb index 780af6ca3..6dda8cb1b 100644 --- a/lib/puppet/provider/cisco_command_config/cisco.rb +++ b/lib/puppet/provider/cisco_command_config/cisco.rb @@ -42,15 +42,16 @@ def command manifest_hash = Cisco::ConfigParser::Configuration.new(@resource[:command]) # Compare full manifest config to running-config. - existing_str = manifest_hash.compare_with(running_hash) - debug "Existing:\n>#{existing_str}<" + strip_pattern = Regexp.new('^ *| *$|\s') + existing_str = manifest_hash.compare_with(running_hash).gsub(strip_pattern, '') + debug "Existing:\n>#{existing_str.inspect}<" manifest_config_str = Cisco::ConfigParser::Configuration.config_hash_to_str( - manifest_hash.configuration) - debug "Manifest:\n>#{manifest_config_str}<" + manifest_hash.configuration).gsub(strip_pattern, '') + debug "Manifest:\n>#{manifest_config_str.inspect}<" - if existing_str.gsub(/^ *| *$/, '').include?(manifest_config_str) + if existing_str.include?(manifest_config_str) debug 'Current running-config already satisfies manifest' @property_hash[:command] = @resource[:command] else diff --git a/tests/beaker_tests/cisco_command_config/test_command_config.rb b/tests/beaker_tests/cisco_command_config/test_command_config.rb index 49933c313..7847c149a 100644 --- a/tests/beaker_tests/cisco_command_config/test_command_config.rb +++ b/tests/beaker_tests/cisco_command_config/test_command_config.rb @@ -163,6 +163,25 @@ }, } +tests[:control_characters] = { + desc: '1.6 CTRL_CHARS', + # Command indentation is very important! + # Make sure config appears exactly how it nvgens on the switch. + manifest_props: { + command: " +feature bgp\r\n +router bgp 55\r\n + neighbor 1.1.1.1\r\n + address-family ipv6 unicast\r\n + capability additional-paths send\t\r\n + " + }, + resource: { + 'ensure' => 'present', + 'additional_paths_send' => 'enable', + }, +} + def test_set_get stepinfo = 'Test test_get/test_set properties' logger.info("\n#{'-' * 60}\n#{stepinfo}") @@ -211,6 +230,10 @@ def test_harness_run_cc(tests, id, res_cmd) test_harness_run_cc(tests, :bgp_neighbor_af, 'cisco_bgp_neighbor_af') test_harness_run_cc(tests, :loopback, 'cisco_interface') + # Cleanup before next test. + cleanup(agent) + test_harness_run_cc(tests, :control_characters, 'cisco_bgp_neighbor_af') + # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. test_set / test_get") cleanup(agent) From aff38d9120559889740bfea9795911d7ad53bcf0 Mon Sep 17 00:00:00 2001 From: saichint Date: Thu, 16 Feb 2017 10:17:11 -0800 Subject: [PATCH 141/203] Add load-interval props to cisco_interface (#425) * add load-interval attribs to interface * clean up changelog * review comment --- CHANGELOG.md | 4 +++ README.md | 14 ++++++++++ examples/cisco/demo_interface.pp | 11 +++++--- lib/puppet/provider/cisco_interface/cisco.rb | 3 ++ lib/puppet/type/cisco_interface.rb | 28 +++++++++++++++++++ .../cisco_interface/test_interface_L2.rb | 9 ++++++ 6 files changed, 65 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2688f7838..2cf011cd3 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `cisco_route_map` type and provider. ### Added +- Extend cisco_interface with attributes: + - `load_interval_counter_1_delay` + - `load_interval_counter_2_delay` + - `load_interval_counter_3_delay` ### Changed diff --git a/README.md b/README.md index 6a659a7b1..586d08a58 100644 --- a/README.md +++ b/README.md @@ -1990,6 +1990,9 @@ Manages a Cisco Network Interface. Any resource dependency should be run before | `hsrp_use_bia` | Not supported on N5k,N6k
Minimum puppet module version 1.5.0
Supported in OS Version 8.0 and later on N7k | | `hsrp_version` | Not supported on N5k,N6k
Minimum puppet module version 1.5.0
Supported in OS Version 8.0 and later on N7k | | `pim_bfd` | Minimum puppet module version 1.5.0 | +| `load_interval_counter_1_delay` | Minimum puppet module version 1.6.0 | +| `load_interval_counter_2_delay` | Minimum puppet module version 1.6.0 | +| `load_interval_counter_3_delay` | Minimum puppet module version 1.6.0 | #### Parameters @@ -2294,6 +2297,17 @@ HSRP uses this interface's burned in address. Valid values are 'use_bia', 'use_b ##### `hsrp_version` HSRP version for this interface. Valid values are integer, keyword 'default'. +##### load-interval config attributes + +##### `load_interval_counter_1_delay` +Load interval delay for counter 1 in seconds. Valid values are integer, keyword 'default' + +##### `load_interval_counter_2_delay` +Load interval delay for counter 2 in seconds. Valid values are integer, keyword 'default' + +##### `load_interval_counter_3_delay` +Load interval delay for counter 3 in seconds. Valid values are integer, keyword 'default' + -- ### Type: cisco_interface_channel_group diff --git a/examples/cisco/demo_interface.pp b/examples/cisco/demo_interface.pp index f78c79e22..495ebf8b2 100755 --- a/examples/cisco/demo_interface.pp +++ b/examples/cisco/demo_interface.pp @@ -78,10 +78,13 @@ } cisco_interface { 'Ethernet1/3': - description => 'default', - shutdown => 'default', - access_vlan => 'default', - switchport_mode => access, + description => 'default', + shutdown => 'default', + access_vlan => 'default', + load_interval_counter_1_delay => 150, + load_interval_counter_2_delay => 250, + load_interval_counter_3_delay => 90, + switchport_mode => access, } cisco_interface { 'Ethernet1/4': diff --git a/lib/puppet/provider/cisco_interface/cisco.rb b/lib/puppet/provider/cisco_interface/cisco.rb index 553ebbfaf..8f2ab8181 100644 --- a/lib/puppet/provider/cisco_interface/cisco.rb +++ b/lib/puppet/provider/cisco_interface/cisco.rb @@ -61,6 +61,9 @@ :ipv6_acl_in, :ipv6_acl_out, :ipv6_dhcp_relay_src_intf, + :load_interval_counter_1_delay, + :load_interval_counter_2_delay, + :load_interval_counter_3_delay, :mtu, :speed, :duplex, diff --git a/lib/puppet/type/cisco_interface.rb b/lib/puppet/type/cisco_interface.rb index 375ea1d18..5b24e7c5c 100755 --- a/lib/puppet/type/cisco_interface.rb +++ b/lib/puppet/type/cisco_interface.rb @@ -110,6 +110,9 @@ ipv4_arp_timeout => 300, svi_autostate => true, svi_management => true, + load_interval_counter_1_delay => 150, + load_interval_counter_2_delay => 250, + load_interval_counter_3_delay => 90, } cisco_interface { 'ethernet8/1' : description => 'Private-vlan host', @@ -1251,4 +1254,29 @@ def is_to_s(value) munge { |value| value == 'default' ? :default : Integer(value) } end # property hsrp_version + + ############################ + # load-interval attributes # + ############################ + + newproperty(:load_interval_counter_1_delay) do + desc "Load interval delay for counter 1 in seconds. Valid values + are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property load_interval_counter_1_delay + + newproperty(:load_interval_counter_2_delay) do + desc "Load interval delay for counter 2 in seconds. Valid values + are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property load_interval_counter_2_delay + + newproperty(:load_interval_counter_3_delay) do + desc "Load interval delay for counter 3 in seconds. Valid values + are integer, keyword 'default'." + + munge { |value| value == 'default' ? :default : Integer(value) } + end # property load_interval_counter_3_delay end # Puppet::Type.newtype diff --git a/tests/beaker_tests/cisco_interface/test_interface_L2.rb b/tests/beaker_tests/cisco_interface/test_interface_L2.rb index db385aaa0..56391d4fe 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_L2.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_L2.rb @@ -86,6 +86,9 @@ sys_def_sw_shut: true, manifest_props: { shutdown: 'default', + load_interval_counter_1_delay: 'default', + load_interval_counter_2_delay: 'default', + load_interval_counter_3_delay: 'default', storm_control_broadcast: 'default', storm_control_multicast: 'default', storm_control_unicast: 'default', @@ -97,6 +100,9 @@ }, resource: { shutdown: 'true', + load_interval_counter_1_delay: '30', + load_interval_counter_2_delay: '300', + load_interval_counter_3_delay: 'false', storm_control_broadcast: '100.00', storm_control_multicast: '100.00', storm_control_unicast: '100.00', @@ -115,6 +121,9 @@ sys_def_sw_shut: true, manifest_props: { shutdown: 'false', + load_interval_counter_1_delay: '200', + load_interval_counter_2_delay: '100', + load_interval_counter_3_delay: '150', storm_control_broadcast: '22.22', storm_control_multicast: '44.44', storm_control_unicast: '66.66', From 2fcc6ec20e15a26ee7dcfa834c47f54bf26e5ccc Mon Sep 17 00:00:00 2001 From: saichint Date: Fri, 24 Feb 2017 10:55:48 -0800 Subject: [PATCH 142/203] fix double quotes issue for passwords (#426) * add load-interval attribs to interface * clean up changelog * review comment * double quote string cleanup * review comments --- lib/puppet/provider/cisco_tacacs_server/cisco.rb | 15 +++++++++++++++ .../provider/cisco_tacacs_server_host/cisco.rb | 15 +++++++++++++++ lib/puppet/provider/radius_global/cisco.rb | 12 ++++++++++++ lib/puppet/provider/radius_server/cisco.rb | 12 ++++++++++++ lib/puppet/provider/tacacs_global/cisco.rb | 12 ++++++++++++ lib/puppet/provider/tacacs_server/cisco.rb | 12 ++++++++++++ tests/beaker_tests/lib/utilitylib.rb | 3 ++- 7 files changed, 80 insertions(+), 1 deletion(-) diff --git a/lib/puppet/provider/cisco_tacacs_server/cisco.rb b/lib/puppet/provider/cisco_tacacs_server/cisco.rb index 0d79c5ae2..c8ea17ec6 100644 --- a/lib/puppet/provider/cisco_tacacs_server/cisco.rb +++ b/lib/puppet/provider/cisco_tacacs_server/cisco.rb @@ -114,6 +114,21 @@ def destroy @tacacs_server = nil end # destroy + # this method is put here instead of type file due to older + # releases of platform code is not taking care of quotes in + # the manifest properly + def encryption_password + res = @resource[:encryption_password] + ph = @property_hash[:encryption_password] + return ph if res.nil? + return :default if res == :default && + ph == @tacacs_server.default_encryption_password + unless res.start_with?('"') && res.end_with?('"') + ph = ph.gsub(/\A"|"\Z/, '') + end + ph + end + def timeout debug 'Getting timeout.' if @resource[:timeout] == :default && diff --git a/lib/puppet/provider/cisco_tacacs_server_host/cisco.rb b/lib/puppet/provider/cisco_tacacs_server_host/cisco.rb index 2813eec29..2746483a1 100644 --- a/lib/puppet/provider/cisco_tacacs_server_host/cisco.rb +++ b/lib/puppet/provider/cisco_tacacs_server_host/cisco.rb @@ -69,6 +69,21 @@ def destroy @property_flush[:ensure] = :absent end + # this method is put here instead of type file due to older + # releases of platform code is not taking care of quotes in + # the manifest properly + def encryption_password + res = @resource[:encryption_password] + ph = @property_hash[:encryption_password] + return ph if res.nil? + return :default if res == :default && + ph == @tacacs_server_host.default_encryption_password + unless res.start_with?('"') && res.end_with?('"') + ph = ph.gsub(/\A"|"\Z/, '') + end + ph + end + def port if @resource[:port] == :default && @property_hash[:port] == Cisco::TacacsServerHost.default_port diff --git a/lib/puppet/provider/radius_global/cisco.rb b/lib/puppet/provider/radius_global/cisco.rb index 203fcef2d..8a12e96a3 100644 --- a/lib/puppet/provider/radius_global/cisco.rb +++ b/lib/puppet/provider/radius_global/cisco.rb @@ -76,6 +76,18 @@ def self.prefetch(resources) end end # self.prefetch + def key + res = @resource[:key] + ph = @property_hash[:key] + return ph if res.nil? + return :default if res == :default && + ph == @radius_global.default_key + unless res.start_with?('"') && res.end_with?('"') + ph = ph.gsub(/\A"|"\Z/, '') + end + ph + end + def munge_flush(val) if val.is_a?(String) && val.eql?('unset') nil diff --git a/lib/puppet/provider/radius_server/cisco.rb b/lib/puppet/provider/radius_server/cisco.rb index cada4b15b..dc975cb7d 100644 --- a/lib/puppet/provider/radius_server/cisco.rb +++ b/lib/puppet/provider/radius_server/cisco.rb @@ -89,6 +89,18 @@ def self.prefetch(resources) end end # self.prefetch + def key + res = @resource[:key] + ph = @property_hash[:key] + return ph if res.nil? + return :default if res == :default && + ph == @radius_server.default_key + unless res.start_with?('"') && res.end_with?('"') + ph = ph.gsub(/\A"|"\Z/, '') + end + ph + end + def munge_flush(val) if val.is_a?(String) && val.eql?('unset') nil diff --git a/lib/puppet/provider/tacacs_global/cisco.rb b/lib/puppet/provider/tacacs_global/cisco.rb index 0d2e7471c..96d7d92e4 100644 --- a/lib/puppet/provider/tacacs_global/cisco.rb +++ b/lib/puppet/provider/tacacs_global/cisco.rb @@ -74,6 +74,18 @@ def self.prefetch(resources) end end # self.prefetch + def key + res = @resource[:key] + ph = @property_hash[:key] + return ph if res.nil? + return :default if res == :default && + ph == @tacacs_global.default_key + unless res.start_with?('"') && res.end_with?('"') + ph = ph.gsub(/\A"|"\Z/, '') + end + ph + end + def munge_flush(val) if val.is_a?(String) && val.eql?('unset') nil diff --git a/lib/puppet/provider/tacacs_server/cisco.rb b/lib/puppet/provider/tacacs_server/cisco.rb index d1edca277..2d44b6afe 100644 --- a/lib/puppet/provider/tacacs_server/cisco.rb +++ b/lib/puppet/provider/tacacs_server/cisco.rb @@ -117,6 +117,18 @@ def destroy @property_flush[:ensure] = :absent end + def key + res = @resource[:key] + ph = @property_hash[:key] + return ph if res.nil? + return :default if res == :default && + ph == @tacacs_server.encryption_password + unless res.start_with?('"') && res.end_with?('"') + ph = ph.gsub(/\A"|"\Z/, '') + end + ph + end + def munge_flush(val) if val.is_a?(String) && val.eql?('unset') nil diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 8b3480155..d01443b0a 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1450,6 +1450,7 @@ def test_patch_version(tests, id, name, ver) # Returns: String with double quotes: (Example: '"foo"' # def add_quotes(string) - string = "\"#{string}\"" if image?[/8.0/] + return string if image?[/7.3.0/] + string = "\"#{string}\"" string end From bd4e3c7a55db701ae12f6969704662fdfcb94fa9 Mon Sep 17 00:00:00 2001 From: saichint Date: Sat, 4 Mar 2017 10:26:52 -0800 Subject: [PATCH 143/203] fix backspace issue for dhcp (#428) --- lib/puppet/type/cisco_dhcp_relay_global.rb | 6 +++++- .../cisco_dhcp_relay_global/test_dhcp_relay_global.rb | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/puppet/type/cisco_dhcp_relay_global.rb b/lib/puppet/type/cisco_dhcp_relay_global.rb index 6ec90b0b8..8accbc9b0 100644 --- a/lib/puppet/type/cisco_dhcp_relay_global.rb +++ b/lib/puppet/type/cisco_dhcp_relay_global.rb @@ -124,7 +124,11 @@ munge do |value| value = value.strip - value = :default if value == 'default' + if value == 'default' + value = :default + else + value = "\"#{value}\"" unless value.start_with?('"') && value.end_with?('"') + end value end end # property ipv4_sub_option_circuit_id_string diff --git a/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb b/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb index aac0514bf..0d6953e1d 100644 --- a/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb +++ b/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb @@ -99,7 +99,7 @@ ipv4_src_addr_hsrp: 'true', ipv4_src_intf: 'port-channel200', ipv4_sub_option_circuit_id_custom: 'true', - ipv4_sub_option_circuit_id_string: 'WORD', + ipv4_sub_option_circuit_id_string: add_quotes('WORD'), ipv4_sub_option_cisco: 'true', ipv6_option_cisco: 'true', ipv6_option_vpn: 'true', From f36eac96f1ef145e5ffe66cb40fe07fce519bbab Mon Sep 17 00:00:00 2001 From: rahushen Date: Sat, 4 Mar 2017 16:52:34 -0500 Subject: [PATCH 144/203] Feature/upgrade services (#427) * Add new type for cisco_service * Add new provider cisco_Service * Fix rubopcop errors * fix rubocop errors * Fix date on CopyRight * Rename cisco_service to cisco_upgrade * Redesigned upgrade resource type based on @mikewiebe PR comments * Redesigned upgrade resource provider based on @mikewiebe PR comments * removing cisco_service resource type * rename media to uri and misc. fixes * converting del_boot_image and force_upgrade to bools * Remove whitespace * Source_uri is required * Handle node_utils API change from service to upgrade --- lib/puppet/provider/cisco_upgrade/cisco.rb | 80 ++++++++++++++ lib/puppet/type/cisco_upgrade.rb | 121 +++++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 lib/puppet/provider/cisco_upgrade/cisco.rb create mode 100644 lib/puppet/type/cisco_upgrade.rb diff --git a/lib/puppet/provider/cisco_upgrade/cisco.rb b/lib/puppet/provider/cisco_upgrade/cisco.rb new file mode 100644 index 000000000..e28172bb6 --- /dev/null +++ b/lib/puppet/provider/cisco_upgrade/cisco.rb @@ -0,0 +1,80 @@ +# +# Puppet provider to manage upgrade of Cisco devices +# +# Copyright (c) 2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'cisco_node_utils' if Puppet.features.cisco_node_utils? +begin + require 'puppet_x/cisco/autogen' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'autogen.rb')) +end + +begin + require 'puppet_x/cisco/cmnutils' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'cmnutils.rb')) +end +Puppet::Type.type(:cisco_upgrade).provide(:cisco) do + desc 'The Cisco Upgrade provider to upgrade Cisco devices.' + + confine feature: :cisco_node_utils + defaultfor operatingsystem: :nexus + + mk_resource_methods + + UPGRADE_NON_BOOL_PROPS = [ + :version + ] + + UPGRADE_ALL_PROPS = UPGRADE_NON_BOOL_PROPS + + PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@nu', + UPGRADE_NON_BOOL_PROPS) + + def initialize(value={}) + super(value) + @nu = Cisco::Upgrade + @property_flush = {} + end + + def self.instances + inst = [] + upgrade = Cisco::Upgrade + + inst << new( + name: 'image', + version: upgrade.image_version) + end + + def self.prefetch(resources) + resources.values.first.provider = instances.first + end + + def version=(new_version) + return if new_version.nil? + # Convert del_boot_image and force_upgrade from symbols + # to Boolean Class + del_boot_image = (@resource[:delete_boot_image] == :true) + force_upgrade = (@resource[:force_upgrade] == :true) + @nu.upgrade(new_version, @resource[:source_uri][:image_name], @resource[:source_uri][:uri], + del_boot_image, force_upgrade) + @property_hash[:version] = new_version + end +end diff --git a/lib/puppet/type/cisco_upgrade.rb b/lib/puppet/type/cisco_upgrade.rb new file mode 100644 index 000000000..4f498bf04 --- /dev/null +++ b/lib/puppet/type/cisco_upgrade.rb @@ -0,0 +1,121 @@ +# +# Manages the version of Cisco Image running on a device. +# +# Copyright (c) 2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Puppet::Type.newtype(:cisco_upgrade) do + @doc = "Manages the version of Cisco Image running on a device. + + ``` + cisco_upgrade {\"\": + ..attributes.. + } + ``` + + There can only be one instance of cisco_upgrade i.e. 'image' + + Example: + ``` + cisco_upgrade {'image' : + version => '7.0(3)I5(1)', + source_uri => 'bootflash:///nxos.7.0.3.I5.1.bin', + force_upgrade => false, + delete_boot_image => false, + } + ``` + " + + # Parse out the title to fill in the attributes in these + # patterns. These attributes can be overwritten later. + def self.title_patterns + identity = ->(x) { x } + patterns = [] + + patterns << [ + /^(\S+)$/, + [ + [:name, identity] + ], + ] + patterns + end + + newparam(:name, namevar: :true) do + # Parameter used only to satisfy namevar + desc 'Name of cisco_upgrade instance. Valid values are string' + validate do |name| + warning "only 'image' is accepted as a valid name" if name != 'image' + end + end + + newparam(:source_uri) do + examples = "\nExample:\nbootflash:nxos.7.0.3.I5.2.bin" + supported = "\nNOTE: Only bootflash: is supported." + desc "URI to the image to install on the device. Format :. + Valid values are string.#{examples}#{supported}" + + validate do |uri| + fail 'source_uri must match format :' unless uri[/\S+:\S+/] + end + munge do |uri| + image = {} + # Convert : to a hash. + # The Node-utils API expects uri and image_name as two + # separate arguments. Pre-processing the arguments here. + if uri.include?('/') + image[:uri] = uri.split('/')[0] + image[:image_name] = uri.split('/')[-1] + else + image[:uri] = uri.split(':')[0] + ':' + image[:image_name] = uri.split(':')[-1] + end + image + end + end # param source_uri + + newparam(:force_upgrade) do + desc 'Force upgrade the device.' + defaultto :false + newvalues(:true, :false) + end # param force_upgrade + + newparam(:delete_boot_image) do + desc 'Delete the booted image(s).' + defaultto :false + newvalues(:true, :false) + end # param delete_boot_image + + ############## + # Attributes # + ############## + + newproperty(:version) do + desc 'Version of the Cisco image to install on the device. + Valid values are strings' + validate do |ver| + fail "Version can't be nil or an empty string" if + ver == '' || ver.nil? == :true + valid_chars = 'Version can only have the following + characters: 0-9, a-z, A-Z, (, ) and .' + fail "Invalid version string. #{valid_chars}" unless + (/([0-9a-zA-Z().]*)/.match(ver))[0] == ver + end + end # property version + + validate do + fail("The 'source_uri' parameter must be set in the manifest") if + self[:source_uri].nil? || self[:source_uri] == '' + end +end From f6671b46aa97b0ca0d85c5badbeb16912e014e2e Mon Sep 17 00:00:00 2001 From: rahushen Date: Mon, 6 Mar 2017 12:36:25 -0500 Subject: [PATCH 145/203] Feature/upgrade services (#429) * Add new type for cisco_service * Add new provider cisco_Service * Fix rubopcop errors * fix rubocop errors * Fix date on CopyRight * Rename cisco_service to cisco_upgrade * Redesigned upgrade resource type based on @mikewiebe PR comments * Redesigned upgrade resource provider based on @mikewiebe PR comments * removing cisco_service resource type * rename media to uri and misc. fixes * converting del_boot_image and force_upgrade to bools * Remove whitespace * Source_uri is required * Handle node_utils API change from service to upgrade * Move source_uri validation to provider --- lib/puppet/provider/cisco_upgrade/cisco.rb | 2 ++ lib/puppet/type/cisco_upgrade.rb | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/puppet/provider/cisco_upgrade/cisco.rb b/lib/puppet/provider/cisco_upgrade/cisco.rb index e28172bb6..c6c187e4d 100644 --- a/lib/puppet/provider/cisco_upgrade/cisco.rb +++ b/lib/puppet/provider/cisco_upgrade/cisco.rb @@ -71,6 +71,8 @@ def version=(new_version) return if new_version.nil? # Convert del_boot_image and force_upgrade from symbols # to Boolean Class + fail 'The source_uri parameter must be set in the manifest' if + @resource[:source_uri].nil? del_boot_image = (@resource[:delete_boot_image] == :true) force_upgrade = (@resource[:force_upgrade] == :true) @nu.upgrade(new_version, @resource[:source_uri][:image_name], @resource[:source_uri][:uri], diff --git a/lib/puppet/type/cisco_upgrade.rb b/lib/puppet/type/cisco_upgrade.rb index 4f498bf04..3bf9cc55a 100644 --- a/lib/puppet/type/cisco_upgrade.rb +++ b/lib/puppet/type/cisco_upgrade.rb @@ -113,9 +113,4 @@ def self.title_patterns (/([0-9a-zA-Z().]*)/.match(ver))[0] == ver end end # property version - - validate do - fail("The 'source_uri' parameter must be set in the manifest") if - self[:source_uri].nil? || self[:source_uri] == '' - end end From b8bafaec702f36e348a29d4298d1ef8ad5292036 Mon Sep 17 00:00:00 2001 From: saichint Date: Tue, 7 Mar 2017 17:09:26 -0800 Subject: [PATCH 146/203] remove all route maps after beaker test (#431) * remove all route maps after beaker test * review comment --- tests/beaker_tests/cisco_route_map/test_route_map.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/beaker_tests/cisco_route_map/test_route_map.rb b/tests/beaker_tests/cisco_route_map/test_route_map.rb index 67bebd483..7ff8abdbb 100644 --- a/tests/beaker_tests/cisco_route_map/test_route_map.rb +++ b/tests/beaker_tests/cisco_route_map/test_route_map.rb @@ -540,11 +540,7 @@ def unsupported_properties(_tests, _id) end def cleanup(agent) - test_set(agent, 'no route-map rm1') - test_set(agent, 'no route-map rm2') - test_set(agent, 'no route-map rm3') - test_set(agent, 'no route-map rm4') - test_set(agent, 'no route-map rm5') + resource_absent_cleanup(agent, 'cisco_route_map') end ################################################################# From a61ec7e87224d50d85b2035c1700a3759232b84f Mon Sep 17 00:00:00 2001 From: rahushen Date: Wed, 8 Mar 2017 11:09:08 -0500 Subject: [PATCH 147/203] Feature/upgrade services (#430) * Add new type for cisco_service * Add new provider cisco_Service * Fix rubopcop errors * fix rubocop errors * Fix date on CopyRight * Rename cisco_service to cisco_upgrade * Redesigned upgrade resource type based on @mikewiebe PR comments * Redesigned upgrade resource provider based on @mikewiebe PR comments * removing cisco_service resource type * rename media to uri and misc. fixes * converting del_boot_image and force_upgrade to bools * Remove whitespace * Source_uri is required * Handle node_utils API change from service to upgrade * Move source_uri validation to provider * Added image_version proc and ability to process '()' * Fix rubocop errors * Add new test file for cisco_upgrade resource beaker_tests/cisco_upgrade/test_upgrade_idempotence.rb * Running cisco_upgrade tests on n3k,n9k only * Update README with references to the new cisco_upgrade resource * Update README.md * Add demo manifest for cisco_upgrade * Add new manifest for cisco_upgrade * Added 'cisco_upgrade' to changelog * Update Support Table for cisco_upgrade Add Release for Fretta and update N9K/N3k release to camden 2e * Minor changes to README.md * Update README.md * Minor changes to documentation --- CHANGELOG.md | 1 + README.md | 47 +++++++++++ examples/cisco/demo_upgrade.pp | 78 +++++++++++++++++++ examples/demo_all_cisco.pp | 1 + .../cisco_upgrade/test_upgrade_idempotence.rb | 60 ++++++++++++++ tests/beaker_tests/lib/utilitylib.rb | 9 +++ 6 files changed, 196 insertions(+) create mode 100644 examples/cisco/demo_upgrade.pp create mode 100644 tests/beaker_tests/cisco_upgrade/test_upgrade_idempotence.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cf011cd3..404562bc6 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### New feature support #### Cisco Resources - `cisco_route_map` type and provider. +- `cisco_upgrade` type and provider. ### Added - Extend cisco_interface with attributes: diff --git a/README.md b/README.md index 586d08a58..fbcd83341 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,7 @@ The following resources include cisco types and providers along with cisco provi * Miscellaneous Types * [`cisco_command_config`](#type-cisco_command_config) * [`cisco_vdc`](#type-cisco_vdc) + * [`cisco_upgrade`](#type-cisco_upgrade) * AAA Types * [`cisco_aaa_authentication_login`](#type-cisco_aaa_authentication_login) @@ -334,6 +335,7 @@ The following resources include cisco types and providers along with cisco provi * [`cisco_snmp_user`](#type-cisco_snmp_user) * [`cisco_tacacs_server`](#type-cisco_tacacs_server) * [`cisco_tacacs_server_host`](#type-cisco_tacacs_server_host) +* [`cisco_upgrade`](#type-cisco_upgrade) * [`cisco_vdc`](#type-cisco_vdc) * [`cisco_vlan`](#type-cisco_vlan) * [`cisco_vpc_domain`](#type-cisco_vpc_domain) @@ -445,6 +447,7 @@ Symbol | Meaning | Description | [cisco_snmp_user](#type-cisco_snmp_user) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_tacacs_server](#type-cisco_tacacs_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_tacacs_server_host](#type-cisco_tacacs_server_host) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_upgrade](type-cisco_upgrade) | ✅* | ✅* | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign:| \*[caveats](#cisco_upgrade-caveats) | | [cisco_vdc](#type-cisco_vdc) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | | [cisco_vlan](#type-cisco_vlan) | ✅* | ✅* | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_vlan-caveats) | | [cisco_vpc_domain](#type-cisco_vpc_domain) | ✅* | ✅* | ✅* | ✅* | ✅* | :heavy_minus_sign: | \*[caveats](#cisco_vpc_domain-caveats) | @@ -4010,6 +4013,50 @@ Specifies a preshared key for the host. Valid values are 'clear', 'encrypted', ##### `encryption_password` "Specifies the preshared key password for the host. Valid value is a string. +-- +### Type: cisco_upgrade + +Manages the upgrade of a Cisco device. + +| Platform | OS Minimum Version | Module Minimum Version | +|----------|:------------------:|:----------------------:| +| N9k | 7.0(3)I2(2e) | 1.6.0 | +| N3k | 7.0(3)I2(2e) | 1.6.0 | +| N5k | not applicable | not applicable | +| N6k | not applicable | not applicable | +| N7k | not applicable | not applicable | +| N9k-F | 7.0(3)F1(1) | 1.6.0 | + +#### Caveats + +The `cisco_upgrade` is only supported on *simplex* N3k, N9k and N9k-F devices. HA devices are currently not supported. + +| Property | Caveat Description | +|:--------|:-------------| +| `source_uri` | Only images on `bootflash:` are supported. The puppet file provider can be used to copy the image file to `bootflash`. Refer to Demo Upgrade for an example. | + +#### Parameters + +##### `name` +Name of cisco_upgrade instance. Valid values are string. +*Only 'image' is a valid name for the cisco_upgrade resource.* + +##### `source_uri` +Image upgrade URI. Format `:`. Valid values are string. +*Example --> bootflash:nxos.7.0.3.I5.2.bin* +*NOTE: Only images on `bootflash:` are supported.* + +##### `delete_boot_image` +Delete the booted image. Valid values are `true`, `false`. + +##### `force_upgrade` +Force upgrade the device.Valid values are `true`, `false`. + +#### Properties + +##### `version` +Version of the Cisco image to install on the device. Valid values are strings. + -- ### Type: cisco_vdc diff --git a/examples/cisco/demo_upgrade.pp b/examples/cisco/demo_upgrade.pp new file mode 100644 index 000000000..7a1876044 --- /dev/null +++ b/examples/cisco/demo_upgrade.pp @@ -0,0 +1,78 @@ +# Manifest to demo cisco_upgrade +# +# Copyright (c) 2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +class ciscopuppet::cisco::demo_upgrade { + + # To use this manifest, make sure the gem and bin file are in the files directory under your + # puppet module on the puppet master. + + # agent-lab9-pm:files:2009> cd /etc/puppetlabs/code/environments/production/modules/ciscopuppet/files/ + # agent-lab9-pm:files:2010> ls -lh + # total 1.3G + # -rw-r--r-- 1 root root 431K Mar 2 14:19 cisco_node_utils-1.5.0.gem + # -rwxr-xr-- 1 root root 530M Mar 2 15:46 nxos.7.0.3.I2.5.bin + # -rwxr-xr-- 1 root root 723M Mar 2 15:25 nxos.7.0.3.I5.1.bin + # agent-lab9-pm:files:2011> + + node default { + $gem = 'cisco_node_utils-1.5.0.gem' + $uri = 'bootflash' + $image = 'nxos.7.0.3.I2.5.bin' + $version = '7.0(3)I2(5)' + + # If you are behind proxy, please set the proxy variable. + # $proxy = 'http://.:' + $proxy = '' + + if $proxy == '' { + $opts = {} + } + else { + $opts = { '--http-proxy' => $proxy } + } + + # If installing cisco_node_utils from local source + # file { "/${uri}/${gem}" : + # ensure => file, + # source => "puppet:///modules/ciscopuppet/${gem}", + # owner => 'root', + # group => 'root', + # mode => 'ug+rwx', + # } + + package { 'cisco_node_utils' : + ensure => present, + provider => 'gem', + # source => "/${uri}/${gem}", + install_options => $opts, + } + + file { "/${uri}/${image}" : + ensure => file, + source => "puppet:///modules/ciscopuppet/${image}", + owner => 'root', + group => 'root', + mode => 'ug+rwx', + } + + cisco_upgrade { 'image' : + version => "${version}", + source_uri => "${uri}:///${image}", + force_upgrade => false, + delete_boot_image => false, + } + } +} diff --git a/examples/demo_all_cisco.pp b/examples/demo_all_cisco.pp index b4b57b74c..7948b6304 100644 --- a/examples/demo_all_cisco.pp +++ b/examples/demo_all_cisco.pp @@ -49,6 +49,7 @@ include ciscopuppet::cisco::demo_stp_vlan include ciscopuppet::cisco::demo_tacacs_server include ciscopuppet::cisco::demo_tacacs_server_host + # include ciscopuppet::cisco::demo_upgrade include ciscopuppet::cisco::demo_vlan include ciscopuppet::cisco::demo_vpc_domain include ciscopuppet::cisco::demo_vrf diff --git a/tests/beaker_tests/cisco_upgrade/test_upgrade_idempotence.rb b/tests/beaker_tests/cisco_upgrade/test_upgrade_idempotence.rb new file mode 100644 index 000000000..f6bb498a7 --- /dev/null +++ b/tests/beaker_tests/cisco_upgrade/test_upgrade_idempotence.rb @@ -0,0 +1,60 @@ +############################################################################### +# Copyright (c) 2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### +require File.expand_path('../../lib/utilitylib.rb', __FILE__) +@version = image_version.to_s.strip +# Test hash top-level keys +tests = { + master: master, + agent: agent, + operating_system: 'nexus', + ensurable: false, + resource_name: 'cisco_upgrade', +} + +# Non-default Tests. NOTE: [:resource] = [:manifest_props] for all non-default +tests[:non_default] = { + desc: '1.1 Non_Defaults', + code: [0], + title_pattern: 'image', + platform: 'n(3|9)k', + manifest_props: { + version: @version, + source_uri: on(agent, facter_cmd('-p cisco.images.system_image')).output, + force_upgrade: false, + delete_boot_image: false, + }, + resource: { + version: @version + }, +} + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection Non Default Property Testing") + test_harness_run(tests, :non_default) +end +logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index d01443b0a..c23a19784 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -75,6 +75,7 @@ def hash_to_patterns(hash) if /^\[.*\]$/.match(value) value.gsub!(/[\[\]]/) { |s| '\\' + "#{s}" }.gsub!(/\"/) { |_s| '\'' } end + value.gsub!(/[\(\)]/) { |s| '\\' + "#{s}" } if /\(.*\)/.match(value) regexparr << Regexp.new("#{key}\s+=>\s+'?#{value}'?") end regexparr @@ -1110,6 +1111,14 @@ def nexus_image @image ||= image_regexp.match(data)[1] end +# Gets the version of the image running on a device +@version = nil +def image_version + facter_opt = '-p os.release.full' + data = on(agent, facter_cmd(facter_opt)).output + @version ||= data +end + # On match will skip all testcases # Do not use this for skipping individual properties. def skip_nexus_image(image, tests) From de1190da90f8610df44e3f9e9af673e728eb38fb Mon Sep 17 00:00:00 2001 From: rahushen Date: Thu, 9 Mar 2017 14:48:13 -0500 Subject: [PATCH 148/203] Feature/upgrade services (#433) * Add new type for cisco_service * Add new provider cisco_Service * Fix rubopcop errors * fix rubocop errors * Fix date on CopyRight * Rename cisco_service to cisco_upgrade * Redesigned upgrade resource type based on @mikewiebe PR comments * Redesigned upgrade resource provider based on @mikewiebe PR comments * removing cisco_service resource type * rename media to uri and misc. fixes * converting del_boot_image and force_upgrade to bools * Remove whitespace * Source_uri is required * Handle node_utils API change from service to upgrade * Move source_uri validation to provider * Added image_version proc and ability to process '()' * Fix rubocop errors * Add new test file for cisco_upgrade resource beaker_tests/cisco_upgrade/test_upgrade_idempotence.rb * Running cisco_upgrade tests on n3k,n9k only * Update README with references to the new cisco_upgrade resource * Update README.md * Add demo manifest for cisco_upgrade * Add new manifest for cisco_upgrade * Added 'cisco_upgrade' to changelog * Update Support Table for cisco_upgrade Add Release for Fretta and update N9K/N3k release to camden 2e * Minor changes to README.md * Update README.md * Minor changes to documentation * Replace output with stdout.chomp for facter call --- tests/beaker_tests/lib/utilitylib.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index c23a19784..74fc7e3e2 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1115,7 +1115,7 @@ def nexus_image @version = nil def image_version facter_opt = '-p os.release.full' - data = on(agent, facter_cmd(facter_opt)).output + data = on(agent, facter_cmd(facter_opt)).stdout.chomp @version ||= data end From 5c2a73d0bf6a679c1fd912655994601b580d5cdc Mon Sep 17 00:00:00 2001 From: Rick Sherman Date: Thu, 23 Mar 2017 13:48:02 -0500 Subject: [PATCH 149/203] =?UTF-8?q?Replace=20heavy=5Fminus=5Fsign=20emoji?= =?UTF-8?q?=20with=20utf-8=20=E2=9E=96symbol=20in=20README=20(#438)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously the README was a mix of utf-8 symbols and GitHub emoji. This was not properly rendering on Puppet Forge, as GitHub emoji codes are not supported. --- README.md | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index fbcd83341..88ac39ae6 100644 --- a/README.md +++ b/README.md @@ -396,11 +396,11 @@ Platform | Description | Environments Symbol | Meaning | Description :--|:--|:-- ✅ | Supported | The provider has been validated to work on the platform.
An asterisk '*' indicates that some provider properties may have software or hardware limitations, caveats, or other noted behaviors.
Click on the associated caveat link for more information. -:heavy_minus_sign: | Not Applicable | The provider is not supported on the platform because of hardware or software limitations. +➖ | Not Applicable | The provider is not supported on the platform because of hardware or software limitations. **Support Matrix** -| ✅ = Supported
:heavy_minus_sign: = Not Applicable | N9k | N3k | N5k | N6k | N7k | N9k-F | Caveats | +| ✅ = Supported
➖ = Not Applicable | N9k | N3k | N5k | N6k | N7k | N9k-F | Caveats | |:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| | [cisco_aaa_
authentication_login](#type-cisco_aaa_authentication_login) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_aaa_
authorization_login_cfg_svc](#type-cisco_aaa_authorization_login_cfg_svc) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | @@ -414,27 +414,27 @@ Symbol | Meaning | Description | [cisco_bgp_af](#type-cisco_bgp_af) | ✅* | ✅* | ✅ | ✅* | ✅ | ✅ | \*[caveats](#cisco_bgp_af-caveats) | | [cisco_bgp_neighbor](#type-cisco_bgp_neighbor) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_bgp_neighbor_af](#type-cisco_bgp_neighbor_af) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_bridge_domain](#type-cisco_bridge_domain) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | -| [cisco_bridge_domain_vni](#type-cisco_bridge_domain_vni) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | +| [cisco_bridge_domain](#type-cisco_bridge_domain) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | +| [cisco_bridge_domain_vni](#type-cisco_bridge_domain_vni) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | | [cisco_dhcp_relay_global](#type-cisco_dhcp_relay_global) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_dhcp_relay_global-caveats) -| [cisco_encapsulation](#type-cisco_encapsulation) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | -| [cisco_evpn_vni](#type-cisco_evpn_vni) | ✅ | :heavy_minus_sign: | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_evpn_vni-caveats) | -| [cisco_fabricpath_global](#type-cisco_fabricpath_global) | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | ✅ | ✅* | :heavy_minus_sign: | \*[caveats](#cisco_fabricpath_global-caveats) | -| [cisco_fabricpath_topology](#type-cisco_fabricpath_topology) | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | ✅ | ✅ | :heavy_minus_sign: | +| [cisco_encapsulation](#type-cisco_encapsulation) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | +| [cisco_evpn_vni](#type-cisco_evpn_vni) | ✅ | ➖ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_evpn_vni-caveats) | +| [cisco_fabricpath_global](#type-cisco_fabricpath_global) | ➖ | ➖ | ✅ | ✅ | ✅* | ➖ | \*[caveats](#cisco_fabricpath_global-caveats) | +| [cisco_fabricpath_topology](#type-cisco_fabricpath_topology) | ➖ | ➖ | ✅ | ✅ | ✅ | ➖ | | [cisco_hsrp_global](#type-cisco_hsrp_global) | ✅ | ✅* | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_hsrp_global-caveats) | | [cisco_interface](#type-cisco_interface) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_interface-caveats) | | [cisco_interface_channel_group](#type-cisco_interface_channel_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_interface_hsrp_group](#type-cisco_interface_hsrp_group) | ✅ | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | ✅* | ✅ | \*[caveats](#cisco_interface_hsrp_group-caveats) | +| [cisco_interface_hsrp_group](#type-cisco_interface_hsrp_group) | ✅ | ✅ | ➖ | ➖ | ✅* | ✅ | \*[caveats](#cisco_interface_hsrp_group-caveats) | | [cisco_interface_ospf](#type-cisco_interface_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_interface_portchannel](#type-cisco_interface_portchannel) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_interface_portchannel-caveats) | -| [cisco_interface_service_vni](#type-cisco_interface_service_vni) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | -| [cisco_itd_device_group](#type-cisco_itd_device_group) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | -| [cisco_itd_device_group_node](#type-cisco_itd_device_group_node) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | -| [cisco_itd_service](#type-cisco_itd_service) | ✅ | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | \*[caveats](#cisco_itd_service-caveats) | +| [cisco_interface_service_vni](#type-cisco_interface_service_vni) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | +| [cisco_itd_device_group](#type-cisco_itd_device_group) | ✅ | ➖ | ➖ | ➖ | ✅ | ➖ | +| [cisco_itd_device_group_node](#type-cisco_itd_device_group_node) | ✅ | ➖ | ➖ | ➖ | ✅ | ➖ | +| [cisco_itd_service](#type-cisco_itd_service) | ✅ | ➖ | ➖ | ➖ | ✅ | ➖ | \*[caveats](#cisco_itd_service-caveats) | | [cisco_ospf](#type-cisco_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_ospf_vrf](#type-cisco_ospf_vrf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| ✅ = Supported
:heavy_minus_sign: = Not Applicable | N9k | N3k | N5k | N6k | N7k | N9k-F | Caveats | -| [cisco_overlay_global](#type-cisco_overlay_global) | ✅ | :heavy_minus_sign: | ✅ | ✅ | ✅ | ✅ | +| ✅ = Supported
➖ = Not Applicable | N9k | N3k | N5k | N6k | N7k | N9k-F | Caveats | +| [cisco_overlay_global](#type-cisco_overlay_global) | ✅ | ➖ | ✅ | ✅ | ✅ | ✅ | | [cisco_pim](#type-cisco_pim) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_pim-caveats) | | [cisco_pim_rp_address](#type-cisco_pim_rp_address) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_pim_grouplist](#type-cisco_pim_grouplist) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | @@ -447,19 +447,19 @@ Symbol | Meaning | Description | [cisco_snmp_user](#type-cisco_snmp_user) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_tacacs_server](#type-cisco_tacacs_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_tacacs_server_host](#type-cisco_tacacs_server_host) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_upgrade](type-cisco_upgrade) | ✅* | ✅* | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign:| \*[caveats](#cisco_upgrade-caveats) | -| [cisco_vdc](#type-cisco_vdc) | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | ✅ | :heavy_minus_sign: | +| [cisco_upgrade](type-cisco_upgrade) | ✅* | ✅* | ➖ | ➖ | ➖ | ➖| \*[caveats](#cisco_upgrade-caveats) | +| [cisco_vdc](#type-cisco_vdc) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | | [cisco_vlan](#type-cisco_vlan) | ✅* | ✅* | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_vlan-caveats) | -| [cisco_vpc_domain](#type-cisco_vpc_domain) | ✅* | ✅* | ✅* | ✅* | ✅* | :heavy_minus_sign: | \*[caveats](#cisco_vpc_domain-caveats) | +| [cisco_vpc_domain](#type-cisco_vpc_domain) | ✅* | ✅* | ✅* | ✅* | ✅* | ➖ | \*[caveats](#cisco_vpc_domain-caveats) | | [cisco_vrf](#type-cisco_vrf) | ✅ | ✅* | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_vrf-caveats) | | [cisco_vrf_af](#type-cisco_vrf_af) | ✅ | ✅* | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_vrf_af-caveats) | | [cisco_vtp](#type-cisco_vtp) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_vxlan_vtep](#type-cisco_vxlan_vtep) | ✅ | :heavy_minus_sign: | ✅ | ✅ | ✅ | ✅ | -| [cisco_vxlan_vtep_vni](#type-cisco_vxlan_vtep_vni) | ✅ | :heavy_minus_sign: | ✅ | ✅ | ✅ | ✅ | +| [cisco_vxlan_vtep](#type-cisco_vxlan_vtep) | ✅ | ➖ | ✅ | ✅ | ✅ | ✅ | +| [cisco_vxlan_vtep_vni](#type-cisco_vxlan_vtep_vni) | ✅ | ➖ | ✅ | ✅ | ✅ | ✅ | ##### NetDev Providers -| ✅ = Supported
:heavy_minus_sign: = Not Applicable | N9k | N3k | N5k | N6k | N7k | N9k-F | Caveats | +| ✅ = Supported
➖ = Not Applicable | N9k | N3k | N5k | N6k | N7k | N9k-F | Caveats | |:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| | [domain_name](#type-domain_name) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [name_server](#type-name_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | From c796ebcfffa0c2aa87f874a89060c52fb56da954 Mon Sep 17 00:00:00 2001 From: saichint Date: Fri, 24 Mar 2017 06:02:41 -0700 Subject: [PATCH 150/203] use resource name for all cisco provider cases (#439) --- lib/puppet/provider/package/cisco.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/puppet/provider/package/cisco.rb b/lib/puppet/provider/package/cisco.rb index 980f461c2..6e2ddcd72 100644 --- a/lib/puppet/provider/package/cisco.rb +++ b/lib/puppet/provider/package/cisco.rb @@ -232,7 +232,9 @@ def uninstall if in_guestshell? && target_host? debug 'Guestshell + target=>host detected, using nxapi for uninstall' if @resource[:platform] - Cisco::Yum.remove("#{@resource[:name]}.#{@resource[:platform]}") + # TODO: adding platform to the name is NOT working, for now just use name + # Cisco::Yum.remove("#{@resource[:name]}.#{@resource[:platform]}") + Cisco::Yum.remove(@resource[:name]) else Cisco::Yum.remove(@resource[:name]) end From 02d299ee22979ac97d062888a90947d9f89b468d Mon Sep 17 00:00:00 2001 From: saichint Date: Mon, 27 Mar 2017 12:27:33 -0700 Subject: [PATCH 151/203] fix beaker patch test for evergreen (#440) --- .../beaker_tests/file_service_package/test_package_patch.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/beaker_tests/file_service_package/test_package_patch.rb b/tests/beaker_tests/file_service_package/test_package_patch.rb index 37ee70904..1b0eeac1e 100755 --- a/tests/beaker_tests/file_service_package/test_package_patch.rb +++ b/tests/beaker_tests/file_service_package/test_package_patch.rb @@ -43,10 +43,14 @@ when /7.0.3.I4.2/ filename = 'nxos.sample-n9k_EOR-1.0.0-7.0.3.I4.2.lib32_n9000.rpm' version = '1.0.0-7.0.3.I4.2' -when /7.0.3.I5/ +when /7.0.3.I5.1/ name = 'nxos.sample-n9k_ALL' filename = 'nxos.sample-n9k_ALL-1.0.0-7.0.3.I5.1.lib32_n9000.rpm' version = '1.0.0-7.0.3.I5.1' +when /7.0.3.I5.2/ + name = 'nxos.sample-n9k_ALL' + filename = 'nxos.sample-n9k_ALL-1.0.0-7.0.3.I5.2.lib32_n9000.rpm' + version = '1.0.0-7.0.3.I5.2' when /7.0.3.F1/ name = 'nxos.sample-n8k_EOR' filename = 'nxos.sample-n8k_EOR-1.0.0-7.0.3.F1.1.lib32_nxos.rpm' From afd7e862327e6847e952109ee2ff8a1203041871 Mon Sep 17 00:00:00 2001 From: sai chintalapudi Date: Fri, 31 Mar 2017 16:02:20 -0700 Subject: [PATCH 152/203] fix eth default --- lib/puppet/provider/cisco_interface/cisco.rb | 1 + lib/puppet/type/cisco_interface.rb | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/lib/puppet/provider/cisco_interface/cisco.rb b/lib/puppet/provider/cisco_interface/cisco.rb index 8f2ab8181..1884a1fc9 100644 --- a/lib/puppet/provider/cisco_interface/cisco.rb +++ b/lib/puppet/provider/cisco_interface/cisco.rb @@ -98,6 +98,7 @@ :ipv4_dhcp_smart_relay, :negotiate_auto, :pim_bfd, + :purge_config, :shutdown, :switchport_autostate_exclude, :switchport_pvlan_host, diff --git a/lib/puppet/type/cisco_interface.rb b/lib/puppet/type/cisco_interface.rb index 5b24e7c5c..0d3cac6c3 100755 --- a/lib/puppet/type/cisco_interface.rb +++ b/lib/puppet/type/cisco_interface.rb @@ -1279,4 +1279,14 @@ def is_to_s(value) munge { |value| value == 'default' ? :default : Integer(value) } end # property load_interval_counter_3_delay + + newproperty(:purge_config) do + desc 'Puts the ethernet interface in default state.' + + newvalues(:true) + end + + validate do + fail ArgumentError, 'All params MUST be nil if purge_config is true' if self[:purge_config] == :true && properties.length > 2 + end end # Puppet::Type.newtype From f6518af4b49eaa6d1b903968cc9c02095686fd70 Mon Sep 17 00:00:00 2001 From: saichint Date: Mon, 3 Apr 2017 07:18:59 -0700 Subject: [PATCH 153/203] Patch skips (#442) * fix beaker patch test for evergreen * skip patching tests for I2 & I3 --- tests/beaker_tests/file_service_package/test_package_patch.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/beaker_tests/file_service_package/test_package_patch.rb b/tests/beaker_tests/file_service_package/test_package_patch.rb index 1b0eeac1e..1d3583a8a 100755 --- a/tests/beaker_tests/file_service_package/test_package_patch.rb +++ b/tests/beaker_tests/file_service_package/test_package_patch.rb @@ -72,6 +72,7 @@ # Skip -ALL- tests if a top-level platform/os key exludes this platform skip_unless_supported(tests) +skip_nexus_image('I2|I3', tests) tests[:yum_patch_install] = { desc: "1.1 Apply sample patch #{name} to image #{image?}", From d2cb34a4d964b3c9dd1c545f276b08c75cf65a44 Mon Sep 17 00:00:00 2001 From: sai chintalapudi Date: Fri, 7 Apr 2017 11:16:54 -0700 Subject: [PATCH 154/203] add docs --- CHANGELOG.md | 2 ++ README.md | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ad52906c..f8b29385e 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### New feature support ### Added +- Extend cisco_interface with attributes: + - `purge_config` ### Changed diff --git a/README.md b/README.md index 88ac39ae6..c95c4cd4c 100644 --- a/README.md +++ b/README.md @@ -1996,6 +1996,7 @@ Manages a Cisco Network Interface. Any resource dependency should be run before | `load_interval_counter_1_delay` | Minimum puppet module version 1.6.0 | | `load_interval_counter_2_delay` | Minimum puppet module version 1.6.0 | | `load_interval_counter_3_delay` | Minimum puppet module version 1.6.0 | +| `purge_config` | Minimum puppet module version 1.7.0 | #### Parameters @@ -2020,6 +2021,9 @@ Description of the interface. Valid values are a string or the keyword 'default' ###### `duplex` Duplex of the interface. Valid values are 'full', and 'auto'. +###### `purge_config` +Puts the ethenet interface into default state. Valid value is 'true'. + ###### `speed` Speed of the interface. Valid values are 100, 1000, 10000, 40000, 1000000, and 'auto'. From 7526fb5c0443aee87cef3cde0c58e8a4cf771191 Mon Sep 17 00:00:00 2001 From: sai chintalapudi Date: Fri, 7 Apr 2017 11:21:08 -0700 Subject: [PATCH 155/203] add example --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c95c4cd4c..9843691c6 100644 --- a/README.md +++ b/README.md @@ -2022,7 +2022,14 @@ Description of the interface. Valid values are a string or the keyword 'default' Duplex of the interface. Valid values are 'full', and 'auto'. ###### `purge_config` -Puts the ethenet interface into default state. Valid value is 'true'. +Puts the ethenet interface into default state. Valid value is 'true'. When this property is set to 'true', the manifest can have no other properties. + +#### Example Usage + +```puppet +cisco_interface { 'ethernet1/10': + purge_config => true, + } ###### `speed` Speed of the interface. Valid values are 100, 1000, 10000, 40000, 1000000, and 'auto'. From 5d936c6b4a35e2de8bc6b12b54f4135ccf93d333 Mon Sep 17 00:00:00 2001 From: sai chintalapudi Date: Fri, 7 Apr 2017 13:56:23 -0700 Subject: [PATCH 156/203] beaker test for purge_config --- .../cisco_interface/test_interface_L2.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/beaker_tests/cisco_interface/test_interface_L2.rb b/tests/beaker_tests/cisco_interface/test_interface_L2.rb index 56391d4fe..e15ce64d9 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_L2.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_L2.rb @@ -135,6 +135,14 @@ }, } +tests[:purge] = { + desc: '2.3 Purge Properties', + title_pattern: intf, + manifest_props: { + purge_config: 'true' + }, +} + def unsupported_properties(_tests, _id) unprops = [] unprops << @@ -161,6 +169,10 @@ def unsupported_properties(_tests, _id) logger.info("\n#{'-' * 60}\nSection 1. 'trunk' Property Testing") test_harness_run(tests, :default_trunk) test_harness_run(tests, :non_default_trunk) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2.3 Purge_config Testing") + test_harness_run(tests, :purge) end logger.info("TestCase :: #{tests[:resource_name]} :: End") From 40fc64916d4f10bec2e4e2bfda622556e02899b4 Mon Sep 17 00:00:00 2001 From: sai chintalapudi Date: Tue, 11 Apr 2017 08:50:04 -0700 Subject: [PATCH 157/203] review comments --- README.md | 2 +- lib/puppet/type/cisco_interface.rb | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9843691c6..5fe841955 100644 --- a/README.md +++ b/README.md @@ -2022,7 +2022,7 @@ Description of the interface. Valid values are a string or the keyword 'default' Duplex of the interface. Valid values are 'full', and 'auto'. ###### `purge_config` -Puts the ethenet interface into default state. Valid value is 'true'. When this property is set to 'true', the manifest can have no other properties. +Puts the ethernet interface into default state. Valid value is 'true'. When this property is set to 'true', the manifest can have no other properties. #### Example Usage diff --git a/lib/puppet/type/cisco_interface.rb b/lib/puppet/type/cisco_interface.rb index 0d3cac6c3..d3e74f142 100755 --- a/lib/puppet/type/cisco_interface.rb +++ b/lib/puppet/type/cisco_interface.rb @@ -126,6 +126,9 @@ switchport_pvlan_trunk_allowed_vlan => '88-91,94', switchport_pvlan_trunk_native_vlan => 12, } + cisco_interface { 'ethernet8/2' : + purge_config => true, + } cisco_interface {'Vlan98': pvlan_mapping => '10-11,13', } From b3ca63f079bc776f6341662090b37b7dfa02fd22 Mon Sep 17 00:00:00 2001 From: saichint Date: Mon, 17 Apr 2017 18:24:23 -0700 Subject: [PATCH 158/203] Channel group mode addition (#449) * fix channel_group_mode * ad beaker for channel_group_mode * add example manifest * add example manifest * doc * fix readme * fix skip for patch * added dplus5 * add beaker test for no mode * fix netdev port_channel --- CHANGELOG.md | 3 ++ README.md | 12 ++++++- examples/cisco/demo_interface.pp | 33 ++++++++--------- .../cisco_interface_channel_group/cisco.rb | 11 ++++++ lib/puppet/provider/port_channel/cisco.rb | 2 +- .../type/cisco_interface_channel_group.rb | 20 +++++++++-- .../test_interface_channel_group.rb | 36 ++++++++++++++----- .../test_package_patch.rb | 5 ++- tests/beaker_tests/lib/utilitylib.rb | 2 +- 9 files changed, 93 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8b29385e..5129bfb42 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Extend cisco_interface with attributes: - `purge_config` +- Extend cisco_interface_channel_group with attributes: + - `channel_group_mode` + ### Changed ### Removed diff --git a/README.md b/README.md index 5fe841955..177729d1b 100644 --- a/README.md +++ b/README.md @@ -423,7 +423,7 @@ Symbol | Meaning | Description | [cisco_fabricpath_topology](#type-cisco_fabricpath_topology) | ➖ | ➖ | ✅ | ✅ | ✅ | ➖ | | [cisco_hsrp_global](#type-cisco_hsrp_global) | ✅ | ✅* | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_hsrp_global-caveats) | | [cisco_interface](#type-cisco_interface) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_interface-caveats) | -| [cisco_interface_channel_group](#type-cisco_interface_channel_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_interface_channel_group](#type-cisco_interface_channel_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_interface_channel_group-caveats) | | [cisco_interface_hsrp_group](#type-cisco_interface_hsrp_group) | ✅ | ✅ | ➖ | ➖ | ✅* | ✅ | \*[caveats](#cisco_interface_hsrp_group-caveats) | | [cisco_interface_ospf](#type-cisco_interface_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_interface_portchannel](#type-cisco_interface_portchannel) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_interface_portchannel-caveats) | @@ -2030,6 +2030,7 @@ Puts the ethernet interface into default state. Valid value is 'true'. When this cisco_interface { 'ethernet1/10': purge_config => true, } +``` ###### `speed` Speed of the interface. Valid values are 100, 1000, 10000, 40000, 1000000, and 'auto'. @@ -2336,6 +2337,12 @@ Manages a Cisco Network Interface Channel-group. | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +#### Caveats + +| Property | Caveat Description | +|:---------|:-------------| +| `channel_group_mode` | Minimum puppet module version 1.7.0 | + #### Parameters ##### Basic interface channel-group config attributes @@ -2351,6 +2358,9 @@ channel_group is an aggregation of multiple physical interfaces that creates a l Note: On some platforms a normal side-effect of adding the channel-group property is that an independent port-channel interface will be created; however, removing the channel-group configuration by itself will not also remove the port-channel interface. Therefore, the port-channel interface itself may be explicitly removed by using the `cisco_interface` provider with `ensure => absent`. +###### `channel_group_mode` +channel_group_mode is the port-channel mode of the interface. Valid values are 'active', 'passive', 'on', and 'default'. + ###### `description` Description of the interface. Valid values are a string or the keyword 'default'. diff --git a/examples/cisco/demo_interface.pp b/examples/cisco/demo_interface.pp index 495ebf8b2..0c86bda90 100755 --- a/examples/cisco/demo_interface.pp +++ b/examples/cisco/demo_interface.pp @@ -74,7 +74,8 @@ } cisco_interface_channel_group { 'Ethernet1/2': - channel_group => 200, + channel_group => 200, + channel_group_mode => 'active', } cisco_interface { 'Ethernet1/3': @@ -130,19 +131,19 @@ } if platform_get() =~ /n7k/ { - cisco_bridge_domain { "100": - ensure => 'present', - shutdown => false, - bd_name => 'test1' + cisco_bridge_domain { '100': + ensure => 'present', + shutdown => false, + bd_name => 'test1' } cisco_interface { 'Bdi100': - require => Cisco_bridge_domain['100'], - ensure => 'present', - shutdown => false, - ipv4_address => "10.10.10.1", - ipv4_netmask_length => 24, - vrf => 'test1' + require => Cisco_bridge_domain['100'], + ensure => 'present', + shutdown => false, + ipv4_address => '10.10.10.1', + ipv4_netmask_length => 24, + vrf => 'test1' } } else { warning('This platform does not support cisco_bridge_domain') @@ -154,9 +155,9 @@ cisco_vlan { '2': pvlan_type => 'primary', pvlan_association => '12' } cisco_interface { 'Ethernet1/6': - description => 'Private-vlan Host Port', - switchport_pvlan_host => true, - switchport_pvlan_host_association => [2, 12], + description => 'Private-vlan Host Port', + switchport_pvlan_host => true, + switchport_pvlan_host_association => [2, 12], } cisco_vlan { '13': pvlan_type => 'isolated' } @@ -194,8 +195,8 @@ switchport_pvlan_mapping_trunk => $trunk_map, } cisco_interface { 'vlan29': - description => 'SVI Private-vlan Mapping', - pvlan_mapping => '108-109', + description => 'SVI Private-vlan Mapping', + pvlan_mapping => '108-109', } } else { warning('This platform does not support the private-vlan feature') diff --git a/lib/puppet/provider/cisco_interface_channel_group/cisco.rb b/lib/puppet/provider/cisco_interface_channel_group/cisco.rb index e1cba5cd4..9de81425d 100644 --- a/lib/puppet/provider/cisco_interface_channel_group/cisco.rb +++ b/lib/puppet/provider/cisco_interface_channel_group/cisco.rb @@ -36,6 +36,7 @@ # Note: channel_group should always process first. INTF_CG_NON_BOOL_PROPS = [ :channel_group, + :channel_group_mode, :description, ] INTF_CG_BOOL_PROPS = [ @@ -119,6 +120,16 @@ def properties_set(new_interface=false) @nu.respond_to?("#{prop}=") end end + # custom setters which require one-shot multi-param setters + channel_group_mode_set + end + + def channel_group_mode_set + group = @property_flush[:channel_group] ? @property_flush[:channel_group] : @nu.channel_group + mode = @property_flush[:channel_group_mode] ? @property_flush[:channel_group_mode] : @nu.channel_group_mode + group = false if @resource[:channel_group] == :default + mode = false if @resource[:channel_group_mode] == :default + @nu.channel_group_mode_set(group, mode) end def flush diff --git a/lib/puppet/provider/port_channel/cisco.rb b/lib/puppet/provider/port_channel/cisco.rb index 69b823549..c59d95a2a 100644 --- a/lib/puppet/provider/port_channel/cisco.rb +++ b/lib/puppet/provider/port_channel/cisco.rb @@ -65,7 +65,7 @@ def flush if @resource[:interfaces] @resource[:interfaces].each do |i| bla = Cisco::InterfaceChannelGroup.interfaces[i] - bla.channel_group = @resource[:id] if @resource[:id] + bla.channel_group_mode_set(@resource[:id]) if @resource[:id] end end end diff --git a/lib/puppet/type/cisco_interface_channel_group.rb b/lib/puppet/type/cisco_interface_channel_group.rb index bf6db0df3..e5438eef8 100644 --- a/lib/puppet/type/cisco_interface_channel_group.rb +++ b/lib/puppet/type/cisco_interface_channel_group.rb @@ -29,9 +29,10 @@ Example: cisco_interface_channel_group {'Ethernet1/15': - channel_group => 201, - description => 'my channel group', - shutdown => true, + channel_group => 201, + channel_group_mode => 'active', + description => 'myChannelGroup', + shutdown => true, } " @@ -69,6 +70,13 @@ def self.title_patterns munge { |value| value == 'default' ? :default : value.to_i } end # property channel_group + newproperty(:channel_group_mode) do + desc 'Port-channel mode of the interface. Valid values are string' + + munge { |value| value == 'on' || value == 'default' ? :default : value } + newvalues(:active, :passive, :on, :default) + end + newproperty(:description) do desc "Description of the interface. Valid values are string, keyword 'default'." @@ -84,4 +92,10 @@ def self.title_patterns newvalues(:true, :false, :default) end # property shutdown + + validate do + return unless self[:channel_group] == :default + fail ArgumentError, 'channel_group_mode MUST be default if channel_group_mode is default' unless + self[:channel_group_mode] == :default + end end # Puppet::Type.newtype diff --git a/tests/beaker_tests/cisco_interface_channel_group/test_interface_channel_group.rb b/tests/beaker_tests/cisco_interface_channel_group/test_interface_channel_group.rb index 45c92e53b..c30874166 100644 --- a/tests/beaker_tests/cisco_interface_channel_group/test_interface_channel_group.rb +++ b/tests/beaker_tests/cisco_interface_channel_group/test_interface_channel_group.rb @@ -37,18 +37,20 @@ title_pattern: intf, code: [0, 2], manifest_props: { - channel_group: 'default', - description: 'default', - shutdown: 'default', + channel_group: 'default', + channel_group_mode: 'default', + description: 'default', + shutdown: 'default', }, resource: { - 'channel_group' => 'false', - 'shutdown' => 'true', + 'channel_group' => 'false', + 'channel_group_mode' => 'false', + 'shutdown' => 'true', }, } -tests[:non_default] = { - desc: '2.1 Non Default Properties commands', +tests[:non_default_no_mode] = { + desc: '2.1 Non Default Properties with no channel group mode', title_pattern: intf, manifest_props: { channel_group: 201, @@ -62,6 +64,23 @@ }, } +tests[:non_default_mode] = { + desc: '2.2 Non Default Properties with channel group mode', + title_pattern: intf, + manifest_props: { + channel_group: 201, + channel_group_mode: 'active', + description: 'chan group desc', + shutdown: 'false', + }, + resource: { + 'channel_group' => '201', + 'channel_group_mode' => 'active', + 'description' => 'chan group desc', + 'shutdown' => 'false', + }, +} + ################################################################# # TEST CASE EXECUTION ################################################################# @@ -75,6 +94,7 @@ # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") - test_harness_run(tests, :non_default) + test_harness_run(tests, :non_default_no_mode) + test_harness_run(tests, :non_default_mode) end logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/file_service_package/test_package_patch.rb b/tests/beaker_tests/file_service_package/test_package_patch.rb index 1d3583a8a..6507bdd0a 100755 --- a/tests/beaker_tests/file_service_package/test_package_patch.rb +++ b/tests/beaker_tests/file_service_package/test_package_patch.rb @@ -43,6 +43,9 @@ when /7.0.3.I4.2/ filename = 'nxos.sample-n9k_EOR-1.0.0-7.0.3.I4.2.lib32_n9000.rpm' version = '1.0.0-7.0.3.I4.2' +when /7.0.3.I4.5/ + filename = 'nxos.sample-n9k_EOR-1.0.0-7.0.3.I4.5.lib32_n9000.rpm' + version = '1.0.0-7.0.3.I4.5' when /7.0.3.I5.1/ name = 'nxos.sample-n9k_ALL' filename = 'nxos.sample-n9k_ALL-1.0.0-7.0.3.I5.1.lib32_n9000.rpm' @@ -72,7 +75,7 @@ # Skip -ALL- tests if a top-level platform/os key exludes this platform skip_unless_supported(tests) -skip_nexus_image('I2|I3', tests) +skip_nexus_image(%w(I2 I3), tests) tests[:yum_patch_install] = { desc: "1.1 Apply sample patch #{name} to image #{image?}", diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 74fc7e3e2..957580828 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1122,7 +1122,7 @@ def image_version # On match will skip all testcases # Do not use this for skipping individual properties. def skip_nexus_image(image, tests) - return unless nexus_image[image] + return unless nexus_image.match(Regexp.union(image)) msg = "Skipping all tests; '#{tests[:resource_name]}' "\ "is not supported on #{image} images" banner = '#' * msg.length From 30fea2a0f98d1e7f3709873e1081bd28104654cc Mon Sep 17 00:00:00 2001 From: mikewiebe Date: Fri, 21 Apr 2017 16:11:48 -0400 Subject: [PATCH 159/203] Remove range checks --- lib/puppet/type/cisco_vpc_domain.rb | 103 ++++++---------------------- 1 file changed, 21 insertions(+), 82 deletions(-) diff --git a/lib/puppet/type/cisco_vpc_domain.rb b/lib/puppet/type/cisco_vpc_domain.rb index 36db1be62..d8a6c35e0 100644 --- a/lib/puppet/type/cisco_vpc_domain.rb +++ b/lib/puppet/type/cisco_vpc_domain.rb @@ -2,7 +2,7 @@ # # January 2016 # -# Copyright (c) 2013-2016 Cisco and/or its affiliates. +# Copyright (c) 2013-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -112,39 +112,20 @@ def self.title_patterns end # property name newproperty(:auto_recovery_reload_delay) do - desc 'Delay (in secs) before peer is assumed dead before attempting to - recover VPCs. Valid values are integers in the range 240 .. 3600' - validate do |value| - if value != 'default' - fail('auto_recovery_reload_delay should be a value in the range - 240 .. 3600') unless value.to_i.between?(240, 3600) - end - end + desc "Delay (in secs) before peer is assumed dead before attempting to + recover VPCs. Valid values are Integer, keyword 'default'" munge { |value| value == 'default' ? :default : value.to_i } end # property name newproperty(:delay_restore) do - desc 'Delay (in secs) after peer link is restored to bring up VPCs - Valid values are integers in the range 240 .. 3600' - validate do |value| - if value != 'default' - fail('delay_restore should be a value in the range 240 .. 3600') unless - value.to_i.between?(240, 3600) - end - end + desc "Delay (in secs) after peer link is restored to bring up VPCs + Valid values are Integer, keyword 'default'" munge { |value| value == 'default' ? :default : value.to_i } end # property name newproperty(:delay_restore_interface_vlan) do - desc 'Delay (in secs) after peer link is restored to bring up Interface - VLANs or Interface BDs. Valid values are integers in the - range 240 .. 3600' - validate do |value| - if value != 'default' - fail('delay_restore should be a value in the range 240 .. 3600') unless - value.to_i.between?(240, 3600) - end - end + desc "Delay (in secs) after peer link is restored to bring up Interface + VLANs or Interface BDs. Valid values are Integer, keyword 'default'" munge { |value| value == 'default' ? :default : value.to_i } end # property name @@ -159,7 +140,7 @@ def self.title_patterns newproperty(:fabricpath_emulated_switch_id) do desc 'In vPC+ mode, configure the fabricpath switch-id aka the - Emulated switch-id. Valid values are integers in the range 1..4095' + Emulated switch-id. Valid value is an Integer' munge(&:to_i) end # property name @@ -191,50 +172,26 @@ def self.title_patterns end # property name newproperty(:peer_keepalive_hold_timeout) do - desc 'Peer keep-alive hold timeout in secs. Valid Values are integers in the - range 3..10' - validate do |value| - if value != 'default' - fail('pka hold_timeout should be a value in the range 3 .. 10') unless - value.to_i.between?(3, 10) - end - end + desc "Peer keep-alive hold timeout in secs. Valid values are Integer + or keyword 'default'" munge { |value| value == 'default' ? :default : value.to_i } end # property name newproperty(:peer_keepalive_interval) do - desc 'Peer keep-alive interval in millisecs. Valid Values are integers in - the range 400..10000' - validate do |value| - if value != 'default' - fail('pka interval should be a value in the range 400..10000') unless - value.to_i.between?(400, 10_000) - end - end + desc "Peer keep-alive interval in millisecs. Valid values are Integer + or keyword 'default'" munge { |value| value == 'default' ? :default : value.to_i } end # property name newproperty(:peer_keepalive_interval_timeout) do - desc 'Peer keep-alive interval timeout. Valid Values are integers in the - range 3..20' - validate do |value| - if value != 'default' - fail('pka interval timeout should be a value in the range 3..20') unless - value.to_i.between?(3, 20) - end - end + desc "Peer keep-alive interval timeout. Valid values are Integer + or keyword 'default'" munge { |value| value == 'default' ? :default : value.to_i } end # property name newproperty(:peer_keepalive_precedence) do - desc 'Peer keep-alive precedence. Valid Values are integers in the - range 0..7' - validate do |value| - if value != 'default' - fail('pka precedence should be a value in the range 0..7') unless - value.to_i.between?(0, 7) - end - end + desc "Peer keep-alive precedence. Valid Values are Integer + or keyword 'default'" munge { |value| value == 'default' ? :default : value.to_i } end # property name @@ -248,14 +205,8 @@ def self.title_patterns end # property name newproperty(:peer_keepalive_udp_port) do - desc 'Peer keep-alive udp port used for hellos. Valid Values are integers - in the range 1024..65000' - validate do |value| - if value != 'default' - fail('pka udp port should be a value in the range 1024..65000') unless - value.to_i.between?(1024, 65_000) - end - end + desc "Peer keep-alive udp port used for hellos. Valid Values are Integer + or keyword 'default'" munge { |value| value == 'default' ? :default : value.to_i } end # property name @@ -298,14 +249,8 @@ def self.title_patterns end # property name newproperty(:role_priority) do - desc 'Priority to be used during vPC role selection of primary vs secondary - Valid values are integers in the range 1 .. 65535' - validate do |value| - if value != 'default' - fail('system_priority should be a value in the range 1 .. 65535') unless - value.to_i.between?(1, 65_535) - end - end + desc "Priority to be used during vPC role selection of primary vs secondary + Valid values are Integer or keyword 'defalult'" munge { |value| value == 'default' ? :default : value.to_i } end # property name @@ -326,13 +271,7 @@ def self.title_patterns end # property name newproperty(:system_priority) do - desc 'VPC system priority. Valid values are integers in the range 1..65535' - validate do |value| - if value != 'default' - fail('system_priority should be a value in the range 1..65535') unless - value.to_i.between?(1, 65_535) - end - end + desc "VPC system priority. Valid values are Integer or keyword 'default'" munge { |value| value == 'default' ? :default : value.to_i } end # property name end # Puppet::Type.newtype From 705c9aa4e797e50de423134e3efcd05cbfb927a8 Mon Sep 17 00:00:00 2001 From: mikewiebe Date: Fri, 21 Apr 2017 16:37:12 -0400 Subject: [PATCH 160/203] Update README.md --- README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 177729d1b..b1df1265c 100644 --- a/README.md +++ b/README.md @@ -4208,20 +4208,19 @@ vPC domain ID. Valid values are integer in the range 1-1000. There is no default Auto Recovery enable or disable if peer is non-operational. Valid values are true, false or default. This parameter is available only on Nexus 7000 series. Default value: true. ##### `auto_recovery_reload_delay` -Delay (in secs) before peer is assumed dead before attempting to recover vPCs. Valid values are integers in the range 240..3600. Default value: 240. +Delay (in secs) before peer is assumed dead before attempting to recover vPCs. Valid values are Integer or keyword 'default' ##### `delay_restore` -Delay (in secs) after peer link is restored to bring up vPCs. Valid values are integers in the range 1..3600. Default vlaue: 30. +Delay (in secs) after peer link is restored to bring up vPCs. Valid values are Integer or keyword 'default'. ##### `delay_restore_interface_vlan` -Delay (in secs) after peer link is restored to bring up Interface VLANs or Interface BDs. Valid values are integers in the -range 1..3600. Default value: 10. +Delay (in secs) after peer link is restored to bring up Interface VLANs or Interface BDs. Valid values are Integer or keyword 'default'. ##### `dual_active_exclude_interface_vlan_bridge_domain` -Interface VLANs or BDs to exclude from suspension when dual-active. Valid value is a string of integer ranges from 1..4095. There is no default value. +Interface VLANs or BDs to exclude from suspension when dual-active. Valid values are Integer or keyword 'default'. ##### `fabricpath_emulated_switch_id` -Configure a fabricpath switch_Id to enable vPC+ mode. This is also known as the Emulated switch-id. Valid values are integers in the range 1..4095. There is no default value. +Configure a fabricpath switch_Id to enable vPC+ mode. This is also known as the Emulated switch-id. Valid values are Integer or keyword 'default'. ##### `fabricpath_multicast_load_balance` In vPC+ mode, enable or disable the fabricpath multicast load balance. This loadbalances the Designated Forwarder selection for multicast traffic. Valid values are true, false or default @@ -4236,10 +4235,10 @@ Enable or Disable Layer3 peer routing. Valid values are true/false or default. D Destination IPV4 address of the peer where Peer Keep-alives are terminated. Valid values are IPV4 unicast address. There is no default value. ##### `peer_keepalive_hold_timeout` -Peer keep-alive hold timeout in secs. Valid Values are integers in the range 3..10. Default value: 3. +Peer keep-alive hold timeout in secs. Valid values are Integer or keyword 'default'. ##### `peer_keepalive_interval` -Peer keep-alive interval in millisecs. Valid Values are integers in the range 400..10000. Default value: 1000. +Peer keep-alive interval in millisecs. Valid values are Integer or keyword 'default'. ##### `peer_keepalive_interval_timeout` Peer keep-alive interval timeout. Valid Values are integers in the range 3..20. Default value: 5. From 2c205e735eb104b9d5d0923c940acd83485ba3d6 Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Mon, 24 Apr 2017 13:34:22 -0400 Subject: [PATCH 161/203] Update README-agent-install.md --- docs/README-agent-install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README-agent-install.md b/docs/README-agent-install.md index d58184bb4..cd474d644 100644 --- a/docs/README-agent-install.md +++ b/docs/README-agent-install.md @@ -530,7 +530,7 @@ Reference | Description [PUP_CR]: https://docs.puppetlabs.com/references/latest/configuration.html ## How to get a virtual Nexus N9k -A virtual Nexus N9k may be helpful for development and testing. To obtain a virtual N9k, first register for a [cisco.com](http://cisco.com) userid at , then send the userid in an email to . +A virtual Nexus N9k may be helpful for development and testing. To obtain a virtual N9k, first register for a [cisco.com](http://cisco.com) userid at , then download the software from [CCO](https://software.cisco.com/download/release.html?mdfid=286312239&softwareid=282088129&release=7.0(3)I5(2)&relind=AVAILABLE&rellifecycle=&reltype=latest). ## License From cdf445efc392c85594511ce0a28fbdcda8ca71ef Mon Sep 17 00:00:00 2001 From: rahushen Date: Tue, 25 Apr 2017 16:58:46 -0400 Subject: [PATCH 162/203] Feature/upgrade services (#451) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add new type for cisco_service * Add new provider cisco_Service * Fix rubopcop errors * fix rubocop errors * Fix date on CopyRight * Rename cisco_service to cisco_upgrade * Redesigned upgrade resource type based on @mikewiebe PR comments * Redesigned upgrade resource provider based on @mikewiebe PR comments * removing cisco_service resource type * rename media to uri and misc. fixes * converting del_boot_image and force_upgrade to bools * Remove whitespace * Source_uri is required * Handle node_utils API change from service to upgrade * Move source_uri validation to provider * Added image_version proc and ability to process '()' * Fix rubocop errors * Add new test file for cisco_upgrade resource beaker_tests/cisco_upgrade/test_upgrade_idempotence.rb * Running cisco_upgrade tests on n3k,n9k only * Update README with references to the new cisco_upgrade resource * Update README.md * Add demo manifest for cisco_upgrade * Add new manifest for cisco_upgrade * Added 'cisco_upgrade' to changelog * Update Support Table for cisco_upgrade Add Release for Fretta and update N9K/N3k release to camden 2e * Minor changes to README.md * Update README.md * Minor changes to documentation * Replace output with stdout.chomp for facter call * deprecate 'version' and add new property 'package' * Add support for tftp: URI Also added new property package and warning to handle deprecation of ‘version’ * Update tests * Fix typo * Modify failure message * Rework validation * remove package nil check * Add package to UPGRADE_NON_BOOL_PROPS * Fix beaker test * Add source uri validation for usb * Fix rubocop errors * Remove references to 'version' * Update Documentation * Removing source_uri and repurposing package * Adding full package path to type * Move Uri Processing to the provider * Add 'version' back for tests * Updating documentation to remove source_uri * Fix rubocop errors * Incorporating feedback on documentation --- CHANGELOG.md | 6 +++ README.md | 20 ++++---- examples/cisco/demo_upgrade.pp | 10 ++-- lib/puppet/provider/cisco_upgrade/cisco.rb | 30 +++++++++--- lib/puppet/type/cisco_upgrade.rb | 49 ++++++++----------- .../cisco_upgrade/test_upgrade_idempotence.rb | 7 +-- 6 files changed, 67 insertions(+), 55 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5129bfb42..bfdff919f 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,9 +13,15 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Extend cisco_interface_channel_group with attributes: - `channel_group_mode` +- Added support for tftp and usb URIs to `cisco_upgrade` + +- Extend `cisco_upgrade` with attributes: + - `package` + ### Changed ### Removed +- Deprecated `version` and `source_uri` attributes for `cisco_upgrade` in favor of a single attribute `package`. ### Resolved Issues diff --git a/README.md b/README.md index b1df1265c..98a10f52b 100644 --- a/README.md +++ b/README.md @@ -416,7 +416,7 @@ Symbol | Meaning | Description | [cisco_bgp_neighbor_af](#type-cisco_bgp_neighbor_af) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_bridge_domain](#type-cisco_bridge_domain) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | | [cisco_bridge_domain_vni](#type-cisco_bridge_domain_vni) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | -| [cisco_dhcp_relay_global](#type-cisco_dhcp_relay_global) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_dhcp_relay_global-caveats) +| [cisco_dhcp_relay_global](#type-cisco_dhcp_relay_global) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_dhcp_relay_global-caveats) | [cisco_encapsulation](#type-cisco_encapsulation) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | | [cisco_evpn_vni](#type-cisco_evpn_vni) | ✅ | ➖ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_evpn_vni-caveats) | | [cisco_fabricpath_global](#type-cisco_fabricpath_global) | ➖ | ➖ | ✅ | ✅ | ✅* | ➖ | \*[caveats](#cisco_fabricpath_global-caveats) | @@ -447,7 +447,7 @@ Symbol | Meaning | Description | [cisco_snmp_user](#type-cisco_snmp_user) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_tacacs_server](#type-cisco_tacacs_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_tacacs_server_host](#type-cisco_tacacs_server_host) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_upgrade](type-cisco_upgrade) | ✅* | ✅* | ➖ | ➖ | ➖ | ➖| \*[caveats](#cisco_upgrade-caveats) | +| [cisco_upgrade](type-cisco_upgrade) | ✅* | ✅* | ➖ | ➖ | ➖ | ✅* | \*[caveats](#cisco_upgrade-caveats) | | [cisco_vdc](#type-cisco_vdc) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | | [cisco_vlan](#type-cisco_vlan) | ✅* | ✅* | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_vlan-caveats) | | [cisco_vpc_domain](#type-cisco_vpc_domain) | ✅* | ✅* | ✅* | ✅* | ✅* | ➖ | \*[caveats](#cisco_vpc_domain-caveats) | @@ -4050,11 +4050,11 @@ Manages the upgrade of a Cisco device. #### Caveats -The `cisco_upgrade` is only supported on *simplex* N3k, N9k and N9k-F devices. HA devices are currently not supported. +The `cisco_upgrade` is only supported on *simplex* N3k, N9k and N9k-F devices. HA devices are currently not supported. | Property | Caveat Description | |:--------|:-------------| -| `source_uri` | Only images on `bootflash:` are supported. The puppet file provider can be used to copy the image file to `bootflash`. Refer to Demo Upgrade for an example. | +| `package` | Only images on `bootflash`, `tftp` and `usb` (if available) are supported. The puppet file provider can be used to copy the image file to `bootflash`. Refer to Demo Upgrade for an example. | #### Parameters @@ -4062,11 +4062,6 @@ The `cisco_upgrade` is only supported on *simplex* N3k, N9k and N9k-F devices. H Name of cisco_upgrade instance. Valid values are string. *Only 'image' is a valid name for the cisco_upgrade resource.* -##### `source_uri` -Image upgrade URI. Format `:`. Valid values are string. -*Example --> bootflash:nxos.7.0.3.I5.2.bin* -*NOTE: Only images on `bootflash:` are supported.* - ##### `delete_boot_image` Delete the booted image. Valid values are `true`, `false`. @@ -4075,8 +4070,11 @@ Force upgrade the device.Valid values are `true`, `false`. #### Properties -##### `version` -Version of the Cisco image to install on the device. Valid values are strings. +##### `package` +Package to install on the device. Format `:`. Valid values are strings. +*Example --> bootflash:nxos.7.0.3.I5.2.bin + --> tftp://x.x.x.x/path/to/nxos.7.0.3.I5.2.bin* +*NOTE: Only images on `bootflash:`, `tftp:` and `usb` (if available) are supported.* -- ### Type: cisco_vdc diff --git a/examples/cisco/demo_upgrade.pp b/examples/cisco/demo_upgrade.pp index 7a1876044..960c4881d 100644 --- a/examples/cisco/demo_upgrade.pp +++ b/examples/cisco/demo_upgrade.pp @@ -28,13 +28,12 @@ # agent-lab9-pm:files:2011> node default { - $gem = 'cisco_node_utils-1.5.0.gem' + $gem = 'cisco_node_utils-1.6.0.gem' $uri = 'bootflash' $image = 'nxos.7.0.3.I2.5.bin' - $version = '7.0(3)I2(5)' - + # If you are behind proxy, please set the proxy variable. - # $proxy = 'http://.:' + # $proxy = 'http://.:' $proxy = '' if $proxy == '' { @@ -69,8 +68,7 @@ } cisco_upgrade { 'image' : - version => "${version}", - source_uri => "${uri}:///${image}", + package => "${uri}:///${image}", force_upgrade => false, delete_boot_image => false, } diff --git a/lib/puppet/provider/cisco_upgrade/cisco.rb b/lib/puppet/provider/cisco_upgrade/cisco.rb index c6c187e4d..029e51075 100644 --- a/lib/puppet/provider/cisco_upgrade/cisco.rb +++ b/lib/puppet/provider/cisco_upgrade/cisco.rb @@ -40,7 +40,8 @@ mk_resource_methods UPGRADE_NON_BOOL_PROPS = [ - :version + :version, + :package, ] UPGRADE_ALL_PROPS = UPGRADE_NON_BOOL_PROPS @@ -60,6 +61,7 @@ def self.instances inst << new( name: 'image', + package: upgrade.package, version: upgrade.image_version) end @@ -69,14 +71,30 @@ def self.prefetch(resources) def version=(new_version) return if new_version.nil? + fail "The property 'version' has been deprecated." + end + + def package=(new_package) + return if new_package.nil? # Convert del_boot_image and force_upgrade from symbols # to Boolean Class - fail 'The source_uri parameter must be set in the manifest' if - @resource[:source_uri].nil? del_boot_image = (@resource[:delete_boot_image] == :true) force_upgrade = (@resource[:force_upgrade] == :true) - @nu.upgrade(new_version, @resource[:source_uri][:image_name], @resource[:source_uri][:uri], - del_boot_image, force_upgrade) - @property_hash[:version] = new_version + # The Node-utils API expects uri and image_name as two + # separate arguments. Pre-processing the arguments here. + pkg = @resource[:package] + if pkg.include?('/') + if pkg.include?('bootflash') || pkg.include?('usb') + uri = pkg.split('/')[0] + else + uri = pkg.rpartition('/')[0] + '/' + end + image_name = pkg.split('/')[-1] + else + uri = pkg.split(':')[0] + ':' + image_name = pkg.split(':')[-1] + end + @nu.upgrade(image_name, uri, del_boot_image, force_upgrade) + @property_hash[:package] = pkg end end diff --git a/lib/puppet/type/cisco_upgrade.rb b/lib/puppet/type/cisco_upgrade.rb index 3bf9cc55a..c6411dcf9 100644 --- a/lib/puppet/type/cisco_upgrade.rb +++ b/lib/puppet/type/cisco_upgrade.rb @@ -29,8 +29,7 @@ Example: ``` cisco_upgrade {'image' : - version => '7.0(3)I5(1)', - source_uri => 'bootflash:///nxos.7.0.3.I5.1.bin', + package => 'bootflash:///nxos.7.0.3.I5.1.bin', force_upgrade => false, delete_boot_image => false, } @@ -60,31 +59,6 @@ def self.title_patterns end end - newparam(:source_uri) do - examples = "\nExample:\nbootflash:nxos.7.0.3.I5.2.bin" - supported = "\nNOTE: Only bootflash: is supported." - desc "URI to the image to install on the device. Format :. - Valid values are string.#{examples}#{supported}" - - validate do |uri| - fail 'source_uri must match format :' unless uri[/\S+:\S+/] - end - munge do |uri| - image = {} - # Convert : to a hash. - # The Node-utils API expects uri and image_name as two - # separate arguments. Pre-processing the arguments here. - if uri.include?('/') - image[:uri] = uri.split('/')[0] - image[:image_name] = uri.split('/')[-1] - else - image[:uri] = uri.split(':')[0] + ':' - image[:image_name] = uri.split(':')[-1] - end - image - end - end # param source_uri - newparam(:force_upgrade) do desc 'Force upgrade the device.' defaultto :false @@ -101,16 +75,33 @@ def self.title_patterns # Attributes # ############## + # Deprecated newproperty(:version) do desc 'Version of the Cisco image to install on the device. Valid values are strings' validate do |ver| - fail "Version can't be nil or an empty string" if - ver == '' || ver.nil? == :true valid_chars = 'Version can only have the following characters: 0-9, a-z, A-Z, (, ) and .' fail "Invalid version string. #{valid_chars}" unless (/([0-9a-zA-Z().]*)/.match(ver))[0] == ver end end # property version + + newproperty(:package) do + examples = "\nExample:\nbootflash:nxos.7.0.3.I5.2.bin\n + tftp://x.x.x.x/path/to/nxos.7.0.3.I5.2.bin\n + usb1:nxos.7.0.3.I5.2.bin" + supported = "\nNOTE: Only bootflash:,tftp: and usb are supported." + desc "{ackage to install on the device. Format :. + Valid values are string.#{examples}#{supported}" + validate do |pkg| + fail 'Package should be a string.' unless pkg.is_a?(String) + fail 'package must match format :' unless pkg[/\S+:\S+/] + end + end # property package + + validate do + fail "The property 'version' has been deprecated. Please use 'package'." if + self[:version] && self[:package].nil? + end end diff --git a/tests/beaker_tests/cisco_upgrade/test_upgrade_idempotence.rb b/tests/beaker_tests/cisco_upgrade/test_upgrade_idempotence.rb index f6bb498a7..5a9e45f0b 100644 --- a/tests/beaker_tests/cisco_upgrade/test_upgrade_idempotence.rb +++ b/tests/beaker_tests/cisco_upgrade/test_upgrade_idempotence.rb @@ -22,6 +22,7 @@ # ############################################################################### require File.expand_path('../../lib/utilitylib.rb', __FILE__) +@package = on(agent, facter_cmd('-p cisco.images.system_image')).stdout.chomp @version = image_version.to_s.strip # Test hash top-level keys tests = { @@ -39,13 +40,13 @@ title_pattern: 'image', platform: 'n(3|9)k', manifest_props: { - version: @version, - source_uri: on(agent, facter_cmd('-p cisco.images.system_image')).output, + package: @package, force_upgrade: false, delete_boot_image: false, }, resource: { - version: @version + package: @package, + version: @version, }, } From 91f79e735bde2fe40d2969681c9aa3884d89517a Mon Sep 17 00:00:00 2001 From: Rick Sherman Date: Thu, 4 May 2017 10:52:48 -0500 Subject: [PATCH 163/203] (NETDEV-29) Enhance ntp_config and ntp_server add ntp_auth_key (#447) This commit enhances the existing ntp_config and ntp_server providers and adds ntp_auth_key ntp_config - authenticate - trusted_key ntp_server - key - maxpoll - minpoll - source_interface - vrf ntp_auth_key - key - algorithim - mode - password --- CHANGELOG.md | 17 ++ lib/puppet/provider/ntp_auth_key/cisco.rb | 113 +++++++++++++ lib/puppet/provider/ntp_config/cisco.rb | 93 ++++++++--- lib/puppet/provider/ntp_server/cisco.rb | 42 +++-- metadata.json | 2 +- .../ntp_auth_key_provider_defaults.rb | 131 +++++++++++++++ .../ntp_auth_key/ntp_auth_keylib.rb | 70 ++++++++ .../beaker_tests/ntp_config/ntp_configlib.rb | 20 ++- .../ntp_server_provider_defaults.rb | 31 +--- .../ntp_server_provider_nondefaults.rb | 151 ++++++++++++++++++ .../beaker_tests/ntp_server/ntp_serverlib.rb | 41 +++-- 11 files changed, 626 insertions(+), 85 deletions(-) create mode 100644 lib/puppet/provider/ntp_auth_key/cisco.rb create mode 100644 tests/beaker_tests/ntp_auth_key/ntp_auth_key_provider_defaults.rb create mode 100644 tests/beaker_tests/ntp_auth_key/ntp_auth_keylib.rb create mode 100644 tests/beaker_tests/ntp_server/ntp_server_provider_nondefaults.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index bfdff919f..141fcaca0 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,23 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Extend `cisco_upgrade` with attributes: - `package` +- Added `ntp_auth_key` with attributes: + - `algorithm` + - `key` + - `mode` + - `password` + +- Extend `ntp_config` with attributes: + - `authenticate` + - `trusted_key` + +- Extend `ntp_server` with attributes: + - `key` + - `maxpoll` + - `minpoll` + - `source_interface` + - `vrf` + ### Changed ### Removed diff --git a/lib/puppet/provider/ntp_auth_key/cisco.rb b/lib/puppet/provider/ntp_auth_key/cisco.rb new file mode 100644 index 000000000..2f2611b27 --- /dev/null +++ b/lib/puppet/provider/ntp_auth_key/cisco.rb @@ -0,0 +1,113 @@ +# April, 2017 +# +# Copyright (c) 2014-2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'cisco_node_utils' if Puppet.features.cisco_node_utils? + +begin + require 'puppet_x/cisco/autogen' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'autogen.rb')) +end + +Puppet::Type.type(:ntp_auth_key).provide(:cisco) do + desc 'The Cisco provider for ntp_auth_key.' + + confine feature: :cisco_node_utils + defaultfor operatingsystem: :nexus + + mk_resource_methods + + NTP_AUTH_KEY_ALL_PROPS = [ + :algorithm, + :mode, + :password, + ] + + def initialize(value={}) + super(value) + @ntpkey = Cisco::NtpAuthKey.ntpkeys[@property_hash[:name]] + @property_flush = {} + debug 'Created provider instance of ntp_auth_key' + end + + def self.properties_get(key, v) + debug "Checking instance, ntp_auth_key #{key}" + + current_state = { + name: key, + ensure: :present, + algorithm: v.algorithm, + mode: v.mode, + password: v.password, + } + + new(current_state) + end # self.properties_get + + def self.instances + ntpkeys = [] + Cisco::NtpAuthKey.ntpkeys.each do |key, v| + ntpkeys << properties_get(key, v) + end + + ntpkeys + end + + def self.prefetch(resources) + ntpkeys = instances + + resources.keys.each do |id| + provider = ntpkeys.find { |ntpserver| ntpserver.name == id.to_s } + resources[id].provider = provider unless provider.nil? + end + end # self.prefetch + + def exists? + @property_hash[:ensure] == :present + end + + def create + @property_flush[:ensure] = :present + end + + def destroy + @property_flush[:ensure] = :absent + end + + def flush + if @property_flush[:ensure] == :absent + @ntpkey.destroy + @ntpkey = nil + else + # Create/Update + # Create new instance with configured options + opts = { 'name' => @resource[:name] } + NTP_AUTH_KEY_ALL_PROPS.each do |prop| + next unless @resource[prop] + opts[prop.to_s] = @resource[prop].to_s + end + + begin + @ntpkey = Cisco::NtpAuthKey.new(opts) + rescue Cisco::CliError => e + error "Unable to set new values: #{e.message}" + end + end + # puts_config + end +end # Puppet::Type diff --git a/lib/puppet/provider/ntp_config/cisco.rb b/lib/puppet/provider/ntp_config/cisco.rb index 186c6477c..48961296d 100644 --- a/lib/puppet/provider/ntp_config/cisco.rb +++ b/lib/puppet/provider/ntp_config/cisco.rb @@ -1,6 +1,6 @@ # November, 2014 # -# Copyright (c) 2014-2016 Cisco and/or its affiliates. +# Copyright (c) 2014-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -31,9 +31,21 @@ mk_resource_methods - NTP_CONFIG_PROPS = { - source_interface: :source_interface - } + NTP_CONFIG_NON_BOOL_PROPS = [ + :source_interface, + :trusted_key, + ] + + NTP_CONFIG_BOOL_PROPS = [ + :authenticate + ] + + NTP_CONFIG_PROPS = NTP_CONFIG_NON_BOOL_PROPS + NTP_CONFIG_BOOL_PROPS + + PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@ntpconfig', + NTP_CONFIG_NON_BOOL_PROPS) + PuppetX::Cisco::AutoGen.mk_puppet_methods(:bool, self, '@ntpconfig', + NTP_CONFIG_BOOL_PROPS) def initialize(value={}) super(value) @@ -46,11 +58,28 @@ def self.properties_get(ntpconfig_name, v) debug "Checking instance, NtpConfig #{ntpconfig_name}" current_state = { - name: 'default', - source_interface: v.source_interface.nil? ? 'unset' : v.source_interface, - ensure: :present, + name: 'default', + ensure: :present, } + # Call node_utils getter for each property + NTP_CONFIG_NON_BOOL_PROPS.each do |prop| + val = v.send(prop) + if prop == :trusted_key + current_state[prop] = val ? val : ['unset'] + else + current_state[prop] = val ? val : 'unset' + end + end + NTP_CONFIG_BOOL_PROPS.each do |prop| + val = v.send(prop) + if val.nil? + current_state[prop] = nil + else + current_state[prop] = val ? :true : :false + end + end + debug current_state new(current_state) end # self.properties_get @@ -84,34 +113,56 @@ def validate return unless @resource[:source_interface] if @resource[:source_interface] =~ /\s+/ - fail ArgumentError, "The parameter 'source_interface' must not contain" \ + fail ArgumentError, "The parameter 'source_interface' must not contain " \ 'any spaces' end return unless @resource[:source_interface] =~ /[A-Z]+/ - fail ArgumentError, "The parameter 'source_interface' must not contain" \ + fail ArgumentError, "The parameter 'source_interface' must not contain " \ 'any uppercase characters' end - def munge_flush(val) - if val.is_a?(String) && val.eql?('unset') - nil - elsif val.is_a?(Symbol) - val.to_s + # Custom setters. + # The following properties are setters and cannot be handled + # by PuppetX::Cisco::AutoGen.mk_puppet_methods. + def ntp_trusted_keys + return unless @property_flush[:trusted_key] + # Get array of keys to remove + # If unset - remove all configured keys + if @property_flush[:trusted_key] == ['unset'] + remove = @property_hash[:trusted_key].map(&:to_s) else - val + # Otherwise calculate the delta + remove = @property_hash[:trusted_key].map(&:to_s).sort - + @property_flush[:trusted_key].map(&:to_s).sort + remove.delete('unset') + end + remove.each do |key| + @ntpconfig.trusted_key_set(false, key) unless key == 'unset' + end + # Get array of keys to add + return if @property_flush[:trusted_key] == ['unset'] + add = @property_flush[:trusted_key].map(&:to_s).sort - + @property_hash[:trusted_key].map(&:to_s).sort + remove.delete('unset') + add.each do |key| + @ntpconfig.trusted_key_set(true, key) end end def flush validate - - NTP_CONFIG_PROPS.each do |puppet_prop, cisco_prop| - if @resource[puppet_prop] - @ntpconfig.send("#{cisco_prop}=", munge_flush(@resource[puppet_prop])) if - @ntpconfig.respond_to?("#{cisco_prop}=") - end + NTP_CONFIG_PROPS.each do |prop| + next unless @resource[prop] + next if @property_flush[prop].nil? + # Call the AutoGen setters for the @ntpconfig + # node_utils object. + @property_flush[prop] = nil if @property_flush[prop] == 'unset' + @ntpconfig.send("#{prop}=", @property_flush[prop]) if + @ntpconfig.respond_to?("#{prop}=") end + # Set methods that are not autogenerated follow. + ntp_trusted_keys end end # Puppet::Type diff --git a/lib/puppet/provider/ntp_server/cisco.rb b/lib/puppet/provider/ntp_server/cisco.rb index c7c66856e..81ca3b68f 100644 --- a/lib/puppet/provider/ntp_server/cisco.rb +++ b/lib/puppet/provider/ntp_server/cisco.rb @@ -1,6 +1,6 @@ # November, 2014 # -# Copyright (c) 2014-2016 Cisco and/or its affiliates. +# Copyright (c) 2014-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -32,7 +32,11 @@ mk_resource_methods NTP_SERVER_ALL_PROPS = [ - :prefer + :key, + :prefer, + :maxpoll, + :minpoll, + :vrf, ] def initialize(value={}) @@ -46,14 +50,15 @@ def self.properties_get(ntpserver_ip, v) debug "Checking instance, ntpserver #{ntpserver_ip}" current_state = { - name: ntpserver_ip, - ensure: :present, + name: ntpserver_ip, + ensure: :present, + key: v.key, + prefer: v.prefer.to_s, + maxpoll: v.maxpoll, + minpoll: v.minpoll, + vrf: v.vrf, } - NTP_SERVER_ALL_PROPS.each do |prop| - current_state[prop] = v.send(prop) == true ? :true : :false - end - new(current_state) end # self.properties_get @@ -93,7 +98,26 @@ def flush @ntpserver = nil else # Create/Update - @ntpserver = Cisco::NtpServer.new(@resource[:name], @resource[:prefer] == :true ? true : false) + # NTP is a single line in the config and cannot be easily changed adhoc + # Remove existing config and set intended state + unless @ntpserver.nil? + # retain previous value to rollback if set fails + old_value = @ntpserver + @ntpserver.destroy + end + # Create new instance with configured options + opts = { 'name' => @resource[:name] } + NTP_SERVER_ALL_PROPS.each do |prop| + next unless @resource[prop] + opts[prop.to_s] = @resource[prop].to_s + end + + begin + @ntpserver = Cisco::NtpServer.new(opts) + rescue Cisco::CliError => e + error "Unable to set new values: #{e.message}" + old_value.create unless old_value.nil? + end end # puts_config end diff --git a/metadata.json b/metadata.json index 9e55de5b1..c5cbff93d 100644 --- a/metadata.json +++ b/metadata.json @@ -8,7 +8,7 @@ "project_page": "https://github.com/cisco/cisco-network-puppet-module", "issues_url": "https://github.com/cisco/cisco-network-puppet-module/issues", "dependencies": [ - { "name": "puppetlabs/netdev_stdlib", "version_requirement": ">=0.11.1" } + { "name": "puppetlabs/netdev_stdlib", "version_requirement": ">=0.12.0" } ], "requirements": [ { diff --git a/tests/beaker_tests/ntp_auth_key/ntp_auth_key_provider_defaults.rb b/tests/beaker_tests/ntp_auth_key/ntp_auth_key_provider_defaults.rb new file mode 100644 index 000000000..31a690c8e --- /dev/null +++ b/tests/beaker_tests/ntp_auth_key/ntp_auth_key_provider_defaults.rb @@ -0,0 +1,131 @@ +############################################################################### +# Copyright (c) 2014-2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# TestCase Name: +# ------------- +# NtpAuthKey-Provider-Defaults.rb +# +# TestCase Prerequisites: +# ----------------------- +# This is a ntp_auth_key resource testcase for Puppet Agent on Nexus devices. +# The test case assumes the following prerequisites are already satisfied: +# A. Populating the HOSTS configuration file with the agent and master +# information. +# B. Enabling SSH connection prerequisites on the N9K switch based Agent. +# C. Starting of Puppet master server on master. +# D. Sending to and signing of Puppet agent certificate request on master. +# +# TestCase: +# --------- +# This is a ntp_auth_key resource test that tests for default value for +# 'ensure' attribute of a ntp_auth_key resource. +# +# There are 2 sections to the testcase: Setup, group of teststeps. +# The 1st step is the Setup teststep that cleans up the switch state. +# Steps 2-4 deal with ntp_auth_key resource and its +# verification using Puppet Agent and the switch running-config. +# +# The testcode checks for exit_codes from Puppet Agent, Vegas shell and +# Bash shell command executions. For Vegas shell and Bash shell command +# string executions, this is the exit_code convention: +# 0 - successful command execution, > 0 - failed command execution. +# For Puppet Agent command string executions, this is the exit_code convention: +# 0 - no changes have occurred, 1 - errors have occurred, +# 2 - changes have occurred, 4 - failures have occurred and +# 6 - changes and failures have occurred. +# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. +# The testcode also uses RegExp pattern matching on stdout or output IO +# instance attributes of Result object from on() method invocation. +# +############################################################################### + +# Require UtilityLib.rb and NtpAuthKeyLib.rb paths. +require File.expand_path('../../lib/utilitylib.rb', __FILE__) +require File.expand_path('../ntp_auth_keylib.rb', __FILE__) + +result = 'PASS' +testheader = 'ntp_auth_key Resource :: All Attributes Defaults' + +# @test_name [TestCase] Executes defaults testcase for ntp_auth_key Resource. +test_name "TestCase :: #{testheader}" do + # @step [Step] Sets up switch for provider test. + step 'TestStep :: Setup switch for provider test' do + # Cleanup before starting test. + resource_absent_cleanup(agent, 'ntp_auth_key') + # Cleanup after running test. + teardown { resource_absent_cleanup(agent, 'ntp_auth_key') } + end + + # @step [Step] Requests manifest from the master server to the agent. + step 'TestStep :: Get resource present manifest from master' do + # Expected exit_code is 0 since this is a bash shell cmd. + on(master, NtpAuthKeyLib.create_ntp_auth_key_manifest_present) + + # Expected exit_code is 2 since this is a puppet agent cmd with change. + cmd_str = PUPPET_BINPATH + 'agent -t' + on(agent, cmd_str, acceptable_exit_codes: [2]) + + logger.info("Get resource present manifest from master :: #{result}") + end + + # @step [Step] Checks ntp_auth_key resource on agent using resource cmd. + step 'TestStep :: Check ntp_auth_key resource presence on agent' do + # Expected exit_code is 0 since this is a puppet resource cmd. + # Flag is set to false to check for presence of RegExp pattern in stdout. + cmd_str = PUPPET_BINPATH + 'resource ntp_auth_key 1' + on(agent, cmd_str) do + search_pattern_in_output(stdout, { 'ensure' => 'present' }, + false, self, logger) + search_pattern_in_output(stdout, { 'algorithm' => 'md5' }, + false, self, logger) + search_pattern_in_output(stdout, { 'mode' => '7' }, + false, self, logger) + search_pattern_in_output(stdout, { 'password' => 'test' }, + false, self, logger) + end + + logger.info("Check ntp_auth_key resource presence on agent :: #{result}") + end + + # @step [Step] Requests manifest from the master server to the agent. + step 'TestStep :: Get resource absent manifest from master' do + # Expected exit_code is 0 since this is a bash shell cmd. + on(master, NtpAuthKeyLib.create_ntp_auth_key_manifest_absent) + + # Expected exit_code is 2 since this is a puppet agent cmd with change. + cmd_str = PUPPET_BINPATH + 'agent -t' + on(agent, cmd_str, acceptable_exit_codes: [2]) + + logger.info("Get resource absent manifest from master :: #{result}") + end + + # @step [Step] Checks ntp_auth_key resource on agent using resource cmd. + step 'TestStep :: Check ntp_auth_key resource absence on agent' do + # Expected exit_code is 0 since this is a puppet resource cmd. + # Flag is set to true to check for absence of RegExp pattern in stdout. + cmd_str = PUPPET_BINPATH + 'resource ntp_auth_key 1' + on(agent, cmd_str) do + search_pattern_in_output(stdout, { 'ensure' => 'present' }, + true, self, logger) + end + + logger.info("Check ntp_auth_key resource absence on agent :: #{result}") + end + + # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. + raise_passfail_exception(result, testheader, self, logger) +end + +logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/ntp_auth_key/ntp_auth_keylib.rb b/tests/beaker_tests/ntp_auth_key/ntp_auth_keylib.rb new file mode 100644 index 000000000..a919ac964 --- /dev/null +++ b/tests/beaker_tests/ntp_auth_key/ntp_auth_keylib.rb @@ -0,0 +1,70 @@ +############################################################################### +# Copyright (c) 2014-2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# NTP Server Utility Library: +# --------------------- +# ntp_auth_keylib.rb +# +# This is the utility library for the ntp server provider Beaker test cases +# that contains the common methods used across the ntp server testsuite's +# cases. The library is implemented as a module with related methods and +# constants defined inside it for use as a namespace. All of the methods are +# defined as module methods. +# +# Every Beaker ntp server test case that runs an instance of Beaker::TestCase +# requires NtpAuthKeyLib module. +# +# The module has a single set of methods: +# A. Methods to create manifests for ntp_auth_key Puppet provider test cases. +############################################################################### + +# Require UtilityLib.rb path. +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# A library to assist testing ntp_auth_key resource +module NtpAuthKeyLib + # A. Methods to create manifests for ntp_auth_key Puppet provider test cases. + + # Method to create a manifest for ntp_auth_key resource attribute 'ensure' + # where 'ensure' is set to present. + # @param none [None] No input parameters exist. + # @result none [None] Returns no object. + def self.create_ntp_auth_key_manifest_present + manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} +node default { + ntp_auth_key { '1': + ensure => 'present', + password => 'test', + } +} +EOF" + manifest_str + end + + # Method to create a manifest for ntp_auth_key resource attribute 'ensure' + # where 'ensure' is set to absent. + # @param none [None] No input parameters exist. + # @result none [None] Returns no object. + def self.create_ntp_auth_key_manifest_absent + manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} +node default { + ntp_auth_key {'1': + ensure => absent, + } +} +EOF" + manifest_str + end +end diff --git a/tests/beaker_tests/ntp_config/ntp_configlib.rb b/tests/beaker_tests/ntp_config/ntp_configlib.rb index 87292416b..46dda8226 100644 --- a/tests/beaker_tests/ntp_config/ntp_configlib.rb +++ b/tests/beaker_tests/ntp_config/ntp_configlib.rb @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. +# Copyright (c) 2014-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -35,20 +35,25 @@ # A library to assist testing ntp_config resource module NtpConfigLib - # Group of Constants used in negative tests for ntp_config provider. - ENSURE_NEGATIVE = 'unknown' - # A. Methods to create manifests for ntp_config Puppet provider test cases. # Method to create a manifest for ntp_config resource attribute 'ensure' # where 'ensure' is set to present. - # @param none [None] No input parameters exist. + # @param intf [String] source_interface # @result none [None] Returns no object. def self.create_ntp_config_manifest_set(intf) manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { + ntp_auth_key { '1': + ensure => 'present', + algorithm => 'md5', + mode => '7', + password => 'test', + } ntp_config {'default': + authenticate => 'true', source_interface => '#{intf}', + trusted_key => '1', } } EOF" @@ -62,8 +67,13 @@ def self.create_ntp_config_manifest_set(intf) def self.create_ntp_config_manifest_unset manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { + ntp_auth_key { '1': + ensure => 'absent', + } ntp_config {'default': + authenticate => 'false', source_interface => 'unset', + trusted_key => 'unset', } } EOF" diff --git a/tests/beaker_tests/ntp_server/ntp_server_provider_defaults.rb b/tests/beaker_tests/ntp_server/ntp_server_provider_defaults.rb index 6ffca7972..7e98c0900 100644 --- a/tests/beaker_tests/ntp_server/ntp_server_provider_defaults.rb +++ b/tests/beaker_tests/ntp_server/ntp_server_provider_defaults.rb @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. +# Copyright (c) 2014-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -64,6 +64,8 @@ step 'TestStep :: Setup switch for provider test' do # Cleanup before starting test. resource_absent_cleanup(agent, 'ntp_server') + # Cleanup after running test. + teardown { resource_absent_cleanup(agent, 'ntp_server') } end # @step [Step] Requests manifest from the master server to the agent. @@ -88,32 +90,7 @@ false, self, logger) search_pattern_in_output(stdout, { 'prefer' => 'false' }, false, self, logger) - end - - logger.info("Check ntp_server resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource present and prefer true manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, NtpServerLib.create_ntp_server_manifest_present_prefer) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource present and prefer true manifest from master :: #{result}") - end - - # @step [Step] Checks ntp_server resource on agent using resource cmd. - step 'TestStep :: Check ntp_server resource presence with prefer true on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + 'resource ntp_server 5.5.5.5' - on(agent, cmd_str) do - search_pattern_in_output(stdout, { 'ensure' => 'present' }, - false, self, logger) - search_pattern_in_output(stdout, { 'prefer' => 'true' }, + search_pattern_in_output(stdout, { 'vrf' => 'default' }, false, self, logger) end diff --git a/tests/beaker_tests/ntp_server/ntp_server_provider_nondefaults.rb b/tests/beaker_tests/ntp_server/ntp_server_provider_nondefaults.rb new file mode 100644 index 000000000..148ab032d --- /dev/null +++ b/tests/beaker_tests/ntp_server/ntp_server_provider_nondefaults.rb @@ -0,0 +1,151 @@ +############################################################################### +# Copyright (c) 2014-2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# TestCase Name: +# ------------- +# NtpServer-Provider-NonDefaults.rb +# +# TestCase Prerequisites: +# ----------------------- +# This is a ntp_server resource testcase for Puppet Agent on Nexus devices. +# The test case assumes the following prerequisites are already satisfied: +# A. Populating the HOSTS configuration file with the agent and master +# information. +# B. Enabling SSH connection prerequisites on the N9K switch based Agent. +# C. Starting of Puppet master server on master. +# D. Sending to and signing of Puppet agent certificate request on master. +# +# TestCase: +# --------- +# This is a ntp_server resource test that tests for nondefault value for +# 'ensure' attribute of a ntp_server resource. +# +# There are 2 sections to the testcase: Setup, group of teststeps. +# The 1st step is the Setup teststep that cleans up the switch state. +# Steps 2-4 deal with ntp_server resource and its +# verification using Puppet Agent and the switch running-config. +# +# The testcode checks for exit_codes from Puppet Agent, Vegas shell and +# Bash shell command executions. For Vegas shell and Bash shell command +# string executions, this is the exit_code convention: +# 0 - successful command execution, > 0 - failed command execution. +# For Puppet Agent command string executions, this is the exit_code convention: +# 0 - no changes have occurred, 1 - errors have occurred, +# 2 - changes have occurred, 4 - failures have occurred and +# 6 - changes and failures have occurred. +# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. +# The testcode also uses RegExp pattern matching on stdout or output IO +# instance attributes of Result object from on() method invocation. +# +############################################################################### + +# Require UtilityLib.rb and NtpServerLib.rb paths. +require File.expand_path('../../lib/utilitylib.rb', __FILE__) +require File.expand_path('../ntp_serverlib.rb', __FILE__) + +result = 'PASS' +testheader = 'ntp_server Resource :: All Attributes Nondefaults' + +tests = { + agent: agent, + master: master, + intf_type: 'ethernet', + resource_name: 'ntp_server', +} + +# @test_name [TestCase] Executes defaults testcase for ntp_server Resource. +test_name "TestCase :: #{testheader}" do + # Find an available test interface on this device + intf = find_interface(tests) + + # @step [Step] Sets up switch for provider test. + step 'TestStep :: Setup switch for provider test' do + # Cleanup before starting test. + resource_absent_cleanup(agent, 'ntp_auth_key') + resource_absent_cleanup(agent, 'ntp_server') + resource_absent_cleanup(agent, 'cisco_vrf') + # Cleanup after running test. + teardown do + resource_absent_cleanup(agent, 'ntp_auth_key') + resource_absent_cleanup(agent, 'ntp_server') + resource_absent_cleanup(agent, 'cisco_vrf') + end + end + + # @step [Step] Requests manifest from the master server to the agent. + step 'TestStep :: Get resource present manifest from master' do + # Expected exit_code is 0 since this is a bash shell cmd. + on(master, NtpServerLib.create_ntp_server_manifest_present_nondefault(intf)) + + # Expected exit_code is 2 since this is a puppet agent cmd with change. + cmd_str = PUPPET_BINPATH + 'agent -t' + on(agent, cmd_str, acceptable_exit_codes: [2]) + + logger.info("Get resource present manifest from master :: #{result}") + end + + # @step [Step] Checks ntp_server resource on agent using resource cmd. + step 'TestStep :: Check ntp_server resource presence on agent' do + # Expected exit_code is 0 since this is a puppet resource cmd. + # Flag is set to false to check for presence of RegExp pattern in stdout. + cmd_str = PUPPET_BINPATH + 'resource ntp_server 5.5.5.5' + on(agent, cmd_str) do + search_pattern_in_output(stdout, { 'ensure' => 'present' }, + false, self, logger) + search_pattern_in_output(stdout, { 'key' => '1' }, + false, self, logger) + search_pattern_in_output(stdout, { 'maxpoll' => '12' }, + false, self, logger) + search_pattern_in_output(stdout, { 'minpoll' => '6' }, + false, self, logger) + search_pattern_in_output(stdout, { 'prefer' => 'true' }, + false, self, logger) + search_pattern_in_output(stdout, { 'vrf' => 'red' }, + false, self, logger) + end + + logger.info("Check ntp_server resource presence on agent :: #{result}") + end + + # @step [Step] Requests manifest from the master server to the agent. + step 'TestStep :: Get resource absent manifest from master' do + # Expected exit_code is 0 since this is a bash shell cmd. + on(master, NtpServerLib.create_ntp_server_manifest_absent) + + # Expected exit_code is 2 since this is a puppet agent cmd with change. + cmd_str = PUPPET_BINPATH + 'agent -t' + on(agent, cmd_str, acceptable_exit_codes: [2]) + + logger.info("Get resource absent manifest from master :: #{result}") + end + + # @step [Step] Checks ntp_server resource on agent using resource cmd. + step 'TestStep :: Check ntp_server resource absence on agent' do + # Expected exit_code is 0 since this is a puppet resource cmd. + # Flag is set to true to check for absence of RegExp pattern in stdout. + cmd_str = PUPPET_BINPATH + 'resource ntp_server 5.5.5.5' + on(agent, cmd_str) do + search_pattern_in_output(stdout, { 'ensure' => 'present' }, + true, self, logger) + end + + logger.info("Check ntp_server resource absence on agent :: #{result}") + end + + # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. + raise_passfail_exception(result, testheader, self, logger) +end + +logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/ntp_server/ntp_serverlib.rb b/tests/beaker_tests/ntp_server/ntp_serverlib.rb index 3b55b18b2..a78d78c9a 100644 --- a/tests/beaker_tests/ntp_server/ntp_serverlib.rb +++ b/tests/beaker_tests/ntp_server/ntp_serverlib.rb @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. +# Copyright (c) 2014-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -35,9 +35,6 @@ # A library to assist testing ntp_server resource module NtpServerLib - # Group of Constants used in negative tests for ntp_server provider. - ENSURE_NEGATIVE = 'unknown' - # A. Methods to create manifests for ntp_server Puppet provider test cases. # Method to create a manifest for ntp_server resource attribute 'ensure' @@ -70,31 +67,31 @@ def self.create_ntp_server_manifest_absent manifest_str end - # Method to create a manifest for ntp_server resource attribute 'ensure' - # where 'ensure' is set to unknown. - # @param none [None] No input parameters exist. + # Method to create a manifest for ntp_server attributes: + # ensure, timeout, deadtime, encryption_type, encryption_password + # @param intf [String] source_interface # @result none [None] Returns no object. - def self.create_ntp_server_manifest_negative + def self.create_ntp_server_manifest_present_nondefault(intf) manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} node default { - ntp_server {'5.5.5.5': - ensure => #{NtpServerLib::ENSURE_NEGATIVE}, + ntp_auth_key { '1': + ensure => 'present', + algorithm => 'md5', + mode => '7', + password => 'test', } -} -EOF" - manifest_str - end - - # Method to create a manifest for ntp_server resource attribute 'ensure' and 'prefer', - # where 'ensure' is set to present and 'prefer' is set to true. - # @param none [None] No input parameters exist. - # @result none [None] Returns no object. - def self.create_ntp_server_manifest_present_prefer - manifest_str = "cat <#{PUPPETMASTER_MANIFESTPATH} -node default { ntp_server {'5.5.5.5': ensure => present, + key => 1, prefer => true, + maxpoll => 12, + minpoll => 6, + source_interface => '#{intf}', + vrf => 'red', + require => [ Cisco_vrf['red'], Ntp_auth_key['1'] ], + } + cisco_vrf { 'red': + ensure => 'present', } } EOF" From 97f56b8f1dc2fc5216bf786a6534b960bdafcd80 Mon Sep 17 00:00:00 2001 From: saichint Date: Mon, 8 May 2017 06:47:04 -0700 Subject: [PATCH 164/203] Exclude (#453) * adding excluded props for versions * add bgp_neighbor exclude * more tests for adding properties * vxlan_vtep to be tested * vxlan_vtp_vni beaker * fix beaker dependency manifest * doc * README.md * typo fix * examples * rubocop fix --- README.md | 68 ++- lib/facter/cisco.rb | 2 + .../cisco_bfd_global/test_bfd_global.rb | 9 +- tests/beaker_tests/cisco_bgp/test_bgp.rb | 18 +- .../cisco_bgp_neighbor/test_bgpneighbor.rb | 8 +- .../test_dhcp_relay_global.rb | 9 +- .../test_overlay_global.rb | 22 +- .../cisco_route_map/test_route_map.rb | 41 +- .../cisco_stp_global/test_stp_global.rb | 68 +-- .../cisco_vpc_domain/test_vpc_domain.rb | 39 +- .../cisco_vxlan_vtep/test_vxlan_vtep.rb | 8 +- .../test_vxlan_vtep_vni.rb | 399 +++++++----------- tests/beaker_tests/lib/utilitylib.rb | 42 ++ 13 files changed, 366 insertions(+), 367 deletions(-) diff --git a/README.md b/README.md index 98a10f52b..1578f5c89 100644 --- a/README.md +++ b/README.md @@ -434,7 +434,7 @@ Symbol | Meaning | Description | [cisco_ospf](#type-cisco_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_ospf_vrf](#type-cisco_ospf_vrf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅ = Supported
➖ = Not Applicable | N9k | N3k | N5k | N6k | N7k | N9k-F | Caveats | -| [cisco_overlay_global](#type-cisco_overlay_global) | ✅ | ➖ | ✅ | ✅ | ✅ | ✅ | +| [cisco_overlay_global](#type-cisco_overlay_global) | ✅ | ✅* | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_overlay_global-caveats) | | [cisco_pim](#type-cisco_pim) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_pim-caveats) | | [cisco_pim_rp_address](#type-cisco_pim_rp_address) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_pim_grouplist](#type-cisco_pim_grouplist) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | @@ -454,8 +454,8 @@ Symbol | Meaning | Description | [cisco_vrf](#type-cisco_vrf) | ✅ | ✅* | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_vrf-caveats) | | [cisco_vrf_af](#type-cisco_vrf_af) | ✅ | ✅* | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_vrf_af-caveats) | | [cisco_vtp](#type-cisco_vtp) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_vxlan_vtep](#type-cisco_vxlan_vtep) | ✅ | ➖ | ✅ | ✅ | ✅ | ✅ | -| [cisco_vxlan_vtep_vni](#type-cisco_vxlan_vtep_vni) | ✅ | ➖ | ✅ | ✅ | ✅ | ✅ | +| [cisco_vxlan_vtep](#type-cisco_vxlan_vtep) | ✅ | ➖ | ✅ | ✅ | ✅* | ✅ | \*[caveats](#cisco_vxlan_vtep-caveats) | +| [cisco_vxlan_vtep_vni](#type-cisco_vxlan_vtep_vni) | ✅ | ➖ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_vxlan_vtep_vni-caveats) | ##### NetDev Providers @@ -972,7 +972,7 @@ Manages configuration of a BFD (Bidirectional Forwarding Detection) instance. | `fabricpath_interval` | Not supported on N3k, N9k-F, N9k | | `fabricpath_slow_timer` | Not supported on N3k, N9k-F, N9k | | `fabricpath_vlan` | Not supported on N3k, N9k-F, N9k | -| `interval` | Not supported on N9k-F, N9k | +| `interval` | Supported on N3k, N5k, N6k, N7k
Supported in OS Version 7.0(3)F2(1) and later on N9k-F
Supported in OS Version 7.0(3)I6(1) and later on N9k | | `ipv4_echo_rx_interval` | Not supported on N5k, N6k | | `ipv4_interval` | Not supported on N5k, N6k | | `ipv4_slow_timer` | Not supported on N5k, N6k | @@ -1053,14 +1053,14 @@ Manages configuration of a BGP instance. | Property | Caveat Description | |:--------|:-------------| -| `disable_policy_batching_ipv4` | Not supported on N5k, N6k, N7k | -| `disable_policy_batching_ipv6` | Not supported on N5k, N6k, N7k | +| `disable_policy_batching_ipv4` | Not supported on N5k, N6k
Supported in OS Version 8.1.1 and later on N7k | +| `disable_policy_batching_ipv6` | Not supported on N5k, N6k
Supported in OS Version 8.1.1 and later on N7k | | `event_history_errors ` | supported on N3|9k on 7.0(3)I5(1) and later images | | `event_history_events ` | default value is 'large' for N3|9k on 7.0(3)I5(1) and later images | | `event_history_objstore ` | supported on N3|9k on 7.0(3)I5(1) and later images | | `event_history_periodic ` | default value is 'false' for N3|9k on 7.0(3)I5(1) and later images | -| `neighbor_down_fib_accelerate` | Not supported on N5k, N6k, N7k | -| `reconnect_interval` | Not supported on N5k, N6k, N7k | +| `neighbor_down_fib_accelerate` | Not supported on N5k, N6k
Supported in OS Version 8.1.1 and later on N7k | +| `reconnect_interval` | Not supported on N5k, N6k
Supported in OS Version 8.1.1 and later on N7k | | `suppress_fib_pending` | Idempotence supported only on 7.0(3)I5(1) and later images N3|9k | #### Parameters @@ -1402,7 +1402,7 @@ Manages configuration of a BGP Neighbor. | Property | Caveat Description | |:--------|:-------------| -| `log_neighbor_changes` | Not supported on N5k, N6k, N7k | +| `log_neighbor_changes` | Not supported on N5k, N6k
Minimum puppet module version 1.7.0 for N7k
Supported in OS Version 8.1.1 and later on N7k | | `bfd` | (ciscopuppet v1.4.0) BFD support added for all platforms | | `bfd` on IPv6 | Not supported on N5k, N6k | @@ -1689,7 +1689,7 @@ Manages configuration of a DHCP relay global configuration. | `ipv4_information_trust_all` | Not supported on N5k, N6k | | `ipv4_src_addr_hsrp` | Not supported on N3k, N9k, N9k-F | | `ipv4_sub_option_circuit_id_custom` | Not supported on N7k, N9k-F(TBD) and supported on N3k and N9k running os version 7.0(3)I3.1 and later | -| `ipv4_sub_option_circuit_id_string` | Only supported on N3k | +| `ipv4_sub_option_circuit_id_string` | Supported on N3k
Supported in OS Version 7.0(3)I6(1) and later on N9k | | `ipv6_option_cisco` | Not supported on N5k, N6k | #### Parameters @@ -3096,12 +3096,22 @@ Also configures anycast gateway MAC of the switch. | Platform | OS Minimum Version | Module Minimum Version | |----------|:------------------:|:----------------------:| | N9k | 7.0(3)I2(1) | 1.2.0 | -| N3k | not applicable | not applicable | +| N3k | 7.0(3)I6(1) | 1.7.0 | | N5k | 7.3(0)N1(1) | 1.3.0 | | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +#### Caveats + +| Property | Caveat Description | +|:--------|:-------------| +| `anycast_gateway_mac ` | Not supported on N3k | +| `dup_host_ip_addr_detection_host_moves` | Not supported on N3k | +| `dup_host_ip_addr_detection_timeout` | Not supported on N3k | +| `dup_host_mac_detection_host_moves` | Supported in OS Version 7.0(3)I6(1) and later on N3k | +| `dup_host_mac_detection_timeout` | Supported in OS Version 7.0(3)I6(1) and later on N3k | + #### Parameters ##### `name` @@ -3295,12 +3305,12 @@ Manages a Cisco Route Map. | `match_evpn_route_type_all` | Not supported on N3k,N9k-F,N9k | | `match_length` | Not supported on N3k,N9k-F,N9k | | `match_mac_list` | Not supported on N3k,N9k-F,N9k | -| `match_metric` | Not supported on N9k-F | +| `match_metric` | Supported in OS Version 7.0(3)F2(1) and later on N9k-F | | `match_ospf_area` | Not supported on N5k,N6k,N7k,N9k-F
Supported in OS version 7.0(3)I5.1 and later on N3k, N9k | | `match_vlan` | Not supported on N3k,N9k-F,N9k | -| `set_extcommunity_4bytes_additive` | Not supported on N9k-F | -| `set_extcommunity_4bytes_non_transitive` | Not supported on N9k-F | -| `set_extcommunity_4bytes_transitive` | Not supported on N9k-F | +| `set_extcommunity_4bytes_additive` | Supported in OS Version 7.0(3)F2(1) and later on N9k-F | +| `set_extcommunity_4bytes_non_transitive` | Supported in OS Version 7.0(3)F2(1) and later on N9k-F | +| `set_extcommunity_4bytes_transitive` | Supported in OS Version 7.0(3)F2(1) and later on N9k-F | | `set_extcommunity_cost_igp` | Not supported on N9k-F | | `set_extcommunity_cost_pre_bestpath` | Not supported on N9k-F | | `set_extcommunity_rt_additive` | Not supported on N9k-F | @@ -3309,15 +3319,14 @@ Manages a Cisco Route Map. | `set_ipv4_default_next_hop` | Not supported on N5k,N6k,N9k-F,N9k | | `set_ipv4_default_next_hop_load_share` | Not supported on N5k,N6k,N9k-F,N9k | | `set_ipv4_next_hop` | Not supported on N9k-F | -| `set_ipv4_next_hop_load_share` | Not supported on N5k,N6k,N9k-F
Supported in OS Version 7.0(3)I5.1 and later on N9k | +| `set_ipv4_next_hop_load_share` | Not supported on N5k,N6k
Supported in OS Version 7.0(3)I5.1 and later on N9k
Supported in OS Version 7.0(3)F2(1) and later on N9k-F | | `set_ipv4_next_hop_redist` | Supported on N5k,N6k,N7k,N9k-F
Supported in OS Version 7.0(3)I5.1 and later on N3k,N9k | | `set_ipv4_precedence` | Not supported on N9k-F | | `set_ipv4_prefix` | Not supported on N5k,N6k,N9k-F | -OS Version 7.0(3)I5.1 and later on N3k,N9k | | `set_ipv6_default_next_hop` | Not supported on N5k,N6k,N9k-F,N9k | | `set_ipv6_default_next_hop_load_share` | Not supported on N5k,N6k,N9k-F,N9k | | `set_ipv6_next_hop` | Not supported on N9k-F | -| `set_ipv6_next_hop_load_share` | Not supported on N5k,N6k,N9k-F
Supported in OS Version 7.0(3)I5.1 and later on N9k | +| `set_ipv6_next_hop_load_share` | Not supported on N5k,N6k
Supported in OS Version 7.0(3)I5.1 and later on N9k
Supported in OS Version 7.0(3)F2(1) and later on N9k-F | | `set_ipv6_next_hop_redist` | Supported on N5k,N6k,N7k,N9k-F
Supported in OS Version 7.0(3)I5.1 and later on N3k,N9k | | `set_ipv6_prefix` | Not supported on N5k,N6k,N9k-F | | `set_vrf` | Supported on N7k | @@ -3712,7 +3721,7 @@ Manages spanning tree global parameters | `bd_max_age` | Supported only on N7k | | `bd_priority` | Supported only on N7k | | `bd_root_priority` | Supported only on N7k | -| `domain` | Supported only on N5k, N6k, N7k | +| `domain` | Supported only on N5k, N6k, N7k
Supported in OS Version 7.0(3)I6(1) and later on N3k, N9k | | `fcoe` | Supported only on N9k | #### Parameters @@ -4188,11 +4197,11 @@ Manages the virtual Port Channel (vPC) domain configuration of a Cisco device. | `auto_recovery` | Only supported on N3k, N7k, N9k | | `fabricpath_emulated_switch_id` | Only supported on N7k | | `fabricpath_multicast_load_balance` | Only supported on N7k | -| `layer3_peer_routing` | Only supported on N5k, N6k, N7k | +| `layer3_peer_routing` | Only supported on N5k, N6k, N7k
Supported in OS Version 7.0(3)I6(1) and later on N3k, N9k | | `peer_gateway_exclude_vlan` | Only supported on N5k, N6k, N7k | | `port_channel_limit` | Only supported on N7k | | `self_isolation` | Only supported on N7k | -| `shutdown` | Only supported on N5k, N6k, N7k | +| `shutdown` | Only supported on N5k, N6k, N7k
Supported in OS Version 7.0(3)I6(1) and later on N3k, N9k | #### Parameters @@ -4480,6 +4489,12 @@ Creates a VXLAN Network Virtualization Endpoint (NVE) overlay interface that ter | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +#### Caveats + +| Property | Caveat Description | +|---------------------------------|--------------------------------------| +| source_interface_hold_down_time | Not supported on N3k, N5k, N6k
Supported in OS Version 8.1.1 and later on N7k | + #### Parameters ##### `ensure` @@ -4513,6 +4528,14 @@ Creates a Virtual Network Identifier member (VNI) for an NVE overlay interface. | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +#### Caveats + +| Property | Caveat Description | +|---------------------------------|--------------------------------------| +| ingress_replication | Not supported on N3k, N5k, N6k
Supported in OS Version 8.1.1 and later on N7k | +| peer_list | Not supported on N3k, N5k, N6k
Supported in OS Version 8.1.1 and later on N7k | +| suppress_uuc | Not supported on N3k, N9k, N9k-F
Supported in OS Version 8.1.1 and later on N7k | + #### Parameters ##### `ensure` @@ -4539,6 +4562,9 @@ Set the ingress-replication static peer list. Valid values are an Array, a space ##### `suppress_arp` Suppress arp under layer 2 VNI. Valid values are true, false, or 'default'. +##### `suppress_uuc` +Suppress uuc under layer 2 VNI. Valid values are true, false, or 'default'. + -- ### NetDev StdLib Resource Type Details diff --git a/lib/facter/cisco.rb b/lib/facter/cisco.rb index 025fb7116..cc759fd26 100644 --- a/lib/facter/cisco.rb +++ b/lib/facter/cisco.rb @@ -13,11 +13,13 @@ hash['images'] = {} begin hash['images']['system_image'] = Platform.system_image + hash['images']['full_version'] = Platform.image_version rescue NameError # In more recent versions, Platform moved into the Cisco namespace. Platform = Cisco::Platform Feature = Cisco::Feature hash['images']['system_image'] = Platform.system_image + hash['images']['full_version'] = Platform.image_version end hash['images']['packages'] = Platform.packages diff --git a/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb b/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb index e9495129b..a4a19ad59 100644 --- a/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb +++ b/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb @@ -139,10 +139,13 @@ def unsupported_properties(_tests, _id) unprops << :startup_timer end + unprops +end - # TBD: this is due to nxos bug on n9k-f and n9k - unprops << :interval if platform[/n9k/] - +def version_unsupported_properties(_tests, _id) + unprops = {} + unprops[:interval] = '7.0.3.I6.1' if platform[/n9k$/] + unprops[:interval] = '7.0.3.F2.1' if platform[/n9k-f/] unprops end diff --git a/tests/beaker_tests/cisco_bgp/test_bgp.rb b/tests/beaker_tests/cisco_bgp/test_bgp.rb index 02d778840..9505d6654 100644 --- a/tests/beaker_tests/cisco_bgp/test_bgp.rb +++ b/tests/beaker_tests/cisco_bgp/test_bgp.rb @@ -228,7 +228,7 @@ def unsupported_properties(tests, id) unprops << :event_history_errors << - :event_history_objstore if nexus_image[/I2|I3|I4/] || platform[/n5|n6|n7|n9k-f/] + :event_history_objstore if platform[/n5|n6|n7|n9k-f/] if vrf != 'default' # NX-OS does not support these properties under a non-default vrf @@ -247,7 +247,7 @@ def unsupported_properties(tests, id) :suppress_fib_pending end - if platform[/n(5|6|7)k/] + if platform[/n(5|6)k/] unprops << :disable_policy_batching_ipv4 << :disable_policy_batching_ipv6 << @@ -258,6 +258,20 @@ def unsupported_properties(tests, id) unprops end +def version_unsupported_properties(_tests, _id) + unprops = {} + if platform[/n7k/] + unprops[:disable_policy_batching_ipv4] = '8.1.1' + unprops[:disable_policy_batching_ipv6] = '8.1.1' + unprops[:neighbor_down_fib_accelerate] = '8.1.1' + unprops[:reconnect_interval] = '8.1.1' + elsif platform[/n3k|n9k$/] + unprops[:event_history_errors] = '7.0.3.I5.1' + unprops[:event_history_objstore] = '7.0.3.I5.1' + end + unprops +end + def cleanup(agent) if operating_system == 'nexus' test_set(agent, 'no feature bgp') diff --git a/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb b/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb index 0421eca60..42e211b81 100644 --- a/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb +++ b/tests/beaker_tests/cisco_bgp_neighbor/test_bgpneighbor.rb @@ -130,12 +130,18 @@ def unsupported_properties(_tests, _id) :remove_private_as else - unprops << :log_neighbor_changes if platform[/n(5|6|7)/] + unprops << :log_neighbor_changes if platform[/n(5|6)/] end unprops end +def version_unsupported_properties(_tests, _id) + unprops = {} + unprops[:log_neighbor_changes] = '8.1.1' if platform[/n7k/] + unprops +end + def cleanup(agent) if operating_system == 'nexus' test_set(agent, 'no feature bgp ; no feature bfd') diff --git a/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb b/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb index 0d6953e1d..0536ce7de 100644 --- a/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb +++ b/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb @@ -140,13 +140,18 @@ def unsupported_properties(_tests, _id) :ipv4_sub_option_circuit_id_string elsif platform[/n9k/] unprops << - :ipv4_src_addr_hsrp << - :ipv4_sub_option_circuit_id_string + :ipv4_src_addr_hsrp end unprops << :ipv4_sub_option_circuit_id_custom if nexus_image['I2'] unprops end +def version_unsupported_properties(_tests, _id) + unprops = {} + unprops[:ipv4_sub_option_circuit_id_string] = '7.0.3.I6.1' if platform[/n9k$/] + unprops +end + def cleanup(agent) test_set(agent, 'no feature dhcp') end diff --git a/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb b/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb index e1aeb9f4e..fa4e4043c 100755 --- a/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb +++ b/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb @@ -26,7 +26,7 @@ tests = { agent: agent, master: master, - platform: 'n(5|6|7|9)k', + platform: 'n(3|5|6|7|9)k', resource_name: 'cisco_overlay_global', ensurable: false, } @@ -73,6 +73,26 @@ }, } +def unsupported_properties(_tests, _id) + unprops = [] + if platform[/n3k/] + unprops << + :anycast_gateway_mac << + :dup_host_ip_addr_detection_host_moves << + :dup_host_ip_addr_detection_timeout + end + unprops +end + +def version_unsupported_properties(_tests, _id) + unprops = {} + if platform[/n3k/] + unprops[:dup_host_mac_detection_host_moves] = '7.0.3.I6.1' + unprops[:dup_host_mac_detection_timeout] = '7.0.3.I6.1' + end + unprops +end + def cleanup(agent) config_find_remove(agent, 'nv overlay evpn', 'incl ^nv') end diff --git a/tests/beaker_tests/cisco_route_map/test_route_map.rb b/tests/beaker_tests/cisco_route_map/test_route_map.rb index 7ff8abdbb..3f454dc25 100644 --- a/tests/beaker_tests/cisco_route_map/test_route_map.rb +++ b/tests/beaker_tests/cisco_route_map/test_route_map.rb @@ -407,7 +407,6 @@ } def unsupp_n3k - im = nexus_image unprops = [] unprops << :match_evpn_route_type_1 << @@ -423,10 +422,6 @@ def unsupp_n3k :match_mac_list << :match_vlan << :set_vrf - unprops << - :match_ospf_area << - :set_ipv4_next_hop_redist << - :set_ipv6_next_hop_redist if im[/(I2|I3|I4)/] unprops end @@ -475,12 +470,6 @@ def unsupp_n9k :set_ipv6_default_next_hop_load_share << :set_extcommunity_rt_asn << :set_vrf - unprops << - :set_ipv4_next_hop_load_share << - :set_ipv6_next_hop_load_share << - :match_ospf_area << - :set_ipv4_next_hop_redist << - :set_ipv6_next_hop_redist if im[/(I2|I3|I4)/] unprops << :match_metric if im['I4'] unprops end @@ -499,12 +488,8 @@ def unsupp_n9kf :match_evpn_route_type_all << :match_length << :match_mac_list << - :match_metric << :match_ospf_area << :match_vlan << - :set_extcommunity_4bytes_additive << - :set_extcommunity_4bytes_non_transitive << - :set_extcommunity_4bytes_transitive << :set_extcommunity_cost_igp << :set_extcommunity_cost_pre_bestpath << :set_extcommunity_rt_additive << @@ -515,11 +500,9 @@ def unsupp_n9kf :set_ipv6_default_next_hop << :set_ipv6_default_next_hop_load_share << :set_ipv4_next_hop << - :set_ipv4_next_hop_load_share << :set_ipv4_precedence << :set_ipv4_prefix << :set_ipv6_next_hop << - :set_ipv6_next_hop_load_share << :set_ipv6_prefix << :set_vrf unprops @@ -539,6 +522,30 @@ def unsupported_properties(_tests, _id) end end +def version_unsupported_properties(_tests, _id) + unprops = {} + if platform[/n9k-f/] + unprops[:match_metric] = '7.0.3.F2.1' + unprops[:set_extcommunity_4bytes_additive] = '7.0.3.F2.1' + unprops[:set_extcommunity_4bytes_non_transitive] = '7.0.3.F2.1' + unprops[:set_extcommunity_4bytes_transitive] = '7.0.3.F2.1' + unprops[:set_ipv4_next_hop_load_share] = '7.0.3.F2.1' + unprops[:set_ipv6_next_hop_load_share] = '7.0.3.F2.1' + elsif platform[/n9k$/] + unprops[:match_ospf_area] = '7.0.3.I5.1' + unprops[:set_ipv4_next_hop_load_share] = '7.0.3.I5.1' + unprops[:set_ipv6_next_hop_load_share] = '7.0.3.I5.1' + unprops[:set_ipv4_next_hop_redist] = '7.0.3.I5.1' + unprops[:set_ipv6_next_hop_redist] = '7.0.3.I5.1' + unprops[:set_community] = '7.0.3.I5.1' + elsif platform[/n3k/] + unprops[:match_ospf_area] = '7.0.3.I5.1' + unprops[:set_ipv4_next_hop_redist] = '7.0.3.I5.1' + unprops[:set_ipv6_next_hop_redist] = '7.0.3.I5.1' + end + unprops +end + def cleanup(agent) resource_absent_cleanup(agent, 'cisco_route_map') end diff --git a/tests/beaker_tests/cisco_stp_global/test_stp_global.rb b/tests/beaker_tests/cisco_stp_global/test_stp_global.rb index aadaa2cfe..7ecefbe97 100644 --- a/tests/beaker_tests/cisco_stp_global/test_stp_global.rb +++ b/tests/beaker_tests/cisco_stp_global/test_stp_global.rb @@ -41,6 +41,8 @@ bpdufilter: 'default', bpduguard: 'default', bridge_assurance: 'default', + domain: 'default', + fcoe: 'default', loopguard: 'default', mode: 'default', pathcost: 'default', @@ -56,6 +58,8 @@ 'bpdufilter' => 'false', 'bpduguard' => 'false', 'bridge_assurance' => 'true', + 'domain' => 'false', + 'fcoe' => 'true', 'loopguard' => 'false', 'mode' => 'rapid-pvst', 'pathcost' => 'short', @@ -117,6 +121,8 @@ bpdufilter: 'true', bpduguard: 'true', bridge_assurance: 'false', + domain: '100', + fcoe: 'true', loopguard: 'true', mode: 'mst', mst_designated_priority: mst_dp, @@ -151,50 +157,6 @@ }, } -tests[:default_plat_1] = { - desc: '1.4 Default Properties platform specific part 1', - platform: 'n9k', - title_pattern: 'default', - manifest_props: { - fcoe: 'default' - }, - code: [0, 2], - resource: { - 'fcoe' => 'true' - }, -} - -tests[:non_default_plat_1] = { - desc: '2.3 Non Default Properties platform specific part 1', - platform: 'n9k', - title_pattern: 'default', - manifest_props: { - fcoe: 'false' - }, -} - -tests[:default_plat_2] = { - desc: '1.4 Default Properties platform specific part 2', - platform: 'n(5|6|7)k', - title_pattern: 'default', - manifest_props: { - domain: 'default' - }, - code: [0, 2], - resource: { - 'domain' => 'false' - }, -} - -tests[:non_default_plat_2] = { - desc: '2.3 Non Default Properties platform specific part 2', - platform: 'n(5|6|7)k', - title_pattern: 'default', - manifest_props: { - domain: '100' - }, -} - tests[:default_bd] = { desc: '1.2 bridge-domain Default Properties platform specific', platform: 'n7k', @@ -258,6 +220,20 @@ def test_harness_dependencies(_tests, id) end end +def unsupported_properties(_tests, _id) + unprops = [] + unprops << :domain if platform[/n9k-f/] + unprops << :fcoe if platform[/n(3|5|6|7)k/] + unprops +end + +def version_unsupported_properties(_tests, _id) + unprops = {} + unprops[:domain] = '7.0.3.I6.1' if platform[/n3k/] + unprops[:domain] = '7.0.3.I6.1' if platform[/n9k$/] + unprops +end + ################################################################# # TEST CASE EXECUTION ################################################################# @@ -270,16 +246,12 @@ def test_harness_dependencies(_tests, id) test_harness_run(tests, :default) test_harness_run(tests, :default_bd) test_harness_run(tests, :default_mst) - test_harness_run(tests, :default_plat_1) - test_harness_run(tests, :default_plat_2) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") test_harness_run(tests, :non_default) test_harness_run(tests, :non_default_bd) - test_harness_run(tests, :non_default_plat_1) - test_harness_run(tests, :non_default_plat_2) # ------------------------------------------------------------------- skipped_tests_summary(tests) diff --git a/tests/beaker_tests/cisco_vpc_domain/test_vpc_domain.rb b/tests/beaker_tests/cisco_vpc_domain/test_vpc_domain.rb index 9505a04e4..d1fc99874 100644 --- a/tests/beaker_tests/cisco_vpc_domain/test_vpc_domain.rb +++ b/tests/beaker_tests/cisco_vpc_domain/test_vpc_domain.rb @@ -43,8 +43,10 @@ delay_restore: 'default', delay_restore_interface_vlan: 'default', graceful_consistency_check: 'default', + layer3_peer_routing: 'default', peer_gateway: 'default', role_priority: 'default', + shutdown: 'default', system_priority: 'default', }, @@ -53,8 +55,10 @@ 'delay_restore' => '30', 'delay_restore_interface_vlan' => '10', 'graceful_consistency_check' => 'true', + 'layer3_peer_routing' => 'false', 'peer_gateway' => 'false', 'role_priority' => '32667', + 'shutdown' => 'false', 'system_priority' => '32667', }, } @@ -68,6 +72,7 @@ delay_restore_interface_vlan: '300', dual_active_exclude_interface_vlan_bridge_domain: '10-30, 500', graceful_consistency_check: 'true', + layer3_peer_routing: 'true', peer_keepalive_dest: '1.1.1.1', peer_keepalive_hold_timeout: 5, peer_keepalive_interval: 1000, @@ -78,6 +83,7 @@ peer_keepalive_vrf: 'management', peer_gateway: 'true', role_priority: '1024', + shutdown: 'true', system_mac: '00:0c:0d:11:22:33', system_priority: '3000', @@ -89,6 +95,7 @@ 'delay_restore_interface_vlan' => '300', 'dual_active_exclude_interface_vlan_bridge_domain' => '10-30,500', 'graceful_consistency_check' => 'true', + 'layer3_peer_routing' => 'true', 'peer_keepalive_dest' => '1.1.1.1', 'peer_keepalive_hold_timeout' => '5', 'peer_keepalive_interval' => '1000', @@ -99,41 +106,23 @@ 'peer_keepalive_vrf' => 'management', 'peer_gateway' => 'true', 'role_priority' => '1024', + 'shutdown' => 'true', 'system_mac' => '00:0c:0d:11:22:33', 'system_priority' => '3000', }, } -tests[:default_properties_n6k7k] = { - desc: '1.2 Default Properties exclusive to N6K and N7K', - title_pattern: '200', - platform: 'n(6|7)k', - manifest_props: { - layer3_peer_routing: 'default', - shutdown: 'default', - }, - code: [0, 2], - resource: { - 'layer3_peer_routing' => 'false', - 'shutdown' => 'false', - }, -} - tests[:non_default_properties_n6k7k] = { desc: '2.2 Non Default Properties exclusive to N6K and N7K', title_pattern: '200', platform: 'n(6|7)k', manifest_props: { - layer3_peer_routing: 'true', - peer_gateway_exclude_vlan: '500-510, 1100, 1120', - shutdown: 'true', + peer_gateway_exclude_vlan: '500-510, 1100, 1120' }, code: [0, 2], resource: { - 'layer3_peer_routing' => 'true', - 'peer_gateway_exclude_vlan' => '500-510,1100,1120', - 'shutdown' => 'true', + 'peer_gateway_exclude_vlan' => '500-510,1100,1120' }, } @@ -184,6 +173,13 @@ }, } +def version_unsupported_properties(_tests, _id) + unprops = {} + unprops[:layer3_peer_routing] = '7.0.3.I6.1' if platform[/n(3|9)k$/] + unprops[:shutdown] = '7.0.3.I6.1' if platform[/n(3|9)k$/] + unprops +end + def cleanup(agent) remove_all_vlans(agent) resource_absent_cleanup(agent, 'cisco_vpc_domain') @@ -203,7 +199,6 @@ def cleanup(agent) logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") test_harness_run(tests, :default_properties) - test_harness_run(tests, :default_properties_n6k7k) test_harness_run(tests, :default_properties_n7k) # Resource absent test diff --git a/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb b/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb index 2f24ba57e..6e5ae5a08 100644 --- a/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb +++ b/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb @@ -64,7 +64,13 @@ def unsupported_properties(*) unprops = [] - unprops << :source_interface_hold_down_time unless platform[/n(9)k/] + unprops << :source_interface_hold_down_time if platform[/n(5|6)k/] + unprops +end + +def version_unsupported_properties(_tests, _id) + unprops = {} + unprops[:source_interface_hold_down_time] = '8.1.1' if platform[/n7k/] unprops end diff --git a/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb b/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb index 330667432..47912301c 100644 --- a/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb +++ b/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb @@ -55,20 +55,7 @@ require File.expand_path('../../lib/utilitylib.rb', __FILE__) -# ----------------------------- -# Common settings and variables -# ----------------------------- -testheader = 'Resource cisco_vxlan_vtep_vni' - -# The 'tests' hash is used to define all of the test data values and expected -# results. It is also used to pass optional flags to the test methods when -# necessary. - -# 'tests' hash -# Top-level keys set by caller: -# tests[:master] - the master object -# tests[:agent] - the agent object -# +# Test hash top-level keys tests = { master: master, agent: agent, @@ -79,261 +66,206 @@ # Skip -ALL- tests if a top-level platform/os key exludes this platform skip_unless_supported(tests) -# tests[id] keys set by caller and used by test_harness_common: -# -# tests[id] keys set by caller: -# tests[id][:desc] - a string to use with logs & debugs -# tests[id][:manifest] - the complete manifest, as used by test_harness_common -# tests[id][:resource] - a hash of expected states, used by test_resource -# tests[id][:resource_cmd] - 'puppet resource' command to use with test_resource -# tests[id][:ensure] - (Optional) set to :present or :absent before calling -# tests[id][:code] - (Optional) override the default exit code in some tests. -# -# These keys are local use only and not used by test_harness_common: -# -# tests[id][:manifest_props] - This is essentially a master list of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the list -# tests[id][:resource_props] - This is essentially a master hash of properties -# that permits re-use of the properties for both :present and :absent testing -# without destroying the hash -# tests[id][:title_pattern] - (Optional) defines the manifest title. -# Can be used with :af for mixed title/af testing. If mixing, :af values will -# be merged with title values and override any duplicates. If omitted, -# :title_pattern will be set to 'id'. -# tests[id][:af] - (Optional) defines the address-family values. -# Must use :title_pattern if :af is not specified. Useful for testing mixed -# title/af manifests -# -tests['default_properties_ingress_replication'] = { +# Test hash test cases +tests[:default_properties_ingress_replication] = { + desc: '1.1 Default Properties Ingress replication', title_pattern: 'nve1 10000', - platform: 'n9k', - manifest_props: " - ingress_replication => 'default', - suppress_arp => 'default', - ", - resource_props: { - # 'ingress_replication' not set. - 'suppress_arp' => 'false' + platform: 'n(7|9)k', + manifest_props: { + ingress_replication: 'default', + suppress_arp: 'default', + }, + code: [0, 2], + resource: { + suppress_arp: 'false' }, } -tests['default_properties_multicast_group'] = { +tests[:default_properties_multicast_group] = { + desc: '1.2 Default Properties Multicast Group', title_pattern: 'nve1 10000', - manifest_props: " - multicast_group => 'default', - suppress_arp => 'default', - ", - resource_props: { - # 'multicast_group' not set. - 'suppress_arp' => 'false' + manifest_props: { + multicast_group: 'default', + suppress_arp: 'default', + suppress_uuc: 'default', + }, + code: [0, 2], + resource: { + suppress_arp: 'false', + suppress_uuc: 'false', }, } -# Suppress Unknown Unicast -if platform[/n(5|6)k/] - tests['default_properties_multicast_group'][:manifest_props] += - " suppress_uuc => 'default'," - tests['default_properties_multicast_group'][:resource_props]['suppress_uuc'] = 'false' -end - -tests['ingress_replication_static_peer_list_empty'] = { +tests[:ingress_replication_static_peer_list_empty] = { + desc: '2.1 Ingress Replication Static Peer List Empty', title_pattern: 'nve1 10000', - platform: 'n9k', - manifest_props: " - ingress_replication => 'static', - peer_list => [], - suppress_arp => 'default' - ", - resource_props: { - 'ingress_replication' => 'static', - # 'peer_list' not set. - 'suppress_arp' => 'false', + platform: 'n(7|9)k', + manifest_props: { + ingress_replication: 'static', + peer_list: [], + suppress_arp: 'default', + }, + code: [0, 2], + resource: { + suppress_arp: 'false' }, } -tests['peer_list'] = { +tests[:peer_list] = { + desc: '2.2 Peer List', title_pattern: 'nve1 10000', - platform: 'n9k', - manifest_props: " - ingress_replication => 'static', - peer_list => ['1.1.1.1', '2.2.2.2', '3.3.3.3'], - suppress_arp => 'default' - ", - resource_props: { - 'ingress_replication' => 'static', - 'peer_list' => "['1.1.1.1', '2.2.2.2', '3.3.3.3']", - 'suppress_arp' => 'false', + platform: 'n(7|9)k', + manifest_props: { + ingress_replication: 'static', + peer_list: ['1.1.1.1', '2.2.2.2', '3.3.3.3'], + suppress_arp: 'default', + }, + code: [0, 2], + resource: { + suppress_arp: 'false' }, } -tests['peer_list_change_add'] = { +tests[:peer_list_change_add] = { + desc: '2.3 Peer List Change Add', title_pattern: 'nve1 10000', - platform: 'n9k', - manifest_props: " - ingress_replication => 'static', - peer_list => ['1.1.1.1', '6.6.6.6', '3.3.3.3', '4.4.4.4'], - suppress_arp => 'default' - ", - resource_props: { - 'ingress_replication' => 'static', - 'peer_list' => "['1.1.1.1', '3.3.3.3', '4.4.4.4', '6.6.6.6']", - 'suppress_arp' => 'false', + platform: 'n(7|9)k', + manifest_props: { + ingress_replication: 'static', + peer_list: ['1.1.1.1', '6.6.6.6', '3.3.3.3', '4.4.4.4'], + suppress_arp: 'default', + }, + code: [0, 2], + resource: { + suppress_arp: 'false' }, } -tests['peer_list_default'] = { +tests[:peer_list_default] = { + desc: '2.4 Peer List Default', title_pattern: 'nve1 10000', - platform: 'n9k', - manifest_props: " - ingress_replication => 'static', - peer_list => 'default', - suppress_arp => 'default' - ", - resource_props: { - 'ingress_replication' => 'static', - # 'peer_list' not set. - 'suppress_arp' => 'false', + platform: 'n(7|9)k', + manifest_props: { + ingress_replication: 'static', + peer_list: 'default', + suppress_arp: 'default', + }, + code: [0, 2], + resource: { + suppress_arp: 'false' }, } -tests['ingress_replication_bgp'] = { +tests[:ingress_replication_bgp] = { + desc: '2.5 Ingress replication BGP', title_pattern: 'nve1 10000', - platform: 'n9k', - manifest_props: " - ingress_replication => 'bgp', - suppress_arp => 'default' - ", - resource_props: { - 'ingress_replication' => 'bgp', - 'suppress_arp' => 'false', + platform: 'n(7|9)k', + manifest_props: { + ingress_replication: 'bgp', + suppress_arp: 'default', + }, + code: [0, 2], + resource: { + suppress_arp: 'false' }, } -tests['multicast_group'] = { +tests[:multicast_group] = { + desc: '2.6 Multicast Group', title_pattern: 'nve1 10000', - manifest_props: " - multicast_group => '224.1.1.1', - suppress_arp => 'default' - ", - resource_props: { - 'multicast_group' => '224.1.1.1', - 'suppress_arp' => 'false', + manifest_props: { + multicast_group: '224.1.1.1', + suppress_arp: 'default', + }, + code: [0, 2], + resource: { + suppress_arp: 'false' }, } -tests['suppress_arp_true'] = { +tests[:suppress_arp_true] = { + desc: '2.7 Suppress ARP True', title_pattern: 'nve1 10000', - manifest_props: " - suppress_arp => 'true' - ", - resource_props: { - 'suppress_arp' => 'true' + manifest_props: { + suppress_arp: 'true' }, + code: [0, 2], } -tests['suppress_arp_false'] = { +tests[:suppress_arp_false] = { + desc: '2.8 Suppress ARP False', title_pattern: 'nve1 10000', - manifest_props: " - suppress_arp => 'false' - ", - resource_props: { - 'suppress_arp' => 'false' + manifest_props: { + suppress_arp: 'false' }, + code: [0, 2], } -tests['suppress_uuc_true'] = { +tests[:suppress_uuc_true] = { + desc: '2.9 Suppress UUC True', title_pattern: 'nve1 10000', - platform: 'n(5|6)k', - manifest_props: " - suppress_uuc => 'true' - ", - resource_props: { - 'suppress_uuc' => 'true' + manifest_props: { + suppress_uuc: 'true' }, + code: [0, 2], } -tests['suppress_uuc_false'] = { +tests[:suppress_uuc_false] = { + desc: '2.10 Suppress UUC False', title_pattern: 'nve1 10000', - platform: 'n(5|6)k', - manifest_props: " - suppress_uuc => 'false' - ", - resource_props: { - 'suppress_uuc' => 'false' + manifest_props: { + suppress_uuc: 'false' }, + code: [0, 2], } # Note: In the current implementation assciate-vrf is a namevar. # It will be changed to a property in future. A correspoding test # will be added here. -################################################################# -# HELPER FUNCTIONS -################################################################# - -# Full command string for puppet resource command -def puppet_resource_cmd - PUPPET_BINPATH + 'resource cisco_vxlan_vtep_vni' -end - def test_harness_dependencies(*) return unless platform[/n(5|6)k/] skip_if_nv_overlay_rejected(agent) end -def build_manifest_cisco_vxlan_vtep_vni(tests, id) - if tests[id][:ensure] == :absent - state = 'ensure => absent,' - tests[id][:resource] = {} - else - state = 'ensure => present,' - manifest = tests[id][:manifest_props] - tests[id][:resource] = tests[id][:resource_props] - end - - tests[id][:title_pattern] = id if tests[id][:title_pattern].nil? - logger.debug("build_manifest_cisco_vxlan_vtep_vni :: title_pattern:\n" + - tests[id][:title_pattern]) - - # cisco_vxlan_vtep_vni needs cisco_vxlan_vtep as a prerequisite. - tests[id][:manifest] = "cat <#{PUPPETMASTER_MANIFESTPATH} - node 'default' { +# Overridden to properly handle dependencies for this test file. +def dependency_manifest(_tests, _id) + " cisco_vxlan_vtep {'nve1': ensure => present, host_reachability => 'evpn', shutdown => 'false', } - cisco_vxlan_vtep_vni { '#{tests[id][:title_pattern]}': - #{state} - #{manifest} - } - } -EOF" + " end -def test_harness_cisco_vxlan_vtep_vni(tests, id) - return unless platform_supports_test(tests, id) - - test_harness_dependencies(tests, id) - - tests[id][:ensure] = :present if tests[id][:ensure].nil? - tests[id][:resource_cmd] = puppet_resource_cmd - tests[id][:desc] += " [ensure => #{tests[id][:ensure]}]" - - build_manifest_cisco_vxlan_vtep_vni(tests, id) - - test_manifest(tests, id) - test_resource(tests, id) - test_idempotence(tests, id) +def unsupported_properties(_tests, _id) + unprops = [] + if platform[/n(5|6)k/] + unprops << + :ingress_replication << + :peer_list + elsif platform[/n9k/] + unprops << + :suppress_uuc + end + unprops +end - tests[id][:ensure] = nil +def version_unsupported_properties(_tests, _id) + unprops = {} + if platform[/n7k/] + unprops[:ingress_replication] = '8.1.1' + unprops[:peer_list] = '8.1.1' + unprops[:suppress_uuc] = '8.1.1' + end + unprops end ################################################################# # TEST CASE EXECUTION ################################################################# -test_name "TestCase :: #{testheader}" do +test_name "TestCase :: #{tests[:resource_name]}" do teardown do resource_absent_cleanup(agent, 'cisco_vxlan_vtep_vni') vdc_limit_f3_no_intf_needed(:clear) @@ -343,69 +275,38 @@ def test_harness_cisco_vxlan_vtep_vni(tests, id) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") - id = 'default_properties_ingress_replication' - tests[id][:desc] = '1.1 Default Properties Ingress Replication' - test_harness_cisco_vxlan_vtep_vni(tests, id) - - tests[id][:desc] = '1.2 Default Properties Ingress Replication' + id = :default_properties_ingress_replication + test_harness_run(tests, id) tests[id][:ensure] = :absent - test_harness_cisco_vxlan_vtep_vni(tests, id) - - id = 'default_properties_multicast_group' - tests[id][:desc] = '1.3 Default Properties Multicast Group' - test_harness_cisco_vxlan_vtep_vni(tests, id) - - tests[id][:desc] = '1.4 Default Properties Multicast Group' + test_harness_run(tests, id) + id = :default_properties_multicast_group + test_harness_run(tests, id) tests[id][:ensure] = :absent - test_harness_cisco_vxlan_vtep_vni(tests, id) + test_harness_run(tests, id) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") - id = 'ingress_replication_static_peer_list_empty' - tests[id][:desc] = '2.1 Ingress Replication Static' - test_harness_cisco_vxlan_vtep_vni(tests, id) - - id = 'peer_list' - tests[id][:desc] = '2.2 Peer List' - test_harness_cisco_vxlan_vtep_vni(tests, id) - - id = 'peer_list_change_add' - tests[id][:desc] = '2.3 Peer List Change Add' - test_harness_cisco_vxlan_vtep_vni(tests, id) - - id = 'peer_list_default' - tests[id][:desc] = '2.4 Peer List Default' - test_harness_cisco_vxlan_vtep_vni(tests, id) - - id = 'ingress_replication_bgp' - tests[id][:desc] = '2.5 Ingress Replication BGP' - test_harness_cisco_vxlan_vtep_vni(tests, id) - - id = 'multicast_group' - tests[id][:desc] = '2.6 Multicast Group' - test_harness_cisco_vxlan_vtep_vni(tests, id) + test_harness_run(tests, :ingress_replication_static_peer_list_empty) + test_harness_run(tests, :peer_list) + test_harness_run(tests, :peer_list_change_add) + test_harness_run(tests, :peer_list_default) + test_harness_run(tests, :ingress_replication_bgp) + test_harness_run(tests, :multicast_group) # TBD - The suppress_arp tests will generate the following error. # ERROR: Please configure TCAM region... Configuring the TCAM region # requires a switch reboot. These tests will remain commented out # until we can design a solution. - # id = 'suppress_arp_true' - # tests[id][:desc] = '2.7 Suppress ARP' - # test_harness_cisco_vxlan_vtep_vni(tests, id) + # test_harness_run(tests, :suppress_arp_true) + # test_harness_run(tests, :suppress_arp_false) - # id = 'suppress_arp_false' - # tests[id][:desc] = '2.8 Suppress ARP' - # test_harness_cisco_vxlan_vtep_vni(tests, id) + test_harness_run(tests, :suppress_uuc_true) + test_harness_run(tests, :suppress_uuc_false) - id = 'suppress_uuc_true' - tests[id][:desc] = '2.9 Suppress Unknown Unicast' - test_harness_cisco_vxlan_vtep_vni(tests, id) - - id = 'suppress_uuc_false' - tests[id][:desc] = '2.10 Suppress Unknown Unicast' - test_harness_cisco_vxlan_vtep_vni(tests, id) + # ------------------------------------------------------------------- + skipped_tests_summary(tests) end -logger.info('TestCase :: # {testheader} :: End') +logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 957580828..49e25a0f2 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -685,6 +685,21 @@ def unsupported_properties(_tests, _id) [] # defaults to no unsupported properties end +# version_unsupported_properties +# +# Returns an array of properties that are not supported for +# a particular operating_system or platform for a particular +# software version. +# Override this in a particular test file as needed. +# Ex: If property 'ipv4_sub_option_circuit_id_string' is +# supported on n9k only on version '7.0.3.I6.1' or higher +# then add this line in the overridden method. +# unprops[:ipv4_sub_option_circuit_id_string] = '7.0.3.I6.1' if +# platform[/n9k$/] +def version_unsupported_properties(_tests, _id) + {} # defaults to no version_unsupported properties +end + # supported_property_hash # # This method creates a clone of the specified property @@ -699,6 +714,25 @@ def supported_property_hash(tests, id, property_hash) # because :resource hash currently uses strings for keys copy.delete(prop_symbol.to_s) end + return copy if version_unsupported_properties(tests, id).empty? + lim = full_version.split[0].tr('(', '.').tr(')', '.').chomp('.') + # due to a bug in Gem::Version, we need to append a letter + # to the version field if the to be compared version + # has a letter at the end + # For ex: + # 7.0.3.I2.2e < 7.0.3.I2.2 is TRUE instead of FALSE + # Once we add a letter 'a' to the end, + # 7.0.3.I2.2e < 7.0.3.I2.2a is FALSE + append_a = false + append_a = true if lim[-1, 1] =~ /[[:alpha:]]/ + version_unsupported_properties(tests, id).each do |key, val| + val << 'a' if append_a + append_a = false + next unless Gem::Version.new(lim) < Gem::Version.new(val) + copy.delete(key) + # because :resource hash currently uses strings for keys + copy.delete(key.to_s) + end copy end @@ -1119,6 +1153,14 @@ def image_version @version ||= data end +# Gets the full version of the image running on a device +@full_ver = nil +def full_version + facter_opt = '-p cisco.images.full_version' + data = on(agent, facter_cmd(facter_opt)).stdout.chomp + @full_ver ||= data +end + # On match will skip all testcases # Do not use this for skipping individual properties. def skip_nexus_image(image, tests) From c8288d9e54e04d9a1b5c2fd2b52f729ca80c311b Mon Sep 17 00:00:00 2001 From: Mike Wiebe Date: Thu, 11 May 2017 13:38:05 -0400 Subject: [PATCH 165/203] Update release checklist (#455) * Update release checklist * Fix CHANGELOG link. * Fixed typo. --- docs/README-maintainers.md | 153 ++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 87 deletions(-) diff --git a/docs/README-maintainers.md b/docs/README-maintainers.md index 747905768..39b5b8cbe 100644 --- a/docs/README-maintainers.md +++ b/docs/README-maintainers.md @@ -14,94 +14,73 @@ Guidelines for the core maintainers of the ciscopuppet project - above and beyon * Is the data still relevant? * Do the version dependencies need to be updated? (e.g. rubocop) -## Setting up git-flow +## Release Process Checklist -If you don't already have [`git-flow`](https://github.com/petervanderdoes/gitflow/) installed, install it. - -Either run `git flow init` from the repository root directory, or manually edit your `.git/config` file. Either way, when done, you should have the following in your config: - -```ini -[gitflow "branch"] - master = master - develop = develop -[gitflow "prefix"] - feature = feature/ - release = release/ - hotfix = hotfix/ - support = support/ - versiontag = v -``` - -Most of these are default for git-flow except for the `versiontag` setting. - -## Release Checklist +### Pre-Merge to `master` branch: When we are considering publishing a new release, all of the following steps must be carried out (using the latest code base in `develop`): -1. With the latest released `cisco_node_utils` gem (no development code! -released only! Do a new gem release first if needed!), run Beaker tests and -demo manifests against all supported platforms running latest OS release or -release candidate: - * N30xx - * N31xx - * N9xxx - -2. Triage any test failures. - -3. Make sure CHANGELOG.md accurately reflects all changes since the last release. - * Add any significant changes that weren't documented in the changelog - * Clean up any entries that are overly verbose, unclear, or otherwise could be improved. - -## Release Process - -When the release checklist above has been fully completed, the process for publishing a new release is as follows: - - -1. Ensure that tests have been executed against released Gem versions (release a new version if necessary!) and do not have dependencies on unreleased Gem code. - -2. Create a release branch. Follow [semantic versioning](http://semver.org): - * 0.0.x - a bugfix release - * 0.x.0 - new feature(s) - * x.0.0 - backward-incompatible change (only if unavoidable!) - - ``` - git flow release start 1.0.1 - ``` - -3. In the newly created release branch, update `CHANGELOG.md`: - - ```diff - -## [Unreleased] - +## [1.0.1] - 2015-08-28 - ... - +[1.0.1]: https://github.com/cisco/cisco-network-puppet-module/compare/v1.0.0...v1.0.1 - [1.0.0]: https://github.com/cisco/cisco-network-puppet-module/compare/v0.9.0...v1.0.0 - ``` - - and also update `metadata.json`: - - ```diff - "name": "puppetlabs-ciscopuppet", - - "version": "1.0.0", - + "version": "1.0.1", - "author": "cisco", - ``` - -4. Commit your changes and push the release branch to GitHub for review by Cisco and PuppetLabs: - - ``` - git flow release publish 1.0.1 - ``` - -5. Once Cisco and PuppetLabs are in agreement that the release branch is sane, finish the release and push the finished release to GitHub: - - ``` - git flow release finish 1.0.1 - git push origin master - git push origin develop - git push --tags - ``` - -6. Add release notes on GitHub, for example `https://github.com/cisco/cisco-network-puppet-module/releases/new?tag=v1.0.1`. Usually this will just be a copy-and-paste of the relevant section of the `CHANGELOG.md`. - -7. Reach out to PuppetLabs to publish the new module version to PuppetForge. +1. Pull release branch based on the `develop` branch. + * 0.0.x - a bugfix release + * 0.x.0 - new feature(s) + * x.0.0 - backward-incompatible change (if unvoidable!) + +1. Run full beaker test regression on [supported platforms.](https://github.com/cisco/cisco-network-puppet-module#resource-platform-support-matrix) + * Fix All Bugs. + * Make sure proper test case skips are in place for unsupported platforms. + * Ensure that tests have been executed against released Gem versions (release a new version if necessary!) and do not have dependencies on unreleased Gem code. + + +1. Update [changelog.](https://github.com/cisco/cisco-network-puppet-module/blob/develop/CHANGELOG.md) + * Make sure CHANGELOG.md accurately reflects all changes since the last release. + * Add any significant changes that weren't documented in the changelog + * Clean up any entries that are overly verbose, unclear, or otherwise could be improved. + * Indicate new platform support (if any) for exisiting providers. + * Create markdown release tag. + ```diff + -## [Unreleased] + +## [1.0.1] - 2015-08-28 + ... + +[1.0.1]: https://github.com/cisco/cisco-network-puppet-module/compare/v1.0.0...v1.0.1 + [1.0.0]: https://github.com/cisco/cisco-network-puppet-module/compare/v0.9.0...v1.0.0 + ``` + +1. Update [metadata.json](https://github.com/cisco/cisco-network-puppet-module/blob/develop/metadata.json) file. + * Update Version + ```diff + "name": "puppetlabs-ciscopuppet", + - "version": "1.0.0", + + "version": "1.0.1", + "author": "cisco", + ``` + * Update Supported OS Verions (if applicable) + +1. Update [`cisco_node_utils.rb` rec_version = Gem::Version.new('x.x.x')](https://github.com/cisco/cisco-network-puppet-module/blob/develop/lib/puppet/feature/cisco_node_utils.rb#L40) version. + +1. Verify/Update [netdev_stdlib version](https://github.com/cisco/cisco-network-puppet-module/blob/develop/metadata.json#L11) requirement if needed. + +1. Verify puppet module can be built using the [new puppet module version](https://github.com/cisco/cisco-network-puppet-module/blob/develop/metadata.json#L3). + +1. Scrub README Docs. + * Update references to indicate new platorm support where applicable. + * Update nxos release information where applicable. + * Update caveats for any new properties added in to existing/new providers. + +1. Open pull request from release branch against the `master` branch. + * Merge after approval. + +1. Reach out to PuppetLabs and ask them to verify new release branches (Puppet and NodeUtils) against their CI. + * **Important** Only after getting approval from puppet, move on to post-merge steps. + +### Post-Merge to `master` branch: + +1. Create annotated git tag for the release. + * [HowTo](https://git-scm.com/book/en/v2/Git-Basics-Tagging#Annotated-Tags) + +1. Draft a [new release](https://github.com/cisco/cisco-network-puppet-module/releases) on github. + +1. Merge `master` branch back into `develop` branch. + * Resolve any merge conflicts + * Optional: Delete release branch (May want to keep for reference) + +1. Reach out to PuppetLabs to publish the new module version to PuppetForge. From a7b151cb9f0bdc053cb46e6626991255deb98216 Mon Sep 17 00:00:00 2001 From: saichint Date: Thu, 11 May 2017 11:00:31 -0700 Subject: [PATCH 166/203] Bgp address family aggregate address (#454) * provider code for bgp af aggregate addr * comment cleanup * doc * review comments --- CHANGELOG.md | 2 + README.md | 55 ++++++ examples/cisco/demo_bgp.pp | 13 ++ lib/puppet/provider/cisco_bgp_af_aa/cisco.rb | 185 ++++++++++++++++++ lib/puppet/type/cisco_bgp_af_aa.rb | 153 +++++++++++++++ .../cisco_bgp_af_aa/test_bgp_af_aa.rb | 114 +++++++++++ 6 files changed, 522 insertions(+) create mode 100644 lib/puppet/provider/cisco_bgp_af_aa/cisco.rb create mode 100644 lib/puppet/type/cisco_bgp_af_aa.rb create mode 100644 tests/beaker_tests/cisco_bgp_af_aa/test_bgp_af_aa.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 141fcaca0..7196e79d4 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### New feature support +#### Cisco Resources +- `cisco_bgp_af_aa` type and provider. ### Added - Extend cisco_interface with attributes: diff --git a/README.md b/README.md index 1578f5c89..f49327017 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,7 @@ The following resources include cisco types and providers along with cisco provi * [`cisco_vrf_af`](#type-cisco_vrf_af) * [`cisco_bgp`](#type-cisco_bgp) * [`cisco_bgp_af`](#type-cisco_bgp_af) + * [`cisco_bgp_af_aa`](#type-cisco_bgp_af_aa) * [`cisco_bgp_neighbor`](#type-cisco_bgp_neighbor) * [`cisco_bgp_neighbor_af`](#type-cisco_bgp_neighbor_af) @@ -299,6 +300,7 @@ The following resources include cisco types and providers along with cisco provi * [`cisco_bfd_global`](#type-cisco_bfd_global) * [`cisco_bgp`](#type-cisco_bgp) * [`cisco_bgp_af`](#type-cisco_bgp_af) +* [`cisco_bgp_af_aa`](#type-cisco_bgp_af_aa) * [`cisco_bgp_neighbor`](#type-cisco_bgp_neighbor) * [`cisco_bgp_neighbor_af`](#type-cisco_bgp_neighbor_af) * [`cisco_bridge_domain`](#type-cisco_bridge_domain) @@ -412,6 +414,7 @@ Symbol | Meaning | Description | [cisco_command_config](#type-cisco_command_config) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_bgp](#type-cisco_bgp) | ✅ | ✅ | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_bgp-caveats) | | [cisco_bgp_af](#type-cisco_bgp_af) | ✅* | ✅* | ✅ | ✅* | ✅ | ✅ | \*[caveats](#cisco_bgp_af-caveats) | +| [cisco_bgp_af_aa](#type-cisco_bgp_af_aa) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_bgp_neighbor](#type-cisco_bgp_neighbor) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_bgp_neighbor_af](#type-cisco_bgp_neighbor_af) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_bridge_domain](#type-cisco_bridge_domain) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | @@ -1384,6 +1387,58 @@ Apply table-map to filter routes downloaded into URIB. Valid values are a string ##### `table_map_filter` Filters routes rejected by the route-map and does not download them to the RIB. Valid values are true, false, or 'default'. +-- +### Type: cisco_bgp_af_aa + +Manages configuration of a BGP Address-family Aggregate-address instance. + +| Platform | OS Minimum Version | Module Minimum Version | +|----------|:------------------:|:----------------------:| +| N9k | 7.0(3)I2(5) | 1.7.0 | +| N3k | 7.0(3)I2(5) | 1.7.0 | +| N5k | 7.3(0)N1(1) | 1.7.0 | +| N6k | 7.3(0)N1(1) | 1.7.0 | +| N7k | 7.3(0)D1(1) | 1.7.0 | +| N9k-F | 7.0(3)F1(1) | 1.7.0 | + +#### Parameters + +###### `ensure` +Determine whether the BGP address family aggregate address should be present or not. Valid values + are 'present' and 'absent'. + +##### `asn` +BGP autonomous system number. Required. Valid values are String, Integer in ASPLAIN or ASDOT notation. + +##### `vrf` +VRF name. Required. Valid values are string. The name 'default' is a valid VRF representing the global bgp. + +##### `afi` +Address Family Identifier (AFI). Required. Valid values are `ipv4`, `ipv6`, `vpnv4`, `vpnv6` and `l2vpn`. + +##### `safi` +Sub Address Family Identifier (SAFI). Required. Valid values are `unicast`, `multicast` and `evpn`. + +##### `aa` +Aggregate address mask in ipv4/ipv6 format. Required. Valid values are string. Examples: 1.1.1.1/32 or 2000:1/128. + +#### Properties + +##### `as_set` +Generates autonomous system set path information. Valid values are true, false or 'default'. + +##### `advertise_map` +Name of the route map used to select the routes to create AS_SET origin communities. Valid values are string or 'default'. + +##### `attribute_map` +Name of the route map used to set the attribute of the aggregate route. Valid values are string or 'default'. + +##### `summary_only` +Filters all more-specific routes from updates. Valid values are true, false or 'default'. + +##### `suppress_map` +Name of the route map used to select the routes to be suppressed. Valid values are string or 'default'. + -- ### Type: cisco_bgp_neighbor diff --git a/examples/cisco/demo_bgp.pp b/examples/cisco/demo_bgp.pp index 1bcf59fdd..082583a05 100644 --- a/examples/cisco/demo_bgp.pp +++ b/examples/cisco/demo_bgp.pp @@ -207,6 +207,19 @@ redistribute => $ipv4_redistribute, } + cisco_bgp_af_aa { '55.77 default ipv4 unicast 1.1.1.1/32': + ensure => present, + as_set => true, + advertise_map => 'adm', + attribute_map => 'atm', + suppress_map => 'sum', + } + + cisco_bgp_af_aa { '55.77 default ipv4 unicast 2.2.2.2/32': + ensure => present, + summary_only => true, + } + if platform_get() != 'n3k' { cisco_bgp_af { '55.77 default l2vpn evpn': ensure => present, diff --git a/lib/puppet/provider/cisco_bgp_af_aa/cisco.rb b/lib/puppet/provider/cisco_bgp_af_aa/cisco.rb new file mode 100644 index 000000000..2bd5aa372 --- /dev/null +++ b/lib/puppet/provider/cisco_bgp_af_aa/cisco.rb @@ -0,0 +1,185 @@ +# May, 2017 +# +# Copyright (c) 2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'cisco_node_utils' if Puppet.features.cisco_node_utils? + +begin + require 'puppet_x/cisco/autogen' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'autogen.rb')) +end + +Puppet::Type.type(:cisco_bgp_af_aa).provide(:cisco) do + desc 'The Cisco bgp_af_aa provider.' + + confine feature: :cisco_node_utils + defaultfor operatingsystem: :nexus + + mk_resource_methods + + BGP_AF_AA_NON_BOOL_PROPS = [ + :advertise_map, + :attribute_map, + :suppress_map, + ] + + BGP_AF_AA_BOOL_PROPS = [ + :as_set, + :summary_only, + ] + + BGP_AF_AA_ALL_PROPS = BGP_AF_AA_NON_BOOL_PROPS + BGP_AF_AA_BOOL_PROPS + PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@nu', + BGP_AF_AA_NON_BOOL_PROPS) + PuppetX::Cisco::AutoGen.mk_puppet_methods(:bool, self, '@nu', + BGP_AF_AA_BOOL_PROPS) + + def initialize(value={}) + super(value) + asn = @property_hash[:asn] + vrf = @property_hash[:vrf] + afi = @property_hash[:afi] + safi = @property_hash[:safi] + aa = @property_hash[:aa] + af = [afi.to_s, safi.to_s] + + @nu = Cisco::RouterBgpAFAggrAddr.aas[asn][vrf][af][aa] unless asn.nil? + @property_flush = {} + end + + def self.properties_get(asn, vrf, af, aa, nu_obj) + current_state = { + name: [asn, vrf, af.first, af.last, aa].join(' '), + asn: asn, + vrf: vrf, + afi: af.first, + safi: af.last, + aa: aa, + ensure: :present, + } + + # Call node_utils getter for each property + BGP_AF_AA_NON_BOOL_PROPS.each do |prop| + current_state[prop] = nu_obj.send(prop) + end + + BGP_AF_AA_BOOL_PROPS.each do |prop| + val = nu_obj.send(prop) + if val.nil? + current_state[prop] = nil + else + current_state[prop] = val ? :true : :false + end + end + new(current_state) + end # self.properties_get + + def self.instances + aa_objs = [] + Cisco::RouterBgpAFAggrAddr.aas.each do |asn, vrfs| + vrfs.each do |vrf, afs| + afs.each do |af, aas| + aas.each do |aa, nu_obj| + aa_objs << properties_get(asn, vrf, af, aa, nu_obj) + end + end + end + end + aa_objs + end # self.instances + + def self.prefetch(resources) + aas = instances + resources.keys.each do |name| + provider = aas.find do |aga| + aga.asn.to_s == resources[name][:asn].to_s && + aga.vrf == resources[name][:vrf] && + aga.afi.to_s == resources[name][:afi].to_s && + aga.safi.to_s == resources[name][:safi].to_s && + aga.aa.to_s == resources[name][:aa].to_s + end + resources[name].provider = provider unless provider.nil? + end + end + + def exists? + @property_hash[:ensure] == :present + end + + def create + @property_flush[:ensure] = :present + end + + def destroy + @property_flush[:ensure] = :absent + end + + def properties_set(new_obj=false) + BGP_AF_AA_ALL_PROPS.each do |prop| + next unless @resource[prop] + send("#{prop}=", @resource[prop]) if new_obj + unless @property_flush[prop].nil? + @nu.send("#{prop}=", @property_flush[prop]) if + @nu.respond_to?("#{prop}=") + end + end + # Custom set methods + aa_set + end + + def aa_set + attrs = {} + vars = [ + :advertise_map, + :attribute_map, + :suppress_map, + :as_set, + :summary_only, + ] + return unless vars.any? { |p| @property_flush.key?(p) } + # At least one var has changed, get all vals from manifest + vars.each do |p| + if @resource[p] == :default + attrs[p] = @nu.send("default_#{p}") + else + attrs[p] = @resource[p] + attrs[p] = PuppetX::Cisco::Utils.bool_sym_to_s(attrs[p]) + end + end + @nu.aa_set(attrs) + end + + def flush + if @property_flush[:ensure] == :absent + @nu.destroy + @nu = nil + else + # Create/Update + new_obj = false + if @nu.nil? + new_obj = true + @nu = Cisco::RouterBgpAFAggrAddr.new(@resource[:asn], + @resource[:vrf], + [@resource[:afi], + @resource[:safi]], + @resource[:aa]) + end + properties_set(new_obj) + end + end +end diff --git a/lib/puppet/type/cisco_bgp_af_aa.rb b/lib/puppet/type/cisco_bgp_af_aa.rb new file mode 100644 index 000000000..03a3651e4 --- /dev/null +++ b/lib/puppet/type/cisco_bgp_af_aa.rb @@ -0,0 +1,153 @@ +# Manages the Cisco Bgp_af_aa configuration resource. +# +# May 2017 +# +# Copyright (c) 2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Puppet::Type.newtype(:cisco_bgp_af_aa) do + @doc = "Manages a cisco BGP Address family aggregate address + + cisco_bgp_af_aa { '': + ..attributes.. + } + + `<title>` is the title of the bgp_af_aa resource + +Example: + + cisco_bgp_af_aa{ '55.77 default ipv4 multicast 1.1.1.1/32': + ensure => 'present', + as_set => 'true', + summary_only => 'false', + advertise_map => 'adm', + attribute_map => 'atm', + suppress_map => 'sum', + } + " + + ################### + # Resource Naming # + ################### + + # Parse out the title to fill in the attributes in these + # patterns. These attributes can be overwritten later. + def self.title_patterns + identity = ->(x) { x } + [ + [ + /^(\d+|\d+\.\d+) (\S+) (\S+) (\S+) (\S+)$/, + [ + [:asn, identity], + [:vrf, identity], + [:afi, identity], + [:safi, identity], + [:aa, identity], + ], + ] + ] + end + + ############## + # Parameters # + ############## + + ensurable + + # Overwrites the name method which by default returns only + # self[:name]. + def name + "#{self[:asn]} #{self[:vrf]} #{self[:afi]} #{self[:safi]} #{self[:aa]}" + end + + # Only needed to satisfy name parameter. + newparam(:name) do + end + + newparam(:asn, namevar: true) do + desc "BGP autonomous system number. Valid values are String, Integer in + ASPLAIN or ASDOT notation" + validate do |value| + unless /^(\d+|\d+\.\d+)$/.match(value.to_s) + fail("BGP asn #{value} must be specified in ASPLAIN or ASDOT notation") + end + end + + munge(&:to_s) + end + + newparam(:vrf, namevar: true) do + desc 'BGP vrf name. Valid values are string. ' \ + "The name 'default' is a valid VRF." + + defaultto('default') + newvalues(/^\S+$/) + end + + newparam(:afi, namevar: true) do + desc 'BGP Address-family AFI (ipv4|ipv6). Valid values are string.' + newvalues(:ipv4, :ipv6) + end + + newparam(:safi, namevar: true) do + desc 'BGP Address-family SAFI (unicast|multicast). Valid values are string.' + newvalues(:unicast, :multicast) + end + + newparam(:aa, namevar: true) do + desc 'BGP aggregate address prefix. Valid values are String.' + munge do |value| + begin + value = PuppetX::Cisco::Utils.process_network_mask(value) unless value.nil? + value + rescue + raise 'aggregate address must be in valid address/length format' + end + end + end + + ############## + # Attributes # + ############## + + newproperty(:as_set) do + desc "Generates autonomous system set path information. Valid values are true, false or 'default'" + + newvalues(:true, :false, :default) + end # property as_set + + newproperty(:summary_only) do + desc "Filters all more-specific routes from updates. Valid values are true, false or 'default'" + + newvalues(:true, :false, :default) + end # property summary_only + + newproperty(:advertise_map) do + desc "Name of the route map used to select the routes to create AS_SET origin communities. Valid values are String or 'default'" + + munge { |value| value == 'default' ? :default : value } + end # property advertise_map + + newproperty(:attribute_map) do + desc "Name of the route map used to set the attribute of the aggregate route. Valid values are String or 'default'" + + munge { |value| value == 'default' ? :default : value } + end # property attribute_map + + newproperty(:suppress_map) do + desc "Name of the route map used to select the routes to be suppressed. Valid values are String or 'default'" + + munge { |value| value == 'default' ? :default : value } + end # property suppress_map +end diff --git a/tests/beaker_tests/cisco_bgp_af_aa/test_bgp_af_aa.rb b/tests/beaker_tests/cisco_bgp_af_aa/test_bgp_af_aa.rb new file mode 100644 index 000000000..79a233bf2 --- /dev/null +++ b/tests/beaker_tests/cisco_bgp_af_aa/test_bgp_af_aa.rb @@ -0,0 +1,114 @@ +############################################################################### +# Copyright (c) 2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# Test hash top-level keys +tests = { + master: master, + agent: agent, + operating_system: 'nexus', + resource_name: 'cisco_bgp_af_aa', +} + +skip_unless_supported(tests) + +# Test hash test cases +tests[:default] = { + desc: '1.1 Default Properties', + title_pattern: '2 default ipv4 unicast 1.1.1.1/32', + manifest_props: { + as_set: 'default', + summary_only: 'default', + advertise_map: 'default', + attribute_map: 'default', + suppress_map: 'default', + }, + code: [0, 2], + resource: { + as_set: 'false', + summary_only: 'false', + }, +} + +tests[:non_default1] = { + desc: '2.1 Non Defaults', + title_pattern: '2 red ipv6 multicast 2000::1/128', + manifest_props: { + as_set: 'true', + advertise_map: 'adm', + attribute_map: 'atm', + suppress_map: 'sum', + }, +} + +tests[:non_default2] = { + desc: '2.2 Non Defaults', + title_pattern: '2 red ipv6 multicast 2000::1/128', + manifest_props: { + summary_only: 'true' + }, +} + +def dependency_manifest(_tests, _id) + " + cisco_bgp { '2 default': + ensure => present, + } + + cisco_bgp_af { '2 default ipv4 unicast': + ensure => present, + } + + cisco_bgp_af { '2 red ipv6 multicast': + ensure => present, + } + " +end + +def cleanup(agent) + test_set(agent, 'no feature bgp') +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + teardown { cleanup(agent) } + cleanup(agent) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. Default Property Testing") + test_harness_run(tests, :default) + + # ------------------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 2. Non Default Property Testing") + + test_harness_run(tests, :non_default1) + test_harness_run(tests, :non_default2) + + # ------------------------------------------------------------------- + skipped_tests_summary(tests) +end + +logger.info("TestCase :: #{tests[:resource_name]} :: End") From e13f432fc58e83f2c22304c42fc84480a01e61b5 Mon Sep 17 00:00:00 2001 From: saichint <saichint@cisco.com> Date: Thu, 11 May 2017 12:23:00 -0700 Subject: [PATCH 167/203] add require ipaddr and cmnutils (#456) --- lib/puppet/type/cisco_bgp_af_aa.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/puppet/type/cisco_bgp_af_aa.rb b/lib/puppet/type/cisco_bgp_af_aa.rb index 03a3651e4..d97aaed29 100644 --- a/lib/puppet/type/cisco_bgp_af_aa.rb +++ b/lib/puppet/type/cisco_bgp_af_aa.rb @@ -16,6 +16,15 @@ # See the License for the specific language governing permissions and # limitations under the License. +require 'ipaddr' +begin + require 'puppet_x/cisco/cmnutils' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', + 'puppet_x', 'cisco', 'cmnutils.rb')) +end + Puppet::Type.newtype(:cisco_bgp_af_aa) do @doc = "Manages a cisco BGP Address family aggregate address From 154563304c81f0cad3be3230f2083bdba9bb9a5e Mon Sep 17 00:00:00 2001 From: Mike Wiebe <mwiebe@cisco.com> Date: Wed, 31 May 2017 16:47:34 -0400 Subject: [PATCH 168/203] Boiler plate changelog headings --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15fd33377..171812fed 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### New feature support + +### Added + +### Changed + +### Removed + +### Resolved Issues + ## [1.7.0] - 2017-05-31 ### New feature support From 86effcaaa4b34cefaba7bb00b607705871d58aa5 Mon Sep 17 00:00:00 2001 From: sai chintalapudi <saichint@cisco.com> Date: Thu, 1 Jun 2017 12:06:08 -0700 Subject: [PATCH 169/203] beaker --- .../cisco_object_group/test_object_group.rb | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 tests/beaker_tests/cisco_object_group/test_object_group.rb diff --git a/tests/beaker_tests/cisco_object_group/test_object_group.rb b/tests/beaker_tests/cisco_object_group/test_object_group.rb new file mode 100644 index 000000000..96b698a6b --- /dev/null +++ b/tests/beaker_tests/cisco_object_group/test_object_group.rb @@ -0,0 +1,103 @@ +############################################################################### +# Copyright (c) 2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# Test hash top-level keys +tests = { + master: master, + agent: agent, + resource_name: 'cisco_object_group_entry', +} + +tests[:seq_10_v4] = { + title_pattern: 'ipv4 address beaker 10', + manifest_props: { + address: '1.2.3.4 2.3.4.5' + }, +} + +tests[:seq_10_v6] = { + desc: 'IPv6 Seq 10', + title_pattern: 'ipv6 address beaker6 10', + manifest_props: { + address: '1:1::1/64' + }, +} + +tests[:seq_20_v4] = { + title_pattern: 'ipv4 port beakerp 20', + manifest_props: { + port: 'eq 40' + }, +} + +tests[:seq_30_v4] = { + desc: 'IPv4 Seq 30', + title_pattern: 'ipv4 port beakerp 30', + manifest_props: { + port: 'range 300 550' + }, +} + +def dependency_manifest(_tests, _id) + " + cisco_object_group { 'ipv4 address beaker': + ensure => present, + } + + cisco_object_group { 'ipv6 address beaker6': + ensure => present, + } + + cisco_object_group { 'ipv4 port beakerp': + ensure => present, + } + " +end + +def cleanup + logger.info('Testcase Cleanup:') + resource_absent_cleanup(agent, 'cisco_object_group_entry') + resource_absent_cleanup(agent, 'cisco_object_group') +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + cleanup + teardown { cleanup } + + # --------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. ObjectGroup Testing") + + test_harness_run(tests, :seq_10_v4) + test_harness_run(tests, :seq_10_v6) + test_harness_run(tests, :seq_20_v4) + test_harness_run(tests, :seq_30_v4) + + # --------------------------------------------------------- + skipped_tests_summary(tests) +end +logger.info("TestCase :: #{tests[:resource_name]} :: End") From e8cf47769827320a43698c5533779355059e1eec Mon Sep 17 00:00:00 2001 From: sai chintalapudi <saichint@cisco.com> Date: Thu, 1 Jun 2017 12:26:20 -0700 Subject: [PATCH 170/203] remove beaker for object_group --- .../cisco_object_group/test_object_group.rb | 103 ------------------ 1 file changed, 103 deletions(-) delete mode 100644 tests/beaker_tests/cisco_object_group/test_object_group.rb diff --git a/tests/beaker_tests/cisco_object_group/test_object_group.rb b/tests/beaker_tests/cisco_object_group/test_object_group.rb deleted file mode 100644 index 96b698a6b..000000000 --- a/tests/beaker_tests/cisco_object_group/test_object_group.rb +++ /dev/null @@ -1,103 +0,0 @@ -############################################################################### -# Copyright (c) 2017 Cisco and/or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -############################################################################### -# -# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) -# for information regarding: -# - test script general prequisites -# - command return codes -# - A description of the 'tests' hash and its usage -# -############################################################################### -require File.expand_path('../../lib/utilitylib.rb', __FILE__) - -# Test hash top-level keys -tests = { - master: master, - agent: agent, - resource_name: 'cisco_object_group_entry', -} - -tests[:seq_10_v4] = { - title_pattern: 'ipv4 address beaker 10', - manifest_props: { - address: '1.2.3.4 2.3.4.5' - }, -} - -tests[:seq_10_v6] = { - desc: 'IPv6 Seq 10', - title_pattern: 'ipv6 address beaker6 10', - manifest_props: { - address: '1:1::1/64' - }, -} - -tests[:seq_20_v4] = { - title_pattern: 'ipv4 port beakerp 20', - manifest_props: { - port: 'eq 40' - }, -} - -tests[:seq_30_v4] = { - desc: 'IPv4 Seq 30', - title_pattern: 'ipv4 port beakerp 30', - manifest_props: { - port: 'range 300 550' - }, -} - -def dependency_manifest(_tests, _id) - " - cisco_object_group { 'ipv4 address beaker': - ensure => present, - } - - cisco_object_group { 'ipv6 address beaker6': - ensure => present, - } - - cisco_object_group { 'ipv4 port beakerp': - ensure => present, - } - " -end - -def cleanup - logger.info('Testcase Cleanup:') - resource_absent_cleanup(agent, 'cisco_object_group_entry') - resource_absent_cleanup(agent, 'cisco_object_group') -end - -################################################################# -# TEST CASE EXECUTION -################################################################# -test_name "TestCase :: #{tests[:resource_name]}" do - cleanup - teardown { cleanup } - - # --------------------------------------------------------- - logger.info("\n#{'-' * 60}\nSection 1. ObjectGroup Testing") - - test_harness_run(tests, :seq_10_v4) - test_harness_run(tests, :seq_10_v6) - test_harness_run(tests, :seq_20_v4) - test_harness_run(tests, :seq_30_v4) - - # --------------------------------------------------------- - skipped_tests_summary(tests) -end -logger.info("TestCase :: #{tests[:resource_name]} :: End") From 8bd764ec49a2b4af7c8d7eda466450104278990a Mon Sep 17 00:00:00 2001 From: Mike Wiebe <mwiebe@cisco.com> Date: Sat, 3 Jun 2017 09:49:32 -0400 Subject: [PATCH 171/203] Minor updates --- examples/cisco/demo_upgrade.pp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/cisco/demo_upgrade.pp b/examples/cisco/demo_upgrade.pp index 960c4881d..74ede861a 100644 --- a/examples/cisco/demo_upgrade.pp +++ b/examples/cisco/demo_upgrade.pp @@ -19,13 +19,13 @@ # To use this manifest, make sure the gem and bin file are in the files directory under your # puppet module on the puppet master. - # agent-lab9-pm:files:2009> cd /etc/puppetlabs/code/environments/production/modules/ciscopuppet/files/ - # agent-lab9-pm:files:2010> ls -lh + # puppetmaster:files:2009> cd /etc/puppetlabs/code/environments/production/modules/ciscopuppet/files/ + # puppetmaster:files:2010> ls -lh # total 1.3G - # -rw-r--r-- 1 root root 431K Mar 2 14:19 cisco_node_utils-1.5.0.gem + # -rw-r--r-- 1 root root 431K Mar 2 14:19 cisco_node_utils-1.6.0.gem # -rwxr-xr-- 1 root root 530M Mar 2 15:46 nxos.7.0.3.I2.5.bin # -rwxr-xr-- 1 root root 723M Mar 2 15:25 nxos.7.0.3.I5.1.bin - # agent-lab9-pm:files:2011> + # puppetmaster:files:2011> node default { $gem = 'cisco_node_utils-1.6.0.gem' From ecb426877b02fd46bb00f73f97c8d01c6b063978 Mon Sep 17 00:00:00 2001 From: saichint <saichint@cisco.com> Date: Thu, 8 Jun 2017 08:50:30 -0700 Subject: [PATCH 172/203] Object_group (#459) * puppet module for object_group * add beaker * docs * typo correction * typo fix * exclude n5k,n6k * review comments --- CHANGELOG.md | 3 + README.md | 132 ++++++++++++++- examples/cisco/demo_object_group.pp | 65 ++++++++ examples/demo_all_cisco.pp | 1 + .../provider/cisco_object_group/cisco.rb | 100 ++++++++++++ .../cisco_object_group_entry/cisco.rb | 152 ++++++++++++++++++ lib/puppet/type/cisco_object_group.rb | 91 +++++++++++ lib/puppet/type/cisco_object_group_entry.rb | 140 ++++++++++++++++ .../cisco_object_group/test_object_group.rb | 107 ++++++++++++ 9 files changed, 789 insertions(+), 2 deletions(-) create mode 100644 examples/cisco/demo_object_group.pp create mode 100644 lib/puppet/provider/cisco_object_group/cisco.rb create mode 100644 lib/puppet/provider/cisco_object_group_entry/cisco.rb create mode 100644 lib/puppet/type/cisco_object_group.rb create mode 100644 lib/puppet/type/cisco_object_group_entry.rb create mode 100644 tests/beaker_tests/cisco_object_group/test_object_group.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 171812fed..2d46ba724 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### New feature support +#### Cisco Resources +- `cisco_object_group` type and provider. +- `cisco_object_group_entry` type and provider. ### Added diff --git a/README.md b/README.md index d852616ad..23d2f0393 100644 --- a/README.md +++ b/README.md @@ -215,6 +215,10 @@ The following resources include cisco types and providers along with cisco provi * [`ntp_config (netdev_stdlib)`](#type-ntp_config) * [`ntp_server (netdev_stdlib)`](#type-ntp_server) +* ObjectGroup Types + * [`cisco_object_group`](#type-cisco_object_group) + * [`cisco_object_group_entry`](#type-cisco_object_group_entry) + * OSPF Types * [`cisco_vrf`](#type-cisco_vrf) * [`cisco_ospf`](#type-cisco_ospf) @@ -321,6 +325,8 @@ The following resources include cisco types and providers along with cisco provi * [`cisco_itd_device_group`](#type-cisco_itd_device_group) * [`cisco_itd_device_group_node`](#type-cisco_itd_device_group_node) * [`cisco_itd_service`](#type-cisco_itd_service) +* [`cisco_object_group`](#type-cisco_object_group) +* [`cisco_object_group_entry`](#type-cisco_object_group_entry) * [`cisco_ospf`](#type-cisco_ospf) * [`cisco_ospf_area`](#type-cisco_ospf_area) * [`cisco_ospf_area_vlink`](#type-cisco_ospf_area_vlink) @@ -436,6 +442,8 @@ Symbol | Meaning | Description | [cisco_itd_device_group](#type-cisco_itd_device_group) | ✅ | ➖ | ➖ | ➖ | ✅ | ➖ | | [cisco_itd_device_group_node](#type-cisco_itd_device_group_node) | ✅ | ➖ | ➖ | ➖ | ✅ | ➖ | | [cisco_itd_service](#type-cisco_itd_service) | ✅ | ➖ | ➖ | ➖ | ✅ | ➖ | \*[caveats](#cisco_itd_service-caveats) | +| [cisco_object_group](#type-cisco_object_group) | ✅ | ✅ | ➖ | ➖ | ✅ | ✅ | +| [cisco_object_group_entry](#type-cisco_object_group_entry) | ✅ | ✅ | ➖ | ➖ | ✅ | ✅ | | [cisco_ospf](#type-cisco_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | [cisco_ospf_vrf](#type-cisco_ospf_vrf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅ = Supported <br> ➖ = Not Applicable | N9k | N3k | N5k | N6k | N7k | N9k-F | Caveats | @@ -679,7 +687,7 @@ Manages configuration of a Access Control List (ACL) instance. Determines whether the config should be present or not on the device. Valid values are 'present' and 'absent'. ##### `afi` -Address Family Identifier (AFI). Required. Valid values are ipv4 and ipv6. +Address Family Identifier (AFI). Required. Valid values are 'ipv4' and 'ipv6'. ##### `acl_name` Name of the acl instance. Valid values are string. @@ -757,7 +765,7 @@ cisco_ace { 'ipv6 my_v6_acl 42': | `cisco_ace { 'ipv4 my_acl 42':` ##### `afi` -Address Family Identifier (AFI). Required. Valid values are ipv4 and ipv6. +Address Family Identifier (AFI). Required. Valid values are 'ipv4' and 'ipv6'. ##### `acl_name` Access Control List (ACL) name. Required. Valid values are type String. @@ -2865,6 +2873,126 @@ keyword 'default'. ##### `virtual_ip` Virtual ip configuration. Valid values are an array of Strings or 'default'. +-- +### Type: cisco_object_group + +Manages configuration of an ObjectGroup instance. This has no properties and it is the parent of ObjectGroupEntry. + +| Platform | OS Minimum Version | Module Minimum Version | +|----------|:------------------:|:----------------------:| +| N9k | 7.0(3)I2(2e) | 1.8.0 | +| N3k | 7.0(3)I2(2e) | 1.8.0 | +| N5k | not applicable | not applicable | +| N6k | not applicable | not applicable | +| N7k | 7.3(0)D1(1) | 1.8.0 | +| N9k-F | 7.0(3)F1(1) | 1.8.0 | + +#### Parameters + +| Example Parameter Usage +|:-- +| `cisco_object_group { '<afi> <type> <grp_name>':` +| `cisco_object_group { 'ipv4 address myog_v4_addr':` + +##### `afi` +Address Family Identifier (AFI). Required. Valid values are 'ipv4' and 'ipv6'. + +##### `type` +Type of the object_group instance. Required. Valid values are 'address' and 'port'. + +##### `grp_name` +Name of the object_group instance. Required. Valid values are type String. + +##### Properties + +##### `ensure` +Determines whether the config should be present or not on the device. Valid values are 'present' and 'absent'. + +-- +### Type: cisco_object_group_entry + +Manages configuration of an ObjectGroupEntry instance. + +| Platform | OS Minimum Version | Module Minimum Version | +|----------|:------------------:|:----------------------:| +| N9k | 7.0(3)I2(2e) | 1.8.0 | +| N3k | 7.0(3)I2(2e) | 1.8.0 | +| N5k | not applicable | not applicable | +| N6k | not applicable | not applicable | +| N7k | 7.3(0)D1(1) | 1.8.0 | +| N9k-F | 7.0(3)F1(1) | 1.8.0 | + +#### Example Usage + +```puppet +cisco_object_group_entry { 'ipv4 address myoge_v4_addr 10': + ensure => 'present', + address => '10.10.10.1/24', +} + +cisco_object_group_entry { 'ipv4 port myoge_v4_port 20': + ensure => 'present', + port => 'neq 40', +} + +cisco_object_group_entry { 'ipv6 address myoge_v6_addr 30': + ensure => 'present', + address => '2000::1/64', +} +``` + +#### Parameters + +| Example Parameter Usage +|:-- +| `cisco_object_group_entry { '<afi> <type> <grp_name> <seqno>':` +| `cisco_object_group_entry { 'ipv4 address myoge_v4_addr 10':` + +##### `afi` +Address Family Identifier (AFI). Required. Valid values are 'ipv4' and 'ipv6'. + +##### `type` +Type of the object_group instance. Required. Valid values are 'address' and 'port'. + +##### `grp_name` +Name of the object_group instance. Required. Valid values are type String. + +##### `seqno` +Object Group Entry Sequence Number. Required. Valid values are type Integer. + +##### `ensure` +Determines whether the config should be present or not on the device. Valid values are 'present' and 'absent'. + +#### Properties + +##### `address` +The Address to match against. Valid values are type String, which must be one of the following forms: + +* An IPv4/IPv6 address/prefix length +* The keyword `host` and a host address +* An IP Address and wildcard + +| Examples +|:-- +| `address => '10.10.10.1/24'` +| `address => '10.10.10.1 11.12.13.14'` +| `address => 'host 10.0.0.1'` +| `address => '2000::1/64'` +| `address => '2000::1 2002::1'` +| `address => 'host 2001::1'` + +##### `port` +The TCP or UDP Port to match against. Valid values are type String, which must be one of the following forms: + +* A comparison operator (`eq`, `neq`, `lt`, `gt`) and value +* The keyword `range` and a range value + +| Examples +|:-- +| `port => 'neq 40'` +| `port => 'range 68 69'` +| `port => 'lt 400'` + -- ### Type: cisco_ospf diff --git a/examples/cisco/demo_object_group.pp b/examples/cisco/demo_object_group.pp new file mode 100644 index 000000000..49244671a --- /dev/null +++ b/examples/cisco/demo_object_group.pp @@ -0,0 +1,65 @@ +# Manifest to demo cisco_acl providers +# +# Copyright (c) 2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +class ciscopuppet::cisco::demo_object_group { + + cisco_object_group { 'ipv4 address my_addr1': + ensure => 'present', + } + + cisco_object_group_entry { 'ipv4 address my_addr1 10': + ensure => 'present', + address => '1.2.3.4 2.3.4.5', + } + + cisco_object_group_entry { 'ipv4 address my_addr1 20': + ensure => 'present', + address => '3.3.3.3/24', + } + + cisco_object_group_entry { 'ipv4 address my_addr1 30': + ensure => 'present', + address => 'host 4.4.4.4', + } + + cisco_object_group { 'ipv6 address my_addr2': + ensure => 'present' + } + + cisco_object_group_entry { 'ipv6 address my_addr2 20': + ensure => 'present', + address => '2000::1/64', + } + + cisco_object_group_entry { 'ipv6 address my_addr2 30': + ensure => 'present', + address => 'host 2001::10', + } + + cisco_object_group { 'ipv4 port my_port': + ensure => 'present' + } + + cisco_object_group_entry { 'ipv4 port my_port 20': + ensure => 'present', + port => 'range 100 200', + } + + cisco_object_group_entry { 'ipv4 port my_port 30': + ensure => 'present', + port => 'neq 154', + } +} diff --git a/examples/demo_all_cisco.pp b/examples/demo_all_cisco.pp index 7948b6304..5d733aea1 100644 --- a/examples/demo_all_cisco.pp +++ b/examples/demo_all_cisco.pp @@ -38,6 +38,7 @@ include ciscopuppet::cisco::demo_interface #include ciscopuppet::cisco::demo_interface_service_vni include ciscopuppet::cisco::demo_itd + include ciscopuppet::cisco::demo_object_group include ciscopuppet::cisco::demo_ospf include ciscopuppet::cisco::demo_patching include ciscopuppet::cisco::demo_pim diff --git a/lib/puppet/provider/cisco_object_group/cisco.rb b/lib/puppet/provider/cisco_object_group/cisco.rb new file mode 100644 index 000000000..8b1cc0866 --- /dev/null +++ b/lib/puppet/provider/cisco_object_group/cisco.rb @@ -0,0 +1,100 @@ +# Copyright (c) 2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'cisco_node_utils' if Puppet.features.cisco_node_utils? +begin + require 'puppet_x/cisco/autogen' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'autogen.rb')) +end + +Puppet::Type.type(:cisco_object_group).provide(:cisco) do + desc 'The Cisco provider for cisco_object_group.' + + confine feature: :cisco_node_utils + + mk_resource_methods + + def initialize(value={}) + super(value) + afi = @property_hash[:afi] + type = @property_hash[:type] + grp_name = @property_hash[:grp_name] + @nu = Cisco::ObjectGroup.object_groups[afi][type][grp_name] unless afi.nil? + @property_flush = {} + end + + def self.properties_get(afi, type, grp_name, _inst) + current_state = { + name: "#{afi} #{type} #{grp_name}", + afi: afi, + type: type, + grp_name: grp_name, + ensure: :present, + } + new(current_state) + end # self.properties_get + + def self.instances + instance_array = [] + Cisco::ObjectGroup.object_groups.each do |afi, types| + types.each do |type, grp_names| + grp_names.each do |grp_name, inst| + begin + instance_array << properties_get(afi, type, grp_name, inst) + end + end + end + end + instance_array + end # self.instances + + def self.prefetch(resources) + instance_array = instances + resources.keys.each do |name| + provider = instance_array.find do |objgrp| + objgrp.afi.to_s == resources[name][:afi].to_s && + objgrp.type.to_s == resources[name][:type].to_s && + objgrp.grp_name.to_s == resources[name][:grp_name].to_s + end + resources[name].provider = provider unless provider.nil? + end + end # self.prefetch + + def exists? + (@property_hash[:ensure] == :present) + end + + def create + @property_flush[:ensure] = :present + end + + def destroy + @property_flush[:ensure] = :absent + end + + def flush + if @property_flush[:ensure] == :absent + @nu.destroy + @nu = nil + else + if @nu.nil? + # create new + @nu = Cisco::ObjectGroup.new(@resource[:afi], @resource[:type], @resource[:grp_name]) + end + end + end +end diff --git a/lib/puppet/provider/cisco_object_group_entry/cisco.rb b/lib/puppet/provider/cisco_object_group_entry/cisco.rb new file mode 100644 index 000000000..c309a1c65 --- /dev/null +++ b/lib/puppet/provider/cisco_object_group_entry/cisco.rb @@ -0,0 +1,152 @@ +# June 2017 +# +# Copyright (c) 2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'cisco_node_utils' if Puppet.features.cisco_node_utils? +begin + require 'puppet_x/cisco/autogen' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'cisco', 'autogen.rb')) +end + +Puppet::Type.type(:cisco_object_group_entry).provide(:cisco) do + desc 'The Cisco provider.' + + confine feature: :cisco_node_utils + defaultfor operatingsystem: :nexus + + mk_resource_methods + + # Property symbol array for method auto-generation. + OGE_NON_BOOL_PROPS = [ + :address, + :port, + ] + + PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@nu', + OGE_NON_BOOL_PROPS) + + def initialize(value={}) + super(value) + afi = @property_hash[:afi] + type = @property_hash[:type] + grp_name = @property_hash[:grp_name] + seqno = @property_hash[:seqno] + @nu = Cisco::ObjectGroupEntry.object_group_entries[afi][type][grp_name][seqno] unless grp_name.nil? || seqno.nil? + @property_flush = {} + end + + def self.properties_get(afi, type, grp_name, seqno, instance) + debug "Checking object_group_entry instance, #{afi} #{type} #{grp_name} #{seqno}" + current_state = { + name: "#{afi} #{type} #{grp_name} #{seqno}", + afi: afi, + type: type, + grp_name: grp_name, + seqno: seqno, + ensure: :present, + } + + # Call node_utils getter for each property + OGE_NON_BOOL_PROPS.each do |prop| + current_state[prop] = instance.send(prop) + end + new(current_state) + end # self.properties_get + + def self.instances + oge_hash = [] + Cisco::ObjectGroupEntry.object_group_entries.each do |afi, types| + types.each do |type, grp_names| + grp_names.each do |grp_name, entries| + entries.each do |seqno, instance| + oge_hash << properties_get(afi, type, grp_name, seqno, instance) + end + end + end + end + oge_hash + end + + def self.prefetch(resources) + oge_instances = instances + resources.keys.each do |name| + provider = oge_instances.find do |oge| + resources[name][:afi].to_s == oge.afi.to_s && + resources[name][:type].to_s == oge.type.to_s && + resources[name][:grp_name].to_s == oge.grp_name.to_s && + resources[name][:seqno].to_i == oge.seqno.to_i + end + resources[name].provider = provider unless provider.nil? + end + end # self.prefetch + + def exists? + @property_hash[:ensure] == :present + end + + def create + @property_flush[:ensure] = :present + end + + def destroy + @property_flush[:ensure] = :absent + end + + def properties_set(new_object_group_entry=false) + OGE_NON_BOOL_PROPS.each do |prop| + next unless @resource[prop] + send("#{prop}=", @resource[prop]) if new_object_group_entry + next if @property_flush[prop].nil? + @nu.send("#{prop}=", @property_flush[prop]) if + @nu.respond_to?("#{prop}=") + end + entry_set + end + + # The following properties are setters and cannot be handled + # by PuppetX::Cisco::AutoGen.mk_puppet_methods. + def entry_set + attrs = {} + vars = [ + :address, + :port, + ] + if vars.any? { |p| @property_flush.key?(p) } + # At least one var has changed, get all vals from manifest + vars.each do |p| + attrs[p] = @resource[p] + end + end + return if attrs.empty? + @nu.entry_set(attrs) + end + + def flush + if @property_flush[:ensure] == :absent + @nu.destroy + @nu = nil + else + if @nu.nil? + new_object_group_entry = true + @nu = Cisco::ObjectGroupEntry.new(@resource[:afi], @resource[:type], + @resource[:grp_name], @resource[:seqno]) + end + properties_set(new_object_group_entry) + end + end +end diff --git a/lib/puppet/type/cisco_object_group.rb b/lib/puppet/type/cisco_object_group.rb new file mode 100644 index 000000000..5ea0009af --- /dev/null +++ b/lib/puppet/type/cisco_object_group.rb @@ -0,0 +1,91 @@ +# +# Puppet resource type for cisco_object_group +# +# Copyright (c) 2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Puppet::Type.newtype(:cisco_object_group) do + # --------------------------------------------------------------- + # @doc entry to describe the resource and usage + # --------------------------------------------------------------- + @doc = "Manages configuration of an object_group instance + + ~~~puppet + cisco_object_group {'<string>': + ..attributes.. + } + ~~~ + + `<string>` is the name of the object_group instance. + + Example: + + ~~~puppet + cisco_object_group { 'ipv4 port MyObjGrp1' : + ensure => present, + } + ~~~ + + ~~~puppet + cisco_object_group { 'ipv6 address MyObjGrp2' : + ensure => present, + } + ~~~ + " + + ensurable + + ################### + # Resource Naming # + ################### + + # Parse the title to populate the attributes in these patterns. + # These attributes may be overwritten later. + def self.title_patterns + identity = ->(x) { x } + [ + [ + /^(\S+)\s+(\S+)\s+(\S+)$/, + [ + [:afi, identity], + [:type, identity], + [:grp_name, identity], + ], + ] + ] + end + + newparam(:afi, namevar: true) do + desc 'The Address-Family Indentifier (ipv4|ipv6).' + newvalues(:ipv4, :ipv6) + end + + newparam(:type, namevar: true) do + desc 'Type of the object_group instance.' + newvalues(:address, :port) + end + + newparam(:grp_name, namevar: true) do + desc 'Name of the object_group instance. Valid values are string.' + end + + # Overwrites the name method which by default returns only self[:name]. + def name + "#{self[:afi]} #{self[:type]} #{self[:grp_name]}" + end + + # Only needed to satisfy name parameter. + newparam(:name) do + end +end diff --git a/lib/puppet/type/cisco_object_group_entry.rb b/lib/puppet/type/cisco_object_group_entry.rb new file mode 100644 index 000000000..a97557cd1 --- /dev/null +++ b/lib/puppet/type/cisco_object_group_entry.rb @@ -0,0 +1,140 @@ +# Manages configuration for cisco_object_group_entry +# +# June 2017 +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'ipaddr' +begin + require 'puppet_x/cisco/cmnutils' +rescue LoadError # seen on master, not on agent + # See longstanding Puppet issues #4248, #7316, #14073, #14149, etc. Ugh. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', + 'puppet_x', 'cisco', 'cmnutils.rb')) +end + +Puppet::Type.newtype(:cisco_object_group_entry) do + @doc = "Manages ObjectGroupEntry configuration. + + ~~~puppet + cisco_object_group_entry { '<afi> <type> <grp_name> <seqno>': + ..attributes.. + } + ~~~ + + `<afi> <type> <grp_name> <seqno>` is the title of the object_group_entry resource. + + Example: + + ~~~puppet + cisco_object_group_entry { 'ipv4 address my_obj_grp_entry1 10': + address => '1.2.3.4 2.3.4.5', + } + cisco_object_group_entry { 'ipv6 address my_obj_grp_entry2 10': + address => '2000::1/64', + } + cisco_object_group_entry { 'ipv4 port my_obj_grp_entry3 20': + port => 'eq 40', + } + cisco_object_group_entry { 'ipv4 port my_obj_grp_entry4 30': + port => 'range 40 100', + } + ~~~ + " + + ################### + # Resource Naming # + ################### + # Parse out the title to fill in the attributes in these patterns. These + # attributes can be overwritten later. + + def self.title_patterns + identity = ->(x) { x } + [ + [ + /^(ipv4|ipv6)\s+(\S+)\s+(\S+)\s+(\d+)$/, + [ + [:afi, identity], + [:type, identity], + [:grp_name, identity], + [:seqno, identity], + ], + ] + ] + end + + ############## + # Parameters # + ############## + + ensurable + + # Overwrites the name method which by default returns only + # self[:name]. + def name + "#{self[:afi]} #{self[:type]} #{self[:grp_name]} #{self[:seqno]}" + end + + # Only needed to satisfy name parameter. + newparam(:name) do + end + + newparam(:afi, namevar: true) do + desc 'The Address-Family Identifier (ipv4|ipv6).' + newvalues(:ipv4, :ipv6) + end + + newparam(:type, namevar: true) do + desc 'Type of the object_group instance.' + newvalues(:address, :port) + end + + newparam(:grp_name, namevar: true) do + desc 'Object Group Entry name' + end + + newparam(:seqno, namevar: true) do + desc 'Sequence number of the entry' + end + + ############## + # Properties # + ############## + + newproperty(:address) do + desc 'Address to match against. Valid values are an IP'\ + ' address/prefix_len, IP Address and wildcard, host and '\ + 'host address' + + validate do |address| + addr_arr = address.split + addr_arr.each do |addr| + next if addr == 'host' + PuppetX::Cisco::Utils.process_network_mask(addr) + end + end + end + + newproperty(:port) do + desc 'port number to match against. valid'\ + ' port configuration should be eq 40 or range 30 50 etc.' + + validate do |port| + fail 'port should be eq , neq, lt, gt or '\ + "range. port: #{port} " unless + /eq \S+|neq \S+|lt \S+|gt \S+|range \S+ \S+/.match(port) + end + end +end diff --git a/tests/beaker_tests/cisco_object_group/test_object_group.rb b/tests/beaker_tests/cisco_object_group/test_object_group.rb new file mode 100644 index 000000000..d536a9439 --- /dev/null +++ b/tests/beaker_tests/cisco_object_group/test_object_group.rb @@ -0,0 +1,107 @@ +############################################################################### +# Copyright (c) 2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# +# See README-develop-beaker-scripts.md (Section: Test Script Variable Reference) +# for information regarding: +# - test script general prequisites +# - command return codes +# - A description of the 'tests' hash and its usage +# +############################################################################### +require File.expand_path('../../lib/utilitylib.rb', __FILE__) + +# Test hash top-level keys +tests = { + master: master, + agent: agent, + platform: 'n(3|7|9)k', + resource_name: 'cisco_object_group_entry', +} + +# Skip -ALL- tests if a top-level platform/os key exludes this platform +skip_unless_supported(tests) + +tests[:seq_10_v4] = { + title_pattern: 'ipv4 address beaker 10', + manifest_props: { + address: '1.2.3.4 2.3.4.5' + }, +} + +tests[:seq_10_v6] = { + desc: 'IPv6 Seq 10', + title_pattern: 'ipv6 address beaker6 10', + manifest_props: { + address: '1:1::1/64' + }, +} + +tests[:seq_20_v4] = { + title_pattern: 'ipv4 port beakerp 20', + manifest_props: { + port: 'eq 40' + }, +} + +tests[:seq_30_v4] = { + desc: 'IPv4 Seq 30', + title_pattern: 'ipv4 port beakerp 30', + manifest_props: { + port: 'range 300 550' + }, +} + +def dependency_manifest(_tests, _id) + " + cisco_object_group { 'ipv4 address beaker': + ensure => present, + } + + cisco_object_group { 'ipv6 address beaker6': + ensure => present, + } + + cisco_object_group { 'ipv4 port beakerp': + ensure => present, + } + " +end + +def cleanup + logger.info('Testcase Cleanup:') + resource_absent_cleanup(agent, 'cisco_object_group_entry') + resource_absent_cleanup(agent, 'cisco_object_group') +end + +################################################################# +# TEST CASE EXECUTION +################################################################# +test_name "TestCase :: #{tests[:resource_name]}" do + cleanup + teardown { cleanup } + + # --------------------------------------------------------- + logger.info("\n#{'-' * 60}\nSection 1. ObjectGroup Testing") + + test_harness_run(tests, :seq_10_v4) + test_harness_run(tests, :seq_10_v6) + test_harness_run(tests, :seq_20_v4) + test_harness_run(tests, :seq_30_v4) + + # --------------------------------------------------------- + skipped_tests_summary(tests) +end +logger.info("TestCase :: #{tests[:resource_name]} :: End") From 4cc0b633a9edd775a1e3833d2d7c1610993b76b0 Mon Sep 17 00:00:00 2001 From: sai chintalapudi <saichint@cisco.com> Date: Wed, 5 Jul 2017 14:38:08 -0700 Subject: [PATCH 173/203] fix readme for object_group --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 23d2f0393..0fcb355c5 100644 --- a/README.md +++ b/README.md @@ -2970,7 +2970,7 @@ The Address to match against. Valid values are type String, which must be one of * An IPv4/IPv6 address/prefix length * The keyword `host` and a host address -* An IP Address and wildcard +* An IPv4 Address and wildcard | Examples |:-- @@ -2978,7 +2978,6 @@ The Address to match against. Valid values are type String, which must be one of | `address => '10.10.10.1 11.12.13.14'` | `address => 'host 10.0.0.1'` | `address => '2000::1/64'` -| `address => '2000::1 2002::1'` | `address => 'host 2001::1'` ##### `port` From b7dc1cc7dcaff1c0e46fb2ef60fc2037ef0a7b3f Mon Sep 17 00:00:00 2001 From: rahushen <rahul.shenoy.86@gmail.com> Date: Thu, 6 Jul 2017 13:08:25 -0400 Subject: [PATCH 174/203] Add separate test for match_src_proto (#461) --- .../cisco_route_map/test_route_map.rb | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/beaker_tests/cisco_route_map/test_route_map.rb b/tests/beaker_tests/cisco_route_map/test_route_map.rb index 3f454dc25..33af52196 100644 --- a/tests/beaker_tests/cisco_route_map/test_route_map.rb +++ b/tests/beaker_tests/cisco_route_map/test_route_map.rb @@ -24,6 +24,14 @@ require File.expand_path('../../lib/utilitylib.rb', __FILE__) +# In I7 match_src_proto order is not maintained in running config. +# This behavior is currently observed only on the N9K. +if platform[/n9k/] && image_version.to_s.strip[/I7/] + @src_proto = %w(udp igmp tcp) +else + @src_proto = %w(tcp udp igmp) +end + # Test hash top-level keys tests = { master: master, @@ -290,7 +298,6 @@ match_route_type_nssa_external: 'true', match_route_type_type_1: 'true', match_route_type_type_2: 'true', - match_src_proto: %w(tcp udp igmp), match_tag: %w(5 342 28 3221), match_vlan: '32, 45-200, 300-399, 402', set_as_path_prepend: ['55.77', '12', '45.3'], @@ -406,6 +413,17 @@ }, } +tests[:non_default_6] = { + desc: '2.6 Non Defaults 6', + title_pattern: 'rm6 321 permit', + manifest_props: { + match_src_proto: %w(tcp udp igmp) + }, + resource: { + match_src_proto: @src_proto + }, +} + def unsupp_n3k unprops = [] unprops << @@ -573,6 +591,7 @@ def cleanup(agent) test_harness_run(tests, :non_default_3) test_harness_run(tests, :non_default_4) test_harness_run(tests, :non_default_5) + test_harness_run(tests, :non_default_6) end logger.info("TestCase :: #{tests[:resource_name]} :: End") From a33e3f944e5d76debab05fffc82e859fd42d60a6 Mon Sep 17 00:00:00 2001 From: Rahul Shenoy <rahushen@cisco.com> Date: Fri, 4 Aug 2017 11:49:27 -0400 Subject: [PATCH 175/203] Fix rubocop errors --- tests/beaker_tests/lib/utilitylib.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 49e25a0f2..c07699be0 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -73,6 +73,9 @@ def hash_to_patterns(hash) # Becomes: # \[\['192.168.5.0\/24', 'nrtemap1'\], \['192.168.6.0\/32'\]\] if /^\[.*\]$/.match(value) + # Puppet 5 - Line wrap + value.gsub!(/^[\[]/, '[\n? *').gsub!(', [', ',\n? +?[') + # END Puppet 5 - Line wrap value.gsub!(/[\[\]]/) { |s| '\\' + "#{s}" }.gsub!(/\"/) { |_s| '\'' } end value.gsub!(/[\(\)]/) { |s| '\\' + "#{s}" } if /\(.*\)/.match(value) From 5df2eeeda16324de2ff632e94846b75f40ae79a8 Mon Sep 17 00:00:00 2001 From: Rahul Shenoy <rahushen@cisco.com> Date: Fri, 4 Aug 2017 11:55:38 -0400 Subject: [PATCH 176/203] Revert "Fix rubocop errors" This reverts commit a33e3f944e5d76debab05fffc82e859fd42d60a6. --- tests/beaker_tests/lib/utilitylib.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index c07699be0..49e25a0f2 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -73,9 +73,6 @@ def hash_to_patterns(hash) # Becomes: # \[\['192.168.5.0\/24', 'nrtemap1'\], \['192.168.6.0\/32'\]\] if /^\[.*\]$/.match(value) - # Puppet 5 - Line wrap - value.gsub!(/^[\[]/, '[\n? *').gsub!(', [', ',\n? +?[') - # END Puppet 5 - Line wrap value.gsub!(/[\[\]]/) { |s| '\\' + "#{s}" }.gsub!(/\"/) { |_s| '\'' } end value.gsub!(/[\(\)]/) { |s| '\\' + "#{s}" } if /\(.*\)/.match(value) From 7f2cc5469414fae565334f4cb75bed3129f963c3 Mon Sep 17 00:00:00 2001 From: rahushen <rahul.shenoy.86@gmail.com> Date: Tue, 8 Aug 2017 12:17:56 -0400 Subject: [PATCH 177/203] Fix line wrap issue for puppet5 testing (#462) --- tests/beaker_tests/lib/utilitylib.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 49e25a0f2..2f85fd699 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -73,6 +73,9 @@ def hash_to_patterns(hash) # Becomes: # \[\['192.168.5.0\/24', 'nrtemap1'\], \['192.168.6.0\/32'\]\] if /^\[.*\]$/.match(value) + # Handle Puppet 5 line wrap issue + value.gsub!(/^[\[]/, '[\n? *').gsub!(', [', ',\n? +?[') + # END Handle Puppet 5 line wrap issue value.gsub!(/[\[\]]/) { |s| '\\' + "#{s}" }.gsub!(/\"/) { |_s| '\'' } end value.gsub!(/[\(\)]/) { |s| '\\' + "#{s}" } if /\(.*\)/.match(value) From 46886dd07745923f206f4a11eb6b3d113ec3faa0 Mon Sep 17 00:00:00 2001 From: rahushen <rahul.shenoy.86@gmail.com> Date: Fri, 18 Aug 2017 13:54:06 -0400 Subject: [PATCH 178/203] Puppet5 fixes (#464) * Fix line wrap issue for puppet5 testing * Fix test_comman_config testcase * Fix issue where vrfs are not removed * fix rubocop errors * Adding comments to document change --- .../cisco_command_config/test_command_config.rb | 15 ++++++++++++++- tests/beaker_tests/lib/utilitylib.rb | 16 ++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/tests/beaker_tests/cisco_command_config/test_command_config.rb b/tests/beaker_tests/cisco_command_config/test_command_config.rb index 7847c149a..20d5e8b27 100644 --- a/tests/beaker_tests/cisco_command_config/test_command_config.rb +++ b/tests/beaker_tests/cisco_command_config/test_command_config.rb @@ -192,8 +192,21 @@ def test_set_get logger.info('* check config') on(agent, cmd_prefix + "test_get='incl loopback1'") + # The output of test_get has changed in Puppet5 and newer versions of Puppet. + # Old output: + # cisco_command_config { 'cc': + # test_get => ' + # interface loopback1 + # interface loopback10 + # ', + # } + # New output: + # cisco_command_config { 'cc': + # test_get => "\ninterface loopback1\ninterface loopback10\n", + # } + # Modifying the below regular expression to make ^ and \n optional. fail_test("TestStep :: set/get :: FAIL\nstdout:\n#{stdout}") unless - stdout[/^interface loopback1/] + stdout[/^?\n?interface loopback1/] logger.info("#{stepinfo} :: PASS\n#{'-' * 60}\n") end diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 2f85fd699..fac0b6250 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1463,8 +1463,20 @@ def remove_all_vlans(agent, stepinfo='Remove all vlans & bridge-domains') end def remove_all_vrfs(agent) - found = test_get(agent, "incl 'vrf context' | excl management").split("\n") - found.map! { |cmd| "no #{cmd}" if cmd[/^vrf context/] } + # The output of test_get has changed in Puppet5 and newer versions of Puppet. + # Old output: + # cisco_command_config { 'cc': + # test_get => ' + # vrf context blue + # ', + # } + # New output: + # cisco_command_config { 'cc': + # test_get => "\nvrf context blue\n", + # } + # Modifying the below regular expression to make ^ and \n optional. + found = test_get(agent, "incl 'vrf context' | excl management").split('\\n') + found.map! { |cmd| "no #{cmd}" if cmd[/^?\n?vrf context/] } test_set(agent, found.compact.join(' ; ')) end From b40dad5516a1bc3f463db032a64ecd487b5889ba Mon Sep 17 00:00:00 2001 From: saichint <saichint@cisco.com> Date: Tue, 22 Aug 2017 11:06:20 -0700 Subject: [PATCH 179/203] Fix for CISCO-50, CISCO-51, CISCO-52 (#463) * snmp_user edge case bug fix * validation for remark/log/established for ace * review comments * take care of resource absent case for admin user * fix netdev snmp user --- lib/puppet/provider/cisco_snmp_user/cisco.rb | 8 +++++++- lib/puppet/provider/snmp_user/cisco.rb | 8 +++++++- lib/puppet/type/cisco_aaa_authorization_login_cfg_svc.rb | 2 +- lib/puppet/type/cisco_aaa_authorization_login_exec_svc.rb | 2 +- lib/puppet/type/cisco_ace.rb | 8 ++++++++ 5 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/puppet/provider/cisco_snmp_user/cisco.rb b/lib/puppet/provider/cisco_snmp_user/cisco.rb index aed31dd1d..589cc142a 100644 --- a/lib/puppet/provider/cisco_snmp_user/cisco.rb +++ b/lib/puppet/provider/cisco_snmp_user/cisco.rb @@ -214,13 +214,19 @@ def handle_attribute_update end def unconfigure_snmp_user - @snmp_user.destroy + # admin user is always present on the device and it + # cannot be removed by using 'no' cmd or else + # errors will be thrown + @snmp_user.destroy unless @resource[:user] == 'admin' @snmp_user = nil @property_hash[:ensure] = :absent end def flush if @property_flush[:ensure] == :absent + fail ArgumentError, + 'The admin account cannot be deactivated on this platform. ' \ + if @resource[:user] == 'admin' unconfigure_snmp_user elsif @property_flush[:ensure] == :present configure_snmp_user diff --git a/lib/puppet/provider/snmp_user/cisco.rb b/lib/puppet/provider/snmp_user/cisco.rb index e07e80415..d512b835e 100644 --- a/lib/puppet/provider/snmp_user/cisco.rb +++ b/lib/puppet/provider/snmp_user/cisco.rb @@ -116,7 +116,13 @@ def validate def flush validate - @snmpuser.destroy if @snmpuser + if @resource[:name] == 'admin' + fail ArgumentError, + 'The admin account cannot be deactivated on this platform. ' \ + if @property_flush[:ensure] == :absent + else + @snmpuser.destroy if @snmpuser + end @snmpuser = nil return if @property_flush[:ensure] == :absent diff --git a/lib/puppet/type/cisco_aaa_authorization_login_cfg_svc.rb b/lib/puppet/type/cisco_aaa_authorization_login_cfg_svc.rb index af8796ae8..d9f6766c7 100644 --- a/lib/puppet/type/cisco_aaa_authorization_login_cfg_svc.rb +++ b/lib/puppet/type/cisco_aaa_authorization_login_cfg_svc.rb @@ -81,7 +81,7 @@ def self.title_patterns end munge do |val| - val == 'default' ? :default : val.split + val == 'default' ? :default : val end def in_sync?(is) diff --git a/lib/puppet/type/cisco_aaa_authorization_login_exec_svc.rb b/lib/puppet/type/cisco_aaa_authorization_login_exec_svc.rb index 87b1118da..f07a4dd98 100644 --- a/lib/puppet/type/cisco_aaa_authorization_login_exec_svc.rb +++ b/lib/puppet/type/cisco_aaa_authorization_login_exec_svc.rb @@ -81,7 +81,7 @@ def self.title_patterns end munge do |val| - val == 'default' ? :default : val.split + val == 'default' ? :default : val end def in_sync?(is) diff --git a/lib/puppet/type/cisco_ace.rb b/lib/puppet/type/cisco_ace.rb index 11512e7c9..c6525bfac 100644 --- a/lib/puppet/type/cisco_ace.rb +++ b/lib/puppet/type/cisco_ace.rb @@ -291,4 +291,12 @@ def name desc 'Log matches against this entry' newvalues(:true, :false) end + + validate do + unless self[:remark].nil? + fail ArgumentError, + "'established' and 'log' properties should not be set for remark ace" unless + self[:log].nil? && self[:established].nil? + end + end end From 4a23f8338cd69cf0f06662d5deb32dca63869fe9 Mon Sep 17 00:00:00 2001 From: mikewiebe <mwiebe@cisco.com> Date: Thu, 24 Aug 2017 10:26:32 -0400 Subject: [PATCH 180/203] Fix nexus_image method --- tests/beaker_tests/lib/utilitylib.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index fac0b6250..f0ab83807 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1142,8 +1142,8 @@ def image?(reset_cache=false) @image = nil # Cache the lookup result def nexus_image - facter_opt = '-p cisco.images.system_image' - image_regexp = /.*\.(\S+\.\S+)\.bin/ + facter_opt = '-p cisco.images.full_version' + image_regexp = /(\S+)/ data = on(agent, facter_cmd(facter_opt)).output @image ||= image_regexp.match(data)[1] end From 216d16555232baedecb670011f1811f330f19fce Mon Sep 17 00:00:00 2001 From: mikewiebe <mwiebe@cisco.com> Date: Thu, 24 Aug 2017 16:35:03 -0400 Subject: [PATCH 181/203] Revert change to remove_all_vrfs method --- tests/beaker_tests/lib/utilitylib.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index f0ab83807..b86fe7e82 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1475,7 +1475,7 @@ def remove_all_vrfs(agent) # test_get => "\nvrf context blue\n", # } # Modifying the below regular expression to make ^ and \n optional. - found = test_get(agent, "incl 'vrf context' | excl management").split('\\n') + found = test_get(agent, "incl 'vrf context' | excl management").split("\n") found.map! { |cmd| "no #{cmd}" if cmd[/^?\n?vrf context/] } test_set(agent, found.compact.join(' ; ')) end From 88306a51c724670882869dc7ab6641c6d686070d Mon Sep 17 00:00:00 2001 From: mikewiebe <mwiebe@cisco.com> Date: Mon, 28 Aug 2017 13:14:43 -0400 Subject: [PATCH 182/203] Revert change to remove_all_vrfs --- tests/beaker_tests/lib/utilitylib.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index b86fe7e82..f0ab83807 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1475,7 +1475,7 @@ def remove_all_vrfs(agent) # test_get => "\nvrf context blue\n", # } # Modifying the below regular expression to make ^ and \n optional. - found = test_get(agent, "incl 'vrf context' | excl management").split("\n") + found = test_get(agent, "incl 'vrf context' | excl management").split('\\n') found.map! { |cmd| "no #{cmd}" if cmd[/^?\n?vrf context/] } test_set(agent, found.compact.join(' ; ')) end From 1ef1b3e199ba877fa5f8c94abbfebdb44ff144cd Mon Sep 17 00:00:00 2001 From: mikewiebe <mwiebe@cisco.com> Date: Thu, 21 Sep 2017 11:32:31 -0400 Subject: [PATCH 183/203] Inital attempt --- tests/beaker_tests/lib/utilitylib.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index f0ab83807..dc3b19568 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1475,9 +1475,14 @@ def remove_all_vrfs(agent) # test_get => "\nvrf context blue\n", # } # Modifying the below regular expression to make ^ and \n optional. - found = test_get(agent, "incl 'vrf context' | excl management").split('\\n') - found.map! { |cmd| "no #{cmd}" if cmd[/^?\n?vrf context/] } - test_set(agent, found.compact.join(' ; ')) + #found = test_get(agent, "incl 'vrf context' | excl management").split('\\n') + found = test_get(agent, "incl 'vrf context' | excl management") + if /.*test_get => \'(.*)\'/m.match(found) + vrfs = /.*test_get => \'(.*)\'/m.match(found)[1].split("\n") + vrfs.map! { |cmd| "no #{cmd}" if cmd[/^?\n?vrf context/] } + puts "VRFS:\n#{vrfs}\nVRFS:" + test_set(agent, vrfs.compact.join(' ; ')) + end end # Return yum patch version from host From 0e0cc48523f84d1990f274611388d1dee3a3d6bb Mon Sep 17 00:00:00 2001 From: mikewiebe <mwiebe@cisco.com> Date: Thu, 21 Sep 2017 12:08:51 -0400 Subject: [PATCH 184/203] Make rubocop happy --- tests/beaker_tests/lib/utilitylib.rb | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index dc3b19568..3b45792d7 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1475,14 +1475,12 @@ def remove_all_vrfs(agent) # test_get => "\nvrf context blue\n", # } # Modifying the below regular expression to make ^ and \n optional. - #found = test_get(agent, "incl 'vrf context' | excl management").split('\\n') found = test_get(agent, "incl 'vrf context' | excl management") - if /.*test_get => \'(.*)\'/m.match(found) - vrfs = /.*test_get => \'(.*)\'/m.match(found)[1].split("\n") - vrfs.map! { |cmd| "no #{cmd}" if cmd[/^?\n?vrf context/] } - puts "VRFS:\n#{vrfs}\nVRFS:" - test_set(agent, vrfs.compact.join(' ; ')) - end + return unless /.*test_get => \'(.*)\'/m.match(found) + vrfs = /.*test_get => \'(.*)\'/m.match(found)[1].split("\n") + vrfs.map! { |cmd| "no #{cmd}" if cmd[/^?\n?vrf context/] } + puts "VRFS:\n#{vrfs}\nVRFS:" + test_set(agent, vrfs.compact.join(' ; ')) end # Return yum patch version from host From b46d812a1c6cf71ed754710062068550366cecc3 Mon Sep 17 00:00:00 2001 From: mikewiebe <mwiebe@cisco.com> Date: Thu, 21 Sep 2017 15:34:25 -0400 Subject: [PATCH 185/203] Working solution --- tests/beaker_tests/lib/utilitylib.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 3b45792d7..12410662d 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1476,10 +1476,10 @@ def remove_all_vrfs(agent) # } # Modifying the below regular expression to make ^ and \n optional. found = test_get(agent, "incl 'vrf context' | excl management") - return unless /.*test_get => \'(.*)\'/m.match(found) - vrfs = /.*test_get => \'(.*)\'/m.match(found)[1].split("\n") - vrfs.map! { |cmd| "no #{cmd}" if cmd[/^?\n?vrf context/] } - puts "VRFS:\n#{vrfs}\nVRFS:" + found.gsub!(/\\n/, ' ') + vrfs = found.scan(/(vrf context \S+)/) + return if vrfs.empty? + vrfs.flatten!.map! { |cmd| "no #{cmd}" if cmd[/^?\n?vrf context/] } test_set(agent, vrfs.compact.join(' ; ')) end From 7c78896f3e37101c07dc1a6bd3cdf02fa12a8e31 Mon Sep 17 00:00:00 2001 From: Mike Wiebe <mwiebe@cisco.com> Date: Fri, 22 Sep 2017 09:45:52 -0400 Subject: [PATCH 186/203] Update utilitylib.rb --- tests/beaker_tests/lib/utilitylib.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 12410662d..7f7516a2d 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1474,7 +1474,7 @@ def remove_all_vrfs(agent) # cisco_command_config { 'cc': # test_get => "\nvrf context blue\n", # } - # Modifying the below regular expression to make ^ and \n optional. + # The following logic handles both output styles. found = test_get(agent, "incl 'vrf context' | excl management") found.gsub!(/\\n/, ' ') vrfs = found.scan(/(vrf context \S+)/) From 743caeb76cead4a5975cee0aac2ac873c7fe6cc3 Mon Sep 17 00:00:00 2001 From: saichint <saichint@cisco.com> Date: Fri, 22 Sep 2017 09:43:37 -0700 Subject: [PATCH 187/203] bridge_domain fixes (#467) * fix for default bd name * add default test for bd * add defaults for fabric control and shutdown * fix default bd name to empty * fix docs --- README.md | 4 ++-- lib/puppet/type/cisco_bridge_domain.rb | 13 ++++--------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 0fcb355c5..147930f16 100644 --- a/README.md +++ b/README.md @@ -1701,13 +1701,13 @@ Determines whether or not the config should be present on the device. Valid valu ID of the Bridge Domain. Valid values are integer. ##### `bd_name` -The bridge-domain name. Valid values are String or keyword 'default'. +The bridge-domain name. Valid values are String or keyword 'default'. When the bd_name is set to 'default', this property is NOT idempotent. ##### `shutdown` Specifies the shutdown state of the bridge-domain. Valid values are true, false, 'default'. ##### `fabric_control` -Specifies this bridge-domain as the fabric control bridge-domain. Only one bridge-domain or VLAN can be configured as fabric-control. Valid values are true, false. +Specifies this bridge-domain as the fabric control bridge-domain. Only one bridge-domain or VLAN can be configured as fabric-control. Valid values are true, false, keyword 'default. -- ### Type: cisco_bridge_domain_vni diff --git a/lib/puppet/type/cisco_bridge_domain.rb b/lib/puppet/type/cisco_bridge_domain.rb index 450d19d55..8b6c3d571 100644 --- a/lib/puppet/type/cisco_bridge_domain.rb +++ b/lib/puppet/type/cisco_bridge_domain.rb @@ -79,8 +79,7 @@ def self.title_patterns desc "The bridge-domain name. Valid values are String or keyword 'default'." munge do |value| - fail 'BD Name is not a valid string' unless value.is_a?(String) - value = :default if value == 'default' + value = '' if value == 'default' value end end # property name @@ -88,18 +87,14 @@ def self.title_patterns newproperty(:fabric_control) do desc %(Specifies this bridge-domain as the fabric control bridge-domain. Only one bridge-domain or VLAN can be configured as fabric-control. - Valid values are true, false.) + Valid values are true, false, 'default'.) - newvalues( - :true, - :false) + newvalues(:true, :false, :default) end # property fabric_control newproperty(:shutdown) do desc "Specifies the shutdown state of the bridge-domain. Valid values are true, false, 'default'." - newvalues( - :true, - :false) + newvalues(:true, :false, :default) end # property shutdown end # Puppet::Type.newtype From cf3f51f849a703cec7914c60846147eb4850d873 Mon Sep 17 00:00:00 2001 From: saichint <saichint@cisco.com> Date: Mon, 25 Sep 2017 06:14:26 -0700 Subject: [PATCH 188/203] remove peerlist and ingress replication for n7k (#469) --- README.md | 4 ++-- .../test_vxlan_vtep_vni.rb | 20 ++++++++----------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 147930f16..35b15b014 100644 --- a/README.md +++ b/README.md @@ -4717,8 +4717,8 @@ Creates a Virtual Network Identifier member (VNI) for an NVE overlay interface. | Property | Caveat Description | |---------------------------------|--------------------------------------| -| ingress_replication | Not supported on N3k, N5k, N6k <br> Supported in OS Version 8.1.1 and later on N7k | -| peer_list | Not supported on N3k, N5k, N6k <br> Supported in OS Version 8.1.1 and later on N7k | +| ingress_replication | Not supported on N3k, N5k, N6k, N7k | +| peer_list | Not supported on N3k, N5k, N6k, N7k | | suppress_uuc | Not supported on N3k, N9k, N9k-F <br> Supported in OS Version 8.1.1 and later on N7k | #### Parameters diff --git a/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb b/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb index 47912301c..18b3174c4 100644 --- a/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb +++ b/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb @@ -70,7 +70,7 @@ tests[:default_properties_ingress_replication] = { desc: '1.1 Default Properties Ingress replication', title_pattern: 'nve1 10000', - platform: 'n(7|9)k', + platform: 'n9k', manifest_props: { ingress_replication: 'default', suppress_arp: 'default', @@ -99,7 +99,7 @@ tests[:ingress_replication_static_peer_list_empty] = { desc: '2.1 Ingress Replication Static Peer List Empty', title_pattern: 'nve1 10000', - platform: 'n(7|9)k', + platform: 'n9k', manifest_props: { ingress_replication: 'static', peer_list: [], @@ -114,7 +114,7 @@ tests[:peer_list] = { desc: '2.2 Peer List', title_pattern: 'nve1 10000', - platform: 'n(7|9)k', + platform: 'n9k', manifest_props: { ingress_replication: 'static', peer_list: ['1.1.1.1', '2.2.2.2', '3.3.3.3'], @@ -129,7 +129,7 @@ tests[:peer_list_change_add] = { desc: '2.3 Peer List Change Add', title_pattern: 'nve1 10000', - platform: 'n(7|9)k', + platform: 'n9k', manifest_props: { ingress_replication: 'static', peer_list: ['1.1.1.1', '6.6.6.6', '3.3.3.3', '4.4.4.4'], @@ -144,7 +144,7 @@ tests[:peer_list_default] = { desc: '2.4 Peer List Default', title_pattern: 'nve1 10000', - platform: 'n(7|9)k', + platform: 'n9k', manifest_props: { ingress_replication: 'static', peer_list: 'default', @@ -159,7 +159,7 @@ tests[:ingress_replication_bgp] = { desc: '2.5 Ingress replication BGP', title_pattern: 'nve1 10000', - platform: 'n(7|9)k', + platform: 'n9k', manifest_props: { ingress_replication: 'bgp', suppress_arp: 'default', @@ -241,7 +241,7 @@ def dependency_manifest(_tests, _id) def unsupported_properties(_tests, _id) unprops = [] - if platform[/n(5|6)k/] + if platform[/n(5|6|7)k/] unprops << :ingress_replication << :peer_list @@ -254,11 +254,7 @@ def unsupported_properties(_tests, _id) def version_unsupported_properties(_tests, _id) unprops = {} - if platform[/n7k/] - unprops[:ingress_replication] = '8.1.1' - unprops[:peer_list] = '8.1.1' - unprops[:suppress_uuc] = '8.1.1' - end + unprops[:suppress_uuc] = '8.1.1' if platform[/n7k/] unprops end From 37c822cdbaa1376d7bfd0fcda0d7cc1798836b34 Mon Sep 17 00:00:00 2001 From: saichint <saichint@cisco.com> Date: Wed, 11 Oct 2017 11:29:04 -0700 Subject: [PATCH 189/203] yet another cache stuff (#471) --- lib/facter/cisco.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/facter/cisco.rb b/lib/facter/cisco.rb index cc759fd26..a4751f8d9 100644 --- a/lib/facter/cisco.rb +++ b/lib/facter/cisco.rb @@ -28,7 +28,6 @@ hash['hardware']['cpu'] = Platform.cpu hash['hardware']['memory'] = Platform.memory hash['hardware']['board'] = Platform.board - hash['hardware']['uptime'] = Platform.uptime hash['hardware']['last_reset'] = Platform.last_reset hash['hardware']['reset_reason'] = Platform.reset_reason @@ -49,6 +48,7 @@ hash['feature_compatible_module_iflist'] = {} interface_list = Feature.compatible_interfaces('fabricpath') hash['feature_compatible_module_iflist']['fabricpath'] = interface_list + hash['hardware']['uptime'] = Platform.uptime hash end From 9810ea69aae9a1016fcbab80c4e47d44fe71cb77 Mon Sep 17 00:00:00 2001 From: Mike Wiebe <mwiebe@cisco.com> Date: Mon, 23 Oct 2017 14:59:10 -0400 Subject: [PATCH 190/203] ensure => absent for physical cisco_interface provider (#470) * Add physial interface default check * Use @nu object to check default state * Update cisco.rb * Fix L2 interface tests * Fix L3 interface tests * Remove default description test * Fix test_interface_capabilities * Fix test_interface_private_vlan * Fix test_interface_stp * Fix test_interface_svi * Update CHANGELOG.md * Document new cisco_interface behavior for ethernet interfaces * Add default value for skip_idempotence_check arg * Add --trace option to puppet agent run * Fix test_interface_stp * Create interface object for physical ints * Don't manage nve interfaces with cisco_interface type * Raise error for nve interfaces * Update cisco_interface docs * Update copyright for cisco_interface provider * Update copyrights * Update CHANGELOG.md * Update README.md * Update README Copyright --- CHANGELOG.md | 3 +++ README.md | 10 +++++++++- lib/puppet/provider/cisco_interface/cisco.rb | 18 ++++++++++++++---- .../cisco_interface/test_interface_L2.rb | 12 ++++++++++-- .../cisco_interface/test_interface_L3.rb | 7 ++++--- .../test_interface_capabilities.rb | 3 ++- .../test_interface_private_vlan.rb | 6 ++++-- .../cisco_interface/test_interface_stp.rb | 7 +++++-- .../cisco_interface/test_interface_svi.rb | 8 +++++--- tests/beaker_tests/lib/utilitylib.rb | 12 ++++++------ 10 files changed, 62 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d46ba724..6c8cf80be 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added ### Changed +- `cisco_interface` Refactored to allow physical ethernet interfaces to be managed as ensurable resources. + - `ensure => absent` for physical interfaces will put the interface into a default state. + - `ensure => absent` for logical interfaces will cause them to be destroyed. ### Removed diff --git a/README.md b/README.md index 35b15b014..29d7d04b5 100644 --- a/README.md +++ b/README.md @@ -2063,6 +2063,7 @@ Manages a Cisco Network Interface. Any resource dependency should be run before | `load_interval_counter_2_delay` | Minimum puppet module version 1.6.0 | | `load_interval_counter_3_delay` | Minimum puppet module version 1.6.0 | | `purge_config` | Minimum puppet module version 1.7.0 | +| Ensure absent for ethernet interfaces | Minimum puppet module version 1.8.0 | #### Parameters @@ -2072,6 +2073,13 @@ Manages a Cisco Network Interface. Any resource dependency should be run before Determine whether the interface config should be present or not. Valid values are 'present' and 'absent'. +Version `1.8.0` of the module allows physical ethernet interfaces to be managed as ensurable resources. + +Notes about `ensure => present` and `ensure => absent` on physical ethernet interfaces: +* `ensure => present` along with non-default property values will put the interface into a non-default state. +* `ensure => absent` will put the interface into a default state. +* Physical interfaces will be displayed as `ensure => absent` by the `puppet resource` command when they are in a default state. + ###### `interface` Name of the interface on the network element. Valid value is a string. @@ -5536,7 +5544,7 @@ Yum | <https://en.wikipedia.org/wiki/Yellowdog_Updater,_Modified><br><http ## License ~~~text -Copyright (c) 2014-2016 Cisco and/or its affiliates. +Copyright (c) 2014-2017 Cisco and/or its affiliates. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/lib/puppet/provider/cisco_interface/cisco.rb b/lib/puppet/provider/cisco_interface/cisco.rb index 1884a1fc9..6780feadb 100644 --- a/lib/puppet/provider/cisco_interface/cisco.rb +++ b/lib/puppet/provider/cisco_interface/cisco.rb @@ -1,6 +1,6 @@ # May 2015 # -# Copyright (c) 2015-2016 Cisco and/or its affiliates. +# Copyright (c) 2015-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -206,8 +206,10 @@ def self.instances interfaces = [] Cisco::Interface.interfaces.each do |interface_name, nu_obj| begin - # Not allowed to create an interface for mgmt0 or MgmtEth0/* - next if interface_name.match(/mgmt/i) + # Some interfaces cannot or should not be managed by this type. + # - Management Interface + # - NVE Interfaces (managed by cisco_vxlan_vtep type) + next if interface_name.match(/mgmt|nve/i) interfaces << properties_get(interface_name, nu_obj) end end @@ -223,6 +225,10 @@ def self.prefetch(resources) end # self.prefetch def exists? + # @nu.state_default returns true if @nu is a physical interface and it's + # in a default state. Physical interfaces are treated as resources + # that don't exist if they are in a default state. + return false unless @nu.nil? || !@nu.state_default (@property_hash[:ensure] == :present) end @@ -352,12 +358,16 @@ def vrf=(val) end def flush + if @resource['name'][/nve/] + msg = 'Use the cisco_vxlan_vtep type to manage nve interfaces' + fail msg + end if @property_flush[:ensure] == :absent @nu.destroy @nu = nil else # Create/Update - if @nu.nil? + if @nu.nil? || @nu.state_default new_interface = true @nu = Cisco::Interface.new(@resource[:interface]) end diff --git a/tests/beaker_tests/cisco_interface/test_interface_L2.rb b/tests/beaker_tests/cisco_interface/test_interface_L2.rb index e15ce64d9..58c3b00d9 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_L2.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_L2.rb @@ -1,6 +1,6 @@ # rubocop:disable Style/FileName ############################################################################### -# Copyright (c) 2014-2016 Cisco and/or its affiliates. +# Copyright (c) 2014-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -54,11 +54,13 @@ sys_def_switchport: true, sys_def_sw_shut: true, manifest_props: { + description: 'testing default access properties', shutdown: 'default', switchport_autostate_exclude: 'default', switchport_mode: 'access', }, resource: { + description: 'testing default access properties', shutdown: 'true', switchport_autostate_exclude: 'false', switchport_mode: 'access', @@ -85,6 +87,7 @@ sys_def_switchport: true, sys_def_sw_shut: true, manifest_props: { + description: 'testing default trunk properties', shutdown: 'default', load_interval_counter_1_delay: 'default', load_interval_counter_2_delay: 'default', @@ -99,6 +102,7 @@ }, resource: { + description: 'testing default trunk properties', shutdown: 'true', load_interval_counter_1_delay: '30', load_interval_counter_2_delay: '300', @@ -141,6 +145,9 @@ manifest_props: { purge_config: 'true' }, + resource: { + ensure: 'absent' + }, } def unsupported_properties(_tests, _id) @@ -172,7 +179,8 @@ def unsupported_properties(_tests, _id) # ------------------------------------------------------------------- logger.info("\n#{'-' * 60}\nSection 2.3 Purge_config Testing") - test_harness_run(tests, :purge) + skip_idempotence_check = true + test_harness_run(tests, :purge, skip_idempotence_check) end logger.info("TestCase :: #{tests[:resource_name]} :: End") diff --git a/tests/beaker_tests/cisco_interface/test_interface_L3.rb b/tests/beaker_tests/cisco_interface/test_interface_L3.rb index c5e15701b..eff5e51b6 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_L3.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_L3.rb @@ -1,6 +1,6 @@ # rubocop:disable Style/FileName ############################################################################### -# Copyright (c) 2014-2016 Cisco and/or its affiliates. +# Copyright (c) 2014-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -44,11 +44,11 @@ tests[:default] = { desc: '1.1 Default Properties', title_pattern: intf, - code: [0], + code: [0, 2], sys_def_switchport: false, manifest_props: { + description: 'Verify default L3 properties', bfd_echo: 'default', - description: 'default', duplex: 'default', ipv4_forwarding: 'default', ipv4_pim_sparse_mode: 'default', @@ -68,6 +68,7 @@ vrf: 'default', }, resource: { + description: 'Verify default L3 properties', duplex: 'auto', ipv4_forwarding: 'false', ipv4_pim_sparse_mode: 'false', diff --git a/tests/beaker_tests/cisco_interface/test_interface_capabilities.rb b/tests/beaker_tests/cisco_interface/test_interface_capabilities.rb index 89a48a031..6901e5881 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_capabilities.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_capabilities.rb @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2016-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -50,6 +50,7 @@ title_pattern: intf, code: [0, 2], manifest_props: { + description: 'Testing default properties', switchport_mode: 'disabled', # interface_pre_check() will define add'l properties }, diff --git a/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb b/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb index 99edba0eb..df153d614 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2016-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -49,9 +49,10 @@ tests[:default] = { desc: '1.1 Default Properties', title_pattern: intf, - code: [0], + code: [0, 2], sys_def_switchport: true, manifest_props: { + description: 'Test default private vlan properties', switchport_pvlan_host: 'default', switchport_pvlan_host_association: 'default', switchport_pvlan_mapping: 'default', @@ -64,6 +65,7 @@ switchport_pvlan_trunk_secondary: 'default', }, resource: { + description: 'Test default private vlan properties', switchport_pvlan_host: 'false', # switchport_pvlan_host_association: nil, # switchport_pvlan_mapping: nil, diff --git a/tests/beaker_tests/cisco_interface/test_interface_stp.rb b/tests/beaker_tests/cisco_interface/test_interface_stp.rb index 91c90d925..5797ce926 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_stp.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_stp.rb @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2016-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -46,8 +46,10 @@ tests[:default] = { desc: '1.1 Default Properties', title_pattern: intf, - code: [0], + code: [0, 2], manifest_props: { + description: 'Test default properties', + switchport_mode: 'access', stp_bpdufilter: 'default', stp_bpduguard: 'default', stp_cost: 'default', @@ -61,6 +63,7 @@ stp_vlan_port_priority: 'default', }, resource: { + 'description' => 'Test default properties', 'stp_bpdufilter' => 'false', 'stp_bpduguard' => 'false', 'stp_cost' => 'auto', diff --git a/tests/beaker_tests/cisco_interface/test_interface_svi.rb b/tests/beaker_tests/cisco_interface/test_interface_svi.rb index 391cd3f4f..fa5baadbb 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_svi.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_svi.rb @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2014-2016 Cisco and/or its affiliates. +# Copyright (c) 2014-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -46,10 +46,12 @@ desc: "1.1 Default 'mgmt'", title_pattern: intf, manifest_props: { - svi_management: 'default' + description: 'Test default properties', + svi_management: 'default', }, resource: { - svi_management: 'false' + description: 'Test default properties', + svi_management: 'false', }, } diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 7f7516a2d..7bd7ea767 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2014-2016 Cisco and/or its affiliates. +# Copyright (c) 2014-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -137,7 +137,7 @@ def raise_skip_exception(message, testcase) # Full command string for puppet agent def puppet_agent_cmd - PUPPET_BINPATH + 'agent -t' + PUPPET_BINPATH + 'agent -t --trace' end # Auto generation of properties for manifests @@ -178,7 +178,7 @@ def prop_hash_to_manifest(attributes) # Reserved keys # tests[id][:log_desc] - the final form of the log description # -def test_harness_common(tests, id) +def test_harness_common(tests, id, skip_idempotence_check=false) tests[id][:ensure] = :present if tests[id][:ensure].nil? tests[id][:state] = false if tests[id][:state].nil? tests[id][:desc] = '' if tests[id][:desc].nil? @@ -187,7 +187,7 @@ def test_harness_common(tests, id) test_manifest(tests, id) test_resource(tests, id) - test_idempotence(tests, id) + test_idempotence(tests, id) unless skip_idempotence_check tests[id].delete(:log_desc) end @@ -745,7 +745,7 @@ def supported_property_hash(tests, id, property_hash) # - Creates manifests # - Creates puppet resource title strings # - Cleans resource -def test_harness_run(tests, id) +def test_harness_run(tests, id, skip_idempotence_check=false) return unless platform_supports_test(tests, id) logger.info("\n * Process test_harness_run") tests[id][:ensure] = :present if tests[id][:ensure].nil? @@ -763,7 +763,7 @@ def test_harness_run(tests, id) # Check for additional pre-requisites test_harness_dependencies(tests, id) - test_harness_common(tests, id) + test_harness_common(tests, id, skip_idempotence_check) tests[id][:ensure] = nil end From d44f52365bc79602bf00e530ed57060b325da1d8 Mon Sep 17 00:00:00 2001 From: saichint <saichint@cisco.com> Date: Wed, 1 Nov 2017 11:55:12 -0700 Subject: [PATCH 191/203] N3600 support (#473) * make n3600 same as n9k-f * fix tests as well * fix snmp packet size * fix snmp server * bgp event history version independent * fix README * review comment * n3k-f * typo * add vxlan tests for n3k-f * fix properties for vxlan_vtep_vni * fix matrix * copyright fix --- README.md | 414 +++++++++++------- docs/README-agent-install.md | 1 + examples/cisco/demo_acl.pp | 14 +- examples/cisco/demo_bfd.pp | 16 +- examples/cisco/demo_bgp.pp | 4 +- examples/cisco/demo_dhcp_relay_global.pp | 6 +- examples/cisco/demo_interface.pp | 6 +- examples/cisco/demo_portchannel.pp | 6 +- examples/cisco/demo_route_map.pp | 12 +- lib/puppet/parser/functions/platform_get.rb | 6 +- lib/puppet/provider/cisco_bgp/cisco.rb | 90 +--- lib/puppet/type/cisco_bgp.rb | 39 +- lib/puppet_x/cisco/cmnutils.rb | 2 +- .../cisco_bfd_global/test_bfd_global.rb | 2 +- tests/beaker_tests/cisco_bgp/test_bgp.rb | 40 +- tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb | 2 +- .../test_bgpneighboraf.rb | 2 +- .../test_dhcp_relay_global.rb | 4 +- .../cisco_hsrp/test_hsrp_global.rb | 2 +- .../test_interface_private_vlan.rb | 2 +- .../test_interface_portchannel.rb | 4 +- .../test_overlay_global.rb | 4 +- .../test_portchannel_global.rb | 34 +- .../cisco_route_map/test_route_map.rb | 8 +- .../cisco_snmp_server/test_snmp_server.rb | 8 +- .../cisco_stp_global/test_stp_global.rb | 4 +- tests/beaker_tests/cisco_vlan/test_vlan.rb | 2 +- .../beaker_tests/cisco_vrf_af/test_vrf_af.rb | 2 +- .../cisco_vxlan_vtep/test_vxlan_vtep.rb | 2 +- .../test_vxlan_vtep_vni.rb | 16 +- tests/beaker_tests/lib/utilitylib.rb | 7 +- 31 files changed, 407 insertions(+), 354 deletions(-) diff --git a/README.md b/README.md index 29d7d04b5..7044120aa 100644 --- a/README.md +++ b/README.md @@ -394,6 +394,7 @@ Platform | Description | Environments :--|:--|:-- **N9k** | Support includes all N9xxx models | bash-shell, guestshell **N3k** | Support includes N30xx and N31xx models only.<br>The N35xx model is not supported. | bash-shell, guestshell +**N3k-F** | Support includes all N3xxx models running os version 7.0(3)Fx(x) | bash-shell, guestshell **N5k** | Support includes N56xx models only.<br>The N50xx and N55xx models are not supported at this time. | Open Agent Container (OAC) **N6k** | Support includes all N6xxx models | Open Agent Container (OAC) **N7k** | Support includes all N7xxx models | Open Agent Container (OAC) @@ -410,96 +411,96 @@ Symbol | Meaning | Description **Support Matrix** -| ✅ = Supported <br> ➖ = Not Applicable | N9k | N3k | N5k | N6k | N7k | N9k-F | Caveats | -|:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| -| [cisco_aaa_<br>authentication_login](#type-cisco_aaa_authentication_login) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_aaa_<br>authorization_login_cfg_svc](#type-cisco_aaa_authorization_login_cfg_svc) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_aaa_<br>authorization_login_exec_svc](#type-cisco_aaa_authorization_login_exec_svc) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_aaa_group_tacacs](#type-cisco_aaa_group_tacacs) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_acl](#type-cisco_acl) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_ace](#type-cisco_ace) | ✅ | ✅ | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_ace-caveats) | -| [cisco_bfd_global](#type-cisco_bfd_global) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_bfd_global-caveats) | -| [cisco_command_config](#type-cisco_command_config) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_bgp](#type-cisco_bgp) | ✅ | ✅ | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_bgp-caveats) | -| [cisco_bgp_af](#type-cisco_bgp_af) | ✅* | ✅* | ✅ | ✅* | ✅ | ✅ | \*[caveats](#cisco_bgp_af-caveats) | -| [cisco_bgp_af_aa](#type-cisco_bgp_af_aa) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_bgp_neighbor](#type-cisco_bgp_neighbor) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_bgp_neighbor_af](#type-cisco_bgp_neighbor_af) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_bridge_domain](#type-cisco_bridge_domain) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | -| [cisco_bridge_domain_vni](#type-cisco_bridge_domain_vni) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | -| [cisco_dhcp_relay_global](#type-cisco_dhcp_relay_global) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_dhcp_relay_global-caveats) -| [cisco_encapsulation](#type-cisco_encapsulation) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | -| [cisco_evpn_vni](#type-cisco_evpn_vni) | ✅ | ➖ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_evpn_vni-caveats) | -| [cisco_fabricpath_global](#type-cisco_fabricpath_global) | ➖ | ➖ | ✅ | ✅ | ✅* | ➖ | \*[caveats](#cisco_fabricpath_global-caveats) | -| [cisco_fabricpath_topology](#type-cisco_fabricpath_topology) | ➖ | ➖ | ✅ | ✅ | ✅ | ➖ | -| [cisco_hsrp_global](#type-cisco_hsrp_global) | ✅ | ✅* | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_hsrp_global-caveats) | -| [cisco_interface](#type-cisco_interface) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_interface-caveats) | -| [cisco_interface_channel_group](#type-cisco_interface_channel_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_interface_channel_group-caveats) | -| [cisco_interface_hsrp_group](#type-cisco_interface_hsrp_group) | ✅ | ✅ | ➖ | ➖ | ✅* | ✅ | \*[caveats](#cisco_interface_hsrp_group-caveats) | -| [cisco_interface_ospf](#type-cisco_interface_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_interface_portchannel](#type-cisco_interface_portchannel) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_interface_portchannel-caveats) | -| [cisco_interface_service_vni](#type-cisco_interface_service_vni) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | -| [cisco_itd_device_group](#type-cisco_itd_device_group) | ✅ | ➖ | ➖ | ➖ | ✅ | ➖ | -| [cisco_itd_device_group_node](#type-cisco_itd_device_group_node) | ✅ | ➖ | ➖ | ➖ | ✅ | ➖ | -| [cisco_itd_service](#type-cisco_itd_service) | ✅ | ➖ | ➖ | ➖ | ✅ | ➖ | \*[caveats](#cisco_itd_service-caveats) | -| [cisco_object_group](#type-cisco_object_group) | ✅ | ✅ | ➖ | ➖ | ✅ | ✅ | -| [cisco_object_group_entry](#type-cisco_object_group_entry) | ✅ | ✅ | ➖ | ➖ | ✅ | ✅ | -| [cisco_ospf](#type-cisco_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_ospf_vrf](#type-cisco_ospf_vrf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| ✅ = Supported <br> ➖ = Not Applicable | N9k | N3k | N5k | N6k | N7k | N9k-F | Caveats | -| [cisco_overlay_global](#type-cisco_overlay_global) | ✅ | ✅* | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_overlay_global-caveats) | -| [cisco_pim](#type-cisco_pim) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_pim-caveats) | -| [cisco_pim_rp_address](#type-cisco_pim_rp_address) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_pim_grouplist](#type-cisco_pim_grouplist) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_portchannel_global](#type-cisco_portchannel_global) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_portchannel_global-caveats) | -| [cisco_route_map](#type-cisco_route_map) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_route_map-caveats) | -| [cisco_stp_global](#type-cisco_stp_global) | ✅* | ✅* | ✅* | ✅* | ✅ | ✅ | \*[caveats](#cisco_stp_global-caveats) | -| [cisco_snmp_community](#type-cisco_snmp_community) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_snmp_group](#type-cisco_snmp_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_snmp_server](#type-cisco_snmp_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_snmp_user](#type-cisco_snmp_user) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_tacacs_server](#type-cisco_tacacs_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_tacacs_server_host](#type-cisco_tacacs_server_host) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_upgrade](type-cisco_upgrade) | ✅* | ✅* | ➖ | ➖ | ➖ | ✅* | \*[caveats](#cisco_upgrade-caveats) | -| [cisco_vdc](#type-cisco_vdc) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | -| [cisco_vlan](#type-cisco_vlan) | ✅* | ✅* | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_vlan-caveats) | +| ✅ = Supported <br> ➖ = Not Applicable | N9k | N3k | N5k | N6k | N7k | N9k-F | N3k-F | Caveats | +|:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| [cisco_aaa_<br>authentication_login](#type-cisco_aaa_authentication_login) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_aaa_<br>authorization_login_cfg_svc](#type-cisco_aaa_authorization_login_cfg_svc) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_aaa_<br>authorization_login_exec_svc](#type-cisco_aaa_authorization_login_exec_svc) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_aaa_group_tacacs](#type-cisco_aaa_group_tacacs) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_acl](#type-cisco_acl) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_ace](#type-cisco_ace) | ✅ | ✅ | ✅* | ✅* | ✅* | ✅ | ✅ | \*[caveats](#cisco_ace-caveats) | +| [cisco_bfd_global](#type-cisco_bfd_global) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_bfd_global-caveats) | +| [cisco_command_config](#type-cisco_command_config) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_bgp](#type-cisco_bgp) | ✅ | ✅ | ✅* | ✅* | ✅* | ✅ | ✅ | \*[caveats](#cisco_bgp-caveats) | +| [cisco_bgp_af](#type-cisco_bgp_af) | ✅* | ✅* | ✅ | ✅* | ✅ | ✅ | ✅ | \*[caveats](#cisco_bgp_af-caveats) | +| [cisco_bgp_af_aa](#type-cisco_bgp_af_aa) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_bgp_neighbor](#type-cisco_bgp_neighbor) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_bgp_neighbor_af](#type-cisco_bgp_neighbor_af) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_bridge_domain](#type-cisco_bridge_domain) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | ➖ | +| [cisco_bridge_domain_vni](#type-cisco_bridge_domain_vni) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | ➖ | +| [cisco_dhcp_relay_global](#type-cisco_dhcp_relay_global) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_dhcp_relay_global-caveats) +| [cisco_encapsulation](#type-cisco_encapsulation) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | ➖ | +| [cisco_evpn_vni](#type-cisco_evpn_vni) | ✅ | ➖ | ✅ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_evpn_vni-caveats) | +| [cisco_fabricpath_global](#type-cisco_fabricpath_global) | ➖ | ➖ | ✅ | ✅ | ✅* | ➖ | ➖ | \*[caveats](#cisco_fabricpath_global-caveats) | +| [cisco_fabricpath_topology](#type-cisco_fabricpath_topology) | ➖ | ➖ | ✅ | ✅ | ✅ | ➖ | ➖ | +| [cisco_hsrp_global](#type-cisco_hsrp_global) | ✅ | ✅* | ✅ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_hsrp_global-caveats) | +| [cisco_interface](#type-cisco_interface) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_interface-caveats) | +| [cisco_interface_channel_group](#type-cisco_interface_channel_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_interface_channel_group-caveats) | +| [cisco_interface_hsrp_group](#type-cisco_interface_hsrp_group) | ✅ | ✅ | ➖ | ➖ | ✅* | ✅ | ✅ | \*[caveats](#cisco_interface_hsrp_group-caveats) | +| [cisco_interface_ospf](#type-cisco_interface_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_interface_portchannel](#type-cisco_interface_portchannel) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅ | ✅ | \*[caveats](#cisco_interface_portchannel-caveats) | +| [cisco_interface_service_vni](#type-cisco_interface_service_vni) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | ➖ | +| [cisco_itd_device_group](#type-cisco_itd_device_group) | ✅ | ➖ | ➖ | ➖ | ✅ | ➖ | ➖ | +| [cisco_itd_device_group_node](#type-cisco_itd_device_group_node) | ✅ | ➖ | ➖ | ➖ | ✅ | ➖ | ➖ | +| [cisco_itd_service](#type-cisco_itd_service) | ✅ | ➖ | ➖ | ➖ | ✅ | ➖ | ➖ | \*[caveats](#cisco_itd_service-caveats) | +| [cisco_object_group](#type-cisco_object_group) | ✅ | ✅ | ➖ | ➖ | ✅ | ✅ | ✅ | +| [cisco_object_group_entry](#type-cisco_object_group_entry) | ✅ | ✅ | ➖ | ➖ | ✅ | ✅ | ✅ | +| [cisco_ospf](#type-cisco_ospf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_ospf_vrf](#type-cisco_ospf_vrf) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| ✅ = Supported <br> ➖ = Not Applicable | N9k | N3k | N5k | N6k | N7k | N9k-F | N3k-F | Caveats | +| [cisco_overlay_global](#type-cisco_overlay_global) | ✅ | ✅* | ✅ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_overlay_global-caveats) | +| [cisco_pim](#type-cisco_pim) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_pim-caveats) | +| [cisco_pim_rp_address](#type-cisco_pim_rp_address) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_pim_grouplist](#type-cisco_pim_grouplist) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_portchannel_global](#type-cisco_portchannel_global) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_portchannel_global-caveats) | +| [cisco_route_map](#type-cisco_route_map) | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | ✅* | \*[caveats](#cisco_route_map-caveats) | +| [cisco_stp_global](#type-cisco_stp_global) | ✅* | ✅* | ✅* | ✅* | ✅ | ✅ | ✅ | \*[caveats](#cisco_stp_global-caveats) | +| [cisco_snmp_community](#type-cisco_snmp_community) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_snmp_group](#type-cisco_snmp_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_snmp_server](#type-cisco_snmp_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_snmp_user](#type-cisco_snmp_user) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_tacacs_server](#type-cisco_tacacs_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_tacacs_server_host](#type-cisco_tacacs_server_host) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_upgrade](type-cisco_upgrade) | ✅* | ✅* | ➖ | ➖ | ➖ | ✅* | ✅* | \*[caveats](#cisco_upgrade-caveats) | +| [cisco_vdc](#type-cisco_vdc) | ➖ | ➖ | ➖ | ➖ | ✅ | ➖ | ➖ | ➖ | +| [cisco_vlan](#type-cisco_vlan) | ✅* | ✅* | ✅ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_vlan-caveats) | | [cisco_vpc_domain](#type-cisco_vpc_domain) | ✅* | ✅* | ✅* | ✅* | ✅* | ➖ | \*[caveats](#cisco_vpc_domain-caveats) | -| [cisco_vrf](#type-cisco_vrf) | ✅ | ✅* | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_vrf-caveats) | -| [cisco_vrf_af](#type-cisco_vrf_af) | ✅ | ✅* | ✅* | ✅* | ✅* | ✅ | \*[caveats](#cisco_vrf_af-caveats) | -| [cisco_vtp](#type-cisco_vtp) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [cisco_vxlan_vtep](#type-cisco_vxlan_vtep) | ✅ | ➖ | ✅ | ✅ | ✅* | ✅ | \*[caveats](#cisco_vxlan_vtep-caveats) | -| [cisco_vxlan_vtep_vni](#type-cisco_vxlan_vtep_vni) | ✅ | ➖ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_vxlan_vtep_vni-caveats) | +| [cisco_vrf](#type-cisco_vrf) | ✅ | ✅* | ✅ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_vrf-caveats) | +| [cisco_vrf_af](#type-cisco_vrf_af) | ✅ | ✅* | ✅* | ✅* | ✅* | ✅ | ✅ | \*[caveats](#cisco_vrf_af-caveats) | +| [cisco_vtp](#type-cisco_vtp) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [cisco_vxlan_vtep](#type-cisco_vxlan_vtep) | ✅ | ➖ | ✅ | ✅ | ✅* | ✅ | ✅ | \*[caveats](#cisco_vxlan_vtep-caveats) | +| [cisco_vxlan_vtep_vni](#type-cisco_vxlan_vtep_vni) | ✅ | ➖ | ✅ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#cisco_vxlan_vtep_vni-caveats) | ##### NetDev Providers -| ✅ = Supported <br> ➖ = Not Applicable | N9k | N3k | N5k | N6k | N7k | N9k-F | Caveats | -|:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| -| [domain_name](#type-domain_name) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [name_server](#type-name_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [network_dns](#type-network_dns) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [network_interface](#type-network_interface) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [network_snmp](#type-network_snmp) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [network_trunk](#type-network_trunk) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [network_vlan](#type-network_vlan) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [ntp_auth_key](#type-ntp_auth_key) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [ntp_config](#type-ntp_config) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#ntp_config-caveats) -| [ntp_server](#type-ntp_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#ntp_server-caveats) -| [port_channel](#type-port_channel) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [radius](#type-radius) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [radius_global](#type-radius_global) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [radius_server_group](#type-tacacs_server_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [radius_server](#type-radius_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [search_domain](#type-search_domain) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [snmp_community](#type-snmp_community) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [snmp_notification](#type-snmp_notification) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [snmp_notification_receiver](#type-snmp_notification_receiver) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [snmp_user](#type-snmp_user) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [syslog_server](#type-syslog_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [syslog_setting](#type-syslog_setting) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [tacacs](#type-tacacs) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [tacacs_global](#type-tacacs_global) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [tacacs_server](#type-tacacs_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| [tacacs_server_group](#type-tacacs_server_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| ✅ = Supported <br> ➖ = Not Applicable | N9k | N3k | N5k | N6k | N7k | N9k-F | N3k-F | Caveats | +|:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| [domain_name](#type-domain_name) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [name_server](#type-name_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [network_dns](#type-network_dns) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [network_interface](#type-network_interface) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [network_snmp](#type-network_snmp) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [network_trunk](#type-network_trunk) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [network_vlan](#type-network_vlan) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [ntp_auth_key](#type-ntp_auth_key) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [ntp_config](#type-ntp_config) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#ntp_config-caveats) +| [ntp_server](#type-ntp_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | \*[caveats](#ntp_server-caveats) +| [port_channel](#type-port_channel) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [radius](#type-radius) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [radius_global](#type-radius_global) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [radius_server_group](#type-tacacs_server_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [radius_server](#type-radius_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [search_domain](#type-search_domain) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [snmp_community](#type-snmp_community) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [snmp_notification](#type-snmp_notification) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [snmp_notification_receiver](#type-snmp_notification_receiver) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [snmp_user](#type-snmp_user) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [syslog_server](#type-syslog_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [syslog_setting](#type-syslog_setting) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [tacacs](#type-tacacs) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [tacacs_global](#type-tacacs_global) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [tacacs_server](#type-tacacs_server) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| [tacacs_server_group](#type-tacacs_server_group) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -- ### Cisco Resource Type Details @@ -519,6 +520,7 @@ Allows execution of configuration commands. | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### Parameters @@ -551,6 +553,7 @@ Manages AAA Authentication Login configuration. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### Parameters @@ -585,6 +588,7 @@ Manages configuration for Authorization Login Config Service. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### Parameters @@ -613,6 +617,7 @@ Manages configuration for Authorization Login Exec Service. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### Parameters @@ -641,6 +646,7 @@ Manages configuration for a TACACS+ server group. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### Parameters @@ -977,15 +983,16 @@ Manages configuration of a BFD (Bidirectional Forwarding Detection) instance. | N6k | 7.3(0)N1(1) | 1.4.0 | | N7k | 7.3(0)D1(1) | 1.4.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### <a name="cisco_bfd_global-caveats">Caveats</a> | Property | Caveat Description | |:--------|:-------------| | `echo_rx_interval` | Not supported on N5k, N6k | -| `fabricpath_interval` | Not supported on N3k, N9k-F, N9k | -| `fabricpath_slow_timer` | Not supported on N3k, N9k-F, N9k | -| `fabricpath_vlan` | Not supported on N3k, N9k-F, N9k | +| `fabricpath_interval` | Not supported on N3k, N3k-F, N9k-F, N9k | +| `fabricpath_slow_timer` | Not supported on N3k, N3k-F, N9k-F, N9k | +| `fabricpath_vlan` | Not supported on N3k, N3k-F, N9k-F, N9k | | `interval` | Supported on N3k, N5k, N6k, N7k <br> Supported in OS Version 7.0(3)F2(1) and later on N9k-F <br> Supported in OS Version 7.0(3)I6(1) and later on N9k | | `ipv4_echo_rx_interval` | Not supported on N5k, N6k | | `ipv4_interval` | Not supported on N5k, N6k | @@ -1062,6 +1069,9 @@ Manages configuration of a BGP instance. | N5k | 7.3(0)N1(1) | 1.2.0 | | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | +| N9k-f | 7.3(0)F3(2) | 1.8.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### <a name="cisco_bgp-caveats">Caveats</a> @@ -1069,10 +1079,8 @@ Manages configuration of a BGP instance. |:--------|:-------------| | `disable_policy_batching_ipv4` | Not supported on N5k, N6k <br> Supported in OS Version 8.1.1 and later on N7k | | `disable_policy_batching_ipv6` | Not supported on N5k, N6k <br> Supported in OS Version 8.1.1 and later on N7k | -| `event_history_errors ` | supported on N3|9k on 7.0(3)I5(1) and later images | -| `event_history_events ` | default value is 'large' for N3|9k on 7.0(3)I5(1) and later images | -| `event_history_objstore ` | supported on N3|9k on 7.0(3)I5(1) and later images | -| `event_history_periodic ` | default value is 'false' for N3|9k on 7.0(3)I5(1) and later images | +| `event_history_errors ` | Supported in OS Version 8.0.1 and later on N7k <br> Supported in OS Version 7.0(3)I5(1) and later on N3|9k | +| `event_history_objstore ` | Supported in OS Version 8.0.1 and later on N7k <br> Supported in OS Version 7.0(3)I5(1) and later on N3|9k | | `neighbor_down_fib_accelerate` | Not supported on N5k, N6k <br> Supported in OS Version 8.1.1 and later on N7k | | `reconnect_interval` | Not supported on N5k, N6k <br> Supported in OS Version 8.1.1 and later on N7k | | `suppress_fib_pending` | Idempotence supported only on 7.0(3)I5(1) and later images N3|9k | @@ -1133,22 +1141,22 @@ Enable/Disable the batching evaluation of prefix advertisements to all peers wit Enable/Disable enforces the neighbor autonomous system to be the first AS number listed in the AS path attribute for eBGP. Valid values are 'true', 'false', and 'default'. On NX-OS, this property is only supported in the global BGP context. ##### `event_history_cli` -Enable/Disable/specify size of cli event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. Size can also be specified in bytes. +Enable/Disable/specify size of cli event history buffer. Valid values are false', 'size_small', 'size_medium', 'size_large', 'size_disable'. Size can also be specified in bytes. Please Note: Setting this value to 'default' or 'true' has been deprecated in module version 1.8.0. This property is only used for BGP debugging purposes and idempotency is not guaranteed. ##### `event_history_detail` -Enable/Disable/specify size of detail event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. Size can also be specified in bytes. +Enable/Disable/specify size of detail event history buffer. Valid values are 'false', 'size_small', 'size_medium', 'size_large', 'size_disable'. Size can also be specified in bytes. Please Note: Setting this value to 'default' or 'true' has been deprecated in module version 1.8.0. This property is only used for BGP debugging purposes and idempotency is not guaranteed. ##### `event_history_errors` -Enable/Disable/specify size of error history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. Size can also be specified in bytes. +Enable/Disable/specify size of error history buffer. Valid values are 'false', 'size_small', 'size_medium', 'size_large', 'size_disable'. Size can also be specified in bytes. Please Note: Setting this value to 'default' or 'true' has been deprecated in module version 1.8.0. This property is only used for BGP debugging purposes and idempotency is not guaranteed. ##### `event_history_events` -Enable/Disable/specify size of event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. Size can also be specified in bytes. +Enable/Disable/specify size of event history buffer. Valid values are 'false', 'size_small', 'size_medium', 'size_large', 'size_disable'. Size can also be specified in bytes. Please Note: Setting this value to 'default' or 'true' has been deprecated in module version 1.8.0. This property is only used for BGP debugging purposes and idempotency is not guaranteed. ##### `event_history_objstore` -Enable/Disable/specify size of objstore history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. Size can also be specified in bytes. +Enable/Disable/specify size of objstore history buffer. Valid values are 'false', 'size_small', 'size_medium', 'size_large', 'size_disable'. Size can also be specified in bytes. Please Note: Setting this value to 'default' or 'true' has been deprecated in module version 1.8.0. This property is only used for BGP debugging purposes and idempotency is not guaranteed. ##### `event_history_periodic` -Enable/Disable/specify size of periodic event history buffer. Valid values are 'true', 'false', 'size_small', 'size_medium', 'size_large', 'size_disable' and 'default'. Size can also be specified in bytes. +Enable/Disable/specify size of periodic event history buffer. Valid values are 'false', 'size_small', 'size_medium', 'size_large', 'size_disable'. Size can also be specified in bytes. Please Note: Setting this value to 'default' or 'true' has been deprecated in module version 1.8.0. This property is only used for BGP debugging purposes and idempotency is not guaranteed. ##### `fast_external_fallover` Enable/Disable immediately reset the session if the link to a directly connected BGP peer goes down. Valid values are 'true', 'false', and 'default'. On NX-OS, this property is only supported in the global BGP context. @@ -1231,12 +1239,13 @@ Manages configuration of a BGP Address-family instance. | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### <a name="cisco_bgp_af-caveats">Caveats</a> | Property | Caveat Description | |:--------|:-------------| -| `additional_paths_install` | Not supported on N3k, N9k-F, N9k | +| `additional_paths_install` | Not supported on N3k, N3k-F, N9k-F, N9k | | `advertise_l2vpn_evpn` | Not supported on N3k, N6k | | address-family `l2vpn/evpn` | Module Minimum Version 1.3.2 <br> OS Minimum Version 7.0(3)I3(1) <br> Not supported on N3k | @@ -1411,6 +1420,7 @@ Manages configuration of a BGP Address-family Aggregate-address instance. | N6k | 7.3(0)N1(1) | 1.7.0 | | N7k | 7.3(0)D1(1) | 1.7.0 | | N9k-F | 7.0(3)F1(1) | 1.7.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### Parameters @@ -1463,6 +1473,7 @@ Manages configuration of a BGP Neighbor. | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### <a name="cisco_bgp_neighbor-caveats">Caveats</a> @@ -1565,6 +1576,7 @@ Manages configuration of a BGP Neighbor Address-family instance. | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### <a name="cisco_bgp_neighbor_af-caveats">Caveats</a> @@ -1691,6 +1703,7 @@ Manages a cisco Bridge-Domain | N6k | not applicable | not applicable | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | not applicable | not applicable | +| N3k-F | not applicable | not applicable | #### Parameters @@ -1721,6 +1734,7 @@ Creates a Virtual Network Identifier member (VNI) mapping for cisco Bridge-Domai | N6k | not applicable | not applicable | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | not applicable | not applicable | +| N3k-F | not applicable | not applicable | #### Parameters @@ -1746,6 +1760,7 @@ Manages configuration of a DHCP relay global configuration. | N6k | 7.3(0)N1(1) | 1.4.0 | | N7k | 7.3(0)D1(1) | 1.4.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### <a name="cisco_dhcp_relay_global-caveats">Caveats</a> @@ -1753,8 +1768,8 @@ Manages configuration of a DHCP relay global configuration. |:--------|:-------------| | `ipv4_information_option_trust` | Not supported on N5k, N6k | | `ipv4_information_trust_all` | Not supported on N5k, N6k | -| `ipv4_src_addr_hsrp` | Not supported on N3k, N9k, N9k-F | -| `ipv4_sub_option_circuit_id_custom` | Not supported on N7k, N9k-F(TBD) and supported on N3k and N9k running os version 7.0(3)I3.1 and later | +| `ipv4_src_addr_hsrp` | Not supported on N3k, N3k-F, N9k, N9k-F | +| `ipv4_sub_option_circuit_id_custom` | Not supported on N7k, N3k-F, N9k-F(TBD) and supported on N3k and N9k running os version 7.0(3)I3.1 and later | | `ipv4_sub_option_circuit_id_string` | Supported on N3k <br> Supported in OS Version 7.0(3)I6(1) and later on N9k | | `ipv6_option_cisco` | Not supported on N5k, N6k | @@ -1817,6 +1832,7 @@ Manages a Global VNI Encapsulation profile | N6k | not applicable | not applicable | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | not applicable | not applicable | +| N3k-F | not applicable | not applicable | #### Parameters @@ -1844,6 +1860,7 @@ Manages Cisco Ethernet Virtual Private Network (EVPN) VXLAN Network Identifier ( | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### <a name="cisco_evpn_vni-caveats">Caveats</a> @@ -1974,6 +1991,7 @@ Manages a Cisco fabricpath Topology | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | not applicable | not applicable | +| N3k-F | not applicable | not applicable | #### Parameters @@ -2000,6 +2018,7 @@ Manages Cisco Hot Standby Router Protocol (HSRP) global parameters. | N6k | 7.3(0)N1(1) | 1.5.0 | | N7k | 7.3(0)D1(1) | 1.5.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### <a name="cisco_hsrp_global-caveats">Caveats</a> @@ -2029,26 +2048,27 @@ Manages a Cisco Network Interface. Any resource dependency should be run before | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### <a name="cisco_interface-caveats">Caveats</a> | Property | Caveat Description | |:---------|:-------------| | `ipv4_dhcp_relay_info_trust` | Not supported on N5k,N6k | -| `ipv4_dhcp_relay_src_addr_hsrp` | Not supported on N3k,N9k-F,N9k | +| `ipv4_dhcp_relay_src_addr_hsrp` | Not supported on N3k,N3k-F,N9k-F,N9k | | `storm_control_broadcast` | Not supported on N7k | | `storm_control_multicast` | Not supported on N7k | -| `pvlan_mapping` | Not supported on N9k-F | -| `switchport_pvlan_host` | Not supported on N9k-F | -| `switchport_pvlan_host_association` | Not supported on N9k-F | -| `switchport_pvlan_mapping` | Not supported on N9k-F | -| `switchport_pvlan_mapping_trunk` | Not supported on N3k,N9k-F | -| `switchport_pvlan_promiscuous` | Not supported on N9k-F | -| `switchport_pvlan_trunk_allowed_vlan` | Not supported on N9k-F | -| `switchport_pvlan_trunk_association` | Not supported on N3k,N9k-F | -| `switchport_pvlan_trunk_native_vlan` | Not supported on N9k-F | -| `switchport_pvlan_trunk_promiscuous` | Not supported on N3k,N9k-F | -| `switchport_pvlan_trunk_secondary` | Not supported on N3k,N9k-F | +| `pvlan_mapping` | Not supported on N3k-F,N9k-F | +| `switchport_pvlan_host` | Not supported on N3k-F,N9k-F | +| `switchport_pvlan_host_association` | Not supported on N3k-F,N9k-F | +| `switchport_pvlan_mapping` | Not supported on N3k-F,N9k-F | +| `switchport_pvlan_mapping_trunk` | Not supported on N3k,N3k-F,N9k-F | +| `switchport_pvlan_promiscuous` | Not supported on N3k-F,N9k-F | +| `switchport_pvlan_trunk_allowed_vlan` | Not supported on N3k-F,N9k-F | +| `switchport_pvlan_trunk_association` | Not supported on N3k,N3k-F,N9k-F | +| `switchport_pvlan_trunk_native_vlan` | Not supported on N3k-F,N9k-F | +| `switchport_pvlan_trunk_promiscuous` | Not supported on N3k,N3k-F,N9k-F | +| `switchport_pvlan_trunk_secondary` | Not supported on N3k,N3k-F,N9k-F | | `svi_autostate` | Only supported on N3k,N7k,N9k | | `vlan_mapping` | Only supported on N7k | | `vlan_mapping_enable` | Only supported on N7k | @@ -2410,6 +2430,7 @@ Manages a Cisco Network Interface Channel-group. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### <a name="cisco_interface_channel_group-caveats">Caveats</a> @@ -2454,6 +2475,7 @@ Manages a Cisco Network Interface HSRP group. | N6k | not applicable | not applicable | | N7k | 8.0 | 1.5.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### <a name="cisco_interface_hsrp_group-caveats">Caveats</a> @@ -2550,6 +2572,7 @@ Manages a Cisco Network Interface Service VNI. | N6k | not applicable | not applicable | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | not applicable | not applicable | +| N3k-F | not applicable | not applicable | #### Parameters @@ -2584,6 +2607,7 @@ Manages configuration of an OSPF interface instance. | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### Parameters @@ -2717,6 +2741,7 @@ Manages configuration of ITD (Intelligent Traffic Director) device group | N6k | not applicable | not applicable | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | not applicable | not applicable | +| N3k-F | not applicable | not applicable | #### Parameters @@ -2760,6 +2785,7 @@ Manages configuration of ITD (Intelligent Traffic Director) device group node | N6k | not applicable | not applicable | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | not applicable | not applicable | +| N3k-F | not applicable | not applicable | #### Parameters @@ -2894,6 +2920,7 @@ Manages configuration of an ObjectGroup instance. This has no properties and it | N6k | not applicable | not applicable | | N7k | 7.3(0)D1(1) | 1.8.0 | | N9k-F | 7.0(3)F1(1) | 1.8.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### Parameters @@ -2929,6 +2956,7 @@ Manages configuration of an ObjectGroupEntry instance. | N6k | not applicable | not applicable | | N7k | 7.3(0)D1(1) | 1.8.0 | | N9k-F | 7.0(3)F1(1) | 1.8.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### Example Usage @@ -3013,6 +3041,7 @@ Manages configuration of an ospf instance. | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### Parameters @@ -3036,6 +3065,7 @@ Manages an area for an OSPF router. | N6k | 7.3(0)N1(1) | 1.4.0 | | N7k | 7.3(0)D1(1) | 1.4.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### Example Usage @@ -3144,6 +3174,7 @@ Manages an area virtual link for an OSPF router. | N6k | 7.3(0)N1(1) | 1.4.0 | | N7k | 7.3(0)D1(1) | 1.4.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### Example Usage @@ -3225,6 +3256,7 @@ Manages a VRF for an OSPF router. | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### Parameters @@ -3294,6 +3326,7 @@ Also configures anycast gateway MAC of the switch. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### <a name="cisco_overlay_global-caveats">Caveats</a> @@ -3337,6 +3370,7 @@ Manages configuration of an Protocol Independent Multicast (PIM) instance. | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### <a name="cisco_pim-caveats">Caveats</a> @@ -3372,6 +3406,7 @@ Manages configuration of an Protocol Independent Multicast (PIM) static route pr | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### Parameters @@ -3399,6 +3434,7 @@ Manages configuration of an Protocol Independent Multicast (PIM) static route pr | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | #### Parameters @@ -3423,6 +3459,8 @@ Manages configuration of a portchannel global parameters | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### <a name="cisco_portchannel_global-caveats">Caveats</a> @@ -3435,7 +3473,7 @@ Manages configuration of a portchannel global parameters | `concatenation` | Supported only on N9k | | `hash_poly` | Supported only on N5k, N6k | | `resilient` <br> `symmetry` | Supported only on N3k, N9k | -| `rotate` | Supported only on N7k, N9k-F, N9k | +| `rotate` | Supported only on N3k-F, N7k, N9k-F, N9k | #### Parameters @@ -3482,46 +3520,48 @@ Manages a Cisco Route Map. | N6k | 7.3(0)N1(1) | 1.6.0 | | N7k | 7.3(0)D1(1) | 1.6.0 | | N9k-F | 7.0(3)F1(1) | 1.6.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### <a name="cisco_route_map-caveats">Caveats</a> | Property | Caveat Description | |:---------|:-------------| -| `match_evpn_route_type_1` | Not supported on N3k,N9k-F,N9k | -| `match_evpn_route_type_2_all` | Not supported on N3k,N9k-F,N9k | -| `match_evpn_route_type_2_mac_ip` | Not supported on N3k,N9k-F,N9k | -| `match_evpn_route_type_2_mac_only` | Not supported on N3k,N9k-F,N9k | -| `match_evpn_route_type_3` | Not supported on N3k,N9k-F,N9k | -| `match_evpn_route_type_4` | Not supported on N3k,N9k-F,N9k | -| `match_evpn_route_type_5` | Not supported on N3k,N9k-F,N9k | -| `match_evpn_route_type_6` | Not supported on N3k,N9k-F,N9k | -| `match_evpn_route_type_all` | Not supported on N3k,N9k-F,N9k | -| `match_length` | Not supported on N3k,N9k-F,N9k | -| `match_mac_list` | Not supported on N3k,N9k-F,N9k | +| `match_evpn_route_type_1` | Not supported on N3k,N3k-F,N9k-F,N9k | +| `match_evpn_route_type_2_all` | Not supported on N3k,N3k-F,N9k-F,N9k | +| `match_evpn_route_type_2_mac_ip` | Not supported on N3k,N3k-F,N9k-F,N9k | +| `match_evpn_route_type_2_mac_only` | Not supported on N3k,N3k-F,N9k-F,N9k | +| `match_evpn_route_type_3` | Not supported on N3k,N3k-F,N9k-F,N9k | +| `match_evpn_route_type_4` | Not supported on N3k,N3k-F,N9k-F,N9k | +| `match_evpn_route_type_5` | Not supported on N3k,N3k-F,N9k-F,N9k | +| `match_evpn_route_type_6` | Not supported on N3k,N3k-F,N9k-F,N9k | +| `match_evpn_route_type_all` | Not supported on N3k,N3k-F,N9k-F,N9k | +| `match_length` | Not supported on N3k,N3k-F,N9k-F,N9k | +| `match_mac_list` | Not supported on N3k,N3k-F,N9k-F,N9k | | `match_metric` | Supported in OS Version 7.0(3)F2(1) and later on N9k-F | -| `match_ospf_area` | Not supported on N5k,N6k,N7k,N9k-F <br> Supported in OS version 7.0(3)I5.1 and later on N3k, N9k | -| `match_vlan` | Not supported on N3k,N9k-F,N9k | +| `match_ospf_area` | Not supported on N5k,N6k,N7k,N3k-F,N9k-F <br> Supported in OS version 7.0(3)I5.1 and later on N3k, N9k | +| `match_vlan` | Not supported on N3k,N3k-F,N9k-F,N9k | | `set_extcommunity_4bytes_additive` | Supported in OS Version 7.0(3)F2(1) and later on N9k-F | | `set_extcommunity_4bytes_non_transitive` | Supported in OS Version 7.0(3)F2(1) and later on N9k-F | | `set_extcommunity_4bytes_transitive` | Supported in OS Version 7.0(3)F2(1) and later on N9k-F | -| `set_extcommunity_cost_igp` | Not supported on N9k-F | -| `set_extcommunity_cost_pre_bestpath` | Not supported on N9k-F | -| `set_extcommunity_rt_additive` | Not supported on N9k-F | -| `set_extcommunity_rt_asn` | Not supported on N9k-F,N9k | -| `set_forwarding_addr` | Not supported on N9k-F | -| `set_ipv4_default_next_hop` | Not supported on N5k,N6k,N9k-F,N9k | -| `set_ipv4_default_next_hop_load_share` | Not supported on N5k,N6k,N9k-F,N9k | -| `set_ipv4_next_hop` | Not supported on N9k-F | +| `set_extcommunity_cost_igp` | Not supported on N3k-F,N9k-F | +| `set_extcommunity_cost_pre_bestpath` | Not supported on N3k-F,N9k-F | +| `set_extcommunity_rt_additive` | Not supported on N3k-F,N9k-F | +| `set_extcommunity_rt_asn` | Not supported on N3k-F,N9k-F,N9k | +| `set_forwarding_addr` | Not supported on N3k-F,N9k-F | +| `set_ipv4_default_next_hop` | Not supported on N5k,N6k,N3k-F,N9k-F,N9k | +| `set_ipv4_default_next_hop_load_share` | Not supported on N5k,N6k,N3k-F,N9k-F,N9k | +| `set_ipv4_next_hop` | Not supported on N3k-F,N9k-F | | `set_ipv4_next_hop_load_share` | Not supported on N5k,N6k <br> Supported in OS Version 7.0(3)I5.1 and later on N9k <br> Supported in OS Version 7.0(3)F2(1) and later on N9k-F | -| `set_ipv4_next_hop_redist` | Supported on N5k,N6k,N7k,N9k-F <br> Supported in OS Version 7.0(3)I5.1 and later on N3k,N9k | -| `set_ipv4_precedence` | Not supported on N9k-F | -| `set_ipv4_prefix` | Not supported on N5k,N6k,N9k-F | -| `set_ipv6_default_next_hop` | Not supported on N5k,N6k,N9k-F,N9k | -| `set_ipv6_default_next_hop_load_share` | Not supported on N5k,N6k,N9k-F,N9k | -| `set_ipv6_next_hop` | Not supported on N9k-F | +| `set_ipv4_next_hop_redist` | Supported on N5k,N6k,N7k,N3k-F,N9k-F <br> Supported in OS Version 7.0(3)I5.1 and later on N3k,N9k | +| `set_ipv4_precedence` | Not supported on N3k-F,N9k-F | +| `set_ipv4_prefix` | Not supported on N5k,N6k,N3k-F,N9k-F | +| `set_ipv6_default_next_hop` | Not supported on N5k,N6k,N3k-F,N9k-F,N9k | +| `set_ipv6_default_next_hop_load_share` | Not supported on N5k,N6k,N3k-F,N9k-F,N9k | +| `set_ipv6_next_hop` | Not supported on N3k-F,N9k-F | | `set_ipv6_next_hop_load_share` | Not supported on N5k,N6k <br> Supported in OS Version 7.0(3)I5.1 and later on N9k <br> Supported in OS Version 7.0(3)F2(1) and later on N9k-F | -| `set_ipv6_next_hop_redist` | Supported on N5k,N6k,N7k,N9k-F <br> Supported in OS Version 7.0(3)I5.1 and later on N3k,N9k | -| `set_ipv6_prefix` | Not supported on N5k,N6k,N9k-F | +| `set_ipv6_next_hop_redist` | Supported on N5k,N6k,N7k,N3k-F,N9k-F <br> Supported in OS Version 7.0(3)I5.1 and later on N3k,N9k | +| `set_ipv6_prefix` | Not supported on N5k,N6k,N3k-F,N9k-F | | `set_vrf` | Supported on N7k | @@ -4018,6 +4058,8 @@ Manages an SNMP community on a Cisco SNMP server. | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -4052,6 +4094,8 @@ of group; thus this provider utility does not create snmp groups and only report | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -4075,6 +4119,8 @@ cisco_snmp_server. | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -4120,6 +4166,8 @@ Manages an SNMP user on an cisco SNMP server. | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -4169,6 +4217,8 @@ instance of the cisco_tacacs_server. | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -4212,6 +4262,8 @@ Configures Cisco TACACS+ server hosts. | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -4249,10 +4301,12 @@ Manages the upgrade of a Cisco device. | N6k | not applicable | not applicable | | N7k | not applicable | not applicable | | N9k-F | 7.0(3)F1(1) | 1.6.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### <a name="cisco_upgrade-caveats">Caveats</a> -The `cisco_upgrade` is only supported on *simplex* N3k, N9k and N9k-F devices. HA devices are currently not supported. +The `cisco_upgrade` is only supported on *simplex* N3k, N3k-F, N9k and N9k-F devices. HA devices are currently not supported. | Property | Caveat Description | |:--------|:-------------| @@ -4291,6 +4345,8 @@ Manages a Cisco VDC (Virtual Device Context). | N6k | not applicable | not applicable | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | not applicable | not applicable | +| N3k-F | not applicable | not applicable | + #### Parameters @@ -4325,8 +4381,8 @@ Manages a Cisco VLAN. |:--------|:-------------| | `fabric_control` | Only supported on N7k (support added in ciscopuppet 1.3.0) | | `mode` | Only supported on N5k,N6k,N7k | -| `pvlan_type` | Not supported on N9k-F | -| `pvlan_association` | Not supported on N9k-F | +| `pvlan_type` | Not supported on N3k-F,N9k-F | +| `pvlan_association` | Not supported on N3k-F,N9k-F | #### Parameters @@ -4382,6 +4438,8 @@ Manages the virtual Port Channel (vPC) domain configuration of a Cisco device. | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### <a name="cisco_vpc_domain-caveats">Caveats</a> @@ -4494,6 +4552,8 @@ device. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### <a name="cisco_vrf-caveats">Caveats</a> @@ -4564,6 +4624,8 @@ Manages Cisco Virtual Routing and Forwarding (VRF) Address-Family configuration. | N6k | 7.3(0)N1(1) | 1.2.0 | | N7k | 7.3(0)D1(1) | 1.2.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### <a name="cisco_vrf_af-caveats">Caveats</a> @@ -4647,6 +4709,8 @@ There can only be one instance of the cisco_vtp. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -4681,6 +4745,8 @@ Creates a VXLAN Network Virtualization Endpoint (NVE) overlay interface that ter | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### <a name="cisco_vxlan_vtep-caveats">Caveats</a> @@ -4720,6 +4786,8 @@ Creates a Virtual Network Identifier member (VNI) for an NVE overlay interface. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### <a name="cisco_vxlan_vtep_vni-caveats">Caveats</a> @@ -4727,7 +4795,7 @@ Creates a Virtual Network Identifier member (VNI) for an NVE overlay interface. |---------------------------------|--------------------------------------| | ingress_replication | Not supported on N3k, N5k, N6k, N7k | | peer_list | Not supported on N3k, N5k, N6k, N7k | -| suppress_uuc | Not supported on N3k, N9k, N9k-F <br> Supported in OS Version 8.1.1 and later on N7k | +| suppress_uuc | Not supported on N3k, N3k-F, N9k, N9k-F <br> Supported in OS Version 8.1.1 and later on N7k | #### Parameters @@ -4777,6 +4845,8 @@ Configure the domain name of the device | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -4797,6 +4867,8 @@ Domain name of the device. Valid value is a string. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -4817,6 +4889,8 @@ Hostname or address of the DNS server. Valid value is a string. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -4848,6 +4922,8 @@ Manages a puppet netdev_stdlib Network Interface. Any resource dependency should | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -4880,6 +4956,8 @@ interface. Valid value is an integer. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -4908,6 +4986,8 @@ Manages a puppet netdev_stdlib Network Trunk. It should be noted that while the | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -4944,6 +5024,8 @@ Manages a puppet netdev_stdlib Network Vlan. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -4970,6 +5052,8 @@ The name of the VLAN. Valid value is a string. | N6k | 7.3(0)N1(1) | 1.7.0 | | N7k | 7.3(0)D1(1) | 1.7.0 | | N9k-F | 7.0(3)F1(1) | 1.7.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -4996,6 +5080,8 @@ Authentication password. Valid value is a string. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### <a name="ntp_config-caveats">Caveats</a> @@ -5029,6 +5115,8 @@ Trusted key for the NTP server. Valid value is integer. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### <a name="ntp_server-caveats">Caveats</a> @@ -5070,6 +5158,8 @@ Name of the vrf. Valid value is a string. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -5099,6 +5189,8 @@ Name of the port channel. eg port-channel100. Valid value is a string. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -5119,6 +5211,8 @@ Enable or disable radius functionality. Valid values are 'true' or 'false'. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -5196,6 +5290,8 @@ Encryption key format [0-7]. Valid value is an integer. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -5215,6 +5311,8 @@ Configure the search domain of the device. Note that this type is functionally e | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -5237,6 +5335,8 @@ Manages an SNMP community on a Cisco SNMP server. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -5265,6 +5365,8 @@ Manages an SNMP notification on a Cisco SNMP server. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -5285,6 +5387,8 @@ Manages an SNMP notification receiver on an cisco SNMP server. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -5329,6 +5433,8 @@ Manages an SNMP user on an cisco SNMP server. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -5373,6 +5479,8 @@ format (in case of true) or cleartext (in case of false). Valid values are 'true | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -5399,6 +5507,8 @@ Interface to send syslog data from, e.g. "management". Valid value is a string. | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -5419,6 +5529,8 @@ The unit of measurement for log time values. Valid values are 'seconds' and 'mi | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -5436,6 +5548,8 @@ Enable or disable radius functionality [true|false] | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters @@ -5462,6 +5576,8 @@ Number of seconds before the timeout period ends | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + ##### `ensure` Determines whether or not the config should be present on the device. Valid values are 'present' and 'absent'. @@ -5492,6 +5608,8 @@ Number of seconds before the timeout period ends | N6k | 7.3(0)N1(1) | 1.3.0 | | N7k | 7.3(0)D1(1) | 1.3.0 | | N9k-F | 7.0(3)F1(1) | 1.5.0 | +| N3k-F | 7.0(3)F3(2) | 1.8.0 | + #### Parameters diff --git a/docs/README-agent-install.md b/docs/README-agent-install.md index cd474d644..38d078d32 100644 --- a/docs/README-agent-install.md +++ b/docs/README-agent-install.md @@ -45,6 +45,7 @@ Cisco Nexus N5k | NX-OS | 7.3(0)N1(1) and later Cisco Nexus N6k | NX-OS | 7.3(0)N1(1) and later Cisco Nexus N7k | NX-OS | 7.3(0)D1(1) and later Cisco Nexus N9k-F | NX-OS | 7.0(3)F1(1) and later +Cisco Nexus N3k-F | NX-OS | 7.0(3)F3(2) and later <br> diff --git a/examples/cisco/demo_acl.pp b/examples/cisco/demo_acl.pp index 3ee69a59d..0783f3992 100644 --- a/examples/cisco/demo_acl.pp +++ b/examples/cisco/demo_acl.pp @@ -17,7 +17,7 @@ class ciscopuppet::cisco::demo_acl { $fragments = platform_get() ? { - /(n3k|n7k|n9k-f|n9k)/ => 'permit', + /(n3k|n7k|n3k-f|n9k-f|n9k)/ => 'permit', default => undef } @@ -38,32 +38,32 @@ } $http_method = platform_get() ? { - /(n3k|n9k-f|n9k)/ => 'post', + /(n3k|n3k-f|n9k-f|n9k)/ => 'post', default => undef } $packet_length = platform_get() ? { - /(n3k|n7k|n9k-f|n9k)/ => 'range 80 1000', + /(n3k|n7k|n3k-f|n9k-f|n9k)/ => 'range 80 1000', default => undef } $redirect = platform_get() ? { - /(n3k|n9k-f|n9k)/ => 'Ethernet1/1,Ethernet1/2,port-channel1', + /(n3k|n3k-f|n9k-f|n9k)/ => 'Ethernet1/1,Ethernet1/2,port-channel1', default => undef } $tcp_option_length = platform_get() ? { - /(n3k|n9k-f|n9k)/ => '20', + /(n3k|n3k-f|n9k-f|n9k)/ => '20', default => undef } $time_range = platform_get() ? { - /(n3k|n7k|n9k-f|n9k)/ => 'my_range', + /(n3k|n7k|n3k-f|n9k-f|n9k)/ => 'my_range', default => undef } $ttl = platform_get() ? { - /(n3k|n9k-f|n9k)/ => '153', + /(n3k|n3k-f|n9k-f|n9k)/ => '153', default => undef } diff --git a/examples/cisco/demo_bfd.pp b/examples/cisco/demo_bfd.pp index bcbdcd1e2..851c39fec 100644 --- a/examples/cisco/demo_bfd.pp +++ b/examples/cisco/demo_bfd.pp @@ -17,7 +17,7 @@ class ciscopuppet::cisco::demo_bfd { $echo_rx_interval = platform_get() ? { - /(n3k|n7k|n9k-f|n9k)/ => 300, + /(n3k|n7k|n3k-f|n9k-f|n9k)/ => 300, default => undef } @@ -43,37 +43,37 @@ } $ipv4_echo_rx_interval = platform_get() ? { - /(n3k|n7k|n9k-f|n9k)/ => 100, + /(n3k|n7k|n3k-f|n9k-f|n9k)/ => 100, default => undef } $ipv4_interval = platform_get() ? { - /(n3k|n7k|n9k-f|n9k)/ => ['200', '200', '50'], + /(n3k|n7k|n3k-f|n9k-f|n9k)/ => ['200', '200', '50'], default => undef } $ipv4_slow_timer = platform_get() ? { - /(n3k|n7k|n9k-f|n9k)/ => 10000, + /(n3k|n7k|n3k-f|n9k-f|n9k)/ => 10000, default => undef } $ipv6_echo_rx_interval = platform_get() ? { - /(n3k|n7k|n9k-f|n9k)/ => 200, + /(n3k|n7k|n3k-f|n9k-f|n9k)/ => 200, default => undef } $ipv6_interval = platform_get() ? { - /(n3k|n7k|n9k-f|n9k)/ => ['500', '500', '30'], + /(n3k|n7k|n3k-f|n9k-f|n9k)/ => ['500', '500', '30'], default => undef } $ipv6_slow_timer = platform_get() ? { - /(n3k|n7k|n9k-f|n9k)/ => 25000, + /(n3k|n7k|n3k-f|n9k-f|n9k)/ => 25000, default => undef } $startup_timer = platform_get() ? { - /(n3k|n9k-f|n9k)/ => 25, + /(n3k|n3k-f|n9k-f|n9k)/ => 25, default => undef } diff --git a/examples/cisco/demo_bgp.pp b/examples/cisco/demo_bgp.pp index 082583a05..319c7d007 100644 --- a/examples/cisco/demo_bgp.pp +++ b/examples/cisco/demo_bgp.pp @@ -59,7 +59,7 @@ } $event_history_errors = platform_get() ? { - /(n3k|n9k$)/ => $facts['cisco']['images']['system_image'] ? { + /(n3k$|n9k$)/ => $facts['cisco']['images']['system_image'] ? { /(I2|I3|I4)/ => undef, default => 'size_small' }, @@ -67,7 +67,7 @@ } $event_history_objstore = platform_get() ? { - /(n3k|n9k$)/ => $facts['cisco']['images']['system_image'] ? { + /(n3k$|n9k$)/ => $facts['cisco']['images']['system_image'] ? { /(I2|I3|I4)/ => undef, default => 'size_small' }, diff --git a/examples/cisco/demo_dhcp_relay_global.pp b/examples/cisco/demo_dhcp_relay_global.pp index 3e26e6e4a..aa44e4b30 100644 --- a/examples/cisco/demo_dhcp_relay_global.pp +++ b/examples/cisco/demo_dhcp_relay_global.pp @@ -17,12 +17,12 @@ class ciscopuppet::cisco::demo_dhcp_relay_global { $ipv4_information_option_trust = platform_get() ? { - /(n3k|n7k|n9k-f|n9k)/ => true, + /(n3k|n7k|n3k-f|n9k-f|n9k)/ => true, default => undef } $ipv4_information_trust_all = platform_get() ? { - /(n3k|n7k|n9k-f|n9k)/ => true, + /(n3k|n7k|n3k-f|n9k-f|n9k)/ => true, default => undef } @@ -42,7 +42,7 @@ } $ipv6_option_cisco = platform_get() ? { - /(n3k|n7k|n9k-f|n9k)/ => true, + /(n3k|n7k|n3k-f|n9k-f|n9k)/ => true, default => undef } diff --git a/examples/cisco/demo_interface.pp b/examples/cisco/demo_interface.pp index 0c86bda90..33f995611 100755 --- a/examples/cisco/demo_interface.pp +++ b/examples/cisco/demo_interface.pp @@ -37,7 +37,7 @@ } $ipv4_dhcp_relay_info_trust = platform_get() ? { - /(n3k|n7k|n9k-f|n9k)/ => true, + /(n3k|n7k|n3k-f|n9k-f|n9k)/ => true, default => undef } @@ -100,12 +100,12 @@ ipv6_dhcp_relay_src_intf => 'ethernet 2/2', } $storm_control_broadcast = platform_get() ? { - /(n3k|n5k|n6k|n9k-f|n9k)/ => '77.77', + /(n3k|n5k|n6k|n3k-f|n9k-f|n9k)/ => '77.77', default => undef } $storm_control_multicast = platform_get() ? { - /(n3k|n5k|n6k|n9k-f|n9k)/ => '22.22', + /(n3k|n5k|n6k|n3k-f|n9k-f|n9k)/ => '22.22', default => undef } diff --git a/examples/cisco/demo_portchannel.pp b/examples/cisco/demo_portchannel.pp index 3b65bc927..9be0ebe7a 100644 --- a/examples/cisco/demo_portchannel.pp +++ b/examples/cisco/demo_portchannel.pp @@ -42,12 +42,12 @@ } $port_hash_distribution = platform_get() ? { - /(n3k|n7k|n9k-f|n9k)/ => 'adaptive', + /(n3k|n7k|n3k-f|n9k-f|n9k)/ => 'adaptive', default => undef } $port_load_defer = platform_get() ? { - /(n3k|n7k|n9k-f|n9k)/ => true, + /(n3k|n7k|n3k-f|n9k-f|n9k)/ => true, default => undef } @@ -57,7 +57,7 @@ } $rotate = platform_get() ? { - /(n7k|n9k-f|n9k)/ => '4', + /(n7k|n3k-f|n9k-f|n9k)/ => '4', default => undef } diff --git a/examples/cisco/demo_route_map.pp b/examples/cisco/demo_route_map.pp index bb2773761..59ff3973c 100644 --- a/examples/cisco/demo_route_map.pp +++ b/examples/cisco/demo_route_map.pp @@ -72,7 +72,7 @@ } $match_ospf_area = platform_get() ? { - /(n3k|n9k$)/ => $facts['cisco']['images']['system_image'] ? { + /(n3k$|n9k$)/ => $facts['cisco']['images']['system_image'] ? { /(I2|I3|I4)/ => undef, default => ['10', '7', '222'] }, @@ -95,7 +95,7 @@ } $set_ipv4_next_hop_load_share = platform_get() ? { - /(n3k|n9k$)/ => $facts['cisco']['images']['system_image'] ? { + /(n3k$|n9k$)/ => $facts['cisco']['images']['system_image'] ? { /(I2|I3|I4)/ => undef, default => true }, @@ -119,7 +119,7 @@ } $set_ipv4_next_hop_redist = platform_get() ? { - /(n3k|n9k$)/ => $facts['cisco']['images']['system_image'] ? { + /(n3k$|n9k$)/ => $facts['cisco']['images']['system_image'] ? { /(I2|I3|I4)/ => undef, default => true }, @@ -127,7 +127,7 @@ } $set_ipv6_next_hop_redist = platform_get() ? { - /(n3k|n9k$)/ => $facts['cisco']['images']['system_image'] ? { + /(n3k$|n9k$)/ => $facts['cisco']['images']['system_image'] ? { /(I2|I3|I4)/ => undef, default => true }, @@ -135,7 +135,7 @@ } $set_ipv6_next_hop_load_share = platform_get() ? { - /(n3k|n9k$)/ => $facts['cisco']['images']['system_image'] ? { + /(n3k$|n9k$)/ => $facts['cisco']['images']['system_image'] ? { /(I2|I3|I4)/ => undef, default => true }, @@ -261,7 +261,7 @@ } } - if platform_get() =~ /n9k-f/ { + if platform_get() =~ /n(3|9)k-f/ { cisco_route_map {'MyRouteMap1 123 permit': ensure => 'present', description => 'Testing', diff --git a/lib/puppet/parser/functions/platform_get.rb b/lib/puppet/parser/functions/platform_get.rb index b0076978b..d5d5d2d60 100644 --- a/lib/puppet/parser/functions/platform_get.rb +++ b/lib/puppet/parser/functions/platform_get.rb @@ -32,7 +32,11 @@ module Functions # - NX-OSv Chassis case pi when /Nexus\s?3\d\d\d/ - cisco_hardware = 'n3k' + if function_platform_fretta([]) + cisco_hardware = 'n3k-f' + else + cisco_hardware = 'n3k' + end when /Nexus\s?5\d\d\d/ cisco_hardware = 'n5k' when /Nexus\s?6\d\d\d/ diff --git a/lib/puppet/provider/cisco_bgp/cisco.rb b/lib/puppet/provider/cisco_bgp/cisco.rb index f38fb4520..d203a9334 100644 --- a/lib/puppet/provider/cisco_bgp/cisco.rb +++ b/lib/puppet/provider/cisco_bgp/cisco.rb @@ -218,106 +218,32 @@ def timer_bestpath_limit_set @bgp_vrf.timer_bestpath_limit_set(limit, always) end - def legacy_image? - utils = PuppetX::Cisco::Utils - fd = Facter.value('cisco') - image = fd['images']['system_image'] - image[/7.0.3.I2|I3|I4/] || utils.product_tag[/n(5k|6k|7k|9k-f)/] - end - - def event_history_default?(prop) - @property_hash[prop.to_sym] == @bgp_vrf.send("default_#{prop}") - end - - def event_history_false?(prop) - @property_hash[prop.to_sym] == 'false' - end - - def event_history_cli - case resource[:event_history_cli] - when 'default' - return 'default' if event_history_default?('event_history_cli') - when 'true' - return 'true' if event_history_default?('event_history_cli') - when 'size_disable' - return 'size_disable' if - event_history_false?('event_history_cli') && !legacy_image? - end - @property_hash[:event_history_cli] - end - def event_history_cli=(should_value) - should_value = @bgp_vrf.default_event_history_cli if - should_value == 'default' || should_value == 'true' should_value = should_value.to_sym unless should_value =~ /\A\d+\z/ @property_flush[:event_history_cli] = should_value end - def event_history_detail - case resource[:event_history_detail] - when 'default' - return 'default' if event_history_default?('event_history_detail') - when 'size_disable' - return 'size_disable' if - event_history_default?('event_history_detail') && !legacy_image? - end - @property_hash[:event_history_detail] - end - def event_history_detail=(should_value) - should_value = @bgp_vrf.default_event_history_detail if - should_value == 'default' - should_value = @bgp_vrf.default_event_history_detail if - should_value == 'size_disable' && !legacy_image? should_value = should_value.to_sym unless should_value =~ /\A\d+\z/ @property_flush[:event_history_detail] = should_value end - def event_history_events - case resource[:event_history_events] - when 'default' - return 'default' if event_history_default?('event_history_events') - when 'true' - return 'true' if event_history_default?('event_history_events') - when 'size_disable' - return 'size_disable' if event_history_false?('event_history_events') - when 'size_large' - return 'size_large' if event_history_default?('event_history_events') && - !legacy_image? - end - @property_hash[:event_history_events] - end - def event_history_events=(should_value) - should_value = @bgp_vrf.default_event_history_events if - should_value == 'default' || should_value == 'true' - should_value = 'false' if should_value == 'size_disable' && !legacy_image? - should_value = 'true' if should_value == 'size_large' && !legacy_image? should_value = should_value.to_sym unless should_value =~ /\A\d+\z/ @property_flush[:event_history_events] = should_value end - def event_history_periodic - case resource[:event_history_periodic] - when 'default' - return 'default' if event_history_default?('event_history_periodic') - when 'true' - return 'true' if event_history_default?('event_history_periodic') && - legacy_image? - when 'size_disable' - return 'size_disable' if - event_history_default?('event_history_periodic') && !legacy_image? - end - @property_hash[:event_history_periodic] + def event_history_errors=(should_value) + should_value = should_value.to_sym unless should_value =~ /\A\d+\z/ + @property_flush[:event_history_errors] = should_value + end + + def event_history_objstore=(should_value) + should_value = should_value.to_sym unless should_value =~ /\A\d+\z/ + @property_flush[:event_history_objstore] = should_value end def event_history_periodic=(should_value) - should_value = @bgp_vrf.default_event_history_periodic if - should_value == 'default' - should_value = @bgp_vrf.default_event_history_periodic if - should_value == 'true' && legacy_image? - should_value = @bgp_vrf.default_event_history_periodic if - should_value == 'size_disable' && !legacy_image? should_value = should_value.to_sym unless should_value =~ /\A\d+\z/ @property_flush[:event_history_periodic] = should_value end diff --git a/lib/puppet/type/cisco_bgp.rb b/lib/puppet/type/cisco_bgp.rb index d13bf9730..a9b470bc4 100644 --- a/lib/puppet/type/cisco_bgp.rb +++ b/lib/puppet/type/cisco_bgp.rb @@ -51,12 +51,12 @@ disable_policy_batching_ipv4 => 'xx', disable_policy_batching_ipv6 => 'yy', enforce_first_as => true, - event_history_cli => 'true', - event_history_detail => 'small', - event_history_errors => 'large', - event_history_events => 'large', - event_history_objstore => 'medium', - event_history_periodic => 'disable', + event_history_cli => 'size_large', + event_history_detail => 'size_large', + event_history_errors => 'size_large', + event_history_events => 'size_large', + event_history_objstore => 'size_medium', + event_history_periodic => '100000', fast_external_fallover => true, flush_routes => false, isolate => false, @@ -348,46 +348,43 @@ def insync?(is) end # property enforce_first_as newproperty(:event_history_cli) do - desc "event_history_cli state. Valid values are True, False, size_small, - size_medium, size_large, size_disable, size in bytes or 'default'" + desc "event_history_cli state. Valid values are False, size_small, + size_medium, size_large, size_disable, size in bytes" end # property event_history_cli newproperty(:event_history_detail) do - desc "event_history_detail state. Valid values are True, False, size_small, - size_medium, size_large, size_disable, size in bytes or 'default'" + desc "event_history_detail state. Valid values are False, size_small, + size_medium, size_large, size_disable, size in bytes" end # property event_history_detail newproperty(:event_history_errors) do - desc "event_history_errors state. Valid values are True, False, size_small, - size_medium, size_large, size_disable, size in bytes or 'default'" + desc "event_history_errors state. Valid values are False, size_small, + size_medium, size_large, size_disable, size in bytes" munge do |value| - value = 'size_medium' if value == 'true' - value = 'false' if value == 'size_disable' value = value.to_sym unless value =~ /\A\d+\z/ value end end # property event_history_errors newproperty(:event_history_events) do - desc "event_history_events state. Valid values are True, False, size_small, - size_medium, size_large, size_disable, size in bytes or 'default'" + desc "event_history_events state. Valid values are False, size_small, + size_medium, size_large, size_disable, size in bytes" end # property event_history_events newproperty(:event_history_objstore) do - desc "event_history_objstore state. Valid values are True, False, size_small, - size_medium, size_large, size_disable, size in bytes or 'default'" + desc "event_history_objstore state. Valid values are False, size_small, + size_medium, size_large, size_disable, size in bytes" munge do |value| - value = 'false' if value == 'size_disable' value = value.to_sym unless value =~ /\A\d+\z/ value end end # property event_history_objstore newproperty(:event_history_periodic) do - desc "event_history_periodic state. Valid values are True, False, size_small, - size_medium, size_large, size_disable, size in bytes or 'default'" + desc "event_history_periodic state. Valid values are False, size_small, + size_medium, size_large, size_disable, size in bytes" end # property event_history_periodic newproperty(:fast_external_fallover) do diff --git a/lib/puppet_x/cisco/cmnutils.rb b/lib/puppet_x/cisco/cmnutils.rb index d0f32a0f5..8abd70b27 100644 --- a/lib/puppet_x/cisco/cmnutils.rb +++ b/lib/puppet_x/cisco/cmnutils.rb @@ -242,7 +242,7 @@ def self.product_tag data = Facter.value('cisco') case data['inventory']['chassis']['pid'] when /N3/ - tag = 'n3k' + tag = data['images']['system_image'][/7.0.3.F/] ? 'n3k-f' : 'n3k' when /N5/ tag = 'n5k' when /N6/ diff --git a/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb b/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb index a4a19ad59..18a78c6e7 100644 --- a/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb +++ b/tests/beaker_tests/cisco_bfd_global/test_bfd_global.rb @@ -90,7 +90,7 @@ } } -tests[:default][:resource].merge!(resource[:n3k]) if platform[/n3k/] +tests[:default][:resource].merge!(resource[:n3k]) if platform[/n3k$/] # Non-default Tests. NOTE: [:resource] = [:manifest_props] for all non-default diff --git a/tests/beaker_tests/cisco_bgp/test_bgp.rb b/tests/beaker_tests/cisco_bgp/test_bgp.rb index 9505d6654..c8e6ab72a 100644 --- a/tests/beaker_tests/cisco_bgp/test_bgp.rb +++ b/tests/beaker_tests/cisco_bgp/test_bgp.rb @@ -47,12 +47,6 @@ bestpath_med_non_deterministic: 'default', disable_policy_batching: 'default', enforce_first_as: 'default', - event_history_cli: 'default', - event_history_detail: 'default', - event_history_errors: 'default', - event_history_events: 'default', - event_history_objstore: 'default', - event_history_periodic: 'default', fast_external_fallover: 'default', flush_routes: 'default', graceful_restart: 'default', @@ -81,12 +75,6 @@ 'bestpath_med_non_deterministic' => 'false', 'disable_policy_batching' => 'false', 'enforce_first_as' => 'true', - 'event_history_cli' => 'size_small', - 'event_history_detail' => 'false', - 'event_history_errors' => 'size_medium', - 'event_history_events' => 'size_large', - 'event_history_objstore' => 'false', - 'event_history_periodic' => 'false', 'fast_external_fallover' => 'true', 'flush_routes' => 'false', 'graceful_restart' => 'true', @@ -107,17 +95,6 @@ }, } -# older_version default value -resource = { - legacy: { - 'event_history_events' => 'size_small', - 'event_history_periodic' => 'size_small', - } -} - -tests[:default][:resource].merge!(resource[:legacy]) if - nexus_image[/I2|I3|I4/] || platform[/n5|n6|n7|n9k-f/] - # Non-default Tests. NOTE: [:resource] = [:manifest_props] for all non-default tests[:non_default] = { desc: '2.1 Non Defaults', @@ -135,11 +112,11 @@ confederation_peers: ['200.1', '23.4', '55', '88'], disable_policy_batching: 'true', enforce_first_as: 'false', - event_history_cli: 'size_medium', + event_history_cli: 'size_large', event_history_detail: 'size_large', - event_history_errors: 'size_small', + event_history_errors: 'size_large', event_history_events: 'size_medium', - event_history_objstore: 'size_large', + event_history_objstore: 'size_medium', event_history_periodic: '100000', fast_external_fallover: 'false', flush_routes: 'true', @@ -187,6 +164,8 @@ def unsupp_prop_xr(tests, id) :event_history_cli << :event_history_detail << :event_history_events << + :event_history_errors << + :event_history_objstore << :event_history_periodic << :flush_routes << :graceful_restart_helper << @@ -228,7 +207,7 @@ def unsupported_properties(tests, id) unprops << :event_history_errors << - :event_history_objstore if platform[/n5|n6|n7|n9k-f/] + :event_history_objstore if platform[/n5|n6/] if vrf != 'default' # NX-OS does not support these properties under a non-default vrf @@ -265,9 +244,14 @@ def version_unsupported_properties(_tests, _id) unprops[:disable_policy_batching_ipv6] = '8.1.1' unprops[:neighbor_down_fib_accelerate] = '8.1.1' unprops[:reconnect_interval] = '8.1.1' - elsif platform[/n3k|n9k$/] + unprops[:event_history_errors] = '8.0' + unprops[:event_history_objstore] = '8.0' + elsif platform[/n3k$|n9k$/] unprops[:event_history_errors] = '7.0.3.I5.1' unprops[:event_history_objstore] = '7.0.3.I5.1' + elsif platform[/n(3|9)k-f/] + unprops[:event_history_errors] = '7.0.3.F3.2' + unprops[:event_history_objstore] = '7.0.3.F3.2' end unprops end diff --git a/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb b/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb index 64c88caf8..ebf3e1b01 100644 --- a/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb +++ b/tests/beaker_tests/cisco_bgp_af/test_bgpaf.rb @@ -266,7 +266,7 @@ def unsupported_properties(tests, id) end else unprops << :advertise_l2vpn_evpn if - vrf == 'default' || platform[/n(3|6)k/] + vrf == 'default' || platform[/n(3|6)k$/] unprops << :additional_paths_install if platform[/n(3|9)k/] unprops << :additional_paths_selection if platform[/n9k$/] && nexus_image[/I5.3/] diff --git a/tests/beaker_tests/cisco_bgp_neighbor_af/test_bgpneighboraf.rb b/tests/beaker_tests/cisco_bgp_neighbor_af/test_bgpneighboraf.rb index bd138e756..982df8f55 100644 --- a/tests/beaker_tests/cisco_bgp_neighbor_af/test_bgpneighboraf.rb +++ b/tests/beaker_tests/cisco_bgp_neighbor_af/test_bgpneighboraf.rb @@ -362,7 +362,7 @@ def cleanup(agent) test_harness_run(tests, :non_def_ibgp_only) # ------------------------------------------------------------------- - unless platform[/n3k/] + unless platform[/n3k$/] logger.info("\n#{'-' * 60}\nSection 3. L2VPN Property Testing") resource_absent_cleanup(agent, 'cisco_bgp', 'BGP CLEAN :: ') title = '2 default 1.1.1.1 l2vpn evpn' diff --git a/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb b/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb index 0536ce7de..9977c7403 100644 --- a/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb +++ b/tests/beaker_tests/cisco_dhcp_relay_global/test_dhcp_relay_global.rb @@ -120,7 +120,7 @@ def unsupported_properties(_tests, _id) unprops = [] - if platform[/n3k/] + if platform[/n3k$/] unprops << :ipv4_src_addr_hsrp elsif platform[/n(5|6)k/] @@ -133,7 +133,7 @@ def unsupported_properties(_tests, _id) unprops << :ipv4_sub_option_circuit_id_custom << :ipv4_sub_option_circuit_id_string - elsif platform[/n9k-f/] + elsif platform[/n(3|9)k-f/] unprops << :ipv4_src_addr_hsrp << :ipv4_sub_option_circuit_id_custom << diff --git a/tests/beaker_tests/cisco_hsrp/test_hsrp_global.rb b/tests/beaker_tests/cisco_hsrp/test_hsrp_global.rb index 4d3721340..1b78ff949 100644 --- a/tests/beaker_tests/cisco_hsrp/test_hsrp_global.rb +++ b/tests/beaker_tests/cisco_hsrp/test_hsrp_global.rb @@ -61,7 +61,7 @@ def unsupported_properties(_tests, _id) unprops = [] - unprops << :bfd_all_intf if platform[/n3k/] + unprops << :bfd_all_intf if platform[/n3k$/] unprops end diff --git a/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb b/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb index df153d614..bb8cdbecc 100755 --- a/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb +++ b/tests/beaker_tests/cisco_interface/test_interface_private_vlan.rb @@ -163,7 +163,7 @@ # This method overrides utilitylib.rb:unsupported_properties() def unsupported_properties(_tests, _id) unprops = [] - if platform[/n3k/] + if platform[/n3k$/] unprops << :switchport_pvlan_mapping_trunk << :switchport_pvlan_trunk_association << diff --git a/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb b/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb index 10b08633e..fa43b5ab3 100755 --- a/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb +++ b/tests/beaker_tests/cisco_interface_portchannel/test_interface_portchannel.rb @@ -90,7 +90,7 @@ 'lacp_graceful_convergence' => 'true', 'lacp_max_bundle' => '32', 'lacp_min_links' => '1', - 'lacp_suspend_individual' => platform[/n3k/] ? 'false' : 'true', + 'lacp_suspend_individual' => platform[/n3k$/] ? 'false' : 'true', 'port_hash_distribution' => 'false', 'port_load_defer' => 'false', }, @@ -105,7 +105,7 @@ lacp_graceful_convergence: 'false', lacp_max_bundle: '10', lacp_min_links: '3', - lacp_suspend_individual: platform[/n3k/] ? 'true' : 'false', + lacp_suspend_individual: platform[/n3k$/] ? 'true' : 'false', port_hash_distribution: 'fixed', port_load_defer: 'true', }, diff --git a/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb b/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb index fa4e4043c..f957b99c5 100755 --- a/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb +++ b/tests/beaker_tests/cisco_overlay_global/test_overlay_global.rb @@ -75,7 +75,7 @@ def unsupported_properties(_tests, _id) unprops = [] - if platform[/n3k/] + if platform[/n3k$/] unprops << :anycast_gateway_mac << :dup_host_ip_addr_detection_host_moves << @@ -86,7 +86,7 @@ def unsupported_properties(_tests, _id) def version_unsupported_properties(_tests, _id) unprops = {} - if platform[/n3k/] + if platform[/n3k$/] unprops[:dup_host_mac_detection_host_moves] = '7.0.3.I6.1' unprops[:dup_host_mac_detection_timeout] = '7.0.3.I6.1' end diff --git a/tests/beaker_tests/cisco_portchannel_global/test_portchannel_global.rb b/tests/beaker_tests/cisco_portchannel_global/test_portchannel_global.rb index 79d49233a..488fe99db 100755 --- a/tests/beaker_tests/cisco_portchannel_global/test_portchannel_global.rb +++ b/tests/beaker_tests/cisco_portchannel_global/test_portchannel_global.rb @@ -111,9 +111,9 @@ }, } -tests[:non_default][:manifest_props].merge!(manifest_non[:n3k]) if platform[/n3k/] +tests[:non_default][:manifest_props].merge!(manifest_non[:n3k]) if platform[/n3k$/] tests[:non_default][:manifest_props].merge!(manifest_non[:n56k]) if platform[/n(5|6)k/] -tests[:non_default][:manifest_props].merge!(manifest_non[:n9kf]) if platform[/n9k-f/] +tests[:non_default][:manifest_props].merge!(manifest_non[:n9kf]) if platform[/n(3|9)k-f/] tests[:non_default][:manifest_props].merge!(manifest_non[:n9k]) if platform[/n9k$/] def unsupported_properties(tests, _id) @@ -133,23 +133,23 @@ def unsupported_properties(tests, _id) :resilient << :rotate << :symmetry - elsif platform[/n3k/] + elsif platform[/n(3|9)k-f/] unprops << :asymmetric << :concatenation << :hash_distribution << :hash_poly << :load_defer << - :rotate - elsif platform[/n9k-f/] + :resilient << + :symmetry + elsif platform[/n3k/] unprops << :asymmetric << :concatenation << :hash_distribution << :hash_poly << :load_defer << - :resilient << - :symmetry + :rotate elsif platform[/n9k/] unprops << :asymmetric << @@ -162,7 +162,7 @@ def unsupported_properties(tests, _id) unprops end -if platform[/n3k/] +if platform[/n3k$/] tests[:resilient_unsupported] = resource_probe(agent, 'cisco_portchannel_global default resilient=true', @@ -271,6 +271,24 @@ def unsupported_properties(tests, _id) mhash[:bundle_select] = rhash[:bundle_select] = 'src' test_harness_run(tests, id) + elsif device == 'n3k-f' + tests[id][:desc] = '2.2 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'ip-l4port-vlan' + test_harness_run(tests, id) + + tests[id][:desc] = '2.3 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'ip-vlan' + test_harness_run(tests, id) + + tests[id][:desc] = '2.4 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'l4port' + test_harness_run(tests, id) + + tests[id][:desc] = '2.5 Non Defaults' + mhash[:bundle_hash] = rhash[:bundle_hash] = 'mac' + mhash[:bundle_select] = rhash[:bundle_select] = 'src' + test_harness_run(tests, id) + elsif device == 'n3k' if tests[:resilient_unsupported] tests[id][:desc] = '2.2 Non Defaults' diff --git a/tests/beaker_tests/cisco_route_map/test_route_map.rb b/tests/beaker_tests/cisco_route_map/test_route_map.rb index 33af52196..7a9c12af8 100644 --- a/tests/beaker_tests/cisco_route_map/test_route_map.rb +++ b/tests/beaker_tests/cisco_route_map/test_route_map.rb @@ -527,7 +527,7 @@ def unsupp_n9kf end def unsupported_properties(_tests, _id) - if platform[/n3k/] + if platform[/n3k$/] unsupp_n3k elsif platform[/n(5|6)k/] unsupp_n56k @@ -535,21 +535,21 @@ def unsupported_properties(_tests, _id) unsupp_n7k elsif platform[/n9k$/] unsupp_n9k - elsif platform[/n9k-f/] + elsif platform[/n(3|9)k-f/] unsupp_n9kf end end def version_unsupported_properties(_tests, _id) unprops = {} - if platform[/n9k-f/] + if platform[/n(3|9)k-f/] unprops[:match_metric] = '7.0.3.F2.1' unprops[:set_extcommunity_4bytes_additive] = '7.0.3.F2.1' unprops[:set_extcommunity_4bytes_non_transitive] = '7.0.3.F2.1' unprops[:set_extcommunity_4bytes_transitive] = '7.0.3.F2.1' unprops[:set_ipv4_next_hop_load_share] = '7.0.3.F2.1' unprops[:set_ipv6_next_hop_load_share] = '7.0.3.F2.1' - elsif platform[/n9k$/] + elsif platform[/n9k/] unprops[:match_ospf_area] = '7.0.3.I5.1' unprops[:set_ipv4_next_hop_load_share] = '7.0.3.I5.1' unprops[:set_ipv6_next_hop_load_share] = '7.0.3.I5.1' diff --git a/tests/beaker_tests/cisco_snmp_server/test_snmp_server.rb b/tests/beaker_tests/cisco_snmp_server/test_snmp_server.rb index 71376e2ee..681a58cdc 100644 --- a/tests/beaker_tests/cisco_snmp_server/test_snmp_server.rb +++ b/tests/beaker_tests/cisco_snmp_server/test_snmp_server.rb @@ -31,14 +31,14 @@ resource_name: 'cisco_snmp_server', } -# Add an anchor to the platform regexp below so that -# it only matches non-fretta n9k platforms. -@def_pkt_size = platform[/n(3|9)k$/] ? '1500' : '0' +# for fretta running F3.2 or later, this is fixed +# it will fail if older versions are run +@def_pkt_size = platform[/n(3|9)k/] ? '1500' : '0' # Test hash test cases tests[:default] = { desc: '1.1 Default Properties', - code: [0], + code: [0, 2], title_pattern: 'default', manifest_props: { aaa_user_cache_timeout: 'default', diff --git a/tests/beaker_tests/cisco_stp_global/test_stp_global.rb b/tests/beaker_tests/cisco_stp_global/test_stp_global.rb index 7ecefbe97..7ddadfe21 100644 --- a/tests/beaker_tests/cisco_stp_global/test_stp_global.rb +++ b/tests/beaker_tests/cisco_stp_global/test_stp_global.rb @@ -222,14 +222,14 @@ def test_harness_dependencies(_tests, id) def unsupported_properties(_tests, _id) unprops = [] - unprops << :domain if platform[/n9k-f/] + unprops << :domain if platform[/n(3|9)k-f/] unprops << :fcoe if platform[/n(3|5|6|7)k/] unprops end def version_unsupported_properties(_tests, _id) unprops = {} - unprops[:domain] = '7.0.3.I6.1' if platform[/n3k/] + unprops[:domain] = '7.0.3.I6.1' if platform[/n3k$/] unprops[:domain] = '7.0.3.I6.1' if platform[/n9k$/] unprops end diff --git a/tests/beaker_tests/cisco_vlan/test_vlan.rb b/tests/beaker_tests/cisco_vlan/test_vlan.rb index 68838a7e4..8c3d3beb6 100644 --- a/tests/beaker_tests/cisco_vlan/test_vlan.rb +++ b/tests/beaker_tests/cisco_vlan/test_vlan.rb @@ -102,7 +102,7 @@ # State cannot be modified for extended vlans on N5k and N6k platforms. tests[:non_default_extended][:manifest_props].delete(:state) if platform[/n(5|6)k/] -if platform[/n3k/] +if platform[/n3k$/] tests[:vn_segment_unsupported] = resource_probe(agent, 'cisco_vlan 128 mapped_vni=128000', diff --git a/tests/beaker_tests/cisco_vrf_af/test_vrf_af.rb b/tests/beaker_tests/cisco_vrf_af/test_vrf_af.rb index 228342561..de607efe5 100644 --- a/tests/beaker_tests/cisco_vrf_af/test_vrf_af.rb +++ b/tests/beaker_tests/cisco_vrf_af/test_vrf_af.rb @@ -100,7 +100,7 @@ def unsupported_properties(_tests, _id) :route_target_export_stitching << :route_target_import_stitching - if platform[/n3k/] + if platform[/n3k$/] unprops << :route_target_both_auto << :route_target_both_auto_evpn << diff --git a/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb b/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb index 6e5ae5a08..be22879b8 100644 --- a/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb +++ b/tests/beaker_tests/cisco_vxlan_vtep/test_vxlan_vtep.rb @@ -28,7 +28,7 @@ agent: agent, master: master, operating_system: 'nexus', - platform: 'n(5|6|7|9)k', + platform: 'n(3k-f|5k|6k|7k|9k)', resource_name: 'cisco_vxlan_vtep', } diff --git a/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb b/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb index 18b3174c4..a1b2d3bfa 100644 --- a/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb +++ b/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb @@ -59,7 +59,7 @@ tests = { master: master, agent: agent, - platform: 'n(5|6|7|9)k', + platform: 'n(3k-f|5k|6k|7k|9k)', resource_name: 'cisco_vxlan_vtep_vni', } @@ -70,7 +70,7 @@ tests[:default_properties_ingress_replication] = { desc: '1.1 Default Properties Ingress replication', title_pattern: 'nve1 10000', - platform: 'n9k', + platform: 'n(3k-f|9k)', manifest_props: { ingress_replication: 'default', suppress_arp: 'default', @@ -99,7 +99,7 @@ tests[:ingress_replication_static_peer_list_empty] = { desc: '2.1 Ingress Replication Static Peer List Empty', title_pattern: 'nve1 10000', - platform: 'n9k', + platform: 'n(3k-f|9k)', manifest_props: { ingress_replication: 'static', peer_list: [], @@ -114,7 +114,7 @@ tests[:peer_list] = { desc: '2.2 Peer List', title_pattern: 'nve1 10000', - platform: 'n9k', + platform: 'n(3k-f|9k)', manifest_props: { ingress_replication: 'static', peer_list: ['1.1.1.1', '2.2.2.2', '3.3.3.3'], @@ -129,7 +129,7 @@ tests[:peer_list_change_add] = { desc: '2.3 Peer List Change Add', title_pattern: 'nve1 10000', - platform: 'n9k', + platform: 'n(3k-f|9k)', manifest_props: { ingress_replication: 'static', peer_list: ['1.1.1.1', '6.6.6.6', '3.3.3.3', '4.4.4.4'], @@ -144,7 +144,7 @@ tests[:peer_list_default] = { desc: '2.4 Peer List Default', title_pattern: 'nve1 10000', - platform: 'n9k', + platform: 'n(3k-f|9k)', manifest_props: { ingress_replication: 'static', peer_list: 'default', @@ -159,7 +159,7 @@ tests[:ingress_replication_bgp] = { desc: '2.5 Ingress replication BGP', title_pattern: 'nve1 10000', - platform: 'n9k', + platform: 'n(3k-f|9k)', manifest_props: { ingress_replication: 'bgp', suppress_arp: 'default', @@ -245,7 +245,7 @@ def unsupported_properties(_tests, _id) unprops << :ingress_replication << :peer_list - elsif platform[/n9k/] + elsif platform[/n(3k-f|9k)/] unprops << :suppress_uuc end diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 7bd7ea767..bb0b2cabd 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1114,7 +1114,7 @@ def platform # - Cisco XRv9K Virtual Router case pi when /Nexus\s?3\d\d\d/ - @cisco_hardware = 'n3k' + @cisco_hardware = image?[/7.0.3.F/] ? 'n3k-f' : 'n3k' when /Nexus\s?5\d\d\d/ @cisco_hardware = 'n5k' when /Nexus\s?6\d\d\d/ @@ -1145,6 +1145,11 @@ def nexus_image facter_opt = '-p cisco.images.full_version' image_regexp = /(\S+)/ data = on(agent, facter_cmd(facter_opt)).output + darr = data.split("\n") + darr.each do |line| + next if line.include?('stty') || line.include?('WARN') + data = line + end @image ||= image_regexp.match(data)[1] end From 70aebb5ab4a879f0a36d1c58c79d377f64fe8efe Mon Sep 17 00:00:00 2001 From: saichint <saichint@cisco.com> Date: Tue, 7 Nov 2017 06:53:39 -0800 Subject: [PATCH 192/203] fix sys_image (#474) --- lib/puppet/provider/cisco_route_map/cisco.rb | 2 +- lib/puppet_x/cisco/cmnutils.rb | 4 ++-- tests/beaker_tests/lib/utilitylib.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/puppet/provider/cisco_route_map/cisco.rb b/lib/puppet/provider/cisco_route_map/cisco.rb index bcfd06f31..e548614d6 100644 --- a/lib/puppet/provider/cisco_route_map/cisco.rb +++ b/lib/puppet/provider/cisco_route_map/cisco.rb @@ -484,7 +484,7 @@ def set_extcommunity_cost_set def legacy_image? fd = Facter.value('cisco') - image = fd['images']['system_image'] + image = fd['images']['full_version'] image[/7.0.3.I2|I3|I4/] end diff --git a/lib/puppet_x/cisco/cmnutils.rb b/lib/puppet_x/cisco/cmnutils.rb index 8abd70b27..70d40c5c2 100644 --- a/lib/puppet_x/cisco/cmnutils.rb +++ b/lib/puppet_x/cisco/cmnutils.rb @@ -242,7 +242,7 @@ def self.product_tag data = Facter.value('cisco') case data['inventory']['chassis']['pid'] when /N3/ - tag = data['images']['system_image'][/7.0.3.F/] ? 'n3k-f' : 'n3k' + tag = data['images']['full_version'][/7.0.3.F/] ? 'n3k-f' : 'n3k' when /N5/ tag = 'n5k' when /N6/ @@ -250,7 +250,7 @@ def self.product_tag when /N7/ tag = 'n7k' when /N9/ - tag = data['images']['system_image'][/7.0.3.F/] ? 'n9k-f' : 'n9k' + tag = data['images']['full_version'][/7.0.3.F/] ? 'n9k-f' : 'n9k' else fail "Unrecognized product_id: #{data['inventory']['chassis']['pid']}" end diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index bb0b2cabd..727261878 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1136,7 +1136,7 @@ def platform @cached_img = nil def image?(reset_cache=false) return @cached_img unless @cached_img.nil? || reset_cache - on(agent, facter_cmd('-p cisco.images.system_image')) + on(agent, facter_cmd('-p cisco.images.full_version')) @cached_img = stdout.nil? ? '' : stdout end From 1454cb1588cc233ae023d0845f351889960d1287 Mon Sep 17 00:00:00 2001 From: sai chintalapudi <saichint@cisco.com> Date: Thu, 9 Nov 2017 12:59:35 -0800 Subject: [PATCH 193/203] release 1.8.0 --- CHANGELOG.md | 4 ++-- lib/puppet/feature/cisco_node_utils.rb | 2 +- metadata.json | 7 +++++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c8cf80be..db9ade404 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [Unreleased] +## [1.8.0] - 2017-11-09 ### New feature support #### Cisco Resources @@ -426,7 +426,7 @@ This version was never released. - Initial release of puppetlabs-ciscopuppet module, supporting Cisco NX-OS software release 7.0(3)I2(1) on Cisco Nexus switch platforms: N95xx, N93xx, N30xx and N31xx. - Please note: 0.9.0 is an EFT pre-release for a limited audience with access to NX-OS 7.0(3)I2(1). Additional code changes may occur in 0.9.x prior to the final 1.0.0 release. -[Unreleased]: https://github.com/cisco/cisco-network-puppet-module/compare/master...develop +[1.8.0]: https://github.com/cisco/cisco-network-puppet-module/compare/v1.7.0...v1.8.0 [1.7.0]: https://github.com/cisco/cisco-network-puppet-module/compare/v1.6.0...v1.7.0 [1.6.0]: https://github.com/cisco/cisco-network-puppet-module/compare/v1.5.0...v1.6.0 [1.5.0]: https://github.com/cisco/cisco-network-puppet-module/compare/v1.4.1...v1.5.0 diff --git a/lib/puppet/feature/cisco_node_utils.rb b/lib/puppet/feature/cisco_node_utils.rb index 65c8b45ff..ef6567fd2 100644 --- a/lib/puppet/feature/cisco_node_utils.rb +++ b/lib/puppet/feature/cisco_node_utils.rb @@ -37,7 +37,7 @@ def cisco_node_utils? @results['cisco_node_utils'] = test('cisco_node_utils', libs: ['cisco_node_utils']) if @results['cisco_node_utils'] - rec_version = Gem::Version.new('1.7.0') + rec_version = Gem::Version.new('1.8.0') gem_version = Gem::Version.new(CiscoNodeUtils::VERSION) if gem_version < rec_version warn "This module works best with version #{rec_version} of gem "\ diff --git a/metadata.json b/metadata.json index e3be10fc4..4a41f9fb6 100644 --- a/metadata.json +++ b/metadata.json @@ -1,6 +1,6 @@ { "name": "puppetlabs-ciscopuppet", - "version": "1.7.0", + "version": "1.8.0", "author": "cisco", "summary": "Cisco Puppet providers and types for NX-OS devices", "license": "Apache-2.0", @@ -35,12 +35,15 @@ "7.0(3)I4", "7.0(3)I5", "7.0(3)I6", + "7.0(3)I7", "7.3(0)N1", "7.3(0)D1", "7.0(3)F1", "7.0(3)F2", + "7.0(3)F3", "8.0(1)", - "8.1(1)" + "8.1(1)", + "8.2(1)" ] } ] From 6d88ce51cb19ff5428a3448cc6c3b3307ee6411e Mon Sep 17 00:00:00 2001 From: sai chintalapudi <saichint@cisco.com> Date: Thu, 9 Nov 2017 13:08:13 -0800 Subject: [PATCH 194/203] mgmt intf display --- lib/puppet/provider/cisco_interface/cisco.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/puppet/provider/cisco_interface/cisco.rb b/lib/puppet/provider/cisco_interface/cisco.rb index 6780feadb..652595016 100644 --- a/lib/puppet/provider/cisco_interface/cisco.rb +++ b/lib/puppet/provider/cisco_interface/cisco.rb @@ -207,9 +207,8 @@ def self.instances Cisco::Interface.interfaces.each do |interface_name, nu_obj| begin # Some interfaces cannot or should not be managed by this type. - # - Management Interface # - NVE Interfaces (managed by cisco_vxlan_vtep type) - next if interface_name.match(/mgmt|nve/i) + next if interface_name.match(/nve/i) interfaces << properties_get(interface_name, nu_obj) end end @@ -358,7 +357,11 @@ def vrf=(val) end def flush - if @resource['name'][/nve/] + if @resource['name'][/mgmt/i] + msg = 'Management interfaces can be displayed but not configured' + fail msg + end + if @resource['name'][/nve/i] msg = 'Use the cisco_vxlan_vtep type to manage nve interfaces' fail msg end From 3e94acc8e8fb59736a84d9019b450b1da44c10a4 Mon Sep 17 00:00:00 2001 From: sai chintalapudi <saichint@cisco.com> Date: Fri, 10 Nov 2017 09:03:58 -0800 Subject: [PATCH 195/203] remove mgmt check for interface --- lib/puppet/type/cisco_interface.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/puppet/type/cisco_interface.rb b/lib/puppet/type/cisco_interface.rb index d3e74f142..33dc721a7 100755 --- a/lib/puppet/type/cisco_interface.rb +++ b/lib/puppet/type/cisco_interface.rb @@ -141,12 +141,6 @@ newparam(:interface, namevar: :true) do desc 'Name of the interface on the network element. Valid values are string.' - validate do |name| - if name[/mgmt/i] - fail('Stay away from the management port.') - end # if - end - munge(&:downcase) end # param name From 743dbc8f0c4c230c91714d930f9d79021fc73114 Mon Sep 17 00:00:00 2001 From: Rick Sherman <rick@shermdog.com> Date: Mon, 13 Nov 2017 15:58:36 -0600 Subject: [PATCH 196/203] (NETDEV-30) Enhance syslog, tacacs, radius (#466) * (NETDEV-30) syslog_server add port Added port to syslog_server Updated syslog server to operate as a single item vs multiple calls. Follows work previously done with ntp types * (NETDEV-30) Add console, monitor, source interface to syslog_settings * (NETDEV-30) Add source_interface to tacacs/radius_global Added source_interface to tacacs_global and radius_global Moved attributes to use autogenerated methods were possible - so that they can be flushed only when modified. Corrected behaviour with encryption key and format handling. * (NETDEV-30) CHANGELOG * (NETDEV-30) allow for source_interface array in syslog, tacacs, radius Prior to this commit the following attributes were strings. To support systems that allow for per-VRF source interfaces these attributes were moved to arrays. Nexus does not support this functionality, but does need to allow for the data type as defined. Users can still use just the string value in their manifests, but puppet resource will display the value as an array. syslog_settings - source_interface - vrf radius_global - source_interface - vrf tacacs_global - source_interface - vrf * (NETDEV-30) bump netdev_stdlib req to 0.13.0 --- CHANGELOG.md | 15 ++ lib/puppet/provider/radius_global/cisco.rb | 54 +++-- lib/puppet/provider/syslog_server/cisco.rb | 24 ++- lib/puppet/provider/syslog_settings/cisco.rb | 67 ++++--- lib/puppet/provider/tacacs_global/cisco.rb | 61 ++++-- metadata.json | 2 +- tests/beaker_tests/lib/utilitylib.rb | 5 + .../radius_global_provider_defaults.rb | 103 +++------- .../radius_global_provider_nondefaults.rb | 185 ++++++++++++++++++ .../radius_global/radius_globallib.rb | 32 +-- .../syslog_server_provider_defaults.rb | 6 +- .../syslog_server/syslog_serverlib.rb | 4 +- .../syslog_settings_provider_defaults.rb | 50 +---- .../syslog_settings_provider_nondefaults.rb | 150 ++++++++++++++ .../syslog_settings/syslog_settingslib.rb | 41 +++- .../tacacs_global_provider_defaults.rb | 53 ++--- .../tacacs_global_provider_nondefaults.rb | 145 ++++++++++++++ .../tacacs_global/tacacs_globallib.rb | 13 +- 18 files changed, 761 insertions(+), 249 deletions(-) create mode 100644 tests/beaker_tests/radius_global/radius_global_provider_nondefaults.rb create mode 100644 tests/beaker_tests/syslog_settings/syslog_settings_provider_nondefaults.rb create mode 100644 tests/beaker_tests/tacacs_global/tacacs_global_provider_nondefaults.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index db9ade404..bbc59d14e 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,21 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `ensure => absent` for physical interfaces will put the interface into a default state. - `ensure => absent` for logical interfaces will cause them to be destroyed. +- Extend `syslog_server` with attribute: + - `port` + +- Extend `syslog_settings` with attributes: + - `console` + - `monitor` + - `source_interface` + - `vrf` + +- Extend `radius_global` with attribute: + - `source_interface` + +- Extend `tacacs_global` with attribute: + - `source_interface` + ### Removed ### Resolved Issues diff --git a/lib/puppet/provider/radius_global/cisco.rb b/lib/puppet/provider/radius_global/cisco.rb index 8a12e96a3..a1572826f 100644 --- a/lib/puppet/provider/radius_global/cisco.rb +++ b/lib/puppet/provider/radius_global/cisco.rb @@ -1,6 +1,6 @@ -# October, 2015 +# September, 2017 # -# Copyright (c) 2014-2016 Cisco and/or its affiliates. +# Copyright (c) 2014-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -31,10 +31,31 @@ mk_resource_methods - RADIUS_GLOBAL_PROPS = { - timeout: :timeout, - retransmit_count: :retransmit_count, - } + RADIUS_GLOBAL_GET_PROPS = [ + :key, + :key_format, + ] + + RADIUS_GLOBAL_SET_PROPS = [ + :retransmit_count, + :timeout, + ] + + RADIUS_GLOBAL_ARRAY_PROPS = [ + :source_interface + ] + + RADIUS_GLOBAL_NON_BOOL_PROPS = RADIUS_GLOBAL_GET_PROPS + + RADIUS_GLOBAL_SET_PROPS + + RADIUS_GLOBAL_CONFIG_PROPS = RADIUS_GLOBAL_SET_PROPS + + RADIUS_GLOBAL_ARRAY_PROPS + + PuppetX::Cisco::AutoGen.mk_puppet_methods(:array_flat, self, '@radius_global', + RADIUS_GLOBAL_ARRAY_PROPS) + + PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@radius_global', + RADIUS_GLOBAL_NON_BOOL_PROPS) def initialize(value={}) super(value) @@ -52,7 +73,9 @@ def self.get_properties(name, v) timeout: v.timeout ? v.timeout : -1, retransmit_count: v.retransmit_count ? v.retransmit_count : -1, key: v.key ? v.key : 'unset', - key_format: v.key_format ? v.key_format : -1, + # Only return the key format if there is a key configured + key_format: v.key.nil? || v.key.empty? ? nil : v.key_format, + source_interface: v.source_interface.nil? || v.source_interface.empty? ? ['unset'] : [v.source_interface], } new(current_state) @@ -109,6 +132,8 @@ def validate "This provider does not support the 'enable' property." if @resource[:enable] fail ArgumentError, "The 'key' property must be set when specifying 'key_format'." if @resource[:key_format] && !resource[:key] + fail ArgumentError, + "This provider does not support the 'vrf' property. " if @resource[:vrf] end def exists? @@ -118,13 +143,18 @@ def exists? def flush validate - RADIUS_GLOBAL_PROPS.each do |puppet_prop, cisco_prop| - if @resource[puppet_prop] && @radius_global.respond_to?("#{cisco_prop}=") - @radius_global.send("#{cisco_prop}=", munge_flush(@resource[puppet_prop])) - end + RADIUS_GLOBAL_CONFIG_PROPS.each do |prop| + next unless @resource[prop] + next if @property_flush[prop].nil? + # Other platforms require array for some types - Nexus does not + @property_flush[prop] = @property_flush[prop][0] if @property_flush[prop].is_a?(Array) + # Call the AutoGen setters for the @radius_global node_utils object. + @property_flush[prop] = nil if @property_flush[prop] == 'unset' + @radius_global.send("#{prop}=", @property_flush[prop]) if + @radius_global.respond_to?("#{prop}=") end # Handle key and keyformat setting - @radius_global.send('key_set', munge_flush(@resource[:key]), @resource[:key_format]) if @resource[:key] + @radius_global.send('key_set', munge_flush(@property_flush[:key]), munge_flush(@property_flush[:key_format])) if @property_flush[:key] end end # Puppet::Type diff --git a/lib/puppet/provider/syslog_server/cisco.rb b/lib/puppet/provider/syslog_server/cisco.rb index 93053d57d..410fca31c 100644 --- a/lib/puppet/provider/syslog_server/cisco.rb +++ b/lib/puppet/provider/syslog_server/cisco.rb @@ -1,4 +1,4 @@ -# November, 2014 +# September, 2017 # # Copyright (c) 2014-2016 Cisco and/or its affiliates. # @@ -31,6 +31,12 @@ mk_resource_methods + SYSLOG_SERVER_ALL_PROPS = [ + :severity_level, + :port, + :vrf, + ] + def initialize(value={}) super(value) @syslogserver = Cisco::SyslogServer.syslogservers[@property_hash[:name]] @@ -44,7 +50,8 @@ def self.properties_get(syslogserver_name, v) current_state = { ensure: :present, name: syslogserver_name, - severity_level: v.level, + severity_level: v.severity_level, + port: v.port, vrf: v.vrf, } @@ -92,7 +99,18 @@ def flush @syslogserver.destroy @syslogserver = nil else - @syslogserver = Cisco::SyslogServer.new(@resource[:name], @resource[:severity_level], @resource[:vrf]) + # Create new instance with configured options + opts = { 'name' => @resource[:name] } + SYSLOG_SERVER_ALL_PROPS.each do |prop| + next unless @resource[prop] + opts[prop.to_s] = @resource[prop].to_s + end + + begin + @ntpserver = Cisco::SyslogServer.new(opts) + rescue Cisco::CliError => e + error "Unable to set new values: #{e.message}" + end end end end # Puppet::Type diff --git a/lib/puppet/provider/syslog_settings/cisco.rb b/lib/puppet/provider/syslog_settings/cisco.rb index 0d360404e..1fa931e3a 100644 --- a/lib/puppet/provider/syslog_settings/cisco.rb +++ b/lib/puppet/provider/syslog_settings/cisco.rb @@ -1,6 +1,6 @@ -# November, 2014 +# September, 2017 # -# Copyright (c) 2014-2016 Cisco and/or its affiliates. +# Copyright (c) 2014-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -31,9 +31,23 @@ mk_resource_methods - SYSLOG_SETTINGS_PROPS = { - time_stamp_units: :timestamp - } + SYSLOG_SETTINGS_ARRAY_PROPS = [ + :source_interface + ] + + SYSLOG_SETTINGS_NON_BOOL_PROPS = [ + :console, + :monitor, + :time_stamp_units, + ] + + SYSLOG_CONFIG_PROPS = SYSLOG_SETTINGS_ARRAY_PROPS + SYSLOG_SETTINGS_NON_BOOL_PROPS + + PuppetX::Cisco::AutoGen.mk_puppet_methods(:array_flat, self, '@syslogsetting', + SYSLOG_SETTINGS_ARRAY_PROPS) + + PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@syslogsetting', + SYSLOG_SETTINGS_NON_BOOL_PROPS) def initialize(value={}) super(value) @@ -46,11 +60,20 @@ def self.properties_get(syslogsetting_name, v) debug "Checking instance, SyslogSetting #{syslogsetting_name}" current_state = { - name: 'default', - time_stamp_units: v.timestamp, - ensure: :present, + name: 'default', + ensure: :present, } + SYSLOG_SETTINGS_ARRAY_PROPS.each do |prop| + val = v.send(prop) + current_state[prop] = val ? [val] : ['unset'] + end + + SYSLOG_SETTINGS_NON_BOOL_PROPS.each do |prop| + val = v.send(prop) + current_state[prop] = val ? val : 'unset' + end + new(current_state) end # self.properties_get @@ -73,7 +96,7 @@ def self.prefetch(resources) end # self.prefetch def exists? - true + @property_hash[:ensure] == :present end def validate @@ -83,26 +106,22 @@ def validate fail ArgumentError, "This provider does not support the 'enable' property. "\ 'Syslog servers are enabled implicitly when using the syslog_server resource.' if @resource[:enable] - end - - def munge_flush(val) - if val.is_a?(String) && val.eql?('unset') - nil - elsif val.is_a?(Symbol) - val.to_s - else - val - end + fail ArgumentError, + "This provider does not support the 'vrf' property. " if @resource[:vrf] end def flush validate - SYSLOG_SETTINGS_PROPS.each do |puppet_prop, cisco_prop| - if @resource[puppet_prop] - @syslogsetting.send("#{cisco_prop}=", munge_flush(@resource[puppet_prop])) \ - if @syslogsetting.respond_to?("#{cisco_prop}=") - end + SYSLOG_CONFIG_PROPS.each do |prop| + next unless @resource[prop] + next if @property_flush[prop].nil? + # Other platforms require array for some types - Nexus does not + @property_flush[prop] = @property_flush[prop][0] if @property_flush[prop].is_a?(Array) + # Call the AutoGen setters for the @syslogsetting node_utils object. + @property_flush[prop] = nil if @property_flush[prop] == 'unset' + @syslogsetting.send("#{prop}=", @property_flush[prop]) if + @syslogsetting.respond_to?("#{prop}=") end end end # Puppet::Type diff --git a/lib/puppet/provider/tacacs_global/cisco.rb b/lib/puppet/provider/tacacs_global/cisco.rb index 96d7d92e4..37eafd283 100644 --- a/lib/puppet/provider/tacacs_global/cisco.rb +++ b/lib/puppet/provider/tacacs_global/cisco.rb @@ -1,6 +1,6 @@ -# June, 2016 +# September, 2017 # -# Copyright (c) 2014-2016 Cisco and/or its affiliates. +# Copyright (c) 2014-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -31,9 +31,30 @@ mk_resource_methods - TACACS_GLOBAL_PROPS = { - timeout: :timeout - } + TACACS_GLOBAL_GET_PROPS = [ + :key, + :key_format, + ] + + TACACS_GLOBAL_SET_PROPS = [ + :timeout + ] + + TACACS_GLOBAL_ARRAY_PROPS = [ + :source_interface + ] + + TACACS_GLOBAL_NON_BOOL_PROPS = TACACS_GLOBAL_GET_PROPS + + TACACS_GLOBAL_SET_PROPS + + TACACS_GLOBAL_CONFIG_PROPS = TACACS_GLOBAL_SET_PROPS + + TACACS_GLOBAL_ARRAY_PROPS + + PuppetX::Cisco::AutoGen.mk_puppet_methods(:array_flat, self, '@tacacs_global', + TACACS_GLOBAL_ARRAY_PROPS) + + PuppetX::Cisco::AutoGen.mk_puppet_methods(:non_bool, self, '@tacacs_global', + TACACS_GLOBAL_NON_BOOL_PROPS) def initialize(value={}) super(value) @@ -46,11 +67,13 @@ def self.get_properties(name, v) debug "Checking instance, TacacsGlobal #{name}" current_state = { - ensure: :present, - name: v.name, - timeout: v.timeout ? v.timeout : -1, - key: v.key ? v.key : 'unset', - key_format: v.key_format ? v.key_format : -1, + ensure: :present, + name: v.name, + timeout: v.timeout ? v.timeout : -1, + key: v.key.nil? || v.key.empty? ? 'unset' : v.key, + # Only return the key format if there is a key configured + key_format: v.key.nil? || v.key.empty? ? nil : v.key_format, + source_interface: v.source_interface.nil? || v.source_interface.empty? ? ['unset'] : [v.source_interface], } new(current_state) @@ -107,6 +130,8 @@ def validate "This provider does not support the 'enable' property." if @resource[:enable] fail ArgumentError, "The 'key' property must be set when specifying 'key_format'." if @resource[:key_format] && !resource[:key] + fail ArgumentError, + "This provider does not support the 'vrf' property. " if @resource[:vrf] end def exists? @@ -116,11 +141,17 @@ def exists? def flush validate - TACACS_GLOBAL_PROPS.each do |puppet_prop, cisco_prop| - if @resource[puppet_prop] && @tacacs_global.respond_to?("#{cisco_prop}=") - @tacacs_global.send("#{cisco_prop}=", munge_flush(@resource[puppet_prop])) - end + TACACS_GLOBAL_CONFIG_PROPS.each do |prop| + next unless @resource[prop] + next if @property_flush[prop].nil? + # Other platforms require array for some types - Nexus does not + @property_flush[prop] = @property_flush[prop][0] if @property_flush[prop].is_a?(Array) + # Call the AutoGen setters for the @tacacs_global node_utils object. + @property_flush[prop] = nil if @property_flush[prop] == 'unset' + @tacacs_global.send("#{prop}=", @property_flush[prop]) if + @tacacs_global.respond_to?("#{prop}=") end - @tacacs_global.send('encryption_key_set', munge_flush(@resource[:key_format]), @resource[:key]) if @resource[:key] + + @tacacs_global.send('encryption_key_set', munge_flush(@property_flush[:key_format]), munge_flush(@property_flush[:key])) if @property_flush[:key] end end # Puppet::Type diff --git a/metadata.json b/metadata.json index 4a41f9fb6..5c9fc69b6 100644 --- a/metadata.json +++ b/metadata.json @@ -8,7 +8,7 @@ "project_page": "https://github.com/cisco/cisco-network-puppet-module", "issues_url": "https://github.com/cisco/cisco-network-puppet-module/issues", "dependencies": [ - { "name": "puppetlabs/netdev_stdlib", "version_requirement": ">=0.12.0" } + { "name": "puppetlabs/netdev_stdlib", "version_requirement": ">=0.13.0" } ], "requirements": [ { diff --git a/tests/beaker_tests/lib/utilitylib.rb b/tests/beaker_tests/lib/utilitylib.rb index 727261878..e53279e70 100644 --- a/tests/beaker_tests/lib/utilitylib.rb +++ b/tests/beaker_tests/lib/utilitylib.rb @@ -1253,6 +1253,11 @@ def find_interface(tests, id=nil, skipcheck=true) # Skip the first interface we find in case it's our access interface. # TODO: check the interface IP address like we do in node_utils intf = all.grep(%r{ethernet\d+/\d+$})[1] + + when /mgmt/i + all = get_current_resource_instances(tests[:agent], 'network_interface') + # TODO: check the interface IP address like we do in node_utils + intf = all.grep(/mgmt\d+$/)[0] end if skipcheck && intf.nil? diff --git a/tests/beaker_tests/radius_global/radius_global_provider_defaults.rb b/tests/beaker_tests/radius_global/radius_global_provider_defaults.rb index d02f5c109..a0704e415 100644 --- a/tests/beaker_tests/radius_global/radius_global_provider_defaults.rb +++ b/tests/beaker_tests/radius_global/radius_global_provider_defaults.rb @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. +# Copyright (c) 2014-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -29,12 +29,12 @@ # # TestCase: # --------- -# This is a radius_global resource test that tests for default value for -# 'ensure' attribute of a radius_global resource. +# This is a radius_global resource test that tests default attributes of +# tacacs_global resource. # # There are 2 sections to the testcase: Setup, group of teststeps. # The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-4 deal with cisco_snmp_group_resource and its +# Steps 2+ deal with radius_global and its # verification using Puppet Agent and the switch running-config. # # The testcode checks for exit_codes from Puppet Agent, Vegas shell and @@ -58,55 +58,23 @@ result = 'PASS' testheader = 'radius_global Resource :: All Attributes Defaults' -# @test_name [TestCase] Executes defaults testcase for radius_global Resource. -test_name "TestCase :: #{testheader}" do - # @step [Step] Sets up switch for provider test. - step 'TestStep :: Setup switch for provider' do - logger.info('Setup switch for provider') - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource present manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, RadiusGlobalLib.create_radius_global_manifest) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [0, 2]) +def cleanup + logger.info('Testcase Cleanup:') - logger.info("Get resource present manifest from master :: #{result}") - end - - # @step [Step] Checks radius_global resource on agent using resource cmd. - step 'TestStep :: Check radius_global resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + 'resource radius_global default' - on(agent, cmd_str) - output = stdout - search_pattern_in_output(output, { 'key' => add_quotes('44444444') }, - false, self, logger) - search_pattern_in_output(output, { 'key_format' => '7' }, - false, self, logger) - search_pattern_in_output(output, { 'retransmit_count' => '4' }, - false, self, logger) - search_pattern_in_output(output, { 'timeout' => '2' }, - false, self, logger) + command_config(agent, 'radius-server timeout 5') + command_config(agent, 'radius-server retransmit 1') + command_config(agent, 'no ip radius source-interface') - logger.info("Check radius_global resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource present (with changes)manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, RadiusGlobalLib.create_radius_global_manifest_change) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) + # To remove a configured key we have ot know the key value + on(agent, get_vshell_cmd('show running-config radius | include key')) + key = stdout.match('^radius-server key (\d+)\s+(.*)') + command_config(agent, "no radius-server key #{key[1]} #{key[2]}", "removing key #{key[2]}") if key +end - logger.info("Get resource present manifest from master :: #{result}") - end +# @test_name [TestCase] Executes defaults testcase for radius_global Resource. +test_name "TestCase :: #{testheader}" do + cleanup + teardown { cleanup } # @step [Step] Checks radius_global resource on agent using resource cmd. step 'TestStep :: Check radius_global resource presence on agent' do @@ -115,45 +83,18 @@ cmd_str = PUPPET_BINPATH + 'resource radius_global default' on(agent, cmd_str) output = stdout - search_pattern_in_output(output, { 'key' => add_quotes('44444444') }, + search_pattern_in_output(output, { 'key' => 'unset' }, false, self, logger) - search_pattern_in_output(output, { 'key_format' => '7' }, + search_pattern_in_output(output, { 'retransmit_count' => '1' }, false, self, logger) - search_pattern_in_output(output, { 'retransmit_count' => '3' }, + search_pattern_in_output(output, { 'source_interface' => "['unset']" }, false, self, logger) - search_pattern_in_output(output, { 'timeout' => '1' }, + search_pattern_in_output(output, { 'timeout' => '5' }, false, self, logger) logger.info("Check radius_global resource presence on agent :: #{result}") end - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource present (with changes)manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, RadiusGlobalLib.create_radius_global_manifest_change_removed) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource present manifest from master :: #{result}") - end - - # @step [Step] Checks radius_global resource on agent using resource cmd. - step 'TestStep :: Check radius_global resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + 'resource radius_global default' - on(agent, cmd_str) do - search_pattern_in_output(stdout, { 'key' => 'unset' }, - false, self, logger) - search_pattern_in_output(stdout, { 'key_format' => '-1' }, - false, self, logger) - end - - logger.info("Check radius_global resource presence on agent :: #{result}") - end - # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. raise_passfail_exception(result, testheader, self, logger) end diff --git a/tests/beaker_tests/radius_global/radius_global_provider_nondefaults.rb b/tests/beaker_tests/radius_global/radius_global_provider_nondefaults.rb new file mode 100644 index 000000000..98c728cdd --- /dev/null +++ b/tests/beaker_tests/radius_global/radius_global_provider_nondefaults.rb @@ -0,0 +1,185 @@ +############################################################################### +# Copyright (c) 2014-2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# TestCase Name: +# ------------- +# RadiusGlobal-Provider-NonDefaults.rb +# +# TestCase Prerequisites: +# ----------------------- +# This is a radius_global resource testcase for Puppet Agent on Nexus devices. +# The test case assumes the following prerequisites are already satisfied: +# A. Populating the HOSTS configuration file with the agent and master +# information. +# B. Enabling SSH connection prerequisites on the N9K switch based Agent. +# C. Starting of Puppet master server on master. +# D. Sending to and signing of Puppet agent certificate request on master. +# +# TestCase: +# --------- +# This is a radius_global resource test that tests non-default attributes of +# tacacs_global resource. +# +# There are 2 sections to the testcase: Setup, group of teststeps. +# The 1st step is the Setup teststep that cleans up the switch state. +# Steps 2+ deal with radius_global and its +# verification using Puppet Agent and the switch running-config. +# +# The testcode checks for exit_codes from Puppet Agent, Vegas shell and +# Bash shell command executions. For Vegas shell and Bash shell command +# string executions, this is the exit_code convention: +# 0 - successful command execution, > 0 - failed command execution. +# For Puppet Agent command string executions, this is the exit_code convention: +# 0 - no changes have occurred, 1 - errors have occurred, +# 2 - changes have occurred, 4 - failures have occurred and +# 6 - changes and failures have occurred. +# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. +# The testcode also uses RegExp pattern matching on stdout or output IO +# instance attributes of Result object from on() method invocation. +# +############################################################################### + +# Require UtilityLib.rb and SnmpGroupLib.rb paths. +require File.expand_path('../../lib/utilitylib.rb', __FILE__) +require File.expand_path('../radius_globallib.rb', __FILE__) + +result = 'PASS' +testheader = 'radius_global Resource :: All Attributes Defaults' + +def cleanup + logger.info('Testcase Cleanup:') + + command_config(agent, 'radius-server timeout 5') + command_config(agent, 'radius-server retransmit 1') + command_config(agent, 'no ip radius source-interface') + + # To remove a configured key we have ot know the key value + on(agent, get_vshell_cmd('show running-config radius | include key')) + key = stdout.match('^radius-server key (\d+)\s+(.*)') + command_config(agent, "no radius-server key #{key[1]} #{key[2]}", "removing key #{key[2]}") if key +end + +# @test_name [TestCase] Executes non-defaults testcase for radius_global Resource. +test_name "TestCase :: #{testheader}" do + cleanup + teardown { cleanup } + + # @step [Step] Sets up switch for provider test. + step 'TestStep :: Setup switch for provider' do + logger.info('Setup switch for provider') + end + + # @step [Step] Requests manifest from the master server to the agent. + step 'TestStep :: Get resource present manifest from master' do + # Expected exit_code is 0 since this is a bash shell cmd. + on(master, RadiusGlobalLib.create_radius_global_manifest) + + # Expected exit_code is 2 since this is a puppet agent cmd with change. + cmd_str = PUPPET_BINPATH + 'agent -t' + on(agent, cmd_str, acceptable_exit_codes: [0, 2]) + + logger.info("Get resource present manifest from master :: #{result}") + end + + # @step [Step] Checks radius_global resource on agent using resource cmd. + step 'TestStep :: Check radius_global resource presence on agent' do + # Expected exit_code is 0 since this is a puppet resource cmd. + # Flag is set to false to check for presence of RegExp pattern in stdout. + cmd_str = PUPPET_BINPATH + 'resource radius_global default' + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, { 'key' => add_quotes('44444444') }, + false, self, logger) + search_pattern_in_output(output, { 'key_format' => '7' }, + false, self, logger) + search_pattern_in_output(output, { 'retransmit_count' => '4' }, + false, self, logger) + search_pattern_in_output(output, { 'source_interface' => "['loopback0']" }, + false, self, logger) + search_pattern_in_output(output, { 'timeout' => '2' }, + false, self, logger) + + logger.info("Check radius_global resource presence on agent :: #{result}") + end + + # @step [Step] Requests manifest from the master server to the agent. + step 'TestStep :: Get resource present (with changes)manifest from master' do + # Expected exit_code is 0 since this is a bash shell cmd. + on(master, RadiusGlobalLib.create_radius_global_manifest_change) + + # Expected exit_code is 2 since this is a puppet agent cmd with change. + cmd_str = PUPPET_BINPATH + 'agent -t' + on(agent, cmd_str, acceptable_exit_codes: [2]) + + logger.info("Get resource present manifest from master :: #{result}") + end + + # @step [Step] Checks radius_global resource on agent using resource cmd. + step 'TestStep :: Check radius_global resource presence on agent' do + # Expected exit_code is 0 since this is a puppet resource cmd. + # Flag is set to false to check for presence of RegExp pattern in stdout. + cmd_str = PUPPET_BINPATH + 'resource radius_global default' + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, { 'key' => add_quotes('55555555') }, + false, self, logger) + search_pattern_in_output(output, { 'key_format' => '7' }, + false, self, logger) + search_pattern_in_output(output, { 'retransmit_count' => '2' }, + false, self, logger) + search_pattern_in_output(output, { 'source_interface' => "['loopback1']" }, + false, self, logger) + search_pattern_in_output(output, { 'timeout' => '2' }, + false, self, logger) + + logger.info("Check radius_global resource presence on agent :: #{result}") + end + + # @step [Step] Requests manifest from the master server to the agent. + step 'TestStep :: Get resource present (with changes)manifest from master' do + # Expected exit_code is 0 since this is a bash shell cmd. + on(master, RadiusGlobalLib.create_radius_global_default) + + # Expected exit_code is 2 since this is a puppet agent cmd with change. + cmd_str = PUPPET_BINPATH + 'agent -t' + on(agent, cmd_str, acceptable_exit_codes: [2]) + + logger.info("Get resource present manifest from master :: #{result}") + end + + # @step [Step] Checks radius_global resource on agent using resource cmd. + step 'TestStep :: Check radius_global resource presence on agent' do + # Expected exit_code is 0 since this is a puppet resource cmd. + # Flag is set to false to check for presence of RegExp pattern in stdout. + cmd_str = PUPPET_BINPATH + 'resource radius_global default' + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, { 'key' => 'unset' }, + false, self, logger) + search_pattern_in_output(output, { 'retransmit_count' => '1' }, + false, self, logger) + search_pattern_in_output(output, { 'source_interface' => "['unset']" }, + false, self, logger) + search_pattern_in_output(output, { 'timeout' => '5' }, + false, self, logger) + + logger.info("Check radius_global resource presence on agent :: #{result}") + end + + # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. + raise_passfail_exception(result, testheader, self, logger) +end + +logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/radius_global/radius_globallib.rb b/tests/beaker_tests/radius_global/radius_globallib.rb index 77df16215..5a55e09f5 100644 --- a/tests/beaker_tests/radius_global/radius_globallib.rb +++ b/tests/beaker_tests/radius_global/radius_globallib.rb @@ -40,35 +40,35 @@ module RadiusGlobalLib # A. Methods to create manifests for radius_global Puppet provider test cases. - # Method to create a manifest for radius_global + # Method to create a default manifest for radius_global # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_radius_global_manifest + def self.create_radius_global_default manifest_str = "cat <<EOF >#{PUPPETMASTER_MANIFESTPATH} node default { radius_global { 'default': - key => '44444444', - key_format => '7', - retransmit_count => '4', - timeout => '2', + key => 'unset', + retransmit_count => '1', + source_interface => 'unset', + timeout => '5', } } EOF" manifest_str end - # Method to create a manifest for radius_global resource - # with a few changes made from above. + # Method to create a manifest for radius_global # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_radius_global_manifest_change + def self.create_radius_global_manifest manifest_str = "cat <<EOF >#{PUPPETMASTER_MANIFESTPATH} node default { radius_global { 'default': key => '44444444', key_format => '7', - retransmit_count => '3', - timeout => '1', + retransmit_count => '4', + source_interface => 'loopback0', + timeout => '2', } } EOF" @@ -76,14 +76,18 @@ def self.create_radius_global_manifest_change end # Method to create a manifest for radius_global resource - # with a few properties removed made from above. + # with a few changes made from above. # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_radius_global_manifest_change_removed + def self.create_radius_global_manifest_change manifest_str = "cat <<EOF >#{PUPPETMASTER_MANIFESTPATH} node default { radius_global { 'default': - key => 'unset', + key => '55555555', + key_format => '7', + retransmit_count => '2', + source_interface => 'loopback1', + timeout => '2', } } EOF" diff --git a/tests/beaker_tests/syslog_server/syslog_server_provider_defaults.rb b/tests/beaker_tests/syslog_server/syslog_server_provider_defaults.rb index 0587c3dfe..88bdde961 100644 --- a/tests/beaker_tests/syslog_server/syslog_server_provider_defaults.rb +++ b/tests/beaker_tests/syslog_server/syslog_server_provider_defaults.rb @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. +# Copyright (c) 2014-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -90,6 +90,8 @@ false, self, logger) search_pattern_in_output(stdout, { 'severity_level' => '2' }, false, self, logger) + search_pattern_in_output(stdout, { 'port' => '5555' }, + false, self, logger) search_pattern_in_output(stdout, { 'vrf' => 'default' }, false, self, logger) end @@ -144,6 +146,8 @@ false, self, logger) search_pattern_in_output(stdout, { 'severity_level' => '2' }, false, self, logger) + search_pattern_in_output(stdout, { 'port' => '5555' }, + false, self, logger) search_pattern_in_output(stdout, { 'vrf' => 'default' }, false, self, logger) end diff --git a/tests/beaker_tests/syslog_server/syslog_serverlib.rb b/tests/beaker_tests/syslog_server/syslog_serverlib.rb index 9a0f1e70a..356ae5fe1 100644 --- a/tests/beaker_tests/syslog_server/syslog_serverlib.rb +++ b/tests/beaker_tests/syslog_server/syslog_serverlib.rb @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. +# Copyright (c) 2014-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -50,6 +50,7 @@ def self.create_syslog_server_manifest_present syslog_server {'1.2.3.4': ensure => present, severity_level => 2, + port => 5555, vrf => 'default', } } @@ -84,6 +85,7 @@ def self.create_syslog_server_manifest_present_ipv6 syslog_server {'2003::3': ensure => present, severity_level => 2, + port => 5555, vrf => 'default', } } diff --git a/tests/beaker_tests/syslog_settings/syslog_settings_provider_defaults.rb b/tests/beaker_tests/syslog_settings/syslog_settings_provider_defaults.rb index 71d785b9d..f6a9af0e8 100644 --- a/tests/beaker_tests/syslog_settings/syslog_settings_provider_defaults.rb +++ b/tests/beaker_tests/syslog_settings/syslog_settings_provider_defaults.rb @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. +# Copyright (c) 2014-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -29,12 +29,12 @@ # # TestCase: # --------- -# This is a syslog_settings resource test that tests for default value for -# 'ensure' attribute of a syslog_settings resource. +# This is a syslog_settings resource test that tests for default values for the +# syslog_settings resource. # # There are 2 sections to the testcase: Setup, group of teststeps. # The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-4 deal with cisco_snmp_group_resource and its +# Steps 2+ deal with syslog_settings and its # verification using Puppet Agent and the switch running-config. # # The testcode checks for exit_codes from Puppet Agent, Vegas shell and @@ -64,52 +64,14 @@ # @step [Step] Sets up switch for provider test. step 'TestStep :: Setup switch for provider' do - # For deterministic results, make sure syslog_settings is set to - # seconds. - on(master, SyslogSettingLib.create_syslog_settings_manifest_seconds) + # For deterministic results, make sure syslog_settings is set to defaults. + on(master, SyslogSettingLib.create_syslog_settings_manifest_default) cmd_str = PUPPET_BINPATH + 'agent -t' on(agent, cmd_str, acceptable_exit_codes: [0, 2]) logger.info('Setup switch for provider') end - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource present manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SyslogSettingLib.create_syslog_settings_manifest_milliseconds) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource present manifest from master :: #{result}") - end - - # @step [Step] Checks syslog_settings resource on agent using resource cmd. - step 'TestStep :: Check syslog_settings resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + 'resource syslog_settings default' - on(agent, cmd_str) do - search_pattern_in_output(stdout, { 'time_stamp_units' => 'milliseconds' }, - false, self, logger) - end - - logger.info("Check syslog_settings resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource present manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, SyslogSettingLib.create_syslog_settings_manifest_seconds) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource present manifest from master :: #{result}") - end - # @step [Step] Checks syslog_settings resource on agent using resource cmd. step 'TestStep :: Check syslog_settings resource presence on agent' do # Expected exit_code is 0 since this is a puppet resource cmd. diff --git a/tests/beaker_tests/syslog_settings/syslog_settings_provider_nondefaults.rb b/tests/beaker_tests/syslog_settings/syslog_settings_provider_nondefaults.rb new file mode 100644 index 000000000..6471d8fdb --- /dev/null +++ b/tests/beaker_tests/syslog_settings/syslog_settings_provider_nondefaults.rb @@ -0,0 +1,150 @@ +############################################################################### +# Copyright (c) 2014-2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# TestCase Name: +# ------------- +# SyslogSetting-Provider-NonDefaults.rb +# +# TestCase Prerequisites: +# ----------------------- +# This is a syslog_settings resource testcase for Puppet Agent on Nexus devices. +# The test case assumes the following prerequisites are already satisfied: +# A. Populating the HOSTS configuration file with the agent and master +# information. +# B. Enabling SSH connection prerequisites on the N9K switch based Agent. +# C. Starting of Puppet master server on master. +# D. Sending to and signing of Puppet agent certificate request on master. +# +# TestCase: +# --------- +# This is a syslog_settings resource test that tests for nondefault values for +# the syslog_settings resource. +# +# There are 2 sections to the testcase: Setup, group of teststeps. +# The 1st step is the Setup teststep that cleans up the switch state. +# Steps 2+ deal with syslog_settings and its +# verification using Puppet Agent and the switch running-config. +# +# The testcode checks for exit_codes from Puppet Agent, Vegas shell and +# Bash shell command executions. For Vegas shell and Bash shell command +# string executions, this is the exit_code convention: +# 0 - successful command execution, > 0 - failed command execution. +# For Puppet Agent command string executions, this is the exit_code convention: +# 0 - no changes have occurred, 1 - errors have occurred, +# 2 - changes have occurred, 4 - failures have occurred and +# 6 - changes and failures have occurred. +# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. +# The testcode also uses RegExp pattern matching on stdout or output IO +# instance attributes of Result object from on() method invocation. +# +############################################################################### + +# Require UtilityLib.rb and SnmpGroupLib.rb paths. +require File.expand_path('../../lib/utilitylib.rb', __FILE__) +require File.expand_path('../syslog_settingslib.rb', __FILE__) + +result = 'PASS' +testheader = 'syslog_settings Resource :: All Attributes Non-Defaults' + +tests = { + agent: agent, + master: master, + intf_type: 'mgmt', + resource_name: 'syslog_settings', +} + +# @test_name [TestCase] Executes defaults testcase for syslog_settings Resource. +test_name "TestCase :: #{testheader}" do + raise_skip_exception('Not supported on IOS XR', self) if operating_system == 'ios_xr' + # Find an available test interface on this device + intf = find_interface(tests) + + # @step [Step] Sets up switch for provider test. + step 'TestStep :: Setup switch for provider' do + # For deterministic results, make sure syslog_settings is set to defaults. + on(master, SyslogSettingLib.create_syslog_settings_manifest_default) + cmd_str = PUPPET_BINPATH + 'agent -t' + on(agent, cmd_str, acceptable_exit_codes: [0, 2]) + + logger.info('Setup switch for provider') + end + + # @step [Step] Requests manifest from the master server to the agent. + step 'TestStep :: Get resource non-default manifest from master' do + # Expected exit_code is 0 since this is a bash shell cmd. + on(master, SyslogSettingLib.create_syslog_settings_manifest_nondefault(intf)) + + # Expected exit_code is 2 since this is a puppet agent cmd with change. + cmd_str = PUPPET_BINPATH + 'agent -t' + on(agent, cmd_str, acceptable_exit_codes: [2]) + + logger.info("Get resource non-default manifest from master :: #{result}") + end + + # @step [Step] Checks syslog_settings resource on agent using resource cmd. + step 'TestStep :: Check syslog_settings non-default on agent' do + # Expected exit_code is 0 since this is a puppet resource cmd. + # Flag is set to false to check for presence of RegExp pattern in stdout. + cmd_str = PUPPET_BINPATH + 'resource syslog_settings default' + on(agent, cmd_str) do + search_pattern_in_output(stdout, { 'console' => '1' }, + false, self, logger) + search_pattern_in_output(stdout, { 'monitor' => '1' }, + false, self, logger) + search_pattern_in_output(stdout, { 'source_interface' => "['#{intf}']" }, + false, self, logger) + search_pattern_in_output(stdout, { 'time_stamp_units' => 'milliseconds' }, + false, self, logger) + end + + logger.info("Check syslog_settings non-default on agent :: #{result}") + end + + # @step [Step] Requests manifest from the master server to the agent. + step 'TestStep :: Get resource unset manifest from master' do + # Expected exit_code is 0 since this is a bash shell cmd. + on(master, SyslogSettingLib.create_syslog_settings_manifest_unset) + + # Expected exit_code is 2 since this is a puppet agent cmd with change. + cmd_str = PUPPET_BINPATH + 'agent -t' + on(agent, cmd_str, acceptable_exit_codes: [2]) + + logger.info("Get resource non-default manifest from master :: #{result}") + end + + # @step [Step] Checks syslog_settings resource on agent using resource cmd. + step 'TestStep :: Check syslog_settings unset on agent' do + # Expected exit_code is 0 since this is a puppet resource cmd. + # Flag is set to false to check for presence of RegExp pattern in stdout. + cmd_str = PUPPET_BINPATH + 'resource syslog_settings default' + on(agent, cmd_str) do + search_pattern_in_output(stdout, { 'console' => 'unset' }, + false, self, logger) + search_pattern_in_output(stdout, { 'monitor' => 'unset' }, + false, self, logger) + search_pattern_in_output(stdout, { 'source_interface' => "['unset']" }, + false, self, logger) + search_pattern_in_output(stdout, { 'time_stamp_units' => 'seconds' }, + false, self, logger) + end + + logger.info("Check syslog_settings unset on agent :: #{result}") + end + + # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. + raise_passfail_exception(result, testheader, self, logger) +end + +logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/syslog_settings/syslog_settingslib.rb b/tests/beaker_tests/syslog_settings/syslog_settingslib.rb index 842a4e026..bd6f6e71a 100644 --- a/tests/beaker_tests/syslog_settings/syslog_settingslib.rb +++ b/tests/beaker_tests/syslog_settings/syslog_settingslib.rb @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. +# Copyright (c) 2014-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -40,29 +40,50 @@ module SyslogSettingLib # A. Methods to create manifests for syslog_setting Puppet provider test cases. - # Method to create a manifest for syslog_setting resource attribute 'ensure' - # where 'ensure' is set to present. + # Method to create a manifest for syslog_setting with default attributes # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_syslog_settings_manifest_milliseconds + def self.create_syslog_settings_manifest_default manifest_str = "cat <<EOF >#{PUPPETMASTER_MANIFESTPATH} node default { - syslog_settings {'default': - time_stamp_units => 'milliseconds', - } + syslog_settings {'default': + console => '2', + monitor => '5', + source_interface => 'unset', + time_stamp_units => 'seconds', + } +} +EOF" + manifest_str + end + + # Method to create a manifest for syslog_setting with non-default attributes + # @param intf [String] source_interface + # @result none [None] Returns no object. + def self.create_syslog_settings_manifest_nondefault(intf) + manifest_str = "cat <<EOF >#{PUPPETMASTER_MANIFESTPATH} +node default { + syslog_settings {'default': + console => '1', + monitor => '1', + source_interface => '#{intf}', + time_stamp_units => 'milliseconds', + } } EOF" manifest_str end - # Method to create a manifest for syslog_setting resource attribute 'ensure' - # where 'ensure' is set to absent. + # Method to create a manifest for syslog_setting with unset attributes # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_syslog_settings_manifest_seconds + def self.create_syslog_settings_manifest_unset manifest_str = "cat <<EOF >#{PUPPETMASTER_MANIFESTPATH} node default { syslog_settings {'default': + console => 'unset', + monitor => 'unset', + source_interface => 'unset', time_stamp_units => 'seconds', } } diff --git a/tests/beaker_tests/tacacs_global/tacacs_global_provider_defaults.rb b/tests/beaker_tests/tacacs_global/tacacs_global_provider_defaults.rb index 4acdfef1d..5587fe6fa 100644 --- a/tests/beaker_tests/tacacs_global/tacacs_global_provider_defaults.rb +++ b/tests/beaker_tests/tacacs_global/tacacs_global_provider_defaults.rb @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2014-2015 Cisco and/or its affiliates. +# Copyright (c) 2014-2017 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -29,12 +29,12 @@ # # TestCase: # --------- -# This is a tacacs_global resource test that tests for default value for -# 'ensure' attribute of a tacacs_global resource. +# This is a tacacs_global resource test that tests default attribute of +# tacacs_global resource. # # There are 2 sections to the testcase: Setup, group of teststeps. # The 1st step is the Setup teststep that cleans up the switch state. -# Steps 2-4 deal with cisco_snmp_group_resource and its +# Steps 2+ deal with tacacs_global and its # verification using Puppet Agent and the switch running-config. # # The testcode checks for exit_codes from Puppet Agent, Vegas shell and @@ -58,12 +58,20 @@ result = 'PASS' testheader = 'tacacs_global Resource :: All Attributes Defaults' +def cleanup + logger.info('Testcase Cleanup:') + command_config(agent, 'no feature tacacs+') +end + # @test_name [TestCase] Executes defaults testcase for tacacs_global Resource. test_name "TestCase :: #{testheader}" do + cleanup + teardown { cleanup } + # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource present manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. - on(master, TacacsGlobalLib.create_tacacs_global_manifest) + on(master, TacacsGlobalLib.create_tacacs_global_default) # Expected exit_code is 0 or 2 depending on the state of the device. cmd_str = PUPPET_BINPATH + 'agent -t' @@ -79,40 +87,11 @@ cmd_str = PUPPET_BINPATH + 'resource tacacs_global default' on(agent, cmd_str) output = stdout - search_pattern_in_output(output, { 'key' => add_quotes('44444444') }, - false, self, logger) - search_pattern_in_output(output, { 'key_format' => '7' }, - false, self, logger) - search_pattern_in_output(output, { 'timeout' => '2' }, - false, self, logger) - - logger.info("Check tacacs_global resource presence on agent :: #{result}") - end - - # @step [Step] Requests manifest from the master server to the agent. - step 'TestStep :: Get resource present (with changes) manifest from master' do - # Expected exit_code is 0 since this is a bash shell cmd. - on(master, TacacsGlobalLib.create_tacacs_global_manifest_change) - - # Expected exit_code is 2 since this is a puppet agent cmd with change. - cmd_str = PUPPET_BINPATH + 'agent -t' - on(agent, cmd_str, acceptable_exit_codes: [2]) - - logger.info("Get resource present manifest from master :: #{result}") - end - - # @step [Step] Checks tacacs_global resource on agent using resource cmd. - step 'TestStep :: Check tacacs_global resource presence on agent' do - # Expected exit_code is 0 since this is a puppet resource cmd. - # Flag is set to false to check for presence of RegExp pattern in stdout. - cmd_str = PUPPET_BINPATH + 'resource tacacs_global default' - on(agent, cmd_str) - output = stdout - search_pattern_in_output(output, { 'key' => add_quotes('44444444') }, + search_pattern_in_output(output, { 'key' => 'unset' }, false, self, logger) - search_pattern_in_output(output, { 'key_format' => '7' }, + search_pattern_in_output(output, { 'timeout' => '5' }, false, self, logger) - search_pattern_in_output(output, { 'timeout' => '1' }, + search_pattern_in_output(output, { 'source_interface' => "['unset']" }, false, self, logger) logger.info("Check tacacs_global resource presence on agent :: #{result}") diff --git a/tests/beaker_tests/tacacs_global/tacacs_global_provider_nondefaults.rb b/tests/beaker_tests/tacacs_global/tacacs_global_provider_nondefaults.rb new file mode 100644 index 000000000..051e68ffc --- /dev/null +++ b/tests/beaker_tests/tacacs_global/tacacs_global_provider_nondefaults.rb @@ -0,0 +1,145 @@ +############################################################################### +# Copyright (c) 2014-2017 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################### +# TestCase Name: +# ------------- +# TacacsGlobal-Provider-NonDefaults.rb +# +# TestCase Prerequisites: +# ----------------------- +# This is a tacacs_global resource testcase for Puppet Agent on Nexus devices. +# The test case assumes the following prerequisites are already satisfied: +# A. Populating the HOSTS configuration file with the agent and master +# information. +# B. Enabling SSH connection prerequisites on the N9K switch based Agent. +# C. Starting of Puppet master server on master. +# D. Sending to and signing of Puppet agent certificate request on master. +# +# TestCase: +# --------- +# This is a tacacs_global resource test that tests non-default attribute of +# tacacs_global resource. +# +# There are 2 sections to the testcase: Setup, group of teststeps. +# The 1st step is the Setup teststep that cleans up the switch state. +# Steps 2+ deal with tacacs_global and its +# verification using Puppet Agent and the switch running-config. +# +# The testcode checks for exit_codes from Puppet Agent, Vegas shell and +# Bash shell command executions. For Vegas shell and Bash shell command +# string executions, this is the exit_code convention: +# 0 - successful command execution, > 0 - failed command execution. +# For Puppet Agent command string executions, this is the exit_code convention: +# 0 - no changes have occurred, 1 - errors have occurred, +# 2 - changes have occurred, 4 - failures have occurred and +# 6 - changes and failures have occurred. +# 0 is the default exit_code checked in Beaker::DSL::Helpers::on() method. +# The testcode also uses RegExp pattern matching on stdout or output IO +# instance attributes of Result object from on() method invocation. +# +############################################################################### + +# Require UtilityLib.rb and SnmpGroupLib.rb paths. +require File.expand_path('../../lib/utilitylib.rb', __FILE__) +require File.expand_path('../tacacs_globallib.rb', __FILE__) + +result = 'PASS' +testheader = 'tacacs_global Resource :: All Attributes Non-Defaults' + +tests = { + agent: agent, + master: master, + intf_type: 'ethernet', + resource_name: 'tacacs_global', +} + +def cleanup + logger.info('Testcase Cleanup:') + command_config(agent, 'no feature tacacs+') +end + +# @test_name [TestCase] Executes defaults testcase for tacacs_global Resource. +test_name "TestCase :: #{testheader}" do + cleanup + teardown { cleanup } + + # Find an available test interface on this device + intf = find_interface(tests) + + # @step [Step] Requests manifest from the master server to the agent. + step 'TestStep :: Get resource present (with changes) manifest from master' do + # Expected exit_code is 0 since this is a bash shell cmd. + on(master, TacacsGlobalLib.create_tacacs_global_non_default(intf)) + + # Expected exit_code is 2 since this is a puppet agent cmd with change. + cmd_str = PUPPET_BINPATH + 'agent -t' + on(agent, cmd_str, acceptable_exit_codes: [2]) + + logger.info("Get resource present manifest from master :: #{result}") + end + + # @step [Step] Checks tacacs_global resource on agent using resource cmd. + step 'TestStep :: Check tacacs_global resource presence on agent' do + # Expected exit_code is 0 since this is a puppet resource cmd. + # Flag is set to false to check for presence of RegExp pattern in stdout. + cmd_str = PUPPET_BINPATH + 'resource tacacs_global default' + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, { 'key' => add_quotes('44444444') }, + false, self, logger) + search_pattern_in_output(output, { 'key_format' => '7' }, + false, self, logger) + search_pattern_in_output(output, { 'source_interface' => "['#{intf}']" }, + false, self, logger) + search_pattern_in_output(output, { 'timeout' => '1' }, + false, self, logger) + + logger.info("Check tacacs_global resource presence on agent :: #{result}") + end + + # @step [Step] Requests manifest from the master server to the agent. + step 'TestStep :: Get resource present manifest from master' do + # Expected exit_code is 0 since this is a bash shell cmd. + on(master, TacacsGlobalLib.create_tacacs_global_default) + + # Expected exit_code is 0 or 2 depending on the state of the device. + cmd_str = PUPPET_BINPATH + 'agent -t' + on(agent, cmd_str, acceptable_exit_codes: [0, 2]) + + logger.info("Get resource present manifest from master :: #{result}") + end + + # @step [Step] Checks tacacs_global resource on agent using resource cmd. + step 'TestStep :: Check tacacs_global resource presence on agent' do + # Expected exit_code is 0 since this is a puppet resource cmd. + # Flag is set to false to check for presence of RegExp pattern in stdout. + cmd_str = PUPPET_BINPATH + 'resource tacacs_global default' + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, { 'key' => 'unset' }, + false, self, logger) + search_pattern_in_output(output, { 'timeout' => '5' }, + false, self, logger) + search_pattern_in_output(output, { 'source_interface' => "['unset']" }, + false, self, logger) + + logger.info("Check tacacs_global resource presence on agent :: #{result}") + end + + # @raise [PassTest/FailTest] Raises PassTest/FailTest exception using result. + raise_passfail_exception(result, testheader, self, logger) +end + +logger.info("TestCase :: #{testheader} :: End") diff --git a/tests/beaker_tests/tacacs_global/tacacs_globallib.rb b/tests/beaker_tests/tacacs_global/tacacs_globallib.rb index 56e9073e0..4d1a63c65 100644 --- a/tests/beaker_tests/tacacs_global/tacacs_globallib.rb +++ b/tests/beaker_tests/tacacs_global/tacacs_globallib.rb @@ -43,13 +43,13 @@ module TacacsGlobalLib # Method to create a manifest for tacacs_global # @param none [None] No input parameters exist. # @result none [None] Returns no object. - def self.create_tacacs_global_manifest + def self.create_tacacs_global_default manifest_str = "cat <<EOF >#{PUPPETMASTER_MANIFESTPATH} node default { tacacs_global { 'default': - key => '44444444', - key_format => '7', - timeout => '2', + key => 'unset', + source_interface => 'unset', + timeout => '5', } } EOF" @@ -58,14 +58,15 @@ def self.create_tacacs_global_manifest # Method to create a manifest for tacacs_global resource # with a few changes made from above. - # @param none [None] No input parameters exist. + # @param intf [String] source_interface # @result none [None] Returns no object. - def self.create_tacacs_global_manifest_change + def self.create_tacacs_global_non_default(intf) manifest_str = "cat <<EOF >#{PUPPETMASTER_MANIFESTPATH} node default { tacacs_global { 'default': key => '44444444', key_format => '7', + source_interface => '#{intf}', timeout => '1', } } From 28742a45d06bed61ff0767bc90c6c1ce84156a26 Mon Sep 17 00:00:00 2001 From: Sean P McDonald <sean.mcdonald@puppetlabs.com> Date: Mon, 13 Nov 2017 14:00:55 -0800 Subject: [PATCH 197/203] Add puppet-blacksmith puppet_spec_helper gems/rake tasks (#472) This commit updates the Gemfile to include puppet-blacksmith and updates the Rakefile to include requiring the rake tasks for both puppet_spec_helper and puppet-blacksmith. This will allow use of rake tasks from both projects useful in testing and publishing this module --- .gitignore | 1 + Gemfile | 1 + Rakefile | 2 ++ 3 files changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index f26e04d0b..e90afb79f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ Gemfile.lock *~ *.swp hosts.cfg +vendor/ diff --git a/Gemfile b/Gemfile index a406af5fd..24227c91f 100644 --- a/Gemfile +++ b/Gemfile @@ -65,6 +65,7 @@ group :development, :unit_tests do gem 'pry', require: false gem 'rubocop', '= 0.35.1', require: false gem 'simplecov', require: false + gem 'puppet-blacksmith', '~> 3.4', require: false end # vim:ft=ruby diff --git a/Rakefile b/Rakefile index 61baca3ed..d45f1bc72 100644 --- a/Rakefile +++ b/Rakefile @@ -18,6 +18,8 @@ require 'facter' require 'rubocop/rake_task' +require 'puppetlabs_spec_helper/rake_tasks' +require 'puppet_blacksmith/rake_tasks' task default: %w(rubocop test) From 1447e985537b6cc50780b9bc962860b712989b30 Mon Sep 17 00:00:00 2001 From: Mike Smith <mike.smith@puppet.com> Date: Tue, 14 Nov 2017 07:51:32 -0800 Subject: [PATCH 198/203] Restrict package prodiver tests (#475) --- lib/puppet/provider/package/cisco.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/puppet/provider/package/cisco.rb b/lib/puppet/provider/package/cisco.rb index 6e2ddcd72..489101c8b 100644 --- a/lib/puppet/provider/package/cisco.rb +++ b/lib/puppet/provider/package/cisco.rb @@ -39,7 +39,7 @@ rpm('--version') yum('--version') python('--version') - rescue Puppet::ExecutionFailure + rescue Puppet::ExecutionFailure, Puppet::MissingCommand commands_present = false end confine true: commands_present From 749b508592de5729f7366a017a43838833e4eb7c Mon Sep 17 00:00:00 2001 From: Sean P McDonald <sean.mcdonald@puppetlabs.com> Date: Thu, 16 Nov 2017 07:29:19 -0800 Subject: [PATCH 199/203] Add requirement for beaker-abs in Gemfile, add puppet_test_env group (#476) This commit does two things: 1. Adds the beaker-abs gem in to the gemfile (this gem is another required gem for working in the test environment at Puppet.) 2. Adds a puppet_test_env group to the gemfile, to allow users at puppet to test with the beaker-abs gem and the users at cisco to install gems without it if necessary. --- Gemfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Gemfile b/Gemfile index 24227c91f..048cdd893 100644 --- a/Gemfile +++ b/Gemfile @@ -68,4 +68,8 @@ group :development, :unit_tests do gem 'puppet-blacksmith', '~> 3.4', require: false end +group :puppet_test_env do + gem 'beaker-abs', require: false +end + # vim:ft=ruby From dc7fe96926abc20058f3a326e67322b26bf6338e Mon Sep 17 00:00:00 2001 From: Mike Wiebe <mwiebe@cisco.com> Date: Mon, 27 Nov 2017 12:40:22 -0500 Subject: [PATCH 200/203] Use correct host_reachability flood for vxlan_vtp_vni dependencies (#477) --- tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb b/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb index a1b2d3bfa..8c576b3dd 100644 --- a/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb +++ b/tests/beaker_tests/cisco_vxlan_vtep_vni/test_vxlan_vtep_vni.rb @@ -233,7 +233,7 @@ def dependency_manifest(_tests, _id) " cisco_vxlan_vtep {'nve1': ensure => present, - host_reachability => 'evpn', + host_reachability => 'flood', shutdown => 'false', } " From 76f433a10990ab22706358b08d306c3212e6f345 Mon Sep 17 00:00:00 2001 From: Mike Wiebe <mwiebe@cisco.com> Date: Wed, 29 Nov 2017 11:42:56 -0500 Subject: [PATCH 201/203] Admin cookie override (#478) * Refactor TOC setup section * Document agent cookie auth * Address PR comment --- README.md | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7044120aa..8e9cdb2b8 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,9 @@ 1. [Module Description](#module-description) 1. [Setup](#setup) + * [Puppet Master](#setup-puppet-master) + * [Puppet Agent](#setup-puppet-agent) + * [Puppet Agent Authentication](#setup-agent-auth) 1. [Example Manifests](#example-manifests) 1. [Resource Reference](#resource-reference) * [Resource Type Catalog (by Technology)](#resource-by-tech) @@ -29,7 +32,7 @@ Contributions to the `ciscopuppet` module are welcome. See [CONTRIBUTING.md][DEV ## <a href='setup'>Setup</a> -#### Puppet Master +### <a name="setup-puppet-master">Puppet Master<a> The `ciscopuppet` module must be installed on the Puppet Master server. @@ -43,7 +46,7 @@ For more information on Puppet module installation see [Puppet Labs: Installing PuppetLabs provides NetDev resource support for Cisco Nexus devices with their [`puppetlabs-netdev-stdlib`](https://forge.puppet.com/puppetlabs/netdev_stdlib) module. Installing the `ciscopuppet` module automatically installs both the `ciscopuppet` and `netdev_stdlib` modules. -#### Puppet Agent +### <a name="setup-puppet-agent">Puppet Agent<a> The Puppet Agent requires installation and setup on each device. Agent setup can be performed as a manual process or it may be automated. For more information please see the [README-agent-install.md][USER-1] document for detailed instructions on agent installation and configuration on Cisco Nexus devices. @@ -94,6 +97,44 @@ Once installed, the GEM will remain persistent across system reloads within the See [General Documentation](#general-documentation) for information on Guestshell and OAC. +### <a name="setup-agent-auth">Puppet Agent Authentication<a> + +Puppet makes use of the nxos `admin` user by default for all types in this module. If a different user is required for puppet agent runs then the following procedure can be used to override `admin` with the desired user. + +**NOTE:** The user you select must already be configured on your device with the role `network-admin`. + +First create a different user with the role `network-admin`. + +~~~ +config term + username puppetuser password puppet role network-admin +end +~~~ + +Next create a file called `cisco_node_utils.yaml` under the `modules/ciscopuppet/files` directory on the puppet server and add a cookie `puppetuser:local` under the `default:` yaml key. + +```bash +puppetserver:> cat /etc/puppetlabs/code/environments/production/modules/ciscopuppet/files/cisco_node_utils.yaml +default: + cookie: 'puppetuser:local' +puppetserver:> +``` + +Now create and apply the following manifest on your nxos devices. + +~~~puppet + $cookie_src = "puppet:///modules/ciscopuppet/cisco_node_utils.yaml" + $cookie_tgt = "/${::identity['user']}/cisco_node_utils.yaml" + + file { $cookie_tgt : + ensure => file, + source => $cookie_src, + owner => 'root', + group => 'root', + mode => 'ug+rwx', + } +~~~ + ## <a href='example-manifests'>Example Manifests</a> This module has dependencies on the [`cisco_node_utils`](https://rubygems.org/gems/cisco_node_utils) ruby gem. After installing the Puppet Agent software, use Puppet's built-in [`Package`](https://github.com/cisco/cisco-network-puppet-module/blob/master/examples/install.pp#L17) provider to install the gem. From 759e2e870ae619023c97f0deb4dce04f74c00ede Mon Sep 17 00:00:00 2001 From: Rick Sherman <rick@shermdog.com> Date: Mon, 11 Dec 2017 13:17:00 -0600 Subject: [PATCH 202/203] (CISCO-60) tacacs_global timeout undef (#479) This commit changes the output of timeout from -1 to undef when not set. This more accurately reflects the real world state of the resource. --- README.md | 2 +- lib/puppet/provider/tacacs_global/cisco.rb | 4 ++-- .../tacacs_global_provider_defaults.rb | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8e9cdb2b8..235ba6bf8 100644 --- a/README.md +++ b/README.md @@ -5604,7 +5604,7 @@ Encryption key (plaintext or in hash form depending on key_format) Encryption key format [0-7] ##### `timeout` -Number of seconds before the timeout period ends +Number of seconds before the timeout period ends. Also supports [undef](https://puppet.com/docs/puppet/5.3/lang_data_undef.html) -- ### Type: tacacs_server diff --git a/lib/puppet/provider/tacacs_global/cisco.rb b/lib/puppet/provider/tacacs_global/cisco.rb index 37eafd283..73263a98f 100644 --- a/lib/puppet/provider/tacacs_global/cisco.rb +++ b/lib/puppet/provider/tacacs_global/cisco.rb @@ -1,4 +1,4 @@ -# September, 2017 +# December, 2017 # # Copyright (c) 2014-2017 Cisco and/or its affiliates. # @@ -69,7 +69,7 @@ def self.get_properties(name, v) current_state = { ensure: :present, name: v.name, - timeout: v.timeout ? v.timeout : -1, + timeout: v.timeout, key: v.key.nil? || v.key.empty? ? 'unset' : v.key, # Only return the key format if there is a key configured key_format: v.key.nil? || v.key.empty? ? nil : v.key_format, diff --git a/tests/beaker_tests/tacacs_global/tacacs_global_provider_defaults.rb b/tests/beaker_tests/tacacs_global/tacacs_global_provider_defaults.rb index 5587fe6fa..f49df80f0 100644 --- a/tests/beaker_tests/tacacs_global/tacacs_global_provider_defaults.rb +++ b/tests/beaker_tests/tacacs_global/tacacs_global_provider_defaults.rb @@ -68,6 +68,22 @@ def cleanup cleanup teardown { cleanup } + # @step [Step] Checks tacacs_global resource on agent using resource cmd. + step 'TestStep :: Check tacacs_global resource unconfigured' do + # Expected exit_code is 0 since this is a puppet resource cmd. + # Flag is set to false to check for presence of RegExp pattern in stdout. + cmd_str = PUPPET_BINPATH + 'resource tacacs_global default' + on(agent, cmd_str) + output = stdout + search_pattern_in_output(output, { 'key' => 'unset' }, + false, self, logger) + search_pattern_in_output(output, { 'source_interface' => "['unset']" }, + false, self, logger) + search_pattern_in_output(output, [/timeout/], true, self, logger) + + logger.info("Check tacacs_global resource unconfigured :: #{result}") + end + # @step [Step] Requests manifest from the master server to the agent. step 'TestStep :: Get resource present manifest from master' do # Expected exit_code is 0 since this is a bash shell cmd. From 4430435f8b1b6f51fcc085d2f6267b37128c115b Mon Sep 17 00:00:00 2001 From: Mike Wiebe <mwiebe@cisco.com> Date: Tue, 12 Dec 2017 10:19:58 -0500 Subject: [PATCH 203/203] Update markdown release tag date --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbc59d14e..9d569f77c 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [1.8.0] - 2017-11-09 +## [1.8.0] - 2017-12-12 ### New feature support #### Cisco Resources