diff --git a/CHANGELOG.md b/CHANGELOG.md index 52c2e6bb..6b51b787 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,9 @@ Changelog * pim (@smigopal) * pim_group_list (@smigopal) * pim_rp_address (@smigopal) +* Port Channel + * interface_portchannel (@saichint) + * portchannel_global (@saichint) * SNMP * snmpnotification (@tphoney) * VDC diff --git a/lib/cisco_node_utils/cmd_ref/interface_portchannel.yaml b/lib/cisco_node_utils/cmd_ref/interface_portchannel.yaml new file mode 100644 index 00000000..95ddc0b5 --- /dev/null +++ b/lib/cisco_node_utils/cmd_ref/interface_portchannel.yaml @@ -0,0 +1,54 @@ +# interface_portchannel +--- +_template: + config_set: ["interface %s"] + config_get_token: '/^interface %s$/i' + config_get: "show running interface all" + +create: + config_set: "interface %s" + +destroy: + config_set: "no interface %s" + +lacp_graceful_convergence: + kind: boolean + auto_default: false + config_get_token_append: ['/^lacp graceful.convergence$/'] + config_set_append: ["%s lacp graceful-convergence"] + default_value: true + +lacp_max_bundle: + kind: int + config_get_token_append: ['/^lacp max.bundle (\d+)$/'] + config_set_append: ["lacp max-bundle %s"] + /N(3|9)/: + default_value: 32 + else: + default_value: 16 + +lacp_min_links: + kind: int + config_get_token_append: ['/^lacp min.links (\d+)$/'] + config_set_append: ["lacp min-links %s"] + default_value: 1 + +lacp_suspend_individual: + kind: boolean + auto_default: false + config_get_token_append: ['/^lacp suspend.individual$/'] + config_set_append: ["%s lacp suspend-individual"] + default_value: true + +port_hash_distribution: + _exclude: [/N6/, /N5/] + config_get_token_append: ['/^port-channel port hash.distribution (.*)$/'] + config_set: ["terminal dont-ask", "interface %s", "%s port-channel port hash-distribution %s", "end"] + default_value: false + +port_load_defer: + _exclude: [/N6/, /N5/] + kind: boolean + config_get_token_append: ['/^port-channel port load.defer$/'] + config_set_append: ["%s port-channel port load-defer"] + default_value: false diff --git a/lib/cisco_node_utils/cmd_ref/portchannel_global.yaml b/lib/cisco_node_utils/cmd_ref/portchannel_global.yaml new file mode 100644 index 00000000..559db253 --- /dev/null +++ b/lib/cisco_node_utils/cmd_ref/portchannel_global.yaml @@ -0,0 +1,69 @@ +# portchannel_global +--- +_template: + config_get: "show running all" + +asymmetric: + _exclude: [/N6/, /N5/, /N3/, /N9/] + default_value: false + +bundle_hash: + /N(5|6|7)/: + default_value: 'ip' + /N(3|9)/: + default_value: 'ip-l4port' + +bundle_select: + default_value: 'src-dst' + +concatenation: + _exclude: [/N7/, /N5/, /N6/] + default_value: false + +hash_distribution: + _exclude: [/N6/, /N5/, /N3/, /N9/] + config_get_token: ['/^port.channel hash.distribution (.*)$/'] + config_set: ["terminal dont-ask", "port-channel hash-distribution %s", "end"] + default_value: 'adaptive' + +hash_poly: + _exclude: [/N7/, /N3/, /N9/] + default_value: 'CRC10b' + +load_balance_type: + kind: string + /N(5|6)/: + default_only: "ethernet" + /N7/: + default_only: "asymmetric" + /N(3|9)/: + default_only: "symmetry" + +load_defer: + _exclude: [/N6/, /N5/, /N3/, /N9/] + kind: int + config_get_token: ['/^port.channel load.defer (\d+)$/'] + config_set: ["port-channel load-defer %s"] + default_value: 120 + +port_channel_load_balance: + multiple: + config_get_token: '/^port-channel load-balance (.*)$/' + config_set: "port-channel load-balance %s %s %s %s %s %s" + +resilient: + _exclude: [/N6/, /N5/, /N7/] + kind: boolean + config_get: "show running-config all" + config_get_token: '/^port-channel load-balance resilient$/' + config_set: "%s port-channel load-balance resilient" + default_value: false + +rotate: + _exclude: [/N5/, /N6/] + kind: int + default_value: 0 + +symmetry: + _exclude: [/N7/, /N3/, /N5/, /N6/] + default_value: false diff --git a/lib/cisco_node_utils/interface_portchannel.rb b/lib/cisco_node_utils/interface_portchannel.rb new file mode 100644 index 00000000..c65c2a9d --- /dev/null +++ b/lib/cisco_node_utils/interface_portchannel.rb @@ -0,0 +1,157 @@ +# December 2015, Sai Chintalapudi +# +# 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_relative 'node_util' +require_relative 'interface' + +# Add some interface-specific constants to the Cisco namespace +module Cisco + # InterfacePortChannel - node utility class for port channel config management + class InterfacePortChannel < NodeUtil + attr_reader :name + + def initialize(name, instantiate=true) + fail TypeError unless name.is_a?(String) + fail ArgumentError unless name.length > 0 + @name = name.downcase + fail ArgumentError unless @name.start_with?('port-channel') + + create if instantiate + end + + def self.interfaces + hash = {} + intf_list = config_get('interface', 'all_interfaces') + return hash if intf_list.nil? + + intf_list.each do |id| + id = id.downcase + next unless id.start_with?('port-channel') + hash[id] = InterfacePortChannel.new(id, false) + end + hash + end + + def create + config_set('interface_portchannel', 'create', @name) + end + + def destroy + config_set('interface_portchannel', 'destroy', @name) + end + + ######################################################## + # PROPERTIES # + ######################################################## + + def lacp_graceful_convergence + config_get('interface_portchannel', 'lacp_graceful_convergence', @name) + end + + def lacp_graceful_convergence=(state) + no_cmd = (state ? '' : 'no') + config_set('interface_portchannel', + 'lacp_graceful_convergence', @name, no_cmd) + rescue Cisco::CliError => e + raise "[#{@name}] '#{e.command}' : #{e.clierror}" + end + + def default_lacp_graceful_convergence + config_get_default('interface_portchannel', 'lacp_graceful_convergence') + end + + def lacp_max_bundle + config_get('interface_portchannel', 'lacp_max_bundle', @name) + end + + def lacp_max_bundle=(val) + config_set('interface_portchannel', 'lacp_max_bundle', @name, val) + rescue Cisco::CliError => e + raise "[#{@name}] '#{e.command}' : #{e.clierror}" + end + + def default_lacp_max_bundle + config_get_default('interface_portchannel', 'lacp_max_bundle') + end + + def lacp_min_links + config_get('interface_portchannel', 'lacp_min_links', @name) + end + + def lacp_min_links=(val) + config_set('interface_portchannel', 'lacp_min_links', @name, val) + rescue Cisco::CliError => e + raise "[#{@name}] '#{e.command}' : #{e.clierror}" + end + + def default_lacp_min_links + config_get_default('interface_portchannel', 'lacp_min_links') + end + + def lacp_suspend_individual + config_get('interface_portchannel', 'lacp_suspend_individual', @name) + end + + def lacp_suspend_individual=(state) + no_cmd = (state ? '' : 'no') + config_set('interface_portchannel', + 'lacp_suspend_individual', @name, no_cmd) + rescue Cisco::CliError => e + raise "[#{@name}] '#{e.command}' : #{e.clierror}" + end + + def default_lacp_suspend_individual + config_get_default('interface_portchannel', 'lacp_suspend_individual') + end + + def port_hash_distribution + config_get('interface_portchannel', 'port_hash_distribution', @name) + end + + def port_hash_distribution=(val) + if val + state = '' + else + state = 'no' + val = '' + end + config_set('interface_portchannel', + 'port_hash_distribution', @name, state, val) + rescue Cisco::CliError => e + raise "[#{@name}] '#{e.command}' : #{e.clierror}" + end + + def default_port_hash_distribution + config_get_default('interface_portchannel', 'port_hash_distribution') + end + + def port_load_defer + config_get('interface_portchannel', 'port_load_defer', @name) + end + + def port_load_defer=(state) + no_cmd = (state ? '' : 'no') + config_set('interface_portchannel', + 'port_load_defer', @name, no_cmd) + rescue Cisco::CliError => e + raise "[#{@name}] '#{e.command}' : #{e.clierror}" + end + + def default_port_load_defer + config_get_default('interface_portchannel', 'port_load_defer') + end + end # Class +end # Module diff --git a/lib/cisco_node_utils/portchannel_global.rb b/lib/cisco_node_utils/portchannel_global.rb new file mode 100644 index 00000000..59040a7e --- /dev/null +++ b/lib/cisco_node_utils/portchannel_global.rb @@ -0,0 +1,281 @@ +# December 2015, Sai Chintalapudi +# +# 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_relative 'node_util' + +module Cisco + # node_utils class for portchannel_global + class PortChannelGlobal < NodeUtil + attr_reader :name + + def initialize(name) + fail TypeError unless name.is_a?(String) + fail ArgumentError unless name == 'default' + @name = name.downcase + end + + def self.globals + hash = {} + # if the key already exists, no need create instance + hash['default'] = + PortChannelGlobal.new('default', false) + hash + end + + ######################################################## + # PROPERTIES # + ######################################################## + + def hash_distribution + config_get('portchannel_global', 'hash_distribution') + end + + def hash_distribution=(val) + config_set('portchannel_global', + 'hash_distribution', val) + rescue Cisco::CliError => e + raise "[#{@name}] '#{e.command}' : #{e.clierror}" + end + + def default_hash_distribution + config_get_default('portchannel_global', 'hash_distribution') + end + + def load_defer + config_get('portchannel_global', 'load_defer') + end + + def load_defer=(val) + config_set('portchannel_global', 'load_defer', val) + rescue Cisco::CliError => e + raise "[#{@name}] '#{e.command}' : #{e.clierror}" + end + + def default_load_defer + config_get_default('portchannel_global', 'load_defer') + end + + def resilient + config_get('portchannel_global', 'resilient') + end + + def resilient=(state) + fail TypeError unless state == true || state == false + no_cmd = (state ? '' : 'no') + config_set('portchannel_global', 'resilient', no_cmd) + rescue Cisco::CliError => e + raise "[#{@name}] '#{e.command}' : #{e.clierror}" + end + + def default_resilient + config_get_default('portchannel_global', 'resilient') + end + + # port-channel load-balance is a complicated command + # and this has so many forms in the same device and in + # different devices. + # For ex: + # port-channel load-balance src-dst ip rotate 4 concatenation symmetric + # port-channel load-balance resilient + # port-channel load-balance ethernet destination-mac CRC10c + # port-channel load-balance ethernet source-ip + # port-channel load-balance dst ip-l4port rotate 4 asymmetric + # port-channel load-balance dst ip-l4port-vlan module 9 + # port-channel load-balance hash-modulo + # we need to eliminate all the clutter and get the correct + # line of config first and after that get index of each property + # and get the property value because some properties may or + # may not be present always. + # This method returns a hash + def port_channel_load_balance + lb = config_get('portchannel_global', + 'port_channel_load_balance') + hash = {} + lb.each do |line| + next if line[/(internal|resilient|module|fex|hash)/] + params = line.split + lb_type = config_get('portchannel_global', 'load_balance_type') + case lb_type.to_sym + when :ethernet # n6k + _parse_ethernet_params(hash, params) + when :asymmetric # n7k + _parse_asymmetric_params(hash, params, line) + when :symmetry # n9k + _parse_symmetry_params(hash, params, line) + end + end + hash + end + + def asymmetric + port_channel_load_balance[:asymmetric] + end + + def default_asymmetric + config_get_default('portchannel_global', + 'asymmetric') + end + + def bundle_hash + port_channel_load_balance[:bundle_hash] + end + + def default_bundle_hash + config_get_default('portchannel_global', + 'bundle_hash') + end + + def bundle_select + port_channel_load_balance[:bundle_select] + end + + def default_bundle_select + config_get_default('portchannel_global', + 'bundle_select') + end + + def concatenation + port_channel_load_balance[:concatenation] + end + + def default_concatenation + config_get_default('portchannel_global', + 'concatenation') + end + + def hash_poly + port_channel_load_balance[:hash_poly] + end + + def default_hash_poly + config_get_default('portchannel_global', + 'hash_poly') + end + + def rotate + port_channel_load_balance[:rotate] + end + + def default_rotate + config_get_default('portchannel_global', + 'rotate') + end + + def symmetry + port_channel_load_balance[:symmetry] + end + + def default_symmetry + config_get_default('portchannel_global', + 'symmetry') + end + + def port_channel_load_balance=(bselect, bhash, hpoly, asy, sy, conc, rot) + lb_type = config_get('portchannel_global', 'load_balance_type') + case lb_type.to_sym + when :ethernet # n6k + if bselect == 'src' + sel = 'source' + elsif bselect == 'dst' + sel = 'destination' + else + sel = 'source-dest' + end + sel_hash = sel + '-' + bhash + # port-channel load-balance ethernet destination-mac CRC10c + config_set('portchannel_global', 'port_channel_load_balance', + 'ethernet', sel_hash, hpoly, '', '', '') + when :asymmetric # n7k + asym = (asy == true) ? 'asymmetric' : '' + # port-channel load-balance dst ip-l4port rotate 4 asymmetric + config_set('portchannel_global', 'port_channel_load_balance', + bselect, bhash, 'rotate', rot.to_s, asym, '') + when :symmetry # n9k + sym = sy ? 'symmetric' : '' + concat = conc ? 'concatenation' : '' + rot_str = rot.zero? ? '' : 'rotate' + rot_val = rot.zero? ? '' : rot.to_s + # port-channel load-balance src-dst ip rotate 4 concatenation symmetric + config_set('portchannel_global', 'port_channel_load_balance', + bselect, bhash, rot_str, rot_val, concat, sym) + end + rescue Cisco::CliError => e + raise "[#{@name}] '#{e.command}' : #{e.clierror}" + end + + # on n6k, the bundle hash and bundle select are + # merged into one and so we need to break them apart, + # also they are called source and destination instead of + # src and dst as in other devices, so we convert them + def _parse_ethernet_params(hash, params) + hash_poly = params[2] + # hash_poly is not shown on the running config + # if it is default under some circumstatnces + hash_poly = hash_poly.nil? ? 'CRC10b' : hash_poly + select_hash = params[1] + lparams = select_hash.split('-') + if lparams[0].downcase == 'destination' + bselect = 'dst' + bhash = lparams[1] + else + if select_hash.include? '-dest-' + bselect = 'src-dst' + bhash = lparams[2] + # there are bundles hashes like ip-only and + # port-only specific to src-dst + bhash += '-only' if select_hash.include? 'only' + else + bselect = 'src' + bhash = lparams[1] + end + end + hash[:bundle_select] = bselect + hash[:bundle_hash] = bhash + hash[:hash_poly] = hash_poly + hash + end + + def _parse_asymmetric_params(hash, params, line) + bselect = params[0] + bhash = params[1] + # asymmetric keyword does not show up if it is false + asym = (line.include? 'asymmetric') ? true : false + ri = params.index('rotate') + rotate = params[ri + 1].to_i + hash[:bundle_select] = bselect + hash[:bundle_hash] = bhash + hash[:asymmetric] = asym + hash[:rotate] = rotate + hash + end + + def _parse_symmetry_params(hash, params, line) + bselect = params[0] + bhash = params[1] + ri = params.index('rotate') + rotate = params[ri + 1].to_i + # concatenation and symmetry keywords do not show up if false + concat = (line.include? 'concatenation') ? true : false + sym = (line.include? 'symmetric') ? true : false + hash[:bundle_select] = bselect + hash[:bundle_hash] = bhash + hash[:symmetry] = sym + hash[:rotate] = rotate + hash[:concatenation] = concat + hash + end + end # class +end # module diff --git a/tests/test_interface_portchannel.rb b/tests/test_interface_portchannel.rb new file mode 100644 index 00000000..c42fe342 --- /dev/null +++ b/tests/test_interface_portchannel.rb @@ -0,0 +1,105 @@ +# 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_relative 'ciscotest' +require_relative '../lib/cisco_node_utils/interface' +require_relative '../lib/cisco_node_utils/interface_portchannel' + +# TestX__CLASS_NAME__X - Minitest for X__CLASS_NAME__X node utility class +class TestInterfacePortChannel < CiscoTestCase + # TESTS + + DEFAULT_NAME = 'port-channel134' + + def setup + super + config "no interface #{DEFAULT_NAME}" + end + + def teardown + config "no interface #{DEFAULT_NAME}" + super + end + + def n6k_platform? + /N(5|6)/ =~ node.product_id + end + + def create_port_channel(ifname=DEFAULT_NAME) + InterfacePortChannel.new(ifname) + end + + def test_get_set_port_hash_distribution + skip('Platform does not support this property') if n6k_platform? + interface = create_port_channel + interface.port_hash_distribution = 'adaptive' + assert_equal('adaptive', interface.port_hash_distribution) + interface.port_hash_distribution = 'fixed' + assert_equal('fixed', interface.port_hash_distribution) + interface.port_hash_distribution = + interface.default_port_hash_distribution + assert_equal(interface.default_port_hash_distribution, + interface.port_hash_distribution) + end + + def test_get_set_lacp_graceful_convergence + interface = create_port_channel + interface.lacp_graceful_convergence = false + assert_equal(false, interface.lacp_graceful_convergence) + interface.lacp_graceful_convergence = + interface.default_lacp_graceful_convergence + assert_equal(interface.default_lacp_graceful_convergence, + interface.lacp_graceful_convergence) + end + + def test_get_set_lacp_min_links + interface = create_port_channel + interface.lacp_min_links = 5 + assert_equal(5, interface.lacp_min_links) + interface.lacp_min_links = interface.default_lacp_min_links + assert_equal(interface.default_lacp_min_links, + interface.lacp_min_links) + end + + def test_get_set_lacp_max_bundle + interface = create_port_channel + interface.lacp_max_bundle = 10 + assert_equal(10, interface.lacp_max_bundle) + interface.lacp_max_bundle = + interface.default_lacp_max_bundle + assert_equal(interface.default_lacp_max_bundle, + interface.lacp_max_bundle) + end + + def test_get_set_lacp_suspend_individual + interface = create_port_channel + interface.lacp_suspend_individual = false + assert_equal(false, interface.lacp_suspend_individual) + interface.lacp_suspend_individual = + interface.default_lacp_suspend_individual + assert_equal(interface.default_lacp_suspend_individual, + interface.lacp_suspend_individual) + end + + def test_get_set_port_load_defer + skip('Platform does not support this property') if n6k_platform? + interface = create_port_channel + interface.port_load_defer = true + assert_equal(true, interface.port_load_defer) + interface.port_load_defer = + interface.default_port_load_defer + assert_equal(interface.default_port_load_defer, + interface.port_load_defer) + end +end diff --git a/tests/test_portchannel_global.rb b/tests/test_portchannel_global.rb new file mode 100644 index 00000000..ba663bf2 --- /dev/null +++ b/tests/test_portchannel_global.rb @@ -0,0 +1,193 @@ +# 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_relative 'ciscotest' +require_relative '../lib/cisco_node_utils/portchannel_global' + +# TestX__CLASS_NAME__X - Minitest for X__CLASS_NAME__X node utility class +class TestPortchannelGlobal < CiscoTestCase + # TESTS + + DEFAULT_NAME = 'default' + + def setup + super + config 'no port-channel load-balance' unless n6k_platform? + config 'no port-channel load-balance ethernet' unless + n9k_platform? || n7k_platform? + end + + def teardown + config 'no port-channel load-balance' unless n6k_platform? + config 'no port-channel load-balance ethernet' unless + n9k_platform? || n7k_platform? + super + end + + def n7k_platform? + /N7/ =~ node.product_id + end + + def n9k_platform? + /N(3|9)/ =~ node.product_id + end + + def n6k_platform? + /N(5|6)/ =~ node.product_id + end + + def create_portchannel_global(name=DEFAULT_NAME) + PortChannelGlobal.new(name) + end + + def test_get_hash_distribution + skip('Platform does not support this property') if n6k_platform? || + n9k_platform? + @global = create_portchannel_global + @global.hash_distribution = 'fixed' + assert_equal('fixed', @global.hash_distribution) + @global.hash_distribution = + @global.default_hash_distribution + assert_equal(@global.default_hash_distribution, + @global.hash_distribution) + end + + def test_get_set_load_defer + skip('Platform does not support this property') if n6k_platform? || + n9k_platform? + @global = create_portchannel_global + @global.load_defer = 1000 + assert_equal(1000, @global.load_defer) + @global.load_defer = + @global.default_load_defer + assert_equal(@global.default_load_defer, + @global.load_defer) + end + + def test_get_set_resilient + skip('Platform does not support this property') if n6k_platform? || + n7k_platform? + @global = create_portchannel_global + @global.resilient = true + assert_equal(true, @global.resilient) + @global.resilient = @global.default_resilient + assert_equal(@global.default_resilient, @global.resilient) + end + + def test_get_set_port_channel_load_balance_sym_concat_rot + skip('Platform does not support this property') if n6k_platform? || + n7k_platform? + @global = create_portchannel_global + @global.send(:port_channel_load_balance=, + 'src-dst', 'ip-l4port', nil, nil, true, true, 4) + assert_equal('src-dst', + @global.bundle_select) + assert_equal('ip-l4port', + @global.bundle_hash) + assert_equal(true, @global.symmetry) + assert_equal(true, @global.concatenation) + assert_equal(4, @global.rotate) + @global.send( + :port_channel_load_balance=, + @global.default_bundle_select, + @global.default_bundle_hash, + nil, + nil, + @global.default_symmetry, + @global.default_concatenation, + @global.default_rotate) + assert_equal( + @global.default_bundle_select, + @global.bundle_select) + assert_equal( + @global.default_bundle_hash, + @global.bundle_hash) + assert_equal( + @global.default_symmetry, + @global.symmetry) + assert_equal( + @global.default_concatenation, + @global.concatenation) + assert_equal(@global.default_rotate, + @global.rotate) + end + + def test_get_set_port_channel_load_balance_hash_poly + skip('Platform does not support this property') if n7k_platform? || + n9k_platform? + @global = create_portchannel_global + @global.send(:port_channel_load_balance=, + 'src-dst', 'ip-only', 'CRC10c', nil, nil, nil, nil) + assert_equal('src-dst', + @global.bundle_select) + assert_equal('ip-only', + @global.bundle_hash) + assert_equal('CRC10c', @global.hash_poly) + @global.send(:port_channel_load_balance=, + 'dst', 'mac', 'CRC10a', nil, nil, nil, nil) + assert_equal('dst', + @global.bundle_select) + assert_equal('mac', + @global.bundle_hash) + assert_equal('CRC10a', @global.hash_poly) + @global.send( + :port_channel_load_balance=, + @global.default_bundle_select, + @global.default_bundle_hash, + @global.default_hash_poly, + nil, nil, nil, nil) + assert_equal( + @global.default_bundle_select, + @global.bundle_select) + assert_equal( + @global.default_bundle_hash, + @global.bundle_hash) + assert_equal(@global.default_hash_poly, + @global.hash_poly) + end + + def test_get_set_port_channel_load_balance_asym_rot + skip('Platform does not support this property') if n6k_platform? || + n9k_platform? + @global = create_portchannel_global + @global.send(:port_channel_load_balance=, + 'src-dst', 'ip-vlan', nil, true, nil, nil, 4) + assert_equal('src-dst', + @global.bundle_select) + assert_equal('ip-vlan', + @global.bundle_hash) + assert_equal(true, @global.asymmetric) + assert_equal(4, @global.rotate) + @global.send( + :port_channel_load_balance=, + @global.default_bundle_select, + @global.default_bundle_hash, + nil, + @global.default_asymmetric, + nil, + nil, + @global.default_rotate) + assert_equal( + @global.default_bundle_select, + @global.bundle_select) + assert_equal( + @global.default_bundle_hash, + @global.bundle_hash) + assert_equal( + @global.default_asymmetric, + @global.asymmetric) + assert_equal(@global.default_rotate, + @global.rotate) + end +end