Skip to content

Commit ce091bb

Browse files
ThePrezkadler
authored andcommitted
Add SSH transport (#32)
Signed-off-by: Jesse Gorzinski <jgorzinski@gmail.com>
1 parent e6aeeb6 commit ce091bb

File tree

5 files changed

+112
-0
lines changed

5 files changed

+112
-0
lines changed

docs/api.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ Database Transport
7373
:members:
7474
:inherited-members:
7575

76+
SSH Transport
77+
~~~~~~~~~~~~~~~~~~
78+
79+
.. autoclass:: SshTransport
80+
:members:
81+
:inherited-members:
82+
7683

7784
Direct Memory Transport
7885
~~~~~~~~~~~~~~~~~~~~~~~

samples/config.py.sample

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ Transports:
2525
3) XMLSERVICE http/rest/web call (Apache job)
2626
from itoolkit.transport import HttpTransport
2727
itransport = HttpTransport(url, user, password)
28+
29+
4) XMLService ssh call
30+
import paramiko
31+
ssh = paramiko.SSHClient()
32+
ssh.set_missing_host_key_policy(paramiko.WarningPolicy())
33+
ssh.connect("hostname", username="username", password="password")
34+
itransport = SshTransport(ssh)
2835
"""
2936
from itoolkit.transport import DirectTransport
3037
itransport = DirectTransport()

src/itoolkit/README

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Transport classes:
2929
class DirectTransport: Transport XMLSERVICE direct job call (within job/process calls).
3030
class DatabaseTransport: Transport XMLSERVICE calls over database connection.
3131
class HttpTransport: Transport XMLSERVICE calls over standard HTTP rest.
32+
class SshTransport: Transport XMLSERVICE calls over SSH.
3233

3334
XMLSERVICE classes:
3435
Base:

src/itoolkit/transport/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
from .database import DatabaseTransport
44
from .direct import DirectTransport
55
from .http import HttpTransport
6+
from .ssh import SshTransport
67

78
__all__ = [
89
'XmlServiceTransport',
910
'DatabaseTransport',
1011
'DirectTransport',
1112
'HttpTransport',
13+
'SshTransport',
1214
]

src/itoolkit/transport/ssh.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
XMLSERVICE ssh call (ssh job, often in QUSRWRK)
4+
5+
License:
6+
BSD (LICENSE)
7+
-- or --
8+
http://yips.idevcloud.com/wiki/index.php/XMLService/LicenseXMLService
9+
10+
Example:
11+
from itoolkit.transport import SshTransport
12+
import paramiko
13+
ssh = paramiko.SSHClient()
14+
# configure paramiko. Using only WarningPolicy() is not secure
15+
ssh.set_missing_host_key_policy(paramiko.WarningPolicy())
16+
ssh.connect(host, username="linux", password="linux1")
17+
itransport = SshTransport(ssh)
18+
19+
Notes:
20+
1) Always uses XMLSERVICE shipped in the QXMLSERV library
21+
2) does not disconnect the given SSHClient object when finished
22+
23+
"""
24+
from .base import XmlServiceTransport
25+
26+
__all__ = [
27+
'SshTransport'
28+
]
29+
30+
31+
class SshTransport(XmlServiceTransport):
32+
"""
33+
Transport XMLSERVICE calls over SSH connection.
34+
35+
Args:
36+
sshclient (paramiko.SSHClient): connected and authenticated connection
37+
38+
Example:
39+
from itoolkit.transport import SshTransport
40+
import paramiko
41+
ssh = paramiko.SSHClient()
42+
# configure paramiko. Using only WarningPolicy() is not secure
43+
ssh.set_missing_host_key_policy(paramiko.WarningPolicy())
44+
ssh.connect(host, username="linux", password="linux1")
45+
itransport = SshTransport(ssh)
46+
47+
Returns:
48+
(obj)
49+
"""
50+
51+
def __init__(self, sshclient=None):
52+
# TODO: allow connection to be materialized from IBM Cloud deployments
53+
if not hasattr(sshclient, "exec_command"):
54+
raise Exception("An instance of paramiko.SSHClient is required")
55+
self.conn = sshclient
56+
57+
def call(self, tk):
58+
"""Call xmlservice with accumulated input XML.
59+
60+
Args:
61+
tk - iToolkit object
62+
63+
Returns:
64+
xml
65+
"""
66+
command = "/QOpenSys/pkgs/bin/xmlservice-cli"
67+
stdin, stdout, stderr = self.conn.exec_command(command)
68+
xml_in = tk.xml_in()
69+
stdin.write(xml_in.encode())
70+
stdin.flush()
71+
stdin.channel.shutdown_write()
72+
73+
# rather than doing all this loop-de-loop, we could instead just use
74+
# a single call to stdout.readlines(), but there is a remote
75+
# possibility that the process is hanging writing to a filled-up
76+
# stderr pipe. So, we read a little from both until we're all done
77+
err_out = b""
78+
xml_out = b""
79+
blockSize = 64 # arbitrary
80+
while not stderr.closed or not stdout.closed:
81+
if not stderr.closed:
82+
newData = stderr.read(blockSize)
83+
if not newData:
84+
stderr.close() # reaching EOF doesn't implicitly close
85+
else:
86+
err_out += newData
87+
if not stdout.closed:
88+
newData = stdout.read(blockSize)
89+
if not newData:
90+
stdout.close() # reaching EOF doesn't implicitly close
91+
else:
92+
xml_out += newData
93+
stdout.channel.close()
94+
stderr.channel.close()
95+
return xml_out

0 commit comments

Comments
 (0)