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 gpu in explorer #706

Merged
merged 9 commits into from
Jun 25, 2023
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
30 changes: 27 additions & 3 deletions packages/dashboard/src/explorer/components/DetailsV2.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@
<v-col :cols="screen_max_700.matches ? 12 : screen_max_1200.matches ? 6 : 4" v-if="node && interfaces">
<InterfacesDetails :interfaces="interfaces" />
</v-col>

<v-col :cols="screen_max_700.matches ? 12 : screen_max_1200.matches ? 6 : 4" v-if="node && nodeGPU">
<GPUDetails :nodeGPU="nodeGPU" />
</v-col>
<v-snackbar :timeout="2000" :value="gpuError" color="transparent" text>
<v-alert class="ma-2" dense outlined type="error"> Failed to receive node GPUs information </v-alert>
</v-snackbar>
</v-row>
</div>
<div v-if="loading" class="pt-10">
Expand All @@ -53,11 +60,12 @@ import axios from "axios";
import { DocumentNode } from "graphql";
import { Component, Prop, Vue, Watch } from "vue-property-decorator";

import { ICountry, INode, INodeStatistics } from "../graphql/api";
import { ICountry, INode, INodeGPU, INodeStatistics } from "../graphql/api";
import { GrafanaStatistics } from "../utils/getMetricsUrl";
import mediaMatcher from "../utils/mediaMatcher";
import CountryDetails from "./CountryDetails.vue";
import FarmDetails from "./FarmDetails.vue";
import GPUDetails from "./GPUDetails.vue";
import InterfacesDetails from "./InterfacesDetails.vue";
import LocationDetails from "./LocationDetails.vue";
import NodeDetails from "./NodeDetails.vue";
Expand All @@ -74,6 +82,7 @@ import TwinDetails from "./TwinDetails.vue";
TwinDetails,
PublicConfigDetails,
InterfacesDetails,
GPUDetails,
NodeUsedResources,
},
})
Expand All @@ -86,7 +95,8 @@ export default class Details extends Vue {
loading = false;
grafanaUrl = "";
interfaces = undefined;

nodeGPU: INodeGPU[] | undefined;
gpuError = false;
data: any = {};

get node(): INode {
Expand All @@ -107,7 +117,6 @@ export default class Details extends Vue {
@Watch("open", { immediate: true })
onOpenChange() {
if (!this.open) return;

this.loading = true;
const { query, variables } = this;
this.$apollo
Expand All @@ -123,6 +132,21 @@ export default class Details extends Vue {
this.data.node = await fetch(`${window.configs.APP_GRIDPROXY_URL}/nodes/${this.nodeId}`).then(res =>
res.json(),
);

if (this.data.node.num_gpu) {
try {
this.nodeGPU = await (
await axios.get(`${window.configs.APP_GRIDPROXY_URL}/nodes/${this.nodeId}/gpu`, {
timeout: 5000,
})
).data;
this.gpuError = false;
} catch (error) {
this.gpuError = true;
this.nodeGPU = undefined;
}
}

try {
this.data.nodeStatistics = await (
await axios.get(`${window.configs.APP_GRIDPROXY_URL}/nodes/${this.nodeId}/statistics`, {
Expand Down
120 changes: 120 additions & 0 deletions packages/dashboard/src/explorer/components/GPUDetails.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<template>
<v-card flat color="transparent" tag="div">
<v-list-item>
<v-list-item-icon>
<v-icon size="40" class="mr-2">mdi-expansion-card-variant</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title style="font-size: 30px"> GPU Details </v-list-item-title>
</v-list-item-content>
</v-list-item>
<!-- Details -->
<v-row>
<v-col cols="12" class="pt-0">
<!-- :cols="screen_max_800.matches ? 12 : screen_max_1000.matches ? 6 : 4" -->
<v-list v-if="gpuItem">
<v-list-item>
<v-list-item-content>
<v-tooltip top nudge-bottom="30">
<template v-slot:activator="{ on, attrs }">
<v-list-item-title class="pt-3" v-bind="attrs" v-on="on"
>Card ID
<v-chip
lose-icon="mdi-delete"
:color="gpuItem.contract ? 'warning' : 'success'"
small
class="mb-1 ml-2"
>{{ gpuItem.contract ? "Reserved" : "Available" }}</v-chip
>
</v-list-item-title>
</template>
<span>Card id that's used in a deployment</span>
</v-tooltip>
</v-list-item-content>
<v-col class="mr-n4">
<v-select
v-if="nodeGPUitems.length > 1"
append-outer-icon="mdi-content-copy"
hide-details
solo
v-model="gpuItem"
:items="nodeGPUitems"
@input.native="gpuItem = $event.srcElement.value.value"
@click:append-outer="copy(gpuItem.id)"
/>
<v-text-field
v-else
:value="gpuItem.id"
readonly
hide-details
append-outer-icon="mdi-content-copy"
@click:append-outer="copy(gpuItem.id)"
solo
></v-text-field>
</v-col>
</v-list-item>
<v-divider />
<v-list-item>
<v-list-item-content>
<v-list-item-title> Vendor </v-list-item-title>
</v-list-item-content>
{{ gpuItem?.vendor }}
</v-list-item>
<v-divider />

<v-list-item>
<v-list-item-content>
<v-list-item-title> Device </v-list-item-title>
</v-list-item-content>
{{ gpuItem?.device }}
</v-list-item>
<v-divider />
<v-list-item v-if="gpuItem.contract !== undefined">
<v-tooltip top nudge-bottom="30">
<template v-slot:activator="{ on, attrs }">
<v-list-item-content v-bind="attrs" v-on="on">
<v-list-item-title> Contract ID</v-list-item-title>
</v-list-item-content>
</template>
<span>The contract id that reserves this GPU card</span>
</v-tooltip>
{{ gpuItem?.contract }}
</v-list-item>
<v-divider />
</v-list> </v-col
></v-row>
</v-card>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";

import { INodeGPU } from "../graphql/api";

@Component({})
export default class GPUDetails extends Vue {
@Prop({ required: true }) nodeGPU!: INodeGPU[];
nodeGPUitems: {
text: string;
value: INodeGPU;

disabled: false;
}[] = this.$props.nodeGPU.map((item: INodeGPU) => {
return {
text: item.id,
value: item,
disabled: false,
};
});

gpuItem = this.$props.nodeGPU[0];
copy(id: string) {
navigator.clipboard.writeText(id);
}
}
</script>

<style>
.v-data-table > .v-data-table__wrapper tbody tr.v-data-table__expanded__content {
box-shadow: none;
}
</style>
8 changes: 8 additions & 0 deletions packages/dashboard/src/explorer/graphql/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,16 @@ export interface INode {
certificationType: "Diy" | "Certified";
farmingPolicyName: string;
countryFullName: string;
num_gpu: number;
extra_fee: number;
}

export interface INodeGPU {
id: string;
vendor: string;
device: string;
contract?: number;
}
export interface INodeStatisticsUser {
deployments: number;
workloads: number;
Expand Down
1 change: 1 addition & 0 deletions packages/dashboard/src/explorer/store/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export default {

if (state.nodesUpFilter) url += "&status=up";
if (state.nodesGatewayFilter) url += "&ipv4=true&domain=true";
if (state.nodesGPUFilter) url += "&has_gpu=true";

for (const key in state.nodesFilter) {
let value = state.nodesFilter[key];
Expand Down
1 change: 1 addition & 0 deletions packages/dashboard/src/explorer/store/getters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ export default {
getNodesTablePageSize: state => state.nodesTablePageSize,
getNodesUpFilter: state => state.nodesUpFilter,
getNodesGatewayFilter: state => state.nodesGatewayFilter,
getNodesGPUFilter: state => state.nodesGPUFilter,

getNodesFilter: state => state.nodesFilter,
} as GetterTree<IState, IState>;
6 changes: 6 additions & 0 deletions packages/dashboard/src/explorer/store/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export enum MutationTypes {
SET_NODES_TABLE_PAGE_SIZE = "setNodesTablePageSize",
SET_GATEWAY_FILTER = "setGatewayFilter",
SET_UP_FILTER = "setUpFilter",
SET_GPU_FILTER = "setGPUFilter",
SET_NODES = "setNodes",
SET_NODES_FILTER = "setNodesFilter",
CLEAR_NODES_FILTER = "clearNodesFilter",
Expand Down Expand Up @@ -62,6 +63,8 @@ function fillNodesFields(state: IState, node: any, farms: any): INode {
location: node.location,
country: node.country,
city: node.city,
num_gpu: node.num_gpu,
extra_fee: node.extra_fee,
interfaces: [
{
name: "",
Expand Down Expand Up @@ -123,6 +126,9 @@ export default {
setGatewayFilter(state: IState, payload: boolean) {
state.nodesGatewayFilter = payload;
},
setGPUFilter(state: IState, payload: boolean) {
state.nodesGPUFilter = payload;
},
setUpFilter(state: IState, payload: boolean) {
state.nodesUpFilter = payload;
},
Expand Down
3 changes: 2 additions & 1 deletion packages/dashboard/src/explorer/store/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export interface IState {
nodesTablePageSize: number;
nodesGatewayFilter: boolean;
nodesUpFilter: boolean;
nodesGPUFilter: boolean;

/* Refactored Data */
farms: IPaginationData<IFarm>;
Expand Down Expand Up @@ -160,7 +161,7 @@ export default {
nodesTablePageSize: 10,
nodesUpFilter: true,
nodesGatewayFilter: false,

nodesGPUFilter: false,
/* Refactored data */
farms: createPaginationData(),
} as IState;
20 changes: 13 additions & 7 deletions packages/dashboard/src/explorer/views/Nodes.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,12 @@
<template v-slot:table>
<div style="display: flex; flex-direction: column; align-items: flex-end; justify-content: center">
<div>
<v-switch
label="Gateways (Only)"
style="margin-bottom: -30px"
v-model="gatewayFilter"
@change="requestNodes"
/>
<v-switch label="Gateways (Only)" hide-details v-model="gatewayFilter" @change="requestNodes" />
<v-switch label="GPU Node (Only)" hide-details v-model="gpuFilter" @change="requestNodes" />
<v-tooltip bottom>
<template v-slot:activator="{ on, attrs }">
<div v-bind="attrs" v-on="on">
<v-switch label="Online (Only)" v-model="onlineFilter" @change="requestNodes" />
<v-switch label="Online (Only)" hide-details v-model="onlineFilter" @change="requestNodes" />
</div>
</template>
<span>Does not include Standby nodes</span>
Expand Down Expand Up @@ -152,6 +148,7 @@ export default class Nodes extends Vue {
{ text: "MRU", value: "mru", align: "center", customAlign: "text-center", description: "Total Memory" },
{ text: "SRU", value: "sru", align: "center", customAlign: "text-center", description: "Total SSD" },
{ text: "HRU", value: "hru", align: "center", customAlign: "text-center", description: "Total HDD" },
{ text: "GPU", value: "num_gpu", align: "center", customAlign: "text-center", description: "GPU card" },
{ text: "Up Time", value: "uptime", align: "center", customAlign: "text-center" },
{ text: "Status", value: "status", align: "center", customAlign: "text-center" },
];
Expand Down Expand Up @@ -246,6 +243,15 @@ export default class Nodes extends Vue {
this.$store.commit("explorer/" + MutationTypes.SET_GATEWAY_FILTER, value);
}

get gpuFilter() {
return this.$store.getters["explorer/getNodesGPUFilter"];
}

set gpuFilter(value) {
console.log("set gpuFilter");
this.$store.commit("explorer/" + MutationTypes.SET_GPU_FILTER, value);
}

get onlineFilter() {
return this.$store.getters["explorer/getNodesUpFilter"];
}
Expand Down