//! //! //! //! @param[in] key //! @param[in] val //! //! @pre //! //! @post //! static void set_global_parameter(char *key, char *val) { if (strcmp(key, "debug") == 0) { print_debug = parse_boolean(val); set_debug(print_debug); } else if (strcmp(key, "argv") == 0) { print_argv = parse_boolean(val); } else if (strcmp(key, "work") == 0) { set_work_dir(val); } else if (strcmp(key, "work_size") == 0) { set_work_limit(parse_bytes(val)); } else if (strcmp(key, "cache") == 0) { set_cache_dir(val); } else if (strcmp(key, "cache_size") == 0) { set_cache_limit(parse_bytes(val)); } else if (strcmp(key, "purge_cache") == 0) { purge_cache = parse_boolean(val); } else if (strcmp(key, "cloud_cert") == 0) { euca_strncpy(cloud_cert_path, val, sizeof(cloud_cert_path)); } else if (strcmp(key, "service_key") == 0) { euca_strncpy(service_key_path, val, sizeof(service_key_path)); } else { err("unknown global parameter '%s'", key); } LOGINFO("GLOBAL: %s=%s\n", key, val); }
//! //! Sets and opens the log file //! //! @param[in] file the file name of the log file. A NULL value unset the file //! //! @return EUCA_OK on success or EUCA_ERROR on failure //! int log_file_set(const char *file, const char *req_track_file) { if (file == NULL) { // NULL means standard output log_file_path[0] = '\0'; log_file_path_req_track[0] = '\0'; return (EUCA_OK); } if (strcmp(log_file_path, file) == 0) { ; } else { euca_strncpy(log_file_path, file, EUCA_MAX_PATH); if (get_file(log_file_path, TRUE) == NULL) { return (EUCA_ERROR); } release_file(log_file_path); } if (req_track_file == NULL || strlen(req_track_file) == 0) { log_file_path_req_track[0] = '\0'; return (EUCA_OK); } if (strcmp(log_file_path_req_track, req_track_file) == 0) { return (EUCA_OK); } else { euca_strncpy(log_file_path_req_track, req_track_file, EUCA_MAX_PATH); if (get_file(log_file_path_req_track, TRUE) == NULL) { return (EUCA_ERROR); } release_file(log_file_path_req_track); } return (EUCA_OK); }
//! //! Allocate and initialize a resource structure with given information. Resource is //! used to return information about resources //! //! @param[in] sNodeStatus the current node status string //! @param[in] migrationCapable flag indicating whether node can participate in live migration //! @param[in] sIQN //! @param[in] memorySizeMax the maximum amount of memory available on this node //! @param[in] memorySizeAvailable the current amount of memory available on this node //! @param[in] diskSizeMax the maximum amount of disk space available on this node //! @param[in] diskSizeAvailable the current amount of disk space available on this node //! @param[in] numberOfCoresMax the maximum number of cores available on this node //! @param[in] numberOfCoresAvailable the current number of cores available on this node //! @param[in] sPublicSubnets the available public subnet for this node //! @param[in] sHypervisor node's hypervisor //! //! @return a pointer to the newly allocated resource structure or NULL if any error occured. //! //! @see free_resource() //! //! @pre The \p sNodeStatus field must not be NULL. //! //! @post On success, a resource structure is allocated and initialized with the given information //! //! @note Caller is responsible to free the allocated memory using the free_resource() function call. //! ncResource *allocate_resource(const char *sNodeStatus, boolean migrationCapable, const char *sIQN, int memorySizeMax, int memorySizeAvailable, int diskSizeMax, int diskSizeAvailable, int numberOfCoresMax, int numberOfCoresAvailable, const char *sPublicSubnets, const char *sHypervisor) { ncResource *pResource = NULL; // Make sure we have a valid parameter if (sNodeStatus == NULL) return (NULL); // See if we can allocate our resource structure if ((pResource = EUCA_ZALLOC(1, sizeof(ncResource))) == NULL) return (NULL); // // Initialize the structure with the given values // euca_strncpy(pResource->nodeStatus, sNodeStatus, CHAR_BUFFER_SIZE); if (sIQN) euca_strncpy(pResource->iqn, sIQN, CHAR_BUFFER_SIZE); pResource->migrationCapable = migrationCapable; if (sPublicSubnets) euca_strncpy(pResource->publicSubnets, sPublicSubnets, CHAR_BUFFER_SIZE); if (sHypervisor) euca_strncpy(pResource->hypervisor, sHypervisor, CHAR_BUFFER_SIZE); pResource->memorySizeMax = memorySizeMax; pResource->memorySizeAvailable = memorySizeAvailable; pResource->diskSizeMax = diskSizeMax; pResource->diskSizeAvailable = diskSizeAvailable; pResource->numberOfCoresMax = numberOfCoresMax; pResource->numberOfCoresAvailable = numberOfCoresAvailable; return (pResource); }
//! //! Sets the custom log prefix string //! //! @param[in] log_spec the log prefic specification. //! //! @return Always return EUCA_OK //! //! @pre The log_spec field should not be NULL and must have at least 1 character //! //! @post If the log_spec has at least 1 character, it'll be copied in our global log_custom_prefix field. This //! will ensure we're not overflowing our log_custom_prefix field. If log_spec is invalid, then the //! USE_STANDARD_PREFIX string will be applied. //! int log_prefix_set(const char *log_spec) { // @todo eventually, enable empty prefix if ((log_spec == NULL) || (strlen(log_spec) == 0)) euca_strncpy(log_custom_prefix, USE_STANDARD_PREFIX, sizeof(log_custom_prefix)); else euca_strncpy(log_custom_prefix, log_spec, sizeof(log_custom_prefix)); return (EUCA_OK); }
int imaging_init(const char *new_euca_home_path, const char *new_cloud_cert_path, const char *new_service_key_path) { assert(new_euca_home_path); assert(new_cloud_cert_path); assert(new_service_key_path); euca_strncpy(euca_home_path, new_euca_home_path, sizeof(euca_home_path)); euca_strncpy(cloud_cert_path, new_cloud_cert_path, sizeof(cloud_cert_path)); euca_strncpy(service_key_path, new_service_key_path, sizeof(service_key_path)); return EUCA_OK; }
//! //! //! //! @param[in] req //! @param[in] prev_art //! //! @return A pointer to the newly created artifact //! artifact *prepare_requirements(imager_request * req, artifact * prev_art) { artifact *result = NULL; artifact *sentinel = NULL; prepare_params *state = NULL; assert(req); assert(req->internal); assert(prev_art == NULL); state = (prepare_params *) req->internal; // compute tree of dependencies sentinel = vbr_alloc_tree(&(state->vm), // the struct containing the VBR state->bootable, // TRUE when hypervisors can't take a kernel/ramdisk state->work, // TRUE when disk will be used by hypervisor on this host FALSE, // this is not a migration destination state->sshkey, // the SSH key state->id); // ID is for logging if (sentinel == NULL) err("failed to prepare image %s", state->id); assert(sentinel->deps[0]); result = sentinel->deps[0]; // result should be disk, not the dummy sentinel EUCA_FREE(sentinel); if (state->out) { // specified ID trumps generated one euca_strncpy(result->id, state->out, sizeof(result->id)); } return result; }
char *find_ip_addr(void){ char hostname[HOSTNAME_SIZE]; if (gethostname(hostname, sizeof(hostname)) != 0) { fprintf(stderr, "failed to find hostname\n"); return NULL; } fprintf(stderr, "Searching for IP by hostname %s\n", hostname); struct addrinfo hints, *servinfo, *p; struct sockaddr_in *h; memset(&hints, 0, sizeof hints); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; char ip[BUFSIZE]; int rv; if ((rv = getaddrinfo(hostname, "http", &hints, &servinfo)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); return NULL; } int found = 0; for(p = servinfo; !found && p != NULL; p = p->ai_next) { if (!found) { h = (struct sockaddr_in *) p->ai_addr; euca_strncpy(ip, inet_ntoa(h->sin_addr), sizeof(ip)); found = 1; } } freeaddrinfo(servinfo); return strdup(ip); }
//! //! Updates VBR[] of the instance struct (which must be locked by //! the caller) with new resource locations (URLs of images) if //! such are present in the new //! static void update_resource_locations(virtualMachine *vm, char ** resourceLocations, int resourceLocationsLen) { virtualBootRecord *vbr = NULL; char *id_loc = NULL; char *loc = NULL; for (int i = 0; i < EUCA_MAX_VBRS && i < vm->virtualBootRecordLen; i++) { vbr = &(vm->virtualBootRecord[i]); //Skip invalid vbr or any entry without an id since the below match will not behave correctly. if(vbr == NULL || strlen(vbr->id) <= 0) { continue; } // see if ID in the VBR is among IDs associated with resourceLocations to be updated for (int j = 0; j < resourceLocationsLen; j++) { id_loc = resourceLocations[j]; if ((strstr(id_loc, vbr->id) == id_loc) // id_loc begins with ID && (strlen(id_loc) > (strlen(vbr->id) + 1))) { // id_loc has more than ID and '=' loc = id_loc + strlen(vbr->id) + 1; // URL starts after ID and '=' euca_strncpy(vbr->resourceLocation, loc, sizeof(vbr->resourceLocation)); // update the URL of in-memory struct } } } }
//! //! Validate and initialize the Eucalyptus and hook directories to use. //! //! @param[in] euca_dir a string containing the Eucalyptus directory to use //! @param[in] hooks_dir a string containing the hook directory to use //! //! @return EUCA_OK if the operation is successful or proper error code. Known error //! code returned include EUCA_ERROR. //! int init_hooks(const char *euca_dir, const char *hooks_dir) { assert(euca_dir); assert(hooks_dir); euca_strncpy(euca_path, euca_dir, sizeof(euca_path)); if (check_directory(euca_path)) return (EUCA_ERROR); euca_strncpy(hooks_path, hooks_dir, sizeof(hooks_path)); if (check_directory(hooks_path)) return (EUCA_ERROR); LOGINFO("using hooks directory %s\n", hooks_path); initialized = TRUE; return (EUCA_OK); }
//! //! Places raw XML result of an xpath query into buf. The query must return //! only one element. //! //! @param[in] xml_path a string containing the path to the XML file to parse //! @param[in] xpath a string contianing the XPATH expression to evaluate //! @param[out] buf for the XML string //! @param[in] buf_len size of the buf //! //! @return EUCA_OK or EUCA_ERROR //! int get_xpath_xml(const char *xml_path, const char *xpath, char *buf, int buf_len) { int ret = EUCA_ERROR; xmlDocPtr doc = NULL; xmlXPathContextPtr context = NULL; xmlXPathObjectPtr result = NULL; xmlNodeSetPtr nodeset = NULL; INIT(); pthread_mutex_lock(&xml_mutex); { if ((doc = xmlParseFile(xml_path)) != NULL) { if ((context = xmlXPathNewContext(doc)) != NULL) { if ((result = xmlXPathEvalExpression(((const xmlChar *)xpath), context)) != NULL) { if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { nodeset = result->nodesetval; if (nodeset->nodeNr > 1) { fprintf(stderr, "multiple matches for '%s' in '%s'\n", xpath, xml_path); } else { xmlNodePtr node = nodeset->nodeTab[0]->xmlChildrenNode; xmlBufferPtr xbuf = xmlBufferCreate(); if (xbuf) { int len = xmlNodeDump(xbuf, doc, node, 0, 1); if (len < 0) { fprintf(stderr, "failed to extract XML from %s\n", xpath); } else if (len > buf_len) { fprintf(stderr, "insufficient buffer for %s\n", xpath); } else { char *str = (char *)xmlBufferContent(xbuf); euca_strncpy(buf, str, buf_len); ret = EUCA_OK; } xmlBufferFree(xbuf); } else { fprintf(stderr, "failed to allocate XML buffer\n"); } } } xmlXPathFreeObject(result); } else { fprintf(stderr, "no results for '%s' in '%s'\n", xpath, xml_path); } xmlXPathFreeContext(context); } else { fprintf(stderr, "failed to set xpath '%s' context for '%s'\n", xpath, xml_path); } xmlFreeDoc(doc); } else { fprintf(stderr, "failed to parse XML in '%s'\n", xml_path); } } pthread_mutex_unlock(&xml_mutex); return ret; }
//! //! Initialize a network configuration structure with given values. //! //! @param[out] pNetCfg a pointer to the resulting network configuration structure //! @param[in] sPvMac the private MAC string //! @param[in] sPvIp the private IP string //! @param[in] sPbIp the public IP string //! @param[in] vlan the network Virtual LAN //! @param[in] networkIndex the network index //! //! @return EUCA_OK on success or EUCA_ERROR on failure. //! //! @pre The \p pNetCfg field must not be NULL. //! //! @post The network configuration structure is updated with the provided information //! int allocate_netConfig(netConfig * pNetCfg, const char *sPvMac, const char *sPvIp, const char *sPbIp, int vlan, int networkIndex) { // make sure our netconfig parameter isn't NULL if (pNetCfg != NULL) { if (sPvMac) euca_strncpy(pNetCfg->privateMac, sPvMac, ENET_ADDR_LEN); if (sPvIp) euca_strncpy(pNetCfg->privateIp, sPvIp, INET_ADDR_LEN); if (sPbIp) euca_strncpy(pNetCfg->publicIp, sPbIp, INET_ADDR_LEN); pNetCfg->networkIndex = networkIndex; pNetCfg->vlan = vlan; return (EUCA_OK); } return (EUCA_ERROR); }
//! //! Parses the volume string and returns a newly allocated ebs_volume_data structure via the parameter. //! Caller must free the returned structure. //! Format expected: //! sc://volumeId,token. //! OR //! http://schost:port/services/Storage/volumeId,token //! //! @param[in] volume_string containing the encoded volume information //! @param[in] dest a pointer to a pointer for ebs_volume_data, the referenced pointer will be set to newly allocated struct //! //! @return ok|fail //! //! @pre //! //! @post //! //! @note //! int deserialize_volume(char *volume_string, ebs_volume_data ** dest) { if (volume_string == NULL || strlen(volume_string) <= strlen(VOLUME_STRING_PREFIX)) { *dest = NULL; return EUCA_ERROR; } ebs_volume_data *vol_data = EUCA_ZALLOC(1, sizeof(ebs_volume_data)); if (vol_data == NULL) { LOGERROR("Cannot allocate memory!\n"); *dest = NULL; return EUCA_ERROR; } char *volume_start = volume_string + strlen(VOLUME_STRING_PREFIX); //skip the prefix. if (volume_start == NULL) { LOGERROR("Failed parsing token string: %s\n", volume_string); EUCA_FREE(vol_data); return EUCA_ERROR; } char *token_start = strchr(volume_start, ','); if (token_start == NULL) { LOGERROR("Failed parsing token string: %s\n", volume_string); EUCA_FREE(vol_data); return EUCA_ERROR; } token_start += sizeof(char); //Go 1 past the comma delimiter if (euca_strncpy(vol_data->volumeId, volume_start, token_start - volume_start) == NULL) { EUCA_FREE(vol_data); return EUCA_ERROR; } if (euca_strncpy(vol_data->token, token_start, strlen(token_start) + 1) == NULL) { EUCA_FREE(vol_data); return EUCA_ERROR; } *dest = vol_data; return EUCA_OK; }
//! //! Looks for file 'stage1' in dir. //! //! @param[in] dir the path where we are looking for the stage1 file //! //! @return EUCA_OK if the file is found or the following error code: //! \li EUCA_INVALID_ERROR: if any parameter does not meet the preconditions //! \li EUCA_NOT_FOUND_ERROR: if the 'stage1' file isn't found in the directory //! //! @pre The dir parameter must not be NULL //! //! @post If the 'stage1' file is found, the stage_file_dir variable is set. //! //! @note //! static int try_stage_dir(const char *dir) { char stage_file_path[EUCA_MAX_PATH] = { 0 }; if (dir) { snprintf(stage_file_path, sizeof(stage_file_path), "%s/stage1", dir); if (check_file(stage_file_path)) return (EUCA_NOT_FOUND_ERROR); euca_strncpy(stage_files_dir, dir, sizeof(stage_files_dir)); return (EUCA_OK); } return (EUCA_INVALID_ERROR); }
//! Not thread-safe. Initalizes semaphores etc. Must be guarded externally int init_stats(const char *euca_home, const char *current_component_name, void (*lock_fn)(), void (*unlock_fn)()) { int result = EUCA_OK; if(euca_home == NULL || current_component_name == NULL || lock_fn == NULL || unlock_fn == NULL) { LOGERROR("Cannot initialize stats subsystem due to invalid config parameters\n"); return EUCA_INVALID_ERROR; } if(is_initialized) { LOGDEBUG("Stats already initialized.\n"); return EUCA_OK; } LOGINFO("Initializing internal stats subsystem for component %s with euca home %s\n", current_component_name, euca_home); if(euca_strncpy(component_name, current_component_name, EUCA_MAX_PATH) <= 0) { LOGERROR("Failed setting stats component name to %s\n", current_component_name); result = EUCA_FATAL_ERROR; goto cleanup; } get_lock_fn = lock_fn; release_lock_fn = unlock_fn; LOGDEBUG("Registering sensors\n"); //Register the sensors (map the function pointers, etc). result = register_sensor_set(component_name); if(result != EUCA_OK) { LOGERROR("Error registering internal sensor set: %d\n", result); goto cleanup; } LOGDEBUG("Initializing event emitter\n"); //Initialize the event emitter result = init_emitter(euca_home); if(result != EUCA_OK) { LOGERROR("Error initializing emitter: %d\n", result); goto cleanup; } cleanup: if(result == EUCA_OK) { is_initialized = TRUE; LOGINFO("Internal stats initialization complete\n"); } else { LOGERROR("Initialization of stats system failed due to error: %d\n", result); } return result; }
//! //! Records volume's information in the instance struct, updating the non-NULL values if the record //! already exists //! //! @param[in] pInstance a pointer to our instance containing the volume information to save //! @param[in] sVolumeId the volume identifier string (vol-XXXXXXXX) //! @param[in] sVolumeAttachmentToken the attachment token associated with this volume and attachment //! @param[in] sConnectionString the connection string info specific to this host's volume attachment //! @param[in] sDevName the device name //! @param[in] sStateName the current volume state name //! @param[in] sXml the current volume xml //! //! @return a pointer to the volume if found. Otherwise NULL is returned. //! //! @pre \li Both \p pInstance and \p sVolumeId fields must not be NULL //! \li A volume with \p sVolumeId for \p pInstance should exists //! \li If such volume does not exists, we must have an empty slot in the volume list //! //! @post \li If any of \p pInstance or \p sVolumeId is NULL, the application will throw a SIGABRT signal //! \li If the volume is found or if we have an empty slot, the volume information will be saved //! \li If the volume is not found and if we do not have empty slot, NULL is returned and nothing is saved //! ncVolume *save_volume(ncInstance * pInstance, const char *sVolumeId, const char *sVolumeAttachmentToken, const char *sConnectionString, const char *sDevName, const char *sStateName, const char *sXml) { ncVolume *pVol = NULL; // Make sure pInstance and sVolumeId aren't NULL assert(pInstance != NULL); assert(sVolumeId != NULL); // Lookup for our device if ((pVol = find_volume(pInstance, sVolumeId)) != NULL) { // // Save our volume information // euca_strncpy(pVol->volumeId, sVolumeId, CHAR_BUFFER_SIZE); if (sVolumeAttachmentToken) euca_strncpy(pVol->attachmentToken, sVolumeAttachmentToken, CHAR_BUFFER_SIZE); if (sConnectionString) euca_strncpy(pVol->connectionString, sConnectionString, VERY_BIG_CHAR_BUFFER_SIZE); if (sDevName) euca_strncpy(pVol->devName, sDevName, CHAR_BUFFER_SIZE); if (sStateName) euca_strncpy(pVol->stateName, sStateName, CHAR_BUFFER_SIZE); if (sXml) euca_strncpy(pVol->volLibvirtXml, sXml, VERY_BIG_CHAR_BUFFER_SIZE); } return (pVol); }
//! //! Callback used when checking for the integrity of the work blobstore. //! //! @param[in] bb pointer to the blockblob to examine //! //! @return EUCA_OK on success or EUCA_ERROR on failure //! //! @see check_backing_store() //! @see blobstore_fsck() //! //! @pre The bb field must not be NULL. //! static int stale_blob_examiner(const blockblob * bb) { char *s = NULL; char *user_id = NULL; char *inst_id = NULL; char *file = NULL; char path[EUCA_MAX_PATH] = ""; char work_path[EUCA_MAX_PATH] = ""; int work_path_len = 0; ncInstance *instance = NULL; set_path(work_path, sizeof(work_path), NULL, NULL); work_path_len = strlen(work_path); assert(work_path_len > 0); s = strstr(bb->blocks_path, work_path); if ((s == NULL) || (s != bb->blocks_path)) { // blob not under work blobstore path return (EUCA_OK); } // parse the path past the work directory base euca_strncpy(work_path, bb->blocks_path, sizeof(work_path)); s = work_path + work_path_len + 1; user_id = strtok(s, "/"); inst_id = strtok(NULL, "/"); file = strtok(NULL, "/"); if (((instance = find_instance(instances, inst_id)) == NULL) // not found among instances => stale || instance->state == TEARDOWN) { // found among instances, but is already marked as terminated // if this instance is not among those we know about, // load it into memory and report it in Teardown state //! @TODO technically, disk state for this instance is not gone, //! but it soon will be, once this examiner function returns error if ((instance == NULL) && ((instance = load_instance_struct(inst_id)) != NULL)) { LOGINFO("marking non-running instance %s as terminated\n", inst_id); instance->terminationTime = time(NULL); // set time to now, so record won't get expired immediately change_state(instance, TEARDOWN); int err = add_instance(instances, instance); // we are not using locks because we assume the caller does if (err) { free_instance(&instance); } } // while we're here, try to delete extra files that aren't managed by the blobstore snprintf(path, sizeof(path), "%s/work/%s/%s", instances_path, user_id, inst_id); blobstore_delete_nonblobs(bb->store, path); return (EUCA_ERROR); } return (EUCA_OK); }
//! //! //! //! @param[in] euca_home //! void init_iscsi(const char *euca_home) { const char *tmp = NULL; if (euca_home) { tmp = euca_home; } else { if ((tmp = getenv(EUCALYPTUS_ENV_VAR_NAME)) == NULL) { tmp = "/opt/eucalyptus"; } } euca_strncpy(home, tmp, sizeof(home)); snprintf(connect_storage_cmd_path, MAX_PATH, EUCALYPTUS_CONNECT_ISCSI, home, home); snprintf(disconnect_storage_cmd_path, MAX_PATH, EUCALYPTUS_DISCONNECT_ISCSI, home, home); snprintf(get_storage_cmd_path, MAX_PATH, EUCALYPTUS_GET_ISCSI, home, home); iscsi_sem = sem_alloc(1, IPC_MUTEX_SEMAPHORE); }
//! Do a full init and execution run. Tests end-to-end static int test_stats_run(const char *config_file_path) { print_header(__func__); char *(*check_call)() = testing_service_check_call; char *(*state_call)() = testing_service_state_call; const char *test_home = getenv(EUCALYPTUS_ENV_VAR_NAME); //Init the config file stuff LOGDEBUG("Setting up config files and values for init test\n"); char configFiles[1][EUCA_MAX_PATH]; bzero(configFiles[0], EUCA_MAX_PATH); euca_strncpy(configFiles[0], config_file_path, EUCA_MAX_PATH); configInitValues(configEntryKeysRestart, configEntryKeysNoRestart); readConfigFile(configFiles, 1); LOGDEBUG("Getting sensor list to validate config works\n"); char *test_value = configFileValue(SENSOR_LIST_CONF_PARAM_NAME); if(!test_value) { LOGERROR("Config setup didn't work. Null value found\n"); return EUCA_ERROR; } else { LOGINFO("Config file has enabled stats: %s\n", test_value); } LOGDEBUG("Done with config file checks\n"); flush_sensor_registry(); //just to be sure from other tests initialize_message_sensor("testservice", 60, 60, test_get_msg_stats, test_set_msg_stats); initialize_service_state_sensor("testservice", 60, 60, state_call, check_call); if(init_stats(test_home, "testservice", test_lock, test_unlock) != EUCA_OK) { LOGERROR("Error initialing stats\n"); flush_sensor_registry(); return EUCA_ERROR; } LOGINFO("Setting some message stats and doing an internal run\n"); //populate some stats for the message stats update_message_stats(test_msg_stats, "fakemessage", 500, 0); update_message_stats(test_msg_stats, "fakemessageDescribe", 250, 0); update_message_stats(test_msg_stats, "fakemessageRun", 200, 0); int ret = internal_sensor_pass(TRUE); flush_sensor_registry(); return ret; }
//! //! Sets and opens the log file //! //! @param[in] file the file name of the log file. A NULL value unset the file //! //! @return EUCA_OK on success or EUCA_ERROR on failure //! int log_file_set(const char *file) { if (file == NULL) { // NULL means standard output log_file_path[0] = '\0'; return (EUCA_OK); } if (strcmp(log_file_path, file) == 0) { // hasn't changed return (EUCA_OK); } euca_strncpy(log_file_path, file, EUCA_MAX_PATH); if (get_file(TRUE) == NULL) { return (EUCA_ERROR); } release_file(); return (EUCA_OK); }
//! //! Main entry point of the application //! //! @param[in] argc the number of parameter passed on the command line //! @param[in] argv the list of arguments //! //! @return EUCA_OK on success or EUCA_ERROR on failure. //! int main(int argc, char **argv) { int err = EUCA_ERROR; char *in_path = NULL; char *out_path = NULL; char xml_buf[2048] = ""; if (argc != 2) { LOGERROR("required parameters are <XSLT stylesheet path>\n"); return (EUCA_ERROR); } euca_strncpy(xslt_path, argv[1], sizeof(xslt_path)); in_path = tempnam(NULL, "xml-"); out_path = tempnam(NULL, "xml-"); create_dummy_instance(in_path); LOGINFO("parsing stylesheet %s\n", xslt_path); if ((err = apply_xslt_stylesheet(xslt_path, in_path, out_path, NULL, 0)) != EUCA_OK) goto out; LOGINFO("parsing stylesheet %s again\n", xslt_path); if ((err = apply_xslt_stylesheet(xslt_path, in_path, out_path, xml_buf, sizeof(xml_buf))) != EUCA_OK) goto out; LOGINFO("wrote XML to %s\n", out_path); if (strlen(xml_buf) < 1) { err = EUCA_ERROR; LOGERROR("failed to see XML in buffer\n"); goto out; } cat(out_path); out: remove(out_path); remove(in_path); EUCA_FREE(in_path); EUCA_FREE(out_path); return (err); }
//! //! Initialize the XML local parameters from the NC state structure. //! //! @param[in] nc_state a pointer to the NC state structure to initialize //! static void init(struct nc_state_t *nc_state) { pthread_mutex_lock(&xml_mutex); { if (!initialized) { xmlInitParser(); LIBXML_TEST_VERSION; // verifies that loaded library matches the compiled library xmlSubstituteEntitiesDefault(1); // substitute entities while parsing xmlSetGenericErrorFunc(NULL, error_handler); // catches errors/warnings that libxml2 writes to stderr xsltSetGenericErrorFunc(NULL, error_handler); // catches errors/warnings that libslt writes to stderr if (nc_state != NULL) { config_use_virtio_root = nc_state->config_use_virtio_root; config_use_virtio_disk = nc_state->config_use_virtio_disk; config_use_virtio_net = nc_state->config_use_virtio_net; euca_strncpy(xslt_path, nc_state->libvirt_xslt_path, sizeof(xslt_path)); } initialized = TRUE; } } pthread_mutex_unlock(&xml_mutex); }
//! //! //! //! @param[in] req //! @param[in] prev_art //! //! @return A pointer to the newly created artifact //! artifact *prepare_requirements(imager_request * req, artifact * prev_art) { artifact *result = NULL; artifact *sentinel = NULL; prepare_params *state = NULL; assert(req); assert(req->internal); assert(prev_art == NULL); state = (prepare_params *) req->internal; // compute tree of dependencies sentinel = vbr_alloc_tree(&(state->vm), // the struct containing the VBR state->bootable, // TRUE when hypervisors can't take a kernel/ramdisk state->work, // TRUE when disk will be used by hypervisor on this host ! (state->action & ACTION_DOWNLOAD), // migration destination => do not bother with download state->sshkey, // the SSH key NULL, // bail flag state->id); // ID is for logging if (sentinel == NULL) err("failed to prepare image %s", state->id); assert(sentinel->deps[0]); result = sentinel->deps[0]; // result should be disk, not the dummy sentinel EUCA_FREE(sentinel); // for disk, do_not_download means don't bother constructing it // so, if 'convert' action wasn't requested, we'll set do_not_download result->do_not_download = ! (state->action & ACTION_CONVERT); if (state->out) { // specified ID trumps generated one euca_strncpy(result->id, state->out, sizeof(result->id)); } return result; }
//! //! Defines the thread that does the actual reboot of an instance. //! //! @param[in] arg a transparent pointer to the argument passed to this thread handler //! //! @return Always return NULL //! static void *rebooting_thread(void *arg) { #define REATTACH_RETRIES 3 int i = 0; int err = 0; int error = 0; int rc = 0; int log_level_for_devstring = EUCATRACE; char *xml = NULL; char *remoteDevStr = NULL; char path[MAX_PATH] = ""; char lpath[MAX_PATH] = ""; char resourceName[1][MAX_SENSOR_NAME_LEN] = { {0} }; char resourceAlias[1][MAX_SENSOR_NAME_LEN] = { {0} }; ncVolume *volume = NULL; ncInstance *instance = ((ncInstance *) arg); virDomainPtr dom = NULL; virConnectPtr *conn = NULL; logprintfl(EUCADEBUG, "[%s] spawning rebooting thread\n", instance->instanceId); if ((xml = file2str(instance->libvirtFilePath)) == NULL) { logprintfl(EUCAERROR, "[%s] cannot obtain instance XML file %s\n", instance->instanceId, instance->libvirtFilePath); return NULL; } if ((conn = check_hypervisor_conn()) == NULL) { logprintfl(EUCAERROR, "[%s] cannot restart instance %s, abandoning it\n", instance->instanceId, instance->instanceId); change_state(instance, SHUTOFF); EUCA_FREE(xml); return NULL; } sem_p(hyp_sem); { dom = virDomainLookupByName(*conn, instance->instanceId); } sem_v(hyp_sem); if (dom == NULL) { EUCA_FREE(xml); return NULL; } sem_p(hyp_sem); { // for KVM, must stop and restart the instance logprintfl(EUCADEBUG, "[%s] destroying domain\n", instance->instanceId); error = virDomainDestroy(dom); // @todo change to Shutdown? is this synchronous? virDomainFree(dom); } sem_v(hyp_sem); if (error) { EUCA_FREE(xml); return NULL; } // Add a shift to values of three of the metrics: ones that // drop back to zero after a reboot. The shift, which is based // on the latest value, ensures that values sent upstream do // not go backwards . sensor_shift_metric(instance->instanceId, "CPUUtilization"); sensor_shift_metric(instance->instanceId, "NetworkIn"); sensor_shift_metric(instance->instanceId, "NetworkOut"); // domain is now shut down, create a new one with the same XML sem_p(hyp_sem); { logprintfl(EUCAINFO, "[%s] rebooting\n", instance->instanceId); dom = virDomainCreateLinux(*conn, xml, 0); } sem_v(hyp_sem); EUCA_FREE(xml); euca_strncpy(resourceName[0], instance->instanceId, MAX_SENSOR_NAME_LEN); sensor_refresh_resources(resourceName, resourceAlias, 1); // refresh stats so we set base value accurately // re-attach each volume previously attached for (i = 0; i < EUCA_MAX_VOLUMES; ++i) { volume = &instance->volumes[i]; if (strcmp(volume->stateName, VOL_STATE_ATTACHED) && strcmp(volume->stateName, VOL_STATE_ATTACHING)) continue; // skip the entry unless attached or attaching logprintfl(EUCADEBUG, "[%s] volumes [%d] = '%s'\n", instance->instanceId, i, volume->stateName); // get credentials, decrypt them remoteDevStr = get_iscsi_target(volume->remoteDev); if (!remoteDevStr || !strstr(remoteDevStr, "/dev")) { logprintfl(EUCAERROR, "[%s] failed to get local name of host iscsi device when re-attaching\n", instance->instanceId); rc = 1; } else { // set the path snprintf(path, sizeof(path), EUCALYPTUS_VOLUME_XML_PATH_FORMAT, instance->instancePath, volume->volumeId); // vol-XXX.xml snprintf(lpath, sizeof(lpath), EUCALYPTUS_VOLUME_LIBVIRT_XML_PATH_FORMAT, instance->instancePath, volume->volumeId); // vol-XXX-libvirt.xml // read in libvirt XML, which may have been modified by the hook above if ((xml = file2str(lpath)) == NULL) { logprintfl(EUCAERROR, "[%s][%s] failed to read volume XML from %s\n", instance->instanceId, volume->volumeId, lpath); rc = 1; } } EUCA_FREE(remoteDevStr); if (!rc) { // zhill - wrap with retry in case libvirt is dumb. err = 0; for (i = 1; i < REATTACH_RETRIES; i++) { // protect libvirt calls because we've seen problems during concurrent libvirt invocations sem_p(hyp_sem); { err = virDomainAttachDevice(dom, xml); } sem_v(hyp_sem); if (err) { logprintfl(EUCAERROR, "[%s][%s] failed to reattach volume (attempt %d of %d)\n", instance->instanceId, volume->volumeId, i, REATTACH_RETRIES); logprintfl(EUCADEBUG, "[%s][%s] error from virDomainAttachDevice: %d xml: %s\n", instance->instanceId, volume->volumeId, err, xml); sleep(3); // sleep a bit and retry } else { logprintfl(EUCAINFO, "[%s][%s] volume reattached as '%s'\n", instance->instanceId, volume->volumeId, volume->localDevReal); break; } } log_level_for_devstring = EUCATRACE; if (err) log_level_for_devstring = EUCADEBUG; logprintfl(log_level_for_devstring, "[%s][%s] remote device string: %s\n", instance->instanceId, volume->volumeId, volume->remoteDev); } EUCA_FREE(xml); } if (dom == NULL) { logprintfl(EUCAERROR, "[%s] failed to restart instance\n", instance->instanceId); change_state(instance, SHUTOFF); return NULL; } sem_p(hyp_sem); { virDomainFree(dom); } sem_v(hyp_sem); return NULL; #undef REATTACH_RETRIES }
//! //! 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); }
//! //! 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); }
//! //! Initialize the backing store. Called during initialization of node controller. //! //! @param[in] conf_instances_path path to where the instances information are stored //! @param[in] conf_work_size_mb the work blobstore size limit in MB (if 0 then unlimitted) //! @param[in] conf_cache_size_mb the cache blobstore size limit in MB (if 0 then cache isn't used) //! //! @return EUCA_OK on success or the following error codes: //! \li EUCA_INVALID_ERROR: if any parameter does not meet the preconditions //! \li EUCA_ACCESS_ERROR: if we fail to access our cache and work directories //! \li EUCA_PERMISSION_ERROR: if we fail to create the cache or work stores. //! //! @pre The conf_instances_path field must not be NULL //! //! @post On success, the backing store module is initialized and the following happened: //! \li our global instance_path variable is set with the given conf_instance_path //! \li the work blobstore is created and our global work_bs variable is set //! \li the cache blobstore is created if necessary and the cache_bs variable is set //! \li the disk semaphore is created if necessary //! int init_backing_store(const char *conf_instances_path, unsigned int conf_work_size_mb, unsigned int conf_cache_size_mb) { char cache_path[EUCA_MAX_PATH] = ""; char work_path[EUCA_MAX_PATH] = ""; unsigned long long cache_limit_blocks = 0; unsigned long long work_limit_blocks = 0; blobstore_snapshot_t snapshot_policy = BLOBSTORE_SNAPSHOT_ANY; LOGINFO("initializing backing store...\n"); // Make sure we have a valid intance path passed to us if (conf_instances_path == NULL) { LOGERROR("INSTANCE_PATH not specified\n"); return (EUCA_INVALID_ERROR); } // Set our global instance_path variable with the content of conf_instance_path euca_strncpy(instances_path, conf_instances_path, sizeof(instances_path)); if (check_directory(instances_path)) { LOGERROR("INSTANCE_PATH (%s) does not exist!\n", instances_path); return (EUCA_ACCESS_ERROR); } // Check if our cache path exist. If not it should get crated snprintf(cache_path, sizeof(cache_path), "%s/cache", instances_path); if (ensure_directories_exist(cache_path, 0, NULL, NULL, BACKING_DIRECTORY_PERM) == -1) return (EUCA_ACCESS_ERROR); // Check if our work path exist. If not it should get crated snprintf(work_path, sizeof(work_path), "%s/work", instances_path); if (ensure_directories_exist(work_path, 0, NULL, NULL, BACKING_DIRECTORY_PERM) == -1) return (EUCA_ACCESS_ERROR); // convert MB to blocks cache_limit_blocks = (unsigned long long)conf_cache_size_mb *2048; work_limit_blocks = (unsigned long long)conf_work_size_mb *2048; // we take 0 as unlimited if (work_limit_blocks == 0) { work_limit_blocks = ULLONG_MAX; } // by default we let blobstore pick the snapshot policy, which // will use device mapper if available, which is faster than copying snapshot_policy = BLOBSTORE_SNAPSHOT_ANY; if (nc_state.disable_snapshots) { LOGINFO("if allocating storage, will avoid using snapshots\n"); snapshot_policy = BLOBSTORE_SNAPSHOT_NONE; } // Set the backing store error callback function blobstore_set_error_function(&bs_errors); // Do we need to create a cache blobstore if (cache_limit_blocks) { cache_bs = blobstore_open(cache_path, cache_limit_blocks, BLOBSTORE_FLAG_CREAT, BLOBSTORE_FORMAT_DIRECTORY, BLOBSTORE_REVOCATION_LRU, snapshot_policy); if (cache_bs == NULL) { LOGERROR("failed to open/create cache blobstore: %s\n", blobstore_get_error_str(blobstore_get_error())); return (EUCA_PERMISSION_ERROR); } } // Lets open the work blobstore work_bs = blobstore_open(work_path, work_limit_blocks, BLOBSTORE_FLAG_CREAT, BLOBSTORE_FORMAT_FILES, BLOBSTORE_REVOCATION_NONE, snapshot_policy); if (work_bs == NULL) { LOGERROR("failed to open/create work blobstore: %s\n", blobstore_get_error_str(blobstore_get_error())); LOGERROR("%s\n", blobstore_get_last_trace()); BLOBSTORE_CLOSE(cache_bs); return (EUCA_PERMISSION_ERROR); } // set the initial value of the semaphore to the number of // disk-intensive operations that can run in parallel on this node if (nc_state.concurrent_disk_ops && ((disk_sem = sem_alloc(nc_state.concurrent_disk_ops, IPC_MUTEX_SEMAPHORE)) == NULL)) { LOGERROR("failed to create and initialize disk semaphore\n"); return (EUCA_PERMISSION_ERROR); } return (EUCA_OK); }
//! //! Handles the instance migration request. //! //! @param[in] nc a pointer to the node controller (NC) state //! @param[in] pMeta a pointer to the node controller (NC) metadata structure //! @param[in] instances metadata for the instance to migrate to destination //! @param[in] instancesLen number of instances in the instance list //! @param[in] action IP of the destination Node Controller //! @param[in] credentials credentials that enable the migration //! //! @return EUCA_OK on success or EUCA_*ERROR on failure //! //! @pre //! //! @post static int doMigrateInstances(struct nc_state_t *nc, ncMetadata * pMeta, ncInstance ** instances, int instancesLen, char *action, char *credentials, char ** resourceLocations, int resourceLocationsLen) { int ret = EUCA_OK; int credentials_prepared = 0; char *libvirt_xml_modified = NULL; if (instancesLen <= 0) { LOGERROR("called with invalid instancesLen (%d)\n", instancesLen); pMeta->replyString = strdup("internal error (invalid instancesLen)"); return (EUCA_INVALID_ERROR); } LOGDEBUG("verifying %d instance[s] for migration...\n", instancesLen); for (int inst_idx = 0; inst_idx < instancesLen; inst_idx++) { LOGDEBUG("verifying instance # %d...\n", inst_idx); if (instances[inst_idx]) { ncInstance *instance_idx = instances[inst_idx]; LOGDEBUG("[%s] proposed migration action '%s' (%s > %s) [creds=%s]\n", SP(instance_idx->instanceId), SP(action), SP(instance_idx->migration_src), SP(instance_idx->migration_dst), (instance_idx->migration_credentials == NULL) ? "UNSET" : "present"); } else { pMeta->replyString = strdup("internal error (instance count mismatch)"); LOGERROR("Mismatch between migration instance count (%d) and length of instance list\n", instancesLen); return (EUCA_ERROR); } } // TO-DO: Optimize the location of this loop, placing it inside various conditionals below? for (int inst_idx = 0; inst_idx < instancesLen; inst_idx++) { ncInstance *instance_req = instances[inst_idx]; char *sourceNodeName = instance_req->migration_src; char *destNodeName = instance_req->migration_dst; LOGDEBUG("[%s] processing instance # %d (%s > %s)\n", instance_req->instanceId, inst_idx, instance_req->migration_src, instance_req->migration_dst); // this is a call to the source of migration if (!strcmp(pMeta->nodeName, sourceNodeName)) { // locate the instance structure ncInstance *instance; sem_p(inst_sem); { instance = find_instance(&global_instances, instance_req->instanceId); } sem_v(inst_sem); if (instance == NULL) { LOGERROR("[%s] cannot find instance\n", instance_req->instanceId); pMeta->replyString = strdup("failed to locate instance to migrate"); return (EUCA_NOT_FOUND_ERROR); } if (strcmp(action, "prepare") == 0) { sem_p(inst_sem); instance->migration_state = MIGRATION_PREPARING; euca_strncpy(instance->migration_src, sourceNodeName, HOSTNAME_SIZE); euca_strncpy(instance->migration_dst, destNodeName, HOSTNAME_SIZE); euca_strncpy(instance->migration_credentials, credentials, CREDENTIAL_SIZE); instance->migrationTime = time(NULL); update_resource_locations(&(instance->params), resourceLocations, resourceLocationsLen); save_instance_struct(instance); copy_instances(); sem_v(inst_sem); // Establish migration-credential keys if this is the first instance preparation for this host. LOGINFO("[%s] migration source preparing %s > %s [creds=%s]\n", SP(instance->instanceId), SP(instance->migration_src), SP(instance->migration_dst), (instance->migration_credentials == NULL) ? "UNSET" : "present"); if (!credentials_prepared) { if (generate_migration_keys(sourceNodeName, credentials, TRUE, instance) != EUCA_OK) { pMeta->replyString = strdup("internal error (migration credentials generation failed)"); return (EUCA_SYSTEM_ERROR); } else { credentials_prepared++; } } sem_p(inst_sem); instance->migration_state = MIGRATION_READY; save_instance_struct(instance); copy_instances(); sem_v(inst_sem); } else if (strcmp(action, "commit") == 0) { sem_p(inst_sem); if (instance->migration_state == MIGRATION_IN_PROGRESS) { LOGWARN("[%s] duplicate request to migration source to initiate %s > %s (already migrating)\n", instance->instanceId, instance->migration_src, instance->migration_dst); sem_v(inst_sem); return (EUCA_DUPLICATE_ERROR); } else if (instance->migration_state != MIGRATION_READY) { LOGERROR("[%s] request to commit migration %s > %s when source migration_state='%s' (not 'ready')\n", instance->instanceId, SP(sourceNodeName), SP(destNodeName), migration_state_names[instance->migration_state]); sem_v(inst_sem); return (EUCA_UNSUPPORTED_ERROR); } instance->migration_state = MIGRATION_IN_PROGRESS; outgoing_migrations_in_progress++; LOGINFO("[%s] migration source initiating %s > %s [creds=%s] (1 of %d active outgoing migrations)\n", instance->instanceId, instance->migration_src, instance->migration_dst, (instance->migration_credentials == NULL) ? "UNSET" : "present", outgoing_migrations_in_progress); save_instance_struct(instance); copy_instances(); sem_v(inst_sem); // since migration may take a while, we do them in a thread pthread_t tcb = { 0 }; if (pthread_create(&tcb, NULL, migrating_thread, (void *)instance)) { LOGERROR("[%s] failed to spawn a migration thread\n", instance->instanceId); return (EUCA_THREAD_ERROR); } set_corrid_pthread(get_corrid() != NULL ? get_corrid()->correlation_id : NULL, tcb); if (pthread_detach(tcb)) { LOGERROR("[%s] failed to detach the migration thread\n", instance->instanceId); return (EUCA_THREAD_ERROR); } } else if (strcmp(action, "rollback") == 0) { if ((instance->migration_state == MIGRATION_READY) || (instance->migration_state == MIGRATION_PREPARING)) { LOGINFO("[%s] rolling back migration (%s > %s) on source\n", instance->instanceId, instance->migration_src, instance->migration_dst); sem_p(inst_sem); migration_rollback(instance); sem_v(inst_sem); } else { LOGINFO("[%s] ignoring request to roll back migration on source with instance in state %s(%s) -- duplicate rollback request?\n", instance->instanceId, instance->stateName, migration_state_names[instance->migration_state]); } } else { LOGERROR("[%s] action '%s' is not valid\n", instance->instanceId, action); return (EUCA_INVALID_ERROR); } } else if (!strcmp(pMeta->nodeName, destNodeName)) { // this is a migrate request to destination if (!strcmp(action, "commit")) { LOGERROR("[%s] action '%s' for migration (%s > %s) is not valid on destination node\n", instance_req->instanceId, action, SP(sourceNodeName), SP(destNodeName)); return (EUCA_UNSUPPORTED_ERROR); } else if (!strcmp(action, "rollback")) { LOGINFO("[%s] rolling back migration (%s > %s) on destination\n", instance_req->instanceId, SP(sourceNodeName), SP(destNodeName)); sem_p(inst_sem); { ncInstance *instance = find_instance(&global_instances, instance_req->instanceId); if (instance != NULL) { LOGDEBUG("[%s] marked for cleanup\n", instance->instanceId); change_state(instance, SHUTOFF); instance->migration_state = MIGRATION_CLEANING; save_instance_struct(instance); } } sem_v(inst_sem); return EUCA_OK; } else if (strcmp(action, "prepare") != 0) { LOGERROR("[%s] action '%s' is not valid or not implemented\n", instance_req->instanceId, action); return (EUCA_INVALID_ERROR); } // Everything from here on is specific to "prepare" on a destination. // allocate a new instance struct ncInstance *instance = clone_instance(instance_req); if (instance == NULL) { LOGERROR("[%s] could not allocate instance struct\n", instance_req->instanceId); goto failed_dest; } sem_p(inst_sem); instance->migration_state = MIGRATION_PREPARING; instance->migrationTime = time(NULL); //In preparing state, so set migrationTime. euca_strncpy(instance->migration_src, sourceNodeName, HOSTNAME_SIZE); euca_strncpy(instance->migration_dst, destNodeName, HOSTNAME_SIZE); euca_strncpy(instance->migration_credentials, credentials, CREDENTIAL_SIZE); update_resource_locations(&(instance->params), resourceLocations, resourceLocationsLen); sem_v(inst_sem); // Establish migration-credential keys. LOGINFO("[%s] migration destination preparing %s > %s [creds=%s]\n", instance->instanceId, SP(instance->migration_src), SP(instance->migration_dst), (instance->migration_credentials == NULL) ? "UNSET" : "present"); // First, call config-file modification script to authorize source node. LOGDEBUG("[%s] authorizing migration source node %s\n", instance->instanceId, instance->migration_src); if (authorize_migration_keys("-a", instance->migration_src, instance->migration_credentials, instance, TRUE) != EUCA_OK) { goto failed_dest; } // Then, generate keys and restart libvirtd. if (generate_migration_keys(instance->migration_dst, instance->migration_credentials, TRUE, instance) != EUCA_OK) { goto failed_dest; } int error; //Fix for EUCA-10433, need instance struct in global_instances prior to doing volume ops //The monitor thread will now pick up the instance, so the migrationTime must be set sem_p(inst_sem); save_instance_struct(instance); error = add_instance(&global_instances, instance); copy_instances(); sem_v(inst_sem); if (error) { if (error == EUCA_DUPLICATE_ERROR) { LOGINFO("[%s] instance struct already exists (from previous migration?), deleting and re-adding...\n", instance->instanceId); error = remove_instance(&global_instances, instance); if (error) { LOGERROR("[%s] could not replace (remove) instance struct, failing...\n", instance->instanceId); goto failed_dest; } error = add_instance(&global_instances, instance); if (error) { LOGERROR("[%s] could not replace (add) instance struct, failing...\n", instance->instanceId); goto failed_dest; } } else { LOGERROR("[%s] could not add instance struct, failing...\n", instance->instanceId); goto failed_dest; } } if (vbr_parse(&(instance->params), pMeta) != EUCA_OK) { goto failed_dest; } // set up networking char brname[IF_NAME_LEN] = ""; if (!strcmp(nc->pEucaNet->sMode, NETMODE_MANAGED)) { snprintf(brname, IF_NAME_LEN, "%s", instance->groupIds[0]); } else { snprintf(brname, IF_NAME_LEN, "%s", nc->pEucaNet->sBridgeDevice); } euca_strncpy(instance->params.guestNicDeviceName, brname, sizeof(instance->params.guestNicDeviceName)); // TODO: move stuff in startup_thread() into a function? set_instance_params(instance); if ((error = create_instance_backing(instance, TRUE)) // create files that back the disks || (error = gen_instance_xml(instance)) // create euca-specific instance XML file || (error = gen_libvirt_instance_xml(instance))) { // transform euca-specific XML into libvirt XML LOGERROR("[%s] failed to prepare images for migrating instance (error=%d)\n", instance->instanceId, error); goto failed_dest; } // attach any volumes for (int v = 0; v < EUCA_MAX_VOLUMES; v++) { ncVolume *volume = &instance->volumes[v]; if (strcmp(volume->stateName, VOL_STATE_ATTACHED) && strcmp(volume->stateName, VOL_STATE_ATTACHING)) continue; // skip the entry unless attached or attaching LOGDEBUG("[%s] volumes [%d] = '%s'\n", instance->instanceId, v, volume->stateName); ebs_volume_data *vol_data = NULL; char *libvirt_xml = NULL; char serial[128]; char bus[16]; set_serial_and_bus(volume->volumeId, volume->devName, serial, sizeof(serial), bus, sizeof(bus)); if ((ret = connect_ebs(volume->devName, serial, bus, nc, instance->instanceId, volume->volumeId, volume->attachmentToken, &libvirt_xml, &vol_data)) != EUCA_OK) { goto unroll; } // update the volume struct with connection string obtained from SC euca_strncpy(volume->connectionString, vol_data->connect_string, sizeof(volume->connectionString)); // save volume info into vol-XXX-libvirt.xml for future detach if (create_vol_xml(instance->instanceId, volume->volumeId, libvirt_xml, &libvirt_xml_modified) != EUCA_OK) { goto unroll; } continue; unroll: ret = EUCA_ERROR; // @TODO: unroll all previous ones // for (int uv = v - 1; uv >= 0; uv--) { // disconnect_ebs(nc, instance->instanceId, volume->volumeId, ) // } goto failed_dest; } // build any secondary network interface xml files for (int w=0; w < EUCA_MAX_NICS; w++) { if (strlen(instance->secNetCfgs[w].interfaceId) == 0) continue; gen_libvirt_nic_xml(instance->instancePath, instance->secNetCfgs[w].interfaceId); } sem_p(inst_sem); instance->migration_state = MIGRATION_READY; instance->migrationTime = 0; //Reset the timer, to ensure monitoring thread handles this properly. This is required when setting BOOTING state instance->bootTime = time(NULL); // otherwise nc_state.booting_cleanup_threshold will kick in change_state(instance, BOOTING); // not STAGING, since in that mode we don't poll hypervisor for info LOGINFO("[%s] migration destination ready %s > %s\n", instance->instanceId, instance->migration_src, instance->migration_dst); save_instance_struct(instance); copy_instances(); sem_v(inst_sem); continue; failed_dest: sem_p(inst_sem); // Just making sure... if (instance != NULL) { LOGERROR("[%s] setting instance to Teardown(cleaning) after destination failure to prepare for migration\n", instance->instanceId); // Set state to Teardown(cleaning) so source won't wait until timeout to roll back. instance->migration_state = MIGRATION_CLEANING; instance->terminationTime = time(NULL); change_state(instance, TEARDOWN); save_instance_struct(instance); add_instance(&global_instances, instance); // OK if this fails--that should mean it's already been added. copy_instances(); } // If no remaining incoming or pending migrations, deauthorize all clients. // TO-DO: Consolidate with similar sequence in handlers.c into a utility function? if (!incoming_migrations_in_progress) { int incoming_migrations_pending = 0; LOGINFO("[%s] no remaining active incoming migrations -- checking to see if there are any pending migrations\n", instance->instanceId); bunchOfInstances *head = NULL; for (head = global_instances; head; head = head->next) { if ((head->instance->migration_state == MIGRATION_PREPARING) || (head->instance->migration_state == MIGRATION_READY)) { LOGINFO("[%s] is pending migration, state='%s', deferring deauthorization of migration keys\n", head->instance->instanceId, migration_state_names[head->instance->migration_state]); incoming_migrations_pending++; } } // TO-DO: Add belt and suspenders? if (!incoming_migrations_pending) { LOGINFO("[%s] no remaining incoming or pending migrations -- deauthorizing all migration client keys\n", instance->instanceId); authorize_migration_keys("-D -r", NULL, NULL, NULL, FALSE); } } sem_v(inst_sem); // Set to generic EUCA_ERROR unless already set to a more-specific error. if (ret == EUCA_OK) { ret = EUCA_ERROR; } } else { LOGERROR("unexpected migration request (node %s is neither source nor destination)\n", pMeta->nodeName); ret = EUCA_ERROR; } } return ret; }
//! //! Defines the thread that does the actual reboot of an instance. //! //! @param[in] arg a transparent pointer to the argument passed to this thread handler //! //! @return Always return NULL //! static void *rebooting_thread(void *arg) { char *xml = NULL; char resourceName[1][MAX_SENSOR_NAME_LEN] = { "" }; char resourceAlias[1][MAX_SENSOR_NAME_LEN] = { "" }; // ncInstance *instance = ((ncInstance *) arg); ncInstance *instance = NULL; struct nc_state_t *nc = NULL; virDomainPtr dom = NULL; virConnectPtr conn = NULL; rebooting_thread_params *params = ((rebooting_thread_params *) arg); instance = &(params->instance); nc = &(params->nc); LOGDEBUG("[%s] spawning rebooting thread\n", instance->instanceId); if ((conn = lock_hypervisor_conn()) == NULL) { LOGERROR("[%s] cannot connect to hypervisor to restart instance, giving up\n", instance->instanceId); EUCA_FREE(params); return NULL; } dom = virDomainLookupByName(conn, instance->instanceId); if (dom == NULL) { LOGERROR("[%s] cannot locate instance to reboot, giving up\n", instance->instanceId); unlock_hypervisor_conn(); EUCA_FREE(params); return NULL; } // obtain the most up-to-date XML for domain from libvirt xml = virDomainGetXMLDesc(dom, 0); if (xml == NULL) { LOGERROR("[%s] cannot obtain metadata for instance to reboot, giving up\n", instance->instanceId); virDomainFree(dom); // release libvirt resource unlock_hypervisor_conn(); EUCA_FREE(params); return NULL; } virDomainFree(dom); // release libvirt resource unlock_hypervisor_conn(); // try shutdown first, then kill it if uncooperative if (shutdown_then_destroy_domain(instance->instanceId, TRUE) != EUCA_OK) { LOGERROR("[%s] failed to shutdown and destroy the instance to reboot, giving up\n", instance->instanceId); EUCA_FREE(params); return NULL; } // Add a shift to values of three of the metrics: ones that // drop back to zero after a reboot. The shift, which is based // on the latest value, ensures that values sent upstream do // not go backwards . sensor_shift_metric(instance->instanceId, "CPUUtilization"); sensor_shift_metric(instance->instanceId, "NetworkIn"); sensor_shift_metric(instance->instanceId, "NetworkOut"); if ((conn = lock_hypervisor_conn()) == NULL) { LOGERROR("[%s] cannot connect to hypervisor to restart instance, giving up\n", instance->instanceId); EUCA_FREE(params); return NULL; } // domain is now shut down, create a new one with the same XML LOGINFO("[%s] rebooting\n", instance->instanceId); if (!strcmp(nc->pEucaNet->sMode, NETMODE_VPCMIDO)) { // need to sleep to allow midolman to update the VM interface sleep(10); } dom = virDomainCreateLinux(conn, xml, 0); if (dom == NULL) { LOGERROR("[%s] failed to restart instance\n", instance->instanceId); change_state(instance, SHUTOFF); } else { euca_strncpy(resourceName[0], instance->instanceId, MAX_SENSOR_NAME_LEN); sensor_refresh_resources(resourceName, resourceAlias, 1); // refresh stats so we set base value accurately virDomainFree(dom); if (!strcmp(nc->pEucaNet->sMode, NETMODE_VPCMIDO)) { char iface[16], cmd[EUCA_MAX_PATH], obuf[256], ebuf[256], sPath[EUCA_MAX_PATH]; int rc; snprintf(iface, 16, "vn_%s", instance->instanceId); // If this device does not have a 'brport' path, this isn't a bridge device snprintf(sPath, EUCA_MAX_PATH, "/sys/class/net/%s/brport/", iface); if (!check_directory(sPath)) { LOGDEBUG("[%s] removing instance interface %s from host bridge\n", instance->instanceId, iface); snprintf(cmd, EUCA_MAX_PATH, "%s brctl delif %s %s", nc->rootwrap_cmd_path, instance->params.guestNicDeviceName, iface); rc = timeshell(cmd, obuf, ebuf, 256, 10); if (rc) { LOGERROR("unable to remove instance interface from bridge after launch: instance will not be able to connect to midonet (will not connect to network): check bridge/libvirt/kvm health\n"); } } // Repeat process for secondary interfaces as well for (int i=0; i < EUCA_MAX_NICS; i++) { if (strlen(instance->secNetCfgs[i].interfaceId) == 0) continue; snprintf(iface, 16, "vn_%s", instance->secNetCfgs[i].interfaceId); // If this device does not have a 'brport' path, this isn't a bridge device snprintf(sPath, EUCA_MAX_PATH, "/sys/class/net/%s/brport/", iface); if (!check_directory(sPath)) { LOGDEBUG("[%s] removing instance interface %s from host bridge\n", instance->instanceId, iface); snprintf(cmd, EUCA_MAX_PATH, "%s brctl delif %s %s", nc->rootwrap_cmd_path, instance->params.guestNicDeviceName, iface); rc = timeshell(cmd, obuf, ebuf, 256, 10); if (rc) { LOGERROR("unable to remove instance interface from bridge after launch: instance will not be able to connect to midonet (will not connect to network): check bridge/libvirt/kvm health\n"); } } } } } EUCA_FREE(xml); unlock_hypervisor_conn(); unset_corrid(get_corrid()); EUCA_FREE(params); return NULL; }
//! //! downloads a decrypted image from Walrus based on the manifest URL, //! saves it to outfile //! //! @param[in] walrus_op //! @param[in] verb //! @param[in] requested_url //! @param[in] outfile //! @param[in] do_compress //! @param[in] connect_timeout //! @param[in] total_timeout //! //! @return EUCA_OK on success or proper error code. Known error code returned include: EUCA_ERROR. //! static int walrus_request_timeout(const char *walrus_op, const char *verb, const char *requested_url, const char *outfile, const int do_compress, int connect_timeout, int total_timeout) { int code = EUCA_ERROR; char url[BUFSIZE]; pthread_mutex_lock(&wreq_mutex); /* lock for curl construction */ euca_strncpy(url, requested_url, BUFSIZE); #if defined(CAN_GZIP) if (do_compress) snprintf(url, BUFSIZE, "%s%s", requested_url, "?IsCompressed=true"); #endif /* CAN_GZIP */ /* isolate the PATH in the URL as it will be needed for signing */ char *url_path; if (strncasecmp(url, "http://", 7) != 0 && strncasecmp(url, "https://", 8) != 0) { logprintfl(EUCAERROR, "Walrus URL must start with http(s)://...\n"); pthread_mutex_unlock(&wreq_mutex); return code; } if ((url_path = strchr(url + 8, '/')) == NULL) { /* find first '/' after hostname */ logprintfl(EUCAERROR, "Walrus URL has no path\n"); pthread_mutex_unlock(&wreq_mutex); return code; } if (euca_init_cert()) { logprintfl(EUCAERROR, "failed to initialize certificate for Walrus request\n"); pthread_mutex_unlock(&wreq_mutex); return code; } int fd = open(outfile, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); // we do not truncate the file if (fd == -1 || lseek(fd, 0, SEEK_SET) == -1) { logprintfl(EUCAERROR, "failed to open %s for writing Walrus request\n", outfile); pthread_mutex_unlock(&wreq_mutex); if (fd >= 0) close(fd); return code; } logprintfl(EUCADEBUG, "will use URL: %s\n", url); CURL *curl; CURLcode result; curl = curl_easy_init(); if (curl == NULL) { logprintfl(EUCAERROR, "could not initialize libcurl for Walrus request\n"); close(fd); pthread_mutex_unlock(&wreq_mutex); return code; } char error_msg[CURL_ERROR_SIZE]; curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_msg); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, write_header); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); //! @todo make this optional? curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 360L); // must have at least a 360 baud modem curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 10L); // abort if below speed limit for this many seconds // curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1); //! @todo remove the comment once we want to follow redirects (e.g., on HTTP 407) if (strncmp(verb, "GET", 4) == 0) { curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); } else if (strncmp(verb, "HEAD", 5) == 0) { //! @todo HEAD isn't very useful atm since we don't look at headers curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); } else { close(fd); logprintfl(EUCAERROR, "invalid HTTP verb %s in Walrus request\n", verb); pthread_mutex_unlock(&wreq_mutex); return EUCA_ERROR; //! @todo dealloc structs before returning! } if (connect_timeout > 0) { curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, connect_timeout); } if (total_timeout > 0) { curl_easy_setopt(curl, CURLOPT_TIMEOUT, total_timeout); } /* set up the default write function, but possibly override * it below, if compression is desired and possible */ struct request params; params.fd = fd; curl_easy_setopt(curl, CURLOPT_WRITEDATA, ¶ms); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); #if defined(CAN_GZIP) if (do_compress) { curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data_zlib); } #endif /* CAN_GZIP */ struct curl_slist *headers = NULL; /* beginning of a DLL with headers */ headers = curl_slist_append(headers, "Authorization: Euca"); char op_hdr[STRSIZE]; if (walrus_op != NULL) { snprintf(op_hdr, STRSIZE, "EucaOperation: %s", walrus_op); headers = curl_slist_append(headers, op_hdr); } time_t t = time(NULL); char date_str[26]; if (ctime_r(&t, date_str) == NULL) { close(fd); pthread_mutex_unlock(&wreq_mutex); return EUCA_ERROR; } assert(strlen(date_str) + 7 <= STRSIZE); char *newline = strchr(date_str, '\n'); if (newline != NULL) { *newline = '\0'; } // remove newline that terminates asctime() output char date_hdr[STRSIZE]; snprintf(date_hdr, STRSIZE, "Date: %s", date_str); headers = curl_slist_append(headers, date_hdr); char *cert_str = euca_get_cert(0); /* read the cloud-wide cert */ if (cert_str == NULL) { close(fd); pthread_mutex_unlock(&wreq_mutex); return EUCA_ERROR; } char *cert64_str = base64_enc((unsigned char *)cert_str, strlen(cert_str)); assert(strlen(cert64_str) + 11 <= BUFSIZE); char cert_hdr[BUFSIZE]; snprintf(cert_hdr, BUFSIZE, "EucaCert: %s", cert64_str); logprintfl(EUCATRACE, "base64 certificate: %s\n", get_string_stats(cert64_str)); headers = curl_slist_append(headers, cert_hdr); EUCA_FREE(cert64_str); EUCA_FREE(cert_str); char *sig_str = euca_sign_url(verb, date_str, url_path); /* create Walrus-compliant sig */ if (sig_str == NULL) { close(fd); pthread_mutex_unlock(&wreq_mutex); return EUCA_ERROR; } assert(strlen(sig_str) + 16 <= BUFSIZE); char sig_hdr[BUFSIZE]; snprintf(sig_hdr, BUFSIZE, "EucaSignature: %s", sig_str); headers = curl_slist_append(headers, sig_hdr); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); /* register headers */ if (walrus_op) { logprintfl(EUCADEBUG, "writing %s/%s output\n", verb, walrus_op); logprintfl(EUCADEBUG, " from %s\n", url); logprintfl(EUCADEBUG, " to %s\n", outfile); } else { logprintfl(EUCADEBUG, "writing %s output to %s\n", verb, outfile); } int retries = TOTAL_RETRIES; int timeout = FIRST_TIMEOUT; do { params.total_wrote = 0L; params.total_calls = 0L; #if defined(CAN_GZIP) if (do_compress) { /* allocate zlib inflate state */ params.strm.zalloc = Z_NULL; params.strm.zfree = Z_NULL; params.strm.opaque = Z_NULL; params.strm.avail_in = 0; params.strm.next_in = Z_NULL; params.ret = inflateInit2(&(params.strm), 31); if (params.ret != Z_OK) { zerr(params.ret, "walrus_request"); break; } } #endif /* CAN_GZIP */ //! @todo There used to be a 'pthread_mutex_unlock(&wreq_mutex)' before curl invocation //! and a 'lock' after it, but under heavy load we were seeing failures inside //! libcurl code that would propagate to NC, implying lack of thread safety in //! the library. For now, we will serialize all curl operations, but in the future //! an approach to parallelizing Walrus downloads is necessary result = curl_easy_perform(curl); /* do it */ logprintfl(EUCADEBUG, "wrote %lld byte(s) in %lld write(s)\n", params.total_wrote, params.total_calls); #if defined(CAN_GZIP) if (do_compress) { inflateEnd(&(params.strm)); if (params.ret != Z_STREAM_END) { zerr(params.ret, "walrus_request"); } } #endif /* CAN_GZIP */ if (result) { // curl error (connection or transfer failed) logprintfl(EUCAERROR, "curl error: %s (%d)\n", error_msg, result); } else { long httpcode; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpcode); //! @todo pull out response message, too switch (httpcode) { case 200L: /* all good */ logprintfl(EUCAINFO, "downloaded %s\n", outfile); code = EUCA_OK; break; case 408L: /* timeout, retry */ logprintfl(EUCAWARN, "server responded with HTTP code %ld (timeout) for %s\n", httpcode, url); //logcat (EUCADEBUG, outfile); /* dump the error from outfile into the log */ break; default: /* some kind of error */ logprintfl(EUCAERROR, "server responded with HTTP code %ld for %s\n", httpcode, url); //logcat (EUCADEBUG, outfile); /* dump the error from outfile into the log */ retries = 0; break; } } if (code != EUCA_OK && retries > 0) { logprintfl(EUCAWARN, "download retry %d of %d will commence in %d sec for %s\n", retries, TOTAL_RETRIES, timeout, url); sleep(timeout); lseek(fd, 0L, SEEK_SET); timeout <<= 1; if (timeout > MAX_TIMEOUT) timeout = MAX_TIMEOUT; } retries--; } while (code != EUCA_OK && retries > 0); close(fd); if (code != EUCA_OK) { logprintfl(EUCAWARN, "removing %s\n", outfile); remove(outfile); } EUCA_FREE(sig_str); curl_slist_free_all(headers); curl_easy_cleanup(curl); pthread_mutex_unlock(&wreq_mutex); return code; }
//! //! Allocate and initialize an instance structure with given information. Instances are //! present in instance-related requests. //! //! @param[in] sUUID the unique user identifier string //! @param[in] sInstanceId the instance identifier string (i-XXXXXXXX) //! @param[in] sReservationId the reservation identifier string //! @param[in] pVirtMachine a pointer to our virtual machine parametes //! @param[in] sStateName the current instance state name string //! @param[in] stateCode the current instance state code //! @param[in] sUserId the user identifier string //! @param[in] sOwnerId the owner identifier string //! @param[in] sAccountId the account identifier string //! @param[in] pNetCfg a pointer to the network configuration of this instance //! @param[in] sKeyName the SSH key name to use //! @param[in] sUserData user data string to pass to the instance //! @param[in] sLaunchIndex the instance's launch index //! @param[in] sPlatform the instance's platform type //! @param[in] expiryTime the instance's expiration time before it reaches running //! @param[in] asGroupNames an array list of group name string //! @param[in] groupNamesSize the number of group name in the asGroupNames list //! @param[in] asGroupIds an array list of group identifier string //! @param[in] groupIdsSize the number of group name in the asGroupIds list //! //! @return a pointer to the newly allocated instance structure or NULL if any error occured. //! //! @see add_instance() //! @see free_instance() //! //! @post On succes an instance structure is allocated and initialized with the given information. //! ncInstance *allocate_instance(const char *sUUID, const char *sInstanceId, const char *sReservationId, virtualMachine * pVirtMachine, const char *sStateName, int stateCode, const char *sUserId, const char *sOwnerId, const char *sAccountId, netConfig * pNetCfg, const char *sKeyName, const char *sUserData, const char *sLaunchIndex, const char *sPlatform, int expiryTime, char **asGroupNames, int groupNamesSize, char **asGroupIds, int groupIdsSize) { u32 i = 0; ncInstance *pInstance = NULL; /* zeroed out for cleaner-looking checkpoints and strings that are empty unless set */ if ((pInstance = EUCA_ZALLOC(1, sizeof(ncInstance))) == NULL) return (NULL); if (sUserData) euca_strncpy(pInstance->userData, sUserData, CHAR_BUFFER_SIZE * 32); if (sLaunchIndex) euca_strncpy(pInstance->launchIndex, sLaunchIndex, CHAR_BUFFER_SIZE); if (sPlatform) euca_strncpy(pInstance->platform, sPlatform, CHAR_BUFFER_SIZE); pInstance->groupNamesSize = groupNamesSize; if ((asGroupNames != NULL) && (groupNamesSize > 0)) { for (i = 0; i < groupNamesSize && asGroupNames[i]; i++) euca_strncpy(pInstance->groupNames[i], asGroupNames[i], CHAR_BUFFER_SIZE); } pInstance->groupIdsSize = groupIdsSize; if ((asGroupIds != NULL) && (groupIdsSize > 0)) { for (i = 0; i < groupIdsSize && asGroupIds[i]; i++) euca_strncpy(pInstance->groupIds[i], asGroupIds[i], CHAR_BUFFER_SIZE); } if (pNetCfg != NULL) memcpy(&(pInstance->ncnet), pNetCfg, sizeof(netConfig)); if (sUUID) euca_strncpy(pInstance->uuid, sUUID, CHAR_BUFFER_SIZE); if (sInstanceId) euca_strncpy(pInstance->instanceId, sInstanceId, CHAR_BUFFER_SIZE); if (sKeyName) euca_strncpy(pInstance->keyName, sKeyName, CHAR_BUFFER_SIZE * 4); if (sReservationId) euca_strncpy(pInstance->reservationId, sReservationId, CHAR_BUFFER_SIZE); if (sStateName) euca_strncpy(pInstance->stateName, sStateName, CHAR_BUFFER_SIZE); if (sUserId) euca_strncpy(pInstance->userId, sUserId, CHAR_BUFFER_SIZE); if (sOwnerId) euca_strncpy(pInstance->ownerId, sOwnerId, CHAR_BUFFER_SIZE); if (sAccountId) euca_strncpy(pInstance->accountId, sAccountId, CHAR_BUFFER_SIZE); if (pVirtMachine) memcpy(&(pInstance->params), pVirtMachine, sizeof(virtualMachine)); pInstance->stateCode = stateCode; euca_strncpy(pInstance->bundleTaskStateName, bundling_progress_names[NOT_BUNDLING], CHAR_BUFFER_SIZE); pInstance->expiryTime = expiryTime; return (pInstance); }