Intermediate update... consider this version completely broken
[handlervirt.git] / handler_virt.c
blob4f678096c2ad550df1f50aaa0f7af5594661e5cc
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_avahi.h"
84 #include <cherokee/cherokee.h>
85 #include <libxml/parser.h>
86 #include <libxml/xpath.h>
87 #include <float.h>
89 #include <avahi-client/client.h>
90 #include <avahi-client/publish.h>
91 #include <avahi-client/lookup.h>
93 #include <avahi-common/alternative.h>
94 #include <avahi-common/simple-watch.h>
95 #include <avahi-common/malloc.h>
96 #include <avahi-common/error.h>
97 #include <avahi-common/timeval.h>
100 /* Plug-in initialization
102 * In this function you can use any of these:
103 * http_delete | http_get | http_post | http_put
105 * For a full list: cherokee_http_method_t
107 * It is what your handler to be implements.
110 PLUGIN_INFO_HANDLER_EASIEST_INIT (virt, http_get | http_post);
113 /* Methods implementation
115 static ret_t
116 props_free (cherokee_handler_virt_props_t *props)
118 cherokee_buffer_mrproper(&props->xsl);
119 cherokee_buffer_mrproper(&props->virt);
121 return cherokee_handler_avahi_props_free (PROP_AVAHI(props));
125 ret_t
126 cherokee_handler_virt_configure (cherokee_config_node_t *conf, cherokee_server_t *srv, cherokee_module_props_t **_props)
128 cherokee_list_t *i;
129 cherokee_handler_virt_props_t *props;
131 /* Instance a new property object
134 if (*_props == NULL) {
135 CHEROKEE_NEW_STRUCT (n, handler_virt_props);
137 cherokee_handler_avahi_props_init_base (PROP_AVAHI(n), MODULE_PROPS_FREE(props_free));
139 /* Look at handler_virt.h
140 * This is an virt of configuration.
142 n->authenticate = true; /* by default we are secure! */
143 n->read_only = true; /* by default we are secure! */
144 cherokee_buffer_init (&n->xsl); /* we support a custom header */
145 cherokee_buffer_init (&n->virt); /* your first xenserver */
147 *_props = MODULE_PROPS(n);
150 props = PROP_VIRT(*_props);
152 cherokee_config_node_foreach (i, conf) {
153 cherokee_config_node_t *subconf = CONFIG_NODE(i);
155 if (equal_buf_str (&subconf->key, "authenticate")) {
156 props->authenticate = atoi(subconf->val.buf);
158 else if (equal_buf_str (&subconf->key, "read_only")) {
159 props->read_only = atoi(subconf->val.buf);
161 else if (equal_buf_str (&subconf->key, "xsl")) {
162 cherokee_buffer_add_buffer (&props->xsl, &subconf->val);
164 else if (equal_buf_str (&subconf->key, "virt")) {
165 cherokee_buffer_add_buffer (&props->virt, &subconf->val);
170 /* Init base class
173 return cherokee_handler_avahi_configure (conf, srv, _props);
176 static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
177 switch (state) {
178 case AVAHI_ENTRY_GROUP_ESTABLISHED :
179 /* The entry group has been established successfully */
180 break;
182 case AVAHI_ENTRY_GROUP_COLLISION : {
183 /* A service name collision with a remote service
184 * happened. */
185 break;
188 case AVAHI_ENTRY_GROUP_FAILURE :
189 /* Some kind of failure happened while we were registering our services */
190 break;
192 case AVAHI_ENTRY_GROUP_UNCOMMITED:
193 case AVAHI_ENTRY_GROUP_REGISTERING:
198 static void tender_resolve_callback(
199 AvahiServiceResolver *r,
200 AVAHI_GCC_UNUSED AvahiIfIndex interface,
201 AVAHI_GCC_UNUSED AvahiProtocol protocol,
202 AvahiResolverEvent event,
203 const char *name,
204 const char *type,
205 const char *domain,
206 const char *host_name,
207 const AvahiAddress *address,
208 uint16_t port,
209 AvahiStringList *txt,
210 AvahiLookupResultFlags flags,
211 void* userdata) {
213 tender_t *tender = userdata;
215 assert(r);
217 /* Called whenever a service has been resolved successfully or timed out */
219 switch (event) {
220 case AVAHI_RESOLVER_FAILURE:
221 avahi_service_resolver_free(r);
222 break;
224 case AVAHI_RESOLVER_FOUND: {
225 char a[AVAHI_ADDRESS_STR_MAX], *t;
226 AvahiStringList *needle;
228 fprintf(stderr, "(Resolver) Service '%s' of type '%s' in domain '%s':\n", name, type, domain);
230 avahi_address_snprint(a, sizeof(a), address);
231 t = avahi_string_list_to_string(txt);
232 fprintf(stderr,
233 "\t%s:%u (%s)\n"
234 "\tTXT=%s\n"
235 "\tcookie is %u\n"
236 "\tis_local: %i\n"
237 "\tour_own: %i\n"
238 "\twide_area: %i\n"
239 "\tmulticast: %i\n"
240 "\tcached: %i\n",
241 host_name, port, a,
243 avahi_string_list_get_service_cookie(txt),
244 !!(flags & AVAHI_LOOKUP_RESULT_LOCAL),
245 !!(flags & AVAHI_LOOKUP_RESULT_OUR_OWN),
246 !!(flags & AVAHI_LOOKUP_RESULT_WIDE_AREA),
247 !!(flags & AVAHI_LOOKUP_RESULT_MULTICAST),
248 !!(flags & AVAHI_LOOKUP_RESULT_CACHED));
251 avahi_free(t);
253 unsigned int len = strlen(tender->name);
255 if (strlen(name) > len && name[len] == '.' && strncmp(tender->name, name, len) == 0) {
256 if ((needle = avahi_string_list_find (txt, "cost")) != NULL) {
258 float amount;
259 char *cost;
260 avahi_string_list_get_pair (needle, NULL, &cost, NULL);
261 amount = atof(cost);
263 TRACE("tender", "%s will run %s for the cost of L$W %f\n", host_name, tender->name, amount);
264 if (amount < tender->cost) {
265 /* TODO: LOCK */
266 tender->cost = amount;
267 tender->dom = strdup(host_name);
268 TRACE("tender", "We will consider his offer!");
271 avahi_free(cost);
273 break;
280 static void tender_browse_callback(
281 AvahiServiceBrowser *b,
282 AvahiIfIndex interface,
283 AvahiProtocol protocol,
284 AvahiBrowserEvent event,
285 const char *name,
286 const char *type,
287 const char *domain,
288 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
289 void* userdata) {
291 assert(b);
292 AvahiClient *c = avahi_service_browser_get_client(b);
294 /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
296 switch (event) {
297 case AVAHI_BROWSER_FAILURE:
298 fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
299 return;
301 case AVAHI_BROWSER_NEW:
302 fprintf(stderr, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
304 /* We ignore the returned resolver object. In the callback
305 function we free it. If the server is terminated before
306 the callback function is called the server will free
307 the resolver for us. */
309 if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, tender_resolve_callback, userdata)))
310 fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(c)));
312 break;
314 case AVAHI_BROWSER_REMOVE:
315 fprintf(stderr, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
316 break;
318 case AVAHI_BROWSER_ALL_FOR_NOW:
319 case AVAHI_BROWSER_CACHE_EXHAUSTED:
320 fprintf(stderr, "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
321 break;
325 ret_t
326 cherokee_handler_virt_init (cherokee_handler_virt_t *hdl)
328 cherokee_connection_t *conn = HANDLER_CONN(hdl);
330 /* If someone is posting the server should be in
331 * read/write mode if this is not the case, just
332 * bail out directly */
334 if (conn->header.method == http_post && HDL_VIRT_PROPS(hdl)->read_only == TRUE) {
335 conn->error_code = http_unauthorized;
336 return ret_error;
339 int isroot = false;
340 int len;
341 char *this, *next;
343 cherokee_buffer_init(&hdl->user);
344 cherokee_buffer_init(&hdl->vm);
346 hdl->action = nothing;
348 cherokee_buffer_add (&conn->pathinfo,
349 conn->request.buf + conn->web_directory.len,
350 conn->request.len - conn->web_directory.len);
352 this = conn->pathinfo.buf + 1;
353 next = strchr(this, '/');
356 if ((!next && (this && (len = strlen(this)) == 0)) || (next && ((len = next - this) == 0)) )
357 hdl->action = showall;
358 else {
359 cherokee_buffer_add (&hdl->user, this, len);
362 if (HDL_VIRT_PROPS(hdl)->authenticate) {
363 if (!conn->validator ||
364 (conn->validator &&
365 (!cherokee_buffer_cmp_buf(&conn->validator->user, &hdl->user) &&
366 !(isroot = cherokee_buffer_cmp (&conn->validator->user, "root", 4))))) {
367 hdl->action = nothing; /* just in case */
368 conn->error_code = http_unauthorized;
369 return ret_error;
371 } else {
372 isroot = true;
375 if (hdl->action == showall) {
376 if (!isroot) {
377 hdl->action = nothing;
378 conn->error_code = http_unauthorized;
379 return ret_error;
380 } else {
381 return virt_build_page(hdl);
386 if (!next) {
387 hdl->action = showuservms;
388 return virt_build_page(hdl);
389 } else {
390 this = next + 1;
391 next = strchr(this, '/');
393 if ( ( !next && (this && (len = strlen(this)) == 0) ) || (next && ((len = next - this) == 0)) ) {
394 //if (!next && (this && (len = strlen(this)) == 0) || (next && ((len = next - this) == 0)) ) {
395 hdl->action = showuservms;
396 return virt_build_page(hdl);
400 cherokee_buffer_add (&hdl->vm, this, len);
402 if (!next) {
403 hdl->action = domainGetXMLDesc;
404 return virt_build_page(hdl);
405 } else {
406 this = next + 1;
407 next = strchr(this, '/');
409 if (( !next && (this && (len = strlen(this)) == 0) ) || (next && ((len = next - this) == 0)) ) {
410 hdl->action = domainGetXMLDesc;
411 return virt_build_page(hdl);
415 hdl->action = not_implemented;
416 switch (conn->header.method) {
417 case http_get:
418 if (strncmp(this, "virDomain", 9) == 0) {
419 if (strncmp(this+9, "Get", 3) == 0) {
420 if (strcmp(this+12, "ID") == 0) hdl->action = domainGetID;
421 else if (strcmp(this+12, "Name") == 0) hdl->action = domainGetName;
422 else if (strcmp(this+12, "MaxMemory") == 0) hdl->action = domainGetMaxMemory;
423 else if (strcmp(this+12, "MaxVcpus") == 0) hdl->action = domainGetMaxVcpus;
424 else if (strcmp(this+12, "OSType") == 0) hdl->action = domainGetOSType;
425 else if (strcmp(this+12, "UUID") == 0) hdl->action = domainGetUUID;
426 else if (strcmp(this+12, "UUIDString") == 0) hdl->action = domainGetUUIDString;
427 else if (strcmp(this+12, "XMLDesc") == 0) hdl->action = domainGetXMLDesc;
430 else if (HDL_VIRT_PROPS(hdl)->read_only == FALSE) {
431 if (strcmp(this+9, "Create") == 0) hdl->action = domainCreate;
432 else if (strcmp(this+9, "Destroy") == 0) hdl->action = domainDestroy;
433 else if (strcmp(this+9, "Reboot") == 0) hdl->action = domainReboot;
434 else if (strcmp(this+9, "Shutdown") == 0) hdl->action = domainShutdown;
436 else if (strcmp(this+9, "Save") == 0) hdl->action = domainSave;
437 else if (strcmp(this+9, "Restore") == 0) hdl->action = domainRestore;
439 else if (strcmp(this+9, "AttachDevice") == 0) hdl->action = domainAttachDevice_args;
441 else if (strcmp(this+9, "DefineXML") == 0) hdl->action = domainDefineXML_args;
442 else if (strcmp(this+9, "Undefine") == 0) hdl->action = domainUndefine;
445 else if (HDL_VIRT_PROPS(hdl)->read_only == FALSE && strncmp(this, "virStorage", 10) == 0) {
446 if (strncmp(this+10, "Vol", 3) == 0) {
447 if (strcmp(this+13, "CreateXML") == 0) hdl->action = storageVolCreateXML_args;
448 else if (strcmp(this+13, "Delete") == 0) hdl->action = storageVolDelete_args;
449 else if (strcmp(this+13, "CloneXML") == 0) hdl->action = storageVolCloneXML_args;
450 else if (strcmp(this+13, "CloneStatus") == 0) hdl->action = storageVolCloneStatus_args;
451 else if (strcmp(this+13, "GetXMLDesc") == 0) hdl->action = storageVolGetXMLDesc_args;
452 else if (strcmp(this+13, "SetPassword") == 0) hdl->action = storageVolSetPassword_args;
455 else if (strncmp(this, "virGraph", 8) == 0) {
456 if (strcmp(this+8, "Load") == 0) hdl->action = graphLoad_args;
457 if (strcmp(this+8, "Interface") == 0) hdl->action = graphInterface_args;
459 break;
461 case http_post: {
462 off_t postl;
463 cherokee_post_get_len (&conn->post, &postl);
465 if (postl <= 0 || postl >= (INT_MAX-1)) {
466 TRACE("virt", "post without post");
467 conn->error_code = http_bad_request;
468 return ret_error;
471 if (strncmp(this, "virDomain", 9) == 0) {
472 if (strcmp(this+9, "AttachDevice") == 0) hdl->action = domainAttachDevice;
473 else if (strcmp(this+9, "DetachDevice") == 0) hdl->action = domainDetachDevice;
474 else if (strcmp(this+9, "DefineXML") == 0) hdl->action = domainDefineXML;
476 else if (strncmp(this, "virStorage", 10) == 0) {
477 if (strncmp(this+10, "Vol", 3) == 0) {
478 if (strcmp(this+13, "CreateXML") == 0) hdl->action = storageVolCreateXML;
482 break;
485 default:
486 return ret_error;
489 if (hdl->action <= 0) {
490 TRACE("virt", "There was no action specified");
491 conn->error_code = http_bad_request;
492 return ret_error;
496 return virt_build_page(hdl);
499 ret_t
500 cherokee_handler_virt_free (cherokee_handler_virt_t *hdl)
502 cherokee_buffer_mrproper (&hdl->buffer);
503 cherokee_buffer_mrproper (&hdl->user);
504 cherokee_buffer_mrproper (&hdl->vm);
506 return ret_ok;
509 ret_t
510 cherokee_handler_virt_step (cherokee_handler_virt_t *hdl, cherokee_buffer_t *buffer)
512 cuint_t tosend;
514 if (cherokee_buffer_is_empty (&hdl->buffer))
515 return ret_eof;
517 tosend = (hdl->buffer.len > 1024 ? 1024 : hdl->buffer.len);
519 cherokee_buffer_add (buffer, hdl->buffer.buf, tosend);
520 cherokee_buffer_move_to_begin (&hdl->buffer, tosend);
522 if (cherokee_buffer_is_empty (&hdl->buffer))
523 return ret_eof_have_data;
525 return ret_ok;
528 ret_t
529 cherokee_handler_virt_add_headers (cherokee_handler_virt_t *hdl, cherokee_buffer_t *buffer)
531 cherokee_buffer_add_va (buffer, "Content-Length: %d"CRLF, hdl->buffer.len);
533 if (hdl->action > xml)
534 cherokee_buffer_add_str (buffer, "Content-Type: application/xml"CRLF);
535 else if (hdl->action > graph) {
536 cherokee_buffer_add_str (buffer, "Content-Type: image/png"CRLF);
537 } else
538 cherokee_buffer_add_str (buffer, "Content-Type: text/plain"CRLF);
540 return ret_ok;
544 static ret_t
545 while_func_entries (cherokee_buffer_t *key, void *value, void *param) {
546 virConnectPtr conn = NULL;
547 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
548 cherokee_buffer_t *buf = (cherokee_buffer_t *)param;
550 cherokee_buffer_add_va (&uri, "xen://%s/", value);
552 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE && !(conn = virConnectOpen (uri.buf))) {
553 if (!(conn = virConnectOpen (uri.buf))) {
554 return ret_error;
558 if (!conn && !(conn = virConnectOpenReadOnly (uri.buf))) {
559 return ret_error;
560 } else {
561 virDomainPtr dom;
562 if (!(dom = virDomainLookupByName(conn, (const char *) key->buf))) {
563 return ret_error;
564 } else {
565 char *xml = virDomainGetXMLDesc(dom, 0);
566 cherokee_buffer_add(buf, xml, strlen(xml));
568 virConnectClose(conn);
571 cherokee_buffer_mrproper(&uri);
573 return ret_ok;
576 static ret_t load_xml(cherokee_handler_virt_t *hdl, cherokee_buffer_t *xmlDesc) {
577 ret_t ret = ret_error;
578 FILE *fd;
579 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
580 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s", hdl->user.buf);
582 mkdir(path.buf, 0755);
583 cherokee_buffer_add_va (&path, "/%s", hdl->vm.buf);
585 mkdir(path.buf, 0755);
586 cherokee_buffer_add_str (&path, "/index.xml");
588 if ((fd = fopen(path.buf, "r")) != NULL) {
589 while (!feof(fd)) {
590 char buf[1024];
591 size_t amount = fread(buf, sizeof(char), 1024, fd);
593 cherokee_buffer_add (xmlDesc, buf, amount);
595 fclose(fd);
596 if (xmlDesc->len > 0)
597 ret = ret_ok;
600 cherokee_buffer_mrproper(&path);
601 return ret;
604 static ret_t save_xml(cherokee_handler_virt_t *hdl, virDomainPtr result, cherokee_buffer_t *buf) {
605 ret_t ret = ret_error;
606 FILE *fd;
607 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
608 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s", hdl->user.buf);
610 mkdir(path.buf, 0755);
611 cherokee_buffer_add_va (&path, "/%s", hdl->vm.buf);
613 mkdir(path.buf, 0755);
614 cherokee_buffer_add_str (&path, "/index.xml");
616 if ((fd = fopen(path.buf, "w")) == NULL) {
617 perror(__func__);
618 } else {
619 char *output = virDomainGetXMLDesc(result, VIR_DOMAIN_XML_INACTIVE);
620 fwrite(output, strlen(output), sizeof(char), fd);
621 fclose(fd);
622 if (buf)
623 cherokee_buffer_add(buf, output, strlen(output));
624 free(output);
625 ret = ret_ok;
627 cherokee_buffer_mrproper(&path);
628 return ret;
632 static ret_t
633 virt_virt_function(cherokee_handler_virt_t *hdl, virDomainPtr dom, virConnectPtr virConn) {
634 cherokee_connection_t *conn = HANDLER_CONN(hdl);
635 cherokee_buffer_t *buf = &HDL_AVAHI(hdl)->buffer;
637 switch (hdl->action) {
638 /* Returns the status of a clone copy */
639 case storageVolCloneStatus_args: {
640 ret_t ret;
641 void *name;
642 struct stat statbuf;
643 cherokee_buffer_t queue = CHEROKEE_BUF_INIT;
645 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
646 if (unlikely(ret < ret_ok)) {
647 TRACE("virt", "storageVolCloneXML_args; name argument not specified");
648 conn->error_code = http_bad_request;
649 return ret_error;
652 cherokee_buffer_add_va (&queue, "/mnt/images/queue/%s_%s.queued", hdl->user.buf, name);
654 if (stat(queue.buf, &statbuf) != 0) {
655 conn->error_code = http_not_found;
656 ret = ret_error;
657 } else {
658 char hardlink[1024];
659 int namelen;
660 if ((namelen = readlink(queue.buf, hardlink, 1023)) == -1) {
661 conn->error_code = http_internal_error;
662 ret = ret_error;
663 } else {
664 hardlink[namelen] = '\0';
665 if (stat(hardlink, &statbuf) != 0) {
666 conn->error_code = http_internal_error;
667 ret = ret_error;
668 } else {
669 struct stat statbuf2;
670 cherokee_buffer_t busy = CHEROKEE_BUF_INIT;
671 cherokee_buffer_add_va (&busy, "/mnt/images/queue/%s_%s.busy", hdl->user.buf, name);
672 if (stat(busy.buf, &statbuf2) != 0) {
673 conn->error_code = http_internal_error;
674 ret = ret_error;
675 } else {
676 cherokee_buffer_add_va (buf, "%f", (float)((float)statbuf2.st_size / (float)statbuf.st_size));
677 ret = ret_ok;
679 cherokee_buffer_mrproper(&busy);
684 cherokee_buffer_mrproper(&queue);
685 return ret;
686 break;
689 /* Save the memory of a domain to a file and suspend the domain */
690 case domainSave: {
691 ret_t ret = ret_ok;
692 int result;
693 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
694 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/memory", hdl->user.buf, hdl->vm.buf);
695 if ((result = virDomainSave(dom, path.buf)) != 0) {
696 TRACE("virt", "Saving of %s/%s failed", hdl->user.buf, hdl->vm.buf);
697 conn->error_code = http_internal_error;
698 ret = ret_error;
700 cherokee_buffer_mrproper(&path);
702 cherokee_buffer_add_long10(buf, result);
703 return ret;
704 break;
707 /* Restore the memory of a domain from a file and resume the domain */
708 case domainRestore: {
709 ret_t ret = ret_ok;
710 struct stat statbuf;
711 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
712 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/memory", hdl->user.buf, hdl->vm.buf);
714 if (stat(path.buf, &statbuf) == 0) {
715 int result;
716 if ((result = virDomainRestore(virConn, path.buf)) != 0) {
717 TRACE("virt", "Restoring of %s/%s failed", hdl->user.buf, hdl->vm.buf);
718 conn->error_code = http_internal_error;
719 ret = ret_error;
721 cherokee_buffer_add_long10(buf, result);
722 } else {
723 TRACE("virt", "Memory file for %s/%s does not exist", hdl->user.buf, hdl->vm.buf);
724 conn->error_code = http_not_found;
725 ret = ret_error;
728 cherokee_buffer_mrproper(&path);
729 return ret;
730 break;
733 case domainUndefine: {
734 int result;
735 if ((result = virDomainUndefine(dom)) != 0) {
736 conn->error_code = http_internal_error; /* TODO */
739 cherokee_buffer_add_long10(buf, result);
740 break;
743 case domainAttachDevice_args: {
744 int result;
745 void *temp;
746 ret_t ret;
748 ret = cherokee_avl_get_ptr (conn->arguments, "type", &temp);
750 if (ret == ret_ok) {
751 cherokee_buffer_t xml = CHEROKEE_BUF_INIT;
753 if (strcmp(temp, "disk") == 0) {
754 void *device;
755 if ((ret = cherokee_avl_get_ptr (conn->arguments, "device", &device)) == ret_ok) {
756 void *file;
757 if ((ret = cherokee_avl_get_ptr (conn->arguments, "file", &file)) == ret_ok) {
758 cherokee_buffer_add_va (&xml, VIRT_DISK_XML, file, device);
759 } else {
760 virStorageVolPtr volume = virt_get_vol_by_args(hdl, virConn, 1);
762 if (volume == NULL) {
763 return ret_error;
766 file = virStorageVolGetPath(volume);
767 cherokee_buffer_add_va (&xml, VIRT_DISK_XML, file, device);
768 free(file);
770 virStorageVolFree(volume);
771 ret = ret_ok;
776 else if (strcmp(temp, "interface") == 0) {
777 void *mac, *ip;
778 if ((ret = cherokee_avl_get_ptr (conn->arguments, "mac", &mac)) == ret_ok && (ret = cherokee_avl_get_ptr (conn->arguments, "ip", &ip)) == ret_ok) {
779 cherokee_buffer_add_va (&xml, VIRT_INTERFACE_XML, mac, ip);
780 } else {
781 char *mac = NULL;
782 char *ip = NULL;
783 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
784 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
785 if (getNewMac("dv28", domu.buf, &mac, &ip) == 0)
786 cherokee_buffer_add_va (&xml, VIRT_INTERFACE_XML, mac, ip);
787 cherokee_buffer_mrproper(&domu);
791 if (ret == ret_ok && (result = virDomainAttachDevice(dom, (const char *) xml.buf)) == 0) {
792 ret = ret_ok;
793 } else {
794 conn->error_code = http_internal_error;
795 return ret_error;
798 cherokee_buffer_add_long10(buf, result);
799 cherokee_buffer_mrproper(&xml);
800 } else {
801 TRACE("virt", "DeviceAttach_args; type was not specified");
802 conn->error_code = http_bad_request;
803 return ret_error;
806 break;
809 case domainAttachDevice: {
810 off_t postl;
811 int result;
812 cherokee_buffer_t post = CHEROKEE_BUF_INIT;
813 cherokee_post_get_len (&conn->post, &postl);
814 cherokee_post_walk_read (&conn->post, &post, (cuint_t) postl);
815 if ((result = virDomainAttachDevice(dom, (const char *) post.buf)) != 0)
816 conn->error_code = http_internal_error;
818 cherokee_buffer_mrproper(&post);
819 cherokee_buffer_add_long10(buf, result);
820 break;
824 case domainGetXMLDesc: {
825 char *xml = virDomainGetXMLDesc(dom, 0);
826 cherokee_buffer_add(buf, xml, strlen(xml));
827 free(xml);
828 break;
832 case domainDetachDevice: {
833 off_t postl;
834 int result;
835 ret_t ret;
836 cherokee_buffer_t post = CHEROKEE_BUF_INIT;
837 cherokee_post_get_len (&conn->post, &postl);
838 cherokee_post_walk_read (&conn->post, &post, (cuint_t) postl);
840 xmlDocPtr doc = xmlParseMemory((const char *) post.buf, post.len);
841 if (doc == NULL) {
842 TRACE("virt", "DeviceAttach; XML document unparceble");
843 conn->error_code = http_bad_request;
844 ret = ret_error;
845 } else
846 ret = ret_ok;
848 if (ret == ret_ok) {
849 if ((result = virDomainDetachDevice(dom, (const char *) post.buf)) != 0) {
850 conn->error_code = http_internal_error;
851 /* TODO: betere afhandeling */
853 xmlXPathContextPtr context = xmlXPathNewContext(doc);
854 if (context != NULL) {
855 xmlXPathObjectPtr obj = xmlXPathEval("string(//interface/mac/@address)", context);
856 xmlXPathFreeContext(context);
857 if ((obj != NULL) && (obj->type == XPATH_STRING) &&
858 (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
859 removeOldMac("dv28", (char *) obj->stringval);
861 xmlXPathFreeObject(obj);
864 xmlFreeDoc(doc);
865 xmlCleanupParser();
868 cherokee_buffer_mrproper(&post);
869 cherokee_buffer_add_long10(buf, result);
870 break;
873 case domainGetID: {
874 cherokee_buffer_add_ulong10(buf, virDomainGetID(dom));
875 break;
877 case domainGetMaxMemory: {
878 cherokee_buffer_add_ulong10(buf, virDomainGetMaxMemory (dom));
879 break;
881 case domainGetMaxVcpus: {
882 cherokee_buffer_add_long10(buf, virDomainGetMaxVcpus (dom));
883 break;
885 case domainGetName: {
886 const char *name = virDomainGetName (dom);
887 cherokee_buffer_add(buf, name, strlen(name));
888 break;
890 case domainGetUUID: {
891 unsigned char uuid[VIR_UUID_BUFLEN];
892 if (virDomainGetUUID(dom, uuid) == 0) {
893 cherokee_buffer_add_str(buf, uuid);
894 } else {
895 conn->error_code = http_internal_error;
896 return ret_error;
898 break;
900 case domainGetUUIDString: {
901 unsigned char uuid[VIR_UUID_STRING_BUFLEN];
902 if (virDomainGetUUIDString(dom, uuid) == 0) {
903 cherokee_buffer_add_str(buf, uuid);
904 } else {
905 conn->error_code = http_internal_error;
906 return ret_error;
908 break;
911 case domainCreate: {
912 cherokee_buffer_t xmlDesc = CHEROKEE_BUF_INIT;
913 char txt[254];
914 save_xml(hdl, dom, &xmlDesc);
915 tender_t best_tender;
916 snprintf(txt, 254, "memory=%lu", virDomainGetMaxMemory(dom));
917 txt[253] = '\0';
919 best_tender.name = virDomainGetName(dom);
920 best_tender.cost = FLT_MAX;
921 best_tender.dom = NULL;
923 AvahiServiceBrowser *sb = avahi_service_browser_new(HDL_AVAHI_PROPS(hdl)->client, AVAHI_IF_UNSPEC,
924 AVAHI_PROTO_UNSPEC, "_tender._tcp",
925 NULL, 0, tender_browse_callback, &best_tender);
927 if (sb == NULL) {
928 TRACE("avahi", "For whatever reason the sb is NULL\n");
929 } else {
931 AvahiEntryGroup *group = avahi_entry_group_new(HDL_AVAHI_PROPS(hdl)->client, entry_group_callback, NULL);
933 if (group == NULL) {
934 TRACE("avahi", "For whatever reason the group is NULL\n");
935 } else {
936 avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0,
937 best_tender.name, "_offer._tcp", NULL, NULL, 651, txt, NULL, NULL);
938 avahi_entry_group_commit(group);
940 sleep(3); /* we are for quick bidding ;) */
942 avahi_entry_group_free(group);
945 avahi_service_browser_free(sb);
947 if (best_tender.dom == NULL) {
948 TRACE("virt", "Nobody wants %s, poor vm!\n", best_tender.name);
949 cherokee_buffer_add_long10(buf, -1);
950 } else {
951 if (virDomainUndefine(dom) != 0) {
952 TRACE("virt", "Can't undefine %s, some idiot run it already?\n", best_tender.name);
953 cherokee_buffer_add_long10(buf, -1);
954 } else {
955 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
956 virConnectClose(virConn);
957 cherokee_buffer_add_va (&uri, "xen://%s/", best_tender.dom);
958 virConn = virConnectOpen (uri.buf);
959 if (virConn == NULL) {
960 TRACE("virt", "Can't connect to %s\n", uri.buf);
961 cherokee_buffer_add_long10(buf, -1);
962 } else {
963 cherokee_buffer_add_long10(buf,
964 (virDomainCreateLinux(virConn, xmlDesc.buf, 0) != NULL ?
965 0 : -1 ));
967 cherokee_buffer_mrproper(&uri);
969 TRACE("virt", "going to free\n");
970 free(best_tender.dom);
975 cherokee_buffer_mrproper(&xmlDesc);
976 break;
979 case domainDestroy: {
980 cherokee_buffer_add_long10(buf, virDomainDestroy (dom));
981 break;
984 case domainReboot: {
985 cherokee_buffer_add_long10(buf, virDomainReboot (dom, 0));
986 break;
989 case domainShutdown: {
990 cherokee_buffer_add_long10(buf, virDomainShutdown (dom));
991 break;
995 case domainGetOSType: {
996 char *ostype = virDomainGetOSType(dom);
997 cherokee_buffer_add(buf, ostype, strlen(ostype));
998 free(ostype);
999 break;
1006 if (hdl->action == domainUndefine) {
1007 /* Remove VM data from filesystem */
1008 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
1009 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/index.xml", hdl->user.buf, hdl->vm.buf);
1010 unlink(path.buf); /* TODO: instead of delet replace */
1011 cherokee_buffer_mrproper(&path);
1012 } else if (hdl->action != domainCreate) {
1013 save_xml(hdl, dom, NULL);
1016 return ret_ok;
1019 static virStoragePoolPtr
1020 virt_get_pool_by_args(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
1021 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1022 virStoragePoolPtr pool;
1023 void *temp;
1024 ret_t ret;
1026 ret = cherokee_avl_get_ptr (conn->arguments, "pool", &temp);
1027 if (unlikely(ret < ret_ok)) {
1028 TRACE("virt", "virStoragePoolPtr; Pool argument not specified");
1029 conn->error_code = http_bad_request;
1030 return NULL;
1033 pool = virStoragePoolLookupByName(virConn, temp);
1035 return pool;
1038 static virStorageVolPtr
1039 virt_get_vol_by_args(cherokee_handler_virt_t *hdl, virConnectPtr virConn, unsigned short int prefix) {
1040 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1041 virStoragePoolPtr pool;
1042 virStorageVolPtr volume;
1043 void *temp;
1044 ret_t ret;
1046 pool = virt_get_pool_by_args(hdl, virConn);
1048 if (pool == NULL) {
1049 conn->error_code = http_not_found;
1050 return NULL;
1053 virStoragePoolRefresh(pool, 0); /* TODO: might be better to do it outside */
1055 ret = cherokee_avl_get_ptr (conn->arguments, "volume", &temp);
1056 if (unlikely(ret < ret_ok)) {
1057 TRACE("virt", "virStorageVolPtr; Volume argument not specified");
1058 conn->error_code = http_bad_request;
1059 return NULL;
1062 if (prefix == 1) {
1063 cherokee_buffer_t fullvol = CHEROKEE_BUF_INIT;
1064 cherokee_buffer_add_va (&fullvol, "%s_%s", hdl->user.buf, temp);
1065 volume = virStorageVolLookupByName(pool, fullvol.buf);
1066 cherokee_buffer_mrproper(&fullvol);
1067 } else {
1068 volume = virStorageVolLookupByName(pool, temp);
1071 if (volume == NULL)
1072 conn->error_code = http_not_found;
1074 virStoragePoolFree(pool);
1076 return volume;
1079 /* This function is the home for all functions that need a working
1080 * pool/volume combination */
1081 static ret_t
1082 virt_pool_vol(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
1083 cherokee_buffer_t *buf = &HDL_AVAHI(hdl)->buffer;
1084 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1085 virStorageVolPtr volume;
1086 ret_t ret = ret_ok;
1088 /* We only allow clone to run 'unsafe', others get prefixed */
1089 volume = virt_get_vol_by_args(hdl, virConn, (hdl->action != storageVolCloneXML_args));
1091 /* If the volume doesn't exist, no point to continue */
1092 if (volume == NULL)
1093 return ret_error;
1095 switch (hdl->action) {
1096 /* Sets the password of a specific volume, requires the password= option */
1097 case storageVolSetPassword_args: {
1098 void *temp;
1099 ret = cherokee_avl_get_ptr (conn->arguments, "password", &temp);
1100 if (unlikely(ret < ret_ok)) {
1101 TRACE("virt", "storageVolSetPassword_args; password argument not specified");
1102 conn->error_code = http_bad_request;
1103 goto virt_pool_vol_cleanup;
1104 } else {
1105 cherokee_buffer_t cmd_passwd = CHEROKEE_BUF_INIT;
1106 cherokee_buffer_add_va (&cmd_passwd, "/usr/sbin/passwdchanger.sh %s %s\n", virStorageVolGetKey(volume), temp);
1107 cherokee_buffer_add_long10(buf, system(cmd_passwd.buf));
1108 cherokee_buffer_mrproper(&cmd_passwd);
1110 break;
1113 /* Removes a volume */
1114 case storageVolDelete_args: {
1115 cherokee_buffer_add_long10(buf, virStorageVolDelete(volume, 0));
1116 break;
1119 /* Gives a description of a storage volume in XML */
1120 case storageVolGetXMLDesc_args: {
1121 char *xml = virStorageVolGetXMLDesc(volume, 0);
1122 cherokee_buffer_add(buf, xml, strlen(xml));
1123 free(xml);
1124 break;
1127 /* Clones a volume, insecure method! requires a new name= */
1128 case storageVolCloneXML_args: {
1129 void *name;
1130 cherokee_buffer_t busy = CHEROKEE_BUF_INIT;
1132 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
1133 if (unlikely(ret < ret_ok)) {
1134 TRACE("virt", "storageVolCloneXML_args; name argument not specified");
1135 conn->error_code = http_bad_request;
1136 goto virt_pool_vol_cleanup;
1139 cherokee_buffer_add_va (&busy, "/mnt/images/queue/%s_%s.queued", hdl->user.buf, name);
1141 if (unlikely(symlink(virStorageVolGetKey(volume), busy.buf) == 1)) {
1142 conn->error_code = http_internal_error;
1143 ret = ret_error;
1144 } else {
1145 cherokee_buffer_add_str(buf, "QUEUED");
1148 cherokee_buffer_mrproper(&busy);
1150 break;
1155 virt_pool_vol_cleanup:
1156 /* And in the end we need to free the volume we have used */
1157 virStorageVolFree(volume);
1159 return ret;
1162 static ret_t
1163 virt_virt_new(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
1164 cherokee_buffer_t *buf = &HDL_AVAHI(hdl)->buffer;
1165 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1166 cherokee_buffer_t xml = CHEROKEE_BUF_INIT;
1167 virStoragePoolPtr pool = NULL;
1168 ret_t ret = ret_ok;
1170 switch (hdl->action) {
1172 case storageVolCreateXML_args: {
1173 void *temp, *type, *name, *unit;
1174 unsigned long int capacity, allocation;
1176 ret = cherokee_avl_get_ptr (conn->arguments, "pool", &temp);
1177 if (unlikely(ret < ret_ok)) {
1178 TRACE("virt", "storageVolCreateXML_args; pool argument not specified");
1179 conn->error_code = http_bad_request;
1180 goto virt_virt_new_cleanup;
1183 TRACE("args", "%s", temp);
1185 pool = virStoragePoolLookupByName(virConn, temp);
1187 if (pool == NULL) {
1188 conn->error_code = http_not_found;
1189 goto virt_virt_new_cleanup;
1192 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
1193 if (unlikely(ret < ret_ok)) {
1194 TRACE("virt", "storageVolCreateXML_args; name argument not specified");
1195 conn->error_code = http_bad_request;
1196 goto virt_virt_new_cleanup;
1199 ret = cherokee_avl_get_ptr (conn->arguments, "type", &type);
1200 if (unlikely(ret < ret_ok)) {
1201 TRACE("virt", "storageVolCreateXML_args; type argument not specified");
1202 conn->error_code = http_bad_request;
1203 goto virt_virt_new_cleanup;
1206 ret = cherokee_avl_get_ptr (conn->arguments, "unit", &unit);
1207 if (unlikely(ret < ret_ok)) {
1208 TRACE("virt", "storageVolCreateXML_args; unit argument not specified");
1209 conn->error_code = http_bad_request;
1210 goto virt_virt_new_cleanup;
1213 ret = cherokee_avl_get_ptr (conn->arguments, "allocation", &temp);
1214 if (unlikely(ret < ret_ok)) {
1215 TRACE("virt", "storageVolCreateXML_args; allocation argument not specified");
1216 conn->error_code = http_bad_request;
1217 goto virt_virt_new_cleanup;
1220 allocation = strtoul(temp, NULL, 10);
1221 if (errno == ERANGE) {
1222 TRACE("virt", "storageVolCreateXML_args; allocation is not a number");
1223 conn->error_code = http_bad_request;
1224 ret = ret_error;
1225 goto virt_virt_new_cleanup;
1228 ret = cherokee_avl_get_ptr (conn->arguments, "capacity", &temp);
1229 if (unlikely(ret < ret_ok)) {
1230 TRACE("virt", "storageVolCreateXML_args; capacity argument not specified");
1231 conn->error_code = http_bad_request;
1232 goto virt_virt_new_cleanup;
1235 capacity = strtoul(temp, NULL, 10);
1236 if (errno == ERANGE || capacity == 0) {
1237 TRACE("virt", "storageVolCreateXML_args; capacity is not a number");
1238 conn->error_code = http_bad_request;
1239 ret = ret_error;
1240 goto virt_virt_new_cleanup;
1243 cherokee_buffer_add_va (&xml, VIRT_STORAGE_XML, type, hdl->user.buf, name, allocation, unit, capacity, hdl->user.buf, name, hdl->user.buf, name);
1244 break;
1247 case domainDefineXML_args: {
1248 ret_t ret;
1249 unsigned int i;
1250 unsigned long vcpu = 0, interface = 0, memory = 0;
1252 void *temp = NULL;
1254 ret = cherokee_avl_get_ptr (conn->arguments, "vcpu", &temp);
1255 if (ret == ret_ok)
1256 vcpu = strtoul(temp, (char **) NULL, 10);
1258 if (ret != ret_ok || errno == ERANGE || vcpu == 0) {
1259 conn->error_code = http_internal_error;
1260 goto virt_virt_new_cleanup;
1264 ret = cherokee_avl_get_ptr (conn->arguments, "memory", &temp);
1265 if (ret == ret_ok)
1266 memory = strtoul(temp, (char **) NULL, 10);
1268 if (ret != ret_ok || errno == ERANGE || memory == 0) {
1269 conn->error_code = http_internal_error;
1270 goto virt_virt_new_cleanup;
1274 ret = cherokee_avl_get_ptr (conn->arguments, "interface", &temp);
1275 if (ret == ret_ok)
1276 interface = strtoul(temp, (char **) NULL, 10);
1278 if (ret != ret_ok || errno == ERANGE) {
1279 conn->error_code = http_internal_error;
1280 goto virt_virt_new_cleanup;
1283 cherokee_buffer_t cmdline_extra = CHEROKEE_BUF_INIT;
1284 cherokee_buffer_t xml_interfaces = CHEROKEE_BUF_INIT;
1285 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
1286 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
1288 for (i = 0; i < interface; i++) {
1289 char *mac = NULL;
1290 char *ip = NULL;
1291 if (getNewMac("dv28", domu.buf, &mac, &ip) == 0) {
1292 cherokee_buffer_add_va (&xml_interfaces, VIRT_INTERFACE_XML, mac, ip);
1293 if (i == 0) {
1294 /* TODO: terrible hack */
1295 char gateway[16];
1296 char *temp;
1297 strcpy(gateway, ip);
1298 temp = strchr(gateway, '.');
1299 temp = strchr(++temp, '.');
1300 temp = strchr(++temp, '.');
1301 strcpy(++temp, "254");
1302 cherokee_buffer_add_va (&cmdline_extra, VIRT_DOMAIN_CMD_IP, ip, gateway);
1307 cherokee_buffer_mrproper(&domu);
1309 cherokee_buffer_add_va (&xml, VIRT_DOMAIN_XML,
1310 hdl->user.buf, hdl->vm.buf, cmdline_extra.buf, memory, vcpu, xml_interfaces.buf);
1312 cherokee_buffer_mrproper(&xml_interfaces);
1313 cherokee_buffer_mrproper(&cmdline_extra);
1314 break;
1317 case storageVolCreateXML:
1318 case domainDefineXML: {
1319 off_t postl;
1320 cherokee_post_get_len (&conn->post, &postl);
1321 cherokee_post_walk_read (&conn->post, &xml, (cuint_t) postl);
1322 break;
1326 switch (hdl->action) {
1327 case domainDefineXML_args:
1328 case domainDefineXML: {
1329 virDomainPtr result = virDomainDefineXML(virConn, (const char *) xml.buf);
1331 if (result == NULL) {
1332 /* TODO: vrij maken eventuele uitgegeven macs! */
1333 conn->error_code = http_internal_error;
1334 goto virt_virt_new_cleanup;
1337 save_xml(hdl, result, buf);
1339 virDomainFree(result);
1341 break;
1344 case storageVolCreateXML_args:
1345 case storageVolCreateXML: {
1346 virStorageVolPtr vol = virStorageVolCreateXML(pool, xml.buf, 0);
1348 if (vol == NULL) {
1349 cherokee_buffer_add_long10(buf, -1);
1350 goto virt_virt_new_cleanup;
1353 virStorageVolFree(vol);
1355 cherokee_buffer_add_long10(buf, 0);
1356 break;
1360 virt_virt_new_cleanup:
1361 cherokee_buffer_mrproper(&xml);
1363 if (pool)
1364 virStoragePoolFree(pool);
1366 return ret;
1370 static ret_t
1371 virt_virt_do(cherokee_handler_virt_t *hdl, cherokee_buffer_t *domu, cherokee_buffer_t *uri)
1373 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1375 ret_t ret = ret_error;
1376 virConnectPtr virConn = NULL;
1379 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE)
1380 virConn = virConnectOpen (uri->buf);
1382 if (!virConn && !(virConn = virConnectOpenReadOnly (uri->buf))) {
1383 conn->error_code = http_service_unavailable;
1384 return ret_error;
1387 switch (hdl->action) {
1388 case storageVolDelete_args:
1389 case storageVolSetPassword_args:
1390 case storageVolGetXMLDesc_args:
1391 case storageVolCloneXML_args:
1392 ret = virt_pool_vol(hdl, virConn);
1393 break;
1395 case storageVolCreateXML_args:
1396 case storageVolCreateXML:
1397 case domainDefineXML_args:
1398 case domainDefineXML:
1399 ret = virt_virt_new(hdl, virConn);
1400 break;
1402 default: {
1403 virDomainPtr dom;
1404 if ((dom = virDomainLookupByName(virConn, domu->buf)) == NULL) {
1405 conn->error_code = http_not_found;
1406 } else {
1407 ret = virt_virt_function(hdl, dom, virConn);
1408 virDomainFree(dom);
1413 virConnectClose(virConn);
1414 return ret;
1417 static ret_t
1418 virt_while (cherokee_buffer_t *key, void *value, void *param) {
1419 cherokee_handler_virt_t * hdl = param;
1420 // cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
1421 // cherokee_buffer_add_va (&uri, "xen://%s/", ((cherokee_buffer_t *) value)->buf);
1422 // virt_virt_do((cherokee_handler_virt_t *) param, key, &uri);
1423 // cherokee_buffer_mrproper(&uri);
1425 cherokee_buffer_add_va (&hdl->buffer, "<domain><name>%s</name></domain>", key->buf);
1427 return ret_ok;
1430 static ret_t
1431 virt_while_user (cherokee_buffer_t *key, void *value, void *param) {
1432 cherokee_handler_virt_t *hdl = param;
1433 if (key->len > hdl->user.len && key->buf[hdl->user.len] == '_' && strncmp(key->buf, hdl->user.buf, hdl->user.len) == 0)
1434 return virt_while (key, value, param);
1436 return ret_ok;
1439 static ret_t
1440 virt_build_page (cherokee_handler_virt_t *hdl)
1442 ret_t ret;
1443 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1444 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
1445 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
1447 /* We use the webserver methods to parse the querystring */
1448 /* Maybe do something smart with ENUM, begin_args, end_args... */
1449 if ((hdl->action == domainDefineXML_args) ||
1450 (hdl->action == domainAttachDevice_args) ||
1451 (hdl->action == storageVolGetXMLDesc_args) ||
1452 (hdl->action == storageVolDelete_args) ||
1453 (hdl->action == storageVolSetPassword_args) ||
1454 (hdl->action == storageVolCloneXML_args) ||
1455 (hdl->action == storageVolCloneStatus_args) ||
1456 (hdl->action == storageVolCreateXML_args) ||
1457 (hdl->action == graphLoad_args) ||
1458 (hdl->action == graphInterface_args)) {
1459 ret = cherokee_connection_parse_args (conn);
1460 if (unlikely(ret < ret_ok)) {
1461 conn->error_code = http_internal_error;
1462 return ret_error;
1466 switch (hdl->action) {
1467 case graphInterface_args: {
1468 struct stat statbuf;
1469 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
1470 void *interface;
1471 if ((ret = cherokee_avl_get_ptr (conn->arguments, "interface", &interface)) != ret_ok)
1472 interface = "eth0";
1474 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/interface_%s.rrd", hdl->user.buf, hdl->vm.buf, interface);
1475 if (stat(path.buf, &statbuf) != 0) {
1476 conn->error_code = http_not_found;
1477 ret = ret_error;
1478 } else {
1479 void *width, *height, *start, *end;
1480 if ((ret = cherokee_avl_get_ptr (conn->arguments, "width", &width)) != ret_ok)
1481 width = "600";
1483 if ((ret = cherokee_avl_get_ptr (conn->arguments, "height", &height)) != ret_ok)
1484 height = "200";
1486 if ((ret = cherokee_avl_get_ptr (conn->arguments, "start", &start)) != ret_ok)
1487 start = "now-1h";
1489 if ((ret = cherokee_avl_get_ptr (conn->arguments, "end", &end)) != ret_ok)
1490 end = "now";
1492 cherokee_buffer_t def1 = CHEROKEE_BUF_INIT;
1493 cherokee_buffer_t def2 = CHEROKEE_BUF_INIT;
1494 cherokee_buffer_add_va (&def1, "DEF:rxbytes=%s:rxbytes:AVERAGE:step=30", path.buf);
1495 cherokee_buffer_add_va (&def2, "DEF:txbytes=%s:txbytes:AVERAGE:step=30", path.buf);
1496 char **calcpr = NULL;
1497 int xsize, ysize;
1498 double ymin, ymax;
1499 char *filename = mktemp(strdup("handler_virt_XXXXXX"));
1500 char *r_graph[] = { "rrdgraph", filename,
1501 "-a", "PNG",
1502 "-w", width,
1503 "-h", height,
1504 "--start", start,
1505 "--end", end,
1506 "--title", interface,
1507 "--lower-limit", "0", "--alt-autoscale-max", "--vertical-label", "bits per second",
1508 def1.buf,
1509 def2.buf,
1510 "CDEF:txbits=txbytes,8,*",
1511 "CDEF:rxbits=rxbytes,8,*",
1512 "AREA:rxbits#00EE00:rxbits",
1513 "LINE:txbits#0000EE:txbits" };
1515 rrd_graph(25, r_graph, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
1516 if ((ret = cherokee_buffer_read_file(&hdl->buffer, filename)) == ret_error) {
1517 hdl->action = http_internal_error;
1520 unlink(filename);
1521 free(filename);
1523 cherokee_buffer_mrproper(&def1);
1524 cherokee_buffer_mrproper(&def2);
1526 cherokee_buffer_mrproper(&path);
1527 goto virt_build_page_cleanup;
1532 case graphLoad_args: {
1533 struct stat statbuf;
1534 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
1535 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/cpuTime.rrd", hdl->user.buf, hdl->vm.buf);
1536 if (stat(path.buf, &statbuf) != 0) {
1537 conn->error_code = http_not_found;
1538 ret = ret_error;
1539 } else {
1540 void *width, *height, *start, *end;
1541 if ((ret = cherokee_avl_get_ptr (conn->arguments, "width", &width)) != ret_ok)
1542 width = "600";
1544 if ((ret = cherokee_avl_get_ptr (conn->arguments, "height", &height)) != ret_ok)
1545 height = "200";
1547 if ((ret = cherokee_avl_get_ptr (conn->arguments, "start", &start)) != ret_ok)
1548 start = "now-1h";
1550 if ((ret = cherokee_avl_get_ptr (conn->arguments, "end", &end)) != ret_ok)
1551 end = "now";
1553 /* TODO: wat error checking? */
1555 cherokee_buffer_t def = CHEROKEE_BUF_INIT;
1556 cherokee_buffer_add_va (&def, "DEF:cputime=%s:cpuTime:AVERAGE:step=30", path.buf);
1557 char **calcpr = NULL;
1558 int xsize, ysize;
1559 double ymin, ymax;
1560 char *filename = mktemp(strdup("handler_virt_XXXXXX"));
1561 char *r_graph[] = { "rrdgraph",
1562 filename,
1563 "-a", "PNG",
1564 "-w", width,
1565 "-h", height,
1566 "--start", start,
1567 "--end", end,
1568 "--title", hdl->vm.buf,
1569 "--lower-limit", "0", "--alt-autoscale-max", "--vertical-label", "(used / total) time",
1570 def.buf,
1571 "CDEF:cpuload=cputime,1000000000,/",
1572 "LINE:cpuload#EE0000:cpuLoad" };
1574 rrd_graph(22, r_graph, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
1575 if ((ret = cherokee_buffer_read_file(&hdl->buffer, filename)) == ret_error) {
1576 hdl->action = http_internal_error;
1579 unlink(filename);
1580 free(filename);
1582 cherokee_buffer_mrproper(&def);
1584 cherokee_buffer_mrproper(&path);
1585 goto virt_build_page_cleanup;
1589 if (hdl->action > xml && HDL_VIRT_PROPS(hdl)->xsl.len > 0)
1590 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);
1593 switch (hdl->action) {
1594 case showall:
1595 cherokee_buffer_add_str (&hdl->buffer, "<domains>");
1596 avahi_threaded_poll_lock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
1597 cherokee_avl_while(&HDL_AVAHI_PROPS(hdl)->entries, virt_while, hdl, NULL, NULL);
1598 avahi_threaded_poll_unlock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
1599 cherokee_buffer_add_str (&hdl->buffer, "</domains>");
1600 ret = ret_ok;
1601 break;
1603 case showuservms: {
1604 cherokee_buffer_add_str (&hdl->buffer, "<domains>");
1605 avahi_threaded_poll_lock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
1606 cherokee_avl_while(&HDL_AVAHI_PROPS(hdl)->entries, virt_while_user, hdl, NULL, NULL);
1607 avahi_threaded_poll_unlock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
1608 cherokee_buffer_add_str (&hdl->buffer, "</domains>");
1609 ret = ret_ok;
1610 break;
1613 default: {
1614 cherokee_buffer_t *hostname = NULL;
1615 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
1617 avahi_threaded_poll_lock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
1618 ret = cherokee_avl_get_ptr(&HDL_AVAHI_PROPS(hdl)->entries, domu.buf, (void **) &hostname);
1619 avahi_threaded_poll_unlock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
1621 if (ret == ret_not_found) {
1622 virDomainPtr virDom;
1623 virConnectPtr virConn;
1624 cherokee_buffer_add_va (&uri, "xen://%s/", HDL_VIRT_PROPS(hdl)->virt); // TODO: change!
1626 /* If we have the read only parameter, we will set up a connection to the
1627 * Hypervisor here. */
1628 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE)
1629 virConn = virConnectOpen (uri.buf);
1631 /* We should already have a connection (read only) or build a new connection
1632 * if this doesn't work, we can safely assume our services is fubar */
1633 if (!virConn && !(virConn = virConnectOpenReadOnly (uri.buf))) {
1634 conn->error_code = http_service_unavailable;
1635 return ret_error;
1638 /* We lookup if there is a domain somewhere with this name */
1639 if ((virDom = virDomainLookupByName(virConn, domu.buf)) == NULL) {
1640 /* If the domain is not found on the network the only possible
1641 * command that is possible would be to Define it */
1642 if (hdl->action == domainDefineXML_args || hdl->action == domainDefineXML) {
1643 ret = ret_ok;
1644 } else {
1645 /* We should also look on disk for defined stuff */
1646 cherokee_buffer_t xmlDesc = CHEROKEE_BUF_INIT;
1647 if (load_xml(hdl, &xmlDesc) == ret_ok && (virDom = virDomainDefineXML(virConn, xmlDesc.buf)) != NULL) {
1648 /* The domain existed and is loaded! */
1649 ret = ret_ok;
1650 } else {
1651 /* Otherwise we don't have anything to do and we should
1652 * return an error */
1653 hdl->action = nothing;
1654 conn->error_code = http_not_found;
1655 ret = ret_error;
1658 cherokee_buffer_mrproper(&xmlDesc);
1660 } else {
1661 /* We don't want te recreate things that already found on the network
1662 * first undefine them! */
1663 if (hdl->action == domainDefineXML_args || hdl->action == domainDefineXML) {
1664 TRACE("virt", "domainDefineXML_args/domainDefineXML; domain already exists");
1665 hdl->action = nothing;
1666 conn->error_code = http_bad_request;
1667 ret = ret_error;
1668 } else {
1669 /* Everything is ok, because nothing is found. */
1670 ret = ret_ok;
1673 /* Domain wasn't NULL, so we should free it here */
1674 virDomainFree(virDom);
1676 virConnectClose (virConn);
1677 } else if (ret == ret_ok) {
1678 cherokee_buffer_add_va (&uri, "xen://%s/", hostname->buf);
1679 printf("%s\n", uri.buf);
1680 } else {
1681 hdl->action = http_internal_error;
1682 hdl->action = nothing;
1683 ret = ret_error;
1689 if (ret == ret_ok && hdl->action != showall && hdl->action != showuservms) {
1690 ret = virt_virt_do(hdl, &domu, &uri);
1693 virt_build_page_cleanup:
1694 cherokee_buffer_mrproper(&domu);
1695 cherokee_buffer_mrproper(&uri);
1697 return ret;
1701 ret_t
1702 cherokee_handler_virt_new (cherokee_handler_t **hdl, cherokee_connection_t *cnt, cherokee_module_props_t *props)
1704 ret_t ret;
1705 CHEROKEE_NEW_STRUCT (n, handler_virt);
1707 /* Init the base class
1710 cherokee_handler_init_base(HANDLER(n), cnt, HANDLER_PROPS(props), PLUGIN_INFO_HANDLER_PTR(virt));
1712 MODULE(n)->init = (handler_func_init_t) cherokee_handler_virt_init;
1713 MODULE(n)->free = (module_func_free_t) cherokee_handler_virt_free;
1714 HANDLER(n)->step = (handler_func_step_t) cherokee_handler_virt_step;
1715 HANDLER(n)->add_headers = (handler_func_add_headers_t) cherokee_handler_virt_add_headers;
1717 HANDLER(n)->support = hsupport_length | hsupport_range;
1719 ret = cherokee_buffer_init (&n->buffer);
1720 if (unlikely(ret != ret_ok))
1721 return ret;
1723 ret = cherokee_buffer_ensure_size (&n->buffer, 4*1024);
1724 if (unlikely(ret != ret_ok))
1725 return ret;
1727 *hdl = HANDLER(n);
1729 return ret_ok;