libvirt/src/conf/virnetworkobj.c
Andrea Bolognani f69e5ea9f7 conf: Write network config to disk after generating UUID
While we generally expect libvirt objects to be defined using the
appropriate APIs, there are cases where it's reasonable for an
external entity, usually a package manager, to drop a valid
configuration file under /etc/libvirt and have libvirt take over
from there: notably, this is exactly how the default network is
handled.

For the most part, whether the configuration is saved back to disk
after being parsed by libvirt doesn't matter, because we'll end up
with the same values anyway, but an obvious exception to this is
data that gets randomly generated when not present, namely MAC
address and UUID.

Historically, both were handled by our build system, but commit
a47ae7c004 moved handling of the former inside libvirt proper;
this commit extends such behavior to the latter as well.

Proper error handling for the virNetworkSaveConfig() call, which
was missing until now, is introduced in the process.

Signed-off-by: Andrea Bolognani <abologna@redhat.com>
Reviewed-by: Laine Stump <laine@redhat.com>
2020-11-20 11:43:56 +01:00

1906 lines
49 KiB
C

/*
* virnetworkobj.c: handle network objects
* (derived from network_conf.c)
*
* 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 <dirent.h>
#include "datatypes.h"
#include "virnetworkobj.h"
#include "viralloc.h"
#include "virerror.h"
#include "virfile.h"
#include "virhash.h"
#include "virlog.h"
#include "virstring.h"
#define VIR_FROM_THIS VIR_FROM_NETWORK
VIR_LOG_INIT("conf.virnetworkobj");
/* Currently, /sbin/tc implementation allows up to 16 bits for
* minor class size. But the initial bitmap doesn't have to be
* that big. */
#define INIT_CLASS_ID_BITMAP_SIZE (1<<4)
struct _virNetworkObj {
virObjectLockable parent;
pid_t dnsmasqPid;
pid_t radvdPid;
bool active;
bool autostart;
bool persistent;
virNetworkDefPtr def; /* The current definition */
virNetworkDefPtr newDef; /* New definition to activate at shutdown */
virBitmapPtr classIdMap; /* bitmap of class IDs for QoS */
unsigned long long floor_sum; /* sum of all 'floor'-s of attached NICs */
unsigned int taint;
/* Immutable pointer, self locking APIs */
virMacMapPtr macmap;
GHashTable *ports; /* uuid -> virNetworkPortDefPtr */
};
struct _virNetworkObjList {
virObjectRWLockable parent;
GHashTable *objs;
};
static virClassPtr virNetworkObjClass;
static virClassPtr virNetworkObjListClass;
static void virNetworkObjDispose(void *obj);
static void virNetworkObjListDispose(void *obj);
static int
virNetworkObjOnceInit(void)
{
if (!VIR_CLASS_NEW(virNetworkObj, virClassForObjectLockable()))
return -1;
if (!VIR_CLASS_NEW(virNetworkObjList, virClassForObjectRWLockable()))
return -1;
return 0;
}
VIR_ONCE_GLOBAL_INIT(virNetworkObj);
static int
virNetworkObjLoadAllPorts(virNetworkObjPtr net,
const char *stateDir);
static void
virNetworkObjPortFree(void *val)
{
virNetworkPortDefFree(val);
}
virNetworkObjPtr
virNetworkObjNew(void)
{
virNetworkObjPtr obj;
if (virNetworkObjInitialize() < 0)
return NULL;
if (!(obj = virObjectLockableNew(virNetworkObjClass)))
return NULL;
obj->classIdMap = virBitmapNew(INIT_CLASS_ID_BITMAP_SIZE);
/* The first three class IDs are already taken */
if (virBitmapSetBitExpand(obj->classIdMap, 0) < 0 ||
virBitmapSetBitExpand(obj->classIdMap, 1) < 0 ||
virBitmapSetBitExpand(obj->classIdMap, 2) < 0)
goto error;
if (!(obj->ports = virHashNew(virNetworkObjPortFree)))
goto error;
virObjectLock(obj);
return obj;
error:
virObjectUnref(obj);
return NULL;
}
void
virNetworkObjEndAPI(virNetworkObjPtr *obj)
{
if (!*obj)
return;
virObjectUnlock(*obj);
virObjectUnref(*obj);
*obj = NULL;
}
virNetworkDefPtr
virNetworkObjGetDef(virNetworkObjPtr obj)
{
return obj->def;
}
void
virNetworkObjSetDef(virNetworkObjPtr obj,
virNetworkDefPtr def)
{
obj->def = def;
}
virNetworkDefPtr
virNetworkObjGetNewDef(virNetworkObjPtr obj)
{
return obj->newDef;
}
bool
virNetworkObjIsActive(virNetworkObjPtr obj)
{
return obj->active;
}
void
virNetworkObjSetActive(virNetworkObjPtr obj,
bool active)
{
obj->active = active;
}
bool
virNetworkObjIsPersistent(virNetworkObjPtr obj)
{
return obj->persistent;
}
bool
virNetworkObjIsAutostart(virNetworkObjPtr obj)
{
return obj->autostart;
}
void
virNetworkObjSetAutostart(virNetworkObjPtr obj,
bool autostart)
{
obj->autostart = autostart;
}
pid_t
virNetworkObjGetDnsmasqPid(virNetworkObjPtr obj)
{
return obj->dnsmasqPid;
}
void
virNetworkObjSetDnsmasqPid(virNetworkObjPtr obj,
pid_t dnsmasqPid)
{
obj->dnsmasqPid = dnsmasqPid;
}
pid_t
virNetworkObjGetRadvdPid(virNetworkObjPtr obj)
{
return obj->radvdPid;
}
void
virNetworkObjSetRadvdPid(virNetworkObjPtr obj,
pid_t radvdPid)
{
obj->radvdPid = radvdPid;
}
virBitmapPtr
virNetworkObjGetClassIdMap(virNetworkObjPtr obj)
{
return obj->classIdMap;
}
virMacMapPtr
virNetworkObjGetMacMap(virNetworkObjPtr obj)
{
return obj->macmap;
}
unsigned long long
virNetworkObjGetFloorSum(virNetworkObjPtr obj)
{
return obj->floor_sum;
}
void
virNetworkObjSetFloorSum(virNetworkObjPtr obj,
unsigned long long floor_sum)
{
obj->floor_sum = floor_sum;
}
void
virNetworkObjSetMacMap(virNetworkObjPtr obj,
virMacMapPtr macmap)
{
obj->macmap = macmap;
}
void
virNetworkObjUnrefMacMap(virNetworkObjPtr obj)
{
virObjectUnref(obj->macmap);
obj->macmap = NULL;
}
int
virNetworkObjMacMgrAdd(virNetworkObjPtr obj,
const char *dnsmasqStateDir,
const char *domain,
const virMacAddr *mac)
{
char macStr[VIR_MAC_STRING_BUFLEN];
char *file = NULL;
int ret = -1;
if (!obj->macmap)
return 0;
virMacAddrFormat(mac, macStr);
if (!(file = virMacMapFileName(dnsmasqStateDir, obj->def->bridge)))
goto cleanup;
if (virMacMapAdd(obj->macmap, domain, macStr) < 0)
goto cleanup;
if (virMacMapWriteFile(obj->macmap, file) < 0)
goto cleanup;
ret = 0;
cleanup:
VIR_FREE(file);
return ret;
}
int
virNetworkObjMacMgrDel(virNetworkObjPtr obj,
const char *dnsmasqStateDir,
const char *domain,
const virMacAddr *mac)
{
char macStr[VIR_MAC_STRING_BUFLEN];
char *file = NULL;
int ret = -1;
if (!obj->macmap)
return 0;
virMacAddrFormat(mac, macStr);
if (!(file = virMacMapFileName(dnsmasqStateDir, obj->def->bridge)))
goto cleanup;
if (virMacMapRemove(obj->macmap, domain, macStr) < 0)
goto cleanup;
if (virMacMapWriteFile(obj->macmap, file) < 0)
goto cleanup;
ret = 0;
cleanup:
VIR_FREE(file);
return ret;
}
virNetworkObjListPtr
virNetworkObjListNew(void)
{
virNetworkObjListPtr nets;
if (virNetworkObjInitialize() < 0)
return NULL;
if (!(nets = virObjectRWLockableNew(virNetworkObjListClass)))
return NULL;
if (!(nets->objs = virHashNew(virObjectFreeHashData))) {
virObjectUnref(nets);
return NULL;
}
return nets;
}
static virNetworkObjPtr
virNetworkObjFindByUUIDLocked(virNetworkObjListPtr nets,
const unsigned char *uuid)
{
virNetworkObjPtr obj = NULL;
char uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(uuid, uuidstr);
obj = virHashLookup(nets->objs, uuidstr);
if (obj)
virObjectRef(obj);
return obj;
}
/**
* virNetworkObjFindByUUID:
* @nets: list of network objects
* @uuid: network uuid to find
*
* This functions locks @nets and find network object which
* corresponds to @uuid.
*
* Returns: locked and ref'd network object.
*/
virNetworkObjPtr
virNetworkObjFindByUUID(virNetworkObjListPtr nets,
const unsigned char *uuid)
{
virNetworkObjPtr obj;
virObjectRWLockRead(nets);
obj = virNetworkObjFindByUUIDLocked(nets, uuid);
virObjectRWUnlock(nets);
if (obj)
virObjectLock(obj);
return obj;
}
static int
virNetworkObjSearchName(const void *payload,
const char *name G_GNUC_UNUSED,
const void *data)
{
virNetworkObjPtr obj = (virNetworkObjPtr) payload;
int want = 0;
virObjectLock(obj);
if (STREQ(obj->def->name, (const char *)data))
want = 1;
virObjectUnlock(obj);
return want;
}
static virNetworkObjPtr
virNetworkObjFindByNameLocked(virNetworkObjListPtr nets,
const char *name)
{
virNetworkObjPtr obj = NULL;
obj = virHashSearch(nets->objs, virNetworkObjSearchName, name, NULL);
if (obj)
virObjectRef(obj);
return obj;
}
/**
* virNetworkObjFindByName:
* @nets: list of network objects
* @name: network name to find
*
* This functions locks @nets and find network object which
* corresponds to @name.
*
* Returns: locked and ref'd network object.
*/
virNetworkObjPtr
virNetworkObjFindByName(virNetworkObjListPtr nets,
const char *name)
{
virNetworkObjPtr obj;
virObjectRWLockRead(nets);
obj = virNetworkObjFindByNameLocked(nets, name);
virObjectRWUnlock(nets);
if (obj)
virObjectLock(obj);
return obj;
}
bool
virNetworkObjTaint(virNetworkObjPtr obj,
virNetworkTaintFlags taint)
{
unsigned int flag = (1 << taint);
if (obj->taint & flag)
return false;
obj->taint |= flag;
return true;
}
static void
virNetworkObjDispose(void *opaque)
{
virNetworkObjPtr obj = opaque;
virHashFree(obj->ports);
virNetworkDefFree(obj->def);
virNetworkDefFree(obj->newDef);
virBitmapFree(obj->classIdMap);
virObjectUnref(obj->macmap);
}
static void
virNetworkObjListDispose(void *opaque)
{
virNetworkObjListPtr nets = opaque;
virHashFree(nets->objs);
}
/*
* virNetworkObjUpdateAssignDef:
* @network: the network object to update
* @def: the new NetworkDef (will be consumed by this function)
* @live: is this new def the "live" version, or the "persistent" version
*
* Replace the appropriate copy of the given network's def or newDef
* with def. Use "live" and current state of the network to determine
* which to replace and what to do with the old defs. When a non-live
* def is set, indicate that the network is now persistent.
*
* NB: a persistent network can be made transient by calling with:
* virNetworkObjAssignDef(network, NULL, false) (i.e. set the
* persistent def to NULL)
*
*/
void
virNetworkObjUpdateAssignDef(virNetworkObjPtr obj,
virNetworkDefPtr def,
bool live)
{
if (live) {
/* before setting new live def, save (into newDef) any
* existing persistent (!live) def to be restored when the
* network is destroyed, unless there is one already saved.
*/
if (obj->persistent && !obj->newDef)
obj->newDef = obj->def;
else
virNetworkDefFree(obj->def);
obj->def = def;
} else { /* !live */
virNetworkDefFree(obj->newDef);
if (virNetworkObjIsActive(obj)) {
/* save new configuration to be restored on network
* shutdown, leaving current live def alone
*/
obj->newDef = def;
} else { /* !live and !active */
if (obj->def && !obj->persistent) {
/* network isn't (yet) marked active or persistent,
* but already has a "live" def set. This means we are
* currently setting the persistent def as a part of
* the process of starting the network, so we need to
* preserve the "not yet live" def in network->def.
*/
obj->newDef = def;
} else {
/* either there is no live def set, or this network
* was already set as persistent, so the proper thing
* is to overwrite network->def.
*/
obj->newDef = NULL;
virNetworkDefFree(obj->def);
obj->def = def;
}
}
obj->persistent = !!def;
}
}
/*
* If flags & VIR_NETWORK_OBJ_LIST_ADD_CHECK_LIVE then this will
* refuse updating an existing def if the current def is live
*
* If flags & VIR_NETWORK_OBJ_LIST_ADD_LIVE then the @def being
* added is assumed to represent a live config, not a future
* inactive config
*
* If flags is zero, network is considered as inactive and persistent.
*/
static virNetworkObjPtr
virNetworkObjAssignDefLocked(virNetworkObjListPtr nets,
virNetworkDefPtr def,
unsigned int flags)
{
virNetworkObjPtr obj;
virNetworkObjPtr ret = NULL;
char uuidstr[VIR_UUID_STRING_BUFLEN];
/* See if a network with matching UUID already exists */
if ((obj = virNetworkObjFindByUUIDLocked(nets, def->uuid))) {
virObjectLock(obj);
/* UUID matches, but if names don't match, refuse it */
if (STRNEQ(obj->def->name, def->name)) {
virUUIDFormat(obj->def->uuid, uuidstr);
virReportError(VIR_ERR_OPERATION_FAILED,
_("network '%s' is already defined with uuid %s"),
obj->def->name, uuidstr);
goto cleanup;
}
if (flags & VIR_NETWORK_OBJ_LIST_ADD_CHECK_LIVE) {
/* UUID & name match, but if network is already active, refuse it */
if (virNetworkObjIsActive(obj)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("network is already active as '%s'"),
obj->def->name);
goto cleanup;
}
}
virNetworkObjUpdateAssignDef(obj, def,
!!(flags & VIR_NETWORK_OBJ_LIST_ADD_LIVE));
} else {
/* UUID does not match, but if a name matches, refuse it */
if ((obj = virNetworkObjFindByNameLocked(nets, def->name))) {
virObjectLock(obj);
virUUIDFormat(obj->def->uuid, uuidstr);
virReportError(VIR_ERR_OPERATION_FAILED,
_("network '%s' already exists with uuid %s"),
def->name, uuidstr);
goto cleanup;
}
if (!(obj = virNetworkObjNew()))
goto cleanup;
virUUIDFormat(def->uuid, uuidstr);
if (virHashAddEntry(nets->objs, uuidstr, obj) < 0)
goto cleanup;
virObjectRef(obj);
obj->def = def;
obj->persistent = !(flags & VIR_NETWORK_OBJ_LIST_ADD_LIVE);
}
ret = g_steal_pointer(&obj);
cleanup:
virNetworkObjEndAPI(&obj);
return ret;
}
/*
* virNetworkObjAssignDef:
* @nets: list of all networks
* @def: the new NetworkDef (will be consumed by this function iff successful)
* @flags: bitwise-OR of VIR_NETWORK_OBJ_LIST_ADD_* flags
*
* Either replace the appropriate copy of the NetworkDef with name
* matching def->name or, if not found, create a new NetworkObj with
* def. For an existing network, use "live" and current state of the
* network to determine which to replace.
*
* Look at virNetworkObjAssignDefLocked() for @flags description.
*
* Returns NULL on error, virNetworkObjPtr on success.
*/
virNetworkObjPtr
virNetworkObjAssignDef(virNetworkObjListPtr nets,
virNetworkDefPtr def,
unsigned int flags)
{
virNetworkObjPtr obj;
virObjectRWLockWrite(nets);
obj = virNetworkObjAssignDefLocked(nets, def, flags);
virObjectRWUnlock(nets);
return obj;
}
/*
* virNetworkObjSetDefTransient:
* @network: network object pointer
* @live: if true, run this operation even for an inactive network.
* this allows freely updated network->def with runtime defaults
* before starting the network, which will be discarded on network
* shutdown. Any cleanup paths need to be sure to handle newDef if
* the network is never started.
*
* Mark the active network config as transient. Ensures live-only update
* operations do not persist past network destroy.
*
* Returns 0 on success, -1 on failure
*/
int
virNetworkObjSetDefTransient(virNetworkObjPtr obj,
bool live,
virNetworkXMLOptionPtr xmlopt)
{
if (!virNetworkObjIsActive(obj) && !live)
return 0;
if (!obj->persistent || obj->newDef)
return 0;
obj->newDef = virNetworkDefCopy(obj->def,
xmlopt,
VIR_NETWORK_XML_INACTIVE);
return obj->newDef ? 0 : -1;
}
/* virNetworkObjUnsetDefTransient:
*
* This *undoes* what virNetworkObjSetDefTransient did.
*/
void
virNetworkObjUnsetDefTransient(virNetworkObjPtr obj)
{
if (obj->newDef) {
virNetworkDefFree(obj->def);
obj->def = obj->newDef;
obj->newDef = NULL;
}
}
/*
* virNetworkObjGetPersistentDef:
* @network: network object pointer
*
* Return the persistent network configuration. If network is transient,
* return the running config.
*
* Returns NULL on error, virNetworkDefPtr on success.
*/
virNetworkDefPtr
virNetworkObjGetPersistentDef(virNetworkObjPtr obj)
{
if (obj->newDef)
return obj->newDef;
else
return obj->def;
}
/*
* virNetworkObjReplacePersistentDef:
* @network: network object pointer
* @def: new virNetworkDef to replace current persistent config
*
* Replace the "persistent" network configuration with the given new
* virNetworkDef. This pays attention to whether or not the network
* is active.
*
* Returns -1 on error, 0 on success
*/
int
virNetworkObjReplacePersistentDef(virNetworkObjPtr obj,
virNetworkDefPtr def)
{
if (virNetworkObjIsActive(obj)) {
virNetworkDefFree(obj->newDef);
obj->newDef = def;
} else {
virNetworkDefFree(obj->def);
obj->def = def;
}
return 0;
}
/*
* virNetworkObjConfigChangeSetup:
*
* 1) checks whether network state is consistent with the requested
* type of modification.
*
* 3) make sure there are separate "def" and "newDef" copies of
* networkDef if appropriate.
*
* Returns 0 on success, -1 on error.
*/
static int
virNetworkObjConfigChangeSetup(virNetworkObjPtr obj,
virNetworkXMLOptionPtr xmlopt,
unsigned int flags)
{
bool isActive;
isActive = virNetworkObjIsActive(obj);
if (!isActive && (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("network is not running"));
return -1;
}
if (flags & VIR_NETWORK_UPDATE_AFFECT_CONFIG) {
if (!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))
return -1;
}
return 0;
}
void
virNetworkObjRemoveInactive(virNetworkObjListPtr nets,
virNetworkObjPtr obj)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(obj->def->uuid, uuidstr);
virObjectRef(obj);
virObjectUnlock(obj);
virObjectRWLockWrite(nets);
virObjectLock(obj);
virHashRemoveEntry(nets->objs, uuidstr);
virObjectRWUnlock(nets);
virObjectUnref(obj);
}
static char *
virNetworkObjFormat(virNetworkObjPtr obj,
virNetworkXMLOptionPtr xmlopt,
unsigned int flags)
{
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
char *classIdStr = virBitmapFormat(obj->classIdMap);
size_t i;
if (!classIdStr)
return NULL;
virBufferAddLit(&buf, "<networkstatus>\n");
virBufferAdjustIndent(&buf, 2);
virBufferAsprintf(&buf, "<class_id bitmap='%s'/>\n", classIdStr);
virBufferAsprintf(&buf, "<floor sum='%llu'/>\n", obj->floor_sum);
VIR_FREE(classIdStr);
for (i = 0; i < VIR_NETWORK_TAINT_LAST; i++) {
if (obj->taint & (1 << i))
virBufferAsprintf(&buf, "<taint flag='%s'/>\n",
virNetworkTaintTypeToString(i));
}
if (virNetworkDefFormatBuf(&buf, obj->def, xmlopt, flags) < 0)
return NULL;
virBufferAdjustIndent(&buf, -2);
virBufferAddLit(&buf, "</networkstatus>");
return virBufferContentAndReset(&buf);
}
int
virNetworkObjSaveStatus(const char *statusDir,
virNetworkObjPtr obj,
virNetworkXMLOptionPtr xmlopt)
{
int ret = -1;
int flags = 0;
char *xml;
if (!(xml = virNetworkObjFormat(obj, xmlopt, flags)))
goto cleanup;
if (virNetworkSaveXML(statusDir, obj->def, xml))
goto cleanup;
ret = 0;
cleanup:
VIR_FREE(xml);
return ret;
}
static virNetworkObjPtr
virNetworkLoadState(virNetworkObjListPtr nets,
const char *stateDir,
const char *name,
virNetworkXMLOptionPtr xmlopt)
{
char *configFile = NULL;
virNetworkDefPtr def = NULL;
virNetworkObjPtr obj = NULL;
xmlDocPtr xml = NULL;
xmlNodePtr node = NULL, *nodes = NULL;
xmlXPathContextPtr ctxt = NULL;
virBitmapPtr classIdMap = NULL;
unsigned long long floor_sum_val = 0;
unsigned int taint = 0;
int n;
size_t i;
if ((configFile = virNetworkConfigFile(stateDir, name)) == NULL)
goto error;
if (!(xml = virXMLParseCtxt(configFile, NULL, _("(network status)"), &ctxt)))
goto error;
if (!(node = virXPathNode("//network", ctxt))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Could not find any 'network' element in status file"));
goto error;
}
/* parse the definition first */
ctxt->node = node;
if (!(def = virNetworkDefParseXML(ctxt, xmlopt)))
goto error;
if (STRNEQ(name, def->name)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Network config filename '%s'"
" does not match network name '%s'"),
configFile, def->name);
goto error;
}
/* now parse possible status data */
node = xmlDocGetRootElement(xml);
if (virXMLNodeNameEqual(node, "networkstatus")) {
/* Newer network status file. Contains useful
* info which are not to be found in bare config XML */
char *classIdStr = NULL;
char *floor_sum = NULL;
ctxt->node = node;
if ((classIdStr = virXPathString("string(./class_id[1]/@bitmap)",
ctxt))) {
if (!(classIdMap = virBitmapParseUnlimited(classIdStr))) {
VIR_FREE(classIdStr);
goto error;
}
}
VIR_FREE(classIdStr);
floor_sum = virXPathString("string(./floor[1]/@sum)", ctxt);
if (floor_sum &&
virStrToLong_ull(floor_sum, NULL, 10, &floor_sum_val) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Malformed 'floor_sum' attribute: %s"),
floor_sum);
VIR_FREE(floor_sum);
goto error;
}
VIR_FREE(floor_sum);
if ((n = virXPathNodeSet("./taint", ctxt, &nodes)) < 0)
goto error;
for (i = 0; i < n; i++) {
char *str = virXMLPropString(nodes[i], "flag");
if (str) {
int flag = virNetworkTaintTypeFromString(str);
if (flag < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Unknown taint flag %s"), str);
VIR_FREE(str);
goto error;
}
VIR_FREE(str);
/* Compute taint mask here. The network object does not
* exist yet, so we can't use virNetworkObjtTaint. */
taint |= (1 << flag);
}
}
VIR_FREE(nodes);
}
/* create the object */
if (!(obj = virNetworkObjAssignDef(nets, def,
VIR_NETWORK_OBJ_LIST_ADD_LIVE)))
goto error;
/* do not put any "goto error" below this comment */
/* assign status data stored in the network object */
if (classIdMap) {
virBitmapFree(obj->classIdMap);
obj->classIdMap = classIdMap;
}
if (floor_sum_val > 0)
obj->floor_sum = floor_sum_val;
obj->taint = taint;
obj->active = true; /* network with a state file is by definition active */
cleanup:
VIR_FREE(configFile);
xmlFreeDoc(xml);
xmlXPathFreeContext(ctxt);
return obj;
error:
VIR_FREE(nodes);
virBitmapFree(classIdMap);
virNetworkDefFree(def);
goto cleanup;
}
static virNetworkObjPtr
virNetworkLoadConfig(virNetworkObjListPtr nets,
const char *configDir,
const char *autostartDir,
const char *name,
virNetworkXMLOptionPtr xmlopt)
{
char *configFile = NULL, *autostartLink = NULL;
virNetworkDefPtr def = NULL;
virNetworkObjPtr obj;
bool saveConfig = false;
int autostart;
if ((configFile = virNetworkConfigFile(configDir, name)) == NULL)
goto error;
if ((autostartLink = virNetworkConfigFile(autostartDir, name)) == NULL)
goto error;
if ((autostart = virFileLinkPointsTo(autostartLink, configFile)) < 0)
goto error;
if (!(def = virNetworkDefParseFile(configFile, xmlopt)))
goto error;
if (STRNEQ(name, def->name)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Network config filename '%s'"
" does not match network name '%s'"),
configFile, def->name);
goto error;
}
switch ((virNetworkForwardType) def->forward.type) {
case VIR_NETWORK_FORWARD_NONE:
case VIR_NETWORK_FORWARD_NAT:
case VIR_NETWORK_FORWARD_ROUTE:
case VIR_NETWORK_FORWARD_OPEN:
if (!def->mac_specified) {
virNetworkSetBridgeMacAddr(def);
/* We just generated a new MAC address, and we need to persist
* the configuration to disk to avoid the network getting a
* different one the next time the daemon is started */
saveConfig = true;
}
break;
case VIR_NETWORK_FORWARD_BRIDGE:
case VIR_NETWORK_FORWARD_PRIVATE:
case VIR_NETWORK_FORWARD_VEPA:
case VIR_NETWORK_FORWARD_PASSTHROUGH:
case VIR_NETWORK_FORWARD_HOSTDEV:
/* Throw away MAC address for other forward types,
* which could have been generated by older libvirt RPMs */
def->mac_specified = false;
break;
case VIR_NETWORK_FORWARD_LAST:
default:
virReportEnumRangeError(virNetworkForwardType, def->forward.type);
goto error;
}
/* The network didn't have a UUID so we generated a new one, and
* we need to persist the configuration to disk to avoid the network
* getting a different one the next time the daemon is started */
if (!def->uuid_specified)
saveConfig = true;
if (saveConfig &&
virNetworkSaveConfig(configDir, def, xmlopt) < 0) {
goto error;
}
if (!(obj = virNetworkObjAssignDef(nets, def, 0)))
goto error;
obj->autostart = (autostart == 1);
VIR_FREE(configFile);
VIR_FREE(autostartLink);
return obj;
error:
VIR_FREE(configFile);
VIR_FREE(autostartLink);
virNetworkDefFree(def);
return NULL;
}
int
virNetworkObjLoadAllState(virNetworkObjListPtr nets,
const char *stateDir,
virNetworkXMLOptionPtr xmlopt)
{
g_autoptr(DIR) dir = NULL;
struct dirent *entry;
int ret = -1;
int rc;
if ((rc = virDirOpenIfExists(&dir, stateDir)) <= 0)
return rc;
while ((ret = virDirRead(dir, &entry, stateDir)) > 0) {
virNetworkObjPtr obj;
if (!virStringStripSuffix(entry->d_name, ".xml"))
continue;
obj = virNetworkLoadState(nets, stateDir, entry->d_name, xmlopt);
if (obj &&
virNetworkObjLoadAllPorts(obj, stateDir) < 0) {
virNetworkObjEndAPI(&obj);
return -1;
}
virNetworkObjEndAPI(&obj);
}
return ret;
}
int
virNetworkObjLoadAllConfigs(virNetworkObjListPtr nets,
const char *configDir,
const char *autostartDir,
virNetworkXMLOptionPtr xmlopt)
{
g_autoptr(DIR) dir = NULL;
struct dirent *entry;
int ret = -1;
int rc;
if ((rc = virDirOpenIfExists(&dir, configDir)) <= 0)
return rc;
while ((ret = virDirRead(dir, &entry, configDir)) > 0) {
virNetworkObjPtr obj;
if (!virStringStripSuffix(entry->d_name, ".xml"))
continue;
/* NB: ignoring errors, so one malformed config doesn't
kill the whole process */
obj = virNetworkLoadConfig(nets,
configDir,
autostartDir,
entry->d_name,
xmlopt);
virNetworkObjEndAPI(&obj);
}
return ret;
}
int
virNetworkObjDeleteConfig(const char *configDir,
const char *autostartDir,
virNetworkObjPtr obj)
{
char *configFile = NULL;
char *autostartLink = NULL;
int ret = -1;
if (!(configFile = virNetworkConfigFile(configDir, obj->def->name)))
goto error;
if (!(autostartLink = virNetworkConfigFile(autostartDir, obj->def->name)))
goto error;
/* Not fatal if this doesn't work */
unlink(autostartLink);
obj->autostart = false;
if (unlink(configFile) < 0) {
virReportSystemError(errno,
_("cannot remove config file '%s'"),
configFile);
goto error;
}
ret = 0;
error:
VIR_FREE(configFile);
VIR_FREE(autostartLink);
return ret;
}
struct virNetworkObjBridgeInUseHelperData {
const char *bridge;
const char *skipname;
};
static int
virNetworkObjBridgeInUseHelper(const void *payload,
const char *name G_GNUC_UNUSED,
const void *opaque)
{
int ret;
virNetworkObjPtr obj = (virNetworkObjPtr) payload;
const struct virNetworkObjBridgeInUseHelperData *data = opaque;
virObjectLock(obj);
if (data->skipname &&
((obj->def && STREQ(obj->def->name, data->skipname)) ||
(obj->newDef && STREQ(obj->newDef->name, data->skipname))))
ret = 0;
else if ((obj->def && obj->def->bridge &&
STREQ(obj->def->bridge, data->bridge)) ||
(obj->newDef && obj->newDef->bridge &&
STREQ(obj->newDef->bridge, data->bridge)))
ret = 1;
else
ret = 0;
virObjectUnlock(obj);
return ret;
}
bool
virNetworkObjBridgeInUse(virNetworkObjListPtr nets,
const char *bridge,
const char *skipname)
{
virNetworkObjPtr obj;
struct virNetworkObjBridgeInUseHelperData data = {bridge, skipname};
virObjectRWLockRead(nets);
obj = virHashSearch(nets->objs, virNetworkObjBridgeInUseHelper, &data, NULL);
virObjectRWUnlock(nets);
return obj != NULL;
}
/*
* virNetworkObjUpdate:
*
* Apply the supplied update to the given virNetworkObj. Except for
* @network pointing to an actual network object rather than the
* opaque virNetworkPtr, parameters are identical to the public API
* virNetworkUpdate.
*
* The original virNetworkDefs are copied, and all modifications made
* to these copies. The originals are replaced with the copies only
* after success has been guaranteed.
*
* Returns: -1 on error, 0 on success.
*/
int
virNetworkObjUpdate(virNetworkObjPtr obj,
unsigned int command, /* virNetworkUpdateCommand */
unsigned int section, /* virNetworkUpdateSection */
int parentIndex,
const char *xml,
virNetworkXMLOptionPtr xmlopt,
unsigned int flags) /* virNetworkUpdateFlags */
{
int ret = -1;
virNetworkDefPtr livedef = NULL, configdef = NULL;
/* normalize config data, and check for common invalid requests. */
if (virNetworkObjConfigChangeSetup(obj, xmlopt, flags) < 0)
goto cleanup;
if (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE) {
virNetworkDefPtr checkdef;
/* work on a copy of the def */
if (!(livedef = virNetworkDefCopy(obj->def, xmlopt, 0)))
goto cleanup;
if (virNetworkDefUpdateSection(livedef, command, section,
parentIndex, xml, flags) < 0) {
goto cleanup;
}
/* run a final format/parse cycle to make sure we didn't
* add anything illegal to the def
*/
if (!(checkdef = virNetworkDefCopy(livedef, xmlopt, 0)))
goto cleanup;
virNetworkDefFree(checkdef);
}
if (flags & VIR_NETWORK_UPDATE_AFFECT_CONFIG) {
virNetworkDefPtr checkdef;
/* work on a copy of the def */
if (!(configdef = virNetworkDefCopy(virNetworkObjGetPersistentDef(obj),
xmlopt,
VIR_NETWORK_XML_INACTIVE))) {
goto cleanup;
}
if (virNetworkDefUpdateSection(configdef, command, section,
parentIndex, xml, flags) < 0) {
goto cleanup;
}
if (!(checkdef = virNetworkDefCopy(configdef,
xmlopt,
VIR_NETWORK_XML_INACTIVE))) {
goto cleanup;
}
virNetworkDefFree(checkdef);
}
if (configdef) {
/* successfully modified copy, now replace original */
if (virNetworkObjReplacePersistentDef(obj, configdef) < 0)
goto cleanup;
configdef = NULL;
}
if (livedef) {
/* successfully modified copy, now replace original */
virNetworkDefFree(obj->def);
obj->def = livedef;
livedef = NULL;
}
ret = 0;
cleanup:
virNetworkDefFree(livedef);
virNetworkDefFree(configdef);
return ret;
}
#define MATCH(FLAG) (flags & (FLAG))
static bool
virNetworkObjMatch(virNetworkObjPtr obj,
unsigned int flags)
{
/* filter by active state */
if (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) &&
!((MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE) &&
virNetworkObjIsActive(obj)) ||
(MATCH(VIR_CONNECT_LIST_NETWORKS_INACTIVE) &&
!virNetworkObjIsActive(obj))))
return false;
/* filter by persistence */
if (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_PERSISTENT) &&
!((MATCH(VIR_CONNECT_LIST_NETWORKS_PERSISTENT) &&
obj->persistent) ||
(MATCH(VIR_CONNECT_LIST_NETWORKS_TRANSIENT) &&
!obj->persistent)))
return false;
/* filter by autostart option */
if (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_AUTOSTART) &&
!((MATCH(VIR_CONNECT_LIST_NETWORKS_AUTOSTART) &&
obj->autostart) ||
(MATCH(VIR_CONNECT_LIST_NETWORKS_NO_AUTOSTART) &&
!obj->autostart)))
return false;
return true;
}
#undef MATCH
typedef struct _virNetworkObjListExportData virNetworkObjListExportData;
typedef virNetworkObjListExportData *virNetworkObjListExportDataPtr;
struct _virNetworkObjListExportData {
virConnectPtr conn;
virNetworkPtr *nets;
virNetworkObjListFilter filter;
unsigned int flags;
int nnets;
bool error;
};
static int
virNetworkObjListExportCallback(void *payload,
const char *name G_GNUC_UNUSED,
void *opaque)
{
virNetworkObjListExportDataPtr data = opaque;
virNetworkObjPtr obj = payload;
virNetworkPtr net = NULL;
if (data->error)
return 0;
virObjectLock(obj);
if (data->filter &&
!data->filter(data->conn, obj->def))
goto cleanup;
if (!virNetworkObjMatch(obj, data->flags))
goto cleanup;
if (!data->nets) {
data->nnets++;
goto cleanup;
}
if (!(net = virGetNetwork(data->conn, obj->def->name, obj->def->uuid))) {
data->error = true;
goto cleanup;
}
data->nets[data->nnets++] = net;
cleanup:
virObjectUnlock(obj);
return 0;
}
int
virNetworkObjListExport(virConnectPtr conn,
virNetworkObjListPtr netobjs,
virNetworkPtr **nets,
virNetworkObjListFilter filter,
unsigned int flags)
{
int ret = -1;
virNetworkObjListExportData data = {
.conn = conn, .nets = NULL, .filter = filter, .flags = flags,
.nnets = 0, .error = false };
virObjectRWLockRead(netobjs);
if (nets)
data.nets = g_new0(virNetworkPtr, virHashSize(netobjs->objs) + 1);
virHashForEach(netobjs->objs, virNetworkObjListExportCallback, &data);
if (data.error)
goto cleanup;
if (data.nets) {
/* trim the array to the final size */
ignore_value(VIR_REALLOC_N(data.nets, data.nnets + 1));
*nets = data.nets;
data.nets = NULL;
}
ret = data.nnets;
cleanup:
virObjectRWUnlock(netobjs);
while (data.nets && data.nnets)
virObjectUnref(data.nets[--data.nnets]);
VIR_FREE(data.nets);
return ret;
}
struct virNetworkObjListForEachHelperData {
virNetworkObjListIterator callback;
void *opaque;
int ret;
};
static int
virNetworkObjListForEachHelper(void *payload,
const char *name G_GNUC_UNUSED,
void *opaque)
{
struct virNetworkObjListForEachHelperData *data = opaque;
if (data->callback(payload, data->opaque) < 0)
data->ret = -1;
return 0;
}
/**
* virNetworkObjListForEach:
* @nets: a list of network objects
* @callback: function to call over each of object in the list
* @opaque: pointer to pass to the @callback
*
* Function iterates over the list of network objects and calls
* passed callback over each one of them. You should avoid
* calling those virNetworkObjList APIs, which lock the list
* again in favor of their virNetworkObj*Locked variants.
*
* Returns: 0 on success, -1 otherwise.
*/
int
virNetworkObjListForEach(virNetworkObjListPtr nets,
virNetworkObjListIterator callback,
void *opaque)
{
struct virNetworkObjListForEachHelperData data = {
.callback = callback, .opaque = opaque, .ret = 0};
virObjectRWLockRead(nets);
virHashForEachSafe(nets->objs, virNetworkObjListForEachHelper, &data);
virObjectRWUnlock(nets);
return data.ret;
}
struct virNetworkObjListGetHelperData {
virConnectPtr conn;
virNetworkObjListFilter filter;
char **names;
int nnames;
int maxnames;
bool active;
bool error;
};
static int
virNetworkObjListGetHelper(void *payload,
const char *name G_GNUC_UNUSED,
void *opaque)
{
struct virNetworkObjListGetHelperData *data = opaque;
virNetworkObjPtr obj = payload;
if (data->error)
return 0;
if (data->maxnames >= 0 &&
data->nnames == data->maxnames)
return 0;
virObjectLock(obj);
if (data->filter &&
!data->filter(data->conn, obj->def))
goto cleanup;
if ((data->active && virNetworkObjIsActive(obj)) ||
(!data->active && !virNetworkObjIsActive(obj))) {
if (data->names)
data->names[data->nnames] = g_strdup(obj->def->name);
data->nnames++;
}
cleanup:
virObjectUnlock(obj);
return 0;
}
int
virNetworkObjListGetNames(virNetworkObjListPtr nets,
bool active,
char **names,
int maxnames,
virNetworkObjListFilter filter,
virConnectPtr conn)
{
int ret = -1;
struct virNetworkObjListGetHelperData data = {
.conn = conn, .filter = filter, .names = names, .nnames = 0,
.maxnames = maxnames, .active = active, .error = false};
virObjectRWLockRead(nets);
virHashForEach(nets->objs, virNetworkObjListGetHelper, &data);
virObjectRWUnlock(nets);
if (data.error)
goto cleanup;
ret = data.nnames;
cleanup:
if (ret < 0) {
while (data.nnames)
VIR_FREE(data.names[--data.nnames]);
}
return ret;
}
int
virNetworkObjListNumOfNetworks(virNetworkObjListPtr nets,
bool active,
virNetworkObjListFilter filter,
virConnectPtr conn)
{
struct virNetworkObjListGetHelperData data = {
.conn = conn, .filter = filter, .names = NULL, .nnames = 0,
.maxnames = -1, .active = active, .error = false};
virObjectRWLockRead(nets);
virHashForEach(nets->objs, virNetworkObjListGetHelper, &data);
virObjectRWUnlock(nets);
return data.nnames;
}
struct virNetworkObjListPruneHelperData {
unsigned int flags;
};
static int
virNetworkObjListPruneHelper(const void *payload,
const char *name G_GNUC_UNUSED,
const void *opaque)
{
const struct virNetworkObjListPruneHelperData *data = opaque;
virNetworkObjPtr obj = (virNetworkObjPtr) payload;
int want = 0;
virObjectLock(obj);
want = virNetworkObjMatch(obj, data->flags);
virObjectUnlock(obj);
return want;
}
/**
* virNetworkObjListPrune:
* @nets: a list of network objects
* @flags: bitwise-OR of virConnectListAllNetworksFlags
*
* Iterate over list of network objects and remove the desired
* ones from it.
*/
void
virNetworkObjListPrune(virNetworkObjListPtr nets,
unsigned int flags)
{
struct virNetworkObjListPruneHelperData data = {flags};
virObjectRWLockWrite(nets);
virHashRemoveSet(nets->objs, virNetworkObjListPruneHelper, &data);
virObjectRWUnlock(nets);
}
char *
virNetworkObjGetPortStatusDir(virNetworkObjPtr net,
const char *stateDir)
{
char *ret;
ret = g_strdup_printf("%s/%s/ports", stateDir, net->def->name);
return ret;
}
int
virNetworkObjAddPort(virNetworkObjPtr net,
virNetworkPortDefPtr portdef,
const char *stateDir)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
g_autofree char *dir = NULL;
virUUIDFormat(portdef->uuid, uuidstr);
if (virHashLookup(net->ports, uuidstr)) {
virReportError(VIR_ERR_NETWORK_PORT_EXIST,
_("Network port with UUID %s already exists"),
uuidstr);
return -1;
}
if (!(dir = virNetworkObjGetPortStatusDir(net, stateDir)))
return -1;
if (virHashAddEntry(net->ports, uuidstr, portdef) < 0)
return -1;
if (virNetworkPortDefSaveStatus(portdef, dir) < 0) {
virHashRemoveEntry(net->ports, uuidstr);
return -1;
}
return 0;
}
virNetworkPortDefPtr
virNetworkObjLookupPort(virNetworkObjPtr net,
const unsigned char *uuid)
{
virNetworkPortDefPtr ret = NULL;
char uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(uuid, uuidstr);
if (!(ret = virHashLookup(net->ports, uuidstr))) {
virReportError(VIR_ERR_NO_NETWORK_PORT,
_("Network port with UUID %s does not exist"),
uuidstr);
return NULL;
}
return ret;
}
int
virNetworkObjDeletePort(virNetworkObjPtr net,
const unsigned char *uuid,
const char *stateDir)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
g_autofree char *dir = NULL;
virNetworkPortDefPtr portdef;
virUUIDFormat(uuid, uuidstr);
if (!(portdef = virHashLookup(net->ports, uuidstr))) {
virReportError(VIR_ERR_NO_NETWORK_PORT,
_("Network port with UUID %s does not exist"),
uuidstr);
return -1;
}
if (!(dir = virNetworkObjGetPortStatusDir(net, stateDir)))
return -1;
if (virNetworkPortDefDeleteStatus(portdef, dir) < 0)
return -1;
if (virHashRemoveEntry(net->ports, uuidstr) < 0)
return -1;
return 0;
}
int
virNetworkObjDeleteAllPorts(virNetworkObjPtr net,
const char *stateDir)
{
g_autofree char *dir = NULL;
g_autoptr(DIR) dh = NULL;
struct dirent *de;
int rc;
if (!(dir = virNetworkObjGetPortStatusDir(net, stateDir)))
return -1;
if ((rc = virDirOpenIfExists(&dh, dir)) <= 0)
return rc;
while ((rc = virDirRead(dh, &de, dir)) > 0) {
char *file = NULL;
if (!virStringStripSuffix(de->d_name, ".xml"))
continue;
file = g_strdup_printf("%s/%s.xml", dir, de->d_name);
if (unlink(file) < 0 && errno != ENOENT)
VIR_WARN("Unable to delete %s", file);
VIR_FREE(file);
}
virHashRemoveAll(net->ports);
return 0;
}
typedef struct _virNetworkObjPortListExportData virNetworkObjPortListExportData;
typedef virNetworkObjPortListExportData *virNetworkObjPortListExportDataPtr;
struct _virNetworkObjPortListExportData {
virNetworkPtr net;
virNetworkDefPtr def;
virNetworkPortPtr *ports;
virNetworkPortListFilter filter;
int nports;
bool error;
};
static int
virNetworkObjPortListExportCallback(void *payload,
const char *name G_GNUC_UNUSED,
void *opaque)
{
virNetworkObjPortListExportDataPtr data = opaque;
virNetworkPortDefPtr def = payload;
virNetworkPortPtr port;
if (data->error)
return 0;
if (data->filter &&
!data->filter(data->net->conn, data->def, def))
return 0;
if (!data->ports) {
data->nports++;
return 0;
}
if (!(port = virGetNetworkPort(data->net, def->uuid))) {
data->error = true;
return 0;
}
data->ports[data->nports++] = port;
return 0;
}
int
virNetworkObjPortListExport(virNetworkPtr net,
virNetworkObjPtr obj,
virNetworkPortPtr **ports,
virNetworkPortListFilter filter)
{
virNetworkObjPortListExportData data = {
net, obj->def, NULL, filter, 0, false,
};
int ret = -1;
if (ports) {
*ports = NULL;
data.ports = g_new0(virNetworkPortPtr, virHashSize(obj->ports) + 1);
}
virHashForEach(obj->ports, virNetworkObjPortListExportCallback, &data);
if (data.error)
goto cleanup;
if (data.ports) {
/* trim the array to the final size */
ignore_value(VIR_REALLOC_N(data.ports, data.nports + 1));
*ports = data.ports;
data.ports = NULL;
}
ret = data.nports;
cleanup:
while (data.ports && data.nports)
virObjectUnref(data.ports[--data.nports]);
VIR_FREE(data.ports);
return ret;
}
typedef struct _virNetworkObjPortListForEachData virNetworkObjPortListForEachData;
struct _virNetworkObjPortListForEachData {
virNetworkPortListIter iter;
void *opaque;
bool err;
};
static int
virNetworkObjPortForEachCallback(void *payload,
const char *name G_GNUC_UNUSED,
void *opaque)
{
virNetworkObjPortListForEachData *data = opaque;
if (!data->iter(payload, data->opaque))
data->err = true;
return 0;
}
int
virNetworkObjPortForEach(virNetworkObjPtr obj,
virNetworkPortListIter iter,
void *opaque)
{
virNetworkObjPortListForEachData data = { iter, opaque, false };
virHashForEachSafe(obj->ports, virNetworkObjPortForEachCallback, &data);
if (data.err)
return -1;
return 0;
}
static int
virNetworkObjLoadAllPorts(virNetworkObjPtr net,
const char *stateDir)
{
g_autofree char *dir = NULL;
g_autoptr(DIR) dh = NULL;
struct dirent *de;
int rc;
char uuidstr[VIR_UUID_STRING_BUFLEN];
g_autoptr(virNetworkPortDef) portdef = NULL;
if (!(dir = virNetworkObjGetPortStatusDir(net, stateDir)))
return -1;
if ((rc = virDirOpenIfExists(&dh, dir)) <= 0)
return rc;
while ((rc = virDirRead(dh, &de, dir)) > 0) {
g_autofree char *file = NULL;
if (!virStringStripSuffix(de->d_name, ".xml"))
continue;
file = g_strdup_printf("%s/%s.xml", dir, de->d_name);
portdef = virNetworkPortDefParseFile(file);
if (!portdef) {
VIR_WARN("Cannot parse port %s", file);
continue;
}
virUUIDFormat(portdef->uuid, uuidstr);
if (virHashAddEntry(net->ports, uuidstr, portdef) < 0)
return -1;
portdef = NULL;
}
return 0;
}