manageServices.js 27 KB

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