os-cmpi-xen

view src/xen_utils.c @ 121:4868ace2726b

Add initial consistence checks in test suite for Xen_MemoryPool, Xen_ProcessorPool and Xen_VirtualSystemManagementService.

Signed-off-by: Luke Szymanski <Lukasz.Szymanski@Unisys.com>
author Jim Fehlig <jfehlig@novell.com>
date Fri Jun 08 10:22:05 2007 -0600 (2007-06-08)
parents 8ecc856bd445
children 28e8c94e92de
line source
1 // Copyright (C) 2006 Novell, Inc.
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 // ============================================================================
17 // Authors: Jim Fehlig, <jfehlig@novell.com>
18 // Contributors: Raj Subrahmanian <raj.subrahmanian@unisys.com>
19 // Description: Utilitiy functions built on top of libxen for use in all
20 // providers.
21 // ============================================================================
23 #include <string.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/un.h>
29 #include <pthread.h>
30 #include <assert.h>
32 #include <xen_common.h>
33 #include <xen_host.h>
34 #include <xen_vm.h>
36 /* Init the parser for libxenapi */
37 #include <libxml/parser.h>
39 #include <cmpidt.h>
40 #include <cmpimacs.h>
42 #include "xen_utils.h"
43 #include "provider_common.h"
45 /* Include _SBLIM_TRACE() logging support */
46 #include "cmpitrace.h"
49 /* Global variables for reference counting this library's use. */
50 static pthread_mutex_t ref_count_lock = PTHREAD_MUTEX_INITIALIZER;
51 static unsigned int ref_count = 0;
54 ///////////////////////////////////////////////////////////////////////////
55 /* Private functions */
57 /*
58 * Do a synchronous read or write on the file descriptor.
59 *
60 * Returns the number of bytes exchanged, or -1 in case of error
61 */
62 static ssize_t xen_utils_wr_sync(int fd, void *buffer, size_t size, int do_read)
63 {
64 size_t offset = 0;
66 while (offset < size) {
67 ssize_t len;
69 if (do_read)
70 len = read(fd, ((char *) buffer) + offset, size - offset);
71 else
72 len = write(fd, ((char *) buffer) + offset, size - offset);
74 /* recoverable error, retry */
75 if ((len == -1) && ((errno == EAGAIN) || (errno == EINTR)))
76 continue;
78 /* eof */
79 if (len == 0)
80 break;
82 /* unrecoverable error */
83 if (len == -1)
84 return -1;
86 offset += len;
87 }
89 return offset;
90 }
93 /*
94 * Internal routine to do a synchronous read.
95 *
96 * Returns the number of bytes read, or -1 in case of error
97 */
98 static ssize_t xen_utils_sread(int fd, void *buffer, size_t size)
99 {
100 return xen_utils_wr_sync(fd, buffer, size, 1);
101 }
104 /*
105 * Internal routine to do a synchronous write.
106 *
107 * Returns the number of bytes written, or -1 in case of error
108 */
109 static ssize_t xen_utils_swrite(int fd, const void *buffer, size_t size)
110 {
111 return xen_utils_wr_sync(fd, (void *) buffer, size, 0);
112 }
115 /*
116 * Internal routine to do a synchronous write of a string.
117 *
118 * Returns the number of bytes written, or -1 in case of error
119 */
120 static ssize_t xen_utils_swrites(int fd, const char *string)
121 {
122 return xen_utils_swrite(fd, string, strlen(string));
123 }
126 /*
127 * Internal routine to do a synchronous read of a line.
128 *
129 * Returns the number of bytes read, or -1 in case of error
130 */
131 static ssize_t xen_utils_sreads(int fd, char *buffer, size_t n_buffer)
132 {
133 size_t offset;
135 if (n_buffer < 1)
136 return (-1);
138 for (offset = 0; offset < (n_buffer - 1); offset++) {
139 ssize_t ret;
141 ret = xen_utils_sread(fd, buffer + offset, 1);
142 if (ret == 0)
143 break;
144 else if (ret == -1)
145 return ret;
147 if (buffer[offset] == '\n') {
148 offset++;
149 break;
150 }
151 }
152 buffer[offset] = 0;
154 return offset;
155 }
158 static int xen_utils_istartswith(const char *haystack, const char *needle)
159 {
160 return (strncasecmp(haystack, needle, strlen(needle)) == 0);
161 }
164 /*
165 * Read HTTP response from xend xmlrpc server.
166 * read_buf will be allocated and populated with the xmlrpc
167 * content contained in the response. Caller must free
168 * the buffer.
169 *
170 * Returns the HTTP return code or -1 on failure.
171 */
172 static int xen_utils_xend_read(int fd, char **read_buf)
173 {
174 char buf[2048];
175 int content_length = -1;
176 int retcode = -1;
178 while (xen_utils_sreads(fd, buf, sizeof(buf)) > 0) {
179 if (strcmp(buf, "\r\n") == 0)
180 break;
182 if (xen_utils_istartswith(buf, "Content-Length: "))
183 content_length = atoi(buf + 16);
184 else if (xen_utils_istartswith(buf, "HTTP/1.1 "))
185 retcode = atoi(buf + 9);
186 }
188 /* Fail if we did not get an HTTP response code. */
189 if (retcode == -1)
190 return -1;
192 if (content_length > -1) {
193 ssize_t ret;
195 *read_buf = calloc(1, content_length + 1);
196 if (*read_buf == NULL)
197 return -1;
199 ret = xen_utils_sread(fd, *read_buf, content_length);
200 if (ret < 0)
201 return -1;
202 }
204 return retcode;
205 }
208 /*
209 * Do an HTTP POST to the xend xmlrpc server. The post will
210 * contain content specified in paramenter content.
211 *
212 * Returns non-zero on success, 0 on failure.
213 */
214 static int xen_utils_xend_post(int soc, char *content, size_t n_content)
215 {
216 char buffer[100];
217 int ssize_t;
219 if (soc == -1)
220 return 0;
222 xen_utils_swrites(soc, "POST /RPC2 HTTP/1.1\r\n");
224 xen_utils_swrites(soc,
225 "Host: localhost:8006\r\n"
226 "Accept-Encoding: identity\r\n"
227 "Content-Type: application/x-www-form-urlencoded\r\n"
228 "Content-Length: ");
229 snprintf(buffer, sizeof(buffer), "%d", (int)n_content);
230 xen_utils_swrites(soc, buffer);
231 xen_utils_swrites(soc, "\r\n\r\n");
232 xen_utils_swrites(soc, content);
234 return 1;
235 }
238 /*
239 * Connect to unix domain socket specified by path.
240 *
241 * Returns a connected socket on success or -1 on failure.
242 */
243 static int xen_utils_xend_connect_unix(const char *path)
244 {
245 struct sockaddr_un uds_addr;
246 int soc;
248 if (path == NULL)
249 return -1;
251 memset((void *)&uds_addr, '\0', sizeof(uds_addr));
252 uds_addr.sun_family = PF_UNIX;
253 strncpy(uds_addr.sun_path, path, sizeof(uds_addr.sun_path));
255 soc = socket(PF_UNIX, SOCK_STREAM, 0);
256 if (soc == -1)
257 return -1;
259 if (connect(soc, (struct sockaddr *)&uds_addr, sizeof(uds_addr))) {
260 close(soc);
261 return -1;
262 }
264 return soc;
265 }
268 /*
269 * Callback function invoked by libxenapi when the libraray
270 * has data to send.
271 *
272 * Returns 0 on success, -1 on failure.
273 */
274 static int call_func(const void *data, size_t len, void *user_handle,
275 void *result_handle, xen_result_func result_func)
276 {
277 xen_utils_session *s = (xen_utils_session *)user_handle;
278 char *read_buf = NULL;
279 int ccode;
281 /* Send data to xend xmlrpc server. */
282 if (!xen_utils_xend_post(s->socket, (char *)data, len))
283 return -1;
285 /* Read response from xend. */
286 ccode = xen_utils_xend_read(s->socket, &read_buf);
288 if ((ccode < 0) || (ccode >= 300)) {
289 ccode = -1;
290 goto Exit;
291 }
292 else if ((ccode == 202) && read_buf && (strstr(read_buf, "failed") != NULL)) {
293 ccode = -1;
294 goto Exit;
295 }
297 if (!result_func(read_buf, strlen(read_buf), result_handle)) {
298 ccode = -1;
299 goto Exit;
300 }
301 ccode = 0;
303 Exit:
304 if (read_buf)
305 free(read_buf);
306 return ccode;
307 }
310 static xen_vm_set* xen_utils_enum_domains(xen_utils_session *session)
311 {
312 xen_vm_set *resident_vms;
313 char error_msg[XEN_UTILS_ERROR_BUF_LEN];
315 if(!xen_host_get_resident_vms(session->xen, &resident_vms, session->host)) {
316 /* Error description in session object. */
317 XEN_UTILS_GET_ERROR_STRING(error_msg, session->xen);
318 _SBLIM_TRACE(1, ("--- xen_host_get_resident_vms failed: \"%s\"", error_msg));
319 return NULL;
320 }
322 return resident_vms;
323 }
326 static int xen_utils_get_session(xen_utils_session **session)
327 {
328 xen_utils_session *s;
330 *session = NULL;
332 s = calloc(1, sizeof(xen_utils_session));
333 if (s == NULL) {
334 _SBLIM_TRACE(1, ("No memory for Xen Daemon session object"));
335 return 0;
336 }
338 s->socket = xen_utils_xend_connect_unix("/var/run/xend/xen-api.sock");
339 if (s->socket == -1) {
340 _SBLIM_TRACE(1, ("Unable to connect to Xen Daemon"));
341 goto Error;
342 }
344 s->xen = xen_session_login_with_password(call_func,
345 (void *)s,
346 NULL, NULL);
347 if (s->xen == NULL || !s->xen->ok) {
348 _SBLIM_TRACE(1, ("Login to Xen Daemon failed"));
349 goto Error;
350 }
352 if (!xen_session_get_this_host(s->xen, &(s->host), s->xen)) {
353 _SBLIM_TRACE(1, ("Failed to get session host"));
354 goto Error;
355 }
357 *session = s;
358 return 1;
360 Error:
361 if (s->socket > 0)
362 close(s->socket);
363 free(s);
364 return 0;
365 }
368 /*
369 * Generate exported functions for concatenating lists of references.
370 */
371 XEN_UTILS_REF_LIST_CONCAT(xen_vm)
372 XEN_UTILS_REF_LIST_CONCAT(xen_vdi)
373 XEN_UTILS_REF_LIST_CONCAT(xen_vbd)
374 XEN_UTILS_REF_LIST_CONCAT(xen_vif)
377 /*
378 * Generate exported functions for adding a reference (e.g. xen_vm) to a list
379 * of references.
380 */
381 XEN_UTILS_REF_LIST_ADD(xen_vm)
382 XEN_UTILS_REF_LIST_ADD(xen_vdi)
383 XEN_UTILS_REF_LIST_ADD(xen_vbd)
384 XEN_UTILS_REF_LIST_ADD(xen_vif)
387 /*
388 * Generate exported functions for adding a device (e.g. vbd) to a list
389 * of such devices.
390 */
391 XEN_UTILS_DEV_LIST_ADD(xen_vdi_record)
392 XEN_UTILS_DEV_LIST_ADD(xen_vbd_record)
393 XEN_UTILS_DEV_LIST_ADD(xen_vif_record)
396 /*
397 * Initialize a session with Xen. Providers should acquire a Xen
398 * Session when loaded into the cimom, i.e. when the provider's
399 * Initialize() method is invoked by the cimom.
400 *
401 * On success a xen_utils_session object is placed in parameter
402 * session. The object is connected and logged-in to the Xen Daemon
403 * and ready for use.
404 *
405 * Returns non-zero on success, 0 on failure.
406 */
407 int xen_utils_xen_init(xen_utils_session **session)
408 {
409 pthread_mutex_lock(&ref_count_lock);
410 if (ref_count == 0) {
411 xmlInitParser();
412 xen_init();
413 }
414 ref_count++;
415 pthread_mutex_unlock(&ref_count_lock);
417 return xen_utils_get_session(session);
418 }
421 /*
422 * Close Xen session. Connection to Xen Daemon is closed and the
423 * session handle is freed. Providers should invoke this function
424 * when their Cleanup() method is invoked by the cimom.
425 */
426 void xen_utils_xen_close(xen_utils_session *session)
427 {
428 if (session) {
429 xen_session_logout(session->xen);
430 close(session->socket);
431 xen_host_free(session->host);
432 free(session);
433 }
435 pthread_mutex_lock(&ref_count_lock);
436 ref_count--;
437 if (ref_count == 0)
438 {
439 xen_fini();
440 xmlCleanupParser();
441 }
442 pthread_mutex_unlock(&ref_count_lock);
443 }
446 /*
447 * Validate xend session. If sesssion is null, create one.
448 * Session is ready for use on success.
449 *
450 * Returns a non-zero on success, 0 on failure.
451 */
452 int xen_utils_validate_session(xen_utils_session **session)
453 {
454 xen_utils_session *s;
456 if (session == NULL)
457 return 0;
459 if (*session == NULL)
460 if (!xen_utils_get_session(session))
461 return 0;
463 s = *session;
465 /* Clear any errors and attempt a simple call */
466 s->xen->ok = 1;
467 xen_host_free(s->host);
468 if (xen_session_get_this_host(s->xen, &(s->host), s->xen) && s->xen->ok)
469 return 1;
471 /* Simple call failed. Reconnect. */
472 xen_session_logout(s->xen);
473 close(s->socket);
474 free(s);
475 return xen_utils_get_session(session);
476 }
479 /*
480 * Retrieve the domain resources (a list of VMs) using the provided
481 * session.
482 *
483 * Returns non-zero on success, 0 on failure.
484 */
485 int xen_utils_get_domain_resources(xen_utils_session *session,
486 xen_domain_resources **resources)
487 {
488 if (session == NULL)
489 return 0;
491 /* malloc a new handle for the resources list. */
492 *resources = (xen_domain_resources *)calloc(1, sizeof(xen_domain_resources));
493 if (*resources == NULL)
494 return 0;
496 /* Get the list of Xen domains. */
497 (*resources)->domains = xen_utils_enum_domains(session);
498 if ((*resources)->domains == NULL)
499 return 0;
501 (*resources)->numdomains = (*resources)->domains->size;
503 return 1;
504 }
507 /*
508 * Free the list of domain resources.
509 * Returns non-zero on success, 0 on failure.
510 */
511 int xen_utils_free_domain_resources(xen_domain_resources *resources)
512 {
513 if (resources) {
514 if (resources->domains)
515 xen_vm_set_free(resources->domains);
517 free(resources);
518 resources = NULL;
519 }
521 return 1;
522 }
525 /*
526 * Retrieve the current domain from the list of domain resources.
527 * Returns non-zero on success, 0 on failure.
528 */
529 int xen_utils_get_current_domain_resource(xen_utils_session *session,
530 xen_domain_resources *resources,
531 xen_vm_record **resource)
532 {
533 char error_msg[XEN_UTILS_ERROR_BUF_LEN];
535 if (session == NULL || resources == NULL)
536 return 0;
538 xen_vm vm = resources->domains->contents[resources->currentdomain];
539 if (!xen_vm_get_record(session->xen, resource, vm)) {
540 /* Error description in session object! */
541 XEN_UTILS_GET_ERROR_STRING(error_msg, session->xen);
542 _SBLIM_TRACE(1, ("--- xen_vm_get_record(current) failed: \"%s\"", error_msg));
543 return 0;
544 }
546 return 1;
547 }
550 /*
551 * Retrieve the next domain from the list of domain resources.
552 * Returns non-zero on success, 0 on failure.
553 */
554 int xen_utils_get_next_domain_resource(xen_utils_session *session,
555 xen_domain_resources *resources,
556 xen_vm_record **resource)
557 {
558 char error_msg[XEN_UTILS_ERROR_BUF_LEN];
560 if (session == NULL || resources == NULL)
561 return 0;
563 /* Check if reached the end of the list of Xen domain names. */
564 if (resources->currentdomain == resources->numdomains)
565 return 0;
567 xen_vm vm = resources->domains->contents[resources->currentdomain];
568 if (!xen_vm_get_record(session->xen, resource, vm)) {
569 XEN_UTILS_GET_ERROR_STRING(error_msg, session->xen);
570 _SBLIM_TRACE(1, ("--- xen_vm_get_record(next) failed: \"%s\"", error_msg));
571 /* Error description in session object! */
572 return 0;
573 }
575 /* Move the iterator to the next domain name. */
576 resources->currentdomain++;
578 return 1;
579 }
582 /*
583 * Free the domain resource specified by resource.
584 * Returns non-zero on success, 0 on failure.
585 */
586 int xen_utils_free_domain_resource(xen_vm_record *resource)
587 {
588 if (resource != NULL) {
589 xen_vm_record_free(resource);
590 resource = NULL;
591 }
593 return 1;
594 }
597 /*
598 * Retrieve a domain resource given a CIM_SettingData object path.
599 * Domain record will be placed in out param "domain".
600 * Returns non-zero on success, 0 on failure.
601 */
602 int xen_utils_get_domain_from_sd_OP(xen_utils_session *session,
603 xen_vm_record **domain,
604 const CMPIObjectPath *domOP)
605 {
606 char name[MAX_SYSTEM_NAME_LEN];
607 char error_msg[XEN_UTILS_ERROR_BUF_LEN];
608 CMPIStatus status = {CMPI_RC_OK, NULL}; /* Return status of CIM operations. */
610 if (domain == NULL) return 0;
611 if (CMIsNullObject(domOP)) return 0;
613 *domain = NULL;
615 /* Obtain the target domain name from the CMPIObjectPath "InstanceID" key. */
616 CMPIData namedata = CMGetKey(domOP, "InstanceID", &status);
617 if ((status.rc != CMPI_RC_OK) || CMIsNullValue(namedata)) return 0;
619 /* Extract the domain name string from the CMPIString. */
620 char *domainname = CMGetCharPtr(namedata.value.string);
621 if ((domainname == NULL) || (*domainname == '\0')) return 0;
623 /* Retrieve domain name from InstanceID. */
624 if (!_CMPIStrncpySystemNameFromID(name, domainname, MAX_SYSTEM_NAME_LEN))
625 return 0;
627 /* Get the domain data for the target domain name. */
628 xen_vm_set *vms;
629 if (!xen_vm_get_by_name_label(session->xen, &vms, name)) {
630 /* Error is in session object! */
631 XEN_UTILS_GET_ERROR_STRING(error_msg, session->xen);
632 _SBLIM_TRACE(1, ("--- xen_vm_get_by_name_label failed: \"%s\"", error_msg));
633 return 0;
634 }
636 assert(vms->size == 1);
637 if (!xen_vm_get_record(session->xen, domain, vms->contents[0])) {
638 /* Error description in session object! */
639 XEN_UTILS_GET_ERROR_STRING(error_msg, session->xen);
640 _SBLIM_TRACE(1, ("--- xen_vm_get_record failed: \"%s\"", error_msg));
641 xen_vm_set_free(vms);
642 return 0;
643 }
645 xen_vm_set_free(vms);
647 return 1;
648 }
651 /*
652 * Determine if the domain specified by name active.
653 * If active, isActive will be set to 1, otherwise 0.
654 * Returns non-zero on success, 0 on failure.
655 */
656 int xen_utils_is_domain_active(xen_utils_session *session,
657 const char *name,
658 int *isActive)
659 {
660 xen_vm_set *vms;
661 if (!xen_vm_get_by_name_label(session->xen, &vms, (char *)name)) {
662 /* Error is in session object! */
663 return 0;
664 }
666 if (vms->size == 0) {
667 /* There were no vms with given name - still need to free empty set. */
668 xen_vm_set_free(vms);
669 return 0;
670 }
672 assert(vms->size == 1);
673 xen_vm_record *vm_rec;
674 if (!xen_vm_get_record(session->xen, &vm_rec, vms->contents[0])) {
675 /* Error description in session object! */
676 xen_vm_set_free(vms);
677 return 0;
678 }
680 xen_vm_set_free(vms);
681 if (vm_rec->power_state == XEN_VM_POWER_STATE_HALTED)
682 *isActive = 0;
683 else
684 *isActive = 1;
686 xen_vm_record_free(vm_rec);
687 return 1;
688 }
691 /*
692 * Extract a value with given key.
693 * Returns pointer to value on success, NULL on failure.
694 *
695 */
696 char *xen_utils_get_value_from_map(xen_string_string_map *map, const char *key)
697 {
698 int i = 0;
699 // Make sure you have a good value
700 if(!map)
701 return NULL;
703 // String map is empty
704 if (map->size == 0)
705 return NULL;
707 while (i < map->size) {
708 if (strcmp(key, map->contents[i].key) == 0)
709 return map->contents[i].val;
710 i++;
711 }
713 return NULL;
714 }
717 /*
718 * Trace the error descriptions found in xen session object.
719 * This routine uses _sblim_trace function in cmpitrace interface
720 * for actual tracing. Output is to a location specified in the
721 * cmpitrace module.
722 */
723 void xen_utils_trace_error(xen_session *session)
724 {
725 char *msg = (char *)calloc(1, XEN_UTILS_ERROR_BUF_LEN);
726 if (msg == NULL)
727 return;
729 int ndx;
730 for (ndx = 0; ndx < session->error_description_count; ndx++)
731 {
732 strncat(msg, session->error_description[ndx],
733 XEN_UTILS_ERROR_BUF_LEN - (strlen(msg) + 1));
734 strncat(msg, " ", XEN_UTILS_ERROR_BUF_LEN - (strlen(msg) + 1));
735 }
737 if (msg[0] == '\0') {
738 free(msg);
739 return;
740 }
742 _sblim_trace(_SBLIM_TRACE_LEVEL_ERROR, __FILE__, __LINE__, msg);
744 /* TODO:
745 * msg is freed in _sblim_trace. The cmpitrace interface needs a little
746 * work to remove the freeing in _sblim_trace. For now, let it free the
747 * msg buffer - nothing left to do.
748 */
749 }