blob: b9e325bb624451efd1c735dfafc5c4cbd251cd7c [file] [log] [blame]
#!/usr/bin/python
# *****************************************************************************
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# ******************************************************************************
from fabric.api import *
import argparse
import filecmp
import yaml
import sys
import os
parser = argparse.ArgumentParser(description="Restore script for DLab configs, keys, certs, jars & database")
parser.add_argument('--dlab_path', type=str, default='/opt/dlab/', help='Path to DLab. Default: /opt/dlab/')
parser.add_argument('--configs', type=str, default='all', help='Comma separated names of config files, like "security.yml", etc. Also available: skip. Default: all')
parser.add_argument('--keys', type=str, default='all', help='Comma separated names of keys, like "user_name.pub". Also available: skip. Default: all')
parser.add_argument('--certs', type=str, default='all', help='Comma separated names of SSL certificates and keys, like "dlab.crt", etc. Also available: skip. Default: all')
parser.add_argument('--jars', type=str, default='skip', help='Comma separated names of jar application, like "self-service", etc. Default: skip')
parser.add_argument('--db', action='store_true', default=False, help='Mongo DB. Key without arguments. Default: disable')
parser.add_argument('--file', type=str, default='', required=True, help='Full or relative path to backup file or folder. Required field')
parser.add_argument('--force', action='store_true', default=False, help='Force mode. Without any questions. Key without arguments. Default: disable')
args = parser.parse_args()
def ask(question):
if args.force:
return True
valid = {"": True, "yes": True, "y": True, "no": False, "n": False}
while True:
choice = raw_input("{0} {1} ".format(question, "[Y/yes/N/no]")).lower()
try:
if valid[choice]:
return True
else:
return False
except:
print("Incorrect answer. Try again...")
continue
def restore_prepare():
try:
if os.path.isfile(backup_file):
head, tail = os.path.split(args.file)
temp_folder = "/tmp/{}/".format(tail.split(".")[0])
if os.path.isdir(temp_folder):
print("Temporary folder with this backup already exist.")
print("Use folder path '{}' in --file key".format(temp_folder))
raise Exception
print("Backup acrhive will be unpacked to: {}".format(temp_folder))
local("mkdir {}".format(temp_folder))
local("tar -xf {0} -C {1}".format(backup_file, temp_folder))
elif os.path.isdir(backup_file):
temp_folder = backup_file
else:
print("Please, specify file or folder. Try --help for more details.")
raise Exception
print("Backup acrhive: {} contains following files (exclude logs):".format(backup_file))
local("find {} -not -name '*log'".format(temp_folder))
except Exception as err:
print("Failed to open backup.{}".format(str(err)))
sys.exit(1)
try:
if ask("Maybe you want to create backup of existing configuration before restoring?"):
with settings(hide('everything')):
print("Creating new backup...")
local("python backup.py --configs all --keys all --certs all --jar all --db")
except:
print("Failed to create new backup.")
sys.exit(1)
try:
if ask("Stop all services before restoring?"):
local("sudo supervisorctl stop all")
else:
raise Exception
except:
print("Failed to stop all services. Can not continue.")
sys.exit(1)
return temp_folder
def restore_configs():
try:
if not os.path.isdir("{0}{1}".format(temp_folder, conf_folder)):
print("Config files are not available in this backup.")
raise Exception
configs = list()
if args.configs == "all":
configs = [files for root, dirs, files in os.walk("{0}{1}".format(temp_folder, conf_folder))][0]
else:
configs = args.configs.split(",")
print("Restore configs: {}".format(configs))
if args.configs != "skip":
for filename in configs:
if not os.path.isfile("{0}{1}{2}".format(temp_folder, conf_folder, filename)):
print("Config {} are not available in this backup.".format(filename))
else:
if os.path.isfile("{0}{1}{2}".format(args.dlab_path, conf_folder, filename)):
backupfile = "{0}{1}{2}".format(temp_folder, conf_folder, filename)
destfile = "{0}{1}{2}".format(args.dlab_path, conf_folder, filename)
if not filecmp.cmp(backupfile, destfile):
if ask("Config {} was changed, rewrite it?".format(filename)):
local("cp -f {0} {1}".format(backupfile, destfile))
else:
print("Config {} was skipped.".format(destfile))
else:
print("Config {} was not changed. Skipped.".format(filename))
else:
print("Config {} does not exist. Creating.".format(filename))
local("cp {0}{1}{2} {3}{1}{2}".format(temp_folder, conf_folder, filename, args.dlab_path))
except:
print("Restore configs failed.")
def restore_keys():
try:
if not os.path.isdir("{}keys".format(temp_folder)):
print("Key files are not available in this backup.")
raise Exception
keys = list()
if args.keys == "all":
keys = [files for root, dirs, files in os.walk("{}keys".format(temp_folder))][0]
else:
keys = args.keys.split(",")
print("Restore keys: {}".format(keys))
if args.keys != "skip":
for filename in keys:
if not os.path.isfile("{0}keys/{1}".format(temp_folder, filename)):
print("Key {} are not available in this backup.".format(filename))
else:
if os.path.isfile("{0}{1}".format(keys_folder, filename)):
print("Key {} already exist.".format(filename))
if not filecmp.cmp("{0}keys/{1}".format(temp_folder, filename), "{0}{1}".format(keys_folder, filename)):
if ask("Key {} was changed, rewrite it?".format(filename)):
local("cp -f {0}keys/{2} {1}{2}".format(temp_folder, keys_folder, filename))
else:
print("Key {} was skipped.".format(filename))
else:
print("Key {} was not changed. Skipped.".format(filename))
else:
print("Key {} does not exist. Creating.".format(filename))
local("cp {0}keys/{2} {1}{2}".format(temp_folder, keys_folder, filename))
except:
print("Restore keys failed.")
def restore_certs():
try:
if not os.path.isdir("{}certs".format(temp_folder)):
print("Cert files are not available in this backup.")
raise Exception
certs = list()
if args.certs == "all":
certs = [files for root, dirs, files in os.walk("{}certs".format(temp_folder))][0]
else:
certs = args.certs.split(",")
print("Restore certs: {}".format(certs))
if args.certs != "skip":
for filename in certs:
if not os.path.isfile("{0}certs/{1}".format(temp_folder, filename)):
print("Cert {} are not available in this backup.".format(filename))
else:
if os.path.isfile("{0}{1}".format(certs_folder, filename)):
print("Cert {} already exist.".format(filename))
if not filecmp.cmp("{0}certs/{1}".format(temp_folder, filename), "{0}{1}".format(certs_folder, filename)):
if ask("Cert {} was changed, rewrite it?".format(filename)):
local("sudo cp -f {0}certs/{2} {1}{2}".format(temp_folder, certs_folder, filename))
local("sudo chown {0}:{0} {1}{2}".format("root", certs_folder, filename))
else:
print("Cert {} was skipped.".format(filename))
else:
print("Cert {} was not changed. Skipped.".format(filename))
else:
print("Cert {} does not exist. Creating.".format(filename))
local("sudo cp {0}certs/{2} {1}{2}".format(temp_folder, certs_folder, filename))
local("sudo chown {0}:{0} {1}{2}".format("root", certs_folder, filename))
except:
print("Restore certs failed.")
def restore_jars():
try:
if not os.path.isdir("{0}jars".format(temp_folder)):
print("Jar files are not available in this backup.")
raise Exception
jars = list()
if args.jars == "all":
jars = [dirs for root, dirs, files in os.walk("{}jars".format(temp_folder))][0]
else:
jars = args.jars.split(",")
print("Restore jars: {}".format(jars))
if args.jars != "skip":
for service in jars:
if not os.path.isdir("{0}jars/{1}".format(temp_folder, service)):
print("Jar {} are not available in this backup.".format(service))
else:
for root, dirs, files in os.walk("{0}jars/{1}".format(temp_folder, service)):
for filename in files:
if os.path.isfile("{0}{1}{2}/{3}".format(args.dlab_path, jars_folder, service, filename)):
backupfile = "{0}jars/{1}/{2}".format(temp_folder, service, filename)
destfile = "{0}{1}{2}/{3}".format(args.dlab_path, jars_folder, service, filename)
if not filecmp.cmp(backupfile, destfile):
if ask("Jar {} was changed, rewrite it?".format(filename)):
local("cp -fP {0} {1}".format(backupfile, destfile))
else:
print("Jar {} was skipped.".format(destfile))
else:
print("Jar {} was not changed. Skipped.".format(filename))
else:
print("Jar {} does not exist. Creating.".format(filename))
local("cp -P {0}jars/{1}/{2} {3}{4}{1}".format(temp_folder, service, filename, args.dlab_path, jars_folder))
except:
print("Restore jars failed.")
def restore_database():
try:
print("Restore database: {}".format(args.db))
if args.db:
if not os.path.isfile("{0}{1}".format(temp_folder, "mongo.db")):
print("File {} are not available in this backup.".format("mongo.db"))
raise Exception
else:
if ask("Do you want to drop existing database and restore another from backup?"):
ssn_conf = open(args.dlab_path + conf_folder + 'ssn.yml').read()
data = yaml.load("mongo" + ssn_conf.split("mongo")[-1])
print("Restoring database from backup")
local("mongorestore --drop --host {0} --port {1} --archive={2}/mongo.db --username {3} --password '{4}' --authenticationDatabase={5}" \
.format(data['mongo']['host'], data['mongo']['port'], temp_folder,
data['mongo']['username'], data['mongo']['password'], data['mongo']['database']))
else:
print("Restore database was skipped.")
except:
print("Restore database failed.")
def restore_finalize():
try:
if ask("Start all services after restoring?"):
local("sudo supervisorctl start all")
except:
print("Failed to start all services.")
try:
if ask("Clean temporary folder {}?".format(temp_folder)) and temp_folder != "/":
local("rm -rf {}".format(temp_folder))
except Exception as err:
print("Clear temp folder failed. {}".format(str(err)))
if __name__ == "__main__":
backup_file = os.path.join(os.path.dirname(__file__), args.file)
conf_folder = "conf/"
keys_folder = "/home/{}/keys/".format(os.environ['USER'])
certs_folder = "/etc/ssl/certs/"
jars_folder = "webapp/lib/"
temp_folder = ""
# Backup file section
temp_folder = restore_prepare()
# Restore section
restore_configs()
restore_keys()
restore_certs()
restore_jars()
restore_database()
# Starting services & cleaning tmp folder
restore_finalize()
print("Restore is finished. Good luck.")