2012-07-31 18:56:05 +00:00
|
|
|
/*
|
|
|
|
* parallels_driver.c: core driver functions for managing
|
|
|
|
* Parallels Cloud Server hosts
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 Parallels, 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
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-07-31 18:56:05 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/poll.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <paths.h>
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/statvfs.h>
|
|
|
|
|
|
|
|
#include "datatypes.h"
|
|
|
|
#include "virterror_internal.h"
|
|
|
|
#include "memory.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "logging.h"
|
2012-12-12 16:27:01 +00:00
|
|
|
#include "vircommand.h"
|
2012-07-31 18:56:05 +00:00
|
|
|
#include "configmake.h"
|
|
|
|
#include "storage_file.h"
|
|
|
|
#include "nodeinfo.h"
|
2012-12-11 10:59:42 +00:00
|
|
|
#include "c-ctype.h"
|
2012-07-31 18:56:05 +00:00
|
|
|
|
|
|
|
#include "parallels_driver.h"
|
2012-07-31 18:56:07 +00:00
|
|
|
#include "parallels_utils.h"
|
2012-07-31 18:56:05 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_PARALLELS
|
|
|
|
|
|
|
|
#define PRLCTL "prlctl"
|
2012-07-31 18:56:07 +00:00
|
|
|
#define PRLSRVCTL "prlsrvctl"
|
2012-07-31 18:56:05 +00:00
|
|
|
|
2012-07-31 18:56:07 +00:00
|
|
|
#define parallelsDomNotFoundError(domain) \
|
|
|
|
do { \
|
|
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN]; \
|
|
|
|
virUUIDFormat(domain->uuid, uuidstr); \
|
|
|
|
virReportError(VIR_ERR_NO_DOMAIN, \
|
|
|
|
_("no domain with matching uuid '%s'"), uuidstr); \
|
|
|
|
} while (0)
|
|
|
|
|
2012-12-04 13:43:01 +00:00
|
|
|
#define IS_CT(def) (STREQ_NULLABLE(def->os.type, "exe"))
|
|
|
|
|
2012-07-31 18:56:05 +00:00
|
|
|
static int parallelsClose(virConnectPtr conn);
|
|
|
|
|
2012-12-04 13:43:10 +00:00
|
|
|
static const char * parallelsGetDiskBusName(int bus) {
|
|
|
|
switch (bus) {
|
|
|
|
case VIR_DOMAIN_DISK_BUS_IDE:
|
|
|
|
return "ide";
|
|
|
|
case VIR_DOMAIN_DISK_BUS_SATA:
|
|
|
|
return "sata";
|
|
|
|
case VIR_DOMAIN_DISK_BUS_SCSI:
|
|
|
|
return "scsi";
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-01 03:46:22 +00:00
|
|
|
void
|
2012-07-31 18:56:05 +00:00
|
|
|
parallelsDriverLock(parallelsConnPtr driver)
|
|
|
|
{
|
|
|
|
virMutexLock(&driver->lock);
|
|
|
|
}
|
|
|
|
|
2012-08-01 03:46:22 +00:00
|
|
|
void
|
2012-07-31 18:56:05 +00:00
|
|
|
parallelsDriverUnlock(parallelsConnPtr driver)
|
|
|
|
{
|
|
|
|
virMutexUnlock(&driver->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2012-11-09 15:00:36 +00:00
|
|
|
parallelsDefaultConsoleType(const char *ostype ATTRIBUTE_UNUSED,
|
2012-12-10 22:28:09 +00:00
|
|
|
virArch arch ATTRIBUTE_UNUSED)
|
2012-07-31 18:56:05 +00:00
|
|
|
{
|
|
|
|
return VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL;
|
|
|
|
}
|
|
|
|
|
2012-07-31 18:56:07 +00:00
|
|
|
static void
|
|
|
|
parallelsDomObjFreePrivate(void *p)
|
|
|
|
{
|
|
|
|
parallelsDomObjPtr pdom = p;
|
|
|
|
|
|
|
|
if (!pdom)
|
|
|
|
return;
|
|
|
|
|
|
|
|
VIR_FREE(pdom->uuid);
|
2012-12-04 13:43:05 +00:00
|
|
|
VIR_FREE(pdom->home);
|
2012-07-31 18:56:07 +00:00
|
|
|
VIR_FREE(p);
|
|
|
|
};
|
|
|
|
|
2012-07-31 18:56:05 +00:00
|
|
|
static virCapsPtr
|
|
|
|
parallelsBuildCapabilities(void)
|
|
|
|
{
|
|
|
|
virCapsPtr caps;
|
|
|
|
virCapsGuestPtr guest;
|
|
|
|
|
2012-12-10 22:28:09 +00:00
|
|
|
if ((caps = virCapabilitiesNew(virArchFromHost(),
|
|
|
|
0, 0)) == NULL)
|
2012-07-31 18:56:05 +00:00
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (nodeCapsInitNUMA(caps) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
virCapabilitiesSetMacPrefix(caps, (unsigned char[]) {
|
|
|
|
0x42, 0x1C, 0x00});
|
|
|
|
|
2012-12-10 22:28:09 +00:00
|
|
|
if ((guest = virCapabilitiesAddGuest(caps, "hvm",
|
|
|
|
VIR_ARCH_X86_64,
|
|
|
|
"parallels",
|
2012-07-31 18:56:05 +00:00
|
|
|
NULL, 0, NULL)) == NULL)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (virCapabilitiesAddGuestDomain(guest,
|
|
|
|
"parallels", NULL, NULL, 0, NULL) == NULL)
|
|
|
|
goto no_memory;
|
|
|
|
|
2012-12-10 22:28:09 +00:00
|
|
|
if ((guest = virCapabilitiesAddGuest(caps, "exe",
|
|
|
|
VIR_ARCH_X86_64,
|
|
|
|
"parallels",
|
2012-09-10 15:22:42 +00:00
|
|
|
NULL, 0, NULL)) == NULL)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (virCapabilitiesAddGuestDomain(guest,
|
|
|
|
"parallels", NULL, NULL, 0, NULL) == NULL)
|
|
|
|
goto no_memory;
|
|
|
|
|
2012-07-31 18:56:05 +00:00
|
|
|
caps->defaultConsoleTargetType = parallelsDefaultConsoleType;
|
|
|
|
return caps;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
virCapabilitiesFree(caps);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
parallelsGetCapabilities(virConnectPtr conn)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = conn->privateData;
|
|
|
|
char *xml;
|
|
|
|
|
|
|
|
parallelsDriverLock(privconn);
|
|
|
|
if ((xml = virCapabilitiesFormatXML(privconn->caps)) == NULL)
|
|
|
|
virReportOOMError();
|
|
|
|
parallelsDriverUnlock(privconn);
|
|
|
|
return xml;
|
|
|
|
}
|
|
|
|
|
2012-07-31 18:56:09 +00:00
|
|
|
static int
|
|
|
|
parallelsGetSerialInfo(virDomainChrDefPtr chr,
|
|
|
|
const char *name, virJSONValuePtr value)
|
|
|
|
{
|
|
|
|
const char *tmp;
|
|
|
|
|
|
|
|
chr->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL;
|
|
|
|
chr->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL;
|
|
|
|
if (virStrToLong_i(name + strlen("serial"),
|
|
|
|
NULL, 10, &chr->target.port) < 0) {
|
|
|
|
parallelsParseError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virJSONValueObjectHasKey(value, "output")) {
|
|
|
|
chr->source.type = VIR_DOMAIN_CHR_TYPE_FILE;
|
|
|
|
|
|
|
|
tmp = virJSONValueObjectGetString(value, "output");
|
|
|
|
if (!tmp) {
|
|
|
|
parallelsParseError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(chr->source.data.file.path = strdup(tmp)))
|
|
|
|
goto no_memory;
|
|
|
|
} else if (virJSONValueObjectHasKey(value, "socket")) {
|
|
|
|
chr->source.type = VIR_DOMAIN_CHR_TYPE_UNIX;
|
|
|
|
|
|
|
|
tmp = virJSONValueObjectGetString(value, "socket");
|
|
|
|
if (!tmp) {
|
|
|
|
parallelsParseError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(chr->source.data.nix.path = strdup(tmp)))
|
|
|
|
goto no_memory;
|
|
|
|
chr->source.data.nix.listen = false;
|
|
|
|
} else if (virJSONValueObjectHasKey(value, "real")) {
|
|
|
|
chr->source.type = VIR_DOMAIN_CHR_TYPE_DEV;
|
|
|
|
|
|
|
|
tmp = virJSONValueObjectGetString(value, "real");
|
|
|
|
if (!tmp) {
|
|
|
|
parallelsParseError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(chr->source.data.file.path = strdup(tmp)))
|
|
|
|
goto no_memory;
|
|
|
|
} else {
|
|
|
|
parallelsParseError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2012-09-26 15:12:28 +00:00
|
|
|
parallelsAddSerialInfo(virDomainChrDefPtr **serials, size_t *nserials,
|
2012-07-31 18:56:09 +00:00
|
|
|
const char *key, virJSONValuePtr value)
|
|
|
|
{
|
|
|
|
virDomainChrDefPtr chr = NULL;
|
|
|
|
|
|
|
|
if (!(chr = virDomainChrDefNew()))
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (parallelsGetSerialInfo(chr, key, value))
|
|
|
|
goto cleanup;
|
|
|
|
|
2012-07-31 18:56:11 +00:00
|
|
|
if (VIR_REALLOC_N(*serials, *nserials + 1) < 0)
|
2012-07-31 18:56:09 +00:00
|
|
|
goto no_memory;
|
|
|
|
|
2012-07-31 18:56:11 +00:00
|
|
|
(*serials)[(*nserials)++] = chr;
|
2012-07-31 18:56:09 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
cleanup:
|
|
|
|
virDomainChrDefFree(chr);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-31 18:56:11 +00:00
|
|
|
static int
|
|
|
|
parallelsAddVideoInfo(virDomainDefPtr def, virJSONValuePtr value)
|
|
|
|
{
|
|
|
|
virDomainVideoDefPtr video = NULL;
|
|
|
|
virDomainVideoAccelDefPtr accel = NULL;
|
|
|
|
const char *tmp;
|
|
|
|
char *endptr;
|
|
|
|
unsigned long mem;
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(value, "size"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_ul(tmp, &endptr, 10, &mem) < 0) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!STREQ(endptr, "Mb")) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_ALLOC(video) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (VIR_ALLOC(accel) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
2012-09-26 12:30:00 +00:00
|
|
|
if (VIR_REALLOC_N(def->videos, def->nvideos + 1) < 0)
|
2012-07-31 18:56:11 +00:00
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
def->videos[def->nvideos++] = video;
|
|
|
|
|
|
|
|
video->type = VIR_DOMAIN_VIDEO_TYPE_VGA;
|
|
|
|
video->vram = mem << 20;
|
|
|
|
video->heads = 1;
|
|
|
|
video->accel = accel;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
cleanup:
|
|
|
|
virDomainVideoDefFree(video);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-12-04 13:43:01 +00:00
|
|
|
static int
|
|
|
|
parallelsGetHddInfo(virDomainDefPtr def,
|
|
|
|
virDomainDiskDefPtr disk,
|
|
|
|
const char *key,
|
|
|
|
virJSONValuePtr value)
|
|
|
|
{
|
|
|
|
const char *tmp;
|
|
|
|
unsigned int idx;
|
|
|
|
|
|
|
|
disk->device = VIR_DOMAIN_DISK_DEVICE_DISK;
|
|
|
|
|
|
|
|
if (virJSONValueObjectHasKey(value, "real") == 1) {
|
|
|
|
disk->type = VIR_DOMAIN_DISK_TYPE_BLOCK;
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(value, "real"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(disk->src = strdup(tmp))) {
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
disk->type = VIR_DOMAIN_DISK_TYPE_FILE;
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(value, "image"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(disk->src = strdup(tmp))) {
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = virJSONValueObjectGetString(value, "port");
|
|
|
|
if (!tmp && !IS_CT(def)) {
|
|
|
|
parallelsParseError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tmp) {
|
|
|
|
if (STRPREFIX(tmp, "ide")) {
|
|
|
|
disk->bus = VIR_DOMAIN_DISK_BUS_IDE;
|
|
|
|
} else if (STRPREFIX(tmp, "sata")) {
|
|
|
|
disk->bus = VIR_DOMAIN_DISK_BUS_SATA;
|
|
|
|
} else if (STRPREFIX(tmp, "scsi")) {
|
|
|
|
disk->bus = VIR_DOMAIN_DISK_BUS_SCSI;
|
|
|
|
} else {
|
|
|
|
parallelsParseError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *colonp;
|
|
|
|
unsigned int pos;
|
|
|
|
|
|
|
|
if (!(colonp = strchr(tmp, ':'))) {
|
|
|
|
parallelsParseError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_ui(colonp + 1, NULL, 10, &pos) < 0) {
|
|
|
|
parallelsParseError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
disk->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE;
|
|
|
|
disk->info.addr.drive.target = pos;
|
|
|
|
} else {
|
|
|
|
/* Actually there are no disk devices in containers, but in
|
|
|
|
* in Parallels Cloud Server we mount disk images as container's
|
|
|
|
* root fs during start, so it looks like a disk device. */
|
|
|
|
disk->bus = VIR_DOMAIN_DISK_BUS_IDE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_ui(key + strlen("hdd"), NULL, 10, &idx) < 0) {
|
|
|
|
parallelsParseError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(disk->dst = virIndexToDiskName(idx, "sd")))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsAddHddInfo(virDomainDefPtr def, const char *key, virJSONValuePtr value)
|
|
|
|
{
|
|
|
|
virDomainDiskDefPtr disk = NULL;
|
|
|
|
|
|
|
|
if (VIR_ALLOC(disk) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (parallelsGetHddInfo(def, disk, key, value))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (VIR_REALLOC_N(def->disks, def->ndisks + 1) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
def->disks[def->ndisks++] = disk;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
error:
|
|
|
|
virDomainDiskDefFree(disk);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-12-11 10:59:42 +00:00
|
|
|
static inline unsigned char hex2int(char c)
|
|
|
|
{
|
|
|
|
if (c <= '9')
|
|
|
|
return c - '0';
|
|
|
|
else
|
|
|
|
return 10 + c - 'A';
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse MAC address in format XXXXXXXXXXXX.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
parallelsMacAddrParse(const char *str, virMacAddrPtr addr)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (strlen(str) != 12)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
|
|
if (!c_isxdigit(str[2 * i]) || !c_isxdigit(str[2 * i + 1]))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
addr->addr[i] = (hex2int(str[2 * i]) << 4) + hex2int(str[2 * i + 1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
error:
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Invalid MAC address format '%s'"), str);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsGetNetInfo(virDomainNetDefPtr net,
|
|
|
|
const char *key,
|
|
|
|
virJSONValuePtr value)
|
|
|
|
{
|
|
|
|
const char *tmp;
|
|
|
|
|
|
|
|
/* use device name, shown by prlctl as target device
|
|
|
|
* for identifying network adapter in virDomainDefineXML */
|
|
|
|
if (!(net->ifname = strdup(key))) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
net->type = VIR_DOMAIN_NET_TYPE_NETWORK;
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(value, "mac"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parallelsMacAddrParse(tmp, &net->mac) < 0) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-12-11 10:59:49 +00:00
|
|
|
if (virJSONValueObjectHasKey(value, "network")) {
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(value, "network"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(net->data.network.name = strdup(tmp))) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
} else if (virJSONValueObjectHasKey(value, "type")) {
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(value, "type"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!STREQ(tmp, "routed")) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(net->data.network.name = strdup(PARALLELS_ROUTED_NETWORK_NAME))) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
parallelsParseError();
|
2012-12-11 10:59:42 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
net->linkstate = VIR_DOMAIN_NET_INTERFACE_LINK_STATE_UP;
|
|
|
|
if (virJSONValueObjectHasKey(value, "state")) {
|
|
|
|
tmp = virJSONValueObjectGetString(value, "state");
|
|
|
|
if STREQ(tmp, "disconnected")
|
|
|
|
net->linkstate = VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsAddNetInfo(virDomainDefPtr def, const char *key, virJSONValuePtr value)
|
|
|
|
{
|
|
|
|
virDomainNetDefPtr net = NULL;
|
|
|
|
|
|
|
|
if (VIR_ALLOC(net) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (parallelsGetNetInfo(net, key, value))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (VIR_EXPAND_N(def->nets, def->nnets, 1) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
def->nets[def->nnets - 1] = net;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
error:
|
|
|
|
virDomainNetDefFree(net);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-31 18:56:09 +00:00
|
|
|
static int
|
|
|
|
parallelsAddDomainHardware(virDomainDefPtr def, virJSONValuePtr jobj)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
size_t i;
|
|
|
|
virJSONValuePtr value;
|
|
|
|
const char *key;
|
|
|
|
|
|
|
|
n = virJSONValueObjectKeysNumber(jobj);
|
|
|
|
if (n < 1)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
key = virJSONValueObjectGetKey(jobj, i);
|
|
|
|
value = virJSONValueObjectGetValue(jobj, i);
|
|
|
|
|
|
|
|
if (STRPREFIX(key, "serial")) {
|
2012-07-31 18:56:11 +00:00
|
|
|
if (parallelsAddSerialInfo(&def->serials,
|
|
|
|
&def->nserials, key, value))
|
|
|
|
goto cleanup;
|
|
|
|
if (def->nconsoles == 0) {
|
|
|
|
if (parallelsAddSerialInfo(&def->consoles,
|
|
|
|
&def->nconsoles, key, value))
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
} else if (STREQ(key, "video")) {
|
|
|
|
if (parallelsAddVideoInfo(def, value))
|
2012-07-31 18:56:09 +00:00
|
|
|
goto cleanup;
|
2012-12-04 13:43:01 +00:00
|
|
|
} else if (STRPREFIX(key, "hdd")) {
|
|
|
|
if (parallelsAddHddInfo(def, key, value))
|
|
|
|
goto cleanup;
|
2012-12-11 10:59:42 +00:00
|
|
|
} else if (STRPREFIX(key, "net")) {
|
|
|
|
if (parallelsAddNetInfo(def, key, value))
|
|
|
|
goto cleanup;
|
2012-07-31 18:56:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-31 18:56:10 +00:00
|
|
|
static int
|
|
|
|
parallelsAddVNCInfo(virDomainDefPtr def, virJSONValuePtr jobj_root)
|
|
|
|
{
|
|
|
|
const char *tmp;
|
|
|
|
unsigned int port;
|
|
|
|
virJSONValuePtr jobj;
|
|
|
|
int ret = -1;
|
|
|
|
virDomainGraphicsDefPtr gr = NULL;
|
|
|
|
|
|
|
|
jobj = virJSONValueObjectGet(jobj_root, "Remote display");
|
|
|
|
if (!jobj) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = virJSONValueObjectGetString(jobj, "mode");
|
|
|
|
if (!tmp) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STREQ(tmp, "off")) {
|
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_ALLOC(gr) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (STREQ(tmp, "auto")) {
|
|
|
|
if (virJSONValueObjectGetNumberUint(jobj, "port", &port) < 0)
|
|
|
|
port = 0;
|
|
|
|
gr->data.vnc.autoport = 1;
|
|
|
|
} else {
|
|
|
|
if (virJSONValueObjectGetNumberUint(jobj, "port", &port) < 0) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
gr->data.vnc.autoport = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
gr->type = VIR_DOMAIN_GRAPHICS_TYPE_VNC;
|
|
|
|
gr->data.vnc.port = port;
|
|
|
|
gr->data.vnc.keymap = NULL;
|
|
|
|
gr->data.vnc.socket = NULL;
|
|
|
|
gr->data.vnc.auth.passwd = NULL;
|
|
|
|
gr->data.vnc.auth.expires = 0;
|
|
|
|
gr->data.vnc.auth.connected = 0;
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(jobj, "address"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_ALLOC(gr->listens) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
gr->nListens = 1;
|
|
|
|
|
|
|
|
if (!(gr->listens[0].address = strdup(tmp)))
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
gr->listens[0].type = VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS;
|
|
|
|
|
|
|
|
if (VIR_REALLOC_N(def->graphics, def->ngraphics + 1) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
def->graphics[def->ngraphics++] = gr;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
cleanup:
|
|
|
|
virDomainGraphicsDefFree(gr);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-07-31 18:56:07 +00:00
|
|
|
/*
|
|
|
|
* Must be called with privconn->lock held
|
|
|
|
*/
|
|
|
|
static virDomainObjPtr
|
|
|
|
parallelsLoadDomain(parallelsConnPtr privconn, virJSONValuePtr jobj)
|
|
|
|
{
|
|
|
|
virDomainObjPtr dom = NULL;
|
|
|
|
virDomainDefPtr def = NULL;
|
|
|
|
parallelsDomObjPtr pdom = NULL;
|
|
|
|
virJSONValuePtr jobj2, jobj3;
|
|
|
|
const char *tmp;
|
|
|
|
char *endptr;
|
|
|
|
unsigned long mem;
|
|
|
|
unsigned int x;
|
|
|
|
const char *autostart;
|
|
|
|
const char *state;
|
|
|
|
|
|
|
|
if (VIR_ALLOC(def) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
def->virtType = VIR_DOMAIN_VIRT_PARALLELS;
|
|
|
|
def->id = -1;
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(jobj, "Name"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (!(def->name = strdup(tmp)))
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(jobj, "ID"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virUUIDParse(tmp, def->uuid) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("UUID in config file malformed"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(jobj, "Description"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (!(def->description = strdup(tmp)))
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (!(jobj2 = virJSONValueObjectGet(jobj, "Hardware"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(jobj3 = virJSONValueObjectGet(jobj2, "cpu"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-09-10 15:22:43 +00:00
|
|
|
if (virJSONValueObjectGetNumberUint(jobj3, "cpus", &x) == 0) {
|
|
|
|
def->vcpus = x;
|
|
|
|
def->maxvcpus = x;
|
|
|
|
} else if ((tmp = virJSONValueObjectGetString(jobj3, "cpus"))) {
|
|
|
|
if (STREQ(tmp, "unlimited")) {
|
|
|
|
virNodeInfo nodeinfo;
|
|
|
|
|
|
|
|
if (nodeGetInfo(NULL, &nodeinfo) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Can't get node info"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
def->vcpus = nodeinfo.cpus;
|
|
|
|
def->maxvcpus = def->vcpus;
|
|
|
|
} else {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
} else {
|
2012-07-31 18:56:07 +00:00
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(jobj3 = virJSONValueObjectGet(jobj2, "memory"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(jobj3, "size"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_ul(tmp, &endptr, 10, &mem) < 0) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!STREQ(endptr, "Mb")) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
def->mem.max_balloon = mem;
|
|
|
|
def->mem.max_balloon <<= 10;
|
|
|
|
def->mem.cur_balloon = def->mem.max_balloon;
|
|
|
|
|
2012-09-10 15:22:42 +00:00
|
|
|
if (!(tmp = virJSONValueObjectGetString(jobj, "Type"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STREQ(tmp, "CT")) {
|
|
|
|
if (!(def->os.type = strdup("exe")))
|
|
|
|
goto no_memory;
|
|
|
|
if (!(def->os.init = strdup("/sbin/init")))
|
|
|
|
goto no_memory;
|
|
|
|
} else if (STREQ(tmp, "VM")) {
|
|
|
|
if (!(def->os.type = strdup("hvm")))
|
|
|
|
goto no_memory;
|
|
|
|
}
|
2012-07-31 18:56:07 +00:00
|
|
|
|
2012-12-10 22:28:09 +00:00
|
|
|
def->os.arch = VIR_ARCH_X86_64;
|
2012-07-31 18:56:07 +00:00
|
|
|
|
|
|
|
if (VIR_ALLOC(pdom) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (virJSONValueObjectGetNumberUint(jobj, "EnvID", &x) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
pdom->id = x;
|
|
|
|
if (!(tmp = virJSONValueObjectGetString(jobj, "ID"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (!(pdom->uuid = strdup(tmp)))
|
|
|
|
goto no_memory;
|
|
|
|
|
2012-12-04 13:43:05 +00:00
|
|
|
if (!(tmp = virJSONValueObjectGetString(jobj, "Home"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(pdom->home = strdup(tmp)))
|
|
|
|
goto no_memory;
|
|
|
|
|
2012-07-31 18:56:07 +00:00
|
|
|
if (!(tmp = virJSONValueObjectGetString(jobj, "OS")))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!(state = virJSONValueObjectGetString(jobj, "State"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(autostart = virJSONValueObjectGetString(jobj, "Autostart"))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-07-31 18:56:09 +00:00
|
|
|
if (parallelsAddDomainHardware(def, jobj2) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2012-07-31 18:56:10 +00:00
|
|
|
if (parallelsAddVNCInfo(def, jobj) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2012-07-31 18:56:07 +00:00
|
|
|
if (!(dom = virDomainAssignDef(privconn->caps,
|
|
|
|
&privconn->domains, def, false)))
|
|
|
|
goto cleanup;
|
|
|
|
/* dom is locked here */
|
|
|
|
|
|
|
|
dom->privateDataFreeFunc = parallelsDomObjFreePrivate;
|
|
|
|
dom->privateData = pdom;
|
|
|
|
dom->persistent = 1;
|
|
|
|
|
|
|
|
/* TODO: handle all possible states */
|
|
|
|
if (STREQ(state, "running")) {
|
|
|
|
virDomainObjSetState(dom, VIR_DOMAIN_RUNNING,
|
|
|
|
VIR_DOMAIN_RUNNING_BOOTED);
|
|
|
|
def->id = pdom->id;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STREQ(autostart, "on"))
|
|
|
|
dom->autostart = 1;
|
|
|
|
else
|
|
|
|
dom->autostart = 0;
|
|
|
|
|
|
|
|
virDomainObjUnlock(dom);
|
|
|
|
|
|
|
|
return dom;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
cleanup:
|
|
|
|
virDomainDefFree(def);
|
|
|
|
parallelsDomObjFreePrivate(pdom);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Must be called with privconn->lock held
|
|
|
|
*
|
|
|
|
* if domain_name is NULL - load information about all
|
|
|
|
* registered domains.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
parallelsLoadDomains(parallelsConnPtr privconn, const char *domain_name)
|
|
|
|
{
|
|
|
|
int count, i;
|
|
|
|
virJSONValuePtr jobj;
|
|
|
|
virJSONValuePtr jobj2;
|
|
|
|
virDomainObjPtr dom = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
jobj = parallelsParseOutput(PRLCTL, "list", "-j", "-a", "-i", "-H",
|
2012-09-10 15:22:42 +00:00
|
|
|
"--vmtype", "all", domain_name, NULL);
|
2012-07-31 18:56:07 +00:00
|
|
|
if (!jobj) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
count = virJSONValueArraySize(jobj);
|
|
|
|
if (count < 0) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
jobj2 = virJSONValueArrayGet(jobj, i);
|
|
|
|
if (!jobj2) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
dom = parallelsLoadDomain(privconn, jobj2);
|
|
|
|
if (!dom)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
virJSONValueFree(jobj);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-07-31 18:56:05 +00:00
|
|
|
static int
|
|
|
|
parallelsOpenDefault(virConnectPtr conn)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn;
|
|
|
|
|
|
|
|
if (VIR_ALLOC(privconn) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
return VIR_DRV_OPEN_ERROR;
|
|
|
|
}
|
|
|
|
if (virMutexInit(&privconn->lock) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("cannot initialize mutex"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(privconn->caps = parallelsBuildCapabilities()))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (virDomainObjListInit(&privconn->domains) < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
conn->privateData = privconn;
|
|
|
|
|
2012-07-31 18:56:07 +00:00
|
|
|
if (parallelsLoadDomains(privconn, NULL))
|
|
|
|
goto error;
|
|
|
|
|
2012-07-31 18:56:05 +00:00
|
|
|
return VIR_DRV_OPEN_SUCCESS;
|
|
|
|
|
|
|
|
error:
|
|
|
|
virDomainObjListDeinit(&privconn->domains);
|
|
|
|
virCapabilitiesFree(privconn->caps);
|
|
|
|
virStoragePoolObjListFree(&privconn->pools);
|
|
|
|
VIR_FREE(privconn);
|
|
|
|
return VIR_DRV_OPEN_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
static virDrvOpenStatus
|
|
|
|
parallelsOpen(virConnectPtr conn,
|
|
|
|
virConnectAuthPtr auth ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
|
|
|
|
|
|
|
|
if (!conn->uri)
|
|
|
|
return VIR_DRV_OPEN_DECLINED;
|
|
|
|
|
|
|
|
if (!conn->uri->scheme || STRNEQ(conn->uri->scheme, "parallels"))
|
|
|
|
return VIR_DRV_OPEN_DECLINED;
|
|
|
|
|
|
|
|
/* Remote driver should handle these. */
|
|
|
|
if (conn->uri->server)
|
|
|
|
return VIR_DRV_OPEN_DECLINED;
|
|
|
|
|
|
|
|
/* From this point on, the connection is for us. */
|
2012-08-13 15:50:13 +00:00
|
|
|
if (!STREQ_NULLABLE(conn->uri->path, "/system")) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Unexpected Parallels URI path '%s', try parallels:///system"),
|
|
|
|
conn->uri->path);
|
2012-07-31 18:56:05 +00:00
|
|
|
return VIR_DRV_OPEN_ERROR;
|
|
|
|
}
|
|
|
|
|
2012-08-13 15:50:13 +00:00
|
|
|
if ((ret = parallelsOpenDefault(conn)) != VIR_DRV_OPEN_SUCCESS)
|
2012-07-31 18:56:05 +00:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
return VIR_DRV_OPEN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsClose(virConnectPtr conn)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = conn->privateData;
|
|
|
|
|
|
|
|
parallelsDriverLock(privconn);
|
|
|
|
virCapabilitiesFree(privconn->caps);
|
|
|
|
virDomainObjListDeinit(&privconn->domains);
|
|
|
|
conn->privateData = NULL;
|
|
|
|
|
|
|
|
parallelsDriverUnlock(privconn);
|
|
|
|
virMutexDestroy(&privconn->lock);
|
|
|
|
|
|
|
|
VIR_FREE(privconn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsGetVersion(virConnectPtr conn ATTRIBUTE_UNUSED, unsigned long *hvVer)
|
|
|
|
{
|
2012-07-31 18:56:07 +00:00
|
|
|
char *output, *sVer, *tmp;
|
|
|
|
const char *searchStr = "prlsrvctl version ";
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
output = parallelsGetOutput(PRLSRVCTL, "--help", NULL);
|
|
|
|
|
|
|
|
if (!output) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(sVer = strstr(output, searchStr))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
sVer = sVer + strlen(searchStr);
|
|
|
|
|
|
|
|
/* parallels server has versions number like 6.0.17977.782218,
|
|
|
|
* so libvirt can handle only first two numbers. */
|
|
|
|
if (!(tmp = strchr(sVer, '.'))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(tmp = strchr(tmp + 1, '.'))) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp[0] = '\0';
|
|
|
|
if (virParseVersionString(sVer, hvVer, true) < 0) {
|
|
|
|
parallelsParseError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(output);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsListDomains(virConnectPtr conn, int *ids, int maxids)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = conn->privateData;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
parallelsDriverLock(privconn);
|
|
|
|
n = virDomainObjListGetActiveIDs(&privconn->domains, ids, maxids);
|
|
|
|
parallelsDriverUnlock(privconn);
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsNumOfDomains(virConnectPtr conn)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = conn->privateData;
|
|
|
|
int count;
|
|
|
|
|
|
|
|
parallelsDriverLock(privconn);
|
|
|
|
count = virDomainObjListNumOfDomains(&privconn->domains, 1);
|
|
|
|
parallelsDriverUnlock(privconn);
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsListDefinedDomains(virConnectPtr conn, char **const names, int maxnames)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = conn->privateData;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
parallelsDriverLock(privconn);
|
|
|
|
memset(names, 0, sizeof(*names) * maxnames);
|
|
|
|
n = virDomainObjListGetInactiveNames(&privconn->domains, names,
|
|
|
|
maxnames);
|
|
|
|
parallelsDriverUnlock(privconn);
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsNumOfDefinedDomains(virConnectPtr conn)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = conn->privateData;
|
|
|
|
int count;
|
|
|
|
|
|
|
|
parallelsDriverLock(privconn);
|
|
|
|
count = virDomainObjListNumOfDomains(&privconn->domains, 0);
|
|
|
|
parallelsDriverUnlock(privconn);
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsListAllDomains(virConnectPtr conn,
|
|
|
|
virDomainPtr **domains,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = conn->privateData;
|
|
|
|
int ret = -1;
|
|
|
|
|
2012-08-03 15:48:05 +00:00
|
|
|
virCheckFlags(VIR_CONNECT_LIST_DOMAINS_FILTERS_ALL, -1);
|
2012-07-31 18:56:07 +00:00
|
|
|
parallelsDriverLock(privconn);
|
|
|
|
ret = virDomainList(conn, privconn->domains.objs, domains, flags);
|
|
|
|
parallelsDriverUnlock(privconn);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static virDomainPtr
|
|
|
|
parallelsLookupDomainByID(virConnectPtr conn, int id)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = conn->privateData;
|
|
|
|
virDomainPtr ret = NULL;
|
|
|
|
virDomainObjPtr dom;
|
|
|
|
|
|
|
|
parallelsDriverLock(privconn);
|
|
|
|
dom = virDomainFindByID(&privconn->domains, id);
|
|
|
|
parallelsDriverUnlock(privconn);
|
|
|
|
|
|
|
|
if (dom == NULL) {
|
|
|
|
virReportError(VIR_ERR_NO_DOMAIN, NULL);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = virGetDomain(conn, dom->def->name, dom->def->uuid);
|
|
|
|
if (ret)
|
|
|
|
ret->id = dom->def->id;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (dom)
|
|
|
|
virDomainObjUnlock(dom);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static virDomainPtr
|
|
|
|
parallelsLookupDomainByUUID(virConnectPtr conn, const unsigned char *uuid)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = conn->privateData;
|
|
|
|
virDomainPtr ret = NULL;
|
|
|
|
virDomainObjPtr dom;
|
|
|
|
|
|
|
|
parallelsDriverLock(privconn);
|
|
|
|
dom = virDomainFindByUUID(&privconn->domains, uuid);
|
|
|
|
parallelsDriverUnlock(privconn);
|
|
|
|
|
|
|
|
if (dom == NULL) {
|
|
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
|
|
virUUIDFormat(uuid, uuidstr);
|
|
|
|
virReportError(VIR_ERR_NO_DOMAIN,
|
|
|
|
_("no domain with matching uuid '%s'"), uuidstr);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = virGetDomain(conn, dom->def->name, dom->def->uuid);
|
|
|
|
if (ret)
|
|
|
|
ret->id = dom->def->id;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (dom)
|
|
|
|
virDomainObjUnlock(dom);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static virDomainPtr
|
|
|
|
parallelsLookupDomainByName(virConnectPtr conn, const char *name)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = conn->privateData;
|
|
|
|
virDomainPtr ret = NULL;
|
|
|
|
virDomainObjPtr dom;
|
|
|
|
|
|
|
|
parallelsDriverLock(privconn);
|
|
|
|
dom = virDomainFindByName(&privconn->domains, name);
|
|
|
|
parallelsDriverUnlock(privconn);
|
|
|
|
|
|
|
|
if (dom == NULL) {
|
|
|
|
virReportError(VIR_ERR_NO_DOMAIN,
|
|
|
|
_("no domain with matching name '%s'"), name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = virGetDomain(conn, dom->def->name, dom->def->uuid);
|
|
|
|
if (ret)
|
|
|
|
ret->id = dom->def->id;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (dom)
|
|
|
|
virDomainObjUnlock(dom);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsGetDomainInfo(virDomainPtr domain, virDomainInfoPtr info)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = domain->conn->privateData;
|
|
|
|
virDomainObjPtr privdom;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
parallelsDriverLock(privconn);
|
|
|
|
privdom = virDomainFindByUUID(&privconn->domains, domain->uuid);
|
|
|
|
parallelsDriverUnlock(privconn);
|
|
|
|
|
|
|
|
if (privdom == NULL) {
|
|
|
|
parallelsDomNotFoundError(domain);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
info->state = virDomainObjGetState(privdom, NULL);
|
|
|
|
info->memory = privdom->def->mem.cur_balloon;
|
|
|
|
info->maxMem = privdom->def->mem.max_balloon;
|
|
|
|
info->nrVirtCpu = privdom->def->vcpus;
|
|
|
|
info->cpuTime = 0;
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (privdom)
|
|
|
|
virDomainObjUnlock(privdom);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
parallelsGetOSType(virDomainPtr domain)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = domain->conn->privateData;
|
|
|
|
virDomainObjPtr privdom;
|
|
|
|
|
|
|
|
char *ret = NULL;
|
|
|
|
|
|
|
|
parallelsDriverLock(privconn);
|
|
|
|
privdom = virDomainFindByUUID(&privconn->domains, domain->uuid);
|
|
|
|
if (privdom == NULL) {
|
|
|
|
parallelsDomNotFoundError(domain);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(ret = strdup(privdom->def->os.type)))
|
|
|
|
virReportOOMError();
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (privdom)
|
|
|
|
virDomainObjUnlock(privdom);
|
|
|
|
parallelsDriverUnlock(privconn);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsDomainIsPersistent(virDomainPtr domain)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = domain->conn->privateData;
|
|
|
|
virDomainObjPtr privdom;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
parallelsDriverLock(privconn);
|
|
|
|
privdom = virDomainFindByUUID(&privconn->domains, domain->uuid);
|
|
|
|
if (privdom == NULL) {
|
|
|
|
parallelsDomNotFoundError(domain);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (privdom)
|
|
|
|
virDomainObjUnlock(privdom);
|
|
|
|
parallelsDriverUnlock(privconn);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsDomainGetState(virDomainPtr domain,
|
|
|
|
int *state, int *reason, unsigned int flags)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = domain->conn->privateData;
|
|
|
|
virDomainObjPtr privdom;
|
|
|
|
int ret = -1;
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
|
|
|
|
parallelsDriverLock(privconn);
|
|
|
|
privdom = virDomainFindByUUID(&privconn->domains, domain->uuid);
|
|
|
|
parallelsDriverUnlock(privconn);
|
|
|
|
|
|
|
|
if (privdom == NULL) {
|
|
|
|
parallelsDomNotFoundError(domain);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
*state = virDomainObjGetState(privdom, reason);
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (privdom)
|
|
|
|
virDomainObjUnlock(privdom);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
parallelsDomainGetXMLDesc(virDomainPtr domain, unsigned int flags)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = domain->conn->privateData;
|
|
|
|
virDomainDefPtr def;
|
|
|
|
virDomainObjPtr privdom;
|
|
|
|
char *ret = NULL;
|
|
|
|
|
|
|
|
/* Flags checked by virDomainDefFormat */
|
|
|
|
|
|
|
|
parallelsDriverLock(privconn);
|
|
|
|
privdom = virDomainFindByUUID(&privconn->domains, domain->uuid);
|
|
|
|
parallelsDriverUnlock(privconn);
|
|
|
|
|
|
|
|
if (privdom == NULL) {
|
|
|
|
parallelsDomNotFoundError(domain);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
def = (flags & VIR_DOMAIN_XML_INACTIVE) &&
|
|
|
|
privdom->newDef ? privdom->newDef : privdom->def;
|
|
|
|
|
|
|
|
ret = virDomainDefFormat(def, flags);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (privdom)
|
|
|
|
virDomainObjUnlock(privdom);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsDomainGetAutostart(virDomainPtr domain, int *autostart)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = domain->conn->privateData;
|
|
|
|
virDomainObjPtr privdom;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
parallelsDriverLock(privconn);
|
|
|
|
privdom = virDomainFindByUUID(&privconn->domains, domain->uuid);
|
|
|
|
parallelsDriverUnlock(privconn);
|
|
|
|
|
|
|
|
if (privdom == NULL) {
|
|
|
|
parallelsDomNotFoundError(domain);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
*autostart = privdom->autostart;
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (privdom)
|
|
|
|
virDomainObjUnlock(privdom);
|
|
|
|
return ret;
|
2012-07-31 18:56:05 +00:00
|
|
|
}
|
|
|
|
|
2012-10-17 09:23:12 +00:00
|
|
|
typedef int (*parallelsChangeStateFunc)(virDomainObjPtr privdom);
|
2012-07-31 18:56:08 +00:00
|
|
|
#define PARALLELS_UUID(x) (((parallelsDomObjPtr)(x->privateData))->uuid)
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsDomainChangeState(virDomainPtr domain,
|
|
|
|
virDomainState req_state, const char *req_state_name,
|
|
|
|
parallelsChangeStateFunc chstate,
|
|
|
|
virDomainState new_state, int reason)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = domain->conn->privateData;
|
|
|
|
virDomainObjPtr privdom;
|
|
|
|
int state;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
parallelsDriverLock(privconn);
|
|
|
|
privdom = virDomainFindByUUID(&privconn->domains, domain->uuid);
|
|
|
|
parallelsDriverUnlock(privconn);
|
|
|
|
|
|
|
|
if (privdom == NULL) {
|
|
|
|
parallelsDomNotFoundError(domain);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = virDomainObjGetState(privdom, NULL);
|
|
|
|
if (state != req_state) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, _("domain '%s' not %s"),
|
|
|
|
privdom->def->name, req_state_name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chstate(privdom))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
virDomainObjSetState(privdom, new_state, reason);
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (privdom)
|
|
|
|
virDomainObjUnlock(privdom);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parallelsPause(virDomainObjPtr privdom)
|
|
|
|
{
|
|
|
|
return parallelsCmdRun(PRLCTL, "pause", PARALLELS_UUID(privdom), NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsPauseDomain(virDomainPtr domain)
|
|
|
|
{
|
|
|
|
return parallelsDomainChangeState(domain,
|
|
|
|
VIR_DOMAIN_RUNNING, "running",
|
|
|
|
parallelsPause,
|
|
|
|
VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_USER);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parallelsResume(virDomainObjPtr privdom)
|
|
|
|
{
|
|
|
|
return parallelsCmdRun(PRLCTL, "resume", PARALLELS_UUID(privdom), NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsResumeDomain(virDomainPtr domain)
|
|
|
|
{
|
|
|
|
return parallelsDomainChangeState(domain,
|
|
|
|
VIR_DOMAIN_PAUSED, "paused",
|
|
|
|
parallelsResume,
|
|
|
|
VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_UNPAUSED);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parallelsStart(virDomainObjPtr privdom)
|
|
|
|
{
|
|
|
|
return parallelsCmdRun(PRLCTL, "start", PARALLELS_UUID(privdom), NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsDomainCreate(virDomainPtr domain)
|
|
|
|
{
|
|
|
|
return parallelsDomainChangeState(domain,
|
|
|
|
VIR_DOMAIN_SHUTOFF, "stopped",
|
|
|
|
parallelsStart,
|
|
|
|
VIR_DOMAIN_RUNNING, VIR_DOMAIN_EVENT_STARTED_BOOTED);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parallelsKill(virDomainObjPtr privdom)
|
|
|
|
{
|
|
|
|
return parallelsCmdRun(PRLCTL, "stop", PARALLELS_UUID(privdom), "--kill", NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsDestroyDomain(virDomainPtr domain)
|
|
|
|
{
|
|
|
|
return parallelsDomainChangeState(domain,
|
|
|
|
VIR_DOMAIN_RUNNING, "running",
|
|
|
|
parallelsKill,
|
|
|
|
VIR_DOMAIN_SHUTOFF, VIR_DOMAIN_SHUTOFF_DESTROYED);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parallelsStop(virDomainObjPtr privdom)
|
|
|
|
{
|
|
|
|
return parallelsCmdRun(PRLCTL, "stop", PARALLELS_UUID(privdom), NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsShutdownDomain(virDomainPtr domain)
|
|
|
|
{
|
|
|
|
return parallelsDomainChangeState(domain,
|
|
|
|
VIR_DOMAIN_RUNNING, "running",
|
|
|
|
parallelsStop,
|
|
|
|
VIR_DOMAIN_SHUTOFF, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
|
|
|
|
}
|
|
|
|
|
2012-07-31 18:56:11 +00:00
|
|
|
static int
|
|
|
|
parallelsApplyGraphicsParams(virDomainGraphicsDefPtr *oldgraphics, int nold,
|
|
|
|
virDomainGraphicsDefPtr *newgraphics, int nnew)
|
|
|
|
{
|
|
|
|
virDomainGraphicsDefPtr new, old;
|
|
|
|
|
|
|
|
/* parallels server supports only 1 VNC display per VM */
|
|
|
|
if (nold != nnew || nnew > 1)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (nnew == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (newgraphics[0]->type != VIR_DOMAIN_GRAPHICS_TYPE_VNC)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
old = oldgraphics[0];
|
|
|
|
new = newgraphics[0];
|
|
|
|
|
|
|
|
if (old->data.vnc.port != new->data.vnc.port &&
|
2012-09-10 15:23:44 +00:00
|
|
|
(old->data.vnc.port != 0 && new->data.vnc.port != 0)) {
|
2012-07-31 18:56:11 +00:00
|
|
|
|
|
|
|
goto error;
|
|
|
|
} else if (old->data.vnc.autoport != new->data.vnc.autoport ||
|
|
|
|
new->data.vnc.keymap != NULL ||
|
|
|
|
new->data.vnc.socket != NULL ||
|
|
|
|
!STREQ_NULLABLE(old->data.vnc.auth.passwd, new->data.vnc.auth.passwd) ||
|
|
|
|
old->data.vnc.auth.expires != new->data.vnc.auth.expires ||
|
|
|
|
old->data.vnc.auth.validTo != new->data.vnc.auth.validTo ||
|
|
|
|
old->data.vnc.auth.connected != new->data.vnc.auth.connected) {
|
|
|
|
|
|
|
|
goto error;
|
|
|
|
} else if (old->nListens != new->nListens ||
|
|
|
|
new->nListens > 1 ||
|
|
|
|
old->listens[0].type != new->listens[0].type ||
|
|
|
|
!STREQ_NULLABLE(old->listens[0].address, new->listens[0].address) ||
|
|
|
|
!STREQ_NULLABLE(old->listens[0].network, new->listens[0].network)) {
|
|
|
|
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
error:
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("changing display parameters is not supported "
|
|
|
|
"by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsApplySerialParams(virDomainChrDefPtr *oldserials, int nold,
|
|
|
|
virDomainChrDefPtr *newserials, int nnew)
|
|
|
|
{
|
2012-10-26 17:23:56 +00:00
|
|
|
int i, j;
|
|
|
|
|
2012-07-31 18:56:11 +00:00
|
|
|
if (nold != nnew)
|
|
|
|
goto error;
|
|
|
|
|
2012-10-26 17:23:56 +00:00
|
|
|
for (i = 0; i < nold; i++) {
|
2012-07-31 18:56:11 +00:00
|
|
|
virDomainChrDefPtr oldserial = oldserials[i];
|
|
|
|
virDomainChrDefPtr newserial = NULL;
|
|
|
|
|
2012-10-26 17:23:56 +00:00
|
|
|
for (j = 0; j < nnew; j++) {
|
2012-07-31 18:56:11 +00:00
|
|
|
if (newserials[j]->target.port == oldserial->target.port) {
|
|
|
|
newserial = newserials[j];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!newserial)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (oldserial->source.type != newserial->source.type)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if ((newserial->source.type == VIR_DOMAIN_CHR_TYPE_DEV ||
|
|
|
|
newserial->source.type == VIR_DOMAIN_CHR_TYPE_FILE) &&
|
|
|
|
!STREQ_NULLABLE(oldserial->source.data.file.path,
|
|
|
|
newserial->source.data.file.path))
|
|
|
|
goto error;
|
2012-10-17 09:23:12 +00:00
|
|
|
if (newserial->source.type == VIR_DOMAIN_CHR_TYPE_UNIX &&
|
2012-07-31 18:56:11 +00:00
|
|
|
(!STREQ_NULLABLE(oldserial->source.data.nix.path,
|
|
|
|
newserial->source.data.nix.path) ||
|
|
|
|
oldserial->source.data.nix.listen == newserial->source.data.nix.listen)) {
|
|
|
|
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
error:
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("changing serial device parameters is "
|
|
|
|
"not supported by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsApplyVideoParams(parallelsDomObjPtr pdom,
|
|
|
|
virDomainVideoDefPtr *oldvideos, int nold,
|
|
|
|
virDomainVideoDefPtr *newvideos, int nnew)
|
|
|
|
{
|
|
|
|
virDomainVideoDefPtr old, new;
|
|
|
|
char str_vram[32];
|
|
|
|
|
|
|
|
if (nold != 1 || nnew != 1) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("Only one video device is "
|
|
|
|
"supported by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
old = oldvideos[0];
|
|
|
|
new = newvideos[0];
|
|
|
|
if (new->type != VIR_DOMAIN_VIDEO_TYPE_VGA) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("Only VGA video device is "
|
|
|
|
"supported by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new->heads != 1) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("Only one monitor is supported by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* old->accel must be always non-NULL */
|
|
|
|
if (new->accel == NULL ||
|
|
|
|
old->accel->support2d != new->accel->support2d ||
|
|
|
|
old->accel->support3d != new->accel->support3d) {
|
|
|
|
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("Changing video acceleration parameters is "
|
|
|
|
"not supported by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old->vram != new->vram) {
|
|
|
|
if (new->vram % (1 << 20) != 0) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("Video RAM size should be multiple of 1Mb."));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(str_vram, 31, "%d", new->vram >> 20);
|
|
|
|
str_vram[31] = '\0';
|
|
|
|
|
|
|
|
if (parallelsCmdRun(PRLCTL, "set", pdom->uuid,
|
|
|
|
"--videosize", str_vram, NULL))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-04 13:43:11 +00:00
|
|
|
static int parallelsAddHddByVolume(parallelsDomObjPtr pdom,
|
|
|
|
virDomainDiskDefPtr disk,
|
|
|
|
virStoragePoolObjPtr pool,
|
|
|
|
virStorageVolDefPtr voldef)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
const char *strbus;
|
|
|
|
|
|
|
|
virCommandPtr cmd = virCommandNewArgList(PRLCTL, "set", pdom->uuid,
|
|
|
|
"--device-add", "hdd", NULL);
|
|
|
|
virCommandAddArgFormat(cmd, "--size=%lluM", voldef->capacity >> 20);
|
|
|
|
|
|
|
|
if (!(strbus = parallelsGetDiskBusName(disk->bus))) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
|
|
|
|
_("Invalid disk bus: %d"), disk->bus);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
virCommandAddArgFormat(cmd, "--iface=%s", strbus);
|
|
|
|
|
|
|
|
if (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE)
|
|
|
|
virCommandAddArgFormat(cmd, "--position=%d",
|
|
|
|
disk->info.addr.drive.target);
|
|
|
|
|
|
|
|
if (virCommandRun(cmd, NULL))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (parallelsStorageVolumeDefRemove(pool, voldef))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
virCommandFree(cmd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parallelsAddHdd(virConnectPtr conn,
|
|
|
|
parallelsDomObjPtr pdom,
|
|
|
|
virDomainDiskDefPtr disk)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = conn->privateData;
|
|
|
|
virStorageVolDefPtr voldef = NULL;
|
|
|
|
virStoragePoolObjPtr pool = NULL;
|
|
|
|
virStorageVolPtr vol = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (!(vol = parallelsStorageVolumeLookupByPathLocked(conn, disk->src))) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Can't find volume with path '%s'"), disk->src);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pool = virStoragePoolObjFindByName(&privconn->pools, vol->pool);
|
|
|
|
if (!pool) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Can't find storage pool with name '%s'"),
|
|
|
|
vol->pool);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
voldef = virStorageVolDefFindByPath(pool, disk->src);
|
|
|
|
if (!voldef) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Can't find storage volume definition for path '%s'"),
|
|
|
|
disk->src);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = parallelsAddHddByVolume(pdom, disk, pool, voldef);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (pool)
|
|
|
|
virStoragePoolObjUnlock(pool);
|
|
|
|
virObjectUnref(vol);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-12-04 13:43:13 +00:00
|
|
|
static int parallelsRemoveHdd(parallelsDomObjPtr pdom,
|
|
|
|
virDomainDiskDefPtr disk)
|
|
|
|
{
|
|
|
|
char prlname[16];
|
|
|
|
|
|
|
|
prlname[15] = '\0';
|
|
|
|
snprintf(prlname, 15, "hdd%d", virDiskNameToIndex(disk->dst));
|
|
|
|
|
|
|
|
if (parallelsCmdRun(PRLCTL, "set", pdom->uuid,
|
|
|
|
"--device-del", prlname,
|
|
|
|
"--detach-only", NULL))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-04 13:43:02 +00:00
|
|
|
static int
|
2012-12-04 13:43:11 +00:00
|
|
|
parallelsApplyDisksParams(virConnectPtr conn, parallelsDomObjPtr pdom,
|
2012-12-04 13:43:02 +00:00
|
|
|
virDomainDiskDefPtr *olddisks, int nold,
|
|
|
|
virDomainDiskDefPtr *newdisks, int nnew)
|
|
|
|
{
|
2012-12-04 13:43:11 +00:00
|
|
|
int i, j;
|
2012-12-04 13:43:02 +00:00
|
|
|
|
2012-12-04 13:43:11 +00:00
|
|
|
for (i = 0; i < nold; i++) {
|
2012-12-04 13:43:02 +00:00
|
|
|
virDomainDiskDefPtr newdisk = NULL;
|
|
|
|
virDomainDiskDefPtr olddisk = olddisks[i];
|
2012-12-04 13:43:11 +00:00
|
|
|
for (j = 0; j < nnew; j++) {
|
2012-12-04 13:43:02 +00:00
|
|
|
if (STREQ_NULLABLE(newdisks[j]->dst, olddisk->dst)) {
|
|
|
|
newdisk = newdisks[j];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!newdisk) {
|
2012-12-04 13:43:13 +00:00
|
|
|
if (parallelsRemoveHdd(pdom, olddisk)) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
|
|
|
|
_("Can't remove disk '%s' "
|
|
|
|
"in the specified config"), olddisks[i]->serial);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
2012-12-04 13:43:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (olddisk->bus != newdisk->bus ||
|
|
|
|
olddisk->info.addr.drive.target != newdisk->info.addr.drive.target ||
|
|
|
|
!STREQ_NULLABLE(olddisk->src, newdisk->src)) {
|
|
|
|
|
|
|
|
char prlname[16];
|
|
|
|
char strpos[16];
|
|
|
|
const char *strbus;
|
|
|
|
|
|
|
|
prlname[15] = '\0';
|
|
|
|
snprintf(prlname, 15, "hdd%d", virDiskNameToIndex(newdisk->dst));
|
|
|
|
|
|
|
|
strpos[15] = '\0';
|
|
|
|
snprintf(strpos, 15, "%d", newdisk->info.addr.drive.target);
|
|
|
|
|
2012-12-04 13:43:10 +00:00
|
|
|
if (!(strbus = parallelsGetDiskBusName(newdisk->bus))) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
|
2012-12-04 13:43:11 +00:00
|
|
|
_("Unsupported disk bus: %d"), newdisk->bus);
|
2012-12-04 13:43:10 +00:00
|
|
|
return -1;
|
2012-12-04 13:43:02 +00:00
|
|
|
}
|
2012-12-04 13:43:10 +00:00
|
|
|
|
2012-12-04 13:43:02 +00:00
|
|
|
if (parallelsCmdRun(PRLCTL, "set", pdom->uuid,
|
|
|
|
"--device-set", prlname,
|
|
|
|
"--iface", strbus,
|
|
|
|
"--position", strpos,
|
|
|
|
"--image", newdisk->src, NULL))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-04 13:43:11 +00:00
|
|
|
for (i = 0; i < nnew; i++) {
|
|
|
|
virDomainDiskDefPtr newdisk = newdisks[i];
|
|
|
|
bool found = false;
|
|
|
|
for (j = 0; j < nold; j++)
|
|
|
|
if (STREQ_NULLABLE(olddisks[j]->dst, newdisk->dst))
|
|
|
|
found = true;
|
|
|
|
if (found)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (parallelsAddHdd(conn, pdom, newdisk))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-12-04 13:43:02 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-11 10:59:43 +00:00
|
|
|
static int parallelsApplyIfaceParams(parallelsDomObjPtr pdom,
|
|
|
|
virDomainNetDefPtr oldnet,
|
|
|
|
virDomainNetDefPtr newnet)
|
|
|
|
{
|
|
|
|
bool create = false;
|
|
|
|
bool is_changed = false;
|
|
|
|
virCommandPtr cmd;
|
|
|
|
char strmac[VIR_MAC_STRING_BUFLEN];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!oldnet) {
|
|
|
|
create = true;
|
|
|
|
if (VIR_ALLOC(oldnet) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!create && oldnet->type != newnet->type) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("Changing network type is not supported"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!STREQ_NULLABLE(oldnet->model, newnet->model)) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("Changing network device model is not supported"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!STREQ_NULLABLE(oldnet->data.network.portgroup,
|
|
|
|
newnet->data.network.portgroup)) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("Changing network portgroup is not supported"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!virNetDevVPortProfileEqual(oldnet->virtPortProfile,
|
|
|
|
newnet->virtPortProfile)) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("Changing virtual port profile is not supported"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newnet->tune.sndbuf_specified) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("Setting send buffer size is not supported"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!STREQ_NULLABLE(oldnet->script, newnet->script)) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("Setting startup script is not supported"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!STREQ_NULLABLE(oldnet->filter, newnet->filter)) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("Changing filter params is not supported"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newnet->bandwidth != NULL) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("Setting bandwidth params is not supported"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < sizeof(newnet->vlan); i++) {
|
|
|
|
if (((char *)&newnet->vlan)[i] != 0) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("Setting vlan params is not supported"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Here we know, that there are no differences, that are forbidden.
|
|
|
|
* Check is something changed, if no - do nothing */
|
|
|
|
|
|
|
|
if (create) {
|
|
|
|
cmd = virCommandNewArgList(PRLCTL, "set", pdom->uuid,
|
|
|
|
"--device-add", "net", NULL);
|
|
|
|
} else {
|
|
|
|
cmd = virCommandNewArgList(PRLCTL, "set", pdom->uuid,
|
|
|
|
"--device-set", newnet->ifname, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virMacAddrCmp(&oldnet->mac, &newnet->mac)) {
|
|
|
|
virMacAddrFormat(&newnet->mac, strmac);
|
|
|
|
virCommandAddArgFormat(cmd, "--mac=%s", strmac);
|
|
|
|
is_changed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!STREQ_NULLABLE(oldnet->data.network.name, newnet->data.network.name)) {
|
2012-12-11 10:59:49 +00:00
|
|
|
if (STREQ_NULLABLE(newnet->data.network.name,
|
|
|
|
PARALLELS_ROUTED_NETWORK_NAME)) {
|
|
|
|
virCommandAddArgFormat(cmd, "--type=routed");
|
|
|
|
} else {
|
|
|
|
virCommandAddArgFormat(cmd, "--network=%s",
|
|
|
|
newnet->data.network.name);
|
|
|
|
}
|
|
|
|
|
2012-12-11 10:59:43 +00:00
|
|
|
is_changed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (oldnet->linkstate != newnet->linkstate) {
|
|
|
|
if (newnet->linkstate == VIR_DOMAIN_NET_INTERFACE_LINK_STATE_UP) {
|
|
|
|
virCommandAddArgFormat(cmd, "--connect");
|
|
|
|
} else if (newnet->linkstate == VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN) {
|
|
|
|
virCommandAddArgFormat(cmd, "--disconnect");
|
|
|
|
}
|
|
|
|
is_changed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!create && !is_changed) {
|
|
|
|
/* nothing changed - no need to run prlctl */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virCommandRun(cmd, NULL))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parallelsApplyIfacesParams(parallelsDomObjPtr pdom,
|
|
|
|
virDomainNetDefPtr *oldnets, int nold,
|
|
|
|
virDomainNetDefPtr *newnets, int nnew)
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
virDomainNetDefPtr newnet;
|
|
|
|
virDomainNetDefPtr oldnet;
|
|
|
|
bool found;
|
|
|
|
|
|
|
|
for (i = 0; i < nold; i++) {
|
|
|
|
newnet = NULL;
|
|
|
|
oldnet = oldnets[i];
|
|
|
|
for (j = 0; j < nnew; j++) {
|
|
|
|
if (STREQ_NULLABLE(newnets[j]->ifname, oldnet->ifname)) {
|
|
|
|
newnet = newnets[j];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!newnet) {
|
|
|
|
if (parallelsCmdRun(PRLCTL, "set", pdom->uuid,
|
|
|
|
"--device-del", oldnet->ifname, NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parallelsApplyIfaceParams(pdom, oldnet, newnet) < 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < nnew; i++) {
|
|
|
|
newnet = newnets[i];
|
|
|
|
found = false;
|
|
|
|
|
|
|
|
for (j = 0; j < nold; j++)
|
|
|
|
if (STREQ_NULLABLE(oldnets[j]->ifname, newnet->ifname))
|
|
|
|
found = true;
|
|
|
|
if (found)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (parallelsApplyIfaceParams(pdom, NULL, newnet))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-31 18:56:11 +00:00
|
|
|
static int
|
2012-12-04 13:43:11 +00:00
|
|
|
parallelsApplyChanges(virConnectPtr conn, virDomainObjPtr dom, virDomainDefPtr new)
|
2012-07-31 18:56:11 +00:00
|
|
|
{
|
|
|
|
char buf[32];
|
|
|
|
|
|
|
|
virDomainDefPtr old = dom->def;
|
|
|
|
parallelsDomObjPtr pdom = dom->privateData;
|
|
|
|
|
|
|
|
if (new->description && !STREQ_NULLABLE(old->description, new->description)) {
|
|
|
|
if (parallelsCmdRun(PRLCTL, "set", pdom->uuid,
|
|
|
|
"--description", new->description, NULL))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new->name && !STREQ_NULLABLE(old->name, new->name)) {
|
|
|
|
if (parallelsCmdRun(PRLCTL, "set", pdom->uuid,
|
|
|
|
"--name", new->name, NULL))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new->title && !STREQ_NULLABLE(old->title, new->title)) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("titles are not supported by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new->blkio.ndevices > 0) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("blkio parameters are not supported "
|
|
|
|
"by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old->mem.max_balloon != new->mem.max_balloon) {
|
|
|
|
if (new->mem.max_balloon != new->mem.cur_balloon) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("changing balloon parameters is not supported "
|
|
|
|
"by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new->mem.max_balloon % (1 << 10) != 0) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("Memory size should be multiple of 1Mb."));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(buf, 31, "%llu", new->mem.max_balloon >> 10);
|
|
|
|
buf[31] = '\0';
|
|
|
|
|
|
|
|
if (parallelsCmdRun(PRLCTL, "set", pdom->uuid,
|
|
|
|
"--memsize", buf, NULL))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old->mem.hugepage_backed != new->mem.hugepage_backed ||
|
|
|
|
old->mem.hard_limit != new->mem.hard_limit ||
|
|
|
|
old->mem.soft_limit != new->mem.soft_limit ||
|
|
|
|
old->mem.min_guarantee != new->mem.min_guarantee ||
|
|
|
|
old->mem.swap_hard_limit != new->mem.swap_hard_limit) {
|
|
|
|
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("Memory parameter is not supported "
|
|
|
|
"by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old->vcpus != new->vcpus) {
|
|
|
|
if (new->vcpus != new->maxvcpus) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("current vcpus must be equal to maxvcpus"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(buf, 31, "%d", new->vcpus);
|
|
|
|
buf[31] = '\0';
|
|
|
|
|
|
|
|
if (parallelsCmdRun(PRLCTL, "set", pdom->uuid,
|
|
|
|
"--cpus", buf, NULL))
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old->placement_mode != new->placement_mode) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("changing cpu placement mode is not supported "
|
|
|
|
"by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-09-26 12:30:02 +00:00
|
|
|
if ((old->cpumask != NULL || new->cpumask != NULL) &&
|
|
|
|
(old->cpumask == NULL || new->cpumask == NULL ||
|
|
|
|
!virBitmapEqual(old->cpumask, new->cpumask))) {
|
2012-07-31 18:56:11 +00:00
|
|
|
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("changing cpu mask is not supported "
|
|
|
|
"by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old->cputune.shares != new->cputune.shares ||
|
|
|
|
old->cputune.period != new->cputune.period ||
|
|
|
|
old->cputune.quota != new->cputune.quota ||
|
|
|
|
old->cputune.nvcpupin != new->cputune.nvcpupin) {
|
|
|
|
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("cputune is not supported by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old->numatune.memory.mode != new->numatune.memory.mode ||
|
|
|
|
old->numatune.memory.placement_mode != new->numatune.memory.placement_mode ||
|
2012-09-26 12:30:02 +00:00
|
|
|
((old->numatune.memory.nodemask != NULL || new->numatune.memory.nodemask != NULL) &&
|
|
|
|
(old->numatune.memory.nodemask == NULL || new->numatune.memory.nodemask == NULL ||
|
|
|
|
!virBitmapEqual(old->numatune.memory.nodemask, new->numatune.memory.nodemask)))){
|
2012-07-31 18:56:11 +00:00
|
|
|
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("numa parameters are not supported "
|
|
|
|
"by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old->onReboot != new->onReboot ||
|
|
|
|
old->onPoweroff != new->onPoweroff ||
|
|
|
|
old->onCrash != new->onCrash) {
|
|
|
|
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("on_reboot, on_poweroff and on_crash parameters "
|
|
|
|
"are not supported by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-09-10 15:22:44 +00:00
|
|
|
/* we fill only type and arch fields in parallelsLoadDomain for
|
|
|
|
* hvm type and also init for containers, so we can check that all
|
|
|
|
* other paramenters are null and boot devices config is default */
|
|
|
|
|
2012-07-31 18:56:11 +00:00
|
|
|
if (!STREQ_NULLABLE(old->os.type, new->os.type) ||
|
2012-12-10 22:28:09 +00:00
|
|
|
old->os.arch != new->os.arch ||
|
2012-09-10 15:22:44 +00:00
|
|
|
new->os.machine != NULL || new->os.bootmenu != 0 ||
|
|
|
|
new->os.kernel != NULL || new->os.initrd != NULL ||
|
|
|
|
new->os.cmdline != NULL || new->os.root != NULL ||
|
|
|
|
new->os.loader != NULL || new->os.bootloader != NULL ||
|
|
|
|
new->os.bootloaderArgs != NULL || new->os.smbios_mode != 0 ||
|
|
|
|
new->os.bios.useserial != 0) {
|
2012-07-31 18:56:11 +00:00
|
|
|
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("changing OS parameters is not supported "
|
|
|
|
"by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
2012-09-10 15:22:44 +00:00
|
|
|
if (STREQ(new->os.type, "hvm")) {
|
|
|
|
if (new->os.nBootDevs != 1 ||
|
|
|
|
new->os.bootDevs[0] != VIR_DOMAIN_BOOT_DISK ||
|
|
|
|
new->os.init != NULL || new->os.initargv != NULL) {
|
|
|
|
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("changing OS parameters is not supported "
|
|
|
|
"by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (new->os.nBootDevs != 0 ||
|
|
|
|
!STREQ_NULLABLE(old->os.init, new->os.init) ||
|
|
|
|
(new->os.initargv != NULL && new->os.initargv[0] != NULL)) {
|
|
|
|
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("changing OS parameters is not supported "
|
|
|
|
"by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-31 18:56:11 +00:00
|
|
|
|
|
|
|
if (!STREQ_NULLABLE(old->emulator, new->emulator)) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("changing emulator is not supported "
|
|
|
|
"by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old->features != new->features) {
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("changing features is not supported "
|
|
|
|
"by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new->clock.offset != VIR_DOMAIN_CLOCK_OFFSET_UTC ||
|
|
|
|
new->clock.ntimers != 0) {
|
|
|
|
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("changing clock parameters is not supported "
|
|
|
|
"by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parallelsApplyGraphicsParams(old->graphics, old->ngraphics,
|
|
|
|
new->graphics, new->ngraphics) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2012-12-11 10:59:43 +00:00
|
|
|
if (new->nfss != 0 ||
|
2012-07-31 18:56:11 +00:00
|
|
|
new->nsounds != 0 || new->nhostdevs != 0 ||
|
|
|
|
new->nredirdevs != 0 || new->nsmartcards != 0 ||
|
|
|
|
new->nparallels || new->nchannels != 0 ||
|
|
|
|
new->nleases != 0 || new->nhubs != 0) {
|
|
|
|
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("changing devices parameters is not supported "
|
|
|
|
"by parallels driver"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* there may be one auto-input */
|
|
|
|
if (new->ninputs > 1 ||
|
|
|
|
(new->ninputs > 1 &&
|
|
|
|
(new->inputs[0]->type != VIR_DOMAIN_INPUT_TYPE_MOUSE ||
|
|
|
|
new->inputs[0]->bus != VIR_DOMAIN_INPUT_BUS_PS2))) {
|
|
|
|
|
|
|
|
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
|
|
|
_("changing input devices parameters is not supported "
|
|
|
|
"by parallels driver"));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (parallelsApplySerialParams(old->serials, old->nserials,
|
|
|
|
new->serials, new->nserials) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (parallelsApplySerialParams(old->consoles, old->nconsoles,
|
|
|
|
new->consoles, new->nconsoles) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (parallelsApplyVideoParams(pdom, old->videos, old->nvideos,
|
|
|
|
new->videos, new->nvideos) < 0)
|
|
|
|
return -1;
|
2012-12-04 13:43:11 +00:00
|
|
|
if (parallelsApplyDisksParams(conn, pdom, old->disks, old->ndisks,
|
2012-12-04 13:43:02 +00:00
|
|
|
new->disks, new->ndisks) < 0)
|
|
|
|
return -1;
|
2012-12-11 10:59:43 +00:00
|
|
|
if (parallelsApplyIfacesParams(pdom, old->nets, old->nnets,
|
|
|
|
new->nets, new->nnets) < 0)
|
|
|
|
return -1;
|
2012-12-04 13:43:02 +00:00
|
|
|
|
2012-07-31 18:56:11 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-31 18:56:13 +00:00
|
|
|
static int
|
|
|
|
parallelsCreateVm(virConnectPtr conn, virDomainDefPtr def)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = conn->privateData;
|
|
|
|
int i;
|
|
|
|
virStorageVolDefPtr privvol = NULL;
|
|
|
|
virStoragePoolObjPtr pool = NULL;
|
|
|
|
virStorageVolPtr vol = NULL;
|
|
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
|
|
|
|
|
|
for (i = 0; i < def->ndisks; i++) {
|
|
|
|
if (def->disks[i]->device != VIR_DOMAIN_DISK_DEVICE_DISK)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
vol = parallelsStorageVolumeLookupByPathLocked(conn, def->disks[i]->src);
|
|
|
|
if (!vol) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Can't find volume with path '%s'"),
|
|
|
|
def->disks[i]->src);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!vol) {
|
|
|
|
/* We determine path to VM directory from volume, so
|
|
|
|
* let's report error if no disk until better solution
|
|
|
|
* will be found */
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Can't create VM '%s' without hard disks"),
|
2012-08-03 16:22:08 +00:00
|
|
|
def->name ? def->name : _("(unnamed)"));
|
2012-07-31 18:56:13 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pool = virStoragePoolObjFindByName(&privconn->pools, vol->pool);
|
|
|
|
if (!pool) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Can't find storage pool with name '%s'"),
|
|
|
|
vol->pool);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
privvol = virStorageVolDefFindByPath(pool, def->disks[i]->src);
|
|
|
|
if (!privvol) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Can't find storage volume definition for path '%s'"),
|
|
|
|
def->disks[i]->src);
|
|
|
|
goto error2;
|
|
|
|
}
|
|
|
|
|
|
|
|
virUUIDFormat(def->uuid, uuidstr);
|
|
|
|
|
|
|
|
if (parallelsCmdRun(PRLCTL, "create", def->name, "--dst",
|
|
|
|
pool->def->target.path, "--no-hdd",
|
|
|
|
"--uuid", uuidstr, NULL) < 0)
|
|
|
|
goto error2;
|
|
|
|
|
|
|
|
if (parallelsCmdRun(PRLCTL, "set", def->name, "--vnc-mode", "auto", NULL) < 0)
|
|
|
|
goto error2;
|
|
|
|
|
|
|
|
virStoragePoolObjUnlock(pool);
|
Convert public datatypes to inherit from virObject
This converts the following public API datatypes to use the
virObject infrastructure:
virConnectPtr
virDomainPtr
virDomainSnapshotPtr
virInterfacePtr
virNetworkPtr
virNodeDevicePtr
virNWFilterPtr
virSecretPtr
virStreamPtr
virStorageVolPtr
virStoragePoolPtr
The code is significantly simplified, since the mutex in the
virConnectPtr object now only needs to be held when accessing
the per-connection virError object instance. All other operations
are completely lock free.
* src/datatypes.c, src/datatypes.h, src/libvirt.c: Convert
public datatypes to use virObject
* src/conf/domain_event.c, src/phyp/phyp_driver.c,
src/qemu/qemu_command.c, src/qemu/qemu_migration.c,
src/qemu/qemu_process.c, src/storage/storage_driver.c,
src/vbox/vbox_tmpl.c, src/xen/xend_internal.c,
tests/qemuxml2argvtest.c, tests/qemuxmlnstest.c,
tests/sexpr2xmltest.c, tests/xmconfigtest.c: Convert
to use virObjectUnref/virObjectRef
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2012-07-31 16:55:36 +00:00
|
|
|
virObjectUnref(vol);
|
2012-07-31 18:56:13 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error2:
|
|
|
|
virStoragePoolObjUnlock(pool);
|
|
|
|
error:
|
Convert public datatypes to inherit from virObject
This converts the following public API datatypes to use the
virObject infrastructure:
virConnectPtr
virDomainPtr
virDomainSnapshotPtr
virInterfacePtr
virNetworkPtr
virNodeDevicePtr
virNWFilterPtr
virSecretPtr
virStreamPtr
virStorageVolPtr
virStoragePoolPtr
The code is significantly simplified, since the mutex in the
virConnectPtr object now only needs to be held when accessing
the per-connection virError object instance. All other operations
are completely lock free.
* src/datatypes.c, src/datatypes.h, src/libvirt.c: Convert
public datatypes to use virObject
* src/conf/domain_event.c, src/phyp/phyp_driver.c,
src/qemu/qemu_command.c, src/qemu/qemu_migration.c,
src/qemu/qemu_process.c, src/storage/storage_driver.c,
src/vbox/vbox_tmpl.c, src/xen/xend_internal.c,
tests/qemuxml2argvtest.c, tests/qemuxmlnstest.c,
tests/sexpr2xmltest.c, tests/xmconfigtest.c: Convert
to use virObjectUnref/virObjectRef
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2012-07-31 16:55:36 +00:00
|
|
|
virObjectUnref(vol);
|
2012-07-31 18:56:13 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-09-12 12:40:54 +00:00
|
|
|
static int
|
|
|
|
parallelsCreateCt(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainDefPtr def)
|
|
|
|
{
|
|
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
|
|
|
|
|
|
virUUIDFormat(def->uuid, uuidstr);
|
|
|
|
|
|
|
|
if (def->nfss != 1 ||
|
|
|
|
def->fss[0]->type != VIR_DOMAIN_FS_TYPE_TEMPLATE) {
|
|
|
|
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
|
|
_("There must be only 1 template FS for "
|
|
|
|
"container creation"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parallelsCmdRun(PRLCTL, "create", def->name, "--vmtype", "ct",
|
|
|
|
"--uuid", uuidstr,
|
|
|
|
"--ostemplate", def->fss[0]->src, NULL) < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (parallelsCmdRun(PRLCTL, "set", def->name, "--vnc-mode", "auto", NULL) < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-31 18:56:11 +00:00
|
|
|
static virDomainPtr
|
|
|
|
parallelsDomainDefineXML(virConnectPtr conn, const char *xml)
|
|
|
|
{
|
|
|
|
parallelsConnPtr privconn = conn->privateData;
|
|
|
|
virDomainPtr ret = NULL;
|
|
|
|
virDomainDefPtr def;
|
|
|
|
virDomainObjPtr dom = NULL, olddom = NULL;
|
|
|
|
int dupVM;
|
|
|
|
|
|
|
|
parallelsDriverLock(privconn);
|
|
|
|
if ((def = virDomainDefParseString(privconn->caps, xml,
|
|
|
|
1 << VIR_DOMAIN_VIRT_PARALLELS,
|
|
|
|
VIR_DOMAIN_XML_INACTIVE)) == NULL) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
|
|
_("Can't parse XML desc"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((dupVM = virDomainObjIsDuplicate(&privconn->domains, def, 0)) < 0) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s", _("Already exists"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dupVM == 1) {
|
|
|
|
olddom = virDomainFindByUUID(&privconn->domains, def->uuid);
|
|
|
|
} else {
|
2012-09-12 12:40:54 +00:00
|
|
|
if (STREQ(def->os.type, "hvm")) {
|
|
|
|
if (parallelsCreateVm(conn, def))
|
|
|
|
goto cleanup;
|
|
|
|
} else if (STREQ(def->os.type, "exe")) {
|
|
|
|
if (parallelsCreateCt(conn, def))
|
|
|
|
goto cleanup;
|
|
|
|
} else {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Unsupported OS type: %s"), def->os.type);
|
2012-07-31 18:56:11 +00:00
|
|
|
goto cleanup;
|
2012-09-12 12:40:54 +00:00
|
|
|
}
|
2012-07-31 18:56:13 +00:00
|
|
|
if (parallelsLoadDomains(privconn, def->name))
|
|
|
|
goto cleanup;
|
2012-12-04 13:43:12 +00:00
|
|
|
olddom = virDomainFindByName(&privconn->domains, def->name);
|
|
|
|
if (!olddom) {
|
2012-07-31 18:56:13 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Domain for '%s' is not defined after creation"),
|
2012-08-03 16:22:08 +00:00
|
|
|
def->name ? def->name : _("(unnamed)"));
|
2012-07-31 18:56:13 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
2012-07-31 18:56:11 +00:00
|
|
|
}
|
|
|
|
|
2012-12-04 13:43:12 +00:00
|
|
|
if (parallelsApplyChanges(conn, olddom, def) < 0) {
|
|
|
|
virDomainObjUnlock(olddom);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
virDomainObjUnlock(olddom);
|
|
|
|
|
|
|
|
if (!(dom = virDomainAssignDef(privconn->caps,
|
|
|
|
&privconn->domains, def, false))) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Can't allocate domobj"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
def = NULL;
|
|
|
|
|
2012-07-31 18:56:11 +00:00
|
|
|
ret = virGetDomain(conn, dom->def->name, dom->def->uuid);
|
|
|
|
if (ret)
|
|
|
|
ret->id = dom->def->id;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
virDomainDefFree(def);
|
|
|
|
if (dom)
|
|
|
|
virDomainObjUnlock(dom);
|
|
|
|
parallelsDriverUnlock(privconn);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-07-31 18:56:05 +00:00
|
|
|
static virDriver parallelsDriver = {
|
|
|
|
.no = VIR_DRV_PARALLELS,
|
|
|
|
.name = "Parallels",
|
|
|
|
.open = parallelsOpen, /* 0.10.0 */
|
|
|
|
.close = parallelsClose, /* 0.10.0 */
|
|
|
|
.version = parallelsGetVersion, /* 0.10.0 */
|
|
|
|
.getHostname = virGetHostname, /* 0.10.0 */
|
|
|
|
.nodeGetInfo = nodeGetInfo, /* 0.10.0 */
|
|
|
|
.getCapabilities = parallelsGetCapabilities, /* 0.10.0 */
|
2012-07-31 18:56:07 +00:00
|
|
|
.listDomains = parallelsListDomains, /* 0.10.0 */
|
|
|
|
.numOfDomains = parallelsNumOfDomains, /* 0.10.0 */
|
|
|
|
.listDefinedDomains = parallelsListDefinedDomains, /* 0.10.0 */
|
|
|
|
.numOfDefinedDomains = parallelsNumOfDefinedDomains, /* 0.10.0 */
|
|
|
|
.listAllDomains = parallelsListAllDomains, /* 0.10.0 */
|
|
|
|
.domainLookupByID = parallelsLookupDomainByID, /* 0.10.0 */
|
|
|
|
.domainLookupByUUID = parallelsLookupDomainByUUID, /* 0.10.0 */
|
|
|
|
.domainLookupByName = parallelsLookupDomainByName, /* 0.10.0 */
|
|
|
|
.domainGetOSType = parallelsGetOSType, /* 0.10.0 */
|
|
|
|
.domainGetInfo = parallelsGetDomainInfo, /* 0.10.0 */
|
|
|
|
.domainGetState = parallelsDomainGetState, /* 0.10.0 */
|
|
|
|
.domainGetXMLDesc = parallelsDomainGetXMLDesc, /* 0.10.0 */
|
|
|
|
.domainIsPersistent = parallelsDomainIsPersistent, /* 0.10.0 */
|
|
|
|
.domainGetAutostart = parallelsDomainGetAutostart, /* 0.10.0 */
|
2012-07-31 18:56:08 +00:00
|
|
|
.domainSuspend = parallelsPauseDomain, /* 0.10.0 */
|
|
|
|
.domainResume = parallelsResumeDomain, /* 0.10.0 */
|
|
|
|
.domainDestroy = parallelsDestroyDomain, /* 0.10.0 */
|
|
|
|
.domainShutdown = parallelsShutdownDomain, /* 0.10.0 */
|
|
|
|
.domainCreate = parallelsDomainCreate, /* 0.10.0 */
|
2012-07-31 18:56:11 +00:00
|
|
|
.domainDefineXML = parallelsDomainDefineXML, /* 0.10.0 */
|
2012-07-31 18:56:05 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* parallelsRegister:
|
|
|
|
*
|
|
|
|
* Registers the parallels driver
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
parallelsRegister(void)
|
|
|
|
{
|
|
|
|
char *prlctl_path;
|
|
|
|
|
|
|
|
prlctl_path = virFindFileInPath(PRLCTL);
|
|
|
|
if (!prlctl_path) {
|
|
|
|
VIR_DEBUG("%s", _("Can't find prlctl command in the PATH env"));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FREE(prlctl_path);
|
|
|
|
|
|
|
|
if (virRegisterDriver(¶llelsDriver) < 0)
|
|
|
|
return -1;
|
2012-08-01 03:46:22 +00:00
|
|
|
if (parallelsStorageRegister())
|
|
|
|
return -1;
|
2012-12-11 10:59:45 +00:00
|
|
|
if (parallelsNetworkRegister())
|
|
|
|
return -1;
|
2012-07-31 18:56:05 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|