http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/views/TopologyDetailView.jsx ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/scripts/views/TopologyDetailView.jsx b/contrib/views/storm/src/main/resources/scripts/views/TopologyDetailView.jsx new file mode 100644 index 0000000..136d95f --- /dev/null +++ b/contrib/views/storm/src/main/resources/scripts/views/TopologyDetailView.jsx @@ -0,0 +1,1039 @@ +/** + 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. +*/ + +define([ + 'jsx!components/Table', + 'jsx!modules/Table/Pagination', + 'utils/Utils', + 'react', + 'react-dom', + 'collections/BaseCollection', + 'models/VTopology', + 'models/BaseModel', + 'jsx!containers/TopologyConfiguration', + 'jsx!containers/TopologyDetailGraph', + 'jsx!components/Breadcrumbs', + 'jsx!components/SearchLogs', + 'jsx!components/BarChart', + 'jsx!views/RebalanceView', + 'bootbox', + 'x-editable', + 'bootstrap', + 'bootstrap-switch' + ],function(Table, Pagination, Utils, React, ReactDOM, BaseCollection, VTopology, BaseModel, TopologyConfiguration, TopologyDetailGraph, Breadcrumbs, SearchLogs, BarChart, RebalanceView, bootbox, XEditable){ + 'use strict'; + + return React.createClass({ + displayName: 'TopologyDetailView', + propTypes: { + id: React.PropTypes.string.isRequired + }, + getInitialState: function(){ + this.model = new VTopology({'id': this.props.id}); + this.spoutCollection = new BaseCollection(); + this.boltCollection = new BaseCollection(); + this.lagCollection = new BaseCollection(); + this.systemFlag = false; + this.windowSize = ':all-time'; + return { + model: this.model, + graphData: {}, + logLevels: {}, + rebalanceModalOpen: false, + lagData: [], + hideKafkaLagBox: false, + workerHostPort: '' + }; + }, + componentWillMount: function(){ + $('.loader').show(); + this.initializeData(); + }, + componentDidMount: function(){ + $(".boot-switch.systemSum").bootstrapSwitch({ + size: 'small', + onSwitchChange: function(event, state){ + this.systemFlag = state; + this.initializeData(); + }.bind(this) + }); + $("#slideContent").hide(); + $(".boot-switch.debug").bootstrapSwitch({ + size: 'small', + onSwitchChange: function(event, state){ + this.debugAction(state); + }.bind(this) + }); + $("#lag-graph").hide(); + $("#kafkaSpout").bootstrapSwitch({ + onSwitchChange: function() { + $('#lag-graph, #lag-table').slideToggle(); + } + }); + $('[data-rel="tooltip"]').tooltip(); + $('.loader').hide(); + }, + componentWillUpdate: function(){ + $('#collapse-spout').off('hidden.bs.collapse'); + $('#collapse-spout').off('shown.bs.collapse'); + $('#collapse-bolt').off('hidden.bs.collapse'); + $('#collapse-bolt').off('shown.bs.collapse'); + $('#modal-rebalance').off('hidden.bs.modal'); + this.spoutCollection.getFirstPage().fullCollection.reset([]); + this.spouts = this.renderSpouts(); + this.boltCollection.getFirstPage().fullCollection.reset([]); + this.bolts = this.renderBolts(); + }, + componentDidUpdate: function(){ + $('#collapse-spout').on('hidden.bs.collapse', function () { + $("#spout-box").toggleClass("fa-compress fa-expand"); + }).on('shown.bs.collapse', function() { + $("#spout-box").toggleClass("fa-compress fa-expand"); + }); + + $('#collapse-bolt').on('hidden.bs.collapse', function () { + $("#bolt-box").toggleClass("fa-compress fa-expand"); + }).on('shown.bs.collapse', function() { + $("#bolt-box").toggleClass("fa-compress fa-expand"); + }); + $('#modal-rebalance').on('hidden.bs.modal', function (e) { + this.initializeData(); + this.setState({"rebalanceModalOpen":false}); + }.bind(this)); + if(this.state.rebalanceModalOpen){ + $('#modal-rebalance').modal("show"); + } + if(this.refs.barChart){ + ReactDOM.findDOMNode(document.getElementById('lag-graph')).appendChild(this.refs.barChart.legendsEl) + } + }, + initializeData: function(){ + this.model.getData({ + id: this.model.get('id'), + window: this.windowSize, + sys: this.systemFlag, + success: function(model, response){ + if(response.error || model.error){ + Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); + } else { + this.model.set(model); + this.setState({"model": this.model}); + } + }.bind(this), + error: function(model, response, options){ + Utils.notifyError("Error occured in fetching topology details."); + } + }); + this.initializeGraphData(); + this.initializeLogConfig(); + this.initializeLagData(); + this.initializeWorkerData(); + }, + initializeGraphData: function(){ + $('#graphLoader').show(); + this.model.getGraphData({ + id: this.model.get('id'), + window: this.windowSize, + success: function(model, response){ + if(response.error || model.error){ + Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); + } else { + if(_.isString(model)){ + model = JSON.parse(model); + } + this.setState({graphData: model}); + } + $('#graphLoader').hide(); + }.bind(this), + error: function(model, response, options){ + Utils.notifyError("Error occured in fetching topology visualization data."); + } + }); + }, + + initializeLogConfig: function() { + this.collection = new BaseCollection(); + this.model.getLogConfig({ + id: this.model.get('id'), + success: function(model, response){ + if(response.error || model.error){ + Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); + } else { + this.resetLogCollection(model); + } + }.bind(this), + error: function(model, response, options){ + Utils.notifyError("Error occured in fetching log configuration data."); + } + }); + }, + + initializeLagData: function(){ + $('#kafkaLoader').show(); + this.model.getTopologyLag({ + id: this.model.get('id'), + success: function(model, response){ + if(response.error || model.error){ + Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); + } else { + if(model && _.keys(model).length > 0){ + var keys = _.keys(model); + var arr = []; + for(var i = 0; i < keys.length; i++){ + var data = model[keys[i]]; + var topicKeys = _.keys(data.spoutLagResult); + for(var j = 0; j < topicKeys.length; j++){ + var topicName = topicKeys[j]; + var partitionData = data.spoutLagResult[topicName]; + var id = _.keys(partitionData); + for(var k = 0; k < id.length; k++){ + var partitionId = id[k]; + var obj = partitionData[partitionId]; + obj['spoutId'] = data.spoutId; + obj['spoutType'] = data.spoutType; + obj['partition'] = partitionId; + obj['topic'] = topicName; + arr.push(obj); + } + } + } + this.resetLagCollection(arr); + } else { + this.setState({hideKafkaLagBox : true}); + } + } + $('#kafkaLoader').hide(); + }.bind(this) + }) + }, + initializeWorkerData: function(){ + this.model.getWorkerHost({ + id: this.model.get('id'), + success: function(model, response){ + if(response.error || model.error){ + Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); + } else { + var workerHostPortArr = model.hostPortList; + var result = ''; + for(var i = 0; i < workerHostPortArr.length; i++){ + result += workerHostPortArr[i].host+':'+workerHostPortArr[i].port + if(i !== workerHostPortArr.length - 1){ + result += ', \n'; + } + } + this.setState({'workerHostPort': result}) + } + }.bind(this) + }) + }, + resetLagCollection: function(model){ + this.lagCollection.reset(model); + this.setState({"lagData": model}); + }, + getLagColums: function(){ + var self = this; + return [ + {name: 'spoutId', title: 'Id', tooltip:'Id'}, + {name: 'topic', title: 'Topic', tooltip:'Topic'}, + {name: 'partition', title: 'Partition', tooltip:'Partition'}, + {name: 'logHeadOffset', title: 'Latest Offset', tooltip:'Latest Offset'}, + {name: 'consumerCommittedOffset', title: 'Spout Committed Offset', tooltip:'Spout Committed Offset'}, + {name: 'lag', title: 'Lag', tooltip:'Lag'}, + ]; + }, + resetLogCollection: function(model) { + this.collection.reset(); + this.setState({logLevels: model.namedLoggerLevels}); + var keys = _.keys(this.state.logLevels); + keys.map(function(key, index) { + var obj = this.state.logLevels[key]; + var model = new BaseModel({ + logger: key, + target_level: obj.target_level, + timeout: obj.timeout, + timeout_epoch: obj.timeout_epoch + }); + this.collection.add(model); + }.bind(this)); + + this.collection.add(new BaseModel({ + logger: 'com.your.organization.LoggerName', + target_level: 'ALL', + timeout: 30, + timeout_epoch: 0, + isAdd: true + })); + }, + + renderAccordion: function(type, header, searchField, searchCb, collection, emptyText, columns, toggleCb){ + return ( + <div className="box"> + <div className="box-header" data-toggle="collapse" data-target={"#collapse-"+type} aria-expanded="false" aria-controls={"collapse-"+type}> + <h4>{header}</h4> + <h4 className="box-control"> + <a href="javascript:void(0);" className="primary"> + <i className="fa fa-compress" id={type+"-box"} onClick={toggleCb}></i> + </a> + </h4> + </div> + <div className="box-body collapse in" id={"collapse-"+type}> + <div className="input-group col-sm-4"> + <input type="text" onKeyUp={searchCb} className="form-control" placeholder={"Search by "+searchField} /> + <span className="input-group-btn"> + <button className="btn btn-primary" type="button"><i className="fa fa-search"></i></button> + </span> + </div> + <Table className="table table-striped" collection={collection} emptyText={emptyText} columns={columns()} /> + <Pagination collection={collection} /> + </div> + </div> + ); + }, + renderSpouts: function(){ + if(this.state.model.has('spouts')){ + Utils.ArrayToCollection(this.state.model.get('spouts'), this.spoutCollection); + this.spoutCollection.searchFields = ['spoutId']; + var searchCb = function(e){ + var value = e.currentTarget.value; + this.spoutCollection.search(value); + }.bind(this); + var toggleCb = function(e){ + $("#collapse-spout").collapse('toggle'); + } + return this.renderAccordion('spout', 'Spouts', 'id', searchCb, this.spoutCollection, 'No spouts found !', this.getSpoutColumns, toggleCb); + } else { + return null; + } + }, + getSpoutColumns: function(){ + var self = this; + return [ + {name: 'spoutId', title: 'Id', tooltip:'The ID assigned to a the Component by the Topology. Click on the name to view the Component\'s page.', component: React.createClass({ + propTypes: { + model: React.PropTypes.object.isRequired + }, + render: function(){ + var topologyId = self.state.model.has('id') ? self.state.model.get('id') : ""; + return ( <a href={"#!/topology/"+topologyId+"/component/"+this.props.model.get('spoutId')}>{this.props.model.get('spoutId')}</a>); + } + })}, + {name: 'executors', title: 'Executors', tooltip:'Executors are threads in a Worker process.'}, + {name: 'tasks', title: 'Tasks', tooltip:'A Task is an instance of a Bolt or Spout. The number of Tasks is almost always equal to the number of Executors.'}, + {name: 'emitted', title: 'Emitted', tooltip:'The number of Tuples emitted.'}, + {name: 'transferred', title: 'Transferred', tooltip:'The number of Tuples emitted that sent to one or more bolts.'}, + {name: 'completeLatency', title: 'Complete Latency (ms)', tooltip:'The average time a Tuple "tree" takes to be completely processed by the Topology. A value of 0 is expected if no acking is done.'}, + {name: 'acked', title: 'Acked', tooltip:'The number of Tuple "trees" successfully processed. A value of 0 is expected if no acking is done.'}, + {name: 'failed', title: 'Failed', tooltip:'The number of Tuple "trees" that were explicitly failed or timed out before acking was completed. A value of 0 is expected if no acking is done.'}, + {name: 'errorHost', title: 'Error Host:Port', component: React.createClass({ + propTypes: { + model: React.PropTypes.object.isRequired + }, + render: function(){ + return (<span>{this.props.model.has('errorHost') && this.props.model.get('errorHost') !== '' ? this.props.model.get('errorHost')+':'+this.props.model.get('errorPort') : null}</span>); + } + })}, + {name: 'lastError', title: 'Last Error'}, + {name: 'errorTime', title: 'Error Time', component: React.createClass({ + propTypes: { + model: React.PropTypes.object.isRequired + }, + render: function(){ + if(this.props.model.get('errorTime') && this.props.model.get('errorTime') != 0) { + var d = new Date(this.props.model.get('errorTime') * 1000), + date = d.toLocaleDateString() + ' ' + d.toLocaleTimeString(); + return (<span>{date}</span>); + } else return (<span></span>); + } + })} + ]; + }, + renderBolts: function(){ + if(this.state.model.has('bolts')){ + Utils.ArrayToCollection(this.state.model.get('bolts'), this.boltCollection); + this.boltCollection.searchFields = ['boltId']; + var searchCb = function(e){ + var value = e.currentTarget.value; + this.boltCollection.search(value); + }.bind(this); + var toggleCb = function(e){ + $("#collapse-bolt").collapse('toggle'); + } + return this.renderAccordion('bolt', 'Bolts', 'id', searchCb, this.boltCollection, 'No bolts found !', this.getBoltColumns, toggleCb); + } else { + return null; + } + }, + getBoltColumns: function(){ + var self = this; + return [ + {name: 'boltId', title: 'Id', tooltip:'The ID assigned to a the Component by the Topology. Click on the name to view the Component\'s page.', component: React.createClass({ + propTypes: { + model: React.PropTypes.object.isRequired + }, + render: function(){ + var topologyId = self.state.model.has('id') ? self.state.model.get('id') : ""; + return ( <a href={"#!/topology/"+topologyId+"/component/"+this.props.model.get('boltId')}>{this.props.model.get('boltId')}</a>); + } + })}, + {name: 'executors', title: 'Executors', tooltip:'Executors are threads in a Worker process.'}, + {name: 'tasks', title: 'Tasks', tooltip:'A Task is an instance of a Bolt or Spout. The number of Tasks is almost always equal to the number of Executors.'}, + {name: 'emitted', title: 'Emitted', tooltip:'The number of Tuples emitted.'}, + {name: 'transferred', title: 'Transferred', tooltip:'The number of Tuples emitted that sent to one or more bolts.'}, + {name: 'capacity', title: 'Capacity (last 10m)', tooltip:"If this is around 1.0, the corresponding Bolt is running as fast as it can, so you may want to increase the Bolt's parallelism. This is (number executed * average execute latency) / measurement time."}, + {name: 'executeLatency', title: 'Execute Latency (ms)', tooltip:'The average time a Tuple spends in the execute method. The execute method may complete without sending an Ack for the tuple.'}, + {name: 'executed', title: 'Executed', tooltip:'The number of incoming Tuples processed.'}, + {name: 'processLatency', title: 'Process Latency (ms)', tooltip:'The average time it takes to Ack a Tuple after it is first received. Bolts that join, aggregate or batch may not Ack a tuple until a number of other Tuples have been received.'}, + {name: 'acked', title: 'Acked', tooltip:'The number of Tuples acknowledged by this Bolt.'}, + {name: 'failed', title: 'Failed', tooltip:'The number of tuples Failed by this Bolt.'}, + {name: 'errorHost', title: 'Error Host:Port', component: React.createClass({ + propTypes: { + model: React.PropTypes.object.isRequired + }, + render: function(){ + return (<span>{this.props.model.has('errorHost') && this.props.model.get('errorHost') !== '' ? this.props.model.get('errorHost')+':'+this.props.model.get('errorPort') : null}</span>); + } + })}, + {name: 'lastError', title: 'Last Error'}, + {name: 'errorTime', title: 'Error Time', component: React.createClass({ + propTypes: { + model: React.PropTypes.object.isRequired + }, + render: function(){ + if(this.props.model.get('errorTime') && this.props.model.get('errorTime') != 0) { + var d = new Date(this.props.model.get('errorTime') * 1000), + date = d.toLocaleDateString() + ' ' + d.toLocaleTimeString(); + return (<span>{date}</span>); + } else return (<span></span>); + } + })} + ]; + }, + renderWindowOptions: function(){ + if(this.state.model.has('topologyStats')){ + return this.state.model.get('topologyStats').map(function(object, i){ + return ( <option key={i} value={object.window}>{object.windowPretty}</option> ); + }); + } else { + return null; + } + }, + handleWindowChange: function(e){ + this.windowSize = e.currentTarget.value; + this.initializeData(); + }, + getLinks: function() { + var links = [ + {link: '#!/dashboard', title: 'Dashboard'}, + {link: '#!/topology', title: 'Topology Listing'}, + {link: 'javascript:void(0);', title: this.state.model.has('name') ? this.state.model.get('name') : ""} + ]; + return links; + }, + + addLogLevel: function(e) { + var self = this; + var id = e.currentTarget.getAttribute('data-name'); + var namedLoggerLevels = {}; + var targetLevel = $(e.currentTarget).parent().siblings().find('.target-level').val(), + timeout = $(e.currentTarget).parent().siblings().find('.timeout').html(), + logger = $(e.currentTarget).parent().siblings().find('.logger').html(); + + namedLoggerLevels[logger] = { + target_level: targetLevel, + reset_level: 'INFO', + timeout: parseInt(timeout, 10) + }; + + var dataObj = { + namedLoggerLevels: namedLoggerLevels + } + + this.model.saveLogConfig({ + id: this.model.get('id'), + data: JSON.stringify(dataObj), + contentType: "application/json", + success: function(model, response, options){ + if(response.error || model.error){ + Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); + } else { + this.resetLogCollection(model); + Utils.notifySuccess("Log configuration added successfully."); + } + }.bind(this), + error: function(model, response, options){ + Utils.notifyError("Error occured in saving log configuration data."); + } + + }); + }, + applyLogLevel: function(e) { + var self = this; + var id = e.currentTarget.getAttribute('data-name'); + var namedLoggerLevels = {}; + var targetLevel = $(e.currentTarget).parents('td').siblings().find('.target-level').val(), + timeout = $(e.currentTarget).parents('td').siblings().find('.timeout').html(), + logger = $(e.currentTarget).parents('td').siblings().find('.logger').html(); + + namedLoggerLevels[logger] = { + target_level: targetLevel, + reset_level: 'INFO', + timeout: parseInt(timeout, 10) + }; + + var dataObj = { + namedLoggerLevels: namedLoggerLevels + } + + this.model.saveLogConfig({ + id: this.model.get('id'), + data: JSON.stringify(dataObj), + contentType: "application/json", + success: function(model, response, options){ + if(response.error || model.error){ + Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); + } else { + this.resetLogCollection(model); + Utils.notifySuccess("Log configuration applied successfully."); + } + }.bind(this), + error: function(model, response, options){ + Utils.notifyError("Error occured in applying log configuration data."); + } + }); + }, + clearLogLevel: function(e) { + var self = this; + var id = e.currentTarget.getAttribute('data-name'); + var namedLoggerLevels = {}; + var logger = $(e.currentTarget).parents('td').siblings().find('.logger').html(); + + namedLoggerLevels[logger] = { + target_level: null, + reset_level: 'INFO', + timeout: 0 + }; + + var dataObj = { + namedLoggerLevels: namedLoggerLevels + } + + this.model.saveLogConfig({ + id: this.model.get('id'), + data: JSON.stringify(dataObj), + contentType: "application/json", + success: function(model, response, options){ + if(response.error || model.error){ + Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); + } else { + this.resetLogCollection(model); + Utils.notifySuccess("Log configuration cleared successfully."); + } + }.bind(this), + error: function(model, response, options){ + Utils.notifyError("Error occured in clearing log configuration data."); + } + }); + }, + getColumns: function(){ + var self = this; + return [ + {name: 'logger', title: 'Logger', component: React.createClass({ + propTypes: { + model: React.PropTypes.object.isRequired + }, + render: function(){ + if(this.props.model.get('isAdd')) + return (<a href="javascript:void(0)" className="x-editable logger">{this.props.model.get('logger')}</a>); + else return (<a href="javascript:void(0)" className="logger">{this.props.model.get('logger')}</a>); + }, + componentDidMount: function() { + $(".x-editable").editable({ + mode: 'inline' + }); + }}) + }, + {name: 'target_level', title: 'Level', component: React.createClass({ + propTypes: { + model: React.PropTypes.object.isRequired + }, + render: function() { + return ( + <select className="form-control target-level" defaultValue={this.props.model.get('target_level')}> + <option value="ALL">ALL</option> + <option value="TRACE">TRACE</option> + <option value="DEBUG">DEBUG</option> + <option value="INFO">INFO</option> + <option value="WARN">WARN</option> + <option value="ERROR">ERROR</option> + <option value="FATAL">FATAL</option> + <option value="OFF">OFF</option> + </select> + ); + } + })}, + {name: 'timeout', title: 'Timeout', component: React.createClass({ + propTypes: { + model: React.PropTypes.object.isRequired + }, + render: function(){ + return (<a href="javascript:void(0)" className="x-editable timeout">{this.props.model.get('timeout')}</a>); + }, + componentDidMount: function() { + $(".x-editable").editable({ + mode: 'inline' + }); + }}) + }, + {name: 'timeout_epoch', title: 'Expires At', component: React.createClass({ + propTypes: { + model: React.PropTypes.object.isRequired + }, + render: function(){ + if(this.props.model.get('timeout_epoch') != 0) { + var d = new Date(this.props.model.get('timeout_epoch')), + date = d.toLocaleDateString() + ' ' + d.toLocaleTimeString(); + return (<span>{date}</span>); + } else return (<span></span>); + + } + }) + }, + {name: 'action', title: 'Action', component: React.createClass({ + propTypes: { + model: React.PropTypes.object.isRequired + }, + render: function(){ + if(this.props.model.get('isAdd')) + return( + <a href="javascript:void(0)" data-name={this.props.model.get('logger')} className="btn btn-primary btn-xs" onClick={self.addLogLevel}><i className="fa fa-plus"></i></a> + ) + else + return ( + <span> + <a href="javascript:void(0)" data-name={this.props.model.get('logger')} className="btn btn-success btn-xs" onClick={self.applyLogLevel}><i className="fa fa-check"></i></a> + <a href="javascript:void(0)" data-name={this.props.model.get('logger')} className="btn btn-danger btn-xs" onClick={self.clearLogLevel}><i className="fa fa-times"></i></a> + </span> + ); + } + })} + ]; + }, + toggleSlide: function() { + $("#slideContent").slideToggle(); + }, + + renderStatsRow: function(){ + var statsArr = this.state.model.get('topologyStats'); + if(statsArr){ + return statsArr.map(function(stats, i){ + return ( + <tr key={i}> + <td>{stats.windowPretty}</td> + <td>{stats.emitted}</td> + <td>{stats.transferred}</td> + <td>{stats.completeLatency}</td> + <td>{stats.acked}</td> + <td>{stats.failed}</td> + </tr> + ); + }); + } + }, + render: function() { + var status = this.state.model.has('status') ? this.state.model.get('status') : null; + var workersTotal = this.state.model.has('workersTotal') ? this.state.model.get('workersTotal').toString() : '0'; + if(this.state.model.get('debug')){ + $(".boot-switch.debug").bootstrapSwitch('state', true, true); + } else { + $(".boot-switch.debug").bootstrapSwitch('state', false, true); + } + return ( + <div> + <Breadcrumbs links={this.getLinks()} /> + <SearchLogs id={this.model.get('id')}/> + <div className="row"> + <div className="col-sm-12"> + <div className="box filter"> + <div className="box-body form-horizontal"> + <div className="form-group no-margin"> + <label className="col-sm-1 control-label">Window</label> + <div className="col-sm-2"> + <select className="form-control" onChange={this.handleWindowChange} value={this.windowSize}> + {this.renderWindowOptions()} + </select> + </div> + <label className="col-sm-2 control-label">System Summary</label> + <div className="col-sm-2"> + <input className="boot-switch systemSum" type="checkbox" /> + </div> + <label className="col-sm-1 control-label">Debug</label> + <div className="col-sm-1"> + <input className="boot-switch debug" type="checkbox"/> + </div> + <div className="col-sm-3 text-right"> + <div className="btn-group" role="group"> + <button type="button" className="btn btn-primary" onClick={this.handleTopologyActivation} title="Activate" data-rel="tooltip" disabled={status === 'ACTIVE' ? "disabled" : null}> + <i className="fa fa-play"></i> + </button> + <button type="button" className="btn btn-primary" onClick={this.handleTopologyDeactivation} title="Deactivate" data-rel="tooltip" disabled={status === 'INACTIVE' ? "disabled" : null}> + <i className="fa fa-stop"></i> + </button> + <button type="button" className="btn btn-primary" onClick={this.handleTopologyRebalancing} title="Rebalance" data-rel="tooltip" disabled={status === 'REBALANCING' ? "disabled" : null}> + <i className="fa fa-balance-scale"></i> + </button> + <button type="button" className="btn btn-primary" onClick={this.handleTopologyKilling} title="Kill" data-rel="tooltip" disabled={status === 'KILLED' ? "disabled" : null}> + <i className="fa fa-ban"></i> + </button> + <button type="button" className="btn btn-primary" onClick={this.toggleSlide} title="Change Log Level" data-rel="tooltip"> + <i className="fa fa-file-o"></i> + </button> + </div> + </div> + </div> + <div className="row" id="slideContent"> + <div className="col-sm-12"> + <hr/> + <h4 className="col-sm-offset-5">Change Log Level</h4> + <p>Modify the logger levels for topology. Note that applying a setting restarts the timer in the workers. To configure the root logger, use the name ROOT.</p> + <Table className="table no-margin" collection={this.collection} columns={this.getColumns()}/> + </div> + </div> + </div> + </div> + </div> + </div> + <div className="row"> + <div className="col-sm-5"> + <div className="summary-tile"> + <div className="summary-title">Topology Summary</div> + <div className="summary-body form-horizontal"> + <div className="form-group"> + <label className="col-sm-4 control-label">ID:</label> + <div className="col-sm-8"> + <p className="form-control-static">{this.state.model.get('id')}</p> + </div> + </div> + <div className="form-group"> + <label className="col-sm-4 control-label">Owner:</label> + <div className="col-sm-8"> + <p className="form-control-static">{this.state.model.get('owner')}</p> + </div> + </div> + <div className="form-group"> + <label className="col-sm-4 control-label">Status:</label> + <div className="col-sm-8"> + <p className="form-control-static">{this.state.model.get('status')}</p> + </div> + </div> + <div className="form-group"> + <label className="col-sm-4 control-label">Uptime:</label> + <div className="col-sm-8"> + <p className="form-control-static">{this.state.model.get('uptime')}</p> + </div> + </div> + <div className="form-group"> + <label className="col-sm-4 control-label">Workers:</label> + <div className="col-sm-8"> + <p className="form-control-static">{this.state.model.get('workersTotal')}</p> + </div> + </div> + <div className="form-group"> + <label className="col-sm-4 control-label">Executors:</label> + <div className="col-sm-8"> + <p className="form-control-static">{this.state.model.get('executorsTotal')}</p> + </div> + </div> + <div className="form-group"> + <label className="col-sm-4 control-label">Tasks:</label> + <div className="col-sm-8"> + <p className="form-control-static">{this.state.model.get('tasksTotal')}</p> + </div> + </div> + <div className="form-group"> + <label className="col-sm-4 control-label">Memory:</label> + <div className="col-sm-8"> + <p className="form-control-static">{this.state.model.get('assignedTotalMem')}</p> + </div> + </div> + <div className="form-group"> + <label className="col-sm-4 control-label">Worker-Host:Port:</label> + <div className="col-sm-8"> + <p className="form-control-static preformatted">{this.state.workerHostPort}</p> + </div> + </div> + + </div> + </div> + </div> + <div className="col-sm-7"> + <div className="stats-tile"> + <div className="stats-title">Topology Stats</div> + <div className="stats-body"> + <table className="table table-enlarge"> + <thead> + <tr> + <th><span data-rel="tooltip" title="The past period of time for which the statistics apply.">Window</span></th> + <th><span data-rel="tooltip" title="The number of Tuples emitted.">Emitted</span></th> + <th><span data-rel="tooltip" title="The number of Tuples emitted that sent to one or more bolts.">Transferred</span></th> + <th><span data-rel="tooltip" title='The average time a Tuple "tree" takes to be completely processed by the Topology. A value of 0 is expected if no acking is done.'>Complete Latency (ms)</span></th> + <th><span data-rel="tooltip" title='The number of Tuple "trees" successfully processed. A value of 0 is expected if no acking is done.'>Acked</span></th> + <th><span data-rel="tooltip" title='The number of Tuple "trees" that were explicitly failed or timed out before acking was completed. A value of 0 is expected if no acking is done.'>Failed</span></th> + </tr> + </thead> + <tbody> + {this.renderStatsRow()} + </tbody> + </table> + </div> + </div> + </div> + </div> + <div className="row"> + <div className="col-sm-12"> + <div className="inner-loader" id="graphLoader" /> + <TopologyDetailGraph model={this.state.model} graphData={this.state.graphData}/> + </div> + </div> + {this.state.hideKafkaLagBox ? null : + <div className="row"> + <div className="col-sm-12"> + <div className="box"> + <div className="box-header"> + <h4>Kafka Spout Lag</h4> + <div className="box-control"> + <input + id="kafkaSpout" + type="checkbox" + data-size="mini" + data-off-color="success" + data-off-text="Table" + data-on-color="info" + data-on-text="Graph" /> + </div> + </div> + <div className="box-body"> + <div className="row"> + <div className="col-sm-12"> + <div className="inner-loader" id="kafkaLoader" /> + <div id="lag-graph"> + {this.lagCollection.length > 0 ? + <BarChart + ref="barChart" + width={window != window.parent ? 1100 : 1300} + height={400} + xAttr="spoutId-partition" + yAttr="count" + data={this.lagCollection.toJSON().map(function(d){ + return { + 'Latest Offset': d.logHeadOffset, + 'Spout Committed Offset': d.consumerCommittedOffset, + 'spoutId-partition': d.spoutId+'-'+d.partition + }; + })} + /> + : null} + </div> + <div id="lag-table"> + <Table + className="table table-striped table-bordered" + collection={this.lagCollection} + emptyText="No Data Found." + columns={this.getLagColums()} + /> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + } + <div className="row"> + <div className="col-sm-12"> + {this.spouts} + </div> + </div> + <div className="row"> + <div className="col-sm-12"> + {this.bolts} + </div> + </div> + <div className="row"> + <div className="col-sm-12"> + <TopologyConfiguration configArr={this.state.model.get('configuration') ? this.state.model.get('configuration') : {}}/> + </div> + </div> + {this.state.rebalanceModalOpen ? <RebalanceView modalId="modal-rebalance" topologyId={this.state.model.get('id')} topologyExecutors={workersTotal} spouts={this.state.model.get('spouts') ? this.state.model.get('spouts') : []} bolts={this.state.model.get('bolts') ? this.state.model.get('bolts') : []}/> : null} + </div> + ); + }, + handleTopologyActivation: function(e){ + if(this.model.get('status') !== 'ACTIVE'){ + var msg = "Do you really want to activate this topology ?"; + var successCb = function(){ + this.model.activateTopology({ + id: this.model.get('id'), + success: function(model, response){ + if(response.error || model.error){ + Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); + } else { + this.initializeData(); + Utils.notifySuccess("Topology activated successfully.") + } + }.bind(this), + error: function(model, response, options){ + Utils.notifyError("Error occured in activating topology."); + } + }); + }.bind(this); + Utils.ConfirmDialog(msg, '', successCb); + } + }, + handleTopologyDeactivation: function(e){ + if(this.model.get('status') !== 'INACTIVE'){ + var msg = "Do you really want to deactivate this topology ?"; + var successCb = function(){ + this.model.deactivateTopology({ + id: this.model.get('id'), + success: function(model, response){ + if(response.error || model.error){ + Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); + } else { + this.initializeData(); + Utils.notifySuccess("Topology deactivated successfully.") + } + }.bind(this), + error: function(model, response, options){ + Utils.notifyError("Error occured in deactivating topology."); + } + }); + }.bind(this); + Utils.ConfirmDialog(msg, '', successCb); + } + }, + handleTopologyRebalancing: function(e){ + if(this.model.get('status') !== 'REBALANCING'){ + this.setState({"rebalanceModalOpen":true}); + } + }, + handleTopologyKilling: function(e){ + if(this.model.get('status') !== 'KILLED'){ + bootbox.prompt({ + title: 'Are you sure you want to kill this topology ? If yes, please, specify wait time in seconds.', + value: "30", + buttons: { + confirm: { + label: 'Yes', + className: "btn-success", + }, + cancel: { + label: 'No', + className: "btn-default", + } + }, + callback: function(result) { + if(result != null){ + this.model.killTopology({ + id: this.model.get('id'), + waitTime: result, + success: function(model, response){ + if(response.error || model.error){ + Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); + } else { + this.initializeData(); + Utils.notifySuccess("Topology killed successfully.") + } + }.bind(this), + error: function(model, response, options){ + Utils.notifyError("Error occured in killing topology."); + } + }); + } + }.bind(this) + }); + } + }, + debugAction: function(toEnableFlag){ + if(toEnableFlag){ + bootbox.prompt({ + title: 'Do you really want to debug this topology ? If yes, please, specify sampling percentage.', + value: this.model.get("samplingPct") ? this.model.get("samplingPct") : '10', + buttons: { + confirm: { + label: 'Yes', + className: "btn-success", + }, + cancel: { + label: 'No', + className: "btn-default", + } + }, + callback: function(result) { + if(result == null) { + $(".boot-switch.debug").bootstrapSwitch('toggleState', true); + } else if(result == "" || isNaN(result) || result < 0) { + Utils.notifyError("Enter valid sampling percentage"); + $(".boot-switch.debug").bootstrapSwitch('toggleState', true); + } else { + this.model.debugTopology({ + id: this.model.get('id'), + debugType: 'enable', + percent: result, + success: function(model, response){ + if(response.error || model.error){ + Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); + } else { + this.initializeData(); + Utils.notifySuccess("Debugging enabled successfully.") + } + }.bind(this), + error: function(model, response, options){ + Utils.notifyError("Error occured in enabling debugging."); + } + }); + } + }.bind(this) + }); + } else { + var title = "Do you really want to stop debugging this topology ?"; + var successCb = function(){ + this.model.debugTopology({ + id: this.model.get('id'), + debugType: 'disable', + percent: '0', + success: function(model, response){ + if(response.error || model.error){ + Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); + } else { + this.initializeData(); + Utils.notifySuccess("Debugging disabled successfully.") + } + }.bind(this), + error: function(model, response, options){ + Utils.notifyError("Error occured in disabling debugging."); + } + }); + }.bind(this); + var cancelCb = function(){ + $(".boot-switch.debug").bootstrapSwitch('toggleState', true) + }.bind(this); + Utils.ConfirmDialog(' ', title, successCb, cancelCb); + } + }, + }); +});
http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/scripts/views/TopologyListingView.jsx ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/scripts/views/TopologyListingView.jsx b/contrib/views/storm/src/main/resources/scripts/views/TopologyListingView.jsx new file mode 100644 index 0000000..25441fa --- /dev/null +++ b/contrib/views/storm/src/main/resources/scripts/views/TopologyListingView.jsx @@ -0,0 +1,65 @@ +/** + 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. +*/ + +define([ + 'jsx!components/Table', + 'react', + 'react-dom', + 'jsx!containers/TopologyListing', + 'jsx!components/Breadcrumbs' + ],function(Table, React, ReactDOM, TopologyListing, Breadcrumbs){ + 'use strict'; + + return React.createClass({ + displayName: 'TopologyListingView', + getInitialState: function(){ + return null; + }, + componentWillMount: function(){ + $('.loader').show(); + }, + componentDidMount: function(){ + $('.loader').hide(); + }, + componentWillUpdate: function(){ + $('.loader').show(); + }, + componentDidUpdate: function(){ + $('.loader').hide(); + }, + render: function() { + return ( + <div> + <Breadcrumbs links={this.getLinks()} /> + <div className="row"> + <div className="col-sm-12"> + <TopologyListing /> + </div> + </div> + </div> + ); + }, + getLinks: function() { + var links = [ + {link: '#!/dashboard', title: 'Dashboard'}, + {link: 'javascript:void(0);', title: 'Topology Listing'} + ]; + return links; + } + }); +}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/styles/style.css ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/styles/style.css b/contrib/views/storm/src/main/resources/styles/style.css index 71c26c4..f6b1685 100644 --- a/contrib/views/storm/src/main/resources/styles/style.css +++ b/contrib/views/storm/src/main/resources/styles/style.css @@ -14,7 +14,7 @@ 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. -**/ +*/ /* Theme: Apache Storm Author: Sanket @@ -37,12 +37,12 @@ body { .row-margin-bottom { margin-bottom: 20px; } - -.table-borderless>tbody>tr>td, -.table-borderless>tbody>tr>th, -.table-borderless>tfoot>tr>td, -.table-borderless>tfoot>tr>th, -.table-borderless>thead>tr>td, + +.table-borderless>tbody>tr>td, +.table-borderless>tbody>tr>th, +.table-borderless>tfoot>tr>td, +.table-borderless>tfoot>tr>th, +.table-borderless>thead>tr>td, .table-borderless>thead>tr>th { border-top: none; } @@ -140,8 +140,7 @@ body { } /* Boxes */ -.box, -.panel.panel-default { +.box { position: relative; margin-bottom: 15px; border: 1px #bcbcbc solid; @@ -162,8 +161,8 @@ body { border-bottom: 1px #bcbcbc solid; border-radius: 4px 4px 0px 0px; } -.box .box-header h4 { - float: left; +.box .box-header h4 { + float: left; margin: 0px; font-size: 16px; font-weight: 700; @@ -171,12 +170,12 @@ body { letter-spacing: 1px; } .box .box-header .box-control { - float: right; + float: right; } .box .box-header .box-control .bootstrap-switch { margin: 9px 2px; } -.box .box-header .box-control a,.box-control a { +.box .box-header .box-control a { display: inline-block; width: 20px; height: 20px; @@ -188,21 +187,18 @@ body { background-color: #4b4b4b; color: rgba(255,255,255,0.75); } -.box-control a { - margin: 0 2px; -} /*.box .box-header .box-control a i { visibility: hidden; } .box .box-header .box-control:hover a i { visibility: visible; }*/ -.box .box-header .box-control a.primary, .box-control a.primary {background-color: #1b75bb;} -.box .box-header .box-control a.success, .box-control a.success {background-color: #1bbb60;} -.box .box-header .box-control a.info, .box-control a.info {background-color: #27a9e1;} -.box .box-header .box-control a.warning, .box-control a.warning {background-color: #fbaf3f;} -.box .box-header .box-control a.danger, .box-control a.danger {background-color: #ff5816;} -.box .box-header .box-control a.secondary, .box-control a.secondary {background-color: #df206a;} +.box .box-header .box-control a.primary {background-color: #1b75bb;} +.box .box-header .box-control a.success {background-color: #1bbb60;} +.box .box-header .box-control a.info {background-color: #27a9e1;} +.box .box-header .box-control a.warning {background-color: #fbaf3f;} +.box .box-header .box-control a.danger {background-color: #ff5816;} +.box .box-header .box-control a.secondary {background-color: #df206a;} .box .box-body { padding: 10px; @@ -289,7 +285,7 @@ body { .tile.warning > .tile-header { background-color: #ED940E; } -.tile.success, .label-success { +.tile.success { background: #1bbb60; } .tile.success > .tile-header { @@ -476,12 +472,12 @@ text.id { font-weight: bold; } .d3-tip ul { - padding:0; - margin:0; + padding:0; + margin:0; list-style: none; -}ââ¬Â¨ +}⨠.d3-tip ul li { - font-size: 12px; + font-size: 12px; line-height: 20px; } marker { @@ -538,7 +534,7 @@ ul.legends li.legend{ } #modal-rebalance .modal-body{ max-height: 450px; - overflow-y: auto; + overflow-y: auto; } .loader { position: fixed; @@ -546,7 +542,7 @@ ul.legends li.legend{ bottom: 0; left: 0; right: 0; - background: url('../img/loader.gif') rgba(255,255,255,0.75) no-repeat center center; + background: url('../images/loader.gif') rgba(255,255,255,0.75) no-repeat center center; z-index: 9; } .inner-loader{ @@ -555,7 +551,7 @@ ul.legends li.legend{ left: 0px; bottom: 0px; right: 0px; - background: url('../img/loader.gif') rgba(255,255,255,0.75) no-repeat center center; + background: url('../images/loader.gif') rgba(255,255,255,0.75) no-repeat center center; } .searchbar{ margin-top: 15px; @@ -577,260 +573,7 @@ ul.legends li.legend{ vertical-align: top; margin-right: 5px; } -.searchbar .open > .dropdown-toggle.btn-default:hover, +.searchbar .open > .dropdown-toggle.btn-default:hover, .searchbar .open > .dropdown-toggle.btn-default:focus{ border: 1px solid transparent; -} -.searchbar .form-group, .searchbar .checkbox { - margin-top: 0; - margin-bottom: 0; -} -.searchbar .checkbox label { - font-weight: 600; - margin-bottom: 5px; -} -.topology-table{ - margin-bottom: 0 !important; -} -.topology-table a:hover,.panel-heading:hover{ - cursor: pointer; -} -.pagination-wrapper{ - width: 100%; - height: 35px; - display: block; - margin-top: 10px; -} -.pagination-wrapper > div > p { - line-height: 35px; -} -.pagination-wrapper > ul { - margin : 0 !important; -} -.panel-title > a { - display: block; - text-decoration: none; -} -.reactable-pagination{ - display: none; -} -.pagination > li > a { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; -} - -.panel-default > .panel-heading{ - background-color: #f3f6f9; - /*padding: 0 10px;*/ - border-bottom: 1px #bcbcbc solid; - border-radius: 4px 4px 0px 0px; -} - -.panel-default > .panel-heading h4 { - margin: 0px; - font-size: 16px; - font-weight: 700; - letter-spacing: 1px; -} -/*------------------------------------------------*/ -/* Switch SECTION START*/ -/*------------------------------------------------*/ -.switchWrapper{ - width: 90px; - height: 30px; - line-height: 30px; - overflow: hidden; - position: relative; - border-radius: 4px; - border: 1px solid #ccc; -} -.switchSlider{ - width: 135px; - height: auto; - position: absolute; - top:0; - left:-46px; - cursor: pointer; -} -.switchItemOn{ - width: 45px; - height: 30px; - color: #ffffff; - background-color: #337ab7; - float: left; -} -.switchItemMid{ - width: 44px; - height: 30px; - background-color: #ffffff; - float: left; -} -.switchItemOff{ - width: 45px; - height: 30px; - color: #000000; - background-color: #eeeeee; - float: left; - clear: right; -} - -.switchSlider.onSlider{ - left : 0 !important; - transition: all 0.5s ease; -} -.switchSlider.offSlider{ - left : -46px !important; - transition: all 0.5s ease; -} -.sliderText{ - text-align: center; - font-size: 12px; - line-height: 29px; -} -.switchItemOn.graphSwitchOn{ - background-color: #5cb85c; -} - -.switchItemOff.graphSwitchOff{ - background-color: #5bc0de; - color: #fff; -} -/*------------------------------------------------*/ -/* Switch SECTION END*/ -/*------------------------------------------------*/ -.toast-message { - display: none; -} -.switchWrapper.lagSwitchSetting{ - margin-top: 0; - width: 90px; - height: 20px; -} -.lagSwitchSetting .sliderText{ - line-height: 1.5; -} -.box .box-header .box-control span,.box-control span { - display: inline-block; - width: 20px; - height: 20px; - font-size: 12px; - line-height: 20px; - text-align: center; - margin: 10px 2px; - border-radius: 50%; - background-color: #4b4b4b; - color: rgba(255,255,255,0.75); -} -.box-control span { - margin: 0 2px; -} -/*.box .box-header .box-control a i { - visibility: hidden; -} -.box .box-header .box-control:hover a i { - visibility: visible; -}*/ -.box .box-header .box-control span.primary, .box-control span.primary {background-color: #1b75bb;} -/* input range css start here*/ -input[type=range] { - -webkit-appearance: none; - width: 100%; - margin: 6.8px 0; -} -input[type=range]:focus { - outline: none; -} -input[type=range]::-webkit-slider-runnable-track { - width: 100%; - height: 6.4px; - cursor: pointer; - box-shadow: 1px 1px 1px rgba(1, 1, 1, 0), 0px 0px 1px rgba(14, 14, 14, 0); - background: -moz-linear-gradient(0deg, #cccccc 0%, #ffffff 27%, #ffffff 100%); /* ff3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(73%, #ffffff), color-stop(100%, #cccccc)); /* safari4+,chrome */ - background: -webkit-linear-gradient(0deg, #cccccc 0%, #ffffff 27%, #ffffff 100%); /* safari5.1+,chrome10+ */ - background: -o-linear-gradient(0deg, #cccccc 0%, #ffffff 27%, #ffffff 100%); /* opera 11.10+ */ - background: -ms-linear-gradient(0deg, #cccccc 0%, #ffffff 27%, #ffffff 100%); /* ie10+ */ - background: linear-gradient(0deg, #cccccc 0%, #ffffff 27%, #ffffff 100%); /* w3c */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#cccccc',GradientType=0 ); /* ie6-9 */ - border-radius: 1px; - border: 0px solid #010101; -} -input[type=range]::-webkit-slider-thumb { - box-shadow: 1px 1px 1px rgba(0, 0, 0, 0), 0px 0px 1px rgba(13, 13, 13, 0); - border: 1px solid #186ef7; - height: 20px; - width: 20px; - border-radius: 10px; - background: #186ef7; - cursor: pointer; - -webkit-appearance: none; - margin-top: -6.8px; -} -input[type=range]:focus::-webkit-slider-runnable-track { - background: -moz-linear-gradient(0deg, #cccccc 10%, #ffffff 27%, #ffffff 100%); /* ff3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(10%, #ffffff), color-stop(73%, #ffffff), color-stop(100%, #cccccc)); /* safari4+,chrome */ - background: -webkit-linear-gradient(0deg, #cccccc 10%, #ffffff 27%, #ffffff 100%); /* safari5.1+,chrome10+ */ - background: -o-linear-gradient(0deg, #cccccc 10%, #ffffff 27%, #ffffff 100%); /* opera 11.10+ */ - background: -ms-linear-gradient(0deg, #cccccc 10%, #ffffff 27%, #ffffff 100%); /* ie10+ */ - background: linear-gradient(0deg, #cccccc 10%, #ffffff 27%, #ffffff 100%); /* w3c */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#cccccc',GradientType=0 ); /* ie6-9 */ -} -input[type=range]::-moz-range-track { - width: 100%; - height: 6.4px; - cursor: pointer; - box-shadow: 1px 1px 1px rgba(1, 1, 1, 0), 0px 0px 1px rgba(14, 14, 14, 0); - background: #c6c6c6; - border-radius: 1px; - border: 0px solid #010101; -} -input[type=range]::-moz-range-thumb { - box-shadow: 1px 1px 1px rgba(0, 0, 0, 0), 0px 0px 1px rgba(13, 13, 13, 0); - border: 1px solid #186ef7; - height: 20px; - width: 20px; - border-radius: 10px; - background: #186ef7; - cursor: pointer; -} -input[type=range]::-ms-track { - width: 100%; - height: 6.4px; - cursor: pointer; - background: transparent; - border-color: transparent; - color: transparent; -} -input[type=range]::-ms-fill-lower { - background: #c6c6c6; - border: 0px solid #010101; - border-radius: 2px; - box-shadow: 1px 1px 1px rgba(1, 1, 1, 0), 0px 0px 1px rgba(14, 14, 14, 0); -} -input[type=range]::-ms-fill-upper { - background: #c6c6c6; - border: 0px solid #010101; - border-radius: 2px; - box-shadow: 1px 1px 1px rgba(1, 1, 1, 0), 0px 0px 1px rgba(14, 14, 14, 0); -} -input[type=range]::-ms-thumb { - box-shadow: 1px 1px 1px rgba(0, 0, 0, 0), 0px 0px 1px rgba(13, 13, 13, 0); - border: 1px solid #186ef7; - height: 20px; - width: 20px; - border-radius: 10px; - background: #186ef7; - cursor: pointer; - height: 6.4px; -} -input[type=range]:focus::-ms-fill-lower { - background: #c6c6c6; -} -input[type=range]:focus::-ms-fill-upper { - background: #c6c6c6; -} -input.editInput{ - width: 68%; - float: left; -} +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/ui/.babelrc ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/.babelrc b/contrib/views/storm/src/main/resources/ui/.babelrc deleted file mode 100644 index b533394..0000000 --- a/contrib/views/storm/src/main/resources/ui/.babelrc +++ /dev/null @@ -1,25 +0,0 @@ -{ - "presets": [ - ["es2015"], - "react", - "stage-0", - "airbnb" - ], - "plugins": [ - "transform-runtime", - "transform-decorators-legacy", - "transform-flow-strip-types", - "transform-es2015-modules-commonjs", - "transform-class-properties", - "react-hot-loader/babel", - "transform-async-to-generator", - ["babel-root-slash-import", { - "rootPathSuffix": "./app/scripts" - }] - ], - "env": { - "production": { - "plugins": ["transform-react-remove-prop-types", "transform-react-constant-elements","transform-async-to-generator"] - } - } -} http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/ui/.eslintignore.js ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/.eslintignore.js b/contrib/views/storm/src/main/resources/ui/.eslintignore.js deleted file mode 100644 index 2c4e446..0000000 --- a/contrib/views/storm/src/main/resources/ui/.eslintignore.js +++ /dev/null @@ -1,3 +0,0 @@ -node_modules/* -**/bower_components/* -**/vendor/*.js http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/ui/.eslintrc.js ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/.eslintrc.js b/contrib/views/storm/src/main/resources/ui/.eslintrc.js deleted file mode 100644 index 473fe48..0000000 --- a/contrib/views/storm/src/main/resources/ui/.eslintrc.js +++ /dev/null @@ -1,58 +0,0 @@ -module.exports = { - "parser": "babel-eslint", - "rules": { - "strict": 0 - }, - "env": { - "browser": true, - "es6": true, - "jquery": true - }, - "parserOptions": { - "sourceType": "module" - }, - "plugins": [ - "header", - "react" - ], - "rules": { - "header/header": [2, "block", [ - "*", - " 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.", - "*" - ]], - "comma-dangle": [ - "error", - "never" - ], - "indent": [ - "error", - 2 - ], - "linebreak-style": [ - "error", - "unix" - ], - "semi": [ - "error", - "always" - ], - /* Advanced Rules*/ - "no-unexpected-multiline": 2, - "curly": [2,"all"] - } -}; http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/ui/app/scripts/components/BarChart.jsx ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/BarChart.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/BarChart.jsx deleted file mode 100644 index dffd898..0000000 --- a/contrib/views/storm/src/main/resources/ui/app/scripts/components/BarChart.jsx +++ /dev/null @@ -1,429 +0,0 @@ -/** - 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. -**/ - -import React, {Component} from 'react'; -import PropTypes from 'prop-types'; -import ReactDOM from 'react-dom'; -import d3 from 'd3'; -import d3Tip from 'd3-tip'; - -export default class BarChart extends Component{ - static propTypes = { - data: PropTypes.array.isRequired, - width: PropTypes.number, - height: PropTypes.number - } - - constructor(props) { - super(props); - } - - componentDidMount(){ - this.setUpSVG(); - this.initToolTip(); - this.setLayout(); - this.initSets(); - this.barTypeTransition = this.transitionGrouped; - this.hiddenLayers = []; - this.drawBars(); - this.drawXAxis(); - this.drawYAxis(); - this.drawTooltip(); - this.drawLegends(); - } - - initSets(){ - this.layers = this.dataMapY(this.props.data); - // this.setMax(); - this.setX(); - this.setY(); - this.colorDomain(); - this.setXAxis(); - this.setYAxis(); - } - - setUpSVG(){ - this.svg = d3.select(ReactDOM.findDOMNode(this)) - .attr('width', this.props.width+"px") - .attr('height', this.props.height+50+"px"); - // .attr("viewBox", "-46 -5 " + (this.props.width+82) + " " + (this.props.height+28) ); - - this.container = this.svg.append("g") - .attr('class', 'svg-container') - .attr("transform", "translate(50,10)"); - - this.tipcontainer = this.svg.append('g').classed('tip-g', true) - .attr("transform", "translate(" + 40 + "," + 0 + ")"); - - this.tipcontainer.append('g').classed('tipLine-g', true).append('line').classed('tipline', true) - .style('stroke', '#aaa') - .style('visibility', 'hidden') - // .style('shape-rendering', 'crispEdges') - .attr('x1', 0).attr('x2', 0).attr('y1', 0).attr('y2', this.props.height); - } - - initToolTip() { - let self = this; - this.tip = d3Tip() - .attr('class', 'd3-tip') - .offset([-10, 0]) - .html(function(d) { - return self.toolTipHtml.call(self, d); - }); - this.svg.call(this.tip); - // const container = document.getElementById('app_container'); - // container.append($('body > .d3-tip')); - } - - setMax() { - this.yGroupMax = d3.max(this.layers, function(layer) { - return d3.max(layer, function(d) { - return d.y; - }); - }); - this.yGroupMin = d3.min(this.layers, function(layer) { - return d3.min(layer, function(d) { - return d.y; - }); - }); - this.yStackMax = d3.max(this.layers, function(layer) { - return d3.max(layer, function(d) { - return d.y0 + d.y; - }); - }); - this.yStackMin = d3.min(this.layers, function(layer) { - return d3.min(layer, function(d) { - return d3.min([d.y0, d.y]); - }); - }); - } - - setX() { - let self = this; - this.x = d3.scale.ordinal() - .domain(self.layers[0].map(function(d) { - return d.x; - })) - .rangeRoundBands([0, this.props.width], 0.08); - } - - setY() { - this.y = d3.scale.linear() - .domain([this.yStackMin, this.yStackMax]) - .range([this.props.height, 0]); - } - - setXAxis() { - this.xAxis = d3.svg.axis().scale(this.x).orient("bottom"); - } - - setYAxis() { - let formatValue = d3.format('.2s'); - this.yAxis = d3.svg - .axis() - .scale(this.y) - .orient("left") - .tickFormat(function(d){return formatValue(d);}); - } - - drawXAxis(xAxis, container, height) { - let xA = xAxis || this.xAxis, - containor = container || this.container, - hght = height || this.props.height; - - this.xAxisGrp = containor['xAxisEl'] = containor.append("g") - .attr("class", "x axis") - .attr("transform", "translate(0," + hght + ")") - .call(xA) - .selectAll(".tick text") - .call(this.wrap, this.x.rangeBand()); - } - - wrap(text, width) { - text.each(function() { - let text = d3.select(this), - words = text.text().split(/-+/).reverse(), - word, - line = [], - lineNumber = 0, - lineHeight = 1.1, // ems - y = text.attr("y"), - dy = parseFloat(text.attr("dy")), - tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em"); - - //Hack to show hidden div to find getComputedTextLength - // $('#lag-graph').css({visibility: 'hidden', display: 'block', position: 'absolute'}); - - while (word = words.pop()) { - line.push(word); - tspan.text(line.join(" ")); - if (tspan.node().getComputedTextLength() > width) { - line.pop(); - tspan.text(line.join(" ")); - line = [word]; - tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word); - } - } - // $('#lag-graph').css({visibility: '', display: 'none', position: ''}); - }); - } - - drawYAxis(x) { - let yAxis = this.yAxis; - this.yAxisGrp = this.container.append("g") - .attr("class", "y axis"); - this.yAxisGrp.ticks = this.yAxisGrp.call(yAxis); - this.yAxisGrp.append('text') - .text(this.props.yAttr[0].toUpperCase() + this.props.yAttr.substr(1,this.props.yAttr.length)).attr("text-anchor", "end") - .attr("y", 6) - .attr("dy", ".75em") - .attr("transform", "rotate(-90)"); - } - - dataMapY(data) { - let self = this; - let keys = d3.keys(data[0]).filter(function(key) { - return key !== self.props.xAttr; - }); - let layers = this.stack(keys.map(function(yAttr) { - return data.map(function(d) { - return { - x: d[self.props.xAttr], - y: d[yAttr], - type: yAttr - }; - }); - })); - let allLayers = layers.allLayers = []; - layers.forEach(function(d) { - allLayers.push(d); - }); - return layers; - } - - setLayout() { - let self = this; - this.stack = d3.layout.stack(); - } - - colorDomain() { - let self = this; - this.color = d3.scale.ordinal() - .range(["#b9cde5", "#1B76BB"]); - // this.color = d3.scale.category20c(); - // this.color.domain(d3.keys(this.props.data[0]).filter(function(key) { - // return key !== self.props.xAttr; - // })); - } - - drawBars() { - let self = this; - - this.layers_g = this.container.selectAll(".barLayer") - .data(this.layers); - - this.layers_g - .exit() - .remove(); - - this.layers_g - .enter().append("g") - .attr("class", "barLayer") - .style("fill", function(d, i) { - return self.color(d[0].type); - }); - - this.rect = this.layers_g.selectAll("rect") - .data(function(d) { - return d; - }); - - this.rect - .exit() - .remove(); - - this.rect - .enter().append("rect") - .attr("x", function(d) { - return self.x(d.x); - }) - .attr("y", function(d) { - return self.props.height; - }) - .attr("width", function(d) { - return self.x.rangeBand(); - }) - .classed("visible", true) - .attr("height", function(d) { - return 0; - }); - - this.barTypeTransition(); - } - - transitionGrouped() { - let x = this.x, - y = this.y, - height = this.props.height, - n = this.layers.length; - this.setMax(); - let yMin = this.yGroupMin < 0 ? this.yGroupMin : 0; - this.y.domain([yMin, this.yGroupMax]); - - let barWidth = (x.rangeBand() / n > 25) ? 25 : x.rangeBand() / n; - let xArr = new Array(n); - this.layers_g.selectAll('rect.visible') - .attr("x", function(d, i, j) { - if (xArr[i] == undefined) { - xArr[i] = x(d.x) + (x.rangeBand() / 2) - (n / 2 * barWidth); - } else { - xArr[i] += barWidth; - } - return xArr[i]; - }) - .attr("width", barWidth) - .transition().duration(500) - .attr("y", function(d) { - let _y = y(d.y); - if (d.y < 0){ - _y = y(d.y) - (height - y(0)); - } - return _y; - }) - .attr("height", function(d) { - return (height - y(Math.abs(d.y))) - (height - y(0)); - }); - this.container.select(".y.axis").transition().duration(500).call(this.yAxis); - } - - transitionStacked() { - this.stack(this.layers); - let x = this.x, - y = this.y, - height = this.props.height, - self = this, - n = this.layers.length; - this.setMax(); - this.y.domain([this.yStackMin, this.yStackMax]); - - let barWidth = (x.rangeBand() / n > 25) ? 25 : x.rangeBand() / n; - let xArr = new Array(n); - this.layers_g.selectAll('rect.visible').transition().duration(500) - .attr("y", function(d) { - let _y = y(d.y0 + d.y); - if (d.y < 0){ - _y = y(d.y) - Math.abs(y(d.y0) - y(d.y0 + d.y)); - } - return _y; - }) - .attr("height", function(d) { - return Math.abs(y(d.y0) - y(d.y0 + d.y)); - }) - .attr("x", function(d, i, j) { - xArr[i] = x(d.x) + (x.rangeBand() / 2) - (barWidth / 2); - return xArr[i]; - }) - .attr("width", barWidth); - this.container.select(".y.axis").transition().duration(500).call(this.yAxis); - } - - drawTooltip() { - let self = this; - let x = this.x.rangeBand ? this.x : d3.scale.ordinal() - .domain(self.data.map(function(d) { - return d[self.props.xAttr]; - })) - .rangeRoundBands([0, this.props.width]); - - let tipline = this.tipcontainer.select('.tipline'); - - this.tipcontainer.append('g').classed('tipRect-g', true).selectAll(".tipRect") - .data(this.props.data) - .enter().append("rect") - .attr("class", "tipRect") - .style('opacity', '0') - .attr("x", function(d) { - return self.x(d[self.props.xAttr]); - }) - .attr("width", function() { - return x.rangeBand(); - }) - .attr("y", function(d) { - return 0; - }) - .attr("height", function(d) { - return self.props.height; - }) - .on('mouseover', function(d) { - let x1 = parseInt(d3.select(this).attr('x')) + parseInt((x.rangeBand() / 2)); - tipline.attr('x1', x1).attr('x2', x1); - tipline.style('visibility', 'visible'); - return self.tip.show(d,this); - }) - .on('mouseout', function(d) { - tipline.style('visibility', 'hidden'); - return self.tip.hide(d,this); - }); - } - - toolTipHtml(d) { - let self = this; - let html = d[self.props.xAttr] + '<table><tbody>'; - _.each(d, function(val, key) { - if (key != self.props.xAttr){ - html += '<tr><td>' + key + ' </td><td> ' + val + '</td></tr>'; - } - }); - html += '</tbody></table>'; - return html; - } - - drawLegends() { - let self = this; - let legends = this.legendsEl = document.createElement('ul'); - legends = d3.select(legends) - .attr('class', 'legends') - .style('list-style', 'none'); - - let legend = legends.selectAll('.legend') - .data(this.color.domain()) - .enter() - .append('li') - .attr('class', 'legend'); - - legend.append('div') - .style('width', '10px') - .style('height', '10px') - .style('display', 'inline-block') - .style('background-color', function(d) { - return self.color(d); - }); - - legend.append('span') - .style('padding', '4px 0 4px 4px') - .text(function(d) { - return d; - }); - } - - render() { - return ( - <svg></svg> - ); - } -} http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/ui/app/scripts/components/Breadcrumbs.jsx ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/Breadcrumbs.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/Breadcrumbs.jsx deleted file mode 100644 index e4926ab..0000000 --- a/contrib/views/storm/src/main/resources/ui/app/scripts/components/Breadcrumbs.jsx +++ /dev/null @@ -1,45 +0,0 @@ -/** - 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. -**/ - -import React, {Component} from 'react'; -import PropTypes from 'prop-types'; -import ReactDom from 'react-dom'; - -export default class Breadcrumbs extends Component{ - static propTypes = { - links: PropTypes.array.isRequired - } - render() { - return ( - <ol id="breadcrumb"> - {this.renderLinks()} - </ol> - ); - } - renderLinks() { - var links = []; - for(var i = 0; i < this.props.links.length; i++){ - var object = this.props.links[i]; - if(object.link === '#/'){ - object.title = <i className="fa fa-home"></i>; - } - links.push(<li key={i}><a href={object.link}>{object.title}</a></li>); - } - return links; - } -} http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonNotification.jsx ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonNotification.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonNotification.jsx deleted file mode 100644 index 34e402c..0000000 --- a/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonNotification.jsx +++ /dev/null @@ -1,69 +0,0 @@ -/** - 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. -**/ - -import React, {Component} from 'react'; -import PropTypes from 'prop-types'; -import {notifyTextLimit} from '../utils/Constants'; - -class CommonNotification extends Component { - constructor(props) { - super(props); - this.state = { - data: false, - text: "Read more" - }; - } - showMore = () => { - if (this.state.text === "Read more") { - this.setState({text: "Hide", data: true}); - } else { - this.setState({text: "Read more", data: false}); - } - } - - render() { - /* flag value error, info, sucess */ - const {text, data} = this.state; - const {flag, content} = this.props; - const initial = content.substr(0, notifyTextLimit); - const moreText = content.substr(notifyTextLimit); - const readMoreTag = <a href="javascript:void(0)" onClick={this.showMore}>{text}</a>; - return ( - <div> - {initial} - {(data) - ? moreText - : null -} - <div> - {(flag === 'error' && moreText.length > 0) - ? readMoreTag - : null -} - </div> - </div> - ); - } -} - -export default CommonNotification; - -CommonNotification.propTypes = { - flag: PropTypes.string.isRequired, - content: PropTypes.string -}; http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonPagination.jsx ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonPagination.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonPagination.jsx deleted file mode 100644 index 5128a09..0000000 --- a/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonPagination.jsx +++ /dev/null @@ -1,56 +0,0 @@ -/** - 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. -**/ - -import React, {Component} from 'react'; -import _ from 'lodash'; -import {Pagination} from 'react-bootstrap'; - -export default class CommonPagination extends Component{ - constructor(props){ - super(props); - } - - handleSelect = (eventKey) => { - this.props.callBackFunction(eventKey,this.props.tableName); - } - - render(){ - const {activePage,pageSize,filteredEntities} = this.props; - const totalPages = Math.ceil(filteredEntities.length / pageSize); - - return( - <div className="pagination-wrapper"> - <div className="pull-left"> - <span>{`Showing ${activePage > 1 ? (activePage-1)*pageSize : activePage } to ${activePage*pageSize > filteredEntities.length ? filteredEntities.length : (activePage*pageSize)} of ${filteredEntities.length} entries`}</span> - </div> - <Pagination - className={`${filteredEntities.length === 0? 'hidden':'shown pull-right'}`} - prev={false} - next={false} - first - last - ellipsis - items={totalPages} - maxButtons={5} - activePage={activePage} - onSelect={this.handleSelect}> - </Pagination> - </div> - ); - } -} http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonSwitchComponent.jsx ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonSwitchComponent.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonSwitchComponent.jsx deleted file mode 100644 index 804f51e..0000000 --- a/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonSwitchComponent.jsx +++ /dev/null @@ -1,41 +0,0 @@ -/** - 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. -**/ - -import React, {Component} from 'react'; - -export default class CommonSwitchComponent extends Component { - render(){ - const {switchCallBack,checked,textON,textOFF,KYC} = this.props; - let switchId = "switch-"+((Math.random())*100).toFixed(0); - return ( - <div className={`switchWrapper ${!!KYC ? 'lagSwitchSetting pull-right' : ''}`}> - <span className={`switchSlider ${checked ? 'onSlider' : 'offSlider'}`} onClick={switchCallBack}> - <span className={`switchItemOn sliderText ${!!KYC ? 'graphSwitchOn' : ''}`}>{textON}</span> - <span className="switchItemMid"></span> - <span className={`switchItemOff sliderText ${!!KYC ? 'graphSwitchOff' : ''}`}>{textOFF}</span> - </span> - </div> - - ); - } -} - -CommonSwitchComponent.defaultProps = { - textON : "ON", - textOFF : "OFF" -}; http://git-wip-us.apache.org/repos/asf/ambari/blob/cf5c068c/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonWindowPanel.jsx ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonWindowPanel.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonWindowPanel.jsx deleted file mode 100644 index 0f8130f..0000000 --- a/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonWindowPanel.jsx +++ /dev/null @@ -1,99 +0,0 @@ -/** - 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. -**/ - -import React, {Component} from 'react'; -import Select from 'react-select'; -import CommonSwitchComponent from './CommonSwitchComponent'; -import {OverlayTrigger, Tooltip} from 'react-bootstrap'; - -export default class CommonWindowPanel extends Component{ - constructor(props){ - super(props); - } - - windowChange = (obj) => { - this.props.handleWindowChange(obj); - } - - commonToggleChange = (params) => { - this.props.toggleSystem(params); - } - - commonTopologyActionHandler = (action) => { - this.props.handleTopologyAction(action); - } - - render(){ - const {selectedWindowKey,windowOptions,systemFlag,debugFlag,handleLogLevel,topologyStatus,KYC,handleProfiling} = this.props; - return( - <div className="form-group no-margin"> - <label className="col-sm-1 control-label">Window</label> - <div className="col-sm-2"> - <Select value={selectedWindowKey} options={windowOptions} onChange={this.windowChange.bind(this)} valueKey="label" labelKey="label" clearable={false}/> - </div> - <label className="col-sm-2 control-label">System Summary</label> - <div className="col-sm-2"> - <CommonSwitchComponent checked={systemFlag} switchCallBack={this.commonToggleChange.bind(this,'systemFlag')}/> - </div> - <label className="col-sm-1 control-label">Debug</label> - <div className="col-sm-1"> - <CommonSwitchComponent checked={debugFlag} switchCallBack={this.commonToggleChange.bind(this,'debugFlag')}/> - </div> - <div className="col-sm-3 text-right"> - <div className="btn-group" role="group"> - { - KYC === 'detailView' - ? [ <OverlayTrigger key={1} placement="top" overlay={<Tooltip id="tooltip1">Activate</Tooltip>}> - <button type="button" className="btn btn-primary" onClick={this.commonTopologyActionHandler.bind(this,'activate')} disabled={topologyStatus === 'ACTIVE' ? "disabled" : null}> - <i className="fa fa-play"></i> - </button> - </OverlayTrigger>, - <OverlayTrigger key={2} placement="top" overlay={<Tooltip id="tooltip1">Deactivate</Tooltip>}> - <button type="button" className="btn btn-primary" onClick={this.commonTopologyActionHandler.bind(this,'deactivate')} disabled={topologyStatus === 'INACTIVE' ? "disabled" : null}> - <i className="fa fa-stop"></i> - </button> - </OverlayTrigger>, - <OverlayTrigger key={3} placement="top" overlay={<Tooltip id="tooltip1">Rebalance</Tooltip>}> - <button type="button" className="btn btn-primary" onClick={this.commonTopologyActionHandler.bind(this,'rebalance')} disabled={topologyStatus === 'REBALANCING' ? "disabled" : null}> - <i className="fa fa-balance-scale"></i> - </button> - </OverlayTrigger>, - <OverlayTrigger key={4} placement="top" overlay={<Tooltip id="tooltip1">Kill</Tooltip>}> - <button type="button" className="btn btn-primary" onClick={this.commonTopologyActionHandler.bind(this,'kill')} disabled={topologyStatus === 'KILLED' ? "disabled" : null}> - <i className="fa fa-ban"></i> - </button> - </OverlayTrigger>, - <OverlayTrigger key={5} placement="top" overlay={<Tooltip id="tooltip1">Change Log Level</Tooltip>}> - <button type="button" className="btn btn-primary" onClick={handleLogLevel}> - <i className="fa fa-file-o"></i> - </button> - </OverlayTrigger> - ] - : <OverlayTrigger placement="top" overlay={<Tooltip id="tooltip1">Profiling & Debugging</Tooltip>}> - <button type="button" className="btn btn-primary" onClick={handleProfiling}> - <i className="fa fa-cogs"></i> - </button> - </OverlayTrigger> - - } - </div> - </div> - </div> - ); - } -}
