From nobody Sun May 5 00:55:53 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) client-ip=216.205.24.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=fail(p=none dis=none) header.from=intel.com ARC-Seal: i=1; a=rsa-sha256; t=1598553166; cv=none; d=zohomail.com; s=zohoarc; b=nMniut/QfILLSeGB7f4fbjId+TeBZJOw23t2RFa3u9HzFJmi/FmtcXF5P446Cw3daIyCPg8K846rvWNYB9wXgmTdjq+AjbxwDUhXvabPtUBPpgdHejeZVqcurv+pYMei3WeSE+bV1UgrwHyzCSmXSaebaZAXSI8wwqCYIN//GQQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1598553166; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Sender:Subject:To; bh=GDndGdUK2QGVvXwTBHPNvRc7fAm0UQEMqB2HXIMGHJk=; b=BMRt9fcPg7om20wsls54YAmRDN9+s46J5BKulYVvEQXc+L66exxBLCRlKcHo7fthtXpJqp+ooGkh2Oc7qLdXlWLFORGYfzr2SOhmHFCZKybKq9OnUCqf4aGugESZs7SNG9wSnUgEJAie9nSSUHnYwzWL6LFO4aX3NE3oW020eZc= ARC-Authentication-Results: i=1; mx.zohomail.com; spf=pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by mx.zohomail.com with SMTPS id 1598553166864133.97587289463684; Thu, 27 Aug 2020 11:32:46 -0700 (PDT) Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-543-6RkHoU3VNf6dwNhbXK0Yww-1; Thu, 27 Aug 2020 14:32:42 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 2EADA100C61E; Thu, 27 Aug 2020 18:32:36 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 566CC78381; Thu, 27 Aug 2020 18:32:33 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 3FEE5668E1; Thu, 27 Aug 2020 18:32:29 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 07RIWRNa014666 for ; Thu, 27 Aug 2020 14:32:27 -0400 Received: by smtp.corp.redhat.com (Postfix) id 68F3C208483E; Thu, 27 Aug 2020 18:32:27 +0000 (UTC) Received: from mimecast-mx02.redhat.com (mimecast03.extmail.prod.ext.rdu2.redhat.com [10.11.55.19]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 355DA207B241 for ; Thu, 27 Aug 2020 18:32:22 +0000 (UTC) Received: from us-smtp-1.mimecast.com (us-smtp-delivery-1.mimecast.com [205.139.110.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 54F53811E76 for ; Thu, 27 Aug 2020 18:32:21 +0000 (UTC) Received: from mga01.intel.com (mga01.intel.com [192.55.52.88]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-394-Dua_EtL_OAS-ZlkEE4Qw-g-1; Thu, 27 Aug 2020 14:32:15 -0400 Received: from orsmga008.jf.intel.com ([10.7.209.65]) by fmsmga101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 27 Aug 2020 11:31:10 -0700 Received: from wdouglas-dev.jf.intel.com (HELO desk.jf.intel.com) ([10.7.200.157]) by orsmga008.jf.intel.com with ESMTP; 27 Aug 2020 11:31:09 -0700 X-MC-Unique: 6RkHoU3VNf6dwNhbXK0Yww-1 X-MC-Unique: Dua_EtL_OAS-ZlkEE4Qw-g-1 IronPort-SDR: djFwqSl47MpX7BQPBFEROWoyQ/tmn5wHt34BSHQJkyiRJlJBU/844X+JOcURhuodz6U6gkwKMt 2pxYnPaU+o7w== X-IronPort-AV: E=McAfee;i="6000,8403,9726"; a="174595764" X-IronPort-AV: E=Sophos;i="5.76,360,1592895600"; d="scan'208";a="174595764" X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False IronPort-SDR: gO8mVaxjtsieRL4s4DBTJh2EFu/IjYcfar+XFZB7NHd9dMnne790Tbne21OEEip3oRVFkHlX50 ycqYX1GB47VA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.76,360,1592895600"; d="scan'208";a="329683637" From: William Douglas To: libvir-list@redhat.com Subject: [RFC] Add basic driver for the Cloud-Hypervisor Date: Thu, 27 Aug 2020 11:24:32 -0700 Message-Id: <20200827182430.753251-1-william.douglas@intel.com> MIME-Version: 1.0 X-Mimecast-Impersonation-Protect: Policy=CLT - Impersonation Protection Definition; Similar Internal Domain=false; Similar Monitored External Domain=false; Custom External Domain=false; Mimecast External Domain=false; Newly Observed Domain=false; Internal User Name=false; Custom Display Name List=false; Reply-to Address Mismatch=false; Targeted Threat Dictionary=false; Mimecast Threat Dictionary=false; Custom Threat Dictionary=false; X-Scanned-By: MIMEDefang 2.78 on 10.11.54.4 X-loop: libvir-list@redhat.com Cc: William Douglas X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=libvir-list-bounces@redhat.com X-Mimecast-Spam-Score: 0.004 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch adds support for the following initial VM actions using the Cloud-Hypervsior API: * vm.create * vm.delete * vm.boot * vm.shutdown * vm.reboot * vm.pause * vm.resume To use the Cloud-Hypervisor driver, the v0.9.0 (the as of now current) release of Cloud-Hypervisor is required to be installed. Signed-off-by: William Douglas --- include/libvirt/virterror.h | 1 + libvirt.spec.in | 32 ++ meson.build | 5 + meson_options.txt | 1 + po/POTFILES.in | 5 + src/ch/ch_conf.c | 239 +++++++++ src/ch/ch_conf.h | 87 ++++ src/ch/ch_domain.c | 219 +++++++++ src/ch/ch_domain.h | 68 +++ src/ch/ch_driver.c | 937 ++++++++++++++++++++++++++++++++++++ src/ch/ch_driver.h | 24 + src/ch/ch_monitor.c | 796 ++++++++++++++++++++++++++++++ src/ch/ch_monitor.h | 62 +++ src/ch/ch_process.c | 125 +++++ src/ch/ch_process.h | 30 ++ src/ch/meson.build | 44 ++ src/conf/domain_conf.c | 1 + src/conf/domain_conf.h | 1 + src/meson.build | 1 + src/qemu/qemu_command.c | 1 + src/remote/remote_daemon.c | 4 + src/util/virerror.c | 1 + tools/virsh.c | 3 + 23 files changed, 2687 insertions(+) create mode 100644 src/ch/ch_conf.c create mode 100644 src/ch/ch_conf.h create mode 100644 src/ch/ch_domain.c create mode 100644 src/ch/ch_domain.h create mode 100644 src/ch/ch_driver.c create mode 100644 src/ch/ch_driver.h create mode 100644 src/ch/ch_monitor.c create mode 100644 src/ch/ch_monitor.h create mode 100644 src/ch/ch_process.c create mode 100644 src/ch/ch_process.h create mode 100644 src/ch/meson.build diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index 0f1c32283d..21faa8f128 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -136,6 +136,7 @@ typedef enum { =20 VIR_FROM_TPM =3D 70, /* Error from TPM */ VIR_FROM_BPF =3D 71, /* Error from BPF code */ + VIR_FROM_CH =3D 72, /* Error from Cloud-Hypervisor driver */ =20 # ifdef VIR_ENUM_SENTINELS VIR_ERR_DOMAIN_LAST diff --git a/libvirt.spec.in b/libvirt.spec.in index bb74443484..66edb1fa76 100644 --- a/libvirt.spec.in +++ b/libvirt.spec.in @@ -14,6 +14,7 @@ =20 # The hypervisor drivers that run in libvirtd %define with_qemu 0%{!?_without_qemu:1} +%define with_ch 0%{!?_without_ch:1} %define with_lxc 0%{!?_without_lxc:1} %define with_libxl 0%{!?_without_libxl:1} %define with_vbox 0%{!?_without_vbox:1} @@ -232,6 +233,9 @@ Requires: libvirt-daemon-driver-lxc =3D %{version}-%{re= lease} %if %{with_qemu} Requires: libvirt-daemon-driver-qemu =3D %{version}-%{release} %endif +%if %{with_ch} +Requires: libvirt-daemon-driver-ch =3D %{version}-%{release} +%endif # We had UML driver, but we've removed it. Obsoletes: libvirt-daemon-driver-uml <=3D 5.0.0 Obsoletes: libvirt-daemon-uml <=3D 5.0.0 @@ -744,6 +748,20 @@ QEMU %endif =20 =20 +%if %{with_ch} +%package daemon-driver-ch +Summary: Cloud-Hypervisor driver plugin for the libvirtd daemon +Requires: libvirt-daemon =3D %{version}-%{release} +Requires: libvirt-libs =3D %{version}-%{release} +Requires: /usr/bin/cloud-hypervisor + +%description daemon-driver-ch +The Cloud-Hypervisor driver plugin for the libvirtd daemon, +providing an implementation of the hypervisor driver APIs +using Cloud-Hypervisor +%endif + + %if %{with_lxc} %package daemon-driver-lxc Summary: LXC driver plugin for the libvirtd daemon @@ -1001,6 +1019,12 @@ exit 1 %define arg_qemu -Ddriver_qemu=3Ddisabled %endif =20 +%if %{with_ch} + %define arg_ch -Ddriver_ch=3Denabled +%else + %define arg_ch -Ddriver_ch=3Ddisabled +%endif + %if %{with_openvz} %define arg_openvz -Ddriver_openvz=3Denabled %else @@ -1132,6 +1156,7 @@ export SOURCE_DATE_EPOCH=3D$(stat --printf=3D'%Y' %{_= specdir}/%{name}.spec) %meson \ -Drunstatedir=3D%{_rundir} \ %{?arg_qemu} \ + %{?arg_ch} \ %{?arg_openvz} \ %{?arg_lxc} \ %{?arg_vbox} \ @@ -1739,6 +1764,13 @@ exit 0 %{_mandir}/man1/virt-qemu-run.1* %endif =20 +%if %{with_ch} +%files daemon-driver-ch +%dir %attr(0700, root, root) %{_localstatedir}/log/libvirt/ch/ +%ghost %dir %{_rundir}/libvirt/ch/ +%{_libdir}/%{name}/connection-driver/libvirt_driver_ch.so +%endif + %if %{with_lxc} %files daemon-driver-lxc %config(noreplace) %{_sysconfdir}/sysconfig/virtlxcd diff --git a/meson.build b/meson.build index dabd4196e6..a6759cb051 100644 --- a/meson.build +++ b/meson.build @@ -1722,6 +1722,10 @@ elif get_option('driver_lxc').enabled() error('linux and remote_driver are required for LXC') endif =20 +if not get_option('driver_ch').disabled() and host_machine.system() =3D=3D= 'linux' and conf.has('WITH_LIBVIRTD') + conf.set('WITH_CH', 1) +endif + # there's no use compiling the network driver without the libvirt # daemon, nor compiling it for macOS, where it breaks the compile if not get_option('driver_network').disabled() and conf.has('WITH_LIBVIRTD= ') and host_machine.system() !=3D 'darwin' @@ -2369,6 +2373,7 @@ driver_summary =3D { 'VBox': conf.has('WITH_VBOX'), 'libxl': conf.has('WITH_LIBXL'), 'LXC': conf.has('WITH_LXC'), + 'Cloud-Hypervisor': conf.has('WITH_CH'), 'ESX': conf.has('WITH_ESX'), 'Hyper-V': conf.has('WITH_HYPERV'), 'vz': conf.has('WITH_VZ'), diff --git a/meson_options.txt b/meson_options.txt index 79554c3186..bfb3e79e1a 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -55,6 +55,7 @@ option('driver_interface', type: 'feature', value: 'auto'= , description: 'host in option('driver_libvirtd', type: 'feature', value: 'auto', description: 'li= bvirtd driver') option('driver_libxl', type: 'feature', value: 'auto', description: 'libxe= nlight driver') option('driver_lxc', type: 'feature', value: 'auto', description: 'Linux C= ontainer driver') +option('driver_ch', type: 'feature', value: 'auto', description: 'Cloud-Hy= pervisor driver') option('driver_network', type: 'feature', value: 'auto', description: 'vir= tual network driver') option('driver_openvz', type: 'feature', value: 'auto', description: 'Open= VZ driver') option('driver_qemu', type: 'feature', value: 'auto', description: 'QEMU/K= VM driver') diff --git a/po/POTFILES.in b/po/POTFILES.in index 3d6c20c55f..b12a1b1e56 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -18,6 +18,11 @@ @SRCDIR@src/bhyve/bhyve_monitor.c @SRCDIR@src/bhyve/bhyve_parse_command.c @SRCDIR@src/bhyve/bhyve_process.c +@SRCDIR@src/ch/ch_conf.c +@SRCDIR@src/ch/ch_domain.c +@SRCDIR@src/ch/ch_driver.c +@SRCDIR@src/ch/ch_monitor.c +@SRCDIR@src/ch/ch_process.c @SRCDIR@src/conf/backup_conf.c @SRCDIR@src/conf/capabilities.c @SRCDIR@src/conf/checkpoint_conf.c diff --git a/src/ch/ch_conf.c b/src/ch/ch_conf.c new file mode 100644 index 0000000000..8769b0f7e2 --- /dev/null +++ b/src/ch/ch_conf.c @@ -0,0 +1,239 @@ +/* + * Copyright Intel Corp. 2020 + * + * ch_driver.h: header file for Cloud-Hypervisor driver functions + * + * 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 + * . + */ + +#include + +#include "configmake.h" +#include "viralloc.h" +#include "vircommand.h" +#include "virlog.h" +#include "virobject.h" +#include "virstring.h" +#include "virutil.h" + +#include "ch_conf.h" +#include "ch_domain.h" + +#define VIR_FROM_THIS VIR_FROM_CH + +VIR_LOG_INIT("ch.ch_conf"); + +static virClassPtr virCHDriverConfigClass; +static void virCHDriverConfigDispose(void *obj); + +static int virCHConfigOnceInit(void) +{ + if (!VIR_CLASS_NEW(virCHDriverConfig, virClassForObject())) + return -1; + + return 0; +} + +VIR_ONCE_GLOBAL_INIT(virCHConfig); + + +/* Functions */ +virCapsPtr virCHDriverCapsInit(void) +{ + virCapsPtr caps; + virCapsGuestPtr guest; + + if ((caps =3D virCapabilitiesNew(virArchFromHost(), + false, false)) =3D=3D NULL) + goto cleanup; + + if (!(caps->host.numa =3D virCapabilitiesHostNUMANewHost())) + goto cleanup; + + if (virCapabilitiesInitCaches(caps) < 0) + goto cleanup; + + if ((guest =3D virCapabilitiesAddGuest(caps, + VIR_DOMAIN_OSTYPE_HVM, + caps->host.arch, + NULL, + NULL, + 0, + NULL)) =3D=3D NULL) + goto cleanup; + + if (virCapabilitiesAddGuestDomain(guest, + VIR_DOMAIN_VIRT_CH, + NULL, + NULL, + 0, + NULL) =3D=3D NULL) + goto cleanup; + + return caps; + + cleanup: + virObjectUnref(caps); + return NULL; +} + +/** + * virCHDriverGetCapabilities: + * + * Get a reference to the virCapsPtr instance for the + * driver. If @refresh is true, the capabilities will be + * rebuilt first + * + * The caller must release the reference with virObjetUnref + * + * Returns: a reference to a virCapsPtr instance or NULL + */ +virCapsPtr virCHDriverGetCapabilities(virCHDriverPtr driver, + bool refresh) +{ + virCapsPtr ret; + if (refresh) { + virCapsPtr caps =3D NULL; + if ((caps =3D virCHDriverCapsInit()) =3D=3D NULL) + return NULL; + + chDriverLock(driver); + virObjectUnref(driver->caps); + driver->caps =3D caps; + } else { + chDriverLock(driver); + } + + ret =3D virObjectRef(driver->caps); + chDriverUnlock(driver); + return ret; +} + +virDomainXMLOptionPtr +chDomainXMLConfInit(virCHDriverPtr driver) +{ + virCHDriverDomainDefParserConfig.priv =3D driver; + return virDomainXMLOptionNew(&virCHDriverDomainDefParserConfig, + &virCHDriverPrivateDataCallbacks, + NULL, NULL, NULL); +} + +virCHDriverConfigPtr +virCHDriverConfigNew(void) +{ + virCHDriverConfigPtr cfg; + + if (virCHConfigInitialize() < 0) + return NULL; + + if (!(cfg =3D virObjectNew(virCHDriverConfigClass))) + return NULL; + + cfg->stateDir =3D g_strdup(CH_STATE_DIR); + cfg->logDir =3D g_strdup(CH_LOG_DIR); + + return cfg; +} + +virCHDriverConfigPtr virCHDriverGetConfig(virCHDriverPtr driver) +{ + virCHDriverConfigPtr cfg; + chDriverLock(driver); + cfg =3D virObjectRef(driver->config); + chDriverUnlock(driver); + return cfg; +} + +static void +virCHDriverConfigDispose(void *obj) +{ + virCHDriverConfigPtr cfg =3D obj; + + VIR_FREE(cfg->stateDir); + VIR_FREE(cfg->logDir); +} + +static int +chExtractVersionInfo(int *retversion) +{ + int ret =3D -1; + unsigned long version; + char *help =3D NULL; + char *tmp; + virCommandPtr cmd =3D virCommandNewArgList(CH_CMD, "--version", NULL); + + if (retversion) + *retversion =3D 0; + + virCommandAddEnvString(cmd, "LC_ALL=3DC"); + virCommandSetOutputBuffer(cmd, &help); + + if (virCommandRun(cmd, NULL) < 0) + goto cleanup; + + tmp =3D help; + + /* expected format: cloud-hypervisor v.. */ + if ((tmp =3D STRSKIP(tmp, "cloud-hypervisor v")) =3D=3D NULL) + goto cleanup; + + if (virParseVersionString(tmp, &version, false) < 0) + goto cleanup; + + // v0.9.0 is the minimum supported version + if ((unsigned int)(version / 1000000) < 1) { + if (((unsigned int)((unsigned long)(version % 1000000)) / 1000) < = 9) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Cloud-Hypervisor version is too old (v0.9.0 = is the minimum supported version)")); + goto cleanup; + } + } + + + if (retversion) + *retversion =3D version; + + ret =3D 0; + + cleanup: + virCommandFree(cmd); + VIR_FREE(help); + + return ret; +} + +int chExtractVersion(virCHDriverPtr driver) +{ + if (driver->version > 0) + return 0; + + if (chExtractVersionInfo(&driver->version) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not extract Cloud-Hypervisor version")); + return -1; + } + + return 0; +} + +int chStrToInt(const char *str) +{ + int val; + + if (virStrToLong_i(str, NULL, 10, &val) < 0) + return 0; + + return val; +} diff --git a/src/ch/ch_conf.h b/src/ch/ch_conf.h new file mode 100644 index 0000000000..04334130f7 --- /dev/null +++ b/src/ch/ch_conf.h @@ -0,0 +1,87 @@ +/* + * Copyright Intel Corp. 2020 + * + * ch_driver.h: header file for Cloud-Hypervisor driver functions + * + * 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 + * . + */ + +#pragma once + +#include "virdomainobjlist.h" +#include "virthread.h" + +#define CH_DRIVER_NAME "CH" +#define CH_CMD "cloud-hypervisor" + +#define CH_STATE_DIR RUNSTATEDIR "/libvirt/ch" +#define CH_LOG_DIR LOCALSTATEDIR "/log/libvirt/ch" + +typedef struct _virCHDriver virCHDriver; +typedef virCHDriver *virCHDriverPtr; + +typedef struct _virCHDriverConfig virCHDriverConfig; +typedef virCHDriverConfig *virCHDriverConfigPtr; + +struct _virCHDriverConfig { + virObject parent; + + char *stateDir; + char *logDir; +}; + +struct _virCHDriver +{ + virMutex lock; + + /* Require lock to get a reference on the object, + * lockless access thereafter */ + virCapsPtr caps; + + /* Immutable pointer, Immutable object */ + virDomainXMLOptionPtr xmlopt; + + /* Immutable pointer, self-locking APIs */ + virDomainObjListPtr domains; + + /* Cloud-Hypervisor version */ + int version; + + /* Require lock to get reference on 'config', + * then lockless thereafter */ + virCHDriverConfigPtr config; + + /* pid file FD, ensures two copies of the driver can't use the same ro= ot */ + int lockFD; +}; + +virCapsPtr virCHDriverCapsInit(void); +virCapsPtr virCHDriverGetCapabilities(virCHDriverPtr driver, + bool refresh); +virDomainXMLOptionPtr chDomainXMLConfInit(virCHDriverPtr driver); +virCHDriverConfigPtr virCHDriverConfigNew(void); +virCHDriverConfigPtr virCHDriverGetConfig(virCHDriverPtr driver); +int chExtractVersion(virCHDriverPtr driver); +int chStrToInt(const char *str); + +static inline void chDriverLock(virCHDriverPtr driver) +{ + virMutexLock(&driver->lock); +} + +static inline void chDriverUnlock(virCHDriverPtr driver) +{ + virMutexUnlock(&driver->lock); +} diff --git a/src/ch/ch_domain.c b/src/ch/ch_domain.c new file mode 100644 index 0000000000..a46641d50d --- /dev/null +++ b/src/ch/ch_domain.c @@ -0,0 +1,219 @@ +/* + * Copyright Intel Corp. 2020 + * + * ch_driver.h: header file for Cloud-Hypervisor driver functions + * + * 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 + * . + */ + +#include + +#include "ch_domain.h" +#include "viralloc.h" +#include "virlog.h" +#include "virtime.h" + +#define VIR_FROM_THIS VIR_FROM_CH + +VIR_ENUM_IMPL(virCHDomainJob, + CH_JOB_LAST, + "none", + "query", + "destroy", + "modify", +); + +VIR_LOG_INIT("ch.ch_domain"); + +static int +virCHDomainObjInitJob(virCHDomainObjPrivatePtr priv) +{ + memset(&priv->job, 0, sizeof(priv->job)); + + if (virCondInit(&priv->job.cond) < 0) + return -1; + + return 0; +} + +static void +virCHDomainObjResetJob(virCHDomainObjPrivatePtr priv) +{ + struct virCHDomainJobObj *job =3D &priv->job; + + job->active =3D CH_JOB_NONE; + job->owner =3D 0; +} + +static void +virCHDomainObjFreeJob(virCHDomainObjPrivatePtr priv) +{ + ignore_value(virCondDestroy(&priv->job.cond)); +} + +/* + * obj must be locked before calling, virCHDriverPtr must NOT be locked + * + * This must be called by anything that will change the VM state + * in any way + * + * Upon successful return, the object will have its ref count increased. + * Successful calls must be followed by EndJob eventually. + */ +int +virCHDomainObjBeginJob(virDomainObjPtr obj, enum virCHDomainJob job) +{ + virCHDomainObjPrivatePtr priv =3D obj->privateData; + unsigned long long now; + unsigned long long then; + + if (virTimeMillisNow(&now) < 0) + return -1; + then =3D now + CH_JOB_WAIT_TIME; + + while (priv->job.active) { + VIR_DEBUG("Wait normal job condition for starting job: %s", + virCHDomainJobTypeToString(job)); + if (virCondWaitUntil(&priv->job.cond, &obj->parent.lock, then) < 0) + goto error; + } + + virCHDomainObjResetJob(priv); + + VIR_DEBUG("Starting job: %s", virCHDomainJobTypeToString(job)); + priv->job.active =3D job; + priv->job.owner =3D virThreadSelfID(); + + return 0; + + error: + VIR_WARN("Cannot start job (%s) for domain %s;" + " current job is (%s) owned by (%d)", + virCHDomainJobTypeToString(job), + obj->def->name, + virCHDomainJobTypeToString(priv->job.active), + priv->job.owner); + + if (errno =3D=3D ETIMEDOUT) + virReportError(VIR_ERR_OPERATION_TIMEOUT, + "%s", _("cannot acquire state change lock")); + else + virReportSystemError(errno, + "%s", _("cannot acquire job mutex")); + return -1; +} + +/* + * obj must be locked and have a reference before calling + * + * To be called after completing the work associated with the + * earlier virCHDomainBeginJob() call + */ +void +virCHDomainObjEndJob(virDomainObjPtr obj) +{ + virCHDomainObjPrivatePtr priv =3D obj->privateData; + enum virCHDomainJob job =3D priv->job.active; + + VIR_DEBUG("Stopping job: %s", + virCHDomainJobTypeToString(job)); + + virCHDomainObjResetJob(priv); + virCondSignal(&priv->job.cond); +} + +static void * +virCHDomainObjPrivateAlloc(void *opaque G_GNUC_UNUSED) +{ + virCHDomainObjPrivatePtr priv; + + if (VIR_ALLOC(priv) < 0) + return NULL; + + if (virCHDomainObjInitJob(priv) < 0) { + VIR_FREE(priv); + return NULL; + } + + return priv; +} + +static void +virCHDomainObjPrivateFree(void *data) +{ + virCHDomainObjPrivatePtr priv =3D data; + + virCHDomainObjFreeJob(priv); + VIR_FREE(priv); +} + +static int +virCHDomainObjPrivateXMLFormat(virBufferPtr buf, + virDomainObjPtr vm) +{ + virCHDomainObjPrivatePtr priv =3D vm->privateData; + virBufferAsprintf(buf, "\n", + (long long)priv->initpid); + + return 0; +} + +static int +virCHDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, + virDomainObjPtr vm, + virDomainDefParserConfigPtr config G_GNUC_UN= USED) +{ + virCHDomainObjPrivatePtr priv =3D vm->privateData; + long long thepid; + + if (virXPathLongLong("string(./init[1]/@pid)", ctxt, &thepid) < 0) { + VIR_WARN("Failed to load init pid from state %s", + virGetLastErrorMessage()); + priv->initpid =3D 0; + } else { + priv->initpid =3D thepid; + } + + return 0; +} + +virDomainXMLPrivateDataCallbacks virCHDriverPrivateDataCallbacks =3D { + .alloc =3D virCHDomainObjPrivateAlloc, + .free =3D virCHDomainObjPrivateFree, + .format =3D virCHDomainObjPrivateXMLFormat, + .parse =3D virCHDomainObjPrivateXMLParse, +}; + +static int +virCHDomainDefPostParse(virDomainDefPtr def, + unsigned int parseFlags G_GNUC_UNUSED, + void *opaque, + void *parseOpaque G_GNUC_UNUSED) +{ + virCHDriverPtr driver =3D opaque; + g_autoptr(virCaps) caps =3D virCHDriverGetCapabilities(driver, false); + if (!caps) + return -1; + if (!virCapabilitiesDomainSupported(caps, def->os.type, + def->os.arch, + def->virtType)) + return -1; + + return 0; +} + +virDomainDefParserConfig virCHDriverDomainDefParserConfig =3D { + .domainPostParseCallback =3D virCHDomainDefPostParse, +}; diff --git a/src/ch/ch_domain.h b/src/ch/ch_domain.h new file mode 100644 index 0000000000..144f147173 --- /dev/null +++ b/src/ch/ch_domain.h @@ -0,0 +1,68 @@ +/* + * Copyright Intel Corp. 2020 + * + * ch_driver.h: header file for Cloud-Hypervisor driver functions + * + * 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 + * . + */ + +#pragma once + +#include "ch_conf.h" +#include "ch_monitor.h" + +/* Give up waiting for mutex after 30 seconds */ +#define CH_JOB_WAIT_TIME (1000ull * 30) + +/* Only 1 job is allowed at any time + * A job includes *all* ch.so api, even those just querying + * information, not merely actions */ + +enum virCHDomainJob { + CH_JOB_NONE =3D 0, /* Always set to 0 for easy if (jobActive) con= ditions */ + CH_JOB_QUERY, /* Doesn't change any state */ + CH_JOB_DESTROY, /* Destroys the domain (cannot be masked out) */ + CH_JOB_MODIFY, /* May change state */ + CH_JOB_LAST +}; +VIR_ENUM_DECL(virCHDomainJob); + + +struct virCHDomainJobObj { + virCond cond; /* Use to coordinate jobs */ + enum virCHDomainJob active; /* Currently running job */ + int owner; /* Thread which set current job */ +}; + + +typedef struct _virCHDomainObjPrivate virCHDomainObjPrivate; +typedef virCHDomainObjPrivate *virCHDomainObjPrivatePtr; +struct _virCHDomainObjPrivate { + pid_t initpid; + + struct virCHDomainJobObj job; + + virCHMonitorPtr monitor; +}; + +extern virDomainXMLPrivateDataCallbacks virCHDriverPrivateDataCallbacks; +extern virDomainDefParserConfig virCHDriverDomainDefParserConfig; + +int +virCHDomainObjBeginJob(virDomainObjPtr obj, enum virCHDomainJob job) + G_GNUC_WARN_UNUSED_RESULT; + +void +virCHDomainObjEndJob(virDomainObjPtr obj); diff --git a/src/ch/ch_driver.c b/src/ch/ch_driver.c new file mode 100644 index 0000000000..e5b027f71f --- /dev/null +++ b/src/ch/ch_driver.c @@ -0,0 +1,937 @@ +/* + * Copyright Intel Corp. 2020 + * + * ch_driver.h: header file for Cloud-Hypervisor driver functions + * + * 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 + * . + */ + +#include + +#include "ch_conf.h" +#include "ch_domain.h" +#include "ch_driver.h" +#include "ch_monitor.h" +#include "ch_process.h" +#include "datatypes.h" +#include "driver.h" +#include "viraccessapicheck.h" +#include "viralloc.h" +#include "virbuffer.h" +#include "vircommand.h" +#include "virerror.h" +#include "virfile.h" +#include "virlog.h" +#include "virnetdevtap.h" +#include "virobject.h" +#include "virstring.h" +#include "virtypedparam.h" +#include "viruri.h" +#include "virutil.h" +#include "viruuid.h" + +#define VIR_FROM_THIS VIR_FROM_CH + +VIR_LOG_INIT("ch.ch_driver"); + +static int chStateInitialize(bool privileged, + const char *root, + virStateInhibitCallback callback, + void *opaque); +static int chStateCleanup(void); +virCHDriverPtr ch_driver =3D NULL; + +static virDomainObjPtr +chDomObjFromDomain(virDomainPtr domain) +{ + virDomainObjPtr vm; + virCHDriverPtr driver =3D domain->conn->privateData; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + vm =3D virDomainObjListFindByUUID(driver->domains, domain->uuid); + if (!vm) { + virUUIDFormat(domain->uuid, uuidstr); + virReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s' (%s)"), + uuidstr, domain->name); + return NULL; + } + + return vm; +} + +/* Functions */ +static int +chConnectURIProbe(char **uri) +{ + if (ch_driver =3D=3D NULL) + return 0; + + *uri =3D g_strdup("ch:///system"); + return 1; +} + +static virDrvOpenStatus chConnectOpen(virConnectPtr conn, + virConnectAuthPtr auth G_GNUC_UNUSED, + virConfPtr conf G_GNUC_UNUSED, + unsigned int flags) +{ + virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR); + + /* URI was good, but driver isn't active */ + if (ch_driver =3D=3D NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Cloud-Hypervisor state driver is not activ= e")); + return VIR_DRV_OPEN_ERROR; + } + + if (virConnectOpenEnsureACL(conn) < 0) + return VIR_DRV_OPEN_ERROR; + + conn->privateData =3D ch_driver; + + return VIR_DRV_OPEN_SUCCESS; +} + +static int chConnectClose(virConnectPtr conn) +{ + conn->privateData =3D NULL; + return 0; +} + +static const char *chConnectGetType(virConnectPtr conn) +{ + if (virConnectGetTypeEnsureACL(conn) < 0) + return NULL; + + return "CH"; +} + +static int chConnectGetVersion(virConnectPtr conn, + unsigned long *version) +{ + virCHDriverPtr driver =3D conn->privateData; + + if (virConnectGetVersionEnsureACL(conn) < 0) + return -1; + + chDriverLock(driver); + *version =3D driver->version; + chDriverUnlock(driver); + return 0; +} + +static char *chConnectGetHostname(virConnectPtr conn) +{ + if (virConnectGetHostnameEnsureACL(conn) < 0) + return NULL; + + return virGetHostname(); +} + +static int chConnectNumOfDomains(virConnectPtr conn) +{ + virCHDriverPtr driver =3D conn->privateData; + + if (virConnectNumOfDomainsEnsureACL(conn) < 0) + return -1; + + return virDomainObjListNumOfDomains(driver->domains, true, + virConnectNumOfDomainsCheckACL, co= nn); +} + +static int chConnectListDomains(virConnectPtr conn, int *ids, int nids) +{ + virCHDriverPtr driver =3D conn->privateData; + + if (virConnectListDomainsEnsureACL(conn) < 0) + return -1; + + return virDomainObjListGetActiveIDs(driver->domains, ids, nids, + virConnectListDomainsCheckACL, conn); +} + +static int +chConnectListAllDomains(virConnectPtr conn, + virDomainPtr **domains, + unsigned int flags) +{ + virCHDriverPtr driver =3D conn->privateData; + + virCheckFlags(VIR_CONNECT_LIST_DOMAINS_FILTERS_ALL, -1); + + if (virConnectListAllDomainsEnsureACL(conn) < 0) + return -1; + + return virDomainObjListExport(driver->domains, conn, domains, + virConnectListAllDomainsCheckACL, flags); +} + +static int chNodeGetInfo(virConnectPtr conn, + virNodeInfoPtr nodeinfo) +{ + if (virNodeGetInfoEnsureACL(conn) < 0) + return -1; + + return virCapabilitiesGetNodeInfo(nodeinfo); +} + +static char *chConnectGetCapabilities(virConnectPtr conn) +{ + virCHDriverPtr driver =3D conn->privateData; + virCapsPtr caps; + char *xml; + + if (virConnectGetCapabilitiesEnsureACL(conn) < 0) + return NULL; + + if (!(caps =3D virCHDriverGetCapabilities(driver, true))) + return NULL; + + xml =3D virCapabilitiesFormatXML(caps); + + virObjectUnref(caps); + return xml; +} + +/** + * chDomainCreateXML: + * @conn: pointer to connection + * @xml: XML definition of domain + * @flags: bitwise-OR of supported virDomainCreateFlags + * + * Creates a domain based on xml and starts it + * + * Returns a new domain object or NULL in case of failure. + */ +static virDomainPtr +chDomainCreateXML(virConnectPtr conn, + const char *xml, + unsigned int flags) +{ + virCHDriverPtr driver =3D conn->privateData; + virDomainDefPtr vmdef =3D NULL; + virDomainObjPtr vm =3D NULL; + virDomainPtr dom =3D NULL; + unsigned int parse_flags =3D VIR_DOMAIN_DEF_PARSE_INACTIVE; + + virCheckFlags(VIR_DOMAIN_START_VALIDATE, NULL); + + if (flags & VIR_DOMAIN_START_VALIDATE) + parse_flags |=3D VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA; + + + if ((vmdef =3D virDomainDefParseString(xml, driver->xmlopt, + NULL, parse_flags)) =3D=3D NULL) + goto cleanup; + + if (virDomainCreateXMLEnsureACL(conn, vmdef) < 0) + goto cleanup; + + if (!(vm =3D virDomainObjListAdd(driver->domains, + vmdef, + driver->xmlopt, + VIR_DOMAIN_OBJ_LIST_ADD_LIVE | + VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE, + NULL))) + goto cleanup; + + vmdef =3D NULL; + vm->persistent =3D 1; + + if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0) + goto cleanup; + + if (virCHProcessStart(driver, vm, VIR_DOMAIN_RUNNING_BOOTED) < 0) + goto cleanup; + + dom =3D virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id); + + virCHDomainObjEndJob(vm); + + cleanup: + virDomainDefFree(vmdef); + virDomainObjEndAPI(&vm); + chDriverUnlock(driver); + return dom; +} + +static int +chDomainCreateWithFlags(virDomainPtr dom, unsigned int flags) +{ + virCHDriverPtr driver =3D dom->conn->privateData; + virDomainObjPtr vm; + int ret =3D -1; + + virCheckFlags(0, -1); + + if (!(vm =3D chDomObjFromDomain(dom))) + goto cleanup; + + if (virDomainCreateWithFlagsEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0) + goto cleanup; + + ret =3D virCHProcessStart(driver, vm, VIR_DOMAIN_RUNNING_BOOTED); + + virCHDomainObjEndJob(vm); + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + +static int +chDomainCreate(virDomainPtr dom) +{ + return chDomainCreateWithFlags(dom, 0); +} + +static virDomainPtr +chDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int f= lags) +{ + virCHDriverPtr driver =3D conn->privateData; + virDomainDefPtr vmdef =3D NULL; + virDomainObjPtr vm =3D NULL; + virDomainPtr dom =3D NULL; + unsigned int parse_flags =3D VIR_DOMAIN_DEF_PARSE_INACTIVE; + + virCheckFlags(VIR_DOMAIN_DEFINE_VALIDATE, NULL); + + if (flags & VIR_DOMAIN_START_VALIDATE) + parse_flags |=3D VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA; + + if ((vmdef =3D virDomainDefParseString(xml, driver->xmlopt, + NULL, parse_flags)) =3D=3D NULL) + goto cleanup; + + if (virXMLCheckIllegalChars("name", vmdef->name, "\n") < 0) + goto cleanup; + + if (virDomainDefineXMLFlagsEnsureACL(conn, vmdef) < 0) + goto cleanup; + + if (!(vm =3D virDomainObjListAdd(driver->domains, vmdef, + driver->xmlopt, + 0, NULL))) + goto cleanup; + + vmdef =3D NULL; + vm->persistent =3D 1; + + dom =3D virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id); + + cleanup: + virDomainDefFree(vmdef); + virDomainObjEndAPI(&vm); + return dom; +} + +static virDomainPtr +chDomainDefineXML(virConnectPtr conn, const char *xml) +{ + return chDomainDefineXMLFlags(conn, xml, 0); +} + +static int +chDomainUndefineFlags(virDomainPtr dom, + unsigned int flags) +{ + virCHDriverPtr driver =3D dom->conn->privateData; + virDomainObjPtr vm; + int ret =3D -1; + + virCheckFlags(0, -1); + + if (!(vm =3D chDomObjFromDomain(dom))) + goto cleanup; + + if (virDomainUndefineFlagsEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + if (!vm->persistent) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("Cannot undefine transient domain")); + goto cleanup; + } + + if (virDomainObjIsActive(vm)) { + vm->persistent =3D 0; + } else { + virDomainObjListRemove(driver->domains, vm); + } + + ret =3D 0; + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + +static int +chDomainUndefine(virDomainPtr dom) +{ + return chDomainUndefineFlags(dom, 0); +} + +static int chDomainIsActive(virDomainPtr dom) +{ + virCHDriverPtr driver =3D dom->conn->privateData; + virDomainObjPtr vm; + int ret =3D -1; + + chDriverLock(driver); + if (!(vm =3D chDomObjFromDomain(dom))) + goto cleanup; + + if (virDomainIsActiveEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + ret =3D virDomainObjIsActive(vm); + + cleanup: + virDomainObjEndAPI(&vm); + chDriverUnlock(driver); + return ret; +} + +static int +chDomainShutdownFlags(virDomainPtr dom, + unsigned int flags) +{ + virCHDomainObjPrivatePtr priv; + virDomainObjPtr vm; + virDomainState state; + int ret =3D -1; + + virCheckFlags(VIR_DOMAIN_SHUTDOWN_INITCTL | + VIR_DOMAIN_SHUTDOWN_SIGNAL, -1); + + if (!(vm =3D chDomObjFromDomain(dom))) + goto cleanup; + + priv =3D vm->privateData; + + if (virDomainShutdownFlagsEnsureACL(dom->conn, vm->def, flags) < 0) + goto cleanup; + + if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0) + goto cleanup; + + if (virDomainObjCheckActive(vm) < 0) + goto endjob; + + state =3D virDomainObjGetState(vm, NULL); + if (state !=3D VIR_DOMAIN_RUNNING && state !=3D VIR_DOMAIN_PAUSED) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("only can shutdown running/paused domain")); + goto endjob; + } else { + if (virCHMonitorShutdownVM(priv->monitor) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to shutdown guest VM")); + goto endjob; + } + } + + virDomainObjSetState(vm, VIR_DOMAIN_SHUTDOWN, VIR_DOMAIN_SHUTDOWN_USER= ); + + ret =3D 0; + + endjob: + virCHDomainObjEndJob(vm); + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + +static int +chDomainShutdown(virDomainPtr dom) +{ + return chDomainShutdownFlags(dom, 0); +} + + +static int +chDomainReboot(virDomainPtr dom, unsigned int flags) +{ + virCHDomainObjPrivatePtr priv; + virDomainObjPtr vm; + virDomainState state; + int ret =3D -1; + + virCheckFlags(VIR_DOMAIN_REBOOT_INITCTL | + VIR_DOMAIN_REBOOT_SIGNAL, -1); + + if (!(vm =3D chDomObjFromDomain(dom))) + goto cleanup; + + priv =3D vm->privateData; + + if (virDomainRebootEnsureACL(dom->conn, vm->def, flags) < 0) + goto cleanup; + + if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0) + goto cleanup; + + if (virDomainObjCheckActive(vm) < 0) + goto endjob; + + state =3D virDomainObjGetState(vm, NULL); + if (state !=3D VIR_DOMAIN_RUNNING && state !=3D VIR_DOMAIN_PAUSED) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("only can reboot running/paused domain")); + goto endjob; + } else { + if (virCHMonitorRebootVM(priv->monitor) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to reboot domain")); + goto endjob; + } + } + + if (state =3D=3D VIR_DOMAIN_RUNNING) + virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_BO= OTED); + else + virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_UN= PAUSED); + + ret =3D 0; + + endjob: + virCHDomainObjEndJob(vm); + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + +static int +chDomainSuspend(virDomainPtr dom) +{ + virCHDomainObjPrivatePtr priv; + virDomainObjPtr vm; + int ret =3D -1; + + if (!(vm =3D chDomObjFromDomain(dom))) + goto cleanup; + + priv =3D vm->privateData; + + if (virDomainSuspendEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0) + goto cleanup; + + if (virDomainObjCheckActive(vm) < 0) + goto endjob; + + if (virDomainObjGetState(vm, NULL) !=3D VIR_DOMAIN_RUNNING) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("only can suspend running domain")); + goto endjob; + } else { + if (virCHMonitorSuspendVM(priv->monitor) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to suspend domain")); + goto endjob; + } + } + + virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_USER); + + ret =3D 0; + + endjob: + virCHDomainObjEndJob(vm); + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + +static int +chDomainResume(virDomainPtr dom) +{ + virCHDomainObjPrivatePtr priv; + virDomainObjPtr vm; + int ret =3D -1; + + if (!(vm =3D chDomObjFromDomain(dom))) + goto cleanup; + + priv =3D vm->privateData; + + if (virDomainResumeEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0) + goto cleanup; + + if (virDomainObjCheckActive(vm) < 0) + goto endjob; + + if (virDomainObjGetState(vm, NULL) !=3D VIR_DOMAIN_PAUSED) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("only can resume paused domain")); + goto endjob; + } else { + if (virCHMonitorResumeVM(priv->monitor) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to resume domain")); + goto endjob; + } + } + + virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_UNPAUS= ED); + + ret =3D 0; + + endjob: + virCHDomainObjEndJob(vm); + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + +/** + * chDomainDestroyFlags: + * @dom: pointer to domain to destroy + * @flags: extra flags; not used yet. + * + * Sends SIGKILL to Cloud-Hypervisor process to terminate it + * + * Returns 0 on success or -1 in case of error + */ +static int +chDomainDestroyFlags(virDomainPtr dom, unsigned int flags) +{ + virCHDriverPtr driver =3D dom->conn->privateData; + virDomainObjPtr vm; + int ret =3D -1; + + virCheckFlags(0, -1); + + if (!(vm =3D chDomObjFromDomain(dom))) + goto cleanup; + + if (virDomainDestroyFlagsEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + if (virCHDomainObjBeginJob(vm, CH_JOB_DESTROY) < 0) + goto cleanup; + + if (virDomainObjCheckActive(vm) < 0) + goto endjob; + + ret =3D virCHProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_DESTROYED); + + endjob: + virCHDomainObjEndJob(vm); + if (!vm->persistent) + virDomainObjListRemove(driver->domains, vm); + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + +static int +chDomainDestroy(virDomainPtr dom) +{ + return chDomainDestroyFlags(dom, 0); +} + +static virDomainPtr chDomainLookupByID(virConnectPtr conn, + int id) +{ + virCHDriverPtr driver =3D conn->privateData; + virDomainObjPtr vm; + virDomainPtr dom =3D NULL; + + chDriverLock(driver); + vm =3D virDomainObjListFindByID(driver->domains, id); + chDriverUnlock(driver); + + if (!vm) { + virReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching id '%d'"), id); + goto cleanup; + } + + if (virDomainLookupByIDEnsureACL(conn, vm->def) < 0) + goto cleanup; + + dom =3D virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id); + + cleanup: + virDomainObjEndAPI(&vm); + return dom; +} + +static virDomainPtr chDomainLookupByName(virConnectPtr conn, + const char *name) +{ + virCHDriverPtr driver =3D conn->privateData; + virDomainObjPtr vm; + virDomainPtr dom =3D NULL; + + chDriverLock(driver); + vm =3D virDomainObjListFindByName(driver->domains, name); + chDriverUnlock(driver); + + if (!vm) { + virReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching name '%s'"), name); + goto cleanup; + } + + if (virDomainLookupByNameEnsureACL(conn, vm->def) < 0) + goto cleanup; + + dom =3D virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id); + + cleanup: + virDomainObjEndAPI(&vm); + return dom; +} + +static virDomainPtr chDomainLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) +{ + virCHDriverPtr driver =3D conn->privateData; + virDomainObjPtr vm; + virDomainPtr dom =3D NULL; + + chDriverLock(driver); + vm =3D virDomainObjListFindByUUID(driver->domains, uuid); + chDriverUnlock(driver); + + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(uuid, uuidstr); + virReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; + } + + if (virDomainLookupByUUIDEnsureACL(conn, vm->def) < 0) + goto cleanup; + + dom =3D virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id); + + cleanup: + virDomainObjEndAPI(&vm); + return dom; +} + +static int +chDomainGetState(virDomainPtr dom, + int *state, + int *reason, + unsigned int flags) +{ + virDomainObjPtr vm; + int ret =3D -1; + + virCheckFlags(0, -1); + + if (!(vm =3D chDomObjFromDomain(dom))) + goto cleanup; + + if (virDomainGetStateEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + *state =3D virDomainObjGetState(vm, reason); + ret =3D 0; + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + +static char *chDomainGetXMLDesc(virDomainPtr dom, + unsigned int flags) +{ + virCHDriverPtr driver =3D dom->conn->privateData; + virDomainObjPtr vm; + char *ret =3D NULL; + + virCheckFlags(VIR_DOMAIN_XML_COMMON_FLAGS, NULL); + + if (!(vm =3D chDomObjFromDomain(dom))) + goto cleanup; + + if (virDomainGetXMLDescEnsureACL(dom->conn, vm->def, flags) < 0) + goto cleanup; + + ret =3D virDomainDefFormat(vm->def, driver->xmlopt, + virDomainDefFormatConvertXMLFlags(flags)); + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + +static int chDomainGetInfo(virDomainPtr dom, + virDomainInfoPtr info) +{ + virDomainObjPtr vm; + int ret =3D -1; + + if (!(vm =3D chDomObjFromDomain(dom))) + goto cleanup; + + if (virDomainGetInfoEnsureACL(dom->conn, vm->def) < 0) + goto cleanup; + + info->state =3D virDomainObjGetState(vm, NULL); + + info->cpuTime =3D 0; + + info->maxMem =3D virDomainDefGetMemoryTotal(vm->def); + info->memory =3D vm->def->mem.cur_balloon; + info->nrVirtCpu =3D virDomainDefGetVcpus(vm->def); + + ret =3D 0; + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + +static int chStateCleanup(void) +{ + if (ch_driver =3D=3D NULL) + return -1; + + virObjectUnref(ch_driver->domains); + virObjectUnref(ch_driver->xmlopt); + virObjectUnref(ch_driver->caps); + virObjectUnref(ch_driver->config); + virMutexDestroy(&ch_driver->lock); + VIR_FREE(ch_driver); + + return 0; +} + +static int chStateInitialize(bool privileged, + const char *root, + virStateInhibitCallback callback G_GNUC_UNUSE= D, + void *opaque G_GNUC_UNUSED) +{ + if (root !=3D NULL) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("Driver does not support embedded mode")); + return -1; + } + + /* Check that the user is root, silently disable if not */ + if (!privileged) { + VIR_INFO("Not running privileged, disabling driver"); + return VIR_DRV_STATE_INIT_SKIPPED; + } + + if (VIR_ALLOC(ch_driver) < 0) + return VIR_DRV_STATE_INIT_ERROR; + + if (virMutexInit(&ch_driver->lock) < 0) { + VIR_FREE(ch_driver); + return VIR_DRV_STATE_INIT_ERROR; + } + + if (!(ch_driver->domains =3D virDomainObjListNew())) + goto cleanup; + + if (!(ch_driver->caps =3D virCHDriverCapsInit())) + goto cleanup; + + if (!(ch_driver->xmlopt =3D chDomainXMLConfInit(ch_driver))) + goto cleanup; + + if (!(ch_driver->config =3D virCHDriverConfigNew())) + goto cleanup; + + if (chExtractVersion(ch_driver) < 0) + goto cleanup; + + return VIR_DRV_STATE_INIT_COMPLETE; + + cleanup: + chStateCleanup(); + return VIR_DRV_STATE_INIT_ERROR; +} + +/* Function Tables */ +static virHypervisorDriver chHypervisorDriver =3D { + .name =3D "CH", + .connectURIProbe =3D chConnectURIProbe, + .connectOpen =3D chConnectOpen, /* 6.7.0 */ + .connectClose =3D chConnectClose, /* 6.7.0 */ + .connectGetType =3D chConnectGetType, /* 6.7.0 */ + .connectGetVersion =3D chConnectGetVersion, /* 6.7.0 */ + .connectGetHostname =3D chConnectGetHostname, /* 6.7.0 */ + .connectNumOfDomains =3D chConnectNumOfDomains, /* 6.7.0 */ + .connectListAllDomains =3D chConnectListAllDomains, /* 6.7.0 */ + .connectListDomains =3D chConnectListDomains, /* 6.7.0 */ + .connectGetCapabilities =3D chConnectGetCapabilities, /* 6.7.0 */ + .domainCreateXML =3D chDomainCreateXML, /* 6.7.0 */ + .domainCreate =3D chDomainCreate, /* 6.7.0 */ + .domainCreateWithFlags =3D chDomainCreateWithFlags, /* 6.7.0 */ + .domainShutdown =3D chDomainShutdown, /* 6.7.0 */ + .domainShutdownFlags =3D chDomainShutdownFlags, /* 6.7.0 */ + .domainReboot =3D chDomainReboot, /* 6.7.0 */ + .domainSuspend =3D chDomainSuspend, /* 6.7.0 */ + .domainResume =3D chDomainResume, /* 6.7.0 */ + .domainDestroy =3D chDomainDestroy, /* 6.7.0 */ + .domainDestroyFlags =3D chDomainDestroyFlags, /* 6.7.0 */ + .domainDefineXML =3D chDomainDefineXML, /* 6.7.0 */ + .domainDefineXMLFlags =3D chDomainDefineXMLFlags, /* 6.7.0 */ + .domainUndefine =3D chDomainUndefine, /* 6.7.0 */ + .domainUndefineFlags =3D chDomainUndefineFlags, /* 6.7.0 */ + .domainLookupByID =3D chDomainLookupByID, /* 6.7.0 */ + .domainLookupByUUID =3D chDomainLookupByUUID, /* 6.7.0 */ + .domainLookupByName =3D chDomainLookupByName, /* 6.7.0 */ + .domainGetState =3D chDomainGetState, /* 6.7.0 */ + .domainGetXMLDesc =3D chDomainGetXMLDesc, /* 6.7.0 */ + .domainGetInfo =3D chDomainGetInfo, /* 6.7.0 */ + .domainIsActive =3D chDomainIsActive, /* 6.7.0 */ + .nodeGetInfo =3D chNodeGetInfo, /* 6.7.0 */ +}; + +static virConnectDriver chConnectDriver =3D { + .localOnly =3D true, + .uriSchemes =3D (const char *[]){"CH", "Ch", "ch", "Cloud-Hypervisor",= NULL}, + .hypervisorDriver =3D &chHypervisorDriver, +}; + +static virStateDriver chStateDriver =3D { + .name =3D "CH", + .stateInitialize =3D chStateInitialize, + .stateCleanup =3D chStateCleanup, +}; + +int chRegister(void) +{ + if (virRegisterConnectDriver(&chConnectDriver, false) < 0) + return -1; + if (virRegisterStateDriver(&chStateDriver) < 0) + return -1; + return 0; +} diff --git a/src/ch/ch_driver.h b/src/ch/ch_driver.h new file mode 100644 index 0000000000..0516c91c24 --- /dev/null +++ b/src/ch/ch_driver.h @@ -0,0 +1,24 @@ +/* + * Copyright Intel Corp. 2020 + * + * ch_driver.h: header file for Cloud-Hypervisor driver functions + * + * 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 + * . + */ + +#pragma once + +/* Function declarations */ +int chRegister(void); diff --git a/src/ch/ch_monitor.c b/src/ch/ch_monitor.c new file mode 100644 index 0000000000..ccef70f719 --- /dev/null +++ b/src/ch/ch_monitor.c @@ -0,0 +1,796 @@ +/* + * Copyright Intel Corp. 2020 + * + * ch_driver.h: header file for Cloud-Hypervisor driver functions + * + * 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 + * . + */ + +#include + +#include +#include + +#include "ch_conf.h" +#include "ch_monitor.h" +#include "viralloc.h" +#include "vircommand.h" +#include "virerror.h" +#include "virfile.h" +#include "virjson.h" +#include "virlog.h" +#include "virtime.h" + +#define VIR_FROM_THIS VIR_FROM_CH + +VIR_LOG_INIT("ch.ch_monitor"); + +static virClassPtr virCHMonitorClass; +static void virCHMonitorDispose(void *obj); + +static int virCHMonitorOnceInit(void) +{ + if (!VIR_CLASS_NEW(virCHMonitor, virClassForObjectLockable())) + return -1; + + return 0; +} + +VIR_ONCE_GLOBAL_INIT(virCHMonitor); + +int virCHMonitorShutdownVMM(virCHMonitorPtr mon); +int virCHMonitorPutNoContent(virCHMonitorPtr mon, const char *endpoint); +int virCHMonitorGet(virCHMonitorPtr mon, const char *endpoint); +int virCHMonitorPingVMM(virCHMonitorPtr mon); + +static int +virCHMonitorBuildCPUJson(virJSONValuePtr content, virDomainDefPtr vmdef) +{ + virJSONValuePtr cpus; + unsigned int maxvcpus =3D 0; + unsigned int nvcpus =3D 0; + virDomainVcpuDefPtr vcpu; + size_t i; + + /* count maximum allowed number vcpus and enabled vcpus when boot.*/ + maxvcpus =3D virDomainDefGetVcpusMax(vmdef); + for (i =3D 0; i < maxvcpus; i++) { + vcpu =3D virDomainDefGetVcpu(vmdef, i); + if (vcpu->online) + nvcpus++; + } + + if (maxvcpus !=3D 0 || nvcpus !=3D 0) { + cpus =3D virJSONValueNewObject(); + if (virJSONValueObjectAppendNumberInt(cpus, "boot_vcpus", nvcpus) = < 0) + goto cleanup; + if (virJSONValueObjectAppendNumberInt(cpus, "max_vcpus", vmdef->ma= xvcpus) < 0) + goto cleanup; + if (virJSONValueObjectAppend(content, "cpus", cpus) < 0) + goto cleanup; + } + + return 0; + + cleanup: + virJSONValueFree(cpus); + return -1; +} + +static int +virCHMonitorBuildKernelJson(virJSONValuePtr content, virDomainDefPtr vmdef) +{ + virJSONValuePtr kernel; + + if (vmdef->os.kernel =3D=3D NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Kernel image path in this domain is not defined"= )); + return -1; + } else { + kernel =3D virJSONValueNewObject(); + if (virJSONValueObjectAppendString(kernel, "path", vmdef->os.kerne= l) < 0) + goto cleanup; + if (virJSONValueObjectAppend(content, "kernel", kernel) < 0) + goto cleanup; + } + + return 0; + + cleanup: + virJSONValueFree(kernel); + return -1; +} + +static int +virCHMonitorBuildCmdlineJson(virJSONValuePtr content, virDomainDefPtr vmde= f) +{ + virJSONValuePtr cmdline; + + cmdline =3D virJSONValueNewObject(); + if (vmdef->os.cmdline) { + if (virJSONValueObjectAppendString(cmdline, "args", vmdef->os.cmdl= ine) < 0) + goto cleanup; + if (virJSONValueObjectAppend(content, "cmdline", cmdline) < 0) + goto cleanup; + } + + return 0; + + cleanup: + virJSONValueFree(cmdline); + return -1; +} + +static int +virCHMonitorBuildMemoryJson(virJSONValuePtr content, virDomainDefPtr vmdef) +{ + virJSONValuePtr memory; + unsigned long long total_memory =3D virDomainDefGetMemoryInitial(vmdef= ) * 1024; + + if (total_memory !=3D 0) { + memory =3D virJSONValueNewObject(); + if (virJSONValueObjectAppendNumberUlong(memory, "size", total_memo= ry) < 0) + goto cleanup; + if (virJSONValueObjectAppend(content, "memory", memory) < 0) + goto cleanup; + } + + return 0; + + cleanup: + virJSONValueFree(memory); + return -1; +} + +static int +virCHMonitorBuildInitramfsJson(virJSONValuePtr content, virDomainDefPtr vm= def) +{ + virJSONValuePtr initramfs; + + if (vmdef->os.initrd !=3D NULL) { + initramfs =3D virJSONValueNewObject(); + if (virJSONValueObjectAppendString(initramfs, "path", vmdef->os.in= itrd) < 0) + goto cleanup; + if (virJSONValueObjectAppend(content, "initramfs", initramfs) < 0) + goto cleanup; + } + + return 0; + + cleanup: + virJSONValueFree(initramfs); + return -1; +} + +static int +virCHMonitorBuildDiskJson(virJSONValuePtr disks, virDomainDiskDefPtr diskd= ef) +{ + virJSONValuePtr disk; + + if (diskdef->src !=3D NULL && diskdef->src->path !=3D NULL) { + disk =3D virJSONValueNewObject(); + if (virJSONValueObjectAppendString(disk, "path", diskdef->src->pat= h) < 0) + goto cleanup; + if (diskdef->src->readonly) { + if (virJSONValueObjectAppendBoolean(disk, "readonly", true) < = 0) + goto cleanup; + } + if (virJSONValueArrayAppend(disks, disk) < 0) + goto cleanup; + } + + return 0; + + cleanup: + virJSONValueFree(disk); + return -1; +} + +static int +virCHMonitorBuildDisksJson(virJSONValuePtr content, virDomainDefPtr vmdef) +{ + virJSONValuePtr disks; + size_t i; + + if (vmdef->ndisks > 0) { + disks =3D virJSONValueNewArray(); + + for (i =3D 0; i < vmdef->ndisks; i++) { + if (virCHMonitorBuildDiskJson(disks, vmdef->disks[i]) < 0) + goto cleanup; + } + if (virJSONValueObjectAppend(content, "disks", disks) < 0) + goto cleanup; + } + + return 0; + + cleanup: + virJSONValueFree(disks); + return -1; +} + +static int +virCHMonitorBuildNetJson(virJSONValuePtr nets, virDomainNetDefPtr netdef) +{ + virDomainNetType netType =3D virDomainNetGetActualType(netdef); + char macaddr[VIR_MAC_STRING_BUFLEN]; + virJSONValuePtr net; + + // check net type at first + net =3D virJSONValueNewObject(); + + switch (netType) { + case VIR_DOMAIN_NET_TYPE_ETHERNET: + if (netdef->guestIP.nips =3D=3D 1) { + const virNetDevIPAddr *ip =3D netdef->guestIP.ips[0]; + g_autofree char *addr =3D NULL; + virSocketAddr netmask; + g_autofree char *netmaskStr =3D NULL; + if (!(addr =3D virSocketAddrFormat(&ip->address))) + goto cleanup; + if (virJSONValueObjectAppendString(net, "ip", addr) < 0) + goto cleanup; + + if (virSocketAddrPrefixToNetmask(ip->prefix, &netmask, AF_INET= ) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to translate net prefix %d to net= mask"), + ip->prefix); + goto cleanup; + } + if (!(netmaskStr =3D virSocketAddrFormat(&netmask))) + goto cleanup; + if (virJSONValueObjectAppendString(net, "mask", netmaskStr) < = 0) + goto cleanup; + } + break; + case VIR_DOMAIN_NET_TYPE_VHOSTUSER: + if ((virDomainChrType)netdef->data.vhostuser->type !=3D VIR_DOMAIN= _CHR_TYPE_UNIX) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("vhost_user type support UNIX socket in this = CH")); + goto cleanup; + } else { + if (virJSONValueObjectAppendString(net, "vhost_socket", netdef= ->data.vhostuser->data.nix.path) < 0) + goto cleanup; + if (virJSONValueObjectAppendBoolean(net, "vhost_user", true) <= 0) + goto cleanup; + } + break; + case VIR_DOMAIN_NET_TYPE_BRIDGE: + case VIR_DOMAIN_NET_TYPE_NETWORK: + case VIR_DOMAIN_NET_TYPE_DIRECT: + 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_HOSTDEV: + case VIR_DOMAIN_NET_TYPE_UDP: + case VIR_DOMAIN_NET_TYPE_LAST: + default: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Only ethernet and vhost_user type network types = are " + "supported in this CH")); + goto cleanup; + } + + if (netdef->ifname !=3D NULL) { + if (virJSONValueObjectAppendString(net, "tap", netdef->ifname) < 0) + goto cleanup; + } + if (virJSONValueObjectAppendString(net, "mac", virMacAddrFormat(&netde= f->mac, macaddr)) < 0) + goto cleanup; + + + if (netdef->virtio !=3D NULL) { + if (netdef->virtio->iommu =3D=3D VIR_TRISTATE_SWITCH_ON) { + if (virJSONValueObjectAppendBoolean(net, "iommu", true) < 0) + goto cleanup; + } + } + if (netdef->driver.virtio.queues) { + if (virJSONValueObjectAppendNumberInt(net, "num_queues", netdef->d= river.virtio.queues) < 0) + goto cleanup; + } + + if (netdef->driver.virtio.rx_queue_size || netdef->driver.virtio.tx_qu= eue_size) { + if (netdef->driver.virtio.rx_queue_size !=3D netdef->driver.virtio= .tx_queue_size) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("virtio rx_queue_size option %d is not same with tx_queue= _size %d"), + netdef->driver.virtio.rx_queue_size, + netdef->driver.virtio.tx_queue_size); + goto cleanup; + } + if (virJSONValueObjectAppendNumberInt(net, "queue_size", netdef->d= river.virtio.rx_queue_size) < 0) + goto cleanup; + } + + if (virJSONValueArrayAppend(nets, net) < 0) + goto cleanup; + + return 0; + + cleanup: + virJSONValueFree(net); + return -1; +} + +static int +virCHMonitorBuildNetsJson(virJSONValuePtr content, virDomainDefPtr vmdef) +{ + virJSONValuePtr nets; + size_t i; + + if (vmdef->nnets > 0) { + nets =3D virJSONValueNewArray(); + + for (i =3D 0; i < vmdef->nnets; i++) { + if (virCHMonitorBuildNetJson(nets, vmdef->nets[i]) < 0) + goto cleanup; + } + if (virJSONValueObjectAppend(content, "net", nets) < 0) + goto cleanup; + } + + return 0; + + cleanup: + virJSONValueFree(nets); + return -1; +} + +static int +virCHMonitorBuildVMJson(virDomainDefPtr vmdef, char **jsonstr) +{ + virJSONValuePtr content =3D virJSONValueNewObject(); + int ret =3D -1; + + if (vmdef =3D=3D NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("VM is not defined")); + goto cleanup; + } + + if (virCHMonitorBuildCPUJson(content, vmdef) < 0) + goto cleanup; + + if (virCHMonitorBuildMemoryJson(content, vmdef) < 0) + goto cleanup; + + if (virCHMonitorBuildKernelJson(content, vmdef) < 0) + goto cleanup; + + if (virCHMonitorBuildCmdlineJson(content, vmdef) < 0) + goto cleanup; + + if (virCHMonitorBuildInitramfsJson(content, vmdef) < 0) + goto cleanup; + + if (virCHMonitorBuildDisksJson(content, vmdef) < 0) + goto cleanup; + + if (virCHMonitorBuildNetsJson(content, vmdef) < 0) + goto cleanup; + + if (!(*jsonstr =3D virJSONValueToString(content, false))) + goto cleanup; + + ret =3D 0; + + cleanup: + virJSONValueFree(content); + return ret; +} + +/* generate command to launch Cloud-Hypervisor socket + return -1 - error + 0 - OK + Caller has to free the cmd +*/ +static virCommandPtr +chMonitorBuildSocketCmd(virDomainObjPtr vm, const char *socket_path) +{ + virCommandPtr cmd; + + if (vm->def =3D=3D NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("VM is not defined")); + return NULL; + } + + if (vm->def->emulator !=3D NULL) + cmd =3D virCommandNew(vm->def->emulator); + else + cmd =3D virCommandNew(CH_CMD); + + virCommandAddArgList(cmd, "--api-socket", socket_path, NULL); + + return cmd; +} + +virCHMonitorPtr +virCHMonitorNew(virDomainObjPtr vm, const char *socketdir) +{ + virCHMonitorPtr ret =3D NULL; + virCHMonitorPtr mon =3D NULL; + virCommandPtr cmd =3D NULL; + int pings =3D 0; + + if (virCHMonitorInitialize() < 0) + return NULL; + + if (!(mon =3D virObjectLockableNew(virCHMonitorClass))) + return NULL; + + mon->socketpath =3D g_strdup_printf("%s/%s-socket", socketdir, vm->def= ->name); + + /* prepare to launch Cloud-Hypervisor socket */ + if (!(cmd =3D chMonitorBuildSocketCmd(vm, mon->socketpath))) + goto cleanup; + + if (virFileMakePath(socketdir) < 0) { + virReportSystemError(errno, + _("Cannot create socket directory '%s'"), + socketdir); + goto cleanup; + } + + /* launch Cloud-Hypervisor socket */ + if (virCommandRunAsync(cmd, &mon->pid) < 0) + goto cleanup; + + /* get a curl handle */ + mon->handle =3D curl_easy_init(); + + /* try to ping VMM socket 5 times to make sure it is ready */ + while (pings < 5) { + if (virCHMonitorPingVMM(mon) =3D=3D 0) + break; + if (pings =3D=3D 5) + goto cleanup; + + g_usleep(100 * 1000); + } + + /* now has its own reference */ + virObjectRef(mon); + mon->vm =3D virObjectRef(vm); + + ret =3D mon; + + cleanup: + virCommandFree(cmd); + return ret; +} + +static void virCHMonitorDispose(void *opaque) +{ + virCHMonitorPtr mon =3D opaque; + + VIR_DEBUG("mon=3D%p", mon); + virObjectUnref(mon->vm); +} + +void virCHMonitorClose(virCHMonitorPtr mon) +{ + if (!mon) + return; + + if (mon->pid > 0) { + /* try cleaning up the Cloud-Hypervisor process */ + virProcessAbort(mon->pid); + mon->pid =3D 0; + } + + if (mon->handle) + curl_easy_cleanup(mon->handle); + + if (mon->socketpath) { + if (virFileRemove(mon->socketpath, -1, -1) < 0) { + VIR_WARN("Unable to remove CH socket file '%s'", + mon->socketpath); + } + VIR_FREE(mon->socketpath); + } + + virObjectUnref(mon); + if (mon->vm) + virObjectUnref(mon->vm); +} + + +struct data { + char trace_ascii; /* 1 or 0 */ +}; + +static void dump(const char *text, + FILE *stream, + unsigned char *ptr, + size_t size, + char nohex) +{ + size_t i; + size_t c; + + unsigned int width =3D 0x10; + + if (nohex) + /* without the hex output, we can fit more on screen */ + width =3D 0x40; + + fprintf(stream, "%s, %10.10lu bytes (0x%8.8lx)\n", text, (unsigned lon= g)size, + (unsigned long)size); + + for (i =3D 0; i < size; i +=3D width) { + + fprintf(stream, "%4.4lx: ", (unsigned long)i); + + if (!nohex) { + /* hex not disabled, show it */ + for (c =3D 0; c < width; c++) { + if (i + c < size) + fprintf(stream, "%02x ", ptr[i + c]); + else + fputs(" ", stream); + } + } + + for (c =3D 0; (c < width) && (i + c < size); c++) { + /* check for 0D0A; if found, skip past and start a new line of= output */ + if (nohex && (i + c + 1 < size) && ptr[i + c] =3D=3D 0x0D && + ptr[i + c + 1] =3D=3D 0x0A) { + i +=3D (c + 2 - width); + break; + } + fprintf(stream, "%c", + (ptr[i + c] >=3D 0x20) && (ptr[i + c] < 0x80) ? ptr[i = + c] : '.'); + /* check again for 0D0A, to avoid an extra \n if it's at width= */ + if (nohex && (i + c + 2 < size) && ptr[i + c + 1] =3D=3D 0x0D = && + ptr[i + c + 2] =3D=3D 0x0A) { + i +=3D (c + 3 - width); + break; + } + } + fputc('\n', stream); /* newline */ + } + fflush(stream); +} + +static int my_trace(CURL *handle, + curl_infotype type, + char *data, + size_t size, + void *userp) +{ + struct data *config =3D (struct data *)userp; + const char *text =3D ""; + (void)handle; /* prevent compiler warning */ + + switch (type) { + case CURLINFO_TEXT: + fprintf(stderr, "=3D=3D Info: %s", data); + /* FALLTHROUGH */ + case CURLINFO_END: /* in case a new one is introduced to shock us */ + break; + case CURLINFO_HEADER_OUT: + text =3D "=3D> Send header"; + break; + case CURLINFO_DATA_OUT: + text =3D "=3D> Send data"; + break; + case CURLINFO_SSL_DATA_OUT: + text =3D "=3D> Send SSL data"; + break; + case CURLINFO_HEADER_IN: + text =3D "<=3D Recv header"; + break; + case CURLINFO_DATA_IN: + text =3D "<=3D Recv data"; + break; + case CURLINFO_SSL_DATA_IN: + text =3D "<=3D Recv SSL data"; + break; + } + + dump(text, stderr, (unsigned char *)data, size, config->trace_ascii); + return 0; +} + +static int +virCHMonitorCurlPerform(CURL *handle) +{ + CURLcode errorCode; + long responseCode =3D 0; + + struct data config; + + config.trace_ascii =3D 1; /* enable ascii tracing */ + + curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(handle, CURLOPT_DEBUGDATA, &config); + + /* the DEBUGFUNCTION has no effect until we enable VERBOSE */ + curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L); + + errorCode =3D curl_easy_perform(handle); + + if (errorCode !=3D CURLE_OK) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("curl_easy_perform() returned an error: %s (%d)"), + curl_easy_strerror(errorCode), errorCode); + return -1; + } + + errorCode =3D curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, + &responseCode); + + if (errorCode !=3D CURLE_OK) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("curl_easy_getinfo(CURLINFO_RESPONSE_CODE) return= ed an " + "error: %s (%d)"), curl_easy_strerror(errorCode), + errorCode); + return -1; + } + + if (responseCode < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("curl_easy_getinfo(CURLINFO_RESPONSE_CODE) return= ed a " + "negative response code")); + return -1; + } + + return responseCode; +} + +int +virCHMonitorPutNoContent(virCHMonitorPtr mon, const char *endpoint) +{ + char *url; + int responseCode =3D 0; + int ret =3D -1; + + url =3D g_strdup_printf("%s/%s", URL_ROOT, endpoint); + + virObjectLock(mon); + + /* reset all options of a libcurl session handle at first */ + curl_easy_reset(mon->handle); + + curl_easy_setopt(mon->handle, CURLOPT_UNIX_SOCKET_PATH, mon->socketpat= h); + curl_easy_setopt(mon->handle, CURLOPT_URL, url); + curl_easy_setopt(mon->handle, CURLOPT_PUT, true); + curl_easy_setopt(mon->handle, CURLOPT_HTTPHEADER, NULL); + + responseCode =3D virCHMonitorCurlPerform(mon->handle); + + virObjectUnlock(mon); + + if (responseCode =3D=3D 200 || responseCode =3D=3D 204) + ret =3D 0; + + VIR_FREE(url); + return ret; +} + +int +virCHMonitorGet(virCHMonitorPtr mon, const char *endpoint) +{ + char *url; + int responseCode =3D 0; + int ret =3D -1; + + url =3D g_strdup_printf("%s/%s", URL_ROOT, endpoint); + + virObjectLock(mon); + + /* reset all options of a libcurl session handle at first */ + curl_easy_reset(mon->handle); + + curl_easy_setopt(mon->handle, CURLOPT_UNIX_SOCKET_PATH, mon->socketpat= h); + curl_easy_setopt(mon->handle, CURLOPT_URL, url); + + responseCode =3D virCHMonitorCurlPerform(mon->handle); + + virObjectUnlock(mon); + + if (responseCode =3D=3D 200 || responseCode =3D=3D 204) + ret =3D 0; + + VIR_FREE(url); + return ret; +} + +int +virCHMonitorPingVMM(virCHMonitorPtr mon) +{ + return virCHMonitorGet(mon, URL_VMM_PING); +} + +int +virCHMonitorShutdownVMM(virCHMonitorPtr mon) +{ + return virCHMonitorPutNoContent(mon, URL_VMM_SHUTDOWN); +} + +int +virCHMonitorCreateVM(virCHMonitorPtr mon) +{ + g_autofree char *url =3D NULL; + int responseCode =3D 0; + int ret =3D -1; + g_autofree char *payload =3D NULL; + struct curl_slist *headers =3D NULL; + + url =3D g_strdup_printf("%s/%s", URL_ROOT, URL_VM_CREATE); + headers =3D curl_slist_append(headers, "Accept: application/json"); + headers =3D curl_slist_append(headers, "Content-Type: application/json= "); + headers =3D curl_slist_append(headers, "Expect:"); + + if (virCHMonitorBuildVMJson(mon->vm->def, &payload) !=3D 0) + return -1; + + virObjectLock(mon); + + /* reset all options of a libcurl session handle at first */ + curl_easy_reset(mon->handle); + + curl_easy_setopt(mon->handle, CURLOPT_UNIX_SOCKET_PATH, mon->socketpat= h); + curl_easy_setopt(mon->handle, CURLOPT_URL, url); + curl_easy_setopt(mon->handle, CURLOPT_CUSTOMREQUEST, "PUT"); + curl_easy_setopt(mon->handle, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(mon->handle, CURLOPT_POSTFIELDS, payload); + + responseCode =3D virCHMonitorCurlPerform(mon->handle); + + virObjectUnlock(mon); + + if (responseCode =3D=3D 200 || responseCode =3D=3D 204) + ret =3D 0; + + curl_slist_free_all(headers); + VIR_FREE(url); + VIR_FREE(payload); + return ret; +} + +int +virCHMonitorBootVM(virCHMonitorPtr mon) +{ + return virCHMonitorPutNoContent(mon, URL_VM_BOOT); +} + +int +virCHMonitorShutdownVM(virCHMonitorPtr mon) +{ + return virCHMonitorPutNoContent(mon, URL_VM_SHUTDOWN); +} + +int +virCHMonitorRebootVM(virCHMonitorPtr mon) +{ + return virCHMonitorPutNoContent(mon, URL_VM_REBOOT); +} + +int +virCHMonitorSuspendVM(virCHMonitorPtr mon) +{ + return virCHMonitorPutNoContent(mon, URL_VM_Suspend); +} + +int +virCHMonitorResumeVM(virCHMonitorPtr mon) +{ + return virCHMonitorPutNoContent(mon, URL_VM_RESUME); +} diff --git a/src/ch/ch_monitor.h b/src/ch/ch_monitor.h new file mode 100644 index 0000000000..bd6046a50b --- /dev/null +++ b/src/ch/ch_monitor.h @@ -0,0 +1,62 @@ +/* + * Copyright Intel Corp. 2020 + * + * ch_driver.h: header file for Cloud-Hypervisor driver functions + * + * 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 + * . + */ + +#pragma once + +#include + +#include "virobject.h" +#include "domain_conf.h" + +#define URL_ROOT "http://localhost/api/v1" +#define URL_VMM_SHUTDOWN "vmm.shutdown" +#define URL_VMM_PING "vmm.ping" +#define URL_VM_CREATE "vm.create" +#define URL_VM_DELETE "vm.delete" +#define URL_VM_BOOT "vm.boot" +#define URL_VM_SHUTDOWN "vm.shutdown" +#define URL_VM_REBOOT "vm.reboot" +#define URL_VM_Suspend "vm.pause" +#define URL_VM_RESUME "vm.resume" + +typedef struct _virCHMonitor virCHMonitor; +typedef virCHMonitor *virCHMonitorPtr; + +struct _virCHMonitor { + virObjectLockable parent; + + CURL *handle; + + char *socketpath; + + pid_t pid; + + virDomainObjPtr vm; +}; + +virCHMonitorPtr virCHMonitorNew(virDomainObjPtr vm, const char *socketdir); +void virCHMonitorClose(virCHMonitorPtr mon); + +int virCHMonitorCreateVM(virCHMonitorPtr mon); +int virCHMonitorBootVM(virCHMonitorPtr mon); +int virCHMonitorShutdownVM(virCHMonitorPtr mon); +int virCHMonitorRebootVM(virCHMonitorPtr mon); +int virCHMonitorSuspendVM(virCHMonitorPtr mon); +int virCHMonitorResumeVM(virCHMonitorPtr mon); diff --git a/src/ch/ch_process.c b/src/ch/ch_process.c new file mode 100644 index 0000000000..15f4801549 --- /dev/null +++ b/src/ch/ch_process.c @@ -0,0 +1,125 @@ +/* + * Copyright Intel Corp. 2020 + * + * ch_driver.h: header file for Cloud-Hypervisor driver functions + * + * 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 + * . + */ + +#include + +#include +#include + +#include "ch_domain.h" +#include "ch_monitor.h" +#include "ch_process.h" +#include "viralloc.h" +#include "virerror.h" +#include "virlog.h" + +#define VIR_FROM_THIS VIR_FROM_CH + +VIR_LOG_INIT("ch.ch_process"); + +#define START_SOCKET_POSTFIX ": starting up socket\n" +#define START_VM_POSTFIX ": starting up vm\n" + + + +static virCHMonitorPtr virCHProcessConnectMonitor(virCHDriverPtr driver, + virDomainObjPtr vm) +{ + virCHMonitorPtr monitor =3D NULL; + virCHDriverConfigPtr cfg =3D virCHDriverGetConfig(driver); + + monitor =3D virCHMonitorNew(vm, cfg->stateDir); + + virObjectUnref(cfg); + return monitor; +} + +/** + * virCHProcessStart: + * @driver: pointer to driver structure + * @vm: pointer to virtual machine structure + * @reason: reason for switching vm to running state + * + * Starts Cloud-Hypervisor listen on a local socket + * + * Returns 0 on success or -1 in case of error + */ +int virCHProcessStart(virCHDriverPtr driver, + virDomainObjPtr vm, + virDomainRunningReason reason) +{ + int ret =3D -1; + virCHDomainObjPrivatePtr priv =3D vm->privateData; + + if (!priv->monitor) { + /* And we can get the first monitor connection now too */ + if (!(priv->monitor =3D virCHProcessConnectMonitor(driver, vm))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create connection to CH socket")); + goto cleanup; + } + + if (virCHMonitorCreateVM(priv->monitor) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to create guest VM")); + goto cleanup; + } + } + + if (virCHMonitorBootVM(priv->monitor) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to boot guest VM")); + goto cleanup; + } + + vm->pid =3D priv->monitor->pid; + vm->def->id =3D vm->pid; + virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason); + + return 0; + + cleanup: + if (ret) + virCHProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED); + + return ret; +} + +int virCHProcessStop(virCHDriverPtr driver G_GNUC_UNUSED, + virDomainObjPtr vm, + virDomainShutoffReason reason) +{ + virCHDomainObjPrivatePtr priv =3D vm->privateData; + + VIR_DEBUG("Stopping VM name=3D%s pid=3D%d reason=3D%d", + vm->def->name, (int)vm->pid, (int)reason); + + if (priv->monitor) { + virCHMonitorClose(priv->monitor); + priv->monitor =3D NULL; + } + + vm->pid =3D -1; + vm->def->id =3D -1; + + virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, reason); + + return 0; +} diff --git a/src/ch/ch_process.h b/src/ch/ch_process.h new file mode 100644 index 0000000000..22f3c39618 --- /dev/null +++ b/src/ch/ch_process.h @@ -0,0 +1,30 @@ +/* + * Copyright Intel Corp. 2020 + * + * ch_driver.h: header file for Cloud-Hypervisor driver functions + * + * 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 + * . + */ + +#pragma once + +#include "internal.h" + +int virCHProcessStart(virCHDriverPtr driver, + virDomainObjPtr vm, + virDomainRunningReason reason); +int virCHProcessStop(virCHDriverPtr driver, + virDomainObjPtr vm, + virDomainShutoffReason reason); diff --git a/src/ch/meson.build b/src/ch/meson.build new file mode 100644 index 0000000000..e41691bc05 --- /dev/null +++ b/src/ch/meson.build @@ -0,0 +1,44 @@ +ch_driver_sources =3D [ + 'ch_conf.c', + 'ch_conf.h', + 'ch_domain.c', + 'ch_domain.h', + 'ch_driver.c', + 'ch_driver.h', + 'ch_monitor.c', + 'ch_monitor.h', + 'ch_process.c', + 'ch_process.h', +] + +driver_source_files +=3D files(ch_driver_sources) + +stateful_driver_source_files +=3D files(ch_driver_sources) + +if conf.has('WITH_CH') + ch_driver_impl =3D static_library( + 'virt_driver_ch_impl', + [ + ch_driver_sources, + ], + dependencies: [ + access_dep, + curl_dep, + log_dep, + src_dep, + ], + include_directories: [ + conf_inc_dir, + ], + ) + + virt_modules +=3D { + 'name': 'virt_driver_ch', + 'link_whole': [ + ch_driver_impl, + ], + 'link_args': [ + libvirt_no_undefined, + ], + } +endif diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 5d3ae8bb28..11b183ad2c 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -130,6 +130,7 @@ VIR_ENUM_IMPL(virDomainVirt, "parallels", "bhyve", "vz", + "ch", ); =20 VIR_ENUM_IMPL(virDomainOS, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 8a0f26f5c0..4dba588728 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -139,6 +139,7 @@ typedef enum { VIR_DOMAIN_VIRT_PARALLELS, VIR_DOMAIN_VIRT_BHYVE, VIR_DOMAIN_VIRT_VZ, + VIR_DOMAIN_VIRT_CH, =20 VIR_DOMAIN_VIRT_LAST } virDomainVirtType; diff --git a/src/meson.build b/src/meson.build index 5d8deaf548..90fbee39e5 100644 --- a/src/meson.build +++ b/src/meson.build @@ -272,6 +272,7 @@ subdir('esx') subdir('hyperv') subdir('libxl') subdir('lxc') +subdir('ch') subdir('openvz') subdir('qemu') subdir('test') diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 6798febf8d..819b381e21 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -6820,6 +6820,7 @@ qemuBuildMachineCommandLine(virCommandPtr cmd, case VIR_DOMAIN_VIRT_PARALLELS: case VIR_DOMAIN_VIRT_BHYVE: case VIR_DOMAIN_VIRT_VZ: + case VIR_DOMAIN_VIRT_CH: case VIR_DOMAIN_VIRT_NONE: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("the QEMU binary does not support %s"), diff --git a/src/remote/remote_daemon.c b/src/remote/remote_daemon.c index 1aa9bfc0d2..f8df8de095 100644 --- a/src/remote/remote_daemon.c +++ b/src/remote/remote_daemon.c @@ -169,6 +169,10 @@ static int daemonInitialize(void) if (virDriverLoadModule("qemu", "qemuRegister", false) < 0) return -1; # endif +# ifdef WITH_CH + if (virDriverLoadModule("ch", "chRegister", false) < 0) + return -1; +# endif # ifdef WITH_LXC if (virDriverLoadModule("lxc", "lxcRegister", false) < 0) return -1; diff --git a/src/util/virerror.c b/src/util/virerror.c index 507a29f50f..9446c908bf 100644 --- a/src/util/virerror.c +++ b/src/util/virerror.c @@ -144,6 +144,7 @@ VIR_ENUM_IMPL(virErrorDomain, =20 "TPM", /* 70 */ "BPF", + "Cloud-Hypervisor Driver", ); =20 =20 diff --git a/tools/virsh.c b/tools/virsh.c index 06ff5e8336..9555ea3374 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -510,6 +510,9 @@ virshShowVersion(vshControl *ctl G_GNUC_UNUSED) #ifdef WITH_OPENVZ vshPrint(ctl, " OpenVZ"); #endif +#ifdef WITH_CH + vshPrint(ctl, " Cloud-Hypervisor"); +#endif #ifdef WITH_VZ vshPrint(ctl, " Virtuozzo"); #endif --=20 2.26.2