On Wed, Jun 27, 2012 at 03:58:02PM -0300, Eduardo Lima (Etrunko) wrote:
From: "Eduardo Lima (Etrunko)" <eblima(a)br.ibm.com>
For Reboot and Shutdown, th RequestStateChange method returns immediately with
typo th -> the
return code 0 (successful) even though the state change is still not
completed.
According to the DMTF specification DSP1052 (Computer System Profile) the
RequestStateChange() method should return 0x1000 and a corresponding job
reference in the return parameters which can be polled for completion.
Signed-off-by: Eduardo Lima (Etrunko) <eblima(a)br.ibm.com>
---
schema/ComputerSystem.mof | 9 ++
src/Virt_ComputerSystem.c | 326 +++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 325 insertions(+), 10 deletions(-)
diff --git a/schema/ComputerSystem.mof b/schema/ComputerSystem.mof
index 10cb8c4..886c085 100644
--- a/schema/ComputerSystem.mof
+++ b/schema/ComputerSystem.mof
@@ -1,5 +1,14 @@
// Copyright IBM Corp. 2007
+class Xen_ComputerSystemStateChangeJob : CIM_ConcreteJob {
+};
+
+class KVM_ComputerSystemStateChangeJob : CIM_ConcreteJob {
+};
+
+class LXC_ComputerSystemStateChangeJob : CIM_ConcreteJob {
+};
+
[Description (
"A class derived from CIM_ComputerSystem to represent "
"the Xen virtual machines/domains running on the system."),
diff --git a/src/Virt_ComputerSystem.c b/src/Virt_ComputerSystem.c
index e6c7e55..9546da2 100644
--- a/src/Virt_ComputerSystem.c
+++ b/src/Virt_ComputerSystem.c
@@ -30,23 +30,40 @@
#include <cmpift.h>
#include <cmpimacs.h>
+#include <uuid.h>
#include <libvirt/libvirt.h>
+#include <libvirt/virterror.h>
-#include "cs_util.h"
#include <libcmpiutil/libcmpiutil.h>
-#include "misc_util.h"
-#include "infostore.h"
-#include "device_parsing.h"
#include <libcmpiutil/std_invokemethod.h>
#include <libcmpiutil/std_instance.h>
#include <libcmpiutil/std_indication.h>
+#include "cs_util.h"
+#include "misc_util.h"
+#include "infostore.h"
+#include "device_parsing.h"
+#include "svpc_types.h"
+
#include "Virt_ComputerSystem.h"
#include "Virt_HostSystem.h"
#include "Virt_VirtualSystemSnapshotService.h"
const static CMPIBroker *_BROKER;
+typedef struct _state_change_job state_change_job_t;
+struct _state_change_job {
+ char uuid[VIR_UUID_STRING_BUFLEN];
+ CMPIContext *context;
+ CMPIObjectPath *obj_path;
+ CMPIInstance *inst;
+ char *dom_name;
+ uint16_t dom_state;
+ uint16_t status; /* job status */
+};
+
+static bool events_registered = false;
+
/* Set the "Name" property of an instance from a domain */
static int set_name_from_dom(virDomainPtr dom, CMPIInstance *instance)
{
@@ -1189,19 +1206,20 @@ static CMPIStatus __state_change(const char *name,
s = state_change_enable(dom, &info);
else if (state == CIM_STATE_DISABLED)
s = state_change_disable(dom, &info);
- else if (state == CIM_STATE_SHUTDOWN)
- s = state_change_shutdown(dom, &info);
else if (state == CIM_STATE_PAUSED)
s = state_change_pause(dom, &info);
- else if (state == CIM_STATE_REBOOT)
- s = state_change_reboot(dom, &info);
else if (state == CIM_STATE_RESET)
s = state_change_reset(dom, &info);
+ else if (state == CIM_STATE_SHUTDOWN || state == CIM_STATE_REBOOT)
+ s.rc = CIM_SVPC_RETURN_JOB_STARTED;
else
cu_statusf(_BROKER, &s,
CMPI_RC_ERR_NOT_SUPPORTED,
"State not supported");
+ if (s.rc != CMPI_RC_OK && s.rc != CIM_SVPC_RETURN_JOB_STARTED)
+ goto out;
+
infostore = infostore_open(dom);
if (infostore != NULL) {
infostore_set_u64(infostore, "reqstate", (uint64_t)state);
@@ -1215,6 +1233,265 @@ static CMPIStatus __state_change(const char *name,
return s;
}
+static CMPIStatus create_state_change_job(const CMPIObjectPath *ref,
+ const CMPIContext *context,
+ state_change_job_t **job,
+ uint16_t state)
+{
+ CMPIStatus s = {CMPI_RC_OK, NULL};
+ CMPIInstance *job_inst;
+ CMPIDateTime *start;
+ CMPIBoolean autodelete = true;
+ CMPIObjectPath *obj_path;
+ uuid_t uuid;
+ char *type = NULL, *cn = NULL, *ns = NULL;
+
+ start = CMNewDateTime(_BROKER, &s);
+ if ((s.rc != CMPI_RC_OK) || CMIsNullObject(start)) {
+ cu_statusf(_BROKER, &s,
+ CMPI_RC_ERR_FAILED,
+ "Failed to get job start time");
+ goto out;
+ }
+
+ cn = strdup(CLASSNAME(ref));
+ type = get_typed_class(cn, "ComputerSystemStateChangeJob");
+
+ obj_path = CMNewObjectPath(_BROKER, ns, type, &s);
+ if ((s.rc != CMPI_RC_OK) || (CMIsNullObject(obj_path))) {
+ cu_statusf(_BROKER, &s,
+ CMPI_RC_ERR_FAILED,
+ "Failed to get new object path");
+ goto out;
+ }
+
+ job_inst = CMNewInstance(_BROKER, obj_path, &s);
+ if ((s.rc != CMPI_RC_OK) || (CMIsNullObject(job_inst))) {
+ cu_statusf(_BROKER, &s,
+ CMPI_RC_ERR_FAILED,
+ "Failed to get new instance object");
+ goto out;
+ }
+
+ /* Alloc job struct */
+ *job = calloc(1, sizeof(**job));
+ if (*job == NULL) {
+ cu_statusf(_BROKER, &s,
+ CMPI_RC_ERR_FAILED,
+ "Failed to allocate memory for job structure");
+ goto out;
+ }
+
+ (*job)->dom_state = state;
+ (*job)->status = CIM_JOB_STATE_NEW;
+
+ uuid_generate(uuid);
+ uuid_unparse(uuid, (*job)->uuid);
+
+ /* Set Properties */
+ CMSetProperty(job_inst, "InstanceID",
+ (CMPIValue *) (*job)->uuid, CMPI_chars);
+ CMSetProperty(job_inst, "Name",
+ (CMPIValue *) "ComputerSystemStateChange", CMPI_chars);
+ CMSetProperty(job_inst, "StartTime",
+ (CMPIValue *) &start, CMPI_dateTime);
+ CMSetProperty(job_inst, "JobState",
+ (CMPIValue *) &((*job)->status), CMPI_uint16);
+ CMSetProperty(job_inst, "Status",
+ (CMPIValue *) "New", CMPI_chars);
+ CMSetProperty(job_inst, "DeleteOnCompletion",
+ (CMPIValue *)&autodelete, CMPI_boolean);
+
+ obj_path = CMGetObjectPath(job_inst, &s);
+ if ((obj_path == NULL) || (s.rc != CMPI_RC_OK)) {
+ cu_statusf(_BROKER, &s,
+ CMPI_RC_ERR_FAILED,
+ "Failed to get path for ComputerSystemStateChangeJob
instance");
+ goto out;
+ }
+
+ CMSetNameSpace(obj_path, ns);
+
+ CU_DEBUG("Creating ComputerSystemStateChangeJob instance: %s",
+ CMGetCharPtr(CMObjectPathToString(obj_path, NULL)));
+
+ obj_path = CBCreateInstance(_BROKER, context, obj_path, job_inst, &s);
+ if ((s.rc != CMPI_RC_OK) || (CMIsNullObject(obj_path))) {
+ CU_DEBUG("Failed to create ComputerSystemStateChangeJob instance:
%i", s.rc);
+ goto out;
+ }
+
+ ns = strdup(NAMESPACE(ref));
+ CMSetNameSpace(obj_path, ns);
+
+ (*job)->obj_path = obj_path;
+ (*job)->context = CBPrepareAttachThread(_BROKER, context);
+ if ((*job)->context == NULL) {
+ CU_DEBUG("Failed to create thread context");
+ cu_statusf(_BROKER, &s, CMPI_RC_ERR_FAILED,
+ "Unable to create thread context");
+ goto out;
+ }
+
+ (*job)->inst = CBGetInstance(_BROKER, (*job)->context,
(*job)->obj_path, NULL, &s);
+ if (((*job)->inst == NULL) || (s.rc != CMPI_RC_OK)) {
+ CU_DEBUG("Failed to get job instance (%i)", s.rc);
+ goto out;
+ }
+
+ out:
+ free(type);
+ free(cn);
+ free(ns);
Hum, we rely here on "free(NULL)' to do the right thing, but that's
okay for all recent implementations :-)
+ return s;
+}
+
+static void state_change_reboot_cb(virConnectPtr conn,
+ virDomainPtr dom,
+ void *data)
+{
+ state_change_job_t *job = (state_change_job_t *) data;
+ job->status = CIM_JOB_STATE_COMPLETED;
+ CU_DEBUG("state_change_reboot_cb(): Reboot complete");
+}
+
+static void state_change_shutdown_cb(virConnectPtr conn,
+ virDomainPtr dom,
+ int event,
+ int detail,
+ void *data)
+{
+ state_change_job_t *job = (state_change_job_t *) data;
+ if (event == VIR_DOMAIN_EVENT_SHUTDOWN) {
+ job->status = CIM_JOB_STATE_COMPLETED;
+ CU_DEBUG("state_change_shutdown_cb(): Shutdown complete");
+ }
+}
+
+static CMPI_THREAD_RETURN state_change_thread(void *data)
+{
+ CMPIStatus s;
+ state_change_job_t *job = (state_change_job_t *) data;
+ virConnectPtr conn = NULL;
+ virDomainPtr dom = NULL;
+ virDomainInfo info;
+ int job_cb = -1;
+
+ /* Set job state */
+ CBAttachThread(_BROKER, job->context);
+ CU_DEBUG("State change job %s started", job->uuid);
+
+ job->status = CIM_JOB_STATE_STARTING;
+ CMSetProperty(job->inst, "JobState",
+ (CMPIValue *) &(job->status), CMPI_uint16);
+ CMSetProperty(job->inst, "Status",
+ (CMPIValue *) "Starting", CMPI_chars);
+
+ /* Connect to domain event callback */
+ conn = connect_by_classname(_BROKER, CLASSNAME(job->obj_path), &s);
+ if (conn == NULL) {
+ CU_DEBUG("Unable to connect to '%s' hypervisor",
+ CLASSNAME(job->obj_path));
+ goto err;
+ }
+
+ dom = virDomainLookupByName(conn, job->dom_name);
+ if (dom == NULL) {
+ CU_DEBUG("Unable to get domain '%s'",
job->dom_name);
+ goto err;
+ }
+
+ if (virDomainGetInfo(dom, &info) != 0) {
+ CU_DEBUG("Unable to get domain info for '%s'",
job->dom_name);
+ goto err;
+ }
+
+ if (job->dom_state == CIM_STATE_REBOOT) {
+ job_cb = virConnectDomainEventRegisterAny(conn, NULL,
+ VIR_DOMAIN_EVENT_ID_REBOOT,
+ VIR_DOMAIN_EVENT_CALLBACK(state_change_reboot_cb),
+ job, NULL);
+
+ if (job_cb == -1) {
+ CU_DEBUG("Unable to connect domain reboot callback");
+ goto err;
+ }
+
+ s = state_change_reboot(dom, &info);
+
+ if (s.rc != CMPI_RC_OK) {
+ CU_DEBUG("Unable to trigger domain reboot:
'%s'",
+ CMGetCharPtr(s.msg));
+ goto err;
+ }
+ } else if (job->dom_state == CIM_STATE_SHUTDOWN) {
+ job_cb = virConnectDomainEventRegisterAny(conn, NULL,
+ VIR_DOMAIN_EVENT_ID_LIFECYCLE,
+ VIR_DOMAIN_EVENT_CALLBACK(state_change_shutdown_cb),
+ job, NULL);
+
+ if (job_cb == -1) {
+ CU_DEBUG("Unable to connect domain shutdown
callback");
+ goto err;
+ }
+
+ s = state_change_shutdown(dom, &info);
+
+ if (s.rc != CMPI_RC_OK) {
+ CU_DEBUG("Unable to trigger domain shutdown:
'%s'",
+ CMGetCharPtr(s.msg));
+ goto err;
+ }
+ } else {
+ CU_DEBUG("Unrecognized state '%d'",
job->dom_state);
+ goto err;
+ }
+
+ job->status = CIM_JOB_STATE_RUNNING;
+ CMSetProperty(job->inst, "JobState",
+ (CMPIValue *) &(job->status), CMPI_uint16);
+ CMSetProperty(job->inst, "Status",
+ (CMPIValue *) "Running", CMPI_chars);
+
+ CU_DEBUG("Entering mainloop implementation");
+ /* Wait for operation (shutdown/reboot) to complete */
+ while (job->status == CIM_JOB_STATE_RUNNING) {
+ if (virEventRunDefaultImpl() < 0) {
+ virErrorPtr err = virGetLastError();
+ CU_DEBUG("Failed to run event loop: %s\n",
+ err && err->message ? err->message : "Unknown
error");
+ goto err;
+ }
+ }
+
+ CU_DEBUG("Job completed");
+ CMSetProperty(job->inst, "JobState",
+ (CMPIValue *) &(job->status), CMPI_uint16);
+ CMSetProperty(job->inst, "Status",
+ (CMPIValue *) "Completed", CMPI_chars);
+
+ goto out;
+
+ err:
+ job->status = CIM_JOB_STATE_EXCEPTION;
+ CMSetProperty(job->inst, "JobState",
+ (CMPIValue *) &(job->status), CMPI_uint16);
+ CMSetProperty(job->inst, "Status",
+ (CMPIValue *) "Error", CMPI_chars);
+
+ out:
+ if (job_cb != -1)
+ virConnectDomainEventDeregisterAny(conn, job_cb);
+
+ virDomainFree(dom);
+ virConnectClose(conn);
+
+ CBDetachThread(_BROKER, job->context);
+ free(job->dom_name);
+ free(job);
+ return NULL;
+}
+
static CMPIStatus state_change(CMPIMethodMI *self,
const CMPIContext *context,
const CMPIResult *results,
@@ -1244,7 +1521,8 @@ static CMPIStatus state_change(CMPIMethodMI *self,
goto out;
}
- /* Retain original instance of the guest to use for the PreviousInstance
attribute when generating an indication. */
+ /* Retain original instance of the guest to use for the PreviousInstance
+ attribute when generating an indication. */
s = get_domain_by_name(_BROKER, reference, name, &prev_inst);
if (s.rc != CMPI_RC_OK || prev_inst == NULL) {
cu_statusf(_BROKER, &s,
@@ -1256,8 +1534,36 @@ static CMPIStatus state_change(CMPIMethodMI *self,
s = __state_change(name, state, reference);
+ rc = s.rc;
+
if (s.rc == CMPI_RC_OK)
- rc = 0;
+ goto out;
+
+ if (s.rc == CIM_SVPC_RETURN_JOB_STARTED) {
+ state_change_job_t *job = NULL;
+
+ if (events_registered == false) {
+ events_registered = true;
+ if (virEventRegisterDefaultImpl() != 0) {
+ CU_DEBUG("virEventRegisterDefaultImpl()
failed");
+ goto out;
+ }
+ }
+
+ s = create_state_change_job(reference, context, &job, state);
+ if (s.rc != CMPI_RC_OK) {
+ rc = s.rc;
+ free(job);
+ goto out;
+ }
+
+ job->dom_name = strdup(name);
+
+ _BROKER->xft->newThread(state_change_thread, job, 0);
+
+ CMAddArg(argsout, "Job", (CMPIValue
*)&(job->obj_path),
+ CMPI_ref);
+ }
out:
CMReturnData(results, &rc, CMPI_uint32);
Okay, fairly complex but that's DMTF style :-)
What i don't see is how/where the object associated to the job are freed
is there some generic mechanism handling all of this, or are we missing
some kind of routine which would be called asynchronously to free the
data associated to the job ?
I don't know, if you're sure that everything is deallocated then fine,
ACK :-)
Daniel
--
Daniel Veillard | libxml Gnome XML XSLT toolkit
http://xmlsoft.org/
daniel(a)veillard.com | Rpmfind RPM search engine
http://rpmfind.net/
http://veillard.com/ | virtualization library
http://libvirt.org/