split ebpf store

This commit is contained in:
Fine 2022-08-10 16:51:51 +08:00
parent b9b41f8627
commit ab1ae85694
7 changed files with 237 additions and 167 deletions

View File

@ -21,16 +21,12 @@ import {
EBPFProfilingSchedule,
EBPFTaskList,
AnalyzationTrees,
ProcessNode,
} from "@/types/ebpf";
import { store } from "@/store";
import graphql from "@/graphql";
import { AxiosResponse } from "axios";
import { Call } from "@/types/topology";
import { LayoutConfig } from "@/types/dashboard";
interface EbpfStore {
interface EbpfState {
taskList: EBPFTaskList[];
networkTasks: EBPFTaskList[];
eBPFSchedules: EBPFProfilingSchedule[];
currentSchedule: EBPFProfilingSchedule | Record<string, never>;
analyzeTrees: AnalyzationTrees[];
@ -39,22 +35,13 @@ interface EbpfStore {
tip: string;
networkTip: string;
selectedTask: Recordable<EBPFTaskList>;
selectedNetworkTask: Recordable<EBPFTaskList>;
aggregateType: string;
nodes: ProcessNode[];
calls: Call[];
node: Nullable<ProcessNode>;
call: Nullable<Call>;
metricsLayout: LayoutConfig[];
selectedMetric: Nullable<LayoutConfig>;
activeMetricIndex: string;
}
export const ebpfStore = defineStore({
id: "eBPF",
state: (): EbpfStore => ({
state: (): EbpfState => ({
taskList: [],
networkTasks: [],
eBPFSchedules: [],
currentSchedule: {},
analyzeTrees: [],
@ -63,15 +50,7 @@ export const ebpfStore = defineStore({
tip: "",
networkTip: "",
selectedTask: {},
selectedNetworkTask: {},
aggregateType: "COUNT",
nodes: [],
calls: [],
node: null,
call: null,
metricsLayout: [],
selectedMetric: null,
activeMetricIndex: "",
}),
actions: {
setSelectedTask(task: EBPFTaskList) {
@ -86,44 +65,6 @@ export const ebpfStore = defineStore({
setAnalyzeTrees(tree: AnalyzationTrees[]) {
this.analyzeTrees = tree;
},
setNode(node: Node) {
this.node = node;
},
setLink(link: Call) {
this.call = link;
},
setMetricsLayout(layout: LayoutConfig[]) {
this.metricsLayout = layout;
},
setSelectedMetric(item: LayoutConfig) {
this.selectedMetric = item;
},
setActiveItem(index: string) {
this.activeMetricIndex = index;
},
setTopology(data: { nodes: ProcessNode[]; calls: Call[] }) {
const obj = {} as any;
const calls = (data.calls || []).reduce((prev: Call[], next: Call) => {
if (!obj[next.id]) {
obj[next.id] = true;
next.value = next.value || 1;
for (const node of data.nodes) {
if (next.source === node.id) {
next.sourceObj = node;
}
if (next.target === node.id) {
next.targetObj = node;
}
}
next.value = next.value || 1;
prev.push(next);
}
return prev;
}, []);
this.calls = calls;
this.nodes = data.nodes;
},
async getCreateTaskData(serviceId: string) {
const res: AxiosResponse = await graphql
.query("getCreateTaskData")
@ -153,23 +94,6 @@ export const ebpfStore = defineStore({
});
return res.data;
},
async createNetworkTask(param: {
serviceId: string;
serviceInstanceId: string;
}) {
const res: AxiosResponse = await graphql
.query("newNetworkProfiling")
.params({ request: { instanceId: param.serviceInstanceId } });
if (res.data.errors) {
return res.data;
}
this.getTaskList({
...param,
targets: ["NETWORK"],
});
return res.data;
},
async getTaskList(params: {
serviceId: string;
serviceInstanceId: string;
@ -182,27 +106,17 @@ export const ebpfStore = defineStore({
.query("getEBPFTasks")
.params(params);
if (params.serviceInstanceId) {
this.networkTip = "";
if (res.data.errors) {
return res.data;
}
this.networkTasks = res.data.data.queryEBPFTasks || [];
this.selectedNetworkTask = this.networkTasks[0] || {};
this.setSelectedNetworkTask(this.selectedNetworkTask);
} else {
this.tip = "";
if (res.data.errors) {
return res.data;
}
this.taskList = res.data.data.queryEBPFTasks || [];
this.selectedTask = this.taskList[0] || {};
this.setSelectedTask(this.selectedTask);
if (!this.taskList.length) {
return res.data;
}
this.getEBPFSchedules({ taskId: this.taskList[0].taskId });
this.tip = "";
if (res.data.errors) {
return res.data;
}
this.taskList = res.data.data.queryEBPFTasks || [];
this.selectedTask = this.taskList[0] || {};
this.setSelectedTask(this.selectedTask);
if (!this.taskList.length) {
return res.data;
}
this.getEBPFSchedules({ taskId: this.taskList[0].taskId });
return res.data;
},
async getEBPFSchedules(params: { taskId: string }) {
@ -260,23 +174,6 @@ export const ebpfStore = defineStore({
this.analyzeTrees = analysisEBPFResult.trees;
return res.data;
},
async getProcessTopology(params: {
duration: any;
serviceInstanceId: string;
}) {
const res: AxiosResponse = await graphql
.query("getProcessTopology")
.params(params);
if (res.data.errors) {
this.nodes = [];
this.calls = [];
return res.data;
}
const { topology } = res.data.data;
this.setTopology(topology);
return res.data;
},
},
});

View File

@ -0,0 +1,154 @@
/**
* 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 { defineStore } from "pinia";
import { EBPFTaskList, ProcessNode } from "@/types/ebpf";
import { store } from "@/store";
import graphql from "@/graphql";
import { AxiosResponse } from "axios";
import { Call } from "@/types/topology";
import { LayoutConfig } from "@/types/dashboard";
interface NetworkProfilingState {
networkTasks: EBPFTaskList[];
networkTip: string;
selectedNetworkTask: Recordable<EBPFTaskList>;
nodes: ProcessNode[];
calls: Call[];
node: Nullable<ProcessNode>;
call: Nullable<Call>;
metricsLayout: LayoutConfig[];
selectedMetric: Nullable<LayoutConfig>;
activeMetricIndex: string;
}
export const networkProfilingStore = defineStore({
id: "networkProfiling",
state: (): NetworkProfilingState => ({
networkTasks: [],
networkTip: "",
selectedNetworkTask: {},
nodes: [],
calls: [],
node: null,
call: null,
metricsLayout: [],
selectedMetric: null,
activeMetricIndex: "",
}),
actions: {
setSelectedNetworkTask(task: EBPFTaskList) {
this.selectedNetworkTask = task || {};
},
setNode(node: Node) {
this.node = node;
},
setLink(link: Call) {
this.call = link;
},
setMetricsLayout(layout: LayoutConfig[]) {
this.metricsLayout = layout;
},
setSelectedMetric(item: LayoutConfig) {
this.selectedMetric = item;
},
setActiveItem(index: string) {
this.activeMetricIndex = index;
},
setTopology(data: { nodes: ProcessNode[]; calls: Call[] }) {
const obj = {} as any;
const calls = (data.calls || []).reduce((prev: Call[], next: Call) => {
if (!obj[next.id]) {
obj[next.id] = true;
next.value = next.value || 1;
for (const node of data.nodes) {
if (next.source === node.id) {
next.sourceObj = node;
}
if (next.target === node.id) {
next.targetObj = node;
}
}
next.value = next.value || 1;
prev.push(next);
}
return prev;
}, []);
this.calls = calls;
this.nodes = data.nodes;
},
async createNetworkTask(param: {
serviceId: string;
serviceInstanceId: string;
}) {
const res: AxiosResponse = await graphql
.query("newNetworkProfiling")
.params({ request: { instanceId: param.serviceInstanceId } });
if (res.data.errors) {
return res.data;
}
this.getTaskList({
...param,
targets: ["NETWORK"],
});
return res.data;
},
async getTaskList(params: {
serviceId: string;
serviceInstanceId: string;
targets: string[];
}) {
if (!params.serviceId) {
return new Promise((resolve) => resolve({}));
}
const res: AxiosResponse = await graphql
.query("getEBPFTasks")
.params(params);
this.networkTip = "";
if (res.data.errors) {
return res.data;
}
this.networkTasks = res.data.data.queryEBPFTasks || [];
this.selectedNetworkTask = this.networkTasks[0] || {};
this.setSelectedNetworkTask(this.selectedNetworkTask);
return res.data;
},
async getProcessTopology(params: {
duration: any;
serviceInstanceId: string;
}) {
const res: AxiosResponse = await graphql
.query("getProcessTopology")
.params(params);
if (res.data.errors) {
this.nodes = [];
this.calls = [];
return res.data;
}
const { topology } = res.data.data;
this.setTopology(topology);
return res.data;
},
},
});
export function useNetworkProfilingStore(): any {
return networkProfilingStore(store);
}

View File

@ -20,7 +20,7 @@ import type { PropType } from "vue";
import { ref, onMounted, watch } from "vue";
import * as d3 from "d3";
import { useI18n } from "vue-i18n";
import { useEbpfStore } from "@/store/modules/ebpf";
import { useNetworkProfilingStore } from "@/store/modules/network-profiling";
import { useDashboardStore } from "@/store/modules/dashboard";
import d3tip from "d3-tip";
import {
@ -47,7 +47,7 @@ const props = defineProps({
});
const { t } = useI18n();
const dashboardStore = useDashboardStore();
const ebpfStore = useEbpfStore();
const networkProfilingStore = useNetworkProfilingStore();
const height = ref<number>(100);
const width = ref<number>(100);
const simulation = ref<any>(null);
@ -71,7 +71,7 @@ onMounted(() => {
async function init() {
svg.value = d3.select(chart.value).append("svg").attr("class", "process-svg");
if (!ebpfStore.nodes.length) {
if (!networkProfilingStore.nodes.length) {
return;
}
drawGraph();
@ -94,8 +94,8 @@ function drawGraph() {
graph.value.call(tip.value);
simulation.value = simulationInit(
d3,
ebpfStore.nodes,
ebpfStore.calls,
networkProfilingStore.nodes,
networkProfilingStore.calls,
ticked
);
node.value = graph.value.append("g").selectAll(".topo-node");
@ -106,8 +106,8 @@ function drawGraph() {
svg.value.on("click", (event: any) => {
event.stopPropagation();
event.preventDefault();
ebpfStore.setNode(null);
ebpfStore.setLink(null);
networkProfilingStore.setNode(null);
networkProfilingStore.setLink(null);
dashboardStore.selectWidget(props.config);
});
useThrottleFn(resize, 500)();
@ -117,7 +117,10 @@ function update() {
if (!node.value || !link.value) {
return;
}
node.value = node.value.data(ebpfStore.nodes, (d: ProcessNode) => d.id);
node.value = node.value.data(
networkProfilingStore.nodes,
(d: ProcessNode) => d.id
);
node.value.exit().remove();
node.value = nodeElement(
d3,
@ -130,11 +133,14 @@ function update() {
tip.value
).merge(node.value);
// line element
link.value = link.value.data(ebpfStore.calls, (d: Call) => d.id);
link.value = link.value.data(networkProfilingStore.calls, (d: Call) => d.id);
link.value.exit().remove();
link.value = linkElement(link.value.enter()).merge(link.value);
// anchorElement
anchor.value = anchor.value.data(ebpfStore.calls, (d: Call) => d.id);
anchor.value = anchor.value.data(
networkProfilingStore.calls,
(d: Call) => d.id
);
anchor.value.exit().remove();
anchor.value = anchorElement(
anchor.value.enter(),
@ -150,25 +156,28 @@ function update() {
tip.value
).merge(anchor.value);
// arrow marker
arrow.value = arrow.value.data(ebpfStore.calls, (d: Call) => d.id);
arrow.value = arrow.value.data(
networkProfilingStore.calls,
(d: Call) => d.id
);
arrow.value.exit().remove();
arrow.value = arrowMarker(arrow.value.enter()).merge(arrow.value);
// force element
simulation.value.nodes(ebpfStore.nodes);
simulation.value.nodes(networkProfilingStore.nodes);
simulation.value
.force("link")
.links(ebpfStore.calls)
.links(networkProfilingStore.calls)
.id((d: Call) => d.id);
simulationSkip(d3, simulation.value, ticked);
const loopMap: any = {};
for (let i = 0; i < ebpfStore.calls.length; i++) {
const link: any = ebpfStore.calls[i];
for (let i = 0; i < networkProfilingStore.calls.length; i++) {
const link: any = networkProfilingStore.calls[i];
link.loopFactor = 1;
for (let j = 0; j < ebpfStore.calls.length; j++) {
for (let j = 0; j < networkProfilingStore.calls.length; j++) {
if (i === j || loopMap[i]) {
continue;
}
const otherLink = ebpfStore.calls[j];
const otherLink = networkProfilingStore.calls[j];
if (
link.source.id === otherLink.target.id &&
link.target.id === otherLink.source.id
@ -189,8 +198,8 @@ function handleLinkClick(event: any, d: Call) {
return;
}
event.stopPropagation();
ebpfStore.setNode(null);
ebpfStore.setLink(d);
networkProfilingStore.setNode(null);
networkProfilingStore.setLink(d);
}
function ticked() {
@ -234,14 +243,14 @@ function resize() {
async function freshNodes() {
svg.value.selectAll(".svg-graph").remove();
if (!ebpfStore.nodes.length) {
if (!networkProfilingStore.nodes.length) {
return;
}
drawGraph();
update();
}
watch(
() => ebpfStore.nodes,
() => networkProfilingStore.nodes,
() => {
freshNodes();
}

View File

@ -21,14 +21,14 @@ limitations under the License. -->
<script lang="ts" setup>
import { ref, onMounted, watch } from "vue";
import { useI18n } from "vue-i18n";
import { useEbpfStore } from "@/store/modules/ebpf";
import { useNetworkProfilingStore } from "@/store/modules/network-profiling";
import { DataSet, Timeline } from "vis-timeline/standalone";
import "vis-timeline/styles/vis-timeline-graph2d.css";
import { useThrottleFn } from "@vueuse/core";
/*global Nullable */
const { t } = useI18n();
const ebpfStore = useEbpfStore();
const networkProfilingStore = useNetworkProfilingStore();
const timeRange = ref<Nullable<HTMLDivElement>>(null);
const visGraph = ref<Nullable<any>>(null);
const oldVal = ref<{ width: number; height: number }>({ width: 0, height: 0 });
@ -49,23 +49,25 @@ function visTimeline() {
if (visGraph.value) {
visGraph.value.destroy();
}
if (!ebpfStore.selectedNetworkTask.taskId) {
if (!networkProfilingStore.selectedNetworkTask.taskId) {
return;
}
const h = timeRange.value.getBoundingClientRect().height;
const task = [
{
id: 1,
content: ebpfStore.selectedNetworkTask.name,
start: new Date(Number(ebpfStore.selectedNetworkTask.taskStartTime)),
content: networkProfilingStore.selectedNetworkTask.name,
start: new Date(
Number(networkProfilingStore.selectedNetworkTask.taskStartTime)
),
end: new Date(
Number(
ebpfStore.selectedNetworkTask.taskStartTime +
ebpfStore.selectedNetworkTask.fixedTriggerDuration
networkProfilingStore.selectedNetworkTask.taskStartTime +
networkProfilingStore.selectedNetworkTask.fixedTriggerDuration
)
),
data: ebpfStore.selectedNetworkTask,
className: ebpfStore.selectedNetworkTask.type,
data: networkProfilingStore.selectedNetworkTask,
className: networkProfilingStore.selectedNetworkTask.type,
},
];
const items = new DataSet(task);
@ -96,7 +98,7 @@ function resize() {
}
}
watch(
() => ebpfStore.selectedNetworkTask,
() => networkProfilingStore.selectedNetworkTask,
() => {
visTimeline();
}

View File

@ -29,20 +29,24 @@ limitations under the License. -->
</el-popconfirm>
</div>
<div class="profile-t-wrapper">
<div class="no-data" v-show="!ebpfStore.networkTasks.length">
<div
class="no-data"
v-show="!networkProfilingStore.networkTasks.length"
>
{{ t("noData") }}
</div>
<table class="profile-t">
<tr
class="profile-tr cp"
v-for="(i, index) in ebpfStore.networkTasks"
v-for="(i, index) in networkProfilingStore.networkTasks"
@click="changeTask(i)"
:key="index"
>
<td
class="profile-td"
:class="{
selected: ebpfStore.selectedNetworkTask.taskId === i.taskId,
selected:
networkProfilingStore.selectedNetworkTask.taskId === i.taskId,
}"
>
<div class="ell">
@ -73,14 +77,14 @@ limitations under the License. -->
fullscreen
@closed="viewDetail = false"
>
<TaskDetails :details="ebpfStore.selectedNetworkTask" />
<TaskDetails :details="networkProfilingStore.selectedNetworkTask" />
</el-dialog>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import dayjs from "dayjs";
import { useI18n } from "vue-i18n";
import { useEbpfStore } from "@/store/modules/ebpf";
import { useNetworkProfilingStore } from "@/store/modules/network-profiling";
import { useSelectorStore } from "@/store/modules/selectors";
import { EBPFTaskList } from "@/types/ebpf";
import { ElMessage } from "element-plus";
@ -91,7 +95,7 @@ import { useAppStoreWithOut } from "@/store/modules/app";
const { t } = useI18n();
const selectorStore = useSelectorStore();
const ebpfStore = useEbpfStore();
const networkProfilingStore = useNetworkProfilingStore();
const appStore = useAppStoreWithOut();
const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
dayjs(date).format(pattern);
@ -100,19 +104,19 @@ const viewDetail = ref<boolean>(false);
fetchTasks();
async function changeTask(item: EBPFTaskList) {
ebpfStore.setSelectedNetworkTask(item);
networkProfilingStore.setSelectedNetworkTask(item);
getTopology();
}
async function getTopology() {
const serviceInstanceId =
(selectorStore.currentPod && selectorStore.currentPod.id) || "";
const resp = await ebpfStore.getProcessTopology({
const resp = await networkProfilingStore.getProcessTopology({
serviceInstanceId,
duration: {
start: dateFormatStep(
getLocalTime(
appStore.utc,
new Date(ebpfStore.selectedNetworkTask.taskStartTime)
new Date(networkProfilingStore.selectedNetworkTask.taskStartTime)
),
appStore.duration.step,
true
@ -121,8 +125,9 @@ async function getTopology() {
getLocalTime(
appStore.utc,
new Date(
ebpfStore.selectedNetworkTask.taskStartTime +
ebpfStore.selectedNetworkTask.fixedTriggerDuration * 1000
networkProfilingStore.selectedNetworkTask.taskStartTime +
networkProfilingStore.selectedNetworkTask.fixedTriggerDuration *
1000
)
),
appStore.duration.step,
@ -144,7 +149,7 @@ async function createTask() {
if (!serviceInstanceId) {
return;
}
ebpfStore.createNetworkTask({
networkProfilingStore.createNetworkTask({
serviceId,
serviceInstanceId,
});
@ -154,7 +159,7 @@ async function fetchTasks() {
(selectorStore.currentService && selectorStore.currentService.id) || "";
const serviceInstanceId =
(selectorStore.currentPod && selectorStore.currentPod.id) || "";
const res = await ebpfStore.getTaskList({
const res = await networkProfilingStore.getTaskList({
serviceId,
serviceInstanceId,
targets: ["NETWORK"],

View File

@ -30,9 +30,12 @@ limitations under the License. -->
:i="item.i"
:key="item.i"
@click="clickGrid(item)"
:class="{ active: ebpfStore.activeMetricIndex === item.i }"
:class="{ active: networkProfilingStore.activeMetricIndex === item.i }"
>
<metric-widget :data="item" :activeIndex="ebpfStore.activeMetricIndex" />
<metric-widget
:data="item"
:activeIndex="networkProfilingStore.activeMetricIndex"
/>
</grid-item>
</grid-layout>
<div class="no-data-tips" v-else>{{ t("noWidget") }}</div>
@ -43,7 +46,7 @@ import { onBeforeUnmount, defineProps, ref } from "vue";
import { useI18n } from "vue-i18n";
import { LayoutConfig } from "@/types/dashboard";
import MetricWidget from "./MetricWidget.vue";
import { useEbpfStore } from "@/store/modules/ebpf";
import { useNetworkProfilingStore } from "@/store/modules/network-profiling";
const props = defineProps({
widgets: {
@ -52,14 +55,14 @@ const props = defineProps({
},
});
const { t } = useI18n();
const ebpfStore = useEbpfStore();
const networkProfilingStore = useNetworkProfilingStore();
const layout = ref<LayoutConfig[]>(props.widgets);
function clickGrid(item: LayoutConfig) {
ebpfStore.setActiveItem(item.i);
ebpfStore.setSelectedMetric(item);
networkProfilingStore.setActiveItem(item.i);
networkProfilingStore.setSelectedMetric(item);
}
onBeforeUnmount(() => {
ebpfStore.setMetricsLayout([]);
networkProfilingStore.setMetricsLayout([]);
});
</script>

View File

@ -72,7 +72,7 @@ limitations under the License. -->
</div>
</template>
<script lang="ts">
import { toRefs, reactive, defineComponent, ref, watch, computed } from "vue";
import { toRefs, reactive, defineComponent, ref, computed } from "vue";
import type { PropType } from "vue";
import { LayoutConfig } from "@/types/dashboard";
import { useDashboardStore } from "@/store/modules/dashboard";