add process entity

This commit is contained in:
Fine 2022-08-11 15:50:15 +08:00
parent 58aca03d75
commit e6e1f6457b
9 changed files with 137 additions and 302 deletions

View File

@ -58,6 +58,23 @@ export const Endpoints = {
} }
`, `,
}; };
export const Processes = {
variable: "$instanceId: ID!, $duration: Duration!",
query: `
processes: listProcesses(instanceId: $instanceId, duration: $duration) {
id
value: name
label: name
serviceId
serviceName
instanceId
instanceName
agentId
detectType
attributes
labels
`,
};
export const getService = { export const getService = {
variable: "$serviceId: String!", variable: "$serviceId: String!",
@ -102,3 +119,22 @@ export const getEndpoint = {
} }
`, `,
}; };
export const getProcess = {
variable: "$processId: ID!",
query: `
process: getProcess(processId: $processId) {
id
value: name
label: name
serviceId
serviceName
instanceId
instanceName
agentId
detectType
attributes
labels
}
`,
};

View File

@ -22,6 +22,8 @@ import {
getService, getService,
getInstance, getInstance,
getEndpoint, getEndpoint,
Processes,
getProcess,
} from "../fragments/selector"; } from "../fragments/selector";
export const queryServices = `query queryServices(${Services.variable}) {${Services.query}}`; export const queryServices = `query queryServices(${Services.variable}) {${Services.query}}`;
@ -31,3 +33,5 @@ export const queryLayers = `query listLayer {${Layers.query}}`;
export const queryService = `query queryService(${getService.variable}) {${getService.query}}`; export const queryService = `query queryService(${getService.variable}) {${getService.query}}`;
export const queryInstance = `query queryInstance(${getInstance.variable}) {${getInstance.query}}`; export const queryInstance = `query queryInstance(${getInstance.variable}) {${getInstance.query}}`;
export const queryEndpoint = `query queryInstance(${getEndpoint.variable}) {${getEndpoint.query}}`; export const queryEndpoint = `query queryInstance(${getEndpoint.variable}) {${getEndpoint.query}}`;
export const queryProcesses = `query queryProcesses(${Processes.variable}) {${Processes.query}}`;
export const queryProcess = `query queryProcess(${getProcess.variable}) {${getProcess.query}}`;

View File

@ -1,3 +1,4 @@
import { getProcess } from "./../../graphql/fragments/selector";
/** /**
* Licensed to the Apache Software Foundation (ASF) under one or more * Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with * contributor license agreements. See the NOTICE file distributed with
@ -15,7 +16,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { Service, Instance, Endpoint } from "@/types/selector"; import { Service, Instance, Endpoint, Process } from "@/types/selector";
import { store } from "@/store"; import { store } from "@/store";
import graphql from "@/graphql"; import graphql from "@/graphql";
import { AxiosResponse } from "axios"; import { AxiosResponse } from "axios";
@ -24,11 +25,15 @@ interface SelectorState {
services: Service[]; services: Service[];
destServices: Service[]; destServices: Service[];
pods: Array<Instance | Endpoint>; pods: Array<Instance | Endpoint>;
processes: Process[];
destProcesses: Process[];
currentService: Nullable<Service>; currentService: Nullable<Service>;
currentPod: Nullable<Instance | Endpoint>; currentPod: Nullable<Instance | Endpoint>;
currentProcess: Nullable<Process>;
currentDestService: Nullable<Service>; currentDestService: Nullable<Service>;
currentDestPod: Nullable<Instance | Endpoint>; currentDestPod: Nullable<Instance | Endpoint>;
destPods: Array<Instance | Endpoint>; destPods: Array<Instance | Endpoint>;
currentDestProcess: Nullable<Process>;
} }
export const selectorStore = defineStore({ export const selectorStore = defineStore({
@ -38,10 +43,14 @@ export const selectorStore = defineStore({
destServices: [], destServices: [],
pods: [], pods: [],
destPods: [], destPods: [],
processes: [],
destProcesses: [],
currentService: null, currentService: null,
currentPod: null, currentPod: null,
currentProcess: null,
currentDestService: null, currentDestService: null,
currentDestPod: null, currentDestPod: null,
currentDestProcess: null,
}), }),
actions: { actions: {
setCurrentService(service: Nullable<Service>) { setCurrentService(service: Nullable<Service>) {
@ -56,6 +65,12 @@ export const selectorStore = defineStore({
setCurrentDestPod(pod: Nullable<Instance | Endpoint>) { setCurrentDestPod(pod: Nullable<Instance | Endpoint>) {
this.currentDestPod = pod; this.currentDestPod = pod;
}, },
setCurrentProcess(process: Nullable<Process>) {
this.currentPod = process;
},
setCurrentDestProcess(process: Nullable<Process>) {
this.currentDestPod = process;
},
async fetchLayers(): Promise<AxiosResponse> { async fetchLayers(): Promise<AxiosResponse> {
const res: AxiosResponse = await graphql.query("queryLayers").params({}); const res: AxiosResponse = await graphql.query("queryLayers").params({});
@ -93,6 +108,27 @@ export const selectorStore = defineStore({
} }
return res.data; return res.data;
}, },
async getProcesses(param?: {
instanceId: string;
isRelation: boolean;
}): Promise<Nullable<AxiosResponse>> {
const instanceId = param ? param.instanceId : this.currentPod?.id;
if (!instanceId) {
return null;
}
const res: AxiosResponse = await graphql.query("queryInstances").params({
instanceId,
duration: useAppStoreWithOut().durationTime,
});
if (!res.data.errors) {
if (param && param.isRelation) {
this.destProcesses = res.data.data.processes || [];
return res.data;
}
this.processes = res.data.data.processes || [];
}
return res.data;
},
async getEndpoints(params: { async getEndpoints(params: {
keyword?: string; keyword?: string;
serviceId?: string; serviceId?: string;
@ -176,6 +212,25 @@ export const selectorStore = defineStore({
this.pods = [res.data.data.endpoint]; this.pods = [res.data.data.endpoint];
} }
return res.data;
},
async getProcess(instanceId: string, isRelation?: boolean) {
if (!instanceId) {
return;
}
const res: AxiosResponse = await graphql.query("queryProcess").params({
instanceId,
});
if (!res.data.errors) {
if (isRelation) {
this.currentDestProcess = res.data.data.process || null;
this.destProcesses = [res.data.data.process];
return;
}
this.currentProcess = res.data.data.process || null;
this.processes = [res.data.data.process];
}
return res.data; return res.data;
}, },
}, },

14
src/types/ebpf.d.ts vendored
View File

@ -15,6 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { Process } from "./selector";
export interface EBPFTaskCreationRequest { export interface EBPFTaskCreationRequest {
serviceId: string; serviceId: string;
processLabels: string[]; processLabels: string[];
@ -43,18 +44,7 @@ export interface EBPFProfilingSchedule {
startTime: number; startTime: number;
} }
export type Process = { export type Process = Process;
id: string;
name: string;
serviceId: string;
serviceName: string;
instanceId: string;
instanceName: string;
agentId: string;
detectType: string;
attributes: { name: string; value: string }[];
labels: string[];
};
export type StackElement = { export type StackElement = {
id: string; id: string;
originId: string; originId: string;

View File

@ -46,3 +46,16 @@ export type Service = {
layers: string[]; layers: string[];
shortName: string; shortName: string;
}; };
export type Process = {
id: string;
name: string;
serviceId: string;
serviceName: string;
instanceId: string;
instanceName: string;
agentId: string;
detectType: string;
attributes: { name: string; value: string }[];
labels: string[];
};

View File

@ -159,6 +159,7 @@ export const EntityType = [
key: 4, key: 4,
}, },
{ value: "EndpointRelation", label: "Endpoint Relation", key: 4 }, { value: "EndpointRelation", label: "Endpoint Relation", key: 4 },
{ value: "ProcessRelation", label: "Process Relation", key: 5 },
]; ];
export const ListEntity: any = { export const ListEntity: any = {
InstanceList: EntityType[3].value, InstanceList: EntityType[3].value,

View File

@ -26,7 +26,7 @@ limitations under the License. -->
class="selectors" class="selectors"
/> />
</div> </div>
<div class="selectors-item" v-if="key === 3 || key === 4"> <div class="selectors-item" v-if="key === 3 || key === 4 || key === 5">
<span class="label"> <span class="label">
{{ {{
["EndpointRelation", "Endpoint"].includes(dashboardStore.entity) ["EndpointRelation", "Endpoint"].includes(dashboardStore.entity)
@ -47,7 +47,18 @@ limitations under the License. -->
" "
/> />
</div> </div>
<div class="selectors-item" v-if="key === 2 || key === 4"> <div class="selectors-item" v-if="key === 5">
<span class="label"> $Process </span>
<Selector
v-model="states.currentDestPod"
:options="selectorStore.destPods"
size="small"
placeholder="Select a data"
@change="changeDestPods"
class="selectors"
/>
</div>
<div class="selectors-item" v-if="key === 2 || key === 4 || key === 5">
<span class="label">$DestinationService</span> <span class="label">$DestinationService</span>
<Selector <Selector
v-model="states.currentDestService" v-model="states.currentDestService"
@ -58,7 +69,7 @@ limitations under the License. -->
class="selectors" class="selectors"
/> />
</div> </div>
<div class="selectors-item" v-if="key === 4"> <div class="selectors-item" v-if="key === 4 || key === 5">
<span class="label"> <span class="label">
{{ {{
dashboardStore.entity === "EndpointRelation" dashboardStore.entity === "EndpointRelation"
@ -77,6 +88,17 @@ limitations under the License. -->
:isRemote="dashboardStore.entity === 'EndpointRelation'" :isRemote="dashboardStore.entity === 'EndpointRelation'"
/> />
</div> </div>
<div class="selectors-item" v-if="key === 5">
<span class="label"> $DestinationProcess </span>
<Selector
v-model="states.currentDestPod"
:options="selectorStore.destPods"
size="small"
placeholder="Select a data"
@change="changeDestPods"
class="selectors"
/>
</div>
</div> </div>
<div class="flex-h tools" v-loading="loading" v-if="!appStore.isMobile"> <div class="flex-h tools" v-loading="loading" v-if="!appStore.isMobile">
<div class="tool-icons flex-h" v-if="dashboardStore.editMode"> <div class="tool-icons flex-h" v-if="dashboardStore.editMode">

View File

@ -1,68 +0,0 @@
<!-- 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. -->
<template>
<grid-layout
v-model:layout="layout"
:col-num="24"
:row-height="10"
:is-draggable="false"
:is-resizable="false"
v-if="layout.length"
>
<grid-item
v-for="item in layout"
:x="item.x"
:y="item.y"
:w="item.w"
:h="item.h"
:i="item.i"
:key="item.i"
@click="clickGrid(item)"
:class="{ active: networkProfilingStore.activeMetricIndex === item.i }"
>
<metric-widget
:data="item"
:activeIndex="networkProfilingStore.activeMetricIndex"
/>
</grid-item>
</grid-layout>
<div class="no-data-tips" v-else>{{ t("noWidget") }}</div>
</template>
<script lang="ts" setup>
import type { PropType } from "vue";
import { onBeforeUnmount, defineProps, ref } from "vue";
import { useI18n } from "vue-i18n";
import { LayoutConfig } from "@/types/dashboard";
import MetricWidget from "./MetricWidget.vue";
import { useNetworkProfilingStore } from "@/store/modules/network-profiling";
const props = defineProps({
widgets: {
type: Array as PropType<LayoutConfig[]>,
default: () => [],
},
});
const { t } = useI18n();
const networkProfilingStore = useNetworkProfilingStore();
const layout = ref<LayoutConfig[]>(props.widgets || []);
function clickGrid(item: LayoutConfig) {
networkProfilingStore.setActiveItem(item.i);
networkProfilingStore.setSelectedMetric(item);
}
onBeforeUnmount(() => {
networkProfilingStore.setMetricsLayout([]);
});
</script>

View File

@ -1,218 +0,0 @@
<!-- 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. -->
<template>
<div class="widget">
<div class="header flex-h">
<div>
<span>
{{ widget.title || "" }}
</span>
</div>
<div>
<el-tooltip :content="widget.tips || ''">
<span>
<Icon
iconName="info_outline"
size="sm"
class="operation"
v-show="widget.tips"
/>
</span>
</el-tooltip>
<el-popover
placement="bottom"
trigger="click"
:width="100"
v-if="dashboardStore.editMode"
>
<template #reference>
<span>
<Icon iconName="ellipsis_v" size="middle" class="operation" />
</span>
</template>
<div class="tools" @click="editConfig">
<span>{{ t("edit") }}</span>
</div>
<div class="tools" @click="removeWidget">
<span>{{ t("delete") }}</span>
</div>
</el-popover>
</div>
</div>
<div class="body" v-if="graph.type" v-loading="loading">
<component
:is="graph.type"
:intervalTime="appStore.intervalTime"
:data="state.source"
:config="{
...data.graph,
metrics: data.metrics || [''],
metricTypes: data.metricTypes || [''],
i: data.i,
id: data.id,
metricConfig: data.metricConfig,
filters: data.filters || {},
}"
:needQuery="needQuery"
/>
</div>
<div v-else class="no-data">{{ t("noGraph") }}</div>
</div>
</template>
<script lang="ts">
import { toRefs, reactive, defineComponent, ref, computed } from "vue";
import type { PropType } from "vue";
import { LayoutConfig } from "@/types/dashboard";
import { useDashboardStore } from "@/store/modules/dashboard";
import { useAppStoreWithOut } from "@/store/modules/app";
import { useNetworkProfilingStore } from "@/store/modules/network-profiling";
import graphs from "../../../../graphs/topology";
import { useI18n } from "vue-i18n";
import {
useQueryProcessor,
useSourceProcessor,
useGetMetricEntity,
} from "@/hooks/useProcessor";
const props = {
data: {
type: Object as PropType<LayoutConfig>,
default: () => ({ widget: {}, graph: {} }),
},
activeIndex: { type: String, default: "" },
needQuery: { type: Boolean, default: false },
};
export default defineComponent({
name: "Metric",
components: { ...graphs },
props,
setup(props) {
const { t } = useI18n();
const loading = ref<boolean>(false);
const state = reactive<{ source: { [key: string]: unknown } }>({
source: {},
});
const { data } = toRefs(props);
const appStore = useAppStoreWithOut();
const dashboardStore = useDashboardStore();
const networkProfilingStore = useNetworkProfilingStore();
const graph = computed(() => props.data.graph || {});
const widget = computed(() => props.data.widget || {});
queryMetrics();
async function queryMetrics() {
const metricTypes = props.data.metricTypes || [];
const metrics = props.data.metrics || [];
const catalog = await useGetMetricEntity(metrics[0], metricTypes[0]);
const params = await useQueryProcessor({ ...props.data, catalog });
if (!params) {
state.source = {};
return;
}
loading.value = true;
const json = await dashboardStore.fetchMetricValue(params);
loading.value = false;
if (!json) {
return;
}
const d = {
metrics: props.data.metrics || [],
metricTypes: props.data.metricTypes || [],
metricConfig: props.data.metricConfig || [],
};
state.source = useSourceProcessor(json, d);
}
function removeWidget() {
networkProfilingStore.removeControls(props.data);
}
function editConfig() {
networkProfilingStore.setConfigPanel(true);
networkProfilingStore.setSelectedMetric(props.data);
if (props.activeIndex) {
networkProfilingStore.setActiveItem(props.activeIndex);
} else {
networkProfilingStore.setActiveItem(props.data.i);
}
}
return {
state,
appStore,
removeWidget,
editConfig,
data,
loading,
dashboardStore,
t,
graph,
widget,
};
},
});
</script>
<style lang="scss" scoped>
.widget {
font-size: 12px;
height: 100%;
}
.header {
height: 30px;
padding: 5px;
width: 100%;
border-bottom: 1px solid #eee;
justify-content: space-between;
}
.operation {
cursor: pointer;
}
.tools {
padding: 5px 0;
color: #999;
cursor: pointer;
position: relative;
text-align: center;
&:hover {
color: #409eff;
background-color: #eee;
}
}
.body {
padding: 5px 10px;
width: 100%;
height: calc(100% - 30px);
}
.no-data {
font-size: 14px;
color: #888;
width: 100%;
text-align: center;
padding-top: 20px;
}
.unit {
display: inline-block;
margin-left: 5px;
}
</style>