200770fff734003c35d482a10fc825458edd9323
[handlervirt.git] / handler_virt.c
blob200770fff734003c35d482a10fc825458edd9323
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", hdl->user.buf);
436 mkdir(path.buf, 0755);
437 cherokee_buffer_add_va (&path, "/%s", hdl->vm.buf);
439 mkdir(path.buf, 0755);
440 cherokee_buffer_add_str (&path, "/index.xml");
442 if ((fd = fopen(path.buf, "r")) != NULL) {
443 while (!feof(fd)) {
444 char buf[1024];
445 size_t amount = fread(buf, sizeof(char), 1024, fd);
447 cherokee_buffer_add (xmlDesc, buf, amount);
449 fclose(fd);
450 if (xmlDesc->len > 0)
451 ret = ret_ok;
454 cherokee_buffer_mrproper(&path);
455 return ret;
458 static ret_t save_xml(cherokee_handler_virt_t *hdl, virDomainPtr result, cherokee_buffer_t *buf) {
459 ret_t ret = ret_error;
460 FILE *fd;
461 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
462 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s", hdl->user.buf);
464 mkdir(path.buf, 0755);
465 cherokee_buffer_add_va (&path, "/%s", hdl->vm.buf);
467 mkdir(path.buf, 0755);
468 cherokee_buffer_add_str (&path, "/index.xml");
470 if ((fd = fopen(path.buf, "w")) == NULL) {
471 perror(__func__);
472 } else {
473 char *output = virDomainGetXMLDesc(result, VIR_DOMAIN_XML_INACTIVE);
474 if (output) {
475 fwrite(output, strlen(output), sizeof(char), fd);
476 if (buf)
477 cherokee_buffer_add(buf, output, strlen(output));
478 free(output);
480 fclose(fd);
481 ret = ret_ok;
483 cherokee_buffer_mrproper(&path);
484 return ret;
488 static ret_t
489 virt_virt_function(cherokee_handler_virt_t *hdl, virDomainPtr dom, virConnectPtr virConn) {
490 cherokee_connection_t *conn = HANDLER_CONN(hdl);
491 cherokee_buffer_t *buf = &HDL_VIRT(hdl)->buffer;
493 switch (hdl->action) {
494 /* Returns the status of a clone copy */
495 case storageVolCloneStatus_args: {
496 ret_t ret;
497 void *name;
498 struct stat statbuf;
499 cherokee_buffer_t queue = CHEROKEE_BUF_INIT;
501 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
502 if (unlikely(ret < ret_ok)) {
503 TRACE("virt", "storageVolCloneXML_args; name argument not specified");
504 conn->error_code = http_bad_request;
505 return ret_error;
508 cherokee_buffer_add_va (&queue, "/mnt/images/queue/%s_%s.queued", hdl->user.buf, name);
510 if (stat(queue.buf, &statbuf) != 0) {
511 conn->error_code = http_not_found;
512 ret = ret_error;
513 } else {
514 char hardlink[1024];
515 int namelen;
516 if ((namelen = readlink(queue.buf, hardlink, 1023)) == -1) {
517 conn->error_code = http_internal_error;
518 ret = ret_error;
519 } else {
520 hardlink[namelen] = '\0';
521 if (stat(hardlink, &statbuf) != 0) {
522 conn->error_code = http_internal_error;
523 ret = ret_error;
524 } else {
525 struct stat statbuf2;
526 cherokee_buffer_t busy = CHEROKEE_BUF_INIT;
527 cherokee_buffer_add_va (&busy, "/mnt/images/queue/%s_%s.busy", hdl->user.buf, name);
528 if (stat(busy.buf, &statbuf2) != 0) {
529 conn->error_code = http_internal_error;
530 ret = ret_error;
531 } else {
532 cherokee_buffer_add_va (buf, "%f", (float)((float)statbuf2.st_size / (float)statbuf.st_size));
533 ret = ret_ok;
535 cherokee_buffer_mrproper(&busy);
540 cherokee_buffer_mrproper(&queue);
541 return ret;
542 break;
545 /* Save the memory of a domain to a file and suspend the domain */
546 case domainSave: {
547 ret_t ret = ret_ok;
548 int result;
549 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
550 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/memory", hdl->user.buf, hdl->vm.buf);
551 if ((result = virDomainSave(dom, path.buf)) != 0) {
552 TRACE("virt", "Saving of %s/%s failed", hdl->user.buf, hdl->vm.buf);
553 conn->error_code = http_internal_error;
554 ret = ret_error;
556 cherokee_buffer_mrproper(&path);
558 cherokee_buffer_add_long10(buf, result);
559 return ret;
560 break;
563 /* Restore the memory of a domain from a file and resume the domain */
564 case domainRestore: {
565 ret_t ret = ret_ok;
566 struct stat statbuf;
567 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
568 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/memory", hdl->user.buf, hdl->vm.buf);
570 if (stat(path.buf, &statbuf) == 0) {
571 char *destination = create_tender(HDL_CLUSTERSTATS_PROPS(hdl)->client,
572 HDL_CLUSTERSTATS_PROPS(hdl)->threaded_poll,
573 virDomainGetName(dom), virDomainGetMaxMemory(dom));
575 if (destination == NULL) {
576 TRACE("virt", "Nobody wants %s, poor vm!\n", destination);
577 cherokee_buffer_add_long10(buf, -1);
578 } else {
579 virConnectPtr virConnNew;
580 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
582 if (virDomainUndefine(dom) != 0) {
583 TRACE("virt", "Can't undefine %s, some idiot run it already?\n", destination);
584 cherokee_buffer_add_long10(buf, -1);
585 } else {
586 cherokee_buffer_add_va (&uri, "xen://%s/", destination);
588 TRACE("virt", "We will transfer to: %s\n", uri.buf);
590 virConnNew = virConnectOpen (uri.buf);
592 if (virConnNew == NULL) {
593 TRACE("virt", "Can't connect to %s\n", uri.buf);
594 cherokee_buffer_add_long10(buf, -1);
595 } else {
596 cherokee_buffer_add_long10(buf, virDomainRestore(virConnNew, path.buf));
597 virConnectClose(virConn);
598 virConn = virConnNew;
599 /* TODO: Here I actually want virConnNew to be virConn */
602 cherokee_buffer_mrproper(&uri);
603 free(destination);
605 } else {
606 TRACE("virt", "Memory file for %s/%s does not exist", hdl->user.buf, hdl->vm.buf);
607 conn->error_code = http_not_found;
608 ret = ret_error;
611 cherokee_buffer_mrproper(&path);
613 return ret;
614 break;
617 case domainMigrate_args: {
618 void *destination;
619 ret_t ret;
621 ret = cherokee_avl_get_ptr (conn->arguments, "destination", &destination);
623 if (ret != ret_ok) {
624 destination = create_tender(HDL_CLUSTERSTATS_PROPS(hdl)->client,
625 HDL_CLUSTERSTATS_PROPS(hdl)->threaded_poll,
626 virDomainGetName(dom), virDomainGetMaxMemory(dom));
629 if (destination == NULL) {
630 TRACE("virt", "Nobody wants %s, poor vm!\n", destination);
631 cherokee_buffer_add_long10(buf, -1);
632 } else {
633 virConnectPtr virConnNew;
634 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
635 cherokee_buffer_add_va (&uri, "xen://%s/", destination);
636 TRACE("virt", "We will transfer to: %s\n", uri.buf);
637 virConnNew = virConnectOpen (uri.buf);
638 if (virConnNew == NULL) {
639 TRACE("virt", "Can't connect to %s\n", uri.buf);
640 cherokee_buffer_add_long10(buf, -1);
641 } else {
642 /* TODO: voodoo oplossen, pointers virConnNew/domNew gaat niet werken */
643 virDomainPtr domNew = virDomainMigrate(dom, virConnNew, VIR_MIGRATE_LIVE, NULL, NULL, 0);
645 if (domNew) {
646 virDomainFree(dom);
647 dom = domNew;
649 virConnectClose(virConn);
650 virConn = virConnNew;
652 cherokee_buffer_add_long10(buf, 0);
653 } else {
654 cherokee_buffer_add_long10(buf, -1);
657 cherokee_buffer_mrproper(&uri);
658 free(destination);
661 return ret_ok;
662 break;
665 case domainUndefine: {
666 int result;
667 if ((result = virDomainUndefine(dom)) != 0) {
668 conn->error_code = http_internal_error; /* TODO */
671 cherokee_buffer_add_long10(buf, result);
672 break;
675 case domainAttachDevice_args: {
676 int result;
677 void *temp;
678 ret_t ret;
680 ret = cherokee_avl_get_ptr (conn->arguments, "type", &temp);
682 if (ret == ret_ok) {
683 cherokee_buffer_t xml = CHEROKEE_BUF_INIT;
685 if (strcmp(temp, "disk") == 0) {
686 void *device;
687 if ((ret = cherokee_avl_get_ptr (conn->arguments, "device", &device)) == ret_ok) {
688 void *file;
689 if ((ret = cherokee_avl_get_ptr (conn->arguments, "file", &file)) == ret_ok) {
690 cherokee_buffer_add_va (&xml, VIRT_DISK_XML, file, device);
691 } else {
692 virStorageVolPtr volume = virt_get_vol_by_args(hdl, virConn, 1);
694 if (volume == NULL) {
695 return ret_error;
698 file = virStorageVolGetPath(volume);
699 cherokee_buffer_add_va (&xml, VIRT_DISK_XML, file, device);
700 free(file);
702 virStorageVolFree(volume);
703 ret = ret_ok;
708 else if (strcmp(temp, "interface") == 0) {
709 void *mac, *ip;
710 if ((ret = cherokee_avl_get_ptr (conn->arguments, "mac", &mac)) == ret_ok && (ret = cherokee_avl_get_ptr (conn->arguments, "ip", &ip)) == ret_ok) {
711 cherokee_buffer_add_va (&xml, VIRT_INTERFACE_XML, mac, ip);
712 } else {
713 char *mac = NULL;
714 char *ip = NULL;
715 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
716 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
717 if (getNewMac("dv28", domu.buf, &mac, &ip) == 0)
718 cherokee_buffer_add_va (&xml, VIRT_INTERFACE_XML, mac, ip);
719 cherokee_buffer_mrproper(&domu);
723 if (ret == ret_ok && (result = virDomainAttachDevice(dom, (const char *) xml.buf)) == 0) {
724 ret = ret_ok;
725 } else {
726 conn->error_code = http_internal_error;
727 return ret_error;
730 cherokee_buffer_add_long10(buf, result);
731 cherokee_buffer_mrproper(&xml);
732 } else {
733 TRACE("virt", "DeviceAttach_args; type was not specified");
734 conn->error_code = http_bad_request;
735 return ret_error;
738 break;
741 case domainAttachDevice: {
742 off_t postl;
743 int result;
744 cherokee_buffer_t post = CHEROKEE_BUF_INIT;
745 cherokee_post_get_len (&conn->post, &postl);
746 cherokee_post_walk_read (&conn->post, &post, (cuint_t) postl);
747 if ((result = virDomainAttachDevice(dom, (const char *) post.buf)) != 0)
748 conn->error_code = http_internal_error;
750 cherokee_buffer_mrproper(&post);
751 cherokee_buffer_add_long10(buf, result);
752 break;
756 case domainGetXMLDesc: {
757 char *xml = virDomainGetXMLDesc(dom, 0);
758 cherokee_buffer_add(buf, xml, strlen(xml));
759 free(xml);
760 break;
763 case domainGetXMLSnapshots: {
764 /* TODO: maybe in the future one would like to have all snapshots here */
765 struct stat statbuf;
766 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
768 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/memory", hdl->user.buf, hdl->vm.buf);
770 if (stat(path.buf, &statbuf) != 0) {
771 cherokee_buffer_add_str(buf, "<snapshots />");
772 } else {
773 char *date = ctime(&(statbuf.st_ctime));
774 cherokee_buffer_add_str(buf, "<snapshots>\n");
775 cherokee_buffer_add_va (buf, " <snapshot at=\"");
776 cherokee_buffer_add (buf, date, strlen(date)-1);
777 cherokee_buffer_add_va (buf, "\" />\n");
778 cherokee_buffer_add_str(buf, "</snapshots>");
781 cherokee_buffer_mrproper(&path);
782 break;
785 case domainDetachDevice: {
786 off_t postl;
787 int result;
788 ret_t ret;
789 cherokee_buffer_t post = CHEROKEE_BUF_INIT;
790 cherokee_post_get_len (&conn->post, &postl);
791 cherokee_post_walk_read (&conn->post, &post, (cuint_t) postl);
793 xmlDocPtr doc = xmlParseMemory((const char *) post.buf, post.len);
794 if (doc == NULL) {
795 TRACE("virt", "DeviceAttach; XML document unparceble");
796 conn->error_code = http_bad_request;
797 ret = ret_error;
798 } else
799 ret = ret_ok;
801 if (ret == ret_ok) {
802 if ((result = virDomainDetachDevice(dom, (const char *) post.buf)) != 0) {
803 conn->error_code = http_internal_error;
804 /* TODO: betere afhandeling */
806 xmlXPathContextPtr context = xmlXPathNewContext(doc);
807 if (context != NULL) {
808 xmlXPathObjectPtr obj = xmlXPathEval("string(//interface/mac/@address)", context);
809 xmlXPathFreeContext(context);
810 if ((obj != NULL) && (obj->type == XPATH_STRING) &&
811 (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
812 removeOldMac("dv28", (char *) obj->stringval);
814 xmlXPathFreeObject(obj);
817 xmlFreeDoc(doc);
818 xmlCleanupParser();
821 cherokee_buffer_mrproper(&post);
822 cherokee_buffer_add_long10(buf, result);
823 break;
826 case domainGetID: {
827 cherokee_buffer_add_ulong10(buf, virDomainGetID(dom));
828 break;
830 case domainGetMaxMemory: {
831 cherokee_buffer_add_ulong10(buf, virDomainGetMaxMemory (dom));
832 break;
834 case domainGetMaxVcpus: {
835 cherokee_buffer_add_long10(buf, virDomainGetMaxVcpus (dom));
836 break;
838 case domainGetName: {
839 const char *name = virDomainGetName (dom);
840 cherokee_buffer_add(buf, name, strlen(name));
841 break;
843 case domainGetUUID: {
844 unsigned char uuid[VIR_UUID_BUFLEN];
845 if (virDomainGetUUID(dom, uuid) == 0) {
846 cherokee_buffer_add_str(buf, uuid);
847 } else {
848 conn->error_code = http_internal_error;
849 return ret_error;
851 break;
853 case domainGetUUIDString: {
854 unsigned char uuid[VIR_UUID_STRING_BUFLEN];
855 if (virDomainGetUUIDString(dom, uuid) == 0) {
856 cherokee_buffer_add_str(buf, uuid);
857 } else {
858 conn->error_code = http_internal_error;
859 return ret_error;
861 break;
864 case domainCreate: {
865 char *destination;
866 cherokee_buffer_t xmlDesc = CHEROKEE_BUF_INIT;
867 save_xml(hdl, dom, &xmlDesc);
868 destination = create_tender(HDL_CLUSTERSTATS_PROPS(hdl)->client,
869 HDL_CLUSTERSTATS_PROPS(hdl)->threaded_poll,
870 virDomainGetName(dom), virDomainGetMaxMemory(dom));
872 if (destination == NULL) {
873 TRACE("virt", "Nobody wants %s, poor vm!\n", destination);
874 cherokee_buffer_add_long10(buf, -1);
875 } else {
876 virConnectPtr virConnNew;
877 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
879 if (virDomainUndefine(dom) != 0) {
880 TRACE("virt", "Can't undefine %s, some idiot run it already?\n", destination);
883 cherokee_buffer_add_va (&uri, "xen://%s/", destination);
884 virConnNew = virConnectOpen (uri.buf);
886 if (virConnNew == NULL) {
887 TRACE("virt", "Can't connect to %s\n", uri.buf);
888 cherokee_buffer_add_long10(buf, -1);
889 } else {
890 cherokee_buffer_add_long10(buf,
891 (virDomainCreateLinux(virConnNew, xmlDesc.buf, 0) != NULL ?
892 0 : -1 ));
893 virConnectClose(virConn);
895 virConn = virConnNew;
896 /* TODO: Here I actually want virConnNew to be virConn */
898 cherokee_buffer_mrproper(&uri);
899 free(destination);
902 cherokee_buffer_mrproper(&xmlDesc);
903 break;
906 case domainDestroy: {
907 cherokee_buffer_add_long10(buf, virDomainDestroy (dom));
908 break;
911 case domainReboot: {
912 cherokee_buffer_add_long10(buf, virDomainReboot (dom, 0));
913 break;
916 case domainShutdown: {
917 cherokee_buffer_add_long10(buf, virDomainShutdown (dom));
918 break;
922 case domainGetOSType: {
923 char *ostype = virDomainGetOSType(dom);
924 cherokee_buffer_add(buf, ostype, strlen(ostype));
925 free(ostype);
926 break;
933 if (hdl->action == domainUndefine) {
934 /* Remove VM data from filesystem */
935 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
936 cherokee_buffer_t path2 = CHEROKEE_BUF_INIT;
937 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/index.xml", hdl->user.buf, hdl->vm.buf);
938 cherokee_buffer_add_buffer (&path2, &path);
939 cherokee_buffer_add_str (&path2, ".deleted");
940 // unlink(path.buf); /* TODO: instead of delet replace */
941 rename(path.buf, path2.buf);
942 cherokee_buffer_mrproper(&path);
943 cherokee_buffer_mrproper(&path2);
944 } else if (hdl->action != domainCreate) {
945 save_xml(hdl, dom, NULL);
948 return ret_ok;
951 static virStoragePoolPtr
952 virt_get_pool_by_args(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
953 cherokee_connection_t *conn = HANDLER_CONN(hdl);
954 virStoragePoolPtr pool;
955 void *temp;
956 ret_t ret;
958 ret = cherokee_avl_get_ptr (conn->arguments, "pool", &temp);
959 if (unlikely(ret < ret_ok)) {
960 TRACE("virt", "virStoragePoolPtr; Pool argument not specified");
961 conn->error_code = http_bad_request;
962 return NULL;
965 pool = virStoragePoolLookupByName(virConn, temp);
967 return pool;
970 static virStorageVolPtr
971 virt_get_vol_by_args(cherokee_handler_virt_t *hdl, virConnectPtr virConn, unsigned short int prefix) {
972 cherokee_connection_t *conn = HANDLER_CONN(hdl);
973 virStoragePoolPtr pool;
974 virStorageVolPtr volume;
975 void *temp;
976 ret_t ret;
978 pool = virt_get_pool_by_args(hdl, virConn);
980 if (pool == NULL) {
981 conn->error_code = http_not_found;
982 return NULL;
985 virStoragePoolRefresh(pool, 0); /* TODO: might be better to do it outside */
987 ret = cherokee_avl_get_ptr (conn->arguments, "volume", &temp);
988 if (unlikely(ret < ret_ok)) {
989 TRACE("virt", "virStorageVolPtr; Volume argument not specified");
990 conn->error_code = http_bad_request;
991 return NULL;
994 if (prefix == 1) {
995 cherokee_buffer_t fullvol = CHEROKEE_BUF_INIT;
996 cherokee_buffer_add_va (&fullvol, "%s_%s", hdl->user.buf, temp);
997 volume = virStorageVolLookupByName(pool, fullvol.buf);
998 cherokee_buffer_mrproper(&fullvol);
999 } else {
1000 volume = virStorageVolLookupByName(pool, temp);
1003 if (volume == NULL)
1004 conn->error_code = http_not_found;
1006 virStoragePoolFree(pool);
1008 return volume;
1011 /* This function is the home for all functions that need a working
1012 * pool/volume combination */
1013 static ret_t
1014 virt_pool_vol(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
1015 cherokee_buffer_t *buf = &HDL_VIRT(hdl)->buffer;
1016 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1017 virStorageVolPtr volume;
1018 ret_t ret = ret_ok;
1020 /* We only allow clone to run 'unsafe', others get prefixed */
1021 volume = virt_get_vol_by_args(hdl, virConn, (hdl->action != storageVolCloneXML_args));
1023 /* If the volume doesn't exist, no point to continue */
1024 if (volume == NULL) {
1025 conn->error_code = http_not_found;
1026 return ret_error;
1029 switch (hdl->action) {
1030 /* Sets the password of a specific volume, requires the password= option */
1031 case storageVolSetPassword_args: {
1032 void *temp;
1033 ret = cherokee_avl_get_ptr (conn->arguments, "password", &temp);
1034 if (unlikely(ret < ret_ok)) {
1035 TRACE("virt", "storageVolSetPassword_args; password argument not specified");
1036 conn->error_code = http_bad_request;
1037 ret = ret_error;
1038 goto virt_pool_vol_cleanup;
1039 } else {
1040 cherokee_buffer_t cmd_passwd = CHEROKEE_BUF_INIT;
1041 cherokee_buffer_add_va (&cmd_passwd, "/usr/sbin/passwdchanger.sh %s %s\n", virStorageVolGetKey(volume), temp);
1042 cherokee_buffer_add_long10(buf, system(cmd_passwd.buf));
1043 cherokee_buffer_mrproper(&cmd_passwd);
1045 break;
1048 /* Removes a volume */
1049 case storageVolDelete_args: {
1050 cherokee_buffer_add_long10(buf, virStorageVolDelete(volume, 0));
1051 break;
1054 /* Gives a description of a storage volume in XML */
1055 case storageVolGetXMLDesc_args: {
1056 char *xml = virStorageVolGetXMLDesc(volume, 0);
1057 cherokee_buffer_add(buf, xml, strlen(xml));
1058 free(xml);
1059 break;
1062 /* Clones a volume, insecure method! requires a new name= */
1063 case storageVolCloneXML_args: {
1064 void *name;
1065 cherokee_buffer_t busy = CHEROKEE_BUF_INIT;
1067 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
1068 if (unlikely(ret < ret_ok)) {
1069 TRACE("virt", "storageVolCloneXML_args; name argument not specified");
1070 conn->error_code = http_bad_request;
1071 ret = ret_error;
1072 goto virt_pool_vol_cleanup;
1075 cherokee_buffer_add_va (&busy, "/mnt/images/queue/%s_%s.queued", hdl->user.buf, name);
1077 if (unlikely(symlink(virStorageVolGetKey(volume), busy.buf) == 1)) {
1078 conn->error_code = http_internal_error;
1079 ret = ret_error;
1080 } else {
1081 cherokee_buffer_add_str(buf, "QUEUED");
1084 cherokee_buffer_mrproper(&busy);
1086 break;
1091 virt_pool_vol_cleanup:
1092 /* And in the end we need to free the volume we have used */
1093 virStorageVolFree(volume);
1095 return ret;
1098 static ret_t
1099 virt_virt_new(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
1100 cherokee_buffer_t *buf = &HDL_VIRT(hdl)->buffer;
1101 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1102 cherokee_buffer_t xml = CHEROKEE_BUF_INIT;
1103 virStoragePoolPtr pool = NULL;
1104 ret_t ret = ret_ok;
1106 switch (hdl->action) {
1108 case storageVolCreateXML_args: {
1109 void *temp, *type, *name, *unit;
1110 unsigned long int capacity, allocation;
1112 ret = cherokee_avl_get_ptr (conn->arguments, "pool", &temp);
1113 if (unlikely(ret < ret_ok)) {
1114 TRACE("virt", "storageVolCreateXML_args; pool argument not specified");
1115 conn->error_code = http_bad_request;
1116 goto virt_virt_new_cleanup;
1119 TRACE("args", "%s", temp);
1121 pool = virStoragePoolLookupByName(virConn, temp);
1123 if (pool == NULL) {
1124 conn->error_code = http_not_found;
1125 goto virt_virt_new_cleanup;
1128 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
1129 if (unlikely(ret < ret_ok)) {
1130 TRACE("virt", "storageVolCreateXML_args; name argument not specified");
1131 conn->error_code = http_bad_request;
1132 goto virt_virt_new_cleanup;
1135 ret = cherokee_avl_get_ptr (conn->arguments, "type", &type);
1136 if (unlikely(ret < ret_ok)) {
1137 TRACE("virt", "storageVolCreateXML_args; type argument not specified");
1138 conn->error_code = http_bad_request;
1139 goto virt_virt_new_cleanup;
1142 ret = cherokee_avl_get_ptr (conn->arguments, "unit", &unit);
1143 if (unlikely(ret < ret_ok)) {
1144 TRACE("virt", "storageVolCreateXML_args; unit argument not specified");
1145 conn->error_code = http_bad_request;
1146 goto virt_virt_new_cleanup;
1149 ret = cherokee_avl_get_ptr (conn->arguments, "allocation", &temp);
1150 if (unlikely(ret < ret_ok)) {
1151 TRACE("virt", "storageVolCreateXML_args; allocation argument not specified");
1152 conn->error_code = http_bad_request;
1153 goto virt_virt_new_cleanup;
1156 allocation = strtoul(temp, NULL, 10);
1157 if (errno == ERANGE) {
1158 TRACE("virt", "storageVolCreateXML_args; allocation is not a number");
1159 conn->error_code = http_bad_request;
1160 ret = ret_error;
1161 goto virt_virt_new_cleanup;
1164 ret = cherokee_avl_get_ptr (conn->arguments, "capacity", &temp);
1165 if (unlikely(ret < ret_ok)) {
1166 TRACE("virt", "storageVolCreateXML_args; capacity argument not specified");
1167 conn->error_code = http_bad_request;
1168 goto virt_virt_new_cleanup;
1171 capacity = strtoul(temp, NULL, 10);
1172 if (errno == ERANGE || capacity == 0) {
1173 TRACE("virt", "storageVolCreateXML_args; capacity is not a number");
1174 conn->error_code = http_bad_request;
1175 ret = ret_error;
1176 goto virt_virt_new_cleanup;
1179 cherokee_buffer_add_va (&xml, VIRT_STORAGE_XML, type, hdl->user.buf, name, allocation, unit, capacity, hdl->user.buf, name, hdl->user.buf, name);
1180 break;
1183 case domainDefineXML_args: {
1184 ret_t ret;
1185 unsigned int i;
1186 unsigned long vcpu = 0, interface = 0, memory = 0;
1188 void *temp = NULL;
1190 ret = cherokee_avl_get_ptr (conn->arguments, "vcpu", &temp);
1191 if (ret == ret_ok)
1192 vcpu = strtoul(temp, (char **) NULL, 10);
1194 if (ret != ret_ok || errno == ERANGE || vcpu == 0) {
1195 conn->error_code = http_internal_error;
1196 goto virt_virt_new_cleanup;
1200 ret = cherokee_avl_get_ptr (conn->arguments, "memory", &temp);
1201 if (ret == ret_ok)
1202 memory = strtoul(temp, (char **) NULL, 10);
1204 if (ret != ret_ok || errno == ERANGE || memory == 0) {
1205 conn->error_code = http_internal_error;
1206 goto virt_virt_new_cleanup;
1210 ret = cherokee_avl_get_ptr (conn->arguments, "interface", &temp);
1211 if (ret == ret_ok)
1212 interface = strtoul(temp, (char **) NULL, 10);
1214 if (ret != ret_ok || errno == ERANGE) {
1215 conn->error_code = http_internal_error;
1216 goto virt_virt_new_cleanup;
1219 cherokee_buffer_t cmdline_extra = CHEROKEE_BUF_INIT;
1220 cherokee_buffer_t xml_interfaces = CHEROKEE_BUF_INIT;
1221 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
1222 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
1224 for (i = 0; i < interface; i++) {
1225 char *mac = NULL;
1226 char *ip = NULL;
1227 if (getNewMac("dv28", domu.buf, &mac, &ip) == 0) {
1228 cherokee_buffer_add_va (&xml_interfaces, VIRT_INTERFACE_XML, mac, ip);
1229 if (i == 0) {
1230 /* TODO: terrible hack */
1231 char gateway[16];
1232 char *temp;
1233 strcpy(gateway, ip);
1234 temp = strchr(gateway, '.');
1235 temp = strchr(++temp, '.');
1236 temp = strchr(++temp, '.');
1237 strcpy(++temp, "254");
1238 cherokee_buffer_add_va (&cmdline_extra, VIRT_DOMAIN_CMD_IP, ip, gateway);
1243 cherokee_buffer_mrproper(&domu);
1245 cherokee_buffer_add_va (&xml, VIRT_DOMAIN_XML,
1246 hdl->user.buf, hdl->vm.buf, cmdline_extra.buf, memory, vcpu, xml_interfaces.buf);
1248 cherokee_buffer_mrproper(&xml_interfaces);
1249 cherokee_buffer_mrproper(&cmdline_extra);
1250 break;
1253 case storageVolCreateXML:
1254 case domainDefineXML: {
1255 off_t postl;
1256 cherokee_post_get_len (&conn->post, &postl);
1257 cherokee_post_walk_read (&conn->post, &xml, (cuint_t) postl);
1258 break;
1262 switch (hdl->action) {
1263 case domainDefineXML_args:
1264 case domainDefineXML: {
1265 virDomainPtr result = virDomainDefineXML(virConn, (const char *) xml.buf);
1267 if (result == NULL) {
1268 /* TODO: vrij maken eventuele uitgegeven macs! */
1269 conn->error_code = http_internal_error;
1270 goto virt_virt_new_cleanup;
1273 save_xml(hdl, result, buf);
1275 virDomainFree(result);
1277 break;
1280 case storageVolCreateXML_args:
1281 case storageVolCreateXML: {
1282 virStorageVolPtr vol = virStorageVolCreateXML(pool, xml.buf, 0);
1284 if (vol == NULL) {
1285 cherokee_buffer_add_long10(buf, -1);
1286 goto virt_virt_new_cleanup;
1289 virStorageVolFree(vol);
1291 cherokee_buffer_add_long10(buf, 0);
1292 break;
1296 virt_virt_new_cleanup:
1297 cherokee_buffer_mrproper(&xml);
1299 if (pool)
1300 virStoragePoolFree(pool);
1302 return ret;
1306 static ret_t
1307 virt_virt_do(cherokee_handler_virt_t *hdl, cherokee_buffer_t *domu, cherokee_buffer_t *uri)
1309 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1311 ret_t ret = ret_error;
1312 virConnectPtr virConn = NULL;
1315 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE)
1316 virConn = virConnectOpen (uri->buf);
1318 if (!virConn && !(virConn = virConnectOpenReadOnly (uri->buf))) {
1319 conn->error_code = http_service_unavailable;
1320 return ret_error;
1323 switch (hdl->action) {
1324 case storageVolDelete_args:
1325 case storageVolSetPassword_args:
1326 case storageVolGetXMLDesc_args:
1327 case storageVolCloneXML_args:
1328 ret = virt_pool_vol(hdl, virConn);
1329 break;
1331 case storageVolCreateXML_args:
1332 case storageVolCreateXML:
1333 case domainDefineXML_args:
1334 case domainDefineXML:
1335 ret = virt_virt_new(hdl, virConn);
1336 break;
1338 default: {
1339 virDomainPtr dom;
1340 if ((dom = virDomainLookupByName(virConn, domu->buf)) == NULL) {
1341 conn->error_code = http_not_found;
1342 } else {
1343 ret = virt_virt_function(hdl, dom, virConn);
1344 virDomainFree(dom);
1349 virConnectClose(virConn);
1350 return ret;
1353 static ret_t
1354 virt_while (cherokee_buffer_t *key, void *value, void *param) {
1355 cherokee_handler_virt_t * hdl = param;
1356 // cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
1357 // cherokee_buffer_add_va (&uri, "xen://%s/", ((cherokee_buffer_t *) value)->buf);
1358 // virt_virt_do((cherokee_handler_virt_t *) param, key, &uri);
1359 // cherokee_buffer_mrproper(&uri);
1361 cherokee_buffer_add_va (&hdl->buffer, "<domain><name>%s</name></domain>", key->buf);
1363 return ret_ok;
1366 static ret_t
1367 virt_while_user (cherokee_buffer_t *key, void *value, void *param) {
1368 cherokee_handler_virt_t *hdl = param;
1369 if (key->len > hdl->user.len && key->buf[hdl->user.len] == '_' && strncmp(key->buf, hdl->user.buf, hdl->user.len) == 0)
1370 return virt_while (key, value, param);
1372 return ret_ok;
1375 static ret_t
1376 virt_build_page (cherokee_handler_virt_t *hdl)
1378 ret_t ret;
1379 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1380 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
1381 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
1383 /* We use the webserver methods to parse the querystring */
1384 /* Maybe do something smart with ENUM, begin_args, end_args... */
1385 if ((hdl->action == domainDefineXML_args) ||
1386 (hdl->action == domainAttachDevice_args) ||
1387 (hdl->action == domainMigrate_args) ||
1388 (hdl->action == storageVolGetXMLDesc_args) ||
1389 (hdl->action == storageVolDelete_args) ||
1390 (hdl->action == storageVolSetPassword_args) ||
1391 (hdl->action == storageVolCloneXML_args) ||
1392 (hdl->action == storageVolCloneStatus_args) ||
1393 (hdl->action == storageVolCreateXML_args) ||
1394 (hdl->action == graphLoad_args) ||
1395 (hdl->action == graphInterface_args)) {
1396 ret = cherokee_connection_parse_args (conn);
1397 if (unlikely(ret < ret_ok)) {
1398 conn->error_code = http_internal_error;
1399 return ret_error;
1403 switch (hdl->action) {
1404 case graphInterface_args: {
1405 struct stat statbuf;
1406 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
1407 void *interface;
1408 if ((ret = cherokee_avl_get_ptr (conn->arguments, "interface", &interface)) != ret_ok)
1409 interface = "eth0";
1411 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/interface_%s.rrd", hdl->user.buf, hdl->vm.buf, interface);
1412 if (stat(path.buf, &statbuf) != 0) {
1413 conn->error_code = http_not_found;
1414 ret = ret_error;
1415 } else {
1416 void *width, *height, *start, *end;
1417 if ((ret = cherokee_avl_get_ptr (conn->arguments, "width", &width)) != ret_ok)
1418 width = "600";
1420 if ((ret = cherokee_avl_get_ptr (conn->arguments, "height", &height)) != ret_ok)
1421 height = "200";
1423 if ((ret = cherokee_avl_get_ptr (conn->arguments, "start", &start)) != ret_ok)
1424 start = "now-1h";
1426 if ((ret = cherokee_avl_get_ptr (conn->arguments, "end", &end)) != ret_ok)
1427 end = "now";
1429 cherokee_buffer_t def1 = CHEROKEE_BUF_INIT;
1430 cherokee_buffer_t def2 = CHEROKEE_BUF_INIT;
1431 cherokee_buffer_add_va (&def1, "DEF:rxbytes=%s:rxbytes:AVERAGE:step=30", path.buf);
1432 cherokee_buffer_add_va (&def2, "DEF:txbytes=%s:txbytes:AVERAGE:step=30", path.buf);
1433 char **calcpr = NULL;
1434 int xsize, ysize;
1435 double ymin, ymax;
1436 /* TODO: betere random hier */
1437 char filenametemp[L_tmpnam+1];
1438 char *filename = tmpnam(filenametemp);
1440 // char *filename = mktemp(strdup("handler_virt_XXXXXX"));
1441 char *r_graph[] = { "rrdgraph", filename,
1442 "-a", "PNG",
1443 "-w", width,
1444 "-h", height,
1445 "--start", start,
1446 "--end", end,
1447 "--title", interface,
1448 "--lower-limit", "0", "--alt-autoscale-max", "--vertical-label", "bits per second",
1449 def1.buf,
1450 def2.buf,
1451 "CDEF:txbits=txbytes,8,*",
1452 "CDEF:rxbits=rxbytes,8,*",
1453 "AREA:rxbits#00EE00:rxbits",
1454 "LINE:txbits#0000EE:txbits" };
1456 rrd_graph(25, r_graph, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
1457 if ((ret = cherokee_buffer_read_file(&hdl->buffer, filename)) == ret_error) {
1458 hdl->action = http_internal_error;
1461 unlink(filename);
1463 cherokee_buffer_mrproper(&def1);
1464 cherokee_buffer_mrproper(&def2);
1466 cherokee_buffer_mrproper(&path);
1467 goto virt_build_page_cleanup;
1472 case graphLoad_args: {
1473 struct stat statbuf;
1474 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
1475 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/cpuTime.rrd", hdl->user.buf, hdl->vm.buf);
1476 if (stat(path.buf, &statbuf) != 0) {
1477 conn->error_code = http_not_found;
1478 ret = ret_error;
1479 } else {
1480 void *width, *height, *start, *end;
1481 if ((ret = cherokee_avl_get_ptr (conn->arguments, "width", &width)) != ret_ok)
1482 width = "600";
1484 if ((ret = cherokee_avl_get_ptr (conn->arguments, "height", &height)) != ret_ok)
1485 height = "200";
1487 if ((ret = cherokee_avl_get_ptr (conn->arguments, "start", &start)) != ret_ok)
1488 start = "now-1h";
1490 if ((ret = cherokee_avl_get_ptr (conn->arguments, "end", &end)) != ret_ok)
1491 end = "now";
1493 /* TODO: wat error checking? */
1495 cherokee_buffer_t def = CHEROKEE_BUF_INIT;
1496 cherokee_buffer_add_va (&def, "DEF:cputime=%s:cpuTime:AVERAGE:step=30", path.buf);
1497 char **calcpr = NULL;
1498 int xsize, ysize;
1499 double ymin, ymax;
1500 char *filename = mktemp(strdup("handler_virt_XXXXXX"));
1501 char *r_graph[] = { "rrdgraph",
1502 filename,
1503 "-a", "PNG",
1504 "-w", width,
1505 "-h", height,
1506 "--start", start,
1507 "--end", end,
1508 "--title", hdl->vm.buf,
1509 "--lower-limit", "0", "--alt-autoscale-max", "--vertical-label", "(used / total) time",
1510 def.buf,
1511 "CDEF:cpuload=cputime,1000000000,/",
1512 "LINE:cpuload#EE0000:cpuLoad" };
1514 rrd_graph(22, r_graph, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
1515 if ((ret = cherokee_buffer_read_file(&hdl->buffer, filename)) == ret_error) {
1516 hdl->action = http_internal_error;
1519 unlink(filename);
1520 free(filename);
1522 cherokee_buffer_mrproper(&def);
1524 cherokee_buffer_mrproper(&path);
1525 goto virt_build_page_cleanup;
1529 if (hdl->action > xml && HDL_VIRT_PROPS(hdl)->xsl.len > 0)
1530 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);
1533 switch (hdl->action) {
1534 case showall: {
1535 size_t len;
1536 if (cherokee_avl_r_len(&HDL_CLUSTERSTATS_PROPS(hdl)->entries, &len) == ret_ok && len > 0) {
1537 cherokee_buffer_add_str (&hdl->buffer, "<domains>\n");
1538 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);
1539 cherokee_buffer_add_str (&hdl->buffer, "</domains>");
1540 } else {
1541 cherokee_buffer_add_str (&hdl->buffer, "<domains/>");
1544 ret = ret_ok;
1545 break;
1548 case showuservms: {
1549 cherokee_buffer_add_str (&hdl->buffer, "<domains>\n");
1550 get_all_configurations(&HDL_CLUSTERSTATS_PROPS(hdl)->entries, &hdl->user, &hdl->buffer);
1551 cherokee_buffer_add_str (&hdl->buffer, "</domains>");
1552 ret = ret_ok;
1553 break;
1556 default: {
1557 AvahiStringList *list = NULL;
1558 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
1560 ret = cherokee_avl_r_get(&HDL_CLUSTERSTATS_PROPS(hdl)->entries, &domu, (void **) &list);
1562 if (ret == ret_not_found) {
1563 virDomainPtr virDom;
1564 virConnectPtr virConn;
1565 if (HDL_VIRT_PROPS(hdl)->virt.len > 0)
1566 cherokee_buffer_add_va (&uri, "xen://%s/", HDL_VIRT_PROPS(hdl)->virt); // TODO: change!
1568 /* If we have the read only parameter, we will set up a connection to the
1569 * Hypervisor here. */
1570 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE)
1571 virConn = virConnectOpen (uri.buf);
1573 /* We should already have a connection (read only) or build a new connection
1574 * if this doesn't work, we can safely assume our services is fubar */
1575 if (!virConn && !(virConn = virConnectOpenReadOnly (uri.buf))) {
1576 conn->error_code = http_service_unavailable;
1577 return ret_error;
1580 /* We lookup if there is a domain somewhere with this name */
1581 if ((virDom = virDomainLookupByName(virConn, domu.buf)) == NULL) {
1582 /* If the domain is not found on the network the only possible
1583 * command that is possible would be to Define it */
1584 if (hdl->action == domainDefineXML_args || hdl->action == domainDefineXML) {
1585 ret = ret_ok;
1586 } else {
1587 /* We should also look on disk for defined stuff */
1588 cherokee_buffer_t xmlDesc = CHEROKEE_BUF_INIT;
1589 if (load_xml(hdl, &xmlDesc) == ret_ok)
1590 if ((virDom = virDomainDefineXML(virConn, xmlDesc.buf)) != NULL) {
1591 /* The domain existed and is loaded! */
1592 ret = ret_ok;
1593 } else {
1594 hdl->action = nothing;
1595 conn->error_code = http_internal_error;
1596 ret = ret_error;
1598 } else {
1599 /* Otherwise we don't have anything to do and we should
1600 * return an error */
1601 hdl->action = nothing;
1602 conn->error_code = http_not_found;
1603 ret = ret_error;
1606 cherokee_buffer_mrproper(&xmlDesc);
1608 } else {
1609 /* We don't want te recreate things that already found on the network
1610 * first undefine them! */
1611 if (hdl->action == domainDefineXML_args || hdl->action == domainDefineXML) {
1612 TRACE("virt", "domainDefineXML_args/domainDefineXML; domain already exists");
1613 hdl->action = nothing;
1614 conn->error_code = http_bad_request;
1615 ret = ret_error;
1616 } else {
1617 /* Everything is ok, because nothing is found. */
1618 ret = ret_ok;
1621 /* Domain wasn't NULL, so we should free it here */
1622 virDomainFree(virDom);
1624 virConnectClose (virConn);
1625 } else if (ret == ret_ok) {
1626 char *hostname;
1627 avahi_string_list_get_pair(avahi_string_list_find(list, "dom0"), NULL, &hostname, NULL);
1628 cherokee_buffer_add_va (&uri, "xen://%s/", hostname);
1629 printf("%s\n", uri.buf);
1630 avahi_free(hostname);
1631 } else {
1632 hdl->action = http_internal_error;
1633 hdl->action = nothing;
1634 ret = ret_error;
1640 if (ret == ret_ok && hdl->action != showall && hdl->action != showuservms) {
1641 ret = virt_virt_do(hdl, &domu, &uri);
1644 virt_build_page_cleanup:
1645 cherokee_buffer_mrproper(&domu);
1646 cherokee_buffer_mrproper(&uri);
1648 return ret;
1652 ret_t
1653 cherokee_handler_virt_new (cherokee_handler_t **hdl, cherokee_connection_t *cnt, cherokee_module_props_t *props)
1655 ret_t ret;
1656 CHEROKEE_NEW_STRUCT (n, handler_virt);
1658 /* Init the base class
1661 cherokee_handler_init_base(HANDLER(n), cnt, HANDLER_PROPS(props), PLUGIN_INFO_HANDLER_PTR(virt));
1663 MODULE(n)->init = (handler_func_init_t) cherokee_handler_virt_init;
1664 MODULE(n)->free = (module_func_free_t) cherokee_handler_virt_free;
1665 HANDLER(n)->step = (handler_func_step_t) cherokee_handler_virt_step;
1666 HANDLER(n)->add_headers = (handler_func_add_headers_t) cherokee_handler_virt_add_headers;
1668 HANDLER(n)->support = hsupport_length | hsupport_range;
1670 ret = cherokee_buffer_init (&n->buffer);
1671 if (unlikely(ret != ret_ok))
1672 return ret;
1674 ret = cherokee_buffer_ensure_size (&n->buffer, 4*1024);
1675 if (unlikely(ret != ret_ok))
1676 return ret;
1678 *hdl = HANDLER(n);
1680 return ret_ok;