feat: add Policy list

This commit is contained in:
Fine 2023-05-18 17:37:30 +08:00
parent 8f0b0eae35
commit e876010975
6 changed files with 458 additions and 0 deletions

View File

@ -123,3 +123,32 @@ export const GetProfileTaskLogs = {
}
`,
};
export const GetStrategyList = {
variable: "$serviceId: ID",
query: `
strategyList: getStrategyList(serviceId: $serviceId) {
serviceId
targets {
targetType
checkItems {
type
threshold
period
count
uriList
uriRegex
}
}
}
`,
};
export const EditStrategy = {
variable: "$request: ContinuousProfilingPolicyCreation!",
query: `
strategy: setContinuousProfilingPolicy(request: $request) {
errorReason
status
}
`,
};

View File

@ -21,6 +21,8 @@ import {
GetProfileTaskSegmentList,
GetProfileAnalyze,
GetProfileTaskLogs,
GetStrategyList,
EditStrategy,
} from "../fragments/profile";
export const saveProfileTask = `mutation createProfileTask(${CreateProfileTask.variable}) {${CreateProfileTask.query}}`;
@ -34,3 +36,7 @@ export const getProfileTaskSegmentList = `query getProfileTaskSegmentList(${GetP
export const getProfileAnalyze = `query getProfileAnalyze(${GetProfileAnalyze.variable}) {${GetProfileAnalyze.query}}`;
export const getProfileTaskLogs = `query profileTaskLogs(${GetProfileTaskLogs.variable}) {${GetProfileTaskLogs.query}}`;
export const getStrategyList = `query GetStrategyList(${GetStrategyList.variable}) {${GetStrategyList.query}}`;
export const editStrategy = `query GetStrategyList(${EditStrategy.variable}) {${EditStrategy.query}}`;

View File

@ -0,0 +1,171 @@
/**
* 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 type { StrategyItem, CheckItems } from "@/types/continous-profiling";
import type { ProcessNode } from "@/types/ebpf";
import { store } from "@/store";
import graphql from "@/graphql";
import type { AxiosResponse } from "axios";
import type { Call } from "@/types/topology";
import type { LayoutConfig } from "@/types/dashboard";
import type { DurationTime } from "@/types/app";
interface ContinousProfilingState {
strategyList: Array<Recordable<StrategyItem>>;
selectedStrategyTask: Recordable<StrategyItem>;
errorReason: string;
nodes: ProcessNode[];
calls: Call[];
node: Nullable<ProcessNode>;
call: Nullable<Call>;
metricsLayout: LayoutConfig[];
selectedMetric: Nullable<LayoutConfig>;
activeMetricIndex: string;
aliveNetwork: boolean;
loadNodes: boolean;
}
export const continousProfilingStore = defineStore({
id: "continousProfiling",
state: (): ContinousProfilingState => ({
strategyList: [],
selectedStrategyTask: {},
errorReason: "",
nodes: [],
calls: [],
node: null,
call: null,
metricsLayout: [],
selectedMetric: null,
activeMetricIndex: "",
aliveNetwork: false,
loadNodes: false,
}),
actions: {
setSelectedStrategyTask(task: Recordable<StrategyItem>) {
this.selectedStrategyTask = task || {};
},
setNode(node: Nullable<ProcessNode>) {
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 Recordable;
let 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;
}, []);
const param = {} as Recordable;
calls = data.calls.reduce((prev: (Call & Recordable)[], next: Call & Recordable) => {
if (param[next.targetId + next.sourceId]) {
next.lowerArc = true;
}
param[next.sourceId + next.targetId] = true;
next.sourceId = next.source;
next.targetId = next.target;
next.source = next.sourceObj;
next.target = next.targetObj;
delete next.sourceObj;
delete next.targetObj;
prev.push(next);
return prev;
}, []);
this.calls = calls;
this.nodes = data.nodes;
},
async setContinuousProfilingPolicy(
serviceId: string,
targets: {
targetType: string;
checkItems: CheckItems[];
}[],
) {
const res: AxiosResponse = await graphql.query("editStrategy").params({
request: {
serviceId,
targets,
},
});
if (res.data.errors) {
return res.data;
}
return res.data;
},
async getStrategyList(params: { serviceId: string }) {
if (!params.serviceId) {
return new Promise((resolve) => resolve({}));
}
const res: AxiosResponse = await graphql.query("getStrategyList").params(params);
if (res.data.errors) {
return res.data;
}
this.strategyList = res.data.data.queryEBPFTasks || [];
this.selectedStrategyTask = this.strategyList[0] || {};
this.setSelectedStrategyTask(this.selectedStrategyTask);
if (!this.strategyList.length) {
this.nodes = [];
this.calls = [];
}
return res.data;
},
async getProcessTopology(params: { duration: DurationTime; serviceInstanceId: string }) {
this.loadNodes = true;
const res: AxiosResponse = await graphql.query("getProcessTopology").params(params);
this.loadNodes = false;
if (res.data.errors) {
this.nodes = [];
this.calls = [];
return res.data;
}
const { topology } = res.data.data;
this.setTopology(topology);
return res.data;
},
},
});
export function useContinousProfilingStore(): Recordable {
return continousProfilingStore(store);
}

29
src/types/continous-profiling.d.ts vendored Normal file
View File

@ -0,0 +1,29 @@
/**
* 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.
*/
export interface StrategyItem {
type: string;
checkItems: CheckItems[];
}
export type CheckItems = {
type: string;
threshold: string;
period: number;
count: number;
uriList: string[];
uriRegex: string;
};

View File

@ -0,0 +1,51 @@
<!-- 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="profile-task">
Edit Strategy
<div>
<el-button @click="save" type="primary" class="create-task-btn">
{{ t("save") }}
</el-button>
</div>
</div>
</template>
<script lang="ts" setup>
import { useI18n } from "vue-i18n";
/* global defineEmits */
const emits = defineEmits(["create"]);
const { t } = useI18n();
function save() {
emits("create", {});
}
</script>
<style lang="scss" scoped>
.profile-task {
width: 100%;
}
.create-task-btn {
width: 300px;
margin-top: 50px;
}
.title {
display: inline-block;
margin-right: 5px;
}
</style>

View File

@ -0,0 +1,172 @@
<!-- 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="profile-task-list flex-v">
<div class="profile-task-wrapper flex-v">
<div class="profile-t-tool">
<span>{{ t("taskList") }}</span>
<span class="new-task cp" @click="setStrategies">
<Icon iconName="library_add" size="middle" />
</span>
</div>
<div class="profile-t-wrapper">
<div class="no-data" v-show="!continousProfilingStore.strategyList.length">
{{ t("noData") }}
</div>
<div>data</div>
</div>
</div>
</div>
<el-dialog
v-model="updateStrategies"
:title="t('editStrategies')"
:destroy-on-close="true"
fullscreen
@closed="updateStrategies = false"
>
<EditStrategy @create="editStrategies" />
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, watch } from "vue";
import { useI18n } from "vue-i18n";
import { useContinousProfilingStore } from "@/store/modules/continous-profiling";
import { useSelectorStore } from "@/store/modules/selectors";
import type { StrategyItem, CheckItems } from "@/types/continous-profiling";
import { ElMessage } from "element-plus";
import EditStrategy from "./EditStrategy.vue";
const { t } = useI18n();
const selectorStore = useSelectorStore();
const continousProfilingStore = useContinousProfilingStore();
const updateStrategies = ref<boolean>(false);
const inProcess = ref<boolean>(false);
fetchStrategyList();
async function changeStrategy(item: StrategyItem) {
continousProfilingStore.setSelectedNetworkTask(item);
}
function setStrategies() {
updateStrategies.value = true;
}
async function editStrategies(
targets: {
targetType: string;
checkItems: CheckItems[];
}[],
) {
const serviceId = (selectorStore.currentService && selectorStore.currentService.id) || "";
if (!serviceId) {
return ElMessage.error("No Service ID");
}
const res = await continousProfilingStore.setContinuousProfilingPolicy(serviceId, targets);
if (res.errors) {
ElMessage.error(res.errors);
return;
}
if (!res.data.strategy.status) {
ElMessage.error(res.data.strategy.errorReason);
return;
}
updateStrategies.value = false;
await fetchStrategyList();
}
async function fetchStrategyList() {
const serviceId = (selectorStore.currentService && selectorStore.currentService.id) || "";
const res = await continousProfilingStore.getStrategyList({
serviceId,
});
if (res.errors) {
return ElMessage.error(res.errors);
}
if (!continousProfilingStore.strategyList.length) {
return;
}
}
watch(
() => selectorStore.currentService,
() => {
inProcess.value = false;
fetchStrategyList();
},
);
</script>
<style lang="scss" scoped>
.profile-task-list {
width: 330px;
height: calc(100% - 10px);
overflow: auto;
border-right: 1px solid rgba(0, 0, 0, 0.1);
}
.item span {
height: 21px;
}
.profile-td {
padding: 10px 5px 10px 10px;
border-bottom: 1px solid rgba(0, 0, 0, 0.07);
&.selected {
background-color: #ededed;
}
}
.no-data {
text-align: center;
margin-top: 10px;
}
.profile-t-wrapper {
overflow: auto;
flex-grow: 1;
}
.profile-t {
width: 100%;
border-spacing: 0;
table-layout: fixed;
flex-grow: 1;
position: relative;
border: none;
}
.profile-tr {
&:hover {
background-color: rgba(0, 0, 0, 0.04);
}
}
.profile-t-tool {
padding: 10px 5px 10px 10px;
border-bottom: 1px solid rgba(0, 0, 0, 0.07);
background: #f3f4f9;
width: 100%;
}
.new-task {
float: right;
}
.reload {
margin-left: 30px;
}
</style>