Skip to content

Commit

Permalink
support source&destination ping
Browse files Browse the repository at this point in the history
Signed-off-by: bingshen.wbs <[email protected]>
  • Loading branch information
BSWANG committed Apr 2, 2024
1 parent 4321cac commit 60a3ce8
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 46 deletions.
23 changes: 17 additions & 6 deletions pkg/controller/service/pingmesh.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package service
import (
"context"
"fmt"
"reflect"
"strconv"
"sync"
"time"
Expand All @@ -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 {
Expand All @@ -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":
Expand All @@ -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{
Expand Down Expand Up @@ -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 {
Expand Down
15 changes: 15 additions & 0 deletions webui/src/pages/pingmesh/pingForm/index.module.css
Original file line number Diff line number Diff line change
@@ -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;
}

Expand Down
81 changes: 64 additions & 17 deletions webui/src/pages/pingmesh/pingForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,55 +10,102 @@ interface PingFormProps {

const PingForm: React.FunctionComponent<PingFormProps> = (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 (
<Form inline labelAlign='left'>
<Form.Item label="Targets" >
<Form.Item>
<div className={styles.custom}>
{pingMeshList.map((v, i) => {
if (v.type == "Node") {
return <Button className={styles.btn} key={i}>{v.type + ": " + v.name}</Button>;
} else {
return <Button className={styles.btn} key={i}>{v.type + ": " + v.namespace + "/" + v.name}</Button>;
}
})}
<Button className={styles.btn} type="primary" onClick={() => { setShowSelectorDialog(!showSelectorDialog) }}>
<span className={styles.directionSel}>Source: </span>
<div className={styles.selectorGroup}>
<Button className={styles.btn} type="primary" onClick={() => { setShowSourceSelectorDialog(!showSourceSelectorDialog) }}>
Add +{" "}
</Button>
<Button className={styles.btn} warning type="primary" onClick={() => { setPingMeshList([]) }}>
<Button className={styles.btn} warning type="primary" onClick={() => { setPingMeshSourceList([]) }}>
Clear
</Button>
<SelectorDialog visible={showSelectorDialog}
<div>
{pingMeshSourceList.map((v, i) => {
if (v.type == "Node" || v.type == "IP") {
return <Button className={styles.btn} key={i}>{v.type + ": " + v.name}</Button>;
} else {
return <Button className={styles.btn} key={i}>{v.type + ": " + v.namespace + "/" + v.name}</Button>;
}
})}
</div>

<SelectorDialog visible={showSourceSelectorDialog}
submitSelector={(value) => {
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) }}></SelectorDialog>
onClose={() => { setShowSourceSelectorDialog(!showSourceSelectorDialog) }}></SelectorDialog>
</div>
</div>
</Form.Item>
<br/>
<Form.Item>
<span className={styles.directionSel}>Destination: </span>
<div className={styles.custom}>
<div className={styles.selectorGroup}>
<Button className={styles.btn} type="primary" onClick={() => { setShowSelectorDialog(!showSelectorDialog) }}>
Add +{" "}
</Button>
<Button className={styles.btn} warning type="primary" onClick={() => { setPingMeshList([]) }}>
Clear
</Button>
<br/>
{pingMeshList.map((v, i) => {
if (v.type == "Node" || v.type == "IP") {
return <Button className={styles.btn} key={i}>{v.type + ": " + v.name}</Button>;
} else {
return <Button className={styles.btn} key={i}>{v.type + ": " + v.namespace + "/" + v.name}</Button>;
}
})}

<SelectorDialog visible={showSelectorDialog} displayIPSelector={true}
submitSelector={(value) => {
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) }}></SelectorDialog>
</div>
</div>
</Form.Item>
<br />
<Form.Item>
<Form.Submit type="primary" validate onClick={handleSubmit}>
Expand Down
56 changes: 42 additions & 14 deletions webui/src/pages/pingmesh/pingForm/selectorDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface SelectorProps {
podList: PodInfo[];
nodeList: NodeInfo[];
visible: boolean;
displayIPSelector: boolean
onClose: () => void
}

Expand All @@ -24,6 +25,8 @@ const SelectorDialog: React.FunctionComponent<SelectorProps> = (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") {
Expand Down Expand Up @@ -123,18 +126,30 @@ const SelectorDialog: React.FunctionComponent<SelectorProps> = (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 (
<Dialog
v2
title="Add Target"
footerActions={['ok']}
visible={props.visible}
onClose={props.onClose}
onOk={() => props.submitSelector(selectedResult())}
onOk={() => {!(formCaptureType=="IP"&&ipAddressCheckState!="success") && props.submitSelector(selectedResult())}}
>
<Form
labelAlign='left'
Expand All @@ -145,22 +160,35 @@ const SelectorDialog: React.FunctionComponent<SelectorProps> = (props: SelectorP
<Radio.Group
shape="button"
value={formCaptureType}
onChange={(value) => { setFormCaptureType(value); setFormNamespace(""); setFormName(""); filterCaptureObject(value) }}
onChange={(value) => {setFormCaptureType(value); setFormNamespace(""); setFormName(""); filterCaptureObject(value) }}
>
<Radio value="Node">Node</Radio>
<Radio value="Pod">Pod</Radio>
{props.displayIPSelector &&
<Radio value="IP">IP</Radio>
}
</Radio.Group>
</Form.Item>
<Form.Item label="Select Target By">
<Radio.Group
shape="button"
value={captureSelectorType}
onChange={(value) => { setCaptureSelectorType(value); setFormNamespace(""); setFormName(""); filterCaptureObject(formCaptureType) }}
>
<Radio value="Name">Namespace & Name</Radio>
<Radio value="Selector">Label Selector</Radio>
</Radio.Group>
</Form.Item>
{isPodOrNode(formCaptureType) &&
<Form.Item label="Select Target By">
<Radio.Group
shape="button"
value={captureSelectorType}
onChange={(value) => { setCaptureSelectorType(value); setFormNamespace(""); setFormName(""); filterCaptureObject(formCaptureType) }}
>
<Radio value="Name">Namespace & Name</Radio>
<Radio value="Selector">Label Selector</Radio>
</Radio.Group>
</Form.Item>
}
{!isPodOrNode(formCaptureType) &&
<Form.Item label="IP Address">
<Input onChange={(value) => {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"
/>
</Form.Item>
}

{formCaptureType == "Pod" &&
<Form.Item label="Namespace" required >
Expand All @@ -172,7 +200,7 @@ const SelectorDialog: React.FunctionComponent<SelectorProps> = (props: SelectorP
</Form.Item>
}

{captureSelectorType == "Selector" &&
{isPodOrNode(formCaptureType) && captureSelectorType == "Selector" &&
<Form.Item label="LabelSelector" required>
<Box direction="row" style={{alignItems: "center"}}>
<Select showSearch value={labelSelectorKey} onChange={setLabelSelectorKey} dataSource={labelSelectorKeys} style={{ width: 200 }} name="labelKey" placeholder="key" />
Expand All @@ -181,7 +209,7 @@ const SelectorDialog: React.FunctionComponent<SelectorProps> = (props: SelectorP
</Box>
</Form.Item>
}
{captureSelectorType == "Name" &&
{isPodOrNode(formCaptureType) && captureSelectorType == "Name" &&
<Form.Item label="Name" required>
<Select className={styles.selector} name="name" placeholder="Please select name" useDetailValue showSearch
value={formName}
Expand Down
Loading

0 comments on commit 60a3ce8

Please sign in to comment.