More fun with snapshots.
[handlervirt.git] / handler_virt.c
blobec3e2d44fff29933f4180fe44450c4f44c7966c1
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;
430 else if (strcmp(this+12, "XMLSnapshots") == 0) hdl->action = domainGetXMLSnapshots;
433 else if (HDL_VIRT_PROPS(hdl)->read_only == FALSE) {
434 if (strcmp(this+9, "Create") == 0) hdl->action = domainCreate;
435 else if (strcmp(this+9, "Destroy") == 0) hdl->action = domainDestroy;
436 else if (strcmp(this+9, "Reboot") == 0) hdl->action = domainReboot;
437 else if (strcmp(this+9, "Shutdown") == 0) hdl->action = domainShutdown;
439 else if (strcmp(this+9, "Save") == 0) hdl->action = domainSave;
440 else if (strcmp(this+9, "Restore") == 0) hdl->action = domainRestore;
442 else if (strcmp(this+9, "AttachDevice") == 0) hdl->action = domainAttachDevice_args;
444 else if (strcmp(this+9, "DefineXML") == 0) hdl->action = domainDefineXML_args;
445 else if (strcmp(this+9, "Undefine") == 0) hdl->action = domainUndefine;
448 else if (HDL_VIRT_PROPS(hdl)->read_only == FALSE && strncmp(this, "virStorage", 10) == 0) {
449 if (strncmp(this+10, "Vol", 3) == 0) {
450 if (strcmp(this+13, "CreateXML") == 0) hdl->action = storageVolCreateXML_args;
451 else if (strcmp(this+13, "Delete") == 0) hdl->action = storageVolDelete_args;
452 else if (strcmp(this+13, "CloneXML") == 0) hdl->action = storageVolCloneXML_args;
453 else if (strcmp(this+13, "CloneStatus") == 0) hdl->action = storageVolCloneStatus_args;
454 else if (strcmp(this+13, "GetXMLDesc") == 0) hdl->action = storageVolGetXMLDesc_args;
455 else if (strcmp(this+13, "SetPassword") == 0) hdl->action = storageVolSetPassword_args;
458 else if (strncmp(this, "virGraph", 8) == 0) {
459 if (strcmp(this+8, "Load") == 0) hdl->action = graphLoad_args;
460 if (strcmp(this+8, "Interface") == 0) hdl->action = graphInterface_args;
462 break;
464 case http_post: {
465 off_t postl;
466 cherokee_post_get_len (&conn->post, &postl);
468 if (postl <= 0 || postl >= (INT_MAX-1)) {
469 TRACE("virt", "post without post");
470 conn->error_code = http_bad_request;
471 return ret_error;
474 if (strncmp(this, "virDomain", 9) == 0) {
475 if (strcmp(this+9, "AttachDevice") == 0) hdl->action = domainAttachDevice;
476 else if (strcmp(this+9, "DetachDevice") == 0) hdl->action = domainDetachDevice;
477 else if (strcmp(this+9, "DefineXML") == 0) hdl->action = domainDefineXML;
479 else if (strncmp(this, "virStorage", 10) == 0) {
480 if (strncmp(this+10, "Vol", 3) == 0) {
481 if (strcmp(this+13, "CreateXML") == 0) hdl->action = storageVolCreateXML;
485 break;
488 default:
489 return ret_error;
492 if (hdl->action <= 0) {
493 TRACE("virt", "There was no action specified");
494 conn->error_code = http_bad_request;
495 return ret_error;
498 return virt_build_page(hdl);
501 ret_t
502 cherokee_handler_virt_free (cherokee_handler_virt_t *hdl)
504 cherokee_buffer_mrproper (&hdl->buffer);
505 cherokee_buffer_mrproper (&hdl->user);
506 cherokee_buffer_mrproper (&hdl->vm);
508 return ret_ok;
511 ret_t
512 cherokee_handler_virt_step (cherokee_handler_virt_t *hdl, cherokee_buffer_t *buffer)
514 cuint_t tosend;
516 if (cherokee_buffer_is_empty (&hdl->buffer))
517 return ret_eof;
519 tosend = (hdl->buffer.len > 1024 ? 1024 : hdl->buffer.len);
521 cherokee_buffer_add (buffer, hdl->buffer.buf, tosend);
522 cherokee_buffer_move_to_begin (&hdl->buffer, tosend);
524 if (cherokee_buffer_is_empty (&hdl->buffer))
525 return ret_eof_have_data;
527 return ret_ok;
530 ret_t
531 cherokee_handler_virt_add_headers (cherokee_handler_virt_t *hdl, cherokee_buffer_t *buffer)
533 cherokee_buffer_add_va (buffer, "Content-Length: %d"CRLF, hdl->buffer.len);
535 if (hdl->action > xml)
536 cherokee_buffer_add_str (buffer, "Content-Type: application/xml"CRLF);
537 else if (hdl->action > graph) {
538 cherokee_buffer_add_str (buffer, "Content-Type: image/png"CRLF);
539 } else
540 cherokee_buffer_add_str (buffer, "Content-Type: text/plain"CRLF);
542 return ret_ok;
546 static ret_t
547 while_func_entries (cherokee_buffer_t *key, void *value, void *param) {
548 virConnectPtr conn = NULL;
549 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
550 cherokee_buffer_t *buf = (cherokee_buffer_t *)param;
552 cherokee_buffer_add_va (&uri, "xen://%s/", value);
554 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE && !(conn = virConnectOpen (uri.buf))) {
555 if (!(conn = virConnectOpen (uri.buf))) {
556 return ret_error;
560 if (!conn && !(conn = virConnectOpenReadOnly (uri.buf))) {
561 return ret_error;
562 } else {
563 virDomainPtr dom;
564 if (!(dom = virDomainLookupByName(conn, (const char *) key->buf))) {
565 return ret_error;
566 } else {
567 char *xml = virDomainGetXMLDesc(dom, 0);
568 cherokee_buffer_add(buf, xml, strlen(xml));
570 virConnectClose(conn);
573 cherokee_buffer_mrproper(&uri);
575 return ret_ok;
578 static ret_t load_xml(cherokee_handler_virt_t *hdl, cherokee_buffer_t *xmlDesc) {
579 ret_t ret = ret_error;
580 FILE *fd;
581 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
582 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s", hdl->user.buf);
584 mkdir(path.buf, 0755);
585 cherokee_buffer_add_va (&path, "/%s", hdl->vm.buf);
587 mkdir(path.buf, 0755);
588 cherokee_buffer_add_str (&path, "/index.xml");
590 if ((fd = fopen(path.buf, "r")) != NULL) {
591 while (!feof(fd)) {
592 char buf[1024];
593 size_t amount = fread(buf, sizeof(char), 1024, fd);
595 cherokee_buffer_add (xmlDesc, buf, amount);
597 fclose(fd);
598 if (xmlDesc->len > 0)
599 ret = ret_ok;
602 cherokee_buffer_mrproper(&path);
603 return ret;
606 static ret_t save_xml(cherokee_handler_virt_t *hdl, virDomainPtr result, cherokee_buffer_t *buf) {
607 ret_t ret = ret_error;
608 FILE *fd;
609 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
610 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s", hdl->user.buf);
612 mkdir(path.buf, 0755);
613 cherokee_buffer_add_va (&path, "/%s", hdl->vm.buf);
615 mkdir(path.buf, 0755);
616 cherokee_buffer_add_str (&path, "/index.xml");
618 if ((fd = fopen(path.buf, "w")) == NULL) {
619 perror(__func__);
620 } else {
621 char *output = virDomainGetXMLDesc(result, VIR_DOMAIN_XML_INACTIVE);
622 if (output) {
623 fwrite(output, strlen(output), sizeof(char), fd);
624 if (buf)
625 cherokee_buffer_add(buf, output, strlen(output));
626 free(output);
628 fclose(fd);
629 ret = ret_ok;
631 cherokee_buffer_mrproper(&path);
632 return ret;
636 static ret_t
637 virt_virt_function(cherokee_handler_virt_t *hdl, virDomainPtr dom, virConnectPtr virConn) {
638 cherokee_connection_t *conn = HANDLER_CONN(hdl);
639 cherokee_buffer_t *buf = &HDL_VIRT(hdl)->buffer;
641 switch (hdl->action) {
642 /* Returns the status of a clone copy */
643 case storageVolCloneStatus_args: {
644 ret_t ret;
645 void *name;
646 struct stat statbuf;
647 cherokee_buffer_t queue = CHEROKEE_BUF_INIT;
649 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
650 if (unlikely(ret < ret_ok)) {
651 TRACE("virt", "storageVolCloneXML_args; name argument not specified");
652 conn->error_code = http_bad_request;
653 return ret_error;
656 cherokee_buffer_add_va (&queue, "/mnt/images/queue/%s_%s.queued", hdl->user.buf, name);
658 if (stat(queue.buf, &statbuf) != 0) {
659 conn->error_code = http_not_found;
660 ret = ret_error;
661 } else {
662 char hardlink[1024];
663 int namelen;
664 if ((namelen = readlink(queue.buf, hardlink, 1023)) == -1) {
665 conn->error_code = http_internal_error;
666 ret = ret_error;
667 } else {
668 hardlink[namelen] = '\0';
669 if (stat(hardlink, &statbuf) != 0) {
670 conn->error_code = http_internal_error;
671 ret = ret_error;
672 } else {
673 struct stat statbuf2;
674 cherokee_buffer_t busy = CHEROKEE_BUF_INIT;
675 cherokee_buffer_add_va (&busy, "/mnt/images/queue/%s_%s.busy", hdl->user.buf, name);
676 if (stat(busy.buf, &statbuf2) != 0) {
677 conn->error_code = http_internal_error;
678 ret = ret_error;
679 } else {
680 cherokee_buffer_add_va (buf, "%f", (float)((float)statbuf2.st_size / (float)statbuf.st_size));
681 ret = ret_ok;
683 cherokee_buffer_mrproper(&busy);
688 cherokee_buffer_mrproper(&queue);
689 return ret;
690 break;
693 /* Save the memory of a domain to a file and suspend the domain */
694 case domainSave: {
695 ret_t ret = ret_ok;
696 int result;
697 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
698 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/memory", hdl->user.buf, hdl->vm.buf);
699 if ((result = virDomainSave(dom, path.buf)) != 0) {
700 TRACE("virt", "Saving of %s/%s failed", hdl->user.buf, hdl->vm.buf);
701 conn->error_code = http_internal_error;
702 ret = ret_error;
704 cherokee_buffer_mrproper(&path);
706 cherokee_buffer_add_long10(buf, result);
707 return ret;
708 break;
711 /* Restore the memory of a domain from a file and resume the domain */
712 case domainRestore: {
713 ret_t ret = ret_ok;
714 struct stat statbuf;
715 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
716 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/memory", hdl->user.buf, hdl->vm.buf);
718 if (stat(path.buf, &statbuf) == 0) {
719 int result;
720 if ((result = virDomainRestore(virConn, path.buf)) != 0) {
721 TRACE("virt", "Restoring of %s/%s failed", hdl->user.buf, hdl->vm.buf);
722 conn->error_code = http_internal_error;
723 ret = ret_error;
725 cherokee_buffer_add_long10(buf, result);
726 } else {
727 TRACE("virt", "Memory file for %s/%s does not exist", hdl->user.buf, hdl->vm.buf);
728 conn->error_code = http_not_found;
729 ret = ret_error;
732 cherokee_buffer_mrproper(&path);
733 return ret;
734 break;
737 case domainUndefine: {
738 int result;
739 if ((result = virDomainUndefine(dom)) != 0) {
740 conn->error_code = http_internal_error; /* TODO */
743 cherokee_buffer_add_long10(buf, result);
744 break;
747 case domainAttachDevice_args: {
748 int result;
749 void *temp;
750 ret_t ret;
752 ret = cherokee_avl_get_ptr (conn->arguments, "type", &temp);
754 if (ret == ret_ok) {
755 cherokee_buffer_t xml = CHEROKEE_BUF_INIT;
757 if (strcmp(temp, "disk") == 0) {
758 void *device;
759 if ((ret = cherokee_avl_get_ptr (conn->arguments, "device", &device)) == ret_ok) {
760 void *file;
761 if ((ret = cherokee_avl_get_ptr (conn->arguments, "file", &file)) == ret_ok) {
762 cherokee_buffer_add_va (&xml, VIRT_DISK_XML, file, device);
763 } else {
764 virStorageVolPtr volume = virt_get_vol_by_args(hdl, virConn, 1);
766 if (volume == NULL) {
767 return ret_error;
770 file = virStorageVolGetPath(volume);
771 cherokee_buffer_add_va (&xml, VIRT_DISK_XML, file, device);
772 free(file);
774 virStorageVolFree(volume);
775 ret = ret_ok;
780 else if (strcmp(temp, "interface") == 0) {
781 void *mac, *ip;
782 if ((ret = cherokee_avl_get_ptr (conn->arguments, "mac", &mac)) == ret_ok && (ret = cherokee_avl_get_ptr (conn->arguments, "ip", &ip)) == ret_ok) {
783 cherokee_buffer_add_va (&xml, VIRT_INTERFACE_XML, mac, ip);
784 } else {
785 char *mac = NULL;
786 char *ip = NULL;
787 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
788 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
789 if (getNewMac("dv28", domu.buf, &mac, &ip) == 0)
790 cherokee_buffer_add_va (&xml, VIRT_INTERFACE_XML, mac, ip);
791 cherokee_buffer_mrproper(&domu);
795 if (ret == ret_ok && (result = virDomainAttachDevice(dom, (const char *) xml.buf)) == 0) {
796 ret = ret_ok;
797 } else {
798 conn->error_code = http_internal_error;
799 return ret_error;
802 cherokee_buffer_add_long10(buf, result);
803 cherokee_buffer_mrproper(&xml);
804 } else {
805 TRACE("virt", "DeviceAttach_args; type was not specified");
806 conn->error_code = http_bad_request;
807 return ret_error;
810 break;
813 case domainAttachDevice: {
814 off_t postl;
815 int result;
816 cherokee_buffer_t post = CHEROKEE_BUF_INIT;
817 cherokee_post_get_len (&conn->post, &postl);
818 cherokee_post_walk_read (&conn->post, &post, (cuint_t) postl);
819 if ((result = virDomainAttachDevice(dom, (const char *) post.buf)) != 0)
820 conn->error_code = http_internal_error;
822 cherokee_buffer_mrproper(&post);
823 cherokee_buffer_add_long10(buf, result);
824 break;
828 case domainGetXMLDesc: {
829 char *xml = virDomainGetXMLDesc(dom, 0);
830 cherokee_buffer_add(buf, xml, strlen(xml));
831 free(xml);
832 break;
835 case domainGetXMLSnapshots: {
836 /* TODO: maybe in the future one would like to have all snapshots here */
837 struct stat statbuf;
838 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
840 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/memory", hdl->user.buf, hdl->vm.buf);
842 if (stat(path.buf, &statbuf) != 0) {
843 cherokee_buffer_add_str(buf, "<snapshots />");
844 } else {
845 char *date = ctime(&(statbuf.st_ctime));
846 cherokee_buffer_add_str(buf, "<snapshots>\n");
847 cherokee_buffer_add_va (buf, " <snapshot at=\"");
848 cherokee_buffer_add (buf, date, strlen(date)-1);
849 cherokee_buffer_add_va (buf, "\" />\n");
850 cherokee_buffer_add_str(buf, "</snapshots>");
853 cherokee_buffer_mrproper(&path);
854 break;
857 case domainDetachDevice: {
858 off_t postl;
859 int result;
860 ret_t ret;
861 cherokee_buffer_t post = CHEROKEE_BUF_INIT;
862 cherokee_post_get_len (&conn->post, &postl);
863 cherokee_post_walk_read (&conn->post, &post, (cuint_t) postl);
865 xmlDocPtr doc = xmlParseMemory((const char *) post.buf, post.len);
866 if (doc == NULL) {
867 TRACE("virt", "DeviceAttach; XML document unparceble");
868 conn->error_code = http_bad_request;
869 ret = ret_error;
870 } else
871 ret = ret_ok;
873 if (ret == ret_ok) {
874 if ((result = virDomainDetachDevice(dom, (const char *) post.buf)) != 0) {
875 conn->error_code = http_internal_error;
876 /* TODO: betere afhandeling */
878 xmlXPathContextPtr context = xmlXPathNewContext(doc);
879 if (context != NULL) {
880 xmlXPathObjectPtr obj = xmlXPathEval("string(//interface/mac/@address)", context);
881 xmlXPathFreeContext(context);
882 if ((obj != NULL) && (obj->type == XPATH_STRING) &&
883 (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
884 removeOldMac("dv28", (char *) obj->stringval);
886 xmlXPathFreeObject(obj);
889 xmlFreeDoc(doc);
890 xmlCleanupParser();
893 cherokee_buffer_mrproper(&post);
894 cherokee_buffer_add_long10(buf, result);
895 break;
898 case domainGetID: {
899 cherokee_buffer_add_ulong10(buf, virDomainGetID(dom));
900 break;
902 case domainGetMaxMemory: {
903 cherokee_buffer_add_ulong10(buf, virDomainGetMaxMemory (dom));
904 break;
906 case domainGetMaxVcpus: {
907 cherokee_buffer_add_long10(buf, virDomainGetMaxVcpus (dom));
908 break;
910 case domainGetName: {
911 const char *name = virDomainGetName (dom);
912 cherokee_buffer_add(buf, name, strlen(name));
913 break;
915 case domainGetUUID: {
916 unsigned char uuid[VIR_UUID_BUFLEN];
917 if (virDomainGetUUID(dom, uuid) == 0) {
918 cherokee_buffer_add_str(buf, uuid);
919 } else {
920 conn->error_code = http_internal_error;
921 return ret_error;
923 break;
925 case domainGetUUIDString: {
926 unsigned char uuid[VIR_UUID_STRING_BUFLEN];
927 if (virDomainGetUUIDString(dom, uuid) == 0) {
928 cherokee_buffer_add_str(buf, uuid);
929 } else {
930 conn->error_code = http_internal_error;
931 return ret_error;
933 break;
936 case domainCreate: {
937 cherokee_buffer_t xmlDesc = CHEROKEE_BUF_INIT;
938 char txt[254];
939 save_xml(hdl, dom, &xmlDesc);
940 tender_t best_tender;
941 snprintf(txt, 252, "memory=%lu", virDomainGetMaxMemory(dom));
942 txt[253] = '\0';
944 best_tender.name = virDomainGetName(dom);
945 best_tender.cost = FLT_MAX;
946 best_tender.dom = NULL;
948 avahi_threaded_poll_lock(HDL_CLUSTERSTATS_PROPS(hdl)->threaded_poll);
949 AvahiServiceBrowser *sb = avahi_service_browser_new(HDL_CLUSTERSTATS_PROPS(hdl)->client, AVAHI_IF_UNSPEC,
950 AVAHI_PROTO_UNSPEC, "_tender._tcp",
951 NULL, 0, tender_browse_callback, &best_tender);
953 if (sb == NULL) {
954 TRACE("avahi", "For whatever reason the sb is NULL\n");
955 } else {
956 AvahiEntryGroup *group = avahi_entry_group_new(HDL_CLUSTERSTATS_PROPS(hdl)->client, entry_group_callback, NULL);
958 if (group == NULL) {
959 TRACE("avahi", "For whatever reason the group is NULL\n");
960 } else {
961 avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0,
962 best_tender.name, "_offer._tcp", NULL, NULL, 651, txt, NULL, NULL);
963 avahi_entry_group_commit(group);
964 avahi_threaded_poll_unlock(HDL_CLUSTERSTATS_PROPS(hdl)->threaded_poll);
966 sleep(3); /* we are for quick bidding ;) */
968 avahi_threaded_poll_lock(HDL_CLUSTERSTATS_PROPS(hdl)->threaded_poll);
969 avahi_entry_group_free(group);
972 avahi_service_browser_free(sb);
973 avahi_threaded_poll_unlock(HDL_CLUSTERSTATS_PROPS(hdl)->threaded_poll);
975 if (best_tender.dom == NULL) {
976 TRACE("virt", "Nobody wants %s, poor vm!\n", best_tender.name);
977 cherokee_buffer_add_long10(buf, -1);
978 } else {
979 if (virDomainUndefine(dom) != 0) {
980 TRACE("virt", "Can't undefine %s, some idiot run it already?\n", best_tender.name);
981 cherokee_buffer_add_long10(buf, -1);
982 } else {
983 virConnectPtr virConnNew;
984 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
985 // virConnectClose(virConn);
986 cherokee_buffer_add_va (&uri, "xen://%s/", best_tender.dom);
987 virConnNew = virConnectOpen (uri.buf);
988 if (virConnNew == NULL) {
989 TRACE("virt", "Can't connect to %s\n", uri.buf);
990 cherokee_buffer_add_long10(buf, -1);
991 } else {
992 cherokee_buffer_add_long10(buf,
993 (virDomainCreateLinux(virConnNew, xmlDesc.buf, 0) != NULL ?
994 0 : -1 ));
995 virConnectClose(virConn);
997 virConn = virConnNew;
998 /* TODO: Here I actually want virConnNew to be virConn */
1000 cherokee_buffer_mrproper(&uri);
1002 TRACE("virt", "going to free\n");
1003 free(best_tender.dom);
1008 cherokee_buffer_mrproper(&xmlDesc);
1009 break;
1012 case domainDestroy: {
1013 cherokee_buffer_add_long10(buf, virDomainDestroy (dom));
1014 break;
1017 case domainReboot: {
1018 cherokee_buffer_add_long10(buf, virDomainReboot (dom, 0));
1019 break;
1022 case domainShutdown: {
1023 cherokee_buffer_add_long10(buf, virDomainShutdown (dom));
1024 break;
1028 case domainGetOSType: {
1029 char *ostype = virDomainGetOSType(dom);
1030 cherokee_buffer_add(buf, ostype, strlen(ostype));
1031 free(ostype);
1032 break;
1039 if (hdl->action == domainUndefine) {
1040 /* Remove VM data from filesystem */
1041 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
1042 cherokee_buffer_t path2 = CHEROKEE_BUF_INIT;
1043 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/index.xml", hdl->user.buf, hdl->vm.buf);
1044 cherokee_buffer_add_buffer (&path2, &path);
1045 cherokee_buffer_add_str (&path2, ".deleted");
1046 // unlink(path.buf); /* TODO: instead of delet replace */
1047 rename(path.buf, path2.buf);
1048 cherokee_buffer_mrproper(&path);
1049 cherokee_buffer_mrproper(&path2);
1050 } else if (hdl->action != domainCreate) {
1051 save_xml(hdl, dom, NULL);
1054 return ret_ok;
1057 static virStoragePoolPtr
1058 virt_get_pool_by_args(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
1059 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1060 virStoragePoolPtr pool;
1061 void *temp;
1062 ret_t ret;
1064 ret = cherokee_avl_get_ptr (conn->arguments, "pool", &temp);
1065 if (unlikely(ret < ret_ok)) {
1066 TRACE("virt", "virStoragePoolPtr; Pool argument not specified");
1067 conn->error_code = http_bad_request;
1068 return NULL;
1071 pool = virStoragePoolLookupByName(virConn, temp);
1073 return pool;
1076 static virStorageVolPtr
1077 virt_get_vol_by_args(cherokee_handler_virt_t *hdl, virConnectPtr virConn, unsigned short int prefix) {
1078 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1079 virStoragePoolPtr pool;
1080 virStorageVolPtr volume;
1081 void *temp;
1082 ret_t ret;
1084 pool = virt_get_pool_by_args(hdl, virConn);
1086 if (pool == NULL) {
1087 conn->error_code = http_not_found;
1088 return NULL;
1091 virStoragePoolRefresh(pool, 0); /* TODO: might be better to do it outside */
1093 ret = cherokee_avl_get_ptr (conn->arguments, "volume", &temp);
1094 if (unlikely(ret < ret_ok)) {
1095 TRACE("virt", "virStorageVolPtr; Volume argument not specified");
1096 conn->error_code = http_bad_request;
1097 return NULL;
1100 if (prefix == 1) {
1101 cherokee_buffer_t fullvol = CHEROKEE_BUF_INIT;
1102 cherokee_buffer_add_va (&fullvol, "%s_%s", hdl->user.buf, temp);
1103 volume = virStorageVolLookupByName(pool, fullvol.buf);
1104 cherokee_buffer_mrproper(&fullvol);
1105 } else {
1106 volume = virStorageVolLookupByName(pool, temp);
1109 if (volume == NULL)
1110 conn->error_code = http_not_found;
1112 virStoragePoolFree(pool);
1114 return volume;
1117 /* This function is the home for all functions that need a working
1118 * pool/volume combination */
1119 static ret_t
1120 virt_pool_vol(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
1121 cherokee_buffer_t *buf = &HDL_VIRT(hdl)->buffer;
1122 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1123 virStorageVolPtr volume;
1124 ret_t ret = ret_ok;
1126 /* We only allow clone to run 'unsafe', others get prefixed */
1127 volume = virt_get_vol_by_args(hdl, virConn, (hdl->action != storageVolCloneXML_args));
1129 /* If the volume doesn't exist, no point to continue */
1130 if (volume == NULL) {
1131 conn->error_code = http_not_found;
1132 return ret_error;
1135 switch (hdl->action) {
1136 /* Sets the password of a specific volume, requires the password= option */
1137 case storageVolSetPassword_args: {
1138 void *temp;
1139 ret = cherokee_avl_get_ptr (conn->arguments, "password", &temp);
1140 if (unlikely(ret < ret_ok)) {
1141 TRACE("virt", "storageVolSetPassword_args; password argument not specified");
1142 conn->error_code = http_bad_request;
1143 ret = ret_error;
1144 goto virt_pool_vol_cleanup;
1145 } else {
1146 cherokee_buffer_t cmd_passwd = CHEROKEE_BUF_INIT;
1147 cherokee_buffer_add_va (&cmd_passwd, "/usr/sbin/passwdchanger.sh %s %s\n", virStorageVolGetKey(volume), temp);
1148 cherokee_buffer_add_long10(buf, system(cmd_passwd.buf));
1149 cherokee_buffer_mrproper(&cmd_passwd);
1151 break;
1154 /* Removes a volume */
1155 case storageVolDelete_args: {
1156 cherokee_buffer_add_long10(buf, virStorageVolDelete(volume, 0));
1157 break;
1160 /* Gives a description of a storage volume in XML */
1161 case storageVolGetXMLDesc_args: {
1162 char *xml = virStorageVolGetXMLDesc(volume, 0);
1163 cherokee_buffer_add(buf, xml, strlen(xml));
1164 free(xml);
1165 break;
1168 /* Clones a volume, insecure method! requires a new name= */
1169 case storageVolCloneXML_args: {
1170 void *name;
1171 cherokee_buffer_t busy = CHEROKEE_BUF_INIT;
1173 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
1174 if (unlikely(ret < ret_ok)) {
1175 TRACE("virt", "storageVolCloneXML_args; name argument not specified");
1176 conn->error_code = http_bad_request;
1177 ret = ret_error;
1178 goto virt_pool_vol_cleanup;
1181 cherokee_buffer_add_va (&busy, "/mnt/images/queue/%s_%s.queued", hdl->user.buf, name);
1183 if (unlikely(symlink(virStorageVolGetKey(volume), busy.buf) == 1)) {
1184 conn->error_code = http_internal_error;
1185 ret = ret_error;
1186 } else {
1187 cherokee_buffer_add_str(buf, "QUEUED");
1190 cherokee_buffer_mrproper(&busy);
1192 break;
1197 virt_pool_vol_cleanup:
1198 /* And in the end we need to free the volume we have used */
1199 virStorageVolFree(volume);
1201 return ret;
1204 static ret_t
1205 virt_virt_new(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
1206 cherokee_buffer_t *buf = &HDL_VIRT(hdl)->buffer;
1207 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1208 cherokee_buffer_t xml = CHEROKEE_BUF_INIT;
1209 virStoragePoolPtr pool = NULL;
1210 ret_t ret = ret_ok;
1212 switch (hdl->action) {
1214 case storageVolCreateXML_args: {
1215 void *temp, *type, *name, *unit;
1216 unsigned long int capacity, allocation;
1218 ret = cherokee_avl_get_ptr (conn->arguments, "pool", &temp);
1219 if (unlikely(ret < ret_ok)) {
1220 TRACE("virt", "storageVolCreateXML_args; pool argument not specified");
1221 conn->error_code = http_bad_request;
1222 goto virt_virt_new_cleanup;
1225 TRACE("args", "%s", temp);
1227 pool = virStoragePoolLookupByName(virConn, temp);
1229 if (pool == NULL) {
1230 conn->error_code = http_not_found;
1231 goto virt_virt_new_cleanup;
1234 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
1235 if (unlikely(ret < ret_ok)) {
1236 TRACE("virt", "storageVolCreateXML_args; name argument not specified");
1237 conn->error_code = http_bad_request;
1238 goto virt_virt_new_cleanup;
1241 ret = cherokee_avl_get_ptr (conn->arguments, "type", &type);
1242 if (unlikely(ret < ret_ok)) {
1243 TRACE("virt", "storageVolCreateXML_args; type argument not specified");
1244 conn->error_code = http_bad_request;
1245 goto virt_virt_new_cleanup;
1248 ret = cherokee_avl_get_ptr (conn->arguments, "unit", &unit);
1249 if (unlikely(ret < ret_ok)) {
1250 TRACE("virt", "storageVolCreateXML_args; unit argument not specified");
1251 conn->error_code = http_bad_request;
1252 goto virt_virt_new_cleanup;
1255 ret = cherokee_avl_get_ptr (conn->arguments, "allocation", &temp);
1256 if (unlikely(ret < ret_ok)) {
1257 TRACE("virt", "storageVolCreateXML_args; allocation argument not specified");
1258 conn->error_code = http_bad_request;
1259 goto virt_virt_new_cleanup;
1262 allocation = strtoul(temp, NULL, 10);
1263 if (errno == ERANGE) {
1264 TRACE("virt", "storageVolCreateXML_args; allocation is not a number");
1265 conn->error_code = http_bad_request;
1266 ret = ret_error;
1267 goto virt_virt_new_cleanup;
1270 ret = cherokee_avl_get_ptr (conn->arguments, "capacity", &temp);
1271 if (unlikely(ret < ret_ok)) {
1272 TRACE("virt", "storageVolCreateXML_args; capacity argument not specified");
1273 conn->error_code = http_bad_request;
1274 goto virt_virt_new_cleanup;
1277 capacity = strtoul(temp, NULL, 10);
1278 if (errno == ERANGE || capacity == 0) {
1279 TRACE("virt", "storageVolCreateXML_args; capacity is not a number");
1280 conn->error_code = http_bad_request;
1281 ret = ret_error;
1282 goto virt_virt_new_cleanup;
1285 cherokee_buffer_add_va (&xml, VIRT_STORAGE_XML, type, hdl->user.buf, name, allocation, unit, capacity, hdl->user.buf, name, hdl->user.buf, name);
1286 break;
1289 case domainDefineXML_args: {
1290 ret_t ret;
1291 unsigned int i;
1292 unsigned long vcpu = 0, interface = 0, memory = 0;
1294 void *temp = NULL;
1296 ret = cherokee_avl_get_ptr (conn->arguments, "vcpu", &temp);
1297 if (ret == ret_ok)
1298 vcpu = strtoul(temp, (char **) NULL, 10);
1300 if (ret != ret_ok || errno == ERANGE || vcpu == 0) {
1301 conn->error_code = http_internal_error;
1302 goto virt_virt_new_cleanup;
1306 ret = cherokee_avl_get_ptr (conn->arguments, "memory", &temp);
1307 if (ret == ret_ok)
1308 memory = strtoul(temp, (char **) NULL, 10);
1310 if (ret != ret_ok || errno == ERANGE || memory == 0) {
1311 conn->error_code = http_internal_error;
1312 goto virt_virt_new_cleanup;
1316 ret = cherokee_avl_get_ptr (conn->arguments, "interface", &temp);
1317 if (ret == ret_ok)
1318 interface = strtoul(temp, (char **) NULL, 10);
1320 if (ret != ret_ok || errno == ERANGE) {
1321 conn->error_code = http_internal_error;
1322 goto virt_virt_new_cleanup;
1325 cherokee_buffer_t cmdline_extra = CHEROKEE_BUF_INIT;
1326 cherokee_buffer_t xml_interfaces = CHEROKEE_BUF_INIT;
1327 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
1328 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
1330 for (i = 0; i < interface; i++) {
1331 char *mac = NULL;
1332 char *ip = NULL;
1333 if (getNewMac("dv28", domu.buf, &mac, &ip) == 0) {
1334 cherokee_buffer_add_va (&xml_interfaces, VIRT_INTERFACE_XML, mac, ip);
1335 if (i == 0) {
1336 /* TODO: terrible hack */
1337 char gateway[16];
1338 char *temp;
1339 strcpy(gateway, ip);
1340 temp = strchr(gateway, '.');
1341 temp = strchr(++temp, '.');
1342 temp = strchr(++temp, '.');
1343 strcpy(++temp, "254");
1344 cherokee_buffer_add_va (&cmdline_extra, VIRT_DOMAIN_CMD_IP, ip, gateway);
1349 cherokee_buffer_mrproper(&domu);
1351 cherokee_buffer_add_va (&xml, VIRT_DOMAIN_XML,
1352 hdl->user.buf, hdl->vm.buf, cmdline_extra.buf, memory, vcpu, xml_interfaces.buf);
1354 cherokee_buffer_mrproper(&xml_interfaces);
1355 cherokee_buffer_mrproper(&cmdline_extra);
1356 break;
1359 case storageVolCreateXML:
1360 case domainDefineXML: {
1361 off_t postl;
1362 cherokee_post_get_len (&conn->post, &postl);
1363 cherokee_post_walk_read (&conn->post, &xml, (cuint_t) postl);
1364 break;
1368 switch (hdl->action) {
1369 case domainDefineXML_args:
1370 case domainDefineXML: {
1371 virDomainPtr result = virDomainDefineXML(virConn, (const char *) xml.buf);
1373 if (result == NULL) {
1374 /* TODO: vrij maken eventuele uitgegeven macs! */
1375 conn->error_code = http_internal_error;
1376 goto virt_virt_new_cleanup;
1379 save_xml(hdl, result, buf);
1381 virDomainFree(result);
1383 break;
1386 case storageVolCreateXML_args:
1387 case storageVolCreateXML: {
1388 virStorageVolPtr vol = virStorageVolCreateXML(pool, xml.buf, 0);
1390 if (vol == NULL) {
1391 cherokee_buffer_add_long10(buf, -1);
1392 goto virt_virt_new_cleanup;
1395 virStorageVolFree(vol);
1397 cherokee_buffer_add_long10(buf, 0);
1398 break;
1402 virt_virt_new_cleanup:
1403 cherokee_buffer_mrproper(&xml);
1405 if (pool)
1406 virStoragePoolFree(pool);
1408 return ret;
1412 static ret_t
1413 virt_virt_do(cherokee_handler_virt_t *hdl, cherokee_buffer_t *domu, cherokee_buffer_t *uri)
1415 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1417 ret_t ret = ret_error;
1418 virConnectPtr virConn = NULL;
1421 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE)
1422 virConn = virConnectOpen (uri->buf);
1424 if (!virConn && !(virConn = virConnectOpenReadOnly (uri->buf))) {
1425 conn->error_code = http_service_unavailable;
1426 return ret_error;
1429 switch (hdl->action) {
1430 case storageVolDelete_args:
1431 case storageVolSetPassword_args:
1432 case storageVolGetXMLDesc_args:
1433 case storageVolCloneXML_args:
1434 ret = virt_pool_vol(hdl, virConn);
1435 break;
1437 case storageVolCreateXML_args:
1438 case storageVolCreateXML:
1439 case domainDefineXML_args:
1440 case domainDefineXML:
1441 ret = virt_virt_new(hdl, virConn);
1442 break;
1444 default: {
1445 virDomainPtr dom;
1446 if ((dom = virDomainLookupByName(virConn, domu->buf)) == NULL) {
1447 conn->error_code = http_not_found;
1448 } else {
1449 ret = virt_virt_function(hdl, dom, virConn);
1450 virDomainFree(dom);
1455 virConnectClose(virConn);
1456 return ret;
1459 static ret_t
1460 virt_while (cherokee_buffer_t *key, void *value, void *param) {
1461 cherokee_handler_virt_t * hdl = param;
1462 // cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
1463 // cherokee_buffer_add_va (&uri, "xen://%s/", ((cherokee_buffer_t *) value)->buf);
1464 // virt_virt_do((cherokee_handler_virt_t *) param, key, &uri);
1465 // cherokee_buffer_mrproper(&uri);
1467 cherokee_buffer_add_va (&hdl->buffer, "<domain><name>%s</name></domain>", key->buf);
1469 return ret_ok;
1472 static ret_t
1473 virt_while_user (cherokee_buffer_t *key, void *value, void *param) {
1474 cherokee_handler_virt_t *hdl = param;
1475 if (key->len > hdl->user.len && key->buf[hdl->user.len] == '_' && strncmp(key->buf, hdl->user.buf, hdl->user.len) == 0)
1476 return virt_while (key, value, param);
1478 return ret_ok;
1481 static ret_t
1482 virt_build_page (cherokee_handler_virt_t *hdl)
1484 ret_t ret;
1485 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1486 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
1487 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
1489 /* We use the webserver methods to parse the querystring */
1490 /* Maybe do something smart with ENUM, begin_args, end_args... */
1491 if ((hdl->action == domainDefineXML_args) ||
1492 (hdl->action == domainAttachDevice_args) ||
1493 (hdl->action == storageVolGetXMLDesc_args) ||
1494 (hdl->action == storageVolDelete_args) ||
1495 (hdl->action == storageVolSetPassword_args) ||
1496 (hdl->action == storageVolCloneXML_args) ||
1497 (hdl->action == storageVolCloneStatus_args) ||
1498 (hdl->action == storageVolCreateXML_args) ||
1499 (hdl->action == graphLoad_args) ||
1500 (hdl->action == graphInterface_args)) {
1501 ret = cherokee_connection_parse_args (conn);
1502 if (unlikely(ret < ret_ok)) {
1503 conn->error_code = http_internal_error;
1504 return ret_error;
1508 switch (hdl->action) {
1509 case graphInterface_args: {
1510 struct stat statbuf;
1511 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
1512 void *interface;
1513 if ((ret = cherokee_avl_get_ptr (conn->arguments, "interface", &interface)) != ret_ok)
1514 interface = "eth0";
1516 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/interface_%s.rrd", hdl->user.buf, hdl->vm.buf, interface);
1517 if (stat(path.buf, &statbuf) != 0) {
1518 conn->error_code = http_not_found;
1519 ret = ret_error;
1520 } else {
1521 void *width, *height, *start, *end;
1522 if ((ret = cherokee_avl_get_ptr (conn->arguments, "width", &width)) != ret_ok)
1523 width = "600";
1525 if ((ret = cherokee_avl_get_ptr (conn->arguments, "height", &height)) != ret_ok)
1526 height = "200";
1528 if ((ret = cherokee_avl_get_ptr (conn->arguments, "start", &start)) != ret_ok)
1529 start = "now-1h";
1531 if ((ret = cherokee_avl_get_ptr (conn->arguments, "end", &end)) != ret_ok)
1532 end = "now";
1534 cherokee_buffer_t def1 = CHEROKEE_BUF_INIT;
1535 cherokee_buffer_t def2 = CHEROKEE_BUF_INIT;
1536 cherokee_buffer_add_va (&def1, "DEF:rxbytes=%s:rxbytes:AVERAGE:step=30", path.buf);
1537 cherokee_buffer_add_va (&def2, "DEF:txbytes=%s:txbytes:AVERAGE:step=30", path.buf);
1538 char **calcpr = NULL;
1539 int xsize, ysize;
1540 double ymin, ymax;
1541 /* TODO: betere random hier */
1542 char filenametemp[L_tmpnam+1];
1543 char *filename = tmpnam(filenametemp);
1545 // char *filename = mktemp(strdup("handler_virt_XXXXXX"));
1546 char *r_graph[] = { "rrdgraph", filename,
1547 "-a", "PNG",
1548 "-w", width,
1549 "-h", height,
1550 "--start", start,
1551 "--end", end,
1552 "--title", interface,
1553 "--lower-limit", "0", "--alt-autoscale-max", "--vertical-label", "bits per second",
1554 def1.buf,
1555 def2.buf,
1556 "CDEF:txbits=txbytes,8,*",
1557 "CDEF:rxbits=rxbytes,8,*",
1558 "AREA:rxbits#00EE00:rxbits",
1559 "LINE:txbits#0000EE:txbits" };
1561 rrd_graph(25, r_graph, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
1562 if ((ret = cherokee_buffer_read_file(&hdl->buffer, filename)) == ret_error) {
1563 hdl->action = http_internal_error;
1566 unlink(filename);
1568 cherokee_buffer_mrproper(&def1);
1569 cherokee_buffer_mrproper(&def2);
1571 cherokee_buffer_mrproper(&path);
1572 goto virt_build_page_cleanup;
1577 case graphLoad_args: {
1578 struct stat statbuf;
1579 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
1580 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/cpuTime.rrd", hdl->user.buf, hdl->vm.buf);
1581 if (stat(path.buf, &statbuf) != 0) {
1582 conn->error_code = http_not_found;
1583 ret = ret_error;
1584 } else {
1585 void *width, *height, *start, *end;
1586 if ((ret = cherokee_avl_get_ptr (conn->arguments, "width", &width)) != ret_ok)
1587 width = "600";
1589 if ((ret = cherokee_avl_get_ptr (conn->arguments, "height", &height)) != ret_ok)
1590 height = "200";
1592 if ((ret = cherokee_avl_get_ptr (conn->arguments, "start", &start)) != ret_ok)
1593 start = "now-1h";
1595 if ((ret = cherokee_avl_get_ptr (conn->arguments, "end", &end)) != ret_ok)
1596 end = "now";
1598 /* TODO: wat error checking? */
1600 cherokee_buffer_t def = CHEROKEE_BUF_INIT;
1601 cherokee_buffer_add_va (&def, "DEF:cputime=%s:cpuTime:AVERAGE:step=30", path.buf);
1602 char **calcpr = NULL;
1603 int xsize, ysize;
1604 double ymin, ymax;
1605 char *filename = mktemp(strdup("handler_virt_XXXXXX"));
1606 char *r_graph[] = { "rrdgraph",
1607 filename,
1608 "-a", "PNG",
1609 "-w", width,
1610 "-h", height,
1611 "--start", start,
1612 "--end", end,
1613 "--title", hdl->vm.buf,
1614 "--lower-limit", "0", "--alt-autoscale-max", "--vertical-label", "(used / total) time",
1615 def.buf,
1616 "CDEF:cpuload=cputime,1000000000,/",
1617 "LINE:cpuload#EE0000:cpuLoad" };
1619 rrd_graph(22, r_graph, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
1620 if ((ret = cherokee_buffer_read_file(&hdl->buffer, filename)) == ret_error) {
1621 hdl->action = http_internal_error;
1624 unlink(filename);
1625 free(filename);
1627 cherokee_buffer_mrproper(&def);
1629 cherokee_buffer_mrproper(&path);
1630 goto virt_build_page_cleanup;
1634 if (hdl->action > xml && HDL_VIRT_PROPS(hdl)->xsl.len > 0)
1635 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);
1638 switch (hdl->action) {
1639 case showall: {
1640 size_t len;
1641 if (cherokee_avl_r_len(&HDL_CLUSTERSTATS_PROPS(hdl)->entries, &len) == ret_ok && len > 0) {
1642 cherokee_buffer_add_str (&hdl->buffer, "<domains>\n");
1643 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);
1644 cherokee_buffer_add_str (&hdl->buffer, "</domains>");
1645 } else {
1646 cherokee_buffer_add_str (&hdl->buffer, "<domains/>");
1649 ret = ret_ok;
1650 break;
1653 case showuservms: {
1654 cherokee_buffer_add_str (&hdl->buffer, "<domains>\n");
1655 get_all_configurations(&HDL_CLUSTERSTATS_PROPS(hdl)->entries, &hdl->user, &hdl->buffer);
1656 cherokee_buffer_add_str (&hdl->buffer, "</domains>");
1657 ret = ret_ok;
1658 break;
1661 default: {
1662 AvahiStringList *list = NULL;
1663 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
1665 ret = cherokee_avl_r_get(&HDL_CLUSTERSTATS_PROPS(hdl)->entries, &domu, (void **) &list);
1667 if (ret == ret_not_found) {
1668 virDomainPtr virDom;
1669 virConnectPtr virConn;
1670 if (HDL_VIRT_PROPS(hdl)->virt.len > 0)
1671 cherokee_buffer_add_va (&uri, "xen://%s/", HDL_VIRT_PROPS(hdl)->virt); // TODO: change!
1673 /* If we have the read only parameter, we will set up a connection to the
1674 * Hypervisor here. */
1675 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE)
1676 virConn = virConnectOpen (uri.buf);
1678 /* We should already have a connection (read only) or build a new connection
1679 * if this doesn't work, we can safely assume our services is fubar */
1680 if (!virConn && !(virConn = virConnectOpenReadOnly (uri.buf))) {
1681 conn->error_code = http_service_unavailable;
1682 return ret_error;
1685 /* We lookup if there is a domain somewhere with this name */
1686 if ((virDom = virDomainLookupByName(virConn, domu.buf)) == NULL) {
1687 /* If the domain is not found on the network the only possible
1688 * command that is possible would be to Define it */
1689 if (hdl->action == domainDefineXML_args || hdl->action == domainDefineXML) {
1690 ret = ret_ok;
1691 } else {
1692 /* We should also look on disk for defined stuff */
1693 cherokee_buffer_t xmlDesc = CHEROKEE_BUF_INIT;
1694 if (load_xml(hdl, &xmlDesc) == ret_ok && (virDom = virDomainDefineXML(virConn, xmlDesc.buf)) != NULL) {
1695 /* The domain existed and is loaded! */
1696 ret = ret_ok;
1697 } else {
1698 /* Otherwise we don't have anything to do and we should
1699 * return an error */
1700 hdl->action = nothing;
1701 conn->error_code = http_not_found;
1702 ret = ret_error;
1705 cherokee_buffer_mrproper(&xmlDesc);
1707 } else {
1708 /* We don't want te recreate things that already found on the network
1709 * first undefine them! */
1710 if (hdl->action == domainDefineXML_args || hdl->action == domainDefineXML) {
1711 TRACE("virt", "domainDefineXML_args/domainDefineXML; domain already exists");
1712 hdl->action = nothing;
1713 conn->error_code = http_bad_request;
1714 ret = ret_error;
1715 } else {
1716 /* Everything is ok, because nothing is found. */
1717 ret = ret_ok;
1720 /* Domain wasn't NULL, so we should free it here */
1721 virDomainFree(virDom);
1723 virConnectClose (virConn);
1724 } else if (ret == ret_ok) {
1725 char *hostname;
1726 avahi_string_list_get_pair(avahi_string_list_find(list, "dom0"), NULL, &hostname, NULL);
1727 cherokee_buffer_add_va (&uri, "xen://%s/", hostname);
1728 printf("%s\n", uri.buf);
1729 avahi_free(hostname);
1730 } else {
1731 hdl->action = http_internal_error;
1732 hdl->action = nothing;
1733 ret = ret_error;
1739 if (ret == ret_ok && hdl->action != showall && hdl->action != showuservms) {
1740 ret = virt_virt_do(hdl, &domu, &uri);
1743 virt_build_page_cleanup:
1744 cherokee_buffer_mrproper(&domu);
1745 cherokee_buffer_mrproper(&uri);
1747 return ret;
1751 ret_t
1752 cherokee_handler_virt_new (cherokee_handler_t **hdl, cherokee_connection_t *cnt, cherokee_module_props_t *props)
1754 ret_t ret;
1755 CHEROKEE_NEW_STRUCT (n, handler_virt);
1757 /* Init the base class
1760 cherokee_handler_init_base(HANDLER(n), cnt, HANDLER_PROPS(props), PLUGIN_INFO_HANDLER_PTR(virt));
1762 MODULE(n)->init = (handler_func_init_t) cherokee_handler_virt_init;
1763 MODULE(n)->free = (module_func_free_t) cherokee_handler_virt_free;
1764 HANDLER(n)->step = (handler_func_step_t) cherokee_handler_virt_step;
1765 HANDLER(n)->add_headers = (handler_func_add_headers_t) cherokee_handler_virt_add_headers;
1767 HANDLER(n)->support = hsupport_length | hsupport_range;
1769 ret = cherokee_buffer_init (&n->buffer);
1770 if (unlikely(ret != ret_ok))
1771 return ret;
1773 ret = cherokee_buffer_ensure_size (&n->buffer, 4*1024);
1774 if (unlikely(ret != ret_ok))
1775 return ret;
1777 *hdl = HANDLER(n);
1779 return ret_ok;