manageServices.js 27 KB

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