2011-02-21 13:40:08 +00:00
|
|
|
/*
|
|
|
|
* xen_sxpr.c: Xen SEXPR parsing functions
|
|
|
|
*
|
|
|
|
* Copyright (C) 2011 Univention GmbH
|
|
|
|
* Copyright (C) 2010-2011 Red Hat, Inc.
|
|
|
|
* Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com>
|
|
|
|
*
|
|
|
|
* 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, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*
|
|
|
|
* Author: Anthony Liguori <aliguori@us.ibm.com>
|
|
|
|
* Author: Daniel Veillard <veillard@redhat.com>
|
|
|
|
* Author: Markus Groß <gross@univention.de>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
#include "virterror_internal.h"
|
|
|
|
#include "conf.h"
|
|
|
|
#include "memory.h"
|
|
|
|
#include "verify.h"
|
|
|
|
#include "uuid.h"
|
|
|
|
#include "logging.h"
|
|
|
|
#include "count-one-bits.h"
|
|
|
|
#include "xenxs_private.h"
|
|
|
|
#include "xen_sxpr.h"
|
|
|
|
|
|
|
|
/* Get a domain id from a sexpr string */
|
|
|
|
int xenGetDomIdFromSxprString(const char *sexpr, int xendConfigVersion)
|
|
|
|
{
|
|
|
|
struct sexpr *root = string2sexpr(sexpr);
|
|
|
|
|
|
|
|
if (!root)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
int id = xenGetDomIdFromSxpr(root, xendConfigVersion);
|
|
|
|
sexpr_free(root);
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get a domain id from a sexpr */
|
|
|
|
int xenGetDomIdFromSxpr(const struct sexpr *root, int xendConfigVersion)
|
|
|
|
{
|
|
|
|
int id = -1;
|
|
|
|
const char * tmp = sexpr_node(root, "domain/domid");
|
|
|
|
if (tmp == NULL && xendConfigVersion < 3) { /* Old XenD, domid was mandatory */
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("domain information incomplete, missing id"));
|
|
|
|
} else {
|
|
|
|
id = tmp ? sexpr_int(root, "domain/domid") : -1;
|
|
|
|
}
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************
|
|
|
|
******
|
|
|
|
****** Parsing of SEXPR into virDomainDef objects
|
|
|
|
******
|
|
|
|
*****************************************************************/
|
|
|
|
|
|
|
|
/**
|
2011-02-21 13:40:12 +00:00
|
|
|
* xenParseSxprOS
|
2011-02-21 13:40:08 +00:00
|
|
|
* @node: the root of the parsed S-Expression
|
|
|
|
* @def: the domain config
|
|
|
|
* @hvm: true or 1 if no contains HVM S-Expression
|
|
|
|
* @bootloader: true or 1 if a bootloader is defined
|
|
|
|
*
|
|
|
|
* Parse the xend sexp for description of os and append it to buf.
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success and -1 in case of error
|
|
|
|
*/
|
|
|
|
static int
|
2011-02-21 13:40:12 +00:00
|
|
|
xenParseSxprOS(const struct sexpr *node,
|
|
|
|
virDomainDefPtr def,
|
|
|
|
int hvm)
|
2011-02-21 13:40:08 +00:00
|
|
|
{
|
|
|
|
if (hvm) {
|
|
|
|
if (sexpr_node_copy(node, "domain/image/hvm/loader", &def->os.loader) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
if (def->os.loader == NULL) {
|
|
|
|
if (sexpr_node_copy(node, "domain/image/hvm/kernel", &def->os.loader) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (def->os.loader == NULL) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("domain information incomplete, missing HVM loader"));
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (sexpr_node_copy(node, "domain/image/hvm/kernel", &def->os.kernel) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
if (sexpr_node_copy(node, "domain/image/hvm/ramdisk", &def->os.initrd) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
if (sexpr_node_copy(node, "domain/image/hvm/args", &def->os.cmdline) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
if (sexpr_node_copy(node, "domain/image/hvm/root", &def->os.root) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (sexpr_node_copy(node, "domain/image/linux/kernel", &def->os.kernel) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
if (sexpr_node_copy(node, "domain/image/linux/ramdisk", &def->os.initrd) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
if (sexpr_node_copy(node, "domain/image/linux/args", &def->os.cmdline) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
if (sexpr_node_copy(node, "domain/image/linux/root", &def->os.root) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If HVM kenrel == loader, then old xend, so kill off kernel */
|
|
|
|
if (hvm &&
|
|
|
|
def->os.kernel &&
|
|
|
|
STREQ(def->os.kernel, def->os.loader)) {
|
|
|
|
VIR_FREE(def->os.kernel);
|
|
|
|
}
|
2011-10-10 16:22:44 +00:00
|
|
|
/* Drop kernel argument that has no value */
|
|
|
|
if (hvm &&
|
|
|
|
def->os.kernel && *def->os.kernel == '\0' &&
|
|
|
|
def->os.loader) {
|
|
|
|
VIR_FREE(def->os.kernel);
|
|
|
|
}
|
2011-02-21 13:40:08 +00:00
|
|
|
|
|
|
|
if (!def->os.kernel &&
|
|
|
|
hvm) {
|
|
|
|
const char *boot = sexpr_node(node, "domain/image/hvm/boot");
|
|
|
|
if ((boot != NULL) && (boot[0] != 0)) {
|
|
|
|
while (*boot &&
|
|
|
|
def->os.nBootDevs < VIR_DOMAIN_BOOT_LAST) {
|
|
|
|
if (*boot == 'a')
|
|
|
|
def->os.bootDevs[def->os.nBootDevs++] = VIR_DOMAIN_BOOT_FLOPPY;
|
|
|
|
else if (*boot == 'c')
|
|
|
|
def->os.bootDevs[def->os.nBootDevs++] = VIR_DOMAIN_BOOT_DISK;
|
|
|
|
else if (*boot == 'd')
|
|
|
|
def->os.bootDevs[def->os.nBootDevs++] = VIR_DOMAIN_BOOT_CDROM;
|
|
|
|
else if (*boot == 'n')
|
|
|
|
def->os.bootDevs[def->os.nBootDevs++] = VIR_DOMAIN_BOOT_NET;
|
|
|
|
boot++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hvm &&
|
|
|
|
!def->os.kernel &&
|
|
|
|
!def->os.bootloader) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("domain information incomplete, missing kernel & bootloader"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
virDomainChrDefPtr
|
2011-02-21 13:40:12 +00:00
|
|
|
xenParseSxprChar(const char *value,
|
|
|
|
const char *tty)
|
2011-02-21 13:40:08 +00:00
|
|
|
{
|
|
|
|
const char *prefix;
|
|
|
|
char *tmp;
|
|
|
|
virDomainChrDefPtr def;
|
|
|
|
|
2011-04-14 16:05:14 +00:00
|
|
|
if (!(def = virDomainChrDefNew()))
|
2011-02-21 13:40:08 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
prefix = value;
|
|
|
|
|
|
|
|
if (value[0] == '/') {
|
|
|
|
def->source.type = VIR_DOMAIN_CHR_TYPE_DEV;
|
2011-02-25 14:41:12 +00:00
|
|
|
def->source.data.file.path = strdup(value);
|
|
|
|
if (!def->source.data.file.path)
|
|
|
|
goto no_memory;
|
2011-02-21 13:40:08 +00:00
|
|
|
} else {
|
|
|
|
if ((tmp = strchr(value, ':')) != NULL) {
|
|
|
|
*tmp = '\0';
|
|
|
|
value = tmp + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STRPREFIX(prefix, "telnet")) {
|
|
|
|
def->source.type = VIR_DOMAIN_CHR_TYPE_TCP;
|
|
|
|
def->source.data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET;
|
|
|
|
} else {
|
|
|
|
if ((def->source.type = virDomainChrTypeFromString(prefix)) < 0) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unknown chr device type '%s'"), prefix);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (def->source.type) {
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_PTY:
|
|
|
|
if (tty != NULL &&
|
|
|
|
!(def->source.data.file.path = strdup(tty)))
|
|
|
|
goto no_memory;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_FILE:
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_PIPE:
|
|
|
|
if (!(def->source.data.file.path = strdup(value)))
|
|
|
|
goto no_memory;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_TCP:
|
|
|
|
{
|
|
|
|
const char *offset = strchr(value, ':');
|
|
|
|
const char *offset2;
|
|
|
|
|
|
|
|
if (offset == NULL) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("malformed char device string"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset != value &&
|
|
|
|
(def->source.data.tcp.host = strndup(value,
|
|
|
|
offset - value)) == NULL)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
offset2 = strchr(offset, ',');
|
|
|
|
if (offset2 == NULL)
|
|
|
|
def->source.data.tcp.service = strdup(offset+1);
|
|
|
|
else
|
|
|
|
def->source.data.tcp.service = strndup(offset+1,
|
|
|
|
offset2-(offset+1));
|
|
|
|
if (def->source.data.tcp.service == NULL)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (offset2 && strstr(offset2, ",server"))
|
|
|
|
def->source.data.tcp.listen = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_UDP:
|
|
|
|
{
|
|
|
|
const char *offset = strchr(value, ':');
|
|
|
|
const char *offset2, *offset3;
|
|
|
|
|
|
|
|
if (offset == NULL) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("malformed char device string"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset != value &&
|
|
|
|
(def->source.data.udp.connectHost
|
|
|
|
= strndup(value, offset - value)) == NULL)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
offset2 = strchr(offset, '@');
|
|
|
|
if (offset2 != NULL) {
|
|
|
|
if ((def->source.data.udp.connectService
|
|
|
|
= strndup(offset + 1, offset2-(offset+1))) == NULL)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
offset3 = strchr(offset2, ':');
|
|
|
|
if (offset3 == NULL) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("malformed char device string"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset3 > (offset2 + 1) &&
|
|
|
|
(def->source.data.udp.bindHost
|
|
|
|
= strndup(offset2 + 1, offset3 - (offset2+1))) == NULL)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if ((def->source.data.udp.bindService
|
|
|
|
= strdup(offset3 + 1)) == NULL)
|
|
|
|
goto no_memory;
|
|
|
|
} else {
|
|
|
|
if ((def->source.data.udp.connectService
|
|
|
|
= strdup(offset + 1)) == NULL)
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_UNIX:
|
|
|
|
{
|
|
|
|
const char *offset = strchr(value, ',');
|
|
|
|
if (offset)
|
|
|
|
def->source.data.nix.path = strndup(value, (offset - value));
|
|
|
|
else
|
|
|
|
def->source.data.nix.path = strdup(value);
|
|
|
|
if (def->source.data.nix.path == NULL)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (offset != NULL &&
|
|
|
|
strstr(offset, ",server") != NULL)
|
|
|
|
def->source.data.nix.listen = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return def;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
error:
|
|
|
|
virDomainChrDefFree(def);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* xend_parse_sexp_desc_disks
|
|
|
|
* @conn: connection
|
|
|
|
* @root: root sexpr
|
|
|
|
* @xendConfigVersion: version of xend
|
|
|
|
*
|
|
|
|
* This parses out block devices from the domain sexpr
|
|
|
|
*
|
|
|
|
* Returns 0 if successful or -1 if failed.
|
|
|
|
*/
|
|
|
|
static int
|
2011-02-21 13:40:12 +00:00
|
|
|
xenParseSxprDisks(virDomainDefPtr def,
|
|
|
|
const struct sexpr *root,
|
|
|
|
int hvm,
|
|
|
|
int xendConfigVersion)
|
2011-02-21 13:40:08 +00:00
|
|
|
{
|
|
|
|
const struct sexpr *cur, *node;
|
|
|
|
virDomainDiskDefPtr disk = NULL;
|
|
|
|
|
|
|
|
for (cur = root; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) {
|
|
|
|
node = cur->u.s.car;
|
|
|
|
/* Normally disks are in a (device (vbd ...)) block
|
|
|
|
but blktap disks ended up in a differently named
|
|
|
|
(device (tap ....)) block.... */
|
|
|
|
if (sexpr_lookup(node, "device/vbd") ||
|
|
|
|
sexpr_lookup(node, "device/tap") ||
|
|
|
|
sexpr_lookup(node, "device/tap2")) {
|
|
|
|
char *offset;
|
|
|
|
const char *src = NULL;
|
|
|
|
const char *dst = NULL;
|
|
|
|
const char *mode = NULL;
|
xen: fix PyGrub boot device order
When PyGrub is used as the bootloader in Xen, it gets passed the first
bootable disk. Xend supports a "bootable"-flag for this, which isn't
explicitly supported by libvirt.
When converting libvirt-xml to xen-sxpr the "bootable"-flag gets
implicitly set by xen.xend.XenConfig.device_add() for the first disk
(marked as "Compat hack -- mark first disk bootable").
When converting back xen-sxpr to libvirt-xml, the disks are returned in
the internal order used by Xend ignoring the "bootable"-flag, which
loses the original order. When the domain is then re-defined, the order
of disks is changed, which breaks PyGrub, since a different disk gets
passed.
When converting xen-sxpr to libvirt-xml, use the "bootable"-flag to
determine the first disk.
This isn't perfect, since several disks can be marked as bootable using
the Xend-API, but that is not supported by libvirt. In all known cases
relevant to libvirt exactly one disk is marked as bootable.
Signed-off-by: Philipp Hahn <hahn@univention.de>
2011-10-12 08:26:46 +00:00
|
|
|
const char *bootable = NULL;
|
2011-02-21 13:40:08 +00:00
|
|
|
|
|
|
|
/* Again dealing with (vbd...) vs (tap ...) differences */
|
|
|
|
if (sexpr_lookup(node, "device/vbd")) {
|
|
|
|
src = sexpr_node(node, "device/vbd/uname");
|
|
|
|
dst = sexpr_node(node, "device/vbd/dev");
|
|
|
|
mode = sexpr_node(node, "device/vbd/mode");
|
xen: fix PyGrub boot device order
When PyGrub is used as the bootloader in Xen, it gets passed the first
bootable disk. Xend supports a "bootable"-flag for this, which isn't
explicitly supported by libvirt.
When converting libvirt-xml to xen-sxpr the "bootable"-flag gets
implicitly set by xen.xend.XenConfig.device_add() for the first disk
(marked as "Compat hack -- mark first disk bootable").
When converting back xen-sxpr to libvirt-xml, the disks are returned in
the internal order used by Xend ignoring the "bootable"-flag, which
loses the original order. When the domain is then re-defined, the order
of disks is changed, which breaks PyGrub, since a different disk gets
passed.
When converting xen-sxpr to libvirt-xml, use the "bootable"-flag to
determine the first disk.
This isn't perfect, since several disks can be marked as bootable using
the Xend-API, but that is not supported by libvirt. In all known cases
relevant to libvirt exactly one disk is marked as bootable.
Signed-off-by: Philipp Hahn <hahn@univention.de>
2011-10-12 08:26:46 +00:00
|
|
|
bootable = sexpr_node(node, "device/vbd/bootable");
|
2011-02-21 13:40:08 +00:00
|
|
|
} else if (sexpr_lookup(node, "device/tap2")) {
|
|
|
|
src = sexpr_node(node, "device/tap2/uname");
|
|
|
|
dst = sexpr_node(node, "device/tap2/dev");
|
|
|
|
mode = sexpr_node(node, "device/tap2/mode");
|
xen: fix PyGrub boot device order
When PyGrub is used as the bootloader in Xen, it gets passed the first
bootable disk. Xend supports a "bootable"-flag for this, which isn't
explicitly supported by libvirt.
When converting libvirt-xml to xen-sxpr the "bootable"-flag gets
implicitly set by xen.xend.XenConfig.device_add() for the first disk
(marked as "Compat hack -- mark first disk bootable").
When converting back xen-sxpr to libvirt-xml, the disks are returned in
the internal order used by Xend ignoring the "bootable"-flag, which
loses the original order. When the domain is then re-defined, the order
of disks is changed, which breaks PyGrub, since a different disk gets
passed.
When converting xen-sxpr to libvirt-xml, use the "bootable"-flag to
determine the first disk.
This isn't perfect, since several disks can be marked as bootable using
the Xend-API, but that is not supported by libvirt. In all known cases
relevant to libvirt exactly one disk is marked as bootable.
Signed-off-by: Philipp Hahn <hahn@univention.de>
2011-10-12 08:26:46 +00:00
|
|
|
bootable = sexpr_node(node, "device/tap2/bootable");
|
2011-02-21 13:40:08 +00:00
|
|
|
} else {
|
|
|
|
src = sexpr_node(node, "device/tap/uname");
|
|
|
|
dst = sexpr_node(node, "device/tap/dev");
|
|
|
|
mode = sexpr_node(node, "device/tap/mode");
|
xen: fix PyGrub boot device order
When PyGrub is used as the bootloader in Xen, it gets passed the first
bootable disk. Xend supports a "bootable"-flag for this, which isn't
explicitly supported by libvirt.
When converting libvirt-xml to xen-sxpr the "bootable"-flag gets
implicitly set by xen.xend.XenConfig.device_add() for the first disk
(marked as "Compat hack -- mark first disk bootable").
When converting back xen-sxpr to libvirt-xml, the disks are returned in
the internal order used by Xend ignoring the "bootable"-flag, which
loses the original order. When the domain is then re-defined, the order
of disks is changed, which breaks PyGrub, since a different disk gets
passed.
When converting xen-sxpr to libvirt-xml, use the "bootable"-flag to
determine the first disk.
This isn't perfect, since several disks can be marked as bootable using
the Xend-API, but that is not supported by libvirt. In all known cases
relevant to libvirt exactly one disk is marked as bootable.
Signed-off-by: Philipp Hahn <hahn@univention.de>
2011-10-12 08:26:46 +00:00
|
|
|
bootable = sexpr_node(node, "device/tap/bootable");
|
2011-02-21 13:40:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_ALLOC(disk) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (dst == NULL) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("domain information incomplete, vbd has no dev"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (src == NULL) {
|
|
|
|
/* There is a case without the uname to the CD-ROM device */
|
|
|
|
offset = strchr(dst, ':');
|
|
|
|
if (!offset ||
|
|
|
|
!hvm ||
|
|
|
|
STRNEQ(offset, ":cdrom")) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("domain information incomplete, vbd has no src"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (src != NULL) {
|
|
|
|
offset = strchr(src, ':');
|
|
|
|
if (!offset) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("cannot parse vbd filename, missing driver name"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2011-10-12 08:55:37 +00:00
|
|
|
if (sexpr_lookup(node, "device/tap2") &&
|
|
|
|
STRPREFIX(src, "tap:")) {
|
|
|
|
if (!(disk->driverName = strdup("tap2")))
|
|
|
|
goto no_memory;
|
|
|
|
} else {
|
|
|
|
if (VIR_ALLOC_N(disk->driverName, (offset-src)+1) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
if (virStrncpy(disk->driverName, src, offset-src,
|
|
|
|
(offset-src)+1) == NULL) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Driver name %s too big for destination"),
|
|
|
|
src);
|
|
|
|
goto error;
|
|
|
|
}
|
2011-02-21 13:40:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
src = offset + 1;
|
|
|
|
|
|
|
|
if (STREQ (disk->driverName, "tap") ||
|
|
|
|
STREQ (disk->driverName, "tap2")) {
|
|
|
|
offset = strchr(src, ':');
|
|
|
|
if (!offset) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("cannot parse vbd filename, missing driver type"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_ALLOC_N(disk->driverType, (offset-src)+1)< 0)
|
|
|
|
goto no_memory;
|
|
|
|
if (virStrncpy(disk->driverType, src, offset-src,
|
|
|
|
(offset-src)+1) == NULL) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Driver type %s too big for destination"),
|
|
|
|
src);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
src = offset + 1;
|
|
|
|
/* Its possible to use blktap driver for block devs
|
|
|
|
too, but kinda pointless because blkback is better,
|
|
|
|
so we assume common case here. If blktap becomes
|
|
|
|
omnipotent, we can revisit this, perhaps stat()'ing
|
|
|
|
the src file in question */
|
|
|
|
disk->type = VIR_DOMAIN_DISK_TYPE_FILE;
|
|
|
|
} else if (STREQ(disk->driverName, "phy")) {
|
|
|
|
disk->type = VIR_DOMAIN_DISK_TYPE_BLOCK;
|
|
|
|
} else if (STREQ(disk->driverName, "file")) {
|
|
|
|
disk->type = VIR_DOMAIN_DISK_TYPE_FILE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* No CDROM media so can't really tell. We'll just
|
|
|
|
call if a FILE for now and update when media
|
|
|
|
is inserted later */
|
|
|
|
disk->type = VIR_DOMAIN_DISK_TYPE_FILE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STREQLEN (dst, "ioemu:", 6))
|
|
|
|
dst += 6;
|
|
|
|
|
|
|
|
disk->device = VIR_DOMAIN_DISK_DEVICE_DISK;
|
|
|
|
/* New style disk config from Xen >= 3.0.3 */
|
|
|
|
if (xendConfigVersion > 1) {
|
|
|
|
offset = strrchr(dst, ':');
|
|
|
|
if (offset) {
|
|
|
|
if (STREQ (offset, ":cdrom")) {
|
|
|
|
disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM;
|
|
|
|
} else if (STREQ (offset, ":disk")) {
|
|
|
|
/* The default anyway */
|
|
|
|
} else {
|
|
|
|
/* Unknown, lets pretend its a disk too */
|
|
|
|
}
|
|
|
|
offset[0] = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(disk->dst = strdup(dst)))
|
|
|
|
goto no_memory;
|
|
|
|
if (src &&
|
|
|
|
!(disk->src = strdup(src)))
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (STRPREFIX(disk->dst, "xvd"))
|
|
|
|
disk->bus = VIR_DOMAIN_DISK_BUS_XEN;
|
|
|
|
else if (STRPREFIX(disk->dst, "hd"))
|
|
|
|
disk->bus = VIR_DOMAIN_DISK_BUS_IDE;
|
|
|
|
else if (STRPREFIX(disk->dst, "sd"))
|
|
|
|
disk->bus = VIR_DOMAIN_DISK_BUS_SCSI;
|
|
|
|
else
|
|
|
|
disk->bus = VIR_DOMAIN_DISK_BUS_IDE;
|
|
|
|
|
|
|
|
if (mode &&
|
|
|
|
strchr(mode, 'r'))
|
|
|
|
disk->readonly = 1;
|
|
|
|
if (mode &&
|
|
|
|
strchr(mode, '!'))
|
|
|
|
disk->shared = 1;
|
|
|
|
|
|
|
|
if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
xen: fix PyGrub boot device order
When PyGrub is used as the bootloader in Xen, it gets passed the first
bootable disk. Xend supports a "bootable"-flag for this, which isn't
explicitly supported by libvirt.
When converting libvirt-xml to xen-sxpr the "bootable"-flag gets
implicitly set by xen.xend.XenConfig.device_add() for the first disk
(marked as "Compat hack -- mark first disk bootable").
When converting back xen-sxpr to libvirt-xml, the disks are returned in
the internal order used by Xend ignoring the "bootable"-flag, which
loses the original order. When the domain is then re-defined, the order
of disks is changed, which breaks PyGrub, since a different disk gets
passed.
When converting xen-sxpr to libvirt-xml, use the "bootable"-flag to
determine the first disk.
This isn't perfect, since several disks can be marked as bootable using
the Xend-API, but that is not supported by libvirt. In all known cases
relevant to libvirt exactly one disk is marked as bootable.
Signed-off-by: Philipp Hahn <hahn@univention.de>
2011-10-12 08:26:46 +00:00
|
|
|
/* re-order disks if there is a bootable device */
|
|
|
|
if (STREQ_NULLABLE(bootable, "1")) {
|
|
|
|
def->disks[def->ndisks++] = def->disks[0];
|
|
|
|
def->disks[0] = disk;
|
|
|
|
} else {
|
|
|
|
def->disks[def->ndisks++] = disk;
|
|
|
|
}
|
2011-02-21 13:40:08 +00:00
|
|
|
disk = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
|
|
|
|
error:
|
|
|
|
virDomainDiskDefFree(disk);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2011-02-21 13:40:12 +00:00
|
|
|
xenParseSxprNets(virDomainDefPtr def,
|
|
|
|
const struct sexpr *root)
|
2011-02-21 13:40:08 +00:00
|
|
|
{
|
|
|
|
virDomainNetDefPtr net = NULL;
|
|
|
|
const struct sexpr *cur, *node;
|
|
|
|
const char *tmp;
|
|
|
|
int vif_index = 0;
|
|
|
|
|
|
|
|
for (cur = root; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) {
|
|
|
|
node = cur->u.s.car;
|
|
|
|
if (sexpr_lookup(node, "device/vif")) {
|
|
|
|
const char *tmp2, *model, *type;
|
|
|
|
tmp2 = sexpr_node(node, "device/vif/script");
|
|
|
|
tmp = sexpr_node(node, "device/vif/bridge");
|
|
|
|
model = sexpr_node(node, "device/vif/model");
|
|
|
|
type = sexpr_node(node, "device/vif/type");
|
|
|
|
|
|
|
|
if (VIR_ALLOC(net) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (tmp != NULL ||
|
|
|
|
(tmp2 != NULL && STREQ(tmp2, DEFAULT_VIF_SCRIPT))) {
|
|
|
|
net->type = VIR_DOMAIN_NET_TYPE_BRIDGE;
|
|
|
|
/* XXX virtual network reverse resolve */
|
|
|
|
|
|
|
|
if (tmp &&
|
|
|
|
!(net->data.bridge.brname = strdup(tmp)))
|
|
|
|
goto no_memory;
|
|
|
|
if (tmp2 &&
|
|
|
|
net->type == VIR_DOMAIN_NET_TYPE_BRIDGE &&
|
|
|
|
!(net->data.bridge.script = strdup(tmp2)))
|
|
|
|
goto no_memory;
|
|
|
|
tmp = sexpr_node(node, "device/vif/ip");
|
|
|
|
if (tmp &&
|
|
|
|
!(net->data.bridge.ipaddr = strdup(tmp)))
|
|
|
|
goto no_memory;
|
|
|
|
} else {
|
|
|
|
net->type = VIR_DOMAIN_NET_TYPE_ETHERNET;
|
|
|
|
if (tmp2 &&
|
|
|
|
!(net->data.ethernet.script = strdup(tmp2)))
|
|
|
|
goto no_memory;
|
|
|
|
tmp = sexpr_node(node, "device/vif/ip");
|
|
|
|
if (tmp &&
|
|
|
|
!(net->data.ethernet.ipaddr = strdup(tmp)))
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = sexpr_node(node, "device/vif/vifname");
|
2011-04-27 22:39:37 +00:00
|
|
|
/* If vifname is specified in xend config, include it in net
|
|
|
|
* definition regardless of domain state. If vifname is not
|
|
|
|
* specified, only generate one if domain is active (id != -1). */
|
|
|
|
if (tmp) {
|
|
|
|
if (!(net->ifname = strdup(tmp)))
|
|
|
|
goto no_memory;
|
|
|
|
} else if (def->id != -1) {
|
|
|
|
if (virAsprintf(&net->ifname, "vif%d.%d", def->id, vif_index) < 0)
|
|
|
|
goto no_memory;
|
2011-02-21 13:40:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
tmp = sexpr_node(node, "device/vif/mac");
|
|
|
|
if (tmp) {
|
|
|
|
if (virParseMacAddr(tmp, net->mac) < 0) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("malformed mac address '%s'"), tmp);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (model &&
|
|
|
|
!(net->model = strdup(model)))
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (!model && type &&
|
|
|
|
STREQ(type, "netfront") &&
|
|
|
|
!(net->model = strdup("netfront")))
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (VIR_REALLOC_N(def->nets, def->nnets + 1) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
def->nets[def->nnets++] = net;
|
|
|
|
vif_index++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
cleanup:
|
|
|
|
virDomainNetDefFree(net);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
2011-02-21 13:40:12 +00:00
|
|
|
xenParseSxprSound(virDomainDefPtr def,
|
|
|
|
const char *str)
|
2011-02-21 13:40:08 +00:00
|
|
|
{
|
|
|
|
if (STREQ(str, "all")) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Special compatability code for Xen with a bogus
|
|
|
|
* sound=all in config.
|
|
|
|
*
|
2011-08-23 17:02:02 +00:00
|
|
|
* NB deliberately, don't include all possible
|
2011-02-21 13:40:08 +00:00
|
|
|
* sound models anymore, just the 2 that were
|
|
|
|
* historically present in Xen's QEMU.
|
|
|
|
*
|
|
|
|
* ie just es1370 + sb16.
|
|
|
|
*
|
|
|
|
* Hence use of MODEL_ES1370 + 1, instead of MODEL_LAST
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (VIR_ALLOC_N(def->sounds,
|
|
|
|
VIR_DOMAIN_SOUND_MODEL_ES1370 + 1) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
|
|
|
|
for (i = 0 ; i < (VIR_DOMAIN_SOUND_MODEL_ES1370 + 1) ; i++) {
|
|
|
|
virDomainSoundDefPtr sound;
|
|
|
|
if (VIR_ALLOC(sound) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
sound->model = i;
|
|
|
|
def->sounds[def->nsounds++] = sound;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
char model[10];
|
|
|
|
const char *offset = str, *offset2;
|
|
|
|
|
|
|
|
do {
|
|
|
|
int len;
|
|
|
|
virDomainSoundDefPtr sound;
|
|
|
|
offset2 = strchr(offset, ',');
|
|
|
|
if (offset2)
|
|
|
|
len = (offset2 - offset);
|
|
|
|
else
|
|
|
|
len = strlen(offset);
|
|
|
|
if (virStrncpy(model, offset, len, sizeof(model)) == NULL) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Sound model %s too big for destination"),
|
|
|
|
offset);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_ALLOC(sound) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if ((sound->model = virDomainSoundModelTypeFromString(model)) < 0) {
|
|
|
|
VIR_FREE(sound);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_REALLOC_N(def->sounds, def->nsounds+1) < 0) {
|
|
|
|
virDomainSoundDefFree(sound);
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
|
|
|
|
def->sounds[def->nsounds++] = sound;
|
|
|
|
offset = offset2 ? offset2 + 1 : NULL;
|
|
|
|
} while (offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
error:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2011-02-21 13:40:12 +00:00
|
|
|
xenParseSxprUSB(virDomainDefPtr def,
|
|
|
|
const struct sexpr *root)
|
2011-02-21 13:40:08 +00:00
|
|
|
{
|
|
|
|
struct sexpr *cur, *node;
|
|
|
|
const char *tmp;
|
|
|
|
|
|
|
|
for (cur = sexpr_lookup(root, "domain/image/hvm"); cur && cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) {
|
|
|
|
node = cur->u.s.car;
|
|
|
|
if (sexpr_lookup(node, "usbdevice")) {
|
|
|
|
tmp = sexpr_node(node, "usbdevice");
|
|
|
|
if (tmp && *tmp) {
|
|
|
|
if (STREQ(tmp, "tablet") ||
|
|
|
|
STREQ(tmp, "mouse")) {
|
|
|
|
virDomainInputDefPtr input;
|
|
|
|
if (VIR_ALLOC(input) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
input->bus = VIR_DOMAIN_INPUT_BUS_USB;
|
|
|
|
if (STREQ(tmp, "tablet"))
|
|
|
|
input->type = VIR_DOMAIN_INPUT_TYPE_TABLET;
|
|
|
|
else
|
|
|
|
input->type = VIR_DOMAIN_INPUT_TYPE_MOUSE;
|
|
|
|
|
|
|
|
if (VIR_REALLOC_N(def->inputs, def->ninputs+1) < 0) {
|
|
|
|
VIR_FREE(input);
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
def->inputs[def->ninputs++] = input;
|
|
|
|
} else {
|
|
|
|
/* XXX Handle other non-input USB devices later */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2011-02-21 13:40:12 +00:00
|
|
|
xenParseSxprGraphicsOld(virDomainDefPtr def,
|
|
|
|
const struct sexpr *root,
|
|
|
|
int hvm,
|
|
|
|
int xendConfigVersion, int vncport)
|
2011-02-21 13:40:08 +00:00
|
|
|
{
|
|
|
|
const char *tmp;
|
|
|
|
virDomainGraphicsDefPtr graphics = NULL;
|
|
|
|
|
|
|
|
if ((tmp = sexpr_fmt_node(root, "domain/image/%s/vnc", hvm ? "hvm" : "linux")) &&
|
|
|
|
tmp[0] == '1') {
|
|
|
|
/* Graphics device (HVM, or old (pre-3.0.4) style PV VNC config) */
|
|
|
|
int port;
|
|
|
|
const char *listenAddr = sexpr_fmt_node(root, "domain/image/%s/vnclisten", hvm ? "hvm" : "linux");
|
|
|
|
const char *vncPasswd = sexpr_fmt_node(root, "domain/image/%s/vncpasswd", hvm ? "hvm" : "linux");
|
|
|
|
const char *keymap = sexpr_fmt_node(root, "domain/image/%s/keymap", hvm ? "hvm" : "linux");
|
|
|
|
const char *unused = sexpr_fmt_node(root, "domain/image/%s/vncunused", hvm ? "hvm" : "linux");
|
|
|
|
|
|
|
|
port = vncport;
|
|
|
|
|
|
|
|
if (VIR_ALLOC(graphics) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
graphics->type = VIR_DOMAIN_GRAPHICS_TYPE_VNC;
|
|
|
|
/* For Xen >= 3.0.3, don't generate a fixed port mapping
|
|
|
|
* because it will almost certainly be wrong ! Just leave
|
|
|
|
* it as -1 which lets caller see that the VNC server isn't
|
|
|
|
* present yet. Subsquent dumps of the XML will eventually
|
|
|
|
* find the port in XenStore once VNC server has started
|
|
|
|
*/
|
|
|
|
if (port == -1 && xendConfigVersion < 2)
|
|
|
|
port = 5900 + def->id;
|
|
|
|
|
|
|
|
if ((unused && STREQ(unused, "1")) || port == -1)
|
|
|
|
graphics->data.vnc.autoport = 1;
|
|
|
|
graphics->data.vnc.port = port;
|
|
|
|
|
|
|
|
if (listenAddr &&
|
conf: add <listen> subelement to domain <graphics> element
Once it's plugged in, the <listen> element will be an optional
replacement for the "listen" attribute that graphics elements already
have. If the <listen> element is type='address', it will have an
attribute called 'address' which will contain an IP address or dns
name that the guest's display server should listen on. If, however,
type='network', the <listen> element should have an attribute called
'network' that will be set to the name of a network configuration to
get the IP address from.
* docs/schemas/domain.rng: updated to allow the <listen> element
* docs/formatdomain.html.in: document the <listen> element and its
attributes.
* src/conf/domain_conf.[hc]:
1) The domain parser, formatter, and data structure are modified to
support 0 or more <listen> subelements to each <graphics>
element. The old style "legacy" listen attribute is also still
accepted, and will be stored internally just as if it were a
separate <listen> element. On output (i.e. format), the address
attribute of the first <listen> element of type 'address' will be
duplicated in the legacy "listen" attribute of the <graphic>
element.
2) The "listenAddr" attribute has been removed from the unions in
virDomainGRaphicsDef for graphics types vnc, rdp, and spice.
This attribute is now in the <listen> subelement (aka
virDomainGraphicsListenDef)
3) Helper functions were written to provide simple access
(both Get and Set) to the listen elements and their attributes.
* src/libvirt_private.syms: export the listen helper functions
* src/qemu/qemu_command.c, src/qemu/qemu_hotplug.c,
src/qemu/qemu_migration.c, src/vbox/vbox_tmpl.c,
src/vmx/vmx.c, src/xenxs/xen_sxpr.c, src/xenxs/xen_xm.c
Modify all these files to use the listen helper functions rather
than directly referencing the (now missing) listenAddr
attribute. There can be multiple <listen> elements to a single
<graphics>, but the drivers all currently only support one, so all
replacements of direct access with a helper function indicate index
"0".
* tests/* - only 3 of these are new files added explicitly to test the
new <listen> element. All the others have been modified to reflect
the fact that any legacy "listen" attributes passed in to the domain
parse will be saved in a <listen> element (i.e. one of the
virDomainGraphicsListenDefs), and during the domain format function,
both the <listen> element as well as the legacy attributes will be
output.
2011-07-07 04:20:28 +00:00
|
|
|
virDomainGraphicsListenSetAddress(graphics, 0, listenAddr, -1, true))
|
|
|
|
goto error;
|
2011-02-21 13:40:08 +00:00
|
|
|
|
|
|
|
if (vncPasswd &&
|
|
|
|
!(graphics->data.vnc.auth.passwd = strdup(vncPasswd)))
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (keymap &&
|
|
|
|
!(graphics->data.vnc.keymap = strdup(keymap)))
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (VIR_ALLOC_N(def->graphics, 1) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
def->graphics[0] = graphics;
|
|
|
|
def->ngraphics = 1;
|
|
|
|
graphics = NULL;
|
|
|
|
} else if ((tmp = sexpr_fmt_node(root, "domain/image/%s/sdl", hvm ? "hvm" : "linux")) &&
|
|
|
|
tmp[0] == '1') {
|
|
|
|
/* Graphics device (HVM, or old (pre-3.0.4) style PV sdl config) */
|
|
|
|
const char *display = sexpr_fmt_node(root, "domain/image/%s/display", hvm ? "hvm" : "linux");
|
|
|
|
const char *xauth = sexpr_fmt_node(root, "domain/image/%s/xauthority", hvm ? "hvm" : "linux");
|
|
|
|
|
|
|
|
if (VIR_ALLOC(graphics) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
graphics->type = VIR_DOMAIN_GRAPHICS_TYPE_SDL;
|
|
|
|
if (display &&
|
|
|
|
!(graphics->data.sdl.display = strdup(display)))
|
|
|
|
goto no_memory;
|
|
|
|
if (xauth &&
|
|
|
|
!(graphics->data.sdl.xauth = strdup(xauth)))
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (VIR_ALLOC_N(def->graphics, 1) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
def->graphics[0] = graphics;
|
|
|
|
def->ngraphics = 1;
|
|
|
|
graphics = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
conf: add <listen> subelement to domain <graphics> element
Once it's plugged in, the <listen> element will be an optional
replacement for the "listen" attribute that graphics elements already
have. If the <listen> element is type='address', it will have an
attribute called 'address' which will contain an IP address or dns
name that the guest's display server should listen on. If, however,
type='network', the <listen> element should have an attribute called
'network' that will be set to the name of a network configuration to
get the IP address from.
* docs/schemas/domain.rng: updated to allow the <listen> element
* docs/formatdomain.html.in: document the <listen> element and its
attributes.
* src/conf/domain_conf.[hc]:
1) The domain parser, formatter, and data structure are modified to
support 0 or more <listen> subelements to each <graphics>
element. The old style "legacy" listen attribute is also still
accepted, and will be stored internally just as if it were a
separate <listen> element. On output (i.e. format), the address
attribute of the first <listen> element of type 'address' will be
duplicated in the legacy "listen" attribute of the <graphic>
element.
2) The "listenAddr" attribute has been removed from the unions in
virDomainGRaphicsDef for graphics types vnc, rdp, and spice.
This attribute is now in the <listen> subelement (aka
virDomainGraphicsListenDef)
3) Helper functions were written to provide simple access
(both Get and Set) to the listen elements and their attributes.
* src/libvirt_private.syms: export the listen helper functions
* src/qemu/qemu_command.c, src/qemu/qemu_hotplug.c,
src/qemu/qemu_migration.c, src/vbox/vbox_tmpl.c,
src/vmx/vmx.c, src/xenxs/xen_sxpr.c, src/xenxs/xen_xm.c
Modify all these files to use the listen helper functions rather
than directly referencing the (now missing) listenAddr
attribute. There can be multiple <listen> elements to a single
<graphics>, but the drivers all currently only support one, so all
replacements of direct access with a helper function indicate index
"0".
* tests/* - only 3 of these are new files added explicitly to test the
new <listen> element. All the others have been modified to reflect
the fact that any legacy "listen" attributes passed in to the domain
parse will be saved in a <listen> element (i.e. one of the
virDomainGraphicsListenDefs), and during the domain format function,
both the <listen> element as well as the legacy attributes will be
output.
2011-07-07 04:20:28 +00:00
|
|
|
error:
|
2011-02-21 13:40:08 +00:00
|
|
|
virDomainGraphicsDefFree(graphics);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2011-02-21 13:40:12 +00:00
|
|
|
xenParseSxprGraphicsNew(virDomainDefPtr def,
|
|
|
|
const struct sexpr *root, int vncport)
|
2011-02-21 13:40:08 +00:00
|
|
|
{
|
|
|
|
virDomainGraphicsDefPtr graphics = NULL;
|
|
|
|
const struct sexpr *cur, *node;
|
|
|
|
const char *tmp;
|
|
|
|
|
|
|
|
/* append network devices and framebuffer */
|
|
|
|
for (cur = root; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) {
|
|
|
|
node = cur->u.s.car;
|
|
|
|
if (sexpr_lookup(node, "device/vfb")) {
|
|
|
|
/* New style graphics config for PV guests in >= 3.0.4,
|
|
|
|
* or for HVM guests in >= 3.0.5 */
|
|
|
|
if (sexpr_node(node, "device/vfb/type")) {
|
|
|
|
tmp = sexpr_node(node, "device/vfb/type");
|
|
|
|
} else if (sexpr_node(node, "device/vfb/vnc")) {
|
|
|
|
tmp = "vnc";
|
|
|
|
} else if (sexpr_node(node, "device/vfb/sdl")) {
|
|
|
|
tmp = "sdl";
|
|
|
|
} else {
|
|
|
|
tmp = "unknown";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_ALLOC(graphics) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if ((graphics->type = virDomainGraphicsTypeFromString(tmp)) < 0) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unknown graphics type '%s'"), tmp);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) {
|
|
|
|
const char *display = sexpr_node(node, "device/vfb/display");
|
|
|
|
const char *xauth = sexpr_node(node, "device/vfb/xauthority");
|
|
|
|
if (display &&
|
|
|
|
!(graphics->data.sdl.display = strdup(display)))
|
|
|
|
goto no_memory;
|
|
|
|
if (xauth &&
|
|
|
|
!(graphics->data.sdl.xauth = strdup(xauth)))
|
|
|
|
goto no_memory;
|
|
|
|
} else {
|
|
|
|
int port;
|
|
|
|
const char *listenAddr = sexpr_node(node, "device/vfb/vnclisten");
|
|
|
|
const char *vncPasswd = sexpr_node(node, "device/vfb/vncpasswd");
|
|
|
|
const char *keymap = sexpr_node(node, "device/vfb/keymap");
|
|
|
|
const char *unused = sexpr_node(node, "device/vfb/vncunused");
|
|
|
|
|
|
|
|
port = vncport;
|
|
|
|
|
|
|
|
/* Didn't find port entry in xenstore */
|
|
|
|
if (port == -1) {
|
|
|
|
const char *str = sexpr_node(node, "device/vfb/vncdisplay");
|
|
|
|
int val;
|
|
|
|
if (str != NULL && virStrToLong_i(str, NULL, 0, &val) == 0)
|
|
|
|
port = val;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((unused && STREQ(unused, "1")) || port == -1)
|
|
|
|
graphics->data.vnc.autoport = 1;
|
|
|
|
|
|
|
|
if (port >= 0 && port < 5900)
|
|
|
|
port += 5900;
|
|
|
|
graphics->data.vnc.port = port;
|
|
|
|
|
|
|
|
if (listenAddr &&
|
conf: add <listen> subelement to domain <graphics> element
Once it's plugged in, the <listen> element will be an optional
replacement for the "listen" attribute that graphics elements already
have. If the <listen> element is type='address', it will have an
attribute called 'address' which will contain an IP address or dns
name that the guest's display server should listen on. If, however,
type='network', the <listen> element should have an attribute called
'network' that will be set to the name of a network configuration to
get the IP address from.
* docs/schemas/domain.rng: updated to allow the <listen> element
* docs/formatdomain.html.in: document the <listen> element and its
attributes.
* src/conf/domain_conf.[hc]:
1) The domain parser, formatter, and data structure are modified to
support 0 or more <listen> subelements to each <graphics>
element. The old style "legacy" listen attribute is also still
accepted, and will be stored internally just as if it were a
separate <listen> element. On output (i.e. format), the address
attribute of the first <listen> element of type 'address' will be
duplicated in the legacy "listen" attribute of the <graphic>
element.
2) The "listenAddr" attribute has been removed from the unions in
virDomainGRaphicsDef for graphics types vnc, rdp, and spice.
This attribute is now in the <listen> subelement (aka
virDomainGraphicsListenDef)
3) Helper functions were written to provide simple access
(both Get and Set) to the listen elements and their attributes.
* src/libvirt_private.syms: export the listen helper functions
* src/qemu/qemu_command.c, src/qemu/qemu_hotplug.c,
src/qemu/qemu_migration.c, src/vbox/vbox_tmpl.c,
src/vmx/vmx.c, src/xenxs/xen_sxpr.c, src/xenxs/xen_xm.c
Modify all these files to use the listen helper functions rather
than directly referencing the (now missing) listenAddr
attribute. There can be multiple <listen> elements to a single
<graphics>, but the drivers all currently only support one, so all
replacements of direct access with a helper function indicate index
"0".
* tests/* - only 3 of these are new files added explicitly to test the
new <listen> element. All the others have been modified to reflect
the fact that any legacy "listen" attributes passed in to the domain
parse will be saved in a <listen> element (i.e. one of the
virDomainGraphicsListenDefs), and during the domain format function,
both the <listen> element as well as the legacy attributes will be
output.
2011-07-07 04:20:28 +00:00
|
|
|
virDomainGraphicsListenSetAddress(graphics, 0, listenAddr, -1, true))
|
|
|
|
goto error;
|
2011-02-21 13:40:08 +00:00
|
|
|
|
|
|
|
if (vncPasswd &&
|
|
|
|
!(graphics->data.vnc.auth.passwd = strdup(vncPasswd)))
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (keymap &&
|
|
|
|
!(graphics->data.vnc.keymap = strdup(keymap)))
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_ALLOC_N(def->graphics, 1) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
def->graphics[0] = graphics;
|
|
|
|
def->ngraphics = 1;
|
|
|
|
graphics = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
error:
|
|
|
|
virDomainGraphicsDefFree(graphics);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-02-21 13:40:12 +00:00
|
|
|
* xenParseSxprPCI
|
2011-02-21 13:40:08 +00:00
|
|
|
* @root: root sexpr
|
|
|
|
*
|
|
|
|
* This parses out block devices from the domain sexpr
|
|
|
|
*
|
|
|
|
* Returns 0 if successful or -1 if failed.
|
|
|
|
*/
|
|
|
|
static int
|
2011-02-21 13:40:12 +00:00
|
|
|
xenParseSxprPCI(virDomainDefPtr def,
|
|
|
|
const struct sexpr *root)
|
2011-02-21 13:40:08 +00:00
|
|
|
{
|
|
|
|
const struct sexpr *cur, *tmp = NULL, *node;
|
|
|
|
virDomainHostdevDefPtr dev = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* With the (domain ...) block we have the following odd setup
|
|
|
|
*
|
|
|
|
* (device
|
|
|
|
* (pci
|
|
|
|
* (dev (domain 0x0000) (bus 0x00) (slot 0x1b) (func 0x0))
|
|
|
|
* (dev (domain 0x0000) (bus 0x00) (slot 0x13) (func 0x0))
|
|
|
|
* )
|
|
|
|
* )
|
|
|
|
*
|
|
|
|
* Normally there is one (device ...) block per device, but in
|
2011-06-24 14:01:10 +00:00
|
|
|
* weird world of Xen PCI, once (device ...) covers multiple
|
2011-02-21 13:40:08 +00:00
|
|
|
* devices.
|
|
|
|
*/
|
|
|
|
|
|
|
|
for (cur = root; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) {
|
|
|
|
node = cur->u.s.car;
|
|
|
|
if ((tmp = sexpr_lookup(node, "device/pci")) != NULL)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tmp)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (cur = tmp; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) {
|
|
|
|
const char *domain = NULL;
|
|
|
|
const char *bus = NULL;
|
|
|
|
const char *slot = NULL;
|
|
|
|
const char *func = NULL;
|
|
|
|
int domainID;
|
|
|
|
int busID;
|
|
|
|
int slotID;
|
|
|
|
int funcID;
|
|
|
|
|
|
|
|
node = cur->u.s.car;
|
|
|
|
if (!sexpr_lookup(node, "dev"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!(domain = sexpr_node(node, "dev/domain"))) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("missing PCI domain"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (!(bus = sexpr_node(node, "dev/bus"))) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("missing PCI bus"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (!(slot = sexpr_node(node, "dev/slot"))) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("missing PCI slot"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (!(func = sexpr_node(node, "dev/func"))) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("missing PCI func"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_i(domain, NULL, 0, &domainID) < 0) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot parse PCI domain '%s'"), domain);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (virStrToLong_i(bus, NULL, 0, &busID) < 0) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot parse PCI bus '%s'"), bus);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (virStrToLong_i(slot, NULL, 0, &slotID) < 0) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot parse PCI slot '%s'"), slot);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (virStrToLong_i(func, NULL, 0, &funcID) < 0) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot parse PCI func '%s'"), func);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_ALLOC(dev) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
dev->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS;
|
|
|
|
dev->managed = 0;
|
|
|
|
dev->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI;
|
|
|
|
dev->source.subsys.u.pci.domain = domainID;
|
|
|
|
dev->source.subsys.u.pci.bus = busID;
|
|
|
|
dev->source.subsys.u.pci.slot = slotID;
|
|
|
|
dev->source.subsys.u.pci.function = funcID;
|
|
|
|
|
|
|
|
if (VIR_REALLOC_N(def->hostdevs, def->nhostdevs+1) < 0) {
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
|
|
|
|
def->hostdevs[def->nhostdevs++] = dev;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
|
|
|
|
error:
|
|
|
|
virDomainHostdevDefFree(dev);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2011-02-21 13:40:12 +00:00
|
|
|
* xenParseSxpr:
|
2011-02-21 13:40:08 +00:00
|
|
|
* @conn: the connection associated with the XML
|
|
|
|
* @root: the root of the parsed S-Expression
|
|
|
|
* @xendConfigVersion: version of xend
|
|
|
|
* @cpus: set of cpus the domain may be pinned to
|
|
|
|
*
|
|
|
|
* Parse the xend sexp description and turn it into the XML format similar
|
|
|
|
* to the one unsed for creation.
|
|
|
|
*
|
|
|
|
* Returns the 0 terminated XML string or NULL in case of error.
|
|
|
|
* the caller must free() the returned value.
|
|
|
|
*/
|
|
|
|
virDomainDefPtr
|
2011-02-21 13:40:12 +00:00
|
|
|
xenParseSxpr(const struct sexpr *root,
|
|
|
|
int xendConfigVersion,
|
|
|
|
const char *cpus, char *tty, int vncport)
|
2011-02-21 13:40:08 +00:00
|
|
|
{
|
|
|
|
const char *tmp;
|
|
|
|
virDomainDefPtr def;
|
|
|
|
int hvm = 0;
|
|
|
|
|
|
|
|
if (VIR_ALLOC(def) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
tmp = sexpr_node(root, "domain/domid");
|
|
|
|
if (tmp == NULL && xendConfigVersion < 3) { /* Old XenD, domid was mandatory */
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("domain information incomplete, missing id"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
def->virtType = VIR_DOMAIN_VIRT_XEN;
|
|
|
|
if (tmp)
|
|
|
|
def->id = sexpr_int(root, "domain/domid");
|
|
|
|
else
|
|
|
|
def->id = -1;
|
|
|
|
|
|
|
|
if (sexpr_node_copy(root, "domain/name", &def->name) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
if (def->name == NULL) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("domain information incomplete, missing name"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = sexpr_node(root, "domain/uuid");
|
|
|
|
if (tmp == NULL) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("domain information incomplete, missing name"));
|
|
|
|
goto error;
|
|
|
|
}
|
2011-10-12 23:24:52 +00:00
|
|
|
if (virUUIDParse(tmp, def->uuid) < 0)
|
|
|
|
goto error;
|
2011-02-21 13:40:08 +00:00
|
|
|
|
|
|
|
if (sexpr_node_copy(root, "domain/description", &def->description) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
hvm = sexpr_lookup(root, "domain/image/hvm") ? 1 : 0;
|
|
|
|
if (!hvm) {
|
|
|
|
if (sexpr_node_copy(root, "domain/bootloader",
|
|
|
|
&def->os.bootloader) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (!def->os.bootloader &&
|
|
|
|
sexpr_has(root, "domain/bootloader") &&
|
|
|
|
(def->os.bootloader = strdup("")) == NULL)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (def->os.bootloader &&
|
|
|
|
sexpr_node_copy(root, "domain/bootloader_args",
|
|
|
|
&def->os.bootloaderArgs) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(def->os.type = strdup(hvm ? "hvm" : "linux")))
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (def->id != 0) {
|
|
|
|
if (sexpr_lookup(root, "domain/image")) {
|
2011-02-21 13:40:12 +00:00
|
|
|
if (xenParseSxprOS(root, def, hvm) < 0)
|
2011-02-21 13:40:08 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
def->mem.max_balloon = (unsigned long)
|
|
|
|
(sexpr_u64(root, "domain/maxmem") << 10);
|
|
|
|
def->mem.cur_balloon = (unsigned long)
|
|
|
|
(sexpr_u64(root, "domain/memory") << 10);
|
|
|
|
if (def->mem.cur_balloon > def->mem.max_balloon)
|
|
|
|
def->mem.cur_balloon = def->mem.max_balloon;
|
|
|
|
|
|
|
|
if (cpus != NULL) {
|
|
|
|
def->cpumasklen = VIR_DOMAIN_CPUMASK_LEN;
|
|
|
|
if (VIR_ALLOC_N(def->cpumask, def->cpumasklen) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virDomainCpuSetParse(&cpus,
|
|
|
|
0, def->cpumask,
|
|
|
|
def->cpumasklen) < 0) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("invalid CPU mask %s"), cpus);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
def->maxvcpus = sexpr_int(root, "domain/vcpus");
|
|
|
|
def->vcpus = count_one_bits_l(sexpr_u64(root, "domain/vcpu_avail"));
|
|
|
|
if (!def->vcpus || def->maxvcpus < def->vcpus)
|
|
|
|
def->vcpus = def->maxvcpus;
|
|
|
|
|
|
|
|
tmp = sexpr_node(root, "domain/on_poweroff");
|
|
|
|
if (tmp != NULL) {
|
|
|
|
if ((def->onPoweroff = virDomainLifecycleTypeFromString(tmp)) < 0) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unknown lifecycle type %s"), tmp);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
def->onPoweroff = VIR_DOMAIN_LIFECYCLE_DESTROY;
|
|
|
|
|
|
|
|
tmp = sexpr_node(root, "domain/on_reboot");
|
|
|
|
if (tmp != NULL) {
|
|
|
|
if ((def->onReboot = virDomainLifecycleTypeFromString(tmp)) < 0) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unknown lifecycle type %s"), tmp);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
def->onReboot = VIR_DOMAIN_LIFECYCLE_RESTART;
|
|
|
|
|
|
|
|
tmp = sexpr_node(root, "domain/on_crash");
|
|
|
|
if (tmp != NULL) {
|
|
|
|
if ((def->onCrash = virDomainLifecycleCrashTypeFromString(tmp)) < 0) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unknown lifecycle type %s"), tmp);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
def->onCrash = VIR_DOMAIN_LIFECYCLE_DESTROY;
|
|
|
|
|
|
|
|
def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_UTC;
|
|
|
|
if (hvm) {
|
|
|
|
if (sexpr_int(root, "domain/image/hvm/acpi"))
|
|
|
|
def->features |= (1 << VIR_DOMAIN_FEATURE_ACPI);
|
|
|
|
if (sexpr_int(root, "domain/image/hvm/apic"))
|
|
|
|
def->features |= (1 << VIR_DOMAIN_FEATURE_APIC);
|
|
|
|
if (sexpr_int(root, "domain/image/hvm/pae"))
|
|
|
|
def->features |= (1 << VIR_DOMAIN_FEATURE_PAE);
|
|
|
|
if (sexpr_int(root, "domain/image/hvm/hap"))
|
|
|
|
def->features |= (1 << VIR_DOMAIN_FEATURE_HAP);
|
2011-06-15 13:27:43 +00:00
|
|
|
if (sexpr_int(root, "domain/image/hvm/viridian"))
|
|
|
|
def->features |= (1 << VIR_DOMAIN_FEATURE_VIRIDIAN);
|
2011-02-21 13:40:08 +00:00
|
|
|
|
|
|
|
/* Old XenD only allows localtime here for HVM */
|
|
|
|
if (sexpr_int(root, "domain/image/hvm/localtime"))
|
|
|
|
def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME;
|
2011-05-10 11:24:14 +00:00
|
|
|
|
|
|
|
if (sexpr_lookup(root, "domain/image/hvm/hpet")) {
|
|
|
|
virDomainTimerDefPtr timer;
|
|
|
|
|
|
|
|
if (VIR_ALLOC_N(def->clock.timers, 1) < 0 ||
|
|
|
|
VIR_ALLOC(timer) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
timer->name = VIR_DOMAIN_TIMER_NAME_HPET;
|
|
|
|
timer->present = sexpr_int(root, "domain/image/hvm/hpet");
|
|
|
|
timer->tickpolicy = -1;
|
|
|
|
|
|
|
|
def->clock.ntimers = 1;
|
|
|
|
def->clock.timers[0] = timer;
|
|
|
|
}
|
2011-09-05 06:22:36 +00:00
|
|
|
} else { /* !hvm */
|
|
|
|
if (sexpr_int(root, "domain/image/linux/localtime"))
|
|
|
|
def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME;
|
2011-02-21 13:40:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Current XenD allows localtime here, for PV and HVM */
|
|
|
|
if (sexpr_int(root, "domain/localtime"))
|
|
|
|
def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME;
|
|
|
|
|
|
|
|
if (sexpr_node_copy(root, hvm ?
|
|
|
|
"domain/image/hvm/device_model" :
|
|
|
|
"domain/image/linux/device_model",
|
|
|
|
&def->emulator) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
/* append block devices */
|
2011-02-21 13:40:12 +00:00
|
|
|
if (xenParseSxprDisks(def, root, hvm, xendConfigVersion) < 0)
|
2011-02-21 13:40:08 +00:00
|
|
|
goto error;
|
|
|
|
|
2011-02-21 13:40:12 +00:00
|
|
|
if (xenParseSxprNets(def, root) < 0)
|
2011-02-21 13:40:08 +00:00
|
|
|
goto error;
|
|
|
|
|
2011-02-21 13:40:12 +00:00
|
|
|
if (xenParseSxprPCI(def, root) < 0)
|
2011-02-21 13:40:08 +00:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* New style graphics device config */
|
2011-02-21 13:40:12 +00:00
|
|
|
if (xenParseSxprGraphicsNew(def, root, vncport) < 0)
|
2011-02-21 13:40:08 +00:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* Graphics device (HVM <= 3.0.4, or PV <= 3.0.3) vnc config */
|
|
|
|
if ((def->ngraphics == 0) &&
|
2011-02-21 13:40:12 +00:00
|
|
|
xenParseSxprGraphicsOld(def, root, hvm, xendConfigVersion,
|
2011-02-21 13:40:08 +00:00
|
|
|
vncport) < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
|
|
/* Old style cdrom config from Xen <= 3.0.2 */
|
|
|
|
if (hvm &&
|
|
|
|
xendConfigVersion == 1) {
|
|
|
|
tmp = sexpr_node(root, "domain/image/hvm/cdrom");
|
|
|
|
if ((tmp != NULL) && (tmp[0] != 0)) {
|
|
|
|
virDomainDiskDefPtr disk;
|
|
|
|
if (VIR_ALLOC(disk) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
if (!(disk->src = strdup(tmp))) {
|
|
|
|
virDomainDiskDefFree(disk);
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
disk->type = VIR_DOMAIN_DISK_TYPE_FILE;
|
|
|
|
disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM;
|
|
|
|
if (!(disk->dst = strdup("hdc"))) {
|
|
|
|
virDomainDiskDefFree(disk);
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
if (!(disk->driverName = strdup("file"))) {
|
|
|
|
virDomainDiskDefFree(disk);
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
disk->bus = VIR_DOMAIN_DISK_BUS_IDE;
|
|
|
|
disk->readonly = 1;
|
|
|
|
|
|
|
|
if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) {
|
|
|
|
virDomainDiskDefFree(disk);
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
def->disks[def->ndisks++] = disk;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Floppy disk config */
|
|
|
|
if (hvm) {
|
|
|
|
const char *const fds[] = { "fda", "fdb" };
|
|
|
|
int i;
|
|
|
|
for (i = 0 ; i < ARRAY_CARDINALITY(fds) ; i++) {
|
|
|
|
tmp = sexpr_fmt_node(root, "domain/image/hvm/%s", fds[i]);
|
|
|
|
if ((tmp != NULL) && (tmp[0] != 0)) {
|
|
|
|
virDomainDiskDefPtr disk;
|
|
|
|
if (VIR_ALLOC(disk) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
if (!(disk->src = strdup(tmp))) {
|
|
|
|
VIR_FREE(disk);
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
disk->type = VIR_DOMAIN_DISK_TYPE_FILE;
|
|
|
|
disk->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY;
|
|
|
|
if (!(disk->dst = strdup(fds[i]))) {
|
|
|
|
virDomainDiskDefFree(disk);
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
if (!(disk->driverName = strdup("file"))) {
|
|
|
|
virDomainDiskDefFree(disk);
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
disk->bus = VIR_DOMAIN_DISK_BUS_FDC;
|
|
|
|
|
|
|
|
if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) {
|
|
|
|
virDomainDiskDefFree(disk);
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
def->disks[def->ndisks++] = disk;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* in case of HVM we have USB device emulation */
|
|
|
|
if (hvm &&
|
2011-02-21 13:40:12 +00:00
|
|
|
xenParseSxprUSB(def, root) < 0)
|
2011-02-21 13:40:08 +00:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* Character device config */
|
|
|
|
if (hvm) {
|
2011-02-25 14:41:12 +00:00
|
|
|
const struct sexpr *serial_root;
|
|
|
|
bool have_multiple_serials = false;
|
|
|
|
|
|
|
|
serial_root = sexpr_lookup(root, "domain/image/hvm/serial");
|
|
|
|
if (serial_root) {
|
|
|
|
const struct sexpr *cur, *node, *cur2;
|
|
|
|
int ports_skipped = 0;
|
|
|
|
|
|
|
|
for (cur = serial_root; cur->kind == SEXPR_CONS; cur = cur->u.s.cdr) {
|
|
|
|
node = cur->u.s.car;
|
|
|
|
|
|
|
|
for (cur2 = node; cur2->kind == SEXPR_CONS; cur2 = cur2->u.s.cdr) {
|
|
|
|
tmp = cur2->u.s.car->u.value;
|
|
|
|
|
|
|
|
if (tmp && STRNEQ(tmp, "none")) {
|
|
|
|
virDomainChrDefPtr chr;
|
|
|
|
if ((chr = xenParseSxprChar(tmp, tty)) == NULL)
|
|
|
|
goto error;
|
|
|
|
if (VIR_REALLOC_N(def->serials, def->nserials+1) < 0) {
|
|
|
|
virDomainChrDefFree(chr);
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
chr->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL;
|
|
|
|
chr->target.port = def->nserials + ports_skipped;
|
|
|
|
def->serials[def->nserials++] = chr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ports_skipped++;
|
|
|
|
|
|
|
|
have_multiple_serials = true;
|
|
|
|
}
|
2011-02-21 13:40:08 +00:00
|
|
|
}
|
|
|
|
}
|
2011-02-25 14:41:12 +00:00
|
|
|
|
|
|
|
if (!have_multiple_serials) {
|
|
|
|
tmp = sexpr_node(root, "domain/image/hvm/serial");
|
|
|
|
if (tmp && STRNEQ(tmp, "none")) {
|
|
|
|
virDomainChrDefPtr chr;
|
|
|
|
if ((chr = xenParseSxprChar(tmp, tty)) == NULL)
|
|
|
|
goto error;
|
|
|
|
if (VIR_REALLOC_N(def->serials, def->nserials+1) < 0) {
|
|
|
|
virDomainChrDefFree(chr);
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
chr->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL;
|
2011-04-14 16:05:14 +00:00
|
|
|
chr->target.port = 0;
|
2011-02-25 14:41:12 +00:00
|
|
|
def->serials[def->nserials++] = chr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-21 13:40:08 +00:00
|
|
|
tmp = sexpr_node(root, "domain/image/hvm/parallel");
|
|
|
|
if (tmp && STRNEQ(tmp, "none")) {
|
|
|
|
virDomainChrDefPtr chr;
|
|
|
|
/* XXX does XenD stuff parallel port tty info into xenstore somewhere ? */
|
2011-02-21 13:40:12 +00:00
|
|
|
if ((chr = xenParseSxprChar(tmp, NULL)) == NULL)
|
2011-02-21 13:40:08 +00:00
|
|
|
goto error;
|
|
|
|
if (VIR_REALLOC_N(def->parallels, def->nparallels+1) < 0) {
|
|
|
|
virDomainChrDefFree(chr);
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
chr->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL;
|
2011-04-14 16:05:14 +00:00
|
|
|
chr->target.port = 0;
|
2011-02-21 13:40:08 +00:00
|
|
|
def->parallels[def->nparallels++] = chr;
|
|
|
|
}
|
|
|
|
} else {
|
Allow multiple consoles per virtual guest
While Xen only has a single paravirt console, UML, and
QEMU both support multiple paravirt consoles. The LXC
driver can also be trivially made to support multiple
consoles. This patch extends the XML to allow multiple
<console> elements in the XML. It also makes the UML
and QEMU drivers support this config.
* src/conf/domain_conf.c, src/conf/domain_conf.h: Allow
multiple <console> devices
* src/lxc/lxc_driver.c, src/xen/xen_driver.c,
src/xenxs/xen_sxpr.c, src/xenxs/xen_xm.c: Update for
internal API changes
* src/security/security_selinux.c, src/security/virt-aa-helper.c:
Only label consoles that aren't a copy of the serial device
* src/qemu/qemu_command.c, src/qemu/qemu_driver.c,
src/qemu/qemu_process.c, src/uml/uml_conf.c,
src/uml/uml_driver.c: Support multiple console devices
* tests/qemuxml2xmltest.c, tests/qemuxml2argvtest.c: Extra
tests for multiple virtio consoles. Set QEMU_CAPS_CHARDEV
for all console /channel tests
* tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-auto.args,
tests/qemuxml2argvdata/qemuxml2argv-channel-virtio.args
tests/qemuxml2argvdata/qemuxml2argv-console-virtio.args: Update
for correct chardev syntax
* tests/qemuxml2argvdata/qemuxml2argv-console-virtio-many.args,
tests/qemuxml2argvdata/qemuxml2argv-console-virtio-many.xml: New
test file
2011-02-23 18:27:23 +00:00
|
|
|
def->nconsoles = 1;
|
|
|
|
if (VIR_ALLOC_N(def->consoles, 1) < 0)
|
|
|
|
goto no_memory;
|
2011-02-21 13:40:08 +00:00
|
|
|
/* Fake a paravirt console, since that's not in the sexpr */
|
Allow multiple consoles per virtual guest
While Xen only has a single paravirt console, UML, and
QEMU both support multiple paravirt consoles. The LXC
driver can also be trivially made to support multiple
consoles. This patch extends the XML to allow multiple
<console> elements in the XML. It also makes the UML
and QEMU drivers support this config.
* src/conf/domain_conf.c, src/conf/domain_conf.h: Allow
multiple <console> devices
* src/lxc/lxc_driver.c, src/xen/xen_driver.c,
src/xenxs/xen_sxpr.c, src/xenxs/xen_xm.c: Update for
internal API changes
* src/security/security_selinux.c, src/security/virt-aa-helper.c:
Only label consoles that aren't a copy of the serial device
* src/qemu/qemu_command.c, src/qemu/qemu_driver.c,
src/qemu/qemu_process.c, src/uml/uml_conf.c,
src/uml/uml_driver.c: Support multiple console devices
* tests/qemuxml2xmltest.c, tests/qemuxml2argvtest.c: Extra
tests for multiple virtio consoles. Set QEMU_CAPS_CHARDEV
for all console /channel tests
* tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-auto.args,
tests/qemuxml2argvdata/qemuxml2argv-channel-virtio.args
tests/qemuxml2argvdata/qemuxml2argv-console-virtio.args: Update
for correct chardev syntax
* tests/qemuxml2argvdata/qemuxml2argv-console-virtio-many.args,
tests/qemuxml2argvdata/qemuxml2argv-console-virtio-many.xml: New
test file
2011-02-23 18:27:23 +00:00
|
|
|
if (!(def->consoles[0] = xenParseSxprChar("pty", tty)))
|
2011-02-21 13:40:08 +00:00
|
|
|
goto error;
|
Allow multiple consoles per virtual guest
While Xen only has a single paravirt console, UML, and
QEMU both support multiple paravirt consoles. The LXC
driver can also be trivially made to support multiple
consoles. This patch extends the XML to allow multiple
<console> elements in the XML. It also makes the UML
and QEMU drivers support this config.
* src/conf/domain_conf.c, src/conf/domain_conf.h: Allow
multiple <console> devices
* src/lxc/lxc_driver.c, src/xen/xen_driver.c,
src/xenxs/xen_sxpr.c, src/xenxs/xen_xm.c: Update for
internal API changes
* src/security/security_selinux.c, src/security/virt-aa-helper.c:
Only label consoles that aren't a copy of the serial device
* src/qemu/qemu_command.c, src/qemu/qemu_driver.c,
src/qemu/qemu_process.c, src/uml/uml_conf.c,
src/uml/uml_driver.c: Support multiple console devices
* tests/qemuxml2xmltest.c, tests/qemuxml2argvtest.c: Extra
tests for multiple virtio consoles. Set QEMU_CAPS_CHARDEV
for all console /channel tests
* tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-auto.args,
tests/qemuxml2argvdata/qemuxml2argv-channel-virtio.args
tests/qemuxml2argvdata/qemuxml2argv-console-virtio.args: Update
for correct chardev syntax
* tests/qemuxml2argvdata/qemuxml2argv-console-virtio-many.args,
tests/qemuxml2argvdata/qemuxml2argv-console-virtio-many.xml: New
test file
2011-02-23 18:27:23 +00:00
|
|
|
def->consoles[0]->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE;
|
|
|
|
def->consoles[0]->target.port = 0;
|
|
|
|
def->consoles[0]->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_XEN;
|
2011-02-21 13:40:08 +00:00
|
|
|
}
|
|
|
|
VIR_FREE(tty);
|
|
|
|
|
|
|
|
|
|
|
|
/* Sound device config */
|
|
|
|
if (hvm &&
|
|
|
|
(tmp = sexpr_node(root, "domain/image/hvm/soundhw")) != NULL &&
|
|
|
|
*tmp) {
|
2011-02-21 13:40:12 +00:00
|
|
|
if (xenParseSxprSound(def, tmp) < 0)
|
2011-02-21 13:40:08 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return def;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
error:
|
|
|
|
VIR_FREE(tty);
|
|
|
|
virDomainDefFree(def);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
virDomainDefPtr
|
2011-02-21 13:40:12 +00:00
|
|
|
xenParseSxprString(const char *sexpr,
|
2011-02-21 13:40:08 +00:00
|
|
|
int xendConfigVersion, char *tty, int vncport)
|
|
|
|
{
|
|
|
|
struct sexpr *root = string2sexpr(sexpr);
|
|
|
|
virDomainDefPtr def;
|
|
|
|
|
|
|
|
if (!root)
|
|
|
|
return NULL;
|
|
|
|
|
2011-02-21 13:40:12 +00:00
|
|
|
def = xenParseSxpr(root, xendConfigVersion, NULL, tty, vncport);
|
2011-02-21 13:40:08 +00:00
|
|
|
|
|
|
|
sexpr_free(root);
|
|
|
|
|
|
|
|
return def;
|
2011-02-21 13:40:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************
|
|
|
|
* *
|
|
|
|
* Converter functions to go from the XML tree to an S-Expr for Xen *
|
|
|
|
* *
|
|
|
|
************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virtDomainParseXMLGraphicsDescVFB:
|
|
|
|
* @conn: pointer to the hypervisor connection
|
|
|
|
* @node: node containing graphics description
|
|
|
|
* @buf: a buffer for the result S-Expr
|
|
|
|
*
|
|
|
|
* Parse the graphics part of the XML description and add it to the S-Expr
|
|
|
|
* in buf. This is a temporary interface as the S-Expr interface will be
|
|
|
|
* replaced by XML-RPC in the future. However the XML format should stay
|
|
|
|
* valid over time.
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success, -1 in case of error
|
|
|
|
*/
|
|
|
|
static int
|
2011-02-21 13:40:12 +00:00
|
|
|
xenFormatSxprGraphicsNew(virDomainGraphicsDefPtr def,
|
|
|
|
virBufferPtr buf)
|
2011-02-21 13:40:09 +00:00
|
|
|
{
|
conf: add <listen> subelement to domain <graphics> element
Once it's plugged in, the <listen> element will be an optional
replacement for the "listen" attribute that graphics elements already
have. If the <listen> element is type='address', it will have an
attribute called 'address' which will contain an IP address or dns
name that the guest's display server should listen on. If, however,
type='network', the <listen> element should have an attribute called
'network' that will be set to the name of a network configuration to
get the IP address from.
* docs/schemas/domain.rng: updated to allow the <listen> element
* docs/formatdomain.html.in: document the <listen> element and its
attributes.
* src/conf/domain_conf.[hc]:
1) The domain parser, formatter, and data structure are modified to
support 0 or more <listen> subelements to each <graphics>
element. The old style "legacy" listen attribute is also still
accepted, and will be stored internally just as if it were a
separate <listen> element. On output (i.e. format), the address
attribute of the first <listen> element of type 'address' will be
duplicated in the legacy "listen" attribute of the <graphic>
element.
2) The "listenAddr" attribute has been removed from the unions in
virDomainGRaphicsDef for graphics types vnc, rdp, and spice.
This attribute is now in the <listen> subelement (aka
virDomainGraphicsListenDef)
3) Helper functions were written to provide simple access
(both Get and Set) to the listen elements and their attributes.
* src/libvirt_private.syms: export the listen helper functions
* src/qemu/qemu_command.c, src/qemu/qemu_hotplug.c,
src/qemu/qemu_migration.c, src/vbox/vbox_tmpl.c,
src/vmx/vmx.c, src/xenxs/xen_sxpr.c, src/xenxs/xen_xm.c
Modify all these files to use the listen helper functions rather
than directly referencing the (now missing) listenAddr
attribute. There can be multiple <listen> elements to a single
<graphics>, but the drivers all currently only support one, so all
replacements of direct access with a helper function indicate index
"0".
* tests/* - only 3 of these are new files added explicitly to test the
new <listen> element. All the others have been modified to reflect
the fact that any legacy "listen" attributes passed in to the domain
parse will be saved in a <listen> element (i.e. one of the
virDomainGraphicsListenDefs), and during the domain format function,
both the <listen> element as well as the legacy attributes will be
output.
2011-07-07 04:20:28 +00:00
|
|
|
const char *listenAddr;
|
|
|
|
|
2011-02-21 13:40:09 +00:00
|
|
|
if (def->type != VIR_DOMAIN_GRAPHICS_TYPE_SDL &&
|
|
|
|
def->type != VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected graphics type %d"),
|
|
|
|
def->type);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
virBufferAddLit(buf, "(device (vkbd))");
|
|
|
|
virBufferAddLit(buf, "(device (vfb ");
|
|
|
|
|
|
|
|
if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) {
|
|
|
|
virBufferAddLit(buf, "(type sdl)");
|
|
|
|
if (def->data.sdl.display)
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, "(display '%s')", def->data.sdl.display);
|
2011-02-21 13:40:09 +00:00
|
|
|
if (def->data.sdl.xauth)
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, "(xauthority '%s')", def->data.sdl.xauth);
|
2011-02-21 13:40:09 +00:00
|
|
|
} else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
|
|
|
|
virBufferAddLit(buf, "(type vnc)");
|
|
|
|
if (def->data.vnc.autoport) {
|
|
|
|
virBufferAddLit(buf, "(vncunused 1)");
|
|
|
|
} else {
|
|
|
|
virBufferAddLit(buf, "(vncunused 0)");
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, "(vncdisplay %d)", def->data.vnc.port-5900);
|
2011-02-21 13:40:09 +00:00
|
|
|
}
|
|
|
|
|
conf: add <listen> subelement to domain <graphics> element
Once it's plugged in, the <listen> element will be an optional
replacement for the "listen" attribute that graphics elements already
have. If the <listen> element is type='address', it will have an
attribute called 'address' which will contain an IP address or dns
name that the guest's display server should listen on. If, however,
type='network', the <listen> element should have an attribute called
'network' that will be set to the name of a network configuration to
get the IP address from.
* docs/schemas/domain.rng: updated to allow the <listen> element
* docs/formatdomain.html.in: document the <listen> element and its
attributes.
* src/conf/domain_conf.[hc]:
1) The domain parser, formatter, and data structure are modified to
support 0 or more <listen> subelements to each <graphics>
element. The old style "legacy" listen attribute is also still
accepted, and will be stored internally just as if it were a
separate <listen> element. On output (i.e. format), the address
attribute of the first <listen> element of type 'address' will be
duplicated in the legacy "listen" attribute of the <graphic>
element.
2) The "listenAddr" attribute has been removed from the unions in
virDomainGRaphicsDef for graphics types vnc, rdp, and spice.
This attribute is now in the <listen> subelement (aka
virDomainGraphicsListenDef)
3) Helper functions were written to provide simple access
(both Get and Set) to the listen elements and their attributes.
* src/libvirt_private.syms: export the listen helper functions
* src/qemu/qemu_command.c, src/qemu/qemu_hotplug.c,
src/qemu/qemu_migration.c, src/vbox/vbox_tmpl.c,
src/vmx/vmx.c, src/xenxs/xen_sxpr.c, src/xenxs/xen_xm.c
Modify all these files to use the listen helper functions rather
than directly referencing the (now missing) listenAddr
attribute. There can be multiple <listen> elements to a single
<graphics>, but the drivers all currently only support one, so all
replacements of direct access with a helper function indicate index
"0".
* tests/* - only 3 of these are new files added explicitly to test the
new <listen> element. All the others have been modified to reflect
the fact that any legacy "listen" attributes passed in to the domain
parse will be saved in a <listen> element (i.e. one of the
virDomainGraphicsListenDefs), and during the domain format function,
both the <listen> element as well as the legacy attributes will be
output.
2011-07-07 04:20:28 +00:00
|
|
|
listenAddr = virDomainGraphicsListenGetAddress(def, 0);
|
|
|
|
if (listenAddr)
|
|
|
|
virBufferAsprintf(buf, "(vnclisten '%s')", listenAddr);
|
2011-02-21 13:40:09 +00:00
|
|
|
if (def->data.vnc.auth.passwd)
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, "(vncpasswd '%s')", def->data.vnc.auth.passwd);
|
2011-02-21 13:40:09 +00:00
|
|
|
if (def->data.vnc.keymap)
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, "(keymap '%s')", def->data.vnc.keymap);
|
2011-02-21 13:40:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virBufferAddLit(buf, "))");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2011-02-21 13:40:12 +00:00
|
|
|
xenFormatSxprGraphicsOld(virDomainGraphicsDefPtr def,
|
|
|
|
virBufferPtr buf,
|
|
|
|
int xendConfigVersion)
|
2011-02-21 13:40:09 +00:00
|
|
|
{
|
conf: add <listen> subelement to domain <graphics> element
Once it's plugged in, the <listen> element will be an optional
replacement for the "listen" attribute that graphics elements already
have. If the <listen> element is type='address', it will have an
attribute called 'address' which will contain an IP address or dns
name that the guest's display server should listen on. If, however,
type='network', the <listen> element should have an attribute called
'network' that will be set to the name of a network configuration to
get the IP address from.
* docs/schemas/domain.rng: updated to allow the <listen> element
* docs/formatdomain.html.in: document the <listen> element and its
attributes.
* src/conf/domain_conf.[hc]:
1) The domain parser, formatter, and data structure are modified to
support 0 or more <listen> subelements to each <graphics>
element. The old style "legacy" listen attribute is also still
accepted, and will be stored internally just as if it were a
separate <listen> element. On output (i.e. format), the address
attribute of the first <listen> element of type 'address' will be
duplicated in the legacy "listen" attribute of the <graphic>
element.
2) The "listenAddr" attribute has been removed from the unions in
virDomainGRaphicsDef for graphics types vnc, rdp, and spice.
This attribute is now in the <listen> subelement (aka
virDomainGraphicsListenDef)
3) Helper functions were written to provide simple access
(both Get and Set) to the listen elements and their attributes.
* src/libvirt_private.syms: export the listen helper functions
* src/qemu/qemu_command.c, src/qemu/qemu_hotplug.c,
src/qemu/qemu_migration.c, src/vbox/vbox_tmpl.c,
src/vmx/vmx.c, src/xenxs/xen_sxpr.c, src/xenxs/xen_xm.c
Modify all these files to use the listen helper functions rather
than directly referencing the (now missing) listenAddr
attribute. There can be multiple <listen> elements to a single
<graphics>, but the drivers all currently only support one, so all
replacements of direct access with a helper function indicate index
"0".
* tests/* - only 3 of these are new files added explicitly to test the
new <listen> element. All the others have been modified to reflect
the fact that any legacy "listen" attributes passed in to the domain
parse will be saved in a <listen> element (i.e. one of the
virDomainGraphicsListenDefs), and during the domain format function,
both the <listen> element as well as the legacy attributes will be
output.
2011-07-07 04:20:28 +00:00
|
|
|
const char *listenAddr;
|
|
|
|
|
2011-02-21 13:40:09 +00:00
|
|
|
if (def->type != VIR_DOMAIN_GRAPHICS_TYPE_SDL &&
|
|
|
|
def->type != VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected graphics type %d"),
|
|
|
|
def->type);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) {
|
|
|
|
virBufferAddLit(buf, "(sdl 1)");
|
|
|
|
if (def->data.sdl.display)
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, "(display '%s')", def->data.sdl.display);
|
2011-02-21 13:40:09 +00:00
|
|
|
if (def->data.sdl.xauth)
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, "(xauthority '%s')", def->data.sdl.xauth);
|
2011-02-21 13:40:09 +00:00
|
|
|
} else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
|
|
|
|
virBufferAddLit(buf, "(vnc 1)");
|
|
|
|
if (xendConfigVersion >= 2) {
|
|
|
|
if (def->data.vnc.autoport) {
|
|
|
|
virBufferAddLit(buf, "(vncunused 1)");
|
|
|
|
} else {
|
|
|
|
virBufferAddLit(buf, "(vncunused 0)");
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, "(vncdisplay %d)", def->data.vnc.port-5900);
|
2011-02-21 13:40:09 +00:00
|
|
|
}
|
|
|
|
|
conf: add <listen> subelement to domain <graphics> element
Once it's plugged in, the <listen> element will be an optional
replacement for the "listen" attribute that graphics elements already
have. If the <listen> element is type='address', it will have an
attribute called 'address' which will contain an IP address or dns
name that the guest's display server should listen on. If, however,
type='network', the <listen> element should have an attribute called
'network' that will be set to the name of a network configuration to
get the IP address from.
* docs/schemas/domain.rng: updated to allow the <listen> element
* docs/formatdomain.html.in: document the <listen> element and its
attributes.
* src/conf/domain_conf.[hc]:
1) The domain parser, formatter, and data structure are modified to
support 0 or more <listen> subelements to each <graphics>
element. The old style "legacy" listen attribute is also still
accepted, and will be stored internally just as if it were a
separate <listen> element. On output (i.e. format), the address
attribute of the first <listen> element of type 'address' will be
duplicated in the legacy "listen" attribute of the <graphic>
element.
2) The "listenAddr" attribute has been removed from the unions in
virDomainGRaphicsDef for graphics types vnc, rdp, and spice.
This attribute is now in the <listen> subelement (aka
virDomainGraphicsListenDef)
3) Helper functions were written to provide simple access
(both Get and Set) to the listen elements and their attributes.
* src/libvirt_private.syms: export the listen helper functions
* src/qemu/qemu_command.c, src/qemu/qemu_hotplug.c,
src/qemu/qemu_migration.c, src/vbox/vbox_tmpl.c,
src/vmx/vmx.c, src/xenxs/xen_sxpr.c, src/xenxs/xen_xm.c
Modify all these files to use the listen helper functions rather
than directly referencing the (now missing) listenAddr
attribute. There can be multiple <listen> elements to a single
<graphics>, but the drivers all currently only support one, so all
replacements of direct access with a helper function indicate index
"0".
* tests/* - only 3 of these are new files added explicitly to test the
new <listen> element. All the others have been modified to reflect
the fact that any legacy "listen" attributes passed in to the domain
parse will be saved in a <listen> element (i.e. one of the
virDomainGraphicsListenDefs), and during the domain format function,
both the <listen> element as well as the legacy attributes will be
output.
2011-07-07 04:20:28 +00:00
|
|
|
listenAddr = virDomainGraphicsListenGetAddress(def, 0);
|
|
|
|
if (listenAddr)
|
|
|
|
virBufferAsprintf(buf, "(vnclisten '%s')", listenAddr);
|
2011-02-21 13:40:09 +00:00
|
|
|
if (def->data.vnc.auth.passwd)
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, "(vncpasswd '%s')", def->data.vnc.auth.passwd);
|
2011-02-21 13:40:09 +00:00
|
|
|
if (def->data.vnc.keymap)
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, "(keymap '%s')", def->data.vnc.keymap);
|
2011-02-21 13:40:09 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2011-02-21 13:40:12 +00:00
|
|
|
xenFormatSxprChr(virDomainChrDefPtr def,
|
|
|
|
virBufferPtr buf)
|
2011-02-21 13:40:09 +00:00
|
|
|
{
|
|
|
|
const char *type = virDomainChrTypeToString(def->source.type);
|
|
|
|
|
|
|
|
if (!type) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("unexpected chr device type"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (def->source.type) {
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_NULL:
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_STDIO:
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_VC:
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_PTY:
|
2011-04-14 19:27:47 +00:00
|
|
|
virBufferAdd(buf, type, -1);
|
2011-02-21 13:40:09 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_FILE:
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_PIPE:
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, "%s:", type);
|
2011-02-21 13:40:09 +00:00
|
|
|
virBufferEscapeSexpr(buf, "%s", def->source.data.file.path);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_DEV:
|
|
|
|
virBufferEscapeSexpr(buf, "%s", def->source.data.file.path);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_TCP:
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, "%s:%s:%s%s",
|
2011-02-21 13:40:09 +00:00
|
|
|
(def->source.data.tcp.protocol
|
|
|
|
== VIR_DOMAIN_CHR_TCP_PROTOCOL_RAW ?
|
|
|
|
"tcp" : "telnet"),
|
|
|
|
(def->source.data.tcp.host ?
|
|
|
|
def->source.data.tcp.host : ""),
|
|
|
|
(def->source.data.tcp.service ?
|
|
|
|
def->source.data.tcp.service : ""),
|
|
|
|
(def->source.data.tcp.listen ?
|
|
|
|
",server,nowait" : ""));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_UDP:
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, "%s:%s:%s@%s:%s", type,
|
2011-02-21 13:40:09 +00:00
|
|
|
(def->source.data.udp.connectHost ?
|
|
|
|
def->source.data.udp.connectHost : ""),
|
|
|
|
(def->source.data.udp.connectService ?
|
|
|
|
def->source.data.udp.connectService : ""),
|
|
|
|
(def->source.data.udp.bindHost ?
|
|
|
|
def->source.data.udp.bindHost : ""),
|
|
|
|
(def->source.data.udp.bindService ?
|
|
|
|
def->source.data.udp.bindService : ""));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_UNIX:
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, "%s:", type);
|
2011-02-21 13:40:09 +00:00
|
|
|
virBufferEscapeSexpr(buf, "%s", def->source.data.nix.path);
|
|
|
|
if (def->source.data.nix.listen)
|
|
|
|
virBufferAddLit(buf, ",server,nowait");
|
|
|
|
break;
|
Allow multiple consoles per virtual guest
While Xen only has a single paravirt console, UML, and
QEMU both support multiple paravirt consoles. The LXC
driver can also be trivially made to support multiple
consoles. This patch extends the XML to allow multiple
<console> elements in the XML. It also makes the UML
and QEMU drivers support this config.
* src/conf/domain_conf.c, src/conf/domain_conf.h: Allow
multiple <console> devices
* src/lxc/lxc_driver.c, src/xen/xen_driver.c,
src/xenxs/xen_sxpr.c, src/xenxs/xen_xm.c: Update for
internal API changes
* src/security/security_selinux.c, src/security/virt-aa-helper.c:
Only label consoles that aren't a copy of the serial device
* src/qemu/qemu_command.c, src/qemu/qemu_driver.c,
src/qemu/qemu_process.c, src/uml/uml_conf.c,
src/uml/uml_driver.c: Support multiple console devices
* tests/qemuxml2xmltest.c, tests/qemuxml2argvtest.c: Extra
tests for multiple virtio consoles. Set QEMU_CAPS_CHARDEV
for all console /channel tests
* tests/qemuxml2argvdata/qemuxml2argv-channel-virtio-auto.args,
tests/qemuxml2argvdata/qemuxml2argv-channel-virtio.args
tests/qemuxml2argvdata/qemuxml2argv-console-virtio.args: Update
for correct chardev syntax
* tests/qemuxml2argvdata/qemuxml2argv-console-virtio-many.args,
tests/qemuxml2argvdata/qemuxml2argv-console-virtio-many.xml: New
test file
2011-02-23 18:27:23 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
XENXS_ERROR(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("unsupported chr device type '%s'"), type);
|
|
|
|
return -1;
|
2011-02-21 13:40:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (virBufferError(buf)) {
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virDomainParseXMLDiskDesc:
|
|
|
|
* @node: node containing disk description
|
|
|
|
* @buf: a buffer for the result S-Expr
|
|
|
|
* @xendConfigVersion: xend configuration file format
|
|
|
|
*
|
|
|
|
* Parse the one disk in the XML description and add it to the S-Expr in buf
|
|
|
|
* This is a temporary interface as the S-Expr interface
|
|
|
|
* will be replaced by XML-RPC in the future. However the XML format should
|
|
|
|
* stay valid over time.
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success, -1 in case of error.
|
|
|
|
*/
|
|
|
|
int
|
2011-07-08 21:58:28 +00:00
|
|
|
xenFormatSxprDisk(virDomainDiskDefPtr def,
|
2011-02-21 13:40:12 +00:00
|
|
|
virBufferPtr buf,
|
|
|
|
int hvm,
|
|
|
|
int xendConfigVersion,
|
|
|
|
int isAttach)
|
2011-02-21 13:40:09 +00:00
|
|
|
{
|
|
|
|
/* Xend (all versions) put the floppy device config
|
|
|
|
* under the hvm (image (os)) block
|
|
|
|
*/
|
|
|
|
if (hvm &&
|
|
|
|
def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) {
|
|
|
|
if (isAttach) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Cannot directly attach floppy %s"), def->src);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Xend <= 3.0.2 doesn't include cdrom config here */
|
|
|
|
if (hvm &&
|
|
|
|
def->device == VIR_DOMAIN_DISK_DEVICE_CDROM &&
|
|
|
|
xendConfigVersion == 1) {
|
|
|
|
if (isAttach) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Cannot directly attach CDROM %s"), def->src);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isAttach)
|
|
|
|
virBufferAddLit(buf, "(device ");
|
|
|
|
|
|
|
|
/* Normally disks are in a (device (vbd ...)) block
|
|
|
|
* but blktap disks ended up in a differently named
|
|
|
|
* (device (tap ....)) block.... */
|
|
|
|
if (def->driverName && STREQ(def->driverName, "tap")) {
|
|
|
|
virBufferAddLit(buf, "(tap ");
|
|
|
|
} else if (def->driverName && STREQ(def->driverName, "tap2")) {
|
|
|
|
virBufferAddLit(buf, "(tap2 ");
|
|
|
|
} else {
|
|
|
|
virBufferAddLit(buf, "(vbd ");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hvm) {
|
|
|
|
/* Xend <= 3.0.2 wants a ioemu: prefix on devices for HVM */
|
|
|
|
if (xendConfigVersion == 1) {
|
|
|
|
virBufferEscapeSexpr(buf, "(dev 'ioemu:%s')", def->dst);
|
|
|
|
} else {
|
|
|
|
/* But newer does not */
|
|
|
|
virBufferEscapeSexpr(buf, "(dev '%s:", def->dst);
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, "%s')",
|
2011-02-21 13:40:09 +00:00
|
|
|
def->device == VIR_DOMAIN_DISK_DEVICE_CDROM ?
|
|
|
|
"cdrom" : "disk");
|
|
|
|
}
|
|
|
|
} else if (def->device == VIR_DOMAIN_DISK_DEVICE_CDROM) {
|
|
|
|
virBufferEscapeSexpr(buf, "(dev '%s:cdrom')", def->dst);
|
|
|
|
} else {
|
|
|
|
virBufferEscapeSexpr(buf, "(dev '%s')", def->dst);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (def->src) {
|
|
|
|
if (def->driverName) {
|
|
|
|
if (STREQ(def->driverName, "tap") ||
|
|
|
|
STREQ(def->driverName, "tap2")) {
|
|
|
|
virBufferEscapeSexpr(buf, "(uname '%s:", def->driverName);
|
|
|
|
virBufferEscapeSexpr(buf, "%s:",
|
|
|
|
def->driverType ? def->driverType : "aio");
|
|
|
|
virBufferEscapeSexpr(buf, "%s')", def->src);
|
|
|
|
} else {
|
|
|
|
virBufferEscapeSexpr(buf, "(uname '%s:", def->driverName);
|
|
|
|
virBufferEscapeSexpr(buf, "%s')", def->src);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (def->type == VIR_DOMAIN_DISK_TYPE_FILE) {
|
|
|
|
virBufferEscapeSexpr(buf, "(uname 'file:%s')", def->src);
|
|
|
|
} else if (def->type == VIR_DOMAIN_DISK_TYPE_BLOCK) {
|
|
|
|
if (def->src[0] == '/')
|
|
|
|
virBufferEscapeSexpr(buf, "(uname 'phy:%s')", def->src);
|
|
|
|
else
|
|
|
|
virBufferEscapeSexpr(buf, "(uname 'phy:/dev/%s')",
|
|
|
|
def->src);
|
|
|
|
} else {
|
|
|
|
XENXS_ERROR(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("unsupported disk type %s"),
|
|
|
|
virDomainDiskTypeToString(def->type));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (def->readonly)
|
|
|
|
virBufferAddLit(buf, "(mode 'r')");
|
|
|
|
else if (def->shared)
|
|
|
|
virBufferAddLit(buf, "(mode 'w!')");
|
|
|
|
else
|
|
|
|
virBufferAddLit(buf, "(mode 'w')");
|
2011-08-13 21:42:48 +00:00
|
|
|
if (def->transient) {
|
|
|
|
XENXS_ERROR(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("transient disks not supported yet"));
|
|
|
|
return -1;
|
|
|
|
}
|
2011-02-21 13:40:09 +00:00
|
|
|
|
|
|
|
if (!isAttach)
|
|
|
|
virBufferAddLit(buf, ")");
|
|
|
|
|
|
|
|
virBufferAddLit(buf, ")");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-02-21 13:40:12 +00:00
|
|
|
* xenFormatSxprNet
|
2011-02-21 13:40:09 +00:00
|
|
|
* @node: node containing the interface description
|
|
|
|
* @buf: a buffer for the result S-Expr
|
|
|
|
* @xendConfigVersion: xend configuration file format
|
|
|
|
*
|
|
|
|
* Parse the one interface the XML description and add it to the S-Expr in buf
|
|
|
|
* This is a temporary interface as the S-Expr interface
|
|
|
|
* will be replaced by XML-RPC in the future. However the XML format should
|
|
|
|
* stay valid over time.
|
|
|
|
*
|
|
|
|
* Returns 0 in case of success, -1 in case of error.
|
|
|
|
*/
|
|
|
|
int
|
2011-02-21 13:40:12 +00:00
|
|
|
xenFormatSxprNet(virConnectPtr conn,
|
|
|
|
virDomainNetDefPtr def,
|
|
|
|
virBufferPtr buf,
|
|
|
|
int hvm,
|
|
|
|
int xendConfigVersion,
|
|
|
|
int isAttach)
|
2011-02-21 13:40:09 +00:00
|
|
|
{
|
|
|
|
const char *script = DEFAULT_VIF_SCRIPT;
|
|
|
|
|
|
|
|
if (def->type != VIR_DOMAIN_NET_TYPE_BRIDGE &&
|
|
|
|
def->type != VIR_DOMAIN_NET_TYPE_NETWORK &&
|
|
|
|
def->type != VIR_DOMAIN_NET_TYPE_ETHERNET) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unsupported network type %d"), def->type);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isAttach)
|
|
|
|
virBufferAddLit(buf, "(device ");
|
|
|
|
|
|
|
|
virBufferAddLit(buf, "(vif ");
|
|
|
|
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf,
|
2011-02-21 13:40:09 +00:00
|
|
|
"(mac '%02x:%02x:%02x:%02x:%02x:%02x')",
|
|
|
|
def->mac[0], def->mac[1], def->mac[2],
|
|
|
|
def->mac[3], def->mac[4], def->mac[5]);
|
|
|
|
|
|
|
|
switch (def->type) {
|
|
|
|
case VIR_DOMAIN_NET_TYPE_BRIDGE:
|
|
|
|
virBufferEscapeSexpr(buf, "(bridge '%s')", def->data.bridge.brname);
|
|
|
|
if (def->data.bridge.script)
|
|
|
|
script = def->data.bridge.script;
|
|
|
|
|
|
|
|
virBufferEscapeSexpr(buf, "(script '%s')", script);
|
|
|
|
if (def->data.bridge.ipaddr != NULL)
|
|
|
|
virBufferEscapeSexpr(buf, "(ip '%s')", def->data.bridge.ipaddr);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_NET_TYPE_NETWORK:
|
|
|
|
{
|
|
|
|
virNetworkPtr network =
|
|
|
|
virNetworkLookupByName(conn, def->data.network.name);
|
|
|
|
char *bridge;
|
|
|
|
|
|
|
|
if (!network) {
|
|
|
|
XENXS_ERROR(VIR_ERR_NO_NETWORK, "%s",
|
|
|
|
def->data.network.name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bridge = virNetworkGetBridgeName(network);
|
|
|
|
virNetworkFree(network);
|
|
|
|
if (!bridge) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("network %s is not active"),
|
|
|
|
def->data.network.name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
virBufferEscapeSexpr(buf, "(bridge '%s')", bridge);
|
|
|
|
virBufferEscapeSexpr(buf, "(script '%s')", script);
|
|
|
|
VIR_FREE(bridge);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_NET_TYPE_ETHERNET:
|
|
|
|
if (def->data.ethernet.script)
|
|
|
|
virBufferEscapeSexpr(buf, "(script '%s')",
|
|
|
|
def->data.ethernet.script);
|
|
|
|
if (def->data.ethernet.ipaddr != NULL)
|
|
|
|
virBufferEscapeSexpr(buf, "(ip '%s')", def->data.ethernet.ipaddr);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_NET_TYPE_USER:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_SERVER:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_CLIENT:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_MCAST:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_INTERNAL:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_DIRECT:
|
|
|
|
case VIR_DOMAIN_NET_TYPE_LAST:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (def->ifname != NULL &&
|
|
|
|
!STRPREFIX(def->ifname, "vif"))
|
|
|
|
virBufferEscapeSexpr(buf, "(vifname '%s')", def->ifname);
|
|
|
|
|
|
|
|
if (!hvm) {
|
|
|
|
if (def->model != NULL)
|
|
|
|
virBufferEscapeSexpr(buf, "(model '%s')", def->model);
|
|
|
|
}
|
|
|
|
else if (def->model == NULL) {
|
|
|
|
/*
|
|
|
|
* apparently (type ioemu) breaks paravirt drivers on HVM so skip
|
|
|
|
* this from XEND_CONFIG_MAX_VERS_NET_TYPE_IOEMU
|
|
|
|
*/
|
|
|
|
if (xendConfigVersion <= XEND_CONFIG_MAX_VERS_NET_TYPE_IOEMU)
|
|
|
|
virBufferAddLit(buf, "(type ioemu)");
|
|
|
|
}
|
|
|
|
else if (STREQ(def->model, "netfront")) {
|
|
|
|
virBufferAddLit(buf, "(type netfront)");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
virBufferEscapeSexpr(buf, "(model '%s')", def->model);
|
|
|
|
virBufferAddLit(buf, "(type ioemu)");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isAttach)
|
|
|
|
virBufferAddLit(buf, ")");
|
|
|
|
|
|
|
|
virBufferAddLit(buf, ")");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2011-02-21 13:40:12 +00:00
|
|
|
xenFormatSxprPCI(virDomainHostdevDefPtr def,
|
|
|
|
virBufferPtr buf)
|
2011-02-21 13:40:09 +00:00
|
|
|
{
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, "(dev (domain 0x%04x)(bus 0x%02x)(slot 0x%02x)(func 0x%x))",
|
2011-02-21 13:40:09 +00:00
|
|
|
def->source.subsys.u.pci.domain,
|
|
|
|
def->source.subsys.u.pci.bus,
|
|
|
|
def->source.subsys.u.pci.slot,
|
|
|
|
def->source.subsys.u.pci.function);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2011-02-21 13:40:12 +00:00
|
|
|
xenFormatSxprOnePCI(virDomainHostdevDefPtr def,
|
|
|
|
virBufferPtr buf,
|
|
|
|
int detach)
|
2011-02-21 13:40:09 +00:00
|
|
|
{
|
|
|
|
if (def->managed) {
|
2011-08-23 08:53:15 +00:00
|
|
|
XENXS_ERROR(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
2011-02-21 13:40:09 +00:00
|
|
|
_("managed PCI devices not supported with XenD"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
virBufferAddLit(buf, "(pci ");
|
2011-02-21 13:40:12 +00:00
|
|
|
xenFormatSxprPCI(def, buf);
|
2011-02-21 13:40:09 +00:00
|
|
|
if (detach)
|
|
|
|
virBufferAddLit(buf, "(state 'Closing')");
|
|
|
|
else
|
|
|
|
virBufferAddLit(buf, "(state 'Initialising')");
|
|
|
|
virBufferAddLit(buf, ")");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2011-02-21 13:40:12 +00:00
|
|
|
xenFormatSxprAllPCI(virDomainDefPtr def,
|
|
|
|
virBufferPtr buf)
|
2011-02-21 13:40:09 +00:00
|
|
|
{
|
|
|
|
int hasPCI = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0 ; i < def->nhostdevs ; i++)
|
|
|
|
if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
|
|
|
|
def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
|
|
|
|
hasPCI = 1;
|
|
|
|
|
|
|
|
if (!hasPCI)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* With the (domain ...) block we have the following odd setup
|
|
|
|
*
|
|
|
|
* (device
|
|
|
|
* (pci
|
|
|
|
* (dev (domain 0x0000) (bus 0x00) (slot 0x1b) (func 0x0))
|
|
|
|
* (dev (domain 0x0000) (bus 0x00) (slot 0x13) (func 0x0))
|
|
|
|
* )
|
|
|
|
* )
|
|
|
|
*
|
|
|
|
* Normally there is one (device ...) block per device, but in the
|
|
|
|
* weird world of Xen PCI, one (device ...) covers multiple devices.
|
|
|
|
*/
|
|
|
|
|
|
|
|
virBufferAddLit(buf, "(device (pci ");
|
|
|
|
for (i = 0 ; i < def->nhostdevs ; i++) {
|
|
|
|
if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
|
|
|
|
def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
|
|
|
|
if (def->hostdevs[i]->managed) {
|
2011-08-23 08:53:15 +00:00
|
|
|
XENXS_ERROR(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
2011-02-21 13:40:09 +00:00
|
|
|
_("managed PCI devices not supported with XenD"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-02-21 13:40:12 +00:00
|
|
|
xenFormatSxprPCI(def->hostdevs[i], buf);
|
2011-02-21 13:40:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
virBufferAddLit(buf, "))");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2011-02-21 13:40:12 +00:00
|
|
|
xenFormatSxprSound(virDomainDefPtr def,
|
|
|
|
virBufferPtr buf)
|
2011-02-21 13:40:09 +00:00
|
|
|
{
|
|
|
|
const char *str;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0 ; i < def->nsounds ; i++) {
|
|
|
|
if (!(str = virDomainSoundModelTypeToString(def->sounds[i]->model))) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected sound model %d"),
|
|
|
|
def->sounds[i]->model);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (i)
|
|
|
|
virBufferAddChar(buf, ',');
|
|
|
|
virBufferEscapeSexpr(buf, "%s", str);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virBufferError(buf)) {
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2011-02-21 13:40:12 +00:00
|
|
|
xenFormatSxprInput(virDomainInputDefPtr input,
|
|
|
|
virBufferPtr buf)
|
2011-02-21 13:40:09 +00:00
|
|
|
{
|
|
|
|
if (input->bus != VIR_DOMAIN_INPUT_BUS_USB)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (input->type != VIR_DOMAIN_INPUT_TYPE_MOUSE &&
|
|
|
|
input->type != VIR_DOMAIN_INPUT_TYPE_TABLET) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected input type %d"), input->type);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(buf, "(usbdevice %s)",
|
2011-02-21 13:40:09 +00:00
|
|
|
input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE ?
|
|
|
|
"mouse" : "tablet");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Computing the vcpu_avail bitmask works because MAX_VIRT_CPUS is
|
|
|
|
either 32, or 64 on a platform where long is big enough. */
|
|
|
|
verify(MAX_VIRT_CPUS <= sizeof(1UL) * CHAR_BIT);
|
|
|
|
|
|
|
|
/**
|
2011-02-21 13:40:12 +00:00
|
|
|
* xenFormatSxpr:
|
2011-02-21 13:40:09 +00:00
|
|
|
* @conn: pointer to the hypervisor connection
|
|
|
|
* @def: domain config definition
|
|
|
|
* @xendConfigVersion: xend configuration file format
|
|
|
|
*
|
|
|
|
* Generate an SEXPR representing the domain configuration.
|
|
|
|
*
|
|
|
|
* Returns the 0 terminated S-Expr string or NULL in case of error.
|
|
|
|
* the caller must free() the returned value.
|
|
|
|
*/
|
|
|
|
char *
|
2011-02-21 13:40:12 +00:00
|
|
|
xenFormatSxpr(virConnectPtr conn,
|
|
|
|
virDomainDefPtr def,
|
|
|
|
int xendConfigVersion)
|
2011-02-21 13:40:09 +00:00
|
|
|
{
|
|
|
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
|
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
|
|
const char *tmp;
|
|
|
|
char *bufout;
|
|
|
|
int hvm = 0, i;
|
|
|
|
|
2011-05-09 09:24:09 +00:00
|
|
|
VIR_DEBUG("Formatting domain sexpr");
|
2011-02-21 13:40:09 +00:00
|
|
|
|
|
|
|
virBufferAddLit(&buf, "(vm ");
|
|
|
|
virBufferEscapeSexpr(&buf, "(name '%s')", def->name);
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(&buf, "(memory %lu)(maxmem %lu)",
|
2011-02-21 13:40:09 +00:00
|
|
|
VIR_DIV_UP(def->mem.cur_balloon, 1024),
|
|
|
|
VIR_DIV_UP(def->mem.max_balloon, 1024));
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(&buf, "(vcpus %u)", def->maxvcpus);
|
2011-02-21 13:40:09 +00:00
|
|
|
/* Computing the vcpu_avail bitmask works because MAX_VIRT_CPUS is
|
|
|
|
either 32, or 64 on a platform where long is big enough. */
|
|
|
|
if (def->vcpus < def->maxvcpus)
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(&buf, "(vcpu_avail %lu)", (1UL << def->vcpus) - 1);
|
2011-02-21 13:40:09 +00:00
|
|
|
|
|
|
|
if (def->cpumask) {
|
|
|
|
char *ranges = virDomainCpuSetFormat(def->cpumask, def->cpumasklen);
|
|
|
|
if (ranges == NULL)
|
|
|
|
goto error;
|
|
|
|
virBufferEscapeSexpr(&buf, "(cpus '%s')", ranges);
|
|
|
|
VIR_FREE(ranges);
|
|
|
|
}
|
|
|
|
|
|
|
|
virUUIDFormat(def->uuid, uuidstr);
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(&buf, "(uuid '%s')", uuidstr);
|
2011-02-21 13:40:09 +00:00
|
|
|
|
|
|
|
if (def->description)
|
|
|
|
virBufferEscapeSexpr(&buf, "(description '%s')", def->description);
|
|
|
|
|
|
|
|
if (def->os.bootloader) {
|
|
|
|
if (def->os.bootloader[0])
|
|
|
|
virBufferEscapeSexpr(&buf, "(bootloader '%s')", def->os.bootloader);
|
|
|
|
else
|
|
|
|
virBufferAddLit(&buf, "(bootloader)");
|
|
|
|
|
|
|
|
if (def->os.bootloaderArgs)
|
|
|
|
virBufferEscapeSexpr(&buf, "(bootloader_args '%s')", def->os.bootloaderArgs);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(tmp = virDomainLifecycleTypeToString(def->onPoweroff))) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected lifecycle value %d"), def->onPoweroff);
|
|
|
|
goto error;
|
|
|
|
}
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(&buf, "(on_poweroff '%s')", tmp);
|
2011-02-21 13:40:09 +00:00
|
|
|
|
|
|
|
if (!(tmp = virDomainLifecycleTypeToString(def->onReboot))) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected lifecycle value %d"), def->onReboot);
|
|
|
|
goto error;
|
|
|
|
}
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(&buf, "(on_reboot '%s')", tmp);
|
2011-02-21 13:40:09 +00:00
|
|
|
|
|
|
|
if (!(tmp = virDomainLifecycleCrashTypeToString(def->onCrash))) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unexpected lifecycle value %d"), def->onCrash);
|
|
|
|
goto error;
|
|
|
|
}
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(&buf, "(on_crash '%s')", tmp);
|
2011-02-21 13:40:09 +00:00
|
|
|
|
|
|
|
/* Set localtime here for current XenD (both PV & HVM) */
|
|
|
|
if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME) {
|
|
|
|
if (def->clock.data.timezone) {
|
|
|
|
XENXS_ERROR(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
"%s", _("configurable timezones are not supported"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
virBufferAddLit(&buf, "(localtime 1)");
|
|
|
|
} else if (def->clock.offset != VIR_DOMAIN_CLOCK_OFFSET_UTC) {
|
|
|
|
XENXS_ERROR(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("unsupported clock offset '%s'"),
|
|
|
|
virDomainClockOffsetTypeToString(def->clock.offset));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!def->os.bootloader) {
|
|
|
|
if (STREQ(def->os.type, "hvm"))
|
|
|
|
hvm = 1;
|
|
|
|
|
|
|
|
if (hvm)
|
|
|
|
virBufferAddLit(&buf, "(image (hvm ");
|
|
|
|
else
|
|
|
|
virBufferAddLit(&buf, "(image (linux ");
|
|
|
|
|
|
|
|
if (hvm &&
|
|
|
|
def->os.loader == NULL) {
|
|
|
|
XENXS_ERROR(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s",_("no HVM domain loader"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (def->os.kernel)
|
|
|
|
virBufferEscapeSexpr(&buf, "(kernel '%s')", def->os.kernel);
|
|
|
|
if (def->os.initrd)
|
|
|
|
virBufferEscapeSexpr(&buf, "(ramdisk '%s')", def->os.initrd);
|
|
|
|
if (def->os.root)
|
|
|
|
virBufferEscapeSexpr(&buf, "(root '%s')", def->os.root);
|
|
|
|
if (def->os.cmdline)
|
|
|
|
virBufferEscapeSexpr(&buf, "(args '%s')", def->os.cmdline);
|
|
|
|
|
|
|
|
if (hvm) {
|
|
|
|
char bootorder[VIR_DOMAIN_BOOT_LAST+1];
|
|
|
|
if (def->os.kernel)
|
|
|
|
virBufferEscapeSexpr(&buf, "(loader '%s')", def->os.loader);
|
|
|
|
else
|
|
|
|
virBufferEscapeSexpr(&buf, "(kernel '%s')", def->os.loader);
|
|
|
|
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(&buf, "(vcpus %u)", def->maxvcpus);
|
2011-02-21 13:40:09 +00:00
|
|
|
if (def->vcpus < def->maxvcpus)
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(&buf, "(vcpu_avail %lu)",
|
2011-02-21 13:40:09 +00:00
|
|
|
(1UL << def->vcpus) - 1);
|
|
|
|
|
|
|
|
for (i = 0 ; i < def->os.nBootDevs ; i++) {
|
|
|
|
switch (def->os.bootDevs[i]) {
|
|
|
|
case VIR_DOMAIN_BOOT_FLOPPY:
|
|
|
|
bootorder[i] = 'a';
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
case VIR_DOMAIN_BOOT_DISK:
|
|
|
|
bootorder[i] = 'c';
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_BOOT_CDROM:
|
|
|
|
bootorder[i] = 'd';
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_BOOT_NET:
|
|
|
|
bootorder[i] = 'n';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (def->os.nBootDevs == 0) {
|
|
|
|
bootorder[0] = 'c';
|
|
|
|
bootorder[1] = '\0';
|
|
|
|
} else {
|
|
|
|
bootorder[def->os.nBootDevs] = '\0';
|
|
|
|
}
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(&buf, "(boot %s)", bootorder);
|
2011-02-21 13:40:09 +00:00
|
|
|
|
|
|
|
/* some disk devices are defined here */
|
|
|
|
for (i = 0 ; i < def->ndisks ; i++) {
|
|
|
|
switch (def->disks[i]->device) {
|
|
|
|
case VIR_DOMAIN_DISK_DEVICE_CDROM:
|
|
|
|
/* Only xend <= 3.0.2 wants cdrom config here */
|
|
|
|
if (xendConfigVersion != 1)
|
|
|
|
break;
|
|
|
|
if (!STREQ(def->disks[i]->dst, "hdc") ||
|
|
|
|
def->disks[i]->src == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
virBufferEscapeSexpr(&buf, "(cdrom '%s')",
|
|
|
|
def->disks[i]->src);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case VIR_DOMAIN_DISK_DEVICE_FLOPPY:
|
|
|
|
/* all xend versions define floppies here */
|
|
|
|
virBufferEscapeSexpr(&buf, "(%s ", def->disks[i]->dst);
|
|
|
|
virBufferEscapeSexpr(&buf, "'%s')", def->disks[i]->src);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (def->features & (1 << VIR_DOMAIN_FEATURE_ACPI))
|
|
|
|
virBufferAddLit(&buf, "(acpi 1)");
|
|
|
|
if (def->features & (1 << VIR_DOMAIN_FEATURE_APIC))
|
|
|
|
virBufferAddLit(&buf, "(apic 1)");
|
|
|
|
if (def->features & (1 << VIR_DOMAIN_FEATURE_PAE))
|
|
|
|
virBufferAddLit(&buf, "(pae 1)");
|
|
|
|
if (def->features & (1 << VIR_DOMAIN_FEATURE_HAP))
|
|
|
|
virBufferAddLit(&buf, "(hap 1)");
|
2011-06-15 13:27:43 +00:00
|
|
|
if (def->features & (1 << VIR_DOMAIN_FEATURE_VIRIDIAN))
|
|
|
|
virBufferAddLit(&buf, "(viridian 1)");
|
2011-02-21 13:40:09 +00:00
|
|
|
|
|
|
|
virBufferAddLit(&buf, "(usb 1)");
|
|
|
|
|
|
|
|
for (i = 0 ; i < def->ninputs ; i++)
|
2011-02-21 13:40:12 +00:00
|
|
|
if (xenFormatSxprInput(def->inputs[i], &buf) < 0)
|
2011-02-21 13:40:09 +00:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (def->parallels) {
|
|
|
|
virBufferAddLit(&buf, "(parallel ");
|
2011-02-21 13:40:12 +00:00
|
|
|
if (xenFormatSxprChr(def->parallels[0], &buf) < 0)
|
2011-02-21 13:40:09 +00:00
|
|
|
goto error;
|
|
|
|
virBufferAddLit(&buf, ")");
|
|
|
|
} else {
|
|
|
|
virBufferAddLit(&buf, "(parallel none)");
|
|
|
|
}
|
|
|
|
if (def->serials) {
|
2011-02-25 14:41:12 +00:00
|
|
|
if ((def->nserials > 1) || (def->serials[0]->target.port != 0)) {
|
|
|
|
int maxport = -1;
|
|
|
|
int j = 0;
|
|
|
|
|
|
|
|
virBufferAddLit(&buf, "(serial (");
|
|
|
|
for (i = 0; i < def->nserials; i++)
|
|
|
|
if (def->serials[i]->target.port > maxport)
|
|
|
|
maxport = def->serials[i]->target.port;
|
|
|
|
|
|
|
|
for (i = 0; i <= maxport; i++) {
|
|
|
|
virDomainChrDefPtr chr = NULL;
|
|
|
|
|
|
|
|
if (i)
|
|
|
|
virBufferAddLit(&buf, " ");
|
|
|
|
for (j = 0; j < def->nserials; j++) {
|
|
|
|
if (def->serials[j]->target.port == i) {
|
|
|
|
chr = def->serials[j];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (chr) {
|
|
|
|
if (xenFormatSxprChr(chr, &buf) < 0)
|
|
|
|
goto error;
|
|
|
|
} else {
|
|
|
|
virBufferAddLit(&buf, "none");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
virBufferAddLit(&buf, "))");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
virBufferAddLit(&buf, "(serial ");
|
|
|
|
if (xenFormatSxprChr(def->serials[0], &buf) < 0)
|
|
|
|
goto error;
|
|
|
|
virBufferAddLit(&buf, ")");
|
|
|
|
}
|
2011-02-21 13:40:09 +00:00
|
|
|
} else {
|
|
|
|
virBufferAddLit(&buf, "(serial none)");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set localtime here to keep old XenD happy for HVM */
|
|
|
|
if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME)
|
|
|
|
virBufferAddLit(&buf, "(localtime 1)");
|
|
|
|
|
|
|
|
if (def->sounds) {
|
|
|
|
virBufferAddLit(&buf, "(soundhw '");
|
2011-02-21 13:40:12 +00:00
|
|
|
if (xenFormatSxprSound(def, &buf) < 0)
|
2011-02-21 13:40:09 +00:00
|
|
|
goto error;
|
|
|
|
virBufferAddLit(&buf, "')");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get the device emulation model */
|
|
|
|
if (def->emulator && (hvm || xendConfigVersion >= 3))
|
|
|
|
virBufferEscapeSexpr(&buf, "(device_model '%s')", def->emulator);
|
|
|
|
|
2011-05-10 11:24:14 +00:00
|
|
|
/* look for HPET in order to override the hypervisor/xend default */
|
|
|
|
for (i = 0; i < def->clock.ntimers; i++) {
|
|
|
|
if (def->clock.timers[i]->name == VIR_DOMAIN_TIMER_NAME_HPET &&
|
|
|
|
def->clock.timers[i]->present != -1) {
|
|
|
|
virBufferAsprintf(&buf, "(hpet %d)",
|
|
|
|
def->clock.timers[i]->present);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-02-21 13:40:09 +00:00
|
|
|
|
|
|
|
/* PV graphics for xen <= 3.0.4, or HVM graphics for xen <= 3.1.0 */
|
|
|
|
if ((!hvm && xendConfigVersion < XEND_CONFIG_MIN_VERS_PVFB_NEWCONF) ||
|
|
|
|
(hvm && xendConfigVersion < 4)) {
|
|
|
|
if ((def->ngraphics == 1) &&
|
2011-02-21 13:40:12 +00:00
|
|
|
xenFormatSxprGraphicsOld(def->graphics[0],
|
|
|
|
&buf, xendConfigVersion) < 0)
|
2011-02-21 13:40:09 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
virBufferAddLit(&buf, "))");
|
2011-07-07 01:17:16 +00:00
|
|
|
} else {
|
|
|
|
/* PV domains accept kernel cmdline args */
|
|
|
|
if (def->os.cmdline) {
|
|
|
|
virBufferEscapeSexpr(&buf, "(image (linux (args '%s')))",
|
|
|
|
def->os.cmdline);
|
|
|
|
}
|
2011-02-21 13:40:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0 ; i < def->ndisks ; i++)
|
2011-07-08 21:58:28 +00:00
|
|
|
if (xenFormatSxprDisk(def->disks[i],
|
2011-02-21 13:40:12 +00:00
|
|
|
&buf, hvm, xendConfigVersion, 0) < 0)
|
2011-02-21 13:40:09 +00:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
for (i = 0 ; i < def->nnets ; i++)
|
2011-02-21 13:40:12 +00:00
|
|
|
if (xenFormatSxprNet(conn, def->nets[i],
|
|
|
|
&buf, hvm, xendConfigVersion, 0) < 0)
|
2011-02-21 13:40:09 +00:00
|
|
|
goto error;
|
|
|
|
|
2011-02-21 13:40:12 +00:00
|
|
|
if (xenFormatSxprAllPCI(def, &buf) < 0)
|
2011-02-21 13:40:09 +00:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* New style PV graphics config xen >= 3.0.4,
|
|
|
|
* or HVM graphics config xen >= 3.0.5 */
|
|
|
|
if ((xendConfigVersion >= XEND_CONFIG_MIN_VERS_PVFB_NEWCONF && !hvm) ||
|
|
|
|
(xendConfigVersion >= 4 && hvm)) {
|
|
|
|
if ((def->ngraphics == 1) &&
|
2011-02-21 13:40:12 +00:00
|
|
|
xenFormatSxprGraphicsNew(def->graphics[0], &buf) < 0)
|
2011-02-21 13:40:09 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
virBufferAddLit(&buf, ")"); /* closes (vm */
|
|
|
|
|
|
|
|
if (virBufferError(&buf)) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
bufout = virBufferContentAndReset(&buf);
|
|
|
|
VIR_DEBUG("Formatted sexpr: \n%s", bufout);
|
|
|
|
return bufout;
|
|
|
|
|
|
|
|
error:
|
|
|
|
virBufferFreeAndReset(&buf);
|
|
|
|
return NULL;
|
|
|
|
}
|