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

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: Documentation 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
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 14681 matching lines...) Expand 10 before | Expand all | Expand 10 after
14692 type: String, 14692 type: String,
14693 } 14693 }
14694 }, 14694 },
14695 14695
14696 signIn: function(){ 14696 signIn: function(){
14697 this.$.request.generateRequest(); 14697 this.$.request.generateRequest();
14698 }, 14698 },
14699 14699
14700 }); 14700 });
14701 </script> 14701 </script>
14702 </dom-module><dom-module id="dynamic-table-style" assetpath="/res/imp/common/"> 14702 </dom-module><script>
14703 (function(){
14704 var ANDROID_ALIASES = {
14705 "bullhead": "Nexus 5X",
14706 "flo": "Nexus 7 (2013)",
14707 "flounder": "Nexus 9",
14708 "foster": "NVIDIA Shield",
14709 "fugu": "Nexus Player",
14710 "grouper": "Nexus 7 (2012)",
14711 "hammerhead": "Nexus 5",
14712 "m0": "Galaxy S3",
14713 "mako": "Nexus 4",
14714 "manta": "Nexus 10",
14715 "shamu": "Nexus 6",
14716 "sprout": "Android One",
14717 };
14718
14719 var UNKNOWN = "unknown";
14720
14721 var GPU_ALIASES = {
14722 "1002": "AMD",
14723 "1002:6779": "AMD Radeon HD 6450/7450/8450",
14724 "1002:6821": "AMD Radeon HD 8870M",
14725 "1002:683d": "AMD Radeon HD 7770/8760",
14726 "1002:9830": "AMD Radeon HD 8400",
14727 "102b": "Matrox",
14728 "102b:0522": "Matrox MGA G200e",
14729 "102b:0532": "Matrox MGA G200eW",
14730 "102b:0534": "Matrox G200eR2",
14731 "10de": "NVIDIA",
14732 "10de:08a4": "NVIDIA GeForce 320M",
14733 "10de:08aa": "NVIDIA GeForce 320M",
14734 "10de:0fe9": "NVIDIA GeForce GT 750M Mac Edition",
14735 "10de:104a": "NVIDIA GeForce GT 610",
14736 "10de:11c0": "NVIDIA GeForce GTX 660",
14737 "10de:1244": "NVIDIA GeForce GTX 550 Ti",
14738 "10de:1401": "NVIDIA GeForce GTX 960",
14739 "8086": "Intel",
14740 "8086:0412": "Intel Haswell Integrated",
14741 "8086:041a": "Intel Xeon Integrated",
14742 "8086:0a2e": "Intel Haswell Integrated",
14743 "8086:0d26": "Intel Crystal Well Integrated",
14744 "8086:22b1": "Intel Braswell Integrated",
14745 }
14746
14747 // For consistency, all aliases are displayed like:
14748 // Nexus 5X (bullhead)
14749 // This regex matches a string like "ALIAS (ORIG)", with ORIG as group 1.
14750 var ALIAS_REGEXP = /.+ \((.*)\)/;
14751
14752 // This behavior wraps up all the shared bot-list functionality by
14753 // extending SwarmingBehaviors.SwarmingBehavior
14754 SwarmingBehaviors.Aliases = {
14755
14756 properties: {
14757 DIMENSIONS_WITH_ALIASES: {
14758 type: Array,
14759 value: function(){
14760 return ["device_type", "gpu"];
14761 },
14762 },
14763 },
14764
14765 _androidAlias: function(dt) {
14766 return ANDROID_ALIASES[dt] || UNKNOWN;
14767 },
14768
14769 // _applyAlias is the consistent way to modify a string to show its alias.
14770 _applyAlias: function(orig, alias) {
14771 return alias +" ("+orig+")";
14772 },
14773
14774 _gpuAlias: function(gpu) {
14775 return GPU_ALIASES[gpu] || UNKNOWN;
14776 },
14777
14778 // _unalias will return the base dimension/state with its alias removed
14779 // if it had one. This is handy for sorting and filtering.
14780 _unalias: function(str) {
14781 var match = ALIAS_REGEXP.exec(str);
14782 if (match) {
14783 return match[1];
14784 }
14785 return str;
14786 },
14787 };
14788 })()
14789 </script>
14790 <dom-module id="dynamic-table-style" assetpath="/res/imp/common/">
14703 <template> 14791 <template>
14704 <style> 14792 <style>
14705 table { 14793 table {
14706 border-collapse: collapse; 14794 border-collapse: collapse;
14707 margin-left: 5px; 14795 margin-left: 5px;
14708 } 14796 }
14709 td, th { 14797 td, th {
14710 border: 1px solid #DDD; 14798 border: 1px solid #DDD;
14711 padding: 5px; 14799 padding: 5px;
14712 } 14800 }
14713 th { 14801 th {
14714 position: relative; 14802 position: relative;
14715 } 14803 }
14716 sort-toggle { 14804 sort-toggle {
14717 position: absolute; 14805 position: absolute;
14718 right: 0; 14806 right: 0;
14719 top: 0.4em; 14807 top: 0.4em;
14720 } 14808 }
14721 </style> 14809 </style>
14722 14810
14723 </template> 14811 </template>
14724 </dom-module> 14812 </dom-module>
14725 14813
14726 <script> 14814 <script>
14727 window.SwarmingBehaviors = window.SwarmingBehaviors || {};
14728 (function(){ 14815 (function(){
14729 // This behavior wraps up all the shared swarming functionality. 14816 // This behavior wraps up all the shared swarming functionality.
14730 SwarmingBehaviors.DynamicTableBehavior = { 14817 SwarmingBehaviors.DynamicTableBehavior = [SwarmingBehaviors.Aliases, {
14731 14818
14732 properties: { 14819 properties: {
14733 14820
14734 _columns: { 14821 _columns: {
14735 type: Array, 14822 type: Array,
14736 }, 14823 },
14737 14824
14738 _filter: { 14825 _filter: {
14739 type: Function, 14826 type: Function,
14740 }, 14827 },
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
14848 }, 14935 },
14849 // _stripSpecial removes the special columns and sorts the remaining 14936 // _stripSpecial removes the special columns and sorts the remaining
14850 // columns so they always appear in the same order, regardless of 14937 // columns so they always appear in the same order, regardless of
14851 // the order they are added. 14938 // the order they are added.
14852 _stripSpecial: function(){ 14939 _stripSpecial: function(){
14853 return this._columns.filter(function(c) { 14940 return this._columns.filter(function(c) {
14854 return this._specialColumns.indexOf(c) === -1; 14941 return this._specialColumns.indexOf(c) === -1;
14855 }.bind(this)).sort(); 14942 }.bind(this)).sort();
14856 }, 14943 },
14857 14944
14858 }; 14945 // Common columns shared between tasklist and botlist
14946 _commonColumns: function() {
14947 // return a fresh object so all elements have their own copy
14948 return {
14949 android_devices: function(bot) {
14950 var devs = this._attribute(bot, "android_devices", "0");
14951 if (this._verbose) {
14952 return devs.join(" | ") + " devices available";
14953 }
14954 // max() works on strings as long as they can be coerced to Number.
14955 return Math.max(...devs) + " devices available";
14956 },
14957 device_type: function(bot) {
14958 var dt = this._attribute(bot, "device_type", "none");
14959 dt = dt[0];
14960 var alias = this._androidAlias(dt);
14961 if (alias === "unknown") {
14962 return dt;
14963 }
14964 return this._applyAlias(dt, alias);
14965 },
14966 gpu: function(bot){
14967 var gpus = this._attribute(bot, "gpu", "none");
14968 var verbose = []
14969 var named = [];
14970 // non-verbose mode has only the top level GPU info "e.g. NVidia"
14971 // which is found by looking for gpu ids w/o a colon.
14972 gpus.forEach(function(g){
14973 var alias = this._gpuAlias(g);
14974 if (alias === "unknown") {
14975 verbose.push(g);
14976 if (g.indexOf(":") === -1) {
14977 named.push(g);
14978 }
14979 return;
14980 }
14981 verbose.push(this._applyAlias(g, alias));
14982 if (g.indexOf(":") === -1) {
14983 named.push(this._applyAlias(g, alias));
14984 }
14985 }.bind(this))
14986 if (this._verbose || !named.length) {
14987 return verbose.join(" | ");
14988 }
14989 return named.join(" | ");
14990 },
14991 pool: function(bot) {
14992 var pool = this._attribute(bot, "pool");
14993 return pool.join(" | ");
14994 },
14995 };
14996 },
14997
14998
14999 }];
14859 })(); 15000 })();
14860 </script> 15001 </script>
14861 15002
14862 <script> 15003 <script>
14863 15004
14864 (function() { 15005 (function() {
14865 15006
14866 // monostate data 15007 // monostate data
14867 var metaDatas = {}; 15008 var metaDatas = {};
14868 var metaArrays = {}; 15009 var metaArrays = {};
(...skipping 7990 matching lines...) Expand 10 before | Expand all | Expand 10 after
22859 <script> 23000 <script>
22860 Polymer({ 23001 Polymer({
22861 is: 'paper-input', 23002 is: 'paper-input',
22862 23003
22863 behaviors: [ 23004 behaviors: [
22864 Polymer.IronFormElementBehavior, 23005 Polymer.IronFormElementBehavior,
22865 Polymer.PaperInputBehavior 23006 Polymer.PaperInputBehavior
22866 ] 23007 ]
22867 }); 23008 });
22868 </script> 23009 </script>
22869 <script> 23010 <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> 23011 <template>
23037 <style is="custom-style" include="iron-flex iron-flex-alignment iron-positio ning"> 23012 <style>
23038 :host { 23013 :host {
23039 display: block; 23014 display: block;
23040 font-family: sans-serif; 23015 font-family: sans-serif;
23041 } 23016 }
23042 #filter { 23017 #filter {
23043 margin:0 5px; 23018 margin:0 5px;
23044 } 23019 }
23045 23020
23046 .container { 23021 .container {
23047 min-height: 120px; 23022 min-height: 120px;
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
23110 margin: 2px; 23085 margin: 2px;
23111 /* See https://sites.google.com/a/google.com/skia-infrastructure/design- docs/general-design-guidance */ 23086 /* See https://sites.google.com/a/google.com/skia-infrastructure/design- docs/general-design-guidance */
23112 --paper-checkbox-checked-color: black; 23087 --paper-checkbox-checked-color: black;
23113 --paper-checkbox-checked-ink-color: black; 23088 --paper-checkbox-checked-ink-color: black;
23114 --paper-checkbox-unchecked-color: black; 23089 --paper-checkbox-unchecked-color: black;
23115 --paper-checkbox-unchecked-ink-color: black; 23090 --paper-checkbox-unchecked-ink-color: black;
23116 --paper-checkbox-label-color: black; 23091 --paper-checkbox-label-color: black;
23117 } 23092 }
23118 </style> 23093 </style>
23119 23094
23120 <url-param name="filters" value="{{_filters}}" default_values="[]" multi=""> 23095 </template>
23121 </url-param> 23096 </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 23097
23131 <div class="container horizontal layout"> 23098 <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(){ 23099 (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 23100 // Given a space separated list of queries, matchPartCaseInsensitive
23239 // returns an object of any query that matches a part of str, case 23101 // 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. 23102 // insensitive. The object has an idx (index) and the part that matched.
23241 var matchPartCaseInsensitive = function(str, queries) { 23103 var matchPartCaseInsensitive = function(str, queries) {
23242 if (!queries) { 23104 if (!queries) {
23243 return { 23105 return {
23244 idx: 0, 23106 idx: 0,
23245 part: "", 23107 part: "",
23246 }; 23108 };
23247 } 23109 }
(...skipping 12 matching lines...) Expand all
23260 idx: idx, 23122 idx: idx,
23261 part: xq[i], 23123 part: xq[i],
23262 }; 23124 };
23263 } 23125 }
23264 } 23126 }
23265 return { 23127 return {
23266 idx: -1, 23128 idx: -1,
23267 }; 23129 };
23268 }; 23130 };
23269 23131
23270 Polymer({ 23132 // Extend the Aliases behavior
23271 is: "bot-filters", 23133 SwarmingBehaviors.QueryColumnFilter = [SwarmingBehaviors.Aliases, {
23272
23273 behaviors: [SwarmingBehaviors.BotListBehavior],
23274 23134
23275 properties: { 23135 properties: {
23276 // input 23136 // input
23277 primary_map: { 23137 primary_map: {
23278 type: Object, 23138 type: Object,
23279 }, 23139 },
23280 primary_arr: { 23140 primary_arr: {
23281 type: Array, 23141 type: Array,
23282 }, 23142 },
23283 dimensions: { 23143 dimensions: {
23284 type: Array, 23144 type: Array,
23285 }, 23145 },
23286
23287 // output 23146 // output
23288 columns: {
23289 type: Array,
23290 notify: true,
23291 },
23292 filter: { 23147 filter: {
23293 type: Function, 23148 type: Function,
23294 computed: "_makeFilter(_filters.*)", 23149 computed: "_makeFilter(_filters.*)",
23295 notify: true, 23150 notify: true,
23296 }, 23151 },
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 23152
23307 // private 23153 // private
23154 FILTER_SEP: {
23155 type:String,
23156 value: ":",
23157 },
23308 _filters: { 23158 _filters: {
23309 type:Array, 23159 type:Array,
23310 }, 23160 },
23311 _limit: { 23161 _limit: {
23312 type: Number, 23162 type: Number,
23313 }, 23163 },
23314 _primaryItems: { 23164 _primaryItems: {
23315 type: Array, 23165 type: Array,
23316 computed: "_primary(_query, primary_map, primary_arr, columns.*)", 23166 computed: "_primary(_query, primary_map, primary_arr, columns.*)",
23317 }, 23167 },
23318 _primarySelected: { 23168 _primarySelected: {
23319 type: String, 23169 type: String,
23320 value: "", 23170 value: "",
23321 }, 23171 },
23322 // query is treated as a space separated list. 23172 // query is treated as a space separated list.
23323 _query: { 23173 _query: {
23324 type:String, 23174 type:String,
23325 }, 23175 },
23326 _secondaryItems: { 23176 _secondaryItems: {
23327 type: Array, 23177 type: Array,
23328 computed: "_secondary(_primarySelected, _query, primary_map)", 23178 computed: "_secondary(_primarySelected, _query, primary_map)",
23329 }, 23179 },
23180 },
23330 23181
23331 },
23332 23182
23333 _addFilter: function(e) { 23183 _addFilter: function(e) {
23334 // e.model.foo is a way to get access to the "foo" inside a dom-repeat 23184 // 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, 23185 // 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"' 23186 // "foo", is set by the dom-repeat above 'as="foo"'
23337 var filterItem = e.model.item; 23187 var filterItem = e.model.item;
23338 if (this._cantAddFilter(this._primarySelected, filterItem)) { 23188 if (this._cantAddFilter(this._primarySelected, filterItem)) {
23339 return; 23189 return;
23340 } 23190 }
23341 var filter = this._primarySelected + FILTER_SEP + filterItem; 23191 var filter = this._primarySelected + this.FILTER_SEP + filterItem;
23342 this.push("_filters", filter); 23192 this.push("_filters", filter);
23343 }, 23193 },
23344 23194
23345 _removeFilter: function(e){ 23195 _removeFilter: function(e){
23346 var filter = e.model.fil; 23196 var filter = e.model.fil;
23347 if (this._cantRemoveFilter(filter)){ 23197 if (this._cantRemoveFilter(filter)){
23348 return; 23198 return;
23349 } 23199 }
23350 var idx = this._filters.indexOf(filter); 23200 var idx = this._filters.indexOf(filter);
23351 if (idx !== -1) { 23201 if (idx !== -1) {
23352 this.splice("_filters", idx, 1); 23202 this.splice("_filters", idx, 1);
23353 } 23203 }
23354 }, 23204 },
23355 23205
23356 _cantAddFilter: function(primarySelected, filterItem) { 23206 _cantAddFilter: function(primarySelected, filterItem) {
23357 // Check that everything is selected and this filter isn't already in 23207 // Check that everything is selected and this filter isn't already in
23358 // the array. 23208 // the array.
23359 if (!primarySelected || !filterItem) { 23209 if (!primarySelected || !filterItem) {
23360 return true; 23210 return true;
23361 } 23211 }
23362 var filter = primarySelected + FILTER_SEP + filterItem; 23212 var filter = primarySelected + this.FILTER_SEP + filterItem;
23363 return this._filters.indexOf(filter) !== -1; 23213 return this._filters.indexOf(filter) !== -1;
23364 }, 23214 },
23365 23215
23366 _cantRemoveFilter: function(filter) { 23216 _cantRemoveFilter: function(filter) {
23367 return !filter || this._filters.indexOf(filter) === -1; 23217 return !filter || this._filters.indexOf(filter) === -1;
23368 }, 23218 },
23369 23219
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() { 23220 _makeFilter: function() {
23400 // All filters will be AND'd together. 23221 // All filters will be AND'd together.
23401 // filterGroups will be a map of primary (i.e. column) -> array of 23222 // filterGroups will be a map of primary (i.e. column) -> array of
23402 // options that should be filtered to. 23223 // options that should be filtered to.
23403 // e.g. "os" -> ["Windows", "Linux"] 23224 // e.g. "os" -> ["Windows", "Linux"]
23404 // Since they will be or'd together, order doesn't matter. 23225 // Since they will be or'd together, order doesn't matter.
23405 var filterGroups = {}; 23226 var filterGroups = {};
23406 this._filters.forEach(function(filterString){ 23227 this._filters.forEach(function(filterString){
23407 var idx = filterString.indexOf(FILTER_SEP); 23228 var idx = filterString.indexOf(this.FILTER_SEP);
23408 var primary = filterString.slice(0, idx); 23229 var primary = filterString.slice(0, idx);
23409 var param = filterString.slice(idx + FILTER_SEP.length); 23230 var param = filterString.slice(idx + this.FILTER_SEP.length);
23410 var arr = filterGroups[primary] || []; 23231 var arr = filterGroups[primary] || [];
23411 arr.push(param); 23232 arr.push(param);
23412 filterGroups[primary] = arr; 23233 filterGroups[primary] = arr;
23413 }); 23234 }.bind(this));
23235 var filterMap = this._filterMap || {};
23414 return function(bot){ 23236 return function(bot){
23415 var retVal = true; 23237 var retVal = true;
23416 // Look up all the primary keys we are filter by, then look up how 23238 // 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 23239 // to filter (in filterMap) and apply the filter for each filter
23418 // option. 23240 // option.
23419 for (primary in filterGroups){ 23241 for (primary in filterGroups){
23420 var params = filterGroups[primary]; 23242 var params = filterGroups[primary];
23421 var filter = filterMap[primary]; 23243 var filter = filterMap[primary];
23422 if (!filter) { 23244 if (!filter) {
23423 filter = function(bot, c) { 23245 filter = function(bot, c) {
23424 var o = this._attribute(bot, primary); 23246 var o = this._attribute(bot, primary);
23425 return o.indexOf(c) !== -1; 23247 return o.indexOf(c) !== -1;
23426 }.bind(this); 23248 }.bind(this);
23427 } 23249 }
23428 if (filter) { 23250 if (filter) {
23429 params.forEach(function(param){ 23251 params.forEach(function(param){
23430 retVal = retVal && filter.bind(this)(bot,param); 23252 retVal = retVal && filter.bind(this)(bot,param);
23431 }.bind(this)); 23253 }.bind(this));
23432 } 23254 }
23433 } 23255 }
23434 return retVal; 23256 return retVal;
23435 } 23257 }
23436 }, 23258 },
23437 23259
23260 _toggleColumn: function(e) {
23261 var col = e.model.item;
23262
23263 if (this._cantToggleColumn(col)) {
23264 return;
23265 }
23266 if (this._columnState(col)) {
23267 var idx = this.columns.indexOf(col);
23268 if (idx !== -1) {
23269 this.splice("columns", idx, 1);
23270 }
23271 return;
23272 }
23273 this.push("columns", col);
23274 },
23275
23276 _cantToggleColumn: function(col) {
23277 // Clients can override this
23278 return false;
23279 },
23280
23281 _columnState: function(col) {
23282 if (!col) {
23283 return false;
23284 }
23285 return this.columns.indexOf(col) !== -1;
23286 },
23287
23288
23438 _primary: function(query, primary_map, primary_arr) { 23289 _primary: function(query, primary_map, primary_arr) {
23439 // If the user has typed in a query, only show those primary keys that 23290 // 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 23291 // partially match the query or that have secondary values which
23441 // partially match. 23292 // partially match.
23442 var arr = this.primary_arr.filter(function(s){ 23293 var arr = this.primary_arr.filter(function(s){
23443 if (matchPartCaseInsensitive(s, query).idx !== -1) { 23294 if (matchPartCaseInsensitive(s, query).idx !== -1) {
23444 return true; 23295 return true;
23445 } 23296 }
23446 var opts = primary_map[s]; 23297 var opts = primary_map[s];
23447 for (var i = 0; i < opts.length; i++) { 23298 for (var i = 0; i < opts.length; i++) {
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
23513 }, 23364 },
23514 23365
23515 _afterBold: function(item, query) { 23366 _afterBold: function(item, query) {
23516 var match = matchPartCaseInsensitive(item, query); 23367 var match = matchPartCaseInsensitive(item, query);
23517 if (match.idx === -1) { 23368 if (match.idx === -1) {
23518 return ""; 23369 return "";
23519 } 23370 }
23520 return item.substring(match.idx + match.part.length); 23371 return item.substring(match.idx + match.part.length);
23521 }, 23372 },
23522 23373
23374 // Common filters shared between tasklist and botlist
23375 _commonFilters: function() {
23376 // return a fresh object so all elements have their own copy
23377 return {
23378 android_devices: function(bot, num) {
23379 var o = this._attribute(bot, "android_devices", "0");
23380 return o.indexOf(num) !== -1;
23381 },
23382 device_os: function(bot, os) {
23383 var o = this._attribute(bot, "device_os", "none");
23384 return o.indexOf(os) !== -1;
23385 },
23386 device_type: function(bot, dt) {
23387 var o = this._attribute(bot, "device_type", "none");
23388 return o.indexOf(this._unalias(dt)) !== -1;
23389 },
23390 gpu: function(bot, gpu) {
23391 var o = this._attribute(bot, "gpu", "none");
23392 return o.indexOf(this._unalias(gpu)) !== -1;
23393 },
23394 };
23395 },
23396
23397 }];
23398 })();
23399 </script>
23400 <script>
23401 (function(){
23402 // Taken from http://developer.android.com/reference/android/os/BatteryManag er.html
23403 var BATTERY_HEALTH_UNKNOWN = 1;
23404 var BATTERY_HEALTH_GOOD = 2;
23405 var BATTERY_STATUS_CHARGING = 2;
23406
23407 var UNAUTHENTICATED = "unauthenticated";
23408 var AVAILABLE = "available";
23409 var UNKNOWN = "unknown";
23410
23411 // This behavior wraps up all the shared bot-list functionality by
23412 // extending SwarmingBehaviors.SwarmingBehavior
23413 SwarmingBehaviors.BotListBehavior = [SwarmingBehaviors.SwarmingBehavior, {
23414
23415 properties: {
23416 BOT_PROPERTIES: {
23417 type: Array,
23418 value: function() {
23419 // TODO(kjlubick): Add more of these things from state, as they
23420 // needed/useful/requested.
23421 return ["disk_space", "task", "status"];
23422 }
23423 },
23424 },
23425
23426 // _attribute looks first in dimension and then in state for the
23427 // specified attribute. This will always return an array. If there is
23428 // no matching attribute, ["unknown"] will be returned.
23429 _attribute: function(bot, attr, none) {
23430 none = none || UNKNOWN;
23431 return this._dimension(bot, attr) || this._state(bot, attr) || [none];
23432 },
23433
23434 _devices: function(bot) {
23435 var devices = [];
23436 var d = (bot && bot.state && bot.state.devices) || {};
23437 // state.devices is like {Serial:Object}, so we need to keep the serial
23438 for (key in d) {
23439 var o = d[key];
23440 o.serial = key;
23441 o.okay = (o.state === AVAILABLE);
23442 // It is easier to assume all devices on a bot are of the same type
23443 // than to pick through the (incomplete) device state and find it.
23444 o.device_type = this._attribute(bot, "device_type")[0];
23445 devices.push(o);
23446 }
23447 return devices;
23448 },
23449
23450 // _deviceType returns the codename of a given Android device.
23451 _deviceType: function(device) {
23452 return device.device_type.toLowerCase();
23453 },
23454
23455 // _dimension returns the given dimension of a bot. If it is defined, it
23456 // is an array of strings.
23457 _dimension: function(bot, dim) {
23458 if (!bot || !bot.dimensions || !dim) {
23459 return undefined;
23460 }
23461 for (var i = 0; i < bot.dimensions.length; i++) {
23462 if (bot.dimensions[i].key === dim) {
23463 return bot.dimensions[i].value;
23464 }
23465 }
23466 return undefined;
23467 },
23468
23469 // _state returns the requested attribute from a bot's state.
23470 // For consistency with _dimension, if the attribute is not an array,
23471 // it is put as the only element in an array.
23472 _state: function(bot, attr) {
23473 if (!bot || !bot.state || !bot.state[attr]) {
23474 return undefined
23475 }
23476 var state = bot.state[attr];
23477 if (Array.isArray(state)) {
23478 return state;
23479 }
23480 return [state];
23481 },
23482
23483 _taskId: function(bot) {
23484 if (bot && bot.task_id) {
23485 return bot.task_id;
23486 }
23487 return "idle";
23488 },
23489
23490 }];
23491 })()
23492 </script>
23493 <dom-module id="bot-filters" assetpath="/res/imp/botlist/">
23494 <template>
23495 <style is="custom-style" include="iron-flex iron-flex-alignment iron-positio ning query-column-filter-style">
23496
23497 </style>
23498
23499 <url-param name="filters" value="{{_filters}}" default_values="[]" multi="">
23500 </url-param>
23501 <url-param name="columns" value="{{columns}}" default_values="[&quot;id&quot ;,&quot;os&quot;,&quot;task&quot;,&quot;status&quot;]" multi="">
23502 </url-param>
23503 <url-param name="query" value="{{_query}}" default_value="">
23504 </url-param>
23505 <url-param name="verbose" value="{{verbose}}">
23506 </url-param>
23507 <url-param name="limit" default_value="200" value="{{limit}}">
23508 </url-param>
23509
23510 <div class="container horizontal layout">
23511
23512
23513 <div class="narrow-down-selector">
23514 <div>
23515 <paper-input id="filter" label="Search columns and filters" placeholde r="gpu nvidia" value="{{_query}}">
23516 </paper-input>
23517 </div>
23518
23519 <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.">
23520 <iron-selector attr-for-selected="label" selected="{{_primarySelected} }">
23521 <template is="dom-repeat" items="[[_primaryItems]]" as="item">
23522 <div class="selectable item horizontal layout" label="[[item]]">
23523
23524 <span>[[_beforeBold(item,_query)]]<span class="bold">[[_bold(ite m,_query)]]</span>[[_afterBold(item,_query)]]</span>
23525 <span class="flex"></span>
23526 <paper-checkbox noink="" disabled$="[[_cantToggleColumn(item)]]" checked="[[_columnState(item,columns.*)]]" on-change="_toggleColumn">
23527 </paper-checkbox>
23528 </div>
23529 </template>
23530 </iron-selector>
23531 </div>
23532
23533 <div class="selector side-by-side" title="These are all options (if any) that the bot list can be filtered on.">
23534 <template is="dom-repeat" id="secondaryList" items="[[_secondaryItems] ]" as="item">
23535 <div class="item horizontal layout" label="[[item]]">
23536
23537 <span>[[_beforeBold(item,_query)]]<span class="bold">[[_bold(item, _query)]]</span>[[_afterBold(item,_query)]]</span>
23538 <span class="flex"></span>
23539 <iron-icon class="icons" icon="icons:arrow-forward" hidden="[[_can tAddFilter(_primarySelected,item,_filters.*)]]" on-tap="_addFilter">
23540 </iron-icon>
23541 </div>
23542 </template>
23543 </div>
23544
23545 <div class="selector side-by-side" title="These filters are AND'd togeth er and applied to all bots in
23546 the fleet.">
23547 <template is="dom-repeat" items="[[_filters]]" as="fil">
23548 <div class="item horizontal layout" label="[[fil]]">
23549 <span>[[fil]]</span>
23550 <span class="flex"></span>
23551 <iron-icon class="icons" icon="icons:remove-circle-outline" hidden ="[[_cantRemoveFilter(fil,_filters.*)]]" on-tap="_removeFilter">
23552 </iron-icon>
23553 </div>
23554 </template>
23555 </div>
23556
23557 <div class="side-by-side">
23558 <paper-checkbox checked="{{verbose}}">Verbose Entries</paper-checkbox>
23559 <paper-input id="limit" label="Limit Results" auto-validate="" min="0" max="1000" pattern="[0-9]+" value="{{limit}}">
23560 </paper-input>
23561 </div>
23562 </div>
23563
23564 </div>
23565
23566 </template>
23567 <script>
23568 (function(){
23569 // See query-column-filter for more documentation on these properties.
23570 var filterMap = {
23571 disk_space: function(bot, space) {
23572 return true;
23573 },
23574 id: function(bot, id) {
23575 return true;
23576 },
23577 status: function(bot, status) {
23578 if (status === "quarantined") {
23579 return bot.quarantined;
23580 } else if (status === "dead") {
23581 return bot.is_dead;
23582 } else {
23583 // Status must be "alive".
23584 return !bot.quarantined && !bot.is_dead;
23585 }
23586 },
23587 task: function(bot, task) {
23588 if (task === "idle") {
23589 return this._taskId(bot) === "idle";
23590 }
23591 // Task must be "busy".
23592 return this._taskId(bot) !== "idle";
23593 }
23594 };
23595
23596 Polymer({
23597 is: "bot-filters",
23598
23599 behaviors: [
23600 SwarmingBehaviors.BotListBehavior,
23601 SwarmingBehaviors.QueryColumnFilter,
23602 ],
23603
23604 properties: {
23605 // url-param doesn't like columns to be defined in query_column-filter,
23606 // so we define it here.
23607 columns: {
23608 type: Array,
23609 notify: true,
23610 },
23611 query_params: {
23612 type: Object,
23613 computed: "_extractQueryParams(dimensions.*,_filters.*, limit)",
23614 notify: true,
23615 },
23616 verbose: {
23617 type: Boolean,
23618 notify: true,
23619 },
23620
23621 // for QueryColumnFilter
23622 _filterMap: {
23623 type: Object,
23624 value: function() {
23625 var base = this._commonFilters();
23626 for (var attr in filterMap) {
23627 base[attr] = filterMap[attr];
23628 }
23629 return base;
23630 },
23631 }
23632 },
23633
23634 _cantToggleColumn: function(col) {
23635 // Don't allow the id column to be removed, as the bot list is basically
23636 // meaningless without it.
23637 return !col || col === "id" ;
23638 },
23639
23523 _extractQueryParams: function() { 23640 _extractQueryParams: function() {
23524 var params = {}; 23641 var params = {};
23525 var dims = []; 23642 var dims = [];
23526 this._filters.forEach(function(f) { 23643 this._filters.forEach(function(f) {
23527 var split = f.split(FILTER_SEP, 1) 23644 var split = f.split(this.FILTER_SEP, 1)
23528 var col = split[0]; 23645 var col = split[0];
23529 if (this.dimensions.indexOf(col) !== -1) { 23646 if (this.dimensions.indexOf(col) !== -1) {
23530 var rest = f.substring(col.length + FILTER_SEP.length); 23647 var rest = f.substring(col.length + this.FILTER_SEP.length);
23531 dims.push(col + FILTER_SEP + this._unalias(rest)) 23648 dims.push(col + this.FILTER_SEP + this._unalias(rest))
23532 } else if (col === "status") { 23649 } else if (col === "status") {
23533 var rest = f.substring(col.length + FILTER_SEP.length); 23650 var rest = f.substring(col.length + this.FILTER_SEP.length);
23534 if (rest === "alive") { 23651 if (rest === "alive") {
23535 params["is_dead"] = "FALSE"; 23652 params["is_dead"] = "FALSE";
23536 params["quarantined"] = "FALSE"; 23653 params["quarantined"] = "FALSE";
23537 } else if (rest === "quarantined") { 23654 } else if (rest === "quarantined") {
23538 params["quarantined"] = "TRUE"; 23655 params["quarantined"] = "TRUE";
23539 } else if (rest === "dead") { 23656 } else if (rest === "dead") {
23540 params["is_dead"] = "TRUE"; 23657 params["is_dead"] = "TRUE";
23541 } 23658 }
23542 } 23659 }
23543 }.bind(this)); 23660 }.bind(this));
23544 params["dimensions"] = dims; 23661 params["dimensions"] = dims;
23545 var lim = Math.floor(this.limit) 23662 var lim = Math.floor(this.limit)
23546 if (Number.isInteger(lim)) { 23663 if (Number.isInteger(lim)) {
23547 // Clamp the limit 23664 // Clamp the limit
23548 lim = Math.max(lim, 1); 23665 lim = Math.max(lim, 1);
23549 lim = Math.min(1000, lim); 23666 lim = Math.min(1000, lim);
23550 params["limit"] = lim; 23667 params["limit"] = lim;
23551 // not !-- because limit could be "900" 23668 // not !== because limit could be a string, e.g. "900"
23552 if (this.limit != lim) { 23669 if (this.limit != lim) {
23553 this.set("limit", lim); 23670 this.set("limit", lim);
23554 } 23671 }
23555 } 23672 }
23556 return params; 23673 return params;
23557 } 23674 }
23558 23675
23559 }); 23676 });
23560 })(); 23677 })();
23561 </script> 23678 </script>
23562 </dom-module><dom-module id="bot-list-data" assetpath="/res/imp/botlist/"> 23679 </dom-module><dom-module id="bot-list-data" assetpath="/res/imp/botlist/">
23563 <template> 23680 <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}}"> 23681 <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> 23682 </iron-ajax>
23566 23683
23567 <iron-ajax id="dimensions" url="/_ah/api/swarming/v1/bots/dimensions" header s="[[auth_headers]]" handle-as="json" last-response="{{_dimensions}}" loading="{ {_busy2}}"> 23684 <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> 23685 </iron-ajax>
23569 23686
23570 <iron-ajax id="fleet" url="/_ah/api/swarming/v1/bots/count" headers="[[auth_ headers]]" handle-as="json" last-response="{{_count}}" loading="{{_busy3}}"> 23687 <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> 23688 </iron-ajax>
23572 </template> 23689 </template>
23573 <script> 23690 <script>
23574 (function(){ 23691 (function(){
23575 var BLACKLIST_DIMENSIONS = ["quarantined", "error"]; 23692 var BLACKLIST_DIMENSIONS = ["quarantined", "error"];
23576 23693
23577 Polymer({ 23694 Polymer({
23578 is: 'bot-list-data', 23695 is: 'bot-list-data',
23579 23696
23580 behaviors: [SwarmingBehaviors.BotListBehavior], 23697 behaviors: [
23698 SwarmingBehaviors.BotListBehavior,
23699 SwarmingBehaviors.Aliases,
23700 ],
23581 23701
23582 properties: { 23702 properties: {
23583 // inputs 23703 // inputs
23584 auth_headers: { 23704 auth_headers: {
23585 type: Object, 23705 type: Object,
23586 observer: "signIn", 23706 observer: "signIn",
23587 }, 23707 },
23588 query_params: { 23708 query_params: {
23589 type: Object, 23709 type: Object,
23590 }, 23710 },
(...skipping 13 matching lines...) Expand all
23604 type: Array, 23724 type: Array,
23605 computed: "_makeArray(_dimensions)", 23725 computed: "_makeArray(_dimensions)",
23606 notify: true, 23726 notify: true,
23607 }, 23727 },
23608 fleet: { 23728 fleet: {
23609 type: Object, 23729 type: Object,
23610 computed: "_fleet(_count)", 23730 computed: "_fleet(_count)",
23611 notify: true, 23731 notify: true,
23612 }, 23732 },
23613 primary_map: { 23733 primary_map: {
23614 type:Object, 23734 type: Object,
23615 computed: "_primaryMap(_dimensions)", 23735 computed: "_primaryMap(_dimensions)",
23616 notify: true, 23736 notify: true,
23617 }, 23737 },
23618 primary_arr: { 23738 primary_arr: {
23619 type: Array, 23739 type: Array,
23620 //BOT_PROPERTIES is inherited from BotListBehavior 23740 //BOT_PROPERTIES is inherited from BotListBehavior
23621 computed: "_primaryArr(dimensions, BOT_PROPERTIES)", 23741 computed: "_primaryArr(dimensions, BOT_PROPERTIES)",
23622 notify: true, 23742 notify: true,
23623 }, 23743 },
23624 23744
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
23691 _makeArray: function(dimObj) { 23811 _makeArray: function(dimObj) {
23692 if (!dimObj || !dimObj.bots_dimensions) { 23812 if (!dimObj || !dimObj.bots_dimensions) {
23693 return []; 23813 return [];
23694 } 23814 }
23695 var dims = []; 23815 var dims = [];
23696 dimObj.bots_dimensions.forEach(function(d){ 23816 dimObj.bots_dimensions.forEach(function(d){
23697 if (BLACKLIST_DIMENSIONS.indexOf(d.key) === -1) { 23817 if (BLACKLIST_DIMENSIONS.indexOf(d.key) === -1) {
23698 dims.push(d.key); 23818 dims.push(d.key);
23699 } 23819 }
23700 }); 23820 });
23821 dims.push("id");
23701 dims.sort(); 23822 dims.sort();
23702 return dims; 23823 return dims;
23703 }, 23824 },
23704 23825
23705 _primaryArr: function(dimensions, properties) { 23826 _primaryArr: function(dimensions, properties) {
23706 return dimensions.concat(properties); 23827 return dimensions.concat(properties);
23707 }, 23828 },
23708 23829
23709 _primaryMap: function(dimensions){ 23830 _primaryMap: function(dimensions){
23710 // pMap will have a list of columns to available values (primary key 23831 // pMap will have a list of columns to available values (primary key
(...skipping 384 matching lines...) Expand 10 before | Expand all | Expand 10 after
24095 "device_type": "Device Type", 24216 "device_type": "Device Type",
24096 "disk_space": "Free Space (MB)", 24217 "disk_space": "Free Space (MB)",
24097 "gpu": "GPU", 24218 "gpu": "GPU",
24098 "os": "OS", 24219 "os": "OS",
24099 "pool": "Pool", 24220 "pool": "Pool",
24100 "status": "Status", 24221 "status": "Status",
24101 "xcode_version": "XCode Version", 24222 "xcode_version": "XCode Version",
24102 }; 24223 };
24103 24224
24104 var columnMap = { 24225 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) { 24226 disk_space: function(bot) {
24123 var aliased = []; 24227 var aliased = [];
24124 bot.disks.forEach(function(disk){ 24228 bot.disks.forEach(function(disk){
24125 var alias = sk.human.bytes(disk.mb, swarming.MB); 24229 var alias = sk.human.bytes(disk.mb, swarming.MB);
24126 aliased.push(this._applyAlias(disk.mb, disk.id + " "+ alias)); 24230 aliased.push(this._applyAlias(disk.mb, disk.id + " "+ alias));
24127 }.bind(this)); 24231 }.bind(this));
24128 if (this._verbose) { 24232 if (this._verbose) {
24129 return aliased.join(" | "); 24233 return aliased.join(" | ");
24130 } 24234 }
24131 return aliased[0]; 24235 return aliased[0];
24132 }, 24236 },
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) { 24237 id: function(bot) {
24159 return bot.bot_id; 24238 return bot.bot_id;
24160 }, 24239 },
24161 pool: function(bot) {
24162 var pool = this._attribute(bot, "pool");
24163 return pool.join(" | ");
24164 },
24165 status: function(bot) { 24240 status: function(bot) {
24166 // If a bot is both dead and quarantined, show the deadness over the 24241 // If a bot is both dead and quarantined, show the deadness over the
24167 // quarentinedness. 24242 // quarentinedness.
24168 if (bot.is_dead) { 24243 if (bot.is_dead) {
24169 return "Dead. Last seen " + sk.human.diffDate(bot.last_seen_ts) + 24244 return "Dead. Last seen " + sk.human.diffDate(bot.last_seen_ts) +
24170 " ago"; 24245 " ago";
24171 } 24246 }
24172 if (bot.quarantined) { 24247 if (bot.quarantined) {
24173 return "Quarantined: " + this._attribute(bot, "quarantined"); 24248 var msg = this._state(bot, "quarantined")[0];
24249 // Sometimes, the quarantined message is actually in "error". This
24250 // happens when the bot code has thrown an exception.
24251 if (msg === "unknown" || msg === "true" || msg === true) {
24252 msg = this._attribute(bot, "error");
24253 }
24254 return "Quarantined: " + msg;
24174 } 24255 }
24175 return "Alive"; 24256 return "Alive";
24176 }, 24257 },
24177 task: function(bot){ 24258 task: function(bot){
24178 return this._taskId(bot); 24259 return this._taskId(bot);
24179 }, 24260 },
24180 }; 24261 };
24181 24262
24182 var deviceColumnMap = { 24263 var deviceColumnMap = {
24183 android_devices: function(device) { 24264 android_devices: function(device) {
(...skipping 29 matching lines...) Expand all
24213 disk_space: function(dir, botA, botB) { 24294 disk_space: function(dir, botA, botB) {
24214 // We sort based on the raw number of MB of the first disk. 24295 // We sort based on the raw number of MB of the first disk.
24215 var botACol = botA.disks[0].mb; 24296 var botACol = botA.disks[0].mb;
24216 var botBCol = botB.disks[0].mb;; 24297 var botBCol = botB.disks[0].mb;;
24217 return dir * swarming.naturalCompare(botACol, botBCol); 24298 return dir * swarming.naturalCompare(botACol, botBCol);
24218 }, 24299 },
24219 }; 24300 };
24220 24301
24221 Polymer({ 24302 Polymer({
24222 is: 'bot-list', 24303 is: 'bot-list',
24223 behaviors: [SwarmingBehaviors.BotListBehavior, 24304
24224 SwarmingBehaviors.DynamicTableBehavior], 24305 // The order behaviors are applied in matters - later ones overwrite
24306 // attributes of earlier ones
24307 behaviors: [
24308 SwarmingBehaviors.BotListBehavior,
24309 SwarmingBehaviors.DynamicTableBehavior,
24310 ],
24225 24311
24226 properties: { 24312 properties: {
24227 client_id: { 24313 client_id: {
24228 type: String, 24314 type: String,
24229 }, 24315 },
24230 24316
24231 // For dynamic table. 24317 // For dynamic table.
24232 _columnMap: { 24318 _columnMap: {
24233 type: Object, 24319 type: Object,
24234 value: columnMap, 24320 value: function() {
24321 var base = this._commonColumns();
24322 for (var attr in columnMap) {
24323 base[attr] = columnMap[attr];
24324 }
24325 return base;
24326 },
24235 }, 24327 },
24236 _headerMap: { 24328 _headerMap: {
24237 type: Object, 24329 type: Object,
24238 value: headerMap, 24330 value: headerMap,
24239 }, 24331 },
24240 _specialColumns: { 24332 _specialColumns: {
24241 type: Array, 24333 type: Array,
24242 value: specialColumns, 24334 value: specialColumns,
24243 }, 24335 },
24244 _specialSort: { 24336 _specialSort: {
(...skipping 288 matching lines...) Expand 10 before | Expand all | Expand 10 after
24533 24625
24534 _taskClass: function(task) { 24626 _taskClass: function(task) {
24535 // TODO(kjlubick): Color tasks? 24627 // TODO(kjlubick): Color tasks?
24536 return ""; 24628 return "";
24537 } 24629 }
24538 24630
24539 }); 24631 });
24540 })(); 24632 })();
24541 </script> 24633 </script>
24542 </dom-module></div></body></html> 24634 </dom-module></div></body></html>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698