XEN subagent: implemented network stats for domains
authorVictor Kirhenshtein <victor@netxms.org>
Wed, 19 Jul 2017 13:17:31 +0000 (16:17 +0300)
committerVictor Kirhenshtein <victor@netxms.org>
Wed, 19 Jul 2017 13:17:31 +0000 (16:17 +0300)
src/agent/subagents/xen/Makefile.am
src/agent/subagents/xen/cpu.cpp
src/agent/subagents/xen/domain.cpp [moved from src/agent/subagents/xen/vm.cpp with 76% similarity]
src/agent/subagents/xen/net.cpp [new file with mode: 0644]
src/agent/subagents/xen/xen.cpp
src/agent/subagents/xen/xen.h

index 7e9a662..503c5d5 100644 (file)
@@ -1,7 +1,7 @@
 SUBAGENT = xen
 
 pkglib_LTLIBRARIES = xen.la
-xen_la_SOURCES = cpu.cpp host.cpp vm.cpp xen.cpp
+xen_la_SOURCES = cpu.cpp domain.cpp host.cpp net.cpp xen.cpp
 xen_la_CPPFLAGS=-I@top_srcdir@/include @XEN_CPPFLAGS@
 xen_la_LDFLAGS = -module -avoid-version -export-symbols ../subagent.sym @XEN_LDFLAGS@
 xen_la_LIBADD = ../../libnxagent/libnxagent.la ../../../libnetxms/libnetxms.la @XEN_LIBS@
index 0ceccec..829f2bb 100644 (file)
@@ -78,6 +78,22 @@ static HashMap<uint32_t, CpuUsageData> s_vmCpuUsage(true);
 static CpuUsageData s_hostCpuUsage;
 static Mutex s_dataLock;
 
+/**
+ * Query CPU usage for domain
+ */
+bool XenQueryDomainCpuUsage(uint32_t domId, INT32 *curr, INT32 *avg1min)
+{
+   s_dataLock.lock();
+   CpuUsageData *data = s_vmCpuUsage.get(domId);
+   if (data != NULL)
+   {
+      *curr = data->getCurrentUsage();
+      *avg1min = data->getAverageUsage(60);
+   }
+   s_dataLock.unlock();
+   return data != NULL;
+}
+
 /**
  * Collect CPU usage data
  */
similarity index 76%
rename from src/agent/subagents/xen/vm.cpp
rename to src/agent/subagents/xen/domain.cpp
index 6e1b4c5..5259674 100644 (file)
@@ -16,7 +16,7 @@
 ** along with this program; if not, write to the Free Software
 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 **
-** File: vm.cpp
+** File: domain.cpp
 **
 **/
 
@@ -88,6 +88,12 @@ LONG H_XenDomainTable(const TCHAR *param, const TCHAR *arg, Table *value, Abstra
       value->addColumn(_T("MEM_MAX"), DCI_DT_UINT64, _T("Max memory"));
       value->addColumn(_T("CPU_COUNT"), DCI_DT_INT, _T("CPU count"));
       value->addColumn(_T("CPU_TIME"), DCI_DT_UINT64, _T("CPU time"));
+      value->addColumn(_T("CPU_USAGE_CURR"), DCI_DT_FLOAT, _T("CPU usage (current)"));
+      value->addColumn(_T("CPU_USAGE_1MIN"), DCI_DT_FLOAT, _T("CPU usage (1 minute average)"));
+      value->addColumn(_T("NET_RX_BYTES"), DCI_DT_UINT64, _T("Net Rx Bytes"));
+      value->addColumn(_T("NET_TX_BYTES"), DCI_DT_UINT64, _T("Net Tx Bytes"));
+      value->addColumn(_T("NET_RX_PACKETS"), DCI_DT_UINT64, _T("Net Rx Packets"));
+      value->addColumn(_T("NET_TX_PACKETS"), DCI_DT_UINT64, _T("Net Tx Packets"));
 
       for(int i = 0; i < count; i++)
       {
@@ -118,6 +124,25 @@ LONG H_XenDomainTable(const TCHAR *param, const TCHAR *arg, Table *value, Abstra
 
          value->set(8, d->vcpu_online);
          value->set(9, (UINT64)d->cpu_time);
+
+         INT32 curr, avg;
+         if (XenQueryDomainCpuUsage(d->domid, &curr, &avg))
+         {
+            TCHAR buffer[32];
+            _sntprintf(buffer, 32, _T("%d.%d"), curr / 10, curr % 10);
+            value->set(10, buffer);
+            _sntprintf(buffer, 32, _T("%d.%d"), avg / 10, avg % 10);
+            value->set(11, buffer);
+         }
+
+         UINT64 rxBytes, txBytes, rxPackets, txPackets;
+         if (XenQueryDomainNetworkTraffic(d->domid, &rxBytes, &txBytes, &rxPackets, &txPackets))
+         {
+            value->set(12, rxBytes);
+            value->set(13, txBytes);
+            value->set(14, rxPackets);
+            value->set(15, txPackets);
+         }
       }
       libxl_dominfo_list_free(domains, count);
       rc = SYSINFO_RC_SUCCESS;
diff --git a/src/agent/subagents/xen/net.cpp b/src/agent/subagents/xen/net.cpp
new file mode 100644 (file)
index 0000000..d5b4200
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+** NetXMS XEN hypervisor subagent
+** Copyright (C) 2017 Raden Solutions
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** File: net.cpp
+**
+**/
+
+#include "xen.h"
+
+/**
+ * Network device information
+ */
+struct NetDevice
+{
+   char name[64];
+   uint32_t domId;
+   uint32_t netId;
+   UINT64 rxBytes;
+   UINT64 txBytes;
+   UINT64 rxPackets;
+   UINT64 txPackets;
+};
+
+/**
+ * Get domaind and network ID from network device name
+ */
+static bool NetDeviceToDomId(const char *dev, uint32_t *domId, uint32_t *netId)
+{
+   if (sscanf(dev, "vif%u.%u", domId, netId) == 2)
+      return true;
+
+   char path[128];
+   snprintf(path, 128, "/sys/class/net/%s/device/nodename", dev);
+   FILE *f = fopen(path, "r");
+   if (path != NULL)
+   {
+      char line[256];
+      fgets(line, 256, f);
+      fclose(f);
+      if (sscanf(line, "backend/vif/%u/%u", domId, netId) == 2)
+         return true;
+   }
+
+   return false;
+}
+
+/**
+ * Scan network devices
+ */
+ObjectArray<NetDevice> *ScanNetworkDevices()
+{
+   FILE *f = fopen("/proc/net/dev", "r");
+   if (f == NULL)
+      return NULL;
+
+   char line[1024];
+   NetDevice dev;
+
+   ObjectArray<NetDevice> *devices = new ObjectArray<NetDevice>(32, 32, true);
+   while(!feof(f))
+   {
+      fgets(line, 1024, f);
+      if (sscanf(line, "%s: %llu %llu %*u %*u %*u %*u %*u %*u %llu %llu %*u %*u %*u %*u %*u %*u",
+               dev.name, &dev.rxBytes, &dev.rxPackets, &dev.txBytes, &dev.txPackets) == 5)
+      {
+         if (NetDeviceToDomId(dev.name, &dev.domId, &dev.netId))
+         {
+            devices->add(new NetDevice(dev));
+         }
+      }
+   }
+   fclose(f);
+   return devices;
+}
+
+/**
+ * Network device data cache
+ */
+static ObjectArray<NetDevice> *s_cache = NULL;
+static time_t s_cacheTimestamp = 0;
+static Mutex s_cacheLock;
+
+/**
+ * Acquire device data cache and update if needed
+ */
+inline void AcquireNetDeviceCache()
+{
+   s_cacheLock.lock();
+   time_t now = time(NULL);
+   if (now - s_cacheTimestamp > 1)
+   {
+      delete s_cache;
+      s_cache = ScanNetworkDevices();
+      if (s_cache != NULL)
+         s_cacheTimestamp = now;
+   }
+}
+
+/**
+ * Query network traffic for domain
+ */
+bool XenQueryDomainNetworkTraffic(uint32_t domId, UINT64 *rxBytes, UINT64 *txBytes, UINT64 *rxPackets, UINT64 *txPackets)
+{
+   bool success = false;
+
+   *rxBytes = 0;
+   *txBytes = 0;
+   *rxPackets = 0;
+   *txPackets = 0;
+
+   AcquireNetDeviceCache();
+   if (s_cache != NULL)
+   {
+      for(int i = 0; i < s_cache->size(); i++)
+      {
+         NetDevice *d = s_cache->get(i);
+         if (d->domId == domId)
+         {
+            *rxBytes += d->rxBytes;
+            *txBytes += d->txBytes;
+            *rxPackets += d->rxPackets;
+            *txPackets += d->txPackets;
+         }
+      }
+      success = true;
+   }
+   s_cacheLock.unlock();
+   return success;
+}
+
+/**
+ * Handler for XEN.Domain.Net.* parameters
+ */
+LONG H_XenDomainNetStats(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
+{
+   char domName[256];
+   if (!AgentGetParameterArgA(param, 1, domName, 256))
+      return SYSINFO_RC_UNSUPPORTED;
+
+   char *eptr;
+   uint32_t domId = strtoul(domName, &eptr, 0);
+   if (*eptr != 0)
+   {
+      LONG rc = XenResolveDomainName(domName, &domId);
+      if (rc != SYSINFO_RC_SUCCESS)
+         return rc;
+   }
+
+   LONG rc = SYSINFO_RC_ERROR;
+   UINT64 rxBytes, txBytes, rxPackets, txPackets;
+   if (XenQueryDomainNetworkTraffic(domId, &rxBytes, &txBytes, &rxPackets, &txPackets))
+   {
+      if (arg[0] == 'R')
+      {
+         ret_uint64(value, arg[1] == 'B' ? rxBytes : rxPackets);
+      }
+      else
+      {
+         ret_uint64(value, arg[1] == 'B' ? txBytes : txPackets);
+      }
+      rc = SYSINFO_RC_SUCCESS;
+   }
+   return rc;
+}
index 0eb5230..ef53089 100644 (file)
@@ -27,6 +27,7 @@
  */
 LONG H_XenDomainCPUUsage(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session);
 LONG H_XenDomainList(const TCHAR *param, const TCHAR *arg, StringList *value, AbstractCommSession *session);
+LONG H_XenDomainNetStats(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session);
 LONG H_XenDomainTable(const TCHAR *param, const TCHAR *arg, Table *value, AbstractCommSession *session);
 LONG H_XenHostCPUUsage(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session);
 LONG H_XenHostOnlineCPUs(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session);
@@ -110,6 +111,10 @@ static NETXMS_SUBAGENT_PARAM s_parameters[] =
    { _T("XEN.Domain.CPU.Usage(*)"), H_XenDomainCPUUsage, _T("1"), DCI_DT_FLOAT, _T("XEN domain {instance}: average CPU utilization for last minute") },
    { _T("XEN.Domain.CPU.Usage15(*)"), H_XenDomainCPUUsage, _T("F"), DCI_DT_FLOAT, _T("XEN domain {instance}: average CPU utilization for last 15 minutes") },
    { _T("XEN.Domain.CPU.Usage5(*)"), H_XenDomainCPUUsage, _T("5"), DCI_DT_FLOAT, _T("XEN domain {instance}: average CPU utilization for last 5 minutes") },
+   { _T("XEN.Domain.Net.RxBytes(*)"), H_XenDomainNetStats, _T("RB"), DCI_DT_UINT64, _T("XEN domain {instance}: network usage (bytes received)") },
+   { _T("XEN.Domain.Net.RxPackets(*)"), H_XenDomainNetStats, _T("RP"), DCI_DT_UINT64, _T("XEN domain {instance}: network usage (packets received)") },
+   { _T("XEN.Domain.Net.TxBytes(*)"), H_XenDomainNetStats, _T("TB"), DCI_DT_UINT64, _T("XEN domain {instance}: network usage (bytes transmitted)") },
+   { _T("XEN.Domain.Net.TxPackets(*)"), H_XenDomainNetStats, _T("TP"), DCI_DT_UINT64, _T("XEN domain {instance}: network usage (packets transmitted)") },
    { _T("XEN.Host.CPU.Cores"), H_XenHostPhyInfo, _T("c"), DCI_DT_INT, _T("XEN host: number of CPU cores") },
    { _T("XEN.Host.CPU.CurrentUsage"), H_XenHostCPUUsage, _T("0"), DCI_DT_FLOAT, _T("XEN host: current CPU utilization") },
    { _T("XEN.Host.CPU.Frequency"), H_XenHostPhyInfo, _T("F"), DCI_DT_INT, _T("XEN host: number of CPU cores") },
@@ -144,7 +149,7 @@ static NETXMS_SUBAGENT_LIST s_lists[] =
  */
 static NETXMS_SUBAGENT_TABLE s_tables[] =
 {
-    { _T("XEN.Domains"), H_XenDomainTable, NULL, _T("ID"), _T("XEN: domains (virtual machines)") }
+   { _T("XEN.Domains"), H_XenDomainTable, NULL, _T("ID"), _T("XEN: domains (virtual machines)") }
 };
 
 /**
index 87ee4e7..70ba27f 100644 (file)
@@ -63,4 +63,14 @@ void XenStopCPUCollector();
  */
 LONG XenResolveDomainName(const char *name, uint32_t *domId);
 
+/**
+ * Query domain CPU usage
+ */
+bool XenQueryDomainCpuUsage(uint32_t domId, INT32 *curr, INT32 *avg1min);
+
+/**
+ * Query network traffic for domain
+ */
+bool XenQueryDomainNetworkTraffic(uint32_t domId, UINT64 *rxBytes, UINT64 *txBytes, UINT64 *rxPackets, UINT64 *txPackets);
+
 #endif