1 #include <config.h>
2
3 #include "internal.h"
4 #include "testutils.h"
5 #include "datatypes.h"
6 #include "node_device/node_device_driver.h"
7 #include "vircommand.h"
8 #define LIBVIRT_VIRCOMMANDPRIV_H_ALLOW
9 #include "vircommandpriv.h"
10
11 #define VIR_FROM_THIS VIR_FROM_NODEDEV
12
13 #define VIRT_TYPE "QEMU"
14
15 static virNodeDeviceDefParserCallbacks parser_callbacks = {
16 .postParse = nodeDeviceDefPostParse,
17 .validate = nodeDeviceDefValidate
18 };
19
20 struct TestInfo {
21 const char *filename;
22 virMdevctlCommand command;
23 };
24
25 /* capture stdin passed to command */
26 static void
testCommandDryRunCallback(const char * const * args G_GNUC_UNUSED,const char * const * env G_GNUC_UNUSED,const char * input,char ** output G_GNUC_UNUSED,char ** error G_GNUC_UNUSED,int * status G_GNUC_UNUSED,void * opaque G_GNUC_UNUSED)27 testCommandDryRunCallback(const char *const*args G_GNUC_UNUSED,
28 const char *const*env G_GNUC_UNUSED,
29 const char *input,
30 char **output G_GNUC_UNUSED,
31 char **error G_GNUC_UNUSED,
32 int *status G_GNUC_UNUSED,
33 void *opaque G_GNUC_UNUSED)
34 {
35 char **stdinbuf = opaque;
36
37 *stdinbuf = g_strdup(input);
38 }
39
40 typedef virCommand * (*MdevctlCmdFunc)(virNodeDeviceDef *, char **, char **);
41
42
43 static int
testMdevctlCmd(virMdevctlCommand cmd_type,const char * mdevxml,const char * cmdfile,const char * jsonfile)44 testMdevctlCmd(virMdevctlCommand cmd_type,
45 const char *mdevxml,
46 const char *cmdfile,
47 const char *jsonfile)
48 {
49 g_autoptr(virNodeDeviceDef) def = NULL;
50 g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
51 const char *actualCmdline = NULL;
52 g_autofree char *outbuf = NULL;
53 g_autofree char *errbuf = NULL;
54 g_autofree char *stdinbuf = NULL;
55 g_autoptr(virCommand) cmd = NULL;
56 g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew();
57 int create;
58
59 switch (cmd_type) {
60 case MDEVCTL_CMD_CREATE:
61 case MDEVCTL_CMD_DEFINE:
62 create = CREATE_DEVICE;
63 break;
64 case MDEVCTL_CMD_START:
65 case MDEVCTL_CMD_STOP:
66 case MDEVCTL_CMD_UNDEFINE:
67 create = EXISTING_DEVICE;
68 break;
69 case MDEVCTL_CMD_LAST:
70 default:
71 return -1;
72 }
73
74 if (!(def = virNodeDeviceDefParseFile(mdevxml, create, VIRT_TYPE,
75 &parser_callbacks, NULL)))
76 return -1;
77
78 /* this function will set a stdin buffer containing the json configuration
79 * of the device. The json value is captured in the callback above */
80 cmd = nodeDeviceGetMdevctlCommand(def, cmd_type, &outbuf, &errbuf);
81
82 if (!cmd)
83 return -1;
84
85 if (create)
86 virCommandSetDryRun(dryRunToken, &buf, true, true,
87 testCommandDryRunCallback, &stdinbuf);
88 else
89 virCommandSetDryRun(dryRunToken, &buf, true, true, NULL, NULL);
90
91 if (virCommandRun(cmd, NULL) < 0)
92 return -1;
93
94 if (!(actualCmdline = virBufferCurrentContent(&buf)))
95 return -1;
96
97 if (virTestCompareToFileFull(actualCmdline, cmdfile, false) < 0)
98 return -1;
99
100 if (create && virTestCompareToFile(stdinbuf, jsonfile) < 0)
101 return -1;
102
103 return 0;
104 }
105
106
107 static int
testMdevctlHelper(const void * data)108 testMdevctlHelper(const void *data)
109 {
110 const struct TestInfo *info = data;
111 const char *cmd = virMdevctlCommandTypeToString(info->command);
112 g_autofree char *mdevxml = NULL;
113 g_autofree char *cmdlinefile = NULL;
114 g_autofree char *jsonfile = NULL;
115
116 mdevxml = g_strdup_printf("%s/nodedevschemadata/%s.xml", abs_srcdir,
117 info->filename);
118 cmdlinefile = g_strdup_printf("%s/nodedevmdevctldata/%s-%s.argv",
119 abs_srcdir, info->filename, cmd);
120 jsonfile = g_strdup_printf("%s/nodedevmdevctldata/%s-%s.json", abs_srcdir,
121 info->filename, cmd);
122
123 return testMdevctlCmd(info->command, mdevxml, cmdlinefile, jsonfile);
124 }
125
126
127 static int
testMdevctlAutostart(const void * data G_GNUC_UNUSED)128 testMdevctlAutostart(const void *data G_GNUC_UNUSED)
129 {
130 g_autoptr(virNodeDeviceDef) def = NULL;
131 virBuffer buf = VIR_BUFFER_INITIALIZER;
132 const char *actualCmdline = NULL;
133 int ret = -1;
134 g_autoptr(virCommand) enablecmd = NULL;
135 g_autoptr(virCommand) disablecmd = NULL;
136 g_autofree char *errmsg = NULL;
137 /* just concatenate both calls into the same output file */
138 g_autofree char *cmdlinefile =
139 g_strdup_printf("%s/nodedevmdevctldata/mdevctl-autostart.argv",
140 abs_srcdir);
141 g_autofree char *mdevxml =
142 g_strdup_printf("%s/nodedevschemadata/mdev_d069d019_36ea_4111_8f0a_8c9a70e21366.xml",
143 abs_srcdir);
144 g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew();
145
146 if (!(def = virNodeDeviceDefParseFile(mdevxml, CREATE_DEVICE, VIRT_TYPE,
147 &parser_callbacks, NULL)))
148 return -1;
149
150 virCommandSetDryRun(dryRunToken, &buf, true, true, NULL, NULL);
151
152 if (!(enablecmd = nodeDeviceGetMdevctlSetAutostartCommand(def, true, &errmsg)))
153 goto cleanup;
154
155 if (virCommandRun(enablecmd, NULL) < 0)
156 goto cleanup;
157
158 if (!(disablecmd = nodeDeviceGetMdevctlSetAutostartCommand(def, false, &errmsg)))
159 goto cleanup;
160
161 if (virCommandRun(disablecmd, NULL) < 0)
162 goto cleanup;
163
164 if (!(actualCmdline = virBufferCurrentContent(&buf)))
165 goto cleanup;
166
167 if (virTestCompareToFileFull(actualCmdline, cmdlinefile, false) < 0)
168 goto cleanup;
169
170 ret = 0;
171
172 cleanup:
173 virBufferFreeAndReset(&buf);
174 return ret;
175 }
176
177 static int
testMdevctlListDefined(const void * data G_GNUC_UNUSED)178 testMdevctlListDefined(const void *data G_GNUC_UNUSED)
179 {
180 virBuffer buf = VIR_BUFFER_INITIALIZER;
181 const char *actualCmdline = NULL;
182 int ret = -1;
183 g_autoptr(virCommand) cmd = NULL;
184 g_autofree char *output = NULL;
185 g_autofree char *errmsg = NULL;
186 g_autofree char *cmdlinefile =
187 g_strdup_printf("%s/nodedevmdevctldata/mdevctl-list-defined.argv",
188 abs_srcdir);
189 g_autoptr(virCommandDryRunToken) dryRunToken = virCommandDryRunTokenNew();
190
191 cmd = nodeDeviceGetMdevctlListCommand(true, &output, &errmsg);
192
193 if (!cmd)
194 goto cleanup;
195
196 virCommandSetDryRun(dryRunToken, &buf, true, true, NULL, NULL);
197 if (virCommandRun(cmd, NULL) < 0)
198 goto cleanup;
199
200 if (!(actualCmdline = virBufferCurrentContent(&buf)))
201 goto cleanup;
202
203 if (virTestCompareToFileFull(actualCmdline, cmdlinefile, false) < 0)
204 goto cleanup;
205
206 ret = 0;
207
208 cleanup:
209 virBufferFreeAndReset(&buf);
210 return ret;
211 }
212
213 static int
testMdevctlParse(const void * data)214 testMdevctlParse(const void *data)
215 {
216 g_autofree char *buf = NULL;
217 const char *filename = data;
218 g_autofree char *jsonfile = g_strdup_printf("%s/nodedevmdevctldata/%s.json",
219 abs_srcdir, filename);
220 g_autofree char *xmloutfile = g_strdup_printf("%s/nodedevmdevctldata/%s.out.xml",
221 abs_srcdir, filename);
222 int ret = -1;
223 int nmdevs = 0;
224 virNodeDeviceDef **mdevs = NULL;
225 virBuffer xmloutbuf = VIR_BUFFER_INITIALIZER;
226 size_t i;
227
228 if (virFileReadAll(jsonfile, 1024*1024, &buf) < 0) {
229 VIR_TEST_DEBUG("Unable to read file %s", jsonfile);
230 return -1;
231 }
232
233 if ((nmdevs = nodeDeviceParseMdevctlJSON(buf, &mdevs)) < 0) {
234 VIR_TEST_DEBUG("Unable to parse json for %s", filename);
235 return -1;
236 }
237
238 for (i = 0; i < nmdevs; i++) {
239 g_autofree char *devxml = virNodeDeviceDefFormat(mdevs[i]);
240 if (!devxml)
241 goto cleanup;
242 virBufferAddStr(&xmloutbuf, devxml);
243 }
244
245 if (virTestCompareToFileFull(virBufferCurrentContent(&xmloutbuf), xmloutfile, false) < 0)
246 goto cleanup;
247
248 ret = 0;
249
250 cleanup:
251 virBufferFreeAndReset(&xmloutbuf);
252 for (i = 0; i < nmdevs; i++)
253 virNodeDeviceDefFree(mdevs[i]);
254 g_free(mdevs);
255
256 return ret;
257 }
258
259 static void
nodedevTestDriverFree(virNodeDeviceDriverState * drv)260 nodedevTestDriverFree(virNodeDeviceDriverState *drv)
261 {
262 if (!drv)
263 return;
264
265 virNodeDeviceObjListFree(drv->devs);
266 virCondDestroy(&drv->initCond);
267 virMutexDestroy(&drv->lock);
268 g_free(drv->stateDir);
269 g_free(drv);
270 }
271
272 /* Add a fake root 'computer' device */
273 static virNodeDeviceDef *
fakeRootDevice(void)274 fakeRootDevice(void)
275 {
276 virNodeDeviceDef *def = NULL;
277
278 def = g_new0(virNodeDeviceDef, 1);
279 def->caps = g_new0(virNodeDevCapsDef, 1);
280 def->name = g_strdup("computer");
281
282 return def;
283 }
284
285 /* Add a fake pci device that can be used as a parent device for mediated
286 * devices. For our purposes, it only needs to have a name that matches the
287 * parent of the mdev, and it needs a PCI address
288 */
289 static virNodeDeviceDef *
fakePCIDevice(void)290 fakePCIDevice(void)
291 {
292 virNodeDeviceDef *def = NULL;
293 virNodeDevCapPCIDev *pci_dev;
294
295 def = g_new0(virNodeDeviceDef, 1);
296 def->caps = g_new0(virNodeDevCapsDef, 1);
297
298 def->name = g_strdup("pci_0000_00_02_0");
299 def->parent = g_strdup("computer");
300
301 def->caps->data.type = VIR_NODE_DEV_CAP_PCI_DEV;
302 pci_dev = &def->caps->data.pci_dev;
303 pci_dev->domain = 0;
304 pci_dev->bus = 0;
305 pci_dev->slot = 2;
306 pci_dev->function = 0;
307
308 return def;
309 }
310
311
312 /* Add a fake matrix device that can be used as a parent device for mediated
313 * devices. For our purposes, it only needs to have a name that matches the
314 * parent of the mdev, and it needs the proper name
315 */
316 static virNodeDeviceDef *
fakeMatrixDevice(void)317 fakeMatrixDevice(void)
318 {
319 virNodeDeviceDef *def = NULL;
320 virNodeDevCapAPMatrix *cap;
321
322 def = g_new0(virNodeDeviceDef, 1);
323 def->caps = g_new0(virNodeDevCapsDef, 1);
324
325 def->name = g_strdup("ap_matrix");
326 def->parent = g_strdup("computer");
327
328 def->caps->data.type = VIR_NODE_DEV_CAP_AP_MATRIX;
329 cap = &def->caps->data.ap_matrix;
330 cap->addr = g_strdup("matrix");
331
332 return def;
333 }
334 static int
addDevice(virNodeDeviceDef * def)335 addDevice(virNodeDeviceDef *def)
336 {
337 virNodeDeviceObj *obj;
338 if (!def)
339 return -1;
340
341 obj = virNodeDeviceObjListAssignDef(driver->devs, def);
342
343 if (!obj) {
344 virNodeDeviceDefFree(def);
345 return -1;
346 }
347
348 virNodeDeviceObjEndAPI(&obj);
349 return 0;
350 }
351
352 static int
nodedevTestDriverAddTestDevices(void)353 nodedevTestDriverAddTestDevices(void)
354 {
355 if (addDevice(fakeRootDevice()) < 0 ||
356 addDevice(fakePCIDevice()) < 0 ||
357 addDevice(fakeMatrixDevice()) < 0)
358 return -1;
359
360 return 0;
361 }
362
363 /* Bare minimum driver init to be able to test nodedev functionality */
364 static int
nodedevTestDriverInit(void)365 nodedevTestDriverInit(void)
366 {
367 int ret = -1;
368 driver = g_new0(virNodeDeviceDriverState, 1);
369
370 driver->lockFD = -1;
371 if (virMutexInit(&driver->lock) < 0 ||
372 virCondInit(&driver->initCond) < 0) {
373 VIR_TEST_DEBUG("Unable to initialize test nodedev driver");
374 goto error;
375 }
376
377 if (!(driver->devs = virNodeDeviceObjListNew()))
378 goto error;
379
380 return 0;
381
382 error:
383 nodedevTestDriverFree(driver);
384 return ret;
385 }
386
387 static int
mymain(void)388 mymain(void)
389 {
390 int ret = 0;
391
392 if (nodedevTestDriverInit() < 0)
393 return EXIT_FAILURE;
394
395 /* add a mock device to the device list so it can be used as a parent
396 * reference */
397 if (nodedevTestDriverAddTestDevices() < 0) {
398 ret = EXIT_FAILURE;
399 goto done;
400 }
401
402 #define DO_TEST_FULL(desc, func, info) \
403 if (virTestRun(desc, func, info) < 0) \
404 ret = -1;
405
406 #define DO_TEST_CMD(desc, filename, command) \
407 do { \
408 struct TestInfo info = { filename, command }; \
409 DO_TEST_FULL(desc, testMdevctlHelper, &info); \
410 } \
411 while (0)
412
413 #define DO_TEST_CREATE(filename) \
414 DO_TEST_CMD("create mdev " filename, filename, MDEVCTL_CMD_CREATE)
415
416 #define DO_TEST_DEFINE(filename) \
417 DO_TEST_CMD("define mdev " filename, filename, MDEVCTL_CMD_DEFINE)
418
419 #define DO_TEST_STOP(filename) \
420 DO_TEST_CMD("stop mdev " filename, filename, MDEVCTL_CMD_STOP)
421
422 #define DO_TEST_UNDEFINE(filename) \
423 DO_TEST_CMD("undefine mdev" filename, filename, MDEVCTL_CMD_UNDEFINE)
424
425 #define DO_TEST_START(filename) \
426 DO_TEST_CMD("start mdev " filename, filename, MDEVCTL_CMD_START)
427
428 #define DO_TEST_LIST_DEFINED() \
429 DO_TEST_FULL("list defined mdevs", testMdevctlListDefined, NULL)
430
431 #define DO_TEST_AUTOSTART() \
432 DO_TEST_FULL("autostart mdevs", testMdevctlAutostart, NULL)
433
434 #define DO_TEST_PARSE_JSON(filename) \
435 DO_TEST_FULL("parse mdevctl json " filename, testMdevctlParse, filename)
436
437 DO_TEST_CREATE("mdev_d069d019_36ea_4111_8f0a_8c9a70e21366");
438 DO_TEST_CREATE("mdev_fedc4916_1ca8_49ac_b176_871d16c13076");
439 DO_TEST_CREATE("mdev_d2441d39_495e_4243_ad9f_beb3f14c23d9");
440
441 /* Test mdevctl stop command, pass an arbitrary uuid */
442 DO_TEST_STOP("mdev_d069d019_36ea_4111_8f0a_8c9a70e21366");
443
444 DO_TEST_LIST_DEFINED();
445
446 DO_TEST_PARSE_JSON("mdevctl-list-empty");
447 DO_TEST_PARSE_JSON("mdevctl-list-multiple");
448
449 DO_TEST_DEFINE("mdev_d069d019_36ea_4111_8f0a_8c9a70e21366");
450 DO_TEST_DEFINE("mdev_fedc4916_1ca8_49ac_b176_871d16c13076");
451 DO_TEST_DEFINE("mdev_d2441d39_495e_4243_ad9f_beb3f14c23d9");
452
453 DO_TEST_UNDEFINE("mdev_d069d019_36ea_4111_8f0a_8c9a70e21366");
454
455 DO_TEST_START("mdev_d069d019_36ea_4111_8f0a_8c9a70e21366");
456
457 DO_TEST_AUTOSTART();
458
459 done:
460 nodedevTestDriverFree(driver);
461
462 return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
463 }
464
465 VIR_TEST_MAIN(mymain)
466