| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| /** |
| * Render the application/entity summary tab. |
| * @type {*} |
| */ |
| define([ |
| "underscore", "jquery", "backbone", "brooklyn", "brooklyn-utils", "view/viewutils", |
| "text!tpl/apps/summary.html", "view/entity-config", |
| ], function (_, $, Backbone, Brooklyn, Util, ViewUtils, |
| SummaryHtml, EntityConfigView) { |
| |
| var EntitySummaryView = Backbone.View.extend({ |
| events:{ |
| 'click a.open-tab':'tabSelected' |
| }, |
| template:_.template(SummaryHtml), |
| initialize: function() { |
| _.bindAll(this); |
| var that = this; |
| this.$el.html(this.template({ |
| entity:this.model, |
| application:this.options.application, |
| isApp: this.isApp() |
| })); |
| if (this.model.get('catalogItemId')) |
| this.$("div.catalogItemId").show(); |
| else |
| this.$("div.catalogItemId").hide(); |
| |
| this.options.tabView.configView = new EntityConfigView({ |
| model:this.options.model, |
| tabView:this.options.tabView, |
| }); |
| this.$("div#advanced-config").html(this.options.tabView.configView.render().el); |
| |
| ViewUtils.attachToggler(this.$el); |
| |
| // TODO we should have a backbone object exported from the sensors view which we can listen to here |
| // (currently we just take the URL from that view) - and do the same for active tasks; |
| ViewUtils.getRepeatedlyWithDelay(this, this.model.getSensorUpdateUrl(), |
| function(data) { that.updateWithData(data); }); |
| // however if we only use external objects we must either subscribe to their errors also |
| // or do our own polling against the server, so we know when to disable ourselves |
| // ViewUtils.fetchRepeatedlyWithDelay(this, this.model, { period: 10*1000 }) |
| |
| this.loadSpec(); |
| }, |
| isApp: function() { |
| var id = this.model.get('id'); |
| var selfLink = this.model.get('links').self; |
| return selfLink.indexOf("/applications/" + id) != -1; |
| }, |
| render:function () { |
| return this; |
| }, |
| revealIfHasValue: function(sensor, $div, renderer, values) { |
| var that = this; |
| if (!renderer) renderer = function(data) { return _.escape(data); } |
| |
| if (values) { |
| var data = values[sensor] |
| if (data || data===false) { |
| $(".value", $div).html(renderer(data)) |
| $div.show() |
| } else { |
| $div.hide(); |
| } |
| } else { |
| // direct ajax call not used anymore - but left just in case |
| $.ajax({ |
| url: that.model.getLinkByName("sensors")+"/"+sensor, |
| contentType:"application/json", |
| success:function (data) { |
| if (data || data===false) { |
| $(".value", $div).html(renderer(data)) |
| $div.show() |
| } else { |
| $div.hide(); |
| } |
| that.updateStatusIcon(); |
| }}) |
| } |
| }, |
| updateWithData: function (data) { |
| this.revealIfHasValue("service.state", this.$(".status"), null, data) |
| this.revealIfHasValue("service.isUp", this.$(".serviceUp"), null, data) |
| |
| var renderAsLink = function(data) { return "<a href='"+_.escape(data)+"'>"+_.escape(data)+"</a>" }; |
| this.revealIfHasValue("main.uri", this.$(".url"), renderAsLink, data) |
| |
| var status = this.updateStatusIcon(); |
| |
| this.updateCachedProblemIndicator(data); |
| |
| if (status.problem) { |
| this.updateAddlInfoForProblem(); |
| } else { |
| this.$(".additional-info-on-problem").html("").hide() |
| } |
| }, |
| updateStatusIcon: function() { |
| var statusIconInfo = ViewUtils.computeStatusIconInfo(this.$(".serviceUp .value").html(), this.$(".status .value").html()); |
| if (statusIconInfo.url) { |
| this.$('#status-icon').html('<img src="'+encodeURI(statusIconInfo.url)+'" '+ |
| 'style="max-width: 64px; max-height: 64px;"/>'); |
| } else { |
| this.$('#status-icon').html(''); |
| } |
| return statusIconInfo; |
| }, |
| updateCachedProblemIndicator: function(data) { |
| if (!data) return; |
| this.problemIndicators = data['service.problems']; |
| if (!this.problemIndicators || !_.size(this.problemIndicators)) |
| this.problemIndicators = data['service.notUp.indicators']; |
| if (!this.problemIndicators || !_.size(this.problemIndicators)) |
| this.problemIndicators = null; |
| }, |
| updateAddlInfoForProblem: function(tasksReloaded) { |
| if (!this.options.tasks) |
| // if tasks not supplied, then don't attempt to show status info! |
| return; |
| |
| var problemDetails = ""; |
| var lastFailedTask = null, that = this; |
| // ideally get the time the status changed, and return the last failure on or around that time |
| // (or take it from some causal log) |
| // but for now, we just return the most recent failed task |
| this.options.tasks.each(function(it) { |
| if (it.isError() && it.isLocalTopLevel()) { |
| if (!lastFailedTask || it.attributes.endTimeUtc < lastFailedTask.attributes.endTimeUtc) |
| lastFailedTask = it; |
| } |
| } ); |
| |
| if (this.problemIndicators) { |
| var indicatorText = _.values(this.problemIndicators); |
| for (var error in indicatorText) { |
| if (problemDetails) { |
| problemDetails = problemDetails + "<br style='line-height: 24px;'>"; |
| } |
| problemDetails = problemDetails + _.escape(indicatorText[error]); |
| } |
| } |
| if (lastFailedTask) { |
| var path = "activities/subtask/"+encodeURIComponent(lastFailedTask.id); |
| var base = this.model.getLinkByName("self"); |
| if (problemDetails) |
| problemDetails = problemDetails + "<br style='line-height: 24px;'>"; |
| problemDetails = problemDetails + "<b>"+_.escape("Failure running task ") |
| +"<a class='open-tab' tab-target='"+path+"'" + |
| "href='#"+encodeURI(base)+"/"+path+"'>" + |
| "<i>"+_.escape(lastFailedTask.attributes.displayName)+"</i> " |
| +"("+_.escape(lastFailedTask.id)+")</a>: </b>"+ |
| _.escape(lastFailedTask.attributes.result); |
| } |
| if (!that.problemTasksLoaded && this.options.tasks) { |
| // trigger callback to get tasks |
| if (!problemDetails) |
| problemDetails = "<i>Loading problem details...</i>"; |
| |
| ViewUtils.get(this, this.options.tasks.url, function() { |
| that.problemTasksLoaded = true; |
| that.updateAddlInfoForProblem(); |
| }); |
| } |
| |
| if (problemDetails) { |
| this.$(".additional-info-on-problem").html(problemDetails).show(); |
| } else { |
| var base = this.model.getLinkByName("self"); |
| this.$(".additional-info-on-problem").html( |
| "The entity appears to have failed externally. " + |
| "<br style='line-height: 24px;'>" + |
| "No Brooklyn-managed task failures reported. " + |
| "For more information, investigate " + |
| "<a class='open-tab' tab-target='sensors' href='#"+encodeURI(base)+"/sensors'>sensors</a> and " + |
| "streams on recent " + |
| "<a class='open-tab' tab-target='activities' href='#"+encodeURI(base)+"/activities'>activity</a>, " + |
| "as well as external systems and logs where necessary.").show(); |
| } |
| }, |
| tabSelected: function(event) { |
| if (event.metaKey || event.shiftKey) |
| // trying to open in a new tab, do not act on it here! |
| return; |
| var tab = $(event.currentTarget).attr('tab-target'); |
| this.options.tabView.openTab(tab); |
| // and prevent the a from firing |
| event.preventDefault(); |
| return false; |
| }, |
| loadSpec: function(flushCache) { |
| if (!flushCache && this.spec) { |
| this.renderSpec(this.spec); |
| return; |
| } |
| ViewUtils.get(this, this.model.get('links').spec, this.renderSpec); |
| }, |
| renderSpec: function(data) { |
| if (!data) data=this.spec; |
| if (!data) { |
| this.$('#entity-spec-yaml-toggler').hide(); |
| } else { |
| ViewUtils.updateTextareaWithData($("#entity-spec-yaml", this.$el), data, true, false, 150, 400); |
| this.$('#entity-spec-yaml-toggler').show(); |
| } |
| } |
| }); |
| |
| return EntitySummaryView; |
| }); |