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

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

Issue 2269643002: Extract shared filters and aliasing code (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-py@master
Patch Set: Address nit 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/js.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 14616 matching lines...) Expand 10 before | Expand all | Expand 10 after
14627 <style> 14627 <style>
14628 * { 14628 * {
14629 font-family: sans-serif; 14629 font-family: sans-serif;
14630 } 14630 }
14631 /* Only style anchor tags that are actually linking somewhere.*/ 14631 /* Only style anchor tags that are actually linking somewhere.*/
14632 a[href] { 14632 a[href] {
14633 color: #1F78B4; 14633 color: #1F78B4;
14634 } 14634 }
14635 </style> 14635 </style>
14636 </dom-module> 14636 </dom-module>
14637
14638 <script>
14639 window.SwarmingBehaviors = window.SwarmingBehaviors || {};
14640 (function(){
14641 // This behavior wraps up all the shared swarming functionality.
14642 SwarmingBehaviors.SwarmingBehavior = {
14643
14644 _not: function(a) {
14645 return !a;
14646 },
14647
14648 _or: function() {
14649 var result = false;
14650 // can't use .foreach, as arguments isn't really an Array.
14651 for (var i = 0; i < arguments.length; i++) {
14652 result = result || arguments[i];
14653 }
14654 return result;
14655 },
14656 };
14657 })();
14658 </script>
14659 <dom-module id="swarming-index" assetpath="/res/imp/index/"> 14637 <dom-module id="swarming-index" assetpath="/res/imp/index/">
14660 <template> 14638 <template>
14661 <style include="swarming-app-style"> 14639 <style include="swarming-app-style">
14662 14640
14663 </style> 14641 </style>
14664 14642
14665 <swarming-app client_id="[[client_id]]" auth_headers="{{auth_headers}}" name ="Swarming" busy="[[busy]]"> 14643 <swarming-app client_id="[[client_id]]" auth_headers="{{auth_headers}}" name ="Swarming" busy="[[busy]]">
14666 14644
14667 <iron-ajax id="request" url="/_ah/api/swarming/v1/server/details" headers= "[[auth_headers]]" handle-as="json" last-response="{{serverDetails}}" loading="{ {busy}}"> 14645 <iron-ajax id="request" url="/_ah/api/swarming/v1/server/details" headers= "[[auth_headers]]" handle-as="json" last-response="{{serverDetails}}" loading="{ {busy}}">
14668 </iron-ajax> 14646 </iron-ajax>
(...skipping 23 matching lines...) Expand all
14692 type: String, 14670 type: String,
14693 } 14671 }
14694 }, 14672 },
14695 14673
14696 signIn: function(){ 14674 signIn: function(){
14697 this.$.request.generateRequest(); 14675 this.$.request.generateRequest();
14698 }, 14676 },
14699 14677
14700 }); 14678 });
14701 </script> 14679 </script>
14702 </dom-module><dom-module id="dynamic-table-style" assetpath="/res/imp/common/"> 14680 </dom-module><script>
14681 window.SwarmingBehaviors = window.SwarmingBehaviors || {};
14682 (function(){
14683 // This behavior wraps up all the shared swarming functionality.
14684 SwarmingBehaviors.CommonBehavior = {
14685
14686 _not: function(a) {
14687 return !a;
14688 },
14689
14690 _or: function() {
14691 var result = false;
14692 // can't use .foreach, as arguments isn't really an Array.
14693 for (var i = 0; i < arguments.length; i++) {
14694 result = result || arguments[i];
14695 }
14696 return result;
14697 },
14698 };
14699 })();
14700 </script>
14701 <dom-module id="dynamic-table-style" assetpath="/res/imp/common/">
14703 <template> 14702 <template>
14704 <style> 14703 <style>
14705 table { 14704 table {
14706 border-collapse: collapse; 14705 border-collapse: collapse;
14707 margin-left: 5px; 14706 margin-left: 5px;
14708 } 14707 }
14709 td, th { 14708 td, th {
14710 border: 1px solid #DDD; 14709 border: 1px solid #DDD;
14711 padding: 5px; 14710 padding: 5px;
14712 } 14711 }
14713 th { 14712 th {
14714 position: relative; 14713 position: relative;
14715 } 14714 }
14716 sort-toggle { 14715 sort-toggle {
14717 position: absolute; 14716 position: absolute;
14718 right: 0; 14717 right: 0;
14719 top: 0.4em; 14718 top: 0.4em;
14720 } 14719 }
14721 </style> 14720 </style>
14722 14721
14723 </template> 14722 </template>
14724 </dom-module> 14723 </dom-module>
14725 14724
14726 <script> 14725 <script>
14727 window.SwarmingBehaviors = window.SwarmingBehaviors || {};
14728 (function(){ 14726 (function(){
14729 // This behavior wraps up all the shared swarming functionality. 14727 // This behavior wraps up all the shared swarming functionality.
14730 SwarmingBehaviors.DynamicTableBehavior = { 14728 SwarmingBehaviors.DynamicTableBehavior = [SwarmingBehaviors.CommonBehavior, {
14731 14729
14732 properties: { 14730 properties: {
14733 14731
14734 _columns: { 14732 _columns: {
14735 type: Array, 14733 type: Array,
14736 }, 14734 },
14737 14735
14738 _filter: { 14736 _filter: {
14739 type: Function, 14737 type: Function,
14740 }, 14738 },
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
14848 }, 14846 },
14849 // _stripSpecial removes the special columns and sorts the remaining 14847 // _stripSpecial removes the special columns and sorts the remaining
14850 // columns so they always appear in the same order, regardless of 14848 // columns so they always appear in the same order, regardless of
14851 // the order they are added. 14849 // the order they are added.
14852 _stripSpecial: function(){ 14850 _stripSpecial: function(){
14853 return this._columns.filter(function(c) { 14851 return this._columns.filter(function(c) {
14854 return this._specialColumns.indexOf(c) === -1; 14852 return this._specialColumns.indexOf(c) === -1;
14855 }.bind(this)).sort(); 14853 }.bind(this)).sort();
14856 }, 14854 },
14857 14855
14858 }; 14856 // Common columns shared between tasklist and botlist
14857 _commonColumns: function() {
14858 // return a fresh object so all elements have their own copy
14859 return {
14860 android_devices: function(bot) {
14861 var devs = this._attribute(bot, "android_devices", "0");
14862 if (this._verbose) {
14863 return devs.join(" | ") + " devices available";
14864 }
14865 // max() works on strings as long as they can be coerced to Number.
14866 return Math.max(...devs) + " devices available";
14867 },
14868 device_type: function(bot) {
14869 var dt = this._attribute(bot, "device_type", "none");
14870 dt = dt[0];
14871 var alias = swarming.alias.android(dt);
14872 if (alias === "unknown") {
14873 return dt;
14874 }
14875 return swarming.alias.apply(dt, alias);
14876 },
14877 gpu: function(bot){
14878 var gpus = this._attribute(bot, "gpu", "none");
14879 var verbose = []
14880 var named = [];
14881 // non-verbose mode has only the top level GPU info "e.g. NVidia"
14882 // which is found by looking for gpu ids w/o a colon.
14883 gpus.forEach(function(g){
14884 var alias = swarming.alias.gpu(g);
14885 if (alias === "unknown") {
14886 verbose.push(g);
14887 if (g.indexOf(":") === -1) {
14888 named.push(g);
14889 }
14890 return;
14891 }
14892 verbose.push(swarming.alias.apply(g, alias));
14893 if (g.indexOf(":") === -1) {
14894 named.push(swarming.alias.apply(g, alias));
14895 }
14896 }.bind(this))
14897 if (this._verbose || !named.length) {
14898 return verbose.join(" | ");
14899 }
14900 return named.join(" | ");
14901 },
14902 pool: function(bot) {
14903 var pool = this._attribute(bot, "pool");
14904 return pool.join(" | ");
14905 },
14906 };
14907 },
14908
14909
14910 }];
14859 })(); 14911 })();
14860 </script> 14912 </script>
14861 14913
14862 <script> 14914 <script>
14863 14915
14864 (function() { 14916 (function() {
14865 14917
14866 // monostate data 14918 // monostate data
14867 var metaDatas = {}; 14919 var metaDatas = {};
14868 var metaArrays = {}; 14920 var metaArrays = {};
(...skipping 7990 matching lines...) Expand 10 before | Expand all | Expand 10 after
22859 <script> 22911 <script>
22860 Polymer({ 22912 Polymer({
22861 is: 'paper-input', 22913 is: 'paper-input',
22862 22914
22863 behaviors: [ 22915 behaviors: [
22864 Polymer.IronFormElementBehavior, 22916 Polymer.IronFormElementBehavior,
22865 Polymer.PaperInputBehavior 22917 Polymer.PaperInputBehavior
22866 ] 22918 ]
22867 }); 22919 });
22868 </script> 22920 </script>
22869 <script> 22921 <dom-module id="query-column-filter-style" assetpath="/res/imp/common/">
22870 (function(){
22871 var ANDROID_ALIASES = {
22872 "bullhead": "Nexus 5X",
22873 "flo": "Nexus 7 (2013)",
22874 "flounder": "Nexus 9",
22875 "foster": "NVIDIA Shield",
22876 "fugu": "Nexus Player",
22877 "grouper": "Nexus 7 (2012)",
22878 "hammerhead": "Nexus 5",
22879 "m0": "Galaxy S3",
22880 "mako": "Nexus 4",
22881 "manta": "Nexus 10",
22882 "shamu": "Nexus 6",
22883 "sprout": "Android One",
22884 };
22885 // Taken from http://developer.android.com/reference/android/os/BatteryManag er.html
22886 var BATTERY_HEALTH_UNKNOWN = 1;
22887 var BATTERY_HEALTH_GOOD = 2;
22888 var BATTERY_STATUS_CHARGING = 2;
22889
22890 var UNAUTHENTICATED = "unauthenticated";
22891 var AVAILABLE = "available";
22892 var UNKNOWN = "unknown";
22893
22894 var GPU_ALIASES = {
22895 "1002": "AMD",
22896 "1002:6779": "AMD Radeon HD 6450/7450/8450",
22897 "1002:6821": "AMD Radeon HD 8870M",
22898 "1002:683d": "AMD Radeon HD 7770/8760",
22899 "1002:9830": "AMD Radeon HD 8400",
22900 "102b": "Matrox",
22901 "102b:0522": "Matrox MGA G200e",
22902 "102b:0532": "Matrox MGA G200eW",
22903 "102b:0534": "Matrox G200eR2",
22904 "10de": "NVIDIA",
22905 "10de:08a4": "NVIDIA GeForce 320M",
22906 "10de:08aa": "NVIDIA GeForce 320M",
22907 "10de:0fe9": "NVIDIA GeForce GT 750M Mac Edition",
22908 "10de:104a": "NVIDIA GeForce GT 610",
22909 "10de:11c0": "NVIDIA GeForce GTX 660",
22910 "10de:1244": "NVIDIA GeForce GTX 550 Ti",
22911 "10de:1401": "NVIDIA GeForce GTX 960",
22912 "8086": "Intel",
22913 "8086:0412": "Intel Haswell Integrated",
22914 "8086:041a": "Intel Xeon Integrated",
22915 "8086:0a2e": "Intel Haswell Integrated",
22916 "8086:0d26": "Intel Crystal Well Integrated",
22917 "8086:22b1": "Intel Braswell Integrated",
22918 }
22919
22920 // For consistency, all aliases are displayed like:
22921 // Nexus 5X (bullhead)
22922 // This regex matches a string like "ALIAS (ORIG)", with ORIG as group 1.
22923 var ALIAS_REGEXP = /.+ \((.*)\)/;
22924
22925 // This behavior wraps up all the shared bot-list functionality by
22926 // extending SwarmingBehaviors.SwarmingBehavior
22927 SwarmingBehaviors.BotListBehavior = [SwarmingBehaviors.SwarmingBehavior, {
22928
22929 properties: {
22930 DIMENSIONS_WITH_ALIASES: {
22931 type: Array,
22932 value: function(){
22933 return ["device_type", "gpu"];
22934 },
22935 },
22936 BOT_PROPERTIES: {
22937 type: Array,
22938 value: function() {
22939 // TODO(kjlubick): Add more of these things from state, as they
22940 // needed/useful/requested.
22941 return ["disk_space", "task", "status"];
22942 }
22943 },
22944 },
22945
22946 _androidAlias: function(dt) {
22947 return ANDROID_ALIASES[dt] || UNKNOWN;
22948 },
22949
22950 // _applyAlias is the consistent way to modify a string to show its alias.
22951 _applyAlias: function(orig, alias) {
22952 return alias +" ("+orig+")";
22953 },
22954
22955 // _attribute looks first in dimension and then in state for the
22956 // specified attribute. This will always return an array. If there is
22957 // no matching attribute, ["unknown"] will be returned.
22958 _attribute: function(bot, attr, none) {
22959 none = none || UNKNOWN;
22960 return this._dimension(bot, attr) || this._state(bot, attr) || [none];
22961 },
22962
22963 _devices: function(bot) {
22964 var devices = [];
22965 var d = (bot && bot.state && bot.state.devices) || {};
22966 // state.devices is like {Serial:Object}, so we need to keep the serial
22967 for (key in d) {
22968 var o = d[key];
22969 o.serial = key;
22970 o.okay = (o.state === AVAILABLE);
22971 // It is easier to assume all devices on a bot are of the same type
22972 // than to pick through the (incomplete) device state and find it.
22973 o.device_type = this._attribute(bot, "device_type")[0];
22974 devices.push(o);
22975 }
22976 return devices;
22977 },
22978
22979 // _deviceType returns the codename of a given Android device.
22980 _deviceType: function(device) {
22981 return device.device_type.toLowerCase();
22982 },
22983
22984 // _dimension returns the given dimension of a bot. If it is defined, it
22985 // is an array of strings.
22986 _dimension: function(bot, dim) {
22987 if (!bot || !bot.dimensions || !dim) {
22988 return undefined;
22989 }
22990 for (var i = 0; i < bot.dimensions.length; i++) {
22991 if (bot.dimensions[i].key === dim) {
22992 return bot.dimensions[i].value;
22993 }
22994 }
22995 return undefined;
22996 },
22997
22998 _gpuAlias: function(gpu) {
22999 return GPU_ALIASES[gpu] || UNKNOWN;
23000 },
23001
23002 // _state returns the requested attribute from a bot's state.
23003 // For consistency with _dimension, if the attribute is not an array,
23004 // it is put as the only element in an array.
23005 _state: function(bot, attr) {
23006 if (!bot || !bot.state || !bot.state[attr]) {
23007 return undefined
23008 }
23009 var state = bot.state[attr];
23010 if (Array.isArray(state)) {
23011 return state;
23012 }
23013 return [state];
23014 },
23015
23016 _taskId: function(bot) {
23017 if (bot && bot.task_id) {
23018 return bot.task_id;
23019 }
23020 return "idle";
23021 },
23022
23023 // _unalias will return the base dimension/state with its alias removed
23024 // if it had one. This is handy for sorting and filtering.
23025 _unalias: function(str) {
23026 var match = ALIAS_REGEXP.exec(str);
23027 if (match) {
23028 return match[1];
23029 }
23030 return str;
23031 },
23032 }];
23033 })()
23034 </script>
23035 <dom-module id="bot-filters" assetpath="/res/imp/botlist/">
23036 <template> 22922 <template>
23037 <style is="custom-style" include="iron-flex iron-flex-alignment iron-positio ning"> 22923 <style>
23038 :host { 22924 :host {
23039 display: block; 22925 display: block;
23040 font-family: sans-serif; 22926 font-family: sans-serif;
23041 } 22927 }
23042 #filter { 22928 #filter {
23043 margin:0 5px; 22929 margin:0 5px;
23044 } 22930 }
23045 22931
23046 .container { 22932 .container {
23047 min-height: 120px; 22933 min-height: 120px;
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
23110 margin: 2px; 22996 margin: 2px;
23111 /* See https://sites.google.com/a/google.com/skia-infrastructure/design- docs/general-design-guidance */ 22997 /* See https://sites.google.com/a/google.com/skia-infrastructure/design- docs/general-design-guidance */
23112 --paper-checkbox-checked-color: black; 22998 --paper-checkbox-checked-color: black;
23113 --paper-checkbox-checked-ink-color: black; 22999 --paper-checkbox-checked-ink-color: black;
23114 --paper-checkbox-unchecked-color: black; 23000 --paper-checkbox-unchecked-color: black;
23115 --paper-checkbox-unchecked-ink-color: black; 23001 --paper-checkbox-unchecked-ink-color: black;
23116 --paper-checkbox-label-color: black; 23002 --paper-checkbox-label-color: black;
23117 } 23003 }
23118 </style> 23004 </style>
23119 23005
23120 <url-param name="filters" value="{{_filters}}" default_values="[]" multi=""> 23006 </template>
23121 </url-param> 23007 </dom-module>
23122 <url-param name="columns" value="{{columns}}" default_values="[&quot;id&quot ;,&quot;os&quot;,&quot;task&quot;,&quot;status&quot;]" multi="">
23123 </url-param>
23124 <url-param name="query" value="{{_query}}">
23125 </url-param>
23126 <url-param name="verbose" value="{{verbose}}">
23127 </url-param>
23128 <url-param name="limit" default_value="200" value="{{limit}}">
23129 </url-param>
23130 23008
23131 <div class="container horizontal layout"> 23009 <script>
23132
23133
23134 <div class="narrow-down-selector">
23135 <div>
23136 <paper-input id="filter" label="Search columns and filters" placeholde r="gpu nvidia" value="{{_query}}">
23137 </paper-input>
23138 </div>
23139
23140 <div class="selector side-by-side" title="This shows all bot dimension n ames and other interesting bot properties. Mark the check box to add as a column . Select the row to see filter options.">
23141 <iron-selector attr-for-selected="label" selected="{{_primarySelected} }">
23142 <template is="dom-repeat" items="[[_primaryItems]]" as="item">
23143 <div class="selectable item horizontal layout" label="[[item]]">
23144
23145 <span>[[_beforeBold(item,_query)]]<span class="bold">[[_bold(ite m,_query)]]</span>[[_afterBold(item,_query)]]</span>
23146 <span class="flex"></span>
23147 <paper-checkbox noink="" disabled$="[[_cantToggleColumn(item)]]" checked="[[_columnState(item,columns.*)]]" on-change="_toggleColumn">
23148 </paper-checkbox>
23149 </div>
23150 </template>
23151 </iron-selector>
23152 </div>
23153
23154 <div class="selector side-by-side" title="These are all options (if any) that the bot list can be filtered on.">
23155 <template is="dom-repeat" id="secondaryList" items="[[_secondaryItems] ]" as="item">
23156 <div class="item horizontal layout" label="[[item]]">
23157
23158 <span>[[_beforeBold(item,_query)]]<span class="bold">[[_bold(item, _query)]]</span>[[_afterBold(item,_query)]]</span>
23159 <span class="flex"></span>
23160 <iron-icon class="icons" icon="icons:arrow-forward" hidden="[[_can tAddFilter(_primarySelected,item,_filters.*)]]" on-tap="_addFilter">
23161 </iron-icon>
23162 </div>
23163 </template>
23164 </div>
23165
23166 <div class="selector side-by-side" title="These filters are AND'd togeth er and applied to all bots in
23167 the fleet.">
23168 <template is="dom-repeat" items="[[_filters]]" as="fil">
23169 <div class="item horizontal layout" label="[[fil]]">
23170 <span>[[fil]]</span>
23171 <span class="flex"></span>
23172 <iron-icon class="icons" icon="icons:remove-circle-outline" hidden ="[[_cantRemoveFilter(fil,_filters.*)]]" on-tap="_removeFilter">
23173 </iron-icon>
23174 </div>
23175 </template>
23176 </div>
23177
23178 <div class="side-by-side">
23179 <paper-checkbox checked="{{verbose}}">Verbose Entries</paper-checkbox>
23180 <paper-input id="limit" label="Limit Results" auto-validate="" min="0" max="1000" pattern="[0-9]+" value="{{limit}}">
23181 </paper-input>
23182 </div>
23183 </div>
23184
23185 </div>
23186
23187 </template>
23188 <script>
23189 (function(){ 23010 (function(){
23190 var FILTER_SEP = ":";
23191 // filterMap is a map of primary -> function. The function returns a
23192 // boolean "does the bot (first arg) match the second argument". These
23193 // functions will have "this" be the botlist, and will have access to all
23194 // functions defined in bot-list and bot-list-shared. If there is not
23195 // one provided, a default will be used, see _makeFilter().
23196 var filterMap = {
23197 android_devices: function(bot, num) {
23198 var o = this._attribute(bot, "android_devices", "0");
23199 return o.indexOf(num) !== -1;
23200 },
23201 device_os: function(bot, os) {
23202 var o = this._attribute(bot, "device_os", "none");
23203 return o.indexOf(os) !== -1;
23204 },
23205 device_type: function(bot, dt) {
23206 var o = this._attribute(bot, "device_type", "none");
23207 return o.indexOf(this._unalias(dt)) !== -1;
23208 },
23209 disk_space: function(bot, space) {
23210 return true;
23211 },
23212 gpu: function(bot, gpu) {
23213 var o = this._attribute(bot, "gpu", "none");
23214 return o.indexOf(this._unalias(gpu)) !== -1;
23215 },
23216 id: function(bot, id) {
23217 return true;
23218 },
23219 status: function(bot, status) {
23220 if (status === "quarantined") {
23221 return bot.quarantined;
23222 } else if (status === "dead") {
23223 return bot.is_dead;
23224 } else {
23225 // Status must be "alive".
23226 return !bot.quarantined && !bot.is_dead;
23227 }
23228 },
23229 task: function(bot, task) {
23230 if (task === "idle") {
23231 return this._taskId(bot) === "idle";
23232 }
23233 // Task must be "busy".
23234 return this._taskId(bot) !== "idle";
23235 }
23236 };
23237
23238 // Given a space separated list of queries, matchPartCaseInsensitive 23011 // Given a space separated list of queries, matchPartCaseInsensitive
23239 // returns an object of any query that matches a part of str, case 23012 // returns an object of any query that matches a part of str, case
23240 // insensitive. The object has an idx (index) and the part that matched. 23013 // insensitive. The object has an idx (index) and the part that matched.
23241 var matchPartCaseInsensitive = function(str, queries) { 23014 var matchPartCaseInsensitive = function(str, queries) {
23242 if (!queries) { 23015 if (!queries) {
23243 return { 23016 return {
23244 idx: 0, 23017 idx: 0,
23245 part: "", 23018 part: "",
23246 }; 23019 };
23247 } 23020 }
(...skipping 12 matching lines...) Expand all
23260 idx: idx, 23033 idx: idx,
23261 part: xq[i], 23034 part: xq[i],
23262 }; 23035 };
23263 } 23036 }
23264 } 23037 }
23265 return { 23038 return {
23266 idx: -1, 23039 idx: -1,
23267 }; 23040 };
23268 }; 23041 };
23269 23042
23270 Polymer({ 23043 // Extend the Aliases behavior
23271 is: "bot-filters", 23044 SwarmingBehaviors.QueryColumnFilter = [SwarmingBehaviors.CommonBehavior, {
23272
23273 behaviors: [SwarmingBehaviors.BotListBehavior],
23274 23045
23275 properties: { 23046 properties: {
23276 // input 23047 // input
23277 primary_map: { 23048 primary_map: {
23278 type: Object, 23049 type: Object,
23279 }, 23050 },
23280 primary_arr: { 23051 primary_arr: {
23281 type: Array, 23052 type: Array,
23282 }, 23053 },
23283 dimensions: { 23054 dimensions: {
23284 type: Array, 23055 type: Array,
23285 }, 23056 },
23286
23287 // output 23057 // output
23288 columns: {
23289 type: Array,
23290 notify: true,
23291 },
23292 filter: { 23058 filter: {
23293 type: Function, 23059 type: Function,
23294 computed: "_makeFilter(_filters.*)", 23060 computed: "_makeFilter(_filters.*)",
23295 notify: true, 23061 notify: true,
23296 }, 23062 },
23297 query_params: {
23298 type: Object,
23299 computed: "_extractQueryParams(dimensions.*,_filters.*, limit)",
23300 notify: true,
23301 },
23302 verbose: {
23303 type: Boolean,
23304 notify: true,
23305 },
23306 23063
23307 // private 23064 // private
23065 FILTER_SEP: {
23066 type:String,
23067 value: ":",
23068 },
23308 _filters: { 23069 _filters: {
23309 type:Array, 23070 type:Array,
23310 }, 23071 },
23311 _limit: { 23072 _limit: {
23312 type: Number, 23073 type: Number,
23313 }, 23074 },
23314 _primaryItems: { 23075 _primaryItems: {
23315 type: Array, 23076 type: Array,
23316 computed: "_primary(_query, primary_map, primary_arr, columns.*)", 23077 computed: "_primary(_query, primary_map, primary_arr, columns.*)",
23317 }, 23078 },
23318 _primarySelected: { 23079 _primarySelected: {
23319 type: String, 23080 type: String,
23320 value: "", 23081 value: "",
23321 }, 23082 },
23322 // query is treated as a space separated list. 23083 // query is treated as a space separated list.
23323 _query: { 23084 _query: {
23324 type:String, 23085 type:String,
23325 }, 23086 },
23326 _secondaryItems: { 23087 _secondaryItems: {
23327 type: Array, 23088 type: Array,
23328 computed: "_secondary(_primarySelected, _query, primary_map)", 23089 computed: "_secondary(_primarySelected, _query, primary_map)",
23329 }, 23090 },
23091 },
23330 23092
23331 },
23332 23093
23333 _addFilter: function(e) { 23094 _addFilter: function(e) {
23334 // e.model.foo is a way to get access to the "foo" inside a dom-repeat 23095 // e.model.foo is a way to get access to the "foo" inside a dom-repeat
23335 // that had the event (in our case, a tap) acted upon it. This name, 23096 // that had the event (in our case, a tap) acted upon it. This name,
23336 // "foo", is set by the dom-repeat above 'as="foo"' 23097 // "foo", is set by the dom-repeat above 'as="foo"'
23337 var filterItem = e.model.item; 23098 var filterItem = e.model.item;
23338 if (this._cantAddFilter(this._primarySelected, filterItem)) { 23099 if (this._cantAddFilter(this._primarySelected, filterItem)) {
23339 return; 23100 return;
23340 } 23101 }
23341 var filter = this._primarySelected + FILTER_SEP + filterItem; 23102 var filter = this._primarySelected + this.FILTER_SEP + filterItem;
23342 this.push("_filters", filter); 23103 this.push("_filters", filter);
23343 }, 23104 },
23344 23105
23345 _removeFilter: function(e){ 23106 _removeFilter: function(e){
23346 var filter = e.model.fil; 23107 var filter = e.model.fil;
23347 if (this._cantRemoveFilter(filter)){ 23108 if (this._cantRemoveFilter(filter)){
23348 return; 23109 return;
23349 } 23110 }
23350 var idx = this._filters.indexOf(filter); 23111 var idx = this._filters.indexOf(filter);
23351 if (idx !== -1) { 23112 if (idx !== -1) {
23352 this.splice("_filters", idx, 1); 23113 this.splice("_filters", idx, 1);
23353 } 23114 }
23354 }, 23115 },
23355 23116
23356 _cantAddFilter: function(primarySelected, filterItem) { 23117 _cantAddFilter: function(primarySelected, filterItem) {
23357 // Check that everything is selected and this filter isn't already in 23118 // Check that everything is selected and this filter isn't already in
23358 // the array. 23119 // the array.
23359 if (!primarySelected || !filterItem) { 23120 if (!primarySelected || !filterItem) {
23360 return true; 23121 return true;
23361 } 23122 }
23362 var filter = primarySelected + FILTER_SEP + filterItem; 23123 var filter = primarySelected + this.FILTER_SEP + filterItem;
23363 return this._filters.indexOf(filter) !== -1; 23124 return this._filters.indexOf(filter) !== -1;
23364 }, 23125 },
23365 23126
23366 _cantRemoveFilter: function(filter) { 23127 _cantRemoveFilter: function(filter) {
23367 return !filter || this._filters.indexOf(filter) === -1; 23128 return !filter || this._filters.indexOf(filter) === -1;
23368 }, 23129 },
23369 23130
23370 _toggleColumn: function(e) {
23371 var col = e.model.item;
23372
23373 if (this._cantToggleColumn(col)) {
23374 return;
23375 }
23376 if (this._columnState(col)) {
23377 var idx = this.columns.indexOf(col);
23378 if (idx !== -1) {
23379 this.splice("columns", idx, 1);
23380 }
23381 return;
23382 }
23383 this.push("columns", col);
23384 },
23385
23386 _cantToggleColumn: function(col) {
23387 // Don't allow the id column to be removed, as the bot list is basically
23388 // meaningless without it.
23389 return !col || col === "id" ;
23390 },
23391
23392 _columnState: function(col) {
23393 if (!col) {
23394 return false;
23395 }
23396 return this.columns.indexOf(col) !== -1;
23397 },
23398
23399 _makeFilter: function() { 23131 _makeFilter: function() {
23400 // All filters will be AND'd together. 23132 // All filters will be AND'd together.
23401 // filterGroups will be a map of primary (i.e. column) -> array of 23133 // filterGroups will be a map of primary (i.e. column) -> array of
23402 // options that should be filtered to. 23134 // options that should be filtered to.
23403 // e.g. "os" -> ["Windows", "Linux"] 23135 // e.g. "os" -> ["Windows", "Linux"]
23404 // Since they will be or'd together, order doesn't matter. 23136 // Since they will be or'd together, order doesn't matter.
23405 var filterGroups = {}; 23137 var filterGroups = {};
23406 this._filters.forEach(function(filterString){ 23138 this._filters.forEach(function(filterString){
23407 var idx = filterString.indexOf(FILTER_SEP); 23139 var idx = filterString.indexOf(this.FILTER_SEP);
23408 var primary = filterString.slice(0, idx); 23140 var primary = filterString.slice(0, idx);
23409 var param = filterString.slice(idx + FILTER_SEP.length); 23141 var param = filterString.slice(idx + this.FILTER_SEP.length);
23410 var arr = filterGroups[primary] || []; 23142 var arr = filterGroups[primary] || [];
23411 arr.push(param); 23143 arr.push(param);
23412 filterGroups[primary] = arr; 23144 filterGroups[primary] = arr;
23413 }); 23145 }.bind(this));
23146 var filterMap = this._filterMap || {};
23414 return function(bot){ 23147 return function(bot){
23415 var retVal = true; 23148 var retVal = true;
23416 // Look up all the primary keys we are filter by, then look up how 23149 // Look up all the primary keys we are filter by, then look up how
23417 // to filter (in filterMap) and apply the filter for each filter 23150 // to filter (in filterMap) and apply the filter for each filter
23418 // option. 23151 // option.
23419 for (primary in filterGroups){ 23152 for (primary in filterGroups){
23420 var params = filterGroups[primary]; 23153 var params = filterGroups[primary];
23421 var filter = filterMap[primary]; 23154 var filter = filterMap[primary];
23422 if (!filter) { 23155 if (!filter) {
23423 filter = function(bot, c) { 23156 filter = function(bot, c) {
23424 var o = this._attribute(bot, primary); 23157 var o = this._attribute(bot, primary);
23425 return o.indexOf(c) !== -1; 23158 return o.indexOf(c) !== -1;
23426 }.bind(this); 23159 }.bind(this);
23427 } 23160 }
23428 if (filter) { 23161 if (filter) {
23429 params.forEach(function(param){ 23162 params.forEach(function(param){
23430 retVal = retVal && filter.bind(this)(bot,param); 23163 retVal = retVal && filter.bind(this)(bot,param);
23431 }.bind(this)); 23164 }.bind(this));
23432 } 23165 }
23433 } 23166 }
23434 return retVal; 23167 return retVal;
23435 } 23168 }
23436 }, 23169 },
23437 23170
23171 _toggleColumn: function(e) {
23172 var col = e.model.item;
23173
23174 if (this._cantToggleColumn(col)) {
23175 return;
23176 }
23177 if (this._columnState(col)) {
23178 var idx = this.columns.indexOf(col);
23179 if (idx !== -1) {
23180 this.splice("columns", idx, 1);
23181 }
23182 return;
23183 }
23184 this.push("columns", col);
23185 },
23186
23187 _cantToggleColumn: function(col) {
23188 // Clients can override this
23189 return false;
23190 },
23191
23192 _columnState: function(col) {
23193 if (!col) {
23194 return false;
23195 }
23196 return this.columns.indexOf(col) !== -1;
23197 },
23198
23199
23438 _primary: function(query, primary_map, primary_arr) { 23200 _primary: function(query, primary_map, primary_arr) {
23439 // If the user has typed in a query, only show those primary keys that 23201 // If the user has typed in a query, only show those primary keys that
23440 // partially match the query or that have secondary values which 23202 // partially match the query or that have secondary values which
23441 // partially match. 23203 // partially match.
23442 var arr = this.primary_arr.filter(function(s){ 23204 var arr = this.primary_arr.filter(function(s){
23443 if (matchPartCaseInsensitive(s, query).idx !== -1) { 23205 if (matchPartCaseInsensitive(s, query).idx !== -1) {
23444 return true; 23206 return true;
23445 } 23207 }
23446 var opts = primary_map[s]; 23208 var opts = primary_map[s];
23447 for (var i = 0; i < opts.length; i++) { 23209 for (var i = 0; i < opts.length; i++) {
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
23513 }, 23275 },
23514 23276
23515 _afterBold: function(item, query) { 23277 _afterBold: function(item, query) {
23516 var match = matchPartCaseInsensitive(item, query); 23278 var match = matchPartCaseInsensitive(item, query);
23517 if (match.idx === -1) { 23279 if (match.idx === -1) {
23518 return ""; 23280 return "";
23519 } 23281 }
23520 return item.substring(match.idx + match.part.length); 23282 return item.substring(match.idx + match.part.length);
23521 }, 23283 },
23522 23284
23285 // Common filters shared between tasklist and botlist
23286 _commonFilters: function() {
23287 // return a fresh object so all elements have their own copy
23288 return {
23289 android_devices: function(bot, num) {
23290 var o = this._attribute(bot, "android_devices", "0");
23291 return o.indexOf(num) !== -1;
23292 },
23293 device_os: function(bot, os) {
23294 var o = this._attribute(bot, "device_os", "none");
23295 return o.indexOf(os) !== -1;
23296 },
23297 device_type: function(bot, dt) {
23298 var o = this._attribute(bot, "device_type", "none");
23299 return o.indexOf(swarming.alias.unapply(dt)) !== -1;
23300 },
23301 gpu: function(bot, gpu) {
23302 var o = this._attribute(bot, "gpu", "none");
23303 return o.indexOf(swarming.alias.unapply(gpu)) !== -1;
23304 },
23305 };
23306 },
23307
23308 }];
23309 })();
23310 </script>
23311 <script>
23312 (function(){
23313 // Taken from http://developer.android.com/reference/android/os/BatteryManag er.html
23314 var BATTERY_HEALTH_UNKNOWN = 1;
23315 var BATTERY_HEALTH_GOOD = 2;
23316 var BATTERY_STATUS_CHARGING = 2;
23317
23318 var UNAUTHENTICATED = "unauthenticated";
23319 var AVAILABLE = "available";
23320 var UNKNOWN = "unknown";
23321
23322 // This behavior wraps up all the shared bot-list functionality by
23323 // extending SwarmingBehaviors.CommonBehavior
23324 SwarmingBehaviors.BotListBehavior = [SwarmingBehaviors.CommonBehavior, {
23325
23326 properties: {
23327 BOT_PROPERTIES: {
23328 type: Array,
23329 value: function() {
23330 // TODO(kjlubick): Add more of these things from state, as they
23331 // needed/useful/requested.
23332 return ["disk_space", "task", "status"];
23333 }
23334 },
23335 },
23336
23337 // _attribute looks first in dimension and then in state for the
23338 // specified attribute. This will always return an array. If there is
23339 // no matching attribute, ["unknown"] will be returned.
23340 _attribute: function(bot, attr, none) {
23341 none = none || UNKNOWN;
23342 return this._dimension(bot, attr) || this._state(bot, attr) || [none];
23343 },
23344
23345 _devices: function(bot) {
23346 var devices = [];
23347 var d = (bot && bot.state && bot.state.devices) || {};
23348 // state.devices is like {Serial:Object}, so we need to keep the serial
23349 for (key in d) {
23350 var o = d[key];
23351 o.serial = key;
23352 o.okay = (o.state === AVAILABLE);
23353 // It is easier to assume all devices on a bot are of the same type
23354 // than to pick through the (incomplete) device state and find it.
23355 o.device_type = this._attribute(bot, "device_type")[0];
23356 devices.push(o);
23357 }
23358 return devices;
23359 },
23360
23361 // _deviceType returns the codename of a given Android device.
23362 _deviceType: function(device) {
23363 return device.device_type.toLowerCase();
23364 },
23365
23366 // _dimension returns the given dimension of a bot. If it is defined, it
23367 // is an array of strings.
23368 _dimension: function(bot, dim) {
23369 if (!bot || !bot.dimensions || !dim) {
23370 return undefined;
23371 }
23372 for (var i = 0; i < bot.dimensions.length; i++) {
23373 if (bot.dimensions[i].key === dim) {
23374 return bot.dimensions[i].value;
23375 }
23376 }
23377 return undefined;
23378 },
23379
23380 // _state returns the requested attribute from a bot's state.
23381 // For consistency with _dimension, if the attribute is not an array,
23382 // it is put as the only element in an array.
23383 _state: function(bot, attr) {
23384 if (!bot || !bot.state || !bot.state[attr]) {
23385 return undefined
23386 }
23387 var state = bot.state[attr];
23388 if (Array.isArray(state)) {
23389 return state;
23390 }
23391 return [state];
23392 },
23393
23394 _taskId: function(bot) {
23395 if (bot && bot.task_id) {
23396 return bot.task_id;
23397 }
23398 return "idle";
23399 },
23400
23401 }];
23402 })()
23403 </script>
23404 <dom-module id="bot-filters" assetpath="/res/imp/botlist/">
23405 <template>
23406 <style is="custom-style" include="iron-flex iron-flex-alignment iron-positio ning query-column-filter-style">
23407
23408 </style>
23409
23410 <url-param name="filters" value="{{_filters}}" default_values="[]" multi="">
23411 </url-param>
23412 <url-param name="columns" value="{{columns}}" default_values="[&quot;id&quot ;,&quot;os&quot;,&quot;task&quot;,&quot;status&quot;]" multi="">
23413 </url-param>
23414 <url-param name="query" value="{{_query}}" default_value="">
23415 </url-param>
23416 <url-param name="verbose" value="{{verbose}}">
23417 </url-param>
23418 <url-param name="limit" default_value="200" value="{{limit}}">
23419 </url-param>
23420
23421 <div class="container horizontal layout">
23422
23423
23424 <div class="narrow-down-selector">
23425 <div>
23426 <paper-input id="filter" label="Search columns and filters" placeholde r="gpu nvidia" value="{{_query}}">
23427 </paper-input>
23428 </div>
23429
23430 <div class="selector side-by-side" title="This shows all bot dimension n ames and other interesting bot properties. Mark the check box to add as a column . Select the row to see filter options.">
23431 <iron-selector attr-for-selected="label" selected="{{_primarySelected} }">
23432 <template is="dom-repeat" items="[[_primaryItems]]" as="item">
23433 <div class="selectable item horizontal layout" label="[[item]]">
23434
23435 <span>[[_beforeBold(item,_query)]]<span class="bold">[[_bold(ite m,_query)]]</span>[[_afterBold(item,_query)]]</span>
23436 <span class="flex"></span>
23437 <paper-checkbox noink="" disabled$="[[_cantToggleColumn(item)]]" checked="[[_columnState(item,columns.*)]]" on-change="_toggleColumn">
23438 </paper-checkbox>
23439 </div>
23440 </template>
23441 </iron-selector>
23442 </div>
23443
23444 <div class="selector side-by-side" title="These are all options (if any) that the bot list can be filtered on.">
23445 <template is="dom-repeat" id="secondaryList" items="[[_secondaryItems] ]" as="item">
23446 <div class="item horizontal layout" label="[[item]]">
23447
23448 <span>[[_beforeBold(item,_query)]]<span class="bold">[[_bold(item, _query)]]</span>[[_afterBold(item,_query)]]</span>
23449 <span class="flex"></span>
23450 <iron-icon class="icons" icon="icons:arrow-forward" hidden="[[_can tAddFilter(_primarySelected,item,_filters.*)]]" on-tap="_addFilter">
23451 </iron-icon>
23452 </div>
23453 </template>
23454 </div>
23455
23456 <div class="selector side-by-side" title="These filters are AND'd togeth er and applied to all bots in
23457 the fleet.">
23458 <template is="dom-repeat" items="[[_filters]]" as="fil">
23459 <div class="item horizontal layout" label="[[fil]]">
23460 <span>[[fil]]</span>
23461 <span class="flex"></span>
23462 <iron-icon class="icons" icon="icons:remove-circle-outline" hidden ="[[_cantRemoveFilter(fil,_filters.*)]]" on-tap="_removeFilter">
23463 </iron-icon>
23464 </div>
23465 </template>
23466 </div>
23467
23468 <div class="side-by-side">
23469 <paper-checkbox checked="{{verbose}}">Verbose Entries</paper-checkbox>
23470 <paper-input id="limit" label="Limit Results" auto-validate="" min="0" max="1000" pattern="[0-9]+" value="{{limit}}">
23471 </paper-input>
23472 </div>
23473 </div>
23474
23475 </div>
23476
23477 </template>
23478 <script>
23479 (function(){
23480 // See query-column-filter for more documentation on these properties.
23481 var filterMap = {
23482 disk_space: function(bot, space) {
23483 return true;
23484 },
23485 id: function(bot, id) {
23486 return true;
23487 },
23488 status: function(bot, status) {
23489 if (status === "quarantined") {
23490 return bot.quarantined;
23491 } else if (status === "dead") {
23492 return bot.is_dead;
23493 } else {
23494 // Status must be "alive".
23495 return !bot.quarantined && !bot.is_dead;
23496 }
23497 },
23498 task: function(bot, task) {
23499 if (task === "idle") {
23500 return this._taskId(bot) === "idle";
23501 }
23502 // Task must be "busy".
23503 return this._taskId(bot) !== "idle";
23504 }
23505 };
23506
23507 Polymer({
23508 is: "bot-filters",
23509
23510 behaviors: [
23511 SwarmingBehaviors.BotListBehavior,
23512 SwarmingBehaviors.QueryColumnFilter,
23513 ],
23514
23515 properties: {
23516 // url-param doesn't like columns to be defined in query_column-filter,
23517 // so we define it here.
23518 columns: {
23519 type: Array,
23520 notify: true,
23521 },
23522 query_params: {
23523 type: Object,
23524 computed: "_extractQueryParams(dimensions.*,_filters.*, limit)",
23525 notify: true,
23526 },
23527 verbose: {
23528 type: Boolean,
23529 notify: true,
23530 },
23531
23532 // for QueryColumnFilter
23533 _filterMap: {
23534 type: Object,
23535 value: function() {
23536 var base = this._commonFilters();
23537 for (var attr in filterMap) {
23538 base[attr] = filterMap[attr];
23539 }
23540 return base;
23541 },
23542 }
23543 },
23544
23545 _cantToggleColumn: function(col) {
23546 // Don't allow the id column to be removed, as the bot list is basically
23547 // meaningless without it.
23548 return !col || col === "id" ;
23549 },
23550
23523 _extractQueryParams: function() { 23551 _extractQueryParams: function() {
23524 var params = {}; 23552 var params = {};
23525 var dims = []; 23553 var dims = [];
23526 this._filters.forEach(function(f) { 23554 this._filters.forEach(function(f) {
23527 var split = f.split(FILTER_SEP, 1) 23555 var split = f.split(this.FILTER_SEP, 1)
23528 var col = split[0]; 23556 var col = split[0];
23529 if (this.dimensions.indexOf(col) !== -1) { 23557 if (this.dimensions.indexOf(col) !== -1) {
23530 var rest = f.substring(col.length + FILTER_SEP.length); 23558 var rest = f.substring(col.length + this.FILTER_SEP.length);
23531 dims.push(col + FILTER_SEP + this._unalias(rest)) 23559 dims.push(col + this.FILTER_SEP + swarming.alias.unapply(rest))
23532 } else if (col === "status") { 23560 } else if (col === "status") {
23533 var rest = f.substring(col.length + FILTER_SEP.length); 23561 var rest = f.substring(col.length + this.FILTER_SEP.length);
23534 if (rest === "alive") { 23562 if (rest === "alive") {
23535 params["is_dead"] = "FALSE"; 23563 params["is_dead"] = "FALSE";
23536 params["quarantined"] = "FALSE"; 23564 params["quarantined"] = "FALSE";
23537 } else if (rest === "quarantined") { 23565 } else if (rest === "quarantined") {
23538 params["quarantined"] = "TRUE"; 23566 params["quarantined"] = "TRUE";
23539 } else if (rest === "dead") { 23567 } else if (rest === "dead") {
23540 params["is_dead"] = "TRUE"; 23568 params["is_dead"] = "TRUE";
23541 } 23569 }
23542 } 23570 }
23543 }.bind(this)); 23571 }.bind(this));
23544 params["dimensions"] = dims; 23572 params["dimensions"] = dims;
23545 var lim = Math.floor(this.limit) 23573 var lim = Math.floor(this.limit)
23546 if (Number.isInteger(lim)) { 23574 if (Number.isInteger(lim)) {
23547 // Clamp the limit 23575 // Clamp the limit
23548 lim = Math.max(lim, 1); 23576 lim = Math.max(lim, 1);
23549 lim = Math.min(1000, lim); 23577 lim = Math.min(1000, lim);
23550 params["limit"] = lim; 23578 params["limit"] = lim;
23551 // not !-- because limit could be "900" 23579 // not !== because limit could be a string, e.g. "900"
23552 if (this.limit != lim) { 23580 if (this.limit != lim) {
23553 this.set("limit", lim); 23581 this.set("limit", lim);
23554 } 23582 }
23555 } 23583 }
23556 return params; 23584 return params;
23557 } 23585 }
23558 23586
23559 }); 23587 });
23560 })(); 23588 })();
23561 </script> 23589 </script>
23562 </dom-module><dom-module id="bot-list-data" assetpath="/res/imp/botlist/"> 23590 </dom-module><dom-module id="bot-list-data" assetpath="/res/imp/botlist/">
23563 <template> 23591 <template>
23564 <iron-ajax id="botlist" url="/_ah/api/swarming/v1/bots/list" headers="[[auth _headers]]" params="[[query_params]]" handle-as="json" last-response="{{_list}}" loading="{{_busy1}}"> 23592 <iron-ajax id="botlist" url="/_ah/api/swarming/v1/bots/list" headers="[[auth _headers]]" params="[[query_params]]" handle-as="json" last-response="{{_list}}" loading="{{_busy1}}">
23565 </iron-ajax> 23593 </iron-ajax>
23566 23594
23567 <iron-ajax id="dimensions" url="/_ah/api/swarming/v1/bots/dimensions" header s="[[auth_headers]]" handle-as="json" last-response="{{_dimensions}}" loading="{ {_busy2}}"> 23595 <iron-ajax id="dimensions" url="/_ah/api/swarming/v1/bots/dimensions" header s="[[auth_headers]]" handle-as="json" last-response="{{_dimensions}}" loading="{ {_busy2}}">
23568 </iron-ajax> 23596 </iron-ajax>
23569 23597
23570 <iron-ajax id="fleet" url="/_ah/api/swarming/v1/bots/count" headers="[[auth_ headers]]" handle-as="json" last-response="{{_count}}" loading="{{_busy3}}"> 23598 <iron-ajax id="fleet" url="/_ah/api/swarming/v1/bots/count" headers="[[auth_ headers]]" handle-as="json" last-response="{{_count}}" loading="{{_busy3}}">
23571 </iron-ajax> 23599 </iron-ajax>
23572 </template> 23600 </template>
23573 <script> 23601 <script>
23574 (function(){ 23602 (function(){
23575 var BLACKLIST_DIMENSIONS = ["quarantined", "error"]; 23603 var BLACKLIST_DIMENSIONS = ["quarantined", "error"];
23576 23604
23577 Polymer({ 23605 Polymer({
23578 is: 'bot-list-data', 23606 is: 'bot-list-data',
23579 23607
23580 behaviors: [SwarmingBehaviors.BotListBehavior], 23608 behaviors: [
23609 SwarmingBehaviors.BotListBehavior,
23610 ],
23581 23611
23582 properties: { 23612 properties: {
23583 // inputs 23613 // inputs
23584 auth_headers: { 23614 auth_headers: {
23585 type: Object, 23615 type: Object,
23586 observer: "signIn", 23616 observer: "signIn",
23587 }, 23617 },
23588 query_params: { 23618 query_params: {
23589 type: Object, 23619 type: Object,
23590 }, 23620 },
(...skipping 13 matching lines...) Expand all
23604 type: Array, 23634 type: Array,
23605 computed: "_makeArray(_dimensions)", 23635 computed: "_makeArray(_dimensions)",
23606 notify: true, 23636 notify: true,
23607 }, 23637 },
23608 fleet: { 23638 fleet: {
23609 type: Object, 23639 type: Object,
23610 computed: "_fleet(_count)", 23640 computed: "_fleet(_count)",
23611 notify: true, 23641 notify: true,
23612 }, 23642 },
23613 primary_map: { 23643 primary_map: {
23614 type:Object, 23644 type: Object,
23615 computed: "_primaryMap(_dimensions)", 23645 computed: "_primaryMap(_dimensions)",
23616 notify: true, 23646 notify: true,
23617 }, 23647 },
23618 primary_arr: { 23648 primary_arr: {
23619 type: Array, 23649 type: Array,
23620 //BOT_PROPERTIES is inherited from BotListBehavior 23650 //BOT_PROPERTIES is inherited from BotListBehavior
23621 computed: "_primaryArr(dimensions, BOT_PROPERTIES)", 23651 computed: "_primaryArr(dimensions, BOT_PROPERTIES)",
23622 notify: true, 23652 notify: true,
23623 }, 23653 },
23624 23654
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
23691 _makeArray: function(dimObj) { 23721 _makeArray: function(dimObj) {
23692 if (!dimObj || !dimObj.bots_dimensions) { 23722 if (!dimObj || !dimObj.bots_dimensions) {
23693 return []; 23723 return [];
23694 } 23724 }
23695 var dims = []; 23725 var dims = [];
23696 dimObj.bots_dimensions.forEach(function(d){ 23726 dimObj.bots_dimensions.forEach(function(d){
23697 if (BLACKLIST_DIMENSIONS.indexOf(d.key) === -1) { 23727 if (BLACKLIST_DIMENSIONS.indexOf(d.key) === -1) {
23698 dims.push(d.key); 23728 dims.push(d.key);
23699 } 23729 }
23700 }); 23730 });
23731 dims.push("id");
23701 dims.sort(); 23732 dims.sort();
23702 return dims; 23733 return dims;
23703 }, 23734 },
23704 23735
23705 _primaryArr: function(dimensions, properties) { 23736 _primaryArr: function(dimensions, properties) {
23706 return dimensions.concat(properties); 23737 return dimensions.concat(properties);
23707 }, 23738 },
23708 23739
23709 _primaryMap: function(dimensions){ 23740 _primaryMap: function(dimensions){
23710 // pMap will have a list of columns to available values (primary key 23741 // pMap will have a list of columns to available values (primary key
23711 // to secondary values). This includes bot dimensions, but also 23742 // to secondary values). This includes bot dimensions, but also
23712 // includes state like disk_space, quarantined, busy, etc. 23743 // includes state like disk_space, quarantined, busy, etc.
23713 dimensions = dimensions.bots_dimensions; 23744 dimensions = dimensions.bots_dimensions;
23714 23745
23715 var pMap = {}; 23746 var pMap = {};
23716 dimensions.forEach(function(d){ 23747 dimensions.forEach(function(d){
23717 if (this.DIMENSIONS_WITH_ALIASES.indexOf(d.key) === -1) { 23748 if (swarming.alias.DIMENSIONS_WITH_ALIASES.indexOf(d.key) === -1) {
23718 // value is an array of all seen values for the dimension d.key 23749 // value is an array of all seen values for the dimension d.key
23719 pMap[d.key] = d.value; 23750 pMap[d.key] = d.value;
23720 } else if (d.key === "gpu") { 23751 } else if (d.key === "gpu") {
23721 var gpus = []; 23752 var gpus = [];
23722 d.value.forEach(function(g){ 23753 d.value.forEach(function(g){
23723 var alias = this._gpuAlias(g); 23754 var alias = swarming.alias.gpu(g);
23724 if (alias !== "unknown") { 23755 if (alias !== "unknown") {
23725 gpus.push(this._applyAlias(g, alias)); 23756 gpus.push(swarming.alias.apply(g, alias));
23726 } else { 23757 } else {
23727 gpus.push(g); 23758 gpus.push(g);
23728 } 23759 }
23729 }.bind(this)); 23760 }.bind(this));
23730 pMap["gpu"] = gpus; 23761 pMap["gpu"] = gpus;
23731 } else if (d.key === "device_type") { 23762 } else if (d.key === "device_type") {
23732 var devs = []; 23763 var devs = [];
23733 d.value.forEach(function(dt){ 23764 d.value.forEach(function(dt){
23734 var alias = this._androidAlias(dt); 23765 var alias = swarming.alias.android(dt);
23735 if (alias !== "unknown") { 23766 if (alias !== "unknown") {
23736 devs.push(this._applyAlias(dt, alias)); 23767 devs.push(swarming.alias.apply(dt, alias));
23737 } else { 23768 } else {
23738 devs.push(dt); 23769 devs.push(dt);
23739 } 23770 }
23740 }.bind(this)); 23771 }.bind(this));
23741 pMap["device_type"] = devs; 23772 pMap["device_type"] = devs;
23742 } else { 23773 } else {
23743 console.log("Unknown alias type: ", d); 23774 console.log("Unknown alias type: ", d);
23744 } 23775 }
23745 }.bind(this)); 23776 });
23746 23777
23747 // Add some options that might not show up. 23778 // Add some options that might not show up.
23748 pMap["android_devices"].push("0"); 23779 pMap["android_devices"].push("0");
23749 pMap["device_os"].push("none"); 23780 pMap["device_os"].push("none");
23750 pMap["device_type"].push("none"); 23781 pMap["device_type"].push("none");
23751 23782
23752 pMap["id"] = []; 23783 pMap["id"] = [];
23753 23784
23754 // Create custom filter options 23785 // Create custom filter options
23755 pMap["disk_space"] = []; 23786 pMap["disk_space"] = [];
(...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after
24095 "device_type": "Device Type", 24126 "device_type": "Device Type",
24096 "disk_space": "Free Space (MB)", 24127 "disk_space": "Free Space (MB)",
24097 "gpu": "GPU", 24128 "gpu": "GPU",
24098 "os": "OS", 24129 "os": "OS",
24099 "pool": "Pool", 24130 "pool": "Pool",
24100 "status": "Status", 24131 "status": "Status",
24101 "xcode_version": "XCode Version", 24132 "xcode_version": "XCode Version",
24102 }; 24133 };
24103 24134
24104 var columnMap = { 24135 var columnMap = {
24105 android_devices: function(bot) {
24106 var devs = this._attribute(bot, "android_devices", "0");
24107 if (this._verbose) {
24108 return devs.join(" | ") + " devices available";
24109 }
24110 // max() works on strings as long as they can be coerced to Number.
24111 return Math.max(...devs) + " devices available";
24112 },
24113 device_type: function(bot) {
24114 var dt = this._attribute(bot, "device_type", "none");
24115 dt = dt[0];
24116 var alias = this._androidAlias(dt);
24117 if (alias === "unknown") {
24118 return dt;
24119 }
24120 return this._applyAlias(dt, alias);
24121 },
24122 disk_space: function(bot) { 24136 disk_space: function(bot) {
24123 var aliased = []; 24137 var aliased = [];
24124 bot.disks.forEach(function(disk){ 24138 bot.disks.forEach(function(disk){
24125 var alias = sk.human.bytes(disk.mb, swarming.MB); 24139 var alias = sk.human.bytes(disk.mb, swarming.MB);
24126 aliased.push(this._applyAlias(disk.mb, disk.id + " "+ alias)); 24140 aliased.push(swarming.alias.apply(disk.mb, disk.id + " "+ alias));
24127 }.bind(this)); 24141 }.bind(this));
24128 if (this._verbose) { 24142 if (this._verbose) {
24129 return aliased.join(" | "); 24143 return aliased.join(" | ");
24130 } 24144 }
24131 return aliased[0]; 24145 return aliased[0];
24132 }, 24146 },
24133 gpu: function(bot){
24134 var gpus = this._attribute(bot, "gpu", "none")
24135 var verbose = []
24136 var named = [];
24137 // non-verbose mode has only the top level GPU info "e.g. NVidia"
24138 // which is found by looking for gpu ids w/o a colon.
24139 gpus.forEach(function(g){
24140 var alias = this._gpuAlias(g);
24141 if (alias === "unknown") {
24142 verbose.push(g);
24143 if (g.indexOf(":") === -1) {
24144 named.push(g);
24145 }
24146 return;
24147 }
24148 verbose.push(this._applyAlias(g, alias));
24149 if (g.indexOf(":") === -1) {
24150 named.push(this._applyAlias(g, alias));
24151 }
24152 }.bind(this))
24153 if (this._verbose) {
24154 return verbose.join(" | ");
24155 }
24156 return named.join(" | ");
24157 },
24158 id: function(bot) { 24147 id: function(bot) {
24159 return bot.bot_id; 24148 return bot.bot_id;
24160 }, 24149 },
24161 pool: function(bot) {
24162 var pool = this._attribute(bot, "pool");
24163 return pool.join(" | ");
24164 },
24165 status: function(bot) { 24150 status: function(bot) {
24166 // If a bot is both dead and quarantined, show the deadness over the 24151 // If a bot is both dead and quarantined, show the deadness over the
24167 // quarentinedness. 24152 // quarentinedness.
24168 if (bot.is_dead) { 24153 if (bot.is_dead) {
24169 return "Dead. Last seen " + sk.human.diffDate(bot.last_seen_ts) + 24154 return "Dead. Last seen " + sk.human.diffDate(bot.last_seen_ts) +
24170 " ago"; 24155 " ago";
24171 } 24156 }
24172 if (bot.quarantined) { 24157 if (bot.quarantined) {
24173 return "Quarantined: " + this._attribute(bot, "quarantined"); 24158 var msg = this._state(bot, "quarantined")[0];
24159 // Sometimes, the quarantined message is actually in "error". This
24160 // happens when the bot code has thrown an exception.
24161 if (msg === "unknown" || msg === "true" || msg === true) {
24162 msg = this._attribute(bot, "error");
24163 }
24164 return "Quarantined: " + msg;
24174 } 24165 }
24175 return "Alive"; 24166 return "Alive";
24176 }, 24167 },
24177 task: function(bot){ 24168 task: function(bot){
24178 return this._taskId(bot); 24169 return this._taskId(bot);
24179 }, 24170 },
24180 }; 24171 };
24181 24172
24182 var deviceColumnMap = { 24173 var deviceColumnMap = {
24183 android_devices: function(device) { 24174 android_devices: function(device) {
24184 var str = this._androidAliasDevice(device); 24175 var str = this._androidAliasDevice(device);
24185 if (device.okay) { 24176 if (device.okay) {
24186 str = this._applyAlias(this._deviceType(device), str); 24177 str = swarming.alias.apply(this._deviceType(device), str);
24187 } 24178 }
24188 str += " S/N:"; 24179 str += " S/N:";
24189 str += device.serial; 24180 str += device.serial;
24190 return str; 24181 return str;
24191 }, 24182 },
24192 device_os: function(device) { 24183 device_os: function(device) {
24193 if (device.build) { 24184 if (device.build) {
24194 return device.build["build.id"]; 24185 return device.build["build.id"];
24195 } 24186 }
24196 return "unknown"; 24187 return "unknown";
(...skipping 16 matching lines...) Expand all
24213 disk_space: function(dir, botA, botB) { 24204 disk_space: function(dir, botA, botB) {
24214 // We sort based on the raw number of MB of the first disk. 24205 // We sort based on the raw number of MB of the first disk.
24215 var botACol = botA.disks[0].mb; 24206 var botACol = botA.disks[0].mb;
24216 var botBCol = botB.disks[0].mb;; 24207 var botBCol = botB.disks[0].mb;;
24217 return dir * swarming.naturalCompare(botACol, botBCol); 24208 return dir * swarming.naturalCompare(botACol, botBCol);
24218 }, 24209 },
24219 }; 24210 };
24220 24211
24221 Polymer({ 24212 Polymer({
24222 is: 'bot-list', 24213 is: 'bot-list',
24223 behaviors: [SwarmingBehaviors.BotListBehavior, 24214
24224 SwarmingBehaviors.DynamicTableBehavior], 24215 // The order behaviors are applied in matters - later ones overwrite
24216 // attributes of earlier ones
24217 behaviors: [
24218 SwarmingBehaviors.BotListBehavior,
24219 SwarmingBehaviors.DynamicTableBehavior,
24220 ],
24225 24221
24226 properties: { 24222 properties: {
24227 client_id: { 24223 client_id: {
24228 type: String, 24224 type: String,
24229 }, 24225 },
24230 24226
24231 // For dynamic table. 24227 // For dynamic table.
24232 _columnMap: { 24228 _columnMap: {
24233 type: Object, 24229 type: Object,
24234 value: columnMap, 24230 value: function() {
24231 var base = this._commonColumns();
24232 for (var attr in columnMap) {
24233 base[attr] = columnMap[attr];
24234 }
24235 return base;
24236 },
24235 }, 24237 },
24236 _headerMap: { 24238 _headerMap: {
24237 type: Object, 24239 type: Object,
24238 value: headerMap, 24240 value: headerMap,
24239 }, 24241 },
24240 _specialColumns: { 24242 _specialColumns: {
24241 type: Array, 24243 type: Array,
24242 value: specialColumns, 24244 value: specialColumns,
24243 }, 24245 },
24244 _specialSort: { 24246 _specialSort: {
(...skipping 16 matching lines...) Expand all
24261 _botLink: function(id) { 24263 _botLink: function(id) {
24262 // TODO(kjlubick) Make this point to /newui/ when appropriate. 24264 // TODO(kjlubick) Make this point to /newui/ when appropriate.
24263 return "/restricted/bot/"+id; 24265 return "/restricted/bot/"+id;
24264 }, 24266 },
24265 24267
24266 24268
24267 _androidAliasDevice: function(device) { 24269 _androidAliasDevice: function(device) {
24268 if (device.notReady) { 24270 if (device.notReady) {
24269 return UNAUTHENTICATED.toUpperCase(); 24271 return UNAUTHENTICATED.toUpperCase();
24270 } 24272 }
24271 return this._androidAlias(this._deviceType(device)); 24273 return swarming.alias.android(this._deviceType(device));
24272 }, 24274 },
24273 24275
24274 _deviceColumn: function(col, device) { 24276 _deviceColumn: function(col, device) {
24275 var f = deviceColumnMap[col]; 24277 var f = deviceColumnMap[col];
24276 if (!f || !device) { 24278 if (!f || !device) {
24277 return ""; 24279 return "";
24278 } 24280 }
24279 return f.bind(this)(device); 24281 return f.bind(this)(device);
24280 }, 24282 },
24281 24283
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
24346 <iron-ajax id="tasklist" url="/_ah/api/swarming/v1/tasks/list" headers="[[au th_headers]]" params="[[query_params]]" handle-as="json" last-response="{{_list} }" loading="{{_busy1}}"> 24348 <iron-ajax id="tasklist" url="/_ah/api/swarming/v1/tasks/list" headers="[[au th_headers]]" params="[[query_params]]" handle-as="json" last-response="{{_list} }" loading="{{_busy1}}">
24347 </iron-ajax> 24349 </iron-ajax>
24348 24350
24349 24351
24350 </template> 24352 </template>
24351 <script> 24353 <script>
24352 (function(){ 24354 (function(){
24353 Polymer({ 24355 Polymer({
24354 is: 'task-list-data', 24356 is: 'task-list-data',
24355 24357
24356 behaviors: [SwarmingBehaviors.SwarmingBehavior], 24358 behaviors: [SwarmingBehaviors.CommonBehavior],
24357 24359
24358 properties: { 24360 properties: {
24359 // inputs 24361 // inputs
24360 auth_headers: { 24362 auth_headers: {
24361 type: Object, 24363 type: Object,
24362 observer: "signIn", 24364 observer: "signIn",
24363 }, 24365 },
24364 query_params: { 24366 query_params: {
24365 type: Object, 24367 type: Object,
24366 }, 24368 },
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
24483 var specialColumns = ["name", "state"]; 24485 var specialColumns = ["name", "state"];
24484 var columnMap = {}; 24486 var columnMap = {};
24485 var headerMap = { 24487 var headerMap = {
24486 "user": "Requesting User", 24488 "user": "Requesting User",
24487 }; 24489 };
24488 var specialSort = {}; 24490 var specialSort = {};
24489 24491
24490 Polymer({ 24492 Polymer({
24491 is: 'task-list', 24493 is: 'task-list',
24492 behaviors: [ 24494 behaviors: [
24493 SwarmingBehaviors.SwarmingBehavior,
24494 SwarmingBehaviors.DynamicTableBehavior, 24495 SwarmingBehaviors.DynamicTableBehavior,
24495 ], 24496 ],
24496 24497
24497 properties: { 24498 properties: {
24498 client_id: { 24499 client_id: {
24499 type: String, 24500 type: String,
24500 }, 24501 },
24501 24502
24502 // For dynamic table. 24503 // For dynamic table.
24503 _columnMap: { 24504 _columnMap: {
(...skipping 29 matching lines...) Expand all
24533 24534
24534 _taskClass: function(task) { 24535 _taskClass: function(task) {
24535 // TODO(kjlubick): Color tasks? 24536 // TODO(kjlubick): Color tasks?
24536 return ""; 24537 return "";
24537 } 24538 }
24538 24539
24539 }); 24540 });
24540 })(); 24541 })();
24541 </script> 24542 </script>
24542 </dom-module></div></body></html> 24543 </dom-module></div></body></html>
OLDNEW
« no previous file with comments | « appengine/swarming/elements/Makefile ('k') | appengine/swarming/elements/build/js/js.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698