qemu_migration_params.c 19.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
/*
 * qemu_migration_params.c: QEMU migration parameters handling
 *
 * Copyright (C) 2006-2018 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 */

#include <config.h>

#include "virlog.h"
#include "virerror.h"
#include "viralloc.h"
#include "virstring.h"

#include "qemu_alias.h"
#include "qemu_hotplug.h"
#include "qemu_migration.h"
#include "qemu_migration_params.h"
33
#include "qemu_monitor.h"
34 35 36 37 38 39 40

#define VIR_FROM_THIS VIR_FROM_QEMU

VIR_LOG_INIT("qemu.qemu_migration_params");

#define QEMU_MIGRATION_TLS_ALIAS_BASE "libvirt_migrate"

41
struct _qemuMigrationParams {
42
    virBitmapPtr caps;
43 44 45
    qemuMonitorMigrationParams params;
};

46 47 48 49 50 51
typedef struct _qemuMigrationParamsAlwaysOnItem qemuMigrationParamsAlwaysOnItem;
struct _qemuMigrationParamsAlwaysOnItem {
    qemuMonitorMigrationCaps cap;
    int party; /* bit-wise OR of qemuMigrationParty */
};

52 53 54 55 56 57 58
typedef struct _qemuMigrationParamsFlagMapItem qemuMigrationParamsFlagMapItem;
struct _qemuMigrationParamsFlagMapItem {
    virDomainMigrateFlags flag;
    qemuMonitorMigrationCaps cap;
    int party; /* bit-wise OR of qemuMigrationParty */
};

59 60 61 62 63 64 65
/* Migration capabilities which should always be enabled as long as they
 * are supported by QEMU. */
static const qemuMigrationParamsAlwaysOnItem qemuMigrationParamsAlwaysOn[] = {
    {QEMU_MONITOR_MIGRATION_CAPS_PAUSE_BEFORE_SWITCHOVER,
     QEMU_MIGRATION_SOURCE},
};

66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
/* Translation from virDomainMigrateFlags to qemuMonitorMigrationCaps. */
static const qemuMigrationParamsFlagMapItem qemuMigrationParamsFlagMap[] = {
    {VIR_MIGRATE_RDMA_PIN_ALL,
     QEMU_MONITOR_MIGRATION_CAPS_RDMA_PIN_ALL,
     QEMU_MIGRATION_SOURCE | QEMU_MIGRATION_DESTINATION},

    {VIR_MIGRATE_AUTO_CONVERGE,
     QEMU_MONITOR_MIGRATION_CAPS_AUTO_CONVERGE,
     QEMU_MIGRATION_SOURCE},

    {VIR_MIGRATE_POSTCOPY,
     QEMU_MONITOR_MIGRATION_CAPS_POSTCOPY,
     QEMU_MIGRATION_SOURCE | QEMU_MIGRATION_DESTINATION},
};

81

82
static qemuMigrationParamsPtr
83 84
qemuMigrationParamsNew(void)
{
85
    qemuMigrationParamsPtr params;
86 87 88 89

    if (VIR_ALLOC(params) < 0)
        return NULL;

90 91 92 93
    params->caps = virBitmapNew(QEMU_MONITOR_MIGRATION_CAPS_LAST);
    if (!params->caps)
        goto error;

94
    return params;
95 96 97 98

 error:
    qemuMigrationParamsFree(params);
    return NULL;
99 100 101
}


102
void
103
qemuMigrationParamsFree(qemuMigrationParamsPtr migParams)
104 105 106 107
{
    if (!migParams)
        return;

108
    virBitmapFree(migParams->caps);
109 110
    VIR_FREE(migParams->params.tlsCreds);
    VIR_FREE(migParams->params.tlsHostname);
111
    VIR_FREE(migParams);
112 113 114
}


115
qemuMigrationParamsPtr
116 117
qemuMigrationParamsFromFlags(virTypedParameterPtr params,
                             int nparams,
118 119
                             unsigned long flags,
                             qemuMigrationParty party)
120
{
121
    qemuMigrationParamsPtr migParams;
122
    size_t i;
123

124
    if (!(migParams = qemuMigrationParamsNew()))
125 126
        return NULL;

127 128 129 130 131 132 133 134 135 136
    for (i = 0; i < ARRAY_CARDINALITY(qemuMigrationParamsFlagMap); i++) {
        qemuMonitorMigrationCaps cap = qemuMigrationParamsFlagMap[i].cap;

        if (qemuMigrationParamsFlagMap[i].party & party &&
            flags & qemuMigrationParamsFlagMap[i].flag) {
            VIR_DEBUG("Enabling migration capability '%s'",
                      qemuMonitorMigrationCapsTypeToString(cap));
            ignore_value(virBitmapSetBit(migParams->caps, cap));
        }
    }
137 138 139 140 141 142

#define GET(PARAM, VAR) \
    do { \
        int rc; \
        if ((rc = virTypedParamsGetInt(params, nparams, \
                                       VIR_MIGRATE_PARAM_ ## PARAM, \
143
                                       &migParams->params.VAR)) < 0) \
144 145 146
            goto error; \
 \
        if (rc == 1) \
147
            migParams->params.VAR ## _set = true; \
148 149
    } while (0)

150 151 152 153 154
    if (params) {
        if (party == QEMU_MIGRATION_SOURCE) {
            GET(AUTO_CONVERGE_INITIAL, cpuThrottleInitial);
            GET(AUTO_CONVERGE_INCREMENT, cpuThrottleIncrement);
        }
155
    }
156 157 158

#undef GET

159 160
    if ((migParams->params.cpuThrottleInitial_set ||
         migParams->params.cpuThrottleIncrement_set) &&
161 162 163 164 165 166 167 168 169
        !(flags & VIR_MIGRATE_AUTO_CONVERGE)) {
        virReportError(VIR_ERR_INVALID_ARG, "%s",
                       _("Turn auto convergence on to tune it"));
        goto error;
    }

    return migParams;

 error:
170
    qemuMigrationParamsFree(migParams);
171 172 173 174
    return NULL;
}


175 176 177 178 179 180 181 182 183 184 185
/**
 * qemuMigrationParamsApply
 * @driver: qemu driver
 * @vm: domain object
 * @asyncJob: migration job
 * @migParams: migration parameters to send to QEMU
 *
 * Send all parameters stored in @migParams to QEMU.
 *
 * Returns 0 on success, -1 on failure.
 */
186
int
187 188 189 190
qemuMigrationParamsApply(virQEMUDriverPtr driver,
                         virDomainObjPtr vm,
                         int asyncJob,
                         qemuMigrationParamsPtr migParams)
191 192
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
193
    bool xbzrleCacheSize_old = false;
194 195 196 197 198
    int ret = -1;

    if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
        return -1;

199 200 201 202
    if (qemuMonitorSetMigrationCapabilities(priv->mon, priv->migrationCaps,
                                            migParams->caps) < 0)
        goto cleanup;

203 204 205 206 207 208 209 210 211 212 213 214 215 216
    /* If QEMU is too old to support xbzrle-cache-size migration parameter,
     * we need to set it via migrate-set-cache-size and tell
     * qemuMonitorSetMigrationParams to ignore this parameter.
     */
    if (migParams->params.xbzrleCacheSize_set &&
        (!priv->job.migParams ||
         !priv->job.migParams->params.xbzrleCacheSize_set)) {
        if (qemuMonitorSetMigrationCacheSize(priv->mon,
                                             migParams->params.xbzrleCacheSize) < 0)
            goto cleanup;
        xbzrleCacheSize_old = true;
        migParams->params.xbzrleCacheSize_set = false;
    }

217
    if (qemuMonitorSetMigrationParams(priv->mon, &migParams->params) < 0)
218 219 220 221 222 223 224 225
        goto cleanup;

    ret = 0;

 cleanup:
    if (qemuDomainObjExitMonitor(driver, vm) < 0)
        ret = -1;

226 227 228
    if (xbzrleCacheSize_old)
        migParams->params.xbzrleCacheSize_set = true;

229 230 231 232
    return ret;
}


233
static int
234
qemuMigrationParamsSetCapability(virDomainObjPtr vm ATTRIBUTE_UNUSED,
235 236 237 238 239 240 241 242 243 244 245 246 247
                                 qemuMonitorMigrationCaps capability,
                                 bool state,
                                 qemuMigrationParamsPtr migParams)
{
    if (state)
        ignore_value(virBitmapSetBit(migParams->caps, capability));
    else
        ignore_value(virBitmapClearBit(migParams->caps, capability));

    return 0;
}


248
/* qemuMigrationParamsEnableTLS
249 250
 * @driver: pointer to qemu driver
 * @vm: domain object
251 252 253 254
 * @tlsListen: server or client
 * @asyncJob: Migration job to join
 * @tlsAlias: alias to be generated for TLS object
 * @secAlias: alias to be generated for a secinfo object
255
 * @hostname: hostname of the migration destination
256
 * @migParams: migration parameters to set
257
 *
258 259 260
 * Create the TLS objects for the migration and set the migParams value.
 * If QEMU itself does not connect to the destination @hostname must be
 * provided for certificate verification.
261
 *
262
 * Returns 0 on success, -1 on failure
263 264
 */
int
265 266 267 268 269 270
qemuMigrationParamsEnableTLS(virQEMUDriverPtr driver,
                             virDomainObjPtr vm,
                             bool tlsListen,
                             int asyncJob,
                             char **tlsAlias,
                             char **secAlias,
271
                             const char *hostname,
272
                             qemuMigrationParamsPtr migParams)
273 274
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
275 276
    virJSONValuePtr tlsProps = NULL;
    virJSONValuePtr secProps = NULL;
277 278
    virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
    int ret = -1;
279 280 281 282

    if (!cfg->migrateTLSx509certdir) {
        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
                       _("host migration TLS directory not configured"));
283
        goto error;
284 285
    }

286
    if ((!priv->job.migParams->params.tlsCreds)) {
287 288 289
        virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
                       _("TLS migration is not supported with this "
                         "QEMU binary"));
290
        goto error;
291 292 293 294 295 296 297
    }

    /* If there's a secret, then grab/store it now using the connection */
    if (cfg->migrateTLSx509secretUUID &&
        !(priv->migSecinfo =
          qemuDomainSecretInfoTLSNew(priv, QEMU_MIGRATION_TLS_ALIAS_BASE,
                                     cfg->migrateTLSx509secretUUID)))
298
        goto error;
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316

    if (qemuDomainGetTLSObjects(priv->qemuCaps, priv->migSecinfo,
                                cfg->migrateTLSx509certdir, tlsListen,
                                cfg->migrateTLSx509verify,
                                QEMU_MIGRATION_TLS_ALIAS_BASE,
                                &tlsProps, tlsAlias, &secProps, secAlias) < 0)
        goto error;

    /* Ensure the domain doesn't already have the TLS objects defined...
     * This should prevent any issues just in case some cleanup wasn't
     * properly completed (both src and dst use the same alias) or
     * some other error path between now and perform . */
    qemuDomainDelTLSObjects(driver, vm, asyncJob, *secAlias, *tlsAlias);

    if (qemuDomainAddTLSObjects(driver, vm, asyncJob, *secAlias, &secProps,
                                *tlsAlias, &tlsProps) < 0)
        goto error;

317 318
    if (VIR_STRDUP(migParams->params.tlsCreds, *tlsAlias) < 0 ||
        VIR_STRDUP(migParams->params.tlsHostname, hostname ? hostname : "") < 0)
319 320
        goto error;

321 322 323 324 325
    ret = 0;

 cleanup:
    virObjectUnref(cfg);
    return ret;
326 327 328 329

 error:
    virJSONValueFree(tlsProps);
    virJSONValueFree(secProps);
330
    goto cleanup;
331 332 333
}


334
/* qemuMigrationParamsDisableTLS
335 336 337 338 339 340 341 342 343 344
 * @vm: domain object
 * @migParams: Pointer to a migration parameters block
 *
 * If we support setting the tls-creds, then set both tls-creds and
 * tls-hostname to the empty string ("") which indicates to not use
 * TLS on this migration.
 *
 * Returns 0 on success, -1 on failure
 */
int
345 346
qemuMigrationParamsDisableTLS(virDomainObjPtr vm,
                              qemuMigrationParamsPtr migParams)
347
{
348
    qemuDomainObjPrivatePtr priv = vm->privateData;
349

350
    if (!priv->job.migParams->params.tlsCreds)
351
        return 0;
352

353 354
    if (VIR_STRDUP(migParams->params.tlsCreds, "") < 0 ||
        VIR_STRDUP(migParams->params.tlsHostname, "") < 0)
355
        return -1;
356 357 358 359 360 361

    return 0;
}


int
362
qemuMigrationParamsSetCompression(virDomainObjPtr vm,
363
                                  qemuMigrationCompressionPtr compression,
364
                                  qemuMigrationParamsPtr migParams)
365
{
366 367 368 369 370
    if (qemuMigrationParamsSetCapability(vm,
                                         QEMU_MONITOR_MIGRATION_CAPS_XBZRLE,
                                         compression->methods &
                                         (1ULL << QEMU_MIGRATION_COMPRESS_XBZRLE),
                                         migParams) < 0)
371 372
        return -1;

373 374 375 376 377
    if (qemuMigrationParamsSetCapability(vm,
                                         QEMU_MONITOR_MIGRATION_CAPS_COMPRESS,
                                         compression->methods &
                                         (1ULL << QEMU_MIGRATION_COMPRESS_MT),
                                         migParams) < 0)
378 379
        return -1;

380 381
    migParams->params.compressLevel_set = compression->level_set;
    migParams->params.compressLevel = compression->level;
382

383 384
    migParams->params.compressThreads_set = compression->threads_set;
    migParams->params.compressThreads = compression->threads;
385

386 387
    migParams->params.decompressThreads_set = compression->dthreads_set;
    migParams->params.decompressThreads = compression->dthreads;
388

389 390
    migParams->params.xbzrleCacheSize_set = compression->xbzrle_cache_set;
    migParams->params.xbzrleCacheSize = compression->xbzrle_cache;
391

392
    return 0;
393 394 395 396 397 398 399 400 401 402 403
}


/* qemuMigrationParamsResetTLS
 * @driver: pointer to qemu driver
 * @vm: domain object
 * @asyncJob: migration job to join
 *
 * Deconstruct all the setup possibly done for TLS - delete the TLS and
 * security objects, free the secinfo, and reset the migration params to "".
 */
404
static void
405 406
qemuMigrationParamsResetTLS(virQEMUDriverPtr driver,
                            virDomainObjPtr vm,
407 408
                            int asyncJob,
                            qemuMigrationParamsPtr origParams)
409 410 411 412
{
    char *tlsAlias = NULL;
    char *secAlias = NULL;

413 414 415
    /* If QEMU does not support TLS migration we didn't set the aliases. */
    if (!origParams->params.tlsCreds)
        return;
416

417 418 419 420 421 422 423
    /* NB: If either or both fail to allocate memory we can still proceed
     *     since the next time we migrate another deletion attempt will be
     *     made after successfully generating the aliases. */
    tlsAlias = qemuAliasTLSObjFromSrcAlias(QEMU_MIGRATION_TLS_ALIAS_BASE);
    secAlias = qemuDomainGetSecretAESAlias(QEMU_MIGRATION_TLS_ALIAS_BASE, false);

    qemuDomainDelTLSObjects(driver, vm, asyncJob, secAlias, tlsAlias);
424
    qemuDomainSecretInfoFree(&QEMU_DOMAIN_PRIVATE(vm)->migSecinfo);
425 426 427 428 429 430

    VIR_FREE(tlsAlias);
    VIR_FREE(secAlias);
}


431 432 433 434 435
/**
 * qemuMigrationParamsCheck:
 *
 * Check supported migration parameters and keep their original values in
 * qemuDomainJobObj so that we can properly reset them at the end of migration.
436 437
 * Reports an error if any of the currently used capabilities in @migParams
 * are unsupported by QEMU.
438 439 440 441
 */
int
qemuMigrationParamsCheck(virQEMUDriverPtr driver,
                         virDomainObjPtr vm,
442 443
                         int asyncJob,
                         qemuMigrationParamsPtr migParams)
444 445 446
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
    qemuMigrationParamsPtr origParams = NULL;
447
    qemuMonitorMigrationCaps cap;
448 449
    qemuMigrationParty party;
    size_t i;
450 451
    int ret = -1;

452 453 454 455 456
    if (asyncJob == QEMU_ASYNC_JOB_MIGRATION_OUT)
        party = QEMU_MIGRATION_SOURCE;
    else
        party = QEMU_MIGRATION_DESTINATION;

457 458 459 460 461 462 463 464 465 466 467 468 469
    for (cap = 0; cap < QEMU_MONITOR_MIGRATION_CAPS_LAST; cap++) {
        bool state = false;

        ignore_value(virBitmapGetBit(migParams->caps, cap, &state));

        if (state && !qemuMigrationCapsGet(vm, cap)) {
            virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
                           _("Migration option '%s' is not supported by QEMU binary"),
                           qemuMonitorMigrationCapsTypeToString(cap));
            return -1;
        }
    }

470 471 472 473 474 475 476 477 478 479 480
    for (i = 0; i < ARRAY_CARDINALITY(qemuMigrationParamsAlwaysOn); i++) {
        cap = qemuMigrationParamsAlwaysOn[i].cap;

        if (qemuMigrationParamsAlwaysOn[i].party & party &&
            qemuMigrationCapsGet(vm, cap)) {
            VIR_DEBUG("Enabling migration capability '%s'",
                      qemuMonitorMigrationCapsTypeToString(cap));
            ignore_value(virBitmapSetBit(migParams->caps, cap));
        }
    }

481 482 483 484 485 486
    if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
        return -1;

    if (!(origParams = qemuMigrationParamsNew()))
        goto cleanup;

487 488 489 490 491
    /*
     * We want to disable all migration capabilities after migration, no need
     * to ask QEMU for their current settings.
     */

492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
    if (qemuMonitorGetMigrationParams(priv->mon, &origParams->params) < 0)
        goto cleanup;

    ret = 0;

 cleanup:
    if (qemuDomainObjExitMonitor(driver, vm) < 0)
        ret = -1;

    if (ret == 0)
        VIR_STEAL_PTR(priv->job.migParams, origParams);
    qemuMigrationParamsFree(origParams);

    return ret;
}


509 510 511 512 513 514 515 516 517
/*
 * qemuMigrationParamsReset:
 *
 * Reset all migration parameters so that the next job which internally uses
 * migration (save, managedsave, snapshots, dump) will not try to use them.
 */
void
qemuMigrationParamsReset(virQEMUDriverPtr driver,
                         virDomainObjPtr vm,
518 519
                         int asyncJob,
                         qemuMigrationParamsPtr origParams)
520 521 522
{
    virErrorPtr err = virSaveLastError();

523 524
    VIR_DEBUG("Resetting migration parameters %p", origParams);

525
    if (!virDomainObjIsActive(vm) || !origParams)
526 527
        goto cleanup;

528 529
    if (qemuMigrationParamsApply(driver, vm, asyncJob, origParams) < 0)
        goto cleanup;
530

531
    qemuMigrationParamsResetTLS(driver, vm, asyncJob, origParams);
532 533 534 535 536 537 538

 cleanup:
    if (err) {
        virSetError(err);
        virFreeError(err);
    }
}
539 540 541 542 543 544 545 546


int
qemuMigrationCapsCheck(virQEMUDriverPtr driver,
                       virDomainObjPtr vm,
                       int asyncJob)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
547
    virBitmapPtr migEvent = NULL;
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
    char **caps = NULL;
    char **capStr;
    int ret = -1;
    int rc;

    if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
        return -1;

    rc = qemuMonitorGetMigrationCapabilities(priv->mon, &caps);

    if (qemuDomainObjExitMonitor(driver, vm) < 0 || rc < 0)
        goto cleanup;

    if (!caps) {
        ret = 0;
        goto cleanup;
    }

    priv->migrationCaps = virBitmapNew(QEMU_MONITOR_MIGRATION_CAPS_LAST);
    if (!priv->migrationCaps)
        goto cleanup;

    for (capStr = caps; *capStr; capStr++) {
        int cap = qemuMonitorMigrationCapsTypeFromString(*capStr);

        if (cap < 0) {
            VIR_DEBUG("Unknown migration capability: '%s'", *capStr);
        } else {
            ignore_value(virBitmapSetBit(priv->migrationCaps, cap));
            VIR_DEBUG("Found migration capability: '%s'", *capStr);
        }
    }

    if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_MIGRATION_EVENT)) {
582 583 584 585 586 587
        migEvent = virBitmapNew(QEMU_MONITOR_MIGRATION_CAPS_LAST);
        if (!migEvent)
            goto cleanup;

        ignore_value(virBitmapSetBit(migEvent, QEMU_MONITOR_MIGRATION_CAPS_EVENTS));

588 589 590
        if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
            goto cleanup;

591
        rc = qemuMonitorSetMigrationCapabilities(priv->mon, migEvent, migEvent);
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615

        if (qemuDomainObjExitMonitor(driver, vm) < 0)
            goto cleanup;

        if (rc < 0) {
            virResetLastError();
            VIR_DEBUG("Cannot enable migration events; clearing capability");
            virQEMUCapsClear(priv->qemuCaps, QEMU_CAPS_MIGRATION_EVENT);
        }
    }

    /* Migration events capability must always be enabled, clearing it from
     * migration capabilities bitmap makes sure it won't be touched anywhere
     * else.
     */
    ignore_value(virBitmapClearBit(priv->migrationCaps,
                                   QEMU_MONITOR_MIGRATION_CAPS_EVENTS));

    ret = 0;

 cleanup:
    virStringListFree(caps);
    return ret;
}
J
Jiri Denemark 已提交
616 617 618 619 620 621 622 623 624 625 626 627 628 629


bool
qemuMigrationCapsGet(virDomainObjPtr vm,
                     qemuMonitorMigrationCaps cap)
{
    qemuDomainObjPrivatePtr priv = vm->privateData;
    bool enabled = false;

    if (priv->migrationCaps)
        ignore_value(virBitmapGetBit(priv->migrationCaps, cap, &enabled));

    return enabled;
}