| OLD | NEW |
| (Empty) |
| 1 <!-- | |
| 2 Copyright 2016 The LUCI Authors. All rights reserved. | |
| 3 Use of this source code is governed under the Apache License, Version 2.0 | |
| 4 that can be found in the LICENSE file. | |
| 5 | |
| 6 This in an HTML Import-able file that contains the definition | |
| 7 of the following elements: | |
| 8 | |
| 9 <bot-page> | |
| 10 | |
| 11 bot-page shows the tasks, events, and dimensions of a bot. | |
| 12 | |
| 13 This is a top-level element. | |
| 14 | |
| 15 Properties: | |
| 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 | |
| 18 template evaluation. | |
| 19 | |
| 20 Methods: | |
| 21 None. | |
| 22 | |
| 23 Events: | |
| 24 None. | |
| 25 --> | |
| 26 | |
| 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"> | |
| 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"> | |
| 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"> | |
| 33 <link rel="import" href="/res/imp/bower_components/paper-input/paper-input.html"
> | |
| 34 <link rel="import" href="/res/imp/bower_components/paper-tabs/paper-tabs.html"> | |
| 35 <link rel="import" href="/res/imp/bower_components/polymer/polymer.html"> | |
| 36 | |
| 37 <link rel="import" href="/res/imp/common/error-toast.html"> | |
| 38 <link rel="import" href="/res/imp/common/pageable-data.html"> | |
| 39 <link rel="import" href="/res/imp/common/single-page-style.html"> | |
| 40 <link rel="import" href="/res/imp/common/swarming-app.html"> | |
| 41 <link rel="import" href="/res/imp/common/task-behavior.html"> | |
| 42 <link rel="import" href="/res/imp/common/url-param.html"> | |
| 43 | |
| 44 <link rel="import" href="bot-page-data.html"> | |
| 45 <link rel="import" href="bot-page-shared-behavior.html"> | |
| 46 <link rel="import" href="bot-page-summary.html"> | |
| 47 | |
| 48 | |
| 49 <dom-module id="bot-page"> | |
| 50 <template> | |
| 51 <style include="iron-flex iron-flex-alignment iron-positioning swarming-app-
style single-page-style task-style"> | |
| 52 .message { | |
| 53 white-space: pre-line; | |
| 54 font-family: monospace; | |
| 55 } | |
| 56 | |
| 57 .bot_state { | |
| 58 white-space: pre; | |
| 59 font-family: monospace; | |
| 60 margin-bottom: 10px; | |
| 61 } | |
| 62 | |
| 63 .tasks_table, | |
| 64 .events_table { | |
| 65 border: 3px solid #1F78B4; | |
| 66 } | |
| 67 | |
| 68 .old_version { | |
| 69 background-color: #ffffdd; | |
| 70 } | |
| 71 | |
| 72 .stats { | |
| 73 min-width: 700px; | |
| 74 flex-grow: 2; | |
| 75 } | |
| 76 | |
| 77 #collapse { | |
| 78 max-width: 700px; | |
| 79 } | |
| 80 | |
| 81 .cloud { | |
| 82 white-space: nowrap; | |
| 83 margin-bottom: 5px; | |
| 84 margin-top: auto; | |
| 85 } | |
| 86 | |
| 87 paper-checkbox { | |
| 88 --paper-checkbox-label-color: #fff; | |
| 89 --paper-checkbox-checked-color: #fff; | |
| 90 --paper-checkbox-checkmark-color: #000; | |
| 91 --paper-checkbox-unchecked-color: #fff; | |
| 92 padding: 3px; | |
| 93 } | |
| 94 | |
| 95 paper-dialog { | |
| 96 border-radius: 6px; | |
| 97 } | |
| 98 </style> | |
| 99 | |
| 100 <url-param name="id" | |
| 101 value="{{bot_id}}"> | |
| 102 </url-param> | |
| 103 <url-param name="show_all_events" | |
| 104 value="{{_show_all}}"> | |
| 105 </url-param> | |
| 106 <url-param name="selected" | |
| 107 value="{{_selected}}"> | |
| 108 </url-param> | |
| 109 <url-param name="show_state" | |
| 110 value="{{_show_state}}"> | |
| 111 </url-param> | |
| 112 | |
| 113 <swarming-app | |
| 114 client_id="[[client_id]]" | |
| 115 auth_headers="{{_auth_headers}}" | |
| 116 permissions="{{_permissions}}" | |
| 117 server_details="{{_server_details}}" | |
| 118 signed_in="{{_signed_in}}" | |
| 119 | |
| 120 busy="[[_or(_busy1,_busy2,_busy3)]]" | |
| 121 name="Swarming Bot Page"> | |
| 122 | |
| 123 <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2> | |
| 124 | |
| 125 <div hidden$="[[_not(_signed_in)]]"> | |
| 126 | |
| 127 <bot-page-data | |
| 128 id="data" | |
| 129 auth_headers="[[_auth_headers]]" | |
| 130 bot_id="[[bot_id]]" | |
| 131 | |
| 132 bot="{{_bot}}" | |
| 133 busy="{{_busy1}}" | |
| 134 events="{{_events}}" | |
| 135 tasks="{{_tasks}}" | |
| 136 on-reload="_clearAndReload"> | |
| 137 </bot-page-data> | |
| 138 | |
| 139 <div class="header horizontal layout"> | |
| 140 <paper-input class="id_input" label="Bot id" value="{{bot_id}}"></pape
r-input> | |
| 141 <template is="dom-if" if="[[_ccLink(_bot)]]"> | |
| 142 <div class="vertical layout"> | |
| 143 <a href$="[[_ccLink(_bot)]]" class="cloud">Cloud Console</a> | |
| 144 </div> | |
| 145 </template> | |
| 146 <button on-click="_refresh"> | |
| 147 <iron-icon class="refresh" icon="icons:refresh"></iron-icon> | |
| 148 </button> | |
| 149 </div> | |
| 150 | |
| 151 <div class="horizontal wrap layout"> | |
| 152 <div class="flex"> | |
| 153 <table> | |
| 154 <tr class$="[[_isDead(_bot)]]" title="Last time the bot contacted
the server."> | |
| 155 <td>Last Seen</td> | |
| 156 <td title="[[_bot.human_last_seen_ts]]"> | |
| 157 [[_timeDiffExact(_bot.last_seen_ts)]] ago</td> | |
| 158 <td> | |
| 159 <!-- dom-ifs are slightly less performant than hidden$=, but | |
| 160 prevent things from first drawing and then hiding. We prefer to | |
| 161 not flash buttons or quarantined messages --> | |
| 162 <template is="dom-if" if="[[_canShutdown(_bot,_permissions)]]"
> | |
| 163 <button class="raised" on-click="_promptShutdown"> | |
| 164 Shut Down Gracefully | |
| 165 </button> | |
| 166 </template> | |
| 167 <template is="dom-if" if="[[_canDelete(_bot,_permissions)]]"> | |
| 168 <button class="raised" on-click="_promptDelete"> | |
| 169 Delete | |
| 170 </button> | |
| 171 </template> | |
| 172 </td> | |
| 173 </tr> | |
| 174 <template is="dom-if" if="[[_bot.quarantined]]"> | |
| 175 <tr class="quarantined"> | |
| 176 <td>Quarantined</td> | |
| 177 <td colspan="2" class="message">[[_quarantineMessage(_bot)]]</
td> | |
| 178 </tr> | |
| 179 </template> | |
| 180 <tr> | |
| 181 <td>Current Task</td> | |
| 182 <td> | |
| 183 <a target="_blank" rel="noopener" | |
| 184 href$="[[_taskLink(_bot.task_id)]]"> | |
| 185 [[_task(_bot)]] | |
| 186 </a> | |
| 187 </td> | |
| 188 <td> | |
| 189 <!-- TODO(kjlubick) add the cancel button when swarming can | |
| 190 cancel running tasks --> | |
| 191 </td> | |
| 192 </tr> | |
| 193 <tr> | |
| 194 <td rowspan$="[[_numRows(_bot.dimensions)]]">Dimensions</td> | |
| 195 </tr> | |
| 196 <template | |
| 197 is="dom-repeat" | |
| 198 items="[[_bot.dimensions]]" | |
| 199 as="dim"> | |
| 200 <tr> | |
| 201 <td>[[dim.key]]</td> | |
| 202 <td>[[_concat(dim.value)]]</td> | |
| 203 </tr> | |
| 204 </template> | |
| 205 | |
| 206 <tr title="IP address that the server saw the connection from."> | |
| 207 <td>External IP</td> | |
| 208 <td><a href$="[[_bot.external_ip]]">[[_bot.external_ip]]</a></td
> | |
| 209 <td></td> | |
| 210 </tr> | |
| 211 <tr | |
| 212 class$="[[_classVersion(_server_details.bot_version,_bot.versi
on)]]" | |
| 213 title="Version is based on the content of swarming_bot.zip whi
ch is the swarming bot code. The bot won't update if quarantined, dead, or busy.
"> | |
| 214 <td>Bot Version</td> | |
| 215 <td>[[_shorten(_bot.version,'8')]]</td> | |
| 216 <td></td> | |
| 217 </tr> | |
| 218 <tr title="The version the server expects the bot to be using."> | |
| 219 <td>Expected Bot Version</td> | |
| 220 <td>[[_shorten(_server_details.bot_version,'8')]]</td> | |
| 221 <td></td> | |
| 222 </tr> | |
| 223 <tr title="First time ever a bot with this id contacted the server
."> | |
| 224 <td>First seen</td> | |
| 225 <td title="[[_bot.human_first_seen_ts]]"> | |
| 226 [[_timeDiffApprox(_bot.first_seen_ts)]] ago | |
| 227 </td> | |
| 228 <td></td> | |
| 229 </tr> | |
| 230 <tr title="How the bot is authenticated by the server."> | |
| 231 <td>Authenticated as</td> | |
| 232 <td colspan=2>[[_bot.authenticated_as]]</td> | |
| 233 </tr> | |
| 234 <template is="dom-if" if="[[_bot.lease_id]]"> | |
| 235 <tr> | |
| 236 <td>Machine Provider Lease ID</td> | |
| 237 <td colspan=2> | |
| 238 <a href$="[[_mpLink(_bot,_server_details.machine_provider_te
mplate)]]"> | |
| 239 [[_bot.lease_id]] | |
| 240 </a> | |
| 241 </td> | |
| 242 </tr> | |
| 243 <tr> | |
| 244 <td>Machine Provider Lease Expires</td> | |
| 245 <td colspan=2>[[_bot.human_lease_expiration_ts]]</td> | |
| 246 </tr> | |
| 247 </template> | |
| 248 </table> | |
| 249 | |
| 250 <span class="title">State</span> | |
| 251 | |
| 252 <template is="dom-if" if="[[_not(_show_state)]]"> | |
| 253 <button on-click="_toggleState"> | |
| 254 <iron-icon icon="icons:add-circle-outline"></iron-icon> | |
| 255 </button> | |
| 256 </template> | |
| 257 | |
| 258 <template is="dom-if" if="[[_show_state]]"> | |
| 259 <button on-click="_toggleState"> | |
| 260 <iron-icon icon="icons:remove-circle-outline"></iron-icon> | |
| 261 </button> | |
| 262 </template> | |
| 263 | |
| 264 <iron-collapse id="collapse" opened="[[_show_state]]"> | |
| 265 <div class="bot_state">[[_prettyPrint(_bot.state)]]</div> | |
| 266 </iron-collapse> | |
| 267 </div> | |
| 268 | |
| 269 <div class="stats flex"> | |
| 270 <bot-page-summary | |
| 271 tasks="[[_tasks]]"> | |
| 272 </bot-page-summary> | |
| 273 </div> | |
| 274 </div> | |
| 275 | |
| 276 <div class="tabs"> | |
| 277 <paper-tabs selected="{{_selected}}" no-bar> | |
| 278 <paper-tab>Tasks</paper-tab> | |
| 279 <paper-tab>Events</paper-tab> | |
| 280 </paper-tabs> | |
| 281 | |
| 282 <template is="dom-if" if="[[_showEvents]]"> | |
| 283 <paper-checkbox checked="{{_show_all}}"> | |
| 284 Show all events | |
| 285 </paper-checkbox> | |
| 286 </template> | |
| 287 </div> | |
| 288 | |
| 289 <template is="dom-if" if="[[_not(_showEvents)]]"> | |
| 290 <table class="tasks_table"> | |
| 291 <thead> | |
| 292 <tr> | |
| 293 <th>Task</th> | |
| 294 <th>Started</th> | |
| 295 <th>Duration</th> | |
| 296 <th>Result</th> | |
| 297 </tr> | |
| 298 </thead> | |
| 299 <tbody> | |
| 300 <template is="dom-repeat" items="{{_tasks}}" as="task"> | |
| 301 <tr class$="[[_taskClass(task)]]"> | |
| 302 <td> | |
| 303 <a target="_blank" rel="noopener" | |
| 304 href$="[[_taskLink(task.task_id)]]"> | |
| 305 [[task.name]] | |
| 306 </a> | |
| 307 </td> | |
| 308 <td>[[task.human_started_ts]]</td> | |
| 309 <td title="[[task.human_completed_ts]]">[[task.human_duration]
]</td> | |
| 310 <td>[[task.state]]</td> | |
| 311 </tr> | |
| 312 </template> | |
| 313 </tbody> | |
| 314 </table> | |
| 315 </template> | |
| 316 | |
| 317 <template is="dom-if" if="[[_showEvents]]"> | |
| 318 <table class="events_table"> | |
| 319 <thead> | |
| 320 <tr> | |
| 321 <th>Message</th> | |
| 322 <th>Type</th> | |
| 323 <th>Timestamp</th> | |
| 324 <th>Task ID</th> | |
| 325 <th>Version</th> | |
| 326 </tr> | |
| 327 </thead> | |
| 328 <tbody> | |
| 329 <template is="dom-repeat" items="{{_eventList(_show_all,_events.*)
}}" as="event"> | |
| 330 <tr> | |
| 331 <td class="message">[[event.message]]</td> | |
| 332 <td>[[event.event_type]]</td> | |
| 333 <td>[[event.human_ts]]</td> | |
| 334 <td> | |
| 335 <a target="_blank" rel="noopener" | |
| 336 href$="[[_taskLink(event.task_id)]]"> | |
| 337 [[event.task_id]] | |
| 338 </a> | |
| 339 </td> | |
| 340 <td class$="[[_classVersion(_server_details.bot_version,event.
version)]]"> | |
| 341 [[_shorten(event.version,'8')]] | |
| 342 </td> | |
| 343 </tr> | |
| 344 </template> | |
| 345 </tbody> | |
| 346 </table> | |
| 347 </template> | |
| 348 <!-- https://github.com/Polymer/polymer/issues/3669 hidden$ doesn't | |
| 349 respect truthiness, only booleanness, so we have _showEvents | |
| 350 instead of using _selected directly.--> | |
| 351 <pageable-data | |
| 352 id="page_tasks" | |
| 353 hidden$="[[_showEvents]]" | |
| 354 busy="{{_busy2}}" | |
| 355 label="Show more tasks" | |
| 356 output="{{_tasks}}" | |
| 357 parse="[[_parseTasks]]"> | |
| 358 </pageable-data> | |
| 359 <pageable-data | |
| 360 id="page_events" | |
| 361 hidden$="[[_not(_showEvents)]]" | |
| 362 busy="{{_busy3}}" | |
| 363 label="Show more events" | |
| 364 output="{{_events}}" | |
| 365 parse="[[_parseEvents]]"> | |
| 366 </pageable-data> | |
| 367 </div> <!-- hidden when not signed in--> | |
| 368 </swarming-app> | |
| 369 | |
| 370 <paper-dialog id="prompt" modal on-iron-overlay-closed="_promptClosed"> | |
| 371 <h2>Are you sure?</h2> | |
| 372 <div>Are you sure you want to [[_dialogPrompt]]?</div> | |
| 373 <div class="buttons"> | |
| 374 <paper-button dialog-dismiss autofocus>No</paper-button> | |
| 375 <paper-button dialog-confirm>Yes</paper-button> | |
| 376 </div> | |
| 377 </paper-dialog> | |
| 378 | |
| 379 <error-toast></error-toast> | |
| 380 | |
| 381 </template> | |
| 382 <script> | |
| 383 (function(){ | |
| 384 | |
| 385 Polymer({ | |
| 386 is: 'bot-page', | |
| 387 | |
| 388 behaviors: [ | |
| 389 SwarmingBehaviors.BotPageBehavior, | |
| 390 ], | |
| 391 | |
| 392 properties: { | |
| 393 bot_id: { | |
| 394 type: String, | |
| 395 }, | |
| 396 client_id: { | |
| 397 type: String, | |
| 398 }, | |
| 399 | |
| 400 _auth_headers: { | |
| 401 type: Object, | |
| 402 observer: "_reload", | |
| 403 }, | |
| 404 _bot: { | |
| 405 type: Object, | |
| 406 }, | |
| 407 _dialogPrompt: { | |
| 408 type: String, | |
| 409 value: "", | |
| 410 }, | |
| 411 _selected: { | |
| 412 type: Number, | |
| 413 }, | |
| 414 _show_all: { | |
| 415 type: Boolean, | |
| 416 }, | |
| 417 _showEvents: { | |
| 418 type: Boolean, | |
| 419 computed: "_truthy(_selected)" | |
| 420 }, | |
| 421 _show_state: { | |
| 422 type: Boolean, | |
| 423 }, | |
| 424 | |
| 425 _parseEvents: { | |
| 426 type: Function, | |
| 427 value: function() { | |
| 428 return this.$.data.parseEvents.bind(this); | |
| 429 } | |
| 430 }, | |
| 431 _parseTasks: { | |
| 432 type: Function, | |
| 433 value: function() { | |
| 434 return this.$.data.parseTasks.bind(this); | |
| 435 } | |
| 436 } | |
| 437 }, | |
| 438 | |
| 439 _canCancel: function(bot, permissions) { | |
| 440 return bot && bot.task_id && permissions.cancel_task; | |
| 441 }, | |
| 442 | |
| 443 _canDelete: function(bot, permissions) { | |
| 444 return bot && bot.is_dead && permissions.delete_bot; | |
| 445 }, | |
| 446 | |
| 447 _canShutdown: function(bot, permissions){ | |
| 448 return bot && !bot.is_dead && permissions.terminate_bot; | |
| 449 }, | |
| 450 | |
| 451 _classVersion: function(serverVersion, otherVersion) { | |
| 452 if (serverVersion !== otherVersion) { | |
| 453 return "old_version"; | |
| 454 } | |
| 455 return ""; | |
| 456 }, | |
| 457 | |
| 458 _clearAndReload: function(botID) { | |
| 459 this.$.page_tasks.clear(); | |
| 460 this.$.page_events.clear(); | |
| 461 this._reload(); | |
| 462 }, | |
| 463 | |
| 464 _ccLink: function(bot) { | |
| 465 // We create a link to this bot in cloud console if we detect it is | |
| 466 // on GCE. We look for the zone dimension and create the link using | |
| 467 // that. | |
| 468 if (!bot || !bot.dimensions) { | |
| 469 return false; | |
| 470 } | |
| 471 var link; | |
| 472 bot.dimensions.forEach(function(d){ | |
| 473 if (d.key === "zone") { | |
| 474 link = this._cloudConsoleLink(d.value[0], bot.bot_id); | |
| 475 } | |
| 476 }.bind(this)) | |
| 477 return link; | |
| 478 }, | |
| 479 | |
| 480 _concat: function(arr) { | |
| 481 if (!arr) { | |
| 482 return ""; | |
| 483 } | |
| 484 return arr.join(" | "); | |
| 485 }, | |
| 486 | |
| 487 _deleteBot: function() { | |
| 488 swarming.postWithToast("/_ah/api/swarming/v1/bot/"+this.bot_id+"/delete"
, | |
| 489 "Deleting "+this.bot_id, this._auth_headers); | |
| 490 }, | |
| 491 | |
| 492 _eventList(showAll) { | |
| 493 if (!this._events) { | |
| 494 return []; | |
| 495 } | |
| 496 return this._events.filter(function(e){ | |
| 497 return showAll || e.message; | |
| 498 }); | |
| 499 }, | |
| 500 | |
| 501 _isDead(bot){ | |
| 502 if (bot && bot.is_dead) { | |
| 503 return "dead"; | |
| 504 } | |
| 505 return ""; | |
| 506 }, | |
| 507 | |
| 508 _luciLink: function(revision) { | |
| 509 if (!revision) { | |
| 510 return undefined; | |
| 511 } | |
| 512 return "https://github.com/luci/luci-py/commit/" + revision; | |
| 513 }, | |
| 514 | |
| 515 _mpLink: function(bot, template) { | |
| 516 if (!bot || !bot.lease_id || !template) { | |
| 517 return false; | |
| 518 } | |
| 519 return template.replace("%s", bot.lease_id); | |
| 520 }, | |
| 521 | |
| 522 _numRows: function(arr) { | |
| 523 if (!arr || !arr.length) { | |
| 524 return 1; | |
| 525 } | |
| 526 return 1 + arr.length; | |
| 527 }, | |
| 528 | |
| 529 _prettyPrint: function(obj) { | |
| 530 obj = obj || {}; | |
| 531 return JSON.stringify(obj, null, 2); | |
| 532 }, | |
| 533 | |
| 534 _promptClosed: function(e) { | |
| 535 if (e.detail.confirmed) { | |
| 536 if (this._dialogPrompt.startsWith("shut down")) { | |
| 537 this._shutdownBot(); | |
| 538 } else { | |
| 539 this._deleteBot(); | |
| 540 } | |
| 541 } | |
| 542 }, | |
| 543 | |
| 544 _promptDelete: function() { | |
| 545 this.set("_dialogPrompt", "delete "+this.bot_id); | |
| 546 this.$.prompt.open(); | |
| 547 }, | |
| 548 | |
| 549 _promptShutdown: function() { | |
| 550 this.set("_dialogPrompt", "shut down "+this.bot_id); | |
| 551 this.$.prompt.open(); | |
| 552 }, | |
| 553 | |
| 554 _quarantineMessage: function(bot) { | |
| 555 if (bot && bot.quarantined) { | |
| 556 var msg = bot.state.quarantined; | |
| 557 // Sometimes, the quarantined message is actually in "error". This | |
| 558 // happens when the bot code has thrown an exception. | |
| 559 if (msg === undefined || msg === "true" || msg === true) { | |
| 560 msg = bot.state && bot.state.error; | |
| 561 } | |
| 562 return msg || "True"; | |
| 563 } | |
| 564 return ""; | |
| 565 }, | |
| 566 | |
| 567 _refresh: function() { | |
| 568 this.$.data.request(); | |
| 569 }, | |
| 570 | |
| 571 _reload: function() { | |
| 572 if (!this._auth_headers) { | |
| 573 return; | |
| 574 } | |
| 575 var baseUrl = "/_ah/api/swarming/v1/bot/"+this.bot_id; | |
| 576 // We limit the fields on these two queries to make them faster. | |
| 577 this.$.page_tasks.load(baseUrl + "/tasks?fields=cursor%2Citems(abandoned
_ts%2Cbot_version%2Ccompleted_ts%2Cduration%2Cexit_code%2Cfailure%2Cinternal_fai
lure%2Cmodified_ts%2Cname%2Cstarted_ts%2Cstate%2Ctask_id%2Ctry_number)", | |
| 578 this._auth_headers, 30); | |
| 579 this.$.page_events.load(baseUrl + "/events?fields=cursor%2Citems(event_t
ype%2Cmessage%2Cquarantined%2Ctask_id%2Cts%2Cversion)", | |
| 580 this._auth_headers, 50); | |
| 581 }, | |
| 582 | |
| 583 _shorten: function(str, length) { | |
| 584 if (!str || ! length) { | |
| 585 return ""; | |
| 586 } | |
| 587 return str.substring(0, length); | |
| 588 }, | |
| 589 | |
| 590 _shutdownBot: function() { | |
| 591 swarming.postWithToast("/_ah/api/swarming/v1/bot/"+this.bot_id+"/termina
te", | |
| 592 "Shutting down "+this.bot_id, this._auth_headers); | |
| 593 }, | |
| 594 | |
| 595 _task: function(bot) { | |
| 596 return (bot && bot.task_id) || "idle"; | |
| 597 }, | |
| 598 | |
| 599 _taskClass: function(task) { | |
| 600 if (task && task.internal_failure) { | |
| 601 return "bot_died"; | |
| 602 } | |
| 603 if (task && task.failure) { | |
| 604 return "failed_task"; | |
| 605 } | |
| 606 return ""; | |
| 607 }, | |
| 608 | |
| 609 _toggleState: function() { | |
| 610 this.set("_show_state", !this._show_state); | |
| 611 } | |
| 612 | |
| 613 }); | |
| 614 })(); | |
| 615 </script> | |
| 616 </dom-module> | |
| OLD | NEW |