diff options
Diffstat (limited to 'src/parallels')
-rw-r--r-- | src/parallels/parallels_driver.c | 608 | ||||
-rw-r--r-- | src/parallels/parallels_utils.c | 109 | ||||
-rw-r--r-- | src/parallels/parallels_utils.h | 31 |
3 files changed, 745 insertions, 3 deletions
diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c index bdbb0aaa9..585260c23 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -53,14 +53,29 @@ #include "nodeinfo.h" #include "json.h" #include "domain_conf.h" +#include "virdomainlist.h" #include "parallels_driver.h" +#include "parallels_utils.h" #define VIR_FROM_THIS VIR_FROM_PARALLELS #define PRLCTL "prlctl" +#define PRLSRVCTL "prlsrvctl" #define PARALLELS_DEFAULT_ARCH "x86_64" +#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) + +#define parallelsParseError() \ + virReportErrorHelper(VIR_FROM_TEST, VIR_ERR_OPERATION_FAILED, __FILE__, \ + __FUNCTION__, __LINE__, _("Can't parse prlctl output")) + struct _parallelsConn { virMutex lock; virDomainObjList domains; @@ -71,6 +86,14 @@ struct _parallelsConn { typedef struct _parallelsConn parallelsConn; typedef struct _parallelsConn *parallelsConnPtr; +struct parallelsDomObj { + int id; + char *uuid; + char *os; +}; + +typedef struct parallelsDomObj *parallelsDomObjPtr; + static int parallelsClose(virConnectPtr conn); static void @@ -91,6 +114,18 @@ parallelsDefaultConsoleType(const char *ostype ATTRIBUTE_UNUSED) return VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL; } +static void +parallelsDomObjFreePrivate(void *p) +{ + parallelsDomObjPtr pdom = p; + + if (!pdom) + return; + + VIR_FREE(pdom->uuid); + VIR_FREE(p); +}; + static virCapsPtr parallelsBuildCapabilities(void) { @@ -139,6 +174,207 @@ parallelsGetCapabilities(virConnectPtr conn) return xml; } +/* + * 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; + } + + if (virJSONValueObjectGetNumberUint(jobj3, "cpus", &x) < 0) { + parallelsParseError(); + goto cleanup; + } + def->vcpus = x; + def->maxvcpus = x; + + 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; + + if (!(def->os.type = strdup("hvm"))) + goto no_memory; + + if (!(def->os.arch = strdup(PARALLELS_DEFAULT_ARCH))) + goto no_memory; + + 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; + + if (!(tmp = virJSONValueObjectGetString(jobj, "OS"))) + goto cleanup; + + if (!(state = virJSONValueObjectGetString(jobj, "State"))) { + parallelsParseError(); + goto cleanup; + } + + if (!(autostart = virJSONValueObjectGetString(jobj, "Autostart"))) { + parallelsParseError(); + goto cleanup; + } + + 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", + "--vmtype", "vm", domain_name, NULL); + 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; +} + static int parallelsOpenDefault(virConnectPtr conn) { @@ -162,6 +398,9 @@ parallelsOpenDefault(virConnectPtr conn) conn->privateData = privconn; + if (parallelsLoadDomains(privconn, NULL)) + goto error; + return VIR_DRV_OPEN_SUCCESS; error: @@ -232,9 +471,358 @@ parallelsClose(virConnectPtr conn) static int parallelsGetVersion(virConnectPtr conn ATTRIBUTE_UNUSED, unsigned long *hvVer) { - /* TODO */ - *hvVer = 6; - return 0; + 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; + + virCheckFlags(VIR_CONNECT_LIST_FILTERS_ALL, -1); + 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; } static virDriver parallelsDriver = { @@ -246,6 +834,20 @@ static virDriver parallelsDriver = { .getHostname = virGetHostname, /* 0.10.0 */ .nodeGetInfo = nodeGetInfo, /* 0.10.0 */ .getCapabilities = parallelsGetCapabilities, /* 0.10.0 */ + .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 */ }; /** diff --git a/src/parallels/parallels_utils.c b/src/parallels/parallels_utils.c new file mode 100644 index 000000000..afb221ea1 --- /dev/null +++ b/src/parallels/parallels_utils.c @@ -0,0 +1,109 @@ +/* + * parallels_utils.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 + * License along with this library; If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include <config.h> + +#include <stdarg.h> + +#include "command.h" +#include "virterror_internal.h" +#include "memory.h" +#include "json.h" + +#include "parallels_utils.h" + +#define VIR_FROM_THIS VIR_FROM_PARALLELS + +static int +parallelsDoCmdRun(char **outbuf, const char *binary, va_list list) +{ + virCommandPtr cmd = virCommandNewVAList(binary, list); + char *scmd = NULL; + int ret = -1; + + if (outbuf) + virCommandSetOutputBuffer(cmd, outbuf); + + scmd = virCommandToString(cmd); + if (!scmd) + goto cleanup; + + if (virCommandRun(cmd, NULL)) + goto cleanup; + + ret = 0; + + cleanup: + VIR_FREE(scmd); + virCommandFree(cmd); + if (ret) + VIR_FREE(*outbuf); + return ret; +} + +/* + * Run command and parse its JSON output, return + * pointer to virJSONValue or NULL in case of error. + */ +virJSONValuePtr +parallelsParseOutput(const char *binary, ...) +{ + char *outbuf; + virJSONValuePtr jobj = NULL; + va_list list; + int ret; + + va_start(list, binary); + ret = parallelsDoCmdRun(&outbuf, binary, list); + va_end(list); + if (ret) + return NULL; + + jobj = virJSONValueFromString(outbuf); + if (!jobj) + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid output from prlctl: %s"), outbuf); + + VIR_FREE(outbuf); + return jobj; +} + +/* + * Run command and return its output, pointer to + * buffer or NULL in case of error. Caller os responsible + * for freeing the buffer. + */ +char * +parallelsGetOutput(const char *binary, ...) +{ + char *outbuf; + va_list list; + int ret; + + va_start(list, binary); + ret = parallelsDoCmdRun(&outbuf, binary, list); + va_end(list); + if (ret) + return NULL; + + return outbuf; +} diff --git a/src/parallels/parallels_utils.h b/src/parallels/parallels_utils.h new file mode 100644 index 000000000..422df6fdf --- /dev/null +++ b/src/parallels/parallels_utils.h @@ -0,0 +1,31 @@ +/* + * parallels_utils.h: 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 + * License along with this library; If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#ifndef PARALLELS_UTILS_H +# define PARALLELS_UTILS_H + +virJSONValuePtr parallelsParseOutput(const char *binary, ...) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_SENTINEL; +char * parallelsGetOutput(const char *binary, ...) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_SENTINEL; + +#endif |