#!/usr/bin/env python3
# Copyright (c) 2016 Arista Networks, Inc.  All rights reserved.
# Arista Networks, Inc. Confidential and Proprietary.

import os
import sys
import argparse

import Tac
import TacUtils
import Agent
import Cell
import StageHelper
import Tracing

__defaultTraceHandle__ = Tracing.Handle( 'PlutoDaemon' )
Tracing.traceSettingIs( 'PlutoDaemon/0' )

t0 = Tracing.trace0
t3 = Tracing.trace3

class DaemonLauncher( Agent.Agent ):
   def __init__( self, entityManager, agentName=None ):

      self.daemonDir_ = None
      self.daemonConfig_ = None

      self.bootConfig_ = None
      self.bootStatus_ = None
      self.bootStagesHelper_ = None

      self.exe_ = None
      self.argv_ = None

      self.ready_ = False

      Agent.Agent.__init__( self, entityManager, agentName )
      t0( 'Starting daemon loader for', self.agentName )

   def doInit( self, entityManager ):
      t3( 'Mounting state' )
      mg = entityManager.mountGroup()

      # For daemon management
      self.daemonDir_ = mg.mount(
            'hardware/pluto/daemons',
            'Hardware::PlutoDaemon::Dir', 'r' )

      # For ASU boot stage progression
      self.bootConfig_ = mg.mount(
         # pylint: disable-next=consider-using-f-string
         Cell.path( 'stageInput/boot/%s' % self.agentName ),
         'Stage::AgentConfig', 'r' )
      self.bootStatus_ = mg.mount(
         # pylint: disable-next=consider-using-f-string
         Cell.path( 'stageAgentStatus/boot/%s' % self.agentName ),
         'Stage::AgentStatus', 'wcf' )

      mg.close( self._doMountComplete )

   def _doMountComplete( self ):
      t3( 'Post mount operations' )
      # Wait for Fru to create our Daemon Config
      Tac.Poller( lambda: self.agentName in self.daemonDir_.config,
                  self.daemonConfigComplete, timeout=120.0,
                  description="daemon config to be created" )

   def daemonConfigComplete( self, *args, **kwargs ):
      self.daemonConfig_ = self.daemonDir_.config[ self.agentName ]
      self.exe_ = self.daemonConfig_.exe
      self.argv_ = [ self.exe_ ] + list( self.daemonConfig_.argv.values() )

      # Return to the event loop to let the agent initialisation finish
      Tac.Poller( lambda: self.ready_, self.finishInit )

   def warm( self ):
      ready = super().warm()
      self.ready_ = ready
      return ready

   def finishInit( self, *args, **kwargs ):
      t0( 'Finishing initialisation' )
      self.updateStage( 'boot', 'PrimaryAgent' )
      self.executeDaemon()

   def updateStage( self, stage, stageEvent ):
      t3( 'Updating stage status' )
      self.bootStagesHelper_ = StageHelper.PyStagesHelper(
            stage, None, None, self.bootConfig_, self )
      self.bootStagesHelper_.cStagesHelper().agentStatus = self.bootStatus_
      self.bootStagesHelper_.doStageComplete( stageEvent )

   def executeDaemon( self ):
      # fd 999 is used by ProcMgr so that it can find previously spawned children
      # after restart or reexec.
      t0( 'Closing all fds except 0,1,2,999 prior to execv' )
      TacUtils.closeAllFdsExcept( ( 0, 1, 2, 999 ) )
      t0( 'Execing:', self.exe_, ' '.join( self.argv_ ) )
      os.execv( self.exe_, self.argv_ )

# There is currently no way of disabling the watchdog in an agent
# If the LauncherConfig has a heartbeat period of 0 then the watchdog class will
# assert due to an invalid HEARTBEAT_PERIOD. Unsetting the variable will set the
# watchdog to be punched at Tac::endOfTime while the ProcMgr configuration will
# not expect to be punched
os.environ.pop( 'HEARTBEAT_PERIOD', None )

# usage: $0 agentName [args...]
daemonName = sys.argv[ 1 ]
container = Agent.AgentContainer( [ DaemonLauncher ], agentTitle=daemonName )

# This trick consist of adding an option agentName that will trick PyAgent into
# passing the desired agentName as the agentName= parameter of the DaemonLauncher
container.addOption( '--agentName', dest='agentName', help=argparse.SUPPRESS,
                     agentClass=DaemonLauncher )

# usage: $0 --agentName agentName [args...]
# XXX: this could also be set by the LauncherPlugin
sys.argv.insert( 1, '--agentName' )

container.runAgents()
