XEN subagent: added table XEN.Net.DomainInterfaces
[public/netxms.git] / src / agent / subagents / xen / net.cpp
1 /*
2 ** NetXMS XEN hypervisor subagent
3 ** Copyright (C) 2017 Raden Solutions
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: net.cpp
20 **
21 **/
22
23 #include "xen.h"
24
25 /**
26 * Network device information
27 */
28 struct NetDevice
29 {
30 char name[64];
31 uint32_t domId;
32 uint32_t netId;
33 UINT64 rxBytes;
34 UINT64 txBytes;
35 UINT64 rxPackets;
36 UINT64 txPackets;
37 };
38
39 /**
40 * Get domaind and network ID from network device name
41 */
42 static bool NetDeviceToDomId(const char *dev, uint32_t *domId, uint32_t *netId)
43 {
44 if (sscanf(dev, "vif%u.%u", domId, netId) == 2)
45 return true;
46
47 char path[128];
48 snprintf(path, 128, "/sys/class/net/%s/device/nodename", dev);
49 FILE *f = fopen(path, "r");
50 if (path != NULL)
51 {
52 char line[256];
53 fgets(line, 256, f);
54 fclose(f);
55 if (sscanf(line, "backend/vif/%u/%u", domId, netId) == 2)
56 return true;
57 }
58
59 return false;
60 }
61
62 /**
63 * Scan network devices
64 */
65 ObjectArray<NetDevice> *ScanNetworkDevices()
66 {
67 FILE *f = fopen("/proc/net/dev", "r");
68 if (f == NULL)
69 return NULL;
70
71 char line[1024];
72 NetDevice dev;
73
74 ObjectArray<NetDevice> *devices = new ObjectArray<NetDevice>(32, 32, true);
75 while(!feof(f))
76 {
77 fgets(line, 1024, f);
78 if (sscanf(line, "%s: %llu %llu %*u %*u %*u %*u %*u %*u %llu %llu %*u %*u %*u %*u %*u %*u",
79 dev.name, &dev.rxBytes, &dev.rxPackets, &dev.txBytes, &dev.txPackets) == 5)
80 {
81 if (NetDeviceToDomId(dev.name, &dev.domId, &dev.netId))
82 {
83 devices->add(new NetDevice(dev));
84 }
85 }
86 }
87 fclose(f);
88 return devices;
89 }
90
91 /**
92 * Get NIC data
93 */
94 static bool GetNicData(libxl_ctx *ctx, uint32_t domId, uint32_t netId, libxl_device_nic *data)
95 {
96 int count;
97 libxl_device_nic *nicList = libxl_device_nic_list(ctx, domId, &count);
98 if (nicList == NULL)
99 return false;
100
101 bool found = false;
102 for(int i = 0; i < count; i++)
103 {
104 if (nicList[i].devid == netId)
105 {
106 memcpy(data, &nicList[i], sizeof(libxl_device_nic));
107 found = true;
108 break;
109 }
110 }
111 return found;
112 }
113
114 /**
115 * Network device data cache
116 */
117 static ObjectArray<NetDevice> *s_cache = NULL;
118 static time_t s_cacheTimestamp = 0;
119 static Mutex s_cacheLock;
120
121 /**
122 * Acquire device data cache and update if needed
123 */
124 inline void AcquireNetDeviceCache()
125 {
126 s_cacheLock.lock();
127 time_t now = time(NULL);
128 if (now - s_cacheTimestamp > 1)
129 {
130 delete s_cache;
131 s_cache = ScanNetworkDevices();
132 if (s_cache != NULL)
133 s_cacheTimestamp = now;
134 }
135 }
136
137 /**
138 * Query network traffic for domain
139 */
140 bool XenQueryDomainNetworkTraffic(uint32_t domId, UINT64 *rxBytes, UINT64 *txBytes, UINT64 *rxPackets, UINT64 *txPackets)
141 {
142 bool success = false;
143
144 *rxBytes = 0;
145 *txBytes = 0;
146 *rxPackets = 0;
147 *txPackets = 0;
148
149 AcquireNetDeviceCache();
150 if (s_cache != NULL)
151 {
152 for(int i = 0; i < s_cache->size(); i++)
153 {
154 NetDevice *d = s_cache->get(i);
155 if (d->domId == domId)
156 {
157 *rxBytes += d->rxBytes;
158 *txBytes += d->txBytes;
159 *rxPackets += d->rxPackets;
160 *txPackets += d->txPackets;
161 }
162 }
163 success = true;
164 }
165 s_cacheLock.unlock();
166 return success;
167 }
168
169 /**
170 * Handler for XEN.Domain.Net.* parameters
171 */
172 LONG H_XenDomainNetStats(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
173 {
174 char domName[256];
175 if (!AgentGetParameterArgA(param, 1, domName, 256))
176 return SYSINFO_RC_UNSUPPORTED;
177
178 char *eptr;
179 uint32_t domId = strtoul(domName, &eptr, 0);
180 if (*eptr != 0)
181 {
182 LONG rc = XenResolveDomainName(domName, &domId);
183 if (rc != SYSINFO_RC_SUCCESS)
184 return rc;
185 }
186
187 LONG rc = SYSINFO_RC_ERROR;
188 UINT64 rxBytes, txBytes, rxPackets, txPackets;
189 if (XenQueryDomainNetworkTraffic(domId, &rxBytes, &txBytes, &rxPackets, &txPackets))
190 {
191 if (arg[0] == 'R')
192 {
193 ret_uint64(value, arg[1] == 'B' ? rxBytes : rxPackets);
194 }
195 else
196 {
197 ret_uint64(value, arg[1] == 'B' ? txBytes : txPackets);
198 }
199 rc = SYSINFO_RC_SUCCESS;
200 }
201 return rc;
202 }
203
204 /**
205 * Handler for XEN.VirtualMachines table
206 */
207 LONG H_XenDomainNetIfTable(const TCHAR *param, const TCHAR *arg, Table *value, AbstractCommSession *session)
208 {
209 libxl_ctx *ctx;
210 XEN_CONNECT(ctx);
211
212 LONG rc = SYSINFO_RC_ERROR;
213
214 AcquireNetDeviceCache();
215 if (s_cache != NULL)
216 {
217 value->addColumn(_T("NAME"), DCI_DT_STRING, _T("Name"), true);
218 value->addColumn(_T("DOMAIN_ID"), DCI_DT_UINT, _T("Domain ID"));
219 value->addColumn(_T("DOMAIN_NAME"), DCI_DT_STRING, _T("Domain name"));
220 value->addColumn(_T("NET_ID"), DCI_DT_UINT, _T("Network ID"));
221 value->addColumn(_T("BRIDGE"), DCI_DT_STRING, _T("Bridge"));
222 value->addColumn(_T("MAC_ADDR"), DCI_DT_STRING, _T("MAC Address"));
223 value->addColumn(_T("IP_ADDR"), DCI_DT_STRING, _T("IP Address"));
224 value->addColumn(_T("MODEL"), DCI_DT_STRING, _T("Model"));
225 value->addColumn(_T("RX_BYTES"), DCI_DT_UINT64, _T("Bytes Rx"));
226 value->addColumn(_T("TX_BYTES"), DCI_DT_UINT64, _T("Bytes Tx"));
227 value->addColumn(_T("RX_PACKETS"), DCI_DT_UINT64, _T("Packets Rx"));
228 value->addColumn(_T("TX_PACKETS"), DCI_DT_UINT64, _T("Packets Tx"));
229 for(int i = 0; i < s_cache->size(); i++)
230 {
231 NetDevice *d = s_cache->get(i);
232 value->addRow();
233 value->set(0, d->name);
234 value->set(1, d->domId);
235 value->set(2, libxl_domid_to_name(ctx, d->domId));
236 value->set(3, d->netId);
237
238 libxl_device_nic nicData;
239 if (GetNicData(ctx, d->domId, d->netId, &nicData))
240 {
241 value->set(4, nicData.bridge);
242 TCHAR buffer[64];
243 value->set(5, MACToStr(nicData.mac, buffer));
244 value->set(6, nicData.ip);
245 value->set(7, nicData.model);
246 }
247
248 value->set(8, d->rxBytes);
249 value->set(9, d->txBytes);
250 value->set(10, d->rxPackets);
251 value->set(11, d->txPackets);
252 }
253 rc = SYSINFO_RC_SUCCESS;
254 }
255
256 s_cacheLock.unlock();
257 libxl_ctx_free(ctx);
258 return rc;
259 }