Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 365AF200D53 for ; Mon, 30 Oct 2017 23:35:35 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id 35414160C02; Mon, 30 Oct 2017 22:35:35 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 7C8AF160C07 for ; Mon, 30 Oct 2017 23:35:32 +0100 (CET) Received: (qmail 29990 invoked by uid 500); 30 Oct 2017 22:35:31 -0000 Mailing-List: contact commits-help@ambari.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: ambari-dev@ambari.apache.org Delivered-To: mailing list commits@ambari.apache.org Received: (qmail 29766 invoked by uid 99); 30 Oct 2017 22:35:31 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 30 Oct 2017 22:35:31 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id A1ECFE0779; Mon, 30 Oct 2017 22:35:27 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: mradhakrishnan@apache.org To: commits@ambari.apache.org Date: Mon, 30 Oct 2017 22:35:35 -0000 Message-Id: In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [09/56] [abbrv] ambari git commit: AMBARI-21955. Update React version to 15.6.2 to get MIT license. (Sanket Shah via yusaku) archived-at: Mon, 30 Oct 2017 22:35:35 -0000 http://git-wip-us.apache.org/repos/asf/ambari/blob/5bdede50/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonWindowPanel.jsx ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonWindowPanel.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonWindowPanel.jsx new file mode 100644 index 0000000..0f8130f --- /dev/null +++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonWindowPanel.jsx @@ -0,0 +1,99 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at +* + http://www.apache.org/licenses/LICENSE-2.0 +* + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +**/ + +import React, {Component} from 'react'; +import Select from 'react-select'; +import CommonSwitchComponent from './CommonSwitchComponent'; +import {OverlayTrigger, Tooltip} from 'react-bootstrap'; + +export default class CommonWindowPanel extends Component{ + constructor(props){ + super(props); + } + + windowChange = (obj) => { + this.props.handleWindowChange(obj); + } + + commonToggleChange = (params) => { + this.props.toggleSystem(params); + } + + commonTopologyActionHandler = (action) => { + this.props.handleTopologyAction(action); + } + + render(){ + const {selectedWindowKey,windowOptions,systemFlag,debugFlag,handleLogLevel,topologyStatus,KYC,handleProfiling} = this.props; + return( +
+ +
+ + + + + + + + {this.getDateFormat(logLevelObj[logKey].timeout_epoch)} + + +   + + + + ; + }) + } + + + + + + + + + + +   + + +   + + + + +
+ ); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/5bdede50/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( +
+
+ Warning! Please select atleast one worker to perform operation. +
+
+ Success! Action performed successfully. +
+
+ Error! Error occured while performing the action. +
+
+
+ + + +
+
+
+ + + + + + + { + _.map(executorArr , (e,i) => { + return + + + + ; + }) + } +
+ + Host:PortExecutor Id
+ + {e.hostPort}{e.executorId.join(',')}
+
+ ); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/5bdede50/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 = "
" + this.props.labels[0] + ": " + this.props.data[0] + "
"; + text += "
Free: " + (parseInt(this.props.data[1], 10) - parseInt(this.props.data[0], 10)) + "
"; + text += "
" + this.props.labels[1] + ": " + this.props.data[1] + "
"; + 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/5bdede50/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( + , '', 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( +
+
+
+ +
+
+ 0{this.totalWorker} +
+
+ { + _.map(spoutArr, (s , i) => { + return
+
+ +
+
+ +
+
; + }) + } + { + _.map(boltArr, (b , n) => { + return
+
+ +
+
+ +
+
; + }) + } +
+
+ +
+
+ +
+
+
+ ); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/5bdede50/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 ( +
+
+ +
+
+
+ + + Search archived logs + + + Deep search + + +
+ +
+
+
+
+ ); + } + 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/5bdede50/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 = "
    "; + if (d[":capacity"] !== null){ tip += "
  • Capacity: " + d[":capacity"].toFixed(2) + "
  • ";} + if (d[":latency"] !== null){ tip += "
  • Latency: " + d[":latency"].toFixed(2) + "
  • ";} + if (d[":transferred"] !== null){ tip += "
  • Transferred: " + d[":transferred"].toFixed(2) + "
  • ";} + tip += "
"; + 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 ( + + ); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/5bdede50/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 ( +
+ {this.props.children} + +
+
+ ); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/5bdede50/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( + , '', toastOpt); + } else { + this.setState({entity : result}); + } + }); + } + render(){ + const {entity} = this.state; + return( +
+
+
+ Executors are threads in a Worker process.}> +
+
Executor
+
+ + {entity.executorsTotal} +
+
+
+
+
+ A Task is an instance of a Bolt or Spout. The number of Tasks is almost always equal to the number of Executors.}> +
+
Tasks
+
+ + {entity.tasksTotal} +
+
+
+
+
+
+
+ The number of nodes in the cluster currently.}> +
+
Supervisor
+
+
+ +
+
+
+
+
+
+ Slots are Workers (processes).}> +
+
Slots
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+ ); + } +}