/*
   Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.

   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; version 2 of the License.

   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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
*/

#include <NDBT.hpp>
#include <NDBT_Test.hpp>
#include "NdbMgmd.hpp"
#include <mgmapi.h>
#include <mgmapi_debug.h>
#include <InputStream.hpp>
#include <signaldata/EventReport.hpp>
#include <NdbRestarter.hpp>
#include <random.h>

/*
  Tests that only need the mgmd(s) started

  Start ndb_mgmd and set NDB_CONNECTSTRING pointing
  to that/those ndb_mgmd(s), then run testMgm
 */


int runTestApiSession(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;
  Uint64 session_id= 0;

  NdbMgmHandle h;
  h= ndb_mgm_create_handle();
  ndb_mgm_set_connectstring(h, mgmd.getConnectString());
  ndb_mgm_connect(h,0,0,0);
#ifdef NDB_WIN
  SOCKET s = ndb_mgm_get_fd(h);
#else
  int s= ndb_mgm_get_fd(h);
#endif
  session_id= ndb_mgm_get_session_id(h);
  ndbout << "MGM Session id: " << session_id << endl;
  send(s,"get",3,0);
  ndb_mgm_disconnect(h);
  ndb_mgm_destroy_handle(&h);

  struct NdbMgmSession sess;
  int slen= sizeof(struct NdbMgmSession);

  h= ndb_mgm_create_handle();
  ndb_mgm_set_connectstring(h, mgmd.getConnectString());
  ndb_mgm_connect(h,0,0,0);

  NdbSleep_SecSleep(1);

  if(ndb_mgm_get_session(h,session_id,&sess,&slen))
  {
    ndbout << "Failed, session still exists" << endl;
    ndb_mgm_disconnect(h);
    ndb_mgm_destroy_handle(&h);
    return NDBT_FAILED;
  }
  else
  {
    ndbout << "SUCCESS: session is gone" << endl;
    ndb_mgm_disconnect(h);
    ndb_mgm_destroy_handle(&h);
    return NDBT_OK;
  }
}

int runTestApiConnectTimeout(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;

  g_info << "Check connect works with timeout 3000" << endl;
  if (!mgmd.set_timeout(3000))
    return NDBT_FAILED;

  if (!mgmd.connect())
  {
    g_err << "Connect failed with timeout 3000" << endl;
    return NDBT_FAILED;
  }

  if (!mgmd.disconnect())
    return NDBT_FAILED;

  g_info << "Check connect to illegal host will timeout after 3000" << endl;
  if (!mgmd.set_timeout(3000))
    return NDBT_FAILED;
  mgmd.setConnectString("1.1.1.1");

  NDB_TICKS tstart= NdbTick_CurrentMillisecond();
  if (mgmd.connect())
  {
    g_err << "Connect to illegal host suceeded" << endl;
    return NDBT_FAILED;
  }

  NDB_TICKS msecs= NdbTick_CurrentMillisecond() - tstart;
  ndbout << "Took about " << msecs <<" milliseconds"<<endl;

  if(msecs > 6000)
  {
    g_err << "The connect to illegal host timedout after much longer "
          << "time than was expected, expected <= 6000, got " << msecs << endl;
    return NDBT_FAILED;
  }
  return NDBT_OK;
}


int runTestApiTimeoutBasic(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;
  int result= NDBT_FAILED;
  int cc= 0;
  int mgmd_nodeid= 0;
  ndb_mgm_reply reply;

  NdbMgmHandle h;
  h= ndb_mgm_create_handle();
  ndb_mgm_set_connectstring(h, mgmd.getConnectString());

  ndbout << "TEST timout check_connection" << endl;
  int errs[] = { 1, 2, 3, -1};

  for(int error_ins_no=0; errs[error_ins_no]!=-1; error_ins_no++)
  {
    int error_ins= errs[error_ins_no];
    ndbout << "trying error " << error_ins << endl;
    ndb_mgm_connect(h,0,0,0);

    if(ndb_mgm_check_connection(h) < 0)
    {
      result= NDBT_FAILED;
      goto done;
    }

    mgmd_nodeid= ndb_mgm_get_mgmd_nodeid(h);
    if(mgmd_nodeid==0)
    {
      ndbout << "Failed to get mgmd node id to insert error" << endl;
      result= NDBT_FAILED;
      goto done;
    }

    reply.return_code= 0;

    if(ndb_mgm_insert_error(h, mgmd_nodeid, error_ins, &reply)< 0)
    {
      ndbout << "failed to insert error " << endl;
      result= NDBT_FAILED;
      goto done;
    }

    ndb_mgm_set_timeout(h,2500);

    cc= ndb_mgm_check_connection(h);
    if(cc < 0)
      result= NDBT_OK;
    else
      result= NDBT_FAILED;

    if(ndb_mgm_is_connected(h))
    {
      ndbout << "FAILED: still connected" << endl;
      result= NDBT_FAILED;
    }
  }

  ndbout << "TEST get_mgmd_nodeid" << endl;
  ndb_mgm_connect(h,0,0,0);

  if(ndb_mgm_insert_error(h, mgmd_nodeid, 0, &reply)< 0)
  {
    ndbout << "failed to remove inserted error " << endl;
    result= NDBT_FAILED;
    goto done;
  }

  cc= ndb_mgm_get_mgmd_nodeid(h);
  ndbout << "got node id: " << cc << endl;
  if(cc==0)
  {
    ndbout << "FAILED: didn't get node id" << endl;
    result= NDBT_FAILED;
  }
  else
    result= NDBT_OK;

  ndbout << "TEST end_session" << endl;
  ndb_mgm_connect(h,0,0,0);

  if(ndb_mgm_insert_error(h, mgmd_nodeid, 4, &reply)< 0)
  {
    ndbout << "FAILED: insert error 1" << endl;
    result= NDBT_FAILED;
    goto done;
  }

  cc= ndb_mgm_end_session(h);
  if(cc==0)
  {
    ndbout << "FAILED: success in calling end_session" << endl;
    result= NDBT_FAILED;
  }
  else if(ndb_mgm_get_latest_error(h)!=ETIMEDOUT)
  {
    ndbout << "FAILED: Incorrect error code (" << ndb_mgm_get_latest_error(h)
           << " != expected " << ETIMEDOUT << ") desc: "
           << ndb_mgm_get_latest_error_desc(h)
           << " line: " << ndb_mgm_get_latest_error_line(h)
           << " msg: " << ndb_mgm_get_latest_error_msg(h)
           << endl;
    result= NDBT_FAILED;
  }
  else
    result= NDBT_OK;

  if(ndb_mgm_is_connected(h))
  {
    ndbout << "FAILED: is still connected after error" << endl;
    result= NDBT_FAILED;
  }
done:
  ndb_mgm_disconnect(h);
  ndb_mgm_destroy_handle(&h);

  return result;
}

int runTestApiGetStatusTimeout(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;
  int result= NDBT_OK;
  int mgmd_nodeid= 0;

  NdbMgmHandle h;
  h= ndb_mgm_create_handle();
  ndb_mgm_set_connectstring(h, mgmd.getConnectString());

  int errs[] = { 0, 5, 6, 7, 8, 9, -1 };

  for(int error_ins_no=0; errs[error_ins_no]!=-1; error_ins_no++)
  {
    int error_ins= errs[error_ins_no];
    ndb_mgm_connect(h,0,0,0);

    if(ndb_mgm_check_connection(h) < 0)
    {
      result= NDBT_FAILED;
      goto done;
    }

    mgmd_nodeid= ndb_mgm_get_mgmd_nodeid(h);
    if(mgmd_nodeid==0)
    {
      ndbout << "Failed to get mgmd node id to insert error" << endl;
      result= NDBT_FAILED;
      goto done;
    }

    ndb_mgm_reply reply;
    reply.return_code= 0;

    if(ndb_mgm_insert_error(h, mgmd_nodeid, error_ins, &reply)< 0)
    {
      ndbout << "failed to insert error " << error_ins << endl;
      result= NDBT_FAILED;
    }

    ndbout << "trying error: " << error_ins << endl;

    ndb_mgm_set_timeout(h,2500);

    struct ndb_mgm_cluster_state *cl= ndb_mgm_get_status(h);

    if(cl!=NULL)
      free(cl);

    /*
     * For whatever strange reason,
     * get_status is okay with not having the last enter there.
     * instead of "fixing" the api, let's have a special case
     * so we don't break any behaviour
     */

    if(error_ins!=0 && error_ins!=9 && cl!=NULL)
    {
      ndbout << "FAILED: got a ndb_mgm_cluster_state back" << endl;
      result= NDBT_FAILED;
    }

    if(error_ins!=0 && error_ins!=9 && ndb_mgm_is_connected(h))
    {
      ndbout << "FAILED: is still connected after error" << endl;
      result= NDBT_FAILED;
    }

    if(error_ins!=0 && error_ins!=9 && ndb_mgm_get_latest_error(h)!=ETIMEDOUT)
    {
      ndbout << "FAILED: Incorrect error code (" << ndb_mgm_get_latest_error(h)
             << " != expected " << ETIMEDOUT << ") desc: "
             << ndb_mgm_get_latest_error_desc(h)
             << " line: " << ndb_mgm_get_latest_error_line(h)
             << " msg: " << ndb_mgm_get_latest_error_msg(h)
             << endl;
      result= NDBT_FAILED;
    }
  }

done:
  ndb_mgm_disconnect(h);
  ndb_mgm_destroy_handle(&h);

  return result;
}

int runTestMgmApiGetConfigTimeout(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;
  int result= NDBT_OK;
  int mgmd_nodeid= 0;

  NdbMgmHandle h;
  h= ndb_mgm_create_handle();
  ndb_mgm_set_connectstring(h, mgmd.getConnectString());

  int errs[] = { 0, 1, 2, 3, -1 };

  for(int error_ins_no=0; errs[error_ins_no]!=-1; error_ins_no++)
  {
    int error_ins= errs[error_ins_no];
    ndb_mgm_connect(h,0,0,0);

    if(ndb_mgm_check_connection(h) < 0)
    {
      result= NDBT_FAILED;
      goto done;
    }

    mgmd_nodeid= ndb_mgm_get_mgmd_nodeid(h);
    if(mgmd_nodeid==0)
    {
      ndbout << "Failed to get mgmd node id to insert error" << endl;
      result= NDBT_FAILED;
      goto done;
    }

    ndb_mgm_reply reply;
    reply.return_code= 0;

    if(ndb_mgm_insert_error(h, mgmd_nodeid, error_ins, &reply)< 0)
    {
      ndbout << "failed to insert error " << error_ins << endl;
      result= NDBT_FAILED;
    }

    ndbout << "trying error: " << error_ins << endl;

    ndb_mgm_set_timeout(h,2500);

    struct ndb_mgm_configuration *c= ndb_mgm_get_configuration(h,0);

    if(c!=NULL)
      free(c);

    if(error_ins!=0 && c!=NULL)
    {
      ndbout << "FAILED: got a ndb_mgm_configuration back" << endl;
      result= NDBT_FAILED;
    }

    if(error_ins!=0 && ndb_mgm_is_connected(h))
    {
      ndbout << "FAILED: is still connected after error" << endl;
      result= NDBT_FAILED;
    }

    if(error_ins!=0 && ndb_mgm_get_latest_error(h)!=ETIMEDOUT)
    {
      ndbout << "FAILED: Incorrect error code (" << ndb_mgm_get_latest_error(h)
             << " != expected " << ETIMEDOUT << ") desc: "
             << ndb_mgm_get_latest_error_desc(h)
             << " line: " << ndb_mgm_get_latest_error_line(h)
             << " msg: " << ndb_mgm_get_latest_error_msg(h)
             << endl;
      result= NDBT_FAILED;
    }
  }

done:
  ndb_mgm_disconnect(h);
  ndb_mgm_destroy_handle(&h);

  return result;
}

int runTestMgmApiEventTimeout(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;
  int result= NDBT_OK;
  int mgmd_nodeid= 0;

  NdbMgmHandle h;
  h= ndb_mgm_create_handle();
  ndb_mgm_set_connectstring(h, mgmd.getConnectString());

  int errs[] = { 10000, 0, -1 };

  for(int error_ins_no=0; errs[error_ins_no]!=-1; error_ins_no++)
  {
    int error_ins= errs[error_ins_no];
    ndb_mgm_connect(h,0,0,0);

    if(ndb_mgm_check_connection(h) < 0)
    {
      result= NDBT_FAILED;
      goto done;
    }

    mgmd_nodeid= ndb_mgm_get_mgmd_nodeid(h);
    if(mgmd_nodeid==0)
    {
      ndbout << "Failed to get mgmd node id to insert error" << endl;
      result= NDBT_FAILED;
      goto done;
    }

    ndb_mgm_reply reply;
    reply.return_code= 0;

    if(ndb_mgm_insert_error(h, mgmd_nodeid, error_ins, &reply)< 0)
    {
      ndbout << "failed to insert error " << error_ins << endl;
      result= NDBT_FAILED;
    }

    ndbout << "trying error: " << error_ins << endl;

    ndb_mgm_set_timeout(h,2500);

    int filter[] = { 15, NDB_MGM_EVENT_CATEGORY_BACKUP,
                     1, NDB_MGM_EVENT_CATEGORY_STARTUP,
                     0 };

    NDB_SOCKET_TYPE my_fd;
#ifdef NDB_WIN
    SOCKET fd= ndb_mgm_listen_event(h, filter);
    my_fd.s= fd;
#else
    int fd= ndb_mgm_listen_event(h, filter);
    my_fd.fd= fd;
#endif

    if(!my_socket_valid(my_fd))
    {
      ndbout << "FAILED: could not listen to event" << endl;
      result= NDBT_FAILED;
    }

    union {
      Uint32 theData[25];
      EventReport repData;
    };
    EventReport *fake_event = &repData;
    fake_event->setEventType(NDB_LE_NDBStopForced);
    fake_event->setNodeId(42);
    theData[2]= 0;
    theData[3]= 0;
    theData[4]= 0;
    theData[5]= 0;

    ndb_mgm_report_event(h, theData, 6);

    char *tmp= 0;
    char buf[512];

    SocketInputStream in(my_fd,2000);
    for(int i=0; i<20; i++)
    {
      if((tmp = in.gets(buf, sizeof(buf))))
      {
//        const char ping_token[]="<PING>";
//        if(memcmp(ping_token,tmp,sizeof(ping_token)-1))
          if(tmp && strlen(tmp))
            ndbout << tmp;
      }
      else
      {
        if(in.timedout())
        {
          ndbout << "TIMED OUT READING EVENT at iteration " << i << endl;
          break;
        }
      }
    }

    /*
     * events go through a *DIFFERENT* socket than the NdbMgmHandle
     * so we should still be connected (and be able to check_connection)
     *
     */

    if(ndb_mgm_check_connection(h) && !ndb_mgm_is_connected(h))
    {
      ndbout << "FAILED: is still connected after error" << endl;
      result= NDBT_FAILED;
    }

    ndb_mgm_disconnect(h);
  }

done:
  ndb_mgm_disconnect(h);
  ndb_mgm_destroy_handle(&h);

  return result;
}

int runTestMgmApiStructEventTimeout(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;
  int result= NDBT_OK;
  int mgmd_nodeid= 0;

  NdbMgmHandle h;
  h= ndb_mgm_create_handle();
  ndb_mgm_set_connectstring(h, mgmd.getConnectString());

  int errs[] = { 10000, 0, -1 };

  for(int error_ins_no=0; errs[error_ins_no]!=-1; error_ins_no++)
  {
    int error_ins= errs[error_ins_no];
    ndb_mgm_connect(h,0,0,0);

    if(ndb_mgm_check_connection(h) < 0)
    {
      result= NDBT_FAILED;
      goto done;
    }

    mgmd_nodeid= ndb_mgm_get_mgmd_nodeid(h);
    if(mgmd_nodeid==0)
    {
      ndbout << "Failed to get mgmd node id to insert error" << endl;
      result= NDBT_FAILED;
      goto done;
    }

    ndb_mgm_reply reply;
    reply.return_code= 0;

    if(ndb_mgm_insert_error(h, mgmd_nodeid, error_ins, &reply)< 0)
    {
      ndbout << "failed to insert error " << error_ins << endl;
      result= NDBT_FAILED;
    }

    ndbout << "trying error: " << error_ins << endl;

    ndb_mgm_set_timeout(h,2500);

    int filter[] = { 15, NDB_MGM_EVENT_CATEGORY_BACKUP,
                     1, NDB_MGM_EVENT_CATEGORY_STARTUP,
                     0 };
    NdbLogEventHandle le_handle= ndb_mgm_create_logevent_handle(h, filter);

    struct ndb_logevent le;
    for(int i=0; i<20; i++)
    {
      if(error_ins==0 || (error_ins!=0 && i<5))
      {
        union {
	  Uint32 theData[25];
	  EventReport repData;
	};
        EventReport *fake_event = &repData;
        fake_event->setEventType(NDB_LE_NDBStopForced);
        fake_event->setNodeId(42);
        theData[2]= 0;
        theData[3]= 0;
        theData[4]= 0;
        theData[5]= 0;

        ndb_mgm_report_event(h, theData, 6);
      }
      int r= ndb_logevent_get_next(le_handle, &le, 2500);
      if(r>0)
      {
        ndbout << "Receieved event" << endl;
      }
      else if(r<0)
      {
        ndbout << "ERROR" << endl;
      }
      else // no event
      {
        ndbout << "TIMED OUT READING EVENT at iteration " << i << endl;
        if(error_ins==0)
          result= NDBT_FAILED;
        else
          result= NDBT_OK;
        break;
      }
    }

    /*
     * events go through a *DIFFERENT* socket than the NdbMgmHandle
     * so we should still be connected (and be able to check_connection)
     *
     */

    if(ndb_mgm_check_connection(h) && !ndb_mgm_is_connected(h))
    {
      ndbout << "FAILED: is still connected after error" << endl;
      result= NDBT_FAILED;
    }

    ndb_mgm_disconnect(h);
  }

done:
  ndb_mgm_disconnect(h);
  ndb_mgm_destroy_handle(&h);

  return result;
}

#include <mgmapi_internal.h>

int runSetConfig(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;

  if (!mgmd.connect())
    return NDBT_FAILED;

  int loops= ctx->getNumLoops();
  for (int l= 0; l < loops; l++){
    g_info << l << ": ";

    struct ndb_mgm_configuration* conf=
      ndb_mgm_get_configuration(mgmd.handle(), 0);
    if (!conf)
    {
      g_err << "ndb_mgm_get_configuration failed, error: "
            << ndb_mgm_get_latest_error_msg(mgmd.handle()) << endl;
      return NDBT_FAILED;
    }

    int r= ndb_mgm_set_configuration(mgmd.handle(), conf);
    free(conf);

    if (r != 0)
    {
      g_err << "ndb_mgm_set_configuration failed, error: "
            << ndb_mgm_get_latest_error_msg(mgmd.handle()) << endl;
      return NDBT_FAILED;
    }
  }
  return NDBT_OK;
}


int runSetConfigUntilStopped(NDBT_Context* ctx, NDBT_Step* step)
{
  int result= NDBT_OK;
  while(!ctx->isTestStopped() &&
        (result= runSetConfig(ctx, step)) == NDBT_OK)
    ;
  return result;
}


int runGetConfig(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;

  if (!mgmd.connect())
    return NDBT_FAILED;

  int loops= ctx->getNumLoops();
  for (int l= 0; l < loops; l++){
    g_info << l << ": ";
    struct ndb_mgm_configuration* conf=
      ndb_mgm_get_configuration(mgmd.handle(), 0);
    if (!conf)
      return NDBT_FAILED;
    free(conf);
  }
  return NDBT_OK;
}


int runGetConfigUntilStopped(NDBT_Context* ctx, NDBT_Step* step)
{
  int result= NDBT_OK;
  while(!ctx->isTestStopped() &&
        (result= runGetConfig(ctx, step)) == NDBT_OK)
    ;
  return result;
}


// Find a random node of a given type.

static bool
get_nodeid_of_type(NdbMgmd& mgmd, ndb_mgm_node_type type, int *nodeId)
{
  ndb_mgm_node_type
    node_types[2] = { type,
                      NDB_MGM_NODE_TYPE_UNKNOWN };

  ndb_mgm_cluster_state *cs = ndb_mgm_get_status2(mgmd.handle(), node_types);
  if (cs == NULL)
  {
    g_err << "ndb_mgm_get_status2 failed, error: "
            << ndb_mgm_get_latest_error(mgmd.handle()) << " "
            << ndb_mgm_get_latest_error_msg(mgmd.handle()) << endl;
    return false;
  }

  int noOfNodes = cs->no_of_nodes;
  int randomnode = myRandom48(noOfNodes);
  ndb_mgm_node_state *ns = cs->node_states + randomnode;
  assert(ns->node_type == (Uint32)type);
  assert(ns->node_id);

  *nodeId = ns->node_id;
  g_info << "Got node id " << *nodeId << " of type " << type << endl;

  free(cs);
  return true;
}


// Ensure getting config from an illegal node fails.
// Return true in that case.

static bool
get_config_from_illegal_node(NdbMgmd& mgmd, int nodeId)
{
  struct ndb_mgm_configuration* conf=
      ndb_mgm_get_configuration_from_node(mgmd.handle(), nodeId);

  // Get conf from an illegal node should fail.
  if (ndb_mgm_get_latest_error(mgmd.handle()) != NDB_MGM_GET_CONFIG_FAILED)
  {
      g_err << "ndb_mgm_get_configuration from illegal node "
            << nodeId << " not failed, error: "
            << ndb_mgm_get_latest_error(mgmd.handle()) << " "
            << ndb_mgm_get_latest_error_msg(mgmd.handle()) << endl;
      return false;
  }

  if (conf)
  {
    // Should not get a conf from an illegal node.
    g_err << "ndb_mgm_get_configuration from illegal node: "
          << nodeId << ", error: "
          << ndb_mgm_get_latest_error(mgmd.handle()) << " "
          << ndb_mgm_get_latest_error_msg(mgmd.handle()) << endl;
    free(conf);
    return false;
  }
  return true;
}


// Check get_config from a non-existing node fails.

static bool
check_get_config_illegal_node(NdbMgmd& mgmd)
{
  // Find a node that does not exist
  Config conf;
  if (!mgmd.get_config(conf))
    return false;

  int nodeId = 0;
  for(Uint32 i= 1; i < MAX_NODES; i++){
    ConfigIter iter(&conf, CFG_SECTION_NODE);
    if (iter.find(CFG_NODE_ID, i) != 0){
      nodeId = i;
      break;
    }
  }
  if (nodeId == 0)
    return true; // All nodes probably defined

  return get_config_from_illegal_node(mgmd, nodeId);
}



// Check get_config from a non-NDB/MGM node type fails

static bool
check_get_config_wrong_type(NdbMgmd& mgmd)
{
  int nodeId = 0;

  if (get_nodeid_of_type(mgmd, NDB_MGM_NODE_TYPE_API, &nodeId))
  {
    return get_config_from_illegal_node(mgmd, nodeId);
  }
  // No API nodes found.
  return true;
}

/* Find management node or a random data node, and get config from it.
 * Also ensure failure when getting config from
 * an illegal node (a non-NDB/MGM type, nodeid not defined,
 * or nodeid > MAX_NODES).
 */
int runGetConfigFromNode(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;
  if (!mgmd.connect())
    return NDBT_FAILED;

  if (!check_get_config_wrong_type(mgmd) ||
      !check_get_config_illegal_node(mgmd) ||
      !get_config_from_illegal_node(mgmd, MAX_NODES + 2))
  {
    return NDBT_FAILED;
  }

  int loops= ctx->getNumLoops();
  for (int l= 0; l < loops; l++)
  {
    /* Get config from a node of type:
     * NDB_MGM_NODE_TYPE_NDB or NDB_MGM_NODE_TYPE_MGM
     */
    int myChoice = myRandom48(2);
    ndb_mgm_node_type randomAllowedType = (myChoice) ?
                                          NDB_MGM_NODE_TYPE_NDB :
                                          NDB_MGM_NODE_TYPE_MGM;
    int nodeId = 0;
    if (get_nodeid_of_type(mgmd, randomAllowedType, &nodeId))
    {
      struct ndb_mgm_configuration* conf =
        ndb_mgm_get_configuration_from_node(mgmd.handle(), nodeId);
      if (!conf)
      {
        g_err << "ndb_mgm_get_configuration_from_node "
              << nodeId << " failed, error: "
              << ndb_mgm_get_latest_error(mgmd.handle()) << " "
              << ndb_mgm_get_latest_error_msg(mgmd.handle()) << endl;
        return NDBT_FAILED;
      }
      free(conf);
    }
    else
    {
      // ignore
    }
  }
  return NDBT_OK;
}


int runGetConfigFromNodeUntilStopped(NDBT_Context* ctx, NDBT_Step* step)
{
  int result= NDBT_OK;
  while(!ctx->isTestStopped() &&
        (result= runGetConfigFromNode(ctx, step)) == NDBT_OK)
    ;
  return result;
}


int runTestStatus(NDBT_Context* ctx, NDBT_Step* step)
{
  ndb_mgm_node_type types[2] = {
    NDB_MGM_NODE_TYPE_NDB,
    NDB_MGM_NODE_TYPE_UNKNOWN
  };

  NdbMgmd mgmd;
  struct ndb_mgm_cluster_state *state;
  int iterations = ctx->getNumLoops();

  if (!mgmd.connect())
    return NDBT_FAILED;

  int result= NDBT_OK;
  while (iterations-- != 0 && result == NDBT_OK)
  {
    state = ndb_mgm_get_status(mgmd.handle());
    if(state == NULL) {
      ndbout_c("Could not get status!");
      result= NDBT_FAILED;
      continue;
    }
    free(state);

    state = ndb_mgm_get_status2(mgmd.handle(), types);
    if(state == NULL){
      ndbout_c("Could not get status2!");
      result= NDBT_FAILED;
      continue;
    }
    free(state);

    state = ndb_mgm_get_status2(mgmd.handle(), 0);
    if(state == NULL){
      ndbout_c("Could not get status2 second time!");
      result= NDBT_FAILED;
      continue;
    }
    free(state);
  }
  return result;
}


int runTestStatusUntilStopped(NDBT_Context* ctx, NDBT_Step* step)
{
  int result= NDBT_OK;
  while(!ctx->isTestStopped() &&
        (result= runTestStatus(ctx, step)) == NDBT_OK)
    ;
  return result;
}


static bool
get_nodeid(NdbMgmd& mgmd,
           const Properties& args,
           Properties& reply)
{
  // Fill in default values of other args
  Properties call_args(args);
  if (!call_args.contains("version"))
    call_args.put("version", 1);
  if (!call_args.contains("nodetype"))
    call_args.put("nodetype", 1);
  if (!call_args.contains("nodeid"))
    call_args.put("nodeid", 1);
  if (!call_args.contains("user"))
    call_args.put("user", "mysqld");
  if (!call_args.contains("password"))
    call_args.put("password", "mysqld");
  if (!call_args.contains("public key"))
  call_args.put("public key", "a public key");
  if (!call_args.contains("name"))
    call_args.put("name", "testMgm");
  if (!call_args.contains("log_event"))
    call_args.put("log_event", 1);
  if (!call_args.contains("timeout"))
    call_args.put("timeout", 100);

  if (!call_args.contains("endian"))
  {
    union { long l; char c[sizeof(long)]; } endian_check;
    endian_check.l = 1;
    call_args.put("endian", (endian_check.c[sizeof(long)-1])?"big":"little");
  }

  if (!mgmd.call("get nodeid", call_args,
                 "get nodeid reply", reply))
  {
    g_err << "get_nodeid: mgmd.call failed" << endl;
    return false;
  }

  // reply.print();
  return true;
}


static const char*
get_result(const Properties& reply)
{
  const char* result;
  if (!reply.get("result", &result)){
    ndbout_c("result: no 'result' found in reply");
    return NULL;
  }
  return result;
}


static bool result_contains(const Properties& reply,
                            const char* expected_result)
{
  BaseString result(get_result(reply));
  if (strstr(result.c_str(), expected_result) == NULL){
    ndbout_c("result_contains: result string '%s' "
             "didn't contain expected result '%s'",
             result.c_str(), expected_result);
    return false;
  }
  g_info << " result: " << result << endl;
  return true;
}


static bool ok(const Properties& reply)
{
  BaseString result(get_result(reply));
  if (result == "Ok")
    return true;
  return false;
}

static bool failed(const Properties& reply)
{
  BaseString result(get_result(reply));
  if (result == "Failed")
    return true;
  return false;
}

static const char*
get_message(const Properties& reply)
{
  const char* message;
  if (!reply.get("message", &message)){
    ndbout_c("message: no 'message' found in reply");
    return NULL;
  }
  return message;
}


static bool message_contains(const Properties& reply,
                            const char* expected_message)
{
  BaseString message(get_message(reply));
  if (strstr(message.c_str(), expected_message) == NULL){
    ndbout_c("message_contains: message string '%s' "
             "didn't contain expected message '%s'",
             message.c_str(), expected_message);
    return false;
  }
  g_info << " message: " << message << endl;
  return true;
}


static bool get_nodeid_result_contains(NdbMgmd& mgmd,
                                       const Properties& args,
                                       const char* expected_result)
{
  Properties reply;
  if (!get_nodeid(mgmd, args, reply))
    return false;
  return result_contains(reply, expected_result);
}



static bool
check_get_nodeid_invalid_endian1(NdbMgmd& mgmd)
{
  union { long l; char c[sizeof(long)]; } endian_check;
  endian_check.l = 1;
  Properties args;
  /* Set endian to opposite value */
  args.put("endian", (endian_check.c[sizeof(long)-1])?"little":"big");
  return get_nodeid_result_contains(mgmd, args,
                                    "Node does not have the same endian");
}


static bool
check_get_nodeid_invalid_endian2(NdbMgmd& mgmd)
{
  Properties args;
  /* Set endian to weird value */
  args.put("endian", "hepp");
  return get_nodeid_result_contains(mgmd, args,
                                    "Node does not have the same endian");
}


static bool
check_get_nodeid_invalid_nodetype1(NdbMgmd& mgmd)
{
  Properties args;
  args.put("nodetype", 37);
  return get_nodeid_result_contains(mgmd, args,
                                    "unknown nodetype 37");
}

static bool
check_get_nodeid_invalid_nodeid(NdbMgmd& mgmd)
{
  for (int nodeId = MAX_NODES; nodeId < MAX_NODES+2; nodeId++){
    g_info << "Testing invalid node " << nodeId << endl;;

    Properties args;
    args.put("nodeid", nodeId);
    BaseString expected;
    expected.assfmt("illegal nodeid %d", nodeId);
    if (!get_nodeid_result_contains(mgmd, args, expected.c_str()))
      return false;
  }
  return true;
}

static bool
check_get_nodeid_dynamic_nodeid(NdbMgmd& mgmd)
{
  bool result = true;
  Uint32 nodeId= 0; // Get dynamic node id
  for (int nodeType = NDB_MGM_NODE_TYPE_MIN;
       nodeType < NDB_MGM_NODE_TYPE_MAX; nodeType++){
    while(true)
    {
      g_info << "Testing dynamic nodeid " << nodeId
             << ", nodeType: " << nodeType << endl;

      Properties args;
      args.put("nodeid", nodeId);
      args.put("nodetype", nodeType);
      Properties reply;
      if (!get_nodeid(mgmd, args, reply))
        return false;

      /*
        Continue to get dynamic id's until
        an error "there is no more nodeid" occur
      */
      if (!ok(reply)){
        BaseString expected1;
        expected1.assfmt("No free node id found for %s",
                        NdbMgmd::NodeType(nodeType).c_str());
        BaseString expected2;
        expected2.assfmt("Connection done from wrong host");
        if (!(result_contains(reply, expected1.c_str()) ||
              result_contains(reply, expected2.c_str())))
          result= false; // Got wrong error message
        break;
      }
    }
  }
  return result;
}


static bool
check_get_nodeid_nonode(NdbMgmd& mgmd)
{
  // Find a node that does not exist
  Config conf;
  if (!mgmd.get_config(conf))
    return false;

  Uint32 nodeId = 0;
  for(Uint32 i= 1; i < MAX_NODES; i++){
    ConfigIter iter(&conf, CFG_SECTION_NODE);
    if (iter.find(CFG_NODE_ID, i) != 0){
      nodeId = i;
      break;
    }
  }
  if (nodeId == 0)
    return true; // All nodes probably defined

  g_info << "Testing nonexisting node " << nodeId << endl;;

  Properties args;
  args.put("nodeid", nodeId);
  BaseString expected;
  expected.assfmt("No node defined with id=%d", nodeId);
  return get_nodeid_result_contains(mgmd, args, expected.c_str());
}

#if 0
static bool
check_get_nodeid_nodeid1(NdbMgmd& mgmd)
{
  // Find a node that does exist
  Config conf;
  if (!mgmd.get_config(conf))
    return false;

  Uint32 nodeId = 0;
  Uint32 nodeType = NDB_MGM_NODE_TYPE_UNKNOWN;
  for(Uint32 i= 1; i < MAX_NODES; i++){
    ConfigIter iter(&conf, CFG_SECTION_NODE);
    if (iter.find(CFG_NODE_ID, i) == 0){
      nodeId = i;
      iter.get(CFG_TYPE_OF_SECTION, &nodeType);
      break;
    }
  }
  assert(nodeId);
  assert(nodeType != (Uint32)NDB_MGM_NODE_TYPE_UNKNOWN);

  Properties args, reply;
  args.put("nodeid",nodeId);
  args.put("nodetype",nodeType);
  if (!get_nodeid(mgmd, args, reply))
  {
    g_err << "check_get_nodeid_nodeid1: failed for "
          << "nodeid: " << nodeId << ", nodetype: " << nodeType << endl;
    return false;
  }
  reply.print();
  return ok(reply);
}
#endif

static bool
check_get_nodeid_wrong_nodetype(NdbMgmd& mgmd)
{
  // Find a node that does exist
  Config conf;
  if (!mgmd.get_config(conf))
    return false;

  Uint32 nodeId = 0;
  Uint32 nodeType = NDB_MGM_NODE_TYPE_UNKNOWN;
  for(Uint32 i= 1; i < MAX_NODES; i++){
    ConfigIter iter(&conf, CFG_SECTION_NODE);
    if (iter.find(CFG_NODE_ID, i) == 0){
      nodeId = i;
      iter.get(CFG_TYPE_OF_SECTION, &nodeType);
      break;
    }
  }
  assert(nodeId && nodeType != (Uint32)NDB_MGM_NODE_TYPE_UNKNOWN);

  nodeType = (nodeType + 1) / NDB_MGM_NODE_TYPE_MAX;
  assert((int)nodeType >= (int)NDB_MGM_NODE_TYPE_MIN &&
         (int)nodeType <= (int)NDB_MGM_NODE_TYPE_MAX);

  Properties args, reply;
  args.put("nodeid",nodeId);
  args.put("nodeid",nodeType);
  if (!get_nodeid(mgmd, args, reply))
  {
    g_err << "check_get_nodeid_nodeid1: failed for "
          << "nodeid: " << nodeId << ", nodetype: " << nodeType << endl;
    return false;
  }
  BaseString expected;
  expected.assfmt("Id %d configured as", nodeId);
  return result_contains(reply, expected.c_str());
}



int runTestGetNodeId(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;

  if (!mgmd.connect())
    return NDBT_FAILED;

  int result= NDBT_FAILED;
  if (
      check_get_nodeid_invalid_endian1(mgmd) &&
      check_get_nodeid_invalid_endian2(mgmd) &&
      check_get_nodeid_invalid_nodetype1(mgmd) &&
      check_get_nodeid_invalid_nodeid(mgmd) &&
      check_get_nodeid_dynamic_nodeid(mgmd) &&
      check_get_nodeid_nonode(mgmd) &&
//      check_get_nodeid_nodeid1(mgmd) &&
      check_get_nodeid_wrong_nodetype(mgmd) &&
      true)
    result= NDBT_OK;

  if (!mgmd.end_session())
    result= NDBT_FAILED;

  return result;
}


int runTestGetNodeIdUntilStopped(NDBT_Context* ctx, NDBT_Step* step)
{
  int result= NDBT_OK;
  while(!ctx->isTestStopped() &&
        (result= runTestGetNodeId(ctx, step)) == NDBT_OK)
    ;
  return result;
}


int runSleepAndStop(NDBT_Context* ctx, NDBT_Step* step)
{
  int counter= 3*ctx->getNumLoops();

  while(!ctx->isTestStopped() && counter--)
    NdbSleep_SecSleep(1);;
  ctx->stopTest();
  return NDBT_OK;
}


static bool
check_connection(NdbMgmd& mgmd)
{
  Properties args, reply;
  mgmd.verbose(false); // Verbose off
  bool result= mgmd.call("check connection", args,
                         "check connection reply", reply);
  mgmd.verbose(); // Verbose on
  return result;
}


static bool
check_transporter_connect(NdbMgmd& mgmd, const char * hello)
{
  SocketOutputStream out(mgmd.socket());

  // Call 'transporter connect'
  if (out.println("transporter connect\n"))
  {
    g_err << "Send failed" << endl;
    return false;
  }

  // Send the 'hello'
  g_info << "Client hello: '" << hello << "'" << endl;
  if (out.println("%s", hello))
  {
    g_err << "Send hello '" << hello << "' failed" << endl;
    return false;
  }

  // Should not be possible to read a reply now, socket
  // should have been closed
  if (check_connection(mgmd)){
    g_err << "not disconnected" << endl;
    return false;
  }

  // disconnect and connect again
  if (!mgmd.disconnect())
    return false;
  if (!mgmd.connect())
    return false;

  return true;
}


int runTestTransporterConnect(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;

  if (!mgmd.connect())
    return NDBT_FAILED;

  int result = NDBT_FAILED;
  if (
      // Junk hello strings
      check_transporter_connect(mgmd, "hello") &&
      check_transporter_connect(mgmd, "hello again") &&

      // "Blow" the buffer
      check_transporter_connect(mgmd, "string_longer_than_buf_1234567890") &&

      // Out of range nodeid
      check_transporter_connect(mgmd, "-1") &&
      check_transporter_connect(mgmd, "-2 2") &&
      check_transporter_connect(mgmd, "10000") &&
      check_transporter_connect(mgmd, "99999 8") &&

      // Valid nodeid, invalid transporter type
      // Valid nodeid and transporter type, state != CONNECTING
      // ^These are only possible to test by finding an existing
      //  NDB node that are not started and use its setting(s)

      true)
   result = NDBT_OK;

  return result;
}


static bool
show_config(NdbMgmd& mgmd,
            const Properties& args,
            Properties& reply)
{
  if (!mgmd.call("show config", args,
                 "show config reply", reply, NULL, false))
  {
    g_err << "show_config: mgmd.call failed" << endl;
    return false;
  }

  // reply.print();
  return true;
}


int runCheckConfig(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;

  // Connect to any mgmd and get the config
  if (!mgmd.connect())
    return NDBT_FAILED;

  Properties args1;
  Properties config1;
  if (!show_config(mgmd, args1, config1))
    return NDBT_FAILED;

  // Get the binary config
  Config conf;
  if (!mgmd.get_config(conf))
    return NDBT_FAILED;

  // Extract list of connectstrings to each mgmd
  BaseString connectstring;
  conf.getConnectString(connectstring, ";");

  Vector<BaseString> mgmds;
  connectstring.split(mgmds, ";");

  // Connect to each mgmd and check
  // they all have the same config
  for (size_t i = 0; i < mgmds.size(); i++)
  {
    NdbMgmd mgmd2;
    g_info << "Connecting to " << mgmds[i].c_str() << endl;
    if (!mgmd2.connect(mgmds[i].c_str()))
      return NDBT_FAILED;

    Properties args2;
    Properties config2;
    if (!show_config(mgmd, args2, config2))
      return NDBT_FAILED;

    // Compare config1 and config2 line by line
    Uint32 line = 1;
    const char* value1;
    const char* value2;
    while (true)
    {
      if (config1.get("line", line, &value1))
      {
        // config1 had line, so should config2
        if (config2.get("line", line, &value2))
        {
          // both configs had line, check they are equal
          if (strcmp(value1, value2) != 0)
          {
            g_err << "the value on line " << line << "didn't match!" << endl;
            g_err << "config1, value: " << value1 << endl;
            g_err << "config2, value: " << value2 << endl;
            return NDBT_FAILED;
          }
          // g_info << line << ": " << value1 << " = " << value2 << endl;
        }
        else
        {
          g_err << "config2 didn't have line " << line << "!" << endl;
          return NDBT_FAILED;
        }
      }
      else
      {
        // Make sure config2 does not have this line either and end loop
        if (config2.get("line", line, &value2))
        {
          g_err << "config2 had line " << line << " not in config1!" << endl;
          return NDBT_FAILED;
        }

        // End of loop
        g_info << "There was " << line << " lines in config" << endl;
        break;
      }
      line++;
    }
    if (line == 0)
    {
      g_err << "FAIL: config should have lines!" << endl;
      return NDBT_FAILED;
    }

    // Compare the binary config
    Config conf2;
    if (!mgmd.get_config(conf2))
      return NDBT_FAILED;

    if (!conf.equal(&conf2))
    {
      g_err << "The binary config was different! host: " << mgmds[i] << endl;
      return NDBT_FAILED;
    }

  }

  return NDBT_OK;
}


static bool
reload_config(NdbMgmd& mgmd,
              const Properties& args,
              Properties& reply)
{
  if (!mgmd.call("reload config", args,
                 "reload config reply", reply))
  {
    g_err << "reload config: mgmd.call failed" << endl;
    return false;
  }

  //reply.print();
  return true;
}


static bool reload_config_result_contains(NdbMgmd& mgmd,
                                          const Properties& args,
                                          const char* expected_result)
{
  Properties reply;
  if (!reload_config(mgmd, args, reply))
    return false;
  return result_contains(reply, expected_result);
}


static bool
check_reload_config_both_config_and_mycnf(NdbMgmd& mgmd)
{
  Properties args;
  // Send reload command with both config_filename and mycnf set
  args.put("config_filename", "some filename");
  args.put("mycnf", 1);
  return reload_config_result_contains(mgmd, args,
                                       "ERROR: Both mycnf and config_filename");
}


static bool
show_variables(NdbMgmd& mgmd, Properties& reply)
{
  if (!mgmd.call("show variables", "",
                 "show variables reply", reply))
  {
    g_err << "show_variables: mgmd.call failed" << endl;
    return false;
  }
  return true;
}


static bool
check_reload_config_invalid_config_filename(NdbMgmd& mgmd, bool mycnf)
{

  BaseString expected("Could not load configuration from 'nonexisting_file");
  if (mycnf)
  {
    // Differing error message if started from my.cnf
    expected.assign("Can't switch to use config.ini 'nonexisting_file' "
                    "when node was started from my.cnf");
  }

  Properties args;
  // Send reload command with an invalid config_filename
  args.put("config_filename", "nonexisting_file");
  return reload_config_result_contains(mgmd, args, expected.c_str());
}


int runTestReloadConfig(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;

  if (!mgmd.connect())
    return NDBT_FAILED;

  Properties variables;
  if (!show_variables(mgmd, variables))
    return NDBT_FAILED;

  variables.print();

  const char* mycnf_str;
  if (!variables.get("mycnf", &mycnf_str))
    abort();
  bool uses_mycnf = (strcmp(mycnf_str, "yes") == 0);

  int result= NDBT_FAILED;
  if (
      check_reload_config_both_config_and_mycnf(mgmd) &&
      check_reload_config_invalid_config_filename(mgmd, uses_mycnf) &&
      true)
    result= NDBT_OK;

  if (!mgmd.end_session())
    result= NDBT_FAILED;

  return result;
}


static bool
set_config(NdbMgmd& mgmd,
           const Properties& args,
           BaseString encoded_config,
           Properties& reply)
{

  // Fill in default values of other args
  Properties call_args(args);
  if (!call_args.contains("Content-Type"))
    call_args.put("Content-Type", "ndbconfig/octet-stream");
  if (!call_args.contains("Content-Transfer-Encoding"))
    call_args.put("Content-Transfer-Encoding", "base64");
  if (!call_args.contains("Content-Length"))
    call_args.put("Content-Length",
                  encoded_config.length() ? encoded_config.length() - 1 : 1);

  if (!mgmd.call("set config", call_args,
                 "set config reply", reply,
                 encoded_config.c_str()))
  {
    g_err << "set config: mgmd.call failed" << endl;
    return false;
  }

  //reply.print();
  return true;
}


static bool set_config_result_contains(NdbMgmd& mgmd,
                                       const Properties& args,
                                       const BaseString& encoded_config,
                                       const char* expected_result)
{
  Properties reply;
  if (!set_config(mgmd, args, encoded_config, reply))
    return false;
  return result_contains(reply, expected_result);
}


static bool set_config_result_contains(NdbMgmd& mgmd,
                                       const Config& conf,
                                       const char* expected_result)
{
  Properties reply;
  Properties args;

  BaseString encoded_config;
  if (!conf.pack64(encoded_config))
    return false;

  if (!set_config(mgmd, args, encoded_config, reply))
    return false;
  return result_contains(reply, expected_result);
}


static bool
check_set_config_invalid_content_type(NdbMgmd& mgmd)
{
  Properties args;
  args.put("Content-Type", "illegal type");
  return set_config_result_contains(mgmd, args, BaseString(""),
                                    "Unhandled content type 'illegal type'");
}

static bool
check_set_config_invalid_content_encoding(NdbMgmd& mgmd)
{
  Properties args;
  args.put("Content-Transfer-Encoding", "illegal encoding");
  return set_config_result_contains(mgmd, args, BaseString(""),
                                    "Unhandled content encoding "
                                    "'illegal encoding'");
}

static bool
check_set_config_too_large_content_length(NdbMgmd& mgmd)
{
  Properties args;
  args.put("Content-Length", 1024*1024 + 1);
  return set_config_result_contains(mgmd, args, BaseString(""),
                                    "Illegal config length size 1048577");
}

static bool
check_set_config_too_small_content_length(NdbMgmd& mgmd)
{
  Properties args;
  args.put("Content-Length", (Uint32)0);
  return set_config_result_contains(mgmd, args, BaseString(""),
                                    "Illegal config length size 0");
}

static bool
check_set_config_wrong_config_length(NdbMgmd& mgmd)
{

  // Get the binary config
  Config conf;
  if (!mgmd.get_config(conf))
    return false;

  BaseString encoded_config;
  if (!conf.pack64(encoded_config))
    return false;

  Properties args;
  args.put("Content-Length", encoded_config.length() - 20);
  bool res = set_config_result_contains(mgmd, args, encoded_config,
                                        "Failed to unpack config");

  if (res){
    /*
      There are now additional 20 bytes of junk that has been
      sent to mgmd, reconnect to get rid of it
    */
    if (!mgmd.disconnect())
      return false;
    if (!mgmd.connect())
       return false;
  }
  return res;
}

static bool
check_set_config_any_node(NDBT_Context* ctx, NDBT_Step* step, NdbMgmd& mgmd)
{

  // Get the binary config
  Config conf;
  if (!mgmd.get_config(conf))
    return false;

  // Extract list of connectstrings to each mgmd
  BaseString connectstring;
  conf.getConnectString(connectstring, ";");

  Vector<BaseString> mgmds;
  connectstring.split(mgmds, ";");

  // Connect to each mgmd and check
  // they all have the same config
  for (size_t i = 0; i < mgmds.size(); i++)
  {
    NdbMgmd mgmd2;
    g_info << "Connecting to " << mgmds[i].c_str() << endl;
    if (!mgmd2.connect(mgmds[i].c_str()))
      return false;

    // Get the binary config
    Config conf2;
    if (!mgmd2.get_config(conf2))
      return false;

#if 0
    // Change one value in the config
    if (!conf2.setValue(CFG_SECTION_NODE, 0,
                        CFG_NODE_ARBIT_DELAY,
#endif

    // Set the modified config
    if (!mgmd2.set_config(conf2))
      return false;

    // Check that all mgmds now have the new config
    if (runCheckConfig(ctx, step) != NDBT_OK)
      return false;

  }

  return true;
}

static bool
check_set_config_fail_wrong_generation(NdbMgmd& mgmd)
{
  // Get the binary config
  Config conf;
  if (!mgmd.get_config(conf))
    return false;

  // Change generation
  if (!conf.setGeneration(conf.getGeneration() + 10))
    return false;

  // Set the modified config
  return set_config_result_contains(mgmd, conf,
                                    "Invalid generation in");
}

static bool
check_set_config_fail_wrong_name(NdbMgmd& mgmd)
{
  // Get the binary config
  Config conf;
  if (!mgmd.get_config(conf))
    return false;

  // Change name
  if (!conf.setName("NEWNAME"))
    return false;

  // Set the modified config
  return set_config_result_contains(mgmd, conf,
                                    "Invalid configuration name");
}

static bool
check_set_config_fail_wrong_primary(NdbMgmd& mgmd)
{
  // Get the binary config
  Config conf;
  if (!mgmd.get_config(conf))
    return false;

  // Change primary and thus make this configuration invalid
  if (!conf.setPrimaryMgmNode(conf.getPrimaryMgmNode()+10))
    return false;

  // Set the modified config
  return set_config_result_contains(mgmd, conf,
                                    "Not primary mgm node");
}

int runTestSetConfig(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;

  if (!mgmd.connect())
    return NDBT_FAILED;

  int result= NDBT_FAILED;
  if (
      check_set_config_invalid_content_type(mgmd) &&
      check_set_config_invalid_content_encoding(mgmd) &&
      check_set_config_too_large_content_length(mgmd) &&
      check_set_config_too_small_content_length(mgmd) &&
      check_set_config_wrong_config_length(mgmd) &&
      check_set_config_any_node(ctx, step, mgmd) &&
      check_set_config_fail_wrong_generation(mgmd) &&
      check_set_config_fail_wrong_name(mgmd) &&
      check_set_config_fail_wrong_primary(mgmd) &&
      true)
    result= NDBT_OK;

  if (!mgmd.end_session())
    result= NDBT_FAILED;

  return result;
}

int runTestSetConfigParallel(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;

  if (!mgmd.connect())
    return NDBT_FAILED;

  int result = NDBT_OK;
  int loops = ctx->getNumLoops();
  int sucessful = 0;

  int invalid_generation = 0, config_change_ongoing = 0;

  /*
    continue looping until "loops" number of successful
    changes have been made from this thread
  */
  while (sucessful < loops &&
         !ctx->isTestStopped() &&
         result == NDBT_OK)
  {
    // Get the binary config
    Config conf;
    if (!mgmd.get_config(conf))
      return NDBT_FAILED;

    /* Set the config and check for valid errors */
    mgmd.verbose(false);
    if (mgmd.set_config(conf))
    {
      /* Config change suceeded */
      sucessful++;
    }
    else
    {
      /* Config change failed */
      if (mgmd.last_error() != NDB_MGM_CONFIG_CHANGE_FAILED)
      {
        g_err << "Config change failed with unexpected error: "
              << mgmd.last_error() << endl;
        result = NDBT_FAILED;
        continue;
      }

      BaseString error(mgmd.last_error_message());
      if (error == "Invalid generation in configuration")
        invalid_generation++;
      else
      if (error == "Config change ongoing")
        config_change_ongoing++;
      else
      {
        g_err << "Config change failed with unexpected error: '"
              << error << "'" << endl;
        result = NDBT_FAILED;

      }
    }
  }

  ndbout << "Thread " << step->getStepNo()
         << ", sucess: " << sucessful
         << ", ongoing: " << config_change_ongoing
         << ", invalid_generation: " << invalid_generation << endl;
  return result;
}

int runTestSetConfigParallelUntilStopped(NDBT_Context* ctx, NDBT_Step* step)
{
  int result= NDBT_OK;
  while(!ctx->isTestStopped() &&
        (result= runTestSetConfigParallel(ctx, step)) == NDBT_OK)
    ;
  return result;
}



static bool
get_connection_parameter(NdbMgmd& mgmd,
                         const Properties& args,
                         Properties& reply)
{

  // Fill in default values of other args
  Properties call_args(args);
  if (!call_args.contains("node1"))
    call_args.put("node1", 1);
  if (!call_args.contains("node2"))
    call_args.put("node2", 1);
  if (!call_args.contains("param"))
    call_args.put("param", CFG_CONNECTION_SERVER_PORT);

  if (!mgmd.call("get connection parameter", call_args,
                 "get connection parameter reply", reply))
  {
    g_err << "get_connection_parameter: mgmd.call failed" << endl;
    return false;
  }
  return true;
}


static bool
set_connection_parameter(NdbMgmd& mgmd,
                         const Properties& args,
                         Properties& reply)
{

  // Fill in default values of other args
  Properties call_args(args);
  if (!call_args.contains("node1"))
    call_args.put("node1", 1);
  if (!call_args.contains("node2"))
    call_args.put("node2", 1);
  if (!call_args.contains("param"))
    call_args.put("param", CFG_CONNECTION_SERVER_PORT);
 if (!call_args.contains("value"))
    call_args.put("value", 37);

  if (!mgmd.call("set connection parameter", call_args,
                 "set connection parameter reply", reply))
  {
    g_err << "set_connection_parameter: mgmd.call failed" << endl;
    return false;
  }
  return true;
}


static bool
check_connection_parameter_invalid_nodeid(NdbMgmd& mgmd)
{
  for (int nodeId = MAX_NODES; nodeId < MAX_NODES+2; nodeId++){
    g_info << "Testing invalid node " << nodeId << endl;;

    Properties args;
    args.put("node1", nodeId);
    args.put("node2", nodeId);

    Properties get_result;
    if (!get_connection_parameter(mgmd, args, get_result))
      return false;

    if (!result_contains(get_result,
                         "Unable to find connection between nodes"))
        return false;

    Properties set_result;
    if (!set_connection_parameter(mgmd, args, set_result))
      return false;

    if (!failed(set_result))
        return false;

    if (!message_contains(set_result,
                          "Unable to find connection between nodes"))
        return false;
  }
  return true;
}


static bool
check_connection_parameter(NdbMgmd& mgmd)
{
  // Find a NDB node with dynamic port
  Config conf;
  if (!mgmd.get_config(conf))
    return false;

  Uint32 nodeId1 = 0;
  for(Uint32 i= 1; i < MAX_NODES; i++){
    Uint32 nodeType;
    ConfigIter iter(&conf, CFG_SECTION_NODE);
    if (iter.find(CFG_NODE_ID, i) == 0 &&
        iter.get(CFG_TYPE_OF_SECTION, &nodeType) == 0 &&
        nodeType == NDB_MGM_NODE_TYPE_NDB){
      nodeId1 = i;
      break;
    }
  }

  NodeId otherNodeId = 0;
  BaseString original_value;

  // Get current value of first connection between mgmd and other node
  for (int nodeId = 1; nodeId < MAX_NODES; nodeId++){

    g_info << "Checking if connection between " << nodeId1
           << " and " << nodeId << " exists" << endl;

    Properties args;
    args.put("node1", nodeId1);
    args.put("node2", nodeId);

    Properties result;
    if (!get_connection_parameter(mgmd, args, result))
      return false;

    if (!ok(result))
      continue;

    result.print();
    // Get the nodeid
    otherNodeId = nodeId;

    // Get original value
    if (!result.get("value", original_value))
    {
      g_err << "Failed to get original value" << endl;
      return false;
    }
    break; // Done with the loop
  }

  if (otherNodeId == 0)
  {
    g_err << "Could not find a suitable connection for test" << endl;
    return false;
  }

  Properties get_args;
  get_args.put("node1", nodeId1);
  get_args.put("node2", otherNodeId);

  {
    g_info <<  "Set new value(37 by default)" << endl;

    Properties set_args(get_args);
    Properties set_result;
    if (!set_connection_parameter(mgmd, set_args, set_result))
      return false;

    if (!ok(set_result))
      return false;
  }

  {
    g_info << "Check new value" << endl;

    Properties get_result;
    if (!get_connection_parameter(mgmd, get_args, get_result))
      return false;

    if (!ok(get_result))
      return false;

    BaseString new_value;
    if (!get_result.get("value", new_value))
    {
      g_err << "Failed to get new value" << endl;
      return false;
    }

    g_info << "new_value: " << new_value << endl;
    if (new_value != "37")
    {
      g_err << "New value was not correct, expected 37, got "
            << new_value << endl;
      return false;
    }
  }

  {
    g_info << "Restore old value" << endl;

    Properties set_args(get_args);
    if (!set_args.put("value", original_value.c_str()))
    {
      g_err << "Failed to put original_value" << endl;
      return false;
    }

    Properties set_result;
    if (!set_connection_parameter(mgmd, set_args, set_result))
      return false;

    if (!ok(set_result))
      return false;
  }

  {
    g_info << "Check restored value" << endl;
    Properties get_result;
    if (!get_connection_parameter(mgmd, get_args, get_result))
      return false;

    if (!ok(get_result))
      return false;

    BaseString restored_value;
    if (!get_result.get("value", restored_value))
    {
      g_err << "Failed to get restored value" << endl;
      return false;
    }

    if (restored_value != original_value)
    {
      g_err << "Restored value was not correct, expected "
            << original_value << ", got "
            << restored_value << endl;
      return false;
    }
    g_info << "restored_value: " << restored_value << endl;
  }

  return true;

}


int runTestConnectionParameter(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;

  if (!mgmd.connect())
    return NDBT_FAILED;

  int result= NDBT_FAILED;
  if (
      check_connection_parameter(mgmd) &&
      check_connection_parameter_invalid_nodeid(mgmd) &&
      true)
    result= NDBT_OK;

  if (!mgmd.end_session())
    result= NDBT_FAILED;

  return result;
}


int runTestConnectionParameterUntilStopped(NDBT_Context* ctx, NDBT_Step* step)
{
  int result= NDBT_OK;
  while(!ctx->isTestStopped() &&
        (result= runTestConnectionParameter(ctx, step)) == NDBT_OK)
    ;
  return result;
}


#ifdef NOT_YET
static bool
check_restart_connected(NdbMgmd& mgmd)
{
  if (!mgmd.restart())
    return false;
  return true;
 }

int runTestRestartMgmd(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;

  if (!mgmd.connect())
    return NDBT_FAILED;

  int result= NDBT_FAILED;
  if (
      check_restart_connected(mgmd) &&
      true)
    result= NDBT_OK;

  if (!mgmd.end_session())
    result= NDBT_FAILED;

  return result;
}
#endif


static bool
set_logfilter(NdbMgmd& mgmd,
              enum ndb_mgm_event_severity severity,
              int enable)
{
  struct ndb_mgm_reply reply;
  if (ndb_mgm_set_clusterlog_severity_filter(mgmd.handle(),
					     severity,
					     enable,
                                             &reply
                                             ) == -1)
  {
    g_err << "set_logfilter: ndb_mgm_set_clusterlog_severity_filter failed"
          << endl;
    return false;
  }
  return true;
}

static bool
get_logfilter(NdbMgmd& mgmd,
              enum ndb_mgm_event_severity severity,
              unsigned int* value)
{

  struct ndb_mgm_severity severity_struct;
  severity_struct.category = severity;
  if (ndb_mgm_get_clusterlog_severity_filter(mgmd.handle(),
					     &severity_struct,
					     1) != 1)
  {
    g_err << "get_logfilter: ndb_mgm_get_clusterlog_severity_filter failed"
          << endl;
    return false;
  }

  assert(value);
  *value = severity_struct.value;

  return true;
}


int runTestSetLogFilter(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;

  if (!mgmd.connect())
    return NDBT_FAILED;

  for (int i = 0; i < (int)NDB_MGM_EVENT_SEVERITY_ALL; i++)
  {
    g_info << "severity: " << i << endl;
    ndb_mgm_event_severity severity = (ndb_mgm_event_severity)i;

    // Get initial value of level
    unsigned int initial_value;
    if (!get_logfilter(mgmd, severity, &initial_value))
      return NDBT_FAILED;

    // Turn level off
    if (!set_logfilter(mgmd, severity, 0))
      return NDBT_FAILED;

    // Check it's off
    unsigned int curr_value;
    if (!get_logfilter(mgmd, severity, &curr_value))
      return NDBT_FAILED;

    if (curr_value != 0)
    {
      g_err << "Failed to turn off severity: "  << severity << endl;
      return NDBT_FAILED;
    }

    // Turn level on
    if (!set_logfilter(mgmd, severity, 1))
      return NDBT_FAILED;

    // Check it's on
    if (!get_logfilter(mgmd, severity, &curr_value))
      return NDBT_FAILED;

    if (curr_value == 0)
    {
      g_err << "Filed to turn on severity: "  << severity << endl;
      return NDBT_FAILED;
    }

    // Toggle, ie. turn off
    if (!set_logfilter(mgmd, severity, -1))
      return NDBT_FAILED;

    // Check it's off
    if (!get_logfilter(mgmd, severity, &curr_value))
      return NDBT_FAILED;

    if (curr_value != 0)
    {
      g_err << "Failed to toggle severity : "  << severity << endl;
      return NDBT_FAILED;
    }

    // Set back initial value
    if (!set_logfilter(mgmd, severity, initial_value))
      return NDBT_FAILED;

  }

  return NDBT_OK;
}


int runTestBug40922(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;

  if (!mgmd.connect())
    return NDBT_FAILED;

  int filter[] = {
    15, NDB_MGM_EVENT_CATEGORY_BACKUP,
    1, NDB_MGM_EVENT_CATEGORY_STARTUP,
    0
  };
  NdbLogEventHandle le_handle =
    ndb_mgm_create_logevent_handle(mgmd.handle(), filter);
  if (!le_handle)
    return NDBT_FAILED;

  g_info << "Calling ndb_log_event_get_next" << endl;

  struct ndb_logevent le_event;
  int r = ndb_logevent_get_next(le_handle,
                                &le_event,
                                2000);
  g_info << "ndb_log_event_get_next returned " << r << endl;

  int result = NDBT_FAILED;
  if (r == 0)
  {
    // Got timeout
    g_info << "ndb_logevent_get_next returned timeout" << endl;
    result = NDBT_OK;
  }
  else
  {
    if(r>0)
      g_err << "ERROR: Receieved unexpected event: "
            << le_event.type << endl;
    if(r<0)
      g_err << "ERROR: ndb_logevent_get_next returned error: "
            << r << endl;
  }

  ndb_mgm_destroy_logevent_handle(&le_handle);

  return result;
}


int runTestBug45497(NDBT_Context* ctx, NDBT_Step* step)
{
  int result = NDBT_OK;
  int loops = ctx->getNumLoops();
  Vector<NdbMgmd*> mgmds;

  while(true)
  {
    NdbMgmd* mgmd = new NdbMgmd();

    // Set quite short timeout
    if (!mgmd->set_timeout(1000))
    {
      result = NDBT_FAILED;
      break;
    }

    if (mgmd->connect())
    {
      mgmds.push_back(mgmd);
      g_info << "connections: " << mgmds.size() << endl;
      continue;
    }

    g_err << "Failed to make another connection, connections: "
          << mgmds.size() << endl;


    // Disconnect some connections
    int to_disconnect = 10;
    while(mgmds.size() && to_disconnect--)
    {
      g_info << "disconnnect, connections: " << mgmds.size() << endl;
      NdbMgmd* mgmd = mgmds[0];
      mgmds.erase(0);
      delete mgmd;
    }

    if (loops-- == 0)
      break;
  }

  while(mgmds.size())
  {
    NdbMgmd* mgmd = mgmds[0];
    mgmds.erase(0);
    delete mgmd;
  }

  return result;
}


static int
runTestGetVersion(NDBT_Context* ctx, NDBT_Step* step)
{

  NdbMgmd mgmd;

  if (!mgmd.connect())
    return NDBT_FAILED;

  char verStr[64];
  int major, minor, build;
  if (ndb_mgm_get_version(mgmd.handle(),
                          &major, &minor, &build,
                          sizeof(verStr), verStr) != 1)
  {
    g_err << "ndb_mgm_get_version failed,"
          << "error: " << ndb_mgm_get_latest_error_msg(mgmd.handle())
          << "desc: " << ndb_mgm_get_latest_error_desc(mgmd.handle()) << endl;
    return NDBT_FAILED;
  }

  g_info << "Using major: " << major
         << " minor: " << minor
         << " build: " << build
         << " string: " << verStr << endl;

  int l = 0;
  int loops = ctx->getNumLoops();
  while(l < loops)
  {
    char verStr2[64];
    int major2, minor2, build2;
    if (ndb_mgm_get_version(mgmd.handle(),
                            &major2, &minor2, &build2,
                            sizeof(verStr2), verStr2) != 1)
    {
      g_err << "ndb_mgm_get_version failed,"
            << "error: " << ndb_mgm_get_latest_error_msg(mgmd.handle())
            << "desc: " << ndb_mgm_get_latest_error_desc(mgmd.handle()) << endl;
      return NDBT_FAILED;
    }

    if (major != major2)
    {
      g_err << "Got different major: " << major2
            << " excpected: " << major << endl;
      return NDBT_FAILED;
    }

    if (minor != minor2)
    {
      g_err << "Got different minor: " << minor2
            << " excpected: " << minor << endl;
      return NDBT_FAILED;
    }

    if (build != build2)
    {
      g_err << "Got different build: " << build2
            << " excpected: " << build << endl;
      return NDBT_FAILED;
    }

    if (strcmp(verStr, verStr2) != 0)
    {
      g_err << "Got different verStr: " << verStr2
            << " excpected: " << verStr << endl;
      return NDBT_FAILED;
    }

    l++;
  }

  return NDBT_OK;
}


static int
runTestGetVersionUntilStopped(NDBT_Context* ctx, NDBT_Step* step)
{
  int result= NDBT_OK;
  while(!ctx->isTestStopped() &&
        (result= runTestGetVersion(ctx, step)) == NDBT_OK)
    ;
  return result;
}


int runTestDumpEvents(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;

  if (!mgmd.connect())
    return NDBT_FAILED;

  // Test with unsupported logevent_type
  {
    const Ndb_logevent_type unsupported = NDB_LE_NDBStopForced;
    g_info << "ndb_mgm_dump_events(" << unsupported << ")" << endl;

    const struct ndb_mgm_events* events =
      ndb_mgm_dump_events(mgmd.handle(), unsupported, 0, 0);
    if (events != NULL)
    {
      g_err << "ndb_mgm_dump_events returned events "
            << "for unsupported Ndb_logevent_type" << endl;
      return NDBT_FAILED;
    }

    if (ndb_mgm_get_latest_error(mgmd.handle()) != NDB_MGM_USAGE_ERROR ||
        strcmp("ndb_logevent_type 59 not supported",
               ndb_mgm_get_latest_error_desc(mgmd.handle())))
    {
      g_err << "Unexpected error for unsupported logevent type, "
            << ndb_mgm_get_latest_error(mgmd.handle())
            << ", desc: " << ndb_mgm_get_latest_error_desc(mgmd.handle())
            << endl;
      return NDBT_FAILED;
    }
  }

  // Test with nodes >= MAX_NDB_NODES
  for (int i = MAX_NDB_NODES; i < MAX_NDB_NODES + 3; i++)
  {
    g_info << "ndb_mgm_dump_events(NDB_LE_MemoryUsage, 1, "
           << i << ")" << endl;

    const struct ndb_mgm_events* events =
      ndb_mgm_dump_events(mgmd.handle(), NDB_LE_MemoryUsage, 1, &i);
    if (events != NULL)
    {
      g_err << "ndb_mgm_dump_events returned events "
            << "for too large nodeid" << endl;
      return NDBT_FAILED;
    }

    int invalid_nodeid;
    if (ndb_mgm_get_latest_error(mgmd.handle()) != NDB_MGM_USAGE_ERROR ||
        sscanf(ndb_mgm_get_latest_error_desc(mgmd.handle()),
               "invalid nodes: '%d'", &invalid_nodeid) != 1 ||
        invalid_nodeid != i)
    {
      g_err << "Unexpected error for too large nodeid, "
            << ndb_mgm_get_latest_error(mgmd.handle())
            << ", desc: " << ndb_mgm_get_latest_error_desc(mgmd.handle())
            << endl;
      return NDBT_FAILED;
    }

  }

  int l = 0;
  int loops = ctx->getNumLoops();
  while (l<loops)
  {
    const Ndb_logevent_type supported[] =
      {
        NDB_LE_MemoryUsage,
        NDB_LE_BackupStatus,
        (Ndb_logevent_type)0
      };

    // Test with supported logevent_type
    for (int i = 0; supported[i]; i++)
    {
      g_info << "ndb_mgm_dump_events(" << supported[i] << ")" << endl;

      struct ndb_mgm_events* events =
        ndb_mgm_dump_events(mgmd.handle(), supported[i], 0, 0);
      if (events == NULL)
      {
        g_err << "ndb_mgm_dump_events failed, type: " << supported[i]
              << ", error: " << ndb_mgm_get_latest_error(mgmd.handle())
              << ", msg: " << ndb_mgm_get_latest_error_msg(mgmd.handle())
              << endl;
        return NDBT_FAILED;
      }

      if (events->no_of_events < 0)
      {
        g_err << "ndb_mgm_dump_events returned a negative number of events: "
              << events->no_of_events << endl;
        free(events);
        return NDBT_FAILED;
      }

      g_info << "Got " << events->no_of_events << " events" << endl;
      free(events);
    }

    l++;
  }

  return NDBT_OK;
}

int runTestStatusAfterStop(NDBT_Context* ctx, NDBT_Step* step)
{
  NdbMgmd mgmd;
  mgmd.set_timeout(50); // Short timeout, should be upgraded

  if (!mgmd.connect())
    return NDBT_FAILED;

  ndb_mgm_node_type
    node_types[2] = { NDB_MGM_NODE_TYPE_NDB,
                      NDB_MGM_NODE_TYPE_UNKNOWN };

  // Test: get status, stop node, get status again
  printf("Getting status\n");
  ndb_mgm_cluster_state *cs = ndb_mgm_get_status2(mgmd.handle(), node_types);
  if (cs == NULL)
  {
    printf("%s (%d)\n", ndb_mgm_get_latest_error_msg(mgmd.handle()),
           ndb_mgm_get_latest_error(mgmd.handle()));
    return NDBT_FAILED;
  }

  int nodeId = 0;
  for(int i=0; i < cs->no_of_nodes; i++ )
  {
    ndb_mgm_node_state *ns = cs->node_states + i;
    printf("Node ID: %d  status:%d\n", ns->node_id, ns->node_status);
    if (nodeId == 0 && ns->node_type == NDB_MGM_NODE_TYPE_NDB)
      nodeId = ns->node_id;
  }
  free(cs);
  cs = NULL;

  printf("Stopping data node\n");
  // We only stop 1 data node, in this case NodeId=2
  int nodes[1] =  { nodeId };
  int stopped = ndb_mgm_restart2(mgmd.handle(), NDB_ARRAY_SIZE(nodes), nodes, 
                                 0, 0, 1);
  if (stopped < 0)
  {
    printf("ndb_mgm_stop failed, '%s' (%d)\n",
           ndb_mgm_get_latest_error_msg(mgmd.handle()),
           ndb_mgm_get_latest_error(mgmd.handle()));
    return NDBT_FAILED;
  }

  printf("Stopped %d data node(s)\n", stopped);

  printf("Getting status\n");
  cs = ndb_mgm_get_status2(mgmd.handle(), node_types);
  if (cs == NULL)
  {
    printf("%s (%d)\n", ndb_mgm_get_latest_error_msg(mgmd.handle()),
           ndb_mgm_get_latest_error(mgmd.handle()));
    return NDBT_FAILED;
  }
  for(int i=0; i < cs->no_of_nodes; i++ )
  {
    ndb_mgm_node_state *ns = cs->node_states + i;
    printf("Node ID: %d  status:%d\n", ns->node_id, ns->node_status);
  }
  free(cs);

  NdbRestarter res;
  res.startAll();
  res.waitClusterStarted();

  return NDBT_OK;
}

NDBT_TESTSUITE(testMgm);
DRIVER(DummyDriver); /* turn off use of NdbApi */
TESTCASE("ApiSessionFailure",
	 "Test failures in MGMAPI session"){
  INITIALIZER(runTestApiSession);

}
TESTCASE("ApiConnectTimeout",
	 "Connect timeout tests for MGMAPI"){
  INITIALIZER(runTestApiConnectTimeout);

}
TESTCASE("ApiTimeoutBasic",
	 "Basic timeout tests for MGMAPI"){
  INITIALIZER(runTestApiTimeoutBasic);

}
TESTCASE("ApiGetStatusTimeout",
	 "Test timeout for MGMAPI getStatus"){
  INITIALIZER(runTestApiGetStatusTimeout);

}
TESTCASE("ApiGetConfigTimeout",
	 "Test timeouts for mgmapi get_configuration"){
  INITIALIZER(runTestMgmApiGetConfigTimeout);

}
TESTCASE("ApiMgmEventTimeout",
	 "Test timeouts for mgmapi get_configuration"){
  INITIALIZER(runTestMgmApiEventTimeout);

}
TESTCASE("ApiMgmStructEventTimeout",
	 "Test timeouts for mgmapi get_configuration"){
  INITIALIZER(runTestMgmApiStructEventTimeout);

}
TESTCASE("SetConfig",
	 "Tests the ndb_mgm_set_configuration function"){
  INITIALIZER(runSetConfig);
}
TESTCASE("CheckConfig",
	 "Connect to each ndb_mgmd and check they have the same configuration"){
  INITIALIZER(runCheckConfig);
}
TESTCASE("TestReloadConfig",
	 "Test of 'reload config'"){
  INITIALIZER(runTestReloadConfig);
}
TESTCASE("TestSetConfig",
	 "Test of 'set config'"){
  INITIALIZER(runTestSetConfig);
}
TESTCASE("TestSetConfigParallel",
	 "Test of 'set config' from 5 threads"){
  STEPS(runTestSetConfigParallel, 5);
}
TESTCASE("GetConfig", "Run ndb_mgm_get_configuration in parallel"){
  STEPS(runGetConfig, 100);
}
TESTCASE("TestStatus",
	 "Test status and status2"){
  INITIALIZER(runTestStatus);

}
TESTCASE("TestStatus200",
	 "Test status and status2 with 200 threads"){
  STEPS(runTestStatus, 200);

}
TESTCASE("TestGetNodeId",
	 "Test 'get nodeid'"){
  INITIALIZER(runTestGetNodeId);
}
TESTCASE("TestGetVersion",
 	 "Test 'get version' and 'ndb_mgm_get_version'"){
  STEPS(runTestGetVersion, 20);
}
TESTCASE("TestTransporterConnect",
	 "Test 'transporter connect'"){
  INITIALIZER(runTestTransporterConnect);
}
TESTCASE("TestConnectionParameter",
	 "Test 'get/set connection parameter'"){
  INITIALIZER(runTestConnectionParameter);
}
TESTCASE("TestSetLogFilter",
	 "Test 'set logfilter' and 'get info clusterlog'"){
  INITIALIZER(runTestSetLogFilter);
}
#ifdef NOT_YET
TESTCASE("TestRestartMgmd",
        "Test restart of ndb_mgmd(s)"){
  INITIALIZER(runTestRestartMgmd);
}
#endif
TESTCASE("Bug40922",
	 "Make sure that ndb_logevent_get_next returns when "
         "called with a timeout"){
  INITIALIZER(runTestBug40922);
}
TESTCASE("Stress",
	 "Run everything while changing config"){
  STEP(runTestGetNodeIdUntilStopped);
  STEP(runSetConfigUntilStopped);
  STEPS(runGetConfigUntilStopped, 10);
  STEPS(runGetConfigFromNodeUntilStopped, 10);
  STEPS(runTestStatusUntilStopped, 10);
  STEPS(runTestGetVersionUntilStopped, 5);
  STEP(runSleepAndStop);
}
TESTCASE("Stress2",
	 "Run everything while changing config in parallel"){
  STEP(runTestGetNodeIdUntilStopped);
  STEPS(runTestSetConfigParallelUntilStopped, 5);
  STEPS(runGetConfigUntilStopped, 10);
  STEPS(runGetConfigFromNodeUntilStopped, 10);
  STEPS(runTestStatusUntilStopped, 10);
  STEPS(runTestGetVersionUntilStopped, 5);
  STEP(runSleepAndStop);
}
TESTCASE("Bug45497",
         "Connect to ndb_mgmd until it can't handle more connections"){
  STEP(runTestBug45497);
}
TESTCASE("TestGetVersion",
 	 "Test 'get version' and 'ndb_mgm_get_version'"){
  STEPS(runTestGetVersion, 20);
}
TESTCASE("TestDumpEvents",
 	 "Test 'dump events'"){
  STEPS(runTestDumpEvents, 1);
}
TESTCASE("TestStatusAfterStop",
 	 "Test get status after stop "){
  STEPS(runTestStatusAfterStop, 1);
}
NDBT_TESTSUITE_END(testMgm);

int main(int argc, const char** argv){
  ndb_init();
  NDBT_TESTSUITE_INSTANCE(testMgm);
  testMgm.setCreateTable(false);
  testMgm.setRunAllTables(true);
  return testMgm.execute(argc, argv);
}

template class Vector<NdbMgmd*>;
