Getting Started

Welcome! This tutorial highlights the basics of the features added by the FabricPlus library; for more information on the base Fabric library itself, please see the Fabric documentation.

This tutorial assumes you are already familiar with the Fabric library and its usage.

A Note About Imports

FabricPlus, and in turn, it’s ConnectionPlus object, inherits and imports directly from the Fabric library.

It also imports and modifies some things from the paramiko library.

The expected use case for this library is simply to import the ConnectionPlus object, and use that directly.

But you may want to modify the behavior of underlying objects, or use them directly.

In this case, importing and using objects from paramiko, or invoke, just as in the base Fabric library, is still possible.

Installation

As of FabricPlus version 0.1.0, the library is available on PyPI at the FabricPlus Project Page.

To install FabricPlus, simply run:

pip install fabricplus

Or, if you prefer to install from source (requires poetry):

# Clone the repository
git clone https://github.com/prokopto-dev/fabric-plus.git
# Go into the directory
cd fabric-plus
# Run the poetry build command
poetry build
# Install the newly built wheel file
pip install dist/fabricplus-0.1.0-py3-none-any.whl

Note

The version number may change, so be sure to check the version number of the wheel file you build.

Using The Pacakage

Using ConnectionPlus As A Drop-In Replacement for Fabric’s Connection

If you want to create a connection, it is nearly identical to Fabric itself.

The only difference is if you want the drop-in replacement, you’ll need to import the ConnectionPlus object as a Connection, as below.

from fabricplus.connection import ConnectionPlus as Connection

conn: Connection = Connection("some_host")

# From here, you can do all the things you're used to, like run commands
conn.run("date")

# But now you can also run an su command as well
conn.su("date", "otheruser", password="otheruserspassword")

The following examples will work just as well regardless of naming it Connection or ConnectionPlus via the import.

Using SCP instead of the default SFTP

By default the Connection object will use SFTP, and does not have the capacity to use SCP.

The former is true for ConnectionPlus objects, but the latter is definitely not true.

There are two ways to access SCP. First, to set it as the default method via an argument to the initializer.

from fabricplus.connection import ConnectionPlus

# Add in the scp=True
conn_a: ConnectionPlus = ConnectionPlus("some_host", scp=True)

# Then run a put command; it will run through SCP!
conn_a.put("/path/to/some/local/file", "/path/on/the/remote")

You can also do it at the time of the call to put or get, like so:

from fabricplus.connection import ConnectionPlus

# leaving out scp=True
conn_b: ConnectionPlus = ConnectionPlus("some_host")

# we run this with an scp=True arg.
conn_b.put("/path/to/some/local/file", "/path/on/the/remote", scp=True)

Connecting Via A Jumphost

There are several ways to specify the jumphost you wish to connect through. There are benefits and drawbacks to each approach.

You can:

  • Pass in a string for the URL or IP Address of the Jumphost you wish to target.

  • Pass in an SSHClient-like object

  • Pass in a Connection-like object

Each is detailed below for clarity.

Using an IP Address or URL

Here we will generate a ConnectionPlus object via a jumphost passed in as a string argument.

The example could as easily be done with an IP address in the format XXX.XXX.XXX.XXX, where X is an integer, and XXX is together an integer no larger than 255.

In the example, we will also be using some other user name to log into the jumphost.

This is the only time that the jump_uname argument makes any sense, because in all other cases, the host is already logged in via a user.

from fabricplus.connection import ConnectionPlus

jumphost_url: str = "jumphost.example.com"

# create the connection object, passing in the URL and a username for the jumphost
conn_c: ConnectionPlus = ConnectionPlus("some_host",
                                        jumphost_target=jumphost_url,
                                        jump_uname="jumphost_username")

# from here, you can simply run all your commands on the target host via the standard processes
conn_c.run("date")

Using an SSHClient-like object

So an SSHClient (or SSHJumpClient, or anything else that inherits from the base SSHClient and behaves, roughly, similarly, will work) can be passed through as well.

This is useful for two cases:

  1. You want to control some more behaviors about how the SSHClient connections

  2. You want to proxy multiple connections VIA the same jumphost connection

Let us do the latter example:

from fabricplus.connection import ConnectionPlus
from fabricplus.paramiko_modifications.client import SSHJumpClient
from paramiko.client import WarningPolicy

# Creating the client object
jumphost_client: SSHJumpClient = SSHJumpClient()
# Doing some back end stuff for host key handling, because it's often necessary
jumphost_client.set_missing_host_key_policy(WarningPolicy())
jumphost_client.load_system_host_keys()
# then connecting
jumphost_client.connect("some_jumphost_url")

# create the connection object, passing in the SSHJumpClient object
conn_c: ConnectionPlus = ConnectionPlus("some_host",
                                        jumphost_target=jumphost_client)

# importantly you can REUSE the jumphost_client
conn_d: ConnectionPlus = ConnectionPlus("some_other_host",
                                        jumphost_target=jumphost_client)

# from here, you can simply run all your commands on the target host
# via the standard processes
conn_c.run("date")
conn_d.run("date")

Using a Connection-like object

Similar to above, you may also pass in a Connection-derived object.

All this does is have the back end extract the client from that Connection object, and so essentially behaves as above, but the example below should work.

from fabricplus.connection import ConnectionPlus

# Creating the client object
jumphost_connection: ConnectionPlus = ConnectionPlus("some_jumphost_url")

# create the connection object, passing in the ConnectionPlus object
conn_c: ConnectionPlus = ConnectionPlus("some_host",
                                        jumphost_target=jumphost_connection)

# importantly you can REUSE the jumphost_connection
conn_d: ConnectionPlus = ConnectionPlus("some_other_host",
                                        jumphost_target=jumphost_connection)

# from here, you can simply run all your commands on the target host
# via the standard processes
conn_c.run("date")
conn_d.run("date")