2012-02-10 21:09:00 +00:00
|
|
|
/*
|
2013-10-05 19:41:44 +00:00
|
|
|
* Copyright (C) 2013 Red Hat, Inc.
|
2012-02-10 21:09:00 +00:00
|
|
|
* Copyright (C) 2012 Nicira, Inc.
|
2017-02-07 15:16:43 +00:00
|
|
|
* Copyright (C) 2017 IBM Corporation
|
2012-02-10 21:09:00 +00:00
|
|
|
*
|
|
|
|
* 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
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 10:06:23 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2012-02-10 21:09:00 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
2016-11-18 22:51:13 +00:00
|
|
|
|
2012-02-10 21:09:00 +00:00
|
|
|
#include "virnetdevopenvswitch.h"
|
2012-12-12 16:27:01 +00:00
|
|
|
#include "vircommand.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2012-02-10 21:09:00 +00:00
|
|
|
#include "virmacaddr.h"
|
2013-04-03 10:36:23 +00:00
|
|
|
#include "virstring.h"
|
util: don't fail if no PortData is found while getting migrateData
Introduced by f6a2f97e
Problem Description:
After multiple times of migrating a domain, which has an ovs interface with no portData set,
with non-shared disk, nbd ports got overflowed.
The steps to reproduce the problem:
1 define and start a domain with its network configured as:
<interface type='bridge'>
<source bridge='br0'/>
<virtualport type='openvswitch'>
</virtualport>
<model type='virtio'/>
<driver name='vhost' queues='4'/>
</interface>
2 do not set the network's portData.
3 migrate(ToURI2) it with flag 91(1011011), which means:
VIR_MIGRATE_LIVE
VIR_MIGRATE_PEER2PEER
VIR_MIGRATE_PERSIST_DEST
VIR_MIGRATE_UNDEFINE_SOURCE
VIR_MIGRATE_NON_SHARED_DISK
4 migrate success, but we got an error log in libvirtd.log:
error : virCommandWait:2423 : internal error: Child process (ovs-vsctl --timeout=5 get Interface
vnet1 external_ids:PortData) unexpected exit status 1: ovs-vsctl: no key "PortData" in Interface
record "vnet1" column external_ids
5 migrate it back, migrate it , migrate it back, .......
6 nbd port got overflowed.
The reasons for the problem is :
1 virNetDevOpenvswitchGetMigrateData() takes it as wrong if no portData is available for the ovs
interface of a domain. (We think it's not appropriate, as portData is just OPTIONAL)
2 in func qemuMigrationBakeCookie(), it fails in qemuMigrationCookieAddNetwork(), and returns with -1.
qemuMigrationCookieAddNBD() is not called thereafter, and mig->nbd is still NULL.
3 However, qemuMigrationRun() just *WARN* if qemuMigrationBakeCookie() fails, migration still successes.
cookie is NULL, it's not baked on the src side.
4 On the destination side, it would alloc a port first and then free the nbd port in COOKIE.
But the cookie is NULL due to qemuMigrationCookieAddNetwork() failure at src side. thus the nbd port
is not freed.
In this patch, we add "--if-exists" option to make ovs-vsctl not raise error if there's no portData available.
Further more, because portData may be NULL in the cookie at the dest side, check it before setting portData.
Signed-off-by: Zhou Yimin <zhouyimin@huawei.com>
Signed-off-by: Zhang Bo <oscar.zhangbo@huawei.com>
2015-03-05 02:01:50 +00:00
|
|
|
#include "virlog.h"
|
2019-06-28 08:33:09 +00:00
|
|
|
#include "virjson.h"
|
2021-01-09 08:16:56 +00:00
|
|
|
#include "virfile.h"
|
2021-07-07 09:18:17 +00:00
|
|
|
#include "virutil.h"
|
2012-02-10 21:09:00 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
|
|
util: don't fail if no PortData is found while getting migrateData
Introduced by f6a2f97e
Problem Description:
After multiple times of migrating a domain, which has an ovs interface with no portData set,
with non-shared disk, nbd ports got overflowed.
The steps to reproduce the problem:
1 define and start a domain with its network configured as:
<interface type='bridge'>
<source bridge='br0'/>
<virtualport type='openvswitch'>
</virtualport>
<model type='virtio'/>
<driver name='vhost' queues='4'/>
</interface>
2 do not set the network's portData.
3 migrate(ToURI2) it with flag 91(1011011), which means:
VIR_MIGRATE_LIVE
VIR_MIGRATE_PEER2PEER
VIR_MIGRATE_PERSIST_DEST
VIR_MIGRATE_UNDEFINE_SOURCE
VIR_MIGRATE_NON_SHARED_DISK
4 migrate success, but we got an error log in libvirtd.log:
error : virCommandWait:2423 : internal error: Child process (ovs-vsctl --timeout=5 get Interface
vnet1 external_ids:PortData) unexpected exit status 1: ovs-vsctl: no key "PortData" in Interface
record "vnet1" column external_ids
5 migrate it back, migrate it , migrate it back, .......
6 nbd port got overflowed.
The reasons for the problem is :
1 virNetDevOpenvswitchGetMigrateData() takes it as wrong if no portData is available for the ovs
interface of a domain. (We think it's not appropriate, as portData is just OPTIONAL)
2 in func qemuMigrationBakeCookie(), it fails in qemuMigrationCookieAddNetwork(), and returns with -1.
qemuMigrationCookieAddNBD() is not called thereafter, and mig->nbd is still NULL.
3 However, qemuMigrationRun() just *WARN* if qemuMigrationBakeCookie() fails, migration still successes.
cookie is NULL, it's not baked on the src side.
4 On the destination side, it would alloc a port first and then free the nbd port in COOKIE.
But the cookie is NULL due to qemuMigrationCookieAddNetwork() failure at src side. thus the nbd port
is not freed.
In this patch, we add "--if-exists" option to make ovs-vsctl not raise error if there's no portData available.
Further more, because portData may be NULL in the cookie at the dest side, check it before setting portData.
Signed-off-by: Zhou Yimin <zhouyimin@huawei.com>
Signed-off-by: Zhang Bo <oscar.zhangbo@huawei.com>
2015-03-05 02:01:50 +00:00
|
|
|
VIR_LOG_INIT("util.netdevopenvswitch");
|
|
|
|
|
2017-02-07 15:16:43 +00:00
|
|
|
/*
|
2020-07-09 04:42:21 +00:00
|
|
|
* Set openvswitch default timeout
|
2017-02-07 15:16:43 +00:00
|
|
|
*/
|
|
|
|
static unsigned int virNetDevOpenvswitchTimeout = VIR_NETDEV_OVS_DEFAULT_TIMEOUT;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virNetDevOpenvswitchSetTimeout:
|
|
|
|
* @timeout: the timeout in seconds
|
|
|
|
*
|
|
|
|
* Set the openvswitch timeout
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
virNetDevOpenvswitchSetTimeout(unsigned int timeout)
|
|
|
|
{
|
|
|
|
virNetDevOpenvswitchTimeout = timeout;
|
|
|
|
}
|
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virCommand *
|
2020-11-11 08:12:18 +00:00
|
|
|
virNetDevOpenvswitchCreateCmd(void)
|
2017-02-07 15:16:43 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
virCommand *cmd = virCommandNew(OVS_VSCTL);
|
2017-02-07 15:16:43 +00:00
|
|
|
virCommandAddArgFormat(cmd, "--timeout=%u", virNetDevOpenvswitchTimeout);
|
2020-11-11 08:12:18 +00:00
|
|
|
return cmd;
|
2017-02-07 15:16:43 +00:00
|
|
|
}
|
|
|
|
|
2017-07-17 15:48:59 +00:00
|
|
|
/**
|
|
|
|
* virNetDevOpenvswitchConstructVlans:
|
|
|
|
* @cmd: command to construct
|
|
|
|
* @virtVlan: VLAN configuration to be applied
|
|
|
|
*
|
|
|
|
* Construct the VLAN configuration parameters to be passed to
|
|
|
|
* ovs-vsctl command.
|
|
|
|
*/
|
2020-05-13 09:56:59 +00:00
|
|
|
static void
|
2021-03-11 07:16:13 +00:00
|
|
|
virNetDevOpenvswitchConstructVlans(virCommand *cmd, const virNetDevVlan *virtVlan)
|
2017-07-17 15:48:59 +00:00
|
|
|
{
|
2020-05-13 09:47:37 +00:00
|
|
|
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
2017-07-17 15:48:59 +00:00
|
|
|
|
|
|
|
if (!virtVlan || !virtVlan->nTags)
|
2020-05-13 09:56:59 +00:00
|
|
|
return;
|
2017-07-17 15:48:59 +00:00
|
|
|
|
|
|
|
switch (virtVlan->nativeMode) {
|
|
|
|
case VIR_NATIVE_VLAN_MODE_TAGGED:
|
|
|
|
virCommandAddArg(cmd, "vlan_mode=native-tagged");
|
|
|
|
virCommandAddArgFormat(cmd, "tag=%d", virtVlan->nativeTag);
|
|
|
|
break;
|
|
|
|
case VIR_NATIVE_VLAN_MODE_UNTAGGED:
|
|
|
|
virCommandAddArg(cmd, "vlan_mode=native-untagged");
|
|
|
|
virCommandAddArgFormat(cmd, "tag=%d", virtVlan->nativeTag);
|
|
|
|
break;
|
|
|
|
case VIR_NATIVE_VLAN_MODE_DEFAULT:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virtVlan->trunk) {
|
2020-05-13 09:49:07 +00:00
|
|
|
size_t i;
|
|
|
|
|
2017-07-17 15:48:59 +00:00
|
|
|
virBufferAddLit(&buf, "trunk=");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Trunk ports have at least one VLAN. Do the first one
|
|
|
|
* outside the "for" loop so we can put a "," at the
|
|
|
|
* start of the for loop if there are more than one VLANs
|
|
|
|
* on this trunk port.
|
|
|
|
*/
|
2020-05-13 09:49:07 +00:00
|
|
|
virBufferAsprintf(&buf, "%d", virtVlan->tag[0]);
|
2017-07-17 15:48:59 +00:00
|
|
|
|
|
|
|
for (i = 1; i < virtVlan->nTags; i++) {
|
|
|
|
virBufferAddLit(&buf, ",");
|
|
|
|
virBufferAsprintf(&buf, "%d", virtVlan->tag[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
virCommandAddArg(cmd, virBufferCurrentContent(&buf));
|
|
|
|
} else if (virtVlan->nTags) {
|
|
|
|
virCommandAddArgFormat(cmd, "tag=%d", virtVlan->tag[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-10 21:09:00 +00:00
|
|
|
/**
|
|
|
|
* virNetDevOpenvswitchAddPort:
|
|
|
|
* @brname: the bridge name
|
|
|
|
* @ifname: the network interface name
|
|
|
|
* @macaddr: the mac address of the virtual interface
|
2012-03-07 07:15:36 +00:00
|
|
|
* @vmuuid: the Domain UUID that has this interface
|
2012-02-10 21:09:00 +00:00
|
|
|
* @ovsport: the ovs specific fields
|
|
|
|
*
|
|
|
|
* Add an interface to the OVS bridge
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success or -1 in case of failure.
|
|
|
|
*/
|
|
|
|
int virNetDevOpenvswitchAddPort(const char *brname, const char *ifname,
|
2018-09-19 08:38:14 +00:00
|
|
|
const virMacAddr *macaddr,
|
|
|
|
const unsigned char *vmuuid,
|
2019-10-01 17:56:35 +00:00
|
|
|
const virNetDevVPortProfile *ovsport,
|
2019-10-01 16:25:47 +00:00
|
|
|
const virNetDevVlan *virtVlan)
|
2012-02-10 21:09:00 +00:00
|
|
|
{
|
|
|
|
char macaddrstr[VIR_MAC_STRING_BUFLEN];
|
2012-03-07 07:15:36 +00:00
|
|
|
char ifuuidstr[VIR_UUID_STRING_BUFLEN];
|
|
|
|
char vmuuidstr[VIR_UUID_STRING_BUFLEN];
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *attachedmac_ex_id = NULL;
|
|
|
|
g_autofree char *ifaceid_ex_id = NULL;
|
|
|
|
g_autofree char *profile_ex_id = NULL;
|
|
|
|
g_autofree char *vmid_ex_id = NULL;
|
2021-07-07 09:18:17 +00:00
|
|
|
g_autofree char *ifname_ex_id = NULL;
|
2012-02-10 21:09:00 +00:00
|
|
|
|
|
|
|
virMacAddrFormat(macaddr, macaddrstr);
|
2012-07-25 01:14:41 +00:00
|
|
|
virUUIDFormat(ovsport->interfaceID, ifuuidstr);
|
2012-03-07 07:15:36 +00:00
|
|
|
virUUIDFormat(vmuuid, vmuuidstr);
|
2012-02-10 21:09:00 +00:00
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
attachedmac_ex_id = g_strdup_printf("external-ids:attached-mac=\"%s\"",
|
|
|
|
macaddrstr);
|
|
|
|
ifaceid_ex_id = g_strdup_printf("external-ids:iface-id=\"%s\"", ifuuidstr);
|
|
|
|
vmid_ex_id = g_strdup_printf("external-ids:vm-id=\"%s\"", vmuuidstr);
|
2021-07-07 09:18:17 +00:00
|
|
|
ifname_ex_id = g_strdup_printf("external-ids:ifname=\"%s\"", ifname);
|
2012-07-25 01:14:41 +00:00
|
|
|
if (ovsport->profileID[0] != '\0') {
|
2019-10-22 13:26:14 +00:00
|
|
|
profile_ex_id = g_strdup_printf("external-ids:port-profile=\"%s\"",
|
|
|
|
ovsport->profileID);
|
2012-02-10 21:09:00 +00:00
|
|
|
}
|
2012-08-29 18:44:36 +00:00
|
|
|
|
2020-11-11 08:12:18 +00:00
|
|
|
cmd = virNetDevOpenvswitchCreateCmd();
|
2021-06-07 20:27:49 +00:00
|
|
|
virCommandAddArgList(cmd, "--", "--may-exist",
|
|
|
|
"add-port", brname, ifname, NULL);
|
2013-06-25 15:42:13 +00:00
|
|
|
|
2020-05-13 09:56:59 +00:00
|
|
|
virNetDevOpenvswitchConstructVlans(cmd, virtVlan);
|
2012-02-10 21:09:00 +00:00
|
|
|
|
2012-07-25 01:14:41 +00:00
|
|
|
if (ovsport->profileID[0] == '\0') {
|
2012-08-30 20:38:06 +00:00
|
|
|
virCommandAddArgList(cmd,
|
2018-09-19 08:38:14 +00:00
|
|
|
"--", "set", "Interface", ifname, attachedmac_ex_id,
|
|
|
|
"--", "set", "Interface", ifname, ifaceid_ex_id,
|
|
|
|
"--", "set", "Interface", ifname, vmid_ex_id,
|
|
|
|
"--", "set", "Interface", ifname,
|
|
|
|
"external-ids:iface-status=active",
|
|
|
|
NULL);
|
2012-02-10 21:09:00 +00:00
|
|
|
} else {
|
2012-08-30 20:38:06 +00:00
|
|
|
virCommandAddArgList(cmd,
|
2018-09-19 08:38:14 +00:00
|
|
|
"--", "set", "Interface", ifname, attachedmac_ex_id,
|
|
|
|
"--", "set", "Interface", ifname, ifaceid_ex_id,
|
|
|
|
"--", "set", "Interface", ifname, vmid_ex_id,
|
|
|
|
"--", "set", "Interface", ifname, profile_ex_id,
|
2021-07-07 09:18:17 +00:00
|
|
|
"--", "set", "Interface", ifname, ifname_ex_id,
|
2018-09-19 08:38:14 +00:00
|
|
|
"--", "set", "Interface", ifname,
|
|
|
|
"external-ids:iface-status=active",
|
|
|
|
NULL);
|
2012-02-10 21:09:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (virCommandRun(cmd, NULL) < 0) {
|
2014-11-28 08:37:42 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unable to add port %s to OVS bridge %s"),
|
|
|
|
ifname, brname);
|
2018-08-09 04:12:18 +00:00
|
|
|
return -1;
|
2012-02-10 21:09:00 +00:00
|
|
|
}
|
|
|
|
|
2018-08-09 04:12:18 +00:00
|
|
|
return 0;
|
2012-02-10 21:09:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virNetDevOpenvswitchRemovePort:
|
|
|
|
* @ifname: the network interface name
|
|
|
|
*
|
|
|
|
* Deletes an interface from a OVS bridge
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success or -1 in case of failure.
|
|
|
|
*/
|
2019-10-14 12:45:33 +00:00
|
|
|
int virNetDevOpenvswitchRemovePort(const char *brname G_GNUC_UNUSED, const char *ifname)
|
2012-02-10 21:09:00 +00:00
|
|
|
{
|
2020-11-11 08:12:18 +00:00
|
|
|
g_autoptr(virCommand) cmd = virNetDevOpenvswitchCreateCmd();
|
2012-02-10 21:09:00 +00:00
|
|
|
|
2017-02-07 15:16:43 +00:00
|
|
|
virCommandAddArgList(cmd, "--", "--if-exists", "del-port", ifname, NULL);
|
2012-02-10 21:09:00 +00:00
|
|
|
|
|
|
|
if (virCommandRun(cmd, NULL) < 0) {
|
2014-11-28 08:37:42 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unable to delete port %s from OVS"), ifname);
|
2018-08-09 04:12:18 +00:00
|
|
|
return -1;
|
2012-02-10 21:09:00 +00:00
|
|
|
}
|
2012-10-01 15:18:21 +00:00
|
|
|
|
2018-08-09 04:12:18 +00:00
|
|
|
return 0;
|
2012-10-01 15:18:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virNetDevOpenvswitchGetMigrateData:
|
|
|
|
* @migrate: a pointer to store the data into, allocated by this function
|
|
|
|
* @ifname: name of the interface for which data is being migrated
|
|
|
|
*
|
|
|
|
* Allocates data to be migrated specific to Open vSwitch
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success or -1 in case of failure
|
|
|
|
*/
|
|
|
|
int virNetDevOpenvswitchGetMigrateData(char **migrate, const char *ifname)
|
|
|
|
{
|
2016-01-27 09:35:17 +00:00
|
|
|
size_t len;
|
2020-11-11 08:12:18 +00:00
|
|
|
g_autoptr(virCommand) cmd = virNetDevOpenvswitchCreateCmd();
|
2012-10-01 15:18:21 +00:00
|
|
|
|
2017-02-07 15:16:43 +00:00
|
|
|
virCommandAddArgList(cmd, "--if-exists", "get", "Interface",
|
|
|
|
ifname, "external_ids:PortData", NULL);
|
2012-10-01 15:18:21 +00:00
|
|
|
|
|
|
|
virCommandSetOutputBuffer(cmd, migrate);
|
|
|
|
|
|
|
|
/* Run the command */
|
|
|
|
if (virCommandRun(cmd, NULL) < 0) {
|
2014-11-28 08:37:42 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unable to run command to get OVS port data for "
|
|
|
|
"interface %s"), ifname);
|
2018-08-09 04:12:18 +00:00
|
|
|
return -1;
|
2012-10-01 15:18:21 +00:00
|
|
|
}
|
|
|
|
|
2016-01-26 18:25:17 +00:00
|
|
|
/* Wipeout the newline, if it exists */
|
2016-01-27 09:35:17 +00:00
|
|
|
len = strlen(*migrate);
|
|
|
|
if (len > 0)
|
|
|
|
(*migrate)[len - 1] = '\0';
|
2016-01-26 18:25:17 +00:00
|
|
|
|
2018-08-09 04:12:18 +00:00
|
|
|
return 0;
|
2012-10-01 15:18:21 +00:00
|
|
|
}
|
2012-02-10 21:09:00 +00:00
|
|
|
|
2012-10-01 15:18:21 +00:00
|
|
|
/**
|
|
|
|
* virNetDevOpenvswitchSetMigrateData:
|
|
|
|
* @migrate: the data which was transferred during migration
|
|
|
|
* @ifname: the name of the interface the data is associated with
|
|
|
|
*
|
|
|
|
* Repopulates OVS per-port data on destination host
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success or -1 in case of failure
|
|
|
|
*/
|
|
|
|
int virNetDevOpenvswitchSetMigrateData(char *migrate, const char *ifname)
|
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
2012-10-01 15:18:21 +00:00
|
|
|
|
util: don't fail if no PortData is found while getting migrateData
Introduced by f6a2f97e
Problem Description:
After multiple times of migrating a domain, which has an ovs interface with no portData set,
with non-shared disk, nbd ports got overflowed.
The steps to reproduce the problem:
1 define and start a domain with its network configured as:
<interface type='bridge'>
<source bridge='br0'/>
<virtualport type='openvswitch'>
</virtualport>
<model type='virtio'/>
<driver name='vhost' queues='4'/>
</interface>
2 do not set the network's portData.
3 migrate(ToURI2) it with flag 91(1011011), which means:
VIR_MIGRATE_LIVE
VIR_MIGRATE_PEER2PEER
VIR_MIGRATE_PERSIST_DEST
VIR_MIGRATE_UNDEFINE_SOURCE
VIR_MIGRATE_NON_SHARED_DISK
4 migrate success, but we got an error log in libvirtd.log:
error : virCommandWait:2423 : internal error: Child process (ovs-vsctl --timeout=5 get Interface
vnet1 external_ids:PortData) unexpected exit status 1: ovs-vsctl: no key "PortData" in Interface
record "vnet1" column external_ids
5 migrate it back, migrate it , migrate it back, .......
6 nbd port got overflowed.
The reasons for the problem is :
1 virNetDevOpenvswitchGetMigrateData() takes it as wrong if no portData is available for the ovs
interface of a domain. (We think it's not appropriate, as portData is just OPTIONAL)
2 in func qemuMigrationBakeCookie(), it fails in qemuMigrationCookieAddNetwork(), and returns with -1.
qemuMigrationCookieAddNBD() is not called thereafter, and mig->nbd is still NULL.
3 However, qemuMigrationRun() just *WARN* if qemuMigrationBakeCookie() fails, migration still successes.
cookie is NULL, it's not baked on the src side.
4 On the destination side, it would alloc a port first and then free the nbd port in COOKIE.
But the cookie is NULL due to qemuMigrationCookieAddNetwork() failure at src side. thus the nbd port
is not freed.
In this patch, we add "--if-exists" option to make ovs-vsctl not raise error if there's no portData available.
Further more, because portData may be NULL in the cookie at the dest side, check it before setting portData.
Signed-off-by: Zhou Yimin <zhouyimin@huawei.com>
Signed-off-by: Zhang Bo <oscar.zhangbo@huawei.com>
2015-03-05 02:01:50 +00:00
|
|
|
if (!migrate) {
|
|
|
|
VIR_DEBUG("No OVS port data for interface %s", ifname);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-11-11 08:12:18 +00:00
|
|
|
cmd = virNetDevOpenvswitchCreateCmd();
|
2017-02-07 15:16:43 +00:00
|
|
|
virCommandAddArgList(cmd, "set", "Interface", ifname, NULL);
|
2012-10-01 15:18:21 +00:00
|
|
|
virCommandAddArgFormat(cmd, "external_ids:PortData=%s", migrate);
|
|
|
|
|
|
|
|
/* Run the command */
|
|
|
|
if (virCommandRun(cmd, NULL) < 0) {
|
2014-11-28 08:37:42 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unable to run command to set OVS port data for "
|
|
|
|
"interface %s"), ifname);
|
2018-08-09 04:12:18 +00:00
|
|
|
return -1;
|
2012-10-01 15:18:21 +00:00
|
|
|
}
|
|
|
|
|
2018-08-09 04:12:18 +00:00
|
|
|
return 0;
|
2012-02-10 21:09:00 +00:00
|
|
|
}
|
2016-11-18 22:51:13 +00:00
|
|
|
|
2019-07-16 07:33:38 +00:00
|
|
|
|
2016-11-18 22:51:13 +00:00
|
|
|
/**
|
2019-07-16 07:33:38 +00:00
|
|
|
* virNetDevOpenvswitchInterfaceParseStats:
|
|
|
|
* @json: Input string in JSON format
|
|
|
|
* @stats: parsed stats
|
2016-11-18 22:51:13 +00:00
|
|
|
*
|
2019-07-16 07:33:38 +00:00
|
|
|
* For given input string @json parse interface statistics and store them into
|
|
|
|
* @stats.
|
2016-11-18 22:51:13 +00:00
|
|
|
*
|
2019-07-16 07:33:38 +00:00
|
|
|
* Returns: 0 on success,
|
|
|
|
* -1 otherwise (with error reported).
|
2016-11-18 22:51:13 +00:00
|
|
|
*/
|
|
|
|
int
|
2019-07-16 07:33:38 +00:00
|
|
|
virNetDevOpenvswitchInterfaceParseStats(const char *json,
|
|
|
|
virDomainInterfaceStatsPtr stats)
|
2016-11-18 22:51:13 +00:00
|
|
|
{
|
2019-10-15 12:47:50 +00:00
|
|
|
g_autoptr(virJSONValue) jsonStats = NULL;
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *jsonMap = NULL;
|
2019-06-28 08:33:09 +00:00
|
|
|
size_t i;
|
2016-11-18 22:51:13 +00:00
|
|
|
|
2019-07-16 07:33:38 +00:00
|
|
|
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;
|
2016-11-18 22:51:13 +00:00
|
|
|
|
2019-07-16 07:33:38 +00:00
|
|
|
if (!(jsonStats = virJSONValueFromString(json)) ||
|
2019-06-28 08:33:09 +00:00
|
|
|
!virJSONValueIsArray(jsonStats) ||
|
|
|
|
!(jsonMap = virJSONValueArrayGet(jsonStats, 1))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unable to parse ovs-vsctl output"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < virJSONValueArraySize(jsonMap); i++) {
|
2021-03-11 07:16:13 +00:00
|
|
|
virJSONValue *item = virJSONValueArrayGet(jsonMap, i);
|
|
|
|
virJSONValue *jsonKey;
|
|
|
|
virJSONValue *jsonVal;
|
2019-06-28 08:33:09 +00:00
|
|
|
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"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-16 07:33:38 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2020-11-11 08:12:18 +00:00
|
|
|
g_autoptr(virCommand) cmd = virNetDevOpenvswitchCreateCmd();
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree char *output = NULL;
|
2019-07-16 07:33:38 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2019-06-28 08:33:09 +00:00
|
|
|
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) {
|
2016-11-18 22:51:13 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
2017-06-14 13:23:29 +00:00
|
|
|
_("Interface doesn't have any statistics"));
|
2018-08-09 04:12:18 +00:00
|
|
|
return -1;
|
2016-11-18 22:51:13 +00:00
|
|
|
}
|
|
|
|
|
2018-08-09 04:12:18 +00:00
|
|
|
return 0;
|
2016-11-18 22:51:13 +00:00
|
|
|
}
|
2016-12-22 09:33:28 +00:00
|
|
|
|
2018-07-01 23:24:19 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* virNetDeOpenvswitchGetMaster:
|
|
|
|
* @ifname: name of interface we're interested in
|
|
|
|
* @master: used to return a string containing the name of @ifname's "master"
|
|
|
|
* (this is the bridge or bond device that this device is attached to)
|
|
|
|
*
|
|
|
|
* Returns 0 on success, -1 on failure (if @ifname has no master
|
|
|
|
* @master will be NULL, but return value will still be 0 (success)).
|
|
|
|
*
|
|
|
|
* NB: This function is needed because the IFLA_MASTER attribute of an
|
|
|
|
* interface in a netlink dump (see virNetDevGetMaster()) will always
|
|
|
|
* return "ovs-system" for any interface that is attached to an OVS
|
|
|
|
* switch. When that happens, virNetDevOpenvswitchInterfaceGetMaster()
|
|
|
|
* must be called to get the "real" master of the interface.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virNetDevOpenvswitchInterfaceGetMaster(const char *ifname, char **master)
|
|
|
|
{
|
2020-12-02 12:43:15 +00:00
|
|
|
g_autoptr(virCommand) cmd = virNetDevOpenvswitchCreateCmd();
|
2018-07-01 23:24:19 +00:00
|
|
|
int exitstatus;
|
|
|
|
|
|
|
|
*master = NULL;
|
|
|
|
|
|
|
|
virCommandAddArgList(cmd, "iface-to-br", ifname, NULL);
|
|
|
|
virCommandSetOutputBuffer(cmd, master);
|
|
|
|
|
|
|
|
if (virCommandRun(cmd, &exitstatus) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unable to run command to get OVS master for "
|
|
|
|
"interface %s"), ifname);
|
2018-08-07 13:00:22 +00:00
|
|
|
return -1;
|
2018-07-01 23:24:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* non-0 exit code just means that the interface has no master in OVS */
|
|
|
|
if (exitstatus != 0)
|
|
|
|
VIR_FREE(*master);
|
|
|
|
|
|
|
|
if (*master) {
|
|
|
|
/* truncate at the first newline */
|
|
|
|
char *nl = strchr(*master, '\n');
|
|
|
|
if (nl)
|
|
|
|
*nl = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_DEBUG("OVS master for %s is %s", ifname, *master ? *master : "(none)");
|
|
|
|
|
2018-08-07 13:00:22 +00:00
|
|
|
return 0;
|
2018-07-01 23:24:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-12-16 16:52:14 +00:00
|
|
|
/**
|
|
|
|
* virNetDevOpenvswitchMaybeUnescapeReply:
|
|
|
|
* @reply: a string to unescape
|
|
|
|
*
|
|
|
|
* Depending on ovs-vsctl version a string might be escaped. For instance:
|
|
|
|
* -version 2.11.4 allows only is_alpha(), an underscore, a dash or a dot,
|
|
|
|
* -version 2.14.0 allows only is_alnum(), an underscore, a dash or a dot,
|
|
|
|
* any other character causes the string to be escaped.
|
|
|
|
*
|
|
|
|
* What this function does, is it checks whether @reply string consists solely
|
|
|
|
* from safe, not escaped characters (as defined by version 2.14.0) and if not
|
|
|
|
* an error is reported. If @reply is a string enclosed in double quotes, but
|
|
|
|
* otherwise safe those double quotes are removed.
|
|
|
|
*
|
|
|
|
* Returns: 0 on success,
|
|
|
|
* -1 otherwise (with error reported).
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virNetDevOpenvswitchMaybeUnescapeReply(char *reply)
|
|
|
|
{
|
|
|
|
g_autoptr(virJSONValue) json = NULL;
|
|
|
|
g_autofree char *jsonStr = NULL;
|
|
|
|
const char *tmp = NULL;
|
|
|
|
size_t replyLen = strlen(reply);
|
|
|
|
|
|
|
|
if (*reply != '"')
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
jsonStr = g_strdup_printf("{\"name\": %s}", reply);
|
|
|
|
if (!(json = virJSONValueFromString(jsonStr)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(json, "name"))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Malformed ovs-vsctl output"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return virStrcpy(reply, tmp, replyLen);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-12-22 09:33:28 +00:00
|
|
|
/**
|
2019-11-06 10:59:22 +00:00
|
|
|
* virNetDevOpenvswitchGetVhostuserIfname:
|
2016-12-22 09:33:28 +00:00
|
|
|
* @path: the path of the unix socket
|
2019-11-06 10:59:22 +00:00
|
|
|
* @server: true if OVS creates the @path
|
2016-12-22 09:33:28 +00:00
|
|
|
* @ifname: the retrieved name of the interface
|
|
|
|
*
|
2019-11-06 10:59:22 +00:00
|
|
|
* Retrieves the OVS ifname from vhostuser UNIX socket path.
|
|
|
|
* There are two types of vhostuser ports which differ in client/server
|
|
|
|
* role:
|
|
|
|
*
|
|
|
|
* dpdkvhostuser - OVS creates the socket and QEMU connects to it
|
|
|
|
* (@server = true)
|
|
|
|
* dpdkvhostuserclient - QEMU creates the socket and OVS connects to it
|
|
|
|
* (@server = false)
|
|
|
|
*
|
|
|
|
* Since the way of retrieving ifname is different in these two cases,
|
|
|
|
* caller must set @server according to the interface definition.
|
2016-12-22 09:33:28 +00:00
|
|
|
*
|
|
|
|
* Returns: 1 if interface is an openvswitch interface,
|
|
|
|
* 0 if it is not, but no other error occurred,
|
|
|
|
* -1 otherwise.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virNetDevOpenvswitchGetVhostuserIfname(const char *path,
|
2019-11-06 10:59:22 +00:00
|
|
|
bool server,
|
2016-12-22 09:33:28 +00:00
|
|
|
char **ifname)
|
|
|
|
{
|
2021-01-09 08:16:56 +00:00
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
|
|
|
g_autofree char *absoluteOvsVsctlPath = NULL;
|
2019-11-06 10:59:22 +00:00
|
|
|
int status;
|
2016-12-22 09:33:28 +00:00
|
|
|
|
2021-01-09 08:16:56 +00:00
|
|
|
if (!(absoluteOvsVsctlPath = virFindFileInPath(OVS_VSCTL))) {
|
|
|
|
/* If there is no 'ovs-vsctl' then the interface is
|
|
|
|
* probably not an OpenVSwitch interface and the @path to
|
|
|
|
* socket was created by some DPDK testing script (e.g.
|
|
|
|
* dpdk-testpmd). */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd = virNetDevOpenvswitchCreateCmd();
|
2019-11-06 10:59:22 +00:00
|
|
|
|
|
|
|
if (server) {
|
|
|
|
virCommandAddArgList(cmd, "--no-headings", "--columns=name", "find",
|
|
|
|
"Interface", NULL);
|
2020-12-16 17:52:48 +00:00
|
|
|
virCommandAddArgPair(cmd, "options:vhost-server-path", path);
|
2019-11-06 10:59:22 +00:00
|
|
|
} else {
|
|
|
|
const char *tmpIfname = NULL;
|
|
|
|
|
|
|
|
/* Openvswitch vhostuser path is hardcoded to
|
|
|
|
* /<runstatedir>/openvswitch/<ifname>
|
|
|
|
* for example: /var/run/openvswitch/dpdkvhostuser0
|
|
|
|
*
|
|
|
|
* so we pick the filename and check it's an openvswitch interface
|
|
|
|
*/
|
|
|
|
if (!path ||
|
|
|
|
!(tmpIfname = strrchr(path, '/'))) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmpIfname++;
|
|
|
|
virCommandAddArgList(cmd, "get", "Interface", tmpIfname, "name", NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
virCommandSetOutputBuffer(cmd, ifname);
|
|
|
|
if (virCommandRun(cmd, &status) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (status != 0) {
|
2016-12-22 09:33:28 +00:00
|
|
|
/* it's not a openvswitch vhostuser interface. */
|
2020-05-13 09:49:37 +00:00
|
|
|
return 0;
|
2016-12-22 09:33:28 +00:00
|
|
|
}
|
|
|
|
|
2021-02-05 03:10:51 +00:00
|
|
|
virStringTrimOptionalNewline(*ifname);
|
2020-12-16 16:52:14 +00:00
|
|
|
if (virNetDevOpenvswitchMaybeUnescapeReply(*ifname) < 0) {
|
|
|
|
VIR_FREE(*ifname);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-05-13 09:49:37 +00:00
|
|
|
return 1;
|
2016-12-22 09:33:28 +00:00
|
|
|
}
|
2017-07-17 15:49:00 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* virNetDevOpenvswitchUpdateVlan:
|
|
|
|
* @ifname: the network interface name
|
|
|
|
* @virtVlan: VLAN configuration to be applied
|
|
|
|
*
|
|
|
|
* Update VLAN configuration of an OVS port.
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success or -1 in case of failure.
|
|
|
|
*/
|
|
|
|
int virNetDevOpenvswitchUpdateVlan(const char *ifname,
|
2019-10-01 16:25:47 +00:00
|
|
|
const virNetDevVlan *virtVlan)
|
2017-07-17 15:49:00 +00:00
|
|
|
{
|
2020-11-11 08:12:18 +00:00
|
|
|
g_autoptr(virCommand) cmd = virNetDevOpenvswitchCreateCmd();
|
2017-07-17 15:49:00 +00:00
|
|
|
|
|
|
|
virCommandAddArgList(cmd,
|
|
|
|
"--", "--if-exists", "clear", "Port", ifname, "tag",
|
|
|
|
"--", "--if-exists", "clear", "Port", ifname, "trunk",
|
|
|
|
"--", "--if-exists", "clear", "Port", ifname, "vlan_mode",
|
|
|
|
"--", "--if-exists", "set", "Port", ifname, NULL);
|
|
|
|
|
2020-05-13 09:56:59 +00:00
|
|
|
virNetDevOpenvswitchConstructVlans(cmd, virtVlan);
|
2017-07-17 15:49:00 +00:00
|
|
|
|
|
|
|
if (virCommandRun(cmd, NULL) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unable to set vlan configuration on port %s"), ifname);
|
2018-08-09 04:12:18 +00:00
|
|
|
return -1;
|
2017-07-17 15:49:00 +00:00
|
|
|
}
|
|
|
|
|
2018-08-09 04:12:18 +00:00
|
|
|
return 0;
|
2017-07-17 15:49:00 +00:00
|
|
|
}
|
2021-07-07 09:18:17 +00:00
|
|
|
|
2021-08-17 04:38:13 +00:00
|
|
|
static char*
|
|
|
|
virNetDevOpenvswitchFindUUID(const char *table,
|
|
|
|
const char *vmid_ex_id,
|
|
|
|
const char *ifname_ex_id)
|
|
|
|
{
|
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
|
|
|
char *uuid = NULL;
|
|
|
|
|
|
|
|
cmd = virNetDevOpenvswitchCreateCmd();
|
|
|
|
virCommandAddArgList(cmd, "--no-heading", "--columns=_uuid", "find", table,
|
|
|
|
vmid_ex_id, ifname_ex_id, NULL);
|
|
|
|
virCommandSetOutputBuffer(cmd, &uuid);
|
|
|
|
if (virCommandRun(cmd, NULL) < 0) {
|
|
|
|
VIR_WARN("Unable to find queue on port with %s", ifname_ex_id);
|
|
|
|
}
|
|
|
|
return uuid;
|
|
|
|
}
|
2021-07-07 09:18:17 +00:00
|
|
|
|
2021-08-17 04:38:12 +00:00
|
|
|
/*
|
|
|
|
* Average, peak, floor and burst in virNetDevBandwidth are in kbytes.
|
|
|
|
* However other_config in ovs qos is in bit.
|
|
|
|
* ingress_policing_rate in ovs interface is in kbit.
|
|
|
|
*/
|
2021-11-01 07:39:55 +00:00
|
|
|
#define VIR_NETDEV_TX_TO_OVS 8000
|
2021-08-17 04:38:12 +00:00
|
|
|
#define VIR_NETDEV_RX_TO_OVS 8
|
|
|
|
|
2021-07-07 09:18:17 +00:00
|
|
|
/**
|
|
|
|
* virNetDevOpenvswitchInterfaceSetQos:
|
|
|
|
* @ifname: on which interface
|
|
|
|
* @bandwidth: rates to set (may be NULL)
|
2021-08-17 04:38:11 +00:00
|
|
|
* @vmuuid: the Domain UUID that has this interface
|
2021-07-07 09:18:17 +00:00
|
|
|
* @swapped: true if IN/OUT should be set contrariwise
|
|
|
|
*
|
|
|
|
* Update qos configuration of an OVS port.
|
|
|
|
*
|
|
|
|
* If @swapped is set, the IN part of @bandwidth is set on
|
|
|
|
* @ifname's TX, and vice versa. If it is not set, IN is set on
|
|
|
|
* RX and OUT on TX. This is because for some types of interfaces
|
|
|
|
* domain and the host live on the same side of the interface (so
|
|
|
|
* domain's RX/TX is host's RX/TX), and for some it's swapped
|
|
|
|
* (domain's RX/TX is hosts's TX/RX).
|
|
|
|
*
|
|
|
|
* Return 0 on success, -1 otherwise.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virNetDevOpenvswitchInterfaceSetQos(const char *ifname,
|
|
|
|
const virNetDevBandwidth *bandwidth,
|
2021-08-17 04:38:11 +00:00
|
|
|
const unsigned char *vmuuid,
|
2021-07-07 09:18:17 +00:00
|
|
|
bool swapped)
|
|
|
|
{
|
|
|
|
virNetDevBandwidthRate *rx = NULL; /* From domain POV */
|
|
|
|
virNetDevBandwidthRate *tx = NULL; /* From domain POV */
|
|
|
|
|
|
|
|
if (!bandwidth) {
|
|
|
|
/* nothing to be enabled */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (geteuid() != 0) {
|
|
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
|
|
|
|
_("Network bandwidth tuning is not available"
|
|
|
|
" in session mode"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ifname) {
|
|
|
|
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
|
|
|
|
_("Unable to set bandwidth for interface because "
|
|
|
|
"device name is unknown"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (swapped) {
|
|
|
|
rx = bandwidth->out;
|
|
|
|
tx = bandwidth->in;
|
|
|
|
} else {
|
|
|
|
rx = bandwidth->in;
|
|
|
|
tx = bandwidth->out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bandwidth->out && !bandwidth->in) {
|
2021-08-17 04:38:11 +00:00
|
|
|
if (virNetDevOpenvswitchInterfaceClearQos(ifname, vmuuid) < 0) {
|
2021-07-07 09:18:17 +00:00
|
|
|
VIR_WARN("Clean qos for interface %s failed", ifname);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tx && tx->average) {
|
|
|
|
char vmuuidstr[VIR_UUID_STRING_BUFLEN];
|
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
|
|
|
g_autofree char *vmid_ex_id = NULL;
|
|
|
|
g_autofree char *ifname_ex_id = NULL;
|
|
|
|
g_autofree char *average = NULL;
|
|
|
|
g_autofree char *peak = NULL;
|
|
|
|
g_autofree char *burst = NULL;
|
|
|
|
g_autofree char *qos_uuid = NULL;
|
|
|
|
g_autofree char *queue_uuid = NULL;
|
|
|
|
|
2021-08-17 04:38:12 +00:00
|
|
|
average = g_strdup_printf("%llu", tx->average * VIR_NETDEV_TX_TO_OVS);
|
2021-07-07 09:18:17 +00:00
|
|
|
if (tx->burst)
|
2021-08-17 04:38:12 +00:00
|
|
|
burst = g_strdup_printf("%llu", tx->burst * VIR_NETDEV_TX_TO_OVS);
|
2021-07-07 09:18:17 +00:00
|
|
|
if (tx->peak)
|
2021-08-17 04:38:12 +00:00
|
|
|
peak = g_strdup_printf("%llu", tx->peak * VIR_NETDEV_TX_TO_OVS);
|
2021-07-07 09:18:17 +00:00
|
|
|
|
2021-08-17 04:38:11 +00:00
|
|
|
virUUIDFormat(vmuuid, vmuuidstr);
|
2021-07-07 09:18:17 +00:00
|
|
|
vmid_ex_id = g_strdup_printf("external-ids:vm-id=\"%s\"", vmuuidstr);
|
|
|
|
ifname_ex_id = g_strdup_printf("external-ids:ifname=\"%s\"", ifname);
|
2021-08-17 04:38:13 +00:00
|
|
|
/* find queue */
|
|
|
|
queue_uuid = virNetDevOpenvswitchFindUUID("queue", vmid_ex_id, ifname_ex_id);
|
2021-07-07 09:18:17 +00:00
|
|
|
/* find qos */
|
2021-08-17 04:38:13 +00:00
|
|
|
qos_uuid = virNetDevOpenvswitchFindUUID("qos", vmid_ex_id, ifname_ex_id);
|
2021-07-07 09:18:17 +00:00
|
|
|
|
|
|
|
/* create qos and set */
|
|
|
|
cmd = virNetDevOpenvswitchCreateCmd();
|
|
|
|
if (queue_uuid && *queue_uuid) {
|
|
|
|
g_auto(GStrv) lines = g_strsplit(queue_uuid, "\n", 0);
|
|
|
|
virCommandAddArgList(cmd, "set", "queue", lines[0], NULL);
|
|
|
|
} else {
|
|
|
|
virCommandAddArgList(cmd, "set", "port", ifname, "qos=@qos1",
|
|
|
|
vmid_ex_id, ifname_ex_id,
|
|
|
|
"--", "--id=@qos1", "create", "qos", "type=linux-htb", NULL);
|
|
|
|
virCommandAddArgFormat(cmd, "other_config:min-rate=%s", average);
|
|
|
|
if (burst) {
|
|
|
|
virCommandAddArgFormat(cmd, "other_config:burst=%s", burst);
|
|
|
|
}
|
|
|
|
if (peak) {
|
|
|
|
virCommandAddArgFormat(cmd, "other_config:max-rate=%s", peak);
|
|
|
|
}
|
|
|
|
virCommandAddArgList(cmd, "queues:0=@queue0", vmid_ex_id, ifname_ex_id,
|
|
|
|
"--", "--id=@queue0", "create", "queue", NULL);
|
|
|
|
}
|
|
|
|
virCommandAddArgFormat(cmd, "other_config:min-rate=%s", average);
|
|
|
|
if (burst) {
|
|
|
|
virCommandAddArgFormat(cmd, "other_config:burst=%s", burst);
|
|
|
|
}
|
|
|
|
if (peak) {
|
|
|
|
virCommandAddArgFormat(cmd, "other_config:max-rate=%s", peak);
|
|
|
|
}
|
|
|
|
virCommandAddArgList(cmd, vmid_ex_id, ifname_ex_id, NULL);
|
|
|
|
if (virCommandRun(cmd, NULL) < 0) {
|
2021-10-29 09:13:33 +00:00
|
|
|
if (queue_uuid && *queue_uuid) {
|
2021-07-07 09:18:17 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unable to set queue configuration on port %s"), ifname);
|
|
|
|
} else {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unable to create and set qos configuration on port %s"), ifname);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qos_uuid && *qos_uuid) {
|
|
|
|
g_auto(GStrv) lines = g_strsplit(qos_uuid, "\n", 0);
|
|
|
|
|
|
|
|
virCommandFree(cmd);
|
|
|
|
cmd = virNetDevOpenvswitchCreateCmd();
|
|
|
|
virCommandAddArgList(cmd, "set", "qos", lines[0], NULL);
|
|
|
|
virCommandAddArgFormat(cmd, "other_config:min-rate=%s", average);
|
|
|
|
if (burst) {
|
|
|
|
virCommandAddArgFormat(cmd, "other_config:burst=%s", burst);
|
|
|
|
}
|
|
|
|
if (peak) {
|
|
|
|
virCommandAddArgFormat(cmd, "other_config:max-rate=%s", peak);
|
|
|
|
}
|
|
|
|
virCommandAddArgList(cmd, vmid_ex_id, ifname_ex_id, NULL);
|
|
|
|
if (virCommandRun(cmd, NULL) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unable to set qos configuration on port %s"), ifname);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2021-08-17 04:38:14 +00:00
|
|
|
} else {
|
|
|
|
if (virNetDevOpenvswitchInterfaceClearTxQos(ifname, vmuuid) < 0) {
|
|
|
|
VIR_WARN("Clean tx qos for interface %s failed", ifname);
|
|
|
|
}
|
2021-07-07 09:18:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (rx) {
|
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
|
|
|
|
|
|
|
cmd = virNetDevOpenvswitchCreateCmd();
|
|
|
|
virCommandAddArgList(cmd, "set", "Interface", ifname, NULL);
|
2021-08-17 04:38:12 +00:00
|
|
|
virCommandAddArgFormat(cmd, "ingress_policing_rate=%llu",
|
|
|
|
rx->average * VIR_NETDEV_RX_TO_OVS);
|
2021-07-07 09:18:17 +00:00
|
|
|
if (rx->burst)
|
2021-08-17 04:38:12 +00:00
|
|
|
virCommandAddArgFormat(cmd, "ingress_policing_burst=%llu",
|
|
|
|
rx->burst * VIR_NETDEV_RX_TO_OVS);
|
2021-07-07 09:18:17 +00:00
|
|
|
|
|
|
|
if (virCommandRun(cmd, NULL) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unable to set vlan configuration on port %s"), ifname);
|
|
|
|
return -1;
|
|
|
|
}
|
2021-08-17 04:38:14 +00:00
|
|
|
} else {
|
|
|
|
if (virNetDevOpenvswitchInterfaceClearRxQos(ifname) < 0) {
|
|
|
|
VIR_WARN("Clean rx qos for interface %s failed", ifname);
|
|
|
|
}
|
2021-07-07 09:18:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2021-08-17 04:38:14 +00:00
|
|
|
virNetDevOpenvswitchInterfaceClearTxQos(const char *ifname,
|
|
|
|
const unsigned char *vmuuid)
|
2021-07-07 09:18:17 +00:00
|
|
|
{
|
2021-08-17 04:38:15 +00:00
|
|
|
int ret = 0;
|
2021-07-07 09:18:17 +00:00
|
|
|
char vmuuidstr[VIR_UUID_STRING_BUFLEN];
|
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
2021-08-17 04:38:13 +00:00
|
|
|
g_autofree char *ifname_ex_id = NULL;
|
2021-07-07 09:18:17 +00:00
|
|
|
g_autofree char *vmid_ex_id = NULL;
|
|
|
|
g_autofree char *qos_uuid = NULL;
|
|
|
|
g_autofree char *queue_uuid = NULL;
|
|
|
|
g_autofree char *port_qos = NULL;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
/* find qos */
|
2021-08-17 04:38:11 +00:00
|
|
|
virUUIDFormat(vmuuid, vmuuidstr);
|
2021-07-07 09:18:17 +00:00
|
|
|
vmid_ex_id = g_strdup_printf("external-ids:vm-id=\"%s\"", vmuuidstr);
|
2021-08-17 04:38:13 +00:00
|
|
|
ifname_ex_id = g_strdup_printf("external-ids:ifname=\"%s\"", ifname);
|
2021-07-07 09:18:17 +00:00
|
|
|
/* find queue */
|
2021-08-17 04:38:13 +00:00
|
|
|
queue_uuid = virNetDevOpenvswitchFindUUID("queue", vmid_ex_id, ifname_ex_id);
|
|
|
|
/* find qos */
|
|
|
|
qos_uuid = virNetDevOpenvswitchFindUUID("qos", vmid_ex_id, ifname_ex_id);
|
2021-07-07 09:18:17 +00:00
|
|
|
|
|
|
|
if (qos_uuid && *qos_uuid) {
|
|
|
|
g_auto(GStrv) lines = g_strsplit(qos_uuid, "\n", 0);
|
|
|
|
|
|
|
|
/* destroy qos */
|
|
|
|
for (i = 0; lines[i] != NULL; i++) {
|
|
|
|
const char *line = lines[i];
|
|
|
|
if (!*line) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
virCommandFree(cmd);
|
|
|
|
cmd = virNetDevOpenvswitchCreateCmd();
|
|
|
|
virCommandAddArgList(cmd, "--no-heading", "--columns=_uuid", "--if-exists",
|
2021-08-17 04:38:13 +00:00
|
|
|
"list", "port", ifname, "qos", NULL);
|
2021-07-07 09:18:17 +00:00
|
|
|
virCommandSetOutputBuffer(cmd, &port_qos);
|
|
|
|
if (virCommandRun(cmd, NULL) < 0) {
|
|
|
|
VIR_WARN("Unable to remove port qos on port %s", ifname);
|
|
|
|
}
|
|
|
|
if (port_qos && *port_qos) {
|
|
|
|
virCommandFree(cmd);
|
|
|
|
cmd = virNetDevOpenvswitchCreateCmd();
|
|
|
|
virCommandAddArgList(cmd, "remove", "port", ifname, "qos", line, NULL);
|
|
|
|
if (virCommandRun(cmd, NULL) < 0) {
|
|
|
|
VIR_WARN("Unable to remove port qos on port %s", ifname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
virCommandFree(cmd);
|
|
|
|
cmd = virNetDevOpenvswitchCreateCmd();
|
|
|
|
virCommandAddArgList(cmd, "destroy", "qos", line, NULL);
|
|
|
|
if (virCommandRun(cmd, NULL) < 0) {
|
2021-08-17 04:38:15 +00:00
|
|
|
VIR_WARN("Unable to destroy qos on port %s", ifname);
|
|
|
|
ret = -1;
|
2021-07-07 09:18:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* destroy queue */
|
|
|
|
if (queue_uuid && *queue_uuid) {
|
|
|
|
g_auto(GStrv) lines = g_strsplit(queue_uuid, "\n", 0);
|
|
|
|
|
|
|
|
for (i = 0; lines[i] != NULL; i++) {
|
|
|
|
const char *line = lines[i];
|
|
|
|
if (!*line) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
virCommandFree(cmd);
|
|
|
|
cmd = virNetDevOpenvswitchCreateCmd();
|
|
|
|
virCommandAddArgList(cmd, "destroy", "queue", line, NULL);
|
|
|
|
if (virCommandRun(cmd, NULL) < 0) {
|
2021-08-17 04:38:15 +00:00
|
|
|
VIR_WARN("Unable to destroy queue on port %s", ifname);
|
|
|
|
ret = -1;
|
2021-07-07 09:18:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-17 04:38:15 +00:00
|
|
|
return ret;
|
2021-07-07 09:18:17 +00:00
|
|
|
}
|
2021-08-17 04:38:14 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
virNetDevOpenvswitchInterfaceClearRxQos(const char *ifname)
|
|
|
|
{
|
|
|
|
g_autoptr(virCommand) cmd = NULL;
|
|
|
|
|
|
|
|
cmd = virNetDevOpenvswitchCreateCmd();
|
|
|
|
virCommandAddArgList(cmd, "set", "Interface", ifname, NULL);
|
|
|
|
virCommandAddArgFormat(cmd, "ingress_policing_rate=%llu", 0llu);
|
|
|
|
virCommandAddArgFormat(cmd, "ingress_policing_burst=%llu", 0llu);
|
|
|
|
|
|
|
|
if (virCommandRun(cmd, NULL) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
2021-08-17 04:38:15 +00:00
|
|
|
_("Unable to reset ingress on port %s"), ifname);
|
2021-08-17 04:38:14 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virNetDevOpenvswitchInterfaceClearQos(const char *ifname,
|
|
|
|
const unsigned char *vmuuid)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (virNetDevOpenvswitchInterfaceClearTxQos(ifname, vmuuid) < 0) {
|
|
|
|
VIR_WARN("Clean tx qos for interface %s failed", ifname);
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virNetDevOpenvswitchInterfaceClearRxQos(ifname) < 0) {
|
|
|
|
VIR_WARN("Clean rx qos for interface %s failed", ifname);
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|