vmware_conf.c 14.2 KB
Newer Older
1
/*---------------------------------------------------------------------------*/
2
/*
3
 * Copyright (C) 2011-2014 Red Hat, Inc.
4
 * Copyright (C) 2010-2014, diateam (www.diateam.net)
5 6 7 8 9 10 11 12 13 14 15 16
 *
 * 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
17
 * License along with this library.  If not, see
O
Osier Yang 已提交
18
 * <http://www.gnu.org/licenses/>.
19 20 21 22 23 24 25
 */
/*---------------------------------------------------------------------------*/

#include <config.h>

#include <string.h>

26
#include "vircommand.h"
27 28
#include "cpu/cpu.h"
#include "dirname.h"
29
#include "viralloc.h"
30
#include "nodeinfo.h"
E
Eric Blake 已提交
31
#include "virfile.h"
32
#include "viruuid.h"
33
#include "virerror.h"
34
#include "vmx.h"
35
#include "vmware_conf.h"
36
#include "virstring.h"
37

38 39
VIR_ENUM_IMPL(vmwareDriver, VMWARE_DRIVER_LAST,
              "player",
40 41
              "ws",
              "fusion");
42

43 44 45 46 47 48 49 50
/* Free all memory associated with a vmware_driver structure */
void
vmwareFreeDriver(struct vmware_driver *driver)
{
    if (!driver)
        return;

    virMutexDestroy(&driver->lock);
51
    virObjectUnref(driver->domains);
52
    virObjectUnref(driver->caps);
53
    virObjectUnref(driver->xmlopt);
54
    VIR_FREE(driver->vmrun);
55 56 57
    VIR_FREE(driver);
}

58

59 60 61 62 63 64
virCapsPtr
vmwareCapsInit(void)
{
    virCapsPtr caps = NULL;
    virCapsGuestPtr guest = NULL;
    virCPUDefPtr cpu = NULL;
65
    virCPUDataPtr data = NULL;
66

67
    if ((caps = virCapabilitiesNew(virArchFromHost(),
68
                                   false, false)) == NULL)
69 70
        goto error;

71
    if (nodeCapsInitNUMA(NULL, caps) < 0)
72 73 74 75
        goto error;

    /* i686 guests are always supported */
    if ((guest = virCapabilitiesAddGuest(caps,
76
                                         VIR_DOMAIN_OSTYPE_HVM,
77
                                         VIR_ARCH_I686,
78 79 80 81
                                         NULL, NULL, 0, NULL)) == NULL)
        goto error;

    if (virCapabilitiesAddGuestDomain(guest,
82
                                      VIR_DOMAIN_VIRT_VMWARE,
83 84 85
                                      NULL, NULL, 0, NULL) == NULL)
        goto error;

86
    if (VIR_ALLOC(cpu) < 0)
87 88
        goto error;

J
Jiri Denemark 已提交
89
    cpu->arch = caps->host.arch;
90 91 92 93 94 95 96 97 98 99 100 101
    cpu->type = VIR_CPU_TYPE_HOST;

    if (!(data = cpuNodeData(cpu->arch))
        || cpuDecode(cpu, data, NULL, 0, NULL) < 0) {
        goto error;
    }

    /* x86_64 guests are supported if
     *  - Host arch is x86_64
     * Or
     *  - Host CPU is x86_64 with virtualization extensions
     */
102
    if (caps->host.arch == VIR_ARCH_X86_64 ||
J
Jiri Denemark 已提交
103 104 105
        (cpuHasFeature(data, "lm") &&
         (cpuHasFeature(data, "vmx") ||
          cpuHasFeature(data, "svm")))) {
106 107

        if ((guest = virCapabilitiesAddGuest(caps,
108
                                             VIR_DOMAIN_OSTYPE_HVM,
109
                                             VIR_ARCH_X86_64,
110 111 112 113
                                             NULL, NULL, 0, NULL)) == NULL)
            goto error;

        if (virCapabilitiesAddGuestDomain(guest,
114
                                          VIR_DOMAIN_VIRT_VMWARE,
115 116 117 118
                                          NULL, NULL, 0, NULL) == NULL)
            goto error;
    }

119
 cleanup:
120
    virCPUDefFree(cpu);
J
Jiri Denemark 已提交
121
    cpuDataFree(data);
122 123 124

    return caps;

125
 error:
126
    virObjectUnref(caps);
127 128 129 130 131 132 133 134 135 136 137 138 139 140
    goto cleanup;
}

int
vmwareLoadDomains(struct vmware_driver *driver)
{
    virDomainDefPtr vmdef = NULL;
    virDomainObjPtr vm = NULL;
    char *vmxPath = NULL;
    char *vmx = NULL;
    vmwareDomainPtr pDomain;
    char *directoryName = NULL;
    char *fileName = NULL;
    int ret = -1;
141
    virVMXContext ctx;
142 143 144 145 146
    char *outbuf = NULL;
    char *str;
    char *saveptr = NULL;
    virCommandPtr cmd;

147
    ctx.parseFileName = vmwareCopyVMXFileName;
148

149
    cmd = virCommandNewArgList(driver->vmrun, "-T",
150
                               vmwareDriverTypeToString(driver->type),
151 152 153 154 155
                               "list", NULL);
    virCommandSetOutputBuffer(cmd, &outbuf);
    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;

156
    for (str = outbuf; (vmxPath = strtok_r(str, "\n", &saveptr)) != NULL;
157 158 159 160 161 162 163 164 165
        str = NULL) {

        if (vmxPath[0] != '/')
            continue;

        if (virFileReadAll(vmxPath, 10000, &vmx) < 0)
            goto cleanup;

        if ((vmdef =
166
             virVMXParseConfig(&ctx, driver->xmlopt, vmx)) == NULL) {
167 168 169
            goto cleanup;
        }

170
        if (!(vm = virDomainObjListAdd(driver->domains, vmdef,
171
                                       driver->xmlopt,
172
                                       0, NULL)))
173 174 175 176
            goto cleanup;

        pDomain = vm->privateData;

177
        if (VIR_STRDUP(pDomain->vmxPath, vmxPath) < 0)
178 179 180 181 182 183 184
            goto cleanup;

        vmwareDomainConfigDisplay(pDomain, vmdef);

        if ((vm->def->id = vmwareExtractPid(vmxPath)) < 0)
            goto cleanup;
        /* vmrun list only reports running vms */
J
Jiri Denemark 已提交
185 186
        virDomainObjSetState(vm, VIR_DOMAIN_RUNNING,
                             VIR_DOMAIN_RUNNING_UNKNOWN);
187 188
        vm->persistent = 1;

189
        virObjectUnlock(vm);
190 191 192 193 194 195 196

        vmdef = NULL;
        vm = NULL;
    }

    ret = 0;

197
 cleanup:
198 199 200 201 202 203
    virCommandFree(cmd);
    VIR_FREE(outbuf);
    virDomainDefFree(vmdef);
    VIR_FREE(directoryName);
    VIR_FREE(fileName);
    VIR_FREE(vmx);
204
    virObjectUnref(vm);
205 206 207 208 209 210 211 212 213
    return ret;
}

void
vmwareSetSentinal(const char **prog, const char *key)
{
    const char **tmp = prog;

    while (tmp && *tmp) {
E
Eric Blake 已提交
214
        if (*tmp == PROGRAM_SENTINEL) {
215 216 217 218 219 220 221
            *tmp = key;
            break;
        }
        tmp++;
    }
}

222 223 224 225 226 227 228 229 230 231 232 233 234
int
vmwareParseVersionStr(int type, const char *verbuf, unsigned long *version)
{
    const char *pattern;
    const char *tmp;

    switch (type) {
        case VMWARE_DRIVER_PLAYER:
            pattern = "VMware Player ";
            break;
        case VMWARE_DRIVER_WORKSTATION:
            pattern = "VMware Workstation ";
            break;
235 236 237
        case VMWARE_DRIVER_FUSION:
            pattern = "\nVMware Fusion Information:\nVMware Fusion ";
            break;
238 239 240 241 242 243
        default:
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Invalid driver type: %d"), type);
            return -1;
    }

244 245 246 247 248 249 250
    if ((tmp = strstr(verbuf, pattern)) == NULL) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("cannot find version pattern \"%s\""), pattern);
        return -1;
    }

    if ((tmp = STRSKIP(tmp, pattern)) == NULL) {
251 252 253 254 255 256 257 258 259 260 261 262 263 264
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to parse %sversion"), pattern);
        return -1;
    }

    if (virParseVersionString(tmp, version, false) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("version parsing error"));
        return -1;
    }

    return 0;
}

265 266 267 268
int
vmwareExtractVersion(struct vmware_driver *driver)
{
    int ret = -1;
269
    virCommandPtr cmd = NULL;
270
    char * outbuf = NULL;
271 272 273 274 275 276 277 278
    char *bin = NULL;
    char *vmwarePath = NULL;

    if ((vmwarePath = mdir_name(driver->vmrun)) == NULL)
        goto cleanup;

    switch (driver->type) {
        case VMWARE_DRIVER_PLAYER:
279
            if (virAsprintf(&bin, "%s/%s", vmwarePath, "vmplayer") < 0)
280 281 282 283
                goto cleanup;
            break;

        case VMWARE_DRIVER_WORKSTATION:
284
            if (virAsprintf(&bin, "%s/%s", vmwarePath, "vmware") < 0)
285 286 287
                goto cleanup;
            break;

288
        case VMWARE_DRIVER_FUSION:
289
            if (virAsprintf(&bin, "%s/%s", vmwarePath, "vmware-vmx") < 0)
290 291 292
                goto cleanup;
            break;

293 294
        default:
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
295
                           _("invalid driver type for version detection"));
296 297
            goto cleanup;
    }
298 299 300

    cmd = virCommandNewArgList(bin, "-v", NULL);
    virCommandSetOutputBuffer(cmd, &outbuf);
301
    virCommandSetErrorBuffer(cmd, &outbuf);
302 303 304 305

    if (virCommandRun(cmd, NULL) < 0)
        goto cleanup;

306
    if (vmwareParseVersionStr(driver->type, outbuf, &driver->version) < 0)
307 308 309 310
        goto cleanup;

    ret = 0;

311
 cleanup:
312 313
    virCommandFree(cmd);
    VIR_FREE(outbuf);
314 315
    VIR_FREE(bin);
    VIR_FREE(vmwarePath);
316 317 318 319 320 321
    return ret;
}

int
vmwareDomainConfigDisplay(vmwareDomainPtr pDomain, virDomainDefPtr def)
{
322
    size_t i;
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338

    if (def->ngraphics == 0) {
        pDomain->gui = true;
        return 0;
    } else {
        pDomain->gui = false;
        for (i = 0; i < def->ngraphics; i++) {
            if (def->graphics[i]->type == VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP) {
                pDomain->gui = true;
                return 0;
            }
        }
        return 0;
    }
}

E
Eric Blake 已提交
339 340
static int
vmwareParsePath(const char *path, char **directory, char **filename)
341 342 343 344 345 346
{
    char *separator;

    separator = strrchr(path, '/');

    if (separator != NULL) {
E
Eric Blake 已提交
347
        separator++;
348 349

        if (*separator == '\0') {
350 351
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("path '%s' doesn't reference a file"), path);
352 353 354
            return -1;
        }

E
Eric Blake 已提交
355
        if (VIR_STRNDUP(*directory, path, separator - path - 1) < 0)
356 357
            goto error;
        if (VIR_STRDUP(*filename, separator) < 0) {
358
            VIR_FREE(*directory);
359
            goto error;
360 361 362
        }

    } else {
363 364
        if (VIR_STRDUP(*filename, path) < 0)
            goto error;
365 366 367 368
    }

    return 0;

369
 error:
370 371 372 373 374 375
    return -1;
}

int
vmwareConstructVmxPath(char *directoryName, char *name, char **vmxPath)
{
376 377
    int ret;

378
    if (directoryName != NULL)
379 380 381 382 383 384 385
        ret = virAsprintf(vmxPath, "%s/%s.vmx", directoryName, name);
    else
        ret = virAsprintf(vmxPath, "%s.vmx", name);

    if (ret < 0)
        return -1;
    return 0;
386 387 388 389 390 391 392 393 394
}

int
vmwareVmxPath(virDomainDefPtr vmdef, char **vmxPath)
{
    virDomainDiskDefPtr disk = NULL;
    char *directoryName = NULL;
    char *fileName = NULL;
    int ret = -1;
395
    size_t i;
396
    const char *src;
397 398 399 400

    /*
     * Build VMX URL. Use the source of the first file-based harddisk
     * to deduce the path for the VMX file. Don't just use the
N
Nehal J Wani 已提交
401
     * first disk, because it may be CDROM disk and ISO images are normally not
402 403 404 405
     * located in the virtual machine's directory. This approach
     * isn't perfect but should work in the majority of cases.
     */
    if (vmdef->ndisks < 1) {
406 407 408
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Domain XML doesn't contain any disks, "
                         "cannot deduce datastore and path for VMX file"));
409 410 411 412 413
        goto cleanup;
    }

    for (i = 0; i < vmdef->ndisks; ++i) {
        if (vmdef->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_DISK &&
E
Eric Blake 已提交
414
            virDomainDiskGetType(vmdef->disks[i]) == VIR_STORAGE_TYPE_FILE) {
415 416 417 418 419 420
            disk = vmdef->disks[i];
            break;
        }
    }

    if (disk == NULL) {
421 422 423
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Domain XML doesn't contain any file-based harddisks, "
                         "cannot deduce datastore and path for VMX file"));
424 425 426
        goto cleanup;
    }

427 428
    src = virDomainDiskGetSource(disk);
    if (!src) {
429 430 431
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("First file-based harddisk has no source, cannot "
                         "deduce datastore and path for VMX file"));
432 433 434
        goto cleanup;
    }

435
    if (vmwareParsePath(src, &directoryName, &fileName) < 0)
436 437 438
        goto cleanup;

    if (!virFileHasSuffix(fileName, ".vmdk")) {
439 440
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Expecting source '%s' of first file-based harddisk "
441
                         "to be a VMDK image"), src);
442 443 444
        goto cleanup;
    }

445
    if (vmwareConstructVmxPath(directoryName, vmdef->name, vmxPath) < 0)
446 447 448 449
        goto cleanup;

    ret = 0;

450
 cleanup:
451 452 453 454 455 456 457 458 459
    VIR_FREE(directoryName);
    VIR_FREE(fileName);
    return ret;
}

int
vmwareMoveFile(char *srcFile, char *dstFile)
{
    const char *cmdmv[] =
E
Eric Blake 已提交
460
        { "mv", PROGRAM_SENTINEL, PROGRAM_SENTINEL, NULL };
461 462

    if (!virFileExists(srcFile)) {
463 464
        virReportError(VIR_ERR_INTERNAL_ERROR, _("file %s does not exist"),
                       srcFile);
465 466 467 468 469 470 471 472 473
        return -1;
    }

    if (STREQ(srcFile, dstFile))
        return 0;

    vmwareSetSentinal(cmdmv, srcFile);
    vmwareSetSentinal(cmdmv, dstFile);
    if (virRun(cmdmv, NULL) < 0) {
474 475
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("failed to move file to %s "), dstFile);
476 477 478 479 480 481 482 483 484
        return -1;
    }

    return 0;
}

int
vmwareMakePath(char *srcDir, char *srcName, char *srcExt, char **outpath)
{
485 486 487
    if (virAsprintf(outpath, "%s/%s.%s", srcDir, srcName, srcExt) < 0)
        return -1;
    return 0;
488 489 490 491 492 493 494 495 496 497
}

int
vmwareExtractPid(const char * vmxPath)
{
    char *vmxDir = NULL;
    char *logFilePath = NULL;
    FILE *logFile = NULL;
    char line[1024];
    char *tmp = NULL;
498
    int pid_value = -1;
499 500 501 502 503

    if ((vmxDir = mdir_name(vmxPath)) == NULL)
        goto cleanup;

    if (virAsprintf(&logFilePath, "%s/vmware.log",
504
                    vmxDir) < 0)
505 506 507 508 509 510
        goto cleanup;

    if ((logFile = fopen(logFilePath, "r")) == NULL)
        goto cleanup;

    if (!fgets(line, sizeof(line), logFile)) {
511 512
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("unable to read vmware log file"));
513 514 515 516
        goto cleanup;
    }

    if ((tmp = strstr(line, " pid=")) == NULL) {
517 518
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("cannot find pid in vmware log file"));
519 520 521 522 523
        goto cleanup;
    }

    tmp += strlen(" pid=");

524 525 526
    /* Although 64-bit windows allows 64-bit pid_t, a domain id has to be
     * 32 bits.  For now, we just reject pid values that overflow int.  */
    if (virStrToLong_i(tmp, &tmp, 10, &pid_value) < 0 || *tmp != ' ') {
527 528
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("cannot parse pid in vmware log file"));
529 530 531
        goto cleanup;
    }

532
 cleanup:
533 534 535
    VIR_FREE(vmxDir);
    VIR_FREE(logFilePath);
    VIR_FORCE_FCLOSE(logFile);
536
    return pid_value;
537 538 539
}

char *
540
vmwareCopyVMXFileName(const char *datastorePath, void *opaque ATTRIBUTE_UNUSED)
541
{
542
    char *path;
543

544
    ignore_value(VIR_STRDUP(path, datastorePath));
545 546
    return path;
}