From 60a3ce8ebb42cbbe32bf525654daee28ff999ebc Mon Sep 17 00:00:00 2001 From: "bingshen.wbs" Date: Fri, 29 Mar 2024 10:15:45 +0800 Subject: [PATCH] support source&destination ping Signed-off-by: bingshen.wbs --- pkg/controller/service/pingmesh.go | 23 ++++-- .../pages/pingmesh/pingForm/index.module.css | 15 ++++ webui/src/pages/pingmesh/pingForm/index.tsx | 81 +++++++++++++++---- .../pingmesh/pingForm/selectorDialog.tsx | 56 +++++++++---- webui/src/pages/pingmesh/pingGraph/index.tsx | 49 ++++++++--- webui/src/services/pingmesh.ts | 1 + 6 files changed, 179 insertions(+), 46 deletions(-) diff --git a/pkg/controller/service/pingmesh.go b/pkg/controller/service/pingmesh.go index 506f926c..1344f005 100644 --- a/pkg/controller/service/pingmesh.go +++ b/pkg/controller/service/pingmesh.go @@ -3,6 +3,7 @@ package service import ( "context" "fmt" + "reflect" "strconv" "sync" "time" @@ -27,7 +28,8 @@ type Latency struct { } type PingMeshArgs struct { - PingMeshList []NodeInfo `json:"ping_mesh_list"` + PingMeshSourceList []NodeInfo `json:"ping_mesh_source_list"` + PingMeshList []NodeInfo `json:"ping_mesh_list"` } type PingMeshResult struct { @@ -52,6 +54,8 @@ func (c *controller) dispatchPingTask(ctx context.Context, src, dst NodeInfo, ta if err != nil { return err } + case "IP": + return fmt.Errorf("not support ip as source") } switch dst.Type { case "Pod": @@ -64,6 +68,8 @@ func (c *controller) dispatchPingTask(ctx context.Context, src, dst NodeInfo, ta if err != nil { return err } + case "IP": + pingInfo.Destination = dst.Name } _, err = c.commitTask(src.Nodename, &rpc.Task{ @@ -113,20 +119,25 @@ func (c *controller) PingMesh(ctx context.Context, pingmesh *PingMeshArgs) (*Pin taskGroup := sync.WaitGroup{} timeoutCtx, cancel := context.WithTimeout(ctx, time.Second*10) defer cancel() - latencyResult := make(chan *Latency, len(pingmesh.PingMeshList)*len(pingmesh.PingMeshList)) + latencyResult := make(chan *Latency, len(pingmesh.PingMeshSourceList)*len(pingmesh.PingMeshList)) pingResult := &PingMeshResult{} var err error - for sidx, src := range pingmesh.PingMeshList { - pingResult.Nodes = append(pingResult.Nodes, src) - for didx, dst := range pingmesh.PingMeshList { - if sidx == didx { + NodeSet := make(map[NodeInfo]interface{}) + for _, src := range pingmesh.PingMeshSourceList { + NodeSet[src] = struct{}{} + for _, dst := range pingmesh.PingMeshList { + if reflect.DeepEqual(src, dst) { continue } + NodeSet[dst] = struct{}{} if err = c.dispatchPingTask(timeoutCtx, src, dst, &taskGroup, latencyResult); err != nil { log.Errorf("dispatch ping task error: %v", err) } } } + for node := range NodeSet { + pingResult.Nodes = append(pingResult.Nodes, node) + } taskGroup.Wait() close(latencyResult) for l := range latencyResult { diff --git a/webui/src/pages/pingmesh/pingForm/index.module.css b/webui/src/pages/pingmesh/pingForm/index.module.css index 038044cd..6e165042 100644 --- a/webui/src/pages/pingmesh/pingForm/index.module.css +++ b/webui/src/pages/pingmesh/pingForm/index.module.css @@ -1,10 +1,25 @@ .btn { margin-left: 5px; + margin-top: 3px; +} + +.directionSel { + font-size: 30px; + height: 30px; + margin-top: 20px; + display: inline-block; + position: absolute; } .custom { border: 1px dashed; padding: 4px; + height: 80px; + display: inline-block; +} + +.selectorGroup { + margin-left: 150px; display: inline-block; } diff --git a/webui/src/pages/pingmesh/pingForm/index.tsx b/webui/src/pages/pingmesh/pingForm/index.tsx index 029a8cd1..11f1b7b0 100644 --- a/webui/src/pages/pingmesh/pingForm/index.tsx +++ b/webui/src/pages/pingmesh/pingForm/index.tsx @@ -10,55 +10,102 @@ interface PingFormProps { const PingForm: React.FunctionComponent = (props: PingFormProps) => { const { onSubmit } = props; + const [showSourceSelectorDialog, setShowSourceSelectorDialog] = useState(false) const [showSelectorDialog, setShowSelectorDialog] = useState(false) + const [pingMeshSourceList, setPingMeshSourceList] = useState([]) const [pingMeshList, setPingMeshList] = useState([]) const handleSubmit = (values: PingMeshArgs, errors: any) => { if (errors) { return } - if (pingMeshList.length < 2) { - Message.error("You have to select at least two targets.") + if (pingMeshList.length == 0 || pingMeshSourceList.length == 0) { + Message.error("You have to select src and dst object to detect the latency") return } + values.ping_mesh_source_list = pingMeshSourceList values.ping_mesh_list = pingMeshList onSubmit(values); }; return (
- +
- {pingMeshList.map((v, i) => { - if (v.type == "Node") { - return ; - } else { - return ; - } - })} - - - + {pingMeshSourceList.map((v, i) => { + if (v.type == "Node" || v.type == "IP") { + return ; + } else { + return ; + } + })} +
+ + { let toAdd = [] skip: for (const v of value.values()) { - for (const c of pingMeshList.values()) { + for (const c of pingMeshSourceList.values()) { if (v.name == c.name) { continue skip } } toAdd = [...toAdd, v] } - setPingMeshList([...pingMeshList, ...toAdd]) - setShowSelectorDialog(!showSelectorDialog) + setPingMeshSourceList([...pingMeshSourceList, ...toAdd]) + setShowSourceSelectorDialog(!showSourceSelectorDialog) }} - onClose={() => { setShowSelectorDialog(!showSelectorDialog) }}> + onClose={() => { setShowSourceSelectorDialog(!showSourceSelectorDialog) }}> +
+
+ + Destination: +
+
+ + +
+ {pingMeshList.map((v, i) => { + if (v.type == "Node" || v.type == "IP") { + return ; + } else { + return ; + } + })} + + { + let toAdd = [] + skip: for (const v of value.values()) { + for (const c of pingMeshList.values()) { + if (v.name == c.name) { + continue skip + } + } + toAdd = [...toAdd, v] + } + setPingMeshList([...pingMeshList, ...toAdd]) + setShowSelectorDialog(!showSelectorDialog) + }} + onClose={() => { setShowSelectorDialog(!showSelectorDialog) }}> +
+
+

diff --git a/webui/src/pages/pingmesh/pingForm/selectorDialog.tsx b/webui/src/pages/pingmesh/pingForm/selectorDialog.tsx index 37f4281a..d3e493aa 100644 --- a/webui/src/pages/pingmesh/pingForm/selectorDialog.tsx +++ b/webui/src/pages/pingmesh/pingForm/selectorDialog.tsx @@ -10,6 +10,7 @@ interface SelectorProps { podList: PodInfo[]; nodeList: NodeInfo[]; visible: boolean; + displayIPSelector: boolean onClose: () => void } @@ -24,6 +25,8 @@ const SelectorDialog: React.FunctionComponent = (props: SelectorP const [labelSelectorValues, setLabelSelectorValues] = useState([]) const [labelSelectorValue, setLabelSelectorValue] = useState("") const [formNamespace, setFormNamespace] = useState("") + const [ipAddress, setIPAddress] = useState("") + const [ipAddressCheckState, setIPAddressCheckState] = useState("error") const filterCaptureObject = (type, ns) => { if (type == "Node") { @@ -123,10 +126,22 @@ const SelectorDialog: React.FunctionComponent = (props: SelectorP } }).filter(item => item))] } + } else if (formCaptureType == "IP") { + setFormCaptureType("Pod") + return [ + { + name: ipAddress, + type: "IP" + } + ] } return [] } + const isPodOrNode = (type) => { + return type == "Pod" || type == "Node" + } + return ( = (props: SelectorP footerActions={['ok']} visible={props.visible} onClose={props.onClose} - onOk={() => props.submitSelector(selectedResult())} + onOk={() => {!(formCaptureType=="IP"&&ipAddressCheckState!="success") && props.submitSelector(selectedResult())}} > = (props: SelectorP { setFormCaptureType(value); setFormNamespace(""); setFormName(""); filterCaptureObject(value) }} + onChange={(value) => {setFormCaptureType(value); setFormNamespace(""); setFormName(""); filterCaptureObject(value) }} > Node Pod + {props.displayIPSelector && + IP + } - - { setCaptureSelectorType(value); setFormNamespace(""); setFormName(""); filterCaptureObject(formCaptureType) }} - > - Namespace & Name - Label Selector - - + {isPodOrNode(formCaptureType) && + + { setCaptureSelectorType(value); setFormNamespace(""); setFormName(""); filterCaptureObject(formCaptureType) }} + > + Namespace & Name + Label Selector + + + } + {!isPodOrNode(formCaptureType) && + + {setIPAddressCheckState(/^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/.test(value) ? "success" : "error");setIPAddress(value)}} + state={ipAddressCheckState} + placeholder="Input IP Address, eg: 1.1.1.1" + /> + + } {formCaptureType == "Pod" && @@ -172,7 +200,7 @@ const SelectorDialog: React.FunctionComponent = (props: SelectorP } - {captureSelectorType == "Selector" && + {isPodOrNode(formCaptureType) && captureSelectorType == "Selector" && { case 'Node': id = `${node.type}/${node.name}` break + case 'IP': + id = `${node.type}/${node.name}` + break } return id } @@ -35,12 +40,26 @@ const toGraphData = (data: any): GraphData => { case 'Node': label = item.name break + case 'IP': + label = item.name + break } let id = `${item.type}/${label}` + let group = "" + for (let lat in data.latencies) { + if(id == nodeID(data.latencies[lat].source)) { + group = "SRC" + break + } else if (id == nodeID(data.latencies[lat].destination)) { + group = "DST" + break + } + } return { id: id, name: id, label: label, + group: group, ...item, } }); @@ -55,7 +74,6 @@ const toGraphData = (data: any): GraphData => { curvature: 0.3, } }); - return { nodes, links, @@ -68,28 +86,36 @@ const PingGraph: React.FC = (props: PingGraphProps): JSX.Element const graphData = data ? toGraphData(data) : null useEffect(() => { const fg = ref.current; - fg.d3Force('link').distance(link => 100); + if(fg == null){ + return + } + fg.d3Force('link').distance(link => 200); + + fg.d3Force('x', d3.forceX().x(node => node.targetX || 0).strength(0.2)); + fg.d3Force('y', d3.forceY().y(node => 0).strength(0.2)); + fg.d3Force('charge', d3.forceManyBody().strength(-100)) + }, []); return (
{ node.fx = node.x; @@ -98,6 +124,11 @@ const PingGraph: React.FC = (props: PingGraphProps): JSX.Element nodeLabel={(node) => node.name} nodeCanvasObjectMode={() => 'after'} nodeCanvasObject={(node, ctx, globalScale) => { + if(node.group === "SRC") { + node.targetX = -60; + } else if(node.group === "DST") { + node.targetX = 60; + } const label = node.name; const fontSize = 12 / globalScale; ctx.font = `${fontSize}px Sans-Serif`; diff --git a/webui/src/services/pingmesh.ts b/webui/src/services/pingmesh.ts index 42366afc..8b975797 100644 --- a/webui/src/services/pingmesh.ts +++ b/webui/src/services/pingmesh.ts @@ -21,6 +21,7 @@ export interface PingMeshLatency { } export interface PingMeshArgs { + ping_mesh_source_list: NodeInfo[] ping_mesh_list: NodeInfo[] }