Skip to main content

Convenience classes for CLI Python applications.

Project description

robutils is a Python module that provides a handful of convenient classes mainly for command line applications, but will work in any Python script. It is designed for Python 2.7.3 on Linux systems, and may work on OS X and other Unix-based systems. robutils may also work on other platforms and Python versions but this is not supported.

Some of the features of robutils are:

  • Wrapper for executing external commands over SSH (paramiko) or locally (subprocess) with timeouts.

  • Enforce single instances using locking PID files.

  • Color text on Bash terminals, demonizing the main process, console redirects (for Altiris), logging, and email.

  • Centralizing exit messages for different exit codes. Also useful for Altiris.

  • Progress bar with ETA.

Installation

The following Python packages are required as prerequisites:

To install run one of the following commands:

pip install robutils
easy_install robutils
git clone https://github.com/Robpol86/robutils.git; cp -r robutils/robutils /usr/lib/python2.7/site-packages/

Once installed, additional documentation is available within docstrings:

>>> import robutils; help(robutils)

Examples

kjVlbuk

pPI3ePX

Avdq73Y

du2IWZ6

uOQVrxB

Message

  1. Easy to use color syntax for Linux Bash terminals (.term() hides the class instance from the interactive console):

    >>> from robutils.Message import Message
    >>> message = Message()
    >>> message('Sample text.')
    Sample text.
    <robutils.Message.Message instance at 0x107e4d0>
    >>> message('Colors: [red]red[/red] and [hiblue]multi[hired]colored[bgyellow]text[/all].').term()
    Colors: red and multicoloredtext.
    >>>
  2. Print messages to stdout, stderr, and/or log to file (with colors):

    >>> from robutils.Message import Message
    >>> message = Message(log_file='/tmp/test.log', log_level='error')
    >>> message('Regular stdout non-logged text.').term()
    Regular stdout non-logged text.
    >>> message('stderr non-logged text.', stderr=True).term()
    stderr non-logged text.
    >>> message('stdout and logged as info.').log().term()
    stdout and logged as info.
    >>> message('[red]only logged as error[/all].', quiet=True).log('error').term()
    >>> message('stdout, but not logged since debug < error').log('debug').term()
    stdout, but not logged since debug < error
    >>>
  3. Centralize messages for different exit codes. Also supports terminating the script with different exit codes:

    [testuser@localhost ~]$ ~/python27/bin/python
    Python 2.7.3 (default, Nov 24 2012, 23:17:40)
    [GCC 4.4.6 20120305 (Red Hat 4.4.6-4)] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from robutils.Message import Message
    >>> message = Message()
    >>> message.retcodes[1] = 'An error occurred.'
    >>> message.retcodes[5] = 'Error doing x, check file y.'
    >>> message.retcodes[6] = 'Error doing a, check file b.'
    >>> message.quit(5)
    
    
    QUITTING: Error doing x, check file y.
    [testuser@localhost ~]$
  4. Send email via SMTP or local sendmail. Supports a tail of the log and/or file attachments:

    >>> from robutils.Message import Message
    >>> message = Message(mail_smtp='smtp-server.austin.rr.com',
    ...     mail_from='test@gmail.com',
    ...     mail_to='robpol86@robpol86.com')
    >>> message.mail('Test Email', body='This is a test email.').term()
    >>>
  5. Demonize scripts and/or redirect stdout/stderr to a file (useful for Altiris scripts):

    from robutils.Message import Message
    message = Message(daemon=True, redirect='/tmp/redir.txt')
    message("[hiblue]This is a test[/all]")
    message.retcodes[1] = 'Exiting sample script.'
    message.quit(1)

ExternalCmd

  1. Run external commands on the local machine:

    >>> from robutils.ExternalCmd import ExternalCmd
    >>> cmd = ExternalCmd('echo "test1\ntest2\ntest3\n" | grep test2')
    >>> cmd.run_local()
    >>> cmd.stdout
    'test2\n'
    >>> cmd.code
    0
    >>> cmd = ExternalCmd('echo test1 && echo test2')
    >>> cmd.run_local()
    >>> cmd.stdout
    'test1\ntest2\n'
    >>> cmd = ExternalCmd(['ls', '-lahd', '/tmp'])
    >>> cmd.run_local()
    >>> cmd.stdout
    'drwxrwxrwt 4 root root 32K Nov 20 04:02 /tmp\n'
    >>>
  2. Run external commands on a remote host using SSH:

    >>> from robutils.ExternalCmd import ExternalCmd
    >>> cmd = ExternalCmd('echo first && sleep 10 && echo done')
    >>> cmd.run_remote('localhost')
    >>> (cmd.code, cmd.stdout)
    (None, '')
    >>> time.sleep(10)
    >>> (cmd.code, cmd.stdout)
    (0, 'first\ndone\n')
    >>>

Progress

  1. Create a progress bar and manually display the summary periodically:

    >>> from robutils.Progress import Progress
    >>> from robutils.Message import Message
    >>> message = Message()
    >>> progress = Progress(43)
    >>> while progress.total_percent < 80:
    ...     time.sleep(1)
    ...     progress.inc_pass() if random.randint(1, 5) < 5 else progress.inc_fail()
    >>> message(progress.summary())
      81% (35/43) [########################      ] eta 0:00:06 - 14% ( 5/35) failed
    >>> message(progress.summary(hide_failed=True))
      81% (35/43) [#######################################          ] eta 0:00:03 \
    >>> message(progress.summary(max_width=70))
      81% (35/43) [################    ] eta 0:00:01 | 14% ( 5/35) failed
    >>> message(progress.summary(hide_failed=True, eta_countdown=False))
      81% (35/43) [#################################        ] eta 11:45:30 PM CST /
    >>> while progress.total_percent < 100: progress.inc_pass()
    >>> message(progress.summary())
     100% (43/43) [##############################] eta 0:00:00 - 11% ( 5/43) failed
    >>>
  2. Have the progress bar print periodically in the provided threaded method:

    >>> from robutils.Progress import Progress
    >>> from robutils.Message import Message
    >>> message = Message()
    >>> progress = Progress(43)
    >>> progress.threaded_summary(message, hide_failed=True)
    >>> while progress.total_percent < 100:
    ...     time.sleep(1)
    ...     progress.inc_pass() if random.randint(1, 5) < 5 else progress.inc_fail()
    >>> print
     100% (43/43) [#################################################] eta 0:00:00 /

Instance

>>> from robutils import Instance
>>> instance = Instance('/var/tmp/example_script.pid')
>>> if not instance.single_instance_success:
...     if instance.old_pid_exists: print 'Another instance is running.'
...     if not instance.pdir_exists: print "PID file parent dir doesn't exist."
...     if not instance.can_write: print 'No write permissions.'

Supplemental Installation Steps

This section details how I setup my custom Python environment, which includes packages unrelated to robutils.

CentOS 6.3

Run this as a non-root user:

sudo yum install gcc gcc-c++ autoconf automake make
sudo yum install sqlite-devel bzip2-devel tk-devel readline-devel ncurses-devel openssl-devel gdbm-devel
sudo yum install libxslt-devel atlas-devel gcc-gfortran
mkdir -p Python27_Build/python27 && cd Python27_Build
curl http://python.org/ftp/python/2.7.3/Python-2.7.3.tar.bz2 |tar --strip-components=1 -xj
vim Modules/_sqlite/connection.c # CentOS, http://bugs.python.org/issue14572
./configure --prefix="$(pwd)/python27" && make clean && make
make install
wget "http://pypi.python.org/packages/2.7/s/setuptools/setuptools-0.6c11-py2.7.egg"
(export PATH=/bin:/usr/bin:$(pwd)/python27/bin; sh setuptools-0.6c11-py2.7.egg)
./python27/bin/easy_install pip
./python27/bin/pip install nose docutils distribute NumPy
./python27/bin/pip install SciPy pandas
./python27/bin/pip install pytz lxml psutil paramiko mutagen winpdb robutils
find ./python27 \( -name '*.pyc' -o -name '*.pyo' \) -exec rm {} \;
find ./python27 -name *egg-info* -exec rm -r {} \;
tar -czf python27-$(date +%Y%m%d).tar.gz python27

Windows 7/8

This is out of scope for this project since Windows is not supported, but I left it here for future reference:

rem Root: %localappdata%\python27
rem http://www.enthought.com/products/epd_free.php (includes Python)
rem http://www.wxpython.org/download.php#binaries
easy_install pip pytz pygments
easy_install http://pypi.python.org/packages/source/p/python-dateutil/python-dateutil-1.5.tar.gz
pip install winpdb
rem http://pypi.python.org/pypi/pandas
ipython notebook --pylab=inline

Project details


Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page