From 7acbbb8e935b1ca029bd6a468dcbaf901010427c Mon Sep 17 00:00:00 2001 From: Rick Sherman Date: Fri, 3 Aug 2018 17:34:27 -0500 Subject: [PATCH] (CISCO-71) Add support for banner/motd --- lib/cisco_node_utils/banner.rb | 63 ++++++++++++++++++ lib/cisco_node_utils/client/utils.rb | 28 +++++--- lib/cisco_node_utils/cmd_ref/banner.yaml | 7 ++ tests/ciscotest.rb | 9 ++- tests/test_banner.rb | 85 ++++++++++++++++++++++++ 5 files changed, 182 insertions(+), 10 deletions(-) create mode 100644 lib/cisco_node_utils/banner.rb create mode 100644 lib/cisco_node_utils/cmd_ref/banner.yaml create mode 100644 tests/test_banner.rb 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/client/utils.rb b/lib/cisco_node_utils/client/utils.rb index 18b0088e..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 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/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_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