Skip to content

Commit 90f33fc

Browse files
worked with nodes placing
1 parent 2e2ccc3 commit 90f33fc

File tree

17 files changed

+452
-96
lines changed

17 files changed

+452
-96
lines changed

frontend/kubecloud-v2/assets/scss/vuetify.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,7 @@
3131
}
3232
}
3333
}
34+
35+
.table-hidden-overflow > div {
36+
overflow: hidden;
37+
}

frontend/kubecloud-v2/components/NodeCard.vue

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,13 @@
109109
block
110110
size="x-large"
111111
class="btn-form"
112-
text="Reserve Node"
113-
append-icon="mdi-arrow-right"
112+
:text="reserved ? 'Node Reserved' : 'Reserve Node'"
113+
:style="reserved ? { borderColor: 'rgb(var(--v-theme-success)) !important', boxShadow: 'none !important' } : {}"
114+
:append-icon="reserved ? undefined : 'mdi-arrow-right'"
115+
:prepend-icon="reserved ? 'mdi-check' : undefined"
116+
:color="reserved ? 'success' : undefined"
114117
variant="outlined"
118+
:readonly="reserved"
115119
:loading="isLoading"
116120
@click="reserveNode()"
117121
/>
@@ -121,39 +125,9 @@
121125

122126
<script setup lang="ts">
123127
import type { HandlersNodesWithDiscount } from "../generated/api"
124-
import { AxiosError } from "axios"
125128
126129
const props = defineProps<{ node: HandlersNodesWithDiscount }>()
127-
const api = useApi()
128130
129-
const monitoringUrl = computedAsync(async () => {
130-
const accountId = await api.helpers.getAccountId(props.node.twinId!)
131-
const params = new URLSearchParams({
132-
"orgId": "2",
133-
"refresh": "30s",
134-
"var-network": "dev",
135-
"var-farm": props.node.farmId!.toString(),
136-
"var-node": accountId,
137-
"var-diskdevices": "[a-z]+|nvme[0-9]+n[0-9]+|mmcblk[0-9]+",
138-
})
139-
140-
return `https://metrics.grid.tf/d/rYdddlPWkfqwf/zos-host-metrics?${params.toString()}`
141-
})
142-
143-
const toast = useToast()
144-
const { isLoading, execute: reserveNode } = useAsyncState(
145-
async () => {
146-
const { data } = await api.nodes.reserveNode(props.node.nodeId!.toString())
147-
return data
148-
},
149-
null,
150-
{
151-
immediate: false,
152-
onError(e: unknown) {
153-
if (e instanceof AxiosError) {
154-
toast.error({ message: e.response?.data?.message ?? "An unknown error occurred" })
155-
}
156-
},
157-
},
158-
)
131+
const monitoringUrl = useNodeMonitoringUrl(() => props.node)
132+
const { isLoading, execute: reserveNode, state: reserved } = useNodeReserve(() => props.node)
159133
</script>

frontend/kubecloud-v2/components/deploy-cluster/place/PlaceVMsForm.vue

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<v-row>
33
<v-col cols="12">
4-
<PlaceVMsHead />
4+
<PlaceVMsHead v-model="$props.modelValue" />
55
</v-col>
66

77
<v-col cols="12">
@@ -35,9 +35,10 @@
3535
<script setup lang="ts">
3636
defineProps<{ modelValue: ClusterForm }>()
3737
38-
// const api = useApi()
39-
// const { state: sshKeys } = useAsyncState(async () => {
40-
// const { data } = await api.users.listSshKeys()
41-
// return data.data ?? []
42-
// }, [])
38+
const loadingNode = ref<number | undefined>()
39+
40+
provide(NodePickCtxKey, {
41+
loadingNode,
42+
setLoadingNode: (nodeId?: number) => (loadingNode.value = nodeId),
43+
})
4344
</script>

frontend/kubecloud-v2/components/deploy-cluster/place/PlaceVMsHead.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
</v-alert>
2929

3030
<v-select
31+
v-model="$props.modelValue.region"
3132
label="Filter By Region"
3233
placeholder="Pick a region"
3334
clearable
@@ -40,6 +41,5 @@
4041
</template>
4142

4243
<script setup lang="ts">
43-
// defineProps<{ modelValue: string }>()
44-
// defineEmits<{ (e: "update:modelValue", value: string): void }>()
44+
defineProps<{ modelValue: ClusterForm }>()
4545
</script>
Lines changed: 75 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,85 @@
11
<template>
2-
<div class="border rounded-xl pa-4 border-dashed">
3-
<div class="d-flex align-center ga-2">
4-
<h5 class="text-h6 font-weight-bold" v-text="modelValue.name" />
5-
<v-chip color="primary" size="x-small" :text="modelValue.type" class="rounded font-weight-bold text-caption text-capitalize" />
6-
</div>
2+
<!-- class="border rounded-xl border-dashed" -->
3+
<div>
4+
<div class="pa-4">
5+
<div class="d-flex align-center ga-2">
6+
<h5 class="text-h6 font-weight-bold" v-text="modelValue.name" />
7+
<v-chip color="primary" size="x-small" :text="modelValue.type" class="rounded font-weight-bold text-caption text-capitalize" />
8+
</div>
79

8-
<v-chip
9-
v-if="modelValue.useFullNodeCapabilities"
10-
color="success"
11-
size="small"
12-
prepend-icon="mdi-check-decagram"
13-
class="font-weight-bold mt-2"
14-
>
15-
Use Full Node Capabilities
16-
</v-chip>
17-
18-
<div v-else class="d-flex align-center flex-wrap ga-2 mt-2">
19-
<v-chip size="small" prepend-icon="mdi-cpu-64-bit" color="primary" class="font-weight-bold">
20-
CPU: {{ modelValue.cpu }} vCores
21-
</v-chip>
22-
<v-chip size="small" prepend-icon="mdi-memory" color="success" class="font-weight-bold">
23-
RAM: {{ modelValue.memory }} GB
24-
</v-chip>
25-
<v-chip size="small" prepend-icon="mdi-server" color="secondary" class="font-weight-bold">
26-
Disk Size: {{ modelValue.disk }} GB
10+
<v-chip
11+
v-if="modelValue.useFullNodeCapabilities"
12+
color="success"
13+
size="small"
14+
prepend-icon="mdi-check-decagram"
15+
class="font-weight-bold mt-2"
16+
>
17+
Use Full Node Capabilities
2718
</v-chip>
19+
20+
<div v-else class="d-flex align-center flex-wrap ga-2 mt-2">
21+
<v-chip size="small" prepend-icon="mdi-cpu-64-bit" color="primary" class="font-weight-bold">
22+
CPU: {{ modelValue.cpu }} vCores
23+
</v-chip>
24+
<v-chip size="small" prepend-icon="mdi-memory" color="success" class="font-weight-bold">
25+
RAM: {{ modelValue.memory }} GB
26+
</v-chip>
27+
<v-chip size="small" prepend-icon="mdi-server" color="secondary" class="font-weight-bold">
28+
Disk Size: {{ modelValue.disk }} GB
29+
</v-chip>
30+
</div>
2831
</div>
2932

30-
<v-select
31-
class="mt-8"
32-
label="Select Node"
33-
placeholder="pick a node"
34-
:items="['node 1', 'node 2']"
35-
density="compact"
36-
variant="outlined"
37-
/>
33+
<v-expand-transition>
34+
<div v-if="activeNode">
35+
<NodeListItem active :node="activeNode!" />
36+
</div>
37+
</v-expand-transition>
38+
39+
<v-table class="border-t border-0 border-dashed" :class="{ 'table-hidden-overflow': !!loadingNode }" :style="{ maxHeight: '400px' }">
40+
<tbody>
41+
<tr
42+
v-for="(node, index) in nodes"
43+
:key="node.nodeId"
44+
>
45+
<VDivider v-if="index !== 0" />
46+
<NodeListItem
47+
:active="modelValue.nodeId === node.nodeId"
48+
:node="node"
49+
@reserve="$emit('reserve', node.nodeId!)"
50+
@pick="getNodeStoragePool(undefined, $event)"
51+
/>
52+
</tr>
53+
</tbody>
54+
</v-table>
3855
</div>
3956
</template>
4057

4158
<script setup lang="ts">
42-
defineProps<{ modelValue: ClusterNode }>()
59+
import type { HandlersNodesWithDiscount } from "~/generated/api"
60+
61+
const props = defineProps<{ modelValue: ClusterNode, allNodes: HandlersNodesWithDiscount[], nodes: HandlersNodesWithDiscount[] }>()
62+
const emit = defineEmits<{
63+
(e: "reserve", nodeId: number): void
64+
(e: "pick", nodeId: number | null): void
65+
}>()
66+
67+
const { loadingNode, setLoadingNode } = inject(NodePickCtxKey)!
68+
69+
const activeNode = computed(() => props.allNodes.filter(n => n.nodeId === props.modelValue.nodeId)[0])
70+
71+
const api = useApi()
72+
const { execute: getNodeStoragePool } = useAsyncState(async (node: HandlersNodesWithDiscount) => {
73+
setLoadingNode(node.nodeId!)
74+
emit("pick", null)
75+
/* const { data } = */await api.nodes.getNodeStoragePool(node.nodeId!.toString())
76+
return node.nodeId!
77+
}, null, {
78+
immediate: false,
79+
onSuccess(nodeId) {
80+
setLoadingNode(undefined)
81+
emit("pick", nodeId!)
82+
},
83+
onError() { setLoadingNode(undefined) },
84+
})
4385
</script>

frontend/kubecloud-v2/components/deploy-cluster/place/PlaceVMsNodes.vue

Lines changed: 97 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,105 @@
77
</h3>
88
</div>
99

10-
<v-row>
11-
<v-col v-for="(node, idx) in cluster.masters" :key="node.id" cols="6">
12-
<PlaceVMsNode v-model="$props.cluster.masters[idx]!" />
13-
</v-col>
14-
15-
<v-col v-for="(node, idx) in cluster.workers" :key="node.id" cols="6">
16-
<PlaceVMsNode v-model="$props.cluster.workers[idx]!" />
17-
</v-col>
18-
</v-row>
10+
<div class="d-flex border border-dashed rounded-lg">
11+
<v-tabs
12+
:model-value="nodeTab.join('|')"
13+
color="primary"
14+
direction="vertical"
15+
class="border-e border-0 border-dashed"
16+
mandatory
17+
@update:model-value="nodeTab = ($event.split('|') as ['masters' | 'workers', number])"
18+
>
19+
<DefineNodeTab #="{ text, value, active }">
20+
<v-tab
21+
:value="value"
22+
:text="text"
23+
:style="{ borderRadius: '0 4px 4px 0 !important', backgroundColor: active ? 'rgba(var(--v-theme-success), 0.12)' : nodeTab.join('|') === value ? 'rgba(var(--v-theme-primary), 0.12)' : undefined }"
24+
:class="{ 'text-success': active }"
25+
/>
26+
</DefineNodeTab>
27+
28+
<ReuseNodeTab
29+
v-for="(node, index) in cluster.masters"
30+
:key="node.id"
31+
:text="`${node.name} (${node.type})`"
32+
:value="`masters|${index}`"
33+
:loading="loadingNode && nodeTab.join('|') === `masters|${index}`"
34+
:disabled="loadingNode && nodeTab.join('|') !== `masters|${index}`"
35+
:active="!!node.nodeId"
36+
/>
37+
38+
<ReuseNodeTab
39+
v-for="(node, index) in cluster.workers"
40+
:key="node.id"
41+
:text="`${node.name} (${node.type})`"
42+
:value="`workers|${index}`"
43+
:loading="loadingNode && nodeTab.join('|') === `workers|${index}`"
44+
:disabled="loadingNode && nodeTab.join('|') !== `workers|${index}`"
45+
:active="!!node.nodeId"
46+
/>
47+
</v-tabs>
48+
49+
<div v-if="cluster[nodeTab[0]][nodeTab[1]]" class="flex-grow-1">
50+
<PlaceVMsNode
51+
v-model="cluster[nodeTab[0]][nodeTab[1]]!"
52+
:all-nodes="_nodes"
53+
:nodes="nodes"
54+
@reserve="onReserveNode"
55+
@pick="$props.cluster[nodeTab[0]][nodeTab[1]]!.nodeId = $event"
56+
/>
57+
</div>
58+
</div>
1959
</v-card>
2060
</template>
2161

2262
<script setup lang="ts">
23-
defineProps<{ cluster: ClusterForm }>()
63+
import type { HandlersNodesWithDiscount, ListRentableNodes200Response } from "~/generated/api"
64+
65+
const props = defineProps<{ cluster: ClusterForm }>()
66+
67+
const [DefineNodeTab, ReuseNodeTab] = createReusableTemplate({
68+
props: { text: String, value: String, active: Boolean },
69+
})
70+
71+
const { loadingNode } = inject(NodePickCtxKey)!
72+
const nodeTab = ref<["masters" | "workers", number]>(["masters", 0])
73+
74+
watchImmediate(() => {
75+
const [t, i] = nodeTab.value
76+
return props.cluster[t][i]
77+
}, (v) => {
78+
if (!v) {
79+
nodeTab.value = ["masters", 0]
80+
}
81+
})
82+
83+
const api = useApi()
84+
function sortNodes(nodes: HandlersNodesWithDiscount[]): HandlersNodesWithDiscount[] {
85+
return nodes.toSorted((a, b) => {
86+
const _a = (a.rented ? 1 : 0) + (a.rentable ? 1 : 0)
87+
const _b = (b.rented ? 1 : 0) + (b.rentable ? 1 : 0)
88+
return _b - _a
89+
})
90+
}
91+
92+
const { state: _nodes } = useAsyncState(async () => {
93+
const { data } = await api.nodes.listNodes()
94+
const nodes = (data as ListRentableNodes200Response).data?.nodes ?? []
95+
return sortNodes(nodes)
96+
}, [], { resetOnExecute: false })
97+
98+
function onReserveNode(nodeId: number): void {
99+
// const __nodes = _nodes.value.map(n => n.nodeId === nodeId ? { ...n, rented: true } : n)
100+
// _nodes.value = sortNodes(__nodes)
101+
_nodes.value = _nodes.value.map(n => n.nodeId === nodeId ? { ...n, rented: true } : n)
102+
}
103+
104+
const nodes = computed(() => {
105+
const pickedNodes = props.cluster.masters.concat(props.cluster.workers).map(n => n.nodeId)
106+
return _nodes.value.filter((n) => {
107+
const regionFilter = !props.cluster.region || n.location?.region === props.cluster.region
108+
return regionFilter && !pickedNodes.includes(n.nodeId!)
109+
})
110+
})
24111
</script>

0 commit comments

Comments
 (0)