Pelix Shell¶
Most of the time, it is necessary to access a Pelix application locally or remotely in order to monitor it, to update its components or simply to check its sanity. The easiest to do those tasks is to use the Pelix Shell: it provides an extensible set of commands that allows to work on bundles, iPOPO components, …
The shell is split into two parts:
- the core shell, handling and executing commands
- the UI, which handles input/output operations with the user
Pelix comes with some bundles providing shell commands for various actions, and a few UI implementations. Feel free to implement and, maybe, publish new commands UIs according to your needs.
In order to use the shell, the pelix.shell.core
bundle must be installed
and running.
It doesn’t require iPOPO and can therefore be used in minimalist applications.
Provided user interfaces¶
Pelix includes 3 main user interfaces:
- Text UI: the one to use when running a basic Pelix application
- Remote Shell: useful when managing an application running on a server
- XMPP Shell: useful to access applications behind firewalls
Common script arguments¶
Before looking at the available user interfaces, note that all of them support arguments to handle the Initial Configuration files (see Initial Configuration File).
In addition to their specific arguments, the scripts starting the user interfaces also accept the following ones:
Argument | Description |
---|---|
-h , --help |
Prints the script usage |
--version |
Prints the script version |
-D KEY=VALUE |
Sets up a framework property |
-v , --verbose |
Sets the logger to DEBUG mode |
--init FILE |
Start by running a Pelix shell script |
--run FILE |
Run a Pelix shell script then exit |
-c FILE , --conf FILE |
Use a configuration file, above the system configuration |
-C FILE , --exclusive-conf FILE |
Use a configuration file, ignore the system configuration |
-e , --empty-conf |
Don’t use any initial configuration |
Text UI¶
The Text UI is the easiest way to manage or test your programs with Pelix/iPOPO. It provides the most basic yet complete interaction with the Pelix Shell core service.
If it is available, the Text UI relies on readline
to provide command
and arguments completion.
Script startup¶
The text (or console) UI can be started using the python -m pelix.shell
command.
This command will start a Pelix framework, with iPOPO and the most commonly
used shell command providers.
This script only accepts the common shell parameters.
Programmatic startup¶
This UI is provided by the pelix.shell.console
bundle.
It is a raw bundle, which does not provide a component factory: the UI is
available while the bundle is active.
There is no configuration available when starting the Text UI programmatically.
Remote Shell¶
Pelix frameworks are often started on remote locations, but still need to be managed with the Pelix shell. Instead of using an SSH connection to work on a foreground server, you can use the Pelix Remote Shell.
The Pelix Remote Shell is a simple interface to the Pelix Shell core service of its framework instance, based on a TCP server. Unlike the console UI, multiple users can connect the framework at the same time, each with his own shell session (variables, …).
By default, the remote shell starts a TCP server listening the local interface (localhost) on port 9000. It is possible to enforce the server by setting up OpenSSL certificates. The server will have its own certificate, which should be checked by the clients, and each client will have to connect with its own certificate, signed by an authority recognized by the server. See How to prepare certificates for the Remote Shell for more information on how to setup this kind of certificates.
Note
TLS features and arguments are available only if the Python interpreters
fully provides the ssl
module, i.e. if it has been built with OpenSSL.
Script startup¶
The remote shell UI can be started using the python -m pelix.shell.remote
command.
This command will start a Pelix framework with iPOPO, and will start a Python
console locally.
In addition to the common parameters, the script accepts the following ones:
Argument | Default | Description |
---|---|---|
--no-input |
not set | If set, don’t start the Python console (useful for server/daemon mode) |
-a ADDR , --address ADDR |
localhost | Server binding address |
-p PORT , --port PORT |
9000 | Server binding port |
--ca-chain FILE |
None |
Path to the certificate authority chain file (to authenticate clients) |
--cert FILE |
None |
Path to the server certificate file |
--key FILE |
None |
Path to the server private key file |
--key-password PASSWORD |
None |
Password of the server private key |
Programmatic startup¶
The remote shell is provided as the ipopo-remote-shell-factory
component
factory defined in the pelix.shell.remote
bundle.
You should use the constant pelix.shell.FACTORY_REMOTE_SHELL
instead of the
factory name when instantiating the component.
This factory accepts the following properties:
Name | Default | Description |
---|---|---|
pelix.shell.address |
localhost | Server binding address |
pelix.shell.port |
9000 | Server binding port |
pelix.shell.ssl.ca |
None |
Path to the clients certificate authority chain file |
pelix.shell.ssl.cert |
None |
Path to the server’s SSL certificate file |
pelix.shell.ssl.key |
None |
Path to the server’s private key |
pelix.shell.ssl.key_password |
None |
Password of the server’s private key |
XMPP Shell¶
The XMPP shell interface allows to communicate with a Pelix framework using an XMPP client, e.g. Pidgin, Psi. The biggest advantages of this interface are the possibility to use TLS to encrypt conversations and the fact that it is an output-only communication. This allows to protect Pelix applications behind hardened firewalls, letting them only to connect the XMPP server.
It requires an XMPP account to connect an XMPP server. Early tests of this bundle were made against Google Talk (with a GMail account, not to be confused with Google Hangout) and a private OpenFire server.
Script startup¶
The XMPP UI can be started using the python -m pelix.shell.xmpp
command.
This command will start a Pelix framework with iPOPO, and will start a Pelix
console UI locally.
In addition to the common parameters, the script accepts the following ones:
Argument | Default | Description |
---|---|---|
-j JID , --jid JID |
None |
Jabber ID (user account) |
--password PASSWORD |
None |
Account password |
-s ADDR , --server ADDR |
None |
Address of the XMPP server (found in the Jabber ID by default) |
-p PORT , --port PORT |
5222 | Port of the XMPP server |
--tls |
not set | If set, use a STARTTLS connection |
--ssl |
not set | If set, use an SSL connection |
Programmatic startup¶
This UI depends on the sleekxmpp
third-party package, which can be installed
using the following command:
pip install sleekxmpp
The XMPP shell is provided as the ipopo-xmpp-shell-factory
component
factory defined in the pelix.shell.xmpp
bundle.
You should use the constant pelix.shell.FACTORY_XMPP_SHELL
instead of the
factory name when instantiating the component.
This factory accepts the following properties:
Name | Default | Description |
---|---|---|
shell.xmpp.server |
localhost | XMPP server hostname |
shell.xmpp.port |
5222 | XMPP server port |
shell.xmpp.jid |
None |
JID (XMPP account) to use |
shell.xmpp.password |
None |
User password |
shell.xmpp.tls |
1 | Use a STARTTLS connection |
shell.xmpp.ssl |
0 | Use an SSL connection |
Provided command bundles¶
Pelix/iPOPO comes with some batteries included. Here is the list of the bundles which provide commands for specific services.
Note that the commands themselves won’t be described here: it is recommended
to use the help
command in the shell to have the latest usage information.
Bundle name | Description |
---|---|
pelix.shell.ipopo |
Handles iPOPO factories and instances. |
pelix.shell.configadmin |
Handles the Configuration Admin service (provided by pelix.misc.configadmin ). See Configuration Admin. |
pelix.shell.eventadmin |
Handles the Event Admin service (provided by pelix.misc.eventadmin ). See EventAdmin service. |
pelix.shell.log |
Looks into the Log Service (provided by pelix.misc.log ). See Logging. |
pelix.shell.report |
Generates framework state reports. See Shell reports. |
How to provide commands¶
Shell Command service¶
Shell commands are detected by the Shell Core Service when a Shell Command
service (use the pelix.shell.SERVICE_SHELL_COMMAND
constant) is registered.
First, the Shell Core calls the get_namespace()
method of the new service,
in order to prepare the (potentially new) command namespace.
Each shell command provider should have a unique, human-readable, namespace.
Sometimes it can be interesting to have multiple services providing sets of
optional commands in the same namespace, but this can lead to some unexpected
behaviour, e.g. when trying to provide the same command name twice in the
same namespace.
A namespace must not contain spaces nor separator characters (dot, comma, …).
Then, the Shell Core calls get_methods()
, which must a return a list of
(command name, command method) couples.
Like its namespace, a command name must not contain spaces nor separator
characters (dot, comma, …).
Each command method must accept at least one argument: the
ShellSession
object representing the current
session and handling interactions with the client.
Note that the Python docstring of the method will be what is shown by the
core help command.
The shell core bundle also provides a utility service,
pelix.shell.SERVICE_SHELL_UTILS
,
which can be used to generate ASCII tables to print out to the user.
This is the service used by the core method to print the list of bundles,
services, iPOPO instances, etc..
Here is an example of a simple command service providing the echo and hello shell commands. echo accepts an unlimited list of arguments and prints it back to the client. hello asks a name if it wasn’t given as parameter then says hello.
from pelix.ipopo.decorators import ComponentFactory, Provides, Instantiate
import pelix.shell
@ComponentFactory("sample-commands-factory")
@Provides(pelix.shell.SERVICE_SHELL_COMMAND)
@Instantiate("sample-shell-commands")
class SampleCommands(object):
"""
Sample shell commands
"""
@staticmethod
def get_namespace():
"""
Retrieves the name space of this command handler
"""
return "sample"
def get_methods(self):
"""
Retrieves the list of tuples (command, method) for this command handler
"""
return [("echo", self.echo), ("hello", self.hello)]
def hello(self, session, name=None):
"""
Says hello
"""
if not name:
# Name not given as parameter, ask for it
name = session.prompt("What's your name? ")
session.write_line("Hello, {0} !", name)
def echo(self, session, *words):
"""
Prints back the words it has been given
"""
session.write_line(" ".join(words))
To use this sample, simply start a framework with the Shell Core, a Shell UI and iPOPO, then install and start the sample bundle. For example:
bash:~ $ python -m pelix.shell
** Pelix Shell prompt **
$ start pelix.shell.toto
Bundle ID: 14
Starting bundle 14 (pelix.shell.toto)...
$ sample.echo Hello, world !
Hello, world !
$ hello World
Hello, World !
$ hello
What's your name? Thomas
Hello, Thomas !
The I/O handling of the session
argument is implemented by the shell UI and
hides the ways used to communicate with the client.
The code of this example works with all UIs: local text UI, remote shell and
XMPP shell.
API¶
-
class
pelix.shell.beans.
ShellSession
(io_handler, initial_vars=None)¶ Represents a shell session. This is the kind of object given as parameter to shell commands
Sets up the shell session
Parameters: - io_handler – The I/O handler associated to the session
- initial_vars – Initial variables
Note
This class is instantiated by Shell UI implementations and its instances shouldn’t be shared nor stored by command providers.
-
prompt
(prompt=None)¶ Waits for a line to be written by the user
Parameters: prompt – An optional prompt message Returns: The read line, after a conversion to str
-
write_line
(line=None, *args, **kwargs)¶ Formats and writes a line to the output. This method has the same signature as
str.format
. If necessary, a new-line marker (\n
) is added at the end of the given string. The output stream is flushed to ensure that the text is written.Parameters: - line – A line for
str.format
markers - args – Content for the positional markers
- kwargs – Content for the keyword markers
- line – A line for
-
write_line_no_feed
(line=None, *args, **kwargs)¶ Formats and writes a line to the output. This method has the same signature as
str.format
. If the given line ended with a new-line marker, the latter is removed. The output stream is flushed to ensure that the text is written.Parameters: - line – A line for
str.format
markers - args – Content for the positional markers
- kwargs – Content for the keyword markers
- line – A line for
-
get
(name)¶ Returns the value of a variable
Parameters: name – Name of the variable Returns: The value of the variable Raises: KeyError – Unknown name
-
set
(name, value)¶ Sets/overrides the value of a variable
Parameters: - name – Variable name
- value – New value
-
unset
(name)¶ Unsets the variable with the given name
Parameters: name – Variable name Raises: KeyError – Unknown name
-
last_result
¶ Returns the content of $result
-
variables
¶ A copy of the session variables
-
class
pelix.shell.core.
_ShellUtils
¶ Utility methods for the shell
Note
This class shouldn’t be instantiated directly. The developer must instead look for and the
pelix.shell.SERVICE_SHELL_UTILS
service.-
static
bundlestate_to_str
(state)¶ Converts a bundle state integer to a string
-
static
make_table
(headers, lines, prefix=None)¶ Generates an ASCII table according to the given headers and lines
Parameters: - headers – List of table headers (N-tuple)
- lines – List of table lines (N-tuples)
- prefix – Optional prefix for each line
Returns: The ASCII representation of the table
Raises: ValueError – Different number of columns between headers and lines
-
static
How to prepare certificates for the Remote Shell¶
In order to use certificate-based client authentication with the Remote Shell in TLS mode, you will have to prepare a certificate authority, which will be used to sign server and clients certificates.
The following commands are a summary of OpenSSL Certificate Authority page by Jamie Nguyen.
Prepare the root certificate¶
Prepare the environment of the root certificate:
mkdir ca cd ca/ mkdir certs crl newcerts private chmod 700 private/ touch index.txt echo 1000 > serial
Download the sample openssl.cnf file to the
ca/
directory and edit it to fit your needs.Create the root certificate. The following snippet creates a 4096 bits private key and creates a certificate valid for 7300 days (20 years). The
v3_ca
extension allows to use the certificate as an authority.openssl genrsa -aes256 -out private/ca.key.pem 4096 chmod 400 private/ca.key.pem openssl req -config openssl.cnf -key private/ca.key.pem \ -new -x509 -days 7300 -sha256 -extensions v3_ca \ -out certs/ca.cert.pem chmod 444 certs/ca.cert.pem openssl x509 -noout -text -in certs/ca.cert.pem
Prepare an intermediate certificate¶
Using intermediate certificates allows to hide the root certificate private key from the network: once the intermediate certificate has signed, the root certificate private key should be hidden in a server somewhere not accessible from the outside. If your intermediate certificate is compromised, you can use the root certificate to revoke it.
Prepare the environment of the intermediate certificate:
mkdir intermediate cd intermediate/ mkdir certs crl csr newcerts private chmod 700 private/ touch index.txt echo 1000 > serial echo 1000 > crlnumber
Download the sample intermediate/openssl.cnf file to the
ca/intermediate
folder and edit it to your needs.Generate the intermediate certificate and sign it with the root certificate. The
v3_intermediate_ca
extension allows to use the certificate as an intermediate authority. Intermediate certificates are valid less time than the root certificate. Here we consider a validity of 10 years.openssl genrsa -aes256 -out intermediate/private/intermediate.key.pem 4096 chmod 400 intermediate/private/intermediate.key.pem openssl req -config intermediate/openssl.cnf \ -new -sha256 -key intermediate/private/intermediate.key.pem \ -out intermediate/csr/intermediate.csr.pem openssl ca -config openssl.cnf -extensions v3_intermediate_ca \ -days 3650 -notext -md sha256 \ -in intermediate/csr/intermediate.csr.pem \ -out intermediate/certs/intermediate.cert.pem chmod 444 intermediate/certs/intermediate.cert.pem openssl x509 -noout -text -in intermediate/certs/intermediate.cert.pem openssl verify -CAfile certs/ca.cert.pem \ intermediate/certs/intermediate.cert.pem
Generate the Certificate Authority chain file. This is simply the bottom list of certificates of your authority:
cat intermediate/certs/intermediate.cert.pem certs/ca.cert.pem \ > intermediate/certs/ca-chain.cert.pem chmod 444 intermediate/certs/ca-chain.cert.pem
Prepare the server certificate¶
The steps to generate the certificate is simple. For simplicity, we consider we are in the same folder hierarchy as before.
This certificate must has a validity period shorter than the intermediate certificate.
Generate a server private key. This can be done on any machine:
openssl genrsa -aes256 -out intermediate/private/server.key.pem 2048 openssl genrsa -out intermediate/private/server.key.pem 2048 chmod 400 intermediate/private/server.key.pem
Prepare a certificate signing request
openssl req -config intermediate/openssl.cnf \ -key intermediate/private/server.key.pem -new -sha256 \ -out intermediate/csr/server.csr.pem
Sign the certificate with the intermediate certificate. The
server_cert
extension indicates a server certificate which can’t sign other ones.openssl ca -config intermediate/openssl.cnf -extensions server_cert \ -days 375 -notext -md sha256 \ -in intermediate/csr/server.csr.pem \ -out intermediate/certs/server.cert.pem chmod 444 intermediate/certs/server.cert.pem openssl x509 -noout -text -in intermediate/certs/server.cert.pem openssl verify -CAfile intermediate/certs/ca-chain.cert.pem \ intermediate/certs/server.cert.pem
Prepare a client certificate¶
The steps to generate the client certificates are the same as for the server.
Generate a client private key. This can be done on any machine:
openssl genrsa -out intermediate/private/client1.key.pem 2048 chmod 400 intermediate/private/client1.key.pem
Prepare a certificate signing request
openssl req -config intermediate/openssl.cnf \ -key intermediate/private/client1.key.pem -new -sha256 \ -out intermediate/csr/client1.csr.pem
Sign the certificate with the intermediate certificate. The
usr_cert
extension indicates this is a client certificate, which cannot be used to sign other certificates.openssl ca -config intermediate/openssl.cnf -extensions usr_cert \ -days 375 -notext -md sha256 \ -in intermediate/csr/client1.csr.pem \ -out intermediate/certs/client1.cert.pem chmod 444 intermediate/certs/client1.cert.pem openssl x509 -noout -text -in intermediate/certs/client1.cert.pem openssl verify -CAfile intermediate/certs/ca-chain.cert.pem \ intermediate/certs/client1.cert.pem
Connect a TLS Remote Shell¶
To connect a basic remote shell, you can use netcat
, which is available
for nearly all operating systems and all architectures.
To connect a TLS remote shell, you should use the OpenSSL client: s_client
.
It is necessary to indicate the client certificate in order to be accepted by
the server.
It is also recommended to indicate the authority chain to ensure that the
server is not a rogue one.
Here is a sample command line to connect a TLS remote shell on the local host, listening on port 9001.
openssl s_client -connect localhost:9001 \
-cert client1.cert.pem -key client1.key.pem \
-CAfile ca-chain.cert.pem