| Index: appengine/swarming/ui/build/elements.html
|
| diff --git a/appengine/swarming/ui/build/elements.html b/appengine/swarming/ui/build/elements.html
|
| index e13c6d3849e26d7d62c43ed6009265f21bbea5a1..3f6032eab6c6a5fb6a9fbb88cfa4d02a1aeecec1 100644
|
| --- a/appengine/swarming/ui/build/elements.html
|
| +++ b/appengine/swarming/ui/build/elements.html
|
| @@ -713,7 +713,7 @@ the fleet."> <template is="dom-repeat" items="[[_filters]]" as="fil"> <div class
|
| }
|
|
|
| });
|
| - })(); </script> </dom-module> <dom-module id="paper-button" assetpath="/res/imp/bower_components/paper-button/"> <template strip-whitespace=""> <style include="paper-material-shared-styles">:host{@apply(--layout-inline);@apply(--layout-center-center);position:relative;box-sizing:border-box;min-width:5.14em;margin:0 .29em;background:0 0;-webkit-tap-highlight-color:transparent;-webkit-tap-highlight-color:transparent;font:inherit;text-transform:uppercase;outline-width:0;border-radius:3px;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none;cursor:pointer;z-index:0;padding:.7em .57em;@apply(--paper-font-common-base);@apply(--paper-button)}:host([hidden]){display:none!important}:host([raised].keyboard-focus){font-weight:700;@apply(--paper-button-raised-keyboard-focus)}:host(:not([raised]).keyboard-focus){font-weight:700;@apply(--paper-button-flat-keyboard-focus)}:host([disabled]){background:#eaeaea;color:#a8a8a8;cursor:auto;pointer-events:none;@apply(--paper-button-disabled)}:host([animated]){@apply(--shadow-transition)}paper-ripple{color:var(--paper-button-ink-color)}</style> <content></content> </template> <script>Polymer({is:"paper-button",behaviors:[Polymer.PaperButtonBehavior],properties:{raised:{type:Boolean,reflectToAttribute:!0,value:!1,observer:"_calculateElevation"}},_calculateElevation:function(){this.raised?Polymer.PaperButtonBehaviorImpl._calculateElevation.apply(this):this._setElevation(0)}})</script> </dom-module> <script>Polymer.NeonAnimatableBehavior={properties:{animationConfig:{type:Object},entryAnimation:{observer:"_entryAnimationChanged",type:String},exitAnimation:{observer:"_exitAnimationChanged",type:String}},_entryAnimationChanged:function(){this.animationConfig=this.animationConfig||{},this.animationConfig.entry=[{name:this.entryAnimation,node:this}]},_exitAnimationChanged:function(){this.animationConfig=this.animationConfig||{},this.animationConfig.exit=[{name:this.exitAnimation,node:this}]},_copyProperties:function(i,n){for(var t in n)i[t]=n[t]},_cloneConfig:function(i){var n={isClone:!0};return this._copyProperties(n,i),n},_getAnimationConfigRecursive:function(i,n,t){if(this.animationConfig){if(this.animationConfig.value&&"function"==typeof this.animationConfig.value)return void this._warn(this._logf("playAnimation","Please put 'animationConfig' inside of your components 'properties' object instead of outside of it."));var o;if(o=i?this.animationConfig[i]:this.animationConfig,Array.isArray(o)||(o=[o]),o)for(var e,a=0;e=o[a];a++)if(e.animatable)e.animatable._getAnimationConfigRecursive(e.type||i,n,t);else if(e.id){var r=n[e.id];r?(r.isClone||(n[e.id]=this._cloneConfig(r),r=n[e.id]),this._copyProperties(r,e)):n[e.id]=e}else t.push(e)}},getAnimationConfig:function(i){var n={},t=[];this._getAnimationConfigRecursive(i,n,t);for(var o in n)t.push(n[o]);return t}}</script> <script>Polymer.NeonAnimationRunnerBehaviorImpl={_configureAnimations:function(n){var i=[];if(n.length>0)for(var e,t=0;e=n[t];t++){var o=document.createElement(e.name);if(o.isNeonAnimation){var a=null;try{a=o.configure(e),"function"!=typeof a.cancel&&(a=document.timeline.play(a))}catch(n){a=null,console.warn("Couldnt play","(",e.name,").",n)}a&&i.push({neonAnimation:o,config:e,animation:a})}else console.warn(this.is+":",e.name,"not found!")}return i},_shouldComplete:function(n){for(var i=!0,e=0;e<n.length;e++)if("finished"!=n[e].animation.playState){i=!1;break}return i},_complete:function(n){for(var i=0;i<n.length;i++)n[i].neonAnimation.complete(n[i].config);for(var i=0;i<n.length;i++)n[i].animation.cancel()},playAnimation:function(n,i){var e=this.getAnimationConfig(n);if(e){this._active=this._active||{},this._active[n]&&(this._complete(this._active[n]),delete this._active[n]);var t=this._configureAnimations(e);if(0==t.length)return void this.fire("neon-animation-finish",i,{bubbles:!1});this._active[n]=t;for(var o=0;o<t.length;o++)t[o].animation.onfinish=function(){this._shouldComplete(t)&&(this._complete(t),delete this._active[n],this.fire("neon-animation-finish",i,{bubbles:!1}))}.bind(this)}},cancelAnimation:function(){for(var n in this._animations)this._animations[n].cancel();this._animations={}}},Polymer.NeonAnimationRunnerBehavior=[Polymer.NeonAnimatableBehavior,Polymer.NeonAnimationRunnerBehaviorImpl]</script> <script>Polymer.PaperDialogBehaviorImpl={hostAttributes:{role:"dialog",tabindex:"-1"},properties:{modal:{type:Boolean,value:!1}},observers:["_modalChanged(modal, _readied)"],listeners:{tap:"_onDialogClick"},ready:function(){this.__prevNoCancelOnOutsideClick=this.noCancelOnOutsideClick,this.__prevNoCancelOnEscKey=this.noCancelOnEscKey,this.__prevWithBackdrop=this.withBackdrop},_modalChanged:function(i,e){e&&(i?(this.__prevNoCancelOnOutsideClick=this.noCancelOnOutsideClick,this.__prevNoCancelOnEscKey=this.noCancelOnEscKey,this.__prevWithBackdrop=this.withBackdrop,this.noCancelOnOutsideClick=!0,this.noCancelOnEscKey=!0,this.withBackdrop=!0):(this.noCancelOnOutsideClick=this.noCancelOnOutsideClick&&this.__prevNoCancelOnOutsideClick,this.noCancelOnEscKey=this.noCancelOnEscKey&&this.__prevNoCancelOnEscKey,this.withBackdrop=this.withBackdrop&&this.__prevWithBackdrop))},_updateClosingReasonConfirmed:function(i){this.closingReason=this.closingReason||{},this.closingReason.confirmed=i},_onDialogClick:function(i){for(var e=Polymer.dom(i).path,o=0;o<e.indexOf(this);o++){var t=e[o];if(t.hasAttribute&&(t.hasAttribute("dialog-dismiss")||t.hasAttribute("dialog-confirm"))){this._updateClosingReasonConfirmed(t.hasAttribute("dialog-confirm")),this.close(),i.stopPropagation();break}}}},Polymer.PaperDialogBehavior=[Polymer.IronOverlayBehavior,Polymer.PaperDialogBehaviorImpl]</script> <dom-module id="paper-dialog-shared-styles" assetpath="/res/imp/bower_components/paper-dialog-behavior/"> <template> <style>:host{display:block;margin:24px 40px;background:var(--paper-dialog-background-color,--primary-background-color);color:var(--paper-dialog-color,--primary-text-color);@apply(--paper-font-body1);@apply(--shadow-elevation-16dp);@apply(--paper-dialog)}:host>::content>*{margin-top:20px;padding:0 24px}:host>::content>.no-padding{padding:0}:host>::content>:first-child{margin-top:24px}:host>::content>:last-child{margin-bottom:24px}:host>::content h2{position:relative;margin:0;@apply(--paper-font-title);@apply(--paper-dialog-title)}:host>::content .buttons{position:relative;padding:8px 8px 8px 24px;margin:0;color:var(--paper-dialog-button-color,--primary-color);@apply(--layout-horizontal);@apply(--layout-end-justified)}</style> </template> </dom-module> <dom-module id="paper-dialog" assetpath="/res/imp/bower_components/paper-dialog/"> <template> <style include="paper-dialog-shared-styles"></style> <content></content> </template> </dom-module> <script>!function(){Polymer({is:"paper-dialog",behaviors:[Polymer.PaperDialogBehavior,Polymer.NeonAnimationRunnerBehavior],listeners:{"neon-animation-finish":"_onNeonAnimationFinish"},_renderOpened:function(){this.cancelAnimation(),this.playAnimation("entry")},_renderClosed:function(){this.cancelAnimation(),this.playAnimation("exit")},_onNeonAnimationFinish:function(){this.opened?this._finishRenderOpened():this._finishRenderClosed()}})}()</script> <script>window.SwarmingBehaviors=window.SwarmingBehaviors||{},function(){SwarmingBehaviors.TaskBehavior={properties:{BOT_DIED:{type:String,value:"BOT_DIED"},CANCELED:{type:String,value:"CANCELED"},COMPLETED:{type:String,value:"COMPLETED"},COMPLETED_DEDUPED:{type:String,value:"COMPLETED (DEDUPED)"},COMPLETED_FAILURE:{type:String,value:"COMPLETED (FAILURE)"},COMPLETED_SUCCESS:{type:String,value:"COMPLETED (SUCCESS)"},EXPIRED:{type:String,value:"EXPIRED"},PENDING:{type:String,value:"PENDING"},RUNNING:{type:String,value:"RUNNING"},TIMED_OUT:{type:String,value:"TIMED_OUT"}},stateClass:function(t){return t===this.CANCELED||t===this.TIMED_OUT||t===this.EXPIRED?"exception":t===this.BOT_DIED?"bot_died":t===this.COMPLETED_FAILURE?"failed_task":t===this.RUNNING||t===this.PENDING?"pending_task":""},state:function(t){return t?t.state===this.COMPLETED?t.failure?this.COMPLETED_FAILURE:"0"===t.try_number?this.COMPLETED_DEDUPED:this.COMPLETED_SUCCESS:t.state:""},_stateClass:function(t){return this.stateClass(this.state(t))}}}()</script> <dom-module id="task-style" assetpath="/res/imp/common/"> <template> <style>.failed_task{background-color:#fdd}.bot_died{background-color:#ccc}.exception{background-color:#edd2ff}.pending_task{background-color:#fffc6c}</style> </template> </dom-module><dom-module id="task-filters" assetpath="/res/imp/tasklist/"> <template> <style is="custom-style" include="iron-flex iron-flex-alignment iron-positioning query-column-filter-style">.item.wide{max-width:400px}.selector.wide{min-width:275px}</style> <url-param name="f" value="{{_filters}}" default_values="[]" multi=""> </url-param> <url-param name="c" value="{{columns}}" default_values="["name","state","created_ts","user"]" multi=""> </url-param> <url-param name="q" value="{{_query}}" default_value=""> </url-param> <url-param name="l" default_value="50" value="{{_limit}}"> </url-param> <div class="container horizontal layout"> <div class="narrow-down-selector"> <div> <iron-a11y-keys target="[[_filter_input]]" keys="enter" on-keys-pressed="_manuallyAddFilter"></iron-a11y-keys> <paper-input id="filter" label="Search columns and filters or supply a filter and press enter" placeholder="gpu nvidia pool:Chrome" value="{{_query::input}}"> </paper-input> </div> <div class="wide selector side-by-side" title="This shows all task tags and other interesting task properties. Mark the check box to add as a column. Select the row to see filter options."> <iron-selector attr-for-selected="label" selected="{{_primarySelected}}"> <template is="dom-repeat" items="[[_primaryItems]]" as="item"> <div class="selectable item horizontal layout" label="[[item]]"> <span>[[_beforeBold(item,_query)]]<span class="bold">[[_bold(item,_query)]]</span>[[_afterBold(item,_query)]]</span> <span class="flex"></span> <paper-checkbox noink="" disabled$="[[_cantToggleColumn(item)]]" checked="[[_columnState(item,columns.*)]]" on-change="_toggleColumn"> </paper-checkbox> </div> </template> </iron-selector> </div> <div class="selector side-by-side" title="These are all options (if any) that the task list can be filtered on."> <template is="dom-repeat" id="secondaryList" items="[[_secondaryItems]]" as="item"> <div class="item horizontal layout" label="[[item]]"> <span>[[_beforeBold(item,_query)]]<span class="bold">[[_bold(item,_query)]]</span>[[_afterBold(item,_query)]]</span> <span class="flex"></span> <iron-icon class="icons" icon="icons:arrow-forward" hidden="[[_cantAddFilter(_primarySelected,item,_filters.*)]]" on-tap="_addFilter"> </iron-icon> </div> </template> </div> <div class="selector side-by-side" title="These tag filters are AND'd together and applied to all tasks."> <template is="dom-repeat" items="[[_filters]]" as="fil"> <div class="item horizontal layout" label="[[fil]]"> <span>[[fil]]</span> <span class="flex"></span> <iron-icon class="icons" icon="icons:remove-circle-outline" hidden="[[_cantRemoveFilter(fil,_filters.*)]]" on-tap="_removeFilter"> </iron-icon> </div> </template> </div> <div class="side-by-side"> <paper-input id="_limit" label="Limit Results" auto-validate="" min="0" max="1000" pattern="[0-9]+" value="{{_limit}}"> </paper-input> </div> </div> </div> </template> <script>!function(){var t={state:function(t,i){var r=this._attribute(t,"state")[0];if(i===r||"ALL"===i)return!0;if("PENDING_RUNNING"===i)return r===this.PENDING||r===this.RUNNING;var e=this._attribute(t,"failure",!1)[0];if("COMPLETED_SUCCESS"===i)return r===this.COMPLETED&&!e;if("COMPLETED_FAILURE"===i)return r===this.COMPLETED&&e;var a=this._attribute(t,"try_number","-1")[0];return"DEDUPED"===i?r===this.COMPLETED&&"0"===a:void 0}};Polymer({is:"task-filters",behaviors:[SwarmingBehaviors.QueryColumnFilter,SwarmingBehaviors.TaskBehavior],properties:{columns:{type:Array,notify:!0},query_params:{type:Object,computed:"_extractQueryParams(_filters.*, _limit)",notify:!0},_filterMap:{type:Object,value:function(){var i=this._commonFilters();for(var r in t)i[r]=t[r];return i}}},_cantToggleColumn:function(t){return!t||"name"===t},_extractQueryParams:function(){var t={},i=[];this._filters.forEach(function(r){var e=r.split(this.FILTER_SEP,1),a=e[0],s=r.substring(a.length+this.FILTER_SEP.length);"state"===a?t.state=[s]:("user"===a&&"none"===s&&(s=""),i.push(a+this.FILTER_SEP+swarming.alias.unapply(s)))}.bind(this)),t.tags=i;var r=parseInt(this._limit);return Number.isInteger(r)&&(r=Math.max(r,1),r=Math.min(1e3,r),t.limit=[r],this._limit!=r&&this.set("_limit",r)),t}})}()</script> </dom-module><dom-module id="task-list-data" assetpath="/res/imp/tasklist/"> <script>!function(){var t=["abandoned_ts","completed_ts","created_ts","modified_ts","started_ts"];Polymer({is:"task-list-data",behaviors:[SwarmingBehaviors.CommonBehavior,SwarmingBehaviors.TaskBehavior],properties:{auth_headers:{type:Object,observer:"signIn"},query_params:{type:Object},tasks:{type:Array},busy:{type:Boolean,computed:"_or(_busy2,_busy1)",notify:!0},primary_map:{type:Object,computed:"_primaryMap(_tags,_dimensions,tasks.*)",notify:!0},primary_arr:{type:Array,computed:"_primaryArr(primary_map)",notify:!0},_busy2:{type:Boolean,value:!1},_busy1:{type:Boolean,value:!1},_dimensions:{type:Object},_list:{type:Object},_tags:{type:Object}},signIn:function(){this._getJsonAsync("_tags","/api/swarming/v1/tasks/tags","_busy2",this.auth_headers),this._getJsonAsync("_dimensions","/api/swarming/v1/bots/dimensions","_busy1",this.auth_headers)},_primaryArr:function(t){var a=Object.keys(t);return a.sort(),a},_primaryMap:function(a,s){a=a&&a.tasks_tags||[],s=s&&s.bots_dimensions||[],tasks=this.tasks||[];var e={};a.forEach(function(t){e[t.key]||(e[t.key]={});var a=t.value||[];a.forEach(function(a){e[t.key][a]=!0})}),s.forEach(function(t){var a=t.value;e[t.key]||(e[t.key]={}),a.forEach(function(a){e[t.key][a]=!0})}),tasks.forEach(function(t){Object.keys(t.tagMap).forEach(function(a){var s=t.tagMap[a];e[a]||(e[a]={}),e[a][s]=!0})}),e.user&&(delete e.user[""],e.user.none=!0);var r={};for(key in e){var i=Object.keys(e[key]);if(swarming.alias.DIMENSIONS_WITH_ALIASES.indexOf(key)===-1)r[key]=i;else{var n=[];i.forEach(function(t){n.push(swarming.alias.apply(t,key))}),r[key]=n}}return r.name=[],r.state=[this.PENDING,this.RUNNING,"PENDING_RUNNING",this.COMPLETED,"COMPLETED_SUCCESS","COMPLETED_FAILURE",this.EXPIRED,this.TIMED_OUT,this.BOT_DIED,this.CANCELED,"DEDUPED","ALL"],r.costs_usd=[],r.deduped_from=[],r.duration=[],r.server_versions=[],r.bot=[],t.forEach(function(t){r[t]=[]}),r},parseTasks:function(a){if(!a||!a.items)return[];var s=new Date;return a.items.forEach(function(a){var e={};a.tags=a.tags||[],a.tags.forEach(function(t){var a=t.split(":",1),s=a[0],r=t.substring(s.length+1);e[s]=r}),a.tagMap=e,t.forEach(function(t){a[t]&&(a[t]=new Date(a[t]),a["human_"+t]=sk.human.localeTime(a[t]))}),!a.duration&&a.state===this.RUNNING&&a.started_ts&&(a.duration=(s-a.started_ts)/1e3),a.duration&&(a.human_duration=this._humanDuration(a.duration))}.bind(this)),a.items}})}()</script> </dom-module> <dom-module id="task-list" assetpath="/res/imp/tasklist/"> <template> <style include="iron-flex iron-flex-alignment iron-positioning swarming-app-style dynamic-table-style task-style">task-filters{margin-bottom:8px;margin-right:10px}.task-list th>span{padding-right:30px}</style> <url-param name="s" value="{{_sortstr}}" default_value="created_ts:desc"> </url-param> <swarming-app client_id="[[client_id]]" auth_headers="{{_auth_headers}}" permissions="{{_permissions}}" signed_in="{{_signed_in}}" busy="[[_or(_busy1,_busy2)]]" name="Swarming Task List"> <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2> <div hidden$="[[_not(_signed_in)]]"> <task-list-data id="data" auth_headers="[[_auth_headers]]" query_params="[[_query_params]]" tasks="[[_items]]" busy="{{_busy1)}}" primary_map="{{_primary_map}}" primary_arr="{{_primary_arr}}"> </task-list-data> <div class="horizontal layout"> <task-filters primary_map="[[_primary_map]]" primary_arr="[[_primary_arr]]" columns="{{_columns}}" query_params="{{_query_params}}" filter="{{_filter}}"> </task-filters> </div> <table class="task-list"> <thead on-sort_change="_sortChange"> <tr> <th> <span>Task Name</span> <sort-toggle name="name" current="[[_sort]]"> </sort-toggle> </th> <th hidden$="[[_hide('state', _columns.*)]]"> <span>State</span> <sort-toggle name="state" current="[[_sort]]"> </sort-toggle> </th> <th hidden$="[[_hide('bot', _columns.*)]]"> <span>Bot Assigned</span> <sort-toggle name="bot" current="[[_sort]]"> </sort-toggle> </th> <th hidden$="[[_hide('deduped_from', _columns.*)]]"> <span>Deduped from</span> <sort-toggle name="deduped_from" current="[[_sort]]"> </sort-toggle> </th> <th hidden$="[[_hide('source_revision', _columns.*)]]"> <span>Source Revision</span> <sort-toggle name="source_revision" current="[[_sort]]"> </sort-toggle> </th> <template is="dom-repeat" items="[[_plainColumns]]" as="c"> <th hidden$="[[_hide(c)]]"> <span>[[_header(c)]]</span> <sort-toggle name="[[c]]" current="[[_sort]]"> </sort-toggle> </th> </template> </tr> </thead> <tbody> <template id="tasks_table" is="dom-repeat" items="[[_filteredSortedItems]]" as="task" initial-count="50"> <tr class$="[[_taskClass(task)]]"> <td> <a class="center" href$="[[_taskLink(task.task_id)]]" target="_blank"> [[task.name]] </a> </td> <td hidden$="[[_hide('state', _columns.*)]]"> [[_column('state', task)]] <paper-button raised="" hidden$="[[_cannotCancel(task,_permissions)]]" on-tap="_promptCancel"> Cancel </paper-button> </td> <td hidden$="[[_hide('bot', _columns.*)]]"> <a class="center" href$="[[_botLink(task.bot_id)]]" target="_blank"> [[_column('bot',task)]] </a> </td> <td hidden$="[[_hide('deduped_from', _columns.*)]]"> <a class="center" href$="[[_taskLink(task.deduped_from)]]" target="_blank"> [[_column('deduped_from',task)]] </a> </td> <td hidden$="[[_hide('source_revision', _columns.*)]]"> <a class="center" href$="[[_sourceLink(task)]]" target="_blank"> [[_column('source_revision',task)]] </a> </td> <template is="dom-repeat" items="[[_plainColumns]]" as="c"> <td hidden$="[[_hide(c)]]"> [[_column(c, task)]] </td> </template> </tr> </template> </tbody> </table> <pageable-data id="page_tasks" busy="{{_busy2}}" label="Show more tasks" output="{{_items}}" parse="[[_parseTasks]]"> </pageable-data> </div> </swarming-app> <paper-dialog id="prompt" modal="" on-iron-overlay-closed="_promptClosed"> <h2>Are you sure?</h2> <div>Are you sure you want to [[_dialogPrompt]]?</div> <div class="buttons"> <paper-button dialog-dismiss="" autofocus="">No</paper-button> <paper-button dialog-confirm="">Yes</paper-button> </div> </paper-dialog> <error-toast></error-toast> </template> <script>!function(){function t(t){return function(e){return this._attribute(e,"human_"+t)[0]}}function e(t){return function(e,a,s){var r=this._attribute(a,t,"0")[0],i=this._attribute(s,t,"0")[0];return e*(r-i)}}var a=["deduped_from","name","state","bot","source_revision"],s={abandoned_ts:t("abandoned_ts"),bot:function(t){return this._attribute(t,"bot_id")[0]},completed_ts:t("completed_ts"),costs_usd:function(t){return this._attribute(t,"costs_usd",0)[0]},created_ts:t("created_ts"),duration:t("duration"),modified_ts:t("modified_ts"),source_revision:function(t){var e=this._attribute(t,"source_revision")[0];return e.substring(0,8)},started_ts:t("started_ts"),state:function(t){var e=this._attribute(t,"state")[0];if("COMPLETED"===e){if(this._attribute(t,"failure",!1)[0])return"COMPLETED (FAILURE)";var a=this._attribute(t,"try_number","-1")[0];return"0"===a?"COMPLETED (DEDUPED)":"COMPLETED (SUCCESS)"}return e}},r={user:"Requesting User"},i={abandoned_ts:e("abandoned_ts"),completed_ts:e("completed_ts"),created_ts:e("created_ts"),duration:e("duration"),modified_ts:e("modified_ts"),started_ts:e("started_ts")};Polymer({is:"task-list",behaviors:[SwarmingBehaviors.DynamicTableBehavior,SwarmingBehaviors.TaskBehavior],properties:{client_id:{type:String},_busy1:{type:Boolean,value:!1},_busy2:{type:Boolean,value:!1},_parseTasks:{type:Function,value:function(){return this.$.data.parseTasks.bind(this)}},_toCancel:{type:String},_columnMap:{type:Object,value:function(){var t=this._commonColumns();for(var e in s)t[e]=s[e];return t}},_headerMap:{type:Object,value:r},_specialColumns:{type:Array,value:a},_specialSort:{type:Object,value:i}},observers:["reload(_query_params,_auth_headers)"],_attribute:function(t,e,a){void 0===a&&(a="none");var s=this._tag(t,e)||t[e]||[a];return Array.isArray(s)?s:[s]},_cannotCancel:function(t,e){return!(e&&e.cancel_task&&"PENDING"===this._column("state",t))},_cancelTask:function(){var t="/api/swarming/v1/task/"+this._toCancel+"/cancel";swarming.postWithToast(t,"Canceling task "+this._toCancel,this._auth_headers),this.set("_toCancel","")},_promptClosed:function(t){t.detail.confirmed&&this._cancelTask()},_promptCancel:function(t){var e=t.model.task;return e&&e.task_id?(this.set("_toCancel",e.task_id),this.set("_dialogPrompt",'cancel task "'+e.name+'"'),void this.$.prompt.open()):void console.log("Missing task info",e)},reload:function(){if(this._auth_headers&&this._query_params){var t="/api/swarming/v1/tasks/list?"+sk.query.fromParamSet(this._query_params);this.$.page_tasks.load(t,this._auth_headers)}},_sourceLink:function(t){var e=this._attribute(t,"source_revision")[0],a=this._attribute(t,"source_repo")[0];return"none"!==e&&"none"!==a&&a.replace("%s",e)},_tag:function(t,e){if(t&&t.tagMap)return t.tagMap[e]},_taskClass:function(t){return this.stateClass(this._column("state",t))}})}()</script> </dom-module> <dom-module id="iron-collapse" assetpath="/res/imp/bower_components/iron-collapse/"> <template> <style>:host{display:block;transition-duration:var(--iron-collapse-transition-duration,300ms);overflow:visible}:host(.iron-collapse-closed){display:none}:host(:not(.iron-collapse-opened)){overflow:hidden}</style> <content></content> </template> </dom-module> <script>Polymer({is:"iron-collapse",behaviors:[Polymer.IronResizableBehavior],properties:{horizontal:{type:Boolean,value:!1,observer:"_horizontalChanged"},opened:{type:Boolean,value:!1,notify:!0,observer:"_openedChanged"},noAnimation:{type:Boolean},_desiredSize:{type:String,value:""}},get dimension(){return this.horizontal?"width":"height"},get _dimensionMax(){return this.horizontal?"maxWidth":"maxHeight"},get _dimensionMaxCss(){return this.horizontal?"max-width":"max-height"},hostAttributes:{role:"group","aria-hidden":"true","aria-expanded":"false"},listeners:{transitionend:"_transitionEnd"},attached:function(){this._transitionEnd()},toggle:function(){this.opened=!this.opened},show:function(){this.opened=!0},hide:function(){this.opened=!1},updateSize:function(i,t){if(i="auto"===i?"":i,this._desiredSize!==i){this._desiredSize=i,this._updateTransition(!1);var e=t&&!this.noAnimation&&this._isDisplayed;if(e){var n=this._calcSize();""===i&&(this.style[this._dimensionMax]="",i=this._calcSize()),this.style[this._dimensionMax]=n,this.scrollTop=this.scrollTop,this._updateTransition(!0),e=i!==n}this.style[this._dimensionMax]=i,e||this._transitionEnd()}},enableTransition:function(i){Polymer.Base._warn("`enableTransition()` is deprecated, use `noAnimation` instead."),this.noAnimation=!i},_updateTransition:function(i){this.style.transitionDuration=i&&!this.noAnimation?"":"0s"},_horizontalChanged:function(){this.style.transitionProperty=this._dimensionMaxCss;var i="maxWidth"===this._dimensionMax?"maxHeight":"maxWidth";this.style[i]="",this.updateSize(this.opened?"auto":"0px",!1)},_openedChanged:function(){this.setAttribute("aria-expanded",this.opened),this.setAttribute("aria-hidden",!this.opened),this.toggleClass("iron-collapse-closed",!1),this.toggleClass("iron-collapse-opened",!1),this.updateSize(this.opened?"auto":"0px",!0),this.opened&&this.focus()},_transitionEnd:function(){this.style[this._dimensionMax]=this._desiredSize,this.toggleClass("iron-collapse-closed",!this.opened),this.toggleClass("iron-collapse-opened",this.opened),this._updateTransition(!1),this.notifyResize()},get _isDisplayed(){var i=this.getBoundingClientRect();for(var t in i)if(0!==i[t])return!0;return!1},_calcSize:function(){return this.getBoundingClientRect()[this.dimension]+"px"}})</script> <script>Polymer.IronMenubarBehaviorImpl={hostAttributes:{role:"menubar"},keyBindings:{left:"_onLeftKey",right:"_onRightKey"},_onUpKey:function(e){this.focusedItem.click(),e.detail.keyboardEvent.preventDefault()},_onDownKey:function(e){this.focusedItem.click(),e.detail.keyboardEvent.preventDefault()},get _isRTL(){return"rtl"===window.getComputedStyle(this).direction},_onLeftKey:function(e){this._isRTL?this._focusNext():this._focusPrevious(),e.detail.keyboardEvent.preventDefault()},_onRightKey:function(e){this._isRTL?this._focusPrevious():this._focusNext(),e.detail.keyboardEvent.preventDefault()},_onKeydown:function(e){this.keyboardEventMatchesKeys(e,"up down left right esc")||this._focusWithKeyboardEvent(e)}},Polymer.IronMenubarBehavior=[Polymer.IronMenuBehavior,Polymer.IronMenubarBehaviorImpl]</script> <iron-iconset-svg name="paper-tabs" size="24"> <svg><defs> <g id="chevron-left"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"></path></g> <g id="chevron-right"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path></g> </defs></svg> </iron-iconset-svg> <dom-module id="paper-tab" assetpath="/res/imp/bower_components/paper-tabs/"> <template> <style>:host{@apply(--layout-inline);@apply(--layout-center);@apply(--layout-center-justified);@apply(--layout-flex-auto);position:relative;padding:0 12px;overflow:hidden;cursor:pointer;vertical-align:middle;@apply(--paper-font-common-base);@apply(--paper-tab)}:host(:focus){outline:0}:host([link]){padding:0}.tab-content{height:100%;transform:translateZ(0);-webkit-transform:translateZ(0);transition:opacity .1s cubic-bezier(.4,0,1,1);@apply(--layout-horizontal);@apply(--layout-center-center);@apply(--layout-flex-auto);@apply(--paper-tab-content)}:host(:not(.iron-selected))>.tab-content{opacity:.8;@apply(--paper-tab-content-unselected)}:host(:focus) .tab-content{opacity:1;font-weight:700}paper-ripple{color:var(--paper-tab-ink,--paper-yellow-a100)}.tab-content>::content>a{@apply(--layout-flex-auto);height:100%}</style> <div class="tab-content"> <content></content> </div> </template> <script>Polymer({is:"paper-tab",behaviors:[Polymer.IronControlState,Polymer.IronButtonState,Polymer.PaperRippleBehavior],properties:{link:{type:Boolean,value:!1,reflectToAttribute:!0}},hostAttributes:{role:"tab"},listeners:{down:"_updateNoink",tap:"_onTap"},attached:function(){this._updateNoink()},get _parentNoink(){var t=Polymer.dom(this).parentNode;return!!t&&!!t.noink},_updateNoink:function(){this.noink=!!this.noink||!!this._parentNoink},_onTap:function(t){if(this.link){var e=this.queryEffectiveChildren("a");if(!e)return;if(t.target===e)return;e.click()}}})</script> </dom-module> <dom-module id="paper-tabs" assetpath="/res/imp/bower_components/paper-tabs/"> <template> <style>:host{@apply(--layout);@apply(--layout-center);height:48px;font-size:14px;font-weight:500;overflow:hidden;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;-webkit-tap-highlight-color:transparent;@apply(--paper-tabs)}:host-context([dir=rtl]){@apply(--layout-horizontal-reverse)}#tabsContainer{position:relative;height:100%;white-space:nowrap;overflow:hidden;@apply(--layout-flex-auto)}#tabsContent{height:100%;-moz-flex-basis:auto;-ms-flex-basis:auto;flex-basis:auto}#tabsContent.scrollable{position:absolute;white-space:nowrap}#tabsContent.scrollable.fit-container,#tabsContent:not(.scrollable){@apply(--layout-horizontal)}#tabsContent.scrollable.fit-container{min-width:100%}#tabsContent.scrollable.fit-container>::content>*{-ms-flex:1 0 auto;-webkit-flex:1 0 auto;flex:1 0 auto}.hidden{display:none}.not-visible{opacity:0;cursor:default}paper-icon-button{width:48px;height:48px;padding:12px;margin:0 4px}#selectionBar{position:absolute;height:2px;bottom:0;left:0;right:0;background-color:var(--paper-tabs-selection-bar-color,--paper-yellow-a100);-webkit-transform:scale(0);transform:scale(0);-webkit-transform-origin:left center;transform-origin:left center;transition:-webkit-transform;transition:transform;@apply(--paper-tabs-selection-bar)}#selectionBar.align-bottom{top:0;bottom:auto}#selectionBar.expand{transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,1,1)}#selectionBar.contract{transition-duration:.18s;transition-timing-function:cubic-bezier(0,0,.2,1)}#tabsContent>::content>:not(#selectionBar){height:100%}</style> <paper-icon-button icon="paper-tabs:chevron-left" class$="[[_computeScrollButtonClass(_leftHidden, scrollable, hideScrollButtons)]]" on-up="_onScrollButtonUp" on-down="_onLeftScrollButtonDown" tabindex="-1"></paper-icon-button> <div id="tabsContainer" on-track="_scroll" on-down="_down"> <div id="tabsContent" class$="[[_computeTabsContentClass(scrollable, fitContainer)]]"> <div id="selectionBar" class$="[[_computeSelectionBarClass(noBar, alignBottom)]]" on-transitionend="_onBarTransitionEnd"></div> <content select="*"></content> </div> </div> <paper-icon-button icon="paper-tabs:chevron-right" class$="[[_computeScrollButtonClass(_rightHidden, scrollable, hideScrollButtons)]]" on-up="_onScrollButtonUp" on-down="_onRightScrollButtonDown" tabindex="-1"></paper-icon-button> </template> <script>Polymer({is:"paper-tabs",behaviors:[Polymer.IronResizableBehavior,Polymer.IronMenubarBehavior],properties:{noink:{type:Boolean,value:!1,observer:"_noinkChanged"},noBar:{type:Boolean,value:!1},noSlide:{type:Boolean,value:!1},scrollable:{type:Boolean,value:!1},fitContainer:{type:Boolean,value:!1},disableDrag:{type:Boolean,value:!1},hideScrollButtons:{type:Boolean,value:!1},alignBottom:{type:Boolean,value:!1},selectable:{type:String,value:"paper-tab"},autoselect:{type:Boolean,value:!1},autoselectDelay:{type:Number,value:0},_step:{type:Number,value:10},_holdDelay:{type:Number,value:1},_leftHidden:{type:Boolean,value:!1},_rightHidden:{type:Boolean,value:!1},_previousTab:{type:Object}},hostAttributes:{role:"tablist"},listeners:{"iron-resize":"_onTabSizingChanged","iron-items-changed":"_onTabSizingChanged","iron-select":"_onIronSelect","iron-deselect":"_onIronDeselect"},keyBindings:{"left:keyup right:keyup":"_onArrowKeyup"},created:function(){this._holdJob=null,this._pendingActivationItem=void 0,this._pendingActivationTimeout=void 0,this._bindDelayedActivationHandler=this._delayedActivationHandler.bind(this),this.addEventListener("blur",this._onBlurCapture.bind(this),!0)},ready:function(){this.setScrollDirection("y",this.$.tabsContainer)},detached:function(){this._cancelPendingActivation()},_noinkChanged:function(t){var e=Polymer.dom(this).querySelectorAll("paper-tab");e.forEach(t?this._setNoinkAttribute:this._removeNoinkAttribute)},_setNoinkAttribute:function(t){t.setAttribute("noink","")},_removeNoinkAttribute:function(t){t.removeAttribute("noink")},_computeScrollButtonClass:function(t,e,i){return!e||i?"hidden":t?"not-visible":""},_computeTabsContentClass:function(t,e){return t?"scrollable"+(e?" fit-container":""):" fit-container"},_computeSelectionBarClass:function(t,e){return t?"hidden":e?"align-bottom":""},_onTabSizingChanged:function(){this.debounce("_onTabSizingChanged",function(){this._scroll(),this._tabChanged(this.selectedItem)},10)},_onIronSelect:function(t){this._tabChanged(t.detail.item,this._previousTab),this._previousTab=t.detail.item,this.cancelDebouncer("tab-changed")},_onIronDeselect:function(t){this.debounce("tab-changed",function(){this._tabChanged(null,this._previousTab),this._previousTab=null},1)},_activateHandler:function(){this._cancelPendingActivation(),Polymer.IronMenuBehaviorImpl._activateHandler.apply(this,arguments)},_scheduleActivation:function(t,e){this._pendingActivationItem=t,this._pendingActivationTimeout=this.async(this._bindDelayedActivationHandler,e)},_delayedActivationHandler:function(){var t=this._pendingActivationItem;this._pendingActivationItem=void 0,this._pendingActivationTimeout=void 0,t.fire(this.activateEvent,null,{bubbles:!0,cancelable:!0})},_cancelPendingActivation:function(){void 0!==this._pendingActivationTimeout&&(this.cancelAsync(this._pendingActivationTimeout),this._pendingActivationItem=void 0,this._pendingActivationTimeout=void 0)},_onArrowKeyup:function(t){this.autoselect&&this._scheduleActivation(this.focusedItem,this.autoselectDelay)},_onBlurCapture:function(t){t.target===this._pendingActivationItem&&this._cancelPendingActivation()},get _tabContainerScrollSize(){return Math.max(0,this.$.tabsContainer.scrollWidth-this.$.tabsContainer.offsetWidth)},_scroll:function(t,e){if(this.scrollable){var i=e&&-e.ddx||0;this._affectScroll(i)}},_down:function(t){this.async(function(){this._defaultFocusAsync&&(this.cancelAsync(this._defaultFocusAsync),this._defaultFocusAsync=null)},1)},_affectScroll:function(t){this.$.tabsContainer.scrollLeft+=t;var e=this.$.tabsContainer.scrollLeft;this._leftHidden=0===e,this._rightHidden=e===this._tabContainerScrollSize},_onLeftScrollButtonDown:function(){this._scrollToLeft(),this._holdJob=setInterval(this._scrollToLeft.bind(this),this._holdDelay)},_onRightScrollButtonDown:function(){this._scrollToRight(),this._holdJob=setInterval(this._scrollToRight.bind(this),this._holdDelay)},_onScrollButtonUp:function(){clearInterval(this._holdJob),this._holdJob=null},_scrollToLeft:function(){this._affectScroll(-this._step)},_scrollToRight:function(){this._affectScroll(this._step)},_tabChanged:function(t,e){if(!t)return this.$.selectionBar.classList.remove("expand"),this.$.selectionBar.classList.remove("contract"),void this._positionBar(0,0);var i=this.$.tabsContent.getBoundingClientRect(),n=i.width,o=t.getBoundingClientRect(),s=o.left-i.left;if(this._pos={width:this._calcPercent(o.width,n),left:this._calcPercent(s,n)},this.noSlide||null==e)return this.$.selectionBar.classList.remove("expand"),this.$.selectionBar.classList.remove("contract"),void this._positionBar(this._pos.width,this._pos.left);var a=e.getBoundingClientRect(),l=this.items.indexOf(e),c=this.items.indexOf(t),r=5;this.$.selectionBar.classList.add("expand");var h=l<c,d=this._isRTL;d&&(h=!h),h?this._positionBar(this._calcPercent(o.left+o.width-a.left,n)-r,this._left):this._positionBar(this._calcPercent(a.left+a.width-o.left,n)-r,this._calcPercent(s,n)+r),this.scrollable&&this._scrollToSelectedIfNeeded(o.width,s)},_scrollToSelectedIfNeeded:function(t,e){var i=e-this.$.tabsContainer.scrollLeft;i<0?this.$.tabsContainer.scrollLeft+=i:(i+=t-this.$.tabsContainer.offsetWidth,i>0&&(this.$.tabsContainer.scrollLeft+=i))},_calcPercent:function(t,e){return 100*t/e},_positionBar:function(t,e){t=t||0,e=e||0,this._width=t,this._left=e,this.transform("translateX("+e+"%) scaleX("+t/100+")",this.$.selectionBar)},_onBarTransitionEnd:function(t){var e=this.$.selectionBar.classList;e.contains("expand")?(e.remove("expand"),e.add("contract"),this._positionBar(this._pos.width,this._pos.left)):e.contains("contract")&&e.remove("contract")}})</script> </dom-module> <dom-module id="single-page-style" assetpath="/res/imp/common/"> <template> <style>.header{max-width:800px}.title{font-size:1.5em;font-weight:700;margin:5px}.id_input{margin-left:5px;--paper-input-container-input:{font-size:2em};}.refresh{max-width:40px;max-height:40px;width:initial;height:initial}button{min-width:4em;max-height:55px}table{border-collapse:collapse;margin-left:5px;margin-bottom:8px}td,th{border:1px solid #BBB;padding:5px}.tabs{background-color:#1F78B4;color:#fff;max-width:600px;margin-left:5px;--paper-checkbox-label-color:#fff}paper-tab{background-color:#A6CEE3;color:#000}paper-tab.iron-selected{background-color:#1F78B4;color:#fff;font-weight:700;text-decoration:underline}paper-tab[disabled]{background-color:#AAA;text-decoration:line-through}.quarantined{background-color:#fdd}.dead{background-color:#ccc}</style> </template> </dom-module><script>!function(){SwarmingBehaviors.BotPageBehavior=[SwarmingBehaviors.CommonBehavior,{}]}()</script> <dom-module id="bot-page-data" assetpath="/res/imp/botpage/"> <script>!function(){function t(t){if(!s){var e=t.toString();s=e.substring(e.indexOf("("))}return t.toLocaleString()+" "+s}var e,s,i=400,n=["first_seen_ts","last_seen_ts","lease_expiration_ts"],a=["started_ts","completed_ts","abandoned_ts","modified_ts"];Polymer({is:"bot-page-data",behaviors:[SwarmingBehaviors.BotPageBehavior],properties:{auth_headers:{type:Object},bot_id:{type:String},busy:{type:Boolean,computed:"_or(_busy1)",notify:!0},bot:{type:Object,computed:"_parseBot(_bot)",notify:!0},bot_exists:{type:Boolean,value:!0,notify:!0},_busy1:{type:Boolean,value:!1},_bot:{type:Object},_events:{type:Object},_tasks:{type:Object}},observers:["request(auth_headers,bot_id)"],request:function(){this.bot_id&&this.auth_headers&&(e&&this.cancelAsync(e),e=this.async(function(){e=void 0;var t="/api/swarming/v1/bot/"+this.bot_id,s=this._getJsonAsync("_bot",t+"/get","_busy1",this.auth_headers);s.then(function(){this.set("bot_exists",!0)}.bind(this)).catch(function(t){404===t.status?this.set("bot_exists",!1):sk.errorMessage("Http response: "+(t.status||" ")+" "+t.response)}.bind(this)),this.fire("reload",{id:this.bot_id})},i))},_parseBot:function(e){if(!e)return{};e.state=e.state||"{}",e.state=JSON.parse(e.state)||{};var s=e.state.disks||{},i=Object.keys(s);if(i.length){e.disks=[];for(var a=0;a<i.length;a++)e.disks.push({id:i[a],mb:s[i[a]].free_mb});e.disks.sort(function(t,e){return e.mb-t.mb})}else e.disks=[{id:"unknown",mb:0}];return e.dimensions=e.dimensions||[],e.dimensions.forEach(function(t){swarming.alias.DIMENSIONS_WITH_ALIASES.indexOf(t.key)!==-1&&t.value.forEach(function(e,s){t.value[s]=swarming.alias.apply(e,t.key)})}),n.forEach(function(s){e[s]&&(e[s]=new Date(e[s]),e["human_"+s]=t(e[s]))}),e},parseEvents:function(e){if(!e||!e.items)return[];var e=e.items;return e.forEach(function(e){e.ts&&(e.ts=new Date(e.ts),e.human_ts=t(e.ts))}),e.sort(function(t,e){return e.ts-t.ts}),e},parseTasks:function(e){if(!e||!e.items)return[];var e=e.items;return e.forEach(function(e){if(a.forEach(function(s){e[s]&&(e[s]=new Date(e[s]),e["human_"+s]=t(e[s]))}),e.duration)e.human_duration=this._humanDuration(e.duration);else{var s=e.completed_ts||e.abandoned_ts||e.modified_ts||new Date;e.human_duration=this._timeDiffExact(e.started_ts,s),e.duration=(s.getTime()-e.started_ts)/1e3}e.state=e.state||"UNKNOWN","COMPLETED"===e.state&&(e.failure?e.state="FAILURE":e.state="SUCCESS")}.bind(this)),e.sort(function(t,e){return e.started_ts-t.started_ts}),e}})}()</script> </dom-module> <dom-module id="bot-page-summary" assetpath="/res/imp/botpage/"> <template> <style include="single-page-style">.wrapper{display:table;margin-left:auto;margin-bottom:10px;margin-right:5px}paper-checkbox{margin-left:5px}.thick{border-top-style:solid}</style> <url-param name="show_full_names" value="{{_show_full_names}}"> </url-param> <url-param name="show_all_tasks" value="{{_show_all_tasks}}"> </url-param> <url-param name="sort_stats" value="{{_sortstr}}" default_value="total:desc"> </url-param> <div class="wrapper"> <table> <thead on-sort_change="_sortChange"> <tr> <th> <span>Name</span> <sort-toggle name="full_name" current="[[_sort]]"> </sort-toggle> </th> <th> <span>Total</span> <sort-toggle name="total" current="[[_sort]]"> </sort-toggle> </th> <th> <span>Success</span> <sort-toggle name="success" current="[[_sort]]"> </sort-toggle> </th> <th> <span>Failed</span> <sort-toggle name="failed" current="[[_sort]]"> </sort-toggle> </th> <th> <span>Died</span> <sort-toggle name="bot_died" current="[[_sort]]"> </sort-toggle> </th> <th> <span>Average Duration</span> <sort-toggle name="avg_duration" current="[[_sort]]"> </sort-toggle> </th> <th> <span>Total Duration</span> <sort-toggle name="total_time" current="[[_sort]]"> </sort-toggle> </th> <th>Percent of Total</th> </tr> </thead> <tbody> <template is="dom-repeat" items="[[_tasksToShow]]" as="task"> <tr> <td hidden$="[[_truthy(_show_full_names)]]" title="[[task.full_name]]">[[task.name]]</td> <td hidden$="[[_not(_show_full_names)]]" title="[[task.full_name]]">[[task.full_name]]</td> <td>[[task.total]]</td> <td>[[task.success]]</td> <td>[[task.failed]]</td> <td>[[task.bot_died]]</td> <td>[[_humanDuration(task.avg_duration)]]</td> <td>[[_humanDuration(task.total_time)]]</td> <td>[[task.total_time_percent]]%</td> </tr> </template> </tbody> <tbody><tr class="thick"> <td>Total</td> <td>[[_totalStats.total]]</td> <td>[[_totalStats.success]]</td> <td>[[_totalStats.failed]]</td> <td>[[_totalStats.bot_died]]</td> <td>[[_humanDuration(_totalStats.avg_duration)]]</td> <td>[[_humanDuration(_totalStats.total_time)]]</td> <td>100.0%</td> </tr> </tbody></table> <div> <table> <thead> <tr> <th title="How much time passed between the oldest task fetched and now."> Total Wall Time </th> <th title="How much of the wall time this bot was busy with a task."> Wall Time Utilization </th> </tr> </thead> <tbody> <tr> <td>[[_humanDuration(_totalStats.wall_time)]]</td> <td>[[_totalStats.wall_time_utilization]]%</td> </tr> </tbody> </table> <paper-checkbox checked="{{_show_full_names}}"> Show Full Names </paper-checkbox> <paper-checkbox hidden$="[[_cannotExpand]]" checked="{{_show_all_tasks}}"> Show All Tasks </paper-checkbox> </div> </div> </template> <script>!function(){var t=15;Polymer({is:"bot-page-summary",behaviors:[SwarmingBehaviors.BotPageBehavior],properties:{tasks:{type:Array},_cannotExpand:{type:Boolean,computed:"_countTasks(_taskStats.*)"},_show_all_tasks:{type:Boolean},_show_full_names:{type:Boolean},_sortstr:{type:String},_sort:{type:Object,computed:"_makeSortObject(_sortstr)"},_taskStats:{type:Array},_tasksToShow:{type:Array,computed:"_sortAndLimitTasks(_taskStats.*,_sort.*,_show_all_tasks)"},_totalStats:{type:Object}},observers:["_aggregate(tasks.*)"],_aggregate:function(){if(this.tasks&&this.tasks.length){var t=new Date(1e3*sk.now()),a={},e={total:this.tasks.length,success:0,failed:0,bot_died:0,avg_duration:0,total_time:0,wall_time:(t-this.tasks[this.tasks.length-1].started_ts)/1e3};this.tasks.forEach(function(t){var s=t.name.trim(),i=s.split("/");5===i.length&&(s=i[0]+"/"+i[3]),a[s]||(a[s]={full_name:s,name:s,total:0,success:0,failed:0,bot_died:0,avg_duration:0,total_time:0}),a[s].total++,t.failure?(e.failed++,a[s].failed++):t.internal_failure?(e.bot_died++,a[s].bot_died++):(e.success++,a[s].success++),e.total_time+=t.duration,a[s].total_time+=t.duration}),e.avg_duration=e.total_time/e.total,e.wall_time_utilization=(100*e.total_time/e.wall_time).toFixed(1),this.set("_totalStats",e);var s=Object.keys(a),i=[];s.forEach(function(t){a[t].avg_duration=a[t].total_time/a[t].total,a[t].total_time_percent=(100*a[t].total_time/e.total_time).toFixed(1),i.push(a[t])});var o=new Substrings({minOccurrence:Math.max(2,s.length-1),minLength:6});o.build(s);var n=o.weighByAverage()||[];n.length&&n[0].source.forEach(function(t){var a=i[t].full_name;i[t].name=a.replace(n[0].name,"...")}),this.set("_taskStats",i)}},_compare:function(t,a){if(!this._sort)return 0;var e=1;return"desc"===this._sort.direction&&(e=-1),e*swarming.naturalCompare(t[this._sort.name],a[this._sort.name])},_countTasks:function(){return this._taskStats.length<=t},_makeSortObject:function(t){if(t){var a=t.split(":");return 2!=a.length?{name:"full_name",direction:"asc"}:{name:a[0],direction:a[1]}}},_sortAndLimitTasks:function(){swarming.stableSort(this._taskStats,this._compare.bind(this));var a=this._taskStats.length;return!this._show_all_tasks&&this._taskStats.length>t&&(a=t),this._taskStats.slice(0,a)},_sortChange:function(t){t&&t.detail&&t.detail.name&&(t.preventDefault(),t.stopPropagation(),this.set("_sortstr",t.detail.name+":"+t.detail.direction))}})}()</script> </dom-module><dom-module id="bot-page" assetpath="/res/imp/botpage/"> <template> <style include="iron-flex iron-flex-alignment iron-positioning swarming-app-style single-page-style task-style">.message{white-space:pre-line;font-family:monospace}.bot_state{white-space:pre;font-family:monospace;margin-bottom:10px}.events_table,.tasks_table{border:3px solid #1F78B4}.old_version{background-color:#ffd}.stats{min-width:700px;flex-grow:2}#collapse{max-width:700px}.cloud{white-space:nowrap;margin-bottom:5px;margin-top:auto}paper-checkbox{--paper-checkbox-label-color:#fff;--paper-checkbox-checked-color:#fff;--paper-checkbox-checkmark-color:#000;--paper-checkbox-unchecked-color:#fff;padding:3px}paper-dialog{border-radius:6px}</style> <url-param name="id" value="{{bot_id}}"> </url-param> <url-param name="show_all_events" value="{{_show_all}}"> </url-param> <url-param name="selected" value="{{_selected}}"> </url-param> <url-param name="show_state" value="{{_show_state}}"> </url-param> <swarming-app client_id="[[client_id]]" auth_headers="{{_auth_headers}}" permissions="{{_permissions}}" server_details="{{_server_details}}" signed_in="{{_signed_in}}" busy="[[_or(_busy1,_busy2,_busy3)]]" name="Swarming Bot Page"> <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2> <div hidden$="[[_not(_signed_in)]]"> <bot-page-data id="data" auth_headers="[[_auth_headers]]" bot_id="[[bot_id]]" bot="{{_bot}}" bot_exists="{{_bot_exists}}" busy="{{_busy1}}" events="{{_events}}" tasks="{{_tasks}}" on-reload="_clearAndReload"> </bot-page-data> <div class="header horizontal layout"> <paper-input class="id_input" label="Bot id" value="{{bot_id}}"></paper-input> <template is="dom-if" if="[[_ccLink(_bot)]]"> <div class="vertical layout"> <a href$="[[_ccLink(_bot)]]" class="cloud">Cloud Console</a> </div> </template> <button on-click="_refresh"> <iron-icon class="refresh" icon="icons:refresh"></iron-icon> </button> </div> </div> <h2 hidden$="[[_bot_exists]]">Bot not found.</h2> <div hidden$="[[_not(_bot_exists)]]"> <div class="horizontal wrap layout"> <div class="flex"> <table> <tbody><tr class$="[[_isDead(_bot)]]" title="Last time the bot contacted the server."> <td>Last Seen</td> <td title="[[_bot.human_last_seen_ts]]"> [[_timeDiffExact(_bot.last_seen_ts)]] ago</td> <td> <template is="dom-if" if="[[_canShutdown(_bot,_permissions)]]"> <button class="raised" on-click="_promptShutdown"> Shut Down Gracefully </button> </template> <template is="dom-if" if="[[_canDelete(_bot,_permissions)]]"> <button class="raised" on-click="_promptDelete"> Delete </button> </template> </td> </tr> <template is="dom-if" if="[[_bot.quarantined]]"> <tr class="quarantined"> <td>Quarantined</td> <td colspan="2" class="message">[[_quarantineMessage(_bot)]]</td> </tr> </template> <tr> <td>Current Task</td> <td> <a target="_blank" rel="noopener" href$="[[_taskLink(_bot.task_id)]]"> [[_task(_bot)]] </a> </td> <td> </td> </tr> <tr> <td rowspan$="[[_numRows(_bot.dimensions)]]">Dimensions</td> </tr> <template is="dom-repeat" items="[[_bot.dimensions]]" as="dim"> <tr> <td>[[dim.key]]</td> <td>[[_concat(dim.value)]]</td> </tr> </template> <tr title="IP address that the server saw the connection from."> <td>External IP</td> <td><a href$="[[_bot.external_ip]]">[[_bot.external_ip]]</a></td> <td></td> </tr> <tr class$="[[_classVersion(_server_details.bot_version,_bot.version)]]" title="Version is based on the content of swarming_bot.zip which is the swarming bot code. The bot won't update if quarantined, dead, or busy."> <td>Bot Version</td> <td>[[_shorten(_bot.version,'8')]]</td> <td></td> </tr> <tr title="The version the server expects the bot to be using."> <td>Expected Bot Version</td> <td>[[_shorten(_server_details.bot_version,'8')]]</td> <td></td> </tr> <tr title="First time ever a bot with this id contacted the server."> <td>First seen</td> <td title="[[_bot.human_first_seen_ts]]"> [[_timeDiffApprox(_bot.first_seen_ts)]] ago </td> <td></td> </tr> <tr title="How the bot is authenticated by the server."> <td>Authenticated as</td> <td colspan="2">[[_bot.authenticated_as]]</td> </tr> <template is="dom-if" if="[[_bot.lease_id]]"> <tr> <td>Machine Provider Lease ID</td> <td colspan="2"> <a href$="[[_mpLink(_bot,_server_details.machine_provider_template)]]"> [[_bot.lease_id]] </a> </td> </tr> <tr> <td>Machine Provider Lease Expires</td> <td colspan="2">[[_bot.human_lease_expiration_ts]]</td> </tr> </template> </tbody></table> <span class="title">State</span> <template is="dom-if" if="[[_not(_show_state)]]"> <button on-click="_toggleState"> <iron-icon icon="icons:add-circle-outline"></iron-icon> </button> </template> <template is="dom-if" if="[[_show_state]]"> <button on-click="_toggleState"> <iron-icon icon="icons:remove-circle-outline"></iron-icon> </button> </template> <iron-collapse id="collapse" opened="[[_show_state]]"> <div class="bot_state">[[_prettyPrint(_bot.state)]]</div> </iron-collapse> </div> <div class="stats flex"> <bot-page-summary tasks="[[_tasks]]"> </bot-page-summary> </div> </div> <div class="tabs"> <paper-tabs selected="{{_selected}}" no-bar=""> <paper-tab>Tasks</paper-tab> <paper-tab>Events</paper-tab> </paper-tabs> <template is="dom-if" if="[[_showEvents]]"> <paper-checkbox checked="{{_show_all}}"> Show all events </paper-checkbox> </template> </div> <template is="dom-if" if="[[_not(_showEvents)]]"> <table class="tasks_table"> <thead> <tr> <th>Task</th> <th>Started</th> <th>Duration</th> <th>Result</th> </tr> </thead> <tbody> <template is="dom-repeat" items="{{_tasks}}" as="task"> <tr class$="[[_taskClass(task)]]"> <td> <a target="_blank" rel="noopener" href$="[[_taskLink(task.task_id)]]"> [[task.name]] </a> </td> <td>[[task.human_started_ts]]</td> <td title="[[task.human_completed_ts]]">[[task.human_duration]]</td> <td>[[task.state]]</td> </tr> </template> </tbody> </table> </template> <template is="dom-if" if="[[_showEvents]]"> <table class="events_table"> <thead> <tr> <th>Message</th> <th>Type</th> <th>Timestamp</th> <th>Task ID</th> <th>Version</th> </tr> </thead> <tbody> <template is="dom-repeat" items="{{_eventList(_show_all,_events.*)}}" as="event"> <tr> <td class="message">[[event.message]]</td> <td>[[event.event_type]]</td> <td>[[event.human_ts]]</td> <td> <a target="_blank" rel="noopener" href$="[[_taskLink(event.task_id)]]"> [[event.task_id]] </a> </td> <td class$="[[_classVersion(_server_details.bot_version,event.version)]]"> [[_shorten(event.version,'8')]] </td> </tr> </template> </tbody> </table> </template> <pageable-data id="page_tasks" hidden$="[[_showEvents]]" busy="{{_busy2}}" label="Show more tasks" output="{{_tasks}}" parse="[[_parseTasks]]"> </pageable-data> <pageable-data id="page_events" hidden$="[[_not(_showEvents)]]" busy="{{_busy3}}" label="Show more events" output="{{_events}}" parse="[[_parseEvents]]"> </pageable-data> </div> </swarming-app> <paper-dialog id="prompt" modal="" on-iron-overlay-closed="_promptClosed"> <h2>Are you sure?</h2> <div>Are you sure you want to [[_dialogPrompt]]?</div> <div class="buttons"> <paper-button dialog-dismiss="" autofocus="">No</paper-button> <paper-button dialog-confirm="">Yes</paper-button> </div> </paper-dialog> <error-toast></error-toast> </template> <script> (function(){
|
| + })(); </script> </dom-module> <dom-module id="paper-button" assetpath="/res/imp/bower_components/paper-button/"> <template strip-whitespace=""> <style include="paper-material-shared-styles">:host{@apply(--layout-inline);@apply(--layout-center-center);position:relative;box-sizing:border-box;min-width:5.14em;margin:0 .29em;background:0 0;-webkit-tap-highlight-color:transparent;-webkit-tap-highlight-color:transparent;font:inherit;text-transform:uppercase;outline-width:0;border-radius:3px;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none;cursor:pointer;z-index:0;padding:.7em .57em;@apply(--paper-font-common-base);@apply(--paper-button)}:host([hidden]){display:none!important}:host([raised].keyboard-focus){font-weight:700;@apply(--paper-button-raised-keyboard-focus)}:host(:not([raised]).keyboard-focus){font-weight:700;@apply(--paper-button-flat-keyboard-focus)}:host([disabled]){background:#eaeaea;color:#a8a8a8;cursor:auto;pointer-events:none;@apply(--paper-button-disabled)}:host([animated]){@apply(--shadow-transition)}paper-ripple{color:var(--paper-button-ink-color)}</style> <content></content> </template> <script>Polymer({is:"paper-button",behaviors:[Polymer.PaperButtonBehavior],properties:{raised:{type:Boolean,reflectToAttribute:!0,value:!1,observer:"_calculateElevation"}},_calculateElevation:function(){this.raised?Polymer.PaperButtonBehaviorImpl._calculateElevation.apply(this):this._setElevation(0)}})</script> </dom-module> <script>Polymer.NeonAnimatableBehavior={properties:{animationConfig:{type:Object},entryAnimation:{observer:"_entryAnimationChanged",type:String},exitAnimation:{observer:"_exitAnimationChanged",type:String}},_entryAnimationChanged:function(){this.animationConfig=this.animationConfig||{},this.animationConfig.entry=[{name:this.entryAnimation,node:this}]},_exitAnimationChanged:function(){this.animationConfig=this.animationConfig||{},this.animationConfig.exit=[{name:this.exitAnimation,node:this}]},_copyProperties:function(i,n){for(var t in n)i[t]=n[t]},_cloneConfig:function(i){var n={isClone:!0};return this._copyProperties(n,i),n},_getAnimationConfigRecursive:function(i,n,t){if(this.animationConfig){if(this.animationConfig.value&&"function"==typeof this.animationConfig.value)return void this._warn(this._logf("playAnimation","Please put 'animationConfig' inside of your components 'properties' object instead of outside of it."));var o;if(o=i?this.animationConfig[i]:this.animationConfig,Array.isArray(o)||(o=[o]),o)for(var e,a=0;e=o[a];a++)if(e.animatable)e.animatable._getAnimationConfigRecursive(e.type||i,n,t);else if(e.id){var r=n[e.id];r?(r.isClone||(n[e.id]=this._cloneConfig(r),r=n[e.id]),this._copyProperties(r,e)):n[e.id]=e}else t.push(e)}},getAnimationConfig:function(i){var n={},t=[];this._getAnimationConfigRecursive(i,n,t);for(var o in n)t.push(n[o]);return t}}</script> <script>Polymer.NeonAnimationRunnerBehaviorImpl={_configureAnimations:function(n){var i=[];if(n.length>0)for(var e,t=0;e=n[t];t++){var o=document.createElement(e.name);if(o.isNeonAnimation){var a=null;try{a=o.configure(e),"function"!=typeof a.cancel&&(a=document.timeline.play(a))}catch(n){a=null,console.warn("Couldnt play","(",e.name,").",n)}a&&i.push({neonAnimation:o,config:e,animation:a})}else console.warn(this.is+":",e.name,"not found!")}return i},_shouldComplete:function(n){for(var i=!0,e=0;e<n.length;e++)if("finished"!=n[e].animation.playState){i=!1;break}return i},_complete:function(n){for(var i=0;i<n.length;i++)n[i].neonAnimation.complete(n[i].config);for(var i=0;i<n.length;i++)n[i].animation.cancel()},playAnimation:function(n,i){var e=this.getAnimationConfig(n);if(e){this._active=this._active||{},this._active[n]&&(this._complete(this._active[n]),delete this._active[n]);var t=this._configureAnimations(e);if(0==t.length)return void this.fire("neon-animation-finish",i,{bubbles:!1});this._active[n]=t;for(var o=0;o<t.length;o++)t[o].animation.onfinish=function(){this._shouldComplete(t)&&(this._complete(t),delete this._active[n],this.fire("neon-animation-finish",i,{bubbles:!1}))}.bind(this)}},cancelAnimation:function(){for(var n in this._animations)this._animations[n].cancel();this._animations={}}},Polymer.NeonAnimationRunnerBehavior=[Polymer.NeonAnimatableBehavior,Polymer.NeonAnimationRunnerBehaviorImpl]</script> <script>Polymer.PaperDialogBehaviorImpl={hostAttributes:{role:"dialog",tabindex:"-1"},properties:{modal:{type:Boolean,value:!1}},observers:["_modalChanged(modal, _readied)"],listeners:{tap:"_onDialogClick"},ready:function(){this.__prevNoCancelOnOutsideClick=this.noCancelOnOutsideClick,this.__prevNoCancelOnEscKey=this.noCancelOnEscKey,this.__prevWithBackdrop=this.withBackdrop},_modalChanged:function(i,e){e&&(i?(this.__prevNoCancelOnOutsideClick=this.noCancelOnOutsideClick,this.__prevNoCancelOnEscKey=this.noCancelOnEscKey,this.__prevWithBackdrop=this.withBackdrop,this.noCancelOnOutsideClick=!0,this.noCancelOnEscKey=!0,this.withBackdrop=!0):(this.noCancelOnOutsideClick=this.noCancelOnOutsideClick&&this.__prevNoCancelOnOutsideClick,this.noCancelOnEscKey=this.noCancelOnEscKey&&this.__prevNoCancelOnEscKey,this.withBackdrop=this.withBackdrop&&this.__prevWithBackdrop))},_updateClosingReasonConfirmed:function(i){this.closingReason=this.closingReason||{},this.closingReason.confirmed=i},_onDialogClick:function(i){for(var e=Polymer.dom(i).path,o=0;o<e.indexOf(this);o++){var t=e[o];if(t.hasAttribute&&(t.hasAttribute("dialog-dismiss")||t.hasAttribute("dialog-confirm"))){this._updateClosingReasonConfirmed(t.hasAttribute("dialog-confirm")),this.close(),i.stopPropagation();break}}}},Polymer.PaperDialogBehavior=[Polymer.IronOverlayBehavior,Polymer.PaperDialogBehaviorImpl]</script> <dom-module id="paper-dialog-shared-styles" assetpath="/res/imp/bower_components/paper-dialog-behavior/"> <template> <style>:host{display:block;margin:24px 40px;background:var(--paper-dialog-background-color,--primary-background-color);color:var(--paper-dialog-color,--primary-text-color);@apply(--paper-font-body1);@apply(--shadow-elevation-16dp);@apply(--paper-dialog)}:host>::content>*{margin-top:20px;padding:0 24px}:host>::content>.no-padding{padding:0}:host>::content>:first-child{margin-top:24px}:host>::content>:last-child{margin-bottom:24px}:host>::content h2{position:relative;margin:0;@apply(--paper-font-title);@apply(--paper-dialog-title)}:host>::content .buttons{position:relative;padding:8px 8px 8px 24px;margin:0;color:var(--paper-dialog-button-color,--primary-color);@apply(--layout-horizontal);@apply(--layout-end-justified)}</style> </template> </dom-module> <dom-module id="paper-dialog" assetpath="/res/imp/bower_components/paper-dialog/"> <template> <style include="paper-dialog-shared-styles"></style> <content></content> </template> </dom-module> <script>!function(){Polymer({is:"paper-dialog",behaviors:[Polymer.PaperDialogBehavior,Polymer.NeonAnimationRunnerBehavior],listeners:{"neon-animation-finish":"_onNeonAnimationFinish"},_renderOpened:function(){this.cancelAnimation(),this.playAnimation("entry")},_renderClosed:function(){this.cancelAnimation(),this.playAnimation("exit")},_onNeonAnimationFinish:function(){this.opened?this._finishRenderOpened():this._finishRenderClosed()}})}()</script> <script>window.SwarmingBehaviors=window.SwarmingBehaviors||{},function(){SwarmingBehaviors.TaskBehavior={properties:{BOT_DIED:{type:String,value:"BOT_DIED"},CANCELED:{type:String,value:"CANCELED"},COMPLETED:{type:String,value:"COMPLETED"},COMPLETED_DEDUPED:{type:String,value:"COMPLETED (DEDUPED)"},COMPLETED_FAILURE:{type:String,value:"COMPLETED (FAILURE)"},COMPLETED_SUCCESS:{type:String,value:"COMPLETED (SUCCESS)"},EXPIRED:{type:String,value:"EXPIRED"},PENDING:{type:String,value:"PENDING"},RUNNING:{type:String,value:"RUNNING"},TIMED_OUT:{type:String,value:"TIMED_OUT"}},stateClass:function(t){return t===this.CANCELED||t===this.TIMED_OUT||t===this.EXPIRED?"exception":t===this.BOT_DIED?"bot_died":t===this.COMPLETED_FAILURE?"failed_task":t===this.RUNNING||t===this.PENDING?"pending_task":""},state:function(t){return t?t.state===this.COMPLETED?t.failure?this.COMPLETED_FAILURE:"0"===t.try_number?this.COMPLETED_DEDUPED:this.COMPLETED_SUCCESS:t.state:""},_stateClass:function(t){return this.stateClass(this.state(t))}}}()</script> <dom-module id="task-style" assetpath="/res/imp/common/"> <template> <style>.failed_task{background-color:#fdd}.bot_died{background-color:#ccc}.exception{background-color:#edd2ff}.pending_task{background-color:#fffc6c}</style> </template> </dom-module><dom-module id="task-filters" assetpath="/res/imp/tasklist/"> <template> <style is="custom-style" include="iron-flex iron-flex-alignment iron-positioning query-column-filter-style">.item.wide{max-width:400px}.selector.wide{min-width:275px}</style> <url-param name="f" value="{{_filters}}" default_values="[]" multi=""> </url-param> <url-param name="c" value="{{columns}}" default_values="["name","state","created_ts","user"]" multi=""> </url-param> <url-param name="q" value="{{_query}}" default_value=""> </url-param> <url-param name="l" default_value="50" value="{{_limit}}"> </url-param> <div class="container horizontal layout"> <div class="narrow-down-selector"> <div> <iron-a11y-keys target="[[_filter_input]]" keys="enter" on-keys-pressed="_manuallyAddFilter"></iron-a11y-keys> <paper-input id="filter" label="Search columns and filters or supply a filter and press enter" placeholder="gpu nvidia pool:Chrome" value="{{_query::input}}"> </paper-input> </div> <div class="wide selector side-by-side" title="This shows all task tags and other interesting task properties. Mark the check box to add as a column. Select the row to see filter options."> <iron-selector attr-for-selected="label" selected="{{_primarySelected}}"> <template is="dom-repeat" items="[[_primaryItems]]" as="item"> <div class="selectable item horizontal layout" label="[[item]]"> <span>[[_beforeBold(item,_query)]]<span class="bold">[[_bold(item,_query)]]</span>[[_afterBold(item,_query)]]</span> <span class="flex"></span> <paper-checkbox noink="" disabled$="[[_cantToggleColumn(item)]]" checked="[[_columnState(item,columns.*)]]" on-change="_toggleColumn"> </paper-checkbox> </div> </template> </iron-selector> </div> <div class="selector side-by-side" title="These are most of the options (if any) that the task list can be filtered on. This list is all that have been seen recently, augmented with the ones returned by the current query. There may be others."> <template is="dom-repeat" id="secondaryList" items="[[_secondaryItems]]" as="item"> <div class="item horizontal layout" label="[[item]]"> <span>[[_beforeBold(item,_query)]]<span class="bold">[[_bold(item,_query)]]</span>[[_afterBold(item,_query)]]</span> <span class="flex"></span> <iron-icon class="icons" icon="icons:arrow-forward" hidden="[[_cantAddFilter(_primarySelected,item,_filters.*)]]" on-tap="_addFilter"> </iron-icon> </div> </template> </div> <div class="selector side-by-side" title="These tag filters are AND'd together and applied to all tasks."> <template is="dom-repeat" items="[[_filters]]" as="fil"> <div class="item horizontal layout" label="[[fil]]"> <span>[[fil]]</span> <span class="flex"></span> <iron-icon class="icons" icon="icons:remove-circle-outline" hidden="[[_cantRemoveFilter(fil,_filters.*)]]" on-tap="_removeFilter"> </iron-icon> </div> </template> </div> <div class="side-by-side"> <paper-input id="_limit" label="Limit Results" auto-validate="" min="0" max="1000" pattern="[0-9]+" value="{{_limit}}"> </paper-input> </div> </div> </div> </template> <script>!function(){var t={state:function(t,i){var r=this._attribute(t,"state")[0];if(i===r||"ALL"===i)return!0;if("PENDING_RUNNING"===i)return r===this.PENDING||r===this.RUNNING;var e=this._attribute(t,"failure",!1)[0];if("COMPLETED_SUCCESS"===i)return r===this.COMPLETED&&!e;if("COMPLETED_FAILURE"===i)return r===this.COMPLETED&&e;var a=this._attribute(t,"try_number","-1")[0];return"DEDUPED"===i?r===this.COMPLETED&&"0"===a:void 0}};Polymer({is:"task-filters",behaviors:[SwarmingBehaviors.QueryColumnFilter,SwarmingBehaviors.TaskBehavior],properties:{columns:{type:Array,notify:!0},query_params:{type:Object,computed:"_extractQueryParams(_filters.*, _limit)",notify:!0},_filterMap:{type:Object,value:function(){var i=this._commonFilters();for(var r in t)i[r]=t[r];return i}}},_cantToggleColumn:function(t){return!t||"name"===t},_extractQueryParams:function(){var t={},i=[];this._filters.forEach(function(r){var e=r.split(this.FILTER_SEP,1),a=e[0],s=r.substring(a.length+this.FILTER_SEP.length);"state"===a?t.state=[s]:("user"===a&&"none"===s&&(s=""),i.push(a+this.FILTER_SEP+swarming.alias.unapply(s)))}.bind(this)),t.tags=i;var r=parseInt(this._limit);return Number.isInteger(r)&&(r=Math.max(r,1),r=Math.min(1e3,r),t.limit=[r],this._limit!=r&&this.set("_limit",r)),t}})}()</script> </dom-module><dom-module id="task-list-data" assetpath="/res/imp/tasklist/"> <script>!function(){var t=["abandoned_ts","completed_ts","created_ts","modified_ts","started_ts"];Polymer({is:"task-list-data",behaviors:[SwarmingBehaviors.CommonBehavior,SwarmingBehaviors.TaskBehavior],properties:{auth_headers:{type:Object,observer:"signIn"},query_params:{type:Object},tasks:{type:Array},busy:{type:Boolean,computed:"_or(_busy2,_busy1)",notify:!0},primary_map:{type:Object,computed:"_primaryMap(_tags,_dimensions,tasks.*)",notify:!0},primary_arr:{type:Array,computed:"_primaryArr(primary_map)",notify:!0},_busy2:{type:Boolean,value:!1},_busy1:{type:Boolean,value:!1},_dimensions:{type:Object},_list:{type:Object},_tags:{type:Object}},signIn:function(){this._getJsonAsync("_tags","/api/swarming/v1/tasks/tags","_busy2",this.auth_headers),this._getJsonAsync("_dimensions","/api/swarming/v1/bots/dimensions","_busy1",this.auth_headers)},_primaryArr:function(t){var a=Object.keys(t);return a.sort(),a},_primaryMap:function(a,s){a=a&&a.tasks_tags||[],s=s&&s.bots_dimensions||[],tasks=this.tasks||[];var e={};a.forEach(function(t){e[t.key]||(e[t.key]={});var a=t.value||[];a.forEach(function(a){e[t.key][a]=!0})}),s.forEach(function(t){var a=t.value;e[t.key]||(e[t.key]={}),a.forEach(function(a){e[t.key][a]=!0})}),tasks.forEach(function(t){Object.keys(t.tagMap).forEach(function(a){var s=t.tagMap[a];e[a]||(e[a]={}),e[a][s]=!0})}),e.user&&(delete e.user[""],e.user.none=!0);var r={};for(key in e){var i=Object.keys(e[key]);if(swarming.alias.DIMENSIONS_WITH_ALIASES.indexOf(key)===-1)r[key]=i;else{var n=[];i.forEach(function(t){n.push(swarming.alias.apply(t,key))}),r[key]=n}}return r.name=[],r.state=[this.PENDING,this.RUNNING,"PENDING_RUNNING",this.COMPLETED,"COMPLETED_SUCCESS","COMPLETED_FAILURE",this.EXPIRED,this.TIMED_OUT,this.BOT_DIED,this.CANCELED,"DEDUPED","ALL"],r.costs_usd=[],r.deduped_from=[],r.duration=[],r.server_versions=[],r.bot=[],t.forEach(function(t){r[t]=[]}),r},parseTasks:function(a){if(!a||!a.items)return[];var s=new Date;return a.items.forEach(function(a){var e={};a.tags=a.tags||[],a.tags.forEach(function(t){var a=t.split(":",1),s=a[0],r=t.substring(s.length+1);e[s]=r}),a.tagMap=e,t.forEach(function(t){a[t]&&(a[t]=new Date(a[t]),a["human_"+t]=sk.human.localeTime(a[t]))}),!a.duration&&a.state===this.RUNNING&&a.started_ts&&(a.duration=(s-a.started_ts)/1e3),a.duration&&(a.human_duration=this._humanDuration(a.duration))}.bind(this)),a.items}})}()</script> </dom-module> <dom-module id="task-list" assetpath="/res/imp/tasklist/"> <template> <style include="iron-flex iron-flex-alignment iron-positioning swarming-app-style dynamic-table-style task-style">task-filters{margin-bottom:8px;margin-right:10px}.task-list th>span{padding-right:30px}</style> <url-param name="s" value="{{_sortstr}}" default_value="created_ts:desc"> </url-param> <swarming-app client_id="[[client_id]]" auth_headers="{{_auth_headers}}" permissions="{{_permissions}}" signed_in="{{_signed_in}}" busy="[[_or(_busy1,_busy2)]]" name="Swarming Task List"> <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2> <div hidden$="[[_not(_signed_in)]]"> <task-list-data id="data" auth_headers="[[_auth_headers]]" query_params="[[_query_params]]" tasks="[[_items]]" busy="{{_busy1)}}" primary_map="{{_primary_map}}" primary_arr="{{_primary_arr}}"> </task-list-data> <div class="horizontal layout"> <task-filters primary_map="[[_primary_map]]" primary_arr="[[_primary_arr]]" columns="{{_columns}}" query_params="{{_query_params}}" filter="{{_filter}}"> </task-filters> </div> <table class="task-list"> <thead on-sort_change="_sortChange"> <tr> <th> <span>Task Name</span> <sort-toggle name="name" current="[[_sort]]"> </sort-toggle> </th> <th hidden$="[[_hide('state', _columns.*)]]"> <span>State</span> <sort-toggle name="state" current="[[_sort]]"> </sort-toggle> </th> <th hidden$="[[_hide('bot', _columns.*)]]"> <span>Bot Assigned</span> <sort-toggle name="bot" current="[[_sort]]"> </sort-toggle> </th> <th hidden$="[[_hide('deduped_from', _columns.*)]]"> <span>Deduped from</span> <sort-toggle name="deduped_from" current="[[_sort]]"> </sort-toggle> </th> <th hidden$="[[_hide('source_revision', _columns.*)]]"> <span>Source Revision</span> <sort-toggle name="source_revision" current="[[_sort]]"> </sort-toggle> </th> <template is="dom-repeat" items="[[_plainColumns]]" as="c"> <th hidden$="[[_hide(c)]]"> <span>[[_header(c)]]</span> <sort-toggle name="[[c]]" current="[[_sort]]"> </sort-toggle> </th> </template> </tr> </thead> <tbody> <template id="tasks_table" is="dom-repeat" items="[[_filteredSortedItems]]" as="task" initial-count="50"> <tr class$="[[_taskClass(task)]]"> <td> <a class="center" href$="[[_taskLink(task.task_id)]]" target="_blank"> [[task.name]] </a> </td> <td hidden$="[[_hide('state', _columns.*)]]"> [[_column('state', task)]] <paper-button raised="" hidden$="[[_cannotCancel(task,_permissions)]]" on-tap="_promptCancel"> Cancel </paper-button> </td> <td hidden$="[[_hide('bot', _columns.*)]]"> <a class="center" href$="[[_botLink(task.bot_id)]]" target="_blank"> [[_column('bot',task)]] </a> </td> <td hidden$="[[_hide('deduped_from', _columns.*)]]"> <a class="center" href$="[[_taskLink(task.deduped_from)]]" target="_blank"> [[_column('deduped_from',task)]] </a> </td> <td hidden$="[[_hide('source_revision', _columns.*)]]"> <a class="center" href$="[[_sourceLink(task)]]" target="_blank"> [[_column('source_revision',task)]] </a> </td> <template is="dom-repeat" items="[[_plainColumns]]" as="c"> <td hidden$="[[_hide(c)]]"> [[_column(c, task)]] </td> </template> </tr> </template> </tbody> </table> <pageable-data id="page_tasks" busy="{{_busy2}}" label="Show more tasks" output="{{_items}}" parse="[[_parseTasks]]"> </pageable-data> </div> </swarming-app> <paper-dialog id="prompt" modal="" on-iron-overlay-closed="_promptClosed"> <h2>Are you sure?</h2> <div>Are you sure you want to [[_dialogPrompt]]?</div> <div class="buttons"> <paper-button dialog-dismiss="" autofocus="">No</paper-button> <paper-button dialog-confirm="">Yes</paper-button> </div> </paper-dialog> <error-toast></error-toast> </template> <script>!function(){function t(t){return function(e){return this._attribute(e,"human_"+t)[0]}}function e(t){return function(e,a,s){var r=this._attribute(a,t,"0")[0],i=this._attribute(s,t,"0")[0];return e*(r-i)}}var a=["deduped_from","name","state","bot","source_revision"],s={abandoned_ts:t("abandoned_ts"),bot:function(t){return this._attribute(t,"bot_id")[0]},completed_ts:t("completed_ts"),costs_usd:function(t){return this._attribute(t,"costs_usd",0)[0]},created_ts:t("created_ts"),duration:t("duration"),modified_ts:t("modified_ts"),source_revision:function(t){var e=this._attribute(t,"source_revision")[0];return e.substring(0,8)},started_ts:t("started_ts"),state:function(t){var e=this._attribute(t,"state")[0];if("COMPLETED"===e){if(this._attribute(t,"failure",!1)[0])return"COMPLETED (FAILURE)";var a=this._attribute(t,"try_number","-1")[0];return"0"===a?"COMPLETED (DEDUPED)":"COMPLETED (SUCCESS)"}return e}},r={user:"Requesting User"},i={abandoned_ts:e("abandoned_ts"),completed_ts:e("completed_ts"),created_ts:e("created_ts"),duration:e("duration"),modified_ts:e("modified_ts"),started_ts:e("started_ts")};Polymer({is:"task-list",behaviors:[SwarmingBehaviors.DynamicTableBehavior,SwarmingBehaviors.TaskBehavior],properties:{client_id:{type:String},_busy1:{type:Boolean,value:!1},_busy2:{type:Boolean,value:!1},_parseTasks:{type:Function,value:function(){return this.$.data.parseTasks.bind(this)}},_toCancel:{type:String},_columnMap:{type:Object,value:function(){var t=this._commonColumns();for(var e in s)t[e]=s[e];return t}},_headerMap:{type:Object,value:r},_specialColumns:{type:Array,value:a},_specialSort:{type:Object,value:i}},observers:["reload(_query_params,_auth_headers)"],_attribute:function(t,e,a){void 0===a&&(a="none");var s=this._tag(t,e)||t[e]||[a];return Array.isArray(s)?s:[s]},_cannotCancel:function(t,e){return!(e&&e.cancel_task&&"PENDING"===this._column("state",t))},_cancelTask:function(){var t="/api/swarming/v1/task/"+this._toCancel+"/cancel";swarming.postWithToast(t,"Canceling task "+this._toCancel,this._auth_headers),this.set("_toCancel","")},_promptClosed:function(t){t.detail.confirmed&&this._cancelTask()},_promptCancel:function(t){var e=t.model.task;return e&&e.task_id?(this.set("_toCancel",e.task_id),this.set("_dialogPrompt",'cancel task "'+e.name+'"'),void this.$.prompt.open()):void console.log("Missing task info",e)},reload:function(){if(this._auth_headers&&this._query_params){var t="/api/swarming/v1/tasks/list?"+sk.query.fromParamSet(this._query_params);this.$.page_tasks.load(t,this._auth_headers)}},_sourceLink:function(t){var e=this._attribute(t,"source_revision")[0],a=this._attribute(t,"source_repo")[0];return"none"!==e&&"none"!==a&&a.replace("%s",e)},_tag:function(t,e){if(t&&t.tagMap)return t.tagMap[e]},_taskClass:function(t){return this.stateClass(this._column("state",t))}})}()</script> </dom-module> <dom-module id="iron-collapse" assetpath="/res/imp/bower_components/iron-collapse/"> <template> <style>:host{display:block;transition-duration:var(--iron-collapse-transition-duration,300ms);overflow:visible}:host(.iron-collapse-closed){display:none}:host(:not(.iron-collapse-opened)){overflow:hidden}</style> <content></content> </template> </dom-module> <script>Polymer({is:"iron-collapse",behaviors:[Polymer.IronResizableBehavior],properties:{horizontal:{type:Boolean,value:!1,observer:"_horizontalChanged"},opened:{type:Boolean,value:!1,notify:!0,observer:"_openedChanged"},noAnimation:{type:Boolean},_desiredSize:{type:String,value:""}},get dimension(){return this.horizontal?"width":"height"},get _dimensionMax(){return this.horizontal?"maxWidth":"maxHeight"},get _dimensionMaxCss(){return this.horizontal?"max-width":"max-height"},hostAttributes:{role:"group","aria-hidden":"true","aria-expanded":"false"},listeners:{transitionend:"_transitionEnd"},attached:function(){this._transitionEnd()},toggle:function(){this.opened=!this.opened},show:function(){this.opened=!0},hide:function(){this.opened=!1},updateSize:function(i,t){if(i="auto"===i?"":i,this._desiredSize!==i){this._desiredSize=i,this._updateTransition(!1);var e=t&&!this.noAnimation&&this._isDisplayed;if(e){var n=this._calcSize();""===i&&(this.style[this._dimensionMax]="",i=this._calcSize()),this.style[this._dimensionMax]=n,this.scrollTop=this.scrollTop,this._updateTransition(!0),e=i!==n}this.style[this._dimensionMax]=i,e||this._transitionEnd()}},enableTransition:function(i){Polymer.Base._warn("`enableTransition()` is deprecated, use `noAnimation` instead."),this.noAnimation=!i},_updateTransition:function(i){this.style.transitionDuration=i&&!this.noAnimation?"":"0s"},_horizontalChanged:function(){this.style.transitionProperty=this._dimensionMaxCss;var i="maxWidth"===this._dimensionMax?"maxHeight":"maxWidth";this.style[i]="",this.updateSize(this.opened?"auto":"0px",!1)},_openedChanged:function(){this.setAttribute("aria-expanded",this.opened),this.setAttribute("aria-hidden",!this.opened),this.toggleClass("iron-collapse-closed",!1),this.toggleClass("iron-collapse-opened",!1),this.updateSize(this.opened?"auto":"0px",!0),this.opened&&this.focus()},_transitionEnd:function(){this.style[this._dimensionMax]=this._desiredSize,this.toggleClass("iron-collapse-closed",!this.opened),this.toggleClass("iron-collapse-opened",this.opened),this._updateTransition(!1),this.notifyResize()},get _isDisplayed(){var i=this.getBoundingClientRect();for(var t in i)if(0!==i[t])return!0;return!1},_calcSize:function(){return this.getBoundingClientRect()[this.dimension]+"px"}})</script> <script>Polymer.IronMenubarBehaviorImpl={hostAttributes:{role:"menubar"},keyBindings:{left:"_onLeftKey",right:"_onRightKey"},_onUpKey:function(e){this.focusedItem.click(),e.detail.keyboardEvent.preventDefault()},_onDownKey:function(e){this.focusedItem.click(),e.detail.keyboardEvent.preventDefault()},get _isRTL(){return"rtl"===window.getComputedStyle(this).direction},_onLeftKey:function(e){this._isRTL?this._focusNext():this._focusPrevious(),e.detail.keyboardEvent.preventDefault()},_onRightKey:function(e){this._isRTL?this._focusPrevious():this._focusNext(),e.detail.keyboardEvent.preventDefault()},_onKeydown:function(e){this.keyboardEventMatchesKeys(e,"up down left right esc")||this._focusWithKeyboardEvent(e)}},Polymer.IronMenubarBehavior=[Polymer.IronMenuBehavior,Polymer.IronMenubarBehaviorImpl]</script> <iron-iconset-svg name="paper-tabs" size="24"> <svg><defs> <g id="chevron-left"><path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"></path></g> <g id="chevron-right"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path></g> </defs></svg> </iron-iconset-svg> <dom-module id="paper-tab" assetpath="/res/imp/bower_components/paper-tabs/"> <template> <style>:host{@apply(--layout-inline);@apply(--layout-center);@apply(--layout-center-justified);@apply(--layout-flex-auto);position:relative;padding:0 12px;overflow:hidden;cursor:pointer;vertical-align:middle;@apply(--paper-font-common-base);@apply(--paper-tab)}:host(:focus){outline:0}:host([link]){padding:0}.tab-content{height:100%;transform:translateZ(0);-webkit-transform:translateZ(0);transition:opacity .1s cubic-bezier(.4,0,1,1);@apply(--layout-horizontal);@apply(--layout-center-center);@apply(--layout-flex-auto);@apply(--paper-tab-content)}:host(:not(.iron-selected))>.tab-content{opacity:.8;@apply(--paper-tab-content-unselected)}:host(:focus) .tab-content{opacity:1;font-weight:700}paper-ripple{color:var(--paper-tab-ink,--paper-yellow-a100)}.tab-content>::content>a{@apply(--layout-flex-auto);height:100%}</style> <div class="tab-content"> <content></content> </div> </template> <script>Polymer({is:"paper-tab",behaviors:[Polymer.IronControlState,Polymer.IronButtonState,Polymer.PaperRippleBehavior],properties:{link:{type:Boolean,value:!1,reflectToAttribute:!0}},hostAttributes:{role:"tab"},listeners:{down:"_updateNoink",tap:"_onTap"},attached:function(){this._updateNoink()},get _parentNoink(){var t=Polymer.dom(this).parentNode;return!!t&&!!t.noink},_updateNoink:function(){this.noink=!!this.noink||!!this._parentNoink},_onTap:function(t){if(this.link){var e=this.queryEffectiveChildren("a");if(!e)return;if(t.target===e)return;e.click()}}})</script> </dom-module> <dom-module id="paper-tabs" assetpath="/res/imp/bower_components/paper-tabs/"> <template> <style>:host{@apply(--layout);@apply(--layout-center);height:48px;font-size:14px;font-weight:500;overflow:hidden;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;-webkit-tap-highlight-color:transparent;@apply(--paper-tabs)}:host-context([dir=rtl]){@apply(--layout-horizontal-reverse)}#tabsContainer{position:relative;height:100%;white-space:nowrap;overflow:hidden;@apply(--layout-flex-auto)}#tabsContent{height:100%;-moz-flex-basis:auto;-ms-flex-basis:auto;flex-basis:auto}#tabsContent.scrollable{position:absolute;white-space:nowrap}#tabsContent.scrollable.fit-container,#tabsContent:not(.scrollable){@apply(--layout-horizontal)}#tabsContent.scrollable.fit-container{min-width:100%}#tabsContent.scrollable.fit-container>::content>*{-ms-flex:1 0 auto;-webkit-flex:1 0 auto;flex:1 0 auto}.hidden{display:none}.not-visible{opacity:0;cursor:default}paper-icon-button{width:48px;height:48px;padding:12px;margin:0 4px}#selectionBar{position:absolute;height:2px;bottom:0;left:0;right:0;background-color:var(--paper-tabs-selection-bar-color,--paper-yellow-a100);-webkit-transform:scale(0);transform:scale(0);-webkit-transform-origin:left center;transform-origin:left center;transition:-webkit-transform;transition:transform;@apply(--paper-tabs-selection-bar)}#selectionBar.align-bottom{top:0;bottom:auto}#selectionBar.expand{transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,1,1)}#selectionBar.contract{transition-duration:.18s;transition-timing-function:cubic-bezier(0,0,.2,1)}#tabsContent>::content>:not(#selectionBar){height:100%}</style> <paper-icon-button icon="paper-tabs:chevron-left" class$="[[_computeScrollButtonClass(_leftHidden, scrollable, hideScrollButtons)]]" on-up="_onScrollButtonUp" on-down="_onLeftScrollButtonDown" tabindex="-1"></paper-icon-button> <div id="tabsContainer" on-track="_scroll" on-down="_down"> <div id="tabsContent" class$="[[_computeTabsContentClass(scrollable, fitContainer)]]"> <div id="selectionBar" class$="[[_computeSelectionBarClass(noBar, alignBottom)]]" on-transitionend="_onBarTransitionEnd"></div> <content select="*"></content> </div> </div> <paper-icon-button icon="paper-tabs:chevron-right" class$="[[_computeScrollButtonClass(_rightHidden, scrollable, hideScrollButtons)]]" on-up="_onScrollButtonUp" on-down="_onRightScrollButtonDown" tabindex="-1"></paper-icon-button> </template> <script>Polymer({is:"paper-tabs",behaviors:[Polymer.IronResizableBehavior,Polymer.IronMenubarBehavior],properties:{noink:{type:Boolean,value:!1,observer:"_noinkChanged"},noBar:{type:Boolean,value:!1},noSlide:{type:Boolean,value:!1},scrollable:{type:Boolean,value:!1},fitContainer:{type:Boolean,value:!1},disableDrag:{type:Boolean,value:!1},hideScrollButtons:{type:Boolean,value:!1},alignBottom:{type:Boolean,value:!1},selectable:{type:String,value:"paper-tab"},autoselect:{type:Boolean,value:!1},autoselectDelay:{type:Number,value:0},_step:{type:Number,value:10},_holdDelay:{type:Number,value:1},_leftHidden:{type:Boolean,value:!1},_rightHidden:{type:Boolean,value:!1},_previousTab:{type:Object}},hostAttributes:{role:"tablist"},listeners:{"iron-resize":"_onTabSizingChanged","iron-items-changed":"_onTabSizingChanged","iron-select":"_onIronSelect","iron-deselect":"_onIronDeselect"},keyBindings:{"left:keyup right:keyup":"_onArrowKeyup"},created:function(){this._holdJob=null,this._pendingActivationItem=void 0,this._pendingActivationTimeout=void 0,this._bindDelayedActivationHandler=this._delayedActivationHandler.bind(this),this.addEventListener("blur",this._onBlurCapture.bind(this),!0)},ready:function(){this.setScrollDirection("y",this.$.tabsContainer)},detached:function(){this._cancelPendingActivation()},_noinkChanged:function(t){var e=Polymer.dom(this).querySelectorAll("paper-tab");e.forEach(t?this._setNoinkAttribute:this._removeNoinkAttribute)},_setNoinkAttribute:function(t){t.setAttribute("noink","")},_removeNoinkAttribute:function(t){t.removeAttribute("noink")},_computeScrollButtonClass:function(t,e,i){return!e||i?"hidden":t?"not-visible":""},_computeTabsContentClass:function(t,e){return t?"scrollable"+(e?" fit-container":""):" fit-container"},_computeSelectionBarClass:function(t,e){return t?"hidden":e?"align-bottom":""},_onTabSizingChanged:function(){this.debounce("_onTabSizingChanged",function(){this._scroll(),this._tabChanged(this.selectedItem)},10)},_onIronSelect:function(t){this._tabChanged(t.detail.item,this._previousTab),this._previousTab=t.detail.item,this.cancelDebouncer("tab-changed")},_onIronDeselect:function(t){this.debounce("tab-changed",function(){this._tabChanged(null,this._previousTab),this._previousTab=null},1)},_activateHandler:function(){this._cancelPendingActivation(),Polymer.IronMenuBehaviorImpl._activateHandler.apply(this,arguments)},_scheduleActivation:function(t,e){this._pendingActivationItem=t,this._pendingActivationTimeout=this.async(this._bindDelayedActivationHandler,e)},_delayedActivationHandler:function(){var t=this._pendingActivationItem;this._pendingActivationItem=void 0,this._pendingActivationTimeout=void 0,t.fire(this.activateEvent,null,{bubbles:!0,cancelable:!0})},_cancelPendingActivation:function(){void 0!==this._pendingActivationTimeout&&(this.cancelAsync(this._pendingActivationTimeout),this._pendingActivationItem=void 0,this._pendingActivationTimeout=void 0)},_onArrowKeyup:function(t){this.autoselect&&this._scheduleActivation(this.focusedItem,this.autoselectDelay)},_onBlurCapture:function(t){t.target===this._pendingActivationItem&&this._cancelPendingActivation()},get _tabContainerScrollSize(){return Math.max(0,this.$.tabsContainer.scrollWidth-this.$.tabsContainer.offsetWidth)},_scroll:function(t,e){if(this.scrollable){var i=e&&-e.ddx||0;this._affectScroll(i)}},_down:function(t){this.async(function(){this._defaultFocusAsync&&(this.cancelAsync(this._defaultFocusAsync),this._defaultFocusAsync=null)},1)},_affectScroll:function(t){this.$.tabsContainer.scrollLeft+=t;var e=this.$.tabsContainer.scrollLeft;this._leftHidden=0===e,this._rightHidden=e===this._tabContainerScrollSize},_onLeftScrollButtonDown:function(){this._scrollToLeft(),this._holdJob=setInterval(this._scrollToLeft.bind(this),this._holdDelay)},_onRightScrollButtonDown:function(){this._scrollToRight(),this._holdJob=setInterval(this._scrollToRight.bind(this),this._holdDelay)},_onScrollButtonUp:function(){clearInterval(this._holdJob),this._holdJob=null},_scrollToLeft:function(){this._affectScroll(-this._step)},_scrollToRight:function(){this._affectScroll(this._step)},_tabChanged:function(t,e){if(!t)return this.$.selectionBar.classList.remove("expand"),this.$.selectionBar.classList.remove("contract"),void this._positionBar(0,0);var i=this.$.tabsContent.getBoundingClientRect(),n=i.width,o=t.getBoundingClientRect(),s=o.left-i.left;if(this._pos={width:this._calcPercent(o.width,n),left:this._calcPercent(s,n)},this.noSlide||null==e)return this.$.selectionBar.classList.remove("expand"),this.$.selectionBar.classList.remove("contract"),void this._positionBar(this._pos.width,this._pos.left);var a=e.getBoundingClientRect(),l=this.items.indexOf(e),c=this.items.indexOf(t),r=5;this.$.selectionBar.classList.add("expand");var h=l<c,d=this._isRTL;d&&(h=!h),h?this._positionBar(this._calcPercent(o.left+o.width-a.left,n)-r,this._left):this._positionBar(this._calcPercent(a.left+a.width-o.left,n)-r,this._calcPercent(s,n)+r),this.scrollable&&this._scrollToSelectedIfNeeded(o.width,s)},_scrollToSelectedIfNeeded:function(t,e){var i=e-this.$.tabsContainer.scrollLeft;i<0?this.$.tabsContainer.scrollLeft+=i:(i+=t-this.$.tabsContainer.offsetWidth,i>0&&(this.$.tabsContainer.scrollLeft+=i))},_calcPercent:function(t,e){return 100*t/e},_positionBar:function(t,e){t=t||0,e=e||0,this._width=t,this._left=e,this.transform("translateX("+e+"%) scaleX("+t/100+")",this.$.selectionBar)},_onBarTransitionEnd:function(t){var e=this.$.selectionBar.classList;e.contains("expand")?(e.remove("expand"),e.add("contract"),this._positionBar(this._pos.width,this._pos.left)):e.contains("contract")&&e.remove("contract")}})</script> </dom-module> <dom-module id="single-page-style" assetpath="/res/imp/common/"> <template> <style>.header{max-width:800px}.title{font-size:1.5em;font-weight:700;margin:5px}.id_input{margin-left:5px;--paper-input-container-input:{font-size:2em};}.refresh{max-width:40px;max-height:40px;width:initial;height:initial}button{min-width:4em;max-height:55px}table{border-collapse:collapse;margin-left:5px;margin-bottom:8px}td,th{border:1px solid #BBB;padding:5px}.tabs{background-color:#1F78B4;color:#fff;max-width:600px;margin-left:5px;--paper-checkbox-label-color:#fff}paper-tab{background-color:#A6CEE3;color:#000}paper-tab.iron-selected{background-color:#1F78B4;color:#fff;font-weight:700;text-decoration:underline}paper-tab[disabled]{background-color:#AAA;text-decoration:line-through}.quarantined{background-color:#fdd}.dead{background-color:#ccc}</style> </template> </dom-module><script>!function(){SwarmingBehaviors.BotPageBehavior=[SwarmingBehaviors.CommonBehavior,{}]}()</script> <dom-module id="bot-page-data" assetpath="/res/imp/botpage/"> <script>!function(){function t(t){if(!s){var e=t.toString();s=e.substring(e.indexOf("("))}return t.toLocaleString()+" "+s}var e,s,i=400,n=["first_seen_ts","last_seen_ts","lease_expiration_ts"],a=["started_ts","completed_ts","abandoned_ts","modified_ts"];Polymer({is:"bot-page-data",behaviors:[SwarmingBehaviors.BotPageBehavior],properties:{auth_headers:{type:Object},bot_id:{type:String},busy:{type:Boolean,computed:"_or(_busy1)",notify:!0},bot:{type:Object,computed:"_parseBot(_bot)",notify:!0},bot_exists:{type:Boolean,value:!0,notify:!0},_busy1:{type:Boolean,value:!1},_bot:{type:Object},_events:{type:Object},_tasks:{type:Object}},observers:["request(auth_headers,bot_id)"],request:function(){this.bot_id&&this.auth_headers&&(e&&this.cancelAsync(e),e=this.async(function(){e=void 0;var t="/api/swarming/v1/bot/"+this.bot_id,s=this._getJsonAsync("_bot",t+"/get","_busy1",this.auth_headers);s.then(function(){this.set("bot_exists",!0)}.bind(this)).catch(function(t){404===t.status?this.set("bot_exists",!1):sk.errorMessage("Http response: "+(t.status||" ")+" "+t.response)}.bind(this)),this.fire("reload",{id:this.bot_id})},i))},_parseBot:function(e){if(!e)return{};e.state=e.state||"{}",e.state=JSON.parse(e.state)||{};var s=e.state.disks||{},i=Object.keys(s);if(i.length){e.disks=[];for(var a=0;a<i.length;a++)e.disks.push({id:i[a],mb:s[i[a]].free_mb});e.disks.sort(function(t,e){return e.mb-t.mb})}else e.disks=[{id:"unknown",mb:0}];return e.dimensions=e.dimensions||[],e.dimensions.forEach(function(t){swarming.alias.DIMENSIONS_WITH_ALIASES.indexOf(t.key)!==-1&&t.value.forEach(function(e,s){t.value[s]=swarming.alias.apply(e,t.key)})}),n.forEach(function(s){e[s]&&(e[s]=new Date(e[s]),e["human_"+s]=t(e[s]))}),e},parseEvents:function(e){if(!e||!e.items)return[];var e=e.items;return e.forEach(function(e){e.ts&&(e.ts=new Date(e.ts),e.human_ts=t(e.ts))}),e.sort(function(t,e){return e.ts-t.ts}),e},parseTasks:function(e){if(!e||!e.items)return[];var e=e.items;return e.forEach(function(e){if(a.forEach(function(s){e[s]&&(e[s]=new Date(e[s]),e["human_"+s]=t(e[s]))}),e.duration)e.human_duration=this._humanDuration(e.duration);else{var s=e.completed_ts||e.abandoned_ts||e.modified_ts||new Date;e.human_duration=this._timeDiffExact(e.started_ts,s),e.duration=(s.getTime()-e.started_ts)/1e3}e.state=e.state||"UNKNOWN","COMPLETED"===e.state&&(e.failure?e.state="FAILURE":e.state="SUCCESS")}.bind(this)),e.sort(function(t,e){return e.started_ts-t.started_ts}),e}})}()</script> </dom-module> <dom-module id="bot-page-summary" assetpath="/res/imp/botpage/"> <template> <style include="single-page-style">.wrapper{display:table;margin-left:auto;margin-bottom:10px;margin-right:5px}paper-checkbox{margin-left:5px}.thick{border-top-style:solid}</style> <url-param name="show_full_names" value="{{_show_full_names}}"> </url-param> <url-param name="show_all_tasks" value="{{_show_all_tasks}}"> </url-param> <url-param name="sort_stats" value="{{_sortstr}}" default_value="total:desc"> </url-param> <div class="wrapper"> <table> <thead on-sort_change="_sortChange"> <tr> <th> <span>Name</span> <sort-toggle name="full_name" current="[[_sort]]"> </sort-toggle> </th> <th> <span>Total</span> <sort-toggle name="total" current="[[_sort]]"> </sort-toggle> </th> <th> <span>Success</span> <sort-toggle name="success" current="[[_sort]]"> </sort-toggle> </th> <th> <span>Failed</span> <sort-toggle name="failed" current="[[_sort]]"> </sort-toggle> </th> <th> <span>Died</span> <sort-toggle name="bot_died" current="[[_sort]]"> </sort-toggle> </th> <th> <span>Average Duration</span> <sort-toggle name="avg_duration" current="[[_sort]]"> </sort-toggle> </th> <th> <span>Total Duration</span> <sort-toggle name="total_time" current="[[_sort]]"> </sort-toggle> </th> <th>Percent of Total</th> </tr> </thead> <tbody> <template is="dom-repeat" items="[[_tasksToShow]]" as="task"> <tr> <td hidden$="[[_truthy(_show_full_names)]]" title="[[task.full_name]]">[[task.name]]</td> <td hidden$="[[_not(_show_full_names)]]" title="[[task.full_name]]">[[task.full_name]]</td> <td>[[task.total]]</td> <td>[[task.success]]</td> <td>[[task.failed]]</td> <td>[[task.bot_died]]</td> <td>[[_humanDuration(task.avg_duration)]]</td> <td>[[_humanDuration(task.total_time)]]</td> <td>[[task.total_time_percent]]%</td> </tr> </template> </tbody> <tbody><tr class="thick"> <td>Total</td> <td>[[_totalStats.total]]</td> <td>[[_totalStats.success]]</td> <td>[[_totalStats.failed]]</td> <td>[[_totalStats.bot_died]]</td> <td>[[_humanDuration(_totalStats.avg_duration)]]</td> <td>[[_humanDuration(_totalStats.total_time)]]</td> <td>100.0%</td> </tr> </tbody></table> <div> <table> <thead> <tr> <th title="How much time passed between the oldest task fetched and now."> Total Wall Time </th> <th title="How much of the wall time this bot was busy with a task."> Wall Time Utilization </th> </tr> </thead> <tbody> <tr> <td>[[_humanDuration(_totalStats.wall_time)]]</td> <td>[[_totalStats.wall_time_utilization]]%</td> </tr> </tbody> </table> <paper-checkbox checked="{{_show_full_names}}"> Show Full Names </paper-checkbox> <paper-checkbox hidden$="[[_cannotExpand]]" checked="{{_show_all_tasks}}"> Show All Tasks </paper-checkbox> </div> </div> </template> <script>!function(){var t=15;Polymer({is:"bot-page-summary",behaviors:[SwarmingBehaviors.BotPageBehavior],properties:{tasks:{type:Array},_cannotExpand:{type:Boolean,computed:"_countTasks(_taskStats.*)"},_show_all_tasks:{type:Boolean},_show_full_names:{type:Boolean},_sortstr:{type:String},_sort:{type:Object,computed:"_makeSortObject(_sortstr)"},_taskStats:{type:Array},_tasksToShow:{type:Array,computed:"_sortAndLimitTasks(_taskStats.*,_sort.*,_show_all_tasks)"},_totalStats:{type:Object}},observers:["_aggregate(tasks.*)"],_aggregate:function(){if(this.tasks&&this.tasks.length){var t=new Date(1e3*sk.now()),a={},e={total:this.tasks.length,success:0,failed:0,bot_died:0,avg_duration:0,total_time:0,wall_time:(t-this.tasks[this.tasks.length-1].started_ts)/1e3};this.tasks.forEach(function(t){var s=t.name.trim(),i=s.split("/");5===i.length&&(s=i[0]+"/"+i[3]),a[s]||(a[s]={full_name:s,name:s,total:0,success:0,failed:0,bot_died:0,avg_duration:0,total_time:0}),a[s].total++,t.failure?(e.failed++,a[s].failed++):t.internal_failure?(e.bot_died++,a[s].bot_died++):(e.success++,a[s].success++),e.total_time+=t.duration,a[s].total_time+=t.duration}),e.avg_duration=e.total_time/e.total,e.wall_time_utilization=(100*e.total_time/e.wall_time).toFixed(1),this.set("_totalStats",e);var s=Object.keys(a),i=[];s.forEach(function(t){a[t].avg_duration=a[t].total_time/a[t].total,a[t].total_time_percent=(100*a[t].total_time/e.total_time).toFixed(1),i.push(a[t])});var o=new Substrings({minOccurrence:Math.max(2,s.length-1),minLength:6});o.build(s);var n=o.weighByAverage()||[];n.length&&n[0].source.forEach(function(t){var a=i[t].full_name;i[t].name=a.replace(n[0].name,"...")}),this.set("_taskStats",i)}},_compare:function(t,a){if(!this._sort)return 0;var e=1;return"desc"===this._sort.direction&&(e=-1),e*swarming.naturalCompare(t[this._sort.name],a[this._sort.name])},_countTasks:function(){return this._taskStats.length<=t},_makeSortObject:function(t){if(t){var a=t.split(":");return 2!=a.length?{name:"full_name",direction:"asc"}:{name:a[0],direction:a[1]}}},_sortAndLimitTasks:function(){swarming.stableSort(this._taskStats,this._compare.bind(this));var a=this._taskStats.length;return!this._show_all_tasks&&this._taskStats.length>t&&(a=t),this._taskStats.slice(0,a)},_sortChange:function(t){t&&t.detail&&t.detail.name&&(t.preventDefault(),t.stopPropagation(),this.set("_sortstr",t.detail.name+":"+t.detail.direction))}})}()</script> </dom-module><dom-module id="bot-page" assetpath="/res/imp/botpage/"> <template> <style include="iron-flex iron-flex-alignment iron-positioning swarming-app-style single-page-style task-style">.message{white-space:pre-line;font-family:monospace}.bot_state{white-space:pre;font-family:monospace;margin-bottom:10px}.events_table,.tasks_table{border:3px solid #1F78B4}.old_version{background-color:#ffd}.stats{min-width:700px;flex-grow:2}#collapse{max-width:700px}.cloud{white-space:nowrap;margin-bottom:5px;margin-top:auto}paper-checkbox{--paper-checkbox-label-color:#fff;--paper-checkbox-checked-color:#fff;--paper-checkbox-checkmark-color:#000;--paper-checkbox-unchecked-color:#fff;padding:3px}paper-dialog{border-radius:6px}</style> <url-param name="id" value="{{bot_id}}"> </url-param> <url-param name="show_all_events" value="{{_show_all}}"> </url-param> <url-param name="selected" value="{{_selected}}"> </url-param> <url-param name="show_state" value="{{_show_state}}"> </url-param> <swarming-app client_id="[[client_id]]" auth_headers="{{_auth_headers}}" permissions="{{_permissions}}" server_details="{{_server_details}}" signed_in="{{_signed_in}}" busy="[[_or(_busy1,_busy2,_busy3)]]" name="Swarming Bot Page"> <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2> <div hidden$="[[_not(_signed_in)]]"> <bot-page-data id="data" auth_headers="[[_auth_headers]]" bot_id="[[bot_id]]" bot="{{_bot}}" bot_exists="{{_bot_exists}}" busy="{{_busy1}}" events="{{_events}}" tasks="{{_tasks}}" on-reload="_clearAndReload"> </bot-page-data> <div class="header horizontal layout"> <paper-input class="id_input" label="Bot id" value="{{bot_id}}"></paper-input> <template is="dom-if" if="[[_ccLink(_bot)]]"> <div class="vertical layout"> <a href$="[[_ccLink(_bot)]]" class="cloud">Cloud Console</a> </div> </template> <button on-click="_refresh"> <iron-icon class="refresh" icon="icons:refresh"></iron-icon> </button> </div> </div> <h2 hidden$="[[_bot_exists]]">Bot not found.</h2> <div hidden$="[[_not(_bot_exists)]]"> <div class="horizontal wrap layout"> <div class="flex"> <table> <tbody><tr class$="[[_isDead(_bot)]]" title="Last time the bot contacted the server."> <td>Last Seen</td> <td title="[[_bot.human_last_seen_ts]]"> [[_timeDiffExact(_bot.last_seen_ts)]] ago</td> <td> <template is="dom-if" if="[[_canShutdown(_bot,_permissions)]]"> <button class="raised" on-click="_promptShutdown"> Shut Down Gracefully </button> </template> <template is="dom-if" if="[[_canDelete(_bot,_permissions)]]"> <button class="raised" on-click="_promptDelete"> Delete </button> </template> </td> </tr> <template is="dom-if" if="[[_bot.quarantined]]"> <tr class="quarantined"> <td>Quarantined</td> <td colspan="2" class="message">[[_quarantineMessage(_bot)]]</td> </tr> </template> <tr> <td>Current Task</td> <td> <a target="_blank" rel="noopener" href$="[[_taskLink(_bot.task_id)]]"> [[_task(_bot)]] </a> </td> <td> </td> </tr> <tr> <td rowspan$="[[_numRows(_bot.dimensions)]]">Dimensions</td> </tr> <template is="dom-repeat" items="[[_bot.dimensions]]" as="dim"> <tr> <td>[[dim.key]]</td> <td>[[_concat(dim.value)]]</td> </tr> </template> <tr title="IP address that the server saw the connection from."> <td>External IP</td> <td><a href$="[[_bot.external_ip]]">[[_bot.external_ip]]</a></td> <td></td> </tr> <tr class$="[[_classVersion(_server_details.bot_version,_bot.version)]]" title="Version is based on the content of swarming_bot.zip which is the swarming bot code. The bot won't update if quarantined, dead, or busy."> <td>Bot Version</td> <td>[[_shorten(_bot.version,'8')]]</td> <td></td> </tr> <tr title="The version the server expects the bot to be using."> <td>Expected Bot Version</td> <td>[[_shorten(_server_details.bot_version,'8')]]</td> <td></td> </tr> <tr title="First time ever a bot with this id contacted the server."> <td>First seen</td> <td title="[[_bot.human_first_seen_ts]]"> [[_timeDiffApprox(_bot.first_seen_ts)]] ago </td> <td></td> </tr> <tr title="How the bot is authenticated by the server."> <td>Authenticated as</td> <td colspan="2">[[_bot.authenticated_as]]</td> </tr> <template is="dom-if" if="[[_bot.lease_id]]"> <tr> <td>Machine Provider Lease ID</td> <td colspan="2"> <a href$="[[_mpLink(_bot,_server_details.machine_provider_template)]]"> [[_bot.lease_id]] </a> </td> </tr> <tr> <td>Machine Provider Lease Expires</td> <td colspan="2">[[_bot.human_lease_expiration_ts]]</td> </tr> </template> </tbody></table> <span class="title">State</span> <template is="dom-if" if="[[_not(_show_state)]]"> <button on-click="_toggleState"> <iron-icon icon="icons:add-circle-outline"></iron-icon> </button> </template> <template is="dom-if" if="[[_show_state]]"> <button on-click="_toggleState"> <iron-icon icon="icons:remove-circle-outline"></iron-icon> </button> </template> <iron-collapse id="collapse" opened="[[_show_state]]"> <div class="bot_state">[[_prettyPrint(_bot.state)]]</div> </iron-collapse> </div> <div class="stats flex"> <bot-page-summary tasks="[[_tasks]]"> </bot-page-summary> </div> </div> <div class="tabs"> <paper-tabs selected="{{_selected}}" no-bar=""> <paper-tab>Tasks</paper-tab> <paper-tab>Events</paper-tab> </paper-tabs> <template is="dom-if" if="[[_showEvents]]"> <paper-checkbox checked="{{_show_all}}"> Show all events </paper-checkbox> </template> </div> <template is="dom-if" if="[[_not(_showEvents)]]"> <table class="tasks_table"> <thead> <tr> <th>Task</th> <th>Started</th> <th>Duration</th> <th>Result</th> </tr> </thead> <tbody> <template is="dom-repeat" items="{{_tasks}}" as="task"> <tr class$="[[_taskClass(task)]]"> <td> <a target="_blank" rel="noopener" href$="[[_taskLink(task.task_id)]]"> [[task.name]] </a> </td> <td>[[task.human_started_ts]]</td> <td title="[[task.human_completed_ts]]">[[task.human_duration]]</td> <td>[[task.state]]</td> </tr> </template> </tbody> </table> </template> <template is="dom-if" if="[[_showEvents]]"> <table class="events_table"> <thead> <tr> <th>Message</th> <th>Type</th> <th>Timestamp</th> <th>Task ID</th> <th>Version</th> </tr> </thead> <tbody> <template is="dom-repeat" items="{{_eventList(_show_all,_events.*)}}" as="event"> <tr> <td class="message">[[event.message]]</td> <td>[[event.event_type]]</td> <td>[[event.human_ts]]</td> <td> <a target="_blank" rel="noopener" href$="[[_taskLink(event.task_id)]]"> [[event.task_id]] </a> </td> <td class$="[[_classVersion(_server_details.bot_version,event.version)]]"> [[_shorten(event.version,'8')]] </td> </tr> </template> </tbody> </table> </template> <pageable-data id="page_tasks" hidden$="[[_showEvents]]" busy="{{_busy2}}" label="Show more tasks" output="{{_tasks}}" parse="[[_parseTasks]]"> </pageable-data> <pageable-data id="page_events" hidden$="[[_not(_showEvents)]]" busy="{{_busy3}}" label="Show more events" output="{{_events}}" parse="[[_parseEvents]]"> </pageable-data> </div> </swarming-app> <paper-dialog id="prompt" modal="" on-iron-overlay-closed="_promptClosed"> <h2>Are you sure?</h2> <div>Are you sure you want to [[_dialogPrompt]]?</div> <div class="buttons"> <paper-button dialog-dismiss="" autofocus="">No</paper-button> <paper-button dialog-confirm="">Yes</paper-button> </div> </paper-dialog> <error-toast></error-toast> </template> <script> (function(){
|
|
|
| Polymer({
|
| is: 'bot-page',
|
| @@ -941,4 +941,4 @@ the fleet."> <template is="dom-repeat" items="[[_filters]]" as="fil"> <div class
|
| }
|
|
|
| });
|
| - })(); </script> </dom-module><dom-module id="interval-timer" assetpath="/res/imp/common/"> <script>!function(){Polymer({is:"interval-timer",properties:{period:{type:Number,value:-1,observer:"_periodChanged"}},_periodChanged:function(e){this._timeout&&window.clearTimeout(this._timeout),e>0&&(this._timeout=window.setTimeout(function(){this.fire("trigger"),this._periodChanged(e)}.bind(this),1e3*e))}})}()</script> </dom-module> <dom-module id="task-page-data" assetpath="/res/imp/taskpage/"> <script>!function(){var t,e=400,s=["abandoned_ts","completed_ts","created_ts","modified_ts","started_ts"];Polymer({is:"task-page-data",behaviors:[SwarmingBehaviors.CommonBehavior,SwarmingBehaviors.TaskBehavior],properties:{auth_headers:{type:Object},task_id:{type:String},busy:{type:Boolean,computed:"_or(_busy1,_busy2,_busy3)",notify:!0},request:{type:Object,computed:"_parseRequest(_request)",notify:!0},result:{type:Object,computed:"_parseResult(_result)",notify:!0},stdout:{type:String,computed:"_parseStdout(_stdout)",notify:!0},task_exists:{type:Boolean,value:!0,notify:!0},_busy1:{type:Boolean,value:!1},_busy2:{type:Boolean,value:!1},_busy3:{type:Boolean,value:!1},_request:{type:Object},_result:{type:Object},_stdout:{type:Object}},observers:["reload(auth_headers,task_id)"],reload:function(){if(!this.task_id||!this.auth_headers)return void console.log("task_id and auth_headers can't be empty");t&&this.cancelAsync(t);var s="/api/swarming/v1/task/"+this.task_id;t=this.async(function(){t=void 0;var e=this._getJsonAsync("_request",s+"/request","_busy1",this.auth_headers);e.then(function(){this.set("task_exists",!0)}.bind(this)).catch(function(t){404===t.status?this.set("task_exists",!1):sk.errorMessage("Http response: "+(t.status||" ")+" "+t.response)}.bind(this)),this._getJsonAsync("_result",s+"/result?include_performance_stats=true","_busy2",this.auth_headers),this.reloadStdout()},e)},_parseRequest:function(t){return t?(t.tagMap={},t.tags=t.tags||[],t.tags.forEach(function(e){var s=e.split(":",1),a=s[0],u=e.substring(a.length+1);t.tagMap[a]=u}),s.forEach(function(e){t[e]&&(t[e]=new Date(t[e]),t["human_"+e]=sk.human.localeTime(t[e]))}),t):{}},_parseResult:function(t){if(!t)return{};var e=new Date;return s.forEach(function(e){t[e]&&(t[e]=new Date(t[e]),t["human_"+e]=sk.human.localeTime(t[e]))}),!t.duration&&t.state===this.RUNNING&&t.started_ts&&(t.duration=(e-t.started_ts)/1e3),t.duration&&(t.human_duration=this._humanDuration(t.duration)),t},_parseStdout:function(t){return t&&t.output?t.output:"[No output yet]"},reloadStdout:function(){this._getJsonAsync("_stdout","/api/swarming/v1/task/"+this.task_id+"/stdout","_busy3",this.auth_headers)}})}()</script> </dom-module><dom-module id="task-disambiguation" assetpath="/res/imp/taskpage/"> <template> <style include="swarming-app-style single-page-style task-style"></style> <table> <thead> <tr> <th>Try ID</th> <th>Bot ID</th> <th>Status</th> </tr> </thead> <tbody> <template id="result_list" is="dom-repeat" items="[[_results]]" as="result" observe="task_id bot_id state"> <tr> <td> <a href$="[[_taskLink(result.task_id,'true')]]"> [[result.task_id]] </a> </td> <td> <a href$="[[_botLink(result.bot_id)]]"> [[result.bot_id]] </a> </td> <td class$="[[_stateClass(result)]]">[[state(result)]]</td> </tr> </template> </tbody> </table> </template> <script>Polymer({is:"task-disambiguation",behaviors:[SwarmingBehaviors.CommonBehavior,SwarmingBehaviors.TaskBehavior],properties:{auth_headers:{type:Object},summary_result:{type:Object},task_id:{type:String},busy:{type:Boolean,value:!1,notify:!0},_busyArr:{type:Array,value:function(){return[]}},_results:{type:Array,value:function(){return[]}}},observers:["_fetchRest(auth_headers,task_id,summary_result)","_computeBusy(_busyArr.*)"],_computeBusy:function(){for(var s=0;s<this._busyArr.length;s++)if(this._busyArr[s].status)return!0;return!1},_fetchRest:function(s,r,t){if(s&&r&&t){var e=t.try_number;this.set("_busyArr",[]),this.set("_results",[]);for(var u=r.substring(0,r.length-1),i="/api/swarming/v1/task/",a=0;a<e-1;a++){var n=u+(a+1);this.splice("_busyArr",a,0,{}),this.splice("_results",a,0,{task_id:n}),this._getJsonAsyncArr(a,"_results",i+n+"/result","_busyArr",s)}t.task_id=u+e,this.splice("_results",e-1,1,t)}}})</script> </dom-module><dom-module id="task-page" assetpath="/res/imp/taskpage/"> <template> <style include="iron-flex iron-flex-alignment swarming-app-style single-page-style task-style">.milo{width:calc(100% - 11px);height:2000px}.left{min-width:550px}.right{min-width:500px;margin-top:8px}.expand{min-width:3em;vertical-align:middle;padding:.5em}.code{font-family:monospace}.stdout{white-space:pre-line;padding:2px}.refresh_input{padding:0 5px}.reproduce{margin-left:5px}.tabbed{border:3px solid #1F78B4;margin-left:5px;min-height:80vh}</style> <url-param name="id" value="{{task_id}}"> </url-param> <url-param name="try_detail" value="{{_try_detail}}"> </url-param> <url-param name="request_detail" value="{{_request_detail}}"> </url-param> <url-param name="show_raw" value="{{_show_raw}}"> </url-param> <url-param name="refresh" value="{{_refresh_interval}}" default_value="10"> </url-param> <swarming-app client_id="[[client_id]]" auth_headers="{{_auth_headers}}" permissions="{{_permissions}}" profile="{{_profile}}" server_details="{{_server_details}}" signed_in="{{_signed_in}}" busy="[[_or(_busy1,busy2)]]" name="Swarming Task Page"> <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2> <div hidden$="[[_not(_signed_in)]]"> <task-page-data id="data" auth_headers="[[_auth_headers]]" task_id="[[task_id]]" busy="{{_busy1}}" request="{{_request}}" result="{{_result}}" stdout="{{_stdout}}" task_exists="{{_task_exists}}"> </task-page-data> <div class="horizontal layout wrap"> <div class="left flex"> <div class="horizontal layout"> <paper-input class="id_input" label="Task id" value="{{task_id}}"></paper-input> <button on-click="_refresh"> <iron-icon class="refresh" icon="icons:refresh"></iron-icon> </button> <button on-click="_promptRetry">Retry</button> <template is="dom-if" if="[[_canCancelTask(_result,_permissions)]]"> <button on-click="_promptCancel">Cancel</button> </template> </div> <h2 hidden$="[[_task_exists]]">Task not found.</h2> <template is="dom-if" if="[[_disambiguate(task_id,_result)]]"> <h2>Displaying a summary for a task with multiple tries</h2> <task-disambiguation busy="{{busy2}}" auth_headers="[[_auth_headers]]" task_id="[[task_id]]" summary_result="[[_result]]"> </task-disambiguation> </template> <table hidden$="[[_not(_task_exists)]]"> <tbody><tr> <td>Name</td> <td>[[_request.name]]</td> </tr> <tr> <td>State</td> <td class$="[[_stateClass(_result)]]">[[state(_result)]]</td> </tr> <tr> <td>Created</td> <td title$="[[_request.created_ts]]">[[_request.human_created_ts]]</td> </tr> <template is="dom-if" if="[[_wasPickedUp(_result)]]"> <tr> <td>Started</td> <td title$="[[_result.started_ts]]">[[_result.human_started_ts]]</td> </tr> </template> <template is="dom-if" if="[[_wasNotPickedUp(_result)]]"> <tr> <td>Expires</td> <td>[[_expires(_request)]]</td> </tr> </template> <template is="dom-if" if="[[_result.human_completed_ts]]"> <tr> <td>Completed</td> <td title$="[[_result.completed_ts]]">[[_result.human_completed_ts]]</td> </tr> </template> <template is="dom-if" if="[[_result.human_abandoned_ts]]"> <tr> <td>Abandoned</td> <td title$="[[_result.abandoned_ts]]">[[_result.human_abandoned_ts]]</td> </tr> </template> <tr> <td>Last Updated</td> <td title$="[[_result.modified_ts]]">[[_result.human_modified_ts]]</td> </tr> <template is="dom-if" if="[[_result.deduped_from]]"> <tr> <td><b>Deduped from</b></td> <td> <a href$="[[_taskLink(_result.deduped_from)]]"> [[_result.deduped_from]] </a> </td> </tr> </template> <tr> <td>Pending Time</td> <td>[[_pending(_result)]]</td> </tr> <tr> <td>Duration</td> <td>[[_result.human_duration]]</td> </tr> <tr> <td>Priority</td> <td>[[_request.priority]]</td> </tr> <tr> <td>User</td> <td>[[_request.user]]</td> </tr> <tr> <td>Authenticated</td> <td>[[_request.authenticated]]</td> </tr> <template is="dom-if" if="[[_request.service_account]]"> <tr> <td>Service Account</td> <td>[[_request.service_account]]</td> </tr> </template> <template is="dom-if" if="[[_request.properties.secret_bytes]]"> <tr> <td>Secret Bytes</td> <td>[[_request.properties.secret_bytes]]</td> </tr> </template> <template is="dom-if" if="[[_request.parent_task_id]]"> <tr> <td>Parent Task</td> <td> <a href$="[[_taskLink(_request.parent_task_id)]]">[[_request.parent_task_id]]</a> </td> </tr> </template> <tr> <td rowspan$="[[_rowspan(_request.properties.dimensions)]]"> <a title="The list of bots that matches the list of dimensions" href$="[[_botListLink(_request.properties.dimensions)]]"> Requested Dimensions </a> </td> </tr> <template is="dom-repeat" items="{{_request.properties.dimensions}}" as="dimension"> <tr> <td><b>[[dimension.key]]:</b> [[_alias(dimension)]]</td> </tr> </template> <tr> <td>Isolated Inputs</td> <td> <a href$="[[_isolateLink(_request.properties.inputs_ref)]]"> [[_request.properties.inputs_ref.isolated]] </a> </td> </tr> <template is="dom-if" if="[[_request.properties.outputs.length]]"> <tr> <td rowspan$="[[_rowspan(_request.properties.outputs)]]">Expected outputs</td> </tr> <template is="dom-repeat" items="{{_request.properties.outputs}}" as="output"> <tr> <td>[[output]]</td> </tr> </template> </template> <template is="dom-if" if="[[_not(_request_detail)]]"> <tr> <td>More Details</td> <td> <button on-click="_toggleDetails"> <iron-icon icon="icons:add-circle-outline"></iron-icon> </button> </td> </tr> </template> <template is="dom-if" if="[[_request_detail]]"> <tr> <td>Hide Details</td> <td> <button on-click="_toggleDetails"> <iron-icon icon="icons:remove-circle-outline"></iron-icon> </button> </td> </tr> </template> <template is="dom-if" if="[[_request_detail]]"> <tr> <td>Extra Args</td> <td class="code">[[_extraArgs(_request)]]</td> </tr> <tr> <td rowspan$="[[_rowspan(_request.tags)]]">Tags</td> </tr> <template is="dom-repeat" items="{{_request.tags}}" as="tag"> <tr> <td>[[tag]]</td> </tr> </template> <tr> <td>Execution timeout</td> <td>[[_humanDuration(_request.properties.execution_timeout_secs)]]</td> </tr> <tr> <td>I/O timeout</td> <td>[[_humanDuration(_request.properties.io_timeout_secs)]]</td> </tr> <tr> <td>Grace period</td> <td>[[_humanDuration(_request.properties.grace_period_secs)]]</td> </tr> <tr> <td>CIPD server</td> <td> <a href$="[[_request.properties.cipd_input.server]]"> [[_request.properties.cipd_input.server]] </a> </td> </tr> <tr> <td>CIPD version</td> <td>[[_request.properties.cipd_input.client_package.version]]</td> </tr> <template is="dom-if" if="[[_wasPickedUp(_result)]]"> <tr> <td>CIPD package name</td> <td>[[_result.cipd_pins.client_package.package_name]]</td> </tr> </template> <tr hidden$="[[_not(_request.properties.cipd_input)]]"> <td rowspan$="[[_cipdRowspan(_request,_result)]]">CIPD packages</td> </tr> <template is="dom-repeat" items="[[_cipdPackages(_request,_result)]]" as="cipd"> <tr> <td>[[cipd.path]]/</td> </tr> <tr> <td><b>Requested:</b>[[cipd.requested]]</td> </tr> <tr hidden$="[[_wasNotPickedUp(_result)]]"> <td><b>Actual:</b>[[cipd.actual]]</td> </tr> </template> <tr hidden$="[[_empty(_request.properties.caches)]]"> <td rowspan$="[[_rowspan(_request.properties.caches)]]">Named caches</td> </tr> <template is="dom-repeat" items="[[_request.properties.caches]]" as="cache"> <tr> <td><span>[[cache.name]]</span>:<span>[[cache.path]]</span></td> </tr> </template> </template> </tbody></table> <div class="title" hidden$="[[_not(_task_exists)]]">Task Execution</div> <template is="dom-if" if="[[_wasPickedUp(_result)]]"> <table hidden$="[[_not(_task_exists)]]"> <tbody><tr> <td>Bot assigned to task</td> <td><a href$="[[_botLink(_result.bot_id)]]">[[_result.bot_id]]</a></td> </tr> <tr> <td rowspan$="[[_rowspan(_result.bot_dimensions)]]"> <a>Bot Dimensions</a> </td> </tr> <template is="dom-repeat" items="[[_result.bot_dimensions]]" as="dimension"> <tr> <td><b>[[dimension.key]]:</b> [[_alias(dimension)]]</td> </tr> </template> <tr> <td>Exit code</td> <td>[[_result.exit_code]]</td> </tr> <tr> <td>Try number</td> <td>[[_result.try_number]]</td> </tr> <tr> <td>Failure</td> <td class$="[[_failureClass(_result.failure)]]">[[_result.failure]]</td> </tr> <tr> <td>Internal Failure</td> <td class$="[[_internalClass(_result.internal_failure)]]">[[_result.internal_failure]]</td> </tr> <tr> <td>Isolated Outputs</td> <td> <a href$="[[_isolateLink(_result.outputs_ref)]]"> [[_result.outputs_ref.isolated]] </a> </td> </tr> <tr> <td>Bot version</td> <td>[[_result.bot_version]]</td> </tr> <tr> <td>Server version</td> <td>[[_result.server_versions]]</td> </tr> </tbody></table> </template> <template is="dom-if" if="[[_wasNotPickedUp(_result)]]"> This space left blank until a bot is assigned to the task. </template> <template is="dom-if" if="[[_result.performance_stats]]"> <div class="title">Performance Stats</div> <table> <tbody><tr> <td title="This includes time taken to download inputs, isolate outputs, and setup CIPD">Total Overhead</td> <td>[[_humanDuration(_result.performance_stats.bot_overhead)]]</td> </tr> <tr> <td>Downloading Inputs From Isolate</td> <td>[[_humanDuration(_result.performance_stats.isolated_download.duration)]]</td> </tr> <tr> <td>Uploading Outputs To Isolate</td> <td>[[_humanDuration(_result.performance_stats.isolated_upload.duration)]]</td> </tr> <tr> <td>Initial bot cache</td> <td>[[_result.performance_stats.isolated_download.initial_number_items]] items; [[_bytes(_result.performance_stats.isolated_download.initial_size)]]</td> </tr> </tbody></table> </template> <div class="title">Reproducing the task locally</div> <div class="reproduce"> <div>Download inputs files into directory <i>foo</i>:</div> <div class="code"> python isolateserver.py download -I [[_request.properties.inputs_ref.isolatedserver]] --namespace [[_request.properties.inputs_ref.namespace]] -s [[_request.properties.inputs_ref.isolated]] --target foo</div> <br> <div>Run this task locally:</div> <div class="code"> python swarming.py reproduce -S [[_host_url]] [[task_id]]</div> <br> <div>Download output results into directory <i>foo</i>:</div> <div class="code"> python swarming.py collect -S [[_host_url]] --task-output-dir=foo [[task_id]]</div> <br> <div>Looking for <i>swarming.py</i>?</div> <div class="code"> git clone https://github.com/luci/client-py</div> </div> </div> <div class="flex right" hidden$="[[_not(_task_exists)]]"> <div class="horizontal layout"> <div class="tabs"> <paper-tabs selected="{{_show_raw}}" no-bar=""> <paper-tab disabled$="[[_noMilo(_request)]]">Milo Output</paper-tab> <paper-tab>Raw Output</paper-tab> </paper-tabs> </div> <paper-input class="refresh_input" label="Refresh Interval (seconds)" value="{{_refresh_interval}}" title="How often to refresh all information about the task" auto-validate="" min="1" max="1000" pattern="[0-9]+"> </paper-input> </div> <template is="dom-if" if="[[_supportsMilo(_request,_show_raw)]]"> <div class="milo tabbed" hidden$="[[_isSummaryLink(task_id)]]"> Milo results are only generated for task summaires, that is, tasks whose ids end in 0. Tasks ending in 1 or 2 represent possible retries of tasks. See <a href="//goo.gl/LE4rwV">the docs</a> for more. </div> <iframe id="miloFrame" class="milo tabbed" src$="[[_getDisplayServerLink(_server_details.display_server_url_template,task_id)]]"></iframe> </template> <template is="dom-if" if="[[_show_raw]]"> <div class="code stdout tabbed">[[_stdout]]</div> </template> </div> </div> </div> </swarming-app> <paper-dialog id="prompt" modal="" on-iron-overlay-closed="_promptClosed"> <h2>Are you sure?</h2> <div>Are you sure you want to [[_dialog_prompt]]?</div> <div class="buttons"> <paper-button dialog-dismiss="" autofocus="">No</paper-button> <paper-button dialog-confirm="">Yes</paper-button> </div> </paper-dialog> <error-toast></error-toast> <interval-timer period="[[_refresh_interval]]" on-trigger="_softRefresh"> </interval-timer> </template> <script>!function(){Polymer({is:"task-page",behaviors:[SwarmingBehaviors.CommonBehavior,SwarmingBehaviors.TaskBehavior],properties:{task_id:{type:String},client_id:{type:String},_dialog_prompt:{type:String,value:""},_host_url:{type:String,value:function(){return window.location.hostname}},_refresh_interval:{type:Number},_request:{type:Object,observer:"_requestUpdated"},_request_detail:{type:Boolean},_result:{type:Object},_server_details:{type:Object},_stdout:{type:String}},_alias:function(t){var e=t.value;return Array.isArray(e)||(e=[e]),swarming.alias.has(t.key)&&e.forEach(function(i,s){e[s]=swarming.alias.apply(i,t.key)}),e.join(" | ")},_bytes:function(t){return sk.human.bytes(t)},_canCancelTask:function(t,e){return t&&"PENDING"===t.state&&e.cancel_task},_cancelTask:function(){var t="/api/swarming/v1/task/"+this.task_id+"/cancel";swarming.postWithToast(t,"Canceling task "+this.task_id,this._auth_headers)},_cipdRowspan:function(t,e){if(!t||!t.properties||!t.properties.cipd_input)return 0;var i=(t.properties.cipd_input.packages||[]).length;return i*=e&&e.cipd_pins&&e.cipd_pins.packages?3:2,i+1},_cipdPackages:function(t,e){if(!t||!t.properties||!t.properties.cipd_input)return[];var i=t.properties.cipd_input.packages||[],s=e&&e.cipd_pins&&e.cipd_pins.packages||[];return i.forEach(function(t){t.requested=t.package_name+":"+t.version,s.forEach(function(e){e.path===t.path&&(t.actual=e.package_name+":"+e.version)})}),i},_disambiguate:function(t,e){return!(!t.endsWith("0")||!e)&&e.try_number>1},_expires:function(t){var e=parseInt(t.expiration_secs);return e?sk.human.localeTime(new Date(t.created_ts.getTime()+1e3*e)):t.expiration_secs+" seconds from created time"},_extraArgs:function(t){if(!t||!t.properties)return"";var e=t.properties.extra_args||[];return e.join(" ")},_failureClass:function(t){return t?"failed_task":""},_getDisplayServerLink:function(t,e){if(t)return t.replace("%s",e)},_internalClass:function(t){return t?"exception":""},_isolateLink:function(t){if(t&&t.isolatedserver)return t.isolatedserver+"/browse?namespace="+t.namespace+"&hash="+t.isolated},_isSummaryLink:function(t){return t&&t.endsWith(0)},_noMilo:function(t){return!this._tag(t,"allow_milo")},_pending:function(t){if(!t.created_ts)return"";var e=t.started_ts||t.abandoned_ts||new Date;return e<=t.created_ts?"0s":this._timeDiffExact(t.created_ts,e)},_promptClosed:function(t){t.detail.confirmed&&(this._dialog_prompt.startsWith("cancel")?this._cancelTask():this._retryTask())},_promptCancel:function(){this.set("_dialog_prompt","cancel task "+this.task_id),this.$.prompt.open()},_promptRetry:function(){this.set("_dialog_prompt","retry task "+this.task_id),this.$.prompt.open()},_refresh:function(){this.$.data.reload()},_requestUpdated:function(t){this._noMilo(t)&&this.set("_show_raw",1)},_softRefresh:function(){if(!this._result||"RUNNING"===this._result.state||"PENDING"===this._result.state){this.$.data.reload();var t=this.$$("iframe");t&&(t.src=this._getDisplayServerLink(this._server_details.display_server_url_template,this.task_id))}},_retryTask:function(){if(!this._request)return void sk.errorMessage("Task not yet loaded",3e3);var t={expiration_secs:this._request.expiration_secs,name:this._request.name+" (retry)",parent_task_id:this._request.parent_task_id,priority:this._request.priority,properties:this._request.properties,tags:this._request.tags,user:this._profile.email,service_account:this._request.service_account};swarming.postWithToast("/api/swarming/v1/tasks/new","Retrying task "+this.task_id,this._auth_headers,t).then(function(t){t=JSON.parse(t),t&&t.task_id&&this.set("task_id",t.task_id)}.bind(this),function(t){console.log("Task could not be retried",t)})},_rowspan:function(t){return t=t||[],t.length+1},_empty:function(t){return!t||0==t.length},_supportsMilo:function(t,e){return!e&&t&&this._tag(t,"allow_milo")},_toggleDetails:function(){this.set("_request_detail",!this._request_detail)},_tag:function(t,e){if(t&&t.tagMap)return t.tagMap[e]},_wasPickedUp:function(t){return t&&t.state!==this.PENDING&&t.state!==this.CANCELED&&t.state!=this.EXPIRED},_wasNotPickedUp:function(t){return t&&!this._wasPickedUp(t)}})}()</script> </dom-module> </div></body></html>
|
| + })(); </script> </dom-module><dom-module id="interval-timer" assetpath="/res/imp/common/"> <script>!function(){Polymer({is:"interval-timer",properties:{period:{type:Number,value:-1,observer:"_periodChanged"}},_periodChanged:function(e){this._timeout&&window.clearTimeout(this._timeout),e>0&&(this._timeout=window.setTimeout(function(){this.fire("trigger"),this._periodChanged(e)}.bind(this),1e3*e))}})}()</script> </dom-module> <dom-module id="task-page-data" assetpath="/res/imp/taskpage/"> <script>!function(){var t,e=400,s=["abandoned_ts","completed_ts","created_ts","modified_ts","started_ts"];Polymer({is:"task-page-data",behaviors:[SwarmingBehaviors.CommonBehavior,SwarmingBehaviors.TaskBehavior],properties:{auth_headers:{type:Object},task_id:{type:String},busy:{type:Boolean,computed:"_or(_busy1,_busy2,_busy3)",notify:!0},request:{type:Object,computed:"_parseRequest(_request)",notify:!0},result:{type:Object,computed:"_parseResult(_result)",notify:!0},stdout:{type:String,computed:"_parseStdout(_stdout)",notify:!0},task_exists:{type:Boolean,value:!0,notify:!0},_busy1:{type:Boolean,value:!1},_busy2:{type:Boolean,value:!1},_busy3:{type:Boolean,value:!1},_request:{type:Object},_result:{type:Object},_stdout:{type:Object}},observers:["reload(auth_headers,task_id)"],reload:function(){if(!this.task_id||!this.auth_headers)return void console.log("task_id and auth_headers can't be empty");t&&this.cancelAsync(t);var s="/api/swarming/v1/task/"+this.task_id;t=this.async(function(){t=void 0;var e=this._getJsonAsync("_request",s+"/request","_busy1",this.auth_headers);e.then(function(){this.set("task_exists",!0)}.bind(this)).catch(function(t){404===t.status?this.set("task_exists",!1):sk.errorMessage("Http response: "+(t.status||" ")+" "+t.response)}.bind(this)),this._getJsonAsync("_result",s+"/result?include_performance_stats=true","_busy2",this.auth_headers),this.reloadStdout()},e)},_parseRequest:function(t){return t?(t.tagMap={},t.tags=t.tags||[],t.tags.forEach(function(e){var s=e.split(":",1),a=s[0],u=e.substring(a.length+1);t.tagMap[a]=u}),s.forEach(function(e){t[e]&&(t[e]=new Date(t[e]),t["human_"+e]=sk.human.localeTime(t[e]))}),t):{}},_parseResult:function(t){if(!t)return{};var e=new Date;return s.forEach(function(e){t[e]&&(t[e]=new Date(t[e]),t["human_"+e]=sk.human.localeTime(t[e]))}),!t.duration&&t.state===this.RUNNING&&t.started_ts&&(t.duration=(e-t.started_ts)/1e3),t.duration&&(t.human_duration=this._humanDuration(t.duration)),t},_parseStdout:function(t){return t&&t.output?t.output:""},reloadStdout:function(){this._getJsonAsync("_stdout","/api/swarming/v1/task/"+this.task_id+"/stdout","_busy3",this.auth_headers)}})}()</script> </dom-module><dom-module id="task-disambiguation" assetpath="/res/imp/taskpage/"> <template> <style include="swarming-app-style single-page-style task-style"></style> <table> <thead> <tr> <th>Try ID</th> <th>Bot ID</th> <th>Status</th> </tr> </thead> <tbody> <template id="result_list" is="dom-repeat" items="[[_results]]" as="result" observe="task_id bot_id state"> <tr> <td> <a href$="[[_taskLink(result.task_id,'true')]]"> [[result.task_id]] </a> </td> <td> <a href$="[[_botLink(result.bot_id)]]"> [[result.bot_id]] </a> </td> <td class$="[[_stateClass(result)]]">[[state(result)]]</td> </tr> </template> </tbody> </table> </template> <script>Polymer({is:"task-disambiguation",behaviors:[SwarmingBehaviors.CommonBehavior,SwarmingBehaviors.TaskBehavior],properties:{auth_headers:{type:Object},summary_result:{type:Object},task_id:{type:String},busy:{type:Boolean,value:!1,notify:!0},_busyArr:{type:Array,value:function(){return[]}},_results:{type:Array,value:function(){return[]}}},observers:["_fetchRest(auth_headers,task_id,summary_result)","_computeBusy(_busyArr.*)"],_computeBusy:function(){for(var s=0;s<this._busyArr.length;s++)if(this._busyArr[s].status)return!0;return!1},_fetchRest:function(s,r,t){if(s&&r&&t){var e=t.try_number;this.set("_busyArr",[]),this.set("_results",[]);for(var u=r.substring(0,r.length-1),i="/api/swarming/v1/task/",a=0;a<e-1;a++){var n=u+(a+1);this.splice("_busyArr",a,0,{}),this.splice("_results",a,0,{task_id:n}),this._getJsonAsyncArr(a,"_results",i+n+"/result","_busyArr",s)}t.task_id=u+e,this.splice("_results",e-1,1,t)}}})</script> </dom-module><dom-module id="task-page" assetpath="/res/imp/taskpage/"> <template> <style include="iron-flex iron-flex-alignment swarming-app-style single-page-style task-style">.milo{width:calc(100% - 11px);height:2000px}.left{min-width:550px}.right{min-width:500px;margin-top:8px}.expand{min-width:3em;vertical-align:middle;padding:.5em}.code{font-family:monospace}.stdout{white-space:pre-line;padding:2px}.refresh_input{padding:0 5px}.reproduce{margin-left:5px}.tabbed{border:3px solid #1F78B4;margin-left:5px;min-height:80vh}</style> <url-param name="id" value="{{task_id}}"> </url-param> <url-param name="try_detail" value="{{_try_detail}}"> </url-param> <url-param name="request_detail" value="{{_request_detail}}"> </url-param> <url-param name="show_raw" value="{{_show_raw}}"> </url-param> <url-param name="refresh" value="{{_refresh_interval}}" default_value="10"> </url-param> <swarming-app client_id="[[client_id]]" auth_headers="{{_auth_headers}}" permissions="{{_permissions}}" profile="{{_profile}}" server_details="{{_server_details}}" signed_in="{{_signed_in}}" busy="[[_or(_busy1,busy2)]]" name="Swarming Task Page"> <h2 hidden$="[[_signed_in]]">You must sign in to see anything useful.</h2> <div hidden$="[[_not(_signed_in)]]"> <task-page-data id="data" auth_headers="[[_auth_headers]]" task_id="[[task_id]]" busy="{{_busy1}}" request="{{_request}}" result="{{_result}}" stdout="{{_stdout}}" task_exists="{{_task_exists}}"> </task-page-data> <div class="horizontal layout wrap"> <div class="left flex"> <div class="horizontal layout"> <paper-input class="id_input" label="Task id" value="{{task_id}}"></paper-input> <button on-click="_refresh"> <iron-icon class="refresh" icon="icons:refresh"></iron-icon> </button> <button on-click="_promptRetry">Retry</button> <template is="dom-if" if="[[_canCancelTask(_result,_permissions)]]"> <button on-click="_promptCancel">Cancel</button> </template> </div> <h2 hidden$="[[_task_exists]]">Task not found.</h2> <template is="dom-if" if="[[_disambiguate(task_id,_result)]]"> <h2>Displaying a summary for a task with multiple tries</h2> <task-disambiguation busy="{{busy2}}" auth_headers="[[_auth_headers]]" task_id="[[task_id]]" summary_result="[[_result]]"> </task-disambiguation> </template> <table hidden$="[[_not(_task_exists)]]"> <tbody><tr> <td>Name</td> <td>[[_request.name]]</td> </tr> <tr> <td>State</td> <td class$="[[_stateClass(_result)]]">[[state(_result)]]</td> </tr> <tr> <td>Created</td> <td title$="[[_request.created_ts]]">[[_request.human_created_ts]]</td> </tr> <template is="dom-if" if="[[_wasPickedUp(_result)]]"> <tr> <td>Started</td> <td title$="[[_result.started_ts]]">[[_result.human_started_ts]]</td> </tr> </template> <template is="dom-if" if="[[_wasNotPickedUp(_result)]]"> <tr> <td>Expires</td> <td>[[_expires(_request)]]</td> </tr> </template> <template is="dom-if" if="[[_result.human_completed_ts]]"> <tr> <td>Completed</td> <td title$="[[_result.completed_ts]]">[[_result.human_completed_ts]]</td> </tr> </template> <template is="dom-if" if="[[_result.human_abandoned_ts]]"> <tr> <td>Abandoned</td> <td title$="[[_result.abandoned_ts]]">[[_result.human_abandoned_ts]]</td> </tr> </template> <tr> <td>Last Updated</td> <td title$="[[_result.modified_ts]]">[[_result.human_modified_ts]]</td> </tr> <template is="dom-if" if="[[_result.deduped_from]]"> <tr> <td><b>Deduped from</b></td> <td> <a href$="[[_taskLink(_result.deduped_from)]]"> [[_result.deduped_from]] </a> </td> </tr> </template> <tr> <td>Pending Time</td> <td>[[_pending(_result)]]</td> </tr> <tr> <td>Duration</td> <td>[[_result.human_duration]]</td> </tr> <tr> <td>Priority</td> <td>[[_request.priority]]</td> </tr> <tr> <td>User</td> <td>[[_request.user]]</td> </tr> <tr> <td>Authenticated</td> <td>[[_request.authenticated]]</td> </tr> <template is="dom-if" if="[[_request.service_account]]"> <tr> <td>Service Account</td> <td>[[_request.service_account]]</td> </tr> </template> <template is="dom-if" if="[[_request.properties.secret_bytes]]"> <tr> <td>Secret Bytes</td> <td>[[_request.properties.secret_bytes]]</td> </tr> </template> <template is="dom-if" if="[[_request.parent_task_id]]"> <tr> <td>Parent Task</td> <td> <a href$="[[_taskLink(_request.parent_task_id)]]">[[_request.parent_task_id]]</a> </td> </tr> </template> <tr> <td rowspan$="[[_rowspan(_request.properties.dimensions)]]"> <a title="The list of bots that matches the list of dimensions" href$="[[_botListLink(_request.properties.dimensions)]]"> Requested Dimensions </a> </td> </tr> <template is="dom-repeat" items="{{_request.properties.dimensions}}" as="dimension"> <tr> <td><b>[[dimension.key]]:</b> [[_alias(dimension)]]</td> </tr> </template> <tr> <td>Isolated Inputs</td> <td> <a href$="[[_isolateLink(_request.properties.inputs_ref)]]"> [[_request.properties.inputs_ref.isolated]] </a> </td> </tr> <template is="dom-if" if="[[_request.properties.outputs.length]]"> <tr> <td rowspan$="[[_rowspan(_request.properties.outputs)]]">Expected outputs</td> </tr> <template is="dom-repeat" items="{{_request.properties.outputs}}" as="output"> <tr> <td>[[output]]</td> </tr> </template> </template> <template is="dom-if" if="[[_not(_request_detail)]]"> <tr> <td>More Details</td> <td> <button on-click="_toggleDetails"> <iron-icon icon="icons:add-circle-outline"></iron-icon> </button> </td> </tr> </template> <template is="dom-if" if="[[_request_detail]]"> <tr> <td>Hide Details</td> <td> <button on-click="_toggleDetails"> <iron-icon icon="icons:remove-circle-outline"></iron-icon> </button> </td> </tr> </template> <template is="dom-if" if="[[_request_detail]]"> <tr> <td>Extra Args</td> <td class="code">[[_extraArgs(_request)]]</td> </tr> <tr> <td rowspan$="[[_rowspan(_request.tags)]]">Tags</td> </tr> <template is="dom-repeat" items="{{_request.tags}}" as="tag"> <tr> <td>[[tag]]</td> </tr> </template> <tr> <td>Execution timeout</td> <td>[[_humanDuration(_request.properties.execution_timeout_secs)]]</td> </tr> <tr> <td>I/O timeout</td> <td>[[_humanDuration(_request.properties.io_timeout_secs)]]</td> </tr> <tr> <td>Grace period</td> <td>[[_humanDuration(_request.properties.grace_period_secs)]]</td> </tr> <tr> <td>CIPD server</td> <td> <a href$="[[_request.properties.cipd_input.server]]"> [[_request.properties.cipd_input.server]] </a> </td> </tr> <tr> <td>CIPD version</td> <td>[[_request.properties.cipd_input.client_package.version]]</td> </tr> <template is="dom-if" if="[[_wasPickedUp(_result)]]"> <tr> <td>CIPD package name</td> <td>[[_result.cipd_pins.client_package.package_name]]</td> </tr> </template> <tr hidden$="[[_not(_request.properties.cipd_input)]]"> <td rowspan$="[[_cipdRowspan(_request,_result)]]">CIPD packages</td> </tr> <template is="dom-repeat" items="[[_cipdPackages(_request,_result)]]" as="cipd"> <tr> <td>[[cipd.path]]/</td> </tr> <tr> <td><b>Requested:</b>[[cipd.requested]]</td> </tr> <tr hidden$="[[_wasNotPickedUp(_result)]]"> <td><b>Actual:</b>[[cipd.actual]]</td> </tr> </template> <tr hidden$="[[_empty(_request.properties.caches)]]"> <td rowspan$="[[_rowspan(_request.properties.caches)]]">Named caches</td> </tr> <template is="dom-repeat" items="[[_request.properties.caches]]" as="cache"> <tr> <td><span>[[cache.name]]</span>:<span>[[cache.path]]</span></td> </tr> </template> </template> </tbody></table> <div class="title" hidden$="[[_not(_task_exists)]]">Task Execution</div> <template is="dom-if" if="[[_wasPickedUp(_result)]]"> <table hidden$="[[_not(_task_exists)]]"> <tbody><tr> <td>Bot assigned to task</td> <td><a href$="[[_botLink(_result.bot_id)]]">[[_result.bot_id]]</a></td> </tr> <tr> <td rowspan$="[[_rowspan(_result.bot_dimensions)]]"> <a>Bot Dimensions</a> </td> </tr> <template is="dom-repeat" items="[[_result.bot_dimensions]]" as="dimension"> <tr> <td><b>[[dimension.key]]:</b> [[_alias(dimension)]]</td> </tr> </template> <tr> <td>Exit code</td> <td>[[_result.exit_code]]</td> </tr> <tr> <td>Try number</td> <td>[[_result.try_number]]</td> </tr> <tr> <td>Failure</td> <td class$="[[_failureClass(_result.failure)]]">[[_result.failure]]</td> </tr> <tr> <td>Internal Failure</td> <td class$="[[_internalClass(_result.internal_failure)]]">[[_result.internal_failure]]</td> </tr> <tr> <td>Isolated Outputs</td> <td> <a href$="[[_isolateLink(_result.outputs_ref)]]"> [[_result.outputs_ref.isolated]] </a> </td> </tr> <tr> <td>Bot version</td> <td>[[_result.bot_version]]</td> </tr> <tr> <td>Server version</td> <td>[[_result.server_versions]]</td> </tr> </tbody></table> </template> <template is="dom-if" if="[[_wasNotPickedUp(_result)]]"> This space left blank until a bot is assigned to the task. </template> <template is="dom-if" if="[[_result.performance_stats]]"> <div class="title">Performance Stats</div> <table> <tbody><tr> <td title="This includes time taken to download inputs, isolate outputs, and setup CIPD">Total Overhead</td> <td>[[_humanDuration(_result.performance_stats.bot_overhead)]]</td> </tr> <tr> <td>Downloading Inputs From Isolate</td> <td>[[_humanDuration(_result.performance_stats.isolated_download.duration)]]</td> </tr> <tr> <td>Uploading Outputs To Isolate</td> <td>[[_humanDuration(_result.performance_stats.isolated_upload.duration)]]</td> </tr> <tr> <td>Initial bot cache</td> <td>[[_result.performance_stats.isolated_download.initial_number_items]] items; [[_bytes(_result.performance_stats.isolated_download.initial_size)]]</td> </tr> </tbody></table> </template> <div class="title">Reproducing the task locally</div> <div class="reproduce"> <div>Download inputs files into directory <i>foo</i>:</div> <div class="code"> python isolateserver.py download -I [[_request.properties.inputs_ref.isolatedserver]] --namespace [[_request.properties.inputs_ref.namespace]] -s [[_request.properties.inputs_ref.isolated]] --target foo</div> <br> <div>Run this task locally:</div> <div class="code"> python swarming.py reproduce -S [[_host_url]] [[task_id]]</div> <br> <div>Download output results into directory <i>foo</i>:</div> <div class="code"> python swarming.py collect -S [[_host_url]] --task-output-dir=foo [[task_id]]</div> <br> <div>Looking for <i>swarming.py</i>?</div> <div class="code"> git clone https://github.com/luci/client-py</div> </div> </div> <div class="flex right" hidden$="[[_not(_task_exists)]]"> <div class="horizontal layout"> <div class="tabs"> <paper-tabs selected="{{_show_raw}}" no-bar=""> <paper-tab disabled$="[[_noMilo(_request)]]">Milo Output</paper-tab> <paper-tab>Raw Output</paper-tab> </paper-tabs> </div> <paper-input class="refresh_input" label="Refresh Interval (seconds)" value="{{_refresh_interval}}" title="How often to refresh all information about the task" auto-validate="" min="1" max="1000" pattern="[0-9]+"> </paper-input> </div> <template is="dom-if" if="[[_supportsMilo(_request,_show_raw)]]"> <div class="milo tabbed" hidden$="[[_isSummaryLink(task_id)]]"> Milo results are only generated for task summaires, that is, tasks whose ids end in 0. Tasks ending in 1 or 2 represent possible retries of tasks. See <a href="//goo.gl/LE4rwV">the docs</a> for more. </div> <iframe id="miloFrame" class="milo tabbed" src$="[[_getDisplayServerLink(_server_details.display_server_url_template,task_id)]]"></iframe> </template> <template is="dom-if" if="[[_show_raw]]"> <div class="code stdout tabbed">[[_rawOutput(_stdout,_result)]]</div> </template> </div> </div> </div> </swarming-app> <paper-dialog id="prompt" modal="" on-iron-overlay-closed="_promptClosed"> <h2>Are you sure?</h2> <div>Are you sure you want to [[_dialog_prompt]]?</div> <div class="buttons"> <paper-button dialog-dismiss="" autofocus="">No</paper-button> <paper-button dialog-confirm="">Yes</paper-button> </div> </paper-dialog> <error-toast></error-toast> <interval-timer period="[[_refresh_interval]]" on-trigger="_softRefresh"> </interval-timer> </template> <script>!function(){Polymer({is:"task-page",behaviors:[SwarmingBehaviors.CommonBehavior,SwarmingBehaviors.TaskBehavior],properties:{task_id:{type:String},client_id:{type:String},_dialog_prompt:{type:String,value:""},_host_url:{type:String,value:function(){return window.location.hostname}},_refresh_interval:{type:Number},_request:{type:Object,observer:"_requestUpdated"},_request_detail:{type:Boolean},_result:{type:Object},_server_details:{type:Object},_stdout:{type:String}},_alias:function(t){var e=t.value;return Array.isArray(e)||(e=[e]),swarming.alias.has(t.key)&&e.forEach(function(i,s){e[s]=swarming.alias.apply(i,t.key)}),e.join(" | ")},_bytes:function(t){return sk.human.bytes(t)},_canCancelTask:function(t,e){return t&&"PENDING"===t.state&&e.cancel_task},_cancelTask:function(){var t="/api/swarming/v1/task/"+this.task_id+"/cancel";swarming.postWithToast(t,"Canceling task "+this.task_id,this._auth_headers)},_cipdRowspan:function(t,e){if(!t||!t.properties||!t.properties.cipd_input)return 0;var i=(t.properties.cipd_input.packages||[]).length;return i*=e&&e.cipd_pins&&e.cipd_pins.packages?3:2,i+1},_cipdPackages:function(t,e){if(!t||!t.properties||!t.properties.cipd_input)return[];var i=t.properties.cipd_input.packages||[],s=e&&e.cipd_pins&&e.cipd_pins.packages||[];return i.forEach(function(t){t.requested=t.package_name+":"+t.version,s.forEach(function(e){e.path===t.path&&(t.actual=e.package_name+":"+e.version)})}),i},_disambiguate:function(t,e){return!(!t.endsWith("0")||!e)&&e.try_number>1},_empty:function(t){return!t||0==t.length},_expires:function(t){var e=parseInt(t.expiration_secs);return e?sk.human.localeTime(new Date(t.created_ts.getTime()+1e3*e)):t.expiration_secs+" seconds from created time"},_extraArgs:function(t){if(!t||!t.properties)return"";var e=t.properties.extra_args||[];return e.join(" ")},_failureClass:function(t){return t?"failed_task":""},_getDisplayServerLink:function(t,e){if(t)return t.replace("%s",e)},_internalClass:function(t){return t?"exception":""},_isolateLink:function(t){if(t&&t.isolatedserver)return t.isolatedserver+"/browse?namespace="+t.namespace+"&hash="+t.isolated},_isSummaryLink:function(t){return t&&t.endsWith(0)},_noMilo:function(t){return!this._tag(t,"allow_milo")},_pending:function(t){if(!t.created_ts)return"";var e=t.started_ts||t.abandoned_ts||new Date;return e<=t.created_ts?"0s":this._timeDiffExact(t.created_ts,e)},_promptClosed:function(t){t.detail.confirmed&&(this._dialog_prompt.startsWith("cancel")?this._cancelTask():this._retryTask())},_promptCancel:function(){this.set("_dialog_prompt","cancel task "+this.task_id),this.$.prompt.open()},_promptRetry:function(){this.set("_dialog_prompt","retry task "+this.task_id),this.$.prompt.open()},_rawOutput:function(t,e){return t?t:"PENDING"===e.state||"RUNNING"===e.state?"[No output yet]":"[No output received]"},_refresh:function(){this.$.data.reload()},_requestUpdated:function(t){this._noMilo(t)&&this.set("_show_raw",1)},_softRefresh:function(){if(!this._result||"RUNNING"===this._result.state||"PENDING"===this._result.state){this.$.data.reload();var t=this.$$("iframe");t&&(t.src=this._getDisplayServerLink(this._server_details.display_server_url_template,this.task_id))}},_retryTask:function(){if(!this._request)return void sk.errorMessage("Task not yet loaded",3e3);var t={expiration_secs:this._request.expiration_secs,name:this._request.name+" (retry)",parent_task_id:this._request.parent_task_id,priority:this._request.priority,properties:this._request.properties,tags:this._request.tags,user:this._profile.email,service_account:this._request.service_account};swarming.postWithToast("/api/swarming/v1/tasks/new","Retrying task "+this.task_id,this._auth_headers,t).then(function(t){t=JSON.parse(t),t&&t.task_id&&this.set("task_id",t.task_id)}.bind(this),function(t){console.log("Task could not be retried",t)})},_rowspan:function(t){return t=t||[],t.length+1},_supportsMilo:function(t,e){return!e&&t&&this._tag(t,"allow_milo")},_toggleDetails:function(){this.set("_request_detail",!this._request_detail)},_tag:function(t,e){if(t&&t.tagMap)return t.tagMap[e]},_wasPickedUp:function(t){return t&&t.state!==this.PENDING&&t.state!==this.CANCELED&&t.state!=this.EXPIRED},_wasNotPickedUp:function(t){return t&&!this._wasPickedUp(t)}})}()</script> </dom-module> </div></body></html>
|
|
|