Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support source&destination ping #227

Merged
merged 1 commit into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading