# Path: bts_tools/notification.py
# def send_notification(nodes, node_msg, alert=False):
#     msg = '[%s] %s: %s' % (nodes[0].type(), ', '.join({n.name for n in nodes}), node_msg)
#     if alert:
#         log.warning(msg)
#     else:
#         log.info(msg)
# 
#     for ntype, notify in [('email', send_notification_email),
#                           ('boxcar', send_notification_boxcar),
#                           ('telegram', send_notification_telegram)]:
#         notify_nodes = [node for node in nodes if ntype in node.notification]
#         if notify_nodes:
#             node_names = ', '.join(n.name for n in notify_nodes)
#             msg = '[%s] %s: %s' % (notify_nodes[0].type(), node_names, node_msg)
#             if core.config.get('hostname'):
#                 msg = '[{}] {}'.format(core.config['hostname'], msg)
#             try:
#                 notify(msg, alert)
#             except Exception as e:
#                 log.warning('Failed sending %s notification to %s: %s' % (ntype, node_names, node_msg))
#                 log.exception(e)
# 
# Path: bts_tools/monitor.py
# class StableStateMonitor(object):
#     """Monitor a sequence of states, and is able to compute a "stable" state value when N
#     consecutive same states have happened.
#     For example, we only want to decide the client is offline after 3 consecutive 'offline' states
#     to absorb transient errors and avoid reacting too fast on wrong alerts.
#     """
#     def __init__(self, n):
#         self.n = n
#         self.states = deque(maxlen=n+1)
#         self.last_stable_state = None
# 
#     def push(self, state):
#         stable_state = self.stable_state()
#         if stable_state is not None:
#             self.last_stable_state = stable_state
#         self.states.append(state)
# 
#     def stable_state(self):
#         size = len(self.states)
#         if size < self.n:
#             return None
#         last_state = self.states[-1]
#         if all(state == last_state for state in islice(self.states, size-self.n, size)):
#             return last_state
#         return None
# 
#     def just_changed(self):
#         stable_state = self.stable_state()
#         if stable_state is None or self.last_stable_state is None:
#             return False
#         return stable_state != self.last_stable_state
# 
# Path: bts_tools/core.py
# def run(cmd, capture_io=False, verbose=True, log_on_fail=True,
#         run_dir=None, forward_signals=False, pidfile=None, shell=False):
#     r = _run(cmd, capture_io=capture_io, verbose=verbose,
#              run_dir=run_dir, forward_signals=forward_signals, pidfile=pidfile, shell=shell)
#     if r.status != 0:
#         if log_on_fail:
#             log.warning('Failed running: %s' % cmd)
#             if capture_io:
#                 log.warning('\nSTDOUT:\n{}\nSTDERR:\n:{}'.format(r.stdout, r.stderr))
#         raise RuntimeError('Failed running: %s' % cmd)
#     return r
# 
# BTS_TOOLS_CONFIG_FILE = join(BTS_TOOLS_HOMEDIR, 'config.yaml')


from ..notification import send_notification
from ..monitor import StableStateMonitor
from ..core import run, BTS_TOOLS_CONFIG_FILE
import logging
def free_disk_space(filename):
    lines = run('df "{}"'.format(filename), capture_io=True, verbose=False).stdout.split('\n')
    block_size = to_int(lines[0].split()[1].split('-')[0])
    device, size, used, available, *other = lines[1].split()
    return int(available) * block_size



def init_ctx(node, ctx, cfg):
    ctx.enough_space = StableStateMonitor(1)


def is_valid_node(node):
    return True


def monitor(node, ctx, cfg):
    free_space = free_disk_space(BTS_TOOLS_CONFIG_FILE)
    required = to_int(cfg['min_required_space'])

    if free_space < required:
        ctx.enough_space.push(False)
        if ctx.enough_space.just_changed():
            msg = 'there are now fewer than {} bytes free on the hard drive'.format(cfg['min_required_space'])
            send_notification(ctx.nodes, msg, alert=True)

    else:
        ctx.enough_space.push(True)
        if ctx.enough_space.just_changed():
            msg = 'there are now more than {} bytes free on the hard drive'.format(cfg['min_required_space'])

# Path: bts_tools/notification.py
# def send_notification(nodes, node_msg, alert=False):
#     msg = '[%s] %s: %s' % (nodes[0].type(), ', '.join({n.name for n in nodes}), node_msg)
#     if alert:
#         log.warning(msg)
#     else:
#         log.info(msg)
# 
#     for ntype, notify in [('email', send_notification_email),
#                           ('boxcar', send_notification_boxcar),
#                           ('telegram', send_notification_telegram)]:
#         notify_nodes = [node for node in nodes if ntype in node.notification]
#         if notify_nodes:
#             node_names = ', '.join(n.name for n in notify_nodes)
#             msg = '[%s] %s: %s' % (notify_nodes[0].type(), node_names, node_msg)
#             if core.config.get('hostname'):
#                 msg = '[{}] {}'.format(core.config['hostname'], msg)
#             try:
#                 notify(msg, alert)
#             except Exception as e:
#                 log.warning('Failed sending %s notification to %s: %s' % (ntype, node_names, node_msg))
#                 log.exception(e)
# 
# Path: bts_tools/monitor.py
# class StableStateMonitor(object):
#     """Monitor a sequence of states, and is able to compute a "stable" state value when N
#     consecutive same states have happened.
#     For example, we only want to decide the client is offline after 3 consecutive 'offline' states
#     to absorb transient errors and avoid reacting too fast on wrong alerts.
#     """
#     def __init__(self, n):
#         self.n = n
#         self.states = deque(maxlen=n+1)
#         self.last_stable_state = None
# 
#     def push(self, state):
#         stable_state = self.stable_state()
#         if stable_state is not None:
#             self.last_stable_state = stable_state
#         self.states.append(state)
# 
#     def stable_state(self):
#         size = len(self.states)
#         if size < self.n:
#             return None
#         last_state = self.states[-1]
#         if all(state == last_state for state in islice(self.states, size-self.n, size)):
#             return last_state
#         return None
# 
#     def just_changed(self):
#         stable_state = self.stable_state()
#         if stable_state is None or self.last_stable_state is None:
#             return False
#         return stable_state != self.last_stable_state
# 
# Path: bts_tools/core.py
# def run(cmd, capture_io=False, verbose=True, log_on_fail=True,
#         run_dir=None, forward_signals=False, pidfile=None, shell=False):
#     r = _run(cmd, capture_io=capture_io, verbose=verbose,
#              run_dir=run_dir, forward_signals=forward_signals, pidfile=pidfile, shell=shell)
#     if r.status != 0:
#         if log_on_fail:
#             log.warning('Failed running: %s' % cmd)
#             if capture_io:
#                 log.warning('\nSTDOUT:\n{}\nSTDERR:\n:{}'.format(r.stdout, r.stderr))
#         raise RuntimeError('Failed running: %s' % cmd)
#     return r
# 
# BTS_TOOLS_CONFIG_FILE = join(BTS_TOOLS_HOMEDIR, 'config.yaml')


# Path: bts_tools/monitoring/free_disk_space.py
from ..notification import send_notification
from ..monitor import StableStateMonitor
from ..core import run, BTS_TOOLS_CONFIG_FILE
import logging

def free_disk_space(filename):
    lines = run('df "{}"'.format(filename), capture_io=True, verbose=False).stdout.split('\n')
    block_size = to_int(lines[0].split()[1].split('-')[0])
    device, size, used, available, *other = lines[1].split()
    return int(available) * block_size



def init_ctx(node, ctx, cfg):
    ctx.enough_space = StableStateMonitor(1)


def is_valid_node(node):
    return True


def monitor(node, ctx, cfg):
    free_space = free_disk_space(BTS_TOOLS_CONFIG_FILE)
    required = to_int(cfg['min_required_space'])

    if free_space < required:
        ctx.enough_space.push(False)
        if ctx.enough_space.just_changed():
            msg = 'there are now fewer than {} bytes free on the hard drive'.format(cfg['min_required_space'])
            send_notification(ctx.nodes, msg, alert=True)

    else:
        ctx.enough_space.push(True)
        if ctx.enough_space.just_changed():
            msg = 'there are now more than {} bytes free on the hard drive'.format(cfg['min_required_space'])

            send_notification(ctx.nodes, msg)