Updates with respect to migration
[handlervirt.git] / handler_virt.c
blobcff82ce49671aac5de56e1c3654f6c47f2c95ea2
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /* Cherokee
5 * Authors:
6 * Alvaro Lopez Ortega <alvaro@alobbs.com>
7 * Stefan de Konink <stefan@konink.de>
9 * Copyright (C) 2001-2008 Alvaro Lopez Ortega
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of version 2 of the GNU General Public
13 * License as published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 * USA
26 // #define LINUX_CMDLINE "root=/dev/xvda ro ip=%s:1.2.3.4:%s:%s::eth0:off"
28 #define VIRT_INTERFACE_XML \
29 " <interface type='bridge'>" \
30 " <source bridge='xenbr0'/>" \
31 " <mac address='%s'/>" \
32 " <ip address='%s' />" \
33 " <script path='vif-bridge'/>" \
34 " </interface>"
36 //" <target dev='vif1.0'/>" \
38 #define VIRT_DISK_XML \
39 " <disk type='file' device='disk'>" \
40 " <driver name='tap' type='aio' />" \
41 " <source file='%s'/>" \
42 " <target dev='%s' bus='xen'/>" \
43 " </disk>"
45 #define VIRT_DOMAIN_CMD_IP "ip=%s:1.2.3.4:%s:255.255.255.0::eth0:off"
47 #define VIRT_DOMAIN_XML \
48 "<domain type='xen'>" \
49 " <name>%s_%s</name>" \
50 " <os>" \
51 " <type>linux</type>" \
52 " <kernel>/usr/lib/xen/boot/linux-2.6.20-xen-r6</kernel>" \
53 " <cmdline> root=/dev/xvda ro %s</cmdline>" \
54 " </os>" \
55 " <memory>%d</memory>" \
56 " <vcpu>%d</vcpu>" \
57 " <on_poweroff>destroy</on_poweroff>" \
58 " <on_reboot>restart</on_reboot>" \
59 " <on_crash>destroy</on_crash>" \
60 " <devices>" \
61 " %s" \
62 " </devices>" \
63 "</domain>"
65 #define VIRT_STORAGE_XML \
66 "<volume type='%s'>" \
67 " <name>%s_%s</name>" \
68 " <allocation>%lu</allocation>" \
69 " <capacity unit='%s'>%lu</capacity>" \
70 " <target>" \
71 " <path>%s_%s</path>" \
72 " <permissions>" \
73 " <owner>0744</owner>" \
74 " <group>0744</group>" \
75 " <mode>0744</mode>" \
76 " <label>%s_%s</label>" \
77 " </permissions>" \
78 " </target>" \
79 "</volume>"
82 #include "handler_virt.h"
83 #include "handler_clusterstats.h"
84 #include "handler_virt_tender.h"
85 #include <cherokee/cherokee.h>
86 #include <libxml/parser.h>
87 #include <libxml/xpath.h>
88 #include <float.h>
90 #include <avahi-client/client.h>
91 #include <avahi-client/publish.h>
92 #include <avahi-client/lookup.h>
94 #include <avahi-common/alternative.h>
95 #include <avahi-common/simple-watch.h>
96 #include <avahi-common/malloc.h>
97 #include <avahi-common/error.h>
98 #include <avahi-common/timeval.h>
102 /* Plug-in initialization
104 * In this function you can use any of these:
105 * http_delete | http_get | http_post | http_put
107 * For a full list: cherokee_http_method_t
109 * It is what your handler to be implements.
112 PLUGIN_INFO_HANDLER_EASIEST_INIT (virt, http_get | http_post);
115 /* Methods implementation
117 static ret_t
118 props_free (cherokee_handler_virt_props_t *props)
120 cherokee_buffer_mrproper(&props->xsl);
121 cherokee_buffer_mrproper(&props->virt);
123 return cherokee_handler_clusterstats_props_free (PROP_CLUSTERSTATS(props));
127 ret_t
128 cherokee_handler_virt_configure (cherokee_config_node_t *conf, cherokee_server_t *srv, cherokee_module_props_t **_props)
130 cherokee_list_t *i;
131 cherokee_handler_virt_props_t *props;
133 /* Instance a new property object
136 if (*_props == NULL) {
137 CHEROKEE_NEW_STRUCT (n, handler_virt_props);
139 cherokee_handler_clusterstats_props_init_base (PROP_CLUSTERSTATS(n), MODULE_PROPS_FREE(props_free));
141 /* Look at handler_virt.h
142 * This is an virt of configuration.
144 n->authenticate = true; /* by default we are secure! */
145 n->read_only = true; /* by default we are secure! */
146 cherokee_buffer_init (&n->xsl); /* we support a custom header */
147 cherokee_buffer_init (&n->virt); /* your first xenserver */
149 *_props = MODULE_PROPS(n);
152 props = PROP_VIRT(*_props);
154 cherokee_config_node_foreach (i, conf) {
155 cherokee_config_node_t *subconf = CONFIG_NODE(i);
157 if (equal_buf_str (&subconf->key, "authenticate")) {
158 props->authenticate = atoi(subconf->val.buf);
160 else if (equal_buf_str (&subconf->key, "read_only")) {
161 props->read_only = atoi(subconf->val.buf);
163 else if (equal_buf_str (&subconf->key, "xsl")) {
164 cherokee_buffer_add_buffer (&props->xsl, &subconf->val);
166 else if (equal_buf_str (&subconf->key, "virt")) {
167 cherokee_buffer_add_buffer (&props->virt, &subconf->val);
172 /* Init base class
175 return cherokee_handler_clusterstats_configure (conf, srv, _props);
178 ret_t
179 cherokee_handler_virt_init (cherokee_handler_virt_t *hdl)
181 cherokee_connection_t *conn = HANDLER_CONN(hdl);
183 /* If someone is posting the server should be in
184 * read/write mode if this is not the case, just
185 * bail out directly */
187 if (conn->header.method == http_post && HDL_VIRT_PROPS(hdl)->read_only == TRUE) {
188 conn->error_code = http_unauthorized;
189 return ret_error;
192 int isroot = false;
193 int len;
194 char *this, *next;
196 cherokee_buffer_init(&hdl->user);
197 cherokee_buffer_init(&hdl->vm);
199 hdl->action = nothing;
201 cherokee_buffer_add (&conn->pathinfo,
202 conn->request.buf + conn->web_directory.len,
203 conn->request.len - conn->web_directory.len);
205 this = conn->pathinfo.buf + 1;
207 next = strchr(this, '/'); /* TODO: this code borks! */
209 if ((!next && (this && (len = strlen(this)) == 0)) || (next && ((len = next - this) == 0)) )
210 hdl->action = showall;
211 else {
212 cherokee_buffer_add (&hdl->user, this, len);
215 if (HDL_VIRT_PROPS(hdl)->authenticate) {
216 if (!conn->validator ||
217 (conn->validator &&
218 (!cherokee_buffer_cmp_buf(&conn->validator->user, &hdl->user) &&
219 !(isroot = cherokee_buffer_cmp (&conn->validator->user, "root", 4))))) {
220 hdl->action = nothing; /* just in case */
221 conn->error_code = http_unauthorized;
222 return ret_error;
224 } else {
225 isroot = true;
228 if (hdl->action == showall) {
229 if (!isroot) {
230 hdl->action = nothing;
231 conn->error_code = http_unauthorized;
232 return ret_error;
233 } else {
234 return virt_build_page(hdl);
239 if (!next) {
240 hdl->action = showuservms;
241 return virt_build_page(hdl);
242 } else {
243 this = next + 1;
244 next = strchr(this, '/');
246 if ( ( !next && (this && (len = strlen(this)) == 0) ) || (next && ((len = next - this) == 0)) ) {
247 //if (!next && (this && (len = strlen(this)) == 0) || (next && ((len = next - this) == 0)) ) {
248 hdl->action = showuservms;
249 return virt_build_page(hdl);
253 cherokee_buffer_add (&hdl->vm, this, len);
255 if (!next) {
256 hdl->action = domainGetXMLDesc;
257 return virt_build_page(hdl);
258 } else {
259 this = next + 1;
260 next = strchr(this, '/');
262 if (( !next && (this && (len = strlen(this)) == 0) ) || (next && ((len = next - this) == 0)) ) {
263 hdl->action = domainGetXMLDesc;
264 return virt_build_page(hdl);
268 hdl->action = not_implemented;
269 switch (conn->header.method) {
270 case http_get:
271 if (strncmp(this, "virDomain", 9) == 0) {
272 if (strncmp(this+9, "Get", 3) == 0) {
273 if (strcmp(this+12, "ID") == 0) hdl->action = domainGetID;
274 else if (strcmp(this+12, "Name") == 0) hdl->action = domainGetName;
275 else if (strcmp(this+12, "MaxMemory") == 0) hdl->action = domainGetMaxMemory;
276 else if (strcmp(this+12, "MaxVcpus") == 0) hdl->action = domainGetMaxVcpus;
277 else if (strcmp(this+12, "OSType") == 0) hdl->action = domainGetOSType;
278 else if (strcmp(this+12, "UUID") == 0) hdl->action = domainGetUUID;
279 else if (strcmp(this+12, "UUIDString") == 0) hdl->action = domainGetUUIDString;
280 else if (strcmp(this+12, "XMLDesc") == 0) hdl->action = domainGetXMLDesc;
281 else if (strcmp(this+12, "XMLSnapshots") == 0) hdl->action = domainGetXMLSnapshots;
284 else if (HDL_VIRT_PROPS(hdl)->read_only == FALSE) {
285 if (strcmp(this+9, "Create") == 0) hdl->action = domainCreate;
286 else if (strcmp(this+9, "Destroy") == 0) hdl->action = domainDestroy;
287 else if (strcmp(this+9, "Reboot") == 0) hdl->action = domainReboot;
288 else if (strcmp(this+9, "Shutdown") == 0) hdl->action = domainShutdown;
290 else if (strcmp(this+9, "Save") == 0) hdl->action = domainSave;
291 else if (strcmp(this+9, "Restore") == 0) hdl->action = domainRestore;
292 else if (strcmp(this+9, "Migrate") == 0) hdl->action = domainMigrate_args;
294 else if (strcmp(this+9, "AttachDevice") == 0) hdl->action = domainAttachDevice_args;
296 else if (strcmp(this+9, "DefineXML") == 0) hdl->action = domainDefineXML_args;
297 else if (strcmp(this+9, "Undefine") == 0) hdl->action = domainUndefine;
300 else if (HDL_VIRT_PROPS(hdl)->read_only == FALSE && strncmp(this, "virStorage", 10) == 0) {
301 if (strncmp(this+10, "Vol", 3) == 0) {
302 if (strcmp(this+13, "CreateXML") == 0) hdl->action = storageVolCreateXML_args;
303 else if (strcmp(this+13, "Delete") == 0) hdl->action = storageVolDelete_args;
304 else if (strcmp(this+13, "CloneXML") == 0) hdl->action = storageVolCloneXML_args;
305 else if (strcmp(this+13, "CloneStatus") == 0) hdl->action = storageVolCloneStatus_args;
306 else if (strcmp(this+13, "GetXMLDesc") == 0) hdl->action = storageVolGetXMLDesc_args;
307 else if (strcmp(this+13, "SetPassword") == 0) hdl->action = storageVolSetPassword_args;
310 else if (strncmp(this, "virGraph", 8) == 0) {
311 if (strcmp(this+8, "Load") == 0) hdl->action = graphLoad_args;
312 if (strcmp(this+8, "Interface") == 0) hdl->action = graphInterface_args;
314 break;
316 case http_post: {
317 off_t postl;
318 cherokee_post_get_len (&conn->post, &postl);
320 if (postl <= 0 || postl >= (INT_MAX-1)) {
321 TRACE("virt", "post without post");
322 conn->error_code = http_bad_request;
323 return ret_error;
326 if (strncmp(this, "virDomain", 9) == 0) {
327 if (strcmp(this+9, "AttachDevice") == 0) hdl->action = domainAttachDevice;
328 else if (strcmp(this+9, "DetachDevice") == 0) hdl->action = domainDetachDevice;
329 else if (strcmp(this+9, "DefineXML") == 0) hdl->action = domainDefineXML;
331 else if (strncmp(this, "virStorage", 10) == 0) {
332 if (strncmp(this+10, "Vol", 3) == 0) {
333 if (strcmp(this+13, "CreateXML") == 0) hdl->action = storageVolCreateXML;
337 break;
340 default:
341 return ret_error;
344 if (hdl->action <= 0) {
345 TRACE("virt", "There was no action specified");
346 conn->error_code = http_bad_request;
347 return ret_error;
350 return virt_build_page(hdl);
353 ret_t
354 cherokee_handler_virt_free (cherokee_handler_virt_t *hdl)
356 cherokee_buffer_mrproper (&hdl->buffer);
357 cherokee_buffer_mrproper (&hdl->user);
358 cherokee_buffer_mrproper (&hdl->vm);
360 return ret_ok;
363 ret_t
364 cherokee_handler_virt_step (cherokee_handler_virt_t *hdl, cherokee_buffer_t *buffer)
366 cuint_t tosend;
368 if (cherokee_buffer_is_empty (&hdl->buffer))
369 return ret_eof;
371 tosend = (hdl->buffer.len > 1024 ? 1024 : hdl->buffer.len);
373 cherokee_buffer_add (buffer, hdl->buffer.buf, tosend);
374 cherokee_buffer_move_to_begin (&hdl->buffer, tosend);
376 if (cherokee_buffer_is_empty (&hdl->buffer))
377 return ret_eof_have_data;
379 return ret_ok;
382 ret_t
383 cherokee_handler_virt_add_headers (cherokee_handler_virt_t *hdl, cherokee_buffer_t *buffer)
385 cherokee_buffer_add_va (buffer, "Content-Length: %d"CRLF, hdl->buffer.len);
387 if (hdl->action > xml)
388 cherokee_buffer_add_str (buffer, "Content-Type: application/xml"CRLF);
389 else if (hdl->action > graph) {
390 cherokee_buffer_add_str (buffer, "Content-Type: image/png"CRLF);
391 } else
392 cherokee_buffer_add_str (buffer, "Content-Type: text/plain"CRLF);
394 return ret_ok;
398 static ret_t
399 while_func_entries (cherokee_buffer_t *key, void *value, void *param) {
400 virConnectPtr conn = NULL;
401 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
402 cherokee_buffer_t *buf = (cherokee_buffer_t *)param;
404 cherokee_buffer_add_va (&uri, "xen://%s/", value);
406 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE && !(conn = virConnectOpen (uri.buf))) {
407 if (!(conn = virConnectOpen (uri.buf))) {
408 return ret_error;
412 if (!conn && !(conn = virConnectOpenReadOnly (uri.buf))) {
413 return ret_error;
414 } else {
415 virDomainPtr dom;
416 if (!(dom = virDomainLookupByName(conn, (const char *) key->buf))) {
417 return ret_error;
418 } else {
419 char *xml = virDomainGetXMLDesc(dom, 0);
420 cherokee_buffer_add(buf, xml, strlen(xml));
422 virConnectClose(conn);
425 cherokee_buffer_mrproper(&uri);
427 return ret_ok;
430 static ret_t load_xml(cherokee_handler_virt_t *hdl, cherokee_buffer_t *xmlDesc) {
431 ret_t ret = ret_error;
432 FILE *fd;
433 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
434 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/index.xml", hdl->user.buf, hdl->vm.buf);
436 if ((fd = fopen(path.buf, "r")) != NULL) {
437 while (!feof(fd)) {
438 char buf[1024];
439 size_t amount = fread(buf, sizeof(char), 1024, fd);
441 cherokee_buffer_add (xmlDesc, buf, amount);
443 fclose(fd);
444 if (xmlDesc->len > 0)
445 ret = ret_ok;
448 cherokee_buffer_mrproper(&path);
449 return ret;
452 static ret_t save_xml(cherokee_handler_virt_t *hdl, virDomainPtr result, cherokee_buffer_t *buf) {
453 ret_t ret = ret_error;
454 FILE *fd;
455 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
456 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s", hdl->user.buf);
458 mkdir(path.buf, 0755);
459 cherokee_buffer_add_va (&path, "/%s", hdl->vm.buf);
461 mkdir(path.buf, 0755);
462 cherokee_buffer_add_str (&path, "/index.xml");
464 if ((fd = fopen(path.buf, "w")) == NULL) {
465 perror(__func__);
466 } else {
467 char *output = virDomainGetXMLDesc(result, VIR_DOMAIN_XML_INACTIVE);
468 if (output) {
469 fwrite(output, strlen(output), sizeof(char), fd);
470 if (buf)
471 cherokee_buffer_add(buf, output, strlen(output));
472 free(output);
474 fclose(fd);
475 ret = ret_ok;
477 cherokee_buffer_mrproper(&path);
478 return ret;
482 static ret_t
483 virt_virt_function(cherokee_handler_virt_t *hdl, virDomainPtr dom, virConnectPtr virConn) {
484 cherokee_connection_t *conn = HANDLER_CONN(hdl);
485 cherokee_buffer_t *buf = &HDL_VIRT(hdl)->buffer;
487 switch (hdl->action) {
488 /* Returns the status of a clone copy */
489 case storageVolCloneStatus_args: {
490 ret_t ret;
491 void *name;
492 struct stat statbuf;
493 cherokee_buffer_t queue = CHEROKEE_BUF_INIT;
495 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
496 if (unlikely(ret < ret_ok)) {
497 TRACE("virt", "storageVolCloneXML_args; name argument not specified");
498 conn->error_code = http_bad_request;
499 return ret_error;
502 cherokee_buffer_add_va (&queue, "/mnt/images/queue/%s_%s.queued", hdl->user.buf, name);
504 if (stat(queue.buf, &statbuf) != 0) {
505 conn->error_code = http_not_found;
506 ret = ret_error;
507 } else {
508 char hardlink[1024];
509 int namelen;
510 if ((namelen = readlink(queue.buf, hardlink, 1023)) == -1) {
511 conn->error_code = http_internal_error;
512 ret = ret_error;
513 } else {
514 hardlink[namelen] = '\0';
515 if (stat(hardlink, &statbuf) != 0) {
516 conn->error_code = http_internal_error;
517 ret = ret_error;
518 } else {
519 struct stat statbuf2;
520 cherokee_buffer_t busy = CHEROKEE_BUF_INIT;
521 cherokee_buffer_add_va (&busy, "/mnt/images/queue/%s_%s.busy", hdl->user.buf, name);
522 if (stat(busy.buf, &statbuf2) != 0) {
523 conn->error_code = http_internal_error;
524 ret = ret_error;
525 } else {
526 cherokee_buffer_add_va (buf, "%f", (float)((float)statbuf2.st_size / (float)statbuf.st_size));
527 ret = ret_ok;
529 cherokee_buffer_mrproper(&busy);
534 cherokee_buffer_mrproper(&queue);
535 return ret;
536 break;
539 /* Save the memory of a domain to a file and suspend the domain */
540 case domainSave: {
541 ret_t ret = ret_ok;
542 int result;
543 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
544 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/memory", hdl->user.buf, hdl->vm.buf);
545 if ((result = virDomainSave(dom, path.buf)) != 0) {
546 TRACE("virt", "Saving of %s/%s failed", hdl->user.buf, hdl->vm.buf);
547 conn->error_code = http_internal_error;
548 ret = ret_error;
550 cherokee_buffer_mrproper(&path);
552 cherokee_buffer_add_long10(buf, result);
553 return ret;
554 break;
557 /* Restore the memory of a domain from a file and resume the domain */
558 case domainRestore: {
559 ret_t ret = ret_ok;
560 struct stat statbuf;
561 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
562 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/memory", hdl->user.buf, hdl->vm.buf);
564 if (stat(path.buf, &statbuf) == 0) {
565 char *destination = create_tender(HDL_CLUSTERSTATS_PROPS(hdl)->client,
566 HDL_CLUSTERSTATS_PROPS(hdl)->threaded_poll,
567 virDomainGetName(dom), virDomainGetMaxMemory(dom));
569 if (destination == NULL) {
570 TRACE("virt", "Nobody wants %s, poor vm!\n", destination);
571 cherokee_buffer_add_long10(buf, -1);
572 } else {
573 virConnectPtr virConnNew;
574 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
576 if (virDomainUndefine(dom) != 0) {
577 TRACE("virt", "Can't undefine %s, some idiot run it already?\n", destination);
578 cherokee_buffer_add_long10(buf, -1);
579 } else {
580 cherokee_buffer_add_va (&uri, "xen://%s/", destination);
582 TRACE("virt", "We will transfer to: %s\n", uri.buf);
584 virConnNew = virConnectOpen (uri.buf);
586 if (virConnNew == NULL) {
587 TRACE("virt", "Can't connect to %s\n", uri.buf);
588 cherokee_buffer_add_long10(buf, -1);
589 } else {
590 cherokee_buffer_add_long10(buf, virDomainRestore(virConnNew, path.buf));
591 virConnectClose(virConn);
592 virConn = virConnNew;
593 /* TODO: Here I actually want virConnNew to be virConn */
596 cherokee_buffer_mrproper(&uri);
597 free(destination);
599 } else {
600 TRACE("virt", "Memory file for %s/%s does not exist", hdl->user.buf, hdl->vm.buf);
601 conn->error_code = http_not_found;
602 ret = ret_error;
605 cherokee_buffer_mrproper(&path);
607 return ret;
608 break;
611 case domainMigrate_args: {
612 void *destination;
613 ret_t ret;
615 ret = cherokee_avl_get_ptr (conn->arguments, "destination", &destination);
617 if (ret != ret_ok) {
618 destination = create_tender(HDL_CLUSTERSTATS_PROPS(hdl)->client,
619 HDL_CLUSTERSTATS_PROPS(hdl)->threaded_poll,
620 virDomainGetName(dom), virDomainGetMaxMemory(dom));
623 if (destination == NULL) {
624 TRACE("virt", "Nobody wants %s, poor vm!\n", destination);
625 cherokee_buffer_add_long10(buf, -1);
626 } else {
627 virConnectPtr virConnNew;
628 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
629 cherokee_buffer_add_va (&uri, "xen://%s/", destination);
630 TRACE("virt", "We will transfer to: %s\n", uri.buf);
631 virConnNew = virConnectOpen (uri.buf);
632 if (virConnNew == NULL) {
633 TRACE("virt", "Can't connect to %s\n", uri.buf);
634 cherokee_buffer_add_long10(buf, -1);
635 } else {
636 /* TODO: voodoo oplossen, pointers virConnNew/domNew gaat niet werken */
637 virDomainPtr domNew = virDomainMigrate(dom, virConnNew, VIR_MIGRATE_LIVE, NULL, NULL, 0);
639 if (domNew) {
640 // virDomainFree(dom);
641 dom = domNew;
643 virConnectClose(virConn);
644 virConn = virConnNew;
646 cherokee_buffer_add_long10(buf, 0);
647 } else {
648 cherokee_buffer_add_long10(buf, -1);
651 cherokee_buffer_mrproper(&uri);
652 free(destination);
655 return ret_ok;
656 break;
659 case domainUndefine: {
660 int result;
661 if ((result = virDomainUndefine(dom)) != 0) {
662 conn->error_code = http_internal_error; /* TODO */
665 cherokee_buffer_add_long10(buf, result);
666 break;
669 case domainAttachDevice_args: {
670 int result;
671 void *temp;
672 ret_t ret;
674 ret = cherokee_avl_get_ptr (conn->arguments, "type", &temp);
676 if (ret == ret_ok) {
677 cherokee_buffer_t xml = CHEROKEE_BUF_INIT;
679 if (strcmp(temp, "disk") == 0) {
680 void *device;
681 if ((ret = cherokee_avl_get_ptr (conn->arguments, "device", &device)) == ret_ok) {
682 void *file;
683 if ((ret = cherokee_avl_get_ptr (conn->arguments, "file", &file)) == ret_ok) {
684 cherokee_buffer_add_va (&xml, VIRT_DISK_XML, file, device);
685 } else {
686 virStorageVolPtr volume = virt_get_vol_by_args(hdl, virConn, 1);
688 if (volume == NULL) {
689 return ret_error;
692 file = virStorageVolGetPath(volume);
693 cherokee_buffer_add_va (&xml, VIRT_DISK_XML, file, device);
694 free(file);
696 virStorageVolFree(volume);
697 ret = ret_ok;
702 else if (strcmp(temp, "interface") == 0) {
703 void *mac, *ip;
704 if ((ret = cherokee_avl_get_ptr (conn->arguments, "mac", &mac)) == ret_ok && (ret = cherokee_avl_get_ptr (conn->arguments, "ip", &ip)) == ret_ok) {
705 cherokee_buffer_add_va (&xml, VIRT_INTERFACE_XML, mac, ip);
706 } else {
707 char *mac = NULL;
708 char *ip = NULL;
709 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
710 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
711 if (getNewMac("dv28", domu.buf, &mac, &ip) == 0)
712 cherokee_buffer_add_va (&xml, VIRT_INTERFACE_XML, mac, ip);
713 cherokee_buffer_mrproper(&domu);
717 if (ret == ret_ok && (result = virDomainAttachDevice(dom, (const char *) xml.buf)) == 0) {
718 ret = ret_ok;
719 } else {
720 conn->error_code = http_internal_error;
721 return ret_error;
724 cherokee_buffer_add_long10(buf, result);
725 cherokee_buffer_mrproper(&xml);
726 } else {
727 TRACE("virt", "DeviceAttach_args; type was not specified");
728 conn->error_code = http_bad_request;
729 return ret_error;
732 break;
735 case domainAttachDevice: {
736 off_t postl;
737 int result;
738 cherokee_buffer_t post = CHEROKEE_BUF_INIT;
739 cherokee_post_get_len (&conn->post, &postl);
740 cherokee_post_walk_read (&conn->post, &post, (cuint_t) postl);
741 if ((result = virDomainAttachDevice(dom, (const char *) post.buf)) != 0)
742 conn->error_code = http_internal_error;
744 cherokee_buffer_mrproper(&post);
745 cherokee_buffer_add_long10(buf, result);
746 break;
750 case domainGetXMLDesc: {
751 char *xml = virDomainGetXMLDesc(dom, 0);
752 cherokee_buffer_add(buf, xml, strlen(xml));
753 free(xml);
754 break;
757 case domainGetXMLSnapshots: {
758 /* TODO: maybe in the future one would like to have all snapshots here */
759 struct stat statbuf;
760 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
762 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/memory", hdl->user.buf, hdl->vm.buf);
764 if (stat(path.buf, &statbuf) != 0) {
765 cherokee_buffer_add_str(buf, "<snapshots />");
766 } else {
767 char *date = ctime(&(statbuf.st_ctime));
768 cherokee_buffer_add_str(buf, "<snapshots>\n");
769 cherokee_buffer_add_va (buf, " <snapshot at=\"");
770 cherokee_buffer_add (buf, date, strlen(date)-1);
771 cherokee_buffer_add_va (buf, "\" />\n");
772 cherokee_buffer_add_str(buf, "</snapshots>");
775 cherokee_buffer_mrproper(&path);
776 break;
779 case domainDetachDevice: {
780 off_t postl;
781 int result;
782 ret_t ret;
783 cherokee_buffer_t post = CHEROKEE_BUF_INIT;
784 cherokee_post_get_len (&conn->post, &postl);
785 cherokee_post_walk_read (&conn->post, &post, (cuint_t) postl);
787 xmlDocPtr doc = xmlParseMemory((const char *) post.buf, post.len);
788 if (doc == NULL) {
789 TRACE("virt", "DeviceAttach; XML document unparceble");
790 conn->error_code = http_bad_request;
791 ret = ret_error;
792 } else
793 ret = ret_ok;
795 if (ret == ret_ok) {
796 if ((result = virDomainDetachDevice(dom, (const char *) post.buf)) != 0) {
797 conn->error_code = http_internal_error;
798 /* TODO: betere afhandeling */
800 xmlXPathContextPtr context = xmlXPathNewContext(doc);
801 if (context != NULL) {
802 xmlXPathObjectPtr obj = xmlXPathEval("string(//interface/mac/@address)", context);
803 xmlXPathFreeContext(context);
804 if ((obj != NULL) && (obj->type == XPATH_STRING) &&
805 (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
806 removeOldMac("dv28", (char *) obj->stringval);
808 xmlXPathFreeObject(obj);
811 xmlFreeDoc(doc);
812 xmlCleanupParser();
815 cherokee_buffer_mrproper(&post);
816 cherokee_buffer_add_long10(buf, result);
817 break;
820 case domainGetID: {
821 cherokee_buffer_add_ulong10(buf, virDomainGetID(dom));
822 break;
824 case domainGetMaxMemory: {
825 cherokee_buffer_add_ulong10(buf, virDomainGetMaxMemory (dom));
826 break;
828 case domainGetMaxVcpus: {
829 cherokee_buffer_add_long10(buf, virDomainGetMaxVcpus (dom));
830 break;
832 case domainGetName: {
833 const char *name = virDomainGetName (dom);
834 cherokee_buffer_add(buf, name, strlen(name));
835 break;
837 case domainGetUUID: {
838 unsigned char uuid[VIR_UUID_BUFLEN];
839 if (virDomainGetUUID(dom, uuid) == 0) {
840 cherokee_buffer_add_str(buf, uuid);
841 } else {
842 conn->error_code = http_internal_error;
843 return ret_error;
845 break;
847 case domainGetUUIDString: {
848 unsigned char uuid[VIR_UUID_STRING_BUFLEN];
849 if (virDomainGetUUIDString(dom, uuid) == 0) {
850 cherokee_buffer_add_str(buf, uuid);
851 } else {
852 conn->error_code = http_internal_error;
853 return ret_error;
855 break;
858 case domainCreate: {
859 char *destination;
860 cherokee_buffer_t xmlDesc = CHEROKEE_BUF_INIT;
861 save_xml(hdl, dom, &xmlDesc);
862 destination = create_tender(HDL_CLUSTERSTATS_PROPS(hdl)->client,
863 HDL_CLUSTERSTATS_PROPS(hdl)->threaded_poll,
864 virDomainGetName(dom), virDomainGetMaxMemory(dom));
866 if (destination == NULL) {
867 TRACE("virt", "Nobody wants %s, poor vm!\n", destination);
868 cherokee_buffer_add_long10(buf, -1);
869 } else {
870 virConnectPtr virConnNew;
871 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
873 if (virDomainUndefine(dom) != 0) {
874 TRACE("virt", "Can't undefine %s, some idiot run it already?\n", destination);
877 cherokee_buffer_add_va (&uri, "xen://%s/", destination);
878 virConnNew = virConnectOpen (uri.buf);
880 if (virConnNew == NULL) {
881 TRACE("virt", "Can't connect to %s\n", uri.buf);
882 cherokee_buffer_add_long10(buf, -1);
883 } else {
884 cherokee_buffer_add_long10(buf,
885 (virDomainCreateLinux(virConnNew, xmlDesc.buf, 0) != NULL ?
886 0 : -1 ));
887 virConnectClose(virConn);
889 virConn = virConnNew;
890 /* TODO: Here I actually want virConnNew to be virConn */
892 cherokee_buffer_mrproper(&uri);
893 free(destination);
896 cherokee_buffer_mrproper(&xmlDesc);
897 break;
900 case domainDestroy: {
901 cherokee_buffer_add_long10(buf, virDomainDestroy (dom));
902 break;
905 case domainReboot: {
906 cherokee_buffer_add_long10(buf, virDomainReboot (dom, 0));
907 break;
910 case domainShutdown: {
911 cherokee_buffer_add_long10(buf, virDomainShutdown (dom));
912 break;
916 case domainGetOSType: {
917 char *ostype = virDomainGetOSType(dom);
918 cherokee_buffer_add(buf, ostype, strlen(ostype));
919 free(ostype);
920 break;
927 if (hdl->action == domainUndefine) {
928 /* Remove VM data from filesystem */
929 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
930 cherokee_buffer_t path2 = CHEROKEE_BUF_INIT;
931 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/index.xml", hdl->user.buf, hdl->vm.buf);
932 cherokee_buffer_add_buffer (&path2, &path);
933 cherokee_buffer_add_str (&path2, ".deleted");
934 // unlink(path.buf); /* TODO: instead of delet replace */
935 rename(path.buf, path2.buf);
936 cherokee_buffer_mrproper(&path);
937 cherokee_buffer_mrproper(&path2);
938 } else if (hdl->action != domainCreate) {
939 save_xml(hdl, dom, NULL);
942 return ret_ok;
945 static virStoragePoolPtr
946 virt_get_pool_by_args(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
947 cherokee_connection_t *conn = HANDLER_CONN(hdl);
948 virStoragePoolPtr pool;
949 void *temp;
950 ret_t ret;
952 ret = cherokee_avl_get_ptr (conn->arguments, "pool", &temp);
953 if (unlikely(ret < ret_ok)) {
954 TRACE("virt", "virStoragePoolPtr; Pool argument not specified");
955 conn->error_code = http_bad_request;
956 return NULL;
959 pool = virStoragePoolLookupByName(virConn, temp);
961 return pool;
964 static virStorageVolPtr
965 virt_get_vol_by_args(cherokee_handler_virt_t *hdl, virConnectPtr virConn, unsigned short int prefix) {
966 cherokee_connection_t *conn = HANDLER_CONN(hdl);
967 virStoragePoolPtr pool;
968 virStorageVolPtr volume;
969 void *temp;
970 ret_t ret;
972 pool = virt_get_pool_by_args(hdl, virConn);
974 if (pool == NULL) {
975 conn->error_code = http_not_found;
976 return NULL;
979 virStoragePoolRefresh(pool, 0); /* TODO: might be better to do it outside */
981 ret = cherokee_avl_get_ptr (conn->arguments, "volume", &temp);
982 if (unlikely(ret < ret_ok)) {
983 TRACE("virt", "virStorageVolPtr; Volume argument not specified");
984 conn->error_code = http_bad_request;
985 return NULL;
988 if (prefix == 1) {
989 cherokee_buffer_t fullvol = CHEROKEE_BUF_INIT;
990 cherokee_buffer_add_va (&fullvol, "%s_%s", hdl->user.buf, temp);
991 volume = virStorageVolLookupByName(pool, fullvol.buf);
992 cherokee_buffer_mrproper(&fullvol);
993 } else {
994 volume = virStorageVolLookupByName(pool, temp);
997 if (volume == NULL)
998 conn->error_code = http_not_found;
1000 virStoragePoolFree(pool);
1002 return volume;
1005 /* This function is the home for all functions that need a working
1006 * pool/volume combination */
1007 static ret_t
1008 virt_pool_vol(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
1009 cherokee_buffer_t *buf = &HDL_VIRT(hdl)->buffer;
1010 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1011 virStorageVolPtr volume;
1012 ret_t ret = ret_ok;
1014 /* We only allow clone to run 'unsafe', others get prefixed */
1015 volume = virt_get_vol_by_args(hdl, virConn, (hdl->action != storageVolCloneXML_args));
1017 /* If the volume doesn't exist, no point to continue */
1018 if (volume == NULL) {
1019 conn->error_code = http_not_found;
1020 return ret_error;
1023 switch (hdl->action) {
1024 /* Sets the password of a specific volume, requires the password= option */
1025 case storageVolSetPassword_args: {
1026 void *temp;
1027 ret = cherokee_avl_get_ptr (conn->arguments, "password", &temp);
1028 if (unlikely(ret < ret_ok)) {
1029 TRACE("virt", "storageVolSetPassword_args; password argument not specified");
1030 conn->error_code = http_bad_request;
1031 ret = ret_error;
1032 goto virt_pool_vol_cleanup;
1033 } else {
1034 cherokee_buffer_t cmd_passwd = CHEROKEE_BUF_INIT;
1035 cherokee_buffer_add_va (&cmd_passwd, "/usr/sbin/passwdchanger.sh %s %s\n", virStorageVolGetKey(volume), temp);
1036 cherokee_buffer_add_long10(buf, system(cmd_passwd.buf));
1037 cherokee_buffer_mrproper(&cmd_passwd);
1039 break;
1042 /* Removes a volume */
1043 case storageVolDelete_args: {
1044 cherokee_buffer_add_long10(buf, virStorageVolDelete(volume, 0));
1045 break;
1048 /* Gives a description of a storage volume in XML */
1049 case storageVolGetXMLDesc_args: {
1050 char *xml = virStorageVolGetXMLDesc(volume, 0);
1051 cherokee_buffer_add(buf, xml, strlen(xml));
1052 free(xml);
1053 break;
1056 /* Clones a volume, insecure method! requires a new name= */
1057 case storageVolCloneXML_args: {
1058 void *name;
1059 cherokee_buffer_t busy = CHEROKEE_BUF_INIT;
1061 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
1062 if (unlikely(ret < ret_ok)) {
1063 TRACE("virt", "storageVolCloneXML_args; name argument not specified");
1064 conn->error_code = http_bad_request;
1065 ret = ret_error;
1066 goto virt_pool_vol_cleanup;
1069 cherokee_buffer_add_va (&busy, "/mnt/images/queue/%s_%s.queued", hdl->user.buf, name);
1071 if (unlikely(symlink(virStorageVolGetKey(volume), busy.buf) == 1)) {
1072 conn->error_code = http_internal_error;
1073 ret = ret_error;
1074 } else {
1075 cherokee_buffer_add_str(buf, "QUEUED");
1078 cherokee_buffer_mrproper(&busy);
1080 break;
1085 virt_pool_vol_cleanup:
1086 /* And in the end we need to free the volume we have used */
1087 virStorageVolFree(volume);
1089 return ret;
1092 static ret_t
1093 virt_virt_new(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
1094 cherokee_buffer_t *buf = &HDL_VIRT(hdl)->buffer;
1095 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1096 cherokee_buffer_t xml = CHEROKEE_BUF_INIT;
1097 virStoragePoolPtr pool = NULL;
1098 ret_t ret = ret_ok;
1100 switch (hdl->action) {
1102 case storageVolCreateXML_args: {
1103 void *temp, *type, *name, *unit;
1104 unsigned long int capacity, allocation;
1106 ret = cherokee_avl_get_ptr (conn->arguments, "pool", &temp);
1107 if (unlikely(ret < ret_ok)) {
1108 TRACE("virt", "storageVolCreateXML_args; pool argument not specified");
1109 conn->error_code = http_bad_request;
1110 goto virt_virt_new_cleanup;
1113 TRACE("args", "%s", temp);
1115 pool = virStoragePoolLookupByName(virConn, temp);
1117 if (pool == NULL) {
1118 conn->error_code = http_not_found;
1119 goto virt_virt_new_cleanup;
1122 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
1123 if (unlikely(ret < ret_ok)) {
1124 TRACE("virt", "storageVolCreateXML_args; name argument not specified");
1125 conn->error_code = http_bad_request;
1126 goto virt_virt_new_cleanup;
1129 ret = cherokee_avl_get_ptr (conn->arguments, "type", &type);
1130 if (unlikely(ret < ret_ok)) {
1131 TRACE("virt", "storageVolCreateXML_args; type argument not specified");
1132 conn->error_code = http_bad_request;
1133 goto virt_virt_new_cleanup;
1136 ret = cherokee_avl_get_ptr (conn->arguments, "unit", &unit);
1137 if (unlikely(ret < ret_ok)) {
1138 TRACE("virt", "storageVolCreateXML_args; unit argument not specified");
1139 conn->error_code = http_bad_request;
1140 goto virt_virt_new_cleanup;
1143 ret = cherokee_avl_get_ptr (conn->arguments, "allocation", &temp);
1144 if (unlikely(ret < ret_ok)) {
1145 TRACE("virt", "storageVolCreateXML_args; allocation argument not specified");
1146 conn->error_code = http_bad_request;
1147 goto virt_virt_new_cleanup;
1150 allocation = strtoul(temp, NULL, 10);
1151 if (errno == ERANGE) {
1152 TRACE("virt", "storageVolCreateXML_args; allocation is not a number");
1153 conn->error_code = http_bad_request;
1154 ret = ret_error;
1155 goto virt_virt_new_cleanup;
1158 ret = cherokee_avl_get_ptr (conn->arguments, "capacity", &temp);
1159 if (unlikely(ret < ret_ok)) {
1160 TRACE("virt", "storageVolCreateXML_args; capacity argument not specified");
1161 conn->error_code = http_bad_request;
1162 goto virt_virt_new_cleanup;
1165 capacity = strtoul(temp, NULL, 10);
1166 if (errno == ERANGE || capacity == 0) {
1167 TRACE("virt", "storageVolCreateXML_args; capacity is not a number");
1168 conn->error_code = http_bad_request;
1169 ret = ret_error;
1170 goto virt_virt_new_cleanup;
1173 cherokee_buffer_add_va (&xml, VIRT_STORAGE_XML, type, hdl->user.buf, name, allocation, unit, capacity, hdl->user.buf, name, hdl->user.buf, name);
1174 break;
1177 case domainDefineXML_args: {
1178 ret_t ret;
1179 unsigned int i;
1180 unsigned long vcpu = 0, interface = 0, memory = 0;
1182 void *temp = NULL;
1184 ret = cherokee_avl_get_ptr (conn->arguments, "vcpu", &temp);
1185 if (ret == ret_ok)
1186 vcpu = strtoul(temp, (char **) NULL, 10);
1188 if (ret != ret_ok || errno == ERANGE || vcpu == 0) {
1189 conn->error_code = http_internal_error;
1190 goto virt_virt_new_cleanup;
1194 ret = cherokee_avl_get_ptr (conn->arguments, "memory", &temp);
1195 if (ret == ret_ok)
1196 memory = strtoul(temp, (char **) NULL, 10);
1198 if (ret != ret_ok || errno == ERANGE || memory == 0) {
1199 conn->error_code = http_internal_error;
1200 goto virt_virt_new_cleanup;
1204 ret = cherokee_avl_get_ptr (conn->arguments, "interface", &temp);
1205 if (ret == ret_ok)
1206 interface = strtoul(temp, (char **) NULL, 10);
1208 if (ret != ret_ok || errno == ERANGE) {
1209 conn->error_code = http_internal_error;
1210 goto virt_virt_new_cleanup;
1213 cherokee_buffer_t cmdline_extra = CHEROKEE_BUF_INIT;
1214 cherokee_buffer_t xml_interfaces = CHEROKEE_BUF_INIT;
1215 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
1216 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
1218 for (i = 0; i < interface; i++) {
1219 char *mac = NULL;
1220 char *ip = NULL;
1221 if (getNewMac("dv28", domu.buf, &mac, &ip) == 0) {
1222 cherokee_buffer_add_va (&xml_interfaces, VIRT_INTERFACE_XML, mac, ip);
1223 if (i == 0) {
1224 /* TODO: terrible hack */
1225 char gateway[16];
1226 char *temp;
1227 strcpy(gateway, ip);
1228 temp = strchr(gateway, '.');
1229 temp = strchr(++temp, '.');
1230 temp = strchr(++temp, '.');
1231 strcpy(++temp, "254");
1232 cherokee_buffer_add_va (&cmdline_extra, VIRT_DOMAIN_CMD_IP, ip, gateway);
1237 cherokee_buffer_mrproper(&domu);
1239 cherokee_buffer_add_va (&xml, VIRT_DOMAIN_XML,
1240 hdl->user.buf, hdl->vm.buf, cmdline_extra.buf, memory, vcpu, xml_interfaces.buf);
1242 cherokee_buffer_mrproper(&xml_interfaces);
1243 cherokee_buffer_mrproper(&cmdline_extra);
1244 break;
1247 case storageVolCreateXML:
1248 case domainDefineXML: {
1249 off_t postl;
1250 cherokee_post_get_len (&conn->post, &postl);
1251 cherokee_post_walk_read (&conn->post, &xml, (cuint_t) postl);
1252 break;
1256 switch (hdl->action) {
1257 case domainDefineXML_args:
1258 case domainDefineXML: {
1259 virDomainPtr result = virDomainDefineXML(virConn, (const char *) xml.buf);
1261 if (result == NULL) {
1262 /* TODO: vrij maken eventuele uitgegeven macs! */
1263 conn->error_code = http_internal_error;
1264 goto virt_virt_new_cleanup;
1267 save_xml(hdl, result, buf);
1269 virDomainFree(result);
1271 break;
1274 case storageVolCreateXML_args:
1275 case storageVolCreateXML: {
1276 virStorageVolPtr vol = virStorageVolCreateXML(pool, xml.buf, 0);
1278 if (vol == NULL) {
1279 cherokee_buffer_add_long10(buf, -1);
1280 goto virt_virt_new_cleanup;
1283 virStorageVolFree(vol);
1285 cherokee_buffer_add_long10(buf, 0);
1286 break;
1290 virt_virt_new_cleanup:
1291 cherokee_buffer_mrproper(&xml);
1293 if (pool)
1294 virStoragePoolFree(pool);
1296 return ret;
1300 static ret_t
1301 virt_virt_do(cherokee_handler_virt_t *hdl, cherokee_buffer_t *domu, cherokee_buffer_t *uri)
1303 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1305 ret_t ret = ret_error;
1306 virConnectPtr virConn = NULL;
1309 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE)
1310 virConn = virConnectOpen (uri->buf);
1312 if (!virConn && !(virConn = virConnectOpenReadOnly (uri->buf))) {
1313 conn->error_code = http_service_unavailable;
1314 return ret_error;
1317 switch (hdl->action) {
1318 case storageVolDelete_args:
1319 case storageVolSetPassword_args:
1320 case storageVolGetXMLDesc_args:
1321 case storageVolCloneXML_args:
1322 ret = virt_pool_vol(hdl, virConn);
1323 break;
1325 case storageVolCreateXML_args:
1326 case storageVolCreateXML:
1327 case domainDefineXML_args:
1328 case domainDefineXML:
1329 ret = virt_virt_new(hdl, virConn);
1330 break;
1332 default: {
1333 virDomainPtr dom;
1334 if ((dom = virDomainLookupByName(virConn, domu->buf)) == NULL) {
1335 conn->error_code = http_not_found;
1336 } else {
1337 ret = virt_virt_function(hdl, dom, virConn);
1338 virDomainFree(dom);
1343 virConnectClose(virConn);
1344 return ret;
1347 static ret_t
1348 virt_while (cherokee_buffer_t *key, void *value, void *param) {
1349 cherokee_handler_virt_t * hdl = param;
1350 // cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
1351 // cherokee_buffer_add_va (&uri, "xen://%s/", ((cherokee_buffer_t *) value)->buf);
1352 // virt_virt_do((cherokee_handler_virt_t *) param, key, &uri);
1353 // cherokee_buffer_mrproper(&uri);
1355 cherokee_buffer_add_va (&hdl->buffer, "<domain><name>%s</name></domain>", key->buf);
1357 return ret_ok;
1360 static ret_t
1361 virt_while_user (cherokee_buffer_t *key, void *value, void *param) {
1362 cherokee_handler_virt_t *hdl = param;
1363 if (key->len > hdl->user.len && key->buf[hdl->user.len] == '_' && strncmp(key->buf, hdl->user.buf, hdl->user.len) == 0)
1364 return virt_while (key, value, param);
1366 return ret_ok;
1369 static ret_t
1370 virt_build_page (cherokee_handler_virt_t *hdl)
1372 ret_t ret;
1373 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1374 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
1375 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
1377 /* We use the webserver methods to parse the querystring */
1378 /* Maybe do something smart with ENUM, begin_args, end_args... */
1379 if ((hdl->action == domainDefineXML_args) ||
1380 (hdl->action == domainAttachDevice_args) ||
1381 (hdl->action == domainMigrate_args) ||
1382 (hdl->action == storageVolGetXMLDesc_args) ||
1383 (hdl->action == storageVolDelete_args) ||
1384 (hdl->action == storageVolSetPassword_args) ||
1385 (hdl->action == storageVolCloneXML_args) ||
1386 (hdl->action == storageVolCloneStatus_args) ||
1387 (hdl->action == storageVolCreateXML_args) ||
1388 (hdl->action == graphLoad_args) ||
1389 (hdl->action == graphInterface_args)) {
1390 ret = cherokee_connection_parse_args (conn);
1391 if (unlikely(ret < ret_ok)) {
1392 conn->error_code = http_internal_error;
1393 return ret_error;
1397 switch (hdl->action) {
1398 case graphInterface_args: {
1399 struct stat statbuf;
1400 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
1401 void *interface;
1402 if ((ret = cherokee_avl_get_ptr (conn->arguments, "interface", &interface)) != ret_ok)
1403 interface = "eth0";
1405 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/interface_%s.rrd", hdl->user.buf, hdl->vm.buf, interface);
1406 if (stat(path.buf, &statbuf) != 0) {
1407 conn->error_code = http_not_found;
1408 ret = ret_error;
1409 } else {
1410 void *width, *height, *start, *end;
1411 if ((ret = cherokee_avl_get_ptr (conn->arguments, "width", &width)) != ret_ok)
1412 width = "600";
1414 if ((ret = cherokee_avl_get_ptr (conn->arguments, "height", &height)) != ret_ok)
1415 height = "200";
1417 if ((ret = cherokee_avl_get_ptr (conn->arguments, "start", &start)) != ret_ok)
1418 start = "now-1h";
1420 if ((ret = cherokee_avl_get_ptr (conn->arguments, "end", &end)) != ret_ok)
1421 end = "now";
1423 cherokee_buffer_t def1 = CHEROKEE_BUF_INIT;
1424 cherokee_buffer_t def2 = CHEROKEE_BUF_INIT;
1425 cherokee_buffer_add_va (&def1, "DEF:rxbytes=%s:rxbytes:AVERAGE:step=30", path.buf);
1426 cherokee_buffer_add_va (&def2, "DEF:txbytes=%s:txbytes:AVERAGE:step=30", path.buf);
1427 char **calcpr = NULL;
1428 int xsize, ysize;
1429 double ymin, ymax;
1430 /* TODO: betere random hier */
1431 char filenametemp[L_tmpnam+1];
1432 char *filename = tmpnam(filenametemp);
1434 // char *filename = mktemp(strdup("handler_virt_XXXXXX"));
1435 char *r_graph[] = { "rrdgraph", filename,
1436 "-a", "PNG",
1437 "-w", width,
1438 "-h", height,
1439 "--start", start,
1440 "--end", end,
1441 "--title", interface,
1442 "--lower-limit", "0", "--alt-autoscale-max", "--vertical-label", "bits per second",
1443 def1.buf,
1444 def2.buf,
1445 "CDEF:txbits=txbytes,8,*",
1446 "CDEF:rxbits=rxbytes,8,*",
1447 "AREA:rxbits#00EE00:rxbits",
1448 "LINE:txbits#0000EE:txbits" };
1450 rrd_graph(25, r_graph, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
1451 if ((ret = cherokee_buffer_read_file(&hdl->buffer, filename)) == ret_error) {
1452 hdl->action = http_internal_error;
1455 unlink(filename);
1457 cherokee_buffer_mrproper(&def1);
1458 cherokee_buffer_mrproper(&def2);
1460 cherokee_buffer_mrproper(&path);
1461 goto virt_build_page_cleanup;
1466 case graphLoad_args: {
1467 struct stat statbuf;
1468 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
1469 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/cpuTime.rrd", hdl->user.buf, hdl->vm.buf);
1470 if (stat(path.buf, &statbuf) != 0) {
1471 conn->error_code = http_not_found;
1472 ret = ret_error;
1473 } else {
1474 void *width, *height, *start, *end;
1475 if ((ret = cherokee_avl_get_ptr (conn->arguments, "width", &width)) != ret_ok)
1476 width = "600";
1478 if ((ret = cherokee_avl_get_ptr (conn->arguments, "height", &height)) != ret_ok)
1479 height = "200";
1481 if ((ret = cherokee_avl_get_ptr (conn->arguments, "start", &start)) != ret_ok)
1482 start = "now-1h";
1484 if ((ret = cherokee_avl_get_ptr (conn->arguments, "end", &end)) != ret_ok)
1485 end = "now";
1487 /* TODO: wat error checking? */
1489 cherokee_buffer_t def = CHEROKEE_BUF_INIT;
1490 cherokee_buffer_add_va (&def, "DEF:cputime=%s:cpuTime:AVERAGE:step=30", path.buf);
1491 char **calcpr = NULL;
1492 int xsize, ysize;
1493 double ymin, ymax;
1494 char *filename = mktemp(strdup("handler_virt_XXXXXX"));
1495 char *r_graph[] = { "rrdgraph",
1496 filename,
1497 "-a", "PNG",
1498 "-w", width,
1499 "-h", height,
1500 "--start", start,
1501 "--end", end,
1502 "--title", hdl->vm.buf,
1503 "--lower-limit", "0", "--alt-autoscale-max", "--vertical-label", "(used / total) time",
1504 def.buf,
1505 "CDEF:cpuload=cputime,1000000000,/",
1506 "LINE:cpuload#EE0000:cpuLoad" };
1508 rrd_graph(22, r_graph, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
1509 if ((ret = cherokee_buffer_read_file(&hdl->buffer, filename)) == ret_error) {
1510 hdl->action = http_internal_error;
1513 unlink(filename);
1514 free(filename);
1516 cherokee_buffer_mrproper(&def);
1518 cherokee_buffer_mrproper(&path);
1519 goto virt_build_page_cleanup;
1523 if (hdl->action > xml && HDL_VIRT_PROPS(hdl)->xsl.len > 0)
1524 cherokee_buffer_add_va (&hdl->buffer, "<?xml version=\"1.0\"?>\n<?xml-stylesheet type=\"text/xsl\" href=\"%s\"?>\n", HDL_VIRT_PROPS(hdl)->xsl.buf);
1527 switch (hdl->action) {
1528 case showall: {
1529 size_t len;
1530 if (cherokee_avl_r_len(&HDL_CLUSTERSTATS_PROPS(hdl)->entries, &len) == ret_ok && len > 0) {
1531 cherokee_buffer_add_str (&hdl->buffer, "<domains>\n");
1532 cherokee_avl_r_while (&HDL_CLUSTERSTATS_PROPS(hdl)->entries, (cherokee_avl_while_func_t) cherokee_handler_clusterstats_while_func_entries, (void *) &hdl->buffer, NULL, NULL);
1533 cherokee_buffer_add_str (&hdl->buffer, "</domains>");
1534 } else {
1535 cherokee_buffer_add_str (&hdl->buffer, "<domains/>");
1538 ret = ret_ok;
1539 break;
1542 case showuservms: {
1543 cherokee_buffer_add_str (&hdl->buffer, "<domains>\n");
1544 get_all_configurations(&HDL_CLUSTERSTATS_PROPS(hdl)->entries, &hdl->user, &hdl->buffer);
1545 cherokee_buffer_add_str (&hdl->buffer, "</domains>");
1546 ret = ret_ok;
1547 break;
1550 default: {
1551 AvahiStringList *list = NULL;
1552 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
1554 ret = cherokee_avl_r_get(&HDL_CLUSTERSTATS_PROPS(hdl)->entries, &domu, (void **) &list);
1556 if (ret == ret_not_found) {
1557 virDomainPtr virDom;
1558 virConnectPtr virConn;
1559 if (HDL_VIRT_PROPS(hdl)->virt.len > 0)
1560 cherokee_buffer_add_va (&uri, "xen://%s/", HDL_VIRT_PROPS(hdl)->virt); // TODO: change!
1562 /* If we have the read only parameter, we will set up a connection to the
1563 * Hypervisor here. */
1564 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE)
1565 virConn = virConnectOpen (uri.buf);
1567 /* We should already have a connection (read only) or build a new connection
1568 * if this doesn't work, we can safely assume our services is fubar */
1569 if (!virConn && !(virConn = virConnectOpenReadOnly (uri.buf))) {
1570 conn->error_code = http_service_unavailable;
1571 return ret_error;
1574 /* We lookup if there is a domain somewhere with this name */
1575 if ((virDom = virDomainLookupByName(virConn, domu.buf)) == NULL) {
1576 /* If the domain is not found on the network the only possible
1577 * command that is possible would be to Define it */
1578 if (hdl->action == domainDefineXML_args || hdl->action == domainDefineXML) {
1579 ret = ret_ok;
1580 } else {
1581 /* We should also look on disk for defined stuff */
1582 cherokee_buffer_t xmlDesc = CHEROKEE_BUF_INIT;
1583 if (load_xml(hdl, &xmlDesc) == ret_ok) {
1584 if ((virDom = virDomainDefineXML(virConn, xmlDesc.buf)) != NULL) {
1585 /* The domain existed and is loaded! */
1586 ret = ret_ok;
1587 } else {
1588 hdl->action = nothing;
1589 conn->error_code = http_internal_error;
1590 ret = ret_error;
1592 } else {
1593 /* Otherwise we don't have anything to do and we should
1594 * return an error */
1595 hdl->action = nothing;
1596 conn->error_code = http_not_found;
1597 ret = ret_error;
1600 cherokee_buffer_mrproper(&xmlDesc);
1602 } else {
1603 /* We don't want te recreate things that already found on the network
1604 * first undefine them! */
1605 if (hdl->action == domainDefineXML_args || hdl->action == domainDefineXML) {
1606 TRACE("virt", "domainDefineXML_args/domainDefineXML; domain already exists");
1607 hdl->action = nothing;
1608 conn->error_code = http_bad_request;
1609 ret = ret_error;
1610 } else {
1611 /* Everything is ok, because nothing is found. */
1612 ret = ret_ok;
1615 /* Domain wasn't NULL, so we should free it here */
1616 virDomainFree(virDom);
1618 virConnectClose (virConn);
1619 } else if (ret == ret_ok) {
1620 char *hostname;
1621 avahi_string_list_get_pair(avahi_string_list_find(list, "dom0"), NULL, &hostname, NULL);
1622 cherokee_buffer_add_va (&uri, "xen://%s/", hostname);
1623 printf("%s\n", uri.buf);
1624 avahi_free(hostname);
1625 } else {
1626 hdl->action = http_internal_error;
1627 hdl->action = nothing;
1628 ret = ret_error;
1634 if (ret == ret_ok && hdl->action != showall && hdl->action != showuservms) {
1635 ret = virt_virt_do(hdl, &domu, &uri);
1638 virt_build_page_cleanup:
1639 cherokee_buffer_mrproper(&domu);
1640 cherokee_buffer_mrproper(&uri);
1642 return ret;
1646 ret_t
1647 cherokee_handler_virt_new (cherokee_handler_t **hdl, cherokee_connection_t *cnt, cherokee_module_props_t *props)
1649 ret_t ret;
1650 CHEROKEE_NEW_STRUCT (n, handler_virt);
1652 /* Init the base class
1655 cherokee_handler_init_base(HANDLER(n), cnt, HANDLER_PROPS(props), PLUGIN_INFO_HANDLER_PTR(virt));
1657 MODULE(n)->init = (handler_func_init_t) cherokee_handler_virt_init;
1658 MODULE(n)->free = (module_func_free_t) cherokee_handler_virt_free;
1659 HANDLER(n)->step = (handler_func_step_t) cherokee_handler_virt_step;
1660 HANDLER(n)->add_headers = (handler_func_add_headers_t) cherokee_handler_virt_add_headers;
1662 HANDLER(n)->support = hsupport_length | hsupport_range;
1664 ret = cherokee_buffer_init (&n->buffer);
1665 if (unlikely(ret != ret_ok))
1666 return ret;
1668 ret = cherokee_buffer_ensure_size (&n->buffer, 4*1024);
1669 if (unlikely(ret != ret_ok))
1670 return ret;
1672 *hdl = HANDLER(n);
1674 return ret_ok;