nice program to store context from the cloud
[handlervirt.git] / handler_virt.c
blob6cf647fe7056e0a5b10363229725d6ab576738a2
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 " <target dev='vif1.0' />" \
31 " <source bridge='xenbr0'/>" \
32 " <mac address='%s'/>" \
33 " <ip address='%s' />" \
34 " <script path='vif-bridge'/>" \
35 " </interface>"
37 //" <target dev='vif1.0'/>" \
39 #define VIRT_DISK_XML \
40 " <disk type='file' device='disk'>" \
41 " <driver name='tap' type='aio' />" \
42 " <source file='%s'/>" \
43 " <target dev='%s' bus='xen'/>" \
44 " </disk>"
48 #define VIRT_DOMAIN_XML \
49 "<domain type='xen'>" \
50 " <name>%s_%s</name>" \
51 " <os>" \
52 " <type>linux</type>" \
53 " <kernel>/usr/lib/xen/boot/linux-2.6.20-xen-r6</kernel>" \
54 " <cmdline> root=/dev/xvda ro</cmdline>" \
55 " </os>" \
56 " <memory>%d</memory>" \
57 " <vcpu>%d</vcpu>" \
58 " <on_poweroff>destroy</on_poweroff>" \
59 " <on_reboot>restart</on_reboot>" \
60 " <on_crash>destroy</on_crash>" \
61 " <devices>" \
62 " %s" \
63 " </devices>" \
64 "</domain>"
66 #define VIRT_STORAGE_XML \
67 "<volume type='%s'>" \
68 " <name>%s_%s</name>" \
69 " <allocation>%lu</allocation>" \
70 " <capacity unit='%s'>%lu</capacity>" \
71 " <target>" \
72 " <path>%s_%s</path>" \
73 " <permissions>" \
74 " <owner>0744</owner>" \
75 " <group>0744</group>" \
76 " <mode>0744</mode>" \
77 " <label>%s_%s</label>" \
78 " </permissions>" \
79 " </target>" \
80 "</volume>"
83 #include "handler_virt.h"
84 #include "handler_avahi.h"
85 #include <cherokee/cherokee.h>
86 #include <libxml/parser.h>
87 #include <libxml/xpath.h>
90 /* Plug-in initialization
92 * In this function you can use any of these:
93 * http_delete | http_get | http_post | http_put
95 * For a full list: cherokee_http_method_t
97 * It is what your handler to be implements.
100 PLUGIN_INFO_HANDLER_EASIEST_INIT (virt, http_get | http_post);
103 /* Methods implementation
105 static ret_t
106 props_free (cherokee_handler_virt_props_t *props)
109 cherokee_buffer_mrproper(&props->xsl);
110 cherokee_buffer_mrproper(&props->virt);
111 return cherokee_module_props_free_base (MODULE_PROPS(props));
115 ret_t
116 cherokee_handler_virt_configure (cherokee_config_node_t *conf, cherokee_server_t *srv, cherokee_module_props_t **_props)
118 cherokee_list_t *i;
119 cherokee_handler_virt_props_t *props;
121 /* Instance a new property object
124 if (*_props == NULL) {
125 CHEROKEE_NEW_STRUCT (n, handler_virt_props);
127 cherokee_handler_avahi_props_init_base (PROP_AVAHI(n), MODULE_PROPS_FREE(props_free));
129 /* Look at handler_virt.h
130 * This is an virt of configuration.
132 n->authenticate = true; /* by default we are secure! */
133 n->read_only = true; /* by default we are secure! */
134 cherokee_buffer_init (&n->xsl); /* we support a custom header */
135 cherokee_buffer_init (&n->virt); /* your first xenserver */
137 *_props = MODULE_PROPS(n);
140 props = PROP_VIRT(*_props);
142 cherokee_config_node_foreach (i, conf) {
143 cherokee_config_node_t *subconf = CONFIG_NODE(i);
145 if (equal_buf_str (&subconf->key, "authenticate")) {
146 props->authenticate = atoi(subconf->val.buf);
148 else if (equal_buf_str (&subconf->key, "read_only")) {
149 props->read_only = atoi(subconf->val.buf);
151 else if (equal_buf_str (&subconf->key, "xsl")) {
152 cherokee_buffer_add_buffer (&props->xsl, &subconf->val);
154 else if (equal_buf_str (&subconf->key, "virt")) {
155 cherokee_buffer_add_buffer (&props->virt, &subconf->val);
160 /* Init base class
163 return cherokee_handler_avahi_configure (conf, srv, _props);
167 ret_t
168 cherokee_handler_virt_init (cherokee_handler_virt_t *hdl)
170 int isroot = false;
171 int len;
172 char *this, *next;
174 cherokee_connection_t *conn = HANDLER_CONN(hdl);
175 cherokee_buffer_init(&hdl->user);
176 cherokee_buffer_init(&hdl->vm);
178 hdl->action = nothing;
180 cherokee_buffer_add (&conn->pathinfo,
181 conn->request.buf + conn->web_directory.len,
182 conn->request.len - conn->web_directory.len);
184 this = conn->pathinfo.buf + 1;
185 next = strchr(this, '/');
188 if ((!next && (this && (len = strlen(this)) == 0)) || (next && ((len = next - this) == 0)) )
189 hdl->action = showall;
190 else {
191 cherokee_buffer_add (&hdl->user, this, len);
194 if (HDL_VIRT_PROPS(hdl)->authenticate) {
195 if (!conn->validator ||
196 (conn->validator &&
197 (!cherokee_buffer_cmp_buf(&conn->validator->user, &hdl->user) &&
198 !(isroot = cherokee_buffer_cmp (&conn->validator->user, "root", 4))))) {
199 hdl->action = nothing; /* just in case */
200 conn->error_code = http_unauthorized;
201 return ret_error;
203 } else {
204 isroot = true;
207 if (hdl->action == showall) {
208 if (!isroot) {
209 hdl->action = nothing;
210 conn->error_code = http_unauthorized;
211 return ret_error;
212 } else {
213 return virt_build_page(hdl);
218 if (!next) {
219 hdl->action = showuservms;
220 return virt_build_page(hdl);
221 } else {
222 this = next + 1;
223 next = strchr(this, '/');
225 if (!next && (this && ( (len = strlen(this)) == 0) || (next && ((len = next - this) == 0)) ) ) {
226 hdl->action = showuservms;
227 return virt_build_page(hdl);
231 cherokee_buffer_add (&hdl->vm, this, len);
233 if (!next) {
234 hdl->action = domainGetXMLDesc;
235 return virt_build_page(hdl);
236 } else {
237 this = next + 1;
238 next = strchr(this, '/');
240 if (!next && (this && (len = strlen(this)) == 0) || (next && ((len = next - this) == 0)) ) {
241 hdl->action = domainGetXMLDesc;
242 return virt_build_page(hdl);
246 /* TODO: it would be nice to filter read_only methods already on this point */
247 hdl->action = not_implemented;
248 switch (conn->header.method) {
249 case http_get:
250 if (strncmp(this, "virDomain", 9) == 0) {
251 if (strncmp(this+9, "Get", 3) == 0) {
252 if (strcmp(this+12, "ID") == 0) hdl->action = domainGetID;
253 else if (strcmp(this+12, "Name") == 0) hdl->action = domainGetName;
254 else if (strcmp(this+12, "MaxMemory") == 0) hdl->action = domainGetMaxMemory;
255 else if (strcmp(this+12, "MaxVcpus") == 0) hdl->action = domainGetMaxVcpus;
256 else if (strcmp(this+12, "OSType") == 0) hdl->action = domainGetOSType;
257 else if (strcmp(this+12, "UUID") == 0) hdl->action = domainGetUUID;
258 else if (strcmp(this+12, "UUIDString") == 0) hdl->action = domainGetUUIDString;
259 else if (strcmp(this+12, "XMLDesc") == 0) hdl->action = domainGetXMLDesc;
261 else if (strcmp(this+9, "Create") == 0) hdl->action = domainCreate;
262 else if (strcmp(this+9, "Destroy") == 0) hdl->action = domainDestroy;
263 else if (strcmp(this+9, "Reboot") == 0) hdl->action = domainReboot;
264 else if (strcmp(this+9, "Shutdown") == 0) hdl->action = domainShutdown;
266 else if (strcmp(this+9, "Save") == 0) hdl->action = domainSave;
267 else if (strcmp(this+9, "Restore") == 0) hdl->action = domainRestore;
269 else if (strcmp(this+9, "AttachDevice") == 0) hdl->action = domainAttachDevice_args;
271 else if (strcmp(this+9, "DefineXML") == 0) hdl->action = domainDefineXML_args;
272 else if (strcmp(this+9, "Undefine") == 0) hdl->action = domainUndefine;
274 else if (strncmp(this, "virStorage", 10) == 0) {
275 if (strncmp(this+10, "Vol", 3) == 0) {
276 if (strcmp(this+13, "CreateXML") == 0) hdl->action = storageVolCreateXML_args;
277 else if (strcmp(this+13, "Delete") == 0) hdl->action = storageVolDelete_args;
278 else if (strcmp(this+13, "CloneXML") == 0) hdl->action = storageVolCloneXML_args;
279 else if (strcmp(this+13, "GetXMLDesc") == 0) hdl->action = storageVolGetXMLDesc_args;
280 else if (strcmp(this+13, "SetPassword") == 0) hdl->action = storageVolSetPassword_args;
283 break;
285 case http_post: {
286 off_t postl;
287 cherokee_post_get_len (&conn->post, &postl);
289 if (postl <= 0 || postl >= (INT_MAX-1)) {
290 conn->error_code = http_bad_request;
291 return ret_error;
294 if (strncmp(this, "virDomain", 9) == 0) {
295 if (strcmp(this+9, "AttachDevice") == 0) hdl->action = domainAttachDevice;
296 else if (strcmp(this+9, "DetachDevice") == 0) hdl->action = domainDetachDevice;
297 else if (strcmp(this+9, "DefineXML") == 0) hdl->action = domainDefineXML;
299 else if (strncmp(this, "virStorage", 10) == 0) {
300 if (strncmp(this+10, "Vol", 3) == 0) {
301 if (strcmp(this+13, "CreateXML") == 0) hdl->action = storageVolCreateXML;
305 break;
308 default:
309 return ret_error;
312 if (hdl->action <= 0) {
313 conn->error_code = http_bad_request;
314 return ret_error;
318 return virt_build_page(hdl);
321 ret_t
322 cherokee_handler_virt_free (cherokee_handler_virt_t *hdl)
324 cherokee_buffer_mrproper (&hdl->buffer);
325 cherokee_buffer_mrproper (&hdl->user);
326 cherokee_buffer_mrproper (&hdl->vm);
328 return ret_ok;
331 /* TODO: update with alvaro example */
332 ret_t
333 cherokee_handler_virt_step (cherokee_handler_virt_t *hdl, cherokee_buffer_t *buffer)
335 cherokee_buffer_add_buffer (buffer, &hdl->buffer);
336 return ret_eof_have_data;
339 ret_t
340 cherokee_handler_virt_add_headers (cherokee_handler_virt_t *hdl, cherokee_buffer_t *buffer)
342 cherokee_buffer_add_va (buffer, "Content-Length: %d"CRLF, hdl->buffer.len);
344 if (hdl->action > xml)
345 cherokee_buffer_add_str (buffer, "Content-Type: application/xml"CRLF);
346 else
347 cherokee_buffer_add_str (buffer, "Content-Type: text/plain"CRLF);
349 return ret_ok;
353 static ret_t
354 while_func_entries (cherokee_buffer_t *key, void *value, void *param) {
355 virConnectPtr conn = NULL;
356 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
357 cherokee_buffer_t *buf = (cherokee_buffer_t *)param;
359 cherokee_buffer_add_va (&uri, "xen://%s/", value);
361 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE && !(conn = virConnectOpen (uri.buf))) {
362 if (!(conn = virConnectOpen (uri.buf))) {
363 return ret_error;
367 if (!conn && !(conn = virConnectOpenReadOnly (uri.buf))) {
368 return ret_error;
369 } else {
370 virDomainPtr dom;
371 if (!(dom = virDomainLookupByName(conn, (const char *) key->buf))) {
372 return ret_error;
373 } else {
374 char *xml = virDomainGetXMLDesc(dom, 0);
375 cherokee_buffer_add(buf, xml, strlen(xml));
377 virConnectClose(conn);
380 cherokee_buffer_mrproper(&uri);
382 return ret_ok;
385 static ret_t save_xml(cherokee_handler_virt_t *hdl, virDomainPtr result, cherokee_buffer_t *buf) {
386 ret_t ret = ret_error;
387 FILE *fd;
388 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
389 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s", hdl->user.buf);
391 mkdir(path.buf, 0755);
392 cherokee_buffer_add_va (&path, "/%s", hdl->vm.buf);
394 mkdir(path.buf, 0755);
395 cherokee_buffer_add_str (&path, "/index.xml");
397 if ((fd = fopen(path.buf, "w")) == NULL) {
398 perror(__func__);
399 } else {
400 char *output = virDomainGetXMLDesc(result, 0);
401 fwrite(output, strlen(output), sizeof(char), fd);
402 fclose(fd);
403 if (buf)
404 cherokee_buffer_add(buf, output, strlen(output));
405 free(output);
406 ret = ret_ok;
408 cherokee_buffer_mrproper(&path);
409 return ret;
413 static ret_t
414 virt_virt_function(cherokee_handler_virt_t *hdl, virDomainPtr dom, virConnectPtr virConn) {
415 cherokee_connection_t *conn = HANDLER_CONN(hdl);
416 cherokee_buffer_t *buf = &HDL_AVAHI(hdl)->buffer;
418 switch (hdl->action) {
419 /* Save the memory of a domain to a file and suspend the domain */
420 case domainSave: {
421 ret_t ret = ret_ok;
422 int result;
423 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
424 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/memory", hdl->user.buf, hdl->vm.buf);
425 if ((result = virDomainSave(dom, path.buf)) != 0) {
426 conn->error_code = http_internal_error;
427 ret = ret_error;
429 cherokee_buffer_mrproper(&path);
431 cherokee_buffer_add_long10(buf, result);
432 return ret;
433 break;
436 /* Restore the memory of a domain from a file and resume the domain */
437 case domainRestore: {
438 ret_t ret = ret_ok;
439 struct stat statbuf;
440 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
441 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/memory", hdl->user.buf, hdl->vm.buf);
443 if (stat(path.buf, &statbuf) == 0) {
444 int result;
445 if (result = virDomainRestore(virConn, path.buf) != 0) {
446 conn->error_code = http_internal_error;
447 ret = ret_error;
449 cherokee_buffer_add_long10(buf, result);
450 } else {
451 conn->error_code = http_not_found;
452 ret = ret_error;
455 cherokee_buffer_mrproper(&path);
456 return ret;
457 break;
460 case domainUndefine: {
461 int result;
462 if ((result = virDomainUndefine(dom)) != 0) {
463 conn->error_code = http_internal_error; /* TODO */
466 cherokee_buffer_add_long10(buf, result);
467 break;
470 case domainAttachDevice_args: {
471 int result;
472 void *temp;
473 ret_t ret;
474 ret = cherokee_connection_parse_args (conn);
475 if (unlikely(ret < ret_ok))
476 return ret;
478 ret = cherokee_avl_get_ptr (conn->arguments, "type", &temp);
480 if (ret == ret_ok) {
481 cherokee_buffer_t xml = CHEROKEE_BUF_INIT;
483 if (strcmp(temp, "disk") == 0) {
484 void *device;
485 if ((ret = cherokee_avl_get_ptr (conn->arguments, "device", &device)) == ret_ok) {
486 void *file;
487 if ((ret = cherokee_avl_get_ptr (conn->arguments, "file", &file)) == ret_ok) {
488 cherokee_buffer_add_va (&xml, VIRT_DISK_XML, file, device);
489 } else {
490 virStorageVolPtr volume = virt_get_vol_by_args(hdl, virConn, 1);
492 if (volume == NULL) {
493 return ret_error;
496 file = virStorageVolGetPath(volume);
497 cherokee_buffer_add_va (&xml, VIRT_DISK_XML, file, device);
498 free(file);
500 virStorageVolFree(volume);
501 ret = ret_ok;
506 else if (strcmp(temp, "interface") == 0) {
507 void *mac, *ip;
508 if ((ret = cherokee_avl_get_ptr (conn->arguments, "mac", &mac)) == ret_ok && (ret = cherokee_avl_get_ptr (conn->arguments, "ip", &ip)) == ret_ok) {
509 cherokee_buffer_add_va (&xml, VIRT_INTERFACE_XML, mac, ip);
510 } else {
511 char *mac = NULL;
512 char *ip = NULL;
513 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
514 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
515 if (getNewMac("dv28", domu.buf, &mac, &ip) == 0)
516 cherokee_buffer_add_va (&xml, VIRT_INTERFACE_XML, mac, ip);
517 cherokee_buffer_mrproper(&domu);
521 if (ret == ret_ok && (result = virDomainAttachDevice(dom, (const char *) xml.buf)) == 0) {
522 ret = ret_ok;
523 } else {
524 conn->error_code = http_internal_error;
525 return ret_error;
528 cherokee_buffer_add_long10(buf, result);
529 cherokee_buffer_mrproper(&xml);
530 } else {
531 conn->error_code = http_bad_request;
532 return ret_error;
535 break;
538 case domainAttachDevice: {
539 off_t postl;
540 int result;
541 cherokee_buffer_t post = CHEROKEE_BUF_INIT;
542 cherokee_post_get_len (&conn->post, &postl);
543 cherokee_post_walk_read (&conn->post, &post, (cuint_t) postl);
544 if ((result = virDomainAttachDevice(dom, (const char *) post.buf)) != 0)
545 conn->error_code = http_internal_error;
547 cherokee_buffer_mrproper(&post);
548 cherokee_buffer_add_long10(buf, result);
549 break;
553 case domainGetXMLDesc: {
554 char *xml = virDomainGetXMLDesc(dom, 0);
555 cherokee_buffer_add(buf, xml, strlen(xml));
556 free(xml);
557 break;
561 case domainDetachDevice: {
562 off_t postl;
563 int result;
564 ret_t ret;
565 cherokee_buffer_t post = CHEROKEE_BUF_INIT;
566 cherokee_post_get_len (&conn->post, &postl);
567 cherokee_post_walk_read (&conn->post, &post, (cuint_t) postl);
569 xmlDocPtr doc = xmlParseMemory((const char *) post.buf, post.len);
570 if (doc == NULL) {
571 conn->error_code = http_bad_request;
572 ret = ret_error;
573 } else
574 ret = ret_ok;
576 if (ret == ret_ok) {
577 if ((result = virDomainDetachDevice(dom, (const char *) post.buf)) != 0) {
578 conn->error_code = http_internal_error;
579 /* TODO: betere afhandeling */
581 xmlXPathContextPtr context = xmlXPathNewContext(doc);
582 if (context != NULL) {
583 xmlXPathObjectPtr obj = xmlXPathEval("string(//interface/mac/@address)", context);
584 xmlXPathFreeContext(context);
585 if ((obj != NULL) && (obj->type == XPATH_STRING) &&
586 (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
587 removeOldMac("dv28", (char *) obj->stringval);
589 xmlXPathFreeObject(obj);
592 xmlFreeDoc(doc);
593 xmlCleanupParser();
596 cherokee_buffer_mrproper(&post);
597 cherokee_buffer_add_long10(buf, result);
598 break;
601 case domainGetID: {
602 cherokee_buffer_add_ulong10(buf, virDomainGetID(dom));
603 break;
605 case domainGetMaxMemory: {
606 cherokee_buffer_add_ulong10(buf, virDomainGetMaxMemory (dom));
607 break;
609 case domainGetMaxVcpus: {
610 cherokee_buffer_add_long10(buf, virDomainGetMaxVcpus (dom));
611 break;
613 case domainGetName: {
614 const char *name = virDomainGetName (dom);
615 cherokee_buffer_add(buf, name, strlen(name));
616 break;
618 case domainGetUUID: {
619 unsigned char uuid[VIR_UUID_BUFLEN];
620 if (virDomainGetUUID(dom, uuid) == 0) {
621 cherokee_buffer_add_str(buf, uuid);
622 } else {
623 conn->error_code = http_internal_error;
624 return ret_error;
626 break;
628 case domainGetUUIDString: {
629 unsigned char uuid[VIR_UUID_STRING_BUFLEN];
630 if (virDomainGetUUIDString(dom, uuid) == 0) {
631 cherokee_buffer_add_str(buf, uuid);
632 } else {
633 conn->error_code = http_internal_error;
634 return ret_error;
636 break;
639 case domainCreate: {
640 cherokee_buffer_add_long10(buf, virDomainCreate (dom));
641 break;
644 case domainDestroy: {
645 cherokee_buffer_add_long10(buf, virDomainDestroy (dom));
646 break;
649 case domainReboot: {
650 cherokee_buffer_add_long10(buf, virDomainReboot (dom, 0));
651 break;
654 case domainShutdown: {
655 cherokee_buffer_add_long10(buf, virDomainShutdown (dom));
656 break;
660 case domainGetOSType: {
661 char *ostype = virDomainGetOSType(dom);
662 cherokee_buffer_add(buf, ostype, strlen(ostype));
663 free(ostype);
664 break;
671 if (hdl->action == domainUndefine) {
672 /* Remove VM data from filesystem */
673 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
674 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/index.xml", hdl->user.buf, hdl->vm.buf);
675 unlink(path.buf); /* TODO: instead of delet replace */
676 cherokee_buffer_mrproper(&path);
677 } else {
678 save_xml(hdl, dom, NULL);
681 return ret_ok;
684 static virStoragePoolPtr
685 virt_get_pool_by_args(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
686 cherokee_connection_t *conn = HANDLER_CONN(hdl);
687 virStoragePoolPtr pool;
688 void *temp;
689 ret_t ret;
691 ret = cherokee_avl_get_ptr (conn->arguments, "pool", &temp);
692 if (unlikely(ret < ret_ok)) {
693 conn->error_code = http_bad_request;
694 return NULL;
697 pool = virStoragePoolLookupByName(virConn, temp);
699 return pool;
702 static virStorageVolPtr
703 virt_get_vol_by_args(cherokee_handler_virt_t *hdl, virConnectPtr virConn, unsigned short int prefix) {
704 cherokee_connection_t *conn = HANDLER_CONN(hdl);
705 virStoragePoolPtr pool;
706 virStorageVolPtr volume;
707 void *temp;
708 ret_t ret;
710 pool = virt_get_pool_by_args(hdl, virConn);
712 if (pool == NULL) {
713 conn->error_code = http_not_found;
714 return NULL;
717 virStoragePoolRefresh(pool, 0); /* TODO: might be better to do it outside */
719 ret = cherokee_avl_get_ptr (conn->arguments, "volume", &temp);
720 if (unlikely(ret < ret_ok)) {
721 conn->error_code = http_bad_request;
722 return NULL;
725 if (prefix == 1) {
726 cherokee_buffer_t fullvol = CHEROKEE_BUF_INIT;
727 cherokee_buffer_add_va (&fullvol, "%s_%s", hdl->user.buf, temp);
728 volume = virStorageVolLookupByName(pool, fullvol.buf);
729 cherokee_buffer_mrproper(&fullvol);
730 } else {
731 volume = virStorageVolLookupByName(pool, temp);
734 if (volume == NULL)
735 conn->error_code = http_not_found;
737 virStoragePoolFree(pool);
739 return volume;
742 /* This function is the home for all functions that need a working
743 * pool/volume combination */
744 static ret_t
745 virt_pool_vol(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
746 cherokee_buffer_t *buf = &HDL_AVAHI(hdl)->buffer;
747 cherokee_connection_t *conn = HANDLER_CONN(hdl);
748 virStorageVolPtr volume;
749 ret_t ret = ret_ok;
751 /* We only allow clone to run 'unsafe', others get prefixed */
752 volume = virt_get_vol_by_args(hdl, virConn, (hdl->action != storageVolCloneXML_args));
754 /* If the volume doesn't exist, no point to continue */
755 if (volume == NULL)
756 return ret_error;
758 switch (hdl->action) {
759 /* Sets the password of a specific volume, requires the password= option */
760 case storageVolSetPassword_args: {
761 void *temp;
762 ret = cherokee_avl_get_ptr (conn->arguments, "password", &temp);
763 if (unlikely(ret < ret_ok)) {
764 conn->error_code = http_bad_request;
765 goto virt_pool_vol_cleanup;
768 cherokee_buffer_add_long10(buf, execl("/usr/sbin/passwdchanger.sh", virStorageVolGetKey(volume), temp, NULL));
769 break;
772 /* Removes a volume */
773 case storageVolDelete_args: {
774 cherokee_buffer_add_long10(buf, virStorageVolDelete(volume, 0));
775 break;
778 /* Gives a description of a storage volume in XML */
779 case storageVolGetXMLDesc_args: {
780 char *xml = virStorageVolGetXMLDesc(volume, 0);
781 cherokee_buffer_add(buf, xml, strlen(xml));
782 free(xml);
783 break;
786 /* Clones a volume, insecure method! requires a new name= */
787 case storageVolCloneXML_args: {
788 void *name;
789 cherokee_buffer_t busy = CHEROKEE_BUF_INIT;
791 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
792 if (unlikely(ret < ret_ok)) {
793 conn->error_code = http_bad_request;
794 goto virt_pool_vol_cleanup;
797 cherokee_buffer_add_va (&busy, "/mnt/images/queue/%s_%s.queued", hdl->user.buf, name);
799 if (unlikely(symlink(virStorageVolGetKey(volume), busy.buf) == 1)) {
800 conn->error_code = http_internal_error;
801 goto virt_pool_vol_cleanup;
804 cherokee_buffer_mrproper(&busy);
805 cherokee_buffer_add_str(buf, "QUEUED");
807 break;
811 virt_pool_vol_cleanup:
812 /* And in the end we need to free the volume we have used */
813 virStorageVolFree(volume);
815 return ret;
818 static ret_t
819 virt_virt_new(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
820 cherokee_buffer_t *buf = &HDL_AVAHI(hdl)->buffer;
821 cherokee_connection_t *conn = HANDLER_CONN(hdl);
822 cherokee_buffer_t xml = CHEROKEE_BUF_INIT;
823 virStoragePoolPtr pool = NULL;
824 ret_t ret = ret_ok;
826 switch (hdl->action) {
827 case storageVolCreateXML_args: {
828 void *temp, *type, *name, *unit;
829 unsigned long int capacity, allocation;
831 ret = cherokee_avl_get_ptr (conn->arguments, "pool", &temp);
832 if (unlikely(ret < ret_ok)) {
833 conn->error_code = http_bad_request;
834 goto virt_virt_new_cleanup;
837 pool = virStoragePoolLookupByName(virConn, temp);
839 if (pool == NULL) {
840 conn->error_code = http_not_found;
841 goto virt_virt_new_cleanup;
844 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
845 if (unlikely(ret < ret_ok)) {
846 conn->error_code = http_bad_request;
847 goto virt_virt_new_cleanup;
850 ret = cherokee_avl_get_ptr (conn->arguments, "type", &type);
851 if (unlikely(ret < ret_ok)) {
852 conn->error_code = http_bad_request;
853 goto virt_virt_new_cleanup;
856 ret = cherokee_avl_get_ptr (conn->arguments, "unit", &unit);
857 if (unlikely(ret < ret_ok)) {
858 conn->error_code = http_bad_request;
859 goto virt_virt_new_cleanup;
862 ret = cherokee_avl_get_ptr (conn->arguments, "allocation", &temp);
863 if (unlikely(ret < ret_ok)) {
864 conn->error_code = http_bad_request;
865 goto virt_virt_new_cleanup;
868 allocation = strtoul(temp, NULL, 10);
869 if (errno == ERANGE) {
870 conn->error_code = http_bad_request;
871 ret = ret_error;
872 goto virt_virt_new_cleanup;
875 ret = cherokee_avl_get_ptr (conn->arguments, "capacity", &temp);
876 if (unlikely(ret < ret_ok)) {
877 conn->error_code = http_bad_request;
878 goto virt_virt_new_cleanup;
881 capacity = strtoul(temp, NULL, 10);
882 if (errno == ERANGE || capacity == 0) {
883 conn->error_code = http_bad_request;
884 ret = ret_error;
885 goto virt_virt_new_cleanup;
888 cherokee_buffer_add_va (&xml, VIRT_STORAGE_XML, type, hdl->user.buf, name, allocation, unit, capacity, hdl->user.buf, name, hdl->user.buf, name);
889 break;
892 case domainDefineXML_args: {
893 ret_t ret;
894 unsigned int i;
895 unsigned long vcpu = 0, interface = 0, memory = 0;
897 void *temp = NULL;
899 ret = cherokee_avl_get_ptr (conn->arguments, "vcpu", &temp);
900 if (ret == ret_ok)
901 vcpu = strtoul(temp, (char **) NULL, 10);
903 if (ret != ret_ok || errno == ERANGE || vcpu == 0) {
904 conn->error_code = http_internal_error;
905 goto virt_virt_new_cleanup;
909 ret = cherokee_avl_get_ptr (conn->arguments, "memory", &temp);
910 if (ret == ret_ok)
911 memory = strtoul(temp, (char **) NULL, 10);
913 if (ret != ret_ok || errno == ERANGE || memory == 0) {
914 conn->error_code = http_internal_error;
915 goto virt_virt_new_cleanup;
919 ret = cherokee_avl_get_ptr (conn->arguments, "interface", &temp);
920 if (ret == ret_ok)
921 interface = strtoul(temp, (char **) NULL, 10);
923 if (ret != ret_ok || errno == ERANGE) {
924 conn->error_code = http_internal_error;
925 goto virt_virt_new_cleanup;
928 cherokee_buffer_t xml_interfaces = CHEROKEE_BUF_INIT;
929 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
930 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
932 for (i = 0; i < interface; i++) {
933 char *mac = NULL;
934 char *ip = NULL;
935 if (getNewMac("dv28", domu.buf, &mac, &ip) == 0)
936 cherokee_buffer_add_va (&xml_interfaces, VIRT_INTERFACE_XML, mac, ip);
939 cherokee_buffer_mrproper(&domu);
941 cherokee_buffer_add_va (&xml, VIRT_DOMAIN_XML,
942 hdl->user.buf, hdl->vm.buf, memory, vcpu, xml_interfaces.buf);
944 cherokee_buffer_mrproper(&xml_interfaces);
945 break;
948 case storageVolCreateXML:
949 case domainDefineXML: {
950 off_t postl;
951 cherokee_post_get_len (&conn->post, &postl);
952 cherokee_post_walk_read (&conn->post, &xml, (cuint_t) postl);
953 break;
957 switch (hdl->action) {
958 case domainDefineXML_args:
959 case domainDefineXML: {
960 virDomainPtr result = virDomainDefineXML(virConn, (const char *) xml.buf);
962 if (result == NULL) {
963 /* TODO: vrij maken eventuele uitgegeven macs! */
964 conn->error_code = http_internal_error;
965 goto virt_virt_new_cleanup;
968 save_xml(hdl, result, buf);
970 virDomainFree(result);
972 break;
975 case storageVolCreateXML_args:
976 case storageVolCreateXML: {
977 virStorageVolPtr vol = virStorageVolCreateXML(pool, xml.buf, 0);
979 if (vol == NULL) {
980 cherokee_buffer_add_long10(buf, -1);
981 goto virt_virt_new_cleanup;
984 virStorageVolFree(vol);
986 cherokee_buffer_add_long10(buf, 0);
987 break;
991 virt_virt_new_cleanup:
992 cherokee_buffer_mrproper(&xml);
994 if (pool)
995 virStoragePoolFree(pool);
997 return ret;
1001 static ret_t
1002 virt_virt_do(cherokee_handler_virt_t *hdl, cherokee_buffer_t *domu, cherokee_buffer_t *uri)
1004 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1006 ret_t ret = ret_error;
1007 virConnectPtr virConn = NULL;
1010 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE)
1011 virConn = virConnectOpen (uri->buf);
1013 if (!virConn && !(virConn = virConnectOpenReadOnly (uri->buf))) {
1014 conn->error_code = http_service_unavailable;
1015 return ret_error;
1018 /* We use the webserver methods to parse the querystring */
1019 /* Maybe do something smart with ENUM, begin_args, end_args... */
1020 if ((hdl->action == domainDefineXML_args) ||
1021 (hdl->action == storageVolGetXMLDesc_args) ||
1022 (hdl->action == storageVolDelete_args) ||
1023 (hdl->action == storageVolSetPassword_args) ||
1024 (hdl->action == storageVolCloneXML_args) ||
1025 (hdl->action == storageVolCreateXML_args)) {
1026 ret = cherokee_connection_parse_args (conn);
1027 if (unlikely(ret < ret_ok)) {
1028 conn->error_code = http_internal_error;
1029 return ret_error;
1033 switch (hdl->action) {
1034 case storageVolDelete_args:
1035 case storageVolSetPassword_args:
1036 case storageVolGetXMLDesc_args:
1037 case storageVolCloneXML_args:
1038 ret = virt_pool_vol(hdl, virConn);
1039 break;
1041 case storageVolCreateXML_args:
1042 case storageVolCreateXML:
1043 case domainDefineXML_args:
1044 case domainDefineXML:
1045 ret = virt_virt_new(hdl, virConn);
1046 break;
1048 default: {
1049 virDomainPtr dom;
1050 if ((dom = virDomainLookupByName(virConn, domu->buf)) == NULL) {
1051 conn->error_code = http_not_found;
1052 } else {
1053 ret = virt_virt_function(hdl, dom, virConn);
1054 virDomainFree(dom);
1059 virConnectClose(virConn);
1060 return ret;
1063 static ret_t
1064 virt_while (cherokee_buffer_t *key, void *value, void *param) {
1065 cherokee_handler_virt_t * hdl = param;
1066 // cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
1067 // cherokee_buffer_add_va (&uri, "xen://%s/", ((cherokee_buffer_t *) value)->buf);
1068 // virt_virt_do((cherokee_handler_virt_t *) param, key, &uri);
1069 // cherokee_buffer_mrproper(&uri);
1071 cherokee_buffer_add_va (&hdl->buffer, "<domain><name>%s</name></domain>", key->buf);
1073 return ret_ok;
1076 static ret_t
1077 virt_while_user (cherokee_buffer_t *key, void *value, void *param) {
1078 cherokee_handler_virt_t *hdl = param;
1079 if (key->len > hdl->user.len && key->buf[hdl->user.len] == '_' && strncmp(key->buf, hdl->user.buf, hdl->user.len) == 0)
1080 return virt_while (key, value, param);
1082 return ret_ok;
1085 static ret_t
1086 virt_build_page (cherokee_handler_virt_t *hdl)
1088 ret_t ret;
1089 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1090 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
1091 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
1093 if (hdl->action > xml && HDL_VIRT_PROPS(hdl)->xsl.len > 0)
1094 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);
1097 /* First, block the event loop */
1098 avahi_threaded_poll_lock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
1099 switch (hdl->action) {
1100 case showall:
1101 cherokee_buffer_add_str (&hdl->buffer, "<domains>");
1102 cherokee_avl_while(&HDL_AVAHI_PROPS(hdl)->entries, virt_while, hdl, NULL, NULL);
1103 cherokee_buffer_add_str (&hdl->buffer, "</domains>");
1104 ret = ret_ok;
1105 break;
1107 case showuservms: {
1108 cherokee_buffer_add_str (&hdl->buffer, "<domains>");
1109 cherokee_avl_while(&HDL_AVAHI_PROPS(hdl)->entries, virt_while_user, hdl, NULL, NULL);
1110 cherokee_buffer_add_str (&hdl->buffer, "</domains>");
1111 ret = ret_ok;
1112 break;
1115 default: {
1116 cherokee_buffer_t *hostname = NULL;
1117 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
1118 ret = cherokee_avl_get_ptr(&HDL_AVAHI_PROPS(hdl)->entries, domu.buf, (void **) &hostname);
1120 if (ret == ret_not_found) {
1121 virDomainPtr virDom;
1122 virConnectPtr virConn;
1123 cherokee_buffer_add_va (&uri, "xen://%s/", HDL_VIRT_PROPS(hdl)->virt); // TODO: change!
1125 /* If we have the read only parameter, we will set up a connection to the
1126 * Hypervisor here. */
1127 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE)
1128 virConn = virConnectOpen (uri.buf);
1130 /* We should already have a connection (read only) or build a new connection
1131 * if this doesn't work, we can safely assume our services is fubar */
1132 if (!virConn && !(virConn = virConnectOpenReadOnly (uri.buf))) {
1133 conn->error_code = http_service_unavailable;
1134 return ret_error;
1137 /* We lookup if there is a domain somewhere with this name */
1138 if ((virDom = virDomainLookupByName(virConn, domu.buf)) == NULL) {
1139 /* If the domain is not found on the network the only possible
1140 * command that is possible would be to Define it */
1141 if (hdl->action == domainDefineXML_args || hdl->action == domainDefineXML) {
1142 ret = ret_ok;
1143 } else {
1144 /* Otherwise we don't have anything to do and we should
1145 * return an error */
1146 hdl->action = nothing;
1147 conn->error_code = http_not_found;
1148 ret = ret_error;
1150 } else {
1151 /* We don't want te recreate things that already found on the network
1152 * first undefine them! */
1153 if (hdl->action == domainDefineXML_args || hdl->action == domainDefineXML) {
1154 hdl->action = nothing;
1155 conn->error_code = http_bad_request;
1156 ret = ret_error;
1157 } else {
1158 /* Everything is ok, because nothing is found. */
1159 ret = ret_ok;
1162 /* Domain wasn't NULL, so we should free it here */
1163 virDomainFree(virDom);
1165 virConnectClose (virConn);
1166 } else if (ret == ret_ok) {
1167 cherokee_buffer_add_va (&uri, "xen://%s/", hostname->buf);
1168 printf("%s\n", uri.buf);
1169 } else {
1170 hdl->action = http_internal_error;
1171 hdl->action = nothing;
1172 ret = ret_error;
1177 /* Finally, unblock the event loop */
1178 avahi_threaded_poll_unlock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
1180 if (ret == ret_ok && hdl->action != showall && hdl->action != showuservms) {
1181 ret = virt_virt_do(hdl, &domu, &uri);
1184 cherokee_buffer_mrproper(&domu);
1185 cherokee_buffer_mrproper(&uri);
1188 return ret;
1192 ret_t
1193 cherokee_handler_virt_new (cherokee_handler_t **hdl, cherokee_connection_t *cnt, cherokee_module_props_t *props)
1195 ret_t ret;
1196 CHEROKEE_NEW_STRUCT (n, handler_virt);
1198 /* Init the base class
1201 cherokee_handler_init_base(HANDLER(n), cnt, HANDLER_PROPS(props), PLUGIN_INFO_HANDLER_PTR(virt));
1203 MODULE(n)->init = (handler_func_init_t) cherokee_handler_virt_init;
1204 MODULE(n)->free = (module_func_free_t) cherokee_handler_virt_free;
1205 HANDLER(n)->step = (handler_func_step_t) cherokee_handler_virt_step;
1206 HANDLER(n)->add_headers = (handler_func_add_headers_t) cherokee_handler_virt_add_headers;
1208 HANDLER(n)->support = hsupport_length | hsupport_range;
1210 ret = cherokee_buffer_init (&n->buffer);
1211 if (unlikely(ret != ret_ok))
1212 return ret;
1214 ret = cherokee_buffer_ensure_size (&n->buffer, 4*1024);
1215 if (unlikely(ret != ret_ok))
1216 return ret;
1218 *hdl = HANDLER(n);
1220 return ret_ok;