Chromium Code Reviews| Index: appengine/swarming/elements/res/imp/botpage/bot-page.html |
| diff --git a/appengine/swarming/elements/res/imp/botpage/bot-page.html b/appengine/swarming/elements/res/imp/botpage/bot-page.html |
| index 8340a1addde18b06c5ebe875df1db41311392a87..287c52747637c88578903161d45a0bc4908160b7 100644 |
| --- a/appengine/swarming/elements/res/imp/botpage/bot-page.html |
| +++ b/appengine/swarming/elements/res/imp/botpage/bot-page.html |
| @@ -24,28 +24,114 @@ |
| None. |
| --> |
| +<link rel="import" href="/res/imp/bower_components/iron-collapse/iron-collapse.html"> |
| +<link rel="import" href="/res/imp/bower_components/iron-icon/iron-icon.html"> |
| +<link rel="import" href="/res/imp/bower_components/iron-icons/iron-icons.html"> |
| +<link rel="import" href="/res/imp/bower_components/paper-button/paper-button.html"> |
| +<link rel="import" href="/res/imp/bower_components/paper-checkbox/paper-checkbox.html"> |
| +<link rel="import" href="/res/imp/bower_components/paper-input/paper-input.html"> |
| +<link rel="import" href="/res/imp/bower_components/paper-tabs/paper-tabs.html"> |
| +<link rel="import" href="/res/imp/bower_components/paper-icon-button/paper-icon-button.html"> |
| <link rel="import" href="/res/imp/bower_components/polymer/polymer.html"> |
| -<link rel="import" href="/res/imp/common/common-behavior.html"> |
| <link rel="import" href="/res/imp/common/swarming-app.html"> |
| <link rel="import" href="/res/imp/common/url-param.html"> |
| <link rel="import" href="bot-page-data.html"> |
| +<link rel="import" href="bot-page-shared-behavior.html"> |
| <dom-module id="bot-page"> |
| <template> |
| <style include="iron-flex iron-flex-alignment iron-positioning swarming-app-style"> |
| + .header { |
| + max-width: 450px; |
| + } |
| + |
| + .title { |
| + font-size: 1.5em; |
| + font-weight: bold; |
| + margin-bottom: 5px; |
| + } |
| + .id_input { |
| + --paper-input-container-input: { |
| + font-size: 2em; |
| + }; |
| + } |
| + .refresh { |
| + max-width: 65px; |
| + max-height: 65px; |
| + width: initial; |
| + height: initial; |
| + margin: 6px; |
| + } |
| + |
| + table { |
| + border-collapse: collapse; |
| + margin-left: 5px; |
| + margin-bottom: 5px; |
| + } |
| + td, th { |
| + border: 1px solid #BBB; |
| + padding: 5px; |
| + } |
| + |
| + .quarantined, .failed_task { |
| + background-color: #ffdddd; |
| + } |
| + .dead { |
| + background-color: #cccccc; |
| + } |
| + |
| + .message { |
| + white-space: pre-line; |
| + font-family: monospace; |
| + } |
| + |
| + .bot_state { |
| + white-space: pre; |
| + font-family: monospace; |
| + margin-bottom: 10px; |
| + } |
| + |
| + .show_all { |
| + margin-bottom: 5px; |
| + } |
| + |
| + paper-tabs { |
| + background-color: #1F78B4; |
| + color: #fff; |
| + max-width: 600px; |
| + margin-bottom: 5px; |
| + } |
| + paper-tab.iron-selected { |
| + background-color: #A6CEE3; |
| + border: 2px solid #1F78B4; |
| + color: #fff; |
| + font-weight: bold; |
| + text-decoration: underline; |
| + } |
| + |
| </style> |
| <url-param name="id" |
| value="{{bot_id}}"> |
| </url-param> |
| + <url-param name="show_all_events" |
| + value="{{_show_all}}"> |
| + </url-param> |
| + <url-param name="selected" |
| + value="{{_selected}}"> |
| + </url-param> |
| + <url-param name="show_state" |
| + value="{{_show_state}}"> |
| + </url-param> |
| <swarming-app |
| client_id="[[client_id]]" |
| auth_headers="{{_auth_headers}}" |
| + permissions="{{_permissions}}" |
| signed_in="{{_signed_in}}" |
| busy="[[_busy]]" |
| @@ -65,7 +151,169 @@ |
| tasks="{{_tasks}}"> |
| </bot-page-data> |
| - <h1> Bot Page Stub </h1> |
| + <div class="header horizontal layout"> |
| + <paper-input class="id_input" label="Bot id" value="{{bot_id}}"></paper-input> |
| + <paper-icon-button class="refresh" icon="icons:refresh"></paper-icon-button> |
| + </div> |
| + |
| + <div> |
| + <table> |
| + <tr class$="[[_isDead(_bot)]]"> |
| + <td>Last Seen</td> |
| + <td title="[[_bot.human_last_seen_ts]]"> |
| + [[_timeDiffExact(_bot.last_seen_ts)]] ago</td> |
| + <td> |
| + <!-- dom-ifs are slightly less performant than hidden$=, but |
| + prevent things from first drawing and then hiding. We prefer to |
| + not flash buttons or quarantined messages --> |
| + <template is="dom-if" if="[[_canShutdown(_bot,_permissions)]]"> |
| + <paper-button |
| + raised> |
| + Shut Down Gracefully |
| + </paper-button> |
| + </template> |
| + <template is="dom-if" if="[[_canDelete(_bot,_permissions)]]"> |
| + <paper-button |
| + raised> |
| + Delete Bot |
| + </paper-button> |
| + </template> |
| + </td> |
| + </tr> |
| + <template is="dom-if" if="[[_bot.quarantined]]"> |
| + <tr class="quarantined"> |
| + <td>Quarantined</td> |
| + <td colspan="2">[[_quarantineMessage(_bot)]]</td> |
| + </tr> |
| + </template> |
| + <tr> |
| + <td>Current Task</td> |
| + <td> |
| + <a target="_blank" href$="[[_taskLink(_bot.task_id)]]"> |
| + [[_task(_bot)]] |
| + </a> |
| + </td> |
| + <td> |
| + <!-- TODO(kjlubick) add the cancel button when swarming can |
| + cancel running tasks --> |
| + </td> |
| + </tr> |
| + <tr> |
| + <td rowspan$="[[_numRows(_bot.dimensions)]]">Dimensions</td> |
| + </tr> |
| + <template |
| + is="dom-repeat" |
| + items="[[_bot.dimensions]]" |
| + as="dim"> |
| + <tr> |
| + <td>[[dim.key]]</td> |
| + <td>[[_concat(dim.value)]]</td> |
| + </tr> |
| + </template> |
| + |
| + <tr> |
| + <td>External IP</td> |
| + <td>[[_bot.external_ip]]</td> |
| + <td></td> |
| + </tr> |
| + <tr> |
| + <td>Swarming Revision</td> |
| + <td> |
| + <a target="_blank" href$="[[_luciLink(_bot.version)]]">[[_shorten(_bot.version,'8')]]</a> |
| + </td> |
| + <td></td> |
| + </tr> |
| + <tr> |
| + <td>First seen</td> |
| + <td title="[[_bot.human_first_seen_ts]]"> |
| + [[_timeDiffApprox(_bot.first_seen_ts)]] ago |
| + </td> |
| + <td></td> |
| + </tr> |
| + <tr> |
| + <td>Authenticated as</td> |
| + <td>[[_bot.authenticated_as]]</td> |
| + <td></td> |
| + </tr> |
| + </table> |
| + |
| + <span class="title">State</span> |
| + <paper-icon-button |
| + hidden="[[_show_state]]" |
| + icon="icons:add-circle-outline" |
| + on-tap="_toggleState"> |
| + </paper-icon-button> |
| + <paper-icon-button |
| + hidden="[[_not(_show_state)]]" |
| + icon="icons:remove-circle-outline" |
| + on-tap="_toggleState"> |
| + </paper-icon-button> |
| + |
| + <iron-collapse id="collapse" opened="[[_show_state]]"> |
| + <div class="bot_state">[[_prettyPrint(_bot.state)]]</div> |
| + </iron-collapse> |
| + </div> |
| + |
| + <paper-tabs selected="{{_selected}}" no-bar> |
| + <paper-tab>Tasks</paper-tab> |
| + <paper-tab>Events</paper-tab> |
| + </paper-tabs> |
| + |
| + <template is="dom-if" if="[[_not(_selected)]]"> |
| + <table> |
| + <thead> |
| + <tr> |
| + <th>Task</th> |
| + <th>Started</th> |
| + <th>Duration</th> |
| + <th>Result</th> |
| + </tr> |
| + </thead> |
| + <tbody> |
| + <template is="dom-repeat" items="{{_tasks}}" as="task"> |
| + <tr> |
| + <td><a target="_blank" href$="[[_taskLink(task.task_id)]]">[[task.name]]</a></td> |
| + <td>[[task.human_started_ts]]</td> |
| + <td title="[[task.human_completed_ts]]">[[task.human_duration]]</td> |
| + <td>[[task.state]]</td> |
| + </tr> |
| + </template> |
| + </tbody> |
| + </table> |
| + </template> |
| + |
| + <template is="dom-if" if="[[_selected]]"> |
| + <div class="tab_content"> |
| + <paper-checkbox class="show_all" checked="{{_show_all}}"> |
| + Show all events |
| + </paper-checkbox> |
| + <table class="events_table"> |
| + <thead> |
| + <tr> |
| + <th>Message</th> |
| + <th>Type</th> |
| + <th>Timestamp</th> |
| + <th>Task ID</th> |
| + <th>Version</th> |
| + </tr> |
| + </thead> |
| + <tbody> |
| + <template is="dom-repeat" items="{{_eventList(_events,_show_all)}}" as="event"> |
| + <tr> |
| + <td class="message">[[event.message]]</a></td> |
| + <td>[[event.event_type]]</td> |
| + <td>[[event.human_ts]]</td> |
| + <td><a target="_blank" href$="[[_taskLink(event.task_id)]]">[[event.task_id]]</a></td> |
| + <td> |
| + <a target="_blank" href$="[[_luciLink(_bot.version)]]">[[_shorten(_bot.version,'8')]]</a> |
| + </td> |
| + </tr> |
| + </template> |
| + </tbody> |
| + </table> |
| + </div> |
| + </template> |
| + </div> |
| </div> |
| </swarming-app> |
| @@ -79,7 +327,7 @@ |
| is: 'bot-page', |
| behaviors: [ |
| - SwarmingBehaviors.CommonBehavior, |
| + SwarmingBehaviors.BotPageBehavior, |
| ], |
| properties: { |
| @@ -90,8 +338,112 @@ |
| type: String, |
| }, |
| + // private |
|
stephana
2016/09/01 15:16:18
nit: not necessary.
kjlubick
2016/09/01 17:24:08
Removed.
|
| + _bot: { |
| + type: Object, |
| + }, |
| + _selected: { |
| + type: Number, |
| + }, |
| + _show_all: { |
| + type: Boolean, |
| + }, |
| + _show_state: { |
| + type: Boolean, |
| + } |
| }, |
| + _canCancel: function(bot, permissions) { |
| + return bot && bot.task_id && permissions.cancel_task; |
| + }, |
| + |
| + _canDelete: function(bot, permissions) { |
| + return bot && bot.is_dead && permissions.delete_bot; |
| + }, |
| + |
| + _canShutdown: function(bot, permissions){ |
| + return bot && !bot.is_dead && permissions.terminate_bot; |
| + }, |
| + |
| + _concat: function(arr) { |
| + if (!arr) { |
| + return ""; |
| + } |
| + return arr.join(" | "); |
| + }, |
| + |
| + _eventList(events, showAll) { |
| + if (!events) { |
| + return []; |
| + } |
| + return events.filter(function(e){ |
| + return showAll || e.message; |
| + }); |
| + }, |
| + |
| + _isDead(bot){ |
| + if (bot && bot.is_dead) { |
| + return "dead"; |
| + } |
| + return ""; |
| + }, |
| + |
| + _luciLink: function(revision) { |
| + if (!revision) { |
| + return undefined; |
| + } |
| + return "https://github.com/luci/luci-py/commit/" + revision; |
| + |
| + }, |
| + |
| + _numRows: function(arr) { |
| + if (!arr || !arr.length) { |
| + return 1; |
| + } |
| + return 1 + arr.length; |
| + }, |
| + |
| + _prettyPrint: function(obj) { |
| + obj = obj || {}; |
| + return JSON.stringify(obj, null, 2); |
| + }, |
| + |
| + _quarantineMessage: function(bot) { |
| + if (bot && bot.quarantined) { |
| + var msg = bot.state.quarantined; |
| + // Sometimes, the quarantined message is actually in "error". This |
| + // happens when the bot code has thrown an exception. |
| + if (msg === undefined || msg === "true" || msg === true) { |
| + msg = this._attribute(bot, "error"); |
| + } |
| + return msg || "True"; |
| + } |
| + return ""; |
| + }, |
| + |
| + _shorten: function(str, length) { |
| + if (!str || ! length) { |
| + return ""; |
| + } |
| + return str.substring(0, length); |
| + }, |
| + |
| + _task: function(bot) { |
| + return (bot && bot.task_id) || "idle"; |
| + }, |
| + |
| + _taskLink: function(task_id) { |
| + // TODO(kjlubick): Migrate this to /newui/ when ready |
| + if (task_id) { |
| + return "/user/task/" + task_id; |
| + } |
| + return undefined; |
| + }, |
| + |
| + _toggleState: function() { |
| + this.set("_show_state", !this._show_state); |
| + } |
| + |
| }); |
| })(); |
| </script> |