Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(434)

Side by Side Diff: appengine/swarming/elements/build/elements.html

Issue 2204483002: Add UI to new botlist to show summary (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-py@bot-summary-api
Patch Set: Add docs Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « appengine/swarming/elements/Makefile ('k') | appengine/swarming/elements/build/js/common.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 <!DOCTYPE html><html><head><!-- 1 <!DOCTYPE html><html><head><!--
2 @license 2 @license
3 Copyright (c) 2016 The Polymer Project Authors. All rights reserved. 3 Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
4 This code may only be used under the BSD style license found at http://polymer.g ithub.io/LICENSE.txt 4 This code may only be used under the BSD style license found at http://polymer.g ithub.io/LICENSE.txt
5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6 The complete set of contributors may be found at http://polymer.github.io/CONTRI BUTORS.txt 6 The complete set of contributors may be found at http://polymer.github.io/CONTRI BUTORS.txt
7 Code distributed by Google as part of the polymer project is also 7 Code distributed by Google as part of the polymer project is also
8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN TS.txt 8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN TS.txt
9 --><!-- 9 --><!--
10 @license 10 @license
(...skipping 14478 matching lines...) Expand 10 before | Expand all | Expand 10 after
14489 clientId: { 14489 clientId: {
14490 type: String, 14490 type: String,
14491 }, 14491 },
14492 profile: { 14492 profile: {
14493 type: Object, 14493 type: Object,
14494 readOnly: true 14494 readOnly: true
14495 }, 14495 },
14496 signedIn: { 14496 signedIn: {
14497 type: Boolean, 14497 type: Boolean,
14498 readOnly: true, 14498 readOnly: true,
14499 value: false 14499 value: false,
14500 notify: true,
14500 } 14501 }
14501 }, 14502 },
14502 14503
14503 _onSignin: function(e) { 14504 _onSignin: function(e) {
14504 this._setSignedIn(true); 14505 this._setSignedIn(true);
14505 var user = gapi.auth2.getAuthInstance().currentUser.get(); 14506 var user = gapi.auth2.getAuthInstance().currentUser.get();
14506 var profile = user.getBasicProfile(); 14507 var profile = user.getBasicProfile();
14507 this._setProfile({ 14508 this._setProfile({
14508 email: profile.getEmail(), 14509 email: profile.getEmail(),
14509 imageUrl: profile.getImageUrl() 14510 imageUrl: profile.getImageUrl()
14510 }); 14511 });
14511 this.set("authResponse", user.getAuthResponse()); 14512 this.set("authResponse", user.getAuthResponse());
14513 this._setSignedIn(true);
14512 this.fire("auth-signin"); 14514 this.fire("auth-signin");
14513 }, 14515 },
14514 14516
14515 _onSignout: function(e) { 14517 _onSignout: function(e) {
14516 this._setSignedIn(false); 14518 this._setSignedIn(false);
14517 this._setProfile(null); 14519 this._setProfile(null);
14518 }, 14520 },
14519 14521
14520 _makeHeader: function(authResponse) { 14522 _makeHeader: function(authResponse) {
14521 if (!authResponse) { 14523 if (!authResponse) {
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
14575 <app-header-layout> 14577 <app-header-layout>
14576 <app-header fixed=""> 14578 <app-header fixed="">
14577 <app-toolbar> 14579 <app-toolbar>
14578 <div class="title left">[[name]]</div> 14580 <div class="title left">[[name]]</div>
14579 <paper-spinner-lite class="left" active="[[busy]]"></paper-spinner-lit e> 14581 <paper-spinner-lite class="left" active="[[busy]]"></paper-spinner-lit e>
14580 14582
14581 <a class="left" href="/newui/">Home</a> 14583 <a class="left" href="/newui/">Home</a>
14582 <a class="left" href="/newui/botlist">Bot List</a> 14584 <a class="left" href="/newui/botlist">Bot List</a>
14583 <div class="flex"></div> 14585 <div class="flex"></div>
14584 14586
14585 <auth-signin class="right" client-id="20770472288-t5smpbpjptka4nd888fv 0ctd23ftba2o.apps.googleusercontent.com" auth-headers="{{auth_headers}}"> 14587 <auth-signin class="right" client-id="20770472288-t5smpbpjptka4nd888fv 0ctd23ftba2o.apps.googleusercontent.com" auth-headers="{{auth_headers}}" signed- in="{{signed_in}}">
14586 </auth-signin> 14588 </auth-signin>
14587 </app-toolbar> 14589 </app-toolbar>
14588 </app-header> 14590 </app-header>
14589 <div class="main-content"> 14591 <div class="main-content">
14590 <content></content> 14592 <content></content>
14591 </div> 14593 </div>
14592 </app-header-layout> 14594 </app-header-layout>
14593 14595
14594 </template> 14596 </template>
14595 <script> 14597 <script>
14596 Polymer({ 14598 Polymer({
14597 is: 'swarming-app', 14599 is: 'swarming-app',
14598 properties: { 14600 properties: {
14599 auth_headers: { 14601 auth_headers: {
14600 type: Object, 14602 type: Object,
14601 notify: true, 14603 notify: true,
14602 }, 14604 },
14605 signed_in: {
14606 type: Boolean,
14607 value: false,
14608 notify:true,
14609 },
14610
14603 busy: { 14611 busy: {
14604 type: Boolean, 14612 type: Boolean,
14605 }, 14613 },
14606 name: { 14614 name: {
14607 type: String, 14615 type: String,
14608 }, 14616 },
14609 }, 14617 },
14610 14618
14611 }); 14619 });
14612 </script> 14620 </script>
(...skipping 962 matching lines...) Expand 10 before | Expand all | Expand 10 after
15575 <iron-icon style="top:0" class$="[[_hidden(direction,'asc')]]" icon="icons :arrow-drop-down"> 15583 <iron-icon style="top:0" class$="[[_hidden(direction,'asc')]]" icon="icons :arrow-drop-down">
15576 </iron-icon> 15584 </iron-icon>
15577 <iron-icon style="bottom:0" class$="[[_hidden(direction,'desc')]]" icon="i cons:arrow-drop-up"> 15585 <iron-icon style="bottom:0" class$="[[_hidden(direction,'desc')]]" icon="i cons:arrow-drop-up">
15578 </iron-icon> 15586 </iron-icon>
15579 </span> 15587 </span>
15580 15588
15581 </template> 15589 </template>
15582 <script> 15590 <script>
15583 Polymer({ 15591 Polymer({
15584 is: "sort-toggle", 15592 is: "sort-toggle",
15593
15585 properties: { 15594 properties: {
15586 current: { 15595 current: {
15587 type: Object, 15596 type: Object,
15588 observer: "_resetSort", 15597 observer: "_resetSort",
15589 }, 15598 },
15590 name: { 15599 name: {
15591 type: String, 15600 type: String,
15592 observer: "_resetSort", 15601 observer: "_resetSort",
15593 }, 15602 },
15594 15603
(...skipping 24 matching lines...) Expand all
15619 // Because of how Polymer inserts and moves elements around, we need to 15628 // Because of how Polymer inserts and moves elements around, we need to
15620 // update the direction value if the name changes so the ascending sort 15629 // update the direction value if the name changes so the ascending sort
15621 // by "os" doesn't become the ascending sort by "gpu" if a column gets 15630 // by "os" doesn't become the ascending sort by "gpu" if a column gets
15622 // added before "os", for example. Additionally, this makes sure that 15631 // added before "os", for example. Additionally, this makes sure that
15623 // only one sort-toggle is active at a given time. 15632 // only one sort-toggle is active at a given time.
15624 if (this.current && this.current.name === this.name) { 15633 if (this.current && this.current.name === this.name) {
15625 this.set("direction", this.current.direction); 15634 this.set("direction", this.current.direction);
15626 } else { 15635 } else {
15627 this.set("direction", ""); 15636 this.set("direction", "");
15628 } 15637 }
15629
15630 }, 15638 },
15631 }); 15639 });
15632 </script> 15640 </script>
15633 </dom-module><script> 15641 </dom-module><script>
15634 15642
15635 /** 15643 /**
15636 * @param {!Function} selectCallback 15644 * @param {!Function} selectCallback
15637 * @constructor 15645 * @constructor
15638 */ 15646 */
15639 Polymer.IronSelection = function(selectCallback) { 15647 Polymer.IronSelection = function(selectCallback) {
(...skipping 5272 matching lines...) Expand 10 before | Expand all | Expand 10 after
20912 // Since they will be or'd together, order doesn't matter. 20920 // Since they will be or'd together, order doesn't matter.
20913 var filterGroups = {}; 20921 var filterGroups = {};
20914 this._filters.forEach(function(filterString){ 20922 this._filters.forEach(function(filterString){
20915 var idx = filterString.indexOf(FILTER_SEP); 20923 var idx = filterString.indexOf(FILTER_SEP);
20916 var primary = filterString.slice(0, idx); 20924 var primary = filterString.slice(0, idx);
20917 var param = filterString.slice(idx + FILTER_SEP.length); 20925 var param = filterString.slice(idx + FILTER_SEP.length);
20918 var arr = filterGroups[primary] || []; 20926 var arr = filterGroups[primary] || [];
20919 arr.push(param); 20927 arr.push(param);
20920 filterGroups[primary] = arr; 20928 filterGroups[primary] = arr;
20921 }); 20929 });
20922 return { 20930 return function(bot){
20923 filter: function(bot){ 20931 var retVal = true;
20924 var retVal = true; 20932 // Look up all the primary keys we are filter by, then look up how
20925 // Look up all the primary keys we are filter by, then look up how 20933 // to filter (in filterMap) and apply the filter for each filter
20926 // to filter (in filterMap) and apply the filter for each filter 20934 // option.
20927 // option. 20935 for (primary in filterGroups){
20928 for (primary in filterGroups){ 20936 var params = filterGroups[primary];
20929 var params = filterGroups[primary]; 20937 var filter = filterMap[primary];
20930 var filter = filterMap[primary]; 20938 var groupResult = false;
20931 var groupResult = false; 20939 if (filter) {
20932 if (filter) { 20940 params.forEach(function(param){
20933 params.forEach(function(param){ 20941 groupResult = groupResult || filter.bind(this)(bot,param);
20934 groupResult = groupResult || filter.bind(this)(bot,param); 20942 }.bind(this));
20935 }.bind(this));
20936 }
20937 retVal = retVal && groupResult;
20938 } 20943 }
20939 return retVal; 20944 retVal = retVal && groupResult;
20940 } 20945 }
20946 return retVal;
20941 } 20947 }
20942 }, 20948 },
20943 20949
20944 _primary: function(query, primary_map, primary_arr) { 20950 _primary: function(query, primary_map, primary_arr) {
20945 // If the user has typed in a query, only show those primary keys that 20951 // If the user has typed in a query, only show those primary keys that
20946 // partially match the query or that have secondary values which 20952 // partially match the query or that have secondary values which
20947 // partially match. 20953 // partially match.
20948 var arr = this.primary_arr.filter(function(s){ 20954 var arr = this.primary_arr.filter(function(s){
20949 if (matchPartCaseInsensitive(s, query).idx !== -1) { 20955 if (matchPartCaseInsensitive(s, query).idx !== -1) {
20950 return true; 20956 return true;
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after
21144 21150
21145 _gpuAlias: function(gpu) { 21151 _gpuAlias: function(gpu) {
21146 var a = GPU_ALIASES[gpu]; 21152 var a = GPU_ALIASES[gpu];
21147 if (!a) { 21153 if (!a) {
21148 return "UNKNOWN"; 21154 return "UNKNOWN";
21149 } 21155 }
21150 return a; 21156 return a;
21151 }, 21157 },
21152 21158
21153 _not: function(a) { 21159 _not: function(a) {
21154 return a; 21160 return !a;
21161 },
21162
21163 _or: function() {
21164 var result = false;
21165 // can't use .foreach, as arguments isn't really a function.
21166 for (var i = 0; i < arguments.length; i++) {
21167 result = result || arguments[i];
21168 }
21169 return result;
21155 }, 21170 },
21156 21171
21157 _taskId: function(bot) { 21172 _taskId: function(bot) {
21158 if (bot && bot.task_id) { 21173 if (bot && bot.task_id) {
21159 return bot.task_id; 21174 return bot.task_id;
21160 } 21175 }
21161 return "idle"; 21176 return "idle";
21162 }, 21177 },
21163 21178
21164 // _unalias will return the base dimension/state with its alias removed 21179 // _unalias will return the base dimension/state with its alias removed
21165 // if it had one. This is handy for sorting and filtering. 21180 // if it had one. This is handy for sorting and filtering.
21166 _unalias: function(str) { 21181 _unalias: function(str) {
21167 var match = ALIAS_REGEXP.exec(str); 21182 var match = ALIAS_REGEXP.exec(str);
21168 if (match) { 21183 if (match) {
21169 return match[1]; 21184 return match[1];
21170 } 21185 }
21171 return str; 21186 return str;
21172 }, 21187 },
21173 } 21188 }
21174 })() 21189 })()
21175 </script> 21190 </script>
21176 <dom-module id="bot-list-data" assetpath="/res/imp/botlist/"> 21191 <dom-module id="bot-list-data" assetpath="/res/imp/botlist/">
21177 <template> 21192 <template>
21178 <iron-ajax id="request" url="/_ah/api/swarming/v1/bots/list" headers="[[auth _headers]]" handle-as="json" last-response="{{_data}}" loading="{{busy}}"> 21193 <iron-ajax id="botlist" url="/_ah/api/swarming/v1/bots/list" headers="[[auth _headers]]" handle-as="json" last-response="{{_list}}" loading="{{_busy1}}">
21194 </iron-ajax>
21195
21196 <iron-ajax id="fleet" url="/_ah/api/swarming/v1/bots/count" headers="[[auth_ headers]]" handle-as="json" last-response="{{_count}}" loading="{{_busy2}}">
21179 </iron-ajax> 21197 </iron-ajax>
21180 </template> 21198 </template>
21181 <script> 21199 <script>
21182 (function(){ 21200 (function(){
21183 // TODO(kjlubick): Add more of these as well as things from state 21201 // TODO(kjlubick): Add more of these as well as things from state
21184 // i.e. disk space remaining. 21202 // i.e. disk space remaining.
21185 var DIMENSIONS = ["cores", "cpu", "id", "os", "pool"]; 21203 var DIMENSIONS = ["cores", "cpu", "id", "os", "pool"];
21186 // "gpu" and "devices" are added separately because we need to 21204 // "gpu" and "devices" are added separately because we need to
21187 // deal with aliases. 21205 // deal with aliases.
21188 var BOT_PROPERTIES = ["gpu", "devices", "task", "status"]; 21206 var BOT_PROPERTIES = ["gpu", "devices", "task", "status"];
21189 Polymer({ 21207 Polymer({
21190 is: 'bot-list-data', 21208 is: 'bot-list-data',
21209
21210 behaviors: [SwarmingBehaviors.BotListBehavior],
21211
21191 properties: { 21212 properties: {
21192 // inputs 21213 // inputs
21193 auth_headers: { 21214 auth_headers: {
21194 type: Object, 21215 type: Object,
21195 observer: "signIn", 21216 observer: "signIn",
21196 }, 21217 },
21197 21218
21198 //outputs 21219 //outputs
21199 bots: { 21220 bots: {
21200 type: Array, 21221 type: Array,
21201 computed: "_bots(_data)", 21222 computed: "_bots(_list)",
21202 notify: true, 21223 notify: true,
21203 }, 21224 },
21204 busy: { 21225 busy: {
21205 type: Boolean, 21226 type: Boolean,
21227 computed: "_or(_busy1,_busy2)",
21228 notify: true,
21229 },
21230 fleet: {
21231 type: Object,
21232 computed: "_fleet(_count)",
21206 notify: true, 21233 notify: true,
21207 }, 21234 },
21208 primary_map: { 21235 primary_map: {
21209 type:Object, 21236 type:Object,
21210 computed: "_primaryMap(bots)", 21237 computed: "_primaryMap(bots)",
21211 notify: true, 21238 notify: true,
21212 }, 21239 },
21213 primary_arr: { 21240 primary_arr: {
21214 type:Array, 21241 type:Array,
21215 value: function() { 21242 value: function() {
21216 return DIMENSIONS.concat(BOT_PROPERTIES); 21243 return DIMENSIONS.concat(BOT_PROPERTIES);
21217 }, 21244 },
21218 notify: true, 21245 notify: true,
21219 }, 21246 },
21220 21247
21221 // private 21248 // private
21222 _data: { 21249 _count: {
21250 type: Object,
21251 },
21252 _list: {
21223 type: Object, 21253 type: Object,
21224 }, 21254 },
21225 }, 21255 },
21226 behaviors: [SwarmingBehaviors.BotListBehavior],
21227 21256
21228 signIn: function(){ 21257 signIn: function(){
21229 this.$.request.generateRequest(); 21258 this.$.botlist.generateRequest();
21259 this.$.fleet.generateRequest();
21230 }, 21260 },
21231 21261
21232 _bots: function(){ 21262 _bots: function(){
21233 if (!this._data || !this._data.items) { 21263 if (!this._list || !this._list.items) {
21234 return []; 21264 return [];
21235 } 21265 }
21236 this._data.items.forEach(function(o){ 21266 this._list.items.forEach(function(o){
21237 o.state = JSON.parse(o.state); 21267 o.state = JSON.parse(o.state);
21238 }); 21268 });
21239 return this._data.items; 21269 return this._list.items;
21270 },
21271
21272 _fleet: function() {
21273 if (!this._count) {
21274 return {};
21275 }
21276 return {
21277 alive: this._count.count || -1,
21278 busy: this._count.busy || -1,
21279 idle: this._count.count && this._count.busy &&
21280 this._count.count - this._count.busy,
21281 dead: this._count.dead || -1,
21282 quarantined: this._count.quarantined || -1,
21283 }
21240 }, 21284 },
21241 21285
21242 _primaryMap: function(bots){ 21286 _primaryMap: function(bots){
21243 // map will keep track of dimensions that we have seen at least once. 21287 // map will keep track of dimensions that we have seen at least once.
21244 // This will then basically get turned into an array to be used for 21288 // This will then basically get turned into an array to be used for
21245 // filtering. 21289 // filtering.
21246 var map = {}; 21290 var map = {};
21247 DIMENSIONS.forEach(function(p){ 21291 DIMENSIONS.forEach(function(p){
21248 map[p] = {}; 21292 map[p] = {};
21249 }); 21293 });
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
21290 21334
21291 // Create custom filter options 21335 // Create custom filter options
21292 pMap["task"] = ["busy", "idle"]; 21336 pMap["task"] = ["busy", "idle"];
21293 pMap["status"] = ["available", "dead", "quarantined"]; 21337 pMap["status"] = ["available", "dead", "quarantined"];
21294 return pMap; 21338 return pMap;
21295 }, 21339 },
21296 21340
21297 }); 21341 });
21298 })(); 21342 })();
21299 </script> 21343 </script>
21344 </dom-module><dom-module id="bot-list-summary" assetpath="/res/imp/botlist/">
21345 <template>
21346 <style include="swarming-app-style">
21347 :host {
21348 display: block;
21349 border-left: 1px solid black;
21350 padding: 5px 5px;
21351 font-family: sans-serif;
21352 }
21353 .header {
21354 font-size: 1.2em;
21355 font-weight: bold;
21356 }
21357 .right {
21358 text-align: right;
21359 }
21360 .left {
21361 text-align: left;
21362 }
21363 </style>
21364
21365 <div class="header">Fleet</div>
21366
21367 <table>
21368 <tbody><tr>
21369 <td class="right"><a href="/newui/botlist?alive">Alive</a>:</td>
21370 <td class="left">[[fleet.alive]]</td>
21371 </tr>
21372 <tr>
21373 <td class="right"><a href="/newui/botlist?busy">Busy</a>:</td>
21374 <td class="left">[[fleet.busy]]</td>
21375 </tr>
21376 <tr>
21377 <td class="right"><a href="/newui/botlist?idle">Idle</a>:</td>
21378 <td class="left">[[fleet.idle]]</td>
21379 </tr>
21380 <tr>
21381 <td class="right"><a href="/newui/botlist?dead">Dead</a>:</td>
21382 <td class="left">[[fleet.dead]]</td>
21383 </tr>
21384 <tr>
21385 <td class="right"><a href="/newui/botlist?quaren">Quarantined</a>:</td>
21386 <td class="left">[[fleet.quarantined]]</td>
21387 </tr>
21388 </tbody></table>
21389
21390 <div class="header">Displayed</div>
21391 <table>
21392 <tbody><tr>
21393 <td class="right"><a href="/newui/botlist?alive2">Alive</a>:</td>
21394 <td class="left">[[_currently_showing.alive]]</td>
21395 </tr>
21396 <tr>
21397 <td class="right"><a href="/newui/botlist?busy2">Busy</a>:</td>
21398 <td class="left">[[_currently_showing.busy]]</td>
21399 </tr>
21400 <tr>
21401 <td class="right"><a href="/newui/botlist?idle2">Idle</a>:</td>
21402 <td class="left">[[_currently_showing.idle]]</td>
21403 </tr>
21404 <tr>
21405 <td class="right"><a href="/newui/botlist?dead2">Dead</a>:</td>
21406 <td class="left">[[_currently_showing.dead]]</td>
21407 </tr>
21408 <tr>
21409 <td class="right"><a href="/newui/botlist?quaren2">Quarantined</a>:</td>
21410 <td class="left">[[_currently_showing.quarantined]]</td>
21411 </tr>
21412 </tbody></table>
21413
21414 </template>
21415 <script>
21416 Polymer({
21417 is: 'bot-list-summary',
21418
21419 behaviors: [SwarmingBehaviors.BotListBehavior],
21420
21421 properties: {
21422 filtered_bots: {
21423 type: Array,
21424 },
21425 fleet: {
21426 type: Object,
21427 },
21428
21429 _currently_showing: {
21430 type: Object,
21431 value: function() {
21432 return {
21433 alive: -1,
21434 busy: -1,
21435 idle: -1,
21436 dead: -1,
21437 quarantined: -1,
21438 };
21439 },
21440 },
21441 },
21442
21443 // Do this because Array changes in Polymer don't always trigger normal
21444 // property observers
21445 observers: ["_recount(filtered_bots.*)"],
21446
21447 _recount: function() {
21448 var curr = {
21449 alive: 0,
21450 busy: 0,
21451 idle: 0,
21452 dead: 0,
21453 quarantined: 0,
21454 };
21455 if (!this.filtered_bots) {
21456 return curr;
21457 }
21458 this.filtered_bots.forEach(function(bot) {
21459 if (this._taskId(bot) === "idle") {
21460 curr.idle++;
21461 } else {
21462 curr.busy++;
21463 }
21464 if (bot.quarantined) {
21465 curr.quarantined++;
21466 }
21467 if (bot.is_dead) {
21468 curr.dead++;
21469 } else {
21470 curr.alive++;
21471 }
21472 }.bind(this));
21473 this.set("_currently_showing", curr);
21474 }
21475 });
21476 </script>
21300 </dom-module><dom-module id="bot-list" assetpath="/res/imp/botlist/"> 21477 </dom-module><dom-module id="bot-list" assetpath="/res/imp/botlist/">
21301 <template> 21478 <template>
21302 <style include="iron-flex iron-flex-alignment iron-positioning swarming-app- style"> 21479 <style include="iron-flex iron-flex-alignment iron-positioning swarming-app- style">
21303 bot-filters { 21480 bot-filters, bot-list-summary {
21304 margin-bottom: 5px; 21481 margin-bottom: 8px;
21482 margin-right: 10px;
21305 } 21483 }
21306 .bot { 21484 .bot {
21307 margin:5px; 21485 margin:5px;
21308 max-width:400px; 21486 max-width:400px;
21309 min-height:100px; 21487 min-height:100px;
21310 min-width:300px; 21488 min-width:300px;
21311 } 21489 }
21312 table { 21490 table {
21313 border-collapse: collapse; 21491 border-collapse: collapse;
21314 margin-left: 5px; 21492 margin-left: 5px;
(...skipping 17 matching lines...) Expand all
21332 position: absolute; 21510 position: absolute;
21333 right: 0; 21511 right: 0;
21334 top: 0.4em; 21512 top: 0.4em;
21335 } 21513 }
21336 .bot-list th > span { 21514 .bot-list th > span {
21337 /* Leave space for sort-toggle*/ 21515 /* Leave space for sort-toggle*/
21338 padding-right: 30px; 21516 padding-right: 30px;
21339 } 21517 }
21340 </style> 21518 </style>
21341 21519
21342 <swarming-app auth_headers="{{auth_headers}}" busy="[[busy]]" name="Swarming Bot List"> 21520 <swarming-app auth_headers="{{_auth_headers}}" signed_in="{{_signed_in}}" bu sy="[[_busy]]" name="Swarming Bot List">
21343 21521
21344 <bot-filters primary_map="[[primary_map]]" primary_arr="[[primary_arr]]" c olumns="{{columns}}" filter="{{filter}}" verbose="{{verbose}}"> 21522 <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2>
21345 </bot-filters>
21346 21523
21347 <bot-list-data auth_headers="[[auth_headers]]" bots="{{bots}}" busy="{{bus y}}" primary_map="{{primary_map}}" primary_arr="{{primary_arr}}"> 21524 <div hidden$="[[_not(_signed_in)]]">
21348 </bot-list-data>
21349 21525
21350 <table class="bot-list"> 21526 <div class="horizontal layout">
21351 <thead on-sort_change="sortChange"> 21527
21352 21528 <bot-filters primary_map="[[_primary_map]]" primary_arr="[[_primary_ar r]]" columns="{{_columns}}" filter="{{_filter}}" verbose="{{_verbose}}">
21353 <tr><th> 21529 </bot-filters>
21354 <span>Bot Id</span> 21530
21355 <sort-toggle name="id" current="[[sort]]"> 21531 <bot-list-summary fleet="[[_fleet]]" filtered_bots="[[_filteredSortedB ots]]">
21356 </sort-toggle> 21532 </bot-list-summary>
21357 </th> 21533
21534 </div>
21535
21536 <bot-list-data auth_headers="[[_auth_headers]]" bots="{{_bots}}" busy="{ {_busy}}" fleet="{{_fleet}}" primary_map="{{_primary_map}}" primary_arr="{{_prim ary_arr}}">
21537 </bot-list-data>
21538
21539 <table class="bot-list">
21540 <thead on-sort_change="_sortChange">
21358 21541
21359 <th hidden$="[[_hide('task', columns.*)]]"> 21542 <tr>
21360 <span>Current Task</span> 21543 <th>
21361 <sort-toggle name="task" current="[[sort]]"> 21544 <span>Bot Id</span>
21362 </sort-toggle> 21545 <sort-toggle name="id" current="[[_sort]]">
21363 </th> 21546 </sort-toggle>
21547 </th>
21548
21549 <th hidden$="[[_hide('task', _columns.*)]]">
21550 <span>Current Task</span>
21551 <sort-toggle name="task" current="[[_sort]]">
21552 </sort-toggle>
21553 </th>
21364 21554
21365 <template is="dom-repeat" items="[[plain_columns]]" as="c"> 21555 <template is="dom-repeat" items="[[_plain_columns]]" as="c">
21366 <th hidden$="[[_hide(c)]]"> 21556 <th hidden$="[[_hide(c)]]">
21367 <span>[[_header(c)]]</span> 21557 <span>[[_header(c)]]</span>
21368 <sort-toggle name="[[c]]" current="[[sort]]"> 21558 <sort-toggle name="[[c]]" current="[[_sort]]">
21369 </sort-toggle> 21559 </sort-toggle>
21370 </th> 21560 </th>
21371 </template> 21561 </template>
21372 </tr></thead> 21562 </tr>
21373 <tbody> 21563 </thead>
21374 <template id="bot_table" is="dom-repeat" items="[[bots]]" as="bot" ini tial-count="50" filter="_filterBotTable"> 21564 <tbody>
21565 <template id="bot_table" is="dom-repeat" items="[[_filteredSortedBot s]]" as="bot" initial-count="50">
21375 21566
21376 <tr class$="[[_botClass(bot)]]"> 21567 <tr class$="[[_botClass(bot)]]">
21377 <td> 21568 <td>
21378 <a class="center" href$="[[_botLink(bot.bot_id)]]" target="_blan k"> 21569 <a class="center" href$="[[_botLink(bot.bot_id)]]" target="_bl ank">
21379 [[bot.bot_id]] 21570 [[bot.bot_id]]
21380 </a> 21571 </a>
21381 </td> 21572 </td>
21382 <td hidden$="[[_hide('task', columns.*)]]"> 21573 <td hidden$="[[_hide('task', _columns.*)]]">
21383 <a href$="[[_taskLink(bot)]]">[[_taskId(bot)]]</a> 21574 <a href$="[[_taskLink(bot)]]">[[_taskId(bot)]]</a>
21384 </td> 21575 </td>
21385 21576
21386 <template is="dom-repeat" items="[[plain_columns]]" as="c"> 21577 <template is="dom-repeat" items="[[_plain_columns]]" as="c">
21387 <td hidden$="[[_hide(c)]]">
21388 [[_column(c, bot, verbose)]]
21389 </td>
21390 </template>
21391
21392 </tr>
21393 <template is="dom-repeat" items="[[_devices(bot)]]" as="device">
21394 <tr hidden$="[[_hide('devices', columns.*)]]" class$="[[_deviceCla ss(device)]]">
21395 <td></td>
21396 <td hidden$="[[_hide('task', columns.*)]]"></td>
21397 <template is="dom-repeat" items="[[plain_columns]]" as="c">
21398 <td hidden$="[[_hide(c)]]"> 21578 <td hidden$="[[_hide(c)]]">
21399 [[_deviceColumn(c, device, verbose)]] 21579 [[_column(c, bot, _verbose)]]
21400 </td> 21580 </td>
21401 </template> 21581 </template>
21582
21402 </tr> 21583 </tr>
21584 <template is="dom-repeat" items="[[_devices(bot)]]" as="device">
21585 <tr hidden$="[[_hide('devices', _columns.*)]]" class$="[[_device Class(device)]]">
21586 <td></td>
21587 <td hidden$="[[_hide('task', _columns.*)]]"></td>
21588 <template is="dom-repeat" items="[[_plain_columns]]" as="c">
21589 <td hidden$="[[_hide(c)]]">
21590 [[_deviceColumn(c, device, _verbose)]]
21591 </td>
21592 </template>
21593 </tr>
21594 </template>
21403 </template> 21595 </template>
21404 </template> 21596 </tbody>
21405 </tbody> 21597 </table>
21406 </table> 21598 </div>
21407 21599
21408 </swarming-app> 21600 </swarming-app>
21409 21601
21410 </template> 21602 </template>
21411 <script> 21603 <script>
21412 (function(){ 21604 (function(){
21413 var special_columns = ["id", "task"]; 21605 var special_columns = ["id", "task"];
21414 21606
21415 var headerMap = { 21607 var headerMap = {
21416 // "id" and "task" are special, so they don't go here and have their 21608 // "id" and "task" are special, so they don't go here and have their
21417 // headers hard-coded below. 21609 // headers hard-coded below.
21418 "cores": "Cores", 21610 "cores": "Cores",
21419 "cpu": "CPU", 21611 "cpu": "CPU",
21420 "devices": "Devices", 21612 "devices": "Devices",
21421 "gpu": "GPU", 21613 "gpu": "GPU",
21422 "os": "OS", 21614 "os": "OS",
21423 "pool": "Pool", 21615 "pool": "Pool",
21424 "status": "Status", 21616 "status": "Status",
21425 }; 21617 };
21426 21618
21427 // This maps column name to a function that will return the content for a 21619 // This maps column name to a function that will return the content for a
21428 // given bot. These functions are bound to this element, and have access 21620 // given bot. These functions are bound to this element, and have access
21429 // to all functions defined here and in bot-list-shared. 21621 // to all functions defined here and in bot-list-shared.
21430 var columnMap = { 21622 var columnMap = {
21431 cores: function(bot){ 21623 cores: function(bot){
21432 var cores = this._cores(bot); 21624 var cores = this._cores(bot);
21433 if (this.verbose){ 21625 if (this._verbose){
21434 return cores.join(" | "); 21626 return cores.join(" | ");
21435 } 21627 }
21436 return cores[0]; 21628 return cores[0];
21437 }, 21629 },
21438 cpu: function(bot){ 21630 cpu: function(bot){
21439 var cpus = this._dimension(bot, 'cpu') || ['Unknown']; 21631 var cpus = this._dimension(bot, 'cpu') || ['Unknown'];
21440 if (this.verbose){ 21632 if (this._verbose){
21441 return cpus.join(" | "); 21633 return cpus.join(" | ");
21442 } 21634 }
21443 return cpus[0]; 21635 return cpus[0];
21444 }, 21636 },
21445 devices: function(bot){ 21637 devices: function(bot){
21446 return this._devices(bot).length + " devices attached"; 21638 return this._devices(bot).length + " devices attached";
21447 }, 21639 },
21448 gpu: function(bot){ 21640 gpu: function(bot){
21449 var gpus = this._dimension(bot, 'gpu') 21641 var gpus = this._dimension(bot, 'gpu')
21450 if (!gpus) { 21642 if (!gpus) {
(...skipping 10 matching lines...) Expand all
21461 if (g.indexOf(":") === -1) { 21653 if (g.indexOf(":") === -1) {
21462 named.push(g); 21654 named.push(g);
21463 } 21655 }
21464 return; 21656 return;
21465 } 21657 }
21466 verbose.push(this._applyAlias(g, alias)); 21658 verbose.push(this._applyAlias(g, alias));
21467 if (g.indexOf(":") === -1) { 21659 if (g.indexOf(":") === -1) {
21468 named.push(this._applyAlias(g, alias)); 21660 named.push(this._applyAlias(g, alias));
21469 } 21661 }
21470 }.bind(this)) 21662 }.bind(this))
21471 if (this.verbose) { 21663 if (this._verbose) {
21472 return verbose.join(" | "); 21664 return verbose.join(" | ");
21473 } 21665 }
21474 return named.join(" | "); 21666 return named.join(" | ");
21475 }, 21667 },
21476 id: function(bot) { 21668 id: function(bot) {
21477 return bot.bot_id; 21669 return bot.bot_id;
21478 }, 21670 },
21479 os: function(bot) { 21671 os: function(bot) {
21480 var os = this._dimension(bot, 'os') || ['Unknown']; 21672 var os = this._dimension(bot, 'os') || ['Unknown'];
21481 if (this.verbose){ 21673 if (this._verbose){
21482 return os.join(" | "); 21674 return os.join(" | ");
21483 } 21675 }
21484 return os[0]; 21676 return os[0];
21485 }, 21677 },
21486 pool: function(bot) { 21678 pool: function(bot) {
21487 var pool = this._dimension(bot, 'pool') || ['Unknown']; 21679 var pool = this._dimension(bot, 'pool') || ['Unknown'];
21488 return pool.join(" | "); 21680 return pool.join(" | ");
21489 }, 21681 },
21490 status: function(bot) { 21682 status: function(bot) {
21491 // If a bot is both dead and quarantined, show the deadness over the 21683 // If a bot is both dead and quarantined, show the deadness over the
(...skipping 10 matching lines...) Expand all
21502 return this._taskId(bot); 21694 return this._taskId(bot);
21503 }, 21695 },
21504 }; 21696 };
21505 21697
21506 Polymer({ 21698 Polymer({
21507 is: 'bot-list', 21699 is: 'bot-list',
21508 behaviors: [SwarmingBehaviors.BotListBehavior], 21700 behaviors: [SwarmingBehaviors.BotListBehavior],
21509 21701
21510 properties: { 21702 properties: {
21511 21703
21512 columns: { 21704 _bots: {
21513 type: Array, 21705 type: Array,
21514 }, 21706 },
21515 // Should have a property "filter" which is a function. 21707
21516 filter: { 21708 _columns: {
21517 type: Object, 21709 type: Array,
21518 }, 21710 },
21519 21711
21520 plain_columns: { 21712 _filter: {
21521 type: Array, 21713 type: Function,
21522 computed: "_stripSpecial(columns.*)", 21714 value: function() {
21715 return true;
21716 },
21523 }, 21717 },
21524 21718
21525 // sort is an Object {name:String, direction:String}. 21719 _filteredSortedBots: {
21526 sort: { 21720 type: Array,
21527 type: Object, 21721 computed: "_filterAndSort(_bots,_filter.*,_sort.*)"
21528 }, 21722 },
21529 21723
21530 verbose: { 21724 _plain_columns: {
21725 type: Array,
21726 computed: "_stripSpecial(_columns.*)",
21727 },
21728
21729 // _sort is an Object {name:String, direction:String}.
21730 _sort: {
21731 type: Object,
21732 value: function() {
21733 return {
21734 name: "id",
21735 direction: "asc",
21736 };
21737 }
21738 },
21739
21740 _verbose: {
21531 type: Boolean, 21741 type: Boolean,
21532 } 21742 }
21533 }, 21743 },
21534 21744
21535 observers: [
21536 '_reRender(filter.*)',
21537 '_checkSorts(columns.*)'
21538 ],
21539
21540 _botClass: function(bot) { 21745 _botClass: function(bot) {
21541 if (bot.is_dead) { 21746 if (bot.is_dead) {
21542 return "dead"; 21747 return "dead";
21543 } 21748 }
21544 if (bot.quarantined) { 21749 if (bot.quarantined) {
21545 return "quarantined"; 21750 return "quarantined";
21546 } 21751 }
21547 return ""; 21752 return "";
21548 }, 21753 },
21549 21754
21550 _botLink: function(id) { 21755 _botLink: function(id) {
21551 // TODO(kjlubick) Make this point to /newui/ when appropriate. 21756 // TODO(kjlubick) Make this point to /newui/ when appropriate.
21552 return "/restricted/bot/"+id; 21757 return "/restricted/bot/"+id;
21553 }, 21758 },
21554 21759
21555 // _checkSorts makes sure that if a column has been removed, the related
21556 // sort is also removed.
21557 _checkSorts: function() {
21558 if (!this.sort) {
21559 return;
21560 }
21561 this._reRender();
21562 },
21563 21760
21564 _column: function(col, bot) { 21761 _column: function(col, bot) {
21565 return columnMap[col].bind(this)(bot); 21762 return columnMap[col].bind(this)(bot);
21566 }, 21763 },
21567 21764
21568 _deviceColumn: function(col, device) { 21765 _deviceColumn: function(col, device) {
21569 if (col === "devices") { 21766 if (col === "devices") {
21570 var str = this._androidAlias(device); 21767 var str = this._androidAlias(device);
21571 if (device.okay) { 21768 if (device.okay) {
21572 str = this._applyAlias(this._deviceType(device), str); 21769 str = this._applyAlias(this._deviceType(device), str);
21573 } 21770 }
21574 str += " S/N:"; 21771 str += " S/N:";
21575 str += device.serial; 21772 str += device.serial;
21576 return str; 21773 return str;
21577 } 21774 }
21578 if (col === "status") { 21775 if (col === "status") {
21579 return device.state; 21776 return device.state;
21580 } 21777 }
21581 return ""; 21778 return "";
21582 }, 21779 },
21583 21780
21584 _deviceClass: function(device) { 21781 _deviceClass: function(device) {
21585 if (!device.okay) { 21782 if (!device.okay) {
21586 return "bad-device"; 21783 return "bad-device";
21587 } 21784 }
21588 return ""; 21785 return "";
21589 }, 21786 },
21590 21787
21591 _filterBotTable: function(bot) { 21788 _filterAndSort: function(a,b,c) {
21592 if (!this.filter || !this.filter.filter) { 21789 // We intentionally sort this._bots (and not a copy) to allow users to
21593 return true; 21790 // "chain" sorts, that is, sort by one thing and then another, and
21791 // have both orderings properly impact the list.
21792 swarming.stableSort(this._bots, this._sortBotTable.bind(this));
21793 var bots = this._bots;
21794 if (this._filter) {
21795 bots = bots.filter(this._filter.bind(this));
21594 } 21796 }
21595 return this.filter.filter.bind(this)(bot); 21797
21798 return bots;
21596 }, 21799 },
21597 21800
21598 _header: function(col){ 21801 _header: function(col){
21599 return headerMap[col]; 21802 return headerMap[col];
21600 }, 21803 },
21601 21804
21602 _hide: function(col) { 21805 _hide: function(col) {
21603 return this.columns.indexOf(col) === -1; 21806 return this._columns.indexOf(col) === -1;
21604 }, 21807 },
21605 21808
21606 _reRender: function(filter, sort) { 21809 _reRender: function(filter, sort) {
21607 this.$.bot_table.render(); 21810 this.$.bot_table.render();
21608 }, 21811 },
21609 21812
21610 _sortBotTable: function(botA, botB) { 21813 _sortBotTable: function(botA, botB) {
21611 if (!this.sort) { 21814 if (!this._sort) {
21612 return 0; 21815 return 0;
21613 } 21816 }
21614 var dir = 1; 21817 var dir = 1;
21615 if (this.sort.direction === "desc") { 21818 if (this._sort.direction === "desc") {
21616 dir = -1; 21819 dir = -1;
21617 } 21820 }
21618 var botACol = this._column(this.sort.name, botA); 21821 var botACol = this._column(this._sort.name, botA);
21619 var botBCol = this._column(this.sort.name, botB); 21822 var botBCol = this._column(this._sort.name, botB);
21620 21823
21621 return dir * swarming.naturalCompare(botACol, botBCol); 21824 return dir * swarming.naturalCompare(botACol, botBCol);
21622 }, 21825 },
21623 21826
21624 sortChange: function(e) { 21827 _sortChange: function(e) {
21625 // The event we get from sort-toggle tells us the name of what needs 21828 // The event we get from sort-toggle tells us the name of what needs
21626 // to be sorting and how to sort it. 21829 // to be sorting and how to sort it.
21627 if (!(e && e.detail && e.detail.name)) { 21830 if (!(e && e.detail && e.detail.name)) {
21628 return; 21831 return;
21629 } 21832 }
21630 this.set("sort", e.detail); 21833 // should trigger __filterAndSort
21631 swarming.stableSort(this.bots, this._sortBotTable.bind(this)); 21834 this.set("_sort", e.detail);
21632 this._reRender();
21633 }, 21835 },
21634 21836
21635 // _stripSpecial removes the special columns and sorts the remaining 21837 // _stripSpecial removes the special columns and sorts the remaining
21636 // columns so they always appear in the same order, regardless of 21838 // columns so they always appear in the same order, regardless of
21637 // the order they are added. 21839 // the order they are added.
21638 _stripSpecial: function(){ 21840 _stripSpecial: function(){
21639 return this.columns.filter(function(c){ 21841 return this._columns.filter(function(c){
21640 return special_columns.indexOf(c) === -1; 21842 return special_columns.indexOf(c) === -1;
21641 }).sort(); 21843 }).sort();
21642 }, 21844 },
21643 21845
21644 _taskLink: function(data) { 21846 _taskLink: function(data) {
21645 if (data && data.task_id) { 21847 if (data && data.task_id) {
21646 return "/user/task/" + data.task_id; 21848 return "/user/task/" + data.task_id;
21647 } 21849 }
21648 return undefined; 21850 return undefined;
21649 } 21851 }
21650 21852
21651 }); 21853 });
21652 })(); 21854 })();
21653 </script> 21855 </script>
21654 </dom-module></div></body></html> 21856 </dom-module></div></body></html>
OLDNEW
« no previous file with comments | « appengine/swarming/elements/Makefile ('k') | appengine/swarming/elements/build/js/common.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698