| OLD | NEW |
| 1 <!-- | 1 <!-- |
| 2 Copyright 2016 The LUCI Authors. All rights reserved. | 2 Copyright 2016 The LUCI Authors. All rights reserved. |
| 3 Use of this source code is governed under the Apache License, Version 2.0 | 3 Use of this source code is governed under the Apache License, Version 2.0 |
| 4 that can be found in the LICENSE file. | 4 that can be found in the LICENSE file. |
| 5 | 5 |
| 6 This in an HTML Import-able file that contains the definition | 6 This in an HTML Import-able file that contains the definition |
| 7 of the following elements: | 7 of the following elements: |
| 8 | 8 |
| 9 <bot-page> | 9 <bot-page> |
| 10 | 10 |
| 11 bot-page shows the tasks, events, and dimensions of a bot. | 11 bot-page shows the tasks, events, and dimensions of a bot. |
| 12 | 12 |
| 13 This is a top-level element. | 13 This is a top-level element. |
| 14 | 14 |
| 15 Properties: | 15 Properties: |
| 16 bot_id: String, Used in testing to specify a bot_id | 16 bot_id: String, Used in testing to specify a bot_id |
| 17 client_id: String, Oauth 2.0 client id. It will be set by server-side | 17 client_id: String, Oauth 2.0 client id. It will be set by server-side |
| 18 template evaluation. | 18 template evaluation. |
| 19 | 19 |
| 20 Methods: | 20 Methods: |
| 21 None. | 21 None. |
| 22 | 22 |
| 23 Events: | 23 Events: |
| 24 None. | 24 None. |
| 25 --> | 25 --> |
| 26 | 26 |
| 27 <link rel="import" href="/res/imp/bower_components/iron-collapse/iron-collapse.h
tml"> | 27 <link rel="import" href="/res/imp/bower_components/iron-collapse/iron-collapse.h
tml"> |
| 28 <link rel="import" href="/res/imp/bower_components/iron-icon/iron-icon.html"> | 28 <link rel="import" href="/res/imp/bower_components/iron-icon/iron-icon.html"> |
| 29 <link rel="import" href="/res/imp/bower_components/iron-icons/iron-icons.html"> | 29 <link rel="import" href="/res/imp/bower_components/iron-icons/iron-icons.html"> |
| 30 <link rel="import" href="/res/imp/bower_components/paper-button/paper-button.htm
l"> |
| 30 <link rel="import" href="/res/imp/bower_components/paper-checkbox/paper-checkbox
.html"> | 31 <link rel="import" href="/res/imp/bower_components/paper-checkbox/paper-checkbox
.html"> |
| 32 <link rel="import" href="/res/imp/bower_components/paper-dialog/paper-dialog.htm
l"> |
| 31 <link rel="import" href="/res/imp/bower_components/paper-input/paper-input.html"
> | 33 <link rel="import" href="/res/imp/bower_components/paper-input/paper-input.html"
> |
| 32 <link rel="import" href="/res/imp/bower_components/paper-tabs/paper-tabs.html"> | 34 <link rel="import" href="/res/imp/bower_components/paper-tabs/paper-tabs.html"> |
| 33 <link rel="import" href="/res/imp/bower_components/paper-icon-button/paper-icon-
button.html"> | |
| 34 <link rel="import" href="/res/imp/bower_components/polymer/polymer.html"> | 35 <link rel="import" href="/res/imp/bower_components/polymer/polymer.html"> |
| 35 | 36 |
| 36 <link rel="import" href="/res/imp/common/swarming-app.html"> | 37 <link rel="import" href="/res/imp/common/swarming-app.html"> |
| 37 <link rel="import" href="/res/imp/common/url-param.html"> | 38 <link rel="import" href="/res/imp/common/url-param.html"> |
| 39 <link rel="import" href="/res/imp/common/error-toast.html"> |
| 38 | 40 |
| 39 <link rel="import" href="bot-page-data.html"> | 41 <link rel="import" href="bot-page-data.html"> |
| 40 <link rel="import" href="bot-page-shared-behavior.html"> | 42 <link rel="import" href="bot-page-shared-behavior.html"> |
| 41 | 43 |
| 42 | 44 |
| 43 <dom-module id="bot-page"> | 45 <dom-module id="bot-page"> |
| 44 <template> | 46 <template> |
| 45 <style include="iron-flex iron-flex-alignment iron-positioning swarming-app-
style"> | 47 <style include="iron-flex iron-flex-alignment iron-positioning swarming-app-
style"> |
| 46 | 48 |
| 47 .header { | 49 .header { |
| (...skipping 15 matching lines...) Expand all Loading... |
| 63 max-height: 50px; | 65 max-height: 50px; |
| 64 width: initial; | 66 width: initial; |
| 65 height: initial; | 67 height: initial; |
| 66 } | 68 } |
| 67 | 69 |
| 68 table { | 70 table { |
| 69 border-collapse: collapse; | 71 border-collapse: collapse; |
| 70 margin-left: 5px; | 72 margin-left: 5px; |
| 71 margin-bottom: 5px; | 73 margin-bottom: 5px; |
| 72 } | 74 } |
| 73 td, th { | 75 td, |
| 76 th { |
| 74 border: 1px solid #BBB; | 77 border: 1px solid #BBB; |
| 75 padding: 5px; | 78 padding: 5px; |
| 76 } | 79 } |
| 77 | 80 |
| 78 .quarantined, .failed_task { | 81 .quarantined, |
| 82 .failed_task { |
| 79 background-color: #ffdddd; | 83 background-color: #ffdddd; |
| 80 } | 84 } |
| 81 .dead { | 85 .dead, |
| 86 .bot_died { |
| 82 background-color: #cccccc; | 87 background-color: #cccccc; |
| 83 } | 88 } |
| 84 | 89 |
| 85 .message { | 90 .message { |
| 86 white-space: pre-line; | 91 white-space: pre-line; |
| 87 font-family: monospace; | 92 font-family: monospace; |
| 88 } | 93 } |
| 89 | 94 |
| 90 .bot_state { | 95 .bot_state { |
| 91 white-space: pre; | 96 white-space: pre; |
| 92 font-family: monospace; | 97 font-family: monospace; |
| 93 margin-bottom: 10px; | 98 margin-bottom: 10px; |
| 94 } | 99 } |
| 95 | 100 |
| 96 .tabs { | 101 .tabs { |
| 97 background-color: #1F78B4; | 102 background-color: #1F78B4; |
| 98 color: #fff; | 103 color: #fff; |
| 99 max-width: 600px; | 104 max-width: 600px; |
| 100 --paper-checkbox-label-color: #fff; | 105 --paper-checkbox-label-color: #fff; |
| 101 margin-left: 5px; | 106 margin-left: 5px; |
| 102 } | 107 } |
| 103 | 108 |
| 104 .tasks_table, .events_table { | 109 .tasks_table, |
| 110 .events_table { |
| 105 border: 3px solid #1F78B4; | 111 border: 3px solid #1F78B4; |
| 106 } | 112 } |
| 107 | 113 |
| 108 paper-checkbox { | 114 paper-checkbox { |
| 109 --paper-checkbox-label-color: #fff; | 115 --paper-checkbox-label-color: #fff; |
| 110 --paper-checkbox-checked-color: #fff; | 116 --paper-checkbox-checked-color: #fff; |
| 111 --paper-checkbox-checkmark-color: #000; | 117 --paper-checkbox-checkmark-color: #000; |
| 112 --paper-checkbox-unchecked-color: #fff; | 118 --paper-checkbox-unchecked-color: #fff; |
| 113 padding: 3px; | 119 padding: 3px; |
| 114 } | 120 } |
| 115 | 121 |
| 116 paper-tab.iron-selected { | 122 paper-tab.iron-selected { |
| 117 background-color: #A6CEE3; | 123 background-color: #A6CEE3; |
| 118 border: 3px solid #1F78B4; | 124 border: 3px solid #1F78B4; |
| 119 color: #000; | 125 color: #000; |
| 120 font-weight: bold; | 126 font-weight: bold; |
| 121 text-decoration: underline; | 127 text-decoration: underline; |
| 122 } | 128 } |
| 123 | 129 |
| 130 paper-dialog { |
| 131 border-radius: 6px; |
| 132 } |
| 133 |
| 124 </style> | 134 </style> |
| 125 | 135 |
| 126 <url-param name="id" | 136 <url-param name="id" |
| 127 value="{{bot_id}}"> | 137 value="{{bot_id}}"> |
| 128 </url-param> | 138 </url-param> |
| 129 <url-param name="show_all_events" | 139 <url-param name="show_all_events" |
| 130 value="{{_show_all}}"> | 140 value="{{_show_all}}"> |
| 131 </url-param> | 141 </url-param> |
| 132 <url-param name="selected" | 142 <url-param name="selected" |
| 133 value="{{_selected}}"> | 143 value="{{_selected}}"> |
| 134 </url-param> | 144 </url-param> |
| 135 <url-param name="show_state" | 145 <url-param name="show_state" |
| 136 value="{{_show_state}}"> | 146 value="{{_show_state}}"> |
| 137 </url-param> | 147 </url-param> |
| 138 | 148 |
| 139 <swarming-app | 149 <swarming-app |
| 140 client_id="[[client_id]]" | 150 client_id="[[client_id]]" |
| 141 auth_headers="{{_auth_headers}}" | 151 auth_headers="{{_auth_headers}}" |
| 142 permissions="{{_permissions}}" | 152 permissions="{{_permissions}}" |
| 143 signed_in="{{_signed_in}}" | 153 signed_in="{{_signed_in}}" |
| 144 | 154 |
| 145 busy="[[_busy]]" | 155 busy="[[_busy]]" |
| 146 name="Swarming Bot Page"> | 156 name="Swarming Bot Page"> |
| 147 | 157 |
| 148 <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2> | 158 <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2> |
| 149 | 159 |
| 150 <div hidden$="[[_not(_signed_in)]]"> | 160 <div hidden$="[[_not(_signed_in)]]"> |
| 151 | 161 |
| 152 <bot-page-data | 162 <bot-page-data |
| 163 id="data" |
| 153 auth_headers="[[_auth_headers]]" | 164 auth_headers="[[_auth_headers]]" |
| 154 bot_id="[[bot_id]]" | 165 bot_id="[[bot_id]]" |
| 155 | 166 |
| 156 bot="{{_bot}}" | 167 bot="{{_bot}}" |
| 157 busy="{{_busy}}" | 168 busy="{{_busy}}" |
| 158 events="{{_events}}" | 169 events="{{_events}}" |
| 159 tasks="{{_tasks}}"> | 170 tasks="{{_tasks}}"> |
| 160 </bot-page-data> | 171 </bot-page-data> |
| 161 | 172 |
| 162 <div class="header horizontal layout"> | 173 <div class="header horizontal layout"> |
| 163 <paper-input class="id_input" label="Bot id" value="{{bot_id}}"></pape
r-input> | 174 <paper-input class="id_input" label="Bot id" value="{{bot_id}}"></pape
r-input> |
| 164 <button> | 175 <button on-click="_refresh"> |
| 165 <iron-icon class="refresh" icon="icons:refresh"></iron-icon> | 176 <iron-icon class="refresh" icon="icons:refresh"></iron-icon> |
| 166 </button> | 177 </button> |
| 167 </div> | 178 </div> |
| 168 | 179 |
| 169 <div> | 180 <div> |
| 170 <table> | 181 <table> |
| 171 <tr class$="[[_isDead(_bot)]]"> | 182 <tr class$="[[_isDead(_bot)]]"> |
| 172 <td>Last Seen</td> | 183 <td>Last Seen</td> |
| 173 <td title="[[_bot.human_last_seen_ts]]"> | 184 <td title="[[_bot.human_last_seen_ts]]"> |
| 174 [[_timeDiffExact(_bot.last_seen_ts)]] ago</td> | 185 [[_timeDiffExact(_bot.last_seen_ts)]] ago</td> |
| 175 <td> | 186 <td> |
| 176 <!-- dom-ifs are slightly less performant than hidden$=, but | 187 <!-- dom-ifs are slightly less performant than hidden$=, but |
| 177 prevent things from first drawing and then hiding. We prefer to | 188 prevent things from first drawing and then hiding. We prefer to |
| 178 not flash buttons or quarantined messages --> | 189 not flash buttons or quarantined messages --> |
| 179 <template is="dom-if" if="[[_canShutdown(_bot,_permissions)]]"> | 190 <template is="dom-if" if="[[_canShutdown(_bot,_permissions)]]"> |
| 180 <button class="raised"> | 191 <button class="raised" on-click="_promptShutdown"> |
| 181 Shut Down Gracefully | 192 Shut Down Gracefully |
| 182 </button> | 193 </button> |
| 183 </template> | 194 </template> |
| 184 <template is="dom-if" if="[[_canDelete(_bot,_permissions)]]"> | 195 <template is="dom-if" if="[[_canDelete(_bot,_permissions)]]"> |
| 185 <button class="raised"> | 196 <button class="raised" on-click="_promptDelete"> |
| 186 Delete | 197 Delete |
| 187 </button> | 198 </button> |
| 188 </template> | 199 </template> |
| 189 </td> | 200 </td> |
| 190 </tr> | 201 </tr> |
| 191 <template is="dom-if" if="[[_bot.quarantined]]"> | 202 <template is="dom-if" if="[[_bot.quarantined]]"> |
| 192 <tr class="quarantined"> | 203 <tr class="quarantined"> |
| 193 <td>Quarantined</td> | 204 <td>Quarantined</td> |
| 194 <td colspan="2">[[_quarantineMessage(_bot)]]</td> | 205 <td colspan="2">[[_quarantineMessage(_bot)]]</td> |
| 195 </tr> | 206 </tr> |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 282 <thead> | 293 <thead> |
| 283 <tr> | 294 <tr> |
| 284 <th>Task</th> | 295 <th>Task</th> |
| 285 <th>Started</th> | 296 <th>Started</th> |
| 286 <th>Duration</th> | 297 <th>Duration</th> |
| 287 <th>Result</th> | 298 <th>Result</th> |
| 288 </tr> | 299 </tr> |
| 289 </thead> | 300 </thead> |
| 290 <tbody> | 301 <tbody> |
| 291 <template is="dom-repeat" items="{{_tasks}}" as="task"> | 302 <template is="dom-repeat" items="{{_tasks}}" as="task"> |
| 292 <tr> | 303 <tr class$="[[_taskClass(task)]]"> |
| 293 <td><a target="_blank" href$="[[_taskLink(task.task_id)]]">[[t
ask.name]]</a></td> | 304 <td><a target="_blank" href$="[[_taskLink(task.task_id)]]">[[t
ask.name]]</a></td> |
| 294 <td>[[task.human_started_ts]]</td> | 305 <td>[[task.human_started_ts]]</td> |
| 295 <td title="[[task.human_completed_ts]]">[[task.human_duration]
]</td> | 306 <td title="[[task.human_completed_ts]]">[[task.human_duration]
]</td> |
| 296 <td>[[task.state]]</td> | 307 <td>[[task.state]]</td> |
| 297 </tr> | 308 </tr> |
| 298 </template> | 309 </template> |
| 299 </tbody> | 310 </tbody> |
| 300 </table> | 311 </table> |
| 301 </template> | 312 </template> |
| 302 | 313 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 321 <td> | 332 <td> |
| 322 <a target="_blank" href$="[[_luciLink(_bot.version)]]">[[_sh
orten(_bot.version,'8')]]</a> | 333 <a target="_blank" href$="[[_luciLink(_bot.version)]]">[[_sh
orten(_bot.version,'8')]]</a> |
| 323 </td> | 334 </td> |
| 324 </tr> | 335 </tr> |
| 325 </template> | 336 </template> |
| 326 </tbody> | 337 </tbody> |
| 327 </table> | 338 </table> |
| 328 </template> | 339 </template> |
| 329 </div> | 340 </div> |
| 330 </div> | 341 </div> |
| 342 </swarming-app> |
| 331 | 343 |
| 332 </swarming-app> | 344 <paper-dialog id="prompt" modal on-iron-overlay-closed="_promptClosed"> |
| 345 <h2>Are you sure?</h2> |
| 346 <div>Are you sure you want to [[_dialogPrompt]]?</div> |
| 347 <div class="buttons"> |
| 348 <paper-button dialog-dismiss autofocus>No</paper-button> |
| 349 <paper-button dialog-confirm>Yes</paper-button> |
| 350 </div> |
| 351 </paper-dialog> |
| 352 |
| 353 <error-toast></error-toast> |
| 333 | 354 |
| 334 </template> | 355 </template> |
| 335 <script> | 356 <script> |
| 336 (function(){ | 357 (function(){ |
| 337 | 358 |
| 338 | 359 |
| 339 Polymer({ | 360 Polymer({ |
| 340 is: 'bot-page', | 361 is: 'bot-page', |
| 341 | 362 |
| 342 behaviors: [ | 363 behaviors: [ |
| 343 SwarmingBehaviors.BotPageBehavior, | 364 SwarmingBehaviors.BotPageBehavior, |
| 344 ], | 365 ], |
| 345 | 366 |
| 346 properties: { | 367 properties: { |
| 347 bot_id: { | 368 bot_id: { |
| 348 type: String, | 369 type: String, |
| 349 }, | 370 }, |
| 350 client_id: { | 371 client_id: { |
| 351 type: String, | 372 type: String, |
| 352 }, | 373 }, |
| 353 | 374 |
| 375 _auth_headers: { |
| 376 type: Object, |
| 377 }, |
| 354 _bot: { | 378 _bot: { |
| 355 type: Object, | 379 type: Object, |
| 356 }, | 380 }, |
| 381 _dialogPrompt: { |
| 382 type: String, |
| 383 value: "", |
| 384 }, |
| 357 _selected: { | 385 _selected: { |
| 358 type: Number, | 386 type: Number, |
| 359 }, | 387 }, |
| 360 _show_all: { | 388 _show_all: { |
| 361 type: Boolean, | 389 type: Boolean, |
| 362 }, | 390 }, |
| 363 _show_state: { | 391 _show_state: { |
| 364 type: Boolean, | 392 type: Boolean, |
| 365 } | 393 } |
| 366 }, | 394 }, |
| (...skipping 10 matching lines...) Expand all Loading... |
| 377 return bot && !bot.is_dead && permissions.terminate_bot; | 405 return bot && !bot.is_dead && permissions.terminate_bot; |
| 378 }, | 406 }, |
| 379 | 407 |
| 380 _concat: function(arr) { | 408 _concat: function(arr) { |
| 381 if (!arr) { | 409 if (!arr) { |
| 382 return ""; | 410 return ""; |
| 383 } | 411 } |
| 384 return arr.join(" | "); | 412 return arr.join(" | "); |
| 385 }, | 413 }, |
| 386 | 414 |
| 415 _deleteBot: function() { |
| 416 swarming.postWithToast("/_ah/api/swarming/v1/bot/"+this.bot_id+"/delete"
, |
| 417 "Deleting "+this.bot_id, this._auth_headers); |
| 418 }, |
| 419 |
| 387 _eventList(events, showAll) { | 420 _eventList(events, showAll) { |
| 388 if (!events) { | 421 if (!events) { |
| 389 return []; | 422 return []; |
| 390 } | 423 } |
| 391 return events.filter(function(e){ | 424 return events.filter(function(e){ |
| 392 return showAll || e.message; | 425 return showAll || e.message; |
| 393 }); | 426 }); |
| 394 }, | 427 }, |
| 395 | 428 |
| 396 _isDead(bot){ | 429 _isDead(bot){ |
| (...skipping 16 matching lines...) Expand all Loading... |
| 413 return 1; | 446 return 1; |
| 414 } | 447 } |
| 415 return 1 + arr.length; | 448 return 1 + arr.length; |
| 416 }, | 449 }, |
| 417 | 450 |
| 418 _prettyPrint: function(obj) { | 451 _prettyPrint: function(obj) { |
| 419 obj = obj || {}; | 452 obj = obj || {}; |
| 420 return JSON.stringify(obj, null, 2); | 453 return JSON.stringify(obj, null, 2); |
| 421 }, | 454 }, |
| 422 | 455 |
| 456 _promptClosed: function(e) { |
| 457 if (e.detail.confirmed) { |
| 458 if (this._dialogPrompt.startsWith("shut down")) { |
| 459 this._shutdownBot(); |
| 460 } else { |
| 461 this._deleteBot(); |
| 462 } |
| 463 } |
| 464 }, |
| 465 |
| 466 _promptDelete: function() { |
| 467 this.set("_dialogPrompt", "delete "+this.bot_id); |
| 468 this.$.prompt.open(); |
| 469 }, |
| 470 |
| 471 _promptShutdown: function() { |
| 472 this.set("_dialogPrompt", "shut down "+this.bot_id); |
| 473 this.$.prompt.open(); |
| 474 }, |
| 475 |
| 423 _quarantineMessage: function(bot) { | 476 _quarantineMessage: function(bot) { |
| 424 if (bot && bot.quarantined) { | 477 if (bot && bot.quarantined) { |
| 425 var msg = bot.state.quarantined; | 478 var msg = bot.state.quarantined; |
| 426 // Sometimes, the quarantined message is actually in "error". This | 479 // Sometimes, the quarantined message is actually in "error". This |
| 427 // happens when the bot code has thrown an exception. | 480 // happens when the bot code has thrown an exception. |
| 428 if (msg === undefined || msg === "true" || msg === true) { | 481 if (msg === undefined || msg === "true" || msg === true) { |
| 429 msg = this._attribute(bot, "error"); | 482 msg = this._attribute(bot, "error"); |
| 430 } | 483 } |
| 431 return msg || "True"; | 484 return msg || "True"; |
| 432 } | 485 } |
| 433 return ""; | 486 return ""; |
| 434 }, | 487 }, |
| 435 | 488 |
| 489 _refresh: function() { |
| 490 this.$.data.request(); |
| 491 }, |
| 492 |
| 436 _shorten: function(str, length) { | 493 _shorten: function(str, length) { |
| 437 if (!str || ! length) { | 494 if (!str || ! length) { |
| 438 return ""; | 495 return ""; |
| 439 } | 496 } |
| 440 return str.substring(0, length); | 497 return str.substring(0, length); |
| 441 }, | 498 }, |
| 442 | 499 |
| 500 _shutdownBot: function() { |
| 501 swarming.postWithToast("/_ah/api/swarming/v1/bot/"+this.bot_id+"/termina
te", |
| 502 "Shutting down "+this.bot_id, this._auth_headers); |
| 503 }, |
| 504 |
| 443 _task: function(bot) { | 505 _task: function(bot) { |
| 444 return (bot && bot.task_id) || "idle"; | 506 return (bot && bot.task_id) || "idle"; |
| 445 }, | 507 }, |
| 446 | 508 |
| 509 _taskClass: function(task) { |
| 510 if (task && task.internal_failure) { |
| 511 return "bot_died"; |
| 512 } |
| 513 if (task && task.failure) { |
| 514 return "failed_task"; |
| 515 } |
| 516 return ""; |
| 517 }, |
| 518 |
| 447 _taskLink: function(task_id) { | 519 _taskLink: function(task_id) { |
| 448 // TODO(kjlubick): Migrate this to /newui/ when ready | 520 // TODO(kjlubick): Migrate this to /newui/ when ready |
| 449 if (task_id) { | 521 if (task_id) { |
| 450 return "/user/task/" + task_id; | 522 return "/user/task/" + task_id; |
| 451 } | 523 } |
| 452 return undefined; | 524 return undefined; |
| 453 }, | 525 }, |
| 454 | 526 |
| 455 _toggleState: function() { | 527 _toggleState: function() { |
| 456 this.set("_show_state", !this._show_state); | 528 this.set("_show_state", !this._show_state); |
| 457 } | 529 } |
| 458 | 530 |
| 459 }); | 531 }); |
| 460 })(); | 532 })(); |
| 461 </script> | 533 </script> |
| 462 </dom-module> | 534 </dom-module> |
| OLD | NEW |