Skip to content

Commit

Permalink
Merge pull request #706 from threefoldtech/development_dashboard_expl…
Browse files Browse the repository at this point in the history
…orerGPU

support gpu in explorer
  • Loading branch information
0oM4R authored Jun 25, 2023
2 parents 6a7b4ff + 09d4033 commit 1b17ead
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 11 deletions.
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

0 comments on commit 1b17ead

Please sign in to comment.