Skip to content

Commit cef72e7

Browse files
author
Alex Kwiatkowski
committed
export ControlMaster class from root package
1 parent 79dc948 commit cef72e7

File tree

4 files changed

+204
-177
lines changed

4 files changed

+204
-177
lines changed

README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# pycm
22

3-
Simple wrapper around ssh implement put, get, exe via controlmaster session.
3+
Simple wrapper around ssh implementing put, get, exe via [ControlMaster](https://en.wikibooks.org/wiki/OpenSSH/Cookbook/Multiplexing) multiplexing.
44

5-
Generaly used for same tasks as paramiko or fabric
6-
but act as real user and so better handle complex ssh_config files
5+
Generaly used for same tasks as [paramiko](https://github.com/paramiko/paramiko) or [fabric](https://github.com/fabric/fabric)
6+
but act as real user and so better handle complex [ssh_config](https://linux.die.net/man/5/ssh_config) files
77
with long redirect chains especially
88

99
## Usage
@@ -13,14 +13,14 @@ with long redirect chains especially
1313
```
1414

1515
```python
16-
from pycm import controlmaster
17-
18-
customer = controlmaster.controlmaster('customhost')
19-
customer.connect()
20-
customer.put(src,dst)
21-
customer.exe(cmd)
22-
customer.get(src,dst)
23-
customer.disconnect()
16+
from pycm import ControlMaster
17+
18+
ssh = ControlMaster("customhost")
19+
ssh.connect()
20+
ssh.put(src,dst)
21+
ssh.exe(cmd)
22+
ssh.get(src,dst)
23+
ssh.disconnect()
2424
```
2525

2626
## TODO

pycm/__init__.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import sys
2+
3+
from pycm.controlmaster import ControlMaster
4+
5+
__all__ = [
6+
"ControlMaster",
7+
]
8+
9+
if __name__ == "__main__":
10+
assert len(sys.argv) > 1
11+
host = sys.argv[1]
12+
if len(sys.argv) > 2:
13+
debug = sys.argv[2] in ["True", "true", "1"]
14+
else:
15+
debug = False
16+
ssh = ControlMaster(host, debug=debug)
17+
ssh.connect()
18+
ssh.exe("uptime")
19+
ssh.disconnect()

pycm/control_master.py

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import sys
2+
import os
3+
import string
4+
import random
5+
import subprocess
6+
import threading
7+
import time
8+
import signal
9+
10+
11+
class ControlMaster:
12+
host = ""
13+
status = ""
14+
master_socket = ""
15+
controldir = ""
16+
debug = False
17+
masterpid = 0
18+
cmdpid = 0
19+
20+
def __init__(self, hostname, master_socket="", debug=False):
21+
self.ifdebug("in init")
22+
self.host = hostname
23+
self.check_control_dir()
24+
self.stdout = ""
25+
self.stderr = ""
26+
if master_socket == "":
27+
rnd = "".join(random.choice(string.ascii_letters) for i in range(10))
28+
self.master_socket = self.controldir + "/" + rnd
29+
else:
30+
self.master_socket = self.controldir + "/" + master_socket
31+
self.debug = debug
32+
33+
def ifdebug(self, args):
34+
if self.debug:
35+
print(args)
36+
37+
def cmd(self, args):
38+
self.ifdebug("in cmd: executing %s" % args)
39+
try:
40+
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
41+
self.cmdpid = p.pid
42+
retcode = p.wait()
43+
self.stdout = p.communicate()[0]
44+
if retcode < 0:
45+
print("in cmd: Child was terminated ", -retcode, file=sys.stderr)
46+
except OSError as e:
47+
print("in cmd: Execution failed: ", e, file=sys.stderr)
48+
return retcode
49+
50+
def checkauth(self):
51+
self.ifdebug("in checkauth")
52+
if not os.path.exists(self.master_socket):
53+
self.ifdebug("in checkauth: FAIL: NOFILE")
54+
return False
55+
status = self.cmd(["ssh", "-S", self.master_socket, "-O", "check", "go"])
56+
if status != 0:
57+
self.ifdebug("in checkauth: FAIL: NOCONN")
58+
return False
59+
return True
60+
61+
def connect(self):
62+
self.ifdebug("in connect")
63+
if self.checkauth():
64+
return True
65+
threading.Thread(
66+
target=self.cmd, args=(["ssh", "-fNMS", self.master_socket, self.host],)
67+
).start()
68+
print("connecting", self.host)
69+
for i in range(0, 10):
70+
if self.checkauth():
71+
self.masterpid = self.cmdpid
72+
print("Success")
73+
return True
74+
print(".")
75+
sys.stdout.flush()
76+
time.sleep(1)
77+
print("Fail")
78+
self.masterpid = self.cmdpid
79+
print("masterpid = %s" % self.masterpid)
80+
if self.masterpid > 0:
81+
try:
82+
os.kill(self.masterpid, signal.SIGTERM)
83+
except OSError:
84+
pass
85+
return False
86+
87+
def disconnect(self):
88+
self.ifdebug("in disconnect")
89+
if not self.checkauth():
90+
return True
91+
status = self.cmd(["ssh", "-S", self.master_socket, "-O", "exit", "go"])
92+
if status == 0:
93+
return True
94+
else:
95+
print("in disconnect: Fail : %s" % status)
96+
return False
97+
98+
def put(self, src, dst):
99+
if not self.checkauth():
100+
return False
101+
self.ifdebug("in put")
102+
status = self.cmd(
103+
["scp", "-o controlpath=" + self.master_socket, src, "go:" + dst]
104+
)
105+
if status == 0:
106+
return True
107+
else:
108+
print("in put: Fail : %s" % status)
109+
return False
110+
111+
def get(self, src, dst):
112+
if not self.checkauth():
113+
return False
114+
self.ifdebug("in get")
115+
status = self.cmd(
116+
["scp", "-o controlpath=" + self.master_socket, "go:" + src, dst]
117+
)
118+
if status == 0:
119+
return True
120+
else:
121+
print("in get: Fail : %s" % status)
122+
return False
123+
124+
def rput(self):
125+
if not self.checkauth():
126+
return False
127+
self.ifdebug("in rput")
128+
status = self.cmd(
129+
["scp", "-r", "-o controlpath=" + self.master_socket, src, "go:" + dst]
130+
)
131+
if status == 0:
132+
return True
133+
else:
134+
print("in rput: Fail : %s" % status)
135+
return False
136+
137+
def rget(self):
138+
if not self.checkauth():
139+
return False
140+
self.ifdebug("in rget")
141+
status = self.cmd(
142+
["scp", "-r", "-o controlpath=" + self.master_socket, "go:" + src, dst]
143+
)
144+
if status == 0:
145+
return True
146+
else:
147+
print("in rget: Fail : %s" % status)
148+
return False
149+
150+
def exe(self, command):
151+
self.ifdebug("in exe")
152+
if not self.checkauth():
153+
return False
154+
status = self.cmd(["ssh", "-S", self.master_socket, "go", command])
155+
if status == 0:
156+
print(self.stdout)
157+
return status
158+
else:
159+
print("in exe: Fail : %s" % status)
160+
return False
161+
162+
def check_control_dir(self, controldir=os.environ["HOME"] + "/.controlmaster"):
163+
self.ifdebug("in check_control_dir : controldir = %s" % controldir)
164+
if not os.path.isdir(controldir):
165+
os.makedirs(controldir)
166+
self.controldir = controldir
167+
168+
def create_master_socket(self):
169+
self.ifdebug("in create_master_socket")
170+
171+
172+
__all__ = [
173+
"ControlMaster",
174+
]

pycm/controlmaster.py

Lines changed: 0 additions & 166 deletions
This file was deleted.

0 commit comments

Comments
 (0)