diff --git a/lib/cisco_node_utils/cmd_ref/interface.yaml b/lib/cisco_node_utils/cmd_ref/interface.yaml index ff529471..7f55f8bd 100644 --- a/lib/cisco_node_utils/cmd_ref/interface.yaml +++ b/lib/cisco_node_utils/cmd_ref/interface.yaml @@ -18,8 +18,11 @@ all_interfaces: multiple: ios_xr: get_command: 'show running all-interfaces' + get_value: '/^interface (.*)/' + nexus: + get_command: "show running-config interface | section '^interface'" + get_value: '/(.*)/' get_context: ~ - get_value: '/^interface (.*)/' bfd_echo: _exclude: [ios_xr] @@ -40,6 +43,13 @@ create: set_context: ~ set_value: "interface " +default: + multiple: + set_context: ~ + set_value: "default interface " + get_command: "show running interface " + get_value: '/(.*)/' + description: kind: string get_value: '/^description (.*)/' diff --git a/lib/cisco_node_utils/cmd_ref/interface_ospf.yaml b/lib/cisco_node_utils/cmd_ref/interface_ospf.yaml index 2a5a83a1..7fcf8843 100644 --- a/lib/cisco_node_utils/cmd_ref/interface_ospf.yaml +++ b/lib/cisco_node_utils/cmd_ref/interface_ospf.yaml @@ -6,6 +6,11 @@ _template: get_command: 'show running interface all' context: ["interface %s"] +all_interfaces: + multiple: + get_context: ~ + get_value: '/^interface (.*)/' + area: get_value: '/^\s*ip router ospf (\S+) area (\S+)/' set_value: "%s ip router ospf %s area %s" diff --git a/lib/cisco_node_utils/cmd_ref/interface_portchannel.yaml b/lib/cisco_node_utils/cmd_ref/interface_portchannel.yaml index 79b864a3..c445f4ac 100644 --- a/lib/cisco_node_utils/cmd_ref/interface_portchannel.yaml +++ b/lib/cisco_node_utils/cmd_ref/interface_portchannel.yaml @@ -7,6 +7,11 @@ _template: get_context: ['/^interface %s$/i'] set_context: ["interface %s"] +all_interfaces: + multiple: + get_context: ~ + get_value: '/^interface (.*)/' + bfd_per_link: kind: boolean get_value: '/^bfd per-link$/' diff --git a/lib/cisco_node_utils/interface.rb b/lib/cisco_node_utils/interface.rb index ce97e3c0..5db9eaa9 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-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. @@ -51,15 +51,22 @@ class Interface < Cisco::InterfaceDeprecated # Regexp to match various link bundle interface variants PORTCHANNEL = Regexp.new('(port-channel|Bundle-Ether)', Regexp::IGNORECASE) - attr_reader :name + attr_reader :name, :state_default - def initialize(name, instantiate=true) + def initialize(name, instantiate=true, default_state=false) fail TypeError unless name.is_a?(String) fail ArgumentError unless name.length > 0 @name = name.downcase @smr = config_get('interface', 'stp_mst_range') @svr = config_get('interface', 'stp_vlan_range') @match_found = false + # Keep track of default vs non-default state for + # interfaces that cannot be created/destroyed. + @state_default = nil + # Track ethernet but not sub-interfaces + if @name[/ethernet/] && !@name[/ethernet.*\.\d+/] + @state_default = default_state + end create if instantiate end @@ -72,10 +79,23 @@ def self.interfaces(opt=nil) intf_list = config_get('interface', 'all_interfaces') return hash if intf_list.nil? + # Massage intf_list data into an array that is easy + # to work with. + intf_list.collect! { |x| x.strip || x } + intf_list.delete('') + intf_list = intf_list.join(' ').split('interface') + intf_list.delete('') + intf_list.each do |id| - id = id.downcase + int_data = id.strip.split(' ') + next if int_data[0].nil? + id = int_data[0].downcase next if opt && filter(opt, id) - hash[id] = Interface.new(id, false) + # If there are any additional options associated + # with this interface then it's in a non-default + # state. + default_state = int_data.size > 1 ? false : true + hash[id] = Interface.new(id, false, default_state) end hash end @@ -141,7 +161,16 @@ def create end def destroy - config_set('interface', 'destroy', name: @name) + if @name[/ethernet/] && !@name[/ethernet.*\.\d+/] + config_set('interface', 'default', name: @name) + else + config_set('interface', 'destroy', name: @name) + end + end + + def default? + state = config_get('interface', 'default', name: @name) + state.nil? ? true : false end def pvlan_enable @@ -579,6 +608,7 @@ def ipv4_dhcp_relay_info_trust end def ipv4_dhcp_relay_info_trust=(state) + return false if !state && !Feature.dhcp_enabled? Feature.dhcp_enable if state config_set('interface', 'ipv4_dhcp_relay_info_trust', name: @name, state: state ? '' : 'no') @@ -593,6 +623,7 @@ def ipv4_dhcp_relay_src_addr_hsrp end def ipv4_dhcp_relay_src_addr_hsrp=(state) + return false if !state && !Feature.dhcp_enabled? Feature.dhcp_enable if state config_set('interface', 'ipv4_dhcp_relay_src_addr_hsrp', name: @name, state: state ? '' : 'no') @@ -611,8 +642,9 @@ def ipv4_dhcp_relay_src_intf def ipv4_dhcp_relay_src_intf=(val) state = val == default_ipv4_dhcp_relay_src_intf ? 'no' : '' - intf = val == default_ipv4_dhcp_relay_src_intf ? '' : val + return false if state == 'no' && !Feature.dhcp_enabled? Feature.dhcp_enable if state.empty? + intf = val == default_ipv4_dhcp_relay_src_intf ? '' : val config_set('interface', 'ipv4_dhcp_relay_src_intf', name: @name, state: state, intf: intf) end @@ -626,6 +658,7 @@ def ipv4_dhcp_relay_subnet_broadcast end def ipv4_dhcp_relay_subnet_broadcast=(state) + return false if !state && !Feature.dhcp_enabled? Feature.dhcp_enable if state config_set('interface', 'ipv4_dhcp_relay_subnet_broadcast', name: @name, state: state ? '' : 'no') @@ -640,6 +673,7 @@ def ipv4_dhcp_smart_relay end def ipv4_dhcp_smart_relay=(state) + return false if !state && !Feature.dhcp_enabled? Feature.dhcp_enable if state config_set('interface', 'ipv4_dhcp_smart_relay', name: @name, state: state ? '' : 'no') @@ -788,8 +822,9 @@ def ipv6_dhcp_relay_src_intf def ipv6_dhcp_relay_src_intf=(val) state = val == default_ipv6_dhcp_relay_src_intf ? 'no' : '' - intf = val == default_ipv6_dhcp_relay_src_intf ? '' : val + return false if state == 'no' && !Feature.dhcp_enabled? Feature.dhcp_enable if state.empty? + intf = val == default_ipv6_dhcp_relay_src_intf ? '' : val config_set('interface', 'ipv6_dhcp_relay_src_intf', name: @name, state: state, intf: intf) end @@ -1831,6 +1866,8 @@ def switchport_vtp def switchport_vtp=(vtp_set) # TODO: throw UnsupportedError instead of returning false? return false unless switchport_vtp_mode_capable? + return false if !vtp_set && !Feature.vtp_enabled? + Feature.vtp_enable if vtp_set no_cmd = (vtp_set) ? '' : 'no' config_set('interface', 'vtp', name: @name, state: no_cmd) end diff --git a/lib/cisco_node_utils/interface_ospf.rb b/lib/cisco_node_utils/interface_ospf.rb index 8f7f48ce..c7568c3d 100644 --- a/lib/cisco_node_utils/interface_ospf.rb +++ b/lib/cisco_node_utils/interface_ospf.rb @@ -1,6 +1,6 @@ # March 2015, Alex Hunsberger # -# 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. @@ -53,7 +53,7 @@ def self.interfaces(ospf_name=nil) fail TypeError unless ospf_name.is_a?(String) || ospf_name.nil? ints = {} - intf_list = config_get('interface', 'all_interfaces') + intf_list = config_get('interface_ospf', 'all_interfaces') return ints if intf_list.nil? intf_list.each do |name| match = config_get('interface_ospf', 'area', name) diff --git a/lib/cisco_node_utils/interface_portchannel.rb b/lib/cisco_node_utils/interface_portchannel.rb index 16f803f5..5496cf88 100644 --- a/lib/cisco_node_utils/interface_portchannel.rb +++ b/lib/cisco_node_utils/interface_portchannel.rb @@ -1,6 +1,6 @@ # December 2015, Sai Chintalapudi # -# 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. @@ -38,7 +38,7 @@ def to_s def self.interfaces hash = {} - intf_list = config_get('interface', 'all_interfaces') + intf_list = config_get('interface_portchannel', 'all_interfaces') return hash if intf_list.nil? intf_list.each do |id| diff --git a/tests/test_bgp_af.rb b/tests/test_bgp_af.rb index daa78cc1..fc95f2bf 100644 --- a/tests/test_bgp_af.rb +++ b/tests/test_bgp_af.rb @@ -4,7 +4,7 @@ # # Richard Wellum, August, 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. @@ -170,7 +170,7 @@ def check_test_exceptions(test_, os_, vrf_, af_) # triggers a version check below. if expect == :runtime case Platform.image_version - when /8.0|8.1/ + when /8.0|8.1|8.2/ expect = :success when /I5.2|I5.3|I6/ expect = :success if test == :maximum_paths || test == :maximum_paths_ibgp diff --git a/tests/test_interface.rb b/tests/test_interface.rb index a7d708f5..f5c6d5b0 100755 --- a/tests/test_interface.rb +++ b/tests/test_interface.rb @@ -1,4 +1,4 @@ -# 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. @@ -1837,6 +1837,28 @@ def test_load_interval_counter_3_delay lb.destroy end + def test_default_physical + name = interfaces[0] + int = Interface.new(name) + int.switchport_mode = :disabled + + # Verify l3 -> default + int.description = 'default_pysical' + int.ipv4_addr_mask_set('192.168.0.1', '24') + refute(int.default?) + + int.destroy + assert(int.default?) + + # Verify l2 trunk -> default + int.switchport_mode = :access + int.switchport_autostate_exclude = true + refute(int.default?) + + int.destroy + assert(int.default?) + end + def test_purge_config name = interfaces[0] int = Interface.new(name)