1 /* Copyright (C) 2008 MySQL AB, 2008, 2009 Sun Microsystems, Inc.
2     All rights reserved. Use is subject to license terms.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
23 
24 #ifndef NDB_MGMD_HPP
25 #define NDB_MGMD_HPP
26 
27 #include <mgmapi.h>
28 #include <mgmapi_internal.h>
29 
30 #include <BaseString.hpp>
31 #include <Properties.hpp>
32 
33 #include <OutputStream.hpp>
34 #include <SocketInputStream2.hpp>
35 
36 #include "../../src/mgmsrv/Config.hpp"
37 
38 class NdbMgmd {
39   BaseString m_connect_str;
40   NdbMgmHandle m_handle;
41   Uint32 m_nodeid;
42   bool m_verbose;
43   unsigned int m_timeout;
error(const char * msg,...)44   void error(const char* msg, ...) ATTRIBUTE_FORMAT(printf, 2, 3)
45   {
46     if (!m_verbose)
47       return;
48 
49     va_list args;
50     printf("NdbMgmd::");
51     va_start(args, msg);
52     vprintf(msg, args);
53     va_end(args);
54     printf("\n");
55 
56     if (m_handle){
57       ndbout_c(" error: %d, line: %d, desc: %s",
58                ndb_mgm_get_latest_error(m_handle),
59                ndb_mgm_get_latest_error_line(m_handle),
60                ndb_mgm_get_latest_error_desc(m_handle));
61     }
62   }
63 public:
NdbMgmd()64   NdbMgmd() :
65     m_handle(NULL), m_nodeid(0), m_verbose(true), m_timeout(0)
66     {
67       const char* connect_string= getenv("NDB_CONNECTSTRING");
68       if (connect_string)
69         m_connect_str.assign(connect_string);
70     }
71 
~NdbMgmd()72   ~NdbMgmd()
73   {
74     close();
75   }
76 
close(void)77   void close(void)
78   {
79     if (m_handle)
80     {
81       ndb_mgm_disconnect_quiet(m_handle);
82       ndb_mgm_destroy_handle(&m_handle);
83       m_handle = NULL;
84     }
85   }
86 
handle(void) const87   NdbMgmHandle handle(void) const {
88     return m_handle;
89   }
90 
socket(void) const91   NDB_SOCKET_TYPE socket(void) const {
92     return _ndb_mgm_get_socket(m_handle);
93   }
94 
nodeid(void) const95   NodeId nodeid(void) const {
96     return m_nodeid;
97   }
98 
getConnectString() const99   const char* getConnectString() const {
100     return m_connect_str.c_str();
101   }
102 
setConnectString(const char * connect_str)103   void setConnectString(const char* connect_str) {
104     m_connect_str.assign(connect_str);
105   }
106 
set_timeout(unsigned int timeout)107   bool set_timeout(unsigned int timeout) {
108     m_timeout = timeout;
109     if (m_handle &&
110         ndb_mgm_set_timeout(m_handle, timeout) != 0)
111     {
112       error("set_timeout: failed to set timeout on handle");
113       return false;
114     }
115     return true;
116   }
117 
verbose(bool yes=true)118   void verbose(bool yes = true){
119     m_verbose= yes;
120   }
121 
last_error(void) const122   int last_error(void) const {
123     return ndb_mgm_get_latest_error(m_handle);
124   }
125 
last_error_message(void) const126   const char* last_error_message(void) const {
127     return ndb_mgm_get_latest_error_desc(m_handle);
128   }
129 
connect(const char * connect_string=NULL,int num_retries=0,int retry_delay_in_seconds=0)130   bool connect(const char* connect_string = NULL,
131                int num_retries = 0, int retry_delay_in_seconds = 0) {
132     assert(m_handle == NULL);
133     m_handle= ndb_mgm_create_handle();
134     if (!m_handle){
135       error("connect: ndb_mgm_create_handle failed");
136       return false;
137     }
138 
139     if (ndb_mgm_set_connectstring(m_handle,
140                                   connect_string ?
141                                   connect_string : getConnectString()) != 0){
142       error("connect: ndb_mgm_set_connectstring failed");
143       return false;
144     }
145 
146     if (m_timeout > 0 &&
147         ndb_mgm_set_timeout(m_handle, m_timeout) != 0){
148       error("connect: ndb_mgm_set_timeout failed");
149       return false;
150     }
151 
152     if (ndb_mgm_connect(m_handle,num_retries,retry_delay_in_seconds,0) != 0){
153       error("connect: ndb_mgm_connect failed");
154       return false;
155     }
156 
157   // Handshake with the server to make sure it's really there
158     int major, minor, build;
159     char buf[16];
160     if (ndb_mgm_get_version(m_handle, &major, &minor, &build,
161                             sizeof(buf), buf) != 1)
162     {
163         error("connect: ndb_get_version failed");
164         return false;
165     }
166     //printf("connected to ndb_mgmd version %d.%d.%d\n",
167     //        major, minor, build);
168 
169     if ((m_nodeid = ndb_mgm_get_mgmd_nodeid(m_handle)) == 0){
170       error("connect: could not get nodeid of connected mgmd");
171       return false;
172     }
173 
174     return true;
175   }
176 
is_connected(void)177   bool is_connected(void) {
178     if (!m_handle){
179       error("is_connected: no handle");
180       return false;
181     }
182     if (!ndb_mgm_is_connected(m_handle)){
183       error("is_connected: not connected");
184       return false;
185     }
186     return true;
187   }
188 
disconnect(void)189   bool disconnect(void) {
190     if (ndb_mgm_disconnect(m_handle) != 0){
191       error("disconnect: ndb_mgm_disconnect failed");
192       return false;
193     }
194 
195     ndb_mgm_destroy_handle(&m_handle);
196     m_handle = NULL;
197 
198     return true;
199   }
200 
restart(bool abort=false)201   bool restart(bool abort = false) {
202     if (!is_connected()){
203       error("restart: not connected");
204       return false;
205     }
206 
207     int disconnect= 0;
208     int node_list= m_nodeid;
209     int restarted= ndb_mgm_restart3(m_handle,
210                                     1,
211                                     &node_list,
212                                     false, /* initial */
213                                     false, /* nostart */
214                                     abort,
215                                     &disconnect);
216 
217     if (restarted != 1){
218       error("restart: failed to restart node %d, restarted: %d",
219             m_nodeid, restarted);
220       return false;
221     }
222     return true;
223   }
224 
call(const char * cmd,const Properties & args,const char * cmd_reply,Properties & reply,const char * bulk=NULL,bool name_value_pairs=true)225   bool call(const char* cmd, const Properties& args,
226             const char* cmd_reply, Properties& reply,
227             const char* bulk = NULL,
228             bool name_value_pairs = true){
229 
230     if (!is_connected()){
231       error("call: not connected");
232       return false;
233     }
234 
235     SocketOutputStream out(socket());
236 
237     if (out.println(cmd)){
238       error("call: println failed at line %d", __LINE__);
239       return false;
240     }
241 
242     Properties::Iterator iter(&args);
243     const char *name;
244     while((name = iter.next()) != NULL) {
245       PropertiesType t;
246       Uint32 val_i;
247       Uint64 val_64;
248       BaseString val_s;
249 
250       args.getTypeOf(name, &t);
251       switch(t) {
252       case PropertiesType_Uint32:
253 	args.get(name, &val_i);
254 	if (out.println("%s: %d", name, val_i)){
255           error("call: println failed at line %d", __LINE__);
256           return false;
257         }
258 	break;
259       case PropertiesType_Uint64:
260 	args.get(name, &val_64);
261 	if (out.println("%s: %Ld", name, val_64)){
262           error("call: println failed at line %d", __LINE__);
263           return false;
264         }
265 	break;
266       case PropertiesType_char:
267 	args.get(name, val_s);
268 	if (out.println("%s: %s", name, val_s.c_str())){
269           error("call: println failed at line %d", __LINE__);
270           return false;
271         }
272 	break;
273       default:
274       case PropertiesType_Properties:
275 	/* Illegal */
276         abort();
277 	break;
278       }
279     }
280 
281     // Emtpy line terminates argument list
282     if (out.print("\n")){
283       error("call: print('\n') failed at line %d", __LINE__);
284       return false;
285     }
286 
287     // Send any bulk data
288     if (bulk && out.println(bulk)){
289       error("call: print('<bulk>') failed at line %d", __LINE__);
290       return false;
291     }
292 
293     BaseString buf;
294     SocketInputStream2 in(socket());
295     if (cmd_reply)
296     {
297       // Read the reply header and compare against "cmd_reply"
298       if (!in.gets(buf)){
299         error("call: could not read reply command");
300         return false;
301       }
302 
303       // 1. Check correct reply header
304       if (buf != cmd_reply){
305         error("call: unexpected reply command, expected: '%s', got '%s'",
306               cmd_reply, buf.c_str());
307         return false;
308       }
309     }
310 
311     // 2. Read lines until empty line
312     int line = 1;
313     while(in.gets(buf)){
314 
315       // empty line -> end of reply
316       if (buf == "")
317         return true;
318 
319       if (name_value_pairs)
320       {
321         // 3a. Read colon separated name value pair, split
322         // the name value pair on first ':'
323         Vector<BaseString> name_value_pair;
324         if (buf.split(name_value_pair, ":", 2) != 2){
325           error("call: illegal name value pair '%s' received", buf.c_str());
326           return false;
327         }
328 
329         reply.put(name_value_pair[0].trim(" ").c_str(),
330                   name_value_pair[1].trim(" ").c_str());
331       }
332       else
333       {
334         // 3b. Not name value pair, save the line into "reply"
335         // using unique key
336         reply.put("line", line++, buf.c_str());
337       }
338     }
339 
340     error("call: should never come here");
341     reply.print();
342     abort();
343     return false;
344   }
345 
346 
get_config(Config & config)347   bool get_config(Config& config){
348 
349     if (!is_connected()){
350       error("get_config: not connected");
351       return false;
352     }
353 
354     struct ndb_mgm_configuration* conf =
355       ndb_mgm_get_configuration(m_handle,0);
356     if (!conf) {
357       error("get_config: ndb_mgm_get_configuration failed");
358       return false;
359     }
360 
361     config.m_configValues= conf;
362     return true;
363   }
364 
set_config(Config & config)365   bool set_config(Config& config){
366 
367     if (!is_connected()){
368       error("set_config: not connected");
369       return false;
370     }
371 
372     if (ndb_mgm_set_configuration(m_handle,
373                                   config.values()) != 0)
374     {
375       error("set_config: ndb_mgm_set_configuration failed");
376       return false;
377     }
378     return true;
379   }
380 
end_session(void)381   bool end_session(void){
382     if (!is_connected()){
383       error("end_session: not connected");
384       return false;
385     }
386 
387     if (ndb_mgm_end_session(m_handle) != 0){
388       error("end_session: ndb_mgm_end_session failed");
389       return false;
390     }
391     return true;
392   }
393 
394   // Pretty printer for 'ndb_mgm_node_type'
395   class NodeType {
396     BaseString m_str;
397   public:
NodeType(Uint32 node_type)398     NodeType(Uint32 node_type) {
399       const char* str= NULL;
400       const char* alias=
401         ndb_mgm_get_node_type_alias_string((ndb_mgm_node_type)node_type, &str);
402       m_str.assfmt("%s(%s)", alias, str);
403     }
404 
c_str()405     const char* c_str() { return m_str.c_str(); }
406   };
407 };
408 
409 #endif
410