*Very Exprimental*
[handlervirt.git] / handler_virt.c
blob9bdbbb8b8ae35c1191db66a7a32f17a786ee8f16
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /* Cherokee
5 * Authors:
6 * Alvaro Lopez Ortega <alvaro@alobbs.com>
7 * Stefan de Konink <stefan@konink.de>
9 * Copyright (C) 2001-2008 Alvaro Lopez Ortega
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of version 2 of the GNU General Public
13 * License as published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 * USA
26 // #define LINUX_CMDLINE "root=/dev/xvda ro ip=%s:1.2.3.4:%s:%s::eth0:off"
28 #define VIRT_INTERFACE_XML \
29 " <interface type='bridge'>" \
30 " <source bridge='xenbr0'/>" \
31 " <mac address='%s'/>" \
32 " <ip address='%s' />" \
33 " <script path='vif-bridge'/>" \
34 " </interface>"
36 //" <target dev='vif1.0'/>" \
38 #define VIRT_DISK_XML \
39 " <disk type='file' device='disk'>" \
40 " <driver name='tap' type='aio' />" \
41 " <source file='%s'/>" \
42 " <target dev='%s' bus='xen'/>" \
43 " </disk>"
45 #define VIRT_DOMAIN_CMD_IP "ip=%s:1.2.3.4:%s:255.255.255.0::eth0:off"
47 #define VIRT_DOMAIN_XML \
48 "<domain type='xen'>" \
49 " <name>%s_%s</name>" \
50 " <os>" \
51 " <type>linux</type>" \
52 " <kernel>/usr/lib/xen/boot/linux-2.6.20-xen-r6</kernel>" \
53 " <cmdline> root=/dev/xvda ro %s</cmdline>" \
54 " </os>" \
55 " <memory>%d</memory>" \
56 " <vcpu>%d</vcpu>" \
57 " <on_poweroff>destroy</on_poweroff>" \
58 " <on_reboot>restart</on_reboot>" \
59 " <on_crash>destroy</on_crash>" \
60 " <devices>" \
61 " %s" \
62 " </devices>" \
63 "</domain>"
65 #define VIRT_STORAGE_XML \
66 "<volume type='%s'>" \
67 " <name>%s_%s</name>" \
68 " <allocation>%lu</allocation>" \
69 " <capacity unit='%s'>%lu</capacity>" \
70 " <target>" \
71 " <path>%s_%s</path>" \
72 " <permissions>" \
73 " <owner>0744</owner>" \
74 " <group>0744</group>" \
75 " <mode>0744</mode>" \
76 " <label>%s_%s</label>" \
77 " </permissions>" \
78 " </target>" \
79 "</volume>"
82 #include "handler_virt.h"
83 #include "handler_clusterstats.h"
84 #include <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_clusterstats_props_free (PROP_CLUSTERSTATS(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_clusterstats_props_init_base (PROP_CLUSTERSTATS(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_clusterstats_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) {
291 AvahiClient *c;
292 assert(b);
294 c = avahi_service_browser_get_client(b);
296 /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
298 switch (event) {
299 case AVAHI_BROWSER_FAILURE:
300 fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_client_errno(c)));
301 return;
303 case AVAHI_BROWSER_NEW:
304 fprintf(stderr, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
306 /* We ignore the returned resolver object. In the callback
307 function we free it. If the server is terminated before
308 the callback function is called the server will free
309 the resolver for us. */
311 if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, tender_resolve_callback, userdata)))
312 fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(c)));
314 break;
316 case AVAHI_BROWSER_REMOVE:
317 fprintf(stderr, "%s, (Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", __func__, name, type, domain);
318 break;
320 case AVAHI_BROWSER_ALL_FOR_NOW:
321 case AVAHI_BROWSER_CACHE_EXHAUSTED:
322 fprintf(stderr, "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
323 break;
327 ret_t
328 cherokee_handler_virt_init (cherokee_handler_virt_t *hdl)
330 cherokee_connection_t *conn = HANDLER_CONN(hdl);
332 /* If someone is posting the server should be in
333 * read/write mode if this is not the case, just
334 * bail out directly */
336 if (conn->header.method == http_post && HDL_VIRT_PROPS(hdl)->read_only == TRUE) {
337 conn->error_code = http_unauthorized;
338 return ret_error;
341 int isroot = false;
342 int len;
343 char *this, *next;
345 cherokee_buffer_init(&hdl->user);
346 cherokee_buffer_init(&hdl->vm);
348 hdl->action = nothing;
350 cherokee_buffer_add (&conn->pathinfo,
351 conn->request.buf + conn->web_directory.len,
352 conn->request.len - conn->web_directory.len);
354 this = conn->pathinfo.buf + 1;
356 next = strchr(this, '/'); /* TODO: this code borks! */
358 if ((!next && (this && (len = strlen(this)) == 0)) || (next && ((len = next - this) == 0)) )
359 hdl->action = showall;
360 else {
361 cherokee_buffer_add (&hdl->user, this, len);
364 if (HDL_VIRT_PROPS(hdl)->authenticate) {
365 if (!conn->validator ||
366 (conn->validator &&
367 (!cherokee_buffer_cmp_buf(&conn->validator->user, &hdl->user) &&
368 !(isroot = cherokee_buffer_cmp (&conn->validator->user, "root", 4))))) {
369 hdl->action = nothing; /* just in case */
370 conn->error_code = http_unauthorized;
371 return ret_error;
373 } else {
374 isroot = true;
377 if (hdl->action == showall) {
378 if (!isroot) {
379 hdl->action = nothing;
380 conn->error_code = http_unauthorized;
381 return ret_error;
382 } else {
383 return virt_build_page(hdl);
388 if (!next) {
389 hdl->action = showuservms;
390 return virt_build_page(hdl);
391 } else {
392 this = next + 1;
393 next = strchr(this, '/');
395 if ( ( !next && (this && (len = strlen(this)) == 0) ) || (next && ((len = next - this) == 0)) ) {
396 //if (!next && (this && (len = strlen(this)) == 0) || (next && ((len = next - this) == 0)) ) {
397 hdl->action = showuservms;
398 return virt_build_page(hdl);
402 cherokee_buffer_add (&hdl->vm, this, len);
404 if (!next) {
405 hdl->action = domainGetXMLDesc;
406 return virt_build_page(hdl);
407 } else {
408 this = next + 1;
409 next = strchr(this, '/');
411 if (( !next && (this && (len = strlen(this)) == 0) ) || (next && ((len = next - this) == 0)) ) {
412 hdl->action = domainGetXMLDesc;
413 return virt_build_page(hdl);
417 hdl->action = not_implemented;
418 switch (conn->header.method) {
419 case http_get:
420 if (strncmp(this, "virDomain", 9) == 0) {
421 if (strncmp(this+9, "Get", 3) == 0) {
422 if (strcmp(this+12, "ID") == 0) hdl->action = domainGetID;
423 else if (strcmp(this+12, "Name") == 0) hdl->action = domainGetName;
424 else if (strcmp(this+12, "MaxMemory") == 0) hdl->action = domainGetMaxMemory;
425 else if (strcmp(this+12, "MaxVcpus") == 0) hdl->action = domainGetMaxVcpus;
426 else if (strcmp(this+12, "OSType") == 0) hdl->action = domainGetOSType;
427 else if (strcmp(this+12, "UUID") == 0) hdl->action = domainGetUUID;
428 else if (strcmp(this+12, "UUIDString") == 0) hdl->action = domainGetUUIDString;
429 else if (strcmp(this+12, "XMLDesc") == 0) hdl->action = domainGetXMLDesc;
432 else if (HDL_VIRT_PROPS(hdl)->read_only == FALSE) {
433 if (strcmp(this+9, "Create") == 0) hdl->action = domainCreate;
434 else if (strcmp(this+9, "Destroy") == 0) hdl->action = domainDestroy;
435 else if (strcmp(this+9, "Reboot") == 0) hdl->action = domainReboot;
436 else if (strcmp(this+9, "Shutdown") == 0) hdl->action = domainShutdown;
438 else if (strcmp(this+9, "Save") == 0) hdl->action = domainSave;
439 else if (strcmp(this+9, "Restore") == 0) hdl->action = domainRestore;
441 else if (strcmp(this+9, "AttachDevice") == 0) hdl->action = domainAttachDevice_args;
443 else if (strcmp(this+9, "DefineXML") == 0) hdl->action = domainDefineXML_args;
444 else if (strcmp(this+9, "Undefine") == 0) hdl->action = domainUndefine;
447 else if (HDL_VIRT_PROPS(hdl)->read_only == FALSE && strncmp(this, "virStorage", 10) == 0) {
448 if (strncmp(this+10, "Vol", 3) == 0) {
449 if (strcmp(this+13, "CreateXML") == 0) hdl->action = storageVolCreateXML_args;
450 else if (strcmp(this+13, "Delete") == 0) hdl->action = storageVolDelete_args;
451 else if (strcmp(this+13, "CloneXML") == 0) hdl->action = storageVolCloneXML_args;
452 else if (strcmp(this+13, "CloneStatus") == 0) hdl->action = storageVolCloneStatus_args;
453 else if (strcmp(this+13, "GetXMLDesc") == 0) hdl->action = storageVolGetXMLDesc_args;
454 else if (strcmp(this+13, "SetPassword") == 0) hdl->action = storageVolSetPassword_args;
457 else if (strncmp(this, "virGraph", 8) == 0) {
458 if (strcmp(this+8, "Load") == 0) hdl->action = graphLoad_args;
459 if (strcmp(this+8, "Interface") == 0) hdl->action = graphInterface_args;
461 break;
463 case http_post: {
464 off_t postl;
465 cherokee_post_get_len (&conn->post, &postl);
467 if (postl <= 0 || postl >= (INT_MAX-1)) {
468 TRACE("virt", "post without post");
469 conn->error_code = http_bad_request;
470 return ret_error;
473 if (strncmp(this, "virDomain", 9) == 0) {
474 if (strcmp(this+9, "AttachDevice") == 0) hdl->action = domainAttachDevice;
475 else if (strcmp(this+9, "DetachDevice") == 0) hdl->action = domainDetachDevice;
476 else if (strcmp(this+9, "DefineXML") == 0) hdl->action = domainDefineXML;
478 else if (strncmp(this, "virStorage", 10) == 0) {
479 if (strncmp(this+10, "Vol", 3) == 0) {
480 if (strcmp(this+13, "CreateXML") == 0) hdl->action = storageVolCreateXML;
484 break;
487 default:
488 return ret_error;
491 if (hdl->action <= 0) {
492 TRACE("virt", "There was no action specified");
493 conn->error_code = http_bad_request;
494 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_VIRT(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_CLUSTERSTATS_PROPS(hdl)->threaded_poll);
925 AvahiServiceBrowser *sb = avahi_service_browser_new(HDL_CLUSTERSTATS_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 {
932 AvahiEntryGroup *group = avahi_entry_group_new(HDL_CLUSTERSTATS_PROPS(hdl)->client, entry_group_callback, NULL);
934 if (group == NULL) {
935 TRACE("avahi", "For whatever reason the group is NULL\n");
936 } else {
937 avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0,
938 best_tender.name, "_offer._tcp", NULL, NULL, 651, txt, NULL, NULL);
939 avahi_entry_group_commit(group);
940 avahi_threaded_poll_unlock(HDL_CLUSTERSTATS_PROPS(hdl)->threaded_poll);
942 sleep(3); /* we are for quick bidding ;) */
944 avahi_threaded_poll_lock(HDL_CLUSTERSTATS_PROPS(hdl)->threaded_poll);
945 avahi_entry_group_free(group);
948 avahi_service_browser_free(sb);
949 avahi_threaded_poll_unlock(HDL_CLUSTERSTATS_PROPS(hdl)->threaded_poll);
951 if (best_tender.dom == NULL) {
952 TRACE("virt", "Nobody wants %s, poor vm!\n", best_tender.name);
953 cherokee_buffer_add_long10(buf, -1);
954 } else {
955 if (virDomainUndefine(dom) != 0) {
956 TRACE("virt", "Can't undefine %s, some idiot run it already?\n", best_tender.name);
957 cherokee_buffer_add_long10(buf, -1);
958 } else {
959 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
960 virConnectClose(virConn);
961 cherokee_buffer_add_va (&uri, "xen://%s/", best_tender.dom);
962 virConn = virConnectOpen (uri.buf);
963 if (virConn == NULL) {
964 TRACE("virt", "Can't connect to %s\n", uri.buf);
965 cherokee_buffer_add_long10(buf, -1);
966 } else {
967 cherokee_buffer_add_long10(buf,
968 (virDomainCreateLinux(virConn, xmlDesc.buf, 0) != NULL ?
969 0 : -1 ));
971 cherokee_buffer_mrproper(&uri);
973 TRACE("virt", "going to free\n");
974 free(best_tender.dom);
979 cherokee_buffer_mrproper(&xmlDesc);
980 break;
983 case domainDestroy: {
984 cherokee_buffer_add_long10(buf, virDomainDestroy (dom));
985 break;
988 case domainReboot: {
989 cherokee_buffer_add_long10(buf, virDomainReboot (dom, 0));
990 break;
993 case domainShutdown: {
994 cherokee_buffer_add_long10(buf, virDomainShutdown (dom));
995 break;
999 case domainGetOSType: {
1000 char *ostype = virDomainGetOSType(dom);
1001 cherokee_buffer_add(buf, ostype, strlen(ostype));
1002 free(ostype);
1003 break;
1010 if (hdl->action == domainUndefine) {
1011 /* Remove VM data from filesystem */
1012 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
1013 cherokee_buffer_t path2 = CHEROKEE_BUF_INIT;
1014 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/index.xml", hdl->user.buf, hdl->vm.buf);
1015 cherokee_buffer_add_buffer (&path2, &path);
1016 cherokee_buffer_add_str (&path2, ".deleted");
1017 // unlink(path.buf); /* TODO: instead of delet replace */
1018 rename(path.buf, path2.buf);
1019 cherokee_buffer_mrproper(&path);
1020 cherokee_buffer_mrproper(&path2);
1021 } else if (hdl->action != domainCreate) {
1022 save_xml(hdl, dom, NULL);
1025 return ret_ok;
1028 static virStoragePoolPtr
1029 virt_get_pool_by_args(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
1030 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1031 virStoragePoolPtr pool;
1032 void *temp;
1033 ret_t ret;
1035 ret = cherokee_avl_get_ptr (conn->arguments, "pool", &temp);
1036 if (unlikely(ret < ret_ok)) {
1037 TRACE("virt", "virStoragePoolPtr; Pool argument not specified");
1038 conn->error_code = http_bad_request;
1039 return NULL;
1042 pool = virStoragePoolLookupByName(virConn, temp);
1044 return pool;
1047 static virStorageVolPtr
1048 virt_get_vol_by_args(cherokee_handler_virt_t *hdl, virConnectPtr virConn, unsigned short int prefix) {
1049 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1050 virStoragePoolPtr pool;
1051 virStorageVolPtr volume;
1052 void *temp;
1053 ret_t ret;
1055 pool = virt_get_pool_by_args(hdl, virConn);
1057 if (pool == NULL) {
1058 conn->error_code = http_not_found;
1059 return NULL;
1062 virStoragePoolRefresh(pool, 0); /* TODO: might be better to do it outside */
1064 ret = cherokee_avl_get_ptr (conn->arguments, "volume", &temp);
1065 if (unlikely(ret < ret_ok)) {
1066 TRACE("virt", "virStorageVolPtr; Volume argument not specified");
1067 conn->error_code = http_bad_request;
1068 return NULL;
1071 if (prefix == 1) {
1072 cherokee_buffer_t fullvol = CHEROKEE_BUF_INIT;
1073 cherokee_buffer_add_va (&fullvol, "%s_%s", hdl->user.buf, temp);
1074 volume = virStorageVolLookupByName(pool, fullvol.buf);
1075 cherokee_buffer_mrproper(&fullvol);
1076 } else {
1077 volume = virStorageVolLookupByName(pool, temp);
1080 if (volume == NULL)
1081 conn->error_code = http_not_found;
1083 virStoragePoolFree(pool);
1085 return volume;
1088 /* This function is the home for all functions that need a working
1089 * pool/volume combination */
1090 static ret_t
1091 virt_pool_vol(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
1092 cherokee_buffer_t *buf = &HDL_VIRT(hdl)->buffer;
1093 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1094 virStorageVolPtr volume;
1095 ret_t ret = ret_ok;
1097 /* We only allow clone to run 'unsafe', others get prefixed */
1098 volume = virt_get_vol_by_args(hdl, virConn, (hdl->action != storageVolCloneXML_args));
1100 /* If the volume doesn't exist, no point to continue */
1101 if (volume == NULL) {
1102 conn->error_code = http_not_found;
1103 return ret_error;
1106 switch (hdl->action) {
1107 /* Sets the password of a specific volume, requires the password= option */
1108 case storageVolSetPassword_args: {
1109 void *temp;
1110 ret = cherokee_avl_get_ptr (conn->arguments, "password", &temp);
1111 if (unlikely(ret < ret_ok)) {
1112 TRACE("virt", "storageVolSetPassword_args; password argument not specified");
1113 conn->error_code = http_bad_request;
1114 ret = ret_error;
1115 goto virt_pool_vol_cleanup;
1116 } else {
1117 cherokee_buffer_t cmd_passwd = CHEROKEE_BUF_INIT;
1118 cherokee_buffer_add_va (&cmd_passwd, "/usr/sbin/passwdchanger.sh %s %s\n", virStorageVolGetKey(volume), temp);
1119 cherokee_buffer_add_long10(buf, system(cmd_passwd.buf));
1120 cherokee_buffer_mrproper(&cmd_passwd);
1122 break;
1125 /* Removes a volume */
1126 case storageVolDelete_args: {
1127 cherokee_buffer_add_long10(buf, virStorageVolDelete(volume, 0));
1128 break;
1131 /* Gives a description of a storage volume in XML */
1132 case storageVolGetXMLDesc_args: {
1133 char *xml = virStorageVolGetXMLDesc(volume, 0);
1134 cherokee_buffer_add(buf, xml, strlen(xml));
1135 free(xml);
1136 break;
1139 /* Clones a volume, insecure method! requires a new name= */
1140 case storageVolCloneXML_args: {
1141 void *name;
1142 cherokee_buffer_t busy = CHEROKEE_BUF_INIT;
1144 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
1145 if (unlikely(ret < ret_ok)) {
1146 TRACE("virt", "storageVolCloneXML_args; name argument not specified");
1147 conn->error_code = http_bad_request;
1148 ret = ret_error;
1149 goto virt_pool_vol_cleanup;
1152 cherokee_buffer_add_va (&busy, "/mnt/images/queue/%s_%s.queued", hdl->user.buf, name);
1154 if (unlikely(symlink(virStorageVolGetKey(volume), busy.buf) == 1)) {
1155 conn->error_code = http_internal_error;
1156 ret = ret_error;
1157 } else {
1158 cherokee_buffer_add_str(buf, "QUEUED");
1161 cherokee_buffer_mrproper(&busy);
1163 break;
1168 virt_pool_vol_cleanup:
1169 /* And in the end we need to free the volume we have used */
1170 virStorageVolFree(volume);
1172 return ret;
1175 static ret_t
1176 virt_virt_new(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
1177 cherokee_buffer_t *buf = &HDL_VIRT(hdl)->buffer;
1178 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1179 cherokee_buffer_t xml = CHEROKEE_BUF_INIT;
1180 virStoragePoolPtr pool = NULL;
1181 ret_t ret = ret_ok;
1183 switch (hdl->action) {
1185 case storageVolCreateXML_args: {
1186 void *temp, *type, *name, *unit;
1187 unsigned long int capacity, allocation;
1189 ret = cherokee_avl_get_ptr (conn->arguments, "pool", &temp);
1190 if (unlikely(ret < ret_ok)) {
1191 TRACE("virt", "storageVolCreateXML_args; pool argument not specified");
1192 conn->error_code = http_bad_request;
1193 goto virt_virt_new_cleanup;
1196 TRACE("args", "%s", temp);
1198 pool = virStoragePoolLookupByName(virConn, temp);
1200 if (pool == NULL) {
1201 conn->error_code = http_not_found;
1202 goto virt_virt_new_cleanup;
1205 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
1206 if (unlikely(ret < ret_ok)) {
1207 TRACE("virt", "storageVolCreateXML_args; name argument not specified");
1208 conn->error_code = http_bad_request;
1209 goto virt_virt_new_cleanup;
1212 ret = cherokee_avl_get_ptr (conn->arguments, "type", &type);
1213 if (unlikely(ret < ret_ok)) {
1214 TRACE("virt", "storageVolCreateXML_args; type argument not specified");
1215 conn->error_code = http_bad_request;
1216 goto virt_virt_new_cleanup;
1219 ret = cherokee_avl_get_ptr (conn->arguments, "unit", &unit);
1220 if (unlikely(ret < ret_ok)) {
1221 TRACE("virt", "storageVolCreateXML_args; unit argument not specified");
1222 conn->error_code = http_bad_request;
1223 goto virt_virt_new_cleanup;
1226 ret = cherokee_avl_get_ptr (conn->arguments, "allocation", &temp);
1227 if (unlikely(ret < ret_ok)) {
1228 TRACE("virt", "storageVolCreateXML_args; allocation argument not specified");
1229 conn->error_code = http_bad_request;
1230 goto virt_virt_new_cleanup;
1233 allocation = strtoul(temp, NULL, 10);
1234 if (errno == ERANGE) {
1235 TRACE("virt", "storageVolCreateXML_args; allocation is not a number");
1236 conn->error_code = http_bad_request;
1237 ret = ret_error;
1238 goto virt_virt_new_cleanup;
1241 ret = cherokee_avl_get_ptr (conn->arguments, "capacity", &temp);
1242 if (unlikely(ret < ret_ok)) {
1243 TRACE("virt", "storageVolCreateXML_args; capacity argument not specified");
1244 conn->error_code = http_bad_request;
1245 goto virt_virt_new_cleanup;
1248 capacity = strtoul(temp, NULL, 10);
1249 if (errno == ERANGE || capacity == 0) {
1250 TRACE("virt", "storageVolCreateXML_args; capacity is not a number");
1251 conn->error_code = http_bad_request;
1252 ret = ret_error;
1253 goto virt_virt_new_cleanup;
1256 cherokee_buffer_add_va (&xml, VIRT_STORAGE_XML, type, hdl->user.buf, name, allocation, unit, capacity, hdl->user.buf, name, hdl->user.buf, name);
1257 break;
1260 case domainDefineXML_args: {
1261 ret_t ret;
1262 unsigned int i;
1263 unsigned long vcpu = 0, interface = 0, memory = 0;
1265 void *temp = NULL;
1267 ret = cherokee_avl_get_ptr (conn->arguments, "vcpu", &temp);
1268 if (ret == ret_ok)
1269 vcpu = strtoul(temp, (char **) NULL, 10);
1271 if (ret != ret_ok || errno == ERANGE || vcpu == 0) {
1272 conn->error_code = http_internal_error;
1273 goto virt_virt_new_cleanup;
1277 ret = cherokee_avl_get_ptr (conn->arguments, "memory", &temp);
1278 if (ret == ret_ok)
1279 memory = strtoul(temp, (char **) NULL, 10);
1281 if (ret != ret_ok || errno == ERANGE || memory == 0) {
1282 conn->error_code = http_internal_error;
1283 goto virt_virt_new_cleanup;
1287 ret = cherokee_avl_get_ptr (conn->arguments, "interface", &temp);
1288 if (ret == ret_ok)
1289 interface = strtoul(temp, (char **) NULL, 10);
1291 if (ret != ret_ok || errno == ERANGE) {
1292 conn->error_code = http_internal_error;
1293 goto virt_virt_new_cleanup;
1296 cherokee_buffer_t cmdline_extra = CHEROKEE_BUF_INIT;
1297 cherokee_buffer_t xml_interfaces = CHEROKEE_BUF_INIT;
1298 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
1299 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
1301 for (i = 0; i < interface; i++) {
1302 char *mac = NULL;
1303 char *ip = NULL;
1304 if (getNewMac("dv28", domu.buf, &mac, &ip) == 0) {
1305 cherokee_buffer_add_va (&xml_interfaces, VIRT_INTERFACE_XML, mac, ip);
1306 if (i == 0) {
1307 /* TODO: terrible hack */
1308 char gateway[16];
1309 char *temp;
1310 strcpy(gateway, ip);
1311 temp = strchr(gateway, '.');
1312 temp = strchr(++temp, '.');
1313 temp = strchr(++temp, '.');
1314 strcpy(++temp, "254");
1315 cherokee_buffer_add_va (&cmdline_extra, VIRT_DOMAIN_CMD_IP, ip, gateway);
1320 cherokee_buffer_mrproper(&domu);
1322 cherokee_buffer_add_va (&xml, VIRT_DOMAIN_XML,
1323 hdl->user.buf, hdl->vm.buf, cmdline_extra.buf, memory, vcpu, xml_interfaces.buf);
1325 cherokee_buffer_mrproper(&xml_interfaces);
1326 cherokee_buffer_mrproper(&cmdline_extra);
1327 break;
1330 case storageVolCreateXML:
1331 case domainDefineXML: {
1332 off_t postl;
1333 cherokee_post_get_len (&conn->post, &postl);
1334 cherokee_post_walk_read (&conn->post, &xml, (cuint_t) postl);
1335 break;
1339 switch (hdl->action) {
1340 case domainDefineXML_args:
1341 case domainDefineXML: {
1342 virDomainPtr result = virDomainDefineXML(virConn, (const char *) xml.buf);
1344 if (result == NULL) {
1345 /* TODO: vrij maken eventuele uitgegeven macs! */
1346 conn->error_code = http_internal_error;
1347 goto virt_virt_new_cleanup;
1350 save_xml(hdl, result, buf);
1352 virDomainFree(result);
1354 break;
1357 case storageVolCreateXML_args:
1358 case storageVolCreateXML: {
1359 virStorageVolPtr vol = virStorageVolCreateXML(pool, xml.buf, 0);
1361 if (vol == NULL) {
1362 cherokee_buffer_add_long10(buf, -1);
1363 goto virt_virt_new_cleanup;
1366 virStorageVolFree(vol);
1368 cherokee_buffer_add_long10(buf, 0);
1369 break;
1373 virt_virt_new_cleanup:
1374 cherokee_buffer_mrproper(&xml);
1376 if (pool)
1377 virStoragePoolFree(pool);
1379 return ret;
1383 static ret_t
1384 virt_virt_do(cherokee_handler_virt_t *hdl, cherokee_buffer_t *domu, cherokee_buffer_t *uri)
1386 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1388 ret_t ret = ret_error;
1389 virConnectPtr virConn = NULL;
1392 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE)
1393 virConn = virConnectOpen (uri->buf);
1395 if (!virConn && !(virConn = virConnectOpenReadOnly (uri->buf))) {
1396 conn->error_code = http_service_unavailable;
1397 return ret_error;
1400 switch (hdl->action) {
1401 case storageVolDelete_args:
1402 case storageVolSetPassword_args:
1403 case storageVolGetXMLDesc_args:
1404 case storageVolCloneXML_args:
1405 ret = virt_pool_vol(hdl, virConn);
1406 break;
1408 case storageVolCreateXML_args:
1409 case storageVolCreateXML:
1410 case domainDefineXML_args:
1411 case domainDefineXML:
1412 ret = virt_virt_new(hdl, virConn);
1413 break;
1415 default: {
1416 virDomainPtr dom;
1417 if ((dom = virDomainLookupByName(virConn, domu->buf)) == NULL) {
1418 conn->error_code = http_not_found;
1419 } else {
1420 ret = virt_virt_function(hdl, dom, virConn);
1421 virDomainFree(dom);
1426 virConnectClose(virConn);
1427 return ret;
1430 static ret_t
1431 virt_while (cherokee_buffer_t *key, void *value, void *param) {
1432 cherokee_handler_virt_t * hdl = param;
1433 // cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
1434 // cherokee_buffer_add_va (&uri, "xen://%s/", ((cherokee_buffer_t *) value)->buf);
1435 // virt_virt_do((cherokee_handler_virt_t *) param, key, &uri);
1436 // cherokee_buffer_mrproper(&uri);
1438 cherokee_buffer_add_va (&hdl->buffer, "<domain><name>%s</name></domain>", key->buf);
1440 return ret_ok;
1443 static ret_t
1444 virt_while_user (cherokee_buffer_t *key, void *value, void *param) {
1445 cherokee_handler_virt_t *hdl = param;
1446 if (key->len > hdl->user.len && key->buf[hdl->user.len] == '_' && strncmp(key->buf, hdl->user.buf, hdl->user.len) == 0)
1447 return virt_while (key, value, param);
1449 return ret_ok;
1452 static ret_t
1453 virt_build_page (cherokee_handler_virt_t *hdl)
1455 ret_t ret;
1456 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1457 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
1458 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
1460 /* We use the webserver methods to parse the querystring */
1461 /* Maybe do something smart with ENUM, begin_args, end_args... */
1462 if ((hdl->action == domainDefineXML_args) ||
1463 (hdl->action == domainAttachDevice_args) ||
1464 (hdl->action == storageVolGetXMLDesc_args) ||
1465 (hdl->action == storageVolDelete_args) ||
1466 (hdl->action == storageVolSetPassword_args) ||
1467 (hdl->action == storageVolCloneXML_args) ||
1468 (hdl->action == storageVolCloneStatus_args) ||
1469 (hdl->action == storageVolCreateXML_args) ||
1470 (hdl->action == graphLoad_args) ||
1471 (hdl->action == graphInterface_args)) {
1472 ret = cherokee_connection_parse_args (conn);
1473 if (unlikely(ret < ret_ok)) {
1474 conn->error_code = http_internal_error;
1475 return ret_error;
1479 switch (hdl->action) {
1480 case graphInterface_args: {
1481 struct stat statbuf;
1482 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
1483 void *interface;
1484 if ((ret = cherokee_avl_get_ptr (conn->arguments, "interface", &interface)) != ret_ok)
1485 interface = "eth0";
1487 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/interface_%s.rrd", hdl->user.buf, hdl->vm.buf, interface);
1488 if (stat(path.buf, &statbuf) != 0) {
1489 conn->error_code = http_not_found;
1490 ret = ret_error;
1491 } else {
1492 void *width, *height, *start, *end;
1493 if ((ret = cherokee_avl_get_ptr (conn->arguments, "width", &width)) != ret_ok)
1494 width = "600";
1496 if ((ret = cherokee_avl_get_ptr (conn->arguments, "height", &height)) != ret_ok)
1497 height = "200";
1499 if ((ret = cherokee_avl_get_ptr (conn->arguments, "start", &start)) != ret_ok)
1500 start = "now-1h";
1502 if ((ret = cherokee_avl_get_ptr (conn->arguments, "end", &end)) != ret_ok)
1503 end = "now";
1505 cherokee_buffer_t def1 = CHEROKEE_BUF_INIT;
1506 cherokee_buffer_t def2 = CHEROKEE_BUF_INIT;
1507 cherokee_buffer_add_va (&def1, "DEF:rxbytes=%s:rxbytes:AVERAGE:step=30", path.buf);
1508 cherokee_buffer_add_va (&def2, "DEF:txbytes=%s:txbytes:AVERAGE:step=30", path.buf);
1509 char **calcpr = NULL;
1510 int xsize, ysize;
1511 double ymin, ymax;
1512 char *filename = mktemp(strdup("handler_virt_XXXXXX"));
1513 char *r_graph[] = { "rrdgraph", filename,
1514 "-a", "PNG",
1515 "-w", width,
1516 "-h", height,
1517 "--start", start,
1518 "--end", end,
1519 "--title", interface,
1520 "--lower-limit", "0", "--alt-autoscale-max", "--vertical-label", "bits per second",
1521 def1.buf,
1522 def2.buf,
1523 "CDEF:txbits=txbytes,8,*",
1524 "CDEF:rxbits=rxbytes,8,*",
1525 "AREA:rxbits#00EE00:rxbits",
1526 "LINE:txbits#0000EE:txbits" };
1528 rrd_graph(25, r_graph, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
1529 if ((ret = cherokee_buffer_read_file(&hdl->buffer, filename)) == ret_error) {
1530 hdl->action = http_internal_error;
1533 unlink(filename);
1534 free(filename);
1536 cherokee_buffer_mrproper(&def1);
1537 cherokee_buffer_mrproper(&def2);
1539 cherokee_buffer_mrproper(&path);
1540 goto virt_build_page_cleanup;
1545 case graphLoad_args: {
1546 struct stat statbuf;
1547 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
1548 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/cpuTime.rrd", hdl->user.buf, hdl->vm.buf);
1549 if (stat(path.buf, &statbuf) != 0) {
1550 conn->error_code = http_not_found;
1551 ret = ret_error;
1552 } else {
1553 void *width, *height, *start, *end;
1554 if ((ret = cherokee_avl_get_ptr (conn->arguments, "width", &width)) != ret_ok)
1555 width = "600";
1557 if ((ret = cherokee_avl_get_ptr (conn->arguments, "height", &height)) != ret_ok)
1558 height = "200";
1560 if ((ret = cherokee_avl_get_ptr (conn->arguments, "start", &start)) != ret_ok)
1561 start = "now-1h";
1563 if ((ret = cherokee_avl_get_ptr (conn->arguments, "end", &end)) != ret_ok)
1564 end = "now";
1566 /* TODO: wat error checking? */
1568 cherokee_buffer_t def = CHEROKEE_BUF_INIT;
1569 cherokee_buffer_add_va (&def, "DEF:cputime=%s:cpuTime:AVERAGE:step=30", path.buf);
1570 char **calcpr = NULL;
1571 int xsize, ysize;
1572 double ymin, ymax;
1573 char *filename = mktemp(strdup("handler_virt_XXXXXX"));
1574 char *r_graph[] = { "rrdgraph",
1575 filename,
1576 "-a", "PNG",
1577 "-w", width,
1578 "-h", height,
1579 "--start", start,
1580 "--end", end,
1581 "--title", hdl->vm.buf,
1582 "--lower-limit", "0", "--alt-autoscale-max", "--vertical-label", "(used / total) time",
1583 def.buf,
1584 "CDEF:cpuload=cputime,1000000000,/",
1585 "LINE:cpuload#EE0000:cpuLoad" };
1587 rrd_graph(22, r_graph, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
1588 if ((ret = cherokee_buffer_read_file(&hdl->buffer, filename)) == ret_error) {
1589 hdl->action = http_internal_error;
1592 unlink(filename);
1593 free(filename);
1595 cherokee_buffer_mrproper(&def);
1597 cherokee_buffer_mrproper(&path);
1598 goto virt_build_page_cleanup;
1602 if (hdl->action > xml && HDL_VIRT_PROPS(hdl)->xsl.len > 0)
1603 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);
1606 switch (hdl->action) {
1607 case showall: {
1608 size_t len;
1609 if (cherokee_avl_r_len(&HDL_CLUSTERSTATS_PROPS(hdl)->entries, &len) == ret_ok && len > 0) {
1610 cherokee_buffer_add_str (&hdl->buffer, "<domains>\n");
1611 cherokee_avl_r_while (&HDL_CLUSTERSTATS_PROPS(hdl)->entries, (cherokee_avl_while_func_t) cherokee_handler_clusterstats_while_func_entries, (void *) &hdl->buffer, NULL, NULL);
1612 cherokee_buffer_add_str (&hdl->buffer, "</domains>");
1613 } else {
1614 cherokee_buffer_add_str (&hdl->buffer, "<domains/>");
1617 ret = ret_ok;
1618 break;
1621 case showuservms: {
1622 cherokee_buffer_add_str (&hdl->buffer, "<domains>\n");
1623 get_all_configurations(&HDL_CLUSTERSTATS_PROPS(hdl)->entries, &hdl->user, &hdl->buffer);
1624 cherokee_buffer_add_str (&hdl->buffer, "</domains>");
1625 ret = ret_ok;
1626 break;
1629 default: {
1630 AvahiStringList *list = NULL;
1631 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
1633 ret = cherokee_avl_r_get(&HDL_CLUSTERSTATS_PROPS(hdl)->entries, &domu, (void **) &list);
1635 if (ret == ret_not_found) {
1636 virDomainPtr virDom;
1637 virConnectPtr virConn;
1638 if (HDL_VIRT_PROPS(hdl)->virt.len > 0)
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 char *hostname;
1694 avahi_string_list_get_pair(avahi_string_list_find(list, "dom0"), NULL, &hostname, NULL);
1695 cherokee_buffer_add_va (&uri, "xen://%s/", hostname);
1696 printf("%s\n", uri.buf);
1697 avahi_free(hostname);
1698 } else {
1699 hdl->action = http_internal_error;
1700 hdl->action = nothing;
1701 ret = ret_error;
1707 if (ret == ret_ok && hdl->action != showall && hdl->action != showuservms) {
1708 ret = virt_virt_do(hdl, &domu, &uri);
1711 virt_build_page_cleanup:
1712 cherokee_buffer_mrproper(&domu);
1713 cherokee_buffer_mrproper(&uri);
1715 return ret;
1719 ret_t
1720 cherokee_handler_virt_new (cherokee_handler_t **hdl, cherokee_connection_t *cnt, cherokee_module_props_t *props)
1722 ret_t ret;
1723 CHEROKEE_NEW_STRUCT (n, handler_virt);
1725 /* Init the base class
1728 cherokee_handler_init_base(HANDLER(n), cnt, HANDLER_PROPS(props), PLUGIN_INFO_HANDLER_PTR(virt));
1730 MODULE(n)->init = (handler_func_init_t) cherokee_handler_virt_init;
1731 MODULE(n)->free = (module_func_free_t) cherokee_handler_virt_free;
1732 HANDLER(n)->step = (handler_func_step_t) cherokee_handler_virt_step;
1733 HANDLER(n)->add_headers = (handler_func_add_headers_t) cherokee_handler_virt_add_headers;
1735 HANDLER(n)->support = hsupport_length | hsupport_range;
1737 ret = cherokee_buffer_init (&n->buffer);
1738 if (unlikely(ret != ret_ok))
1739 return ret;
1741 ret = cherokee_buffer_ensure_size (&n->buffer, 4*1024);
1742 if (unlikely(ret != ret_ok))
1743 return ret;
1745 *hdl = HANDLER(n);
1747 return ret_ok;