diff --git a/SOURCES/libvirt-domain_conf-Make-virDomainDeviceFindSCSIController-accept-virDomainDeviceDriveAddress-struct.patch b/SOURCES/libvirt-domain_conf-Make-virDomainDeviceFindSCSIController-accept-virDomainDeviceDriveAddress-struct.patch
new file mode 100644
index 0000000..7a633e8
--- /dev/null
+++ b/SOURCES/libvirt-domain_conf-Make-virDomainDeviceFindSCSIController-accept-virDomainDeviceDriveAddress-struct.patch
@@ -0,0 +1,102 @@
+From 0c063a84897d357af0c1d04ec70deee47f97a3a1 Mon Sep 17 00:00:00 2001
+Message-Id: <0c063a84897d357af0c1d04ec70deee47f97a3a1@dist-git>
+From: Michal Privoznik <mprivozn@redhat.com>
+Date: Mon, 28 Oct 2019 14:02:22 +0100
+Subject: [PATCH] domain_conf: Make virDomainDeviceFindSCSIController accept
+ virDomainDeviceDriveAddress struct
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+So far, the virDomainDeviceFindSCSIController() takes
+virDomainDeviceInfo structure which is an overkill. It assumes
+that the passed structure is type of
+VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE which is not obvious.
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
+(cherry picked from commit 9cddc6e8ee9d9ce62dd20a6317c3148f4cd1c0e9)
+
+Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1766086
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+Message-Id: <b8ec50bafde8e4a8cce3699bd7c8625c9f648ccb.1572267657.git.mprivozn@redhat.com>
+Reviewed-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/conf/domain_conf.c         | 6 +++---
+ src/conf/domain_conf.h         | 2 +-
+ src/qemu/qemu_domain_address.c | 2 +-
+ src/vbox/vbox_common.c         | 2 +-
+ 4 files changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
+index 637d971e21..3bc603b48c 100644
+--- a/src/conf/domain_conf.c
++++ b/src/conf/domain_conf.c
+@@ -8235,13 +8235,13 @@ virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED,
+ 
+ virDomainControllerDefPtr
+ virDomainDeviceFindSCSIController(const virDomainDef *def,
+-                                  virDomainDeviceInfoPtr info)
++                                  const virDomainDeviceDriveAddress *addr)
+ {
+     size_t i;
+ 
+     for (i = 0; i < def->ncontrollers; i++) {
+         if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI &&
+-            def->controllers[i]->idx == info->addr.drive.controller)
++            def->controllers[i]->idx == addr->controller)
+             return def->controllers[i];
+     }
+ 
+@@ -18565,7 +18565,7 @@ virDomainDefMaybeAddHostdevSCSIcontroller(virDomainDefPtr def)
+              * So let's grab the model from it and update the model we're
+              * going to add as long as this one isn't undefined. The premise
+              * being keeping the same controller model for all SCSI hostdevs. */
+-            cont = virDomainDeviceFindSCSIController(def, hostdev->info);
++            cont = virDomainDeviceFindSCSIController(def, &hostdev->info->addr.drive);
+             if (cont && cont->model != -1)
+                 newModel = cont->model;
+         }
+diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
+index 0989368e7c..934850b28d 100644
+--- a/src/conf/domain_conf.h
++++ b/src/conf/domain_conf.h
+@@ -2866,7 +2866,7 @@ int virDomainDiskGetFormat(virDomainDiskDefPtr def);
+ void virDomainDiskSetFormat(virDomainDiskDefPtr def, int format);
+ virDomainControllerDefPtr
+ virDomainDeviceFindSCSIController(const virDomainDef *def,
+-                                  virDomainDeviceInfoPtr info);
++                                  const virDomainDeviceDriveAddress *addr);
+ virDomainDiskDefPtr virDomainDiskFindByBusAndDst(virDomainDefPtr def,
+                                                  int bus,
+                                                  char *dst);
+diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c
+index 0cb5af4a87..4f0278d348 100644
+--- a/src/qemu/qemu_domain_address.c
++++ b/src/qemu/qemu_domain_address.c
+@@ -113,7 +113,7 @@ qemuDomainFindSCSIControllerModel(const virDomainDef *def,
+ {
+     virDomainControllerDefPtr cont;
+ 
+-    if (!(cont = virDomainDeviceFindSCSIController(def, info))) {
++    if (!(cont = virDomainDeviceFindSCSIController(def, &info->addr.drive))) {
+         virReportError(VIR_ERR_INTERNAL_ERROR,
+                        _("unable to find a SCSI controller for idx=%d"),
+                        info->addr.drive.controller);
+diff --git a/src/vbox/vbox_common.c b/src/vbox/vbox_common.c
+index eed7c83913..fe3c3ab2aa 100644
+--- a/src/vbox/vbox_common.c
++++ b/src/vbox/vbox_common.c
+@@ -1118,7 +1118,7 @@ vboxAttachDrives(virDomainDefPtr def, vboxDriverPtr data, IMachine *machine)
+         case VIR_DOMAIN_DISK_BUS_SCSI:
+             VBOX_UTF8_TO_UTF16(VBOX_CONTROLLER_SCSI_NAME, &storageCtlName);
+ 
+-            cont = virDomainDeviceFindSCSIController(def, &disk->info);
++            cont = virDomainDeviceFindSCSIController(def, &disk->info.addr.drive);
+             if (cont && cont->model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1068) {
+                 VBOX_UTF16_FREE(storageCtlName);
+                 VBOX_UTF8_TO_UTF16(VBOX_CONTROLLER_SAS_NAME, &storageCtlName);
+-- 
+2.23.0
+
diff --git a/SOURCES/libvirt-domain_conf-Relax-SCSI-addr-used-check.patch b/SOURCES/libvirt-domain_conf-Relax-SCSI-addr-used-check.patch
new file mode 100644
index 0000000..471b3d5
--- /dev/null
+++ b/SOURCES/libvirt-domain_conf-Relax-SCSI-addr-used-check.patch
@@ -0,0 +1,102 @@
+From 99a886eeddb5d208871d149e720b13365cc6d261 Mon Sep 17 00:00:00 2001
+Message-Id: <99a886eeddb5d208871d149e720b13365cc6d261@dist-git>
+From: Michal Privoznik <mprivozn@redhat.com>
+Date: Mon, 28 Oct 2019 14:02:23 +0100
+Subject: [PATCH] domain_conf: Relax SCSI addr used check
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+In domain_conf.c we have virDomainSCSIDriveAddressIsUsed()
+function which returns true or false if given drive address is
+already in use for given domain config or not. However, it also
+takes a shortcut and returns true (meaning address in use) if the
+unit number equals 7. This is because for some controllers this
+is reserved address. The limitation comes mostly from vmware and
+applies to lsilogic, buslogic, spapr-vscsi and vmpvscsi models.
+On the other hand, we were not checking for the maximum unit
+number (aka LUN number) which is also relevant and differs from
+model to model.
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
+(cherry picked from commit c8007fdc5d2ce43fec2753cda60fb4963f55abd5)
+
+Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1766086
+
+I had to drop
+VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_TRANSITIONAL and
+VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_NON_TRANSITIONAL from
+virDomainSCSIDriveAddressIsUsed() because those don't exist in
+RHEL-7.7 branch.
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+Message-Id: <1408596266780329bf02f2537aeb4eae3aa589cf.1572267657.git.mprivozn@redhat.com>
+Reviewed-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/conf/domain_conf.c | 49 +++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 44 insertions(+), 5 deletions(-)
+
+diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
+index 3bc603b48c..f0e948d80a 100644
+--- a/src/conf/domain_conf.c
++++ b/src/conf/domain_conf.c
+@@ -4407,11 +4407,50 @@ bool
+ virDomainSCSIDriveAddressIsUsed(const virDomainDef *def,
+                                 const virDomainDeviceDriveAddress *addr)
+ {
+-    /* In current implementation, the maximum unit number of a controller
+-     * is either 16 or 7 (narrow SCSI bus), and if the maximum unit number
+-     * is 16, the controller itself is on unit 7 */
+-    if (addr->unit == 7)
+-        return true;
++    const virDomainControllerDef *cont;
++
++    cont = virDomainDeviceFindSCSIController(def, addr);
++    if (cont) {
++        int max = -1;
++        int reserved = -1;
++
++        /* Different controllers have different limits. These limits here are
++         * taken from QEMU source code, but nevertheless they should apply to
++         * other hypervisors too. */
++        switch ((virDomainControllerModelSCSI) cont->model) {
++        case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI:
++            max = 16383;
++            break;
++        case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_IBMVSCSI:
++            max = 31;
++            reserved = 7;
++            break;
++        case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1068:
++            max = 1;
++            break;
++        case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1078:
++            max = 255;
++            break;
++        case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC:
++            reserved = 7;
++            break;
++        case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VMPVSCSI:
++            reserved = 7;
++            break;
++        case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_BUSLOGIC:
++            reserved = 7;
++            break;
++        case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_DEFAULT:
++        case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_AUTO:
++        case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LAST:
++            break;
++        }
++
++        if (max != -1 && addr->unit >= max)
++            return true;
++        if (reserved != -1 && addr->unit == reserved)
++            return true;
++    }
+ 
+     if (virDomainDriveAddressIsUsedByDisk(def, VIR_DOMAIN_DISK_BUS_SCSI,
+                                           addr) ||
+-- 
+2.23.0
+
diff --git a/SOURCES/libvirt-test-Introduce-virnetdevopenvswitchtest.patch b/SOURCES/libvirt-test-Introduce-virnetdevopenvswitchtest.patch
new file mode 100644
index 0000000..69a4dd0
--- /dev/null
+++ b/SOURCES/libvirt-test-Introduce-virnetdevopenvswitchtest.patch
@@ -0,0 +1,362 @@
+From 4654a9aba4691204f083d6742940437cca0d87b9 Mon Sep 17 00:00:00 2001
+Message-Id: <4654a9aba4691204f083d6742940437cca0d87b9@dist-git>
+From: Michal Privoznik <mprivozn@redhat.com>
+Date: Wed, 9 Oct 2019 14:27:42 +0200
+Subject: [PATCH] test: Introduce virnetdevopenvswitchtest
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Test if our parsing of interface stats as returned by ovs-vsctl
+works as expected. To achieve this without having to mock
+virCommand* I'm separating parsing of stats into a separate
+function.
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+Reviewed-by: Ján Tomko <jtomko@redhat.com>
+(cherry picked from commit cc34260f5a8715d208ee45a6ebaa79e5264cbe68)
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1759904
+https://bugzilla.redhat.com/show_bug.cgi?id=1760470
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+Message-Id: <a1ed18fa73eadd5b2a87c0829555771b38a003a7.1570623892.git.mprivozn@redhat.com>
+Reviewed-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/libvirt_private.syms                   |   1 +
+ src/util/virnetdevopenvswitch.c            |  98 ++++++++++++--------
+ src/util/virnetdevopenvswitch.h            |   4 +
+ tests/Makefile.am                          |  13 ++-
+ tests/virnetdevopenvswitchdata/stats1.json |   1 +
+ tests/virnetdevopenvswitchdata/stats2.json |   1 +
+ tests/virnetdevopenvswitchtest.c           | 101 +++++++++++++++++++++
+ 7 files changed, 181 insertions(+), 38 deletions(-)
+ create mode 100644 tests/virnetdevopenvswitchdata/stats1.json
+ create mode 100644 tests/virnetdevopenvswitchdata/stats2.json
+ create mode 100644 tests/virnetdevopenvswitchtest.c
+
+diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
+index bf99930802..ac34ea0290 100644
+--- a/src/libvirt_private.syms
++++ b/src/libvirt_private.syms
+@@ -2400,6 +2400,7 @@ virNetDevMidonetUnbindPort;
+ virNetDevOpenvswitchAddPort;
+ virNetDevOpenvswitchGetMigrateData;
+ virNetDevOpenvswitchGetVhostuserIfname;
++virNetDevOpenvswitchInterfaceParseStats;
+ virNetDevOpenvswitchInterfaceStats;
+ virNetDevOpenvswitchRemovePort;
+ virNetDevOpenvswitchSetMigrateData;
+diff --git a/src/util/virnetdevopenvswitch.c b/src/util/virnetdevopenvswitch.c
+index fc92ab2e7f..80da5ad3db 100644
+--- a/src/util/virnetdevopenvswitch.c
++++ b/src/util/virnetdevopenvswitch.c
+@@ -326,50 +326,31 @@ int virNetDevOpenvswitchSetMigrateData(char *migrate, const char *ifname)
+     return ret;
+ }
+ 
++
+ /**
+- * virNetDevOpenvswitchInterfaceStats:
+- * @ifname: the name of the interface
+- * @stats: the retreived domain interface stat
++ * virNetDevOpenvswitchInterfaceParseStats:
++ * @json: Input string in JSON format
++ * @stats: parsed stats
+  *
+- * Retrieves the OVS interfaces stats
++ * For given input string @json parse interface statistics and store them into
++ * @stats.
+  *
+- * Returns 0 in case of success or -1 in case of failure
++ * Returns: 0 on success,
++ *         -1 otherwise (with error reported).
+  */
+ int
+-virNetDevOpenvswitchInterfaceStats(const char *ifname,
+-                                   virDomainInterfaceStatsPtr stats)
++virNetDevOpenvswitchInterfaceParseStats(const char *json,
++                                        virDomainInterfaceStatsPtr stats)
+ {
+-    virCommandPtr cmd = NULL;
+-    VIR_AUTOFREE(char *) output = NULL;
+     virJSONValuePtr jsonStats = NULL;
+     virJSONValuePtr jsonMap = NULL;
+     size_t i;
+     int ret = -1;
+ 
+-    cmd = virCommandNew(OVSVSCTL);
+-    virNetDevOpenvswitchAddTimeout(cmd);
+-    virCommandAddArgList(cmd, "--if-exists", "--format=list", "--data=json",
+-                         "--no-headings", "--columns=statistics", "list",
+-                         "Interface", ifname, NULL);
+-    virCommandSetOutputBuffer(cmd, &output);
+-
+-    /* The above command returns either:
+-     * 1) empty string if @ifname doesn't exist, or
+-     * 2) a JSON array, for instance:
+-     *    ["map",[["collisions",0],["rx_bytes",0],["rx_crc_err",0],["rx_dropped",0],
+-     *    ["rx_errors",0],["rx_frame_err",0],["rx_over_err",0],["rx_packets",0],
+-     *    ["tx_bytes",12406],["tx_dropped",0],["tx_errors",0],["tx_packets",173]]]
+-     */
+-
+-    if (virCommandRun(cmd, NULL) < 0 ||
+-        STREQ_NULLABLE(output, "")) {
+-        /* no ovs-vsctl or interface 'ifname' doesn't exists in ovs */
+-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+-                       _("Interface not found"));
+-        goto cleanup;
+-    }
++    stats->rx_bytes = stats->rx_packets = stats->rx_errs = stats->rx_drop = -1;
++    stats->tx_bytes = stats->tx_packets = stats->tx_errs = stats->tx_drop = -1;
+ 
+-    if (!(jsonStats = virJSONValueFromString(output)) ||
++    if (!(jsonStats = virJSONValueFromString(json)) ||
+         !virJSONValueIsArray(jsonStats) ||
+         !(jsonMap = virJSONValueArrayGet(jsonStats, 1))) {
+         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+@@ -377,9 +358,6 @@ virNetDevOpenvswitchInterfaceStats(const char *ifname,
+         goto cleanup;
+     }
+ 
+-    stats->rx_bytes = stats->rx_packets = stats->rx_errs = stats->rx_drop = -1;
+-    stats->tx_bytes = stats->tx_packets = stats->tx_errs = stats->tx_drop = -1;
+-
+     for (i = 0; i < virJSONValueArraySize(jsonMap); i++) {
+         virJSONValuePtr item = virJSONValueArrayGet(jsonMap, i);
+         virJSONValuePtr jsonKey;
+@@ -420,6 +398,55 @@ virNetDevOpenvswitchInterfaceStats(const char *ifname,
+         }
+     }
+ 
++    ret = 0;
++ cleanup:
++    virJSONValueFree(jsonStats);
++    return ret;
++}
++
++/**
++ * virNetDevOpenvswitchInterfaceStats:
++ * @ifname: the name of the interface
++ * @stats: the retrieved domain interface stat
++ *
++ * Retrieves the OVS interfaces stats
++ *
++ * Returns 0 in case of success or -1 in case of failure
++ */
++int
++virNetDevOpenvswitchInterfaceStats(const char *ifname,
++                                   virDomainInterfaceStatsPtr stats)
++{
++    virCommandPtr cmd = NULL;
++    VIR_AUTOFREE(char *) output = NULL;
++    int ret = -1;
++
++    cmd = virCommandNew(OVSVSCTL);
++    virNetDevOpenvswitchAddTimeout(cmd);
++    virCommandAddArgList(cmd, "--if-exists", "--format=list", "--data=json",
++                         "--no-headings", "--columns=statistics", "list",
++                         "Interface", ifname, NULL);
++    virCommandSetOutputBuffer(cmd, &output);
++
++    /* The above command returns either:
++     * 1) empty string if @ifname doesn't exist, or
++     * 2) a JSON array, for instance:
++     *    ["map",[["collisions",0],["rx_bytes",0],["rx_crc_err",0],["rx_dropped",0],
++     *    ["rx_errors",0],["rx_frame_err",0],["rx_over_err",0],["rx_packets",0],
++     *    ["tx_bytes",12406],["tx_dropped",0],["tx_errors",0],["tx_packets",173]]]
++     */
++
++    if (virCommandRun(cmd, NULL) < 0 ||
++        STREQ_NULLABLE(output, "")) {
++        /* no ovs-vsctl or interface 'ifname' doesn't exists in ovs */
++        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
++                       _("Interface not found"));
++        return -1;
++    }
++
++    if (virNetDevOpenvswitchInterfaceParseStats(output, stats) < 0)
++        return -1;
++
+     if (stats->rx_bytes == -1 &&
+         stats->rx_packets == -1 &&
+         stats->rx_errs == -1 &&
+@@ -436,7 +463,6 @@ virNetDevOpenvswitchInterfaceStats(const char *ifname,
+     ret = 0;
+ 
+  cleanup:
+-    virJSONValueFree(jsonStats);
+     virCommandFree(cmd);
+     return ret;
+ }
+diff --git a/src/util/virnetdevopenvswitch.h b/src/util/virnetdevopenvswitch.h
+index 6f6e620c22..c1a211dec1 100644
+--- a/src/util/virnetdevopenvswitch.h
++++ b/src/util/virnetdevopenvswitch.h
+@@ -53,6 +53,10 @@ int virNetDevOpenvswitchGetMigrateData(char **migrate, const char *ifname)
+ int virNetDevOpenvswitchSetMigrateData(char *migrate, const char *ifname)
+     ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
+ 
++int virNetDevOpenvswitchInterfaceParseStats(const char *json,
++                                            virDomainInterfaceStatsPtr stats)
++    ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
++
+ int virNetDevOpenvswitchInterfaceStats(const char *ifname,
+                                        virDomainInterfaceStatsPtr stats)
+     ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
+diff --git a/tests/Makefile.am b/tests/Makefile.am
+index f871a8a102..8cff413a78 100644
+--- a/tests/Makefile.am
++++ b/tests/Makefile.am
+@@ -158,6 +158,7 @@ EXTRA_DIST = \
+ 	virmock.h \
+ 	virnetdaemondata \
+ 	virnetdevtestdata \
++	virnetdevopenvswitchdata \
+ 	virnwfilterbindingxml2xmldata \
+ 	virpcitestdata \
+ 	virscsidata \
+@@ -1243,9 +1244,17 @@ virmacmaptest_SOURCES = \
+ 	virmacmaptest.c testutils.h testutils.c
+ virmacmaptest_LDADD = $(LDADDS)
+ 
+-test_programs += virmacmaptest
++virnetdevopenvswitchtest_SOURCES = \
++	virnetdevopenvswitchtest.c testutils.h testutils.c
++virnetdevopenvswitchtest_LDADD = $(LDADDS)
++
++test_programs += \
++	virmacmaptest \
++	virnetdevopenvswitchtest
+ else ! WITH_YAJL
+-EXTRA_DIST +=  virmacmaptest.c
++EXTRA_DIST += \
++	virmacmaptest.c \
++	virnetdevopenvswitchtest.c
+ endif ! WITH_YAJL
+ 
+ virnetdevtest_SOURCES = \
+diff --git a/tests/virnetdevopenvswitchdata/stats1.json b/tests/virnetdevopenvswitchdata/stats1.json
+new file mode 100644
+index 0000000000..1138c6271e
+--- /dev/null
++++ b/tests/virnetdevopenvswitchdata/stats1.json
+@@ -0,0 +1 @@
++["map",[["collisions",1],["rx_bytes",2],["rx_crc_err",3],["rx_dropped",4],["rx_errors",5],["rx_frame_err",6],["rx_over_err",7],["rx_packets",8],["tx_bytes",9],["tx_dropped",10],["tx_errors",11],["tx_packets",12]]]
+diff --git a/tests/virnetdevopenvswitchdata/stats2.json b/tests/virnetdevopenvswitchdata/stats2.json
+new file mode 100644
+index 0000000000..d84be7e011
+--- /dev/null
++++ b/tests/virnetdevopenvswitchdata/stats2.json
+@@ -0,0 +1 @@
++["map",[["collisions",0],["rx_bytes",0],["rx_crc_err",0],["rx_dropped",0],["rx_errors",0],["rx_frame_err",0],["rx_over_err",0],["rx_packets",0],["tx_bytes",12406],["tx_dropped",0],["tx_errors",0],["tx_packets",173]]]
+diff --git a/tests/virnetdevopenvswitchtest.c b/tests/virnetdevopenvswitchtest.c
+new file mode 100644
+index 0000000000..f01e77cbba
+--- /dev/null
++++ b/tests/virnetdevopenvswitchtest.c
+@@ -0,0 +1,101 @@
++/*
++ * Copyright (C) 2019 Red Hat, Inc.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library.  If not, see
++ * <http://www.gnu.org/licenses/>.
++ */
++
++#include <config.h>
++
++#include "testutils.h"
++#include "virnetdevopenvswitch.h"
++
++#define VIR_FROM_THIS VIR_FROM_NONE
++
++typedef struct _InterfaceParseStatsData InterfaceParseStatsData;
++struct _InterfaceParseStatsData {
++    const char *filename;
++    const virDomainInterfaceStatsStruct stats;
++};
++
++
++static int
++testInterfaceParseStats(const void *opaque)
++{
++    const InterfaceParseStatsData *data = opaque;
++    VIR_AUTOFREE(char *) filename = NULL;
++    VIR_AUTOFREE(char *) buf = NULL;
++    virDomainInterfaceStatsStruct actual;
++
++    if (virAsprintf(&filename, "%s/virnetdevopenvswitchdata/%s",
++                    abs_srcdir, data->filename) < 0)
++        return -1;
++
++    if (virFileReadAll(filename, 1024, &buf) < 0)
++        return -1;
++
++    if (virNetDevOpenvswitchInterfaceParseStats(buf, &actual) < 0)
++        return -1;
++
++    if (memcmp(&actual, &data->stats, sizeof(actual)) != 0) {
++        fprintf(stderr,
++                "Expected stats: %lld %lld %lld %lld %lld %lld %lld %lld\n"
++                "Actual stats: %lld %lld %lld %lld %lld %lld %lld %lld",
++                data->stats.rx_bytes,
++                data->stats.rx_packets,
++                data->stats.rx_errs,
++                data->stats.rx_drop,
++                data->stats.tx_bytes,
++                data->stats.tx_packets,
++                data->stats.tx_errs,
++                data->stats.tx_drop,
++                actual.rx_bytes,
++                actual.rx_packets,
++                actual.rx_errs,
++                actual.rx_drop,
++                actual.tx_bytes,
++                actual.tx_packets,
++                actual.tx_errs,
++                actual.tx_drop);
++
++        return -1;
++    }
++
++    return 0;
++}
++
++
++static int
++mymain(void)
++{
++    int ret = 0;
++
++#define TEST_INTERFACE_STATS(file, \
++                             rxBytes, rxPackets, rxErrs, rxDrop, \
++                             txBytes, txPackets, txErrs, txDrop) \
++    do { \
++        const InterfaceParseStatsData data = {.filename = file, .stats = { \
++                             rxBytes, rxPackets, rxErrs, rxDrop, \
++                             txBytes, txPackets, txErrs, txDrop}}; \
++        if (virTestRun("Interface stats " file, testInterfaceParseStats, &data) < 0) \
++            ret = -1; \
++    } while (0)
++
++    TEST_INTERFACE_STATS("stats1.json", 9, 12, 11, 10, 2, 8, 5, 4);
++    TEST_INTERFACE_STATS("stats2.json", 12406, 173, 0, 0, 0, 0, 0, 0);
++
++    return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
++}
++
++VIR_TEST_MAIN(mymain);
+-- 
+2.23.0
+
diff --git a/SOURCES/libvirt-util-Avoid-possible-error-in-virCommandMassClose.patch b/SOURCES/libvirt-util-Avoid-possible-error-in-virCommandMassClose.patch
new file mode 100644
index 0000000..c10fbc5
--- /dev/null
+++ b/SOURCES/libvirt-util-Avoid-possible-error-in-virCommandMassClose.patch
@@ -0,0 +1,45 @@
+From 6f186e44c26abd373c927215a0aba4f5892bcd32 Mon Sep 17 00:00:00 2001
+Message-Id: <6f186e44c26abd373c927215a0aba4f5892bcd32@dist-git>
+From: John Ferlan <jferlan@redhat.com>
+Date: Wed, 9 Oct 2019 14:27:46 +0200
+Subject: [PATCH] util: Avoid possible error in virCommandMassClose
+
+Avoid the chance that sysconf(_SC_OPEN_MAX) returns -1 and thus
+would cause virBitmapNew would attempt to allocate a very large
+bitmap.
+
+Found by Coverity
+
+Signed-off-by: John Ferlan <jferlan@redhat.com>
+ACKed-by: Peter Krempa <pkrempa@redhat.com>
+(cherry picked from commit 6ae4f4a4ceb123417b732e869d53099983ae8d3f)
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1759904
+https://bugzilla.redhat.com/show_bug.cgi?id=1760470
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+Message-Id: <69ca4c7bcf0e3c7588fad50d68dbf75180109b31.1570623892.git.mprivozn@redhat.com>
+Reviewed-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/util/vircommand.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/src/util/vircommand.c b/src/util/vircommand.c
+index 17405ceea4..083a8bbee5 100644
+--- a/src/util/vircommand.c
++++ b/src/util/vircommand.c
+@@ -561,6 +561,11 @@ virCommandMassClose(virCommandPtr cmd,
+      * Therefore we can safely allocate memory here (and transitively call
+      * opendir/readdir) without a deadlock. */
+ 
++    if (openmax < 0) {
++        virReportSystemError(errno, "%s", _("sysconf(_SC_OPEN_MAX) failed"));
++        return -1;
++    }
++
+     if (!(fds = virBitmapNew(openmax)))
+         return -1;
+ 
+-- 
+2.23.0
+
diff --git a/SOURCES/libvirt-util-command-Ignore-bitmap-errors-when-enumerating-file-descriptors-to-close.patch b/SOURCES/libvirt-util-command-Ignore-bitmap-errors-when-enumerating-file-descriptors-to-close.patch
new file mode 100644
index 0000000..7c50a31
--- /dev/null
+++ b/SOURCES/libvirt-util-command-Ignore-bitmap-errors-when-enumerating-file-descriptors-to-close.patch
@@ -0,0 +1,60 @@
+From a84a200ecb3867645f051e5c09826facf34786e0 Mon Sep 17 00:00:00 2001
+Message-Id: <a84a200ecb3867645f051e5c09826facf34786e0@dist-git>
+From: Peter Krempa <pkrempa@redhat.com>
+Date: Wed, 9 Oct 2019 14:27:45 +0200
+Subject: [PATCH] util: command: Ignore bitmap errors when enumerating file
+ descriptors to close
+
+virCommandMassCloseGetFDsLinux fails when running libvird on valgrind
+with the following message:
+
+libvirt:  error : internal error: unable to set FD as open: 1024
+
+This is because valgrind opens few file descriptors beyond the limit:
+
+65701125 lr-x------. 1 root root 64 Jul 18 14:48 1024 -> /home/pipo/build/libvirt/gcc/src/.libs/libvirtd
+65701126 lrwx------. 1 root root 64 Jul 18 14:48 1025 -> '/tmp/valgrind_proc_3849_cmdline_186612e3 (deleted)'
+65701127 lrwx------. 1 root root 64 Jul 18 14:48 1026 -> '/tmp/valgrind_proc_3849_auxv_186612e3 (deleted)'
+65701128 lrwx------. 1 root root 64 Jul 18 14:48 1027 -> /dev/pts/11
+65701129 lr-x------. 1 root root 64 Jul 18 14:48 1028 -> 'pipe:[65689522]'
+65701130 l-wx------. 1 root root 64 Jul 18 14:48 1029 -> 'pipe:[65689522]'
+65701131 lr-x------. 1 root root 64 Jul 18 14:48 1030 -> /tmp/vgdb-pipe-from-vgdb-to-3849-by-root-on-angien
+
+Ignore bitmap errors in this case since we'd leak those FD's anyways in
+the previous scenario.
+
+Signed-off-by: Peter Krempa <pkrempa@redhat.com>
+Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
+(cherry picked from commit 728343983787cbd4d7ae8fa2007a157bb140f02a)
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1759904
+https://bugzilla.redhat.com/show_bug.cgi?id=1760470
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+Message-Id: <30a3508e2903b58e695d467844f6d84cd008a0d9.1570623892.git.mprivozn@redhat.com>
+Reviewed-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/util/vircommand.c | 7 +------
+ 1 file changed, 1 insertion(+), 6 deletions(-)
+
+diff --git a/src/util/vircommand.c b/src/util/vircommand.c
+index 2d0c987fe2..17405ceea4 100644
+--- a/src/util/vircommand.c
++++ b/src/util/vircommand.c
+@@ -520,12 +520,7 @@ virCommandMassCloseGetFDsLinux(virCommandPtr cmd ATTRIBUTE_UNUSED,
+             goto cleanup;
+         }
+ 
+-        if (virBitmapSetBit(fds, fd) < 0) {
+-            virReportError(VIR_ERR_INTERNAL_ERROR,
+-                           _("unable to set FD as open: %d"),
+-                           fd);
+-            goto cleanup;
+-        }
++        ignore_value(virBitmapSetBit(fds, fd));
+     }
+ 
+     if (rc < 0)
+-- 
+2.23.0
+
diff --git a/SOURCES/libvirt-virCommand-use-procfs-to-learn-opened-FDs.patch b/SOURCES/libvirt-virCommand-use-procfs-to-learn-opened-FDs.patch
new file mode 100644
index 0000000..242fa48
--- /dev/null
+++ b/SOURCES/libvirt-virCommand-use-procfs-to-learn-opened-FDs.patch
@@ -0,0 +1,146 @@
+From bff0534793ef6097fda6806b40c016f2757cabec Mon Sep 17 00:00:00 2001
+Message-Id: <bff0534793ef6097fda6806b40c016f2757cabec@dist-git>
+From: Michal Privoznik <mprivozn@redhat.com>
+Date: Wed, 9 Oct 2019 14:27:44 +0200
+Subject: [PATCH] virCommand: use procfs to learn opened FDs
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+When spawning a child process, between fork() and exec() we close
+all file descriptors and keep only those the caller wants us to
+pass onto the child. The problem is how we do that. Currently, we
+get the limit of opened files and then iterate through each one
+of them and either close() it or make it survive exec(). This
+approach is suboptimal (although, not that much in default
+configurations where the limit is pretty low - 1024). We have
+/proc where we can learn what FDs we hold open and thus we can
+selectively close only those.
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+Reviewed-by: Ján Tomko <jtomko@redhat.com>
+(cherry picked from commit 432faf259b696043ee5d7e8f657d855419a9a3fa)
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1759904
+https://bugzilla.redhat.com/show_bug.cgi?id=1760470
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+Message-Id: <b9a09b65205ad8d2b001a55c329ec36c0e4f3183.1570623892.git.mprivozn@redhat.com>
+Reviewed-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/util/vircommand.c | 86 +++++++++++++++++++++++++++++++++++++++----
+ 1 file changed, 78 insertions(+), 8 deletions(-)
+
+diff --git a/src/util/vircommand.c b/src/util/vircommand.c
+index 02dbe0fc76..2d0c987fe2 100644
+--- a/src/util/vircommand.c
++++ b/src/util/vircommand.c
+@@ -492,27 +492,97 @@ virExecCommon(virCommandPtr cmd, gid_t *groups, int ngroups)
+     return ret;
+ }
+ 
++# ifdef __linux__
++/* On Linux, we can utilize procfs and read the table of opened
++ * FDs and selectively close only those FDs we don't want to pass
++ * onto child process (well, the one we will exec soon since this
++ * is called from the child). */
++static int
++virCommandMassCloseGetFDsLinux(virCommandPtr cmd ATTRIBUTE_UNUSED,
++                               virBitmapPtr fds)
++{
++    DIR *dp = NULL;
++    struct dirent *entry;
++    const char *dirName = "/proc/self/fd";
++    int rc;
++    int ret = -1;
++
++    if (virDirOpen(&dp, dirName) < 0)
++        return -1;
++
++    while ((rc = virDirRead(dp, &entry, dirName)) > 0) {
++        int fd;
++
++        if (virStrToLong_i(entry->d_name, NULL, 10, &fd) < 0) {
++            virReportError(VIR_ERR_INTERNAL_ERROR,
++                           _("unable to parse FD: %s"),
++                           entry->d_name);
++            goto cleanup;
++        }
++
++        if (virBitmapSetBit(fds, fd) < 0) {
++            virReportError(VIR_ERR_INTERNAL_ERROR,
++                           _("unable to set FD as open: %d"),
++                           fd);
++            goto cleanup;
++        }
++    }
++
++    if (rc < 0)
++        goto cleanup;
++
++    ret = 0;
++ cleanup:
++    VIR_DIR_CLOSE(dp);
++    return ret;
++}
++
++# else /* !__linux__ */
++
++static int
++virCommandMassCloseGetFDsGeneric(virCommandPtr cmd ATTRIBUTE_UNUSED,
++                                 virBitmapPtr fds)
++{
++    virBitmapSetAll(fds);
++    return 0;
++}
++# endif /* !__linux__ */
++
+ static int
+ virCommandMassClose(virCommandPtr cmd,
+                     int childin,
+                     int childout,
+                     int childerr)
+ {
++    VIR_AUTOPTR(virBitmap) fds = NULL;
+     int openmax = sysconf(_SC_OPEN_MAX);
+-    int fd;
+-    int tmpfd;
++    int fd = -1;
+ 
+-    if (openmax < 0) {
+-        virReportSystemError(errno,  "%s",
+-                             _("sysconf(_SC_OPEN_MAX) failed"));
++    /* In general, it is not safe to call malloc() between fork() and exec()
++     * because the child might have forked at the worst possible time, i.e.
++     * when another thread was in malloc() and thus held its lock. That is to
++     * say, POSIX does not mandate malloc() to be async-safe. Fortunately,
++     * glibc developers are aware of this and made malloc() async-safe.
++     * Therefore we can safely allocate memory here (and transitively call
++     * opendir/readdir) without a deadlock. */
++
++    if (!(fds = virBitmapNew(openmax)))
+         return -1;
+-    }
+ 
+-    for (fd = 3; fd < openmax; fd++) {
++# ifdef __linux__
++    if (virCommandMassCloseGetFDsLinux(cmd, fds) < 0)
++        return -1;
++# else
++    if (virCommandMassCloseGetFDsGeneric(cmd, fds) < 0)
++        return -1;
++# endif
++
++    fd = virBitmapNextSetBit(fds, 2);
++    for (; fd >= 0; fd = virBitmapNextSetBit(fds, fd)) {
+         if (fd == childin || fd == childout || fd == childerr)
+             continue;
+         if (!virCommandFDIsSet(cmd, fd)) {
+-            tmpfd = fd;
++            int tmpfd = fd;
+             VIR_MASS_CLOSE(tmpfd);
+         } else if (virSetInherit(fd, true) < 0) {
+             virReportSystemError(errno, _("failed to preserve fd %d"), fd);
+-- 
+2.23.0
+
diff --git a/SOURCES/libvirt-virNetDevOpenvswitchInterfaceStats-Optimize-for-speed.patch b/SOURCES/libvirt-virNetDevOpenvswitchInterfaceStats-Optimize-for-speed.patch
new file mode 100644
index 0000000..4cbdce3
--- /dev/null
+++ b/SOURCES/libvirt-virNetDevOpenvswitchInterfaceStats-Optimize-for-speed.patch
@@ -0,0 +1,188 @@
+From 0a75670e62729ab806f23b10df8556a540d4479d Mon Sep 17 00:00:00 2001
+Message-Id: <0a75670e62729ab806f23b10df8556a540d4479d@dist-git>
+From: Michal Privoznik <mprivozn@redhat.com>
+Date: Wed, 9 Oct 2019 14:27:41 +0200
+Subject: [PATCH] virNetDevOpenvswitchInterfaceStats: Optimize for speed
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+We run 'ovs-vsctl' nine times (first to find if interface is
+there and then eight times = for each stats member separately).
+This is very inefficient. I've found a way to run it once and
+with a bit of help from virJSON module we can parse out stats
+we need.
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+Reviewed-by: Ján Tomko <jtomko@redhat.com>
+(cherry picked from commit c297eab52599c91a4cb26b66dbdfe9d07c3142d3)
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1759904
+https://bugzilla.redhat.com/show_bug.cgi?id=1760470
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+Message-Id: <9904d3bee93a9e103012d088b77999f023acaa2b.1570623892.git.mprivozn@redhat.com>
+Reviewed-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/util/virnetdevopenvswitch.c | 119 +++++++++++++++++++++-----------
+ 1 file changed, 78 insertions(+), 41 deletions(-)
+
+diff --git a/src/util/virnetdevopenvswitch.c b/src/util/virnetdevopenvswitch.c
+index f36916a300..fc92ab2e7f 100644
+--- a/src/util/virnetdevopenvswitch.c
++++ b/src/util/virnetdevopenvswitch.c
+@@ -35,6 +35,7 @@
+ #include "virmacaddr.h"
+ #include "virstring.h"
+ #include "virlog.h"
++#include "virjson.h"
+ 
+ #define VIR_FROM_THIS VIR_FROM_NONE
+ 
+@@ -339,58 +340,94 @@ virNetDevOpenvswitchInterfaceStats(const char *ifname,
+                                    virDomainInterfaceStatsPtr stats)
+ {
+     virCommandPtr cmd = NULL;
+-    char *output;
+-    char *tmp;
+-    bool gotStats = false;
++    VIR_AUTOFREE(char *) output = NULL;
++    virJSONValuePtr jsonStats = NULL;
++    virJSONValuePtr jsonMap = NULL;
++    size_t i;
+     int ret = -1;
+ 
+-    /* Just ensure the interface exists in ovs */
+     cmd = virCommandNew(OVSVSCTL);
+     virNetDevOpenvswitchAddTimeout(cmd);
+-    virCommandAddArgList(cmd, "get", "Interface", ifname, "name", NULL);
++    virCommandAddArgList(cmd, "--if-exists", "--format=list", "--data=json",
++                         "--no-headings", "--columns=statistics", "list",
++                         "Interface", ifname, NULL);
+     virCommandSetOutputBuffer(cmd, &output);
+ 
+-    if (virCommandRun(cmd, NULL) < 0) {
++    /* The above command returns either:
++     * 1) empty string if @ifname doesn't exist, or
++     * 2) a JSON array, for instance:
++     *    ["map",[["collisions",0],["rx_bytes",0],["rx_crc_err",0],["rx_dropped",0],
++     *    ["rx_errors",0],["rx_frame_err",0],["rx_over_err",0],["rx_packets",0],
++     *    ["tx_bytes",12406],["tx_dropped",0],["tx_errors",0],["tx_packets",173]]]
++     */
++
++    if (virCommandRun(cmd, NULL) < 0 ||
++        STREQ_NULLABLE(output, "")) {
+         /* no ovs-vsctl or interface 'ifname' doesn't exists in ovs */
+         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                        _("Interface not found"));
+         goto cleanup;
+     }
+ 
+-#define GET_STAT(name, member) \
+-    do { \
+-        VIR_FREE(output); \
+-        virCommandFree(cmd); \
+-        cmd = virCommandNew(OVSVSCTL); \
+-        virNetDevOpenvswitchAddTimeout(cmd); \
+-        virCommandAddArgList(cmd, "--if-exists", "get", "Interface", \
+-                             ifname, "statistics:" name, NULL); \
+-        virCommandSetOutputBuffer(cmd, &output); \
+-        if (virCommandRun(cmd, NULL) < 0 || !output || !*output || *output == '\n') { \
+-            stats->member = -1; \
+-        } else { \
+-            if (virStrToLong_ll(output, &tmp, 10, &stats->member) < 0 || \
+-                *tmp != '\n') { \
+-                virReportError(VIR_ERR_INTERNAL_ERROR, "%s", \
+-                               _("Fail to parse ovs-vsctl output")); \
+-                goto cleanup; \
+-            } \
+-            gotStats = true; \
+-        } \
+-    } while (0)
+-
+-    /* The TX/RX fields appear to be swapped here
+-     * because this is the host view. */
+-    GET_STAT("rx_bytes", tx_bytes);
+-    GET_STAT("rx_packets", tx_packets);
+-    GET_STAT("rx_errors", tx_errs);
+-    GET_STAT("rx_dropped", tx_drop);
+-    GET_STAT("tx_bytes", rx_bytes);
+-    GET_STAT("tx_packets", rx_packets);
+-    GET_STAT("tx_errors", rx_errs);
+-    GET_STAT("tx_dropped", rx_drop);
+-
+-    if (!gotStats) {
++    if (!(jsonStats = virJSONValueFromString(output)) ||
++        !virJSONValueIsArray(jsonStats) ||
++        !(jsonMap = virJSONValueArrayGet(jsonStats, 1))) {
++        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
++                       _("Unable to parse ovs-vsctl output"));
++        goto cleanup;
++    }
++
++    stats->rx_bytes = stats->rx_packets = stats->rx_errs = stats->rx_drop = -1;
++    stats->tx_bytes = stats->tx_packets = stats->tx_errs = stats->tx_drop = -1;
++
++    for (i = 0; i < virJSONValueArraySize(jsonMap); i++) {
++        virJSONValuePtr item = virJSONValueArrayGet(jsonMap, i);
++        virJSONValuePtr jsonKey;
++        virJSONValuePtr jsonVal;
++        const char *key;
++        long long val;
++
++        if (!item ||
++            (!(jsonKey = virJSONValueArrayGet(item, 0))) ||
++            (!(jsonVal = virJSONValueArrayGet(item, 1))) ||
++            (!(key = virJSONValueGetString(jsonKey))) ||
++            (virJSONValueGetNumberLong(jsonVal, &val) < 0)) {
++            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
++                           _("Malformed ovs-vsctl output"));
++            goto cleanup;
++        }
++
++        /* The TX/RX fields appear to be swapped here
++         * because this is the host view. */
++        if (STREQ(key, "rx_bytes")) {
++            stats->tx_bytes = val;
++        } else if (STREQ(key, "rx_packets")) {
++            stats->tx_packets = val;
++        } else if (STREQ(key, "rx_errors")) {
++            stats->tx_errs = val;
++        } else if (STREQ(key, "rx_dropped")) {
++            stats->tx_drop = val;
++        } else if (STREQ(key, "tx_bytes")) {
++            stats->rx_bytes = val;
++        } else if (STREQ(key, "tx_packets")) {
++            stats->rx_packets = val;
++        } else if (STREQ(key, "tx_errors")) {
++            stats->rx_errs = val;
++        } else if (STREQ(key, "tx_dropped")) {
++            stats->rx_drop = val;
++        } else {
++            VIR_DEBUG("Unused ovs-vsctl stat key=%s val=%lld", key, val);
++        }
++    }
++
++    if (stats->rx_bytes == -1 &&
++        stats->rx_packets == -1 &&
++        stats->rx_errs == -1 &&
++        stats->rx_drop == -1 &&
++        stats->tx_bytes == -1 &&
++        stats->tx_packets == -1 &&
++        stats->tx_errs == -1 &&
++        stats->tx_drop == -1) {
+         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                        _("Interface doesn't have any statistics"));
+         goto cleanup;
+@@ -399,7 +436,7 @@ virNetDevOpenvswitchInterfaceStats(const char *ifname,
+     ret = 0;
+ 
+  cleanup:
+-    VIR_FREE(output);
++    virJSONValueFree(jsonStats);
+     virCommandFree(cmd);
+     return ret;
+ }
+-- 
+2.23.0
+
diff --git a/SOURCES/libvirt-vircommand-Separate-mass-FD-closing-into-a-function.patch b/SOURCES/libvirt-vircommand-Separate-mass-FD-closing-into-a-function.patch
new file mode 100644
index 0000000..8a6afe5
--- /dev/null
+++ b/SOURCES/libvirt-vircommand-Separate-mass-FD-closing-into-a-function.patch
@@ -0,0 +1,112 @@
+From 307d9f2713d86653375d719020698d858d5c753d Mon Sep 17 00:00:00 2001
+Message-Id: <307d9f2713d86653375d719020698d858d5c753d@dist-git>
+From: Michal Privoznik <mprivozn@redhat.com>
+Date: Wed, 9 Oct 2019 14:27:43 +0200
+Subject: [PATCH] vircommand: Separate mass FD closing into a function
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+I will optimize this code a bit in the next commit. But for that
+it is better if the code lives in a separate function.
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+Reviewed-by: Ján Tomko <jtomko@redhat.com>
+(cherry picked from commit c1a9bfbbba48fea44fdfbe532e084c5323c9c9b3)
+
+https://bugzilla.redhat.com/show_bug.cgi?id=1759904
+https://bugzilla.redhat.com/show_bug.cgi?id=1760470
+
+Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
+Message-Id: <0198a865d008ec9fb80376062049e05522413d3e.1570623892.git.mprivozn@redhat.com>
+Reviewed-by: Jiri Denemark <jdenemar@redhat.com>
+---
+ src/util/vircommand.c | 52 ++++++++++++++++++++++++++++---------------
+ 1 file changed, 34 insertions(+), 18 deletions(-)
+
+diff --git a/src/util/vircommand.c b/src/util/vircommand.c
+index f539bafab6..02dbe0fc76 100644
+--- a/src/util/vircommand.c
++++ b/src/util/vircommand.c
+@@ -492,6 +492,37 @@ virExecCommon(virCommandPtr cmd, gid_t *groups, int ngroups)
+     return ret;
+ }
+ 
++static int
++virCommandMassClose(virCommandPtr cmd,
++                    int childin,
++                    int childout,
++                    int childerr)
++{
++    int openmax = sysconf(_SC_OPEN_MAX);
++    int fd;
++    int tmpfd;
++
++    if (openmax < 0) {
++        virReportSystemError(errno,  "%s",
++                             _("sysconf(_SC_OPEN_MAX) failed"));
++        return -1;
++    }
++
++    for (fd = 3; fd < openmax; fd++) {
++        if (fd == childin || fd == childout || fd == childerr)
++            continue;
++        if (!virCommandFDIsSet(cmd, fd)) {
++            tmpfd = fd;
++            VIR_MASS_CLOSE(tmpfd);
++        } else if (virSetInherit(fd, true) < 0) {
++            virReportSystemError(errno, _("failed to preserve fd %d"), fd);
++            return -1;
++        }
++    }
++
++    return 0;
++}
++
+ /*
+  * virExec:
+  * @cmd virCommandPtr containing all information about the program to
+@@ -501,13 +532,12 @@ static int
+ virExec(virCommandPtr cmd)
+ {
+     pid_t pid;
+-    int null = -1, fd, openmax;
++    int null = -1;
+     int pipeout[2] = {-1, -1};
+     int pipeerr[2] = {-1, -1};
+     int childin = cmd->infd;
+     int childout = -1;
+     int childerr = -1;
+-    int tmpfd;
+     char *binarystr = NULL;
+     const char *binary = NULL;
+     int ret;
+@@ -616,23 +646,9 @@ virExec(virCommandPtr cmd)
+     if (cmd->mask)
+         umask(cmd->mask);
+     ret = EXIT_CANCELED;
+-    openmax = sysconf(_SC_OPEN_MAX);
+-    if (openmax < 0) {
+-        virReportSystemError(errno,  "%s",
+-                             _("sysconf(_SC_OPEN_MAX) failed"));
++
++    if (virCommandMassClose(cmd, childin, childout, childerr) < 0)
+         goto fork_error;
+-    }
+-    for (fd = 3; fd < openmax; fd++) {
+-        if (fd == childin || fd == childout || fd == childerr)
+-            continue;
+-        if (!virCommandFDIsSet(cmd, fd)) {
+-            tmpfd = fd;
+-            VIR_MASS_CLOSE(tmpfd);
+-        } else if (virSetInherit(fd, true) < 0) {
+-            virReportSystemError(errno, _("failed to preserve fd %d"), fd);
+-            goto fork_error;
+-        }
+-    }
+ 
+     if (prepareStdFd(childin, STDIN_FILENO) < 0) {
+         virReportSystemError(errno,
+-- 
+2.23.0
+
diff --git a/SPECS/libvirt.spec b/SPECS/libvirt.spec
index 9c264b5..a30e97a 100644
--- a/SPECS/libvirt.spec
+++ b/SPECS/libvirt.spec
@@ -253,7 +253,7 @@
 Summary: Library providing a simple virtualization API
 Name: libvirt
 Version: 4.5.0
-Release: 23%{?dist}.1%{?extra_release}
+Release: 23%{?dist}.3%{?extra_release}
 License: LGPLv2+
 URL: https://libvirt.org/
 
@@ -590,6 +590,14 @@ Patch324: libvirt-qemu-Pass-correct-qemuCaps-to-virDomainDefPostParse.patch
 Patch325: libvirt-qemu-Pass-correct-qemuCaps-to-virDomainDefParseNode.patch
 Patch326: libvirt-qemu-Pass-correct-qemuCaps-to-virDomainDeviceDefPostParse.patch
 Patch327: libvirt-qemu-Fix-crash-on-incoming-migration.patch
+Patch328: libvirt-virNetDevOpenvswitchInterfaceStats-Optimize-for-speed.patch
+Patch329: libvirt-test-Introduce-virnetdevopenvswitchtest.patch
+Patch330: libvirt-vircommand-Separate-mass-FD-closing-into-a-function.patch
+Patch331: libvirt-virCommand-use-procfs-to-learn-opened-FDs.patch
+Patch332: libvirt-util-command-Ignore-bitmap-errors-when-enumerating-file-descriptors-to-close.patch
+Patch333: libvirt-util-Avoid-possible-error-in-virCommandMassClose.patch
+Patch334: libvirt-domain_conf-Make-virDomainDeviceFindSCSIController-accept-virDomainDeviceDriveAddress-struct.patch
+Patch335: libvirt-domain_conf-Relax-SCSI-addr-used-check.patch
 
 Requires: libvirt-daemon = %{version}-%{release}
 Requires: libvirt-daemon-config-network = %{version}-%{release}
@@ -2491,6 +2499,18 @@ exit 0
 
 
 %changelog
+* Mon Oct 28 2019 Jiri Denemark <jdenemar@redhat.com> - 4.5.0-23.el7_7.3
+- domain_conf: Make virDomainDeviceFindSCSIController accept virDomainDeviceDriveAddress struct (rhbz#1766086)
+- domain_conf: Relax SCSI addr used check (rhbz#1766086)
+
+* Mon Oct 14 2019 Jiri Denemark <jdenemar@redhat.com> - 4.5.0-23.el7_7.2
+- virNetDevOpenvswitchInterfaceStats: Optimize for speed (rhbz#1760470)
+- test: Introduce virnetdevopenvswitchtest (rhbz#1760470)
+- vircommand: Separate mass FD closing into a function (rhbz#1760470)
+- virCommand: use procfs to learn opened FDs (rhbz#1760470)
+- util: command: Ignore bitmap errors when enumerating file descriptors to close (rhbz#1760470)
+- util: Avoid possible error in virCommandMassClose (rhbz#1760470)
+
 * Fri Aug 16 2019 Jiri Denemark <jdenemar@redhat.com> - 4.5.0-23.el7_7.1
 - qemu: Pass qemuCaps to qemuDomainDefCopy (rhbz#1742023)
 - qemu: Pass qemuCaps to qemuDomainDefFormatBufInternal (rhbz#1742023)