/* * Async job worker that gets called on a separate thread (see Xen_Job.c) */ static void migrate_task(void *async_job) { /* Perform the migration */ Xen_job *job = (Xen_job *)async_job; xen_utils_session *session = job->session; int state = JobState_Exception; CMPIrc statusrc = CMPI_RC_ERR_FAILED; char *job_status_description = "ERROR: Migrate VM failed with unknown error"; char *xen_error = NULL; migrate_job_context *job_context = (migrate_job_context *)job->job_context; bool kvpNeedsReenabling=false; bool error=false; char *uuid=NULL; xen_string_string_map *other_config=NULL; _SBLIM_TRACE(_SBLIM_TRACE_LEVEL_INFO, ("Migration job started")); state = JobState_Running; job_change_state(job, session, state, 0, 0, STATE_MIGRATE_STARTED); sleep(1); if(!error && !xen_vm_get_uuid(session->xen, &uuid, job_context->vm)) { _SBLIM_TRACE(_SBLIM_TRACE_LEVEL_ERROR, ("Could not get the UUID of the vm.")); error=true; } if(!error && !xen_vm_get_other_config(session->xen, &other_config, job_context->vm)) { _SBLIM_TRACE(_SBLIM_TRACE_LEVEL_ERROR, ("Could not get other_config of the VM")); error=true; } if(!error && xen_utils_get_from_string_string_map(other_config, "kvp_enabled")) { _SBLIM_TRACE(_SBLIM_TRACE_LEVEL_INFO, ("KVP is enabled, disabling it to allow migration")); kvpNeedsReenabling=true; if(xen_utils_preparemigration_kvp_channel(session, uuid) != Xen_KVP_RC_OK) { error=true; _SBLIM_TRACE(_SBLIM_TRACE_LEVEL_ERROR, ("Could not prepare KVP for migration")); } } if(!error) { _SBLIM_TRACE(_SBLIM_TRACE_LEVEL_INFO, ("Doing the migration")); xen_string_string_map *options = xen_string_string_map_alloc(0); if(!xen_vm_pool_migrate(session->xen, job_context->vm, job_context->host, options)) { _SBLIM_TRACE(_SBLIM_TRACE_LEVEL_ERROR, ("Could not do the migration")); error=true; } xen_string_string_map_free(options); } if(!error) { state = JobState_Completed; statusrc = CMPI_RC_OK; job_status_description = "Completed Successfully"; } else { state = JobState_Exception; statusrc = CMPI_RC_ERR_FAILED; xen_error = xen_utils_get_xen_error(session->xen); job_status_description = xen_error; } // The reenabling of KVM is always done, it is not depending on whether the migration was succesfull / an error occured if(kvpNeedsReenabling) { _SBLIM_TRACE(_SBLIM_TRACE_LEVEL_INFO, ("Reenabling KVP")); if (xen_utils_finishmigration_kvp_channel(session, uuid) != Xen_KVP_RC_OK) { _SBLIM_TRACE(_SBLIM_TRACE_LEVEL_ERROR, ("Could not reenable KVP")); state = JobState_Exception; statusrc = CMPI_RC_ERR_FAILED; job_status_description = "Error: Could not re-enable KVP channel"; } } _SBLIM_TRACE(_SBLIM_TRACE_LEVEL_INFO, ("Migration job status %d (%s)", statusrc, job_status_description)); job_change_state(job, session, state, 100, statusrc, job_status_description); if(job_context->vm) xen_vm_free(job_context->vm); if(job_context->host) xen_host_free(job_context->host); free(job_context); if(xen_error) free(xen_error); if(uuid) free(uuid); if(other_config) free(other_config); }
/** * Creation of a new VM by cloning from an existing template (looked up by name) */ static xen_vm create_new_vm(xen_session *session, char* template_name, char* sr_name, bool PV) { /* * Lookup template by name */ xen_vm_set *vms; if (!xen_vm_get_by_name_label(session, &vms, template_name) || vms->size < 1) { fprintf(stderr, "VM lookup failed.\n"); print_error(session); return NULL; } /* * Create VM by cloning from template */ xen_vm vm; char *name_before = "NewVM <&>"; //using xml sensitive characters int name_length = 9; xen_vm_clone(session, &vm, vms->contents[0], name_before); char *name_after; xen_vm_get_name_label(session, &name_after, vm); int result = strncmp(name_before, name_after, name_length); if (result != 0){ fprintf(stderr, "Error: The VM name failed to be encoded/decoded correctly\n"); fprintf(stderr, "Before:%s\nAfter:%s\n", name_before, name_after); return NULL; } xen_vm_set_free(vms); if (!session->ok) { fprintf(stderr, "VM clone failed.\n"); print_error(session); return NULL; } if (PV) { xen_string_string_map *other_config; if (!xen_vm_get_other_config(session, &other_config, vm)) { fprintf(stderr, "VM get other_config failed.\n"); print_error(session); return NULL; } char *disks = NULL; for (size_t i=0; i < other_config->size; i++) { printf("%s -> %s.\n", other_config->contents[i].key, other_config->contents[i].val); if (strcmp(other_config->contents[i].key, "disks") == 0) { disks = other_config->contents[i].val; break; } } if (disks == NULL) { fprintf(stderr, "Did not find provision XML in other_config.\n"); xen_string_string_map_free(other_config); return NULL; } xen_sr_set *srs; if (!xen_sr_get_by_name_label(session, &srs, sr_name) || srs->size < 1) { fprintf(stderr, "SR lookup failed.\n"); print_error(session); xen_vm_free(vm); return NULL; } xen_sr sr = srs->contents[0]; char *sr_uuid; if(!xen_sr_get_uuid(session, &sr_uuid, sr)){ //TODO free...? return NULL; } char *new_str; if(asprintf(&new_str, "sr=\"%s\"", sr_uuid) < 0) { return NULL; } char *new_disks = replace_str(disks, "sr=\"\"", new_str); free(new_str); xen_string_string_map_free(other_config); if (new_disks == NULL) { fprintf(stderr, "Error replacing SR in provision XML.\n"); return NULL; } fprintf(stdout, "New provisions XML: %s\n", new_disks); if (!xen_vm_remove_from_other_config(session, vm, "disks")) { fprintf(stderr, "Error removing old value from other_config.\n"); print_error(session); free(new_disks); return NULL; } if (!xen_vm_add_to_other_config(session, vm, "disks", new_disks)) { fprintf(stderr, "Error adding new value to other_config.\n"); print_error(session); free(new_disks); return NULL; } free(new_disks); } xen_vm_set_name_description(session, vm, "An example VM created via C bindings"); if (!session->ok) { fprintf(stderr, "Failed to set VM description.\n"); print_error(session); return NULL; } xen_vm_provision(session, vm); if (!session->ok) { fprintf(stderr, "Failed to provision VM.\n"); print_error(session); return NULL; } if (PV) return vm; /* * Create a new disk for the new VM. */ printf("Creating new (blank) disk image in 'Shared SR'\n"); xen_sr_set *srs; if (!xen_sr_get_by_name_label(session, &srs, sr_name) || srs->size < 1) { fprintf(stderr, "SR lookup failed.\n"); print_error(session); xen_vm_free(vm); return NULL; } xen_sr_record_opt sr_record = { .u.handle = srs->contents[0] }; xen_string_string_map* other_config = xen_string_string_map_alloc(0); xen_vdi_record vdi0_record = { .name_label = "MyRootFS", .name_description = "MyRootFS description", .sr = &sr_record, .virtual_size = (1024 * 1024 * 1024), /* 1 GiB in bytes */ .type = XEN_VDI_TYPE_SYSTEM, .sharable = false, .read_only = false, .other_config = other_config }; xen_vdi vdi0; if (!xen_vdi_create(session, &vdi0, &vdi0_record)) { fprintf(stderr, "VDI creation failed.\n"); print_error(session); xen_sr_set_free(srs); xen_vm_free(vm); return NULL; } xen_vm_record_opt vm_record_opt = { .u.handle = vm }; xen_vdi_record_opt vdi0_record_opt = { .u.handle = vdi0 }; xen_string_string_map* qos_algorithm_params = xen_string_string_map_alloc(0); xen_string_string_map* vbd_other_config = xen_string_string_map_alloc(0); enum xen_vbd_type vbd_type_disk = xen_vbd_type_from_string(session, "Disk"); printf("Attaching disk image to newly created VM\n"); xen_vbd_record vbd0_record = { .vm = &vm_record_opt, .vdi = &vdi0_record_opt, .userdevice = "xvda", .type = vbd_type_disk, .mode = XEN_VBD_MODE_RW, .bootable = 1, .qos_algorithm_params = qos_algorithm_params, .other_config = vbd_other_config }; xen_vbd vbd0; if (!xen_vbd_create(session, &vbd0, &vbd0_record)) { fprintf(stderr, "VBD creation failed.\n"); print_error(session); xen_vdi_free(vdi0); xen_sr_set_free(srs); xen_vm_free(vm); return NULL; } char *vm_uuid; char *vdi0_uuid; char *vbd0_uuid; xen_vm_get_uuid(session, &vm_uuid, vm); xen_vdi_get_uuid(session, &vdi0_uuid, vdi0); xen_vbd_get_uuid(session, &vbd0_uuid, vbd0); if (!session->ok) { fprintf(stderr, "get_uuid call failed.\n"); print_error(session); xen_uuid_free(vm_uuid); xen_uuid_free(vdi0_uuid); xen_uuid_free(vbd0_uuid); xen_vbd_free(vbd0); xen_vdi_free(vdi0); xen_sr_set_free(srs); xen_vm_free(vm); return NULL; } fprintf(stderr, "Created a new VM, with UUID %s, VDI UUID %s, VBD " "UUID %s.\n", vm_uuid, vdi0_uuid, vbd0_uuid); xen_uuid_free(vm_uuid); xen_uuid_free(vdi0_uuid); xen_uuid_free(vbd0_uuid); xen_vbd_free(vbd0); xen_vdi_free(vdi0); xen_sr_set_free(srs); return vm; } /** * Print the power state for the given VM. */ static void print_vm_power_state(xen_session *session, xen_vm vm) { char *vm_uuid; enum xen_vm_power_state power_state; if (!xen_vm_get_uuid(session, &vm_uuid, vm)) { print_error(session); return; } if (!xen_vm_get_power_state(session, &power_state, vm)) { xen_uuid_free(vm_uuid); print_error(session); return; } printf("VM %s power state is %s.\n", vm_uuid, xen_vm_power_state_to_string(power_state)); xen_uuid_free(vm_uuid); } /* * Replace all occurrences of orig in str with rep * * @returns newly malloc'd string - you must free it */ static char *replace_str(char *str, char *orig, char *rep) { int occurrences = 0; int i = 0, k = 0; char *p = str; while ((p = strstr(p, orig)) != NULL) { ++occurrences; p += strlen(orig); } char *buffer = malloc(strlen(str) + 1 - (occurrences * (strlen(orig) - strlen(rep)))); if(buffer == NULL) return NULL; p = str; while ((p = strstr(p, orig)) != NULL) { int j = p - str - k; strncpy(buffer + i, str + k, j); i += j; strcpy(buffer + i, rep); i += strlen(rep); p += strlen(orig); k += j + strlen(orig); } strncpy(buffer + i, str + k, strlen(str + k)); buffer[i + strlen(str + k)] = '\0'; return buffer; }
/****************************************************************************** * checks for migratability or actually does the migration ******************************************************************************/ int MigrateVirtualSystem( const CMPIBroker *broker, /* in - CMPI Broker that does most of the work */ const CMPIContext *context, /* in - CMPI context for the caller */ const CMPIArgs *argsin, /* in - All the arguments for the method */ const CMPIArgs *argsout, /* out - All the output arguments for the method */ xen_utils_session *session, /* in - Session for making xen calls */ bool host_ip, /* in - The host parameter is an IP address */ bool migrate_check_only, /* in -Check if migration is possible only, dont actually migrate */ CMPIStatus *status) /* out - Report CMPI status of method */ { char *hostid = NULL, *vm_uuid = NULL; CMPIData argdata; xen_vm vm = NULL; xen_host_set *host_set = NULL; CMPIInstance *msd = NULL; xen_host host = NULL; int rc = Xen_VirtualSystemMigrationService_MigrateVirtualSystemToSystem_Invalid_Parameter; CMPIrc statusrc = CMPI_RC_ERR_INVALID_PARAMETER; char *error_msg = "ERROR: Unknown error"; xen_string_string_map *other_config=NULL; /* For now only Live migrations are supported */ if(_GetArgument(broker, argsin, "MigrationSettingData", CMPI_chars, &argdata, status)) { /* Argument passed in as a MOF string, parse it */ msd = xen_utils_parse_embedded_instance(broker, CMGetCharPtr(argdata.value.string)); if (msd == NULL) { // parser returns zero for success, non-zero for error error_msg = "ERROR: Couldnt parse the 'MigrationSettingData' parameter"; goto Exit; } } else /* Argument could have been passed in as an intance */ if(_GetArgument(broker, argsin, "MigrationSettingData", CMPI_instance, &argdata, status)) msd = argdata.value.inst; if(msd != NULL) { CMPIData data = CMGetProperty(msd, "MigrationType", status); if(data.value.uint16 != Xen_VirtualSystemMigrationSettingData_MigrationType_Live) { error_msg = "ERROR: 'MigrationSettingData' contains an invalid MigrationType (Live expected)"; goto Exit; } } /* Host to migrate to */ if(!host_ip) { /* required parameters */ if(!_GetArgument(broker, argsin, "DestinationSystem", CMPI_ref, &argdata, status)){ error_msg = "ERROR: 'DestionationSystem' parameter is missing"; goto Exit; } else { /* This is the CIM reference to an existing Host object */ CMPIData key; key = CMGetKey(argdata.value.ref, "Name", status); if(status->rc != CMPI_RC_OK || CMIsNullValue(key)) { error_msg = "ERROR: 'DestinationSystem' is missing the required 'Name' key"; goto Exit; } hostid = CMGetCharPtr(key.value.string); if(!xen_host_get_by_uuid(session->xen, &host, hostid)) goto Exit; } } else { if(!_GetArgument(broker, argsin, "DestinationHost", CMPI_string, &argdata, status)) { error_msg = "ERROR: 'DestinationHost' parameter is missing"; goto Exit; } else { /* Determing Xen host based on IP address,. Cannot use inet_pton() and so on since DNS may not have been configured properly */ hostid = CMGetCharPtr(argdata.value.string); _SBLIM_TRACE(_SBLIM_TRACE_LEVEL_INFO, ("Trying to migrate to DestinationHost : %s", hostid)); if(!xen_host_get_all(session->xen, &host_set)) goto Exit; int i=0; for (i=0; i<host_set->size; i++) { xen_host_record *host_rec = NULL; if(!xen_host_get_record(session->xen, &host_rec, host_set->contents[i])) goto Exit; /* DestinationHost could be an IP address or the hostname */ if((host_rec->address && (strcmp(hostid, host_rec->address) == 0)) || (host_rec->hostname && (strcmp(hostid, host_rec->hostname) == 0)) || (host_rec->name_label && (strcmp(hostid, host_rec->name_label) == 0))) { xen_host_record_free(host_rec); host = host_set->contents[i]; host_set->contents[i] = NULL; /* dont free this one */ break; } xen_host_record_free(host_rec); } } } /* VM to migrate - required parameter */ if(!_GetArgument(broker, argsin, "ComputerSystem", CMPI_ref, &argdata, status)) { if(!_GetArgument(broker, argsin, "ComputerSystem", CMPI_string, &argdata, status)) { error_msg = "ERROR: Missing the required 'ComputerSystem' parameter"; goto Exit; } else vm_uuid = CMGetCharPtr(argdata.value.string); } else { argdata = CMGetKey(argdata.value.ref, "Name", status); if(status->rc != CMPI_RC_OK || CMIsNullValue(argdata)) { error_msg = "ERROR: ComputerSystem is missing the required 'Name' key"; goto Exit; } vm_uuid = CMGetCharPtr(argdata.value.string); } status->rc = CMPI_RC_ERR_FAILED; rc = Xen_VirtualSystemMigrationService_MigrateVirtualSystemToSystem_Failed; if(xen_vm_get_by_uuid(session->xen, &vm, vm_uuid)) { _SBLIM_TRACE(_SBLIM_TRACE_LEVEL_ERROR, ("Migrating %s to %s", vm_uuid, hostid)); if(migrate_check_only) { /* Check to see if migration is possible */ statusrc = CMPI_RC_OK; rc = Xen_VirtualSystemMigrationService_MigrateVirtualSystemToSystem_Completed_with_No_Error; bool migratable = xen_vm_assert_can_boot_here(session->xen, vm, host); if(migratable == false || !session->xen->ok) { migratable = false; // Workaround for kvp migration if(session->xen->error_description_count==1) { if(xen_vm_get_other_config(session->xen, &other_config, vm)) { if(xen_utils_get_from_string_string_map(other_config, "kvp_enabled")) { _SBLIM_TRACE(_SBLIM_TRACE_LEVEL_INFO, ("Overwriting migratable to mark kvp vm as migratable, although its network")); migratable=true; RESET_XEN_ERROR(session->xen); } free(other_config); } else { _SBLIM_TRACE(_SBLIM_TRACE_LEVEL_ERROR, ("Could not get other config.")); } } if(migratable == false && session->xen->error_description) { /* This is not part of the MOF (and is not documented), but nice to have */ char *xen_error = xen_utils_get_xen_error(session->xen); CMAddArg(argsout, "Reason", (CMPIValue *)xen_error, CMPI_chars); free(xen_error); } } CMAddArg(argsout, "IsMigratable", (CMPIValue *)&migratable, CMPI_boolean); } else { /* Do the actual migration, this could take a few minutes */ CMPIObjectPath* job_instance_op = NULL; migrate_job_context *job_context = calloc(1, sizeof(migrate_job_context)); if(!job_context) { error_msg = "ERROR: Couldn't allocate memory for the migrate job."; goto Exit; } job_context->vm = vm; job_context->host = host; if(!job_create(broker, context, session, MIGRATE_VM_TASK_NAME, vm_uuid, migrate_task, job_context, &job_instance_op, status)) { error_msg = "ERROR: Couldn't prepare the Migrate job. Job wasnt started."; goto Exit; } statusrc = CMPI_RC_OK; rc = Xen_VirtualSystemMigrationService_MigrateVirtualSystemToHost_Method_Parameters_Checked___Job_Started; CMAddArg(argsout, "Job", (CMPIValue *)&job_instance_op, CMPI_ref); vm = NULL; /* freed by async thread */ host = NULL; /* freed by async thread */ } } Exit: if(host) xen_host_free(host); if(vm) xen_vm_free(vm); if(host_set) xen_host_set_free(host_set); xen_utils_set_status(broker, status, statusrc, error_msg, session->xen); return rc; }