Class: TrainPlugins::Juniper::Connection

Inherits:
Train::Plugins::Transport::BaseConnection
  • Object
show all
Includes:
BastionProxy, CommandExecutor, Environment, ErrorHandling, Logging, Platform, SSHSession, Validation
Defined in:
lib/train-juniper/connection.rb

Overview

Main connection class for Juniper devices

Constant Summary collapse

CommandResult =

Alias for Train CommandResult for backward compatibility

Train::Extras::CommandResult
ENV_CONFIG =

Configuration mapping for environment variables

{
  host: { env: 'JUNIPER_HOST' },
  user: { env: 'JUNIPER_USER' },
  password: { env: 'JUNIPER_PASSWORD' },
  port: { env: 'JUNIPER_PORT', type: :int, default: Constants::DEFAULT_SSH_PORT },
  timeout: { env: 'JUNIPER_TIMEOUT', type: :int, default: 30 },
  bastion_host: { env: 'JUNIPER_BASTION_HOST' },
  bastion_user: { env: 'JUNIPER_BASTION_USER' },
  bastion_port: { env: 'JUNIPER_BASTION_PORT', type: :int, default: Constants::DEFAULT_SSH_PORT },
  bastion_password: { env: 'JUNIPER_BASTION_PASSWORD' },
  proxy_command: { env: 'JUNIPER_PROXY_COMMAND' }
}.freeze

Constants included from SSHSession

SSHSession::SSH_DEFAULTS, SSHSession::SSH_OPTION_MAPPING

Constants included from ErrorHandling

ErrorHandling::JUNOS_ERRORS, ErrorHandling::JUNOS_ERROR_PATTERNS

Constants included from CommandExecutor

TrainPlugins::Juniper::CommandExecutor::DANGEROUS_COMMAND_PATTERNS

Constants included from Platform

Platform::PLATFORM_NAME

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Logging

#log_bastion_connection, #log_command, #log_connection_attempt, #log_connection_success, #log_error, #log_mock_mode, #log_platform_detection, #log_ssh_options

Methods included from BastionProxy

#configure_bastion_proxy

Methods included from SshAskpass

#create_ssh_askpass_script, #setup_bastion_password_auth

Methods included from WindowsProxy

#build_plink_proxy_command, #plink_available?

Methods included from SSHSession

#connect, #connected?, #mock?, #test_and_configure_session

Methods included from ErrorHandling

#bastion_auth_error?, #bastion_error_message, #handle_connection_error, #junos_error?

Methods included from CommandExecutor

#run_command_via_connection

Methods included from Validation

#validate_bastion_port!, #validate_connection_options!, #validate_option_types!, #validate_port!, #validate_required_options!, #validate_timeout!

Methods included from Environment

#env_int, #env_value

Methods included from Platform

#platform

Constructor Details

#initialize(options) ⇒ Connection

Initialize a new Juniper connection

Options Hash (options):

  • :host (String)

    The hostname or IP address of the Juniper device

  • :user (String)

    The username for authentication

  • :password (String)

    The password for authentication (optional if using key_files)

  • :port (Integer)

    The SSH port (default: 22)

  • :timeout (Integer)

    Connection timeout in seconds (default: 30)

  • :bastion_host (String)

    Jump/bastion host for connection

  • :proxy_command (String)

    SSH proxy command

  • :logger (Logger)

    Custom logger instance

  • :mock (Boolean)

    Enable mock mode for testing



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/train-juniper/connection.rb', line 83

def initialize(options)
  # Configure SSH connection options for Juniper devices
  # Support environment variables for authentication (following train-vsphere pattern)
  @options = options.dup

  # Apply environment variable configuration using DRY approach
  ENV_CONFIG.each do |key, config|
    # Skip if option already has a value from command line
    next if @options[key]

    # Get value from environment
    env_val = config[:type] == :int ? env_int(config[:env]) : env_value(config[:env])

    # Only apply env value if it exists, otherwise use default (but not for nil CLI values)
    if env_val
      @options[key] = env_val
    elsif !@options.key?(key) && config[:default]
      @options[key] = config[:default]
    end
  end

  @options[:keepalive] = true
  @options[:keepalive_interval] = Constants::SSH_KEEPALIVE_INTERVAL

  # Setup logger
  @logger = @options[:logger] || Logger.new(STDOUT, level: Constants::DEFAULT_LOG_LEVEL)

  # JunOS CLI prompt patterns
  @cli_prompt = /[%>$#]\s*$/
  @config_prompt = /[%#]\s*$/

  # Log connection info safely
  log_connection_info

  # Validate all connection options
  validate_connection_options!

  super(@options)

  # Establish SSH connection to Juniper device (unless in mock mode or skip_connect)
  if @options[:mock]
    log_mock_mode
  elsif !@options[:skip_connect]
    @logger.debug('Attempting to connect to Juniper device...')
    connect
  end
end

Instance Attribute Details

#ssh_sessionObject (readonly)

Returns the value of attribute ssh_session.



56
57
58
# File 'lib/train-juniper/connection.rb', line 56

def ssh_session
  @ssh_session
end

Instance Method Details

#download(remotes, local) ⇒ Object

Note:

Use run_command() to retrieve configuration data instead

Download files from Juniper device (not supported)

Raises:

  • (NotImplementedError)

    Always raises as downloads are not supported



171
172
173
# File 'lib/train-juniper/connection.rb', line 171

def download(remotes, local)
  raise NotImplementedError, Constants::DOWNLOAD_NOT_SUPPORTED
end

#file_via_connection(path) ⇒ JuniperFile

Access Juniper configuration and operational data as pseudo-files

Examples:

Access interface configuration

file = connection.file('/config/interfaces')
puts file.content

Access operational data

file = connection.file('/operational/interfaces')
puts file.content


151
152
153
154
155
# File 'lib/train-juniper/connection.rb', line 151

def file_via_connection(path)
  # For Juniper devices, "files" are typically configuration sections
  # or operational command outputs rather than traditional filesystem paths
  JuniperFile.new(self, path)
end

#healthy?Boolean

Check connection health

Examples:

if connection.healthy?
  puts "Connection is healthy"
end


181
182
183
184
185
186
187
188
# File 'lib/train-juniper/connection.rb', line 181

def healthy?
  return false unless connected?

  result = run_command_via_connection('show version')
  result.exit_status.zero?
rescue StandardError
  false
end

#inspectString

Secure inspect method that uses to_s



138
139
140
# File 'lib/train-juniper/connection.rb', line 138

def inspect
  to_s
end

#to_sObject

Secure string representation (never expose credentials)



132
133
134
# File 'lib/train-juniper/connection.rb', line 132

def to_s
  "#<#{self.class.name}:0x#{object_id.to_s(16)} @host=#{@options[:host]} @user=#{@options[:user]}>"
end

#unique_identifierString

Note:

Tries to get Juniper device serial number, falls back to hostname

Optional method for better UUID generation using device-specific identifiers



213
214
215
216
217
218
219
220
# File 'lib/train-juniper/connection.rb', line 213

def unique_identifier
  # Don't attempt device detection in mock mode
  return @options[:host] if @options[:mock]

  # Use the platform module's serial detection which follows DRY principle
  serial = detect_junos_serial
  serial || @options[:host]
end

#upload(locals, remote) ⇒ Object

Note:

Network devices use command-based configuration instead of file uploads

Upload files to Juniper device (not supported)

Raises:

  • (NotImplementedError)

    Always raises as uploads are not supported



162
163
164
# File 'lib/train-juniper/connection.rb', line 162

def upload(locals, remote)
  raise NotImplementedError, Constants::UPLOAD_NOT_SUPPORTED
end

#uriString

Required by Train framework for node identification

Examples:

Direct connection

"juniper://[email protected]:22"

Bastion connection

"juniper://[email protected]:[email protected]:2222"


196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/train-juniper/connection.rb', line 196

def uri
  base_uri = "juniper://#{@options[:user]}@#{@options[:host]}:#{@options[:port]}"

  # Include bastion information if connecting through a jump host
  if @options[:bastion_host]
    bastion_user = @options[:bastion_user] || @options[:user]
    bastion_port = @options[:bastion_port] || 22
    bastion_info = "via=#{bastion_user}@#{@options[:bastion_host]}:#{bastion_port}"
    "#{base_uri}?#{bastion_info}"
  else
    base_uri
  end
end