bhyve_command.c 10.3 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
 *
 * 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"
R
Roman Bogorodskiy 已提交
36
#include "storage/storage_driver.h"
R
Roman Bogorodskiy 已提交
37 38 39

#define VIR_FROM_THIS VIR_FROM_BHYVE

40 41
VIR_LOG_INIT("bhyve.bhyve_command");

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

53 54 55 56 57 58 59
    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 已提交
60 61 62
        return -1;
    }

63 64 65 66 67 68
    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 已提交
69 70
            return -1;
        }
71
    }
R
Roman Bogorodskiy 已提交
72

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

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

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

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


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

    return 0;
}

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

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

    return 0;
}

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

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

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

R
Roman Bogorodskiy 已提交
195 196 197 198 199 200 201 202 203 204
    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 已提交
205
    virCommandAddArg(cmd, "-s");
206 207
    virCommandAddArgFormat(cmd, "%d:0,%s,%s",
                           disk->info.addr.pci.slot, bus_type,
R
Roman Bogorodskiy 已提交
208
                           disk_source);
R
Roman Bogorodskiy 已提交
209 210 211 212 213

    return 0;
}

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

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

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

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

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

258
    virCommandAddArgList(cmd, "-s", "0:0,hostbridge", NULL);
R
Roman Bogorodskiy 已提交
259
    /* Devices */
260 261 262 263 264 265 266 267
    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];

R
Roman Bogorodskiy 已提交
268 269 270
        if (virStorageTranslateDiskSourcePool(conn, disk) < 0)
            goto error;

271 272 273
        if (bhyveBuildDiskArgStr(def, disk, cmd) < 0)
            goto error;
    }
274
    if (bhyveBuildConsoleArgStr(def, cmd) < 0)
275
        goto error;
276
    virCommandAddArg(cmd, def->name);
R
Roman Bogorodskiy 已提交
277 278 279

    return cmd;

280
 error:
R
Roman Bogorodskiy 已提交
281 282 283 284 285 286
    virCommandFree(cmd);
    return NULL;
}

virCommandPtr
virBhyveProcessBuildDestroyCmd(bhyveConnPtr driver ATTRIBUTE_UNUSED,
287
                               virDomainDefPtr def)
R
Roman Bogorodskiy 已提交
288 289 290 291
{
    virCommandPtr cmd = virCommandNew(BHYVECTL);

    virCommandAddArg(cmd, "--destroy");
292
    virCommandAddArgPair(cmd, "--vm", def->name);
R
Roman Bogorodskiy 已提交
293 294 295 296 297

    return cmd;
}

virCommandPtr
R
Roman Bogorodskiy 已提交
298
virBhyveProcessBuildLoadCmd(virConnectPtr conn,
299
                            virDomainDefPtr def)
R
Roman Bogorodskiy 已提交
300 301 302 303
{
    virCommandPtr cmd;
    virDomainDiskDefPtr disk;

304
    if (def->ndisks < 1) {
R
Roman Bogorodskiy 已提交
305
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
306
                       _("domain should have at least one disk defined"));
R
Roman Bogorodskiy 已提交
307 308 309
        return NULL;
    }

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

R
Roman Bogorodskiy 已提交
312 313 314
    if (virStorageTranslateDiskSourcePool(conn, disk) < 0)
        return NULL;

R
Roman Bogorodskiy 已提交
315 316
    if ((disk->device != VIR_DOMAIN_DISK_DEVICE_DISK) &&
        (disk->device != VIR_DOMAIN_DISK_DEVICE_CDROM)) {
R
Roman Bogorodskiy 已提交
317 318 319 320 321
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("unsupported disk device"));
        return NULL;
    }

R
Roman Bogorodskiy 已提交
322 323
    if ((virDomainDiskGetType(disk) != VIR_STORAGE_TYPE_FILE) &&
        (virDomainDiskGetType(disk) != VIR_STORAGE_TYPE_VOLUME)) {
R
Roman Bogorodskiy 已提交
324 325 326 327 328 329 330 331 332 333
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("unsupported disk type"));
        return NULL;
    }

    cmd = virCommandNew(BHYVELOAD);

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

    /* Image path */
    virCommandAddArg(cmd, "-d");
338
    virCommandAddArg(cmd, virDomainDiskGetSource(disk));
R
Roman Bogorodskiy 已提交
339 340

    /* VM name */
341
    virCommandAddArg(cmd, def->name);
R
Roman Bogorodskiy 已提交
342 343 344

    return cmd;
}