-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathsshdevice.py
234 lines (184 loc) · 6.41 KB
/
sshdevice.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import datetime
import paramiko
import base64
import os
import socket
from termcolor import colored
from pprint import pprint
try:
import interactive
except ImportError:
from . import interactive
class SSHDevice:
ip = ""
mac = ""
__name = ""
username = ""
password = ""
rsa = ""
status = ""
backup_file_base = ""
client = False
def __init__(self, ip="", mac="", name="", username="", password="", rsa="", status="", backup_file=""):
# Set minimal device data
self.ip = ip
self.mac = mac
self.name = name
self.username = username
self.password = password
self.rsa = rsa
self.status = status
self.backup_file_base = backup_file
# Use setters anf getters for properties needing expensive actions
@property
def name(self):
if not self.__name or self.__name == "":
self.getName()
return self.__name
@name.setter
def name(self, name):
self.__name = name
if name and name != "":
self.setBackupName()
@property
def backup_file(self):
if not self.__backup_file or self.__backup_file == "":
self.setBackupName()
return self.__backup_file
@backup_file.setter
def backup_file(self, backup_file):
self.__backup_file = backup_file
def getName(self):
raise NotImplementedError("Should have implemented `getName` method")
def setBackupName(self, backup_file=""):
'''Set backup filename'''
if backup_file:
self.backup_file_base = backup_file
# Set backup file name
if not self.backup_file_base:
today = datetime.date.today()
self.backup_file = "{name}.{date}.bkp".format(
name=self.name, date=today.strftime('%Y-%m-%d'))
else:
self.backup_file = self.backup_file_base
def login(self):
'''Open SSH connection only if it is not already opened'''
if self.client == False:
# Start SSH connection
self.client = paramiko.SSHClient()
self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Try login with user/password
try:
self.client.connect(self.ip, username=self.username, password=self.password,
timeout=5, allow_agent=False, look_for_keys=False)
except:
# Try login with RSA key
key = paramiko.RSAKey.from_private_key_file(self.rsa)
self.client.connect(
self.ip, username=self.username, timeout=5, pkey=key)
def logout(self):
'''Close SSH connection only if it is opened'''
if self.client != False:
self.client.close()
# Higiene
del self.client
self.client = False
def command(self, command):
'''Send command to device and return (stdin, stdout, stderr) streams tuple'''
self.login()
return self.client.exec_command(command, timeout=5)
def shell(self, *args, **kwargs):
'''Opens a TTY shell'''
self.login()
if 'self' in kwargs:
del kwargs['self']
chan = self.client.invoke_shell(*args, **kwargs)
interactive.interactive_shell(chan)
chan.close()
# Higiene
def __del__(self):
self.logout()
def __str__(self):
if self.mac:
return u"{} : {} - {}".format(self.name, self.mac, self.ip)
else:
return u"{} : {}".format(self.name, self.ip)
def backup_devices_list(devices, path):
'''Do backup on an ACDevice list'''
i = 1
failed = []
for device in devices:
# Debug:
# if i == 10:
# break
print(u"{index}.- {device}".format(index=i, device=str(device)))
# Debug:
# if i == 1:
# pprint(device.data)
# break
warning = ""
file = path + "/" + device.backup_file
if os.path.exists(file) and os.stat(file).st_size > 0:
print(
u" " + colored("[WARNING] Backup ja realitzat. Saltem.", 'yellow', attrs=['bold']))
continue
try:
device.backup(path)
except paramiko.ssh_exception.AuthenticationException as e:
warning = u"[WARNING] Credencials incorrectes! (" + str(e) + ")"
except paramiko.ssh_exception.NoValidConnectionsError as e:
warning = u"[WARNING] No es pot establir connexió al port 22! (" + str(
e) + ")"
except socket.timeout as e:
warning = u"[WARNING] Servidor no abastable! (" + str(e) + ")"
except KeyboardInterrupt as e:
raise e
except Exception as e:
warning = u"[WARNING] Excepció no gestionada: " + str(e)
finally:
device.logout()
if warning != "":
device.warning = warning
print(u" " + colored(warning, 'red', attrs=['bold']))
failed.append(device)
# Higiene
else:
del device
i += 1
return failed
def backup_devices(devices, path, retries=3):
# Ensure backup dir exists
print(u"Make dir: " + path)
os.makedirs(path, exist_ok=True)
failed = devices
ok = 0
while retries > 0:
total = len(failed)
# Do backup and get failed list
failed = backup_devices_list(failed, path)
# Sum non-failed to 'ok' counter
ok += total - len(failed)
# If list is empty, break while
if len(failed) == 0:
break
# Decrease counter
retries -= 1
if retries > 0:
print(colored(u"\nTornem a intentar amb les antenes que hagin fallat (queden {} intents, {} fallats)\n".format(
retries-1, len(failed)), 'white', attrs=['bold']))
# Print totals
print(u"\n")
print(colored(u"Descarregats {ok} backups".format(
ok=ok), 'green', attrs=['bold']))
print(colored(u"NO Descarregats {ko} backups".format(
ko=len(failed)), 'red', attrs=['bold']))
print(u"\n")
# Print failed
if len(failed) > 0:
print(u"Failed %d devices:" % (len(failed)))
for f in failed:
print(u" - {device}: {warning}".format(device=str(f),
warning=colored(f.warning, 'red', attrs=['bold'])))
print(u"\n")