1
// -*- mode: C; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-
2
// vim: set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
5
Copyright (c) 2009 Eucalyptus Systems, Inc.
7
This program is free software: you can redistribute it and/or modify
8
it under the terms of the GNU General Public License as published by
9
the Free Software Foundation, only version 3 of the License.
11
This file is distributed in the hope that it will be useful, but WITHOUT
12
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16
You should have received a copy of the GNU General Public License along
17
with this program. If not, see <http://www.gnu.org/licenses/>.
19
Please contact Eucalyptus Systems, Inc., 130 Castilian
20
Dr., Goleta, CA 93101 USA or visit <http://www.eucalyptus.com/licenses/>
21
if you need additional information or have any questions.
23
This file may incorporate work covered under the following copyright and
26
Software License Agreement (BSD License)
28
Copyright (c) 2008, Regents of the University of California
31
Redistribution and use of this software in source and binary forms, with
32
or without modification, are permitted provided that the following
35
Redistributions of source code must retain the above copyright notice,
36
this list of conditions and the following disclaimer.
38
Redistributions in binary form must reproduce the above copyright
39
notice, this list of conditions and the following disclaimer in the
40
documentation and/or other materials provided with the distribution.
42
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
43
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
44
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
45
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
46
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
47
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
48
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
49
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
50
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
51
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
52
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. USERS OF
53
THIS SOFTWARE ACKNOWLEDGE THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE
54
LICENSED MATERIAL, COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS
55
SOFTWARE, AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
56
IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, SANTA
57
BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, WHICH IN
58
THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
59
OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO IDENTIFIED, OR
60
WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT NEEDED TO COMPLY WITH
61
ANY SUCH LICENSES OR RIGHTS.
68
#include <sys/types.h>
76
#include "misc.h" // logprintfl, ensure_...
77
#include "data.h" // ncInstance
79
#include "eucalyptus.h"
80
#include "blobstore.h"
82
#include "storage-windows.h"
83
#include "handlers.h" // nc_state
87
#include "ipc.h" // sem
89
#define CACHE_TIMEOUT_USEC 1000000LL*60*60*2
90
#define STORE_TIMEOUT_USEC 1000000LL*60*2
91
#define DELETE_TIMEOUT_USEC 1000000LL*10
92
#define FIND_TIMEOUT_USEC 50000LL // TODO: use 1000LL or less to induce rare timeouts
94
static char instances_path [MAX_PATH];
95
static blobstore * cache_bs = NULL;
96
static blobstore * work_bs = NULL;
97
static sem * disk_sem = NULL;
99
extern struct nc_state_t nc_state;
101
static void bs_errors (const char * msg) {
102
// we normally do not care to print all messages from blobstore as many are errors that we can handle
103
logprintfl (EUCADEBUG2, "{%u} blobstore: %s", (unsigned int)pthread_self(), msg);
106
static void stat_blobstore (const char * conf_instances_path, const char * name, blobstore_meta * meta)
108
bzero (meta, sizeof (blobstore_meta));
109
char path [MAX_PATH];
110
snprintf (path, sizeof (path), "%s/%s", conf_instances_path, name);
111
blobstore * bs = blobstore_open (path,
113
0, // no flags = do not create it
114
BLOBSTORE_FORMAT_ANY,
115
BLOBSTORE_REVOCATION_ANY,
116
BLOBSTORE_SNAPSHOT_ANY);
119
blobstore_stat (bs, meta);
120
blobstore_close (bs);
123
static int stale_blob_examiner (const blockblob * bb);
124
static bunchOfInstances ** instances = NULL;
126
int check_backing_store (bunchOfInstances ** global_instances)
128
instances = global_instances;
131
if (blobstore_fsck (work_bs, stale_blob_examiner)) {
132
logprintfl (EUCAERROR, "ERROR: work directory failed integrity check: %s\n", blobstore_get_error_str(blobstore_get_error()));
133
blobstore_close (cache_bs);
138
if (blobstore_fsck (cache_bs, NULL)) { // TODO: verify checksums?
139
logprintfl (EUCAERROR, "ERROR: cache failed integrity check: %s\n", blobstore_get_error_str(blobstore_get_error()));
146
void stat_backing_store (const char * conf_instances_path, blobstore_meta * work_meta, blobstore_meta * cache_meta)
148
assert (conf_instances_path);
149
stat_blobstore (conf_instances_path, "work", work_meta);
150
stat_blobstore (conf_instances_path, "cache", cache_meta);
153
int init_backing_store (const char * conf_instances_path, unsigned int conf_work_size_mb, unsigned int conf_cache_size_mb)
155
logprintfl (EUCAINFO, "initializing backing store...\n");
157
if (conf_instances_path == NULL) {
158
logprintfl (EUCAERROR, "error: INSTANCE_PATH not specified\n");
161
safe_strncpy (instances_path, conf_instances_path, sizeof (instances_path));
162
if (check_directory (instances_path)) {
163
logprintfl (EUCAERROR, "error: INSTANCE_PATH (%s) does not exist!\n", instances_path);
166
char cache_path [MAX_PATH]; snprintf (cache_path, sizeof (cache_path), "%s/cache", instances_path);
167
if (ensure_directories_exist (cache_path, 0, NULL, NULL, BACKING_DIRECTORY_PERM) == -1) return ERROR;
168
char work_path [MAX_PATH]; snprintf (work_path, sizeof (work_path), "%s/work", instances_path);
169
if (ensure_directories_exist (work_path, 0, NULL, NULL, BACKING_DIRECTORY_PERM) == -1) return ERROR;
170
unsigned long long cache_limit_blocks = conf_cache_size_mb * 2048; // convert MB to blocks
171
unsigned long long work_limit_blocks = conf_work_size_mb * 2048;
172
if (work_limit_blocks==0) { // we take 0 as unlimited
173
work_limit_blocks = ULLONG_MAX;
176
blobstore_set_error_function ( &bs_errors );
177
if (cache_limit_blocks) {
178
cache_bs = blobstore_open (cache_path, cache_limit_blocks, BLOBSTORE_FLAG_CREAT, BLOBSTORE_FORMAT_DIRECTORY, BLOBSTORE_REVOCATION_LRU, BLOBSTORE_SNAPSHOT_ANY);
179
if (cache_bs==NULL) {
180
logprintfl (EUCAERROR, "ERROR: failed to open/create cache blobstore: %s\n", blobstore_get_error_str(blobstore_get_error()));
184
work_bs = blobstore_open (work_path, work_limit_blocks, BLOBSTORE_FLAG_CREAT, BLOBSTORE_FORMAT_FILES, BLOBSTORE_REVOCATION_NONE, BLOBSTORE_SNAPSHOT_ANY);
186
logprintfl (EUCAERROR, "ERROR: failed to open/create work blobstore: %s\n", blobstore_get_error_str(blobstore_get_error()));
187
logprintfl (EUCAERROR, "ERROR: %s\n", blobstore_get_last_trace());
188
blobstore_close (cache_bs);
192
// set the initial value of the semaphore to the number of
193
// disk-intensive operations that can run in parallel on this node
194
if (nc_state.concurrent_disk_ops && (disk_sem = sem_alloc (nc_state.concurrent_disk_ops, "mutex")) == NULL) {
195
logprintfl (EUCAERROR, "failed to create and initialize disk semaphore\n");
203
// - the blockblob ID of an instance-directory blob (if vbr!=NULL): userId/instanceId/blob-....
204
// - the work prefix within work blobstore for an instance: userId/instanceId
205
static void set_id (const ncInstance * instance, virtualBootRecord * vbr, char * id, unsigned int id_size) // TODO: remove this
209
assert (strlen (instance->userId));
210
assert (strlen (instance->instanceId));
212
char suffix [1024] = "";
215
assert (strlen (vbr->typeName));
217
snprintf (id, id_size, "/blob-%s-%s",
219
(vbr->type==NC_RESOURCE_KERNEL||vbr->type==NC_RESOURCE_RAMDISK)?(vbr->id):(vbr->guestDeviceName));
221
snprintf (id, id_size, "%s/%s%s", instance->userId, instance->instanceId, suffix);
225
// - the work prefix within work blobstore for an instance: userId/instanceId(suffix)
226
static void set_id2 (const ncInstance * instance, const char * suffix, char * id, unsigned int id_size)
230
assert (strlen (instance->userId));
231
assert (strlen (instance->instanceId));
232
snprintf (id, id_size, "%s/%s%s", instance->userId, instance->instanceId, (suffix)?(suffix):(""));
236
// - the path of a file in an instance directory (if filename!=NULL)
237
// - the path of the instance directory (if instance!=NULL)
238
// - the path where all instance directories are kept
239
// this function must be kept consistent with set_id() below
240
static void set_path (char * path, unsigned int path_size, const ncInstance * instance, const char * filename)
242
assert (strlen (instances_path));
244
assert (strlen (instance->userId));
245
assert (strlen (instance->instanceId));
247
set_id (instance, NULL, buf, sizeof (buf));
249
snprintf (path, path_size, "%s/work/%s/%s", instances_path, buf, filename);
251
snprintf (path, path_size, "%s/work/%s", instances_path, buf);
254
snprintf (path, path_size, "%s/work", instances_path);
258
static int stale_blob_examiner (const blockblob * bb)
260
char work_path [MAX_PATH];
262
set_path (work_path, sizeof (work_path), NULL, NULL);
263
int work_path_len = strlen (work_path);
264
assert (work_path_len > 0);
266
char * s = strstr(bb->blocks_path, work_path);
267
if (s==NULL || s!=bb->blocks_path)
268
return 0; // blob not under work blobstore path
270
// parse the path past the work directory base
271
safe_strncpy (work_path, bb->blocks_path, sizeof (work_path));
272
s = work_path + work_path_len + 1;
273
char * user_id = strtok (s, "/");
274
char * inst_id = strtok (NULL, "/");
275
char * file = strtok (NULL, "/");
277
ncInstance * instance = find_instance (instances, inst_id);
278
if (instance == NULL) { // not found among running instances => stale
279
// while we're here, try to delete extra files that aren't managed by the blobstore
280
// TODO: ensure we catch any other files - perhaps by performing this cleanup after all blobs are deleted
281
char path [MAX_PATH];
282
#define del_file(filename) snprintf (path, sizeof (path), "%s/work/%s/%s/%s", instances_path, user_id, inst_id, filename); unlink (path);
283
del_file("instance.xml");
284
del_file("libvirt.xml");
285
del_file("console.log");
286
del_file("instance.checkpoint");
293
int save_instance_struct (const ncInstance * instance)
295
if (instance==NULL) {
296
logprintfl(EUCADEBUG, "save_instance_struct: NULL instance!\n");
300
char checkpoint_path [MAX_PATH];
301
set_path (checkpoint_path, sizeof (checkpoint_path), instance, "instance.checkpoint");
304
if ((fd = open (checkpoint_path, O_CREAT | O_WRONLY, BACKING_FILE_PERM)) < 0) {
305
logprintfl(EUCADEBUG, "[%s] save_instance_struct: failed to create instance checkpoint at %s\n", instance->instanceId, checkpoint_path);
309
if (write (fd, (char *)instance, sizeof(struct ncInstance_t)) != sizeof (struct ncInstance_t)) {
310
logprintfl(EUCADEBUG, "[%s] save_instance_struct: failed to write instance checkpoint at %s\n", instance->instanceId, checkpoint_path);
319
ncInstance * load_instance_struct (const char * instanceId)
321
const int meta_size = sizeof (struct ncInstance_t);
322
ncInstance * instance = calloc (1, meta_size);
323
if (instance==NULL) {
324
logprintfl (EUCADEBUG, "load_instance_struct: out of memory for instance struct\n");
327
safe_strncpy (instance->instanceId, instanceId, sizeof (instance->instanceId));
329
// we don't know userId, so we'll look for instanceId in every user's
330
// directory (we're assuming that instanceIds are unique in the system)
331
char user_paths [MAX_PATH];
332
set_path (user_paths, sizeof (user_paths), NULL, NULL);
333
DIR * insts_dir = opendir(user_paths);
334
if (insts_dir == NULL) {
335
logprintfl (EUCADEBUG, "load_instance_struct: failed to open %s\n", user_paths);
339
struct dirent * dir_entry;
340
while ((dir_entry = readdir (insts_dir)) != NULL) {
341
char tmp_path [MAX_PATH];
344
snprintf(tmp_path, sizeof (tmp_path), "%s/%s/%s", user_paths, dir_entry->d_name, instance->instanceId);
345
if (stat(tmp_path, &mystat)==0) {
346
safe_strncpy (instance->userId, dir_entry->d_name, sizeof (instance->userId));
350
closedir (insts_dir);
352
if (strlen(instance->userId)<1) {
353
logprintfl (EUCADEBUG, "load_instance_struct: didn't find instance %s\n", instance->instanceId);
358
char checkpoint_path [MAX_PATH];
359
set_path (checkpoint_path, sizeof (checkpoint_path), instance, "instance.checkpoint");
360
if ((fd = open(checkpoint_path, O_RDONLY)) < 0
361
|| read (fd, instance, meta_size) < meta_size) {
362
logprintfl(EUCADEBUG, "load_instance_struct: failed to load metadata for %s from %s: %s\n", instance->instanceId, checkpoint_path, strerror (errno));
368
instance->stateCode = NO_STATE;
369
// clear out pointers, since they are now wrong
370
instance->params.root = NULL;
371
instance->params.kernel = NULL;
372
instance->params.ramdisk = NULL;
373
instance->params.swap = NULL;
374
instance->params.ephemeral0 = NULL;
375
vbr_parse (&(instance->params), NULL); // fix up the pointers
379
if (instance) free (instance);
383
int create_instance_backing (ncInstance * instance)
386
virtualMachine * vm = &(instance->params);
388
// ensure instance directory exists
389
set_path (instance->instancePath, sizeof (instance->instancePath), instance, NULL);
390
if (ensure_directories_exist (instance->instancePath, 0, NULL, "root", BACKING_DIRECTORY_PERM) == -1)
393
// set various instance-directory-relative paths in the instance struct
394
set_path (instance->xmlFilePath, sizeof (instance->xmlFilePath), instance, "instance.xml");
395
set_path (instance->libvirtFilePath, sizeof (instance->libvirtFilePath), instance, "libvirt.xml");
396
set_path (instance->consoleFilePath, sizeof (instance->consoleFilePath), instance, "console.log");
397
if (strstr (instance->platform, "windows")) {
398
// generate the floppy file for windows instances
399
if (makeWindowsFloppy (nc_state.home, instance->instancePath, instance->keyName, instance->instanceId)) {
400
logprintfl (EUCAERROR, "[%s] error: could not create windows bootup script floppy\n", instance->instanceId);
403
set_path (instance->floppyFilePath, sizeof (instance->floppyFilePath), instance, "floppy");
407
char work_prefix [1024]; // {userId}/{instanceId}
408
set_id (instance, NULL, work_prefix, sizeof (work_prefix));
410
// compute tree of dependencies
411
artifact * sentinel = vbr_alloc_tree (vm, // the struct containing the VBR
412
FALSE, // for Xen and KVM we do not need to make disk bootable
413
TRUE, // make working copy of runtime-modifiable files
414
(instance->do_inject_key)?(instance->keyName):(NULL), // the SSH key
415
instance->instanceId); // ID is for logging
416
if (sentinel == NULL) {
417
logprintfl (EUCAERROR, "[%s] error: failed to prepare backing for instance\n", instance->instanceId);
422
// download/create/combine the dependencies
423
int rc = art_implement_tree (sentinel, work_bs, cache_bs, work_prefix, INSTANCE_PREP_TIMEOUT_USEC);
427
logprintfl (EUCAERROR, "[%s] error: failed to implement backing for instance\n", instance->instanceId);
431
if (save_instance_struct (instance)) // update instance checkpoint now that the struct got updated
441
int clone_bundling_backing (ncInstance *instance, const char* filePrefix, char* blockPath)
444
char work_regex [1024];
445
char id [BLOBSTORE_MAX_PATH];
446
char workPath [BLOBSTORE_MAX_PATH];
449
blockblob *src_blob = NULL, *dest_blob = NULL;
450
blockblob_meta *matches = NULL;
452
set_path (path, sizeof (path), instance, NULL);
453
set_id2 (instance, "/.*", work_regex, sizeof (work_regex));
455
if( (found=blobstore_search (work_bs, work_regex, &matches) <= 0 ) ) {
456
logprintfl (EUCAERROR, "[%s] error: failed to find blob in %s %d\n", instance->instanceId, path, found);
460
for (blockblob_meta * bm = matches; bm; bm=bm->next) {
461
blockblob * bb = blockblob_open (work_bs, bm->id, 0, 0, NULL, FIND_TIMEOUT_USEC);
462
if (bb!=NULL && bb->snapshot_type == BLOBSTORE_SNAPSHOT_DM && strstr(bb->blocks_path,"emi-") != NULL) { // root image contains substr 'emi-'
465
} else if (bb!=NULL) {
470
logprintfl (EUCAERROR, "[%s] couldn't find the blob to clone from", instance->instanceId);
473
set_id (instance, NULL, workPath, sizeof (workPath));
474
snprintf (id, sizeof(id), "%s/%s", workPath, filePrefix);
476
// open destination blob
477
dest_blob = blockblob_open (work_bs, id, src_blob->size_bytes, BLOBSTORE_FLAG_CREAT | BLOBSTORE_FLAG_EXCL, NULL, FIND_TIMEOUT_USEC);
479
logprintfl (EUCAERROR, "[%s] couldn't create the destination blob for bundling (%s)", instance->instanceId, id);
483
if (strlen (dest_blob->blocks_path) > 0)
484
snprintf (blockPath, MAX_PATH, "%s", dest_blob->blocks_path);
486
// copy blob (will 'dd' eventually)
487
if (blockblob_copy (src_blob, 0, dest_blob, 0, src_blob->size_bytes) != OK) {
488
logprintfl (EUCAERROR, "[%s] couldn't copy block blob for bundling (%s)", instance->instanceId, id);
496
// free the search results
497
for (blockblob_meta * bm = matches; bm;) {
498
blockblob_meta * next = bm->next;
504
blockblob_close(src_blob);
506
blockblob_close(dest_blob);
510
int destroy_instance_backing (ncInstance * instance, int do_destroy_files)
513
int total_prereqs = 0;
514
char path [MAX_PATH];
515
virtualMachine * vm = &(instance->params);
517
// find and detach iSCSI targets, if any
518
for (int i=0; i<EUCA_MAX_VBRS && i<vm->virtualBootRecordLen; i++) {
519
virtualBootRecord * vbr = &(vm->virtualBootRecord[i]);
520
if (vbr->locationType==NC_LOCATION_IQN) {
521
if (disconnect_iscsi_target (vbr->resourceLocation)) {
522
logprintfl(EUCAERROR, "[%s] error: failed to disconnect iSCSI target attached to %s\n", instance->instanceId, vbr->backingPath);
527
// see if instance directory is there (sometimes startup fails before it is created)
528
set_path (path, sizeof (path), instance, NULL);
529
if (check_path (path))
532
// to ensure that we are able to delete all blobs, we chown files back to 'eucalyptus'
533
// (e.g., libvirt on KVM on Maverick chowns them to libvirt-qemu while
534
// VM is running and then chowns them to root after termination)
535
set_path (path, sizeof (path), instance, "*");
536
if (diskutil_ch (path, EUCALYPTUS_ADMIN, NULL, BACKING_FILE_PERM)) {
537
logprintfl (EUCAWARN, "[%s] error: failed to chown files before cleanup\n", instance->instanceId);
540
if (do_destroy_files) {
541
char work_regex [1024]; // {userId}/{instanceId}/.*
542
set_id2 (instance, "/.*", work_regex, sizeof (work_regex));
544
if (blobstore_delete_regex (work_bs, work_regex) == -1) {
545
logprintfl (EUCAERROR, "[%s] error: failed to remove some artifacts in %s\n", instance->instanceId, path);
548
// remove the known leftover files
549
unlink (instance->xmlFilePath);
550
unlink (instance->libvirtFilePath);
551
unlink (instance->consoleFilePath);
552
if (strlen (instance->floppyFilePath)) {
553
unlink (instance->floppyFilePath);
555
set_path (path, sizeof (path), instance, "instance.checkpoint");
557
for (int i=0; i < EUCA_MAX_VOLUMES; ++i) {
558
ncVolume * volume = &instance->volumes[i];
559
snprintf (path, sizeof (path), EUCALYPTUS_VOLUME_XML_PATH_FORMAT, instance->instancePath, volume->volumeId);
562
// bundle instance will leave additional files
563
// let's delete every file in the directory
564
struct dirent **files;
565
int n = scandir(instance->instancePath, &files, 0, alphasort);
566
char toDelete[MAX_PATH];
569
struct dirent *entry = files[n];
570
if( entry !=NULL && strncmp(entry->d_name, ".",1)!=0 && strncmp(entry->d_name, "..", 2)!=0){
571
snprintf(toDelete, MAX_PATH, "%s/%s", instance->instancePath, entry->d_name);
580
// Finally try to remove the directory.
581
// If either the user or our code introduced
582
// any new files, this last step will fail.
583
set_path (path, sizeof (path), instance, NULL);
584
if (rmdir (path) && do_destroy_files) {
585
logprintfl (EUCAWARN, "[%s] warning: failed to remove backing directory %s\n", instance->instanceId, path);