ServiceComponent.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. <?php
  2. /*
  3. * Licensed to the Apache Software Foundation (ASF) under one
  4. * or more contributor license agreements. See the NOTICE file
  5. * distributed with this work for additional information
  6. * regarding copyright ownership. The ASF licenses this file
  7. * to you under the Apache License, Version 2.0 (the
  8. * "License"); you may not use this file except in compliance
  9. * with the License. You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. */
  19. include_once "State.php";
  20. /**
  21. * ServiceComponent represents one of the constituent components of
  22. * a Service e.g. NameNode is a component of HDFS.
  23. */
  24. class ServiceComponent {
  25. // Name of the component
  26. public $name;
  27. // Name of the service to which the component belongs to
  28. public $serviceName;
  29. // State of the component
  30. public $state;
  31. // Component dependencies
  32. public $dependencies;
  33. // Component dependents
  34. public $dependents;
  35. // Database
  36. public $db;
  37. // Puppet
  38. public $puppet;
  39. // logger
  40. private $logger;
  41. // clusterName
  42. private $clusterName;
  43. // is this a client component
  44. public $isClient;
  45. // current action
  46. private $currentAction;
  47. function __construct($clusterName, $componentName, $serviceName, $componentState,
  48. $db, $puppet, $isClient) {
  49. $this->clusterName = $clusterName;
  50. $this->name = $componentName;
  51. $this->serviceName = $serviceName;
  52. $this->state = $componentState;
  53. $this->db = $db;
  54. $this->puppet = $puppet;
  55. $this->logger = new HMCLogger("ServiceComponent:".$componentName);
  56. $this->logger->log_debug("ServiceComponent: $componentName, $serviceName, $componentState, $isClient");
  57. $this->isClient = $isClient;
  58. $this->currentAction = "";
  59. }
  60. /**
  61. * Persist state into DB
  62. * @param State $state
  63. * @param Transaction $transaction
  64. * @param bool $dryRun
  65. * @param bool $persistTxn - FALSE in case of INSTALL only
  66. */
  67. function setState($state, $transaction, $dryRun, $persistTxn) {
  68. if ($persistTxn) {
  69. $txnProgress = getTransactionProgressFromState($state);
  70. $desc = $this->name."-".$this->currentAction."-"
  71. . TransactionProgress::$PROGRESS[$txnProgress];
  72. if ($dryRun) {
  73. $desc = $this->name."-".$this->currentAction."-PENDING";
  74. }
  75. $result = $this->db->persistTransaction($transaction, State::$STATE[$state],
  76. $desc, TransactionProgress::$PROGRESS[$txnProgress],
  77. "SERVICECOMPONENT", $dryRun);
  78. if ($result['result'] !== 0) {
  79. $this->state == State::FAILED;
  80. $this->logger->log_error("$this->name - ".State::$STATE[$state]);
  81. $this->db->setServiceState($this, $state);
  82. return $result;
  83. }
  84. }
  85. if (!$dryRun) {
  86. $result = $this->db->setServiceComponentState($this->serviceName, $this->name, $state);
  87. if ($result['result'] !== 0) {
  88. $this->state == State::FAILED;
  89. $this->logger->log_error("$this->name - ".State::$STATE[$state]);
  90. $this->db->setServiceState($this, $state);
  91. return $result;
  92. }
  93. }
  94. $this->state = $state;
  95. $this->logger->log_info("$this->name - ".State::$STATE[$state] . " dryRun=$dryRun");
  96. return array("result" => 0, "error" => "");
  97. }
  98. /**
  99. * UnInstall the component.
  100. * @return mixed
  101. * array( "result" => 0, "error" => msg)
  102. */
  103. public function uninstall($transaction, $dryRun) {
  104. $this->currentAction = "UNINSTALL";
  105. // Check if it's already UNINSTALLED
  106. if ($this->state === State::UNINSTALLED) {
  107. $this->logger->log_debug("ServiceComponent $this->name is already UNINSTALLED!");
  108. return array("result" => 0, "error" => "");
  109. }
  110. // Note that we are about to UNINSTALL
  111. $result = $this->setState(State::UNINSTALLING, $transaction, $dryRun, FALSE);
  112. if ($result['result'] !== 0) {
  113. return $result;
  114. }
  115. return $this->setState(State::UNINSTALLED, $transaction, $dryRun, FALSE);
  116. }
  117. /**
  118. * Install the component.
  119. * @return mixed
  120. * array( "result" => 0, "error" => msg)
  121. */
  122. public function install($transaction, $dryRun) {
  123. $this->currentAction = "INSTALL";
  124. // Check if it's already INSTALLED
  125. if ($this->state === State::INSTALLED) {
  126. $this->logger->log_debug("ServiceComponent $this->name is already INSTALLED!");
  127. return array("result" => 0, "error" => "");
  128. }
  129. // Ensure each dependent component is INSTALLED
  130. $result = $this->getDependencies($transaction);
  131. if ($result["result"] !== 0) {
  132. return $result;
  133. }
  134. foreach ($this->dependencies as $dep) {
  135. $subTxn = $transaction->createSubTransaction();
  136. $s = $dep->install($subTxn, $dryRun);
  137. $depResult = $s['result'];
  138. $depErrMsg = $s['error'];
  139. if ($depResult !== 0) {
  140. return array("result" => $depResult, "error" => "Failed to start $dep->name with $depResult (\'$depErrMsg\')");
  141. }
  142. }
  143. // Note that we are about to INSTALL
  144. $result = $this->setState(State::INSTALLING, $transaction, $dryRun, FALSE);
  145. if ($result['result'] !== 0) {
  146. return $result;
  147. }
  148. return $this->setState(State::INSTALLED, $transaction, $dryRun, FALSE);
  149. }
  150. /**
  151. * Start the component.
  152. * @return mixed
  153. * array( "result" => 0, "error" => msg)
  154. */
  155. public function start($transaction, $dryRun) {
  156. $this->currentAction = "START";
  157. if ($this->isClient) {
  158. // no-op for clients
  159. return array( "result" => 0, "error" => "");
  160. }
  161. // Check if it's already STARTED
  162. if ($this->state === State::STARTED) {
  163. $this->logger->log_debug("ServiceComponent $this->name is already STARTED!");
  164. return array("result" => 0, "error" => "");
  165. }
  166. // Ensure state is INSTALLED or STOPPED or FAILED
  167. if ($this->state !== State::INSTALLED
  168. && $this->state !== State::STARTING
  169. && $this->state !== State::STOPPING
  170. && $this->state !== State::STOPPED
  171. && $this->state !== State::FAILED) {
  172. $this->logger->log_error("ServiceComponent $this->name is not INSTALLED or STOPPED or FAILED!");
  173. return array("result" => -1, "error" => "ServiceComponent $this->name is not INSTALLED or STOPPED or FAILED!");
  174. }
  175. // Ensure each dependent component is STARTED
  176. $result = $this->getDependencies($transaction);
  177. if ($result["result"] !== 0) {
  178. return $result;
  179. }
  180. foreach ($this->dependencies as $dep) {
  181. $s = $dep->start($transaction->createSubTransaction(), $dryRun);
  182. $depResult = $s['result'];
  183. $depErrMsg = $s['error'];
  184. if ($depResult !== 0) {
  185. return array("result" => $depResult, "error" => "Failed to start $dep->name with $depResult (\'$depErrMsg\')");
  186. }
  187. }
  188. // Note that we are about to START
  189. $result = $this->setState(State::STARTING, $transaction, $dryRun, TRUE);
  190. if ($result['result'] !== 0) {
  191. return $result;
  192. }
  193. // Start self
  194. //$this->logger->log_error("TODO: Call out for Puppet::start on $this->name (generate site.pp & kick)");
  195. $nodes = $this->getNodes();
  196. if ($nodes['result'] !== 0) {
  197. $this->setState(State::FAILED, $transaction, $dryRun, TRUE);
  198. return $nodes;
  199. }
  200. if (!$dryRun) {
  201. $result = $this->puppet->kickPuppet($nodes['nodes'], $transaction,
  202. $this->clusterName, array ( $this->name => $nodes['nodes'] ));
  203. $this->logger->log_debug("Puppet kick response for starting component on "
  204. . " cluster=" . $this->clusterName
  205. . ", servicecomponent=" . $this->name
  206. . ", txn=" . $transaction->toString()
  207. . ", response=" . print_r($result, true));
  208. // handle puppet response
  209. $opStatus = array ( "nodeReport" =>
  210. array ( "PUPPET_KICK_FAILED" => $result[KICKFAILED],
  211. "PUPPET_OPERATION_FAILED" => $result[FAILEDNODES],
  212. "PUPPET_OPERATION_TIMEDOUT" => $result[TIMEDOUTNODES],
  213. "PUPPET_OPERATION_SUCCEEDED" => $result[SUCCESSFULLNODES]));
  214. $this->logger->log_info("Persisting puppet report for starting "
  215. . $this->name);
  216. $this->db->persistTransactionOpStatus($transaction,
  217. json_encode($opStatus));
  218. if ($result['result'] !== 0) {
  219. $this->logger->log_error("Puppet kick failed, result="
  220. . $result['result']);
  221. $this->setState(State::FAILED, $transaction, $dryRun, TRUE);
  222. return $result;
  223. }
  224. if (count($nodes['nodes']) > 0
  225. && count($result[SUCCESSFULLNODES]) == 0) {
  226. $this->logger->log_error("Puppet kick failed, no successful nodes");
  227. $this->setState(State::FAILED, $transaction, $dryRun, TRUE);
  228. return array ( "result" => -3,
  229. "error" => "Puppet kick failed on all nodes");
  230. }
  231. }
  232. // Done!
  233. return $this->setState(State::STARTED, $transaction, $dryRun, TRUE);
  234. }
  235. /**
  236. * Get nodes on which this component is installed.
  237. * @return mixed
  238. * array("result" => 0, "error" => "", "nodes" => array())
  239. */
  240. public function getNodes() {
  241. return $this->db->getComponentNodes($this);
  242. }
  243. /**
  244. * Stop the component.
  245. * @return mixed
  246. * array( "result" => 0, "error" => msg)
  247. */
  248. public function stop($transaction, $dryRun) {
  249. $this->currentAction = "STOP";
  250. if ($this->isClient) {
  251. // no-op for clients
  252. return array( "result" => 0, "error" => "");
  253. }
  254. // Check if it's already STOPPED
  255. if ($this->state === State::STOPPED) {
  256. $this->logger->log_debug("ServiceComponent $this->name is already STOPPED!");
  257. return array("result" => 0, "error" => "");
  258. }
  259. // Only stop if state is STARTED/STARTING/STOPPING/FAILED
  260. if ($this->state !== State::STARTED
  261. && $this->state !== State::STARTING
  262. && $this->state !== State::STOPPING
  263. && $this->state !== State::FAILED) {
  264. $this->logger->log_error("ServiceComponent $this->name is not STARTED/FAILED!"
  265. . "Current state = " . State::$STATE[$this->state]
  266. . " - STOP is a no-op");
  267. return array("result" => 0, "error" => "");
  268. }
  269. // Ensure each dependent component is STOPPED
  270. $result = $this->getDependents($transaction);
  271. if ($result["result"] !== 0) {
  272. return $result;
  273. }
  274. foreach ($this->dependents as $dep) {
  275. $s = $dep->stop($transaction->createSubTransaction(), $dryRun);
  276. $depResult = $s['result'];
  277. $depErrMsg = $s['error'];
  278. if ($depResult !== 0) {
  279. return array("result" => $depResult, "error" => "Failed to stop $dep->name with $depResult (\'$depErrMsg\')");
  280. }
  281. }
  282. // Note we are about to STOP
  283. $result = $this->setState(State::STOPPING, $transaction, $dryRun, TRUE);
  284. if ($result['result'] !== 0) {
  285. return $result;
  286. }
  287. // Stop self
  288. $nodes = $this->getNodes();
  289. if ($nodes['result'] !== 0) {
  290. $this->setState(State::FAILED, $transaction, $dryRun, TRUE);
  291. return $nodes;
  292. }
  293. if (!$dryRun) {
  294. $result = $this->puppet->kickPuppet($nodes['nodes'], $transaction,
  295. $this->clusterName, array ( $this->name => $nodes['nodes'] ));
  296. $this->logger->log_debug("Puppet kick response for stopping component on"
  297. . " cluster=" . $this->clusterName
  298. . ", servicecomponent=" . $this->name
  299. . ", txn=" . $transaction->toString()
  300. . ", response=" . print_r($result, true));
  301. // handle puppet response
  302. $opStatus = array ( "nodeReport" =>
  303. array ( "PUPPET_KICK_FAILED" => $result[KICKFAILED],
  304. "PUPPET_OPERATION_FAILED" => $result[FAILEDNODES],
  305. "PUPPET_OPERATION_TIMEDOUT" => $result[TIMEDOUTNODES],
  306. "PUPPET_OPERATION_SUCCEEDED" => $result[SUCCESSFULLNODES]));
  307. $this->logger->log_info("Persisting puppet report for stopping "
  308. . $this->name);
  309. $this->db->persistTransactionOpStatus($transaction,
  310. json_encode($opStatus));
  311. if ($result['result'] !== 0) {
  312. $this->setState(State::FAILED, $transaction, $dryRun, TRUE);
  313. return $result;
  314. }
  315. if (count($nodes['nodes']) > 0
  316. && count($result[SUCCESSFULLNODES]) == 0) {
  317. $this->logger->log_error("Puppet kick failed, no successful nodes");
  318. $this->setState(State::FAILED, $transaction, $dryRun, TRUE);
  319. return array ( "result" => -3,
  320. "error" => "Puppet kick failed on all nodes");
  321. }
  322. }
  323. // Done!
  324. return $this->setState(State::STOPPED, $transaction, $dryRun, TRUE);
  325. }
  326. private function getDependencies($transaction) {
  327. if (!isset($this->dependencies)) {
  328. $this->dependencies = $this->db->getComponentDependencies($this->serviceName, $this->name);
  329. }
  330. return $this->checkDBReturn($transaction, $this->dependencies);
  331. }
  332. private function getDependents($transaction) {
  333. if (!isset($this->dependents)) {
  334. $this->dependents = $this->db->getComponentDependents($this->serviceName, $this->name);
  335. }
  336. return $this->checkDBReturn($transaction, $this->dependents);
  337. }
  338. private function checkDBReturn($transaction, $dbResult) {
  339. if ($dbResult === FALSE) {
  340. $trace = debug_backtrace();
  341. $this->logger->log_error("DB Error: " . $trace[1]["function"]);
  342. $this->setState(State::FAILED, $transaction, FALSE, TRUE);
  343. return array("result" => $dbResult, "error" => "Failed to update db for $this->name with $dbResult");
  344. }
  345. return array("result" => 0, "error" => "");
  346. }
  347. }
  348. ?>