Code refactoring
[handlervirt.git] / handler_virt.c
blob624aeb7397d0640c80803d75af1f512fa9743e15
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_args;
267 else if (strcmp(this+9, "Restore") == 0) hdl->action = domainRestore_args;
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 ret_t
332 cherokee_handler_virt_step (cherokee_handler_virt_t *hdl, cherokee_buffer_t *buffer)
334 cherokee_buffer_add_buffer (buffer, &hdl->buffer);
335 return ret_eof_have_data;
338 ret_t
339 cherokee_handler_virt_add_headers (cherokee_handler_virt_t *hdl, cherokee_buffer_t *buffer)
341 cherokee_buffer_add_va (buffer, "Content-Length: %d"CRLF, hdl->buffer.len);
343 if (hdl->action > xml)
344 cherokee_buffer_add_str (buffer, "Content-Type: application/xml"CRLF);
345 else
346 cherokee_buffer_add_str (buffer, "Content-Type: text/plain"CRLF);
348 return ret_ok;
352 static ret_t
353 while_func_entries (cherokee_buffer_t *key, void *value, void *param) {
354 virConnectPtr conn = NULL;
355 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
356 cherokee_buffer_t *buf = (cherokee_buffer_t *)param;
358 cherokee_buffer_add_va (&uri, "xen://%s/", value);
360 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE && !(conn = virConnectOpen (uri.buf))) {
361 if (!(conn = virConnectOpen (uri.buf))) {
362 return ret_error;
366 if (!conn && !(conn = virConnectOpenReadOnly (uri.buf))) {
367 return ret_error;
368 } else {
369 virDomainPtr dom;
370 if (!(dom = virDomainLookupByName(conn, (const char *) key->buf))) {
371 return ret_error;
372 } else {
373 char *xml = virDomainGetXMLDesc(dom, 0);
374 cherokee_buffer_add(buf, xml, strlen(xml));
376 virConnectClose(conn);
379 cherokee_buffer_mrproper(&uri);
381 return ret_ok;
384 static ret_t save_xml(cherokee_handler_virt_t *hdl, virDomainPtr result, cherokee_buffer_t *buf) {
385 ret_t ret = ret_error;
386 FILE *fd;
387 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
388 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s", hdl->user.buf);
390 mkdir(path.buf, 0755);
391 cherokee_buffer_add_va (&path, "/%s", hdl->vm.buf);
393 mkdir(path.buf, 0755);
394 cherokee_buffer_add_str (&path, "/index.xml");
396 if ((fd = fopen(path.buf, "w")) == NULL) {
397 perror(__func__);
398 } else {
399 char *output = virDomainGetXMLDesc(result, 0);
400 fwrite(output, strlen(output), sizeof(char), fd);
401 fclose(fd);
402 if (buf)
403 cherokee_buffer_add(buf, output, strlen(output));
404 free(output);
405 ret = ret_ok;
407 cherokee_buffer_mrproper(&path);
408 return ret;
412 static ret_t
413 virt_virt_function(cherokee_handler_virt_t *hdl, virDomainPtr dom, virConnectPtr virConn) {
414 cherokee_connection_t *conn = HANDLER_CONN(hdl);
415 cherokee_buffer_t *buf = &HDL_AVAHI(hdl)->buffer;
417 switch (hdl->action) {
419 case domainUndefine: {
420 int result;
421 if ((result = virDomainUndefine(dom)) != 0) {
422 conn->error_code = http_internal_error;
425 cherokee_buffer_add_long10(buf, result);
426 break;
429 case domainAttachDevice_args: {
430 int result;
431 void *temp;
432 ret_t ret;
433 ret = cherokee_connection_parse_args (conn);
434 if (unlikely(ret < ret_ok))
435 return ret;
437 ret = cherokee_avl_get_ptr (conn->arguments, "type", &temp);
439 if (ret == ret_ok) {
440 cherokee_buffer_t xml = CHEROKEE_BUF_INIT;
442 if (strcmp(temp, "disk") == 0) {
443 void *device;
444 if ((ret = cherokee_avl_get_ptr (conn->arguments, "device", &device)) == ret_ok) {
445 void *file;
446 if ((ret = cherokee_avl_get_ptr (conn->arguments, "file", &file)) == ret_ok) {
447 cherokee_buffer_add_va (&xml, VIRT_DISK_XML, file, device);
448 } else {
449 void *poolstr, *volumestr;
450 if ((ret = cherokee_avl_get_ptr (conn->arguments, "pool", &poolstr)) == ret_ok &&
451 (ret = cherokee_avl_get_ptr (conn->arguments, "volume", &volumestr)) == ret_ok) {
452 cherokee_buffer_t fullvol = CHEROKEE_BUF_INIT;
453 virStoragePoolPtr pool;
454 virStorageVolPtr volume;
456 pool = virStoragePoolLookupByName(virConn, poolstr);
457 if (pool == NULL) {
458 conn->error_code = http_not_found;
459 return ret_error;
462 cherokee_buffer_add_va (&fullvol, "%s_%s", hdl->user.buf, volumestr);
463 volume = virStorageVolLookupByName(pool, fullvol.buf);
465 cherokee_buffer_mrproper(&fullvol);
466 virStoragePoolFree(pool);
468 if (volume == NULL) {
469 conn->error_code = http_not_found;
470 return ret_error;
472 file = virStorageVolGetPath(volume);
473 cherokee_buffer_add_va (&xml, VIRT_DISK_XML, file, device);
474 free(file);
476 virStorageVolFree(volume);
477 ret = ret_ok;
478 } else {
479 conn->error_code = http_bad_request;
480 return ret_error;
486 else if (strcmp(temp, "interface") == 0) {
487 void *mac, *ip;
488 if ((ret = cherokee_avl_get_ptr (conn->arguments, "mac", &mac)) == ret_ok && (ret = cherokee_avl_get_ptr (conn->arguments, "ip", &ip)) == ret_ok) {
489 cherokee_buffer_add_va (&xml, VIRT_INTERFACE_XML, mac, ip);
490 } else {
491 char *mac = NULL;
492 char *ip = NULL;
493 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
494 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
495 if (getNewMac("dv28", domu.buf, &mac, &ip) == 0)
496 cherokee_buffer_add_va (&xml, VIRT_INTERFACE_XML, mac, ip);
497 cherokee_buffer_mrproper(&domu);
501 if (ret == ret_ok && (result = virDomainAttachDevice(dom, (const char *) xml.buf)) == 0) {
502 ret = ret_ok;
503 } else {
504 conn->error_code = http_internal_error;
505 return ret_error;
508 cherokee_buffer_add_long10(buf, result);
509 cherokee_buffer_mrproper(&xml);
510 } else {
511 conn->error_code = http_bad_request;
512 return ret_error;
515 break;
518 case domainAttachDevice: {
519 off_t postl;
520 int result;
521 cherokee_buffer_t post = CHEROKEE_BUF_INIT;
522 cherokee_post_get_len (&conn->post, &postl);
523 cherokee_post_walk_read (&conn->post, &post, (cuint_t) postl);
524 if ((result = virDomainAttachDevice(dom, (const char *) post.buf)) != 0)
525 conn->error_code = http_internal_error;
527 cherokee_buffer_mrproper(&post);
528 cherokee_buffer_add_long10(buf, result);
529 break;
533 case domainGetXMLDesc: {
534 char *xml = virDomainGetXMLDesc(dom, 0);
535 cherokee_buffer_add(buf, xml, strlen(xml));
536 free(xml);
537 break;
541 case domainDetachDevice: {
542 off_t postl;
543 int result;
544 ret_t ret;
545 cherokee_buffer_t post = CHEROKEE_BUF_INIT;
546 cherokee_post_get_len (&conn->post, &postl);
547 cherokee_post_walk_read (&conn->post, &post, (cuint_t) postl);
549 xmlDocPtr doc = xmlParseMemory((const char *) post.buf, post.len);
550 if (doc == NULL) {
551 conn->error_code = http_bad_request;
552 ret = ret_error;
553 } else
554 ret = ret_ok;
556 if (ret == ret_ok) {
557 if ((result = virDomainDetachDevice(dom, (const char *) post.buf)) != 0) {
558 conn->error_code = http_internal_error;
559 /* TODO: betere afhandeling */
561 xmlXPathContextPtr context = xmlXPathNewContext(doc);
562 if (context != NULL) {
563 xmlXPathObjectPtr obj = xmlXPathEval("string(//interface/mac/@address)", context);
564 xmlXPathFreeContext(context);
565 if ((obj != NULL) && (obj->type == XPATH_STRING) &&
566 (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
567 removeOldMac("dv28", (char *) obj->stringval);
569 xmlXPathFreeObject(obj);
572 xmlFreeDoc(doc);
573 xmlCleanupParser();
576 cherokee_buffer_mrproper(&post);
577 cherokee_buffer_add_long10(buf, result);
578 break;
581 case domainGetID: {
582 cherokee_buffer_add_ulong10(buf, virDomainGetID(dom));
583 break;
585 case domainGetMaxMemory: {
586 cherokee_buffer_add_ulong10(buf, virDomainGetMaxMemory (dom));
587 break;
589 case domainGetMaxVcpus: {
590 cherokee_buffer_add_long10(buf, virDomainGetMaxVcpus (dom));
591 break;
593 case domainGetName: {
594 const char *name = virDomainGetName (dom);
595 cherokee_buffer_add(buf, name, strlen(name));
596 break;
598 case domainGetUUID: {
599 unsigned char uuid[VIR_UUID_BUFLEN];
600 if (virDomainGetUUID(dom, uuid) == 0) {
601 cherokee_buffer_add_str(buf, uuid);
602 } else {
603 conn->error_code = http_internal_error;
604 return ret_error;
606 break;
608 case domainGetUUIDString: {
609 unsigned char uuid[VIR_UUID_STRING_BUFLEN];
610 if (virDomainGetUUIDString(dom, uuid) == 0) {
611 cherokee_buffer_add_str(buf, uuid);
612 } else {
613 conn->error_code = http_internal_error;
614 return ret_error;
616 break;
619 case domainCreate: {
620 cherokee_buffer_add_long10(buf, virDomainCreate (dom));
621 break;
624 case domainDestroy: {
625 cherokee_buffer_add_long10(buf, virDomainDestroy (dom));
626 break;
629 case domainReboot: {
630 cherokee_buffer_add_long10(buf, virDomainReboot (dom, 0));
631 break;
634 case domainShutdown: {
635 cherokee_buffer_add_long10(buf, virDomainShutdown (dom));
636 break;
640 case domainGetOSType: {
641 char *ostype = virDomainGetOSType(dom);
642 cherokee_buffer_add(buf, ostype, strlen(ostype));
643 free(ostype);
644 break;
651 if (hdl->action == domainUndefine) {
652 /* Remove VM data from filesystem */
653 cherokee_buffer_t path = CHEROKEE_BUF_INIT;
654 cherokee_buffer_add_va (&path, "/mnt/netapp/users/%s/%s/index.xml", hdl->user.buf, hdl->vm.buf);
655 unlink(path.buf); /* TODO: instead of delet replace */
656 cherokee_buffer_mrproper(&path);
657 } else {
658 save_xml(hdl, dom, NULL);
661 return ret_ok;
665 static virStorageVolPtr
666 virt_get_vol_by_args(cherokee_handler_virt_t *hdl, virConnectPtr virConn, unsigned short int prefix) {
667 cherokee_connection_t *conn = HANDLER_CONN(hdl);
668 virStoragePoolPtr pool;
669 virStorageVolPtr volume;
670 void *temp;
671 ret_t ret;
673 ret = cherokee_avl_get_ptr (conn->arguments, "pool", &temp);
674 if (unlikely(ret < ret_ok)) {
675 conn->error_code = http_bad_request;
676 return NULL;
679 pool = virStoragePoolLookupByName(virConn, temp);
681 if (pool == NULL) {
682 conn->error_code = http_not_found;
683 return NULL;
686 virStoragePoolRefresh(pool, 0); /* TODO: might be better to do it outside */
688 ret = cherokee_avl_get_ptr (conn->arguments, "volume", &temp);
689 if (unlikely(ret < ret_ok)) {
690 conn->error_code = http_bad_request;
691 return NULL;
694 if (prefix == 1) {
695 cherokee_buffer_t fullvol = CHEROKEE_BUF_INIT;
696 cherokee_buffer_add_va (&fullvol, "%s_%s", hdl->user.buf, temp);
697 volume = virStorageVolLookupByName(pool, fullvol.buf);
698 cherokee_buffer_mrproper(&fullvol);
699 } else {
700 volume = virStorageVolLookupByName(pool, temp);
703 if (volume == NULL)
704 conn->error_code = http_not_found;
706 virStoragePoolFree(pool);
708 return volume;
711 /* This function is the home for all functions that need a working
712 * pool/volume combination */
713 static ret_t
714 virt_pool_vol(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
715 cherokee_buffer_t *buf = &HDL_AVAHI(hdl)->buffer;
716 cherokee_connection_t *conn = HANDLER_CONN(hdl);
717 virStorageVolPtr volume;
718 ret_t ret = ret_ok;
720 /* We only allow clone to run 'unsafe', others get prefixed */
721 volume = virt_get_vol_by_args(hdl, virConn, (hdl->action != storageVolCloneXML_args));
723 /* If the volume doesn't exist, no point to continue */
724 if (volume == NULL)
725 return ret_error;
727 switch (hdl->action) {
728 /* Sets the password of a specific volume, requires the password= option */
729 case storageVolSetPassword_args: {
730 void *temp;
731 ret = cherokee_avl_get_ptr (conn->arguments, "password", &temp);
732 if (unlikely(ret < ret_ok)) {
733 conn->error_code = http_bad_request;
734 goto virt_pool_vol_cleanup;
737 cherokee_buffer_add_long10(buf, execl("/usr/sbin/passwdchanger.sh", virStorageVolGetKey(volume), temp, NULL));
738 break;
741 /* Removes a volume */
742 case storageVolDelete_args: {
743 cherokee_buffer_add_long10(buf, virStorageVolDelete(volume, 0));
744 break;
747 /* Gives a description of a storage volume in XML */
748 case storageVolGetXMLDesc_args: {
749 char *xml = virStorageVolGetXMLDesc(volume, 0);
750 cherokee_buffer_add(buf, xml, strlen(xml));
751 free(xml);
752 break;
755 /* Clones a volume, insecure method! requires a new name=*/
756 case storageVolCloneXML_args: {
757 void *name;
758 cherokee_buffer_t busy = CHEROKEE_BUF_INIT;
760 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
761 if (unlikely(ret < ret_ok)) {
762 conn->error_code = http_bad_request;
763 goto virt_pool_vol_cleanup;
766 cherokee_buffer_add_va (&busy, "/mnt/images/queue/%s_%s.queued", hdl->user.buf, name);
768 if (unlikely(symlink(virStorageVolGetKey(volume), busy.buf) == 1)) {
769 conn->error_code = http_internal_error;
770 goto virt_pool_vol_cleanup;
773 cherokee_buffer_mrproper(&busy);
774 cherokee_buffer_add_str(buf, "QUEUED");
776 break;
780 virt_pool_vol_cleanup:
781 /* And in the end we need to free the volume we have used */
782 virStorageVolFree(volume);
784 return ret;
787 static ret_t
788 virt_virt_new(cherokee_handler_virt_t *hdl, virConnectPtr virConn) {
789 cherokee_buffer_t *buf = &HDL_AVAHI(hdl)->buffer;
790 cherokee_connection_t *conn = HANDLER_CONN(hdl);
791 cherokee_buffer_t xml = CHEROKEE_BUF_INIT;
792 virStoragePoolPtr pool = NULL;
793 ret_t ret = ret_ok;
795 switch (hdl->action) {
796 case storageVolCreateXML_args: {
797 void *temp, *type, *name, *unit;
798 unsigned long int capacity, allocation;
800 ret = cherokee_avl_get_ptr (conn->arguments, "pool", &temp);
801 if (unlikely(ret < ret_ok)) {
802 conn->error_code = http_bad_request;
803 goto virt_virt_new_cleanup;
806 pool = virStoragePoolLookupByName(virConn, temp);
808 if (pool == NULL) {
809 conn->error_code = http_not_found;
810 goto virt_virt_new_cleanup;
813 ret = cherokee_avl_get_ptr (conn->arguments, "name", &name);
814 if (unlikely(ret < ret_ok)) {
815 conn->error_code = http_bad_request;
816 goto virt_virt_new_cleanup;
819 ret = cherokee_avl_get_ptr (conn->arguments, "type", &type);
820 if (unlikely(ret < ret_ok)) {
821 conn->error_code = http_bad_request;
822 goto virt_virt_new_cleanup;
825 ret = cherokee_avl_get_ptr (conn->arguments, "unit", &unit);
826 if (unlikely(ret < ret_ok)) {
827 conn->error_code = http_bad_request;
828 goto virt_virt_new_cleanup;
831 ret = cherokee_avl_get_ptr (conn->arguments, "allocation", &temp);
832 if (unlikely(ret < ret_ok)) {
833 conn->error_code = http_bad_request;
834 goto virt_virt_new_cleanup;
837 allocation = strtoul(temp, NULL, 10);
838 if (errno == ERANGE) {
839 conn->error_code = http_bad_request;
840 ret = ret_error;
841 goto virt_virt_new_cleanup;
844 ret = cherokee_avl_get_ptr (conn->arguments, "capacity", &temp);
845 if (unlikely(ret < ret_ok)) {
846 conn->error_code = http_bad_request;
847 goto virt_virt_new_cleanup;
850 capacity = strtoul(temp, NULL, 10);
851 if (errno == ERANGE || capacity == 0) {
852 conn->error_code = http_bad_request;
853 ret = ret_error;
854 goto virt_virt_new_cleanup;
857 cherokee_buffer_add_va (&xml, VIRT_STORAGE_XML, type, hdl->user.buf, name, allocation, unit, capacity, hdl->user.buf, name, hdl->user.buf, name);
858 break;
861 case domainDefineXML_args: {
862 ret_t ret;
863 unsigned int i;
864 unsigned long vcpu = 0, interface = 0, memory = 0;
866 void *temp = NULL;
868 ret = cherokee_avl_get_ptr (conn->arguments, "vcpu", &temp);
869 if (ret == ret_ok)
870 vcpu = strtoul(temp, (char **) NULL, 10);
872 if (ret != ret_ok || errno == ERANGE || vcpu == 0) {
873 conn->error_code = http_internal_error;
874 goto virt_virt_new_cleanup;
878 ret = cherokee_avl_get_ptr (conn->arguments, "memory", &temp);
879 if (ret == ret_ok)
880 memory = strtoul(temp, (char **) NULL, 10);
882 if (ret != ret_ok || errno == ERANGE || memory == 0) {
883 conn->error_code = http_internal_error;
884 goto virt_virt_new_cleanup;
888 ret = cherokee_avl_get_ptr (conn->arguments, "interface", &temp);
889 if (ret == ret_ok)
890 interface = strtoul(temp, (char **) NULL, 10);
892 if (ret != ret_ok || errno == ERANGE) {
893 conn->error_code = http_internal_error;
894 goto virt_virt_new_cleanup;
897 cherokee_buffer_t xml_interfaces = CHEROKEE_BUF_INIT;
898 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
899 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
901 for (i = 0; i < interface; i++) {
902 char *mac = NULL;
903 char *ip = NULL;
904 if (getNewMac("dv28", domu.buf, &mac, &ip) == 0)
905 cherokee_buffer_add_va (&xml_interfaces, VIRT_INTERFACE_XML, mac, ip);
908 cherokee_buffer_mrproper(&domu);
910 cherokee_buffer_add_va (&xml, VIRT_DOMAIN_XML,
911 hdl->user.buf, hdl->vm.buf, memory, vcpu, xml_interfaces.buf);
913 cherokee_buffer_mrproper(&xml_interfaces);
914 break;
917 case storageVolCreateXML:
918 case domainDefineXML: {
919 off_t postl;
920 cherokee_post_get_len (&conn->post, &postl);
921 cherokee_post_walk_read (&conn->post, &xml, (cuint_t) postl);
922 break;
926 switch (hdl->action) {
927 case domainDefineXML_args:
928 case domainDefineXML: {
929 virDomainPtr result = virDomainDefineXML(virConn, (const char *) xml.buf);
931 if (result == NULL) {
932 /* TODO: vrij maken eventuele uitgegeven macs! */
933 conn->error_code = http_internal_error;
934 goto virt_virt_new_cleanup;
937 save_xml(hdl, result, buf);
939 virDomainFree(result);
941 break;
944 case storageVolCreateXML_args:
945 case storageVolCreateXML: {
946 virStorageVolPtr vol = virStorageVolCreateXML(pool, xml.buf, 0);
948 if (vol == NULL) {
949 cherokee_buffer_add_long10(buf, -1);
950 goto virt_virt_new_cleanup;
953 virStorageVolFree(vol);
955 cherokee_buffer_add_long10(buf, 0);
956 break;
960 virt_virt_new_cleanup:
961 cherokee_buffer_mrproper(&xml);
963 if (pool)
964 virStoragePoolFree(pool);
966 return ret;
970 static ret_t
971 virt_virt_do(cherokee_handler_virt_t *hdl, cherokee_buffer_t *domu, cherokee_buffer_t *uri)
973 cherokee_connection_t *conn = HANDLER_CONN(hdl);
975 ret_t ret = ret_error;
976 virConnectPtr virConn = NULL;
979 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE)
980 virConn = virConnectOpen (uri->buf);
982 if (!virConn && !(virConn = virConnectOpenReadOnly (uri->buf))) {
983 conn->error_code = http_service_unavailable;
984 return ret_error;
987 /* We use the webserver methods to parse the querystring */
988 /* Maybe do something smart with ENUM, begin_args, end_args... */
989 if ((hdl->action == domainDefineXML_args) ||
990 (hdl->action == domainSave_args) ||
991 (hdl->action == domainRestore_args) ||
992 (hdl->action == storageVolGetXMLDesc_args) ||
993 (hdl->action == storageVolDelete_args) ||
994 (hdl->action == storageVolSetPassword_args) ||
995 (hdl->action == storageVolCloneXML_args) ||
996 (hdl->action == storageVolCreateXML_args)) {
997 ret = cherokee_connection_parse_args (conn);
998 if (unlikely(ret < ret_ok)) {
999 conn->error_code = http_internal_error;
1000 return ret_error;
1004 switch (hdl->action) {
1005 case domainSave_args:
1006 case domainRestore_args:
1007 case storageVolDelete_args:
1008 case storageVolSetPassword_args:
1009 case storageVolGetXMLDesc_args:
1010 case storageVolCloneXML_args:
1011 ret = virt_pool_vol(hdl, virConn);
1012 break;
1014 case storageVolCreateXML_args:
1015 case storageVolCreateXML:
1016 case domainDefineXML_args:
1017 case domainDefineXML:
1018 ret = virt_virt_new(hdl, virConn);
1019 break;
1021 default: {
1022 virDomainPtr dom;
1023 if ((dom = virDomainLookupByName(virConn, domu->buf)) == NULL) {
1024 conn->error_code = http_not_found;
1025 } else {
1026 ret = virt_virt_function(hdl, dom, virConn);
1027 virDomainFree(dom);
1032 virConnectClose(virConn);
1033 return ret;
1036 static ret_t
1037 virt_while (cherokee_buffer_t *key, void *value, void *param) {
1038 cherokee_handler_virt_t * hdl = param;
1039 // cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
1040 // cherokee_buffer_add_va (&uri, "xen://%s/", ((cherokee_buffer_t *) value)->buf);
1041 // virt_virt_do((cherokee_handler_virt_t *) param, key, &uri);
1042 // cherokee_buffer_mrproper(&uri);
1044 cherokee_buffer_add_va (&hdl->buffer, "<domain><name>%s</name></domain>", key->buf);
1046 return ret_ok;
1049 static ret_t
1050 virt_while_user (cherokee_buffer_t *key, void *value, void *param) {
1051 cherokee_handler_virt_t *hdl = param;
1052 if (key->len > hdl->user.len && key->buf[hdl->user.len] == '_' && strncmp(key->buf, hdl->user.buf, hdl->user.len) == 0)
1053 return virt_while (key, value, param);
1055 return ret_ok;
1058 static ret_t
1059 virt_build_page (cherokee_handler_virt_t *hdl)
1061 ret_t ret;
1062 cherokee_connection_t *conn = HANDLER_CONN(hdl);
1063 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
1064 cherokee_buffer_t domu = CHEROKEE_BUF_INIT;
1066 if (hdl->action > xml && HDL_VIRT_PROPS(hdl)->xsl.len > 0)
1067 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);
1070 /* First, block the event loop */
1071 avahi_threaded_poll_lock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
1072 switch (hdl->action) {
1073 case showall:
1074 cherokee_buffer_add_str (&hdl->buffer, "<domains>");
1075 cherokee_avl_while(&HDL_AVAHI_PROPS(hdl)->entries, virt_while, hdl, NULL, NULL);
1076 cherokee_buffer_add_str (&hdl->buffer, "</domains>");
1077 ret = ret_ok;
1078 break;
1080 case showuservms: {
1081 cherokee_buffer_add_str (&hdl->buffer, "<domains>");
1082 cherokee_avl_while(&HDL_AVAHI_PROPS(hdl)->entries, virt_while_user, hdl, NULL, NULL);
1083 cherokee_buffer_add_str (&hdl->buffer, "</domains>");
1084 ret = ret_ok;
1085 break;
1088 default: {
1089 cherokee_buffer_t *hostname = NULL;
1090 cherokee_buffer_add_va (&domu, "%s_%s", hdl->user.buf, hdl->vm.buf);
1091 ret = cherokee_avl_get_ptr(&HDL_AVAHI_PROPS(hdl)->entries, domu.buf, (void **) &hostname);
1093 if (ret == ret_not_found) {
1094 virDomainPtr virDom;
1095 virConnectPtr virConn;
1096 cherokee_buffer_add_va (&uri, "xen://%s/", HDL_VIRT_PROPS(hdl)->virt); // TODO: change!
1098 /* If we have the read only parameter, we will set up a connection to the
1099 * Hypervisor here. */
1100 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE)
1101 virConn = virConnectOpen (uri.buf);
1103 /* We should already have a connection (read only) or build a new connection
1104 * if this doesn't work, we can safely assume our services is fubar */
1105 if (!virConn && !(virConn = virConnectOpenReadOnly (uri.buf))) {
1106 conn->error_code = http_service_unavailable;
1107 return ret_error;
1110 /* We lookup if there is a domain somewhere with this name */
1111 if ((virDom = virDomainLookupByName(virConn, domu.buf)) == NULL) {
1112 /* If the domain is not found on the network the only possible
1113 * command that is possible would be to Define it */
1114 if (hdl->action == domainDefineXML_args || hdl->action == domainDefineXML) {
1115 ret = ret_ok;
1116 } else {
1117 /* Otherwise we don't have anything to do and we should
1118 * return an error */
1119 hdl->action = nothing;
1120 conn->error_code = http_not_found;
1121 ret = ret_error;
1123 } else {
1124 /* We don't want te recreate things that already found on the network
1125 * first undefine them! */
1126 if (hdl->action == domainDefineXML_args || hdl->action == domainDefineXML) {
1127 hdl->action = nothing;
1128 conn->error_code = http_bad_request;
1129 ret = ret_error;
1130 } else {
1131 /* Everything is ok, because nothing is found. */
1132 ret = ret_ok;
1135 /* Domain wasn't NULL, so we should free it here */
1136 virDomainFree(virDom);
1138 virConnectClose (virConn);
1139 } else if (ret == ret_ok) {
1140 cherokee_buffer_add_va (&uri, "xen://%s/", hostname->buf);
1141 printf("%s\n", uri.buf);
1142 } else {
1143 hdl->action = http_internal_error;
1144 hdl->action = nothing;
1145 ret = ret_error;
1150 /* Finally, unblock the event loop */
1151 avahi_threaded_poll_unlock(HDL_AVAHI_PROPS(hdl)->threaded_poll);
1153 if (ret == ret_ok && hdl->action != showall && hdl->action != showuservms) {
1154 ret = virt_virt_do(hdl, &domu, &uri);
1157 cherokee_buffer_mrproper(&domu);
1158 cherokee_buffer_mrproper(&uri);
1161 return ret;
1165 ret_t
1166 cherokee_handler_virt_new (cherokee_handler_t **hdl, cherokee_connection_t *cnt, cherokee_module_props_t *props)
1168 ret_t ret;
1169 CHEROKEE_NEW_STRUCT (n, handler_virt);
1171 /* Init the base class
1174 cherokee_handler_init_base(HANDLER(n), cnt, HANDLER_PROPS(props), PLUGIN_INFO_HANDLER_PTR(virt));
1176 MODULE(n)->init = (handler_func_init_t) cherokee_handler_virt_init;
1177 MODULE(n)->free = (module_func_free_t) cherokee_handler_virt_free;
1178 HANDLER(n)->step = (handler_func_step_t) cherokee_handler_virt_step;
1179 HANDLER(n)->add_headers = (handler_func_add_headers_t) cherokee_handler_virt_add_headers;
1181 HANDLER(n)->support = hsupport_length | hsupport_range;
1183 ret = cherokee_buffer_init (&n->buffer);
1184 if (unlikely(ret != ret_ok))
1185 return ret;
1187 ret = cherokee_buffer_ensure_size (&n->buffer, 4*1024);
1188 if (unlikely(ret != ret_ok))
1189 return ret;
1191 *hdl = HANDLER(n);
1193 return ret_ok;