ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From yus...@apache.org
Subject [04/22] ambari git commit: AMBARI-21955. Update React version to 15.6.2 to get MIT license. (Sanket Shah via yusaku)
Date Fri, 20 Oct 2017 19:50:13 GMT
http://git-wip-us.apache.org/repos/asf/ambari/blob/e88526e7/contrib/views/storm/src/main/resources/ui/app/scripts/components/Editable.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/Editable.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/Editable.jsx
new file mode 100644
index 0000000..03005ae
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/Editable.jsx
@@ -0,0 +1,127 @@
+/**
+ 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 ReactDOM, {findDOMNode} from 'react-dom';
+import {Overlay, Popover, Button} from 'react-bootstrap';
+
+export default class Editable extends Component {
+  state = {
+    edit: false,
+    errorMsg: ''
+  };
+
+  handleClick = () => {
+    let state = this.state;
+    state.edit = true;
+    this.setState(state);
+  }
+
+  handleResolve = () => {
+    const {resolve} = this.props;
+    if (resolve) {
+      resolve(this);
+    }
+  }
+
+  handleReject = () => {
+    const {reject} = this.props;
+    if (reject) {
+      reject(this);
+    } else {
+      this.hideEditor();
+    }
+  }
+
+  hideEditor = () => {
+    let state = this.state;
+    state.edit = false;
+    this.setState(state);
+  }
+
+  getValueString() {
+    const {children} = this.props;
+
+    if (children.type == 'input' || children.type == 'textarea') {
+      return children.props.value || children.props.defaultValue;
+    } else if (children.type == 'select') {} else {
+      var fn = children.getStringValue;
+      if (fn) {
+        return fn();
+      } else {
+        console.error('Custom component must have getValueString() function.');
+      }
+    }
+  }
+
+  anchorStyle = {
+    textDecoration: 'none',
+    borderBottom: 'dashed 1px #0088cc',
+    cursor: 'pointer',
+    color: '#323133'
+  };
+
+  render() {
+    const {children, showButtons, inline, placement, title} = this.props;
+    const {edit, errorMsg} = this.state;
+
+    const buttons = showButtons
+      ? ([<Button className="btn-primary btn-sm" onClick={this.handleResolve} key="resolve" style={{margin : "0 0 3px 5px"}}>
+          <i className="fa fa-check"></i></Button>,
+        <Button className="btn-default btn-sm"  onClick={this.handleReject} key="reject"  style={{margin : "0 3px"}}>
+          <i className="fa fa-times"></i>
+          </Button>
+      ])
+      : null;
+
+    const error = errorMsg
+      ? (
+        <div className="editable-error">{errorMsg}</div>
+      )
+      : null;
+
+    const popover = (
+      <Popover id="popover-positioned-left" title={title || ''}>
+        {children}
+        {buttons}
+        {error}
+      </Popover>
+    );
+
+    return (
+      <div className="editable-container" style={{display: 'inline'}} id={this.props.id || ''}>
+        {edit && inline
+          ? null
+          : <a ref="target" onClick={this.handleClick} style={this.anchorStyle}>{this.getValueString()}</a>
+}
+        {edit && inline
+          ? [children, buttons, error]
+          : <Overlay show={edit} target={() => ReactDOM.findDOMNode(this.refs.target)} {...this.props}>
+            {popover}
+          </Overlay>
+}
+      </div>
+    );
+  }
+}
+
+Editable.defaultProps = {
+  showButtons: true,
+  inline: false,
+  placement: "top"
+};

http://git-wip-us.apache.org/repos/asf/ambari/blob/e88526e7/contrib/views/storm/src/main/resources/ui/app/scripts/components/FSModel.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/FSModel.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/FSModel.jsx
new file mode 100644
index 0000000..14cb17d
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/FSModel.jsx
@@ -0,0 +1,149 @@
+/**
+ 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 {Modal, Button} from 'react-bootstrap';
+
+const defaultState = {
+  show: false,
+  title: '',
+  btnOkText: 'Ok',
+  btnCancelText: 'Cancel'
+};
+
+export default class FSModal extends Component {
+  state = defaultState;
+  show() {
+    var state = state || {};
+    state.show = true;
+    this.setState(state);
+  }
+  sure() {
+    let resolve = this.props["data-resolve"];
+    if (resolve) {
+      resolve();
+    }
+  }
+  cancel() {
+    let reject = this.props["data-reject"];
+    if (reject) {
+      reject();
+    } else {
+      this.hide();
+    }
+  }
+  hide() {
+    this.setState({show: false});
+  }
+  header() {
+    return (
+      <Modal.Header closeButton>
+        <Modal.Title>
+          {this.props["data-title"]}
+        </Modal.Title>
+      </Modal.Header>
+    );
+  }
+  body() {
+    return (
+      <Modal.Body>
+        {this.props.children}
+      </Modal.Body>
+    );
+  }
+  footer() {
+    return (
+      <Modal.Footer>
+        {
+          this.props.hideCloseBtn
+          ? null
+          : <Button bsStyle='default' onClick={this.cancel.bind(this)} data-stest="cancelbtn">
+              {this.props.closeLabel || this.state.btnCancelText}
+            </Button>
+        }
+        {
+          this.props.hideOkBtn
+          ? null
+          : <Button bsStyle='success' onClick={this.sure.bind(this)}  data-stest="okbtn" disabled={this.props.btnOkDisabled}>
+              {this.props.okLabel || this.state.btnOkText}
+            </Button>
+        }
+      </Modal.Footer>
+    );
+  }
+  render() {
+    return (
+      <Modal aria-labelledby='contained-modal-title' backdrop="static" keyboard={true} onHide={this.cancel.bind(this)} show={this.state.show} {...this.props}>
+        {this.props.hideHeader
+          ? ''
+          : this.header()}
+        {this.body()}
+        {this.props.hideFooter
+          ? ''
+          : this.footer()}
+      </Modal>
+    );
+  }
+}
+
+var _resolve;
+var _reject;
+
+export class Confirm extends FSModal {
+  show(state) {
+    var state = state || {};
+    state.show = true;
+    this.setState(state);
+    let promise = new Promise(function(resolve, reject) {
+      _resolve = resolve;
+      _reject = reject;
+    });
+    return promise;
+  }
+  sure() {
+    _resolve(this);
+  }
+  cancel() {
+    _reject(this);
+    this.setState(defaultState);
+  }
+  header() {
+    return (
+      <Modal.Header closeButton>
+        <Modal.Title>
+          {this.state.title}
+        </Modal.Title>
+      </Modal.Header>
+    );
+  }
+  body() {
+    return '';
+  }
+  footer() {
+    return (
+      <Modal.Footer>
+        <Button bsStyle='danger' onClick={this.cancel.bind(this)} data-stest="confirmBoxCancelBtn">
+          {this.state.btnCancelText || 'No'}
+        </Button>
+        <Button bsStyle='success' onClick={this.sure.bind(this)} data-stest="confirmBoxOkBtn">
+          {this.state.btnOkText || 'Yes'}
+        </Button>
+      </Modal.Footer>
+    );
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e88526e7/contrib/views/storm/src/main/resources/ui/app/scripts/components/LogLevelComponent.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/LogLevelComponent.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/LogLevelComponent.jsx
new file mode 100644
index 0000000..fb8e10b
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/LogLevelComponent.jsx
@@ -0,0 +1,236 @@
+/**
+ 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 TopologyREST from '../rest/TopologyREST';
+import {
+  Table,
+  Thead,
+  Th,
+  Tr,
+  Td,
+  unsafe
+} from 'reactable';
+import CommonPagination from './CommonPagination';
+import {toastOpt,pageSize} from '../utils/Constants';
+import Select from 'react-select';
+import FSReactToastr from './FSReactToastr';
+import CommonNotification from './CommonNotification';
+import Editable  from './Editable';
+
+export default class LogLevelComponent extends Component{
+  constructor(props){
+    super(props);
+    this.state = {
+      logLevelObj : {},
+      traceOption : this.populateTraceOptions(),
+      selectedKeyName : 'com.your.organization.LoggerName',
+      selectedTrace : 'ALL',
+      selectedTimeOut : 30
+    };
+    this.fetchData();
+    this.keyName = '';
+    this.timeChange='';
+  }
+
+  fetchData = () => {
+    const {topologyId} = this.props;
+    TopologyREST.getLogConfig(topologyId).then((result) => {
+      if(result.errorMessage !== undefined){
+        FSReactToastr.error(
+          <CommonNotification flag="error" content={result.errorMessage}/>, '', toastOpt);
+      } else {
+        let stateObj={};
+        stateObj.selectedKeyName = 'com.your.organization.LoggerName';
+        stateObj.selectedTrace = 'ALL';
+        stateObj.selectedTimeOut = 30;
+        stateObj.logLevelObj = result.namedLoggerLevels;
+        this.setState(stateObj);
+      }
+    });
+  }
+
+  populateTraceOptions = () => {
+    let temp=[];
+    const arr = ['ALL','TRACE','DEBUG','INFO','WARN','ERROR','FATAL','OFF'];
+    _.map(arr, (a) => {
+      temp.push({label : a, value : a});
+    });
+    return temp;
+  }
+
+  handleNameChange = (e) => {
+    this.keyName =  e.target.value.trim();
+  }
+
+  handleTimeChange = (e) => {
+    this.timeChange =  e.target.value.trim();
+  }
+
+  traceLavelChange = (type,key,addRow,obj) => {
+    let tempObj = _.cloneDeep(this.state.logLevelObj);
+    let tempKeyName = 'ALL';
+    if(!!addRow){
+      tempKeyName = obj.value;
+    } else{
+      tempObj[type][key] = obj.value;
+    }
+    this.setState({logLevelObj : tempObj,selectedTrace : tempKeyName});
+  }
+
+  modifyCommonObjValue = (refType,type,key,action,addRow) => {
+    let logObj = _.cloneDeep(this.state.logLevelObj);
+    let tempTimeOut = _.cloneDeep(this.state.selectedTimeOut);
+    const timeValue =  (this.timeChange === '' || this.timeChange === undefined) ? parseInt(this.refs[refType].defaultValue || 0,10) : parseInt(this.timeChange,10);
+    if(action === 'save' && addRow === null){
+      logObj[type][key] = timeValue;
+    } else if(action === 'save' && !!addRow){
+      tempTimeOut = timeValue;
+      this.timeChange = '';
+    }else if(action === 'reject'){
+      this.timeChange = parseInt(this.refs[refType].defaultValue || 0,10);
+    }
+    this.refs[refType].hideEditor();
+    this.setState({logLevelObj : logObj ,selectedTimeOut :tempTimeOut });
+  }
+
+  getDateFormat = (str) => {
+    const d = new Date(str);
+    return d.toLocaleDateString() + ' ' + d.toLocaleTimeString();
+  }
+
+  saveAndClearLogConfig = (type,action) => {
+    let tempObj = _.cloneDeep(this.state.logLevelObj);
+    let obj={},namedLoggerLevels={};
+    obj.namedLoggerLevels={};
+    if(action === 'clear'){
+      obj.namedLoggerLevels[type] = {};
+      obj.namedLoggerLevels[type].timeout = 0;
+      obj.namedLoggerLevels[type].target_level = null;
+    } else {
+      obj.namedLoggerLevels[type] = tempObj[type];
+    }
+    obj.namedLoggerLevels[type].reset_level = 'INFO';
+    delete obj.namedLoggerLevels[type].timeout_epoch;
+
+    this.callLogConfigAPI(obj,null,action);
+  }
+
+  callLogConfigAPI = (obj,addRow,action) => {
+    const {topologyId,logConfig} =  this.props;
+    const {logLevelObj} = this.state;
+    TopologyREST.postLogConfig(topologyId, {body : JSON.stringify(obj)}).then((result) => {
+      if(result.errorMessage !== undefined){
+        this.setState({logLevelObj : logConfig});
+        FSReactToastr.error(
+          <CommonNotification flag="error" content={result.errorMessage}/>, '', toastOpt);
+      } else {
+        let msg = !!addRow ? "Log configuration added successfully" : (action === 'save' ? "Log configuration applied successfully." : "Log configuration cleared successfully.");
+        FSReactToastr.success(<strong>{msg}</strong>);
+        this.fetchData();
+      }
+    });
+  }
+
+  addLoggerName = (refType,action) => {
+    let tempName = _.cloneDeep(this.state.selectedKeyName);
+    if(action === 'save'){
+      tempName = !!this.keyName ? this.keyName :  tempName;
+    }else if(action === 'reject'){
+      this.keyName = this.refs[refType].defaultValue || tempName;
+    }
+    this.refs[refType].hideEditor();
+    this.setState({selectedKeyName : tempName});
+  }
+
+  addLogRow = () => {
+    const {selectedKeyName,selectedTrace,selectedTimeOut} = this.state;
+    let obj={};
+    obj.namedLoggerLevels = {};
+    obj.namedLoggerLevels[selectedKeyName] = {};
+    obj.namedLoggerLevels[selectedKeyName].target_level = selectedTrace;
+    obj.namedLoggerLevels[selectedKeyName].reset_level = 'INFO';
+    obj.namedLoggerLevels[selectedKeyName].timeout = selectedTimeOut;
+    this.callLogConfigAPI(obj,'addRow');
+  }
+
+  render(){
+    const {logLevelObj,traceOption,selectedKeyName,selectedTrace,selectedTimeOut} = this.state;
+    return(
+      <div className={`boxAnimated`}>
+        <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">
+          <Thead>
+            <Th column="logger" title="Logger">Logger</Th>
+            <Th column="target_level" title="Level">Level</Th>
+            <Th column="timeout" title="Timeout">Timeout</Th>
+            <Th column="timeout_epoch" title="Expires At">Expires At</Th>
+            <Th column="action" title="Action">Action</Th>
+          </Thead>
+          {
+            _.map(_.keys(logLevelObj), (logKey, i) => {
+              return <Tr key={i}>
+                      <Td column="logger">
+                        <a href="javascript:void(0)">{logKey}</a>
+                      </Td>
+                      <Td column="target_level">{}
+                        <Select value={logLevelObj[logKey].target_level} options={traceOption} onChange={this.traceLavelChange.bind(this,logKey,'target_level',null)} required={true} clearable={false} />
+                      </Td>
+                      <Td column="timeout">
+                        <Editable ref={`logKey${i}`} inline={true} resolve={this.modifyCommonObjValue.bind(this,`logKey${i}`,logKey,'timeout','save',null)} reject={this.modifyCommonObjValue.bind(this,`logKey${i}`,logKey,'timeout','reject',null)}>
+                          <input className="form-control input-sm editInput"  ref={this.focusInput} defaultValue={logLevelObj[logKey].timeout} onChange={this.handleTimeChange.bind(this)}/>
+                        </Editable>
+                      </Td>
+                      <Td column="timeout_epoch">{this.getDateFormat(logLevelObj[logKey].timeout_epoch)}</Td>
+                      <Td column="action">
+                        <span>
+								<a href="javascript:void(0)" className="btn btn-success btn-xs" onClick={this.saveAndClearLogConfig.bind(this,logKey,'save')}><i className="fa fa-check"></i></a>&nbsp;
+								<a href="javascript:void(0)"  className="btn btn-danger btn-xs" onClick={this.saveAndClearLogConfig.bind(this,logKey,'clear')}><i className="fa fa-times"></i></a>
+								</span>
+                      </Td>
+                    </Tr>;
+            })
+          }
+          <Tr key={Math.random()}>
+            <Td  column="logger">
+              <Editable ref="addRowRef" inline={true} resolve={this.addLoggerName.bind(this,'addRowRef','save')} reject={this.addLoggerName.bind(this,"addRowRef",'reject')}>
+                <input className="form-control input-sm editInput"  ref={this.focusInput} defaultValue={selectedKeyName} onChange={this.handleNameChange.bind(this)}/>
+              </Editable>
+            </Td>
+            <Td  column="target_level">
+              <Select value={selectedTrace} options={traceOption} onChange={this.traceLavelChange.bind(this,null,'target_level','ADD')} required={true} clearable={false} />
+            </Td>
+            <Td  column="timeout">
+              <Editable ref={"timeoutRef"} inline={true} resolve={this.modifyCommonObjValue.bind(this,"timeoutRef",null,'timeout','save','ADD')} reject={this.modifyCommonObjValue.bind(this,"timeoutRef",null,'timeout','reject','ADD')}>
+                <input className="form-control input-sm editInput"  ref={this.focusInput} defaultValue={selectedTimeOut} onChange={this.handleTimeChange.bind(this)}/>
+              </Editable>
+            </Td>
+            <Td  column="timeout_epoch">&nbsp;</Td>
+              <Td column="action">
+                <span>
+                  <a href="javascript:void(0)" className="btn btn-primary btn-xs" onClick={this.addLogRow.bind(this,'save')}><i className="fa fa-check"></i></a>&nbsp;
+                  </span>
+              </Td>
+          </Tr>
+        </Table>
+      </div>
+    );
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e88526e7/contrib/views/storm/src/main/resources/ui/app/scripts/components/ProfilingView.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/ProfilingView.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/ProfilingView.jsx
new file mode 100644
index 0000000..eedf0dd
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/ProfilingView.jsx
@@ -0,0 +1,168 @@
+/**
+ 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 TopologyREST from '../rest/TopologyREST';
+import {
+  Table,
+  Thead,
+  Th,
+  Tr,
+  Td,
+  unsafe
+} from 'reactable';
+import {toastOpt,pageSize} from '../utils/Constants';
+import Utils from '../utils/Utils';
+import FSReactToastr from '../components/FSReactToastr';
+import CommonNotification from '../components/CommonNotification';
+import _ from 'lodash';
+
+export default class ProfilingView extends Component{
+  constructor(props){
+    super(props);
+    this.state = {
+      currentPage : 1,
+      executorArr : this.props.executorStats ? this.fetchData() : [],
+      selectedWorker : [],
+      selectAll : false,
+      warnMsg : false,
+      successMsg : false,
+      errorMsg : false
+    };
+  }
+
+  fetchData = () => {
+    const {executorStats} = this.props;
+    let data = {},executorArr=[];
+    _.map(executorStats, (o) => {
+      const hostPort = o.host + ":" + o.port;
+      if(!data[hostPort]){
+        data[hostPort] = {};
+      }
+      if(!data[hostPort].idArr){
+        data[hostPort].idArr = [];
+      }
+      data[hostPort].idArr.push(o.id);
+    });
+    let keys = this.hostPortArr = _.keys(data);
+    _.map(keys, (k) => {
+      executorArr.push({
+        hostPort: k,
+        executorId: data[k].idArr,
+        checked : false
+      });
+    });
+    return executorArr;
+  }
+
+  commonBtnAction = (actionType) => {
+    const {selectedWorker} = this.state;
+    selectedWorker.length ?  this.apiCallback(actionType) : this.setState({warnMsg : true,successMsg : false,errorMsg: false});
+  }
+
+  apiCallback = (actionType) => {
+    const {topologyId} = this.props;
+    const {selectedWorker} = this.state;
+    let promiseArr=[];
+    _.map(selectedWorker, (w) => {
+      promiseArr.push(TopologyREST.getProfiling(topologyId,actionType,w.hostPort));
+    });
+
+    Promise.all(promiseArr).then((results) => {
+      _.map(results, (r) => {
+        let tempErrorMsg= false,tempSuccessMsg=false;
+        if(r.errorMessage !== undefined){
+          tempErrorMsg = true;
+          tempSuccessMsg: false;
+        } else {
+          tempErrorMsg = false;
+          tempSuccessMsg: true;
+        }
+        this.setState({successMsg : tempSuccessMsg,errorMsg: tempErrorMsg,warnMsg : false});
+      });
+    });
+  }
+
+  handleChange = (hostPort) => {
+    let tempSelect = _.cloneDeep(this.state.selectAll);
+    let tempExecutor=_.cloneDeep(this.state.executorArr);
+    let tempWorker = _.cloneDeep(this.state.selectedWorker);
+    if(!!hostPort){
+      const ind = _.findIndex(tempExecutor, (e) => {return e.hostPort === hostPort; });
+      const index = _.findIndex(tempWorker,(t) => {return t.hostPort === hostPort;});
+      if(index === -1 && ind !== -1){
+        tempWorker.push(tempExecutor[ind]);
+      } else {
+        tempWorker.splice(index,1);
+      }
+      tempExecutor[ind].checked = !tempExecutor[ind].checked;
+    } else {
+      tempSelect = !this.state.selectAll;
+      _.map(tempExecutor,(t) => {
+        t.checked = tempSelect;
+      });
+      tempWorker = tempExecutor;
+    }
+    this.setState({selectedWorker : tempWorker,selectAll : tempSelect,executorArr :tempExecutor });
+  }
+
+  render(){
+    const {currentPage,executorArr,selectAll,warnMsg,successMsg,errorMsg} = this.state;
+    return(
+      <div>
+        <div className={`alert alert-warning alert-dismissible warning-msg ${warnMsg ? '' : 'hidden'}`}  role="alert">
+          <strong>Warning!</strong> Please select atleast one worker to perform operation.
+        </div>
+        <div className={`alert alert-success alert-dismissible success-msg ${successMsg ? '' : 'hidden'}`}  role="alert">
+          <strong>Success!</strong> Action performed successfully.
+        </div>
+        <div className={`alert alert-danger alert-dismissible error-msg ${errorMsg ? '' : 'hidden'}`}  role="alert">
+          <strong>Error!</strong> Error occured while performing the action.
+        </div>
+        <div className="clearfix">
+          <div className="btn-group btn-group-sm pull-right">
+            <button type="button" className="btn btn-primary" onClick={this.commonBtnAction.bind(this,'dumpjstack')}>JStack</button>
+            <button type="button" className="btn btn-primary" onClick={this.commonBtnAction.bind(this,'restartworker')}>Restart Worker</button>
+            <button type="button" className="btn btn-primary" onClick={this.commonBtnAction.bind(this,'dumpheap')}>Heap</button>
+          </div>
+        </div>
+        <hr />
+        <Table className="table table-bordered"  columns={currentPage-1} noDataText="No workers found !">
+          <Thead>
+            <Th column="checkbox">
+              <input type="checkbox" name="single" onChange={this.handleChange.bind(this,null)}/>
+            </Th>
+            <Th column="hostPort" >Host:Port</Th>
+            <Th column="executorId" >Executor Id</Th>
+          </Thead>
+          {
+            _.map(executorArr , (e,i) => {
+              return <Tr key={i}>
+                  <Td column="checkbox">
+                    <input type="checkbox" checked={e.checked} name="single" onChange={this.handleChange.bind(this,e.hostPort)}/>
+                  </Td>
+                  <Td column="hostPort">{e.hostPort}</Td>
+                  <Td column="executorId">{e.executorId.join(',')}</Td>
+              </Tr>;
+            })
+          }
+        </Table>
+      </div>
+    );
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e88526e7/contrib/views/storm/src/main/resources/ui/app/scripts/components/RadialChart.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/RadialChart.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/RadialChart.jsx
new file mode 100644
index 0000000..4c4e8fc
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/RadialChart.jsx
@@ -0,0 +1,134 @@
+/**
+ 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 RadialChart extends Component {
+  static propTypes = {
+    data: PropTypes.array.isRequired,
+    labels: PropTypes.array.isRequired,
+    width: PropTypes.number,
+    height: PropTypes.number,
+    innerRadius: PropTypes.number.isRequired,
+    outerRadius: PropTypes.number.isRequired,
+    color: PropTypes.array
+  }
+  constructor(props) {
+    super(props);
+    this.const = {
+      tau: 2 * Math.PI,
+      width: props.width || "44",
+      height: props.height || "52",
+      innerRadius: parseInt(props.innerRadius, 10) || 20,
+      outerRadius: parseInt(props.outerRadius, 10) || 25,
+      color: props.color || d3.scale.category20()
+    };
+    this.arc = d3.svg.arc()
+      .innerRadius(this.const.innerRadius)
+      .outerRadius(this.const.outerRadius)
+      .startAngle(0);
+  }
+  componentDidUpdate() {
+    this.animateGraph();
+  }
+  componentDidMount() {
+    const self = this;
+    this.tip = d3Tip()
+      .attr('class', 'd3-tip')
+      .offset([-10, 0])
+      .html(function() {
+        var text = "<div class='summary'>" + this.props.labels[0] + ": " + this.props.data[0] + "</div>";
+        text += "<div class='summary'>Free: " + (parseInt(this.props.data[1], 10) - parseInt(this.props.data[0], 10)) + "</div>";
+        text += "<div class='summary'>" + this.props.labels[1] + ": " + this.props.data[1] + "</div>";
+        return text;
+      }.bind(this));
+    var svg = this.svg = d3.select(ReactDOM.findDOMNode(this))
+      .attr('width', this.const.width + "px")
+      .attr('height', this.const.height + "px")
+      .append('g').attr('transform', 'translate(' + (this.const.width / 2) + ', ' + (this.const.height / 2) + ')');
+
+    this.text = svg.append("text")
+      .attr("y", "0.3em")
+      .attr("class", "graphVal")
+      .attr("text-anchor", "middle")
+      .attr("font-size", this.const.fontSize)
+      .on("mouseover", function(d){
+        self.tip.show(d, this);
+      })
+      .on("mouseout", function(d){
+        self.tip.hide(d, this);
+      })
+      .text("0");
+
+    var background = svg.append("path")
+      .datum({
+        endAngle: this.const.tau
+      })
+      .style("fill", this.const.color[0])
+      .attr("d", this.arc);
+
+    this.foreground = svg.append("path")
+      .datum({
+        endAngle: 0
+      })
+      .style("fill", function(d, i) {
+        return this.const.color[1];
+      }.bind(this))
+      .attr("d", this.arc);
+    this.svg.call(this.tip);
+    // $('#container').append($('body > .d3-tip'));
+    this.animateGraph();
+  }
+  animateGraph() {
+    var percent = (parseInt(this.props.data[0], 10) / parseInt(this.props.data[1], 10) * 100);
+    if (percent) {
+      percent = percent.toFixed(0) + ' %';
+    } else {
+      percent = '0 %';
+    }
+
+    d3.select(ReactDOM.findDOMNode(this)).select('.graphVal').text(percent);
+
+    var newValue = this.props.data[0] / this.props.data[1] * 100;
+    this.foreground.transition()
+      .duration(750)
+      .call(this._arcTween.bind(this), this.const.tau * (newValue / 100));
+  }
+  _arcTween(transition, newAngle) {
+    var arc = this.arc;
+    transition.attrTween("d", function(d) {
+      var interpolate = d3.interpolate(d.endAngle, newAngle);
+      return function(t) {
+        d.endAngle = interpolate(t);
+        if (!d.endAngle) {
+          d.endAngle = 0;
+        }
+        return arc(d);
+      };
+
+    });
+  }
+  render() {
+    return ( < svg className = "radial-chart" > < /svg>);
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e88526e7/contrib/views/storm/src/main/resources/ui/app/scripts/components/RebalanceTopology.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/RebalanceTopology.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/RebalanceTopology.jsx
new file mode 100644
index 0000000..43c7f78
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/RebalanceTopology.jsx
@@ -0,0 +1,152 @@
+/**
+ 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 TopologyREST from '../rest/TopologyREST';
+import {toastOpt,pageSize} from '../utils/Constants';
+import Utils from '../utils/Utils';
+import FSReactToastr from './FSReactToastr';
+import CommonNotification from './CommonNotification';
+import _  from 'lodash';
+
+export default class RebalanceTopology extends Component{
+  constructor(props){
+    super(props);
+    this.state = {
+      freeSlot : 0,
+      waitTime : 30,
+      rebalanceData : {}
+    };
+    this.fetchData();
+  }
+
+  fetchData = () => {
+    const {topologyExecutors,spoutArr,boltArr} = this.props;
+    TopologyREST.getSummary('cluster').then((result) => {
+      if(result.errorMessage !== undefined){
+        FSReactToastr.error(
+          <CommonNotification flag="error" content={result.errorMessage}/>, '', toastOpt);
+      } else {
+        let stateObj = {};
+        stateObj.freeSlot = result.slotsFree;
+        stateObj.rebalanceData={};
+        stateObj.rebalanceData.workers = topologyExecutors + stateObj.freeSlot;
+        this.totalWorker = stateObj.rebalanceData.workers;
+        _.map(spoutArr, (s) => {
+          stateObj.rebalanceData[s.spoutId] = s.executors;
+        });
+        _.map(boltArr, (b) => {
+          stateObj.rebalanceData[b.boltId] = b.executors;
+        });
+        this.setState(stateObj);
+      }
+    });
+  }
+
+  rebalanceValueChange = (type,e) => {
+    let data = _.cloneDeep(this.state.rebalanceData);
+    data[type] = +e.target.value;
+    this.setState({rebalanceData : data});
+  }
+
+  waitTimeChange = (e) => {
+    this.setState({waitTime : +e.target.value});
+  }
+
+  validateData = () => {
+    const {rebalanceData,waitTime} = this.state;
+    let errorFlag = [];
+    _.map(_.keys(rebalanceData), (key) => {
+      if(rebalanceData[key] === ''){
+        errorFlag.push(false);
+      }
+    });
+    if(waitTime === ''){
+      errorFlag.push(false);
+    }
+    return errorFlag.length ? false : true;
+  }
+
+  handleSave = () => {
+    const {rebalanceData,waitTime} = this.state;
+    const {topologyId} = this.props;
+    let finalData = {
+      "rebalanceOptions": {
+        "executors": {}
+      }
+    };
+    _.map(_.keys(rebalanceData), (key) => {
+      if(key === "workers"){
+        finalData.rebalanceOptions.numWorkers = rebalanceData[key];
+      } else {
+        finalData.rebalanceOptions.executors[key] = rebalanceData[key];
+      }
+    });
+
+    return TopologyREST.postActionOnTopology(topologyId,'rebalance',waitTime,{body : JSON.stringify(finalData)});
+  }
+
+  render(){
+    const {freeSlot,waitTime,rebalanceData} = this.state;
+    const {spoutArr,boltArr}= this.props;
+    return(
+      <div>
+        <div className="form-group row">
+          <div className="col-sm-3">
+            <label>Workers:<span className="text-danger">*</span></label>
+          </div>
+          <div className="col-sm-7">
+            <span style={{float : 'left'}}>0</span><input type="range" title={rebalanceData.workers +' workers selected.'} min={0} style={{width : '90%', float : 'left',margin : '6px 7px' }} max={this.totalWorker} onChange={this.rebalanceValueChange.bind(this,'workers')} /><span style={{float : 'left', clear : 'right'}}>{this.totalWorker}</span>
+          </div>
+        </div>
+        {
+          _.map(spoutArr, (s , i) => {
+            return  <div className="form-group row" key={i}>
+                      <div className="col-sm-3">
+                        <label>{s.spoutId}:<span className="text-danger">*</span></label>
+                      </div>
+                      <div className="col-sm-7">
+                        <input type="number" className="form-control" min={0} defaultValue={s.executors} onChange={this.rebalanceValueChange.bind(this,s.spoutId)} />
+                      </div>
+                    </div>;
+          })
+        }
+        {
+          _.map(boltArr, (b , n) => {
+            return  <div className="form-group row" key={n}>
+                      <div className="col-sm-3">
+                        <label>{b.boltId}:<span className="text-danger">*</span></label>
+                      </div>
+                      <div className="col-sm-7">
+                        <input type="number" className="form-control" min={0} defaultValue={b.executors} onChange={this.rebalanceValueChange.bind(this,b.boltId)} />
+                      </div>
+                    </div>;
+          })
+        }
+        <div className="form-group row">
+          <div className="col-sm-3">
+            <label>Wait Time:<span className="text-danger">*</span></label>
+          </div>
+          <div className="col-sm-7">
+            <input type="number" className="form-control" min={0} value={waitTime} onChange={this.waitTimeChange.bind(this)} />
+          </div>
+        </div>
+      </div>
+    );
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e88526e7/contrib/views/storm/src/main/resources/ui/app/scripts/components/SearchLogs.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/SearchLogs.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/SearchLogs.jsx
new file mode 100644
index 0000000..ebf1615
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/SearchLogs.jsx
@@ -0,0 +1,84 @@
+/**
+ 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 ReactDOM from 'react-dom';
+import {baseUrl} from '../utils/Constants';
+import {DropdownButton, FormGroup, Checkbox} from 'react-bootstrap';
+import fetch from 'isomorphic-fetch';
+
+export default class SearchLogs extends Component{
+  render() {
+    return (
+      <div className="col-md-3 pull-right searchbar">
+        <div className="input-group">
+          <input type="text" id="searchBox" className="form-control" placeholder="Search in Logs"/>
+          <div className="input-group-btn">
+            <div className="btn-group" role="group">
+              <div className="dropdown dropdown-lg">
+                <DropdownButton title="" pullRight id="bg-nested-dropdown">
+                  <FormGroup>
+                    <Checkbox id="searchArchivedLogs">Search archived logs</Checkbox>
+                  </FormGroup>
+                  <FormGroup>
+                    <Checkbox id="deepSearch">Deep search</Checkbox>
+                  </FormGroup>
+                </DropdownButton>
+              </div>
+              <button type="button" className="btn btn-default" onClick={this.handleSearch.bind(this)}>
+                <i className="fa fa-search"></i>
+              </button>
+            </div>
+          </div>
+        </div>
+      </div>
+    );
+  }
+  handleSearch(){
+    var searchBoxEl = document.getElementById('searchBox');
+    var searchArchivedLogsEl = document.getElementById('searchArchivedLogs');
+    var deepSearchEl = document.getElementById('deepSearch');
+    var topologyId = this.props.id;
+
+    fetch(baseUrl.replace('proxy?url=/api/v1/', 'storm_details'), {"credentials": "same-origin"})
+      .then((response) => {
+        return response.json();
+      })
+      .then((response) => {
+        var url = response.hostdata+'/';
+        if(deepSearchEl.checked == true){
+          url += "deep_search_result.html";
+        }else{
+          url += "search_result.html";
+        }
+        url += '?search='+searchBoxEl.value+'&id='+ topologyId +'&count=1';
+        if(searchArchivedLogsEl.checked == true){
+          if(deepSearchEl.checked == true){
+            url += "&search-archived=on";
+          }else{
+            url += "&searchArchived=checked";
+          }
+        }
+        window.open(url, '_blank');
+
+        searchBoxEl.value = '';
+        searchArchivedLogsEl.checked = false;
+        deepSearchEl.checked = false;
+      });
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e88526e7/contrib/views/storm/src/main/resources/ui/app/scripts/components/TopologyGraph.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/TopologyGraph.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/TopologyGraph.jsx
new file mode 100644
index 0000000..4cfc6bb
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/TopologyGraph.jsx
@@ -0,0 +1,208 @@
+/**
+ 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';
+import dagreD3 from 'dagre-d3/dist/dagre-d3';
+
+export default class TopologyGraph extends Component{
+  static propTypes = {
+    data: PropTypes.object.isRequired,
+    width: PropTypes.string,
+    height: PropTypes.string
+  }
+  constructor(props) {
+    super(props);
+    this.syncData(this.props.data);
+    this.updateFlag = true;
+  }
+  componentDidUpdate() {
+    if(!!this.updateFlag){
+      this.syncData(this.props.data);
+      this.updateGraph();
+    }
+  }
+  componentWillReceiveProps(nextProps){
+    _.isEqual(nextProps.data,this.props.data) ? this.updateFlag = false : this.updateFlag = true;
+  }
+  componentDidMount(){
+    var that = this;
+    this.svg = d3.select(ReactDOM.findDOMNode(this));
+    //Set up tooltip
+    this.tooltip = d3Tip()
+      .attr('class', function() {
+        return 'd3-tip testing';
+      })
+      .offset([-10, 0])
+      .html(function(data) {
+        var d = that.g.node(data);
+        var tip = "<ul>";
+        if (d[":capacity"] !== null){ tip += "<li>Capacity: " + d[":capacity"].toFixed(2) + "</li>";}
+        if (d[":latency"] !== null){ tip += "<li>Latency: " + d[":latency"].toFixed(2) + "</li>";}
+        if (d[":transferred"] !== null){ tip += "<li>Transferred: " + d[":transferred"].toFixed(2) + "</li>";}
+        tip += "</ul>";
+        return tip;
+      });
+      //Set up zoom
+    this.zoom = d3.behavior.zoom()
+      .scaleExtent([0, 8])
+      .on("zoom", this.zoomed.bind(this));
+  }
+  zoomed(){
+    this.inner.attr("transform",
+      "translate(" + this.zoom.translate() + ")" +
+      "scale(" + this.zoom.scale() + ")"
+    );
+  }
+  // update graph (called when needed)
+  updateGraph(){
+    var that = this;
+    var g = ReactDOM.findDOMNode(this).children[0];
+    if(g){
+      g.remove();
+    }
+    var inner = this.inner = this.svg.append("g");
+    // Create the renderer
+    var render = new dagreD3.render();
+    render.arrows().arrowPoint = (parent, id, edge, type) => {
+      var marker = parent.append("marker")
+        .attr("id", id)
+        .attr("viewBox", "0 0 10 10")
+        .attr("refX", 5)
+        .attr("refY", 5)
+        .attr("markerUnits", "strokeWidth")
+        .attr("markerWidth", 6)
+        .attr("markerHeight", 6.5)
+        .attr("orient", "auto");
+      var path = marker.append("path")
+        .attr("d", "M 0 0 L 10 5 L 0 10 z")
+        .style("stroke-width", 1)
+        .style("stroke-dasharray", "1,0")
+        .style("fill", "grey")
+        .style("stroke", "grey");
+      dagreD3.util.applyStyle(path, edge[type + "Style"]);
+    };
+
+    render.shapes().img = (parent, bbox, node) => {
+      var shapeSvg;
+      if(parent){
+        shapeSvg = parent.insert("image")
+          .attr("class", "nodeImage")
+          .attr("xlink:href", function(d) {
+            if (node) {
+              if(node.type === 'spout'){
+                return "styles/img/icon-spout.png";
+              } else if(node.type === 'bolt'){
+                return "styles/img/icon-bolt.png";
+              }
+            }
+          }).attr("x", "-12px")
+          .attr("y", "-12px")
+          .attr("width", "30px")
+          .attr("height", "30px");
+      }
+      node.intersect = function(point) {
+        return dagreD3.intersect.circle(node, 20, point);
+      };
+      return shapeSvg;
+    };
+    this.svg.call(this.zoom).call(this.tooltip);
+    // Run the renderer. This is what draws the final graph.
+    render(inner, this.g);
+
+    inner.selectAll("g.nodes image")
+      .on('mouseover', function(d) {
+        that.tooltip.show(d, this);
+      })
+      .on('mouseout', function(d) {
+        that.tooltip.hide(this);
+      });
+    inner.selectAll("g.nodes g.label")
+      .attr("transform", "translate(2,-30)");
+    // Center the graph
+    var initialScale = 1;
+    var svgWidth = this.svg[0][0].parentNode.clientWidth;
+    var svgHeight = this.svg[0][0].parentNode.clientHeight;
+    if(this.linkArray.length > 0){
+      this.zoom.translate([(svgWidth - this.g.graph().width * initialScale) / 2, (svgHeight - this.g.graph().height * initialScale) / 2])
+        .scale(initialScale)
+        .event(this.svg);
+    }
+  }
+  syncData(data){
+    this.nodeArray = [];
+    this.linkArray = [];
+    this.g = new dagreD3.graphlib.Graph().setGraph({
+      nodesep: 50,
+      ranksep: 190,
+      rankdir: "LR",
+      marginx: 20,
+      marginy: 20
+      // transition: function transition(selection) {
+      //     return selection.transition().duration(500);
+      // }
+    });
+    if(data){
+      var keys = _.keys(data);
+      keys.map(function(key){
+        if(!key.startsWith('__')){
+          data[key].id = key;
+          data[key].type = data[key][":type"];
+          this.nodeArray.push(data[key]);
+        }
+      }.bind(this));
+
+      var spoutObjArr = _.filter(this.nodeArray, { "type": "spout" });
+      if (spoutObjArr.length > 1) {
+        for(var i = 0; i < spoutObjArr.length; i++){
+          spoutObjArr[i].x = 50;
+          spoutObjArr[i].y = parseInt(i+'10', 10);
+          spoutObjArr[i].fixed = true;
+        }
+      } else if (spoutObjArr.length == 1) {
+        spoutObjArr[0].x = 50;
+        spoutObjArr[0].y = 100;
+        spoutObjArr[0].fixed = true;
+      }
+
+      this.nodeArray.map(function(node){
+        var inputArr = node[":inputs"] || [];
+        inputArr.map(function(input){
+          if(!input[":component"].startsWith("__")){
+            var sourceNode = _.find(this.nodeArray, {id: input[":component"]});
+            this.linkArray.push({
+              source: sourceNode,
+              target: node
+            });
+            this.g.setNode(sourceNode.id, _.extend(sourceNode, {label: sourceNode.id, shape: 'img'}));
+            this.g.setNode(node.id, _.extend(node, {label: node.id, shape: 'img'}));
+            this.g.setEdge(sourceNode.id, node.id, {"arrowhead": 'arrowPoint'});
+          }
+        }.bind(this));
+      }.bind(this));
+    }
+  }
+  render() {
+    return (
+      <svg className="topology-graph" width="100%" height="300"></svg>
+    );
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e88526e7/contrib/views/storm/src/main/resources/ui/app/scripts/containers/BaseContainer.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/containers/BaseContainer.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/containers/BaseContainer.jsx
new file mode 100644
index 0000000..62846b7
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/app/scripts/containers/BaseContainer.jsx
@@ -0,0 +1,50 @@
+/**
+ 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 Footer from '../components/Footer';
+import {Confirm} from '../components/FSModel';
+
+export default class BaseContainer extends Component {
+
+  constructor(props) {
+    super(props);
+  }
+
+  handleKeyPress = (event) => {
+    event.key === "Enter"
+      ? this.refs.Confirm.state.show
+        ? this.refs.Confirm.sure()
+        : ''
+    :event.key === "Escape"
+      ? this.refs.Confirm.state.show
+        ? this.refs.Confirm.cancel()
+        : ''
+    :'';
+  }
+
+  render() {
+    return (
+      <div className="container-fluid">
+        {this.props.children}
+        <Confirm ref="Confirm" onKeyUp={this.handleKeyPress}/>
+        <Footer />
+      </div>
+    );
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e88526e7/contrib/views/storm/src/main/resources/ui/app/scripts/containers/ClusterSummary.jsx
----------------------------------------------------------------------
diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/containers/ClusterSummary.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/containers/ClusterSummary.jsx
new file mode 100644
index 0000000..904ed68
--- /dev/null
+++ b/contrib/views/storm/src/main/resources/ui/app/scripts/containers/ClusterSummary.jsx
@@ -0,0 +1,125 @@
+/**
+ 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 RadialChart  from '../components/RadialChart';
+import FSReactToastr from '../components/FSReactToastr';
+import {toastOpt} from '../utils/Constants';
+import TopologyREST from '../rest/TopologyREST';
+import NimbusSummary from './NimbusSummary';
+import CommonNotification from '../components/CommonNotification';
+import {OverlayTrigger, Tooltip} from 'react-bootstrap';
+
+export default class ClusterSummary extends Component{
+  constructor(props){
+    super(props);
+    this.fetchData();
+    this.state = {
+      entity :{}
+    };
+  }
+
+  fetchData = () => {
+    TopologyREST.getSummary('cluster').then((result) => {
+      if(result.errorMessage !== undefined){
+        FSReactToastr.error(
+          <CommonNotification flag="error" content={result.errorMessage}/>, '', toastOpt);
+      } else {
+        this.setState({entity : result});
+      }
+    });
+  }
+  render(){
+    const {entity} = this.state;
+    return(
+      <div>
+        <div className="row">
+          <div className="col-sm-6">
+            <OverlayTrigger placement="bottom" overlay={<Tooltip id="tooltip1">Executors are threads in a Worker process.</Tooltip>}>
+              <div className="tile primary">
+                  <div className="tile-header">Executor</div>
+                  <div className="tile-body">
+                      <i className="fa fa-play-circle-o"></i>
+                      <span className="count">{entity.executorsTotal}</span>
+                  </div>
+              </div>
+            </OverlayTrigger>
+          </div>
+          <div className="col-sm-6">
+            <OverlayTrigger placement="bottom" overlay={<Tooltip id="tooltip1">A Task is an instance of a Bolt or Spout. The number of Tasks is almost always equal to the number of Executors.</Tooltip>}>
+              <div className="tile warning">
+                  <div className="tile-header">Tasks</div>
+                  <div className="tile-body">
+                      <i className="fa fa-tasks"></i>
+                      <span className="count">{entity.tasksTotal}</span>
+                  </div>
+              </div>
+            </OverlayTrigger>
+          </div>
+      </div>
+      <div className="row">
+            <div className="col-sm-6">
+              <OverlayTrigger placement="bottom" overlay={<Tooltip id="tooltip1">The number of nodes in the cluster currently.</Tooltip>}>
+                <div className="tile success">
+                    <div className="tile-header" style={{textAlign:"center"}}>Supervisor</div>
+                    <div className="tile-body" style={{textAlign:"center"}}>
+                        <div id="supervisorCount">
+                          <RadialChart
+                            data={[entity.supervisors,entity.supervisors]}
+                            labels={['Used','Total']}
+                            width={100}
+                            height={100}
+                            innerRadius={46}
+                            outerRadius={50}
+                            color={["rgba(255,255,255,0.6)", "rgba(255,255,255,1)"]}
+                          />
+                        </div>
+                    </div>
+                </div>
+              </OverlayTrigger>
+            </div>
+            <div className="col-sm-6">
+              <OverlayTrigger placement="bottom" overlay={<Tooltip id="tooltip1">Slots are Workers (processes).</Tooltip>}>
+                <div className="tile danger">
+                    <div className="tile-header" style={{textAlign:"center"}}>Slots</div>
+                    <div className="tile-body" style={{textAlign:"center"}}>
+                        <div id="slotsCount">
+                          <RadialChart
+                            data={[entity.slotsUsed,entity.slotsTotal]}
+                            labels={['Used','Total']}
+                            width={100}
+                            height={100}
+                            innerRadius={46}
+                            outerRadius={50}
+                            color={["rgba(255,255,255,0.6)", "rgba(255,255,255,1)"]}
+                          />
+                        </div>
+                    </div>
+                </div>
+              </OverlayTrigger>
+            </div>
+        </div>
+        <div className="row">
+          <div className="col-sm-12">
+            <NimbusSummary fromDashboard={true}/>
+          </div>
+        </div>
+      </div>
+    );
+  }
+}


Mime
View raw message