649 lines
24 KiB
Python
649 lines
24 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import paramiko, json
|
|
import os, sys,traceback
|
|
from stat import S_ISDIR
|
|
from modules.pastafari2.models import tasks
|
|
#from modules.pastafari2.models.servers import ServerGroupTask
|
|
from modules.pastafari2.libraries.configtask import config_task
|
|
from paramecio2.libraries.i18n import I18n
|
|
from paramecio2.libraries.db.webmodel import WebModel
|
|
|
|
class Task:
|
|
|
|
#($server='', $ssh_user='root', $ssh_key_priv='./ssh/id_rsa', $ssh_key_password='', $ssh_path='leviathan', $mysql_conn=false)
|
|
|
|
def __init__(self, server, conn, remote_user='root', remote_password='', private_key='./ssh/id_rsa', password_key='', remote_path='pastafari2', task_id=0, data={}, port=22):
|
|
|
|
self.config=config_task
|
|
|
|
self.server=server
|
|
|
|
self.name_task=''
|
|
|
|
self.codename_task=''
|
|
|
|
self.description_task=''
|
|
|
|
self.txt_error=''
|
|
|
|
self.os_server=''
|
|
|
|
self.port=port
|
|
|
|
self.files=[]
|
|
|
|
# Format first array element is command with the interpreter, the task is agnostic, the files in os directory. The commands are setted with 750 permission.
|
|
# First element is the file, next elements are the arguments
|
|
|
|
self.commands_to_execute=[];
|
|
|
|
#THe files to delete
|
|
|
|
self.delete_files=[];
|
|
|
|
self.delete_directories=[];
|
|
|
|
#The id of the task in db
|
|
|
|
self.id=task_id
|
|
|
|
self.user=''
|
|
self.password=''
|
|
|
|
self.connection=conn
|
|
|
|
self.logtask=tasks.LogTask(self.connection)
|
|
self.resulttask=tasks.ResultTask(self.connection)
|
|
self.task=tasks.Task(self.connection)
|
|
self.taskdone=tasks.TaskDone(self.connection)
|
|
|
|
self.ssh=paramiko.SSHClient()
|
|
|
|
self.logtask.reset_require()
|
|
self.task.reset_require()
|
|
self.resulttask.reset_require()
|
|
|
|
self.one_time=False
|
|
|
|
self.version='1.0'
|
|
|
|
self.simultaneous=False
|
|
|
|
self.error_post_task=''
|
|
|
|
self.data=data
|
|
|
|
self.remote_user=remote_user
|
|
|
|
self.remote_password=remote_password
|
|
|
|
self.private_key=private_key
|
|
|
|
self.password_key=password_key
|
|
|
|
self.remote_path=remote_path
|
|
|
|
self.url_return=''
|
|
|
|
self.links=''
|
|
|
|
self.path_module='admin_app.pastafari2_dashboard'
|
|
|
|
"""
|
|
self.pre_task=None
|
|
|
|
self.error_task=None
|
|
|
|
self.post_task=None
|
|
"""
|
|
|
|
def prepare_connection(self):
|
|
|
|
#self.ssh.load_system_host_keys()
|
|
|
|
#Check if the unknown host keys are rejected or not
|
|
|
|
#if self.config.deny_missing_host_key == False:
|
|
|
|
#self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
# Check if in known_hosts
|
|
check_ssh_host= paramiko.hostkeys.HostKeys()
|
|
|
|
if not os.path.isfile(self.config.ssh_directory+'/known_hosts'):
|
|
f=open(self.config.ssh_directory+'/known_hosts', 'w')
|
|
f.write('')
|
|
f.close()
|
|
|
|
self.ssh.load_system_host_keys(self.config.ssh_directory+'/known_hosts')
|
|
|
|
check_ssh_host.load(self.config.ssh_directory+'/known_hosts')
|
|
|
|
host_key=self.ssh.get_host_keys()
|
|
|
|
add_host=False
|
|
|
|
rsa=None
|
|
if self.private_key!='':
|
|
rsa=paramiko.RSAKey.from_private_key_file(self.private_key, self.password_key)
|
|
|
|
if check_ssh_host.lookup(self.server)==None:
|
|
|
|
# Be tolerant for the first connect with hostkey policy
|
|
|
|
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
|
|
add_host=True
|
|
|
|
try:
|
|
|
|
self.ssh.connect(self.server, port=self.port, username=self.remote_user, password=self.remote_password, pkey=rsa, key_filename=None, timeout=None, allow_agent=True, look_for_keys=True, compress=False, sock=None, gss_auth=False, gss_kex=False, gss_deleg_creds=True, gss_host=None, banner_timeout=None)
|
|
|
|
if add_host:
|
|
host_key.save(self.config.ssh_directory+'/known_hosts')
|
|
|
|
except paramiko.SSHException as e:
|
|
self.txt_error="Error: cannot connect to the server SSHException\n"+str(e)
|
|
return False
|
|
|
|
except paramiko.AuthenticationException as e:
|
|
self.txt_error="Error: cannot connect to the server AuthenticationException \n"+str(e)
|
|
return False
|
|
|
|
except paramiko.BadHostKeyException as e:
|
|
self.txt_error="Error: cannot connect to the server BadHostKeyException\n"+str(e)
|
|
return False
|
|
|
|
except OSError as e:
|
|
self.txt_error="Error: cannot connect to the server OsError \n"+str(e)
|
|
return False
|
|
|
|
except:
|
|
self.txt_error="Error: cannot connect to the server Generic\n"+traceback.format_exc()
|
|
return False
|
|
|
|
#finally:
|
|
# self.ssh.close()
|
|
|
|
return True
|
|
|
|
|
|
def upload_files(self):
|
|
|
|
try:
|
|
|
|
sftp=self.ssh.open_sftp()
|
|
|
|
except:
|
|
|
|
self.txt_error='Sorry, error connecting to sftp: '+traceback.format_exc()
|
|
return False
|
|
|
|
c=len(self.files)
|
|
|
|
if c>0:
|
|
|
|
percent=100/c
|
|
|
|
progress=0
|
|
|
|
for f in self.files:
|
|
|
|
source_file=f[0]
|
|
|
|
source_file=source_file.replace('${os_server}', self.os_server)
|
|
|
|
permissions=f[1]
|
|
#dest_file=self.remote_path+'/'+source_file
|
|
dest_file=source_file
|
|
try:
|
|
|
|
if not os.path.isfile(source_file):
|
|
self.txt_error="Sorry, you don't have source file to upload "+source_file
|
|
return False
|
|
|
|
dir_file=os.path.dirname(dest_file)
|
|
|
|
parts_dir_file=dir_file.split('/')
|
|
|
|
# Create remote directory
|
|
|
|
try:
|
|
|
|
f_stat=sftp.stat(dir_file)
|
|
|
|
except IOError:
|
|
|
|
try:
|
|
|
|
final_path=''
|
|
|
|
for d in parts_dir_file:
|
|
|
|
final_path+=d+'/'
|
|
|
|
#print(self.remote_path+'/'+final_path)
|
|
|
|
try:
|
|
|
|
f_stat=sftp.stat(final_path)
|
|
|
|
except IOError:
|
|
|
|
sftp.mkdir(final_path)
|
|
|
|
except:
|
|
|
|
self.txt_error='Sorry, error creating the directories for the files: '+traceback.format_exc()
|
|
return False
|
|
|
|
# Upload file
|
|
|
|
try:
|
|
|
|
sftp.put(source_file, dest_file, callback=None, confirm=True)
|
|
|
|
sftp.chmod(dest_file, permissions)
|
|
|
|
progress+=percent
|
|
|
|
self.logtask.insert({'task_id': self.id, 'progress': progress, 'message': I18n.lang('pastafari', 'uploading_files', 'Uploading file: ')+source_file, 'error': 0, 'server': self.server})
|
|
|
|
except:
|
|
|
|
self.txt_error='Sorry, cannot upload file '+source_file+': '+traceback.format_exc()
|
|
return False
|
|
|
|
# Create directory recursively if not exists
|
|
|
|
except:
|
|
self.txt_error='Error: '+traceback.format_exc()
|
|
return False
|
|
|
|
self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': I18n.lang('pastafari', 'upload_successful', 'Files uploaded successfully...'), 'error': 0, 'server': self.server})
|
|
|
|
return True
|
|
|
|
def delete_files_and_dirs(self):
|
|
|
|
sftp=self.ssh.open_sftp()
|
|
|
|
c=len(self.delete_files)
|
|
|
|
if c>0:
|
|
|
|
percent=100/c
|
|
|
|
progress=0
|
|
|
|
for filepath in self.delete_files:
|
|
|
|
filepath=filepath.replace('${os_server}', self.os_server)
|
|
|
|
try:
|
|
|
|
#print(self.remote_path+'/'+filepath)
|
|
|
|
sftp.remove(filepath)
|
|
|
|
progress+=percent
|
|
|
|
self.logtask.insert({'task_id': self.id, 'progress': progress, 'message': I18n.lang('pastafari', 'cleaning_files', 'Cleaning file: ')+filepath, 'error': 0, 'server': self.server})
|
|
|
|
except IOError:
|
|
|
|
self.txt_error="Sorry, cannot remove file "+filepath+" from server."
|
|
|
|
return False
|
|
|
|
|
|
c=len(self.delete_directories)
|
|
|
|
if c>0:
|
|
|
|
percent=100/c
|
|
|
|
progress=0
|
|
|
|
for path in self.delete_directories:
|
|
|
|
try:
|
|
|
|
path=path.replace('${os_server}', self.os_server)
|
|
|
|
self.delete_dir(path, sftp)
|
|
|
|
progress+=percent
|
|
|
|
self.logtask.insert({'task_id': self.id, 'progress': progress, 'message': I18n.lang('pastafari', 'cleaning_directories', 'Cleaning directory: ')+path, 'error': 0, 'server': self.server})
|
|
|
|
|
|
except IOError:
|
|
|
|
self.txt_error+="Sorry, cannot remove directory "+path+" from server."
|
|
|
|
return False
|
|
|
|
|
|
return True
|
|
|
|
|
|
def isdir(self, path, sftp):
|
|
try:
|
|
return S_ISDIR(sftp.stat(path).st_mode)
|
|
except IOError:
|
|
return False
|
|
|
|
def delete_dir(self, path, sftp):
|
|
|
|
#path=self.config.remote_path+'/'+path
|
|
|
|
if path != "/":
|
|
|
|
files = sftp.listdir(path)
|
|
|
|
for f in files:
|
|
filepath = os.path.join(path, f)
|
|
try:
|
|
if self.isdir(filepath, sftp):
|
|
self.delete_dir(filepath, sftp)
|
|
else:
|
|
sftp.remove(filepath)
|
|
except IOError:
|
|
self.txt_error="Sorry, cannot remove "+filepath+" from server."
|
|
return False
|
|
|
|
sftp.rmdir(path)
|
|
|
|
return True
|
|
|
|
def exec(self):
|
|
|
|
# Get task
|
|
|
|
#self.id=task_id
|
|
|
|
self.task.reset_require()
|
|
|
|
self.task.valid_fields=['name_task', 'description_task', 'error', 'status', 'server']
|
|
|
|
self.logtask.valid_fields=self.logtask.fields.keys()
|
|
self.resulttask.valid_fields=self.resulttask.fields.keys()
|
|
|
|
if self.id==0:
|
|
|
|
# Insert the task
|
|
|
|
self.task.reset_require()
|
|
|
|
self.task.insert({'name_task': self.name_task, 'description_task': self.description_task, 'server': self.server})
|
|
|
|
self.id=self.task.insert_id()
|
|
|
|
if not self.prepare_connection():
|
|
#self.task.conditions=['WHERE id=%s', [self.id]]
|
|
#self.task.update({'error': 1, 'status': 1})
|
|
|
|
self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': self.txt_error, 'error': 1, 'status': 1, 'server': self.server})
|
|
|
|
self.make_error_task()
|
|
|
|
return False
|
|
|
|
# Pre task
|
|
|
|
if hasattr(self, 'pre_task'):
|
|
self.logtask.insert({'task_id': self.id, 'progress': 0, 'message': I18n.lang('pastafari', 'pre_tasks', 'Begin pre tasks execution...'), 'error': 0, 'status': 0, 'server': self.server})
|
|
|
|
if self.pre_task():
|
|
self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': I18n.lang('pastafari', 'pre_tasks_executed', 'Pre tasks executed successfully...'), 'error': 0, 'status': 0, 'server': self.server})
|
|
else:
|
|
#self.logtask.set_conditions('where id=%s', [last_log_id])
|
|
|
|
self.logtask.insert({'progress': 100, 'error': 1, 'message': "Error executing pre task "+self.txt_error, 'status': 1, 'server': self.server, 'task_id': self.id})
|
|
|
|
self.make_error_task()
|
|
|
|
return False
|
|
|
|
#Check if script was executed
|
|
|
|
if self.codename_task!='':
|
|
|
|
if self.one_time==True:
|
|
|
|
with self.ssh.open_sftp() as sftp:
|
|
|
|
try:
|
|
|
|
with sftp.file(self.remote_path+'/tasks/'+self.codename_task) as f:
|
|
version=f.read()
|
|
version=version.decode('utf-8').strip()
|
|
|
|
if version==self.version:
|
|
#self.task.conditions=['WHERE id=%s', [self.id]]
|
|
#self.task.update({'error': 0, 'status': 1})
|
|
|
|
self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': 'This script was executed correctly in this server', 'error': 0, 'status': 1, 'server': self.server})
|
|
|
|
return True
|
|
|
|
except IOError:
|
|
# It was not executed
|
|
pass
|
|
|
|
if not self.upload_files():
|
|
#self.task.conditions=['WHERE id=%s', [self.id]]
|
|
#self.task.update({'error': 1, 'status': 1})
|
|
|
|
self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': self.txt_error, 'error': 1, 'status': 1, 'server': self.server})
|
|
|
|
self.make_error_task()
|
|
|
|
return False
|
|
|
|
self.logtask.insert({'task_id': self.id, 'progress': 0, 'message': 'Executing commands...', 'error': 0, 'status': 0, 'server': self.server})
|
|
|
|
# Execute commands
|
|
|
|
json_code=[]
|
|
|
|
for c in self.commands_to_execute:
|
|
|
|
try:
|
|
command=c[0]
|
|
|
|
command=command.replace('${os_server}', self.os_server)
|
|
|
|
arguments=c[1]
|
|
|
|
sudo_str=''
|
|
|
|
if len(c)==3:
|
|
sudo_str='sudo '
|
|
|
|
#, get_pty=True
|
|
stdin, stdout, stderr = self.ssh.exec_command(sudo_str+command+' '+arguments)
|
|
|
|
for line in stdout:
|
|
|
|
if line==None:
|
|
|
|
line="[]"
|
|
|
|
line=line.strip()
|
|
|
|
try:
|
|
json_code=json.loads(line)
|
|
|
|
if not 'progress' in json_code or not 'message' in json_code or not 'error' in json_code:
|
|
#self.task.conditions=['WHERE id=%s', [self.id]]
|
|
#self.task.update({'error': 1, 'status': 1})
|
|
|
|
self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': 'Malformed json code: '+str(line).strip(), 'error': 1, 'status': 1, 'server': self.server})
|
|
self.make_error_task()
|
|
return False
|
|
|
|
else:
|
|
|
|
json_code['task_id']=self.id
|
|
|
|
json_code['server']=self.server
|
|
|
|
if 'result' in json_code:
|
|
#{'result': 1, 'message': {'username': 'hosting', 'uid': 1002, 'gid': 1002, 'home': '/srv/sites'}, 'error': 0, 'progress': 100, 'task_id': 483, 'server': '192.168.122.55'}
|
|
#json_code['message']=json.dumps(json_code['message'])
|
|
#print(json_code)
|
|
self.resulttask.insert(json_code)
|
|
#print(self.resulttask.show_errors())
|
|
else:
|
|
|
|
self.logtask.insert(json_code)
|
|
|
|
if json_code['error']==1:
|
|
self.make_error_task()
|
|
return False
|
|
|
|
|
|
|
|
except:
|
|
|
|
#self.task.conditions=['WHERE id=%s', [self.id]]
|
|
#self.task.update({'error': 0, 'status':0})
|
|
|
|
self.logtask.insert({'task_id': self.id, 'progress': 0, 'no_progress': 1, 'message': str(line).strip(), 'error': 0, 'status': 0, 'server': self.server})
|
|
|
|
#return False
|
|
|
|
last_log_id=self.logtask.insert_id()
|
|
|
|
if stdout.channel.recv_exit_status()>0:
|
|
#line=stdout.readlines()
|
|
#logging.warning(action.codename+" WARNING: "+line)
|
|
final_text='Error executing the command: %s' % command
|
|
|
|
#self.task.conditions=['WHERE id=%s', [self.id]]
|
|
#self.task.update({'error': 1, 'status': 1})
|
|
|
|
for line in stdout:
|
|
final_text+=' '+line
|
|
|
|
for line in stderr:
|
|
final_text+=' '+line
|
|
|
|
self.logtask.set_conditions('where id=%s', [last_log_id])
|
|
|
|
self.logtask.update({'progress': 100, 'error': 1, 'message': final_text, 'status': 1, 'server': self.server})
|
|
self.make_error_task()
|
|
return False
|
|
|
|
except:
|
|
|
|
#self.task.conditions=['WHERE id=%s', [self.id]]
|
|
#self.task.update({'error': 1, 'status': 1})
|
|
|
|
self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': traceback.format_exc(), 'error': 1, 'status': 1, 'server': self.server})
|
|
self.make_error_task()
|
|
return False
|
|
|
|
# Clean files
|
|
|
|
if not self.delete_files_and_dirs():
|
|
|
|
#self.task.conditions=['WHERE id=%s', [self.id]]
|
|
#self.task.update({'error': 1, 'status': 1})
|
|
|
|
self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': self.txt_error, 'error': 1, 'status': 1, 'server': self.server})
|
|
self.make_error_task()
|
|
return False
|
|
|
|
#Upload files
|
|
|
|
#self.ssh.close()
|
|
|
|
# FInish task
|
|
|
|
#Put this version how executed
|
|
|
|
if self.codename_task!='':
|
|
|
|
if self.one_time==True:
|
|
|
|
with self.ssh.open_sftp() as sftp:
|
|
|
|
try:
|
|
path_check=self.remote_path+'/tasks/'
|
|
|
|
f_stat=sftp.stat(path_check)
|
|
|
|
except IOError:
|
|
|
|
sftp.mkdir(path_check)
|
|
|
|
with sftp.file(path_check+self.codename_task, 'w') as f:
|
|
f.write(self.version)
|
|
|
|
if hasattr(self, 'post_task'):
|
|
self.logtask.insert({'task_id': self.id, 'progress': 0, 'message': I18n.lang('pastafari', 'post_tasks', 'Post tasks executing...'), 'error': 0, 'status': 0, 'server': self.server})
|
|
|
|
if self.post_task():
|
|
self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': I18n.lang('pastafari', 'post_tasks_executed', 'Post tasks executed successfully...'), 'error': 0, 'status': 0, 'server': self.server})
|
|
else:
|
|
#self.logtask.set_conditions('where id=%s', [last_log_id])
|
|
|
|
#self.logtask.update({'progress': 100, 'error': 1, 'message': "Error executing post task", 'status': 1, 'server': self.server})
|
|
|
|
self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': I18n.lang('pastafari', 'error_post_tasks_executed', 'Error executing post task -> '+self.error_post_task), 'error': 1, 'status': 1, 'server': self.server})
|
|
self.make_error_task()
|
|
return False
|
|
|
|
|
|
if 'progress' in json_code:
|
|
#if json_code['progress']!=100:
|
|
self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': I18n.lang('pastafari', 'finished_successfully', 'All tasks done successfully...'), 'error': 0, 'status': 1, 'server': self.server})
|
|
else:
|
|
self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': I18n.lang('pastafari', 'finished_successfully', 'All tasks done successfully...'), 'error': 0, 'status': 1, 'server': self.server})
|
|
|
|
# Add
|
|
self.taskdone.create_forms()
|
|
|
|
self.taskdone.insert({'name_task': self.codename_task, 'ip': self.server})
|
|
|
|
#self.task.conditions=['WHERE id=%s', [self.id]]
|
|
#self.task.update({'error': 0, 'status': 1})
|
|
|
|
#connection.close()
|
|
return True
|
|
|
|
def make_error_task(self):
|
|
|
|
if hasattr(self, 'error_task'):
|
|
self.logtask.insert({'task_id': self.id, 'progress': 0, 'message': I18n.lang('pastafari', 'error_tasks', 'Error tasks executing...'), 'error': 0, 'status': 0, 'server': self.server})
|
|
|
|
if self.error_task():
|
|
self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': I18n.lang('pastafari', 'error_tasks_executed', 'Sorry, you have an error, please, review the log...'), 'error': 1, 'status': 1, 'server': self.server})
|
|
else:
|
|
self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': I18n.lang('pastafari', 'error_tasks_executed', 'Error Post task cannot be executed...'), 'error': 1, 'status': 1, 'server': self.server})
|
|
|
|
return False
|
|
|
|
def __del__(self):
|
|
|
|
#self.connection.close()
|
|
|
|
if self.ssh!=None:
|
|
self.ssh.close()
|
|
|
|
def __enter__(self):
|
|
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_value, tb):
|
|
|
|
if exc_type is not None:
|
|
traceback.print_exception(exc_type, exc_value, tb)
|
|
|
|
return True
|
|
|
|
|
|
|