os-cmpi-xen

view src/xen_utils.c @ 105:05efdeb98072

Fix xen_utils_is_domain_active() to fail when domain does not exist.

Signed-off-by: Jim Fehlig <jfehlig@novell.com>
author Jim Fehlig <jfehlig@novell.com>
date Thu May 10 16:08:16 2007 -0600 (2007-05-10)
parents fa1ca0490794
children d5331bc6ed99
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_session *session)
311 {
312 xen_host host;
313 xen_vm_set *resident_vms;
314 char error_msg[XEN_UTILS_ERROR_BUF_LEN];
316 if (!xen_session_get_this_host(session, &host, session)) {
317 /* Error description in session object. */
318 XEN_UTILS_GET_ERROR_STRING(error_msg, session);
319 _SBLIM_TRACE(1, ("--- xen_session_get_this_host failed: \"%s\"", error_msg));
320 return NULL;
321 }
323 if(!xen_host_get_resident_vms(session, &resident_vms, host)) {
324 /* Error description in session object. */
325 XEN_UTILS_GET_ERROR_STRING(error_msg, session);
326 _SBLIM_TRACE(1, ("--- xen_host_get_resident_vms failed: \"%s\"", error_msg));
327 xen_host_free(host);
328 return NULL;
329 }
330 xen_host_free(host);
332 return resident_vms;
333 }
336 static int xen_utils_get_session(xen_utils_session **session)
337 {
338 xen_utils_session *s;
340 *session = NULL;
342 s = calloc(1, sizeof(xen_utils_session));
343 if (s == NULL) {
344 _SBLIM_TRACE(1, ("No memory for Xen Daemon session object"));
345 return 0;
346 }
348 s->socket = xen_utils_xend_connect_unix("/var/run/xend/xen-api.sock");
349 if (s->socket == -1) {
350 _SBLIM_TRACE(1, ("Unable to connect to Xen Daemon"));
351 free(s);
352 return 0;
353 }
355 s->xen = xen_session_login_with_password(call_func,
356 (void *)s,
357 NULL, NULL);
358 if (s->xen == NULL || !s->xen->ok) {
359 _SBLIM_TRACE(1, ("Login to Xen Daemon failed"));
360 close(s->socket);
361 free(s);
362 return 0;
363 }
365 *session = s;
366 return 1;
367 }
370 /*
371 * Generate exported functions for concatenating lists of references.
372 */
373 XEN_UTILS_REF_LIST_CONCAT(xen_vm)
374 XEN_UTILS_REF_LIST_CONCAT(xen_vdi)
375 XEN_UTILS_REF_LIST_CONCAT(xen_vbd)
376 XEN_UTILS_REF_LIST_CONCAT(xen_vif)
379 /*
380 * Generate exported functions for adding a reference (e.g. xen_vm) to a list
381 * of references.
382 */
383 XEN_UTILS_REF_LIST_ADD(xen_vm)
384 XEN_UTILS_REF_LIST_ADD(xen_vdi)
385 XEN_UTILS_REF_LIST_ADD(xen_vbd)
386 XEN_UTILS_REF_LIST_ADD(xen_vif)
389 /*
390 * Generate exported functions for adding a device (e.g. vbd) to a list
391 * of such devices.
392 */
393 XEN_UTILS_DEV_LIST_ADD(xen_vdi_record)
394 XEN_UTILS_DEV_LIST_ADD(xen_vbd_record)
395 XEN_UTILS_DEV_LIST_ADD(xen_vif_record)
398 /*
399 * Initialize a session with Xen. Providers should acquire a Xen
400 * Session when loaded into the cimom, i.e. when the provider's
401 * Initialize() method is invoked by the cimom.
402 *
403 * On success a xen_utils_session object is placed in parameter
404 * session. The object is connected and logged-in to the Xen Daemon
405 * and ready for use.
406 *
407 * Returns non-zero on success, 0 on failure.
408 */
409 int xen_utils_xen_init(xen_utils_session **session)
410 {
411 pthread_mutex_lock(&ref_count_lock);
412 if (ref_count == 0) {
413 xmlInitParser();
414 xen_init();
415 }
416 ref_count++;
417 pthread_mutex_unlock(&ref_count_lock);
419 return xen_utils_get_session(session);
420 }
423 /*
424 * Close Xen session. Connection to Xen Daemon is closed and the
425 * session handle is freed. Providers should invoke this function
426 * when their Cleanup() method is invoked by the cimom.
427 */
428 void xen_utils_xen_close(xen_utils_session *session)
429 {
430 if (session) {
431 xen_session_logout(session->xen);
432 close(session->socket);
433 free(session);
434 }
436 pthread_mutex_lock(&ref_count_lock);
437 ref_count--;
438 if (ref_count == 0)
439 {
440 xen_fini();
441 xmlCleanupParser();
442 }
443 pthread_mutex_unlock(&ref_count_lock);
444 }
447 /*
448 * Validate xend session. If sesssion is null, create one.
449 * Session is ready for use on success.
450 *
451 * Returns a non-zero on success, 0 on failure.
452 */
453 int xen_utils_validate_session(xen_utils_session **session)
454 {
455 xen_utils_session *s;
457 if (session == NULL)
458 return 0;
460 if (*session == NULL)
461 if (!xen_utils_get_session(session))
462 return 0;
464 s = *session;
466 /* Clear any errors and attempt a simple call */
467 s->xen->ok = 1;
468 xen_host host;
469 if (xen_session_get_this_host(s->xen, &host, s->xen) && s->xen->ok) {
470 xen_host_free(host);
471 return 1;
472 }
474 /* Simple call failed. Reconnect. */
475 xen_session_logout(s->xen);
476 close(s->socket);
477 s->socket = xen_utils_xend_connect_unix("/var/run/xend/xen-api.sock");
478 if (s->socket == -1) {
479 _SBLIM_TRACE(1, ("Unable to connect to Xen Daemon"));
480 free(s);
481 *session = NULL;
482 return 0;
483 }
485 s->xen = xen_session_login_with_password(call_func,
486 (void *)s,
487 NULL, NULL);
488 if (s->xen == NULL || !s->xen->ok) {
489 _SBLIM_TRACE(1, ("Login to Xen Daemon failed"));
490 close(s->socket);
491 free(s);
492 *session = NULL;
493 return 0;
494 }
496 return 1;
497 }
500 /*
501 * Retrieve the domain resources (a list of VMs) using the provided
502 * session.
503 *
504 * Returns non-zero on success, 0 on failure.
505 */
506 int xen_utils_get_domain_resources(xen_utils_session *session,
507 xen_domain_resources **resources)
508 {
509 if (session == NULL)
510 return 0;
512 /* malloc a new handle for the resources list. */
513 *resources = (xen_domain_resources *)malloc(sizeof(xen_domain_resources));
514 if (*resources == NULL)
515 return 0;
517 /*
518 * Get the list of Xen domains. If there are no domains, we'll
519 * explicitly set numdomains to 0.
520 */
521 (*resources)->domains = xen_utils_enum_domains(session->xen);
522 if ((*resources)->domains == NULL)
523 return 0;
525 (*resources)->numdomains = (*resources)->domains->size;
527 /* Start iterating from the first Xen domain name. */
528 (*resources)->currentdomain = 0;
530 return 1;
531 }
534 /*
535 * Free the list of domain resources.
536 * Returns non-zero on success, 0 on failure.
537 */
538 int xen_utils_free_domain_resources(xen_domain_resources *resources)
539 {
540 if (resources) {
541 if (resources->domains)
542 xen_vm_set_free(resources->domains);
544 free(resources);
545 resources = NULL;
546 }
548 return 1;
549 }
552 /*
553 * Retrieve the current domain from the list of domain resources.
554 * Returns non-zero on success, 0 on failure.
555 */
556 int xen_utils_get_current_domain_resource(xen_utils_session *session,
557 xen_domain_resources *resources,
558 xen_vm_record **resource)
559 {
560 char error_msg[XEN_UTILS_ERROR_BUF_LEN];
562 if (session == NULL || resources == NULL)
563 return 0;
565 xen_vm vm = resources->domains->contents[resources->currentdomain];
566 if (!xen_vm_get_record(session->xen, resource, vm)) {
567 /* Error description in session object! */
568 XEN_UTILS_GET_ERROR_STRING(error_msg, session->xen);
569 _SBLIM_TRACE(1, ("--- xen_vm_get_record(current) failed: \"%s\"", error_msg));
570 return 0;
571 }
573 return 1;
574 }
577 /*
578 * Retrieve the next domain from the list of domain resources.
579 * Returns non-zero on success, 0 on failure.
580 */
581 int xen_utils_get_next_domain_resource(xen_utils_session *session,
582 xen_domain_resources *resources,
583 xen_vm_record **resource)
584 {
585 char error_msg[XEN_UTILS_ERROR_BUF_LEN];
587 if (session == NULL || resources == NULL)
588 return 0;
590 /* Check if reached the end of the list of Xen domain names. */
591 if (resources->currentdomain == resources->numdomains)
592 return 0;
594 xen_vm vm = resources->domains->contents[resources->currentdomain];
595 if (!xen_vm_get_record(session->xen, resource, vm)) {
596 XEN_UTILS_GET_ERROR_STRING(error_msg, session->xen);
597 _SBLIM_TRACE(1, ("--- xen_vm_get_record(next) failed: \"%s\"", error_msg));
598 /* Error description in session object! */
599 return 0;
600 }
602 /* Move the iterator to the next domain name. */
603 resources->currentdomain++;
605 return 1;
606 }
609 /*
610 * Free the domain resource specified by resource.
611 * Returns non-zero on success, 0 on failure.
612 */
613 int xen_utils_free_domain_resource(xen_vm_record *resource)
614 {
615 if (resource != NULL) {
616 xen_vm_record_free(resource);
617 resource = NULL;
618 }
620 return 1;
621 }
624 /*
625 * Retrieve a domain resource given a CIM_SettingData object path.
626 * Domain record will be placed in out param "domain".
627 * Returns non-zero on success, 0 on failure.
628 */
629 int xen_utils_get_domain_from_sd_OP(xen_utils_session *session,
630 xen_vm_record **domain,
631 const CMPIObjectPath *domOP)
632 {
633 char name[MAX_SYSTEM_NAME_LEN];
634 char error_msg[XEN_UTILS_ERROR_BUF_LEN];
635 CMPIStatus status = {CMPI_RC_OK, NULL}; /* Return status of CIM operations. */
637 if (domain == NULL) return 0;
638 if (CMIsNullObject(domOP)) return 0;
640 *domain = NULL;
642 /* Obtain the target domain name from the CMPIObjectPath "InstanceID" key. */
643 CMPIData namedata = CMGetKey(domOP, "InstanceID", &status);
644 if ((status.rc != CMPI_RC_OK) || CMIsNullValue(namedata)) return 0;
646 /* Extract the domain name string from the CMPIString. */
647 char *domainname = CMGetCharPtr(namedata.value.string);
648 if ((domainname == NULL) || (*domainname == '\0')) return 0;
650 /* Retrieve domain name from InstanceID. */
651 if (!_CMPIStrncpySystemNameFromID(name, domainname, MAX_SYSTEM_NAME_LEN))
652 return 0;
654 /* Get the domain data for the target domain name. */
655 xen_vm_set *vms;
656 if (!xen_vm_get_by_name_label(session->xen, &vms, name)) {
657 /* Error is in session object! */
658 XEN_UTILS_GET_ERROR_STRING(error_msg, session->xen);
659 _SBLIM_TRACE(1, ("--- xen_vm_get_by_name_label failed: \"%s\"", error_msg));
660 return 0;
661 }
663 assert(vms->size == 1);
664 if (!xen_vm_get_record(session->xen, domain, vms->contents[0])) {
665 /* Error description in session object! */
666 XEN_UTILS_GET_ERROR_STRING(error_msg, session->xen);
667 _SBLIM_TRACE(1, ("--- xen_vm_get_record failed: \"%s\"", error_msg));
668 xen_vm_set_free(vms);
669 return 0;
670 }
672 xen_vm_set_free(vms);
674 return 1;
675 }
678 /*
679 * Determine if the domain specified by name active.
680 * If active, isActive will be set to 1, otherwise 0.
681 * Returns non-zero on success, 0 on failure.
682 */
683 int xen_utils_is_domain_active(xen_utils_session *session,
684 const char *name,
685 int *isActive)
686 {
687 xen_vm_set *vms;
688 if (!xen_vm_get_by_name_label(session->xen, &vms, (char *)name)) {
689 /* Error is in session object! */
690 return 0;
691 }
693 if (vms->size == 0) {
694 /* There were no vms with given name - still need to free empty set. */
695 xen_vm_set_free(vms);
696 return 0;
697 }
699 assert(vms->size == 1);
700 xen_vm_record *vm_rec;
701 if (!xen_vm_get_record(session->xen, &vm_rec, vms->contents[0])) {
702 /* Error description in session object! */
703 xen_vm_set_free(vms);
704 return 0;
705 }
707 xen_vm_set_free(vms);
708 if (vm_rec->power_state == XEN_VM_POWER_STATE_HALTED)
709 *isActive = 0;
710 else
711 *isActive = 1;
713 xen_vm_record_free(vm_rec);
714 return 1;
715 }
718 /*
719 * Extract a value with given key.
720 * Returns pointer to value on success, NULL on failure.
721 *
722 */
723 char *xen_utils_get_value_from_map(xen_string_string_map *map, const char *key)
724 {
725 int i = 0;
726 // Make sure you have a good value
727 if(!map)
728 return NULL;
730 // String map is empty
731 if (map->size == 0)
732 return NULL;
734 while (i < map->size) {
735 if (strcmp(key, map->contents[i].key) == 0)
736 return map->contents[i].val;
737 i++;
738 }
740 return NULL;
741 }