summaryrefslogtreecommitdiffhomepage
path: root/shutit.html.markdown
diff options
context:
space:
mode:
authorSuzane Sant Ana <tetestonaldo@gmail.com>2017-12-31 14:27:06 -0200
committerGitHub <noreply@github.com>2017-12-31 14:27:06 -0200
commit42f9329bb3a028d374d6397991ac48b44064741e (patch)
tree1e75e2b3e122aeb863e3ffa037f6f64c4027fbf8 /shutit.html.markdown
parente6b77595f2669d66ac7be43c6e6083cbff80a9a7 (diff)
parent70a36c9bd970b928adde06afb2bd69f6ba8e5d5c (diff)
Merge pull request #1 from adambard/master
update
Diffstat (limited to 'shutit.html.markdown')
-rw-r--r--shutit.html.markdown318
1 files changed, 318 insertions, 0 deletions
diff --git a/shutit.html.markdown b/shutit.html.markdown
new file mode 100644
index 00000000..67d7a4b5
--- /dev/null
+++ b/shutit.html.markdown
@@ -0,0 +1,318 @@
+---
+category: tool
+tool: ShutIt
+contributors:
+ - ["Ian Miell", "http://ian.meirionconsulting.tk"]
+filename: learnshutit.html
+---
+
+## ShutIt
+
+ShutIt is an shell automation framework designed to be easy to use.
+
+It is a wrapper around a Python-based expect clone (pexpect).
+
+You can look at it as 'expect without the pain'.
+
+It is available as a pip install.
+
+## Hello World
+
+Starting with the simplest example. Create a file called example.py:
+
+```python
+
+import shutit
+session = shutit.create_session('bash')
+session.send('echo Hello World', echo=True)
+```
+
+Running this with:
+
+```bash
+python example.py
+```
+
+outputs:
+
+```bash
+$ python example.py
+echo "Hello World"
+echo "Hello World"
+Hello World
+Ians-MacBook-Air.local:ORIGIN_ENV:RhuebR2T#
+```
+
+The first argument to 'send' is the command you want to run. The 'echo'
+argument outputs the terminal interactions. By default ShutIt is silent.
+
+'send' takes care of all the messing around with prompts and 'expects' that
+you might be familiar with from expect.
+
+
+## Log Into a Server
+
+Let's say you want to log into a server and run a command. Change example.py
+to:
+
+```python
+import shutit
+session = shutit.create_session('bash')
+session.login('ssh you@example.com', user='you', password='mypassword')
+session.send('hostname', echo=True)
+session.logout()
+```
+
+which will log you into your server (if you replace with your details) and
+output the hostname.
+
+```
+$ python example.py
+hostname
+hostname
+example.com
+example.com:cgoIsdVv:heDa77HB#
+```
+
+Obviously that's insecure! Instead you can run:
+
+```python
+import shutit
+session = shutit.create_session('bash')
+password = session.get_input('', ispass=True)
+session.login('ssh you@example.com', user='you', password=password)
+session.send('hostname', echo=True)
+session.logout()
+```
+
+which forces you to input the password:
+
+```
+$ python example.py
+Input Secret:
+hostname
+hostname
+example.com
+example.com:cgoIsdVv:heDa77HB#
+```
+
+Again, the 'login' method handles the changing prompt from a login. You give
+ShutIt the login command, the user you expect to log in as, and a password
+(if needed), and ShutIt takes care of the rest.
+
+'logout' handles the ending of a 'login', handling any changes to the prompt
+for you.
+
+## Log Into Multiple Servers
+
+Let's say you have a server farm of two servers, and want to log onto both.
+Just create two sessions and run similar login and send commands:
+
+```python
+import shutit
+session1 = shutit.create_session('bash')
+session2 = shutit.create_session('bash')
+password1 = session1.get_input('Password for server1', ispass=True)
+password2 = session2.get_input('Password for server2', ispass=True)
+session1.login('ssh you@one.example.com', user='you', password=password1)
+session2.login('ssh you@two.example.com', user='you', password=password2)
+session1.send('hostname', echo=True)
+session2.send('hostname', echo=True)
+session1.logout()
+session2.logout()
+```
+
+would output:
+
+```bash
+$ python example.py
+Password for server1
+Input Secret:
+
+Password for server2
+Input Secret:
+hostname
+hostname
+one.example.com
+one.example.com:Fnh2pyFj:qkrsmUNs# hostname
+hostname
+two.example.com
+two.example.com:Gl2lldEo:D3FavQjA#
+```
+
+## Example: Monitor Multiple Servers
+
+We can turn the above into a simple monitoring tool by adding some logic to
+examine the output of a command:
+
+```python
+import shutit
+capacity_command="""df / | awk '{print $5}' | tail -1 | sed s/[^0-9]//"""
+session1 = shutit.create_session('bash')
+session2 = shutit.create_session('bash')
+password1 = session.get_input('Password for server1', ispass=True)
+password2 = session.get_input('Password for server2', ispass=True)
+session1.login('ssh you@one.example.com', user='you', password=password1)
+session2.login('ssh you@two.example.com', user='you', password=password2)
+capacity = session1.send_and_get_output(capacity_command)
+if int(capacity) < 10:
+ print('RUNNING OUT OF SPACE ON server1!')
+capacity = session2.send_and_get_output(capacity_command)
+if int(capacity) < 10:
+ print('RUNNING OUT OF SPACE ON server2!')
+session1.logout()
+session2.logout()
+```
+
+Here you use the 'send\_and\_get\_output' method to retrieve the output of the
+capacity command (df).
+
+There are much more elegant ways to do the above (e.g. have a dictionary of the
+servers to iterate over), but it's up to you how clever you need the Python to
+be.
+
+
+## More Intricate IO - Expecting
+
+Let's say you have an interaction with an interactive command line application
+you want to automate. Here we will use telnet as a trivial example:
+
+```python
+import shutit
+session = shutit.create_session('bash')
+session.send('telnet', expect='elnet>', echo=True)
+session.send('open google.com 80', expect='scape character', echo=True)
+session.send('GET /', echo=True, check_exit=False)
+session.logout()
+```
+
+Note the 'expect' argument. You only need to give a subset of telnet's
+prompt to match and continue.
+
+Note also the 'check\_exit' argument in the above, which is new. We'll come back
+to that. The output of the above is:
+
+```bash
+$ python example.py
+telnet
+telnet> open google.com 80
+Trying 216.58.214.14...
+Connected to google.com.
+Escape character is '^]'.
+GET /
+HTTP/1.0 302 Found
+Cache-Control: private
+Content-Type: text/html; charset=UTF-8
+Referrer-Policy: no-referrer
+Location: http://www.google.co.uk/?gfe_rd=cr&ei=huczWcj3GfTW8gfq0paQDA
+Content-Length: 261
+Date: Sun, 04 Jun 2017 10:57:10 GMT
+
+<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
+<TITLE>302 Moved</TITLE></HEAD><BODY>
+<H1>302 Moved</H1>
+The document has moved
+<A HREF="http://www.google.co.uk/?gfe_rd=cr&amp;ei=huczWcj3GfTW8gfq0paQDA">
+here
+</A>.
+</BODY></HTML>
+Connection closed by foreign host.
+```
+
+Now back to 'check\_exit=False'. Since the telnet command returns a failure exit
+code (1) and we don't want the script to fail, you set 'check\_exit=False' to
+let ShutIt know you don't care about the exit code.
+
+If you didn't pass that argument in, ShutIt gives you an interactive terminal
+if there is a terminal to communicate with. This is called a 'pause point'.
+
+
+## Pause Points
+
+You can trigger a 'pause point' at any point by calling
+
+```python
+[...]
+session.pause_point('This is a pause point')
+[...]
+```
+
+within your script, and then continue with the script by hitting CTRL and ']'
+at the same time. This is great for debugging: add a pause point, have a look
+around, then continue. Try this:
+
+```python
+import shutit
+session = shutit.create_session('bash')
+session.pause_point('Have a look around!')
+session.send('echo "Did you enjoy your pause point?"', echo=True)
+```
+
+with output like this:
+
+```bash
+$ python example.py
+Have a look around!
+
+Ians-Air.home:ORIGIN_ENV:I00LA1Mq# bash
+imiell@Ians-Air:/space/git/shutit ⑂ master + 
+CTRL-] caught, continuing with run...
+2017-06-05 15:12:33,577 INFO: Sending: exit
+2017-06-05 15:12:33,633 INFO: Output (squashed): exitexitIans-Air.home:ORIGIN_ENV:I00LA1Mq# [...]
+echo "Did you enjoy your pause point?"
+echo "Did you enjoy your pause point?"
+Did you enjoy your pause point?
+Ians-Air.home:ORIGIN_ENV:I00LA1Mq#
+```
+
+
+## More Intricate IO - Backgrounding
+
+Returning to our 'monitoring multiple servers' example, let's imagine we
+have a long-running task that we want to run on each server. By default, ShutIt
+works serially which would take a long time. But we can run tasks in the
+background to speed things up.
+
+Here you can try an example with the trivial command: 'sleep 60'.
+
+
+```python
+import shutit
+import time
+long_command="""sleep 60"""
+session1 = shutit.create_session('bash')
+session2 = shutit.create_session('bash')
+password1 = session1.get_input('Password for server1', ispass=True)
+password2 = session2.get_input('Password for server2', ispass=True)
+session1.login('ssh you@one.example.com', user='you', password=password1)
+session2.login('ssh you@two.example.com', user='you', password=password2)
+start = time.time()
+session1.send(long_command, background=True)
+session2.send(long_command, background=True)
+print('That took: ' + str(time.time() - start) + ' seconds to fire')
+session1.wait()
+session2.wait()
+print('That took: ' + str(time.time() - start) + ' seconds to complete')
+```
+
+My laptop says it took 0.5 seconds to run fire those two commands, and then just
+over a minute to complete (using the 'wait' method).
+
+Again, this is trivial, but imagine you have hundreds of servers to manage like
+this and you can see the power it can bring in a few lines of code and one
+Python import.
+
+
+## Learn More
+
+There's a lot more that can be done with ShutIt.
+
+To learn more, see:
+
+[ShutIt](https://ianmiell.github.io/shutit/)
+[GitHub](https://github.com/ianmiell/shutit/blob/master/README.md)
+
+It's a broader automation framework, and the above is its 'standalone mode'.
+
+Feedback, feature requests, 'how do I?'s highly appreciated! Reach me at
+[@ianmiell](https://twitter.com/ianmiell)