mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-04 03:55:20 +00:00
f9159efec8
Commit 2d43f0a2dc
dropped virDomainDiskTranslateSourcePool()'s
first argument but failed to update callers in the bhyve driver.
Signed-off-by: Andrea Bolognani <abologna@redhat.com>
922 lines
29 KiB
C
922 lines
29 KiB
C
/*
|
|
* bhyve_command.c: bhyve command generation
|
|
*
|
|
* Copyright (C) 2014 Roman Bogorodskiy
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <net/if.h>
|
|
#include <net/if_tap.h>
|
|
|
|
#include "bhyve_capabilities.h"
|
|
#include "bhyve_command.h"
|
|
#include "bhyve_domain.h"
|
|
#include "bhyve_driver.h"
|
|
#include "datatypes.h"
|
|
#include "viralloc.h"
|
|
#include "virfile.h"
|
|
#include "virstring.h"
|
|
#include "virlog.h"
|
|
#include "virnetdev.h"
|
|
#include "virnetdevbridge.h"
|
|
#include "virnetdevtap.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_BHYVE
|
|
|
|
VIR_LOG_INIT("bhyve.bhyve_command");
|
|
|
|
static int
|
|
bhyveBuildNetArgStr(virConnectPtr conn,
|
|
const virDomainDef *def,
|
|
virDomainNetDefPtr net,
|
|
virCommandPtr cmd,
|
|
bool dryRun)
|
|
{
|
|
char macaddr[VIR_MAC_STRING_BUFLEN];
|
|
char *realifname = NULL;
|
|
char *brname = NULL;
|
|
char *nic_model = NULL;
|
|
int ret = -1;
|
|
virDomainNetType actualType = virDomainNetGetActualType(net);
|
|
|
|
if (STREQ(net->model, "virtio")) {
|
|
if (VIR_STRDUP(nic_model, "virtio-net") < 0)
|
|
return -1;
|
|
} else if (STREQ(net->model, "e1000")) {
|
|
if ((bhyveDriverGetCaps(conn) & BHYVE_CAP_NET_E1000) != 0) {
|
|
if (VIR_STRDUP(nic_model, "e1000") < 0)
|
|
return -1;
|
|
} else {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("NIC model 'e1000' is not supported "
|
|
"by given bhyve binary"));
|
|
return -1;
|
|
}
|
|
} else {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("NIC model '%s' is not supported"),
|
|
net->model);
|
|
return -1;
|
|
}
|
|
|
|
if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) {
|
|
if (VIR_STRDUP(brname, virDomainNetGetActualBridgeName(net)) < 0)
|
|
goto cleanup;
|
|
} else {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Network type %d is not supported"),
|
|
virDomainNetGetActualType(net));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!net->ifname ||
|
|
STRPREFIX(net->ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
|
|
strchr(net->ifname, '%')) {
|
|
VIR_FREE(net->ifname);
|
|
if (VIR_STRDUP(net->ifname, VIR_NET_GENERATED_TAP_PREFIX "%d") < 0)
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!dryRun) {
|
|
if (virNetDevTapCreateInBridgePort(brname, &net->ifname, &net->mac,
|
|
def->uuid, NULL, NULL, 0,
|
|
virDomainNetGetActualVirtPortProfile(net),
|
|
virDomainNetGetActualVlan(net),
|
|
NULL, 0, NULL,
|
|
VIR_NETDEV_TAP_CREATE_IFUP | VIR_NETDEV_TAP_CREATE_PERSIST) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
realifname = virNetDevTapGetRealDeviceName(net->ifname);
|
|
|
|
if (realifname == NULL)
|
|
goto cleanup;
|
|
|
|
VIR_DEBUG("%s -> %s", net->ifname, realifname);
|
|
/* hack on top of other hack: we need to set
|
|
* interface to 'UP' again after re-opening to find its
|
|
* name
|
|
*/
|
|
if (virNetDevSetOnline(net->ifname, true) != 0)
|
|
goto cleanup;
|
|
} else {
|
|
if (VIR_STRDUP(realifname, "tap0") < 0)
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
virCommandAddArg(cmd, "-s");
|
|
virCommandAddArgFormat(cmd, "%d:0,%s,%s,mac=%s",
|
|
net->info.addr.pci.slot, nic_model,
|
|
realifname, virMacAddrFormat(&net->mac, macaddr));
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
if (ret < 0)
|
|
VIR_FREE(net->ifname);
|
|
VIR_FREE(brname);
|
|
VIR_FREE(realifname);
|
|
VIR_FREE(nic_model);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
bhyveBuildConsoleArgStr(const virDomainDef *def, virCommandPtr cmd)
|
|
{
|
|
|
|
virDomainChrDefPtr chr = NULL;
|
|
|
|
if (!def->nserials)
|
|
return 0;
|
|
|
|
chr = def->serials[0];
|
|
|
|
if (chr->source->type != VIR_DOMAIN_CHR_TYPE_NMDM) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("only nmdm console types are supported"));
|
|
return -1;
|
|
}
|
|
|
|
/* bhyve supports only two ports: com1 and com2 */
|
|
if (chr->target.port > 2) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("only two serial ports are supported"));
|
|
return -1;
|
|
}
|
|
|
|
virCommandAddArg(cmd, "-l");
|
|
virCommandAddArgFormat(cmd, "com%d,%s",
|
|
chr->target.port + 1, chr->source->data.file.path);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
bhyveBuildAHCIControllerArgStr(const virDomainDef *def,
|
|
virDomainControllerDefPtr controller,
|
|
virConnectPtr conn,
|
|
virCommandPtr cmd)
|
|
{
|
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
|
virBuffer device = VIR_BUFFER_INITIALIZER;
|
|
const char *disk_source;
|
|
size_t i;
|
|
int ret = -1;
|
|
|
|
for (i = 0; i < def->ndisks; i++) {
|
|
virDomainDiskDefPtr disk = def->disks[i];
|
|
if (disk->bus != VIR_DOMAIN_DISK_BUS_SATA)
|
|
continue;
|
|
|
|
if (disk->info.addr.drive.controller != controller->idx)
|
|
continue;
|
|
|
|
VIR_DEBUG("disk %zu controller %d", i, controller->idx);
|
|
|
|
if ((virDomainDiskGetType(disk) != VIR_STORAGE_TYPE_FILE) &&
|
|
(virDomainDiskGetType(disk) != VIR_STORAGE_TYPE_VOLUME)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("unsupported disk type"));
|
|
goto error;
|
|
}
|
|
|
|
if (virDomainDiskTranslateSourcePool(disk) < 0)
|
|
goto error;
|
|
|
|
disk_source = virDomainDiskGetSource(disk);
|
|
|
|
if ((disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) &&
|
|
(disk_source == NULL)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("cdrom device without source path "
|
|
"not supported"));
|
|
goto error;
|
|
}
|
|
|
|
switch (disk->device) {
|
|
case VIR_DOMAIN_DISK_DEVICE_DISK:
|
|
if ((bhyveDriverGetCaps(conn) & BHYVE_CAP_AHCI32SLOT))
|
|
virBufferAsprintf(&device, ",hd:%s", disk_source);
|
|
else
|
|
virBufferAsprintf(&device, "-hd,%s", disk_source);
|
|
break;
|
|
case VIR_DOMAIN_DISK_DEVICE_CDROM:
|
|
if ((bhyveDriverGetCaps(conn) & BHYVE_CAP_AHCI32SLOT))
|
|
virBufferAsprintf(&device, ",cd:%s", disk_source);
|
|
else
|
|
virBufferAsprintf(&device, "-cd,%s", disk_source);
|
|
break;
|
|
default:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("unsupported disk device"));
|
|
goto error;
|
|
}
|
|
virBufferAddBuffer(&buf, &device);
|
|
virBufferFreeAndReset(&device);
|
|
}
|
|
|
|
if (virBufferCheckError(&buf) < 0)
|
|
goto error;
|
|
|
|
virCommandAddArg(cmd, "-s");
|
|
virCommandAddArgFormat(cmd, "%d:0,ahci%s",
|
|
controller->info.addr.pci.slot,
|
|
virBufferCurrentContent(&buf));
|
|
|
|
ret = 0;
|
|
error:
|
|
virBufferFreeAndReset(&buf);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
bhyveBuildUSBControllerArgStr(const virDomainDef *def,
|
|
virDomainControllerDefPtr controller,
|
|
virCommandPtr cmd)
|
|
{
|
|
size_t i;
|
|
int ndevices = 0;
|
|
|
|
for (i = 0; i < def->ninputs; i++) {
|
|
virDomainInputDefPtr input = def->inputs[i];
|
|
|
|
if (input->bus != VIR_DOMAIN_INPUT_BUS_USB) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("only USB input devices are supported"));
|
|
return -1;
|
|
}
|
|
|
|
if (input->type != VIR_DOMAIN_INPUT_TYPE_TABLET) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("only tablet input devices are supported"));
|
|
return -1;
|
|
}
|
|
ndevices++;
|
|
}
|
|
|
|
if (ndevices != 1) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("only single input device is supported"));
|
|
return -1;
|
|
}
|
|
|
|
virCommandAddArg(cmd, "-s");
|
|
virCommandAddArgFormat(cmd, "%d:%d,xhci,tablet",
|
|
controller->info.addr.pci.slot,
|
|
controller->info.addr.pci.function);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
bhyveBuildVirtIODiskArgStr(const virDomainDef *def ATTRIBUTE_UNUSED,
|
|
virDomainDiskDefPtr disk,
|
|
virCommandPtr cmd)
|
|
{
|
|
const char *disk_source;
|
|
|
|
if (virDomainDiskTranslateSourcePool(disk) < 0)
|
|
return -1;
|
|
|
|
if (disk->device != VIR_DOMAIN_DISK_DEVICE_DISK) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("unsupported disk device"));
|
|
return -1;
|
|
}
|
|
|
|
if ((virDomainDiskGetType(disk) != VIR_STORAGE_TYPE_FILE) &&
|
|
(virDomainDiskGetType(disk) != VIR_STORAGE_TYPE_VOLUME)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("unsupported disk type"));
|
|
return -1;
|
|
}
|
|
|
|
disk_source = virDomainDiskGetSource(disk);
|
|
|
|
virCommandAddArg(cmd, "-s");
|
|
virCommandAddArgFormat(cmd, "%d:0,virtio-blk,%s",
|
|
disk->info.addr.pci.slot,
|
|
disk_source);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
bhyveBuildLPCArgStr(const virDomainDef *def ATTRIBUTE_UNUSED,
|
|
virCommandPtr cmd)
|
|
{
|
|
virCommandAddArgList(cmd, "-s", "1,lpc", NULL);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
bhyveBuildGraphicsArgStr(const virDomainDef *def,
|
|
virDomainGraphicsDefPtr graphics,
|
|
virDomainVideoDefPtr video,
|
|
virConnectPtr conn,
|
|
virCommandPtr cmd,
|
|
bool dryRun)
|
|
{
|
|
virBuffer opt = VIR_BUFFER_INITIALIZER;
|
|
virDomainGraphicsListenDefPtr glisten = NULL;
|
|
bool escapeAddr;
|
|
unsigned short port;
|
|
|
|
bhyveConnPtr driver = conn->privateData;
|
|
|
|
if (!(bhyveDriverGetCaps(conn) & BHYVE_CAP_LPC_BOOTROM) ||
|
|
def->os.bootloader ||
|
|
!def->os.loader) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Graphics are only supported"
|
|
" when booting using UEFI"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(bhyveDriverGetCaps(conn) & BHYVE_CAP_FBUF)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Bhyve version does not support framebuffer"));
|
|
return -1;
|
|
}
|
|
|
|
if (graphics->type != VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Only VNC supported"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(glisten = virDomainGraphicsGetListen(graphics, 0))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Missing listen element"));
|
|
goto error;
|
|
}
|
|
|
|
virBufferAsprintf(&opt, "%d:%d,fbuf", video->info.addr.pci.slot, video->info.addr.pci.function);
|
|
|
|
switch (glisten->type) {
|
|
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS:
|
|
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK:
|
|
virBufferAddLit(&opt, ",tcp=");
|
|
|
|
if (!graphics->data.vnc.autoport &&
|
|
(graphics->data.vnc.port < 5900 ||
|
|
graphics->data.vnc.port > 65535)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("vnc port must be in range [5900,65535]"));
|
|
goto error;
|
|
}
|
|
|
|
if (graphics->data.vnc.auth.passwd) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("vnc password auth not supported"));
|
|
goto error;
|
|
} else {
|
|
/* Bhyve doesn't support VNC Auth yet, so print a warning about
|
|
* unauthenticated VNC sessions */
|
|
VIR_WARN("%s", _("Security warning: currently VNC auth is not"
|
|
" supported."));
|
|
}
|
|
|
|
if (glisten->address) {
|
|
escapeAddr = strchr(glisten->address, ':') != NULL;
|
|
if (escapeAddr)
|
|
virBufferAsprintf(&opt, "[%s]", glisten->address);
|
|
else
|
|
virBufferAdd(&opt, glisten->address, -1);
|
|
}
|
|
|
|
if (!dryRun) {
|
|
if (graphics->data.vnc.autoport) {
|
|
if (virPortAllocatorAcquire(driver->remotePorts, &port) < 0)
|
|
return -1;
|
|
graphics->data.vnc.port = port;
|
|
} else {
|
|
if (virPortAllocatorSetUsed(driver->remotePorts,
|
|
graphics->data.vnc.port,
|
|
true) < 0)
|
|
VIR_WARN("Failed to mark VNC port '%d' as used by '%s'",
|
|
graphics->data.vnc.port, def->name);
|
|
}
|
|
}
|
|
|
|
virBufferAsprintf(&opt, ":%d", graphics->data.vnc.port);
|
|
break;
|
|
default:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Unsupported listen type"));
|
|
}
|
|
|
|
if (video->driver)
|
|
virBufferAsprintf(&opt, ",vga=%s",
|
|
virDomainVideoVGAConfTypeToString(video->driver->vgaconf));
|
|
|
|
virCommandAddArg(cmd, "-s");
|
|
virCommandAddArgBuffer(cmd, &opt);
|
|
return 0;
|
|
|
|
error:
|
|
virBufferFreeAndReset(&opt);
|
|
return -1;
|
|
}
|
|
|
|
virCommandPtr
|
|
virBhyveProcessBuildBhyveCmd(virConnectPtr conn,
|
|
virDomainDefPtr def, bool dryRun)
|
|
{
|
|
/*
|
|
* /usr/sbin/bhyve -c 2 -m 256 -AI -H -P \
|
|
* -s 0:0,hostbridge \
|
|
* -s 1:0,virtio-net,tap0 \
|
|
* -s 2:0,ahci-hd,${IMG} \
|
|
* -S 31,uart,stdio \
|
|
* vm0
|
|
*/
|
|
size_t i;
|
|
bool add_lpc = false;
|
|
int nusbcontrollers = 0;
|
|
|
|
virCommandPtr cmd = virCommandNew(BHYVE);
|
|
|
|
/* CPUs */
|
|
virCommandAddArg(cmd, "-c");
|
|
virCommandAddArgFormat(cmd, "%d", virDomainDefGetVcpus(def));
|
|
|
|
/* Memory */
|
|
virCommandAddArg(cmd, "-m");
|
|
virCommandAddArgFormat(cmd, "%llu",
|
|
VIR_DIV_UP(virDomainDefGetMemoryInitial(def), 1024));
|
|
|
|
/* Options */
|
|
if (def->features[VIR_DOMAIN_FEATURE_ACPI] == VIR_TRISTATE_SWITCH_ON)
|
|
virCommandAddArg(cmd, "-A"); /* Create an ACPI table */
|
|
if (def->features[VIR_DOMAIN_FEATURE_APIC] == VIR_TRISTATE_SWITCH_ON)
|
|
virCommandAddArg(cmd, "-I"); /* Present ioapic to the guest */
|
|
|
|
switch (def->clock.offset) {
|
|
case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME:
|
|
/* used by default in bhyve */
|
|
break;
|
|
case VIR_DOMAIN_CLOCK_OFFSET_UTC:
|
|
if ((bhyveDriverGetCaps(conn) & BHYVE_CAP_RTC_UTC) != 0) {
|
|
virCommandAddArg(cmd, "-u");
|
|
} else {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Installed bhyve binary does not support "
|
|
"UTC clock"));
|
|
goto error;
|
|
}
|
|
break;
|
|
default:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("unsupported clock offset '%s'"),
|
|
virDomainClockOffsetTypeToString(def->clock.offset));
|
|
goto error;
|
|
}
|
|
|
|
/* Clarification about -H and -P flags from Peter Grehan:
|
|
* -H and -P flags force the guest to exit when it executes IA32 HLT and PAUSE
|
|
* instructions respectively.
|
|
*
|
|
* For the HLT exit, bhyve uses that to infer that the guest is idling and can
|
|
* be put to sleep until an external event arrives. If this option is not used,
|
|
* the guest will always use 100% of CPU on the host.
|
|
*
|
|
* The PAUSE exit is most useful when there are large numbers of guest VMs running,
|
|
* since it forces the guest to exit when it spins on a lock acquisition.
|
|
*/
|
|
virCommandAddArg(cmd, "-H"); /* vmexit from guest on hlt */
|
|
virCommandAddArg(cmd, "-P"); /* vmexit from guest on pause */
|
|
|
|
virCommandAddArgList(cmd, "-s", "0:0,hostbridge", NULL);
|
|
|
|
if (def->os.bootloader == NULL &&
|
|
def->os.loader) {
|
|
if ((bhyveDriverGetCaps(conn) & BHYVE_CAP_LPC_BOOTROM)) {
|
|
virCommandAddArg(cmd, "-l");
|
|
virCommandAddArgFormat(cmd, "bootrom,%s", def->os.loader->path);
|
|
add_lpc = true;
|
|
} else {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Installed bhyve binary does not support "
|
|
"UEFI loader"));
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
/* Devices */
|
|
for (i = 0; i < def->ncontrollers; i++) {
|
|
virDomainControllerDefPtr controller = def->controllers[i];
|
|
switch (controller->type) {
|
|
case VIR_DOMAIN_CONTROLLER_TYPE_PCI:
|
|
if (controller->model != VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
"%s", _("unsupported PCI controller model: only PCI root supported"));
|
|
goto error;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_CONTROLLER_TYPE_SATA:
|
|
if (bhyveBuildAHCIControllerArgStr(def, controller, conn, cmd) < 0)
|
|
goto error;
|
|
break;
|
|
case VIR_DOMAIN_CONTROLLER_TYPE_USB:
|
|
if (++nusbcontrollers > 1) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
"%s", _("only single USB controller is supported"));
|
|
goto error;
|
|
}
|
|
|
|
if (bhyveBuildUSBControllerArgStr(def, controller, cmd) < 0)
|
|
goto error;
|
|
break;
|
|
}
|
|
}
|
|
for (i = 0; i < def->nnets; i++) {
|
|
virDomainNetDefPtr net = def->nets[i];
|
|
if (bhyveBuildNetArgStr(conn, def, net, cmd, dryRun) < 0)
|
|
goto error;
|
|
}
|
|
for (i = 0; i < def->ndisks; i++) {
|
|
virDomainDiskDefPtr disk = def->disks[i];
|
|
|
|
switch (disk->bus) {
|
|
case VIR_DOMAIN_DISK_BUS_SATA:
|
|
/* Handled by bhyveBuildAHCIControllerArgStr() */
|
|
break;
|
|
case VIR_DOMAIN_DISK_BUS_VIRTIO:
|
|
if (bhyveBuildVirtIODiskArgStr(def, disk, cmd) < 0)
|
|
goto error;
|
|
break;
|
|
default:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("unsupported disk device"));
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (def->ngraphics && def->nvideos) {
|
|
if (def->ngraphics == 1 && def->nvideos == 1) {
|
|
if (bhyveBuildGraphicsArgStr(def, def->graphics[0], def->videos[0],
|
|
conn, cmd, dryRun) < 0)
|
|
goto error;
|
|
add_lpc = true;
|
|
} else {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Multiple graphics devices are not supported"));
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (add_lpc || def->nserials)
|
|
bhyveBuildLPCArgStr(def, cmd);
|
|
|
|
if (bhyveBuildConsoleArgStr(def, cmd) < 0)
|
|
goto error;
|
|
|
|
virCommandAddArg(cmd, def->name);
|
|
|
|
return cmd;
|
|
|
|
error:
|
|
virCommandFree(cmd);
|
|
return NULL;
|
|
}
|
|
|
|
virCommandPtr
|
|
virBhyveProcessBuildDestroyCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED,
|
|
virDomainDefPtr def)
|
|
{
|
|
virCommandPtr cmd = virCommandNew(BHYVECTL);
|
|
|
|
virCommandAddArg(cmd, "--destroy");
|
|
virCommandAddArgPair(cmd, "--vm", def->name);
|
|
|
|
return cmd;
|
|
}
|
|
|
|
static void
|
|
virAppendBootloaderArgs(virCommandPtr cmd, virDomainDefPtr def)
|
|
{
|
|
char **blargs;
|
|
|
|
/* XXX: Handle quoted? */
|
|
blargs = virStringSplit(def->os.bootloaderArgs, " ", 0);
|
|
virCommandAddArgSet(cmd, (const char * const *)blargs);
|
|
virStringListFree(blargs);
|
|
}
|
|
|
|
static virCommandPtr
|
|
virBhyveProcessBuildBhyveloadCmd(virDomainDefPtr def, virDomainDiskDefPtr disk)
|
|
{
|
|
virCommandPtr cmd;
|
|
|
|
cmd = virCommandNew(BHYVELOAD);
|
|
|
|
if (def->os.bootloaderArgs == NULL) {
|
|
VIR_DEBUG("bhyveload with default arguments");
|
|
|
|
/* Memory (MB) */
|
|
virCommandAddArg(cmd, "-m");
|
|
virCommandAddArgFormat(cmd, "%llu",
|
|
VIR_DIV_UP(virDomainDefGetMemoryInitial(def), 1024));
|
|
|
|
/* Image path */
|
|
virCommandAddArg(cmd, "-d");
|
|
virCommandAddArg(cmd, virDomainDiskGetSource(disk));
|
|
|
|
/* VM name */
|
|
virCommandAddArg(cmd, def->name);
|
|
} else {
|
|
VIR_DEBUG("bhyveload with arguments");
|
|
virAppendBootloaderArgs(cmd, def);
|
|
}
|
|
|
|
return cmd;
|
|
}
|
|
|
|
static virCommandPtr
|
|
virBhyveProcessBuildCustomLoaderCmd(virDomainDefPtr def)
|
|
{
|
|
virCommandPtr cmd;
|
|
|
|
if (def->os.bootloaderArgs == NULL) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Custom loader requires explicit %s configuration"),
|
|
"bootloader_args");
|
|
return NULL;
|
|
}
|
|
|
|
VIR_DEBUG("custom loader '%s' with arguments", def->os.bootloader);
|
|
|
|
cmd = virCommandNew(def->os.bootloader);
|
|
virAppendBootloaderArgs(cmd, def);
|
|
return cmd;
|
|
}
|
|
|
|
static bool
|
|
virBhyveUsableDisk(virDomainDiskDefPtr disk)
|
|
{
|
|
|
|
if (virDomainDiskTranslateSourcePool(disk) < 0)
|
|
return false;
|
|
|
|
if ((disk->device != VIR_DOMAIN_DISK_DEVICE_DISK) &&
|
|
(disk->device != VIR_DOMAIN_DISK_DEVICE_CDROM)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("unsupported disk device"));
|
|
return false;
|
|
}
|
|
|
|
if ((virDomainDiskGetType(disk) != VIR_STORAGE_TYPE_FILE) &&
|
|
(virDomainDiskGetType(disk) != VIR_STORAGE_TYPE_VOLUME)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("unsupported disk type"));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
virBhyveFormatGrubDevice(virBufferPtr devicemap, virDomainDiskDefPtr def)
|
|
{
|
|
|
|
if (def->device == VIR_DOMAIN_DISK_DEVICE_CDROM)
|
|
virBufferAsprintf(devicemap, "(cd) %s\n",
|
|
virDomainDiskGetSource(def));
|
|
else
|
|
virBufferAsprintf(devicemap, "(hd0) %s\n",
|
|
virDomainDiskGetSource(def));
|
|
}
|
|
|
|
static virCommandPtr
|
|
virBhyveProcessBuildGrubbhyveCmd(virDomainDefPtr def,
|
|
virConnectPtr conn,
|
|
const char *devmap_file,
|
|
char **devicesmap_out)
|
|
{
|
|
virDomainDiskDefPtr hdd, cd, userdef, diskdef;
|
|
virBuffer devicemap;
|
|
virCommandPtr cmd;
|
|
unsigned int best_idx = UINT_MAX;
|
|
size_t i;
|
|
|
|
if (def->os.bootloaderArgs != NULL)
|
|
return virBhyveProcessBuildCustomLoaderCmd(def);
|
|
|
|
devicemap = (virBuffer)VIR_BUFFER_INITIALIZER;
|
|
|
|
/* Search disk list for CD or HDD device. We'll respect <boot order=''> if
|
|
* present and otherwise pick the first CD or failing that HDD we come
|
|
* across. */
|
|
cd = hdd = userdef = NULL;
|
|
for (i = 0; i < def->ndisks; i++) {
|
|
if (!virBhyveUsableDisk(def->disks[i]))
|
|
continue;
|
|
|
|
diskdef = def->disks[i];
|
|
|
|
if (diskdef->info.bootIndex && diskdef->info.bootIndex < best_idx) {
|
|
userdef = diskdef;
|
|
best_idx = userdef->info.bootIndex;
|
|
continue;
|
|
}
|
|
|
|
if (cd == NULL &&
|
|
def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_CDROM) {
|
|
cd = diskdef;
|
|
VIR_INFO("Picking %s as CD", virDomainDiskGetSource(cd));
|
|
}
|
|
|
|
if (hdd == NULL &&
|
|
def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_DISK) {
|
|
hdd = diskdef;
|
|
VIR_INFO("Picking %s as HDD", virDomainDiskGetSource(hdd));
|
|
}
|
|
}
|
|
|
|
cmd = virCommandNew(def->os.bootloader);
|
|
|
|
VIR_DEBUG("grub-bhyve with default arguments");
|
|
|
|
if (devicesmap_out != NULL) {
|
|
/* Grub device.map (just for boot) */
|
|
if (userdef != NULL) {
|
|
virBhyveFormatGrubDevice(&devicemap, userdef);
|
|
} else {
|
|
if (hdd != NULL)
|
|
virBhyveFormatGrubDevice(&devicemap, hdd);
|
|
|
|
if (cd != NULL)
|
|
virBhyveFormatGrubDevice(&devicemap, cd);
|
|
}
|
|
|
|
*devicesmap_out = virBufferContentAndReset(&devicemap);
|
|
}
|
|
|
|
virCommandAddArg(cmd, "--root");
|
|
if (userdef != NULL) {
|
|
if (userdef->device == VIR_DOMAIN_DISK_DEVICE_CDROM)
|
|
virCommandAddArg(cmd, "cd");
|
|
else
|
|
virCommandAddArg(cmd, "hd0,msdos1");
|
|
} else if (cd != NULL) {
|
|
virCommandAddArg(cmd, "cd");
|
|
} else {
|
|
virCommandAddArg(cmd, "hd0,msdos1");
|
|
}
|
|
|
|
virCommandAddArg(cmd, "--device-map");
|
|
virCommandAddArg(cmd, devmap_file);
|
|
|
|
/* Memory in MB */
|
|
virCommandAddArg(cmd, "--memory");
|
|
virCommandAddArgFormat(cmd, "%llu",
|
|
VIR_DIV_UP(virDomainDefGetMemoryInitial(def), 1024));
|
|
|
|
if ((bhyveDriverGetGrubCaps(conn) & BHYVE_GRUB_CAP_CONSDEV) != 0 &&
|
|
def->nserials > 0) {
|
|
virDomainChrDefPtr chr;
|
|
|
|
chr = def->serials[0];
|
|
|
|
if (chr->source->type != VIR_DOMAIN_CHR_TYPE_NMDM) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("only nmdm console types are supported"));
|
|
return NULL;
|
|
}
|
|
|
|
virCommandAddArg(cmd, "--cons-dev");
|
|
virCommandAddArg(cmd, chr->source->data.file.path);
|
|
}
|
|
|
|
/* VM name */
|
|
virCommandAddArg(cmd, def->name);
|
|
|
|
return cmd;
|
|
}
|
|
|
|
static virDomainDiskDefPtr
|
|
virBhyveGetBootDisk(virDomainDefPtr def)
|
|
{
|
|
size_t i;
|
|
virDomainDiskDefPtr match = NULL;
|
|
int boot_dev = -1;
|
|
|
|
if (def->ndisks < 1) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Domain should have at least one disk defined"));
|
|
return NULL;
|
|
}
|
|
|
|
if (def->os.nBootDevs > 1) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Only one boot device is supported"));
|
|
return NULL;
|
|
} else if (def->os.nBootDevs == 1) {
|
|
switch (def->os.bootDevs[0]) {
|
|
case VIR_DOMAIN_BOOT_CDROM:
|
|
boot_dev = VIR_DOMAIN_DISK_DEVICE_CDROM;
|
|
break;
|
|
case VIR_DOMAIN_BOOT_DISK:
|
|
boot_dev = VIR_DOMAIN_DISK_DEVICE_DISK;
|
|
break;
|
|
default:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Cannot boot from device %s"),
|
|
virDomainBootTypeToString(def->os.bootDevs[0]));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (boot_dev != -1) {
|
|
/* If boot_dev is set, we return the first device of
|
|
* the request type */
|
|
for (i = 0; i < def->ndisks; i++) {
|
|
if (!virBhyveUsableDisk(def->disks[i]))
|
|
continue;
|
|
|
|
if (def->disks[i]->device == boot_dev) {
|
|
match = def->disks[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (match == NULL) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Cannot find boot device of requested type %s"),
|
|
virDomainBootTypeToString(def->os.bootDevs[0]));
|
|
return NULL;
|
|
}
|
|
} else {
|
|
/* Otherwise, if boot_dev is not set, we try to find if bootIndex
|
|
* is set for individual device. However, as bhyve does not support
|
|
* specifying real boot priority for devices, we allow only single
|
|
* device with boot priority set.
|
|
*/
|
|
int first_usable_disk_index = -1;
|
|
|
|
for (i = 0; i < def->ndisks; i++) {
|
|
if (!virBhyveUsableDisk(def->disks[i]))
|
|
continue;
|
|
else
|
|
first_usable_disk_index = i;
|
|
|
|
if (def->disks[i]->info.bootIndex > 0) {
|
|
if (match == NULL) {
|
|
match = def->disks[i];
|
|
} else {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Only one boot device is supported"));
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If user didn't explicily specify boot priority,
|
|
* just return the first usable disk */
|
|
if ((match == NULL) && (first_usable_disk_index >= 0))
|
|
return def->disks[first_usable_disk_index];
|
|
}
|
|
|
|
return match;
|
|
}
|
|
|
|
virCommandPtr
|
|
virBhyveProcessBuildLoadCmd(virConnectPtr conn, virDomainDefPtr def,
|
|
const char *devmap_file, char **devicesmap_out)
|
|
{
|
|
virDomainDiskDefPtr disk = NULL;
|
|
|
|
if (def->os.bootloader == NULL) {
|
|
disk = virBhyveGetBootDisk(def);
|
|
|
|
if (disk == NULL)
|
|
return NULL;
|
|
|
|
return virBhyveProcessBuildBhyveloadCmd(def, disk);
|
|
} else if (strstr(def->os.bootloader, "grub-bhyve") != NULL) {
|
|
return virBhyveProcessBuildGrubbhyveCmd(def, conn, devmap_file,
|
|
devicesmap_out);
|
|
} else {
|
|
return virBhyveProcessBuildCustomLoaderCmd(def);
|
|
}
|
|
}
|