/*
 * 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);
}
Exemple #2
0
/**
 * 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;
}