[libvirt] [PATCH RFC 3/4] libxl: implement qemu-agent-command
Joao Martins
joao.m.martins at oracle.com
Fri Mar 3 13:25:01 UTC 2017
On 03/03/2017 12:15 AM, Jim Fehlig wrote:
> On 03/02/2017 11:46 AM, Jim Fehlig wrote:
>> On 02/08/2017 09:44 AM, Joao Martins wrote:
>>> Signed-off-by: Joao Martins <joao.m.martins at oracle.com>
>
> Opps, forgot to mention that it would be helpful if the commit message contained
> info on what Xen versions are supported and example config for the agent's
> channel device.
Yeap, will add it.
> BTW, does this work with PV domUs that have a qemu process?
Yeap, it works.
>
> Regards,
> Jim
>
>>> ---
>>> src/libxl/libxl_domain.c | 239 ++++++++++++++++++++++++++++++++++++++++++++++-
>>> src/libxl/libxl_domain.h | 16 ++++
>>> src/libxl/libxl_driver.c | 51 ++++++++++
>>> 3 files changed, 305 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/src/libxl/libxl_domain.c b/src/libxl/libxl_domain.c
>>> index ed73cd2..6bdd0ec 100644
>>> --- a/src/libxl/libxl_domain.c
>>> +++ b/src/libxl/libxl_domain.c
>>> @@ -782,6 +782,12 @@ libxlDomainCleanup(libxlDriverPrivatePtr driver,
>>> }
>>> }
>>>
>>> + if (priv->agent) {
>>> + qemuAgentClose(priv->agent);
>>> + priv->agent = NULL;
>>> + priv->agentError = false;
>>> + }
>>> +
>>> if ((vm->def->nnets)) {
>>> size_t i;
>>>
>>> @@ -940,6 +946,228 @@ libxlDomainFreeMem(libxl_ctx *ctx, libxl_domain_config
>>> *d_config)
>>> return -1;
>>> }
>>>
>>> +/*
>>> + * This is a callback registered with a qemuAgentPtr instance,
>>> + * and to be invoked when the agent console hits an end of file
>>> + * condition, or error, thus indicating VM shutdown should be
>>> + * performed
>>> + */
>>> +static void
>>> +libxlHandleAgentEOF(qemuAgentPtr agent,
>>> + virDomainObjPtr vm)
>>
>> Whitespace is off.
>>
>>> +{
>>> + libxlDomainObjPrivatePtr priv;
>>> +
>>> + VIR_DEBUG("Received EOF from agent on %p '%s'", vm, vm->def->name);
>>> +
>>> + virObjectLock(vm);
>>> +
>>> + priv = vm->privateData;
>>> +
>>> + if (!priv->agent) {
>>> + VIR_DEBUG("Agent freed already");
>>> + goto unlock;
>>> + }
>>> +
>>> + qemuAgentClose(agent);
>>> + priv->agent = NULL;
>>
>> Should priv->agentError be reset to false?
>>
>>> +
>>> + unlock:
>>> + virObjectUnlock(vm);
>>> + return;
>>> +}
>>> +
>>> +
>>> +/*
>>> + * This is invoked when there is some kind of error
>>> + * parsing data to/from the agent. The VM can continue
>>> + * to run, but no further agent commands will be
>>> + * allowed
>>> + */
>>> +static void
>>> +libxlHandleAgentError(qemuAgentPtr agent ATTRIBUTE_UNUSED,
>>> + virDomainObjPtr vm)
>>
>> Whitespace is off here too, and in a few other places below that I'll stop
>> nagging about.
>>
>>> +{
>>> + libxlDomainObjPrivatePtr priv;
>>> +
>>> + VIR_DEBUG("Received error from agent on %p '%s'", vm, vm->def->name);
>>> +
>>> + virObjectLock(vm);
>>> +
>>> + priv = vm->privateData;
>>> +
>>> + priv->agentError = true;
>>> +
>>> + virObjectUnlock(vm);
>>> +}
>>> +
>>> +static void libxlHandleAgentDestroy(qemuAgentPtr agent,
>>> + virDomainObjPtr vm)
>>> +{
>>> + VIR_DEBUG("Received destroy agent=%p vm=%p", agent, vm);
>>> +
>>> + virObjectUnref(vm);
>>> +}
>>> +
>>> +static qemuAgentCallbacks agentCallbacks = {
>>> + .destroy = libxlHandleAgentDestroy,
>>> + .eofNotify = libxlHandleAgentEOF,
>>> + .errorNotify = libxlHandleAgentError,
>>> +};
>>> +
>>> +static virDomainChrDefPtr
>>> +libxlFindAgentConfig(virDomainDefPtr def)
>>> +{
>>> + size_t i;
>>> +
>>> + for (i = 0; i < def->nchannels; i++) {
>>> + virDomainChrDefPtr channel = def->channels[i];
>>> +
>>> + if (channel->targetType != VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_XEN)
>>> + continue;
>>> +
>>> + if (STREQ_NULLABLE(channel->target.name, "org.qemu.guest_agent.0"))
>>> + return channel;
>>> + }
>>> +
>>> + return NULL;
>>> +}
>>> +
>>> +bool
>>> +libxlDomainAgentAvailable(virDomainObjPtr vm, bool reportError)
>>
>> Is the reportError parameter needed? The callers in this patch and 4/4 pass 'true'.
>>
>>> +{
>>> + libxlDomainObjPrivatePtr priv = vm->privateData;
>>> +
>>> + if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_RUNNING) {
>>> + if (reportError) {
>>> + virReportError(VIR_ERR_OPERATION_INVALID, "%s",
>>> + _("domain is not running"));
>>> + }
>>> + return false;
>>> + }
>>> + if (priv->agentError) {
>>> + if (reportError) {
>>> + virReportError(VIR_ERR_AGENT_UNRESPONSIVE, "%s",
>>> + _("QEMU guest agent is not "
>>> + "available due to an error"));
>>> + }
>>> + return false;
>>> + }
>>> + if (!priv->agent) {
>>> + if (libxlFindAgentConfig(vm->def)) {
>>> + if (reportError) {
>>> + virReportError(VIR_ERR_AGENT_UNRESPONSIVE, "%s",
>>> + _("QEMU guest agent is not connected"));
>>> + }
>>> + return false;
>>> + } else {
>>> + if (reportError) {
>>> + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
>>> + _("QEMU guest agent is not configured"));
>>> + }
>>> + return false;
>>> + }
>>> + }
>>> + return true;
>>> +}
>>> +
>>> +static int
>>> +libxlConnectAgent(virDomainObjPtr vm)
>>> +{
>>> + virDomainChrDefPtr config = libxlFindAgentConfig(vm->def);
>>> + libxlDomainObjPrivatePtr priv = vm->privateData;
>>> + qemuAgentPtr agent = NULL;
>>> + int ret = -1;
>>> +
>>> + if (!config)
>>> + return 0;
>>> +
>>> + if (priv->agent)
>>> + return 0;
>>> +
>>> + /* Hold an extra reference because we can't allow 'vm' to be
>>> + * deleted while the agent is active */
>>> + virObjectRef(vm);
>>> +
>>> + ignore_value(virTimeMillisNow(&priv->agentStart));
>>> + virObjectUnlock(vm);
>>> +
>>> + agent = qemuAgentOpen(vm, config->source, &agentCallbacks);
>>> +
>>> + virObjectLock(vm);
>>> + priv->agentStart = 0;
>>> +
>>> + if (agent == NULL)
>>> + virObjectUnref(vm);
>>> +
>>> + if (!virDomainObjIsActive(vm)) {
>>> + qemuAgentClose(agent);
>>> + virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>>> + _("guest crashed while connecting to the guest agent"));
>>> + ret = -2;
>>
>> This can be dropped, right? ret is initialized to -1 and the caller only checks
>> for ret < 0.
>>
>>> + goto cleanup;
>>> + }
>>> +
>>> + priv->agent = agent;
>>> +
>>> + if (priv->agent == NULL) {
>>> + VIR_INFO("Failed to connect agent for %s", vm->def->name);
>>> + goto cleanup;
>>> + }
>>> +
>>> + ret = 0;
>>> +
>>> + cleanup:
>>> + return ret;
>>
>> Hmm, since there is nothing to cleanup might as well return -1 at failure points
>> and 0 on success.
>>
>>> +}
>>> +
>>> +/*
>>> + * obj must be locked before calling
>>> + *
>>> + * To be called immediately before any QEMU agent API call.
>>> + * Must have already called libxlDomainObjBeginJob() and checked
>>> + * that the VM is still active.
>>> + *
>>> + * To be followed with libxlDomainObjExitAgent() once complete
>>> + */
>>> +void
>>> +libxlDomainObjEnterAgent(virDomainObjPtr obj)
>>> +{
>>> + libxlDomainObjPrivatePtr priv = obj->privateData;
>>> +
>>> + VIR_DEBUG("Entering agent (agent=%p vm=%p name=%s)",
>>> + priv->agent, obj, obj->def->name);
>>> + virObjectLock(priv->agent);
>>> + virObjectRef(priv->agent);
>>> + ignore_value(virTimeMillisNow(&priv->agentStart));
>>> + virObjectUnlock(obj);
>>> +}
>>> +
>>> +
>>> +/* obj must NOT be locked before calling
>>> + *
>>> + * Should be paired with an earlier qemuDomainObjEnterAgent() call
>>> + */
>>> +void
>>> +libxlDomainObjExitAgent(virDomainObjPtr obj)
>>> +{
>>> + libxlDomainObjPrivatePtr priv = obj->privateData;
>>> + bool hasRefs;
>>> +
>>> + hasRefs = virObjectUnref(priv->agent);
>>> +
>>> + if (hasRefs)
>>> + virObjectUnlock(priv->agent);
>>> +
>>> + virObjectLock(obj);
>>> + VIR_DEBUG("Exited agent (agent=%p vm=%p name=%s)",
>>> + priv->agent, obj, obj->def->name);
>>> +
>>> + priv->agentStart = 0;
>>> + if (!hasRefs)
>>> + priv->agent = NULL;
>>> +}
>>> +
>>> static int
>>> libxlNetworkPrepareDevices(virDomainDefPtr def)
>>> {
>>> @@ -1312,8 +1540,17 @@ libxlDomainStart(libxlDriverPrivatePtr driver,
>>> libxlDomainCreateIfaceNames(vm->def, &d_config);
>>>
>>> #ifdef LIBXL_HAVE_DEVICE_CHANNEL
>>> - if (vm->def->nchannels > 0)
>>> + if (vm->def->nchannels > 0) {
>>> libxlDomainCreateChannelPTY(vm->def, cfg->ctx);
>>> +
>>> + /* Failure to connect to agent shouldn't be fatal */
>>> + if (libxlConnectAgent(vm) < 0) {
>>> + VIR_WARN("Cannot connect to QEMU guest agent for %s",
>>> + vm->def->name);
>>> + virResetLastError();
>>> + priv->agentError = true;
>>> + }
>>> + }
>>> #endif
>>>
>>> if ((dom_xml = virDomainDefFormat(vm->def, cfg->caps, 0)) == NULL)
>>> diff --git a/src/libxl/libxl_domain.h b/src/libxl/libxl_domain.h
>>> index 3a3890b..59f9f8d 100644
>>> --- a/src/libxl/libxl_domain.h
>>> +++ b/src/libxl/libxl_domain.h
>>> @@ -29,6 +29,7 @@
>>> # include "domain_conf.h"
>>> # include "libxl_conf.h"
>>> # include "virchrdev.h"
>>> +# include "virqemuagent.h"
>>>
>>> # define JOB_MASK(job) (1 << (job - 1))
>>> # define DEFAULT_JOB_MASK \
>>> @@ -62,6 +63,11 @@ typedef libxlDomainObjPrivate *libxlDomainObjPrivatePtr;
>>> struct _libxlDomainObjPrivate {
>>> virObjectLockable parent;
>>>
>>> + /* agent */
>>> + qemuAgentPtr agent;
>>> + bool agentError;
>>> + unsigned long long agentStart;
>>> +
>>> /* console */
>>> virChrdevsPtr devs;
>>> libxl_evgen_domain_death *deathW;
>>> @@ -91,6 +97,16 @@ void
>>> libxlDomainObjEndJob(libxlDriverPrivatePtr driver,
>>> virDomainObjPtr obj);
>>>
>>> +bool
>>> +libxlDomainAgentAvailable(virDomainObjPtr vm,
>>> + bool reportError);
>>> +
>>> +void
>>> +libxlDomainObjEnterAgent(virDomainObjPtr obj);
>>> +
>>> +void
>>> +libxlDomainObjExitAgent(virDomainObjPtr obj);
>>> +
>>> int
>>> libxlDomainJobUpdateTime(struct libxlDomainJobObj *job)
>>> ATTRIBUTE_RETURN_CHECK;
>>> diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c
>>> index 3a69720..cf5e702 100644
>>> --- a/src/libxl/libxl_driver.c
>>> +++ b/src/libxl/libxl_driver.c
>>> @@ -57,6 +57,7 @@
>>> #include "virstring.h"
>>> #include "virsysinfo.h"
>>> #include "viraccessapicheck.h"
>>> +#include "viraccessapicheckqemu.h"
>>> #include "viratomic.h"
>>> #include "virhostdev.h"
>>> #include "network/bridge_driver.h"
>>> @@ -6415,6 +6416,55 @@ libxlConnectBaselineCPU(virConnectPtr conn,
>>> return cpu;
>>> }
>>>
>>> +static char *
>>> +libxlDomainQemuAgentCommand(virDomainPtr domain,
>>> + const char *cmd,
>>> + int timeout,
>>> + unsigned int flags)
>>> +{
>>> + libxlDriverPrivatePtr driver = domain->conn->privateData;
>>> + virDomainObjPtr vm;
>>> + int ret = -1;
>>> + char *result = NULL;
>>> + libxlDomainObjPrivatePtr priv;
>>> +
>>> + virCheckFlags(0, NULL);
>>> +
>>> + if (!(vm = libxlDomObjFromDomain(domain)))
>>> + goto cleanup;
>>> +
>>> + priv = vm->privateData;
>>> +
>>> + if (virDomainQemuAgentCommandEnsureACL(domain->conn, vm->def) < 0)
>>> + goto cleanup;
>>> +
>>> + if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0)
>>> + goto cleanup;
>>> +
>>> + if (!virDomainObjIsActive(vm)) {
>>> + virReportError(VIR_ERR_OPERATION_INVALID,
>>> + "%s", _("domain is not running"));
>>> + goto endjob;
>>> + }
>>> +
>>> + if (!libxlDomainAgentAvailable(vm, true))
>>> + goto endjob;
>>> +
>>> + libxlDomainObjEnterAgent(vm);
>>> + ret = qemuAgentArbitraryCommand(priv->agent, cmd, &result, timeout);
>>> + libxlDomainObjExitAgent(vm);
>>> + if (ret < 0)
>>> + VIR_FREE(result);
>>> +
>>> + endjob:
>>> + libxlDomainObjEndJob(driver, vm);
>>> +
>>> + cleanup:
>>> + virDomainObjEndAPI(&vm);
>>> + return result;
>>> +}
>>> +
>>> +
>>> static virHypervisorDriver libxlHypervisorDriver = {
>>> .name = LIBXL_DRIVER_NAME,
>>> .connectOpen = libxlConnectOpen, /* 0.9.0 */
>>> @@ -6522,6 +6572,7 @@ static virHypervisorDriver libxlHypervisorDriver = {
>>> .connectGetDomainCapabilities = libxlConnectGetDomainCapabilities, /*
>>> 2.0.0 */
>>> .connectCompareCPU = libxlConnectCompareCPU, /* 2.3.0 */
>>> .connectBaselineCPU = libxlConnectBaselineCPU, /* 2.3.0 */
>>> + .domainQemuAgentCommand = libxlDomainQemuAgentCommand, /* 3.1.0 */
>>
>> Heh, let's hope for 3.2.0. Apologies for the delay.
>>
>> Regards,
>> Jim
>>
>> --
>> libvir-list mailing list
>> libvir-list at redhat.com
>> https://www.redhat.com/mailman/listinfo/libvir-list
>>
>
More information about the libvir-list
mailing list