bhyve_command.c 10.0 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
    const char *disk_source;
R
Roman Bogorodskiy 已提交
156

157 158
    switch (disk->bus) {
    case VIR_DOMAIN_DISK_BUS_SATA:
R
Roman Bogorodskiy 已提交
159 160 161 162 163 164 165 166 167 168 169 170
        switch (disk->device) {
        case VIR_DOMAIN_DISK_DEVICE_DISK:
            bus_type = "ahci-hd";
            break;
        case VIR_DOMAIN_DISK_DEVICE_CDROM:
            bus_type = "ahci-cd";
            break;
        default:
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                           _("unsupported disk device"));
            return -1;
        }
171 172
        break;
    case VIR_DOMAIN_DISK_BUS_VIRTIO:
R
Roman Bogorodskiy 已提交
173 174 175 176 177 178 179
        if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) {
            bus_type = "virtio-blk";
        } else {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                           _("unsupported disk device"));
            return -1;
        }
180 181
        break;
    default:
R
Roman Bogorodskiy 已提交
182 183 184 185 186
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("unsupported disk bus type"));
        return -1;
    }

E
Eric Blake 已提交
187
    if (virDomainDiskGetType(disk) != VIR_STORAGE_TYPE_FILE) {
R
Roman Bogorodskiy 已提交
188 189 190 191 192
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("unsupported disk type"));
        return -1;
    }

R
Roman Bogorodskiy 已提交
193 194 195 196 197 198 199 200 201 202
    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"));
            return -1;
    }

R
Roman Bogorodskiy 已提交
203
    virCommandAddArg(cmd, "-s");
204 205
    virCommandAddArgFormat(cmd, "%d:0,%s,%s",
                           disk->info.addr.pci.slot, bus_type,
R
Roman Bogorodskiy 已提交
206
                           disk_source);
R
Roman Bogorodskiy 已提交
207 208 209 210 211 212

    return 0;
}

virCommandPtr
virBhyveProcessBuildBhyveCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED,
213
                             virDomainDefPtr def, bool dryRun)
R
Roman Bogorodskiy 已提交
214 215 216 217 218 219 220 221 222
{
    /*
     * /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
     */
223 224
    size_t i;

R
Roman Bogorodskiy 已提交
225 226 227 228
    virCommandPtr cmd = virCommandNew(BHYVE);

    /* CPUs */
    virCommandAddArg(cmd, "-c");
229
    virCommandAddArgFormat(cmd, "%d", def->vcpus);
R
Roman Bogorodskiy 已提交
230 231 232 233

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

    /* Options */
J
Ján Tomko 已提交
237
    if (def->features[VIR_DOMAIN_FEATURE_ACPI] == VIR_TRISTATE_SWITCH_ON)
R
Roman Bogorodskiy 已提交
238
        virCommandAddArg(cmd, "-A"); /* Create an ACPI table */
J
Ján Tomko 已提交
239
    if (def->features[VIR_DOMAIN_FEATURE_APIC] == VIR_TRISTATE_SWITCH_ON)
R
Roman Bogorodskiy 已提交
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
        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 */

256
    virCommandAddArgList(cmd, "-s", "0:0,hostbridge", NULL);
R
Roman Bogorodskiy 已提交
257
    /* Devices */
258 259 260 261 262 263 264 265 266 267 268
    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;
    }
269
    if (bhyveBuildConsoleArgStr(def, cmd) < 0)
270
        goto error;
271
    virCommandAddArg(cmd, def->name);
R
Roman Bogorodskiy 已提交
272 273 274

    return cmd;

275
 error:
R
Roman Bogorodskiy 已提交
276 277 278 279 280 281
    virCommandFree(cmd);
    return NULL;
}

virCommandPtr
virBhyveProcessBuildDestroyCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED,
282
                               virDomainDefPtr def)
R
Roman Bogorodskiy 已提交
283 284 285 286
{
    virCommandPtr cmd = virCommandNew(BHYVECTL);

    virCommandAddArg(cmd, "--destroy");
287
    virCommandAddArgPair(cmd, "--vm", def->name);
R
Roman Bogorodskiy 已提交
288 289 290 291 292 293

    return cmd;
}

virCommandPtr
virBhyveProcessBuildLoadCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED,
294
                            virDomainDefPtr def)
R
Roman Bogorodskiy 已提交
295 296 297 298
{
    virCommandPtr cmd;
    virDomainDiskDefPtr disk;

299
    if (def->ndisks < 1) {
R
Roman Bogorodskiy 已提交
300
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
301
                       _("domain should have at least one disk defined"));
R
Roman Bogorodskiy 已提交
302 303 304
        return NULL;
    }

305
    disk = def->disks[0];
R
Roman Bogorodskiy 已提交
306

R
Roman Bogorodskiy 已提交
307 308
    if ((disk->device != VIR_DOMAIN_DISK_DEVICE_DISK) &&
        (disk->device != VIR_DOMAIN_DISK_DEVICE_CDROM)) {
R
Roman Bogorodskiy 已提交
309 310 311 312 313
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("unsupported disk device"));
        return NULL;
    }

E
Eric Blake 已提交
314
    if (virDomainDiskGetType(disk) != VIR_STORAGE_TYPE_FILE) {
R
Roman Bogorodskiy 已提交
315 316 317 318 319 320 321 322 323 324
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("unsupported disk type"));
        return NULL;
    }

    cmd = virCommandNew(BHYVELOAD);

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

    /* Image path */
    virCommandAddArg(cmd, "-d");
329
    virCommandAddArg(cmd, virDomainDiskGetSource(disk));
R
Roman Bogorodskiy 已提交
330 331

    /* VM name */
332
    virCommandAddArg(cmd, def->name);
R
Roman Bogorodskiy 已提交
333 334 335

    return cmd;
}