[Libvirt-cim] [PATCH 2/2] ComputerSystem: Reboot/Shutdown state changes as jobs

Eduardo Lima (Etrunko) eblima at linux.vnet.ibm.com
Mon Feb 20 19:43:21 UTC 2012


From: "Eduardo Lima (Etrunko)" <eblima at br.ibm.com>

For Reboot and Shutdown, th RequestStateChange method returns immediately with
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 at br.ibm.com>
---
 schema/ComputerSystem.mof |    9 ++
 src/Virt_ComputerSystem.c |  300 +++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 299 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..dee4ef7 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;
+        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,248 @@ 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_STARTING;
+
+        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 *) "Starting", 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);
+
+ out:
+        free(type);
+        free(cn);
+        free(ns);
+        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;
+}
+
+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;
+}
+
+static CMPI_THREAD_RETURN state_change_thread(void *data)
+{
+        CMPIStatus s;
+        CMPIInstance *inst = NULL;
+        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_RUNNING;
+
+        inst = CBGetInstance(_BROKER, job->context, job->obj_path, NULL, &s);
+        if ((inst == NULL) || (s.rc != CMPI_RC_OK)) {
+                CU_DEBUG("Failed to get job instance (%i)", s.rc);
+                return NULL;
+        }
+
+        CMSetProperty(inst, "JobState",
+                      (CMPIValue *)&(job->status), CMPI_uint16);
+        CMSetProperty(inst, "Status",
+                      (CMPIValue *) "Running", 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 out;
+        }
+
+        dom = virDomainLookupByName(conn, job->dom_name);
+        if (dom == NULL) {
+                CU_DEBUG("Unable to get domain '%s'", job->dom_name);
+                goto out;
+        }
+
+        if (virDomainGetInfo(dom, &info) != 0) {
+                CU_DEBUG("Unable to get domain info for '%s'", job->dom_name);
+                goto out;
+        }
+
+        if (events_registered == false) {
+                events_registered = true;
+                virEventRegisterDefaultImpl();
+        }
+
+        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 out;
+                }
+
+                s = state_change_reboot(dom, &info);
+
+                if (s.rc != CMPI_RC_OK) {
+                        CU_DEBUG("Unable to trigger domain reboot: '%s'",
+                                 CMGetCharPtr(s.msg));
+                        goto out;
+                }
+        } else if (job->dom_state == CIM_STATE_SHUTDOWN) {
+                job_cb = virConnectDomainEventRegisterAny(conn, NULL,
+                                VIR_DOMAIN_EVENT_ID_REBOOT,
+                                VIR_DOMAIN_EVENT_CALLBACK(state_change_shutdown_cb),
+                                job, NULL);
+
+                if (job_cb == -1) {
+                        CU_DEBUG("Unable to connect domain shutdown callback");
+                        goto out;
+                }
+
+                s = state_change_shutdown(dom, &info);
+
+                if (s.rc != CMPI_RC_OK) {
+                        CU_DEBUG("Unable to trigger domain shutdown: '%s'",
+                                 CMGetCharPtr(s.msg));
+                        goto out;
+                }
+        } else {
+                CU_DEBUG("Unrecognized state '%d'", job->dom_state);
+                goto out;
+        }
+
+        /* 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");
+                }
+        }
+
+        CU_DEBUG("Job completed");
+
+        /* Set job state */
+        if (job->status == CIM_JOB_STATE_COMPLETED) {
+                CMSetProperty(inst, "JobState",
+                              (CMPIValue *)&(job->status), CMPI_uint16);
+                CMSetProperty(inst, "Status",
+                              (CMPIValue *) "Completed", CMPI_chars);
+        }
+
+        virConnectDomainEventDeregisterAny(conn, job_cb);
+
+ out:
+        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 +1504,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 +1517,27 @@ static CMPIStatus state_change(CMPIMethodMI *self,
 
         s = __state_change(name, state, reference);
 
-        if (s.rc == CMPI_RC_OK)
+        if (s.rc == CMPI_RC_OK) {
                 rc = 0;
+                goto out;
+        }
+
+        if (s.rc == CIM_SVPC_RETURN_JOB_STARTED) {
+                state_change_job_t *job = NULL;
+                s = create_state_change_job(reference, context, &job, state);
+                if (s.rc != CMPI_RC_OK) {
+                        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);
+                rc = s.rc;
+        }
 
  out:
         CMReturnData(results, &rc, CMPI_uint32);
-- 
1.7.7.6




More information about the Libvirt-cim mailing list