From e1a47c4676f97d560ff4ac13edbdce2b8fe61516 Mon Sep 17 00:00:00 2001 From: Robert Cheramy Date: Wed, 21 Feb 2024 12:04:56 +0100 Subject: [PATCH 1/2] apc_aos: fixed ftp and scp. Fixes #1802 (@robertcheramy) - scp support for apc_aos - Needed dependencies on net-ftp and net-scp - net/scp nust be required - introducing minitests for models - Hiding the configuration generated time did not remove the new line and produced a warning in rake test --- CHANGELOG.md | 1 + docs/Supported-OS-Types.md | 2 +- lib/oxidized/input/scp.rb | 1 + lib/oxidized/model/apc_aos.rb | 5 +- oxidized.gemspec | 2 + spec/model/apc_aos_spec.rb | 130 ++++++++++++++++++++++++++++++++++ 6 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 spec/model/apc_aos_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index c838202b7..123011598 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Fix 'wpa passphrase' hashed secret for SonicOS devices with built-in wireless #3036 (@lazynooblet) - Fix potential busy wait when retries and/or next_adds_job is enabled (@gs-kamnas) - Reverting PR #2498 as it broke old procurve models (2510G, 2610, 2824). Fixes #2833, #2871 (@robertcheramy) +- apc_aos: fixed ftp and scp. Fixes #1802 (@robertcheramy) ## [0.29.1 - 2023-04-24] diff --git a/docs/Supported-OS-Types.md b/docs/Supported-OS-Types.md index 29abfeff4..f7d724424 100644 --- a/docs/Supported-OS-Types.md +++ b/docs/Supported-OS-Types.md @@ -16,7 +16,7 @@ |Allied Telesis |Alliedware Plus |[awplus](/lib/oxidized/model/awplus.rb) | |AT-8000S, AT-8000GS series |[powerconnect](/lib/oxidized/model/powerconnect.rb) |Alvarion |BreezeACCESS |[alvarion](/lib/oxidized/model/alvarion.rb) -|APC |AOS |[apc_aos](/lib/oxidized/model/apc_aos.rb) +|APC |AOS |[apc_aos](/lib/oxidized/model/apc_aos.rb) |@robertcheramy |Arbor Networks |ArbOS |[arbos](/lib/oxidized/model/arbos.rb) | |[ArbOS](Model-Notes/ArbOS.md) |Arista |EOS |[eos](/lib/oxidized/model/eos.rb) | |[EOS](Model-Notes/EOS.md) |Arris |C4CMTS |[c4cmts](/lib/oxidized/model/c4cmts.rb) diff --git a/lib/oxidized/input/scp.rb b/lib/oxidized/input/scp.rb index 8ae331758..a251262dd 100644 --- a/lib/oxidized/input/scp.rb +++ b/lib/oxidized/input/scp.rb @@ -1,5 +1,6 @@ module Oxidized require 'net/ssh' + require 'net/scp' require 'timeout' require_relative 'cli' diff --git a/lib/oxidized/model/apc_aos.rb b/lib/oxidized/model/apc_aos.rb index 010f2970f..e2e2cb638 100644 --- a/lib/oxidized/model/apc_aos.rb +++ b/lib/oxidized/model/apc_aos.rb @@ -2,9 +2,10 @@ class Apc_aos < Oxidized::Model # rubocop:disable Naming/ClassAndModuleCamelCase using Refinements cmd 'config.ini' do |cfg| - cfg.gsub! /^; Configuration file, generated on.*/, '' + cfg.gsub!(/^; Configuration file, generated on.*\n/, '') + cfg end - cfg :ftp do + cfg :ftp, :scp do end end diff --git a/oxidized.gemspec b/oxidized.gemspec index 58857d3bc..3d8e0e586 100644 --- a/oxidized.gemspec +++ b/oxidized.gemspec @@ -24,6 +24,8 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'asetus', '~> 0.1' s.add_runtime_dependency 'bcrypt_pbkdf', '~> 1.0' s.add_runtime_dependency 'ed25519', '~> 1.2' + s.add_runtime_dependency 'net-ftp', '~> 0.2' + s.add_runtime_dependency 'net-scp', '~> 4.0' s.add_runtime_dependency 'net-ssh', '~> 7.1' s.add_runtime_dependency 'net-telnet', '~> 0.2' s.add_runtime_dependency 'psych', '~> 3.3.2' diff --git a/spec/model/apc_aos_spec.rb b/spec/model/apc_aos_spec.rb new file mode 100644 index 000000000..5427cf3dc --- /dev/null +++ b/spec/model/apc_aos_spec.rb @@ -0,0 +1,130 @@ +require_relative '../spec_helper' + +describe 'Model apc_aos' do + before(:each) do + Oxidized.asetus = Asetus.new + Oxidized.asetus.cfg.debug = false + Oxidized.setup_logger + + Oxidized::Node.any_instance.stubs(:resolve_repo) + Oxidized::Node.any_instance.stubs(:resolve_output) + end + + it "fetches the configuration with ftp" do + @node = Oxidized::Node.new(name: 'example.com', + input: 'ftp', + output: 'file', + model: 'apc_aos', + username: 'alma', + password: 'armud', + prompt: 'test_prompt') + Oxidized::FTP.any_instance.stubs(:connect).returns(true) + Oxidized::FTP.any_instance.stubs(:node).returns(@node) + Oxidized::FTP.any_instance.stubs(:connect_cli).returns(true) + Oxidized::FTP.any_instance.stubs(:disconnect).returns(true) + Oxidized::FTP.any_instance.stubs(:disconnect_cli).returns(true) + # Make sure we only run "config.ini" an no other command + Oxidized::FTP.any_instance.expects(:cmd).never + Oxidized::FTP.any_instance.expects(:cmd).with("config.ini").returns(CONFIGURATION_FILE) + + status, result = @node.run + + _(status).must_equal :success + _(result.to_cfg).must_equal EXPECTED_RESULT + end + + it "fetches the configuration with scp" do + @node = Oxidized::Node.new(name: 'example.com', + input: 'scp', + output: 'file', + model: 'apc_aos', + username: 'alma', + password: 'armud', + prompt: 'test_prompt') + Oxidized::SCP.any_instance.stubs(:connect).returns(true) + Oxidized::SCP.any_instance.stubs(:node).returns(@node) + Oxidized::SCP.any_instance.stubs(:connect_cli).returns(true) + Oxidized::SCP.any_instance.stubs(:disconnect).returns(true) + Oxidized::SCP.any_instance.stubs(:disconnect_cli).returns(true) + # Make sure we only run "config.ini" an no other command + Oxidized::SCP.any_instance.expects(:cmd).never + Oxidized::SCP.any_instance.expects(:cmd).with("config.ini").returns(CONFIGURATION_FILE) + + status, result = @node.run + + _(status).must_equal :success + _(result.to_cfg).must_equal EXPECTED_RESULT + end + + it "does not fetch the configiguration with ssh" do + @node = Oxidized::Node.new(name: 'example.com', + input: 'ssh', + output: 'file', + model: 'apc_aos', + username: 'alma', + password: 'armud', + prompt: 'test_prompt') + + status, = @node.run + + _(status).must_equal :fail + end +end + +# Not taking the whole configuration. +# For now, the model does only mask the generation date +# In the future, it may hide passwords, so I included a line with snmp comunity strings +CONFIGURATION_FILE = <<~HEREDOC.freeze + ; Schneider Electric + ; Network Management Card AOS v2.5.0.8 + ; Smart-UPS APP v2.5.0.6 + ; (c) 2023 Schneider Electric. All rights reserved. + ; Configuration file, generated on 02/20/2024 at 09:27:23 by Administrator apc + + [NetworkTCP/IP] + SystemIP=0.0.0.0 + SubnetMask=0.0.0.0 + DefaultGateway=0.0.0.0 + IPv4=enabled + BootMode=DHCP Only + HostName=myhostname + DomainName=mydomain.local + ; (...) + + [NetworkSNMP] + ; To change the User Profile Auth Phrase, or the + ; User Profile Encrypt Phrase, use the UserProfile#AuthPhrase, or + ; UserProfile#EncryptPhrase keywords respectively where # is + ; the number of the profile. i.e., UserProfile1EncryptPhrase=apc crypt passphrase + Access=enabled + AccessControl1Community=public + AccessControl2Community=public + ; (...) +HEREDOC + +EXPECTED_RESULT = <<~HEREDOC.freeze + ; Schneider Electric + ; Network Management Card AOS v2.5.0.8 + ; Smart-UPS APP v2.5.0.6 + ; (c) 2023 Schneider Electric. All rights reserved. + + [NetworkTCP/IP] + SystemIP=0.0.0.0 + SubnetMask=0.0.0.0 + DefaultGateway=0.0.0.0 + IPv4=enabled + BootMode=DHCP Only + HostName=myhostname + DomainName=mydomain.local + ; (...) + + [NetworkSNMP] + ; To change the User Profile Auth Phrase, or the + ; User Profile Encrypt Phrase, use the UserProfile#AuthPhrase, or + ; UserProfile#EncryptPhrase keywords respectively where # is + ; the number of the profile. i.e., UserProfile1EncryptPhrase=apc crypt passphrase + Access=enabled + AccessControl1Community=public + AccessControl2Community=public + ; (...) +HEREDOC From 6ec8ee58d6fdd971932c4baebb0df9ec1e35db06 Mon Sep 17 00:00:00 2001 From: Robert Cheramy Date: Tue, 27 Feb 2024 15:33:41 +0100 Subject: [PATCH 2/2] Disabling SCP for apc_aos for now We have to wait until Net::SCP has been fixed in order to support SCP for apc_aos This has been documented in a new Model Note (Fixed Rubocop Warnings introduced by a previous commit) --- .rubocop.yml | 6 ++-- .rubocop_todo.yml | 14 +++++----- CHANGELOG.md | 1 - docs/Model-Notes/APC_AOS.md | 52 +++++++++++++++++++++++++++++++++++ docs/Supported-OS-Types.md | 2 +- lib/oxidized/model/apc_aos.rb | 2 +- spec/model/apc_aos_spec.rb | 1 + 7 files changed, 65 insertions(+), 13 deletions(-) create mode 100644 docs/Model-Notes/APC_AOS.md diff --git a/.rubocop.yml b/.rubocop.yml index de823ffd4..0893f63f4 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -71,10 +71,10 @@ Style/ParallelAssignment: Enabled: false Metrics/MethodLength: - Max: 45 + Max: 50 -## Metrics/AbcSize: -## Max: 28 +Metrics/AbcSize: + Max: 28 Metrics/ClassLength: Max: 200 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 9798a59aa..35655385e 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,22 +1,22 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2024-01-16 10:57:24 UTC using RuboCop version 1.60.0. +# on 2024-02-27 14:27:59 UTC using RuboCop version 1.60.2. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 64 -# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. +# Offense count: 27 +# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max. Metrics/AbcSize: - Max: 92 + Enabled: false -# Offense count: 16 +# Offense count: 17 # Configuration parameters: AllowedMethods, AllowedPatterns. Metrics/CyclomaticComplexity: Max: 12 -# Offense count: 13 +# Offense count: 14 # Configuration parameters: AllowedMethods, AllowedPatterns. Metrics/PerceivedComplexity: Max: 12 @@ -84,7 +84,7 @@ Style/RegexpLiteral: Style/SlicingWithRange: Enabled: false -# Offense count: 82 +# Offense count: 83 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: Mode. Style/StringConcatenation: diff --git a/CHANGELOG.md b/CHANGELOG.md index e209a2da9..f13bb2f18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,7 +60,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Fix potential busy wait when retries and/or next_adds_job is enabled (@gs-kamnas) - Reverting PR #2498 as it broke old procurve models (2510G, 2610, 2824). Fixes #2833, #2871 (@robertcheramy) - Fixed regexp used to remove secrets in nxos model. Fixes #3080 (@desnoe) -- apc_aos: fixed ftp and scp. Fixes #1802 (@robertcheramy) ## [0.29.1 - 2023-04-24] diff --git a/docs/Model-Notes/APC_AOS.md b/docs/Model-Notes/APC_AOS.md new file mode 100644 index 000000000..749a1a419 --- /dev/null +++ b/docs/Model-Notes/APC_AOS.md @@ -0,0 +1,52 @@ +# APC AOS Configuration + +Currently, the configuration of APC Network Management Cards can be downloaded with FTP only. + +A download of the configuration with SCP is [work in progress](https://github.com/ytti/oxidized/issues/1802). +As the APC has an unusual behavior (the connection is closed without an exit-status), this has to be +[fixed](https://github.com/net-ssh/net-scp/pull/71) upstream in [Net::SCP](https://github.com/net-ssh/net-scp). +As soon as there is a release of Net::SCP supporting the behavior of APC OS, we will activate SCP in oxidized. + +## Can I collect more information than just the configuration? +APC OS does not have the ability to show the config.ini within an SSH-session. As oxidized can only get the +configuration with one input type at a time, it is not possible to fetch config.ini via FTP/SCP and get the output of +some commands via SSH at the same time. + +A ticket has been opened with APC support in order to support "cat config.ini" within an SSH-session, but +the chances it will be supported at some time are not very good, and older versions will still not support it. + +## How do I activate FTP input? +In order to download the configuration with FTP (and in the future with SCP), you have to activate it as an +input in the oxidized configuration. If you do not activate the input, oxidized will fail for the node with +a rather unspecific error (`WARN -- : /apc status fail, retry attempt 1`). + +The configuration can be done either globally or only for the model apc_aos. + +The global configuration would look like this. Note that Oxidized will try every input type in the given order +until it succeeds, or it will report a failure. +```yaml +input: + default: ssh, ftp, scp +``` + +Configuration for activating the FTP input for apc_aos only: +```yaml +input: + default: ssh +models: + apc_aos: + input: ftp +``` + +You can also set specific username and password for apc_aos only: +```yaml +username: default-user +password: default-password +input: + default: ssh +models: + apc_aos: + username: apc-user + password: apc-password + input: ftp +``` diff --git a/docs/Supported-OS-Types.md b/docs/Supported-OS-Types.md index a3e68409c..23eb47cfd 100644 --- a/docs/Supported-OS-Types.md +++ b/docs/Supported-OS-Types.md @@ -16,7 +16,7 @@ |Allied Telesis |Alliedware Plus |[awplus](/lib/oxidized/model/awplus.rb) | |AT-8000S, AT-8000GS series |[powerconnect](/lib/oxidized/model/powerconnect.rb) |Alvarion |BreezeACCESS |[alvarion](/lib/oxidized/model/alvarion.rb) -|APC |AOS |[apc_aos](/lib/oxidized/model/apc_aos.rb) |@robertcheramy +|APC |AOS |[apc_aos](/lib/oxidized/model/apc_aos.rb) |@robertcheramy |[APC AOS](Model-Notes/APC_AOS.md) |Arbor Networks |ArbOS |[arbos](/lib/oxidized/model/arbos.rb) | |[ArbOS](Model-Notes/ArbOS.md) |Arista |EOS |[eos](/lib/oxidized/model/eos.rb) | |[EOS](Model-Notes/EOS.md) |Arris |C4CMTS |[c4cmts](/lib/oxidized/model/c4cmts.rb) diff --git a/lib/oxidized/model/apc_aos.rb b/lib/oxidized/model/apc_aos.rb index e2e2cb638..9b4a7e0a2 100644 --- a/lib/oxidized/model/apc_aos.rb +++ b/lib/oxidized/model/apc_aos.rb @@ -6,6 +6,6 @@ class Apc_aos < Oxidized::Model # rubocop:disable Naming/ClassAndModuleCamelCase cfg end - cfg :ftp, :scp do + cfg :ftp do end end diff --git a/spec/model/apc_aos_spec.rb b/spec/model/apc_aos_spec.rb index 5427cf3dc..2ed1693b8 100644 --- a/spec/model/apc_aos_spec.rb +++ b/spec/model/apc_aos_spec.rb @@ -34,6 +34,7 @@ end it "fetches the configuration with scp" do + skip "Work in Progress, see issue #1802" @node = Oxidized::Node.new(name: 'example.com', input: 'scp', output: 'file',