#! /usr/bin/env python2
# -*- coding: utf-8 -*-

# opsi-backup is part of the desktop management solution opsi
# (open pc server integration) http://www.opsi.org
# Copyright (C) 2010-2020 uib GmbH <info@uib.de>

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License version 
# 3 as published by the Free Software Foundation 

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.

# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
opsi-backup - a commandline tool to backup opsi configuration and backends.

:copyright: uib GmbH <info@uib.de>
:author: Erol Ueluekmen <e.ueluekmen@uib.de>
:author: Niko Wenselowski <n.wenselowski@uib.de>
:license: GNU Affero General Public License version 3
"""

from __future__ import print_function

import argparse
import locale
import os
import sys

from OPSI.Logger import Logger, LOG_WARNING, LOG_INFO, LOG_NOTICE
from OPSI.Util.Task.Backup import OpsiBackup

__version__ = "4.1.1.29"

logger = Logger()

USAGE = '''
Usage: %s [common-options] <command> [command-options]

Creates and restores opsi backups.
Version %s.

Common options:
   -h, --help                          Show this help message and exit
   -v, --verbose                       Log to standard error.
   -l, --log-level    <log-level>      Set log level to <log-level> (default: 4)
                                          0=nothing, 1=essential, 2=critical, 3=error, 4=warning
                                          5=notice, 6=info, 7=debug, 8=debug2, 9=confidential
   --log-file         <log-file>       Log to <log-file>.
                                       Default is /var/log/opsi/opsi-backup.log.

Commands:
   verify     <backup-archive>         Verify integrity of <backup-archive>.

   list       <backup-archive>         List contents of <backup-archive>.

   restore    <backup-archive>         Restore data from <backup-archive>.
      --backends    <backend>          Select a backend to restore or 'all' for all backends.
                                          Can be given multiple times.
      --configuration                  Restore opsi configuration.
      -f, --force                      Ignore sanity checks and try to apply anyways. Use with caution!

   create     [destination]            Create a new backup.
                                          If [destination] is omitted, a backup archive will be created
                                          in the current directory. If [destination] is an existing
                                          directory, the backup archive will be created in that directory.
                                          If [destination] does not exist or is a file, [destination]
                                          will be used as backup archive destination file.
      --flush-logs                        Causes mysql to flush table logs to disk before the backup (recommended).
      --backends    <backend>          Select a backend to create a backup for. Use 'auto' for automatic detection
                                          of used backends or 'all' for all backends.
                                          Can be given multiple times.
      --no-configuration               Do not backup opsi configuration.
      -c, --compression {gz|bz2|none}  Sets the compression format for the archive (default: bz2).

''' % (os.path.basename(sys.argv[0]), __version__)


class HelpFormatter(argparse.HelpFormatter):
	def format_help(self):
		return USAGE


def main():
	logger.setLogFormat('[%l] [%D] %M')
	logger.setConsoleLevel(LOG_WARNING)

	parser = argparse.ArgumentParser(prog="opsi-backup", add_help=False, usage=USAGE, formatter_class=HelpFormatter)
	parser.add_argument("-h", "--help", action="help")
	parser.add_argument("-v", "--verbose", action="store_true", default=False)
	parser.add_argument("-l", "--log-level", default=LOG_NOTICE, type=int,
						choices=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
	parser.add_argument("--log-file", metavar='FILE', default="/var/log/opsi/opsi-backup.log")

	subs = parser.add_subparsers(title="commands", dest="command")

	verifyParser = subs.add_parser("verify")
	verifyParser.add_argument("file", nargs="+")

	listParser = subs.add_parser("list")
	listParser.add_argument("file", nargs="+")

	restoreParser = subs.add_parser("restore")
	restoreParser.add_argument("file", nargs=1)
	restoreParser.add_argument("--backends", action="append")
	restoreParser.add_argument("--configuration", action="store_true", default=False)
	restoreParser.add_argument("-f", "--force", action="store_true", default=False)

	creationParser = subs.add_parser("create")
	creationParser.add_argument("destination", nargs="?")
	creationParser.add_argument("--flush-logs", action="store_true", default=False)
	creationParser.add_argument("--backends", action="append")
	creationParser.add_argument("--no-configuration", action="store_true", default=False)
	creationParser.add_argument("-c", "--compression", nargs="?", default="bz2", choices=['gz', 'bz2', 'none'])

	try:
		args = parser.parse_args()
	except Exception:
		print(USAGE)
		return 1

	if args.verbose:
		logger.setConsoleColor(True)
		logger.setConsoleLevel(args.log_level)

	if args.log_file:
		logger.setLogFile(args.log_file)
		logger.setFileLevel(args.log_level)

	backup = OpsiBackup(stdout=sys.stdout)
	result = 0
	if args.command == 'restore':
		if not args.backends:
			logger.debug("No backend given, assuming 'auto'")
			args.backends = ['auto']

		result = backup.restore(
			file=args.file, backends=args.backends,
			configuration=args.configuration, force=args.force
		)
	elif args.command == 'verify':
		result = backup.verify(file=args.file)
		if result == 0:
			print("Verified")
		else:
			print("Verification failed")
	elif args.command == 'list':
		if not args.verbose:
			logger.setConsoleLevel(LOG_NOTICE)
		backup.list(args.file)
	elif args.command == 'create':
		if not args.backends:
			logger.debug("No backends given, assuming 'auto'")
			args.backends = ['auto']

		result = backup.create(
			destination=args.destination,
			backends=args.backends, noConfiguration=args.no_configuration,
			compression=args.compression, flushLogs=args.flush_logs
		)

	try:
		result = int(result)
	except (TypeError, ValueError):
		pass

	return result


if __name__ == "__main__":
	try:
		returnCode = main()
	except Exception as exception:
		logger.logException(exception, LOG_INFO)
		print((u"\nERROR: %s\n" % exception).encode(locale.getpreferredencoding(), 'replace'), file=sys.stderr)
		returnCode = 1

	sys.exit(returnCode)
