GithubHelp home page GithubHelp logo

aeroxis / sultan Goto Github PK

View Code? Open in Web Editor NEW
678.0 15.0 35.0 470 KB

Sultan: Command and Rule over your Shell

Home Page: https://sultan.readthedocs.io/en/latest/

License: MIT License

Python 99.89% Makefile 0.11%
bash command-line cli zsh bash-script terminal compiler python2 python3 ssh

sultan's Introduction

sultan logo

Command and Rule over your Shell

PyPI Version Travis Build Status MIT License Documentation Status

Sultan now supports Python 2.7+ and 3.0+

Note

Your input is welcome! Please provide your feedback by creating issues on Github

Install

pip install --upgrade sultan

Documentation

Documentation Status

Documentation is available on ReadTheDocs: http://sultan.readthedocs.io/en/latest/

What is Sultan?

Sultan is a Python package for interfacing with command-line utilities, like yum, apt-get, or ls, in a Pythonic manner. It lets you run command-line utilities using simple function calls.

The simplest way to use Sultan is to just call it:

from sultan.api import Sultan
s = Sultan()
s.sudo("yum install -y tree").run()

Runs:

sudo yum install -y tree;

The recommended way of using Sultan is to use it in Context Management mode. Here is how to use Sultan with Context Management:

from sultan.api import Sultan

with Sultan.load(sudo=True) as s:
  s.yum("install -y tree").run()

Runs:

sudo su - root -c 'yum install -y tree;'

What if we want to install this command on a remote machine? You can easily achieve this using context management:

from sultan.api import Sultan

with Sultan.load(sudo=True, hostname="myserver.com") as sultan:
  sultan.yum("install -y tree").run()

Runs:

ssh [email protected] 'sudo su - root -c 'yum install -y tree;''

If you enter a wrong command, Sultan will print out details you need to debug and find the problem quickly.

Here, the same command was run on a Mac:

from sultan.api import Sultan

with Sultan.load(sudo=True, hostname="myserver.com") as sultan:
  sultan.yum("install -y tree").run()

Yields:

[sultan]: sudo su - root -c 'yum install -y tree;'
Password:
[sultan]: --{ STDERR }-------------------------------------------------------------------------------------------------------
[sultan]: | -sh: yum: command not found
[sultan]: -------------------------------------------------------------------------------------------------------------------

Want to get started? Simply install Sultan, and start writing your clean code:

pip install --upgrade sultan

If you have more questions, check the docs! http://sultan.readthedocs.io/en/latest/

sultan's People

Contributors

bigolu avatar davydany avatar foobarquaxx avatar fpatwa avatar marcolussetti avatar ograff avatar philae-ael avatar quizzicus avatar tminakov avatar wcass77 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sultan's Issues

Hyphenated commands: Using apt-get like yum was used in the examples

In the examples, the following very elegant syntax is used to install something with yum:

with Sultan.load(sudo=True, hostname="myserver.com") as sultan:
  sultan.yum("install -y tree").run()

I'd love to use this with apt-get, but the hyphen collides with Python syntax, obviously. Is there a way to achieve that?

Of course I could do sultan.sudo("apt-get install ...").run(), but I really like the idea of having the sudo in the contest.

Tried apt_get, which fails as _ is not replaced, probably for good reason.

Possible merger with Fabric?

Hi author of Sultan, I just noticed your project from Hacker News. Sorry if this sounds stupid. It is subjective, I know but I am going to give my opinions anyway.

There is this awesome project called 'Fabric' for automating tasks using command line utilities through Python, and much more. We have already used that in place of Ansible and it served pretty well for us. I believe both of the projects (Sultan & Fabric) serves the same purpose, while Fabric doing much more (not being offensive).

Right now Fabric has reached a tipping point as mentioned in the Roadmap. So why not contribute the best parts of your project to Fabric so that it benefits Fabric's code base.

For your reference,

  1. Invoke
  2. Fabric

Please close this if this is completely ridiculous.

Set which command to run

Is there a way to set the command that sultan runs?

from sultan.api import Sultan
with Sultan.load() as c:
  c.mv(CFGFILE,NEWCFGFILE).run()

I run at a bash command line the following:
which mv
i get:
alias mv='mv -i'
/usr/bin/mv

I believe my c.mv command is doing /usr/bin/mv but I was wondering if there is a way to make sure? BTW I'm using mv as an example as I have a much more complex issue but the answer to this will help.

Problems when sourcing a file

Hi,

When sourcing a file, the sourcing process happens in the dir that the script is running and not in the one specified by cwd. For instance, if one is in /foo and the script below is in /foo/bar/baz, the variable var is equal to foo and not baz. It's possible to add a flag indicating where the sourcing happens?

export var=$(basename $(pwd))

Also, the path src script must always be absolute or can be relative to cwd?

with Sultan.load(
        cwd='/foo/bar',
        src='baz/config.sh') as s:
    s.pip(...)

Regards

No logging option?

Hi ~ First of all: this is awesome!
Second, is there a way to remove sultan logging?

txtx!

Add support for Bash's OR operator

We currently support And &&. We need support for Or ||

||
OR

if [ $condition1 ] || [ $condition2 ]
# Same as:  if [ $condition1 -o $condition2 ]
# Returns true if either condition1 or condition2 holds true...

if [[ $condition1 || $condition2 ]]    # Also works.
#  Note that || operator not permitted inside brackets
#+ of a [ ... ] construct.

Source: http://www.tldp.org/LDP/abs/html/ops.html

Sultan.load() vs Sultan()

In Streaming Results from a Command we see:

with Sultan.load() as s:
    result = s.yum('install', '-y', 'postgresql').run(streaming=True)
    while True:
        # if full output is needed, read the pipes one last time
        # after `is_complete == True` to avoid a race condition
        complete = result.is_complete
        for line in result.stdout:
            print(line)
        for line in result.stderr:
            print(line)
        if complete:
            break
        time.sleep(1)

My question is: how different would this code be if Sultan.load() were replaced with Sultan().

Use custom ssh key (ssh -i)

In Sultan context for remote host, is there a way to connect to remote host with a custom SSH key? Like one would otherwise do with ssh -i mykeyfile.pem user@host.

Thank you!

Getting output on SSH connection

Is there any way to get STDOUT or STDERR streams as strings or lists from the sultan API? I am particularly interested in the remote SSH based commands.

Use async/await pattern with sultan

Hi,

I am using the async/await keywords in more and more programs to easily do asynchronous tasks in Python and I would be interested in using when doing shell commands with sultan. Is it something you are considering or are interested in?

If you want more information on how this can work, I found this post that given some information on this. I'd also be willing to contribute to add async/await to sultan.

Windows cygwin support

There should be a way to specify Cygwin base directory while loading Sultan.
This would able to use Sultan in Windows as well.

Thanks,
Puneet

Current test suite has partial failures: 2 of 45

Platform: macOS
Sultan version: 0.8.1 (git-clone of repo today)

Failures occur w/ both Python 2.7 and Python 3.7.

...
FAIL: test_custom_executable (integration.sultan.test_api.SultanExecutable)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/jens/se_inspect/_topic.cmdrunner/aeroxis_sultan/test/integration/sultan/test_api.py", line 58, in test_custom_executable
    self.assertEqual(result.stdout[0], 'bash')
AssertionError: '/bin/bash' != 'bash'
-------------------- >> begin captured logging << --------------------
sultan: DEBUG: ps | grep `echo $$` | awk '{ print $4 }';
--------------------- >> end captured logging << ---------------------

======================================================================
FAIL: test_default_executable (integration.sultan.test_api.SultanExecutable)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/jens/se_inspect/_topic.cmdrunner/aeroxis_sultan/test/integration/sultan/test_api.py", line 52, in test_default_executable
    self.assertEqual(result.stdout[0], 'sh')
AssertionError: '/bin/sh' != 'sh'
-------------------- >> begin captured logging << --------------------
sultan: DEBUG: ps | grep `echo $$` | awk '{ print $4 }';
--------------------- >> end captured logging << ---------------------

SSH command completion

Sultan is not able to detect SSH command completion. However if you Ctrl+C the first instance of the command and re-run it a second time, things work well.

Here is a code sample of what I am using:

with Sultan.load(hostname='10.0.0.1', user='ubuntu', log=True) as sultan:
            sultan.sudo("apt-get update").run()

This is repeatable through pretty much all commands.

Unreachable code in api.py

Hi, I was reading Sultan's code and PyCharm highlighted a piece of code that makes some code unreachable.

It seems like David Daniel's commit of 30th November made lines 222 to 288 impossible to execute as they follow a return statement (line 219).

observing error No module named api

Traceback (most recent call last):
File "su.py", line 3, in
from sultan.api import *
File "/var/www/html/sultan.py", line 3, in
ImportError: No module named api

import sultan
Traceback (most recent call last):
File "", line 1, in
File "/var/www/html/sultan.py", line 3, in
ImportError: No module named api

from sultan.api import Sultan
Traceback (most recent call last):
File "", line 1, in
File "/var/www/html/sultan.py", line 3, in
ImportError: No module named api

.run() outputs [sultan] command

When I run my python3 script sultan outputs to the console which I control. Is there a way to stop this and/or grab this for later display?

question: support <port> option for remote host

Hi Sultan team,

Thank you for developing and sharing this package. Is it possible to provide an SSH port number, alternative to 22 via the API? I know I could workaround this using an .ssh/config setting. I'd prefer to have this ability supported natively in the API if possible.

Cheers,
-- Jeremy

FINAL: Release 0.2 Features

FINAL

To do:

Completed:

  • Better color schemes. Right now DEBUG is just white and it makes it odd to read. The color schemes should reflect what the level implies.
  • Errors and Stdout should be better formatted with logging. If an exception happens, we see a traceback, which is useless. The stderr and stdout should be more nicely formatted and presented to the user when an error occurs.
  • We can use raw_input to get input via stdin, but it would be nice if the Sultan API also supported it.
  • Context Management:
    • sudo=<True/False>: Set a command should be run as sudo with Context Management.
    • user=<username>: Set a user to run command as
    • host=<hostname>: Should be able to run a command on a remote host, by setting a

Support for custom shell executable

subprocess.Popen with shell=True runs the program through /bin/sh by default, a behavior that can be overridden with executable=value when creating the object.

Sultan should support this overriding - with a very simple rationale (for me :) - Ubuntu links /bin/sh to /bin/dash (they probably have their good reasons for that), which does not have the source command (it's a bash one).
Thus the src context argument cannot be used, which for python's virtual environments is a bummer.

Can we load source file in the load function

Right now I am loading the context with some source which contains informtion about paths etc.

with Sultan.load(user='puneet', logging=False, hostname="hadoopbox",
                 cwd="/home/user/puneet && source /home/user/puneet/hadoop.env") as s:
    for x in s.hadoop('fs -ls /user/puneet').pipe().sed("-r \"s/ +/ /g\"").pipe().cut("-d\" \" -f8").run():
        print(x)

I think this can be made better by having an optional parameter which loads source while loading the context. Or, maybe it's already there and I am missing something.

Something like this would work great

with Sultan.load(user='puneet', logging=False, hostname="hadoopbox",
                 cwd="/home/user/puneet", source="/home/user/puneet/hadoop.env") as s:
    for x in s.hadoop('fs -ls /user/puneet').pipe().sed("-r \"s/ +/ /g\"").pipe().cut("-d\" \" -f8").run():
        print(x)

Default value of tracecback in the constructor to be ''

Currently the default value of traceback is None:

def __init__(self, stdout, stderr, traceback=None, rc=None):

And that value is only filled in if there is an exception in Sultan.run(); if the user unaware of that calls Result.print_traceback(), there's a general exception

TypeError: 'NoneType' object is not iterable

, coming from Result.__format_lines()'s iterator over the Result.traceback.

If the default is changed to an empty string, the exception won't occur and print_traceback() will behave like the other two print methods.

Output everything

I'm using Sultan for an automated installation script internally.

I would always like to see the output. Now I do that like this:

r = s.sh('./configure').run()
print('\n'.join(r.stdout))

But I have a lot of commands. Can I configure that as a constructor parameter or something?

Pydoc

I think the "pydoc" should be filled out with classes and methods in order to provide better documentation for the project. I tried "pydoc sultan" on my local machine but the lack of information there forced me to search out this github page to figure out exactly how to use it in my scripts.

Add background execution

I need in some occasions a background execution of some commands.
With it it would be great to store a pid of such process too

[Improvement] Make the exception dump while invoking run() optional

Hi!

Been using Sultan for a little while now and i've found one part that should be improved.

from api.py -> cmd -> run()

       except Exception as e:
            result = Result(None, commands, self._context, exception=e)
            result.dump_exception()
            if halt_on_nonzero:
                raise e

The dump_exception() should be optional. At the moment i have three instances of Sultan running "inside" each other due to ssh chaining, and as you might understand, the stack dumb turns into a giant mess when a dump is invoked at layer 3 up to layer 1

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.