text: # (c) Copyright IBM Corp. 2021
  # (c) Copyright Instana Inc. 2020
  
  """
  Host Collector: Manages the periodic collection of metrics & snapshot data
  """
  from time import time
  from ..log import logger
  from .base import BaseCollector
  from ..util import DictionaryOfStan
  from .helpers.runtime import RuntimeHelper
  
  
  class HostCollector(BaseCollector):
      """ Collector for host agent """
      def __init__(self, agent):
          super(HostCollector, self).__init__(agent)
          logger.debug("Loading Host Collector")
  
          # Indicates if this Collector has all requirements to run successfully
          self.ready_to_start = True
  
          # Populate the collection helpers
          self.helpers.append(RuntimeHelper(self))
  
      def start(self):
          if self.ready_to_start is False:
              logger.warning("Host Collector is missing requirements and cannot monitor this environment.")
              return
  
          super(HostCollector, self).start()
  
      def prepare_and_report_data(self):
          """
          We override this method from the base class so that we can handle the wait4init
          state machine case.
          """
          try:
              if self.agent.machine.fsm.current == "wait4init":
                  # Test the host agent if we're ready to send data
                  if self.agent.is_agent_ready():
                      if self.agent.machine.fsm.current != "good2go":
                          logger.debug("Agent is ready.  Getting to work.")
                          self.agent.machine.fsm.ready()
                  else:
                      return
  
              if self.agent.machine.fsm.current == "good2go" and self.agent.is_timed_out():
                  logger.info("The Instana host agent has gone offline or is no longer reachable for > 1 min.  Will retry periodically.")
                  self.agent.reset()
          except Exception:
              logger.debug('Harmless state machine thread disagreement.  Will self-correct on next timer cycle.')
  
          super(HostCollector, self).prepare_and_report_data()
  
      def should_send_snapshot_data(self):
          delta = int(time()) - self.snapshot_data_last_sent
          if delta > self.snapshot_data_interval:
              return True
          return False
  
      def prepare_payload(self):
          payload = DictionaryOfStan()
          payload["spans"] = []
          payload["profiles"] = []
          payload["metrics"]["plugins"] = []
  
          try:
              if not self.span_queue.empty():
                  payload["spans"] = self.queued_spans()
  
              if not self.profile_queue.empty():
                  payload["profiles"] = self.queued_profiles()
  
              with_snapshot = self.should_send_snapshot_data()
  
              plugins = []
              for helper in self.helpers:
                  plugins.extend(helper.collect_metrics(with_snapshot=with_snapshot))
  
              payload["metrics"]["plugins"] = plugins
  
              if with_snapshot is True:
                  self.snapshot_data_last_sent = int(time())
          except Exception:
              logger.debug("non-fatal prepare_payload:", exc_info=True)
  
          return payload
  