Commit 6f83dcb5 authored by Björn's avatar Björn

adding #23 #22 #21 #17

parent 2fdd68bf
[default]
sources=
exclude=
remote_host=
remote_target=
source=
includes=
excludes=
namespace=
gpg_key_recipient=
gpg_passphrase=
remote_directory=
remote_host=
remote_target=
remote_port=
remote_ftp_port=
remove_older_than=
remove_all_but_n_full=
remove_all_inc_of_but_n_full=
[mysql]
username=
password=
temp=
mysql_username=
mysql_password=
mysql_temp=
[email]
smpt_server=
smtp_server=
smtp_port=
smpt_username=
smtp_username=
smtp_password=
smtp_auth_mode=
smtp_encryption=
......
.config
.env*
!.env.example
__pycache__
getpass
......@@ -7,7 +7,7 @@ supports rsync to upload and download.
```bash
sudo apt install duplicity python3-pip
pip3 install python-gnupg configparser paramiko termcolor
pip3 install python-gnupg python-dotenv paramiko termcolor
```
## Config
......
......@@ -20,7 +20,6 @@
#
import argparse
import configparser
import os
import subprocess
import paramiko
......@@ -49,10 +48,10 @@ class Backup(Config):
parser = argparse.ArgumentParser()
# define arguments
parser.add_argument('command', choices=['full', 'incremental', 'restore', 'status', 'files'], help='full|incremental|restore|status|files')
parser.add_argument('command', choices=['full', 'incremental', 'restore', 'status', 'files'], help='use full|incremental|restore|status|files')
# define options
parser.add_argument('-c', '--config', help='full path to config-file')
parser.add_argument('-e', '--env', help='name of environment')
# for restore
parser.add_argument('-d', '--destination', help='destination for files')
......@@ -75,7 +74,7 @@ class Backup(Config):
#
def _sendmail(self):
if (self._args.sendmail == True):
sendmail = Sendmail(self._logs, 'Backup / ' + self._args.command + ' / ' + platform.uname()[1], self._config)
sendmail = Sendmail(self._logs, 'Backup / ' + self._args.command + ' / ' + self._namespace)
sendmail.run()
#
......@@ -84,29 +83,35 @@ class Backup(Config):
#
def _includes(self):
includes = ''
result = ''
sources = self._config.get('default', 'sources')
sources = sources.split(',')
includes = os.getenv('include')
# adding includes to duplicity
for source in sources:
if (os.path.exists(source)):
if (len(includes) > 0):
includes += ' --include ' + source
else:
includes = source
else:
self._log('Error: "' + source + '" not found!', 'yellow')
if (includes):
includes = includes.split(',')
# adding includes to duplicity
for include in includes:
result += ' --include ' + include
return result
#
#
#
#
def _source(self):
source = os.getenv('source')
# if includes is empty
if (includes == ''):
self._log('Error: no sources are found!', 'yellow')
if (source == None):
self._log('Error: source not found!', 'yellow')
self._log('Backup Failed! Exit Script!', 'red')
self._sendmail()
exit()
return includes
return source
#
# generate string for source and adding all
......@@ -128,7 +133,7 @@ class Backup(Config):
for key in keys:
for uid in key['uids']:
if (uid.find(self._config.get('default', 'gpg_key_recipient')) > -1):
if (uid.find(os.getenv('gpg_key_recipient')) > -1):
gpg_key_id = key['keyid']
break
......@@ -145,51 +150,14 @@ class Backup(Config):
#
#
def _gpg_passphrase(self):
return 'PASSPHRASE="' + self._config.get('default', 'gpg_passphrase') + '"'
return 'PASSPHRASE="' + os.getenv('gpg_passphrase') + '"'
#
# create target with rsync
#
#
def _target(self):
return 'rsync://' + self._config.get('default', 'remote_host') + ':' + self._remote_port + '/' + self._config.get('default', 'remote_target')
#
# create command for sftp
#
# @param command
#
def _sftp_command(self, command):
return "echo \"" + command + "\" | sftp -P " + self._remote_port + " " + self._config.get('default', 'remote_host')
#
# create directory of target if not exists with sftp
#
#
def _create_target(self):
# split directories
directories = self._config.get('default', 'remote_target').split('/')
directory = ''
for index, current in enumerate(directories):
# if current is empty, that would happen on /home/user path, skip the first
if (current == ''):
continue
elif (index >= 1):
current = '/' + current
directory += current
# check if cd is possible on remote
sftp_command = self._sftp_command('cd ' + directory)
process = self._command(sftp_command)
error = process.stderr
# if directory not found, create with path new
if (error and error.find('No such file or directory') > -1):
self._command(self._sftp_command('mkdir ' + directory))
return 'rsync://' + os.getenv('remote_host') + ':' + self._remote_port + '/' + self._remote_target
#
# start
......@@ -197,10 +165,10 @@ class Backup(Config):
#
def _start(self):
self._create_target()
source = self._source()
includes = self._includes()
process = self._command(self._gpg_passphrase() + ' duplicity ' + self._args.command + ' ' + includes + ' ' + self._gpg_key_id() + ' ' + self._target())
process = self._command(self._gpg_passphrase() + ' duplicity ' + self._args.command + ' ' + source + ' ' + includes + self._gpg_key_id() + ' ' + self._target())
if (process.stdout.find('GPGError') > -1):
print(colored(process.stdout, 'yellow'))
......@@ -289,14 +257,14 @@ class Backup(Config):
option = None
if (self._config.has_option('default', 'remove_older_than')):
option = 'remove-older-than ' + self._config.get('default', 'remove_older_than')
if (os.getenv('remove_older_than')):
option = 'remove-older-than ' + os.getenv('remove_older_than')
if (self._config.has_option('default', 'remove_all_but_n_full')):
option = 'remove-all-but-n-full ' + self._config.get('default', 'remove_all_but_n_full')
if (os.getenv('remove_all_but_n_full')):
option = 'remove-all-but-n-full ' + os.getenv('remove_all_but_n_full')
if (self._config.has_option('default', 'remove_all_inc_of_but_n_full')):
option = 'remove-all-inc-of-but-n-full ' + self._config.get('default', 'remove_all_inc_of_but_n_full')
if (os.getenv('remove_all_inc_of_but_n_full')):
option = 'remove-all-inc-of-but-n-full ' + os.getenv('remove_all_inc_of_but_n_full')
if (option):
process = self._command('duplicity ' + option + ' --force ' + self._target())
......
......@@ -2,15 +2,14 @@
#
# dump mysql-databases
#
# usage: mysql.py [-h] [-c CONFIG] [-s] {all,clean}
# usage: mysql.py [-h] [-e ENV] [-s] {all,clean}
#
# positional arguments:
# {all,clean} all|clean
#
# optional arguments:
# -h, --help show this help message and exit
# -c CONFIG, --config CONFIG
# full path to config-file
# -e ENV, --env ENV full path to config-file
# - s, --sendmail send log with
#
# @author Björn Hase
......@@ -19,8 +18,6 @@
#
import argparse
import configparser
import subprocess
import os
from termcolor import colored
......@@ -44,7 +41,7 @@ class Mysqldump(Config):
parser.add_argument('command', choices=['all', 'clean'], help='all|clean')
# define options
parser.add_argument('-c', '--config', help='full path to config-file')
parser.add_argument('-e', '--env', help='name of environment')
# sending email for output and error message
parser.add_argument('-s', '--sendmail', help='send log with', action='store_true')
......@@ -55,13 +52,13 @@ class Mysqldump(Config):
# init parent
super().__init__()
self._username = self._config.get('mysql', 'username')
self._password = self._config.get('mysql', 'password')
self._host = self._config.get('mysql', 'host', fallback='localhost')
self._temp = self._config.get('mysql', 'temp', fallback=None)
self._username = os.getenv('mysql_username')
self._password = os.getenv('mysql_password')
self._host = os.getenv('mysql_host', 'localhost')
self._temp = os.getenv('mysql_temp')
# if temp-directory not set
if (not self._temp):
if (self._temp == None):
print(colored('Temp-Directory not set!', 'yellow'))
print(colored('Mysql Failed! Exit Script!', 'red'))
exit()
......@@ -88,8 +85,8 @@ class Mysqldump(Config):
date = now.strftime("%d_%m_%Y-%H_%M_%S")
sql = "SHOW DATABASES WHERE \`Database\` NOT REGEXP '(^mysql|_schema$)'"
command = 'mysql --user=' + self._username + ' --password=' + self._password + ' --host=' + self._host + ' -e "' + sql + '"'
sql = "SHOW DATABASES WHERE \`Database\` NOT REGEXP '(^mysql|_schema$|sys)'"
command = 'export MYSQL_PWD=' + self._password + ';mysql --user=' + self._username + ' --host=' + self._host + ' -e "' + sql + '" | awk \'{ print $1 }\''
process = self._command(command)
......@@ -147,7 +144,7 @@ class Mysqldump(Config):
# send mail
if (self._args.sendmail == True):
sendmail = Sendmail(self._logs, 'Mysql / ' + self._args.command + ' / ' + platform.uname()[1])
sendmail = Sendmail(self._logs, 'Mysql / ' + self._args.command + ' / ' + self._namespace)
sendmail.run()
......
......@@ -6,11 +6,13 @@
# @link https://gitlab.tentakelfabrik.de/tentakelfabrik/backup
#
import configparser
import os
import subprocess
import platform
from pathlib import Path
from termcolor import colored
from dotenv import load_dotenv
#
#
......@@ -27,27 +29,43 @@ class Config:
#
def __init__(self):
if (self._args.config is None):
config = '.config'
self._env_path = '.env'
if (self._args.env == None):
self._env = ''
else:
config = self._args.config
self._env = self._args.env
self._env_path = self._env_path + '.' + self._env
self._env_path = Path('.') / self._env_path
# check that config-file exits
if (os.path.exists(config) == False):
print(colored(config + ' not found!', 'yellow'))
if (os.path.exists(self._env_path) == False):
print(colored(self._env_path + ' not found!', 'yellow'))
print(colored('Failed! Exit Script!', 'red'))
exit()
# check that config-file chmod is 0600
if (oct(os.stat(config).st_mode)[4:] != '0600'):
print(colored('chmod of ' + config + ' has to be 600', 'yellow'))
if (oct(os.stat(self._env_path).st_mode)[4:] != '0600'):
print(colored('chmod of ' + str(self._env_path) + ' has to be 600', 'yellow'))
print(colored('Failed! Exit Script!', 'red'))
exit()
self._config = configparser.RawConfigParser(allow_no_value=True)
self._config.read_file(open(config))
# loading env
load_dotenv(dotenv_path=self._env_path)
# namespace
if (os.getenv('namespace') == None):
self._namespace = platform.uname()[1]
if (self._env):
self._namespace = self._namespace + '-' + self._env
else:
self._namespace = os.getenv('namespace')
self._remote_port = self._config.get('default', 'remote_port', fallback='22')
self._remote_target = os.getenv('remote_target', self._namespace)
self._remote_port = str(os.getenv('remote_port', 22))
self._logs = ''
#
......
......@@ -34,10 +34,9 @@ class Sendmail:
# @param subject
# @param options { encrypt: True or False }
#
def __init__(self, message, subject, config):
def __init__(self, message, subject):
self._message = message
self._subject = subject
self._config = config
self._config_required = [
'smtp_server',
......@@ -71,7 +70,7 @@ class Sendmail:
# adding config values
for key in self._config_required:
if self._config.has_option('email', key) == False:
if os.getenv(key) == None:
print(colored('Environment variable "' + key + '" not found!', 'yellow'))
print(colored('Sendmail Failed! Exit Script!', 'red'))
exit()
......@@ -89,8 +88,8 @@ class Sendmail:
multipart.attach(body)
multipart.add_header('Subject', self._subject)
multipart.add_header('From', self._config.get('email', 'smtp_from'))
multipart.add_header('To', self._config.get('email', 'smtp_to'))
multipart.add_header('From', os.getenv('smtp_from'))
multipart.add_header('To', os.getenv('smtp_to'))
return multipart
......@@ -104,8 +103,8 @@ class Sendmail:
encrypted = MIMEBase('multipart', _subtype='encrypted', protocol='application/pgp-encrypted')
encrypted.add_header('Subject', self._subject)
encrypted.add_header('From', self._config.get('email', 'smtp_from'))
encrypted.add_header('To', self._config.get('email', 'smtp_to'))
encrypted.add_header('From', os.getenv('smtp_from'))
encrypted.add_header('To', os.getenv('smtp_to'))
# adding headers
encrypted_header = email.message.Message()
......@@ -126,7 +125,7 @@ class Sendmail:
gpg = gnupg.GPG(gnupghome=gpgpath)
encrypted_gpg = gpg.encrypt(message.as_string(), self._config.get('email', 'smtp_to'))
encrypted_gpg = gpg.encrypt(message.as_string(), os.getenv('smtp_to'))
encrypted_string = str(encrypted_gpg)
# if string is empty, show status and end programm
......@@ -156,15 +155,15 @@ class Sendmail:
# Now send the message
try:
if self._config.get('email', 'smtp_encryption') == 'ssl' or self._config.get('email', 'smtp_encryption') == 'tls':
mailer = smtplib.SMTP_SSL(self._config.get('email', 'smtp_server'), self._config.get('email', 'smtp_port'))
if os.getenv('smtp_encryption') == 'ssl' or os.getenv('smtp_encryption') == 'tls':
mailer = smtplib.SMTP_SSL(os.getenv('smtp_server'), os.getenv('smtp_port'))
else:
mailer = smtplib.SMTP(self._config.get('email', 'smtp_server'), self._config.get('email', 'smtp_port'))
mailer = smtplib.SMTP(os.getenv('smtp_server'), os.getenv('smtp_port'))
except Exception as exception:
print(colored('SMTP Error!', 'yellow'))
print(colored('Backup Failed! Exit Script!', 'red'))
exit()
mailer.login(self._config.get('email', 'smtp_username'), self._config.get('email', 'smtp_password'))
mailer.sendmail(self._config.get('email', 'smtp_from'), self._config.get('email', 'smtp_to'), message.as_string())
mailer.login(os.getenv('smtp_username'), os.getenv('smtp_password'))
mailer.sendmail(os.getenv('smtp_from'), os.getenv('smtp_to'), message.as_string())
mailer.close()
......@@ -16,7 +16,6 @@
import getpass
import argparse
import configparser
import platform
import subprocess
import os
......@@ -47,7 +46,7 @@ class Storagebox(Config):
parser.add_argument('command', help='add|remove')
# define options
parser.add_argument('-c', '--config', help='full path to config-file')
parser.add_argument('-e', '--env', help='name of environment')
# parse arguments
self._args = parser.parse_args()
......@@ -55,16 +54,13 @@ class Storagebox(Config):
# init parent
super().__init__()
self._namespace = platform.uname()[1]
#
# create command for sftp
#
# @param command
#
def _sftp_command(self, command):
return "echo \"" + command + "\" | sftp -P " + self._remote_port + " " + self._config.get('default', 'remote_host')
return "echo \"" + command + "\" | sftp -P " + self._remote_port + " " + os.getenv('remote_host')
#
# call subprocess
......@@ -162,7 +158,6 @@ class Storagebox(Config):
os.remove(ssh_pub_file)
else:
print(colored('No SSH-Key found!', 'yellow'))
print(colored('No SSH-Key removed! Exit Script!', 'red'))
exit()
......@@ -173,7 +168,7 @@ class Storagebox(Config):
#
#
def _download(self):
command = 'sftp -P ' + self._remote_port + ' ' + self._config.get('default', 'remote_host') + ':.ssh/authorized_keys ' + self._homeDir()
command = 'sftp -P ' + str(self._remote_port) + ' ' + os.getenv('remote_host') + ':.ssh/authorized_keys ' + self._homeDir()
process = self._command(command)
if process.stdout.find('No route to host') > -1:
......@@ -198,6 +193,43 @@ class Storagebox(Config):
else:
print(colored(self._args.command.capitalize() + ' Completed!', 'green'))
#
# create command for sftp
#
# @param command
#
def _sftp_command(self, command):
return "echo \"" + command + "\" | sftp -P " + self._remote_port + " " + os.getenv('remote_host')
#
# create directory of target if not exists with sftp
#
#
def _create_target(self):
# split directories
directories = self._remote_target.split('/')
directory = ''
for index, current in enumerate(directories):
# if current is empty, that would happen on /home/user path, skip the first
if (current == ''):
continue
elif (index >= 1):
current = '/' + current
directory += current
# check if cd is possible on remote
sftp_command = self._sftp_command('cd ' + directory)
process = self._command(sftp_command)
error = process.stdout
# if directory not found, create with path new
if (error and error.find('No such file or directory') > -1):
self._command(self._sftp_command('mkdir ' + directory))
#
#
#
......@@ -212,7 +244,7 @@ class Storagebox(Config):
elif self._args.command == 'remove':
self._remove_ssh_key()
host = self._config.get('default', 'remote_host').split('@')[1]
host = os.getenv('remote_host').split('@')[1]
print('')
print(colored('Please ' + self._args.command + ' in ~/.config', 'yellow'))
......@@ -224,6 +256,7 @@ class Storagebox(Config):
print('')
self._upload()
self._create_target()
self._clean()
storagebox = Storagebox()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment