manageServices.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. /*
  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,
  14. * software distributed under the License is distributed on an
  15. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  16. * KIND, either express or implied. See the License for the
  17. * specific language governing permissions and limitations
  18. * under the License.
  19. *
  20. */
  21. /* Declarations of global data. */
  22. var fetchClusterServicesPoller;
  23. var clusterServices;
  24. // Storing globally for the sake of multiple screens in reconfigure
  25. var localReconfigureServiceData = {};
  26. var remoteReconfigureServiceData = {};
  27. var confirmationDataPanelBodyContent = '';
  28. var confirmationDataPanel;
  29. var panelNoButton = {
  30. value: 'Cancel',
  31. action: function (e) {
  32. e.preventDefault();
  33. hideAndDestroyPanel();
  34. },
  35. section: 'footer'
  36. };
  37. var panelYesButton;
  38. // Only one service can be reconfigured at a time.
  39. var reconfigLevelOneYesButton;
  40. var reconfigLevelTwoNoButton;
  41. function showPanel() {
  42. showPanel(function() {});
  43. }
  44. function showPanel(postShowFn) {
  45. confirmationDataPanel.set('y', 200);
  46. confirmationDataPanel.set('x', (globalYui.one('body').get('region').width - confirmationDataPanel.get('width'))/2);
  47. confirmationDataPanel.show();
  48. if (postShowFn != null) {
  49. postShowFn.call();
  50. }
  51. }
  52. function hidePanel(postHideFn) {
  53. if (postHideFn != null) {
  54. postHideFn.call();
  55. }
  56. }
  57. function hideAndDestroyPanel() {
  58. hidePanel(function() {
  59. confirmationDataPanel.hide();
  60. destroyInformationalPanel(confirmationDataPanel);
  61. });
  62. }
  63. function getTitleForReconfiguration(serviceName) {
  64. return 'Make Configuration Changes for ' + serviceName;
  65. }
  66. function setupReconfigureFirstScreen(serviceName) {
  67. var panelTitle = getTitleForReconfiguration(serviceName);
  68. confirmationDataPanel.set( 'headerContent', panelTitle);
  69. confirmationDataPanel.set( 'bodyContent', confirmationDataPanelBodyContent);
  70. // Remove buttons from previous stage
  71. confirmationDataPanel.removeButton(0);
  72. confirmationDataPanel.removeButton(0);
  73. confirmationDataPanel.addButton( panelNoButton );
  74. confirmationDataPanel.addButton( reconfigLevelOneYesButton );
  75. }
  76. function setupReconfigureSecondScreen(serviceName) {
  77. var affectedServices = clusterServices[serviceName].dependencies;
  78. var dependents = clusterServices[serviceName].dependents;
  79. for (dep in dependents) {
  80. affectedServices.push(dependents[dep]);
  81. }
  82. var panelContent = 'Affected services:' + getAffectedDependenciesMarkup(affectedServices, serviceName, 'reconfigure');
  83. var panelTitle = 'Review changes to ' + serviceName + '\'s configuration';
  84. confirmationDataPanel.set( 'headerContent', panelTitle);
  85. confirmationDataPanel.set( 'bodyContent', panelContent);
  86. // Remove buttons from previous stage
  87. confirmationDataPanel.removeButton(0);
  88. confirmationDataPanel.removeButton(0);
  89. confirmationDataPanel.addButton( reconfigLevelTwoNoButton );
  90. confirmationDataPanel.addButton( panelYesButton );
  91. }
  92. // Clean up the affected-services list to only include appropriate installed long-running services
  93. function getAffectedDependenciesMarkup(affectedServices, serviceName, action) {
  94. var affectedDependenciesMarkup = '';
  95. var serviceDisplayName = clusterServices[serviceName].displayName;
  96. var deps = affectedServices;
  97. affectedServices = [];
  98. for (dep in deps) {
  99. var svc = deps[dep];
  100. if (clusterServices.hasOwnProperty(svc) && (clusterServices[svc].isEnabled == 1) && clusterServices[svc].attributes.runnable ) {
  101. affectedServices.push(svc);
  102. }
  103. }
  104. var dependencyMarkup = "";
  105. for (affectedSrvc in affectedServices) {
  106. if (clusterServices[affectedServices[affectedSrvc]].attributes.runnable) {
  107. dependencyMarkup += '<tr><td>' + clusterServices[affectedServices[affectedSrvc]].displayName + '</td><td>' + titleCase(clusterServices[affectedServices[affectedSrvc]].state) + '</td></tr>';
  108. }
  109. }
  110. if (dependencyMarkup != '') {
  111. // Add this service at the top of the list
  112. dependencyMarkup = '<table><thead><th>Service name</th><th>Current state</th></thead><tr><td>' + serviceDisplayName + '</td><td>' + titleCase(clusterServices[serviceName].state) + '</td></tr>' + dependencyMarkup + '</table>';
  113. affectedDependenciesMarkup += 'Including this service and all its recursive dependencies, the following is the list of services that will be affected by ' + action + ' of ' + serviceName + ' :' +
  114. '<br/>' +
  115. '<div id="manageServicesDisplayDepsOnAction">' +
  116. dependencyMarkup +
  117. '</div>';
  118. }
  119. return affectedDependenciesMarkup;
  120. }
  121. function setupStartServiceScreen(serviceName) {
  122. setupStartStopServiceScreen('start', serviceName);
  123. }
  124. function setupStopServiceScreen(serviceName) {
  125. setupStartStopServiceScreen('stop', serviceName);
  126. }
  127. function setupStartStopServiceScreen(action, serviceName) {
  128. var serviceDisplayName = clusterServices[serviceName].displayName;
  129. var affectedServices;
  130. var confirmationDataPanelTitle;
  131. if ( action == 'start') {
  132. confirmationDataPanelTitle = 'Starting ' + serviceDisplayName;
  133. confirmationDataPanelBodyContent = "We are now going to start " + serviceDisplayName + "...<br/><br/>";
  134. affectedServices = clusterServices[serviceName].dependencies;
  135. } else if (action == 'stop') {
  136. confirmationDataPanelTitle = 'Stopping ' + serviceDisplayName;
  137. confirmationDataPanelBodyContent = "We are now going to stop " + serviceDisplayName + "...<br/><br/>";
  138. affectedServices = clusterServices[serviceName].dependents;
  139. }
  140. confirmationDataPanelBodyContent += getAffectedDependenciesMarkup(affectedServices, serviceName, action);
  141. confirmationDataPanelBodyContent = '<div id="confirmationDataPanelBodyContent">' + confirmationDataPanelBodyContent + '</div>';
  142. confirmationDataPanel.set( 'headerContent', confirmationDataPanelTitle);
  143. confirmationDataPanel.set( 'bodyContent', confirmationDataPanelBodyContent);
  144. confirmationDataPanel.set( 'height', 400);
  145. confirmationDataPanel.set( 'width', 800);
  146. confirmationDataPanel.addButton( panelNoButton);
  147. confirmationDataPanel.addButton( panelYesButton );
  148. showPanel();
  149. }
  150. function setupStartAllServicesScreen() {
  151. setupStartStopAllServicesScreen('startAll');
  152. }
  153. function setupStopAllServicesScreen() {
  154. setupStartStopAllServicesScreen('stopAll');
  155. }
  156. function setupStartStopAllServicesScreen(action) {
  157. var confirmationDataPanelTitle;
  158. var confirmationDataPanelBodyContent;
  159. if ( action == 'startAll' ) {
  160. confirmationDataPanelTitle = 'Start All Services';
  161. confirmationDataPanelBodyContent = "We are now going to start all services in the cluster";
  162. } else if ( action == 'stopAll' ) {
  163. confirmationDataPanelTitle = 'Stop All Services';
  164. confirmationDataPanelBodyContent = "We are now going to stop all the services in the cluster";
  165. }
  166. confirmationDataPanel.set( 'headerContent', confirmationDataPanelTitle);
  167. confirmationDataPanel.set( 'bodyContent', confirmationDataPanelBodyContent);
  168. confirmationDataPanel.set( 'height', 400);
  169. confirmationDataPanel.set( 'width', 800);
  170. confirmationDataPanel.addButton( panelNoButton);
  171. confirmationDataPanel.addButton( panelYesButton );
  172. showPanel();
  173. }
  174. function setupReconfigureScreens(serviceName) {
  175. // TODO: Needed for others too?
  176. /* First, (temporarily) stop any further fetches. */
  177. fetchClusterServicesPoller.stop();
  178. reconfigLevelOneYesButton = {
  179. value: 'Apply Changes',
  180. action: function (e) {
  181. e.preventDefault();
  182. localReconfigureServiceData = configureServicesUtil.generateUserOpts();
  183. var remoteProps = remoteReconfigureServiceData.services[serviceName].properties;
  184. var localProps = localReconfigureServiceData[serviceName].properties;
  185. var allEqual = true;
  186. for (key in localProps) {
  187. var remoteValue = remoteProps[key].value;
  188. var localValue = localProps[key]["value"];
  189. if ( localValue != remoteValue) {
  190. allEqual = false;
  191. }
  192. }
  193. if (allEqual) {
  194. alert("You haven't made any changes");
  195. return;
  196. }
  197. hidePanel(function() {
  198. // Store the requestData and the html
  199. confirmationDataPanelBodyContent = confirmationDataPanel.get( 'bodyContent' );
  200. setupReconfigureSecondScreen(serviceName);
  201. showPanel();
  202. });
  203. },
  204. classNames: 'okButton',
  205. section: 'footer'
  206. };
  207. reconfigLevelTwoNoButton = {
  208. value: 'Go back and re-edit',
  209. action: function (e) {
  210. e.preventDefault();
  211. hidePanel(function() {
  212. setupReconfigureFirstScreen(serviceName);
  213. showPanel();
  214. });
  215. },
  216. section: 'footer'
  217. };
  218. // Render first with a loading image and then get config items
  219. confirmationDataPanelBodyContent =
  220. "<img id=errorInfoPanelLoadingImgId class=loadingImg src=../images/loading.gif />";
  221. var confirmationDataPanelTitle = getTitleForReconfiguration(serviceName);
  222. confirmationDataPanel.set( 'height', 500);
  223. confirmationDataPanel.set( 'width', 1000);
  224. confirmationDataPanel.set( 'headerContent', confirmationDataPanelTitle);
  225. confirmationDataPanel.set( 'bodyContent', confirmationDataPanelBodyContent );
  226. showPanel();
  227. executeStage( '../php/frontend/fetchClusterServices.php?clusterName=' + clusterName +
  228. '&getConfigs=true&serviceName=' + serviceName, function (serviceConfigurationData) {
  229. // Store the remote data
  230. remoteReconfigureServiceData = serviceConfigurationData;
  231. var serviceConfigurationMarkup = configureServicesUtil.getOptionsSummaryMarkup(serviceConfigurationData, true);
  232. if( globalYui.Lang.trim( serviceConfigurationMarkup).length == 0 ) {
  233. serviceConfigurationMarkup = '<p>There is nothing to reconfigure for this service.</p>';
  234. }
  235. else {
  236. /* Augment confirmationDataPanel with the relevant buttons only if there
  237. * is something of value to show.
  238. */
  239. confirmationDataPanel.addButton( panelNoButton );
  240. confirmationDataPanel.addButton( reconfigLevelOneYesButton );
  241. }
  242. /* XXX Note that this must be kept in-sync with the corresponding markup
  243. * on the InstallationWizard page.
  244. */
  245. confirmationDataPanelBodyContent =
  246. '<div id=formStatusDivId class=formStatusBar style="display:none">'+
  247. 'Placeholder' +
  248. '</div>' +
  249. '<div id=configureClusterAdvancedCoreDivId>' +
  250. '<form id=configureClusterAdvancedFormId>' +
  251. '<fieldset id=configureClusterAdvancedFieldSetId>' +
  252. '<div id=configureClusterAdvancedDynamicRenderDivId>' +
  253. serviceConfigurationMarkup +
  254. '</div>' +
  255. '</fieldset>' +
  256. '</form>' +
  257. '</div>';
  258. confirmationDataPanelBodyContent = '<div id="confirmationDataPanelBodyContent">' + confirmationDataPanelBodyContent + '</div>';
  259. confirmationDataPanel.set( 'bodyContent', confirmationDataPanelBodyContent );
  260. });
  261. }
  262. function performServiceManagement( action, serviceName, confirmationDataPanel ) {
  263. /* First, (temporarily) stop any further fetches. */
  264. fetchClusterServicesPoller.stop();
  265. var manageServicesRequestData = {
  266. action: action,
  267. services: {}
  268. };
  269. if( action == "reconfigure" ) {
  270. manageServicesRequestData.services = localReconfigureServiceData;
  271. }
  272. else {
  273. /* Need to explicitly set a key named for serviceName this way because it's
  274. * a variable - in the future, the value will be a filled-out array (for
  275. * now, we only support managing a single service at a time).
  276. */
  277. manageServicesRequestData.services[serviceName] = {};
  278. }
  279. globalYui.io( "../php/frontend/manageServices.php?clusterName=" + clusterName, {
  280. method: 'POST',
  281. data: globalYui.JSON.stringify(manageServicesRequestData),
  282. timeout: 10000,
  283. on: {
  284. success: function(x, o) {
  285. globalYui.log("RAW JSON DATA: " + o.responseText);
  286. var manageServicesResponseJson;
  287. try {
  288. manageServicesResponseJson = globalYui.JSON.parse(o.responseText);
  289. }
  290. catch (e) {
  291. alert("JSON Parse failed!");
  292. return;
  293. }
  294. globalYui.log(globalYui.Lang.dump(manageServicesResponseJson));
  295. /* Check that manageServicesResponseJson actually indicates success. */
  296. if( manageServicesResponseJson.result == 0 ) {
  297. /* Only on success should we destroy confirmationDataPanel - on
  298. * failure, we depend on the fact that there'll be errors shown
  299. * inside the panel that the user will want/need to interact with.
  300. */
  301. hideAndDestroyPanel();
  302. renderManageServicesProgress( manageServicesResponseJson.response );
  303. }
  304. else {
  305. /* No need to hide confirmationDataPanel here - there are errors
  306. * that need to be handled.
  307. */
  308. if (action == 'reconfigure') {
  309. hidePanel(function() {
  310. setupReconfigureFirstScreen(serviceName);
  311. showPanel( function() {
  312. configureServicesUtil.handleConfigureServiceErrors( manageServicesResponseJson );
  313. });
  314. });
  315. } else {
  316. // Can't do anything for others
  317. alert('Got error during ' + action + ' : ' + globalYui.Lang.dump(manageServicesResponseJson));
  318. }
  319. }
  320. },
  321. failure: function(x, o) {
  322. alert("Async call failed!");
  323. }
  324. }
  325. });
  326. }
  327. function getServiceConfigurationMarkup( serviceConfigurationData ) {
  328. return serviceConfigurationMarkup;
  329. }
  330. function serviceManagementActionClickHandler( action, serviceName ) {
  331. // Reinit the global content
  332. confirmationDataPanelBodyContent = '';
  333. var confirmationDataPanelTitle = ''; // Set title later
  334. /* Create the panel that'll display our confirmation/data dialog. */
  335. confirmationDataPanel =
  336. createInformationalPanel( '#informationalPanelContainerDivId', confirmationDataPanelTitle );
  337. panelYesButton = {
  338. value: 'OK',
  339. action: function (e) {
  340. e.preventDefault();
  341. performServiceManagement( action, serviceName, confirmationDataPanel );
  342. },
  343. classNames: 'okButton',
  344. section: 'footer'
  345. };
  346. if ( action == 'start') {
  347. setupStartServiceScreen(serviceName);
  348. } else if ( action == 'stop') {
  349. setupStopServiceScreen(serviceName);
  350. } else if( action == 'startAll' ) {
  351. setupStartAllServicesScreen();
  352. } else if( action == 'stopAll' ) {
  353. setupStopAllServicesScreen();
  354. } else if( action == 'reconfigure' ) {
  355. setupReconfigureScreens(serviceName);
  356. }
  357. }
  358. function deduceServiceManagementEntryCssClass( serviceInfo ) {
  359. var serviceManagementEntryCssClass = '';
  360. var serviceState = serviceInfo.state;
  361. if( serviceState.match(/^stop/i) || serviceState.match(/^fail/i) ) {
  362. serviceManagementEntryCssClass = "serviceManagementEntryStopped";
  363. }
  364. else if( serviceState.match(/^start/i) ) {
  365. serviceManagementEntryCssClass = "serviceManagementEntryStarted";
  366. }
  367. else if( serviceState.match(/^install/i) ) {
  368. serviceManagementEntryCssClass = "serviceManagementEntryInstalled";
  369. }
  370. else if( serviceState.match(/^uninstall/i) ) {
  371. serviceManagementEntryCssClass = "serviceManagementEntryUninstalled";
  372. }
  373. // globalYui.log( "Picking CSS class for" + serviceInfo.serviceName + ": " + serviceManagementEntryCssClass );
  374. return serviceManagementEntryCssClass;
  375. }
  376. function generateServiceManagementEntryMarkup( serviceName, serviceInfo ) {
  377. var generatedServiceManagementEntryMarkup = '';
  378. var serviceAttributes = serviceInfo.attributes;
  379. /* Only generate a Service Management entry for services that are:
  380. *
  381. * a) enabled
  382. * b) runnable
  383. * c) meant to be displayed
  384. */
  385. if( (serviceInfo.isEnabled == true) && !serviceAttributes.noDisplay ) {
  386. var serviceManagementEntryCssClass = deduceServiceManagementEntryCssClass( serviceInfo );
  387. generatedServiceManagementEntryMarkup +=
  388. '<li class="serviceManagementEntry '+ serviceManagementEntryCssClass + '">' +
  389. '<div id="serviceManagementFor' + serviceName + '">' +
  390. '<span class="serviceManagementEntryNameContainer">' +
  391. '<a href="javascript:void(null)" name="' + serviceName + '" class="serviceManagementEntryName">' +
  392. serviceInfo.displayName +
  393. '</a>' +
  394. '</span>' +
  395. '<div class="serviceManagementEntryStateContainer">' +
  396. titleCase(serviceInfo.state) +
  397. '</div>' +
  398. '<div class="serviceManagementEntryActionsContainer">';
  399. if( serviceAttributes.runnable ) {
  400. var serviceManagementEntryAnchorName = '';
  401. var serviceManagementEntryAnchorTitle = '';
  402. var serviceManagementEntryAnchorCssClasses = 'serviceManagementEntryAction btn ';
  403. var serviceManagementEntryIconCssClass = '';
  404. /* Already-started/stopped services shouldn't allow a start/stop operation on them. */
  405. if( serviceInfo.state == 'STOPPED' || serviceInfo.state == 'FAILED') {
  406. serviceManagementEntryAnchorName = 'start';
  407. serviceManagementEntryAnchorTitle = 'Start';
  408. serviceManagementEntryAnchorCssClasses += 'serviceManagementEntryActionStart';
  409. serviceManagementEntryIconCssClass = 'iconic-play';
  410. }
  411. else if ( serviceInfo.state == 'STARTED' ) {
  412. serviceManagementEntryAnchorName = 'stop';
  413. serviceManagementEntryAnchorTitle = 'Stop';
  414. serviceManagementEntryAnchorCssClasses += 'serviceManagementEntryActionStop';
  415. serviceManagementEntryIconCssClass = 'iconic-stop';
  416. }
  417. else if ( serviceInfo.state == 'STOPPING') {
  418. serviceManagementEntryAnchorName = 'start';
  419. serviceManagementEntryAnchorTitle = 'Start';
  420. serviceManagementEntryAnchorCssClasses += 'serviceManagementEntryActionStart disabled';
  421. serviceManagementEntryIconCssClass = 'iconic-start disabled';
  422. }
  423. else if ( serviceInfo.state == 'STARTING') {
  424. serviceManagementEntryAnchorName = 'stop';
  425. serviceManagementEntryAnchorTitle = 'Stop';
  426. serviceManagementEntryAnchorCssClasses += 'serviceManagementEntryActionStop disabled';
  427. serviceManagementEntryIconCssClass = 'iconic-stop disabled';
  428. }
  429. generatedServiceManagementEntryMarkup +=
  430. '<a href="javascript:void(null)" name="' + serviceManagementEntryAnchorName + '" ' +
  431. 'title="' + serviceManagementEntryAnchorTitle + '" ' +
  432. 'class="' + serviceManagementEntryAnchorCssClasses + '"><i class="' + serviceManagementEntryIconCssClass + '"></i></a> ';
  433. }
  434. var notReconfigurable = [ 'PIG', 'SQOOP', 'OOZIE', 'TEMPLETON', 'GANGLIA', 'HIVE' ];
  435. var reconfigureClass;
  436. if (globalYui.Array.indexOf(notReconfigurable, serviceName) >= 0) {
  437. reconfigureClass = 'serviceManagementEntryActionReconfigure disabled';
  438. } else {
  439. reconfigureClass = 'serviceManagementEntryActionReconfigure';
  440. }
  441. generatedServiceManagementEntryMarkup += '<a href="javascript:void(null)" name="reconfigure" title="Reconfigure" ' +
  442. 'class="btn serviceManagementEntryAction ' + reconfigureClass + '"><i class="iconic-cog"></i></a>' +
  443. '</div>' +
  444. '</div>' +
  445. '</li>';
  446. }
  447. return generatedServiceManagementEntryMarkup;
  448. }
  449. // Do Not Remove --> We'll uncomment this section when the Service names link to something meaningful.
  450. //
  451. // /* Register click handlers for the service links themselves. */
  452. // globalYui.one('#serviceManagementListId').delegate('click', function (e) {
  453. // alert(this.getAttribute('name'));
  454. // }, 'li.serviceManagementEntry span.serviceManagementEntryNameContainer a.serviceManagementEntryName' );
  455. /* Register click handlers for the global-action buttons. */
  456. globalYui.one('#serviceManagementGlobalActionsDivId').delegate('click', function (e) {
  457. var action = this.getAttribute('name');
  458. serviceManagementActionClickHandler( action );
  459. }, 'button' );
  460. /* Register click handlers for the action links for each service. */
  461. globalYui.one('#serviceManagementListId').delegate('click', function (e) {
  462. var action = this.getAttribute('name');
  463. var serviceName = this.ancestor('li.serviceManagementEntry').
  464. one('span.serviceManagementEntryNameContainer a.serviceManagementEntryName').getAttribute('name');
  465. serviceManagementActionClickHandler( action, serviceName );
  466. }, 'li.serviceManagementEntry div.serviceManagementEntryActionsContainer a.serviceManagementEntryAction' );
  467. /* Main() */
  468. /* The clusterName variable is set in the Javascript scaffolding spit out by manageServices.php */
  469. var fetchClusterServicesPollerContext = {
  470. source: '../php/frontend/fetchClusterServices.php',
  471. schema: {
  472. metaFields: {
  473. services: 'response.services'
  474. }
  475. },
  476. request: '?clusterName=' + clusterName,
  477. /* TODO XXX Change this from 5 seconds to 1 minute. */
  478. pollInterval: 5000,
  479. maxFailedAttempts: 5
  480. };
  481. var fetchClusterServicesPollerResponseHandler = {
  482. success: function (e, pdp) {
  483. /* Clear the screen of the loading image (in case it's currently showing). */
  484. hideLoadingImg();
  485. /* The data from our backend. */
  486. clusterServices = e.response.meta.services;
  487. /* What we're here to render. */
  488. var serviceManagementMarkup = '';
  489. // Separate block for client-only software
  490. /*
  491. var clientOnlySoftwareMarkup = '';
  492. for (var serviceName in clusterServices) {
  493. var serviceInfo = clusterServices[serviceName];
  494. if (clusterServices.hasOwnProperty(serviceName) && !serviceInfo.attributes.runnable) {
  495. clientOnlySoftwareMarkup += generateServiceManagementEntryMarkup( serviceName, serviceInfo );
  496. }
  497. }
  498. if (clientOnlySoftwareMarkup != '') {
  499. serviceManagementMarkup += '<div class="serviceManagementGroup"><h2>Client-only software</h2><ul>';
  500. serviceManagementMarkup += clientOnlySoftwareMarkup;
  501. serviceManagementMarkup += '</div>';
  502. }
  503. */
  504. // Real services with server side components
  505. serviceManagementMarkup += '<div class="serviceManagementGroup" style="margin-top:30px"><ul>';
  506. for (var serviceName in clusterServices) {
  507. var serviceInfo = clusterServices[serviceName];
  508. if (clusterServices.hasOwnProperty(serviceName) && serviceInfo.attributes.runnable) {
  509. serviceManagementMarkup += generateServiceManagementEntryMarkup( serviceName, serviceInfo );
  510. }
  511. }
  512. serviceManagementMarkup += '</ul></div>';
  513. /* Link the newly-generated serviceManagementMarkup into the DOM. */
  514. globalYui.one("#serviceManagementDynamicRenderDivId").setContent( serviceManagementMarkup );
  515. /* If serviceManagementMarkup is non-empty, unveil the contents of
  516. * #serviceManagementGlobalActionsDivId (which contains the StartAll
  517. * and StopAll buttons) as well.
  518. */
  519. if( globalYui.Lang.trim( serviceManagementMarkup ).length > 0 ) {
  520. globalYui.one("#serviceManagementGlobalActionsDivId").setStyle( 'display', 'block' );
  521. }
  522. },
  523. failure: function (e, pdp) {
  524. /* Clear the screen of the loading image (in case it's currently showing). */
  525. hideLoadingImg();
  526. alert('Failed to fetch cluster services!');
  527. }
  528. };
  529. fetchClusterServicesPoller = new PeriodicDataPoller
  530. ( fetchClusterServicesPollerContext, fetchClusterServicesPollerResponseHandler );
  531. /* Kick the polling loop off. */
  532. fetchClusterServicesPoller.start();