/*
* 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 correspoding 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 does'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;
}