/* * vz_sdk.c: core driver functions for managing * Parallels Cloud Server hosts * * Copyright (C) 2014 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 * . * */ #include #include #include "virerror.h" #include "viralloc.h" #include "virstring.h" #include "virlog.h" #include "datatypes.h" #include "domain_conf.h" #include "storage_conf.h" #include "virtime.h" #include "virhostcpu.h" #include "virsocketaddr.h" #include "virutil.h" #include "vz_sdk.h" #define VIR_FROM_THIS VIR_FROM_PARALLELS #define JOB_INFINIT_WAIT_TIMEOUT UINT_MAX static int prlsdkUUIDParse(const char *uuidstr, unsigned char *uuid); static void prlsdkConvertError(PRL_RESULT pret); static PRL_RESULT prlsdkEventsHandler(PRL_HANDLE prlEvent, PRL_VOID_PTR opaque); VIR_LOG_INIT("parallels.sdk"); static PRL_HANDLE prlsdkFindNetByMAC(PRL_HANDLE sdkdom, virMacAddrPtr mac); static PRL_HANDLE prlsdkGetDisk(PRL_HANDLE sdkdom, virDomainDiskDefPtr disk); static bool prlsdkInBootList(PRL_HANDLE sdkdom, PRL_HANDLE sdktargetdev); /* * Log error description */ static void logPrlErrorHelper(PRL_RESULT err, const char *filename, const char *funcname, size_t linenr) { char *msg1 = NULL, *msg2 = NULL; PRL_UINT32 len = 0; /* Get required buffer length */ PrlApi_GetResultDescription(err, PRL_TRUE, PRL_FALSE, NULL, &len); if (VIR_ALLOC_N(msg1, len) < 0) goto cleanup; /* get short error description */ PrlApi_GetResultDescription(err, PRL_TRUE, PRL_FALSE, msg1, &len); PrlApi_GetResultDescription(err, PRL_FALSE, PRL_FALSE, NULL, &len); if (VIR_ALLOC_N(msg2, len) < 0) goto cleanup; /* get long error description */ PrlApi_GetResultDescription(err, PRL_FALSE, PRL_FALSE, msg2, &len); virReportErrorHelper(VIR_FROM_THIS, VIR_ERR_INTERNAL_ERROR, filename, funcname, linenr, _("%s %s"), msg1, msg2); cleanup: VIR_FREE(msg1); VIR_FREE(msg2); } #define logPrlError(code) \ logPrlErrorHelper(code, __FILE__, \ __FUNCTION__, __LINE__) #define prlsdkCheckRetGoto(ret, label) \ do { \ if (PRL_FAILED(ret)) { \ logPrlError(ret); \ goto label; \ } \ } while (0) #define prlsdkCheckRetExit(ret, code) \ do { \ if (PRL_FAILED(ret)) { \ logPrlError(ret); \ return code; \ } \ } while (0) static void logPrlEventErrorHelper(PRL_HANDLE event, const char *filename, const char *funcname, size_t linenr) { char *msg1 = NULL, *msg2 = NULL; PRL_UINT32 len = 0; PrlEvent_GetErrString(event, PRL_TRUE, PRL_FALSE, NULL, &len); if (VIR_ALLOC_N(msg1, len) < 0) goto cleanup; PrlEvent_GetErrString(event, PRL_TRUE, PRL_FALSE, msg1, &len); PrlEvent_GetErrString(event, PRL_FALSE, PRL_FALSE, NULL, &len); if (VIR_ALLOC_N(msg2, len) < 0) goto cleanup; PrlEvent_GetErrString(event, PRL_FALSE, PRL_FALSE, msg2, &len); virReportErrorHelper(VIR_FROM_THIS, VIR_ERR_INTERNAL_ERROR, filename, funcname, linenr, _("%s %s"), msg1, msg2); cleanup: VIR_FREE(msg1); VIR_FREE(msg2); } static PRL_RESULT getJobResultHelper(PRL_HANDLE job, unsigned int timeout, PRL_HANDLE *result, const char *filename, const char *funcname, size_t linenr) { PRL_RESULT ret, retCode; if (PRL_FAILED(ret = PrlJob_Wait(job, timeout))) { logPrlErrorHelper(ret, filename, funcname, linenr); goto cleanup; } if (PRL_FAILED(ret = PrlJob_GetRetCode(job, &retCode))) { logPrlErrorHelper(ret, filename, funcname, linenr); goto cleanup; } if (retCode) { PRL_HANDLE err_handle; ret = retCode; /* Sometimes it's possible to get additional error info. */ if (PRL_FAILED(retCode = PrlJob_GetError(job, &err_handle))) { logPrlErrorHelper(ret, filename, funcname, linenr); goto cleanup; } if (PRL_FAILED(retCode = PrlEvent_GetErrCode(err_handle, &retCode))) { logPrlErrorHelper(ret, filename, funcname, linenr); if (PRL_ERR_NO_DATA != retCode) logPrlError(retCode); PrlHandle_Free(err_handle); goto cleanup; } logPrlEventErrorHelper(err_handle, filename, funcname, linenr); PrlHandle_Free(err_handle); } else { ret = PrlJob_GetResult(job, result); if (PRL_FAILED(ret)) { logPrlErrorHelper(ret, filename, funcname, linenr); PrlHandle_Free(*result); *result = NULL; goto cleanup; } ret = PRL_ERR_SUCCESS; } cleanup: PrlHandle_Free(job); return ret; } #define getJobResult(job, result) \ getJobResultHelper(job, JOB_INFINIT_WAIT_TIMEOUT, \ result, __FILE__, __FUNCTION__, __LINE__) static PRL_RESULT getDomainJobResultHelper(PRL_HANDLE job, virDomainObjPtr dom, unsigned int timeout, PRL_HANDLE *result, const char *filename, const char *funcname, size_t linenr) { PRL_RESULT pret; if (dom) virObjectUnlock(dom); pret = getJobResultHelper(job, timeout, result, filename, funcname, linenr); if (dom) virObjectLock(dom); return pret; } #define getDomainJobResult(job, dom, result) \ getDomainJobResultHelper(job, dom, JOB_INFINIT_WAIT_TIMEOUT, \ result, __FILE__, __FUNCTION__, __LINE__) static PRL_RESULT waitJobHelper(PRL_HANDLE job, unsigned int timeout, const char *filename, const char *funcname, size_t linenr) { PRL_HANDLE result = PRL_INVALID_HANDLE; PRL_RESULT ret; ret = getJobResultHelper(job, timeout, &result, filename, funcname, linenr); PrlHandle_Free(result); return ret; } #define waitJob(job) \ waitJobHelper(job, JOB_INFINIT_WAIT_TIMEOUT, __FILE__, \ __FUNCTION__, __LINE__) static PRL_RESULT waitDomainJobHelper(PRL_HANDLE job, virDomainObjPtr dom, unsigned int timeout, const char *filename, const char *funcname, size_t linenr) { vzDomObjPtr pdom = dom->privateData; PRL_RESULT ret; if (pdom->job.cancelled) { virReportError(VIR_ERR_OPERATION_ABORTED, "%s", _("Operation cancelled by client")); return PRL_ERR_FAILURE; } pdom->job.sdkJob = job; if (dom) virObjectUnlock(dom); ret = waitJobHelper(job, timeout, filename, funcname, linenr); if (dom) virObjectLock(dom); pdom->job.sdkJob = NULL; return ret; } #define waitDomainJob(job, dom) \ waitDomainJobHelper(job, dom, JOB_INFINIT_WAIT_TIMEOUT, __FILE__, \ __FUNCTION__, __LINE__) typedef PRL_RESULT (*prlsdkParamGetterType)(PRL_HANDLE, char*, PRL_UINT32*); int prlsdkCancelJob(virDomainObjPtr dom) { vzDomObjPtr privdom = dom->privateData; PRL_RESULT pret; PRL_HANDLE job; if (!privdom->job.active) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("no job is active on the domain")); return -1; } privdom->job.cancelled = true; job = PrlJob_Cancel(privdom->job.sdkJob); virObjectUnlock(dom); pret = waitJobHelper(job, JOB_INFINIT_WAIT_TIMEOUT, __FILE__, __FUNCTION__, __LINE__); virObjectLock(dom); return PRL_FAILED(pret) ? -1 : 0; } static char* prlsdkGetStringParamVar(prlsdkParamGetterType getter, PRL_HANDLE handle) { PRL_RESULT pret; PRL_UINT32 buflen = 0; char *str = NULL; pret = getter(handle, NULL, &buflen); prlsdkCheckRetGoto(pret, error); if (VIR_ALLOC_N(str, buflen) < 0) goto error; pret = getter(handle, str, &buflen); prlsdkCheckRetGoto(pret, error); return str; error: VIR_FREE(str); return NULL; } static PRL_RESULT prlsdkGetStringParamBuf(prlsdkParamGetterType getter, PRL_HANDLE handle, char *buf, size_t size) { PRL_UINT32 buflen = size; return getter(handle, buf, &buflen); } int prlsdkInit(void) { PRL_RESULT ret; /* Disable console output */ PrlApi_SwitchConsoleLogging(0); ret = PrlApi_InitEx(PARALLELS_API_VER, PAM_SERVER, 0, 0); if (PRL_FAILED(ret)) { logPrlError(ret); return -1; } return 0; }; void prlsdkDeinit(void) { PrlApi_Deinit(); }; int prlsdkConnect(vzDriverPtr driver) { int ret = -1; PRL_RESULT pret; PRL_HANDLE job = PRL_INVALID_HANDLE; PRL_HANDLE result = PRL_INVALID_HANDLE; PRL_HANDLE response = PRL_INVALID_HANDLE; char session_uuid[VIR_UUID_STRING_BRACED_BUFLEN]; pret = PrlSrv_Create(&driver->server); prlsdkCheckRetExit(pret, -1); job = PrlSrv_LoginLocalEx(driver->server, NULL, 0, PSL_HIGH_SECURITY, PACF_NON_INTERACTIVE_MODE); if (PRL_FAILED(getJobResult(job, &result))) goto destroy; pret = PrlResult_GetParam(result, &response); prlsdkCheckRetGoto(pret, logoff); pret = prlsdkGetStringParamBuf(PrlLoginResponse_GetSessionUuid, response, session_uuid, sizeof(session_uuid)); prlsdkCheckRetGoto(pret, logoff); if (prlsdkUUIDParse(session_uuid, driver->session_uuid) < 0) goto logoff; pret = PrlSrv_RegEventHandler(driver->server, prlsdkEventsHandler, driver); prlsdkCheckRetGoto(pret, logoff); ret = 0; cleanup: PrlHandle_Free(result); PrlHandle_Free(response); return ret; logoff: job = PrlSrv_Logoff(driver->server); waitJob(job); destroy: PrlHandle_Free(driver->server); driver->server = PRL_INVALID_HANDLE; goto cleanup; } void prlsdkDisconnect(vzDriverPtr driver) { PRL_HANDLE job; PRL_RESULT ret; if (driver->server == PRL_INVALID_HANDLE) return; ret = PrlSrv_UnregEventHandler(driver->server, prlsdkEventsHandler, driver); if (PRL_FAILED(ret)) logPrlError(ret); job = PrlSrv_Logoff(driver->server); waitJob(job); PrlHandle_Free(driver->server); driver->server = PRL_INVALID_HANDLE; } static int prlsdkSdkDomainLookup(vzDriverPtr driver, const char *id, unsigned int flags, PRL_HANDLE *sdkdom) { PRL_HANDLE job = PRL_INVALID_HANDLE; PRL_HANDLE result = PRL_INVALID_HANDLE; PRL_RESULT pret = PRL_ERR_UNINITIALIZED; int ret = -1; job = PrlSrv_GetVmConfig(driver->server, id, flags); if (PRL_FAILED(getJobResult(job, &result))) goto cleanup; pret = PrlResult_GetParamByIndex(result, 0, sdkdom); prlsdkCheckRetGoto(pret, cleanup); ret = 0; cleanup: PrlHandle_Free(result); return ret; } static void prlsdkUUIDFormat(const unsigned char *uuid, char *uuidstr) { virUUIDFormat(uuid, uuidstr + 1); uuidstr[0] = '{'; uuidstr[VIR_UUID_STRING_BUFLEN] = '}'; uuidstr[VIR_UUID_STRING_BUFLEN + 1] = '\0'; } static PRL_HANDLE prlsdkSdkDomainLookupByUUID(vzDriverPtr driver, const unsigned char *uuid) { char uuidstr[VIR_UUID_STRING_BRACED_BUFLEN]; PRL_HANDLE sdkdom = PRL_INVALID_HANDLE; prlsdkUUIDFormat(uuid, uuidstr); if (prlsdkSdkDomainLookup(driver, uuidstr, PGVC_SEARCH_BY_UUID, &sdkdom) < 0) { virUUIDFormat(uuid, uuidstr); virReportError(VIR_ERR_NO_DOMAIN, _("no domain with matching uuid '%s'"), uuidstr); return PRL_INVALID_HANDLE; } return sdkdom; } PRL_HANDLE prlsdkSdkDomainLookupByName(vzDriverPtr driver, const char *name) { PRL_HANDLE sdkdom = PRL_INVALID_HANDLE; if (prlsdkSdkDomainLookup(driver, name, PGVC_SEARCH_BY_NAME, &sdkdom) < 0) { virReportError(VIR_ERR_NO_DOMAIN, _("no domain with matching name '%s'"), name); return PRL_INVALID_HANDLE; } return sdkdom; } static int prlsdkUUIDParse(const char *uuidstr, unsigned char *uuid) { char *tmp = NULL; int ret = -1; virCheckNonNullArgGoto(uuidstr, error); virCheckNonNullArgGoto(uuid, error); tmp = g_strdup(uuidstr); tmp[strlen(tmp) - 1] = '\0'; /* trim curly braces */ if (virUUIDParse(tmp + 1, uuid) < 0) goto error; ret = 0; error: VIR_FREE(tmp); return ret; } static int prlsdkGetDomainState(virDomainObjPtr dom, PRL_HANDLE sdkdom, VIRTUAL_MACHINE_STATE_PTR vmState) { PRL_HANDLE job = PRL_INVALID_HANDLE; PRL_HANDLE result = PRL_INVALID_HANDLE; PRL_HANDLE vmInfo = PRL_INVALID_HANDLE; PRL_RESULT pret; int ret = -1; job = PrlVm_GetState(sdkdom); if (PRL_FAILED(getDomainJobResult(job, dom, &result))) goto cleanup; pret = PrlResult_GetParamByIndex(result, 0, &vmInfo); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmInfo_GetState(vmInfo, vmState); prlsdkCheckRetGoto(pret, cleanup); ret = 0; cleanup: PrlHandle_Free(vmInfo); PrlHandle_Free(result); return ret; } static int prlsdkAddDomainVideoInfoCt(virDomainDefPtr def, virDomainXMLOptionPtr xmlopt) { virDomainVideoDefPtr video = NULL; int ret = -1; if (def->ngraphics == 0) return 0; if (!(video = virDomainVideoDefNew(xmlopt))) goto cleanup; video->type = VIR_DOMAIN_VIDEO_TYPE_PARALLELS; video->vram = 0; if (VIR_APPEND_ELEMENT(def->videos, def->nvideos, video) < 0) goto cleanup; ret = 0; cleanup: virDomainVideoDefFree(video); return ret; } static int prlsdkAddDomainVideoInfoVm(PRL_HANDLE sdkdom, virDomainDefPtr def) { virDomainVideoDefPtr video = NULL; virDomainVideoAccelDefPtr accel = NULL; PRL_RESULT ret; PRL_UINT32 videoRam; /* video info */ ret = PrlVmCfg_GetVideoRamSize(sdkdom, &videoRam); prlsdkCheckRetGoto(ret, error); if (VIR_ALLOC(video) < 0) goto error; if (VIR_ALLOC(accel) < 0) goto error; if (VIR_APPEND_ELEMENT_COPY(def->videos, def->nvideos, video) < 0) goto error; video->type = VIR_DOMAIN_VIDEO_TYPE_VGA; video->vram = videoRam << 10; /* from mbibytes to kbibytes */ video->heads = 1; video->accel = accel; return 0; error: VIR_FREE(accel); virDomainVideoDefFree(video); return -1; } static int prlsdkGetDiskId(PRL_HANDLE disk, int *bus, char **dst) { PRL_RESULT pret; PRL_UINT32 pos, ifType; pret = PrlVmDev_GetStackIndex(disk, &pos); prlsdkCheckRetExit(pret, -1); pret = PrlVmDev_GetIfaceType(disk, &ifType); prlsdkCheckRetExit(pret, -1); switch (ifType) { case PMS_IDE_DEVICE: *bus = VIR_DOMAIN_DISK_BUS_IDE; *dst = virIndexToDiskName(pos, "hd"); break; case PMS_SCSI_DEVICE: case PMS_UNKNOWN_DEVICE: *bus = VIR_DOMAIN_DISK_BUS_SCSI; *dst = virIndexToDiskName(pos, "sd"); break; case PMS_SATA_DEVICE: *bus = VIR_DOMAIN_DISK_BUS_SATA; *dst = virIndexToDiskName(pos, "sd"); break; default: virReportError(VIR_ERR_INTERNAL_ERROR, _("Unknown disk bus: %X"), ifType); return -1; } if (NULL == *dst) return -1; return 0; } static int prlsdkGetDiskInfo(vzDriverPtr driver, PRL_HANDLE prldisk, virDomainDiskDefPtr disk, bool isCdrom, bool isCt) { char *buf = NULL; PRL_RESULT pret; PRL_UINT32 emulatedType; PRL_UINT32 size; virDomainDeviceDriveAddressPtr address; int busIdx, devIdx; int ret = -1; pret = PrlVmDev_GetEmulatedType(prldisk, &emulatedType); prlsdkCheckRetGoto(pret, cleanup); if (emulatedType == PDT_USE_IMAGE_FILE) { virDomainDiskSetType(disk, VIR_STORAGE_TYPE_FILE); if (isCdrom) { virDomainDiskSetFormat(disk, VIR_STORAGE_FILE_RAW); } else { if (isCt) virDomainDiskSetFormat(disk, driver->vzCaps.ctDiskFormat); else virDomainDiskSetFormat(disk, driver->vzCaps.vmDiskFormat); } } else { virDomainDiskSetType(disk, VIR_STORAGE_TYPE_BLOCK); virDomainDiskSetFormat(disk, VIR_STORAGE_FILE_RAW); } if (isCdrom) { disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM; disk->src->readonly = true; } else { disk->device = VIR_DOMAIN_DISK_DEVICE_DISK; } if (!(buf = prlsdkGetStringParamVar(PrlVmDev_GetFriendlyName, prldisk))) goto cleanup; if (*buf != '\0' && virDomainDiskSetSource(disk, buf) < 0) goto cleanup; if (prlsdkGetDiskId(prldisk, &disk->bus, &disk->dst) < 0) goto cleanup; if (virDiskNameToBusDeviceIndex(disk, &busIdx, &devIdx) < 0) goto cleanup; address = &disk->info.addr.drive; address->bus = busIdx; address->target = 0; address->unit = devIdx; disk->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE; if (!isCdrom) { if (!(disk->serial = prlsdkGetStringParamVar(PrlVmDevHd_GetSerialNumber, prldisk))) goto cleanup; if (*disk->serial == '\0') VIR_FREE(disk->serial); } if (virDomainDiskSetDriver(disk, "vz") < 0) goto cleanup; if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) { pret = PrlVmDevHd_GetDiskSize(prldisk, &size); prlsdkCheckRetGoto(pret, cleanup); /* from MiB to bytes */ disk->src->capacity = ((unsigned long long)size) << 20; } ret = 0; cleanup: VIR_FREE(buf); return ret; } static int prlsdkGetFSInfo(PRL_HANDLE prldisk, virDomainFSDefPtr fs) { char *buf = NULL; int ret = -1; char **matches = NULL; virURIPtr uri = NULL; fs->type = VIR_DOMAIN_FS_TYPE_FILE; fs->fsdriver = VIR_DOMAIN_FS_DRIVER_TYPE_PLOOP; fs->accessmode = VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH; fs->wrpolicy = VIR_DOMAIN_FS_WRPOLICY_DEFAULT; fs->format = VIR_STORAGE_FILE_PLOOP; fs->readonly = false; fs->symlinksResolved = false; if (!(buf = prlsdkGetStringParamVar(PrlVmDevHd_GetStorageURL, prldisk))) goto cleanup; if (!virStringIsEmpty(buf)) { if (!(uri = virURIParse(buf))) goto cleanup; if (STRNEQ("libvirt", uri->scheme)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unknown uri scheme: '%s'"), uri->scheme); goto cleanup; } if (!(matches = virStringSplitCount(uri->path, "/", 0, NULL)) || !matches[0]) { virReportError(VIR_ERR_INTERNAL_ERROR, _("splitting StorageUrl failed %s"), uri->path); goto cleanup; } if (!matches[1]) { virReportError(VIR_ERR_INTERNAL_ERROR, _("can't identify pool in uri %s "), uri->path); goto cleanup; } if (!matches[2]) { virReportError(VIR_ERR_INTERNAL_ERROR, _("can't identify volume in uri %s"), uri->path); goto cleanup; } fs->type = VIR_DOMAIN_FS_TYPE_VOLUME; if (VIR_ALLOC(fs->src->srcpool) < 0) goto cleanup; fs->src->srcpool->pool = g_strdup(matches[1]); fs->src->srcpool->volume = g_strdup(matches[2]); VIR_FREE(buf); } else { fs->type = VIR_DOMAIN_FS_TYPE_FILE; if (!(buf = prlsdkGetStringParamVar(PrlVmDev_GetImagePath, prldisk))) goto cleanup; fs->src->path = buf; buf = NULL; } if (!(buf = prlsdkGetStringParamVar(PrlVmDevHd_GetMountPoint, prldisk))) goto cleanup; fs->dst = buf; buf = NULL; ret = 0; cleanup: VIR_FREE(buf); virStringListFree(matches); return ret; } static int prlsdkAddDomainHardDisksInfo(vzDriverPtr driver, PRL_HANDLE sdkdom, virDomainDefPtr def) { PRL_RESULT pret; PRL_UINT32 hddCount; PRL_UINT32 i; PRL_HANDLE hdd = PRL_INVALID_HANDLE; virDomainDiskDefPtr disk = NULL; virDomainFSDefPtr fs = NULL; pret = PrlVmCfg_GetHardDisksCount(sdkdom, &hddCount); prlsdkCheckRetGoto(pret, error); for (i = 0; i < hddCount; ++i) { PRL_UINT32 emulatedType; pret = PrlVmCfg_GetHardDisk(sdkdom, i, &hdd); prlsdkCheckRetGoto(pret, error); pret = PrlVmDev_GetEmulatedType(hdd, &emulatedType); prlsdkCheckRetGoto(pret, error); if (IS_CT(def) && prlsdkInBootList(sdkdom, hdd)) { if (!(fs = virDomainFSDefNew(NULL))) goto error; if (prlsdkGetFSInfo(hdd, fs) < 0) goto error; if (virDomainFSInsert(def, fs) < 0) goto error; fs = NULL; PrlHandle_Free(hdd); hdd = PRL_INVALID_HANDLE; } else { if (!(disk = virDomainDiskDefNew(NULL))) goto error; if (prlsdkGetDiskInfo(driver, hdd, disk, false, IS_CT(def)) < 0) goto error; if (virDomainDiskInsert(def, disk) < 0) goto error; disk = NULL; PrlHandle_Free(hdd); hdd = PRL_INVALID_HANDLE; } } return 0; error: PrlHandle_Free(hdd); virDomainDiskDefFree(disk); virDomainFSDefFree(fs); return -1; } static int prlsdkAddDomainOpticalDisksInfo(vzDriverPtr driver, PRL_HANDLE sdkdom, virDomainDefPtr def) { PRL_RESULT pret; PRL_UINT32 cdromsCount; PRL_UINT32 i; PRL_HANDLE cdrom = PRL_INVALID_HANDLE; virDomainDiskDefPtr disk = NULL; pret = PrlVmCfg_GetOpticalDisksCount(sdkdom, &cdromsCount); prlsdkCheckRetGoto(pret, error); for (i = 0; i < cdromsCount; ++i) { pret = PrlVmCfg_GetOpticalDisk(sdkdom, i, &cdrom); prlsdkCheckRetGoto(pret, error); if (!(disk = virDomainDiskDefNew(NULL))) goto error; if (prlsdkGetDiskInfo(driver, cdrom, disk, true, IS_CT(def)) < 0) goto error; PrlHandle_Free(cdrom); cdrom = PRL_INVALID_HANDLE; if (virDomainDiskInsert(def, disk) < 0) goto error; } return 0; error: PrlHandle_Free(cdrom); virDomainDiskDefFree(disk); return -1; } static virNetDevIPAddrPtr prlsdkParseNetAddress(char *addr) { char *maskstr = NULL; int nbits; virSocketAddr mask; virNetDevIPAddrPtr ip = NULL, ret = NULL; if (!(maskstr = strchr(addr, '/'))) goto cleanup; *maskstr = '\0'; ++maskstr; if (VIR_ALLOC(ip) < 0) goto cleanup; if (virSocketAddrParse(&ip->address, addr, AF_UNSPEC) < 0) goto cleanup; if (virSocketAddrParse(&mask, maskstr, AF_UNSPEC) < 0) goto cleanup; if ((nbits = virSocketAddrGetNumNetmaskBits(&mask)) < 0) goto cleanup; ip->prefix = nbits; ret = g_steal_pointer(&ip); cleanup: if (!ret) VIR_WARN("cannot parse network address '%s'", addr); VIR_FREE(ip); VIR_FREE(addr); return ret; } static int prlsdkGetNetAddresses(PRL_HANDLE sdknet, virDomainNetDefPtr net) { int ret = -1; PRL_HANDLE addrlist = PRL_INVALID_HANDLE; PRL_UINT32 num; size_t i; PRL_RESULT pret; pret = PrlVmDevNet_GetNetAddresses(sdknet, &addrlist); prlsdkCheckRetGoto(pret, cleanup); PrlStrList_GetItemsCount(addrlist, &num); prlsdkCheckRetGoto(pret, cleanup); for (i = 0; i < num; ++i) { virNetDevIPAddrPtr ip = NULL; PRL_UINT32 buflen = 0; char *addr; pret = PrlStrList_GetItem(addrlist, i, NULL, &buflen); prlsdkCheckRetGoto(pret, cleanup); if (VIR_ALLOC_N(addr, buflen) < 0) goto cleanup; pret = PrlStrList_GetItem(addrlist, i, addr, &buflen); prlsdkCheckRetGoto(pret, cleanup); if (!(ip = prlsdkParseNetAddress(addr))) continue; if (VIR_APPEND_ELEMENT(net->guestIP.ips, net->guestIP.nips, ip) < 0) { VIR_FREE(ip); goto cleanup; } } ret = 0; cleanup: PrlHandle_Free(addrlist); return ret; } static int prlsdkGetRoutes(PRL_HANDLE sdknet, virDomainNetDefPtr net) { int ret = -1; char *gw = NULL; char *gw6 = NULL; virNetDevIPRoutePtr route = NULL; if (!(gw = prlsdkGetStringParamVar(PrlVmDevNet_GetDefaultGateway, sdknet))) goto cleanup; if (!(gw6 = prlsdkGetStringParamVar(PrlVmDevNet_GetDefaultGatewayIPv6, sdknet))) goto cleanup; if (*gw != '\0') { if (!(route = virNetDevIPRouteCreate(_("Domain interface"), "ipv4", VIR_SOCKET_ADDR_IPV4_ALL, NULL, gw, 0, true, 0, false))) goto cleanup; if (VIR_APPEND_ELEMENT(net->guestIP.routes, net->guestIP.nroutes, route) < 0) goto cleanup; } if (*gw6 != '\0') { if (!(route = virNetDevIPRouteCreate(_("Domain interface"), "ipv6", VIR_SOCKET_ADDR_IPV6_ALL, NULL, gw6, 0, true, 0, false))) goto cleanup; if (VIR_APPEND_ELEMENT(net->guestIP.routes, net->guestIP.nroutes, route) < 0) goto cleanup; } ret = 0; cleanup: virNetDevIPRouteFree(route); VIR_FREE(gw); VIR_FREE(gw6); return ret; } static int prlsdkGetNetInfo(PRL_HANDLE netAdapter, virDomainNetDefPtr net, bool isCt) { char macstr[VIR_MAC_STRING_BUFLEN]; PRL_UINT32 netAdapterIndex; PRL_UINT32 emulatedType; PRL_RESULT pret; PRL_BOOL isConnected, isMacFilter; /* use device name, shown by prlctl as target device * for identifying network adapter in virDomainDefineXML */ if (!(net->ifname = prlsdkGetStringParamVar(PrlVmDevNet_GetHostInterfaceName, netAdapter))) return -1; pret = PrlVmDev_GetIndex(netAdapter, &netAdapterIndex); prlsdkCheckRetExit(pret, -1); if (isCt && netAdapterIndex == (PRL_UINT32) -1) { /* venet devices don't have mac address and * always up */ net->linkstate = VIR_DOMAIN_NET_INTERFACE_LINK_STATE_UP; net->type = VIR_DOMAIN_NET_TYPE_NETWORK; net->data.network.name = g_strdup(PARALLELS_DOMAIN_ROUTED_NETWORK_NAME); return 0; } pret = prlsdkGetStringParamBuf(PrlVmDevNet_GetMacAddressCanonical, netAdapter, macstr, sizeof(macstr)); prlsdkCheckRetExit(pret, -1); if (virMacAddrParse(macstr, &net->mac) < 0) return -1; if (prlsdkGetNetAddresses(netAdapter, net) < 0) return -1; if (prlsdkGetRoutes(netAdapter, net) < 0) return -1; pret = PrlVmDev_GetEmulatedType(netAdapter, &emulatedType); prlsdkCheckRetExit(pret, -1); if (emulatedType == PNA_ROUTED) { net->type = VIR_DOMAIN_NET_TYPE_NETWORK; net->data.network.name = g_strdup(PARALLELS_DOMAIN_ROUTED_NETWORK_NAME); } else { char *netid = prlsdkGetStringParamVar(PrlVmDevNet_GetVirtualNetworkId, netAdapter); if (emulatedType == PNA_BRIDGE) { net->type = VIR_DOMAIN_NET_TYPE_BRIDGE; if (netid) net->data.bridge.brname = netid; } else { net->type = VIR_DOMAIN_NET_TYPE_NETWORK; if (netid) net->data.network.name = netid; } } if (!isCt) { PRL_VM_NET_ADAPTER_TYPE type; pret = PrlVmDevNet_GetAdapterType(netAdapter, &type); prlsdkCheckRetExit(pret, -1); switch ((int)type) { case PNT_RTL: net->model = VIR_DOMAIN_NET_MODEL_RTL8139; break; case PNT_E1000: net->model = VIR_DOMAIN_NET_MODEL_E1000; break; case PNT_VIRTIO: net->model = VIR_DOMAIN_NET_MODEL_VIRTIO; break; default: virReportError(VIR_ERR_INTERNAL_ERROR, _("Unknown adapter type: %X"), type); return -1; } } pret = PrlVmDev_IsConnected(netAdapter, &isConnected); prlsdkCheckRetExit(pret, -1); if (isConnected) net->linkstate = VIR_DOMAIN_NET_INTERFACE_LINK_STATE_UP; else net->linkstate = VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN; pret = PrlVmDevNet_IsPktFilterPreventMacSpoof(netAdapter, &isMacFilter); prlsdkCheckRetExit(pret, -1); net->trustGuestRxFilters = isMacFilter ? VIR_TRISTATE_BOOL_YES : VIR_TRISTATE_BOOL_NO; return 0; } static int prlsdkAddDomainNetInfo(PRL_HANDLE sdkdom, virDomainDefPtr def) { virDomainNetDefPtr net = NULL; PRL_RESULT ret; PRL_HANDLE netAdapter; PRL_UINT32 netAdaptersCount; PRL_UINT32 i; ret = PrlVmCfg_GetNetAdaptersCount(sdkdom, &netAdaptersCount); prlsdkCheckRetGoto(ret, error); for (i = 0; i < netAdaptersCount; ++i) { ret = PrlVmCfg_GetNetAdapter(sdkdom, i, &netAdapter); prlsdkCheckRetGoto(ret, error); if (VIR_ALLOC(net) < 0) goto error; if (prlsdkGetNetInfo(netAdapter, net, IS_CT(def)) < 0) goto error; PrlHandle_Free(netAdapter); netAdapter = PRL_INVALID_HANDLE; if (VIR_APPEND_ELEMENT(def->nets, def->nnets, net) < 0) goto error; } return 0; error: PrlHandle_Free(netAdapter); virDomainNetDefFree(net); return -1; } static int prlsdkGetSerialInfo(PRL_HANDLE serialPort, virDomainChrDefPtr chr) { PRL_RESULT pret; PRL_UINT32 serialPortIndex; PRL_UINT32 emulatedType; char *friendlyName = NULL; PRL_SERIAL_PORT_SOCKET_OPERATION_MODE socket_mode; char *uristr = NULL; virURIPtr uri = NULL; int ret = -1; chr->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL; pret = PrlVmDev_GetIndex(serialPort, &serialPortIndex); prlsdkCheckRetGoto(pret, cleanup); chr->target.port = serialPortIndex; pret = PrlVmDev_GetEmulatedType(serialPort, &emulatedType); prlsdkCheckRetGoto(pret, cleanup); if (!(friendlyName = prlsdkGetStringParamVar(PrlVmDev_GetFriendlyName, serialPort))) goto cleanup; pret = PrlVmDevSerial_GetSocketMode(serialPort, &socket_mode); prlsdkCheckRetGoto(pret, cleanup); switch (emulatedType) { case PDT_USE_OUTPUT_FILE: chr->source->type = VIR_DOMAIN_CHR_TYPE_FILE; chr->source->data.file.path = friendlyName; friendlyName = NULL; break; case PDT_USE_SERIAL_PORT_SOCKET_MODE: chr->source->type = VIR_DOMAIN_CHR_TYPE_UNIX; chr->source->data.nix.path = friendlyName; chr->source->data.nix.listen = socket_mode == PSP_SERIAL_SOCKET_SERVER; friendlyName = NULL; break; case PDT_USE_REAL_DEVICE: chr->source->type = VIR_DOMAIN_CHR_TYPE_DEV; chr->source->data.file.path = friendlyName; friendlyName = NULL; break; case PDT_USE_TCP: chr->source->type = VIR_DOMAIN_CHR_TYPE_TCP; uristr = g_strdup_printf("tcp://%s", friendlyName); if (!(uri = virURIParse(uristr))) goto cleanup; chr->source->data.tcp.host = g_strdup(uri->server); chr->source->data.tcp.service = g_strdup_printf("%d", uri->port); chr->source->data.tcp.listen = socket_mode == PSP_SERIAL_SOCKET_SERVER; break; case PDT_USE_UDP: chr->source->type = VIR_DOMAIN_CHR_TYPE_UDP; uristr = g_strdup_printf("udp://%s", friendlyName); if (!(uri = virURIParse(uristr))) goto cleanup; chr->source->data.udp.bindHost = g_strdup(uri->server); chr->source->data.udp.bindService = g_strdup_printf("%d", uri->port); chr->source->data.udp.connectHost = g_strdup(uri->server); chr->source->data.udp.connectService = g_strdup_printf("%d", uri->port); break; default: virReportError(VIR_ERR_INTERNAL_ERROR, _("Unknown serial type: %X"), emulatedType); goto cleanup; break; } ret = 0; cleanup: VIR_FREE(friendlyName); VIR_FREE(uristr); virURIFree(uri); return ret; } static int prlsdkAddSerialInfo(PRL_HANDLE sdkdom, virDomainChrDefPtr **serials, size_t *nserials) { PRL_RESULT ret; PRL_HANDLE serialPort; PRL_UINT32 serialPortsCount; PRL_UINT32 i; virDomainChrDefPtr chr = NULL; ret = PrlVmCfg_GetSerialPortsCount(sdkdom, &serialPortsCount); prlsdkCheckRetGoto(ret, cleanup); for (i = 0; i < serialPortsCount; ++i) { ret = PrlVmCfg_GetSerialPort(sdkdom, i, &serialPort); prlsdkCheckRetGoto(ret, cleanup); if (!(chr = virDomainChrDefNew(NULL))) goto cleanup; if (prlsdkGetSerialInfo(serialPort, chr)) goto cleanup; PrlHandle_Free(serialPort); serialPort = PRL_INVALID_HANDLE; if (VIR_APPEND_ELEMENT(*serials, *nserials, chr) < 0) goto cleanup; } return 0; cleanup: PrlHandle_Free(serialPort); virDomainChrDefFree(chr); return -1; } static int prlsdkAddDomainHardware(vzDriverPtr driver, PRL_HANDLE sdkdom, virDomainDefPtr def, virDomainXMLOptionPtr xmlopt) { if (IS_CT(def)) { if (prlsdkAddDomainVideoInfoCt(def, xmlopt) < 0) return -1; } else { if (prlsdkAddDomainVideoInfoVm(sdkdom, def) < 0) return -1; } if (prlsdkAddDomainHardDisksInfo(driver, sdkdom, def) < 0) return -1; if (prlsdkAddDomainOpticalDisksInfo(driver, sdkdom, def) < 0) return -1; if (prlsdkAddDomainNetInfo(sdkdom, def) < 0) return -1; if (prlsdkAddSerialInfo(sdkdom, &def->serials, &def->nserials) < 0) return -1; return 0; } static int prlsdkAddVNCInfo(PRL_HANDLE sdkdom, virDomainDefPtr def) { virDomainGraphicsDefPtr gr = NULL; PRL_VM_REMOTE_DISPLAY_MODE vncMode; PRL_UINT32 port; PRL_RESULT pret; char *passwd = NULL; pret = PrlVmCfg_GetVNCMode(sdkdom, &vncMode); prlsdkCheckRetGoto(pret, error); if (vncMode == PRD_DISABLED) return 0; if (VIR_ALLOC(gr) < 0) goto error; if (!(passwd = prlsdkGetStringParamVar(PrlVmCfg_GetVNCPassword, sdkdom))) goto error; if (*passwd != '\0') { gr->data.vnc.auth.passwd = passwd; passwd = NULL; } pret = PrlVmCfg_GetVNCPort(sdkdom, &port); prlsdkCheckRetGoto(pret, error); gr->data.vnc.autoport = (vncMode == PRD_AUTO); gr->type = VIR_DOMAIN_GRAPHICS_TYPE_VNC; gr->data.vnc.port = port; if (VIR_ALLOC(gr->listens) < 0) goto error; gr->nListens = 1; if (!(gr->listens[0].address = prlsdkGetStringParamVar(PrlVmCfg_GetVNCHostName, sdkdom))) goto error; gr->listens[0].type = VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS; if (VIR_APPEND_ELEMENT(def->graphics, def->ngraphics, gr) < 0) goto error; return 0; error: virDomainGraphicsDefFree(gr); VIR_FREE(passwd); return -1; } static void prlsdkConvertDomainState(VIRTUAL_MACHINE_STATE domainState, PRL_UINT32 envId, virDomainObjPtr dom) { switch (domainState) { case VMS_STOPPED: case VMS_MOUNTED: virDomainObjSetState(dom, VIR_DOMAIN_SHUTOFF, VIR_DOMAIN_SHUTOFF_SHUTDOWN); dom->def->id = -1; break; case VMS_STARTING: case VMS_COMPACTING: case VMS_RESETTING: case VMS_PAUSING: case VMS_RECONNECTING: case VMS_RUNNING: virDomainObjSetState(dom, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_BOOTED); dom->def->id = envId; break; case VMS_PAUSED: virDomainObjSetState(dom, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_USER); dom->def->id = envId; break; case VMS_SUSPENDED: case VMS_DELETING_STATE: case VMS_SUSPENDING_SYNC: virDomainObjSetState(dom, VIR_DOMAIN_SHUTOFF, VIR_DOMAIN_SHUTOFF_SAVED); dom->def->id = -1; break; case VMS_STOPPING: virDomainObjSetState(dom, VIR_DOMAIN_SHUTDOWN, VIR_DOMAIN_SHUTDOWN_USER); dom->def->id = envId; break; case VMS_SNAPSHOTING: virDomainObjSetState(dom, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_SNAPSHOT); dom->def->id = envId; break; case VMS_MIGRATING: virDomainObjSetState(dom, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_MIGRATION); dom->def->id = envId; break; case VMS_SUSPENDING: virDomainObjSetState(dom, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_SAVE); dom->def->id = envId; break; case VMS_RESTORING: virDomainObjSetState(dom, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_RESTORED); dom->def->id = envId; break; case VMS_CONTINUING: virDomainObjSetState(dom, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_UNPAUSED); dom->def->id = envId; break; case VMS_RESUMING: virDomainObjSetState(dom, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_RESTORED); dom->def->id = envId; break; case VMS_UNKNOWN: default: virDomainObjSetState(dom, VIR_DOMAIN_NOSTATE, VIR_DOMAIN_NOSTATE_UNKNOWN); dom->def->id = -1; break; } } static int prlsdkConvertCpuInfo(PRL_HANDLE sdkdom, virDomainDefPtr def, virDomainXMLOptionPtr xmlopt) { g_autofree char *buf = NULL; int hostcpus; PRL_UINT32 cpuCount; PRL_RESULT pret; if ((hostcpus = virHostCPUGetCount()) < 0) return -1; /* get number of CPUs */ pret = PrlVmCfg_GetCpuCount(sdkdom, &cpuCount); prlsdkCheckRetExit(pret, -1); if (cpuCount > hostcpus) cpuCount = hostcpus; if (virDomainDefSetVcpusMax(def, cpuCount, xmlopt) < 0) return -1; if (virDomainDefSetVcpus(def, cpuCount) < 0) return -1; if (!(buf = prlsdkGetStringParamVar(PrlVmCfg_GetCpuMask, sdkdom))) return -1; if (strlen(buf) == 0) { if (!(def->cpumask = virBitmapNew(hostcpus))) return -1; virBitmapSetAll(def->cpumask); } else { if (virBitmapParse(buf, &def->cpumask, hostcpus) < 0) return -1; } return 0; } static int prlsdkConvertDomainType(PRL_HANDLE sdkdom, virDomainDefPtr def) { PRL_VM_TYPE domainType; PRL_RESULT pret; pret = PrlVmCfg_GetVmType(sdkdom, &domainType); prlsdkCheckRetExit(pret, -1); switch (domainType) { case PVT_VM: def->os.type = VIR_DOMAIN_OSTYPE_HVM; break; case PVT_CT: def->os.type = VIR_DOMAIN_OSTYPE_EXE; def->os.init = g_strdup("/sbin/init"); break; default: virReportError(VIR_ERR_INTERNAL_ERROR, _("Unknown domain type: %X"), domainType); return -1; } return 0; } static int prlsdkConvertCpuMode(PRL_HANDLE sdkdom, virDomainDefPtr def) { PRL_RESULT pret; PRL_CPU_MODE cpuMode; pret = PrlVmCfg_GetCpuMode(sdkdom, &cpuMode); prlsdkCheckRetExit(pret, -1); switch (cpuMode) { case PCM_CPU_MODE_32: def->os.arch = VIR_ARCH_I686; break; case PCM_CPU_MODE_64: def->os.arch = VIR_ARCH_X86_64; break; default: virReportError(VIR_ERR_INTERNAL_ERROR, _("Unknown CPU mode: %X"), cpuMode); return -1; } return 0; } static PRL_HANDLE prlsdkGetDevByDevIndex(PRL_HANDLE sdkdom, PRL_DEVICE_TYPE type, PRL_UINT32 devIndex) { PRL_RESULT pret; PRL_UINT32 index, num; PRL_HANDLE dev = PRL_INVALID_HANDLE; size_t i; pret = PrlVmCfg_GetDevsCountByType(sdkdom, type, &num); prlsdkCheckRetGoto(pret, error); for (i = 0; i < num; ++i) { pret = PrlVmCfg_GetDevByType(sdkdom, type, i, &dev); prlsdkCheckRetGoto(pret, error); pret = PrlVmDev_GetIndex(dev, &index); prlsdkCheckRetGoto(pret, error); if (index == devIndex) break; PrlHandle_Free(dev); dev = PRL_INVALID_HANDLE; } return dev; error: PrlHandle_Free(dev); return PRL_INVALID_HANDLE; } static virDomainDiskDefPtr virFindDiskBootIndex(virDomainDefPtr def, virDomainDiskDevice type, int index) { size_t i; int c = 0; for (i = 0; i < def->ndisks; ++i) { if (def->disks[i]->device != type) continue; if (c == index) return def->disks[i]; ++c; } return NULL; } static bool prlsdkInBootList(PRL_HANDLE sdkdom, PRL_HANDLE sdktargetdev) { bool ret = false; PRL_RESULT pret; PRL_UINT32 bootNum; PRL_HANDLE bootDev = PRL_INVALID_HANDLE; PRL_BOOL inUse; PRL_DEVICE_TYPE sdkType, targetType; PRL_UINT32 sdkIndex, targetIndex; size_t i; pret = PrlVmDev_GetType(sdktargetdev, &targetType); prlsdkCheckRetExit(pret, false); pret = PrlVmDev_GetIndex(sdktargetdev, &targetIndex); prlsdkCheckRetExit(pret, false); pret = PrlVmCfg_GetBootDevCount(sdkdom, &bootNum); prlsdkCheckRetExit(pret, false); for (i = 0; i < bootNum; ++i) { pret = PrlVmCfg_GetBootDev(sdkdom, i, &bootDev); prlsdkCheckRetGoto(pret, cleanup); pret = PrlBootDev_IsInUse(bootDev, &inUse); prlsdkCheckRetGoto(pret, cleanup); if (!inUse) { PrlHandle_Free(bootDev); bootDev = PRL_INVALID_HANDLE; continue; } pret = PrlBootDev_GetType(bootDev, &sdkType); prlsdkCheckRetGoto(pret, cleanup); pret = PrlBootDev_GetIndex(bootDev, &sdkIndex); prlsdkCheckRetGoto(pret, cleanup); PrlHandle_Free(bootDev); bootDev = PRL_INVALID_HANDLE; if (sdkIndex == targetIndex && sdkType == targetType) { ret = true; break; } } cleanup: PrlHandle_Free(bootDev); return ret; } static int prlsdkBootOrderCheck(PRL_HANDLE sdkdom, PRL_DEVICE_TYPE sdkType, int sdkIndex, virDomainDefPtr def, int bootIndex) { char *sdkName = NULL; PRL_HANDLE dev = PRL_INVALID_HANDLE; virDomainDiskDefPtr disk; virDomainDiskDevice device; int bus; char *dst = NULL; int ret = -1; dev = prlsdkGetDevByDevIndex(sdkdom, sdkType, sdkIndex); if (dev == PRL_INVALID_HANDLE) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Can't find boot device of type: %d, device index: %d"), sdkType, sdkIndex); return -1; } switch ((int)sdkType) { case PDE_OPTICAL_DISK: case PDE_HARD_DISK: switch ((int)sdkType) { case PDE_OPTICAL_DISK: device = VIR_DOMAIN_DISK_DEVICE_CDROM; break; case PDE_HARD_DISK: device = VIR_DOMAIN_DISK_DEVICE_DISK; break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unsupported disk type %d"), sdkType); goto cleanup; } if (!(disk = virFindDiskBootIndex(def, device, bootIndex))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Can't find boot device of type: %s, index: %d"), virDomainDiskDeviceTypeToString(device), bootIndex); goto cleanup; } if (prlsdkGetDiskId(dev, &bus, &dst) < 0) goto cleanup; if (!(bus == disk->bus && STREQ(disk->dst, dst))) VIR_WARN("Unrepresentable boot order configuration"); break; case PDE_GENERIC_NETWORK_ADAPTER: if (!(sdkName = prlsdkGetStringParamVar(PrlVmDevNet_GetHostInterfaceName, dev))) goto cleanup; if (bootIndex >= def->nnets) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Can't find network boot device for index: %d"), bootIndex); goto cleanup; } if (STRNEQ(sdkName, def->nets[bootIndex]->ifname)) VIR_WARN("Unrepresentable boot order configuration"); break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unexpected device type %d"), sdkType); goto cleanup; } ret = 0; cleanup: VIR_FREE(sdkName); PrlHandle_Free(dev); VIR_FREE(dst); return ret; } static int prlsdkConvertBootOrderVm(PRL_HANDLE sdkdom, virDomainDefPtr def) { int ret = -1; PRL_RESULT pret; PRL_UINT32 bootNum; PRL_HANDLE bootDev = PRL_INVALID_HANDLE; PRL_BOOL inUse; PRL_DEVICE_TYPE sdkType; virDomainBootOrder type; PRL_UINT32 prevBootIndex = 0, bootIndex, sdkIndex; int bootUsage[VIR_DOMAIN_BOOT_LAST] = { 0 }; size_t i; pret = PrlVmCfg_GetBootDevCount(sdkdom, &bootNum); prlsdkCheckRetExit(pret, -1); def->os.nBootDevs = 0; if (bootNum > VIR_DOMAIN_MAX_BOOT_DEVS) { bootNum = VIR_DOMAIN_MAX_BOOT_DEVS; VIR_WARN("Too many boot devices"); } for (i = 0; i < bootNum; ++i) { pret = PrlVmCfg_GetBootDev(sdkdom, i, &bootDev); prlsdkCheckRetGoto(pret, cleanup); pret = PrlBootDev_IsInUse(bootDev, &inUse); prlsdkCheckRetGoto(pret, cleanup); if (!inUse) continue; pret = PrlBootDev_GetSequenceIndex(bootDev, &bootIndex); prlsdkCheckRetGoto(pret, cleanup); /* bootIndex is started from 1 */ if (bootIndex <= prevBootIndex) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Unsupported boot order configuration")); goto cleanup; } prevBootIndex = bootIndex; pret = PrlBootDev_GetType(bootDev, &sdkType); prlsdkCheckRetGoto(pret, cleanup); if (sdkType == PDE_FLOPPY_DISK) { VIR_WARN("Skipping floppy from boot order."); continue; } switch ((int)sdkType) { case PDE_OPTICAL_DISK: type = VIR_DOMAIN_BOOT_CDROM; break; case PDE_HARD_DISK: type = VIR_DOMAIN_BOOT_DISK; break; case PDE_GENERIC_NETWORK_ADAPTER: type = VIR_DOMAIN_BOOT_NET; break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unexpected boot device type %i"), sdkType); goto cleanup; } pret = PrlBootDev_GetIndex(bootDev, &sdkIndex); prlsdkCheckRetGoto(pret, cleanup); if (prlsdkBootOrderCheck(sdkdom, sdkType, sdkIndex, def, bootUsage[type]) < 0) goto cleanup; bootUsage[type]++; def->os.bootDevs[def->os.nBootDevs++] = type; PrlHandle_Free(bootDev); bootDev = PRL_INVALID_HANDLE; } ret = 0; cleanup: PrlHandle_Free(bootDev); return ret; } /* if dom is NULL adds new domain into domain list * if dom not NULL updates given locked dom object. * * Returned object is locked and referenced. */ static virDomainObjPtr prlsdkLoadDomain(vzDriverPtr driver, PRL_HANDLE sdkdom, virDomainObjPtr dom) { virDomainDefPtr def = NULL; vzDomObjPtr pdom = NULL; VIRTUAL_MACHINE_STATE domainState; PRL_RESULT pret; PRL_UINT32 ram; PRL_UINT32 envId; PRL_VM_AUTOSTART_OPTION autostart; PRL_HANDLE job; char uuidstr[VIR_UUID_STRING_BRACED_BUFLEN]; if (!(def = virDomainDefNew())) goto error; if (!(def->name = prlsdkGetStringParamVar(PrlVmCfg_GetName, sdkdom))) goto error; pret = prlsdkGetStringParamBuf(PrlVmCfg_GetUuid, sdkdom, uuidstr, sizeof(uuidstr)); prlsdkCheckRetGoto(pret, error); if (prlsdkUUIDParse(uuidstr, def->uuid) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Domain UUID is malformed or empty")); goto error; } def->virtType = VIR_DOMAIN_VIRT_VZ; def->onReboot = VIR_DOMAIN_LIFECYCLE_ACTION_RESTART; def->onPoweroff = VIR_DOMAIN_LIFECYCLE_ACTION_DESTROY; def->onCrash = VIR_DOMAIN_LIFECYCLE_ACTION_DESTROY; /* get RAM parameters */ pret = PrlVmCfg_GetRamSize(sdkdom, &ram); prlsdkCheckRetGoto(pret, error); virDomainDefSetMemoryTotal(def, ram << 10); /* RAM size obtained in Mbytes, convert to Kbytes */ def->mem.cur_balloon = ram << 10; if (prlsdkConvertCpuInfo(sdkdom, def, driver->xmlopt) < 0) goto error; if (prlsdkConvertCpuMode(sdkdom, def) < 0) goto error; if (prlsdkConvertDomainType(sdkdom, def) < 0) goto error; if (prlsdkAddVNCInfo(sdkdom, def) < 0) goto error; /* depends on prlsdkAddVNCInfo */ if (prlsdkAddDomainHardware(driver, sdkdom, def, driver->xmlopt) < 0) goto error; /* depends on prlsdkAddDomainHardware */ if (!IS_CT(def) && prlsdkConvertBootOrderVm(sdkdom, def) < 0) goto error; pret = PrlVmCfg_GetEnvId(sdkdom, &envId); prlsdkCheckRetGoto(pret, error); pret = PrlVmCfg_GetAutoStart(sdkdom, &autostart); prlsdkCheckRetGoto(pret, error); if (autostart != PAO_VM_START_ON_LOAD && autostart != PAO_VM_START_MANUAL) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unknown autostart mode: %X"), autostart); goto error; } if (prlsdkGetDomainState(dom, sdkdom, &domainState) < 0) goto error; if (!IS_CT(def) && virDomainDefAddImplicitDevices(def, driver->xmlopt) < 0) goto error; if (def->ngraphics > 0) { int bus = IS_CT(def) ? VIR_DOMAIN_INPUT_BUS_PARALLELS : VIR_DOMAIN_INPUT_BUS_PS2; if (virDomainDefMaybeAddInput(def, VIR_DOMAIN_INPUT_TYPE_MOUSE, bus) < 0) goto error; if (virDomainDefMaybeAddInput(def, VIR_DOMAIN_INPUT_TYPE_KBD, bus) < 0) goto error; } if (!dom) { virDomainObjPtr olddom = NULL; job = PrlVm_SubscribeToPerfStats(sdkdom, NULL); if (PRL_FAILED(waitJob(job))) goto error; virObjectLock(driver); if (!(olddom = virDomainObjListFindByUUID(driver->domains, def->uuid))) dom = virDomainObjListAdd(driver->domains, def, driver->xmlopt, 0, NULL); virObjectUnlock(driver); if (olddom) { virDomainDefFree(def); return olddom; } else if (!dom) { goto error; } pdom = dom->privateData; pdom->sdkdom = sdkdom; PrlHandle_AddRef(sdkdom); dom->persistent = 1; } else { /* assign new virDomainDef without any checks * we can't use virDomainObjAssignDef, because it checks * for state and domain name */ virDomainDefFree(dom->def); dom->def = def; } pdom = dom->privateData; pdom->id = envId; prlsdkConvertDomainState(domainState, envId, dom); if (autostart == PAO_VM_START_ON_LOAD) dom->autostart = 1; else dom->autostart = 0; return dom; error: virDomainDefFree(def); return NULL; } int prlsdkLoadDomains(vzDriverPtr driver) { PRL_HANDLE job = PRL_INVALID_HANDLE; PRL_HANDLE result; PRL_HANDLE sdkdom = PRL_INVALID_HANDLE; PRL_UINT32 paramsCount; PRL_RESULT pret; size_t i = 0; virDomainObjPtr dom; job = PrlSrv_GetVmListEx(driver->server, PVTF_VM | PVTF_CT); if (PRL_FAILED(getJobResult(job, &result))) return -1; pret = PrlResult_GetParamsCount(result, ¶msCount); prlsdkCheckRetGoto(pret, error); for (i = 0; i < paramsCount; i++) { pret = PrlResult_GetParamByIndex(result, i, &sdkdom); prlsdkCheckRetGoto(pret, error); dom = prlsdkLoadDomain(driver, sdkdom, NULL); virDomainObjEndAPI(&dom); PrlHandle_Free(sdkdom); sdkdom = PRL_INVALID_HANDLE; } PrlHandle_Free(result); return 0; error: PrlHandle_Free(sdkdom); PrlHandle_Free(result); return -1; } virDomainObjPtr prlsdkAddDomainByUUID(vzDriverPtr driver, const unsigned char *uuid) { PRL_HANDLE sdkdom; virDomainObjPtr dom; sdkdom = prlsdkSdkDomainLookupByUUID(driver, uuid); if (sdkdom == PRL_INVALID_HANDLE) return NULL; dom = prlsdkLoadDomain(driver, sdkdom, NULL); PrlHandle_Free(sdkdom); return dom; } virDomainObjPtr prlsdkAddDomainByName(vzDriverPtr driver, const char *name) { PRL_HANDLE sdkdom; virDomainObjPtr dom; sdkdom = prlsdkSdkDomainLookupByName(driver, name); if (sdkdom == PRL_INVALID_HANDLE) return NULL; dom = prlsdkLoadDomain(driver, sdkdom, NULL); PrlHandle_Free(sdkdom); return dom; } int prlsdkUpdateDomain(vzDriverPtr driver, virDomainObjPtr dom) { PRL_HANDLE job; vzDomObjPtr pdom = dom->privateData; job = PrlVm_RefreshConfig(pdom->sdkdom); if (waitDomainJob(job, dom)) return -1; return prlsdkLoadDomain(driver, pdom->sdkdom, dom) ? 0 : -1; } static void prlsdkSendEvent(vzDriverPtr driver, virDomainObjPtr dom, virDomainEventType lvEventType, int lvEventTypeDetails) { virObjectEventPtr event; event = virDomainEventLifecycleNewFromObj(dom, lvEventType, lvEventTypeDetails); virObjectEventStateQueue(driver->domainEventState, event); } static void prlsdkNewStateToEvent(VIRTUAL_MACHINE_STATE domainState, virDomainEventType *lvEventType, int *lvEventTypeDetails) { /* We skip all intermediate states here, because * libvirt doesn't have corresponding event types for * them */ switch ((int)domainState) { case VMS_STOPPED: case VMS_MOUNTED: *lvEventType = VIR_DOMAIN_EVENT_STOPPED; *lvEventTypeDetails = VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN; break; case VMS_RUNNING: *lvEventType = VIR_DOMAIN_EVENT_STARTED; *lvEventTypeDetails = VIR_DOMAIN_EVENT_STARTED_BOOTED; break; case VMS_PAUSED: *lvEventType = VIR_DOMAIN_EVENT_SUSPENDED; *lvEventTypeDetails = VIR_DOMAIN_EVENT_SUSPENDED_PAUSED; break; case VMS_SUSPENDED: *lvEventType = VIR_DOMAIN_EVENT_STOPPED; *lvEventTypeDetails = VIR_DOMAIN_EVENT_STOPPED_SAVED; break; default: VIR_DEBUG("Skip sending event about changing state to %X", domainState); break; } } static void prlsdkHandleVmStateEvent(vzDriverPtr driver, PRL_HANDLE prlEvent, unsigned char *uuid) { PRL_RESULT pret = PRL_ERR_FAILURE; PRL_HANDLE eventParam = PRL_INVALID_HANDLE; PRL_INT32 domainState; virDomainObjPtr dom = NULL; vzDomObjPtr pdom; virDomainEventType lvEventType = 0; int lvEventTypeDetails = 0; dom = virDomainObjListFindByUUID(driver->domains, uuid); if (dom == NULL) return; pret = PrlEvent_GetParamByName(prlEvent, "vminfo_vm_state", &eventParam); prlsdkCheckRetGoto(pret, cleanup); pret = PrlEvtPrm_ToInt32(eventParam, &domainState); prlsdkCheckRetGoto(pret, cleanup); pdom = dom->privateData; prlsdkConvertDomainState(domainState, pdom->id, dom); prlsdkNewStateToEvent(domainState, &lvEventType, &lvEventTypeDetails); prlsdkSendEvent(driver, dom, lvEventType, lvEventTypeDetails); cleanup: PrlHandle_Free(eventParam); virDomainObjEndAPI(&dom); return; } static void prlsdkHandleVmConfigEvent(vzDriverPtr driver, unsigned char *uuid) { virDomainObjPtr dom = NULL; bool job = false; dom = virDomainObjListFindByUUID(driver->domains, uuid); if (dom == NULL) return; if (vzDomainObjBeginJob(dom) < 0) goto cleanup; job = true; if (dom->removing) goto cleanup; if (prlsdkUpdateDomain(driver, dom) < 0) goto cleanup; prlsdkSendEvent(driver, dom, VIR_DOMAIN_EVENT_DEFINED, VIR_DOMAIN_EVENT_DEFINED_UPDATED); cleanup: if (job) vzDomainObjEndJob(dom); virDomainObjEndAPI(&dom); return; } static void prlsdkHandleVmAddedEvent(vzDriverPtr driver, unsigned char *uuid) { virDomainObjPtr dom = NULL; if (!(dom = virDomainObjListFindByUUID(driver->domains, uuid)) && !(dom = prlsdkAddDomainByUUID(driver, uuid))) goto cleanup; prlsdkSendEvent(driver, dom, VIR_DOMAIN_EVENT_DEFINED, VIR_DOMAIN_EVENT_DEFINED_ADDED); cleanup: virDomainObjEndAPI(&dom); return; } static void prlsdkHandleVmRemovedEvent(vzDriverPtr driver, unsigned char *uuid) { virDomainObjPtr dom = NULL; dom = virDomainObjListFindByUUID(driver->domains, uuid); /* domain was removed from the list from the libvirt * API function in current connection */ if (dom == NULL) return; prlsdkSendEvent(driver, dom, VIR_DOMAIN_EVENT_UNDEFINED, VIR_DOMAIN_EVENT_UNDEFINED_REMOVED); virDomainObjListRemove(driver->domains, dom); virDomainObjEndAPI(&dom); return; } static void prlsdkHandlePerfEvent(vzDriverPtr driver, PRL_HANDLE event, unsigned char *uuid) { virDomainObjPtr dom = NULL; vzDomObjPtr privdom = NULL; if (!(dom = virDomainObjListFindByUUID(driver->domains, uuid))) { PrlHandle_Free(event); return; } privdom = dom->privateData; PrlHandle_Free(privdom->stats); privdom->stats = event; virDomainObjEndAPI(&dom); } static void prlsdkHandleMigrationProgress(vzDriverPtr driver, PRL_HANDLE event, unsigned char *uuid) { virDomainObjPtr dom = NULL; vzDomObjPtr privdom = NULL; PRL_UINT32 progress; PRL_HANDLE param = PRL_INVALID_HANDLE; PRL_RESULT pret; if (!(dom = virDomainObjListFindByUUID(driver->domains, uuid))) return; pret = PrlEvent_GetParam(event, 0, ¶m); prlsdkCheckRetGoto(pret, cleanup); pret = PrlEvtPrm_ToUint32(param, &progress); prlsdkCheckRetGoto(pret, cleanup); privdom = dom->privateData; privdom->job.progress = progress; cleanup: PrlHandle_Free(param); virDomainObjEndAPI(&dom); } static PRL_RESULT prlsdkEventsHandler(PRL_HANDLE prlEvent, PRL_VOID_PTR opaque) { vzDriverPtr driver = opaque; PRL_RESULT pret = PRL_ERR_FAILURE; PRL_HANDLE_TYPE handleType; char uuidstr[VIR_UUID_STRING_BRACED_BUFLEN]; unsigned char uuid[VIR_UUID_BUFLEN]; PRL_EVENT_TYPE prlEventType; pret = PrlHandle_GetType(prlEvent, &handleType); prlsdkCheckRetGoto(pret, cleanup); /* Currently, there is no need to handle anything but events */ if (handleType != PHT_EVENT) goto cleanup; if (driver == NULL) goto cleanup; pret = prlsdkGetStringParamBuf(PrlEvent_GetIssuerId, prlEvent, uuidstr, sizeof(uuidstr)); prlsdkCheckRetGoto(pret, cleanup); pret = PrlEvent_GetType(prlEvent, &prlEventType); prlsdkCheckRetGoto(pret, cleanup); if (prlsdkUUIDParse(uuidstr, uuid) < 0) { VIR_DEBUG("Skipping event type %d", prlEventType); goto cleanup; } switch ((int)prlEventType) { case PET_DSP_EVT_VM_STATE_CHANGED: prlsdkHandleVmStateEvent(driver, prlEvent, uuid); break; case PET_DSP_EVT_VM_CONFIG_CHANGED: prlsdkHandleVmConfigEvent(driver, uuid); break; case PET_DSP_EVT_VM_CREATED: case PET_DSP_EVT_VM_ADDED: prlsdkHandleVmAddedEvent(driver, uuid); break; case PET_DSP_EVT_VM_DELETED: case PET_DSP_EVT_VM_UNREGISTERED: prlsdkHandleVmRemovedEvent(driver, uuid); break; case PET_DSP_EVT_VM_PERFSTATS: prlsdkHandlePerfEvent(driver, prlEvent, uuid); /* above function takes own of event */ prlEvent = PRL_INVALID_HANDLE; break; case PET_DSP_EVT_DISP_CONNECTION_CLOSED: vzDestroyDriverConnection(); break; case PET_DSP_EVT_VM_MIGRATE_PROGRESS_CHANGED: prlsdkHandleMigrationProgress(driver, prlEvent, uuid); break; default: VIR_DEBUG("Skipping event of type %d", prlEventType); } cleanup: PrlHandle_Free(prlEvent); return PRL_ERR_SUCCESS; } int prlsdkStart(virDomainObjPtr dom) { PRL_HANDLE job = PRL_INVALID_HANDLE; vzDomObjPtr privdom = dom->privateData; PRL_RESULT pret; job = PrlVm_StartEx(privdom->sdkdom, PSM_VM_START, 0); if (PRL_FAILED(pret = waitDomainJob(job, dom))) { prlsdkConvertError(pret); return -1; } return 0; } int prlsdkKill(virDomainObjPtr dom) { PRL_HANDLE job = PRL_INVALID_HANDLE; vzDomObjPtr privdom = dom->privateData; PRL_RESULT pret; job = PrlVm_StopEx(privdom->sdkdom, PSM_KILL, 0); if (PRL_FAILED(pret = waitDomainJob(job, dom))) { prlsdkConvertError(pret); return -1; } return 0; } int prlsdkStop(virDomainObjPtr dom) { PRL_HANDLE job = PRL_INVALID_HANDLE; vzDomObjPtr privdom = dom->privateData; PRL_RESULT pret; job = PrlVm_StopEx(privdom->sdkdom, PSM_SHUTDOWN, 0); if (PRL_FAILED(pret = waitDomainJob(job, dom))) { prlsdkConvertError(pret); return -1; } return 0; } int prlsdkPause(virDomainObjPtr dom) { PRL_HANDLE job = PRL_INVALID_HANDLE; vzDomObjPtr privdom = dom->privateData; PRL_RESULT pret; job = PrlVm_Pause(privdom->sdkdom, false); if (PRL_FAILED(pret = waitDomainJob(job, dom))) { prlsdkConvertError(pret); return -1; } return 0; } int prlsdkResume(virDomainObjPtr dom) { PRL_HANDLE job = PRL_INVALID_HANDLE; vzDomObjPtr privdom = dom->privateData; PRL_RESULT pret; job = PrlVm_Resume(privdom->sdkdom); if (PRL_FAILED(pret = waitDomainJob(job, dom))) { prlsdkConvertError(pret); return -1; } return 0; } int prlsdkSuspend(virDomainObjPtr dom) { PRL_HANDLE job = PRL_INVALID_HANDLE; vzDomObjPtr privdom = dom->privateData; PRL_RESULT pret; job = PrlVm_Suspend(privdom->sdkdom); if (PRL_FAILED(pret = waitDomainJob(job, dom))) { prlsdkConvertError(pret); return -1; } return 0; } int prlsdkRestart(virDomainObjPtr dom) { PRL_HANDLE job = PRL_INVALID_HANDLE; vzDomObjPtr privdom = dom->privateData; PRL_RESULT pret; job = PrlVm_Restart(privdom->sdkdom); if (PRL_FAILED(pret = waitDomainJob(job, dom))) { prlsdkConvertError(pret); return -1; } return 0; } int prlsdkReset(virDomainObjPtr dom) { PRL_HANDLE job = PRL_INVALID_HANDLE; vzDomObjPtr privdom = dom->privateData; PRL_RESULT pret; job = PrlVm_Reset(privdom->sdkdom); if (PRL_FAILED(pret = waitDomainJob(job, dom))) { prlsdkConvertError(pret); return -1; } return 0; } static void prlsdkConvertError(PRL_RESULT pret) { virErrorNumber virerr; switch (pret) { case PRL_ERR_DISP_VM_IS_NOT_STARTED: case PRL_ERR_DISP_VM_IS_NOT_STOPPED: case PRL_ERR_INVALID_ACTION_REQUESTED: case PRL_ERR_UNIMPLEMENTED: virerr = VIR_ERR_OPERATION_INVALID; break; default: virerr = VIR_ERR_OPERATION_FAILED; } virResetLastError(); virReportError(virerr, "%s", _("Can't change domain state.")); } static int prlsdkCheckUnsupportedParams(PRL_HANDLE sdkdom, virDomainDefPtr def) { size_t i; PRL_VM_TYPE vmType; PRL_RESULT pret; virDomainNumatuneMemMode memMode; int bus = IS_CT(def) ? VIR_DOMAIN_INPUT_BUS_PARALLELS : VIR_DOMAIN_INPUT_BUS_PS2; if (def->title) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("titles are not supported by vz driver")); return -1; } if (def->blkio.ndevices > 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("blkio parameters are not supported " "by vz driver")); return -1; } if (virDomainDefGetMemoryTotal(def) != def->mem.cur_balloon) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("changing balloon parameters is not supported " "by vz driver")); return -1; } if (virDomainDefGetMemoryTotal(def) % (1 << 10) != 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Memory size should be multiple of 1Mb.")); return -1; } if (def->mem.nhugepages || virMemoryLimitIsSet(def->mem.hard_limit) || virMemoryLimitIsSet(def->mem.soft_limit) || def->mem.min_guarantee || virMemoryLimitIsSet(def->mem.swap_hard_limit)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Memory parameter is not supported " "by vz driver")); return -1; } if (virDomainDefHasVcpusOffline(def)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("current vcpus must be equal to maxvcpus")); return -1; } if (def->placement_mode) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("changing cpu placement mode is not supported " "by vz driver")); return -1; } if (def->cputune.shares || def->cputune.sharesSpecified || def->cputune.period || def->cputune.quota) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("cputune is not supported by vz driver")); return -1; } for (i = 0; i < virDomainDefGetVcpusMax(def); i++) { virDomainVcpuDefPtr vcpu = virDomainDefGetVcpu(def, i); if (vcpu->cpumask && !virBitmapEqual(def->cpumask, vcpu->cpumask)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("vcpupin cpumask differs from default cpumask")); return -1; } } /* * Though we don't support NUMA configuration at the moment * virDomainDefPtr always contain non zero NUMA configuration * So, just make sure this configuration doesn't differ from auto generated. */ if ((virDomainNumatuneGetMode(def->numa, -1, &memMode) == 0 && memMode == VIR_DOMAIN_NUMATUNE_MEM_STRICT) || virDomainNumatuneHasPerNodeBinding(def->numa)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("numa parameters are not supported " "by vz driver")); return -1; } if (def->onReboot != VIR_DOMAIN_LIFECYCLE_ACTION_RESTART || def->onPoweroff != VIR_DOMAIN_LIFECYCLE_ACTION_DESTROY || def->onCrash != VIR_DOMAIN_LIFECYCLE_ACTION_DESTROY) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("on_reboot, on_poweroff and on_crash parameters " "are not supported by vz driver")); return -1; } /* we fill only type and arch fields in vzLoadDomain for * hvm type and also init for containers, so we can check that all * other parameters are null and boot devices config is default */ if (def->os.machine != NULL || def->os.bootmenu != 0 || def->os.kernel != NULL || def->os.initrd != NULL || def->os.cmdline != NULL || def->os.root != NULL || def->os.loader != NULL || def->os.bootloader != NULL || def->os.bootloaderArgs != NULL || def->os.smbios_mode != 0 || def->os.bios.useserial != 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("changing OS parameters is not supported " "by vz driver")); return -1; } pret = PrlVmCfg_GetVmType(sdkdom, &vmType); if (PRL_FAILED(pret)) { logPrlError(pret); return -1; } if (!(vmType == PVT_VM && !IS_CT(def)) && !(vmType == PVT_CT && IS_CT(def))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("changing OS type is not supported " "by vz driver")); return -1; } if (!IS_CT(def)) { if (def->os.init != NULL || def->os.initargv != NULL) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("unsupported OS parameters")); return -1; } } else { if (def->os.nBootDevs != 0 || STRNEQ_NULLABLE(def->os.init, "/sbin/init") || (def->os.initargv != NULL && def->os.initargv[0] != NULL)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("unsupported OS parameters")); return -1; } } if (def->emulator) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("changing emulator is not supported " "by vz driver")); return -1; } for (i = 0; i < VIR_DOMAIN_FEATURE_LAST; i++) { if (def->features[i]) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("changing features is not supported " "by vz driver")); return -1; } } if (def->clock.offset != VIR_DOMAIN_CLOCK_OFFSET_UTC || def->clock.ntimers != 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("changing clock parameters is not supported " "by vz driver")); return -1; } if (!IS_CT(def) && def->nfss != 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Filesystems in VMs are not supported " "by vz driver")); return -1; } if (def->nsounds != 0 || def->nhostdevs != 0 || def->nredirdevs != 0 || def->nsmartcards != 0 || def->nparallels || def->nchannels != 0 || def->nleases != 0 || def->nhubs != 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("changing devices parameters is not supported " "by vz driver")); return -1; } /* check we have only default input devices */ if (def->ngraphics > 0) { if (def->ninputs != 2 || def->inputs[0]->bus != bus || def->inputs[1]->bus != bus || !((def->inputs[0]->type == VIR_DOMAIN_INPUT_TYPE_MOUSE && def->inputs[1]->type == VIR_DOMAIN_INPUT_TYPE_KBD) || (def->inputs[0]->type == VIR_DOMAIN_INPUT_TYPE_KBD && def->inputs[1]->type == VIR_DOMAIN_INPUT_TYPE_MOUSE))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("unsupported input device configuration")); return -1; } } else if (def->ninputs != 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("input devices without vnc are not supported")); return -1; } return 0; } static int prlsdkClearDevices(PRL_HANDLE sdkdom) { PRL_RESULT pret; PRL_UINT32 n, i; PRL_HANDLE devList; PRL_HANDLE dev; int ret = -1; pret = PrlVmCfg_GetAllDevices(sdkdom, &devList); prlsdkCheckRetGoto(pret, cleanup); pret = PrlHndlList_GetItemsCount(devList, &n); prlsdkCheckRetGoto(pret, cleanup); for (i = 0; i < n; i++) { pret = PrlHndlList_GetItem(devList, i, &dev); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDev_Remove(dev); PrlHandle_Free(dev); } ret = 0; cleanup: PrlHandle_Free(devList); return ret; } static int prlsdkRemoveBootDevices(PRL_HANDLE sdkdom) { PRL_RESULT pret; PRL_UINT32 i, devCount; PRL_HANDLE dev = PRL_INVALID_HANDLE; PRL_DEVICE_TYPE devType; pret = PrlVmCfg_GetBootDevCount(sdkdom, &devCount); prlsdkCheckRetExit(pret, -1); for (i = 0; i < devCount; i++) { /* always get device by index 0, because device list resort after delete */ pret = PrlVmCfg_GetBootDev(sdkdom, 0, &dev); prlsdkCheckRetExit(pret, -1); pret = PrlBootDev_GetType(dev, &devType); prlsdkCheckRetExit(pret, -1); pret = PrlBootDev_Remove(dev); prlsdkCheckRetExit(pret, -1); } return 0; } static int prlsdkAddDeviceToBootList(PRL_HANDLE sdkdom, PRL_UINT32 devIndex, PRL_DEVICE_TYPE devType, PRL_UINT32 bootSequence) { PRL_RESULT pret; PRL_HANDLE bootDev = PRL_INVALID_HANDLE; pret = PrlVmCfg_CreateBootDev(sdkdom, &bootDev); prlsdkCheckRetGoto(pret, error); pret = PrlBootDev_SetIndex(bootDev, devIndex); prlsdkCheckRetGoto(pret, error); pret = PrlBootDev_SetType(bootDev, devType); prlsdkCheckRetGoto(pret, error); pret = PrlBootDev_SetSequenceIndex(bootDev, bootSequence); prlsdkCheckRetGoto(pret, error); pret = PrlBootDev_SetInUse(bootDev, PRL_TRUE); prlsdkCheckRetGoto(pret, error); return 0; error: if (bootDev != PRL_INVALID_HANDLE) PrlBootDev_Remove(bootDev); return -1; } static int prlsdkCheckVideoUnsupportedParams(virDomainDefPtr def) { virDomainVideoDefPtr v; if (IS_CT(def)) { if (def->nvideos == 0) { return 0; } else { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Video adapters are not supported " "int containers.")); return -1; } } else { if (def->nvideos != 1) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("vz driver supports " "only one video adapter.")); return -1; } } v = def->videos[0]; if (v->type != VIR_DOMAIN_VIDEO_TYPE_VGA) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("vz driver supports " "only VGA video adapters.")); return -1; } if (v->heads != 1) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("vz driver doesn't support " "multihead video adapters.")); return -1; } if (v->accel != NULL && (v->accel->accel2d || v->accel->accel3d)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("vz driver doesn't support " "setting video acceleration parameters.")); return -1; } return 0; } static int prlsdkCheckSerialUnsupportedParams(virDomainChrDefPtr chr) { if (chr->deviceType != VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Specified character device type is not supported " "by vz driver.")); return -1; } if (chr->targetType != VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Specified character device target type is not " "supported by vz driver.")); return -1; } if (chr->source->type != VIR_DOMAIN_CHR_TYPE_DEV && chr->source->type != VIR_DOMAIN_CHR_TYPE_FILE && chr->source->type != VIR_DOMAIN_CHR_TYPE_UNIX && chr->source->type != VIR_DOMAIN_CHR_TYPE_TCP && chr->source->type != VIR_DOMAIN_CHR_TYPE_UDP) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Specified character device source type is not " "supported by vz driver.")); return -1; } if (chr->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Setting device info for character devices is not " "supported by vz driver.")); return -1; } if (chr->source->nseclabels > 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Setting security labels is not " "supported by vz driver.")); return -1; } if (chr->source->type == VIR_DOMAIN_CHR_TYPE_TCP && chr->source->data.tcp.protocol != VIR_DOMAIN_CHR_TCP_PROTOCOL_RAW) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Protocol '%s' is not supported for " "tcp character device."), virDomainChrTcpProtocolTypeToString(chr->source->data.tcp.protocol)); return -1; } if (chr->source->type == VIR_DOMAIN_CHR_TYPE_UDP && (STRNEQ(chr->source->data.udp.bindHost, chr->source->data.udp.connectHost) || STRNEQ(chr->source->data.udp.bindService, chr->source->data.udp.connectService))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Different bind and connect parameters for " "udp character device is not supported.")); return -1; } return 0; } static int prlsdkCheckNetUnsupportedParams(virDomainNetDefPtr net) { if (net->type != VIR_DOMAIN_NET_TYPE_NETWORK && net->type != VIR_DOMAIN_NET_TYPE_BRIDGE) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Specified network adapter type is not " "supported by vz driver.")); return -1; } if (net->backend.tap || net->backend.vhost) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Interface backend parameters are not " "supported by vz driver.")); return -1; } if (net->data.network.portgroup) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Virtual network portgroups are not " "supported by vz driver.")); return -1; } if (net->tune.sndbuf_specified) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Setting interface sndbuf is not " "supported by vz driver.")); return -1; } if (net->script) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Setting interface script is not " "supported by vz driver.")); return -1; } if (net->ifname_guest) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Setting guest interface name is not " "supported by vz driver.")); return -1; } if (net->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Setting device info for network devices is not " "supported by vz driver.")); return -1; } if (net->filter) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Setting network filter is not " "supported by vz driver.")); return -1; } if (net->bandwidth) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Setting network bandwidth is not " "supported by vz driver.")); return -1; } if (net->vlan.trunk) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Setting up vlans is not " "supported by vz driver.")); return -1; } return 0; } static int prlsdkCheckFSUnsupportedParams(virDomainFSDefPtr fs) { if (fs->type != VIR_DOMAIN_FS_TYPE_FILE && fs->type != VIR_DOMAIN_FS_TYPE_VOLUME) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Only file based or volume based filesystems " "are supported by vz driver.")); return -1; } if (fs->fsdriver != VIR_DOMAIN_FS_DRIVER_TYPE_PLOOP) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Only ploop fs driver is " "supported by vz driver.")); return -1; } if (fs->accessmode != VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Changing fs access mode is not " "supported by vz driver.")); return -1; } if (fs->wrpolicy != VIR_DOMAIN_FS_WRPOLICY_DEFAULT) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Changing fs write policy is not " "supported by vz driver.")); return -1; } if (fs->format != VIR_STORAGE_FILE_PLOOP) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Only ploop disk images are " "supported by vz driver.")); return -1; } if (fs->readonly) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Setting readonly for filesystems is " "not supported by vz driver.")); return -1; } if (fs->space_hard_limit || fs->space_soft_limit) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Setting fs quotas is not " "supported by vz driver.")); return -1; } return 0; } static int prlsdkApplyGraphicsParams(PRL_HANDLE sdkdom, virDomainGraphicsDefPtr gr) { virDomainGraphicsListenDefPtr glisten; PRL_RESULT pret; if (!gr) { pret = PrlVmCfg_SetVNCMode(sdkdom, PRD_DISABLED); prlsdkCheckRetExit(pret, -1); return 0; } pret = PrlVmCfg_SetVNCPassword(sdkdom, gr->data.vnc.auth.passwd ? : ""); prlsdkCheckRetExit(pret, -1); if (gr->data.vnc.autoport) { pret = PrlVmCfg_SetVNCMode(sdkdom, PRD_AUTO); prlsdkCheckRetExit(pret, -1); } else { pret = PrlVmCfg_SetVNCMode(sdkdom, PRD_MANUAL); prlsdkCheckRetExit(pret, -1); pret = PrlVmCfg_SetVNCPort(sdkdom, gr->data.vnc.port); prlsdkCheckRetExit(pret, -1); } glisten = virDomainGraphicsGetListen(gr, 0); pret = PrlVmCfg_SetVNCHostName(sdkdom, glisten && glisten->address ? glisten->address : VIR_LOOPBACK_IPV4_ADDR); prlsdkCheckRetExit(pret, -1); return 0; } static int prlsdkApplyVideoParams(PRL_HANDLE sdkdom G_GNUC_UNUSED, virDomainDefPtr def) { PRL_RESULT pret; if (def->nvideos == 0) return 0; if (IS_CT(def)) { /* ignore video parameters */ return 0; } if (prlsdkCheckVideoUnsupportedParams(def)) return -1; pret = PrlVmCfg_SetVideoRamSize(sdkdom, def->videos[0]->vram >> 10); prlsdkCheckRetExit(pret, -1); return 0; } static int prlsdkAddSerial(PRL_HANDLE sdkdom, virDomainChrDefPtr chr) { PRL_RESULT pret; PRL_HANDLE sdkchr = PRL_INVALID_HANDLE; PRL_VM_DEV_EMULATION_TYPE emutype; PRL_SERIAL_PORT_SOCKET_OPERATION_MODE socket_mode = PSP_SERIAL_SOCKET_SERVER; char *path; char *url = NULL; int ret = -1; if (prlsdkCheckSerialUnsupportedParams(chr) < 0) return -1; pret = PrlVmCfg_CreateVmDev(sdkdom, PDE_SERIAL_PORT, &sdkchr); prlsdkCheckRetGoto(pret, cleanup); switch (chr->source->type) { case VIR_DOMAIN_CHR_TYPE_DEV: emutype = PDT_USE_REAL_DEVICE; path = chr->source->data.file.path; break; case VIR_DOMAIN_CHR_TYPE_FILE: emutype = PDT_USE_OUTPUT_FILE; path = chr->source->data.file.path; break; case VIR_DOMAIN_CHR_TYPE_UNIX: emutype = PDT_USE_SERIAL_PORT_SOCKET_MODE; path = chr->source->data.nix.path; if (!chr->source->data.nix.listen) socket_mode = PSP_SERIAL_SOCKET_CLIENT; break; case VIR_DOMAIN_CHR_TYPE_TCP: emutype = PDT_USE_TCP; url = g_strdup_printf("%s:%s", chr->source->data.tcp.host, chr->source->data.tcp.service); if (!chr->source->data.tcp.listen) socket_mode = PSP_SERIAL_SOCKET_CLIENT; path = url; break; case VIR_DOMAIN_CHR_TYPE_UDP: emutype = PDT_USE_UDP; url = g_strdup_printf("%s:%s", chr->source->data.udp.bindHost, chr->source->data.udp.bindService); path = url; break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("vz driver doesn't support " "specified serial source type.")); goto cleanup; } pret = PrlVmDev_SetEmulatedType(sdkchr, emutype); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDev_SetSysName(sdkchr, path); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDev_SetFriendlyName(sdkchr, path); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDevSerial_SetSocketMode(sdkchr, socket_mode); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDev_SetEnabled(sdkchr, 1); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDev_SetIndex(sdkchr, chr->target.port); prlsdkCheckRetGoto(pret, cleanup); ret = 0; cleanup: PrlHandle_Free(sdkchr); VIR_FREE(url); return ret; } #define PRL_MAC_STRING_BUFNAME 13 static const char * prlsdkFormatMac(virMacAddrPtr mac, char *macstr) { g_snprintf(macstr, PRL_MAC_STRING_BUFNAME, "%02X%02X%02X%02X%02X%02X", mac->addr[0], mac->addr[1], mac->addr[2], mac->addr[3], mac->addr[4], mac->addr[5]); macstr[PRL_MAC_STRING_BUFNAME - 1] = '\0'; return macstr; } static int prlsdkConfigureGateways(PRL_HANDLE sdknet, virDomainNetDefPtr net) { int ret = -1; size_t i; virNetDevIPRoutePtr route4 = NULL, route6 = NULL; char *gw4 = NULL, *gw6 = NULL; PRL_RESULT pret; for (i = 0; i < net->guestIP.nroutes; i++) { virSocketAddrPtr addrdst, gateway; virSocketAddr zero; addrdst = virNetDevIPRouteGetAddress(net->guestIP.routes[i]); gateway = virNetDevIPRouteGetGateway(net->guestIP.routes[i]); ignore_value(virSocketAddrParse(&zero, (VIR_SOCKET_ADDR_IS_FAMILY(addrdst, AF_INET) ? VIR_SOCKET_ADDR_IPV4_ALL : VIR_SOCKET_ADDR_IPV6_ALL), VIR_SOCKET_ADDR_FAMILY(addrdst))); /* virSocketAddrParse raises an error * and we are not going to report it, reset it explicitly */ virResetLastError(); if (!virSocketAddrEqual(addrdst, &zero)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Support only default gateway")); return -1; } switch (VIR_SOCKET_ADDR_FAMILY(gateway)) { case AF_INET: if (route4) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Support only one IPv4 default gateway")); return -1; } route4 = net->guestIP.routes[i]; break; case AF_INET6: if (route6) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Support only one IPv6 default gateway")); return -1; } route6 = net->guestIP.routes[i]; break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unsupported address family %d " "Only IPv4 or IPv6 default gateway"), VIR_SOCKET_ADDR_FAMILY(gateway)); return -1; } } if (route4 && !(gw4 = virSocketAddrFormat(virNetDevIPRouteGetGateway(route4)))) goto cleanup; pret = PrlVmDevNet_SetDefaultGateway(sdknet, gw4 ? : ""); prlsdkCheckRetGoto(pret, cleanup); if (route6 && !(gw6 = virSocketAddrFormat(virNetDevIPRouteGetGateway(route6)))) goto cleanup; pret = PrlVmDevNet_SetDefaultGatewayIPv6(sdknet, gw6 ? : ""); prlsdkCheckRetGoto(pret, cleanup); ret = 0; cleanup: VIR_FREE(gw4); VIR_FREE(gw6); return ret; } static int prlsdkConfigureNet(vzDriverPtr driver G_GNUC_UNUSED, virDomainObjPtr dom G_GNUC_UNUSED, PRL_HANDLE sdkdom, virDomainNetDefPtr net, bool isCt, bool create) { PRL_RESULT pret; PRL_HANDLE sdknet = PRL_INVALID_HANDLE; PRL_HANDLE addrlist = PRL_INVALID_HANDLE; size_t i; int ret = -1; char macstr[PRL_MAC_STRING_BUFNAME]; char *addrstr = NULL; bool ipv6present = false; bool ipv4present = false; if (prlsdkCheckNetUnsupportedParams(net) < 0) return -1; if (create) { pret = PrlVmCfg_CreateVmDev(sdkdom, PDE_GENERIC_NETWORK_ADAPTER, &sdknet); prlsdkCheckRetGoto(pret, cleanup); } else { sdknet = prlsdkFindNetByMAC(sdkdom, &net->mac); if (sdknet == PRL_INVALID_HANDLE) return -1; } pret = PrlVmDev_SetEnabled(sdknet, 1); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDev_SetConnected(sdknet, net->linkstate != VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN); prlsdkCheckRetGoto(pret, cleanup); if (net->ifname) { pret = PrlVmDevNet_SetHostInterfaceName(sdknet, net->ifname); prlsdkCheckRetGoto(pret, cleanup); } prlsdkFormatMac(&net->mac, macstr); pret = PrlVmDevNet_SetMacAddress(sdknet, macstr); prlsdkCheckRetGoto(pret, cleanup); pret = PrlApi_CreateStringsList(&addrlist); prlsdkCheckRetGoto(pret, cleanup); for (i = 0; i < net->guestIP.nips; i++) { char *tmpstr; if (AF_INET == VIR_SOCKET_ADDR_FAMILY(&net->guestIP.ips[i]->address)) ipv4present = true; else if (AF_INET6 == VIR_SOCKET_ADDR_FAMILY(&net->guestIP.ips[i]->address)) ipv6present = true; else continue; if (!(tmpstr = virSocketAddrFormat(&net->guestIP.ips[i]->address))) goto cleanup; addrstr = g_strdup_printf("%s/%d", tmpstr, net->guestIP.ips[i]->prefix); VIR_FREE(tmpstr); pret = PrlStrList_AddItem(addrlist, addrstr); prlsdkCheckRetGoto(pret, cleanup); VIR_FREE(addrstr); } if (ipv4present || ipv6present) { pret = PrlVmDevNet_SetNetAddresses(sdknet, addrlist); prlsdkCheckRetGoto(pret, cleanup); } pret = PrlVmDevNet_SetConfigureWithDhcp(sdknet, !ipv4present); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDevNet_SetConfigureWithDhcpIPv6(sdknet, !ipv6present); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDevNet_SetAutoApply(sdknet, true); prlsdkCheckRetGoto(pret, cleanup); if (prlsdkConfigureGateways(sdknet, net)) goto cleanup; if (isCt) { if (net->model != VIR_DOMAIN_NET_MODEL_UNKNOWN) VIR_WARN("Setting network adapter for containers is not " "supported by vz driver."); } else { if (net->model == VIR_DOMAIN_NET_MODEL_RTL8139) { pret = PrlVmDevNet_SetAdapterType(sdknet, PNT_RTL); } else if (net->model == VIR_DOMAIN_NET_MODEL_E1000) { pret = PrlVmDevNet_SetAdapterType(sdknet, PNT_E1000); } else if (net->model == VIR_DOMAIN_NET_MODEL_VIRTIO) { pret = PrlVmDevNet_SetAdapterType(sdknet, PNT_VIRTIO); } else { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Specified network adapter model is not " "supported by vz driver.")); goto cleanup; } prlsdkCheckRetGoto(pret, cleanup); } if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { if (STREQ(net->data.network.name, PARALLELS_DOMAIN_ROUTED_NETWORK_NAME)) { pret = PrlVmDev_SetEmulatedType(sdknet, PNA_ROUTED); prlsdkCheckRetGoto(pret, cleanup); } else { pret = PrlVmDev_SetEmulatedType(sdknet, PNA_BRIDGED_NETWORK); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDevNet_SetVirtualNetworkId(sdknet, net->data.network.name); prlsdkCheckRetGoto(pret, cleanup); } } else if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE) { pret = PrlVmDev_SetEmulatedType(sdknet, PNA_BRIDGE); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDevNet_SetVirtualNetworkId(sdknet, net->data.bridge.brname); prlsdkCheckRetGoto(pret, cleanup); } pret = PrlVmDevNet_SetPktFilterPreventMacSpoof(sdknet, net->trustGuestRxFilters == VIR_TRISTATE_BOOL_YES); prlsdkCheckRetGoto(pret, cleanup); ret = 0; cleanup: VIR_FREE(addrstr); PrlHandle_Free(addrlist); PrlHandle_Free(sdknet); return ret; } static PRL_HANDLE prlsdkFindNetByMAC(PRL_HANDLE sdkdom, virMacAddrPtr mac) { PRL_RESULT pret; PRL_UINT32 adaptersCount; PRL_UINT32 i; PRL_HANDLE adapter = PRL_INVALID_HANDLE; char adapterMac[PRL_MAC_STRING_BUFNAME]; char expectedMac[PRL_MAC_STRING_BUFNAME]; char virMac[VIR_MAC_STRING_BUFLEN]; prlsdkFormatMac(mac, expectedMac); pret = PrlVmCfg_GetNetAdaptersCount(sdkdom, &adaptersCount); prlsdkCheckRetGoto(pret, cleanup); for (i = 0; i < adaptersCount; ++i) { pret = PrlVmCfg_GetNetAdapter(sdkdom, i, &adapter); prlsdkCheckRetGoto(pret, cleanup); pret = prlsdkGetStringParamBuf(PrlVmDevNet_GetMacAddress, adapter, adapterMac, sizeof(adapterMac)); prlsdkCheckRetGoto(pret, cleanup); if (STREQ(adapterMac, expectedMac)) return adapter; PrlHandle_Free(adapter); adapter = PRL_INVALID_HANDLE; } virReportError(VIR_ERR_INTERNAL_ERROR, _("No net with mac '%s'"), virMacAddrFormat(mac, virMac)); cleanup: PrlHandle_Free(adapter); return adapter; } static int prlsdkConfigureDisk(vzDriverPtr driver, PRL_HANDLE sdkdom, virDomainDiskDefPtr disk, bool create) { PRL_RESULT pret; PRL_HANDLE sdkdisk = PRL_INVALID_HANDLE; int ret = -1; PRL_VM_DEV_EMULATION_TYPE emutype; PRL_MASS_STORAGE_INTERFACE_TYPE sdkbus; int idx; virDomainDeviceDriveAddressPtr drive; PRL_DEVICE_TYPE devType; PRL_CLUSTERED_DEVICE_SUBTYPE scsiModel; const char *path = disk->src->path ? : ""; if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) devType = PDE_HARD_DISK; else devType = PDE_OPTICAL_DISK; if (create) { pret = PrlVmCfg_CreateVmDev(sdkdom, devType, &sdkdisk); prlsdkCheckRetGoto(pret, cleanup); } else { sdkdisk = prlsdkGetDisk(sdkdom, disk); if (sdkdisk == PRL_INVALID_HANDLE) return -1; } pret = PrlVmDev_SetEnabled(sdkdisk, 1); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDev_SetConnected(sdkdisk, 1); prlsdkCheckRetGoto(pret, cleanup); if (disk->src->type == VIR_STORAGE_TYPE_FILE) emutype = PDT_USE_IMAGE_FILE; else emutype = PDT_USE_REAL_DEVICE; pret = PrlVmDev_SetEmulatedType(sdkdisk, emutype); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDev_SetSysName(sdkdisk, path); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDev_SetFriendlyName(sdkdisk, path); prlsdkCheckRetGoto(pret, cleanup); drive = &disk->info.addr.drive; switch (disk->bus) { case VIR_DOMAIN_DISK_BUS_IDE: sdkbus = PMS_IDE_DEVICE; idx = 2 * drive->bus + drive->unit; break; case VIR_DOMAIN_DISK_BUS_SCSI: sdkbus = PMS_SCSI_DEVICE; idx = drive->unit; break; case VIR_DOMAIN_DISK_BUS_SATA: sdkbus = PMS_SATA_DEVICE; idx = drive->unit; break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Specified disk bus is not " "supported by vz driver.")); goto cleanup; } if (disk->bus == VIR_DOMAIN_DISK_BUS_SCSI) { if (vzGetDefaultSCSIModel(driver, &scsiModel) < 0) goto cleanup; pret = PrlVmDev_SetSubType(sdkdisk, scsiModel); prlsdkCheckRetGoto(pret, cleanup); } pret = PrlVmDev_SetIfaceType(sdkdisk, sdkbus); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDev_SetStackIndex(sdkdisk, idx); prlsdkCheckRetGoto(pret, cleanup); if (devType == PDE_HARD_DISK) { pret = PrlVmDevHd_SetSerialNumber(sdkdisk, disk->serial); prlsdkCheckRetGoto(pret, cleanup); } return 0; cleanup: PrlHandle_Free(sdkdisk); return ret; } static PRL_HANDLE prlsdkGetDisk(PRL_HANDLE sdkdom, virDomainDiskDefPtr disk) { PRL_RESULT pret; PRL_UINT32 num; size_t i; PRL_HANDLE sdkdisk = PRL_INVALID_HANDLE; int bus; char *dst = NULL; PRL_DEVICE_TYPE devType; if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) devType = PDE_HARD_DISK; else devType = PDE_OPTICAL_DISK; pret = PrlVmCfg_GetDevsCountByType(sdkdom, devType, &num); prlsdkCheckRetGoto(pret, error); for (i = 0; i < num; ++i) { pret = PrlVmCfg_GetDevByType(sdkdom, devType, i, &sdkdisk); prlsdkCheckRetGoto(pret, error); if (prlsdkGetDiskId(sdkdisk, &bus, &dst) < 0) goto error; if (disk->bus == bus && STREQ(disk->dst, dst)) { VIR_FREE(dst); return sdkdisk; } PrlHandle_Free(sdkdisk); sdkdisk = PRL_INVALID_HANDLE; VIR_FREE(dst); } virReportError(VIR_ERR_INTERNAL_ERROR, _("No disk with bus '%s' and target '%s'"), virDomainDiskBusTypeToString(disk->bus), disk->dst); return PRL_INVALID_HANDLE; error: VIR_FREE(dst); PrlHandle_Free(sdkdisk); return PRL_INVALID_HANDLE; } int prlsdkAttachDevice(vzDriverPtr driver, virDomainObjPtr dom, virDomainDeviceDefPtr dev) { vzDomObjPtr privdom = dom->privateData; PRL_HANDLE job = PRL_INVALID_HANDLE; job = PrlVm_BeginEdit(privdom->sdkdom); if (PRL_FAILED(waitDomainJob(job, dom))) return -1; switch (dev->type) { case VIR_DOMAIN_DEVICE_DISK: if (prlsdkConfigureDisk(driver, privdom->sdkdom, dev->data.disk, true) < 0) return -1; break; case VIR_DOMAIN_DEVICE_NET: if (!IS_CT(dom->def)) { virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", _("attaching network device to VM is unsupported")); return -1; } if (prlsdkConfigureNet(driver, dom, privdom->sdkdom, dev->data.net, IS_CT(dom->def), true) < 0) return -1; break; case VIR_DOMAIN_DEVICE_GRAPHICS: if (dom->def->ngraphics > 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("domain already has VNC graphics")); return -1; } if (prlsdkApplyGraphicsParams(privdom->sdkdom, dev->data.graphics) < 0) return -1; break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("attaching device type '%s' is unsupported"), virDomainDeviceTypeToString(dev->type)); return -1; } job = PrlVm_CommitEx(privdom->sdkdom, PVCF_DETACH_HDD_BUNDLE); if (PRL_FAILED(waitDomainJob(job, dom))) return -1; return 0; } int prlsdkDetachDevice(vzDriverPtr driver G_GNUC_UNUSED, virDomainObjPtr dom, virDomainDeviceDefPtr dev) { int ret = -1; vzDomObjPtr privdom = dom->privateData; PRL_HANDLE job = PRL_INVALID_HANDLE; PRL_HANDLE sdkdev = PRL_INVALID_HANDLE; PRL_RESULT pret; job = PrlVm_BeginEdit(privdom->sdkdom); if (PRL_FAILED(waitDomainJob(job, dom))) goto cleanup; switch (dev->type) { case VIR_DOMAIN_DEVICE_DISK: sdkdev = prlsdkGetDisk(privdom->sdkdom, dev->data.disk); if (sdkdev == PRL_INVALID_HANDLE) goto cleanup; pret = PrlVmDev_Remove(sdkdev); prlsdkCheckRetGoto(pret, cleanup); break; case VIR_DOMAIN_DEVICE_NET: if (!IS_CT(dom->def)) { virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", _("detaching network device from VM is unsupported")); goto cleanup; } sdkdev = prlsdkFindNetByMAC(privdom->sdkdom, &dev->data.net->mac); if (sdkdev == PRL_INVALID_HANDLE) goto cleanup; pret = PrlVmDev_Remove(sdkdev); prlsdkCheckRetGoto(pret, cleanup); break; case VIR_DOMAIN_DEVICE_GRAPHICS: if (dom->def->ngraphics < 1) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("cannot find VNC graphics device")); goto cleanup; } if (prlsdkApplyGraphicsParams(privdom->sdkdom, NULL) < 0) goto cleanup; break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("detaching device type '%s' is unsupported"), virDomainDeviceTypeToString(dev->type)); goto cleanup; } job = PrlVm_CommitEx(privdom->sdkdom, PVCF_DETACH_HDD_BUNDLE); if (PRL_FAILED(waitDomainJob(job, dom))) goto cleanup; ret = 0; cleanup: PrlHandle_Free(sdkdev); return ret; } int prlsdkUpdateDevice(vzDriverPtr driver, virDomainObjPtr dom, virDomainDeviceDefPtr dev) { vzDomObjPtr privdom = dom->privateData; PRL_HANDLE job = PRL_INVALID_HANDLE; job = PrlVm_BeginEdit(privdom->sdkdom); if (PRL_FAILED(waitDomainJob(job, dom))) return -1; switch (dev->type) { case VIR_DOMAIN_DEVICE_DISK: if (prlsdkConfigureDisk(driver, privdom->sdkdom, dev->data.disk, false) < 0) return -1; break; case VIR_DOMAIN_DEVICE_NET: if (prlsdkConfigureNet(driver, dom, privdom->sdkdom, dev->data.net, IS_CT(dom->def), false) < 0) return -1; break; case VIR_DOMAIN_DEVICE_GRAPHICS: if (dom->def->ngraphics < 1) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("cannot find VNC graphics device")); return -1; } if (prlsdkApplyGraphicsParams(privdom->sdkdom, dev->data.graphics) < 0) return -1; break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("updating device type '%s' is unsupported"), virDomainDeviceTypeToString(dev->type)); return -1; } job = PrlVm_CommitEx(privdom->sdkdom, PVCF_DETACH_HDD_BUNDLE); if (PRL_FAILED(waitDomainJob(job, dom))) return -1; return 0; } static int prlsdkAddFS(PRL_HANDLE sdkdom, virDomainFSDefPtr fs) { PRL_RESULT pret; PRL_HANDLE sdkdisk = PRL_INVALID_HANDLE; int ret = -1; char *storage = NULL; if (fs->type == VIR_DOMAIN_FS_TYPE_TEMPLATE) return 0; if (prlsdkCheckFSUnsupportedParams(fs) < 0) return -1; pret = PrlVmCfg_CreateVmDev(sdkdom, PDE_HARD_DISK, &sdkdisk); prlsdkCheckRetGoto(pret, cleanup); if (fs->type == VIR_DOMAIN_FS_TYPE_VOLUME) { storage = g_strdup_printf("libvirt://localhost/%s/%s", fs->src->srcpool->pool, fs->src->srcpool->volume); pret = PrlVmDevHd_SetStorageURL(sdkdisk, storage); prlsdkCheckRetGoto(pret, cleanup); } pret = PrlVmDev_SetEnabled(sdkdisk, 1); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDev_SetConnected(sdkdisk, 1); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDev_SetEmulatedType(sdkdisk, PDT_USE_IMAGE_FILE); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDev_SetSysName(sdkdisk, fs->src->path); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDev_SetImagePath(sdkdisk, fs->src->path); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDev_SetFriendlyName(sdkdisk, fs->src->path); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDevHd_SetMountPoint(sdkdisk, fs->dst); prlsdkCheckRetGoto(pret, cleanup); ret = 0; cleanup: VIR_FREE(storage); PrlHandle_Free(sdkdisk); return ret; } static int prlsdkSetBootOrderCt(PRL_HANDLE sdkdom, virDomainDefPtr def) { size_t i; PRL_HANDLE hdd = PRL_INVALID_HANDLE; PRL_RESULT pret; bool rootfs = false; int ret = -1; for (i = 0; i < def->nfss; i++) { pret = prlsdkAddDeviceToBootList(sdkdom, i, PDE_HARD_DISK, i + 1); prlsdkCheckRetExit(pret, -1); if (STREQ(def->fss[i]->dst, "/")) rootfs = true; } if (!rootfs) { /* if we have root mounted we don't need to explicitly set boot order */ pret = PrlVmCfg_GetHardDisk(sdkdom, def->nfss, &hdd); prlsdkCheckRetExit(pret, -1); PrlVmDevHd_SetMountPoint(hdd, "/"); prlsdkCheckRetGoto(pret, cleanup); } ret = 0; cleanup: PrlHandle_Free(hdd); return ret; } static int prlsdkSetBootOrderVm(PRL_HANDLE sdkdom, virDomainDefPtr def) { size_t i; int idx[VIR_DOMAIN_BOOT_LAST] = { 0 }; int bootIndex = 0; PRL_RESULT pret; PRL_UINT32 num; int sdkType; virDomainBootOrder virType; for (i = 0; i < def->os.nBootDevs; ++i) { virType = def->os.bootDevs[i]; switch ((int)virType) { case VIR_DOMAIN_BOOT_CDROM: sdkType = PDE_OPTICAL_DISK; break; case VIR_DOMAIN_BOOT_DISK: sdkType = PDE_HARD_DISK; break; case VIR_DOMAIN_BOOT_NET: sdkType = PDE_GENERIC_NETWORK_ADAPTER; break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unsupported boot device type: '%s'"), virDomainBootTypeToString(virType)); return -1; } pret = PrlVmCfg_GetDevsCountByType(sdkdom, sdkType, &num); prlsdkCheckRetExit(pret, -1); pret = prlsdkAddDeviceToBootList(sdkdom, idx[virType]++, sdkType, bootIndex++); prlsdkCheckRetExit(pret, -1); } return 0; } int prlsdkDomainSetUserPassword(virDomainObjPtr dom, const char *user, const char *password) { vzDomObjPtr privdom = dom->privateData; PRL_HANDLE job = PRL_INVALID_HANDLE; job = PrlVm_SetUserPasswd(privdom->sdkdom, user, password, 0); if (PRL_FAILED(waitDomainJob(job, dom))) return -1; return 0; } static int prlsdkDoApplyConfig(vzDriverPtr driver, virDomainObjPtr dom, PRL_HANDLE sdkdom, virDomainDefPtr def) { PRL_RESULT pret; size_t i; char uuidstr[VIR_UUID_STRING_BRACED_BUFLEN]; char *mask = NULL; if (prlsdkCheckUnsupportedParams(sdkdom, def) < 0) return -1; if (def->description) { pret = PrlVmCfg_SetDescription(sdkdom, def->description); prlsdkCheckRetGoto(pret, error); } if (def->name) { pret = PrlVmCfg_SetName(sdkdom, def->name); prlsdkCheckRetGoto(pret, error); } if (def->uuid) { prlsdkUUIDFormat(def->uuid, uuidstr); pret = PrlVmCfg_SetUuid(sdkdom, uuidstr); prlsdkCheckRetGoto(pret, error); } pret = PrlVmCfg_SetRamSize(sdkdom, virDomainDefGetMemoryTotal(def) >> 10); prlsdkCheckRetGoto(pret, error); pret = PrlVmCfg_SetCpuCount(sdkdom, virDomainDefGetVcpus(def)); prlsdkCheckRetGoto(pret, error); if (!(mask = virBitmapFormat(def->cpumask))) goto error; pret = PrlVmCfg_SetCpuMask(sdkdom, mask); prlsdkCheckRetGoto(pret, error); VIR_FREE(mask); switch ((int)def->os.arch) { case VIR_ARCH_X86_64: pret = PrlVmCfg_SetCpuMode(sdkdom, PCM_CPU_MODE_64); break; case VIR_ARCH_I686: pret = PrlVmCfg_SetCpuMode(sdkdom, PCM_CPU_MODE_32); break; default: virReportError(VIR_ERR_INTERNAL_ERROR, _("Unknown CPU mode: %s"), virArchToString(def->os.arch)); goto error; } prlsdkCheckRetGoto(pret, error); if (prlsdkClearDevices(sdkdom) < 0) goto error; if (prlsdkRemoveBootDevices(sdkdom) < 0) goto error; for (i = 0; i < def->nnets; i++) { if (prlsdkConfigureNet(driver, dom, sdkdom, def->nets[i], IS_CT(def), true) < 0) goto error; } if (def->ngraphics > 1) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("vz driver supports only VNC graphics")); goto error; } if (prlsdkApplyGraphicsParams(sdkdom, def->ngraphics == 1 ? def->graphics[0] : NULL) < 0) goto error; if (prlsdkApplyVideoParams(sdkdom, def) < 0) goto error; for (i = 0; i < def->nserials; i++) { if (prlsdkAddSerial(sdkdom, def->serials[i]) < 0) goto error; } /* It is important that we add filesystems first and then disks as we rely * on this information in prlsdkSetBootOrderCt */ for (i = 0; i < def->nfss; i++) { if (prlsdkAddFS(sdkdom, def->fss[i]) < 0) goto error; } /* filesystems first, disks go after them as we rely on this order in * prlsdkSetBootOrderCt */ for (i = 0; i < def->ndisks; i++) { if (prlsdkConfigureDisk(driver, sdkdom, def->disks[i], true) < 0) goto error; } if (IS_CT(def)) { if (prlsdkSetBootOrderCt(sdkdom, def) < 0) goto error; } else { if (prlsdkSetBootOrderVm(sdkdom, def) < 0) goto error; } return 0; error: VIR_FREE(mask); return -1; } int prlsdkApplyConfig(vzDriverPtr driver, virDomainObjPtr dom, virDomainDefPtr new) { vzDomObjPtr privdom = dom->privateData; PRL_HANDLE job = PRL_INVALID_HANDLE; int ret; job = PrlVm_BeginEdit(privdom->sdkdom); if (PRL_FAILED(waitDomainJob(job, dom))) return -1; ret = prlsdkDoApplyConfig(driver, dom, privdom->sdkdom, new); if (ret == 0) { job = PrlVm_CommitEx(privdom->sdkdom, PVCF_DETACH_HDD_BUNDLE); if (PRL_FAILED(waitDomainJob(job, dom))) ret = -1; } return ret; } int prlsdkCreateVm(vzDriverPtr driver, virDomainDefPtr def) { PRL_HANDLE sdkdom = PRL_INVALID_HANDLE; PRL_HANDLE job = PRL_INVALID_HANDLE; PRL_HANDLE result = PRL_INVALID_HANDLE; PRL_HANDLE srvconf = PRL_INVALID_HANDLE; PRL_RESULT pret; int ret = -1; pret = PrlSrv_CreateVm(driver->server, &sdkdom); prlsdkCheckRetGoto(pret, cleanup); job = PrlSrv_GetSrvConfig(driver->server); if (PRL_FAILED(getJobResult(job, &result))) goto cleanup; pret = PrlResult_GetParamByIndex(result, 0, &srvconf); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmCfg_SetDefaultConfig(sdkdom, srvconf, PVS_GUEST_VER_LIN_REDHAT, 0); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmCfg_SetOfflineManagementEnabled(sdkdom, 0); prlsdkCheckRetGoto(pret, cleanup); if (prlsdkDoApplyConfig(driver, NULL, sdkdom, def) < 0) goto cleanup; job = PrlVm_Reg(sdkdom, "", 1); if (PRL_FAILED(waitJob(job))) goto cleanup; ret = 0; cleanup: PrlHandle_Free(sdkdom); PrlHandle_Free(srvconf); PrlHandle_Free(result); return ret; } static int virStorageTranslatePoolLocal(virConnectPtr conn, virStorageSourcePtr src) { virStoragePoolPtr pool = NULL; virStorageVolPtr vol = NULL; virStorageVolInfo info; int ret = -1; if (!(pool = virStoragePoolLookupByName(conn, src->srcpool->pool))) return -1; if (virStoragePoolIsActive(pool) != 1) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("storage pool '%s' containing volume '%s' " "is not active"), src->srcpool->pool, src->srcpool->volume); goto cleanup; } if (!(vol = virStorageVolLookupByName(pool, src->srcpool->volume))) goto cleanup; if (virStorageVolGetInfo(vol, &info) < 0) goto cleanup; if (info.type != VIR_STORAGE_VOL_PLOOP) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unsupported volume format '%s'"), virStorageVolTypeToString(info.type)); goto cleanup; } if (!(src->path = virStorageVolGetPath(vol))) goto cleanup; ret = 0; cleanup: virObjectUnref(pool); virObjectUnref(vol); return ret; } int prlsdkCreateCt(virConnectPtr conn, virDomainDefPtr def) { PRL_HANDLE sdkdom = PRL_INVALID_HANDLE; PRL_GET_VM_CONFIG_PARAM_DATA confParam; PRL_HANDLE job = PRL_INVALID_HANDLE; PRL_HANDLE result = PRL_INVALID_HANDLE; PRL_RESULT pret; PRL_UINT32 flags; vzConnPtr privconn = conn->privateData; vzDriverPtr driver = privconn->driver; int ret = -1; int useTemplate = 0; size_t i; for (i = 0; i < def->nfss; i++) { if (useTemplate) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("Unsupported configuration")); return -1; } if (def->fss[i]->type == VIR_DOMAIN_FS_TYPE_TEMPLATE) useTemplate = 1; if (def->fss[i]->type == VIR_DOMAIN_FS_TYPE_VOLUME) { if (virStorageTranslatePoolLocal(conn, def->fss[i]->src) < 0) goto cleanup; } } if (useTemplate && def->nfss > 1) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("Unsupported configuration")); return -1; } confParam.nVmType = PVT_CT; confParam.sConfigSample = "vswap.1024MB"; confParam.nOsVersion = 0; job = PrlSrv_GetDefaultVmConfig(driver->server, &confParam, 0); if (PRL_FAILED(getJobResult(job, &result))) goto cleanup; pret = PrlResult_GetParamByIndex(result, 0, &sdkdom); prlsdkCheckRetGoto(pret, cleanup); if (useTemplate) { pret = PrlVmCfg_SetOsTemplate(sdkdom, def->fss[0]->src->path); prlsdkCheckRetGoto(pret, cleanup); } if (prlsdkDoApplyConfig(driver, NULL, sdkdom, def) < 0) goto cleanup; flags = PACF_NON_INTERACTIVE_MODE; if (!useTemplate) flags |= PRNVM_PRESERVE_DISK; job = PrlVm_RegEx(sdkdom, "", flags); if (PRL_FAILED(waitJob(job))) goto cleanup; ret = 0; cleanup: PrlHandle_Free(sdkdom); PrlHandle_Free(result); return ret; } /** * prlsdkDetachDomainHardDisks: * * @sdkdom: domain handle * * Returns 0 if hard disks were successfully detached or not detected. */ static int prlsdkDetachDomainHardDisks(virDomainObjPtr dom) { int ret = -1; PRL_RESULT pret; PRL_UINT32 hddCount; PRL_UINT32 i; PRL_HANDLE job; PRL_HANDLE sdkdisk = PRL_INVALID_HANDLE; vzDomObjPtr pdom = dom->privateData; PRL_HANDLE sdkdom = pdom->sdkdom; job = PrlVm_BeginEdit(sdkdom); if (PRL_FAILED(waitDomainJob(job, dom))) goto cleanup; pret = PrlVmCfg_GetHardDisksCount(sdkdom, &hddCount); prlsdkCheckRetGoto(pret, cleanup); for (i = 0; i < hddCount; ++i) { pret = PrlVmCfg_GetHardDisk(sdkdom, 0, &sdkdisk); prlsdkCheckRetGoto(pret, cleanup); pret = PrlVmDev_Remove(sdkdisk); prlsdkCheckRetGoto(pret, cleanup); PrlHandle_Free(sdkdisk); sdkdisk = PRL_INVALID_HANDLE; } job = PrlVm_CommitEx(sdkdom, PVCF_DETACH_HDD_BUNDLE); if (PRL_FAILED(waitDomainJob(job, dom))) goto cleanup; ret = 0; cleanup: PrlHandle_Free(sdkdisk); return ret; } int prlsdkUnregisterDomain(vzDriverPtr driver, virDomainObjPtr dom, unsigned int flags) { vzDomObjPtr privdom = dom->privateData; PRL_HANDLE job; virDomainSnapshotObjListPtr snapshots = NULL; VIRTUAL_MACHINE_STATE domainState; int ret = -1; int num; if (prlsdkGetDomainState(dom, privdom->sdkdom, &domainState) < 0) return -1; if (VMS_SUSPENDED == domainState && !(flags & VIR_DOMAIN_UNDEFINE_MANAGED_SAVE)) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("Refusing to undefine while domain managed " "save image exists")); return -1; } if (!(snapshots = prlsdkLoadSnapshots(dom))) return -1; if ((num = virDomainSnapshotObjListNum(snapshots, NULL, 0)) < 0) goto cleanup; if (num > 0 && !(flags & VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA)) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("Refusing to undefine while snapshots exist")); goto cleanup; } if (prlsdkDetachDomainHardDisks(dom)) goto cleanup; job = PrlVm_Delete(privdom->sdkdom, PRL_INVALID_HANDLE); if (PRL_FAILED(waitDomainJob(job, dom))) goto cleanup; prlsdkSendEvent(driver, dom, VIR_DOMAIN_EVENT_UNDEFINED, VIR_DOMAIN_EVENT_UNDEFINED_REMOVED); virDomainObjListRemove(driver->domains, dom); ret = 0; cleanup: virDomainSnapshotObjListFree(snapshots); return ret; } int prlsdkDomainManagedSaveRemove(virDomainObjPtr dom) { vzDomObjPtr privdom = dom->privateData; PRL_HANDLE job; job = PrlVm_DropSuspendedState(privdom->sdkdom); if (PRL_FAILED(waitDomainJob(job, dom))) return -1; return 0; } static int prlsdkExtractStatsParam(PRL_HANDLE sdkstats, const char *name, long long *val) { PRL_HANDLE param = PRL_INVALID_HANDLE; PRL_RESULT pret; PRL_INT64 pval = 0; int ret = -1; pret = PrlEvent_GetParamByName(sdkstats, name, ¶m); if (pret == PRL_ERR_NO_DATA) { *val = -1; ret = 0; goto cleanup; } else if (PRL_FAILED(pret)) { logPrlError(pret); goto cleanup; } pret = PrlEvtPrm_ToInt64(param, &pval); prlsdkCheckRetGoto(pret, cleanup); *val = pval; ret = 0; cleanup: PrlHandle_Free(param); return ret; } #define PARALLELS_STATISTICS_TIMEOUT (60 * 1000) int prlsdkGetBlockStats(PRL_HANDLE sdkstats, virDomainDiskDefPtr disk, virDomainBlockStatsPtr stats, bool isCt) { virDomainDeviceDriveAddressPtr address; int idx; const char *prefix; int ret = -1; char *name = NULL; address = &disk->info.addr.drive; if (isCt) { prefix = "hdd"; idx = address->unit; } else { switch (disk->bus) { case VIR_DOMAIN_DISK_BUS_IDE: prefix = "ide"; idx = address->bus * 2 + address->unit; break; case VIR_DOMAIN_DISK_BUS_SATA: prefix = "sata"; idx = address->unit; break; case VIR_DOMAIN_DISK_BUS_SCSI: prefix = "scsi"; idx = address->unit; break; default: virReportError(VIR_ERR_INTERNAL_ERROR, _("Unknown disk bus: %X"), disk->bus); goto cleanup; } } #define PRLSDK_GET_STAT_PARAM(VAL, TYPE, NAME) \ name = g_strdup_printf("devices.%s%d.%s", prefix, idx, NAME); \ if (prlsdkExtractStatsParam(sdkstats, name, &stats->VAL) < 0) \ goto cleanup; \ VIR_FREE(name); PARALLELS_BLOCK_STATS_FOREACH(PRLSDK_GET_STAT_PARAM) #undef PRLSDK_GET_STAT_PARAM ret = 0; cleanup: VIR_FREE(name); return ret; } static PRL_HANDLE prlsdkFindNetByPath(PRL_HANDLE sdkdom, const char *path) { PRL_UINT32 count = 0; PRL_RESULT pret; size_t i; char *name = NULL; PRL_HANDLE net = PRL_INVALID_HANDLE; pret = PrlVmCfg_GetNetAdaptersCount(sdkdom, &count); prlsdkCheckRetGoto(pret, error); for (i = 0; i < count; ++i) { pret = PrlVmCfg_GetNetAdapter(sdkdom, i, &net); prlsdkCheckRetGoto(pret, error); if (!(name = prlsdkGetStringParamVar(PrlVmDevNet_GetHostInterfaceName, net))) goto error; if (STREQ(name, path)) break; VIR_FREE(name); PrlHandle_Free(net); net = PRL_INVALID_HANDLE; } if (net == PRL_INVALID_HANDLE) virReportError(VIR_ERR_INVALID_ARG, _("invalid path, '%s' is not a known interface"), path); return net; error: VIR_FREE(name); PrlHandle_Free(net); return PRL_INVALID_HANDLE; } int prlsdkGetNetStats(PRL_HANDLE sdkstats, PRL_HANDLE sdkdom, const char *device, virDomainInterfaceStatsPtr stats) { int ret = -1; PRL_UINT32 net_index = -1; char *name = NULL; PRL_RESULT pret; PRL_HANDLE net = PRL_INVALID_HANDLE; virMacAddr mac; if (virMacAddrParse(device, &mac) == 0) net = prlsdkFindNetByMAC(sdkdom, &mac); else net = prlsdkFindNetByPath(sdkdom, device); if (net == PRL_INVALID_HANDLE) goto cleanup; pret = PrlVmDev_GetIndex(net, &net_index); prlsdkCheckRetGoto(pret, cleanup); #define PRLSDK_GET_NET_COUNTER(VAL, NAME) \ name = g_strdup_printf("net.nic%u.%s", net_index, NAME); \ if (prlsdkExtractStatsParam(sdkstats, name, &stats->VAL) < 0) \ goto cleanup; \ VIR_FREE(name); PRLSDK_GET_NET_COUNTER(rx_bytes, "bytes_in") PRLSDK_GET_NET_COUNTER(rx_packets, "pkts_in") PRLSDK_GET_NET_COUNTER(tx_bytes, "bytes_out") PRLSDK_GET_NET_COUNTER(tx_packets, "pkts_out") stats->rx_errs = -1; stats->rx_drop = -1; stats->tx_errs = -1; stats->tx_drop = -1; #undef PRLSDK_GET_NET_COUNTER ret = 0; cleanup: VIR_FREE(name); PrlHandle_Free(net); return ret; } int prlsdkGetVcpuStats(PRL_HANDLE sdkstats, int idx, unsigned long long *vtime) { char *name = NULL; long long ptime = 0; int ret = -1; name = g_strdup_printf("guest.vcpu%u.time", (unsigned int)idx); if (prlsdkExtractStatsParam(sdkstats, name, &ptime) < 0) goto cleanup; *vtime = ptime == -1 ? 0 : ptime; ret = 0; cleanup: VIR_FREE(name); return ret; } int prlsdkGetMemoryStats(PRL_HANDLE sdkstats, virDomainMemoryStatPtr stats, unsigned int nr_stats) { long long v = 0, t = 0, u = 0; size_t i = 0; #define PRLSDK_GET_COUNTER(NAME, VALUE) \ if (prlsdkExtractStatsParam(sdkstats, NAME, &VALUE) < 0) \ return -1; \ #define PRLSDK_MEMORY_STAT_SET(TAG, VALUE) \ if (i < nr_stats) { \ stats[i].tag = (TAG); \ stats[i].val = (VALUE); \ i++; \ } i = 0; // count to kb PRLSDK_GET_COUNTER("guest.ram.swap_in", v) if (v != -1) PRLSDK_MEMORY_STAT_SET(VIR_DOMAIN_MEMORY_STAT_SWAP_IN, v << 12) PRLSDK_GET_COUNTER("guest.ram.swap_out", v) if (v != -1) PRLSDK_MEMORY_STAT_SET(VIR_DOMAIN_MEMORY_STAT_SWAP_OUT, v << 12) PRLSDK_GET_COUNTER("guest.ram.minor_fault", v) if (v != -1) PRLSDK_MEMORY_STAT_SET(VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT, v) PRLSDK_GET_COUNTER("guest.ram.major_fault", v) if (v != -1) PRLSDK_MEMORY_STAT_SET(VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT, v) PRLSDK_GET_COUNTER("guest.ram.total", v) if (v != -1) PRLSDK_MEMORY_STAT_SET(VIR_DOMAIN_MEMORY_STAT_AVAILABLE, v << 10) PRLSDK_GET_COUNTER("guest.ram.balloon_actual", v) if (v != -1) PRLSDK_MEMORY_STAT_SET(VIR_DOMAIN_MEMORY_STAT_ACTUAL_BALLOON, v << 10) PRLSDK_GET_COUNTER("guest.ram.usage", u) PRLSDK_GET_COUNTER("guest.ram.total", t) if (u != -1 && t != -1) PRLSDK_MEMORY_STAT_SET(VIR_DOMAIN_MEMORY_STAT_UNUSED, (t - u) << 10) #undef PRLSDK_GET_COUNTER #undef PRLSDK_MEMORY_STAT_SET return i; } /* memsize is in MiB */ int prlsdkSetMemsize(virDomainObjPtr dom, unsigned int memsize) { vzDomObjPtr privdom = dom->privateData; PRL_HANDLE job; PRL_RESULT pret; job = PrlVm_BeginEdit(privdom->sdkdom); if (PRL_FAILED(waitDomainJob(job, dom))) return -1; pret = PrlVmCfg_SetRamSize(privdom->sdkdom, memsize); prlsdkCheckRetExit(pret, -1); job = PrlVm_CommitEx(privdom->sdkdom, 0); if (PRL_FAILED(waitDomainJob(job, dom))) return -1; return 0; } static long long prlsdkParseDateTime(const char *str) { g_autoptr(GDateTime) then = NULL; g_autoptr(GTimeZone) tz = g_time_zone_new_utc(); char *tmp; int year, mon, mday, hour, min, sec; /* Expect: YYYY-MM-DD HH:MM:SS (%d-%d-%dT%d:%d:%d) eg 2010-11-28 14:29:01 */ if (/* year */ virStrToLong_i(str, &tmp, 10, &year) < 0 || *tmp != '-' || /* month */ virStrToLong_i(tmp+1, &tmp, 10, &mon) < 0 || *tmp != '-' || /* day */ virStrToLong_i(tmp+1, &tmp, 10, &mday) < 0 || *tmp != ' ' || /* hour */ virStrToLong_i(tmp+1, &tmp, 10, &hour) < 0 || *tmp != ':' || /* minute */ virStrToLong_i(tmp+1, &tmp, 10, &min) < 0 || *tmp != ':' || /* second */ virStrToLong_i(tmp+1, &tmp, 10, &sec) < 0 || *tmp != '\0') { virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected DateTime format: '%s'"), str); return -1; } then = g_date_time_new(tz, year, mon, mday, hour, min, sec); return (long long)g_date_time_to_unix(then); } static virDomainSnapshotObjListPtr prlsdkParseSnapshotTree(const char *treexml) { virDomainSnapshotObjListPtr ret = NULL; xmlDocPtr xml = NULL; xmlXPathContextPtr ctxt = NULL; xmlNodePtr root; xmlNodePtr *nodes = NULL; virDomainSnapshotDefPtr def = NULL; virDomainMomentObjPtr snapshot; virDomainSnapshotObjListPtr snapshots = NULL; char *xmlstr = NULL; int n; size_t i; if (!(snapshots = virDomainSnapshotObjListNew())) return NULL; if (*treexml == '\0') return snapshots; if (!(xml = virXMLParse(NULL, treexml, _("(snapshot_tree)")))) goto cleanup; root = xmlDocGetRootElement(xml); if (!virXMLNodeNameEqual(root, "ParallelsSavedStates")) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected root element: '%s'"), root->name); goto cleanup; } if (!(ctxt = virXMLXPathContextNew(xml))) goto cleanup; ctxt->node = root; if ((n = virXPathNodeSet("//SavedStateItem", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot extract snapshot nodes")); goto cleanup; } for (i = 0; i < n; i++) { if (nodes[i]->parent == root) continue; if (VIR_ALLOC(def) < 0) goto cleanup; ctxt->node = nodes[i]; def->parent.name = virXPathString("string(./@guid)", ctxt); if (!def->parent.name) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing 'guid' attribute")); goto cleanup; } def->parent.parent_name = virXPathString("string(../@guid)", ctxt); xmlstr = virXPathString("string(./DateTime)", ctxt); if (!xmlstr) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing 'DateTime' element")); goto cleanup; } if ((def->parent.creationTime = prlsdkParseDateTime(xmlstr)) < 0) goto cleanup; VIR_FREE(xmlstr); def->parent.description = virXPathString("string(./Description)", ctxt); def->memory = VIR_DOMAIN_SNAPSHOT_LOCATION_NONE; xmlstr = virXPathString("string(./@state)", ctxt); if (!xmlstr) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing 'state' attribute")); goto cleanup; } else if (STREQ(xmlstr, "poweron")) { def->state = VIR_DOMAIN_RUNNING; def->memory = VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL; } else if (STREQ(xmlstr, "pause")) { def->state = VIR_DOMAIN_PAUSED; def->memory = VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL; } else if (STREQ(xmlstr, "suspend")) { def->state = VIR_DOMAIN_SHUTOFF; } else if (STREQ(xmlstr, "poweroff")) { def->state = VIR_DOMAIN_SHUTOFF; } else { virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected snapshot state: %s"), xmlstr); } VIR_FREE(xmlstr); if (!(snapshot = virDomainSnapshotAssignDef(snapshots, def))) goto cleanup; def = NULL; xmlstr = virXPathString("string(./@current)", ctxt); if (xmlstr && STREQ("yes", xmlstr)) { if (virDomainSnapshotGetCurrent(snapshots)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("too many current snapshots")); VIR_FREE(xmlstr); goto cleanup; } virDomainSnapshotSetCurrent(snapshots, snapshot); } VIR_FREE(xmlstr); } if (virDomainSnapshotUpdateRelations(snapshots) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("snapshots have inconsistent relations")); goto cleanup; } ret = g_steal_pointer(&snapshots); cleanup: virDomainSnapshotObjListFree(snapshots); VIR_FREE(nodes); VIR_FREE(xmlstr); xmlXPathFreeContext(ctxt); xmlFreeDoc(xml); VIR_FREE(def); return ret; } virDomainSnapshotObjListPtr prlsdkLoadSnapshots(virDomainObjPtr dom) { virDomainSnapshotObjListPtr ret = NULL; PRL_HANDLE job; PRL_HANDLE result = PRL_INVALID_HANDLE; vzDomObjPtr privdom = dom->privateData; char *treexml = NULL; job = PrlVm_GetSnapshotsTreeEx(privdom->sdkdom, PGST_WITHOUT_SCREENSHOTS); if (PRL_FAILED(getDomainJobResult(job, dom, &result))) goto cleanup; if (!(treexml = prlsdkGetStringParamVar(PrlResult_GetParamAsString, result))) goto cleanup; ret = prlsdkParseSnapshotTree(treexml); cleanup: PrlHandle_Free(result); VIR_FREE(treexml); return ret; } int prlsdkCreateSnapshot(virDomainObjPtr dom, const char *description) { vzDomObjPtr privdom = dom->privateData; PRL_HANDLE job; job = PrlVm_CreateSnapshot(privdom->sdkdom, "", description ? : ""); if (PRL_FAILED(waitDomainJob(job, dom))) return -1; return 0; } int prlsdkDeleteSnapshot(virDomainObjPtr dom, const char *uuid, bool children) { vzDomObjPtr privdom = dom->privateData; PRL_HANDLE job; job = PrlVm_DeleteSnapshot(privdom->sdkdom, uuid, children); if (PRL_FAILED(waitDomainJob(job, dom))) return -1; return 0; } int prlsdkSwitchToSnapshot(virDomainObjPtr dom, const char *uuid, bool paused) { vzDomObjPtr privdom = dom->privateData; PRL_HANDLE job; PRL_UINT32 flags = 0; if (paused) flags |= PSSF_SKIP_RESUME; job = PrlVm_SwitchToSnapshotEx(privdom->sdkdom, uuid, flags); if (PRL_FAILED(waitDomainJob(job, dom))) return -1; return 0; } /* high security is default choice for 2 reasons: * 1. as this is the highest set security we can't get * reject from server with high security settings * 2. this is on par with security level of driver * connection to dispatcher */ #define PRLSDK_MIGRATION_FLAGS (PSL_HIGH_SECURITY | PVMT_DONT_CREATE_DISK) int prlsdkMigrate(virDomainObjPtr dom, virURIPtr uri, const unsigned char *session_uuid, const char *dname, unsigned int flags) { vzDomObjPtr privdom = dom->privateData; PRL_HANDLE job = PRL_INVALID_HANDLE; char uuidstr[VIR_UUID_STRING_BRACED_BUFLEN]; PRL_UINT32 vzflags = PRLSDK_MIGRATION_FLAGS; if (flags & VIR_MIGRATE_PAUSED) vzflags |= PVMT_DONT_RESUME_VM; prlsdkUUIDFormat(session_uuid, uuidstr); job = PrlVm_MigrateWithRenameEx(privdom->sdkdom, uri->server, uri->port, uuidstr, dname == NULL ? "" : dname, "", vzflags, 0, PRL_TRUE ); if (PRL_FAILED(waitDomainJob(job, dom))) return -1; return 0; } int prlsdkSetCpuCount(virDomainObjPtr dom, unsigned int count) { vzDomObjPtr privdom = dom->privateData; PRL_HANDLE job; PRL_RESULT pret; job = PrlVm_BeginEdit(privdom->sdkdom); if (PRL_FAILED(waitDomainJob(job, dom))) return -1; pret = PrlVmCfg_SetCpuCount(privdom->sdkdom, count); prlsdkCheckRetExit(pret, -1); job = PrlVm_CommitEx(privdom->sdkdom, 0); if (PRL_FAILED(waitDomainJob(job, dom))) return -1; return 0; } int prlsdkResizeImage(virDomainObjPtr dom, virDomainDiskDefPtr disk, unsigned long long newsize) { int ret = -1; PRL_RESULT pret; vzDomObjPtr privdom = dom->privateData; PRL_UINT32 emulatedType; PRL_HANDLE job = PRL_INVALID_HANDLE; PRL_HANDLE prldisk = PRL_INVALID_HANDLE; prldisk = prlsdkGetDisk(privdom->sdkdom, disk); if (prldisk == PRL_INVALID_HANDLE) goto cleanup; pret = PrlVmDev_GetEmulatedType(prldisk, &emulatedType); prlsdkCheckRetGoto(pret, cleanup); if (emulatedType != PDT_USE_IMAGE_FILE && emulatedType != PDT_USE_FILE_SYSTEM) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("Only disk image supported for resize")); goto cleanup; } job = PrlVmDev_ResizeImage(prldisk, newsize, PRIF_RESIZE_LAST_PARTITION); if (PRL_FAILED(waitDomainJob(job, dom))) goto cleanup; ret = 0; cleanup: PrlHandle_Free(prldisk); return ret; }