| 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-list> | 9 <bot-list> |
| 10 | 10 |
| 11 bot-list creats a dynamic table for viewing swarming bots. Columns can be | 11 bot-list creats a dynamic table for viewing swarming bots. Columns can be |
| 12 dynamically filtered and it supports client-side filtering. | 12 dynamically filtered and it supports client-side filtering. |
| 13 | 13 |
| 14 Properties: | 14 Properties: |
| 15 None. This is a top-level element. | 15 None. This is a top-level element. |
| 16 | 16 |
| 17 Methods: | 17 Methods: |
| 18 None. | 18 None. |
| 19 | 19 |
| 20 Events: | 20 Events: |
| 21 None. | 21 None. |
| 22 --> | 22 --> |
| 23 | 23 |
| 24 <link rel="import" href="/res/imp/bower_components/iron-flex-layout/iron-flex-la
yout-classes.html"> | 24 <link rel="import" href="/res/imp/bower_components/iron-flex-layout/iron-flex-la
yout-classes.html"> |
| 25 <link rel="import" href="/res/imp/bower_components/polymer/polymer.html"> | 25 <link rel="import" href="/res/imp/bower_components/polymer/polymer.html"> |
| 26 | 26 |
| 27 <link rel="import" href="/res/imp/common/sort-toggle.html"> | 27 <link rel="import" href="/res/imp/common/sort-toggle.html"> |
| 28 <link rel="import" href="/res/imp/common/swarming-app.html"> | 28 <link rel="import" href="/res/imp/common/swarming-app.html"> |
| 29 <link rel="import" href="/res/imp/common/url-param.html"> |
| 29 | 30 |
| 30 <link rel="import" href="bot-filters.html"> | 31 <link rel="import" href="bot-filters.html"> |
| 31 <link rel="import" href="bot-list-data.html"> | 32 <link rel="import" href="bot-list-data.html"> |
| 32 <link rel="import" href="bot-list-shared.html"> | 33 <link rel="import" href="bot-list-shared.html"> |
| 33 <link rel="import" href="bot-list-summary.html"> | 34 <link rel="import" href="bot-list-summary.html"> |
| 34 | 35 |
| 35 <dom-module id="bot-list"> | 36 <dom-module id="bot-list"> |
| 36 <template> | 37 <template> |
| 37 <style include="iron-flex iron-flex-alignment iron-positioning swarming-app-
style"> | 38 <style include="iron-flex iron-flex-alignment iron-positioning swarming-app-
style"> |
| 38 bot-filters, bot-list-summary { | 39 bot-filters, bot-list-summary { |
| (...skipping 29 matching lines...) Expand all Loading... |
| 68 position: absolute; | 69 position: absolute; |
| 69 right: 0; | 70 right: 0; |
| 70 top: 0.4em; | 71 top: 0.4em; |
| 71 } | 72 } |
| 72 .bot-list th > span { | 73 .bot-list th > span { |
| 73 /* Leave space for sort-toggle*/ | 74 /* Leave space for sort-toggle*/ |
| 74 padding-right: 30px; | 75 padding-right: 30px; |
| 75 } | 76 } |
| 76 </style> | 77 </style> |
| 77 | 78 |
| 79 <url-param name="sort" |
| 80 value="{{_sortstr}}" |
| 81 default_value="id:asc"> |
| 82 </url-param> |
| 83 |
| 78 <swarming-app | 84 <swarming-app |
| 79 auth_headers="{{_auth_headers}}" | 85 auth_headers="{{_auth_headers}}" |
| 80 signed_in="{{_signed_in}}" | 86 signed_in="{{_signed_in}}" |
| 81 | 87 |
| 82 busy="[[_busy]]" | 88 busy="[[_busy]]" |
| 83 name="Swarming Bot List"> | 89 name="Swarming Bot List"> |
| 84 | 90 |
| 85 <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2> | 91 <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2> |
| 86 | 92 |
| 87 <div hidden$="[[_not(_signed_in)]]"> | 93 <div hidden$="[[_not(_signed_in)]]"> |
| 88 | 94 |
| 89 <div class="horizontal layout"> | 95 <div class="horizontal layout"> |
| 90 | 96 |
| 91 <bot-filters | 97 <bot-filters |
| 92 primary_map="[[_primary_map]]" | 98 primary_map="[[_primary_map]]" |
| 93 primary_arr="[[_primary_arr]]" | 99 primary_arr="[[_primary_arr]]" |
| 94 | 100 |
| 95 columns="{{_columns}}" | 101 columns="{{_columns}}" |
| 96 dimensions="{{_dimensions}}" | 102 query_params="{{_query_params}}" |
| 97 filter="{{_filter}}" | 103 filter="{{_filter}}" |
| 98 verbose="{{_verbose}}"> | 104 verbose="{{_verbose}}"> |
| 99 </bot-filters> | 105 </bot-filters> |
| 100 | 106 |
| 101 <bot-list-summary | 107 <bot-list-summary |
| 108 columns="[[_columns]]" |
| 102 fleet="[[_fleet]]" | 109 fleet="[[_fleet]]" |
| 103 filtered_bots="[[_filteredSortedBots]]"> | 110 filtered_bots="[[_filteredSortedBots]]" |
| 111 sort="[[_sortstr]]" |
| 112 verbose="[[_verbose]]"> |
| 104 </bot-list-summary> | 113 </bot-list-summary> |
| 105 | 114 |
| 106 </div> | 115 </div> |
| 107 | 116 |
| 108 <bot-list-data | 117 <bot-list-data |
| 109 auth_headers="[[_auth_headers]]" | 118 auth_headers="[[_auth_headers]]" |
| 110 dimensions="[[_dimensions]]" | 119 query_params="[[_query_params]]" |
| 111 | 120 |
| 112 bots="{{_bots}}" | 121 bots="{{_bots}}" |
| 113 busy="{{_busy}}" | 122 busy="{{_busy}}" |
| 114 fleet="{{_fleet}}" | 123 fleet="{{_fleet}}" |
| 115 primary_map="{{_primary_map}}" | 124 primary_map="{{_primary_map}}" |
| 116 primary_arr="{{_primary_arr}}"> | 125 primary_arr="{{_primary_arr}}"> |
| 117 </bot-list-data> | 126 </bot-list-data> |
| 118 | 127 |
| 119 <table class="bot-list"> | 128 <table class="bot-list"> |
| 120 <thead on-sort_change="_sortChange"> | 129 <thead on-sort_change="_sortChange"> |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 265 device_type: function(bot){ | 274 device_type: function(bot){ |
| 266 var dt = this._attribute(bot, "device_type", "none"); | 275 var dt = this._attribute(bot, "device_type", "none"); |
| 267 if (this._verbose) { | 276 if (this._verbose) { |
| 268 return dt.join(" | "); | 277 return dt.join(" | "); |
| 269 } | 278 } |
| 270 return dt[0]; | 279 return dt[0]; |
| 271 }, | 280 }, |
| 272 disk_space: function(bot) { | 281 disk_space: function(bot) { |
| 273 var aliased = []; | 282 var aliased = []; |
| 274 bot.disks.forEach(function(disk){ | 283 bot.disks.forEach(function(disk){ |
| 275 var alias = swarming.humanBytes(disk.mb, swarming.MB); | 284 var alias = sk.human.bytes(disk.mb, swarming.MB); |
| 276 aliased.push(this._applyAlias(disk.mb, disk.id + " "+ alias)); | 285 aliased.push(this._applyAlias(disk.mb, disk.id + " "+ alias)); |
| 277 }.bind(this)); | 286 }.bind(this)); |
| 278 if (this._verbose) { | 287 if (this._verbose) { |
| 279 return aliased.join(" | "); | 288 return aliased.join(" | "); |
| 280 } | 289 } |
| 281 return aliased[0]; | 290 return aliased[0]; |
| 282 }, | 291 }, |
| 283 gpu: function(bot){ | 292 gpu: function(bot){ |
| 284 var gpus = this._attribute(bot, "gpu", "none") | 293 var gpus = this._attribute(bot, "gpu", "none") |
| 285 var verbose = [] | 294 var verbose = [] |
| (...skipping 30 matching lines...) Expand all Loading... |
| 316 return os[0]; | 325 return os[0]; |
| 317 }, | 326 }, |
| 318 pool: function(bot) { | 327 pool: function(bot) { |
| 319 var pool = this._attribute(bot, "pool"); | 328 var pool = this._attribute(bot, "pool"); |
| 320 return pool.join(" | "); | 329 return pool.join(" | "); |
| 321 }, | 330 }, |
| 322 status: function(bot) { | 331 status: function(bot) { |
| 323 // If a bot is both dead and quarantined, show the deadness over the | 332 // If a bot is both dead and quarantined, show the deadness over the |
| 324 // quarentinedness. | 333 // quarentinedness. |
| 325 if (bot.is_dead) { | 334 if (bot.is_dead) { |
| 326 return "Dead. Last seen " + swarming.diffDate(bot.last_seen_ts) + | 335 return "Dead. Last seen " + sk.human.diffDate(bot.last_seen_ts) + |
| 327 " ago"; | 336 " ago"; |
| 328 } | 337 } |
| 329 if (bot.quarantined) { | 338 if (bot.quarantined) { |
| 330 return "Quarantined: " + this._attribute(bot, "quarantined"); | 339 return "Quarantined: " + this._attribute(bot, "quarantined"); |
| 331 } | 340 } |
| 332 return "Alive"; | 341 return "Alive"; |
| 333 }, | 342 }, |
| 334 task: function(bot){ | 343 task: function(bot){ |
| 335 return this._taskId(bot); | 344 return this._taskId(bot); |
| 336 }, | 345 }, |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 404 }, | 413 }, |
| 405 | 414 |
| 406 _plain_columns: { | 415 _plain_columns: { |
| 407 type: Array, | 416 type: Array, |
| 408 computed: "_stripSpecial(_columns.*)", | 417 computed: "_stripSpecial(_columns.*)", |
| 409 }, | 418 }, |
| 410 | 419 |
| 411 // _sort is an Object {name:String, direction:String}. | 420 // _sort is an Object {name:String, direction:String}. |
| 412 _sort: { | 421 _sort: { |
| 413 type: Object, | 422 type: Object, |
| 414 value: function() { | 423 computed: "_makeObject(_sortstr)", |
| 415 return { | 424 }, |
| 416 name: "id", | 425 |
| 417 direction: "asc", | 426 // We can't (easily) stick an object in query params, so we use |
| 418 }; | 427 // a serialized string of key:dir and stick that in the query params. |
| 419 } | 428 // This is deserialized (into _sort) for easier use |
| 429 _sortstr: { |
| 430 type: String, |
| 420 }, | 431 }, |
| 421 | 432 |
| 422 _verbose: { | 433 _verbose: { |
| 423 type: Boolean, | 434 type: Boolean, |
| 424 } | 435 } |
| 425 }, | 436 }, |
| 426 | 437 |
| 427 _botClass: function(bot) { | 438 _botClass: function(bot) { |
| 428 if (bot.is_dead) { | 439 if (bot.is_dead) { |
| 429 return "dead"; | 440 return "dead"; |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 480 }, | 491 }, |
| 481 | 492 |
| 482 _header: function(col){ | 493 _header: function(col){ |
| 483 return headerMap[col]; | 494 return headerMap[col]; |
| 484 }, | 495 }, |
| 485 | 496 |
| 486 _hide: function(col) { | 497 _hide: function(col) { |
| 487 return this._columns.indexOf(col) === -1; | 498 return this._columns.indexOf(col) === -1; |
| 488 }, | 499 }, |
| 489 | 500 |
| 501 _makeObject: function(sortstr){ |
| 502 if (!sortstr) { |
| 503 return undefined; |
| 504 } |
| 505 var pieces = sortstr.split(":"); |
| 506 if (pieces.length != 2) { |
| 507 // fail safe |
| 508 return {name: "id", direction:"desc"}; |
| 509 } |
| 510 return { |
| 511 name: pieces[0], |
| 512 direction: pieces[1], |
| 513 } |
| 514 }, |
| 515 |
| 490 _reRender: function(filter, sort) { | 516 _reRender: function(filter, sort) { |
| 491 this.$.bot_table.render(); | 517 this.$.bot_table.render(); |
| 492 }, | 518 }, |
| 493 | 519 |
| 494 _sortBotTable: function(botA, botB) { | 520 _sortBotTable: function(botA, botB) { |
| 495 if (!this._sort) { | 521 if (!this._sort) { |
| 496 return 0; | 522 return 0; |
| 497 } | 523 } |
| 498 var dir = 1; | 524 var dir = 1; |
| 499 if (this._sort.direction === "desc") { | 525 if (this._sort.direction === "desc") { |
| 500 dir = -1; | 526 dir = -1; |
| 501 } | 527 } |
| 502 var sort = specialSort[this._sort.name]; | 528 var sort = specialSort[this._sort.name]; |
| 503 if (sort) { | 529 if (sort) { |
| 504 return sort.bind(this)(dir, botA, botB); | 530 return sort.bind(this)(dir, botA, botB); |
| 505 } | 531 } |
| 506 // Default to a natural compare of the columns. | 532 // Default to a natural compare of the columns. |
| 507 var botACol = this._column(this._sort.name, botA); | 533 var botACol = this._column(this._sort.name, botA); |
| 508 var botBCol = this._column(this._sort.name, botB); | 534 var botBCol = this._column(this._sort.name, botB); |
| 509 | 535 |
| 510 return dir * swarming.naturalCompare(botACol, botBCol); | 536 return dir * swarming.naturalCompare(botACol, botBCol); |
| 511 }, | 537 }, |
| 512 | 538 |
| 513 _sortChange: function(e) { | 539 _sortChange: function(e) { |
| 514 // The event we get from sort-toggle tells us the name of what needs | 540 // The event we get from sort-toggle tells us the name of what needs |
| 515 // to be sorting and how to sort it. | 541 // to be sorting and how to sort it. |
| 516 if (!(e && e.detail && e.detail.name)) { | 542 if (!(e && e.detail && e.detail.name)) { |
| 517 return; | 543 return; |
| 518 } | 544 } |
| 519 // should trigger __filterAndSort | 545 // should trigger the computation of _sort and __filterAndSort |
| 520 this.set("_sort", e.detail); | 546 this.set("_sortstr", e.detail.name +":"+e.detail.direction); |
| 521 }, | 547 }, |
| 522 | 548 |
| 523 // _stripSpecial removes the special columns and sorts the remaining | 549 // _stripSpecial removes the special columns and sorts the remaining |
| 524 // columns so they always appear in the same order, regardless of | 550 // columns so they always appear in the same order, regardless of |
| 525 // the order they are added. | 551 // the order they are added. |
| 526 _stripSpecial: function(){ | 552 _stripSpecial: function(){ |
| 527 return this._columns.filter(function(c){ | 553 return this._columns.filter(function(c){ |
| 528 return special_columns.indexOf(c) === -1; | 554 return special_columns.indexOf(c) === -1; |
| 529 }).sort(); | 555 }).sort(); |
| 530 }, | 556 }, |
| 531 | 557 |
| 532 _taskLink: function(data) { | 558 _taskLink: function(data) { |
| 533 if (data && data.task_id) { | 559 if (data && data.task_id) { |
| 534 return "/user/task/" + data.task_id; | 560 return "/user/task/" + data.task_id; |
| 535 } | 561 } |
| 536 return undefined; | 562 return undefined; |
| 537 } | 563 } |
| 538 | 564 |
| 539 }); | 565 }); |
| 540 })(); | 566 })(); |
| 541 </script> | 567 </script> |
| 542 </dom-module> | 568 </dom-module> |
| OLD | NEW |