5da1a230f73e9660a4aa5fffdf12452bf3604487
[handlervirt.git] / handler_virt.c
blob5da1a230f73e9660a4aa5fffdf12452bf3604487
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 fprintf(stderr, "(Resolver) FAILURE!\n");
222 avahi_service_resolver_free(r);
223 break;
225 case AVAHI_RESOLVER_FOUND: {
226 char a[AVAHI_ADDRESS_STR_MAX], *t;
227 AvahiStringList *needle;
229 fprintf(stderr, "(Resolver) Service '%s' of type '%s' in domain '%s':\n", name, type, domain);
231 avahi_address_snprint(a, sizeof(a), address);
232 t = avahi_string_list_to_string(txt);
233 fprintf(stderr,
234 "\t%s:%u (%s)\n"
235 "\tTXT=%s\n"
236 "\tcookie is %u\n"
237 "\tis_local: %i\n"
238 "\tour_own: %i\n"
239 "\twide_area: %i\n"
240 "\tmulticast: %i\n"
241 "\tcached: %i\n",
242 host_name, port, a,
244 avahi_string_list_get_service_cookie(txt),
245 !!(flags & AVAHI_LOOKUP_RESULT_LOCAL),
246 !!(flags & AVAHI_LOOKUP_RESULT_OUR_OWN),
247 !!(flags & AVAHI_LOOKUP_RESULT_WIDE_AREA),
248 !!(flags & AVAHI_LOOKUP_RESULT_MULTICAST),
249 !!(flags & AVAHI_LOOKUP_RESULT_CACHED));
252 avahi_free(t);
254 unsigned int len = strlen(tender->name);
256 if (strlen(name) > len && name[len] == '.' && strncmp(tender->name, name, len) == 0) {
257 if ((needle = avahi_string_list_find (txt, "cost")) != NULL) {
259 float amount;
260 char *cost;
261 avahi_string_list_get_pair (needle, NULL, &cost, NULL);
262 amount = atof(cost);
264 TRACE("tender", "%s will run %s for the cost of L$W %f\n", host_name, tender->name, amount);
265 if (amount < tender->cost) {
266 /* TODO: LOCK */
267 tender->cost = amount;
268 tender->dom = strdup(host_name);
269 TRACE("tender", "We will consider his offer!");
272 avahi_free(cost);
274 break;
281 static void tender_browse_callback(
282 AvahiServiceBrowser *b,
283 AvahiIfIndex interface,
284 AvahiProtocol protocol,
285 AvahiBrowserEvent event,
286 const char *name,
287 const char *type,
288 const char *domain,
289 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
290 void* userdata) {
292 assert(b);
293 AvahiClient *c = avahi_service_browser_get_client(b);
295 /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
297 switch (event) {
298 case AVAHI_BROWSER_FAILURE:
299 fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
300 return;
302 case AVAHI_BROWSER_NEW:
303 fprintf(stderr, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
305 /* We ignore the returned resolver object. In the callback
306 function we free it. If the server is terminated before
307 the callback function is called the server will free
308 the resolver for us. */
310 if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, tender_resolve_callback, userdata)))
311 fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(c)));
313 break;
315 case AVAHI_BROWSER_REMOVE:
316 fprintf(stderr, "%s, (Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", __func__, name, type, domain);
317 break;
319 case AVAHI_BROWSER_ALL_FOR_NOW:
320 case AVAHI_BROWSER_CACHE_EXHAUSTED:
321 fprintf(stderr, "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
322 break;
326 ret_t
327 cherokee_handler_virt_init (cherokee_handler_virt_t *hdl)
329 cherokee_connection_t *conn = HANDLER_CONN(hdl);
331 /* If someone is posting the server should be in
332 * read/write mode if this is not the case, just
333 * bail out directly */
335 if (conn->header.method == http_post && HDL_VIRT_PROPS(hdl)->read_only == TRUE) {
336 conn->error_code = http_unauthorized;
337 return ret_error;
340 int isroot = false;
341 int len;
342 char *this, *next;
344 cherokee_buffer_init(&hdl->user);
345 cherokee_buffer_init(&hdl->vm);
347 hdl->action = nothing;
349 cherokee_buffer_add (&conn->pathinfo,
350 conn->request.buf + conn->web_directory.len,
351 conn->request.len - conn->web_directory.len);
353 this = conn->pathinfo.buf + 1;
355 next = strchr(this, '/'); /* TODO: this code borks! */
357 if ((!next && (this && (len = strlen(this)) == 0)) || (next && ((len = next - this) == 0)) )
358 hdl->action = showall;
359 else {
360 cherokee_buffer_add (&hdl->user, this, len);
363 if (HDL_VIRT_PROPS(hdl)->authenticate) {
364 if (!conn->validator ||
365 (conn->validator &&
366 (!cherokee_buffer_cmp_buf(&conn->validator->user, &hdl->user) &&
367 !(isroot = cherokee_buffer_cmp (&conn->validator->user, "root", 4))))) {
368 hdl->action = nothing; /* just in case */
369 conn->error_code = http_unauthorized;
370 return ret_error;
372 } else {
373 isroot = true;
376 if (hdl->action == showall) {
377 if (!isroot) {
378 hdl->action = nothing;
379 conn->error_code = http_unauthorized;
380 return ret_error;
381 } else {
382 return virt_build_page(hdl);
387 if (!next) {
388 hdl->action = showuservms;
389 return virt_build_page(hdl);
390 } else {
391 this = next + 1;
392 next = strchr(this, '/');
394 if ( ( !next && (this && (len = strlen(this)) == 0) ) || (next && ((len = next - this) == 0)) ) {
395 //if (!next && (this && (len = strlen(this)) == 0) || (next && ((len = next - this) == 0)) ) {
396 hdl->action = showuservms;
397 return virt_build_page(hdl);
401 cherokee_buffer_add (&hdl->vm, this, len);
403 if (!next) {
404 hdl->action = domainGetXMLDesc;
405 return virt_build_page(hdl);
406 } else {
407 this = next + 1;
408 next = strchr(this, '/');
410 if (( !next && (this && (len = strlen(this)) == 0) ) || (next && ((len = next - this) == 0)) ) {
411 hdl->action = domainGetXMLDesc;
412 return virt_build_page(hdl);
416 hdl->action = not_implemented;
417 switch (conn->header.method) {
418 case http_get:
419 if (strncmp(this, "virDomain", 9) == 0) {
420 if (strncmp(this+9, "Get", 3) == 0) {
421 if (strcmp(this+12, "ID") == 0) hdl->action = domainGetID;
422 else if (strcmp(this+12, "Name") == 0) hdl->action = domainGetName;
423 else if (strcmp(this+12, "MaxMemory") == 0) hdl->action = domainGetMaxMemory;
424 else if (strcmp(this+12, "MaxVcpus") == 0) hdl->action = domainGetMaxVcpus;
425 else if (strcmp(this+12, "OSType") == 0) hdl->action = domainGetOSType;
426 else if (strcmp(this+12, "UUID") == 0) hdl->action = domainGetUUID;
427 else if (strcmp(this+12, "UUIDString") == 0) hdl->action = domainGetUUIDString;
428 else if (strcmp(this+12, "XMLDesc") == 0) hdl->action = domainGetXMLDesc;
431 else if (HDL_VIRT_PROPS(hdl)->read_only == FALSE) {
432 if (strcmp(this+9, "Create") == 0) hdl->action = domainCreate;
433 else if (strcmp(this+9, "Destroy") == 0) hdl->action = domainDestroy;
434 else if (strcmp(this+9, "Reboot") == 0) hdl->action = domainReboot;
435 else if (strcmp(this+9, "Shutdown") == 0) hdl->action = domainShutdown;
437 else if (strcmp(this+9, "Save") == 0) hdl->action = domainSave;
438 else if (strcmp(this+9, "Restore") == 0) hdl->action = domainRestore;
440 else if (strcmp(this+9, "AttachDevice") == 0) hdl->action = domainAttachDevice_args;
442 else if (strcmp(this+9, "DefineXML") == 0) hdl->action = domainDefineXML_args;
443 else if (strcmp(this+9, "Undefine") == 0) hdl->action = domainUndefine;
446 else if (HDL_VIRT_PROPS(hdl)->read_only == FALSE && strncmp(this, "virStorage", 10) == 0) {
447 if (strncmp(this+10, "Vol", 3) == 0) {
448 if (strcmp(this+13, "CreateXML") == 0) hdl->action = storageVolCreateXML_args;
449 else if (strcmp(this+13, "Delete") == 0) hdl->action = storageVolDelete_args;
450 else if (strcmp(this+13, "CloneXML") == 0) hdl->action = storageVolCloneXML_args;
451 else if (strcmp(this+13, "CloneStatus") == 0) hdl->action = storageVolCloneStatus_args;
452 else if (strcmp(this+13, "GetXMLDesc") == 0) hdl->action = storageVolGetXMLDesc_args;
453 else if (strcmp(this+13, "SetPassword") == 0) hdl->action = storageVolSetPassword_args;
456 else if (strncmp(this, "virGraph", 8) == 0) {
457 if (strcmp(this+8, "Load") == 0) hdl->action = graphLoad_args;
458 if (strcmp(this+8, "Interface") == 0) hdl->action = graphInterface_args;
460 break;
462 case http_post: {
463 off_t postl;
464 cherokee_post_get_len (&conn->post, &postl);
466 if (postl <= 0 || postl >= (INT_MAX-1)) {
467 TRACE("virt", "post without post");
468 conn->error_code = http_bad_request;
469 return ret_error;
472 if (strncmp(this, "virDomain", 9) == 0) {
473 if (strcmp(this+9, "AttachDevice") == 0) hdl->action = domainAttachDevice;
474 else if (strcmp(this+9, "DetachDevice") == 0) hdl->action = domainDetachDevice;
475 else if (strcmp(this+9, "DefineXML") == 0) hdl->action = domainDefineXML;
477 else if (strncmp(this, "virStorage", 10) == 0) {
478 if (strncmp(this+10, "Vol", 3) == 0) {
479 if (strcmp(this+13, "CreateXML") == 0) hdl->action = storageVolCreateXML;
483 break;
486 default:
487 return ret_error;
490 if (hdl->action <= 0) {
491 TRACE("virt", "There was no action specified");
492 conn->error_code = http_bad_request;
493 return ret_error;
497 return virt_build_page(hdl);
500 ret_t
501 cherokee_handler_virt_free (cherokee_handler_virt_t *hdl)
503 cherokee_buffer_mrproper (&hdl->buffer);
504 cherokee_buffer_mrproper (&hdl->user);
505 cherokee_buffer_mrproper (&hdl->vm);
507 return ret_ok;
510 ret_t
511 cherokee_handler_virt_step (cherokee_handler_virt_t *hdl, cherokee_buffer_t *buffer)
513 cuint_t tosend;
515 if (cherokee_buffer_is_empty (&hdl->buffer))
516 return ret_eof;
518 tosend = (hdl->buffer.len > 1024 ? 1024 : hdl->buffer.len);
520 cherokee_buffer_add (buffer, hdl->buffer.buf, tosend);
521 cherokee_buffer_move_to_begin (&hdl->buffer, tosend);
523 if (cherokee_buffer_is_empty (&hdl->buffer))
524 return ret_eof_have_data;
526 return ret_ok;
529 ret_t
530 cherokee_handler_virt_add_headers (cherokee_handler_virt_t *hdl, cherokee_buffer_t *buffer)
532 cherokee_buffer_add_va (buffer, "Content-Length: %d"CRLF, hdl->buffer.len);
534 if (hdl->action > xml)
535 cherokee_buffer_add_str (buffer, "Content-Type: application/xml"CRLF);
536 else if (hdl->action > graph) {
537 cherokee_buffer_add_str (buffer, "Content-Type: image/png"CRLF);
538 } else
539 cherokee_buffer_add_str (buffer, "Content-Type: text/plain"CRLF);
541 return ret_ok;
545 static ret_t
546 while_func_entries (cherokee_buffer_t *key, void *value, void *param) {
547 virConnectPtr conn = NULL;
548 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
549 cherokee_buffer_t *buf = (cherokee_buffer_t *)param;
551 cherokee_buffer_add_va (&uri, "xen://%s/", value);
553 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE && !(conn = virConnectOpen (uri.buf))) {
554 if (!(conn = virConnectOpen (uri.buf))) {
555 return ret_error;
559 if (!conn && !(conn = virConnectOpenReadOnly (uri.buf))) {
560 return ret_error;
561 } else {
562 virDomainPtr dom;
563 if (!(dom = virDomainLookupByName(conn, (const char *) key->buf))) {
564 return ret_error;
565 } else {
566 char *xml = virDomainGetXMLDesc(dom, 0);
567 cherokee_buffer_add(buf, xml, strlen(xml));
569 virConnectClose(conn);
572 cherokee_buffer_mrproper(&uri);
574 return ret_ok;
577 static ret_t load_xml(cherokee_handler_virt_t *hdl, cherokee_buffer_t *xmlDesc) {
578 ret_t ret = ret_error;
579 FILE *fd;
580 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
581 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s", hdl->user.buf);
583 mkdir(path.buf, 0755);
584 cherokee_buffer_add_va (&path, "/%s", hdl->vm.buf);
586 mkdir(path.buf, 0755);
587 cherokee_buffer_add_str (&path, "/index.xml");
589 if ((fd = fopen(path.buf, "r")) != NULL) {
590 while (!feof(fd)) {
591 char buf[1024];
592 size_t amount = fread(buf, sizeof(char), 1024, fd);
594 cherokee_buffer_add (xmlDesc, buf, amount);
596 fclose(fd);
597 if (xmlDesc->len > 0)
598 ret = ret_ok;
601 cherokee_buffer_mrproper(&path);
602 return ret;
605 static ret_t save_xml(cherokee_handler_virt_t *hdl, virDomainPtr result, cherokee_buffer_t *buf) {
606 ret_t ret = ret_error;
607 FILE *fd;
608 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
609 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s", hdl->user.buf);
611 mkdir(path.buf, 0755);
612 cherokee_buffer_add_va (&path, "/%s", hdl->vm.buf);
614 mkdir(path.buf, 0755);
615 cherokee_buffer_add_str (&path, "/index.xml");
617 if ((fd = fopen(path.buf, "w")) == NULL) {
618 perror(__func__);
619 } else {
620 char *output = virDomainGetXMLDesc(result, VIR_DOMAIN_XML_INACTIVE);
621 fwrite(output, strlen(output), sizeof(char), fd);
622 fclose(fd);
623 if (buf)
624 cherokee_buffer_add(buf, output, strlen(output));
625 free(output);
626 ret = ret_ok;
628 cherokee_buffer_mrproper(&path);
629 return ret;
633 static ret_t
634 virt_virt_function(cherokee_handler_virt_t *hdl, virDomainPtr dom, virConnectPtr virConn) {
635 cherokee_connection_t *conn = HANDLER_CONN(hdl);
636 cherokee_buffer_t *buf = &HDL_AVAHI(hdl)->buffer;
638 switch (hdl->action) {
639 /* Returns the status of a clone copy */
640 case storageVolCloneStatus_args: {
641 ret_t ret;
642 void *name;
643 struct stat statbuf;
644 cherokee_buffer_t queue = CHEROKEE_BUF_INIT;
646 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
647 if (unlikely(ret < ret_ok)) {
648 TRACE("virt", "storageVolCloneXML_args; name argument not specified");
649 conn->error_code = http_bad_request;
650 return ret_error;
653 cherokee_buffer_add_va (&queue, "/mnt/images/queue/%s_%s.queued", hdl->user.buf, name);
655 if (stat(queue.buf, &statbuf) != 0) {
656 conn->error_code = http_not_found;
657 ret = ret_error;
658 } else {
659 char hardlink[1024];
660 int namelen;
661 if ((namelen = readlink(queue.buf, hardlink, 1023)) == -1) {
662 conn->error_code = http_internal_error;
663 ret = ret_error;
664 } else {
665 hardlink[namelen] = '\0';
666 if (stat(hardlink, &statbuf) != 0) {
667 conn->error_code = http_internal_error;
668 ret = ret_error;
669 } else {
670 struct stat statbuf2;
671 cherokee_buffer_t busy = CHEROKEE_BUF_INIT;
672 cherokee_buffer_add_va (&busy, "/mnt/images/queue/%s_%s.busy", hdl->user.buf, name);
673 if (stat(busy.buf, &statbuf2) != 0) {
674 conn->error_code = http_internal_error;
675 ret = ret_error;
676 } else {
677 cherokee_buffer_add_va (buf, "%f", (float)((float)statbuf2.st_size / (float)statbuf.st_size));
678 ret = ret_ok;
680 cherokee_buffer_mrproper(&busy);
685 cherokee_buffer_mrproper(&queue);
686 return ret;
687 break;
690 /* Save the memory of a domain to a file and suspend the domain */
691 case domainSave: {
692 ret_t ret = ret_ok;
693 int result;
694 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
695 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/memory", hdl->user.buf, hdl->vm.buf);
696 if ((result = virDomainSave(dom, path.buf)) != 0) {
697 TRACE("virt", "Saving of %s/%s failed", hdl->user.buf, hdl->vm.buf);
698 conn->error_code = http_internal_error;
699 ret = ret_error;
701 cherokee_buffer_mrproper(&path);
703 cherokee_buffer_add_long10(buf, result);
704 return ret;
705 break;
708 /* Restore the memory of a domain from a file and resume the domain */
709 case domainRestore: {
710 ret_t ret = ret_ok;
711 struct stat statbuf;
712 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
713 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/memory", hdl->user.buf, hdl->vm.buf);
715 if (stat(path.buf, &statbuf) == 0) {
716 int result;
717 if ((result = virDomainRestore(virConn, path.buf)) != 0) {
718 TRACE("virt", "Restoring of %s/%s failed", hdl->user.buf, hdl->vm.buf);
719 conn->error_code = http_internal_error;
720 ret = ret_error;
722 cherokee_buffer_add_long10(buf, result);
723 } else {
724 TRACE("virt", "Memory file for %s/%s does not exist", hdl->user.buf, hdl->vm.buf);
725 conn->error_code = http_not_found;
726 ret = ret_error;
729 cherokee_buffer_mrproper(&path);
730 return ret;
731 break;
734 case domainUndefine: {
735 int result;
736 if ((result = virDomainUndefine(dom)) != 0) {
737 conn->error_code = http_internal_error; /* TODO */
740 cherokee_buffer_add_long10(buf, result);
741 break;
744 case domainAttachDevice_args: {
745 int result;
746 void *temp;
747 ret_t ret;
749 ret = cherokee_avl_get_ptr (conn->arguments, "type", &temp);
751 if (ret == ret_ok) {
752 cherokee_buffer_t xml = CHEROKEE_BUF_INIT;
754 if (strcmp(temp, "disk") == 0) {
755 void *device;
756 if ((ret = cherokee_avl_get_ptr (conn->arguments, "device", &device)) == ret_ok) {
757 void *file;
758 if ((ret = cherokee_avl_get_ptr (conn->arguments, "file", &file)) == ret_ok) {
759 cherokee_buffer_add_va (&xml, VIRT_DISK_XML, file, device);
760 } else {
761 virStorageVolPtr volume = virt_get_vol_by_args(hdl, virConn, 1);
763 if (volume == NULL) {
764 return ret_error;
767 file = virStorageVolGetPath(volume);
768 cherokee_buffer_add_va (&xml, VIRT_DISK_XML, file, device);
769 free(file);
771 virStorageVolFree(volume);
772 ret = ret_ok;
777 else if (strcmp(temp, "interface") == 0) {
778 void *mac, *ip;
779 if ((ret = cherokee_avl_get_ptr (conn->arguments, "mac", &mac)) == ret_ok && (ret = cherokee_avl_get_ptr (conn->arguments, "ip", &ip)) == ret_ok) {
780 cherokee_buffer_add_va (&xml, VIRT_INTERFACE_XML, mac, ip);
781 } else {
782 char *mac = NULL;
783 char *ip = NULL;
784 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
785 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
786 if (getNewMac("dv28", domu.buf, &mac, &ip) == 0)
787 cherokee_buffer_add_va (&xml, VIRT_INTERFACE_XML, mac, ip);
788 cherokee_buffer_mrproper(&domu);
792 if (ret == ret_ok && (result = virDomainAttachDevice(dom, (const char *) xml.buf)) == 0) {
793 ret = ret_ok;
794 } else {
795 conn->error_code = http_internal_error;
796 return ret_error;
799 cherokee_buffer_add_long10(buf, result);
800 cherokee_buffer_mrproper(&xml);
801 } else {
802 TRACE("virt", "DeviceAttach_args; type was not specified");
803 conn->error_code = http_bad_request;
804 return ret_error;
807 break;
810 case domainAttachDevice: {
811 off_t postl;
812 int result;
813 cherokee_buffer_t post = CHEROKEE_BUF_INIT;
814 cherokee_post_get_len (&conn->post, &postl);
815 cherokee_post_walk_read (&conn->post, &post, (cuint_t) postl);
816 if ((result = virDomainAttachDevice(dom, (const char *) post.buf)) != 0)
817 conn->error_code = http_internal_error;
819 cherokee_buffer_mrproper(&post);
820 cherokee_buffer_add_long10(buf, result);
821 break;
825 case domainGetXMLDesc: {
826 char *xml = virDomainGetXMLDesc(dom, 0);
827 cherokee_buffer_add(buf, xml, strlen(xml));
828 free(xml);
829 break;
833 case domainDetachDevice: {
834 off_t postl;
835 int result;
836 ret_t ret;
837 cherokee_buffer_t post = CHEROKEE_BUF_INIT;
838 cherokee_post_get_len (&conn->post, &postl);
839 cherokee_post_walk_read (&conn->post, &post, (cuint_t) postl);
841 xmlDocPtr doc = xmlParseMemory((const char *) post.buf, post.len);
842 if (doc == NULL) {
843 TRACE("virt", "DeviceAttach; XML document unparceble");
844 conn->error_code = http_bad_request;
845 ret = ret_error;
846 } else
847 ret = ret_ok;
849 if (ret == ret_ok) {
850 if ((result = virDomainDetachDevice(dom, (const char *) post.buf)) != 0) {
851 conn->error_code = http_internal_error;
852 /* TODO: betere afhandeling */
854 xmlXPathContextPtr context = xmlXPathNewContext(doc);
855 if (context != NULL) {
856 xmlXPathObjectPtr obj = xmlXPathEval("string(//interface/mac/@address)", context);
857 xmlXPathFreeContext(context);
858 if ((obj != NULL) && (obj->type == XPATH_STRING) &&
859 (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
860 removeOldMac("dv28", (char *) obj->stringval);
862 xmlXPathFreeObject(obj);
865 xmlFreeDoc(doc);
866 xmlCleanupParser();
869 cherokee_buffer_mrproper(&post);
870 cherokee_buffer_add_long10(buf, result);
871 break;
874 case domainGetID: {
875 cherokee_buffer_add_ulong10(buf, virDomainGetID(dom));
876 break;
878 case domainGetMaxMemory: {
879 cherokee_buffer_add_ulong10(buf, virDomainGetMaxMemory (dom));
880 break;
882 case domainGetMaxVcpus: {
883 cherokee_buffer_add_long10(buf, virDomainGetMaxVcpus (dom));
884 break;
886 case domainGetName: {
887 const char *name = virDomainGetName (dom);
888 cherokee_buffer_add(buf, name, strlen(name));
889 break;
891 case domainGetUUID: {
892 unsigned char uuid[VIR_UUID_BUFLEN];
893 if (virDomainGetUUID(dom, uuid) == 0) {
894 cherokee_buffer_add_str(buf, uuid);
895 } else {
896 conn->error_code = http_internal_error;
897 return ret_error;
899 break;
901 case domainGetUUIDString: {
902 unsigned char uuid[VIR_UUID_STRING_BUFLEN];
903 if (virDomainGetUUIDString(dom, uuid) == 0) {
904 cherokee_buffer_add_str(buf, uuid);
905 } else {
906 conn->error_code = http_internal_error;
907 return ret_error;
909 break;
912 case domainCreate: {
913 cherokee_buffer_t xmlDesc = CHEROKEE_BUF_INIT;
914 char txt[254];
915 save_xml(hdl, dom, &xmlDesc);
916 tender_t best_tender;
917 snprintf(txt, 252, "memory=%lu", virDomainGetMaxMemory(dom));
918 txt[253] = '\0';
920 best_tender.name = virDomainGetName(dom);
921 best_tender.cost = FLT_MAX;
922 best_tender.dom = NULL;
924 avahi_threaded_poll_lock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
925 AvahiServiceBrowser *sb = avahi_service_browser_new(HDL_AVAHI_PROPS(hdl)->client, AVAHI_IF_UNSPEC,
926 AVAHI_PROTO_UNSPEC, "_tender._tcp",
927 NULL, 0, tender_browse_callback, &best_tender);
929 if (sb == NULL) {
930 TRACE("avahi", "For whatever reason the sb is NULL\n");
931 } else {
933 AvahiEntryGroup *group = avahi_entry_group_new(HDL_AVAHI_PROPS(hdl)->client, entry_group_callback, NULL);
935 if (group == NULL) {
936 TRACE("avahi", "For whatever reason the group is NULL\n");
937 } else {
938 avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0,
939 best_tender.name, "_offer._tcp", NULL, NULL, 651, txt, NULL, NULL);
940 avahi_entry_group_commit(group);
941 avahi_threaded_poll_unlock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
943 sleep(3); /* we are for quick bidding ;) */
945 avahi_threaded_poll_lock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
946 avahi_entry_group_free(group);
949 avahi_service_browser_free(sb);
950 avahi_threaded_poll_unlock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
952 if (best_tender.dom == NULL) {
953 TRACE("virt", "Nobody wants %s, poor vm!\n", best_tender.name);
954 cherokee_buffer_add_long10(buf, -1);
955 } else {
956 if (virDomainUndefine(dom) != 0) {
957 TRACE("virt", "Can't undefine %s, some idiot run it already?\n", best_tender.name);
958 cherokee_buffer_add_long10(buf, -1);
959 } else {
960 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
961 virConnectClose(virConn);
962 cherokee_buffer_add_va (&uri, "xen://%s/", best_tender.dom);
963 virConn = virConnectOpen (uri.buf);
964 if (virConn == NULL) {
965 TRACE("virt", "Can't connect to %s\n", uri.buf);
966 cherokee_buffer_add_long10(buf, -1);
967 } else {
968 cherokee_buffer_add_long10(buf,
969 (virDomainCreateLinux(virConn, xmlDesc.buf, 0) != NULL ?
970 0 : -1 ));
972 cherokee_buffer_mrproper(&uri);
974 TRACE("virt", "going to free\n");
975 free(best_tender.dom);
980 cherokee_buffer_mrproper(&xmlDesc);
981 break;
984 case domainDestroy: {
985 cherokee_buffer_add_long10(buf, virDomainDestroy (dom));
986 break;
989 case domainReboot: {
990 cherokee_buffer_add_long10(buf, virDomainReboot (dom, 0));
991 break;
994 case domainShutdown: {
995 cherokee_buffer_add_long10(buf, virDomainShutdown (dom));
996 break;
1000 case domainGetOSType: {
1001 char *ostype = virDomainGetOSType(dom);
1002 cherokee_buffer_add(buf, ostype, strlen(ostype));
1003 free(ostype);
1004 break;
1011 if (hdl->action == domainUndefine) {
1012 /* Remove VM data from filesystem */
1013 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
1014 cherokee_buffer_t path2 = CHEROKEE_BUF_INIT;
1015 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/index.xml", hdl->user.buf, hdl->vm.buf);
1016 cherokee_buffer_add_buffer (&path2, &path);
1017 cherokee_buffer_add_str (&path2, ".deleted");
1018 // unlink(path.buf); /* TODO: instead of delet replace */
1019 rename(path.buf, path2.buf);
1020 cherokee_buffer_mrproper(&path);
1021 cherokee_buffer_mrproper(&path2);
1022 } else if (hdl->action != domainCreate) {
1023 save_xml(hdl, dom, NULL);
1026 return ret_ok;
1029 static virStoragePoolPtr
1030 virt_get_pool_by_args(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
1031 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1032 virStoragePoolPtr pool;
1033 void *temp;
1034 ret_t ret;
1036 ret = cherokee_avl_get_ptr (conn->arguments, "pool", &temp);
1037 if (unlikely(ret < ret_ok)) {
1038 TRACE("virt", "virStoragePoolPtr; Pool argument not specified");
1039 conn->error_code = http_bad_request;
1040 return NULL;
1043 pool = virStoragePoolLookupByName(virConn, temp);
1045 return pool;
1048 static virStorageVolPtr
1049 virt_get_vol_by_args(cherokee_handler_virt_t *hdl, virConnectPtr virConn, unsigned short int prefix) {
1050 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1051 virStoragePoolPtr pool;
1052 virStorageVolPtr volume;
1053 void *temp;
1054 ret_t ret;
1056 pool = virt_get_pool_by_args(hdl, virConn);
1058 if (pool == NULL) {
1059 conn->error_code = http_not_found;
1060 return NULL;
1063 virStoragePoolRefresh(pool, 0); /* TODO: might be better to do it outside */
1065 ret = cherokee_avl_get_ptr (conn->arguments, "volume", &temp);
1066 if (unlikely(ret < ret_ok)) {
1067 TRACE("virt", "virStorageVolPtr; Volume argument not specified");
1068 conn->error_code = http_bad_request;
1069 return NULL;
1072 if (prefix == 1) {
1073 cherokee_buffer_t fullvol = CHEROKEE_BUF_INIT;
1074 cherokee_buffer_add_va (&fullvol, "%s_%s", hdl->user.buf, temp);
1075 volume = virStorageVolLookupByName(pool, fullvol.buf);
1076 cherokee_buffer_mrproper(&fullvol);
1077 } else {
1078 volume = virStorageVolLookupByName(pool, temp);
1081 if (volume == NULL)
1082 conn->error_code = http_not_found;
1084 virStoragePoolFree(pool);
1086 return volume;
1089 /* This function is the home for all functions that need a working
1090 * pool/volume combination */
1091 static ret_t
1092 virt_pool_vol(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
1093 cherokee_buffer_t *buf = &HDL_AVAHI(hdl)->buffer;
1094 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1095 virStorageVolPtr volume;
1096 ret_t ret = ret_ok;
1098 /* We only allow clone to run 'unsafe', others get prefixed */
1099 volume = virt_get_vol_by_args(hdl, virConn, (hdl->action != storageVolCloneXML_args));
1101 /* If the volume doesn't exist, no point to continue */
1102 if (volume == NULL) {
1103 conn->error_code = http_not_found;
1104 return ret_error;
1107 switch (hdl->action) {
1108 /* Sets the password of a specific volume, requires the password= option */
1109 case storageVolSetPassword_args: {
1110 void *temp;
1111 ret = cherokee_avl_get_ptr (conn->arguments, "password", &temp);
1112 if (unlikely(ret < ret_ok)) {
1113 TRACE("virt", "storageVolSetPassword_args; password argument not specified");
1114 conn->error_code = http_bad_request;
1115 ret = ret_error;
1116 goto virt_pool_vol_cleanup;
1117 } else {
1118 cherokee_buffer_t cmd_passwd = CHEROKEE_BUF_INIT;
1119 cherokee_buffer_add_va (&cmd_passwd, "/usr/sbin/passwdchanger.sh %s %s\n", virStorageVolGetKey(volume), temp);
1120 cherokee_buffer_add_long10(buf, system(cmd_passwd.buf));
1121 cherokee_buffer_mrproper(&cmd_passwd);
1123 break;
1126 /* Removes a volume */
1127 case storageVolDelete_args: {
1128 cherokee_buffer_add_long10(buf, virStorageVolDelete(volume, 0));
1129 break;
1132 /* Gives a description of a storage volume in XML */
1133 case storageVolGetXMLDesc_args: {
1134 char *xml = virStorageVolGetXMLDesc(volume, 0);
1135 cherokee_buffer_add(buf, xml, strlen(xml));
1136 free(xml);
1137 break;
1140 /* Clones a volume, insecure method! requires a new name= */
1141 case storageVolCloneXML_args: {
1142 void *name;
1143 cherokee_buffer_t busy = CHEROKEE_BUF_INIT;
1145 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
1146 if (unlikely(ret < ret_ok)) {
1147 TRACE("virt", "storageVolCloneXML_args; name argument not specified");
1148 conn->error_code = http_bad_request;
1149 ret = ret_error;
1150 goto virt_pool_vol_cleanup;
1153 cherokee_buffer_add_va (&busy, "/mnt/images/queue/%s_%s.queued", hdl->user.buf, name);
1155 if (unlikely(symlink(virStorageVolGetKey(volume), busy.buf) == 1)) {
1156 conn->error_code = http_internal_error;
1157 ret = ret_error;
1158 } else {
1159 cherokee_buffer_add_str(buf, "QUEUED");
1162 cherokee_buffer_mrproper(&busy);
1164 break;
1169 virt_pool_vol_cleanup:
1170 /* And in the end we need to free the volume we have used */
1171 virStorageVolFree(volume);
1173 return ret;
1176 static ret_t
1177 virt_virt_new(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
1178 cherokee_buffer_t *buf = &HDL_AVAHI(hdl)->buffer;
1179 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1180 cherokee_buffer_t xml = CHEROKEE_BUF_INIT;
1181 virStoragePoolPtr pool = NULL;
1182 ret_t ret = ret_ok;
1184 switch (hdl->action) {
1186 case storageVolCreateXML_args: {
1187 void *temp, *type, *name, *unit;
1188 unsigned long int capacity, allocation;
1190 ret = cherokee_avl_get_ptr (conn->arguments, "pool", &temp);
1191 if (unlikely(ret < ret_ok)) {
1192 TRACE("virt", "storageVolCreateXML_args; pool argument not specified");
1193 conn->error_code = http_bad_request;
1194 goto virt_virt_new_cleanup;
1197 TRACE("args", "%s", temp);
1199 pool = virStoragePoolLookupByName(virConn, temp);
1201 if (pool == NULL) {
1202 conn->error_code = http_not_found;
1203 goto virt_virt_new_cleanup;
1206 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
1207 if (unlikely(ret < ret_ok)) {
1208 TRACE("virt", "storageVolCreateXML_args; name argument not specified");
1209 conn->error_code = http_bad_request;
1210 goto virt_virt_new_cleanup;
1213 ret = cherokee_avl_get_ptr (conn->arguments, "type", &type);
1214 if (unlikely(ret < ret_ok)) {
1215 TRACE("virt", "storageVolCreateXML_args; type argument not specified");
1216 conn->error_code = http_bad_request;
1217 goto virt_virt_new_cleanup;
1220 ret = cherokee_avl_get_ptr (conn->arguments, "unit", &unit);
1221 if (unlikely(ret < ret_ok)) {
1222 TRACE("virt", "storageVolCreateXML_args; unit argument not specified");
1223 conn->error_code = http_bad_request;
1224 goto virt_virt_new_cleanup;
1227 ret = cherokee_avl_get_ptr (conn->arguments, "allocation", &temp);
1228 if (unlikely(ret < ret_ok)) {
1229 TRACE("virt", "storageVolCreateXML_args; allocation argument not specified");
1230 conn->error_code = http_bad_request;
1231 goto virt_virt_new_cleanup;
1234 allocation = strtoul(temp, NULL, 10);
1235 if (errno == ERANGE) {
1236 TRACE("virt", "storageVolCreateXML_args; allocation is not a number");
1237 conn->error_code = http_bad_request;
1238 ret = ret_error;
1239 goto virt_virt_new_cleanup;
1242 ret = cherokee_avl_get_ptr (conn->arguments, "capacity", &temp);
1243 if (unlikely(ret < ret_ok)) {
1244 TRACE("virt", "storageVolCreateXML_args; capacity argument not specified");
1245 conn->error_code = http_bad_request;
1246 goto virt_virt_new_cleanup;
1249 capacity = strtoul(temp, NULL, 10);
1250 if (errno == ERANGE || capacity == 0) {
1251 TRACE("virt", "storageVolCreateXML_args; capacity is not a number");
1252 conn->error_code = http_bad_request;
1253 ret = ret_error;
1254 goto virt_virt_new_cleanup;
1257 cherokee_buffer_add_va (&xml, VIRT_STORAGE_XML, type, hdl->user.buf, name, allocation, unit, capacity, hdl->user.buf, name, hdl->user.buf, name);
1258 break;
1261 case domainDefineXML_args: {
1262 ret_t ret;
1263 unsigned int i;
1264 unsigned long vcpu = 0, interface = 0, memory = 0;
1266 void *temp = NULL;
1268 ret = cherokee_avl_get_ptr (conn->arguments, "vcpu", &temp);
1269 if (ret == ret_ok)
1270 vcpu = strtoul(temp, (char **) NULL, 10);
1272 if (ret != ret_ok || errno == ERANGE || vcpu == 0) {
1273 conn->error_code = http_internal_error;
1274 goto virt_virt_new_cleanup;
1278 ret = cherokee_avl_get_ptr (conn->arguments, "memory", &temp);
1279 if (ret == ret_ok)
1280 memory = strtoul(temp, (char **) NULL, 10);
1282 if (ret != ret_ok || errno == ERANGE || memory == 0) {
1283 conn->error_code = http_internal_error;
1284 goto virt_virt_new_cleanup;
1288 ret = cherokee_avl_get_ptr (conn->arguments, "interface", &temp);
1289 if (ret == ret_ok)
1290 interface = strtoul(temp, (char **) NULL, 10);
1292 if (ret != ret_ok || errno == ERANGE) {
1293 conn->error_code = http_internal_error;
1294 goto virt_virt_new_cleanup;
1297 cherokee_buffer_t cmdline_extra = CHEROKEE_BUF_INIT;
1298 cherokee_buffer_t xml_interfaces = CHEROKEE_BUF_INIT;
1299 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
1300 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
1302 for (i = 0; i < interface; i++) {
1303 char *mac = NULL;
1304 char *ip = NULL;
1305 if (getNewMac("dv28", domu.buf, &mac, &ip) == 0) {
1306 cherokee_buffer_add_va (&xml_interfaces, VIRT_INTERFACE_XML, mac, ip);
1307 if (i == 0) {
1308 /* TODO: terrible hack */
1309 char gateway[16];
1310 char *temp;
1311 strcpy(gateway, ip);
1312 temp = strchr(gateway, '.');
1313 temp = strchr(++temp, '.');
1314 temp = strchr(++temp, '.');
1315 strcpy(++temp, "254");
1316 cherokee_buffer_add_va (&cmdline_extra, VIRT_DOMAIN_CMD_IP, ip, gateway);
1321 cherokee_buffer_mrproper(&domu);
1323 cherokee_buffer_add_va (&xml, VIRT_DOMAIN_XML,
1324 hdl->user.buf, hdl->vm.buf, cmdline_extra.buf, memory, vcpu, xml_interfaces.buf);
1326 cherokee_buffer_mrproper(&xml_interfaces);
1327 cherokee_buffer_mrproper(&cmdline_extra);
1328 break;
1331 case storageVolCreateXML:
1332 case domainDefineXML: {
1333 off_t postl;
1334 cherokee_post_get_len (&conn->post, &postl);
1335 cherokee_post_walk_read (&conn->post, &xml, (cuint_t) postl);
1336 break;
1340 switch (hdl->action) {
1341 case domainDefineXML_args:
1342 case domainDefineXML: {
1343 virDomainPtr result = virDomainDefineXML(virConn, (const char *) xml.buf);
1345 if (result == NULL) {
1346 /* TODO: vrij maken eventuele uitgegeven macs! */
1347 conn->error_code = http_internal_error;
1348 goto virt_virt_new_cleanup;
1351 save_xml(hdl, result, buf);
1353 virDomainFree(result);
1355 break;
1358 case storageVolCreateXML_args:
1359 case storageVolCreateXML: {
1360 virStorageVolPtr vol = virStorageVolCreateXML(pool, xml.buf, 0);
1362 if (vol == NULL) {
1363 cherokee_buffer_add_long10(buf, -1);
1364 goto virt_virt_new_cleanup;
1367 virStorageVolFree(vol);
1369 cherokee_buffer_add_long10(buf, 0);
1370 break;
1374 virt_virt_new_cleanup:
1375 cherokee_buffer_mrproper(&xml);
1377 if (pool)
1378 virStoragePoolFree(pool);
1380 return ret;
1384 static ret_t
1385 virt_virt_do(cherokee_handler_virt_t *hdl, cherokee_buffer_t *domu, cherokee_buffer_t *uri)
1387 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1389 ret_t ret = ret_error;
1390 virConnectPtr virConn = NULL;
1393 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE)
1394 virConn = virConnectOpen (uri->buf);
1396 if (!virConn && !(virConn = virConnectOpenReadOnly (uri->buf))) {
1397 conn->error_code = http_service_unavailable;
1398 return ret_error;
1401 switch (hdl->action) {
1402 case storageVolDelete_args:
1403 case storageVolSetPassword_args:
1404 case storageVolGetXMLDesc_args:
1405 case storageVolCloneXML_args:
1406 ret = virt_pool_vol(hdl, virConn);
1407 break;
1409 case storageVolCreateXML_args:
1410 case storageVolCreateXML:
1411 case domainDefineXML_args:
1412 case domainDefineXML:
1413 ret = virt_virt_new(hdl, virConn);
1414 break;
1416 default: {
1417 virDomainPtr dom;
1418 if ((dom = virDomainLookupByName(virConn, domu->buf)) == NULL) {
1419 conn->error_code = http_not_found;
1420 } else {
1421 ret = virt_virt_function(hdl, dom, virConn);
1422 virDomainFree(dom);
1427 virConnectClose(virConn);
1428 return ret;
1431 static ret_t
1432 virt_while (cherokee_buffer_t *key, void *value, void *param) {
1433 cherokee_handler_virt_t * hdl = param;
1434 // cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
1435 // cherokee_buffer_add_va (&uri, "xen://%s/", ((cherokee_buffer_t *) value)->buf);
1436 // virt_virt_do((cherokee_handler_virt_t *) param, key, &uri);
1437 // cherokee_buffer_mrproper(&uri);
1439 cherokee_buffer_add_va (&hdl->buffer, "<domain><name>%s</name></domain>", key->buf);
1441 return ret_ok;
1444 static ret_t
1445 virt_while_user (cherokee_buffer_t *key, void *value, void *param) {
1446 cherokee_handler_virt_t *hdl = param;
1447 if (key->len > hdl->user.len && key->buf[hdl->user.len] == '_' && strncmp(key->buf, hdl->user.buf, hdl->user.len) == 0)
1448 return virt_while (key, value, param);
1450 return ret_ok;
1453 static ret_t
1454 virt_build_page (cherokee_handler_virt_t *hdl)
1456 ret_t ret;
1457 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1458 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
1459 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
1461 /* We use the webserver methods to parse the querystring */
1462 /* Maybe do something smart with ENUM, begin_args, end_args... */
1463 if ((hdl->action == domainDefineXML_args) ||
1464 (hdl->action == domainAttachDevice_args) ||
1465 (hdl->action == storageVolGetXMLDesc_args) ||
1466 (hdl->action == storageVolDelete_args) ||
1467 (hdl->action == storageVolSetPassword_args) ||
1468 (hdl->action == storageVolCloneXML_args) ||
1469 (hdl->action == storageVolCloneStatus_args) ||
1470 (hdl->action == storageVolCreateXML_args) ||
1471 (hdl->action == graphLoad_args) ||
1472 (hdl->action == graphInterface_args)) {
1473 ret = cherokee_connection_parse_args (conn);
1474 if (unlikely(ret < ret_ok)) {
1475 conn->error_code = http_internal_error;
1476 return ret_error;
1480 switch (hdl->action) {
1481 case graphInterface_args: {
1482 struct stat statbuf;
1483 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
1484 void *interface;
1485 if ((ret = cherokee_avl_get_ptr (conn->arguments, "interface", &interface)) != ret_ok)
1486 interface = "eth0";
1488 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/interface_%s.rrd", hdl->user.buf, hdl->vm.buf, interface);
1489 if (stat(path.buf, &statbuf) != 0) {
1490 conn->error_code = http_not_found;
1491 ret = ret_error;
1492 } else {
1493 void *width, *height, *start, *end;
1494 if ((ret = cherokee_avl_get_ptr (conn->arguments, "width", &width)) != ret_ok)
1495 width = "600";
1497 if ((ret = cherokee_avl_get_ptr (conn->arguments, "height", &height)) != ret_ok)
1498 height = "200";
1500 if ((ret = cherokee_avl_get_ptr (conn->arguments, "start", &start)) != ret_ok)
1501 start = "now-1h";
1503 if ((ret = cherokee_avl_get_ptr (conn->arguments, "end", &end)) != ret_ok)
1504 end = "now";
1506 cherokee_buffer_t def1 = CHEROKEE_BUF_INIT;
1507 cherokee_buffer_t def2 = CHEROKEE_BUF_INIT;
1508 cherokee_buffer_add_va (&def1, "DEF:rxbytes=%s:rxbytes:AVERAGE:step=30", path.buf);
1509 cherokee_buffer_add_va (&def2, "DEF:txbytes=%s:txbytes:AVERAGE:step=30", path.buf);
1510 char **calcpr = NULL;
1511 int xsize, ysize;
1512 double ymin, ymax;
1513 char *filename = mktemp(strdup("handler_virt_XXXXXX"));
1514 char *r_graph[] = { "rrdgraph", filename,
1515 "-a", "PNG",
1516 "-w", width,
1517 "-h", height,
1518 "--start", start,
1519 "--end", end,
1520 "--title", interface,
1521 "--lower-limit", "0", "--alt-autoscale-max", "--vertical-label", "bits per second",
1522 def1.buf,
1523 def2.buf,
1524 "CDEF:txbits=txbytes,8,*",
1525 "CDEF:rxbits=rxbytes,8,*",
1526 "AREA:rxbits#00EE00:rxbits",
1527 "LINE:txbits#0000EE:txbits" };
1529 rrd_graph(25, r_graph, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
1530 if ((ret = cherokee_buffer_read_file(&hdl->buffer, filename)) == ret_error) {
1531 hdl->action = http_internal_error;
1534 unlink(filename);
1535 free(filename);
1537 cherokee_buffer_mrproper(&def1);
1538 cherokee_buffer_mrproper(&def2);
1540 cherokee_buffer_mrproper(&path);
1541 goto virt_build_page_cleanup;
1546 case graphLoad_args: {
1547 struct stat statbuf;
1548 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
1549 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/cpuTime.rrd", hdl->user.buf, hdl->vm.buf);
1550 if (stat(path.buf, &statbuf) != 0) {
1551 conn->error_code = http_not_found;
1552 ret = ret_error;
1553 } else {
1554 void *width, *height, *start, *end;
1555 if ((ret = cherokee_avl_get_ptr (conn->arguments, "width", &width)) != ret_ok)
1556 width = "600";
1558 if ((ret = cherokee_avl_get_ptr (conn->arguments, "height", &height)) != ret_ok)
1559 height = "200";
1561 if ((ret = cherokee_avl_get_ptr (conn->arguments, "start", &start)) != ret_ok)
1562 start = "now-1h";
1564 if ((ret = cherokee_avl_get_ptr (conn->arguments, "end", &end)) != ret_ok)
1565 end = "now";
1567 /* TODO: wat error checking? */
1569 cherokee_buffer_t def = CHEROKEE_BUF_INIT;
1570 cherokee_buffer_add_va (&def, "DEF:cputime=%s:cpuTime:AVERAGE:step=30", path.buf);
1571 char **calcpr = NULL;
1572 int xsize, ysize;
1573 double ymin, ymax;
1574 char *filename = mktemp(strdup("handler_virt_XXXXXX"));
1575 char *r_graph[] = { "rrdgraph",
1576 filename,
1577 "-a", "PNG",
1578 "-w", width,
1579 "-h", height,
1580 "--start", start,
1581 "--end", end,
1582 "--title", hdl->vm.buf,
1583 "--lower-limit", "0", "--alt-autoscale-max", "--vertical-label", "(used / total) time",
1584 def.buf,
1585 "CDEF:cpuload=cputime,1000000000,/",
1586 "LINE:cpuload#EE0000:cpuLoad" };
1588 rrd_graph(22, r_graph, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
1589 if ((ret = cherokee_buffer_read_file(&hdl->buffer, filename)) == ret_error) {
1590 hdl->action = http_internal_error;
1593 unlink(filename);
1594 free(filename);
1596 cherokee_buffer_mrproper(&def);
1598 cherokee_buffer_mrproper(&path);
1599 goto virt_build_page_cleanup;
1603 if (hdl->action > xml && HDL_VIRT_PROPS(hdl)->xsl.len > 0)
1604 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);
1607 switch (hdl->action) {
1608 case showall:
1609 cherokee_buffer_add_str (&hdl->buffer, "<domains>\n");
1610 // avahi_threaded_poll_lock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
1611 cherokee_avl_r_while(&HDL_AVAHI_PROPS(hdl)->entries, virt_while, hdl, NULL, NULL);
1612 // avahi_threaded_poll_unlock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
1613 cherokee_buffer_add_str (&hdl->buffer, "</domains>");
1614 ret = ret_ok;
1615 break;
1617 case showuservms: {
1618 cherokee_buffer_add_str (&hdl->buffer, "<domains>\n");
1619 // avahi_threaded_poll_lock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
1620 // cherokee_avl_r_while(&HDL_AVAHI_PROPS(hdl)->entries, virt_while_user, hdl, NULL, NULL);
1621 // avahi_threaded_poll_unlock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
1622 get_all_configurations(&HDL_AVAHI_PROPS(hdl)->entries, &hdl->user, &hdl->buffer);
1623 cherokee_buffer_add_str (&hdl->buffer, "</domains>");
1624 ret = ret_ok;
1625 break;
1628 default: {
1629 cherokee_buffer_t *hostname = NULL;
1630 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
1632 // avahi_threaded_poll_lock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
1633 ret = cherokee_avl_r_get(&HDL_AVAHI_PROPS(hdl)->entries, &domu, (void **) &hostname);
1634 // avahi_threaded_poll_unlock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
1636 if (ret == ret_not_found) {
1637 virDomainPtr virDom;
1638 virConnectPtr virConn;
1639 cherokee_buffer_add_va (&uri, "xen://%s/", HDL_VIRT_PROPS(hdl)->virt); // TODO: change!
1641 /* If we have the read only parameter, we will set up a connection to the
1642 * Hypervisor here. */
1643 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE)
1644 virConn = virConnectOpen (uri.buf);
1646 /* We should already have a connection (read only) or build a new connection
1647 * if this doesn't work, we can safely assume our services is fubar */
1648 if (!virConn && !(virConn = virConnectOpenReadOnly (uri.buf))) {
1649 conn->error_code = http_service_unavailable;
1650 return ret_error;
1653 /* We lookup if there is a domain somewhere with this name */
1654 if ((virDom = virDomainLookupByName(virConn, domu.buf)) == NULL) {
1655 /* If the domain is not found on the network the only possible
1656 * command that is possible would be to Define it */
1657 if (hdl->action == domainDefineXML_args || hdl->action == domainDefineXML) {
1658 ret = ret_ok;
1659 } else {
1660 /* We should also look on disk for defined stuff */
1661 cherokee_buffer_t xmlDesc = CHEROKEE_BUF_INIT;
1662 if (load_xml(hdl, &xmlDesc) == ret_ok && (virDom = virDomainDefineXML(virConn, xmlDesc.buf)) != NULL) {
1663 /* The domain existed and is loaded! */
1664 ret = ret_ok;
1665 } else {
1666 /* Otherwise we don't have anything to do and we should
1667 * return an error */
1668 hdl->action = nothing;
1669 conn->error_code = http_not_found;
1670 ret = ret_error;
1673 cherokee_buffer_mrproper(&xmlDesc);
1675 } else {
1676 /* We don't want te recreate things that already found on the network
1677 * first undefine them! */
1678 if (hdl->action == domainDefineXML_args || hdl->action == domainDefineXML) {
1679 TRACE("virt", "domainDefineXML_args/domainDefineXML; domain already exists");
1680 hdl->action = nothing;
1681 conn->error_code = http_bad_request;
1682 ret = ret_error;
1683 } else {
1684 /* Everything is ok, because nothing is found. */
1685 ret = ret_ok;
1688 /* Domain wasn't NULL, so we should free it here */
1689 virDomainFree(virDom);
1691 virConnectClose (virConn);
1692 } else if (ret == ret_ok) {
1693 cherokee_buffer_add_va (&uri, "xen://%s/", hostname->buf);
1694 printf("%s\n", uri.buf);
1695 } else {
1696 hdl->action = http_internal_error;
1697 hdl->action = nothing;
1698 ret = ret_error;
1704 if (ret == ret_ok && hdl->action != showall && hdl->action != showuservms) {
1705 ret = virt_virt_do(hdl, &domu, &uri);
1708 virt_build_page_cleanup:
1709 cherokee_buffer_mrproper(&domu);
1710 cherokee_buffer_mrproper(&uri);
1712 return ret;
1716 ret_t
1717 cherokee_handler_virt_new (cherokee_handler_t **hdl, cherokee_connection_t *cnt, cherokee_module_props_t *props)
1719 ret_t ret;
1720 CHEROKEE_NEW_STRUCT (n, handler_virt);
1722 /* Init the base class
1725 cherokee_handler_init_base(HANDLER(n), cnt, HANDLER_PROPS(props), PLUGIN_INFO_HANDLER_PTR(virt));
1727 MODULE(n)->init = (handler_func_init_t) cherokee_handler_virt_init;
1728 MODULE(n)->free = (module_func_free_t) cherokee_handler_virt_free;
1729 HANDLER(n)->step = (handler_func_step_t) cherokee_handler_virt_step;
1730 HANDLER(n)->add_headers = (handler_func_add_headers_t) cherokee_handler_virt_add_headers;
1732 HANDLER(n)->support = hsupport_length | hsupport_range;
1734 ret = cherokee_buffer_init (&n->buffer);
1735 if (unlikely(ret != ret_ok))
1736 return ret;
1738 ret = cherokee_buffer_ensure_size (&n->buffer, 4*1024);
1739 if (unlikely(ret != ret_ok))
1740 return ret;
1742 *hdl = HANDLER(n);
1744 return ret_ok;