[libvirt] [PATCH v2] storage: Check for invalid storage mode before opening
by Cole Robinson
If a directory pool contains pipes or sockets, a pool start can fail or hang:
https://bugzilla.redhat.com/show_bug.cgi?id=589577
We already try to avoid these special files, but only attempt after
opening the path, which is where the problems lie. Unify volume opening
into a single function which runs stat() before any open() call. Directory
pools can then proceed along, ignoring the invalid files.
v2: Use ATTRIBUTE_NONNULL. Drop stat check, just open with
O_NONBLOCK|O_NOCTTY.
Signed-off-by: Cole Robinson <crobinso(a)redhat.com>
---
src/storage/storage_backend.c | 23 +++++++++++++++++------
src/storage/storage_backend.h | 2 ++
src/storage/storage_backend_fs.c | 7 ++-----
src/storage/storage_backend_logical.c | 9 +++------
src/storage/storage_backend_mpath.c | 9 +++------
src/storage/storage_backend_scsi.c | 17 ++++++++---------
6 files changed, 35 insertions(+), 32 deletions(-)
diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c
index f4124df..bbd3375 100644
--- a/src/storage/storage_backend.c
+++ b/src/storage/storage_backend.c
@@ -872,6 +872,20 @@ virStorageBackendForType(int type) {
return NULL;
}
+int
+virStorageBackendVolOpen(const char *path)
+{
+ int fd;
+
+ if ((fd = open(path, O_RDONLY|O_NONBLOCK|O_NOCTTY)) < 0) {
+ virReportSystemError(errno,
+ _("cannot open volume '%s'"),
+ path);
+ return -1;
+ }
+
+ return fd;
+}
int
virStorageBackendUpdateVolTargetInfo(virStorageVolTargetPtr target,
@@ -880,13 +894,10 @@ virStorageBackendUpdateVolTargetInfo(virStorageVolTargetPtr target,
{
int ret, fd;
- if ((fd = open(target->path, O_RDONLY)) < 0) {
- virReportSystemError(errno,
- _("cannot open volume '%s'"),
- target->path);
- return -1;
- }
+ if ((ret = virStorageBackendVolOpen(target->path)) < 0)
+ return ret;
+ fd = ret;
ret = virStorageBackendUpdateVolTargetInfoFD(target,
fd,
allocation,
diff --git a/src/storage/storage_backend.h b/src/storage/storage_backend.h
index 907c4bc..45683ec 100644
--- a/src/storage/storage_backend.h
+++ b/src/storage/storage_backend.h
@@ -80,6 +80,8 @@ struct _virStorageBackend {
virStorageBackendPtr virStorageBackendForType(int type);
+int virStorageBackendVolOpen(const char *path)
+ATTRIBUTE_NONNULL(1);
int virStorageBackendUpdateVolInfo(virStorageVolDefPtr vol,
int withCapacity);
diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c
index c96c4f3..1650177 100644
--- a/src/storage/storage_backend_fs.c
+++ b/src/storage/storage_backend_fs.c
@@ -61,12 +61,9 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target,
if (encryption)
*encryption = NULL;
- if ((fd = open(target->path, O_RDONLY)) < 0) {
- virReportSystemError(errno,
- _("cannot open volume '%s'"),
- target->path);
+ if ((ret = virStorageBackendVolOpen(target->path)) < 0)
return -1;
- }
+ fd = ret;
if ((ret = virStorageBackendUpdateVolTargetInfoFD(target, fd,
allocation,
diff --git a/src/storage/storage_backend_logical.c b/src/storage/storage_backend_logical.c
index 06238d1..616ca1a 100644
--- a/src/storage/storage_backend_logical.c
+++ b/src/storage/storage_backend_logical.c
@@ -559,7 +559,7 @@ virStorageBackendLogicalCreateVol(virConnectPtr conn,
virStoragePoolObjPtr pool,
virStorageVolDefPtr vol)
{
- int fd = -1;
+ int fdret, fd = -1;
char size[100];
const char *cmdargvnew[] = {
LVCREATE, "--name", vol->name, "-L", size,
@@ -602,12 +602,9 @@ virStorageBackendLogicalCreateVol(virConnectPtr conn,
if (virRun(cmdargv, NULL) < 0)
return -1;
- if ((fd = open(vol->target.path, O_RDONLY)) < 0) {
- virReportSystemError(errno,
- _("cannot read path '%s'"),
- vol->target.path);
+ if ((fdret = virStorageBackendVolOpen(vol->target.path)) < 0)
goto cleanup;
- }
+ fd = fdret;
/* We can only chown/grp if root */
if (getuid() == 0) {
diff --git a/src/storage/storage_backend_mpath.c b/src/storage/storage_backend_mpath.c
index 8d0a92a..a62dc3d 100644
--- a/src/storage/storage_backend_mpath.c
+++ b/src/storage/storage_backend_mpath.c
@@ -44,14 +44,11 @@ virStorageBackendMpathUpdateVolTargetInfo(virStorageVolTargetPtr target,
unsigned long long *capacity)
{
int ret = -1;
- int fd = -1;
+ int fdret, fd = -1;
- if ((fd = open(target->path, O_RDONLY)) < 0) {
- virReportSystemError(errno,
- _("cannot open volume '%s'"),
- target->path);
+ if ((fdret = virStorageBackendVolOpen(target->path)) < 0)
goto out;
- }
+ fd = fdret;
if (virStorageBackendUpdateVolTargetInfoFD(target,
fd,
diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c
index 40f4fd8..71492cf 100644
--- a/src/storage/storage_backend_scsi.c
+++ b/src/storage/storage_backend_scsi.c
@@ -135,14 +135,12 @@ virStorageBackendSCSIUpdateVolTargetInfo(virStorageVolTargetPtr target,
unsigned long long *allocation,
unsigned long long *capacity)
{
- int fd, ret = -1;
+ int fdret, fd = -1;
+ int ret = -1;
- if ((fd = open(target->path, O_RDONLY)) < 0) {
- virReportSystemError(errno,
- _("cannot open volume '%s'"),
- target->path);
- return -1;
- }
+ if ((fdret = virStorageBackendVolOpen(target->path)) < 0)
+ goto cleanup;
+ fd = fdret;
if (virStorageBackendUpdateVolTargetInfoFD(target,
fd,
@@ -155,8 +153,9 @@ virStorageBackendSCSIUpdateVolTargetInfo(virStorageVolTargetPtr target,
ret = 0;
- cleanup:
- close(fd);
+cleanup:
+ if (fd >= 0)
+ close(fd);
return ret;
}
--
1.6.6.1
14 years, 7 months
[libvirt] [v3 PATCH] add 802.1Qbh handling for port-profiles based on Stefan's previous patches
by Scott Feldman
From: Scott Feldman <scofeldm(a)cisco.com>
This patch builds on the work recently posted by Stefan Berger. It builds
on top of Stefan's two posted patches:
[PATCH v8] vepa: parsing for 802.1Qb{g|h} XML
[RFC][PATCH 1/3] vepa+vsi: Introduce dependency on libnl
[PATCH v3] Add host UUID (to libvirt capabilities)
Stefan's RFC patches 2/3 and 3/3 are incorporated into my patch.
Changes from v2 to v3:
- remove debug fprintfs
- use virGetHostUUID (thanks Stefan!)
- fix compile issue when latest if_link.h isn't available
- change poll timeout to 10s, at 1/8 intervals
- if polling times out, log msg and return -ETIMEDOUT
Changes from v1 to v2:
- Add Stefan's code for getPortProfileStatus
- Poll for up to 2 secs for port-profile status, at 1/8 sec intervals:
- if status indicates error, abort openMacvtapTap
- if status indicates success, exit polling
- if status is "in-progress" after 2 secs of polling, exit
polling loop silently, without error
My patch finishes out the 802.1Qbh parts, which Stefan had mostly complete.
I've tested using the recent kernel updates for VF_PORT netlink msgs and
enic for Cisco's 10G Ethernet NIC. I tested many VMs, each with several
direct interfaces, each configured with a port-profile per the XML. VM-to-VM,
and VM-to-external work as expected. VM-to-VM on same host (using same NIC)
works same as VM-to-VM where VMs are on diff hosts. I'm able to change
settings on the port-profile while the VM is running to change the virtual
port behaviour. For example, adjusting a QoS setting like rate limit. All
VMs with interfaces using that port-profile immediatly see the effect of the
change to the port-profile.
I don't have a SR-IOV device to test so source dev is a non-SR-IOV device,
but most of the code paths include support for specifing the source dev and
VF index. We'll need to complete this by discovering the PF given the VF
linkdev. Once we have the PF, we'll also have the VF index. All this info-
mation is available from sysfs.
Signed-off-by: Scott Feldman <scofeldm(a)cisco.com>
---
configure.ac | 16 +
src/util/macvtap.c | 802 +++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 805 insertions(+), 13 deletions(-)
diff --git a/configure.ac b/configure.ac
index 36ba703..885b0ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2005,13 +2005,26 @@ if test "$with_macvtap" != "no" ; then
fi
AM_CONDITIONAL([WITH_MACVTAP], [test "$with_macvtap" = "yes"])
+AC_TRY_COMPILE([ #include <sys/socket.h>
+ #include <linux/rtnetlink.h> ],
+ [ int x = IFLA_PORT_MAX; ],
+ [ with_virtualport=yes ],
+ [ with_virtualport=no ])
+if test "$with_virtualport" = "yes"; then
+ val=1
+else
+ val=0
+fi
+AC_DEFINE_UNQUOTED([WITH_VIRTUALPORT], $val, [whether vsi vepa support is enabled])
+AM_CONDITIONAL([WITH_VIRTUALPORT], [test "$with_virtualport" = "yes"])
+
dnl netlink library
LIBNL_CFLAGS=""
LIBNL_LIBS=""
-if test "$with_macvtap" = "yes"; then
+if test "$with_macvtap" = "yes" || "$with_virtualport" = "yes"; then
PKG_CHECK_MODULES([LIBNL], [libnl-1 >= $LIBNL_REQUIRED], [
], [
AC_MSG_ERROR([libnl >= $LIBNL_REQUIRED is required for macvtap support])
@@ -2084,6 +2097,7 @@ AC_MSG_NOTICE([ Network: $with_network])
AC_MSG_NOTICE([Libvirtd: $with_libvirtd])
AC_MSG_NOTICE([ netcf: $with_netcf])
AC_MSG_NOTICE([ macvtap: $with_macvtap])
+AC_MSG_NOTICE([virtport: $with_virtualport])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Storage Drivers])
AC_MSG_NOTICE([])
diff --git a/src/util/macvtap.c b/src/util/macvtap.c
index da81447..76eea37 100644
--- a/src/util/macvtap.c
+++ b/src/util/macvtap.c
@@ -27,7 +27,7 @@
#include <config.h>
-#if WITH_MACVTAP
+#if WITH_MACVTAP || WITH_VIRTUALPORT
# include <stdio.h>
# include <errno.h>
@@ -41,6 +41,8 @@
# include <linux/rtnetlink.h>
# include <linux/if_tun.h>
+# include <netlink/msg.h>
+
# include "util.h"
# include "memory.h"
# include "logging.h"
@@ -48,6 +50,7 @@
# include "interface.h"
# include "conf/domain_conf.h"
# include "virterror_internal.h"
+# include "uuid.h"
# define VIR_FROM_THIS VIR_FROM_NET
@@ -58,15 +61,23 @@
# define MACVTAP_NAME_PREFIX "macvtap"
# define MACVTAP_NAME_PATTERN "macvtap%d"
+# define MICROSEC_PER_SEC (1000 * 1000)
+
static int associatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort,
- int vf,
const unsigned char *vmuuid);
static int disassociatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort);
+enum virVirtualPortOp {
+ ASSOCIATE = 0x1,
+ DISASSOCIATE = 0x2,
+};
+
static int nlOpen(void)
{
@@ -159,6 +170,156 @@ err_exit:
}
+# ifdef IFLA_VF_PORT_MAX
+
+/**
+ * nlCommWaitSuccess:
+ *
+ * @nlmsg: pointer to netlink message
+ * @nl_grousp: the netlink multicast groups to send to
+ * @respbuf: pointer to pointer where response buffer will be allocated
+ * @respbuflen: pointer to integer holding the size of the response buffer
+ * on return of the function.
+ * @to_usecs: timeout in microseconds to wait for a success message
+ * to be returned
+ *
+ * Send the given message to the netlink multicast group and receive
+ * responses. Skip responses indicating an error and keep on receiving
+ * responses until a success response is returned.
+ * Returns 0 on success, -1 on error. In case of error, no response
+ * buffer will be returned.
+ */
+static int
+nlCommWaitSuccess(struct nlmsghdr *nlmsg, int nl_groups,
+ char **respbuf, int *respbuflen, long to_usecs)
+{
+ int rc = 0;
+ struct sockaddr_nl nladdr = {
+ .nl_family = AF_NETLINK,
+ .nl_pid = getpid(),
+ .nl_groups = nl_groups,
+ };
+ int rcvChunkSize = 1024; // expecting less than that
+ int rcvoffset = 0;
+ ssize_t nbytes;
+ int n;
+ struct timeval tv = {
+ .tv_sec = to_usecs / MICROSEC_PER_SEC,
+ .tv_usec = to_usecs % MICROSEC_PER_SEC,
+ };
+ fd_set rfds;
+ bool gotvalid = false;
+ int fd = nlOpen();
+ static uint32_t seq = 0x1234;
+ uint32_t myseq = seq++;
+ uint32_t mypid = getpid();
+
+ if (fd < 0)
+ return -1;
+
+ nlmsg->nlmsg_pid = mypid;
+ nlmsg->nlmsg_seq = myseq;
+ nlmsg->nlmsg_flags |= NLM_F_ACK;
+
+ nbytes = sendto(fd, (void *)nlmsg, nlmsg->nlmsg_len, 0,
+ (struct sockaddr *)&nladdr, sizeof(nladdr));
+ if (nbytes < 0) {
+ virReportSystemError(errno,
+ "%s", _("cannot send to netlink socket"));
+ rc = -1;
+ goto err_exit;
+ }
+
+ while (!gotvalid) {
+ rcvoffset = 0;
+ while (1) {
+ socklen_t addrlen = sizeof(nladdr);
+
+ if (VIR_REALLOC_N(*respbuf, rcvoffset+rcvChunkSize) < 0) {
+ virReportOOMError();
+ rc = -1;
+ goto err_exit;
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ n = select(fd + 1, &rfds, NULL, NULL, &tv);
+ if (n == 0) {
+ rc = -1;
+ goto err_exit;
+ }
+
+ nbytes = recvfrom(fd, &((*respbuf)[rcvoffset]), rcvChunkSize, 0,
+ (struct sockaddr *)&nladdr, &addrlen);
+ if (nbytes < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ virReportSystemError(errno, "%s",
+ _("error receiving from netlink socket"));
+ rc = -1;
+ goto err_exit;
+ }
+ rcvoffset += nbytes;
+ break;
+ }
+ *respbuflen = rcvoffset;
+
+ /* check message for error */
+ if (*respbuflen > NLMSG_LENGTH(0) && *respbuf != NULL) {
+ struct nlmsghdr *resp = (struct nlmsghdr *)*respbuf;
+ struct nlmsgerr *err;
+
+ if (resp->nlmsg_pid != mypid ||
+ resp->nlmsg_seq != myseq)
+ continue;
+
+ /* skip reflected message */
+ if (resp->nlmsg_type & 0x10)
+ continue;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len >= NLMSG_LENGTH(sizeof(*err))) {
+ if (-err->error != EOPNOTSUPP) {
+ /* assuming error msg from daemon */
+ gotvalid = true;
+ break;
+ }
+ }
+ /* whatever this is, skip it */
+ VIR_FREE(*respbuf);
+ *respbuf = NULL;
+ *respbuflen = 0;
+ break;
+
+ case NLMSG_DONE:
+ gotvalid = true;
+ break;
+
+ default:
+ VIR_FREE(*respbuf);
+ *respbuf = NULL;
+ *respbuflen = 0;
+ break;
+ }
+ }
+ }
+
+err_exit:
+ if (rc == -1) {
+ VIR_FREE(*respbuf);
+ *respbuf = NULL;
+ *respbuflen = 0;
+ }
+
+ nlClose(fd);
+ return rc;
+}
+
+#endif
+
static struct rtattr *
rtattrCreate(char *buffer, int bufsize, int type,
const void *data, int datalen)
@@ -204,6 +365,8 @@ nlAppend(struct nlmsghdr *nlm, int totlen, const void *data, int datalen)
}
+# if WITH_MACVTAP
+
static int
link_add(const char *type,
const unsigned char *macaddress, int macaddrsize,
@@ -651,8 +814,8 @@ create_name:
}
if (associatePortProfileId(cr_ifname,
+ net->data.direct.linkdev,
&net->data.direct.virtPortProfile,
- -1,
vmuuid) != 0) {
rc = -1;
goto link_del_exit;
@@ -685,6 +848,7 @@ create_name:
disassociate_exit:
disassociatePortProfileId(cr_ifname,
+ net->data.direct.linkdev,
&net->data.direct.virtPortProfile);
link_del_exit:
@@ -707,21 +871,606 @@ delMacvtap(virDomainNetDefPtr net)
{
if (net->ifname) {
disassociatePortProfileId(net->ifname,
+ net->data.direct.linkdev,
&net->data.direct.virtPortProfile);
link_del(net->ifname);
}
}
-#endif
+# endif
+
+
+# ifdef IFLA_PORT_MAX
+
+static struct nla_policy ifla_policy[IFLA_MAX + 1] =
+{
+ [IFLA_VF_PORTS] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_vf_ports_policy[IFLA_VF_PORT_MAX + 1] =
+{
+ [IFLA_VF_PORT] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_port_policy[IFLA_PORT_MAX + 1] =
+{
+ [IFLA_PORT_RESPONSE] = { .type = NLA_U16 },
+};
+
+
+static int
+link_dump(int ifindex, const char *ifname, struct nlattr **tb,
+ char **recvbuf)
+{
+ int rc = 0;
+ char nlmsgbuf[256] = { 0, };
+ struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+ struct nlmsgerr *err;
+ char rtattbuf[64];
+ struct rtattr *rta;
+ struct ifinfomsg i = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = ifindex
+ };
+ int recvbuflen;
+
+ *recvbuf = NULL;
+
+ nlInit(nlm, NLM_F_REQUEST, RTM_GETLINK);
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &i, sizeof(i)))
+ goto buffer_too_small;
+
+ if (ifindex < 0 && ifname != NULL) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_IFNAME,
+ ifname, strlen(ifname) + 1);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (nlComm(nlm, recvbuf, &recvbuflen) < 0)
+ return -1;
+
+ if (recvbuflen < NLMSG_LENGTH(0) || *recvbuf == NULL)
+ goto malformed_resp;
+
+ resp = (struct nlmsghdr *)*recvbuf;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+
+ switch (-err->error) {
+ case 0:
+ break;
+
+ default:
+ virReportSystemError(-err->error,
+ _("error dumping %d interface"),
+ ifindex);
+ rc = -1;
+ }
+ break;
+
+ case GENL_ID_CTRL:
+ case NLMSG_DONE:
+ if (nlmsg_parse(resp, sizeof(struct ifinfomsg),
+ tb, IFLA_MAX, ifla_policy)) {
+ goto malformed_resp;
+ }
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ if (rc != 0)
+ VIR_FREE(*recvbuf);
+
+ return rc;
+
+malformed_resp:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(*recvbuf);
+ return -1;
+
+buffer_too_small:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("internal buffer is too small"));
+ return -1;
+}
+
+
+static int
+getPortProfileStatus(struct nlattr **tb, int32_t vf, uint16_t *status)
+{
+ int rc = 1;
+ const char *msg = NULL;
+ struct nlattr *tb2[IFLA_VF_PORT_MAX + 1],
+ *tb3[IFLA_PORT_MAX+1];
+
+ if (vf == PORT_SELF_VF) {
+ if (tb[IFLA_PORT_SELF]) {
+ if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb[IFLA_PORT_SELF],
+ ifla_port_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORT part");
+ goto err_exit;
+ }
+ }
+ } else {
+ if (tb[IFLA_VF_PORTS]) {
+ if (nla_parse_nested(tb2, IFLA_VF_PORT_MAX, tb[IFLA_VF_PORTS],
+ ifla_vf_ports_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORTS part");
+ goto err_exit;
+ }
+ if (tb2[IFLA_VF_PORT]) {
+ if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb2[IFLA_VF_PORT],
+ ifla_port_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORT part");
+ goto err_exit;
+ }
+ }
+ }
+ }
+
+ if (tb3[IFLA_PORT_RESPONSE]) {
+ *status = *(uint16_t *)RTA_DATA(tb3[IFLA_PORT_RESPONSE]);
+ rc = 0;
+ } else {
+ msg = _("no IFLA_PORT_RESPONSE found in netlink message");
+ goto err_exit;
+ }
+
+err_exit:
+ if (msg)
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s", msg);
+
+ return rc;
+}
+
+
+static int
+doPortProfileOpSetLink(bool multicast,
+ const char *ifname, int ifindex,
+ const char *profileId,
+ struct ifla_port_vsi *portVsi,
+ const unsigned char *instanceId,
+ const unsigned char *hostUUID,
+ int32_t vf,
+ uint8_t op)
+{
+ int rc = 0;
+ char nlmsgbuf[256];
+ struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+ struct nlmsgerr *err;
+ char rtattbuf[64];
+ struct rtattr *rta, *vfports, *vfport;
+ struct ifinfomsg ifinfo = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = ifindex,
+ };
+ char *recvbuf = NULL;
+ int recvbuflen = 0;
+
+ memset(&nlmsgbuf, 0, sizeof(nlmsgbuf));
+
+ nlInit(nlm, NLM_F_REQUEST, RTM_SETLINK);
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &ifinfo, sizeof(ifinfo)))
+ goto buffer_too_small;
+
+ if (vf == PORT_SELF_VF) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_SELF, NULL, 0);
+ } else {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORTS, NULL, 0);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!(vfports = nlAppend(nlm, sizeof(nlmsgbuf),
+ rtattbuf, rta->rta_len)))
+ goto buffer_too_small;
+
+ /* beging nesting vfports */
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORT, NULL, 0);
+ }
+
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!(vfport = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
+ goto buffer_too_small;
+
+ if (profileId) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_PROFILE,
+ profileId, strlen(profileId) + 1);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+ if (portVsi) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_VSI_TYPE,
+ portVsi, sizeof(*portVsi));
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (instanceId) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_INSTANCE_UUID,
+ instanceId, VIR_UUID_BUFLEN);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (hostUUID) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_HOST_UUID,
+ hostUUID, VIR_UUID_BUFLEN);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (vf != PORT_SELF_VF) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_VF,
+ &vf, sizeof(vf));
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_REQUEST,
+ &op, sizeof(op));
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+
+ /* end nesting of vport */
+ vfport->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)vfport;
+
+ if (vf != PORT_SELF_VF) {
+ /* end nesting of vfports */
+ vfports->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)vfports;
+ }
+
+ if (!multicast) {
+ if (nlComm(nlm, &recvbuf, &recvbuflen) < 0)
+ return -1;
+ } else {
+ if (nlCommWaitSuccess(nlm, RTMGRP_LINK, &recvbuf, &recvbuflen,
+ 5 * MICROSEC_PER_SEC) < 0)
+ return -1;
+ }
+
+ if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL)
+ goto malformed_resp;
+
+ resp = (struct nlmsghdr *)recvbuf;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+
+ switch (-err->error) {
+ case 0:
+ break;
+
+ default:
+ virReportSystemError(-err->error,
+ _("error during virtual port configuration of %s interface"),
+ ifname);
+ rc = -1;
+ }
+ break;
+
+ case NLMSG_DONE:
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ VIR_FREE(recvbuf);
+
+ return rc;
+
+malformed_resp:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(recvbuf);
+ return -1;
+
+buffer_too_small:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("internal buffer is too small"));
+ return -1;
+}
+
+
+static int
+doPortProfileOpCommon(bool multicast,
+ const char *ifname, int ifindex,
+ const char *profileId,
+ struct ifla_port_vsi *portVsi,
+ const unsigned char *instanceId,
+ const unsigned char *hostUUID,
+ int32_t vf,
+ uint8_t op)
+{
+ int rc;
+ char *recvbuf = NULL;
+ struct nlattr *tb[IFLA_MAX + 1];
+ int repeats = 80;
+ uint16_t status = 0;
+
+ rc = doPortProfileOpSetLink(multicast,
+ ifname, ifindex,
+ profileId,
+ portVsi,
+ instanceId,
+ hostUUID,
+ vf,
+ op);
+
+ if (rc != 0) {
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("sending of PortProfileRequest failed.\n"));
+ return rc;
+ }
+
+ if (!multicast) {
+ while (--repeats) {
+ rc = link_dump(ifindex, ifname, tb, &recvbuf);
+ if (rc)
+ goto err_exit;
+ rc = getPortProfileStatus(tb, vf, &status);
+ if (rc == 0) {
+ if (status == PORT_PROFILE_RESPONSE_SUCCESS ||
+ status == PORT_VDP_RESPONSE_SUCCESS) {
+ break;
+ } else if (status == PORT_PROFILE_RESPONSE_INPROGRESS) {
+ // keep trying...
+ } else {
+ virReportSystemError(EINVAL,
+ _("error %d during port-profile setlink on %s"),
+ status, ifname);
+ rc = 1;
+ break;
+ }
+ }
+ usleep(125000);
+
+ VIR_FREE(recvbuf);
+ }
+ }
+
+ if (status == PORT_PROFILE_RESPONSE_INPROGRESS) {
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("port-profile setlink timed out"));
+ rc = -ETIMEDOUT;
+ }
+
+err_exit:
+ VIR_FREE(recvbuf);
+
+ return rc;
+}
+
+# endif /* IFLA_PORT_MAX */
+
+static int
+doPortProfileOp8021Qbg(const char *ifname,
+ const virVirtualPortProfileDefPtr virtPort,
+ int32_t vf,
+ enum virVirtualPortOp virtPortOp)
+{
+ int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (void)ifname;
+ (void)virtPort;
+ (void)vf;
+ (void)virtPortOp;
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Kernel VF Port support was missing at compile time."));
+ rc = 1;
+
+# else /* IFLA_VF_PORT_MAX */
+
+ int op = PORT_REQUEST_ASSOCIATE;
+ struct ifla_port_vsi portVsi = {
+ .vsi_mgr_id = virtPort->u.virtPort8021Qbg.managerID,
+ .vsi_type_version = virtPort->u.virtPort8021Qbg.typeIDVersion,
+ };
+ bool multicast = true;
+ int ifindex;
+
+ if (ifaceGetIndex(true, ifname, &ifindex) != 0) {
+ rc = 1;
+ goto err_exit;
+ }
+
+ portVsi.vsi_type_id[2] = virtPort->u.virtPort8021Qbg.typeID >> 16;
+ portVsi.vsi_type_id[1] = virtPort->u.virtPort8021Qbg.typeID >> 8;
+ portVsi.vsi_type_id[0] = virtPort->u.virtPort8021Qbg.typeID;
+
+ switch (virtPortOp) {
+ case ASSOCIATE:
+ op = PORT_REQUEST_ASSOCIATE;
+ break;
+ case DISASSOCIATE:
+ op = PORT_REQUEST_DISASSOCIATE;
+ break;
+ default:
+ macvtapError(VIR_ERR_INTERNAL_ERROR,
+ _("operation type %d not supported"), op);
+ rc = 1;
+ goto err_exit;
+ }
+
+ rc = doPortProfileOpCommon(multicast, NULL, ifindex,
+ NULL,
+ &portVsi,
+ virtPort->u.virtPort8021Qbg.instanceID,
+ NULL,
+ vf,
+ op);
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
+
+
+static int
+doPortProfileOp8021Qbh(const char *ifname,
+ const virVirtualPortProfileDefPtr virtPort,
+ int32_t vf,
+ const unsigned char *host_uuid,
+ const unsigned char *vm_uuid,
+ enum virVirtualPortOp virtPortOp)
+{
+ int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (void)ifname;
+ (void)virtPort;
+ (void)vf;
+ (void)host_uuid;
+ (void)vm_uuid;
+ (void)virtPortOp;
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Kernel VF Port support was missing at compile time."));
+ rc = 1;
+
+# else /* IFLA_VF_PORT_MAX */
+
+ int op = PORT_REQUEST_ASSOCIATE;
+ bool multicast = false;
+ int ifindex;
+
+ if (ifaceGetIndex(true, ifname, &ifindex) != 0) {
+ rc = 1;
+ goto err_exit;
+ }
+
+ switch (virtPortOp) {
+ case ASSOCIATE:
+ op = PORT_REQUEST_ASSOCIATE;
+ break;
+ case DISASSOCIATE:
+ op = PORT_REQUEST_DISASSOCIATE;
+ break;
+ default:
+ macvtapError(VIR_ERR_INTERNAL_ERROR,
+ _("operation type %d not supported"), op);
+ rc = 1;
+ goto err_exit;
+ }
+
+ rc = doPortProfileOpCommon(multicast, ifname, ifindex,
+ virtPort->u.virtPort8021Qbh.profileID,
+ NULL,
+ vm_uuid,
+ host_uuid,
+ vf,
+ op);
+
+ switch (virtPortOp) {
+ case ASSOCIATE:
+ ifaceUp(ifname);
+ break;
+ case DISASSOCIATE:
+ ifaceDown(ifname);
+ break;
+ }
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
+
+static int
+getPhysfn(const char *linkdev,
+ int32_t *vf,
+ char **physfndev)
+{
+ int rc = 0;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (void)linkdev;
+ (void)vf;
+ (void)physfndev;
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Kernel VF Port support was missing at compile time."));
+ rc = 1;
+
+# else /* IFLA_VF_PORT_MAX */
+
+ bool virtfn = false;
+
+ if (virtfn) {
+
+ // XXX: if linkdev is SR-IOV VF, then set vf = VF index
+ // XXX: and set linkdev = PF device
+ // XXX: need to use get_physical_function_linux() or
+ // XXX: something like that to get PF
+ // XXX: device and figure out VF index
+
+ rc = 1;
+
+ } else {
+
+ /* Not SR-IOV VF: physfndev is linkdev and VF index
+ * refers to linkdev self
+ */
+
+ *vf = PORT_SELF_VF;
+ *physfndev = (char *)linkdev;
+ }
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
/**
* associatePortProfile
*
* @macvtap_ifname: The name of the macvtap device
+ * @linkdev: The link device in case of macvtap
* @virtPort: pointer to the object holding port profile parameters
- * @vf: virtual function number, -1 if to be ignored
* @vmuuid : the UUID of the virtual machine
+ * @hostuuid : the UUID of the host machine
*
* Associate a port on a swtich with a profile. This function
* may notify a kernel driver or an external daemon to run
@@ -734,15 +1483,25 @@ delMacvtap(virDomainNetDefPtr net)
*/
static int
associatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort,
- int vf,
const unsigned char *vmuuid)
{
+ char *physfndev;
+ unsigned char hostuuid[VIR_UUID_BUFLEN];
+ int32_t vf;
int rc = 0;
+
VIR_DEBUG("Associating port profile '%p' on link device '%s'",
virtPort, macvtap_ifname);
- (void)vf;
- (void)vmuuid;
+
+ rc = virGetHostUUID(hostuuid);
+ if (rc)
+ goto err_exit;
+
+ rc = getPhysfn(linkdev, &vf, &physfndev);
+ if (rc)
+ goto err_exit;
switch (virtPort->virtPortType) {
case VIR_VIRTUALPORT_NONE:
@@ -750,14 +1509,18 @@ associatePortProfileId(const char *macvtap_ifname,
break;
case VIR_VIRTUALPORT_8021QBG:
-
+ rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort, vf,
+ ASSOCIATE);
break;
case VIR_VIRTUALPORT_8021QBH:
-
+ rc = doPortProfileOp8021Qbh(physfndev, virtPort, vf,
+ hostuuid, vmuuid,
+ ASSOCIATE);
break;
}
+err_exit:
return rc;
}
@@ -766,6 +1529,7 @@ associatePortProfileId(const char *macvtap_ifname,
* disassociatePortProfile
*
* @macvtap_ifname: The name of the macvtap device
+ * @linkdev: The link device in case of macvtap
* @virtPort: point to object holding port profile parameters
*
* Returns 0 in case of success, != 0 otherwise with error
@@ -773,25 +1537,39 @@ associatePortProfileId(const char *macvtap_ifname,
*/
static int
disassociatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort)
{
+ char *physfndev;
+ int32_t vf;
int rc = 0;
+
VIR_DEBUG("Disassociating port profile id '%p' on link device '%s' ",
virtPort, macvtap_ifname);
+ rc = getPhysfn(linkdev, &vf, &physfndev);
+ if (rc)
+ goto err_exit;
+
switch (virtPort->virtPortType) {
case VIR_VIRTUALPORT_NONE:
case VIR_VIRTUALPORT_TYPE_LAST:
break;
case VIR_VIRTUALPORT_8021QBG:
-
+ rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort, vf,
+ DISASSOCIATE);
break;
case VIR_VIRTUALPORT_8021QBH:
-
+ rc = doPortProfileOp8021Qbh(physfndev, virtPort, vf,
+ NULL, NULL,
+ DISASSOCIATE);
break;
}
+err_exit:
return rc;
}
+
+#endif
14 years, 7 months
[libvirt] [PATCH v9] vepa: parsing for 802.1Qb{g|h} XML
by Stefan Berger
Below is David Alan's original patch with lots of changes.
In particular, it now parses the following two XML descriptions, one
for 802.1Qbg and 802.1Qbh and stored the data internally. The actual
triggering of the switch setup protocol has not been implemented
here but the relevant code to do that should go into the functions
associatePortProfileId() and disassociatePortProfileId().
<interface type='direct'>
<source dev='eth0.100' mode='vepa'/>
<model type='virtio'/>
<virtualport type='802.1Qbg'>
<parameters managerid='12' typeid='0x123456' typeidversion='1'
instanceid='fa9b7fff-b0a0-4893-8e0e-beef4ff18f8f'/>
</virtualport>
<filterref filter='clean-traffic'/>
</interface>
<interface type='direct'>
<source dev='eth0.100' mode='vepa'/>
<model type='virtio'/>
<virtualport type='802.1Qbh'>
<parameters profileid='my_profile'/>
</virtualport>
</interface>
I'd suggest to use this patch as a base for triggering the setup
protocol with the 802.1Qb{g|h} switch.
V9:
-Addressing Daniel Berrange's comments:
- removing macvtap.h's dependency on domain_conf.h by
moving the virVirtualPortProfileDef structure into macvtap.h
and not passing virtDomainNetDefPtr to any functions in
macvtap.c
Changes from V7 to V8:
- Addressed most of Chris Wright's comments:
- indicating error in case virtualport XML node cannot be parsed
properly
- parsing hex and decimal numbers using virStrToLong_ui() with
parameter '0' for base
- tgifname (target interface name) variable wasn't necessary
to pass to openMacvtapTap function anymore
- assigning the virtual port data structure to the virDomainNetDef
only if it was previously parsed
-> still leaving possibility to start a domain with macvtap but no profile
Changes from V6 to V7:
- make sure that the error code returned by openMacvtapTap() is a negative number
in case the associatePortProfileId() function failed.
Changes from V5 to V6:
- renaming vsi in the XML to virtualport
- replace all occurrences of vsi in the source as well
Changes from V4 to V5:
- removing mode and MAC address parameters from the functions that
will communicate with the hareware diretctly or indirectly
Changes from V3 to V4:
- moving the associate and disassociate functions to the end of the
file for subsequent patches to easier make them generally available
for export
- passing the macvtap interface name rather than the link device since
this otherwise gives funny side effects when using netlink messages
where IFLA_IFNAME and IFLA_ADDRESS are specified and the link dev
all of a sudden gets the MAC address of the macvtap interface.
- Removing rc = -1 error indications in the case of 802.1Qbg|h setup in case
we wanted to use hook scripts for the setup and so the setup doesn't fail
here.
Changes from V2 to V3:
- if instance ID UUID is not supplied it will automatically be generated
- adapted schema to make instance ID UUID optional
- added test case
Some of the changes from V1 to V2:
- parser and XML generator have been separated into their own
functions so they can be re-used elsewhere (passthrough case
for example)
- Adapted XML parser and generator support the above shown type
(802.1Qbg, 802.1Qbh).
- Adapted schema to above XML
- Adapted test XML to above XML
- Passing through the VM's UUID which seems to be necessary for
802.1Qbh -- sorry no host UUID
- adding virtual function ID to association function, in case it's
necessary to use (for SR-IOV)
Signed-off-by: Stefan Berger <stefanb(a)us.ibm.com>
>From a945107f047c7cd71f9c1b74fd74c47d8cdc3670 Mon Sep 17 00:00:00 2001
From: David Allan <dallan(a)redhat.com>
Date: Fri, 12 Mar 2010 13:25:04 -0500
Subject: [PATCH 1/1] POC of port profile id support
* Modified schema per DanPB's feedback
* Added test for modified schema
---
docs/schemas/domain.rng | 69 +++++++++++
src/conf/domain_conf.c | 202 +++++++++++++++++++++++++++++++++
src/conf/domain_conf.h | 2
src/qemu/qemu_conf.c | 21 +--
src/qemu/qemu_conf.h | 5
src/qemu/qemu_driver.c | 19 +--
src/util/macvtap.c | 137 ++++++++++++++++++++--
src/util/macvtap.h | 45 ++++++-
tests/domainschemadata/portprofile.xml | 36 +++++
9 files changed, 498 insertions(+), 38 deletions(-)
create mode 100644 tests/domainschemadata/portprofile.xml
Index: libvirt-acl/docs/schemas/domain.rng
===================================================================
--- libvirt-acl.orig/docs/schemas/domain.rng
+++ libvirt-acl/docs/schemas/domain.rng
@@ -817,6 +817,9 @@
</optional>
<empty/>
</element>
+ <optional>
+ <ref name="virtualPortProfile"/>
+ </optional>
<ref name="interface-options"/>
</interleave>
</group>
@@ -902,6 +905,45 @@
</optional>
</interleave>
</define>
+ <define name="virtualPortProfile">
+ <choice>
+ <group>
+ <element name="virtualport">
+ <attribute name="type">
+ <value>802.1Qbg</value>
+ </attribute>
+ <element name="parameters">
+ <attribute name="managerid">
+ <ref name="uint8range"/>
+ </attribute>
+ <attribute name="typeid">
+ <ref name="uint24range"/>
+ </attribute>
+ <attribute name="typeidversion">
+ <ref name="uint8range"/>
+ </attribute>
+ <optional>
+ <attribute name="instanceid">
+ <ref name="UUID"/>
+ </attribute>
+ </optional>
+ </element>
+ </element>
+ </group>
+ <group>
+ <element name="virtualport">
+ <attribute name="type">
+ <value>802.1Qbh</value>
+ </attribute>
+ <element name="parameters">
+ <attribute name="profileid">
+ <ref name="virtualPortProfileID"/>
+ </attribute>
+ </element>
+ </element>
+ </group>
+ </choice>
+ </define>
<!--
An emulator description is just a path to the binary used for the task
-->
@@ -1769,4 +1811,31 @@
<param name="pattern">[a-zA-Z0-9_\.:]+</param>
</data>
</define>
+ <define name="uint8range">
+ <choice>
+ <data type="string">
+ <param name="pattern">0x[0-9a-fA-F]{1,2}</param>
+ </data>
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">255</param>
+ </data>
+ </choice>
+ </define>
+ <define name="uint24range">
+ <choice>
+ <data type="string">
+ <param name="pattern">0x[0-9a-fA-F]{1,6}</param>
+ </data>
+ <data type="int">
+ <param name="minInclusive">0</param>
+ <param name="maxInclusive">16777215</param>
+ </data>
+ </choice>
+ </define>
+ <define name="virtualPortProfileID">
+ <data type="string">
+ <param name="maxLength">39</param>
+ </data>
+ </define>
</grammar>
Index: libvirt-acl/src/conf/domain_conf.c
===================================================================
--- libvirt-acl.orig/src/conf/domain_conf.c
+++ libvirt-acl/src/conf/domain_conf.c
@@ -242,6 +242,11 @@ VIR_ENUM_IMPL(virDomainNetdevMacvtap, VI
"private",
"bridge")
+VIR_ENUM_IMPL(virVirtualPort, VIR_VIRTUALPORT_TYPE_LAST,
+ "none",
+ "802.1Qbg",
+ "802.1Qbh")
+
VIR_ENUM_IMPL(virDomainClockOffset, VIR_DOMAIN_CLOCK_OFFSET_LAST,
"utc",
"localtime",
@@ -1807,6 +1812,190 @@ cleanup:
}
+static int
+virVirtualPortProfileDefParseXML(xmlNodePtr node,
+ virVirtualPortProfileDefPtr virtPort)
+{
+ int ret = -1;
+ char *virtPortType;
+ char *virtPortManagerID = NULL;
+ char *virtPortTypeID = NULL;
+ char *virtPortTypeIDVersion = NULL;
+ char *virtPortInstanceID = NULL;
+ char *virtPortProfileID = NULL;
+ xmlNodePtr cur = node->children;
+ const char *msg = NULL;
+
+ virtPortType = virXMLPropString(node, "type");
+ if (!virtPortType)
+ return -1;
+
+ while (cur != NULL) {
+ if (xmlStrEqual(cur->name, BAD_CAST "parameters")) {
+
+ virtPortManagerID = virXMLPropString(cur, "managerid");
+ virtPortTypeID = virXMLPropString(cur, "typeid");
+ virtPortTypeIDVersion = virXMLPropString(cur, "typeidversion");
+ virtPortInstanceID = virXMLPropString(cur, "instanceid");
+ virtPortProfileID = virXMLPropString(cur, "profileid");
+
+ break;
+ }
+
+ cur = cur->next;
+ }
+
+ virtPort->virtPortType = VIR_VIRTUALPORT_NONE;
+
+ switch (virVirtualPortTypeFromString(virtPortType)) {
+
+ case VIR_VIRTUALPORT_8021QBG:
+ if (virtPortManagerID != NULL && virtPortTypeID != NULL &&
+ virtPortTypeIDVersion != NULL) {
+ unsigned int val;
+
+ if (virStrToLong_ui(virtPortManagerID, NULL, 0, &val)) {
+ msg = _("cannot parse value of managerid parameter");
+ goto err_exit;
+ }
+
+ if (val > 0xff) {
+ msg = _("value of managerid out of range");
+ goto err_exit;
+ }
+
+ virtPort->u.virtPort8021Qbg.managerID = (uint8_t)val;
+
+ if (virStrToLong_ui(virtPortTypeID, NULL, 0, &val)) {
+ msg = _("cannot parse value of typeid parameter");
+ goto err_exit;
+ }
+
+ if (val > 0xffffff) {
+ msg = _("value for typeid out of range");
+ goto err_exit;
+ }
+
+ virtPort->u.virtPort8021Qbg.typeID = (uint32_t)val;
+
+ if (virStrToLong_ui(virtPortTypeIDVersion, NULL, 0, &val)) {
+ msg = _("cannot parse value of typeidversion parameter");
+ goto err_exit;
+ }
+
+ if (val > 0xff) {
+ msg = _("value of typeidversion out of range");
+ goto err_exit;
+ }
+
+ virtPort->u.virtPort8021Qbg.typeIDVersion = (uint8_t)val;
+
+ if (virtPortInstanceID != NULL) {
+ if (virUUIDParse(virtPortInstanceID,
+ virtPort->u.virtPort8021Qbg.instanceID)) {
+ msg = _("cannot parse instanceid parameter as a uuid");
+ goto err_exit;
+ }
+ } else {
+ if (virUUIDGenerate(virtPort->u.virtPort8021Qbg.instanceID)) {
+ msg = _("cannot generate a random uuid for instanceid");
+ goto err_exit;
+ }
+ }
+
+ virtPort->virtPortType = VIR_VIRTUALPORT_8021QBG;
+ ret = 0;
+ } else {
+ msg = _("a parameter is missing for 802.1Qbg description");
+ goto err_exit;
+ }
+ break;
+
+ case VIR_VIRTUALPORT_8021QBH:
+ if (virtPortProfileID != NULL) {
+ if (virStrcpyStatic(virtPort->u.virtPort8021Qbh.profileID,
+ virtPortProfileID) != NULL) {
+ virtPort->virtPortType = VIR_VIRTUALPORT_8021QBH;
+ ret = 0;
+ } else {
+ msg = _("profileid parameter too long");
+ goto err_exit;
+ }
+ } else {
+ msg = _("profileid parameter is missing for 802.1Qbh descripion");
+ goto err_exit;
+ }
+ break;
+
+
+ default:
+ case VIR_VIRTUALPORT_NONE:
+ case VIR_VIRTUALPORT_TYPE_LAST:
+ msg = _("unknown virtualport type");
+ goto err_exit;
+ break;
+ }
+
+err_exit:
+
+ if (msg)
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", msg);
+
+ VIR_FREE(virtPortManagerID);
+ VIR_FREE(virtPortTypeID);
+ VIR_FREE(virtPortTypeIDVersion);
+ VIR_FREE(virtPortInstanceID);
+ VIR_FREE(virtPortProfileID);
+ VIR_FREE(virtPortType);
+
+ return ret;
+}
+
+
+static void
+virVirtualPortProfileFormat(virBufferPtr buf,
+ virVirtualPortProfileDefPtr virtPort,
+ const char *indent)
+{
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+ if (virtPort->virtPortType == VIR_VIRTUALPORT_NONE)
+ return;
+
+ virBufferVSprintf(buf, "%s<virtualport type='%s'>\n",
+ indent,
+ virVirtualPortTypeToString(virtPort->virtPortType));
+
+ switch (virtPort->virtPortType) {
+ case VIR_VIRTUALPORT_NONE:
+ case VIR_VIRTUALPORT_TYPE_LAST:
+ break;
+
+ case VIR_VIRTUALPORT_8021QBG:
+ virUUIDFormat(virtPort->u.virtPort8021Qbg.instanceID,
+ uuidstr);
+ virBufferVSprintf(buf,
+ "%s <parameters managerid='%d' typeid='%d' "
+ "typeidversion='%d' instanceid='%s'/>\n",
+ indent,
+ virtPort->u.virtPort8021Qbg.managerID,
+ virtPort->u.virtPort8021Qbg.typeID,
+ virtPort->u.virtPort8021Qbg.typeIDVersion,
+ uuidstr);
+ break;
+
+ case VIR_VIRTUALPORT_8021QBH:
+ virBufferVSprintf(buf,
+ "%s <parameters profileid='%s'/>\n",
+ indent,
+ virtPort->u.virtPort8021Qbh.profileID);
+ break;
+ }
+
+ virBufferVSprintf(buf, "%s</virtualport>\n", indent);
+}
+
+
/* Parse the XML definition for a network interface
* @param node XML nodeset to parse for net definition
* @return 0 on success, -1 on failure
@@ -1832,6 +2021,8 @@ virDomainNetDefParseXML(virCapsPtr caps,
char *devaddr = NULL;
char *mode = NULL;
virNWFilterHashTablePtr filterparams = NULL;
+ virVirtualPortProfileDef virtPort;
+ bool virtPortParsed = false;
if (VIR_ALLOC(def) < 0) {
virReportOOMError();
@@ -1873,6 +2064,12 @@ virDomainNetDefParseXML(virCapsPtr caps,
xmlStrEqual(cur->name, BAD_CAST "source")) {
dev = virXMLPropString(cur, "dev");
mode = virXMLPropString(cur, "mode");
+ } else if ((virtPortParsed == false) &&
+ (def->type == VIR_DOMAIN_NET_TYPE_DIRECT) &&
+ xmlStrEqual(cur->name, BAD_CAST "virtualport")) {
+ if (virVirtualPortProfileDefParseXML(cur, &virtPort))
+ goto error;
+ virtPortParsed = true;
} else if ((network == NULL) &&
((def->type == VIR_DOMAIN_NET_TYPE_SERVER) ||
(def->type == VIR_DOMAIN_NET_TYPE_CLIENT) ||
@@ -2048,6 +2245,9 @@ virDomainNetDefParseXML(virCapsPtr caps,
} else
def->data.direct.mode = VIR_DOMAIN_NETDEV_MACVTAP_MODE_VEPA;
+ if (virtPortParsed)
+ def->data.direct.virtPortProfile = virtPort;
+
def->data.direct.linkdev = dev;
dev = NULL;
@@ -5140,6 +5340,8 @@ virDomainNetDefFormat(virBufferPtr buf,
virBufferVSprintf(buf, " mode='%s'",
virDomainNetdevMacvtapTypeToString(def->data.direct.mode));
virBufferAddLit(buf, "/>\n");
+ virVirtualPortProfileFormat(buf, &def->data.direct.virtPortProfile,
+ " ");
break;
case VIR_DOMAIN_NET_TYPE_USER:
Index: libvirt-acl/src/conf/domain_conf.h
===================================================================
--- libvirt-acl.orig/src/conf/domain_conf.h
+++ libvirt-acl/src/conf/domain_conf.h
@@ -38,6 +38,7 @@
# include "network.h"
# include "nwfilter_params.h"
# include "nwfilter_conf.h"
+# include "macvtap.h"
/* Private component of virDomainXMLFlags */
typedef enum {
@@ -290,6 +291,7 @@ struct _virDomainNetDef {
struct {
char *linkdev;
int mode;
+ virVirtualPortProfileDef virtPortProfile;
} direct;
} data;
char *ifname;
Index: libvirt-acl/src/util/macvtap.c
===================================================================
--- libvirt-acl.orig/src/util/macvtap.c
+++ libvirt-acl/src/util/macvtap.c
@@ -43,6 +43,7 @@
# include "util.h"
# include "memory.h"
+# include "logging.h"
# include "macvtap.h"
# include "interface.h"
# include "conf/domain_conf.h"
@@ -57,6 +58,16 @@
# define MACVTAP_NAME_PREFIX "macvtap"
# define MACVTAP_NAME_PATTERN "macvtap%d"
+
+static int associatePortProfileId(const char *macvtap_ifname,
+ const virVirtualPortProfileDefPtr virtPort,
+ int vf,
+ const unsigned char *vmuuid);
+
+static int disassociatePortProfileId(const char *macvtap_ifname,
+ const virVirtualPortProfileDefPtr virtPort);
+
+
static int nlOpen(void)
{
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
@@ -577,8 +588,10 @@ configMacvtapTap(int tapfd, int vnet_hdr
* be NULL if this function is supposed to choose a name
* @macaddress: The MAC address for the macvtap device
* @linkdev: The interface name of the NIC to connect to the external bridge
- * @mode_str: String describing the mode. Valid are 'bridge', 'vepa' and
- * 'private'.
+ * @mode: int describing the mode for 'bridge', 'vepa' or 'private'.
+ * @vnet_hdr: 1 to enable IFF_VNET_HDR, 0 to disable it
+ * @vmuuid: The UUID of the VM the macvtap belongs to
+ * @virtPortProfile: pointer to object holding the virtual port profile data
* @res_ifname: Pointer to a string pointer where the actual name of the
* interface will be stored into if everything succeeded. It is up
* to the caller to free the string.
@@ -592,8 +605,10 @@ openMacvtapTap(const char *tgifname,
const unsigned char *macaddress,
const char *linkdev,
int mode,
- char **res_ifname,
- int vnet_hdr)
+ int vnet_hdr,
+ const unsigned char *vmuuid,
+ virVirtualPortProfileDefPtr virtPortProfile,
+ char **res_ifname)
{
const char *type = "macvtap";
int c, rc;
@@ -639,6 +654,14 @@ create_name:
cr_ifname = ifname;
}
+ if (associatePortProfileId(cr_ifname,
+ virtPortProfile,
+ -1,
+ vmuuid) != 0) {
+ rc = -1;
+ goto link_del_exit;
+ }
+
rc = ifaceUp(cr_ifname);
if (rc != 0) {
virReportSystemError(errno,
@@ -647,7 +670,7 @@ create_name:
"MAC address"),
cr_ifname);
rc = -1;
- goto link_del_exit;
+ goto disassociate_exit;
}
rc = openTap(cr_ifname, 10);
@@ -656,14 +679,18 @@ create_name:
if (configMacvtapTap(rc, vnet_hdr) < 0) {
close(rc);
rc = -1;
- goto link_del_exit;
+ goto disassociate_exit;
}
*res_ifname = strdup(cr_ifname);
} else
- goto link_del_exit;
+ goto disassociate_exit;
return rc;
+disassociate_exit:
+ disassociatePortProfileId(cr_ifname,
+ virtPortProfile);
+
link_del_exit:
link_del(cr_ifname);
@@ -674,13 +701,103 @@ link_del_exit:
/**
* delMacvtap:
* @ifname : The name of the macvtap interface
+ * @virtPortProfile: pointer to object holding the virtual port profile data
*
- * Delete an interface given its name.
+ * Delete an interface given its name. Disassociate
+ * it with the switch if port profile parameters
+ * were provided.
*/
void
-delMacvtap(const char *ifname)
+delMacvtap(const char *ifname,
+ virVirtualPortProfileDefPtr virtPortProfile)
{
- link_del(ifname);
+ if (ifname) {
+ disassociatePortProfileId(ifname,
+ virtPortProfile);
+ link_del(ifname);
+ }
}
#endif
+
+
+/**
+ * associatePortProfile
+ *
+ * @macvtap_ifname: The name of the macvtap device
+ * @virtPort: pointer to the object holding port profile parameters
+ * @vf: virtual function number, -1 if to be ignored
+ * @vmuuid : the UUID of the virtual machine
+ *
+ * Associate a port on a swtich with a profile. This function
+ * may notify a kernel driver or an external daemon to run
+ * the setup protocol. If profile parameters were not supplied
+ * by the user, then this function returns without doing
+ * anything.
+ *
+ * Returns 0 in case of success, != 0 otherwise with error
+ * having been reported.
+ */
+static int
+associatePortProfileId(const char *macvtap_ifname,
+ const virVirtualPortProfileDefPtr virtPort,
+ int vf,
+ const unsigned char *vmuuid)
+{
+ int rc = 0;
+ VIR_DEBUG("Associating port profile '%p' on link device '%s'",
+ virtPort, macvtap_ifname);
+ (void)vf;
+ (void)vmuuid;
+
+ switch (virtPort->virtPortType) {
+ case VIR_VIRTUALPORT_NONE:
+ case VIR_VIRTUALPORT_TYPE_LAST:
+ break;
+
+ case VIR_VIRTUALPORT_8021QBG:
+
+ break;
+
+ case VIR_VIRTUALPORT_8021QBH:
+
+ break;
+ }
+
+ return rc;
+}
+
+
+/**
+ * disassociatePortProfile
+ *
+ * @macvtap_ifname: The name of the macvtap device
+ * @virtPort: point to object holding port profile parameters
+ *
+ * Returns 0 in case of success, != 0 otherwise with error
+ * having been reported.
+ */
+static int
+disassociatePortProfileId(const char *macvtap_ifname,
+ const virVirtualPortProfileDefPtr virtPort)
+{
+ int rc = 0;
+ VIR_DEBUG("Disassociating port profile id '%p' on link device '%s' ",
+ virtPort, macvtap_ifname);
+
+ switch (virtPort->virtPortType) {
+ case VIR_VIRTUALPORT_NONE:
+ case VIR_VIRTUALPORT_TYPE_LAST:
+ break;
+
+ case VIR_VIRTUALPORT_8021QBG:
+
+ break;
+
+ case VIR_VIRTUALPORT_8021QBH:
+
+ break;
+ }
+
+ return rc;
+}
Index: libvirt-acl/src/util/macvtap.h
===================================================================
--- libvirt-acl.orig/src/util/macvtap.h
+++ libvirt-acl/src/util/macvtap.h
@@ -24,6 +24,40 @@
# include <config.h>
+
+enum virVirtualPortType {
+ VIR_VIRTUALPORT_NONE,
+ VIR_VIRTUALPORT_8021QBG,
+ VIR_VIRTUALPORT_8021QBH,
+
+ VIR_VIRTUALPORT_TYPE_LAST,
+};
+
+# ifdef IFLA_VF_PORT_PROFILE_MAX
+# define LIBVIRT_IFLA_VF_PORT_PROFILE_MAX IFLA_VF_PORT_PROFILE_MAX
+# else
+# define LIBVIRT_IFLA_VF_PORT_PROFILE_MAX 40
+# endif
+
+/* profile data for macvtap (VEPA) */
+typedef struct _virVirtualPortProfileDef virVirtualPortProfileDef;
+typedef virVirtualPortProfileDef *virVirtualPortProfileDefPtr;
+struct _virVirtualPortProfileDef {
+ enum virVirtualPortType virtPortType;
+ union {
+ struct {
+ uint8_t managerID;
+ uint32_t typeID; // 24 bit valid
+ uint8_t typeIDVersion;
+ unsigned char instanceID[VIR_UUID_BUFLEN];
+ } virtPort8021Qbg;
+ struct {
+ char profileID[LIBVIRT_IFLA_VF_PORT_PROFILE_MAX];
+ } virtPort8021Qbh;
+ } u;
+};
+
+
# if defined(WITH_MACVTAP)
# include "internal.h"
@@ -32,10 +66,13 @@ int openMacvtapTap(const char *ifname,
const unsigned char *macaddress,
const char *linkdev,
int mode,
- char **res_ifname,
- int vnet_hdr);
+ int vnet_hdr,
+ const unsigned char *vmuuid,
+ virVirtualPortProfileDefPtr virtPortProfile,
+ char **res_ifname);
-void delMacvtap(const char *ifname);
+void delMacvtap(const char *ifname,
+ virVirtualPortProfileDefPtr virtPortProfile);
# endif /* WITH_MACVTAP */
@@ -44,4 +81,6 @@ void delMacvtap(const char *ifname);
# define MACVTAP_MODE_BRIDGE_STR "bridge"
+VIR_ENUM_DECL(virVirtualPort)
+
#endif /* __UTIL_MACVTAP_H__ */
Index: libvirt-acl/tests/domainschemadata/portprofile.xml
===================================================================
--- /dev/null
+++ libvirt-acl/tests/domainschemadata/portprofile.xml
@@ -0,0 +1,36 @@
+<domain type='lxc'>
+ <name>portprofile</name>
+ <uuid>00000000-0000-0000-0000-000000000000</uuid>
+ <memory>1048576</memory>
+ <os>
+ <type>exe</type>
+ <init>/sh</init>
+ </os>
+ <devices>
+ <interface type='direct'>
+ <source dev='eth0' mode='vepa'/>
+ <virtualport type='802.1Qbg'>
+ <parameters managerid='12' typeid='1193046' typeidversion='1'
+ instanceid='fa9b7fff-b0a0-4893-8e0e-beef4ff18f8f'/>
+ </virtualport>
+ </interface>
+ <interface type='direct'>
+ <source dev='eth0' mode='vepa'/>
+ <virtualport type='802.1Qbg'>
+ <parameters managerid='12' typeid='1193046' typeidversion='1'/>
+ </virtualport>
+ </interface>
+ <interface type='direct'>
+ <source dev='eth0' mode='vepa'/>
+ <virtualport type='802.1Qbh'>
+ <parameters profileid='my_profile'/>
+ </virtualport>
+ </interface>
+ <interface type='direct'>
+ <source dev='eth0' mode='vepa'/>
+ </interface>
+ <interface type='direct'>
+ <source dev='eth0' mode='vepa'/>
+ </interface>
+ </devices>
+</domain>
Index: libvirt-acl/src/qemu/qemu_conf.h
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_conf.h
+++ libvirt-acl/src/qemu/qemu_conf.h
@@ -274,9 +274,8 @@ qemudOpenVhostNet(virDomainNetDefPtr net
int qemudPhysIfaceConnect(virConnectPtr conn,
struct qemud_driver *driver,
virDomainNetDefPtr net,
- char *linkdev,
- int brmode,
- unsigned long long qemuCmdFlags);
+ unsigned long long qemuCmdFlags,
+ const unsigned char *vmuuid);
int qemudProbeMachineTypes (const char *binary,
virCapsGuestMachinePtr **machines,
Index: libvirt-acl/src/qemu/qemu_driver.c
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_driver.c
+++ libvirt-acl/src/qemu/qemu_driver.c
@@ -3708,10 +3708,9 @@ static void qemudShutdownVMDaemon(struct
def = vm->def;
for (i = 0; i < def->nnets; i++) {
virDomainNetDefPtr net = def->nets[i];
- if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
- if (net->ifname)
- delMacvtap(net->ifname);
- }
+ if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT)
+ delMacvtap(net->ifname,
+ &net->data.direct.virtPortProfile);
}
#endif
@@ -7467,9 +7466,8 @@ static int qemudDomainAttachNetDevice(vi
}
if ((tapfd = qemudPhysIfaceConnect(conn, driver, net,
- net->data.direct.linkdev,
- net->data.direct.mode,
- qemuCmdFlags)) < 0)
+ qemuCmdFlags,
+ vm->def->uuid)) < 0)
return -1;
}
@@ -8512,10 +8510,9 @@ qemudDomainDetachNetDevice(struct qemud_
virNWFilterTearNWFilter(detach);
#if WITH_MACVTAP
- if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
- if (detach->ifname)
- delMacvtap(detach->ifname);
- }
+ if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT)
+ delMacvtap(detach->ifname,
+ &detach->data.direct.virtPortProfile);
#endif
if ((driver->macFilter) && (detach->ifname != NULL)) {
Index: libvirt-acl/src/qemu/qemu_conf.c
===================================================================
--- libvirt-acl.orig/src/qemu/qemu_conf.c
+++ libvirt-acl/src/qemu/qemu_conf.c
@@ -1470,9 +1470,8 @@ int
qemudPhysIfaceConnect(virConnectPtr conn,
struct qemud_driver *driver,
virDomainNetDefPtr net,
- char *linkdev,
- int brmode,
- unsigned long long qemuCmdFlags)
+ unsigned long long qemuCmdFlags,
+ const unsigned char *vmuuid)
{
int rc;
#if WITH_MACVTAP
@@ -1484,8 +1483,9 @@ qemudPhysIfaceConnect(virConnectPtr conn
net->model && STREQ(net->model, "virtio"))
vnet_hdr = 1;
- rc = openMacvtapTap(net->ifname, net->mac, linkdev, brmode,
- &res_ifname, vnet_hdr);
+ rc = openMacvtapTap(net->ifname, net->mac, net->data.direct.linkdev,
+ net->data.direct.mode, vnet_hdr, vmuuid,
+ &net->data.direct.virtPortProfile, &res_ifname);
if (rc >= 0) {
VIR_FREE(net->ifname);
net->ifname = res_ifname;
@@ -1505,17 +1505,17 @@ qemudPhysIfaceConnect(virConnectPtr conn
if (err) {
close(rc);
rc = -1;
- delMacvtap(net->ifname);
+ delMacvtap(net->ifname,
+ &net->data.direct.virtPortProfile);
}
}
}
#else
(void)conn;
(void)net;
- (void)linkdev;
- (void)brmode;
(void)qemuCmdFlags;
(void)driver;
+ (void)vmuuid;
qemuReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("No support for macvtap device"));
rc = -1;
@@ -4135,9 +4135,8 @@ int qemudBuildCommandLine(virConnectPtr
goto no_memory;
} else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
int tapfd = qemudPhysIfaceConnect(conn, driver, net,
- net->data.direct.linkdev,
- net->data.direct.mode,
- qemuCmdFlags);
+ qemuCmdFlags,
+ def->uuid);
if (tapfd < 0)
goto error;
14 years, 7 months
[libvirt] [PATCH v2] Fix up basic migration.
by Chris Lalancette
Basic live migration was broken by the commit that added
non-shared block support in two ways:
1) It added a virCheckFlags() to doNativeMigrate(). Besides
the fact that typical usage of virCheckFlags() is in driver
entry points, and doNativeMigrate() is not an entry point,
it was missing important flags like VIR_MIGRATE_LIVE. Move
the virCheckFlags to the top-level qemuDomainMigratePrepare2
and friends. Unfortunately this required adding a new variant
of the virCheckFlags() macro that could handle unsigned long
arguments, but it should be easily extendable from here.
2) It also added a memory leak in qemuMonitorTextMigrate()
by not freeing the memory used by virBufferContentAndReset().
This is fixed by storing the pointer in a temporary variable
and freeing it at the end.
With this patch in place, normal live migration works again.
Signed-off-by: Chris Lalancette <clalance(a)redhat.com>
---
src/esx/esx_driver.c | 18 +++++-----
src/internal.h | 10 +++++-
src/nwfilter/nwfilter_driver.c | 2 +-
src/qemu/qemu_driver.c | 64 +++++++++++++++++++++++++++------------
src/qemu/qemu_monitor_text.c | 10 ++++--
src/storage/storage_driver.c | 2 +-
src/vbox/vbox_tmpl.c | 24 +++++++-------
src/xen/xend_internal.c | 6 ++--
8 files changed, 85 insertions(+), 51 deletions(-)
diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c
index 7c9c50e..7a3609b 100644
--- a/src/esx/esx_driver.c
+++ b/src/esx/esx_driver.c
@@ -3310,7 +3310,7 @@ esxDomainSnapshotCreateXML(virDomainPtr domain, const char *xmlDesc,
esxVI_TaskInfoState taskInfoState;
virDomainSnapshotPtr snapshot = NULL;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
if (esxVI_EnsureSession(priv->host) < 0) {
goto failure;
@@ -3383,7 +3383,7 @@ esxDomainSnapshotDumpXML(virDomainSnapshotPtr snapshot,
char uuid_string[VIR_UUID_STRING_BUFLEN] = "";
char *xml = NULL;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
memset(&def, 0, sizeof (virDomainSnapshotDef));
@@ -3435,7 +3435,7 @@ esxDomainSnapshotNum(virDomainPtr domain, unsigned int flags)
esxPrivate *priv = domain->conn->privateData;
esxVI_VirtualMachineSnapshotTree *rootSnapshotTreeList = NULL;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
if (esxVI_EnsureSession(priv->host) < 0) {
goto failure;
@@ -3469,7 +3469,7 @@ esxDomainSnapshotListNames(virDomainPtr domain, char **names, int nameslen,
esxPrivate *priv = domain->conn->privateData;
esxVI_VirtualMachineSnapshotTree *rootSnapshotTreeList = NULL;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
if (names == NULL || nameslen < 0) {
ESX_ERROR(VIR_ERR_INVALID_ARG, "%s", _("Invalid argument"));
@@ -3514,7 +3514,7 @@ esxDomainSnapshotLookupByName(virDomainPtr domain, const char *name,
esxVI_VirtualMachineSnapshotTree *snapshotTreeParent = NULL;
virDomainSnapshotPtr snapshot = NULL;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
if (esxVI_EnsureSession(priv->host) < 0) {
goto cleanup;
@@ -3545,7 +3545,7 @@ esxDomainHasCurrentSnapshot(virDomainPtr domain, unsigned int flags)
esxPrivate *priv = domain->conn->privateData;
esxVI_VirtualMachineSnapshotTree *currentSnapshotTree = NULL;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
if (esxVI_EnsureSession(priv->host) < 0) {
goto failure;
@@ -3581,7 +3581,7 @@ esxDomainSnapshotCurrent(virDomainPtr domain, unsigned int flags)
esxPrivate *priv = domain->conn->privateData;
esxVI_VirtualMachineSnapshotTree *currentSnapshotTree = NULL;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
if (esxVI_EnsureSession(priv->host) < 0) {
goto cleanup;
@@ -3614,7 +3614,7 @@ esxDomainRevertToSnapshot(virDomainSnapshotPtr snapshot, unsigned int flags)
esxVI_ManagedObjectReference *task = NULL;
esxVI_TaskInfoState taskInfoState;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
if (esxVI_EnsureSession(priv->host) < 0) {
goto failure;
@@ -3667,7 +3667,7 @@ esxDomainSnapshotDelete(virDomainSnapshotPtr snapshot, unsigned int flags)
esxVI_ManagedObjectReference *task = NULL;
esxVI_TaskInfoState taskInfoState;
- virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN, -1);
+ virCheckFlagsUI(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN, -1);
if (esxVI_EnsureSession(priv->host) < 0) {
goto failure;
diff --git a/src/internal.h b/src/internal.h
index c3c5311..d39a35e 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -210,7 +210,7 @@
* Returns nothing. Exits the caller function if unsupported flags were
* passed to it.
*/
-# define virCheckFlags(supported, retval) \
+# define virCheckFlags(supported, retval, format) \
do { \
if ((flags & ~(supported))) { \
virReportErrorHelper(NULL, \
@@ -219,10 +219,16 @@
__FILE__, \
__FUNCTION__, \
__LINE__, \
- _("%s: unsupported flags (0x%x)"), \
+ _("%s: unsupported flags (0x" #format ")"),\
__FUNCTION__, flags & ~(supported)); \
return retval; \
} \
} while (0)
+# define virCheckFlagsUI(supported, retval) \
+ virCheckFlags(supported, retval, %x)
+
+# define virCheckFlagsUL(supported, retval) \
+ virCheckFlags(supported, retval, %lx)
+
#endif /* __VIR_INTERNAL_H__ */
diff --git a/src/nwfilter/nwfilter_driver.c b/src/nwfilter/nwfilter_driver.c
index 3ded2be..b356e95 100644
--- a/src/nwfilter/nwfilter_driver.c
+++ b/src/nwfilter/nwfilter_driver.c
@@ -389,7 +389,7 @@ nwfilterDumpXML(virNWFilterPtr obj,
virNWFilterPoolObjPtr pool;
char *ret = NULL;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
nwfilterDriverLock(driver);
pool = virNWFilterPoolObjFindByUUID(&driver->pools, obj->uuid);
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 51ee320..6e93755 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -5252,7 +5252,7 @@ qemuDomainManagedSave(virDomainPtr dom, unsigned int flags)
int ret = -1;
int compressed;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -5313,7 +5313,7 @@ qemuDomainHasManagedSaveImage(virDomainPtr dom, unsigned int flags)
int ret = -1;
char *name = NULL;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -5347,7 +5347,7 @@ qemuDomainManagedSaveRemove(virDomainPtr dom, unsigned int flags)
int ret = -1;
char *name = NULL;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -8077,9 +8077,9 @@ static int qemuDomainUpdateDeviceFlags(virDomainPtr dom,
virCgroupPtr cgroup = NULL;
int ret = -1;
- virCheckFlags(VIR_DOMAIN_DEVICE_MODIFY_CURRENT |
- VIR_DOMAIN_DEVICE_MODIFY_LIVE |
- VIR_DOMAIN_DEVICE_MODIFY_CONFIG, -1);
+ virCheckFlagsUI(VIR_DOMAIN_DEVICE_MODIFY_CURRENT |
+ VIR_DOMAIN_DEVICE_MODIFY_LIVE |
+ VIR_DOMAIN_DEVICE_MODIFY_CONFIG, -1);
if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
qemuReportError(VIR_ERR_OPERATION_INVALID,
@@ -9457,7 +9457,7 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom,
struct stat sb;
int i;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -10149,6 +10149,15 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
int ret = -1;
int internalret;
+ virCheckFlagsUL(VIR_MIGRATE_LIVE |
+ VIR_MIGRATE_PEER2PEER |
+ VIR_MIGRATE_TUNNELLED |
+ VIR_MIGRATE_PERSIST_DEST |
+ VIR_MIGRATE_UNDEFINE_SOURCE |
+ VIR_MIGRATE_PAUSED |
+ VIR_MIGRATE_NON_SHARED_DISK |
+ VIR_MIGRATE_NON_SHARED_INC, -1);
+
*uri_out = NULL;
qemuDriverLock(driver);
@@ -10330,9 +10339,6 @@ static int doNativeMigrate(struct qemud_driver *driver,
qemuDomainObjPrivatePtr priv = vm->privateData;
unsigned int background_flags = 0;
- virCheckFlags(VIR_MIGRATE_NON_SHARED_DISK | VIR_MIGRATE_NON_SHARED_INC,
- -1);
-
/* Issue the migrate command. */
if (STRPREFIX(uri, "tcp:") && !STRPREFIX(uri, "tcp://")) {
/* HACK: source host generates bogus URIs, so fix them up */
@@ -10753,6 +10759,15 @@ qemudDomainMigratePerform (virDomainPtr dom,
int resume = 0;
qemuDomainObjPrivatePtr priv;
+ virCheckFlagsUL(VIR_MIGRATE_LIVE |
+ VIR_MIGRATE_PEER2PEER |
+ VIR_MIGRATE_TUNNELLED |
+ VIR_MIGRATE_PERSIST_DEST |
+ VIR_MIGRATE_UNDEFINE_SOURCE |
+ VIR_MIGRATE_PAUSED |
+ VIR_MIGRATE_NON_SHARED_DISK |
+ VIR_MIGRATE_NON_SHARED_INC, -1);
+
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
if (!vm) {
@@ -10856,6 +10871,15 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn,
virErrorPtr orig_err;
int newVM = 1;
+ virCheckFlagsUL(VIR_MIGRATE_LIVE |
+ VIR_MIGRATE_PEER2PEER |
+ VIR_MIGRATE_TUNNELLED |
+ VIR_MIGRATE_PERSIST_DEST |
+ VIR_MIGRATE_UNDEFINE_SOURCE |
+ VIR_MIGRATE_PAUSED |
+ VIR_MIGRATE_NON_SHARED_DISK |
+ VIR_MIGRATE_NON_SHARED_INC, NULL);
+
/* Migration failed. Save the current error so nothing squashes it */
orig_err = virSaveLastError();
@@ -11224,7 +11248,7 @@ qemuDomainMigrateSetMaxDowntime(virDomainPtr dom,
qemuDomainObjPrivatePtr priv;
int ret = -1;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -11394,7 +11418,7 @@ static virDomainSnapshotPtr qemuDomainSnapshotCreateXML(virDomainPtr domain,
const char *qemuimgarg[] = { NULL, "snapshot", "-c", NULL, NULL, NULL };
int i;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
qemuDriverLock(driver);
virUUIDFormat(domain->uuid, uuidstr);
@@ -11514,7 +11538,7 @@ static int qemuDomainSnapshotListNames(virDomainPtr domain, char **names,
virDomainObjPtr vm = NULL;
int n = -1;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, domain->uuid);
@@ -11542,7 +11566,7 @@ static int qemuDomainSnapshotNum(virDomainPtr domain,
virDomainObjPtr vm = NULL;
int n = -1;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, domain->uuid);
@@ -11572,7 +11596,7 @@ static virDomainSnapshotPtr qemuDomainSnapshotLookupByName(virDomainPtr domain,
virDomainSnapshotObjPtr snap = NULL;
virDomainSnapshotPtr snapshot = NULL;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, domain->uuid);
@@ -11607,7 +11631,7 @@ static int qemuDomainHasCurrentSnapshot(virDomainPtr domain,
virDomainObjPtr vm;
int ret = -1;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, domain->uuid);
@@ -11635,7 +11659,7 @@ static virDomainSnapshotPtr qemuDomainSnapshotCurrent(virDomainPtr domain,
virDomainObjPtr vm;
virDomainSnapshotPtr snapshot = NULL;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, domain->uuid);
@@ -11671,7 +11695,7 @@ static char *qemuDomainSnapshotDumpXML(virDomainSnapshotPtr snapshot,
virDomainSnapshotObjPtr snap = NULL;
char uuidstr[VIR_UUID_STRING_BUFLEN];
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
qemuDriverLock(driver);
virUUIDFormat(snapshot->domain->uuid, uuidstr);
@@ -11711,7 +11735,7 @@ static int qemuDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
qemuDomainObjPrivatePtr priv;
int rc;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
qemuDriverLock(driver);
virUUIDFormat(snapshot->domain->uuid, uuidstr);
@@ -11949,7 +11973,7 @@ static int qemuDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
char uuidstr[VIR_UUID_STRING_BUFLEN];
struct snap_remove rem;
- virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN, -1);
+ virCheckFlagsUI(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN, -1);
qemuDriverLock(driver);
virUUIDFormat(snapshot->domain->uuid, uuidstr);
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index 3df078a..66988aa 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -1134,6 +1134,7 @@ static int qemuMonitorTextMigrate(qemuMonitorPtr mon,
int ret = -1;
char *safedest = qemuMonitorEscapeArg(dest);
virBuffer extra = VIR_BUFFER_INITIALIZER;
+ char *extrastr = NULL;
if (!safedest) {
virReportOOMError();
@@ -1149,10 +1150,12 @@ static int qemuMonitorTextMigrate(qemuMonitorPtr mon,
if (virBufferError(&extra)) {
virBufferFreeAndReset(&extra);
virReportOOMError();
- free(safedest);
- return -1;
+ goto cleanup;
}
- if (virAsprintf(&cmd, "migrate %s\"%s\"", virBufferContentAndReset(&extra), safedest) < 0) {
+
+ extrastr = virBufferContentAndReset(&extra);
+ if (virAsprintf(&cmd, "migrate %s\"%s\"", extrastr ? extrastr : "",
+ safedest) < 0) {
virReportOOMError();
goto cleanup;
}
@@ -1181,6 +1184,7 @@ static int qemuMonitorTextMigrate(qemuMonitorPtr mon,
ret = 0;
cleanup:
+ VIR_FREE(extrastr);
VIR_FREE(safedest);
VIR_FREE(info);
VIR_FREE(cmd);
diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c
index b148e39..27aaf86 100644
--- a/src/storage/storage_driver.c
+++ b/src/storage/storage_driver.c
@@ -1681,7 +1681,7 @@ storageVolumeWipe(virStorageVolPtr obj,
virStorageVolDefPtr vol = NULL;
int ret = -1;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
storageDriverLock(driver);
pool = virStoragePoolObjFindByName(&driver->pools, obj->pool);
diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c
index 6a9a2bf..ce4d659 100644
--- a/src/vbox/vbox_tmpl.c
+++ b/src/vbox/vbox_tmpl.c
@@ -4877,9 +4877,9 @@ static int vboxDomainAttachDeviceFlags(virDomainPtr dom, const char *xml,
static int vboxDomainUpdateDeviceFlags(virDomainPtr dom, const char *xml,
unsigned int flags) {
- virCheckFlags(VIR_DOMAIN_DEVICE_MODIFY_CURRENT |
- VIR_DOMAIN_DEVICE_MODIFY_LIVE |
- VIR_DOMAIN_DEVICE_MODIFY_CONFIG, -1);
+ virCheckFlagsUI(VIR_DOMAIN_DEVICE_MODIFY_CURRENT |
+ VIR_DOMAIN_DEVICE_MODIFY_LIVE |
+ VIR_DOMAIN_DEVICE_MODIFY_CONFIG, -1);
if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
vboxError(VIR_ERR_OPERATION_INVALID, "%s",
@@ -5189,7 +5189,7 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom,
PRInt32 result;
#endif
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
if (!(def = virDomainSnapshotDefParseString(xmlDesc, 1)))
goto cleanup;
@@ -5304,7 +5304,7 @@ vboxDomainSnapshotDumpXML(virDomainSnapshotPtr snapshot,
PRBool online = PR_FALSE;
char uuidstr[VIR_UUID_STRING_BUFLEN];
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
#if VBOX_API_VERSION == 2002
if (VIR_ALLOC(domiid) < 0) {
@@ -5415,7 +5415,7 @@ vboxDomainSnapshotNum(virDomainPtr dom,
nsresult rc;
PRUint32 snapshotCount;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
#if VBOX_API_VERSION == 2002
if (VIR_ALLOC(iid) < 0) {
@@ -5462,7 +5462,7 @@ vboxDomainSnapshotListNames(virDomainPtr dom,
int count = 0;
int i;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
#if VBOX_API_VERSION == 2002
if (VIR_ALLOC(iid) < 0) {
@@ -5532,7 +5532,7 @@ vboxDomainSnapshotLookupByName(virDomainPtr dom,
ISnapshot *snapshot = NULL;
nsresult rc;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
#if VBOX_API_VERSION == 2002
if (VIR_ALLOC(iid) < 0) {
@@ -5571,7 +5571,7 @@ vboxDomainHasCurrentSnapshot(virDomainPtr dom,
ISnapshot *snapshot = NULL;
nsresult rc;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
#if VBOX_API_VERSION == 2002
if (VIR_ALLOC(iid) < 0) {
@@ -5618,7 +5618,7 @@ vboxDomainSnapshotCurrent(virDomainPtr dom,
char *name = NULL;
nsresult rc;
- virCheckFlags(0, NULL);
+ virCheckFlagsUI(0, NULL);
#if VBOX_API_VERSION == 2002
if (VIR_ALLOC(iid) < 0) {
@@ -5793,7 +5793,7 @@ vboxDomainRevertToSnapshot(virDomainSnapshotPtr snapshot,
PRUint32 state;
nsresult rc;
- virCheckFlags(0, -1);
+ virCheckFlagsUI(0, -1);
#if VBOX_API_VERSION == 2002
if (VIR_ALLOC(domiid) < 0) {
@@ -5961,7 +5961,7 @@ vboxDomainSnapshotDelete(virDomainSnapshotPtr snapshot,
PRUint32 state;
nsresult rc;
- virCheckFlags(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN, -1);
+ virCheckFlagsUI(VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN, -1);
#if VBOX_API_VERSION == 2002
if (VIR_ALLOC(domiid) < 0) {
diff --git a/src/xen/xend_internal.c b/src/xen/xend_internal.c
index a203a8d..f1267cd 100644
--- a/src/xen/xend_internal.c
+++ b/src/xen/xend_internal.c
@@ -4205,9 +4205,9 @@ xenDaemonUpdateDeviceFlags(virDomainPtr domain, const char *xml,
virBuffer buf = VIR_BUFFER_INITIALIZER;
char class[8], ref[80];
- virCheckFlags(VIR_DOMAIN_DEVICE_MODIFY_CURRENT |
- VIR_DOMAIN_DEVICE_MODIFY_LIVE |
- VIR_DOMAIN_DEVICE_MODIFY_CONFIG, -1);
+ virCheckFlagsUI(VIR_DOMAIN_DEVICE_MODIFY_CURRENT |
+ VIR_DOMAIN_DEVICE_MODIFY_LIVE |
+ VIR_DOMAIN_DEVICE_MODIFY_CONFIG, -1);
if ((domain == NULL) || (domain->conn == NULL) || (domain->name == NULL)) {
virXendError(VIR_ERR_INVALID_ARG, __FUNCTION__);
--
1.6.6.1
14 years, 7 months
[libvirt] [PATCH] Domain snapshot RNG and tests.
by Chris Lalancette
Signed-off-by: Chris Lalancette <clalance(a)redhat.com>
---
docs/schemas/domainsnapshot.rng | 53 ++++++++++++++++++++
tests/Makefile.am | 6 ++-
tests/domainsnapshotschematest | 10 ++++
tests/domainsnapshotxml2xmlin/description_only.xml | 3 +
tests/domainsnapshotxml2xmlin/empty.xml | 1 +
.../name_and_description.xml | 4 ++
tests/domainsnapshotxml2xmlin/name_only.xml | 3 +
tests/domainsnapshotxml2xmlout/all_parameters.xml | 13 +++++
tests/domainsnapshotxml2xmlout/noparent.xml | 9 +++
.../noparent_nodescription.xml | 7 +++
.../noparent_nodescription_noactive.xml | 6 ++
11 files changed, 114 insertions(+), 1 deletions(-)
create mode 100644 docs/schemas/domainsnapshot.rng
create mode 100755 tests/domainsnapshotschematest
create mode 100644 tests/domainsnapshotxml2xmlin/description_only.xml
create mode 100644 tests/domainsnapshotxml2xmlin/empty.xml
create mode 100644 tests/domainsnapshotxml2xmlin/name_and_description.xml
create mode 100644 tests/domainsnapshotxml2xmlin/name_only.xml
create mode 100644 tests/domainsnapshotxml2xmlout/all_parameters.xml
create mode 100644 tests/domainsnapshotxml2xmlout/noparent.xml
create mode 100644 tests/domainsnapshotxml2xmlout/noparent_nodescription.xml
create mode 100644 tests/domainsnapshotxml2xmlout/noparent_nodescription_noactive.xml
diff --git a/docs/schemas/domainsnapshot.rng b/docs/schemas/domainsnapshot.rng
new file mode 100644
index 0000000..86bab0b
--- /dev/null
+++ b/docs/schemas/domainsnapshot.rng
@@ -0,0 +1,53 @@
+<!-- A Relax NG schema for the libvirt domain snapshot properties XML format -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0">
+ <start>
+ <ref name='domainsnapshot'/>
+ </start>
+
+ <define name='domainsnapshot'>
+ <element name='domainsnapshot'>
+ <interleave>
+ <optional>
+ <element name='name'>
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name='description'>
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name='state'>
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name='creationTime'>
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name='active'>
+ <text/>
+ </element>
+ </optional>
+ <optional>
+ <element name='domain'>
+ <element name='uuid'>
+ <text/>
+ </element>
+ </element>
+ </optional>
+ <optional>
+ <element name='parent'>
+ <element name='name'>
+ <text/>
+ </element>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
+</grammar>
diff --git a/tests/Makefile.am b/tests/Makefile.am
index c5e52e3..caf8cd0 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -76,6 +76,9 @@ EXTRA_DIST = \
nwfilterxml2xmlout \
nwfilterxml2xmlin \
nwfilterschematest \
+ domainsnapshotschematest \
+ domainsnapshotxml2xmlout \
+ domainsnapshotxml2xmlin \
$(patsubst %,qemuhelpdata/%,$(qemuhelpdata))
noinst_PROGRAMS = virshtest conftest \
@@ -123,7 +126,8 @@ test_scripts = \
storagevolschematest \
domainschematest \
nodedevschematest \
- nwfilterschematest
+ nwfilterschematest \
+ domainsnapshotschematest
if WITH_LIBVIRTD
test_scripts += \
diff --git a/tests/domainsnapshotschematest b/tests/domainsnapshotschematest
new file mode 100755
index 0000000..1bdc539
--- /dev/null
+++ b/tests/domainsnapshotschematest
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+: ${srcdir=.}
+. $srcdir/test-lib.sh
+. $abs_srcdir/schematestutils.sh
+
+DIRS="domainsnapshotxml2xmlin domainsnapshotxml2xmlout"
+SCHEMA="domainsnapshot.rng"
+
+check_schema "$DIRS" "$SCHEMA"
diff --git a/tests/domainsnapshotxml2xmlin/description_only.xml b/tests/domainsnapshotxml2xmlin/description_only.xml
new file mode 100644
index 0000000..b76fca2
--- /dev/null
+++ b/tests/domainsnapshotxml2xmlin/description_only.xml
@@ -0,0 +1,3 @@
+<domainsnapshot>
+ <description>My description</description>
+</domainsnapshot>
diff --git a/tests/domainsnapshotxml2xmlin/empty.xml b/tests/domainsnapshotxml2xmlin/empty.xml
new file mode 100644
index 0000000..f2bcfbe
--- /dev/null
+++ b/tests/domainsnapshotxml2xmlin/empty.xml
@@ -0,0 +1 @@
+<domainsnapshot/>
diff --git a/tests/domainsnapshotxml2xmlin/name_and_description.xml b/tests/domainsnapshotxml2xmlin/name_and_description.xml
new file mode 100644
index 0000000..90ce774
--- /dev/null
+++ b/tests/domainsnapshotxml2xmlin/name_and_description.xml
@@ -0,0 +1,4 @@
+<domainsnapshot>
+ <name>snap1</name>
+ <description>A longer description!</description>
+</domainsnapshot>
diff --git a/tests/domainsnapshotxml2xmlin/name_only.xml b/tests/domainsnapshotxml2xmlin/name_only.xml
new file mode 100644
index 0000000..8123a02
--- /dev/null
+++ b/tests/domainsnapshotxml2xmlin/name_only.xml
@@ -0,0 +1,3 @@
+<domainsnapshot>
+ <name>snapshot1</name>
+</domainsnapshot>
diff --git a/tests/domainsnapshotxml2xmlout/all_parameters.xml b/tests/domainsnapshotxml2xmlout/all_parameters.xml
new file mode 100644
index 0000000..ed4a600
--- /dev/null
+++ b/tests/domainsnapshotxml2xmlout/all_parameters.xml
@@ -0,0 +1,13 @@
+<domainsnapshot>
+ <name>my snap name</name>
+ <description>!@#$%^</description>
+ <parent>
+ <name>earlier_snap</name>
+ </parent>
+ <state>running</state>
+ <creationTime>1272917631</creationTime>
+ <domain>
+ <uuid>9d37b878-a7cc-9f9a-b78f-49b3abad25a8</uuid>
+ </domain>
+ <active>1</active>
+</domainsnapshot>
diff --git a/tests/domainsnapshotxml2xmlout/noparent.xml b/tests/domainsnapshotxml2xmlout/noparent.xml
new file mode 100644
index 0000000..cbac0d8
--- /dev/null
+++ b/tests/domainsnapshotxml2xmlout/noparent.xml
@@ -0,0 +1,9 @@
+<domainsnapshot>
+ <name>my snap name</name>
+ <description>!@#$%^</description>
+ <state>running</state>
+ <creationTime>1272917631</creationTime>
+ <domain>
+ <uuid>9d37b878-a7cc-9f9a-b78f-49b3abad25a8</uuid>
+ </domain>
+</domainsnapshot>
diff --git a/tests/domainsnapshotxml2xmlout/noparent_nodescription.xml b/tests/domainsnapshotxml2xmlout/noparent_nodescription.xml
new file mode 100644
index 0000000..0de202d
--- /dev/null
+++ b/tests/domainsnapshotxml2xmlout/noparent_nodescription.xml
@@ -0,0 +1,7 @@
+<domainsnapshot>
+ <name>my snap name</name>
+ <description>!@#$%^</description>
+ <state>running</state>
+ <creationTime>1272917631</creationTime>
+ <active>1</active>
+</domainsnapshot>
diff --git a/tests/domainsnapshotxml2xmlout/noparent_nodescription_noactive.xml b/tests/domainsnapshotxml2xmlout/noparent_nodescription_noactive.xml
new file mode 100644
index 0000000..25b60f3
--- /dev/null
+++ b/tests/domainsnapshotxml2xmlout/noparent_nodescription_noactive.xml
@@ -0,0 +1,6 @@
+<domainsnapshot>
+ <name>my snap name</name>
+ <description>!@#$%^</description>
+ <state>running</state>
+ <creationTime>1272917631</creationTime>
+</domainsnapshot>
--
1.6.6.1
14 years, 7 months
[libvirt] [PATCH v2] storage: Sanitize pool target paths
by Cole Robinson
Spurious / in a pool target path makes life difficult for apps using the
GetVolByPath, and doing other path based comparisons with pools. This
has caused a few issues for virt-manager users:
https://bugzilla.redhat.com/show_bug.cgi?id=494005
https://bugzilla.redhat.com/show_bug.cgi?id=593565
Add a new util API which removes spurious /, virFileSanitizePath. Sanitize
target paths when parsing pool XML, and for paths passed to GetVolByPath.
v2: Leading // must be preserved, properly sanitize path=/, sanitize
away /./ -> /
Signed-off-by: Cole Robinson <crobinso(a)redhat.com>
---
src/conf/storage_conf.c | 8 ++++++-
src/libvirt_private.syms | 1 +
src/storage/storage_driver.c | 8 ++++++-
src/util/util.c | 49 ++++++++++++++++++++++++++++++++++++++++++
src/util/util.h | 3 ++
5 files changed, 67 insertions(+), 2 deletions(-)
diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c
index 9aad081..422e76a 100644
--- a/src/conf/storage_conf.c
+++ b/src/conf/storage_conf.c
@@ -602,6 +602,7 @@ virStoragePoolDefParseXML(xmlXPathContextPtr ctxt) {
xmlNodePtr source_node;
char *type = NULL;
char *uuid = NULL;
+ char *tmppath;
if (VIR_ALLOC(ret) < 0) {
virReportOOMError();
@@ -699,11 +700,16 @@ virStoragePoolDefParseXML(xmlXPathContextPtr ctxt) {
}
}
- if ((ret->target.path = virXPathString("string(./target/path)", ctxt)) == NULL) {
+ if ((tmppath = virXPathString("string(./target/path)", ctxt)) == NULL) {
virStorageReportError(VIR_ERR_XML_ERROR,
"%s", _("missing storage pool target path"));
goto cleanup;
}
+ ret->target.path = virFileSanitizePath(tmppath);
+ VIR_FREE(tmppath);
+ if (!ret->target.path)
+ goto cleanup;
+
if (virStorageDefParsePerms(ctxt, &ret->target.perms,
"./target/permissions", 0700) < 0)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 1594a08..2c74b08 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -667,6 +667,7 @@ virFileReadLimFD;
virFilePid;
virFileReadPid;
virFileLinkPointsTo;
+virFileSanitizePath;
virParseNumber;
virParseVersionString;
virPipeReadUntilEOF;
diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c
index b148e39..0870f74 100644
--- a/src/storage/storage_driver.c
+++ b/src/storage/storage_driver.c
@@ -1204,6 +1204,11 @@ storageVolumeLookupByPath(virConnectPtr conn,
virStorageDriverStatePtr driver = conn->storagePrivateData;
unsigned int i;
virStorageVolPtr ret = NULL;
+ char *cleanpath;
+
+ cleanpath = virFileSanitizePath(path);
+ if (!cleanpath)
+ return NULL;
storageDriverLock(driver);
for (i = 0 ; i < driver->pools.count && !ret ; i++) {
@@ -1213,7 +1218,7 @@ storageVolumeLookupByPath(virConnectPtr conn,
const char *stable_path;
stable_path = virStorageBackendStablePath(driver->pools.objs[i],
- path);
+ cleanpath);
/*
* virStorageBackendStablePath already does
* virStorageReportError if it fails; we just need to keep
@@ -1242,6 +1247,7 @@ storageVolumeLookupByPath(virConnectPtr conn,
"%s", _("no storage vol with matching path"));
cleanup:
+ VIR_FREE(cleanpath);
storageDriverUnlock(driver);
return ret;
}
diff --git a/src/util/util.c b/src/util/util.c
index 92b9a0f..b141406 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -1921,6 +1921,55 @@ int virFileAbsPath(const char *path, char **abspath)
return 0;
}
+/* Remove spurious / characters from a path. The result must be freed */
+char *
+virFileSanitizePath(const char *path)
+{
+ const char *cur = path;
+ char *cleanpath;
+ int idx = 0;
+
+ cleanpath = strdup(path);
+ if (!cleanpath) {
+ virReportOOMError();
+ return NULL;
+ }
+
+ /* Starting with // is valid posix, but ///foo == /foo */
+ if (cur[0] == '/' && cur[1] == '/' && cur[2] != '/') {
+ cleanpath[0] = '/';
+ cleanpath[1] = '/';
+ idx = 2;
+ cur += 2;
+ }
+
+ /* Sanitize path in place */
+ while (*cur != '\0') {
+ if (*cur != '/') {
+ cleanpath[idx++] = *cur++;
+ continue;
+ }
+
+ /* Skip all extra / */
+ while (*cur == '/') {
+ cur++;
+
+ /* Resolve away /./ to just / */
+ if (cur[0] == '.' && cur[1] == '/')
+ cur++;
+ }
+
+ /* Don't add a trailing / unless path is only made of / */
+ if (idx != 0 && *cur == '\0')
+ break;
+
+ cleanpath[idx++] = '/';
+ }
+ cleanpath[idx] = '\0';
+
+ return cleanpath;
+}
+
/* Like strtol, but produce an "int" result, and check more carefully.
Return 0 upon success; return -1 to indicate failure.
When END_PTR is NULL, the byte after the final valid digit must be NUL.
diff --git a/src/util/util.h b/src/util/util.h
index 6bf6bcc..302bddf 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -118,6 +118,9 @@ char *virFindFileInPath(const char *file);
int virFileExists(const char *path);
+char *virFileSanitizePath(const char *path)
+ATTRIBUTE_NONNULL(1);
+
enum {
VIR_FILE_OP_NONE = 0,
VIR_FILE_OP_AS_UID = (1 << 0),
--
1.6.6.1
14 years, 7 months
[libvirt] [PATCH] network: bridge: Don't start network if it collides with host routing
by Cole Robinson
Fedora bug https://bugzilla.redhat.com/show_bug.cgi?id=235961
If using the default virtual network, an easy way to lose guest network
connectivity is to install libvirt inside the VM. The autostarted
default network inside the guest collides with host virtual network
routing. This is a long standing issue that has caused users quite a
bit of pain and confusion.
On network startup, parse /proc/net/route and compare the requested
IP+netmask against host routing destinations: if any matches are found,
refuse to start the network.
Caveat: I'm not very networking savvy
Signed-off-by: Cole Robinson <crobinso(a)redhat.com>
---
src/network/bridge_driver.c | 92 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 92 insertions(+), 0 deletions(-)
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 5d7ef19..79da757 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -42,6 +42,8 @@
#include <stdio.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
#include "virterror_internal.h"
#include "datatypes.h"
@@ -908,6 +910,92 @@ cleanup:
return ret;
}
+#define PROC_NET_ROUTE "/proc/net/route"
+
+static int networkCheckRouteCollision(virNetworkObjPtr network)
+{
+ int ret = -1, len;
+ char *cur, *buf = NULL;
+ enum {MAX_ROUTE_SIZE = 1024*64};
+ struct in_addr inaddress, innetmask;
+ char *netaddr = NULL;
+
+ if (!network->def->ipAddress || !network->def->netmask)
+ return 0;
+
+ if (inet_pton(AF_INET, network->def->ipAddress, &inaddress) <= 0) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse IP address '%s'"),
+ network->def->ipAddress);
+ goto error;
+ }
+ if (inet_pton(AF_INET, network->def->netmask, &innetmask) <= 0) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse netmask '%s'"),
+ network->def->netmask);
+ goto error;
+ }
+
+ inaddress.s_addr &= innetmask.s_addr;
+ netaddr = strdup(inet_ntoa(inaddress));
+ if (!netaddr) {
+ virReportOOMError();
+ goto error;
+ }
+
+ /* Read whole routing table into memory */
+ if ((len = virFileReadAll(PROC_NET_ROUTE, MAX_ROUTE_SIZE, &buf)) < 0)
+ goto error;
+
+ /* Dropping the last character shouldn't hurt */
+ buf[len-1] = '\0';
+
+ /* First line is just headings, skip it */
+ cur = strchr(buf, '\n');
+
+ while (cur) {
+ char iface[17];
+ char dest_buf[128];
+ char *dest_ip;
+ struct in_addr in;
+ int num;
+ unsigned int addr_val;
+
+ cur++;
+ num = sscanf(cur, "%16s %127s", iface, dest_buf);
+ if (num != 2) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Failed to parse %s"), PROC_NET_ROUTE);
+ goto error;
+ }
+
+ if (virStrToLong_ui(dest_buf, NULL, 16, &addr_val) < 0) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Failed not convert network address %s"),
+ dest_buf);
+ goto error;
+ }
+
+ in.s_addr = addr_val;
+ dest_ip = inet_ntoa(in);
+
+ if (STREQ(netaddr, dest_ip)) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Network destination %s is already in use "
+ "by interface %s"), netaddr, iface);
+ goto error;
+ }
+
+ cur = strchr(cur, '\n');
+ }
+
+ ret = 0;
+error:
+ VIR_FREE(buf);
+ VIR_FREE(netaddr);
+ return ret;
+}
+
static int networkStartNetworkDaemon(struct network_driver *driver,
virNetworkObjPtr network)
{
@@ -919,6 +1007,10 @@ static int networkStartNetworkDaemon(struct network_driver *driver,
return -1;
}
+ /* Check to see if network collides with an existing route */
+ if (networkCheckRouteCollision(network) < 0)
+ return -1;
+
if ((err = brAddBridge(driver->brctl, network->def->bridge))) {
virReportSystemError(err,
_("cannot create bridge '%s'"),
--
1.6.6.1
14 years, 7 months
[libvirt] [RFC PATCH v3] qemu: Add suport for pci-assign.configfd option
by Alex Williamson
This allows libvirt to open the PCI device sysfs config file prior
to dropping privileges so qemu can access the full config space.
Without this, a de-privileged qemu can only access the first 64
bytes of config space.
Signed-off-by: Alex Williamson <alex.williamson(a)redhat.com>
---
Note: this is still dependent on the configfd option being accepted
into qemu/kvm.
v2:
- fix qemuargs, two separate args (thanks Chris)
- don't hardcode pci segment
- error message of open() fail
v3:
- add hotplug support
- incorporate Daniel's comments
src/qemu/qemu_conf.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++-
src/qemu/qemu_conf.h | 6 ++-
src/qemu/qemu_driver.c | 28 +++++++++++++-
3 files changed, 128 insertions(+), 4 deletions(-)
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 2755545..926ce48 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -1350,6 +1350,48 @@ fail:
return -1;
}
+static void qemudParsePCIDeviceStrs(const char *qemu, unsigned long long *flags)
+{
+ const char *const qemuarg[] = { qemu, "-device", "pci-assign,?", NULL };
+ const char *const qemuenv[] = { "LC_ALL=C", NULL };
+ pid_t child;
+ int status;
+ int newstderr = -1;
+
+ if (virExec(qemuarg, qemuenv, NULL,
+ &child, -1, NULL, &newstderr, VIR_EXEC_CLEAR_CAPS) < 0)
+ return;
+
+ char *pciassign = NULL;
+ enum { MAX_PCI_OUTPUT_SIZE = 1024*4 };
+ int len = virFileReadLimFD(newstderr, MAX_PCI_OUTPUT_SIZE, &pciassign);
+ if (len < 0) {
+ virReportSystemError(errno,
+ _("Unable to read %s pci-assign device output"),
+ qemu);
+ goto cleanup;
+ }
+
+ if (strstr(pciassign, "pci-assign.configfd"))
+ *flags |= QEMUD_CMD_FLAG_PCI_CONFIGFD;
+
+cleanup:
+ VIR_FREE(pciassign);
+ close(newstderr);
+rewait:
+ if (waitpid(child, &status, 0) != child) {
+ if (errno == EINTR)
+ goto rewait;
+
+ VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"),
+ WEXITSTATUS(status), (unsigned long)child);
+ }
+ if (WEXITSTATUS(status) != 0) {
+ VIR_WARN("Unexpected exit status '%d', qemu probably failed",
+ WEXITSTATUS(status));
+ }
+}
+
int qemudExtractVersionInfo(const char *qemu,
unsigned int *retversion,
unsigned long long *retflags) {
@@ -1383,6 +1425,9 @@ int qemudExtractVersionInfo(const char *qemu,
&version, &is_kvm, &kvm_version) == -1)
goto cleanup2;
+ if (flags & QEMUD_CMD_FLAG_DEVICE)
+ qemudParsePCIDeviceStrs(qemu, &flags);
+
if (retversion)
*retversion = version;
if (retflags)
@@ -2892,8 +2937,33 @@ error:
}
+int
+qemudOpenPCIConfig(virDomainHostdevDefPtr dev)
+{
+ char *path = NULL;
+ int configfd = -1;
+
+ if (virAsprintf(&path, "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/config",
+ dev->source.subsys.u.pci.domain,
+ dev->source.subsys.u.pci.bus,
+ dev->source.subsys.u.pci.slot,
+ dev->source.subsys.u.pci.function) < 0) {
+ virReportOOMError();
+ return -1;
+ }
+
+ configfd = open(path, O_RDWR, 0);
+
+ if (configfd < 0)
+ virReportSystemError(errno, _("Failed opening %s"), path);
+
+ VIR_FREE(path);
+
+ return configfd;
+}
+
char *
-qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev)
+qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev, const char *configfd)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
@@ -2903,6 +2973,8 @@ qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev)
dev->source.subsys.u.pci.slot,
dev->source.subsys.u.pci.function);
virBufferVSprintf(&buf, ",id=%s", dev->info.alias);
+ if (configfd && *configfd)
+ virBufferVSprintf(&buf, ",configfd=%s", configfd);
if (qemuBuildDeviceAddressStr(&buf, &dev->info) < 0)
goto error;
@@ -4605,8 +4677,30 @@ int qemudBuildCommandLine(virConnectPtr conn,
if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ char *configfd_name = NULL;
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_PCI_CONFIGFD) {
+ int configfd = qemudOpenPCIConfig(hostdev);
+
+ if (configfd >= 0) {
+ if (virAsprintf(&configfd_name, "%d", configfd) < 0) {
+ close(configfd);
+ virReportOOMError();
+ goto no_memory;
+ }
+
+ if (VIR_REALLOC_N(*vmfds, (*nvmfds)+1) < 0) {
+ VIR_FREE(configfd_name);
+ close(configfd);
+ goto no_memory;
+ }
+
+ (*vmfds)[(*nvmfds)++] = configfd;
+ }
+ }
ADD_ARG_LIT("-device");
- if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev)))
+ devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd_name);
+ VIR_FREE(configfd_name);
+ if (!devstr)
goto error;
ADD_ARG(devstr);
} else if (qemuCmdFlags & QEMUD_CMD_FLAG_PCIDEVICE) {
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index 8fd8d79..d5ce4ce 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -89,6 +89,7 @@ enum qemud_cmd_flags {
QEMUD_CMD_FLAG_NO_HPET = (1LL << 33), /* -no-hpet flag is supported */
QEMUD_CMD_FLAG_NO_KVM_PIT = (1LL << 34), /* -no-kvm-pit-reinjection supported */
QEMUD_CMD_FLAG_TDF = (1LL << 35), /* -tdf flag (user-mode pit catchup) */
+ QEMUD_CMD_FLAG_PCI_CONFIGFD = (1LL << 36), /* pci-assign.configfd */
};
/* Main driver state */
@@ -245,7 +246,10 @@ char * qemuBuildSoundDevStr(virDomainSoundDefPtr sound);
/* Legacy, pre device support */
char * qemuBuildPCIHostdevPCIDevStr(virDomainHostdevDefPtr dev);
/* Current, best practice */
-char * qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev);
+char * qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev,
+ const char *configfd);
+
+int qemudOpenPCIConfig(virDomainHostdevDefPtr dev);
/* Current, best practice */
char * qemuBuildChrChardevStr(virDomainChrDefPtr dev);
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index afdc718..52f2374 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -7655,6 +7655,8 @@ static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
pciDevice *pci;
int ret;
char *devstr = NULL;
+ int configfd = -1;
+ char *configfd_name = NULL;
if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) {
virReportOOMError();
@@ -7685,8 +7687,26 @@ static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
goto error;
if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &hostdev->info) < 0)
goto error;
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_PCI_CONFIGFD) {
+ configfd = qemudOpenPCIConfig(hostdev);
+ if (configfd >= 0) {
+ if (virAsprintf(&configfd_name, "fd-%s",
+ hostdev->info.alias) < 0) {
+ virReportOOMError();
+ goto error;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuMonitorSendFileHandle(priv->mon, configfd_name,
+ configfd) < 0) {
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ goto error;
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ }
+ }
- if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev)))
+ if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd_name)))
goto error;
qemuDomainObjEnterMonitorWithDriver(driver, vm);
@@ -7710,6 +7730,9 @@ static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
vm->def->hostdevs[vm->def->nhostdevs++] = hostdev;
VIR_FREE(devstr);
+ VIR_FREE(configfd_name);
+ if (configfd >= 0)
+ close(configfd);
return 0;
@@ -7721,6 +7744,9 @@ error:
VIR_FREE(devstr);
pciDeviceListDel(driver->activePciHostdevs, pci);
+ VIR_FREE(configfd_name);
+ if (configfd >= 0)
+ close(configfd);
return -1;
}
14 years, 7 months
[libvirt] [v2 PATCH] add 802.1Qbh handling for port-profiles based on Stefan's previous patches
by Scott Feldman
From: Scott Feldman <scofeldm(a)cisco.com>
This patch builds on the work recently posted by Stefan Berger. It builds
on top of Stefan's two posted patches:
[PATCH v8] vepa: parsing for 802.1Qb{g|h} XML
[RFC][PATCH 1/3] vepa+vsi: Introduce dependency on libnl
Stefan's patches 2/3 and 3/3 are incorporated into my patch.
Changes from v1 to v2:
- Add Stefan's code for getPortProfileStatus
- Poll for up to 2 secs for port-profile status, at 1/8 sec intervals:
- if status indicates error, abort openMacvtapTap
- if status indicates success, exit polling
- if status is "in-progress" after 2 secs of polling, exit
polling loop silently, without error
My patch finishes out the 802.1Qbh parts, which Stefan had mostly complete.
I've tested using the recent kernel updates for VF_PORT netlink msgs and
enic for Cisco's 10G Ethernet NIC. I tested many VMs, each with several
direct interfaces, each configured with a port-profile per the XML. VM-to-VM,
and VM-to-external work as expected. VM-to-VM on same host (using same NIC)
works same as VM-to-VM where VMs are on diff hosts. I'm able to change
settings on the port-profile while the VM is running to change the virtual
port behaviour. For example, adjusting a QoS setting like rate limit. All
VMs with interfaces using that port-profile immediatly see the effect of the
change to the port-profile.
I don't have a SR-IOV device to test so source dev is a non-SR-IOV device,
but most of the code paths include support for specifing the source dev and
VF index. We'll need to complete this by discovering the PF given the VF
linkdev. Once we have the PF, we'll also have the VF index. All this info-
mation is available from sysfs.
Signed-off-by: Scott Feldman <scofeldm(a)cisco.com>
---
configure.ac | 16 +
src/util/macvtap.c | 778 +++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 778 insertions(+), 16 deletions(-)
diff --git a/configure.ac b/configure.ac
index 36ba703..885b0ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2005,13 +2005,26 @@ if test "$with_macvtap" != "no" ; then
fi
AM_CONDITIONAL([WITH_MACVTAP], [test "$with_macvtap" = "yes"])
+AC_TRY_COMPILE([ #include <sys/socket.h>
+ #include <linux/rtnetlink.h> ],
+ [ int x = IFLA_PORT_MAX; ],
+ [ with_virtualport=yes ],
+ [ with_virtualport=no ])
+if test "$with_virtualport" = "yes"; then
+ val=1
+else
+ val=0
+fi
+AC_DEFINE_UNQUOTED([WITH_VIRTUALPORT], $val, [whether vsi vepa support is enabled])
+AM_CONDITIONAL([WITH_VIRTUALPORT], [test "$with_virtualport" = "yes"])
+
dnl netlink library
LIBNL_CFLAGS=""
LIBNL_LIBS=""
-if test "$with_macvtap" = "yes"; then
+if test "$with_macvtap" = "yes" || "$with_virtualport" = "yes"; then
PKG_CHECK_MODULES([LIBNL], [libnl-1 >= $LIBNL_REQUIRED], [
], [
AC_MSG_ERROR([libnl >= $LIBNL_REQUIRED is required for macvtap support])
@@ -2084,6 +2097,7 @@ AC_MSG_NOTICE([ Network: $with_network])
AC_MSG_NOTICE([Libvirtd: $with_libvirtd])
AC_MSG_NOTICE([ netcf: $with_netcf])
AC_MSG_NOTICE([ macvtap: $with_macvtap])
+AC_MSG_NOTICE([virtport: $with_virtualport])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Storage Drivers])
AC_MSG_NOTICE([])
diff --git a/src/util/macvtap.c b/src/util/macvtap.c
index da81447..9ecc7f1 100644
--- a/src/util/macvtap.c
+++ b/src/util/macvtap.c
@@ -27,7 +27,7 @@
#include <config.h>
-#if WITH_MACVTAP
+#if WITH_MACVTAP || WITH_VIRTUALPORT
# include <stdio.h>
# include <errno.h>
@@ -41,6 +41,8 @@
# include <linux/rtnetlink.h>
# include <linux/if_tun.h>
+# include <netlink/msg.h>
+
# include "util.h"
# include "memory.h"
# include "logging.h"
@@ -58,15 +60,24 @@
# define MACVTAP_NAME_PREFIX "macvtap"
# define MACVTAP_NAME_PATTERN "macvtap%d"
+# define MICROSEC_PER_SEC (1000 * 1000)
+
static int associatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort,
- int vf,
- const unsigned char *vmuuid);
+ const unsigned char *vmuuid,
+ const unsigned char *hostuuid);
static int disassociatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort);
+enum virVirtualPortOp {
+ ASSOCIATE = 0x1,
+ DISASSOCIATE = 0x2,
+};
+
static int nlOpen(void)
{
@@ -159,6 +170,169 @@ err_exit:
}
+# ifdef IFLA_VF_PORT_MAX
+
+/**
+ * nlCommWaitSuccess:
+ *
+ * @nlmsg: pointer to netlink message
+ * @nl_grousp: the netlink multicast groups to send to
+ * @respbuf: pointer to pointer where response buffer will be allocated
+ * @respbuflen: pointer to integer holding the size of the response buffer
+ * on return of the function.
+ * @to_usecs: timeout in microseconds to wait for a success message
+ * to be returned
+ *
+ * Send the given message to the netlink multicast group and receive
+ * responses. Skip responses indicating an error and keep on receiving
+ * responses until a success response is returned.
+ * Returns 0 on success, -1 on error. In case of error, no response
+ * buffer will be returned.
+ */
+static int
+nlCommWaitSuccess(struct nlmsghdr *nlmsg, int nl_groups,
+ char **respbuf, int *respbuflen, long to_usecs)
+{
+ int rc = 0;
+ struct sockaddr_nl nladdr = {
+ .nl_family = AF_NETLINK,
+ .nl_pid = getpid(),
+ .nl_groups = nl_groups,
+ };
+ int rcvChunkSize = 1024; // expecting less than that
+ int rcvoffset = 0;
+ ssize_t nbytes;
+ int n;
+ struct timeval tv = {
+ .tv_sec = to_usecs / MICROSEC_PER_SEC,
+ .tv_usec = to_usecs % MICROSEC_PER_SEC,
+ };
+ fd_set rfds;
+ bool gotvalid = false;
+ int fd = nlOpen();
+ static uint32_t seq = 0x1234;
+ uint32_t myseq = seq++;
+ uint32_t mypid = getpid();
+
+ if (fd < 0)
+ return -1;
+
+ nlmsg->nlmsg_pid = mypid;
+ nlmsg->nlmsg_seq = myseq;
+ nlmsg->nlmsg_flags |= NLM_F_ACK;
+
+ nbytes = sendto(fd, (void *)nlmsg, nlmsg->nlmsg_len, 0,
+ (struct sockaddr *)&nladdr, sizeof(nladdr));
+ if (nbytes < 0) {
+ virReportSystemError(errno,
+ "%s", _("cannot send to netlink socket"));
+ rc = -1;
+ goto err_exit;
+ }
+ fprintf(stderr,"sent %d bytes\n", (int)nbytes);
+
+ while (!gotvalid) {
+ rcvoffset = 0;
+ while (1) {
+ socklen_t addrlen = sizeof(nladdr);
+
+ if (VIR_REALLOC_N(*respbuf, rcvoffset+rcvChunkSize) < 0) {
+ virReportOOMError();
+ rc = -1;
+ goto err_exit;
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ n = select(fd + 1, &rfds, NULL, NULL, &tv);
+ if (n == 0) {
+ fprintf(stderr,"Select timed out.\n");
+ rc = -1;
+ goto err_exit;
+ }
+
+ nbytes = recvfrom(fd, &((*respbuf)[rcvoffset]), rcvChunkSize, 0,
+ (struct sockaddr *)&nladdr, &addrlen);
+ fprintf(stderr,"got something... %d bytes\n",(int)nbytes);
+ if (nbytes < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ virReportSystemError(errno, "%s",
+ _("error receiving from netlink socket"));
+ rc = -1;
+ goto err_exit;
+ }
+ rcvoffset += nbytes;
+ break;
+ }
+ *respbuflen = rcvoffset;
+
+ /* check message for error */
+ if (*respbuflen > NLMSG_LENGTH(0) && *respbuf != NULL) {
+ struct nlmsghdr *resp = (struct nlmsghdr *)*respbuf;
+ struct nlmsgerr *err;
+
+ fprintf(stderr,"resp->nlmsg_type=%d\n", resp->nlmsg_type);
+ fprintf(stderr,"resp->nlmsg_flags=0x%x\n", resp->nlmsg_flags);
+ fprintf(stderr,"resp->nlmsg_seq=%d (%d)\n", resp->nlmsg_seq, myseq);
+ fprintf(stderr,"resp->nlmsg_pid=%d (%d)\n", resp->nlmsg_pid, mypid);
+
+ if (resp->nlmsg_pid != mypid ||
+ resp->nlmsg_seq != myseq)
+ continue;
+
+ /* skip reflected message */
+ if (resp->nlmsg_type & 0x10)
+ continue;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len >= NLMSG_LENGTH(sizeof(*err))) {
+ fprintf(stderr,"error %d\n", -err->error);
+ if (-err->error != EOPNOTSUPP) {
+ /* assuming error msg from daemon */
+ fprintf(stderr,"Got error %d, but assuming its response from lldpad: len = %d\n",
+ -err->error,
+ *respbuflen);
+ gotvalid = true;
+ break;
+ }
+ }
+ /* whatever this is, skip it */
+ VIR_FREE(*respbuf);
+ *respbuf = NULL;
+ *respbuflen = 0;
+ fprintf(stderr,"Skipping error resp. msg.\n");
+ break;
+
+ case NLMSG_DONE:
+ gotvalid = true;
+ break;
+
+ default:
+ VIR_FREE(*respbuf);
+ *respbuf = NULL;
+ *respbuflen = 0;
+ break;
+ }
+ }
+ }
+
+err_exit:
+ if (rc == -1) {
+ VIR_FREE(*respbuf);
+ *respbuf = NULL;
+ *respbuflen = 0;
+ }
+
+ nlClose(fd);
+ return rc;
+}
+
+#endif
+
static struct rtattr *
rtattrCreate(char *buffer, int bufsize, int type,
const void *data, int datalen)
@@ -204,6 +378,8 @@ nlAppend(struct nlmsghdr *nlm, int totlen, const void *data, int datalen)
}
+# if WITH_MACVTAP
+
static int
link_add(const char *type,
const unsigned char *macaddress, int macaddrsize,
@@ -651,9 +827,10 @@ create_name:
}
if (associatePortProfileId(cr_ifname,
+ net->data.direct.linkdev,
&net->data.direct.virtPortProfile,
- -1,
- vmuuid) != 0) {
+ vmuuid,
+ NULL /* hostuuid */) != 0) {
rc = -1;
goto link_del_exit;
}
@@ -685,6 +862,7 @@ create_name:
disassociate_exit:
disassociatePortProfileId(cr_ifname,
+ net->data.direct.linkdev,
&net->data.direct.virtPortProfile);
link_del_exit:
@@ -707,21 +885,572 @@ delMacvtap(virDomainNetDefPtr net)
{
if (net->ifname) {
disassociatePortProfileId(net->ifname,
+ net->data.direct.linkdev,
&net->data.direct.virtPortProfile);
link_del(net->ifname);
}
}
-#endif
+# endif
+
+
+# ifdef IFLA_PORT_MAX
+
+static struct nla_policy ifla_policy[IFLA_MAX + 1] =
+{
+ [IFLA_VF_PORTS] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_vf_ports_policy[IFLA_VF_PORT_MAX + 1] =
+{
+ [IFLA_VF_PORT] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy ifla_port_policy[IFLA_PORT_MAX + 1] =
+{
+ [IFLA_PORT_RESPONSE] = { .type = NLA_U16 },
+};
+
+
+static int
+link_dump(int ifindex, const char *ifname, struct nlattr **tb,
+ char **recvbuf)
+{
+ int rc = 0;
+ char nlmsgbuf[256] = { 0, };
+ struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+ struct nlmsgerr *err;
+ char rtattbuf[64];
+ struct rtattr *rta;
+ struct ifinfomsg i = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = ifindex
+ };
+ int recvbuflen;
+
+ *recvbuf = NULL;
+
+ nlInit(nlm, NLM_F_REQUEST, RTM_GETLINK);
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &i, sizeof(i)))
+ goto buffer_too_small;
+
+ if (ifindex < 0 && ifname != NULL) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_IFNAME,
+ ifname, strlen(ifname) + 1);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (nlComm(nlm, recvbuf, &recvbuflen) < 0)
+ return -1;
+
+ if (recvbuflen < NLMSG_LENGTH(0) || *recvbuf == NULL)
+ goto malformed_resp;
+
+ resp = (struct nlmsghdr *)*recvbuf;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+
+ switch (-err->error) {
+ case 0:
+ break;
+
+ default:
+ virReportSystemError(-err->error,
+ _("error dumping %d interface"),
+ ifindex);
+ rc = -1;
+ }
+ break;
+
+ case GENL_ID_CTRL:
+ case NLMSG_DONE:
+ if (nlmsg_parse(resp, sizeof(struct ifinfomsg),
+ tb, IFLA_MAX, ifla_policy)) {
+ goto malformed_resp;
+ }
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ if (rc != 0)
+ VIR_FREE(*recvbuf);
+
+ return rc;
+
+malformed_resp:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(*recvbuf);
+ return -1;
+
+buffer_too_small:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("internal buffer is too small"));
+ return -1;
+}
+
+
+static int
+getPortProfileStatus(struct nlattr **tb, int32_t vf, uint16_t *status)
+{
+ int rc = 1;
+ const char *msg = NULL;
+ struct nlattr *tb2[IFLA_VF_PORT_MAX + 1],
+ *tb3[IFLA_PORT_MAX+1];
+
+ if (vf == PORT_SELF_VF) {
+ if (tb[IFLA_PORT_SELF]) {
+ if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb[IFLA_PORT_SELF],
+ ifla_port_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORT part");
+ goto err_exit;
+ }
+ }
+ } else {
+ if (tb[IFLA_VF_PORTS]) {
+ if (nla_parse_nested(tb2, IFLA_VF_PORT_MAX, tb[IFLA_VF_PORTS],
+ ifla_vf_ports_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORTS part");
+ goto err_exit;
+ }
+ if (tb2[IFLA_VF_PORT]) {
+ if (nla_parse_nested(tb3, IFLA_PORT_MAX, tb2[IFLA_VF_PORT],
+ ifla_port_policy)) {
+ msg = _("error parsing nested IFLA_VF_PORT part");
+ goto err_exit;
+ }
+ }
+ }
+ }
+
+ if (tb3[IFLA_PORT_RESPONSE]) {
+ *status = *(uint16_t *)RTA_DATA(tb3[IFLA_PORT_RESPONSE]);
+ rc = 0;
+ } else {
+ msg = _("no IFLA_PORT_RESPONSE found in netlink message");
+ goto err_exit;
+ }
+
+err_exit:
+ if (msg)
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s", msg);
+
+ return rc;
+}
+
+
+static int
+doPortProfileOpSetLink(bool multicast,
+ const char *ifname, int ifindex,
+ const char *profileId,
+ struct ifla_port_vsi *portVsi,
+ const unsigned char *instanceId,
+ const unsigned char *hostUUID,
+ int32_t vf,
+ uint8_t op)
+{
+ int rc = 0;
+ char nlmsgbuf[256];
+ struct nlmsghdr *nlm = (struct nlmsghdr *)nlmsgbuf, *resp;
+ struct nlmsgerr *err;
+ char rtattbuf[64];
+ struct rtattr *rta, *vfports, *vfport;
+ struct ifinfomsg ifinfo = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = ifindex,
+ };
+ char *recvbuf = NULL;
+ int recvbuflen = 0;
+
+ memset(&nlmsgbuf, 0, sizeof(nlmsgbuf));
+
+ nlInit(nlm, NLM_F_REQUEST, RTM_SETLINK);
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), &ifinfo, sizeof(ifinfo)))
+ goto buffer_too_small;
+
+ if (vf == PORT_SELF_VF) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_SELF, NULL, 0);
+ } else {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORTS, NULL, 0);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!(vfports = nlAppend(nlm, sizeof(nlmsgbuf),
+ rtattbuf, rta->rta_len)))
+ goto buffer_too_small;
+
+ /* beging nesting vfports */
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_VF_PORT, NULL, 0);
+ }
+
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!(vfport = nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len)))
+ goto buffer_too_small;
+
+ if (profileId) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_PROFILE,
+ profileId, strlen(profileId) + 1);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (portVsi) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_VSI_TYPE,
+ portVsi, sizeof(*portVsi));
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (instanceId) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_INSTANCE_UUID,
+ instanceId, VIR_UUID_BUFLEN);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (hostUUID) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_HOST_UUID,
+ hostUUID, VIR_UUID_BUFLEN);
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ if (vf != PORT_SELF_VF) {
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_VF,
+ &vf, sizeof(vf));
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+ }
+
+ rta = rtattrCreate(rtattbuf, sizeof(rtattbuf), IFLA_PORT_REQUEST,
+ &op, sizeof(op));
+ if (!rta)
+ goto buffer_too_small;
+
+ if (!nlAppend(nlm, sizeof(nlmsgbuf), rtattbuf, rta->rta_len))
+ goto buffer_too_small;
+
+ /* end nesting of vport */
+ vfport->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)vfport;
+
+ if (vf != PORT_SELF_VF) {
+ /* end nesting of vfports */
+ vfports->rta_len = (char *)nlm + nlm->nlmsg_len - (char *)vfports;
+ }
+
+ if (!multicast) {
+ if (nlComm(nlm, &recvbuf, &recvbuflen) < 0)
+ return -1;
+ } else {
+ if (nlCommWaitSuccess(nlm, RTMGRP_LINK, &recvbuf, &recvbuflen,
+ 5 * MICROSEC_PER_SEC) < 0)
+ return -1;
+ }
+
+ if (recvbuflen < NLMSG_LENGTH(0) || recvbuf == NULL)
+ goto malformed_resp;
+
+ resp = (struct nlmsghdr *)recvbuf;
+
+ switch (resp->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(resp);
+ if (resp->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
+ goto malformed_resp;
+
+ switch (-err->error) {
+ case 0:
+ break;
+
+ default:
+ virReportSystemError(-err->error,
+ _("error during virtual port configuration of %s interface"), ifname);
+ rc = -1;
+ }
+ break;
+
+ case NLMSG_DONE:
+ break;
+
+ default:
+ goto malformed_resp;
+ }
+
+ VIR_FREE(recvbuf);
+
+ return rc;
+
+malformed_resp:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed netlink response message"));
+ VIR_FREE(recvbuf);
+ return -1;
+
+buffer_too_small:
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("internal buffer is too small"));
+ return -1;
+}
+
+
+static int
+doPortProfileOpCommon(bool multicast,
+ const char *ifname, int ifindex,
+ const char *profileId,
+ struct ifla_port_vsi *portVsi,
+ const unsigned char *instanceId,
+ const unsigned char *hostUUID,
+ int32_t vf,
+ uint8_t op)
+{
+ int rc;
+ char *recvbuf = NULL;
+ struct nlattr *tb[IFLA_MAX + 1];
+ int repeats = 16;
+ uint16_t status;
+
+ rc = doPortProfileOpSetLink(multicast,
+ ifname, ifindex,
+ profileId,
+ portVsi,
+ instanceId,
+ hostUUID,
+ vf,
+ op);
+
+ if (rc != 0) {
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("sending of PortProfileRequest failed.\n"));
+ return rc;
+ }
+
+ if (!multicast) {
+ while (--repeats) {
+ rc = link_dump(ifindex, ifname, tb, &recvbuf);
+ if (rc)
+ goto err_exit;
+ rc = getPortProfileStatus(tb, vf, &status);
+ if (rc == 0) {
+ if (status == PORT_PROFILE_RESPONSE_SUCCESS ||
+ status == PORT_VDP_RESPONSE_SUCCESS)
+ break;
+ if (status != PORT_PROFILE_RESPONSE_INPROGRESS)
+ rc = 1;
+ }
+ usleep(125000);
+
+ VIR_FREE(recvbuf);
+ }
+ }
+
+err_exit:
+ VIR_FREE(recvbuf);
+
+ return rc;
+}
+
+# endif /* IFLA_PORT_MAX */
+
+static int
+doPortProfileOp8021Qbg(const char *ifname,
+ const virVirtualPortProfileDefPtr virtPort,
+ int32_t vf,
+ enum virVirtualPortOp virtPortOp)
+{
+ int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (void)ifname;
+ (void)virtPort;
+ (void)vf;
+ (void)virtPortOp;
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Kernel VF Port support was missing at compile time."));
+ rc = 1;
+# else /* IFLA_VF_PORT_MAX */
+
+ int op = PORT_REQUEST_ASSOCIATE;
+ struct ifla_port_vsi portVsi = {
+ .vsi_mgr_id = virtPort->u.virtPort8021Qbg.managerID,
+ .vsi_type_version = virtPort->u.virtPort8021Qbg.typeIDVersion,
+ };
+ bool multicast = true;
+ int ifindex;
+
+ if (ifaceGetIndex(true, ifname, &ifindex) != 0) {
+ rc = 1;
+ goto err_exit;
+ }
+
+ portVsi.vsi_type_id[2] = virtPort->u.virtPort8021Qbg.typeID >> 16;
+ portVsi.vsi_type_id[1] = virtPort->u.virtPort8021Qbg.typeID >> 8;
+ portVsi.vsi_type_id[0] = virtPort->u.virtPort8021Qbg.typeID;
+
+ switch (virtPortOp) {
+ case ASSOCIATE:
+ op = PORT_REQUEST_ASSOCIATE;
+ break;
+ case DISASSOCIATE:
+ op = PORT_REQUEST_DISASSOCIATE;
+ break;
+ default:
+ macvtapError(VIR_ERR_INTERNAL_ERROR,
+ _("operation type %d not supported"), op);
+ rc = 1;
+ goto err_exit;
+ }
+
+ rc = doPortProfileOpCommon(multicast, NULL, ifindex,
+ NULL,
+ &portVsi,
+ virtPort->u.virtPort8021Qbg.instanceID,
+ NULL,
+ vf,
+ op);
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
+
+
+static int
+doPortProfileOp8021Qbh(const char *ifname,
+ const virVirtualPortProfileDefPtr virtPort,
+ int32_t vf,
+ const unsigned char *host_uuid,
+ const unsigned char *vm_uuid,
+ enum virVirtualPortOp virtPortOp)
+{
+ int rc;
+
+# ifndef IFLA_VF_PORT_MAX
+
+ (void)ifname;
+ (void)virtPort;
+ (void)host_uuid;
+ (void)vm_uuid;
+ (void)virtPortOp;
+ macvtapError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Kernel VF Port support was missing at compile time."));
+ rc = 1;
+
+# else /* IFLA_VF_PORT_MAX */
+
+ int op = PORT_REQUEST_ASSOCIATE;
+ bool multicast = false;
+ int ifindex;
+
+ if (ifaceGetIndex(true, ifname, &ifindex) != 0) {
+ rc = 1;
+ goto err_exit;
+ }
+
+ switch (virtPortOp) {
+ case ASSOCIATE:
+ op = PORT_REQUEST_ASSOCIATE;
+ break;
+ case DISASSOCIATE:
+ op = PORT_REQUEST_DISASSOCIATE;
+ break;
+ default:
+ macvtapError(VIR_ERR_INTERNAL_ERROR,
+ _("operation type %d not supported"), op);
+ rc = 1;
+ goto err_exit;
+ }
+
+ rc = doPortProfileOpCommon(multicast, ifname, ifindex,
+ virtPort->u.virtPort8021Qbh.profileID,
+ NULL,
+ vm_uuid,
+ host_uuid,
+ vf,
+ op);
+
+ switch (virtPortOp) {
+ case ASSOCIATE:
+ ifaceUp(ifname);
+ break;
+ case DISASSOCIATE:
+ ifaceDown(ifname);
+ break;
+ }
+
+err_exit:
+
+# endif /* IFLA_VF_PORT_MAX */
+
+ return rc;
+}
+
+static void
+getPhysfn(const char *linkdev,
+ int32_t *vf,
+ char **physfndev)
+{
+ bool virtfn = false;
+
+ if (virtfn) {
+
+ // XXX: if linkdev is SR-IOV VF, then set vf = VF index
+ // XXX: and set linkdev = PF device
+ // XXX: need to use get_physical_function_linux() or
+ // XXX: something like that to get PF
+ // XXX: device and figure out VF index
+
+ } else {
+
+ /* Not SR-IOV VF: physfndev is linkdev and VF index
+ * refers to linkdev self
+ */
+
+ *vf = PORT_SELF_VF;
+ *physfndev = (char *)linkdev;
+ }
+}
/**
* associatePortProfile
*
* @macvtap_ifname: The name of the macvtap device
+ * @linkdev: The link device in case of macvtap
* @virtPort: pointer to the object holding port profile parameters
- * @vf: virtual function number, -1 if to be ignored
* @vmuuid : the UUID of the virtual machine
+ * @hostuuid : the UUID of the host machine
*
* Associate a port on a swtich with a profile. This function
* may notify a kernel driver or an external daemon to run
@@ -734,15 +1463,19 @@ delMacvtap(virDomainNetDefPtr net)
*/
static int
associatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort,
- int vf,
- const unsigned char *vmuuid)
+ const unsigned char *vmuuid,
+ const unsigned char *hostuuid)
{
+ char *physfndev;
+ int32_t vf;
int rc = 0;
+
VIR_DEBUG("Associating port profile '%p' on link device '%s'",
virtPort, macvtap_ifname);
- (void)vf;
- (void)vmuuid;
+
+ getPhysfn(linkdev, &vf, &physfndev);
switch (virtPort->virtPortType) {
case VIR_VIRTUALPORT_NONE:
@@ -750,11 +1483,14 @@ associatePortProfileId(const char *macvtap_ifname,
break;
case VIR_VIRTUALPORT_8021QBG:
-
+ rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort, vf,
+ ASSOCIATE);
break;
case VIR_VIRTUALPORT_8021QBH:
-
+ rc = doPortProfileOp8021Qbh(physfndev, virtPort, vf,
+ hostuuid, vmuuid,
+ ASSOCIATE);
break;
}
@@ -766,6 +1502,7 @@ associatePortProfileId(const char *macvtap_ifname,
* disassociatePortProfile
*
* @macvtap_ifname: The name of the macvtap device
+ * @linkdev: The link device in case of macvtap
* @virtPort: point to object holding port profile parameters
*
* Returns 0 in case of success, != 0 otherwise with error
@@ -773,25 +1510,36 @@ associatePortProfileId(const char *macvtap_ifname,
*/
static int
disassociatePortProfileId(const char *macvtap_ifname,
+ const char *linkdev,
const virVirtualPortProfileDefPtr virtPort)
{
+ char *physfndev;
+ int32_t vf;
int rc = 0;
+
VIR_DEBUG("Disassociating port profile id '%p' on link device '%s' ",
virtPort, macvtap_ifname);
+ getPhysfn(linkdev, &vf, &physfndev);
+
switch (virtPort->virtPortType) {
case VIR_VIRTUALPORT_NONE:
case VIR_VIRTUALPORT_TYPE_LAST:
break;
case VIR_VIRTUALPORT_8021QBG:
-
+ rc = doPortProfileOp8021Qbg(macvtap_ifname, virtPort, vf,
+ DISASSOCIATE);
break;
case VIR_VIRTUALPORT_8021QBH:
-
+ rc = doPortProfileOp8021Qbh(physfndev, virtPort, vf,
+ NULL, NULL,
+ DISASSOCIATE);
break;
}
return rc;
}
+
+#endif
14 years, 7 months
[libvirt] [PATCH v2] Add host UUID (to libvirt capabilities)
by Stefan Berger
This patch adds the host UUID (to the capabilities of libvirt). The user
may provide it in libvirtd.conf overriding whatever sysfs may
return. If none or no valid UUID is provided in libvirtd.conf, reading the
UUID from sysfs is attempted. If that function doesn't provide a valid
(not all digits may be equal), generate a temporary one.
virSetHostUUIDStr() should be called first with the UUID read from
libvirtd.conf, but may only be called once. Subsequently the functions
virGetHostUUID[Str]() can be called to get the UUID of the host.
Changes from V1 to V2:
- addressing comments from Daniel Berrange:
- rewrite/ code to get the UUID from 2 possible sysfs files
- got rid of dmidecide dependency
- call the newly added virGetHostUUID() function in udevGetDMIData() to get
the UUID of the host ( even if it's just a temporary UUID )
Besides that, this patch
- adds uuid to the capabilties XML schema
- displays the UUID in in 'virsh capabilities'
- adds 3 public functions to uuid.h for setting and getting the UUID of
the host
This patch contains recycled code from Scott Feldman
(getDMISystemUUID()).
Signed-off-by: Stefan Berger <stefanb(a)us.ibm.com>
---
daemon/libvirtd.c | 5 +
src/conf/capabilities.c | 4 +
src/util/uuid.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++
src/util/uuid.h | 4 +
4 files changed, 139 insertions(+)
Index: libvirt-acl/daemon/libvirtd.c
===================================================================
--- libvirt-acl.orig/daemon/libvirtd.c
+++ libvirt-acl/daemon/libvirtd.c
@@ -57,6 +57,7 @@
#include "dispatch.h"
#include "util.h"
+#include "uuid.h"
#include "remote_driver.h"
#include "conf.h"
#include "event.h"
@@ -2718,6 +2719,7 @@ remoteReadConfigFile (struct qemud_serve
char *unix_sock_rw_perms = NULL;
char *unix_sock_group = NULL;
char *buf = NULL;
+ char *host_uuid = NULL;
#if HAVE_POLKIT
/* Change the default back to no auth for non-root */
@@ -2840,6 +2842,10 @@ remoteReadConfigFile (struct qemud_serve
GET_CONF_INT (conf, filename, max_requests);
GET_CONF_INT (conf, filename, max_client_requests);
+ GET_CONF_STR (conf, filename, host_uuid);
+ virSetHostUUIDStr(host_uuid);
+ VIR_FREE(host_uuid);
+
virConfFree (conf);
return 0;
Index: libvirt-acl/src/util/uuid.h
===================================================================
--- libvirt-acl.orig/src/util/uuid.h
+++ libvirt-acl/src/util/uuid.h
@@ -22,6 +22,10 @@
#ifndef __VIR_UUID_H__
# define __VIR_UUID_H__
+int virSetHostUUIDStr(const char *host_uuid);
+int virGetHostUUID(unsigned char *host_uuid);
+int virGetHostUUIDStr(char *host_uuid);
+
int virUUIDGenerate(unsigned char *uuid);
int virUUIDParse(const char *uuidstr,
Index: libvirt-acl/src/util/uuid.c
===================================================================
--- libvirt-acl.orig/src/util/uuid.c
+++ libvirt-acl/src/util/uuid.c
@@ -38,11 +38,14 @@
#include "util.h"
#include "virterror_internal.h"
#include "logging.h"
+#include "memory.h"
#ifndef ENODATA
# define ENODATA EIO
#endif
+static unsigned char host_uuid[VIR_UUID_BUFLEN];
+
static int
virUUIDGenerateRandomBytes(unsigned char *buf,
int buflen)
@@ -208,3 +211,134 @@ void virUUIDFormat(const unsigned char *
uuid[12], uuid[13], uuid[14], uuid[15]);
uuidstr[VIR_UUID_STRING_BUFLEN-1] = '\0';
}
+
+
+
+/**
+ * isValidHostUUID
+ *
+ * @uuid: The UUID to test
+ *
+ * Do some basic tests to check whether the given UUID is
+ * valid as a host UUID.
+ * Basic tests:
+ * - Not all of the digits may be equal
+ */
+static int
+isValidHostUUID(unsigned char *uuid)
+{
+ unsigned int i, ctr = 1;
+ unsigned char c;
+
+ if (!uuid)
+ return 0;
+
+ c = uuid[0];
+
+ for (i = 1; i < VIR_UUID_BUFLEN; i++)
+ if (uuid[i] == c)
+ ctr++;
+
+ return (ctr != VIR_UUID_BUFLEN);
+}
+
+static int
+getDMISystemUUID(char *uuid, int len)
+{
+ unsigned int i = 0;
+ const char *paths[] = {
+ "/sys/devices/virtual/dmi/id/product_uuid",
+ "/sys/class/dmi/id/product_uuid",
+ NULL
+ };
+
+ while (paths[i]) {
+ int fd = open(paths[i], O_RDONLY);
+ if (fd > 0) {
+ if (saferead(fd, uuid, len) == len) {
+ close(fd);
+ return 0;
+ }
+ close(fd);
+ }
+ i++;
+ }
+
+ return -1;
+}
+
+
+/**
+ * setHostUUID
+ *
+ * @host_uuid: UUID that the host is supposed to have
+ *
+ * Set the UUID of the host if it hasn't been set, yet
+ * Returns 0 in case of success, an error code in case of error.
+ */
+int
+virSetHostUUIDStr(const char *uuid)
+{
+ char dmiuuid[VIR_UUID_STRING_BUFLEN];
+
+ if (isValidHostUUID(host_uuid))
+ return EEXIST;
+
+ if (!uuid) {
+ if (!getDMISystemUUID(dmiuuid, sizeof(dmiuuid))) {
+ if (!virUUIDParse(dmiuuid, host_uuid)) {
+ return 0;
+ }
+ }
+ } else {
+ if (!virUUIDParse(uuid, host_uuid))
+ return 0;
+ }
+
+ if (!isValidHostUUID(host_uuid))
+ return virUUIDGenerate(host_uuid);
+ return 0;
+}
+
+/**
+ * getHostUUID:
+ *
+ * @host_uuid: memory to store the host_uuid into
+ *
+ * Get the UUID of the host. Returns 0 in case of success,
+ * an error code otherwise.
+ * Returns 0 in case of success, an error code in case of error.
+ */
+int virGetHostUUID(unsigned char *uuid)
+{
+ int ret = 0;
+
+ if (!isValidHostUUID(host_uuid))
+ ret = virSetHostUUIDStr(NULL);
+
+ memcpy(uuid, host_uuid, sizeof(host_uuid));
+
+ return ret;
+}
+
+
+/**
+ * getHostUUID:
+ *
+ * @host_uuid: memory to store the host_uuid into
+ *
+ * Get the UUID of the host. Returns 0 in case of success,
+ * an error code otherwise.
+ * Returns 0 in case of success, an error code in case of error.
+ */
+int virGetHostUUIDStr(char *uuid)
+{
+ int ret = 0;
+
+ if (!isValidHostUUID(host_uuid))
+ ret = virSetHostUUIDStr(NULL);
+
+ virUUIDFormat(host_uuid, uuid);
+
+ return ret;
+}
Index: libvirt-acl/src/conf/capabilities.c
===================================================================
--- libvirt-acl.orig/src/conf/capabilities.c
+++ libvirt-acl/src/conf/capabilities.c
@@ -27,6 +27,7 @@
#include "buf.h"
#include "memory.h"
#include "util.h"
+#include "uuid.h"
#include "cpu_conf.h"
/**
@@ -662,9 +663,12 @@ virCapabilitiesFormatXML(virCapsPtr caps
{
virBuffer xml = VIR_BUFFER_INITIALIZER;
int i, j, k;
+ char host_uuid[VIR_UUID_STRING_BUFLEN];
+ virGetHostUUIDStr(host_uuid);
virBufferAddLit(&xml, "<capabilities>\n\n");
virBufferAddLit(&xml, " <host>\n");
+ virBufferVSprintf(&xml," <uuid>%s</uuid>\n", host_uuid);
virBufferAddLit(&xml, " <cpu>\n");
virBufferVSprintf(&xml, " <arch>%s</arch>\n",
caps->host.arch);
Index: libvirt-acl/docs/schemas/capability.rng
===================================================================
--- libvirt-acl.orig/docs/schemas/capability.rng
+++ libvirt-acl/docs/schemas/capability.rng
@@ -18,6 +18,9 @@
<define name='hostcaps'>
<element name='host'>
+ <element name='uuid'>
+ <ref name='UUID'/>
+ </element>
<element name='cpu'>
<element name='arch'>
<ref name='archnames'/>
@@ -349,4 +352,15 @@
<param name='pattern'>[a-zA-Z0-9\-_]+</param>
</data>
</define>
+
+ <define name="UUID">
+ <choice>
+ <data type="string">
+ <param name="pattern">[a-fA-F0-9]{32}</param>
+ </data>
+ <data type="string">
+ <param name="pattern">[a-fA-F0-9]{8}\-([a-fA-F0-9]{4}\-){3}[a-fA-F0-9]{12}</param>
+ </data>
+ </choice>
+ </define>
</grammar>
Index: libvirt-acl/src/libvirt_private.syms
===================================================================
--- libvirt-acl.orig/src/libvirt_private.syms
+++ libvirt-acl/src/libvirt_private.syms
@@ -708,6 +708,9 @@ usbDeviceFileIterate;
virUUIDFormat;
virUUIDGenerate;
virUUIDParse;
+virSetHostUUIDStr;
+virGetHostUUID;
+virGetHostUUIDStr;
# virterror_internal.h
Index: libvirt-acl/daemon/libvirtd.conf
===================================================================
--- libvirt-acl.orig/daemon/libvirtd.conf
+++ libvirt-acl/daemon/libvirtd.conf
@@ -312,3 +312,12 @@
# e.g.:
# log_outputs="3:syslog:libvirtd"
# to log all warnings and errors to syslog under the libvirtd ident
+
+# UUID of the host:
+# Provide the UUID of the host here in case the command
+# 'dmidecode -s system-uuid' does not provide a valid uuid. In case
+# 'dmidecode' does not provide a valid UUID and none is provided here, a
+# temporary UUID will be generated.
+# Keep the format of the example UUID below.
+
+#host_uuid = '8510b1a1-1afa-4da6-8111-785fae202c1d'
Index: libvirt-acl/src/node_device/node_device_udev.c
===================================================================
--- libvirt-acl.orig/src/node_device/node_device_udev.c
+++ libvirt-acl/src/node_device/node_device_udev.c
@@ -1473,12 +1473,8 @@ udevGetDMIData(union _virNodeDevCapData
goto out;
}
- if (udevGetStringSysfsAttr(device,
- "product_uuid",
- &tmp) == PROPERTY_ERROR) {
+ if (virGetHostUUID(data->system.hardware.uuid))
goto out;
- }
- virUUIDParse(tmp, data->system.hardware.uuid);
if (udevGetStringSysfsAttr(device,
"bios_vendor",
14 years, 7 months