Name

sn_agent.AccAgentsAPI

Description

API for managing Agent Client Collectors.

Script

var AccAgentsAPI = Class.create();
AccAgentsAPI.prototype = {
  initialize: function() {},

  AGENTS_CI_TABLE: "sn_agent_cmdb_ci_agent",
  AGENTS_INFO_TABLE: "sn_agent_ci_extended_info",

  // "Agent_".length == 6
  AGENT_NAME_PREFIX_LEN: 6,

  CHECK_READ_LOG_SYS_ID: "028fcd5067c80010b7b72dbd2685ef4f",

  // Map agent statuses to their numeric values
  STATUS: {
      UP: 0,
      WARNING: 1,
      DOWN: 2,
      RESTARTING: 3,
  },

  // Map grab log request statuses to their numeric values
  GRAB_LOG_REQUEST_STATUS: {
      DONE: 0,
      IN_PROGRESS: 1,
      TIMED_OUT: 2,
      ERROR: 3,
      REQUEST_NOT_FOUND: 4
  },

  // Data collection enabled value
  DATA_COLLECTION_ENABLED: "0",

  // 2 minutes = 1000 ms * 60 * 2
  GRAB_LOG_REQUEST_TIMEOUT_MS: 120000,

  // Maximum limit for agents list
  DEFAULT_AGENTS_LIST_LIMIT: 20000,

  getAgentGr: function(agentId, table) {
      var gr = new GlideRecord(table);
      gr.addQuery("agent_id", agentId);
      gr.setLimit(1);
      gr.query();
      return gr;
  },

  agentCIExists: function(agentId) {
      return this.getAgentGr(agentId, this.AGENTS_CI_TABLE).hasNext();
  },

  isRestartEnabledForAgent: function(agentGr) {
      if (agentGr.getTableName() != this.AGENTS_INFO_TABLE)
          agentGr = agentGr.getElement("agent_extended_info").getRefRecord();
      return agentGr.getValue("is_windows") == "1" || agentGr.getValue("is_using_systemd") == "1";
  },

  getGrabbedLogFromRecord: function(eccRecord) {
      var xmlDoc = new XMLDocument2();
      xmlDoc.parseXML(new AgentNowHandler().getEccPayload(eccRecord));
      return xmlDoc.getFirstNode("//results/result/output").getTextContent();
  },

  getAgentRelatedCis: function(agentId) {
      var gr = new GlideRecord("sn_agent_computers_connected_to_agent");
      gr.addQuery("agent_agent_id", agentId);
      gr.query();
      if (!gr.next()) {
          gs.error(gs.getMessage("AccAgentsAPI.getAgentRelatedCis: no computers connected to agent with id == {0}", agentId));
          return null;
      }
      var relatedCis = {
          host: {
              name: gr.getValue("comp_name"),
              class_name: gr.getValue("comp_sys_class_name"),
              sys_id: gr.getValue("comp_sys_id")
          },
          apps: []
      };
      gr = new GlideRecord("sn_agent_applications_connected_to_agent");
      gr.addQuery("agent_agent_id", agentId);
      gr.query();
      while (gr.next())
          relatedCis.apps.push({
              name: gr.getValue("appl_name"),
              class_name: gr.getValue("appl_sys_class_name"),
              sys_id: gr.getValue("appl_sys_id")
          });
      return relatedCis;
  },

  getAgentJson: function(agentInfoGr, withRelatedCis) {
      var agent = {
          name: agentInfoGr.getValue("name").slice(this.AGENT_NAME_PREFIX_LEN),
          status: parseInt(agentInfoGr.getValue("status")),
          agent_id: agentInfoGr.getValue("agent_id"),
          ip_address: agentInfoGr.getValue("ip_address"),
          number_of_running_checks: parseInt(agentInfoGr.getValue("running_checks_num")),
          data_collection: parseInt(agentInfoGr.getValue("data_collection")),
          is_restart_enabled: this.isRestartEnabledForAgent(agentInfoGr),
          is_duplicate: agentInfoGr.getValue("is_duplicate") == "1",
          up_since: agentInfoGr.getValue("up_since"),
          version: agentInfoGr.getValue("agent_version")
      };
      if (withRelatedCis)
          agent.related_cis = this.getAgentRelatedCis(agent.agent_id);
      return agent;
  },

  // Returns an array of JSONs, each one containing the following information about the agent:
  // name, status, agent id, ip address, number of running checks, data collection status,
  // if restart is enabled, if the agent is a duplicate one, UTC time since agent is up and the
  // version of the agent.
  //
  // An encoded query and a limit of agents can be given. Use null or undefined for both if they
  // are not required. If no limit is given or the given limit is above 20,000, then the maximum
  // limit of 20,000 will be used.
  //
  // The 3rd argument to this method, withRelatedCis, specifies if to return, per agent, a JSON
  // ("related_cis") containing information about the agent host CI and app CIs (if any).
  //
  // Output example:
  //
  // [
  //   {
  //     "name":  "MREM6A2F8DB2",
  //     "status":  0,
  //     "agent_id":  "e894145d704d4445",
  //     "ip_address":  "192.168.0.219",
  //     "number_of_running_checks":  0,
  //     "data_collection":  0,
  //     "is_restart_enabled":  false,
  //     "is_duplicate":  false,
  //     "up_since":  "2020-12-20 11:19:41",
  //     "version":  "2.2.0"
  //   }
  // ]
  getAgentsList: function(encodedQuery, limit, withRelatedCis) {
      var agents = [];
      // query the agent ci table to enable encoded query on that table
      var agentsGr = new GlideRecord(this.AGENTS_CI_TABLE);
      if (!gs.nil(encodedQuery))
          agentsGr.addEncodedQuery(encodedQuery);
      if (gs.nil(limit) || limit < 1)
          agentsGr.setLimit(this.DEFAULT_AGENTS_LIST_LIMIT);
      else
          agentsGr.setLimit(limit);
      agentsGr.query();
      while (agentsGr.next())
          agents.push(this.getAgentJson(agentsGr.getElement("agent_extended_info").getRefRecord(), withRelatedCis));
      return agents;
  },

  // Given an agent id, return the following information about the agent with that id:
  // name, status, agent id, ip address, number of running checks, data collection status,
  // if restart is enabled, if the agent is a duplicate one, UTC time since agent is up and the
  // version of the agent.
  //
  // This method returns a JSON with 2 attributes: an error message ("error") and a JSON with the above information
  // about the requested agent ("agent"). if no agent exists with the given agent id then the error message will not
  // be null and the agent JSON will be null. if it does exist than output.error == null and output.agent != null.
  //
  // The 2nd argument to this method, withRelatedCis, specifies if to return a JSON ("related_cis") containing information
  // about the agent host CI and app CIs (if any).
  //
  // Output example:
  //
  // {
  //   "error": null,
  //   "agent": {
  //     "name":  "MREM6A2F8DB2",
  //     "status":  0,
  //     "agent_id":  "e894145d704d4445",
  //     "ip_address":  "192.168.0.219",
  //     "number_of_running_checks":  0,
  //     "data_collection":  0,
  //     "is_restart_enabled":  false,
  //     "is_duplicate":  false,
  //     "up_since":  "2020-12-20 11:19:41",
  //     "version":  "(devel)"
  //   }
  // }
  getAgent: function(agentId, withRelatedCis) {
      var agentInfoGr = this.getAgentGr(agentId, this.AGENTS_INFO_TABLE);
      if (!agentInfoGr.next()) {
          return {
              error: "No Agent With ID: " + agentId,
              agent: null
          };
      }
      return {
          error: null,
          agent: this.getAgentJson(agentInfoGr, withRelatedCis)
      };
  },

  // Submit a grab log request to grab the log of the agent with the given agent id.
  //
  // returns a JSON with the request id and a possible error message (null when no error).
  // The returned request id should be passed to AccAgentsAPI.checkGrabLogRequestProgress
  // in order to check the progress of the request and get the log itself.
  //
  // Output example:
  //
  // {
  //   "error": null,
  //   "request_id": "028fcd5067c8201057b72dgd2685ff4f"
  // }
  submitGrabLogRequest: function(agentId) {
      var agentGr = this.getAgentGr(agentId, this.AGENTS_CI_TABLE);
      if (!agentGr.next()) {
          gs.error("sn_agent.AccAgentsAPI.submitGrabLogRequest: no live agent with id: " + agentId);
          return {
              error: "No Agent With ID: " + agentId,
              request_id: null
          };
      }
      if (agentGr.agent_extended_info.status != this.STATUS.UP) {
          return {
              error: "Agent With ID: " + agentId + " Is Not Up",
              request_id: null
          };
      }
      return {
          error: null,
          request_id: new AgentNowCapabilities().grabAgentLog(agentGr)
      };
  },

  // Given a request id obtained from AccAgentsAPI.submitGrabLogRequest,
  // this method checks the status of the request with the given sys id.
  //
  // Returns null in case no request exists with the given sys id and
  // otherwise returns a JSON with 2 attributes: status and output,
  // where status is one of the values in AccAgentsAPI.GRAB_LOG_REQUEST_STATUS
  // and output is the grabbed log itelf when the status is DONE or a message
  // describing any of the other statuses.
  //
  // Output example:
  //
  // {
  //     "status":  0, // AccAgentsAPI.GRAB_LOG_REQUEST_STATUS.DONE
  //     "output":  "...", // the grabbed log
  // }
  checkGrabLogRequestProgress: function(requestId) {
      var requestGr = new GlideRecord("sn_agent_request");
      if (!requestGr.get(requestId)) {
          return {
              status: this.GRAB_LOG_REQUEST_STATUS.REQUEST_NOT_FOUND,
              output: "No Grab Log Agent Request With sys_id: " + requestId
          };
      }
      if (requestGr.getValue("check_def") != this.CHECK_READ_LOG_SYS_ID) {
          return {
              status: this.GRAB_LOG_REQUEST_STATUS.REQUEST_NOT_FOUND,
              output: "Agent Request With sys_id: " + requestId + " Is Not A Grab Log Request"
          };
      }
      // flow invoked by grab log functionality is a bit different than running regular checks
      // so lets check if the ecc queue contains a response for the request
      var eccQ = new GlideRecord("ecc_queue");
      eccQ.addQuery("agent_correlator", requestId);
      eccQ.addQuery("queue", "input");
      eccQ.setLimit(1);
      eccQ.query();
      var status, output;
      if (!eccQ.next()) {
          var requestCreationTime = new GlideDateTime(requestGr.getValue("sys_created_on"));
          var now = new GlideDateTime();
          // agent requests for log grabbing are not getting marked as timeout
          // so lets consider the request as timed out if more than 2 minutes
          // passed since it was created (resembling the logic in
          // AgentNowHandler.handleRequestChecksTable)
          if (now.getNumericValue() - requestCreationTime.getNumericValue() > this.GRAB_LOG_REQUEST_TIMEOUT_MS) {
              status = this.GRAB_LOG_REQUEST_STATUS.TIMED_OUT;
              output = "Grab Log Request Timed Out";
          } else {
              status = this.GRAB_LOG_REQUEST_STATUS.IN_PROGRESS;
              output = "Grab Log Request Still In Progress";
          }
      } else {
          if (eccQ.getValue("state") == "error") {
              status = this.GRAB_LOG_REQUEST_STATUS.ERROR;
              output = "Error Executing Grab Log Request";
          } else {
              status = this.GRAB_LOG_REQUEST_STATUS.DONE;
              output = this.getGrabbedLogFromRecord(eccQ);
          }
      }
      return {
          status: status,
          output: output
      };
  },

  // Run discovery for the agent with the given agent id.
  //
  // returns a string containing an error message which is null
  // if no error happened.
  runDiscovery: function(agentId) {
      var agentGr = this.getAgentGr(agentId, this.AGENTS_CI_TABLE);
      if (!agentGr.next())
          return "No Agent With ID: " + agentId;
      if (agentGr.agent_extended_info.status != this.STATUS.UP)
          return "Agent With ID: " + agentId + " Is Not Up";
      new RunDiscoveryCheck().runCheck(agentGr.getUniqueValue());
      return null;
  },

  // Set the given data collection status (true/false if enabled or not)
  // for the agent with the given agent id.
  //
  // returns a string containing an error message which is null
  // if no error happened.
  setDataCollectionStatus: function(agentId, status) {
      var agentGr = this.getAgentGr(agentId, this.AGENTS_CI_TABLE);
      if (!agentGr.next())
          return "No Agent With ID: " + agentId;
      if (agentGr.agent_extended_info.status != this.STATUS.UP)
          return "Agent With ID: " + agentId + " Is Not Up";
      var dataCollectionStatus = agentGr.agent_extended_info.data_collection;
      if (status && dataCollectionStatus != this.DATA_COLLECTION_ENABLED)
          new AgentSilentModeUtils().unsilentAgents(agentGr.getUniqueValue());
      else if (!status && dataCollectionStatus == this.DATA_COLLECTION_ENABLED)
          new AgentSilentModeUtils().silentAgents(agentGr.getUniqueValue());
      return null;
  },

  // Restart the agent with the given agent id.
  //
  // returns a string containing an error message which is null
  // if no error happened.
  restartAgent: function(agentId) {
      var agentGr = this.getAgentGr(agentId, this.AGENTS_CI_TABLE);
      if (!agentGr.next())
          return "No Agent With ID: " + agentId;
      if (agentGr.agent_extended_info.status != this.STATUS.UP)
          return "Agent With ID: " + agentId + " Is Not Up";
      if (!this.isRestartEnabledForAgent(agentGr))
          return "Agent With ID: " + agentId + " Does Not Support Restart";
      new AgentRestartUtil().restartAgent([agentGr.getUniqueValue()]);
      return null;
  },

  type: 'AccAgentsAPI'
};

Sys ID

315044c4531120106d9bddeeff7b122e

Offical Documentation

Official Docs: