# (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P.
#
# 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.


"""
Freezer Admin modes related functions
"""

import time

from oslo_config import cfg
from oslo_log import log
from oslo_service import loopingcall

CONF = cfg.CONF
LOG = log.getLogger(__name__)


class AdminOs(object):
    def __init__(self, client_manager):
        """
        :param client_manager:
        :return:
        """
        self.client_manager = client_manager
        self.cinder_client = self.client_manager.get_cinder()

    def del_cinderbackup_and_dependend_incremental(self, backup_id):
        """
        :param backup_id: backup_id  of cinder volume
        :return:
        """
        cinder_client = self.cinder_client
        search_opts = {
            'parent_id': backup_id
        }
        backups = cinder_client.backups.list(search_opts=search_opts)
        if backups:
            for backup in backups:
                self.del_cinderbackup_and_dependend_incremental(backup.id)
        LOG.info("preparing to delete backup %s", backup_id)
        cinder_client.backups.delete(backup_id)

        start_time = int(time.time())

        def wait_del_backup():
            timeout = 120
            del_backup = cinder_client.backups.list(
                search_opts={'id': backup_id})
            if len(del_backup) == 0:
                LOG.info("Delete backup %s complete" % backup_id)
                raise loopingcall.LoopingCallDone()
            if del_backup[0].status in ['error', 'error_deleting']:
                raise Exception("Delete backup %s failed, "
                                "the status of backup is %s."
                                % (backup_id, del_backup[0].status))
            if (del_backup[0].status == 'deleting') and (int(time.time()) -
                                                         start_time > timeout):
                LOG.error("Delete backup %s failed, In a state of "
                          "deleting over 120s", backup_id)
                raise Exception(
                    "Delete backup %s failed due to timeout over 120s, "
                    "the status of backup is %s."
                    % (backup_id, del_backup[0].status))
        timer = loopingcall.FixedIntervalLoopingCall(wait_del_backup)
        timer.start(interval=0.5).wait()

    def del_off_limit_fullbackup(self, volume_id, keep_number):
        """
        :param volume_id: id of Volume
        :param keep_number: int  keep number of fullbackup
        :return:
        """
        cinder_client = self.cinder_client
        search_opts = {
            'volume_id': volume_id,
            'status': 'available'
        }
        backups = cinder_client.backups.list(search_opts=search_opts,
                                             sort='created_at:asc')
        # Filter fullbackup
        fullbackups = [backup for backup in backups
                       if not backup.is_incremental]
        if len(fullbackups) <= keep_number:
            LOG.info("The numbers of %s fullbackup is %d,"
                     "but keep-number-of-fullbackup is %d,"
                     "don't need delete old backups."
                     % (volume_id, len(fullbackups), keep_number))
            return
        for fullbackup in fullbackups[:-keep_number]:
            self.del_cinderbackup_and_dependend_incremental(fullbackup.id)

    def remove_cinderbackup_older_than(self, volume_id,
                                       remove_older_timestamp):
        """
        :param volume_id: id of Volume
        :param remove_older_timestamp: int
        :return:
        """
        gmstr_remove_older_timestamp = \
            time.strftime("%Y-%m-%dT%H:%M:%S",
                          time.gmtime(remove_older_timestamp))
        cinder_client = self.cinder_client
        search_opts = {
            'volume_id': volume_id,
            'status': 'available'
        }
        # remove cinder backup order by created_at desc, otherwise
        # we need find it incremental backup
        backups = cinder_client.backups.list(search_opts=search_opts,
                                             sort='created_at:desc')
        for backup in backups:

            if backup.created_at <= gmstr_remove_older_timestamp:
                LOG.info("preparing to delete backup %s", backup.id)
                cinder_client.backups.delete(backup.id)
                start_time = int(time.time())

                def wait_del_backup():
                    timeout = 120
                    del_backup = cinder_client.backups.list(
                        search_opts={'id': backup.id})
                    if len(del_backup) == 0:
                        LOG.info("Delete backup %s complete" % backup.id)
                        raise loopingcall.LoopingCallDone()
                    if del_backup[0].status in ['error', 'error_deleting']:
                        raise Exception("Delete backup %s failed, "
                                        "the status of backup is %s."
                                        % (backup.id, del_backup[0].status))
                    if (del_backup[0].status == 'deleting') \
                            and (int(time.time()) - start_time > timeout):
                        LOG.error("Delete backup %s failed, In a state of "
                                  "deleting over 120s", backup.id)
                        raise Exception(
                            "Delete backup %s failed due to timeout over 120s,"
                            " the status of backup is %s."
                            % (backup.id, del_backup[0].status))

                timer = loopingcall.FixedIntervalLoopingCall(wait_del_backup)
                timer.start(interval=0.5).wait()
