Logo Search packages:      
Sourcecode: pacemaker version File versions  Download package

cib_client.c

/*
 * Copyright (c) 2004 International Business Machines
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
#include <crm_internal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

#include <glib.h>
#include <heartbeat.h>
#include <clplumbing/ipc.h>

#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <cib_private.h>

/* short term hack to reduce callback messages */
typedef struct cib_native_opaque_s 
{
            IPC_Channel *command_channel;
            IPC_Channel *callback_channel;
            GCHSource   *callback_source; 
            
} cib_native_opaque_t;

GHashTable *cib_op_callback_table = NULL;

gboolean verify_cib_cmds(cib_t *cib);

int cib_client_set_op_callback(
      cib_t *cib, void (*callback)(const struct ha_msg *msg, int call_id,
                             int rc, crm_data_t *output));
int cib_client_noop(cib_t *cib, int call_options);
int cib_client_ping(cib_t *cib, crm_data_t **output_data, int call_options);

int cib_client_query(cib_t *cib, const char *section,
           crm_data_t **output_data, int call_options);
int cib_client_query_from(cib_t *cib, const char *host, const char *section,
                    crm_data_t **output_data, int call_options);

int cib_client_sync(cib_t *cib, const char *section, int call_options);
int cib_client_sync_from(
      cib_t *cib, const char *host, const char *section, int call_options);

int cib_client_is_master(cib_t *cib);
int cib_client_set_slave(cib_t *cib, int call_options);
int cib_client_set_slave_all(cib_t *cib, int call_options);
int cib_client_set_master(cib_t *cib, int call_options);

int cib_client_bump_epoch(cib_t *cib, int call_options);
int cib_client_create(cib_t *cib, const char *section, crm_data_t *data,
                  crm_data_t **output_data, int call_options);
int cib_client_modify(cib_t *cib, const char *section, crm_data_t *data,
                  crm_data_t **output_data, int call_options);
int cib_client_update(cib_t *cib, const char *section, crm_data_t *data,
                  crm_data_t **output_data, int call_options);
int cib_client_replace(cib_t *cib, const char *section, crm_data_t *data,
                   crm_data_t **output_data, int call_options);
int cib_client_delete(cib_t *cib, const char *section, crm_data_t *data,
                  crm_data_t **output_data, int call_options);
int cib_client_delete_absolute(
      cib_t *cib, const char *section, crm_data_t *data,
      crm_data_t **output_data, int call_options);
int cib_client_erase(
      cib_t *cib, crm_data_t **output_data, int call_options);
int cib_client_quit(cib_t *cib,   int call_options);

int cib_client_add_notify_callback(
      cib_t *cib, const char *event, void (*callback)(
            const char *event, struct ha_msg *msg));

int cib_client_del_notify_callback(
      cib_t *cib, const char *event, void (*callback)(
            const char *event, struct ha_msg *msg));

gint ciblib_GCompareFunc(gconstpointer a, gconstpointer b);

extern cib_t *cib_native_new(cib_t *cib);
extern void cib_native_delete(cib_t *cib);

static enum cib_variant configured_variant = cib_native;

/* define of the api functions*/
cib_t*
cib_new(void)
{
      cib_t* new_cib = NULL;

      if(configured_variant != cib_native) {
            crm_err("Only the native CIB type is currently implemented");
            return NULL;
      }

      if(cib_op_callback_table != NULL) {
            g_hash_table_destroy(cib_op_callback_table);
            cib_op_callback_table = NULL;
      }
      if(cib_op_callback_table == NULL) {
            cib_op_callback_table = g_hash_table_new_full(
                  g_direct_hash, g_direct_equal,
                  NULL, g_hash_destroy_str);
      }

      crm_malloc0(new_cib, sizeof(cib_t));

      new_cib->call_id = 1;

      new_cib->type  = cib_none;
      new_cib->state = cib_disconnected;

      new_cib->op_callback    = NULL;
      new_cib->variant_opaque = NULL;
      new_cib->notify_list    = NULL;

      /* the rest will get filled in by the variant constructor */
      crm_malloc0(new_cib->cmds, sizeof(cib_api_operations_t));

      new_cib->cmds->set_op_callback     = cib_client_set_op_callback;
      new_cib->cmds->add_notify_callback = cib_client_add_notify_callback;
      new_cib->cmds->del_notify_callback = cib_client_del_notify_callback;
      
      new_cib->cmds->noop    = cib_client_noop;
      new_cib->cmds->ping    = cib_client_ping;
      new_cib->cmds->query   = cib_client_query;
      new_cib->cmds->sync    = cib_client_sync;

      new_cib->cmds->query_from = cib_client_query_from;
      new_cib->cmds->sync_from  = cib_client_sync_from;
      
      new_cib->cmds->is_master  = cib_client_is_master;
      new_cib->cmds->set_master = cib_client_set_master;
      new_cib->cmds->set_slave  = cib_client_set_slave;
      new_cib->cmds->set_slave_all = cib_client_set_slave_all;

      new_cib->cmds->bump_epoch = cib_client_bump_epoch;

      new_cib->cmds->create  = cib_client_create;
      new_cib->cmds->modify  = cib_client_modify;
      new_cib->cmds->update  = cib_client_update;
      new_cib->cmds->replace = cib_client_replace;
      new_cib->cmds->delete  = cib_client_delete;
      new_cib->cmds->erase   = cib_client_erase;
      new_cib->cmds->quit    = cib_client_quit;

      new_cib->cmds->delete_absolute  = cib_client_delete_absolute;
      
      cib_native_new(new_cib);
      if(verify_cib_cmds(new_cib) == FALSE) {
            cib_delete(new_cib);
            return NULL;
      }
      
      return new_cib;
}

void
cib_delete(cib_t *cib)
{
      GList *list = cib->notify_list;
      while(list != NULL) {
            cib_notify_client_t *client = g_list_nth_data(list, 0);
            list = g_list_remove(list, client);
            crm_free(client);
      }
      
      cib_native_delete(cib);
      g_hash_table_destroy(cib_op_callback_table);
      crm_free(cib->cmds);
      crm_free(cib);
}


int
cib_client_set_op_callback(
      cib_t *cib, void (*callback)(const struct ha_msg *msg, int call_id,
                             int rc, crm_data_t *output)) 
{
      if(callback == NULL) {
            crm_info("Un-Setting operation callback");
            
      } else {
            crm_debug_3("Setting operation callback");
      }
      cib->op_callback = callback;
      return cib_ok;
}
      
int cib_client_noop(cib_t *cib, int call_options)
{
      if(cib == NULL) {
            return cib_missing;
      } else if(cib->state == cib_disconnected) {
            return cib_not_connected;
      } else if(cib->cmds->variant_op == NULL) {
            return cib_variant;
      }
      
      return cib->cmds->variant_op(
            cib, CRM_OP_NOOP, NULL, NULL, NULL, NULL, call_options);
}

int cib_client_ping(cib_t *cib, crm_data_t **output_data, int call_options)
{
      if(cib == NULL) {
            return cib_missing;
      } else if(cib->state == cib_disconnected) {
            return cib_not_connected;
      } else if(cib->cmds->variant_op == NULL) {
            return cib_variant;
      }
      
      return cib->cmds->variant_op(
            cib, CRM_OP_PING, NULL,NULL,NULL, output_data, call_options);
}


int cib_client_query(cib_t *cib, const char *section,
                 crm_data_t **output_data, int call_options)
{
      return cib->cmds->query_from(
            cib, NULL, section, output_data, call_options);
}

int cib_client_query_from(cib_t *cib, const char *host, const char *section,
                    crm_data_t **output_data, int call_options)
{
      if(cib == NULL) {
            return cib_missing;
      } else if(cib->state == cib_disconnected) {
            return cib_not_connected;
      } else if(cib->cmds->variant_op == NULL) {
            return cib_variant;
      }
      
      return cib->cmds->variant_op(cib, CIB_OP_QUERY, host, section,
                             NULL, output_data, call_options);
}


int cib_client_is_master(cib_t *cib)
{
      if(cib == NULL) {
            return cib_missing;
      } else if(cib->state == cib_disconnected) {
            return cib_not_connected;
      } else if(cib->cmds->variant_op == NULL) {
            return cib_variant;
      } 
      return cib->cmds->variant_op(
            cib, CIB_OP_ISMASTER, NULL, NULL,NULL,NULL,
            cib_scope_local|cib_sync_call);
}

int cib_client_set_slave(cib_t *cib, int call_options)
{
      if(cib == NULL) {
            return cib_missing;
      } else if(cib->state == cib_disconnected) {
            return cib_not_connected;
      } else if(cib->cmds->variant_op == NULL) {
            return cib_variant;
      } 

      return cib->cmds->variant_op(
            cib, CIB_OP_SLAVE, NULL,NULL,NULL,NULL, call_options);
}

int cib_client_set_slave_all(cib_t *cib, int call_options)
{
      if(cib == NULL) {
            return cib_missing;
      } else if(cib->state == cib_disconnected) {
            return cib_not_connected;
      } else if(cib->cmds->variant_op == NULL) {
            return cib_variant;
      } 

      return cib->cmds->variant_op(
            cib, CIB_OP_SLAVEALL, NULL,NULL,NULL,NULL, call_options);
}

int cib_client_set_master(cib_t *cib, int call_options)
{
      if(cib == NULL) {
            return cib_missing;
      } else if(cib->state == cib_disconnected) {
            return cib_not_connected;
      } else if(cib->cmds->variant_op == NULL) {
            return cib_variant;
      } 

      crm_debug_3("Adding cib_scope_local to options");
      return cib->cmds->variant_op(
            cib, CIB_OP_MASTER, NULL,NULL,NULL,NULL,
            call_options|cib_scope_local);
}



int cib_client_bump_epoch(cib_t *cib, int call_options)
{
      if(cib == NULL) {
            return cib_missing;
      } else if(cib->state == cib_disconnected) {
            return cib_not_connected;
      } else if(cib->cmds->variant_op == NULL) {
            return cib_variant;
      } 

      return cib->cmds->variant_op(
            cib, CIB_OP_BUMP, NULL, NULL, NULL, NULL, call_options);
}

int cib_client_sync(cib_t *cib, const char *section, int call_options)
{
      return cib->cmds->sync_from(cib, NULL, section, call_options);
}

int cib_client_sync_from(
      cib_t *cib, const char *host, const char *section, int call_options)
{
      if(cib == NULL) {
            return cib_missing;
      } else if(cib->state == cib_disconnected) {
            return cib_not_connected;
      } else if(cib->cmds->variant_op == NULL) {
            return cib_variant;
      }

      return cib->cmds->variant_op(
            cib, CIB_OP_SYNC, host, section, NULL, NULL, call_options);
}

int cib_client_create(cib_t *cib, const char *section, crm_data_t *data,
                  crm_data_t **output_data, int call_options) 
{
      if(cib == NULL) {
            return cib_missing;
      } else if(cib->state == cib_disconnected) {
            return cib_not_connected;
      } else if(cib->cmds->variant_op == NULL) {
            return cib_variant;
      } 

      return cib->cmds->variant_op(cib, CIB_OP_CREATE, NULL, section,
                             data, output_data, call_options);
}


int cib_client_modify(cib_t *cib, const char *section, crm_data_t *data,
         crm_data_t **output_data, int call_options) 
{
      if(cib == NULL) {
            return cib_missing;
      } else if(cib->state == cib_disconnected) {
            return cib_not_connected;
      } else if(cib->cmds->variant_op == NULL) {
            return cib_variant;
      } 

      return cib->cmds->variant_op(cib, CIB_OP_MODIFY, NULL, section,
                             data, output_data, call_options);
}

int cib_client_update(cib_t *cib, const char *section, crm_data_t *data,
                  crm_data_t **output_data, int call_options) 
{
      if(cib == NULL) {
            return cib_missing;
      } else if(cib->state == cib_disconnected) {
            return cib_not_connected;
      } else if(cib->cmds->variant_op == NULL) {
            return cib_variant;
      } 

      return cib->cmds->variant_op(cib, CIB_OP_UPDATE, NULL, section,
                             data, output_data, call_options);
}


int cib_client_replace(cib_t *cib, const char *section, crm_data_t *data,
          crm_data_t **output_data, int call_options) 
{
      if(cib == NULL) {
            return cib_missing;
      } else if(cib->state == cib_disconnected) {
            return cib_not_connected;
      } else if(cib->cmds->variant_op == NULL) {
            return cib_variant;
      } else if(data == NULL) {
            return cib_missing_data;
      }
      
      return cib->cmds->variant_op(cib, CIB_OP_REPLACE, NULL, section,
                             data, output_data, call_options);
}


int cib_client_delete(cib_t *cib, const char *section, crm_data_t *data,
         crm_data_t **output_data, int call_options) 
{
      if(cib == NULL) {
            return cib_missing;
      } else if(cib->state == cib_disconnected) {
            return cib_not_connected;
      } else if(cib->cmds->variant_op == NULL) {
            return cib_variant;
      }
      
      return cib->cmds->variant_op(cib, CIB_OP_DELETE, NULL, section,
                             data, output_data, call_options);
}

int cib_client_delete_absolute(
      cib_t *cib, const char *section, crm_data_t *data,
      crm_data_t **output_data, int call_options) 
{
      if(cib == NULL) {
            return cib_missing;
      } else if(cib->state == cib_disconnected) {
            return cib_not_connected;
      } else if(cib->cmds->variant_op == NULL) {
            return cib_variant;
      }
      
      return cib->cmds->variant_op(cib, CIB_OP_DELETE_ALT, NULL, section,
                             data, output_data, call_options);
}

int cib_client_erase(
      cib_t *cib, crm_data_t **output_data, int call_options)
{
      if(cib == NULL) {
            return cib_missing;
      } else if(cib->state == cib_disconnected) {
            return cib_not_connected;
      } else if(cib->cmds->variant_op == NULL) {
            return cib_variant;
      } 

      return cib->cmds->variant_op(cib, CIB_OP_ERASE, NULL, NULL, NULL,
                             output_data, call_options);
}


int cib_client_quit(cib_t *cib, int call_options)
{
      if(cib == NULL) {
            return cib_missing;
      } else if(cib->state == cib_disconnected) {
            return cib_not_connected;
      } else if(cib->cmds->variant_op == NULL) {
            return cib_variant;
      } 

      return cib->cmds->variant_op(
            cib, CRM_OP_QUIT, NULL, NULL, NULL, NULL, call_options);
}

int cib_client_add_notify_callback(
      cib_t *cib, const char *event, void (*callback)(
            const char *event, struct ha_msg *msg))
{
      GList *list_item = NULL;
      cib_notify_client_t *new_client = NULL;
      
      crm_debug_2("Adding callback for %s events (%d)",
                event, g_list_length(cib->notify_list));

      crm_malloc0(new_client, sizeof(cib_notify_client_t));
      new_client->event = event;
      new_client->callback = callback;

      list_item = g_list_find_custom(
            cib->notify_list, new_client, ciblib_GCompareFunc);
      
      if(list_item != NULL) {
            crm_warn("Callback already present");
            crm_free(new_client);
            
      } else {
            cib->notify_list = g_list_append(
                  cib->notify_list, new_client);

            cib->cmds->register_callback(cib, event, 1);
            
            crm_debug_3("Callback added (%d)", g_list_length(cib->notify_list));
      }
      return cib_ok;
}


int cib_client_del_notify_callback(
      cib_t *cib, const char *event, void (*callback)(
            const char *event, struct ha_msg *msg))
{
      GList *list_item = NULL;
      cib_notify_client_t *new_client = NULL;

      crm_debug("Removing callback for %s events", event);

      crm_malloc0(new_client, sizeof(cib_notify_client_t));
      new_client->event = event;
      new_client->callback = callback;

      list_item = g_list_find_custom(
            cib->notify_list, new_client, ciblib_GCompareFunc);
      
      cib->cmds->register_callback(cib, event, 0);

      if(list_item != NULL) {
            cib_notify_client_t *list_client = list_item->data;
            cib->notify_list =
                  g_list_remove(cib->notify_list, list_client);
            crm_free(list_client);

            crm_debug_3("Removed callback");

      } else {
            crm_debug_3("Callback not present");
      }
      crm_free(new_client);
      return cib_ok;
}

gint ciblib_GCompareFunc(gconstpointer a, gconstpointer b)
{
      const cib_notify_client_t *a_client = a;
      const cib_notify_client_t *b_client = b;
      if(a_client->callback == b_client->callback
         && safe_str_neq(a_client->event, b_client->event)) {
            return 0;
      } else if(((long)a_client->callback) < ((long)b_client->callback)) {
            return -1;
      }
      return 1;
}



gboolean
add_cib_op_callback(
      int call_id, gboolean only_success, void *user_data,
      void (*callback)(const HA_Message*, int, int, crm_data_t*,void*)) 
{
      cib_callback_client_t *blob = NULL;

      if(call_id < 0) {
            crm_warn("CIB call failed: %s", cib_error2string(call_id));
            if(only_success == FALSE) {
                  callback(NULL, call_id, call_id, NULL, user_data);
            }
            return FALSE;
      }
      
      crm_malloc0(blob, sizeof(cib_callback_client_t));
      blob->only_success = only_success;
      blob->user_data = user_data;
      blob->callback = callback;
      
      g_hash_table_insert(
            cib_op_callback_table, GINT_TO_POINTER(call_id), blob);
      return TRUE;
}

void
remove_cib_op_callback(int call_id, gboolean all_callbacks) 
{
      if(all_callbacks) {
            if(cib_op_callback_table != NULL) {
                  g_hash_table_destroy(cib_op_callback_table);
            }
            cib_op_callback_table = g_hash_table_new_full(
                  g_direct_hash, g_direct_equal,
                  NULL, g_hash_destroy_str);
      } else {
            g_hash_table_remove(
                  cib_op_callback_table,
                  GINT_TO_POINTER(call_id));
      }
}

int
num_cib_op_callbacks(void)
{
      if(cib_op_callback_table == NULL) {
            return 0;
      }
      return g_hash_table_size(cib_op_callback_table);
}



char *
cib_pluralSection(const char *a_section)
{
      char *a_section_parent = NULL;
      if (a_section == NULL) {
            a_section_parent = crm_strdup("all");

      } else if(strcasecmp(a_section, XML_TAG_CIB) == 0) {
            a_section_parent = crm_strdup("all");

      } else if(strcasecmp(a_section, XML_CIB_TAG_NODE) == 0) {
            a_section_parent = crm_strdup(XML_CIB_TAG_NODES);

      } else if(strcasecmp(a_section, XML_CIB_TAG_STATE) == 0) {
            a_section_parent = crm_strdup(XML_CIB_TAG_STATUS);

      } else if(strcasecmp(a_section, XML_CIB_TAG_CONSTRAINT) == 0) {
            a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS);
            
      } else if(strcasecmp(a_section, XML_CONS_TAG_RSC_LOCATION) == 0) {
            a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS);
            
      } else if(strcasecmp(a_section, XML_CONS_TAG_RSC_DEPEND) == 0) {
            a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS);
            
      } else if(strcasecmp(a_section, XML_CONS_TAG_RSC_ORDER) == 0) {
            a_section_parent = crm_strdup(XML_CIB_TAG_CONSTRAINTS);
            
      } else if(strcasecmp(a_section, "resource") == 0) {
            a_section_parent = crm_strdup(XML_CIB_TAG_RESOURCES);

      } else if(strcasecmp(a_section, XML_CIB_TAG_RESOURCE) == 0) {
            a_section_parent = crm_strdup(XML_CIB_TAG_RESOURCES);

      } else if(strcasecmp(a_section, XML_CIB_TAG_GROUP) == 0) {
            a_section_parent = crm_strdup(XML_CIB_TAG_RESOURCES);

      } else if(strcasecmp(a_section, XML_CIB_TAG_INCARNATION) == 0) {
            a_section_parent = crm_strdup(XML_CIB_TAG_RESOURCES);
            
      } else if(strcasecmp(a_section, XML_CIB_TAG_NVPAIR) == 0) {
            a_section_parent = crm_strdup(XML_CIB_TAG_CRMCONFIG);

      } else if(strcasecmp(a_section, XML_TAG_ATTR_SETS) == 0) {
            a_section_parent = crm_strdup(XML_CIB_TAG_CRMCONFIG);

      } else {
            crm_err("Unknown section %s", a_section);
            a_section_parent = crm_strdup("all");
      }
      
      crm_debug_2("Plural of %s is %s", crm_str(a_section), a_section_parent);

      return a_section_parent;
}

const char *
cib_error2string(enum cib_errors return_code)
{
      const char *error_msg = NULL;
      switch(return_code) {
            case cib_bad_permissions:
                  error_msg = "bad permissions for the on-disk configuration. shutdown heartbeat and repair.";
                  break;
            case cib_bad_digest:
                  error_msg = "the on-disk configuration was manually altered. shutdown heartbeat and repair.";
                  break;
            case cib_bad_config:
                  error_msg = "the on-disk configuration is not valid";
                  break;
            case cib_msg_field_add:
                  error_msg = "failed adding field to cib message";
                  break;                  
            case cib_id_check:
                  error_msg = "missing id or id-collision detected";
                  break;                  
            case cib_operation:
                  error_msg = "invalid operation";
                  break;
            case cib_create_msg:
                  error_msg = "couldnt create cib message";
                  break;
            case cib_client_gone:
                  error_msg = "client left before we could send reply";
                  break;
            case cib_not_connected:
                  error_msg = "not connected";
                  break;
            case cib_not_authorized:
                  error_msg = "not authorized";
                  break;
            case cib_send_failed:
                  error_msg = "send failed";
                  break;
            case cib_reply_failed:
                  error_msg = "reply failed";
                  break;
            case cib_return_code:
                  error_msg = "no return code";
                  break;
            case cib_output_ptr:
                  error_msg = "nowhere to store output";
                  break;
            case cib_output_data:
                  error_msg = "corrupt output data";
                  break;
            case cib_connection:
                  error_msg = "connection failed";
                  break;
            case cib_callback_register:
                  error_msg = "couldnt register callback channel";
                  break;
            case cib_authentication:
                  error_msg = "";
                  break;
            case cib_registration_msg:
                  error_msg = "invalid registration msg";
                  break;
            case cib_callback_token:
                  error_msg = "callback token not found";
                  break;
            case cib_missing:
                  error_msg = "cib object missing";
                  break;
            case cib_variant:
                  error_msg = "unknown/corrupt cib variant";
                  break;
            case CIBRES_MISSING_ID:
                  error_msg = "The id field is missing";
                  break;
            case CIBRES_MISSING_TYPE:
                  error_msg = "The type field is missing";
                  break;
            case CIBRES_MISSING_FIELD:
                  error_msg = "A required field is missing";
                  break;
            case CIBRES_OBJTYPE_MISMATCH:
                  error_msg = "CIBRES_OBJTYPE_MISMATCH";
                  break;
            case cib_EXISTS:
                  error_msg = "The object already exists";
                  break;
            case cib_NOTEXISTS:
                  error_msg = "The object/attribute does not exist";
                  break;
            case CIBRES_CORRUPT:
                  error_msg = "The CIB is corrupt";
                  break;
            case cib_NOOBJECT:
                  error_msg = "The update was empty";
                  break;
            case cib_NOPARENT:
                  error_msg = "The parent object does not exist";
                  break;
            case cib_NODECOPY:
                  error_msg = "Failed while copying update";
                  break;
            case CIBRES_OTHER:
                  error_msg = "CIBRES_OTHER";
                  break;
            case cib_ok:
                  error_msg = "ok";
                  break;
            case cib_unknown:
                  error_msg = "Unknown error";
                  break;
            case cib_STALE:
                  error_msg = "Discarded old update";
                  break;
            case cib_ACTIVATION:
                  error_msg = "Activation Failed";
                  break;
            case cib_NOSECTION:
                  error_msg = "Required section was missing";
                  break;
            case cib_NOTSUPPORTED:
                  error_msg = "Supplied information is not supported";
                  break;
            case cib_not_master:
                  error_msg = "Local service is not the master instance";
                  break;
            case cib_client_corrupt:
                  error_msg = "Service client not valid";
                  break;
            case cib_remote_timeout:
                  error_msg = "Remote node did not respond";
                  break;
            case cib_master_timeout:
                  error_msg = "No master service is currently active";
                  break;
            case cib_revision_unsupported:
                  error_msg = "The required CIB revision number is not supported";
                  break;
            case cib_revision_unknown:
                  error_msg = "The CIB revision number could not be determined";
                  break;
            case cib_missing_data:
                  error_msg = "Required data for this CIB API call not found";
                  break;
            case cib_no_quorum:
                  error_msg = "Write requires quorum";
                  break;
            case cib_diff_failed:
                  error_msg = "Application of an update diff failed";
                  break;
            case cib_diff_resync:
                  error_msg = "Application of an update diff failed, requesting a full refresh";
                  break;
            case cib_bad_section:
                  error_msg = "Invalid CIB section specified";
                  break;
            case cib_old_data:
                  error_msg = "Update was older than existing configuration";
                  break;
            case cib_dtd_validation:
                  error_msg = "Update does not conform to the DTD in "DTD_DIRECTORY"/crm.dtd";
                  break;
            case cib_invalid_argument:
                  error_msg = "Invalid argument";
                  break;
      }
                  
      if(error_msg == NULL) {
            crm_err("Unknown CIB Error Code: %d", return_code);
            error_msg = "<unknown error>";
      }
      
      return error_msg;
}

const char *
cib_op2string(enum cib_update_op operation)
{
      const char *operation_msg = NULL;
      switch(operation) {
            case 0:
                  operation_msg = "none";
                  break;
            case 1:
                  operation_msg = "add";
                  break;
            case 2:
                  operation_msg = "modify";
                  break;
            case 3:
                  operation_msg = "delete";
                  break;
            case CIB_UPDATE_OP_MAX:
                  operation_msg = "invalid operation";
                  break;
                  
      }

      if(operation_msg == NULL) {
            crm_err("Unknown CIB operation %d", operation);
            operation_msg = "<unknown operation>";
      }
      
      return operation_msg;
}

int
cib_section2enum(const char *a_section) 
{
      if(a_section == NULL || strcasecmp(a_section, "all") == 0) {
            return cib_section_all;

      } else if(strcasecmp(a_section, XML_CIB_TAG_NODES) == 0) {
            return cib_section_nodes;

      } else if(strcasecmp(a_section, XML_CIB_TAG_STATUS) == 0) {
            return cib_section_status;

      } else if(strcasecmp(a_section, XML_CIB_TAG_CONSTRAINTS) == 0) {
            return cib_section_constraints;
            
      } else if(strcasecmp(a_section, XML_CIB_TAG_RESOURCES) == 0) {
            return cib_section_resources;

      } else if(strcasecmp(a_section, XML_CIB_TAG_CRMCONFIG) == 0) {
            return cib_section_crmconfig;

      }
      crm_err("Unknown CIB section: %s", a_section);
      return cib_section_none;
}


int
cib_compare_generation(crm_data_t *left, crm_data_t *right)
{
      int lpc = 0;
      const char *attributes[] = {
            XML_ATTR_GENERATION_ADMIN,
            XML_ATTR_GENERATION,
            XML_ATTR_NUMUPDATES,
            XML_ATTR_NUMPEERS
      };

      crm_log_xml_debug_3(left, "left");
      crm_log_xml_debug_3(right, "right");
      
      for(lpc = 0; lpc < DIMOF(attributes); lpc++) {
            int int_elem_l = -1;
            int int_elem_r = -1;
            const char *elem_r = NULL;
            const char *elem_l = crm_element_value(left, attributes[lpc]);

            if(right != NULL) {
                  elem_r = crm_element_value(right, attributes[lpc]);
            }
      
            if(elem_l != NULL) { int_elem_l = crm_parse_int(elem_l, NULL); }
            if(elem_r != NULL) { int_elem_r = crm_parse_int(elem_r, NULL); }

            if(int_elem_l < int_elem_r) {
                  crm_debug_2("%s (%s < %s)", attributes[lpc],
                            crm_str(elem_l), crm_str(elem_r));
                  return -1;
                  
            } else if(int_elem_l > int_elem_r) {
                  crm_debug_2("%s (%s > %s)", attributes[lpc],
                            crm_str(elem_l), crm_str(elem_r));
                  return 1;
            }
      }
      
      return 0;
}

crm_data_t*
get_cib_copy(cib_t *cib)
{
      crm_data_t *xml_cib;
#if CRM_DEPRECATED_SINCE_2_0_4
      crm_data_t *xml_cib_copy;
#endif
      int options = cib_scope_local|cib_sync_call;
      if(cib->cmds->query(cib, NULL, &xml_cib, options) != cib_ok) {
            crm_err("Couldnt retrieve the CIB");
            return NULL;
      } else if(xml_cib == NULL) {
            crm_err("The CIB result was empty");
            return NULL;
      }

      if(safe_str_eq(crm_element_name(xml_cib), XML_TAG_CIB)) {
            return xml_cib;
            
#if CRM_DEPRECATED_SINCE_2_0_4
      } else {
            xml_cib_copy = copy_xml(
                  find_xml_node(xml_cib, XML_TAG_CIB, TRUE));
            free_xml(xml_cib);
            return xml_cib_copy;
#endif
      }
      free_xml(xml_cib);
      return NULL;
}

crm_data_t*
cib_get_generation(cib_t *cib)
{
      crm_data_t *the_cib = get_cib_copy(cib);
      crm_data_t *generation = create_xml_node(
            NULL, XML_CIB_TAG_GENERATION_TUPPLE);

      if(the_cib != NULL) {
            copy_in_properties(generation, the_cib);
            free_xml(the_cib);
      }
      
      return generation;
}

gboolean
apply_cib_diff(crm_data_t *old, crm_data_t *diff, crm_data_t **new)
{
      gboolean result = TRUE;
      const char *value = NULL;

      int this_updates = 0;
      int this_epoch  = 0;
      int this_admin_epoch = 0;

      int diff_add_updates = 0;
      int diff_add_epoch  = 0;
      int diff_add_admin_epoch = 0;

      int diff_del_updates = 0;
      int diff_del_epoch  = 0;
      int diff_del_admin_epoch = 0;

      CRM_CHECK(diff != NULL, return FALSE);
      CRM_CHECK(old != NULL, return FALSE);
      
      value = crm_element_value(old, XML_ATTR_GENERATION_ADMIN);
      this_admin_epoch = crm_parse_int(value, "0");
      crm_debug_3("%s=%d (%s)", XML_ATTR_GENERATION_ADMIN,
              this_admin_epoch, value);
      
      value = crm_element_value(old, XML_ATTR_GENERATION);
      this_epoch = crm_parse_int(value, "0");
      crm_debug_3("%s=%d (%s)", XML_ATTR_GENERATION, this_epoch, value);
      
      value = crm_element_value(old, XML_ATTR_NUMUPDATES);
      this_updates = crm_parse_int(value, "0");
      crm_debug_3("%s=%d (%s)", XML_ATTR_NUMUPDATES, this_updates, value);
      
      cib_diff_version_details(
            diff,
            &diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates, 
            &diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates);

      value = NULL;
      if(result && diff_del_admin_epoch != this_admin_epoch) {
            value = XML_ATTR_GENERATION_ADMIN;
            result = FALSE;
            crm_debug_3("%s=%d", value, diff_del_admin_epoch);

      } else if(result && diff_del_epoch != this_epoch) {
            value = XML_ATTR_GENERATION;
            result = FALSE;
            crm_debug_3("%s=%d", value, diff_del_epoch);

      } else if(result && diff_del_updates != this_updates) {
            value = XML_ATTR_NUMUPDATES;
            result = FALSE;
            crm_debug_3("%s=%d", value, diff_del_updates);
      }

      if(result) {
            int len = 0;
            crm_data_t *tmp = NULL;
            crm_data_t *diff_copy = copy_xml(diff);
            
            tmp = find_xml_node(diff_copy, "diff-removed", TRUE);
            if(tmp != NULL) {
                  len = tmp->nfields;
                  cl_msg_remove(tmp, XML_ATTR_GENERATION_ADMIN);
                  cl_msg_remove(tmp, XML_ATTR_GENERATION);
                  cl_msg_remove(tmp, XML_ATTR_NUMUPDATES);
            }
            
            tmp = find_xml_node(diff_copy, "diff-added", TRUE);
            if(tmp != NULL) {
                  len = tmp->nfields;
                  cl_msg_remove(tmp, XML_ATTR_GENERATION_ADMIN);
                  cl_msg_remove(tmp, XML_ATTR_GENERATION);
                  cl_msg_remove(tmp, XML_ATTR_NUMUPDATES);
            }
            
            result = apply_xml_diff(old, diff_copy, new);
            free_xml(diff_copy);
            
      } else {
            crm_err("target and diff %s values didnt match", value);
      }
      
      
      return result;
}

gboolean xml_has_child(crm_data_t *data, const char *name);

gboolean
xml_has_child(crm_data_t *data, const char *name) 
{
      xml_child_iter_filter(data, child, name,
            return TRUE;
            );
      return FALSE;
}

gboolean
cib_config_changed(crm_data_t *old_cib, crm_data_t *new_cib, crm_data_t **result)
{
      gboolean config_changes = FALSE;
      const char *tag = NULL;
      crm_data_t *diff = NULL;
      crm_data_t *dest = NULL;

      if(result) {
            *result = NULL;
      }

      diff = diff_xml_object(old_cib, new_cib, FALSE);
      if(diff == NULL) {
            return FALSE;
      }

      tag = "diff-removed";
      dest = find_xml_node(diff, tag, FALSE);
      if(dest) {
            dest = find_xml_node(dest, "cib", FALSE);
            
      }

      if(dest) {
            if(xml_has_child(dest, "status")) {
                  cl_msg_remove(dest, "status");
            }
            if(xml_has_children(dest)) {
                  config_changes = TRUE;
            }
      }

      tag = "diff-added";
      dest = find_xml_node(diff, tag, FALSE);
      if(dest) {
            dest = find_xml_node(dest, "cib", FALSE);
      }

      if(dest) {
            if(xml_has_child(dest, "status")) {
                  cl_msg_remove(dest, "status");
            }

            xml_prop_iter(dest, name, value,
                        if(safe_str_neq(name, XML_ATTR_NUMUPDATES)) {
                          config_changes = TRUE;
                          break;
                        }
                );
            
            if(xml_has_children(dest)) {
                  config_changes = TRUE;
            }
      }

      
      if(result) {
            *result = diff;
      } else {
            free_xml(diff);
      }
      
      return config_changes;
}

crm_data_t *
diff_cib_object(crm_data_t *old_cib, crm_data_t *new_cib, gboolean suppress)
{
      crm_data_t *dest = NULL;
      crm_data_t *src = NULL;
      const char *name = NULL;
      const char *value = NULL;

      crm_data_t *diff = diff_xml_object(old_cib, new_cib, suppress);
      
      /* add complete version information */
      src = old_cib;
      dest = find_xml_node(diff, "diff-removed", FALSE);
      if(src != NULL && dest != NULL) {
            name = XML_ATTR_GENERATION_ADMIN;
            value = crm_element_value(src, name);
            if(value == NULL) {
                  value = "0";
            }
            crm_xml_add(dest, name, value);

            name = XML_ATTR_GENERATION;
            value = crm_element_value(src, name);
            if(value == NULL) {
                  value = "0";
            }
            crm_xml_add(dest, name, value);

            name = XML_ATTR_NUMUPDATES;
            value = crm_element_value(src, name);
            if(value == NULL) {
                  value = "0";
            }
            crm_xml_add(dest, name, value);
      }
      
      src = new_cib;
      dest = find_xml_node(diff, "diff-added", FALSE);
      if(src != NULL && dest != NULL) {
            name = XML_ATTR_GENERATION_ADMIN;
            value = crm_element_value(src, name);
            if(value == NULL) {
                  value = "0";
            }
            crm_xml_add(dest, name, value);

            name = XML_ATTR_GENERATION;
            value = crm_element_value(src, name);
            if(value == NULL) {
                  value = "0";
            }
            crm_xml_add(dest, name, value);

            name = XML_ATTR_NUMUPDATES;
            value = crm_element_value(src, name);
            if(value == NULL) {
                  value = "0";
            }
            crm_xml_add(dest, name, value);
      }
      return diff;
}

void
log_cib_diff(int log_level, crm_data_t *diff, const char *function)
{
      int add_updates = 0;
      int add_epoch  = 0;
      int add_admin_epoch = 0;

      int del_updates = 0;
      int del_epoch  = 0;
      int del_admin_epoch = 0;

      if(diff == NULL) {
            return;
      }
      
      cib_diff_version_details(
            diff, &add_admin_epoch, &add_epoch, &add_updates, 
            &del_admin_epoch, &del_epoch, &del_updates);

      if(add_updates != del_updates) {
            do_crm_log(log_level, "%s: Diff: --- %d.%d.%d", function,
                     del_admin_epoch, del_epoch, del_updates);
            do_crm_log(log_level, "%s: Diff: +++ %d.%d.%d", function,
                     add_admin_epoch, add_epoch, add_updates);
      } else if(diff != NULL) {
            do_crm_log(log_level,
                     "%s: Local-only Change: %d.%d.%d", function,
                     add_admin_epoch, add_epoch, add_updates);
      }
      
      log_xml_diff(log_level, diff, function);
}

gboolean
cib_version_details(
      crm_data_t *cib, int *admin_epoch, int *epoch, int *updates)
{
      const char *value = NULL;
      if(cib == NULL) {
            *admin_epoch = -1;
            *epoch  = -1;
            *updates = -1;
            return FALSE;
            
      } else {
            value = crm_element_value(cib, XML_ATTR_GENERATION_ADMIN);
            *admin_epoch = crm_parse_int(value, "-1");

            value  = crm_element_value(cib, XML_ATTR_GENERATION);
            *epoch = crm_parse_int(value, "-1");

            value = crm_element_value(cib, XML_ATTR_NUMUPDATES);
            *updates = crm_parse_int(value, "-1");
      }
      return TRUE;      
}

gboolean
cib_diff_version_details(
      crm_data_t *diff, int *admin_epoch, int *epoch, int *updates, 
      int *_admin_epoch, int *_epoch, int *_updates)
{
      crm_data_t *tmp = NULL;

      tmp = find_xml_node(diff, "diff-added", FALSE);
      cib_version_details(tmp, admin_epoch, epoch, updates);

      tmp = find_xml_node(diff, "diff-removed", FALSE);
      cib_version_details(tmp, _admin_epoch, _epoch, _updates);
      return TRUE;
}

/*
 * The caller should never free the return value
 */
crm_data_t*
get_object_root(const char *object_type, crm_data_t *the_root)
{
      const char *node_stack[2];
      crm_data_t *tmp_node = NULL;
      
      if(the_root == NULL) {
            crm_err("CIB root object was NULL");
            return NULL;
      }
      
      node_stack[0] = XML_CIB_TAG_CONFIGURATION;
      node_stack[1] = object_type;

      if(object_type == NULL
         || strlen(object_type) == 0
         || safe_str_eq(XML_CIB_TAG_SECTION_ALL, object_type)
         || safe_str_eq(XML_TAG_CIB, object_type)) {
            /* get the whole cib */
            return the_root;

      } else if(strcasecmp(object_type, XML_CIB_TAG_STATUS) == 0) {
            /* these live in a different place */
            tmp_node = find_xml_node(the_root, XML_CIB_TAG_STATUS, FALSE);

            node_stack[0] = object_type;
            node_stack[1] = NULL;

      } else {
            tmp_node = find_xml_node_nested(the_root, node_stack, 2);
      }

      if (tmp_node == NULL) {
            crm_debug_2("Section [%s [%s]] not present in %s",
                      node_stack[0],
                      node_stack[1]?node_stack[1]:"",
                      crm_element_name(the_root));
      }
      return tmp_node;
}

const char *
get_crm_option(crm_data_t *cib, const char *name, gboolean do_warn) 
{
      const char * value = NULL;
      crm_data_t * a_default = NULL;
      crm_data_t * config = get_object_root(XML_CIB_TAG_CRMCONFIG, cib);
      
      if(config != NULL) {
            a_default = find_entity(config, XML_CIB_TAG_NVPAIR, name);
      }
      
      if(a_default == NULL) {
            if(do_warn) {
                  crm_warn("Option %s not set", name);
            }
            return NULL;
      }
      
      value = crm_element_value(a_default, XML_NVPAIR_ATTR_VALUE);
      if(safe_str_eq(value, "")) {
            value = NULL;
      }
      
      return value;
}

crm_data_t*
create_cib_fragment_adv(
      crm_data_t *update, const char *update_section, const char *source)
{
      crm_data_t *cib = NULL;
      gboolean whole_cib = FALSE;
      crm_data_t *object_root  = NULL;
      const char *update_name = NULL;
      char *local_section = NULL;

/*    crm_debug("Creating a blank fragment: %s", update_section); */
      
      if(update == NULL && update_section == NULL) {
            crm_debug_3("Creating a blank fragment");
            update = createEmptyCib();
            crm_xml_add(cib, XML_ATTR_ORIGIN, source);
            return update;

      } else if(update == NULL) {
            crm_err("No update to create a fragment for");
            return NULL;
            
      } else if(update_section == NULL) {
            local_section = cib_pluralSection(update_name);
            update_section = local_section;
      }

      if(safe_str_eq(crm_element_name(update), XML_TAG_CIB)) {
            whole_cib = TRUE;
      }
      
      if(whole_cib == FALSE) {
            cib = createEmptyCib();
            crm_xml_add(cib, XML_ATTR_ORIGIN, source);
            object_root = get_object_root(update_section, cib);
            add_node_copy(object_root, update);

      } else {
            cib = copy_xml(update);
            crm_xml_add(cib, XML_ATTR_ORIGIN, source);
      }

      crm_free(local_section);
      crm_debug_3("Verifying created fragment");
      if(verifyCibXml(cib) == FALSE) {
            crm_err("Fragment creation failed");
            crm_log_xml_err(cib, "[src]");            
            free_xml(cib);
            cib = NULL;
      }
      
      return cib;
}

/*
 * It is the callers responsibility to free both the new CIB (output)
 *     and the new CIB (input)
 */
crm_data_t*
createEmptyCib(void)
{
      crm_data_t *cib_root = NULL, *config = NULL, *status = NULL;
      
      cib_root = create_xml_node(NULL, XML_TAG_CIB);

      config = create_xml_node(cib_root, XML_CIB_TAG_CONFIGURATION);
      status = create_xml_node(cib_root, XML_CIB_TAG_STATUS);

/*    crm_xml_add(cib_root, "version", "1"); */
      crm_xml_add(cib_root, "generated", XML_BOOLEAN_TRUE);

      create_xml_node(config, XML_CIB_TAG_CRMCONFIG);
      create_xml_node(config, XML_CIB_TAG_NODES);
      create_xml_node(config, XML_CIB_TAG_RESOURCES);
      create_xml_node(config, XML_CIB_TAG_CONSTRAINTS);
      
      if (verifyCibXml(cib_root)) {
            return cib_root;
      }

      free_xml(cib_root);
      crm_crit("The generated CIB did not pass integrity testing!!"
             "  All hope is lost.");
      return NULL;
}


gboolean
verifyCibXml(crm_data_t *cib)
{
      int lpc = 0;
      gboolean is_valid = TRUE;
      crm_data_t *tmp_node = NULL;

      const char *sections[] = {
            XML_CIB_TAG_NODES,
            XML_CIB_TAG_RESOURCES,
            XML_CIB_TAG_CONSTRAINTS,
            XML_CIB_TAG_STATUS,
            XML_CIB_TAG_CRMCONFIG
      };
      
      if (cib == NULL) {
            crm_warn("CIB was empty.");
            return FALSE;
      }

      /* basic tests... are the standard section all there */
      for(lpc = 0; lpc < DIMOF(sections); lpc++) {
            tmp_node = get_object_root(sections[lpc], cib);
            if (tmp_node == NULL) {
                  crm_warn("Section %s is not present in the CIB",
                         sections[lpc]);
                  is_valid = FALSE;
            }
      }

      /* more integrity tests */

      return is_valid;
}


gboolean verify_cib_cmds(cib_t *cib) 
{
      gboolean valid = TRUE;
      if(cib->cmds->variant_op == NULL) {
            crm_err("Operation variant_op not set");
            valid = FALSE;
      }     
      if(cib->cmds->signon == NULL) {
            crm_err("Operation signon not set");
            valid = FALSE;
      }
      if(cib->cmds->signoff == NULL) {
            crm_err("Operation signoff not set");
            valid = FALSE;
      }
      if(cib->cmds->free == NULL) {
            crm_err("Operation free not set");
            valid = FALSE;
      }
      if(cib->cmds->set_op_callback == NULL) {
            crm_err("Operation set_op_callback not set");
            valid = FALSE;
      }
      if(cib->cmds->add_notify_callback == NULL) {
            crm_err("Operation add_notify_callback not set");
            valid = FALSE;
      }
      if(cib->cmds->del_notify_callback == NULL) {
            crm_err("Operation del_notify_callback not set");
            valid = FALSE;
      }
      if(cib->cmds->set_connection_dnotify == NULL) {
            crm_err("Operation set_connection_dnotify not set");
            valid = FALSE;
      }
      if(cib->cmds->channel == NULL) {
            crm_err("Operation channel not set");
            valid = FALSE;
      }
      if(cib->cmds->inputfd == NULL) {
            crm_err("Operation inputfd not set");
            valid = FALSE;
      }
      if(cib->cmds->noop == NULL) {
            crm_err("Operation noop not set");
            valid = FALSE;
      }
      if(cib->cmds->ping == NULL) {
            crm_err("Operation ping not set");
            valid = FALSE;
      }
      if(cib->cmds->query == NULL) {
            crm_err("Operation query not set");
            valid = FALSE;
      }
      if(cib->cmds->query_from == NULL) {
            crm_err("Operation query_from not set");
            valid = FALSE;
      }
      if(cib->cmds->is_master == NULL) {
            crm_err("Operation is_master not set");
            valid = FALSE;
      }
      if(cib->cmds->set_master == NULL) {
            crm_err("Operation set_master not set");
            valid = FALSE;
      }
      if(cib->cmds->set_slave == NULL) {
            crm_err("Operation set_slave not set");
            valid = FALSE;
      }           
      if(cib->cmds->set_slave_all == NULL) {
            crm_err("Operation set_slave_all not set");
            valid = FALSE;
      }           
      if(cib->cmds->sync == NULL) {
            crm_err("Operation sync not set");
            valid = FALSE;
      }           if(cib->cmds->sync_from == NULL) {
            crm_err("Operation sync_from not set");
            valid = FALSE;
      }
      if(cib->cmds->bump_epoch == NULL) {
            crm_err("Operation bump_epoch not set");
            valid = FALSE;
      }           
      if(cib->cmds->create == NULL) {
            crm_err("Operation create not set");
            valid = FALSE;
      }
      if(cib->cmds->modify == NULL) {
            crm_err("Operation modify not set");
            valid = FALSE;
      }
      if(cib->cmds->replace == NULL) {
            crm_err("Operation replace not set");
            valid = FALSE;
      }
      if(cib->cmds->delete == NULL) {
            crm_err("Operation delete not set");
            valid = FALSE;
      }
      if(cib->cmds->erase == NULL) {
            crm_err("Operation erase not set");
            valid = FALSE;
      }
      if(cib->cmds->quit == NULL) {
            crm_err("Operation quit not set");
            valid = FALSE;
      }
      
      if(cib->cmds->msgready == NULL) {
            crm_err("Operation msgready not set");
            valid = FALSE;
      }
      if(cib->cmds->rcvmsg == NULL) {
            crm_err("Operation rcvmsg not set");
            valid = FALSE;
      }
      if(cib->cmds->dispatch == NULL) {
            crm_err("Operation dispatch not set");
            valid = FALSE;
      }

      return valid;
}

Generated by  Doxygen 1.6.0   Back to index