#!/usr/bin/env python3
# Copyright 2014-2018 CERN for the benefit of the ATLAS collaboration.
#
# Licensed 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.
#
# Authors:
# - Cedric Serfon, <cedric.serfon@cern.ch>, 2014-2018
# - Mario Lassnig, <mario.lassnig@cern.ch>, 2015
# - Vincent Garonne, <vgaronne@gmail.com>, 2018
# - Hannes Hansen, <hannes.jakob.hansen@cern.ch>, 2018
#
# PY3K COMPATIBLE

'''
Necromancer Daemon : Bring the dead files back to life
'''

import argparse
import signal

from rucio.daemons.badreplicas.necromancer import run, stop


def get_parser():
    """
    Returns the argparse parser.
    """
    parser = argparse.ArgumentParser(description="The Necromancer daemon is responsible for managing bad replicas. If a replica that got declared bad has other replicas, it will try to recover it by requesting a new transfer. If there are no replicas anymore, then the file gets marked as lost.", epilog='''
Lost replica:
In this example the file gets uploaded and will only have this replica as there are no replication rules. If it gets declared bad, there will be no replica to recover from.
Therefor the replica gets marked as lost.

Upload a file::

  $ rucio upload --scope mock --rse MOCK --name file filename.txt

Check replicas::

  $ rucio list-file-replicas mock:file
  +---------+--------+------------+-----------+---------------------------------------------------------+
  | SCOPE   | NAME   | FILESIZE   |   ADLER32 | RSE: REPLICA                                            |
  |---------+--------+------------+-----------+---------------------------------------------------------|
  | mock    | file   | 149.000 B  |    948240 | MOCK: file://localhost:0/tmp/rucio_rse/mock/fb/d1/file  |
  +---------+--------+------------+-----------+---------------------------------------------------------+

Declare it as bad::

  $ rucio-admin replicas declare-bad file://localhost:0/tmp/rucio_rse/mock/fb/d1/file --reason 'bad'

Run the daemon::

  $ rucio-necromancer --run-once

Check replicas again::

  $ rucio list-file-replicas mock:file
  +---------+--------+------------+-----------+----------------+
  | SCOPE   | NAME   | FILESIZE   | ADLER32   | RSE: REPLICA   |
  |---------+--------+------------+-----------+----------------|
  +---------+--------+------------+-----------+----------------+

Bad replica:
In this example the file gets uploaded and will have two replicas. If it gets declared bad, then the daemon will try to recover it from the second replica.

Upload a file and replicate it::

  $ rucio upload --scope mock --rse MOCK filename.txt
  $ rucio add-rule mock:file 1 MOCK2
  $ rucio-conveyor-submitter --run-once

Check replicas::

  $ rucio list-file-replicas mock:file
  +---------+--------+------------+-----------+---------------------------------------------------------+
  | SCOPE   | NAME   | FILESIZE   |   ADLER32 | RSE: REPLICA                                            |
  |---------+--------+------------+-----------+---------------------------------------------------------|
  | mock    | file   | 149.000 B  |    948240 | MOCK: file://localhost:0/tmp/rucio_rse/mock/fb/d1/file  |
  |---------+--------+------------+-----------+---------------------------------------------------------|
  | mock    | file   | 149.000 B  |    948240 | MOCK2: file://localhost:1/tmp/rucio_rse/mock/fb/d1/file |
  +---------+--------+------------+-----------+---------------------------------------------------------+

Declare one replica as bad::

  $ rucio-admin replicas declare-bad file://localhost:1/tmp/rucio_rse/mock/fb/d1/file --reason 'bad'

Run the daemon::

  $ rucio-necromancer --run-once

Check replicas again::

  $ rucio list-file-replicas mock:file
  +---------+--------+------------+-----------+---------------------------------------------------------+
  | SCOPE   | NAME   | FILESIZE   |   ADLER32 | RSE: REPLICA                                            |
  |---------+--------+------------+-----------+---------------------------------------------------------|
  | mock    | file   | 149.000 B  |    948240 | MOCK: file://localhost:0/tmp/rucio_rse/mock/fb/d1/file  |
  |---------+--------+------------+-----------+---------------------------------------------------------|
  | mock    | file   | 149.000 B  |    948240 | MOCK2: file://localhost:1/tmp/rucio_rse/mock/fb/d1/file |
  +---------+--------+------------+-----------+---------------------------------------------------------+
    ''')
    parser.add_argument("--run-once", action="store_true", default=False, help='Runs one loop iteration')
    parser.add_argument("--threads", action="store", default=1, type=int, help='Concurrency control: number of threads')
    parser.add_argument("--bulk", action="store", default=1000, type=int, help='Bulk control: number of requests per cycle')
    return parser


if __name__ == "__main__":

    # Bind our callback to the SIGTERM signal and run the daemon:
    signal.signal(signal.SIGTERM, stop)

    parser = get_parser()
    args = parser.parse_args()
    try:
        run(threads=args.threads, bulk=args.bulk, once=args.run_once)
    except KeyboardInterrupt:
        stop()
