job_browser.mako 89 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166
  1. ## Licensed to Cloudera, Inc. under one
  2. ## or more contributor license agreements. See the NOTICE file
  3. ## distributed with this work for additional information
  4. ## regarding copyright ownership. Cloudera, Inc. licenses this file
  5. ## to you under the Apache License, Version 2.0 (the
  6. ## "License"); you may not use this file except in compliance
  7. ## with the License. You may obtain a copy of the License at
  8. ##
  9. ## http://www.apache.org/licenses/LICENSE-2.0
  10. ##
  11. ## Unless required by applicable law or agreed to in writing, software
  12. ## distributed under the License is distributed on an "AS IS" BASIS,
  13. ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. ## See the License for the specific language governing permissions and
  15. ## limitations under the License.
  16. <%
  17. from django.utils.translation import ugettext as _
  18. from desktop import conf
  19. from desktop.views import commonheader, commonfooter, _ko
  20. %>
  21. % if not is_embeddable:
  22. ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
  23. <%namespace name="assist" file="/assist.mako" />
  24. % endif
  25. <span class="notebook">
  26. <link rel="stylesheet" href="${ static('desktop/ext/css/basictable.css') }">
  27. % if not is_embeddable:
  28. <link rel="stylesheet" href="${ static('notebook/css/notebook.css') }">
  29. <link rel="stylesheet" href="${ static('notebook/css/notebook-layout.css') }">
  30. <style type="text/css">
  31. % if conf.CUSTOM.BANNER_TOP_HTML.get():
  32. .show-assist {
  33. top: 110px!important;
  34. }
  35. .main-content {
  36. top: 112px!important;
  37. }
  38. % endif
  39. </style>
  40. % endif
  41. <link rel="stylesheet" href="${ static('jobbrowser/css/jobbrowser-embeddable.css') }">
  42. <script src="${ static('oozie/js/dashboard-utils.js') }" type="text/javascript" charset="utf-8"></script>
  43. <script src="${ static('desktop/ext/js/jquery/plugins/jquery.basictable.min.js') }"></script>
  44. <script src="${ static('desktop/ext/js/jquery/plugins/jquery-ui-1.10.4.custom.min.js') }"></script>
  45. <script src="${ static('desktop/js/ko.charts.js') }"></script>
  46. <script src="${ static('desktop/ext/js/knockout-sortable.min.js') }"></script>
  47. <script src="${ static('desktop/js/ko.editable.js') }"></script>
  48. % if not is_mini:
  49. <div id="jobbrowserComponents" class="jobbrowser-components">
  50. % else:
  51. <div id="jobbrowserMiniComponents" class="jobbrowserComponents">
  52. % endif
  53. % if not is_embeddable:
  54. ${ assist.assistJSModels() }
  55. ${ assist.assistPanel() }
  56. <a title="${_('Toggle Assist')}" class="pointer show-assist" data-bind="visible: !$root.isLeftPanelVisible() && $root.assistAvailable(), click: function() { $root.isLeftPanelVisible(true); }">
  57. <i class="fa fa-chevron-right"></i>
  58. </a>
  59. % endif
  60. % if not is_mini:
  61. <div class="navbar hue-title-bar">
  62. <div class="navbar-inner">
  63. <div class="container-fluid">
  64. <div class="nav-collapse">
  65. <ul class="nav">
  66. <li class="app-header">
  67. <a href="/${app_name}">
  68. <img src="${ static('jobbrowser/art/icon_jobbrowser_48.png') }" class="app-icon" alt="${ _('Job browser icon') }"/>
  69. ${ _('Job Browser') }
  70. </a>
  71. </li>
  72. <!-- ko foreach: availableInterfaces -->
  73. <li data-bind="css: {'active': $parent.interface() === interface}, visible: condition()">
  74. <a class="pointer" data-bind="click: function(){ $parent.selectInterface(interface); }, text: label"></a>
  75. </li>
  76. <!-- /ko -->
  77. </ul>
  78. % if not hiveserver2_impersonation_enabled:
  79. <div class="pull-right label label-warning" style="margin-top: 16px">${ _("Hive jobs are running as the 'hive' user") }</div>
  80. % endif
  81. </div>
  82. </div>
  83. </div>
  84. </div>
  85. % endif
  86. <div class="main-content">
  87. <div class="vertical-full container-fluid" data-bind="style: { 'padding-left' : $root.isLeftPanelVisible() ? '0' : '20px' }">
  88. <div class="vertical-full">
  89. <div class="vertical-full row-fluid panel-container">
  90. % if not is_embeddable:
  91. <div class="assist-container left-panel" data-bind="visible: $root.isLeftPanelVisible() && $root.assistAvailable()">
  92. <a title="${_('Toggle Assist')}" class="pointer hide-assist" data-bind="click: function() { $root.isLeftPanelVisible(false) }">
  93. <i class="fa fa-chevron-left"></i>
  94. </a>
  95. <div class="assist" data-bind="component: {
  96. name: 'assist-panel',
  97. params: {
  98. user: '${user.username}',
  99. sql: {
  100. navigationSettings: {
  101. openItem: false,
  102. showStats: true
  103. }
  104. },
  105. visibleAssistPanels: ['sql']
  106. }
  107. }"></div>
  108. </div>
  109. <div class="resizer" data-bind="visible: $root.isLeftPanelVisible() && $root.assistAvailable(), splitDraggable : { appName: 'notebook', leftPanelVisible: $root.isLeftPanelVisible }"><div class="resize-bar">&nbsp;</div></div>
  110. % endif
  111. <div class="content-panel">
  112. <div class="content-panel-inner">
  113. <!-- ko if: $root.job() -->
  114. <div data-bind="template: { name: 'breadcrumbs' }"></div>
  115. <!-- /ko -->
  116. <!-- ko if: interface() !== 'slas' && interface() !== 'oozie-info' -->
  117. <!-- ko if: !$root.job() -->
  118. <form class="form-inline">
  119. <input type="text" class="input-large" data-bind="textInput: jobs.textFilter" placeholder="${_('Filter by id, name, user...')}" />
  120. <!-- ko if: jobs.statesValuesFilter -->
  121. <span data-bind="foreach: jobs.statesValuesFilter">
  122. <label class="checkbox">
  123. <div class="pull-left margin-left-5 status-border status-content" data-bind="css: value, hueCheckbox: checked"></div>
  124. <div class="inline-block" data-bind="text: name, toggle: checked"></div>
  125. </label>
  126. </span>
  127. <!-- /ko -->
  128. <!-- ko ifnot: $root.isMini -->
  129. ${_('in the last')} <input class="input-mini no-margin" type="number" min="1" max="3650" data-bind="value: jobs.timeValueFilter">
  130. <select class="input-small no-margin" data-bind="value: jobs.timeUnitFilter, options: jobs.timeUnitFilterUnits, optionsText: 'name', optionsValue: 'value'">
  131. <option value="days">${_('days')}</option>
  132. <option value="hours">${_('hours')}</option>
  133. <option value="minutes">${_('minutes')}</option>
  134. </select>
  135. <a class="btn" title="${ _('Refresh') }" data-bind="click: jobs.updateJobs">
  136. <i class="fa fa-refresh"></i>
  137. </a>
  138. <div data-bind="template: { name: 'job-actions', 'data': jobs }" class="pull-right"></div>
  139. <!-- /ko -->
  140. </form>
  141. <div class="card card-small">
  142. <!-- ko hueSpinner: { spin: jobs.loadingJobs(), center: true, size: 'xlarge' } --><!-- /ko -->
  143. <!-- ko ifnot: jobs.loadingJobs() -->
  144. <!-- ko if: $root.isMini -->
  145. <ul class="unstyled status-border-container" id="jobsTable" data-bind="foreach: jobs.apps">
  146. <li class="status-border pointer" data-bind="css: {'completed': apiStatus() == 'SUCCEEDED', 'running': isRunning(), 'failed': apiStatus() == 'FAILED'}, click: fetchJob">
  147. <span class="muted pull-left" data-bind="momentFromNow: {data: submitted, interval: 10000, titleFormat: 'LLL'}"></span><span class="muted">&nbsp;-&nbsp;</span><span class="muted" data-bind="text: status"></span></td>
  148. <span class="muted pull-right" data-bind="text: duration().toHHMMSS()"></span>
  149. <div class="clearfix"></div>
  150. <strong class="pull-left" data-bind="text: type"></strong>
  151. <div class="inline-block pull-right"><i class="fa fa-user muted"></i> <span data-bind="text: user"></span></div>
  152. <div class="clearfix"></div>
  153. <div class="pull-left" data-bind="ellipsis: {data: name(), length: 40 }"></div>
  154. <div class="pull-right muted" data-bind="text: id"></div>
  155. <div class="clearfix"></div>
  156. </li>
  157. <div class="status-bar status-background" data-bind="css: {'running': isRunning()}, style: {'width': progress() + '%'}"></div>
  158. </ul>
  159. <!-- /ko -->
  160. <!-- ko ifnot: $root.isMini -->
  161. <table id="jobsTable" class="datatables table table-condensed status-border-container">
  162. <thead>
  163. <tr>
  164. <th width="1%"><div class="select-all hueCheckbox fa" data-bind="hueCheckAll: { allValues: jobs.apps, selectedValues: jobs.selectedJobs }"></div></th>
  165. <th width="15%">${_('Id')}</th>
  166. <th width="20%">${_('Name')}</th>
  167. <th width="3%">${_('Duration')}</th>
  168. <th width="10%">${_('Started')}</th>
  169. <th width="7%">${_('Type')}</th>
  170. <th width="5%">${_('Status')}</th>
  171. <th width="5%">${_('Progress')}</th>
  172. <th width="10%">${_('User')}</th>
  173. </tr>
  174. </thead>
  175. <tbody data-bind="foreach: jobs.apps">
  176. <tr class="status-border pointer" data-bind="css: {'completed': apiStatus() == 'SUCCEEDED', 'running': isRunning(), 'failed': apiStatus() == 'FAILED'}, click: fetchJob">
  177. <td>
  178. <div class="hueCheckbox fa" data-bind="click: function() {}, clickBubble: false, multiCheck: '#jobsTable', value: $data, hueChecked: $parent.jobs.selectedJobs"></div>
  179. </td>
  180. <td data-bind="text: id"></td>
  181. <td data-bind="text: name"></td>
  182. <td data-bind="text: duration().toHHMMSS()"></td>
  183. <td data-bind="moment: {data: submitted, format: 'LLL'}"></td>
  184. <td data-bind="text: type"></td>
  185. <td data-bind="text: status"></td>
  186. <td data-bind="text: progress"></td>
  187. <td data-bind="text: user"></td>
  188. </tr>
  189. </tbody>
  190. </table>
  191. <!-- /ko -->
  192. <!-- /ko -->
  193. </div>
  194. <!-- /ko -->
  195. <!-- ko if: $root.job() -->
  196. <!-- ko with: $root.job() -->
  197. <!-- ko if: mainType() == 'jobs' -->
  198. <div class="jb-panel" data-bind="template: { name: 'job-page' }"></div>
  199. <!-- /ko -->
  200. <!-- ko if: mainType() == 'workflows' -->
  201. <!-- ko if: type() == 'workflow' -->
  202. <div class="jb-panel" data-bind="template: { name: 'workflow-page' }"></div>
  203. <!-- /ko -->
  204. <!-- ko if: type() == 'workflow-action' -->
  205. <div class="jb-panel" data-bind="template: { name: 'workflow-action-page' }"></div>
  206. <!-- /ko -->
  207. <!-- /ko -->
  208. <!-- ko if: mainType() == 'schedules' -->
  209. <div class="jb-panel" data-bind="template: { name: 'schedule-page' }"></div>
  210. <!-- /ko -->
  211. <!-- ko if: mainType() == 'bundles' -->
  212. <div class="jb-panel" data-bind="template: { name: 'bundle-page' }"></div>
  213. <!-- /ko -->
  214. <!-- ko if: mainType().startsWith('dataeng-job') -->
  215. <div data-bind="template: { name: 'dataeng-job-page' }"></div>
  216. <!-- /ko -->
  217. <!-- /ko -->
  218. <!-- /ko -->
  219. <div data-bind="template: { name: 'pagination', data: $root.jobs }, visible: !$root.job() && !jobs.loadingJobs()"></div>
  220. <!-- /ko -->
  221. % if not is_mini:
  222. <!-- ko if: interface() === 'slas' -->
  223. <!-- ko hueSpinner: { spin: slasLoading(), center: true, size: 'xlarge' } --><!-- /ko -->
  224. <!-- /ko -->
  225. <div id="slas" data-bind="visible: interface() === 'slas'"></div>
  226. <!-- ko if: interface() === 'oozie-info' -->
  227. <!-- ko hueSpinner: { spin: oozieInfoLoading(), center: true, size: 'xlarge' } --><!-- /ko -->
  228. <!-- /ko -->
  229. <div id="oozieInfo" data-bind="visible: interface() === 'oozie-info'"></div>
  230. %endif
  231. </div>
  232. </div>
  233. </div>
  234. </div>
  235. </div>
  236. </div>
  237. <!-- ko if: $root.job() -->
  238. <div id="rerun-modal" class="modal hide" data-bind="html: $root.job().rerunModalContent"></div>
  239. <!-- /ko -->
  240. </div>
  241. <script type="text/html" id="breadcrumbs-icons">
  242. <!-- ko switch: type -->
  243. <!-- ko case: 'workflow' -->
  244. <img src="${ static('oozie/art/icon_oozie_workflow_48.png') }" class="app-icon" alt="${ _('Oozie workflow icon') }"/>
  245. <!-- /ko -->
  246. <!-- ko case: 'workflow-action' -->
  247. <i class="fa fa-fw fa-code-fork"></i>
  248. <!-- /ko -->
  249. <!-- ko case: 'schedule' -->
  250. <img src="${ static('oozie/art/icon_oozie_coordinator_48.png') }" class="app-icon" alt="${ _('Oozie coordinator icon') }"/>
  251. <!-- /ko -->
  252. <!-- ko case: 'bundle' -->
  253. <img src="${ static('oozie/art/icon_oozie_bundle_48.png') }" class="app-icon" alt="${ _('Oozie bundle icon') }"/>
  254. <!-- /ko -->
  255. <!-- /ko -->
  256. </script>
  257. <script type="text/html" id="breadcrumbs">
  258. <h3 class="jb-breadcrumbs">
  259. <ul class="inline hue-breadcrumbs-bar">
  260. <!-- ko foreach: breadcrumbs -->
  261. <li>
  262. <!-- ko if: $index() > 1 -->
  263. <span class="divider">&gt;</span>
  264. <!-- /ko -->
  265. <!-- ko if: $index() != 0 -->
  266. <!-- ko if: $index() != $parent.breadcrumbs().length - 1 -->
  267. <a href="javascript:void(0)" data-bind="click: function() { $parent.breadcrumbs.splice($index()); $root.job().id(id); $root.job().fetchJob(); }">
  268. <span data-bind="template: 'breadcrumbs-icons'"></span>
  269. <span data-bind="text: name"></span></a>
  270. <!-- /ko -->
  271. <!-- ko if: $index() == $parent.breadcrumbs().length - 1 -->
  272. <span data-bind="template: 'breadcrumbs-icons'"></span>
  273. <span data-bind="text: name, attr: { title: id }"></span>
  274. <!-- /ko -->
  275. <!-- /ko -->
  276. </li>
  277. <!-- /ko -->
  278. <!-- ko if: !$root.isMini() -->
  279. <!-- ko if: ['workflows', 'schedules', 'bundles', 'slas'].indexOf(interface()) > -1 -->
  280. <li class="pull-right">
  281. <a href="javascript:void(0)" data-bind="click: function() { $root.selectInterface('oozie-info') }">${ _('Configuration') }</a>
  282. </li>
  283. <!-- /ko -->
  284. <!-- /ko -->
  285. </ul>
  286. </h3>
  287. </script>
  288. <script type="text/html" id="pagination">
  289. <!-- ko ifnot: hasPagination -->
  290. <div class="inline">
  291. <span data-bind="text: paginationResultCounts()"></span>
  292. ${ _('jobs') }
  293. </div>
  294. <!-- /ko -->
  295. <!-- ko if: hasPagination -->
  296. <div class="inline">
  297. <div class="inline">
  298. ${ _('Showing') }
  299. <span data-bind="text: Math.min((paginationPage() - 1) * paginationResultPage() + 1, paginationResultCounts())"></span>
  300. ${ _('to')}
  301. <span data-bind="text: Math.min(paginationPage() * paginationResultPage(), paginationResultCounts())"></span>
  302. ${ _('of') }
  303. <span data-bind="text: paginationResultCounts"></span>
  304. ##${ _('Show')}
  305. ##<span data-bind="text: paginationResultPage"></span>
  306. ##${ _('results by page.') }
  307. </div>
  308. <ul class="inline">
  309. <li class="previous-page" data-bind="visible: showPreviousPage">
  310. <a href="javascript:void(0);" data-bind="click: previousPage" title="${_('Previous page')}"><i class="fa fa-backward"></i></a>
  311. </li>
  312. <li class="next-page" data-bind="visible: showNextPage">
  313. <a href="javascript:void(0);" data-bind="click: nextPage" title="${_('Next page')}"><i class="fa fa-forward"></i></a>
  314. </li>
  315. </ul>
  316. </div>
  317. <!-- /ko -->
  318. </script>
  319. <script type="text/html" id="job-page">
  320. <!-- ko if: type() == 'MAPREDUCE' -->
  321. <div data-bind="template: { name: 'job-mapreduce-page', data: $root.job() }"></div>
  322. <!-- /ko -->
  323. <!-- ko if: type() == 'MAP' || type() == 'REDUCE' -->
  324. <div data-bind="template: { name: 'job-mapreduce-task-page', data: $root.job() }"></div>
  325. <!-- /ko -->
  326. <!-- ko if: type() == 'MAP_ATTEMPT' || type() == 'REDUCE_ATTEMPT' -->
  327. <div data-bind="template: { name: 'job-mapreduce-task-attempt-page', data: $root.job() }"></div>
  328. <!-- /ko -->
  329. <!-- ko if: type() == 'YARN' -->
  330. <div data-bind="template: { name: 'job-yarn-page', data: $root.job() }"></div>
  331. <!-- /ko -->
  332. <!-- ko if: type() == 'IMPALA' -->
  333. <div data-bind="template: { name: 'job-impala-page', data: $root.job() }"></div>
  334. <!-- /ko -->
  335. <!-- ko if: type() == 'SPARK' -->
  336. <div data-bind="template: { name: 'job-spark-page', data: $root.job() }"></div>
  337. <!-- /ko -->
  338. </script>
  339. <script type="text/html" id="job-yarn-page">
  340. <div class="row-fluid">
  341. <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
  342. <div class="sidebar-nav">
  343. <ul class="nav nav-list">
  344. <li class="nav-header">${ _('Id') }</li>
  345. <li><span data-bind="text: id"></span></li>
  346. <li class="nav-header">${ _('Name') }</li>
  347. <li><span data-bind="text: name"></span></li>
  348. <li class="nav-header">${ _('Type') }</li>
  349. <li><span data-bind="text: type"></span></li>
  350. <li class="nav-header">${ _('Status') }</li>
  351. <li><span data-bind="text: status"></span></li>
  352. <li class="nav-header">${ _('User') }</li>
  353. <li><span data-bind="text: user"></span></li>
  354. <li class="nav-header">${ _('Progress') }</li>
  355. <li><span data-bind="text: progress"></span></li>
  356. <li>
  357. <div class="progress-job progress" style="background-color: #FFF; width: 100%" data-bind="css: {'progress-warning': progress() < 100, 'progress-success': progress() === 100}">
  358. <div class="bar" data-bind="style: {'width': progress() + '%'}"></div>
  359. </div>
  360. </li>
  361. <li class="nav-header">${ _('Duration') }</li>
  362. <li><span data-bind="text: duration"></span></li>
  363. <li class="nav-header">${ _('Submitted') }</li>
  364. <li><span data-bind="text: submitted"></span></li>
  365. </ul>
  366. </div>
  367. </div>
  368. <div data-bind="css:{'span10': !$root.isMini(), 'span12': $root.isMini() }">
  369. <div class="pull-right" data-bind="template: { name: 'job-actions' }"></div>
  370. </div>
  371. </div>
  372. </script>
  373. <script type="text/html" id="job-mapreduce-page">
  374. <div class="row-fluid">
  375. <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
  376. <div class="sidebar-nav">
  377. <ul class="nav nav-list">
  378. <li class="nav-header">${ _('Id') }</li>
  379. <li><span data-bind="text: id"></span></li>
  380. <li data-bind="visible: id() != name()" class="nav-header">${ _('Name') }</li>
  381. <li data-bind="visible: id() != name()"><span data-bind="text: name"></span></li>
  382. <li class="nav-header">${ _('Type') }</li>
  383. <li><span data-bind="text: type"></span></li>
  384. <li class="nav-header">${ _('Status') }</li>
  385. <li><span data-bind="text: status"></span></li>
  386. <li class="nav-header">${ _('User') }</li>
  387. <li><span data-bind="text: user"></span></li>
  388. <li class="nav-header">${ _('Progress') }</li>
  389. <li><span data-bind="text: progress"></span></li>
  390. <li>
  391. <div class="progress-job progress" style="background-color: #FFF; width: 100%" data-bind="css: {'progress-warning': progress() < 100, 'progress-success': progress() === 100}">
  392. <div class="bar" data-bind="style: {'width': progress() + '%'}"></div>
  393. </div>
  394. </li>
  395. <!-- ko with: properties -->
  396. <li class="nav-header">${ _('Map') }</li>
  397. <li><span data-bind="text: maps_percent_complete"></span> <span data-bind="text: finishedMaps"></span> /<span data-bind="text: desiredMaps"></span></li>
  398. <li class="nav-header">${ _('Reduce') }</li>
  399. <li><span data-bind="text: reduces_percent_complete"></span> <span data-bind="text: finishedReduces"></span> / <span data-bind="text: desiredReduces"></span></li>
  400. <li class="nav-header">${ _('Duration') }</li>
  401. <li><span data-bind="text: $parent.duration"></span></li>
  402. <!-- /ko -->
  403. <li class="nav-header">${ _('Duration') }</li>
  404. <li><span data-bind="text: duration"></span></li>
  405. <li class="nav-header">${ _('Submitted') }</li>
  406. <li><span data-bind="text: submitted"></span></li>
  407. </ul>
  408. </div>
  409. </div>
  410. <div data-bind="css:{'span10': !$root.isMini(), 'span12': $root.isMini() }">
  411. <ul class="nav nav-pills margin-top-20">
  412. <li class="active"><a class="jb-logs-link" href="#job-mapreduce-page-logs" data-toggle="tab">${ _('Logs') }</a></li>
  413. <li><a href="#job-mapreduce-page-tasks" data-bind="click: function(){ fetchProfile('tasks'); $('a[href=\'#job-mapreduce-page-tasks\']').tab('show'); }">${ _('Tasks') }</a></li>
  414. <li><a href="#job-mapreduce-page-metadata" data-bind="click: function(){ fetchProfile('metadata'); $('a[href=\'#job-mapreduce-page-metadata\']').tab('show'); }">${ _('Metadata') }</a></li>
  415. <li><a href="#job-mapreduce-page-counters" data-bind="click: function(){ fetchProfile('counters'); $('a[href=\'#job-mapreduce-page-counters\']').tab('show'); }">${ _('Counters') }</a></li>
  416. <li class="pull-right" data-bind="template: { name: 'job-actions' }"></li>
  417. </ul>
  418. <div class="clearfix"></div>
  419. <div class="tab-content">
  420. <div class="tab-pane active" id="job-mapreduce-page-logs">
  421. % for name in ['stdout', 'stderr', 'syslog']:
  422. <a href="javascript:void(0)" data-bind="click: function() { fetchLogs('${ name }'); }, text: '${ name }'"></a>
  423. % endfor
  424. <br>
  425. <pre data-bind="html: logs, logScroller: logs"></pre>
  426. </div>
  427. <div class="tab-pane" id="job-mapreduce-page-tasks">
  428. <form class="form-inline">
  429. <input data-bind="textFilter: textFilter, clearable: {value: textFilter}" type="text" class="input-xlarge search-query" placeholder="${_('Filter by name')}">
  430. <span data-bind="foreach: statesValuesFilter">
  431. <label class="checkbox">
  432. <div class="pull-left margin-left-5 status-border status-content" data-bind="css: value, hueCheckbox: checked"></div>
  433. <div class="inline-block" data-bind="text: name, toggle: checked"></div>
  434. </label>
  435. </span>
  436. <span data-bind="foreach: typesValuesFilter" class="margin-left-30">
  437. <label class="checkbox">
  438. <div class="pull-left margin-left-5" data-bind="css: value, hueCheckbox: checked"></div>
  439. <div class="inline-block" data-bind="text: name, toggle: checked"></div>
  440. </label>
  441. </span>
  442. </form>
  443. <table class="table table-condensed">
  444. <thead>
  445. <tr>
  446. <th>${_('Type')}</th>
  447. <th>${_('Id')}</th>
  448. <th>${_('elapsedTime')}</th>
  449. <th>${_('progress')}</th>
  450. <th>${_('state')}</th>
  451. <th>${_('startTime')}</th>
  452. <th>${_('successfulAttempt')}</th>
  453. <th>${_('finishTime')}</th>
  454. </tr>
  455. </thead>
  456. <tbody data-bind="foreach: properties['tasks']()['task_list']">
  457. <tr data-bind="click: function() { $root.job().id(id); $root.job().fetchJob(); }" class="pointer">
  458. <td data-bind="text: type"></td>
  459. <td data-bind="text: id"></td>
  460. <td data-bind="text: elapsedTime"></td>
  461. <td data-bind="text: progress"></td>
  462. <td data-bind="text: state"></td>
  463. <td data-bind="text: startTime"></td>
  464. <td data-bind="text: successfulAttempt"></td>
  465. <td data-bind="text: finishTime"></td>
  466. </tr>
  467. </tbody>
  468. </table>
  469. </div>
  470. <div class="tab-pane" id="job-mapreduce-page-metadata">
  471. <pre data-bind="text: ko.toJSON(properties['metadata'], null, 2)"></pre>
  472. </div>
  473. <div class="tab-pane" id="job-mapreduce-page-counters">
  474. <pre data-bind="text: ko.toJSON(properties['counters'], null, 2)"></pre>
  475. </div>
  476. </div>
  477. </div>
  478. </div>
  479. </script>
  480. <script type="text/html" id="job-mapreduce-task-page">
  481. <div class="row-fluid">
  482. <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
  483. <div class="sidebar-nav">
  484. <ul class="nav nav-list">
  485. <li class="nav-header">${ _('Id') }</li>
  486. <li><span data-bind="text: id, attr: { 'title': id }"></span></li>
  487. <li class="nav-header">${ _('Type') }</li>
  488. <li><span data-bind="text: type"></span></li>
  489. <li class="nav-header">${ _('Progress') }</li>
  490. <li><span data-bind="text: progress"></span></li>
  491. <li>
  492. <div class="progress-job progress" style="background-color: #FFF; width: 100%" data-bind="css: {'progress-warning': progress() < 100, 'progress-success': progress() === 100}">
  493. <div class="bar" data-bind="style: {'width': progress() + '%'}"></div>
  494. </div>
  495. </li>
  496. <!-- ko with: properties -->
  497. <li class="nav-header">${ _('State') }</li>
  498. <li><span data-bind="text: state"></span></li>
  499. <li class="nav-header">${ _('Start time') }</li>
  500. <li><span data-bind="text: startTime"></span></li>
  501. <li class="nav-header">${ _('Successful attempt') }</li>
  502. <li><span data-bind="text: successfulAttempt"></span></li>
  503. <li class="nav-header">${ _('Finish time') }</li>
  504. <li><span data-bind="text: finishTime"></span></li>
  505. <li class="nav-header">${ _('Elapsed time') }</li>
  506. <li><span data-bind="text: elapsedTime"></span></li>
  507. <!-- /ko -->
  508. </ul>
  509. </div>
  510. </div>
  511. <div data-bind="css:{'span10': !$root.isMini(), 'span12': $root.isMini() }">
  512. <ul class="nav nav-pills margin-top-20">
  513. <li class="active"><a class="jb-logs-link" href="#job-mapreduce-task-page-logs" data-toggle="tab">${ _('Logs') }</a></li>
  514. <li><a href="#job-mapreduce-task-page-attempts" data-bind="click: function(){ fetchProfile('attempts'); $('a[href=\'#job-mapreduce-task-page-attempts\']').tab('show'); }">${ _('Attempts') }</a></li>
  515. <li><a href="#job-mapreduce-task-page-counters" data-bind="click: function(){ fetchProfile('counters'); $('a[href=\'#job-mapreduce-task-page-counters\']').tab('show'); }">${ _('Counters') }</a></li>
  516. </ul>
  517. <div class="tab-content">
  518. <div class="tab-pane active" id="job-mapreduce-task-page-logs">
  519. % for name in ['stdout', 'stderr', 'syslog']:
  520. <a href="javascript:void(0)" data-bind="click: function() { fetchLogs('${ name }'); }, text: '${ name }'"></a>
  521. % endfor
  522. <br>
  523. <pre data-bind="html: logs, logScroller: logs"></pre>
  524. </div>
  525. <div class="tab-pane" id="job-mapreduce-task-page-attempts">
  526. <table class="table table-condensed">
  527. <thead>
  528. <tr>
  529. <th width="1%"><div class="select-all hueCheckbox fa"></div></th>
  530. <th>${_('assignedContainerId')}</th>
  531. <th>${_('progress')}</th>
  532. <th>${_('elapsedTime')}</th>
  533. <th>${_('state')}</th>
  534. <th>${_('rack')}</th>
  535. <th>${_('nodeHttpAddress')}</th>
  536. <th>${_('type')}</th>
  537. <th>${_('startTime')}</th>
  538. <th>${_('id')}</th>
  539. <th>${_('finishTime')}</th>
  540. </tr>
  541. </thead>
  542. <tbody data-bind="foreach: properties['attempts']()['task_list']">
  543. <tr data-bind="click: function() { $root.job().id(id); $root.job().fetchJob(); }">
  544. <td><div class="hueCheckbox fa"></div></td>
  545. <td data-bind="text: assignedContainerId"></td>
  546. <td data-bind="text: progress"></td>
  547. <td data-bind="text: elapsedTime"></td>
  548. <td data-bind="text: state"></td>
  549. <td data-bind="text: rack"></td>
  550. <td data-bind="text: nodeHttpAddress"></td>
  551. <td data-bind="text: type"></td>
  552. <td data-bind="text: startTime"></td>
  553. <td data-bind="text: id"></td>
  554. <td data-bind="text: finishTime"></td>
  555. </tr>
  556. </tbody>
  557. </table>
  558. </div>
  559. <div class="tab-pane" id="job-mapreduce-task-page-counters">
  560. <pre data-bind="text: ko.toJSON(properties['counters'], null, 2)"></pre>
  561. </div>
  562. </div>
  563. </div>
  564. </div>
  565. </script>
  566. <script type="text/html" id="job-mapreduce-task-attempt-page">
  567. <div class="row-fluid">
  568. <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
  569. <div class="sidebar-nav">
  570. <ul class="nav nav-list">
  571. <li class="nav-header">${ _('Id') }</li>
  572. <li><span data-bind="text: id, attr: { 'title': id }"></span></li>
  573. <li class="nav-header">${ _('Type') }</li>
  574. <li><span data-bind="text: type"></span></li>
  575. <li class="nav-header">${ _('Progress') }</li>
  576. <li><span data-bind="text: progress"></span></li>
  577. <li>
  578. <div class="progress-job progress" style="background-color: #FFF; width: 100%" data-bind="css: {'progress-warning': progress() < 100, 'progress-success': progress() === 100}">
  579. <div class="bar" data-bind="style: {'width': progress() + '%'}"></div>
  580. </div>
  581. </li>
  582. <!-- ko with: properties -->
  583. <li class="nav-header">${ _('State') }</li>
  584. <li><span data-bind="text: state"></span></li>
  585. <li class="nav-header">${ _('Assigned Container ID') }</li>
  586. <li><span data-bind="text: assignedContainerId"></span></li>
  587. <li class="nav-header">${ _('Rack') }</li>
  588. <li><span data-bind="text: rack"></span></li>
  589. <li class="nav-header">${ _('Node HTTP address') }</li>
  590. <li><span data-bind="text: nodeHttpAddress"></span></li>
  591. <li class="nav-header">${ _('Start time') }</li>
  592. <li><span data-bind="text: startTime"></span></li>
  593. <li class="nav-header">${ _('Finish time') }</li>
  594. <li><span data-bind="text: finishTime"></span></li>
  595. <li class="nav-header">${ _('Elapsed time') }</li>
  596. <li><span data-bind="text: elapsedTime"></span></li>
  597. <!-- /ko -->
  598. </ul>
  599. </div>
  600. </div>
  601. <div data-bind="css:{'span10': !$root.isMini(), 'span12': $root.isMini() }">
  602. <ul class="nav nav-pills margin-top-20">
  603. <li class="active"><a class="jb-logs-link" href="#job-mapreduce-task-attempt-page-logs" data-toggle="tab">${ _('Logs') }</a></li>
  604. <li><a href="#job-mapreduce-task-attempt-page-counters" data-bind="click: function(){ fetchProfile('counters'); $('a[href=\'#job-mapreduce-task-attempt-page-counters\']').tab('show'); }">${ _('Counters') }</a></li>
  605. </ul>
  606. <div class="tab-content">
  607. <div class="tab-pane active" id="job-mapreduce-task-attempt-page-logs">
  608. % for name in ['stdout', 'stderr', 'syslog']:
  609. <a href="javascript:void(0)" data-bind="click: function() { fetchLogs('${ name }'); }, text: '${ name }'"></a>
  610. % endfor
  611. <br>
  612. <pre data-bind="html: logs, logScroller: logs"></pre>
  613. </div>
  614. <div class="tab-pane" id="job-mapreduce-task-attempt-page-counters">
  615. <pre data-bind="text: ko.toJSON(properties['counters'], null, 2)"></pre>
  616. </div>
  617. </div>
  618. </div>
  619. </div>
  620. </script>
  621. <script type="text/html" id="job-impala-page">
  622. <div class="row-fluid">
  623. <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
  624. <div class="sidebar-nav">
  625. <ul class="nav nav-list">
  626. <li class="nav-header">${ _('Id') }</li>
  627. <li><span data-bind="text: id, attr: { 'title': id }"></span></li>
  628. <li class="nav-header">${ _('Name') }</li>
  629. <li><span data-bind="text: name"></span></li>
  630. <li class="nav-header">${ _('Type') }</li>
  631. <li><span data-bind="text: type"></span></li>
  632. <li class="nav-header">${ _('Status') }</li>
  633. <li><span data-bind="text: status"></span></li>
  634. <li class="nav-header">${ _('User') }</li>
  635. <li><span data-bind="text: user"></span></li>
  636. <li class="nav-header">${ _('Progress') }</li>
  637. <li><span data-bind="text: progress"></span></li>
  638. <li>
  639. <div class="progress-job progress" style="background-color: #FFF; width: 100%" data-bind="css: {'progress-warning': progress() < 100, 'progress-success': progress() === 100}">
  640. <div class="bar" data-bind="style: {'width': progress() + '%'}"></div>
  641. </div>
  642. </li>
  643. <li class="nav-header">${ _('Duration') }</li>
  644. <li><span data-bind="text: duration"></span></li>
  645. <li class="nav-header">${ _('Submitted') }</li>
  646. <li><span data-bind="text: submitted"></span></li>
  647. </ul>
  648. </div>
  649. </div>
  650. <div data-bind="css:{'span10': !$root.isMini(), 'span12': $root.isMini() }">
  651. <div class="pull-right" data-bind="template: { name: 'job-actions' }"></div>
  652. </div>
  653. </div>
  654. </script>
  655. <script type="text/html" id="job-spark-page">
  656. <div class="row-fluid">
  657. <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
  658. <div class="sidebar-nav">
  659. <ul class="nav nav-list">
  660. <li class="nav-header">${ _('Id') }</li>
  661. <li><span data-bind="text: id, attr: { 'title': id }"></span></li>
  662. <li class="nav-header">${ _('Name') }</li>
  663. <li><span data-bind="text: name"></span></li>
  664. <li class="nav-header">${ _('Type') }</li>
  665. <li><span data-bind="text: type"></span></li>
  666. <li class="nav-header">${ _('Status') }</li>
  667. <li><span data-bind="text: status"></span></li>
  668. <li class="nav-header">${ _('User') }</li>
  669. <li><span data-bind="text: user"></span></li>
  670. <li class="nav-header">${ _('Progress') }</li>
  671. <li><span data-bind="text: progress"></span></li>
  672. <li>
  673. <div class="progress-job progress" style="background-color: #FFF; width: 100%" data-bind="css: {'progress-warning': progress() < 100, 'progress-success': progress() === 100}">
  674. <div class="bar" data-bind="style: {'width': progress() + '%'}"></div>
  675. </div>
  676. </li>
  677. <li class="nav-header">${ _('Duration') }</li>
  678. <li><span data-bind="text: duration"></span></li>
  679. <li class="nav-header">${ _('Submitted') }</li>
  680. <li><span data-bind="text: submitted"></span></li>
  681. </ul>
  682. </div>
  683. </div>
  684. <div data-bind="css:{'span10': !$root.isMini(), 'span12': $root.isMini() }">
  685. <div class="pull-right" data-bind="template: { name: 'job-actions' }"></div>
  686. </div>
  687. </div>
  688. </script>
  689. <script type="text/html" id="dataeng-job-page">
  690. <!-- ko if: type() == 'dataeng-job-HIVE' -->
  691. <div data-bind="template: { name: 'dataeng-job-hive-page', data: $root.job() }"></div>
  692. <!-- /ko -->
  693. </script>
  694. <script type="text/html" id="dataeng-job-hive-page">
  695. ${ _('Id') } <span data-bind="text: id"></span>
  696. ${ _('Name') } <span data-bind="text: name"></span>
  697. ${ _('Type') } <span data-bind="text: type"></span>
  698. ${ _('Status') } <span data-bind="text: status"></span>
  699. ${ _('User') } <span data-bind="text: user"></span>
  700. ${ _('Progress') } <span data-bind="text: progress"></span>
  701. ${ _('Duration') } <span data-bind="text: duration"></span>
  702. ${ _('Submitted') } <span data-bind="text: submitted"></span>
  703. <div data-bind="template: { name: 'render-properties', data: properties['properties'] }"></div>
  704. </script>
  705. <script type="text/html" id="job-actions">
  706. <div class="btn-group">
  707. <!-- ko if: hasResume -->
  708. <button class="btn" title="${ _('Resume selected') }" data-bind="click: function() { control('resume'); }, enable: resumeEnabled">
  709. <i class="fa fa-play"></i> <!-- ko ifnot: $root.isMini -->${ _('Resume') }<!-- /ko -->
  710. </button>
  711. <!-- /ko -->
  712. <!-- ko if: hasPause -->
  713. <button class="btn" title="${ _('Suspend selected') }" data-bind="click: function() { control('suspend'); }, enable: pauseEnabled">
  714. <i class="fa fa-pause"></i> <!-- ko ifnot: $root.isMini -->${ _('Suspend') }<!-- /ko -->
  715. </button>
  716. <!-- /ko -->
  717. <!-- ko if: hasRerun -->
  718. <button class="btn" title="${ _('Rerun selected') }" data-bind="click: function() { control('rerun'); }, enable: rerunEnabled">
  719. <i class="fa fa-repeat"></i> <!-- ko ifnot: $root.isMini -->${ _('Rerun') }<!-- /ko -->
  720. </button>
  721. <!-- /ko -->
  722. <!-- ko if: hasKill -->
  723. <button class="btn btn-danger" title="${_('Stop selected')}" data-bind="click: function() { control('kill'); }, enable: killEnabled">
  724. ## TODO confirmation
  725. <i class="fa fa-times"></i> <!-- ko ifnot: $root.isMini -->${_('Kill')}<!-- /ko -->
  726. </button>
  727. <!-- /ko -->
  728. <!-- ko if: hasIgnore -->
  729. <button class="btn btn-danger" title="${_('Ignore selected')}" data-bind="click: function() { control('ignore'); }, enable: ignoreEnabled">
  730. ## TODO confirmation
  731. <i class="fa fa-eraser"></i> <!-- ko ifnot: $root.isMini -->${_('Ignore')}<!-- /ko -->
  732. </button>
  733. <!-- /ko -->
  734. </div>
  735. </script>
  736. <script type="text/html" id="workflow-page">
  737. <div class="row-fluid">
  738. <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
  739. <div class="sidebar-nav">
  740. <ul class="nav nav-list">
  741. <li class="nav-header">${ _('Id') }</li>
  742. <li><span data-bind="text: id, attr: { 'title': id }"></span></li>
  743. <!-- ko if: doc_url -->
  744. <li class="nav-header">${ _('Document') }</li>
  745. <li>
  746. <a data-bind="hueLink: doc_url" href="javascript: void(0);" title="${ _('Open in editor') }">
  747. <span data-bind="text: name"></span>
  748. </a>
  749. </li>
  750. <!-- /ko -->
  751. <!-- ko ifnot: doc_url -->
  752. <li class="nav-header">${ _('Name') }</li>
  753. <li><span data-bind="text: name"></span></li>
  754. <!-- /ko -->
  755. <li class="nav-header">${ _('Status') }</li>
  756. <li><span data-bind="text: status"></span></li>
  757. <li class="nav-header">${ _('User') }</li>
  758. <li><span data-bind="text: user"></span></li>
  759. <li class="nav-header">${ _('Progress') }</li>
  760. <li><span data-bind="text: progress"></span></li>
  761. <li>
  762. <div class="progress-job progress" style="background-color: #FFF; width: 100%" data-bind="css: {'progress-warning': isRunning(), 'progress-success': apiStatus() === 'SUCCEEDED', 'progress-danger': apiStatus() === 'FAILED'}">
  763. <div class="bar" data-bind="style: {'width': progress() + '%'}"></div>
  764. </div>
  765. </li>
  766. <li class="nav-header">${ _('Duration') }</li>
  767. <li><span data-bind="text: duration"></span></li>
  768. <li class="nav-header">${ _('Submitted') }</li>
  769. <li><span data-bind="text: submitted"></span></li>
  770. <li class="nav-header">${ _('Variables') }</li>
  771. <li>
  772. <ul class="nav nav-list" data-bind="foreach: properties['parameters']">
  773. <li>
  774. <!-- ko if: link -->
  775. <a data-bind="hueLink: link" href="javascript: void(0);">
  776. <span data-bind="text: name, attr: { title: value }"></span>
  777. </a>
  778. <!-- /ko -->
  779. <!-- ko ifnot: link -->
  780. <span data-bind="text: name, attr: { title: value }"></span>
  781. <!-- /ko -->
  782. </li>
  783. </ul>
  784. </li>
  785. </ul>
  786. </div>
  787. </div>
  788. <div data-bind="css:{'span10': !$root.isMini(), 'span12': $root.isMini() }">
  789. <ul class="nav nav-pills margin-top-20">
  790. <li class="active"><a href="#workflow-page-graph" data-toggle="tab">${ _('Graph') }</a></li>
  791. <li><a href="#workflow-page-metadata" data-bind="click: function(){ fetchProfile('properties'); $('a[href=\'#workflow-page-metadata\']').tab('show'); }">${ _('Properties') }</a></li>
  792. <li><a class="jb-logs-link" href="#workflow-page-logs" data-toggle="tab">${ _('Logs') }</a></li>
  793. <li><a href="#workflow-page-tasks" data-toggle="tab">${ _('Tasks') }</a></li>
  794. <li><a href="#workflow-page-xml" data-bind="click: function(){ fetchProfile('xml'); $('a[href=\'#workflow-page-xml\']').tab('show'); }">${ _('XML') }</a></li>
  795. <li class="pull-right" data-bind="template: { name: 'job-actions' }"></li>
  796. </ul>
  797. <div class="clearfix"></div>
  798. <div class="tab-content">
  799. <div class="tab-pane active dashboard-container" id="workflow-page-graph">
  800. </div>
  801. <div class="tab-pane" id="workflow-page-logs">
  802. <pre data-bind="html: logs, logScroller: logs"></pre>
  803. </div>
  804. <div class="tab-pane" id="workflow-page-tasks">
  805. <table id="actionsTable" class="datatables table table-condensed">
  806. <thead>
  807. <tr>
  808. <th>${_('Log')}</th>
  809. <th>${_('Status')}</th>
  810. <th>${_('Error message')}</th>
  811. <th>${_('Error code')}</th>
  812. <th>${_('External id')}</th>
  813. <th>${_('Id')}</th>
  814. <th>${_('Start time')}</th>
  815. <th>${_('End time')}</th>
  816. </tr>
  817. </thead>
  818. <tbody data-bind="foreach: properties['actions']">
  819. <tr data-bind="click: function() { $root.job().id(id); $root.job().fetchJob(); }" class="pointer">
  820. <td>
  821. <a data-bind="hueLink: '/jobbrowser/jobs/' + externalId(), clickBubble: false">
  822. <i class="fa fa-tasks"></i>
  823. </a>
  824. </td>
  825. <td data-bind="text: status"></td>
  826. <td data-bind="text: errorMessage"></td>
  827. <td data-bind="text: errorCode"></td>
  828. <td data-bind="text: externalId"></td>
  829. <td data-bind="text: id"></td>
  830. <td data-bind="text: startTime"></td>
  831. <td data-bind="text: endTime"></td>
  832. </tr>
  833. </tbody>
  834. </table>
  835. </div>
  836. <div class="tab-pane" id="workflow-page-metadata">
  837. <div data-bind="template: { name: 'render-properties', data: properties['properties'] }"></div>
  838. </div>
  839. <div class="tab-pane" id="workflow-page-xml">
  840. <div data-bind="readonlyXML: properties['xml'], path: 'xml'"></div>
  841. </div>
  842. </div>
  843. </div>
  844. </div>
  845. </script>
  846. <script type="text/html" id="workflow-action-page">
  847. <div class="row-fluid">
  848. <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
  849. <div class="sidebar-nav">
  850. <ul class="nav nav-list">
  851. <li class="nav-header">${ _('Id') }</li>
  852. <li><span data-bind="text: id, attr: { 'title': id }"></span></li>
  853. <li class="nav-header">${ _('Name') }</li>
  854. <li><span data-bind="text: name"></span></li>
  855. <li class="nav-header">${ _('Type') }</li>
  856. <li><span data-bind="text: type"></span></li>
  857. <li class="nav-header">${ _('Job') }</li>
  858. <li>
  859. <a data-bind="hueLink: '/jobbrowser/jobs/' + properties['externalId']()" href="javascript: void(0);">
  860. <span data-bind="text: properties['externalId']"></span>
  861. </a>
  862. </li>
  863. <li class="nav-header">${ _('Status') }</li>
  864. <li><span data-bind="text: status"></span></li>
  865. </ul>
  866. </div>
  867. </div>
  868. <div data-bind="css:{'span10': !$root.isMini(), 'span12': $root.isMini() }">
  869. <ul class="nav nav-pills margin-top-20">
  870. <li class="active"><a href="#workflow-action-page-metadata" data-toggle="tab">${ _('Properties') }</a></li>
  871. <li><a href="#workflow-action-page-tasks" data-toggle="tab">${ _('Child jobs') }</a></li>
  872. <li><a href="#workflow-action-page-xml" data-toggle="tab">${ _('XML') }</a></li>
  873. </ul>
  874. <div class="tab-content">
  875. <div class="tab-pane" id="workflow-action-page-metadata">
  876. <table class="datatables table table-condensed">
  877. <thead>
  878. <tr>
  879. <th>${_('Name')}</th>
  880. <th>${_('Value')}</th>
  881. </tr>
  882. </thead>
  883. <tbody data-bind="foreach: properties['properties']">
  884. <tr>
  885. <td data-bind="text: name"></td>
  886. <td data-bind="text: value"></td>
  887. </tr>
  888. </tbody>
  889. </table>
  890. </div>
  891. <div class="tab-pane" id="workflow-action-page-tasks">
  892. <table class="table table-condensed datatables">
  893. <thead>
  894. <tr>
  895. <th>${ _('Ids') }</th>
  896. </tr>
  897. </thead>
  898. <tbody data-bind="foreach: properties['externalChildIDs']">
  899. <tr>
  900. <td>
  901. <a data-bind="hueLink: '/jobbrowser/jobs/' + $data, text: $data" href="javascript: void(0);">
  902. </a>
  903. </td>
  904. </tr>
  905. </tbody>
  906. </table>
  907. </div>
  908. <div class="tab-pane" id="workflow-action-page-xml">
  909. <pre data-bind="text: ko.toJSON(properties['conf'], null, 2)"></pre>
  910. ## <div data-bind="readonlyXML: properties['conf'](), path: 'xml'"></div>
  911. </div>
  912. </div>
  913. </div>
  914. </div>
  915. </script>
  916. <script type="text/html" id="schedule-page">
  917. <div class="row-fluid">
  918. <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
  919. <div class="sidebar-nav">
  920. <ul class="nav nav-list">
  921. <li class="nav-header">${ _('Id') }</li>
  922. <li><span data-bind="text: id, attr: { 'title': id }"></span></li>
  923. <li class="nav-header">${ _('Name') }</li>
  924. <li><span data-bind="text: name"></span></li>
  925. <li class="nav-header">${ _('Type') }</li>
  926. <li><span data-bind="text: type"></span></li>
  927. <li class="nav-header">${ _('Status') }</li>
  928. <li><span data-bind="text: status"></span></li>
  929. <li class="nav-header">${ _('User') }</li>
  930. <li><span data-bind="text: user"></span></li>
  931. <li class="nav-header">${ _('Progress') }</li>
  932. <li><span data-bind="text: progress"></span></li>
  933. <li>
  934. <div class="progress-job progress" style="background-color: #FFF; width: 100%" data-bind="css: {'progress-warning': progress() < 100, 'progress-success': progress() === 100}">
  935. <div class="bar" data-bind="style: {'width': progress() + '%'}"></div>
  936. </div>
  937. </li>
  938. <li class="nav-header">${ _('Duration') }</li>
  939. <li><span data-bind="text: duration"></span></li>
  940. <li class="nav-header">${ _('Submitted') }</li>
  941. <li><span data-bind="text: submitted"></span></li>
  942. <li class="nav-header">${ _('Next Run') }</li>
  943. <li><span data-bind="text: properties['nextTime']"></span></li>
  944. <li class="nav-header">${ _('Total Actions') }</li>
  945. <li><span data-bind="text: properties['total_actions']"></span></li>
  946. <li class="nav-header">${ _('End time') }</li>
  947. <li><span data-bind="text: properties['endTime']"></span></li>
  948. </ul>
  949. </div>
  950. </div>
  951. <div data-bind="css:{'span10': !$root.isMini(), 'span12': $root.isMini() }">
  952. <ul class="nav nav-pills margin-top-20">
  953. <li class="active"><a href="#schedule-page-task" data-toggle="tab">${ _('Tasks') }</a></li>
  954. <li><a class="jb-logs-link" href="#schedule-page-logs" data-toggle="tab">${ _('Logs') }</a></li>
  955. <li><a href="#schedule-page-metadata" data-bind="click: function(){ fetchProfile('properties'); $('a[href=\'#schedule-page-metadata\']').tab('show'); }">${ _('Properties') }</a></li>
  956. <li><a href="#schedule-page-xml" data-bind="click: function(){ fetchProfile('xml'); $('a[href=\'#schedule-page-xml\']').tab('show'); }">${ _('XML') }</a></li>
  957. <li class="pull-right" data-bind="template: { name: 'job-actions' }"></li>
  958. </ul>
  959. <div class="clearfix"></div>
  960. <div class="tab-content">
  961. <div class="tab-pane active" id="schedule-page-calendar">
  962. <!-- ko with: coordinatorActions() -->
  963. <form class="form-inline">
  964. <input data-bind="value: textFilter" type="text" class="input-xlarge search-query" placeholder="${_('Filter by name')}">
  965. <span data-bind="foreach: statesValuesFilter">
  966. <label class="checkbox">
  967. <div class="pull-left margin-left-5 status-border status-content" data-bind="css: value, hueCheckbox: checked"></div>
  968. <div class="inline-block" data-bind="text: name, toggle: checked"></div>
  969. </label>
  970. </span>
  971. <div data-bind="template: { name: 'job-actions' }" class="pull-right"></div>
  972. </form>
  973. <table id="schedulesTable" class="datatables table table-condensed status-border-container">
  974. <thead>
  975. <tr>
  976. <th width="1%"><div class="select-all hueCheckbox fa"></div></th>
  977. <th>${_('Status')}</th>
  978. <th>${_('Title')}</th>
  979. <th>${_('type')}</th>
  980. <th>${_('errorMessage')}</th>
  981. <th>${_('missingDependencies')}</th>
  982. <th>${_('number')}</th>
  983. <th>${_('errorCode')}</th>
  984. <th>${_('externalId')}</th>
  985. <th>${_('id')}</th>
  986. <th>${_('lastModifiedTime')}</th>
  987. </tr>
  988. </thead>
  989. <tbody data-bind="foreach: apps">
  990. <tr class="status-border pointer" data-bind="css: {'completed': properties.status() == 'SUCCEEDED', 'running': properties.status() !== 'SUCCEEDED' && properties.status() !== 'FAILED', 'failed': properties.status() == 'FAILED'}, click: function() { if (properties.externalId()) { $root.job().id(properties.externalId()); $root.job().fetchJob();} }">
  991. <td>
  992. <div class="hueCheckbox fa" data-bind="click: function() {}, clickBubble: false, multiCheck: '#schedulesTable', value: $data, hueChecked: $parent.selectedJobs"></div>
  993. </td>
  994. <td data-bind="text: properties.status"></td>
  995. <td data-bind="text: properties.title"></td>
  996. <td data-bind="text: properties.type"></td>
  997. <td data-bind="text: properties.errorMessage"></td>
  998. <td data-bind="text: properties.missingDependencies"></td>
  999. <td data-bind="text: properties.number"></td>
  1000. <td data-bind="text: properties.errorCode"></td>
  1001. <td data-bind="text: properties.externalId"></td>
  1002. <td data-bind="text: properties.id"></td>
  1003. <td data-bind="text: properties.lastModifiedTime"></td>
  1004. </tr>
  1005. </tbody>
  1006. </table>
  1007. <!-- /ko -->
  1008. </div>
  1009. <div class="tab-pane" id="schedule-page-logs">
  1010. <pre data-bind="html: logs, logScroller: logs"></pre>
  1011. </div>
  1012. <div class="tab-pane" id="schedule-page-metadata">
  1013. <div data-bind="template: { name: 'render-properties', data: properties['properties'] }"></div>
  1014. </div>
  1015. <div class="tab-pane" id="schedule-page-xml">
  1016. <div data-bind="readonlyXML: properties['xml'], path: 'xml'"></div>
  1017. </div>
  1018. </div>
  1019. </div>
  1020. </div>
  1021. </script>
  1022. <script type="text/html" id="bundle-page">
  1023. <div class="row-fluid">
  1024. <div data-bind="css:{'span2': !$root.isMini(), 'span12': $root.isMini() }">
  1025. <div class="sidebar-nav">
  1026. <ul class="nav nav-list">
  1027. <li class="nav-header">${ _('Id') }</li>
  1028. <li><span data-bind="text: id, attr: { 'title': id }"></span></li>
  1029. <li class="nav-header">${ _('Name') }</li>
  1030. <li><span data-bind="text: name"></span></li>
  1031. <li class="nav-header">${ _('Type') }</li>
  1032. <li><span data-bind="text: type"></span></li>
  1033. <li class="nav-header">${ _('Status') }</li>
  1034. <li><span data-bind="text: status"></span></li>
  1035. <li class="nav-header">${ _('User') }</li>
  1036. <li><span data-bind="text: user"></span></li>
  1037. <li class="nav-header">${ _('Progress') }</li>
  1038. <li><span data-bind="text: progress"></span></li>
  1039. <li>
  1040. <div class="progress-job progress" style="background-color: #FFF; width: 100%" data-bind="css: {'progress-warning': progress() < 100, 'progress-success': progress() === 100}">
  1041. <div class="bar" data-bind="style: {'width': progress() + '%'}"></div>
  1042. </div>
  1043. </li>
  1044. <li class="nav-header">${ _('Duration') }</li>
  1045. <li><span data-bind="text: duration"></span></li>
  1046. <li class="nav-header">${ _('Submitted') }</li>
  1047. <li><span data-bind="text: submitted"></span></li>
  1048. <li class="nav-header">${ _('Next Run') }</li>
  1049. <li><span data-bind="text: properties['nextTime']"></span></li>
  1050. <li class="nav-header">${ _('Total Actions') }</li>
  1051. <li><span data-bind="text: properties['total_actions']"></span></li>
  1052. <li class="nav-header">${ _('End time') }</li>
  1053. <li><span data-bind="text: properties['endTime']"></span></li>
  1054. </ul>
  1055. </div>
  1056. </div>
  1057. <div data-bind="css:{'span10': !$root.isMini(), 'span12': $root.isMini() }">
  1058. <ul class="nav nav-pills margin-top-20">
  1059. <li class="active"><a href="#bundle-page-coordinators" data-toggle="tab">${ _('Tasks') }</a></li>
  1060. <li><a class="jb-logs-link" href="#bundle-page-logs" data-toggle="tab">${ _('Logs') }</a></li>
  1061. <li><a href="#bundle-page-metadata" data-bind="click: function(){ fetchProfile('properties'); $('a[href=\'#bundle-page-metadata\']').tab('show'); }">${ _('Properties') }</a></li>
  1062. <li><a href="#bundle-page-xml" data-bind="click: function(){ fetchProfile('xml'); $('a[href=\'#bundle-page-xml\']').tab('show'); }">${ _('XML') }</a></li>
  1063. <li class="pull-right" data-bind="template: { name: 'job-actions' }"></li>
  1064. </ul>
  1065. <div class="clearfix"></div>
  1066. <div class="tab-content">
  1067. <div class="tab-pane active" id="bundle-page-coordinators">
  1068. <input type="text" class="input-xlarge search-query" placeholder="${_('Filter by id, name, user...')}" />
  1069. <div class="btn-group">
  1070. <select size="3" multiple="true"></select>
  1071. <a class="btn btn-status btn-success" data-value="completed">${ _('Succeeded') }</a>
  1072. <a class="btn btn-status btn-warning" data-value="running">${ _('Running') }</a>
  1073. <a class="btn btn-status btn-danger disable-feedback" data-value="failed">${ _('Failed') }</a>
  1074. </div>
  1075. <table id="coordsTable" class="datatables table table-condensed">
  1076. <thead>
  1077. <tr>
  1078. <th width="1%"><div class="select-all hueCheckbox fa"></div></th>
  1079. <th>${_('Status')}</th>
  1080. <th>${_('Name')}</th>
  1081. <th>${_('Type')}</th>
  1082. <th>${_('nextMaterializedTime')}</th>
  1083. <th>${_('lastAction')}</th>
  1084. <th>${_('frequency')}</th>
  1085. <th>${_('timeUnit')}</th>
  1086. <th>${_('externalId')}</th>
  1087. <th>${_('id')}</th>
  1088. <th>${_('pauseTime')}</th>
  1089. </tr>
  1090. </thead>
  1091. <tbody data-bind="foreach: properties['actions']">
  1092. <tr data-bind="click: function() { if (id()) { $root.job().id(id()); $root.job().fetchJob();} }">
  1093. <td><div class="hueCheckbox fa"></div></td>
  1094. <td data-bind="text: status"></td>
  1095. <td data-bind="text: name"></td>
  1096. <td data-bind="text: type"></td>
  1097. <td data-bind="text: nextMaterializedTime"></td>
  1098. <td data-bind="text: lastAction"></td>
  1099. <td data-bind="text: frequency"></td>
  1100. <td data-bind="text: timeUnit"></td>
  1101. <td data-bind="text: externalId"></td>
  1102. <td data-bind="text: id"></td>
  1103. <td data-bind="text: pauseTime"></td>
  1104. </tr>
  1105. </tbody>
  1106. </table> </div>
  1107. <div class="tab-pane" id="bundle-page-logs">
  1108. <pre data-bind="html: logs, logScroller: logs"></pre>
  1109. </div>
  1110. <div class="tab-pane" id="bundle-page-metadata">
  1111. <div data-bind="template: { name: 'render-properties', data: properties['properties'] }"></div>
  1112. </div>
  1113. <div class="tab-pane" id="bundle-page-xml">
  1114. <div data-bind="readonlyXML: properties['xml'], path: 'xml'"></div>
  1115. </div>
  1116. </div>
  1117. </div>
  1118. </div>
  1119. </script>
  1120. <script type="text/html" id="render-properties">
  1121. <!-- ko hueSpinner: { spin: !$data.properties, center: true, size: 'xlarge' } --><!-- /ko -->
  1122. <!-- ko if: $data.properties -->
  1123. <table class="table table-condensed">
  1124. <thead>
  1125. <tr>
  1126. <th>${ _('Name') }</th>
  1127. <th>${ _('Value') }</th>
  1128. </tr>
  1129. </thead>
  1130. <tbody data-bind="foreach: Object.keys($data.properties)">
  1131. <tr>
  1132. <td data-bind="text: $data"></td>
  1133. <td>
  1134. <!-- ko if: $data.indexOf('dir') > -1 || $data.indexOf('path') > -1 || $data.indexOf('output') > -1 || $data.indexOf('input') > -1 || $parent.properties[$data].startsWith('/') || $parent.properties[$data].startsWith('hdfs://') -->
  1135. <a href="javascript:void(0)" data-bind="hueLink: '/filebrowser/view=' + $root.getHDFSPath($parent.properties[$data]) , text: $parent.properties[$data]"></a>
  1136. <!-- /ko -->
  1137. <!-- ko ifnot: $data.indexOf('dir') > -1 || $data.indexOf('path') > -1 || $data.indexOf('output') > -1 || $data.indexOf('input') > -1 || $parent.properties[$data].startsWith('/') || $parent.properties[$data].startsWith('hdfs://') -->
  1138. <span data-bind="text: $parent.properties[$data]"></span>
  1139. <!-- /ko -->
  1140. </td>
  1141. </tr>
  1142. </tbody>
  1143. </table>
  1144. <!-- /ko -->
  1145. </script>
  1146. <script type="text/javascript">
  1147. (function () {
  1148. var Job = function (vm, job) {
  1149. var self = this;
  1150. self.id = ko.observableDefault(job.id);
  1151. self.id.subscribe(function () {
  1152. huePubSub.publish('graph.stop.refresh.view');
  1153. });
  1154. self.doc_url = ko.observableDefault(job.doc_url);
  1155. self.name = ko.observableDefault(job.name || job.id);
  1156. self.type = ko.observableDefault(job.type);
  1157. self.status = ko.observableDefault(job.status);
  1158. self.apiStatus = ko.observableDefault(job.apiStatus);
  1159. self.progress = ko.observableDefault(job.progress);
  1160. self.isRunning = ko.computed(function() {
  1161. return self.apiStatus() == 'RUNNING' || self.apiStatus() == 'PAUSED';
  1162. });
  1163. self.user = ko.observableDefault(job.user);
  1164. self.cluster = ko.observableDefault(job.cluster);
  1165. self.duration = ko.observableDefault(job.duration);
  1166. self.submitted = ko.observableDefault(job.submitted);
  1167. self.logs = ko.observable('');
  1168. self.properties = ko.mapping.fromJS(job.properties || {});
  1169. self.mainType = ko.observable(vm.interface());
  1170. self.coordinatorActions = ko.pureComputed(function() {
  1171. if (self.mainType() == 'schedules' && self.properties['tasks']) {
  1172. var apps = [];
  1173. self.properties['tasks']().forEach(function (instance) {
  1174. var job = new Job(vm, ko.mapping.toJS(instance));
  1175. job.resumeEnabled = function() { return false };
  1176. job.properties = instance;
  1177. apps.push(job);
  1178. });
  1179. var instances = new Jobs(vm);
  1180. instances.apps(apps)
  1181. instances.isCoordinator(true);
  1182. return instances;
  1183. }
  1184. });
  1185. self.textFilter = ko.observable('').extend({ rateLimit: { method: "notifyWhenChangesStop", timeout: 1000 } });
  1186. self.statesValuesFilter = ko.observableArray([
  1187. ko.mapping.fromJS({'value': 'completed', 'name': '${_("Succeeded")}', 'checked': false, 'klass': 'green'}),
  1188. ko.mapping.fromJS({'value': 'running', 'name': '${_("Running")}', 'checked': false, 'klass': 'orange'}),
  1189. ko.mapping.fromJS({'value': 'failed', 'name': '${_("Failed")}', 'checked': false, 'klass': 'red'}),
  1190. ]);
  1191. self.statesFilter = ko.computed(function () {
  1192. var checkedStates = ko.utils.arrayFilter(self.statesValuesFilter(), function (state) {
  1193. return state.checked();
  1194. });
  1195. return ko.utils.arrayMap(checkedStates, function(state){
  1196. return state.value()
  1197. });
  1198. });
  1199. self.typesValuesFilter = ko.observableArray([
  1200. ko.mapping.fromJS({'value': 'map', 'name': '${_("Map")}', 'checked': false, 'klass': 'green'}),
  1201. ko.mapping.fromJS({'value': 'reduce', 'name': '${_("Reduce")}', 'checked': false, 'klass': 'orange'}),
  1202. ]);
  1203. self.typesFilter = ko.computed(function () {
  1204. var checkedTypes = ko.utils.arrayFilter(self.typesValuesFilter(), function (type) {
  1205. return type.checked();
  1206. });
  1207. return ko.utils.arrayMap(checkedTypes, function(type){
  1208. return type.value()
  1209. });
  1210. });
  1211. self.filters = ko.pureComputed(function() {
  1212. return [
  1213. {'text': self.textFilter()},
  1214. {'states': ko.mapping.toJS(self.statesFilter())},
  1215. {'types': ko.mapping.toJS(self.typesFilter())},
  1216. ];
  1217. });
  1218. self.filters.subscribe(function(value) {
  1219. self.fetchProfile('tasks');
  1220. });
  1221. self.rerunModalContent = ko.observable('');
  1222. self.hasKill = ko.pureComputed(function() {
  1223. return ['MAPREDUCE', 'SPARK', 'workflow', 'schedule', 'bundle'].indexOf(self.type()) != -1;
  1224. });
  1225. self.killEnabled = ko.pureComputed(function() {
  1226. return self.hasKill() && self.apiStatus() == 'RUNNING';
  1227. });
  1228. self.hasResume = ko.pureComputed(function() {
  1229. return ['workflow', 'schedule', 'bundle'].indexOf(self.type()) != -1;
  1230. });
  1231. self.resumeEnabled = ko.pureComputed(function() {
  1232. return self.hasResume() && self.apiStatus() == 'PAUSED';
  1233. });
  1234. self.hasRerun = ko.pureComputed(function() {
  1235. return ['workflow', 'schedule-task'].indexOf(self.type()) != -1;
  1236. });
  1237. self.rerunEnabled = ko.pureComputed(function() {
  1238. return self.hasRerun() && ! self.isRunning();
  1239. });
  1240. self.hasPause = ko.pureComputed(function() {
  1241. return ['workflow', 'schedule', 'bundle'].indexOf(self.type()) != -1;
  1242. });
  1243. self.pauseEnabled = ko.pureComputed(function() {
  1244. return self.hasPause() && self.apiStatus() == 'RUNNING';
  1245. });
  1246. self.hasIgnore = ko.pureComputed(function() {
  1247. return ['schedule-task'].indexOf(self.type()) != -1;
  1248. });
  1249. self.ignoreEnabled = ko.pureComputed(function() {
  1250. return self.hasIgnore() && ! self.isRunning();
  1251. });
  1252. self.loadingJob = ko.observable(false);
  1253. var lastFetchJobRequest = null;
  1254. var lastUpdateJobRequest = null;
  1255. self._fetchJob = function (callback) {
  1256. return $.post("/jobbrowser/api/job", {
  1257. app_id: ko.mapping.toJSON(self.id),
  1258. interface: ko.mapping.toJSON(vm.interface)
  1259. }, function (data) {
  1260. if (data.status == 0) {
  1261. if (callback) {
  1262. callback(data);
  1263. };
  1264. } else {
  1265. $(document).trigger("error", data.message);
  1266. }
  1267. });
  1268. };
  1269. self.fetchJob = function () {
  1270. vm.apiHelper.cancelActiveRequest(lastFetchJobRequest);
  1271. vm.apiHelper.cancelActiveRequest(lastUpdateJobRequest);
  1272. self.loadingJob(true);
  1273. var interface = vm.interface();
  1274. if (/application_/.test(self.id()) || /job_/.test(self.id())) {
  1275. interface = 'jobs';
  1276. }
  1277. if (/oozie-oozi-W/.test(self.id())) {
  1278. interface = 'workflows';
  1279. }
  1280. else if (/oozie-oozi-C/.test(self.id())) {
  1281. interface = 'schedules';
  1282. }
  1283. else if (/oozie-oozi-B/.test(self.id())) {
  1284. interface = 'bundles';
  1285. }
  1286. else if (/[a-z0-9]{8}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{4}\-[a-z0-9]{12}/.test(self.id())) {
  1287. interface = 'dataeng-jobs';
  1288. }
  1289. interface = vm.isValidInterface(interface);
  1290. vm.interface(interface);
  1291. lastFetchJobRequest = self._fetchJob(function (data) {
  1292. if (data.status == 0) {
  1293. vm.interface(interface);
  1294. vm.job(new Job(vm, data.app));
  1295. hueUtils.changeURL('#!id=' + vm.job().id());
  1296. var crumbs = [];
  1297. if (/^attempt_/.test(vm.job().id())) {
  1298. crumbs.push({'id': vm.job().properties['app_id'], 'name': vm.job().properties['app_id'], 'type': 'app'});
  1299. crumbs.push({'id': vm.job().properties['task_id'], 'name': vm.job().properties['task_id'], 'type': 'task'});
  1300. }
  1301. if (/^task_/.test(vm.job().id())) {
  1302. crumbs.push({'id': vm.job().properties['app_id'], 'name': vm.job().properties['app_id'], 'type': 'app'});
  1303. }
  1304. if (/-oozie-oozi-W@/.test(vm.job().id())) {
  1305. crumbs.push({'id': vm.job().properties['workflow_id'], 'name': vm.job().properties['workflow_id'], 'type': 'workflow'});
  1306. }
  1307. else if (/-oozie-oozi-W/.test(vm.job().id())) {
  1308. if (vm.job().properties['bundle_id']()) {
  1309. crumbs.push({'id': vm.job().properties['bundle_id'](), 'name': vm.job().properties['bundle_id'](), 'type': 'bundle'});
  1310. }
  1311. if (vm.job().properties['coordinator_id']()) {
  1312. crumbs.push({'id': vm.job().properties['coordinator_id'](), 'name': vm.job().properties['coordinator_id'](), 'type': 'schedule'});
  1313. }
  1314. }
  1315. else if (/-oozie-oozi-C/.test(vm.job().id())) {
  1316. if (vm.job().properties['bundle_id']()) {
  1317. crumbs.push({'id': vm.job().properties['bundle_id'](), 'name': vm.job().properties['bundle_id'](), 'type': 'bundle'});
  1318. }
  1319. }
  1320. crumbs.push({'id': vm.job().id(), 'name': vm.job().name(), 'type': vm.job().type()});
  1321. vm.resetBreadcrumbs(crumbs);
  1322. if (vm.job().type() === 'workflow' && !vm.job().workflowGraphLoaded) {
  1323. vm.job().updateWorkflowGraph();
  1324. }
  1325. vm.job().fetchLogs();
  1326. } else {
  1327. $(document).trigger("error", data.message);
  1328. }
  1329. }).always(function () {
  1330. self.loadingJob(false);
  1331. });
  1332. };
  1333. self.updateJob = function () {console.log('update job', vm.isMini());
  1334. vm.apiHelper.cancelActiveRequest(lastUpdateJobRequest);
  1335. huePubSub.publish('graph.refresh.view');
  1336. if (vm.job() == self && self.apiStatus() == 'RUNNING') {
  1337. lastFetchJobRequest = self._fetchJob(function (data) {
  1338. if (vm.job().type() == 'schedule') {
  1339. vm.job(new Job(vm, data.app)); // Updates everything but redraw the page
  1340. } else {
  1341. vm.job().fetchStatus();
  1342. vm.job().fetchLogs();
  1343. }
  1344. // vm.job().fetchProfile(); // Get name of active tab?
  1345. });
  1346. }
  1347. };
  1348. self.fetchLogs = function (name) {
  1349. $.post("/jobbrowser/api/job/logs", {
  1350. app_id: ko.mapping.toJSON(self.id),
  1351. interface: ko.mapping.toJSON(vm.interface),
  1352. type: ko.mapping.toJSON(self.type),
  1353. name: ko.mapping.toJSON(name ? name : 'default')
  1354. }, function (data) {
  1355. if (data.status == 0) {
  1356. self.logs(data.logs.logs)
  1357. } else {
  1358. $(document).trigger("error", data.message);
  1359. }
  1360. });
  1361. };
  1362. self.fetchProfile = function (name) {
  1363. $.post("/jobbrowser/api/job/profile", {
  1364. app_id: ko.mapping.toJSON(self.id),
  1365. interface: ko.mapping.toJSON(vm.interface),
  1366. app_type: ko.mapping.toJSON(self.type),
  1367. app_property: ko.mapping.toJSON(name),
  1368. app_filters: ko.mapping.toJSON(self.filters),
  1369. }, function (data) {
  1370. if (data.status == 0) {
  1371. self.properties[name](data[name]);
  1372. } else {
  1373. $(document).trigger("error", data.message);
  1374. }
  1375. });
  1376. };
  1377. self.fetchStatus = function () {
  1378. $.post("/jobbrowser/api/job", {
  1379. app_id: ko.mapping.toJSON(self.id),
  1380. interface: ko.mapping.toJSON(self.mainType)
  1381. }, function (data) {
  1382. if (data.status == 0) {
  1383. self.status(data.app.status);
  1384. self.apiStatus(data.app.apiStatus);
  1385. self.progress(data.app.progress);
  1386. } else {
  1387. $(document).trigger("error", data.message);
  1388. }
  1389. });
  1390. };
  1391. self.control = function (action) {
  1392. if (action == 'rerun') {
  1393. $.get('/oozie/rerun_oozie_job/' + self.id() + '/?format=json', function(response) {
  1394. $('#rerun-modal').modal('show');
  1395. self.rerunModalContent(response);
  1396. });
  1397. } else {
  1398. vm.jobs._control([self.id()], action, function(data) {
  1399. $(document).trigger("info", data.message);
  1400. self.fetchStatus();
  1401. });
  1402. }
  1403. }
  1404. self.workflowGraphLoaded = false;
  1405. self.lastArrowsPosition = {
  1406. top: 0,
  1407. left: 0
  1408. }
  1409. self.initialArrowsDrawingCount = 0;
  1410. self.initialArrowsDrawing = function() {
  1411. if (self.initialArrowsDrawingCount < 20) {
  1412. self.initialArrowsDrawingCount++;
  1413. huePubSub.publish('graph.draw.arrows');
  1414. window.setTimeout(self.initialArrowsDrawing, 100);
  1415. }
  1416. else if (self.initialArrowsDrawingCount < 30){
  1417. self.initialArrowsDrawingCount++;
  1418. huePubSub.publish('graph.draw.arrows');
  1419. window.setTimeout(self.initialArrowsDrawing, 500);
  1420. }
  1421. else {
  1422. self.initialArrowsDrawingCount = 0;
  1423. }
  1424. }
  1425. self.updateArrowsTimeout = -1;
  1426. self.updateArrows = function() {
  1427. if ($('canvas').length > 0 && $('canvas').position().top !== self.lastArrowsPosition.top && $('canvas').position().left !== self.lastArrowsPosition.left) {
  1428. self.lastArrowsPosition = $('canvas').position();
  1429. }
  1430. if ($('#workflow-page-graph').is(':visible')){
  1431. if ($('canvas').length === 0){
  1432. huePubSub.publish('graph.draw.arrows');
  1433. }
  1434. }
  1435. else {
  1436. $('canvas').remove();
  1437. }
  1438. self.updateArrowsTimeout = window.setTimeout(self.updateArrows, 100);
  1439. }
  1440. self.updateWorkflowGraph = function() {
  1441. huePubSub.publish('graph.stop.refresh.view');
  1442. $('canvas').remove();
  1443. if (!IS_HUE_4) {
  1444. huePubSub.subscribe('hue4.process.headers', function (opts) {
  1445. opts.callback(opts.response);
  1446. });
  1447. }
  1448. if (vm.job().type() === 'workflow') {
  1449. $('#workflow-page-graph').html('<div class="hue-spinner"><i class="fa fa-spinner fa-spin hue-spinner-center hue-spinner-xlarge"></i></div>');
  1450. $.ajax({
  1451. url: "/oozie/list_oozie_workflow/" + vm.job().id(),
  1452. data: {
  1453. 'graph': true,
  1454. 'element': 'workflow-page-graph',
  1455. 'is_jb2': true
  1456. },
  1457. beforeSend: function (xhr) {
  1458. xhr.setRequestHeader("X-Requested-With", "Hue");
  1459. },
  1460. dataType: "html",
  1461. success: function (response) {
  1462. self.workflowGraphLoaded = true;
  1463. huePubSub.publish('hue4.process.headers', {
  1464. response: response,
  1465. callback: function (r) {
  1466. $('#workflow-page-graph').html(r);
  1467. window.clearTimeout(self.updateArrowsTimeout);
  1468. self.initialArrowsDrawing();
  1469. self.updateArrows();
  1470. }
  1471. });
  1472. }
  1473. });
  1474. }
  1475. };
  1476. };
  1477. var Jobs = function (vm) {
  1478. var self = this;
  1479. self.apps = ko.observableArray().extend({ rateLimit: 50 });
  1480. self.totalApps = ko.observable(null);
  1481. self.isCoordinator = ko.observable(false);
  1482. self.loadingJobs = ko.observable(false);
  1483. self.selectedJobs = ko.observableArray();
  1484. self.hasKill = ko.pureComputed(function() {
  1485. return ['jobs', 'workflows', 'schedules', 'bundles'].indexOf(vm.interface()) != -1 && ! self.isCoordinator();
  1486. });
  1487. self.killEnabled = ko.pureComputed(function() {
  1488. return self.hasKill() && self.selectedJobs().length > 0 && $.grep(self.selectedJobs(), function(job) {
  1489. return job.killEnabled();
  1490. }).length == self.selectedJobs().length;
  1491. });
  1492. self.hasResume = ko.pureComputed(function() {
  1493. return ['workflows', 'schedules', 'bundles'].indexOf(vm.interface()) != -1 && ! self.isCoordinator();
  1494. });
  1495. self.resumeEnabled = ko.pureComputed(function() {
  1496. return self.hasResume() && self.selectedJobs().length > 0 && $.grep(self.selectedJobs(), function(job) {
  1497. return job.resumeEnabled();
  1498. }).length == self.selectedJobs().length;
  1499. });
  1500. self.hasRerun = ko.pureComputed(function() {
  1501. return self.isCoordinator();
  1502. });
  1503. self.rerunEnabled = ko.pureComputed(function() {
  1504. return self.hasRerun() && self.selectedJobs().length == 1 && $.grep(self.selectedJobs(), function(job) {
  1505. return job.rerunEnabled();
  1506. }).length == self.selectedJobs().length;
  1507. });
  1508. self.hasPause = ko.pureComputed(function() {
  1509. return ['workflows', 'schedules', 'bundles'].indexOf(vm.interface()) != -1 && ! self.isCoordinator();
  1510. });
  1511. self.pauseEnabled = ko.pureComputed(function() {
  1512. return self.hasPause() && self.selectedJobs().length > 0 && $.grep(self.selectedJobs(), function(job) {
  1513. return job.pauseEnabled();
  1514. }).length == self.selectedJobs().length;
  1515. });
  1516. self.hasIgnore = ko.pureComputed(function() {
  1517. return self.isCoordinator();
  1518. });
  1519. self.ignoreEnabled = ko.pureComputed(function() {
  1520. return self.hasIgnore() && self.selectedJobs().length > 0 && $.grep(self.selectedJobs(), function(job) {
  1521. return job.ignoreEnabled();
  1522. }).length == self.selectedJobs().length;
  1523. });
  1524. self.textFilter = ko.observable('user:${ user.username } ').extend({ rateLimit: { method: "notifyWhenChangesStop", timeout: 1000 } });
  1525. self.statesValuesFilter = ko.observableArray([
  1526. ko.mapping.fromJS({'value': 'completed', 'name': '${_("Succeeded")}', 'checked': false, 'klass': 'green'}),
  1527. ko.mapping.fromJS({'value': 'running', 'name': '${_("Running")}', 'checked': false, 'klass': 'orange'}),
  1528. ko.mapping.fromJS({'value': 'failed', 'name': '${_("Failed")}', 'checked': false, 'klass': 'red'}),
  1529. ]);
  1530. self.statesFilter = ko.computed(function () {
  1531. var checkedStates = ko.utils.arrayFilter(self.statesValuesFilter(), function (state) {
  1532. return state.checked();
  1533. });
  1534. return ko.utils.arrayMap(checkedStates, function(state){
  1535. return state.value()
  1536. });
  1537. });
  1538. self.timeValueFilter = ko.observable(7).extend({ throttle: 500 });
  1539. self.timeUnitFilter = ko.observable('days').extend({ throttle: 500 });
  1540. self.timeUnitFilterUnits = ko.observable([
  1541. {'value': 'days', 'name': '${_("days")}'},
  1542. {'value': 'hours', 'name': '${_("hours")}'},
  1543. {'value': 'minutes', 'name': '${_("minutes")}'},
  1544. ]);
  1545. self.hasPagination = ko.computed(function() {
  1546. return ['workflows', 'schedules', 'bundles'].indexOf(vm.interface()) != -1;
  1547. });
  1548. self.paginationPage = ko.observable(1);
  1549. self.paginationOffset = ko.observable(1);
  1550. self.paginationResultPage = ko.observable(100);
  1551. self.paginationResultCounts = ko.computed(function() {
  1552. return self.apps().length;
  1553. });
  1554. self.pagination = ko.computed(function() {
  1555. return {
  1556. 'page': self.paginationPage(),
  1557. 'offset': self.paginationOffset(),
  1558. 'limit': self.paginationResultPage()
  1559. };
  1560. });
  1561. self.showPreviousPage = ko.computed(function() {
  1562. return self.paginationOffset() > 1;
  1563. });
  1564. self.showNextPage = ko.computed(function() {
  1565. return self.totalApps() != null && (self.paginationOffset() + self.paginationResultPage()) < self.totalApps();
  1566. });
  1567. self.previousPage = function() {
  1568. self.paginationOffset(self.paginationOffset() - self.paginationResultPage());
  1569. };
  1570. self.nextPage = function() {
  1571. self.paginationOffset(self.paginationOffset() + self.paginationResultPage());
  1572. };
  1573. self.filters = ko.pureComputed(function() {
  1574. return [
  1575. {'text': self.textFilter()},
  1576. {'time': {'time_value': self.timeValueFilter(), 'time_unit': self.timeUnitFilter()}},
  1577. {'states': ko.mapping.toJS(self.statesFilter())},
  1578. {'pagination': self.pagination()},
  1579. ];
  1580. });
  1581. self.filters.subscribe(function(value) {
  1582. self.fetchJobs();
  1583. });
  1584. self._fetchJobs = function (callback) {
  1585. return $.post("/jobbrowser/api/jobs", {
  1586. interface: ko.mapping.toJSON(vm.interface),
  1587. filters: ko.mapping.toJSON(self.filters),
  1588. }, function (data) {
  1589. if (data.status == 0) {
  1590. if (callback) {
  1591. callback(data);
  1592. };
  1593. } else {
  1594. $(document).trigger("error", data.message);
  1595. }
  1596. });
  1597. };
  1598. var lastFetchJobsRequest = null;
  1599. var lastUpdateJobsRequest = null;
  1600. self.fetchJobs = function () {
  1601. vm.apiHelper.cancelActiveRequest(lastUpdateJobsRequest);
  1602. vm.apiHelper.cancelActiveRequest(lastFetchJobsRequest);
  1603. self.loadingJobs(true);
  1604. vm.job(null);
  1605. lastFetchJobsRequest = self._fetchJobs(function(data) {
  1606. var apps = [];
  1607. if (data && data.apps) {
  1608. data.apps.forEach(function (job) {
  1609. apps.push(new Job(vm, job));
  1610. });
  1611. }
  1612. self.apps(apps);
  1613. self.totalApps(data.total);
  1614. }).always(function () {
  1615. self.loadingJobs(false);
  1616. });
  1617. }
  1618. self.updateJobs = function () {console.log('update jobs', vm.isMini());
  1619. vm.apiHelper.cancelActiveRequest(lastUpdateJobsRequest);
  1620. lastFetchJobsRequest = self._fetchJobs(function(data) {
  1621. var apps = [];
  1622. if (data && data.apps) {
  1623. var i = 0, j = 0;
  1624. var newJobs = [];
  1625. while (i < self.apps().length && j < data.apps.length) {
  1626. if (self.apps()[i].id() != data.apps[j].id) {
  1627. // New Job
  1628. newJobs.push(new Job(vm, data.apps[j]));
  1629. j++;
  1630. } else {
  1631. // Updated jobs
  1632. if (self.apps()[i].status() != data.apps[j].status) {
  1633. self.apps()[i].status(data.apps[j].status);
  1634. self.apps()[i].apiStatus(data.apps[j].apiStatus);
  1635. }
  1636. i++;
  1637. j++;
  1638. }
  1639. }
  1640. if (i < self.apps().length) {
  1641. self.apps().splice(i, self.apps().length - i);
  1642. }
  1643. newJobs.forEach(function (job) {
  1644. self.apps.push(job);
  1645. });
  1646. self.totalApps(data.total);
  1647. }
  1648. });
  1649. };
  1650. self.control = function (action) {
  1651. if (action == 'rerun') {
  1652. $.get('/oozie/rerun_oozie_coord/' + vm.job().id() + '/?format=json', function(response) {
  1653. $('#rerun-modal').modal('show');
  1654. vm.job().rerunModalContent(response);
  1655. var frag = document.createDocumentFragment();
  1656. vm.job().coordinatorActions().selectedJobs().forEach(function (item) {
  1657. var option = $('<option>', {
  1658. value: item.properties.number(),
  1659. selected: true
  1660. });
  1661. option.appendTo($(frag));
  1662. });
  1663. $('#id_actions').find('option').remove();
  1664. $(frag).appendTo('#id_actions');
  1665. });
  1666. } else if (action == 'ignore') {
  1667. $.post('/oozie/manage_oozie_jobs/' + vm.job().id() + '/ignore', {
  1668. actions: $.map(vm.job().coordinatorActions().selectedJobs(), function(wf) {
  1669. return wf.properties.number();
  1670. }).join(' ')
  1671. }, function(response) {
  1672. vm.job().apiStatus('RUNNING');
  1673. vm.job().updateJob();
  1674. });
  1675. } else {
  1676. self._control(
  1677. $.map(self.selectedJobs(), function(job) {
  1678. return job.id();
  1679. }),
  1680. action,
  1681. function(data) {
  1682. $(document).trigger("info", data.message);
  1683. self.updateJobs();
  1684. }
  1685. )
  1686. }
  1687. }
  1688. self._control = function (app_ids, action, callback) {
  1689. $.post("/jobbrowser/api/job/action", {
  1690. app_ids: ko.mapping.toJSON(app_ids),
  1691. interface: ko.mapping.toJSON(vm.interface),
  1692. operation: ko.mapping.toJSON({action: action})
  1693. }, function (data) {
  1694. if (data.status == 0) {
  1695. if (callback) {
  1696. callback(data);
  1697. }
  1698. } else {
  1699. $(document).trigger("error", data.message);
  1700. }
  1701. }).always(function () {
  1702. });
  1703. };
  1704. };
  1705. var JobBrowserViewModel = function () {
  1706. var self = this;
  1707. self.apiHelper = ApiHelper.getInstance();
  1708. self.assistAvailable = ko.observable(true);
  1709. self.isLeftPanelVisible = ko.observable();
  1710. self.apiHelper.withTotalStorage('assist', 'assist_panel_visible', self.isLeftPanelVisible, true);
  1711. self.appConfig = ko.observable();
  1712. self.isMini = ko.observable(false);
  1713. self.availableInterfaces = ko.pureComputed(function () {
  1714. var jobsInterfaceCondition = function () {
  1715. return self.appConfig() && self.appConfig()['browser'] && self.appConfig()['browser']['interpreter_names'].indexOf('yarn') != -1;
  1716. }
  1717. var dataEngInterfaceCondition = function () {
  1718. return self.appConfig() && self.appConfig()['browser'] && self.appConfig()['browser']['interpreter_names'].indexOf('dataeng') != -1;
  1719. }
  1720. var schedulerInterfaceCondition = function () {
  1721. return self.appConfig() && self.appConfig()['scheduler'] && self.appConfig()['scheduler']['interpreters'].length > 0;
  1722. }
  1723. var interfaces = [
  1724. {'interface': 'jobs', 'label': '${ _ko('Jobs') }', 'condition': jobsInterfaceCondition},
  1725. {'interface': 'dataeng-jobs', 'label': '${ _ko('Jobs') }', 'condition': dataEngInterfaceCondition},
  1726. {'interface': 'workflows', 'label': '${ _ko('Workflows') }', 'condition': schedulerInterfaceCondition},
  1727. {'interface': 'schedules', 'label': '${ _ko('Schedules') }', 'condition': schedulerInterfaceCondition},
  1728. {'interface': 'bundles', 'label': '${ _ko('Bundles') }', 'condition': schedulerInterfaceCondition},
  1729. {'interface': 'slas', 'label': '${ _ko('SLAs') }', 'condition': schedulerInterfaceCondition},
  1730. {'interface': 'dataeng-clusters', 'label': '${ _ko('Clusters') }', 'condition': dataEngInterfaceCondition},
  1731. ];
  1732. return interfaces.filter(function (i) {
  1733. return i.condition();
  1734. });
  1735. });
  1736. self.slasLoadedOnce = false;
  1737. self.slasLoading = ko.observable(true);
  1738. self.loadSlaPage = function(){
  1739. if (!self.slasLoadedOnce) {
  1740. $.ajax({
  1741. url: '/oozie/list_oozie_sla/?is_embeddable=true',
  1742. beforeSend: function (xhr) {
  1743. xhr.setRequestHeader('X-Requested-With', 'Hue');
  1744. },
  1745. dataType: 'html',
  1746. success: function (response) {
  1747. self.slasLoading(false);
  1748. $('#slas').html(response);
  1749. }
  1750. });
  1751. }
  1752. }
  1753. self.oozieInfoLoadedOnce = false;
  1754. self.oozieInfoLoading = ko.observable(true);
  1755. self.loadOozieInfoPage = function(){
  1756. if (!self.oozieInfoLoadedOnce) {
  1757. self.oozieInfoLoadedOnce = true;
  1758. $.ajax({
  1759. url: '/oozie/list_oozie_info/?is_embeddable=true',
  1760. beforeSend: function (xhr) {
  1761. xhr.setRequestHeader('X-Requested-With', 'Hue');
  1762. },
  1763. dataType: 'html',
  1764. success: function (response) {
  1765. self.oozieInfoLoading(false);
  1766. $('#oozieInfo').html(response);
  1767. }
  1768. });
  1769. }
  1770. }
  1771. self.interface = ko.observable();
  1772. self.isValidInterface = function(name) {
  1773. var flatAvailableInterfaces = self.availableInterfaces().map(function (i) {
  1774. return i.interface;
  1775. });
  1776. if (flatAvailableInterfaces.indexOf(name) != -1 || name == 'oozie-info') {
  1777. return name;
  1778. } else {
  1779. return flatAvailableInterfaces[0];
  1780. }
  1781. };
  1782. self.selectInterface = function(interface) {
  1783. huePubSub.publish('graph.stop.refresh.view');
  1784. interface = self.isValidInterface(interface);
  1785. self.interface(interface);
  1786. self.resetBreadcrumbs();
  1787. % if not is_mini:
  1788. hueUtils.changeURL('#!' + interface);
  1789. % endif
  1790. self.job(null);
  1791. if (interface === 'slas'){
  1792. % if not is_mini:
  1793. self.loadSlaPage();
  1794. % endif
  1795. }
  1796. else if (interface === 'oozie-info') {
  1797. % if not is_mini:
  1798. self.loadOozieInfoPage();
  1799. % endif
  1800. }
  1801. else {
  1802. self.jobs.fetchJobs();
  1803. }
  1804. };
  1805. self.jobs = new Jobs(self);
  1806. self.job = ko.observable();
  1807. var updateJobInterval = -1;
  1808. var updateJobsInterval = -1;
  1809. self.job.subscribe(function(val) {
  1810. window.clearInterval(updateJobInterval);
  1811. window.clearInterval(updateJobsInterval);
  1812. if (self.interface() && self.interface() !== 'slas' && self.interface() !== 'oozie-info'){
  1813. if (val) {
  1814. if (val.apiStatus() == 'RUNNING') {
  1815. updateJobInterval = setInterval(val.updateJob, 5000, 'jobbrowser');
  1816. }
  1817. }
  1818. else {
  1819. updateJobsInterval = setInterval(self.jobs.updateJobs, 20000, 'jobbrowser');
  1820. }
  1821. }
  1822. });
  1823. self.breadcrumbs = ko.observableArray([]);
  1824. self.resetBreadcrumbs = function(extraCrumbs) {
  1825. var crumbs = [{'id': '', 'name': self.interface(), 'type': self.interface()}]
  1826. if (extraCrumbs) {
  1827. crumbs = crumbs.concat(extraCrumbs);
  1828. }
  1829. self.breadcrumbs(crumbs);
  1830. }
  1831. self.resetBreadcrumbs();
  1832. self.getHDFSPath = function (path) {
  1833. if (path.startsWith('hdfs://')) {
  1834. var bits = path.substr(7).split('/');
  1835. bits.shift();
  1836. return '/' + bits.join('/');
  1837. }
  1838. return path;
  1839. }
  1840. self.load = function() {
  1841. var h = window.location.hash;
  1842. huePubSub.publish('graph.stop.refresh.view');
  1843. h = h.indexOf('#!') === 0 ? h.substr(2) : '';
  1844. switch (h) {
  1845. case '':
  1846. h = 'jobs';
  1847. case 'slas':
  1848. case 'oozie-info':
  1849. case 'jobs':
  1850. case 'workflows':
  1851. case 'schedules':
  1852. case 'bundles':
  1853. case 'dataeng-clusters':
  1854. case 'dataeng-jobs':
  1855. self.selectInterface(h);
  1856. break;
  1857. default:
  1858. if (h.indexOf('id=') === 0 && ! self.isMini()){
  1859. new Job(self, {id: h.substr(3)}).fetchJob();
  1860. }
  1861. else {
  1862. self.selectInterface('reset');
  1863. }
  1864. }
  1865. }
  1866. };
  1867. $(document).ready(function () {
  1868. var jobBrowserViewModel = new JobBrowserViewModel();
  1869. % if not is_mini:
  1870. ko.applyBindings(jobBrowserViewModel, $('#jobbrowserComponents')[0]);
  1871. huePubSub.subscribe('oozie.action.logs.click', function (widget) {
  1872. var jobId = widget.logsURL().match(/jobbrowser\/jobs\/(.+?)\/single_logs$/i);
  1873. if (jobId) {
  1874. jobBrowserViewModel.job().id(jobId[1]);
  1875. jobBrowserViewModel.job().fetchJob();
  1876. } else {
  1877. console.error('Unknown job log url: ' + widget.logsURL());
  1878. }
  1879. }, 'jobbrowser');
  1880. huePubSub.subscribe('oozie.action.click', function (widget) {
  1881. jobBrowserViewModel.job().id(widget.externalId());
  1882. jobBrowserViewModel.job().fetchJob();
  1883. }, 'jobbrowser');
  1884. % else:
  1885. ko.applyBindings(jobBrowserViewModel, $('#jobbrowserMiniComponents')[0]);
  1886. jobBrowserViewModel.isMini(true);
  1887. % endif
  1888. var loadHash = function () {
  1889. if (window.location.pathname.indexOf('jobbrowser') > -1) {
  1890. jobBrowserViewModel.load();
  1891. }
  1892. };
  1893. window.onhashchange = function () {
  1894. loadHash();
  1895. }
  1896. huePubSub.subscribe('cluster.config.set.config', function (clusterConfig) {
  1897. jobBrowserViewModel.appConfig(clusterConfig && clusterConfig['app_config']);
  1898. loadHash();
  1899. });
  1900. huePubSub.publish('cluster.config.get.config');
  1901. % if not is_mini:
  1902. huePubSub.subscribe('submit.rerun.popup.return', function (data) {
  1903. $.jHueNotify.info('${_('Rerun submitted.')}');
  1904. $('#rerun-modal').modal('hide');
  1905. jobBrowserViewModel.job().apiStatus('RUNNING');
  1906. jobBrowserViewModel.job().updateJob();
  1907. }, 'jobbrowser');
  1908. % else:
  1909. huePubSub.subscribe('mini.jb.navigate', function(interface){
  1910. jobBrowserViewModel.selectInterface(interface);
  1911. });
  1912. % endif
  1913. $(document).on('shown', '.jb-logs-link', function (e) {
  1914. var dest = $(e.target).attr('href');
  1915. if (dest.indexOf('logs') > -1){
  1916. $(dest).find('pre').css('overflow-y', 'auto').height(Math.max(200, $(window).height() - $(dest).find('pre').offset().top - $('.page-content').scrollTop() - 30));
  1917. }
  1918. });
  1919. });
  1920. })();
  1921. </script>
  1922. </span>
  1923. % if not is_embeddable:
  1924. ${ commonfooter(request, messages) | n,unicode }
  1925. % endif