Dashboard.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. <template>
  2. <div>
  3. <a-divider orientation="left">{{$t('compute.text_572')}}</a-divider>
  4. <a-row class="mb-2" :gutter="{ lg: 24, xl: 12, xxl: 24 }">
  5. <a-col class="mb-3" :lg="12" :xl="6" v-for="(item, index) in progressList" :key="item.label">
  6. <progress-card :progress="item" v-if="index !== 3" :card-style="{height: '312px'}" />
  7. <ring-card v-else :options="item" height="230px" />
  8. </a-col>
  9. </a-row>
  10. <!-- <a-divider class="mt-3" orientation="left">{{$t('compute.text_573')}}</a-divider>
  11. <a-spin :spinning="loading">
  12. <a-row class="mb-2" :gutter="{ lg: 24, xl: 12, xxl: 24 }">
  13. <a-col class="mb-3" :lg="12" :xl="6" v-for="item in gaugeList" :key="item.label">
  14. <progress-card
  15. :progress="item"
  16. :progress-props="item.progressProps"
  17. :unit="item.unit"
  18. :numerifyFloat="item.numerifyFloat"
  19. :percentFormat="item.percentFormat" />
  20. </a-col>
  21. </a-row>
  22. </a-spin> -->
  23. <a-divider class="mt-3" orientation="left">TOP5</a-divider>
  24. <a-spin :spinning="top5Loading">
  25. <a-row class="mb-2" :gutter="{ lg: 24, xl: 12, xxl: 24 }">
  26. <a-col class="mb-3" :lg="12" :xl="8" v-for="item in topList" :key="item.name">
  27. <top5 :topMsg="item" />
  28. </a-col>
  29. </a-row>
  30. </a-spin>
  31. </div>
  32. </template>
  33. <script>
  34. import _ from 'lodash'
  35. import numerify from 'numerify'
  36. import ProgressCard from '@/sections/ProgressCard'
  37. import RingCard from '@/sections/RingCard'
  38. import { sizestrWithUnit, getRequestT } from '@/utils/utils'
  39. import Top5 from '@/sections/Top5'
  40. import { getSignature } from '@/utils/crypto'
  41. import { GAUGEMSG, HOST_TOP5, HOST_INFO_OPTS } from '../constants'
  42. import { getHostSpecInfo } from '../utils/index'
  43. export default {
  44. name: 'HostDashboard',
  45. components: {
  46. ProgressCard,
  47. Top5,
  48. RingCard,
  49. },
  50. props: {
  51. resId: {
  52. type: String,
  53. required: true,
  54. },
  55. data: {
  56. type: Object,
  57. required: true,
  58. },
  59. },
  60. data () {
  61. return {
  62. gaugeList: [],
  63. loading: false,
  64. top5Loading: false,
  65. topList: [],
  66. progressListPercent: [0, 0, 0],
  67. }
  68. },
  69. computed: {
  70. topType () {
  71. if (this.data.host_type === 'hypervisor') return 'isKvm'
  72. return 'noKvm'
  73. },
  74. progressList () {
  75. const data = this.data
  76. const obj = getHostSpecInfo(data)
  77. const tempList = new Array(3)
  78. tempList[0] = (() => {
  79. return {
  80. title: this.$t('compute.text_563_1'),
  81. percent: obj.cpu_commit / obj.cpu_count_virtual,
  82. msg: {
  83. current: obj.cpu_commit,
  84. totalLabel: this.$t('compute.virtual_total'),
  85. total: obj.cpu_count_virtual,
  86. },
  87. }
  88. })()
  89. tempList[1] = (() => {
  90. return {
  91. title: this.$t('compute.text_564_1'),
  92. percent: obj.mem_commit / obj.mem_size_virtual,
  93. msg: {
  94. current: sizestrWithUnit(obj.mem_commit, 'M', 1024),
  95. totalLabel: this.$t('compute.virtual_total'),
  96. total: `${sizestrWithUnit(obj.mem_size_virtual, 'M', 1024)}`,
  97. },
  98. }
  99. })()
  100. tempList[2] = (() => {
  101. return {
  102. title: this.$t('compute.text_565_1'),
  103. percent: obj.storage_commit / obj.storage_size_virtual,
  104. msg: {
  105. current: sizestrWithUnit(obj.storage_commit, 'M', 1024),
  106. totalLabel: this.$t('compute.virtual_total'),
  107. total: `${sizestrWithUnit(obj.storage_size_virtual, 'M', 1024)}`,
  108. },
  109. }
  110. })()
  111. tempList[3] = (() => {
  112. const current = obj.running_guests || 0
  113. const ready = obj.ready_guests || 0
  114. const pend = obj.pending_deleted_guests || 0
  115. const other = obj.other_guests || 0
  116. const total = current + ready + pend + other
  117. return {
  118. pieData: [
  119. {
  120. name: `${this.$t('common.text00051')}: ${current}`,
  121. value: current,
  122. },
  123. {
  124. name: `${this.$t('status.server.ready')}: ${ready}`,
  125. value: ready,
  126. },
  127. {
  128. name: `${this.$t('common.text00052')}: ${pend}`,
  129. value: pend,
  130. },
  131. {
  132. name: `${this.$t('common.text00053')}: ${other}`,
  133. value: other,
  134. },
  135. ],
  136. title: this.$t('common.text00054'),
  137. total: total,
  138. }
  139. })()
  140. tempList[4] = (() => {
  141. return {
  142. title: this.$t('compute.text_563'),
  143. percent: this.progressListPercent[0],
  144. msg: {
  145. current: parseInt(obj.cpu_count * this.progressListPercent[0]) < obj.cpu_count * this.progressListPercent[0] ? Math.floor(parseInt(obj.cpu_count * this.progressListPercent[0]) + 1, obj.cpu_count) : obj.cpu_count * this.progressListPercent[0], // 向上取整
  146. totalLabel: this.$t('compute.actual_total'),
  147. currentLabel: this.$t('compute.actual_used'),
  148. total: `${obj.cpu_count} (${this.$t('compute.text_563')}: ${obj.cpu_count - obj.cpu_reserved}, ${this.$t('compute.reserved')}: ${obj.cpu_reserved})`,
  149. },
  150. }
  151. })()
  152. tempList[5] = (() => {
  153. return {
  154. title: this.$t('compute.text_564'),
  155. percent: this.progressListPercent[1],
  156. msg: {
  157. current: sizestrWithUnit(obj.mem_size * this.progressListPercent[1], 'M', 1024),
  158. totalLabel: this.$t('compute.actual_total'),
  159. currentLabel: this.$t('compute.actual_used'),
  160. total: `${sizestrWithUnit(obj.mem_size, 'M', 1024)} (${this.$t('compute.text_564')}: ${sizestrWithUnit(obj.mem_size - obj.mem_reserved, 'M', 1024)}, ${this.$t('compute.reserved')}: ${sizestrWithUnit(obj.mem_reserved, 'M', '1024')})`,
  161. },
  162. }
  163. })()
  164. tempList[6] = (() => {
  165. return {
  166. title: this.$t('compute.text_565'),
  167. percent: this.progressListPercent[2],
  168. msg: {
  169. current: sizestrWithUnit(obj.storage_size * this.progressListPercent[2], 'M', 1024),
  170. totalLabel: this.$t('compute.actual_total'),
  171. currentLabel: this.$t('compute.actual_used'),
  172. total: `${sizestrWithUnit(obj.storage_size, 'M', 1024)} (${this.$t('compute.text_565')}: ${sizestrWithUnit(obj.storage_size, 'M', '1024')})`,
  173. },
  174. }
  175. })()
  176. return tempList
  177. },
  178. },
  179. created () {
  180. // this.fetchGaugeData()
  181. this.fetchUsedPercent()
  182. this.fetchTop5Data()
  183. },
  184. methods: {
  185. async fetchUsedPercent () {
  186. try {
  187. const reqList = HOST_INFO_OPTS.map(opt => {
  188. return new this.$Manager('unifiedmonitors', 'v1')
  189. .performAction({
  190. id: 'query',
  191. action: '',
  192. data: this.genQueryData(opt),
  193. params: { $t: getRequestT() },
  194. })
  195. })
  196. const res = await Promise.all(reqList)
  197. const list = []
  198. res.forEach((r, index) => {
  199. const { series = [{}] } = (r.data || {})
  200. const { points = [] } = (series[0] || {})
  201. if (points.length) {
  202. console.log(points)
  203. const percent = points.reduce((acc, cur) => acc + cur[0], 0) / points.length
  204. list.push(percent / 100)
  205. } else {
  206. list.push(0)
  207. }
  208. })
  209. this.progressListPercent = list
  210. } catch (err) {
  211. console.error(err)
  212. }
  213. },
  214. _getSeriesMax (arr) {
  215. if (!arr) return []
  216. const data = arr.map(item => {
  217. this.vmName = item.tags.vm_name
  218. return {
  219. name: this.vmName,
  220. link: '/a/v',
  221. value: Math.max.apply(null, item.points.map(i => i[0])),
  222. }
  223. })
  224. return data
  225. },
  226. async fetchTop5Data () {
  227. const top5ResourceData = HOST_TOP5[this.topType]
  228. this.top5Loading = true
  229. this.topList = []
  230. for (let i = 0; i < top5ResourceData.length; i++) {
  231. const val = top5ResourceData[i]
  232. try {
  233. const { data } = await new this.$Manager('unifiedmonitors', 'v1')
  234. .performAction({
  235. id: 'query',
  236. action: '',
  237. data: this.genQueryData(val),
  238. params: { $t: getRequestT() },
  239. })
  240. const series = this._getSeriesMax(data.series)
  241. this.topList.push({
  242. // metric: TOP5REQDATA[i].metrics[0].name[0], // 需要 link 跳转页面的时候可以加上
  243. title: val.label,
  244. data: series,
  245. unit: val.unit,
  246. })
  247. } catch (error) {
  248. this.top5Loading = false
  249. throw error
  250. }
  251. }
  252. this.top5Loading = false
  253. },
  254. async fetchGaugeData () {
  255. this.loading = true
  256. for (let i = 0; i < GAUGEMSG.length; i++) {
  257. try {
  258. const value = GAUGEMSG[i]
  259. const { data } = await new this.$Manager('unifiedmonitors', 'v1')
  260. .performAction({
  261. id: 'query',
  262. action: '',
  263. data: this.genGaugeQueryData(value),
  264. params: { $t: getRequestT() },
  265. })
  266. const series = data.series
  267. const values = _.get(series, '[0].points')
  268. if (values && values.length) {
  269. const temValues = values.map(v => (v[0] || 0))
  270. const maxNum = temValues.length ? Math.max.apply(null, temValues) : 0
  271. let unit = '%'
  272. let numerifyFloat = '0.00'
  273. let percent = maxNum / 100
  274. if (value.label === this.$t('compute.text_517')) {
  275. unit = ''
  276. numerifyFloat = '0.0000'
  277. percent = maxNum
  278. var percentFormat = this.percentFormat
  279. }
  280. this.gaugeList.push({
  281. title: value.label,
  282. percent,
  283. unit,
  284. numerifyFloat,
  285. percentFormat,
  286. progressProps: {
  287. type: 'dashboard',
  288. },
  289. })
  290. }
  291. } catch (error) {
  292. this.loading = false
  293. throw error
  294. }
  295. }
  296. this.loading = false
  297. },
  298. percentFormat (vm) {
  299. const per = (vm.percent || 0) / 100
  300. const oversell = per > 100 ? <a-tag color="red">{this.$t('common_714')}</a-tag> : null
  301. return (<div>{oversell}<div class="mt-2 text-color">{ numerify(per * 100, vm.numerifyFloat) }{ vm.unit }</div></div>)
  302. },
  303. genGaugeQueryData (val) {
  304. const select = [
  305. {
  306. type: 'field',
  307. params: [val.sql.key],
  308. },
  309. { // 对应 mean(val.seleteItem)
  310. type: 'max',
  311. params: [],
  312. },
  313. ]
  314. let tags = []
  315. if (val.sql.db === 'net') {
  316. tags = [
  317. {
  318. key: 'interface',
  319. value: 'eth0',
  320. operator: '=',
  321. },
  322. {
  323. key: 'host_id',
  324. value: this.resId,
  325. operator: '=',
  326. },
  327. ]
  328. } else { // 其他类型宿主机(esxi、openstack、zstack)
  329. tags.push({
  330. key: 'host_id',
  331. value: this.resId,
  332. operator: '=',
  333. })
  334. }
  335. const data = {
  336. metric_query: [
  337. {
  338. model: {
  339. measurement: val.sql.db,
  340. select: [select],
  341. tags,
  342. },
  343. },
  344. ],
  345. scope: this.$store.getters.scope,
  346. from: '1m',
  347. interval: '1m',
  348. unit: true,
  349. }
  350. data.signature = getSignature(data)
  351. return data
  352. },
  353. genQueryData (val) {
  354. const select = [
  355. {
  356. type: 'field',
  357. params: [val.seleteItem],
  358. },
  359. { // 对应 mean(val.seleteItem)
  360. type: 'max',
  361. params: [],
  362. },
  363. ]
  364. let tags = []
  365. if (this.topType === 'isKvm') { // kvm 型宿主机
  366. tags = [
  367. {
  368. key: 'host_id',
  369. value: this.data.id,
  370. operator: '=',
  371. },
  372. ]
  373. } else { // 其他类型宿主机(esxi、openstack、zstack)
  374. tags.push({
  375. key: 'host_id',
  376. value: this.data.id,
  377. operator: '=',
  378. })
  379. }
  380. const data = {
  381. metric_query: [
  382. {
  383. model: {
  384. measurement: val.fromItem,
  385. select: [select],
  386. group_by: [{ type: 'tag', params: ['vm_name'] }],
  387. tags,
  388. },
  389. },
  390. ],
  391. scope: this.$store.getters.scope,
  392. from: '30m',
  393. interval: '1m',
  394. unit: true,
  395. }
  396. data.signature = getSignature(data)
  397. return data
  398. },
  399. },
  400. }
  401. </script>