#!/usr/bin/env python3

import sys
import os
from optparse import OptionParser
import logging
import configparser
import signal
import time
import multiprocessing
from threading import Event
from logging.handlers import SysLogHandler

from nullunit.common import getConfig, getMCP, PACKAGE_MANAGER
from nullunit.iterate import readState, writeState, doStep
from nullunit.procutils import open_output, close_output, execute


PID_FILE = '/var/run/nullunitIterate.pid'
STATE_FILE = '/var/lib/mcp/nullunitIterate.state'


def _sigHandler( sig, frame ):
  logging.debug( 'Got Signal {0}, stopping...'.format( sig ) )
  stop_event.set()


oparser = OptionParser( description='nullunit iterator' )

oparser.add_option( '-v', '--verbose', dest='verbose', default=False, action='store_true' )
oparser.add_option( '-s', '--step', dest='step', default=False, action='store_true' )

( options, args ) = oparser.parse_args()

logging.basicConfig()
logger = logging.getLogger()
handler = SysLogHandler( address='/dev/log', facility=SysLogHandler.LOG_DAEMON )
handler.setFormatter( logging.Formatter( fmt='nullunitIterate [%(process)d]: %(message)s' ) )
logger.addHandler( handler )

if options.verbose:
  logger.setLevel( logging.DEBUG )
else:
  logger.setLevel( logging.INFO )

stop_event = Event()
logging.info( 'Starting...' )

if os.path.exists( PID_FILE ):
  logging.error( 'pid file exists, bailing...' )
  logging.shutdown()
  sys.exit( 0 )

tmp = open( PID_FILE, 'w' )
tmp.write( '{0}\n'.format( os.getpid() ) )
tmp.close()

signal.signal( signal.SIGINT, _sigHandler )
signal.signal( signal.SIGTERM, _sigHandler )

config = getConfig()
if not config:
  logging.error( 'Unable to load Config' )
  os.unlink( PID_FILE )
  logging.shutdown()
  sys.exit( 1 )

if not config.get( 'mcp', 'job_id' ):
  os.system( 'config-curator -c nullunit -g' )
  config = getConfig()

  if config.get( 'mcp', 'job_id' ):
    open_output( '/var/log/nullunitIterate_debug.{0}.pre.log'.format( os.getpid() ) )
    os.system( 'config-curator -c -a -g' )
    time.sleep( 2 )
    if PACKAGE_MANAGER == 'apt':
      execute( '/usr/bin/apt-get update', retry_rc_list=[ 100 ] )
      execute( '/usr/bin/apt-get -y upgrade' )

    elif PACKAGE_MANAGER == 'yum':
      execute( '/usr/bin/yum -y update' )

    time.sleep( 2 )
    os.system( 'sync' )
    time.sleep( 2 )
    os.system( 'reboot' )

  else:
    logging.warning( 'Preallocated, waiting for instructions' )

  os.unlink( PID_FILE )
  logging.shutdown()
  sys.exit( 0 )

mcp = getMCP( config, stop_event )
if not mcp:
  logging.error( 'Unable to connect to MCP' )
  os.unlink( PID_FILE )
  logging.shutdown()
  sys.exit( 1 )

try:
  state = readState( STATE_FILE )
except configparser.Error:
  print( 'Error retreiving git url, git branch, and/or make target from config file' )
  os.unlink( PID_FILE )
  logging.shutdown()
  sys.exit( 1 )

if state is None:
  state = {
            'state': 'clone',
            'url': config.get( 'git', 'url' ),
            'branch': config.get( 'git', 'branch' ),
            'target': config.get( 'make', 'target' ),
         }

# loop start

counter = 0
while not stop_event.is_set():
  if state[ 'state' ] in ( 'done', 'failed' ):
    logging.info( 'Signaling Job Ran' )
    mcp.signalJobRan()
    logging.info( 'Task Complete' )
    break

  counter += 1
  open_output( '/var/log/nullunitIterate_debug.{0}.{1}.log'.format( os.getpid(), counter ) )

  try:
    doStep( state, mcp, config, multiprocessing.cpu_count(), stop_event )
  except Exception as e:
    logging.exception( 'Unhandled Exception in doStep for "{0}".'.format( state[ 'state' ] ) )
    mcp.sendMessage( 'Unhandled Exception: ({0}) "{1}"'.format( e.__class__.__name__, e ) )
    state[ 'state' ] = 'failed'
    mcp.setSuccess( False )

  close_output()

  logging.info( 'Writing State' )
  writeState( STATE_FILE, state )

  if options.step or state[ 'state' ] == 'failed':
    break

  time.sleep( 2 )

os.unlink( PID_FILE )

if state[ 'state' ] == 'failed':
  logging.info( 'Failed, Quitting.' )
  logging.shutdown()
  sys.exit( 1 )

logging.info( 'Done' )
logging.shutdown()
sys.exit( 0 )
