[PATCH v2 00/11] nwfilter: Add support for user defined metadata

This patchset adds support for the following user defined metadata fields for network filters. - <title>: A short description of the filter. - <description>: Any documentation that the user wants to store. - <metadata>: Other metadata in XML form. Two new public APIs have been added to work with these fields: - virNWFilterGetMetadata() - virNWFilterSetMetadata() This is a v2 of: https://listman.redhat.com/archives/libvir-list/2023-September/241788.html Diff to v1: - Added test cases in tests/nwfilterxml2xmltest.c - Implemented test driver for nwfilter - Added an API testcase tests/nwfiltermetadatatest.c - Merged patches as instructed K Shiva Kiran (11): nwfilter_conf: Add schema and parser logic for nwfilter metadata nwfilter_conf: Introduce public API to get/set user metadata nwfilter: Implement RPC virsh: Add new command `nwfilter-desc` virsh: Add new command `nwfilter-metadata` virsh: Add option --title for nwfilter-list virnwfilterobj: Add obj get and set methods for nwfilter metadata nwfilter_driver: Add Driver implementation for metadata test_driver: Implement test driver for network filters test_driver: Implement test driver for metadata APIs NEWS: Introduce user-defined metadata fields for NWFilter object NEWS.rst | 18 + docs/formatnwfilter.rst | 31 ++ docs/manpages/virsh.rst | 93 ++++- include/libvirt/libvirt-nwfilter.h | 27 ++ include/libvirt/virterror.h | 1 + src/conf/nwfilter_conf.c | 35 +- src/conf/nwfilter_conf.h | 8 + src/conf/schemas/nwfilter.rng | 9 + src/conf/virnwfilterobj.c | 148 ++++++++ src/conf/virnwfilterobj.h | 13 + src/driver-nwfilter.h | 15 + src/libvirt-nwfilter.c | 154 ++++++++ src/libvirt_private.syms | 2 + src/libvirt_public.syms | 6 + src/nwfilter/nwfilter_driver.c | 61 ++++ src/remote/remote_driver.c | 2 + src/remote/remote_protocol.x | 34 +- src/remote_protocol-structs | 19 + src/test/test_driver.c | 287 ++++++++++++++- src/util/virerror.c | 3 + tests/meson.build | 1 + tests/nwfiltermetadatatest.c | 297 ++++++++++++++++ tests/nwfilterxml2xmlin/metadata-test.xml | 12 + tests/nwfilterxml2xmlout/metadata-test.xml | 10 + tests/nwfilterxml2xmltest.c | 1 + tools/virsh-nwfilter.c | 387 ++++++++++++++++++++- tools/virsh-util.c | 25 ++ tools/virsh-util.h | 9 + 28 files changed, 1698 insertions(+), 10 deletions(-) create mode 100644 tests/nwfiltermetadatatest.c create mode 100644 tests/nwfilterxml2xmlin/metadata-test.xml create mode 100644 tests/nwfilterxml2xmlout/metadata-test.xml -- 2.42.0

Adds three new elements <title>, <description> and <metadata> Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- docs/formatnwfilter.rst | 31 ++++++++++++++++++++++ src/conf/nwfilter_conf.c | 30 +++++++++++++++++++++ src/conf/nwfilter_conf.h | 5 ++++ src/conf/schemas/nwfilter.rng | 9 +++++++ tests/nwfilterxml2xmlin/metadata-test.xml | 12 +++++++++ tests/nwfilterxml2xmlout/metadata-test.xml | 10 +++++++ tests/nwfilterxml2xmltest.c | 1 + 7 files changed, 98 insertions(+) create mode 100644 tests/nwfilterxml2xmlin/metadata-test.xml create mode 100644 tests/nwfilterxml2xmlout/metadata-test.xml diff --git a/docs/formatnwfilter.rst b/docs/formatnwfilter.rst index 434da5b1fd..94a35abfce 100644 --- a/docs/formatnwfilter.rst +++ b/docs/formatnwfilter.rst @@ -419,6 +419,37 @@ better organized for more efficient processing by the firewall subsystem of the underlying host. Currently the system only supports the chains ``root, ipv4, ipv6, arp and rarp``. +General Metadata +~~~~~~~~~~~~~~~~ + +:: + + <filter name='clean-traffic' filter='arp'> + <uuid>6ef53069-ba34-94a0-d33d-17751b9b8cb1</uuid> + <title>A short description - title - of the filter</title> + <description>Some human readable description</description> + <metadata> + <app1:foo xmlns:app1="http://app1.org/app1/">..</app1:foo> + <app2:bar xmlns:app2="http://app1.org/app2/">..</app2:bar> + </metadata> + ... + </filter> + +``title`` + The optional element ``title`` provides space for a short description of the + filter. The title should not contain any newlines. :since:`Since 9.9.0` . +``description`` + The content of the ``description`` element provides a human readable + description of the filter. This data is not used by libvirt in any + way, it can contain any information the user wants. :since:`Since 9.9.0` +``metadata`` + The ``metadata`` node can be used by applications to store custom metadata in + the form of XML nodes/trees. Applications must use custom namespaces on their + XML nodes/trees, with only one top-level element per namespace (if the + application needs structure, they should have sub-elements to their namespace + element). :since:`Since 9.9.0` + + References to other filters ~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/conf/nwfilter_conf.c b/src/conf/nwfilter_conf.c index 35f6efbbe2..d03f78af4d 100644 --- a/src/conf/nwfilter_conf.c +++ b/src/conf/nwfilter_conf.c @@ -327,6 +327,10 @@ virNWFilterDefFree(virNWFilterDef *def) g_free(def->filterEntries); g_free(def->chainsuffix); + g_free(def->title); + g_free(def->description); + xmlFreeNode(def->metadata); + g_free(def); } @@ -2516,6 +2520,7 @@ virNWFilterDefParseXML(xmlXPathContextPtr ctxt) virNWFilterEntry *entry; int chain_priority; const char *name_prefix; + xmlNodePtr metadataNode = NULL; ret = g_new0(virNWFilterDef, 1); @@ -2582,6 +2587,23 @@ virNWFilterDefParseXML(xmlXPathContextPtr ctxt) } } + /* Extract short description of filter (title) */ + ret->title = virXPathString("string(./title[1])", ctxt); + if (ret->title && strchr(ret->title, '\n')) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Network filter title can't contain newlines")); + return NULL; + } + + /* Extract documentation if present */ + ret->description = virXPathString("string(./description[1])", ctxt); + + /* Extract custom metadata */ + if ((metadataNode = virXPathNode("./metadata[1]", ctxt)) != NULL) { + ret->metadata = xmlCopyNode(metadataNode, 1); + virXMLNodeSanitizeNamespaces(ret->metadata); + } + curr = curr->children; while (curr != NULL) { @@ -2873,6 +2895,14 @@ virNWFilterDefFormat(const virNWFilterDef *def) virUUIDFormat(def->uuid, uuid); virBufferAsprintf(&buf, "<uuid>%s</uuid>\n", uuid); + virBufferEscapeString(&buf, "<title>%s</title>\n", def->title); + + virBufferEscapeString(&buf, "<description>%s</description>\n", + def->description); + + if (virXMLFormatMetadata(&buf, def->metadata) < 0) + return NULL; + for (i = 0; i < def->nentries; i++) { if (virNWFilterEntryFormat(&buf, def->filterEntries[i]) < 0) return NULL; diff --git a/src/conf/nwfilter_conf.h b/src/conf/nwfilter_conf.h index 22c2fb51f0..34de6eab3d 100644 --- a/src/conf/nwfilter_conf.h +++ b/src/conf/nwfilter_conf.h @@ -517,6 +517,11 @@ struct _virNWFilterDef { size_t nentries; virNWFilterEntry **filterEntries; + + /* User-defined metadata */ + char* title; + char* description; + xmlNodePtr metadata; }; diff --git a/src/conf/schemas/nwfilter.rng b/src/conf/schemas/nwfilter.rng index 262bd551e3..c56bbac732 100644 --- a/src/conf/schemas/nwfilter.rng +++ b/src/conf/schemas/nwfilter.rng @@ -14,6 +14,15 @@ <ref name="UUID"/> </element> </optional> + <optional> + <ref name="title"/> + </optional> + <optional> + <ref name="description"/> + </optional> + <optional> + <ref name="metadata"/> + </optional> <zeroOrMore> <choice> <element name="filterref"> diff --git a/tests/nwfilterxml2xmlin/metadata-test.xml b/tests/nwfilterxml2xmlin/metadata-test.xml new file mode 100644 index 0000000000..db2c7d2828 --- /dev/null +++ b/tests/nwfilterxml2xmlin/metadata-test.xml @@ -0,0 +1,12 @@ +<filter name='testcase' chain='root'> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a8e</uuid> + <title>This is a title</title> + <description>This is a description. +It can contain newlines.</description> + + <!-- intentional mis-indentation --> + <metadata> + <app1:foo xmlns:app1="http://foo.org/">fooish</app1:foo> + <app2:bar xmlns:app2="http://bar.com/" maman="baz">barish</app2:bar> + </metadata> +</filter> diff --git a/tests/nwfilterxml2xmlout/metadata-test.xml b/tests/nwfilterxml2xmlout/metadata-test.xml new file mode 100644 index 0000000000..fe8bdfee01 --- /dev/null +++ b/tests/nwfilterxml2xmlout/metadata-test.xml @@ -0,0 +1,10 @@ +<filter name='testcase' chain='root'> + <uuid>81ff0d90-c91e-6742-64da-4a736edb9a8e</uuid> + <title>This is a title</title> + <description>This is a description. +It can contain newlines.</description> + <metadata> + <app1:foo xmlns:app1="http://foo.org/">fooish</app1:foo> + <app2:bar xmlns:app2="http://bar.com/" maman="baz">barish</app2:bar> + </metadata> +</filter> diff --git a/tests/nwfilterxml2xmltest.c b/tests/nwfilterxml2xmltest.c index c2481481ee..6a378a853a 100644 --- a/tests/nwfilterxml2xmltest.c +++ b/tests/nwfilterxml2xmltest.c @@ -130,6 +130,7 @@ mymain(void) DO_TEST("iter-test3", false); DO_TEST("ipset-test", false); + DO_TEST("metadata-test", false); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -- 2.42.0

Two new APIs to work with user defined metadata: - virNWFilterSetMetadata() - virNWFilterGetMetadata() Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- include/libvirt/libvirt-nwfilter.h | 27 +++++ include/libvirt/virterror.h | 1 + src/driver-nwfilter.h | 15 +++ src/libvirt-nwfilter.c | 154 +++++++++++++++++++++++++++++ src/libvirt_public.syms | 6 ++ src/util/virerror.c | 3 + 6 files changed, 206 insertions(+) diff --git a/include/libvirt/libvirt-nwfilter.h b/include/libvirt/libvirt-nwfilter.h index 33b842b464..1bfe05d633 100644 --- a/include/libvirt/libvirt-nwfilter.h +++ b/include/libvirt/libvirt-nwfilter.h @@ -159,4 +159,31 @@ int virNWFilterBindingDelete(virNWFilterBindingPtr binding); int virNWFilterBindingRef(virNWFilterBindingPtr binding); int virNWFilterBindingFree(virNWFilterBindingPtr binding); +/** + * virNWFilterMetadataType: + * + * Since: 9.9.0 + */ +typedef enum { + VIR_NWFILTER_METADATA_DESCRIPTION = 0, /* Operate on <description> (Since: 9.9.0) */ + VIR_NWFILTER_METADATA_TITLE = 1, /* Operate on <title> (Since: 9.9.0) */ + VIR_NWFILTER_METADATA_ELEMENT = 2, /* Operate on <metadata> (Since: 9.9.0) */ + +# ifdef VIR_ENUM_SENTINELS + VIR_NWFILTER_METADATA_LAST /* (Since: 9.9.0) */ +# endif +} virNWFilterMetadataType; + +int virNWFilterSetMetadata(virNWFilterPtr nwfilter, + int type, + const char *metadata, + const char *key, + const char *uri, + unsigned int flags); + +char * virNWFilterGetMetadata(virNWFilterPtr nwfilter, + int type, + const char *uri, + unsigned int flags); + #endif /* LIBVIRT_NWFILTER_H */ diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 224eddc9e4..04d06b4bda 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -349,6 +349,7 @@ typedef enum { VIR_ERR_CHECKPOINT_INCONSISTENT = 109, /* checkpoint can't be used (Since: 6.10.0) */ VIR_ERR_MULTIPLE_DOMAINS = 110, /* more than one matching domain found (Since: 7.1.0) */ VIR_ERR_NO_NETWORK_METADATA = 111, /* Network metadata is not present (Since: 9.7.0) */ + VIR_ERR_NO_NWFILTER_METADATA = 112, /* NWFilter metadata is not present (Since: 9.9.0) */ # ifdef VIR_ENUM_SENTINELS VIR_ERR_NUMBER_LAST /* (Since: 5.0.0) */ diff --git a/src/driver-nwfilter.h b/src/driver-nwfilter.h index 1ec591ece9..8d38dbd23c 100644 --- a/src/driver-nwfilter.h +++ b/src/driver-nwfilter.h @@ -86,6 +86,19 @@ typedef int typedef int (*virDrvNWFilterBindingFree)(virNWFilterBindingPtr binding); +typedef int +(*virDrvNWFilterSetMetadata)(virNWFilterPtr nwfilter, + int type, + const char *metadata, + const char *key, + const char *uri, + unsigned int flags); + +typedef char * +(*virDrvNWFilterGetMetadata)(virNWFilterPtr nwfilter, + int type, + const char *uri, + unsigned int flags); typedef struct _virNWFilterDriver virNWFilterDriver; @@ -111,4 +124,6 @@ struct _virNWFilterDriver { virDrvNWFilterBindingCreateXML nwfilterBindingCreateXML; virDrvNWFilterBindingDelete nwfilterBindingDelete; virDrvNWFilterBindingGetXMLDesc nwfilterBindingGetXMLDesc; + virDrvNWFilterSetMetadata nwfilterSetMetadata; + virDrvNWFilterGetMetadata nwfilterGetMetadata; }; diff --git a/src/libvirt-nwfilter.c b/src/libvirt-nwfilter.c index ff82cc3b7f..a496bc9f0a 100644 --- a/src/libvirt-nwfilter.c +++ b/src/libvirt-nwfilter.c @@ -920,3 +920,157 @@ virNWFilterBindingRef(virNWFilterBindingPtr binding) virObjectRef(binding); return 0; } + + +/** + * virNWFilterSetMetadata: + * @nwfilter: a network filter object + * @type: type of metadata, from virNWFilterMetadataType + * @metadata: new metadata text + * @key: XML namespace key, or NULL + * @uri: XML namespace URI, or NULL + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Sets the appropriate nwfilter element given by @type to the + * value of @metadata. + * For options VIR_NWFILTER_METADATA_TITLE and VIR_NWFILTER_METADATA_DESCRIPTION + * @key and @uri are irrelevant and must be set to NULL. + * + * For type VIR_NWFILTER_METADATA_ELEMENT @metadata must be a well-formed + * XML belonging to namespace defined by @uri with local name @key. + * + * Passing NULL for @metadata says to remove that element from the + * network filter XML (passing the empty string leaves the element present). + * + * The resulting metadata will be present in virNWFilterGetXMLDesc(), + * as well as quick access through virNWFilterGetMetadata(). + * + * Returns 0 on success, -1 in case of failure. + * + * Since: 9.9.0 + */ +int +virNWFilterSetMetadata(virNWFilterPtr nwfilter, + int type, + const char *metadata, + const char *key, + const char *uri, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DEBUG("nwfilter=%p, type=%d, metadata='%s', key='%s', uri='%s', flags=0x%x", + nwfilter, type, NULLSTR(metadata), NULLSTR(key), NULLSTR(uri), + flags); + + virResetLastError(); + + virCheckNWFilterReturn(nwfilter, -1); + conn = nwfilter->conn; + + virCheckReadOnlyGoto(conn->flags, error); + + switch (type) { + case VIR_NWFILTER_METADATA_TITLE: + if (metadata && strchr(metadata, '\n')) { + virReportInvalidArg(metadata, "%s", + _("metadata title can't contain newlines")); + goto error; + } + G_GNUC_FALLTHROUGH; + case VIR_NWFILTER_METADATA_DESCRIPTION: + virCheckNullArgGoto(uri, error); + virCheckNullArgGoto(key, error); + break; + case VIR_NWFILTER_METADATA_ELEMENT: + virCheckNonNullArgGoto(uri, error); + if (metadata) + virCheckNonNullArgGoto(key, error); + break; + default: + /* For future expansion */ + break; + } + + if (conn->nwfilterDriver->nwfilterSetMetadata) { + int ret; + ret = conn->nwfilterDriver->nwfilterSetMetadata(nwfilter, type, metadata, key, uri, + flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(nwfilter->conn); + return -1; +} + + +/** + * virNWFilterGetMetadata: + * @nwfilter: a network filter object + * @type: type of metadata, from virNWFilterMetadataType + * @uri: XML namespace identifier + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Retrieves the appropriate network filter element given by @type. + * If VIR_NWFILTER_METADATA_ELEMENT is requested parameter @uri + * must be set to the name of the namespace the requested elements + * belong to, otherwise must be NULL. + * + * If an element of the network filter XML is not present, the resulting + * error will be VIR_ERR_NO_NWFILTER_METADATA. This method forms + * a shortcut for seeing information from virNWFilterSetMetadata() + * without having to go through virNWFilterGetXMLDesc(). + * + * Returns the metadata string on success (caller must free), + * or NULL in case of failure. + * + * Since: 9.9.0 + */ +char * +virNWFilterGetMetadata(virNWFilterPtr nwfilter, + int type, + const char *uri, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DEBUG("nwfilter=%p, type=%d, uri='%s', flags=0x%x", + nwfilter, type, NULLSTR(uri), flags); + + virResetLastError(); + + virCheckNWFilterReturn(nwfilter, NULL); + + switch (type) { + case VIR_NWFILTER_METADATA_TITLE: + case VIR_NWFILTER_METADATA_DESCRIPTION: + virCheckNullArgGoto(uri, error); + break; + case VIR_NWFILTER_METADATA_ELEMENT: + virCheckNonNullArgGoto(uri, error); + break; + default: + /* For future expansion */ + break; + } + + conn = nwfilter->conn; + + if (conn->nwfilterDriver->nwfilterGetMetadata) { + char *ret; + if (!(ret = conn->nwfilterDriver->nwfilterGetMetadata(nwfilter, type, uri, flags))) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(nwfilter->conn); + return NULL; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index bd1e916d2a..02d32567a4 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -938,4 +938,10 @@ LIBVIRT_9.7.0 { virNetworkSetMetadata; } LIBVIRT_9.0.0; +LIBVIRT_9.9.0 { + global: + virNWFilterGetMetadata; + virNWFilterSetMetadata; +} LIBVIRT_9.7.0; + # .... define new API here using predicted next version number .... diff --git a/src/util/virerror.c b/src/util/virerror.c index 227a182417..a1d0d73e5d 100644 --- a/src/util/virerror.c +++ b/src/util/virerror.c @@ -1290,6 +1290,9 @@ static const virErrorMsgTuple virErrorMsgStrings[] = { [VIR_ERR_NO_NETWORK_METADATA] = { N_("metadata not found"), N_("metadata not found: %1$s") }, + [VIR_ERR_NO_NWFILTER_METADATA] = { + N_("metadata not found"), + N_("metadata not found: %1$s") }, }; G_STATIC_ASSERT(G_N_ELEMENTS(virErrorMsgStrings) == VIR_ERR_NUMBER_LAST); -- 2.42.0

Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- src/remote/remote_driver.c | 2 ++ src/remote/remote_protocol.x | 34 +++++++++++++++++++++++++++++++++- src/remote_protocol-structs | 19 +++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 857acef69a..0573e1afb3 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8300,6 +8300,8 @@ static virNWFilterDriver nwfilter_driver = { .nwfilterBindingCreateXML = remoteNWFilterBindingCreateXML, /* 4.5.0 */ .nwfilterBindingDelete = remoteNWFilterBindingDelete, /* 4.5.0 */ .nwfilterBindingGetXMLDesc = remoteNWFilterBindingGetXMLDesc, /* 4.5.0 */ + .nwfilterSetMetadata = remoteNWFilterSetMetadata, /* 9.9.0 */ + .nwfilterGetMetadata = remoteNWFilterGetMetadata, /* 9.9.0 */ }; static virConnectDriver connect_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index e295b0acc3..9fbf93e588 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1684,6 +1684,25 @@ struct remote_nwfilter_get_xml_desc_ret { remote_nonnull_string xml; }; +struct remote_nwfilter_set_metadata_args { + remote_nonnull_nwfilter nwfilter; + int type; + remote_string metadata; + remote_string key; + remote_string uri; + unsigned int flags; +}; + +struct remote_nwfilter_get_metadata_args { + remote_nonnull_nwfilter nwfilter; + int type; + remote_string uri; + unsigned int flags; +}; + +struct remote_nwfilter_get_metadata_ret { + remote_nonnull_string metadata; +}; /* Interface calls: */ @@ -7021,5 +7040,18 @@ enum remote_procedure { * @generate: both * @acl: none */ - REMOTE_PROC_NETWORK_EVENT_CALLBACK_METADATA_CHANGE = 446 + REMOTE_PROC_NETWORK_EVENT_CALLBACK_METADATA_CHANGE = 446, + + /** + * @generate: both + * @acl: nwfilter:write + * @acl: nwfilter:save + */ + REMOTE_PROC_NWFILTER_SET_METADATA = 447, + + /** + * @generate: both + * @acl: nwfilter:read + */ + REMOTE_PROC_NWFILTER_GET_METADATA = 448 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 924ca41825..2ee491a3fe 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1220,6 +1220,23 @@ struct remote_nwfilter_get_xml_desc_args { struct remote_nwfilter_get_xml_desc_ret { remote_nonnull_string xml; }; +struct remote_nwfilter_set_metadata_args { + remote_nonnull_nwfilter nwfilter; + int type; + remote_string metadata; + remote_string key; + remote_string uri; + u_int flags; +}; +struct remote_nwfilter_get_metadata_args { + remote_nonnull_nwfilter nwfilter; + int type; + remote_string uri; + u_int flags; +}; +struct remote_nwfilter_get_metadata_ret { + remote_nonnull_string metadata; +}; struct remote_connect_num_of_interfaces_ret { int num; }; @@ -3743,4 +3760,6 @@ enum remote_procedure { REMOTE_PROC_NETWORK_SET_METADATA = 444, REMOTE_PROC_NETWORK_GET_METADATA = 445, REMOTE_PROC_NETWORK_EVENT_CALLBACK_METADATA_CHANGE = 446, + REMOTE_PROC_NWFILTER_SET_METADATA = 447, + REMOTE_PROC_NWFILTER_GET_METADATA = 448, }; -- 2.42.0

This command can be used to view/modify the `<title>` and `<description>` fields of the Network filter object. Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- docs/manpages/virsh.rst | 40 ++++++++ tools/virsh-nwfilter.c | 209 ++++++++++++++++++++++++++++++++++++++++ tools/virsh-util.c | 25 +++++ tools/virsh-util.h | 9 ++ 4 files changed, 283 insertions(+) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index 4ae3bb4d93..3c7cbf1e11 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -8134,6 +8134,46 @@ The editor used can be supplied by the ``$VISUAL`` or ``$EDITOR`` environment variables, and defaults to ``vi``. +nwfilter-desc +------------- + +**Syntax:** + +:: + + nwfilter-desc [--nwfilter] nwfilter-name + [--title] [--edit] [--remove] + [--new-desc new-value] + +Show or modify description and title of a network filter. + +These values are user fields that allow storing arbitrary textual data to +allow easy identification of network filters. +Title should be short, although it's not enforced. +(See also ``nwfilter-metadata`` that works with XML based network filter metadata.) + +- *--title* + + Specifies to operate on the title field instead of description. + +- *--edit* + + Opens an editor with the current title or description. + Modifications to the contents will be saved back. + Alternatively, the new contents can be provided via the *--new-desc* option. + +- *--remove* + + Removes the title or description field. + +- *--new-desc* + + Stores the provided title/description string. + +If neither of *--edit* or *--new-desc* are specified, the title or description +is displayed instead of being modified. + + NWFILTER BINDING COMMANDS ========================= diff --git a/tools/virsh-nwfilter.c b/tools/virsh-nwfilter.c index 92b2b7b3bc..615d126def 100644 --- a/tools/virsh-nwfilter.c +++ b/tools/virsh-nwfilter.c @@ -26,6 +26,7 @@ #include "viralloc.h" #include "virfile.h" #include "vsh-table.h" +#include "virxml.h" virNWFilterPtr virshCommandOptNWFilterBy(vshControl *ctl, const vshCmd *cmd, @@ -345,6 +346,53 @@ virshNWFilterListCollect(vshControl *ctl, return list; } +/* extract description or title from nwfilter xml */ +static char * +virshGetNWFilterDescription(vshControl *ctl, virNWFilterPtr nwfilter, + bool title, unsigned int flags, + unsigned int queryflags) +{ + char *desc = NULL; + g_autoptr(xmlDoc) doc = NULL; + g_autoptr(xmlXPathContext) ctxt = NULL; + int type; + + if (title) + type = VIR_NWFILTER_METADATA_TITLE; + else + type = VIR_NWFILTER_METADATA_DESCRIPTION; + + if ((desc = virNWFilterGetMetadata(nwfilter, type, NULL, flags))) { + return desc; + } else { + int errCode = virGetLastErrorCode(); + + if (errCode == VIR_ERR_NO_NWFILTER_METADATA) { + desc = g_strdup(""); + vshResetLibvirtError(); + return desc; + } + + if (errCode != VIR_ERR_NO_SUPPORT) + return desc; + } + + /* fall back to xml */ + if (virshNWFilterGetXMLFromNWFilter(ctl, nwfilter, queryflags, &doc, &ctxt) < 0) + return NULL; + + if (title) + desc = virXPathString("string(./title[1])", ctxt); + else + desc = virXPathString("string(./description[1])", ctxt); + + if (!desc) + desc = g_strdup(""); + + return desc; +} + + /* * "nwfilter-list" command */ @@ -768,6 +816,161 @@ cmdNWFilterBindingList(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED) } +/* + * "nwfilter-desc" command + */ +static const vshCmdInfo info_nwfilter_desc[] = { + {.name = "help", + .data = N_("show or set network filter's description or title") + }, + {.name = "desc", + .data = N_("Allows setting or modifying the description or title of a network filter.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_nwfilter_desc[] = { + {.name = "nwfilter", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("network filter name or uuid"), + .completer = virshNWFilterNameCompleter, + }, + {.name = "title", + .type = VSH_OT_BOOL, + .help = N_("modify/get the title instead of description") + }, + {.name = "edit", + .type = VSH_OT_BOOL, + .help = N_("open an editor to modify the description") + }, + {.name = "remove", + .type = VSH_OT_BOOL, + .help = N_("remove the element") + }, + {.name = "new-desc", + .type = VSH_OT_ARGV, + .help = N_("message") + }, + {.name = NULL} +}; + +static bool +cmdNWFilterDesc(vshControl *ctl, const vshCmd *cmd) +{ + g_autoptr(virshNWFilter) nwfilter = NULL; + bool title = vshCommandOptBool(cmd, "title"); + bool edit = vshCommandOptBool(cmd, "edit"); + bool remove = vshCommandOptBool(cmd, "remove"); + int type; + g_autofree char *descArg = NULL; + const vshCmdOpt *opt = NULL; + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + unsigned int flags = 0; + unsigned int queryflags = 0; + + VSH_EXCLUSIVE_OPTIONS("remove", "edit"); + + if (!(nwfilter = virshCommandOptNWFilter(ctl, cmd, NULL))) + return false; + + if (title) + type = VIR_NWFILTER_METADATA_TITLE; + else + type = VIR_NWFILTER_METADATA_DESCRIPTION; + + + while ((opt = vshCommandOptArgv(ctl, cmd, opt))) + virBufferAsprintf(&buf, "%s ", opt->data); + + virBufferTrim(&buf, " "); + + descArg = virBufferContentAndReset(&buf); + + if (remove) { + + if (descArg) { + vshPrintExtra(ctl, "unexpected data: \'%s\'", descArg); + return false; + } + + if (virNWFilterSetMetadata(nwfilter, type, "", NULL, NULL, flags) < 0) + goto error; + + vshPrintExtra(ctl, "%s removed successfully", title ? "Title" : "Description"); + + } else if (edit || descArg) { + + g_autofree char *descNWFilter = NULL; + g_autofree char *descNew = NULL; + + if (!(descNWFilter = virshGetNWFilterDescription(ctl, nwfilter, + title, flags, queryflags))) + return false; + + if (!descArg) + descArg = g_strdup(descNWFilter); + + if (edit) { + g_autoptr(vshTempFile) tmp = NULL; + g_autofree char *desc_edited = NULL; + char *tmpstr; + + /* Create and open a temporary file. */ + if (!(tmp = vshEditWriteToTempFile(ctl, descArg))) + return false; + + /* Start the editor. */ + if (vshEditFile(ctl, tmp) == -1) + return false; + + /* Read back the edited file. */ + if (!(desc_edited = vshEditReadBackFile(ctl, tmp))) + return false; + + /* strip a possible newline at the end */ + if (title && + (tmpstr = strrchr(desc_edited, '\n')) && + *(tmpstr+1) == '\0') + *tmpstr = '\0'; + + /* Check whether XML has changed */ + if (STREQ(descNWFilter, desc_edited)) { + vshPrintExtra(ctl, "Network filter %s has not changed", title ? "title" : "description"); + return true; + } + + descNew = g_steal_pointer(&desc_edited); + + } else { + descNew = g_steal_pointer(&descArg); + } + + if (virNWFilterSetMetadata(nwfilter, type, descNew, NULL, NULL, flags) < 0) + goto error; + + vshPrintExtra(ctl, "Network filter %s updated successfully", title ? "title" : "description"); + + } else { + g_autofree char *desc = virshGetNWFilterDescription(ctl, nwfilter, title, flags, queryflags); + if (!desc) + return false; + + if (strlen(desc) > 0) { + vshPrint(ctl, "%s", desc); + } else { + vshPrintExtra(ctl, _("No %1$s for network filter: %2$s"), title ? "title" : "description", virNWFilterGetName(nwfilter)); + } + } + + return true; + + error: + vshError(ctl, "Failed to set %s for network filter", title ? "title" : "description"); + return false; +} + + const vshCmdDef nwfilterCmds[] = { {.name = "nwfilter-define", .handler = cmdNWFilterDefine, @@ -823,5 +1026,11 @@ const vshCmdDef nwfilterCmds[] = { .info = info_nwfilter_binding_list, .flags = 0 }, + {.name = "nwfilter-desc", + .handler = cmdNWFilterDesc, + .opts = opts_nwfilter_desc, + .info = info_nwfilter_desc, + .flags = 0 + }, {.name = NULL} }; diff --git a/tools/virsh-util.c b/tools/virsh-util.c index fb6327613a..c3af770c29 100644 --- a/tools/virsh-util.c +++ b/tools/virsh-util.c @@ -423,6 +423,31 @@ virshNetworkGetXMLFromNet(vshControl *ctl, } +int +virshNWFilterGetXMLFromNWFilter(vshControl *ctl, + virNWFilterPtr nwfilter, + unsigned int flags, + xmlDocPtr *xml, + xmlXPathContextPtr *ctxt) +{ + g_autofree char *desc = NULL; + + if (!(desc = virNWFilterGetXMLDesc(nwfilter, flags))) { + vshError(ctl, _("Failed to get nwfilter description xml")); + return -1; + } + + *xml = virXMLParseStringCtxt(desc, _("(nwfilter_definition)"), ctxt); + + if (!(*xml)) { + vshError(ctl, _("Failed to parse nwfilter description xml")); + return -1; + } + + return 0; +} + + int virshDomainGetXML(vshControl *ctl, const vshCmd *cmd, diff --git a/tools/virsh-util.h b/tools/virsh-util.h index 2386847072..4cad3d7eb9 100644 --- a/tools/virsh-util.h +++ b/tools/virsh-util.h @@ -152,6 +152,15 @@ virshNetworkGetXMLFromNet(vshControl *ctl, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(5) G_GNUC_WARN_UNUSED_RESULT; +int +virshNWFilterGetXMLFromNWFilter(vshControl *ctl, + virNWFilterPtr nwfilter, + unsigned int flags, + xmlDocPtr *xml, + xmlXPathContextPtr *ctxt) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4) + ATTRIBUTE_NONNULL(5) G_GNUC_WARN_UNUSED_RESULT; + int virshDomainGetXML(vshControl *ctl, const vshCmd *cmd, -- 2.42.0

With the new command `nwfilter-metadata`, users will be able to view and modify the `<metadata>` field of the Network Filter XML. Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- docs/manpages/virsh.rst | 48 ++++++++++++++ tools/virsh-nwfilter.c | 142 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index 3c7cbf1e11..e15f1832e7 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -8174,6 +8174,54 @@ If neither of *--edit* or *--new-desc* are specified, the title or description is displayed instead of being modified. +nwfilter-metadata +----------------- + + nwfilter-metadata [--nwfilter] nwfilter-name [--uri] uri + [--edit] [[--key] nskey] [--set new-metadata-xml] + [--remove] + +Show or modify custom XML metadata of a network filter. + +The metadata is a user defined XML that allows storing arbitrary XML data +in the network filter definition. +Multiple separate custom metadata pieces can be stored in the +network filter XML. The pieces are identified by a private XML namespace +provided via the *uri* argument. +(See also ``nwfilter-desc`` that works with textual metadata of +a network filter, such as title and description.) + +- *--uri* + + Specifies the URI for the private namespace. + +- *--edit* + + Opens an editor with the metadata identified by the *uri* argument. + Modifications to the contents will be saved back. + Alternatively, the new contents can be provided via the *--set* argument. + +- *--key* + + Specifies the namespace key to be used. + +- *--set* + + Validates and stores the provided metadata string. + + **Note:** When setting metadata via *--edit* or *--set* the *--key* argument + must be specified and is used to prefix the custom elements to bind them + to the private namespace. + +If neither of *--edit* and *--set* are specified the XML metadata corresponding +to the *uri* namespace is displayed instead of being modified. + +- *--remove* + + Specifies that the metadata element specified by the *--uri* argument should + be removed rather than updated. + + NWFILTER BINDING COMMANDS ========================= diff --git a/tools/virsh-nwfilter.c b/tools/virsh-nwfilter.c index 615d126def..801a52746e 100644 --- a/tools/virsh-nwfilter.c +++ b/tools/virsh-nwfilter.c @@ -971,6 +971,142 @@ cmdNWFilterDesc(vshControl *ctl, const vshCmd *cmd) } +/* + * "nwfilter-metadata" command + */ +static const vshCmdInfo info_nwfilter_metadata[] = { + {.name = "help", + .data = N_("show or set network filter's custom XML metadata") + }, + {.name = "desc", + .data = N_("Shows or modifies the XML metadata of a network filter.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_nwfilter_metadata[] = { + {.name = "nwfilter", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("network filter name or uuid"), + .completer = virshNWFilterNameCompleter, + }, + {.name = "uri", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("URI of the namespace") + }, + {.name = "edit", + .type = VSH_OT_BOOL, + .help = N_("use an editor to change the metadata") + }, + {.name = "key", + .type = VSH_OT_STRING, + .help = N_("key to be used as a namespace identifier"), + }, + {.name = "set", + .type = VSH_OT_STRING, + .completer = virshCompleteEmpty, + .help = N_("new metadata to set"), + }, + {.name = "remove", + .type = VSH_OT_BOOL, + .help = N_("remove the metadata corresponding to an uri") + }, + {.name = NULL} +}; + +/* helper to add new metadata using the --edit option */ +static char * +virshNWFilterGetEditMetadata(vshControl *ctl G_GNUC_UNUSED, + virNWFilterPtr nwfilter, + const char *uri, + unsigned int flags) +{ + char *ret; + + if (!(ret = virNWFilterGetMetadata(nwfilter, VIR_NWFILTER_METADATA_ELEMENT, + uri, flags))) { + vshResetLibvirtError(); + ret = g_strdup("\n"); + } + + return ret; +} + +static bool +cmdNWFilterMetadata(vshControl *ctl, const vshCmd *cmd) +{ + g_autoptr(virshNWFilter) nwfilter = NULL; + bool edit = vshCommandOptBool(cmd, "edit"); + bool rem = vshCommandOptBool(cmd, "remove"); + const char *set = NULL; + const char *uri = NULL; + const char *key = NULL; + unsigned int flags = 0; + bool ret = false; + + VSH_EXCLUSIVE_OPTIONS("edit", "set"); + VSH_EXCLUSIVE_OPTIONS("remove", "set"); + VSH_EXCLUSIVE_OPTIONS("remove", "edit"); + + if (!(nwfilter = virshCommandOptNWFilter(ctl, cmd, NULL))) + return false; + + if (vshCommandOptStringReq(ctl, cmd, "uri", &uri) < 0 || + vshCommandOptStringReq(ctl, cmd, "key", &key) < 0 || + vshCommandOptStringReq(ctl, cmd, "set", &set) < 0) + return false; + + if ((set || edit) && !key) { + vshError(ctl, "%s", + _("namespace key is required when modifying metadata")); + return false; + } + + if (set || rem) { + if (virNWFilterSetMetadata(nwfilter, VIR_NWFILTER_METADATA_ELEMENT, + set, key, uri, flags)) + return false; + + if (rem) + vshPrintExtra(ctl, "%s\n", _("Metadata removed")); + else + vshPrintExtra(ctl, "%s\n", _("Metadata modified")); + } else if (edit) { +#define EDIT_GET_XML \ + virshNWFilterGetEditMetadata(ctl, nwfilter, uri, flags) +#define EDIT_NOT_CHANGED \ + do { \ + vshPrintExtra(ctl, "%s", _("Metadata not changed")); \ + ret = true; \ + goto edit_cleanup; \ + } while (0) + +#define EDIT_DEFINE \ + (virNWFilterSetMetadata(nwfilter, VIR_NWFILTER_METADATA_ELEMENT, doc_edited, \ + key, uri, flags) == 0) +#include "virsh-edit.c" + + vshPrintExtra(ctl, "%s\n", _("Metadata modified")); + } else { + g_autofree char *data = NULL; + + /* get */ + if (!(data = virNWFilterGetMetadata(nwfilter, VIR_NWFILTER_METADATA_ELEMENT, + uri, flags))) + return false; + + vshPrint(ctl, "%s\n", data); + } + + ret = true; + + cleanup: + return ret; +} + + const vshCmdDef nwfilterCmds[] = { {.name = "nwfilter-define", .handler = cmdNWFilterDefine, @@ -1032,5 +1168,11 @@ const vshCmdDef nwfilterCmds[] = { .info = info_nwfilter_desc, .flags = 0 }, + {.name = "nwfilter-metadata", + .handler = cmdNWFilterMetadata, + .opts = opts_nwfilter_metadata, + .info = info_nwfilter_metadata, + .flags = 0 + }, {.name = NULL} }; -- 2.42.0

Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- docs/manpages/virsh.rst | 5 ++++- tools/virsh-nwfilter.c | 36 ++++++++++++++++++++++++++++++------ 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index e15f1832e7..5e03ce1e18 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -8083,10 +8083,13 @@ nwfilter-list :: - nwfilter-list + nwfilter-list [--title] List all of the available network filters. +If *--title* is specified, then the short network filter description +(title) is printed in an extra column. + nwfilter-dumpxml ---------------- diff --git a/tools/virsh-nwfilter.c b/tools/virsh-nwfilter.c index 801a52746e..ca803865bf 100644 --- a/tools/virsh-nwfilter.c +++ b/tools/virsh-nwfilter.c @@ -407,6 +407,10 @@ static const vshCmdInfo info_nwfilter_list[] = { }; static const vshCmdOptDef opts_nwfilter_list[] = { + {.name = "title", + .type = VSH_OT_BOOL, + .help = N_("show network filter title") + }, {.name = NULL} }; @@ -416,13 +420,18 @@ cmdNWFilterList(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED) size_t i; char uuid[VIR_UUID_STRING_BUFLEN]; bool ret = false; + bool optTitle = vshCommandOptBool(cmd, "title"); struct virshNWFilterList *list = NULL; g_autoptr(vshTable) table = NULL; if (!(list = virshNWFilterListCollect(ctl, 0))) return false; - table = vshTableNew(_("UUID"), _("Name"), NULL); + if (optTitle) + table = vshTableNew(_("UUID"), _("Name"), _("Title"), NULL); + else + table = vshTableNew(_("UUID"), _("Name"), NULL); + if (!table) goto cleanup; @@ -430,11 +439,26 @@ cmdNWFilterList(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED) virNWFilterPtr nwfilter = list->filters[i]; virNWFilterGetUUIDString(nwfilter, uuid); - if (vshTableRowAppend(table, - uuid, - virNWFilterGetName(nwfilter), - NULL) < 0) - goto cleanup; + + if (optTitle) { + g_autofree char *title = NULL; + if (!(title = virshGetNWFilterDescription(ctl, nwfilter, true, 0, 0))) + goto cleanup; + + if (vshTableRowAppend(table, + uuid, + virNWFilterGetName(nwfilter), + title, + NULL) < 0) + goto cleanup; + + } else { + if (vshTableRowAppend(table, + uuid, + virNWFilterGetName(nwfilter), + NULL) < 0) + goto cleanup; + } } vshTablePrintToStdout(table, ctl); -- 2.42.0

Adds two new private methods for nwfilter metadata: - virNWFilterObjGetMetadata() - virNWFilterObjSetMetadata() Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- src/conf/virnwfilterobj.c | 148 ++++++++++++++++++++++++++++++++++++++ src/conf/virnwfilterobj.h | 13 ++++ src/libvirt_private.syms | 2 + 3 files changed, 163 insertions(+) diff --git a/src/conf/virnwfilterobj.c b/src/conf/virnwfilterobj.c index 6456add593..437df9631b 100644 --- a/src/conf/virnwfilterobj.c +++ b/src/conf/virnwfilterobj.c @@ -635,3 +635,151 @@ virNWFilterObjUnlock(virNWFilterObj *obj) { virMutexUnlock(&obj->lock); } + + +char * +virNWFilterObjGetMetadata(virNWFilterObj *obj, + int type, + const char *uri) +{ + virNWFilterDef *def; + char *ret = NULL; + + if (type >= VIR_NWFILTER_METADATA_LAST) { + virReportError(VIR_ERR_INVALID_ARG, + _("unknown metadata type '%1$d'"), type); + return NULL; + } + + if (!(def = virNWFilterObjGetDef(obj))) + return NULL; + + switch ((virNWFilterMetadataType) type) { + case VIR_NWFILTER_METADATA_DESCRIPTION: + ret = g_strdup(def->description); + break; + + case VIR_NWFILTER_METADATA_TITLE: + ret = g_strdup(def->title); + break; + + case VIR_NWFILTER_METADATA_ELEMENT: + if (!def->metadata) + break; + + if (virXMLExtractNamespaceXML(def->metadata, uri, &ret) < 0) + return NULL; + break; + + case VIR_NWFILTER_METADATA_LAST: + break; + } + + if (!ret) + virReportError(VIR_ERR_NO_NWFILTER_METADATA, "%s", + _("Requested metadata element is not present")); + + return ret; +} + + +static int +virNWFilterDefSetMetadata(virNWFilterDef *def, + int type, + const char *metadata, + const char *key, + const char *uri) +{ + g_autoptr(xmlDoc) doc = NULL; + xmlNodePtr old; + g_autoptr(xmlNode) new = NULL; + + if (type >= VIR_NWFILTER_METADATA_LAST) { + virReportError(VIR_ERR_INVALID_ARG, + _("unknown metadata type '%1$d'"), type); + return -1; + } + + switch ((virNWFilterMetadataType) type) { + case VIR_NWFILTER_METADATA_DESCRIPTION: + g_clear_pointer(&def->description, g_free); + + if (STRNEQ_NULLABLE(metadata, "")) + def->description = g_strdup(metadata); + break; + + case VIR_NWFILTER_METADATA_TITLE: + g_clear_pointer(&def->title, g_free); + + if (STRNEQ_NULLABLE(metadata, "")) + def->title = g_strdup(metadata); + break; + + case VIR_NWFILTER_METADATA_ELEMENT: + if (metadata) { + + /* parse and modify the xml from the user */ + if (!(doc = virXMLParseStringCtxt(metadata, _("(metadata_xml)"), NULL))) + return -1; + + if (virXMLInjectNamespace(doc->children, uri, key) < 0) + return -1; + + /* create the root node if needed */ + if (!def->metadata) + def->metadata = virXMLNewNode(NULL, "metadata"); + + if (!(new = xmlCopyNode(doc->children, 1))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to copy XML node")); + return -1; + } + } + + /* remove possible other nodes sharing the namespace */ + while ((old = virXMLFindChildNodeByNs(def->metadata, uri))) { + xmlUnlinkNode(old); + xmlFreeNode(old); + } + + if (new) { + if (!(xmlAddChild(def->metadata, new))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to add metadata to XML document")); + return -1; + } + new = NULL; + } + break; + + case VIR_NWFILTER_METADATA_LAST: + break; + } + + return 0; +} + + +int +virNWFilterObjSetMetadata(virNWFilterObj *obj, + int type, + const char *metadata, + const char *key, + const char *uri, + const char *configDir) +{ + virNWFilterDef *def; + + if (!(def = virNWFilterObjGetDef(obj))) + return -1; + + if (def) { + if (virNWFilterDefSetMetadata(def, type, metadata, key, uri) < 0) + return -1; + + if (virNWFilterSaveConfig(configDir, def) < 0) + return -1; + } + + return 0; +} diff --git a/src/conf/virnwfilterobj.h b/src/conf/virnwfilterobj.h index b67dc017c5..4079c0ab3d 100644 --- a/src/conf/virnwfilterobj.h +++ b/src/conf/virnwfilterobj.h @@ -117,3 +117,16 @@ virNWFilterObjLock(virNWFilterObj *obj); void virNWFilterObjUnlock(virNWFilterObj *obj); + +char * +virNWFilterObjGetMetadata(virNWFilterObj *obj, + int type, + const char *uri); + +int +virNWFilterObjSetMetadata(virNWFilterObj *obj, + int type, + const char *metadata, + const char *key, + const char *uri, + const char *configDir); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4e475d5b1a..7774ee03c4 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1410,6 +1410,7 @@ virNWFilterBindingObjListRemove; # conf/virnwfilterobj.h virNWFilterObjGetDef; +virNWFilterObjGetMetadata; virNWFilterObjGetNewDef; virNWFilterObjListAssignDef; virNWFilterObjListExport; @@ -1423,6 +1424,7 @@ virNWFilterObjListNew; virNWFilterObjListNumOfNWFilters; virNWFilterObjListRemove; virNWFilterObjLock; +virNWFilterObjSetMetadata; virNWFilterObjTestUnassignDef; virNWFilterObjUnlock; virNWFilterObjWantRemoved; -- 2.42.0

Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- src/nwfilter/nwfilter_driver.c | 61 ++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/nwfilter/nwfilter_driver.c b/src/nwfilter/nwfilter_driver.c index 09719edd75..00b2d89eb4 100644 --- a/src/nwfilter/nwfilter_driver.c +++ b/src/nwfilter/nwfilter_driver.c @@ -812,6 +812,65 @@ nwfilterBindingDelete(virNWFilterBindingPtr binding) } +static int +nwfilterSetMetadata(virNWFilterPtr nwfilter, + int type, + const char *metadata, + const char *key, + const char *uri, + unsigned int flags) +{ + virNWFilterObj *obj = NULL; + virNWFilterDef *def = NULL; + int ret = -1; + + virCheckFlags(0, -1); + + if (!(obj = nwfilterObjFromNWFilter(nwfilter->uuid))) + return -1; + + def = virNWFilterObjGetDef(obj); + + if (virNWFilterSetMetadataEnsureACL(nwfilter->conn, def) < 0) + goto cleanup; + + ret = virNWFilterObjSetMetadata(obj, type, metadata, key, uri, + driver->configDir); + + cleanup: + virNWFilterObjUnlock(obj); + return ret; +} + + +static char * +nwfilterGetMetadata(virNWFilterPtr nwfilter, + int type, + const char *uri, + unsigned int flags) +{ + virNWFilterObj *obj = NULL; + virNWFilterDef *def = NULL; + char *ret = NULL; + + virCheckFlags(0, NULL); + + if (!(obj = nwfilterObjFromNWFilter(nwfilter->uuid))) + return NULL; + + def = virNWFilterObjGetDef(obj); + + if (virNWFilterGetMetadataEnsureACL(nwfilter->conn, def) < 0) + goto cleanup; + + ret = virNWFilterObjGetMetadata(obj, type, uri); + + cleanup: + virNWFilterObjUnlock(obj); + return ret; +} + + static virNWFilterDriver nwfilterDriver = { .name = "nwfilter", .connectNumOfNWFilters = nwfilterConnectNumOfNWFilters, /* 0.8.0 */ @@ -828,6 +887,8 @@ static virNWFilterDriver nwfilterDriver = { .nwfilterBindingGetXMLDesc = nwfilterBindingGetXMLDesc, /* 4.5.0 */ .nwfilterBindingCreateXML = nwfilterBindingCreateXML, /* 4.5.0 */ .nwfilterBindingDelete = nwfilterBindingDelete, /* 4.5.0 */ + .nwfilterGetMetadata = nwfilterGetMetadata, /* 9.9.0 */ + .nwfilterSetMetadata = nwfilterSetMetadata, /* 9.9.0 */ }; -- 2.42.0

Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- src/conf/nwfilter_conf.c | 5 +- src/conf/nwfilter_conf.h | 3 + src/test/test_driver.c | 240 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 246 insertions(+), 2 deletions(-) diff --git a/src/conf/nwfilter_conf.c b/src/conf/nwfilter_conf.c index d03f78af4d..344bddcd68 100644 --- a/src/conf/nwfilter_conf.c +++ b/src/conf/nwfilter_conf.c @@ -2509,7 +2509,7 @@ virNWFilterIsAllowedChain(const char *chainname) } -static virNWFilterDef * +virNWFilterDef * virNWFilterDefParseXML(xmlXPathContextPtr ctxt) { g_autoptr(virNWFilterDef) ret = NULL; @@ -2660,6 +2660,9 @@ virNWFilterSaveConfig(const char *configDir, char uuidstr[VIR_UUID_STRING_BUFLEN]; g_autofree char *configFile = NULL; + if (!configDir) + return 0; + if (!(xml = virNWFilterDefFormat(def))) return -1; diff --git a/src/conf/nwfilter_conf.h b/src/conf/nwfilter_conf.h index 34de6eab3d..6e13eae262 100644 --- a/src/conf/nwfilter_conf.h +++ b/src/conf/nwfilter_conf.h @@ -552,6 +552,9 @@ virNWFilterDefParse(const char *xmlStr, const char *filename, unsigned int flags); +virNWFilterDef * +virNWFilterDefParseXML(xmlXPathContextPtr ctxt); + typedef int (*virNWFilterTriggerRebuildCallback)(void *opaque); int diff --git a/src/test/test_driver.c b/src/test/test_driver.c index e87d7cfd44..ffcaa07b3e 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -63,6 +63,8 @@ #include "virhostcpu.h" #include "virdomaincheckpointobjlist.h" #include "virdomainsnapshotobjlist.h" +#include "nwfilter_conf.h" +#include "virnwfilterobj.h" #include "virkeycode.h" #include "virutil.h" @@ -114,6 +116,7 @@ struct _testDriver { /* immutable pointer, self-locking APIs */ virDomainObjList *domains; virNetworkObjList *networks; + virNWFilterObjList *nwfilters; virObjectEventState *eventState; }; typedef struct _testDriver testDriver; @@ -158,6 +161,7 @@ testDriverDispose(void *obj) virObjectUnref(driver->domains); virNodeDeviceObjListFree(driver->devs); virObjectUnref(driver->networks); + virObjectUnref(driver->nwfilters); virObjectUnref(driver->ifaces); virObjectUnref(driver->pools); virObjectUnref(driver->eventState); @@ -465,6 +469,7 @@ testDriverNew(void) !(ret->ifaces = virInterfaceObjListNew()) || !(ret->domains = virDomainObjListNew()) || !(ret->networks = virNetworkObjListNew()) || + !(ret->nwfilters = virNWFilterObjListNew()) || !(ret->devs = virNodeDeviceObjListNew()) || !(ret->pools = virStoragePoolObjListNew())) goto error; @@ -519,6 +524,10 @@ static const char *defaultConnXML = " </ip>" "</network>" "" +"<filter name='test-filter' chain='ipv4' priority='1'>" +" <uuid>aafaaaaf-7aca-7007-bbca-dda97dd1adfc</uuid>" +"</filter>" +"" "<interface type=\"ethernet\" name=\"eth1\">" " <start mode=\"onboot\"/>" " <mac address=\"aa:bb:cc:dd:ee:ff\"/>" @@ -1113,6 +1122,40 @@ testParseNetworks(testDriver *privconn, } +static int +testParseNWFilters(testDriver *privconn, + const char *file, + xmlXPathContextPtr ctxt) +{ + VIR_XPATH_NODE_AUTORESTORE(ctxt) + int num; + size_t i; + virNWFilterObj *obj; + g_autofree xmlNodePtr *nodes = NULL; + + num = virXPathNodeSet("/node/filter", ctxt, &nodes); + if (num < 0) + return -1; + + for (i = 0; i < num; i++) { + g_autoptr(virNWFilterDef) def = NULL; + + if (!(ctxt->node = testParseXMLDocFromFile(nodes[i], file))) + return -1; + + if (!(def = virNWFilterDefParseXML(ctxt))) + return -1; + + if (!(obj = virNWFilterObjListAssignDef(privconn->nwfilters, def))) + return -1; + def = NULL; + + virNWFilterObjUnlock(obj); + } + return 0; +} + + static int testParseInterfaces(testDriver *privconn, const char *file, @@ -1320,6 +1363,8 @@ testOpenParse(testDriver *privconn, return -1; if (testParseNetworks(privconn, file, ctxt) < 0) return -1; + if (testParseNWFilters(privconn, file, ctxt) < 0) + return -1; if (testParseInterfaces(privconn, file, ctxt) < 0) return -1; if (testParseStorage(privconn, file, ctxt) < 0) @@ -10036,6 +10081,187 @@ testConnectGetDomainCapabilities(virConnectPtr conn G_GNUC_UNUSED, } +static virNWFilterObj * +testNWFilterObjFromNWFilter(testDriver *driver, + const unsigned char *uuid) +{ + virNWFilterObj *obj; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + if (!(obj = virNWFilterObjListFindByUUID(driver->nwfilters, uuid))) { + virUUIDFormat(uuid, uuidstr); + virReportError(VIR_ERR_NO_NWFILTER, + _("no nwfilter with matching uuid '%1$s'"), uuidstr); + } + return obj; +} + +static virNWFilterPtr +testNWFilterLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) +{ + testDriver *driver = conn->privateData; + virNWFilterObj *obj = NULL; + virNWFilterDef *def; + virNWFilterPtr nwfilter = NULL; + + if (!(obj = testNWFilterObjFromNWFilter(driver, uuid))) + return NULL; + + def = virNWFilterObjGetDef(obj); + nwfilter = virGetNWFilter(conn, def->name, def->uuid); + + virNWFilterObjUnlock(obj); + return nwfilter; +} + + +static virNWFilterPtr +testNWFilterLookupByName(virConnectPtr conn, + const char *name) +{ + testDriver *driver = conn->privateData; + virNWFilterObj *obj = NULL; + virNWFilterDef *def; + virNWFilterPtr nwfilter = NULL; + + if (!(obj = virNWFilterObjListFindByName(driver->nwfilters, name))) { + virReportError(VIR_ERR_NO_NWFILTER, + _("no nwfilter with matching name '%1$s'"), + name); + return NULL; + } + def = virNWFilterObjGetDef(obj); + nwfilter = virGetNWFilter(conn, def->name, def->uuid); + + virNWFilterObjUnlock(obj); + return nwfilter; +} + +static int +testNWFilterConnectNumOfNWFilters(virConnectPtr conn) +{ + testDriver *driver = conn->privateData; + return virNWFilterObjListNumOfNWFilters(driver->nwfilters, + conn, NULL); +} + + +static int +testNWFilterConnectListNWFilters(virConnectPtr conn, + char **const names, + int maxnames) +{ + testDriver *driver = conn->privateData; + return virNWFilterObjListGetNames(driver->nwfilters, conn, + NULL, names, maxnames); +} + + +static int +testNWFilterConnectListAllNWFilters(virConnectPtr conn, + virNWFilterPtr **nwfilters, + unsigned int flags) +{ + testDriver *driver = conn->privateData; + virCheckFlags(0, -1); + return virNWFilterObjListExport(conn, driver->nwfilters, + nwfilters, NULL); +} + + +static virNWFilterPtr +testNWFilterDefineXMLFlags(virConnectPtr conn, + const char *xml, + unsigned int flags) +{ + testDriver *driver = conn->privateData; + virNWFilterDef *def; + virNWFilterObj *obj = NULL; + virNWFilterDef *objdef; + virNWFilterPtr nwfilter = NULL; + + virCheckFlags(VIR_NWFILTER_DEFINE_VALIDATE, NULL); + + if (!(def = virNWFilterDefParse(xml, NULL, flags))) + goto cleanup; + + + if (!(obj = virNWFilterObjListAssignDef(driver->nwfilters, def))) + goto cleanup; + + def = NULL; + objdef = virNWFilterObjGetDef(obj); + + nwfilter = virGetNWFilter(conn, objdef->name, objdef->uuid); + + cleanup: + virNWFilterDefFree(def); + if (obj) + virNWFilterObjUnlock(obj); + return nwfilter; +} + + +static virNWFilterPtr +testNWFilterDefineXML(virConnectPtr conn, + const char *xml) +{ + return testNWFilterDefineXMLFlags(conn, xml, 0); +} + + +static int +testNWFilterUndefine(virNWFilterPtr nwfilter) +{ + testDriver *driver = nwfilter->conn->privateData; + virNWFilterObj *obj; + int ret = -1; + + if (!(obj = testNWFilterObjFromNWFilter(driver, nwfilter->uuid))) + goto cleanup; + + if (virNWFilterObjTestUnassignDef(obj) < 0) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", + _("nwfilter is in use")); + goto cleanup; + } + + virNWFilterObjListRemove(driver->nwfilters, obj); + obj = NULL; + ret = 0; + + cleanup: + if (obj) + virNWFilterObjUnlock(obj); + + return ret; +} + + +static char * +testNWFilterGetXMLDesc(virNWFilterPtr nwfilter, + unsigned int flags) +{ + testDriver *driver = nwfilter->conn->privateData; + virNWFilterObj *obj = NULL; + virNWFilterDef *def = NULL; + char *ret = NULL; + + virCheckFlags(0, NULL); + + if (!(obj = testNWFilterObjFromNWFilter(driver, nwfilter->uuid))) + return NULL; + + def = virNWFilterObjGetDef(obj); + + ret = virNWFilterDefFormat(def); + + virNWFilterObjUnlock(obj); + return ret; +} + /* * Test driver */ @@ -10310,6 +10536,18 @@ static virNodeDeviceDriver testNodeDeviceDriver = { .nodeDeviceDestroy = testNodeDeviceDestroy, /* 0.7.3 */ }; +static virNWFilterDriver testNWFilterDriver = { + .connectNumOfNWFilters = testNWFilterConnectNumOfNWFilters, /* 9.9.0 */ + .connectListNWFilters = testNWFilterConnectListNWFilters, /* 9.9.0 */ + .connectListAllNWFilters = testNWFilterConnectListAllNWFilters, /* 9.9.0 */ + .nwfilterLookupByName = testNWFilterLookupByName, /* 9.9.0 */ + .nwfilterLookupByUUID = testNWFilterLookupByUUID, /* 9.9.0 */ + .nwfilterDefineXML = testNWFilterDefineXML, /* 9.9.0 */ + .nwfilterDefineXMLFlags = testNWFilterDefineXMLFlags, /* 9.9.0 */ + .nwfilterUndefine = testNWFilterUndefine, /* 9.9.0 */ + .nwfilterGetXMLDesc = testNWFilterGetXMLDesc, /* 9.9.0 */ +}; + static virConnectDriver testConnectDriver = { .localOnly = true, .uriSchemes = (const char *[]){ "test", NULL }, @@ -10317,7 +10555,7 @@ static virConnectDriver testConnectDriver = { .interfaceDriver = &testInterfaceDriver, .networkDriver = &testNetworkDriver, .nodeDeviceDriver = &testNodeDeviceDriver, - .nwfilterDriver = NULL, + .nwfilterDriver = &testNWFilterDriver, .secretDriver = NULL, .storageDriver = &testStorageDriver, }; -- 2.42.0

Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- src/test/test_driver.c | 49 +++++- tests/meson.build | 1 + tests/nwfiltermetadatatest.c | 297 +++++++++++++++++++++++++++++++++++ 3 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 tests/nwfiltermetadatatest.c diff --git a/src/test/test_driver.c b/src/test/test_driver.c index ffcaa07b3e..14baa82e52 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -10080,7 +10080,6 @@ testConnectGetDomainCapabilities(virConnectPtr conn G_GNUC_UNUSED, return virDomainCapsFormat(domCaps); } - static virNWFilterObj * testNWFilterObjFromNWFilter(testDriver *driver, const unsigned char *uuid) @@ -10262,6 +10261,52 @@ testNWFilterGetXMLDesc(virNWFilterPtr nwfilter, return ret; } +static int +testNWFilterSetMetadata(virNWFilterPtr nwfilter, + int type, + const char *metadata, + const char *key, + const char *uri, + unsigned int flags) +{ + testDriver *driver = nwfilter->conn->privateData; + virNWFilterObj *obj = NULL; + int ret = -1; + + virCheckFlags(0, -1); + + if (!(obj = testNWFilterObjFromNWFilter(driver, nwfilter->uuid))) + return -1; + + ret = virNWFilterObjSetMetadata(obj, type, metadata, + key, uri, NULL); + + virNWFilterObjUnlock(obj); + return ret; +} + +static char * +testNWFilterGetMetadata(virNWFilterPtr nwfilter, + int type, + const char *uri, + unsigned int flags) +{ + testDriver *driver = nwfilter->conn->privateData; + virNWFilterObj *obj = NULL; + char *ret = NULL; + + virCheckFlags(0, NULL); + + if (!(obj = testNWFilterObjFromNWFilter(driver, nwfilter->uuid))) + return NULL; + + ret = virNWFilterObjGetMetadata(obj, type, uri); + + virNWFilterObjUnlock(obj); + return ret; +} + + /* * Test driver */ @@ -10546,6 +10591,8 @@ static virNWFilterDriver testNWFilterDriver = { .nwfilterDefineXMLFlags = testNWFilterDefineXMLFlags, /* 9.9.0 */ .nwfilterUndefine = testNWFilterUndefine, /* 9.9.0 */ .nwfilterGetXMLDesc = testNWFilterGetXMLDesc, /* 9.9.0 */ + .nwfilterGetMetadata = testNWFilterGetMetadata, /* 9.9.0 */ + .nwfilterSetMetadata = testNWFilterSetMetadata, /* 9.9.0 */ }; static virConnectDriver testConnectDriver = { diff --git a/tests/meson.build b/tests/meson.build index b235c5f4dd..d3055a9988 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -259,6 +259,7 @@ tests += [ { 'name': 'interfacexml2xmltest' }, { 'name': 'metadatatest' }, { 'name': 'networkmetadatatest' }, + { 'name': 'nwfiltermetadatatest' }, { 'name': 'networkxml2xmlupdatetest' }, { 'name': 'nodedevxml2xmltest' }, { 'name': 'nwfilterxml2xmltest' }, diff --git a/tests/nwfiltermetadatatest.c b/tests/nwfiltermetadatatest.c new file mode 100644 index 0000000000..6f2754aa25 --- /dev/null +++ b/tests/nwfiltermetadatatest.c @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2013 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include "testutils.h" + +#include "virerror.h" +#include "virxml.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +static const char metadata1[] = +"<derp xmlns:foobar='http://foo.bar/'>\n" +" <bar>foobar</bar>\n" +" <foo fooish='blurb'>foofoo</foo>\n" +" <foobar:baz>zomg</foobar:baz>\n" +"</derp>"; + + +static const char metadata1_ns[] = +"<herp:derp xmlns:foobar='http://foo.bar/' xmlns:herp='http://herp.derp/'>\n" +" <herp:bar>foobar</herp:bar>\n" +" <herp:foo fooish='blurb'>foofoo</herp:foo>\n" +" <foobar:baz>zomg</foobar:baz>\n" +"</herp:derp>"; + + +static const char metadata2[] = +"<foo>\n" +" <bar>baz</bar>\n" +"</foo>"; + + +static const char metadata2_ns[] = +"<blurb:foo xmlns:blurb='http://herp.derp/'>\n" +" <blurb:bar>baz</blurb:bar>\n" +"</blurb:foo>"; + + +static char * +getMetadataFromXML(virNWFilterPtr nwfilter) +{ + g_autoptr(xmlDoc) doc = NULL; + g_autoptr(xmlXPathContext) ctxt = NULL; + xmlNodePtr node; + + g_autofree char *xml = NULL; + + if (!(xml = virNWFilterGetXMLDesc(nwfilter, 0))) + return NULL; + + if (!(doc = virXMLParseStringCtxt(xml, "(nwfilter_definition)", &ctxt))) + return NULL; + + if (!(node = virXPathNode("//metadata/*", ctxt))) + return NULL; + + return virXMLNodeToString(node->doc, node); +} + + +static void +metadataXMLConvertApostrophe(char *str) +{ + do { + if (*str == '\"') + *str = '\''; + } while ((*++str) != '\0'); +} + + +static bool +verifyMetadata(virNWFilterPtr nwfilter, + const char *expectXML, + const char *expectAPI, + const char *uri) +{ + g_autofree char *metadataXML = NULL; + g_autofree char *metadataAPI = NULL; + + if (!expectAPI) { + if ((metadataAPI = virNWFilterGetMetadata(nwfilter, + VIR_NWFILTER_METADATA_ELEMENT, + uri, 0))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "expected no metadata in API, but got:\n[%s]", + metadataAPI); + return false; + } + } else { + if (!(metadataAPI = virNWFilterGetMetadata(nwfilter, + VIR_NWFILTER_METADATA_ELEMENT, + uri, 0))) + return false; + + metadataXMLConvertApostrophe(metadataAPI); + + if (STRNEQ(metadataAPI, expectAPI)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "XML metadata in API doesn't match expected metadata: " + "expected:\n[%s]\ngot:\n[%s]", + expectAPI, metadataAPI); + return false; + } + + } + + if (!expectXML) { + if ((metadataXML = getMetadataFromXML(nwfilter))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "expected no metadata in XML, but got:\n[%s]", + metadataXML); + return false; + } + } else { + if (!(metadataXML = getMetadataFromXML(nwfilter))) + return false; + + metadataXMLConvertApostrophe(metadataXML); + + if (STRNEQ(metadataXML, expectXML)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "XML in dump doesn't match expected metadata: " + "expected:\n[%s]\ngot:\n[%s]", + expectXML, metadataXML); + return false; + } + } + + return true; +} + + +struct metadataTest { + virConnectPtr conn; + virNWFilterPtr nwfilter; + + const char *data; + const char *expect; + int type; + bool fail; +}; + + +static int +testAssignMetadata(const void *data) +{ + const struct metadataTest *test = data; + + if (virNWFilterSetMetadata(test->nwfilter, VIR_NWFILTER_METADATA_ELEMENT, + metadata1, "herp", "http://herp.derp/", 0) < 0) + return -1; + + if (!verifyMetadata(test->nwfilter, metadata1_ns, metadata1, "http://herp.derp/")) + return -1; + + return 0; +} + +static int +testRewriteMetadata(const void *data) +{ + const struct metadataTest *test = data; + + if (virNWFilterSetMetadata(test->nwfilter, VIR_NWFILTER_METADATA_ELEMENT, + metadata2, "blurb", "http://herp.derp/", 0) < 0) + return -1; + + if (!verifyMetadata(test->nwfilter, metadata2_ns, metadata2, "http://herp.derp/")) + return -1; + + return 0; +} + +static int +testEraseMetadata(const void *data) +{ + const struct metadataTest *test = data; + + if (virNWFilterSetMetadata(test->nwfilter, VIR_NWFILTER_METADATA_ELEMENT, + NULL, NULL, "http://herp.derp/", 0) < 0) + return -1; + + if (!verifyMetadata(test->nwfilter, NULL, NULL, "http://herp.derp/")) + return -1; + + return 0; +} + +static int +testTextMetadata(const void *data) +{ + const struct metadataTest *test = data; + g_autofree char *actual = NULL; + + if (virNWFilterSetMetadata(test->nwfilter, test->type, test->data, NULL, NULL, 0) < 0) { + if (test->fail) + return 0; + return -1; + } + + actual = virNWFilterGetMetadata(test->nwfilter, test->type, NULL, 0); + + if (STRNEQ_NULLABLE(test->expect, actual)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "expected metadata doesn't match actual: " + "expected:'%s'\ngot: '%s'", + NULLSTR(test->data), NULLSTR(actual)); + return -1; + } + + return 0; +} + +#define TEST_TEXT_METADATA(INDEX, TYPE, DATA, EXPECT, FAIL) \ + do { \ + test.type = VIR_NWFILTER_METADATA_ ## TYPE; \ + test.data = DATA; \ + test.expect = EXPECT; \ + test.fail = FAIL; \ + \ + if (virTestRun("text metadata: " #TYPE " " INDEX " ", \ + testTextMetadata, &test) < 0) \ + ret = EXIT_FAILURE; \ + } while (0) + +#define TEST_TITLE(INDEX, DATA) \ + TEST_TEXT_METADATA(INDEX, TITLE, DATA, DATA, false) +#define TEST_TITLE_EXPECT(INDEX, DATA, EXPECT) \ + TEST_TEXT_METADATA(INDEX, TITLE, DATA, EXPECT, false) +#define TEST_TITLE_FAIL(INDEX, DATA) \ + TEST_TEXT_METADATA(INDEX, TITLE, DATA, DATA, true) +#define TEST_DESCR(INDEX, DATA) \ + TEST_TEXT_METADATA(INDEX, DESCRIPTION, DATA, DATA, false) +#define TEST_DESCR_EXPECT(INDEX, DATA, EXPECT) \ + TEST_TEXT_METADATA(INDEX, DESCRIPTION, DATA, EXPECT, false) + +static int +mymain(void) +{ + struct metadataTest test = { 0 }; + int ret = EXIT_SUCCESS; + + if (!(test.conn = virConnectOpen("test:///default"))) + return EXIT_FAILURE; + + if (!(test.nwfilter = virNWFilterLookupByName(test.conn, "test-filter"))) { + virConnectClose(test.conn); + return EXIT_FAILURE; + } + + virTestQuiesceLibvirtErrors(false); + + if (virTestRun("Assign metadata ", testAssignMetadata, &test) < 0) + ret = EXIT_FAILURE; + if (virTestRun("Rewrite Metadata ", testRewriteMetadata, &test) < 0) + ret = EXIT_FAILURE; + if (virTestRun("Erase metadata ", testEraseMetadata, &test) < 0) + ret = EXIT_FAILURE; + + TEST_TITLE("1", "qwert"); + TEST_TITLE("2", NULL); + TEST_TITLE("3", "blah"); + TEST_TITLE_FAIL("4", "qwe\nrt"); + TEST_TITLE_EXPECT("5", "", NULL); + TEST_TITLE_FAIL("6", "qwert\n"); + TEST_TITLE_FAIL("7", "\n"); + + TEST_DESCR("1", "qwert\nqwert"); + TEST_DESCR("2", NULL); + TEST_DESCR("3", "qwert"); + TEST_DESCR("4", "\n"); + TEST_DESCR_EXPECT("5", "", NULL); + + virNWFilterFree(test.nwfilter); + virConnectClose(test.conn); + + return ret; +} + +VIR_TEST_MAIN(mymain) -- 2.42.0

Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- NEWS.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index d04090d43d..94e8c3f575 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -17,6 +17,24 @@ v9.9.0 (unreleased) * **New features** + * nwfilter: Support for user-defined metadata + + The network filter object adds three user-defined metadata fields: + 1) ``<title>``: Stores a short description (title) and can't contain newlines. + 2) ``<description>``: Can store any documentation the user wants. + 3) ``<metadata>``: Can store custom metadata in form of XML nodes. + + Two new APIs have been added to manage the above fields: + 1) ``virNWFilterGetMetadata()`` + 2) ``virNWFilterSetMetadata()`` + + virsh adds two new commands: + 1) ``nwfilter-desc``: To view or modify the title or description. + 2) ``nwfilter-metadata``: To view or modify ``<metadata>``. + + ``nwfilter-list`` adds a new option ``--title`` that prints the content of ``<title>`` + in an extra column within the default ``--table`` output. + * **Improvements** * **Bug fixes** -- 2.42.0
participants (1)
-
K Shiva Kiran