3
#include <string.h> /* strlen, strcpy */
5
#include <limits.h> /* INT_MAX */
6
#include <sys/types.h> /* fork */
7
#include <sys/wait.h> /* waitpid */
14
#include <sys/vfs.h> /* statfs */
15
#include <signal.h> /* SIGINT */
21
#include <eucalyptus.h>
22
#include <libvirt/libvirt.h>
23
#include <libvirt/virterror.h>
25
#include <euca_auth.h>
27
#define BUFSIZE 512 /* random buffer size used all over the place */
29
/* resource limits, in MB, will be determined by hardware if set to 0 */
30
static int config_max_disk = 0;
31
static int config_max_mem = 0;
32
static int config_max_cores = 0;
34
/* actual resource limits of the system, as determined at NC startup */
35
static long long mem_max = 0;
36
static long long disk_max = 0;
37
static int cores_max = 0;
39
/* network configuration defaults (may be overriden from config file) */
40
static char config_network_path [BUFSIZE];
41
static int config_network_port = NC_NET_PORT_DEFAULT;
43
static char * admin_user_id = EUCALYPTUS_ADMIN;
44
static char gen_libvirt_xml_command_path [BUFSIZE] = "";
45
static char get_xen_info_command_path [BUFSIZE] = "";
47
#define BYTES_PER_DISK_UNIT 1048576 /* disk stats are in Gigs */
48
#define SWAP_SIZE 512 /* for now, the only possible swap size, in MBs */
49
#define MAXDOMS 1024 /* max number of running domains on node */
51
vnetConfig *vnetconfig = NULL;
52
sem * xen_sem; /* semaphore for serializing domain creation */
53
sem * inst_sem; /* semaphore for guarding access to global instance structs */
54
bunchOfInstances * global_instances = NULL; /* will be initiated upon first call */
56
const int unbooted_cleanup_threshold = 60 * 60 * 2; /* after this many seconds any unbooted and SHUTOFF domains will be cleaned up */
57
const int teardown_state_duration = 60; /* after this many seconds in TEARDOWN state (no resources), we'll forget about the instance */
58
const int monitoring_period_duration = 5; /* how frequently we check on instances */
60
static void libvirt_error_handler (void * userData, virErrorPtr error)
63
logprintfl (EUCAERROR, "libvirt error handler was given a NULL pointer\n");
65
/* these are common, they appear for evey non-existing domain,
66
* such as BOOTING/CRASHED/SHUTOFF, which we catch elsewhere,
67
* so we won't print them */
68
if (error->code==10) {
71
logprintfl (EUCAERROR, "libvirt: %s (code=%d)\n", error->message, error->code);
75
static void change_state (ncInstance * instance, instance_states state)
77
instance->state = (int) state;
78
switch (state) { /* mapping from NC's internal states into external ones */
80
instance->stateCode = PENDING;
88
instance->stateCode = EXTANT;
91
instance->stateCode = TEARDOWN;
94
logprintfl (EUCAERROR, "error: change_sate(): unexpected state (%d) for instance %s\n", instance->state, instance->instanceId);
98
strncpy(instance->stateName, instance_state_names[instance->stateCode], CHAR_BUFFER_SIZE);
101
static void refresh_instance_info (ncInstance * instance)
103
int now = instance->state;
105
/* no need to bug Xen for domains without state */
109
/* try to get domain state from Xen */
110
virConnectPtr conn = virConnectOpen ("xen:///"); /* NULL means local hypervisor */
112
logprintfl (EUCAERROR, "warning: failed to connect to hypervisor\n");
115
virDomainPtr dom = virDomainLookupByName (conn, instance->instanceId);
116
if (dom == NULL) { /* Xen doesn't know about it */
121
/* Most likely the user has shut it down from the inside */
122
logprintfl (EUCAWARN, "warning: hypervisor failed to find domain %s, assuming it was shut off\n", instance->instanceId);
123
change_state (instance, SHUTOFF);
125
/* else 'now' stays in SHUTFOFF, BOOTING or CRASHED */
126
virConnectClose (conn);
130
int error = virDomainGetInfo(dom, &info);
131
if (error < 0 || info.state == VIR_DOMAIN_NOSTATE) {
132
logprintfl (EUCAWARN, "warning: failed to get informations for domain %s\n", instance->instanceId);
133
/* what to do? hopefully we'll find out more later */
135
virConnectClose (conn);
138
int xen = info.state;
145
/* change to Xen's state, whatever it happens to be */
146
change_state (instance, xen);
154
/* cannot go back! */
155
logprintfl (EUCAWARN, "warning: detected prodigal domain %s, terminating it\n", instance->instanceId);
157
virDomainDestroy (dom);
160
change_state (instance, xen);
164
logprintfl (EUCAERROR, "error: refresh...(): unexpected state (%d) for instance %s\n", now, instance->instanceId);
168
virConnectClose(conn);
170
/* if instance is running, try to find out its IP address */
171
if (instance->state==RUNNING ||
172
instance->state==BLOCKED ||
173
instance->state==PAUSED) {
177
if (!strncmp(instance->ncnet.publicIp, "0.0.0.0", 32)) {
178
rc = discover_mac(vnetconfig, instance->ncnet.publicMac, &ip);
180
logprintfl (EUCAINFO, "discovered public IP %s for instance %s\n", ip, instance->instanceId);
181
strncpy(instance->ncnet.publicIp, ip, 32);
184
if (!strncmp(instance->ncnet.privateIp, "0.0.0.0", 32)) {
185
rc = discover_mac(vnetconfig, instance->ncnet.privateMac, &ip);
187
logprintfl (EUCAINFO, "discovered private IP %s for instance %s\n", ip, instance->instanceId);
188
strncpy(instance->ncnet.privateIp, ip, 32);
194
static void print_running_domains (void)
196
bunchOfInstances * head;
197
char buf [BUFSIZE] = "";
199
for ( head=global_instances; head; head=head->next ) {
200
ncInstance * instance = head->instance;
201
if (instance->state==BOOTING
202
|| instance->state==RUNNING
203
|| instance->state==BLOCKED
204
|| instance->state==PAUSED) {
206
strcat (buf, instance->instanceId);
210
logprintfl (EUCAINFO, "currently running/booting: %s\n", buf);
213
static void * monitoring_thread (void *arg)
218
bunchOfInstances * head;
219
time_t now = time(NULL);
223
for ( head = global_instances; head; head = head->next ) {
224
ncInstance * instance = head->instance;
226
/* query Xen for current state, if any */
227
refresh_instance_info (instance);
228
/* don't touch running threads */
229
if (instance->state!=BOOTING &&
230
instance->state!=SHUTOFF &&
231
instance->state!=SHUTDOWN &&
232
instance->state!=TEARDOWN) continue;
234
if (instance->state==TEARDOWN) {
235
/* it's been long enugh, we can fugetaboutit */
236
if ((now - instance->terminationTime)>teardown_state_duration) {
237
remove_instance (&global_instances, instance);
238
logprintfl (EUCAINFO, "forgetting about instance %s\n", instance->instanceId);
239
free_instance (&instance);
240
goto restart; /* reset the head since we modified the list */
245
if (instance->state==BOOTING &&
246
(now - instance->launchTime)<unbooted_cleanup_threshold) /* hasn't been long enough */
247
continue; /* let it be */
249
/* ok, it's been condemned => destroy the files */
250
if (scCleanupInstanceImage(instance->userId, instance->instanceId)) {
251
logprintfl (EUCAWARN, "warning: failed to cleanup instance image %d\n", instance->instanceId);
254
/* check to see if this is the last instance running on vlan */
256
bunchOfInstances * vnhead;
257
for (vnhead = global_instances; vnhead; vnhead = vnhead->next ) {
258
ncInstance * vninstance = vnhead->instance;
259
if (vninstance->ncnet.vlan == (instance->ncnet).vlan
260
&& strcmp(instance->instanceId, vninstance->instanceId)) {
265
logprintfl (EUCAINFO, "stopping the network (vlan=%d)\n", (instance->ncnet).vlan);
266
vnetStopNetwork (vnetconfig, (instance->ncnet).vlan, NULL, NULL);
268
change_state (instance, TEARDOWN); /* TEARDOWN = no more resources */
269
instance->terminationTime = time (NULL);
273
sleep (monitoring_period_duration);
279
static int get_value (char * s, const char * name, long long * valp)
282
if (s==NULL || name==NULL || valp==NULL) return ERROR;
283
snprintf (buf, BUFSIZE, "%s=%%lld", name);
284
return (sscanf_lines (s, buf, valp)==1 ? OK : ERROR);
287
static int doInitialize (void)
290
char config [BUFSIZE];
291
char *brname, *s, *home, *pubInterface, *bridge, *mode;
294
/* read in configuration - this should be first! */
296
home = getenv (EUCALYPTUS_ENV_VAR_NAME);
298
home = strdup (""); /* root by default */
301
home = strdup (home);
304
snprintf(config, BUFSIZE, "%s/var/log/eucalyptus/nc.log", home);
305
logfile(config, EUCADEBUG); // TODO: right level?
307
logprintfl (EUCAWARN, "env variable %s not set, using /\n", EUCALYPTUS_ENV_VAR_NAME);
309
snprintf(config, BUFSIZE, EUCALYPTUS_CONF_LOCATION, home);
310
if (stat(config, &mystat)==0) {
311
logprintfl (EUCAINFO, "NC is looking for configuration in %s\n", config);
313
#define GET_VAR_INT(var,name) \
314
if (get_conf_var(config, name, &s)>0){\
319
GET_VAR_INT(config_max_mem, CONFIG_MAX_MEM);
320
GET_VAR_INT(config_max_disk, CONFIG_MAX_DISK);
321
GET_VAR_INT(config_max_cores, CONFIG_MAX_CORES);
324
if ((xen_sem = sem_alloc (1, "eucalyptus-nc-xen-semaphore")) == NULL
325
|| (inst_sem = sem_alloc (1, "eucalyptus-nc-inst-semaphore")) == NULL) {
326
logprintfl (EUCAFATAL, "failed to create and initialize a semaphore\n");
330
/* prompt the SC to read the configuration, too */
333
logprintfl (EUCAFATAL, "ERROR: scInitConfig() failed\n");
337
/* set up paths of Eucalyptus commands NC relies on */
338
snprintf (gen_libvirt_xml_command_path, BUFSIZE, EUCALYPTUS_GEN_LIBVIRT_XML, home, home);
339
snprintf (get_xen_info_command_path, BUFSIZE, EUCALYPTUS_GET_XEN_INFO, home, home);
341
/* "adopt" currently running Xen instances */
343
virConnectPtr conn = NULL;
344
int dom_ids[MAXDOMS];
347
logprintfl (EUCAINFO, "looking for existing Xen domains\n");
348
virSetErrorFunc (NULL, libvirt_error_handler);
351
conn = virConnectOpen("xen:///"); /* NULL means local hypervisor */
353
logprintfl (EUCAFATAL, "Failed to connect to hypervisor\n");
358
num_doms = virConnectListDomains(conn, dom_ids, MAXDOMS);
360
virDomainPtr dom = NULL;
363
for ( i=0; i<num_doms; i++) {
364
dom = virDomainLookupByID(conn, dom_ids[i]);
369
const char * dom_name;
370
ncInstance * instance;
372
error = virDomainGetInfo(dom, &info);
373
if (error < 0 || info.state == VIR_DOMAIN_NOSTATE) {
374
logprintfl (EUCAWARN, "WARNING: failed to get info on running Xen domain #%d, ignoring it\n", dom_ids[i]);
378
if (info.state == VIR_DOMAIN_SHUTDOWN ||
379
info.state == VIR_DOMAIN_SHUTOFF ||
380
info.state == VIR_DOMAIN_CRASHED ) {
381
logprintfl (EUCADEBUG, "ignoring non-running Xen domain #%d\n", dom_ids[i]);
385
if ((dom_name = virDomainGetName(dom))==NULL) {
386
logprintfl (EUCAWARN, "WARNING: failed to get name of running Xen domain #%d, ignoring it\n", dom_ids[i]);
390
if (!strcmp(dom_name, "Domain-0")) {
394
if ((instance = scRecoverInstanceInfo (dom_name))==NULL) {
395
logprintfl (EUCAWARN, "WARNING: failed to recover Eucalyptus metadata of running Xen domain %s, ignoring it\n", dom_name);
399
change_state (instance, info.state);
401
int err = add_instance (&global_instances, instance);
404
free_instance (&instance);
408
logprintfl (EUCAINFO, "- adopted running Xen domain %s from user %s\n", instance->instanceId, instance->userId);
409
/* TODO: try to look up IPs? */
413
logprintfl (EUCAWARN, "WARNING: failed to lookup running Xen domain #%d, ignoring it\n", dom_ids[i]);
416
} else if (num_doms==0) {
417
logprintfl (EUCAINFO, "no currently running Xen domains to adopt\n");
419
logprintfl (EUCAWARN, "WARNING: failed to find out about running domains\n");
421
virConnectClose (conn);
424
/* network startup */
425
vnetconfig = malloc(sizeof(vnetConfig));
426
snprintf (config_network_path, BUFSIZE, NC_NET_PATH_DEFAULT, home);
427
pubInterface = getConfString(config, "VNET_INTERFACE");
428
bridge = getConfString(config, "VNET_BRIDGE");
429
mode = getConfString(config, "VNET_MODE");
431
vnetInit(vnetconfig, mode, home, config_network_path, NC, pubInterface, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, bridge);
433
/* cleanup from previous runs and verify integrity of instances
436
long long instances_bytes = scFSCK (&global_instances);
438
if (instances_bytes<0) {
439
logprintfl (EUCAFATAL, "instances store failed integrity check (error=%d)\n", instances_bytes);
443
/* discover resource capacity */
445
const char * ipath = scGetInstancePath();
450
/* calculate disk_max */
452
long long fs_free_blocks = 0;
453
long long fs_block_size = 0;
456
if (statfs(ipath, &fs) == -1) {
457
printf ("error: failed to stat %s\n", ipath);
460
disk_max = fs.f_bsize * fs.f_bavail + instances_bytes; /* max for Euca, not total */
461
disk_max /= BYTES_PER_DISK_UNIT; /* convert bytes to the right units */
463
&& disk_max>config_max_disk)
464
disk_max=config_max_disk; /* reduce if the number exceeds config limits */
466
logprintfl (EUCAINFO, "Maximum disk available = %lld (under %s)\n", disk_max, ipath);
469
/* xm info will be used for memory and cores */
470
s = system_output (get_xen_info_command_path);
471
#define GET_VALUE(name,var) \
472
if (get_value (s, name, &var)) { \
473
logprintfl (EUCAFATAL, "error: did not find %s in output from %s\n", name, get_xen_info_command_path); \
475
return ERROR_FATAL; \
478
/* calculate mem_max */
480
long long total_memory = 0;
481
long long free_memory = 0;
482
long long dom0_min_mem = 0;
484
GET_VALUE("total_memory", total_memory);
485
GET_VALUE("free_memory", free_memory);
486
GET_VALUE("dom0-min-mem", dom0_min_mem);
488
mem_max = total_memory - 32 - dom0_min_mem;
490
&& mem_max>config_max_mem)
491
mem_max = config_max_mem; /* reduce if the number exceeds config limits */
492
logprintfl (EUCAINFO, "Maximum memory available = %lld\n", mem_max);
495
/* calculate cores_max */
500
GET_VALUE("nr_cpus", nr_cpus);
501
GET_VALUE("nr_nodes", nr_nodes);
503
cores_max = (int)nr_cpus * (int)nr_nodes;
504
/* unlike with disk or memory limits, use the limit as the
505
* number of cores, regardless of whether the actual number
506
* of cores is bigger or smaller */
507
if (config_max_cores)
508
cores_max = config_max_cores;
509
logprintfl (EUCAINFO, "Maximum cores available = %d\n", cores_max);
514
if ( pthread_create (&tcb, NULL, monitoring_thread, NULL) ) {
515
logprintfl (EUCAFATAL, "failed to spawn a monitoring thread\n");
522
static int get_instance_xml (char *userId, char *instanceId, int ramdisk, char *disk_path, ncInstParams *params, char *privMac, char *pubMac, char *brname, char **xml)
526
/* TODO: add --ephemeral? */
528
snprintf (buf, BUFSIZE, "%s --ramdisk", gen_libvirt_xml_command_path);
530
snprintf (buf, BUFSIZE, "%s", gen_libvirt_xml_command_path);
532
* xml = system_output (buf);
533
if ( ( * xml ) == NULL ) {
534
logprintfl (EUCAFATAL, "%s: %s\n", gen_libvirt_xml_command_path, strerror (errno));
538
/* the tags better be not substring of other tags: BA will substitute
540
replace_string (xml, "BASEPATH", disk_path);
541
replace_string (xml, "SWAPPATH", disk_path);
542
replace_string (xml, "NAME", instanceId);
543
replace_string (xml, "PRIVMACADDR", privMac);
544
replace_string (xml, "PUBMACADDR", pubMac);
545
replace_string (xml, "BRIDGEDEV", brname);
546
snprintf(buf, BUFSIZE, "%d", params->memorySize * 1024); /* because libvirt wants memory in Kb, while we use Mb */
547
replace_string (xml, "MEMORY", buf);
548
snprintf(buf, BUFSIZE, "%d", params->numberOfCores);
549
replace_string (xml, "VCPUS", buf);
554
void * startup_thread (void * arg)
556
ncInstance * instance = (ncInstance *)arg;
557
virConnectPtr conn = NULL;
558
virDomainPtr dom = NULL;
559
char * disk_path, * xml;
563
conn = virConnectOpen("xen:///"); /* NULL means local hypervisor */
565
logprintfl (EUCAFATAL, "failed to connect to hypervisor to start instance %s, abandoning it\n", instance->instanceId);
566
change_state (instance, SHUTOFF);
570
error = vnetStartNetwork (vnetconfig, instance->ncnet.vlan, NULL, NULL, &brname);
572
logprintfl (EUCAFATAL, "start network failed for instance %s, terminating it\n", instance->instanceId);
573
change_state (instance, SHUTOFF);
574
virConnectClose (conn);
577
logprintfl (EUCAINFO, "network started for instance %s\n", instance->instanceId);
579
error = scMakeInstanceImage (instance->userId,
580
instance->imageId, instance->imageURL,
581
instance->kernelId, instance->kernelURL,
582
instance->ramdiskId, instance->ramdiskURL,
583
instance->instanceId, instance->keyName,
584
&disk_path, xen_sem, 0);
586
logprintfl (EUCAFATAL, "Failed to prepare images for instance %s (error=%d)\n", instance->instanceId, error);
587
change_state (instance, SHUTOFF);
588
virConnectClose (conn);
591
if (instance->state!=BOOTING) {
592
logprintfl (EUCAFATAL, "Startup of instance %s was cancelled\n", instance->instanceId);
593
change_state (instance, SHUTOFF);
594
virConnectClose (conn);
598
error = get_instance_xml (instance->userId, instance->instanceId,
599
strnlen (instance->ramdiskId, CHAR_BUFFER_SIZE), /* 0 if no ramdisk */
602
instance->ncnet.privateMac, instance->ncnet.publicMac,
604
if (xml) logprintfl (EUCADEBUG2, "libvirt XML config:\n%s\n", xml);
606
logprintfl (EUCAFATAL, "Failed to create libvirt XML config for instance %s\n", instance->instanceId);
607
change_state (instance, SHUTOFF);
608
virConnectClose(conn);
612
scStoreStringToInstanceFile (instance->userId, instance->instanceId, "libvirt.xml", xml); /* for debugging */
613
scSaveInstanceInfo(instance); /* to enable NC recovery */
615
/* we serialize domain creation as Xen can get confused with
616
* too many simultaneous create requests */
617
logprintfl (EUCADEBUG2, "about to start domain %s\n", instance->instanceId);
618
print_running_domains ();
620
dom = virDomainCreateLinux (conn, xml, 0);
623
logprintfl (EUCAFATAL, "hypervisor failed to start domain\n");
624
change_state (instance, SHUTOFF);
625
virConnectClose(conn);
628
eventlog("NC", instance->userId, "", "instanceBoot", "begin"); /* TODO: bring back correlationId */
631
virConnectClose(conn);
632
logprintfl (EUCAINFO, "started VM instance %s\n", instance->instanceId);
637
static int doRunInstance (ncMetadata *meta, char *instanceId, char *reservationId, ncInstParams *params,
638
char *imageId, char *imageURL,
639
char *kernelId, char *kernelURL,
640
char *ramdiskId, char *ramdiskURL,
642
char *privMac, char *pubMac, int vlan,
643
char *userData, char *launchIndex, char **groupNames, int groupNamesSize,
644
ncInstance **outInst)
646
ncInstance * instance = NULL;
652
logprintfl (EUCAINFO, "doRunInstance() invoked (id=%s cores=%d disk=%d memory=%d\n",
653
instanceId, params->numberOfCores, params->diskSize, params->memorySize);
654
logprintfl (EUCAINFO, " image=%s at %s\n", imageId, imageURL);
655
logprintfl (EUCAINFO, " krnel=%s at %s\n", kernelId, kernelURL);
657
logprintfl (EUCAINFO, " rmdsk=%s at %s\n", ramdiskId, ramdiskURL);
659
logprintfl (EUCAINFO, " vlan=%d priMAC=%s pubMAC=%s\n",
660
vlan, privMac, pubMac);
662
strcpy(ncnet.privateMac, privMac);
663
strcpy(ncnet.publicMac, pubMac);
666
/* check as much as possible before forking off and returning */
668
instance = find_instance (&global_instances, instanceId);
671
logprintfl (EUCAFATAL, "Error: instance %s already running\n", instanceId);
672
return 1; /* TODO: return meaningful error codes? */
674
if (!(instance = allocate_instance (instanceId,
679
ramdiskId, ramdiskURL,
680
instance_state_names[PENDING],
684
userData, launchIndex, groupNames, groupNamesSize))) {
685
logprintfl (EUCAFATAL, "Error: could not allocate instance struct\n");
688
instance->state = BOOTING; /* TODO: do this in allocate_instance()? */
691
error = add_instance (&global_instances, instance);
694
free_instance (&instance);
695
logprintfl (EUCAFATAL, "Error: could not save instance struct\n");
699
instance->launchTime = time (NULL);
700
instance->params.memorySize = params->memorySize;
701
instance->params.numberOfCores = params->numberOfCores;
702
instance->params.diskSize = params->diskSize;
703
strcpy (instance->ncnet.privateIp, "0.0.0.0");
704
strcpy (instance->ncnet.publicIp, "0.0.0.0");
706
/* do the potentially long tasks in a thread */
708
if ( pthread_create (&(instance->tcb), NULL, startup_thread, (void *)instance) ) {
709
logprintfl (EUCAFATAL, "failed to spawn a VM startup thread\n");
711
remove_instance (&global_instances, instance);
713
free_instance (&instance);
717
* outInst = instance;
722
static int doRebootInstance(ncMetadata *meta, char *instanceId) {
726
snprintf(cmd, 256, "xm reboot %s", instanceId);
729
return(WEXITSTATUS(rc));
732
static int doGetConsoleOutput(ncMetadata *meta, char *instanceId, char **consoleOutput) {
735
int pid, status, rc, bufsize, fd;
738
fprintf(stderr, "getconsoleoutput called\n");
740
bufsize = sizeof(char) * 1024 * 64;
741
output = malloc(bufsize);
742
bzero(output, bufsize);
744
snprintf(filename, 1024, "/tmp/consoleOutput.%s", instanceId);
749
fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0644);
756
rc = execl("/usr/sbin/xm", "/usr/sbin/xm", "console", instanceId, NULL);
757
fprintf(stderr, "execl() failed\n");
768
while(count < 10000 && stat(filename, &statbuf) < 0) {count++;}
769
fd = open(filename, O_RDONLY);
771
logprintfl (EUCAERROR, "ERROR: could not open consoleOutput file %s for reading\n", filename);
777
rc = select(1, &rfds, NULL, NULL, &tv);
778
bzero(output, bufsize);
782
while(rc && count < 1000) {
783
rc = read(fd, output, bufsize-1);
794
if (output[0] == '\0') {
795
snprintf(output, bufsize, "EMPTY");
798
*consoleOutput = base64_enc((unsigned char *)output, strlen(output));
804
static int doTerminateInstance (ncMetadata *meta, char *instanceId, int *shutdownState, int *previousState)
806
ncInstance *instance, *vninstance;
809
logprintfl (EUCAINFO, "doTerminateInstance() invoked (id=%s)\n", instanceId);
811
instance = find_instance(&global_instances, instanceId);
813
if ( instance == NULL ) return NOT_FOUND;
815
/* try stopping the Xen domain */
816
virConnectPtr conn = virConnectOpen("xen:///"); /* NULL means local hypervisor */
818
logprintfl (EUCAFATAL, "Failed to connect to hypervisor\n");
820
virDomainPtr dom = virDomainLookupByName(conn, instanceId);
822
/* also protect 'destroy' commands, just in case */
824
int err=virDomainDestroy (dom);
827
logprintfl (EUCAINFO, "destroyed Xen domain for instance %s\n", instanceId);
829
virDomainFree(dom); /* necessary? */
831
if (instance->state != BOOTING) {
832
logprintfl (EUCAWARN, "warning: domain %s to be terminated not running on hypervisor\n", instanceId);
835
virConnectClose (conn);
838
/* change the state and let the monitoring_thread clean up state */
839
change_state (instance, SHUTOFF);
840
* previousState = instance->stateCode;
841
* shutdownState = instance->stateCode;
846
static int doDescribeInstances (ncMetadata *meta, char **instIds, int instIdsLen, ncInstance ***outInsts, int *outInstsLen)
848
logprintfl (EUCAINFO, "doDescribeInstances() invoked\n");
853
if (instIdsLen == 0) { /* describe all instances */
854
int total = total_instances (&global_instances);
856
ncInstance * instance;
859
* outInsts = malloc (sizeof(ncInstance *)*total);
860
if ( (* outInsts) == NULL ) goto out_oom;
862
for (i=0; (instance = get_instance (&global_instances))!=NULL; i++) {
863
/* only pick ones the user is allowed to see */
864
if (!strcmp(meta->userId, admin_user_id) || /* admin will see all */
865
!strcmp(meta->userId, instance->userId)) { /* owner */
866
(* outInsts)[k++] = instance;
872
} else { /* describe specific instances */
873
ncInstance * instance;
876
* outInsts = malloc (sizeof(ncInstance *)*(instIdsLen));
877
if ( (* outInsts) == NULL ) goto out_oom;
879
for (i=0; (instance = get_instance (&global_instances))!=NULL; i++) {
880
for (j=0; j<instIdsLen; j++) {
881
if ( !strcmp(instance->instanceId, instIds[j]) ) {
882
/* only pick ones the user is allowed to see */
883
if (!strcmp(meta->userId, admin_user_id) || /* admin will see all */
884
!strcmp(meta->userId, instance->userId)) { /* owner */
885
(* outInsts)[k++] = instance;
888
/* TODO: do we complain about instIds[j] that weren't found? */
898
return OUT_OF_MEMORY;
901
static int doDescribeResource (ncMetadata *meta, char *resourceType, ncResource **outRes)
906
/* stats to re-calculate now */
911
/* intermediate sums */
912
long long sum_mem = 0; /* for known domains: sum of requested memory */
913
long long sum_disk = 0; /* for known domains: sum of requested disk sizes */
914
int sum_cores = 0; /* for known domains: sum of requested cores */
916
logprintfl (EUCAINFO, "doDescribeResource() invoked\n");
920
while ((inst=get_instance(&global_instances))!=NULL) {
921
if (inst->state == TEARDOWN) continue; /* they don't take up resources */
922
sum_mem += inst->params.memorySize;
923
sum_disk += (inst->params.diskSize + SWAP_SIZE);
924
sum_cores += inst->params.numberOfCores;
928
disk_free = disk_max - sum_disk;
929
if ( disk_free < 0 ) disk_free = 0; /* should not happen */
931
mem_free = mem_max - sum_mem;
932
if ( mem_free < 0 ) mem_free = 0; /* should not happen */
934
cores_free = cores_max - sum_cores; /* TODO: should we -1 for dom0? */
935
if ( cores_free < 0 ) cores_free = 0; /* due to timesharing */
937
/* check for potential overflow - should not happen */
938
if (mem_max > INT_MAX ||
939
mem_free > INT_MAX ||
940
disk_max > INT_MAX ||
941
disk_free > INT_MAX) {
942
logprintfl (EUCAERROR, "stats integer overflow error (bump up the units?)\n");
943
logprintfl (EUCAERROR, " memory: max=%-10lld free=%-10lld\n", mem_max, mem_free);
944
logprintfl (EUCAERROR, " disk: max=%-10lld free=%-10lld\n", disk_max, disk_free);
945
logprintfl (EUCAERROR, " cores: max=%-10d free=%-10d\n", cores_max, cores_free);
946
logprintfl (EUCAERROR, " INT_MAX=%-10d\n", INT_MAX);
950
res = allocate_resource ("OK", mem_max, mem_free, disk_max, disk_free, cores_max, cores_free, "none");
952
logprintfl (EUCAERROR, "Out of memory\n");
959
static int doStartNetwork(ncMetadata *ccMeta, char **remoteHosts, int remoteHostsLen, int port, int vlan) {
960
int rc, ret, i, status;
963
logprintfl (EUCAINFO, "StartNetwork(): called\n");
965
rc = vnetStartNetwork(vnetconfig, vlan, NULL, NULL, &brname);
968
logprintfl (EUCAERROR, "StartNetwork(): ERROR return from vnetStartNetwork %d\n", rc);
970
logprintfl (EUCAINFO, "StartNetwork(): SUCCESS return from vnetStartNetwork %d\n", rc);
973
logprintfl (EUCAINFO, "StartNetwork(): done\n");
977
static int doAttachVolume (ncMetadata *meta, char *instanceId, char *volumeId, char *remoteDev, char *localDev)
979
ncInstance *instance;
982
logprintfl (EUCAINFO, "doAttachVolume() invoked (id=%s vol=%s remote=%s local=%s)\n", instanceId, volumeId, remoteDev, localDev);
984
instance = find_instance(&global_instances, instanceId);
986
if ( instance == NULL )
991
volume = add_volume (instance, volumeId, remoteDev, localDev);
993
if ( volume == NULL ) {
994
logprintfl (EUCAFATAL, "ERROR: Failed to save the volume record, aborting volume attachment\n");
998
/* try attaching to the Xen domain */
999
virConnectPtr conn = virConnectOpen("xen:///"); /* NULL means local hypervisor */
1001
logprintfl (EUCAFATAL, "Failed to connect to hypervisor\n");
1005
virDomainPtr dom = virDomainLookupByName(conn, instanceId);
1010
snprintf (xml, 1024, "<disk type='block'><driver name='phy'/><source dev='%s'/><target dev='%s'/></disk>", remoteDev, localDev);
1012
/* protect Xen calls, just in case */
1014
// int err = virDomainAttachDevice (dom, xml);
1017
logprintfl (EUCAERROR, "AttachVolume() failed (err=%d) XML=%s\n", err, xml);
1020
logprintfl (EUCAINFO, "attached %s to %s in domain %s\n", remoteDev, localDev, instanceId);
1024
if (instance->state != BOOTING) {
1025
logprintfl (EUCAWARN, "warning: domain %s not running on hypervisor, cannot attach device\n", instanceId);
1029
virConnectClose (conn);
1034
static int doDetachVolume (ncMetadata *meta, char *instanceId, char *volumeId, char *remoteDev, char *localDev, int force)
1036
ncInstance *instance;
1039
logprintfl (EUCAINFO, "doDetachVolume() invoked (id=%s vol=%s remote=%s local=%s force=%d)\n", instanceId, volumeId, remoteDev, localDev, force);
1041
instance = find_instance(&global_instances, instanceId);
1043
if ( instance == NULL )
1048
volume = free_volume (instance, volumeId, remoteDev, localDev);
1050
if ( volume == NULL ) {
1051
logprintfl (EUCAFATAL, "ERROR: Failed to find and remove volume record, aborting volume detachment\n");
1055
/* try attaching to the Xen domain */
1056
virConnectPtr conn = virConnectOpen("xen:///"); /* NULL means local hypervisor */
1058
logprintfl (EUCAFATAL, "Failed to connect to hypervisor\n");
1062
virDomainPtr dom = virDomainLookupByName(conn, instanceId);
1066
snprintf (xml, 1024, "<disk type='block'><driver name='phy'/><source dev='%s'/><target dev='%s'/></disk>", remoteDev, localDev);
1068
/* protect Xen calls, just in case */
1070
// int err = virDomainDetachDevice (dom, xml);
1073
logprintfl (EUCAERROR, "DetachVolume() failed (err=%d) XML=%s\n", err, xml);
1076
logprintfl (EUCAINFO, "detached %s as %s in domain %s\n", remoteDev, localDev, instanceId);
1080
if (instance->state != BOOTING) {
1081
logprintfl (EUCAWARN, "warning: domain %s not running on hypervisor, cannot detach device\n", instanceId);
1085
virConnectClose (conn);
1090
struct handlers xen_libvirt_handlers = {
1092
.doInitialize = doInitialize,
1093
.doDescribeInstances = doDescribeInstances,
1094
.doRunInstance = doRunInstance,
1095
.doTerminateInstance = doTerminateInstance,
1096
.doRebootInstance = doRebootInstance,
1097
.doGetConsoleOutput = doGetConsoleOutput,
1098
.doDescribeResource = doDescribeResource,
1099
.doStartNetwork = doStartNetwork,
1100
.doAttachVolume = doAttachVolume,
1101
.doDetachVolume = doDetachVolume