You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by yu...@apache.org on 2017/10/23 17:24:13 UTC
[10/30] ambari git commit: AMBARI-21955. Update React version to
15.6.2 to get MIT license. (Sanket Shah via yusaku)
http://git-wip-us.apache.org/repos/asf/ambari/blob/810488ab/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
deleted file mode 100644
index 136d95f..0000000
--- a/contrib/views/storm/src/main/resources/scripts/views/TopologyDetailView.jsx
+++ /dev/null
@@ -1,1039 +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.
-*/
-
-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/810488ab/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
deleted file mode 100644
index 25441fa..0000000
--- a/contrib/views/storm/src/main/resources/scripts/views/TopologyListingView.jsx
+++ /dev/null
@@ -1,65 +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.
-*/
-
-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/810488ab/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
deleted file mode 100644
index f6b1685..0000000
--- a/contrib/views/storm/src/main/resources/styles/style.css
+++ /dev/null
@@ -1,579 +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.
-*/
-/*
- Theme: Apache Storm
- Author: Sanket
-*/
-
-*:focus {
- outline: none !important;
-}
-
-body {
- font-family: 'Lato', sans-serif;
- color: #4b4b4b;
-}
-
-/* Bootstrap Extended */
-.no-margin {
- margin: 0px;
-}
-
-.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>thead>tr>th {
- border-top: none;
-}
-.table-enlarge > thead > tr > th,
-.table-enlarge > tbody > tr > th,
-.table-enlarge > tfoot > tr > th,
-.table-enlarge > thead > tr > td,
-.table-enlarge > tbody > tr > td,
-.table-enlarge > tfoot > tr > td {
- padding: 11px;
-}
-#breadcrumb {
- margin-top: 15px;
- list-style: none;
- display: inline-block;
- padding: 0px;
-}
-#breadcrumb .icon {
- font-size: 14px;
-}
-#breadcrumb li {
- float: left;
-}
-#breadcrumb li a {
- color: #FFF;
- display: block;
- background: #27a9e1;
- text-decoration: none;
- position: relative;
- height: 30px;
- line-height: 30px;
- padding: 0 10px 0 5px;
- text-align: center;
- margin-right: 23px;
-}
-#breadcrumb li:nth-child(even) a {
- background-color: #1b75bb;
-}
-#breadcrumb li:nth-child(even) a:before {
- border-color: #1b75bb;
- border-left-color: transparent;
-}
-#breadcrumb li:nth-child(even) a:after {
- border-left-color: #1b75bb;
-}
-#breadcrumb li:first-child a {
- padding-left: 15px;
- -moz-border-radius: 4px 0 0 4px;
- -webkit-border-radius: 4px;
- border-radius: 4px 0 0 4px;
-}
-#breadcrumb li:first-child a:before {
- border: none;
-}
-#breadcrumb li:last-child a {
- padding-right: 15px;
- -moz-border-radius: 0 4px 4px 0;
- -webkit-border-radius: 0;
- border-radius: 0 4px 4px 0;
-}
-#breadcrumb li:last-child a:after {
- border: none;
-}
-#breadcrumb li a:before, #breadcrumb li a:after {
- content: "";
- position: absolute;
- top: 0;
- border: 0 solid #27a9e1;
- border-width: 15px 10px;
- width: 0;
- height: 0;
-}
-#breadcrumb li a:before {
- left: -20px;
- border-left-color: transparent;
-}
-#breadcrumb li a:after {
- left: 100%;
- border-color: transparent;
- border-left-color: #27a9e1;
-}
-#breadcrumb li a:hover {
- background-color: #1bbb60;
-}
-#breadcrumb li a:hover:before {
- border-color: #1bbb60;
- border-left-color: transparent;
-}
-#breadcrumb li a:hover:after {
- border-left-color: #1bbb60;
-}
-
-.preformatted {
- white-space: pre;
-}
-
-/* Boxes */
-.box {
- position: relative;
- margin-bottom: 15px;
- border: 1px #bcbcbc solid;
- border-bottom-width: 3px;
- border-radius: 5px;
-}
-.box .box-header:before,
-.box .box-header:after {
- content: " ";
- display: table;
-}
-.box .box-header:after {
- clear: both;
-}
-.box .box-header {
- background-color: #f3f6f9;
- padding: 0 10px;
- border-bottom: 1px #bcbcbc solid;
- border-radius: 4px 4px 0px 0px;
-}
-.box .box-header h4 {
- float: left;
- margin: 0px;
- font-size: 16px;
- font-weight: 700;
- line-height: 40px;
- letter-spacing: 1px;
-}
-.box .box-header .box-control {
- float: right;
-}
-.box .box-header .box-control .bootstrap-switch {
- margin: 9px 2px;
-}
-.box .box-header .box-control a {
- 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 .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 {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;
-}
-.box .box-body.paddless {
- padding: 0px;
-}
-
-.control-search {
- position: relative;
-}
-.control-search input[type="text"] {
- width: 250px;
- margin-top: 5px;
- padding: 4px 24px 4px 4px;
- background-color: transparent;
- border: none;
- border-bottom: 1px #BCBCBC solid;
-}
-.control-search i.fa-search {
- position: absolute;
- right: 6px;
- top: 12px;
- color: #999;
-}
-
-.box.filter {
- border-color: #27a9e1;
-}
-.box.info {
- border-color: #27a9e1;
-}
-.box.info .box-header {
- background-color: #E9F8FF;
- border-color: #27a9e1;
-}
-.box.warning {
- border-color: #fbaf3f;
-}
-.box.warning .box-header {
- background-color: #FFF6E9;
- border-color: #fbaf3f;
-}
-
-/*Tiles*/
-.tile {
- display: block;
- margin-bottom: 15px;
- color: #fff;
- border-radius: 2px;
-}
-.tile .tile-header {
- letter-spacing: 2px;
- padding: 8px 12px;
- line-height: 1.5;
- text-transform: uppercase;
- font-size: 11px;
- border-top-right-radius: 2px;
- border-top-left-radius: 2px;
-}
-.tile .tile-body {
- overflow: hidden;
- padding: 12px;
-}
-.tile .tile-body i.fa {
- font-size: 30px;
- opacity: .4;
-}
-.tile .tile-body .count {
- float: right;
- font-size: 30px;
- line-height: 30px;
- font-weight: 300;
-}
-.tile.primary {
- background: #1b75bb;
-}
-.tile.primary > .tile-header {
- background-color: #085C9D;
-}
-.tile.warning {
- background: #fbaf3f;
-}
-.tile.warning > .tile-header {
- background-color: #ED940E;
-}
-.tile.success {
- background: #1bbb60;
-}
-.tile.success > .tile-header {
- background-color: #00A347;
-}
-.tile.danger {
- background: #ff5816;
-}
-.tile.danger > .tile-header {
- background-color: #D13B00;
-}
-
-.summary-tile {
- display: block;
- margin-bottom: 15px;
- color: #fff;
- border-radius: 5px;
- background-color: #1B76BB;
-}
-.summary-tile .summary-title {
- letter-spacing: 2px;
- padding: 8px 12px;
- line-height: 1.5;
- text-transform: uppercase;
- border-top-right-radius: 5px;
- border-top-left-radius: 5px;
- background-color: #085C9D;
-}
-.summary-tile .summary-body {
- overflow: hidden;
- padding: 12px;
-}
-.summary-tile .summary-body a{
- color: #fff;
- text-decoration: underline;
-}
-.summary-tile .summary-body strong {
- display: inline-block;
- width: 120px;
- margin-right: 10px;
- text-align: right;
-}
-.summary-tile .summary-body .form-group {
- margin-bottom: 0px;
-}
-
-.stats-tile {
- display: block;
- margin-bottom: 15px;
- color: #333;
- border-radius: 5px;
- background-color: #EEEEEE;
-}
-.stats-tile .stats-title {
- letter-spacing: 2px;
- padding: 8px 12px;
- line-height: 1.5;
- text-transform: uppercase;
- border-top-right-radius: 5px;
- border-top-left-radius: 5px;
- background-color: #C0C0C0;
-}
-.stats-tile .stats-body {
- overflow: hidden;
- padding: 12px;
-}
-
-/* Detail Page */
-.graph-bg {
- background-image: linear-gradient(to right, rgb(0, 0, 0) -10px, transparent 1px), linear-gradient(rgb(0, 0, 0) -10px, transparent 1px);
- background-size: 10px 10px;
- min-height: 300px;
-}
-.summary-panel {
- font-size: 90%;
- padding: 10px;
- background: #f9f9f9;
- border: 1px solid #ccc;
- border-bottom-width: 2px;
- border-left-width: 2px;
- box-shadow: -1px 1px 5px rgba(0,0,0,0.25);
-}
-.circle {
- float: left;
- margin-right: 15px;
-}
-
-/*
-REACT
- */
-#supervisorCount,
-#slotsCount {
- position: 'relative';
- display: 'inline-block'
-}
-#supervisorCount > svg > g > text,
-#slotsCount > svg > g > text {
- font-size: 27px;
- fill: white;
-}
-.supervisor-table svg > g > text {
- font-size: 12px;
- fill: black;
-}
-#supervisorCount > svg > g > text.percent,
-#slotsCount > svg > g > text.percent {
- font-size: 18px;
-}
-#supervisorCount > svg > g > text.graphVal,
-#slotsCount > svg > g > text.graphVal {
- font-weight: 300;
-}
-/* D3 */
-.d3-tip {
- z-index: 99;
- line-height: 1;
- font-weight: bold;
- padding: 10px;
- background: rgba(0, 0, 0, 0.8);
- color: #fff;
- border-radius: 2px;
- font-size: 12px;
-}
-.d3-tip .summary {
- padding: 3px;
-}
-
-/* Creates a small triangle extender for the tooltip */
-.d3-tip:after {
- box-sizing: border-box;
- display: inline;
- font-size: 16px;
- width: 100%;
- line-height: 1;
- color: rgba(0, 0, 0, 0.8);
- content: "\25BC";
- position: absolute;
- text-align: center;
-}
-.node ellipse {
- stroke: #333;
- fill: #fff;
- stroke-width: 1px;
-}
-.edgePath path {
- stroke: grey;
- fill: none;
- cursor: default;
- stroke-width: 2;
- stroke-dasharray: 5, 5;
-}
-
-/* Style northward tooltips differently */
-.d3-tip.n:after {
- margin: -4px 0 0 0;
- top: 100%;
- left: 0;
-}
-path.link {
- fill: none;
- stroke: grey;
- cursor: default;
-}
-
-path.link.dragline {
- pointer-events: none;
-}
-
-path.link.hidden {
- stroke-width: 0;
-}
-
-circle.node {
- stroke-width: 1.5px;
- cursor: pointer;
-}
-
-circle.node.reflexive {
- stroke: #000 !important;
- stroke-width: 2.5px;
-}
-text.id {
- text-anchor: middle;
- font-weight: bold;
-}
-.d3-tip ul {
- padding:0;
- margin:0;
- list-style: none;
-}
-.d3-tip ul li {
- font-size: 12px;
- line-height: 20px;
-}
-marker {
- fill: grey;
-}
-.axis path, .axis rect {
- fill: none;
- stroke: grey;
- stroke-width: 1;
- shape-rendering: crispEdges;
-}
-
-.axis line {
- stroke: grey;
- stroke-width: 1;
- shape-rendering: crispEdges;
-}
-
-.d3-tip table {
- margin-top: 8px;
- width : 100%;
-}
-.d3-tip table tr td {
- padding: 3px;
- border: 1px red solid;
-}
-ul.legends {
- text-align: center;
-}
-ul.legends li.legend{
- display: inline-block;
- margin-right: 10px;
-}
-.table-summary {
- font-size: 12px;
- font-weight: 700;
- -webkit-font-smoothing: antialiased;
-}
-.summary-badge {
- font-weight: bold;
- padding: 1px 4px;
- margin: 1px;
- border: 1px solid #d8d8d8;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-}
-[data-toggle="collapse"]{
- cursor: pointer;
-}
-#ex1Slider {
- margin-left: 13px;
- margin-right: 13px;
-}
-#modal-rebalance .modal-body{
- max-height: 450px;
- overflow-y: auto;
-}
-.loader {
- position: fixed;
- top: 0;
- bottom: 0;
- left: 0;
- right: 0;
- background: url('../images/loader.gif') rgba(255,255,255,0.75) no-repeat center center;
- z-index: 9;
-}
-.inner-loader{
- position: absolute;
- top: 0px;
- left: 0px;
- bottom: 0px;
- right: 0px;
- background: url('../images/loader.gif') rgba(255,255,255,0.75) no-repeat center center;
-}
-.searchbar{
- margin-top: 15px;
-}
-.searchbar .btn-group{
- display: flex !important;
-}
-.searchbar .dropdown-toggle{
- border-radius: 0;
- margin-left: -1px;
-}
-.searchbar .btn-group .btn {
- margin-left: -1px;
-}
-.searchbar .dropdown-menu{
- padding: 10px 15px 5px;
-}
-.searchbar .dropdown-menu input[type="checkbox"]{
- vertical-align: top;
- margin-right: 5px;
-}
-.searchbar .open > .dropdown-toggle.btn-default:hover,
-.searchbar .open > .dropdown-toggle.btn-default:focus{
- border: 1px solid transparent;
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/810488ab/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
new file mode 100644
index 0000000..b533394
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/.babelrc
@@ -0,0 +1,25 @@
+{
+ "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/810488ab/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
new file mode 100644
index 0000000..2c4e446
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/.eslintignore.js
@@ -0,0 +1,3 @@
+node_modules/*
+**/bower_components/*
+**/vendor/*.js
http://git-wip-us.apache.org/repos/asf/ambari/blob/810488ab/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
new file mode 100644
index 0000000..473fe48
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/.eslintrc.js
@@ -0,0 +1,58 @@
+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/810488ab/contrib/views/storm/src/main/resources/ui/app/scripts/app.js
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/app.js b/contrib/views/storm/src/main/resources/ui/app/scripts/app.js
new file mode 100644
index 0000000..41a7fa9
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/app/scripts/app.js
@@ -0,0 +1,40 @@
+/**
+ 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 routes from './routers/routes';
+import {render} from 'react-dom';
+import {Router, browserHistory, hashHistory} from 'react-router';
+import {getStormVersion} from './utils/Constants';
+
+class App extends Component {
+ constructor() {
+ super();
+ this.fetchVersion();
+ }
+ fetchVersion(){
+ getStormVersion().then((res) => {
+ this.forceUpdate();
+ });
+ }
+ render() {
+ return (<Router ref="router" history={hashHistory} routes={routes}/>);
+ }
+}
+
+export default App;
http://git-wip-us.apache.org/repos/asf/ambari/blob/810488ab/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
new file mode 100644
index 0000000..dffd898
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/BarChart.jsx
@@ -0,0 +1,429 @@
+/**
+ 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/810488ab/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
new file mode 100644
index 0000000..e4926ab
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/Breadcrumbs.jsx
@@ -0,0 +1,45 @@
+/**
+ 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/810488ab/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonExpanded.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonExpanded.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonExpanded.jsx
new file mode 100644
index 0000000..558d2a2
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonExpanded.jsx
@@ -0,0 +1,30 @@
+/**
+ 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';
+
+const CommonExpanded = (props) => {
+ const {expandFlag} = props;
+ return (
+ <div className="box-control pull-right" style={{marginLeft : '17px',marginTop : '-2px'}}>
+ <span className="primary"><i className={`fa ${expandFlag ? 'fa-compress' : 'fa-expand'}`}></i></span>
+ </div>
+ );
+};
+
+export default CommonExpanded;
http://git-wip-us.apache.org/repos/asf/ambari/blob/810488ab/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
new file mode 100644
index 0000000..34e402c
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonNotification.jsx
@@ -0,0 +1,69 @@
+/**
+ 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/810488ab/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
new file mode 100644
index 0000000..5128a09
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonPagination.jsx
@@ -0,0 +1,56 @@
+/**
+ 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/810488ab/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
new file mode 100644
index 0000000..804f51e
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonSwitchComponent.jsx
@@ -0,0 +1,41 @@
+/**
+ 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"
+};