mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 05:35:25 +00:00
ch: Enable ETHERNET Network mode support
enable VIR_DOMAIN_NET_TYPE_ETHERNET network support for ch guests. Tested with following interface config: <interface type='ethernet'> <target dev='chtap0' managed="yes"/> <model type='virtio'/> <driver queues='2'/> <interface> Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com> Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
parent
05d46e4e08
commit
6316f26cd2
@ -21,6 +21,7 @@ src/bhyve/bhyve_process.c
|
||||
src/ch/ch_conf.c
|
||||
src/ch/ch_domain.c
|
||||
src/ch/ch_driver.c
|
||||
src/ch/ch_interface.c
|
||||
src/ch/ch_monitor.c
|
||||
src/ch/ch_process.c
|
||||
src/conf/backup_conf.c
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "virdomainobjlist.h"
|
||||
#include "virthread.h"
|
||||
#include "ch_capabilities.h"
|
||||
#include "virebtables.h"
|
||||
|
||||
#define CH_DRIVER_NAME "CH"
|
||||
#define CH_CMD "cloud-hypervisor"
|
||||
@ -75,6 +76,9 @@ struct _virCHDriver
|
||||
|
||||
/* pid file FD, ensures two copies of the driver can't use the same root */
|
||||
int lockFD;
|
||||
|
||||
/* Immutable pointer, lockless APIs. Pointless abstraction */
|
||||
ebtablesContext *ebtables;
|
||||
};
|
||||
|
||||
virCaps *virCHDriverCapsInit(void);
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include "ch_domain.h"
|
||||
#include "domain_driver.h"
|
||||
#include "domain_validate.h"
|
||||
#include "virchrdev.h"
|
||||
#include "virlog.h"
|
||||
#include "virtime.h"
|
||||
@ -355,3 +356,43 @@ virCHDomainObjFromDomain(virDomainPtr domain)
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
int
|
||||
virCHDomainValidateActualNetDef(virDomainNetDef *net)
|
||||
{
|
||||
virDomainNetType actualType = virDomainNetGetActualType(net);
|
||||
|
||||
/* hypervisor-agnostic validation */
|
||||
if (virDomainActualNetDefValidate(net) < 0)
|
||||
return -1;
|
||||
|
||||
/* CH specific validation */
|
||||
switch (actualType) {
|
||||
case VIR_DOMAIN_NET_TYPE_ETHERNET:
|
||||
if (net->guestIP.nips > 1) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||
_("ethernet type supports a single guest ip"));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
|
||||
case VIR_DOMAIN_NET_TYPE_BRIDGE:
|
||||
case VIR_DOMAIN_NET_TYPE_NETWORK:
|
||||
case VIR_DOMAIN_NET_TYPE_DIRECT:
|
||||
case VIR_DOMAIN_NET_TYPE_USER:
|
||||
case VIR_DOMAIN_NET_TYPE_SERVER:
|
||||
case VIR_DOMAIN_NET_TYPE_CLIENT:
|
||||
case VIR_DOMAIN_NET_TYPE_MCAST:
|
||||
case VIR_DOMAIN_NET_TYPE_INTERNAL:
|
||||
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
|
||||
case VIR_DOMAIN_NET_TYPE_UDP:
|
||||
case VIR_DOMAIN_NET_TYPE_VDPA:
|
||||
case VIR_DOMAIN_NET_TYPE_NULL:
|
||||
case VIR_DOMAIN_NET_TYPE_VDS:
|
||||
case VIR_DOMAIN_NET_TYPE_LAST:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -75,3 +75,6 @@ virCHDomainGetMachineName(virDomainObj *vm);
|
||||
|
||||
virDomainObj *
|
||||
virCHDomainObjFromDomain(virDomainPtr domain);
|
||||
|
||||
int
|
||||
virCHDomainValidateActualNetDef(virDomainNetDef *net);
|
||||
|
99
src/ch/ch_interface.c
Normal file
99
src/ch/ch_interface.c
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright Microsoft Corp. 2023
|
||||
*
|
||||
* ch_interface.c: methods to connect guest interfaces to appropriate host
|
||||
* backends
|
||||
*
|
||||
* 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 "domain_conf.h"
|
||||
#include "domain_interface.h"
|
||||
#include "virebtables.h"
|
||||
#include "viralloc.h"
|
||||
#include "ch_interface.h"
|
||||
#include "virjson.h"
|
||||
#include "virlog.h"
|
||||
|
||||
|
||||
#define VIR_FROM_THIS VIR_FROM_CH
|
||||
|
||||
VIR_LOG_INIT("ch.ch_interface");
|
||||
|
||||
/**
|
||||
* virCHConnetNetworkInterfaces:
|
||||
* @driver: pointer to ch driver object
|
||||
* @vm: pointer to domain definition
|
||||
* @net: pointer to a guest net
|
||||
* @nicindexes: returned array of FDs of guest interfaces
|
||||
* @nnicindexes: returned number of guest interfaces
|
||||
*
|
||||
*
|
||||
* Returns 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
virCHConnetNetworkInterfaces(virCHDriver *driver,
|
||||
virDomainDef *vm,
|
||||
virDomainNetDef *net,
|
||||
int *tapfds, int **nicindexes, size_t *nnicindexes)
|
||||
{
|
||||
virDomainNetType actualType = virDomainNetGetActualType(net);
|
||||
|
||||
|
||||
switch (actualType) {
|
||||
case VIR_DOMAIN_NET_TYPE_ETHERNET:
|
||||
|
||||
if (virDomainInterfaceEthernetConnect(vm, net,
|
||||
driver->ebtables, false,
|
||||
driver->privileged, tapfds,
|
||||
net->driver.virtio.queues) < 0)
|
||||
return -1;
|
||||
|
||||
G_GNUC_FALLTHROUGH;
|
||||
case VIR_DOMAIN_NET_TYPE_NETWORK:
|
||||
case VIR_DOMAIN_NET_TYPE_BRIDGE:
|
||||
case VIR_DOMAIN_NET_TYPE_DIRECT:
|
||||
if (nicindexes && nnicindexes && net->ifname) {
|
||||
int nicindex = 0;
|
||||
|
||||
if (virNetDevGetIndex(net->ifname, &nicindex) < 0)
|
||||
return -1;
|
||||
|
||||
VIR_APPEND_ELEMENT(*nicindexes, *nnicindexes, nicindex);
|
||||
}
|
||||
|
||||
break;
|
||||
case VIR_DOMAIN_NET_TYPE_USER:
|
||||
case VIR_DOMAIN_NET_TYPE_SERVER:
|
||||
case VIR_DOMAIN_NET_TYPE_CLIENT:
|
||||
case VIR_DOMAIN_NET_TYPE_MCAST:
|
||||
case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
|
||||
case VIR_DOMAIN_NET_TYPE_INTERNAL:
|
||||
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
|
||||
case VIR_DOMAIN_NET_TYPE_UDP:
|
||||
case VIR_DOMAIN_NET_TYPE_VDPA:
|
||||
case VIR_DOMAIN_NET_TYPE_NULL:
|
||||
case VIR_DOMAIN_NET_TYPE_VDS:
|
||||
case VIR_DOMAIN_NET_TYPE_LAST:
|
||||
default:
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("Unsupported Network type %1$d"), actualType);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
35
src/ch/ch_interface.h
Normal file
35
src/ch/ch_interface.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright Microsoft Corp. 2023
|
||||
*
|
||||
* ch_interface.c: methods to connect guest interfaces to appropriate host
|
||||
* backends
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "ch_conf.h"
|
||||
#include "virconftypes.h"
|
||||
|
||||
|
||||
int
|
||||
virCHConnetNetworkInterfaces(virCHDriver *driver,
|
||||
virDomainDef *vmdef,
|
||||
virDomainNetDef *netdef,
|
||||
int *tapfds,
|
||||
int **nicindexes,
|
||||
size_t *nnicindexes);
|
@ -24,7 +24,11 @@
|
||||
#include <unistd.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include "datatypes.h"
|
||||
#include "ch_conf.h"
|
||||
#include "ch_interface.h"
|
||||
#include "ch_monitor.h"
|
||||
#include "domain_interface.h"
|
||||
#include "viralloc.h"
|
||||
#include "vircommand.h"
|
||||
#include "virerror.h"
|
||||
@ -258,151 +262,95 @@ virCHMonitorBuildDisksJson(virJSONValue *content, virDomainDef *vmdef)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
virCHMonitorBuildNetJson(virJSONValue *nets,
|
||||
virDomainNetDef *netdef,
|
||||
size_t *nnicindexes,
|
||||
int **nicindexes)
|
||||
/**
|
||||
* virCHMonitorBuildNetJson:
|
||||
* @net: pointer to a guest network definition
|
||||
* @jsonstr: returned network json
|
||||
*
|
||||
* Build net json to send to CH
|
||||
* Returns 0 on success or -1 in case of error
|
||||
*/
|
||||
int
|
||||
virCHMonitorBuildNetJson(virDomainNetDef *net, char **jsonstr)
|
||||
{
|
||||
virDomainNetType netType = virDomainNetGetActualType(netdef);
|
||||
char macaddr[VIR_MAC_STRING_BUFLEN];
|
||||
g_autoptr(virJSONValue) net = NULL;
|
||||
g_autoptr(virJSONValue) net_json = virJSONValueNewObject();
|
||||
virDomainNetType actualType = virDomainNetGetActualType(net);
|
||||
|
||||
// check net type at first
|
||||
net = virJSONValueNewObject();
|
||||
if (actualType == VIR_DOMAIN_NET_TYPE_ETHERNET &&
|
||||
net->guestIP.nips == 1) {
|
||||
const virNetDevIPAddr *ip;
|
||||
g_autofree char *addr = NULL;
|
||||
virSocketAddr netmask;
|
||||
g_autofree char *netmaskStr = NULL;
|
||||
|
||||
switch (netType) {
|
||||
case VIR_DOMAIN_NET_TYPE_ETHERNET:
|
||||
if (netdef->guestIP.nips == 1) {
|
||||
const virNetDevIPAddr *ip = netdef->guestIP.ips[0];
|
||||
g_autofree char *addr = NULL;
|
||||
virSocketAddr netmask;
|
||||
g_autofree char *netmaskStr = NULL;
|
||||
ip = net->guestIP.ips[0];
|
||||
|
||||
if (!(addr = virSocketAddrFormat(&ip->address)))
|
||||
return -1;
|
||||
if (virJSONValueObjectAppendString(net, "ip", addr) < 0)
|
||||
return -1;
|
||||
if (!(addr = virSocketAddrFormat(&ip->address)))
|
||||
return -1;
|
||||
|
||||
if (virSocketAddrPrefixToNetmask(ip->prefix, &netmask, AF_INET) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("Failed to translate net prefix %1$d to netmask"),
|
||||
ip->prefix);
|
||||
return -1;
|
||||
}
|
||||
if (!(netmaskStr = virSocketAddrFormat(&netmask)))
|
||||
return -1;
|
||||
if (virJSONValueObjectAppendString(net, "mask", netmaskStr) < 0)
|
||||
return -1;
|
||||
} else if (netdef->guestIP.nips > 1) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||
_("ethernet type supports a single guest ip"));
|
||||
}
|
||||
if (virJSONValueObjectAppendString(net_json, "ip", addr) < 0)
|
||||
return -1;
|
||||
|
||||
/* network and bridge use a tap device, and direct uses a
|
||||
* macvtap device
|
||||
*/
|
||||
if (nicindexes && nnicindexes && netdef->ifname) {
|
||||
int nicindex = 0;
|
||||
if (virSocketAddrPrefixToNetmask(ip->prefix, &netmask, AF_INET) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("Failed to translate net prefix %1$d to netmask"),
|
||||
ip->prefix);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (virNetDevGetIndex(netdef->ifname, &nicindex) < 0)
|
||||
return -1;
|
||||
if (!(netmaskStr = virSocketAddrFormat(&netmask)))
|
||||
return -1;
|
||||
|
||||
VIR_APPEND_ELEMENT(*nicindexes, *nnicindexes, nicindex);
|
||||
}
|
||||
break;
|
||||
case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
|
||||
if ((virDomainChrType)netdef->data.vhostuser->type != VIR_DOMAIN_CHR_TYPE_UNIX) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||
_("vhost_user type support UNIX socket in this CH"));
|
||||
return -1;
|
||||
} else {
|
||||
if (virJSONValueObjectAppendString(net, "vhost_socket", netdef->data.vhostuser->data.nix.path) < 0)
|
||||
return -1;
|
||||
if (virJSONValueObjectAppendBoolean(net, "vhost_user", true) < 0)
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case VIR_DOMAIN_NET_TYPE_BRIDGE:
|
||||
case VIR_DOMAIN_NET_TYPE_NETWORK:
|
||||
case VIR_DOMAIN_NET_TYPE_DIRECT:
|
||||
case VIR_DOMAIN_NET_TYPE_USER:
|
||||
case VIR_DOMAIN_NET_TYPE_SERVER:
|
||||
case VIR_DOMAIN_NET_TYPE_CLIENT:
|
||||
case VIR_DOMAIN_NET_TYPE_MCAST:
|
||||
case VIR_DOMAIN_NET_TYPE_INTERNAL:
|
||||
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
|
||||
case VIR_DOMAIN_NET_TYPE_UDP:
|
||||
case VIR_DOMAIN_NET_TYPE_VDPA:
|
||||
case VIR_DOMAIN_NET_TYPE_NULL:
|
||||
case VIR_DOMAIN_NET_TYPE_VDS:
|
||||
case VIR_DOMAIN_NET_TYPE_LAST:
|
||||
default:
|
||||
virReportEnumRangeError(virDomainNetType, netType);
|
||||
if (virJSONValueObjectAppendString(net_json, "mask", netmaskStr) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (netdef->ifname != NULL) {
|
||||
if (virJSONValueObjectAppendString(net, "tap", netdef->ifname) < 0)
|
||||
return -1;
|
||||
}
|
||||
if (virJSONValueObjectAppendString(net, "mac", virMacAddrFormat(&netdef->mac, macaddr)) < 0)
|
||||
if (virJSONValueObjectAppendString(net_json, "mac",
|
||||
virMacAddrFormat(&net->mac, macaddr)) < 0)
|
||||
return -1;
|
||||
|
||||
|
||||
if (netdef->virtio != NULL) {
|
||||
if (netdef->virtio->iommu == VIR_TRISTATE_SWITCH_ON) {
|
||||
if (virJSONValueObjectAppendBoolean(net, "iommu", true) < 0)
|
||||
if (net->virtio != NULL) {
|
||||
if (net->virtio->iommu == VIR_TRISTATE_SWITCH_ON) {
|
||||
if (virJSONValueObjectAppendBoolean(net_json, "iommu", true) < 0)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (netdef->driver.virtio.queues) {
|
||||
if (virJSONValueObjectAppendNumberInt(net, "num_queues", netdef->driver.virtio.queues) < 0)
|
||||
|
||||
/* Cloud-Hypervisor expects number of queues. 1 for rx and 1 for tx.
|
||||
* Multiply queue pairs by 2 to provide total number of queues to CH
|
||||
*/
|
||||
if (net->driver.virtio.queues) {
|
||||
if (virJSONValueObjectAppendNumberInt(net_json, "num_queues",
|
||||
2 * net->driver.virtio.queues) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (netdef->driver.virtio.rx_queue_size || netdef->driver.virtio.tx_queue_size) {
|
||||
if (netdef->driver.virtio.rx_queue_size != netdef->driver.virtio.tx_queue_size) {
|
||||
if (net->driver.virtio.rx_queue_size || net->driver.virtio.tx_queue_size) {
|
||||
if (net->driver.virtio.rx_queue_size !=
|
||||
net->driver.virtio.tx_queue_size) {
|
||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("virtio rx_queue_size option %1$d is not same with tx_queue_size %2$d"),
|
||||
netdef->driver.virtio.rx_queue_size,
|
||||
netdef->driver.virtio.tx_queue_size);
|
||||
_("virtio rx_queue_size option %1$d is not same with tx_queue_size %2$d"),
|
||||
net->driver.virtio.rx_queue_size,
|
||||
net->driver.virtio.tx_queue_size);
|
||||
return -1;
|
||||
}
|
||||
if (virJSONValueObjectAppendNumberInt(net, "queue_size", netdef->driver.virtio.rx_queue_size) < 0)
|
||||
if (virJSONValueObjectAppendNumberInt(net_json, "queue_size",
|
||||
net->driver.virtio.rx_queue_size) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (virJSONValueArrayAppend(nets, &net) < 0)
|
||||
if (net->mtu) {
|
||||
if (virJSONValueObjectAppendNumberInt(net_json, "mtu", net->mtu) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(*jsonstr = virJSONValueToString(net_json, false)))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
virCHMonitorBuildNetsJson(virJSONValue *content,
|
||||
virDomainDef *vmdef,
|
||||
size_t *nnicindexes,
|
||||
int **nicindexes)
|
||||
{
|
||||
g_autoptr(virJSONValue) nets = NULL;
|
||||
size_t i;
|
||||
|
||||
if (vmdef->nnets > 0) {
|
||||
nets = virJSONValueNewArray();
|
||||
|
||||
for (i = 0; i < vmdef->nnets; i++) {
|
||||
if (virCHMonitorBuildNetJson(nets, vmdef->nets[i],
|
||||
nnicindexes, nicindexes) < 0)
|
||||
return -1;
|
||||
}
|
||||
if (virJSONValueObjectAppend(content, "net", &nets) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
virCHMonitorBuildDeviceJson(virJSONValue *devices,
|
||||
virDomainHostdevDef *hostdevdef)
|
||||
@ -456,11 +404,8 @@ virCHMonitorBuildDevicesJson(virJSONValue *content,
|
||||
}
|
||||
|
||||
static int
|
||||
virCHMonitorBuildVMJson(virCHDriver *driver,
|
||||
virDomainDef *vmdef,
|
||||
char **jsonstr,
|
||||
size_t *nnicindexes,
|
||||
int **nicindexes)
|
||||
virCHMonitorBuildVMJson(virCHDriver *driver, virDomainDef *vmdef,
|
||||
char **jsonstr)
|
||||
{
|
||||
g_autoptr(virJSONValue) content = virJSONValueNewObject();
|
||||
|
||||
@ -490,10 +435,6 @@ virCHMonitorBuildVMJson(virCHDriver *driver,
|
||||
if (virCHMonitorBuildDisksJson(content, vmdef) < 0)
|
||||
return -1;
|
||||
|
||||
|
||||
if (virCHMonitorBuildNetsJson(content, vmdef, nnicindexes, nicindexes) < 0)
|
||||
return -1;
|
||||
|
||||
if (virCHMonitorBuildDevicesJson(content, vmdef) < 0)
|
||||
return -1;
|
||||
|
||||
@ -877,10 +818,7 @@ virCHMonitorShutdownVMM(virCHMonitor *mon)
|
||||
}
|
||||
|
||||
int
|
||||
virCHMonitorCreateVM(virCHDriver *driver,
|
||||
virCHMonitor *mon,
|
||||
size_t *nnicindexes,
|
||||
int **nicindexes)
|
||||
virCHMonitorCreateVM(virCHDriver *driver, virCHMonitor *mon)
|
||||
{
|
||||
g_autofree char *url = NULL;
|
||||
int responseCode = 0;
|
||||
@ -892,8 +830,7 @@ virCHMonitorCreateVM(virCHDriver *driver,
|
||||
headers = curl_slist_append(headers, "Accept: application/json");
|
||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||
|
||||
if (virCHMonitorBuildVMJson(driver, mon->vm->def, &payload,
|
||||
nnicindexes, nicindexes) != 0)
|
||||
if (virCHMonitorBuildVMJson(driver, mon->vm->def, &payload) != 0)
|
||||
return -1;
|
||||
|
||||
VIR_WITH_OBJECT_LOCK_GUARD(mon) {
|
||||
|
@ -104,10 +104,7 @@ void virCHMonitorClose(virCHMonitor *mon);
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCHMonitor, virCHMonitorClose);
|
||||
|
||||
|
||||
int virCHMonitorCreateVM(virCHDriver *driver,
|
||||
virCHMonitor *mon,
|
||||
size_t *nnicindexes,
|
||||
int **nicindexes);
|
||||
int virCHMonitorCreateVM(virCHDriver *driver, virCHMonitor *mon);
|
||||
int virCHMonitorBootVM(virCHMonitor *mon);
|
||||
int virCHMonitorShutdownVM(virCHMonitor *mon);
|
||||
int virCHMonitorRebootVM(virCHMonitor *mon);
|
||||
@ -123,3 +120,5 @@ size_t virCHMonitorGetThreadInfo(virCHMonitor *mon, bool refresh,
|
||||
virCHMonitorThreadInfo **threads);
|
||||
int virCHMonitorGetIOThreads(virCHMonitor *mon,
|
||||
virDomainIOThreadInfo ***iothreads);
|
||||
int
|
||||
virCHMonitorBuildNetJson(virDomainNetDef *netdef, char **jsonstr);
|
||||
|
@ -22,14 +22,19 @@
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include "ch_domain.h"
|
||||
#include "ch_monitor.h"
|
||||
#include "ch_process.h"
|
||||
#include "domain_cgroup.h"
|
||||
#include "domain_interface.h"
|
||||
#include "virerror.h"
|
||||
#include "virfile.h"
|
||||
#include "virjson.h"
|
||||
#include "virlog.h"
|
||||
#include "virstring.h"
|
||||
#include "ch_interface.h"
|
||||
|
||||
#define VIR_FROM_THIS VIR_FROM_CH
|
||||
|
||||
@ -448,13 +453,192 @@ virCHProcessSetupVcpus(virDomainObj *vm)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define PKT_TIMEOUT_MS 500 /* ms */
|
||||
|
||||
static char *
|
||||
chSocketRecv(int sock)
|
||||
{
|
||||
struct pollfd pfds[1];
|
||||
char *buf = NULL;
|
||||
size_t buf_len = 1024;
|
||||
int ret;
|
||||
|
||||
buf = g_new0(char, buf_len);
|
||||
|
||||
pfds[0].fd = sock;
|
||||
pfds[0].events = POLLIN;
|
||||
|
||||
do {
|
||||
ret = poll(pfds, G_N_ELEMENTS(pfds), PKT_TIMEOUT_MS);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
|
||||
if (ret <= 0) {
|
||||
if (ret < 0) {
|
||||
virReportSystemError(errno, _("Poll on sock %1$d failed"), sock);
|
||||
} else if (ret == 0) {
|
||||
virReportSystemError(errno, _("Poll on sock %1$d timed out"), sock);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
do {
|
||||
ret = recv(sock, buf, buf_len - 1, 0);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
|
||||
if (ret < 0) {
|
||||
virReportSystemError(errno, _("recv on sock %1$d failed"), sock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer(&buf);
|
||||
}
|
||||
|
||||
#undef PKT_TIMEOUT_MS
|
||||
|
||||
/**
|
||||
* chProcessAddNetworkDevices:
|
||||
* @driver: pointer to ch driver object
|
||||
* @mon: pointer to the monitor object
|
||||
* @vmdef: pointer to domain definition
|
||||
* @nicindexes: returned array of FDs of guest interfaces
|
||||
* @nnicindexes: returned number of network indexes
|
||||
*
|
||||
* Send tap fds to CH process via AddNet api. Capture the network indexes of
|
||||
* guest interfaces in nicindexes.
|
||||
*
|
||||
* Returns 0 on success, -1 on error.
|
||||
*/
|
||||
static int
|
||||
chProcessAddNetworkDevices(virCHDriver *driver,
|
||||
virCHMonitor *mon,
|
||||
virDomainDef *vmdef,
|
||||
int **nicindexes,
|
||||
size_t *nnicindexes)
|
||||
{
|
||||
size_t i;
|
||||
VIR_AUTOCLOSE mon_sockfd = -1;
|
||||
struct sockaddr_un server_addr;
|
||||
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||
g_auto(virBuffer) http_headers = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
if (!virBitmapIsBitSet(driver->chCaps, CH_MULTIFD_IN_ADDNET)) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("Guest networking is not supported by this version of ch"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
mon_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (mon_sockfd < 0) {
|
||||
virReportSystemError(errno, "%s", _("Failed to open a UNIX socket"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sun_family = AF_UNIX;
|
||||
if (virStrcpyStatic(server_addr.sun_path, mon->socketpath) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("UNIX socket path '%1$s' too long"), mon->socketpath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (connect(mon_sockfd, (struct sockaddr *)&server_addr,
|
||||
sizeof(server_addr)) == -1) {
|
||||
virReportSystemError(errno, "%s", _("Failed to connect to mon socket"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
virBufferAddLit(&http_headers, "PUT /api/v1/vm.add-net HTTP/1.1\r\n");
|
||||
virBufferAddLit(&http_headers, "Host: localhost\r\n");
|
||||
virBufferAddLit(&http_headers, "Content-Type: application/json\r\n");
|
||||
|
||||
for (i = 0; i < vmdef->nnets; i++) {
|
||||
g_autofree int *tapfds = NULL;
|
||||
g_autofree char *payload = NULL;
|
||||
g_autofree char *response = NULL;
|
||||
size_t j;
|
||||
size_t tapfd_len;
|
||||
int http_res;
|
||||
int rc;
|
||||
|
||||
if (vmdef->nets[i]->driver.virtio.queues == 0) {
|
||||
/* "queues" here refers to queue pairs. When 0, initialize
|
||||
* queue pairs to 1.
|
||||
*/
|
||||
vmdef->nets[i]->driver.virtio.queues = 1;
|
||||
}
|
||||
tapfd_len = vmdef->nets[i]->driver.virtio.queues;
|
||||
|
||||
if (virCHDomainValidateActualNetDef(vmdef->nets[i]) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("net definition failed validation"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
tapfds = g_new0(int, tapfd_len);
|
||||
memset(tapfds, -1, (tapfd_len) * sizeof(int));
|
||||
|
||||
/* Connect Guest interfaces */
|
||||
if (virCHConnetNetworkInterfaces(driver, vmdef, vmdef->nets[i], tapfds,
|
||||
nicindexes, nnicindexes) < 0)
|
||||
return -1;
|
||||
|
||||
if (virCHMonitorBuildNetJson(vmdef->nets[i], &payload) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("Failed to build net json"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
VIR_DEBUG("payload sent with net-add request to CH = %s", payload);
|
||||
|
||||
virBufferAsprintf(&buf, "%s", virBufferCurrentContent(&http_headers));
|
||||
virBufferAsprintf(&buf, "Content-Length: %ld\r\n\r\n", strlen(payload));
|
||||
virBufferAsprintf(&buf, "%s", payload);
|
||||
payload = virBufferContentAndReset(&buf);
|
||||
|
||||
rc = virSocketSendMsgWithFDs(mon_sockfd, payload, tapfds, tapfd_len);
|
||||
|
||||
/* Close sent tap fds in Libvirt, as they have been dup()ed in CH */
|
||||
for (j = 0; j < tapfd_len; j++) {
|
||||
VIR_FORCE_CLOSE(tapfds[j]);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("Failed to send net-add request to CH"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Process the response from CH */
|
||||
response = chSocketRecv(mon_sockfd);
|
||||
if (response == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Parse the HTTP response code */
|
||||
rc = sscanf(response, "HTTP/1.%*d %d", &http_res);
|
||||
if (rc != 1) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("Failed to parse HTTP response code"));
|
||||
return -1;
|
||||
}
|
||||
if (http_res != 204 && http_res != 200) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("Unexpected response from CH: %1$d"), http_res);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* virCHProcessStart:
|
||||
* @driver: pointer to driver structure
|
||||
* @vm: pointer to virtual machine structure
|
||||
* @reason: reason for switching vm to running state
|
||||
*
|
||||
* Starts Cloud-Hypervisor listen on a local socket
|
||||
* Starts Cloud-Hypervisor listening on a local socket
|
||||
*
|
||||
* Returns 0 on success or -1 in case of error
|
||||
*/
|
||||
@ -483,8 +667,7 @@ virCHProcessStart(virCHDriver *driver,
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (virCHMonitorCreateVM(driver, priv->monitor,
|
||||
&nnicindexes, &nicindexes) < 0) {
|
||||
if (virCHMonitorCreateVM(driver, priv->monitor) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("failed to create guest VM"));
|
||||
goto cleanup;
|
||||
@ -495,6 +678,13 @@ virCHProcessStart(virCHDriver *driver,
|
||||
vm->def->id = vm->pid;
|
||||
priv->machineName = virCHDomainGetMachineName(vm);
|
||||
|
||||
if (chProcessAddNetworkDevices(driver, priv->monitor, vm->def,
|
||||
&nicindexes, &nnicindexes) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("Failed while adding guest interfaces"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (virDomainCgroupSetupCgroup("ch", vm,
|
||||
nnicindexes, nicindexes,
|
||||
&priv->cgroup,
|
||||
@ -507,6 +697,10 @@ virCHProcessStart(virCHDriver *driver,
|
||||
if (virCHProcessInitCpuAffinity(vm) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* Bring up netdevs before starting CPUs */
|
||||
if (virDomainInterfaceStartDevices(vm->def) < 0)
|
||||
return -1;
|
||||
|
||||
if (virCHMonitorBootVM(priv->monitor) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("failed to boot guest VM"));
|
||||
@ -552,6 +746,9 @@ virCHProcessStop(virCHDriver *driver G_GNUC_UNUSED,
|
||||
int ret;
|
||||
int retries = 0;
|
||||
virCHDomainObjPrivate *priv = vm->privateData;
|
||||
virCHDriverConfig *cfg = virCHDriverGetConfig(driver);
|
||||
virDomainDef *def = vm->def;
|
||||
size_t i;
|
||||
|
||||
VIR_DEBUG("Stopping VM name=%s pid=%d reason=%d",
|
||||
vm->def->name, (int)vm->pid, (int)reason);
|
||||
@ -560,6 +757,14 @@ virCHProcessStop(virCHDriver *driver G_GNUC_UNUSED,
|
||||
g_clear_pointer(&priv->monitor, virCHMonitorClose);
|
||||
}
|
||||
|
||||
/* de-activate netdevs after stopping vm */
|
||||
ignore_value(virDomainInterfaceStopDevices(vm->def));
|
||||
|
||||
for (i = 0; i < def->nnets; i++) {
|
||||
virDomainNetDef *net = def->nets[i];
|
||||
virDomainInterfaceDeleteDevice(def, net, false, cfg->stateDir);
|
||||
}
|
||||
|
||||
retry:
|
||||
if ((ret = virDomainCgroupRemoveCgroup(vm,
|
||||
priv->cgroup,
|
||||
|
@ -7,6 +7,8 @@ ch_driver_sources = [
|
||||
'ch_domain.h',
|
||||
'ch_driver.c',
|
||||
'ch_driver.h',
|
||||
'ch_interface.c',
|
||||
'ch_interface.h',
|
||||
'ch_monitor.c',
|
||||
'ch_monitor.h',
|
||||
'ch_process.c',
|
||||
|
Loading…
Reference in New Issue
Block a user