[libvirt PATCH v2 0/7] Introduce new Metadata fields for Network object with corresponding APIs

This commit adds the following: - Introduction of <title> and <description> fields to the Network Object. - Introduction of Get and Set Public APIs for the aforementioned fields. - virsh exposure of the aforementioned Public APIs. - Adds implementation in test driver along with a testcase. - Implementation in bridge driver. This is a v2 of: https://listman.redhat.com/archives/libvir-list/2023-July/240828.html Diff to v1: - Corrected placement of structs in remote_protocol-structs. - Removed redundant call to virNetworkObjSetDefTransient() in virNetworkConfigChangeSetup(). - Removed redundant logic in networkUpdate(), substituted by call to newly introduced virNetworkObjUpdateModificationImpact(). - Added virsh exposure of the APIs. - Added bridge driver implementation. Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> K Shiva Kiran (7): Add <title> and <description> for Network Objects Adding Public Get and Set APIs for Network Metadata Implementing Remote Protocol for Network Metadata virsh exposure of Network Metadata APIs Add virNetworkObj Get and Set Methods for Metadata Add Test driver and testcase for Network Metadata change APIs Added bridge driver implementation docs/formatnetwork.rst | 11 + docs/manpages/virsh.rst | 77 ++++++ include/libvirt/libvirt-network.h | 29 +++ include/libvirt/virterror.h | 1 + src/conf/network_conf.c | 21 ++ src/conf/network_conf.h | 2 + src/conf/schemas/basictypes.rng | 15 ++ src/conf/schemas/domaincommon.rng | 15 -- src/conf/schemas/network.rng | 10 + src/conf/virnetworkobj.c | 329 +++++++++++++++++++++++- src/conf/virnetworkobj.h | 21 ++ src/driver-network.h | 16 ++ src/libvirt-network.c | 167 ++++++++++++ src/libvirt_private.syms | 3 + src/libvirt_public.syms | 6 + src/network/bridge_driver.c | 78 +++++- src/remote/remote_driver.c | 2 + src/remote/remote_protocol.x | 36 ++- src/remote_protocol-structs | 19 ++ src/test/test_driver.c | 83 +++++- src/util/virerror.c | 3 + tests/meson.build | 1 + tests/networkmetadatatest.c | 297 +++++++++++++++++++++ tools/virsh-network.c | 411 +++++++++++++++++++++++++++++- tools/virsh-util.c | 25 ++ tools/virsh-util.h | 9 + 26 files changed, 1624 insertions(+), 63 deletions(-) create mode 100644 tests/networkmetadatatest.c -- 2.41.0

This patch adds new elements <title> and <description> to the Network XML. - The <title> attribute holds a short title defined by the user and cannot contain newlines. - The <description> attribute holds any documentation that the user wants to store. - Schema definitions of <title> and <description> have been moved from domaincommon.rng to basictypes.rng for use by network and future objects. - Added Network XML parser logic for the above. Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- docs/formatnetwork.rst | 11 +++++++++++ src/conf/network_conf.c | 18 ++++++++++++++++++ src/conf/network_conf.h | 2 ++ src/conf/schemas/basictypes.rng | 15 +++++++++++++++ src/conf/schemas/domaincommon.rng | 15 --------------- src/conf/schemas/network.rng | 10 ++++++++++ 6 files changed, 56 insertions(+), 15 deletions(-) diff --git a/docs/formatnetwork.rst b/docs/formatnetwork.rst index b5dc29db07..c54d53070a 100644 --- a/docs/formatnetwork.rst +++ b/docs/formatnetwork.rst @@ -30,6 +30,8 @@ The first elements provide basic metadata about the virtual network. <network ipv6='yes' trustGuestRxFilters='no'> <name>default</name> <uuid>3e3fce45-4f53-4fa7-bb32-11f34168b82b</uuid> + <title>A short description - title - of the network</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> @@ -47,6 +49,7 @@ The first elements provide basic metadata about the virtual network. the virtual network. The format must be RFC 4122 compliant, eg ``3e3fce45-4f53-4fa7-bb32-11f34168b82b``. If omitted when defining/creating a new network, a random UUID is generated. :since:`Since 0.3.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 @@ -65,6 +68,14 @@ The first elements provide basic metadata about the virtual network. documentation for more details. Note that an explicit setting of this attribute in a portgroup or the individual domain interface will override the setting in the network. +``title`` + The optional element ``title`` provides space for a short description of the + network. The title should not contain any newlines. :since:`Since 9.7.0` . +``description`` + The content of the ``description`` element provides a human readable + description of the network. This data is not used by libvirt in any + way, it can contain any information the user wants. :since:`Since 9.7.0` + Connectivity ~~~~~~~~~~~~ diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 3e137cb562..262e9a9fc7 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -281,6 +281,8 @@ virNetworkDefFree(virNetworkDef *def) virNetDevBandwidthFree(def->bandwidth); virNetDevVlanClear(&def->vlan); + g_free(def->title); + g_free(def->description); xmlFreeNode(def->metadata); if (def->namespaceData && def->ns.free) @@ -1599,6 +1601,17 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt, def->uuid_specified = true; } + /* Extract short description of network (title) */ + def->title = virXPathString("string(./title[1])", ctxt); + if (def->title && strchr(def->title, '\n')) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Network title can't contain newlines")); + return NULL; + } + + /* Extract documentation if present */ + def->description = virXPathString("string(./description[1])", ctxt); + /* check if definitions with no IPv6 gateway addresses is to * allow guest-to-guest communications. */ @@ -2311,6 +2324,11 @@ virNetworkDefFormatBuf(virBuffer *buf, virUUIDFormat(uuid, uuidstr); virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuidstr); + virBufferEscapeString(buf, "<title>%s</title>\n", def->title); + + virBufferEscapeString(buf, "<description>%s</description>\n", + def->description); + if (virXMLFormatMetadata(buf, def->metadata) < 0) return -1; diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h index 2b2e9d15f0..5a1bdb1284 100644 --- a/src/conf/network_conf.h +++ b/src/conf/network_conf.h @@ -249,6 +249,8 @@ struct _virNetworkDef { unsigned char uuid[VIR_UUID_BUFLEN]; bool uuid_specified; char *name; + char *title; + char *description; int connections; /* # of guest interfaces connected to this network */ char *bridge; /* Name of bridge device */ diff --git a/src/conf/schemas/basictypes.rng b/src/conf/schemas/basictypes.rng index 2d6f1a2c84..26eb538077 100644 --- a/src/conf/schemas/basictypes.rng +++ b/src/conf/schemas/basictypes.rng @@ -610,6 +610,21 @@ </choice> </define> + <!-- + title and description element, may be placed anywhere under the root + --> + <define name="title"> + <element name="title"> + <ref name="objectNameWithSlash"/> + </element> + </define> + + <define name="description"> + <element name="description"> + <text/> + </element> + </define> + <define name="metadata"> <element name="metadata"> <zeroOrMore> diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index c2f56b0490..2556ac01ed 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -8,21 +8,6 @@ <include href="nwfilter_params.rng"/> <include href="privatedata.rng"/> - <!-- - description and title element, may be placed anywhere under the root - --> - <define name="description"> - <element name="description"> - <text/> - </element> - </define> - - <define name="title"> - <element name="title"> - <ref name="objectNameWithSlash"/> - </element> - </define> - <define name="createMode"> <data type="unsignedInt"> <param name="pattern">0[0-7]{3}|[0-7]{1,3}</param> diff --git a/src/conf/schemas/network.rng b/src/conf/schemas/network.rng index 4317572208..cda174ab4b 100644 --- a/src/conf/schemas/network.rng +++ b/src/conf/schemas/network.rng @@ -37,6 +37,16 @@ <text/> </element> + <!-- <title> element --> + <optional> + <ref name="title"/> + </optional> + + <!-- <description> element --> + <optional> + <ref name="description"/> + </optional> + <!-- <metadata> element --> <optional> <ref name="metadata"/> -- 2.41.0

This patch introduces public Get and Set APIs for modifying <title>, <description> and <metadata> elements of the Network object. - Added enum virNetworkMetadataType to select one of the above elements to operate on. - Added error code and messages for missing metadata. - Added public API implementation. - Added driver support. Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- include/libvirt/libvirt-network.h | 29 ++++++ include/libvirt/virterror.h | 1 + src/driver-network.h | 16 +++ src/libvirt-network.c | 167 ++++++++++++++++++++++++++++++ src/libvirt_public.syms | 6 ++ src/util/virerror.c | 3 + 6 files changed, 222 insertions(+) diff --git a/include/libvirt/libvirt-network.h b/include/libvirt/libvirt-network.h index 90cde0cf24..0f7ad8300f 100644 --- a/include/libvirt/libvirt-network.h +++ b/include/libvirt/libvirt-network.h @@ -547,4 +547,33 @@ virNetworkPortFree(virNetworkPortPtr port); int virNetworkPortRef(virNetworkPortPtr port); +/** + * virNetworkMetadataType: + * + * Since: 9.7.0 + */ +typedef enum { + VIR_NETWORK_METADATA_DESCRIPTION = 0, /* Operate on <description> (Since: 9.7.0) */ + VIR_NETWORK_METADATA_TITLE = 1, /* Operate on <title> (Since: 9.7.0) */ + VIR_NETWORK_METADATA_ELEMENT = 2, /* Operate on <metadata> (Since: 9.7.0) */ + +# ifdef VIR_ENUM_SENTINELS + VIR_NETWORK_METADATA_LAST /* (Since: 9.7.0) */ +# endif +} virNetworkMetadataType; + +int +virNetworkSetMetadata(virNetworkPtr network, + int type, + const char *metadata, + const char *key, + const char *uri, + unsigned int flags); + +char * +virNetworkGetMetadata(virNetworkPtr network, + int type, + const char *uri, + unsigned int flags); + #endif /* LIBVIRT_NETWORK_H */ diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index df13e4f11e..224eddc9e4 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -348,6 +348,7 @@ typedef enum { VIR_ERR_NO_HOSTNAME = 108, /* no domain's hostname found (Since: 6.1.0) */ 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) */ # ifdef VIR_ENUM_SENTINELS VIR_ERR_NUMBER_LAST /* (Since: 5.0.0) */ diff --git a/src/driver-network.h b/src/driver-network.h index 99efd4c8aa..1d19b013c9 100644 --- a/src/driver-network.h +++ b/src/driver-network.h @@ -161,6 +161,20 @@ typedef int virNetworkPortPtr **ports, unsigned int flags); +typedef int +(*virDrvNetworkSetMetadata)(virNetworkPtr network, + int type, + const char *metadata, + const char *key, + const char *uri, + unsigned int flags); + +typedef char * +(*virDrvNetworkGetMetadata)(virNetworkPtr network, + int type, + const char *uri, + unsigned int flags); + typedef struct _virNetworkDriver virNetworkDriver; /** @@ -202,4 +216,6 @@ struct _virNetworkDriver { virDrvNetworkPortGetParameters networkPortGetParameters; virDrvNetworkPortDelete networkPortDelete; virDrvNetworkListAllPorts networkListAllPorts; + virDrvNetworkSetMetadata networkSetMetadata; + virDrvNetworkGetMetadata networkGetMetadata; }; diff --git a/src/libvirt-network.c b/src/libvirt-network.c index 236dfe2f5d..c0daea3a60 100644 --- a/src/libvirt-network.c +++ b/src/libvirt-network.c @@ -1915,3 +1915,170 @@ virNetworkPortRef(virNetworkPortPtr port) virObjectRef(port); return 0; } + + +/** + * virNetworkSetMetadata: + * @network: a network object + * @type: type of metadata, from virNetworkMetadataType + * @metadata: new metadata text + * @key: XML namespace key, or NULL + * @uri: XML namespace URI, or NULL + * @flags: bitwise-OR of virNetworkUpdateFlags + * + * Sets the appropriate network element given by @type to the + * value of @metadata. A @type of VIR_NETWORK_METADATA_DESCRIPTION + * is free-form text; VIR_NETWORK_METADATA_TITLE is free-form, but no + * newlines are permitted, and should be short (although the length is + * not enforced). For these two options @key and @uri are irrelevant and + * must be set to NULL. + * + * For type VIR_NETWORK_METADATA_ELEMENT @metadata must be 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 XML (passing the empty string leaves the element present). + * + * The resulting metadata will be present in virNetworkGetXMLDesc(), + * as well as quick access through virNetworkGetMetadata(). + * + * @flags controls whether the live network state, persistent configuration, + * or both will be modified. + * + * Returns 0 on success, -1 in case of failure. + * + * Since: 9.7.0 + */ +int +virNetworkSetMetadata(virNetworkPtr network, + int type, + const char *metadata, + const char *key, + const char *uri, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DEBUG("network=%p, type=%d, metadata='%s', key='%s', uri='%s', flags=0x%x", + network, type, NULLSTR(metadata), NULLSTR(key), NULLSTR(uri), + flags); + + virResetLastError(); + + virCheckNetworkReturn(network, -1); + conn = network->conn; + + virCheckReadOnlyGoto(conn->flags, error); + + switch (type) { + case VIR_NETWORK_METADATA_TITLE: + if (metadata && strchr(metadata, '\n')) { + virReportInvalidArg(metadata, "%s", + _("metadata title can't contain " + "newlines")); + goto error; + } + G_GNUC_FALLTHROUGH; + case VIR_NETWORK_METADATA_DESCRIPTION: + virCheckNullArgGoto(uri, error); + virCheckNullArgGoto(key, error); + break; + case VIR_NETWORK_METADATA_ELEMENT: + virCheckNonNullArgGoto(uri, error); + if (metadata) + virCheckNonNullArgGoto(key, error); + break; + default: + /* For future expansion */ + break; + } + + if (conn->networkDriver->networkSetMetadata) { + int ret; + ret = conn->networkDriver->networkSetMetadata(network, type, metadata, key, uri, + flags); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(network->conn); + return -1; +} + + +/** + * virNetworkGetMetadata: + * @network: a network object + * @type: type of metadata, from virNetworkMetadataType + * @uri: XML namespace identifier + * @flags: bitwise-OR of virNetworkUpdateFlags + * + * Retrieves the appropriate network element given by @type. + * If VIR_NETWORK_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 XML is not present, the resulting + * error will be VIR_ERR_NO_NETWORK_METADATA. This method forms + * a shortcut for seeing information from virNetworkSetMetadata() + * without having to go through virNetworkGetXMLDesc(). + * + * @flags controls whether the live network state or persistent + * configuration will be queried. + * + * Returns the metadata string on success (caller must free), + * or NULL in case of failure. + * + * Since: 9.7.0 + */ +char * +virNetworkGetMetadata(virNetworkPtr network, + int type, + const char *uri, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DEBUG("network=%p, type=%d, uri='%s', flags=0x%x", + network, type, NULLSTR(uri), flags); + + virResetLastError(); + + virCheckNetworkReturn(network, NULL); + + VIR_EXCLUSIVE_FLAGS_GOTO(VIR_NETWORK_UPDATE_AFFECT_LIVE, + VIR_NETWORK_UPDATE_AFFECT_CONFIG, + error); + + switch (type) { + case VIR_NETWORK_METADATA_TITLE: + case VIR_NETWORK_METADATA_DESCRIPTION: + virCheckNullArgGoto(uri, error); + break; + case VIR_NETWORK_METADATA_ELEMENT: + virCheckNonNullArgGoto(uri, error); + break; + default: + /* For future expansion */ + break; + } + + conn = network->conn; + + if (conn->networkDriver->networkGetMetadata) { + char *ret; + if (!(ret = conn->networkDriver->networkGetMetadata(network, type, uri, flags))) + goto error; + return ret; + } + + virReportUnsupportedError(); + + error: + virDispatchError(network->conn); + return NULL; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 80742f268e..bd1e916d2a 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -932,4 +932,10 @@ LIBVIRT_9.0.0 { virDomainFDAssociate; } LIBVIRT_8.5.0; +LIBVIRT_9.7.0 { + global: + virNetworkGetMetadata; + virNetworkSetMetadata; +} LIBVIRT_9.0.0; + # .... define new API here using predicted next version number .... diff --git a/src/util/virerror.c b/src/util/virerror.c index 453f19514e..227a182417 100644 --- a/src/util/virerror.c +++ b/src/util/virerror.c @@ -1287,6 +1287,9 @@ static const virErrorMsgTuple virErrorMsgStrings[] = { [VIR_ERR_MULTIPLE_DOMAINS] = { N_("multiple matching domains found"), N_("multiple matching domains found: %1$s") }, + [VIR_ERR_NO_NETWORK_METADATA] = { + N_("metadata not found"), + N_("metadata not found: %1$s") }, }; G_STATIC_ASSERT(G_N_ELEMENTS(virErrorMsgStrings) == VIR_ERR_NUMBER_LAST); -- 2.41.0

- Defines wire protocol format. - Implements remote driver. Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- src/remote/remote_driver.c | 2 ++ src/remote/remote_protocol.x | 36 +++++++++++++++++++++++++++++++++++- src/remote_protocol-structs | 19 +++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index faad7292ed..b81c9bc611 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8153,6 +8153,8 @@ static virNetworkDriver network_driver = { .networkPortSetParameters = remoteNetworkPortSetParameters, /* 5.5.0 */ .networkPortGetParameters = remoteNetworkPortGetParameters, /* 5.5.0 */ .networkPortDelete = remoteNetworkPortDelete, /* 5.5.0 */ + .networkSetMetadata = remoteNetworkSetMetadata, /* 9.7.0 */ + .networkGetMetadata = remoteNetworkGetMetadata, /* 9.7.0 */ }; static virInterfaceDriver interface_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 5d86a51116..7ff059e393 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3323,6 +3323,26 @@ struct remote_network_event_lifecycle_msg { int detail; }; +struct remote_network_set_metadata_args { + remote_nonnull_network network; + int type; + remote_string metadata; + remote_string key; + remote_string uri; + unsigned int flags; +}; + +struct remote_network_get_metadata_args { + remote_nonnull_network network; + int type; + remote_string uri; + unsigned int flags; +}; + +struct remote_network_get_metadata_ret { + remote_nonnull_string metadata; +}; + struct remote_connect_storage_pool_event_register_any_args { int eventID; remote_storage_pool pool; @@ -6974,5 +6994,19 @@ enum remote_procedure { * @generate: none * @acl: domain:write */ - REMOTE_PROC_DOMAIN_FD_ASSOCIATE = 443 + REMOTE_PROC_DOMAIN_FD_ASSOCIATE = 443, + + /** + * @generate: both + * @acl: network:write + * @acl: network:save:!VIR_NETWORK_UPDATE_AFFECT_CONFIG|VIR_NETWORK_UPDATE_AFFECT_LIVE + * @acl: network:save:VIR_NETWORK_UPDATE_AFFECT_CONFIG + */ + REMOTE_PROC_NETWORK_SET_METADATA = 444, + + /** + * @generate: both + * @acl: network:read + */ + REMOTE_PROC_NETWORK_GET_METADATA = 445 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 3c6c230a16..c07e0af1e6 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2687,6 +2687,23 @@ struct remote_network_event_lifecycle_msg { int event; int detail; }; +struct remote_network_set_metadata_args { + remote_nonnull_network network; + int type; + remote_string metadata; + remote_string key; + remote_string uri; + u_int flags; +}; +struct remote_network_get_metadata_args { + remote_nonnull_network network; + int type; + remote_string uri; + u_int flags; +}; +struct remote_network_get_metadata_ret { + remote_nonnull_string metadata; +}; struct remote_connect_storage_pool_event_register_any_args { int eventID; remote_storage_pool pool; @@ -3717,4 +3734,6 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_RESTORE_PARAMS = 441, REMOTE_PROC_DOMAIN_ABORT_JOB_FLAGS = 442, REMOTE_PROC_DOMAIN_FD_ASSOCIATE = 443, + REMOTE_PROC_NETWORK_SET_METADATA = 444, + REMOTE_PROC_NETWORK_GET_METADATA = 445, }; -- 2.41.0

Adds two new commands and a new option: - 'net-desc' to show/modify network title and description. - 'net-metadata' to show/modify network metadata. - Option '--title' for 'net-list' to print corresponding network titles in an additional column. - Documentation for all the above. - XML Fallback function `virshNetworkGetXMLFromNet` for title and description for compatibility with hosts running older versions of libvirtd. Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- docs/manpages/virsh.rst | 77 ++++++++ tools/virsh-network.c | 411 ++++++++++++++++++++++++++++++++++++++-- tools/virsh-util.c | 25 +++ tools/virsh-util.h | 9 + 4 files changed, 511 insertions(+), 11 deletions(-) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index f4e5a0bd62..673812036d 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -5566,6 +5566,7 @@ to get a description of the XML network format used by libvirt. Optionally, the format of the input XML file can be validated against an internal RNG schema with *--validate*. + net-define ---------- @@ -5581,6 +5582,38 @@ Optionally, the format of the input XML file can be validated against an internal RNG schema with *--validate*. +net-desc +-------- + +**Syntax:** + +:: + + net-desc network [[--live] [--config] | + [--current]] [--title] [--edit] [--new-desc + New description or title message] + +Show or modify description and title of a network. These values are user +fields that allow storing arbitrary textual data to allow easy +identification of networks. Title should be short, although it's not enforced. +(See also ``net-metadata`` that works with XML based network metadata.) + +Flags *--live* or *--config* select whether this command works on live +or persistent definitions of the network. If both *--live* and *--config* +are specified, the *--config* option takes precedence on getting the current +description and both live configuration and config are updated while setting +the description. *--current* is exclusive and implied if none of these was +specified. + +Flag *--edit* specifies that an editor with the contents of current +description or title should be opened and the contents saved back afterwards. + +Flag *--title* selects operation on the title field instead of description. + +If neither of *--edit* and *--new-desc* are specified the note or description +is displayed instead of being modified. + + net-destroy ----------- @@ -5689,6 +5722,7 @@ net-list { [--table] | --name | --uuid } [--persistent] [<--transient>] [--autostart] [<--no-autostart>] + [--title] Returns the list of active networks, if *--all* is specified this will also include defined but inactive networks, if *--inactive* is specified only the @@ -5703,12 +5737,55 @@ instead of names. Flag *--table* specifies that the legacy table-formatted output should be used. This is the default. All of these are mutually exclusive. +If *--title* is specified, then the short network description (title) is +printed in an extra column. This flag is usable only with the default +*--table* output. + NOTE: When talking to older servers, this command is forced to use a series of API calls with an inherent race, where a pool might not be listed or might appear more than once if it changed state between calls while the list was being collected. Newer servers do not have this problem. +net-metadata +------------ + +**Syntax:** + +:: + + net-metadata network [[--live] [--config] | [--current]] + [--edit] [uri] [key] [set] [--remove] + +Show or modify custom XML metadata of a network. The metadata is a user +defined XML that allows storing arbitrary XML data in the network definition. +Multiple separate custom metadata pieces can be stored in the network XML. +The pieces are identified by a private XML namespace provided via the +*uri* argument. (See also ``net-desc`` that works with textual metadata of +a network, such as title and description.) + +Flags *--live* or *--config* select whether this command works on live +or persistent definitions of the network. If both *--live* and *--config* +are specified, the *--config* option takes precedence on getting the current +description and both live configuration and config are updated while setting +the description. *--current* is exclusive and implied if none of these was +specified. + +Flag *--remove* specifies that the metadata element specified by the *uri* +argument should be removed rather than updated. + +Flag *--edit* specifies that an editor with the metadata identified by the +*uri* argument should be opened and the contents saved back afterwards. +Otherwise the new contents can be provided via the *set* argument. + +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. + + net-name -------- diff --git a/tools/virsh-network.c b/tools/virsh-network.c index 42b7dba761..5a5cd15e24 100644 --- a/tools/virsh-network.c +++ b/tools/virsh-network.c @@ -330,6 +330,353 @@ cmdNetworkDestroy(vshControl *ctl, const vshCmd *cmd) return ret; } +/* + * "net-desc" command + */ +static const vshCmdInfo info_network_desc[] = { + {.name = "help", + .data = N_("show or set network's description or title") + }, + {.name = "desc", + .data = N_("Allows setting or modifying the description or title of a network.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_network_desc[] = { + VIRSH_COMMON_OPT_NETWORK_FULL(0), + VIRSH_COMMON_OPT_LIVE(N_("modify/get running state")), + VIRSH_COMMON_OPT_CONFIG(N_("modify/get persistent configuration")), + VIRSH_COMMON_OPT_CURRENT(N_("modify/get current state configuration")), + {.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 = "new-desc", + .type = VSH_OT_ARGV, + .help = N_("message") + }, + {.name = NULL} +}; + +/* extract description or title from network xml */ +static char * +virshGetNetworkDescription(vshControl *ctl, virNetworkPtr net, + bool title, unsigned int flags) +{ + char *desc = NULL; + g_autoptr(xmlDoc) doc = NULL; + g_autoptr(xmlXPathContext) ctxt = NULL; + int type; + + if (title) + type = VIR_NETWORK_METADATA_TITLE; + else + type = VIR_NETWORK_METADATA_DESCRIPTION; + + if ((desc = virNetworkGetMetadata(net, type, NULL, flags))) { + return desc; + } else { + int errCode = virGetLastErrorCode(); + + if (errCode == VIR_ERR_NO_NETWORK_METADATA) { + desc = g_strdup(""); + vshResetLibvirtError(); + return desc; + } + + if (errCode != VIR_ERR_NO_SUPPORT) + return desc; + } + + /* fall back to xml */ + if (virshNetworkGetXMLFromNet(ctl, net, flags, &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; +} + +static bool +cmdNetworkDesc(vshControl *ctl, const vshCmd *cmd) +{ + g_autoptr(virshNetwork) net = NULL; + bool config = vshCommandOptBool(cmd, "config"); + bool live = vshCommandOptBool(cmd, "live"); + bool current = vshCommandOptBool(cmd, "current"); + + bool title = vshCommandOptBool(cmd, "title"); + bool edit = vshCommandOptBool(cmd, "edit"); + + int type; + g_autofree char *descArg = NULL; + const vshCmdOpt *opt = NULL; + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + unsigned int flags = VIR_NETWORK_UPDATE_AFFECT_CURRENT; + unsigned int queryflags = 0; + + VSH_EXCLUSIVE_OPTIONS_VAR(current, live); + VSH_EXCLUSIVE_OPTIONS_VAR(current, config); + + if (config) { + flags |= VIR_NETWORK_UPDATE_AFFECT_CONFIG; + queryflags |= VIR_NETWORK_XML_INACTIVE; + } + if (live) + flags |= VIR_NETWORK_UPDATE_AFFECT_LIVE; + + if (!(net = virshCommandOptNetwork(ctl, cmd, NULL))) + return false; + + if (title) + type = VIR_NETWORK_METADATA_TITLE; + else + type = VIR_NETWORK_METADATA_DESCRIPTION; + + while ((opt = vshCommandOptArgv(ctl, cmd, opt))) + virBufferAsprintf(&buf, "%s ", opt->data); + + virBufferTrim(&buf, " "); + + descArg = virBufferContentAndReset(&buf); + + if (edit || descArg) { + g_autofree char *descNet = NULL; + g_autofree char *descNew = NULL; + + if (!(descNet = virshGetNetworkDescription(ctl, net, title, queryflags))) + return false; + + if (!descArg) + descArg = g_strdup(descNet); + + if (edit) { + g_autoptr(vshTempFile) tmp = NULL; + g_autofree char *desc_edited = NULL; + char *tmpstr; + + /* Create and open the 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 of file; some + * editors enforce a newline, this makes editing the title + * more convenient */ + if (title && + (tmpstr = strrchr(desc_edited, '\n')) && + *(tmpstr+1) == '\0') + *tmpstr = '\0'; + + /* Compare original XML with edited. Has it changed at all? */ + if (STREQ(descNet, desc_edited)) { + if (title) + vshPrintExtra(ctl, "%s", _("Network title not changed\n")); + else + vshPrintExtra(ctl, "%s", _("Network description not changed\n")); + + return true; + } + + descNew = g_steal_pointer(&desc_edited); + } else { + descNew = g_steal_pointer(&descArg); + } + + if (virNetworkSetMetadata(net, type, descNew, NULL, NULL, flags) < 0) { + if (title) + vshError(ctl, "%s", _("Failed to set new network title")); + else + vshError(ctl, "%s", _("Failed to set new network description")); + + return false; + } + + if (title) + vshPrintExtra(ctl, "%s", _("Network title updated successfully")); + else + vshPrintExtra(ctl, "%s", _("Network description updated successfully")); + + } else { + g_autofree char *desc = virshGetNetworkDescription(ctl, net, title, queryflags); + if (!desc) + return false; + + if (strlen(desc) > 0) { + vshPrint(ctl, "%s", desc); + } else { + if (title) + vshPrintExtra(ctl, _("No title for network: %1$s"), virNetworkGetName(net)); + else + vshPrintExtra(ctl, _("No description for network: %1$s"), virNetworkGetName(net)); + } + } + + return true; +} + +/* + * "net-metadata" command + */ +static const vshCmdInfo info_network_metadata[] = { + {.name = "help", + .data = N_("show or set network's custom XML metadata") + }, + {.name = "desc", + .data = N_("Shows or modifies the XML metadata of a network.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_network_metadata[] = { + VIRSH_COMMON_OPT_NETWORK_FULL(0), + {.name = "uri", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("URI of the namespace") + }, + VIRSH_COMMON_OPT_LIVE(N_("modify/get running state")), + VIRSH_COMMON_OPT_CONFIG(N_("modify/get persistent configuration")), + VIRSH_COMMON_OPT_CURRENT(N_("modify/get current state configuration")), + {.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 * +virshNetworkGetEditMetadata(vshControl *ctl G_GNUC_UNUSED, + virNetworkPtr net, + const char *uri, + unsigned int flags) +{ + char *ret; + + if (!(ret = virNetworkGetMetadata(net, VIR_NETWORK_METADATA_ELEMENT, + uri, flags))) { + vshResetLibvirtError(); + ret = g_strdup("\n"); + } + + return ret; +} + +static bool +cmdNetworkMetadata(vshControl *ctl, const vshCmd *cmd) +{ + g_autoptr(virshNetwork) net = NULL; + g_autoptr(xmlXPathContext) ctxt = NULL; + bool config = vshCommandOptBool(cmd, "config"); + bool live = vshCommandOptBool(cmd, "live"); + bool current = vshCommandOptBool(cmd, "current"); + 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 = VIR_NETWORK_UPDATE_AFFECT_CURRENT; + bool ret = false; + + VSH_EXCLUSIVE_OPTIONS_VAR(current, live); + VSH_EXCLUSIVE_OPTIONS_VAR(current, config); + VSH_EXCLUSIVE_OPTIONS("edit", "set"); + VSH_EXCLUSIVE_OPTIONS("remove", "set"); + VSH_EXCLUSIVE_OPTIONS("remove", "edit"); + + if (config) + flags |= VIR_NETWORK_UPDATE_AFFECT_CONFIG; + if (live) + flags |= VIR_NETWORK_UPDATE_AFFECT_LIVE; + + if (!(net = virshCommandOptNetwork(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 (virNetworkSetMetadata(net, VIR_NETWORK_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 \ + virshNetworkGetEditMetadata(ctl, net, uri, flags) +#define EDIT_NOT_CHANGED \ + do { \ + vshPrintExtra(ctl, "%s", _("Metadata not changed")); \ + ret = true; \ + goto edit_cleanup; \ + } while (0) + +#define EDIT_DEFINE \ + (virNetworkSetMetadata(net, VIR_NETWORK_METADATA_ELEMENT, doc_edited, \ + key, uri, flags) == 0) +#include "virsh-edit.c" + + vshPrintExtra(ctl, "%s\n", _("Metadata modified")); + } else { + g_autofree char *data = NULL; + g_autoptr(xmlDoc) doc = NULL; + /* get */ + if (!(data = virNetworkGetMetadata(net, VIR_NETWORK_METADATA_ELEMENT, + uri, flags))) + return false; + + vshPrint(ctl, "%s\n", data); + } + + ret = true; + + cleanup: + return ret; +} + /* * "net-dumpxml" command */ @@ -708,6 +1055,10 @@ static const vshCmdOptDef opts_network_list[] = { .type = VSH_OT_BOOL, .help = N_("list table (default)") }, + {.name = "title", + .type = VSH_OT_BOOL, + .help = N_("show network title") + }, {.name = NULL} }; @@ -721,6 +1072,7 @@ cmdNetworkList(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED) size_t i; bool ret = false; bool optName = vshCommandOptBool(cmd, "name"); + bool optTitle = vshCommandOptBool(cmd, "title"); bool optTable = vshCommandOptBool(cmd, "table"); bool optUUID = vshCommandOptBool(cmd, "uuid"); char uuid[VIR_UUID_STRING_BUFLEN]; @@ -754,8 +1106,12 @@ cmdNetworkList(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED) return false; if (optTable) { - table = vshTableNew(_("Name"), _("State"), _("Autostart"), - _("Persistent"), NULL); + if (optTitle) + table = vshTableNew(_("Name"), _("State"), _("Autostart"), + _("Persistent"), _("Title"), NULL); + else + table = vshTableNew(_("Name"), _("State"), _("Autostart"), + _("Persistent"), NULL); if (!table) goto cleanup; } @@ -771,16 +1127,37 @@ cmdNetworkList(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED) else autostartStr = is_autostart ? _("yes") : _("no"); - if (vshTableRowAppend(table, - virNetworkGetName(network), - virNetworkIsActive(network) ? - _("active") : _("inactive"), - autostartStr, - virNetworkIsPersistent(network) ? - _("yes") : _("no"), - NULL) < 0) - goto cleanup; + if (optTitle) { + g_autofree char *title = NULL; + + if (!(title = virshGetNetworkDescription(ctl, network, true, 0))) + goto cleanup; + if (vshTableRowAppend(table, + virNetworkGetName(network), + virNetworkIsActive(network) ? + _("active") : _("inactive"), + autostartStr, + virNetworkIsPersistent(network) ? + _("yes") : _("no"), + title, + NULL) < 0) + goto cleanup; + + } else { + if (vshTableRowAppend(table, + virNetworkGetName(network), + virNetworkIsActive(network) ? + _("active") : _("inactive"), + autostartStr, + virNetworkIsPersistent(network) ? + _("yes") : _("no"), + NULL) < 0) + goto cleanup; + + } + } else if (optUUID) { + if (virNetworkGetUUIDString(network, uuid) < 0) { vshError(ctl, "%s", _("Failed to get network's UUID")); goto cleanup; @@ -1825,6 +2202,12 @@ const vshCmdDef networkCmds[] = { .info = info_network_define, .flags = 0 }, + {.name = "net-desc", + .handler = cmdNetworkDesc, + .opts = opts_network_desc, + .info = info_network_desc, + .flags = 0 + }, {.name = "net-destroy", .handler = cmdNetworkDestroy, .opts = opts_network_destroy, @@ -1867,6 +2250,12 @@ const vshCmdDef networkCmds[] = { .info = info_network_list, .flags = 0 }, + {.name = "net-metadata", + .handler = cmdNetworkMetadata, + .opts = opts_network_metadata, + .info = info_network_metadata, + .flags = 0 + }, {.name = "net-name", .handler = cmdNetworkName, .opts = opts_network_name, diff --git a/tools/virsh-util.c b/tools/virsh-util.c index 61e403a636..fb6327613a 100644 --- a/tools/virsh-util.c +++ b/tools/virsh-util.c @@ -398,6 +398,31 @@ virshDomainGetXMLFromDom(vshControl *ctl, } +int +virshNetworkGetXMLFromNet(vshControl *ctl, + virNetworkPtr net, + unsigned int flags, + xmlDocPtr *xml, + xmlXPathContextPtr *ctxt) +{ + g_autofree char *desc = NULL; + + if (!(desc = virNetworkGetXMLDesc(net, flags))) { + vshError(ctl, _("Failed to get network description xml")); + return -1; + } + + *xml = virXMLParseStringCtxt(desc, _("(network_definition)"), ctxt); + + if (!(*xml)) { + vshError(ctl, _("Failed to parse network 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 0f81a2771b..2386847072 100644 --- a/tools/virsh-util.h +++ b/tools/virsh-util.h @@ -143,6 +143,15 @@ virshDomainGetXMLFromDom(vshControl *ctl, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(5) G_GNUC_WARN_UNUSED_RESULT; +int +virshNetworkGetXMLFromNet(vshControl *ctl, + virNetworkPtr net, + 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.41.0

On 8/16/23 20:47, K Shiva Kiran wrote:
Adds two new commands and a new option: - 'net-desc' to show/modify network title and description. - 'net-metadata' to show/modify network metadata. - Option '--title' for 'net-list' to print corresponding network titles in an additional column. - Documentation for all the above. - XML Fallback function `virshNetworkGetXMLFromNet` for title and description for compatibility with hosts running older versions of libvirtd.
Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- docs/manpages/virsh.rst | 77 ++++++++ tools/virsh-network.c | 411 ++++++++++++++++++++++++++++++++++++++-- tools/virsh-util.c | 25 +++ tools/virsh-util.h | 9 + 4 files changed, 511 insertions(+), 11 deletions(-)
There's too much going on in a single patch. Two new commands are introduced, another one grows new argument. It easily could have been separate patches. The problem with different semantic changes being tangled in one patch is: it's harder to review as reviewer has to track multiple things during review. Subsequently, reviewers tend to pick something better structured and this may then fall through the cracks. Michal

- Introduces virNetworkObjGetMetadata() and virNetworkObjSetMetadata(). - These functions implement common behaviour that can be reused by network drivers. - Introduces virNetworkObjUpdateModificationImpact() among other helper functions that resolve the live/persistent state of the network before setting metadata. - Eliminates redundant call of virNetworkObjSetDefTransient() in virNetworkConfigChangeSetup() among others. - Substituted redundant logic in networkUpdate() with a call to virNetworkObjUpdateModificationImpact(). Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- src/conf/virnetworkobj.c | 329 ++++++++++++++++++++++++++++++++++-- src/conf/virnetworkobj.h | 21 +++ src/libvirt_private.syms | 3 + src/network/bridge_driver.c | 14 +- src/test/test_driver.c | 16 +- 5 files changed, 347 insertions(+), 36 deletions(-) diff --git a/src/conf/virnetworkobj.c b/src/conf/virnetworkobj.c index b8b86da06f..20ee8eb58a 100644 --- a/src/conf/virnetworkobj.c +++ b/src/conf/virnetworkobj.c @@ -725,7 +725,6 @@ virNetworkObjReplacePersistentDef(virNetworkObj *obj, */ static int virNetworkObjConfigChangeSetup(virNetworkObj *obj, - virNetworkXMLOption *xmlopt, unsigned int flags) { bool isActive; @@ -738,17 +737,10 @@ virNetworkObjConfigChangeSetup(virNetworkObj *obj, return -1; } - if (flags & VIR_NETWORK_UPDATE_AFFECT_CONFIG) { - if (!obj->persistent) { + if ((flags & VIR_NETWORK_UPDATE_AFFECT_CONFIG) && + !obj->persistent) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", - _("cannot change persistent config of a " - "transient network")); - return -1; - } - /* this should already have been done by the driver, but do it - * anyway just in case. - */ - if (isActive && (virNetworkObjSetDefTransient(obj, false, xmlopt) < 0)) + _("cannot change persistent config of a transient network")); return -1; } @@ -1187,7 +1179,7 @@ virNetworkObjUpdate(virNetworkObj *obj, g_autoptr(virNetworkDef) configdef = NULL; /* normalize config data, and check for common invalid requests. */ - if (virNetworkObjConfigChangeSetup(obj, xmlopt, flags) < 0) + if (virNetworkObjConfigChangeSetup(obj, flags) < 0) return -1; if (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE) { @@ -1822,3 +1814,316 @@ virNetworkObjLoadAllPorts(virNetworkObj *net, return 0; } + + +/** + * virNetworkObjUpdateModificationImpact: + * + * @obj: network object + * @flags: flags to update the modification impact on + * + * Resolves virNetworkUpdateFlags in @flags so that they correctly + * apply to the actual state of @obj. @flags may be modified after call to this + * function. + * + * Returns 0 on success if @flags point to a valid combination for @obj or -1 on + * error. + */ +int +virNetworkObjUpdateModificationImpact(virNetworkObj *obj, + unsigned int *flags) +{ + bool isActive = virNetworkObjIsActive(obj); + + if ((*flags & (VIR_NETWORK_UPDATE_AFFECT_LIVE | VIR_NETWORK_UPDATE_AFFECT_CONFIG)) == + VIR_NETWORK_UPDATE_AFFECT_CURRENT) { + if (isActive) + *flags |= VIR_NETWORK_UPDATE_AFFECT_LIVE; + else + *flags |= VIR_NETWORK_UPDATE_AFFECT_CONFIG; + } + + if (virNetworkObjConfigChangeSetup(obj, *flags) < 0) + return -1; + + return 0; +} + + +/** + * virNetworkObjGetDefs: + * + * @net: network object + * @flags: for virNetworkUpdateFlags + * @liveDef: Set the pointer to the live definition of @net. + * @persDef: Set the pointer to the config definition of @net. + * + * Helper function to resolve @flags and retrieve correct network pointer + * objects. This function should be used only when the network driver + * creates net->newDef once the network has started. + * + * If @liveDef or @persDef are set it implies that @flags request modification + * thereof. + * + * Returns 0 on success and sets @liveDef and @persDef; -1 if @flags are + * inappropriate. + */ +static int +virNetworkObjGetDefs(virNetworkObj *net, + unsigned int flags, + virNetworkDef **liveDef, + virNetworkDef **persDef) +{ + if (liveDef) + *liveDef = NULL; + + if (persDef) + *persDef = NULL; + + if (virNetworkObjUpdateModificationImpact(net, &flags) < 0) + return -1; + + if (virNetworkObjIsActive(net)) { + if (liveDef && (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE)) + *liveDef = net->def; + + if (persDef && (flags & VIR_NETWORK_UPDATE_AFFECT_CONFIG)) + *persDef = net->newDef; + } else { + if (persDef) + *persDef = net->def; + } + + return 0; +} + + +/** + * virNetworkObjGetOneDefState: + * + * @net: Network object + * @flags: for virNetworkUpdateFlags + * @live: set to true if live config was returned (may be omitted) + * + * Helper function to resolve @flags and return the correct network pointer + * object. This function returns one of @net->def or @net->persistentDef + * according to @flags. @live is set to true if the live net config will be + * returned. This helper should be used only in APIs that guarantee + * that @flags contains exactly one of VIR_NETWORK_UPDATE_AFFECT_LIVE or + * VIR_NETWORK_UPDATE_AFFECT_CONFIG and not both. + * + * Returns the correct definition pointer or NULL on error. + */ +static virNetworkDef * +virNetworkObjGetOneDefState(virNetworkObj *net, + unsigned int flags, + bool *live) +{ + if (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE && + flags & VIR_NETWORK_UPDATE_AFFECT_CONFIG) { + virReportInvalidArg(flags, "%s", + _("Flags 'VIR_NETWORK_UPDATE_AFFECT_LIVE' and 'VIR_NETWORK_UPDATE_AFFECT_CONFIG' are mutually exclusive")); + return NULL; + } + + if (virNetworkObjUpdateModificationImpact(net, &flags) < 0) + return NULL; + + if (live) + *live = flags & VIR_NETWORK_UPDATE_AFFECT_LIVE; + + if (virNetworkObjIsActive(net) && flags & VIR_NETWORK_UPDATE_AFFECT_CONFIG) + return net->newDef; + + return net->def; +} + + +/** + * virNetworkObjGetOneDef: + * + * @net: Network object + * @flags: for virNetworkUpdateFlags + * + * Helper function to resolve @flags and return the correct network pointer + * object. This function returns one of @net->def or @net->persistentDef + * according to @flags. This helper should be used only in APIs that guarantee + * that @flags contains exactly one of VIR_NETWORK_UPDATE_AFFECT_LIVE or + * VIR_NETWORK_UPDATE_AFFECT_CONFIG and not both. + * + * Returns the correct definition pointer or NULL on error. + */ +static virNetworkDef * +virNetworkObjGetOneDef(virNetworkObj *net, + unsigned int flags) +{ + return virNetworkObjGetOneDefState(net, flags, NULL); +} + + +char * +virNetworkObjGetMetadata(virNetworkObj *net, + int type, + const char *uri, + unsigned int flags) +{ + virNetworkDef *def; + char *ret = NULL; + + virCheckFlags(VIR_NETWORK_UPDATE_AFFECT_LIVE | + VIR_NETWORK_UPDATE_AFFECT_CONFIG, NULL); + + if (type >= VIR_NETWORK_METADATA_LAST) { + virReportError(VIR_ERR_INVALID_ARG, + _("unknown metadata type '%1$d'"), type); + return NULL; + } + + if (!(def = virNetworkObjGetOneDef(net, flags))) + return NULL; + + switch ((virNetworkMetadataType) type) { + case VIR_NETWORK_METADATA_DESCRIPTION: + ret = g_strdup(def->description); + break; + + case VIR_NETWORK_METADATA_TITLE: + ret = g_strdup(def->title); + break; + + case VIR_NETWORK_METADATA_ELEMENT: + if (!def->metadata) + break; + + if (virXMLExtractNamespaceXML(def->metadata, uri, &ret) < 0) + return NULL; + break; + + case VIR_NETWORK_METADATA_LAST: + break; + } + + if (!ret) + virReportError(VIR_ERR_NO_NETWORK_METADATA, "%s", + _("Requested metadata element is not present")); + + return ret; +} + + +static int +virNetworkDefSetMetadata(virNetworkDef *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_NETWORK_METADATA_LAST) { + virReportError(VIR_ERR_INVALID_ARG, + _("unknown metadata type '%1$d'"), type); + return -1; + } + + switch ((virNetworkMetadataType) type) { + case VIR_NETWORK_METADATA_DESCRIPTION: + g_clear_pointer(&def->description, g_free); + + if (STRNEQ_NULLABLE(metadata, "")) + def->description = g_strdup(metadata); + break; + + case VIR_NETWORK_METADATA_TITLE: + g_clear_pointer(&def->title, g_free); + + if (STRNEQ_NULLABLE(metadata, "")) + def->title = g_strdup(metadata); + break; + + case VIR_NETWORK_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_NETWORK_METADATA_LAST: + break; + } + + return 0; +} + + +int +virNetworkObjSetMetadata(virNetworkObj *net, + int type, + const char *metadata, + const char *key, + const char *uri, + virNetworkXMLOption *xmlopt, + const char *stateDir, + const char *configDir, + unsigned int flags) +{ + virNetworkDef *def; + virNetworkDef *persistentDef; + + virCheckFlags(VIR_NETWORK_UPDATE_AFFECT_LIVE | + VIR_NETWORK_UPDATE_AFFECT_CONFIG, -1); + + if (virNetworkObjGetDefs(net, flags, &def, &persistentDef) < 0) + return -1; + + if (def) { + if (virNetworkDefSetMetadata(def, type, metadata, key, uri) < 0) + return -1; + + if (virNetworkObjSaveStatus(stateDir, net, xmlopt) < 0) + return -1; + } + + if (persistentDef) { + if (virNetworkDefSetMetadata(persistentDef, type, metadata, key, + uri) < 0) + return -1; + + if (virNetworkSaveConfig(configDir, persistentDef, xmlopt) < 0) + return -1; + } + + return 0; +} diff --git a/src/conf/virnetworkobj.h b/src/conf/virnetworkobj.h index 7d34fa3204..d3847d3422 100644 --- a/src/conf/virnetworkobj.h +++ b/src/conf/virnetworkobj.h @@ -258,3 +258,24 @@ virNetworkObjListNumOfNetworks(virNetworkObjList *nets, void virNetworkObjListPrune(virNetworkObjList *nets, unsigned int flags); + +int +virNetworkObjUpdateModificationImpact(virNetworkObj *obj, + unsigned int *flags); + +char * +virNetworkObjGetMetadata(virNetworkObj *network, + int type, + const char *uri, + unsigned int flags); + +int +virNetworkObjSetMetadata(virNetworkObj *network, + int type, + const char *metadata, + const char *key, + const char *uri, + virNetworkXMLOption *xmlopt, + const char *stateDir, + const char *configDir, + unsigned int flags); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index da60c965dd..015384c0ec 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1291,6 +1291,7 @@ virNetworkObjGetDef; virNetworkObjGetDnsmasqPid; virNetworkObjGetFloorSum; virNetworkObjGetMacMap; +virNetworkObjGetMetadata; virNetworkObjGetNewDef; virNetworkObjGetPersistentDef; virNetworkObjGetPortStatusDir; @@ -1321,11 +1322,13 @@ virNetworkObjSetDefTransient; virNetworkObjSetDnsmasqPid; virNetworkObjSetFloorSum; virNetworkObjSetMacMap; +virNetworkObjSetMetadata; virNetworkObjTaint; virNetworkObjUnrefMacMap; virNetworkObjUnsetDefTransient; virNetworkObjUpdate; virNetworkObjUpdateAssignDef; +virNetworkObjUpdateModificationImpact; # conf/virnetworkportdef.h diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 9eb543a0a3..e776d86c73 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -3210,18 +3210,10 @@ networkUpdate(virNetworkPtr net, } } - /* VIR_NETWORK_UPDATE_AFFECT_CURRENT means "change LIVE if network - * is active, else change CONFIG - */ + if (virNetworkObjUpdateModificationImpact(obj, &flags) < 0) + goto cleanup; + isActive = virNetworkObjIsActive(obj); - if ((flags & (VIR_NETWORK_UPDATE_AFFECT_LIVE | - VIR_NETWORK_UPDATE_AFFECT_CONFIG)) == - VIR_NETWORK_UPDATE_AFFECT_CURRENT) { - if (isActive) - flags |= VIR_NETWORK_UPDATE_AFFECT_LIVE; - else - flags |= VIR_NETWORK_UPDATE_AFFECT_CONFIG; - } if (isActive && (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE)) { /* Take care of anything that must be done before updating the diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 4b8e02c684..ff9d0fabaa 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -5658,7 +5658,7 @@ testNetworkUpdate(virNetworkPtr net, { testDriver *privconn = net->conn->privateData; virNetworkObj *obj = NULL; - int isActive, ret = -1; + int ret = -1; virCheckFlags(VIR_NETWORK_UPDATE_AFFECT_LIVE | VIR_NETWORK_UPDATE_AFFECT_CONFIG, @@ -5667,18 +5667,8 @@ testNetworkUpdate(virNetworkPtr net, if (!(obj = testNetworkObjFindByUUID(privconn, net->uuid))) goto cleanup; - /* VIR_NETWORK_UPDATE_AFFECT_CURRENT means "change LIVE if network - * is active, else change CONFIG - */ - isActive = virNetworkObjIsActive(obj); - if ((flags & (VIR_NETWORK_UPDATE_AFFECT_LIVE - | VIR_NETWORK_UPDATE_AFFECT_CONFIG)) == - VIR_NETWORK_UPDATE_AFFECT_CURRENT) { - if (isActive) - flags |= VIR_NETWORK_UPDATE_AFFECT_LIVE; - else - flags |= VIR_NETWORK_UPDATE_AFFECT_CONFIG; - } + if (virNetworkObjUpdateModificationImpact(obj, &flags) < 0) + goto cleanup; /* update the network config in memory/on disk */ if (virNetworkObjUpdate(obj, command, section, -- 2.41.0

On 8/16/23 20:47, K Shiva Kiran wrote:
- Introduces virNetworkObjGetMetadata() and virNetworkObjSetMetadata(). - These functions implement common behaviour that can be reused by network drivers. - Introduces virNetworkObjUpdateModificationImpact() among other helper functions that resolve the live/persistent state of the network before setting metadata. - Eliminates redundant call of virNetworkObjSetDefTransient() in virNetworkConfigChangeSetup() among others. - Substituted redundant logic in networkUpdate() with a call to virNetworkObjUpdateModificationImpact().
Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- src/conf/virnetworkobj.c | 329 ++++++++++++++++++++++++++++++++++-- src/conf/virnetworkobj.h | 21 +++ src/libvirt_private.syms | 3 + src/network/bridge_driver.c | 14 +- src/test/test_driver.c | 16 +- 5 files changed, 347 insertions(+), 36 deletions(-)
Again, way too much changes, disperse in semantics for one patch. You've introduced virNetworkObjUpdateModificationImpact(). Perfect! But it should have been one patch. Then you eliminate redundant call to virNetworkObjSetDefTransient()? Splendid, but again - it's a different change and has nothing to do with virNetworkObjSetDefTransient(). You implement new APIs? Sweet, but what do they have to do with the redundant call? Splitting patches per directory is not the same as "one semantic change per patch". Sometimes it is, e.g. in the series I've posted earlier today: https://listman.redhat.com/archives/libvir-list/2023-August/241416.html I know I might be asking too much, but try to put yourself into reviewers shoes. Libvirt's code base is not exactly the smallest and reviewing one change (and trying to think of all implications) is hard enough already. If changes are intertwined into one patch then it's needlessly harder. Michal

My previous misunderstanding was exactly as you mentioned, to group commits on a file to file basis. Now I have understood what a semantic change should be like, with your provided example. You are not asking for much, your corrections help me a lot in my learning journey and I am very grateful for them. Please feel free to point out any more such things in the future. Shiva On 25 August 2023 4:09:09 pm IST, "Michal Prívozník" <mprivozn@redhat.com> wrote:
Again, way too much changes, disperse in semantics for one patch. You've introduced virNetworkObjUpdateModificationImpact(). Perfect! But it should have been one patch. Then you eliminate redundant call to virNetworkObjSetDefTransient()? Splendid, but again - it's a different change and has nothing to do with virNetworkObjSetDefTransient(). You implement new APIs? Sweet, but what do they have to do with the redundant call?
Splitting patches per directory is not the same as "one semantic change per patch". Sometimes it is, e.g. in the series I've posted earlier today:
https://listman.redhat.com/archives/libvir-list/2023-August/241416.html
I know I might be asking too much, but try to put yourself into reviewers shoes. Libvirt's code base is not exactly the smallest and reviewing one change (and trying to think of all implications) is hard enough already. If changes are intertwined into one patch then it's needlessly harder.
Michal

This commit implements the newly defined Network Metadata Get and Set APIs into the test driver. It also adds a new testcase "networkmetadatatest" to test the APIs. Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- src/conf/network_conf.c | 3 + src/test/test_driver.c | 67 ++++++++ tests/meson.build | 1 + tests/networkmetadatatest.c | 297 ++++++++++++++++++++++++++++++++++++ 4 files changed, 368 insertions(+) create mode 100644 tests/networkmetadatatest.c diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c index 262e9a9fc7..e4c8c5fd4d 100644 --- a/src/conf/network_conf.c +++ b/src/conf/network_conf.c @@ -2564,6 +2564,9 @@ virNetworkSaveXML(const char *configDir, char uuidstr[VIR_UUID_STRING_BUFLEN]; g_autofree char *configFile = NULL; + if (!configDir) + return 0; + if ((configFile = virNetworkConfigFile(configDir, def->name)) == NULL) return -1; diff --git a/src/test/test_driver.c b/src/test/test_driver.c index ff9d0fabaa..d1a5eaff7e 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -633,6 +633,25 @@ static int testStoragePoolObjSetDefaults(virStoragePoolObj *obj); static int testNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info); static virNetworkObj *testNetworkObjFindByName(testDriver *privconn, const char *name); +static virNetworkObj * +testNetworkObjFromNetwork(virNetworkPtr network) +{ + virNetworkObj *net; + testDriver *driver = network->conn->privateData; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + net = virNetworkObjFindByUUID(driver->networks, network->uuid); + if (!net) { + virUUIDFormat(network->uuid, uuidstr); + virReportError(VIR_ERR_NO_NETWORK, + _("no network with matching uuid '%1$s' (%2$s)"), + uuidstr, network->name); + } + + return net; +} + + static virDomainObj * testDomObjFromDomain(virDomainPtr domain) { @@ -9941,6 +9960,52 @@ testConnectGetAllDomainStats(virConnectPtr conn, return ret; } +static char * +testNetworkGetMetadata(virNetworkPtr net, + int type, + const char *uri, + unsigned int flags) +{ + virNetworkObj *privnet; + char *ret; + + virCheckFlags(VIR_NETWORK_UPDATE_AFFECT_LIVE | + VIR_NETWORK_UPDATE_AFFECT_CONFIG, NULL); + + if (!(privnet = testNetworkObjFromNetwork(net))) + return NULL; + + ret = virNetworkObjGetMetadata(privnet, type, uri, flags); + + virNetworkObjEndAPI(&privnet); + return ret; +} + +static int +testNetworkSetMetadata(virNetworkPtr net, + int type, + const char *metadata, + const char *key, + const char *uri, + unsigned int flags) +{ + virNetworkObj *privnet; + int ret; + + virCheckFlags(VIR_NETWORK_UPDATE_AFFECT_LIVE | + VIR_NETWORK_UPDATE_AFFECT_CONFIG, -1); + + if (!(privnet = testNetworkObjFromNetwork(net))) + return -1; + + ret = virNetworkObjSetMetadata(privnet, type, metadata, + key, uri, NULL, + NULL, NULL, flags); + + virNetworkObjEndAPI(&privnet); + return ret; +} + /* * Test driver */ @@ -10134,6 +10199,8 @@ static virNetworkDriver testNetworkDriver = { .networkSetAutostart = testNetworkSetAutostart, /* 0.3.2 */ .networkIsActive = testNetworkIsActive, /* 0.7.3 */ .networkIsPersistent = testNetworkIsPersistent, /* 0.7.3 */ + .networkSetMetadata = testNetworkSetMetadata, /* 9.7.0 */ + .networkGetMetadata = testNetworkGetMetadata, /* 9.7.0 */ }; static virInterfaceDriver testInterfaceDriver = { diff --git a/tests/meson.build b/tests/meson.build index e6589ec555..e76289da62 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -258,6 +258,7 @@ tests += [ { 'name': 'genericxml2xmltest' }, { 'name': 'interfacexml2xmltest' }, { 'name': 'metadatatest' }, + { 'name': 'networkmetadatatest' }, { 'name': 'networkxml2xmlupdatetest' }, { 'name': 'nodedevxml2xmltest' }, { 'name': 'nwfilterxml2xmltest' }, diff --git a/tests/networkmetadatatest.c b/tests/networkmetadatatest.c new file mode 100644 index 0000000000..c309fb29b0 --- /dev/null +++ b/tests/networkmetadatatest.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(virNetworkPtr net) +{ + g_autoptr(xmlDoc) doc = NULL; + g_autoptr(xmlXPathContext) ctxt = NULL; + xmlNodePtr node; + + g_autofree char *xml = NULL; + + if (!(xml = virNetworkGetXMLDesc(net, 0))) + return NULL; + + if (!(doc = virXMLParseStringCtxt(xml, "(network_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(virNetworkPtr net, + const char *expectXML, + const char *expectAPI, + const char *uri) +{ + g_autofree char *metadataXML = NULL; + g_autofree char *metadataAPI = NULL; + + if (!expectAPI) { + if ((metadataAPI = virNetworkGetMetadata(net, + VIR_NETWORK_METADATA_ELEMENT, + uri, 0))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "expected no metadata in API, but got:\n[%s]", + metadataAPI); + return false; + } + } else { + if (!(metadataAPI = virNetworkGetMetadata(net, + VIR_NETWORK_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(net))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "expected no metadata in XML, but got:\n[%s]", + metadataXML); + return false; + } + } else { + if (!(metadataXML = getMetadataFromXML(net))) + 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; + virNetworkPtr net; + + const char *data; + const char *expect; + int type; + bool fail; +}; + + +static int +testAssignMetadata(const void *data) +{ + const struct metadataTest *test = data; + + if (virNetworkSetMetadata(test->net, VIR_NETWORK_METADATA_ELEMENT, + metadata1, "herp", "http://herp.derp/", 0) < 0) + return -1; + + if (!verifyMetadata(test->net, metadata1_ns, metadata1, "http://herp.derp/")) + return -1; + + return 0; +} + +static int +testRewriteMetadata(const void *data) +{ + const struct metadataTest *test = data; + + if (virNetworkSetMetadata(test->net, VIR_NETWORK_METADATA_ELEMENT, + metadata2, "blurb", "http://herp.derp/", 0) < 0) + return -1; + + if (!verifyMetadata(test->net, metadata2_ns, metadata2, "http://herp.derp/")) + return -1; + + return 0; +} + +static int +testEraseMetadata(const void *data) +{ + const struct metadataTest *test = data; + + if (virNetworkSetMetadata(test->net, VIR_NETWORK_METADATA_ELEMENT, + NULL, NULL, "http://herp.derp/", 0) < 0) + return -1; + + if (!verifyMetadata(test->net, 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 (virNetworkSetMetadata(test->net, test->type, test->data, NULL, NULL, 0) < 0) { + if (test->fail) + return 0; + return -1; + } + + actual = virNetworkGetMetadata(test->net, 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_NETWORK_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.net = virNetworkLookupByName(test.conn, "default"))) { + 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); + + virNetworkFree(test.net); + virConnectClose(test.conn); + + return ret; +} + +VIR_TEST_MAIN(mymain) -- 2.41.0

Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net> --- src/network/bridge_driver.c | 64 +++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index e776d86c73..caad085192 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -5119,6 +5119,68 @@ networkListAllPorts(virNetworkPtr net, } +static int +networkSetMetadata(virNetworkPtr net, + int type, + const char *metadata, + const char *key, + const char *uri, + unsigned int flags) +{ + virNetworkDriverState *driver = networkGetDriver(); + virNetworkObj *obj = NULL; + virNetworkDef *def = NULL; + g_autoptr(virNetworkDriverConfig) cfg = NULL; + int ret = -1; + + virCheckFlags(VIR_NETWORK_UPDATE_AFFECT_LIVE | + VIR_NETWORK_UPDATE_AFFECT_CONFIG, -1); + + if (!(obj = networkObjFromNetwork(net))) + return -1; + + cfg = virNetworkDriverGetConfig(driver); + def = virNetworkObjGetDef(obj); + + if (virNetworkSetMetadataEnsureACL(net->conn, def, flags) < 0) + goto cleanup; + + ret = virNetworkObjSetMetadata(obj, type, metadata, key, uri, + driver->xmlopt, cfg->stateDir, + cfg->networkConfigDir, flags); + + cleanup: + virNetworkObjEndAPI(&obj); + return ret; +} + + +static char * +networkGetMetadata(virNetworkPtr net, + int type, + const char *uri, + unsigned int flags) +{ + virNetworkObj *obj = NULL; + virNetworkDef *def = NULL; + char *ret = NULL; + + if (!(obj = networkObjFromNetwork(net))) + return NULL; + + def = virNetworkObjGetDef(obj); + + if (virNetworkGetMetadataEnsureACL(net->conn, def) < 0) + goto cleanup; + + ret = virNetworkObjGetMetadata(obj, type, uri, flags); + + cleanup: + virNetworkObjEndAPI(&obj); + return ret; +} + + static virNetworkDriver networkDriver = { .name = "bridge", .connectNumOfNetworks = networkConnectNumOfNetworks, /* 0.2.0 */ @@ -5152,6 +5214,8 @@ static virNetworkDriver networkDriver = { .networkListAllPorts = networkListAllPorts, /* 5.5.0 */ .networkPortGetParameters = networkPortGetParameters, /* 5.5.0 */ .networkPortSetParameters = networkPortSetParameters, /* 5.5.0 */ + .networkGetMetadata = networkGetMetadata, /* 9.7.0 */ + .networkSetMetadata = networkSetMetadata, /* 9.7.0 */ }; -- 2.41.0

On 8/16/23 20:47, K Shiva Kiran wrote:
This commit adds the following: - Introduction of <title> and <description> fields to the Network Object. - Introduction of Get and Set Public APIs for the aforementioned fields. - virsh exposure of the aforementioned Public APIs. - Adds implementation in test driver along with a testcase. - Implementation in bridge driver.
This is a v2 of: https://listman.redhat.com/archives/libvir-list/2023-July/240828.html Diff to v1: - Corrected placement of structs in remote_protocol-structs. - Removed redundant call to virNetworkObjSetDefTransient() in virNetworkConfigChangeSetup(). - Removed redundant logic in networkUpdate(), substituted by call to newly introduced virNetworkObjUpdateModificationImpact(). - Added virsh exposure of the APIs. - Added bridge driver implementation.
Signed-off-by: K Shiva Kiran <shiva_kr@riseup.net>
K Shiva Kiran (7): Add <title> and <description> for Network Objects Adding Public Get and Set APIs for Network Metadata Implementing Remote Protocol for Network Metadata virsh exposure of Network Metadata APIs Add virNetworkObj Get and Set Methods for Metadata Add Test driver and testcase for Network Metadata change APIs Added bridge driver implementation
docs/formatnetwork.rst | 11 + docs/manpages/virsh.rst | 77 ++++++ include/libvirt/libvirt-network.h | 29 +++ include/libvirt/virterror.h | 1 + src/conf/network_conf.c | 21 ++ src/conf/network_conf.h | 2 + src/conf/schemas/basictypes.rng | 15 ++ src/conf/schemas/domaincommon.rng | 15 -- src/conf/schemas/network.rng | 10 + src/conf/virnetworkobj.c | 329 +++++++++++++++++++++++- src/conf/virnetworkobj.h | 21 ++ src/driver-network.h | 16 ++ src/libvirt-network.c | 167 ++++++++++++ src/libvirt_private.syms | 3 + src/libvirt_public.syms | 6 + src/network/bridge_driver.c | 78 +++++- src/remote/remote_driver.c | 2 + src/remote/remote_protocol.x | 36 ++- src/remote_protocol-structs | 19 ++ src/test/test_driver.c | 83 +++++- src/util/virerror.c | 3 + tests/meson.build | 1 + tests/networkmetadatatest.c | 297 +++++++++++++++++++++ tools/virsh-network.c | 411 +++++++++++++++++++++++++++++- tools/virsh-util.c | 25 ++ tools/virsh-util.h | 9 + 26 files changed, 1624 insertions(+), 63 deletions(-) create mode 100644 tests/networkmetadatatest.c
Alright. Let me merge these. Vven though they should have been structured differently, the code looks factually okay. Reviewed-by: Michal Privoznik <mprivozn@redhat.com> As we are getting close to the release, please do write a NEWS entry as this is something that users might find valuable. Michal
participants (3)
-
K Shiva Kiran
-
Michal Prívozník
-
Shiva