diff --git a/CHANGELOG.md b/CHANGELOG.md index 38ce9a64..d9ebb746 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,39 @@ Changelog ========= +## [v1.10.0] + +### New Cisco Resources + +### Added +* Added syslog_facility with attribute: + * `level` +* Extend syslog_server with attribute: + * `facility` +* Extend interface with attributes: + * `ipv6_redirects` +* Extend ace with attributes: + * `proto_option` + * `vlan` + * `set_erspan_dscp` + * `set_erspan_gre_proto` +* Extend network_dns with attributes: + * `hostname` +* Added ability to specify environment at run time + +Example: +```ruby +env = { host: '192.168.1.1', port: nil, username: 'admin', password: 'admin123', cookie: nil } +Cisco::Environment.add_env('default', env) +``` + +### Changed + +### Removed + +### Issues Addressed +* Removed default values for authentication in `interface_hsrp_group` + ## [v1.9.0] ### New Cisco Resources @@ -554,6 +587,7 @@ Changelog [git-flow]: https://github.com/petervanderdoes/gitflow-avh [SimpleCov]: https://github.com/colszowka/simplecov +[v1.10.0]: https://github.com/cisco/cisco-network-node-utils/compare/v1.9.0...v1.10.0 [v1.9.0]: https://github.com/cisco/cisco-network-node-utils/compare/v1.8.0...v1.9.0 [v1.8.0]: https://github.com/cisco/cisco-network-node-utils/compare/v1.7.0...v1.8.0 [v1.7.0]: https://github.com/cisco/cisco-network-node-utils/compare/v1.6.0...v1.7.0 diff --git a/README.md b/README.md index c441d9eb..8866850d 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,14 @@ An example configuration file (illustrating each of the above scenarios) is prov *For security purposes, it is highly recommended that access to this file be restricted to only the owning user (`chmod 0600`).* +Configuration may also be specified at runtime and can be used in the absence of configuration files or to override them. + +Example: +```ruby +env = { host: '192.168.1.1', port: nil, username: 'admin', password: 'admin123', cookie: nil } +Cisco::Environment.add_env('default', env) +``` + ## Documentation ### Client @@ -151,12 +159,18 @@ client2 = Cisco::Client.create('n9k') # Warning: Make sure to exclude devices using the 'no_proxy' environment variable # to ensure successful remote connections. +# Add runtime configuration for remote device and connect +env = { host: '192.168.1.1', port: nil, username: 'admin', password: 'admin123', cookie: nil } +Cisco::Environment.add_env('remote', env) +client3 = Cisco::Client.create('remote') + # Use connections to get and set device state. client1.set(values: 'feature vtp') client1.set(values: 'vtp domain mycompany.com') puts client1.get(command: 'show vtp status | inc Domain') puts client2.get(command: 'show version') +puts client3.get(command: 'show version') ``` #### High-level Node API diff --git a/ext/mkrf_conf.rb b/ext/mkrf_conf.rb index 940c99d3..6abfcfab 100644 --- a/ext/mkrf_conf.rb +++ b/ext/mkrf_conf.rb @@ -37,7 +37,7 @@ os == 'ios_xr' || deps << Gem::Dependency.new('net_http_unix', '~> 0.2', '>= 0.2.1') # NX-OS doesn't need gRPC - os == 'nexus' || deps << Gem::Dependency.new('grpc', '~> 0.12') + os == 'nexus' || deps << Gem::Dependency.new('grpc', '~> 1.14.1') deps.each do |dep| installed = dep.matching_specs diff --git a/lib/cisco_node_utils/ace.rb b/lib/cisco_node_utils/ace.rb index a88f3d34..0f4d2d95 100644 --- a/lib/cisco_node_utils/ace.rb +++ b/lib/cisco_node_utils/ace.rb @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2016 Cisco and/or its affiliates. +# Copyright (c) 2015-2018 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. @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +require 'ipaddr' require_relative 'node_util' module Cisco @@ -73,6 +74,9 @@ def ace_get remark = Regexp.new('(?\d+) remark (?.*)').match(str) return remark unless remark.nil? + # specialized icmp protocol handling + return icmp_ace_get(str) if str.include?('icmp') + # rubocop:disable Metrics/LineLength regexp = Regexp.new('(?\d+) (?\S+)'\ ' *(?\d+|\S+)'\ @@ -95,6 +99,60 @@ def ace_get regexp.match(str) end + # icmp ace getter + def icmp_ace_get(str) + # rubocop:disable Metrics/LineLength + # fragments is nvgen at a different location than all other + # proto_option so get rid of it so as not to mess up other fields + str.sub!('fragments ', '') + regexp = Regexp.new('(?\d+) (?\S+)'\ + ' *(?\d+|\S+)'\ + ' *(?any|host \S+|[:\.0-9a-fA-F]+ [:\.0-9a-fA-F]+|[:\.0-9a-fA-F]+\/\d+|addrgroup \S+)'\ + ' *(?any|host \S+|[:\.0-9a-fA-F]+ [:\.0-9a-fA-F]+|[:\.0-9a-fA-F]+\/\d+|addrgroup \S+)'\ + ' *(?\S+)?'\ + ' *(?precedence \S+)?'\ + ' *(?dscp \S+)?'\ + ' *(?time-range \S+)?'\ + ' *(?packet-length (range \d+ \d+|(lt|eq|gt|neq) \d+))?'\ + ' *(?ttl \d+)?'\ + ' *(?vlan \d+)?'\ + ' *(?set-erspan-gre-proto \d+)?'\ + ' *(?set-erspan-dscp \d+)?'\ + ' *(?redirect \S+)?') + regexp_no_proto_option = Regexp.new('(?\d+) (?\S+)'\ + ' *(?\d+|\S+)'\ + ' *(?any|host \S+|[:\.0-9a-fA-F]+ [:\.0-9a-fA-F]+|[:\.0-9a-fA-F]+\/\d+|addrgroup \S+)'\ + ' *(?any|host \S+|[:\.0-9a-fA-F]+ [:\.0-9a-fA-F]+|[:\.0-9a-fA-F]+\/\d+|addrgroup \S+)'\ + ' *(?precedence \S+)?'\ + ' *(?dscp \S+)?'\ + ' *(?time-range \S+)?'\ + ' *(?packet-length (range \d+ \d+|(lt|eq|gt|neq) \d+))?'\ + ' *(?ttl \d+)?'\ + ' *(?vlan \d+)?'\ + ' *(?set-erspan-gre-proto \d+)?'\ + ' *(?set-erspan-dscp \d+)?'\ + ' *(?redirect \S+)?') + temp = regexp.match(str) + po = temp[:proto_option] + if po.nil? + return temp + # redirect can be proto_option or an actual redirect to interface + elsif po.strip.match(/redirect$/) + if str.match(/Ethernet|port-channel/) + # if proto_option is given as redirect and also redirect to intf + # we need to do extra processing + return temp if check_redirect_repeat(str) + return regexp_no_proto_option.match(str) + end + # the reserved keywords check + elsif po.strip.match(/precedence$|dscp$|time-range$|packet-length$|ttl$|vlan$|set-erspan-gre-proto$|set-erspan-dscp$|log$/) + return regexp_no_proto_option.match(str) + else + return temp + end + # rubocop:enable Metrics/LineLength + end + # common ace setter. Put the values you need in a hash and pass it in. # attrs = {:action=>'permit', :proto=>'tcp', :src =>'host 1.1.1.1'} def ace_set(attrs) @@ -130,6 +188,10 @@ def ace_set(attrs) :tcp_option_length, :redirect, :log, + :proto_option, + :set_erspan_dscp, + :set_erspan_gre_proto, + :vlan, ].each do |p| attrs[p] = '' if attrs[p].nil? send(p.to_s + '=', attrs[p]) @@ -139,6 +201,21 @@ def ace_set(attrs) config_set('acl', cmd, @set_args) end + def valid_ipv6?(addr) + begin + ret = IPAddr.new(addr.split[0]).ipv6? + rescue + ret = false + end + ret + end + + def check_redirect_repeat(str) + return false unless str.include?('redirect') + nstr = str.sub('redirect', '').strip + nstr.include?('redirect') ? true : false + end + # PROPERTIES # ---------- def seqno @@ -182,7 +259,7 @@ def src_addr return nil if match.nil? || !match.names.include?('src_addr') addr = match[:src_addr] # Normalize addr. Some platforms zero_pad ipv6 addrs. - addr.gsub!(/^0*/, '').gsub!(/:0*/, ':') + addr.gsub!(/^0*/, '').gsub!(/:0*/, ':') if valid_ipv6?(addr) addr end @@ -205,7 +282,7 @@ def dst_addr return nil if match.nil? || !match.names.include?('dst_addr') addr = match[:dst_addr] # Normalize addr. Some platforms zero_pad ipv6 addrs. - addr.gsub!(/^0*/, '').gsub!(/:0*/, ':') + addr.gsub!(/^0*/, '').gsub!(/:0*/, ':') if valid_ipv6?(addr) addr end @@ -261,6 +338,49 @@ def dscp=(dscp) @set_args[:dscp] = Utils.attach_prefix(dscp, :dscp) end + def vlan + Utils.extract_value(ace_get, 'vlan') + end + + def vlan=(vlan) + @set_args[:vlan] = Utils.attach_prefix(vlan, :vlan) + end + + def set_erspan_dscp + ret = Utils.extract_value(ace_get, 'set_erspan_dscp', 'set-erspan-dscp') + return ret if ret + # position of set_erspan_dscp is different in older release so check again + str = config_get('acl', 'ace', @get_args) + sstr = str.split + return sstr[sstr.index('set-erspan-dscp') + 1] if + sstr.include?('set-erspan-dscp') + end + + def set_erspan_dscp=(set_erspan_dscp) + @set_args[:set_erspan_dscp] = Utils.attach_prefix(set_erspan_dscp, + :set_erspan_dscp, + 'set-erspan-dscp') + end + + def set_erspan_gre_proto + ret = Utils.extract_value(ace_get, 'set_erspan_gre_proto', + 'set-erspan-gre-proto') + return ret if ret + # position of set_erspan_gre_proto is different in older release + # so check again + str = config_get('acl', 'ace', @get_args) + sstr = str.split + return sstr[sstr.index('set-erspan-gre-proto') + 1] if + sstr.include?('set-erspan-gre-proto') + end + + def set_erspan_gre_proto=(set_erspan_gre_proto) + @set_args[:set_erspan_gre_proto] = + Utils.attach_prefix(set_erspan_gre_proto, + :set_erspan_gre_proto, + 'set-erspan-gre-proto') + end + def time_range Utils.extract_value(ace_get, 'time_range', 'time-range') end @@ -317,12 +437,27 @@ def redirect=(redirect) @set_args[:redirect] = Utils.attach_prefix(redirect, :redirect) end - def log + def proto_option match = ace_get + return nil if match.nil? || proto != 'icmp' || !remark.nil? + # fragments is nvgen at a different location than all other + # proto_option + if config_get('acl', 'ace', @get_args).include?('fragments') + return 'fragments' + end + # log is special case + return nil if !match.names.include?('proto_option') || + match[:proto_option] == 'log' + match[:proto_option] + end + + def proto_option=(proto_option) + @set_args[:proto_option] = proto_option + end + + def log return nil unless remark.nil? - return false if match.nil? - return false unless match.names.include?('log') - match[:log] == 'log' ? true : false + config_get('acl', 'ace', @get_args).include?('log') ? true : false end def log=(log) diff --git a/lib/cisco_node_utils/banner.rb b/lib/cisco_node_utils/banner.rb new file mode 100644 index 00000000..015e9636 --- /dev/null +++ b/lib/cisco_node_utils/banner.rb @@ -0,0 +1,63 @@ +# Banner provider class +# +# Rick Sherman et al., August 2018 +# +# Copyright (c) 2014-2018 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_relative 'node_util' + +module Cisco + # Banner - node utility class for Banner configuration management + class Banner < NodeUtil + attr_reader :name + + def initialize(name) + fail TypeError unless name.is_a?(String) + fail ArgumentError, + "This provider only accepts an id of 'default'" \ + unless name.eql?('default') + @name = name + end + + def self.banners + hash = {} + hash['default'] = Banner.new('default') + hash + end + + def ==(other) + name == other.name + end + + def motd + config_get('banner', 'motd') + end + + def motd=(val) + if val.nil? && (motd != default_motd) + config_set('banner', 'motd', state: 'no', motd: '') + elsif !val.nil? + config_set('banner', + 'motd', + state: '', + motd: "^#{val.gsub(/\n/, '\\n')}^") + end + end + + def default_motd + config_get_default('banner', 'motd') + end + end # class +end # module diff --git a/lib/cisco_node_utils/bridge_domain.rb b/lib/cisco_node_utils/bridge_domain.rb index 0d02167c..bbf80557 100644 --- a/lib/cisco_node_utils/bridge_domain.rb +++ b/lib/cisco_node_utils/bridge_domain.rb @@ -144,7 +144,7 @@ def default_bd_name # This type property can be defined only for one bd def fabric_control match = config_get('bridge_domain', 'fabric_control', bd: @bd_ids) - match == @bd_ids ? true : false + match.to_s == @bd_ids ? true : false end def fabric_control=(val) diff --git a/lib/cisco_node_utils/cisco_cmn_utils.rb b/lib/cisco_node_utils/cisco_cmn_utils.rb index 14513084..2e03cc04 100644 --- a/lib/cisco_node_utils/cisco_cmn_utils.rb +++ b/lib/cisco_node_utils/cisco_cmn_utils.rb @@ -362,7 +362,7 @@ def self.merge_range(range) end # merge_range def self.add_quotes(value) - return value if image_version?(/7.3.0/) + return value if image_version?(/7.3/) value = "\"#{value}\"" unless value.start_with?('"') && value.end_with?('"') value diff --git a/lib/cisco_node_utils/client/utils.rb b/lib/cisco_node_utils/client/utils.rb index ef7bbbcc..776b59c5 100644 --- a/lib/cisco_node_utils/client/utils.rb +++ b/lib/cisco_node_utils/client/utils.rb @@ -111,16 +111,26 @@ def self.to_regexp(input) return input.map { |item| to_regexp(item) } else # The string might be explicitly formatted as a regexp - if input[0] == '/' && input[-1] == '/' - # '/foo/' => %r{foo} - return Regexp.new(input[1..-2]) - elsif input[0] == '/' && input[-2..-1] == '/i' - # '/foo/i' => %r{foo}i - return Regexp.new(input[1..-3], Regexp::IGNORECASE) - else - # 'foo' => %r{^foo$}i - return Regexp.new("^#{input}$", Regexp::IGNORECASE) + # Dynamically handle modifiers + input.match(%r{(?^\/.*\/)(?[imx]*)?}) do |m| + options = [] + m['options'].each_char do |c| + case c + when 'i' + options << Regexp::IGNORECASE + when 'm' + options << Regexp::MULTILINE + when 'x' + options << Regexp::EXTENDED + end + end + return Regexp.new(m['regex'][1..-2], options.reduce(:|)) end + # otherwise this value is a regular string + # convert to case insensitive regex + # 'foo' => %r{^foo$}i + return Regexp.new("^#{input}$", Regexp::IGNORECASE) + end end @@ -151,7 +161,7 @@ def self.filter_data(data: nil, end return final end - fail "No key \"#{filter}\" in #{data}" if data[filter].nil? + fail "No key \"#{filter}\" in #{data}" unless data.key?(filter) data = data[filter] end end diff --git a/lib/cisco_node_utils/cmd_ref/acl.yaml b/lib/cisco_node_utils/cmd_ref/acl.yaml index 8a5eafcc..246229c6 100644 --- a/lib/cisco_node_utils/cmd_ref/acl.yaml +++ b/lib/cisco_node_utils/cmd_ref/acl.yaml @@ -12,7 +12,7 @@ _template: ace: get_value: '/^ .+$/' - set_value: ' ' + set_value: ' ' ace_destroy: set_value: 'no ' diff --git a/lib/cisco_node_utils/cmd_ref/banner.yaml b/lib/cisco_node_utils/cmd_ref/banner.yaml new file mode 100644 index 00000000..149b0b11 --- /dev/null +++ b/lib/cisco_node_utils/cmd_ref/banner.yaml @@ -0,0 +1,7 @@ +# banner +--- +motd: + default_value: "User Access Verification\n" + get_command: 'show banner motd' + get_value: '/^.*$/m' + set_value: ' banner motd ' diff --git a/lib/cisco_node_utils/cmd_ref/fabricpath.yaml b/lib/cisco_node_utils/cmd_ref/fabricpath.yaml index 6b873726..a14321ed 100644 --- a/lib/cisco_node_utils/cmd_ref/fabricpath.yaml +++ b/lib/cisco_node_utils/cmd_ref/fabricpath.yaml @@ -82,7 +82,7 @@ loadbalance_multicast_has_vlan: _exclude: [N5k, N6k] kind: boolean auto_default: false - get_command: "show fabricpath load-balance | begin Ftag" + get_command: "show fabricpath load-balance | begin ignore-case Ftag" get_value: '/^Use VLAN: TRUE/' default_value: true @@ -93,7 +93,7 @@ loadbalance_multicast_reset: loadbalance_multicast_rotate: _exclude: [N5k, N6k] kind: int - get_command: "show fabricpath load-balance | begin Ftag" + get_command: "show fabricpath load-balance | begin ignore-case Ftag" get_value: '/^Rotate amount: (\d+)/' # default_value: n/a. The default rotate amount is randomized to avoid polarization. diff --git a/lib/cisco_node_utils/cmd_ref/hostname.yaml b/lib/cisco_node_utils/cmd_ref/hostname.yaml new file mode 100644 index 00000000..c3e60cc0 --- /dev/null +++ b/lib/cisco_node_utils/cmd_ref/hostname.yaml @@ -0,0 +1,8 @@ +# hostname +--- + +name: + get_command: "show running-config all | include 'hostname '" + get_value: '/^hostname (\S+)$/' + set_value: " hostname " + default_value: "" diff --git a/lib/cisco_node_utils/cmd_ref/interface.yaml b/lib/cisco_node_utils/cmd_ref/interface.yaml index 3d7c9698..49b9b04d 100644 --- a/lib/cisco_node_utils/cmd_ref/interface.yaml +++ b/lib/cisco_node_utils/cmd_ref/interface.yaml @@ -127,7 +127,7 @@ hsrp_mac_refresh: kind: int get_value: '/^hsrp mac-refresh (\d+)$/' set_value: ' hsrp mac-refresh ' - default_value: false + default_value: ~ hsrp_use_bia: _exclude: [ios_xr, N5k, N6k] @@ -137,7 +137,7 @@ hsrp_use_bia: # optional match to get the whole config get_value: '/^hsrp use-bia(?:\s+\S+\s+\S+)?$/' set_value: ' hsrp use-bia' - default_value: false + default_value: ~ hsrp_version: _exclude: [ios_xr, N5k, N6k] @@ -211,7 +211,7 @@ ipv4_dhcp_relay_src_intf: _exclude: [ios_xr] get_value: '/^ip dhcp relay source-interface (.*)$/' set_value: " ip dhcp relay source-interface " - default_value: false + default_value: ~ ipv4_dhcp_relay_subnet_broadcast: _exclude: [ios_xr] @@ -258,7 +258,7 @@ ipv4_proxy_arp: ipv4_redirects_loopback: kind: boolean nexus: - default_only: false + default_only: ~ ios_xr: get_value: '/^((?:no )?ipv4 redirects)$/' set_value: " ipv4 redirects" @@ -304,7 +304,14 @@ ipv6_dhcp_relay_src_intf: _exclude: [ios_xr] get_value: '/^ipv6 dhcp relay source-interface (.*)$/' set_value: " ipv6 dhcp relay source-interface " - default_value: false + default_value: ~ + +ipv6_redirects: + _exclude: [ios_xr] + kind: boolean + get_value: '/^((?:no )?ipv6 redirects)$/' + set_value: " ipv6 redirects" + default_value: true load_interval_counter_1_delay: _exclude: [ios_xr] @@ -329,7 +336,7 @@ load_interval_counter_3_delay: kind: int get_value: '/^load-interval counter 3 (\d+)$/' set_value: " load-interval counter 3 " - default_value: false + default_value: ~ mtu_loopback: kind: boolean @@ -377,6 +384,7 @@ purge_config: set_value: "default interface " get_command: "show running interface" get_value: '/(.*)/' + default_value: ~ pvlan_any: multiple: @@ -462,13 +470,13 @@ stp_bpdufilter: kind: string get_value: '/^spanning-tree bpdufilter (.*)$/' set_value: " spanning-tree bpdufilter " - default_value: false + default_value: ~ stp_bpduguard: kind: string get_value: '/^spanning-tree bpduguard (.*)$/' set_value: " spanning-tree bpduguard " - default_value: false + default_value: ~ stp_cost: kind: string @@ -480,7 +488,7 @@ stp_guard: kind: string get_value: '/^spanning-tree guard (.*)$/' set_value: " spanning-tree guard " - default_value: false + default_value: ~ stp_link_type: kind: string @@ -517,7 +525,7 @@ stp_port_type: get_command: "show running interface" get_value: '/^spanning-tree port type (.*)$/' set_value: " spanning-tree port type " - default_value: false + default_value: ~ stp_vlan_cost: multiple: @@ -625,7 +633,7 @@ switchport_pvlan_trunk_allowed_vlan: multiple: true get_value: '/^switchport private-vlan trunk allowed vlan (?:add )?(\S+)$/' set_value: "switchport private-vlan trunk allowed vlan " - default_value: 'none' + default_value: none switchport_pvlan_trunk_association: _exclude: [ios_xr, N3k, N3k-F, N9k-F] @@ -728,7 +736,7 @@ vpc_id: kind: int get_value: '/^vpc (\d+)$/' set_value: ' vpc ' - default_value: false + default_value: ~ vpc_peer_link: kind: boolean diff --git a/lib/cisco_node_utils/cmd_ref/interface_hsrp_group.yaml b/lib/cisco_node_utils/cmd_ref/interface_hsrp_group.yaml index b68e77a8..d308f1f1 100644 --- a/lib/cisco_node_utils/cmd_ref/interface_hsrp_group.yaml +++ b/lib/cisco_node_utils/cmd_ref/interface_hsrp_group.yaml @@ -23,14 +23,14 @@ authentication_enc_type: default_value: '0' authentication_key_type: - default_value: 'key-chain' + default_value: ~ authentication_string: - default_value: '' + default_value: ~ authentication_timeout: kind: int - default_value: 0 + default_value: ~ group_name: get_value: '/^name (\S+)/' diff --git a/lib/cisco_node_utils/cmd_ref/ip_multicast.yaml b/lib/cisco_node_utils/cmd_ref/ip_multicast.yaml index bfd0e330..191432f4 100644 --- a/lib/cisco_node_utils/cmd_ref/ip_multicast.yaml +++ b/lib/cisco_node_utils/cmd_ref/ip_multicast.yaml @@ -7,12 +7,16 @@ _template: overlay_distributed_dr: context: ~ + kind: boolean get_value: '^ip multicast overlay-distributed-dr$' set_value: " ip multicast overlay-distributed-dr" default_value: false overlay_spt_only: context: ~ - get_value: '^ip multicast overlay-spt-only$' + kind: boolean + auto_default: false + get_command: "show fabric multicast globals" + get_value: '^Overlay spt-only:\s+TRUE$' set_value: " ip multicast overlay-spt-only" - default_value: false + default_value: true diff --git a/lib/cisco_node_utils/cmd_ref/snmp_server.yaml b/lib/cisco_node_utils/cmd_ref/snmp_server.yaml index f7ed92e0..69e64527 100644 --- a/lib/cisco_node_utils/cmd_ref/snmp_server.yaml +++ b/lib/cisco_node_utils/cmd_ref/snmp_server.yaml @@ -62,7 +62,10 @@ packet_size: # the n3|9k behavior when the issue is resolved. default_value: 0 N6k: *n5k_default_packet_size - N7k: *n5k_default_packet_size + # On recent versions, packet size is fixed, + # so this change will take care of the fix but + # it could still fail in the older versions. + N7k: *n3k_default_packet_size # On recent versions, packet size is fixed, # so this change will take care of the fix but # it could still fail in the older versions. diff --git a/lib/cisco_node_utils/cmd_ref/syslog_facility.yaml b/lib/cisco_node_utils/cmd_ref/syslog_facility.yaml new file mode 100644 index 00000000..0dbed45d --- /dev/null +++ b/lib/cisco_node_utils/cmd_ref/syslog_facility.yaml @@ -0,0 +1,10 @@ +# syslog_facility +--- +_exclude: [ios_xr] + +facility: + multiple: true + get_command: "show running-config | include '^logging level'" + # Returns , + get_value: '/^(?:logging level)\s+(.*)\s+([0-7])/' + set_value: ' logging level ' diff --git a/lib/cisco_node_utils/cmd_ref/syslog_server.yaml b/lib/cisco_node_utils/cmd_ref/syslog_server.yaml index 256b176a..97c20aee 100644 --- a/lib/cisco_node_utils/cmd_ref/syslog_server.yaml +++ b/lib/cisco_node_utils/cmd_ref/syslog_server.yaml @@ -15,9 +15,9 @@ server: multiple: true nexus: get_command: "show running-config all | include '^logging server'" - # Returns , , , - get_value: '/^(?:logging server )([^\s]+)(?: (\d+))?(?: port (\d+))?(?: use-vrf (\S+))?/' - set_value: ' logging server ' + # Returns , , , , + get_value: '/^(?:logging server )([^\s]+)(?: (\d+))?(?: port (\d+))?(?: use-vrf (\S+))?(?: facility (\S+))?/' + set_value: ' logging server ' ios_xr: get_command: "show running-config logging" get_value: '/^logging (\S+).*/' diff --git a/lib/cisco_node_utils/cmd_ref/syslog_settings.yaml b/lib/cisco_node_utils/cmd_ref/syslog_settings.yaml index c17966e7..4deaed86 100644 --- a/lib/cisco_node_utils/cmd_ref/syslog_settings.yaml +++ b/lib/cisco_node_utils/cmd_ref/syslog_settings.yaml @@ -9,6 +9,22 @@ console: get_value: '/^(no)?\s*logging console\s*(\d)?/' set_value: ' logging console ' +logfile_name: + get_command: "show running-config all | include 'logging'" + # Returns and + get_value: '/^(no)?\s*logging logfile\s(\S*)\s*?/' + set_value: ' logging logfile ' + +logfile_severity_level: + get_command: "show running-config all | include 'logging'" + # Returns and + get_value: '/^(no)?\s*logging logfile\s\S*\s*(\d)?/' + +logfile_size: + get_command: "show running-config all | include 'logging'" + # Returns and + get_value: '/^(no)?\s*logging logfile.*size\s(\d*)/' + monitor: default_value: 5 get_command: "show running-config all | include 'logging'" diff --git a/lib/cisco_node_utils/cmd_ref/tacacs_server_host.yaml b/lib/cisco_node_utils/cmd_ref/tacacs_server_host.yaml index 53cc155a..6adcf6c2 100644 --- a/lib/cisco_node_utils/cmd_ref/tacacs_server_host.yaml +++ b/lib/cisco_node_utils/cmd_ref/tacacs_server_host.yaml @@ -48,7 +48,7 @@ hosts: port: default_value: 49 kind: int - get_value: '/^tacacs-server host .* port (\d+).*$/' + get_value: '/^tacacs-server host \s.*port (\d+).*$/' nexus: set_value: "tacacs-server host port " @@ -56,7 +56,7 @@ timeout: default_value: 0 kind: int nexus: - get_value: '/^tacacs-server host .*timeout (\d+)/' + get_value: '/^tacacs-server host \s.*timeout (\d+)/' set_value: " tacacs-server host timeout " ios_xr: context: ["tacacs-server host port "] diff --git a/lib/cisco_node_utils/environment.rb b/lib/cisco_node_utils/environment.rb index e3b84df3..7d0ad910 100644 --- a/lib/cisco_node_utils/environment.rb +++ b/lib/cisco_node_utils/environment.rb @@ -1,6 +1,7 @@ +# August 2018 # March 2016, Glenn F. Matthews # -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2016-2018 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 +91,12 @@ def self.data_from_file(path) {} end + def self.add_env(env_name, env_hash) + fail ArgumentError, 'empty environment name' if env_name.empty? + fail TypeError, 'invalid environment hash' unless env_hash.is_a?(Hash) + @environments[env_name] = env_hash + end + def self.strings_to_symbols(hash) Hash[hash.map { |k, v| [k.to_sym, v] }] end diff --git a/lib/cisco_node_utils/hostname.rb b/lib/cisco_node_utils/hostname.rb new file mode 100644 index 00000000..a4242d43 --- /dev/null +++ b/lib/cisco_node_utils/hostname.rb @@ -0,0 +1,62 @@ +# Hostname provider class +# +# September 2018 +# +# Copyright (c) 2014-2018 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_relative 'node_util' + +module Cisco + # Hostname- node utility class for hostname configuration + class HostName < NodeUtil + attr_reader :name + + def initialize(name, instantiate=true) + @name = name + create if instantiate + end + + def self.hostname + hash = {} + hostname = config_get('hostname', 'name') + hash[hostname] = HostName.new(hostname, false) + hash + end + + def hostname=(host) + if host + config_set( + 'hostname', 'name', + state: '', name: host) + else + config_set( + 'hostname', 'name', + state: 'no', name: '') + end + end + + def ==(other) + name == other.name + end + + def create + config_set('hostname', 'name', state: '', name: @name) + end + + def destroy + config_set('hostname', 'name', state: 'no', name: @name) + end + end # class +end # module diff --git a/lib/cisco_node_utils/interface.rb b/lib/cisco_node_utils/interface.rb index a25d8e1a..e2dd3d95 100644 --- a/lib/cisco_node_utils/interface.rb +++ b/lib/cisco_node_utils/interface.rb @@ -1,6 +1,6 @@ # November 2015, Chris Van Heuveln # -# Copyright (c) 2015-2017 Cisco and/or its affiliates. +# Copyright (c) 2015-2018 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. @@ -192,6 +192,7 @@ def pvlan_enable # are left untouched. def access_vlan + return nil if switchport_mode == :disabled config_get('interface', 'access_vlan', name: @name) end @@ -275,6 +276,7 @@ def fabric_forwarding_anycast_gateway end def fabric_forwarding_anycast_gateway=(state) + return if fabric_forwarding_anycast_gateway == state no_cmd = (state ? '' : 'no') config_set('interface', 'fabric_forwarding_anycast_gateway', @@ -325,6 +327,7 @@ def default_hsrp_delay end def hsrp_delay_minimum + return nil if switchport_mode != :disabled || @name[/loop/i] minimum, _reload = hsrp_delay minimum.nil? ? default_hsrp_delay_minimum : minimum end @@ -342,6 +345,7 @@ def default_hsrp_delay_minimum end def hsrp_delay_reload + return nil if switchport_mode != :disabled || @name[/loop/i] _minimum, reload = hsrp_delay reload.nil? ? default_hsrp_delay_reload : reload end @@ -413,6 +417,7 @@ def default_hsrp_use_bia end def hsrp_version + return nil if switchport_mode != :disabled || @name[/loop/i] config_get('interface', 'hsrp_version', name: @name) end @@ -604,6 +609,7 @@ def default_ipv4_dhcp_relay_addr end def ipv4_dhcp_relay_info_trust + return nil if @name[/loop/i] || switchport_mode != :disabled config_get('interface', 'ipv4_dhcp_relay_info_trust', name: @name) end @@ -654,6 +660,7 @@ def default_ipv4_dhcp_relay_src_intf end def ipv4_dhcp_relay_subnet_broadcast + return nil if @name[/loop/i] || switchport_mode != :disabled config_get('interface', 'ipv4_dhcp_relay_subnet_broadcast', name: @name) end @@ -669,6 +676,7 @@ def default_ipv4_dhcp_relay_subnet_broadcast end def ipv4_dhcp_smart_relay + return nil if @name[/loop/i] || switchport_mode != :disabled config_get('interface', 'ipv4_dhcp_smart_relay', name: @name) end @@ -698,6 +706,7 @@ def default_ipv4_forwarding end def ipv4_pim_sparse_mode + return nil unless switchport_mode == :disabled config_get('interface', 'ipv4_pim_sparse_mode', name: @name) end @@ -713,6 +722,7 @@ def default_ipv4_pim_sparse_mode end def ipv4_proxy_arp + return nil if @name[/loop/i] || switchport_mode != :disabled config_get('interface', 'ipv4_proxy_arp', name: @name) end @@ -736,6 +746,7 @@ def ipv4_redirects_lookup_string end def ipv4_redirects + return nil unless switchport_mode == :disabled config_get('interface', ipv4_redirects_lookup_string, name: @name) end @@ -834,6 +845,22 @@ def default_ipv6_dhcp_relay_src_intf config_get_default('interface', 'ipv6_dhcp_relay_src_intf') end + def ipv6_redirects + return nil if @name[/loop/i] || switchport_mode != :disabled + config_get('interface', 'ipv6_redirects', name: @name) + end + + def ipv6_redirects=(redirects) + check_switchport(:disabled) + no_cmd = (redirects ? '' : 'no') + config_set('interface', 'ipv6_redirects', + name: @name, state: no_cmd) + end + + def default_ipv6_redirects + config_get_default('interface', 'ipv6_redirects') + end + def feature_lacp? config_get('interface', 'feature_lacp') end @@ -913,6 +940,7 @@ def mtu end def mtu=(val) + return if mtu == val check_switchport(:disabled) config_set('interface', mtu_lookup_string, name: @name, state: '', mtu: val) @@ -923,6 +951,7 @@ def default_mtu end def speed + return nil if @name[/loop|vlan/i] config_get('interface', 'speed', name: @name) end @@ -935,6 +964,7 @@ def default_speed end def duplex + return nil if @name[/loop|vlan/i] config_get('interface', 'duplex', name: @name) end @@ -958,6 +988,7 @@ def negotiate_auto_lookup_string end def negotiate_auto + return nil if @name[/loop|vlan/] config_get('interface', negotiate_auto_lookup_string, name: @name) end @@ -1032,10 +1063,12 @@ def default_pim_bfd end def storm_control_broadcast + return nil if @name[/loop|vlan/i] config_get('interface', 'storm_control_broadcast', name: @name) end def storm_control_broadcast=(val) + return if val == storm_control_broadcast state = val == default_storm_control_broadcast ? 'no' : '' level = val == default_storm_control_broadcast ? '' : val config_set('interface', 'storm_control_broadcast', @@ -1047,10 +1080,12 @@ def default_storm_control_broadcast end def storm_control_multicast + return nil if @name[/loop|vlan/i] config_get('interface', 'storm_control_multicast', name: @name) end def storm_control_multicast=(val) + return if val == storm_control_broadcast state = val == default_storm_control_multicast ? 'no' : '' level = val == default_storm_control_multicast ? '' : val config_set('interface', 'storm_control_multicast', @@ -1062,10 +1097,12 @@ def default_storm_control_multicast end def storm_control_unicast + return nil if @name[/loop|vlan/i] config_get('interface', 'storm_control_unicast', name: @name) end def storm_control_unicast=(val) + return if val == storm_control_broadcast state = val == default_storm_control_unicast ? 'no' : '' level = val == default_storm_control_unicast ? '' : val config_set('interface', 'storm_control_unicast', @@ -1116,6 +1153,7 @@ def default_stp_bpduguard end def stp_cost + return nil if switchport_mode == :disabled cost = config_get('interface', 'stp_cost', name: @name) cost == 'auto' ? cost : cost.to_i end @@ -1150,6 +1188,7 @@ def default_stp_guard end def stp_link_type + return nil if switchport_mode == :disabled config_get('interface', 'stp_link_type', name: @name) end @@ -1163,6 +1202,7 @@ def default_stp_link_type end def stp_port_priority + return nil if switchport_mode == :disabled config_get('interface', 'stp_port_priority', name: @name) end @@ -1294,6 +1334,7 @@ def switchport_enable(val=true) # switchport_autostate_exclude is exclusive to switchport interfaces def switchport_autostate_exclude + return nil if switchport_mode == :disabled config_get('interface', 'switchport_autostate_exclude', name: @name) end @@ -1384,6 +1425,7 @@ def default_switchport_mode end def switchport_trunk_allowed_vlan + return nil if switchport_mode == :disabled vlans = config_get('interface', 'switchport_trunk_allowed_vlan', name: @name) vlans = vlans.join(',') if vlans.is_a?(Array) @@ -1406,6 +1448,7 @@ def default_switchport_trunk_allowed_vlan end def switchport_trunk_native_vlan + return nil if switchport_mode == :disabled config_get('interface', 'switchport_trunk_native_vlan', name: @name) end @@ -1442,6 +1485,7 @@ def cli_error_check(result) # -------------------------- # switchport mode private-vlan host def switchport_pvlan_host + return nil if switchport_mode == :disabled config_get('interface', 'switchport_pvlan_host', name: @name) end @@ -1458,6 +1502,7 @@ def default_switchport_pvlan_host # -------------------------- # switchport mode private-vlan promiscuous def switchport_pvlan_promiscuous + return nil if switchport_mode == :disabled config_get('interface', 'switchport_pvlan_promiscuous', name: @name) end @@ -1650,6 +1695,7 @@ def default_switchport_pvlan_trunk_association # -------------------------- # switchport mode private-vlan trunk promiscuous def switchport_pvlan_trunk_promiscuous + return nil if switchport_mode == :disabled config_get('interface', 'switchport_pvlan_trunk_promiscuous', name: @name) end @@ -1666,6 +1712,7 @@ def default_switchport_pvlan_trunk_promiscuous # -------------------------- # switchport mode private-vlan trunk secondary def switchport_pvlan_trunk_secondary + return nil if switchport_mode == :disabled config_get('interface', 'switchport_pvlan_trunk_secondary', name: @name) end @@ -1684,6 +1731,7 @@ def default_switchport_pvlan_trunk_secondary # Note that range is handled as a string because the entire range is # replaced instead of individually adding or removing vlans from the range. def switchport_pvlan_trunk_allowed_vlan + return nil if switchport_mode == :disabled vlans = config_get('interface', 'switchport_pvlan_trunk_allowed_vlan', name: @name) vlans = vlans.join(',') if vlans.is_a?(Array) @@ -1708,6 +1756,7 @@ def default_switchport_pvlan_trunk_allowed_vlan # -------------------------- # switchport trunk native vlan def switchport_pvlan_trunk_native_vlan + return nil if switchport_mode == :disabled config_get('interface', 'switchport_pvlan_trunk_native_vlan', name: @name) end @@ -1971,10 +2020,12 @@ def default_vpc_id end def vpc_peer_link + return nil unless @name[/port-channel/i] && switchport_mode != :disabled config_get('interface', 'vpc_peer_link', name: @name) end def vpc_peer_link=(state) + return if vpc_peerlink == state no_cmd = (state ? '' : 'no') config_set('interface', 'vpc_peer_link', name: @name, state: no_cmd) end @@ -2037,8 +2088,14 @@ def purge_config=(val) end def purge_config + # This getter is only supported on ethernet interfaces + return nil unless @name[/ethernet/] state = config_get('interface', 'purge_config', name: @name) - state.nil? ? true : false + state.nil? ? true : default_purge_config + end + + def default_purge_config + config_get_default('interface', 'purge_config') end end # Class end # Module diff --git a/lib/cisco_node_utils/interface_DEPRECATED.rb b/lib/cisco_node_utils/interface_DEPRECATED.rb index 38e46b38..652d9961 100644 --- a/lib/cisco_node_utils/interface_DEPRECATED.rb +++ b/lib/cisco_node_utils/interface_DEPRECATED.rb @@ -65,6 +65,7 @@ def switchport_enable_and_mode_private_vlan_host(mode_set) end def switchport_mode_private_vlan_host + return nil if switchport_mode == :disabled mode = config_get('DEPRECATED', 'switchport_mode_private_vlan_host', name: @name) @@ -299,6 +300,7 @@ def default_switchport_mode_private_vlan_host_promisc end def switchport_mode_private_vlan_trunk_promiscuous + return nil if switchport_mode == :disabled config_get('DEPRECATED', 'switchport_mode_private_vlan_trunk_promiscuous', name: @name) @@ -331,6 +333,7 @@ def default_switchport_mode_private_vlan_trunk_promiscuous end def switchport_mode_private_vlan_trunk_secondary + return nil if switchport_mode == :disabled config_get('DEPRECATED', 'switchport_mode_private_vlan_trunk_secondary', name: @name) @@ -361,6 +364,7 @@ def default_switchport_mode_private_vlan_trunk_secondary end def switchport_private_vlan_trunk_allowed_vlan + return nil if switchport_mode == :disabled result = config_get('DEPRECATED', 'switchport_private_vlan_trunk_allowed_vlan', name: @name) @@ -400,6 +404,7 @@ def default_switchport_private_vlan_trunk_allowed_vlan end def switchport_private_vlan_trunk_native_vlan + return nil if switchport_mode == :disabled config_get('DEPRECATED', 'switchport_private_vlan_trunk_native_vlan', name: @name) diff --git a/lib/cisco_node_utils/interface_hsrp_group.rb b/lib/cisco_node_utils/interface_hsrp_group.rb index 1b8c5421..f2a33b30 100644 --- a/lib/cisco_node_utils/interface_hsrp_group.rb +++ b/lib/cisco_node_utils/interface_hsrp_group.rb @@ -1,7 +1,7 @@ # # October 2016, Sai Chintalapudi # -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2016-2018 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. @@ -96,6 +96,12 @@ def destroy # authentication md5 key-string ABCXYZ timeout 22 # authentication md5 key-string 7 12345678901234567890 timeout 22 # authentication md5 key-string 7 123456789 compatibility timeout 22 + # if passwd + # req auth_enc_type + # if encrypted + # req authentication_key_type, authentication_enc_type + # if key-string + # optional compat and timeout def authentication hash = {} hash[:auth_type] = default_authentication_auth_type @@ -203,8 +209,13 @@ def authentication_timeout end def authentication_timeout=(val) - @set_args[:tval] = val - @set_args[:timeout] = 'timeout' + if val.nil? + @set_args[:tval] = '' + @set_args[:timeout] = '' + else + @set_args[:tval] = val + @set_args[:timeout] = 'timeout' + end end def default_authentication_timeout @@ -227,14 +238,14 @@ def authentication_set(attrs) :authentication_compatibility, :authentication_timeout, ].each do |p| - send(p.to_s + '=', attrs[p]) unless attrs[p].nil? + send(p.to_s + '=', attrs[p]) end - return if @set_args[:passwd] == default_authentication_string + return if @set_args[:passwd].nil? || @set_args[:passwd].empty? @set_args[:state] = '' if @set_args[:authtype] == 'text' @set_args[:keytype] = @set_args[:enctype] = '' @set_args[:compatible] = @set_args[:timeout] = @set_args[:tval] = '' - elsif @set_args[:keytype] == default_authentication_key_type + elsif @set_args[:keytype] == 'key-chain' @set_args[:enctype] = @set_args[:compatible] = '' @set_args[:timeout] = @set_args[:tval] = '' end diff --git a/lib/cisco_node_utils/ip_multicast.rb b/lib/cisco_node_utils/ip_multicast.rb index 56e5f8db..d4e2215b 100644 --- a/lib/cisco_node_utils/ip_multicast.rb +++ b/lib/cisco_node_utils/ip_multicast.rb @@ -64,23 +64,27 @@ def default_overlay_distributed_dr end def overlay_spt_only - config_get('ip_multicast', 'overlay_spt_only') + result = config_get('ip_multicast', 'overlay_spt_only') + result.nil? ? false : result end def overlay_spt_only=(bool) fail TypeError unless [true, false].include?(bool) @set_args[:state] = bool ? '' : 'no' - if @set_args[:state] == 'no' - unless overlay_spt_only == default_overlay_spt_only - config_set('ip_multicast', 'overlay_spt_only', @set_args) - end - else - config_set('ip_multicast', 'overlay_spt_only', @set_args) - end + config_set('ip_multicast', 'overlay_spt_only', @set_args) end def default_overlay_spt_only - config_get_default('ip_multicast', 'overlay_spt_only') + val = config_get_default('ip_multicast', 'overlay_spt_only') + # The default value for this property is different for older + # Nexus software verions. + # + # Versions: 7.0(3)I7(1), 7.0(3)I7(2), 7.0(3)I7(3) + # Default State: false + # + # Versions: 7.0(3)I7(4) and later + # Default State: true + node.os_version[/7\.0\(3\)I7\([1-3]\)/] ? !val : val end end end diff --git a/lib/cisco_node_utils/node.rb b/lib/cisco_node_utils/node.rb index 5e200775..bde05460 100644 --- a/lib/cisco_node_utils/node.rb +++ b/lib/cisco_node_utils/node.rb @@ -122,7 +122,10 @@ def drill_down_structured(value, ref) value = value.is_a?(Hash) ? [value] : value data = nil value.each do |row| - data = row[data_key] if row[row_key].to_s[/#{row_index}/] + if row[row_key].to_s[/#{row_index}/] + data = row[data_key] + data = data.nil? ? '' : data + end end return value if data.nil? if regexp_filter diff --git a/lib/cisco_node_utils/syslog_facility.rb b/lib/cisco_node_utils/syslog_facility.rb new file mode 100644 index 00000000..ff975a5e --- /dev/null +++ b/lib/cisco_node_utils/syslog_facility.rb @@ -0,0 +1,64 @@ +# Syslog facility provider class +# +# Rick Sherman et al., August 2018 +# +# Copyright (c) 2014-2018 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_relative 'node_util' + +module Cisco + # SyslogFacility - node utility class for Syslog facility severity management + class SyslogFacility < NodeUtil + attr_reader :facility, :level + + def initialize(opts, instantiate=true) + @facility = opts['facility'] + @level = opts['level'] + + create if instantiate + end + + def self.facilities + keys = %w(facility level) + hash = {} + facility_key_list = config_get('syslog_facility', 'facility') + return hash if facility_key_list.nil? + + facility_key_list.each do |id| + hash[id[0]] = SyslogFacility.new(Hash[keys.zip(id)], false) + end + + hash + end + + def ==(other) + facility == other.facility && level == other.level + end + + def create + config_set('syslog_facility', 'facility', state: '', facility: @facility, + level: @level) + end + + def destroy + config_set('syslog_facility', 'facility', state: 'no', + facility: @facility, level: @level) + end + + def level + @level.to_i + end + end # class +end # module diff --git a/lib/cisco_node_utils/syslog_server.rb b/lib/cisco_node_utils/syslog_server.rb index 7f2e990f..385c7785 100644 --- a/lib/cisco_node_utils/syslog_server.rb +++ b/lib/cisco_node_utils/syslog_server.rb @@ -1,8 +1,9 @@ # Syslog Server provider class # +# June 2018 # Jonathan Tripathy et al., September 2015 # -# Copyright (c) 2014-2017 Cisco and/or its affiliates. +# Copyright (c) 2014-2018 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. @@ -22,7 +23,7 @@ module Cisco # SyslogServer - node utility class for syslog server configuration management class SyslogServer < NodeUtil - attr_reader :name, :level, :port, :vrf, :severity_level + attr_reader :name, :level, :port, :vrf, :severity_level, :facility LEVEL_TO_NUM = { 'emergencies' => 0, 'alerts' => 1, @@ -40,6 +41,7 @@ def initialize(opts, instantiate=true) @port = opts['port'] @vrf = opts['vrf'] @severity_level = opts['severity_level'] || opts['level'] + @facility = opts['facility'] hostname_regex = /^(?=.{1,255}$)[0-9A-Za-z] (?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])? @@ -56,7 +58,7 @@ def initialize(opts, instantiate=true) end def self.syslogservers - keys = %w(name level port vrf severity_level) + keys = %w(name level port vrf facility severity_level) hash = {} syslogservers_list = config_get('syslog_server', 'server') return hash if syslogservers_list.nil? @@ -65,6 +67,7 @@ def self.syslogservers value_hash = Hash[keys.zip(id)] value_hash['severity_level'] = value_hash['level'] value_hash['vrf'] = 'default' if value_hash['vrf'].nil? + value_hash['facility'] = 'local7' if value_hash['facility'].nil? hash[id[0]] = SyslogServer.new(value_hash, false) end @@ -93,11 +96,12 @@ def create else config_set('syslog_server', 'server', - state: '', - ip: @name, - level: @level ? "#{@level}" : '', - port: @port ? "port #{@port}" : '', - vrf: @vrf ? "use-vrf #{@vrf}" : '', + state: '', + ip: @name, + level: @level ? "#{@level}" : '', + port: @port ? "port #{@port}" : '', + vrf: @vrf ? "use-vrf #{@vrf}" : '', + facility: @facility ? "facility #{@facility}" : '', ) end end @@ -129,11 +133,12 @@ def destroy(duplicate_vrfs=[]) else config_set('syslog_server', 'server', - state: 'no', - ip: @name, - level: '', - port: '', - vrf: '', + state: 'no', + ip: @name, + level: '', + port: '', + vrf: '', + facility: '', ) end end diff --git a/lib/cisco_node_utils/syslog_settings.rb b/lib/cisco_node_utils/syslog_settings.rb index d91c39a0..13693dbb 100644 --- a/lib/cisco_node_utils/syslog_settings.rb +++ b/lib/cisco_node_utils/syslog_settings.rb @@ -1,8 +1,9 @@ # Syslog Settings provider class # +# August 2018 # Jonathan Tripathy et al., September 2015 # -# Copyright (c) 2014-2017 Cisco and/or its affiliates. +# Copyright (c) 2014-2018 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. @@ -126,6 +127,47 @@ def timestamp=(val) units: val) end + def logfile_severity_level + logfile_severity_level = + config_get('syslog_settings', 'logfile_severity_level') + if logfile_severity_level.is_a?(Array) + if logfile_severity_level[0] == 'no' + logfile_severity_level = nil + else + logfile_severity_level = logfile_severity_level[1] + end + end + logfile_severity_level + end + + def logfile_name=(logname, severity, size) + if logname + config_set( + 'syslog_settings', 'logfile_name', + state: '', logname: logname, severity: severity, size: size) + else + config_set( + 'syslog_settings', 'logfile_name', + state: 'no', logname: logname, severity: severity, size: size) + end + end + + def logfile_name + logfile_name = config_get('syslog_settings', 'logfile_name') + if logfile_name.is_a?(Array) + logfile_name = (logfile_name[0] == 'no') ? 'unset' : logfile_name[1] + end + logfile_name + end + + def logfile_size + logfile_size = config_get('syslog_settings', 'logfile_size') + if logfile_size.is_a?(Array) + logfile_size = (logfile_size[0] == 'no') ? nil : logfile_size[1] + end + logfile_size + end + alias_method :time_stamp_units, :timestamp alias_method :time_stamp_units=, :timestamp= end # class diff --git a/lib/cisco_node_utils/version.rb b/lib/cisco_node_utils/version.rb index 5d761ab4..60a7c43c 100644 --- a/lib/cisco_node_utils/version.rb +++ b/lib/cisco_node_utils/version.rb @@ -14,7 +14,7 @@ # Container module for version number only. module CiscoNodeUtils - VERSION = '1.9.0' + VERSION = '1.10.0' gem_version = Gem::Version.new(Gem::VERSION) min_gem_version = Gem::Version.new('2.1.0') fail 'Required rubygems version >= 2.1.0' if gem_version < min_gem_version diff --git a/lib/cisco_node_utils/vlan.rb b/lib/cisco_node_utils/vlan.rb index e142d8cd..d00d73fc 100644 --- a/lib/cisco_node_utils/vlan.rb +++ b/lib/cisco_node_utils/vlan.rb @@ -47,7 +47,7 @@ def self.vlans return hash if vlan_list.nil? vlan_list.each do |id| - hash[id] = Vlan.new(id, false) + hash[id.to_s] = Vlan.new(id, false) end hash end diff --git a/lib/cisco_node_utils/vxlan_vtep_vni.rb b/lib/cisco_node_utils/vxlan_vtep_vni.rb index ace6784b..913c7846 100644 --- a/lib/cisco_node_utils/vxlan_vtep_vni.rb +++ b/lib/cisco_node_utils/vxlan_vtep_vni.rb @@ -112,20 +112,33 @@ def ingress_replication config_get('vxlan_vtep_vni', 'ingress_replication', @get_args) end + def set_host_reachability(vtep_name, protocol) + # This is a helper method for the ingress_replication setter. + # In later versions of Nexus, a check was added to make sure + # the host_reachability setting is correct for the desired + # ingress_replication setting. + # + case protocol + when 'bgp' + host_reachability = 'evpn' + when 'static' + host_reachability = 'flood' + else + fail "Protocol #{protocol} currently not supported" + end + VxlanVtep.new(vtep_name).host_reachability = host_reachability + end + def remove_add_ingress_replication(protocol) # Note: ingress-replication is not supported on all platforms. # Use to_s.empty check to also handle nil check. - if ingress_replication.to_s.empty? - set_args_keys(state: '', protocol: protocol) - config_set('vxlan_vtep_vni', 'ingress_replication', @set_args) - else - # Sadly, the only way to change between protocols is to - # first remove the existing protocol. + unless ingress_replication.to_s.empty? set_args_keys(state: 'no', protocol: ingress_replication) config_set('vxlan_vtep_vni', 'ingress_replication', @set_args) - set_args_keys(state: '', protocol: protocol) - config_set('vxlan_vtep_vni', 'ingress_replication', @set_args) end + set_host_reachability(@set_args[:name], protocol) + set_args_keys(state: '', protocol: protocol) + config_set('vxlan_vtep_vni', 'ingress_replication', @set_args) end def ingress_replication=(protocol) diff --git a/spec/environment_spec.rb b/spec/environment_spec.rb index 8ed8fd30..1b0ffc0f 100644 --- a/spec/environment_spec.rb +++ b/spec/environment_spec.rb @@ -70,6 +70,37 @@ class << Cisco::Environment end end + describe '.add_env' do + it 'rejects empty environment name' do + expect { described_class.add_env('', {}) }.to \ + raise_error(ArgumentError, 'empty environment name') + end + + it 'rejects incorrectly typed environment hash' do + expect { described_class.add_env('default', 'hash') }.to \ + raise_error(TypeError, 'invalid environment hash') + end + + context 'environment can be loaded by funcion' do + expected = { + host: '192.168.1.1', + port: nil, + username: 'admin', + password: 'admin', + cookie: nil, + } + it 'can be loaded explicitly by name' do + described_class.add_env('test', expected) + expect(Cisco::Environment.environment('test')).to eq(expected) + end + + it 'can be default' do + described_class.add_env('default', expected) + expect(Cisco::Environment.environment).to eq(expected) + end + end + end + describe '.environments' do before(:each) do allow(Cisco::Environment).to receive(:data_from_file).and_return({}) @@ -162,6 +193,82 @@ class << Cisco::Environment }, ) end + + it 'loaded file can be overridden by add_env' do + expect(Cisco::Environment).to receive(:data_from_file).with( + '/etc/cisco_node_utils.yaml').and_return(global_config) + expect(Cisco::Environment).to receive(:data_from_file).with( + '~/cisco_node_utils.yaml').and_return(user_config) + expect(Cisco::Environment.environments).to eq( + 'default' => { + host: '127.0.0.1', # global config + port: 57_799, # user overrides global + username: 'user', # user config + password: nil, # auto-populated with nil + cookie: nil, + }, + 'global' => { # global config + host: nil, + port: nil, + username: 'global', + password: 'global', + cookie: nil, + }, + 'user' => { # user config + host: nil, + port: nil, + username: 'user', + password: 'user', + cookie: nil, + }, + ) + added_env = { + host: '192.168.1.1', + port: nil, + username: 'admin', + password: 'admin', + cookie: nil, + } + overide_defult = { + host: '192.168.2.2', + port: nil, + username: 'overridden', + password: 'overridden', + cookie: nil, + } + described_class.add_env('added', added_env) + described_class.add_env('default', overide_defult) + expect(Cisco::Environment.environments).to eq( + 'added' => { # added by method + host: '192.168.1.1', + port: nil, + username: 'admin', + password: 'admin', + cookie: nil, + }, + 'default' => { + host: '192.168.2.2', # method overrides files + port: nil, # method overrides files + username: 'overridden', # method overrides files + password: 'overridden', # method overrides files + cookie: nil, # auto-popuplated with nil + }, + 'global' => { # global config + host: nil, + port: nil, + username: 'global', + password: 'global', + cookie: nil, + }, + 'user' => { # user config + host: nil, + port: nil, + username: 'user', + password: 'user', + cookie: nil, + }, + ) + end end context '.environment' do diff --git a/tests/ciscotest.rb b/tests/ciscotest.rb index a3225f39..6cd6ad27 100644 --- a/tests/ciscotest.rb +++ b/tests/ciscotest.rb @@ -223,8 +223,15 @@ def system_image end def skip_legacy_defect?(pattern, msg) + if pattern.is_a?(String) + pattern = [pattern] + elsif !pattern.is_a?(Array) + fail 'Argument: pattern must be a String or Array object' + end msg = "Defect in legacy image: [#{msg}]" - skip(msg) if Utils.image_version?(Regexp.new(pattern)) + pattern.each do |pat| + skip(msg) if Utils.image_version?(Regexp.new(pat)) + end end def step_unless_legacy_defect(pattern, msg) diff --git a/tests/test_ace.rb b/tests/test_ace.rb index 59e8aefe..1d84529a 100644 --- a/tests/test_ace.rb +++ b/tests/test_ace.rb @@ -1,4 +1,4 @@ -# Copyright (c) 2015-2016 Cisco and/or its affiliates. +# Copyright (c) 2015-2018 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. @@ -57,7 +57,7 @@ def ace_helper(afi, props=nil) end rescue CliError => e skip('This property is not supported on this platform') if - e.message[/(Invalid parameter detected|Invalid command)/] + e.message[/(Invalid parameter detected|Invalid command|Invalid number)/] flunk(e.message) end @@ -83,7 +83,7 @@ def test_action def test_proto %w(ipv4 ipv6).each do |afi| # Sampling of proto's - %w(ip tcp udp).each do |val| + %w(ip tcp udp icmp).each do |val| val = 'ipv6' if val[/ip/] && afi[/ipv6/] a = ace_helper(afi, proto: val) assert_equal(val, a.proto) @@ -149,6 +149,15 @@ def test_dscp end end + def test_vlan + %w(ipv4 ipv6).each do |afi| + %w(10 100).each do |val| + a = ace_helper(afi, proto: 'icmp', vlan: val) + assert_equal(val, a.vlan) + end + end + end + def test_tcp_flags %w(ipv4 ipv6).each do |afi| %w(ack fin urg syn psh rst) + [ @@ -187,6 +196,34 @@ def test_log end end + def test_log_proto_option + %w(ipv4 ipv6).each do |afi| + refute(ace_helper(afi).log) + a = ace_helper(afi, proto: 'icmp', proto_option: 'redirect', log: true) + assert(a.log) + end + end + + def test_proto_option + %w(ipv4 ipv6).each do |afi| + refute(ace_helper(afi).log) + a = ace_helper(afi, proto: 'icmp', proto_option: 'time-exceeded') + assert_equal(a.proto_option, 'time-exceeded') + a = ace_helper(afi, proto: 'icmp', dscp: 'af11', proto_option: 'echo-reply') + assert_equal(a.proto_option, 'echo-reply') + assert_equal(a.dscp, 'af11') + end + end + + def test_redirect_proto_option + afi = 'ipv4' + val = 'port-channel1,port-channel2' + a = ace_helper(afi, proto: 'icmp', proto_option: 'redirect', + redirect: val, log: true, set_erspan_dscp: '3', set_erspan_gre_proto: '33') + assert_equal(val, a.redirect) + assert_equal('redirect', a.proto_option) + end + def test_precedence afi = 'ipv4' %w(critical flash flash-override immediate internet network @@ -244,4 +281,24 @@ def test_ttl_ipv6 'CSCuy47463: access-list ttl does not nvgen') if a.ttl.nil? assert_equal(val, a.ttl) end + + def test_set_erspan_dscp + afi = 'ipv4' + val = 'port-channel1,port-channel2' + a = ace_helper(afi, proto: 'icmp', proto_option: 'redirect', + redirect: val, log: true, set_erspan_dscp: '3', set_erspan_gre_proto: '33') + assert_equal('redirect', a.proto_option) + assert_equal(val, a.redirect) + assert_equal('3', a.set_erspan_dscp) + end + + def test_set_erspan_gre_proto + afi = 'ipv4' + val = 'port-channel1,port-channel2' + a = ace_helper(afi, proto: 'icmp', proto_option: 'redirect', + redirect: val, log: true, set_erspan_gre_proto: '33') + assert_equal('redirect', a.proto_option) + assert_equal(val, a.redirect) + assert_equal('33', a.set_erspan_gre_proto) + end end diff --git a/tests/test_banner.rb b/tests/test_banner.rb new file mode 100644 index 00000000..3d5b853c --- /dev/null +++ b/tests/test_banner.rb @@ -0,0 +1,85 @@ +# +# Minitest for Banner class +# +# Copyright (c) 2014-2018 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_relative 'ciscotest' +require_relative '../lib/cisco_node_utils/banner' + +# TestBanner - Minitest for Banner node utility. +class TestBanner < CiscoTestCase + def setup + # setup runs at the beginning of each test + super + no_motd + end + + def teardown + # teardown runs at the end of each test + no_motd + super + end + + def no_motd + # Turn the feature off for a clean test. + config('no banner motd') + end + + # TESTS + + def test_single_motd + id = 'default' + + banner = Cisco::Banner.new(id) + assert_includes(Cisco::Banner.banners, id) + assert_equal(Cisco::Banner.banners[id], banner) + + assert_equal(banner.default_motd, Cisco::Banner.banners['default'].motd) + assert_equal(banner.default_motd, banner.motd) + + banner.motd = 'Test banner!' + assert_equal(Cisco::Banner.banners['default'].motd, + 'Test banner!') + assert_equal(Cisco::Banner.banners['default'].motd, + banner.motd) + + banner.motd = nil + assert_equal(banner.default_motd, Cisco::Banner.banners['default'].motd) + assert_equal(banner.default_motd, banner.motd) + end + + def test_multiline_motd + skip_versions = ['7.0.3.I[2-6]', '7.0.3.I7.[1-3]', '7.3', '8.[1-3]'] + skip_legacy_defect?(skip_versions, 'multiline banner configuration using nxapi not supported') + id = 'default' + + banner = Cisco::Banner.new(id) + assert_includes(Cisco::Banner.banners, id) + assert_equal(Cisco::Banner.banners[id], banner) + + assert_equal(banner.default_motd, Cisco::Banner.banners['default'].motd) + assert_equal(banner.default_motd, banner.motd) + + banner.motd = 'This is\na sweet\n\nmultiline\nbanner!\n' + assert_equal(Cisco::Banner.banners['default'].motd, + "This is\na sweet\n\nmultiline\nbanner!\n") + assert_equal(Cisco::Banner.banners['default'].motd, + banner.motd) + + banner.motd = nil + assert_equal(banner.default_motd, Cisco::Banner.banners['default'].motd) + assert_equal(banner.default_motd, banner.motd) + end +end diff --git a/tests/test_bgp_af.rb b/tests/test_bgp_af.rb index 120167cc..4dae7284 100644 --- a/tests/test_bgp_af.rb +++ b/tests/test_bgp_af.rb @@ -175,7 +175,7 @@ def check_test_exceptions(test_, os_, vrf_, af_) expect = :runtime if test == :additional_paths_receive && Platform.image_version[/8.0|8.1/] when /I5.2|I5.3|I6/ expect = :success if test == :maximum_paths || test == :maximum_paths_ibgp - when /I7/ + when /I7|9\.2/ expect = :success if test == :maximum_paths || test == :maximum_paths_ibgp || test == :additional_paths_send || diff --git a/tests/test_hostname.rb b/tests/test_hostname.rb new file mode 100644 index 00000000..097451f9 --- /dev/null +++ b/tests/test_hostname.rb @@ -0,0 +1,64 @@ +# +# Minitest for HostName class +# +# Copyright (c) 2014-2018 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_relative 'ciscotest' +require_relative '../lib/cisco_node_utils/hostname' + +# TestHostname - Minitest for SyslogSetting node utility. +class TestHostName < CiscoTestCase + def setup + # setup runs at the beginning of each test + super + hostname_setup + end + + def teardown + # teardown runs at the end of each test + hostname_teardown + super + end + + @current_hostname = '' + + def hostname_setup + hostname_output = Cisco::Client.filter_cli( + cli_output: config('show running-config | include ^hostname'), + value: /hostname (.*)/) + @current_hostname = hostname_output.first unless hostname_output.nil? + # Turn the feature off for a clean test. + config('no hostname') + end + + def hostname_teardown + if @current_hostname != '' + config("hostname #{@current_hostname}") + else + config('no hostname') + end + end + + # TESTS + + def test_hostname_name + hostname_setting = Cisco::HostName.new('testhost') + assert_equal(Cisco::HostName.hostname['testhost'], hostname_setting) + hostname_setting = Cisco::HostName.new('testhost2') + assert_equal(Cisco::HostName.hostname['testhost2'], hostname_setting) + hostname_setting.send('hostname=', nil) + assert_nil(Cisco::HostName.hostname['testhost2']) + end +end diff --git a/tests/test_interface.rb b/tests/test_interface.rb index 73457173..8966b449 100755 --- a/tests/test_interface.rb +++ b/tests/test_interface.rb @@ -1,4 +1,4 @@ -# Copyright (c) 2013-2017 Cisco and/or its affiliates. +# Copyright (c) 2013-2018 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. @@ -1250,6 +1250,27 @@ def test_ipv4_redirects 'Error: ip redirects default get value mismatch') end + def test_ipv6_redirects + interface = create_interface + interface.switchport_mode = :disabled if platform == :nexus + + # check default value + assert_equal(interface.default_ipv6_redirects, interface.ipv6_redirects, + 'Error: ipv6 redirects default get value mismatch') + + # set with value false + interface.ipv6_redirects = false + assert_equal(interface.ipv6_redirects, false) + + # set with value true + interface.ipv6_redirects = true + assert_equal(interface.ipv6_redirects, true) + + # get default and set + interface.ipv6_redirects = interface.default_ipv6_redirects + assert_equal(interface.ipv6_redirects, interface.default_ipv6_redirects) + end + def config_from_hash(inttype_h) inttype_h.each do |k, v| config('feature interface-vlan') if (/^Vlan\d./).match(k.to_s) diff --git a/tests/test_interface_hsrp_group.rb b/tests/test_interface_hsrp_group.rb index 4481d26f..967a32c6 100644 --- a/tests/test_interface_hsrp_group.rb +++ b/tests/test_interface_hsrp_group.rb @@ -109,8 +109,7 @@ def test_mac_addr def test_auth_type_clear skip_legacy_defect?('7.3.0.D1.1', 'CSCuh90262: hsrp indentation') ihg = create_interface_hsrp_group_ipv4 - assert_equal(ihg.default_authentication_string, - ihg.authentication_string) + assert_nil(ihg.authentication_string) attrs = {} attrs[:authentication_auth_type] = 'cleartext' attrs[:authentication_string] = 'Test' @@ -119,8 +118,7 @@ def test_auth_type_clear assert_equal('Test', ihg.authentication_string) attrs[:authentication_string] = ihg.default_authentication_string ihg.authentication_set(attrs) - assert_equal(ihg.default_authentication_string, - ihg.authentication_string) + assert_nil(ihg.authentication_string) ihg = create_interface_hsrp_group_ipv6 attrs[:authentication_auth_type] = 'cleartext' @@ -130,8 +128,7 @@ def test_auth_type_clear assert_equal('Test', ihg.authentication_string) attrs[:authentication_string] = ihg.default_authentication_string ihg.authentication_set(attrs) - assert_equal(ihg.default_authentication_string, - ihg.authentication_string) + assert_nil(ihg.authentication_string) end def test_auth_type_md5 @@ -147,8 +144,7 @@ def test_auth_type_md5 assert_equal('MyMD5Password', ihg.authentication_string) attrs[:authentication_string] = ihg.default_authentication_string ihg.authentication_set(attrs) - assert_equal(ihg.default_authentication_string, - ihg.authentication_string) + assert_nil(ihg.authentication_string) ihg = create_interface_hsrp_group_ipv6 attrs[:authentication_string] = 'MyMD5Password' ihg.authentication_set(attrs) @@ -157,8 +153,7 @@ def test_auth_type_md5 assert_equal('MyMD5Password', ihg.authentication_string) attrs[:authentication_string] = ihg.default_authentication_string ihg.authentication_set(attrs) - assert_equal(ihg.default_authentication_string, - ihg.authentication_string) + assert_nil(ihg.authentication_string) end def test_auth_key_string_enc_0 @@ -178,8 +173,7 @@ def test_auth_key_string_enc_0 assert_equal('7', ihg.authentication_string) attrs[:authentication_string] = ihg.default_authentication_string ihg.authentication_set(attrs) - assert_equal(ihg.default_authentication_string, - ihg.authentication_string) + assert_nil(ihg.authentication_string) ihg = create_interface_hsrp_group_ipv6 attrs[:authentication_string] = '7' ihg.authentication_set(attrs) @@ -189,8 +183,7 @@ def test_auth_key_string_enc_0 assert_equal('7', ihg.authentication_string) attrs[:authentication_string] = ihg.default_authentication_string ihg.authentication_set(attrs) - assert_equal(ihg.default_authentication_string, - ihg.authentication_string) + assert_nil(ihg.authentication_string) end def test_auth_key_string_enc_7 @@ -217,8 +210,7 @@ def test_auth_key_string_enc_7 assert_equal('12345678901234567890', ihg.authentication_string) attrs[:authentication_string] = ihg.default_authentication_string ihg.authentication_set(attrs) - assert_equal(ihg.default_authentication_string, - ihg.authentication_string) + assert_nil(ihg.authentication_string) end def test_auth_key_string_enc_7_compat_timeout_ipv4 @@ -250,8 +242,7 @@ def test_auth_key_string_enc_7_compat_timeout_ipv4 assert_equal(false, ihg.authentication_compatibility) attrs[:authentication_string] = ihg.default_authentication_string ihg.authentication_set(attrs) - assert_equal(ihg.default_authentication_string, - ihg.authentication_string) + assert_nil(ihg.authentication_string) end def test_auth_key_string_enc_7_compat_timeout_ipv6 @@ -283,8 +274,7 @@ def test_auth_key_string_enc_7_compat_timeout_ipv6 assert_equal(false, ihg.authentication_compatibility) attrs[:authentication_string] = ihg.default_authentication_string ihg.authentication_set(attrs) - assert_equal(ihg.default_authentication_string, - ihg.authentication_string) + assert_nil(ihg.authentication_string) end def test_auth_key_string_enc_0_compat_timeout_ipv4 @@ -315,8 +305,7 @@ def test_auth_key_string_enc_0_compat_timeout_ipv4 assert_equal(3333, ihg.authentication_timeout) attrs[:authentication_string] = ihg.default_authentication_string ihg.authentication_set(attrs) - assert_equal(ihg.default_authentication_string, - ihg.authentication_string) + assert_nil(ihg.authentication_string) end def test_auth_key_string_enc_0_compat_timeout_ipv6 @@ -347,8 +336,7 @@ def test_auth_key_string_enc_0_compat_timeout_ipv6 assert_equal(3333, ihg.authentication_timeout) attrs[:authentication_string] = ihg.default_authentication_string ihg.authentication_set(attrs) - assert_equal(ihg.default_authentication_string, - ihg.authentication_string) + assert_nil(ihg.authentication_string) end def test_preempt_ipv4 diff --git a/tests/test_ip_multicast.rb b/tests/test_ip_multicast.rb index 4d3391b6..02eb5826 100644 --- a/tests/test_ip_multicast.rb +++ b/tests/test_ip_multicast.rb @@ -38,31 +38,42 @@ def teardown super end - def test_ip_multicast + def test_overlay_distributed_dr + skip_legacy_defect?('7.0.3.I7.4', 'CSCvk01535') + + ipm = IpMulticast.new + + # Test Defaults + have = ipm.overlay_distributed_dr + should = ipm.default_overlay_distributed_dr + assert_equal(have, should, 'overlay_distributed_dr does not match default value') + + # Test property set + ipm.overlay_distributed_dr = true + assert_equal(ipm.overlay_distributed_dr, true, 'overlay_distributed_dr was not set') + + # Test property unset + ipm.overlay_distributed_dr = false + assert_equal(ipm.overlay_distributed_dr, false, 'overlay_distributed_dr was not unset') + + ipm.destroy + end + + def test_overlay_spt_only ipm = IpMulticast.new - opts = %w(overlay_distributed_dr overlay_spt_only) - # test defaults - opts.each do |opt| - have = ipm.send("#{opt}") - should = ipm.send("default_#{opt}") - assert_equal(have, should, "#{opt} doesn't match the default") - end + # Test Defaults + have = ipm.overlay_spt_only + should = ipm.default_overlay_spt_only + assert_equal(have, should, 'overlay_spt_only does not match default value') - # test property set - opts.each do |opt| - ipm.send("#{opt}=", true) - should = 'ip multicast ' + opt.tr('_', '-') - assert_equal(ipm.send("#{opt}"), should, "#{opt} was not set") - end + # Test property set + ipm.overlay_spt_only = true + assert_equal(ipm.overlay_spt_only, true, 'overlay_spt_only was not set') - # unset property - opts.each do |opt| - ipm.send("#{opt}=", false) - should = false - assert_equal(ipm.send("#{opt}"), should, "#{opt} was not unset") - assert_equal(ipm.send("#{opt}"), ipm.send("default_#{opt}"), "#{opt} doesn't match default") - end + # Test property unset + ipm.overlay_spt_only = false + assert_equal(ipm.overlay_spt_only, false, 'overlay_spt_only was not unset') ipm.destroy end diff --git a/tests/test_nxapi.rb b/tests/test_nxapi.rb index 94ee27f0..ff8b1235 100644 --- a/tests/test_nxapi.rb +++ b/tests/test_nxapi.rb @@ -34,11 +34,19 @@ def all_skipped def setup super + @device.cmd('configure terminal') + @device.cmd('feature nxapi') + @device.cmd('nxapi http port 80') + @device.cmd('end') @product_id = Cisco::Node.new.product_id if @product_id.nil? cleanup end def teardown + @device.cmd('configure terminal') + @device.cmd('feature nxapi') + @device.cmd('nxapi http port 80') + @device.cmd('end') cleanup super end @@ -179,10 +187,6 @@ def test_connection_refused assert_raises Cisco::ConnectionRefused do client.set(values: 'interface loopback41') end - ensure - @device.cmd('configure terminal') - @device.cmd('feature nxapi') - @device.cmd('end') end def test_unauthorized diff --git a/tests/test_syslog_facility.rb b/tests/test_syslog_facility.rb new file mode 100644 index 00000000..74f4789d --- /dev/null +++ b/tests/test_syslog_facility.rb @@ -0,0 +1,80 @@ +# +# Minitest for SyslogFacility class +# +# Copyright (c) 2014-2018 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_relative 'ciscotest' +require_relative '../lib/cisco_node_utils/syslog_facility' + +# TestSyslogFacility - Minitest for SyslogFacility node utility. +class TestSyslogFacility < CiscoTestCase + def setup + # setup runs at the beginning of each test + super + no_syslogfacility + end + + def teardown + # teardown runs at the end of each test + no_syslogfacility + super + end + + def no_syslogfacility + # Turn the feature off for a clean test. + config('no logging level aaa 6', + 'no logging level ip igmp 6', + 'no logging level routing ipv4 multicast 7') + end + + # TESTS + + def test_create_destroy + id = 'aaa' + refute_includes(Cisco::SyslogFacility.facilities, id) + + facility = Cisco::SyslogFacility.new({ 'facility' => id, 'level' => 6 }, true) + assert_includes(Cisco::SyslogFacility.facilities, id) + assert_equal(Cisco::SyslogFacility.facilities[id], facility) + assert_equal(id, Cisco::SyslogFacility.facilities[id].facility) + assert_equal(6, Cisco::SyslogFacility.facilities[id].level) + + facility.destroy + refute_includes(Cisco::SyslogFacility.facilities, id) + end + + def test_create_destroy_extended + id = 'ip igmp' + id2 = 'routing ipv4 multicast' + refute_includes(Cisco::SyslogFacility.facilities, id) + refute_includes(Cisco::SyslogFacility.facilities, id2) + + facility = Cisco::SyslogFacility.new({ 'facility' => id, 'level' => 6 }, true) + facility2 = Cisco::SyslogFacility.new({ 'facility' => id2, 'level' => 7 }, true) + assert_includes(Cisco::SyslogFacility.facilities, id) + assert_equal(Cisco::SyslogFacility.facilities[id], facility) + assert_equal(id, Cisco::SyslogFacility.facilities[id].facility) + assert_equal(6, Cisco::SyslogFacility.facilities[id].level) + assert_includes(Cisco::SyslogFacility.facilities, id2) + assert_equal(Cisco::SyslogFacility.facilities[id2], facility2) + assert_equal(id2, Cisco::SyslogFacility.facilities[id2].facility) + assert_equal(7, Cisco::SyslogFacility.facilities[id2].level) + + facility.destroy + facility2.destroy + refute_includes(Cisco::SyslogFacility.facilities, id) + refute_includes(Cisco::SyslogFacility.facilities, id2) + end +end diff --git a/tests/test_syslog_server.rb b/tests/test_syslog_server.rb index c101e3bd..7d188234 100644 --- a/tests/test_syslog_server.rb +++ b/tests/test_syslog_server.rb @@ -1,7 +1,7 @@ # # Minitest for SyslogServer class # -# Copyright (c) 2014-2016 Cisco and/or its affiliates. +# Copyright (c) 2014-2018 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. @@ -101,7 +101,7 @@ def test_create_options end id = '1.2.3.4' - options = { 'name' => id, 'level' => '4', 'port' => '2154', 'vrf' => 'red' } + options = { 'name' => id, 'level' => '4', 'port' => '2154', 'vrf' => 'red', 'facility' => 'local5' } refute_includes(Cisco::SyslogServer.syslogservers, id) @@ -111,6 +111,7 @@ def test_create_options assert_equal('4', Cisco::SyslogServer.syslogservers[id].level) assert_equal('2154', Cisco::SyslogServer.syslogservers[id].port) assert_equal('red', Cisco::SyslogServer.syslogservers[id].vrf) + assert_equal('local5', Cisco::SyslogServer.syslogservers[id].facility) server.destroy refute_includes(Cisco::SyslogServer.syslogservers, id) diff --git a/tests/test_syslog_settings.rb b/tests/test_syslog_settings.rb index 542e3d54..144de1f0 100644 --- a/tests/test_syslog_settings.rb +++ b/tests/test_syslog_settings.rb @@ -39,7 +39,8 @@ def default_syslogsettings config('no logging timestamp seconds', 'logging console 2', 'logging monitor 5', - 'no logging source-interface') + 'no logging source-interface', + 'no logging logfile') end # TESTS @@ -105,4 +106,18 @@ def test_source_interface syslog_setting.source_interface = nil assert_nil(syslog_setting.source_interface) end + + def test_logfile + syslog_setting = Cisco::SyslogSettings.new('default') + + assert_equal('unset', syslog_setting.logfile_name) + syslog_setting.send('logfile_name=', 'testlog', 5, 'size 4097') + assert_equal('testlog', syslog_setting.logfile_name) + assert_equal('5', syslog_setting.logfile_severity_level) + assert_equal('4097', syslog_setting.logfile_size) + syslog_setting.send('logfile_name=', nil, nil, nil) + assert_equal('unset', syslog_setting.logfile_name) + assert_nil(syslog_setting.logfile_severity_level) + assert_nil(syslog_setting.logfile_size) + end end diff --git a/tests/yum_package.yaml b/tests/yum_package.yaml index 1d674469..1e209259 100644 --- a/tests/yum_package.yaml +++ b/tests/yum_package.yaml @@ -78,6 +78,11 @@ name: 'nxos.sample-n9k_ALL' version: '1.0.0-7.0.3.I7.3' +7_0_3_I7_4_: + filename: 'nxos.sample-n9k_ALL-1.0.0-7.0.3.I7.4.lib32_n9000.rpm' + name: 'nxos.sample-n9k_ALL' + version: '1.0.0-7.0.3.I7.4' + 7_0_3_F1_1_: filename: 'nxos.sample-n8k_EOR-1.0.0-7.0.3.F1.1.lib32_nxos.rpm' name: 'nxos.sample-n8k_EOR'