//!
//! Implement the backing store for a given instance
//!
//! @param[in] instance pointer to the instance
//! @param[in] is_migration_dest
//!
//! @return EUCA_OK on success or EUCA_ERROR on failure
//!
//! @pre The instance parameter must not be NULL.
//!
//! @post
//!
int create_instance_backing(ncInstance * instance, boolean is_migration_dest)
{
    int rc = 0;
    int ret = EUCA_ERROR;
    virtualMachine *vm = &(instance->params);
    artifact *sentinel = NULL;
    char work_prefix[1024] = { 0 };    // {userId}/{instanceId}

    // set various instance-directory-relative paths in the instance struct
    set_instance_paths(instance);

    // ensure instance directory exists
    if (ensure_directories_exist(instance->instancePath, 0, NULL, "root", BACKING_DIRECTORY_PERM) == -1)
        goto out;

    if (strstr(instance->platform, "windows")) {
        // generate the floppy file for windows instances
        if (makeWindowsFloppy(nc_state.home, instance->instancePath, instance->keyName, instance->instanceId)) {
            LOGERROR("[%s] could not create windows bootup script floppy\n", instance->instanceId);
            goto out;
        } else {
            set_path(instance->floppyFilePath, sizeof(instance->floppyFilePath), instance, "floppy");
        }
    } else if (strlen(instance->instancePk) > 0) {  // TODO: credential floppy is limited to Linux instances ATM
        LOGDEBUG("[%s] creating floppy for instance credential\n", instance->instanceId);
        if (make_credential_floppy(nc_state.home, instance)) {
            LOGERROR("[%s] could not create credential floppy\n", instance->instanceId);
            goto out;
        } else {
            set_path(instance->floppyFilePath, sizeof(instance->floppyFilePath), instance, "floppy");
        }
    }

    set_id(instance, NULL, work_prefix, sizeof(work_prefix));

    // if this looks like a partition m1.small image, make it a bootable disk
    virtualMachine *vm2 = NULL;
    LOGDEBUG("vm->virtualBootRecordLen=%d\n", vm->virtualBootRecordLen);
    if (vm->virtualBootRecordLen == 5) {    // TODO: make this check more robust

        // as an experiment, construct a new VBR, without swap and ephemeral
        virtualMachine vm_copy;
        vm2 = &vm_copy;
        memcpy(vm2, vm, sizeof(virtualMachine));
        bzero(vm2->virtualBootRecord, EUCA_MAX_VBRS * sizeof(virtualBootRecord));
        vm2->virtualBootRecordLen = 0;

        virtualBootRecord *emi_vbr = NULL;
        for (int i = 0; i < EUCA_MAX_VBRS && i < vm->virtualBootRecordLen; i++) {
            virtualBootRecord *vbr = &(vm->virtualBootRecord[i]);
            if (vbr->type != NC_RESOURCE_KERNEL && vbr->type != NC_RESOURCE_RAMDISK && vbr->type != NC_RESOURCE_IMAGE)
                continue;
            if (vbr->type == NC_RESOURCE_IMAGE)
                emi_vbr = vbr;
            memcpy(vm2->virtualBootRecord + (vm2->virtualBootRecordLen++), vbr, sizeof(virtualBootRecord));
        }

        if (emi_vbr == NULL) {
            LOGERROR("[%s] failed to find EMI among VBR entries\n", instance->instanceId);
            goto out;
        }

        if (vbr_add_ascii("boot:none:104857600:ext3:sda2:none", vm2) != EUCA_OK) {
            LOGERROR("[%s] could not add a boot partition VBR entry\n", instance->instanceId);
            goto out;
        }
        if (vbr_parse(vm2, NULL) != EUCA_OK) {
            LOGERROR("[%s] could not parse the boot partition VBR entry\n", instance->instanceId);
            goto out;
        }
        // compute tree of dependencies
        sentinel = vbr_alloc_tree(vm2, // the struct containing the VBR
                                  TRUE, // we always make the disk bootable, for consistency
                                  TRUE, // make working copy of runtime-modifiable files
                                  is_migration_dest,    // tree of an instance on the migration destination
                                  (instance->do_inject_key) ? (instance->keyName) : (NULL), // the SSH key
                                  instance->instanceId);    // ID is for logging
        if (sentinel == NULL) {
            LOGERROR("[%s] failed to prepare backing for instance\n", instance->instanceId);
            goto out;
        }

        LOGDEBUG("disk size prior to tree implementation is = %lld\n", sentinel->deps[0]->size_bytes);
        long long right_disk_size = sentinel->deps[0]->size_bytes;

        sem_p(disk_sem);
        {
            // download/create/combine the dependencies
            rc = art_implement_tree(sentinel, work_bs, cache_bs, work_prefix, INSTANCE_PREP_TIMEOUT_USEC);
        }
        sem_v(disk_sem);

        if (rc != EUCA_OK) {
            LOGERROR("[%s] failed to implement backing for instance\n", instance->instanceId);
            goto out;
        }

        LOGDEBUG("[%s] created the initial bootable disk\n", instance->instanceId);

        /* option A starts */
        assert(emi_vbr);
        assert(sentinel->deps[0]);
        strcpy(emi_vbr->guestDeviceName, "sda");    // switch 'sda1' to 'sda' now that we've built the disk
        //emi_vbr->sizeBytes = sentinel->deps[0]->size_bytes; // update the size to match the disk
        emi_vbr->sizeBytes = right_disk_size;   // this is bad...
        LOGDEBUG("at boot disk creation time emi_vbr->sizeBytes = %lld\n", emi_vbr->sizeBytes);
        euca_strncpy(emi_vbr->id, sentinel->deps[0]->id, SMALL_CHAR_BUFFER_SIZE);   // change to the ID of the disk
        if (vbr_parse(vm, NULL) != EUCA_OK) {
            LOGERROR("[%s] could not parse the boot partition VBR entry\n", instance->instanceId);
            goto out;
        }
        emi_vbr->locationType = NC_LOCATION_NONE;   // i.e., it should already exist

        art_free(sentinel);
        /* option A end */

        /* option B starts *
           memcpy(vm, vm2, sizeof(virtualMachine));
           if (save_instance_struct(instance)) // update instance checkpoint now that the struct got updated
           goto out;
           ret = EUCA_OK;
           goto out;
           * option B ends */
    }
    // compute tree of dependencies
    sentinel = vbr_alloc_tree(vm,      // the struct containing the VBR
                              FALSE,   // if image had to be made bootable, that was done above
                              TRUE,    // make working copy of runtime-modifiable files
                              is_migration_dest,    // tree of an instance on the migration destination
                              (instance->do_inject_key) ? (instance->keyName) : (NULL), // the SSH key
                              instance->instanceId);    // ID is for logging
    if (sentinel == NULL) {
        LOGERROR("[%s] failed to prepare extended backing for instance\n", instance->instanceId);
        goto out;
    }

    sem_p(disk_sem);
    {
        // download/create/combine the dependencies
        rc = art_implement_tree(sentinel, work_bs, cache_bs, work_prefix, INSTANCE_PREP_TIMEOUT_USEC);
    }
    sem_v(disk_sem);

    if (rc != EUCA_OK) {
        LOGERROR("[%s] failed to implement backing for instance\n", instance->instanceId);
        goto out;
    }

    if (save_instance_struct(instance)) // update instance checkpoint now that the struct got updated
        goto out;

    ret = EUCA_OK;

out:
    if (sentinel)
        art_free(sentinel);
    return (ret);
}
Exemple #2
0
//!
//! Implement the backing store for a given instance
//!
//! @param[in] instance pointer to the instance
//! @param[in] is_migration_dest
//!
//! @return EUCA_OK on success or EUCA_ERROR on failure
//!
//! @pre The instance parameter must not be NULL.
//!
//! @post
//!
int create_instance_backing(ncInstance * instance, boolean is_migration_dest)
{
    int rc = 0;
    int ret = EUCA_ERROR;
    virtualMachine *vm = &(instance->params);
    artifact *sentinel = NULL;
    char work_prefix[1024] = { 0 };    // {userId}/{instanceId}
    char base_path[EUCA_MAX_PATH];
    char user_dir_path[EUCA_MAX_PATH];

    // set various instance-directory-relative paths in the instance struct
    set_instance_paths(instance);

    set_path(base_path, sizeof(base_path), NULL, NULL);
    snprintf(user_dir_path, sizeof(user_dir_path), "%s/%s", base_path, instance->userId);
    // create backing directory
    if ((check_path(user_dir_path) == 1) && (mkdir(user_dir_path, INSTANCE_DIRECTORY_PERM) == -1)) {
        LOGERROR("[%s] could not create backing directory %s\n", instance->instanceId, user_dir_path);
        goto out;
    }
    if (mkdir(instance->instancePath, INSTANCE_DIRECTORY_PERM) == -1) {
        LOGERROR("[%s] could not create backing directory %s\n", instance->instanceId, instance->instancePath);
        goto out;
    }

    if (strstr(instance->platform, "windows")) {
        // generate the floppy file for windows instances
        if (makeWindowsFloppy(nc_state.home, instance->instancePath, instance->keyName, instance->instanceId)) {
            LOGERROR("[%s] could not create windows bootup script floppy\n", instance->instanceId);
            goto out;
        } else {
            set_path(instance->floppyFilePath, sizeof(instance->floppyFilePath), instance, "floppy");
            instance->hasFloppy = TRUE;
        }
    } else if (instance->credential && strlen(instance->credential)) {
        LOGDEBUG("[%s] creating floppy for instance credential\n", instance->instanceId);
        if (make_credential_floppy(nc_state.home, instance->instancePath, instance->credential)) {
            LOGERROR("[%s] could not create credential floppy\n", instance->instanceId);
            goto out;
        } else {
            set_path(instance->floppyFilePath, sizeof(instance->floppyFilePath), instance, "floppy");
            instance->hasFloppy = TRUE;
        }
    } else if(instance->hasFloppy && is_migration_dest) {
        LOGDEBUG("[%s] creating blank instance credential floppy\n", instance->instanceId);
        char dest_path[1024] = "";
        int fd = 0;
        snprintf(dest_path, 1024, "%s/floppy", instance->instancePath);
        if ((fd = open(dest_path, O_CREAT | O_TRUNC | O_RDWR, 0700)) < 0) {
           LOGERROR("[%s] failed to create fake floppy\n", instance->instanceId);
           goto out;
        } else {
           lseek(fd, 1024*2048-1, SEEK_SET);
           write(fd, "\n", 1);
        }
        close(fd);
    } else {
        instance->hasFloppy = FALSE;
    }

    set_id(instance, NULL, work_prefix, sizeof(work_prefix));

    // compute tree of dependencies
    sentinel = vbr_alloc_tree(vm,      // the struct containing the VBR
                              TRUE,    // make working copy of runtime-modifiable files
                              is_migration_dest,    // tree of an instance on the migration destination
                              (instance->do_inject_key) ? (instance->keyName) : (NULL), // the SSH key
                              &(instance->bail_flag),   // flag indicating that provisioning should bail
                              instance->instanceId);    // ID is for logging
    if (sentinel == NULL) {
        LOGERROR("[%s] failed to prepare extended backing for instance\n", instance->instanceId);
        goto out;
    }

    sem_p(disk_sem);
    {
        // download/create/combine the dependencies
        rc = art_implement_tree(sentinel, work_bs, cache_bs, work_prefix, INSTANCE_PREP_TIMEOUT_USEC);
    }
    sem_v(disk_sem);

    if (rc != EUCA_OK) {
        LOGERROR("[%s] failed to implement backing for instance\n", instance->instanceId);
        goto out;
    }
    // copy EBS entries from VBR[] to volumes[]
    for (int i = 0; ((i < EUCA_MAX_VBRS) && (i < instance->params.virtualBootRecordLen)); i++) {
        virtualBootRecord *vbr = &(instance->params.virtualBootRecord[i]);
        if (vbr->locationType == NC_LOCATION_SC) {
            char *volumeId = vbr->id;  // id is 'emi-XXXX', replace it with 'vol-XXXX'
            ebs_volume_data *vol_data = NULL;
            if (deserialize_volume(vbr->resourceLocation, &vol_data) == 0) {
                volumeId = vol_data->volumeId;
            }
            if (save_volume(instance, volumeId, vbr->resourceLocation,  // attachmentToken
                            vbr->preparedResourceLocation,  // connect_string
                            vbr->guestDeviceName, VOL_STATE_ATTACHED, vbr->backingPath) == NULL) {  // the XML
                LOGERROR("[%s] failed to add record for volume %s\n", instance->instanceId, volumeId);
            }
            EUCA_FREE(vol_data);
        }
    }

    if (save_instance_struct(instance)) // update instance checkpoint now that the struct got updated
        goto out;

    ret = EUCA_OK;

out:
    if (sentinel)
        art_free(sentinel);
    return (ret);
}
//!
//! Loads an instance structure data from the instance.xml file under the instance's
//! work blobstore path.
//!
//! @param[in] instanceId the instance identifier string (i-XXXXXXXX)
//!
//! @return A pointer to the instance structure if successful or otherwise NULL.
//!
//! @pre The instanceId parameter must not be NULL.
//!
//! @post On success, a newly allocated pointer to the instance is returned where the stateCode
//!       is set to NO_STATE.
//!
ncInstance *load_instance_struct(const char *instanceId)
{
    DIR *insts_dir = NULL;
    char tmp_path[EUCA_MAX_PATH] = "";
    char user_paths[EUCA_MAX_PATH] = "";
    char checkpoint_path[EUCA_MAX_PATH] = "";
    ncInstance *instance = NULL;
    struct dirent *dir_entry = NULL;
    struct stat mystat = { 0 };

    // Allocate memory for our instance
    if ((instance = EUCA_ZALLOC(1, sizeof(ncInstance))) == NULL) {
        LOGERROR("out of memory (for instance struct)\n");
        return (NULL);
    }
    // We know the instance indentifier
    euca_strncpy(instance->instanceId, instanceId, sizeof(instance->instanceId));

    // we don't know userId, so we'll look for instanceId in every user's
    // directory (we're assuming that instanceIds are unique in the system)
    set_path(user_paths, sizeof(user_paths), NULL, NULL);
    if ((insts_dir = opendir(user_paths)) == NULL) {
        LOGERROR("failed to open %s\n", user_paths);
        goto free;
    }
    // Scan every path under the user path for one that conaints our instance
    while ((dir_entry = readdir(insts_dir)) != NULL) {
        snprintf(tmp_path, sizeof(tmp_path), "%s/%s/%s", user_paths, dir_entry->d_name, instance->instanceId);
        if (stat(tmp_path, &mystat) == 0) {
            // found it. Now save our user identifier
            euca_strncpy(instance->userId, dir_entry->d_name, sizeof(instance->userId));
            break;
        }
    }

    // Done with the directory
    closedir(insts_dir);
    insts_dir = NULL;

    // Did we really find one?
    if (strlen(instance->userId) < 1) {
        LOGERROR("didn't find instance %s\n", instance->instanceId);
        goto free;
    }
    // set various instance-directory-relative paths in the instance struct
    set_instance_paths(instance);

    // Check if there is a binary checkpoint file, used by versions up to 3.3,
    // and load metadata from it (as part of a "warm" upgrade from 3.3.0 and 3.3.1).
    set_path(checkpoint_path, sizeof(checkpoint_path), instance, "instance.checkpoint");
    set_path(instance->xmlFilePath, sizeof(instance->xmlFilePath), instance, INSTANCE_FILE_NAME);
    if (check_file(checkpoint_path) == 0) {
        ncInstance33 instance33;
        {                              // read in the checkpoint
            int fd = open(checkpoint_path, O_RDONLY);
            if (fd < 0) {
                LOGERROR("failed to load metadata for %s from %s: %s\n", instance->instanceId, checkpoint_path, strerror(errno));
                goto free;
            }

            size_t meta_size = (size_t) sizeof(ncInstance33);
            assert(meta_size <= SSIZE_MAX); // beyond that read() behavior is unspecified
            ssize_t bytes_read = read(fd, &instance33, meta_size);
            close(fd);
            if (bytes_read < meta_size) {
                LOGERROR("metadata checkpoint for %s (%ld bytes) in %s is too small (< %ld)\n", instance->instanceId, bytes_read, checkpoint_path, meta_size);
                goto free;
            }
        }
        // Convert the 3.3 struct into the current struct.
        // Currently, a copy is sufficient, but if ncInstance
        // ever changes so that its beginning differs from ncInstanc33,
        // we may have to write something more elaborate or to break
        // the ability to upgrade from 3.3. We attempt to detect such a
        // change with the following if-statement, which compares offsets
        // in the structs of the last member in the 3.3 version.
        if (((unsigned long)&(instance->last_stat) - (unsigned long)instance)
            != ((unsigned long)&(instance33.last_stat) - (unsigned long)&instance33)) {
            LOGERROR("BUG: upgrade from v3.3 is not possible due to changes to instance struct\n");
            goto free;
        }
        memcpy(instance, &instance33, sizeof(ncInstance33));
        LOGINFO("[%s] upgraded instance checkpoint from v3.3\n", instance->instanceId);
    } else {                           // no binary checkpoint, so we expect an XML-formatted checkpoint
        if (read_instance_xml(instance->xmlFilePath, instance) != EUCA_OK) {
            LOGERROR("failed to read instance XML\n");
            goto free;
        }
    }

    // Reset some fields for safety since they would now be wrong
    instance->stateCode = NO_STATE;
    instance->params.root = NULL;
    instance->params.kernel = NULL;
    instance->params.ramdisk = NULL;
    instance->params.swap = NULL;
    instance->params.ephemeral0 = NULL;

    // fix up the pointers
    vbr_parse(&(instance->params), NULL);

    // perform any upgrade-related manipulations to bring the struct up to date

    // save the struct back to disk after the upgrade routine had a chance to modify it
    if (gen_instance_xml(instance) != EUCA_OK) {
        LOGERROR("failed to create instance XML in %s\n", instance->xmlFilePath);
        goto free;
    }
    // remove the binary checkpoint because it is no longer needed and not used past 3.3
    unlink(checkpoint_path);

    return (instance);

free:
    EUCA_FREE(instance);
    return (NULL);
}
Exemple #4
0
//!
//! Implement the backing store for a given instance
//!
//! @param[in] instance pointer to the instance
//! @param[in] is_migration_dest
//!
//! @return EUCA_OK on success or EUCA_ERROR on failure
//!
//! @pre The instance parameter must not be NULL.
//!
//! @post
//!
int create_instance_backing(ncInstance * instance, boolean is_migration_dest)
{
    int rc = 0;
    int ret = EUCA_ERROR;
    virtualMachine *vm = &(instance->params);
    artifact *sentinel = NULL;
    char work_prefix[1024] = { 0 };    // {userId}/{instanceId}

    // set various instance-directory-relative paths in the instance struct
    set_instance_paths(instance);

    // ensure instance directory exists
    if (ensure_directories_exist(instance->instancePath, 0, NULL, "root", BACKING_DIRECTORY_PERM) == -1)
        goto out;

    if (strstr(instance->platform, "windows")) {
        // generate the floppy file for windows instances
        if (makeWindowsFloppy(nc_state.home, instance->instancePath, instance->keyName, instance->instanceId)) {
            LOGERROR("[%s] could not create windows bootup script floppy\n", instance->instanceId);
            goto out;
        } else {
            set_path(instance->floppyFilePath, sizeof(instance->floppyFilePath), instance, "floppy");
        }
    }else if (instance->instancePk != NULL && strlen(instance->instancePk) > 0) {  // TODO: credential floppy is limited to Linux instances ATM
        LOGDEBUG("[%s] creating floppy for instance credential\n", instance->instanceId);
        if (make_credential_floppy(nc_state.home, instance)) {
            LOGERROR("[%s] could not create credential floppy\n", instance->instanceId);
            goto out;
        } else {
            set_path(instance->floppyFilePath, sizeof(instance->floppyFilePath), instance, "floppy");
        }
    }

    set_id(instance, NULL, work_prefix, sizeof(work_prefix));

    // compute tree of dependencies
    sentinel = vbr_alloc_tree(vm,      // the struct containing the VBR
                              FALSE,   // for Xen and KVM we do not need to make disk bootable
                              TRUE,    // make working copy of runtime-modifiable files
                              is_migration_dest,    // tree of an instance on the migration destination
                              (instance->do_inject_key) ? (instance->keyName) : (NULL), // the SSH key
                              instance->instanceId);    // ID is for logging
    if (sentinel == NULL) {
        LOGERROR("[%s] failed to prepare backing for instance\n", instance->instanceId);
        goto out;
    }

    sem_p(disk_sem);
    {
        // download/create/combine the dependencies
        rc = art_implement_tree(sentinel, work_bs, cache_bs, work_prefix, INSTANCE_PREP_TIMEOUT_USEC);
    }
    sem_v(disk_sem);

    if (rc != EUCA_OK) {
        LOGERROR("[%s] failed to implement backing for instance\n", instance->instanceId);
        goto out;
    }

    if (save_instance_struct(instance)) // update instance checkpoint now that the struct got updated
        goto out;

    ret = EUCA_OK;

out:
    if (sentinel)
        art_free(sentinel);
    return (ret);
}