diff --git a/packages/dashboard/src/explorer/components/DetailsV2.vue b/packages/dashboard/src/explorer/components/DetailsV2.vue
index a8904c53b4..3188f2138a 100644
--- a/packages/dashboard/src/explorer/components/DetailsV2.vue
+++ b/packages/dashboard/src/explorer/components/DetailsV2.vue
@@ -39,6 +39,13 @@
+
+
+
+
+
+ Failed to receive node GPUs information
+
@@ -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";
@@ -74,6 +82,7 @@ import TwinDetails from "./TwinDetails.vue";
TwinDetails,
PublicConfigDetails,
InterfacesDetails,
+ GPUDetails,
NodeUsedResources,
},
})
@@ -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 {
@@ -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
@@ -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`, {
diff --git a/packages/dashboard/src/explorer/components/GPUDetails.vue b/packages/dashboard/src/explorer/components/GPUDetails.vue
new file mode 100644
index 0000000000..b93fd4e65d
--- /dev/null
+++ b/packages/dashboard/src/explorer/components/GPUDetails.vue
@@ -0,0 +1,120 @@
+
+
+
+
+ mdi-expansion-card-variant
+
+
+ GPU Details
+
+
+
+
+
+
+
+
+
+
+
+ Card ID
+ {{ gpuItem.contract ? "Reserved" : "Available" }}
+
+
+ Card id that's used in a deployment
+
+
+
+
+
+
+
+
+
+
+ Vendor
+
+ {{ gpuItem?.vendor }}
+
+
+
+
+
+ Device
+
+ {{ gpuItem?.device }}
+
+
+
+
+
+
+ Contract ID
+
+
+ The contract id that reserves this GPU card
+
+ {{ gpuItem?.contract }}
+
+
+
+
+
+
+
+
diff --git a/packages/dashboard/src/explorer/graphql/api.ts b/packages/dashboard/src/explorer/graphql/api.ts
index 4b94a00ee5..b153987c63 100644
--- a/packages/dashboard/src/explorer/graphql/api.ts
+++ b/packages/dashboard/src/explorer/graphql/api.ts
@@ -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;
diff --git a/packages/dashboard/src/explorer/store/actions.ts b/packages/dashboard/src/explorer/store/actions.ts
index e987cf06d8..69aebc6cf4 100644
--- a/packages/dashboard/src/explorer/store/actions.ts
+++ b/packages/dashboard/src/explorer/store/actions.ts
@@ -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];
diff --git a/packages/dashboard/src/explorer/store/getters.ts b/packages/dashboard/src/explorer/store/getters.ts
index 6b7c4864c3..bc62c91a04 100644
--- a/packages/dashboard/src/explorer/store/getters.ts
+++ b/packages/dashboard/src/explorer/store/getters.ts
@@ -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
;
diff --git a/packages/dashboard/src/explorer/store/mutations.ts b/packages/dashboard/src/explorer/store/mutations.ts
index a07f13e61f..65166e7227 100644
--- a/packages/dashboard/src/explorer/store/mutations.ts
+++ b/packages/dashboard/src/explorer/store/mutations.ts
@@ -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",
@@ -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: "",
@@ -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;
},
diff --git a/packages/dashboard/src/explorer/store/state.ts b/packages/dashboard/src/explorer/store/state.ts
index 4541c1c67b..0a3bfad7d3 100644
--- a/packages/dashboard/src/explorer/store/state.ts
+++ b/packages/dashboard/src/explorer/store/state.ts
@@ -93,6 +93,7 @@ export interface IState {
nodesTablePageSize: number;
nodesGatewayFilter: boolean;
nodesUpFilter: boolean;
+ nodesGPUFilter: boolean;
/* Refactored Data */
farms: IPaginationData;
@@ -160,7 +161,7 @@ export default {
nodesTablePageSize: 10,
nodesUpFilter: true,
nodesGatewayFilter: false,
-
+ nodesGPUFilter: false,
/* Refactored data */
farms: createPaginationData(),
} as IState;
diff --git a/packages/dashboard/src/explorer/views/Nodes.vue b/packages/dashboard/src/explorer/views/Nodes.vue
index ebb3f96d58..23575b4f23 100644
--- a/packages/dashboard/src/explorer/views/Nodes.vue
+++ b/packages/dashboard/src/explorer/views/Nodes.vue
@@ -13,16 +13,12 @@
-
+
+
-
+
Does not include Standby nodes
@@ -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" },
];
@@ -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"];
}