[lib] Add recover for data and network backend.
authorMaximilian Wilhelm <max@rfc2324.org>
Tue, 15 Sep 2009 20:17:34 +0000 (22:17 +0200)
committerMaximilian Wilhelm <max@rfc2324.org>
Tue, 15 Sep 2009 20:17:34 +0000 (22:17 +0200)
  refs #89

Signed-off-by: Maximilian Wilhelm <max@rfc2324.org>

13 files changed:
backend/data/data.h
backend/data/glibtree/Makefile
backend/data/glibtree/data_glibtree.c
backend/network/common/Makefile
backend/network/common/common.c
backend/network/common/common.h
backend/network/dhcponly/Makefile
backend/network/dhcponly/network.c
backend/network/network.h
lib/core.c
lib/network.c
lib/types.h
tests/manual/Makefile

index 8be5047..48b4068 100644 (file)
@@ -25,6 +25,8 @@
 #ifndef __VSC_MGMT__BACKEND__DATA_H__
 #define __VSC_MGMT__BACKEND__DATA_H__
 
+#include <libxml/tree.h>
+
 #include <libvscmisc/error.h>
 
 #include "../../include/libvscmgmt/types.h"
@@ -48,7 +50,13 @@ void
 _vsc_mgmt_data_backup (struct VscError *error, FILE *fp);
 
 void
-_vsc_mgmt_data_recover (struct VscError *error, const char* filename);
+_vsc_mgmt_data_recover_start (struct VscError *error, const xmlNode *node);
+
+void
+_vsc_mgmt_data_recover_commit (void);
+
+void
+_vsc_mgmt_data_recover_rollback (void);
 
 
 /* Host focussed functions */
index 92e53ba..b5646dc 100644 (file)
@@ -14,6 +14,10 @@ TARGET   = libvscmgmt-data-glibtree.so
 CFLAGS  += $(shell pkg-config --cflags glib-2.0)
 LDFLAGS += $(shell pkg-config --libs glib-2.0)
 
+# libxml
+CFLAGS  += $(shell pkg-config libxml-2.0 --cflags)
+LDFLAGS += $(shell pkg-config libxml-2.0 --libs)
+
 #
 # Rules
 #
index f87c046..502f412 100644 (file)
  * @file
  */
 
-#include <glib-2.0/glib.h>
 #include <stdlib.h>
 #include <uuid/uuid.h>
+#include <glib-2.0/glib.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
 
 #include <libvscmisc/assert.h>
 #include <libvscmisc/error.h>
 #include <libvscmisc/memory.h>
+#include <libvscmisc/string.h>
 
 #include "../../../include/libvscmgmt/ipv4.h"
 #include "../../../include/libvscmgmt/uuid.h"
@@ -39,6 +43,7 @@
 #include "../../../lib/ipv4.h"
 #include "../../../lib/mac.h"
 #include "../../../lib/uuid.h"
+#include "../../../lib/xml.h"
 
 
 struct HostTreeElem {
@@ -52,6 +57,12 @@ struct VmIpLookupTreeElem {
        struct VscMgmtUuid vm_uuid;
 };
 
+static struct _backup {
+       GTree *_host_by_ip_tree;
+       GTree *_vm_by_uuid_tree;
+       GTree *_vm_by_ip_lookup_tree;
+} _backup;
+
 
 static int _data_initialized = 0;
 
@@ -208,6 +219,7 @@ _host_tree_traverse_backup_hosts (gpointer key, gpointer value, gpointer file_pt
 }
 
 
+
 /*
  * Traversal functions for the VM trees.
  */
@@ -224,6 +236,423 @@ _vm_ip_lookup_tree_traverse_free (gpointer key, gpointer value, gpointer data)
 
 
 
+/*
+ * XML restore related function
+ */
+static inline xmlChar *
+_get_content (const xmlNode *node)
+{
+       VSC__ASSERT (node != NULL);
+
+       if (node && node->children) {
+               return node->children->content;
+       } else {
+               return NULL;
+       }
+}
+
+
+static void
+_read_resource_config (struct VscError *error, const xmlNode *xml_res_conf,
+                       struct VscMgmtVmResourceConfig *res_conf)
+{
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+       VSC__ASSERT (xml_res_conf != NULL);
+       VSC__ASSERT (res_conf != NULL);
+
+       xmlNode *xml_child = NULL;
+       int temp;
+
+       VSC__ASSERT (xmlStrEqual (xml_res_conf->name, BAD_CAST "resource_config"));
+
+       for (xml_child = xml_res_conf->children; xml_child; xml_child = xml_child->next) {
+               if (xml_child->type != XML_ELEMENT_NODE)
+                       continue;
+
+               if (xmlStrEqual (xml_child->name, BAD_CAST "num_cores")) {
+                       _vsc_mgmt_xml_parse_int (error, xml_child, &res_conf->num_cores, 10);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               return;
+                       }
+               }
+
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "core_clock")) {
+                       _vsc_mgmt_xml_parse_int (error, xml_child, &res_conf->core_clock, 10);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               return;
+                       }
+               }
+
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "memory_size")) {
+                       _vsc_mgmt_xml_parse_int (error, xml_child, &temp, 10);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               return;
+                       }
+
+                       res_conf->memory_size = temp;
+               }
+       }
+
+}
+
+static void
+_read_vm (struct VscError *error, const xmlNode *xml_vm, const struct VscMgmtHost *host)
+{
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+       VSC__ASSERT (xml_vm != NULL);
+       VSC__ASSERT (host != NULL);
+
+       struct VscMgmtVm *vm = NULL;
+
+       xmlNode *xml_child = NULL;
+       xmlChar *content = NULL;
+
+       VSC__ASSERT (xmlStrEqual (xml_vm->name, BAD_CAST "vm"));
+
+       vm = vsc_alloc (error, sizeof (struct VscMgmtVm));
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               return;
+       }
+
+       for (xml_child = xml_vm->children; xml_child; xml_child = xml_child->next) {
+               if (xml_child->type != XML_ELEMENT_NODE)
+                       continue;
+
+               /*
+                * Forget about any previos content string and try to prefetch
+                * the new one
+                */
+               content = NULL;
+               content = _get_content (xml_child);
+
+               /*
+                * VmInfo values
+                */
+
+               /* UUID */
+               if (xmlStrEqual (xml_child->name, BAD_CAST "uuid_string")) {
+                       _vsc_mgmt_xml_parse_uuid (error, xml_child,
+                                                 &vm->info.uuid,
+                                                 &vm->info.uuid_string);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto failure;
+                       }
+               }
+
+               /* Image filename */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "image_filename")) {
+                       if (! content && host->info.type != VSC_MGMT__HOST_TYPE__VIRTUAL) {
+                               VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                                            "No value for 'image_filename' given at line %i",
+                                            xml_child->line);
+                               goto failure;
+                       }
+
+                       vm->info.image_filename = vsc_strdup (error, (const char *) content);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto failure;
+                       }
+               }
+
+               /* Network UUID */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "network_uuid")) {
+                       _vsc_mgmt_xml_parse_uuid (error, xml_child,
+                                                 &vm->info.network_uuid,
+                                                 NULL);
+                       if (error->occured) {
+                               goto failure;
+                       }
+               }
+
+               /* IP address  */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "ipv4")) {
+                       _vsc_mgmt_xml_parse_ipv4 (error, xml_child, &vm->info.ipv4, NULL);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto failure;
+                       }
+               }
+
+               /* Mac address */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "mac")) {
+                       _vsc_mgmt_xml_parse_mac (error, xml_child, &vm->info.mac);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto failure;
+                       }
+               }
+
+
+               /* Resource config */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "resource_config")) {
+                       _read_resource_config (error, xml_child, &vm->info.resource_config);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto failure;
+                       }
+               }
+
+               /* FIXME:
+                  - state
+                  - suspension_uuid
+                  - checkpoint_uuid_list
+                  - timevals
+                */
+
+
+               /*
+                * Vm values
+                */
+
+               /* Host IP */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "host_ip")) {
+                       _vsc_mgmt_xml_parse_ipv4 (error, xml_child, &vm->host_ip, NULL);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto failure;
+                       }
+
+                       if (_vsc_mgmt_ipv4_compare (&host->info.ipv4, &vm->host_ip) != 0) {
+                               VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                                            "Host IP of VM does not fit IP of corresponding host");
+                               goto failure;
+                       }
+               }
+       } /* for each value */
+
+       _vsc_mgmt_data_vm_add (error, vm);
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               goto failure;
+       }
+
+
+       return;
+
+failure:
+       vsc_free (&vm->info.uuid_string);
+       vsc_free (&vm->info.image_filename);
+       vsc_free (&vm);
+}
+
+static void
+_read_host (struct VscError *error, const xmlNode *xml_host)
+{
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+       VSC__ASSERT (xml_host != NULL);
+
+       struct VscMgmtHost *host = NULL;
+
+       xmlNode *xml_child = NULL;
+       xmlNode *vms = NULL;
+       xmlChar *content = NULL;
+
+       int int_temp;
+
+       VSC__ASSERT (xmlStrEqual (xml_host->name, BAD_CAST "host"));
+
+       host = vsc_alloc (error, sizeof (struct VscMgmtHost));
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               return;
+       }
+
+       for (xml_child = xml_host->children; xml_child; xml_child = xml_child->next) {
+               if (xml_child->type != XML_ELEMENT_NODE)
+                       continue;
+
+               /*
+                * Forget about any previos content string and try to prefetch
+                * the new one
+                */
+               content = NULL;
+               content = _get_content (xml_child);
+
+               /* VMs (delay parsing) */
+               if (xmlStrEqual (xml_child->name, BAD_CAST "vms")) {
+                       vms = xml_child;
+               }
+
+               /*
+                * HostInfo values
+                */
+
+               /* Host IP */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "ipv4")) {
+                       _vsc_mgmt_xml_parse_ipv4 (error, xml_child, &host->info.ipv4, NULL);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto failure;
+                       }
+               }
+
+               /* Type */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "type")) {
+                       if (! content) {
+                               VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                                            "No value for 'type' given at line %i",
+                                                   xml_child->line);
+                               goto failure;
+                       }
+
+                       host->info.type = _vsc_mgmt_host_type_parse (error,
+                                                                    (const char *) content);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR2 (error, VSC__ERROR_CODE__TRACE,
+                                                   "Invalid host type at line %i",
+                                                   xml_child->line);
+                               goto failure;
+                       }
+               }
+
+               /* Num cores */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "num_cores")) {
+                       _vsc_mgmt_xml_parse_int (error, xml_child, &host->info.num_cores, 10);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               return;
+                       }
+               }
+
+               /* Core clock */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "core_clock")) {
+                       _vsc_mgmt_xml_parse_int (error, xml_child, &host->info.core_clock, 10);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               return;
+                       }
+               }
+
+               /* Memory size */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "memory_size")) {
+                       _vsc_mgmt_xml_parse_int (error, xml_child, &int_temp, 10);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               return;
+                       }
+
+                       host->info.memory_size = int_temp;
+               }
+
+
+               /*
+                * VscMgmtHost values
+                */
+
+               /* Transport type (maybe optional) */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "transport_type") && content) {
+                       host->transport_type = vsc_strdup (error, (const char *) content);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto failure;
+                       }
+               }
+
+               /* Username */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "username") && content) {
+                       host->username = vsc_strdup (error, (const char *) content);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto failure;
+                       }
+               }
+
+               /* Password */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "password") && content) {
+                       host->password = vsc_strdup (error, (const char *) content);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto failure;
+                       }
+               }
+
+
+               /* Virtual hardware filename */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "virtual_hardware_filename") &&
+                    content) {
+                       host->virtual_hardware_filename = vsc_strdup (error, (const char *) content);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto failure;
+                       }
+               }
+
+
+               /* ESX vCenter IP */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "vcenter_ip") && content) {
+                       vsc_mgmt_ipv4_parse (error, (const char *) content, &host->vcenter_ip);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR2 (error, VSC__ERROR_CODE__TRACE,
+                                                   "Invalid IP for host at line %i",
+                                                   xml_child->line);
+                               goto failure;
+                       }
+               }
+
+       } /* for each attribute */
+
+       /*
+        * Validate host and add if to the storage (if valid)
+        */
+       if (! _vsc_mgmt_host_is_valid (host)) {
+               VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                            "Invalid host entry at line %i",
+                            xml_child->line);
+               goto failure;
+       }
+
+       /* It's suspicous if there is no <vms> entry */
+       if (! vms) {
+               VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                            "Host found at line %i does not have a <vms> entry.",
+                            xml_child->line);
+               goto failure;
+       }
+
+       _vsc_mgmt_data_host_add (error, host);
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               goto failure;
+       }
+
+
+       /*
+        * Parse VMs (if any)
+        */
+       for (xml_child = vms->children; xml_child; xml_child = xml_child->next) {
+               if (xml_child->type == XML_ELEMENT_NODE &&
+                   xmlStrEqual (xml_child->name, BAD_CAST "vm")) {
+                       _read_vm (error, xml_child, host);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               /*
+                                * Don't free the host here, as it will be freed
+                                * on the succeding call to recover_rollback.
+                                */
+                               return;
+                       }
+               }
+       }
+
+       return;
+
+
+failure:
+       vsc_free (&host->transport_type);
+       vsc_free (&host->username);
+       vsc_free (&host->password);
+       vsc_free (&host->virtual_hardware_filename);
+       vsc_free (&host);
+}
 
 /******************************************************************************
  *                             private API functions                          *
@@ -330,14 +759,122 @@ _vsc_mgmt_data_backup (struct VscError *error, FILE *fp)
 
 
 
+
 void
-_vsc_mgmt_data_recover (struct VscError *error, const char* filename)
+_vsc_mgmt_data_recover_start (struct VscError *error, const xmlNode *node)
 {
        VSC__ASSERT (error != NULL);
        VSC__ASSERT (! error->occured);
-       VSC__ASSERT (filename != NULL);
+       VSC__ASSERT (node != NULL);
+
+       VSC__ASSERT (xmlStrEqual (node->name, BAD_CAST "data"));
+
+       xmlNode *child = NULL;
+       xmlNode *hosts = NULL;
+
+       /* Save pointers to the current trees */
+       _backup._host_by_ip_tree = _host_by_ip_tree;
+       _backup._vm_by_uuid_tree = _vm_by_uuid_tree;
+       _backup._vm_by_ip_lookup_tree = _vm_by_ip_lookup_tree;
+
+       /*
+        * Clear the current pointers so it's save to use the cleanup()
+        * function if anything fails
+        */
+       _host_by_ip_tree = NULL;
+       _vm_by_uuid_tree = NULL;
+       _vm_by_ip_lookup_tree = NULL;
+
+       /* Setup new trees to be filled from the backup */
+       _host_by_ip_tree = g_tree_new (_ip_compare);
+       if (! _host_by_ip_tree) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__OUT_OF_MEMORY,
+                           "Failed to setup host tree");
+               goto restore;
+       }
+
+       _vm_by_uuid_tree = g_tree_new (_uuid_compare);
+       if (! _vm_by_uuid_tree) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__OUT_OF_MEMORY,
+                           "Failed to setup vm uuid tree");
+               goto restore;
+       }
+
+       _vm_by_ip_lookup_tree = g_tree_new (_ip_compare);
+       if (! _vm_by_ip_lookup_tree) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__OUT_OF_MEMORY,
+                            "Failed to setup vm ipv4 tree");
+               goto restore;
+       }
+
+       /* Search for the 'hosts' entry */
+       for (child = node->children; child; child = child->next) {
+               if (child->type == XML_ELEMENT_NODE &&
+                   xmlStrEqual (child->name, BAD_CAST "hosts")) {
+                       hosts = child;
+                       break;
+               }
+       }
+
+       if (! hosts) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                            "No <hosts> entry found.");
+               goto restore;
+       }
+
+       for (child = hosts->children; child; child = child->next) {
+               if (child->type == XML_ELEMENT_NODE &&
+                   xmlStrEqual (child->name, BAD_CAST "host")) {
+                       _read_host (error, child);
+                       if (error->occured) {
+                               goto restore;
+                       }
+               }
+       }
+
+       return;
+
+restore:
+       _vsc_mgmt_data_recover_rollback ();
+}
+
+
+void
+_vsc_mgmt_data_recover_commit (void)
+{
+       if (_backup._vm_by_uuid_tree) {
+               g_tree_destroy (_backup._vm_by_uuid_tree);
+       }
+
+       if (_backup._vm_by_ip_lookup_tree) {
+               g_tree_foreach (_backup._vm_by_ip_lookup_tree, _vm_ip_lookup_tree_traverse_free, NULL);
+               g_tree_destroy (_backup._vm_by_ip_lookup_tree);
+       }
+
+       if (_backup._host_by_ip_tree) {
+               g_tree_foreach (_backup._host_by_ip_tree, _host_tree_traverse_free, NULL);
+               g_tree_destroy (_backup._host_by_ip_tree);
+       }
+
+       _backup._host_by_ip_tree = NULL;
+       _backup._vm_by_uuid_tree = NULL;
+       _backup._vm_by_ip_lookup_tree = NULL;
+}
+
+void
+_vsc_mgmt_data_recover_rollback (void)
+{
+       /* Cleanup the current trees (if any) but preserve the initialized flag */
+       _vsc_mgmt_data_cleanup ();
+       _data_initialized = 1;
+
+       _host_by_ip_tree = _backup._host_by_ip_tree;
+       _vm_by_uuid_tree = _backup._vm_by_uuid_tree;
+       _vm_by_ip_lookup_tree = _backup._vm_by_ip_lookup_tree;
 
-       VSC__ERROR0 (error, VSC__ERROR_CODE__NOT_IMPLEMENTED_YET);
+       _backup._host_by_ip_tree = NULL;
+       _backup._vm_by_uuid_tree = NULL;
+       _backup._vm_by_ip_lookup_tree = NULL;
 }
 
 
@@ -517,7 +1054,7 @@ _vsc_mgmt_data_vm_add (struct VscError *error, struct VscMgmtVm *vm)
        host_tree_elem = g_tree_lookup (_host_by_ip_tree, &vm->host_ip);
        if (! host_tree_elem) {
                VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
-                            "VM should belong to an unknown host.");
+                            "VM cannot belong to an unknown host.");
                return;
        }
 
index e4a9586..782463b 100644 (file)
@@ -14,6 +14,11 @@ TARGET   = libvscmgmt-network-common.a
 CFLAGS  += $(shell pkg-config --cflags glib-2.0)
 LDFLAGS += $(shell pkg-config --libs glib-2.0)
 
+# libxml
+CFLAGS  += $(shell pkg-config libxml-2.0 --cflags)
+LDFLAGS += $(shell pkg-config libxml-2.0 --libs)
+
+
 #
 # Rules
 #
index 3348f59..5ea4c81 100644 (file)
  */
 
 #include <errno.h>
-#include <inttypes.h>
 #include <glib-2.0/glib.h>
 #include <uuid/uuid.h>
 
 #include <libvscmisc/assert.h>
 #include <libvscmisc/error.h>
 #include <libvscmisc/memory.h>
+#include <libvscmisc/string.h>
 
 #include "../../../include/libvscmgmt/network.h"
+#include "../../../include/libvscmgmt/ipv4.h"
 #include "../../../lib/core.h"
 #include "../../../lib/ipv4.h"
+#include "../../../lib/mac.h"
 #include "../../../lib/network.h"
 #include "../../../lib/uuid.h"
 #include "../../../lib/parser.h"
+#include "../../../lib/xml.h"
 #include "../network.h"
 #include "common.h"
 
+
+static struct _backup {
+       struct VscMgmtNetwork *_network_list_available;
+       struct VscMgmtNetwork *_network_list_used;
+
+       uint64_t _mac_base;
+       uint64_t _mac_last;
+
+       GTree *_mac_tree_available;
+       GTree *_mac_tree_used;
+} _backup;
+
+
 static int _network_initialized = FALSE;
 static struct VscMgmtNetwork *_network_list_available = NULL;
 static struct VscMgmtNetwork *_network_list_used = NULL;
@@ -181,12 +197,7 @@ _generate_MACs (struct VscError *error)
                        goto unlock;
                }
 
-               mac_tmp->bytes[0] = (mac_tmp_uint & 0xFF0000000000) >> 40;
-               mac_tmp->bytes[1] = (mac_tmp_uint & 0x00FF00000000) >> 32;
-               mac_tmp->bytes[2] = (mac_tmp_uint & 0x0000FF000000) >> 24;
-               mac_tmp->bytes[3] = (mac_tmp_uint & 0x000000FF0000) >> 16;
-               mac_tmp->bytes[4] = (mac_tmp_uint & 0x00000000FF00) >>  8;
-               mac_tmp->bytes[5] = (mac_tmp_uint & 0x0000000000FF);
+               _vsc_mgmt_mac_from_uint (mac_tmp, mac_tmp_uint);
 
                g_tree_insert (_mac_tree_available, (gpointer) mac_tmp,
                               (gpointer) mac_tmp);
@@ -269,6 +280,372 @@ unlock:
 }
 
 
+/*
+ * XML restore related functions
+ */
+static inline char *
+_get_content (const xmlNode *node)
+{
+       VSC__ASSERT (node != NULL);
+
+       if (node && node->children) {
+               return (char *)  node->children->content;
+       } else {
+               return NULL;
+       }
+}
+
+
+static void
+_read_network (struct VscError *error, const xmlNode *xml_network,
+               int used)
+{
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+       VSC__ASSERT (xml_network != NULL);
+
+       struct VscMgmtNetwork *network = NULL;
+
+       xmlNode *xml_child = NULL;
+       char *content;
+
+       /* Network information */
+       struct VscMgmtUuid uuid;
+       unsigned int i;
+       struct VscMgmtIpv4 ip_network;
+       int netmask = 0;
+       struct VscMgmtIpv4 gateway;
+
+       /* Network entries */
+       int id_read;
+       uint32_t entry_id;
+       enum VscMgmtIpStatus entry_status;
+       struct VscMgmtMac mac_tmp;
+
+       xmlNode *entries = NULL;
+       xmlNode *entry = NULL;
+
+       VSC__ASSERT (xmlStrEqual (xml_network->name, BAD_CAST "network"));
+
+       uuid_clear (uuid.value);
+
+       for (xml_child = xml_network->children; xml_child; xml_child = xml_child->next) {
+               if (xml_child->type != XML_ELEMENT_NODE)
+                       continue;
+
+               /*
+                * Forget about any previos content string and try to prefetch
+                * the new one
+                */
+               content = NULL;
+               content = _get_content (xml_child);
+
+               /* UUID */
+               if (xmlStrEqual (xml_child->name, BAD_CAST "uuid")) {
+                       /* An unused network may not have an UUID */
+                       if (! content && used == 0)
+                               continue;
+
+                       _vsc_mgmt_xml_parse_uuid (error, xml_child, &uuid, NULL);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto failure;
+                       }
+               }
+
+               /* CIDR */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "cidr")) {
+                       _vsc_mgmt_xml_parse_ipv4 (error, xml_child, &ip_network, &netmask);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto failure;
+                       }
+               }
+
+               /* Gateway */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "gateway")) {
+                       _vsc_mgmt_xml_parse_ipv4 (error, xml_child, &gateway, NULL);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto failure;
+                       }
+               }
+
+               /* Entries */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "entries")) {
+                       entries = xml_child;
+               }
+       }
+
+       /* If this network is a used one and there has to be an UUID in the backup */
+       if (used && uuid_is_null (uuid.value)) {
+               VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                            "No UUID 'used' network starting at line %i",
+                            xml_network->line);
+               goto failure;
+       }
+
+
+       /* _vsc_mgmt_network_new() will validate the parameters */
+       network = _vsc_mgmt_network_new (error, &ip_network, netmask, &gateway);
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               goto failure;
+       }
+
+       memcpy (&network->uuid.value, uuid.value, sizeof (uuid_t));
+
+
+       /* Read the network entries and store them directly */
+       for (entry = entries->children; entry; entry = entry->next) {
+               if (entry->type != XML_ELEMENT_NODE)
+                       continue;
+
+               /* Reset temp vars */
+               id_read = 0;
+               entry_status = VSC_MGMT__IP_STATUS__UNDEFINED;
+               _vsc_mgmt_mac_clear (&mac_tmp);
+
+               for (xml_child = entry->children; xml_child; xml_child = xml_child->next) {
+                       if (xml_child->type != XML_ELEMENT_NODE)
+                               continue;
+
+                       /*
+                        * Forget about any previos content string and try to prefetch
+                        * the new one
+                        */
+                       content = NULL;
+                       content = _get_content (xml_child);
+
+
+                       if (xmlStrEqual (xml_child->name, BAD_CAST "id")) {
+                               _vsc_mgmt_xml_parse_uint (error, xml_child, &entry_id, 10);
+                               if (error->occured) {
+                                       VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                                       goto failure;
+                               }
+
+                               id_read = 1;
+                       }
+
+                       else if (xmlStrEqual (xml_child->name, BAD_CAST "status")) {
+                               if (! content) {
+                                       VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                                                    "No value for 'status' at line %i",
+                                                    xml_child->line);
+                                       goto failure;
+                               }
+
+                               entry_status = vsc_strtoi (error, content, NULL, 10);
+                               if (error->occured || entry_status < 1 || entry_status > 4) {
+                                       VSC__APPEND_ERROR2 (error, VSC__ERROR_CODE__TRACE,
+                                                           "Invalid value for 'status' at line %i: %s",
+                                                           xml_child->line, content);
+                                       goto failure;
+                               }
+                       }
+
+                       else if (xmlStrEqual (xml_child->name, BAD_CAST "mac")) {
+                               if (! content) {
+                                       /* Maybe we read the MAC before the status
+                                        * so it's not possible to decide wether
+                                        * a MAC is neccessary here.
+                                        */
+                                       continue;
+                               }
+
+                               _vsc_mgmt_mac_parse (error, content, &mac_tmp);
+                               if (error->occured || ! _vsc_mgmt_mac_is_valid (&mac_tmp)) {
+                                       VSC__APPEND_ERROR2 (error, VSC__ERROR_CODE__TRACE,
+                                                           "Invalid value for 'status' at line %i: %s",
+                                                           xml_child->line, content);
+                                       goto failure;
+                               }
+
+                       }
+               } /* for each entry element */
+
+
+               if (! id_read || entry_status == VSC_MGMT__IP_STATUS__UNDEFINED) {
+                       VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                                    "Invalid entry starting at line %i",
+                                    entry->line);
+                       goto failure;
+               }
+
+               network->ip_status[entry_id] = entry_status;
+
+               if (entry_status > 1) {
+                       network->num_ips_free--;
+               }
+
+               if (entry_status == VSC_MGMT__IP_STATUS__IN_USE) {
+                       if (_vsc_mgmt_mac_is_valid (&mac_tmp)) {
+                               memcpy (&network->macs[entry_id], &mac_tmp, sizeof (struct VscMgmtMac));
+                       } else {
+                               VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                                            "No or invalid MAC in entry starting at line %i",
+                                            entry->line);
+                               goto failure;
+                       }
+               }
+       } /* for each entry */
+
+
+       /* Check if any entry was missing */
+       for (entry_id = 0; entry_id < network->size; entry_id++) {
+               if (network->ip_status[i] == VSC_MGMT__IP_STATUS__UNDEFINED) {
+                       VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                                    "Missing entry information for entry #%i",
+                                    entry_id);
+                       goto failure;
+               }
+       }
+
+       /* Append this network to the correct list */
+       if (used) {
+               _vsc_mgmt_network_list_append (&_network_list_used, network);
+       } else {
+               _vsc_mgmt_network_list_append (&_network_list_available, network);
+       }
+
+       return;
+
+failure:
+       _vsc_mgmt_network_free (&network);
+}
+
+
+static void
+_read_networks (struct VscError *error, const xmlNode *xml_networks)
+{
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+       VSC__ASSERT (xml_networks != NULL);
+
+       xmlNode *child = NULL;
+       xmlNode *network = NULL;
+
+       VSC__ASSERT (xmlStrEqual (xml_networks->name, BAD_CAST "networks"));
+
+       for (child = xml_networks->children; child; child = child->next) {
+               if (child->type != XML_ELEMENT_NODE)
+                       continue;
+
+               if (xmlStrEqual (child->name, BAD_CAST "available")) {
+                       for (network = child->children; network; network = network->next) {
+                               if (network->type != XML_ELEMENT_NODE)
+                                       continue;
+
+                               /* Maybe this should be handled as error */
+                               if (! xmlStrEqual (network->name, BAD_CAST "network"))
+                                       continue;
+
+                               /* Read an available (un-used) network */
+                               _read_network (error, network, 0);
+                               if (error->occured) {
+                                       VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                                       goto failure;
+                               }
+                       }
+               }
+
+               else if (xmlStrEqual (child->name, BAD_CAST "used")) {
+                       for (network = child->children; network; network = network->next) {
+                               if (network->type != XML_ELEMENT_NODE)
+                                       continue;
+
+                               /* Maybe this should be handled as error */
+                               if (! xmlStrEqual (network->name, BAD_CAST "network"))
+                                       continue;
+
+                               /* Read an used network */
+                               _read_network (error, network, 1);
+                               if (error->occured) {
+                                       VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                                       goto failure;
+                               }
+                       }
+               }
+       }
+
+       return;
+
+failure:
+       return;
+}
+
+
+static void
+_read_macs (struct VscError *error, const xmlNode *xml_macs)
+{
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+       VSC__ASSERT (xml_macs != NULL);
+
+       xmlNode *child = NULL;
+       xmlNode *available_macs = NULL;
+       char *content;
+       struct VscMgmtMac mac_tmp;
+       struct VscMgmtMac *mac;
+
+       VSC__ASSERT (xmlStrEqual (xml_macs->name, BAD_CAST "macs"));
+
+       /* Get available macs tag */
+       for (child = xml_macs->children; child; child = child->next) {
+               if (child->type != XML_ELEMENT_NODE)
+                       continue;
+
+               if (xmlStrEqual (child->name, BAD_CAST "available")) {
+                       available_macs = child;
+               }
+       }
+
+       if (! available_macs) {
+               VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                            "Available macs missing in macs entry at line %i",
+                            xml_macs->line);
+               goto failure;
+       }
+
+       for (child = available_macs->children; child; child = child->next) {
+               if (child->type != XML_ELEMENT_NODE)
+                       continue;
+
+               content = NULL;
+               content = _get_content (child);
+
+               if (xmlStrEqual (child->name, BAD_CAST "mac")) {
+                       _vsc_mgmt_xml_parse_mac (error, child, &mac_tmp);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto failure;
+                       }
+
+                       /* Ok, seems to ba valid, store it */
+                       mac = NULL;
+                       mac = vsc_alloc (error, sizeof (struct VscMgmtMac));
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto failure;
+                       }
+
+                       /* Copy Mac address */
+                       memcpy (mac, &mac_tmp, sizeof (struct VscMgmtMac));
+
+                       g_tree_insert (_mac_tree_available, (gpointer) mac,
+                                      (gpointer) mac);
+               }
+       }
+
+       return;
+
+failure:
+       return;
+}
+
+
+
 /*******************************************************************************
  *                           private API functions                             *
  ******************************************************************************/
@@ -403,6 +780,7 @@ _vsc_mgmt_network_common_backup (struct VscError *error, FILE *fp)
 {
        struct VscMgmtNetwork *net = NULL;
        char network_uuid[VSC_MGMT__UUID__STRING_SIZE];
+       struct VscMgmtMac mac_tmp;
        unsigned int netmask;
        uint32_t i;
 
@@ -412,8 +790,16 @@ _vsc_mgmt_network_common_backup (struct VscError *error, FILE *fp)
 
        /*             <network> */
        fprintf (fp, "  <common>\n");
-       fprintf (fp, "   <mac_base>%"PRIu64"</mac_base>\n", _mac_base);
-       fprintf (fp, "   <mac_last>%"PRIu64"</mac_last>\n", _mac_last);
+
+       _vsc_mgmt_mac_from_uint (&mac_tmp, _mac_base);
+       fprintf (fp, "   <mac_base>%02x:%02x:%02x:%02x:%02x:%02x</mac_base>\n",
+                mac_tmp.bytes[0], mac_tmp.bytes[1],mac_tmp.bytes[2],
+                mac_tmp.bytes[3], mac_tmp.bytes[4],mac_tmp.bytes[5]);
+
+       _vsc_mgmt_mac_from_uint (&mac_tmp, _mac_last);
+       fprintf (fp, "   <mac_last>%02x:%02x:%02x:%02x:%02x:%02x</mac_last>\n",
+                mac_tmp.bytes[0], mac_tmp.bytes[1],mac_tmp.bytes[2],
+                mac_tmp.bytes[3], mac_tmp.bytes[4],mac_tmp.bytes[5]);
 
        /*
         * Backup networks
@@ -427,6 +813,8 @@ _vsc_mgmt_network_common_backup (struct VscError *error, FILE *fp)
                uuid_unparse (net->uuid.value, network_uuid);
                fprintf (fp, "     <network>\n");
 
+               fprintf (fp, "      <uuid>%s</uuid>\n", network_uuid);
+
                netmask = _vsc_mgmt_network_mask_from_bits (net->cidr_mask);
                fprintf (fp, "      <cidr>%u.%u.%u.%u/%u</cidr>\n",
                         (net->cidr_network & 0xFF000000) >> 24,
@@ -467,8 +855,13 @@ _vsc_mgmt_network_common_backup (struct VscError *error, FILE *fp)
                uuid_unparse (net->uuid.value, network_uuid);
                fprintf (fp, "     <network>\n");
 
-               fprintf (fp, "      <cidr>%"PRIu32"/%"PRIu32"</cidr>\n",
-                        net->cidr_network, net->cidr_mask);
+               netmask = _vsc_mgmt_network_mask_from_bits (net->cidr_mask);
+               fprintf (fp, "      <cidr>%u.%u.%u.%u/%u</cidr>\n",
+                        (net->cidr_network & 0xFF000000) >> 24,
+                        (net->cidr_network & 0x00FF0000) >> 16,
+                        (net->cidr_network & 0x0000FF00) >>  8,
+                        (net->cidr_network & 0x000000FF),
+                        netmask);
 
                fprintf (fp, "      <gateway>%u.%u.%u.%u</gateway>\n",
                         net->gateway_ip.bytes[0], net->gateway_ip.bytes[1],
@@ -510,6 +903,173 @@ _vsc_mgmt_network_common_backup (struct VscError *error, FILE *fp)
 
 
 void
+_vsc_mgmt_network_common_recover_start (struct VscError *error,
+                                        const xmlNode *node)
+{
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+       VSC__ASSERT (_network_initialized);
+       VSC__ASSERT (node != NULL);
+
+       VSC__ASSERT (xmlStrEqual (node->name, BAD_CAST "common"));
+
+       xmlNode *child = NULL;
+       char *content = NULL;
+
+       struct VscMgmtMac mac_tmp;
+
+       /* Save pointers to the current trees and lists */
+        _backup._network_list_available = _network_list_available;
+       _backup._network_list_used = _network_list_used;
+       _backup._mac_base = _mac_base;
+       _backup._mac_last = _mac_last;
+       _backup._mac_tree_available = _mac_tree_available;
+       _backup._mac_tree_used = _mac_tree_used;
+
+       /*
+        * Clear the current pointers so it's save to use the cleanup()
+        * function if anything fails
+        */
+       _network_list_available = NULL;
+       _network_list_used = NULL;
+       _mac_base = 0;
+       _mac_last = 0;
+       _mac_tree_available = NULL;
+       _mac_tree_used = NULL;
+
+       /* Set up new trees */
+       _mac_tree_available = g_tree_new (_mac_compare);
+       if (! _mac_tree_available) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__OUT_OF_MEMORY,
+                            "Failed to setup mac tree");
+               goto restore;
+       }
+
+       _mac_tree_used = g_tree_new (_mac_compare);
+       if (! _mac_tree_used) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__OUT_OF_MEMORY,
+                            "Failed to setup mac tree");
+               goto restore;
+       }
+
+
+
+       /* Read data from XML backup file */
+       for (child = node->children; child; child = child->next) {
+               if (child->type != XML_ELEMENT_NODE)
+                       continue;
+
+               content = NULL;
+
+               if (xmlStrEqual (child->name, BAD_CAST "mac_base")) {
+                       content = _get_content (child);
+                       _vsc_mgmt_mac_parse (error, content, &mac_tmp);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto restore;
+                       }
+
+                       _mac_base = _vsc_mgmt_mac_to_uint (&mac_tmp);
+               }
+
+               else if (xmlStrEqual (child->name, BAD_CAST "mac_last")) {
+                       content = _get_content (child);
+                       _vsc_mgmt_mac_parse (error, content, &mac_tmp);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto restore;
+                       }
+
+                       _mac_last = _vsc_mgmt_mac_to_uint (&mac_tmp);
+               }
+
+               else if (xmlStrEqual (child->name, BAD_CAST "networks")) {
+                       _read_networks (error, child);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto restore;
+                       }
+               }
+
+               else if (xmlStrEqual (child->name, BAD_CAST "macs")) {
+                       _read_macs (error, child);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto restore;
+                       }
+               }
+       }
+
+       return;
+
+restore:
+       _vsc_mgmt_network_common_recover_rollback ();
+}
+
+
+
+void
+_vsc_mgmt_network_common_recover_rollback (void)
+{
+       /* Cleanup the current trees/lists (if any) but preserve the initialized flag */
+       _vsc_mgmt_network_common_cleanup ();
+       _network_initialized = TRUE;
+
+        _network_list_available = _backup._network_list_available;
+       _network_list_used = _backup._network_list_used;
+
+       _mac_base = _backup._mac_base;
+       _mac_last = _backup._mac_last;
+
+       _mac_tree_available = _backup._mac_tree_available;
+       _mac_tree_used = _backup._mac_tree_used;
+
+
+       /* Clear all the backup pointers */
+        _backup._network_list_available = NULL;
+       _backup._network_list_used = NULL;
+
+       _backup._mac_base = 0;
+       _backup._mac_last = 0;
+
+       _backup._mac_tree_available = NULL;
+       _backup._mac_tree_used = NULL;
+
+}
+
+
+
+void
+_vsc_mgmt_network_common_recover_commit (void)
+{
+       /* Forget about all backups */
+       _vsc_mgmt_network_list_free (&_backup._network_list_available);
+       _vsc_mgmt_network_list_free (&_backup._network_list_used);
+
+       if (_backup._mac_tree_available) {
+               g_tree_foreach (_backup._mac_tree_available, _mac_tree_traverse_free, NULL);
+               g_tree_destroy (_backup._mac_tree_available);
+       }
+
+       if (_backup._mac_tree_used) {
+               g_tree_foreach (_backup._mac_tree_used, _mac_tree_traverse_free, NULL);
+               g_tree_destroy (_backup._mac_tree_used);
+       }
+
+       /* Clear all the backup pointers */
+        _backup._network_list_available = NULL;
+       _backup._network_list_used = NULL;
+
+       _backup._mac_base = 0;
+       _backup._mac_last = 0;
+
+       _backup._mac_tree_available = NULL;
+       _backup._mac_tree_used = NULL;
+}
+
+
+
+void
 _vsc_mgmt_network_common_synchronize (struct VscError *error)
 {
        VSC__ASSERT (error != NULL);
@@ -830,6 +1390,38 @@ _vsc_mgmt_network_common_getinfo (struct VscError *error,
 }
 
 
+enum VscMgmtIpStatus *
+_vsc_mgmt_network_common_get_ip_status (struct VscError *error,
+                                        const struct VscMgmtUuid *network_uuid)
+{
+       struct VscMgmtNetwork *network_temp;
+
+       enum VscMgmtIpStatus *ip_status = NULL;
+
+       VSC__ASSERT (error);
+       VSC__ASSERT (! error->occured);
+       VSC__ASSERT (_network_initialized);
+       VSC__ASSERT (network_uuid);
+
+       network_temp = _vsc_mgmt_network_list_lookup (_network_list_used, network_uuid);
+       if (! network_temp) {
+               VSC__ERROR1 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                            "Invalid network uuid.");
+               return NULL;
+       }
+
+       ip_status = vsc_alloc (error, network_temp->size * sizeof (enum VscMgmtIpStatus));
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               return NULL;
+       }
+
+       memcpy (ip_status, network_temp->ip_status,
+               network_temp->size * sizeof (*network_temp->ip_status));
+
+       return ip_status;
+}
+
 
 
 /*!
index 7312106..38c0a7e 100644 (file)
@@ -26,6 +26,8 @@
 #ifndef __VSC_MGMT__BACKEND__COMMON_H__
 #define __VSC_MGMT__BACKEND__COMMON_H__
 
+#include <libxml/tree.h>
+
 #include "../../../lib/network.h"
 
 void
@@ -38,6 +40,16 @@ void
 _vsc_mgmt_network_common_backup (struct VscError *error, FILE *fp);
 
 void
+_vsc_mgmt_network_common_recover_start (struct VscError *error,
+                                        const xmlNode *node);
+
+void
+_vsc_mgmt_network_common_recover_rollback (void);
+
+void
+_vsc_mgmt_network_common_recover_commit (void);
+
+void
 _vsc_mgmt_network_common_synchronize (struct VscError *error);
 
 void
@@ -52,6 +64,10 @@ _vsc_mgmt_network_common_getinfo (struct VscError *error,
                                   struct VscMgmtNetwork *network,
                                   const struct VscMgmtUuid *network_uuid);
 
+enum VscMgmtIpStatus *
+_vsc_mgmt_network_common_get_ip_status (struct VscError *error,
+                                        const struct VscMgmtUuid *network_uuid);
+
 void
 _vsc_mgmt_network_common_teardown (struct VscError *error,
                                    const struct VscMgmtUuid *network_uuid);
index 2120daa..997e00c 100644 (file)
@@ -13,6 +13,10 @@ TARGET   = libvscmgmt-network-dhcponly.so
 # network backend common library
 LDFLAGS += -L ../common -lvscmgmt-network-common
 
+# libxml
+CFLAGS  += $(shell pkg-config libxml-2.0 --cflags)
+LDFLAGS += $(shell pkg-config libxml-2.0 --libs)
+
 #
 # Rules
 #
index 240e788..edbcac0 100644 (file)
 #include <time.h>
 #include <unistd.h>
 
+#include <uuid/uuid.h>
+
 #include <libvscmisc/assert.h>
 #include <libvscmisc/error.h>
 #include <libvscmisc/memory.h>
+#include <libvscmisc/string.h>
 
 #include "../../../lib/core.h" /* locking */
-#include "../../../lib/uuid.h"
 #include "../../../lib/ipv4.h"
+#include "../../../lib/mac.h"
+#include "../../../lib/uuid.h"
+#include "../../../lib/xml.h"
 #include "../../../include/libvscmgmt/uuid.h"
 
 #include "../common/common.h"
@@ -57,7 +62,9 @@ static FILE *dhcp_config_file_fp = NULL;
 
 
 enum DhcpIpStatus {
-       _DHCP_IP_STATUS__FREE = 0,
+       _DHCP_IP_STATUS__UNDEFINED = 0,
+       _DHCP_IP_STATUS__RESERVED,
+       _DHCP_IP_STATUS__FREE,
        _DHCP_IP_STATUS__IN_USE,
 };
 
@@ -78,6 +85,10 @@ struct DhcpNetwork {
        struct VscMgmtUuid *uuids;
 };
 
+static struct _backup {
+       struct DhcpNetwork *_active_network_list;
+} _backup;
+
 
 static int _network_initialized = FALSE;
 
@@ -136,12 +147,28 @@ _map_dhcp_config_file_header (struct VscError *error)
 }
 
 
+static void
+_dhcp_network_free (struct DhcpNetwork **network)
+{
+       if (network == NULL || *network == NULL) {
+               return;
+       }
+
+       vsc_free (&(*network)->macs);
+       vsc_free (&(*network)->ip_status);
+       vsc_free (&(*network)->uuid);
+       vsc_free (network);
+}
+
+
 
 static struct DhcpNetwork *
 _dhcp_network_new (struct VscError *error,
                    const struct VscMgmtNetwork *vsc_network)
 {
-       struct DhcpNetwork *dhcp_network;
+       struct DhcpNetwork *dhcp_network = NULL;
+       uint32_t i;
+       enum VscMgmtIpStatus *ip_status = NULL;
 
        VSC__ASSERT (error != NULL);
        VSC__ASSERT (! error->occured);
@@ -150,12 +177,20 @@ _dhcp_network_new (struct VscError *error,
        dhcp_network = vsc_alloc (error, sizeof (struct DhcpNetwork));
        if (error->occured) {
                VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
-               return NULL;
+               goto out_free;
+       }
+
+       /* Query the IP status informaton of the network */
+       ip_status = _vsc_mgmt_network_common_get_ip_status (error, &vsc_network->uuid);
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               goto out_free;
        }
 
        /* To be safe in 'out_free' case */
        dhcp_network->next = NULL;
        dhcp_network->uuid = NULL;
+       dhcp_network->ip_status = NULL;
        dhcp_network->macs = NULL;
 
        dhcp_network->uuid = _vsc_mgmt_uuid_clone (error, &vsc_network->uuid);
@@ -189,19 +224,31 @@ _dhcp_network_new (struct VscError *error,
 
        dhcp_network->num_ips_active = 0;
 
+       for (i = 0; i < vsc_network->size; i++) {
+               if (ip_status[i] == VSC_MGMT__IP_STATUS__RESERVED) {
+                       dhcp_network->ip_status[i] = _DHCP_IP_STATUS__RESERVED;
+               } else {
+                       dhcp_network->ip_status[i] = _DHCP_IP_STATUS__FREE;
+               }
+
+               /*
+                * Ignore all other values here as they are not relevant when setting
+                * a new network and should be set correctly in recover case and can
+                * be easily validated then.
+                */
+       }
+
        return dhcp_network;
 
 out_free:
-       vsc_free (&dhcp_network->macs);
-       vsc_free (&dhcp_network->ip_status);
-       vsc_free (&dhcp_network->uuid);
-       vsc_free (&dhcp_network);
+       vsc_free (&ip_status);
+
+       _dhcp_network_free (&dhcp_network);
 
        return NULL;
 }
 
 
-
 static inline struct DhcpNetwork *
 _dhcp_network_lookup (const struct VscMgmtUuid *uuid)
 {
@@ -240,6 +287,242 @@ _ip_index (const struct DhcpNetwork *network,
 
 
 
+/*
+ * XML related stuff
+ */
+
+static char *
+_get_content (const xmlNode *node)
+{
+       VSC__ASSERT (node != NULL);
+
+       if (node->children && node->children->content)
+               return (char *) node->children->content;
+       else
+               return NULL;
+}
+
+
+
+static void
+_read_network (struct VscError *error, const xmlNode *xml_network)
+{
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+       VSC__ASSERT (xml_network != NULL);
+
+       struct VscMgmtNetwork mgmt_network;
+       struct DhcpNetwork *dhcp_network = NULL;
+       struct DhcpNetwork *dhcp_network_loop = NULL;
+
+       xmlNode *xml_child = NULL;
+       char *content;
+
+       /* Network information */
+       struct VscMgmtUuid network_uuid;
+
+       /* Network entries */
+       int id_read;
+       uint32_t entry_id;
+       int entry_status_temp;
+       enum DhcpIpStatus entry_status;
+       struct VscMgmtMac mac_tmp;
+       struct VscMgmtUuid vm_uuid;
+
+       xmlNode *entries = NULL;
+       xmlNode *entry = NULL;
+
+       VSC__ASSERT (xmlStrEqual (xml_network->name, BAD_CAST "network"));
+
+       uuid_clear (network_uuid.value);
+
+       for (xml_child = xml_network->children; xml_child; xml_child = xml_child->next) {
+               if (xml_child->type != XML_ELEMENT_NODE)
+                       continue;
+
+               /* UUID */
+               if (xmlStrEqual (xml_child->name, BAD_CAST "uuid")) {
+                       _vsc_mgmt_xml_parse_uuid (error, xml_child, &network_uuid, NULL);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto failure;
+                       }
+               }
+
+               /* Entries */
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "entries")) {
+                       entries = xml_child;
+               }
+       }
+
+       /* Check is there is a UUID */
+       if (uuid_is_null (network_uuid.value)) {
+               VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                            "No UUID for network starting at line %i",
+                            xml_network->line);
+               goto failure;
+       }
+
+       /* Query the network information from the common backend */
+       _vsc_mgmt_network_common_getinfo (error, &mgmt_network, &network_uuid);
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               goto failure;
+       }
+
+       /* Set up a DHCP network */
+       dhcp_network = _dhcp_network_new (error, &mgmt_network);
+       if (error->occured) {
+               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+               goto failure;
+       }
+
+
+       /* Read the network entries and store them directly */
+       for (entry = entries->children; entry; entry = entry->next) {
+               if (entry->type != XML_ELEMENT_NODE)
+                       continue;
+
+               /* Reset temp vars */
+               id_read = 0;
+               entry_status = _DHCP_IP_STATUS__UNDEFINED;
+               uuid_clear (vm_uuid.value);
+               _vsc_mgmt_mac_clear (&mac_tmp);
+
+               for (xml_child = entry->children; xml_child; xml_child = xml_child->next) {
+                       if (xml_child->type != XML_ELEMENT_NODE)
+                               continue;
+
+                       /*
+                        * Forget about any previos content string and try to prefetch
+                        * the new one
+                        */
+                       content = NULL;
+                       content = _get_content (xml_child);
+
+
+                       /* ID */
+                       if (xmlStrEqual (xml_child->name, BAD_CAST "id")) {
+                               _vsc_mgmt_xml_parse_uint (error, xml_child, &entry_id, 10);
+                               if (error->occured) {
+                                       VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                                       goto failure;
+                               }
+
+                               id_read = 1;
+                       }
+
+                       /* Status */
+                       else if (xmlStrEqual (xml_child->name, BAD_CAST "status")) {
+                               _vsc_mgmt_xml_parse_int (error, xml_child, &entry_status_temp, 10);
+                               if (error->occured) {
+                                       VSC__APPEND_ERROR2 (error, VSC__ERROR_CODE__TRACE,
+                                                           "Invalid value for 'status' at line %i: %s",
+                                                           xml_child->line, content);
+                                       goto failure;
+                               }
+                               if (entry_status_temp < 0 || entry_status_temp > 3) {
+                                       VSC__ERROR2 (error, VSC__ERROR_CODE__TRACE,
+                                                    "Invalid value for 'status' at line %i: %s",
+                                                    xml_child->line, content);
+                                       goto failure;
+                               }
+
+                               entry_status = entry_status_temp;
+                       }
+
+                       /* MAC */
+                       else if (xmlStrEqual (xml_child->name, BAD_CAST "mac")) {
+                               if (! content) {
+                                       /* Maybe we read the MAC before the status
+                                        * so it's not possible to decide wether
+                                        * a MAC is neccessary here.
+                                        */
+                                       continue;
+                               }
+
+                               _vsc_mgmt_xml_parse_mac (error, xml_child, &mac_tmp);
+                               if (error->occured) {
+                                       VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                                       goto failure;
+                               }
+                       }
+
+                       /* VM UUID */
+                       else if (xmlStrEqual (xml_child->name, BAD_CAST "vm_uuid")) {
+                               _vsc_mgmt_xml_parse_uuid (error, xml_child, &vm_uuid, NULL);
+                               if (error->occured) {
+                                       VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                                       goto failure;
+                               }
+                       }
+               } /* for each entry element */
+
+
+               if (! id_read || entry_status == _DHCP_IP_STATUS__UNDEFINED) {
+                       VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                                    "Invalid entry starting at line %i",
+                                    entry->line);
+                       goto failure;
+               }
+
+               dhcp_network->ip_status[entry_id] = entry_status;
+
+               if (entry_status == _DHCP_IP_STATUS__IN_USE) {
+                       dhcp_network->num_ips_active++;
+
+                       if (_vsc_mgmt_mac_is_valid (&mac_tmp)) {
+                               memcpy (&dhcp_network->macs[entry_id], &mac_tmp, sizeof (struct VscMgmtMac));
+                       } else {
+                               VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                                            "No or invalid MAC in entry starting at line %i",
+                                            entry->line);
+                               goto failure;
+                       }
+
+                       if (! uuid_is_null (vm_uuid.value)) {
+                               memcpy (&dhcp_network->uuids[entry_id], &vm_uuid, sizeof (struct VscMgmtUuid));
+                       } else {
+                               VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                                            "No or invalid VM UUID in entry starting at line %i",
+                                            entry->line);
+                               goto failure;
+                       }
+               }
+       } /* for each entry */
+
+
+       /* Check if any entry was missing */
+       for (entry_id = 0; entry_id < dhcp_network->size; entry_id++) {
+               if (dhcp_network->ip_status[entry_id] == _DHCP_IP_STATUS__UNDEFINED) {
+                       VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                                    "Missing entry information for entry #%i",
+                                    entry_id);
+                       goto failure;
+               }
+       }
+
+       /* Append this network to our list */
+       if (_active_network_list) {
+               dhcp_network_loop = _active_network_list;
+
+               while (dhcp_network_loop->next) {
+                       dhcp_network_loop = dhcp_network_loop->next;
+               }
+
+               dhcp_network_loop->next = dhcp_network;
+       } else {
+               _active_network_list = dhcp_network;
+       }
+
+
+       return;
+
+failure:
+       _dhcp_network_free (&dhcp_network);
+}
+
+
 /*******************************************************************************
  *                            Public API functions                             *
  ******************************************************************************/
@@ -344,7 +627,6 @@ _vsc_mgmt_network_backup (struct VscError *error, FILE *fp)
 {
        struct DhcpNetwork *network = NULL;
        char uuid_string[VSC_MGMT__UUID__STRING_SIZE];
-       unsigned int netmask = 0;
        uint32_t i;
 
        VSC__ASSERT (error != NULL);
@@ -370,15 +652,8 @@ _vsc_mgmt_network_backup (struct VscError *error, FILE *fp)
                fprintf (fp, "   <network>\n");
 
                vsc_mgmt_uuid_format (network->uuid, uuid_string);
-               netmask = _vsc_mgmt_network_mask_from_bits (network->cidr_mask);
-
                fprintf (fp, "    <uuid>%s</uuid>\n", uuid_string);
-               fprintf (fp, "    <cidr>%u.%u.%u.%u/%u</cidr>\n",
-                        (network->cidr_network & 0xFF000000) >> 24,
-                        (network->cidr_network & 0x00FF0000) >> 16,
-                        (network->cidr_network & 0x0000FF00) >>  8,
-                        (network->cidr_network & 0x000000FF),
-                        netmask);
+               /* The UUID is enough to identify this network in the common backend */
 
                fprintf (fp, "     <entries>\n");
                for (i = 0; i < network->size; i++) {
@@ -410,6 +685,75 @@ _vsc_mgmt_network_backup (struct VscError *error, FILE *fp)
 }
 
 
+void
+_vsc_mgmt_network_recover_start (struct VscError *error, const xmlNode *node)
+{
+       VSC__ASSERT (error != NULL);
+       VSC__ASSERT (! error->occured);
+       VSC__ASSERT (node != NULL);
+
+       xmlNode *xml_child = NULL;
+       xmlNode *dhcp_only = NULL;
+
+       VSC__ASSERT (xmlStrEqual (node->name, BAD_CAST "network"));
+
+       /* Save pointer to the network list and clear current one */
+       _backup._active_network_list = _active_network_list;
+       _active_network_list = NULL;
+
+       /* Search for network subnodes and parse them */
+       for (xml_child = node->children; xml_child; xml_child = xml_child->next) {
+               if (xml_child->type != XML_ELEMENT_NODE)
+                       continue;
+
+               if (xmlStrEqual (xml_child->name, BAD_CAST "common")) {
+                       _vsc_mgmt_network_common_recover_start (error, xml_child);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto restore;
+                       }
+               }
+
+               else if (xmlStrEqual (xml_child->name, BAD_CAST "dhcponly")) {
+                       dhcp_only = xml_child;
+               }
+       }
+
+       /* Parse all our networks */
+       for (xml_child = dhcp_only->children; xml_child; xml_child = xml_child->next) {
+               if (xml_child->type != XML_ELEMENT_NODE)
+                       continue;
+
+               if (xmlStrEqual (xml_child->name, BAD_CAST "network")) {
+                       _read_network (error, xml_child);
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto restore;
+                       }
+               }
+       }
+
+       return;
+
+restore:
+       _vsc_mgmt_network_recover_rollback ();
+       return;
+}
+
+
+
+void
+_vsc_mgmt_network_recover_commit (void)
+{
+}
+
+
+
+void
+_vsc_mgmt_network_recover_rollback (void)
+{
+}
+
 
 void
 _vsc_mgmt_network_synchronize (struct VscError *error)
index 698b42c..6a6565a 100644 (file)
@@ -26,6 +26,8 @@
 #ifndef __VSC_MGMT__BACKEND__NETWORK_H__
 #define __VSC_MGMT__BACKEND__NETWORK_H__
 
+#include <libxml/tree.h>
+
 #include "../../lib/network.h"
 #include "../../include/libvscmgmt/network.h"
 
@@ -39,6 +41,15 @@ void
 _vsc_mgmt_network_backup (struct VscError *error, FILE *fp);
 
 void
+_vsc_mgmt_network_recover_start (struct VscError *error, const xmlNode *node);
+
+void
+_vsc_mgmt_network_recover_commit (void);
+
+void
+_vsc_mgmt_network_recover_rollback (void);
+
+void
 _vsc_mgmt_network_synchronize (struct VscError *error);
 
 
index 9e009a8..8b7a2e6 100644 (file)
@@ -271,22 +271,55 @@ unlock:
 }
 
 void
-vsc_mgmt_recover (struct VscError *error, const char *filename VSC__ATTR__UNUSED)
+vsc_mgmt_recover (struct VscError *error, const char *filename)
 {
        VSC__ASSERT (error != NULL);
        VSC__ASSERT (! error->occured);
+       VSC__ASSERT (filename != NULL);
+
+       xmlDoc *doc = NULL;
+       xmlNode *root = NULL;
+       xmlNode *child = NULL;
 
        _IF_NOT_INITIALIZED (return);
 
        _vsc_mgmt_lock();
 
-       /* FIXME: Implement recover */
+       /* Parse the backup file and get the DOM */
+       doc = xmlReadFile (filename, NULL, 0);
+       if (! doc) {
+               VSC__ERROR2 (error, VSC__ERROR_CODE__INVALID_ARGUMENT,
+                            "Failed to parse backup file \"%s\"", filename);
+               return;
+       }
 
-       if (error->occured) {
-               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
-               goto unlock;
+       root = xmlDocGetRootElement (doc);
+
+       for (child = root->children; child; child = child->next) {
+               if (child->type == XML_ELEMENT_NODE) {
+                       if (xmlStrEqual (child->name, BAD_CAST "data")) {
+                               _vsc_mgmt_data_recover_start (error, child);
+                       }
+
+                       else if (xmlStrEqual (child->name, BAD_CAST "network")) {
+                               _vsc_mgmt_network_recover_start (error, child);
+                       }
+
+                       if (error->occured) {
+                               VSC__APPEND_ERROR0 (error, VSC__ERROR_CODE__TRACE);
+                               goto unlock;
+                       }
+               }
        }
 
+       /*
+        * Validate our new world view
+        */
+
+       _vsc_mgmt_data_recover_commit ();
+
+       _vsc_mgmt_network_recover_commit ();
+
 unlock:
        _vsc_mgmt_unlock();
 }
index 822afe7..915c2b4 100644 (file)
@@ -204,13 +204,13 @@ _vsc_mgmt_network_new (struct VscError *error,
        ip_index = gateway_uint - network_uint;
        network->ip_status[ip_index] = VSC_MGMT__IP_STATUS__RESERVED;
 
+       /* Free IPs = All IPs - (network_address + broadcast_address + gateway) */
+       network->num_ips_free = network->size - 3;
 
        return network;
 
 out_free:
-       vsc_free (&network->ips);
-       vsc_free (&network->ip_status);
-       vsc_free (&network);
+       _vsc_mgmt_network_free (&network);
 
        return NULL;
 }
index 80d5d0c..9e68681 100644 (file)
@@ -36,10 +36,10 @@ extern "C" {
 
 enum VscMgmtIpStatus {
        VSC_MGMT__IP_STATUS__UNDEFINED = 0,
-       VSC_MGMT__IP_STATUS__FREE,
-       VSC_MGMT__IP_STATUS__ACQUIRED,
-       VSC_MGMT__IP_STATUS__IN_USE,
-       VSC_MGMT__IP_STATUS__RESERVED,
+       VSC_MGMT__IP_STATUS__FREE = 1,
+       VSC_MGMT__IP_STATUS__ACQUIRED = 2,
+       VSC_MGMT__IP_STATUS__IN_USE = 3,
+       VSC_MGMT__IP_STATUS__RESERVED = 4,
 };
 
 
index 59d1bec..9838ab7 100644 (file)
@@ -19,6 +19,11 @@ LDFLAGS += -lvscmisc
 # libvscmgmt
 LDFLAGS += $(LIBVSCMGMT_LDFLAGS_L2)
 
+# libxml
+CFLAGS  += $(shell pkg-config libxml-2.0 --cflags)
+LDFLAGS += $(shell pkg-config libxml-2.0 --libs)
+
+
 #
 # Rules
 #