
# HG changeset patch # User Dan Smith <danms@us.ibm.com> # Date 1204039738 28800 # Node ID c173208c9c4c70d4ca736f31a19f278268e73971 # Parent bb0530f50ea8d4a75ec34cd54f3bc2daebbda556 Add VirtualSystemSnapshotService Signed-off-by: Dan Smith <danms@us.ibm.com> diff -r bb0530f50ea8 -r c173208c9c4c src/Virt_VirtualSystemSnapshotService.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Virt_VirtualSystemSnapshotService.c Tue Feb 26 07:28:58 2008 -0800 @@ -0,0 +1,508 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdbool.h> + +#include <uuid/uuid.h> + +#include <cmpidt.h> +#include <cmpift.h> +#include <cmpimacs.h> + +#include <libcmpiutil/libcmpiutil.h> +#include <libcmpiutil/std_invokemethod.h> + +#include "misc_util.h" + +#define CIM_VSSS_SNAPSHOT_FULL 2 +#define CIM_VSSS_SNAPSHOT_DISK 3 + +/* VIR_VSSS_SNAPSHOT_MEM - Attempt to save/restore to create a running snap + * VIR_VSSS_SNAPSHOT_MEMT - Just save and let the domain be "off" + */ +#define VIR_VSSS_SNAPSHOT_MEM 32768 +#define VIR_VSSS_SNAPSHOT_MEMT 32769 + +#define CIM_JOBSTATE_STARTING 3 +#define CIM_JOBSTATE_RUNNING 4 +#define CIM_JOBSTATE_COMPLETE 7 + +static const CMPIBroker *_BROKER; + +struct snap_context { + CMPIContext *context; + char *domain; + char uuid[33]; + char *save_path; + char *ref_ns; + char *ref_cn; + + bool save; + bool restore; +}; + +static void snap_job_free(struct snap_context *ctx) +{ + free(ctx->domain); + free(ctx->save_path); + free(ctx->ref_ns); + free(ctx->ref_cn); + free(ctx); +} + +static void snap_job_set_status(struct snap_context *ctx, + uint16_t state, + const char *status) +{ + CMPIInstance *inst; + CMPIStatus s; + CMPIObjectPath *op; + + op = CMNewObjectPath(_BROKER, + ctx->ref_ns, + "CIM_ConcreteJob", + &s); + if (s.rc != CMPI_RC_OK) { + CU_DEBUG("Failed to create job path for update"); + return; + } + + CMAddKey(op, "InstanceID", (CMPIValue *)ctx->uuid, CMPI_chars); + + inst = CBGetInstance(_BROKER, ctx->context, op, NULL, &s); + if ((inst == NULL) || (s.rc != CMPI_RC_OK)) { + CU_DEBUG("Failed to get job instance for update of %s", + ctx->uuid); + return; + } + + CMSetProperty(inst, "JobState", + (CMPIValue *)&state, CMPI_uint16); + CMSetProperty(inst, "Status", + (CMPIValue *)status, CMPI_chars); + + s = CBModifyInstance(_BROKER, ctx->context, op, inst, NULL); + if (s.rc != CMPI_RC_OK) { + CU_DEBUG("Failed to update job instance %s: %s", + ctx->uuid, + CMGetCharPtr(s.msg)); + return; + } + + CU_DEBUG("Set %s status to %i:%s", ctx->uuid, state, status); +} + +static void do_snapshot(struct snap_context *ctx, + virConnectPtr conn, + virDomainPtr dom) +{ + int ret; + + if (ctx->save) { + CU_DEBUG("Starting save to %s", ctx->save_path); + + ret = virDomainSave(dom, ctx->save_path); + if (ret == -1) { + CU_DEBUG("Save failed"); + snap_job_set_status(ctx, + CIM_JOBSTATE_COMPLETE, + "Snapshot Failed (save)"); + return; + } + + CU_DEBUG("Save completed"); + snap_job_set_status(ctx, + CIM_JOBSTATE_RUNNING, + "Save finished"); + } + + if (ctx->restore) { + CU_DEBUG("Starting restore from %s", ctx->save_path); + + ret = virDomainRestore(conn, ctx->save_path); + if (ret == -1) { + CU_DEBUG("Restore failed"); + snap_job_set_status(ctx, + CIM_JOBSTATE_COMPLETE, + "Snapshot Failed (restore)"); + return; + } + + CU_DEBUG("Restore completed"); + snap_job_set_status(ctx, + CIM_JOBSTATE_RUNNING, + "Restore finished"); + } + + CU_DEBUG("Snapshot (%s/%s) completed", + ctx->save ? "Save" : "None", + ctx->restore ? "Restore" : "None"); + + snap_job_set_status(ctx, + CIM_JOBSTATE_COMPLETE, + "Snapshot complete"); + + return; +} + +static CMPI_THREAD_RETURN snapshot_thread(struct snap_context *ctx) +{ + CMPIStatus s; + virConnectPtr conn = NULL; + virDomainPtr dom = NULL; + + CU_DEBUG("Snapshot thread alive"); + + CBAttachThread(_BROKER, ctx->context); + + snap_job_set_status(ctx, CIM_JOBSTATE_RUNNING, "Running"); + + conn = connect_by_classname(_BROKER, ctx->ref_cn, &s); + if (conn == NULL) { + CU_DEBUG("Failed to connect with classname `%s'", ctx->ref_cn); + snap_job_set_status(ctx, + CIM_JOBSTATE_COMPLETE, + "Unable to connect to hypervisor"); + goto out; + } + + dom = virDomainLookupByName(conn, ctx->domain); + if (dom == NULL) { + CU_DEBUG("No such domain `%s'", ctx->domain); + snap_job_set_status(ctx, + CIM_JOBSTATE_COMPLETE, + "No such domain"); + goto out; + } + + do_snapshot(ctx, conn, dom); + + out: + virDomainFree(dom); + virConnectClose(conn); + + snap_job_free(ctx); + + return NULL; +} + +static CMPIStatus create_job(const CMPIContext *context, + const CMPIObjectPath *ref, + struct snap_context *ctx, + CMPIObjectPath **job) +{ + CMPIObjectPath *op; + CMPIInstance *inst; + CMPIStatus s; + + op = CMNewObjectPath(_BROKER, + NAMESPACE(ref), + "CIM_ConcreteJob", /*FIXME*/ + &s); + if ((s.rc != CMPI_RC_OK) || (op == NULL)) { + CU_DEBUG("Failed to create job path"); + goto out; + } + + CMSetNameSpace(op, NAMESPACE(ref)); + + inst = CMNewInstance(_BROKER, op, &s); + if ((s.rc != CMPI_RC_OK) || (inst == NULL)) { + CU_DEBUG("Failed to create job instance"); + goto out; + } + + CMSetProperty(inst, "InstanceID", + (CMPIValue *)ctx->uuid, CMPI_chars); + + CMSetProperty(inst, "Name", + (CMPIValue *)"Snapshot", CMPI_chars); + + CMSetProperty(inst, "Status", + (CMPIValue *)"Queued", CMPI_chars); + + op = CMGetObjectPath(inst, &s); + if ((op == NULL) || (s.rc != CMPI_RC_OK)) { + CU_DEBUG("Failed to get path of job instance"); + goto out; + } + + CMSetNameSpace(op, NAMESPACE(ref)); + + CU_DEBUG("ref was %s", CMGetCharPtr(CMObjectPathToString(op, NULL))); + + *job = CBCreateInstance(_BROKER, context, op, inst, &s); + if ((*job == NULL) || (s.rc != CMPI_RC_OK)) { + CU_DEBUG("Failed to create job"); + goto out; + } + + ctx->ref_ns = strdup(NAMESPACE(ref)); + ctx->ref_cn = strdup(CLASSNAME(ref)); + + ctx->context = CBPrepareAttachThread(_BROKER, context); + + _BROKER->xft->newThread((void *)snapshot_thread, ctx, 0); + + cu_statusf(_BROKER, &s, + CMPI_RC_OK, + ""); + out: + return s; +} + +static char *get_save_path(const char *domname) +{ + int ret; + char *path = NULL; + + ret = asprintf(&path, + "/var/lib/libvirt/%s.save", domname); + if (ret == -1) + return NULL; + + return path; +} + +static struct snap_context *new_context(const char *name, + CMPIStatus *s) +{ + struct snap_context *ctx; + uuid_t uuid; + + ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) { + CU_DEBUG("Failed to alloc snapshot context"); + goto out; + } + + ctx->domain = strdup(name); + + uuid_generate(uuid); + uuid_unparse(uuid, ctx->uuid); + + ctx->save_path = get_save_path(ctx->domain); + if (ctx->save_path == NULL) { + cu_statusf(_BROKER, s, + CMPI_RC_ERR_FAILED, + "Unable to get save_path"); + goto out; + } + + cu_statusf(_BROKER, s, + CMPI_RC_OK, + ""); + out: + if (s->rc != CMPI_RC_OK) { + snap_job_free(ctx); + ctx = NULL; + } + + return ctx; +} + +static CMPIStatus start_snapshot_job(const CMPIObjectPath *ref, + const CMPIContext *context, + const char *name, + uint16_t type) +{ + struct snap_context *ctx; + CMPIStatus s; + CMPIObjectPath *job; + + ctx = new_context(name, &s); + if (ctx == NULL) + goto out; + + ctx->save = (type != 0); + ctx->restore = (type != VIR_VSSS_SNAPSHOT_MEM); + + s = create_job(context, ref, ctx, &job); + + out: + return s; +} + +static CMPIStatus create_snapshot(CMPIMethodMI *self, + const CMPIContext *context, + const CMPIResult *results, + const CMPIObjectPath *reference, + const CMPIArgs *argsin, + CMPIArgs *argsout) +{ + CMPIStatus s = {CMPI_RC_OK, NULL}; + CMPIObjectPath *system; + CMPIInstance *sd; + uint16_t type; + uint32_t retcode = 0; + const char *name; + + if (cu_get_u16_arg(argsin, "SnapshotType", &type) != CMPI_RC_OK) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_INVALID_PARAMETER, + "Missing SnapshotType"); + goto out; + } + + if ((type != VIR_VSSS_SNAPSHOT_MEM) && + (type != VIR_VSSS_SNAPSHOT_MEMT)) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_NOT_SUPPORTED, + "Only memory(%i,%i) snapshots are supported", + VIR_VSSS_SNAPSHOT_MEM, + VIR_VSSS_SNAPSHOT_MEMT); + goto out; + } + + if (cu_get_ref_arg(argsin, "AffectedSystem", &system) != CMPI_RC_OK) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_INVALID_PARAMETER, + "Missing AffectedSystem"); + goto out; + } + + if (cu_get_inst_arg(argsin, "SnapshotSettings", &sd) != CMPI_RC_OK) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_INVALID_PARAMETER, + "Missing SnapshotSettings"); + goto out; + } + + if (cu_get_str_path(system, "Name", &name) != CMPI_RC_OK) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_INVALID_PARAMETER, + "Missing Name property of AffectedSystem"); + goto out; + } + + s = start_snapshot_job(reference, context, name, type); + + CMReturnData(results, (CMPIValue *)&retcode, CMPI_uint32); + out: + CU_DEBUG("Returning: %i", s.rc); + return s; +} + +static CMPIStatus destroy_snapshot(CMPIMethodMI *self, + const CMPIContext *context, + const CMPIResult *results, + const CMPIObjectPath *reference, + const CMPIArgs *argsin, + CMPIArgs *argsout) +{ + CMPIStatus s = {CMPI_RC_OK, NULL}; + CMPIObjectPath *snap; + char *name = NULL; + char *path = NULL; + + if (cu_get_ref_arg(argsin, "AffectedSnapshot", &snap) != CMPI_RC_OK) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_INVALID_PARAMETER, + "Missing Snapshot"); + goto out; + } + + if (!parse_instanceid(snap, NULL, &name)) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_FAILED, + "Invalid InstanceID in Snapshot"); + goto out; + } + + path = get_save_path(name); + if (path == NULL) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_FAILED, + "Unable to get save_path"); + goto out; + } + + if (unlink(path) == -1) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_FAILED, + "Unable to remove snapshot: %s", path); + } + out: + free(path); + free(name); + + return s; +} + +static CMPIStatus apply_snapshot(CMPIMethodMI *self, + const CMPIContext *context, + const CMPIResult *results, + const CMPIObjectPath *reference, + const CMPIArgs *argsin, + CMPIArgs *argsout) +{ + CMPIStatus s = {CMPI_RC_OK, NULL}; + CMPIObjectPath *snap; + char *name = NULL; + + if (cu_get_ref_arg(argsin, "AffectedSnapshot", &snap) != CMPI_RC_OK) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_INVALID_PARAMETER, + "Missing Snapshot"); + goto out; + } + + if (!parse_instanceid(snap, NULL, &name)) { + cu_statusf(_BROKER, &s, + CMPI_RC_ERR_FAILED, + "Invalid InstanceID in Snapshot"); + goto out; + } + + s = start_snapshot_job(reference, context, name, 0); + + out: + free(name); + + return s; +} + +static struct method_handler CreateSnapshot = { + .name = "CreateSnapshot", + .handler = create_snapshot, + .args = {{"AffectedSystem", CMPI_ref, false}, + {"SnapshotSettings", CMPI_instance, false}, + {"SnapshotType", CMPI_uint16, false}, + ARG_END} +}; + +static struct method_handler DestroySnapshot = { + .name = "DestroySnapshot", + .handler = destroy_snapshot, + .args = {{"AffectedSnapshot", CMPI_ref, false}, + ARG_END} +}; + +static struct method_handler ApplySnapshot = { + .name = "ApplySnapshot", + .handler = apply_snapshot, + .args = {{"AffectedSnapshot", CMPI_ref, false}, + ARG_END} +}; + +static struct method_handler *handlers[] = { + &CreateSnapshot, + &DestroySnapshot, + &ApplySnapshot, + NULL +}; + +STDIM_MethodMIStub(, Virt_VirtualSystemSnapshotService, + _BROKER, libvirt_cim_init(), handlers); + + +/* + * Local Variables: + * mode: C + * c-set-style: "K&R" + * tab-width: 8 + * c-basic-offset: 8 + * indent-tabs-mode: nil + * End: + */