diff options
Diffstat (limited to 'src/components/services/Proxmox.vue')
-rw-r--r-- | src/components/services/Proxmox.vue | 314 |
1 files changed, 186 insertions, 128 deletions
diff --git a/src/components/services/Proxmox.vue b/src/components/services/Proxmox.vue index 2b533db..8136050 100644 --- a/src/components/services/Proxmox.vue +++ b/src/components/services/Proxmox.vue | |||
@@ -1,135 +1,193 @@ | |||
1 | <template> | 1 | <template> |
2 | <Generic :item="item"> | 2 | <Generic :item="item"> |
3 | <template #content> | 3 | <template #content> |
4 | <p class="title is-4">{{ item.name }}</p> | 4 | <p class="title is-4">{{ item.name }}</p> |
5 | <p class="subtitle is-6"> | 5 | <p class="subtitle is-6"> |
6 | <template v-if="item.subtitle"> | 6 | <template v-if="item.subtitle"> |
7 | {{ item.subtitle }} | 7 | {{ item.subtitle }} |
8 | </template> | 8 | </template> |
9 | <template v-else-if="vms"> | 9 | <template v-else-if="vms"> |
10 | <div v-if="loading"> | 10 | <div v-if="loading"> |
11 | <strong>Loading...</strong> | 11 | <strong>Loading...</strong> |
12 | </div> | 12 | </div> |
13 | <div v-else-if="error"> | 13 | <div v-else-if="error"> |
14 | <strong class="danger">Error loading info</strong> | 14 | <strong class="danger">Error loading info</strong> |
15 | </div> | 15 | </div> |
16 | <div v-else class="metrics" :class="{'is-size-7-mobile': item.small_font_on_small_screens, 'is-small': item.small_font_on_desktop}"> | 16 | <div |
17 | <span v-if="isValueShown('vms')" class="margined">VMs: <span class="is-number"><span class="has-text-weight-bold">{{ vms.running }}</span><span v-if="isValueShown('vms_total')">/{{vms.total}}</span></span></span> | 17 | v-else |
18 | <span v-if="isValueShown('lxcs')" class="margined">LXCs: <span class="is-number"><span class="has-text-weight-bold">{{ lxcs.running }}</span><span v-if="isValueShown('lxcs_total')">/{{lxcs.total}}</span></span></span> | 18 | class="metrics" |
19 | <span v-if="isValueShown('disk')" class="margined">Disk: <span class="has-text-weight-bold is-number" :class="statusClass(diskUsed)">{{ diskUsed }}%</span></span> | 19 | :class="{ |
20 | <span v-if="isValueShown('mem')" class="margined">Mem: <span class="has-text-weight-bold is-number" :class="statusClass(memoryUsed)">{{ memoryUsed }}%</span></span> | 20 | 'is-size-7-mobile': item.small_font_on_small_screens, |
21 | <span v-if="isValueShown('cpu')" class="margined">CPU: <span class="has-text-weight-bold is-number" :class="statusClass(cpuUsed)">{{ cpuUsed }}%</span></span> | 21 | 'is-small': item.small_font_on_desktop, |
22 | </div> | 22 | }" |
23 | </template> | 23 | > |
24 | </p> | 24 | <span v-if="isValueShown('vms')" class="margined" |
25 | </template> | 25 | >VMs: |
26 | <template #indicator> | 26 | <span class="is-number" |
27 | <i v-if="loading" class="fa fa-circle-notch fa-spin fa-2xl"></i> | 27 | ><span class="has-text-weight-bold">{{ vms.running }}</span |
28 | <i v-if="error" class="fa fa-exclamation-circle fa-2xl danger"></i> | 28 | ><span v-if="isValueShown('vms_total')" |
29 | </template> | 29 | >/{{ vms.total }}</span |
30 | </Generic> | 30 | ></span |
31 | </template> | 31 | ></span |
32 | 32 | > | |
33 | <script> | 33 | <span v-if="isValueShown('lxcs')" class="margined" |
34 | import service from "@/mixins/service.js"; | 34 | >LXCs: |
35 | import Generic from "./Generic.vue"; | 35 | <span class="is-number" |
36 | 36 | ><span class="has-text-weight-bold">{{ lxcs.running }}</span | |
37 | export default { | 37 | ><span v-if="isValueShown('lxcs_total')" |
38 | name: "Proxmox", | 38 | >/{{ lxcs.total }}</span |
39 | mixins: [service], | 39 | ></span |
40 | props: { | 40 | ></span |
41 | item: Object, | 41 | > |
42 | <span v-if="isValueShown('disk')" class="margined" | ||
43 | >Disk: | ||
44 | <span | ||
45 | class="has-text-weight-bold is-number" | ||
46 | :class="statusClass(diskUsed)" | ||
47 | >{{ diskUsed }}%</span | ||
48 | ></span | ||
49 | > | ||
50 | <span v-if="isValueShown('mem')" class="margined" | ||
51 | >Mem: | ||
52 | <span | ||
53 | class="has-text-weight-bold is-number" | ||
54 | :class="statusClass(memoryUsed)" | ||
55 | >{{ memoryUsed }}%</span | ||
56 | ></span | ||
57 | > | ||
58 | <span v-if="isValueShown('cpu')" class="margined" | ||
59 | >CPU: | ||
60 | <span | ||
61 | class="has-text-weight-bold is-number" | ||
62 | :class="statusClass(cpuUsed)" | ||
63 | >{{ cpuUsed }}%</span | ||
64 | ></span | ||
65 | > | ||
66 | </div> | ||
67 | </template> | ||
68 | </p> | ||
69 | </template> | ||
70 | <template #indicator> | ||
71 | <i v-if="loading" class="fa fa-circle-notch fa-spin fa-2xl"></i> | ||
72 | <i v-if="error" class="fa fa-exclamation-circle fa-2xl danger"></i> | ||
73 | </template> | ||
74 | </Generic> | ||
75 | </template> | ||
76 | |||
77 | <script> | ||
78 | import service from "@/mixins/service.js"; | ||
79 | import Generic from "./Generic.vue"; | ||
80 | |||
81 | export default { | ||
82 | name: "Proxmox", | ||
83 | mixins: [service], | ||
84 | props: { | ||
85 | item: Object, | ||
86 | }, | ||
87 | components: { | ||
88 | Generic, | ||
89 | }, | ||
90 | data: () => ({ | ||
91 | vms: { | ||
92 | total: 0, | ||
93 | running: 0, | ||
42 | }, | 94 | }, |
43 | components: { | 95 | lxcs: { |
44 | Generic, | 96 | total: 0, |
97 | running: 0, | ||
45 | }, | 98 | }, |
46 | data: () => ({ | 99 | memoryUsed: 0, |
47 | vms: { | 100 | diskUsed: 0, |
48 | total: 0, | 101 | cpuUsed: 0, |
49 | running: 0 | 102 | hide: [], |
50 | }, | 103 | error: false, |
51 | lxcs: { | 104 | loading: true, |
52 | total: 0, | 105 | }), |
53 | running: 0 | 106 | created() { |
54 | }, | 107 | if (this.item.hide) this.hide = this.item.hide; |
55 | memoryUsed: 0, | 108 | this.fetchStatus(); |
56 | diskUsed: 0, | 109 | }, |
57 | cpuUsed: 0, | 110 | methods: { |
58 | hide: [], | 111 | statusClass(value) { |
59 | error: false, | 112 | if (value > this.item.danger_value) return "danger"; |
60 | loading: true | 113 | if (value > this.item.warning_value) return "warning"; |
61 | }), | 114 | return "healthy"; |
62 | created() { | ||
63 | if (this.item.hide) this.hide = this.item.hide; | ||
64 | this.fetchStatus(); | ||
65 | }, | 115 | }, |
66 | methods: { | 116 | fetchStatus: async function () { |
67 | statusClass(value) { | 117 | try { |
68 | if (value > this.item.danger_value) return 'danger'; | 118 | const options = { |
69 | if (value > this.item.warning_value) return 'warning'; | 119 | headers: { |
70 | return 'healthy' | 120 | Authorization: this.item.api_token, |
71 | }, | 121 | }, |
72 | fetchStatus: async function () { | 122 | }; |
73 | try { | 123 | const status = await this.fetch( |
74 | const options = { | 124 | `/api2/json/nodes/${this.item.node}/status`, |
75 | headers: { | 125 | options |
76 | "Authorization": this.item.api_token | 126 | ); |
77 | } | 127 | // main metrics: |
78 | } | 128 | const decimalsToShow = this.item.hide_decimals ? 0 : 1; |
79 | const status = await this.fetch(`/api2/json/nodes/${this.item.node}/status`, options); | 129 | this.memoryUsed = ( |
80 | // main metrics: | 130 | (status.data.memory.used * 100) / |
81 | const decimalsToShow = this.item.hide_decimals ? 0 : 1; | 131 | status.data.memory.total |
82 | this.memoryUsed = ( (status.data.memory.used * 100) / status.data.memory.total ).toFixed(decimalsToShow); | 132 | ).toFixed(decimalsToShow); |
83 | this.diskUsed = ( (status.data.rootfs.used * 100) / status.data.rootfs.total ).toFixed(decimalsToShow); | 133 | this.diskUsed = ( |
84 | this.cpuUsed = (status.data.cpu * 100).toFixed(decimalsToShow); | 134 | (status.data.rootfs.used * 100) / |
85 | // vms: | 135 | status.data.rootfs.total |
86 | if (this.isValueShown('vms')) { | 136 | ).toFixed(decimalsToShow); |
87 | const vms = await this.fetch(`/api2/json/nodes/${this.item.node}/qemu`, options); | 137 | this.cpuUsed = (status.data.cpu * 100).toFixed(decimalsToShow); |
88 | this.parseVMsAndLXCs(vms, this.vms); | 138 | // vms: |
89 | } | 139 | if (this.isValueShown("vms")) { |
90 | // lxc containers: | 140 | const vms = await this.fetch( |
91 | if (this.isValueShown('lxcs')) { | 141 | `/api2/json/nodes/${this.item.node}/qemu`, |
92 | const lxcs = await this.fetch(`/api2/json/nodes/${this.item.node}/lxc`, options); | 142 | options |
93 | this.parseVMsAndLXCs(lxcs, this.lxcs); | 143 | ); |
94 | } | 144 | this.parseVMsAndLXCs(vms, this.vms); |
95 | this.error = false; | ||
96 | } catch(err) { | ||
97 | console.log(err); | ||
98 | this.error = true; | ||
99 | } | 145 | } |
100 | this.loading = false; | 146 | // lxc containers: |
101 | }, | 147 | if (this.isValueShown("lxcs")) { |
102 | parseVMsAndLXCs(items, value) { | 148 | const lxcs = await this.fetch( |
103 | value.total += items.data.length; | 149 | `/api2/json/nodes/${this.item.node}/lxc`, |
104 | value.running += items.data.filter( i => i.status === 'running' ).length; | 150 | options |
105 | // if no vms, hide this value: | 151 | ); |
106 | if (value.total == 0) this.hide.push('lxcs'); | 152 | this.parseVMsAndLXCs(lxcs, this.lxcs); |
107 | }, | 153 | } |
108 | isValueShown(value) { | 154 | this.error = false; |
109 | return this.hide.indexOf(value) == -1; | 155 | } catch (err) { |
156 | console.log(err); | ||
157 | this.error = true; | ||
110 | } | 158 | } |
159 | this.loading = false; | ||
160 | }, | ||
161 | parseVMsAndLXCs(items, value) { | ||
162 | value.total += items.data.length; | ||
163 | value.running += items.data.filter((i) => i.status === "running").length; | ||
164 | // if no vms, hide this value: | ||
165 | if (value.total == 0) this.hide.push("lxcs"); | ||
166 | }, | ||
167 | isValueShown(value) { | ||
168 | return this.hide.indexOf(value) == -1; | ||
111 | }, | 169 | }, |
112 | }; | 170 | }, |
113 | </script> | 171 | }; |
114 | 172 | </script> | |
115 | <style scoped lang="scss"> | 173 | |
116 | .is-number { | 174 | <style scoped lang="scss"> |
117 | font-family: "Lato" | 175 | .is-number { |
118 | } | 176 | font-family: "Lato"; |
119 | .healthy { | 177 | } |
120 | color: green | 178 | .healthy { |
121 | } | 179 | color: green; |
122 | .warning { | 180 | } |
123 | color: orange | 181 | .warning { |
124 | } | 182 | color: orange; |
125 | .danger { | 183 | } |
126 | color: red | 184 | .danger { |
127 | } | 185 | color: red; |
128 | .metrics .margined:not(:first-child) { | 186 | } |
129 | margin-left: 0.3rem; | 187 | .metrics .margined:not(:first-child) { |
130 | } | 188 | margin-left: 0.3rem; |
131 | .is-small { | 189 | } |
132 | font-size: small; | 190 | .is-small { |
133 | } | 191 | font-size: small; |
134 | </style> | 192 | } |
135 | \ No newline at end of file | 193 | </style> |