diff --git a/src/views/dashboard/related/Topology.vue b/src/views/dashboard/related/topology/Graph.vue similarity index 100% rename from src/views/dashboard/related/Topology.vue rename to src/views/dashboard/related/topology/Graph.vue diff --git a/src/views/dashboard/related/topology/utils/icons.js b/src/views/dashboard/related/topology/utils/icons.js new file mode 100755 index 00000000..8c669f60 --- /dev/null +++ b/src/views/dashboard/related/topology/utils/icons.js @@ -0,0 +1,34 @@ +/** + * 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. + */ +const requireComponent = require.context("../../assets", false, /\.png$/); + +const result = {}; +function capitalizeFirstLetter(str) { + return str.toUpperCase(); +} +function validateFileName(str) { + return ( + /^\S+\.png$/.test(str) && + str.replace(/^\S+\/(\w+)\.png$/, (rs, $1) => capitalizeFirstLetter($1)) + ); +} +requireComponent.keys().forEach((filePath) => { + const componentConfig = requireComponent(filePath); + const fileName = validateFileName(filePath); + result[fileName] = componentConfig; +}); +export default result; diff --git a/src/views/dashboard/related/topology/utils/legend.js b/src/views/dashboard/related/topology/utils/legend.js new file mode 100644 index 00000000..7c075674 --- /dev/null +++ b/src/views/dashboard/related/topology/utils/legend.js @@ -0,0 +1,42 @@ +/** + * 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 icons from "./icons"; + +export default function topoLegend(graph, clientHeight, clientWidth) { + for (const item of ["CUBE", "CUBEERROR"]) { + graph + .append("image") + .attr("width", 30) + .attr("height", 30) + .attr("x", clientWidth - (item === "CUBEERROR" ? 340 : 440)) + .attr("y", clientHeight - 50) + .attr("xlink:href", () => + item === "CUBEERROR" ? icons.CUBEERROR : icons.CUBE + ); + graph + .append("text") + .attr("x", clientWidth - (item === "CUBEERROR" ? 310 : 410)) + .attr("y", clientHeight - 30) + .text(() => { + return item === "CUBEERROR" + ? "Unhealthy (Successful Rate < 95% and Traffic > 1 call/min)" + : "Healthy"; + }) + .style("fill", "#efeff1") + .style("font-size", "11px"); + } +} diff --git a/src/views/dashboard/related/topology/utils/linkElement.js b/src/views/dashboard/related/topology/utils/linkElement.js new file mode 100644 index 00000000..c56c14d7 --- /dev/null +++ b/src/views/dashboard/related/topology/utils/linkElement.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. + */ +export const linkElement = (graph) => { + const linkEnter = graph + .append("path") + .attr("class", "topo-line") + .attr("stroke", (d) => (d.cpm ? "#217EF25f" : "#6a6d7777")); + return linkEnter; +}; +export const anchorElement = (graph, funcs, tip) => { + const linkEnter = graph + .append("circle") + .attr("class", "topo-line-anchor") + .attr("r", 5) + .attr("fill", (d) => (d.cpm ? "#217EF25f" : "#6a6d7777")) + .on("mouseover", function (d) { + tip.html(funcs.$tip).show(d, this); + }) + .on("mouseout", function () { + tip.hide(this); + }) + .on("click", (d) => { + funcs.handleLinkClick(d); + }); + return linkEnter; +}; diff --git a/src/views/dashboard/related/topology/utils/nodeElement.js b/src/views/dashboard/related/topology/utils/nodeElement.js new file mode 100644 index 00000000..c81136f8 --- /dev/null +++ b/src/views/dashboard/related/topology/utils/nodeElement.js @@ -0,0 +1,86 @@ +/** + * 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 icons from "./icons"; + +icons["KAFKA-CONSUMER"] = icons.KAFKA; +export default (d3, graph, tool, funcs, tip) => { + const nodeEnter = graph + .append("g") + .call( + d3 + .drag() + .on("start", funcs.dragstart) + .on("drag", funcs.dragged) + .on("end", funcs.dragended) + ) + .on("mouseover", function (d) { + tip.html((data) => `
${data.name}
`).show(d, this); + }) + .on("mouseout", function () { + tip.hide(this); + }) + .on("click", (d) => { + event.stopPropagation(); + event.preventDefault(); + tool.attr("style", "display: none"); + funcs.handleNodeClick(d); + if (d.isReal) { + tool + .attr("transform", `translate(${d.x},${d.y - 20})`) + .attr("style", "display: block"); + } + }); + nodeEnter + .append("image") + .attr("width", 49) + .attr("height", 49) + .attr("x", 2) + .attr("y", 10) + .attr("style", "cursor: move;") + .attr("xlink:href", (d) => + d.sla < 95 && d.isReal && d.cpm > 1 ? icons.CUBEERROR : icons.CUBE + ); + nodeEnter + .append("image") + .attr("width", 32) + .attr("height", 32) + .attr("x", 6) + .attr("y", -10) + .attr("style", "opacity: 0.5;") + .attr("xlink:href", icons.LOCAL); + nodeEnter + .append("image") + .attr("width", 18) + .attr("height", 18) + .attr("x", 13) + .attr("y", -7) + .attr("xlink:href", (d) => + !d.type || d.type === "N/A" + ? icons.UNDEFINED + : icons[d.type.toUpperCase().replace("-", "")] + ); + nodeEnter + .append("text") + .attr("class", "topo-text") + .attr("text-anchor", "middle") + .attr("x", 22) + .attr("y", 70) + .text((d) => + d.name.length > 20 ? `${d.name.substring(0, 20)}...` : d.name + ); + return nodeEnter; +}; diff --git a/src/views/dashboard/related/topology/utils/simulation.js b/src/views/dashboard/related/topology/utils/simulation.js new file mode 100644 index 00000000..58f37b4d --- /dev/null +++ b/src/views/dashboard/related/topology/utils/simulation.js @@ -0,0 +1,51 @@ +/** + * 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. + */ +export const simulationInit = (d3, data_nodes, data_links, ticked) => { + const simulation = d3 + .forceSimulation(data_nodes) + .force( + "collide", + d3.forceCollide().radius(() => 60) + ) + .force("yPos", d3.forceY().strength(1)) + .force("xPos", d3.forceX().strength(1)) + .force("charge", d3.forceManyBody().strength(-520)) + .force( + "link", + d3.forceLink(data_links).id((d) => d.id) + ) + .force( + "center", + d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2 - 20) + ) + .on("tick", ticked) + .stop(); + simulationSkip(d3, simulation, ticked); + return simulation; +}; + +export const simulationSkip = (d3, simulation, ticked) => { + d3.timeout(() => { + const n = Math.ceil( + Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay()) + ); + for (let i = 0; i < n; i += 1) { + simulation.tick(); + ticked(); + } + }); +}; diff --git a/src/views/dashboard/related/topology/utils/tool.js b/src/views/dashboard/related/topology/utils/tool.js new file mode 100644 index 00000000..98a9f81c --- /dev/null +++ b/src/views/dashboard/related/topology/utils/tool.js @@ -0,0 +1,70 @@ +/** + * 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. + */ +const requireComponent = require.context("./tool", false, /\.png$/); + +const icons = {}; +function capitalizeFirstLetter(str) { + return str.toUpperCase(); +} +function validateFileName(str) { + return ( + /^\S+\.png$/.test(str) && + str.replace(/^\S+\/(\w+)\.png$/, (rs, $1) => capitalizeFirstLetter($1)) + ); +} +requireComponent.keys().forEach((filePath) => { + const componentConfig = requireComponent(filePath); + const fileName = validateFileName(filePath); + icons[fileName] = componentConfig; +}); + +const Hexagon = (side, r, cx, cy) => { + let path = ""; + for (let i = 0; i < side; i += 1) { + let x = Math.cos(((2 / side) * i + 1 / side) * Math.PI) * r + cx; + let y = -Math.sin(((2 / side) * i + 1 / side) * Math.PI) * r + cy; + path += !i ? `M${x},${y} ` : `L${x},${y} `; + if (i == side - 1) path += "Z"; + } + return path; +}; + +export default (graph, data) => { + const tool = graph.append("g").attr("class", "topo-tool"); + const side = 6; + for (let i = 0; i < data.length; i += 1) { + let x = Math.cos((2 / side) * i * Math.PI) * 34; + let y = -Math.sin((2 / side) * i * Math.PI) * 34; + const tool_g = tool + .append("g") + .attr("class", "topo-tool-i") + .on("click", data[i].click); + tool_g + .append("path") + .attr("class", "tool-hexagon") + .attr("d", Hexagon(6, 17, x, y)); + tool_g + .append("svg:image") + .attr("width", 14) + .attr("height", 14) + .attr("x", x - 7) + .attr("y", y - 7) + .attr("style", "opacity: 0.8") + .attr("xlink:href", icons[data[i].icon]); + } + return tool; +}; diff --git a/src/views/dashboard/related/topology/utils/tool/ALARM.png b/src/views/dashboard/related/topology/utils/tool/ALARM.png new file mode 100644 index 00000000..85009cb2 Binary files /dev/null and b/src/views/dashboard/related/topology/utils/tool/ALARM.png differ diff --git a/src/views/dashboard/related/topology/utils/tool/API.png b/src/views/dashboard/related/topology/utils/tool/API.png new file mode 100644 index 00000000..385dcaf6 Binary files /dev/null and b/src/views/dashboard/related/topology/utils/tool/API.png differ diff --git a/src/views/dashboard/related/topology/utils/tool/ENDPOINT.png b/src/views/dashboard/related/topology/utils/tool/ENDPOINT.png new file mode 100644 index 00000000..de26135f Binary files /dev/null and b/src/views/dashboard/related/topology/utils/tool/ENDPOINT.png differ diff --git a/src/views/dashboard/related/topology/utils/tool/INSTANCE.png b/src/views/dashboard/related/topology/utils/tool/INSTANCE.png new file mode 100644 index 00000000..f0f1dbff Binary files /dev/null and b/src/views/dashboard/related/topology/utils/tool/INSTANCE.png differ diff --git a/src/views/dashboard/related/topology/utils/tool/TRACE.png b/src/views/dashboard/related/topology/utils/tool/TRACE.png new file mode 100644 index 00000000..2fe870f0 Binary files /dev/null and b/src/views/dashboard/related/topology/utils/tool/TRACE.png differ diff --git a/src/views/dashboard/related/topology/utils/zoom.js b/src/views/dashboard/related/topology/utils/zoom.js new file mode 100644 index 00000000..f7046881 --- /dev/null +++ b/src/views/dashboard/related/topology/utils/zoom.js @@ -0,0 +1,26 @@ +/** + * 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. + */ +export default (d3, graph) => + d3 + .zoom() + .scaleExtent([0.3, 10]) + .on("zoom", () => { + graph.attr( + "transform", + `translate(${d3.event.transform.x},${d3.event.transform.y})scale(${d3.event.transform.k})` + ); + });