import logging
import os
import re
import time

log = logging.getLogger('freenasUI.failover.notifier')


class FailoverNotifier:

    def get_carp_states(self):
        p1 = self._pipeopen("/sbin/ifconfig -l")
        res = p1.communicate()[0]
        if p1.returncode != 0:
            raise ValueError('ifconfig filed')
        numbers = re.findall(r'carp([0-9]+)', res)
        numbers = filter(lambda y: y not in ('1', '2'), numbers)

        masters = []
        backups = []
        for number in numbers:
            p1 = self._pipeopen("/sbin/ifconfig carp%s" % number)
            res = p1.communicate()[0]
            if p1.returncode != 0:
                raise ValueError('ifconfig filed')
            reg = re.search(r'\bcarp: (?P<status>\w+)\s', res, re.S)
            if not reg:
                raise ValueError('ifconfig filed')
            status = reg.group("status")
            if status == 'MASTER':
                masters.append(number)
            else:
                backups.append(number)
        return masters, backups

    def failover_status(self):
        """
        Return the current status of this node in the failover

        Returns:
            MASTER
            BACKUP
            ELECTING
            IMPORTING
            ERROR
            SINGLE
        """
        from freenasUI.failover.models import Failover
        failover = Failover.objects.order_by('-id')
        if failover.exists():
            failover = failover[0]
        else:
            return 'SINGLE'

        try:
            masters, backups = self.get_carp_states()
        except:
            return 'ERROR'

        if len(masters) > 0:
            p1 = self._pipeopen("/sbin/zpool list %s" % failover.volume.vol_name)
            p1.communicate()
            if p1.returncode == 0:
                return 'MASTER'
            else:
                if os.path.exists('/tmp/.failover_electing'):
                    return 'ELECTING'
                elif os.path.exists('/tmp/.failover_importing'):
                    return 'IMPORTING'
                elif os.path.exists('/tmp/.failover_failed'):
                    return 'ERROR'

        p1 = self._pipeopen("/usr/bin/ssh -T %s zpool list %s" % (
            failover.ipaddress,
            failover.volume.vol_name,
        ))

        timeout = 5
        while p1.poll() is None and timeout > 0:
            time.sleep(1)
            timeout -= 1

        # Other node has the pool
        if p1.returncode == 0:
            # check for carp MASTER (any) in remote?
            return 'BACKUP'
        # Other node has no pool
        elif p1.returncode == 1:
            # check for carp MASTER (none) in remote?
            return 'ERROR'
        # We couldn't contact the other node
        elif p1.returncode == 255 or timeout == 0:
            return 'UNKNOWN'

    def failover_force_master(self):
        self._system('/usr/local/sbin/fenced force')

    def failover_sync_peer(self, fid=None, fromto='to'):
        '''
        Runs synctopeer script

        Returns:
            False - No failover configured
            Failover object - Sync success

        Raises:
            ValueError
        '''
        from freenasUI.failover.models import Failover
        try:
            if fid is None:
                failover = Failover.objects.latest('id')
            else:
                failover = Failover.objects.get(id=fid)
        except Failover.DoesNotExist:
            return False
        p1 = self._pipeopen('/root/sync%speer %s' % (
            fromto,
            failover.ipaddress,
        ))
        stderr = p1.communicate()[1]
        if p1.returncode != 0:
            raise ValueError('Sync %s peer failed: %s' % (fromto, stderr))
        if fromto == 'to':
            remote, wantinit = self.harepl_get()
            if remote != '':
                p1 = self._pipeopen('/root/sync%speer %s' % (fromto, remote))
                stderr = p1.communicate()[1]
                if p1.returncode != 0:
                    raise ValueError('Sync %s peer %s failed: %s' % (
                        fromto,
                        remote,
                        stderr,
                    ))
        return failover

    def harepl_get(self):
        p = self._pipeopen('zpool list -H -o name')
        if p.wait() == 0:
            pool = p.communicate()[0].split('\n')[0].strip()
            p = self._pipeopen(
                '/sbin/zfs list -H -o com.ixsystems:replremote %s' % pool
            )
            remote = p.communicate()[0].split('\n')[0].strip()
            if remote == '-':
                remote = ''
            p = self._pipeopen(
                '/sbin/zfs list -H -o com.ixsystems:replinit %s' % pool
            )
            wantinit = p.communicate()[0].split('\n')[0].strip()
            wantinit = (wantinit == 'True')
            return (remote, wantinit)
        else:
            log.warn('Can not get pool list')

    def harepl_set(self, remote, wantinit):
        p = self._pipeopen('zpool list -H -o name')
        if p.wait() == 0:
            pool = p.communicate()[0].split('\n')[0].strip()
            if remote == '':
                cmd = '/sbin/zfs inherit com.ixsystems:replremote %s' % pool
                self._system(cmd)
                cmd = '/sbin/zfs inherit com.ixsystems:replinit %s' % pool
                self._system(cmd)
            else:
                cmd = '/sbin/zfs set com.ixsystems:replremote=%s %s' % (
                    remote,
                    pool
                )
                self._system(cmd)
                if wantinit:
                    cmd = '/sbin/zfs set com.ixsystems:replinit=True %s' % pool
                else:
                    cmd = '/sbin/zfs inherit com.ixsystems:replinit %s' % pool
                self._system(cmd)
            self._system('/usr/sbin/service ix-crontab quietstart')
        else:
            log.warn('Can not get pool list')
