2
Copyright (c) 2009 Eucalyptus Systems, Inc.
4
This program is free software: you can redistribute it and/or modify
5
it under the terms of the GNU General Public License as published by
6
the Free Software Foundation, only version 3 of the License.
8
This file is distributed in the hope that it will be useful, but WITHOUT
9
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13
You should have received a copy of the GNU General Public License along
14
with this program. If not, see <http://www.gnu.org/licenses/>.
16
Please contact Eucalyptus Systems, Inc., 130 Castilian
17
Dr., Goleta, CA 93101 USA or visit <http://www.eucalyptus.com/licenses/>
18
if you need additional information or have any questions.
20
This file may incorporate work covered under the following copyright and
23
Software License Agreement (BSD License)
25
Copyright (c) 2008, Regents of the University of California
28
Redistribution and use of this software in source and binary forms, with
29
or without modification, are permitted provided that the following
32
Redistributions of source code must retain the above copyright notice,
33
this list of conditions and the following disclaimer.
35
Redistributions in binary form must reproduce the above copyright
36
notice, this list of conditions and the following disclaimer in the
37
documentation and/or other materials provided with the distribution.
39
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
40
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
41
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
42
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
43
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
44
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
45
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
46
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
47
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
48
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
49
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. USERS OF
50
THIS SOFTWARE ACKNOWLEDGE THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE
51
LICENSED MATERIAL, COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS
52
SOFTWARE, AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
53
IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, SANTA
54
BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, WHICH IN
55
THE REGENTS’ DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
56
OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO IDENTIFIED, OR
57
WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT NEEDED TO COMPLY WITH
58
ANY SUCH LICENSES OR RIGHTS.
62
#define __USE_GNU /* strnlen */
63
#include <string.h> /* strlen, strcpy */
65
#include <sys/types.h> /* fork */
66
#include <sys/wait.h> /* waitpid */
71
#include <signal.h> /* SIGINT */
79
#include <eucalyptus.h>
80
#include <euca_auth.h>
82
/* coming from handlers.c */
84
extern sem * inst_sem;
85
extern bunchOfInstances * global_instances;
87
#define HYPERVISOR_URI "xen:///"
89
static int doInitialize (struct nc_state_t *nc)
93
long long dom0_min_mem;
95
logprintfl(EUCADEBUG, "doInitialized() invoked\n");
97
/* set up paths of Eucalyptus commands NC relies on */
98
snprintf (nc->gen_libvirt_cmd_path, MAX_PATH, EUCALYPTUS_GEN_LIBVIRT_XML, nc->home, nc->home);
99
snprintf (nc->get_info_cmd_path, MAX_PATH, EUCALYPTUS_GET_XEN_INFO, nc->home, nc->home);
100
snprintf (nc->virsh_cmd_path, MAX_PATH, EUCALYPTUS_VIRSH, nc->home);
101
snprintf (nc->xm_cmd_path, MAX_PATH, EUCALYPTUS_XM);
102
snprintf (nc->detach_cmd_path, MAX_PATH, EUCALYPTUS_DETACH, nc->home, nc->home);
103
snprintf (nc->connect_storage_cmd_path, MAX_PATH, EUCALYPTUS_CONNECT_ISCSI, nc->home);
104
snprintf (nc->disconnect_storage_cmd_path, MAX_PATH, EUCALYPTUS_DISCONNECT_ISCSI, nc->home);
105
snprintf (nc->get_storage_cmd_path, MAX_PATH, EUCALYPTUS_GET_ISCSI, nc->home);
106
strcpy(nc->uri, HYPERVISOR_URI);
107
nc->convert_to_disk = 0;
109
/* check connection is fresh */
110
if (!check_hypervisor_conn()) {
115
if (virNodeGetInfo(nc->conn, &ni)) {
116
logprintfl (EUCAFATAL, "error: failed to discover resources\n");
120
/* dom0-min-mem has to come from xend config file */
121
s = system_output (nc->get_info_cmd_path);
122
if (get_value (s, "dom0-min-mem", &dom0_min_mem)) {
123
logprintfl (EUCAFATAL, "error: did not find dom0-min-mem in output from %s\n", nc->get_info_cmd_path);
129
/* calculate the available memory */
130
nc->mem_max = ni.memory/1024 - 32 - dom0_min_mem;
132
/* calculate the available cores */
133
nc->cores_max = ni.cpus;
135
/* let's adjust the values based on the config values */
136
if (nc->config_max_mem && nc->config_max_mem < nc->mem_max)
137
nc->mem_max = nc->config_max_mem;
138
if (nc->config_max_cores)
139
nc->cores_max = nc->config_max_cores;
141
logprintfl(EUCAINFO, "Using %lld cores\n", nc->cores_max);
142
logprintfl(EUCAINFO, "Using %lld memory\n", nc->mem_max);
148
doRunInstance( struct nc_state_t *nc,
152
virtualMachine *params,
153
char *imageId, char *imageURL,
154
char *kernelId, char *kernelURL,
155
char *ramdiskId, char *ramdiskURL,
157
// char *privMac, char *privIp, int vlan,
158
netConfig *netparams,
159
char *userData, char *launchIndex,
160
char **groupNames, int groupNamesSize,
161
ncInstance **outInst)
163
ncInstance * instance = NULL;
169
memcpy(&ncnet, netparams, sizeof(netConfig));
171
/* check as much as possible before forking off and returning */
173
instance = find_instance (&global_instances, instanceId);
176
logprintfl (EUCAFATAL, "Error: instance %s already running\n", instanceId);
177
return 1; /* TODO: return meaningful error codes? */
179
if (!(instance = allocate_instance (instanceId,
184
ramdiskId, ramdiskURL,
185
instance_state_names[PENDING],
189
userData, launchIndex, groupNames, groupNamesSize))) {
190
logprintfl (EUCAFATAL, "Error: could not allocate instance struct\n");
193
change_state(instance, STAGING);
196
error = add_instance (&global_instances, instance);
199
free_instance (&instance);
200
logprintfl (EUCAFATAL, "Error: could not save instance struct\n");
204
instance->launchTime = time (NULL);
206
instance->params.mem = params->mem;
207
instance->params.cores = params->cores;
208
instance->params.disk = params->disk;
209
strcpy (instance->ncnet.privateIp, "0.0.0.0");
210
strcpy (instance->ncnet.publicIp, "0.0.0.0");
213
/* do the potentially long tasks in a thread */
214
pthread_attr_t* attr = (pthread_attr_t*) malloc(sizeof(pthread_attr_t));
216
free_instance (&instance);
217
logprintfl (EUCAFATAL, "Warning: out of memory\n");
220
pthread_attr_init(attr);
221
pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED);
223
if ( pthread_create (&(instance->tcb), attr, startup_thread, (void *)instance) ) {
224
pthread_attr_destroy(attr);
225
logprintfl (EUCAFATAL, "failed to spawn a VM startup thread\n");
227
remove_instance (&global_instances, instance);
229
free_instance (&instance);
230
if (attr) free(attr);
233
pthread_attr_destroy(attr);
234
if (attr) free(attr);
236
* outInst = instance;
241
static int doRebootInstance( struct nc_state_t *nc,
245
ncInstance *instance;
249
instance = find_instance(&global_instances, instanceId);
251
if ( instance == NULL ) return NOT_FOUND;
253
/* reboot the Xen domain */
254
conn = check_hypervisor_conn();
257
virDomainPtr dom = virDomainLookupByName(*conn, instanceId);
260
/* also protect 'reboot', just in case */
262
int err=virDomainReboot (dom, 0);
265
logprintfl (EUCAINFO, "rebooting Xen domain for instance %s\n", instanceId);
268
virDomainFree(dom); /* necessary? */
271
if (instance->state != BOOTING && instance->state != STAGING) {
272
logprintfl (EUCAWARN, "warning: domain %s to be rebooted not running on hypervisor\n", instanceId);
281
doGetConsoleOutput( struct nc_state_t *nc,
284
char **consoleOutput) {
286
char *console_output=NULL, *console_append=NULL, *console_main=NULL;
287
char console_file[MAX_PATH];
291
int bufsize, pid, status;
293
*consoleOutput = NULL;
295
snprintf(console_file, 1024, "%s/%s/%s/console.append.log", scGetInstancePath(), meta->userId, instanceId);
296
rc = stat(console_file, &statbuf);
298
fd = open(console_file, O_RDONLY);
300
console_append = malloc(4096);
301
if (console_append) {
302
bzero(console_append, 4096);
303
rc = read(fd, console_append, (4096)-1);
311
console_main = strdup("NOT SUPPORTED");
313
fprintf(stderr, "strdup failed (out of memory?)\n");
314
if (console_append) free(console_append);
319
bufsize = sizeof(char) * 1024 * 64;
320
console_main = malloc(bufsize);
322
logprintfl(EUCAERROR, "doGetConsoleOutput(): out of memory!\n");
323
if (console_append) free(console_append);
326
bzero(console_main, bufsize);
328
snprintf(console_file, MAX_PATH, "/tmp/consoleOutput.%s", instanceId);
333
fd = open(console_file, O_WRONLY | O_TRUNC | O_CREAT, 0644);
340
// TODO: test virsh console:
341
// rc = execl(rootwrap_command_path, rootwrap_command_path, "virsh", "console", instanceId, NULL);
342
rc = execl("/usr/sbin/xm", "/usr/sbin/xm", "console", instanceId, NULL);
343
fprintf(stderr, "execl() failed\n");
354
while(count < 10000 && stat(console_file, &statbuf) < 0) {count++;}
355
fd = open(console_file, O_RDONLY);
357
logprintfl (EUCAERROR, "ERROR: could not open consoleOutput file %s for reading\n", console_file);
363
rc = select(1, &rfds, NULL, NULL, &tv);
364
bzero(console_main, bufsize);
368
while(rc && count < 1000) {
369
rc = read(fd, console_main, bufsize-1);
378
unlink(console_file);
382
console_output = malloc( (64*1024) + 4096 );
383
if (console_output) {
384
bzero(console_output, (64*1024) + 4096 );
385
if (console_append) {
386
strncat(console_output, console_append, 4096);
389
strncat(console_output, console_main, 1024*64);
391
*consoleOutput = base64_enc((unsigned char *)console_output, strlen(console_output));
395
if (console_append) free(console_append);
396
if (console_main) free(console_main);
397
if (console_output) free(console_output);
403
doAttachVolume ( struct nc_state_t *nc,
411
ncInstance * instance;
412
char localDevReal[32];
416
// fix up format of incoming local dev name, if we need to
417
ret = convert_dev_names (localDev, localDevReal, NULL);
422
instance = find_instance(&global_instances, instanceId);
424
if ( instance == NULL )
427
/* try attaching to the Xen domain */
428
conn = check_hypervisor_conn();
431
virDomainPtr dom = virDomainLookupByName(*conn, instanceId);
437
int is_iscsi_target = 0;
438
char *local_iscsi_dev;
440
if(check_iscsi(remoteDev)) {
442
/*get credentials, decrypt them*/
443
//parse_target(remoteDev);
445
local_iscsi_dev = connect_iscsi_target(nc->connect_storage_cmd_path, remoteDev);
446
if (!local_iscsi_dev || !strstr(local_iscsi_dev, "/dev")) {
447
logprintfl(EUCAERROR, "AttachVolume(): failed to connect to iscsi target\n");
450
snprintf (xml, 1024, "<disk type='block'><driver name='phy'/><source dev='%s'/><target dev='%s'/></disk>", local_iscsi_dev, localDevReal);
453
snprintf (xml, 1024, "<disk type='block'><driver name='phy'/><source dev='%s'/><target dev='%s'/></disk>", remoteDev, localDevReal);
454
rc = stat(remoteDev, &statbuf);
456
logprintfl(EUCAERROR, "AttachVolume(): cannot locate local block device file '%s'\n", remoteDev);
461
/* protect Xen calls, just in case */
463
err = virDomainAttachDevice (dom, xml);
466
logprintfl (EUCAERROR, "AttachVolume() failed (err=%d) XML=%s\n", err, xml);
467
// rc = doDetachVolume(nc, meta, instanceId, volumeId, remoteDev, localDev, 1);
470
logprintfl (EUCAINFO, "attached %s to %s in domain %s\n", remoteDev, localDevReal, instanceId);
479
if(is_iscsi_target) {
480
if (local_iscsi_dev) free(local_iscsi_dev);
483
if (instance->state != BOOTING && instance->state != STAGING) {
484
logprintfl (EUCAWARN, "warning: domain %s not running on hypervisor, cannot attach device\n", instanceId);
496
volume = add_volume (instance, volumeId, remoteDev, localDevReal, localDevReal, "attached");
497
scSaveInstanceInfo(instance); /* to enable NC recovery */
499
if ( volume == NULL ) {
500
logprintfl (EUCAFATAL, "ERROR: Failed to save the volume record, aborting volume attachment\n");
509
doDetachVolume ( struct nc_state_t *nc,
518
ncInstance * instance;
519
char localDevReal[32];
522
// fix up format of incoming local dev name, if we need to
523
ret = convert_dev_names (localDev, localDevReal, NULL);
528
instance = find_instance(&global_instances, instanceId);
530
if ( instance == NULL )
533
/* try attaching to the Xen domain */
534
conn = check_hypervisor_conn();
537
virDomainPtr dom = virDomainLookupByName(*conn, instanceId);
540
int err = 0, fd, rc, pid, status;
541
char xml [1024], tmpfile[32], cmd[MAX_PATH];
543
int is_iscsi_target = 0;
544
char *local_iscsi_dev;
545
if(check_iscsi(remoteDev)) {
547
/*get credentials, decrypt them*/
548
//parse_target(remoteDev);
549
/*logout from target*/
550
if((local_iscsi_dev = get_iscsi_target(nc->get_storage_cmd_path, remoteDev)) == NULL)
552
snprintf (xml, 1024, "<disk type='block'><driver name='phy'/><source dev='%s'/><target dev='%s'/></disk>", local_iscsi_dev, localDevReal);
554
snprintf (xml, 1024, "<disk type='block'><driver name='phy'/><source dev='%s'/><target dev='%s'/></disk>", remoteDev, localDevReal);
556
/* protect Xen calls, just in case */
561
snprintf(tmpfile, 32, "/tmp/detachxml.XXXXXX");
562
fd = mkstemp(tmpfile);
564
write(fd, xml, strlen(xml));
566
snprintf(cmd, MAX_PATH, "%s %s `which virsh` %s %s %s", nc->detach_cmd_path, nc->rootwrap_cmd_path, instanceId, localDevReal, tmpfile);
571
logprintfl(EUCAERROR, "could not write to tmpfile for detach XML: %s\n", tmpfile);
576
rc = timewait(pid, &status, 15);
577
if (WEXITSTATUS(status)) {
578
logprintfl(EUCAERROR, "failed to sucessfully run detach helper\n");
587
err = virDomainDetachDevice (dom, xml);
591
/* virsh detach function does not work as non-root user on xen (bug). workaround is to shellout to virsh */
592
snprintf(tmpfile, 32, "/tmp/detachxml.XXXXXX");
593
fd = mkstemp(tmpfile);
595
write(fd, xml, strlen(xml));
597
snprintf(cmd, MAX_PATH, "%s detach-device %s %s",virsh_command_path, instanceId, tmpfile);
598
logprintfl(EUCADEBUG, "Running command: %s\n", cmd);
599
err = WEXITSTATUS(system(cmd));
602
logprintfl(EUCADEBUG, "first workaround command failed (%d), trying second workaround...\n", err);
603
snprintf(cmd, MAX_PATH, "%s block-detach %s %s", xm_command_path, instanceId, localDevReal);
604
logprintfl(EUCADEBUG, "Running command: %s\n", cmd);
605
err = WEXITSTATUS(system(cmd));
615
logprintfl (EUCAERROR, "DetachVolume() failed (err=%d) XML=%s\n", err, xml);
618
logprintfl (EUCAINFO, "detached %s as %s in domain %s\n", remoteDev, localDevReal, instanceId);
623
if(is_iscsi_target) {
624
if(disconnect_iscsi_target(nc->disconnect_storage_cmd_path, remoteDev) != 0) {
625
logprintfl (EUCAERROR, "disconnect_iscsi_target failed for %s\n", remoteDev);
628
free(local_iscsi_dev);
631
if (instance->state != BOOTING && instance->state != STAGING) {
632
logprintfl (EUCAWARN, "warning: domain %s not running on hypervisor, cannot detach device\n", instanceId);
644
volume = free_volume (instance, volumeId, remoteDev, localDevReal);
646
if ( volume == NULL ) {
647
logprintfl (EUCAFATAL, "ERROR: Failed to find and remove volume record, aborting volume detachment\n");
655
struct handlers xen_libvirt_handlers = {
657
.doInitialize = doInitialize,
658
.doDescribeInstances = NULL,
659
.doRunInstance = doRunInstance,
660
.doTerminateInstance = NULL,
661
.doRebootInstance = doRebootInstance,
662
.doGetConsoleOutput = doGetConsoleOutput,
663
.doDescribeResource = NULL,
664
.doStartNetwork = NULL,
666
.doAttachVolume = doAttachVolume,
667
.doDetachVolume = doDetachVolume