galera_check.pl 114 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765
  1. #!/usr/bin/perl
  2. # This tool is "fat-packed": most of its dependent modules are embedded
  3. # in this file.
  4. # https://github.com/Tusamarco/proxy_sql_tools/blob/master/galera_check.pl
  5. #######################################
  6. #
  7. # ProxySQL galera check v1
  8. #
  9. # Author Marco Tusa
  10. # Copyright (C) (2016 - 2020)
  11. #
  12. #
  13. #THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
  14. #WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  15. #MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  16. #
  17. #This program is free software; you can redistribute it and/or modify it under
  18. #the terms of the GNU General Public License as published by the Free Software
  19. #Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
  20. #systems, you can issue `man perlgpl' or `man perlartistic' to read these
  21. #licenses.
  22. #
  23. #You should have received a copy of the GNU General Public License along with
  24. #this program; if not, write to the Free Software Foundation, Inc., 59 Temple
  25. #Place, Suite 330, Boston, MA 02111-1307 USA.
  26. #######################################
  27. package galera_check ;
  28. use Time::HiRes qw(gettimeofday);
  29. use strict;
  30. use DBI;
  31. use Getopt::Long;
  32. use Pod::Usage;
  33. $Getopt::Long::ignorecase = 0;
  34. my $Param = {};
  35. my $user = "admin";
  36. my $pass = "admin";
  37. my $help = '';
  38. my $host = '' ;
  39. my $debug = 0 ;
  40. my %hostgroups;
  41. my $mysql_connect_timeout=6;
  42. my %processState;
  43. my %processCommand;
  44. my @HGIds;
  45. ######################################################################
  46. #Local functions
  47. ######################################################################
  48. sub URLDecode {
  49. my $theURL = $_[0];
  50. $theURL =~ tr/+/ /;
  51. $theURL =~ s/%([a-fA-F0-9]{2,2})/chr(hex($1))/eg;
  52. $theURL =~ s/<!--(.|\n)*-->//g;
  53. return $theURL;
  54. }
  55. sub URLEncode {
  56. my $theURL = $_[0];
  57. $theURL =~ s/([\W])/"%" . uc(sprintf("%2.2x",ord($1)))/eg;
  58. return $theURL;
  59. }
  60. # return a proxy object
  61. sub get_proxy($$$$){
  62. my $dns = shift;
  63. my $user = shift;
  64. my $pass = shift;
  65. my $debug = shift;
  66. my $proxynode = ProxySqlNode->new();
  67. $proxynode->dns($dns);
  68. $proxynode->user($user);
  69. $proxynode->password($pass);
  70. $proxynode->debug($debug);
  71. return $proxynode;
  72. }
  73. sub main{
  74. # ============================================================================
  75. #+++++ INITIALIZATION
  76. # ============================================================================
  77. if($#ARGV < 0){
  78. pod2usage(-verbose => 2) ;
  79. exit 1;
  80. }
  81. if($#ARGV < 3){
  82. #given a ProxySQL scheduler
  83. #limitation we will pass the whole set of params as one
  84. # and will split after
  85. @ARGV = split('\ ',$ARGV[0]);
  86. }
  87. $Param->{user} = '';
  88. $Param->{log} = undef ;
  89. $Param->{password} = '';
  90. $Param->{host} = '';
  91. $Param->{port} = 3306;
  92. $Param->{debug} = 0;
  93. $Param->{processlist} = 0;
  94. $Param->{OS} = $^O;
  95. $Param->{main_segment} = 1;
  96. $Param->{retry_up} = 0;
  97. $Param->{retry_down} = 0;
  98. $Param->{print_execution} = 1;
  99. $Param->{development} = 0;
  100. $Param->{development_time} = 2;
  101. $Param->{active_failover} = 0;
  102. $Param->{single_writer} = 1;
  103. $Param->{writer_is_reader} = 1;
  104. $Param->{check_timeout} = 800;
  105. $Param->{ssl_certs_path} = undef;
  106. my $run_pid_dir = "/tmp" ;
  107. #if (
  108. GetOptions(
  109. 'user|u:s' => \$Param->{user},
  110. 'password|p:s' => \$Param->{password},
  111. 'host|h:s' => \$host,
  112. 'port|P:i' => \$Param->{port},
  113. 'debug|d:i' => \$Param->{debug},
  114. 'log:s' => \$Param->{log},
  115. 'hostgroups|H:s'=> \$Param->{hostgroups},
  116. 'main_segment|S:s'=> \$Param->{main_segment},
  117. 'retry_up:i' => \$Param->{retry_up},
  118. 'retry_down:i' => \$Param->{retry_down},
  119. 'execution_time:i' => \$Param->{print_execution},
  120. 'development:i' => \$Param->{development},
  121. 'development_time:i' => \$Param->{development_time},
  122. 'single_writer:i' => \$Param->{single_writer},
  123. 'writer_is_also_reader:i' => \$Param->{writer_is_reader},
  124. 'active_failover:i' => \$Param->{active_failover},
  125. 'check_timeout:i' => \$Param->{check_timeout},
  126. 'ssl_certs_path:s' => \$Param->{ssl_certs_path},
  127. 'help|?' => \$Param->{help}
  128. ) or pod2usage(2);
  129. pod2usage(-verbose => 2) if $Param->{help};
  130. die print Utils->print_log(1,"Option --hostgroups not specified.\n") unless defined($Param->{hostgroups});
  131. die print Utils->print_log(1,"Option --host not specified.\n") unless defined $Param->{host};
  132. die print Utils->print_log(1,"Option --user not specified.\n") unless defined $Param->{user};
  133. die print Utils->print_log(1,"Option --port not specified.\n") unless defined $Param->{port};
  134. die print Utils->print_log(1,"Option --active_failover has an invalid value ($Param->{active_failover}).\n"
  135. ."Valid values are:\n"
  136. ." 0 [default] do not make failover\n"
  137. ." 1 make failover only if HG 8000 is specified in ProxySQL mysl_servers\n"
  138. ." 2 use PXC_CLUSTER_VIEW to identify a server in the same segment\n"
  139. ." 3 do whatever to keep service up also failover to another segment (use PXC_CLUSTER_VIEW) ") unless $Param->{active_failover} < 4;
  140. #die "Option --log not specified. We need a place to log what is going on, don't we?\n" unless defined $Param->{log};
  141. print Utils->print_log(2,"Option --log not specified. We need a place to log what is going on, don't we?\n") unless defined $Param->{log};
  142. if($Param->{debug}){
  143. Utils::debugEnv();
  144. }
  145. $Param->{host} = URLDecode($host);
  146. my $dsn = "DBI:mysql:host=$Param->{host};port=$Param->{port}";
  147. if(defined $Param->{user}){
  148. $user = "$Param->{user}";
  149. }
  150. if(defined $Param->{password}){
  151. $pass = "$Param->{password}";
  152. }
  153. my $hg =$Param->{hostgroups};
  154. $hg =~ s/[\:,\,]/_/g;
  155. my $base_path = "${run_pid_dir}/proxysql_galera_check_${hg}.pid";
  156. #============================================================================
  157. # Execution
  158. #============================================================================
  159. if(defined $Param->{log}){
  160. open(FH, '>>', $Param->{log}."_".$hg.".log") or die Utils->print_log(1,"cannot open file");
  161. FH->autoflush if $Param->{development} < 2;
  162. select FH;
  163. }
  164. #checks for ssl cert path and identify if accessible.
  165. #If defined and not accessible exit with an error
  166. if(defined $Param->{ssl_certs_path}){
  167. my $ssl_path = $Param->{ssl_certs_path};
  168. if (-d $ssl_path) {
  169. # directory called cgi-bin exists
  170. if(-e $ssl_path."/client-key.pem"
  171. && -e $ssl_path."/client-cert.pem"
  172. && -e $ssl_path."/ca.pem"){
  173. print Utils->print_log(4," SSL Directory exists and all the files are there $ssl_path")
  174. }
  175. else{
  176. print Utils->print_log(1,"SSL Path (ssl_certs_path) declared and accessible [$ssl_path]. But certification files must have specific names:\n \t\t client-key.pem \n \t\t client-cert.pem \n \t\t ca.pem \n");
  177. exit 1
  178. }
  179. }
  180. else{
  181. # ssl path declared but not exists, exit with error
  182. print Utils->print_log(1,"SSL Path (ssl_certs_path) declared but not existing \n \t\t $ssl_path \n \t\t Please create directory and assign the right to access it to the ProxySQL user \n");
  183. exit 1;
  184. }
  185. }
  186. if($Param->{development} < 2){
  187. if(!-e $base_path){
  188. `echo "$$" > $base_path`
  189. }
  190. else{
  191. my $existing_pid=`cat $base_path`;
  192. my $exists = kill 0, $existing_pid;
  193. if($exists > 0){
  194. print STDOUT "Another process is running using the same HostGroup and settings,\n Or orphan pid file. check in $base_path \n";
  195. print Utils->print_log(1,"Another process is running using the same HostGroup and settings,\n Or orphan pid file. check in $base_path \n");
  196. exit 1;
  197. }
  198. else{
  199. `echo "$$" > $base_path`;
  200. }
  201. }
  202. }
  203. # for test only purpose comment for prod
  204. my $xx =1;
  205. my $y =0;
  206. $xx=2000000000 if($Param->{development} > 0);
  207. while($y < $xx){
  208. ++$y ;
  209. my $start = gettimeofday();
  210. if($Param->{debug} >= 1){
  211. print Utils->print_log(3,"START EXECUTION\n");
  212. }
  213. my $proxy_sql_node = get_proxy($dsn, $user, $pass ,$Param->{debug}) ;
  214. $proxy_sql_node->retry_up($Param->{retry_up});
  215. $proxy_sql_node->retry_down($Param->{retry_down});
  216. $proxy_sql_node->hostgroups($Param->{hostgroups}) ;
  217. $proxy_sql_node->require_failover($Param->{active_failover});
  218. $proxy_sql_node->check_timeout($Param->{check_timeout});
  219. $proxy_sql_node->connect();
  220. # create basic galera cluster object and fill info
  221. $proxy_sql_node->set_galera_cluster();
  222. my $galera_cluster = $proxy_sql_node->get_galera_cluster();
  223. if( defined $galera_cluster){
  224. $galera_cluster->main_segment($Param->{main_segment});
  225. $galera_cluster->cluster_identifier($hg);
  226. $galera_cluster->get_nodes();
  227. }
  228. # Retrive the nodes state
  229. if(defined $galera_cluster->nodes){
  230. $galera_cluster->process_nodes();
  231. }
  232. #Analyze nodes state from ProxySQL prospective;
  233. if(defined $galera_cluster->nodes){
  234. my %action_node = $proxy_sql_node->evaluate_nodes($galera_cluster);
  235. }
  236. if(defined $proxy_sql_node->action_nodes){
  237. $proxy_sql_node->push_changes;
  238. }
  239. my $end = gettimeofday();
  240. print Utils->print_log(3,"END EXECUTION Total Time(ms):".($end - $start) * 1000 ."\n") if $Param->{print_execution} >0;
  241. if($Param->{debug} >= 1){
  242. print Utils->print_log(3,"\n");
  243. }
  244. FH->flush();
  245. $proxy_sql_node->disconnect();
  246. #debug braket
  247. sleep $Param->{development_time} if($Param->{development} > 0);
  248. }
  249. if(defined $Param->{log}){
  250. close FH; # in the end
  251. }
  252. `rm -f $base_path`;
  253. exit(0);
  254. }
  255. # ############################################################################
  256. # Run the program.
  257. # ############################################################################
  258. exit main(@ARGV);
  259. {
  260. package Galeracluster;
  261. use threads;
  262. use threads::shared;
  263. use strict;
  264. use warnings;
  265. use Time::HiRes qw(gettimeofday usleep);
  266. sub new {
  267. my $class = shift;
  268. my $SQL_get_mysql_servers=" SELECT a.* FROM runtime_mysql_servers a join stats_mysql_connection_pool b on a.hostname=b.srv_host and a.port=b.srv_port and a.hostgroup_id=b.hostgroup WHERE b.status not in ('OFFLINE_HARD','SHUNNED') ";
  269. # Variable section for looping values
  270. #Generalize object for now I have conceptualize as:
  271. # Cluster (generic container)
  272. # Cluster->{name} This is the cluster name
  273. # Cluster->{nodes} the nodes in the cluster Map by node name
  274. # Cluster->{status} cluster status [Primary|not Primary]
  275. # Cluster->{size} cluster status [Primary|not Primary]
  276. # Cluster->{singlenode}=0; 0 if false 1 if true meaning only one ACTIVE node in the cluster
  277. # Cluster->{haswriter}=0; 0 if false 1 if true at least a node is fully active as writer
  278. # Cluster->{singlewriter}=1; 0 if false 1 if true this cluster can have ONLY one writer a time [true default]
  279. my $self = {
  280. _name => undef,
  281. _hosts => {},
  282. _status => undef,
  283. _size => {},
  284. _singlenode => 0,
  285. _haswriter => 0,
  286. _singlewriter => 1,
  287. _main_segment => 0,
  288. _SQL_get_mysql_servers => $SQL_get_mysql_servers,
  289. _hostgroups => undef,
  290. _dbh_proxy => undef,
  291. _debug => 0,
  292. _monitor_user => undef,
  293. _monitor_password => undef,
  294. _nodes => {},
  295. _nodes_maint => {},
  296. _check_timeout => 100, #timeout in ms
  297. _cluster_identifier => undef,
  298. _hg_writer_id => 0,
  299. _hg_reader_id => 0,
  300. _ssl_certificates_path => undef,
  301. _writer_is_reader => 0,
  302. _reader_nodes => [] ,
  303. _writer_nodes => [] ,
  304. _has_failover_node =>0,
  305. _writers =>0,
  306. #_hg => undef,
  307. };
  308. bless $self, $class;
  309. return $self;
  310. }
  311. sub ssl_certificates_path{
  312. my ( $self, $in ) = @_;
  313. $self->{_ssl_certificates_path} = $in if defined($in);
  314. return $self->{_ssl_certificates_path};
  315. }
  316. sub has_failover_node{
  317. my ( $self, $in ) = @_;
  318. $self->{_has_failover_node} = $in if defined($in);
  319. return $self->{_has_failover_node};
  320. }
  321. sub writer_nodes{
  322. my ( $self, $in ) = @_;
  323. $self->{_writer_nodes} = $in if defined($in);
  324. return $self->{_writer_nodes};
  325. }
  326. sub reader_nodes{
  327. my ( $self, $in ) = @_;
  328. $self->{_reader_nodes} = $in if defined($in);
  329. return $self->{_reader_nodes};
  330. }
  331. sub cluster_identifier{
  332. my ( $self, $in ) = @_;
  333. $self->{_cluster_identifier} = $in if defined($in);
  334. return $self->{_cluster_identifier};
  335. }
  336. sub main_segment{
  337. my ( $self, $main_segment ) = @_;
  338. $self->{_main_segment} = $main_segment if defined($main_segment);
  339. return $self->{_main_segment};
  340. }
  341. sub check_timeout{
  342. my ( $self, $check_timeout ) = @_;
  343. $self->{_check_timeout} = $check_timeout if defined($check_timeout);
  344. return $self->{_check_timeout};
  345. }
  346. sub debug{
  347. my ( $self, $debug ) = @_;
  348. $self->{_debug} = $debug if defined($debug);
  349. return $self->{_debug};
  350. }
  351. sub dbh_proxy{
  352. my ( $self, $dbh_proxy ) = @_;
  353. $self->{_dbh_proxy} = $dbh_proxy if defined($dbh_proxy);
  354. return $self->{_dbh_proxy};
  355. }
  356. sub name {
  357. my ( $self, $name ) = @_;
  358. $self->{_name} = $name if defined($name);
  359. return $self->{_name};
  360. }
  361. sub nodes {
  362. my ( $self, $nodes ) = @_;
  363. $self->{_nodes} = $nodes if defined($nodes);
  364. return $self->{_nodes};
  365. }
  366. sub nodes_maint {
  367. my ( $self, $nodes ) = @_;
  368. $self->{_nodes_maint} = $nodes if defined($nodes);
  369. return $self->{_nodes_maint};
  370. }
  371. sub status {
  372. my ( $self, $status ) = @_;
  373. $self->{_status} = $status if defined($status);
  374. return $self->{_status};
  375. }
  376. sub size {
  377. my ( $self, $size ) = @_;
  378. $self->{_size} = $size if defined($size);
  379. return $size->{_size};
  380. }
  381. sub singlenode {
  382. my ( $self, $singlenode ) = @_;
  383. $self->{_singlenode} = $singlenode if defined($singlenode);
  384. return $self->{_singlenode};
  385. }
  386. sub haswriter {
  387. my ( $self, $haswriter ) = @_;
  388. $self->{_haswriter} = $haswriter if defined($haswriter);
  389. return $self->{_haswriter};
  390. }
  391. sub singlewriter {
  392. my ( $self, $singlewriter ) = @_;
  393. $self->{_singlewriter} = $singlewriter if defined($singlewriter);
  394. return $self->{_singlewriter};
  395. }
  396. sub writer_is_reader {
  397. my ( $self, $writer_is_reader ) = @_;
  398. $self->{_writer_is_reader} = $writer_is_reader if defined($writer_is_reader);
  399. return $self->{_writer_is_reader};
  400. }
  401. sub writers {
  402. my ( $self, $in ) = @_;
  403. $self->{_writers} = $in if defined($in);
  404. return $self->{_writers};
  405. }
  406. sub hostgroups {
  407. my ( $self, $hostgroups ) = @_;
  408. $self->{_hostgroups} = $hostgroups if defined($hostgroups);
  409. return $self->{_hostgroups};
  410. }
  411. sub hg_writer_id {
  412. my ( $self, $hostgroups ) = @_;
  413. $self->{_hg_writer_id} = $hostgroups if defined($hostgroups);
  414. return $self->{_hg_writer_id};
  415. }
  416. sub hg_reader_id {
  417. my ( $self, $hostgroups ) = @_;
  418. $self->{_hg_reader_id} = $hostgroups if defined($hostgroups);
  419. return $self->{_hg_reader_id};
  420. }
  421. sub monitor_user{
  422. my ( $self, $monitor_user ) = @_;
  423. $self->{_monitor_user} = $monitor_user if defined($monitor_user);
  424. return $self->{_monitor_user};
  425. }
  426. sub monitor_password {
  427. my ( $self, $monitor_password ) = @_;
  428. $self->{_monitor_password} = $monitor_password if defined($monitor_password);
  429. return $self->{_monitor_password};
  430. }
  431. # this function is used to identify the nodes in the cluster
  432. # using the HG as reference
  433. sub get_nodes{
  434. my ( $self) = @_;
  435. my $dbh = $self->{_dbh_proxy};
  436. my $cmd =$self->{_SQL_get_mysql_servers}." AND hostgroup_id IN (".join(",",sort keys(%{$self->hostgroups})).") order by hostgroup_id, hostname";
  437. my $sth = $dbh->prepare($cmd);
  438. $sth->execute();
  439. my $i = 1;
  440. my $locHg = $self->{_hostgroups};
  441. my $ssl_certificates = "";
  442. #if a ssl certificate path is defined, will create the path for each certificate and add to the dns string
  443. if(defined $self->{_ssl_certificates_path}){
  444. $ssl_certificates = ";mysql_ssl_client_key=".$self->{_ssl_certificates_path}."/client-key.pem"
  445. .";mysql_ssl_client_cert=".$self->{_ssl_certificates_path}."/client-cert.pem"
  446. .";mysql_ssl_ca_file=".$self->{_ssl_certificates_path}."/ca.pem"
  447. }
  448. while (my $ref = $sth->fetchrow_hashref()) {
  449. my $ssl_options="" ;
  450. my $node = GaleraNode->new();
  451. $node->debug($self->debug);
  452. $node->use_ssl($ref->{use_ssl});
  453. $node->hostgroups($ref->{hostgroup_id});
  454. if($node->{_hostgroups} > 8000
  455. && exists $locHg->{$node->{_hostgroups}}){
  456. $self->{_has_failover_node} = 1;
  457. }
  458. $node->ip($ref->{hostname});
  459. $node->port($ref->{port});
  460. if($node->use_ssl gt 0 ){
  461. $ssl_options = ";mysql_ssl=1";
  462. if($self->debug){print Utils->print_log(4," Galera cluster node " . $node->ip.":". $node->port.":HG=".$node->hostgroups." Using SSL ($ssl_options)\n" ) }
  463. if(defined $self->{_ssl_certificates_path}){
  464. $ssl_options = $ssl_options . $ssl_certificates;
  465. if($self->debug){print Utils->print_log(4," Certificates also in use ($self->{_ssl_certificates_path})\n")}
  466. }
  467. }
  468. $node->dns("DBI:mysql:host=".$node->ip.";port=".$node->port.";mysql_connect_timeout=$mysql_connect_timeout".$ssl_options);
  469. $node->weight($ref->{weight});
  470. $node->connections($ref->{max_connections});
  471. $node->user($self->{_monitor_user});
  472. $node->password($self->{_monitor_password});
  473. $node->proxy_status($ref->{status});
  474. $node->comment($ref->{comment});
  475. $node->set_retry_up_down($self->{_cluster_identifier});
  476. $node->gtid_port($ref->{gtid_port});
  477. $node->compression($ref->{compression});
  478. $node->max_latency($ref->{max_latency_ms});
  479. $node->max_replication_lag($ref->{max_replication_lag});
  480. $self->{_nodes}->{$i++}=$node;
  481. $node->debug($self->debug);
  482. if($self->debug){print Utils->print_log(3," Galera cluster node " . $node->ip.":". $node->port.":HG=".$node->hostgroups."\n" ) }
  483. }
  484. if($self->debug){print Utils->print_log(3," Galera cluster nodes loaded \n") ; }
  485. }
  486. #Processing the nodes in the cluster and identify which node is active and which is to remove
  487. sub process_nodes{
  488. my ( $self ) = @_;
  489. my $nodes = $self->{_nodes} ;
  490. my $start = gettimeofday();
  491. my $run_milliseconds=0;
  492. my $init =0;
  493. my $irun = 1;
  494. my %Threads;
  495. my $new_nodes ={} ;
  496. my $processed_nodes ={} ;
  497. #using multiple threads to connect if a node is present in more than one HG it will have 2 threads
  498. while($irun){
  499. $irun = 0;
  500. foreach my $key (sort keys %{$self->{_nodes}}){
  501. if(!exists $Threads{$key}){
  502. if($self->debug){print Utils->print_log(3, " Creating new thread to manage server check:".
  503. $self->{_nodes}->{$key}->ip.":".
  504. $self->{_nodes}->{$key}->port.":HG".$self->{_nodes}->{$key}->hostgroups."\n" ) }
  505. $new_nodes->{$key} = $self->{_nodes}->{$key};
  506. $new_nodes->{$key}->{_process_status} = -1;
  507. $new_nodes->{$key}->{_ssl_certificates_path} = $self->ssl_certificates_path;
  508. # debug senza threads comment next line
  509. $Threads{$key}=threads->create(sub {return get_node_info($self,$key)});
  510. #DEBUG Without threads uncomment from here
  511. #next unless $new_nodes->{$key} = get_node_info($self,$key);
  512. #evaluate_joined_node($self, $key, $new_nodes, $processed_nodes) ;
  513. # to here
  514. }
  515. }
  516. ##DEBUG SENZA THREADS commenta da qui
  517. foreach my $thr (sort keys %Threads) {
  518. if($new_nodes->{$thr}->{_process_status} eq -100){
  519. next;
  520. }
  521. if ($Threads{$thr}->is_running()) {
  522. my $tid = $Threads{$thr}->tid;
  523. #print " - Thread $tid running\n";
  524. if($run_milliseconds > $self->{_check_timeout} ){
  525. if($self->debug >=0){
  526. my $timeout = ($run_milliseconds - $self->{_check_timeout});
  527. print print Utils->print_log(2,"Check timeout Node ip : $new_nodes->{$thr}->{_ip} , THID " . $tid." (taken: $run_milliseconds max_allowed: $self->{_check_timeout} over for ms: $timeout \n")
  528. }
  529. $irun = 0 ;
  530. }
  531. else{
  532. $irun = 1;
  533. }
  534. }
  535. elsif ( $Threads{$thr}->is_joinable()) {
  536. my $tid = $Threads{$thr}->tid;
  537. ( $new_nodes->{$thr} ) = $Threads{$thr}->join;
  538. #$processed_nodes =
  539. evaluate_joined_node($self, $thr, $new_nodes, $processed_nodes) ;
  540. if($self->debug){print Utils->print_log(3," Thread joined : " . $tid."\n" ) }
  541. #print " - Results for thread $tid:\n";
  542. #print " - Thread $tid has been joined\n";
  543. }
  544. #print ".";
  545. }
  546. ## a qui
  547. if($self->debug){$run_milliseconds = (gettimeofday() -$start ) *1000};
  548. #sleep for a time equal to the half of the timeout to save cpu cicle
  549. #usleep(($self->{_check_timeout} * 1000)/2);
  550. }
  551. $self->{_nodes} = $new_nodes;
  552. if($self->debug){$run_milliseconds = (gettimeofday() -$start ) *1000};
  553. if($debug>=3){
  554. foreach my $key (sort keys %{$new_nodes}){
  555. if($new_nodes->{$key}->{_process_status} == 1){
  556. print Utils->print_log(4,$new_nodes->{$key}->{_ip}.":".$new_nodes->{$key}->{_hostgroups}." Processed \n");
  557. }
  558. else{
  559. print Utils->print_log(4,$new_nodes->{$key}->{_ip}.":".$new_nodes->{$key}->{_hostgroups}." NOT Processed\n");
  560. }
  561. }
  562. }
  563. if($self->debug){print Utils->print_log(3," Multi Thread execution done in : " . $run_milliseconds. "(ms) \n" )}
  564. }
  565. sub evaluate_joined_node($$$$){
  566. my $self = shift;
  567. my $thr = shift;
  568. my $new_nodes = shift;
  569. my $processed_nodes = shift;
  570. #count the number of nodes by segment
  571. if($new_nodes->{$thr}->{_proxy_status} ne "OFFLINE_SOFT"
  572. && $new_nodes->{$thr}->{_proxy_status} ne "SHUNNED"
  573. && ($new_nodes->{$thr}->{_process_status} < 0 ||
  574. !exists $processed_nodes->{$new_nodes->{$thr}->{_ip}})
  575. && defined $new_nodes->{$thr}->{_wsrep_segment}
  576. ){
  577. $self->{_size}->{$new_nodes->{$thr}->{_wsrep_segment}} = (($self->{_size}->{$new_nodes->{$thr}->{_wsrep_segment}}|| 0) +1);
  578. $processed_nodes->{$new_nodes->{$thr}->{_ip}}=$self->{_size}->{$new_nodes->{$thr}->{_wsrep_segment}};
  579. }
  580. #assign size to HG
  581. if($new_nodes->{$thr}->{_proxy_status} ne "OFFLINE_SOFT"
  582. && defined $new_nodes->{$thr}->{_wsrep_segment}
  583. ){
  584. $self->{_hostgroups}->{$new_nodes->{$thr}->{_hostgroups}}->{_size} = ($self->{_hostgroups}->{$new_nodes->{$thr}->{_hostgroups}}->{_size}) + 1;
  585. }
  586. #checks for ONLINE writer(s)
  587. if(defined $new_nodes->{$thr}->{_read_only}
  588. && $new_nodes->{$thr}->{_read_only} eq "OFF"
  589. && ($new_nodes->{$thr}->{_proxy_status} eq "ONLINE" || $new_nodes->{$thr}->{_proxy_status} eq "OFFLINE_SOFT")
  590. && ($new_nodes->{$thr}->{_hostgroups} == $self->hg_writer_id || $new_nodes->{$thr}->{_hostgroups} == ($self->hg_writer_id +9000))
  591. ){
  592. if($new_nodes->{$thr}->{_hostgroups} == $self->hg_writer_id
  593. && $new_nodes->{$thr}->{_proxy_status} eq "ONLINE"
  594. ){
  595. $self->{_haswriter} = 1 ;
  596. $self->{_writers} = $self->{_writers} +1;
  597. }
  598. push (@{$self->{_writer_nodes}}, "$new_nodes->{$thr}->{_ip}:$new_nodes->{$thr}->{_port}");
  599. }
  600. elsif(($new_nodes->{$thr}->{_proxy_status} eq "ONLINE" || $new_nodes->{$thr}->{_proxy_status} eq "OFFLINE_SOFT")
  601. && ($new_nodes->{$thr}->{_hostgroups} == $self->hg_reader_id || $new_nodes->{$thr}->{_hostgroups} == ($self->hg_reader_id +9000))
  602. ){
  603. push (@{$self->{_reader_nodes}}, "$new_nodes->{$thr}->{_ip}:$new_nodes->{$thr}->{_port}");
  604. }
  605. else{
  606. if($self->debug
  607. && $new_nodes->{$thr}->{_hostgroups} == $self->hg_writer_id){
  608. print Utils->print_log(3," Not a writer :" .$new_nodes->{$thr}->{_ip} . " HG: $new_nodes->{$thr}->{_hostgroups} \n" )
  609. }
  610. }
  611. # check if under maintenance
  612. if($new_nodes->{$thr}->{_proxy_status} eq "OFFLINE_SOFT"
  613. && $new_nodes->{$thr}->{_pxc_maint_mode} eq "MAINTENANCE"){
  614. $self->{_nodes_maint}->{$thr} = $new_nodes->{$thr};
  615. }
  616. #return $processed_nodes;
  617. }
  618. sub get_node_info($$){
  619. my $self = shift;
  620. my $key = shift;
  621. my $nodes =shift;
  622. my ( $node ) = $self->{_nodes}->{$key};
  623. if(!defined $node->get_node_info()){
  624. $node->{_process_status}=-100;
  625. }
  626. return $node;
  627. }
  628. }
  629. {
  630. package GaleraNode;
  631. #Node Proxy States
  632. sub new {
  633. my $class = shift;
  634. my $SQL_get_variables="SHOW GLOBAL VARIABLES LIKE 'wsrep%";
  635. my $SQL_get_status="SHOW GLOBAL STATUS LIKE 'wsrep%";
  636. my $SQL_get_read_only="SHOW GLOBAL VARIABLES LIKE 'read_only'";
  637. # Variable section for looping values
  638. #Generalize object for now I have conceptualize as:
  639. # Node (generic container)
  640. # Node->{name} This is the cluster name
  641. # Node->{IP}
  642. # Node->{hostgroups}
  643. # Node->{clustername} This is the cluster name
  644. # Node->{read_only} Read only node
  645. # Node->{wsrep_status} node status (OPEN 0,Primary 1,Joiner 2,Joined 3,Synced 4,Donor 5)
  646. # Node->{wsrep_rejectqueries} (NON, ALL,ALL_KILL)
  647. # Node->{wsrep_donorrejectqueries} If true the node when donor
  648. # Node->{wsrep_connected}=0; if false 1 if true meaning only one ACTIVE node in the cluster
  649. # Node->{wsrep_desinccount}=0; 0 if false 1 if true at least a node is fully active as writer
  650. # Node->{wsrep_ready} ON -OFF
  651. my $self = {
  652. _name => undef,
  653. _ip => undef,
  654. _port => 3306,
  655. _hostgroups => undef,
  656. _clustername => undef,
  657. _read_only => undef,
  658. _wsrep_status => -1,
  659. _wsrep_rejectqueries => undef,
  660. _wsrep_donorrejectqueries => undef,
  661. _wsrep_connected => undef,
  662. _wsrep_desinccount => undef,
  663. _wsrep_ready => undef,
  664. _wsrep_provider => [],
  665. _wsrep_segment => 1000,
  666. _wsrep_pc_weight => 1,
  667. _SQL_get_variables => $SQL_get_variables,
  668. _SQL_get_status=> $SQL_get_status,
  669. _SQL_get_read_only=> $SQL_get_read_only,
  670. _dns => undef,
  671. _user => undef,
  672. _password => undef,
  673. _debug => 0,
  674. _port => undef,
  675. _proxy_status => undef,
  676. _weight => 1,
  677. _connections => 2000,
  678. _cluster_status => undef,
  679. _cluster_size => 0,
  680. _process_status => -1,
  681. _MOVE_UP_OFFLINE => 1000, #move a node from OFFLINE_SOFT
  682. _MOVE_UP_HG_CHANGE => 1010, #move a node from HG 9000 (plus hg id) to reader HG
  683. _MOVE_DOWN_HG_CHANGE => 3001, #move a node from original HG to maintenance HG (HG 9000 (plus hg id) ) kill all existing connections
  684. _MOVE_DOWN_OFFLINE => 3010 , # move node to OFFLINE_soft keep existing connections, no new connections.
  685. _MOVE_TO_MAINTENANCE => 3020 , # move node to OFFLINE_soft keep existing connections, no new connections because maintenance.
  686. _MOVE_OUT_MAINTENANCE => 3030 , # move node to OFFLINE_soft keep existing connections, no new connections because maintenance.
  687. _INSERT_READ => 4010, # Insert a node in the reader host group
  688. _INSERT_WRITE => 4020, # Insert a node in the writer host group
  689. _DELETE_NODE => 5000, # this remove the node from the hostgroup
  690. _SAVE_RETRY => 9999, # this reset the retry counter in the comment
  691. #_MOVE_SWAP_READER_TO_WRITER => 5001, #Future use
  692. #_MOVE_SWAP_WRITER_TO_READER => 5010, #Future use
  693. _retry_down_saved => 0, # number of retry on a node before declaring it as failed.
  694. _retry_up_saved => 0, # number of retry on a node before declaring it OK.
  695. _comment => undef,
  696. _gtid_port => 0,
  697. _compression => 0,
  698. _use_ssl => 0,
  699. _ssl_certificates_path => undef,
  700. _max_latency => 0,
  701. _max_replication_lag => 0,
  702. _wsrep_gcomm_uuid => undef,
  703. _wsrep_local_index => 0,
  704. _pxc_maint_mode => undef,
  705. };
  706. bless $self, $class;
  707. return $self;
  708. }
  709. sub ssl_certificates_path{
  710. my ( $self, $in ) = @_;
  711. $self->{_ssl_certificates_path} = $in if defined($in);
  712. return $self->{_ssl_certificates_path};
  713. }
  714. sub max_replication_lag{
  715. my ( $self, $in ) = @_;
  716. $self->{_max_replication_lag} = $in if defined($in);
  717. return $self->{_max_replication_lag};
  718. }
  719. sub max_latency{
  720. my ( $self, $in ) = @_;
  721. $self->{_max_latency} = $in if defined($in);
  722. return $self->{_max_latency};
  723. }
  724. sub use_ssl{
  725. my ( $self, $in ) = @_;
  726. $self->{_use_ssl} = $in if defined($in);
  727. return $self->{_use_ssl};
  728. }
  729. sub compression{
  730. my ( $self, $in ) = @_;
  731. $self->{_compression} = $in if defined($in);
  732. return $self->{_compression};
  733. }
  734. sub gtid_port{
  735. my ( $self, $in ) = @_;
  736. $self->{_gtid_port} = $in if defined($in);
  737. return $self->{_gtid_port};
  738. }
  739. sub pxc_maint_mode{
  740. my ( $self, $in ) = @_;
  741. $self->{_pxc_maint_mode} = $in if defined($in);
  742. return $self->{_pxc_maint_mode};
  743. }
  744. sub wsrep_local_index{
  745. my ( $self, $in ) = @_;
  746. $self->{_wsrep_local_index} = $in if defined($in);
  747. return $self->{_wsrep_local_index};
  748. }
  749. sub comment{
  750. my ( $self, $in ) = @_;
  751. $self->{_comment} = $in if defined($in);
  752. return $self->{_comment};
  753. }
  754. sub wsrep_gcomm_uuid{
  755. my ( $self, $in ) = @_;
  756. $self->{_wsrep_gcomm_uuid} = $in if defined($in);
  757. return $self->{_wsrep_gcomm_uuid};
  758. }
  759. sub retry_down_saved{
  760. my ( $self, $in ) = @_;
  761. $self->{_retry_down_saved} = $in if defined($in);
  762. return $self->{_retry_down_saved};
  763. }
  764. sub retry_up_saved{
  765. my ( $self, $in ) = @_;
  766. $self->{_retry_up_saved} = $in if defined($in);
  767. return $self->{_retry_up_saved};
  768. }
  769. sub process_status {
  770. my ( $self, $process_status ) = @_;
  771. $self->{_process_status} = $process_status if defined($process_status);
  772. return $self->{_process_status};
  773. }
  774. sub debug{
  775. my ( $self, $debug ) = @_;
  776. $self->{_debug} = $debug if defined($debug);
  777. return $self->{_debug};
  778. }
  779. sub SAVE_RETRY {
  780. my ( $self) = @_;
  781. return $self->{_SAVE_RETRY};
  782. }
  783. sub MOVE_UP_OFFLINE {
  784. my ( $self) = @_;
  785. return $self->{_MOVE_UP_OFFLINE};
  786. }
  787. sub MOVE_UP_HG_CHANGE {
  788. my ( $self) = @_;
  789. return $self->{_MOVE_UP_HG_CHANGE};
  790. }
  791. sub MOVE_DOWN_OFFLINE {
  792. my ( $self) = @_;
  793. return $self->{_MOVE_DOWN_OFFLINE};
  794. }
  795. sub MOVE_TO_MAINTENANCE {
  796. my ( $self) = @_;
  797. return $self->{_MOVE_TO_MAINTENANCE};
  798. }
  799. sub MOVE_OUT_MAINTENANCE {
  800. my ( $self) = @_;
  801. return $self->{_MOVE_OUT_MAINTENANCE};
  802. }
  803. sub MOVE_DOWN_HG_CHANGE {
  804. my ( $self) = @_;
  805. return $self->{_MOVE_DOWN_HG_CHANGE};
  806. }
  807. sub DELETE_NODE {
  808. my ( $self) = @_;
  809. return $self->{_DELETE_NODE};
  810. }
  811. sub INSERT_READ {
  812. my ( $self) = @_;
  813. return $self->{_INSERT_READ};
  814. }
  815. sub INSERT_WRITE {
  816. my ( $self) = @_;
  817. return $self->{_INSERT_WRITE};
  818. }
  819. sub cluster_status {
  820. my ( $self, $status ) = @_;
  821. $self->{_cluster_status} = $status if defined($status);
  822. return $self->{_cluster_status};
  823. }
  824. sub cluster_size {
  825. my ( $self, $size ) = @_;
  826. $self->{_cluster_size} = $size if defined($size);
  827. return $size->{_cluster_size};
  828. }
  829. sub weight {
  830. my ( $self, $weight ) = @_;
  831. $self->{_weight} = $weight if defined($weight);
  832. return $self->{_weight};
  833. }
  834. sub connections {
  835. my ( $self, $connections ) = @_;
  836. $self->{_connections} = $connections if defined($connections);
  837. return $self->{_connections};
  838. }
  839. sub proxy_status {
  840. my ( $self, $status ) = @_;
  841. $self->{_proxy_status} = $status if defined($status);
  842. return $self->{_proxy_status};
  843. }
  844. sub dns {
  845. my ( $self, $dns ) = @_;
  846. $self->{_dns} = $dns if defined($dns);
  847. return $self->{_dns};
  848. }
  849. sub user{
  850. my ( $self, $user ) = @_;
  851. $self->{_user} = $user if defined($user);
  852. return $self->{_user};
  853. }
  854. sub password {
  855. my ( $self, $password ) = @_;
  856. $self->{_password} = $password if defined($password);
  857. return $self->{_password};
  858. }
  859. sub name {
  860. my ( $self, $name ) = @_;
  861. $self->{_name} = $name if defined($name);
  862. return $self->{_name};
  863. }
  864. sub ip {
  865. my ( $self, $ip ) = @_;
  866. $self->{_ip} = $ip if defined($ip);
  867. return $self->{_ip};
  868. }
  869. sub port {
  870. my ( $self, $port ) = @_;
  871. $self->{_port} = $port if defined($port);
  872. return $self->{_port};
  873. }
  874. sub hostgroups {
  875. my ( $self, $hostgroups ) = @_;
  876. $self->{_hostgroups} = $hostgroups if defined($hostgroups);
  877. return $self->{_hostgroups};
  878. }
  879. sub clustername {
  880. my ( $self, $clustername ) = @_;
  881. $self->{_clustername} = $clustername if defined($clustername);
  882. return $self->{_clustername};
  883. }
  884. sub read_only {
  885. my ( $self, $read_only ) = @_;
  886. $self->{_read_only} = $read_only if defined($read_only);
  887. return $self->{_read_only};
  888. }
  889. sub wsrep_status {
  890. my ( $self, $wsrep_status ) = @_;
  891. $self->{_wsrep_status} = $wsrep_status if defined($wsrep_status);
  892. return $self->{_wsrep_status};
  893. }
  894. sub wsrep_rejectqueries {
  895. my ( $self, $wsrep_rejectqueries ) = @_;
  896. $self->{_wsrep_rejectqueries} = $wsrep_rejectqueries if defined($wsrep_rejectqueries);
  897. return $self->{_wsrep_rejectqueries};
  898. }
  899. sub wsrep_donorrejectqueries {
  900. my ( $self, $wsrep_donorrejectqueries ) = @_;
  901. $self->{_wsrep_donorrejectqueries} = $wsrep_donorrejectqueries if defined($wsrep_donorrejectqueries);
  902. return $self->{_wsrep_donorrejectqueries};
  903. }
  904. sub wsrep_connected {
  905. my ( $self, $wsrep_connected ) = @_;
  906. $self->{_wsrep_connected} = $wsrep_connected if defined($wsrep_connected);
  907. return $self->{_wsrep_connected};
  908. }
  909. sub wsrep_desinccount {
  910. my ( $self, $wsrep_desinccount ) = @_;
  911. $self->{_wsrep_desinccount} = $wsrep_desinccount if defined($wsrep_desinccount);
  912. return $self->{_wsrep_desinccount};
  913. }
  914. sub wsrep_ready {
  915. my ( $self, $wsrep_ready ) = @_;
  916. $self->{_wsrep_ready} = $wsrep_ready if defined($wsrep_ready);
  917. return $self->{_wsrep_ready};
  918. }
  919. sub wsrep_segment {
  920. my ( $self, $wsrep_segment ) = @_;
  921. $self->{_wsrep_segment} = $wsrep_segment if defined($wsrep_segment);
  922. return $self->{_wsrep_segment};
  923. }
  924. sub wsrep_pc_weight {
  925. my ( $self, $wsrep_pc_weight ) = @_;
  926. $self->{_wsrep_pc_weight} = $wsrep_pc_weight if defined($wsrep_pc_weight);
  927. return $self->{_wsrep_pc_weight};
  928. }
  929. sub wsrep_provider {
  930. my ( $self, $wsrep_provider ) = @_;
  931. my ( @array)= @{$wsrep_provider} ;
  932. my %provider_map ;
  933. foreach my $item (@array){
  934. my @items = split('\=', $item);
  935. $provider_map{Utils::trim($items[0])}=$items[1];
  936. }
  937. ($self->{_wsrep_provider}) = {%provider_map} ;
  938. $self->wsrep_segment($provider_map{"gmcast.segment"});
  939. $self->wsrep_pc_weight($provider_map{"pc.weight"});
  940. return $self->{_wsrep_provider};
  941. }
  942. sub get_node_info($$){
  943. my ( $self ) = @_;
  944. if($self->debug >=1){
  945. print Utils->print_log(4," Node check START "
  946. .$self->{_ip}
  947. .":".$self->{_port}
  948. .":HG".$self->{_hostgroups}
  949. ."\n" );
  950. }
  951. if($self->debug >=1){
  952. print Utils->print_log(4," Getting connection START "
  953. .$self->{_ip}
  954. .":".$self->{_port}
  955. .":HG".$self->{_hostgroups}." \n" );
  956. }
  957. my $dbh = Utils::get_connection($self->{_dns},$self->{_user},$self->{_password},' ');
  958. if(!defined $dbh){
  959. print Utils->print_log(1," Node is not responding setting it as SHUNNED (internally) (ProxySQL bug - #2658)"
  960. .$self->{_ip}
  961. .":".$self->{_port}
  962. .":HG".$self->{_hostgroups}." \n" );
  963. $self->{_proxy_status} = "SHUNNED";
  964. return $self ;
  965. }
  966. if($self->debug >=1){
  967. print Utils->print_log(4," Getting connection END "
  968. .$self->{_ip}
  969. .":".$self->{_port}
  970. .":HG".$self->{_hostgroups}." \n" );
  971. }
  972. if($self->debug >=1){
  973. print Utils->print_log(4," Getting NODE info START "
  974. .$self->{_ip}
  975. .":".$self->{_port}
  976. .":HG".$self->{_hostgroups}." \n" );
  977. }
  978. my $variables = Utils::get_variables($dbh,0);
  979. my $status = Utils::get_status_by_name($dbh,0,"wsrep_%");
  980. my $pxc_view = Utils::get_pxc_clusterview($dbh, $status->{wsrep_gcomm_uuid} );
  981. if($self->debug >=1){
  982. print Utils->print_log(4," Getting NODE info END "
  983. .$self->{_ip}
  984. .":".$self->{_port}
  985. .":HG".$self->{_hostgroups}." \n" );
  986. }
  987. $self->{_name} = $variables->{wsrep_node_name};
  988. $self->{_clustername} = $variables->{wsrep_cluster_name};
  989. $self->{_read_only} = $variables->{read_only};
  990. $self->{_wsrep_rejectqueries} = $variables->{wsrep_reject_queries};
  991. #print "AAAAAAAAAAAAAAAAAAAAA $self->{_ip} $self->{_wsrep_rejectqueries} \n";
  992. $self->{_wsrep_donorrejectqueries} = $variables->{wsrep_sst_donor_rejects_queries};
  993. my ( @provider ) = split('\;', $variables->{wsrep_provider_options});
  994. $self->{_pxc_maint_mode} = $variables->{pxc_maint_mode};
  995. $self->wsrep_provider( [ @provider]) ;
  996. $self->{_wsrep_status} = $status->{wsrep_local_state};
  997. $self->{_wsrep_connected} = $status->{wsrep_connected};
  998. $self->{_wsrep_desinccount} = $status->{wsrep_desync_count};
  999. $self->{_wsrep_ready} = $status->{wsrep_ready};
  1000. $self->{_cluster_status} = $status->{wsrep_cluster_status};
  1001. $self->{_cluster_size} = $status->{wsrep_cluster_size};
  1002. $self->{_wsrep_gcomm_uuid} = $status->{wsrep_gcomm_uuid};
  1003. $self->{wsrep_segment} = ($self->{_wsrep_provider}->{"gmcast.segment"} );
  1004. $self->{wsrep_segment} =~ s/^\s+|\s+$//g;
  1005. $self->{_wsrep_local_index} = $pxc_view->{local_index};
  1006. if($self->{wsrep_segment} == 0){
  1007. $self->{_wsrep_segment} = $pxc_view->{segment};
  1008. }
  1009. $dbh->disconnect if (defined $dbh);
  1010. #sleep 5;
  1011. $self->{_process_status} = 1;
  1012. if($self->debug>=1){
  1013. print Utils->print_log(4," Node check END "
  1014. .$self->{_ip}
  1015. .":".$self->{_port}
  1016. .":HG".$self->{_hostgroups}
  1017. ."\n" );}
  1018. return $self;
  1019. }
  1020. sub set_retry_up_down(){
  1021. my ( $self, $hg ) = @_;
  1022. if($self->debug >=1){print Utils->print_log(4,"Calculate retry from comment Node:".$self->ip." port:".$self->port . " hg:".$self->hostgroups ." Time IN \n");}
  1023. my %comments = split /[;=]/, $self->{_comment};
  1024. if(exists $comments{$hg."_retry_up"}){
  1025. $self->{_retry_up_saved} = $comments{$hg."_retry_up"};
  1026. }
  1027. else{
  1028. $self->{_retry_up_saved} = 0;
  1029. }
  1030. if(exists $comments{$hg."_retry_down"}){
  1031. $self->{_retry_down_saved} = $comments{$hg."_retry_down"};
  1032. }
  1033. else{
  1034. $self->{_retry_down_saved} = 0;
  1035. }
  1036. my $removeUp=$hg."_retry_up=".$self->{_retry_up_saved}.";";
  1037. my $removeDown=$hg."_retry_down=".$self->{_retry_down_saved}.";";
  1038. $self->{_comment} =~ s/$removeDown//ig ;
  1039. $self->{_comment} =~ s/$removeUp//ig ;
  1040. if($self->debug >=1){print Utils->print_log(4,"Calculate retry from comment Node:".$self->ip." port:".$self->port . " hg:".$self->hostgroups ." Time OUT \n");}
  1041. }
  1042. sub get_retry_up(){
  1043. my ( $self,$in) = @_;
  1044. $self->{_retry_up_saved} = $in if defined($in);
  1045. return $self->{_retry_up_saved};
  1046. }
  1047. sub get_retry_down(){
  1048. my ( $self,$in) = @_;
  1049. $self->{_retry_down_saved} = $in if defined($in);
  1050. return $self->{_retry_down_saved};
  1051. }
  1052. sub promote_writer(){
  1053. my ( $self,$proxynode,$Galera_cluster,$exclude_delete ) = @_;
  1054. if($self->{_hostgroups} > 8000){
  1055. print Utils->print_log(3,"Special Backup - Group found! I am electing a node to writer following the indications\n This Node Try to become the new"
  1056. ." WRITER for HG $proxynode->{_hg_writer_id} Server details: "
  1057. .$self->{_ip}
  1058. .":".$self->{_port}
  1059. .":HG".$self->{_hostgroups}
  1060. ."\n" );
  1061. }
  1062. print Utils->print_log(3,"This Node Try to become a WRITER promoting to HG $proxynode->{_hg_writer_id} "
  1063. .$self->{_ip}
  1064. .":".$self->{_port}
  1065. .":HG ".$self->{_hostgroups}
  1066. ."\n" );
  1067. #my $dbh = Utils::get_connection($self->{_dns},$self->{_user},$self->{_password},' ');
  1068. #if(!defined $dbh){
  1069. # return undef;
  1070. #}
  1071. #(9000 + $proxynode->{_hg_writer_id})
  1072. my $proxy_sql_command= "INSERT INTO mysql_servers (hostname,hostgroup_id,port,weight,max_connections,use_ssl,compression,max_latency_ms) VALUES ('$self->{_ip}',$proxynode->{_hg_writer_id},$self->{_port},$self->{_weight},$self->{_connections},$self->{_use_ssl},$self->{_compression},$self->{_max_latency});";
  1073. if($Galera_cluster->{_singlewriter} > 0){
  1074. my $delete = "DELETE from mysql_servers where hostgroup_id in ($proxynode->{_hg_writer_id},".(9000 + $proxynode->{_hg_writer_id}).") AND STATUS = 'ONLINE'".$exclude_delete;
  1075. print Utils->print_log(2," DELETE from writer group as: "
  1076. ." SQL:" .$delete
  1077. ."\n" );
  1078. $proxynode->{_dbh_proxy}->do($delete) or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1079. }
  1080. #if the writer is NOT a reader by default remove it from reader groups also the
  1081. if($Galera_cluster->{_writer_is_reader} < 1 ){
  1082. my $delete = "DELETE from mysql_servers where hostgroup_id in ($proxynode->{_hg_reader_id},".(9000 + $proxynode->{_hg_reader_id}).") and hostname = '$self->{_ip}' and port=$self->{_port} ";
  1083. $proxynode->{_dbh_proxy}->do($delete) or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1084. }
  1085. $proxynode->{_dbh_proxy}->do($proxy_sql_command) or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1086. $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1087. $proxynode->{_dbh_proxy}->do("SAVE MYSQL SERVERS TO DISK") or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1088. print Utils->print_log(2," Move node:"
  1089. .$self->{_ip}.":"
  1090. .$self->{_port}
  1091. .$self->{_weight}
  1092. .$self->{_connections}
  1093. .$proxynode->{hg_writer_id}
  1094. ." SQL:" .$proxy_sql_command
  1095. ."\n" );
  1096. return 1;
  1097. }
  1098. sub move_to_writer_hg(){
  1099. my ( $self ) = @_;
  1100. print Utils->print_log(3,"This Node Try to become a WRITER set READ_ONLY to 0 "
  1101. .$self->{_ip}
  1102. .":".$self->{_port}
  1103. .":HG".$self->{_hostgroups}
  1104. ."\n" );
  1105. my $dbh = Utils::get_connection($self->{_dns},$self->{_user},$self->{_password},' ');
  1106. if(!defined $dbh){
  1107. return undef;
  1108. }
  1109. if($dbh->do("SET GLOBAL READ_ONLY=0")){
  1110. print Utils->print_log(3,"This Node NOW HAS READ_ONLY = 0 "
  1111. .$self->{_ip}
  1112. .":".$self->{_port}
  1113. .":HG".$self->{_hostgroups}
  1114. ."\n" );
  1115. }
  1116. else{
  1117. die "Couldn't execute statement: " . $dbh->errstr;
  1118. }
  1119. $dbh->disconnect if (defined $dbh);
  1120. #or die "Couldn't execute statement: " . $dbh->errstr;
  1121. return 1;
  1122. }
  1123. }
  1124. {
  1125. package ProxySqlNode;
  1126. sub new {
  1127. my $class = shift;
  1128. my $SQL_get_monitor = "select variable_name name,variable_value value from global_variables where variable_name in( 'mysql-monitor_username','mysql-monitor_password','mysql-monitor_read_only_timeout' ) order by 1";
  1129. my $SQL_get_hostgroups = "select distinct hostgroup_id hg_isd from runtime_mysql_servers order by 1;";
  1130. my $SQL_get_rep_hg = "select writer_hostgroup,reader_hostgroup from mysql_replication_hostgroups order by 1;";
  1131. my $SQL_get_pxc_cluster_view = "select * from performance_schema.pxc_cluster_view order by SEGMENT, LOCAL_INDEX;";
  1132. # Variable section for looping values
  1133. #Generalize object for now I have conceptualize as:
  1134. # Proxy (generic container)
  1135. # Proxy->{DNS} conenction reference
  1136. # Proxy->{PID} processes pid (angel and real)
  1137. # Proxy->{hostgroups}
  1138. # Proxy->{user} This is the user name
  1139. # Proxy->{password}
  1140. # Proxy->{port} node status (OPEN 0,Primary 1,Joiner 2,Joined 3,Synced 4,Donor 5)
  1141. my $self = {
  1142. _dns => undef,
  1143. _pid => undef,
  1144. _hostgroups => undef,
  1145. _hg_writer_id => 0,
  1146. _hg_reader_id => 0,
  1147. _user => undef,
  1148. _password => undef,
  1149. _port => undef,
  1150. _monitor_user => undef,
  1151. _monitor_password => undef,
  1152. _SQL_get_monitor => $SQL_get_monitor,
  1153. _SQL_get_hg=> $SQL_get_hostgroups,
  1154. _SQL_get_replication_hg=> $SQL_get_rep_hg,
  1155. _dbh_proxy => undef,
  1156. _check_timeout => 800, #timeout in ms
  1157. _action_nodes => {},
  1158. _retry_down => 0, # number of retry on a node before declaring it as failed.
  1159. _retry_up => 0, # number of retry on a node before declaring it OK.
  1160. _status_changed => 0, #if 1 something had happen and a node had be modify
  1161. _require_failover => 0, # Valid values are:
  1162. # 0 [default] do not make failover
  1163. # 1 make failover only if HG 8000 is specified in ProxySQL mysl_servers
  1164. # 2 use PXC_CLUSTER_VIEW to identify a server in the same segment
  1165. # 3 do whatever to keep service up also failover to another segment (use PXC_CLUSTER_VIEW)
  1166. };
  1167. bless $self, $class;
  1168. return $self;
  1169. }
  1170. sub require_failover{
  1171. my ( $self, $in ) = @_;
  1172. $self->{_require_failover} = $in if defined($in);
  1173. return $self->{_require_failover};
  1174. }
  1175. sub hg_reader_id{
  1176. my ( $self, $in ) = @_;
  1177. $self->{_hg_reader_id} = $in if defined($in);
  1178. return $self->{_hg_reader_id};
  1179. }
  1180. sub status_changed{
  1181. my ( $self, $in ) = @_;
  1182. $self->{_status_changed} = $in if defined($in);
  1183. return $self->{_status_changed};
  1184. }
  1185. sub retry_down{
  1186. my ( $self, $in ) = @_;
  1187. $self->{_retry_down} = $in if defined($in);
  1188. return $self->{_retry_down};
  1189. }
  1190. sub retry_up{
  1191. my ( $self, $in ) = @_;
  1192. $self->{_retry_up} = $in if defined($in);
  1193. return $self->{_retry_up};
  1194. }
  1195. sub debug{
  1196. my ( $self, $debug ) = @_;
  1197. $self->{_debug} = $debug if defined($debug);
  1198. return $self->{_debug};
  1199. }
  1200. sub action_nodes {
  1201. my ( $self, $action_nodes ) = @_;
  1202. $self->{_action_nodes} = $action_nodes if defined($action_nodes);
  1203. return $self->{_action_nodes};
  1204. }
  1205. sub dns {
  1206. my ( $self, $dns ) = @_;
  1207. $self->{_dns} = $dns if defined($dns);
  1208. return $self->{_dns};
  1209. }
  1210. sub dbh_proxy{
  1211. my ( $self, $dbh_proxy ) = @_;
  1212. $self->{_dbh_proxy} = $dbh_proxy if defined($dbh_proxy);
  1213. return $self->{_dbh_proxy};
  1214. }
  1215. sub pid {
  1216. my ( $self, $pid ) = @_;
  1217. $self->{_pid} = $pid if defined($pid);
  1218. return $self->{_pid};
  1219. }
  1220. sub hg_writer_id {
  1221. my ( $self, $pid ) = @_;
  1222. $self->{_hg_writer_id} = $pid if defined($pid);
  1223. return $self->{_hg_writer_id};
  1224. }
  1225. sub hostgroups {
  1226. my ( $self, $hostgroups ) = @_;
  1227. if (defined $hostgroups){
  1228. my @HGIds=split('\,', $Param->{hostgroups});
  1229. foreach my $hg (@HGIds){
  1230. my $proxy_hg = ProxySqlHG->new();
  1231. my $proxy_hgM = ProxySqlHG->new();
  1232. my $proxy_hgB = ProxySqlHG->new();
  1233. my ($id,$type) = split /:/, $hg;
  1234. $proxy_hg->id($id);
  1235. $proxy_hg->type(lc($type));
  1236. if(lc($type) eq 'w'){
  1237. $self->hg_writer_id($id);
  1238. }
  1239. if(lc($type) eq 'r'){
  1240. $self->hg_reader_id($id);
  1241. }
  1242. $self->{_hostgroups}->{$id}=($proxy_hg);
  1243. $proxy_hgM->id(($id + 9000));
  1244. $proxy_hgM->type("m".lc($type));
  1245. $self->{_hostgroups}->{$proxy_hgM->id(($id + 9000))}=($proxy_hgM);
  1246. #add a special group in case of back server for failover
  1247. if(lc($type) eq "w"){
  1248. $proxy_hgM->id(($id + 8000));
  1249. $proxy_hgM->type("b".lc($type));
  1250. $self->{_hostgroups}->{$proxy_hgM->id(($id + 8000))}=($proxy_hgM);
  1251. }
  1252. if(lc($type) eq "r"){
  1253. $proxy_hgM->id(($id + 8000));
  1254. $proxy_hgM->type("b".lc($type));
  1255. $self->{_hostgroups}->{$proxy_hgM->id(($id + 8000))}=($proxy_hgM);
  1256. }
  1257. if($self->debug >=1){print Utils->print_log(3," Inizializing hostgroup " . $proxy_hg->id ." ".$proxy_hg->type . "with maintenance HG ". $proxy_hgM->id ." ".$proxy_hgM->type."\n") ; }
  1258. }
  1259. }
  1260. return $self->{_hostgroups};
  1261. }
  1262. sub user{
  1263. my ( $self, $user ) = @_;
  1264. $self->{_user} = $user if defined($user);
  1265. return $self->{_user};
  1266. }
  1267. sub password {
  1268. my ( $self, $password ) = @_;
  1269. $self->{_password} = $password if defined($password);
  1270. return $self->{_password};
  1271. }
  1272. sub monitor_user{
  1273. my ( $self, $monitor_user ) = @_;
  1274. $self->{_monitor_user} = $monitor_user if defined($monitor_user);
  1275. return $self->{_monitor_user};
  1276. }
  1277. sub monitor_password {
  1278. my ( $self, $monitor_password ) = @_;
  1279. $self->{_monitor_password} = $monitor_password if defined($monitor_password);
  1280. return $self->{_monitor_password};
  1281. }
  1282. sub port {
  1283. my ( $self, $port ) = @_;
  1284. $self->{_port} = $port if defined($port);
  1285. return $self->{_port};
  1286. }
  1287. sub check_timeout{
  1288. my ( $self, $check_timeout ) = @_;
  1289. $self->{_check_timeout} = $check_timeout if defined($check_timeout);
  1290. return $self->{_check_timeout};
  1291. }
  1292. #Connect method connect an populate the cluster returns the Galera cluster
  1293. sub connect{
  1294. my ( $self, $port ) = @_;
  1295. my $dbh = Utils::get_connection($self->{_dns}, $self->{_user}, $self->{_password},' ');
  1296. $self->{_dbh_proxy} = $dbh;
  1297. # get monitor user/pw
  1298. my $cmd = $self->{_SQL_get_monitor};
  1299. my $sth = $dbh->prepare($cmd);
  1300. $sth->execute();
  1301. while (my $ref = $sth->fetchrow_hashref()) {
  1302. if($ref->{'name'} eq 'mysql-monitor_password' ){$self->{_monitor_password} = $ref->{'value'};}
  1303. if($ref->{'name'} eq 'mysql-monitor_username' ) {$self->{_monitor_user} = $ref->{'value'};}
  1304. #This is for now comment out.
  1305. # this is related to issue #10, where the node is not answering in time to the check.
  1306. # The timeout cannot be the same of the the ProxySQL read_only check
  1307. #if($ref->{'name'} eq 'mysql-monitor_read_only_timeout' ) {$self->{_check_timeout} = $ref->{'value'};}
  1308. }
  1309. if($self->debug >=1){print Utils->print_log(3," Connecting to ProxySQL " . $self->{_dns}. "\n" ); }
  1310. }
  1311. sub disconnect{
  1312. my ( $self, $port ) = @_;
  1313. $self->{_dbh_proxy}->disconnect;
  1314. }
  1315. sub get_galera_cluster{
  1316. my ( $self, $in ) = @_;
  1317. $self->{_galera_cluster} = $in if defined($in);
  1318. return $self->{_galera_cluster};
  1319. }
  1320. sub set_galera_cluster(){
  1321. my ( $self, $port ) = @_;
  1322. my $galera_cluster = Galeracluster->new();
  1323. $galera_cluster->hostgroups($self->hostgroups);
  1324. $galera_cluster->dbh_proxy($self->dbh_proxy);
  1325. $galera_cluster->check_timeout($self->check_timeout);
  1326. $galera_cluster->monitor_user($self->monitor_user);
  1327. $galera_cluster->monitor_password($self->monitor_password);
  1328. $galera_cluster->debug($self->debug);
  1329. $galera_cluster->hg_writer_id($self->hg_writer_id);
  1330. $galera_cluster->hg_reader_id($self->hg_reader_id);
  1331. $galera_cluster->singlewriter($Param->{single_writer});
  1332. $galera_cluster->writer_is_reader($Param->{writer_is_reader});
  1333. $galera_cluster->ssl_certificates_path($Param->{ssl_certs_path});
  1334. $self->get_galera_cluster($galera_cluster);
  1335. if($self->debug >=1){print Utils->print_log(3," Galera cluster object created " . caller(3). "\n" ); }
  1336. }
  1337. sub evaluate_nodes{
  1338. my ($proxynode,$GGalera_cluster) = @_ ;
  1339. my ( $nodes ) = $GGalera_cluster->{_nodes};
  1340. my $action_nodes = undef;
  1341. #Rules:
  1342. #see rules in the doc
  1343. #do the checks
  1344. if($proxynode->debug >=1){print Utils->print_log(3," Evaluate nodes state \n" ) }
  1345. foreach my $key (sort keys %{$nodes}){
  1346. if(defined $nodes->{$key} ){
  1347. #only if node has HG that is not maintenance it can evaluate to be put down in some way
  1348. if($nodes->{$key}->{_hostgroups} < 8000
  1349. && $nodes->{$key}->{_process_status} > 0){
  1350. #Check major exclusions
  1351. # 1) wsrep state
  1352. # 2) Node is not read only
  1353. # 3) at least another node in the HG
  1354. if( $nodes->{$key}->wsrep_status == 2
  1355. && $nodes->{$key}->read_only eq "OFF"
  1356. #&& $GGalera_cluster->{_main_segment} != $nodes->{$key}->wsrep_segment
  1357. && $nodes->{$key}->proxy_status ne "OFFLINE_SOFT"
  1358. ){
  1359. if($GGalera_cluster->{_hostgroups}->{$nodes->{$key}->{_hostgroups}}->{_size} <= 1){
  1360. print Utils->print_log(3," Node ".$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups."; Is in state ".$nodes->{$key}->wsrep_status
  1361. .". But I will not move to OFFLINE_SOFT given last node left in the Host group \n");
  1362. next;
  1363. }
  1364. $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_OFFLINE}}= $nodes->{$key};
  1365. #if retry is > 0 then it's managed
  1366. if($proxynode->retry_down > 0){
  1367. $nodes->{$key}->get_retry_down($nodes->{$key}->get_retry_down + 1);
  1368. }
  1369. if($proxynode->debug >=1){print Utils->print_log(3," Evaluate nodes state "
  1370. .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_OFFLINE}
  1371. ." Retry #".$nodes->{$key}->get_retry_down."\n" ) }
  1372. next;
  1373. }
  1374. if( $nodes->{$key}->wsrep_status ne 4
  1375. && $nodes->{$key}->wsrep_status ne 2){
  1376. $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_HG_CHANGE}}= $nodes->{$key};
  1377. #if retry is > 0 then it's managed
  1378. if($proxynode->retry_down > 0){
  1379. $nodes->{$key}->get_retry_down($nodes->{$key}->get_retry_down + 1);
  1380. }
  1381. if($proxynode->debug >=1){print Utils->print_log(3," Evaluate nodes state "
  1382. .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_HG_CHANGE}
  1383. ." Retry #".$nodes->{$key}->get_retry_down."\n" ) }
  1384. next;
  1385. }
  1386. #3) Node/cluster in non primary
  1387. if($nodes->{$key}->cluster_status ne "Primary"){
  1388. $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_HG_CHANGE}}= $nodes->{$key};
  1389. #if retry is > 0 then it's managed
  1390. if($proxynode->retry_down > 0){
  1391. $nodes->{$key}->get_retry_down($nodes->{$key}->get_retry_down + 1);
  1392. }
  1393. if($proxynode->debug >=1){print Utils->print_log(3," Evaluate nodes state "
  1394. .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_HG_CHANGE}
  1395. ." Retry #".$nodes->{$key}->get_retry_down."\n" ) }
  1396. next;
  1397. }
  1398. # 4) wsrep_reject_queries=NONE
  1399. if($nodes->{$key}->wsrep_rejectqueries ne "NONE" && $nodes->{$key}->proxy_status ne "OFFLINE_SOFT"){
  1400. my $inc =0;
  1401. if($nodes->{$key}->wsrep_rejectqueries eq "ALL"){
  1402. $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_HG_CHANGE}}= $nodes->{$key};
  1403. $inc=1;
  1404. }else{
  1405. $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_HG_CHANGE}}= $nodes->{$key};
  1406. $inc=1;
  1407. }
  1408. #if retry is > 0 then it's managed
  1409. if($proxynode->retry_down > 0 && $inc > 0){
  1410. $nodes->{$key}->get_retry_down($nodes->{$key}->get_retry_down + 1);
  1411. }
  1412. if($proxynode->debug >=1){
  1413. print Utils->print_log(3," Evaluate nodes state "
  1414. .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_HG_CHANGE}
  1415. ." Retry #".$nodes->{$key}->get_retry_down."\n" ) }
  1416. next;
  1417. }
  1418. #5) Donor, node reject queries =1 size of cluster > 2 of nodes in the same segments
  1419. if($nodes->{$key}->wsrep_status eq 2
  1420. && $nodes->{$key}->wsrep_donorrejectqueries eq "ON"){
  1421. $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_HG_CHANGE}}= $nodes->{$key};
  1422. #if retry is > 0 then it's managed
  1423. if($proxynode->retry_down > 0){
  1424. $nodes->{$key}->get_retry_down($nodes->{$key}->get_retry_down + 1);
  1425. }
  1426. if($proxynode->debug >=1){
  1427. print Utils->print_log(3," Evaluate nodes state "
  1428. .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_HG_CHANGE}
  1429. ." Retry #".$nodes->{$key}->get_retry_down."\n" ) }
  1430. next;
  1431. }
  1432. #Set OFFLINE_SOFT a writer:
  1433. #1) donor node reject queries - 0
  1434. #2)size of cluster > 2 of nodes in the same segments
  1435. #3) more then one writer in the same HG
  1436. #4) Node had pxc_maint_mode set to anything except DISABLED, not matter what it will go in OFFLINE_SOFT
  1437. if(
  1438. $nodes->{$key}->read_only eq "ON"
  1439. && $nodes->{$key}->{_hostgroups} == $GGalera_cluster->{_hg_writer_id}
  1440. && $nodes->{$key}->wsrep_donorrejectqueries eq "OFF"
  1441. && $nodes->{$key}->proxy_status ne "OFFLINE_SOFT"
  1442. ){
  1443. ## In case READ_ONLY is OFF and we have only a node left but desync do not put it down
  1444. #if( $GGalera_cluster->{_size}->{$nodes->{$key}->{_wsrep_segment}} == 1
  1445. # &&$nodes->{$key}->read_only eq "OFF"){
  1446. # next;
  1447. #}
  1448. $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_OFFLINE}}= $nodes->{$key};
  1449. #if retry is > 0 then it's managed
  1450. if($proxynode->retry_down > 0){
  1451. $nodes->{$key}->get_retry_down($nodes->{$key}->get_retry_down + 1);
  1452. }
  1453. if($proxynode->debug >=1){print Utils->print_log(3," Evaluate nodes state "
  1454. .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_DOWN_OFFLINE}
  1455. ." Retry #".$nodes->{$key}->get_retry_down."\n" ) }
  1456. next;
  1457. }
  1458. #4) Node had pxc_maint_mode set to anything except DISABLED, not matter what it will go in OFFLINE_SOFT
  1459. if( defined $nodes->{$key}->pxc_maint_mode
  1460. && $nodes->{$key}->pxc_maint_mode ne "DISABLED"
  1461. && $nodes->{$key}->proxy_status ne "OFFLINE_SOFT"){
  1462. $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_TO_MAINTENANCE}}= $nodes->{$key};
  1463. #if retry is > 0 then it's managed
  1464. if($proxynode->retry_down > 0){
  1465. $nodes->{$key}->get_retry_down($proxynode->retry_down ); # this is a known state and we do not want any delay set the retry to his max
  1466. }
  1467. if(
  1468. $nodes->{$key}->{_hostgroups} == $GGalera_cluster->{_hg_writer_id}
  1469. && $GGalera_cluster->{_singlewriter} > 0
  1470. ){
  1471. $GGalera_cluster->{_haswriter} = $GGalera_cluster->{_haswriter} -1;
  1472. }
  1473. if($proxynode->debug >=1){print Utils->print_log(3," Evaluate nodes state "
  1474. .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_TO_MAINTENANCE}
  1475. ." Retry #".$nodes->{$key}->get_retry_down."\n" ) }
  1476. next;
  1477. }
  1478. # Node must be removed if writer is reader is disable and node is in writer group
  1479. if($nodes->{$key}->wsrep_status eq 4
  1480. && $nodes->{$key}->wsrep_rejectqueries eq "NONE"
  1481. && $nodes->{$key}->read_only eq "OFF"
  1482. && $nodes->{$key}->cluster_status eq "Primary"
  1483. && $nodes->{$key}->hostgroups == $proxynode->{_hg_reader_id}
  1484. && $GGalera_cluster->{_writer_is_reader} < 1
  1485. && $nodes->{$key}->proxy_status eq "ONLINE"
  1486. ){
  1487. #my $nodes_read_ips = join(',', @{$GGalera_cluster->{_reader_nodes}});
  1488. my $nodes_write_ips = join(',', @{$GGalera_cluster->{_writer_nodes}});
  1489. my $ip = "$nodes->{$key}->{_ip}:$nodes->{$key}->{_port}";
  1490. if($nodes_write_ips =~ m/$ip/ ){
  1491. $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_DELETE_NODE}}= $nodes->{$key};
  1492. #if retry is > 0 then it's managed
  1493. if($proxynode->retry_up > 0){
  1494. $nodes->{$key}->get_retry_up($nodes->{$key}->get_retry_up +1);
  1495. }
  1496. print Utils->print_log(3," Writer is also reader disabled removing node from reader Hostgroup "
  1497. .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_DELETE_NODE}
  1498. ." Retry #".$nodes->{$key}->get_retry_up."\n" );
  1499. next;
  1500. }
  1501. }
  1502. }
  1503. #Node comes back from offline_soft when (all of them):
  1504. # 1) Node state is 4
  1505. # 3) wsrep_reject_queries = none
  1506. # 4) Primary state
  1507. # 5) pxc_maint_mode is DISABLED or undef
  1508. if($nodes->{$key}->wsrep_status eq 4
  1509. && $nodes->{$key}->proxy_status eq "OFFLINE_SOFT"
  1510. && $nodes->{$key}->wsrep_rejectqueries eq "NONE"
  1511. && $nodes->{$key}->read_only eq "OFF"
  1512. &&$nodes->{$key}->cluster_status eq "Primary"
  1513. &&(!defined $nodes->{$key}->pxc_maint_mode || $nodes->{$key}->pxc_maint_mode eq "DISABLED")
  1514. && $nodes->{$key}->hostgroups < 8000
  1515. ){
  1516. if($GGalera_cluster->haswriter > 0
  1517. && $GGalera_cluster->singlewriter > 0
  1518. && $nodes->{$key}->hostgroups == $GGalera_cluster->hg_writer_id
  1519. ){
  1520. $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_DELETE_NODE}}= $nodes->{$key};
  1521. #if retry is > 0 then it's managed
  1522. if($proxynode->retry_up > 0){
  1523. $nodes->{$key}->get_retry_up($nodes->{$key}->get_retry_up +1);
  1524. }
  1525. if($proxynode->debug <=1){
  1526. print Utils->print_log(3, " Evaluate nodes state "
  1527. .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_DELETE_NODE}
  1528. ." Retry #".$nodes->{$key}->get_retry_up."\n" ) }
  1529. next;
  1530. }
  1531. else{
  1532. $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_UP_OFFLINE}}= $nodes->{$key};
  1533. #if retry is > 0 then it's managed
  1534. if($proxynode->retry_up > 0){
  1535. $nodes->{$key}->get_retry_up($nodes->{$key}->get_retry_up +1);
  1536. }
  1537. if($proxynode->debug <=1){
  1538. print Utils->print_log(3, " Evaluate nodes state "
  1539. .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_UP_OFFLINE}
  1540. ." Retry #".$nodes->{$key}->get_retry_up."\n" ) }
  1541. next;
  1542. }
  1543. }
  1544. # Node comes back from maintenance HG when (all of them):
  1545. # 1) node state is 4
  1546. # 3) wsrep_reject_queries = none
  1547. # 4) Primary state
  1548. if($nodes->{$key}->wsrep_status eq 4
  1549. && $nodes->{$key}->wsrep_rejectqueries eq "NONE"
  1550. && $nodes->{$key}->cluster_status eq "Primary"
  1551. && $nodes->{$key}->hostgroups >= 9000
  1552. ){
  1553. $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_UP_HG_CHANGE}}= $nodes->{$key};
  1554. #if retry is > 0 then it's managed
  1555. if($proxynode->retry_up > 0){
  1556. $nodes->{$key}->get_retry_up($nodes->{$key}->get_retry_up +1);
  1557. }
  1558. if($proxynode->debug >=1){
  1559. print Utils->print_log(3," Evaluate nodes state "
  1560. .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_UP_HG_CHANGE}
  1561. ." Retry #".$nodes->{$key}->get_retry_up."\n" ) }
  1562. next;
  1563. }
  1564. #Special case when a node goes down it goes through several state and the check disable it moving form original group
  1565. #This is to remove it to his original HG when is not reachable
  1566. if($nodes->{$key}->{_process_status} < 0
  1567. && $nodes->{$key}->hostgroups >= 9000
  1568. ){
  1569. #if retry is > 0 then it's managed
  1570. if($proxynode->retry_up > 0){
  1571. $nodes->{$key}->get_retry_up($nodes->{$key}->get_retry_up +1);
  1572. }
  1573. $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_UP_HG_CHANGE}}= $nodes->{$key};
  1574. if($proxynode->debug >=1){
  1575. print Utils->print_log(3," Evaluate nodes state "
  1576. .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_MOVE_UP_HG_CHANGE}
  1577. ." Retry #".$nodes->{$key}->get_retry_up."\n" ) }
  1578. next;
  1579. }
  1580. #Check if any node that is in the read backup host group is not present in the readhostgroup while it should.
  1581. #If identify it will add to the read HG
  1582. if($nodes->{$key}->wsrep_status eq 4
  1583. && $nodes->{$key}->wsrep_rejectqueries eq "NONE"
  1584. && $nodes->{$key}->cluster_status eq "Primary"
  1585. && $nodes->{$key}->hostgroups == (8000 + $proxynode->{_hg_reader_id})
  1586. ){
  1587. my $nodes_read_ips = join(',', @{$GGalera_cluster->{_reader_nodes}});
  1588. my $nodes_write_ips = join(',', @{$GGalera_cluster->{_writer_nodes}});
  1589. my $ip = "$nodes->{$key}->{_ip}:$nodes->{$key}->{_port}";
  1590. if($nodes_read_ips =~ m/$ip/
  1591. || ( $nodes_write_ips =~ m/$ip/
  1592. && $GGalera_cluster->{_writer_is_reader} < 1)){
  1593. if($proxynode->debug >=1){
  1594. print Utils->print_log(3," Node already ONLINE in read hg "
  1595. .$nodes->{$key}->ip.";".$nodes->{$key}->port.";\n" ) }
  1596. }else{
  1597. $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_INSERT_READ}}= $nodes->{$key};
  1598. #if retry is > 0 then it's managed
  1599. if($proxynode->retry_up > 0){
  1600. $nodes->{$key}->get_retry_up($nodes->{$key}->get_retry_up +1);
  1601. }
  1602. if($proxynode->debug >=1){
  1603. print Utils->print_log(3," Evaluate nodes state "
  1604. .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_INSERT_READ}
  1605. ." Retry #".$nodes->{$key}->get_retry_up."\n" )
  1606. }
  1607. }
  1608. next;
  1609. }
  1610. #Check if any node that is in the write backup host group is not present in the WRITE hostgroup while it should WHEN MULTIPLE WRITERS.
  1611. #If identify it will add to the read HG
  1612. if($nodes->{$key}->wsrep_status eq 4
  1613. && $nodes->{$key}->wsrep_rejectqueries eq "NONE"
  1614. && $nodes->{$key}->read_only eq "OFF"
  1615. && $nodes->{$key}->cluster_status eq "Primary"
  1616. && $nodes->{$key}->hostgroups == (8000 + $proxynode->{_hg_writer_id})
  1617. && $GGalera_cluster->{_singlewriter} < 1
  1618. ){
  1619. #my $nodes_read_ips = join(',', @{$GGalera_cluster->{_reader_nodes}});
  1620. my $nodes_write_ips = join(',', @{$GGalera_cluster->{_writer_nodes}});
  1621. my $ip = "$nodes->{$key}->{_ip}:$nodes->{$key}->{_port}";
  1622. if($nodes_write_ips =~ m/$ip/
  1623. && $GGalera_cluster->{_single_writer} < 1){
  1624. if($proxynode->debug >=1){
  1625. print Utils->print_log(3," Node already ONLINE in write hg "
  1626. .$nodes->{$key}->ip.";".$nodes->{$key}->port.";\n" ) }
  1627. }else{
  1628. $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_INSERT_WRITE}}= $nodes->{$key};
  1629. #if retry is > 0 then it's managed
  1630. if($proxynode->retry_up > 0){
  1631. $nodes->{$key}->get_retry_up($nodes->{$key}->get_retry_up +1);
  1632. }
  1633. if($proxynode->debug >=1){
  1634. print Utils->print_log(3," Evaluate nodes state "
  1635. .$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_INSERT_WRITE}
  1636. ." Retry #".$nodes->{$key}->get_retry_up."\n" )
  1637. }
  1638. }
  1639. next;
  1640. }
  1641. # in the case node is not in one of the declared state
  1642. # BUT it has the counter retry set THEN I reset it to 0 whatever it was because
  1643. # I assume it is ok now
  1644. if($proxynode->retry_up > 0
  1645. && $nodes->{$key}->get_retry_up > 0){
  1646. $nodes->{$key}->get_retry_up(0);
  1647. $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_SAVE_RETRY}}= $nodes->{$key};
  1648. }
  1649. if($proxynode->retry_down > 0
  1650. && $nodes->{$key}->get_retry_down > 0){
  1651. $nodes->{$key}->get_retry_down(0);
  1652. $action_nodes->{$nodes->{$key}->ip.";".$nodes->{$key}->port.";".$nodes->{$key}->hostgroups.";".$nodes->{$key}->{_SAVE_RETRY}}= $nodes->{$key};
  1653. }
  1654. }
  1655. }
  1656. $proxynode->action_nodes($action_nodes);
  1657. #failover has higher priority BUT if it happens before other action will interfere with recovery and move nodes that may prevent the failover to happen
  1658. # and it will ends the script work (if successful)
  1659. #if($GGalera_cluster->{_writers} > 1
  1660. # && $GGalera_cluster->{_singlewriter} > 0)
  1661. #{
  1662. # $proxynode->{_require_failover} = 1;
  1663. #}
  1664. if($proxynode->require_failover > 0
  1665. && !defined $action_nodes
  1666. ){
  1667. if($GGalera_cluster->haswriter < 1
  1668. || ( $GGalera_cluster->{_writers} > 1 && $GGalera_cluster->{_singlewriter} > 0)
  1669. ){
  1670. print Utils->print_log(2,"Fail-over in action Using Method = $proxynode->{_require_failover}\n" );
  1671. if($proxynode->initiate_failover($GGalera_cluster) >0){
  1672. #if($proxynode->debug >=1){
  1673. print Utils->print_log(2,"!!!! FAILOVER !!!!! \n Cluster was without WRITER I have try to restore service promoting a node\n" );
  1674. #exit 0;
  1675. #}
  1676. }
  1677. }
  1678. }
  1679. elsif($proxynode->require_failover > 0
  1680. && $GGalera_cluster->haswriter < 1){
  1681. print Utils->print_log(2,"PXC maintenance on single writer, is asking for failover. Fail-over in action Using Method = $proxynode->{_require_failover}\n" );
  1682. $proxynode->push_changes;
  1683. if($proxynode->initiate_failover($GGalera_cluster) >0){
  1684. #if($proxynode->debug >=1){
  1685. print Utils->print_log(2,"!!!! FAILOVER !!!!! \n Cluster was without WRITER I have try to restore service promoting a node\n" );
  1686. #exit 0;
  1687. #}
  1688. }
  1689. }
  1690. }
  1691. sub push_changes{
  1692. my ($proxynode) = @_ ;
  1693. my $node = GaleraNode->new();
  1694. my $SQL_command="";
  1695. foreach my $key (sort keys %{$proxynode->{_action_nodes}}){
  1696. my ($host, $port, $hg, $action) = split /s*;\s*/, $key;
  1697. SWITCH: {
  1698. if ($action == $node->MOVE_DOWN_OFFLINE) { if($proxynode->{_action_nodes}->{$key}->get_retry_down >= $proxynode->retry_down){$proxynode->move_node_offline($key,$proxynode->{_action_nodes}->{$key})}; last SWITCH; }
  1699. if ($action == $node->MOVE_DOWN_HG_CHANGE) { if($proxynode->{_action_nodes}->{$key}->get_retry_down >= $proxynode->retry_down){ $proxynode->move_node_down_hg_change($key,$proxynode->{_action_nodes}->{$key})}; last SWITCH; }
  1700. if ($action == $node->MOVE_UP_OFFLINE) { if($proxynode->{_action_nodes}->{$key}->get_retry_up >= $proxynode->retry_up){ $proxynode->move_node_up_from_offline($key,$proxynode->{_action_nodes}->{$key})}; last SWITCH; }
  1701. if ($action == $node->MOVE_UP_HG_CHANGE) { if($proxynode->{_action_nodes}->{$key}->get_retry_up >= $proxynode->retry_up){$proxynode->move_node_up_from_hg_change($key,$proxynode->{_action_nodes}->{$key})}; last SWITCH; }
  1702. if ($action == $node->MOVE_TO_MAINTENANCE) { if($proxynode->{_action_nodes}->{$key}->get_retry_down >= $proxynode->retry_down){$proxynode->move_node_to_maintenance($key,$proxynode->{_action_nodes}->{$key})}; last SWITCH; }
  1703. if ($action == $node->DELETE_NODE) {
  1704. if($proxynode->{_action_nodes}->{$key}->get_retry_up >= $proxynode->retry_up){
  1705. $proxynode->delete_node_from_hostgroup($key,$proxynode->{_action_nodes}->{$key})}; last SWITCH; }
  1706. if($action == $node->INSERT_READ){if($proxynode->{_action_nodes}->{$key}->get_retry_up >= $proxynode->retry_up){
  1707. $proxynode->insert_reader($key,$proxynode->{_action_nodes}->{$key})
  1708. }; last SWITCH;
  1709. }
  1710. if($action == $node->INSERT_WRITE){if($proxynode->{_action_nodes}->{$key}->get_retry_up >= $proxynode->retry_up){
  1711. $proxynode->insert_writer($key,$proxynode->{_action_nodes}->{$key})
  1712. }; last SWITCH;
  1713. }
  1714. }
  1715. if($proxynode->retry_up > 0 || $proxynode->retry_down > 0){
  1716. save_retry($proxynode,$key,$proxynode->{_action_nodes}->{$key});
  1717. }
  1718. }
  1719. $proxynode->{_action_nodes} = undef;
  1720. }
  1721. sub save_retry{
  1722. #this action will take place only if retry is active
  1723. my ($self,$key,$node) = @_;
  1724. my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
  1725. if($self->debug >=1){print Utils->print_log(4,"Check retry Node:".$host." port:".$port . " hg:".$hg ." Time IN \n");}
  1726. my $sql_string = "UPDATE mysql_servers SET comment='"
  1727. .$node->{_comment}
  1728. .$self->get_galera_cluster->cluster_identifier."_retry_up=".$node->get_retry_up
  1729. .";".$self->get_galera_cluster->cluster_identifier."_retry_down=".$node->get_retry_down
  1730. .";' WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
  1731. $self->{_dbh_proxy}->do($sql_string) or die "Couldn't execute statement: " . $self->{_dbh_proxy}->errstr;
  1732. $self->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or die "Couldn't execute statement: " . $self->{_dbh_proxy}->errstr;
  1733. if($self->debug >=1){print Utils->print_log(2," Reset retry to UP:".$node->get_retry_up." Down:".$node->get_retry_down."for node:" .$key
  1734. ." SQL:" .$sql_string
  1735. ."\n")} ;
  1736. if($self->debug >=1){print Utils->print_log(4,"Check retry Node:".$host." port:".$port . " hg:".$hg ." Time OUT \n");}
  1737. }
  1738. sub move_node_offline{
  1739. #this action involve only the proxy so we will
  1740. my ($proxynode, $key,$node) = @_;
  1741. my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
  1742. my $proxy_sql_command= " UPDATE mysql_servers SET status='OFFLINE_SOFT' WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
  1743. $proxynode->{_dbh_proxy}->do($proxy_sql_command) or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1744. $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1745. print Utils->print_log(2," Move node:" .$key
  1746. ." SQL:" .$proxy_sql_command
  1747. ."\n") ;
  1748. }
  1749. sub move_node_to_maintenance{
  1750. #this action involve only the proxy so we will
  1751. my ($proxynode, $key,$node) = @_;
  1752. my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
  1753. my $proxy_sql_command= " UPDATE mysql_servers SET status='OFFLINE_SOFT' WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
  1754. $proxynode->{_dbh_proxy}->do($proxy_sql_command) or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1755. $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1756. print Utils->print_log(2," Move node:" .$key
  1757. ." SQL:" .$proxy_sql_command
  1758. ."\n") ;
  1759. }
  1760. #remove a node from an hostgroup
  1761. sub delete_node_from_hostgroup{
  1762. #this action involve only the proxy so we will
  1763. my ($proxynode, $key,$node) = @_;
  1764. my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
  1765. my $proxy_sql_command= " DELETE from mysql_servers WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
  1766. $proxynode->{_dbh_proxy}->do($proxy_sql_command) or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1767. $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1768. print Utils->print_log(2," DELETE node:" .$key
  1769. ." SQL:" .$proxy_sql_command
  1770. ."\n") ;
  1771. }
  1772. #move a node to a maintenance HG ((9000 + HG id))
  1773. sub move_node_down_hg_change{
  1774. my ($proxynode, $key,$node) = @_;
  1775. my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
  1776. if($hg > 9000) {return 1;}
  1777. my $node_sql_command = "SET GLOBAL READ_ONLY=1;";
  1778. my $proxy_sql_command =" UPDATE mysql_servers SET hostgroup_id=".(9000 + $hg)." WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
  1779. $proxynode->{_dbh_proxy}->do($proxy_sql_command) or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1780. $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1781. print Utils->print_log(2," Move node:" .$key
  1782. ." SQL:" .$proxy_sql_command
  1783. ."\n" );
  1784. }
  1785. #Bring back a node that is just offline
  1786. sub move_node_up_from_offline{
  1787. my ($proxynode, $key,$node) = @_;
  1788. my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
  1789. my $proxy_sql_command= " UPDATE mysql_servers SET status='ONLINE' WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
  1790. $proxynode->{_dbh_proxy}->do($proxy_sql_command) or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1791. my $error_code = $proxynode->{_dbh_proxy}->err();
  1792. $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1793. print Utils->print_log(2," Move node:" .$key
  1794. ." SQL:" .$proxy_sql_command
  1795. ."\n" );
  1796. }
  1797. #move a node back to his original HG ((HG id - 9000))
  1798. sub move_node_up_from_hg_change{
  1799. my ($proxynode, $key,$node) = @_;
  1800. my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
  1801. #my $node_sql_command = "SET GLOBAL READ_ONLY=1;";
  1802. my $proxy_sql_command =" UPDATE mysql_servers SET hostgroup_id=".($hg - 9000)." WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
  1803. $proxynode->{_dbh_proxy}->do($proxy_sql_command) or warn "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1804. my $error_code = $proxynode->{_dbh_proxy}->err();
  1805. $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1806. print Utils->print_log(2," Move node:" .$key
  1807. ." SQL:" .$proxy_sql_command
  1808. ."\n" ) ;
  1809. }
  1810. #move a node back to his original HG ((HG id - 9000))
  1811. sub add_node_to_readers{
  1812. my ($proxynode, $key,$node) = @_;
  1813. my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
  1814. my $node_sql_command = "SET GLOBAL READ_ONLY=1;";
  1815. my $proxy_sql_command =" UPDATE mysql_servers SET hostgroup_id=".($hg - 9000)." WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
  1816. $proxynode->{_dbh_proxy}->do($proxy_sql_command) or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1817. my $error_code = $proxynode->{_dbh_proxy}->err();
  1818. $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or die "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1819. print Utils->print_log(2," Move node:" .$key
  1820. ." SQL:" .$proxy_sql_command
  1821. ."\n" ) ;
  1822. }
  1823. sub insert_reader{
  1824. my ($proxynode, $key,$node) = @_;
  1825. my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
  1826. my $proxy_sql_command ="INSERT INTO mysql_servers (hostgroup_id, hostname,port,gtid_port,status,weight,compression,max_connections,max_replication_lag,use_ssl,max_latency_ms,comment) ".
  1827. " VALUES($proxynode->{_hg_reader_id}" .
  1828. ",'$node->{_ip}'" .
  1829. ",$node->{_port} " .
  1830. ",$node->{_gtid_port} " .
  1831. ",'$node->{_proxy_status}' " .
  1832. ",$node->{_weight}" .
  1833. ",$node->{_compression}" .
  1834. ",$node->{_connections}" .
  1835. ",$node->{_max_replication_lag}" .
  1836. ",$node->{_use_ssl}" .
  1837. ",$node->{_max_latency}" .
  1838. ",'$node->{_comments}')" ;
  1839. #my $proxy_sql_command =" UPDATE mysql_servers SET hostgroup_id=".($hg - 9000)." WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
  1840. $proxynode->{_dbh_proxy}->do($proxy_sql_command) or warn "Couldn't execute statement: $proxy_sql_command" . $proxynode->{_dbh_proxy}->errstr;
  1841. my $error_code = $proxynode->{_dbh_proxy}->err();
  1842. $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or warn "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1843. print Utils->print_log(2," Move node:" .$key
  1844. ." SQL:" .$proxy_sql_command
  1845. ."\n" ) ;
  1846. }
  1847. sub insert_writer{
  1848. my ($proxynode, $key,$node) = @_;
  1849. my ($host, $port, $hg,$action) = split /s*;\s*/, $key;
  1850. my $proxy_sql_command ="INSERT INTO mysql_servers (hostgroup_id, hostname,port,gtid_port,status,weight,compression,max_connections,max_replication_lag,use_ssl,max_latency_ms,comment) ".
  1851. " VALUES($proxynode->{_hg_writer_id}" .
  1852. ",'$node->{_ip}'" .
  1853. ",$node->{_port} " .
  1854. ",$node->{_gtid_port} " .
  1855. ",'$node->{_proxy_status}' " .
  1856. ",$node->{_weight}" .
  1857. ",$node->{_compression}" .
  1858. ",$node->{_connections}" .
  1859. ",$node->{_max_replication_lag}" .
  1860. ",$node->{_use_ssl}" .
  1861. ",$node->{_max_latency}" .
  1862. ",'$node->{_comments}')" ;
  1863. #my $proxy_sql_command =" UPDATE mysql_servers SET hostgroup_id=".($hg - 9000)." WHERE hostgroup_id=$hg AND hostname='$host' AND port='$port'";
  1864. $proxynode->{_dbh_proxy}->do($proxy_sql_command) or warn "Couldn't execute statement: $proxy_sql_command" . $proxynode->{_dbh_proxy}->errstr;
  1865. my $error_code = $proxynode->{_dbh_proxy}->err();
  1866. $proxynode->{_dbh_proxy}->do("LOAD MYSQL SERVERS TO RUNTIME") or warn "Couldn't execute statement: " . $proxynode->{_dbh_proxy}->errstr;
  1867. print Utils->print_log(2," Move node:" .$key
  1868. ." SQL:" .$proxy_sql_command
  1869. ."\n" ) ;
  1870. }
  1871. sub initiate_failover{
  1872. my ($proxynode,$Galera_cluster) = @_ ;
  1873. my ( $nodes ) = $Galera_cluster->{_nodes};
  1874. my ( $nodes_maint ) = $Galera_cluster->{_nodes_maint};
  1875. my $failover_node;
  1876. my $candidate_failover_node;
  1877. my $min_index = 100;
  1878. my $max_weight=0;
  1879. my $cand_min_index = 100;
  1880. my $cand_max_weight=0;
  1881. my $local_node;
  1882. my $hg_writer_id=0;
  1883. my $exclude_delete="";
  1884. #Valid values are:
  1885. # 0 [default] do not make failover
  1886. # 1 make failover only if HG 8000 is specified in ProxySQL mysl_servers
  1887. # 2 use PXC_CLUSTER_VIEW to identify a server in the same segment
  1888. # 3 do whatever to keep service up also failover to another segment (use PXC_CLUSTER_VIEW)
  1889. #
  1890. foreach my $key (sort keys %{$nodes}){
  1891. if(defined $nodes->{$key} ){
  1892. #only if node has HG that is not maintenance it can be evaluated to be put down in some way
  1893. #Look for the node with the lowest weight in the same segment
  1894. if($nodes->{$key}->{_hostgroups} < 9000
  1895. && $nodes->{$key}->{_proxy_status} eq "ONLINE"
  1896. && $nodes->{$key}->{_process_status} > 0
  1897. && $nodes->{$key}->{_wsrep_status} == 4
  1898. && $nodes->{$key}->{_wsrep_rejectqueries} eq "NONE"
  1899. && $nodes->{$key}->{_wsrep_donorrejectqueries} eq "OFF"
  1900. && $nodes->{$key}->{_pxc_maint_mode} eq "DISABLED"
  1901. && $nodes->{$key}->{_read_only} eq "OFF"
  1902. ){
  1903. #IN case failover option is 1 we need to have:
  1904. #The failover group defined in Proxysql (8xxx + id of the HG)
  1905. #Node must be of HG 8XXXX id
  1906. #And must be in the same segment of the writer
  1907. if(
  1908. $proxynode->{_require_failover} == 1
  1909. # && $nodes->{$key}->{_wsrep_segment} == $Galera_cluster->{_main_segment}
  1910. && $Galera_cluster->{_has_failover_node} >0
  1911. && $nodes->{$key}->{_hostgroups} == (8000 + $Galera_cluster->{_hg_writer_id})
  1912. ){
  1913. if($nodes->{$key}->{_weight} > $max_weight
  1914. && $nodes->{$key}->{_wsrep_segment} == $Galera_cluster->{_main_segment}
  1915. ){
  1916. $max_weight= $nodes->{$key}->{_weight};
  1917. #$min_index = $nodes->{$key}->{_wsrep_local_index};
  1918. $failover_node = $nodes->{$key};
  1919. }
  1920. elsif($nodes->{$key}->{_wsrep_segment} != $Galera_cluster->{_main_segment}
  1921. && !defined $failover_node
  1922. && $nodes->{$key}->{_weight} > $cand_max_weight){
  1923. $cand_max_weight= $nodes->{$key}->{_weight};
  1924. # $cand_min_index = $nodes->{$key}->{_wsrep_local_index};
  1925. $candidate_failover_node = $nodes->{$key};
  1926. }
  1927. #if($nodes->{$key}->{_weight} > $max_weight){
  1928. # $max_weight= $nodes->{$key}->{_weight};
  1929. # $failover_node = $nodes->{$key};
  1930. #}
  1931. }
  1932. #IN case failover option is 2 we need to have:
  1933. # must be in the same segment of the writer
  1934. #and be in the PXC_CLUSTER_VIEW
  1935. elsif(
  1936. $proxynode->{_require_failover} == 2
  1937. && $nodes->{$key}->{_wsrep_segment} == $Galera_cluster->{_main_segment}
  1938. ){
  1939. if($nodes->{$key}->{_wsrep_local_index} < $min_index
  1940. ){
  1941. $min_index = $nodes->{$key}->{_wsrep_local_index};
  1942. $failover_node = $nodes->{$key};
  1943. }
  1944. }
  1945. elsif($proxynode->{_require_failover} == 3){
  1946. if($nodes->{$key}->{_wsrep_segment} == $Galera_cluster->{_main_segment}
  1947. && $nodes->{$key}->{_wsrep_local_index} < $min_index){
  1948. $min_index = $nodes->{$key}->{_wsrep_local_index};
  1949. $failover_node = $nodes->{$key};
  1950. }
  1951. elsif($nodes->{$key}->{_wsrep_segment} != $Galera_cluster->{_main_segment}
  1952. && !defined $failover_node
  1953. && $nodes->{$key}->{_wsrep_local_index} < $cand_min_index){
  1954. $cand_min_index = $nodes->{$key}->{_wsrep_local_index};
  1955. $candidate_failover_node = $nodes->{$key};
  1956. }
  1957. }
  1958. }
  1959. }
  1960. }
  1961. if(defined $nodes_maint ){
  1962. my $exclude_id = "";
  1963. my $exclude_port = "";
  1964. foreach my $key (sort keys %{$nodes_maint}){
  1965. if(defined $nodes_maint->{$key}){
  1966. if(length($exclude_id) > 1){
  1967. $exclude_id = $exclude_id . ",";
  1968. $exclude_port = $exclude_port . ",";
  1969. }
  1970. $exclude_id = $exclude_id ."'". $nodes_maint->{$key}->{_ip} ."'";
  1971. $exclude_port = $exclude_port.$nodes_maint->{$key}->{_port} ;
  1972. }
  1973. }
  1974. if(length($exclude_id) > 1){
  1975. $exclude_delete = $exclude_delete . " AND (hostname not in (".$exclude_id.") AND port not in (".$exclude_port."))" ;
  1976. }
  1977. }
  1978. #if a node was found, try to do the failover removing the READ_ONLY
  1979. if(defined $candidate_failover_node && $failover_node){
  1980. return $failover_node->promote_writer($proxynode,$Galera_cluster,$exclude_delete);
  1981. }
  1982. elsif(defined $candidate_failover_node && !defined $failover_node){
  1983. return $candidate_failover_node->promote_writer($proxynode,$Galera_cluster,$exclude_delete);
  1984. }
  1985. else{
  1986. if(!defined $failover_node){
  1987. SWITCH: {
  1988. if ($proxynode->{_require_failover} == 1) { print Utils->print_log(1,"!!!! No node for failover found , try to use active_failover=2 OR add a valid node to the 8000 HG pool \n" ) ; last SWITCH; }
  1989. if ($proxynode->{_require_failover} == 2) { print Utils->print_log(1,"!!!! No node for failover found , try to use active_failover=3 But that may move production to the other segment.\n" ); last SWITCH; }
  1990. if ($proxynode->{_require_failover} == 3) { print Utils->print_log(1,"!!!! No node for failover found also in the other segments, I cannot continue you need to act manually \n" ); last SWITCH; }
  1991. }
  1992. }
  1993. if(defined $failover_node){
  1994. return $failover_node->promote_writer($proxynode,$Galera_cluster,$exclude_delete);
  1995. }
  1996. }
  1997. return 0;
  1998. }
  1999. }
  2000. {
  2001. package ProxySqlHG;
  2002. sub new {
  2003. my $class = shift;
  2004. my $self = {
  2005. _id => undef, #
  2006. _type => undef, # available types: w writer; r reader ; mw maintance writer; mr maintenance reader
  2007. _size => 0,
  2008. };
  2009. bless $self, $class;
  2010. return $self;
  2011. }
  2012. sub id {
  2013. my ( $self, $id ) = @_;
  2014. $self->{_id} = $id if defined($id);
  2015. return $self->{_id};
  2016. }
  2017. sub type {
  2018. my ( $self, $type ) = @_;
  2019. $self->{_type} = $type if defined($type);
  2020. return $self->{_type};
  2021. }
  2022. sub size {
  2023. my ( $self, $size ) = @_;
  2024. $self->{_size} = $size if defined($size);
  2025. return $self->{_size};
  2026. }
  2027. }
  2028. {
  2029. package Utils;
  2030. use Time::HiRes qw(gettimeofday);
  2031. #============================================================================
  2032. ## get_connection -- return a valid database connection handle (or die)
  2033. ## $dsn -- a perl DSN, e.g. "DBI:mysql:host=ltsdbwm1;port=3311"
  2034. ## $user -- a valid username, e.g. "check"
  2035. ## $pass -- a matching password, e.g. "g33k!"
  2036. sub get_connection($$$$) {
  2037. my $dsn = shift;
  2038. my $user = shift;
  2039. my $pass = shift;
  2040. my $SPACER = shift;
  2041. my $dbh = DBI->connect($dsn, $user, $pass , {
  2042. PrintError => 0,
  2043. PrintWarn => 0,
  2044. RaiseError => 0});
  2045. if (!defined($dbh)) {
  2046. #die
  2047. print Utils->print_log(1, "Cannot connect to $dsn as $user\n");
  2048. # Should not die and instead return undef so we can handle this shit
  2049. #die();
  2050. return undef;
  2051. }
  2052. return $dbh;
  2053. }
  2054. ######################################################################
  2055. ## collection functions -- fetch status data from db
  2056. ## get_status -- return a hash ref to SHOW GLOBAL STATUS output
  2057. ## $dbh -- a non-null database handle, as returned from get_connection()
  2058. ##
  2059. sub get_status($$) {
  2060. my $dbh = shift;
  2061. my $debug = shift;
  2062. my %v;
  2063. my $cmd = "show /*!50000 global */ status";
  2064. my $sth = $dbh->prepare($cmd);
  2065. $sth->execute() or warn "Couldn't execute statement: $cmd" . $dbh->errstr ." \n";
  2066. while (my $ref = $sth->fetchrow_hashref()) {
  2067. my $n = $ref->{'Variable_name'};
  2068. $v{"\L$n\E"} = $ref->{'Value'};
  2069. if ($debug>0){print "MySQL status = ".$n."\n";}
  2070. }
  2071. return \%v;
  2072. }
  2073. ######################################################################
  2074. ## collection functions -- fetch status data from db
  2075. ## get_status -- return a hash ref to SHOW GLOBAL STATUS output
  2076. ## $dbh -- a non-null database handle, as returned from get_connection()
  2077. ##
  2078. sub get_status_by_name($$) {
  2079. my $dbh = shift;
  2080. my $debug = shift;
  2081. my $name = shift ;
  2082. my %v;
  2083. my $cmd = "show /*!50000 global */ status like '$name'";
  2084. my $sth = $dbh->prepare($cmd);
  2085. $sth->execute();
  2086. while (my $ref = $sth->fetchrow_hashref()) {
  2087. my $n = $ref->{'Variable_name'};
  2088. $v{"\L$n\E"} = $ref->{'Value'};
  2089. if ($debug>0){print "MySQL status = ".$n."\n";}
  2090. }
  2091. return \%v;
  2092. }
  2093. ##
  2094. ## get_variables -- return a hash ref to SHOW GLOBAL VARIABLES output
  2095. ##
  2096. ## $dbh -- a non-null database handle, as returned from get_connection()
  2097. ##
  2098. sub get_variables($$) {
  2099. my $dbh = shift;
  2100. my $debug = shift;
  2101. my %v;
  2102. my $cmd = "select * from performance_schema.global_variables";
  2103. $dbh->{LongReadLen} = 0;
  2104. $dbh->{LongTruncOk} = 0;
  2105. my $sth = $dbh->prepare($cmd);
  2106. $sth->execute() or warn "Couldn't execute statement: $cmd" . $dbh->errstr ." \n";
  2107. while (my $ref = $sth->fetchrow_hashref()) {
  2108. my $n = $ref->{'VARIABLE_NAME'};
  2109. $v{"\L$n\E"} = $ref->{'VARIABLE_VALUE'};
  2110. # print STDERR "$n : ".$v{$n}. " ZZZZZZZZZZZZZZZZZZ ". $ref->{'Value'} ."\n";
  2111. }
  2112. return \%v;
  2113. }
  2114. ##
  2115. ## get_variables -- return a hash ref to SHOW GLOBAL VARIABLES output
  2116. ##
  2117. ## $dbh -- a non-null database handle, as returned from get_connection()
  2118. ##
  2119. sub get_variablesByName($$) {
  2120. my $dbh = shift;
  2121. my $variableName = shift;
  2122. #my $debug = shift;
  2123. my %v;
  2124. my $cmd = "show variables like '$variableName'";
  2125. my $sth = $dbh->prepare($cmd);
  2126. $sth->execute() or warn "Couldn't execute statement: $cmd" . $dbh->errstr ." \n";
  2127. while (my $ref = $sth->fetchrow_hashref()) {
  2128. my $n = $ref->{'Variable_name'};
  2129. $v{"\L$n\E"} = $ref->{'Value'};
  2130. }
  2131. return \%v;
  2132. }
  2133. ##
  2134. ## get_variables -- return a hash ref to SHOW GLOBAL VARIABLES output
  2135. ##
  2136. ## $dbh -- a non-null database handle, as returned from get_connection()
  2137. ##
  2138. sub get_pxc_clusterview($$) {
  2139. my $dbh = shift;
  2140. my $variableName = shift;
  2141. #my $debug = shift;
  2142. my %v;
  2143. my $cmd = "select * from performance_schema.pxc_cluster_view where UUID = '$variableName'";
  2144. my $sth = $dbh->prepare($cmd);
  2145. $sth->execute() or warn "Couldn't execute statement: $cmd" . $dbh->errstr ." \n";
  2146. my $ref;
  2147. while ( $ref = $sth->fetchrow_hashref()) {
  2148. foreach my $name ('HOST_NAME', 'UUID','STATUS','LOCAL_INDEX','SEGMENT'){
  2149. my $n = lc $name;
  2150. $v{$n} = $ref->{$name};
  2151. }
  2152. }
  2153. return \%v;
  2154. }
  2155. #Print time from invocation with milliseconds
  2156. sub get_current_time{
  2157. use POSIX qw(strftime);
  2158. my $t = gettimeofday();
  2159. my $date = strftime "%Y/%m/%d %H:%M:%S", localtime $t;
  2160. $date .= sprintf ".%03d", ($t-int($t))*1000; # without rounding
  2161. return $date;
  2162. }
  2163. #prit all environmnt variables
  2164. sub debugEnv{
  2165. my $key = keys %ENV;
  2166. foreach $key (sort(keys %ENV)) {
  2167. print $key, '=', $ENV{$key}, "\n";
  2168. }
  2169. }
  2170. #Print a log entry
  2171. sub print_log($$){
  2172. my $log_level = $_[1];
  2173. my $text = $_[2];
  2174. my $log_text = "[ - ] ";
  2175. SWITCH: {
  2176. if ($log_level == 1) { $log_text= "[ERROR] "; last SWITCH; }
  2177. if ($log_level == 2) { $log_text= "[WARN] "; last SWITCH; }
  2178. if ($log_level == 3) { $log_text= "[INFO] "; last SWITCH; }
  2179. if ($log_level == 4) { $log_text= "[DEBUG] "; last SWITCH; }
  2180. }
  2181. return Utils::get_current_time.":".$log_text.$text;
  2182. }
  2183. #trim a string
  2184. sub trim {
  2185. my $s = shift;
  2186. $s =~ s/^\s+|\s+$//g;
  2187. return $s
  2188. };
  2189. }
  2190. # ############################################################################
  2191. # Documentation
  2192. # #################
  2193. =pod
  2194. =head1 NAME
  2195. galera_check.pl
  2196. =head1 OPTIONS
  2197. =over
  2198. galera_check.pl -u=admin -p=admin -h=192.168.1.50 -H=500:W,501:R -P=3310 --main_segment=1 --debug=0 --log <full_path_to_file> --help
  2199. sample [options] [file ...]
  2200. Options:
  2201. -u|user user to connect to the proxy
  2202. -p|password Password for the proxy
  2203. -h|host Proxy host
  2204. -H Hostgroups with role definition. List comma separated.
  2205. Definition R = reader; W = writer [500:W,501:R]
  2206. --main_segment If segments are in use which one is the leading at the moment
  2207. --retry_up The number of loop/test the check has to do before moving a node up (default 0)
  2208. --retry_down The number of loop/test the check has to do before moving a node Down (default 0)
  2209. --log Full path to the log file ie (/var/log/proxysql/galera_check_) the check will add
  2210. the identifier for the specific HG.
  2211. --active_failover A value from 0 to 3, indicating what level/kind of fail-over the script must perform.
  2212. active_failover
  2213. Valid values are:
  2214. 0 [default] do not make failover
  2215. 1 make failover only if HG 8000 is specified in ProxySQL mysl_servers
  2216. 2 use PXC_CLUSTER_VIEW to identify a server in the same segment
  2217. 3 do whatever to keep service up also failover to another segment (use PXC_CLUSTER_VIEW)
  2218. --single_writer Active by default [single_writer = 1 ] if disable will allow to have multiple writers
  2219. Performance parameters
  2220. --check_timeout This parameter set in ms then time the script can alow a thread connecting to a MySQL node to wait, before forcing a returnn.
  2221. In short if a node will take longer then check_timeout its entry will be not filled and it will eventually ignored in the evaluation.
  2222. Setting the debug option =1 and look for [WARN] Check timeout Node ip : Information will tell you how much your nodes are exceeding the allowed limit.
  2223. You can use the difference to correctly set the check_timeout
  2224. Default is 800 ms
  2225. --help help message
  2226. --debug When active the log will have a lot of information about the execution. Parse it for ERRORS if you have problems
  2227. --print_execution Active by default, it will print the execution time the check is taking in the log. This can be used to tune properly the scheduler time, and also the --check_timeout
  2228. --development When set to 1 you can run the script in a loop from bash directly and test what is going to happen
  2229. --development_time Time in seconds that the loop wait to execute when in development mode (default 2 seconds)
  2230. SSL support
  2231. Now the script identify if the node in the ProxySQL table mysql_servers has use_ssl = 1 and will set SSL to be used for that specific entry.
  2232. This means that SSL connection is by ProxySQL mysql_server entry NOT by IP:port combination.
  2233. --ssl_certs_path This parameter allow you to specify a DIRECTORY to use to assign specific certificates.
  2234. At the moment is NOT possible to change the files names and ALL these 3 files must be there and named as follow:
  2235. - client-key.pem
  2236. - client-cert.pem
  2237. - ca.pem
  2238. Script will exit with an error if ssl_certs_pathis declared but not filled properly
  2239. OR if the user running the script doesn't have acces.
  2240. !!NOTE!! SSL connection requires more time to be established. This script is a check that needs to run very fast and constantly.
  2241. force it to use ssl WILL impact in the performance of the check. Tune properly the check_timeout parameter.
  2242. =back
  2243. =head1 DESCRIPTION
  2244. Galera check is a script to manage integration between ProxySQL and Galera (from Codership).
  2245. Galera and its implementations like Percona Cluster (PCX), use the data-centric concept, as such the status of a node is relvant in relation to a cluster.
  2246. In ProxySQL is possible to represent a cluster and its segments using HostGroups.
  2247. Galera check is design to manage a X number of nodes that belong to a given Hostgroup (HG).
  2248. In Galera_check it is also important to qualify the HG in case of use of Replication HG.
  2249. galera_check works by HG and as such it will perform isolated actions/checks by HG.
  2250. It is not possible to have more than one check running on the same HG. The check will create a lock file {proxysql_galera_check_${hg}.pid} that will be used by the check to prevent duplicates.
  2251. Galera_check will connect to the ProxySQL node and retrieve all the information regarding the Nodes/proxysql configuration.
  2252. It will then check in parallel each node and will retrieve the status and configuration.
  2253. At the moment galera_check analyze and manage the following:
  2254. Node states:
  2255. read_only
  2256. wsrep_status
  2257. wsrep_rejectqueries
  2258. wsrep_donorrejectqueries
  2259. wsrep_connected
  2260. wsrep_desinccount
  2261. wsrep_ready
  2262. wsrep_provider
  2263. wsrep_segment
  2264. Number of nodes in by segment
  2265. Retry loop
  2266. - Number of nodes in by segment
  2267. If a node is the only one in a segment, the check will behave accordingly.
  2268. IE if a node is the only one in the MAIN segment, it will not put the node in OFFLINE_SOFT when the node become donor to prevent the cluster to become unavailable for the applications.
  2269. As mention is possible to declare a segment as MAIN, quite useful when managing prod and DR site.
  2270. -The check can be configure to perform retry in a X interval.
  2271. Where X is the time define in the ProxySQL scheduler.
  2272. As such if the check is set to have 2 retry for UP and 4 for down, it will loop that number before doing anything. Given that Galera does some action behind the hood.
  2273. This feature is very useful in some not well known cases where Galera bhave weird.
  2274. IE whenever a node is set to READ_ONLY=1, galera desync and resync the node.
  2275. A check not taking this into account will cause a node to be set OFFLINE and back for no reason.
  2276. Another important differentiation for this check is that it use special HGs for maintenance, all in range of 9000.
  2277. So if a node belong to HG 10 and the check needs to put it in maintenance mode, the node will be moved to HG 9010.
  2278. Once all is normal again, the Node will be put back on his original HG.
  2279. This check does NOT modify any state of the Nodes.
  2280. Meaning It will NOT modify any variables or settings in the original node. It will ONLY change states in ProxySQL.
  2281. The check is still a prototype and is not suppose to go to production (yet).
  2282. =over
  2283. =item 1
  2284. Note that galera_check is also Segment aware, as such the checks on the presence of Writer /reader is done by segment, respecting the MainSegment as primary.
  2285. =back
  2286. =head1 Configure in ProxySQL
  2287. INSERT INTO scheduler (id,active,interval_ms,filename,arg1) values (10,0,2000,"/var/lib/proxysql/galera_check.pl","-u=remoteUser -p=remotePW -h=192.168.1.50 -H=500:W,501:R -P=6032 --retry_down=2 --retry_up=1 --main_segment=1 --debug=0 --active_failover=1 --single_writer=1 --log=/var/lib/proxysql/galeraLog");
  2288. LOAD SCHEDULER TO RUNTIME;SAVE SCHEDULER TO DISK;
  2289. To activate it
  2290. update scheduler set active=1 where id=10;
  2291. LOAD SCHEDULER TO RUNTIME;SAVE SCHEDULER TO DISK;
  2292. To update the parameters you must pass all of them not only the ones you want to change(IE enabling debug)
  2293. update scheduler set arg1="-u=remoteUser -p=remotePW -h=192.168.1.50 -H=500:W,501:R -P=6032 --retry_down=2 --retry_up=1 --main_segment=1 --debug=1 --active_failover=1 --single_writer=1 --log=/var/lib/proxysql/galeraLog" where id =10;
  2294. LOAD SCHEDULER TO RUNTIME;SAVE SCHEDULER TO DISK;
  2295. delete from scheduler where id=10;
  2296. LOAD SCHEDULER TO RUNTIME;SAVE SCHEDULER TO DISK;
  2297. =head1 Rules:
  2298. =over
  2299. =item 1
  2300. Set to offline_soft :
  2301. any non 4 or 2 state, read only =ON
  2302. donor node reject queries - 0 size of cluster > 2 of nodes in the same segments more then one writer, node is NOT read_only.
  2303. Changes to pxc_maint_mode to anything else DISABLED
  2304. =item 2
  2305. change HG t maintenance HG:
  2306. Node/cluster in non primary
  2307. wsrep_reject_queries different from NONE
  2308. Donor, node reject queries =1 size of cluster
  2309. =item 3
  2310. Node comes back from offline_soft when (all of them):
  2311. 1) Node state is 4
  2312. 3) wsrep_reject_queries = none
  2313. 4) Primary state
  2314. =item 4
  2315. Node comes back from maintenance HG when (all of them):
  2316. 1) node state is 4
  2317. 3) wsrep_reject_queries = none
  2318. 4) Primary state
  2319. =item 5
  2320. active_failover
  2321. Valid values are:
  2322. 0 [default] do not make failover
  2323. 1 make failover only if HG 8000 is specified in ProxySQL mysl_servers
  2324. 2 use PXC_CLUSTER_VIEW to identify a server in the same segment
  2325. 3 do whatever to keep service up also failover to another segment (use PXC_CLUSTER_VIEW)
  2326. =item 6
  2327. PXC_MAIN_MODE is fully supported.
  2328. Any node in a state different from pxc_maint_mode=disabled will be set in OFFLINE_SOFT for all the HostGroup.
  2329. =item 7
  2330. internally shunning node.
  2331. While I am trying to rely as much as possible on ProxySQL, given few inefficiencies there are cases when I have to set a node to SHUNNED because ProxySQL doesn't recognize it correctly.
  2332. =back
  2333. =cut