Connect to Junos Devices Using Junos PyEZ
SUMMARY Connect to a Junos device or to an attached console server using different connection methods and protocols in a Junos PyEZ application.
Junos PyEZ is a microframework for Python that enables you to manage Junos devices. Junos PyEZ
models each device as an instance of the jnpr.junos.device.Device class. The Device
class enables
you to connect to a Junos device using a serial console connection, telnet, or by
establishing a NETCONF session over SSH. In addition, Junos PyEZ also supports
connecting to the device through a telnet or SSH connection to a console server. A
console server, also known as a terminal server, is a specialized device that provides a
network connection to a device’s out-of-band management console port.
This topic provides an overview of the connection methods supported by Junos PyEZ and explains how to use the different methods to connect to a Junos device. The Junos PyEZ examples use various authentication methods, but for detailed information about authenticating a user, see Authenticate Junos PyEZ Users.
Connection Methods Overview
Junos PyEZ enables you to connect to a Junos device using a serial console connection, telnet, or a NETCONF session over SSH. You must use a serial console connection when you are physically connected to the CONSOLE port on a device. You can use telnet or SSH to connect to the device’s management interface or to a console server that is connected to the device’s CONSOLE port. In addition, Junos PyEZ supports outbound SSH connections, in which the Junos device initiates the connection with the client management application.
New or zeroized devices that have factory default configurations require access through a console connection. Thus, you can use Junos PyEZ to initially configure a device that is not yet configured for remote access by using either a serial console connection when you are directly connected to the device or by using telnet or SSH through a console server that is connected to the device.
By default, Junos PyEZ uses SSH to connect to a device . To
specify a different connection type, you must include the mode
parameter in the Device
argument list. To telnet to a device, include the mode='telnet'
argument. To connect to a device using a serial console connection,
include the mode='serial'
argument. Table 1 summarizes the Junos
PyEZ connection methods, their default values for certain parameters,
any required Junos OS configuration, and the Junos PyEZ release in
which support for that connection method was first introduced.
Connection Mode |
Value of |
Default Port |
Required Junos OS Configuration |
First Supported Junos PyEZ Release |
---|---|---|---|---|
NETCONF over SSH (default) |
– |
830 |
[edit system services] netconf { ssh; } |
1.0.0 |
Serial console connection |
serial |
/dev/ttyUSB0 |
– |
2.0.0 (*nix) 2.4.0 (Windows) |
Telnet to Junos device |
telnet |
23 |
[edit system services] telnet; |
2.0.0 |
Telnet through a console server |
telnet |
23 |
– |
2.0.0 |
SSH through a console server |
– |
22 |
– |
2.2.0 |
Outbound SSH |
– |
– |
[edit system services] outbound-ssh { ... } |
2.2.0 |
Before you can access a device’s management interface using telnet or NETCONF over SSH, you must
first enable the appropriate service at the [edit system
services]
hierarchy level. For more information, see Set Up Junos PyEZ Managed Nodes. Because
telnet uses clear-text passwords (therefore creating a potential security
vulnerability), we recommend that you use SSH.
It is the user's responsibility to obtain the username and password authentication credentials in a secure manner appropriate for their environment. It is best practice to prompt for these authentication credentials during each invocation of the script, rather than storing the credentials in an unencrypted format.
Junos PyEZ supports using context managers (with
... as
syntax) for all connection
methods. When you use a context manager, Junos PyEZ automatically
calls the open()
and close()
methods to connect to and disconnect from the device. If you do
not use a context manager, you must explicitly call the open()
and close()
methods
in your application. We recommend that you use a context manager for
console connections, because the context manager automatically handles
closing the connection, and failure to close the connection can lead
to unpredictable results.
Understanding Junos PyEZ Connection Properties
When you connect to a Junos device, Junos PyEZ stores information about the current connection as
properties of the Device
instance. Table 2 outlines the
available connection properties.
Property |
Description |
---|---|
|
Boolean specifying the current state of the connection.
Returns |
|
String specifying the hostname of the device to which the application is connected. |
|
Boolean returning |
|
Integer or string specifying the port used for the connection. |
|
String specifying the Routing Engine name to which the application is connected. |
|
Integer specifying the RPC timeout value in seconds. |
|
Integer representing the number of seconds since the current Routing Engine was booted. This property is available starting in Junos PyEZ Release 2.1.5. |
|
String specifying the user accessing the Junos device. |
For example, after connecting to a device, you can query the connected
property to return the current state of the
connection. A SessionListener
monitors
the session and responds to transport errors by raising a TransportError
exception and setting the Device.connected
property to False
.
The following sample code prints the value of the connected
property after
connecting to a Junos device and again after closing the session.
from jnpr.junos import Device dev = Device(host='router.example.net') dev.open() print (dev.connected) dev.close() print (dev.connected)
When you execute the program, the connected
property returns True
while the application
is connected to the device and returns False
after the connection is closed.
user@host:~$ python connect.py True False
Connect to a Device Using SSH
The Junos PyEZ Device
class supports using SSH to connect to a Junos device. You
can establish a NETCONF session over SSH with the device’s management interface
or you can establish an SSH connection with a console server that is directly
connected to the device’s CONSOLE port. The SSH server
must be able to authenticate the user using standard SSH authentication
mechanisms, as described in Authenticate Junos PyEZ Users. To establish a
NETCONF session over SSH, you must also satisfy the requirements outlined in
Set Up Junos PyEZ Managed Nodes.
Junos PyEZ automatically queries the default SSH configuration file at ~/.ssh/config, if one exists. When using SSH to connect to a Junos device or to a console server connected to the device, Junos PyEZ first attempts SSH public key-based authentication and then tries password-based authentication. When password-based authentication is used, the supplied password is used as the device password. When SSH keys are in use, the supplied password is used as the passphrase for unlocking the private key. If the SSH private key has an empty passphrase, then a password is not required. However, SSH private keys with empty passphrases are not recommended.
To establish a NETCONF session over SSH with a Junos device and print the device facts in a Junos PyEZ application using Python 3:
The sample program in its entirety is presented here:
import sys from getpass import getpass from jnpr.junos import Device from jnpr.junos.exception import ConnectError hostname = input("Device hostname: ") junos_username = input("Junos OS username: ") junos_password = getpass("Junos OS or SSH key password: ") dev = Device(host=hostname, user=junos_username, passwd=junos_password) try: dev.open() except ConnectError as err: print ("Cannot connect to device: {0}".format(err)) sys.exit(1) except Exception as err: print (err) sys.exit(1) print (dev.facts) dev.close()
Alternatively, you can use a context manager when connecting
to the device, which automatically calls the open()
and close()
methods. For example:
import sys from getpass import getpass from jnpr.junos import Device from jnpr.junos.exception import ConnectError hostname = input("Device hostname: ") junos_username = input("Junos OS username: ") junos_password = getpass("Junos OS or SSH key password: ") try: with Device(host=hostname, user=junos_username, passwd=junos_password) as dev: print (dev.facts) except ConnectError as err: print ("Cannot connect to device: {0}".format(err)) sys.exit(1) except Exception as err: print (err) sys.exit(1)
Junos PyEZ also enables a client to connect to a Junos device through an SSH connection to a
console server. In this case, you must specify the login credentials for the
console server by including the cs_user
and
cs_passwd
arguments in the Device
argument
list. When SSH keys are in use, set the cs_passwd
argument to
the variable containing the passphrase for the private key.
The console server connects to the Junos device through a serial connection, which can be slow.
Junos PyEZ connections through a console server have a default connection
timeout value of 0.5 seconds. As a result, you might need to increase the
connection timeout interval by including the Device
timeout=seconds
argument to allow sufficient
time for the client application to establish the connection.
The following Python 3 example authenticates with the console server and then the Junos device. The connection timeout is set to six seconds so that the client has sufficient time to establish the connection.
import sys from getpass import getpass from jnpr.junos import Device from jnpr.junos.exception import ConnectError hostname = input("Console server hostname: ") cs_username = input("Console server username: ") cs_password = getpass("Console server or SSH key password: ") junos_username = input("Junos OS username: ") junos_password = getpass("Junos OS password: ") try: with Device(host=hostname, user=junos_username, passwd=junos_password, cs_user=cs_username, cs_passwd=cs_password, timeout=6) as dev: print (dev.facts) except ConnectError as err: print ("Cannot connect to device: {0}".format(err)) sys.exit(1) except Exception as err: print (err) sys.exit(1)
Junos PyEZ automatically queries the default SSH configuration file at
~/.ssh/config, if one exists. However, you can specify
a different SSH configuration file when you create the device instance by
including the ssh_config
parameter in the
Device
argument list. For example:
ssh_config_file = "~/.ssh/config_dc" dev = Device(host='198.51.100.1', ssh_config=ssh_config_file)
Junos PyEZ also provides support for ProxyCommand, which enables you to access a target device through an intermediary host that supports netcat. This is useful when you can only log in to the target device through the intermediate host.
To configure ProxyCommand, add the appropriate information to the SSH configuration file. For example:
[user1@server ~]$ cat ~/.ssh/config Host 198.51.100.1 User user1 ProxyCommand ssh -l user1 198.51.100.2 nc %h 22 2>/dev/null
Connect to a Device Using Outbound SSH
You can configure a Junos device to initiate a TCP/IP connection with a client management
application that would be blocked if the client attempted to initiate the
connection (for example, if the device is behind a firewall). The
outbound-ssh
configuration instructs the device to create a
TCP/IP connection with the client management application and to forward the
identity of the device. Once the connection is established, the management
application acts as the client and initiates the SSH sequence, and the Junos
device acts as the server and authenticates the client.
Once you configure and commit outbound SSH on the Junos device, the device begins to initiate an outbound SSH connection based on the committed configuration. The device repeatedly attempts to create this connection until successful. If the connection between the device and the client management application is dropped, the device again attempts to create a new outbound SSH connection until successful. This connection is maintained until the outbound SSH configuration is deleted or deactivated.
To configure the Junos device for outbound SSH connections, include the
outbound-ssh
statement at the [edit system
services]
hierarchy level. In the following example, the Junos
device attempts to initiate a connection with the host at 198.51.100.101 on port
2200:
user@router1> show configuration system services outbound-ssh client nms1 { device-id router1; secret "$9$h1/ceWbs4UDkGD/Cpu1I-Vb"; ## SECRET-DATA services netconf; 198.51.100.101 port 2200; }
To establish a connection with the Junos device using outbound SSH, the Junos PyEZ application
sets the sock_fd
argument in the Device
constructor equal to the file descriptor of an existing socket and either omits
the host
argument or sets it to None
.
The following Junos PyEZ example listens on the configured TCP port for incoming SSH sessions
from Junos devices. The application accepts an incoming connection and retrieves
the socket’s file descriptor for that connection, which is used for the value of
the sock_fd
argument. The client application establishes the
SSH connection with the device, collects and prints the device facts,
disconnects from the device, and waits for more connections.
import socket from jnpr.junos import Device from jnpr.junos.exception import ConnectError from getpass import getpass from pprint import pprint """ Listen on TCP port 2200 for incoming SSH session with a Junos device. Upon connecting, collect and print the devices facts, then disconnect from that device and wait for more connections. """ def launch_junos_proxy(client, addr): val = { 'MSG-ID': None, 'MSG-VER': None, 'DEVICE-ID': None, 'HOST-KEY': None, 'HMAC': None } msg = '' count = 0 while count < 5: c = client.recv(1) c = c.decode("utf-8") msg += str(c) if c == '\n': count += 1 for line in msg.splitlines(): (key, value) = line.split(': ') val[key] = value print("{}: {}".format(key, val[key])) return client.fileno() def main(): PORT = 2200 junos_username = input('Junos OS username: ') junos_password = getpass('Junos OS password: ') s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(('', PORT)) s.listen(5) print('\nListening on port %d for incoming sessions ...' % (PORT)) sock_fd = 0 while True: client, addr = s.accept() print('\nGot a connection from %s:%d' % (addr[0], addr[1])) sock_fd = launch_junos_proxy(client, addr) print('Logging in ...') try: with Device(host=None, sock_fd=sock_fd, user=junos_username, passwd=junos_password) as dev: pprint(dev.facts) except ConnectError as err: print ("Cannot connect to device: {0}".format(err)) if __name__ == "__main__": main()
user@nms1:~$ python3 junos-pyez-outbound-ssh.py Junos OS username: user Junos OS password: Listening on port 2200 for incoming sessions ... Got a connection from 10.10.0.5:57881 MSG-ID : DEVICE-CONN-INFO MSG-VER : V1 DEVICE-ID : router1 HOST-KEY : ssh-rsa AAAAB...0aF4Mk= HMAC : 4e61201ec27a8312104f63bfaf77a4478a892c82 Logging in ... {'2RE': True, 'HOME': '/var/home/user', 'RE0': {'last_reboot_reason': 'Router rebooted after a normal shutdown.', 'mastership_state': 'master', 'model': 'RE-MX-104', 'status': 'OK', 'up_time': '2 days, 6 hours, 22 minutes, 22 seconds'}, 'RE1': {'last_reboot_reason': 'Router rebooted after a normal shutdown.', 'mastership_state': 'backup', 'model': 'RE-MX-104', 'status': 'OK', 'up_time': '2 days, 6 hours, 22 minutes, 12 seconds'}, 'RE_hw_mi': False, 'current_re': ['re0', 'master', 'node', 'fwdd', 'member', 'pfem'], 'domain': 'example.com', 'fqdn': 'router1.example.com', 'hostname': 'router1', ...
For detailed information about configuring outbound SSH on Junos devices, see Configure Outbound SSH Service.
Connect to a Device Using Telnet
The Junos PyEZ Device
class supports connecting to a Junos device using telnet,
which provides unencrypted access to the network device. You can telnet to the
device’s management interface or to a console server that is directly connected
to the device’s CONSOLE port. You must configure the
Telnet service at the [edit system services]
hierarchy level on
all devices that require access to the management interface. Accessing the
device through a console server enables you to initially configure a new or
zeroized device that is not yet configured for remote access.
To use Junos PyEZ to telnet to a Junos device, you must include mode='telnet'
in
the Device
argument list, and optionally include the
port
parameter to specify a port. When you specify
mode='telnet'
but omit the port
parameter,
the value for port
defaults to 23. When the application
connects through a console server, specify the port through which the console
server connects to the Junos device.
To use Junos PyEZ to telnet to a Junos device and print the device facts in a Junos PyEZ application using Python 3:
The sample program in its entirety is presented here:
import sys from getpass import getpass from jnpr.junos import Device hostname = input("Device hostname: ") junos_username = input("Junos OS username: ") junos_password = getpass("Junos OS password: ") dev = Device(host=hostname, user=junos_username, passwd=junos_password, mode='telnet', port='23') try: dev.open() except Exception as err: print (err) sys.exit(1) print (dev.facts) dev.close()
Alternatively, you can use a context manager when connecting to the device, which handles opening and closing the connection. For example:
import sys from getpass import getpass from jnpr.junos import Device hostname = input("Device hostname: ") junos_username = input("Junos OS username: ") junos_password = getpass("Junos OS password: ") try: with Device(host=hostname, user=junos_username, passwd=junos_password, mode='telnet', port='23') as dev: print (dev.facts) except Exception as err: print (err) sys.exit(1)
In some cases, when you connect to a console server that emits a banner message, you might be required to press Enter after the message to reach the login prompt. If a Junos PyEZ application opens a Telnet session with a console server that requires the user to press Enter after a banner message, the application might fail to receive the login prompt, which can cause the connection to hang.
Starting in Junos PyEZ Release 2.6.2, Junos PyEZ automatically handles the console server banner.
In Junos PyEZ Releases 2.1.0 through 2.6.1, a Junos PyEZ application can include
console_has_banner=True
in the Device
argument list to telnet to a console server that emits a banner message.
dev = Device(host=hostname, user=username, passwd=password, mode='telnet', console_has_banner=True)
When you include the console_has_banner=True
argument and the application does not receive a login prompt upon
initial connection, the application waits for 5 seconds and then emits
a newline (\n
) character so that the console
server issues the login prompt. If you omit the argument and the connection
hangs, the application instead emits the <close-session/>
RPC to terminate the connection.
Connect to a Device Using a Serial Console Connection
The Junos PyEZ Device
class enables you to connect to a Junos device using a
serial console connection, which is useful when you must initially configure a
new or zeroized device that is not yet configured for remote access. To use this
connection method, you must be physically connected to the device through the
CONSOLE port. For detailed instructions about
connecting to the CONSOLE port on your device, see the
hardware documentation for your specific device.
Junos PyEZ supports using context managers for serial console connections. We recommend that you use a context manager for console connections, because the context manager automatically handles opening and closing the connection. Failure to close the connection can lead to unpredictable results.
To use Junos PyEZ to connect to a Junos device through a serial console connection, you must
include mode='serial'
in the Device
argument
list, and optionally include the port
parameter to specify a
port. When you specify mode='serial'
but omit the
port
parameter, the value for port
defaults to /dev/ttyUSB0
.
To connect to a Junos device using a serial console connection and also load and commit a configuration on the device in a Junos PyEZ application using Python 3:
The sample program in its entirety is presented here:
import sys from getpass import getpass from jnpr.junos import Device from jnpr.junos.utils.config import Config junos_username = input("Junos OS username: ") junos_password = getpass("Junos OS password: ") try: with Device(mode='serial', port='port', user=junos_username, passwd=junos_password) as dev: print (dev.facts) cu = Config(dev) cu.lock() cu.load(path='/tmp/config_mx.conf') cu.commit() cu.unlock() except Exception as err: print (err) sys.exit(1)