#!/usr/bin/env python
#-
# Copyright (c) 2016 iXsystems, Inc.
# All rights reserved.
# This file is a part of TrueNAS
# and may not be copied and/or distributed
# without the express permission of iXsystems.

import logging
import datetime
import json
import paramiko
import os
import socket
import sys
import libzfs

sys.path.append('/usr/local/www')
sys.path.append('/usr/local/www/freenasUI')

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'freenasUI.settings')

# Make sure to load all modules
from django.db.models.loading import cache
cache.get_apps()

from freenasUI.storage.models import Task, Replication, ReplRemote


LOGGING = {
    'version': 1,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'stream': sys.stdout,
        }
    },
    'root': {
        'handlers': ['console'],
        'level': 'INFO'
    }
}

logging.config.dictConfig(LOGGING)
log = logging.getLogger('tools.reverse_replication')
log.setLevel(logging.INFO)


if len(sys.argv) != 5:
    log.error(
        'Invalid number of arguments provided. Valid arguments list is: <username>@<remote>:<port> <passowrd>\
        <remote_dataset> <local_dataset>. <username> and <port> are optional')
    sys.exit(1)

remote_str = sys.argv[1]
split_username_host = remote_str.split('@')
if len(split_username_host) > 1:
    username = split_username_host[0]
    split_host_port = split_username_host[1].split(':')
else:
    username = None
    split_host_port = split_username_host[0].split(':')

remote = split_host_port[0]
port = 22
if len(split_host_port) > 1:
    port = split_host_port[1]

password = sys.argv[2]
remote_dataset = sys.argv[3]
local_dataset = sys.argv[4]

try:
    zfs = libzfs.ZFS()
    for dataset in zfs.datasets:
        if dataset.name.startswith(local_dataset):
            break
    else:
        log.error('Selected local dataset {0} does not exist'.format(local_dataset))
        sys.exit(1)

except libzfs.ZFSException as err:
    log.error('Cannot access ZFS')
    sys.exit(1)

try:
    ssh = paramiko.SSHClient()
    logging.getLogger("paramiko").setLevel(logging.WARNING)
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(
        remote,
        username=username,
        password=password
    )
    log.info('Connected to {0}'.format(remote))
except paramiko.AuthenticationException as err:
    log.error('Authentication exception: {0}'.format(err))
    sys.exit(1)
except paramiko.BadHostKeyException as err:
    log.error('Bad host key exception: {0}'.format(err))
    sys.exit(1)
except paramiko.SSHException as err:
    log.error('SSH exception: {0}'.format(err))
    sys.exit(1)
except socket.error as err:
    log.error('Socket exception: {0} Check if SSH is turned on'.format(err))
    sys.exit(1)

ssh.load_host_keys('/etc/ssh/ssh_known_hosts')
ssh.save_host_keys('/etc/ssh/ssh_known_hosts')

stdin, stdout, stderr = ssh.exec_command(
    "python /usr/local/www/freenasUI/tools/dump_replication.py {0} {1}".format(local_dataset, remote_dataset)
)
out = stdout.readlines()

error = stderr.readlines()
if error:
    ssh.close()
    log.error('Error during remote replication tasks dump: {0}'.format(error))
    sys.exit(1)

if len(out) > 1:
    ssh.close()
    log.error('Error during remote replication tasks dump. Too long output: {0}'.format(len(out)))
    sys.exit(1)

replication_data = json.loads(out[0], encoding='utf-8')

if len(replication_data) == 0:
    ssh.close()
    log.error('There are no tasks associated with {0}->{1} at remote side'.format(remote_dataset, local_dataset))
    sys.exit(1)

log.info('List of tasks fetched. Remote tasks deleted.')

replication_data = sorted(replication_data, key=lambda d: d['type'], reverse=True)
snapshot_tasks = []
replication_tasks = []

for task in replication_data:
    if task['type'] == 'snapshot':
        snapshot_tasks.append(Task(
            task_filesystem='{0}/{1}'.format(local_dataset, remote_dataset.split('/', 1)[1]),
            task_recursive=task['recursive'],
            task_ret_count=task['lifetime'],
            task_ret_unit=task['lifetime_unit'],
            task_begin=datetime.datetime.strptime(task['begin'], '%H:%M:%S').time(),
            task_end=datetime.datetime.strptime(task['end'], '%H:%M:%S').time(),
            task_interval=task['interval'],
            task_repeat_unit=task['interval_unit'],
            task_byweekday=task['days'],
            task_enabled=task['enabled']
        ))
    else:
        key_obj = ssh.get_transport().get_remote_server_key()
        remote_options = task['remote']
        repl_remote = ReplRemote(
            ssh_remote_hostname=remote,
            ssh_remote_port=port,
            ssh_remote_dedicateduser_enabled=remote_options['user_enabled'],
            ssh_remote_dedicateduser=username if username else os.getenv("USER"),
            ssh_cipher=remote_options['cipher'],
            ssh_remote_hostkey='{0} {1}'.format(key_obj.get_name(), key_obj.get_base64())
        )
        repl_remote.save()

        replication_tasks.append(Replication(
            repl_filesystem='{0}/{1}'.format(local_dataset, remote_dataset.split('/', 1)[1]),
            repl_remote=repl_remote,
            repl_zfs=remote_dataset.rsplit('/', 1)[0],
            repl_userepl=task['recursive'],
            repl_followdelete=task['followdelete'],
            repl_compression=task['compression'],
            repl_limit=task['limit'],
            repl_begin=datetime.datetime.strptime(task['begin'], '%H:%M:%S').time(),
            repl_end=datetime.datetime.strptime(task['end'], '%H:%M:%S').time(),
            repl_enabled=task['enabled']
        ))
        recursive = 'yes' if task['recursive'] else 'no'
        stdin, stdout, stderr = ssh.exec_command(
            "set_dataset_readonly {0} on {1}".format(task['dataset'], recursive)
        )
        error = stderr.readlines()
        if error:
            ssh.close()
            log.error('Cannot set remote {0} as readonly: {1}'.format(task['dataset'], error))
            sys.exit(1)
        if os.system('set_dataset_readonly {0} off {1}'.format(task['remote_dataset'], recursive)):
            ssh.close()
            log.error('Cannot set local {0} readonly parameter to off'.format(task['remote_dataset']))
            sys.exit(1)

log.info('Tasks are now being recreated at local side.')

for task in snapshot_tasks:
    task.save()

for task in replication_tasks:
    task.save()

log.info('All tasks recreated successfully. Ending connection.')

ssh.close()
sys.exit(0)
