1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
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
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'/>" \
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'/>" \
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>" \
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>" \
55 " <memory>%d</memory>" \
57 " <on_poweroff>destroy</on_poweroff>" \
58 " <on_reboot>restart</on_reboot>" \
59 " <on_crash>destroy</on_crash>" \
65 #define VIRT_STORAGE_XML \
66 "<volume type='%s'>" \
67 " <name>%s_%s</name>" \
68 " <allocation>%lu</allocation>" \
69 " <capacity unit='%s'>%lu</capacity>" \
71 " <path>%s_%s</path>" \
73 " <owner>0744</owner>" \
74 " <group>0744</group>" \
75 " <mode>0744</mode>" \
76 " <label>%s_%s</label>" \
82 #include "handler_virt.h"
83 #include "handler_avahi.h"
84 #include <cherokee/cherokee.h>
85 #include <libxml/parser.h>
86 #include <libxml/xpath.h>
89 /* Plug-in initialization
91 * In this function you can use any of these:
92 * http_delete | http_get | http_post | http_put
94 * For a full list: cherokee_http_method_t
96 * It is what your handler to be implements.
99 PLUGIN_INFO_HANDLER_EASIEST_INIT (virt
, http_get
| http_post
);
102 /* Methods implementation
105 props_free (cherokee_handler_virt_props_t
*props
)
108 cherokee_buffer_mrproper(&props
->xsl
);
109 cherokee_buffer_mrproper(&props
->virt
);
110 return cherokee_module_props_free_base (MODULE_PROPS(props
));
115 cherokee_handler_virt_configure (cherokee_config_node_t
*conf
, cherokee_server_t
*srv
, cherokee_module_props_t
**_props
)
118 cherokee_handler_virt_props_t
*props
;
120 /* Instance a new property object
123 if (*_props
== NULL
) {
124 CHEROKEE_NEW_STRUCT (n
, handler_virt_props
);
126 cherokee_handler_avahi_props_init_base (PROP_AVAHI(n
), MODULE_PROPS_FREE(props_free
));
128 /* Look at handler_virt.h
129 * This is an virt of configuration.
131 n
->authenticate
= true; /* by default we are secure! */
132 n
->read_only
= true; /* by default we are secure! */
133 cherokee_buffer_init (&n
->xsl
); /* we support a custom header */
134 cherokee_buffer_init (&n
->virt
); /* your first xenserver */
136 *_props
= MODULE_PROPS(n
);
139 props
= PROP_VIRT(*_props
);
141 cherokee_config_node_foreach (i
, conf
) {
142 cherokee_config_node_t
*subconf
= CONFIG_NODE(i
);
144 if (equal_buf_str (&subconf
->key
, "authenticate")) {
145 props
->authenticate
= atoi(subconf
->val
.buf
);
147 else if (equal_buf_str (&subconf
->key
, "read_only")) {
148 props
->read_only
= atoi(subconf
->val
.buf
);
150 else if (equal_buf_str (&subconf
->key
, "xsl")) {
151 cherokee_buffer_add_buffer (&props
->xsl
, &subconf
->val
);
153 else if (equal_buf_str (&subconf
->key
, "virt")) {
154 cherokee_buffer_add_buffer (&props
->virt
, &subconf
->val
);
162 return cherokee_handler_avahi_configure (conf
, srv
, _props
);
167 cherokee_handler_virt_init (cherokee_handler_virt_t
*hdl
)
173 cherokee_connection_t
*conn
= HANDLER_CONN(hdl
);
174 cherokee_buffer_init(&hdl
->user
);
175 cherokee_buffer_init(&hdl
->vm
);
177 hdl
->action
= nothing
;
179 cherokee_buffer_add (&conn
->pathinfo
,
180 conn
->request
.buf
+ conn
->web_directory
.len
,
181 conn
->request
.len
- conn
->web_directory
.len
);
183 this = conn
->pathinfo
.buf
+ 1;
184 next
= strchr(this, '/');
187 if ((!next
&& (this && (len
= strlen(this)) == 0)) || (next
&& ((len
= next
- this) == 0)) )
188 hdl
->action
= showall
;
190 cherokee_buffer_add (&hdl
->user
, this, len
);
193 if (HDL_VIRT_PROPS(hdl
)->authenticate
) {
194 if (!conn
->validator
||
196 (!cherokee_buffer_cmp_buf(&conn
->validator
->user
, &hdl
->user
) &&
197 !(isroot
= cherokee_buffer_cmp (&conn
->validator
->user
, "root", 4))))) {
198 hdl
->action
= nothing
; /* just in case */
199 conn
->error_code
= http_unauthorized
;
206 if (hdl
->action
== showall
) {
208 hdl
->action
= nothing
;
209 conn
->error_code
= http_unauthorized
;
212 return virt_build_page(hdl
);
218 hdl
->action
= showuservms
;
219 return virt_build_page(hdl
);
222 next
= strchr(this, '/');
224 if ( ( !next
&& (this && (len
= strlen(this)) == 0) ) || (next
&& ((len
= next
- this) == 0)) ) {
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
);
234 hdl
->action
= domainGetXMLDesc
;
235 return virt_build_page(hdl
);
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
) {
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
;
262 else if (strcmp(this+9, "Create") == 0) hdl
->action
= domainCreate
;
263 else if (strcmp(this+9, "Destroy") == 0) hdl
->action
= domainDestroy
;
264 else if (strcmp(this+9, "Reboot") == 0) hdl
->action
= domainReboot
;
265 else if (strcmp(this+9, "Shutdown") == 0) hdl
->action
= domainShutdown
;
267 else if (strcmp(this+9, "Save") == 0) hdl
->action
= domainSave
;
268 else if (strcmp(this+9, "Restore") == 0) hdl
->action
= domainRestore
;
270 else if (strcmp(this+9, "AttachDevice") == 0) hdl
->action
= domainAttachDevice_args
;
272 else if (strcmp(this+9, "DefineXML") == 0) hdl
->action
= domainDefineXML_args
;
273 else if (strcmp(this+9, "Undefine") == 0) hdl
->action
= domainUndefine
;
276 else if (strncmp(this, "virStorage", 10) == 0) {
277 if (strncmp(this+10, "Vol", 3) == 0) {
278 if (strcmp(this+13, "CreateXML") == 0) hdl
->action
= storageVolCreateXML_args
;
279 else if (strcmp(this+13, "Delete") == 0) hdl
->action
= storageVolDelete_args
;
280 else if (strcmp(this+13, "CloneXML") == 0) hdl
->action
= storageVolCloneXML_args
;
281 else if (strcmp(this+13, "GetXMLDesc") == 0) hdl
->action
= storageVolGetXMLDesc_args
;
282 else if (strcmp(this+13, "SetPassword") == 0) hdl
->action
= storageVolSetPassword_args
;
285 else if (strncmp(this, "virGraph", 8) == 0) {
286 if (strcmp(this+8, "Load") == 0) hdl
->action
= graphLoad_args
;
287 if (strcmp(this+8, "Interface") == 0) hdl
->action
= graphInterface_args
;
293 cherokee_post_get_len (&conn
->post
, &postl
);
295 if (postl
<= 0 || postl
>= (INT_MAX
-1)) {
296 TRACE("virt", "post without post");
297 conn
->error_code
= http_bad_request
;
301 if (strncmp(this, "virDomain", 9) == 0) {
302 if (strcmp(this+9, "AttachDevice") == 0) hdl
->action
= domainAttachDevice
;
303 else if (strcmp(this+9, "DetachDevice") == 0) hdl
->action
= domainDetachDevice
;
304 else if (strcmp(this+9, "DefineXML") == 0) hdl
->action
= domainDefineXML
;
306 else if (strncmp(this, "virStorage", 10) == 0) {
307 if (strncmp(this+10, "Vol", 3) == 0) {
308 if (strcmp(this+13, "CreateXML") == 0) hdl
->action
= storageVolCreateXML
;
319 if (hdl
->action
<= 0) {
320 TRACE("virt", "There was no action specified");
321 conn
->error_code
= http_bad_request
;
326 return virt_build_page(hdl
);
330 cherokee_handler_virt_free (cherokee_handler_virt_t
*hdl
)
332 cherokee_buffer_mrproper (&hdl
->buffer
);
333 cherokee_buffer_mrproper (&hdl
->user
);
334 cherokee_buffer_mrproper (&hdl
->vm
);
340 cherokee_handler_virt_step (cherokee_handler_virt_t
*hdl
, cherokee_buffer_t
*buffer
)
342 if (cherokee_buffer_is_empty (&hdl
->buffer
))
345 cherokee_buffer_add (buffer
, hdl
->buffer
.buf
, 1024);
346 cherokee_buffer_move_to_begin (&hdl
->buffer
, 1024);
348 if (cherokee_buffer_is_empty (&hdl
->buffer
))
349 return ret_eof_have_data
;
355 cherokee_handler_virt_add_headers (cherokee_handler_virt_t
*hdl
, cherokee_buffer_t
*buffer
)
357 cherokee_buffer_add_va (buffer
, "Content-Length: %d"CRLF
, hdl
->buffer
.len
);
359 if (hdl
->action
> xml
)
360 cherokee_buffer_add_str (buffer
, "Content-Type: application/xml"CRLF
);
361 else if (hdl
->action
> graph
) {
362 cherokee_buffer_add_str (buffer
, "Content-Type: image/png"CRLF
);
364 cherokee_buffer_add_str (buffer
, "Content-Type: text/plain"CRLF
);
371 while_func_entries (cherokee_buffer_t *key, void *value, void *param) {
372 virConnectPtr conn = NULL;
373 cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
374 cherokee_buffer_t *buf = (cherokee_buffer_t *)param;
376 cherokee_buffer_add_va (&uri, "xen://%s/", value);
378 if (HDL_VIRT_PROPS(hdl)->read_only == FALSE && !(conn = virConnectOpen (uri.buf))) {
379 if (!(conn = virConnectOpen (uri.buf))) {
384 if (!conn && !(conn = virConnectOpenReadOnly (uri.buf))) {
388 if (!(dom = virDomainLookupByName(conn, (const char *) key->buf))) {
391 char *xml = virDomainGetXMLDesc(dom, 0);
392 cherokee_buffer_add(buf, xml, strlen(xml));
394 virConnectClose(conn);
397 cherokee_buffer_mrproper(&uri);
402 static ret_t
save_xml(cherokee_handler_virt_t
*hdl
, virDomainPtr result
, cherokee_buffer_t
*buf
) {
403 ret_t ret
= ret_error
;
405 cherokee_buffer_t path
= CHEROKEE_BUF_INIT
;
406 cherokee_buffer_add_va (&path
, "/mnt/netapp/users/%s", hdl
->user
.buf
);
408 mkdir(path
.buf
, 0755);
409 cherokee_buffer_add_va (&path
, "/%s", hdl
->vm
.buf
);
411 mkdir(path
.buf
, 0755);
412 cherokee_buffer_add_str (&path
, "/index.xml");
414 if ((fd
= fopen(path
.buf
, "w")) == NULL
) {
417 char *output
= virDomainGetXMLDesc(result
, 0);
418 fwrite(output
, strlen(output
), sizeof(char), fd
);
421 cherokee_buffer_add(buf
, output
, strlen(output
));
425 cherokee_buffer_mrproper(&path
);
431 virt_virt_function(cherokee_handler_virt_t
*hdl
, virDomainPtr dom
, virConnectPtr virConn
) {
432 cherokee_connection_t
*conn
= HANDLER_CONN(hdl
);
433 cherokee_buffer_t
*buf
= &HDL_AVAHI(hdl
)->buffer
;
435 switch (hdl
->action
) {
436 /* Save the memory of a domain to a file and suspend the domain */
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
);
442 if ((result
= virDomainSave(dom
, path
.buf
)) != 0) {
443 TRACE("virt", "Saving of %s/%s failed", hdl
->user
.buf
, hdl
->vm
.buf
);
444 conn
->error_code
= http_internal_error
;
447 cherokee_buffer_mrproper(&path
);
449 cherokee_buffer_add_long10(buf
, result
);
454 /* Restore the memory of a domain from a file and resume the domain */
455 case domainRestore
: {
458 cherokee_buffer_t path
= CHEROKEE_BUF_INIT
;
459 cherokee_buffer_add_va (&path
, "/mnt/netapp/users/%s/%s/memory", hdl
->user
.buf
, hdl
->vm
.buf
);
461 if (stat(path
.buf
, &statbuf
) == 0) {
463 if ((result
= virDomainRestore(virConn
, path
.buf
)) != 0) {
464 TRACE("virt", "Restoring of %s/%s failed", hdl
->user
.buf
, hdl
->vm
.buf
);
465 conn
->error_code
= http_internal_error
;
468 cherokee_buffer_add_long10(buf
, result
);
470 TRACE("virt", "Memory file for %s/%s does not exist", hdl
->user
.buf
, hdl
->vm
.buf
);
471 conn
->error_code
= http_not_found
;
475 cherokee_buffer_mrproper(&path
);
480 case domainUndefine
: {
482 if ((result
= virDomainUndefine(dom
)) != 0) {
483 conn
->error_code
= http_internal_error
; /* TODO */
486 cherokee_buffer_add_long10(buf
, result
);
490 case domainAttachDevice_args
: {
495 ret
= cherokee_avl_get_ptr (conn
->arguments
, "type", &temp
);
498 cherokee_buffer_t xml
= CHEROKEE_BUF_INIT
;
500 if (strcmp(temp
, "disk") == 0) {
502 if ((ret
= cherokee_avl_get_ptr (conn
->arguments
, "device", &device
)) == ret_ok
) {
504 if ((ret
= cherokee_avl_get_ptr (conn
->arguments
, "file", &file
)) == ret_ok
) {
505 cherokee_buffer_add_va (&xml
, VIRT_DISK_XML
, file
, device
);
507 virStorageVolPtr volume
= virt_get_vol_by_args(hdl
, virConn
, 1);
509 if (volume
== NULL
) {
513 file
= virStorageVolGetPath(volume
);
514 cherokee_buffer_add_va (&xml
, VIRT_DISK_XML
, file
, device
);
517 virStorageVolFree(volume
);
523 else if (strcmp(temp
, "interface") == 0) {
525 if ((ret
= cherokee_avl_get_ptr (conn
->arguments
, "mac", &mac
)) == ret_ok
&& (ret
= cherokee_avl_get_ptr (conn
->arguments
, "ip", &ip
)) == ret_ok
) {
526 cherokee_buffer_add_va (&xml
, VIRT_INTERFACE_XML
, mac
, ip
);
530 cherokee_buffer_t domu
= CHEROKEE_BUF_INIT
;
531 cherokee_buffer_add_va (&domu
, "%s_%s", hdl
->user
.buf
, hdl
->vm
.buf
);
532 if (getNewMac("dv28", domu
.buf
, &mac
, &ip
) == 0)
533 cherokee_buffer_add_va (&xml
, VIRT_INTERFACE_XML
, mac
, ip
);
534 cherokee_buffer_mrproper(&domu
);
538 if (ret
== ret_ok
&& (result
= virDomainAttachDevice(dom
, (const char *) xml
.buf
)) == 0) {
541 conn
->error_code
= http_internal_error
;
545 cherokee_buffer_add_long10(buf
, result
);
546 cherokee_buffer_mrproper(&xml
);
548 TRACE("virt", "DeviceAttach_args; type was not specified");
549 conn
->error_code
= http_bad_request
;
556 case domainAttachDevice
: {
559 cherokee_buffer_t post
= CHEROKEE_BUF_INIT
;
560 cherokee_post_get_len (&conn
->post
, &postl
);
561 cherokee_post_walk_read (&conn
->post
, &post
, (cuint_t
) postl
);
562 if ((result
= virDomainAttachDevice(dom
, (const char *) post
.buf
)) != 0)
563 conn
->error_code
= http_internal_error
;
565 cherokee_buffer_mrproper(&post
);
566 cherokee_buffer_add_long10(buf
, result
);
571 case domainGetXMLDesc
: {
572 char *xml
= virDomainGetXMLDesc(dom
, 0);
573 cherokee_buffer_add(buf
, xml
, strlen(xml
));
579 case domainDetachDevice
: {
583 cherokee_buffer_t post
= CHEROKEE_BUF_INIT
;
584 cherokee_post_get_len (&conn
->post
, &postl
);
585 cherokee_post_walk_read (&conn
->post
, &post
, (cuint_t
) postl
);
587 xmlDocPtr doc
= xmlParseMemory((const char *) post
.buf
, post
.len
);
589 TRACE("virt", "DeviceAttach; XML document unparceble");
590 conn
->error_code
= http_bad_request
;
596 if ((result
= virDomainDetachDevice(dom
, (const char *) post
.buf
)) != 0) {
597 conn
->error_code
= http_internal_error
;
598 /* TODO: betere afhandeling */
600 xmlXPathContextPtr context
= xmlXPathNewContext(doc
);
601 if (context
!= NULL
) {
602 xmlXPathObjectPtr obj
= xmlXPathEval("string(//interface/mac/@address)", context
);
603 xmlXPathFreeContext(context
);
604 if ((obj
!= NULL
) && (obj
->type
== XPATH_STRING
) &&
605 (obj
->stringval
!= NULL
) && (obj
->stringval
[0] != 0)) {
606 removeOldMac("dv28", (char *) obj
->stringval
);
608 xmlXPathFreeObject(obj
);
615 cherokee_buffer_mrproper(&post
);
616 cherokee_buffer_add_long10(buf
, result
);
621 cherokee_buffer_add_ulong10(buf
, virDomainGetID(dom
));
624 case domainGetMaxMemory
: {
625 cherokee_buffer_add_ulong10(buf
, virDomainGetMaxMemory (dom
));
628 case domainGetMaxVcpus
: {
629 cherokee_buffer_add_long10(buf
, virDomainGetMaxVcpus (dom
));
632 case domainGetName
: {
633 const char *name
= virDomainGetName (dom
);
634 cherokee_buffer_add(buf
, name
, strlen(name
));
637 case domainGetUUID
: {
638 unsigned char uuid
[VIR_UUID_BUFLEN
];
639 if (virDomainGetUUID(dom
, uuid
) == 0) {
640 cherokee_buffer_add_str(buf
, uuid
);
642 conn
->error_code
= http_internal_error
;
647 case domainGetUUIDString
: {
648 unsigned char uuid
[VIR_UUID_STRING_BUFLEN
];
649 if (virDomainGetUUIDString(dom
, uuid
) == 0) {
650 cherokee_buffer_add_str(buf
, uuid
);
652 conn
->error_code
= http_internal_error
;
659 cherokee_buffer_add_long10(buf
, virDomainCreate (dom
));
663 case domainDestroy
: {
664 cherokee_buffer_add_long10(buf
, virDomainDestroy (dom
));
669 cherokee_buffer_add_long10(buf
, virDomainReboot (dom
, 0));
673 case domainShutdown
: {
674 cherokee_buffer_add_long10(buf
, virDomainShutdown (dom
));
679 case domainGetOSType
: {
680 char *ostype
= virDomainGetOSType(dom
);
681 cherokee_buffer_add(buf
, ostype
, strlen(ostype
));
690 if (hdl
->action
== domainUndefine
) {
691 /* Remove VM data from filesystem */
692 cherokee_buffer_t path
= CHEROKEE_BUF_INIT
;
693 cherokee_buffer_add_va (&path
, "/mnt/netapp/users/%s/%s/index.xml", hdl
->user
.buf
, hdl
->vm
.buf
);
694 unlink(path
.buf
); /* TODO: instead of delet replace */
695 cherokee_buffer_mrproper(&path
);
697 save_xml(hdl
, dom
, NULL
);
703 static virStoragePoolPtr
704 virt_get_pool_by_args(cherokee_handler_virt_t
*hdl
, virConnectPtr virConn
) {
705 cherokee_connection_t
*conn
= HANDLER_CONN(hdl
);
706 virStoragePoolPtr pool
;
710 ret
= cherokee_avl_get_ptr (conn
->arguments
, "pool", &temp
);
711 if (unlikely(ret
< ret_ok
)) {
712 TRACE("virt", "virStoragePoolPtr; Pool argument not specified");
713 conn
->error_code
= http_bad_request
;
717 pool
= virStoragePoolLookupByName(virConn
, temp
);
722 static virStorageVolPtr
723 virt_get_vol_by_args(cherokee_handler_virt_t
*hdl
, virConnectPtr virConn
, unsigned short int prefix
) {
724 cherokee_connection_t
*conn
= HANDLER_CONN(hdl
);
725 virStoragePoolPtr pool
;
726 virStorageVolPtr volume
;
730 pool
= virt_get_pool_by_args(hdl
, virConn
);
733 conn
->error_code
= http_not_found
;
737 virStoragePoolRefresh(pool
, 0); /* TODO: might be better to do it outside */
739 ret
= cherokee_avl_get_ptr (conn
->arguments
, "volume", &temp
);
740 if (unlikely(ret
< ret_ok
)) {
741 TRACE("virt", "virStorageVolPtr; Volume argument not specified");
742 conn
->error_code
= http_bad_request
;
747 cherokee_buffer_t fullvol
= CHEROKEE_BUF_INIT
;
748 cherokee_buffer_add_va (&fullvol
, "%s_%s", hdl
->user
.buf
, temp
);
749 volume
= virStorageVolLookupByName(pool
, fullvol
.buf
);
750 cherokee_buffer_mrproper(&fullvol
);
752 volume
= virStorageVolLookupByName(pool
, temp
);
756 conn
->error_code
= http_not_found
;
758 virStoragePoolFree(pool
);
763 /* This function is the home for all functions that need a working
764 * pool/volume combination */
766 virt_pool_vol(cherokee_handler_virt_t
*hdl
, virConnectPtr virConn
) {
767 cherokee_buffer_t
*buf
= &HDL_AVAHI(hdl
)->buffer
;
768 cherokee_connection_t
*conn
= HANDLER_CONN(hdl
);
769 virStorageVolPtr volume
;
772 /* We only allow clone to run 'unsafe', others get prefixed */
773 volume
= virt_get_vol_by_args(hdl
, virConn
, (hdl
->action
!= storageVolCloneXML_args
));
775 /* If the volume doesn't exist, no point to continue */
779 switch (hdl
->action
) {
780 /* Sets the password of a specific volume, requires the password= option */
781 case storageVolSetPassword_args
: {
783 ret
= cherokee_avl_get_ptr (conn
->arguments
, "password", &temp
);
784 if (unlikely(ret
< ret_ok
)) {
785 TRACE("virt", "storageVolSetPassword_args; password argument not specified");
786 conn
->error_code
= http_bad_request
;
787 goto virt_pool_vol_cleanup
;
789 cherokee_buffer_t cmd_passwd
= CHEROKEE_BUF_INIT
;
790 cherokee_buffer_add_va (&cmd_passwd
, "/usr/sbin/passwdchanger.sh %s %s\n", virStorageVolGetKey(volume
), temp
);
791 cherokee_buffer_add_long10(buf
, system(cmd_passwd
.buf
));
792 cherokee_buffer_mrproper(&cmd_passwd
);
797 /* Removes a volume */
798 case storageVolDelete_args
: {
799 cherokee_buffer_add_long10(buf
, virStorageVolDelete(volume
, 0));
803 /* Gives a description of a storage volume in XML */
804 case storageVolGetXMLDesc_args
: {
805 char *xml
= virStorageVolGetXMLDesc(volume
, 0);
806 cherokee_buffer_add(buf
, xml
, strlen(xml
));
811 /* Clones a volume, insecure method! requires a new name= */
812 case storageVolCloneXML_args
: {
814 cherokee_buffer_t busy
= CHEROKEE_BUF_INIT
;
816 ret
= cherokee_avl_get_ptr (conn
->arguments
, "name", &name
);
817 if (unlikely(ret
< ret_ok
)) {
818 TRACE("virt", "storageVolCloneXML_args; name argument not specified");
819 conn
->error_code
= http_bad_request
;
820 goto virt_pool_vol_cleanup
;
823 cherokee_buffer_add_va (&busy
, "/mnt/images/queue/%s_%s.queued", hdl
->user
.buf
, name
);
825 if (unlikely(symlink(virStorageVolGetKey(volume
), busy
.buf
) == 1)) {
826 conn
->error_code
= http_internal_error
;
827 goto virt_pool_vol_cleanup
;
830 cherokee_buffer_mrproper(&busy
);
831 cherokee_buffer_add_str(buf
, "QUEUED");
837 virt_pool_vol_cleanup
:
838 /* And in the end we need to free the volume we have used */
839 virStorageVolFree(volume
);
845 virt_virt_new(cherokee_handler_virt_t
*hdl
, virConnectPtr virConn
) {
846 cherokee_buffer_t
*buf
= &HDL_AVAHI(hdl
)->buffer
;
847 cherokee_connection_t
*conn
= HANDLER_CONN(hdl
);
848 cherokee_buffer_t xml
= CHEROKEE_BUF_INIT
;
849 virStoragePoolPtr pool
= NULL
;
852 switch (hdl
->action
) {
853 case storageVolCreateXML_args
: {
854 void *temp
, *type
, *name
, *unit
;
855 unsigned long int capacity
, allocation
;
857 ret
= cherokee_avl_get_ptr (conn
->arguments
, "pool", &temp
);
858 if (unlikely(ret
< ret_ok
)) {
859 TRACE("virt", "storageVolCreateXML_args; pool argument not specified");
860 conn
->error_code
= http_bad_request
;
861 goto virt_virt_new_cleanup
;
864 pool
= virStoragePoolLookupByName(virConn
, temp
);
867 conn
->error_code
= http_not_found
;
868 goto virt_virt_new_cleanup
;
871 ret
= cherokee_avl_get_ptr (conn
->arguments
, "name", &name
);
872 if (unlikely(ret
< ret_ok
)) {
873 TRACE("virt", "storageVolCreateXML_args; name argument not specified");
874 conn
->error_code
= http_bad_request
;
875 goto virt_virt_new_cleanup
;
878 ret
= cherokee_avl_get_ptr (conn
->arguments
, "type", &type
);
879 if (unlikely(ret
< ret_ok
)) {
880 TRACE("virt", "storageVolCreateXML_args; type argument not specified");
881 conn
->error_code
= http_bad_request
;
882 goto virt_virt_new_cleanup
;
885 ret
= cherokee_avl_get_ptr (conn
->arguments
, "unit", &unit
);
886 if (unlikely(ret
< ret_ok
)) {
887 TRACE("virt", "storageVolCreateXML_args; unit argument not specified");
888 conn
->error_code
= http_bad_request
;
889 goto virt_virt_new_cleanup
;
892 ret
= cherokee_avl_get_ptr (conn
->arguments
, "allocation", &temp
);
893 if (unlikely(ret
< ret_ok
)) {
894 TRACE("virt", "storageVolCreateXML_args; allocation argument not specified");
895 conn
->error_code
= http_bad_request
;
896 goto virt_virt_new_cleanup
;
899 allocation
= strtoul(temp
, NULL
, 10);
900 if (errno
== ERANGE
) {
901 TRACE("virt", "storageVolCreateXML_args; allocation is not a number");
902 conn
->error_code
= http_bad_request
;
904 goto virt_virt_new_cleanup
;
907 ret
= cherokee_avl_get_ptr (conn
->arguments
, "capacity", &temp
);
908 if (unlikely(ret
< ret_ok
)) {
909 TRACE("virt", "storageVolCreateXML_args; capacity argument not specified");
910 conn
->error_code
= http_bad_request
;
911 goto virt_virt_new_cleanup
;
914 capacity
= strtoul(temp
, NULL
, 10);
915 if (errno
== ERANGE
|| capacity
== 0) {
916 TRACE("virt", "storageVolCreateXML_args; capacity is not a number");
917 conn
->error_code
= http_bad_request
;
919 goto virt_virt_new_cleanup
;
922 cherokee_buffer_add_va (&xml
, VIRT_STORAGE_XML
, type
, hdl
->user
.buf
, name
, allocation
, unit
, capacity
, hdl
->user
.buf
, name
, hdl
->user
.buf
, name
);
926 case domainDefineXML_args
: {
929 unsigned long vcpu
= 0, interface
= 0, memory
= 0;
933 ret
= cherokee_avl_get_ptr (conn
->arguments
, "vcpu", &temp
);
935 vcpu
= strtoul(temp
, (char **) NULL
, 10);
937 if (ret
!= ret_ok
|| errno
== ERANGE
|| vcpu
== 0) {
938 conn
->error_code
= http_internal_error
;
939 goto virt_virt_new_cleanup
;
943 ret
= cherokee_avl_get_ptr (conn
->arguments
, "memory", &temp
);
945 memory
= strtoul(temp
, (char **) NULL
, 10);
947 if (ret
!= ret_ok
|| errno
== ERANGE
|| memory
== 0) {
948 conn
->error_code
= http_internal_error
;
949 goto virt_virt_new_cleanup
;
953 ret
= cherokee_avl_get_ptr (conn
->arguments
, "interface", &temp
);
955 interface
= strtoul(temp
, (char **) NULL
, 10);
957 if (ret
!= ret_ok
|| errno
== ERANGE
) {
958 conn
->error_code
= http_internal_error
;
959 goto virt_virt_new_cleanup
;
962 cherokee_buffer_t cmdline_extra
= CHEROKEE_BUF_INIT
;
963 cherokee_buffer_t xml_interfaces
= CHEROKEE_BUF_INIT
;
964 cherokee_buffer_t domu
= CHEROKEE_BUF_INIT
;
965 cherokee_buffer_add_va (&domu
, "%s_%s", hdl
->user
.buf
, hdl
->vm
.buf
);
967 for (i
= 0; i
< interface
; i
++) {
970 if (getNewMac("dv28", domu
.buf
, &mac
, &ip
) == 0) {
971 cherokee_buffer_add_va (&xml_interfaces
, VIRT_INTERFACE_XML
, mac
, ip
);
973 /* TODO: terrible hack */
977 temp
= strchr(gateway
, '.');
978 temp
= strchr(++temp
, '.');
979 temp
= strchr(++temp
, '.');
980 strcpy(++temp
, "254");
981 cherokee_buffer_add_va (&cmdline_extra
, VIRT_DOMAIN_CMD_IP
, ip
, gateway
);
986 cherokee_buffer_mrproper(&domu
);
988 cherokee_buffer_add_va (&xml
, VIRT_DOMAIN_XML
,
989 hdl
->user
.buf
, hdl
->vm
.buf
, cmdline_extra
.buf
, memory
, vcpu
, xml_interfaces
.buf
);
991 cherokee_buffer_mrproper(&xml_interfaces
);
992 cherokee_buffer_mrproper(&cmdline_extra
);
996 case storageVolCreateXML
:
997 case domainDefineXML
: {
999 cherokee_post_get_len (&conn
->post
, &postl
);
1000 cherokee_post_walk_read (&conn
->post
, &xml
, (cuint_t
) postl
);
1005 switch (hdl
->action
) {
1006 case domainDefineXML_args
:
1007 case domainDefineXML
: {
1008 virDomainPtr result
= virDomainDefineXML(virConn
, (const char *) xml
.buf
);
1010 if (result
== NULL
) {
1011 /* TODO: vrij maken eventuele uitgegeven macs! */
1012 conn
->error_code
= http_internal_error
;
1013 goto virt_virt_new_cleanup
;
1016 save_xml(hdl
, result
, buf
);
1018 virDomainFree(result
);
1023 case storageVolCreateXML_args
:
1024 case storageVolCreateXML
: {
1025 virStorageVolPtr vol
= virStorageVolCreateXML(pool
, xml
.buf
, 0);
1028 cherokee_buffer_add_long10(buf
, -1);
1029 goto virt_virt_new_cleanup
;
1032 virStorageVolFree(vol
);
1034 cherokee_buffer_add_long10(buf
, 0);
1039 virt_virt_new_cleanup
:
1040 cherokee_buffer_mrproper(&xml
);
1043 virStoragePoolFree(pool
);
1050 virt_virt_do(cherokee_handler_virt_t
*hdl
, cherokee_buffer_t
*domu
, cherokee_buffer_t
*uri
)
1052 cherokee_connection_t
*conn
= HANDLER_CONN(hdl
);
1054 ret_t ret
= ret_error
;
1055 virConnectPtr virConn
= NULL
;
1058 if (HDL_VIRT_PROPS(hdl
)->read_only
== FALSE
)
1059 virConn
= virConnectOpen (uri
->buf
);
1061 if (!virConn
&& !(virConn
= virConnectOpenReadOnly (uri
->buf
))) {
1062 conn
->error_code
= http_service_unavailable
;
1066 switch (hdl
->action
) {
1067 case storageVolDelete_args
:
1068 case storageVolSetPassword_args
:
1069 case storageVolGetXMLDesc_args
:
1070 case storageVolCloneXML_args
:
1071 ret
= virt_pool_vol(hdl
, virConn
);
1074 case storageVolCreateXML_args
:
1075 case storageVolCreateXML
:
1076 case domainDefineXML_args
:
1077 case domainDefineXML
:
1078 ret
= virt_virt_new(hdl
, virConn
);
1083 if ((dom
= virDomainLookupByName(virConn
, domu
->buf
)) == NULL
) {
1084 conn
->error_code
= http_not_found
;
1086 ret
= virt_virt_function(hdl
, dom
, virConn
);
1092 virConnectClose(virConn
);
1097 virt_while (cherokee_buffer_t
*key
, void *value
, void *param
) {
1098 cherokee_handler_virt_t
* hdl
= param
;
1099 // cherokee_buffer_t uri = CHEROKEE_BUF_INIT;
1100 // cherokee_buffer_add_va (&uri, "xen://%s/", ((cherokee_buffer_t *) value)->buf);
1101 // virt_virt_do((cherokee_handler_virt_t *) param, key, &uri);
1102 // cherokee_buffer_mrproper(&uri);
1104 cherokee_buffer_add_va (&hdl
->buffer
, "<domain><name>%s</name></domain>", key
->buf
);
1110 virt_while_user (cherokee_buffer_t
*key
, void *value
, void *param
) {
1111 cherokee_handler_virt_t
*hdl
= param
;
1112 if (key
->len
> hdl
->user
.len
&& key
->buf
[hdl
->user
.len
] == '_' && strncmp(key
->buf
, hdl
->user
.buf
, hdl
->user
.len
) == 0)
1113 return virt_while (key
, value
, param
);
1119 virt_build_page (cherokee_handler_virt_t
*hdl
)
1122 cherokee_connection_t
*conn
= HANDLER_CONN(hdl
);
1123 cherokee_buffer_t uri
= CHEROKEE_BUF_INIT
;
1124 cherokee_buffer_t domu
= CHEROKEE_BUF_INIT
;
1126 /* We use the webserver methods to parse the querystring */
1127 /* Maybe do something smart with ENUM, begin_args, end_args... */
1128 if ((hdl
->action
== domainDefineXML_args
) ||
1129 (hdl
->action
== domainAttachDevice_args
) ||
1130 (hdl
->action
== storageVolGetXMLDesc_args
) ||
1131 (hdl
->action
== storageVolDelete_args
) ||
1132 (hdl
->action
== storageVolSetPassword_args
) ||
1133 (hdl
->action
== storageVolCloneXML_args
) ||
1134 (hdl
->action
== storageVolCreateXML_args
) ||
1135 (hdl
->action
== graphLoad_args
) ||
1136 (hdl
->action
== graphInterface_args
)) {
1137 ret
= cherokee_connection_parse_args (conn
);
1138 if (unlikely(ret
< ret_ok
)) {
1139 conn
->error_code
= http_internal_error
;
1144 switch (hdl
->action
) {
1145 case graphInterface_args
: {
1146 struct stat statbuf
;
1147 cherokee_buffer_t path
= CHEROKEE_BUF_INIT
;
1149 if ((ret
= cherokee_avl_get_ptr (conn
->arguments
, "interface", &interface
)) != ret_ok
)
1152 cherokee_buffer_add_va (&path
, "/mnt/netapp/users/%s/%s/interface_%s.rrd", hdl
->user
.buf
, hdl
->vm
.buf
, interface
);
1153 if (stat(path
.buf
, &statbuf
) != 0) {
1154 conn
->error_code
= http_not_found
;
1157 void *width
, *height
, *start
, *end
;
1158 if ((ret
= cherokee_avl_get_ptr (conn
->arguments
, "width", &width
)) != ret_ok
)
1161 if ((ret
= cherokee_avl_get_ptr (conn
->arguments
, "height", &height
)) != ret_ok
)
1164 if ((ret
= cherokee_avl_get_ptr (conn
->arguments
, "start", &start
)) != ret_ok
)
1167 if ((ret
= cherokee_avl_get_ptr (conn
->arguments
, "end", &end
)) != ret_ok
)
1170 cherokee_buffer_t def1
= CHEROKEE_BUF_INIT
;
1171 cherokee_buffer_t def2
= CHEROKEE_BUF_INIT
;
1172 cherokee_buffer_add_va (&def1
, "DEF:rxbytes=%s:rxbytes:AVERAGE:step=30", path
.buf
);
1173 cherokee_buffer_add_va (&def2
, "DEF:txbytes=%s:txbytes:AVERAGE:step=30", path
.buf
);
1174 char **calcpr
= NULL
;
1177 char *filename
= mktemp(strdup("handler_virt_XXXXXX"));
1178 char *r_graph
[] = { "rrdgraph", filename
,
1184 "--title", interface
,
1185 "--lower-limit", "0", "--alt-autoscale-max", "--vertical-label", "bits per second",
1188 "CDEF:txbits=txbytes,8,*",
1189 "CDEF:rxbits=rxbytes,8,*",
1190 "AREA:rxbits#00EE00:rxbits",
1191 "LINE:txbits#0000EE:txbits" };
1193 rrd_graph(25, r_graph
, &calcpr
, &xsize
, &ysize
, NULL
, &ymin
, &ymax
);
1194 if ((ret
= cherokee_buffer_read_file(&hdl
->buffer
, filename
)) == ret_error
) {
1195 hdl
->action
= http_internal_error
;
1201 cherokee_buffer_mrproper(&def1
);
1202 cherokee_buffer_mrproper(&def2
);
1204 cherokee_buffer_mrproper(&path
);
1205 goto virt_build_page_cleanup
;
1210 case graphLoad_args
: {
1211 struct stat statbuf
;
1212 cherokee_buffer_t path
= CHEROKEE_BUF_INIT
;
1213 cherokee_buffer_add_va (&path
, "/mnt/netapp/users/%s/%s/cpuTime.rrd", hdl
->user
.buf
, hdl
->vm
.buf
);
1214 if (stat(path
.buf
, &statbuf
) != 0) {
1215 conn
->error_code
= http_not_found
;
1218 void *width
, *height
, *start
, *end
;
1219 if ((ret
= cherokee_avl_get_ptr (conn
->arguments
, "width", &width
)) != ret_ok
)
1222 if ((ret
= cherokee_avl_get_ptr (conn
->arguments
, "height", &height
)) != ret_ok
)
1225 if ((ret
= cherokee_avl_get_ptr (conn
->arguments
, "start", &start
)) != ret_ok
)
1228 if ((ret
= cherokee_avl_get_ptr (conn
->arguments
, "end", &end
)) != ret_ok
)
1231 /* TODO: wat error checking? */
1233 cherokee_buffer_t def
= CHEROKEE_BUF_INIT
;
1234 cherokee_buffer_add_va (&def
, "DEF:cputime=%s:cpuTime:AVERAGE:step=30", path
.buf
);
1235 char **calcpr
= NULL
;
1238 char *filename
= mktemp(strdup("handler_virt_XXXXXX"));
1239 char *r_graph
[] = { "rrdgraph",
1246 "--title", hdl
->vm
.buf
,
1247 "--lower-limit", "0", "--alt-autoscale-max", "--vertical-label", "(used / total) time",
1249 "CDEF:cpuload=cputime,1000000000,/",
1250 "LINE:cpuload#EE0000:cpuLoad" };
1252 rrd_graph(22, r_graph
, &calcpr
, &xsize
, &ysize
, NULL
, &ymin
, &ymax
);
1253 if ((ret
= cherokee_buffer_read_file(&hdl
->buffer
, filename
)) == ret_error
) {
1254 hdl
->action
= http_internal_error
;
1260 cherokee_buffer_mrproper(&def
);
1262 cherokee_buffer_mrproper(&path
);
1263 goto virt_build_page_cleanup
;
1267 if (hdl
->action
> xml
&& HDL_VIRT_PROPS(hdl
)->xsl
.len
> 0)
1268 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
);
1271 /* First, block the event loop */
1272 avahi_threaded_poll_lock(HDL_AVAHI_PROPS(hdl
)->threaded_poll
);
1273 switch (hdl
->action
) {
1275 cherokee_buffer_add_str (&hdl
->buffer
, "<domains>");
1276 cherokee_avl_while(&HDL_AVAHI_PROPS(hdl
)->entries
, virt_while
, hdl
, NULL
, NULL
);
1277 cherokee_buffer_add_str (&hdl
->buffer
, "</domains>");
1282 cherokee_buffer_add_str (&hdl
->buffer
, "<domains>");
1283 cherokee_avl_while(&HDL_AVAHI_PROPS(hdl
)->entries
, virt_while_user
, hdl
, NULL
, NULL
);
1284 cherokee_buffer_add_str (&hdl
->buffer
, "</domains>");
1290 cherokee_buffer_t
*hostname
= NULL
;
1291 cherokee_buffer_add_va (&domu
, "%s_%s", hdl
->user
.buf
, hdl
->vm
.buf
);
1292 ret
= cherokee_avl_get_ptr(&HDL_AVAHI_PROPS(hdl
)->entries
, domu
.buf
, (void **) &hostname
);
1294 if (ret
== ret_not_found
) {
1295 virDomainPtr virDom
;
1296 virConnectPtr virConn
;
1297 cherokee_buffer_add_va (&uri
, "xen://%s/", HDL_VIRT_PROPS(hdl
)->virt
); // TODO: change!
1299 /* If we have the read only parameter, we will set up a connection to the
1300 * Hypervisor here. */
1301 if (HDL_VIRT_PROPS(hdl
)->read_only
== FALSE
)
1302 virConn
= virConnectOpen (uri
.buf
);
1304 /* We should already have a connection (read only) or build a new connection
1305 * if this doesn't work, we can safely assume our services is fubar */
1306 if (!virConn
&& !(virConn
= virConnectOpenReadOnly (uri
.buf
))) {
1307 conn
->error_code
= http_service_unavailable
;
1311 /* We lookup if there is a domain somewhere with this name */
1312 if ((virDom
= virDomainLookupByName(virConn
, domu
.buf
)) == NULL
) {
1313 /* If the domain is not found on the network the only possible
1314 * command that is possible would be to Define it */
1315 if (hdl
->action
== domainDefineXML_args
|| hdl
->action
== domainDefineXML
) {
1318 /* Otherwise we don't have anything to do and we should
1319 * return an error */
1320 hdl
->action
= nothing
;
1321 conn
->error_code
= http_not_found
;
1325 /* We don't want te recreate things that already found on the network
1326 * first undefine them! */
1327 if (hdl
->action
== domainDefineXML_args
|| hdl
->action
== domainDefineXML
) {
1328 TRACE("virt", "domainDefineXML_args/domainDefineXML; domain already exists");
1329 hdl
->action
= nothing
;
1330 conn
->error_code
= http_bad_request
;
1333 /* Everything is ok, because nothing is found. */
1337 /* Domain wasn't NULL, so we should free it here */
1338 virDomainFree(virDom
);
1340 virConnectClose (virConn
);
1341 } else if (ret
== ret_ok
) {
1342 cherokee_buffer_add_va (&uri
, "xen://%s/", hostname
->buf
);
1343 printf("%s\n", uri
.buf
);
1345 hdl
->action
= http_internal_error
;
1346 hdl
->action
= nothing
;
1352 /* Finally, unblock the event loop */
1353 avahi_threaded_poll_unlock(HDL_AVAHI_PROPS(hdl
)->threaded_poll
);
1355 if (ret
== ret_ok
&& hdl
->action
!= showall
&& hdl
->action
!= showuservms
) {
1356 ret
= virt_virt_do(hdl
, &domu
, &uri
);
1359 virt_build_page_cleanup
:
1360 cherokee_buffer_mrproper(&domu
);
1361 cherokee_buffer_mrproper(&uri
);
1368 cherokee_handler_virt_new (cherokee_handler_t
**hdl
, cherokee_connection_t
*cnt
, cherokee_module_props_t
*props
)
1371 CHEROKEE_NEW_STRUCT (n
, handler_virt
);
1373 /* Init the base class
1376 cherokee_handler_init_base(HANDLER(n
), cnt
, HANDLER_PROPS(props
), PLUGIN_INFO_HANDLER_PTR(virt
));
1378 MODULE(n
)->init
= (handler_func_init_t
) cherokee_handler_virt_init
;
1379 MODULE(n
)->free
= (module_func_free_t
) cherokee_handler_virt_free
;
1380 HANDLER(n
)->step
= (handler_func_step_t
) cherokee_handler_virt_step
;
1381 HANDLER(n
)->add_headers
= (handler_func_add_headers_t
) cherokee_handler_virt_add_headers
;
1383 HANDLER(n
)->support
= hsupport_length
| hsupport_range
;
1385 ret
= cherokee_buffer_init (&n
->buffer
);
1386 if (unlikely(ret
!= ret_ok
))
1389 ret
= cherokee_buffer_ensure_size (&n
->buffer
, 4*1024);
1390 if (unlikely(ret
!= ret_ok
))