bhyve_command.c 9.2 KB
Newer Older
R
Roman Bogorodskiy 已提交
1
/*
2
 * bhyve_command.c: bhyve command generation
R
Roman Bogorodskiy 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
 *
 * 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_command.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

39 40
VIR_LOG_INIT("bhyve.bhyve_command");

R
Roman Bogorodskiy 已提交
41
static int
42 43 44 45
bhyveBuildNetArgStr(const virDomainDef *def,
                    virDomainNetDefPtr net,
                    virCommandPtr cmd,
                    bool dryRun)
R
Roman Bogorodskiy 已提交
46
{
47
    char macaddr[VIR_MAC_STRING_BUFLEN];
R
Roman Bogorodskiy 已提交
48
    char *realifname = NULL;
49 50
    char *brname = NULL;
    int actualType = virDomainNetGetActualType(net);
R
Roman Bogorodskiy 已提交
51

52 53 54 55 56 57 58
    if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) {
        if (VIR_STRDUP(brname, virDomainNetGetActualBridgeName(net)) < 0)
            return -1;
    } else {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Network type %d is not supported"),
                       virDomainNetGetActualType(net));
R
Roman Bogorodskiy 已提交
59 60 61
        return -1;
    }

62 63 64 65 66 67
    if (!net->ifname ||
        STRPREFIX(net->ifname, VIR_NET_GENERATED_PREFIX) ||
        strchr(net->ifname, '%')) {
        VIR_FREE(net->ifname);
        if (VIR_STRDUP(net->ifname, VIR_NET_GENERATED_PREFIX "%d") < 0) {
            VIR_FREE(brname);
R
Roman Bogorodskiy 已提交
68 69
            return -1;
        }
70
    }
R
Roman Bogorodskiy 已提交
71

72 73
    if (!dryRun) {
        if (virNetDevTapCreateInBridgePort(brname, &net->ifname, &net->mac,
74
                                           def->uuid, NULL, 0,
75 76 77
                                           virDomainNetGetActualVirtPortProfile(net),
                                           virDomainNetGetActualVlan(net),
                                           VIR_NETDEV_TAP_CREATE_IFUP | VIR_NETDEV_TAP_CREATE_PERSIST) < 0) {
R
Roman Bogorodskiy 已提交
78
            VIR_FREE(net->ifname);
79 80
            VIR_FREE(brname);
            return -1;
R
Roman Bogorodskiy 已提交
81 82
        }

83 84 85
        realifname = virNetDevTapGetRealDeviceName(net->ifname);

        if (realifname == NULL) {
R
Roman Bogorodskiy 已提交
86 87 88 89 90
            VIR_FREE(net->ifname);
            VIR_FREE(brname);
            return -1;
        }

91 92 93 94 95 96
        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) {
97
            VIR_FREE(realifname);
98 99 100 101 102 103 104
            VIR_FREE(net->ifname);
            VIR_FREE(brname);
            return -1;
        }
    } else {
        if (VIR_STRDUP(realifname, "tap0") < 0)
            return -1;
R
Roman Bogorodskiy 已提交
105 106 107 108
    }


    virCommandAddArg(cmd, "-s");
109 110
    virCommandAddArgFormat(cmd, "%d:0,virtio-net,%s,mac=%s",
                           net->info.addr.pci.slot,
111
                           realifname, virMacAddrFormat(&net->mac, macaddr));
112
    VIR_FREE(realifname);
R
Roman Bogorodskiy 已提交
113 114 115 116

    return 0;
}

117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
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;
    }

141
    virCommandAddArgList(cmd, "-s", "1,lpc", NULL);
142 143 144 145 146 147 148
    virCommandAddArg(cmd, "-l");
    virCommandAddArgFormat(cmd, "com%d,%s",
                           chr->target.port + 1, chr->source.data.file.path);

    return 0;
}

R
Roman Bogorodskiy 已提交
149
static int
150 151 152
bhyveBuildDiskArgStr(const virDomainDef *def ATTRIBUTE_UNUSED,
                     virDomainDiskDefPtr disk,
                     virCommandPtr cmd)
R
Roman Bogorodskiy 已提交
153
{
154
    const char *bus_type;
R
Roman Bogorodskiy 已提交
155

156 157 158 159 160 161 162 163
    switch (disk->bus) {
    case VIR_DOMAIN_DISK_BUS_SATA:
        bus_type = "ahci-hd";
        break;
    case VIR_DOMAIN_DISK_BUS_VIRTIO:
        bus_type = "virtio-blk";
        break;
    default:
R
Roman Bogorodskiy 已提交
164 165 166 167 168 169 170 171 172 173 174
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("unsupported disk bus type"));
        return -1;
    }

    if (disk->device != VIR_DOMAIN_DISK_DEVICE_DISK) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("unsupported disk device"));
        return -1;
    }

E
Eric Blake 已提交
175
    if (virDomainDiskGetType(disk) != VIR_STORAGE_TYPE_FILE) {
R
Roman Bogorodskiy 已提交
176 177 178 179 180 181
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("unsupported disk type"));
        return -1;
    }

    virCommandAddArg(cmd, "-s");
182 183
    virCommandAddArgFormat(cmd, "%d:0,%s,%s",
                           disk->info.addr.pci.slot, bus_type,
184
                           virDomainDiskGetSource(disk));
R
Roman Bogorodskiy 已提交
185 186 187 188 189 190

    return 0;
}

virCommandPtr
virBhyveProcessBuildBhyveCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED,
191
                             virDomainDefPtr def, bool dryRun)
R
Roman Bogorodskiy 已提交
192 193 194 195 196 197 198 199 200
{
    /*
     * /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
     */
201 202
    size_t i;

R
Roman Bogorodskiy 已提交
203 204 205 206
    virCommandPtr cmd = virCommandNew(BHYVE);

    /* CPUs */
    virCommandAddArg(cmd, "-c");
207
    virCommandAddArgFormat(cmd, "%d", def->vcpus);
R
Roman Bogorodskiy 已提交
208 209 210 211

    /* Memory */
    virCommandAddArg(cmd, "-m");
    virCommandAddArgFormat(cmd, "%llu",
212
                           VIR_DIV_UP(def->mem.max_balloon, 1024));
R
Roman Bogorodskiy 已提交
213 214

    /* Options */
215
    if (def->features[VIR_DOMAIN_FEATURE_ACPI] == VIR_DOMAIN_FEATURE_STATE_ON)
R
Roman Bogorodskiy 已提交
216
        virCommandAddArg(cmd, "-A"); /* Create an ACPI table */
217
    if (def->features[VIR_DOMAIN_FEATURE_APIC] == VIR_DOMAIN_FEATURE_STATE_ON)
R
Roman Bogorodskiy 已提交
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
        virCommandAddArg(cmd, "-I"); /* Present ioapic to the guest */

    /* 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 */

234
    virCommandAddArgList(cmd, "-s", "0:0,hostbridge", NULL);
R
Roman Bogorodskiy 已提交
235
    /* Devices */
236 237 238 239 240 241 242 243 244 245 246
    for (i = 0; i < def->nnets; i++) {
        virDomainNetDefPtr net = def->nets[i];
        if (bhyveBuildNetArgStr(def, net, cmd, dryRun) < 0)
            goto error;
    }
    for (i = 0; i < def->ndisks; i++) {
        virDomainDiskDefPtr disk = def->disks[i];

        if (bhyveBuildDiskArgStr(def, disk, cmd) < 0)
            goto error;
    }
247
    if (bhyveBuildConsoleArgStr(def, cmd) < 0)
248
        goto error;
249
    virCommandAddArg(cmd, def->name);
R
Roman Bogorodskiy 已提交
250 251 252

    return cmd;

253
 error:
R
Roman Bogorodskiy 已提交
254 255 256 257 258 259
    virCommandFree(cmd);
    return NULL;
}

virCommandPtr
virBhyveProcessBuildDestroyCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED,
260
                               virDomainDefPtr def)
R
Roman Bogorodskiy 已提交
261 262 263 264
{
    virCommandPtr cmd = virCommandNew(BHYVECTL);

    virCommandAddArg(cmd, "--destroy");
265
    virCommandAddArgPair(cmd, "--vm", def->name);
R
Roman Bogorodskiy 已提交
266 267 268 269 270 271

    return cmd;
}

virCommandPtr
virBhyveProcessBuildLoadCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED,
272
                            virDomainDefPtr def)
R
Roman Bogorodskiy 已提交
273 274 275 276
{
    virCommandPtr cmd;
    virDomainDiskDefPtr disk;

277
    if (def->ndisks < 1) {
R
Roman Bogorodskiy 已提交
278
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
279
                       _("domain should have at least one disk defined"));
R
Roman Bogorodskiy 已提交
280 281 282
        return NULL;
    }

283
    disk = def->disks[0];
R
Roman Bogorodskiy 已提交
284 285 286 287 288 289 290

    if (disk->device != VIR_DOMAIN_DISK_DEVICE_DISK) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("unsupported disk device"));
        return NULL;
    }

E
Eric Blake 已提交
291
    if (virDomainDiskGetType(disk) != VIR_STORAGE_TYPE_FILE) {
R
Roman Bogorodskiy 已提交
292 293 294 295 296 297 298 299 300 301
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("unsupported disk type"));
        return NULL;
    }

    cmd = virCommandNew(BHYVELOAD);

    /* Memory */
    virCommandAddArg(cmd, "-m");
    virCommandAddArgFormat(cmd, "%llu",
302
                           VIR_DIV_UP(def->mem.max_balloon, 1024));
R
Roman Bogorodskiy 已提交
303 304 305

    /* Image path */
    virCommandAddArg(cmd, "-d");
306
    virCommandAddArg(cmd, virDomainDiskGetSource(disk));
R
Roman Bogorodskiy 已提交
307 308

    /* VM name */
309
    virCommandAddArg(cmd, def->name);
R
Roman Bogorodskiy 已提交
310 311 312

    return cmd;
}