[lib] Use stack-based virSchedParameter to avoid a free() problem.
[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         virSchedParameter sched_params[3];
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         memset (sched_params, 0, sizeof (virSchedParameter) * 3);
394
395         switch (host->info.type) {
396         case VSC_MGMT__HOST_TYPE__VIRTUAL:
397                 /* Do nothing */
398                 break;
399
400         case VSC_MGMT__HOST_TYPE__XEN:
401                 /*
402                  * From http://wiki.xensource.com/xenwiki/CreditScheduler
403                  *
404                  * "The cap is expressed in percentage of one physical CPU:
405                  *  100 is 1 physical CPU, 50 is half a CPU, 400 is 4 CPUs,
406                  *  etc..."
407                  */
408                 sched_name = virDomainGetSchedulerType (domain, NULL);
409                 if (! sched_name) {
410                         _ERROR_FROM_LIBVIRT (error, virDomainGetConnect (domain));
411                         goto cleanup;
412                 }
413
414                 if (strcmp (sched_name, "credit") != 0) {
415                         VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_CALL,
416                                      "Unexpected scheduler type '%s', expecting 'credit'",
417                                      sched_name);
418                         goto cleanup;
419                 }
420
421                 totalRequestedMhz = resource_config->num_cores * resource_config->core_clock;
422                 num_sched_params = 2;
423
424                 snprintf (sched_params[0].field, VIR_DOMAIN_SCHED_FIELD_LENGTH, "%s", "weight");
425                 sched_params[0].type = VIR_DOMAIN_SCHED_FIELD_UINT;
426                 sched_params[0].value.ui = 256; /* enforce the default */
427
428                 snprintf (sched_params[1].field, VIR_DOMAIN_SCHED_FIELD_LENGTH, "%s", "cap");
429                 sched_params[1].type = VIR_DOMAIN_SCHED_FIELD_UINT;
430                 sched_params[1].value.ui = (totalRequestedMhz * 100) / host->info.core_clock;
431
432                 break;
433
434         case VSC_MGMT__HOST_TYPE__ESX:
435                 /*
436                  * Like Xen, ESX handles CPU allocation per virtual machine, not per
437                  * single virtual CPU.
438                  */
439                 sched_name = virDomainGetSchedulerType (domain, NULL);
440                 if (! sched_name) {
441                         _ERROR_FROM_LIBVIRT (error, virDomainGetConnect (domain));
442                         goto cleanup;
443                 }
444
445                 if (strcmp (sched_name, "allocation") != 0) {
446                         VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_CALL,
447                                      "Unexpected scheduler type '%s', expecting 'allocation'",
448                                      sched_name);
449                         goto cleanup;
450                 }
451
452                 totalRequestedMhz = resource_config->num_cores * resource_config->core_clock;
453                 num_sched_params = 3;
454
455                 snprintf (sched_params[0].field, VIR_DOMAIN_SCHED_FIELD_LENGTH, "%s", "reservation");
456                 sched_params[0].type = VIR_DOMAIN_SCHED_FIELD_LLONG;
457
458                 if (_vsc_mgmt_config.esx.set_cpu_reservation) {
459                         sched_params[0].value.ui = totalRequestedMhz;
460                 } else {
461                         sched_params[0].value.ui = 0; /* no reservation */
462                 }
463
464                 snprintf (sched_params[1].field, VIR_DOMAIN_SCHED_FIELD_LENGTH, "%s", "limit");
465                 sched_params[1].type = VIR_DOMAIN_SCHED_FIELD_LLONG;
466                 sched_params[1].value.ui = totalRequestedMhz;
467
468                 snprintf (sched_params[2].field, VIR_DOMAIN_SCHED_FIELD_LENGTH, "%s", "shares");
469                 sched_params[2].type = VIR_DOMAIN_SCHED_FIELD_INT;
470                 sched_params[2].value.ui = -2; /* -2 = normal, enforce the default */
471
472                 break;
473
474         default:
475                 vsc_mgmt_ipv4_format (&host->info.ipv4, host_ipv4_string);
476                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
477                              "Host %s has unknown type", host_ipv4_string);
478                 goto cleanup;
479         }
480
481         /* Apply scheduler parameters */
482         if (num_sched_params > 0 &&
483             virDomainSetSchedulerParameters (domain, sched_params, num_sched_params) != 0) {
484                 _ERROR_FROM_LIBVIRT (error, virDomainGetConnect (domain));
485                 goto cleanup;
486         }
487
488 cleanup:
489         vsc_free (&sched_name);
490 }
491
492
493
494 static virDomainPtr
495 _vm_get_domain_pointer (struct VscError *error, const struct VscMgmtVm *vm)
496 {
497         virConnectPtr host_conn = NULL;
498         virDomainPtr domain = NULL;
499
500         host_conn = _vsc_mgmt_data_host_get_connection (&vm->host_ip);
501         if (! host_conn) {
502                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
503                              "Failed to get libVirt connection pointer for host"
504                              "%u.%u.%u.%u", vm->host_ip.bytes[0],
505                              vm->host_ip.bytes[1], vm->host_ip.bytes[2],
506                              vm->host_ip.bytes[3]);
507                 return NULL;
508         }
509
510         domain = virDomainLookupByUUID (host_conn, vm->info.uuid.value);
511         if (! domain) {
512                 _ERROR_FROM_LIBVIRT (error, host_conn);
513                 return NULL;
514         }
515
516         return domain;
517 }
518
519
520 /*******************************************************************************
521  *                              public API functions                           *
522  *******************************************************************************/
523
524
525
526 void
527 vsc_mgmt_vm_deploy (struct VscError *error,
528                     const struct VscMgmtIpv4 *host_ipv4_list,
529                     const struct VscMgmtUuid *vm_uuid_list,
530                     const struct VscMgmtVmResourceConfig *vm_resource_config_list,
531                     const struct VscMgmtUuid *network_uuid)
532 {
533         struct VscError *cleanup_error = NULL;
534         struct VscError dummy_error;
535
536         struct timeval time_stamp;
537
538         struct VscMgmtIpv4 *ip_loop = NULL;
539         enum VscMgmtHostType host_type = VSC_MGMT__HOST_TYPE__UNDEFINED;
540         struct VscMgmtHost *host_tmp = NULL;
541
542         struct VscMgmtUuid *vm_uuid_loop = NULL;
543         char vm_image_filename[IMAGE_FILENAME_LENGTH];
544         char vm_uuid_string[VSC_MGMT__UUID__STRING_SIZE];
545
546         struct VscMgmtVmResourceConfig *vm_res_loop = NULL;
547
548         /* List of VMs to be deployed */
549         struct VscMgmtVm *vm_list = NULL;
550         struct VscMgmtVm *vm_list_last = NULL;
551         struct VscMgmtVm *vm_tmp = NULL;
552
553         char *domain_xml = NULL;
554         virDomainPtr domain = NULL;
555
556         VSC__ASSERT (error != NULL);
557         VSC__ASSERT (! error->occured);
558         VSC__ASSERT (host_ipv4_list != NULL);
559         VSC__ASSERT (vm_uuid_list != NULL);
560         VSC__ASSERT (vm_resource_config_list != NULL);
561         VSC__ASSERT (network_uuid != NULL);
562
563         _IF_NOT_INITIALIZED (return);
564
565         vsc_error_init (&dummy_error);
566
567         /* Now we need a LOCK */
568         _vsc_mgmt_lock ();
569
570         /*
571          * Valid input parameters:
572          * - Check if all hosts are known and of the same type.
573          * - Check if there as many UUIDs as hosts
574          * - Check if all images are there (if deploying on real hardware)
575          */
576         vm_uuid_loop = (struct VscMgmtUuid *) vm_uuid_list;
577         vm_res_loop = (struct VscMgmtVmResourceConfig *) vm_resource_config_list;
578         for (ip_loop = (struct VscMgmtIpv4 *) host_ipv4_list; ip_loop; ip_loop = ip_loop->next) {
579                 /* Check if the hosts is known to the library */
580                 host_tmp = NULL;
581                 host_tmp = _vsc_mgmt_data_host_lookup_by_ip (ip_loop);
582                 if (! host_tmp) {
583                         VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
584                                      "Unkonwn host IP in host list: %u.%u.%u.%u",
585                                      ip_loop->bytes[0], ip_loop->bytes[1],
586                                      ip_loop->bytes[2], ip_loop->bytes[3]);
587                         goto cleanup_unlock;
588                 }
589
590                 /* Check if the type is consistant to the type of all seen hosts */
591                 if (host_type != VSC_MGMT__HOST_TYPE__UNDEFINED) {
592                         if (host_tmp->info.type != host_type) {
593                                 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
594                                              "Only one host type allowed per call.");
595                                 goto cleanup_unlock;
596                         }
597                 } else {
598                         host_type = host_tmp->info.type;
599                 }
600
601
602                 /*
603                  * Check if there are enough UUIDs for all VMs to be deployed
604                  */
605                 if (vm_uuid_loop == NULL) {
606                         VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
607                                      "Fewer UUIDs than hosts given.");
608                         goto cleanup_unlock;
609                 }
610
611                 /*
612                  * Check if this is an unknown UUID
613                  */
614                 if (_vsc_mgmt_data_vm_lookup_by_uuid (vm_uuid_loop)) {
615                         vsc_mgmt_uuid_format (vm_uuid_loop, vm_uuid_string);
616                         VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
617                                      "Can't use existing VM UUID %s.",
618                                      vm_uuid_string);
619                         goto cleanup_unlock;
620                 }
621
622                 /*
623                  * Check if there are enough resource configs for all VMs to be deployed.
624                  */
625                 if (vm_res_loop == NULL) {
626                         VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
627                                      "Fewer resource configs than hosts given.");
628                         goto cleanup_unlock;
629                 }
630
631                 _vm_resource_config_validate (error, vm_res_loop);
632                 if (error->occured) {
633                         vsc_mgmt_uuid_format (vm_uuid_loop, vm_uuid_string);
634                         VSC__APPEND_ERROR2 (error, VSC__ERROR_CODE__TRACE,
635                                             "Invalid resource config for VM '%s'",
636                                             vm_uuid_string);
637                         goto cleanup_unlock;
638                 }
639
640                 /*
641                  * If this VM should be deployed on real hardware, check if the image file
642                  * if already there.
643                  */
644                 _vsc_mgmt_image_gen_path (vm_uuid_loop, vm_image_filename);
645
646                 if (host_type != VSC_MGMT__HOST_TYPE__VIRTUAL &&
647                     access (vm_image_filename, F_OK) != 0) {
648                         vsc_mgmt_uuid_format (vm_uuid_loop, vm_uuid_string);
649                         VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_CALL,
650                                      "Image file for VM '%s' not found.",
651                                      vm_uuid_string);
652                         goto cleanup_unlock;
653                 }
654
655                 vm_uuid_loop = vm_uuid_loop->next;
656                 vm_res_loop = vm_res_loop->next;
657         }
658
659
660         /*
661          * Check if there are no more UUIDs given than hosts to deploy VMs on.
662          */
663         if (vm_uuid_loop != NULL) {
664                 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
665                              "More UUIDs than hosts given.");
666                 goto cleanup_unlock;
667         }
668
669         /*
670          * Check if there are no more resource configs given than hosts to deploy VMs on.
671          */
672         if (vm_res_loop != NULL) {
673                 VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
674                              "More resource configs than hosts given.");
675                 goto cleanup_unlock;
676         }
677
678
679         /*
680          * Query current time stamp to set it to the VmInfo structs later on
681          */
682         if (gettimeofday (&time_stamp, NULL) != 0) {
683                 VSC__ERROR2 (error, VSC__ERROR_CODE__ERRNO,
684                              "Error while getting deployment time: %s",
685                              strerror (errno));
686                 goto cleanup_unlock;
687         }
688
689
690         /*
691          * Set up a VM struct for each VM which will be put on this host.
692          */
693         vm_uuid_loop = (struct VscMgmtUuid *) vm_uuid_list;
694         vm_res_loop = (struct VscMgmtVmResourceConfig *) vm_resource_config_list;
695         for (ip_loop = (struct VscMgmtIpv4 *) host_ipv4_list; ip_loop; ip_loop = ip_loop->next) {
696                 /* Set up the vm struct */
697                 vm_tmp = _vm_new (error, vm_uuid_loop, network_uuid, ip_loop, vm_res_loop);
698                 if (error->occured) {
699                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
700                         goto cleanup_unlock;
701                 }
702
703                 /* Append this VM to the list of VMs */
704                 if (vm_list != NULL) {
705                         vm_list_last->next = vm_tmp;
706                 } else {
707                         vm_list = vm_tmp;
708                 }
709
710                 vm_list_last = vm_tmp;
711
712                 /*
713                  * Get the next UUID for the next VM.
714                  */
715                 vm_uuid_loop = vm_uuid_loop->next;
716                 vm_res_loop = vm_res_loop->next;
717         }
718
719
720
721         /* Write a new DHCP config file */
722         _vsc_mgmt_network_synchronize (error);
723         if (error->occured) {
724                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
725                 goto cleanup_unlock;
726         }
727
728
729
730         /*
731          * Ok, now we've put everything together to define the VMs to libVirt
732          * and put the VMs into the data storage.
733          */
734         for (vm_tmp = vm_list; vm_tmp; vm_tmp = vm_tmp->next) {
735                 host_tmp = NULL;
736                 host_tmp = _vsc_mgmt_data_host_lookup_by_ip (&vm_tmp->host_ip);
737                 if (! host_tmp) {
738                         VSC__ERROR0 (error, VSC__ERROR_CODE__INTERNAL_ERROR);
739                         goto rollback_cleanup_unlock;
740                 }
741
742                 domain_xml = NULL;
743                 domain_xml = _gen_vm_xml_string (error, vm_tmp, host_type);
744                 if (error->occured) {
745                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
746                         goto rollback_cleanup_unlock;
747                 }
748
749                 /* Define the VM on the host */
750                 domain = NULL;
751                 domain = virDomainDefineXML (host_tmp->connection, domain_xml);
752                 if (! domain) {
753                         _ERROR_FROM_LIBVIRT (error, host_tmp->connection);
754                         goto rollback_cleanup_unlock;
755                 }
756
757                 /* Set VmInfo and HostInfo time stamps */
758                 memcpy (&vm_tmp->info.deployed_at, &time_stamp, sizeof (struct timeval));
759                 memcpy (&vm_tmp->info.last_state_change, &time_stamp, sizeof (struct timeval));
760                 memcpy (&vm_tmp->info.last_config_change, &time_stamp, sizeof (struct timeval));
761                 memcpy (&host_tmp->info.last_modification, &time_stamp, sizeof (struct timeval));
762                 memcpy (&host_tmp->info.last_resource_allocation, &time_stamp, sizeof (struct timeval));
763
764                 /* Don't leak domain XML string representation */
765                 vsc_free (&domain_xml);
766
767
768                 /* Add it to the data storage */
769                 _vsc_mgmt_data_vm_add (error, vm_tmp);
770                 if (error->occured) {
771                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
772                         goto rollback_cleanup_unlock;
773                 }
774         }
775
776
777         /* Release the LOCK */
778         _vsc_mgmt_unlock ();
779
780         return;
781
782
783 cleanup_unlock:
784         cleanup_error = vsc_error_new ();
785
786         /* Release all the IPs and free the VM structs */
787         vm_tmp = vm_list;
788         while (vm_tmp) {
789                 /* Release the IP to the network backend */
790                 _vsc_mgmt_network_release_ipv4 (cleanup_error, network_uuid,
791                                                 &vm_tmp->info.ipv4);
792                 if (cleanup_error->occured) {
793                         VSC__APPEND_ERROR0 (cleanup_error, VSC__ERROR_CODE__TRACE);
794                         vsc_error_append_branch (error, cleanup_error);
795
796                         cleanup_error = vsc_error_new ();
797                 }
798
799                 /* Forget about the VM (use 'vm_list_last' as tmp pointer) */
800                 vm_list_last = vm_tmp;
801                 vm_tmp = vm_tmp->next;
802                 _vm_free (&vm_list_last);
803         }
804
805         /* Some things can be cleanup without holding the lock any longer */
806         _vsc_mgmt_unlock ();
807
808         if (! cleanup_error->occured) {
809                 vsc_error_free (&cleanup_error);
810         }
811
812         return;
813
814
815 rollback_cleanup_unlock:
816         for (vm_tmp = vm_list; vm_tmp; vm_tmp = vm_tmp->next) {
817                 /* As this does not return an error if the VM is unknown,
818                    just call the remove function without prior VM lookup */
819                 _vsc_mgmt_data_vm_take (vm_tmp);
820
821                 /* Try to get a domain libVirt domain pointer and undefine
822                  * the domain from the host if we got one.
823                  */
824                 domain = NULL;
825                 domain = _vm_get_domain_pointer (&dummy_error, vm_tmp);
826                 if (domain) {
827                         virDomainUndefine (domain);
828                 }
829
830                 vsc_error_reset (&dummy_error);
831         }
832
833         goto cleanup_unlock;
834 }
835
836
837
838 void
839 vsc_mgmt_vm_retract (struct VscError *error,
840                      const struct VscMgmtUuid *vm_uuid)
841 {
842         struct VscMgmtVm *vm = NULL;
843         struct VscMgmtHost *host = NULL;
844         char vm_uuid_string[VSC_MGMT__UUID__STRING_SIZE];
845         char uuid_string[VSC_MGMT__UUID__STRING_SIZE];
846         virDomainPtr domain = NULL;
847
848         VSC__ASSERT (error != NULL);
849         VSC__ASSERT (! error->occured);
850         VSC__ASSERT (vm_uuid != NULL);
851
852         _IF_NOT_INITIALIZED (return);
853
854         _vsc_mgmt_lock ();
855
856         /* Check if the VM UUID is known */
857         vm = _vsc_mgmt_data_vm_lookup_by_uuid (vm_uuid);
858         if (! vm) {
859                 vsc_mgmt_uuid_format (vm_uuid, vm_uuid_string);
860                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
861                              "Unknown VM UUID '%s'", vm_uuid_string);
862
863                 goto unlock;
864         }
865
866         /* Check if the VM is powered off */
867         if (vm->info.state != VSC_MGMT__VM_STATE__POWERED_OFF) {
868                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
869                              "VM '%s' not powered off", vm->info.uuid_string);
870
871                 goto unlock;
872         }
873
874         /* Check for an existing checkpoint containing this VM */
875         if (vm->info.checkpoint_uuid_list) {
876                 vsc_mgmt_uuid_format (vm->info.checkpoint_uuid_list, uuid_string);
877                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
878                              "VM '%s' at least part of checkpoint '%s'",
879                              vm->info.uuid_string, uuid_string);
880
881                 goto unlock;
882         }
883
884         /* Check if this VM is suspended */
885         if (! uuid_is_null (vm->info.suspension_uuid.value)) {
886                 vsc_mgmt_uuid_format (&vm->info.suspension_uuid, uuid_string);
887                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
888                              "VM '%s' is part of suspension '%s'.",
889                              vm->info.uuid_string, uuid_string);
890
891                 goto unlock;
892         }
893
894         /* Get host pointer for setting timing information */
895         host = _vsc_mgmt_data_host_lookup_by_ip (&vm->host_ip);
896         if (! host) {
897                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
898                              "Failed to get informatio about host %u.%u.%u.%u",
899                              vm->host_ip.bytes[0], vm->host_ip.bytes[1],
900                              vm->host_ip.bytes[2], vm->host_ip.bytes[3]);
901                 goto unlock;
902         }
903
904         domain = _vm_get_domain_pointer (error, vm);
905         if (error->occured) {
906                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
907                 goto unlock;
908         }
909
910         /* Remove the VM from data storage */
911         _vsc_mgmt_data_vm_take (vm);
912
913         /* Undefine the VM and free the memory from libVirt */
914         virDomainUndefine (domain);
915         virDomainFree (domain);
916
917         if (gettimeofday (&host->info.last_modification, NULL) != 0) {
918                 VSC__ERROR2 (error, VSC__ERROR_CODE__ERRNO,
919                              "Failed to query current time: %s",
920                              strerror (errno));
921         }
922
923         /* Release the IP */
924         _vsc_mgmt_network_release_ipv4 (error, &vm->info.network_uuid, &vm->info.ipv4);
925         if (error->occured) {
926                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
927                 goto unlock;
928         }
929
930         vsc_free (&vm);
931
932 unlock:
933         _vsc_mgmt_unlock ();
934 }
935
936
937
938 void
939 vsc_mgmt_vm_power_on (struct VscError *error,
940                       const struct VscMgmtUuid *vm_uuid)
941 {
942         struct VscError *cleanup_error = NULL;
943
944         struct VscMgmtVm *vm = NULL;
945         struct VscMgmtHost *host = NULL;
946         virDomainPtr domain = NULL;
947         struct timeval time_tmp;
948         char vm_uuid_string[VSC_MGMT__UUID__STRING_SIZE];
949
950         VSC__ASSERT (error != NULL);
951         VSC__ASSERT (! error->occured);
952         VSC__ASSERT (vm_uuid != NULL);
953
954         _IF_NOT_INITIALIZED (return);
955
956         _vsc_mgmt_lock ();
957
958         vm = _vsc_mgmt_data_vm_lookup_by_uuid (vm_uuid);
959         if (! vm) {
960                 vsc_mgmt_uuid_format (vm_uuid, vm_uuid_string);
961                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
962                              "Unknown VM UUID %s", vm_uuid_string);
963                 goto unlock;
964         }
965
966         if (vm->info.state != VSC_MGMT__VM_STATE__POWERED_OFF) {
967                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
968                              "VM not powered off: %s", vm->info.uuid_string);
969                 goto unlock;
970
971         }
972
973         /*
974          * Query the time for the last_state_change value
975          * (query into a temporary stack was as gettimeofday() might fail
976          *  and it would be a bad idea to either write the into the VmInfo
977          *  struct before being sure the action succeeded or first perform
978          *  the action and then cannot update the timing information.)
979          */
980         if (gettimeofday (&time_tmp, NULL) != 0) {
981                 VSC__ERROR2 (error, VSC__ERROR_CODE__ERRNO,
982                              "Failed to query current time: %s",
983                              strerror (errno));
984         }
985
986         /* Get host pointer for setting timing information */
987         host = _vsc_mgmt_data_host_lookup_by_ip (&vm->host_ip);
988         if (! host) {
989                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
990                              "Failed to get informatio about host %u.%u.%u.%u",
991                              vm->host_ip.bytes[0], vm->host_ip.bytes[1],
992                              vm->host_ip.bytes[2], vm->host_ip.bytes[3]);
993                 goto unlock;
994         }
995
996         /* Get the domain pointer */
997         domain = _vm_get_domain_pointer (error, vm);
998         if (error->occured) {
999                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1000                 goto unlock;
1001         }
1002
1003         /* Power on the domain */
1004         if (virDomainCreate (domain) != 0) {
1005                 _ERROR_FROM_LIBVIRT (error, virDomainGetConnect (domain));
1006                 goto unlock;
1007         }
1008
1009         vm->info.state = VSC_MGMT__VM_STATE__POWERED_ON;
1010
1011         /* Apply the CPU scheduler parameters */
1012         _apply_vm_sched_parameters (error, domain, &vm->info.resource_config, host);
1013         if (error->occured) {
1014                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1015
1016                 if (virDomainDestroy (domain) != 0) {
1017                         cleanup_error = vsc_error_new ();
1018                         _ERROR_FROM_LIBVIRT (cleanup_error, virDomainGetConnect (domain));
1019
1020                         /* This renders our world view inconsistent */
1021                         cleanup_error->causes_inconsistency = 1;
1022
1023                         vsc_error_append_branch (error, cleanup_error);
1024                 } else {
1025                         vm->info.state = VSC_MGMT__VM_STATE__POWERED_OFF;
1026                 }
1027
1028                 goto unlock;
1029         }
1030
1031         /* Now copy the time into the VmInfo struct as everything went well. */
1032         memcpy (&vm->info.last_state_change, &time_tmp, sizeof (struct timeval));
1033         memcpy (&host->info.last_modification, &time_tmp, sizeof (struct timeval));
1034
1035 unlock:
1036         _vsc_mgmt_unlock ();
1037 }
1038
1039
1040
1041 void
1042 vsc_mgmt_vm_power_off (struct VscError *error,
1043                        const struct VscMgmtUuid *vm_uuid)
1044 {
1045         struct VscMgmtVm *vm = NULL;
1046         struct VscMgmtHost *host = NULL;
1047         virDomainPtr domain = NULL;
1048         struct timeval time_tmp;
1049         char vm_uuid_string[VSC_MGMT__UUID__STRING_SIZE];
1050
1051         VSC__ASSERT (error != NULL);
1052         VSC__ASSERT (! error->occured);
1053         VSC__ASSERT (vm_uuid != NULL);
1054
1055         _IF_NOT_INITIALIZED (return);
1056
1057         _vsc_mgmt_lock ();
1058
1059         vm = _vsc_mgmt_data_vm_lookup_by_uuid (vm_uuid);
1060         if (! vm) {
1061                 vsc_mgmt_uuid_format (vm_uuid, vm_uuid_string);
1062                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1063                              "Unknown VM UUID: %s", vm_uuid_string);
1064                 goto unlock;
1065         }
1066
1067         if (vm->info.state != VSC_MGMT__VM_STATE__POWERED_ON) {
1068                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1069                              "VM not powered on: %s", vm->info.uuid_string);
1070                 goto unlock;
1071         }
1072
1073         /*
1074          * Query the time for the last_state_change value
1075          * (query into a temporary stack was as gettimeofday() might fail
1076          *  and it would be a bad idea to either write the into the VmInfo
1077          *  struct before being sure the action succeeded or first perform
1078          *  the action and then cannot update the timing information.)
1079          */
1080         if (gettimeofday (&time_tmp, NULL) != 0) {
1081                 VSC__ERROR2 (error, VSC__ERROR_CODE__ERRNO,
1082                              "Failed to query current time: %s",
1083                              strerror (errno));
1084         }
1085
1086         /* Get host pointer for setting timing information */
1087         host = _vsc_mgmt_data_host_lookup_by_ip (&vm->host_ip);
1088         if (! host) {
1089                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
1090                              "Failed to get informatio about host %u.%u.%u.%u",
1091                              vm->host_ip.bytes[0], vm->host_ip.bytes[1],
1092                              vm->host_ip.bytes[2], vm->host_ip.bytes[3]);
1093                 goto unlock;
1094         }
1095
1096         /* Get the domain pointer */
1097         domain = _vm_get_domain_pointer (error, vm);
1098         if (error->occured) {
1099                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1100                 goto unlock;
1101         }
1102
1103         /* Power of the domain */
1104         if (virDomainDestroy (domain) != 0) {
1105                 _ERROR_FROM_LIBVIRT (error, virDomainGetConnect (domain));
1106                 goto unlock;
1107         }
1108
1109         vm->info.state = VSC_MGMT__VM_STATE__POWERED_OFF;
1110
1111         /* Now copy the time into the VmInfo struct as everything went well. */
1112         memcpy (&vm->info.last_state_change, &time_tmp, sizeof (struct timeval));
1113         memcpy (&host->info.last_modification, &time_tmp, sizeof (struct timeval));
1114
1115 unlock:
1116         _vsc_mgmt_unlock ();
1117 }
1118
1119
1120
1121 void
1122 vsc_mgmt_vm_reboot (struct VscError *error,
1123                     const struct VscMgmtUuid *vm_uuid)
1124 {
1125         struct VscMgmtVm *vm = NULL;
1126         struct VscMgmtHost *host = NULL;
1127         virDomainPtr domain = NULL;
1128         struct timeval time_tmp;
1129         char vm_uuid_string[VSC_MGMT__UUID__STRING_SIZE];
1130
1131         VSC__ASSERT (error != NULL);
1132         VSC__ASSERT (! error->occured);
1133         VSC__ASSERT (vm_uuid != NULL);
1134
1135         _IF_NOT_INITIALIZED (return);
1136
1137         _vsc_mgmt_lock ();
1138
1139         vm = _vsc_mgmt_data_vm_lookup_by_uuid (vm_uuid);
1140         if (! vm) {
1141                 vsc_mgmt_uuid_format (vm_uuid, vm_uuid_string);
1142                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1143                              "Unknown VM UUID: %s", vm_uuid_string);
1144                 goto unlock;
1145         }
1146
1147         if (vm->info.state != VSC_MGMT__VM_STATE__POWERED_ON) {
1148                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1149                              "VM not powered on: %s", vm->info.uuid_string);
1150                 goto unlock;
1151         }
1152
1153         /*
1154          * Query the time for the last_state_change value
1155          * (query into a temporary stack was as gettimeofday() might fail
1156          *  and it would be a bad idea to either write the into the VmInfo
1157          *  struct before being sure the action succeeded or first perform
1158          *  the action and then cannot update the timing information.)
1159          */
1160         if (gettimeofday (&time_tmp, NULL) != 0) {
1161                 VSC__ERROR2 (error, VSC__ERROR_CODE__ERRNO,
1162                              "Failed to query current time: %s",
1163                              strerror (errno));
1164         }
1165
1166         /* Get host pointer for setting timing information */
1167         host = _vsc_mgmt_data_host_lookup_by_ip (&vm->host_ip);
1168         if (! host) {
1169                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
1170                              "Failed to get informatio about host %u.%u.%u.%u",
1171                              vm->host_ip.bytes[0], vm->host_ip.bytes[1],
1172                              vm->host_ip.bytes[2], vm->host_ip.bytes[3]);
1173                 goto unlock;
1174         }
1175
1176         /* Get the domain pointer */
1177         domain = _vm_get_domain_pointer (error, vm);
1178         if (error->occured) {
1179                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1180                 goto unlock;
1181         }
1182
1183         /* Reboot the domain */
1184         if (virDomainReboot (domain, 0) != 0) {
1185                 _ERROR_FROM_LIBVIRT (error, virDomainGetConnect (domain));
1186         }
1187
1188         /* Now copy the time into the VmInfo struct as everything went well. */
1189         memcpy (&vm->info.last_state_change, &time_tmp, sizeof (struct timeval));
1190         memcpy (&host->info.last_modification, &time_tmp, sizeof (struct timeval));
1191
1192 unlock:
1193         _vsc_mgmt_unlock ();
1194 }
1195
1196
1197
1198 void
1199 vsc_mgmt_vm_migrate (struct VscError *error,
1200                      const struct VscMgmtUuid *vm_uuid,
1201                      const struct VscMgmtIpv4 *host_ipv4)
1202 {
1203         struct VscMgmtVm *vm = NULL;
1204         struct VscMgmtHost *host = NULL;
1205         struct VscMgmtHost *dst_host = NULL;
1206         struct timeval time_tmp;
1207         char vm_uuid_string[VSC_MGMT__UUID__STRING_SIZE];
1208         char dest_host_ip_string[VSC_MGMT__IPV4__STRING_SIZE];
1209         virDomainPtr domain = NULL;
1210         virDomainPtr domain_new = NULL;
1211
1212         VSC__ASSERT (error != NULL);
1213         VSC__ASSERT (! error->occured);
1214         VSC__ASSERT (vm_uuid != NULL);
1215         VSC__ASSERT (host_ipv4 != NULL);
1216
1217         _IF_NOT_INITIALIZED (return);
1218
1219         _vsc_mgmt_lock ();
1220
1221         vm = _vsc_mgmt_data_vm_lookup_by_uuid (vm_uuid);
1222         if (! vm) {
1223                 vsc_mgmt_uuid_format (vm_uuid, vm_uuid_string);
1224                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1225                              "Unkown VM UUID: '%s'", vm_uuid_string);
1226                 goto unlock;
1227         }
1228
1229         if (vm->info.state != VSC_MGMT__VM_STATE__POWERED_ON) {
1230                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1231                              "VM not powered on: %s", vm->info.uuid_string);
1232                 goto unlock;
1233         }
1234
1235         /* Get host pointer for setting timing information */
1236         host = _vsc_mgmt_data_host_lookup_by_ip (&vm->host_ip);
1237         if (! host) {
1238                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
1239                              "Failed to get informatio about host %u.%u.%u.%u",
1240                              vm->host_ip.bytes[0], vm->host_ip.bytes[1],
1241                              vm->host_ip.bytes[2], vm->host_ip.bytes[3]);
1242                 goto unlock;
1243         }
1244
1245         dst_host = _vsc_mgmt_data_host_lookup_by_ip (host_ipv4);
1246         if (! dst_host) {
1247                 vsc_mgmt_ipv4_format (host_ipv4, dest_host_ip_string);
1248                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1249                              "Unkown host: '%s'", dest_host_ip_string);
1250                 goto unlock;
1251         }
1252
1253         /* Check if the dst host is the same as the current host */
1254         if (_vsc_mgmt_ipv4_compare (&vm->host_ip , host_ipv4) == 0) {
1255                 vsc_mgmt_ipv4_format (host_ipv4, dest_host_ip_string);
1256                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1257                              "Refusing to migrate within host '%s'", dest_host_ip_string);
1258                 goto unlock;
1259
1260         }
1261
1262         /*
1263          * Query the time for the last_state_change value
1264          * (query into a temporary stack was as gettimeofday() might fail
1265          *  and it would be a bad idea to either write the into the VmInfo
1266          *  struct before being sure the action succeeded or first perform
1267          *  the action and then cannot update the timing information.)
1268          */
1269         if (gettimeofday (&time_tmp, NULL) != 0) {
1270                 VSC__ERROR2 (error, VSC__ERROR_CODE__ERRNO,
1271                              "Failed to query current time: %s",
1272                              strerror (errno));
1273         }
1274
1275
1276         /* Get the domain pointer */
1277         domain = _vm_get_domain_pointer (error, vm);
1278         if (error->occured) {
1279                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1280                 goto unlock;
1281         }
1282
1283         domain_new = virDomainMigrate (domain,
1284                                        dst_host->connection,
1285                                        0,       /* TODO check if live migration is possible */
1286                                        NULL,    /* don't rename the VM */
1287                                        NULL,    /* no dest host uri */
1288                                        0);      /* no bandwith limit */
1289         if (! domain_new) {
1290                 _ERROR_FROM_LIBVIRT (error, virDomainGetConnect (domain));
1291                 goto unlock;
1292         }
1293
1294         /*
1295          * Ok the migration worked ...
1296          * ... undefine the VM on the old host in case libVirt left it there
1297          */
1298         virDomainUndefine (domain);
1299         virDomainFree (domain);
1300
1301         /* ... and complete the migration in our data backend. */
1302         _vsc_mgmt_data_vm_move (error, vm_uuid, &dst_host->info.ipv4);
1303         if (error->occured) {
1304                 /*
1305                  * Damn it, this is a realy bad situation...
1306                  * ... the migration worked and our data backend screwd up :(
1307                  * There's not much to do but tell our caller about this and
1308                  * kindly tell him, that this causes library inconsitency
1309                  */
1310                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1311                 error->causes_inconsistency = 1;
1312         }
1313
1314         /* Now copy the time into the VmInfo struct as everything went well. */
1315         memcpy (&vm->info.last_state_change, &time_tmp, sizeof (struct timeval));
1316         memcpy (&host->info.last_modification, &time_tmp, sizeof (struct timeval));
1317         memcpy (&dst_host->info.last_modification, &time_tmp, sizeof (struct timeval));
1318         memcpy (&dst_host->info.last_resource_allocation, &time_tmp, sizeof (struct timeval));
1319
1320 unlock:
1321         _vsc_mgmt_unlock ();
1322 }
1323
1324
1325
1326 void
1327 vsc_mgmt_vm_get_uuid (struct VscError *error,
1328                       const struct VscMgmtIpv4 *vm_ipv4,
1329                       struct VscMgmtUuid *vm_uuid)
1330 {
1331         VSC__ASSERT (error != NULL);
1332         VSC__ASSERT (! error->occured);
1333
1334         _IF_NOT_INITIALIZED (return);
1335
1336         _vsc_mgmt_lock ();
1337
1338         _vsc_mgmt_data_vm_get_uuid (vm_ipv4, vm_uuid);
1339
1340         _vsc_mgmt_unlock ();
1341 }
1342
1343
1344
1345 void
1346 vsc_mgmt_vm_get_host (struct VscError *error,
1347                       const struct VscMgmtUuid *vm_uuid,
1348                       struct VscMgmtIpv4 *host_ipv4)
1349 {
1350         VSC__ASSERT (error != NULL);
1351         VSC__ASSERT (! error->occured);
1352         VSC__ASSERT (vm_uuid != NULL);
1353         VSC__ASSERT (host_ipv4 != NULL);
1354
1355         _IF_NOT_INITIALIZED (return);
1356
1357         _vsc_mgmt_lock ();
1358
1359         _vsc_mgmt_data_vm_get_host_ipv4 (error, vm_uuid, host_ipv4);
1360         if (error->occured) {
1361                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1362                 goto unlock;
1363         }
1364
1365 unlock:
1366         _vsc_mgmt_unlock ();
1367 }
1368
1369
1370
1371 void
1372 vsc_mgmt_vm_get_info (struct VscError *error,
1373                       const struct VscMgmtUuid *vm_uuid,
1374                       struct VscMgmtVmInfo *vm_info)
1375 {
1376         struct VscMgmtVm *vm = NULL;
1377         char vm_uuid_string[VSC_MGMT__UUID__STRING_SIZE];
1378
1379         VSC__ASSERT (error != NULL);
1380         VSC__ASSERT (! error->occured);
1381         VSC__ASSERT (vm_uuid != NULL);
1382         VSC__ASSERT (vm_info != NULL);
1383
1384         _IF_NOT_INITIALIZED (return);
1385
1386         _vsc_mgmt_lock ();
1387
1388         vm = _vsc_mgmt_data_vm_lookup_by_uuid (vm_uuid);
1389         if (! vm) {
1390                 vsc_mgmt_uuid_format (vm_uuid, vm_uuid_string);
1391                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1392                              "Unknown VM UUID '%s'", vm_uuid_string);
1393                 goto unlock;
1394         }
1395
1396         _vm_info_copy (error, vm_info, &vm->info);
1397         if (error->occured) {
1398                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1399                 goto unlock;
1400         }
1401
1402 unlock:
1403         _vsc_mgmt_unlock ();
1404 }
1405
1406
1407
1408 void
1409 vsc_mgmt_vm_info_cleanup (struct VscMgmtVmInfo *vm_info)
1410 {
1411         VSC__ASSERT (vm_info != NULL);
1412
1413         if (vm_info->checkpoint_uuid_list)
1414                 vsc_free (&vm_info->checkpoint_uuid_list);
1415
1416         if (vm_info->image_filename)
1417                 vsc_free (&vm_info->image_filename);
1418
1419         if (vm_info->uuid_string)
1420                 vsc_free (&vm_info->uuid_string);
1421
1422         memset (vm_info, '\0', sizeof (struct VscMgmtVmInfo));
1423 }
1424
1425
1426
1427
1428 void
1429 vsc_mgmt_vm_set_resource_config (struct VscError *error,
1430                                  const struct VscMgmtUuid *vm_uuid,
1431                                  const struct VscMgmtVmResourceConfig *new_res)
1432 {
1433         struct VscMgmtVm *vm = NULL;
1434         struct VscMgmtHost *host = NULL;
1435         virDomainPtr domain = NULL;
1436         char vm_uuid_string[VSC_MGMT__UUID__STRING_SIZE];
1437         struct VscError *cleanup_error = NULL;
1438         struct timeval time_tmp;
1439
1440         VSC__ASSERT (error != NULL);
1441         VSC__ASSERT (! error->occured);
1442         VSC__ASSERT (vm_uuid != NULL);
1443         VSC__ASSERT (new_res != NULL);
1444
1445         _IF_NOT_INITIALIZED (return);
1446
1447         _vsc_mgmt_lock ();
1448
1449         vm = _vsc_mgmt_data_vm_lookup_by_uuid (vm_uuid);
1450         if (! vm) {
1451                 vsc_mgmt_uuid_format (vm_uuid, vm_uuid_string);
1452                 VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1453                              "Unknown VM UUID '%s'", vm_uuid_string);
1454                 goto unlock;
1455         }
1456
1457         host = _vsc_mgmt_data_host_lookup_by_ip (&vm->host_ip);
1458         if (! host) {
1459                 VSC__ERROR2 (error, VSC__ERROR_CODE__INTERNAL_ERROR,
1460                              "Failed to get informatio about host %u.%u.%u.%u",
1461                              vm->host_ip.bytes[0], vm->host_ip.bytes[1],
1462                              vm->host_ip.bytes[2], vm->host_ip.bytes[3]);
1463                 goto unlock;
1464         }
1465
1466         _vm_resource_config_validate (error, new_res);
1467         if (error->occured) {
1468                 vsc_mgmt_uuid_format (vm_uuid, vm_uuid_string);
1469                 VSC__APPEND_ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
1470                                     "Invalid resource config for VM '%s'",
1471                                     vm_uuid_string);
1472                 goto unlock;
1473         }
1474
1475         /*
1476          * Query the time for the last_state_change value
1477          * (query into a temporary stack was as gettimeofday() might fail
1478          *  and it would be a bad idea to either write the into the VmInfo
1479          *  struct before being sure the action succeeded or first perform
1480          *  the action and then cannot update the timing information.)
1481          */
1482         if (gettimeofday (&time_tmp, NULL) != 0) {
1483                 VSC__ERROR2 (error, VSC__ERROR_CODE__ERRNO,
1484                              "Failed to query current time: %s",
1485                              strerror (errno));
1486         }
1487
1488         /* Get the domain pointer */
1489         domain = _vm_get_domain_pointer (error, vm);
1490         if (error->occured) {
1491                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1492                 goto unlock;
1493         }
1494
1495         /* Get the domain pointer */
1496         domain = _vm_get_domain_pointer (error, vm);
1497         if (error->occured) {
1498                 VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1499                 goto unlock;
1500         }
1501
1502
1503         /* Memory changed? */
1504         if (vm->info.resource_config.memory_size != new_res->memory_size) {
1505                 if (virDomainSetMemory (domain, new_res->memory_size) != 0) {
1506                         _ERROR_FROM_LIBVIRT (error, virDomainGetConnect (domain));
1507                         goto unlock;
1508                 }
1509         }
1510
1511         /* Number of cores changed? */
1512         if (vm->info.resource_config.num_cores != new_res->num_cores) {
1513                 if (virDomainSetVcpus (domain, new_res->num_cores) != 0) {
1514                         _ERROR_FROM_LIBVIRT (error, virDomainGetConnect (domain));
1515
1516                         cleanup_error = vsc_error_new ();
1517                         goto rollback_memory;
1518                 }
1519         }
1520
1521         /* Core clock changed? */
1522         if (vm->info.resource_config.num_cores != new_res->num_cores ||
1523             vm->info.resource_config.core_clock != new_res->core_clock) {
1524                 _apply_vm_sched_parameters (error, domain, new_res, host);
1525                 if (error->occured) {
1526                         VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
1527
1528                         cleanup_error = vsc_error_new ();
1529                         goto rollback_vcpus;
1530                 }
1531         }
1532
1533         /* Take over new values */
1534         memcpy (&vm->info.resource_config, new_res,
1535                 sizeof (struct VscMgmtVmResourceConfig));
1536
1537         /* Now copy the time into the VmInfo struct as everything went well. */
1538         memcpy (&vm->info.last_config_change, &time_tmp, sizeof (struct timeval));
1539
1540
1541 unlock:
1542         _vsc_mgmt_unlock ();
1543
1544         return;
1545
1546 rollback_vcpus:
1547         /* If we changed the vcpu setting earlier, try to roll it back to its
1548          * original value to maintain consitency.
1549          */
1550         if (vm->info.resource_config.num_cores != new_res->num_cores) {
1551                 if (virDomainSetVcpus (domain,
1552                                        vm->info.resource_config.num_cores) != 0) {
1553                         _ERROR_FROM_LIBVIRT (cleanup_error,
1554                                              virDomainGetConnect (domain));
1555                         if (cleanup_error->occured) {
1556                                 vsc_error_append_branch (error, cleanup_error);
1557                                 cleanup_error = vsc_error_new ();
1558                         }
1559                 }
1560         }
1561
1562 rollback_memory:
1563         /* Rollback memory if neccessry
1564          */
1565         if (vm->info.resource_config.memory_size != new_res->memory_size) {
1566                 if (virDomainSetMemory (domain,
1567                                         vm->info.resource_config.memory_size) != 0) {
1568                         _ERROR_FROM_LIBVIRT (cleanup_error,
1569                                              virDomainGetConnect (domain));
1570                         if (cleanup_error->occured) {
1571                                 vsc_error_append_branch (error, cleanup_error);
1572                         }
1573                 }
1574         }
1575
1576         if (cleanup_error != NULL && ! cleanup_error->occured) {
1577                 vsc_error_free (&cleanup_error);
1578         }
1579
1580         goto unlock;
1581 }