--- src/ch/ch_conf.h | 4 + src/ch/ch_domain.h | 2 + src/ch/ch_driver.c | 362 +++++++++++++++++++++++++++++- src/ch/ch_monitor.c | 156 +++++++++++++ src/ch/ch_monitor.h | 8 + src/ch/ch_process.c | 136 ++++++++++- src/ch/ch_process.h | 6 + src/hypervisor/domain_interface.c | 1 + src/libvirt-domain.c | 15 +- 9 files changed, 680 insertions(+), 10 deletions(-)
diff --git a/src/ch/ch_conf.h b/src/ch/ch_conf.h index b08573476e..40d639eb2a 100644 --- a/src/ch/ch_conf.h +++ b/src/ch/ch_conf.h @@ -26,6 +26,7 @@ #include "ch_capabilities.h" #include "virebtables.h" #include "object_event.h" +#include "virportallocator.h" #define CH_DRIVER_NAME "CH" #define CH_CMD "cloud-hypervisor" @@ -90,6 +91,9 @@ struct _virCHDriver /* Immutable pointer, self-locking APIs */ virObjectEventState *domainEventState; + + /* Immutable pointer, immutable object */ + virPortAllocatorRange *migrationPorts; }; #define CH_SAVE_MAGIC "libvirt-xml\n \0 \r" diff --git a/src/ch/ch_domain.h b/src/ch/ch_domain.h index 69a657f6af..f9ad18b518 100644 --- a/src/ch/ch_domain.h +++ b/src/ch/ch_domain.h @@ -25,6 +25,7 @@ #include "virchrdev.h" #include "vircgroup.h" #include "virdomainjob.h" +#include "virthread.h" typedef struct _virCHDomainObjPrivate virCHDomainObjPrivate; @@ -32,6 +33,7 @@ struct _virCHDomainObjPrivate { virChrdevs *chrdevs; virCHDriver *driver; virCHMonitor *monitor; + virThread *migrationDstReceiveThr; char *machineName; virBitmap *autoCpuset; virBitmap *autoNodeset; diff --git a/src/ch/ch_driver.c b/src/ch/ch_driver.c index 3bdcf66ebd..c53607251b 100644 --- a/src/ch/ch_driver.c +++ b/src/ch/ch_driver.c @@ -29,20 +29,25 @@ #include "ch_process.h" #include "domain_cgroup.h" #include "domain_event.h" +#include "domain_interface.h" #include "datatypes.h" #include "driver.h" +#include "viralloc.h" #include "viraccessapicheck.h" #include "virchrdev.h" #include "virerror.h" #include "virlog.h" #include "virobject.h" #include "virfile.h" +#include "virtime.h" #include "virtypedparam.h" #include "virutil.h" #include "viruuid.h" #include "virnuma.h" #include "virhostmem.h" +#include "util/virportallocator.h" + #define VIR_FROM_THIS VIR_FROM_CH VIR_LOG_INIT("ch.ch_driver"); @@ -1453,6 +1458,13 @@ chStateInitialize(bool privileged, if (!(ch_driver->domainEventState = virObjectEventStateNew())) goto cleanup; + /* Allocate bitmap for migration port reservation */ + if (!(ch_driver->migrationPorts = + virPortAllocatorRangeNew(_("migration"), + 49152, + 49216))) + goto cleanup; + if ((rv = chExtractVersion(ch_driver)) < 0) { if (rv == -2) ret = VIR_DRV_STATE_INIT_SKIPPED; @@ -1495,8 +1507,9 @@ chConnectSupportsFeature(virConnectPtr conn, _("Global feature %1$d should have already been handled"), feature); return -1; - case VIR_DRV_FEATURE_MIGRATION_V2: case VIR_DRV_FEATURE_MIGRATION_V3: + return 1; + case VIR_DRV_FEATURE_MIGRATION_V2: case VIR_DRV_FEATURE_MIGRATION_P2P: case VIR_DRV_FEATURE_MIGRATE_CHANGE_PROTECTION: case VIR_DRV_FEATURE_XML_MIGRATABLE: @@ -2338,6 +2351,348 @@ chDomainInterfaceAddresses(virDomain *dom, return ret; } +/******************************************************************* + * Migration Protocol Version 3 + *******************************************************************/ + +static char * +chDomainMigrateBegin3(virDomainPtr domain, + const char *xmlin, + char **cookieout, + int *cookieoutlen, + unsigned long flags, + const char *dname, + unsigned long resource G_GNUC_UNUSED) +{ + virDomainObj *vm; + char *xml = NULL; + virCHDriver *driver = domain->conn->privateData; + + VIR_WARN("chDomainMigrateBegin3 %p %s %p %p %lu %s", + domain, xmlin, cookieout, cookieoutlen, flags, dname); + if (!(vm = virCHDomainObjFromDomain(domain))) + return NULL; + + if (virDomainMigrateBegin3EnsureACL(domain->conn, vm->def) < 0) { + virDomainObjEndAPI(&vm); + return NULL; + } + + // Copied from libxl_migration.c:386 + if (virDomainObjBeginJob(vm, VIR_JOB_MODIFY) < 0) + goto cleanup; + + xml = virDomainDefFormat(vm->def, driver->xmlopt, VIR_DOMAIN_DEF_FORMAT_SECURE); + + if (xml) { + VIR_WARN("chDomainMigrateBegin3 success. xml: %s", xml); + goto cleanup; + } + + return NULL; + cleanup: + virDomainObjEndAPI(&vm); + return xml; +} + +static +virDomainDef * +chMigrationAnyPrepareDef(virCHDriver *driver, + const char *dom_xml, + const char *dname) +{ + virDomainDef *def; + char *name = NULL; + + if (!dom_xml) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("no domain XML passed")); + return NULL; + } + + if (!(def = virDomainDefParseString(dom_xml, driver->xmlopt, + NULL, + VIR_DOMAIN_DEF_PARSE_INACTIVE))) + goto cleanup; + + if (dname) { + VIR_FREE(name); + def->name = g_strdup(dname); + } + + cleanup: + return def; +} + +typedef struct _chMigrationDstArgs { + unsigned int port; + virCHDomainObjPrivate *priv; +} chMigrationDstArgs; + +static void +chDoMigrateDstReceive(void *opaque) +{ + chMigrationDstArgs *args = opaque; + virCHDomainObjPrivate *priv = args->priv; + g_autofree char* rcv_uri = NULL; + + VIR_WARN("In thread. %u %p", args->port, args->priv); + if (!priv->monitor) { + VIR_ERROR(_("VMs monitor not initialized")); + } + + rcv_uri = g_strdup_printf("tcp:0.0.0.0:%d", args->port); + + if (virCHMonitorMigrationReceive(priv->monitor, rcv_uri) < 0) { + VIR_WARN("Migration receive failed."); + } + + VIR_WARN("Migration thread finished its duty"); +} + +/** + * Runs on the destination and prepares the empty cloud hypervisor process to + * receive the migration. + * Allocates some tcp port number to use as a migration channel. + */ +static int +chDomainMigratePrepare3(virConnectPtr dconn, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + const char *uri_in, + char **uri_out, + unsigned long flags, + const char *dname, + unsigned long resource G_GNUC_UNUSED, + const char *dom_xml) +{ + virCHDriver *driver = dconn->privateData; + virDomainObj *vm = NULL; + virCHDomainObjPrivate *priv = NULL; + chMigrationDstArgs *args = g_new0(chMigrationDstArgs, 1); + unsigned short port = 0; + g_autofree char *hostname = NULL; + const char *threadname = "mig-ch"; + g_autoptr(virDomainDef) def = NULL; + int rc = 0; + const char *incFormat = "%s:%s:%d"; // seems to differ for AF_INET6 + + VIR_WARN("chDomainMigratePrepare3 %p %s %u %p %p %s %p %lu %s %s", + dconn, cookiein, cookieinlen, cookieout, cookieoutlen, uri_in, uri_out, flags, dname, dom_xml); + + if (virDomainMigratePrepare3EnsureACL(dconn, def) < 0) + return -1; + + if (!(def = chMigrationAnyPrepareDef(driver, dom_xml, dname))) + return -1; + + VIR_WARN("Got DomainDef prepared successfully"); + + if (virPortAllocatorAcquire(driver->migrationPorts, &port) < 0) { + rc = -1; + goto cleanup; + } + VIR_WARN("Got port %i", port); + + if ((hostname = virGetHostname()) == NULL) { + rc = -1; + goto cleanup; + } + + *uri_out = g_strdup_printf(incFormat, "tcp", hostname, port); + VIR_WARN("uri out %s", *uri_out); + + if (!(vm = virDomainObjListAdd(driver->domains, &def, + driver->xmlopt, + VIR_DOMAIN_OBJ_LIST_ADD_LIVE | + VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE, + NULL))) + { + rc = -1; + VIR_WARN("Could not add Domain Obj to List"); + goto cleanup; + } + + if (virCHProcessInit(driver, vm) < 0) { + rc = -1; + VIR_WARN("Could not init process"); + goto cleanup; + } + + VIR_WARN("Try creating migration thread"); + priv = vm->privateData; + args->port = port; + args->priv = priv; + + // VM receiving is blocking which we cannot do here, because it would block + // the Libvirt migration protocol. + // Prepare a thread to receive the migration data + // VIR_FREE(priv->migrationDstReceiveThr); + priv->migrationDstReceiveThr = g_new0(virThread, 1); + if (virThreadCreateFull(priv->migrationDstReceiveThr, true, + chDoMigrateDstReceive, + threadname, + false, + args) < 0) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Failed to create thread for receiving migration data")); + goto cleanup; + } + + VIR_WARN("Finished creating migration thread"); + + + VIR_WARN("Fin migrationPrepare"); + + + cleanup: + virDomainObjEndAPI(&vm); + return rc; +} + +static int +chDomainMigratePerform3(virDomainPtr dom, + const char *xmlin, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + const char *dconnuri, + const char *uri, + unsigned long flags, + const char *dname, + unsigned long resource) +{ + size_t i; + virDomainObj *vm; + g_autoptr(virCHDriverConfig) cfg = virCHDriverGetConfig(dom->conn->privateData); + virCHDomainObjPrivate *priv = NULL; + g_autofree char *id = g_strdup_printf("bla"); + VIR_WARN("chDomainMigratePerform3 %p %s %s %u %p %p %s %s %lu %s %lu", + dom, xmlin, cookiein, cookieinlen, cookieout, cookieoutlen, dconnuri, uri, flags, dname, resource); + + if (!(vm = virCHDomainObjFromDomain(dom))) + return -1; + + priv = vm->privateData; + + if (!priv->monitor) { + VIR_ERROR(_("VMs monitor not initialized")); + goto cleanup; + } + + if (virDomainMigratePerform3EnsureACL(dom->conn, vm->def) < 0) { + goto cleanup; + } + + // Net device id hardcoded currently + // We need to remove network devices from the VM before live migration. + // Libvirt pre-allocates network devices and passes only the FD to CHV. CHV + // is not able to migrate those devices. + // See following CHV issue: https://github.com/cloud-hypervisor/cloud-hypervisor/issues/7054 + /* de-activate netdevs after stopping vm */ + ignore_value(virDomainInterfaceStopDevices(vm->def)); + for (i = 0; i < vm->def->nnets; i++) { + virDomainInterfaceDeleteDevice(vm->def, vm->def->nets[i], false, cfg->stateDir); + id = g_strdup_printf("net_%lu", i); + if (virCHMonitorRemoveDevice(priv->monitor, id) < 0) { + VIR_WARN("Could not remove net device. Continue to migrate regardless."); + } + } + + if (virCHMonitorMigrationSend(priv->monitor, uri) < 0) { + VIR_WARN("Migration send failed."); + goto cleanup; + } + + cleanup: + virDomainObjEndAPI(&vm); + return 0; +} + +static virDomainPtr +chDomainMigrateFinish3(virConnectPtr dconn, + const char *dname, + const char *cookiein, + int cookieinlen, + char **cookieout, + int *cookieoutlen, + const char *dconnuri G_GNUC_UNUSED, + const char *uri G_GNUC_UNUSED, + unsigned long flags, + int cancelled) +{ + virCHDriver *driver = dconn->privateData; + virDomainObj *vm = NULL; + virDomainPtr dom = NULL; + + VIR_WARN("chDomainMigrateFinish3 %p %s %s %d %p %p %lu %d", + dconn, dname, cookiein, cookieinlen, cookieout, cookieoutlen, flags, cancelled); + + vm = virDomainObjListFindByName(driver->domains, dname); + if (!vm) { + virReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching name '%1$s'"), dname); + return NULL; + } + + if (virDomainMigrateFinish3EnsureACL(dconn, vm->def) < 0) { + virDomainObjEndAPI(&vm); + return NULL; + } + if (!(dom = virGetDomain(dconn, vm->def->name, vm->def->uuid, vm->def->id))) { + virDomainObjEndAPI(&vm); + VIR_WARN("virGetDomain failed."); + return NULL; + + } + if (virCHProcessUpdateInfo(vm) < 0) { + VIR_WARN("Could not update console info. Consider that non-fatal."); + } + + if (virCHProcessInitNetwork(driver, vm) < 0) { + VIR_WARN("Could not updatenetwork info. Consider that non-fatal."); + } + + virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_MIGRATED); + + virDomainObjEndAPI(&vm); + return dom; +} + +static int +chDomainMigrateConfirm3(virDomainPtr domain, + const char *cookiein, + int cookieinlen, + unsigned long flags, + int cancelled) +{ + virCHDriver *driver = domain->conn->privateData; + virObjectEvent *event = NULL; + virDomainObj *vm; + + VIR_WARN("chDomainMigrateConfirm3 %p %s %d %lu %d", + domain, cookiein, cookieinlen, flags, cancelled); + + if (!(vm = virCHDomainObjFromDomain(domain))) + return -1; + + if (virDomainMigrateConfirm3EnsureACL(domain->conn, vm->def) < 0) { + virDomainObjEndAPI(&vm); + return -1; + } + + // Code from chDestroyFlags + virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, VIR_DOMAIN_SHUTOFF_MIGRATED); + virCHProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_DESTROYED); + virCHDomainRemoveInactive(driver, vm); + virDomainObjEndAPI(&vm); + + virObjectEventStateQueue(driver->domainEventState, event); + return 0; +} /* Function Tables */ static virHypervisorDriver chHypervisorDriver = { @@ -2400,6 +2755,11 @@ static virHypervisorDriver chHypervisorDriver = { .connectDomainEventRegisterAny = chConnectDomainEventRegisterAny, /* 10.10.0 */ .connectDomainEventDeregisterAny = chConnectDomainEventDeregisterAny, /* 10.10.0 */ .domainInterfaceAddresses = chDomainInterfaceAddresses, /* 11.0.0 */ + .domainMigrateBegin3 = chDomainMigrateBegin3, /* 11.4.0 */ + .domainMigratePrepare3 = chDomainMigratePrepare3, /* 11.4.0 */ + .domainMigratePerform3 = chDomainMigratePerform3, /* 11.4.0 */ + .domainMigrateFinish3 = chDomainMigrateFinish3, /* 11.4.0 */ + .domainMigrateConfirm3 = chDomainMigrateConfirm3, /* 11.4.0 */ }; static virConnectDriver chConnectDriver = { diff --git a/src/ch/ch_monitor.c b/src/ch/ch_monitor.c index 3d3b4cb87d..595fa30be0 100644 --- a/src/ch/ch_monitor.c +++ b/src/ch/ch_monitor.c @@ -548,6 +548,7 @@ virCHMonitorBuildVMJson(virCHDriver *driver, virDomainDef *vmdef, if (!(*jsonstr = virJSONValueToString(content, false))) return -1; + VIR_WARN("Build VM JSON: \n %s \n", *jsonstr); return 0; } @@ -684,6 +685,8 @@ virCHMonitorNew(virDomainObj *vm, virCHDriverConfig *cfg, int logfile) return NULL; } + VIR_WARN("Start emulator with cmd: %s", vm->def->emulator); + cmd = virCommandNew(vm->def->emulator); virCommandSetOutputFD(cmd, &logfile); virCommandSetErrorFD(cmd, &logfile); @@ -1163,6 +1166,159 @@ virCHMonitorSaveVM(virCHMonitor *mon, return ret; } +int virCHMonitorRemoveDevice(virCHMonitor *mon, + const char* device_id) +{ + g_autofree char *url = NULL; + int responseCode = 0; + int ret = -1; + g_autofree char *payload = NULL; + struct curl_slist *headers = NULL; + struct curl_data data = {0}; + + url = g_strdup_printf("%s/%s", URL_ROOT, URL_VM_REMOVE_DEVICE); + + headers = curl_slist_append(headers, "Accept: application/json"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + + if (virCHMonitorBuildKeyValueStringJson(&payload, "id", device_id) != 0) + return -1; + + VIR_WARN("Remove device id %s json %s", device_id, payload); + + VIR_WITH_OBJECT_LOCK_GUARD(mon) { + /* reset all options of a libcurl session handle at first */ + curl_easy_reset(mon->handle); + + curl_easy_setopt(mon->handle, CURLOPT_UNIX_SOCKET_PATH, mon->socketpath); + curl_easy_setopt(mon->handle, CURLOPT_URL, url); + curl_easy_setopt(mon->handle, CURLOPT_CUSTOMREQUEST, "PUT"); + curl_easy_setopt(mon->handle, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(mon->handle, CURLOPT_POSTFIELDS, payload); + curl_easy_setopt(mon->handle, CURLOPT_WRITEFUNCTION, curl_callback); + curl_easy_setopt(mon->handle, CURLOPT_WRITEDATA, (void *)&data); + + responseCode = virCHMonitorCurlPerform(mon->handle); + } + + if (responseCode == 200 || responseCode == 204) { + ret = 0; + } else { + data.content = g_realloc(data.content, data.size + 1); + data.content[data.size] = 0; + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + data.content); + g_free(data.content); + } + + /* reset the libcurl handle to avoid leaking a stack pointer to data */ + curl_easy_reset(mon->handle); + curl_slist_free_all(headers); + return ret; +} + +int virCHMonitorMigrationSend(virCHMonitor *mon, + const char *dst_uri) +{ + g_autofree char *url = NULL; + int responseCode = 0; + int ret = -1; + g_autofree char *payload = NULL; + struct curl_slist *headers = NULL; + struct curl_data data = {0}; + + url = g_strdup_printf("%s/%s", URL_ROOT, URL_VM_SEND_MIGRATION); + + headers = curl_slist_append(headers, "Accept: application/json"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + + if (virCHMonitorBuildKeyValueStringJson(&payload, "destination_url", dst_uri) != 0) + return -1; + + VIR_WARN("Send VM to url %s json %s", dst_uri, payload); + + VIR_WITH_OBJECT_LOCK_GUARD(mon) { + /* reset all options of a libcurl session handle at first */ + curl_easy_reset(mon->handle); + + curl_easy_setopt(mon->handle, CURLOPT_UNIX_SOCKET_PATH, mon->socketpath); + curl_easy_setopt(mon->handle, CURLOPT_URL, url); + curl_easy_setopt(mon->handle, CURLOPT_CUSTOMREQUEST, "PUT"); + curl_easy_setopt(mon->handle, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(mon->handle, CURLOPT_POSTFIELDS, payload); + curl_easy_setopt(mon->handle, CURLOPT_WRITEFUNCTION, curl_callback); + curl_easy_setopt(mon->handle, CURLOPT_WRITEDATA, (void *)&data); + + responseCode = virCHMonitorCurlPerform(mon->handle); + } + + if (responseCode == 200 || responseCode == 204) { + ret = 0; + } else { + data.content = g_realloc(data.content, data.size + 1); + data.content[data.size] = 0; + virReportError(VIR_ERR_INTERNAL_ERROR, _("Error sending VM: '%1$s'"), + data.content); + g_free(data.content); + } + + /* reset the libcurl handle to avoid leaking a stack pointer to data */ + curl_easy_reset(mon->handle); + curl_slist_free_all(headers); + return ret; +} + +int virCHMonitorMigrationReceive(virCHMonitor *mon, + const char *rcv_uri) +{ + g_autofree char *url = NULL; + int responseCode = 0; + int ret = -1; + g_autofree char *payload = NULL; + struct curl_slist *headers = NULL; + struct curl_data data = {0}; + + url = g_strdup_printf("%s/%s", URL_ROOT, URL_VM_RECEIVE_MIGRATION); + + headers = curl_slist_append(headers, "Accept: application/json"); + headers = curl_slist_append(headers, "Content-Type: application/json"); + + if (virCHMonitorBuildKeyValueStringJson(&payload, "receiver_url", rcv_uri) != 0) + return -1; + + VIR_WARN("Receive VM from url %s json: %s", rcv_uri, payload); + + VIR_WITH_OBJECT_LOCK_GUARD(mon) { + /* reset all options of a libcurl session handle at first */ + curl_easy_reset(mon->handle); + + curl_easy_setopt(mon->handle, CURLOPT_UNIX_SOCKET_PATH, mon->socketpath); + curl_easy_setopt(mon->handle, CURLOPT_URL, url); + curl_easy_setopt(mon->handle, CURLOPT_CUSTOMREQUEST, "PUT"); + curl_easy_setopt(mon->handle, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(mon->handle, CURLOPT_POSTFIELDS, payload); + curl_easy_setopt(mon->handle, CURLOPT_WRITEFUNCTION, curl_callback); + curl_easy_setopt(mon->handle, CURLOPT_WRITEDATA, (void *)&data); + + responseCode = virCHMonitorCurlPerform(mon->handle); + } + + if (responseCode == 200 || responseCode == 204) { + ret = 0; + } else { + data.content = g_realloc(data.content, data.size + 1); + data.content[data.size] = 0; + virReportError(VIR_ERR_INTERNAL_ERROR, _("Error receiving VM: '%1$s'"), + data.content); + g_free(data.content); + } + + /* reset the libcurl handle to avoid leaking a stack pointer to data */ + curl_easy_reset(mon->handle); + curl_slist_free_all(headers); + return ret; +} + int virCHMonitorBuildRestoreJson(virDomainDef *vmdef, const char *from, diff --git a/src/ch/ch_monitor.h b/src/ch/ch_monitor.h index ffac9e938e..256ab0d8a1 100644 --- a/src/ch/ch_monitor.h +++ b/src/ch/ch_monitor.h @@ -40,6 +40,9 @@ #define URL_VM_INFO "vm.info" #define URL_VM_SAVE "vm.snapshot" #define URL_VM_RESTORE "vm.restore" +#define URL_VM_RECEIVE_MIGRATION "vm.receive-migration" +#define URL_VM_SEND_MIGRATION "vm.send-migration" +#define URL_VM_REMOVE_DEVICE "vm.remove-device" #define VIRCH_THREAD_NAME_LEN 16 @@ -128,6 +131,11 @@ int virCHMonitorSuspendVM(virCHMonitor *mon); int virCHMonitorResumeVM(virCHMonitor *mon); int virCHMonitorSaveVM(virCHMonitor *mon, const char *to); +int virCHMonitorMigrationSend(virCHMonitor *mon, + const char *dst_uri); +int virCHMonitorMigrationReceive(virCHMonitor *mon, + const char *rcv_uri); +int virCHMonitorRemoveDevice(virCHMonitor *mon, const char* device_id); int virCHMonitorGetInfo(virCHMonitor *mon, virJSONValue **info); size_t virCHMonitorGetThreadInfo(virCHMonitor *mon, bool refresh, diff --git a/src/ch/ch_process.c b/src/ch/ch_process.c index 95c808cb41..4d2bbc2b3d 100644 --- a/src/ch/ch_process.c +++ b/src/ch/ch_process.c @@ -135,6 +135,7 @@ int virCHProcessUpdateInfo(virDomainObj *vm) { g_autoptr(virJSONValue) info = NULL; + virCHDomainObjPrivate *priv = vm->privateData; if (virCHMonitorGetInfo(priv->monitor, &info) < 0) return -1; @@ -643,7 +644,7 @@ chProcessAddNetworkDevices(virCHDriver *driver, int **nicindexes, size_t *nnicindexes) { - size_t i; + size_t i, j; VIR_AUTOCLOSE mon_sockfd = -1; g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; g_auto(virBuffer) http_headers = VIR_BUFFER_INITIALIZER; @@ -654,8 +655,10 @@ chProcessAddNetworkDevices(virCHDriver *driver, return -1; } - if ((mon_sockfd = chMonitorSocketConnect(mon)) < 0) + if ((mon_sockfd = chMonitorSocketConnect(mon)) < 0) { + VIR_WARN("chProcessAddNetworkDevices failed"); return -1; + } virBufferAddLit(&http_headers, "PUT /api/v1/vm.add-net HTTP/1.1\r\n"); virBufferAddLit(&http_headers, "Host: localhost\r\n"); @@ -681,24 +684,33 @@ chProcessAddNetworkDevices(virCHDriver *driver, if (virCHDomainValidateActualNetDef(vmdef->nets[i]) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("net definition failed validation")); + VIR_WARN("virCHDomainValidateActualNetDef failed."); return -1; } tapfds = g_new0(int, tapfd_len); memset(tapfds, -1, (tapfd_len) * sizeof(int)); + VIR_WARN("net type: %u", vmdef->nets[i]->type); /* Connect Guest interfaces */ if (virCHConnetNetworkInterfaces(driver, vmdef, vmdef->nets[i], tapfds, - nicindexes, nnicindexes) < 0) + nicindexes, nnicindexes) < 0) { + VIR_WARN("chProcessAddNetworkDevices failed."); return -1; + } if (virCHMonitorBuildNetJson(vmdef->nets[i], i, &payload) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to build net json")); + VIR_WARN("virCHMonitorBuildNetJson failed."); return -1; } - VIR_DEBUG("payload sent with net-add request to CH = %s", payload); + VIR_WARN("payload sent with net-add request to CH = %s", payload); + + for (j = 0; j < tapfd_len; j++) { + VIR_WARN("tapfd %lu : %d", j, tapfds[j]); + } virBufferAsprintf(&buf, "%s", virBufferCurrentContent(&http_headers)); virBufferAsprintf(&buf, "Content-Length: %zu\r\n\r\n", strlen(payload)); @@ -892,6 +904,122 @@ virCHProcessPrepareDomain(virDomainObj *vm) return 0; } +int virCHProcessInitNetwork(virCHDriver *driver, + virDomainObj *vm) +{ + int ret = -1; + virCHDomainObjPrivate *priv = vm->privateData; + g_autoptr(virCHDriverConfig) cfg = virCHDriverGetConfig(priv->driver); + g_autofree int *nicindexes = NULL; + size_t nnicindexes = 0; + + if (chProcessAddNetworkDevices(driver, priv->monitor, vm->def, + &nicindexes, &nnicindexes) < 0) { + VIR_WARN("Failed chProcessAddNetworkDevices"); + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed while adding guest interfaces")); + goto cleanup; + } + + /* Bring up netdevs before starting CPUs */ + if (virDomainInterfaceStartDevices(vm->def) < 0) { + VIR_WARN("Failed virDomainInterfaceStartDevices"); + return -1; + } + + return 0; + + cleanup: + + return ret; +} + +/** + * A variant of virCHProcessStart that does not start the vCPU threads and the + * VM. Sets up the CH process along most configuration. + * Is used to setup CH in order to receive a live migration afterwards. + */ +int +virCHProcessInit(virCHDriver *driver, + virDomainObj *vm) +{ + int ret = -1; + virCHDomainObjPrivate *priv = vm->privateData; + g_autoptr(virCHDriverConfig) cfg = virCHDriverGetConfig(priv->driver); + g_autofree int *nicindexes = NULL; + size_t nnicindexes = 0; + g_autoptr(domainLogContext) logCtxt = NULL; + int logfile = -1; + + if (virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("VM is already active")); + return -1; + } + + if (virCHProcessStartValidate(driver, vm) < 0) { + return -1; + } + + VIR_WARN("Creating domain log file for %s domain", vm->def->name); + if (!(logCtxt = domainLogContextNew(cfg->stdioLogD, cfg->logDir, + CH_DRIVER_NAME, + vm, driver->privileged, + vm->def->name))) { + virLastErrorPrefixMessage("%s", _("can't connect to virtlogd")); + return -1; + } + logfile = domainLogContextGetWriteFD(logCtxt); + + if (virCHProcessPrepareDomain(vm) < 0) { + return -1; + } + + if (virCHProcessPrepareHost(driver, vm) < 0) + return -1; + + if (!priv->monitor) { + /* And we can get the first monitor connection now too */ + if (!(priv->monitor = virCHProcessConnectMonitor(driver, vm, logfile))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create connection to CH socket")); + goto cleanup; + } + + if (virCHMonitorCreateVM(driver, priv->monitor) < 0) { + VIR_WARN("Failed virCHMonitorCreateVM"); + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create guest VM")); + goto cleanup; + } + } + + vm->def->id = vm->pid; + priv->machineName = virCHDomainGetMachineName(vm); + + if (virDomainCgroupSetupCgroup("ch", vm, + nnicindexes, nicindexes, + &priv->cgroup, + cfg->cgroupControllers, + 0, /*maxThreadsPerProc*/ + priv->driver->privileged, + priv->machineName) < 0) + { + VIR_WARN("Failed virDomainCgroupSetupCgroup"); + goto cleanup; + } + + virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_MIGRATION); + + return 0; + + cleanup: + if (ret) + virCHProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED); + + return ret; +} + /** * virCHProcessStart: * @driver: pointer to driver structure diff --git a/src/ch/ch_process.h b/src/ch/ch_process.h index 7a6995b7cf..3a5a2d17d7 100644 --- a/src/ch/ch_process.h +++ b/src/ch/ch_process.h @@ -23,6 +23,9 @@ #include "ch_conf.h" #include "internal.h" +int virCHProcessInit(virCHDriver *driver, + virDomainObj *vm); + int virCHProcessStart(virCHDriver *driver, virDomainObj *vm, virDomainRunningReason reason); @@ -38,3 +41,6 @@ int virCHProcessStartRestore(virCHDriver *driver, const char *from); int virCHProcessUpdateInfo(virDomainObj *vm); + +int virCHProcessInitNetwork(virCHDriver *driver, + virDomainObj *vm); diff --git a/src/hypervisor/domain_interface.c b/src/hypervisor/domain_interface.c index 5bc698d272..a13fcfb7d2 100644 --- a/src/hypervisor/domain_interface.c +++ b/src/hypervisor/domain_interface.c @@ -82,6 +82,7 @@ virDomainInterfaceEthernetConnect(virDomainDef *def, bool template_ifname = false; const char *tunpath = "/dev/net/tun"; const char *auditdev = tunpath; + VIR_WARN("virDomainInterfaceEthernetConnect %s", net->ifname); if (net->backend.tap) { tunpath = net->backend.tap; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 93e8f5b853..867876b23f 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -3307,6 +3307,11 @@ virDomainMigrateVersion3Full(virDomainPtr domain, "params=%p, nparams=%d, useParams=%d, flags=0x%x", dconn, NULLSTR(xmlin), NULLSTR(dname), NULLSTR(uri), bandwidth, params, nparams, useParams, flags); + VIR_WARN( + "dconn=%p, xmlin=%s, dname=%s, uri=%s, bandwidth=%llu, " + "params=%p, nparams=%d, useParams=%d, flags=0x%x", + dconn, NULLSTR(xmlin), NULLSTR(dname), NULLSTR(uri), + bandwidth, params, nparams, useParams, flags); VIR_TYPED_PARAMS_DEBUG(params, nparams); virCheckNonEmptyOptStringArgReturn(dname, NULL); @@ -3337,7 +3342,7 @@ virDomainMigrateVersion3Full(virDomainPtr domain, if (ret) protection = VIR_MIGRATE_CHANGE_PROTECTION; - VIR_DEBUG("Begin3 %p", domain->conn); + VIR_WARN("Begin3 %p", domain->conn); if (useParams) { dom_xml = domain->conn->driver->domainMigrateBegin3Params (domain, params, nparams, &cookieout, &cookieoutlen, @@ -3366,7 +3371,7 @@ virDomainMigrateVersion3Full(virDomainPtr domain, destflags = flags & ~(VIR_MIGRATE_ABORT_ON_ERROR | VIR_MIGRATE_AUTO_CONVERGE); - VIR_DEBUG("Prepare3 %p flags=0x%x", dconn, destflags); + VIR_WARN("Prepare3 %p flags=0x%x", dconn, destflags); cookiein = g_steal_pointer(&cookieout); cookieinlen = cookieoutlen; cookieoutlen = 0; @@ -3427,7 +3432,7 @@ virDomainMigrateVersion3Full(virDomainPtr domain, * running, but in paused state until the destination can * confirm migration completion. */ - VIR_DEBUG("Perform3 %p uri=%s", domain->conn, uri); + VIR_WARN("Perform3 %p uri=%s", domain->conn, uri); VIR_FREE(cookiein); cookiein = g_steal_pointer(&cookieout); cookieinlen = cookieoutlen; @@ -3465,7 +3470,7 @@ virDomainMigrateVersion3Full(virDomainPtr domain, * send all migration data. Returns NULL for ddomain if * the dest was unable to complete migration. */ - VIR_DEBUG("Finish3 %p ret=%d", dconn, ret); + VIR_WARN("Finish3 %p ret=%d", dconn, ret); VIR_FREE(cookiein); cookiein = g_steal_pointer(&cookieout); cookieinlen = cookieoutlen; @@ -3540,7 +3545,7 @@ virDomainMigrateVersion3Full(virDomainPtr domain, * cancelled there. */ if (notify_source) { - VIR_DEBUG("Confirm3 %p ret=%d domain=%p", domain->conn, ret, domain); + VIR_WARN("Confirm3 %p ret=%d domain=%p", domain->conn, ret, domain); VIR_FREE(cookiein); cookiein = g_steal_pointer(&cookieout); cookieinlen = cookieoutlen; -- 2.49.0