On a Tuesday in 2020, Jonathon Jongsma wrote:
This adds some internal API to query for persistent mediated devices
that are defined by mdevctl. Following commits will make use of this
information. This just provides the infrastructure and tests for this
feature. One test verifies that we are executing mdevctl with the proper
arguments, and other tests verify that we can parse the returned JSON
and convert it to our own internal node device representations.
Signed-off-by: Jonathon Jongsma <jjongsma(a)redhat.com>
---
src/node_device/node_device_driver.c | 156 ++++++++++++++++++
src/node_device/node_device_driver.h | 13 ++
src/node_device/node_device_udev.c | 19 +--
.../mdevctl-list-defined.argv | 1 +
.../mdevctl-list-multiple-parents.json | 59 +++++++
.../mdevctl-list-multiple-parents.out.xml | 39 +++++
.../mdevctl-list-multiple.json | 59 +++++++
.../mdevctl-list-multiple.out.xml | 39 +++++
.../mdevctl-list-single-noattr.json | 11 ++
.../mdevctl-list-single-noattr.out.xml | 8 +
.../mdevctl-list-single.json | 31 ++++
.../mdevctl-list-single.out.xml | 14 ++
tests/nodedevmdevctltest.c | 98 +++++++++++
13 files changed, 531 insertions(+), 16 deletions(-)
create mode 100644 tests/nodedevmdevctldata/mdevctl-list-defined.argv
create mode 100644 tests/nodedevmdevctldata/mdevctl-list-multiple-parents.json
create mode 100644 tests/nodedevmdevctldata/mdevctl-list-multiple-parents.out.xml
create mode 100644 tests/nodedevmdevctldata/mdevctl-list-multiple.json
create mode 100644 tests/nodedevmdevctldata/mdevctl-list-multiple.out.xml
create mode 100644 tests/nodedevmdevctldata/mdevctl-list-single-noattr.json
create mode 100644 tests/nodedevmdevctldata/mdevctl-list-single-noattr.out.xml
create mode 100644 tests/nodedevmdevctldata/mdevctl-list-single.json
create mode 100644 tests/nodedevmdevctldata/mdevctl-list-single.out.xml
diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c
index f766fd9f32..3b042e9a45 100644
--- a/src/node_device/node_device_driver.c
+++ b/src/node_device/node_device_driver.c
@@ -812,6 +812,137 @@ virMdevctlStop(virNodeDeviceDefPtr def)
}
+virCommandPtr
+nodeDeviceGetMdevctlListCommand(bool defined,
+ char **output)
+{
+ virCommandPtr cmd = virCommandNewArgList(MDEVCTL,
+ "list",
+ "--dumpjson",
+ NULL);
+
+ if (defined)
+ virCommandAddArg(cmd, "--defined");
+
+ virCommandSetOutputBuffer(cmd, output);
+
+ return cmd;
+}
+
+
+static void mdevGenerateDeviceName(virNodeDeviceDefPtr dev)
+{
+ nodeDeviceGenerateName(dev, "mdev", dev->caps->data.mdev.uuid,
NULL);
+}
+
+int
+nodeDeviceParseMdevctlJSON(const char *jsonstring,
+ virNodeDeviceDefPtr **devs)
+{
+ int n;
+ g_autoptr(virJSONValue) json_devicelist = NULL;
+ virNodeDeviceDefPtr *outdevs = NULL;
+ size_t noutdevs = 0;
+ size_t i, j, k, m;
+
+ json_devicelist = virJSONValueFromString(jsonstring);
+
+ if (!json_devicelist)
+ goto parsefailure;
+
+ if (!virJSONValueIsArray(json_devicelist))
+ goto parsefailure;
+
+ n = virJSONValueArraySize(json_devicelist);
+
+ for (i = 0; i < n; i++) {
+ virJSONValuePtr obj = virJSONValueArrayGet(json_devicelist, i);
+ int nparents;
+
+ if (!virJSONValueIsObject(obj))
+ goto parsefailure;
+
+ nparents = virJSONValueObjectKeysNumber(obj);
+
+ for (j = 0; j < nparents; j++) {
+ const char *parent = virJSONValueObjectGetKey(obj, j);
+ virJSONValuePtr child_array = virJSONValueObjectGetValue(obj, j);
+ int nchildren;
+
+ if (!virJSONValueIsArray(child_array))
+ goto parsefailure;
+
+ nchildren = virJSONValueArraySize(child_array);
+
+ for (k = 0; k < nchildren; k++) {
This is nested quite deep. Maybe a helper function like
'nodeDeviceParseMdevctlSingleDevJSON'
would help get rid of it.
+ virNodeDevCapMdevPtr mdev;
+ const char *uuid;
+ virJSONValuePtr props;
+ g_autoptr(virNodeDeviceDef) child = g_new0(virNodeDeviceDef, 1);
+ virJSONValuePtr child_obj = virJSONValueArrayGet(child_array, k);
+
+ /* the child object should have a single key equal to its uuid.
+ * The value is an object describing the properties of the mdev */
+ if (virJSONValueObjectKeysNumber(child_obj) != 1)
+ goto parsefailure;
+
+ uuid = virJSONValueObjectGetKey(child_obj, 0);
+ props = virJSONValueObjectGetValue(child_obj, 0);
+
+ child->parent = g_strdup(parent);
+ child->caps = g_new0(virNodeDevCapsDef, 1);
+ child->caps->data.type = VIR_NODE_DEV_CAP_MDEV;
+
+ mdev = &child->caps->data.mdev;
+ mdev->uuid = g_strdup(uuid);
+ mdev->type =
+ g_strdup(virJSONValueObjectGetString(props, "mdev_type"));
+
+ virJSONValuePtr attrs = virJSONValueObjectGet(props, "attrs");
+
+ if (attrs && virJSONValueIsArray(attrs)) {
+ int nattrs = virJSONValueArraySize(attrs);
+
+ mdev->attributes = g_new0(virMediatedDeviceAttrPtr, nattrs);
+ mdev->nattributes = nattrs;
+
+ for (m = 0; m < nattrs; m++) {
+ virJSONValuePtr attr = virJSONValueArrayGet(attrs, m);
+ virMediatedDeviceAttrPtr attribute;
+
+ if (!virJSONValueIsObject(attr) ||
+ virJSONValueObjectKeysNumber(attr) != 1)
+ goto parsefailure;
+
+ attribute = g_new0(virMediatedDeviceAttr, 1);
+ attribute->name = g_strdup(virJSONValueObjectGetKey(attr,
0));
+ virJSONValuePtr value = virJSONValueObjectGetValue(attr, 0);
+ attribute->value = g_strdup(virJSONValueGetString(value));
+ mdev->attributes[m] = attribute;
+ }
+ }
+ mdevGenerateDeviceName(child);
+
+ if (VIR_APPEND_ELEMENT(outdevs, noutdevs, child) <
0)
+ child = NULL;
The indentation is off here. Note that VIR_APPEND_ELEMENT aborts on
failure and NULL's the element on success, so the assignment here is
pointless.
+ }
+ }
+ }
+
+ *devs = outdevs;
+ return noutdevs;
+
+ parsefailure:
s/parsefailure/error/
+ for (i = 0; i < noutdevs; i++)
+ virNodeDeviceDefFree(outdevs[i]);
+ VIR_FREE(outdevs);
+
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("unable to parse JSON response"));
+ return -1;
+}
+
+
int
nodeDeviceDestroy(virNodeDevicePtr device)
{
@@ -931,3 +1062,28 @@ nodedevRegister(void)
# endif
#endif
}
+
+
+void
+nodeDeviceGenerateName(virNodeDeviceDefPtr def,
+ const char *subsystem,
+ const char *sysname,
+ const char *s)
+{
+ size_t i;
+ g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+ virBufferAsprintf(&buf, "%s_%s",
+ subsystem,
+ sysname);
+
+ if (s != NULL)
+ virBufferAsprintf(&buf, "_%s", s);
+
+ def->name = virBufferContentAndReset(&buf);
+
+ for (i = 0; i < strlen(def->name); i++) {
+ if (!(g_ascii_isalnum(*(def->name + i))))
+ *(def->name + i) = '_';
+ }
+}
+static int
+testMdevctlParse(const void *data)
+{
+ g_autofree char *buf = NULL;
+ const char *filename = data;
+ g_autofree char *jsonfile =
g_strdup_printf("%s/nodedevmdevctldata/%s.json",
+ abs_srcdir, filename);
+ g_autofree char *xmloutfile =
g_strdup_printf("%s/nodedevmdevctldata/%s.out.xml",
+ abs_srcdir, filename);
+ g_autofree char *actualxml = NULL;
Fails to compile with clang:
../tests/nodedevmdevctltest.c:190:22: error: unused variable 'actualxml'
[-Werror,-Wunused-variable]
g_autofree char *actualxml = NULL;
^
1 error generated.
Jano
+ int ret = -1;
+ int nmdevs = 0;
+ virNodeDeviceDefPtr *mdevs = NULL;
+ virBuffer xmloutbuf = VIR_BUFFER_INITIALIZER;
+ size_t i;