train-juniper 0.6.2 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TrainPlugins
4
+ module Juniper
5
+ # Helper methods for safely handling environment variables
6
+ module Environment
7
+ # Helper method to safely get environment variable value
8
+ # Returns nil if env var is not set or is empty string
9
+ # @param key [String] The environment variable name
10
+ # @return [String, nil] The value or nil if not set/empty
11
+ def env_value(key)
12
+ value = ENV.fetch(key, nil)
13
+ return nil if value.nil? || value.empty?
14
+
15
+ value
16
+ end
17
+
18
+ # Helper method to get environment variable as integer
19
+ # Returns nil if env var is not set, empty, or not a valid integer
20
+ # @param key [String] The environment variable name
21
+ # @return [Integer, nil] The integer value or nil if not valid
22
+ def env_int(key)
23
+ value = env_value(key)
24
+ return nil unless value
25
+
26
+ value.to_i
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TrainPlugins
4
+ module Juniper
5
+ # Provides consistent logging patterns across the plugin
6
+ module Logging
7
+ # Log a command execution attempt
8
+ # @param cmd [String] The command being executed
9
+ def log_command(cmd)
10
+ @logger.debug("Executing command: #{cmd}")
11
+ end
12
+
13
+ # Log a connection attempt
14
+ # @param target [String] The host/target being connected to
15
+ # @param port [Integer] The port number
16
+ def log_connection_attempt(target, port = nil)
17
+ if port
18
+ @logger.debug("Attempting connection to #{target}:#{port}")
19
+ else
20
+ @logger.debug("Attempting connection to #{target}")
21
+ end
22
+ end
23
+
24
+ # Log an error with consistent formatting
25
+ # @param error [Exception, String] The error to log
26
+ # @param context [String] Additional context for the error
27
+ def log_error(error, context = nil)
28
+ message = if error.is_a?(Exception)
29
+ "#{error.class}: #{error.message}"
30
+ else
31
+ error.to_s
32
+ end
33
+
34
+ if context
35
+ @logger.error("#{context}: #{message}")
36
+ else
37
+ @logger.error(message)
38
+ end
39
+ end
40
+
41
+ # Log successful connection
42
+ # @param target [String] The host that was connected to
43
+ def log_connection_success(target)
44
+ @logger.info("Successfully connected to #{target}")
45
+ end
46
+
47
+ # Log SSH session details (redacting sensitive info)
48
+ # @param options [Hash] SSH options hash
49
+ def log_ssh_options(options)
50
+ safe_options = options.dup
51
+ safe_options[:password] = '[REDACTED]' if safe_options[:password]
52
+ safe_options[:passphrase] = '[REDACTED]' if safe_options[:passphrase]
53
+ safe_options[:keys] = safe_options[:keys]&.map { |k| File.basename(k) } if safe_options[:keys]
54
+
55
+ @logger.debug("SSH options: #{safe_options.inspect}")
56
+ end
57
+
58
+ # Log platform detection results
59
+ # @param platform_name [String] Detected platform name
60
+ # @param version [String] Detected version
61
+ def log_platform_detection(platform_name, version)
62
+ @logger.info("Platform detected: #{platform_name} #{version}")
63
+ end
64
+
65
+ # Log bastion connection attempt
66
+ # @param bastion_host [String] The bastion host
67
+ def log_bastion_connection(bastion_host)
68
+ @logger.debug("Connecting through bastion host: #{bastion_host}")
69
+ end
70
+
71
+ # Log mock mode activation
72
+ def log_mock_mode
73
+ @logger.info('Running in mock mode - no real device connection')
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Mock responses for Juniper device commands
4
+ # This module contains all mock data used in testing and mock mode
5
+ module TrainPlugins
6
+ module Juniper
7
+ # Mock responses for common JunOS commands
8
+ # Used when running in mock mode for testing without real devices
9
+ module MockResponses
10
+ # Configuration of mock responses
11
+ # Maps command patterns to response methods or strings
12
+ RESPONSES = {
13
+ 'show version' => :mock_show_version_output,
14
+ 'show chassis hardware' => :mock_chassis_output,
15
+ 'show configuration' => "interfaces {\n ge-0/0/0 {\n unit 0;\n }\n}",
16
+ 'show route' => "inet.0: 5 destinations, 5 routes\n0.0.0.0/0 *[Static/5] 00:00:01\n",
17
+ 'show system information' => "Hardware: SRX240H2\nOS: JUNOS 12.1X47-D15.4\n",
18
+ 'show interfaces' => "Physical interface: ge-0/0/0, Enabled, Physical link is Up\n"
19
+ }.freeze
20
+
21
+ # Mock JunOS version output for testing
22
+ # @return [String] mock output for 'show version' command
23
+ def self.mock_show_version_output
24
+ <<~OUTPUT
25
+ Hostname: lab-srx
26
+ Model: SRX240H2
27
+ Junos: 12.1X47-D15.4
28
+ JUNOS Software Release [12.1X47-D15.4]
29
+ OUTPUT
30
+ end
31
+
32
+ # Mock chassis hardware output
33
+ # @return [String] mock output for 'show chassis hardware' command
34
+ def self.mock_chassis_output
35
+ <<~OUTPUT
36
+ Hardware inventory:
37
+ Item Version Part number Serial number Description
38
+ Chassis JN123456 SRX240H2
39
+ OUTPUT
40
+ end
41
+
42
+ # Get mock response for a command
43
+ # @param cmd [String] the command to get response for
44
+ # @return [Array<String, Integer>] tuple of [output, exit_status]
45
+ def self.response_for(cmd)
46
+ response = RESPONSES.find { |pattern, _| cmd.match?(/#{pattern}/) }
47
+
48
+ if response
49
+ output = response[1].is_a?(Symbol) ? send(response[1]) : response[1]
50
+ [output, 0]
51
+ else
52
+ ["% Unknown command: #{cmd}", 1]
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -5,16 +5,19 @@
5
5
 
6
6
  module TrainPlugins::Juniper
7
7
  # Platform detection mixin for Juniper network devices
8
+ # @note This module is mixed into the Connection class to provide platform detection
8
9
  module Platform
9
10
  # Platform name constant for consistency
10
11
  PLATFORM_NAME = 'juniper'
11
12
 
12
13
  # Platform detection for Juniper network devices
13
- #
14
- # For dedicated transport plugins, we use force_platform! to bypass
15
- # Train's automatic platform detection, which might run commands before
16
- # the connection is ready. This is the standard pattern used by official
17
- # Train plugins like train-k8s-container.
14
+ # @return [Train::Platform] Platform object with JunOS details
15
+ # @note Uses force_platform! to bypass Train's automatic detection
16
+ # @example
17
+ # platform = connection.platform
18
+ # platform.name #=> "juniper"
19
+ # platform.release #=> "12.1X47-D15.4"
20
+ # platform.arch #=> "x86_64"
18
21
  def platform
19
22
  # Return cached platform if already computed
20
23
  return @platform if defined?(@platform)
@@ -39,73 +42,75 @@ module TrainPlugins::Juniper
39
42
  platform_obj = force_platform!(PLATFORM_NAME, platform_details)
40
43
  logger&.debug("Set platform data: #{platform_obj.platform}")
41
44
 
45
+ # Log platform detection results if logging helpers available
46
+ log_platform_detection(PLATFORM_NAME, device_version) if respond_to?(:log_platform_detection)
47
+
42
48
  # Cache the platform object to prevent repeated calls
43
49
  @platform = platform_obj
44
50
  end
45
51
 
46
52
  private
47
53
 
48
- # Detect JunOS version from device output
49
- # This runs safely after the connection is established
50
- def detect_junos_version
51
- # Return cached version if already detected
52
- return @detected_junos_version if defined?(@detected_junos_version)
54
+ # Generic detection helper for version and architecture
55
+ # @param attribute_name [String] Name of the attribute to detect
56
+ # @param command [String] Command to run (default: 'show version')
57
+ # @yield [String] Block that extracts the attribute from command output
58
+ # @return [String, nil] Detected attribute value or nil
59
+ def detect_attribute(attribute_name, command = 'show version', &extraction_block)
60
+ cache_var = "@detected_#{attribute_name}"
61
+ return instance_variable_get(cache_var) if instance_variable_defined?(cache_var)
53
62
 
54
- # Only try version detection if we have an active connection
55
63
  unless respond_to?(:run_command_via_connection)
56
64
  logger&.debug('run_command_via_connection not available yet')
57
- return @detected_junos_version = nil
65
+ return instance_variable_set(cache_var, nil)
58
66
  end
59
67
 
60
68
  logger&.debug("Mock mode: #{@options&.dig(:mock)}, Connected: #{connected?}")
61
69
 
62
70
  begin
63
- # Check if connection is ready before running commands
64
- unless connected?
65
- logger&.debug('Not connected, skipping version detection')
66
- return @detected_junos_version = nil
67
- end
68
-
69
- # Execute 'show version' command to get JunOS information
70
- logger&.debug("Running 'show version' command")
71
- result = run_command_via_connection('show version')
71
+ return instance_variable_set(cache_var, nil) unless connected?
72
72
 
73
- unless result&.exit_status&.zero?
74
- logger&.debug("Command failed with exit status: #{result&.exit_status}")
75
- return @detected_junos_version = nil
76
- end
73
+ # Reuse cached command result if available
74
+ result = @cached_show_version_result || run_command_via_connection(command)
75
+ @cached_show_version_result ||= result if command == 'show version' && result&.exit_status&.zero?
77
76
 
78
- # Cache the result for architecture detection to avoid duplicate calls
79
- @cached_show_version_result = result
77
+ return instance_variable_set(cache_var, nil) unless result&.exit_status&.zero?
80
78
 
81
- # Parse JunOS version from output using multiple patterns
82
- version = extract_version_from_output(result.stdout)
79
+ value = extraction_block.call(result.stdout)
83
80
 
84
- if version
85
- logger&.debug("Detected JunOS version: #{version}")
86
- @detected_junos_version = version
81
+ if value
82
+ logger&.debug("Detected #{attribute_name}: #{value}")
83
+ instance_variable_set(cache_var, value)
87
84
  else
88
- logger&.debug("Could not parse JunOS version from: #{result.stdout[0..100]}")
89
- @detected_junos_version = nil
85
+ logger&.debug("Could not parse #{attribute_name} from: #{result.stdout[0..100]}")
86
+ instance_variable_set(cache_var, nil)
90
87
  end
91
88
  rescue StandardError => e
92
- # If version detection fails, log and return nil
93
- logger&.debug("JunOS version detection failed: #{e.message}")
94
- @detected_junos_version = nil
89
+ logger&.debug("#{attribute_name} detection failed: #{e.message}")
90
+ instance_variable_set(cache_var, nil)
95
91
  end
96
92
  end
97
93
 
94
+ # Detect JunOS version from device output
95
+ # @return [String, nil] JunOS version string or nil if not detected
96
+ # @note This runs safely after the connection is established
97
+ def detect_junos_version
98
+ detect_attribute('junos_version') { |output| extract_version_from_output(output) }
99
+ end
100
+
98
101
  # Extract version string from JunOS show version output
102
+ # @param output [String] Raw output from 'show version' command
103
+ # @return [String, nil] Extracted version string or nil
99
104
  def extract_version_from_output(output)
100
105
  return nil if output.nil? || output.empty?
101
106
 
102
107
  # Try multiple JunOS version patterns
103
108
  patterns = [
104
- /Junos:\s+([\d\w\.-]+)/, # "Junos: 12.1X47-D15.4"
105
- /JUNOS Software Release \[([\d\w\.-]+)\]/, # "JUNOS Software Release [12.1X47-D15.4]"
106
- /junos version ([\d\w\.-]+)/i, # "junos version 21.4R3"
107
- /Model: \S+, JUNOS Base OS boot \[([\d\w\.-]+)\]/, # Some hardware variants
108
- /([\d]+\.[\d]+[\w\.-]*)/ # Generic version pattern
109
+ /Junos:\s+([\w\d.-]+)/, # "Junos: 12.1X47-D15.4"
110
+ /JUNOS Software Release \[([\w\d.-]+)\]/, # "JUNOS Software Release [12.1X47-D15.4]"
111
+ /junos version ([\w\d.-]+)/i, # "junos version 21.4R3"
112
+ /Model: \S+, JUNOS Base OS boot \[([\w\d.-]+)\]/, # Some hardware variants
113
+ /([\d]+\.[\d]+[\w.-]*)/ # Generic version pattern
109
114
  ]
110
115
 
111
116
  patterns.each do |pattern|
@@ -117,56 +122,22 @@ module TrainPlugins::Juniper
117
122
  end
118
123
 
119
124
  # Detect JunOS architecture from device output
120
- # This runs safely after the connection is established
125
+ # @return [String, nil] Architecture string or nil if not detected
126
+ # @note This runs safely after the connection is established
121
127
  def detect_junos_architecture
122
- # Return cached architecture if already detected
123
- return @detected_junos_architecture if defined?(@detected_junos_architecture)
124
-
125
- # Only try architecture detection if we have an active connection
126
- return @detected_junos_architecture = nil unless respond_to?(:run_command_via_connection)
127
-
128
- begin
129
- # Check if connection is ready before running commands
130
- return @detected_junos_architecture = nil unless connected?
131
-
132
- # Reuse version detection result to avoid duplicate 'show version' calls
133
- # Both version and architecture come from the same command output
134
- if defined?(@detected_junos_version) && @detected_junos_version
135
- # We already have the output from version detection, parse architecture from it
136
- result = @cached_show_version_result
137
- else
138
- # Execute 'show version' command and cache the result
139
- result = run_command_via_connection('show version')
140
- @cached_show_version_result = result if result&.exit_status&.zero?
141
- end
142
-
143
- return @detected_junos_architecture = nil unless result&.exit_status&.zero?
144
-
145
- # Parse architecture from output using multiple patterns
146
- arch = extract_architecture_from_output(result.stdout)
147
-
148
- if arch
149
- logger&.debug("Detected JunOS architecture: #{arch}")
150
- @detected_junos_architecture = arch
151
- else
152
- logger&.debug("Could not parse JunOS architecture from: #{result.stdout[0..100]}")
153
- @detected_junos_architecture = nil
154
- end
155
- rescue StandardError => e
156
- # If architecture detection fails, log and return nil
157
- logger&.debug("JunOS architecture detection failed: #{e.message}")
158
- @detected_junos_architecture = nil
159
- end
128
+ detect_attribute('junos_architecture') { |output| extract_architecture_from_output(output) }
160
129
  end
161
130
 
162
131
  # Extract architecture string from JunOS show version output
132
+ # @param output [String] Raw output from 'show version' command
133
+ # @return [String, nil] Architecture string (x86_64, arm64, etc.) or nil
163
134
  def extract_architecture_from_output(output)
164
135
  return nil if output.nil? || output.empty?
165
136
 
166
137
  # Try multiple JunOS architecture patterns
167
138
  patterns = [
168
139
  /Model:\s+(\S+)/, # "Model: SRX240H2" -> extract model as arch indicator
169
- /Junos:\s+[\d\w\.-]+\s+built\s+[\d-]+\s+[\d:]+\s+by\s+builder\s+on\s+(\S+)/, # Build architecture
140
+ /Junos:\s+[\w\d.-]+\s+built\s+[\d-]+\s+[\d:]+\s+by\s+builder\s+on\s+(\S+)/, # Build architecture
170
141
  /JUNOS.*\[([\w-]+)\]/, # JUNOS package architecture
171
142
  /Architecture:\s+(\S+)/i, # Direct architecture line
172
143
  /Platform:\s+(\S+)/i, # Platform designation
@@ -7,6 +7,13 @@ require 'train-juniper/connection'
7
7
 
8
8
  module TrainPlugins
9
9
  module Juniper
10
+ # Transport plugin for Juniper JunOS devices
11
+ # @example Create a connection
12
+ # conn = Train.create('juniper',
13
+ # host: '192.168.1.1',
14
+ # user: 'admin',
15
+ # password: 'secret'
16
+ # )
10
17
  class Transport < Train.plugin(1)
11
18
  name 'juniper'
12
19
 
@@ -20,7 +27,7 @@ module TrainPlugins
20
27
 
21
28
  # Proxy/Bastion host support (Train standard options)
22
29
  option :bastion_host, default: nil
23
- option :bastion_user, default: nil # Let connection handle env vars and defaults
30
+ option :bastion_user, default: nil # Falls back to main user if not specified
24
31
  option :bastion_port, default: 22
25
32
  option :bastion_password, default: nil # Separate password for bastion authentication
26
33
  option :proxy_command, default: nil
@@ -45,6 +52,11 @@ module TrainPlugins
45
52
  option :disable_complete_on_space, default: false
46
53
 
47
54
  # Create and return a connection to a Juniper device
55
+ # @param _instance_opts [Hash] Instance options (unused, for compatibility)
56
+ # @return [TrainPlugins::Juniper::Connection] Cached connection instance
57
+ # @example
58
+ # transport = TrainPlugins::Juniper::Transport.new(options)
59
+ # conn = transport.connection
48
60
  def connection(_instance_opts = nil)
49
61
  # Cache the connection instance for reuse
50
62
  # @options contains parsed connection details from train URI
@@ -7,6 +7,7 @@
7
7
 
8
8
  module TrainPlugins
9
9
  module Juniper
10
- VERSION = '0.6.2'
10
+ # Version number of the train-juniper plugin
11
+ VERSION = '0.7.1'
11
12
  end
12
13
  end
@@ -70,4 +70,20 @@ Gem::Specification.new do |spec|
70
70
  # FFI dependency - required by train-core
71
71
  # Match InSpec 7's FFI version range for compatibility
72
72
  spec.add_dependency 'ffi', '>= 1.15.5', '< 1.17.0'
73
+
74
+ # Development dependencies
75
+ spec.add_development_dependency 'bundler', '~> 2.0'
76
+ spec.add_development_dependency 'byebug', '~> 11.1'
77
+ spec.add_development_dependency 'minitest', '~> 5.0'
78
+ spec.add_development_dependency 'mocha', '~> 2.0'
79
+ spec.add_development_dependency 'rake', '~> 13.0'
80
+ spec.add_development_dependency 'rubocop', '~> 1.0'
81
+ spec.add_development_dependency 'simplecov', '~> 0.22'
82
+ spec.add_development_dependency 'yard', '~> 0.9'
83
+
84
+ # Security testing
85
+ spec.add_development_dependency 'bundler-audit', '~> 0.9'
86
+
87
+ # Documentation generation
88
+ spec.add_development_dependency 'redcarpet', '~> 3.5'
73
89
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: train-juniper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - MITRE Corporation
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-06-19 00:00:00.000000000 Z
11
+ date: 2025-06-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: train-core
@@ -64,6 +64,146 @@ dependencies:
64
64
  - - "<"
65
65
  - !ruby/object:Gem::Version
66
66
  version: 1.17.0
67
+ - !ruby/object:Gem::Dependency
68
+ name: bundler
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '2.0'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '2.0'
81
+ - !ruby/object:Gem::Dependency
82
+ name: byebug
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '11.1'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '11.1'
95
+ - !ruby/object:Gem::Dependency
96
+ name: minitest
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '5.0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '5.0'
109
+ - !ruby/object:Gem::Dependency
110
+ name: mocha
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '2.0'
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - "~>"
121
+ - !ruby/object:Gem::Version
122
+ version: '2.0'
123
+ - !ruby/object:Gem::Dependency
124
+ name: rake
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: '13.0'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - "~>"
135
+ - !ruby/object:Gem::Version
136
+ version: '13.0'
137
+ - !ruby/object:Gem::Dependency
138
+ name: rubocop
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: '1.0'
144
+ type: :development
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: '1.0'
151
+ - !ruby/object:Gem::Dependency
152
+ name: simplecov
153
+ requirement: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - "~>"
156
+ - !ruby/object:Gem::Version
157
+ version: '0.22'
158
+ type: :development
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - "~>"
163
+ - !ruby/object:Gem::Version
164
+ version: '0.22'
165
+ - !ruby/object:Gem::Dependency
166
+ name: yard
167
+ requirement: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - "~>"
170
+ - !ruby/object:Gem::Version
171
+ version: '0.9'
172
+ type: :development
173
+ prerelease: false
174
+ version_requirements: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - "~>"
177
+ - !ruby/object:Gem::Version
178
+ version: '0.9'
179
+ - !ruby/object:Gem::Dependency
180
+ name: bundler-audit
181
+ requirement: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - "~>"
184
+ - !ruby/object:Gem::Version
185
+ version: '0.9'
186
+ type: :development
187
+ prerelease: false
188
+ version_requirements: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - "~>"
191
+ - !ruby/object:Gem::Version
192
+ version: '0.9'
193
+ - !ruby/object:Gem::Dependency
194
+ name: redcarpet
195
+ requirement: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - "~>"
198
+ - !ruby/object:Gem::Version
199
+ version: '3.5'
200
+ type: :development
201
+ prerelease: false
202
+ version_requirements: !ruby/object:Gem::Requirement
203
+ requirements:
204
+ - - "~>"
205
+ - !ruby/object:Gem::Version
206
+ version: '3.5'
67
207
  description: Provides SSH connectivity to Juniper Networks devices running JunOS for
68
208
  InSpec compliance testing and infrastructure inspection. Supports platform detection,
69
209
  command execution, and configuration file access.
@@ -84,6 +224,16 @@ files:
84
224
  - SECURITY.md
85
225
  - lib/train-juniper.rb
86
226
  - lib/train-juniper/connection.rb
227
+ - lib/train-juniper/connection/bastion_proxy.rb
228
+ - lib/train-juniper/connection/command_executor.rb
229
+ - lib/train-juniper/connection/error_handling.rb
230
+ - lib/train-juniper/connection/ssh_session.rb
231
+ - lib/train-juniper/connection/validation.rb
232
+ - lib/train-juniper/constants.rb
233
+ - lib/train-juniper/file_abstraction/juniper_file.rb
234
+ - lib/train-juniper/helpers/environment.rb
235
+ - lib/train-juniper/helpers/logging.rb
236
+ - lib/train-juniper/helpers/mock_responses.rb
87
237
  - lib/train-juniper/platform.rb
88
238
  - lib/train-juniper/transport.rb
89
239
  - lib/train-juniper/version.rb