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

cluster_info.c

 /*
 * cluster_info.c
 * 
 * Author: Jia Ming Pan <jmltc@cn.ibm.com>
 * Copyright (c) 2005 International Business Machines
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; 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 <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <regex.h>
#include <hb_api.h>
#include <ha_msg.h>
#include <heartbeat.h>
#include <cmpidt.h>
#include "cluster_info.h"
#include "mof_map.h"


/*FIXME: remove hardcode path */
#define         CLIHB               "/etc/init.d/heartbeat"

#define     CONFIG_FILENAME   HA_HBCONF_DIR"/ha.cf"
#define         BACKUP_CONFIG_NAME  CONFIG_NAME".bak"
#define     BACKUP_KEYFILE          KEYFILE".bak"

#define         CONFIG_COMMENT \
                  "#This file was generated by provider\n\n\n"

#define CIM_RSCNAME_TABLE           "resource_name_table"
#define CIM_RSCTYPE_TABLE           "resource_type_table"
#define CIM_RSCOPS_TABLE            "resource_operations"
#define CIM_RSCATTRS_TABLE          "resource_attrubtes_table"
#define CIM_SUBRSC_NAME_TABLE       "subresource_name_table"
#define CIM_RESOURCE_TABLE          "resource_table"


typedef struct FunContext_s {
      int   func_id;    /* function name */
      const char * cmnd[8];   /* mgmt cmnds */
      const char * pattern_exp;
      int (*handle_func)(MClient*, void*, const struct FunContext_s*, void*);
} FunContext;

#define MSG_MAX_DEPTH 2
struct msg_pattern_t {
      char **     keys[MSG_MAX_DEPTH];    /* keys of nodes*/
      int   len[MSG_MAX_DEPTH];     /* keys length of each node */
      char *      tag[MSG_MAX_DEPTH];     /* description for each node */
      int   depth;                  /* message depth */
};
 
static int node_return(MClient*, void*, const FunContext*, void*);
static int list_return(MClient*, void*, const FunContext*, void*);
static int node_update(MClient*, void*, const FunContext*, void*);

static struct msg_pattern_t * msg_pattern_parse(const char *pattern);
static void msg_pattern_free(void*);
 
static int InsertOption(struct ha_msg*, const char *, const char * option);
static int StrIsEmpty(const char * str);

static int        cib_changed(void);

static int        cim_rsctype_s2tid(const char * rsctype);
static const char*      cim_rsctype_tid2s(int rsctype);

#define cim_rscname_table_keys() cim_dbkeys(CIM_RSCNAME_TABLE)
#define cim_rscname_table_add(rscid)      \
            cim_dbput(CIM_RSCNAME_TABLE, rscid, "disabled")
#define cim_rscname_table_del(rscid)      \
            cim_dbdel(CIM_RSCNAME_TABLE, rscid)
#define cim_rscname_table_get(rscid)      \
            cim_dbget(CIM_RSCNAME_TABLE, rscid)

#define cim_rsctype_table_add(rscid,type)       cim_dbput(CIM_RSCTYPE_TABLE, rscid, type) 
#define cim_rsctype_table_del( rscid)     cim_dbdel(CIM_RSCTYPE_TABLE, rscid)


#define STR_CONS_ORDER        "rsc_order"
#define STR_CONS_LOCATION     "rsc_location"
#define STR_CONS_COLOCATION   "rsc_colocation"


/*
description:
      return CRM configuration
format:
      MSG_CRM_CONFIG
return:
      MSG_OK transition_idle_timeout symmetric_cluster(True|False)
        stonith_enabled(True|False) no_quorum_policy(freeze|stop|ignore)
        default_resource_stickiness have_quorum(True|False)
or
      MSG_FAIL
*/

#define BEGIN_NODE      "{"
#define END_NODE  "}"

#define BEGIN_NODE_CHAR '{'
#define END_NODE_CHAR '}'

#define BEGIN_KEY       "("
#define END_KEY   ")"

#define BEGIN_KEY_CHAR  '('
#define END_KEY_CHAR    ')'

#define REPEATE   "?"
#define ANYTIMES  "*"

#define REPEATE_CHAR    '?'
#define ANYTIMES_CHAR   '*'

#define MAXTIMES  999


static const char crm_config [] = 
BEGIN_NODE "crm_config" 
      BEGIN_KEY "transition_idle_timeout" END_KEY 
      BEGIN_KEY "symmetric_cluster"             END_KEY
      BEGIN_KEY "stonith_enabled"         END_KEY
      BEGIN_KEY "no_quorum_policy"        END_KEY
      BEGIN_KEY "default_resource_stickiness" END_KEY
      BEGIN_KEY "have_quorum"             END_KEY
END_NODE;
      
/*
format:
      MSG_HB_CONFIG
return:
      MSG_OK apiauth auto_failback baud debug debugfile deadping deadtime
        hbversion hopfudge initdead keepalive logfacility logfile msgfmt
        nice_failback node normalpoll stonith udpport warntime watchdog
or
      MSG_FAIL
*/
static const char hb_config [] = 
BEGIN_NODE "hb_config"
      BEGIN_KEY "apiauth"           END_KEY     
      BEGIN_KEY "auto_failback"     END_KEY
      BEGIN_KEY "baud"        END_KEY
      BEGIN_KEY "debug"       END_KEY
      BEGIN_KEY "debugfile"         END_KEY
      BEGIN_KEY "deadping"          END_KEY
      BEGIN_KEY "deadtime"          END_KEY
      BEGIN_KEY "hbversion"         END_KEY
      BEGIN_KEY "hopfudge"          END_KEY
      BEGIN_KEY "initdead"          END_KEY
      BEGIN_KEY "keepalive"         END_KEY
      BEGIN_KEY "logfacility"       END_KEY
      BEGIN_KEY "logfile"           END_KEY
      BEGIN_KEY "msgfmt"            END_KEY
      BEGIN_KEY "nice_failback"     END_KEY
      BEGIN_KEY "node"        END_KEY
      BEGIN_KEY "normalpoll"        END_KEY
      BEGIN_KEY "stonith"           END_KEY
      BEGIN_KEY "updport"           END_KEY
      BEGIN_KEY "warntime"          END_KEY
      BEGIN_KEY "watchdog"          END_KEY
END_NODE;

/*
      MSG_NODE_CONFIG NODENAME
return:
      MSG_OK uname online(True|False) standbyTrue|False) unclean(True|False)
        shutdown(True|False) expected_up(True|False) is_dc(True|False)
        node_ping("ping|member")
*/
static const char node_info [] = 
BEGIN_NODE
      BEGIN_KEY "uname" END_KEY
      BEGIN_KEY "online"      END_KEY
      BEGIN_KEY "standby"     END_KEY
      BEGIN_KEY "unclean"     END_KEY
      BEGIN_KEY "shutdown"    END_KEY
      BEGIN_KEY "expected_up" END_KEY
      BEGIN_KEY "is_dc" END_KEY
      BEGIN_KEY "node_ping"   END_KEY
END_NODE;

/*
description:
        return the operations of a given resource
format:
        MSG_RSC_OPS resource
return:
        MSG_OK id1 name1 interval1 timeout1 id2 name2 interval2 timeout2
                ... idn namen intervaln timeoutn
or
        MSG_FAIL
*/

static const char operations [] = 
BEGIN_NODE "operations"
      BEGIN_NODE "op" REPEATE ANYTIMES
            BEGIN_KEY "id"          END_KEY     
            BEGIN_KEY "name"  END_KEY     
            BEGIN_KEY "interval"    END_KEY     
            BEGIN_KEY "timeout"     END_KEY
      END_NODE
END_NODE;

static const char attributes []= 
BEGIN_NODE CIM_MSG_INST_ATTR
      BEGIN_NODE CIM_MSG_ATTR REPEATE ANYTIMES
            BEGIN_KEY "id"          END_KEY     
            BEGIN_KEY "name"  END_KEY     
            BEGIN_KEY "value" END_KEY
      END_NODE
END_NODE;

static const char primitive []= 
BEGIN_NODE "primitive"
      BEGIN_KEY "id"          END_KEY
      BEGIN_KEY "class" END_KEY
      BEGIN_KEY "provider"    END_KEY
      BEGIN_KEY "type"  END_KEY
END_NODE;

static const char clone [] =
BEGIN_NODE "clone"
      BEGIN_KEY "id"                END_KEY
      BEGIN_KEY "clone_max"         END_KEY
      BEGIN_KEY "clone_node_max"    END_KEY
END_NODE;
 
static const char master []= 
BEGIN_NODE "master_slave"
      BEGIN_KEY "id"                END_KEY
      BEGIN_KEY "clone_max"         END_KEY
      BEGIN_KEY "clone_node_max"    END_KEY
      BEGIN_KEY "master_max"        END_KEY
      BEGIN_KEY "master_node_max"   END_KEY
END_NODE;
 

static const char order_constraint [] = 
BEGIN_NODE "rsc_order"
      BEGIN_KEY "id"          END_KEY
      BEGIN_KEY "from"  END_KEY
      BEGIN_KEY "type"  END_KEY
      BEGIN_KEY "to"          END_KEY
END_NODE;

static const char co_constraint[] = 
BEGIN_NODE "rsc_colocation"
      BEGIN_KEY "id"          END_KEY
      BEGIN_KEY "from"  END_KEY
      BEGIN_KEY "to"          END_KEY
      BEGIN_KEY "score" END_KEY
END_NODE;

/*
      rsc_location:
            MSG_OK id resource score 
                        expr_id1 attribute1 operation1 value1
                  expr_id2 attribute2 operation2 value2 ...
                  expr_idn attributen operationn valuen
*/

static const char location_constraint[] = 
BEGIN_NODE "rsc_location"
      BEGIN_KEY "id"          END_KEY
      BEGIN_KEY "resource"    END_KEY
      BEGIN_KEY "score" END_KEY
      BEGIN_NODE "rule" REPEATE ANYTIMES  /* constraint rule */
            BEGIN_KEY "id"          END_KEY
            BEGIN_KEY "attribute"   END_KEY
            BEGIN_KEY "operation"   END_KEY
            BEGIN_KEY "value" END_KEY
      END_NODE
END_NODE;


#define A_1(a)          {a,NULL}
#define A_2(a,b)  {a,b,NULL}
#define A_3(a,b,c)      {a,b,c,NULL}
#define A_4(a,b,c,d)    {a,b,c,d,NULL}
#define A_5(a,b,c,d,e)  {a,b,c,d,e,NULL}

static const FunContext query_ctx_table [] = {
      /* cluster */
      {GET_CRM_CONFIG,A_1(MSG_CRM_CONFIG), crm_config, node_return},
      {GET_HB_CONFIG, A_1(MSG_HB_CONFIG),  hb_config,  node_return},
      {GET_DC,    A_1(MSG_DC),           "{(dc)}",     node_return},
      {GET_NODE_INFO, A_1(MSG_NODE_CONFIG),node_info,  node_return},
      {GET_NODE_LIST, A_1(MSG_ALLNODES),   NULL,      list_return},

      /* resource */
      {GET_RSC_OPERATIONS, A_1(MSG_RSC_OPS), operations, node_return},
      {GET_RSC_ATTRIBUTES, A_1(MSG_RSC_PARAMS),attributes, node_return},
      {GET_RSC_TYPE,    A_1(MSG_RSC_TYPE),      "{(type)}", node_return},
      {GET_PRIMITIVE, A_1(MSG_RSC_ATTRS),       primitive, node_return},
      {GET_CLONE,       A_1(MSG_GET_CLONE),     clone, node_return},
      {GET_MASTER,      A_1(MSG_GET_MASTER),    master, node_return},

      {GET_SUB_RSC,     A_1(MSG_SUB_RSC),       NULL,       list_return},
      {GET_RSC_LIST,    A_1(MSG_ALL_RSC),       NULL,       list_return},
      {GET_RSC_HOST,    A_1(MSG_RSC_RUNNING_ON), "{(host)}", node_return},
      {GET_RSC_STATUS,A_1(MSG_RSC_STATUS),      "{(status)}", node_return},

      /* metadata */
      {GET_RSC_CLASSES, A_1(MSG_RSC_CLASSES), NULL, list_return},
      {GET_RSC_TYPES, A_1(MSG_RSC_TYPE),  NULL, list_return},
      {GET_RSC_PROVIDERS, A_1(MSG_RSC_PROVIDERS), NULL, list_return},

      /* constaint */
      {GET_ORDER_CONSTRAINT, A_2(MSG_GET_CONSTRAINT, STR_CONS_ORDER), 
            order_constraint, node_return 
      },
      {GET_LOCATION_CONSTRAINT, A_2(MSG_GET_CONSTRAINT, STR_CONS_LOCATION),
            location_constraint, node_return 
      },
      {GET_COLOCATION_CONSTRAINT, A_2(MSG_GET_CONSTRAINT,STR_CONS_COLOCATION),
            co_constraint, node_return
      },
      {GET_ORDER_CONS_LIST, A_2(MSG_GET_CONSTRAINTS, STR_CONS_ORDER),
            NULL, list_return
      },
      {GET_LOCATION_CONS_LIST, A_2(MSG_GET_CONSTRAINTS, STR_CONS_LOCATION),
            NULL, list_return
      },
      {GET_COLOCATION_CONS_LIST, A_2(MSG_GET_CONSTRAINTS,STR_CONS_COLOCATION),
            NULL, list_return
      }
};

/*
description:
        add a new resource
format:
        MSGA_DD_RSC rsc_id rsc_class rsc_type rsc_provider group("" for NONE)
                advance(""|"clone"|"master") advance_id clone_max
                clone_node_max master_max master_node_max
                param_id1 param_name1 param_value1
                param_id2 param_name2 param_value2
                ...
                param_idn param_namen param_valuen
*/
static const char resource[] =
BEGIN_NODE
      BEGIN_KEY "id"          END_KEY
      BEGIN_KEY "class" END_KEY
      BEGIN_KEY "type"  END_KEY
      BEGIN_KEY "provider"    END_KEY
      BEGIN_KEY "groupid"     END_KEY
      BEGIN_KEY "advance"     END_KEY
      BEGIN_KEY "advance_id"  END_KEY
      BEGIN_KEY "clone_max"   END_KEY
      BEGIN_KEY "clone_node_max"    END_KEY
      BEGIN_KEY "master_max"        END_KEY
      BEGIN_KEY "master_node_max"   END_KEY
      BEGIN_NODE CIM_MSG_ATTR REPEATE
            BEGIN_KEY "id"          END_KEY
            BEGIN_KEY "name"  END_KEY
            BEGIN_KEY "value" END_KEY
      END_NODE
END_NODE;

static const FunContext update_ctx_table [] = {
      {DEL_OPERATION,         A_1(MSG_DEL_RSC_OP),  NULL, NULL},
      {DEL_ATTRIBUTES,  A_1(MSG_DEL_RSC_PARAM),NULL, NULL},
      {DEL_RESOURCE,          A_1(MSG_DEL_RSC), NULL, NULL},
      {CLEANUP_RESOURCE,      A_1(MSG_CLEANUP_RSC), NULL, NULL},

      {CREATE_RSC_GROUP,      A_1(MSG_ADD_GRP),NULL, NULL},
      {CREATE_RESOURCE,       A_1(MSG_ADD_RSC), resource, node_update},
      {DEL_ORDER_CONSTRAINT,  
            A_2(MSG_DEL_CONSTRAINT, STR_CONS_ORDER), NULL, NULL
      },
      {DEL_LOCATION_CONSTRAINT,  
            A_2(MSG_DEL_CONSTRAINT, STR_CONS_LOCATION), NULL, NULL
      },
      {DEL_COLOCATION_CONSTRAINT,
            A_2(MSG_DEL_CONSTRAINT, STR_CONS_COLOCATION), NULL, NULL
      },

      {UPDATE_CLONE,          A_1(MSG_UPDATE_CLONE), clone, node_update},
      {UPDATE_MASTER,         A_1(MSG_UPDATE_MASTER),master, node_update},
      {UPDATE_OPERATIONS,     A_1(MSG_UP_RSC_OPS), operations, node_update},
      {UPDATE_ATTRIBUTES,     A_1(MSG_UP_RSC_PARAMS), 
            attributes, node_update 
      }, 
      {UPDATE_ORDER_CONSTRAINT, A_2(MSG_UP_CONSTRAINT, STR_CONS_ORDER),
            order_constraint, node_update
      },
      {UPDATE_LOCATION_CONSTRAINT, A_2(MSG_UP_CONSTRAINT, STR_CONS_LOCATION),
            location_constraint, node_update
      },
      {UPDATE_COLOCATION_CONSTRAINT, 
            A_2(MSG_UP_CONSTRAINT, STR_CONS_COLOCATION), 
            co_constraint, node_update
      },
};


static const FunContext * 
find_query_ctx(int func_id)
{
      int i, len;
      const FunContext * ctx_table;

      len = sizeof(query_ctx_table)/sizeof(FunContext);
      ctx_table = query_ctx_table;

      for (i = 0; i < len; i++){
            if (ctx_table[i].func_id == func_id){
                  return &ctx_table[i];
            }
      }
      return NULL;
}

static const FunContext * 
find_update_ctx(int func_id)
{
      int i, len;
      const FunContext * ctx_table;

      len = sizeof(update_ctx_table)/sizeof(FunContext);
      ctx_table = update_ctx_table;
      for (i = 0; i < len; i++){
            if (ctx_table[i].func_id == func_id){
                  return &ctx_table[i];
            }
      }
      return NULL;
}


struct ha_msg *
cim_query_dispatch(int func_id, const char * param, void * out)
{
      const FunContext * ctx;
      MClient * client;
      const char * arg;
      int i = 0;
      struct ha_msg * msg = NULL;
      char pathname[MAXLEN];

      cim_debug2(LOG_INFO, "cim_query_dispatch: %d, %s", 
                        func_id, param? param : "<null>");
      /* look for msg from cache first */
      snprintf(pathname, MAXLEN, 
                  "cache_1_%d_%s", func_id, param?param:"list");  
      if (!cib_changed() &&(msg = cim_disk2msg(pathname))){
            return msg;
      }

      /* not found: get msg from client */ 
      if ((ctx = find_query_ctx(func_id)) == NULL ) {
            cl_log(LOG_ERR, "%s: can't find function %d.", 
                  __FUNCTION__, func_id);
            return NULL;
      }

      if ((msg = ha_msg_new(1)) == NULL ) {
            cl_log(LOG_ERR, "%s: alloc msg failed.", __FUNCTION__);
            return NULL;
      }

      if ( (client = mclient_new()) == NULL ) {
            cl_log(LOG_ERR, "%s: can't create client", __FUNCTION__);   
            ha_msg_del(msg);
                  return NULL;      
        }
      
      while ((arg = ctx->cmnd[i++])){
            mclient_cmnd_append(client, arg);
      }

      if(param){
            mclient_cmnd_append(client, param);
      }

      /* execute the command and call callback to process the result */
      if( mclient_process(client) == MC_OK && ctx->handle_func ) {
            if( client->rlen == 0 ) {
                  cl_log(LOG_WARNING, "cim_qeury_dispatch:"
                        "client only return 'ok'");
                  goto clean_and_return_null;
            } else  if ( ctx->handle_func(client, msg, ctx, out)!= HA_OK) {
                  cl_log(LOG_ERR, 
                        "cim_query_dispatch: handle_func failed."); 
                  goto clean_and_return_null;
            }
      } else {
            goto clean_and_return_null;
      }
      mclient_free(client);

      /* cache it */    
      if ( msg ) {
            snprintf(pathname, MAXLEN, 
                  "cache_1_%d_%s", func_id, param?param:"list");
            cim_msg2disk(pathname, msg); 
      }
      return msg;

clean_and_return_null:
      mclient_free(client);
      ha_msg_del(msg);
      return NULL;
}

int
cim_update_dispatch(int func_id, const char* param, void* in, void* out)
{
      const FunContext * ctx;
      MClient * client;
      const char * arg;
      int ret = HA_OK;
      int i = 0;

      if ((ctx = find_update_ctx(func_id)) == NULL ) {
            cl_log(LOG_ERR, "cim_update: can't find function %d", func_id);
            return HA_FAIL; 
      }
      if ( (client = mclient_new()) == NULL ) {
            cl_log(LOG_ERR, "%s: can't create client", __FUNCTION__);   
                  return HA_FAIL;   
        }

      while ((arg = ctx->cmnd[i++])){
            mclient_cmnd_append(client, arg);
      }
      
      if(param){
            mclient_cmnd_append(client, param);
      }

      ret = HA_OK;
      if ( ctx->handle_func ){
            ret = ctx->handle_func(client, in, ctx, out); 
      }
      
      if ( ret == HA_OK ) {
            if ( mclient_process(client) != MC_OK ) {
                  cl_log(LOG_ERR, "%s: update failed.", __FUNCTION__);
                  ret = HA_FAIL;
            }
      }

      mclient_free(client);
      return ret;
}


static void 
msg_pattern_free(void * param)
{
      struct msg_pattern_t *pattern = NULL;
      int i;

      if ((pattern = (struct msg_pattern_t*)param) == NULL ) {
            return;
      }
      
      for(i = 0; i < MSG_MAX_DEPTH; i++) {
            int j;
            for(j=0; j<pattern->len[i]; j++) {
                  if ( pattern->keys[i][j] ) {
                        cim_free(pattern->keys[i][j]);
                  }
            }
            if ( pattern->tag[i] ) {
                  cim_free(pattern->tag[i]);
            }
      }
      cim_free(pattern);
}

static struct msg_pattern_t * 
msg_pattern_parse(const char *exp)
{
      const char *p, *q;
      char ch, tmp[128];
      int depth = -1;
      struct msg_pattern_t * pattern;

      cim_debug2(LOG_INFO, "msg_pattern_parse: pattern_exp: %s", exp);
      pattern = (struct msg_pattern_t *)
                  cim_malloc(sizeof(struct msg_pattern_t));
      if ( pattern ==  NULL ) {
            cl_log(LOG_ERR, "%s: alloc msg_pattern failed.", __FUNCTION__);
            return NULL;
      }
      memset(pattern, 0, sizeof(struct msg_pattern_t));
      pattern->depth = 0;

      p = exp;
      while((ch = *(p++) )) {
            if ( ch ==  BEGIN_NODE_CHAR ) {
                  depth++; 
                  if ( depth >= MSG_MAX_DEPTH) {
                        cl_log(LOG_ERR, "%s: depth >= %d.", 
                              __FUNCTION__, MSG_MAX_DEPTH); 
                        msg_pattern_free(pattern);
                        return NULL;
                  } 
                  q = p;
                  while(      *p != BEGIN_KEY_CHAR 
                       && *p != BEGIN_NODE_CHAR && *p != REPEATE_CHAR){
                        p++;
                  }
                  if ( p != q ) {
                        memcpy(tmp, q, p - q);
                        tmp[p - q] = EOS;
                        cim_debug2(LOG_INFO, "Got tag %d:%s", depth, tmp);    
                        pattern->tag[depth] = cim_strdup(tmp);
                  }
                  continue;
            } 
            if ( ch == REPEATE_CHAR ) {
                  if(*p == ANYTIMES_CHAR){ /* the only case by now */
                        p++;
                  }
            }
            if ( ch == BEGIN_KEY_CHAR) {  /* found one pattern*/
                  q = p;                        /* its start position */
                  while(*p != END_KEY_CHAR) {
                        p++;              /* its end position */
                  }
                  memcpy(tmp, q, p - q); 
                  tmp[p - q] = EOS;
                  pattern->len[depth] ++;
                  pattern->keys[depth] = cim_realloc(pattern->keys[depth], 
                                    pattern->len[depth]*sizeof(char*));
                  pattern->keys[depth][pattern->len[depth]-1] = cim_strdup(tmp); 
                  continue;
            }
      }
      pattern->depth = depth;
      return pattern;
}

static int      
list_return(MClient *client, void *data, const FunContext *ctx, void *out)
{
      struct ha_msg *msg;
      int i;
      if((msg = (struct ha_msg *)data) == NULL ) {
            cl_log(LOG_ERR, "%s: msg not alloced.", __FUNCTION__);
            return HA_FAIL;
      }

      for(i=0; i < client->rlen; i++){
            char *value = mclient_nth_value(client, i);
            if(value == NULL ) {
                  cl_log(LOG_ERR, "%s: failed to get at %d.",
                        __FUNCTION__, i);
                  return HA_FAIL;
            }
            cim_list_add(msg, value);
      }
      return HA_OK;
}

/* default callback for query_dispatch */
static int      
node_return(MClient *client, void *data, const FunContext *ctx, void *out)
{
      struct ha_msg *msg;
      int i, index, len;
      struct msg_pattern_t * pattern;

      if ((pattern = msg_pattern_parse(ctx->pattern_exp)) == NULL ) {
            cl_log(LOG_ERR, "node_return: pattern parse failed.");
            return HA_FAIL;
      }
      index = 0;
      if((msg = (struct ha_msg *)data) == NULL ) {
            cl_log(LOG_ERR, "%s: msg not alloced.", __FUNCTION__);
            return HA_FAIL;
      }

      /* parent node */
      for(i=0; i < pattern->len[0]; i++){
            char *value = mclient_nth_value(client, index ++);
            if(value == NULL ) {
                  cl_log(LOG_ERR, "%s: get pattern:%d failed.",
                        __FUNCTION__, i);
                  return HA_FAIL;
            }

            ha_msg_add(msg, pattern->keys[0][i], value);
      }

      if( pattern->tag[0] ) {
            ha_msg_add(msg, CIM_MSG_TAG, pattern->tag[0]);
      }
      /* no child node, done */
      if(pattern->len[1] == 0 || pattern->keys[1] == NULL) {
             goto done;
      }

      /* otherwise, create child node and add it to upper node */
      len = (client->rlen - index)/pattern->len[1];
      for(i=0; i < len; i++){
            char * value = NULL;
            struct ha_msg *child;
            int j = 0;

            child = ha_msg_new(pattern->len[1]);
            for (j=0; j < pattern->len[1]; j++) {
                  value = mclient_nth_value(client, index ++);
                  if(value == NULL ) {
                        ha_msg_del(child);
                        goto done;
                  }
                  ha_msg_add(child, pattern->keys[1][j], value);
            }
            if( pattern->tag[1] ) {
                  ha_msg_add(child, CIM_MSG_TAG, pattern->tag[1]);
            }
            cim_msg_add_child(msg, cl_get_string(child, "id"), child);
      }
done:
      msg_pattern_free(pattern);
      return HA_OK;
}

/* default callback for update_dispatch */
static int
node_update(MClient* client, void* data, const FunContext* ctx, void* out)
{
      int len, i;
      struct msg_pattern_t * pattern;
      struct ha_msg *msg = NULL;

      if ( (msg = (struct ha_msg *)data) == NULL ) {
            return HA_OK;
      }
      if ((pattern = msg_pattern_parse(ctx->pattern_exp)) == NULL ) {
            DEBUG_LEAVE();
            return HA_FAIL;
      }

      for(i=0; i < pattern->len[0]; i++){
            const char *value;
            value = cl_get_string(msg, pattern->keys[0][i]);
            mclient_cmnd_append(client, value? value : "");
      }

      /* no child msg, done */
      if(pattern->len[1] == 0 || pattern->keys[1] == NULL) {
            goto done;
      }

      /* otherwise, get child struct and append to client */
      len = cim_msg_children_count(msg);
      for(i=0; i < len; i++){
            const char *value = NULL;
            struct ha_msg *child;
            int j = 0;
            child = cim_msg_child_index(msg, i);
            cl_log(LOG_INFO, "%s: child: %s", __FUNCTION__, msg2string(child));
            for (j=0; j < pattern->len[1]; j++) {
                  value = cl_get_string(child, pattern->keys[1][j]);
                  cl_log(LOG_INFO, "%s: %s -- %s", __FUNCTION__, 
                                    pattern->keys[1][j], value);
                  mclient_cmnd_append(client, value?value : "");
            }
      }
done:
      msg_pattern_free(pattern);
      return HA_OK;
}

int
cim_get_hb_status ()
{
      char ** std_out = NULL;
      int     ret;
      int     status = HB_UNKNOWN;

      run_shell_cmnd(CLIHB" status", &ret, &std_out, NULL);
      if ( std_out == NULL || std_out[0] == NULL ) {
            return HB_UNKNOWN;
      } 

      if ( strstr ( std_out[0], "running") != NULL ) {
            status = HB_RUNNING;
      } else {
            status = HB_STOPED;
      }

      free_2d_zarray(std_out, cim_free);  
      return status;
}

int
cim_change_hb_state (int state)
{
      int status;
      const char * cmnd = NULL;
      switch(state) {
      case START_HB:
            cmnd = CLIHB" start"; break;
      case STOP_HB:
            cmnd = CLIHB" stop"; break;
      case RESTART_HB:
            cmnd = CLIHB" restart"; break;
      default:
            cl_log(LOG_ERR, "cim_change_hb_state: unknown opeation");
      }

      /* run it */
      system(cmnd);

      /* verify the status */
      status = cim_get_hb_status();
      if ( (state == START_HB || state == RESTART_HB) 
                        && (status == HB_RUNNING) ) {
            return HA_OK;
      } else if( state == STOP_HB && status == HB_STOPED ) {
            return HA_OK;
      }     
      return HA_FAIL;
}

static int
InsertOption(struct ha_msg *msg, const char * directive, const char * option)
{
      int i = 0;
      const struct map_t *map = cim_query_map(HA_CLUSTER);

      if (map== NULL ) {
            return HA_FAIL;
      }

      for (i=0; i<map->len; i++) {
            if ( strncmp(directive, map->entry[i].key, MAXLEN) == 0){
                  break;
            }
      }     

      if (map->entry[i].type == CMPI_charsA) {
            cl_msg_list_add_string(msg, directive, option);
      } else {
            cl_msg_modstring(msg, directive, option);
      }
      return HA_OK;
}

struct ha_msg*
cim_get_hacf_config ()
{
      FILE *       f = NULL;
      char       buf[MAXLEN];
      char *           cp;
      char       directive[MAXLEN];
      size_t           dirlength;
      char             option[MAXLEN];
      size_t           optionlength;
      struct ha_msg * info = NULL;
      const char * cfgfile = CONFIG_FILENAME;
      
      DEBUG_ENTER();
      if ((info = ha_msg_new(16)) == NULL ) {
            return NULL;
      }

      if ((f = fopen(cfgfile, "r")) == NULL ) {
            cl_log(LOG_ERR, "Failed to open %s", cfgfile);
            ha_msg_del(info);
            return NULL;
      }

      while (fgets(buf, MAXLEN, f) != NULL) {
            char *   bp = buf; 
            char     option_full[MAXLEN] = "";

            /* Skip over white space */
            bp += strspn(bp, WHITESPACE);
            /* Zap comments on the line */
            if ((cp = strchr(bp, COMMENTCHAR)) != NULL)  {
                  *cp = EOS;
            }
            /* Strip '\n' and '\r' chars */
            if ((cp = strpbrk(bp, CRLF)) != NULL) {
                  *cp = EOS;
            }

            /* Ignore blank (and comment) lines */
            if (*bp == EOS) {
                  continue;
            }

            /* Now we expect a directive name */
            dirlength = strcspn(bp, WHITESPACE);
            strncpy(directive, bp, dirlength);
            directive[dirlength] = EOS;

            while (*bp != EOS) {
                  optionlength = strcspn(bp, DELIMS);
                  strncpy(option, bp, optionlength);
                  option[optionlength] = EOS;
                  bp += optionlength;
                  
                  if ( strcmp(option, directive) != 0 ) {   
                        if ( strcmp(directive, "node") != 0) {
                              strcat(option_full, option);  
                              strcat(option_full, " ");
                        }else{
                              /* directive is "node" */
                              InsertOption(info, directive, option);
                        }
                  }
                  /* Skip over Delimiters */
                  bp += strspn(bp, DELIMS);
            }

            if ( strcmp(directive, "node") != 0 ) {
                  option_full[strlen(option_full) - 1] = EOS;
                  InsertOption(info, directive, option_full);
            }
      }
      fclose(f);  
      DEBUG_LEAVE();
      return info;
}

static int
StrIsEmpty(const char * str)
{
      char ch;
      while ( (ch = *(str++) )){
            if ( ch ==' ' || ch == '\t'){
                  continue;
            } else {
                  return 0;
            }
      }
      return 1;
}

#define SPACELEN 20
#define FILLSPACE(f, n) do{   \
      int i=0;          \
      for(i=0; i<(n); i++){   \
            fprintf(f, " ");\
      }                 \
} while(0);

static void
write_file_foreach(char *key, struct ha_msg *msg, void *user)
{
      FILE * f = (FILE *) user;
      int i, len;
      const struct map_t *map = cim_query_map(HA_CLUSTER);

      if (map == NULL ) {
            return;
      }

      for (i=0; i<map->len; i++) {
            if ( strncmp(key, map->entry[i].key, MAXLEN) == 0){
                  break;
            }
      }     
      if (map->entry[i].type == CMPI_charsA) {
            len = cl_msg_list_length(msg, key);
            for (i =0; i < len; i++) {
                  char * value;
                  value = (char*)cl_msg_list_nth_data(msg, key, i);
                  if ( !StrIsEmpty(value) ){
                        fprintf(f, "%s", (char *)key);
                        FILLSPACE(f, SPACELEN - strlen(key))
                        fprintf(f, "%s\n", value);
                  }
            }
      } else {
            const char *value = cl_get_string(msg, key);
            if (value && !StrIsEmpty(value)) {
                  fprintf(f, "%s", (char *)key);
                  FILLSPACE(f, SPACELEN - strlen(key))
                  fprintf(f, "%s\n", value);
            }
      }
}     

int
cim_update_hacf(struct ha_msg *info) 
{
        FILE * f = NULL;
      int i;
        /* backup old file */
        if ( rename(CONFIG_NAME, BACKUP_CONFIG_NAME) != 0 ) {
            cl_log(LOG_WARNING, "Backup ha.cf failed.");
        }

        if ( ( f = fopen(CONFIG_NAME, "w") ) == NULL ) {
            cl_log(LOG_ERR, "Could not open ha.cf");
                return HA_FAIL;
        }
      cl_log(LOG_INFO, "Begin dump config infomation.");
      fprintf(f, CONFIG_COMMENT);
      for(i=0; i<info->nfields; i++) {
            write_file_foreach(info->names[i], info, f);    
      }

        fclose(f);
        return HA_OK;
}

struct ha_msg *
cim_get_authkeys(void) 
{
        FILE *          f = NULL;
        struct stat     keyfilestat;
        int             authnum = -1;
        char            buf[MAXLEN];
        char            key[MAXLEN];
        char            method[MAXLEN];
        int             i;
        int             src;
      struct ha_msg * authinfo; 

      if ( (authinfo = ha_msg_new(2)) == NULL ) {
            cl_log(LOG_ERR, "Alloc table failed.");         
                return NULL;
        }

        if ((f = fopen(KEYFILE, "r")) == NULL) {
                cl_log(LOG_ERR, "Cannot open keyfile [%s].  Stop."
                ,       KEYFILE);
                ha_msg_del(authinfo);
            return NULL;
        } 

        if (fstat(fileno(f), &keyfilestat) < 0
            ||  keyfilestat.st_mode & (S_IROTH | S_IRGRP)) {
                cl_log(LOG_ERR, "Bad permissions on keyfile"
                       " [%s], 600 recommended.", KEYFILE);
                fclose(f);
                ha_msg_del(authinfo);
                return NULL;
        }

        while(fgets(buf, MAXLEN, f) != NULL) {
                char *  bp = buf;

                bp += strspn(bp, WHITESPACE);
                if (*bp == COMMENTCHAR || *bp == EOS) {
                        continue;
                }
                if (*bp == 'a') {
                        if ((src=sscanf(bp, "auth %d", &authnum)) != 1) {
                                cl_log(LOG_ERR
                                ,       "Invalid auth line [%s] in " KEYFILE
                                ,        buf);
                        }
                        /* Parsing of this line now complete */
                        continue;
                }
                key[0] = EOS;
                if ((src=sscanf(bp, "%d%s%s", &i, method, key)) >= 2) {
                        if ((i < 0) || (i >= MAXAUTH)) {
                                ha_log(LOG_ERR, "Invalid authnum [%d] in "
                                       KEYFILE, i);
                                continue;
                        }
                        
                        /* found */
                        if ( i == authnum ) { break; }
                } else if (*bp != EOS) {
                        ha_log(LOG_ERR, "Auth line [%s] is invalid."
                        ,       buf);
                }
        }
        
        /* add auth method, key to dict */
        ha_msg_add(authinfo, "authmethod", method);
        ha_msg_add(authinfo, "authkey", key);

        fclose(f);
        return authinfo;
}

int 
cim_update_authkeys(struct ha_msg *msg)
{
        FILE * f;
        const char *method, *key;        

        /* backup old file */ 
        if ( rename(KEYFILE, BACKUP_KEYFILE) != 0 ) {
            cl_log(LOG_WARNING, "Backup authkeys failed.");
        }

        if ( ( f = fopen(KEYFILE, "w") ) == NULL ) {
            cl_log(LOG_ERR, "Can not open authkeys.");
                return HA_FAIL;
        }

        /* set mode to 0600 */
        if ( chmod(KEYFILE, 0600) != 0 ) {
            cl_log(LOG_ERR, "Cat not chmod authkeys to 0600.");
                fclose(f);
                return HA_FAIL;
        }

      /* get new value */
        method = cl_get_string(msg, "authmethod");
        key    = cl_get_string(msg, "authkey");

      if (method == NULL || key == NULL ) {
            cl_log(LOG_ERR, "FATAL: method or key is NULL.");
            fclose(f);
            return HA_FAIL;
      }

        /* write file */
      fprintf(f, CONFIG_COMMENT);
        fprintf(f, "auth %u\n", 1);
        fprintf(f, "%u %s %s\n", 1, method, key);

        fclose(f);
        return HA_OK;
}

struct ha_msg *
cim_get_software_identity(void)
{
      char ** out = NULL;
      int     ret;
      DEBUG_ENTER();
      run_shell_cmnd(HA_LIBDIR"/heartbeat/heartbeat -V", &ret, &out, NULL);

      if ( out ) {
            struct ha_msg * msg;
            char *            hbversion;

            if ( (msg = ha_msg_new(1)) == NULL){
                  free_2d_zarray(out, cim_free);
                  return NULL;
            }
            hbversion = out[0];
            if ( hbversion ){
                  hbversion[strlen(hbversion)-1]=EOS;
                  ha_msg_add(msg, "hbversion", hbversion);
                  DEBUG_LEAVE();
                  return msg;
            } else {
                  ha_msg_del(msg);
            }
      }

      DEBUG_LEAVE();
      return NULL;
}

static int
cib_changed()
{
      /*FIXME: implementation required */
      return 1;
}


/****************************************************************
 * resource list
 ***************************************************************/
int
cim_rsc_is_in_cib(const char *rscid)
{
      char * value;
      /*
      struct ha_msg *list=NULL;
      
      if ((list = cim_query_dispatch(GET_RSC_LIST, NULL, NULL))){
            if ( cim_list_find(list, rscid)) {
                  ha_msg_del(list);
                  cim_debug2(LOG_INFO, "%s: %s is in CIB, "
                        "reason: it's in the CIB resource list.", 
                        __FUNCTION__, rscid);
                  return TRUE;
            }     
      }
      */
      if ((value = cim_rscname_table_get(rscid)) == NULL ) {
            return TRUE;
      } else { 
            cim_free(value);
            return FALSE;
      }
}


      

struct ha_msg*    
cim_get_all_rsc_list(void)
{
      struct ha_msg *list1 = NULL, *list2;
      int len2;
      
      /* list in CIB */
      list1 = cim_query_dispatch(GET_RSC_LIST, NULL, NULL);
      if (list1 == NULL && (list1 = ha_msg_new(1)) == NULL ) {
            cl_log(LOG_ERR, "cim_get_all_rsc_list: can't get list1.");
            return NULL;
      }

      /* merge with diabled list */
      if ((list2 = cim_rscname_table_keys())) {
            int i;
            len2 = cim_list_length(list2);
            cim_debug2(LOG_INFO, "len2 = %d", len2);
            for(i=0; i<len2; i++) {
                  char *rsc = cim_list_index(list2, i);
                  cim_debug2(LOG_INFO, "rsc at %d is %s", i , rsc);
                  if (rsc) {
                        cim_list_add(list1, rsc);     
                  }
            }
      }
      return list1;
}

struct ha_msg*    
cim_traverse_allrsc(struct ha_msg* list)
{
      struct ha_msg * newlist;
      int i, len;

      len = cim_list_length(list);
      if ((newlist = ha_msg_new(len))== NULL ) {
            return NULL;
      }
      cim_debug2(LOG_INFO, "length of list: %d", len);
      for (i = 0; i < len; i++) {
            char * rscid;
            struct ha_msg *sublist, *next;
            int j, nextlen;

            rscid = cim_list_index(list, i);
            if ( rscid == NULL ) {
                  continue;
            }
            cim_list_add(newlist, rscid);
            if (cim_get_rsctype(rscid) == TID_RES_PRIMITIVE) {
                  continue;
            }
      
            cim_debug2(LOG_INFO, 
                  "rsc %s is not primitive, get its subrsc.", rscid);
            if ((sublist = cim_get_subrsc_list(rscid)) == NULL ) {
                  continue;
            }
            
            next = cim_traverse_allrsc(sublist);
            nextlen = cim_list_length(next);
            for (j = 0; j < nextlen; j++) {
                  char * nextrscid;
                  nextrscid = cim_list_index(next, j);
                  if ( nextrscid == NULL ) {
                        continue;
                  }
                  cim_list_add(newlist, nextrscid);
            }
            ha_msg_del(sublist);
      }
      return newlist;
}

/*********************************************************
 * resource type 
 ********************************************************/


static int
cim_rsctype_s2tid(const char *type)
{
      int tid = TID_UNKNOWN;
      if ( type == NULL ) {
            return TID_UNKNOWN;
      }
      if(strncmp(type, S_RES_PRIMITIVE, MAXLEN)== 0){
            tid = TID_RES_PRIMITIVE;
      } else if (strncmp(type, S_RES_GROUP, MAXLEN) == 0) {
            tid = TID_RES_GROUP;
      } else if (strncmp(type, S_RES_CLONE, MAXLEN) == 0){
            tid = TID_RES_CLONE;
      } else if (strncmp(type, S_RES_MASTER, MAXLEN) == 0){
            tid = TID_RES_MASTER;
      }

      return tid;
}
static const char*
cim_rsctype_tid2s(int type)
{
      const char * rsctype = NULL;
      if (type == TID_RES_PRIMITIVE ) {
            rsctype = S_RES_PRIMITIVE;
      } else if ( type == TID_RES_CLONE ) {
            rsctype = S_RES_CLONE;
      } else if ( type == TID_RES_MASTER) {
            rsctype = S_RES_MASTER;
      } else if ( type == TID_RES_GROUP ) {
            rsctype = S_RES_GROUP;
      } else {
            rsctype = S_RES_UNKNOWN;
      }
      return rsctype;
}

int
cim_get_rsctype(const char * rscid)
{
      char * type = NULL;
      int tid = TID_UNKNOWN;
      
      DEBUG_ENTER();
      if ( RESOURCE_DISABLED(rscid)) {
            type = cim_dbget(CIM_RSCTYPE_TABLE, rscid);
      } else {
            struct ha_msg *msg;
            msg = cim_query_dispatch(GET_RSC_TYPE, rscid, NULL);
            if ( msg ) {
                  type = cim_strdup(cl_get_string(msg, "type"));
                  ha_msg_del(msg);
            }
      } 
      tid = cim_rsctype_s2tid(type);
      if (type) {
            cim_free(type);
      }

      DEBUG_LEAVE();
      return tid;
}

/*******************************************************
 * resource operations 
 ******************************************************/
struct ha_msg *
cim_get_rscops(const char *rscid)
{
      struct ha_msg *ops = NULL;
      ops = ( RESOURCE_ENABLED(rscid)) ?/* from CIB */
            cim_query_dispatch(GET_RSC_OPERATIONS, rscid, NULL) :
            cim_dbget_msg(CIM_RSCOPS_TABLE, rscid);
      return ops;
}

int
cim_del_rscop(const char *rscid, const char *opid)
{
      struct ha_msg *ops;
      int ret;

      if ( RESOURCE_ENABLED(rscid) ) {    /* update to CIB */
            ret = cim_update_dispatch(DEL_OPERATION, opid, NULL, NULL);
            return ret;
      } ;

      if ((ops = cim_get_rscops(rscid)) == NULL ) {
            cl_log(LOG_WARNING, "cim_del_rscop: ops not exists");
            return HA_OK;
      }

      cim_msg_remove_child(ops, opid);
      ret = cim_dbput_msg(CIM_RSCOPS_TABLE, rscid, ops);
      return ret;
}

int
cim_add_rscop(const char *rscid, struct ha_msg *op)
{
      struct ha_msg *ops;
      int ret;

      if ((ops = cim_get_rscops(rscid)) == NULL) {
            if ((ops = ha_msg_new(1)) == NULL ) {
                        return HA_FAIL;
            }
      }

      cim_msg_add_child(ops, cl_get_string(op, "id"), op);
      ret = ( RESOURCE_ENABLED(rscid) ) ? /* update into CIB */
            cim_update_dispatch(UPDATE_OPERATIONS, rscid, ops, NULL) :
            cim_dbput_msg(CIM_RSCOPS_TABLE, rscid, ops);
      ha_msg_del(ops);
      return ret;
}


int
cim_update_rscop(const char* rscid, const char* id, struct ha_msg* op)
{
      struct ha_msg *ops;
      int ret;

      if ((ops = cim_get_rscops(rscid)) == NULL) {
            cl_log(LOG_ERR, "%s: ops of %s not found.", 
                        __FUNCTION__, rscid);
            return HA_FAIL;
      }

      /* update op */
      cim_msg_remove_child(ops, id);
      cim_msg_add_child(ops, id, op);
      
      ret = RESOURCE_ENABLED(rscid) ?     /* update into CIB */
            cim_update_dispatch(UPDATE_OPERATIONS, rscid, ops, NULL) :
            cim_dbput_msg(CIM_RSCOPS_TABLE, rscid, ops);
      ha_msg_del(ops);
      return ret;
}

/*********************************************************
 * resource attributes 
 ********************************************************/
int
cim_rscattrs_del(const char *rscid)
{
      return cim_dbdel(CIM_RSCATTRS_TABLE, rscid);
}

struct ha_msg *
cim_rscattrs_get(const char *rscid)
{
      struct ha_msg *attrs = NULL;
      attrs = (RESOURCE_ENABLED(rscid)) ?
                  cim_query_dispatch(GET_RSC_ATTRIBUTES, rscid, NULL) :
                  cim_dbget_msg(CIM_RSCATTRS_TABLE, rscid);
      return attrs;
}

int
cim_update_attrnvpair(const char*rscid, const char*nvid, struct ha_msg *nvpair)
{
      int ret;
      struct ha_msg *attrs = cim_rscattrs_get(rscid);

      if ( attrs == NULL ) {
            if ((attrs = ha_msg_new(1)) == NULL ) {
                  cl_log(LOG_ERR, "%s: attributes not found for %s",
                              __FUNCTION__, rscid);
                  return HA_FAIL;
            }
      }

      cl_msg_modstruct(attrs, nvid, nvpair);
      ret =  RESOURCE_DISABLED(rscid)?
            cim_dbput_msg(CIM_RSCATTRS_TABLE, rscid, attrs) :
            cim_update_dispatch(UPDATE_ATTRIBUTES, rscid, attrs, NULL);
      ha_msg_del(attrs);
      return ret;
}

int
cim_remove_attrnvpair(const char* rscid, const char* nvid)
{
      int ret;
      struct ha_msg *attrs = cim_rscattrs_get(rscid);
      if ( attrs == NULL ) {
            return HA_FAIL;
      }

      cl_msg_remove(attrs, nvid);
      ret =  RESOURCE_DISABLED(rscid)? cim_dbdel(CIM_RSCATTRS_TABLE, rscid) :
                  cim_update_dispatch(DEL_ATTRIBUTES, nvid, NULL, NULL);
      ha_msg_del(attrs);
      return ret;
}

/**********************************************************
 * resource
 *********************************************************/

static void
AddPrimitiveForUpdate(struct ha_msg *msg, struct ha_msg* rsc, struct ha_msg *attrs)
{
      int len, i;
      const char * id;

      cim_debug2(LOG_INFO, "In AddPrimitiveForUpdate.");
      id = cl_get_string(rsc, "id");
      ha_msg_add(msg, "id", cl_get_string(rsc, "id"));
      ha_msg_add(msg, "provider", cl_get_string(rsc, "provider"));
      ha_msg_add(msg, "class", cl_get_string(rsc, "class"));
      ha_msg_add(msg, "type", cl_get_string(rsc, "type"));
      
      attrs = attrs? attrs : cim_rscattrs_get(id);
      if ( attrs == NULL ) {
            cl_log(LOG_ERR, "%s: attributes of %s not found.", __FUNCTION__, id);
            return ;
      }
      len = cim_msg_children_count(attrs);
      for (i = 0; i < len; i++) {
            struct ha_msg *nvpair = NULL;
            nvpair = cim_msg_child_index(attrs, i);
            cim_msg_add_child(msg, cl_get_string(nvpair, "id"), nvpair);
      }
}

int
cim_rsc_submit(const char *rscid)
{
      int type, ret;
      struct ha_msg *rsc, *sublist, *msg;

      if ( RESOURCE_ENABLED(rscid) ){
            cl_log(LOG_ERR, "%s: resource %s is already enabled. ",
                  __FUNCTION__, rscid);
            return HA_FAIL;
      }
      type = cim_get_rsctype(rscid);
      if ( (rsc = cim_find_rsc(type, rscid)) == NULL ) {
            cl_log(LOG_ERR, "%s: resource %s not found.",
                  __FUNCTION__, rscid);
            return HA_FAIL;
      }

      if ( (msg = ha_msg_new(16)) == NULL ) {
            cl_log(LOG_ERR, "%s: msg alloc failed.", __FUNCTION__);
            return HA_FAIL;
      }

      if ( type == TID_RES_GROUP ) {
            struct ha_msg *primitive;
            int i, count;

            if ((sublist = cim_get_subrsc_list(rscid)) == NULL ) {
                  cl_log(LOG_ERR, 
                        "%s: no primitive resource for %s found."
                        "imcomplete resource.", __FUNCTION__, rscid);
                  ha_msg_del(rsc);
                  ha_msg_del(msg);
                  return HA_FAIL;
            }

            /* first create a group in CIB */
            ret = cim_update_dispatch(CREATE_RSC_GROUP, rscid, NULL, NULL); 
            if ( ret == HA_FAIL) {
                  ha_msg_del(rsc);
                  ha_msg_del(msg);
                  return HA_FAIL;
            }

            cim_rscdb_cleanup(TID_RES_GROUP, rscid);

            /* then add sub primitive resources into group */
            count = cim_list_length(sublist);
            for (i = 0; i < count; i++) {
                  char *subrscid = cim_list_index(sublist, i);
                  if (subrscid == NULL ) {
                        continue;
                  }
                  primitive = cim_find_rsc(TID_RES_PRIMITIVE, subrscid);
                  cim_debug_msg(primitive, "%s: primitve:%s to be added to group %s",
                              __FUNCTION__, subrscid, rscid);
                  cim_debug2(LOG_INFO, "%s: ready to add", __FUNCTION__);
                  ret = cim_add_subrsc(rsc, primitive);
            }

            ha_msg_del(sublist);
      } else if ( type == TID_RES_CLONE || type == TID_RES_MASTER) {
            struct ha_msg *primitive;
            if ((sublist = cim_get_subrsc_list(rscid)) == NULL ) {
                  cl_log(LOG_ERR, 
                        "%s: no primitive resource for %s found."
                        "imcomplete resource.", __FUNCTION__, rscid);
                  ha_msg_del(rsc);
                  ha_msg_del(msg);
                  return HA_FAIL;
            }
            primitive = cim_find_rsc(TID_RES_PRIMITIVE, 
                                    cim_list_index(sublist, 0)); 
            if ( primitive == NULL ) {
                  cl_log(LOG_ERR, "%s: can't find primitive %s.",
                         __FUNCTION__, cim_list_index(sublist, 0));
                  ha_msg_del(rsc);
                  ha_msg_del(msg);
                  return HA_FAIL;
            }
            AddPrimitiveForUpdate(msg, primitive, NULL);
            ha_msg_add(msg, "groupid", "");     
            ha_msg_add(msg, "advance", cim_rsctype_tid2s(type)); 
            ha_msg_add(msg, "advance_id", rscid);
            ha_msg_add(msg, "clone_max", 
                        cl_get_string(rsc, "clone_max"));
            ha_msg_add(msg, "clone_node_max", 
                        cl_get_string(rsc, "clone_node_max"));
            ha_msg_add(msg, "master_max", ( type == TID_RES_MASTER ) ? 
                        cl_get_string(rsc, "master_max") : "");
            ha_msg_add(msg, "master_node_max", ( type == TID_RES_MASTER ) ? 
                        cl_get_string(rsc, "master_node_max") : "");

            ret = cim_update_dispatch(CREATE_RESOURCE, NULL, msg, NULL);

            cim_rscdb_cleanup(TID_RES_PRIMITIVE, 
                        cl_get_string(primitive, "id"));
            ha_msg_del(sublist);
      } else if ( type == TID_RES_PRIMITIVE ) {
            AddPrimitiveForUpdate(msg, rsc, NULL);
            ha_msg_add(msg, "groupid", "");     
            ha_msg_add(msg, "advance", "");
            ha_msg_add(msg, "advance_id", "");
            ha_msg_add(msg, "clone_max", "");
            ha_msg_add(msg, "clone_node_max", "");
            ha_msg_add(msg, "master_max", "");
            ha_msg_add(msg, "master_node_max", "");
            ret = cim_update_dispatch(CREATE_RESOURCE, NULL, msg, NULL);

            cim_rscdb_cleanup(type, rscid);
      }

      ha_msg_del(rsc);
      ha_msg_del(msg);
      return HA_OK;
}

int
cim_rscdb_cleanup(int type, const char * rscid)
{
      if ( cim_rscname_table_del(rscid) != HA_OK ) {
            cl_log(LOG_ERR, "%s: FATAL: can't update %s.",
                  __FUNCTION__, CIM_RSCNAME_TABLE);
            return HA_FAIL;
      }

      if ( type == TID_RES_PRIMITIVE && 
                  cim_dbdel(CIM_RESOURCE_TABLE, rscid) != HA_OK ) {
            cl_log(LOG_WARNING, "%s: FATAL: can't update %s.",
                  __FUNCTION__, CIM_RESOURCE_TABLE);
      }
      if ( type == TID_RES_PRIMITIVE && 
                  cim_rscattrs_del(rscid) != HA_OK ) {
            cl_log(LOG_WARNING, "%s: FATAL: can't erase the attributes.",
                  __FUNCTION__);
      }
      if ( cim_rsctype_table_del(rscid) != HA_OK ) {
            cl_log(LOG_WARNING, "%s: FATAL: can't remove from type list..",
                  __FUNCTION__);
      }

      if ( type == TID_RES_GROUP ) {
            cim_debug2(LOG_INFO, "%s: %s is group, clean up subrsc_table.",
                  __FUNCTION__, rscid);
            cim_dbdel(CIM_SUBRSC_NAME_TABLE, rscid);  
      }

      return HA_OK;
}

int
cim_rscdb_store(int type, const char *rscid, struct ha_msg *rsc)
{
      const char *rsctype = NULL;
      if ( cim_dbput_msg(CIM_RESOURCE_TABLE, rscid, rsc) != HA_OK ) {
            cl_log(LOG_ERR, "%s: cant't write resource image.", 
                  __FUNCTION__);
            return HA_FAIL;
      }

      /* update rsctype table */
      rsctype = cim_rsctype_tid2s(type);
      if (cim_rsctype_table_add(rscid, rsctype) != HA_OK ) {
            cl_log(LOG_ERR, "%s: cant't update resource type table.", 
                  __FUNCTION__);
            cim_dbdel(CIM_RESOURCE_TABLE, rscid);
            return HA_FAIL;
      }

      /* update rsc list */
      if ( cim_rscname_table_add(rscid) != HA_OK) {
            cl_log(LOG_ERR, "%s: cant't update %s.", 
                  __FUNCTION__, CIM_RSCNAME_TABLE);
            cim_dbdel(CIM_RESOURCE_TABLE, rscid);
            cim_rsctype_table_del(rscid);
            return HA_FAIL;
      }

      return HA_OK;
}

struct ha_msg*    
cim_find_rsc(int type, const char * rscid)
{
      struct ha_msg *rsc = NULL/*, * attributes*/;

      if (RESOURCE_DISABLED(rscid)) {
            rsc = cim_dbget_msg(CIM_RESOURCE_TABLE, rscid);
            goto done;
      }
 
      switch(type){
      case TID_RES_PRIMITIVE:
            rsc = cim_query_dispatch(GET_PRIMITIVE, rscid, NULL);
            break;
      case TID_RES_MASTER:
            rsc = cim_query_dispatch(GET_MASTER, rscid, NULL);
            break;
      case TID_RES_CLONE:
            rsc = cim_query_dispatch(GET_CLONE, rscid, NULL);
            break;
      case TID_RES_GROUP:
            if ((rsc = ha_msg_new(1))){
                  ha_msg_add(rsc, "id", rscid);
            }
            break;
      default:
            break;
      }
done:
      return rsc;
}

int
cim_update_rsc(int type, const char *rscid, struct ha_msg *resource)
{
      int ret = HA_FAIL;

      if ( RESOURCE_ENABLED(rscid) ) {    /* in-cib resource */
            if ( type == TID_RES_CLONE ) {
                  ret = cim_update_dispatch(UPDATE_CLONE, 
                                    NULL, resource, NULL); 
            } else if (type == TID_RES_MASTER ) {
                  ret = cim_update_dispatch(UPDATE_MASTER, 
                                    NULL, resource, NULL);
            } else if (type == TID_RES_PRIMITIVE ) {
                  ret = HA_OK;
            }
      } else {    /* update disk image */
            ret = cim_dbput_msg(CIM_RESOURCE_TABLE, rscid, resource);
      }
      return ret;
}

int
cim_remove_rsc(const char * rscid)
{
      int ret;
      if ( RESOURCE_ENABLED(rscid)) {
            int rsctype = cim_get_rsctype(rscid);
            /* if it's a resource container, remove its sub resource first */
            if ( rsctype == TID_RES_GROUP || rsctype == TID_RES_CLONE ||
                        rsctype == TID_RES_MASTER) {
                  struct ha_msg *sublist = cim_get_subrsc_list(rscid);
                  if (sublist) {
                        int i , len = cim_list_length(sublist);
                        for( i =0; i < len; i++) {
                              char *subrscid = NULL;
                              subrscid = cim_list_index(sublist,i);
                              cim_remove_rsc(subrscid);
                        }
                  }
            }

            if ( rsctype == TID_RES_PRIMITIVE ) {
                  struct ha_msg * msg;
                  msg = cim_query_dispatch(GET_RSC_HOST, rscid, NULL);
                  if (msg) {
                        const char * host = cl_get_string(msg, "host");
                        char * param=mclient_makeup_param(host, rscid);
                        ret = cim_update_dispatch(CLEANUP_RESOURCE, 
                                    param, NULL, NULL);
                  }
                  ha_msg_del(msg);
            }

            ret = cim_update_dispatch(DEL_RESOURCE, rscid, NULL, NULL);
      } else {
            cim_rscdb_cleanup(cim_get_rsctype(rscid), rscid);
            ret = HA_OK;
      }
      return ret;
}

/*****************************************************************
 * sub resources 
 ****************************************************************/
struct ha_msg *
cim_get_subrsc_list(const char *rscid)
{
      struct ha_msg * sublist;

      sublist =  ( RESOURCE_ENABLED(rscid) )?
            cim_query_dispatch(GET_SUB_RSC, rscid, NULL) :
            cim_dbget_msg(CIM_SUBRSC_NAME_TABLE, rscid);
      return sublist;
}

int
cim_add_subrsc(struct ha_msg *rsc, struct ha_msg *subrsc)
{
      struct ha_msg * sublist;
      const char * rscid, *subrscid;
      int ret, type;


      rscid    = cl_get_string(rsc, "id");
      subrscid = cl_get_string(subrsc, "id");
      cim_debug2(LOG_INFO, "%s: adding subrsc: %s to %s.",
                  __FUNCTION__, rscid, subrscid);

      if ( RESOURCE_ENABLED(subrscid) ){
            cl_log(LOG_ERR, "%s: resource %s is already in CIB.",
                  __FUNCTION__, subrscid);
            return HA_FAIL;
      }

      if ( RESOURCE_DISABLED(rscid) ) {   /* write to disk */
            sublist = cim_dbget_msg(CIM_SUBRSC_NAME_TABLE, rscid);
            if (sublist == NULL ) { /* not exist yet */
                  if ((sublist = ha_msg_new(1)) == NULL ) {
                        cl_log(LOG_ERR, "cim_add_subrsc: "
                                    "alloc sublist failed.");
                        return HA_FAIL;
                  }
            }
            /* add subrscid and write back to disk */
            cim_list_add(sublist, subrscid);
            ret = cim_dbput_msg(CIM_SUBRSC_NAME_TABLE, rscid, sublist);
      } else {          /* update to CIB */
            struct ha_msg * msg;

            type = cim_get_rsctype(rscid);
            if ( type != TID_RES_GROUP ){  
                  cl_log(LOG_ERR, "%s: resource %s can't be added to %s,"
                              "%s is not a resource group.",
                        __FUNCTION__, subrscid, rscid, rscid);
                  return HA_FAIL;
            }
            
            /* create new resource */
            if ( (msg = ha_msg_new(16)) == NULL ) {
                  cl_log(LOG_ERR, "cim_add_subrsc: copy msg failed.");
                  return HA_FAIL;
            }
            cim_debug2(LOG_INFO, "%s: call AddPrimitiveForUpdate", __FUNCTION__);
            AddPrimitiveForUpdate(msg, subrsc, NULL);
            if ( type == TID_RES_GROUP ) {
                  ha_msg_add(msg, "groupid", rscid);  
                  ha_msg_add(msg, "advance", "");
                  ha_msg_add(msg, "advance_id", "");
                  ha_msg_add(msg, "clone_max", "");
                  ha_msg_add(msg, "clone_node_max", "");
                  ha_msg_add(msg, "master_max", "");
                  ha_msg_add(msg, "master_node_max", "");
            }

            cim_debug2(LOG_INFO, "%s: create resource.", __FUNCTION__);
            ret = cim_update_dispatch(CREATE_RESOURCE, NULL, msg, NULL);
      
            cim_rscdb_cleanup(TID_RES_PRIMITIVE, subrscid);
            ha_msg_del(msg);
      }
      return ret;
}



Generated by  Doxygen 1.6.0   Back to index