342582ab7ff8cb60e8ebc3b4379b74d949db8f99
[libvscmgmt.git] / lib / vm.c
1 /*
2  * vm.c: Library for Virtualized Supercomputer Management
3  *
4  * Copyright (C) 2009 Matthias Bolte <matthias.bolte@googlemail.com>
5  * Copyright (C) 2009 Maximilian Wilhelm <max@rfc2324.org>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
20  */
21
22 /*!
23  * @file
24  */
25
26 #include <errno.h> /* errno */
27 #include <sys/types.h> /* stat */
28 #include <sys/stat.h> /* stat */
29 #include <sys/statvfs.h> /* statvfs */
30 #include <unistd.h> /* stat */
31 #include <string.h> /* strerror */
32
33 #include <libvirt/libvirt.h>
34 #include <libvirt/virterror.h>
35
36 #include <libvscmisc/assert.h>
37 #include <libvscmisc/buffer.h>
38 #include <libvscmisc/error.h>
39 #include <libvscmisc/memory.h>
40
41 #include "../include/libvscmgmt/ipv4.h"
42 #include "../include/libvscmgmt/uuid.h"
43 #include "../include/libvscmgmt/vm.h"
44 #include "../backend/data/data.h"
45 #include "../backend/network/network.h"
46 #include "core.h"
47 #include "config.h"
48 #include "host.h"
49 #include "image.h"
50 #include "ipv4.h"
51 #include "mac.h"
52 #include "uuid.h"
53
54 /*
55  * Import global config options from core.c
56  *
57  */
58 extern int _vsc_mgmt_initialized;
59 extern struct VscMgmtConfig _vsc_mgmt_config;
60
61
62 /*******************************************************************************
63  *                              internal functions                             *
64  *******************************************************************************/
65
66
67 static void
68 _vm_free (struct VscMgmtVm **vm)
69 {
70         VSC__ASSERT (vm != NULL);
71
72         vsc_free (&(*vm)->info.image_filename);
73         vsc_free (&(*vm)->info.checkpoint_uuid_list);
74
75         vsc_free (vm);
76 }
77
78
79
80 static struct VscMgmtVm *
81 _vm_new (struct VscError *error, const struct VscMgmtUuid *vm_uuid,
82          const struct VscMgmtUuid *network_uuid,
83          const struct VscMgmtIpv4 *host_ip,
84          const struct VscMgmtVmResourceConfig *vm_resource_config)
85 {
86         struct VscMgmtVm *new_vm = NULL;
87
88         VSC__ASSERT (error != NULL);
89         VSC__ASSERT (! error->occured);
90         VSC__ASSERT (vm_uuid != NULL);
91         VSC__ASSERT (network_uuid != NULL);
92         VSC__ASSERT (host_ip != NULL);
93         VSC__ASSERT (vm_resource_config != NULL);
94
95         new_vm = vsc_alloc (error, sizeof (struct VscMgmtVm));
96         if (error->occured) {
97                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
98                 return NULL;
99         }
100
101         /* Copy the VMs UUId and compute and save it's string representation */
102         memcpy (new_vm->info.uuid.value, vm_uuid->value, VSC_MGMT__UUID__SIZE);
103
104         new_vm->info.uuid_string = vsc_alloc (error, VSC_MGMT__UUID__STRING_SIZE);
105         if (error->occured) {
106                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
107                 goto failure;
108         }
109         vsc_mgmt_uuid_format (&new_vm->info.uuid, new_vm->info.uuid_string);
110
111         /* Acquire an IP */
112         _vsc_mgmt_network_acquire_ipv4 (error, network_uuid, &new_vm->info.ipv4);
113         if (error->occured) {
114                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
115                 goto failure;
116         }
117
118         /* Use the IP and acquire a MAC for it */
119         _vsc_mgmt_network_use_ipv4 (error, network_uuid, &new_vm->info.ipv4,
120                                     &new_vm->info.mac, &new_vm->info.uuid);
121         if (error->occured) {
122                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
123                 goto failure;
124         }
125
126         /* Compute and store the VM filename */
127         new_vm->info.image_filename = vsc_alloc (error, IMAGE_FILENAME_LENGTH);
128         if (error->occured) {
129                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
130                 goto failure;
131         }
132
133         _vsc_mgmt_image_gen_path (&new_vm->info.uuid, new_vm->info.image_filename);
134
135         /* Set the host IP */
136         _vsc_mgmt_ipv4_copy (&new_vm->host_ip, host_ip);
137
138         /* Copy the VM ressource config */
139         memcpy (&new_vm->info.resource_config, vm_resource_config,
140                 sizeof (struct VscMgmtVmResourceConfig));
141
142         /* Set some more vm information */
143         new_vm->info.state = VSC_MGMT__VM_STATE__POWERED_OFF;
144
145         memcpy (new_vm->info.network_uuid.value, network_uuid->value,
146                 VSC_MGMT__UUID__SIZE);
147
148         return new_vm;
149
150 failure:
151         _vm_free (&new_vm);
152
153         return NULL;
154 }
155
156
157
158 static void
159 _vm_info_copy (struct VscError *error, struct VscMgmtVmInfo *dest,
160                const struct VscMgmtVmInfo *src)
161 {
162         VSC__ASSERT (error != NULL);
163         VSC__ASSERT (! error->occured);
164         VSC__ASSERT (dest != NULL);
165         VSC__ASSERT (src != NULL);
166
167
168         /* Copy the struct content */
169         memcpy (dest, src, sizeof (struct VscMgmtVmInfo));
170
171         /* Prepare for 'free' case */
172         dest->image_filename = NULL;
173         dest->checkpoint_uuid_list = NULL;
174         dest->uuid_string = NULL;
175
176         /* Copy the image filename */
177         if (src->image_filename != NULL) {
178                 dest->image_filename = strdup (src->image_filename);
179                 if (! dest->image_filename) {
180                         VSC__ERROR1 (error, VSC__ERROR_CODE__OUT_OF_MEMORY,
181                                      "Insufficient memory available to copy image_filename");
182                         goto free;
183                 }
184         }
185
186         /* Clone the checkpoint uuid list */
187         dest->checkpoint_uuid_list = _vsc_mgmt_uuid_list_clone (error,
188                                                                 src->checkpoint_uuid_list);
189         if (error->occured) {
190                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
191                 goto free;
192         }
193
194         /* Copy the VM UUID string representation */
195         dest->uuid_string = strdup (src->uuid_string);
196         if (! dest->uuid_string) {
197                 VSC__ERROR1 (error, VSC__ERROR_CODE__OUT_OF_MEMORY,
198                              "Insufficient memory available to copy uuid_string");
199                 goto free;
200         }
201
202         return;
203
204 free:
205         /* Don't leak */
206         vsc_mgmt_vm_info_cleanup (dest);
207
208         return;
209 }
210
211
212
213 static char *
214 _gen_vm_xml_string (struct VscError *error, const struct VscMgmtVm *vm,
215                     enum VscMgmtHostType host_type)
216 {
217         struct VscBuffer buffer;
218
219         VSC__ASSERT (error != NULL);
220         VSC__ASSERT (! error->occured);
221         VSC__ASSERT (vm != NULL);
222
223         vsc_buffer_init (&buffer);
224
225         /*
226          * Virtual hosts
227          */
228         if (host_type == VSC_MGMT__HOST_TYPE__VIRTUAL) {
229                 vsc_buffer_append (&buffer,
230                                    "<domain type='test'>\n"
231                                    "  <os>\n"
232                                    "    <type arch='i686'>hvm</type>\n"
233                                    "    <boot dev='hd' />\n"
234                                    "  </os>\n");
235         }
236
237
238         /*
239          * Xen hosts
240          */
241         else if (host_type == VSC_MGMT__HOST_TYPE__XEN) {
242                 vsc_buffer_sprintf (&buffer,
243                                     "<domain type='xen'>\n"
244                                     "  <os>\n"
245                                     "    <type arch='i686'>hvm</type>\n"
246                                     "    <loader>%s</loader>\n"
247                                     "    <boot dev='hd' />\n"
248                                     "  </os>\n",
249                                     _vsc_mgmt_config.xen.hvm_loader_path);
250
251                 vsc_buffer_sprintf (&buffer,
252                                     "  <devices>\n"
253                                     "   <disk type='file'>\n"
254                                     "     <source file='%s'/>\n"
255                                     "     <target dev='hda' bus='xen'/>\n"
256                                     "   </disk>\n",
257                                     vm->info.image_filename);
258
259                 vsc_buffer_sprintf (&buffer,
260                                     "   <interface type='bridge'>\n"
261                                     "     <source bridge='%s'/>\n"
262                                     "     <mac address='%02x:%02x:%02x:%02x:%02x:%02x'/>\n"
263                                     "   </interface>\n"
264                                     "   <graphics type='vnc' port='-1' listen='0.0.0.0'/>\n"
265                                     "  </devices>\n",
266                                     _vsc_mgmt_config.xen.bridge_device_name,
267                                     vm->info.mac.bytes[0], vm->info.mac.bytes[1],
268                                     vm->info.mac.bytes[2], vm->info.mac.bytes[3],
269                                     vm->info.mac.bytes[4], vm->info.mac.bytes[5]);
270         }
271
272
273         /*
274          * ESX hosts
275          */
276         else if (host_type == VSC_MGMT__HOST_TYPE__ESX) {
277                 vsc_buffer_append (&buffer,
278                                    "<domain type='vmware'>\n"
279                                    "  <os>\n"
280                                    "    <type arch='i686'>hvm</type>\n"
281                                    "    <boot dev='hd' />\n"
282                                    "  </os>\n");
283
284                 vsc_buffer_sprintf (&buffer,
285                                     " <devices>\n"
286                                     "  <disk type='file'>\n"
287                                     "    <source file='[%s] %s/%s.vmdk'/>\n"
288                                     "    <target dev='sda' bus='scsi'/>\n"
289                                     "  </disk>\n",
290                                     _vsc_mgmt_config.esx.data_store_name,
291                                     vm->info.uuid_string, vm->info.uuid_string);
292
293                 vsc_buffer_sprintf (&buffer,
294                                     "  <interface type='bridge'>\n"
295                                     "   <mac address='%02x:%02x:%02x:%02x:%02x:%02x'/>\n"
296                                     "   <source bridge='%s'/>\n"
297                                     "  </interface>\n"
298                                     " </devices>\n",
299                                     vm->info.mac.bytes[0], vm->info.mac.bytes[1],
300                                     vm->info.mac.bytes[2], vm->info.mac.bytes[3],
301                                     vm->info.mac.bytes[4], vm->info.mac.bytes[5],
302                                     _vsc_mgmt_config.esx.vm_network_name);
303         }
304
305
306         /*
307          * Common stuff
308          */
309         vsc_buffer_sprintf (&buffer,
310                             "  <name>%s</name>\n"
311                             "  <uuid>%s</uuid>\n"
312                             "  <memory>%lu</memory>\n"
313                             "  <vcpu>%u</vcpu>\n",
314                             vm->info.uuid_string,
315                             vm->info.uuid_string,
316                             vm->info.resource_config.memory_size,
317                             vm->info.resource_config.num_cores);
318
319         vsc_buffer_append (&buffer,
320                            "  <clock offset='utc' />\n"
321                            "  <on_poweroff>destroy</on_poweroff>\n"
322                            "  <on_reboot>restart</on_reboot>\n"
323                            "  <on_crash>destroy</on_crash>\n"
324                            "</domain>\n");
325
326         if (buffer.error) {
327                 VSC__ERROR0 (error, VSC__ERROR_CODE__OUT_OF_MEMORY);
328                 return NULL;
329         }
330
331         return vsc_buffer_take (&buffer);
332 }
333
334
335
336 static void
337 _vm_resource_config_validate (struct VscError *error,
338                               const struct VscMgmtVmResourceConfig *resource_config)
339 {
340         VSC__ASSERT (error != NULL);
341         VSC__ASSERT (! error->occured);
342         VSC__ASSERT (resource_config != NULL);
343
344         if (resource_config->num_cores < 1) {
345                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
346                              "Number of cores has to be greater than one, got %d",
347                              resource_config->num_cores);
348                 return;
349         }
350
351         if (resource_config->core_clock < 0) {
352                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
353                              "Core clock has to be greater than zero, got %d",
354                              resource_config->core_clock);
355                 return;
356         }
357
358         if (resource_config->memory_size < 1) {
359                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
360                              "Memory size must be greater than one, got %lu",
361                              resource_config->memory_size);
362                 return;
363         }
364
365         if ((resource_config->memory_size % 4096) != 0) {
366                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
367                              "Memory size has to be multiple of 4096, got %lu",
368                              resource_config->memory_size);
369                 return;
370         }
371 }
372
373
374
375 static void
376 _apply_vm_sched_parameters (struct VscError *error, virDomainPtr domain,
377                             const struct VscMgmtVmResourceConfig *resource_config,
378                             const struct VscMgmtHost *host)
379 {
380         int totalRequestedMhz;
381         char *sched_name = NULL;
382         virSchedParameterPtr sched_params = NULL;
383         int num_sched_params = 0;
384         char host_ipv4_string[VSC_MGMT__IPV4__STRING_SIZE] = "";
385
386         VSC__ASSERT (error != NULL);
387         VSC__ASSERT (! error->occured);
388         VSC__ASSERT (domain != NULL);
389         VSC__ASSERT (resource_config != NULL);
390         VSC__ASSERT (host != NULL);
391
392         /* Prepare scheduler parameters */
393         switch (host->info.type) {
394         case VSC_MGMT__HOST_TYPE__VIRTUAL:
395                 /* Do nothing */
396                 break;
397
398         case VSC_MGMT__HOST_TYPE__XEN:
399                 /*
400                  * From http://wiki.xensource.com/xenwiki/CreditScheduler
401                  *
402                  * "The cap is expressed in percentage of one physical CPU:
403                  *  100 is 1 physical CPU, 50 is half a CPU, 400 is 4 CPUs,
404                  *  etc..."
405                  */
406                 sched_name = virDomainGetSchedulerType (domain, NULL);
407                 if (! sched_name) {
408                         _ERROR_FROM_LIBVIRT (error, virDomainGetConnect (domain));
409                         goto cleanup;
410                 }
411
412                 if (strcmp (sched_name, "credit") != 0) {
413                         VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_CALL,
414                                      "Unexpected scheduler type '%s', expecting 'credit'",
415                                      sched_name);
416                         goto cleanup;
417                 }
418
419                 totalRequestedMhz = resource_config->num_cores * resource_config->core_clock;
420                 num_sched_params = 2;
421
422                 sched_params = vsc_alloc (error, sizeof(virSchedParameter) * num_sched_params);
423                 if (error->occured) {
424                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
425                         goto cleanup;
426                 }
427
428                 snprintf (sched_params[0].field, VIR_DOMAIN_SCHED_FIELD_LENGTH, "%s", "weight");
429                 sched_params[0].type = VIR_DOMAIN_SCHED_FIELD_UINT;
430                 sched_params[0].value.ui = 256; /* enforce the default */
431
432                 snprintf (sched_params[1].field, VIR_DOMAIN_SCHED_FIELD_LENGTH, "%s", "cap");
433                 sched_params[1].type = VIR_DOMAIN_SCHED_FIELD_UINT;
434                 sched_params[1].value.ui = (totalRequestedMhz * 100) / host->info.core_clock;
435
436                 break;
437
438         case VSC_MGMT__HOST_TYPE__ESX:
439                 /*
440                  * Like Xen, ESX handles CPU allocation per virtual machine, not per
441                  * single virtual CPU.
442                  */
443                 sched_name = virDomainGetSchedulerType (domain, NULL);
444                 if (! sched_name) {
445                         _ERROR_FROM_LIBVIRT (error, virDomainGetConnect (domain));
446                         goto cleanup;
447                 }
448
449                 if (strcmp (sched_name, "allocation") != 0) {
450                         VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_CALL,
451                                      "Unexpected scheduler type '%s', expecting 'allocation'",
452                                      sched_name);
453                         goto cleanup;
454                 }
455
456                 totalRequestedMhz = resource_config->num_cores * resource_config->core_clock;
457                 num_sched_params = 3;
458
459                 sched_params = vsc_alloc (error, sizeof(virSchedParameter) * num_sched_params);
460                 if (error->occured) {
461                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
462                         goto cleanup;
463                 }
464
465                 snprintf (sched_params[0].field, VIR_DOMAIN_SCHED_FIELD_LENGTH, "%s", "reservation");
466                 sched_params[0].type = VIR_DOMAIN_SCHED_FIELD_LLONG;
467
468                 if (_vsc_mgmt_config.esx.set_cpu_reservation) {
469                         sched_params[0].value.ui = totalRequestedMhz;
470                 } else {
471                         sched_params[0].value.ui = 0; /* no reservation */
472                 }
473
474                 snprintf (sched_params[1].field, VIR_DOMAIN_SCHED_FIELD_LENGTH, "%s", "limit");
475                 sched_params[1].type = VIR_DOMAIN_SCHED_FIELD_LLONG;
476                 sched_params[1].value.ui = totalRequestedMhz;
477
478                 snprintf (sched_params[2].field, VIR_DOMAIN_SCHED_FIELD_LENGTH, "%s", "shares");
479                 sched_params[2].type = VIR_DOMAIN_SCHED_FIELD_INT;
480                 sched_params[2].value.ui = -2; /* -2 = normal, enforce the default */
481
482                 break;
483
484         default:
485                 vsc_mgmt_ipv4_format (&host->info.ipv4, host_ipv4_string);
486                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
487                              "Host %s has unknown type", host_ipv4_string);
488                 goto cleanup;
489         }
490
491         /* Apply scheduler parameters */
492         if (sched_params != NULL &&
493             virDomainSetSchedulerParameters (domain, sched_params, num_sched_params) != 0) {
494                 _ERROR_FROM_LIBVIRT (error, virDomainGetConnect (domain));
495                 goto cleanup;
496         }
497
498 cleanup:
499         vsc_free (&sched_params);
500         vsc_free (&sched_name);
501 }
502
503
504
505 static virDomainPtr
506 _vm_get_domain_pointer (struct VscError *error, const struct VscMgmtVm *vm)
507 {
508         virConnectPtr host_conn = NULL;
509         virDomainPtr domain = NULL;
510
511         host_conn = _vsc_mgmt_data_host_get_connection (&vm->host_ip);
512         if (! host_conn) {
513                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
514                              "Failed to get libVirt connection pointer for host"
515                              "%u.%u.%u.%u", vm->host_ip.bytes[0],
516                              vm->host_ip.bytes[1], vm->host_ip.bytes[2],
517                              vm->host_ip.bytes[3]);
518                 return NULL;
519         }
520
521         domain = virDomainLookupByUUID (host_conn, vm->info.uuid.value);
522         if (! domain) {
523                 _ERROR_FROM_LIBVIRT (error, host_conn);
524                 return NULL;
525         }
526
527         return domain;
528 }
529
530
531 /*******************************************************************************
532  *                              public API functions                           *
533  *******************************************************************************/
534
535
536
537 void
538 vsc_mgmt_vm_deploy (struct VscError *error,
539                     const struct VscMgmtIpv4 *host_ipv4_list,
540                     const struct VscMgmtUuid *vm_uuid_list,
541                     const struct VscMgmtVmResourceConfig *vm_resource_config_list,
542                     const struct VscMgmtUuid *network_uuid)
543 {
544         struct VscError *cleanup_error = NULL;
545         struct VscError dummy_error;
546
547         struct timeval time_stamp;
548
549         struct VscMgmtIpv4 *ip_loop = NULL;
550         enum VscMgmtHostType host_type = VSC_MGMT__HOST_TYPE__UNDEFINED;
551         struct VscMgmtHost *host_tmp = NULL;
552
553         struct VscMgmtUuid *vm_uuid_loop = NULL;
554         char vm_image_filename[IMAGE_FILENAME_LENGTH];
555         char vm_uuid_string[VSC_MGMT__UUID__STRING_SIZE];
556
557         struct VscMgmtVmResourceConfig *vm_res_loop = NULL;
558
559         /* List of VMs to be deployed */
560         struct VscMgmtVm *vm_list = NULL;
561         struct VscMgmtVm *vm_list_last = NULL;
562         struct VscMgmtVm *vm_tmp = NULL;
563
564         char *domain_xml = NULL;
565         virDomainPtr domain = NULL;
566
567         VSC__ASSERT (error != NULL);
568         VSC__ASSERT (! error->occured);
569         VSC__ASSERT (host_ipv4_list != NULL);
570         VSC__ASSERT (vm_uuid_list != NULL);
571         VSC__ASSERT (vm_resource_config_list != NULL);
572         VSC__ASSERT (network_uuid != NULL);
573
574         _IF_NOT_INITIALIZED (return);
575
576         vsc_error_init (&dummy_error);
577
578         /* Now we need a LOCK */
579         _vsc_mgmt_lock ();
580
581         /*
582          * Valid input parameters:
583          * - Check if all hosts are known and of the same type.
584          * - Check if there as many UUIDs as hosts
585          * - Check if all images are there (if deploying on real hardware)
586          */
587         vm_uuid_loop = (struct VscMgmtUuid *) vm_uuid_list;
588         vm_res_loop = (struct VscMgmtVmResourceConfig *) vm_resource_config_list;
589         for (ip_loop = (struct VscMgmtIpv4 *) host_ipv4_list; ip_loop; ip_loop = ip_loop->next) {
590                 /* Check if the hosts is known to the library */
591                 host_tmp = NULL;
592                 host_tmp = _vsc_mgmt_data_host_lookup_by_ip (ip_loop);
593                 if (! host_tmp) {
594                         VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
595                                      "Unkonwn host IP in host list: %u.%u.%u.%u",
596                                      ip_loop->bytes[0], ip_loop->bytes[1],
597                                      ip_loop->bytes[2], ip_loop->bytes[3]);
598                         goto cleanup_unlock;
599                 }
600
601                 /* Check if the type is consistant to the type of all seen hosts */
602                 if (host_type != VSC_MGMT__HOST_TYPE__UNDEFINED) {
603                         if (host_tmp->info.type != host_type) {
604                                 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
605                                              "Only one host type allowed per call.");
606                                 goto cleanup_unlock;
607                         }
608                 } else {
609                         host_type = host_tmp->info.type;
610                 }
611
612
613                 /*
614                  * Check if there are enough UUIDs for all VMs to be deployed
615                  */
616                 if (vm_uuid_loop == NULL) {
617                         VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
618                                      "Fewer UUIDs than hosts given.");
619                         goto cleanup_unlock;
620                 }
621
622                 /*
623                  * Check if this is an unknown UUID
624                  */
625                 if (_vsc_mgmt_data_vm_lookup_by_uuid (vm_uuid_loop)) {
626                         vsc_mgmt_uuid_format (vm_uuid_loop, vm_uuid_string);
627                         VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
628                                      "Can't use existing VM UUID %s.",
629                                      vm_uuid_string);
630                         goto cleanup_unlock;
631                 }
632
633                 /*
634                  * Check if there are enough resource configs for all VMs to be deployed.
635                  */
636                 if (vm_res_loop == NULL) {
637                         VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
638                                      "Fewer resource configs than hosts given.");
639                         goto cleanup_unlock;
640                 }
641
642                 _vm_resource_config_validate (error, vm_res_loop);
643                 if (error->occured) {
644                         vsc_mgmt_uuid_format (vm_uuid_loop, vm_uuid_string);
645                         VSC__APPEND_ERROR2 (error, VSC__ERROR_CODE__TRACE,
646                                             "Invalid resource config for VM '%s'",
647                                             vm_uuid_string);
648                         goto cleanup_unlock;
649                 }
650
651                 /*
652                  * If this VM should be deployed on real hardware, check if the image file
653                  * if already there.
654                  */
655                 _vsc_mgmt_image_gen_path (vm_uuid_loop, vm_image_filename);
656
657                 if (host_type != VSC_MGMT__HOST_TYPE__VIRTUAL &&
658                     access (vm_image_filename, F_OK) != 0) {
659                         vsc_mgmt_uuid_format (vm_uuid_loop, vm_uuid_string);
660                         VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_CALL,
661                                      "Image file for VM '%s' not found.",
662                                      vm_uuid_string);
663                         goto cleanup_unlock;
664                 }
665
666                 vm_uuid_loop = vm_uuid_loop->next;
667                 vm_res_loop = vm_res_loop->next;
668         }
669
670
671         /*
672          * Check if there are no more UUIDs given than hosts to deploy VMs on.
673          */
674         if (vm_uuid_loop != NULL) {
675                 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
676                              "More UUIDs than hosts given.");
677                 goto cleanup_unlock;
678         }
679
680         /*
681          * Check if there are no more resource configs given than hosts to deploy VMs on.
682          */
683         if (vm_res_loop != NULL) {
684                 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
685                              "More resource configs than hosts given.");
686                 goto cleanup_unlock;
687         }
688
689
690         /*
691          * Query current time stamp to set it to the VmInfo structs later on
692          */
693         if (gettimeofday (&time_stamp, NULL) != 0) {
694                 VSC__ERROR2 (error, VSC__ERROR_CODE__ERRNO,
695                              "Error while getting deployment time: %s",
696                              strerror (errno));
697                 goto cleanup_unlock;
698         }
699
700
701         /*
702          * Set up a VM struct for each VM which will be put on this host.
703          */
704         vm_uuid_loop = (struct VscMgmtUuid *) vm_uuid_list;
705         vm_res_loop = (struct VscMgmtVmResourceConfig *) vm_resource_config_list;
706         for (ip_loop = (struct VscMgmtIpv4 *) host_ipv4_list; ip_loop; ip_loop = ip_loop->next) {
707                 /* Set up the vm struct */
708                 vm_tmp = _vm_new (error, vm_uuid_loop, network_uuid, ip_loop, vm_res_loop);
709                 if (error->occured) {
710                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
711                         goto cleanup_unlock;
712                 }
713
714                 /* Append this VM to the list of VMs */
715                 if (vm_list != NULL) {
716                         vm_list_last->next = vm_tmp;
717                 } else {
718                         vm_list = vm_tmp;
719                 }
720
721                 vm_list_last = vm_tmp;
722
723                 /*
724                  * Get the next UUID for the next VM.
725                  */
726                 vm_uuid_loop = vm_uuid_loop->next;
727                 vm_res_loop = vm_res_loop->next;
728         }
729
730
731
732         /* Write a new DHCP config file */
733         _vsc_mgmt_network_synchronize (error);
734         if (error->occured) {
735                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
736                 goto cleanup_unlock;
737         }
738
739
740
741         /*
742          * Ok, now we've put everything together to define the VMs to libVirt
743          * and put the VMs into the data storage.
744          */
745         for (vm_tmp = vm_list; vm_tmp; vm_tmp = vm_tmp->next) {
746                 host_tmp = NULL;
747                 host_tmp = _vsc_mgmt_data_host_lookup_by_ip (&vm_tmp->host_ip);
748                 if (! host_tmp) {
749                         VSC__ERROR0 (error, VSC__ERROR_CODE__INTERNAL_ERROR);
750                         goto rollback_cleanup_unlock;
751                 }
752
753                 domain_xml = NULL;
754                 domain_xml = _gen_vm_xml_string (error, vm_tmp, host_type);
755                 if (error->occured) {
756                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
757                         goto rollback_cleanup_unlock;
758                 }
759
760                 /* Define the VM on the host */
761                 domain = NULL;
762                 domain = virDomainDefineXML (host_tmp->connection, domain_xml);
763                 if (! domain) {
764                         _ERROR_FROM_LIBVIRT (error, host_tmp->connection);
765                         goto rollback_cleanup_unlock;
766                 }
767
768                 /* Set VmInfo and HostInfo time stamps */
769                 memcpy (&vm_tmp->info.deployed_at, &time_stamp, sizeof (struct timeval));
770                 memcpy (&vm_tmp->info.last_state_change, &time_stamp, sizeof (struct timeval));
771                 memcpy (&vm_tmp->info.last_config_change, &time_stamp, sizeof (struct timeval));
772                 memcpy (&host_tmp->info.last_modification, &time_stamp, sizeof (struct timeval));
773                 memcpy (&host_tmp->info.last_resource_allocation, &time_stamp, sizeof (struct timeval));
774
775                 /* Don't leak domain XML string representation */
776                 vsc_free (&domain_xml);
777
778
779                 /* Add it to the data storage */
780                 _vsc_mgmt_data_vm_add (error, vm_tmp);
781                 if (error->occured) {
782                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
783                         goto rollback_cleanup_unlock;
784                 }
785         }
786
787
788         /* Release the LOCK */
789         _vsc_mgmt_unlock ();
790
791         return;
792
793
794 cleanup_unlock:
795         cleanup_error = vsc_error_new ();
796
797         /* Release all the IPs and free the VM structs */
798         vm_tmp = vm_list;
799         while (vm_tmp) {
800                 /* Release the IP to the network backend */
801                 _vsc_mgmt_network_release_ipv4 (cleanup_error, network_uuid,
802                                                 &vm_tmp->info.ipv4);
803                 if (cleanup_error->occured) {
804                         VSC__APPEND_ERROR0 (cleanup_error, VSC__ERROR_CODE__TRACE);
805                         vsc_error_append_branch (error, cleanup_error);
806
807                         cleanup_error = vsc_error_new ();
808                 }
809
810                 /* Forget about the VM (use 'vm_list_last' as tmp pointer) */
811                 vm_list_last = vm_tmp;
812                 vm_tmp = vm_tmp->next;
813                 _vm_free (&vm_list_last);
814         }
815
816         /* Some things can be cleanup without holding the lock any longer */
817         _vsc_mgmt_unlock ();
818
819         if (! cleanup_error->occured) {
820                 vsc_error_free (&cleanup_error);
821         }
822
823         return;
824
825
826 rollback_cleanup_unlock:
827         for (vm_tmp = vm_list; vm_tmp; vm_tmp = vm_tmp->next) {
828                 /* As this does not return an error if the VM is unknown,
829                    just call the remove function without prior VM lookup */
830                 _vsc_mgmt_data_vm_take (vm_tmp);
831
832                 /* Try to get a domain libVirt domain pointer and undefine
833                  * the domain from the host if we got one.
834                  */
835                 domain = NULL;
836                 domain = _vm_get_domain_pointer (&dummy_error, vm_tmp);
837                 if (domain) {
838                         virDomainUndefine (domain);
839                 }
840
841                 vsc_error_reset (&dummy_error);
842         }
843
844         goto cleanup_unlock;
845 }
846
847
848
849 void
850 vsc_mgmt_vm_retract (struct VscError *error,
851                      const struct VscMgmtUuid *vm_uuid)
852 {
853         struct VscMgmtVm *vm = NULL;
854         struct VscMgmtHost *host = NULL;
855         char vm_uuid_string[VSC_MGMT__UUID__STRING_SIZE];
856         char uuid_string[VSC_MGMT__UUID__STRING_SIZE];
857         virDomainPtr domain = NULL;
858
859         VSC__ASSERT (error != NULL);
860         VSC__ASSERT (! error->occured);
861         VSC__ASSERT (vm_uuid != NULL);
862
863         _IF_NOT_INITIALIZED (return);
864
865         _vsc_mgmt_lock ();
866
867         /* Check if the VM UUID is known */
868         vm = _vsc_mgmt_data_vm_lookup_by_uuid (vm_uuid);
869         if (! vm) {
870                 vsc_mgmt_uuid_format (vm_uuid, vm_uuid_string);
871                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
872                              "Unknown VM UUID '%s'", vm_uuid_string);
873
874                 goto unlock;
875         }
876
877         /* Check if the VM is powered off */
878         if (vm->info.state != VSC_MGMT__VM_STATE__POWERED_OFF) {
879                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
880                              "VM '%s' not powered off", vm->info.uuid_string);
881
882                 goto unlock;
883         }
884
885         /* Check for an existing checkpoint containing this VM */
886         if (vm->info.checkpoint_uuid_list) {
887                 vsc_mgmt_uuid_format (vm->info.checkpoint_uuid_list, uuid_string);
888                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
889                              "VM '%s' at least part of checkpoint '%s'",
890                              vm->info.uuid_string, uuid_string);
891
892                 goto unlock;
893         }
894
895         /* Check if this VM is suspended */
896         if (! uuid_is_null (vm->info.suspension_uuid.value)) {
897                 vsc_mgmt_uuid_format (&vm->info.suspension_uuid, uuid_string);
898                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
899                              "VM '%s' is part of suspension '%s'.",
900                              vm->info.uuid_string, uuid_string);
901
902                 goto unlock;
903         }
904
905         /* Get host pointer for setting timing information */
906         host = _vsc_mgmt_data_host_lookup_by_ip (&vm->host_ip);
907         if (! host) {
908                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
909                              "Failed to get informatio about host %u.%u.%u.%u",
910                              vm->host_ip.bytes[0], vm->host_ip.bytes[1],
911                              vm->host_ip.bytes[2], vm->host_ip.bytes[3]);
912                 goto unlock;
913         }
914
915         domain = _vm_get_domain_pointer (error, vm);
916         if (error->occured) {
917                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
918                 goto unlock;
919         }
920
921         /* Remove the VM from data storage */
922         _vsc_mgmt_data_vm_take (vm);
923
924         /* Undefine the VM and free the memory from libVirt */
925         virDomainUndefine (domain);
926         virDomainFree (domain);
927
928         if (gettimeofday (&host->info.last_modification, NULL) != 0) {
929                 VSC__ERROR2 (error, VSC__ERROR_CODE__ERRNO,
930                              "Failed to query current time: %s",
931                              strerror (errno));
932         }
933
934         /* Release the IP */
935         _vsc_mgmt_network_release_ipv4 (error, &vm->info.network_uuid, &vm->info.ipv4);
936         if (error->occured) {
937                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
938                 goto unlock;
939         }
940
941         vsc_free (&vm);
942
943 unlock:
944         _vsc_mgmt_unlock ();
945 }
946
947
948
949 void
950 vsc_mgmt_vm_power_on (struct VscError *error,
951                       const struct VscMgmtUuid *vm_uuid)
952 {
953         struct VscError *cleanup_error = NULL;
954
955         struct VscMgmtVm *vm = NULL;
956         struct VscMgmtHost *host = NULL;
957         virDomainPtr domain = NULL;
958         struct timeval time_tmp;
959         char vm_uuid_string[VSC_MGMT__UUID__STRING_SIZE];
960
961         VSC__ASSERT (error != NULL);
962         VSC__ASSERT (! error->occured);
963         VSC__ASSERT (vm_uuid != NULL);
964
965         _IF_NOT_INITIALIZED (return);
966
967         _vsc_mgmt_lock ();
968
969         vm = _vsc_mgmt_data_vm_lookup_by_uuid (vm_uuid);
970         if (! vm) {
971                 vsc_mgmt_uuid_format (vm_uuid, vm_uuid_string);
972                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
973                              "Unknown VM UUID %s", vm_uuid_string);
974                 goto unlock;
975         }
976
977         if (vm->info.state != VSC_MGMT__VM_STATE__POWERED_OFF) {
978                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
979                              "VM not powered off: %s", vm->info.uuid_string);
980                 goto unlock;
981
982         }
983
984         /*
985          * Query the time for the last_state_change value
986          * (query into a temporary stack was as gettimeofday() might fail
987          *  and it would be a bad idea to either write the into the VmInfo
988          *  struct before being sure the action succeeded or first perform
989          *  the action and then cannot update the timing information.)
990          */
991         if (gettimeofday (&time_tmp, NULL) != 0) {
992                 VSC__ERROR2 (error, VSC__ERROR_CODE__ERRNO,
993                              "Failed to query current time: %s",
994                              strerror (errno));
995         }
996
997         /* Get host pointer for setting timing information */
998         host = _vsc_mgmt_data_host_lookup_by_ip (&vm->host_ip);
999         if (! host) {
1000                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
1001                              "Failed to get informatio about host %u.%u.%u.%u",
1002                              vm->host_ip.bytes[0], vm->host_ip.bytes[1],
1003                              vm->host_ip.bytes[2], vm->host_ip.bytes[3]);
1004                 goto unlock;
1005         }
1006
1007         /* Get the domain pointer */
1008         domain = _vm_get_domain_pointer (error, vm);
1009         if (error->occured) {
1010                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1011                 goto unlock;
1012         }
1013
1014         /* Power on the domain */
1015         if (virDomainCreate (domain) != 0) {
1016                 _ERROR_FROM_LIBVIRT (error, virDomainGetConnect (domain));
1017                 goto unlock;
1018         }
1019
1020         vm->info.state = VSC_MGMT__VM_STATE__POWERED_ON;
1021
1022         /* Apply the CPU scheduler parameters */
1023         _apply_vm_sched_parameters (error, domain, &vm->info.resource_config, host);
1024         if (error->occured) {
1025                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1026
1027                 if (virDomainDestroy (domain) != 0) {
1028                         cleanup_error = vsc_error_new ();
1029                         _ERROR_FROM_LIBVIRT (cleanup_error, virDomainGetConnect (domain));
1030
1031                         /* This renders our world view inconsistent */
1032                         cleanup_error->causes_inconsistency = 1;
1033
1034                         vsc_error_append_branch (error, cleanup_error);
1035                 } else {
1036                         vm->info.state = VSC_MGMT__VM_STATE__POWERED_OFF;
1037                 }
1038
1039                 goto unlock;
1040         }
1041
1042         /* Now copy the time into the VmInfo struct as everything went well. */
1043         memcpy (&vm->info.last_state_change, &time_tmp, sizeof (struct timeval));
1044         memcpy (&host->info.last_modification, &time_tmp, sizeof (struct timeval));
1045
1046 unlock:
1047         _vsc_mgmt_unlock ();
1048 }
1049
1050
1051
1052 void
1053 vsc_mgmt_vm_power_off (struct VscError *error,
1054                        const struct VscMgmtUuid *vm_uuid)
1055 {
1056         struct VscMgmtVm *vm = NULL;
1057         struct VscMgmtHost *host = NULL;
1058         virDomainPtr domain = NULL;
1059         struct timeval time_tmp;
1060         char vm_uuid_string[VSC_MGMT__UUID__STRING_SIZE];
1061
1062         VSC__ASSERT (error != NULL);
1063         VSC__ASSERT (! error->occured);
1064         VSC__ASSERT (vm_uuid != NULL);
1065
1066         _IF_NOT_INITIALIZED (return);
1067
1068         _vsc_mgmt_lock ();
1069
1070         vm = _vsc_mgmt_data_vm_lookup_by_uuid (vm_uuid);
1071         if (! vm) {
1072                 vsc_mgmt_uuid_format (vm_uuid, vm_uuid_string);
1073                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1074                              "Unknown VM UUID: %s", vm_uuid_string);
1075                 goto unlock;
1076         }
1077
1078         if (vm->info.state != VSC_MGMT__VM_STATE__POWERED_ON) {
1079                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1080                              "VM not powered on: %s", vm->info.uuid_string);
1081                 goto unlock;
1082         }
1083
1084         /*
1085          * Query the time for the last_state_change value
1086          * (query into a temporary stack was as gettimeofday() might fail
1087          *  and it would be a bad idea to either write the into the VmInfo
1088          *  struct before being sure the action succeeded or first perform
1089          *  the action and then cannot update the timing information.)
1090          */
1091         if (gettimeofday (&time_tmp, NULL) != 0) {
1092                 VSC__ERROR2 (error, VSC__ERROR_CODE__ERRNO,
1093                              "Failed to query current time: %s",
1094                              strerror (errno));
1095         }
1096
1097         /* Get host pointer for setting timing information */
1098         host = _vsc_mgmt_data_host_lookup_by_ip (&vm->host_ip);
1099         if (! host) {
1100                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
1101                              "Failed to get informatio about host %u.%u.%u.%u",
1102                              vm->host_ip.bytes[0], vm->host_ip.bytes[1],
1103                              vm->host_ip.bytes[2], vm->host_ip.bytes[3]);
1104                 goto unlock;
1105         }
1106
1107         /* Get the domain pointer */
1108         domain = _vm_get_domain_pointer (error, vm);
1109         if (error->occured) {
1110                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1111                 goto unlock;
1112         }
1113
1114         /* Power of the domain */
1115         if (virDomainDestroy (domain) != 0) {
1116                 _ERROR_FROM_LIBVIRT (error, virDomainGetConnect (domain));
1117                 goto unlock;
1118         }
1119
1120         vm->info.state = VSC_MGMT__VM_STATE__POWERED_OFF;
1121
1122         /* Now copy the time into the VmInfo struct as everything went well. */
1123         memcpy (&vm->info.last_state_change, &time_tmp, sizeof (struct timeval));
1124         memcpy (&host->info.last_modification, &time_tmp, sizeof (struct timeval));
1125
1126 unlock:
1127         _vsc_mgmt_unlock ();
1128 }
1129
1130
1131
1132 void
1133 vsc_mgmt_vm_reboot (struct VscError *error,
1134                     const struct VscMgmtUuid *vm_uuid)
1135 {
1136         struct VscMgmtVm *vm = NULL;
1137         struct VscMgmtHost *host = NULL;
1138         virDomainPtr domain = NULL;
1139         struct timeval time_tmp;
1140         char vm_uuid_string[VSC_MGMT__UUID__STRING_SIZE];
1141
1142         VSC__ASSERT (error != NULL);
1143         VSC__ASSERT (! error->occured);
1144         VSC__ASSERT (vm_uuid != NULL);
1145
1146         _IF_NOT_INITIALIZED (return);
1147
1148         _vsc_mgmt_lock ();
1149
1150         vm = _vsc_mgmt_data_vm_lookup_by_uuid (vm_uuid);
1151         if (! vm) {
1152                 vsc_mgmt_uuid_format (vm_uuid, vm_uuid_string);
1153                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1154                              "Unknown VM UUID: %s", vm_uuid_string);
1155                 goto unlock;
1156         }
1157
1158         if (vm->info.state != VSC_MGMT__VM_STATE__POWERED_ON) {
1159                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1160                              "VM not powered on: %s", vm->info.uuid_string);
1161                 goto unlock;
1162         }
1163
1164         /*
1165          * Query the time for the last_state_change value
1166          * (query into a temporary stack was as gettimeofday() might fail
1167          *  and it would be a bad idea to either write the into the VmInfo
1168          *  struct before being sure the action succeeded or first perform
1169          *  the action and then cannot update the timing information.)
1170          */
1171         if (gettimeofday (&time_tmp, NULL) != 0) {
1172                 VSC__ERROR2 (error, VSC__ERROR_CODE__ERRNO,
1173                              "Failed to query current time: %s",
1174                              strerror (errno));
1175         }
1176
1177         /* Get host pointer for setting timing information */
1178         host = _vsc_mgmt_data_host_lookup_by_ip (&vm->host_ip);
1179         if (! host) {
1180                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
1181                              "Failed to get informatio about host %u.%u.%u.%u",
1182                              vm->host_ip.bytes[0], vm->host_ip.bytes[1],
1183                              vm->host_ip.bytes[2], vm->host_ip.bytes[3]);
1184                 goto unlock;
1185         }
1186
1187         /* Get the domain pointer */
1188         domain = _vm_get_domain_pointer (error, vm);
1189         if (error->occured) {
1190                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1191                 goto unlock;
1192         }
1193
1194         /* Reboot the domain */
1195         if (virDomainReboot (domain, 0) != 0) {
1196                 _ERROR_FROM_LIBVIRT (error, virDomainGetConnect (domain));
1197         }
1198
1199         /* Now copy the time into the VmInfo struct as everything went well. */
1200         memcpy (&vm->info.last_state_change, &time_tmp, sizeof (struct timeval));
1201         memcpy (&host->info.last_modification, &time_tmp, sizeof (struct timeval));
1202
1203 unlock:
1204         _vsc_mgmt_unlock ();
1205 }
1206
1207
1208
1209 void
1210 vsc_mgmt_vm_migrate (struct VscError *error,
1211                      const struct VscMgmtUuid *vm_uuid,
1212                      const struct VscMgmtIpv4 *host_ipv4)
1213 {
1214         struct VscMgmtVm *vm = NULL;
1215         struct VscMgmtHost *host = NULL;
1216         struct VscMgmtHost *dst_host = NULL;
1217         struct timeval time_tmp;
1218         char vm_uuid_string[VSC_MGMT__UUID__STRING_SIZE];
1219         char dest_host_ip_string[VSC_MGMT__IPV4__STRING_SIZE];
1220         virDomainPtr domain = NULL;
1221         virDomainPtr domain_new = NULL;
1222
1223         VSC__ASSERT (error != NULL);
1224         VSC__ASSERT (! error->occured);
1225         VSC__ASSERT (vm_uuid != NULL);
1226         VSC__ASSERT (host_ipv4 != NULL);
1227
1228         _IF_NOT_INITIALIZED (return);
1229
1230         _vsc_mgmt_lock ();
1231
1232         vm = _vsc_mgmt_data_vm_lookup_by_uuid (vm_uuid);
1233         if (! vm) {
1234                 vsc_mgmt_uuid_format (vm_uuid, vm_uuid_string);
1235                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1236                              "Unkown VM UUID: '%s'", vm_uuid_string);
1237                 goto unlock;
1238         }
1239
1240         if (vm->info.state != VSC_MGMT__VM_STATE__POWERED_ON) {
1241                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1242                              "VM not powered on: %s", vm->info.uuid_string);
1243                 goto unlock;
1244         }
1245
1246         /* Get host pointer for setting timing information */
1247         host = _vsc_mgmt_data_host_lookup_by_ip (&vm->host_ip);
1248         if (! host) {
1249                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
1250                              "Failed to get informatio about host %u.%u.%u.%u",
1251                              vm->host_ip.bytes[0], vm->host_ip.bytes[1],
1252                              vm->host_ip.bytes[2], vm->host_ip.bytes[3]);
1253                 goto unlock;
1254         }
1255
1256         dst_host = _vsc_mgmt_data_host_lookup_by_ip (host_ipv4);
1257         if (! dst_host) {
1258                 vsc_mgmt_ipv4_format (host_ipv4, dest_host_ip_string);
1259                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1260                              "Unkown host: '%s'", dest_host_ip_string);
1261                 goto unlock;
1262         }
1263
1264         /* Check if the dst host is the same as the current host */
1265         if (_vsc_mgmt_ipv4_compare (&vm->host_ip , host_ipv4) == 0) {
1266                 vsc_mgmt_ipv4_format (host_ipv4, dest_host_ip_string);
1267                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1268                              "Refusing to migrate within host '%s'", dest_host_ip_string);
1269                 goto unlock;
1270
1271         }
1272
1273         /*
1274          * Query the time for the last_state_change value
1275          * (query into a temporary stack was as gettimeofday() might fail
1276          *  and it would be a bad idea to either write the into the VmInfo
1277          *  struct before being sure the action succeeded or first perform
1278          *  the action and then cannot update the timing information.)
1279          */
1280         if (gettimeofday (&time_tmp, NULL) != 0) {
1281                 VSC__ERROR2 (error, VSC__ERROR_CODE__ERRNO,
1282                              "Failed to query current time: %s",
1283                              strerror (errno));
1284         }
1285
1286
1287         /* Get the domain pointer */
1288         domain = _vm_get_domain_pointer (error, vm);
1289         if (error->occured) {
1290                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1291                 goto unlock;
1292         }
1293
1294         domain_new = virDomainMigrate (domain,
1295                                        dst_host->connection,
1296                                        0,       /* TODO check if live migration is possible */
1297                                        NULL,    /* don't rename the VM */
1298                                        NULL,    /* no dest host uri */
1299                                        0);      /* no bandwith limit */
1300         if (! domain_new) {
1301                 _ERROR_FROM_LIBVIRT (error, virDomainGetConnect (domain));
1302                 goto unlock;
1303         }
1304
1305         /*
1306          * Ok the migration worked ...
1307          * ... undefine the VM on the old host in case libVirt left it there
1308          */
1309         virDomainUndefine (domain);
1310         virDomainFree (domain);
1311
1312         /* ... and complete the migration in our data backend. */
1313         _vsc_mgmt_data_vm_move (error, vm_uuid, &dst_host->info.ipv4);
1314         if (error->occured) {
1315                 /*
1316                  * Damn it, this is a realy bad situation...
1317                  * ... the migration worked and our data backend screwd up :(
1318                  * There's not much to do but tell our caller about this and
1319                  * kindly tell him, that this causes library inconsitency
1320                  */
1321                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1322                 error->causes_inconsistency = 1;
1323         }
1324
1325         /* Now copy the time into the VmInfo struct as everything went well. */
1326         memcpy (&vm->info.last_state_change, &time_tmp, sizeof (struct timeval));
1327         memcpy (&host->info.last_modification, &time_tmp, sizeof (struct timeval));
1328         memcpy (&dst_host->info.last_modification, &time_tmp, sizeof (struct timeval));
1329         memcpy (&dst_host->info.last_resource_allocation, &time_tmp, sizeof (struct timeval));
1330
1331 unlock:
1332         _vsc_mgmt_unlock ();
1333 }
1334
1335
1336
1337 void
1338 vsc_mgmt_vm_get_uuid (struct VscError *error,
1339                       const struct VscMgmtIpv4 *vm_ipv4,
1340                       struct VscMgmtUuid *vm_uuid)
1341 {
1342         VSC__ASSERT (error != NULL);
1343         VSC__ASSERT (! error->occured);
1344
1345         _IF_NOT_INITIALIZED (return);
1346
1347         _vsc_mgmt_lock ();
1348
1349         _vsc_mgmt_data_vm_get_uuid (vm_ipv4, vm_uuid);
1350
1351         _vsc_mgmt_unlock ();
1352 }
1353
1354
1355
1356 void
1357 vsc_mgmt_vm_get_host (struct VscError *error,
1358                       const struct VscMgmtUuid *vm_uuid,
1359                       struct VscMgmtIpv4 *host_ipv4)
1360 {
1361         VSC__ASSERT (error != NULL);
1362         VSC__ASSERT (! error->occured);
1363         VSC__ASSERT (vm_uuid != NULL);
1364         VSC__ASSERT (host_ipv4 != NULL);
1365
1366         _IF_NOT_INITIALIZED (return);
1367
1368         _vsc_mgmt_lock ();
1369
1370         _vsc_mgmt_data_vm_get_host_ipv4 (error, vm_uuid, host_ipv4);
1371         if (error->occured) {
1372                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1373                 goto unlock;
1374         }
1375
1376 unlock:
1377         _vsc_mgmt_unlock ();
1378 }
1379
1380
1381
1382 void
1383 vsc_mgmt_vm_get_info (struct VscError *error,
1384                       const struct VscMgmtUuid *vm_uuid,
1385                       struct VscMgmtVmInfo *vm_info)
1386 {
1387         struct VscMgmtVm *vm = NULL;
1388         char vm_uuid_string[VSC_MGMT__UUID__STRING_SIZE];
1389
1390         VSC__ASSERT (error != NULL);
1391         VSC__ASSERT (! error->occured);
1392         VSC__ASSERT (vm_uuid != NULL);
1393         VSC__ASSERT (vm_info != NULL);
1394
1395         _IF_NOT_INITIALIZED (return);
1396
1397         _vsc_mgmt_lock ();
1398
1399         vm = _vsc_mgmt_data_vm_lookup_by_uuid (vm_uuid);
1400         if (! vm) {
1401                 vsc_mgmt_uuid_format (vm_uuid, vm_uuid_string);
1402                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1403                              "Unknown VM UUID '%s'", vm_uuid_string);
1404                 goto unlock;
1405         }
1406
1407         _vm_info_copy (error, vm_info, &vm->info);
1408         if (error->occured) {
1409                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1410                 goto unlock;
1411         }
1412
1413 unlock:
1414         _vsc_mgmt_unlock ();
1415 }
1416
1417
1418
1419 void
1420 vsc_mgmt_vm_info_cleanup (struct VscMgmtVmInfo *vm_info)
1421 {
1422         VSC__ASSERT (vm_info != NULL);
1423
1424         if (vm_info->checkpoint_uuid_list)
1425                 vsc_free (&vm_info->checkpoint_uuid_list);
1426
1427         if (vm_info->image_filename)
1428                 vsc_free (&vm_info->image_filename);
1429
1430         if (vm_info->uuid_string)
1431                 vsc_free (&vm_info->uuid_string);
1432
1433         memset (vm_info, '\0', sizeof (struct VscMgmtVmInfo));
1434 }
1435
1436
1437
1438
1439 void
1440 vsc_mgmt_vm_set_resource_config (struct VscError *error,
1441                                  const struct VscMgmtUuid *vm_uuid,
1442                                  const struct VscMgmtVmResourceConfig *new_res)
1443 {
1444         struct VscMgmtVm *vm = NULL;
1445         struct VscMgmtHost *host = NULL;
1446         virDomainPtr domain = NULL;
1447         char vm_uuid_string[VSC_MGMT__UUID__STRING_SIZE];
1448         struct VscError *cleanup_error = NULL;
1449         struct timeval time_tmp;
1450
1451         VSC__ASSERT (error != NULL);
1452         VSC__ASSERT (! error->occured);
1453         VSC__ASSERT (vm_uuid != NULL);
1454         VSC__ASSERT (new_res != NULL);
1455
1456         _IF_NOT_INITIALIZED (return);
1457
1458         _vsc_mgmt_lock ();
1459
1460         vm = _vsc_mgmt_data_vm_lookup_by_uuid (vm_uuid);
1461         if (! vm) {
1462                 vsc_mgmt_uuid_format (vm_uuid, vm_uuid_string);
1463                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1464                              "Unknown VM UUID '%s'", vm_uuid_string);
1465                 goto unlock;
1466         }
1467
1468         host = _vsc_mgmt_data_host_lookup_by_ip (&vm->host_ip);
1469         if (! host) {
1470                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
1471                              "Failed to get informatio about host %u.%u.%u.%u",
1472                              vm->host_ip.bytes[0], vm->host_ip.bytes[1],
1473                              vm->host_ip.bytes[2], vm->host_ip.bytes[3]);
1474                 goto unlock;
1475         }
1476
1477         _vm_resource_config_validate (error, new_res);
1478         if (error->occured) {
1479                 vsc_mgmt_uuid_format (vm_uuid, vm_uuid_string);
1480                 VSC__APPEND_ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1481                                     "Invalid resource config for VM '%s'",
1482                                     vm_uuid_string);
1483                 goto unlock;
1484         }
1485
1486         /*
1487          * Query the time for the last_state_change value
1488          * (query into a temporary stack was as gettimeofday() might fail
1489          *  and it would be a bad idea to either write the into the VmInfo
1490          *  struct before being sure the action succeeded or first perform
1491          *  the action and then cannot update the timing information.)
1492          */
1493         if (gettimeofday (&time_tmp, NULL) != 0) {
1494                 VSC__ERROR2 (error, VSC__ERROR_CODE__ERRNO,
1495                              "Failed to query current time: %s",
1496                              strerror (errno));
1497         }
1498
1499         /* Get the domain pointer */
1500         domain = _vm_get_domain_pointer (error, vm);
1501         if (error->occured) {
1502                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1503                 goto unlock;
1504         }
1505
1506         /* Get the domain pointer */
1507         domain = _vm_get_domain_pointer (error, vm);
1508         if (error->occured) {
1509                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1510                 goto unlock;
1511         }
1512
1513
1514         /* Memory changed? */
1515         if (vm->info.resource_config.memory_size != new_res->memory_size) {
1516                 if (virDomainSetMemory (domain, new_res->memory_size) != 0) {
1517                         _ERROR_FROM_LIBVIRT (error, virDomainGetConnect (domain));
1518                         goto unlock;
1519                 }
1520         }
1521
1522         /* Number of cores changed? */
1523         if (vm->info.resource_config.num_cores != new_res->num_cores) {
1524                 if (virDomainSetVcpus (domain, new_res->num_cores) != 0) {
1525                         _ERROR_FROM_LIBVIRT (error, virDomainGetConnect (domain));
1526
1527                         cleanup_error = vsc_error_new ();
1528                         goto rollback_memory;
1529                 }
1530         }
1531
1532         /* Core clock changed? */
1533         if (vm->info.resource_config.num_cores != new_res->num_cores ||
1534             vm->info.resource_config.core_clock != new_res->core_clock) {
1535                 _apply_vm_sched_parameters (error, domain, new_res, host);
1536                 if (error->occured) {
1537                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1538
1539                         cleanup_error = vsc_error_new ();
1540                         goto rollback_vcpus;
1541                 }
1542         }
1543
1544         /* Take over new values */
1545         memcpy (&vm->info.resource_config, new_res,
1546                 sizeof (struct VscMgmtVmResourceConfig));
1547
1548         /* Now copy the time into the VmInfo struct as everything went well. */
1549         memcpy (&vm->info.last_config_change, &time_tmp, sizeof (struct timeval));
1550
1551
1552 unlock:
1553         _vsc_mgmt_unlock ();
1554
1555         return;
1556
1557 rollback_vcpus:
1558         /* If we changed the vcpu setting earlier, try to roll it back to its
1559          * original value to maintain consitency.
1560          */
1561         if (vm->info.resource_config.num_cores != new_res->num_cores) {
1562                 if (virDomainSetVcpus (domain,
1563                                        vm->info.resource_config.num_cores) != 0) {
1564                         _ERROR_FROM_LIBVIRT (cleanup_error,
1565                                              virDomainGetConnect (domain));
1566                         if (cleanup_error->occured) {
1567                                 vsc_error_append_branch (error, cleanup_error);
1568                                 cleanup_error = vsc_error_new ();
1569                         }
1570                 }
1571         }
1572
1573 rollback_memory:
1574         /* Rollback memory if neccessry
1575          */
1576         if (vm->info.resource_config.memory_size != new_res->memory_size) {
1577                 if (virDomainSetMemory (domain,
1578                                         vm->info.resource_config.memory_size) != 0) {
1579                         _ERROR_FROM_LIBVIRT (cleanup_error,
1580                                              virDomainGetConnect (domain));
1581                         if (cleanup_error->occured) {
1582                                 vsc_error_append_branch (error, cleanup_error);
1583                         }
1584                 }
1585         }
1586
1587         if (cleanup_error != NULL && ! cleanup_error->occured) {
1588                 vsc_error_free (&cleanup_error);
1589         }
1590
1591         goto unlock;
1592 }