mirror of https://github.com/apache/cloudstack.git
UI: Admin, account and project dashboard improvements (#7956)
This PR aims at improving the CloudStack dashboard and introduces the following:
Admin dashboard: six cards that are responsive to screen sizes and show zone specific compute, storage and network allocation, as well as instance/hosts stats, alerts and events. Now, by default, the admin dashboard shows aggegate data from all zones, with option for admin to select individual zone to see individual zone stats
Account/project dashboard: six cards that are responsive to screen sizes and show account or project specific resource lists/counts, and limits shown in three cards as (a) compute (with running stopped instances), (b) storage and (c) network allocation, an admin-defined links/docs card (via config.json) and events cards. Admin is allowed to configure project limits on project dashboards.
A global create button on the top global header/user-menu to allow for quick actions such as to deploy a VM, CKS cluster and create a volume (more actions can be added as desired via code changes) etc.
Doc PR - apache/cloudstack-documentation#349
---------
Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
82b981854b
commit
5d9ae31f1b
File diff suppressed because it is too large
Load Diff
|
|
@ -34,13 +34,13 @@
|
|||
"test:unit": "vue-cli-service test:unit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^1.3.0",
|
||||
"@fortawesome/free-brands-svg-icons": "^5.15.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.15.2",
|
||||
"@fortawesome/vue-fontawesome": "^3.0.0-4",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.4.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.4.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
||||
"@fortawesome/vue-fontawesome": "^3.0.3",
|
||||
"@vue-js-cron/ant": "^1.1.3",
|
||||
"@vue-js-cron/core": "^3.7.1",
|
||||
"ant-design-vue": "^3.2.9",
|
||||
"ant-design-vue": "^3.2.20",
|
||||
"antd": "^4.21.4",
|
||||
"antd-theme-webpack-plugin": "^1.3.9",
|
||||
"axios": "^0.21.1",
|
||||
|
|
|
|||
|
|
@ -59,6 +59,36 @@
|
|||
"jp": "label.japanese.keyboard",
|
||||
"sc": "label.simplified.chinese.keyboard"
|
||||
},
|
||||
"userCard": {
|
||||
"title": "label.help",
|
||||
"icon": "question-circle-outlined",
|
||||
"links": [
|
||||
{
|
||||
"title": "Documentation",
|
||||
"text": "CloudStack documentation website",
|
||||
"link": "https://docs.cloudstack.apache.org/en/latest/",
|
||||
"icon": "read-outlined"
|
||||
},
|
||||
{
|
||||
"title": "API Documentation",
|
||||
"text": "Refer to API documentation",
|
||||
"link": "https://cloudstack.apache.org/api.html",
|
||||
"icon": "api-outlined"
|
||||
},
|
||||
{
|
||||
"title": "Email Support",
|
||||
"text": "Join CloudStack users mailing list to seek and provide support",
|
||||
"link": "mailto:users-subscribe@cloudstack.apache.org",
|
||||
"icon": "mail-outlined"
|
||||
},
|
||||
{
|
||||
"title": "Report Issue",
|
||||
"text": "Submit a bug or improvement request",
|
||||
"link": "https://github.com/apache/cloudstack/issues/new",
|
||||
"icon": "bug-outlined"
|
||||
}
|
||||
]
|
||||
},
|
||||
"plugins": [],
|
||||
"basicZoneEnabled": true,
|
||||
"multipleServer": false,
|
||||
|
|
|
|||
|
|
@ -538,6 +538,7 @@
|
|||
"label.cpuused": "CPU utilized",
|
||||
"label.cpuusedghz": "CPU used",
|
||||
"label.create": "Create",
|
||||
"label.create.instance": "Create cloud server",
|
||||
"label.create.account": "Create account",
|
||||
"label.create.backup": "Start backup",
|
||||
"label.create.network": "Create new network",
|
||||
|
|
@ -2193,6 +2194,7 @@
|
|||
"label.volumetotal": "Volume",
|
||||
"label.volumetype": "Volume Type",
|
||||
"label.vpc": "VPC",
|
||||
"label.vpcs": "VPCs",
|
||||
"label.vpc.id": "VPC ID",
|
||||
"label.vpc.offerings": "VPC offerings",
|
||||
"label.vpc.virtual.router": "VPC virtual router",
|
||||
|
|
|
|||
|
|
@ -1,153 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:ns4="http://ns.adobe.com/SaveForWeb/1.0/"
|
||||
xmlns:ns3="http://ns.adobe.com/Variables/1.0/"
|
||||
xmlns:ns2="http://ns.adobe.com/AdobeIllustrator/10.0/"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
ns2:viewOrigin="262 450"
|
||||
ns2:rulerOrigin="0 0"
|
||||
ns2:pageBounds="0 792 612 0"
|
||||
viewBox="0 0 55.999999 56.000069"
|
||||
overflow="visible"
|
||||
enable-background="new 0 0 87.041 108.445"
|
||||
xml:space="preserve"
|
||||
version="1.1"
|
||||
id="svg31"
|
||||
sodipodi:docname="debian.svg"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
style="overflow:visible"><defs
|
||||
id="defs35" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1866"
|
||||
inkscape:window-height="1017"
|
||||
id="namedview33"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.1762184"
|
||||
inkscape:cx="-62.475298"
|
||||
inkscape:cy="28.002047"
|
||||
inkscape:window-x="54"
|
||||
inkscape:window-y="26"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g28"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata2">
|
||||
<ns3:variableSets>
|
||||
<ns3:variableSet
|
||||
varSetName="binding1"
|
||||
locked="none">
|
||||
<ns3:variables />
|
||||
<ns3:sampleDataSets />
|
||||
</ns3:variableSet>
|
||||
</ns3:variableSets>
|
||||
<ns4:sfw>
|
||||
<ns4:slices />
|
||||
<ns4:sliceSourceBounds
|
||||
y="341.555"
|
||||
x="262"
|
||||
width="87.041"
|
||||
height="108.445"
|
||||
bottomLeftOrigin="true" />
|
||||
</ns4:sfw>
|
||||
<rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata>
|
||||
<g
|
||||
id="Layer_1"
|
||||
ns2:layer="yes"
|
||||
ns2:dimmedPercent="50"
|
||||
ns2:rgbTrio="#4F008000FFFF"
|
||||
transform="translate(-20.985947,-26.22447)">
|
||||
<g
|
||||
id="g28">
|
||||
<path
|
||||
ns2:knockout="Off"
|
||||
d="m 53.872479,55.811055 c -0.927921,0.01291 0.175567,0.478161 1.386977,0.664571 0.334609,-0.261284 0.638236,-0.525667 0.908815,-0.78282 -0.75442,0.184861 -1.522266,0.188992 -2.295792,0.118249"
|
||||
id="path4"
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.51637238"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
ns2:knockout="Off"
|
||||
d="m 58.852891,54.569696 c 0.552518,-0.762682 0.955289,-1.597656 1.097291,-2.461031 -0.123929,0.615516 -0.458022,1.146863 -0.772493,1.707644 -1.734495,1.092127 -0.163174,-0.648564 -10e-4,-1.310037 -1.865137,2.347429 -0.256121,1.407631 -0.323766,2.063424"
|
||||
id="path6"
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.51637238"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
ns2:knockout="Off"
|
||||
d="m 60.691176,49.786022 c 0.112053,-1.670981 -0.328929,-1.142732 -0.477128,-0.505012 0.172985,0.08985 0.309824,1.177846 0.477128,0.505012"
|
||||
id="path8"
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.51637238"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
ns2:knockout="Off"
|
||||
d="m 50.353918,26.946873 c 0.495201,0.08882 1.069924,0.156977 0.98937,0.275226 0.541674,-0.118765 0.664571,-0.228236 -0.98937,-0.275226"
|
||||
id="path10"
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.51637238"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
ns2:knockout="Off"
|
||||
d="m 51.343288,27.222099 -0.350101,0.07229 0.325831,-0.02892 0.02427,-0.04338"
|
||||
id="path12"
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.51637238"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
ns2:knockout="Off"
|
||||
d="m 66.784887,50.419611 c 0.05525,1.500578 -0.438917,2.228663 -0.884546,3.517529 l -0.801926,0.400705 c -0.65631,1.274407 0.06351,0.809155 -0.406386,1.822794 -1.024482,0.910881 -3.109077,2.850376 -3.776231,3.027491 -0.486939,-0.01085 0.329962,-0.574722 0.436851,-0.79573 -1.371485,0.941864 -1.100389,1.413828 -3.197894,1.985969 l -0.06145,-0.136323 c -5.173018,2.433663 -12.358856,-2.389255 -12.26436,-8.969904 -0.05525,0.417745 -0.156977,0.313438 -0.271612,0.482292 -0.266964,-3.385854 1.563576,-6.786682 4.650966,-8.175207 3.019746,-1.494898 6.559995,-0.881448 8.723078,1.13447 -1.188172,-1.556347 -3.553158,-3.206156 -6.356027,-3.051761 -2.745552,0.04337 -5.313988,1.788197 -6.171166,3.682251 -1.406598,0.885579 -1.569772,3.413738 -2.182706,3.876408 -0.824647,6.060662 1.551183,8.679186 5.570109,11.759347 0.632556,0.426524 0.178148,0.49107 0.263866,0.815869 -1.335339,-0.625327 -2.558109,-1.569256 -3.563486,-2.724897 0.533413,0.780755 1.109168,1.539822 1.853261,2.136232 -1.258916,-0.426523 -2.940741,-3.050728 -3.431811,-3.157617 2.170313,3.885702 8.805182,6.814566 12.279335,5.361494 -1.607467,0.05938 -3.64972,0.03305 -5.455991,-0.634621 -0.758551,-0.390378 -1.790263,-1.199017 -1.605918,-1.350314 4.741331,1.771157 9.639123,1.341535 13.741702,-1.94724 1.043588,-0.81277 2.183738,-2.195615 2.513184,-2.214721 -0.496234,0.746158 0.08469,0.358879 -0.296398,1.01777 1.039974,-1.677178 -0.451826,-0.682645 1.075087,-2.896333 l 0.563879,0.776624 c -0.209647,-1.39214 1.728815,-3.082743 1.532077,-5.284555 0.444596,-0.673349 0.496234,0.724471 0.02427,2.273588 0.654761,-1.718487 0.172469,-1.994746 0.340806,-3.412705 0.181763,0.476612 0.420327,0.983173 0.542707,1.48612 -0.426523,-1.660654 0.437884,-2.796673 0.651662,-3.761773 -0.21068,-0.09346 -0.658374,0.734282 -0.760616,-1.227417 0.01497,-0.852014 0.237015,-0.446662 0.322733,-0.656309 -0.167305,-0.09605 -0.606222,-0.749257 -0.873186,-2.001976 0.19364,-0.294332 0.517405,0.763198 0.780755,0.806574 -0.16937,-0.996083 -0.461121,-1.755666 -0.472997,-2.519897 -0.769395,-1.607984 -0.272128,0.214294 -0.896423,-0.69039 -0.818966,-2.554494 0.679547,-0.592796 0.780755,-1.753601 1.24136,1.798525 1.949306,4.585903 2.274104,5.740512 -0.247858,-1.407631 -0.648563,-2.771371 -1.137568,-4.090702 0.376952,0.158526 -0.607254,-2.896333 0.490037,-0.873186 -1.172165,-4.312742 -5.016557,-8.342512 -8.553191,-10.233467 0.43272,0.396057 0.979042,0.893324 0.78282,0.971296 -1.758764,-1.047203 -1.449457,-1.12879 -1.701447,-1.571321 -1.432933,-0.582984 -1.526913,0.04699 -2.476005,0.001 -2.70063,-1.432419 -3.221133,-1.280089 -5.706433,-2.177544 l 0.113085,0.528249 c -1.78923,-0.595894 -2.084595,0.226171 -4.018409,0.0021 -0.117733,-0.09191 0.619646,-0.332544 1.226384,-0.420843 -1.729847,0.228236 -1.648777,-0.340806 -3.341446,0.063 0.417229,-0.292783 0.858211,-0.486423 1.303324,-0.735314 -1.410729,0.08572 -3.36778,0.821032 -2.763625,0.15233 -2.300955,1.026548 -6.387526,2.467743 -8.680735,4.617918 l -0.07229,-0.481776 c -1.050818,1.261498 -4.582289,3.767453 -4.863712,5.401255 l -0.280906,0.06558 c -0.546839,0.925856 -0.900553,1.975125 -1.334306,2.927832 -0.715176,1.218638 -1.048236,0.468866 -0.946511,0.659924 -1.406598,2.851924 -2.10525,5.248408 -2.708889,7.213721 0.430138,0.642884 0.01033,3.870211 0.172985,6.453106 -0.706398,12.756463 8.952863,25.142168 19.511129,28.001841 1.547568,0.553548 3.849039,0.532381 5.806607,0.589182 -2.309733,-0.660445 -2.608197,-0.350104 -4.858031,-1.134472 -1.622958,-0.764231 -1.978739,-1.636901 -3.128184,-2.634532 l 0.454924,0.803992 c -2.25448,-0.7978 -1.311068,-0.987308 -3.145223,-1.568227 l 0.485907,-0.634622 c -0.730667,-0.05525 -1.935364,-1.231548 -2.26481,-1.882693 l -0.799344,0.0315 c -0.960453,-1.185074 -1.472178,-2.039154 -1.434999,-2.700627 l -0.258186,0.460088 C 37.555113,72.462514 34.31436,68.520528 35.995668,69.438122 35.683263,69.152568 35.2681,68.973386 34.817823,68.155453 l 0.342355,-0.391411 c -0.809156,-1.041006 -1.489218,-2.375312 -1.437581,-2.819909 0.431688,0.582984 0.731184,0.691939 1.027581,0.791599 -2.043285,-5.069744 -2.15792,-0.279358 -3.705488,-5.160626 l 0.32738,-0.02634 c -0.250956,-0.377984 -0.403286,-0.7885 -0.605188,-1.191271 l 0.142519,-1.420024 c -1.471145,-1.70093 -0.411549,-7.232311 -0.19932,-10.265999 0.147166,-1.233613 1.227933,-2.546748 2.049998,-4.606041 l -0.500881,-0.08623 c 0.957354,-1.669948 5.466318,-6.706644 7.554528,-6.447425 1.011573,-1.270793 -0.200869,-0.0046 -0.39864,-0.324799 2.22195,-2.299406 2.920602,-1.624507 4.420148,-2.038121 1.617278,-0.959937 -1.388009,0.37437 -0.621196,-0.366108 2.79564,-0.714143 1.98132,-1.623475 5.628458,-1.985968 0.384698,0.218941 -0.892807,0.338223 -1.213475,0.622228 2.329356,-1.139634 7.371216,-0.880415 10.646049,0.632556 3.799984,1.775805 8.069351,7.025246 8.237688,11.964348 l 0.191575,0.05164 c -0.09708,1.963248 0.300528,4.233737 -0.388312,6.319365 l 0.468866,-0.987304"
|
||||
id="path14"
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.51637238"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
ns2:knockout="Off"
|
||||
d="m 43.744352,57.084946 -0.130126,0.650629 c 0.609836,0.828261 1.093677,1.725716 1.872366,2.373247 -0.560264,-1.093677 -0.97646,-1.545502 -1.74224,-3.023876"
|
||||
id="path16"
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.51637238"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
ns2:knockout="Off"
|
||||
d="m 45.186064,57.028145 c -0.322733,-0.356814 -0.513791,-0.786435 -0.727569,-1.214508 0.204483,0.752354 0.623261,1.398853 1.013123,2.056195 l -0.285554,-0.841687"
|
||||
id="path18"
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.51637238"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
ns2:knockout="Off"
|
||||
d="m 70.696924,51.483338 -0.136323,0.341839 c -0.249924,1.775288 -0.789533,3.531987 -1.616762,5.160625 0.91398,-1.718487 1.505226,-3.598082 1.753085,-5.502464"
|
||||
id="path20"
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.51637238"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
ns2:knockout="Off"
|
||||
d="m 50.53723,26.50176 c 0.627393,-0.229786 1.542405,-0.125995 2.208009,-0.277292 -0.867506,0.07281 -1.730881,0.116184 -2.583411,0.226171 l 0.375402,0.05112"
|
||||
id="path22"
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.51637238"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
ns2:knockout="Off"
|
||||
d="m 28.511368,38.214118 c 0.144584,1.338437 -1.006926,1.857908 0.255088,0.975427 0.676447,-1.523815 -0.264383,-0.420843 -0.255088,-0.975427"
|
||||
id="path24"
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.51637238"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
ns2:knockout="Off"
|
||||
d="m 27.028346,44.408521 c 0.290718,-0.892292 0.343388,-1.428286 0.454408,-1.944659 -0.803476,1.027065 -0.369723,1.246007 -0.454408,1.944659"
|
||||
id="path26"
|
||||
style="fill:#000000;fill-opacity:1;stroke-width:0.51637238"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 10 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 9.6 KiB |
|
|
@ -0,0 +1,152 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
<template>
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item style="width: 100%; padding: 12px">
|
||||
<router-link :to="{ path: '/action/deployVirtualMachine'}">
|
||||
<a-row>
|
||||
<a-col style="margin-right: 12px">
|
||||
<a-avatar :style="{ backgroundColor: $config.theme['@primary-color'] }">
|
||||
<template #icon>
|
||||
<cloud-server-outlined/>
|
||||
</template>
|
||||
</a-avatar>
|
||||
</a-col>
|
||||
<a-col>
|
||||
<h3 style="margin-bottom: 0px;">
|
||||
{{ $t('label.instance') }}
|
||||
</h3>
|
||||
<small>{{ $t('label.create.instance') }}</small>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
<a-menu-item style="width: 100%; padding: 12px" v-if="'listKubernetesClusters' in $store.getters.apis">
|
||||
<router-link :to="{ path: '/kubernetes', query: { action: 'createKubernetesCluster' } }">
|
||||
<a-row>
|
||||
<a-col style="margin-right: 12px">
|
||||
<a-avatar :style="{ backgroundColor: $config.theme['@primary-color'] }">
|
||||
<template #icon>
|
||||
<font-awesome-icon :icon="['fa-solid', 'fa-dharmachakra']" />
|
||||
</template>
|
||||
</a-avatar>
|
||||
</a-col>
|
||||
<a-col>
|
||||
<h3 style="margin-bottom: 0px;">
|
||||
{{ $t('label.kubernetes') }}
|
||||
</h3>
|
||||
<small>{{ $t('label.kubernetes.cluster.create') }}</small>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
<a-menu-item style="width: 100%; padding: 12px">
|
||||
<router-link :to="{ path: '/volume', query: { action: 'createVolume' } }">
|
||||
<a-row>
|
||||
<a-col style="margin-right: 12px">
|
||||
<a-avatar :style="{ backgroundColor: $config.theme['@primary-color'] }">
|
||||
<template #icon>
|
||||
<hdd-outlined />
|
||||
</template>
|
||||
</a-avatar>
|
||||
</a-col>
|
||||
<a-col>
|
||||
<h3 style="margin-bottom: 0px;">
|
||||
{{ $t('label.volume') }}
|
||||
</h3>
|
||||
<small>{{ $t('label.action.create.volume') }}</small>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
<a-menu-item style="width: 100%; padding: 12px">
|
||||
<router-link :to="{ path: '/guestnetwork', query: { action: 'createNetwork' } }">
|
||||
<a-row>
|
||||
<a-col style="margin-right: 12px">
|
||||
<a-avatar :style="{ backgroundColor: $config.theme['@primary-color'] }">
|
||||
<template #icon>
|
||||
<apartment-outlined />
|
||||
</template>
|
||||
</a-avatar>
|
||||
</a-col>
|
||||
<a-col>
|
||||
<h3 style="margin-bottom: 0px;">
|
||||
{{ $t('label.network') }}
|
||||
</h3>
|
||||
<small>{{ $t('label.add.network') }}</small>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
<a-menu-item style="width: 100%; padding: 12px">
|
||||
<router-link :to="{ path: '/vpc', query: { action: 'createVPC' } }">
|
||||
<a-row>
|
||||
<a-col style="margin-right: 12px">
|
||||
<a-avatar :style="{ backgroundColor: $config.theme['@primary-color'] }">
|
||||
<template #icon>
|
||||
<deployment-unit-outlined />
|
||||
</template>
|
||||
</a-avatar>
|
||||
</a-col>
|
||||
<a-col>
|
||||
<h3 style="margin-bottom: 0px;">
|
||||
{{ $t('label.vpc') }}
|
||||
</h3>
|
||||
<small>{{ $t('label.add.vpc') }}</small>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
<a-menu-item style="width: 100%; padding: 12px">
|
||||
<router-link :to="{ path: '/template', query: { action: 'registerTemplate' } }">
|
||||
<a-row>
|
||||
<a-col style="margin-right: 12px">
|
||||
<a-avatar :style="{ backgroundColor: $config.theme['@primary-color'] }">
|
||||
<template #icon>
|
||||
<picture-outlined />
|
||||
</template>
|
||||
</a-avatar>
|
||||
</a-col>
|
||||
<a-col>
|
||||
<h3 style="margin-bottom: 0px;">
|
||||
{{ $t('label.templatename') }}
|
||||
</h3>
|
||||
<small>{{ $t('label.action.register.template') }}</small>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button type="primary">
|
||||
{{ $t('label.create') }}
|
||||
<DownOutlined />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'CreateMenu',
|
||||
components: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -81,7 +81,7 @@ export default {
|
|||
const projects = []
|
||||
const getNextPage = () => {
|
||||
this.loading = true
|
||||
api('listProjects', { listAll: true, details: 'min', page: page, pageSize: 500, showIcon: true }).then(json => {
|
||||
api('listProjects', { listAll: true, page: page, pageSize: 500, showIcon: true }).then(json => {
|
||||
if (json?.listprojectsresponse?.project) {
|
||||
projects.push(...json.listprojectsresponse.project)
|
||||
}
|
||||
|
|
@ -130,7 +130,7 @@ export default {
|
|||
<style lang="less" scoped>
|
||||
.project {
|
||||
&-select {
|
||||
width: 30vw;
|
||||
width: 27vw;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@
|
|||
|
||||
<template>
|
||||
<div class="user-menu">
|
||||
<span class="action">
|
||||
<create-menu v-if="device === 'desktop'" />
|
||||
</span>
|
||||
<external-link class="action"/>
|
||||
<translation-menu class="action"/>
|
||||
<header-notice class="action"/>
|
||||
|
|
@ -69,6 +72,7 @@
|
|||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
import CreateMenu from './CreateMenu'
|
||||
import ExternalLink from './ExternalLink'
|
||||
import HeaderNotice from './HeaderNotice'
|
||||
import TranslationMenu from './TranslationMenu'
|
||||
|
|
@ -80,11 +84,19 @@ import { SERVER_MANAGER } from '@/store/mutation-types'
|
|||
export default {
|
||||
name: 'UserMenu',
|
||||
components: {
|
||||
CreateMenu,
|
||||
ExternalLink,
|
||||
TranslationMenu,
|
||||
HeaderNotice,
|
||||
ResourceIcon
|
||||
},
|
||||
props: {
|
||||
device: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'desktop'
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
image: '',
|
||||
|
|
|
|||
|
|
@ -41,6 +41,11 @@
|
|||
<render-icon
|
||||
v-if="children.meta.icon && typeof (children.meta.icon) === 'string'"
|
||||
:icon="children.meta.icon" />
|
||||
<font-awesome-icon
|
||||
v-else-if="children.meta.icon && Array.isArray(children.meta.icon)"
|
||||
:icon="children.meta.icon"
|
||||
class="anticon"
|
||||
:style="[$store.getters.darkMode ? { color: 'rgba(255, 255, 255, 0.65)' } : { color: '#888' }]" />
|
||||
<render-icon v-else :svgIcon="children.meta.icon" />
|
||||
<span>{{ $t(children.meta.title) }}</span>
|
||||
</router-link>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
<template>
|
||||
<a-layout-header v-if="!headerBarFixed" :class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', theme ]" :style="{ padding: '0' }">
|
||||
<div v-if="mode === 'sidemenu'" class="header">
|
||||
<template v-if="device==='mobile'">
|
||||
<template v-if="device === 'mobile'">
|
||||
<menu-fold-outlined class="trigger" v-if="collapsed" @click="toggle" />
|
||||
<menu-unfold-outlined class="trigger" v-else @click="toggle" />
|
||||
</template>
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
</template>
|
||||
<project-menu v-if="device !== 'mobile'" />
|
||||
<saml-domain-switcher style="margin-left: 20px" />
|
||||
<user-menu></user-menu>
|
||||
<user-menu :device="device"></user-menu>
|
||||
</div>
|
||||
<div v-else :class="['top-nav-header-index', theme]">
|
||||
<div class="header-index-wide">
|
||||
|
|
|
|||
|
|
@ -279,6 +279,11 @@ export default {
|
|||
&.dark {
|
||||
.ant-drawer-content {
|
||||
background-color: rgb(0, 21, 41);
|
||||
max-width: 256px;
|
||||
}
|
||||
|
||||
.ant-drawer-content-wrapper {
|
||||
width: 256px !important;;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -287,11 +292,16 @@ export default {
|
|||
|
||||
.ant-drawer-content {
|
||||
background-color: #fff;
|
||||
max-width: 256px;
|
||||
}
|
||||
|
||||
.ant-drawer-content-wrapper {
|
||||
width: 256px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-drawer-body {
|
||||
padding: 0
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,12 @@
|
|||
<span v-else>
|
||||
<os-logo v-if="resource.ostypeid || resource.ostypename" :osId="resource.ostypeid" :osName="resource.ostypename" size="4x" @update-osname="setResourceOsType"/>
|
||||
<render-icon v-else-if="typeof $route.meta.icon ==='string'" style="font-size: 36px" :icon="$route.meta.icon" />
|
||||
<font-awesome-icon
|
||||
v-else-if="$route.meta.icon && Array.isArray($route.meta.icon)"
|
||||
:icon="$route.meta.icon"
|
||||
size="4x"
|
||||
class="anticon"
|
||||
:style="[$store.getters.darkMode ? { color: 'rgba(255, 255, 255, 0.65)' } : { color: '#888' }]" />
|
||||
<render-icon v-else style="font-size: 36px" :svgIcon="$route.meta.icon" />
|
||||
</span>
|
||||
</slot>
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
</keep-alive>
|
||||
<a-tabs
|
||||
v-else
|
||||
style="width: 100%"
|
||||
style="width: 100%; margin-top: -12px"
|
||||
:animated="false"
|
||||
:activeKey="activeTab || tabs[0].name"
|
||||
@change="onTabChange" >
|
||||
|
|
|
|||
|
|
@ -93,7 +93,6 @@ export default {
|
|||
}
|
||||
|
||||
&-footer {
|
||||
border-top: 1px solid #e8e8e8;
|
||||
padding-top: 9px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ export default {
|
|||
text-align: center;
|
||||
transition: all 0.5s;
|
||||
cursor: pointer;
|
||||
top: calc(50% - 45px);
|
||||
top: calc(100% - 45px);
|
||||
z-index: 100;
|
||||
|
||||
&.left{
|
||||
|
|
|
|||
|
|
@ -24,29 +24,15 @@
|
|||
:icon="['fab', logo]"
|
||||
:size="size"
|
||||
:style="[$store.getters.darkMode ? { color: 'rgba(255, 255, 255, 0.65)' } : { color: '#666' }]"
|
||||
v-if="logo !== 'debian'" />
|
||||
<debian-icon
|
||||
v-else-if="logo === 'debian'"
|
||||
:width="size === '4x' ? 56 : 16"
|
||||
:height="size === '4x' ? 56 : 16"
|
||||
:style="{
|
||||
height: size === '4x' ? '56px' : '16px',
|
||||
width: size === '4x' ? '56px' : '16px',
|
||||
marginBottom: '-4px',
|
||||
background: $store.getters.darkMode ? 'rgba(255, 255, 255, 0.65)' : ''
|
||||
}" />
|
||||
/>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
import DebianIcon from '@/assets/icons/debian.svg?inline'
|
||||
|
||||
export default {
|
||||
name: 'OsLogo',
|
||||
components: {
|
||||
DebianIcon
|
||||
},
|
||||
props: {
|
||||
osId: {
|
||||
type: String,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import { UserLayout, BasicLayout, RouteView } from '@/layouts'
|
|||
import AutogenView from '@/views/AutogenView.vue'
|
||||
import IFramePlugin from '@/views/plugins/IFramePlugin.vue'
|
||||
|
||||
import { shallowRef, defineAsyncComponent } from 'vue'
|
||||
import { shallowRef } from 'vue'
|
||||
import { vueProps } from '@/vue-app'
|
||||
|
||||
import compute from '@/config/section/compute'
|
||||
|
|
@ -201,26 +201,7 @@ export function asyncRouterMap () {
|
|||
name: 'dashboard',
|
||||
meta: {
|
||||
title: 'label.dashboard',
|
||||
icon: 'DashboardOutlined',
|
||||
tabs: [
|
||||
{
|
||||
name: 'dashboard',
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/dashboard/UsageDashboardChart')))
|
||||
},
|
||||
{
|
||||
name: 'accounts',
|
||||
show: (record, route, user) => { return record.account === user.account || ['Admin', 'DomainAdmin'].includes(user.roletype) },
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/project/AccountsTab')))
|
||||
},
|
||||
{
|
||||
name: 'limits',
|
||||
params: {
|
||||
projectid: 'id'
|
||||
},
|
||||
show: (record, route, user) => { return ['Admin'].includes(user.roletype) },
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/components/view/ResourceLimitTab.vue')))
|
||||
}
|
||||
]
|
||||
icon: 'DashboardOutlined'
|
||||
},
|
||||
component: () => import('@/views/dashboard/Dashboard')
|
||||
},
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
// under the License.
|
||||
|
||||
import { shallowRef, defineAsyncComponent } from 'vue'
|
||||
import kubernetes from '@/assets/icons/kubernetes.svg?inline'
|
||||
import store from '@/store'
|
||||
|
||||
export default {
|
||||
|
|
@ -27,7 +26,7 @@ export default {
|
|||
{
|
||||
name: 'vm',
|
||||
title: 'label.instances',
|
||||
icon: 'desktop-outlined',
|
||||
icon: 'cloud-server-outlined',
|
||||
docHelp: 'adminguide/virtual_machines.html',
|
||||
permission: ['listVirtualMachinesMetrics'],
|
||||
resourceType: 'UserVm',
|
||||
|
|
@ -456,7 +455,7 @@ export default {
|
|||
{
|
||||
name: 'kubernetes',
|
||||
title: 'label.kubernetes',
|
||||
icon: shallowRef(kubernetes),
|
||||
icon: ['fa-solid', 'fa-dharmachakra'],
|
||||
docHelp: 'plugins/cloudstack-kubernetes-service.html',
|
||||
permission: ['listKubernetesClusters'],
|
||||
columns: (store) => {
|
||||
|
|
@ -557,7 +556,7 @@ export default {
|
|||
{
|
||||
name: 'autoscalevmgroup',
|
||||
title: 'label.autoscale.vm.groups',
|
||||
icon: 'ordered-list-outlined',
|
||||
icon: 'fullscreen-outlined',
|
||||
docHelp: 'adminguide/autoscale_without_netscaler.html',
|
||||
resourceType: 'AutoScaleVmGroup',
|
||||
permission: ['listAutoScaleVmGroups'],
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
// under the License.
|
||||
|
||||
import { shallowRef, defineAsyncComponent } from 'vue'
|
||||
import kubernetes from '@/assets/icons/kubernetes.svg?inline'
|
||||
import store from '@/store'
|
||||
|
||||
export default {
|
||||
|
|
@ -340,7 +339,7 @@ export default {
|
|||
{
|
||||
name: 'kubernetesiso',
|
||||
title: 'label.kubernetes.isos',
|
||||
icon: shallowRef(kubernetes),
|
||||
icon: ['fa-solid', 'fa-dharmachakra'],
|
||||
docHelp: 'plugins/cloudstack-kubernetes-service.html#kubernetes-supported-versions',
|
||||
permission: ['listKubernetesSupportedVersions'],
|
||||
columns: ['name', 'state', 'semanticversion', 'isostate', 'mincpunumber', 'minmemory', 'zonename'],
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import store from '@/store'
|
|||
export default {
|
||||
name: 'host',
|
||||
title: 'label.hosts',
|
||||
icon: 'desktop-outlined',
|
||||
icon: 'database-outlined',
|
||||
docHelp: 'conceptsandterminology/concepts.html#about-hosts',
|
||||
permission: ['listHostsMetrics'],
|
||||
resourceType: 'Host',
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import store from '@/store'
|
|||
export default {
|
||||
name: 'storagepool',
|
||||
title: 'label.primary.storage',
|
||||
icon: 'database-outlined',
|
||||
icon: 'hdd-outlined',
|
||||
docHelp: 'adminguide/storage.html#primary-storage',
|
||||
permission: ['listStoragePoolsMetrics'],
|
||||
columns: () => {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import store from '@/store'
|
|||
export default {
|
||||
name: 'storage',
|
||||
title: 'label.storage',
|
||||
icon: 'database-outlined',
|
||||
icon: 'hdd-outlined',
|
||||
children: [
|
||||
{
|
||||
name: 'volume',
|
||||
|
|
|
|||
|
|
@ -22,11 +22,11 @@ import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
|||
// import { fas } from '@fortawesome/free-solid-svg-icons'
|
||||
// import { far } from '@fortawesome/free-regular-svg-icons'
|
||||
|
||||
import { faCentos, faUbuntu, faSuse, faRedhat, faFedora, faLinux, faFreebsd, faApple, faWindows, faJava } from '@fortawesome/free-brands-svg-icons'
|
||||
import { faCompactDisc, faCameraRetro } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faCentos, faUbuntu, faDebian, faSuse, faRedhat, faFedora, faLinux, faFreebsd, faApple, faWindows, faJava } from '@fortawesome/free-brands-svg-icons'
|
||||
import { faCompactDisc, faCameraRetro, faDharmachakra } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(faCentos, faUbuntu, faSuse, faRedhat, faFedora, faLinux, faFreebsd, faApple, faWindows, faJava)
|
||||
library.add(faCompactDisc, faCameraRetro)
|
||||
library.add(faCentos, faUbuntu, faDebian, faSuse, faRedhat, faFedora, faLinux, faFreebsd, faApple, faWindows, faJava)
|
||||
library.add(faCompactDisc, faCameraRetro, faDharmachakra)
|
||||
|
||||
export default {
|
||||
install: (app) => {
|
||||
|
|
|
|||
|
|
@ -63,7 +63,8 @@ import {
|
|||
Slider,
|
||||
AutoComplete,
|
||||
Collapse,
|
||||
Space
|
||||
Space,
|
||||
Statistic
|
||||
} from 'ant-design-vue'
|
||||
import VueClipboard from 'vue3-clipboard'
|
||||
import VueCropper from 'vue-cropper'
|
||||
|
|
@ -131,5 +132,6 @@ export default {
|
|||
app.use(Collapse)
|
||||
app.use(Descriptions)
|
||||
app.use(Space)
|
||||
app.use(Statistic)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
.dark-mode {
|
||||
background: @dark-bgColor;
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
h1, h2, h3, h4, h5, h6, .ant-statistic-title, .ant-statistic-content {
|
||||
color: @dark-text-color-3;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -519,6 +519,11 @@ a {
|
|||
padding: 16px;
|
||||
}
|
||||
|
||||
.ant-tabs-left>.ant-tabs-nav .ant-tabs-tab {
|
||||
padding: 8px 24px 8px 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ant-steps {
|
||||
&-item-container {
|
||||
&:hover {
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export const deviceEnquire = function (callback) {
|
|||
}
|
||||
|
||||
enquireJs
|
||||
.register('screen and (max-width: 800px)', matchMobile)
|
||||
.register('screen and (min-width: 800px) and (max-width: 1366px)', matchTablet)
|
||||
.register('screen and (min-width: 1367px)', matchDesktop)
|
||||
.register('screen and (max-width: 765px)', matchMobile)
|
||||
.register('screen and (min-width: 766px) and (max-width: 1279px)', matchTablet)
|
||||
.register('screen and (min-width: 1280px)', matchDesktop)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -393,8 +393,8 @@
|
|||
</a-modal>
|
||||
</div>
|
||||
|
||||
<div :style="this.$store.getters.shutdownTriggered ? 'margin-top: 25px;' : null">
|
||||
<div v-if="dataView" style="margin-top: -10px">
|
||||
<div :style="this.$store.getters.shutdownTriggered ? 'margin-top: 24px; margin-bottom: 12px' : null">
|
||||
<div v-if="dataView">
|
||||
<slot name="resource" v-if="$route.path.startsWith('/quotasummary') || $route.path.startsWith('/publicip')"></slot>
|
||||
<resource-view
|
||||
v-else
|
||||
|
|
@ -1059,6 +1059,19 @@ export default {
|
|||
this.loading = false
|
||||
this.searchParams = params
|
||||
})
|
||||
|
||||
if ('action' in this.$route.query) {
|
||||
const actionName = this.$route.query.action
|
||||
for (const action of this.actions) {
|
||||
if (action.listView && action.api === actionName) {
|
||||
this.execAction(action, false)
|
||||
const query = Object.assign({}, this.$route.query)
|
||||
delete query.action
|
||||
this.$router.replace({ query })
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
closeAction () {
|
||||
this.actionLoading = false
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
// under the License.
|
||||
|
||||
<template>
|
||||
<a-row class="capacity-dashboard" :gutter="12">
|
||||
<a-col :xl="18">
|
||||
<a-row class="capacity-dashboard" :gutter="[12,12]">
|
||||
<a-col :span="24">
|
||||
<div class="capacity-dashboard-wrapper">
|
||||
<div class="capacity-dashboard-select">
|
||||
<a-select
|
||||
|
|
@ -41,91 +41,282 @@
|
|||
<div class="capacity-dashboard-button">
|
||||
<a-button
|
||||
shape="round"
|
||||
@click="() => { listCapacity(zoneSelected, true); listEvents() }">
|
||||
@click="() => { updateData(zoneSelected); listAlerts(); listEvents(); }">
|
||||
<reload-outlined/>
|
||||
{{ $t('label.fetch.latest') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<a-row :gutter="12">
|
||||
<a-col
|
||||
:xs="12"
|
||||
:sm="8"
|
||||
:md="6"
|
||||
:style="{ marginBottom: '12px' }"
|
||||
v-for="stat in stats"
|
||||
:key="stat.type">
|
||||
<chart-card :loading="loading">
|
||||
<router-link :to="{ path: '/zone/' + zoneSelected.id }">
|
||||
<div class="capacity-dashboard-chart-card-inner">
|
||||
<h3>{{ $t(ts[stat.name]) }}</h3>
|
||||
<a-progress
|
||||
type="dashboard"
|
||||
:status="getStatus(parseFloat(stat.percentused))"
|
||||
:percent="parseFloat(stat.percentused)"
|
||||
:format="percent => `${parseFloat(stat.percentused).toFixed(2)}%`"
|
||||
:strokeColor="getStrokeColour(parseFloat(stat.percentused))"
|
||||
:width="100" />
|
||||
</div>
|
||||
</router-link>
|
||||
<template #footer>
|
||||
<div class="center">{{ displayData(stat.name, stat.capacityused) }} / {{ displayData(stat.name, stat.capacitytotal) }}</div>
|
||||
</template>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-col>
|
||||
|
||||
<a-col :xl="6" class="dashboard-event">
|
||||
<chart-card :loading="loading">
|
||||
<div style="text-align: center">
|
||||
<a-tooltip placement="bottom" class="capacity-dashboard-button-wrapper">
|
||||
<template #title>
|
||||
{{ $t('label.view') + ' ' + $t('label.host.alerts') }}
|
||||
</template>
|
||||
<a-button type="primary" danger shape="circle">
|
||||
<router-link :to="{ name: 'host', query: {'state': 'Alert'} }">
|
||||
<desktop-outlined class="capacity-dashboard-button-icon" />
|
||||
</router-link>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="bottom" class="capacity-dashboard-button-wrapper">
|
||||
<template #title>
|
||||
{{ $t('label.view') + ' ' + $t('label.alerts') }}
|
||||
</template>
|
||||
<a-button shape="circle">
|
||||
<router-link :to="{ name: 'alert' }">
|
||||
<flag-outlined class="capacity-dashboard-button-icon" />
|
||||
</router-link>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip placement="bottom" class="capacity-dashboard-button-wrapper">
|
||||
<template #title>
|
||||
{{ $t('label.view') + ' ' + $t('label.events') }}
|
||||
</template>
|
||||
<a-button shape="circle">
|
||||
<router-link :to="{ name: 'event' }">
|
||||
<schedule-outlined class="capacity-dashboard-button-icon" />
|
||||
</router-link>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="capacity-dashboard-footer">
|
||||
<a-timeline>
|
||||
<a-timeline-item
|
||||
v-for="event in events"
|
||||
:key="event.id"
|
||||
:color="getEventColour(event)">
|
||||
<span :style="{ color: '#999' }"><small>{{ $toLocaleDate(event.created) }}</small></span><br/>
|
||||
<span :style="{ color: '#666' }"><small><router-link :to="{ path: '/event/' + event.id }">{{ event.type }}</router-link></small></span><br/>
|
||||
<resource-label :resourceType="event.resourcetype" :resourceId="event.resourceid" :resourceName="event.resourcename" />
|
||||
<span :style="{ color: '#aaa' }">({{ event.username }}) {{ event.description }}</span>
|
||||
</a-timeline-item>
|
||||
</a-timeline>
|
||||
<a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ span: 8 }">
|
||||
<chart-card :loading="loading" class="dashboard-card">
|
||||
<template #title>
|
||||
<div class="center">
|
||||
<router-link :to="{ path: '/infrasummary' }" v-if="!zoneSelected.id">
|
||||
<h3>
|
||||
<bank-outlined />
|
||||
{{ $t('label.infrastructure') }}
|
||||
</h3>
|
||||
</router-link>
|
||||
<router-link :to="{ path: '/zone/' + zoneSelected.id }" v-else>
|
||||
<h3>
|
||||
<global-outlined />
|
||||
{{ $t('label.zone') }}
|
||||
</h3>
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
<a-divider style="margin: 0px 0px; border-width: 0px"/>
|
||||
<a-row :gutter="[12, 12]">
|
||||
<a-col :span="12">
|
||||
<router-link :to="{ path: '/pod', query: { zoneid: zoneSelected.id } }">
|
||||
<a-statistic
|
||||
:title="$t('label.pods')"
|
||||
:value="data.pods"
|
||||
:value-style="{ color: $config.theme['@primary-color'] }">
|
||||
<template #prefix>
|
||||
<appstore-outlined/>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<router-link :to="{ path: '/cluster', query: { zoneid: zoneSelected.id } }">
|
||||
<a-statistic
|
||||
:title="$t('label.clusters')"
|
||||
:value="data.clusters"
|
||||
:value-style="{ color: $config.theme['@primary-color'] }">
|
||||
<template #prefix>
|
||||
<cluster-outlined/>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<router-link :to="{ path: '/host', query: { zoneid: zoneSelected.id } }">
|
||||
<a-statistic
|
||||
:title="$t('label.hosts')"
|
||||
:value="data.totalHosts"
|
||||
:value-style="{ color: $config.theme['@primary-color'] }">
|
||||
<template #prefix>
|
||||
<database-outlined/>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<router-link :to="{ path: '/host', query: { zoneid: zoneSelected.id, state: 'alert' } }">
|
||||
<a-statistic
|
||||
:title="$t('label.host.alerts')"
|
||||
:value="data.alertHosts"
|
||||
:value-style="{ color: $config.theme['@primary-color'] }">
|
||||
<template #prefix>
|
||||
<database-outlined/>
|
||||
<status class="status" text="Alert" style="margin-left: -10px"/>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<router-link :to="{ path: '/storagepool', query: { zoneid: zoneSelected.id } }">
|
||||
<a-statistic
|
||||
:title="$t('label.primary.storage')"
|
||||
:value="data.pools"
|
||||
:value-style="{ color: $config.theme['@primary-color'] }">
|
||||
<template #prefix>
|
||||
<hdd-outlined/>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<router-link :to="{ path: '/systemvm', query: { zoneid: zoneSelected.id } }">
|
||||
<a-statistic
|
||||
:title="$t('label.system.vms')"
|
||||
:value="data.systemvms"
|
||||
:value-style="{ color: $config.theme['@primary-color'] }">
|
||||
<template #prefix>
|
||||
<thunderbolt-outlined/>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<router-link :to="{ path: '/router', query: { zoneid: zoneSelected.id } }">
|
||||
<a-statistic
|
||||
:title="$t('label.virtual.routers')"
|
||||
:value="data.routers"
|
||||
:value-style="{ color: $config.theme['@primary-color'] }">
|
||||
<template #prefix>
|
||||
<fork-outlined/>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<router-link :to="{ path: '/vm', query: { zoneid: zoneSelected.id, projectid: '-1' } }">
|
||||
<a-statistic
|
||||
:title="$t('label.instances')"
|
||||
:value="data.instances"
|
||||
:value-style="{ color: $config.theme['@primary-color'] }">
|
||||
<template #prefix>
|
||||
<cloud-server-outlined/>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ span: 8 }">
|
||||
<chart-card :loading="loading" class="dashboard-card">
|
||||
<template #title>
|
||||
<div class="center">
|
||||
<h3><cloud-outlined /> {{ $t('label.compute') }}</h3>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<div v-for="ctype in ['MEMORY', 'CPU', 'CPU_CORE', 'GPU']" :key="ctype" >
|
||||
<div v-if="statsMap[ctype]">
|
||||
<div>
|
||||
<strong>{{ $t(ts[ctype]) }}</strong>
|
||||
</div>
|
||||
<a-progress
|
||||
status="active"
|
||||
:percent="statsMap[ctype]?.capacitytotal > 0 ? parseFloat(100.0 * statsMap[ctype]?.capacityused / statsMap[ctype]?.capacitytotal).toFixed(2) : 0"
|
||||
:format="p => statsMap[ctype]?.capacitytotal > 0 ? parseFloat(100.0 * statsMap[ctype]?.capacityused / statsMap[ctype]?.capacitytotal).toFixed(2) + '%' : '0%'"
|
||||
stroke-color="#52c41a"
|
||||
size="small"
|
||||
style="width:95%; float: left"
|
||||
/>
|
||||
<br/>
|
||||
<div style="text-align: center">
|
||||
{{ displayData(ctype, statsMap[ctype]?.capacityused) }} {{ $t('label.allocated') }} | {{ displayData(ctype, statsMap[ctype]?.capacitytotal) }} {{ $t('label.total') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ span: 8 }">
|
||||
<chart-card :loading="loading" class="dashboard-card">
|
||||
<template #title>
|
||||
<div class="center">
|
||||
<h3><hdd-outlined /> {{ $t('label.storage') }}</h3>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<div v-for="ctype in ['STORAGE', 'STORAGE_ALLOCATED', 'LOCAL_STORAGE', 'SECONDARY_STORAGE']" :key="ctype" >
|
||||
<div v-if="statsMap[ctype]">
|
||||
<div>
|
||||
<strong>{{ $t(ts[ctype]) }}</strong>
|
||||
</div>
|
||||
<a-progress
|
||||
status="active"
|
||||
:percent="statsMap[ctype]?.capacitytotal > 0 ? parseFloat(100.0 * statsMap[ctype]?.capacityused / statsMap[ctype]?.capacitytotal).toFixed(2) : 0"
|
||||
:format="p => statsMap[ctype]?.capacitytotal > 0 ? parseFloat(100.0 * statsMap[ctype]?.capacityused / statsMap[ctype]?.capacitytotal).toFixed(2) + '%' : '0%'"
|
||||
stroke-color="#52c41a"
|
||||
size="small"
|
||||
style="width:95%; float: left"
|
||||
/>
|
||||
<br/>
|
||||
<div style="text-align: center">
|
||||
{{ displayData(ctype, statsMap[ctype]?.capacityused) }} <span v-if="ctype !== 'STORAGE'">{{ $t('label.allocated') }}</span><span v-else>{{ $t('label.used') }}</span> | {{ displayData(ctype, statsMap[ctype]?.capacitytotal) }} {{ $t('label.total') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ span: 8 }">
|
||||
<chart-card :loading="loading" class="dashboard-card">
|
||||
<template #title>
|
||||
<div class="center">
|
||||
<h3><apartment-outlined /> {{ $t('label.network') }}</h3>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<div v-for="ctype in ['VLAN', 'VIRTUAL_NETWORK_PUBLIC_IP', 'VIRTUAL_NETWORK_IPV6_SUBNET', 'DIRECT_ATTACHED_PUBLIC_IP', 'PRIVATE_IP']" :key="ctype" >
|
||||
<div v-if="statsMap[ctype]">
|
||||
<div>
|
||||
<strong>{{ $t(ts[ctype]) }}</strong>
|
||||
</div>
|
||||
<a-progress
|
||||
status="active"
|
||||
:percent="statsMap[ctype]?.capacitytotal > 0 ? parseFloat(100.0 * statsMap[ctype]?.capacityused / statsMap[ctype]?.capacitytotal).toFixed(2) : 0"
|
||||
:format="p => statsMap[ctype]?.capacitytotal > 0 ? parseFloat(100.0 * statsMap[ctype]?.capacityused / statsMap[ctype]?.capacitytotal).toFixed(2) + '%' : '0%'"
|
||||
stroke-color="#52c41a"
|
||||
size="small"
|
||||
style="width:95%; float: left"
|
||||
/>
|
||||
<br/>
|
||||
<div style="text-align: center">
|
||||
{{ displayData(ctype, statsMap[ctype]?.capacityused) }} {{ $t('label.allocated') }} | {{ displayData(ctype, statsMap[ctype]?.capacitytotal) }} {{ $t('label.total') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ span: 8 }">
|
||||
<router-link :to="{ path: '/alert' }">
|
||||
<a-card :loading="loading" :bordered="false" class="dashboard-card dashboard-event">
|
||||
<div class="center" style="margin-top: -8px">
|
||||
<h3>
|
||||
<flag-outlined />
|
||||
{{ $t('label.alerts') }}
|
||||
</h3>
|
||||
</div>
|
||||
<a-divider style="margin: 6px 0px; border-width: 0px"/>
|
||||
<a-timeline>
|
||||
<a-timeline-item
|
||||
v-for="alert in alerts"
|
||||
:key="alert.id"
|
||||
color="red">
|
||||
<span :style="{ color: '#999' }"><small>{{ $toLocaleDate(alert.sent) }}</small></span>
|
||||
<span :style="{ color: '#666' }"><small><router-link :to="{ path: '/alert/' + alert.id }">{{ alert.name }}</router-link></small></span><br/>
|
||||
<span :style="{ color: '#aaa' }">{{ alert.description }}</span>
|
||||
</a-timeline-item>
|
||||
</a-timeline>
|
||||
<router-link :to="{ path: '/alert' }">
|
||||
<a-button>
|
||||
{{ $t('label.view') }} {{ $t('label.alerts') }}
|
||||
</a-button>
|
||||
</router-link>
|
||||
</a-card>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ span: 8 }">
|
||||
<router-link :to="{ path: '/event' }">
|
||||
<a-card :loading="loading" :bordered="false" class="dashboard-card dashboard-event">
|
||||
<div class="center" style="margin-top: -8px">
|
||||
<h3>
|
||||
<schedule-outlined />
|
||||
{{ $t('label.events') }}
|
||||
</h3>
|
||||
</div>
|
||||
<a-divider style="margin: 6px 0px; border-width: 0px"/>
|
||||
<a-timeline>
|
||||
<a-timeline-item
|
||||
v-for="event in events"
|
||||
:key="event.id"
|
||||
:color="getEventColour(event)">
|
||||
<span :style="{ color: '#999' }"><small>{{ $toLocaleDate(event.created) }}</small></span>
|
||||
<span :style="{ color: '#666' }"><small><router-link :to="{ path: '/event/' + event.id }">{{ event.type }}</router-link></small></span><br/>
|
||||
<span>
|
||||
<resource-label :resourceType="event.resourcetype" :resourceId="event.resourceid" :resourceName="event.resourcename" />
|
||||
</span>
|
||||
<span :style="{ color: '#aaa' }">({{ event.username }}) {{ event.description }}</span>
|
||||
</a-timeline-item>
|
||||
</a-timeline>
|
||||
<router-link :to="{ path: '/event' }">
|
||||
<a-button>
|
||||
{{ $t('label.view') }} {{ $t('label.events') }}
|
||||
</a-button>
|
||||
</router-link>
|
||||
</a-card>
|
||||
</router-link>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
|
|
@ -135,21 +326,35 @@ import { api } from '@/api'
|
|||
import ChartCard from '@/components/widgets/ChartCard'
|
||||
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||
import ResourceLabel from '@/components/widgets/ResourceLabel'
|
||||
import Status from '@/components/widgets/Status'
|
||||
|
||||
export default {
|
||||
name: 'CapacityDashboard',
|
||||
components: {
|
||||
ChartCard,
|
||||
ResourceIcon,
|
||||
ResourceLabel
|
||||
ResourceLabel,
|
||||
Status
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
loading: true,
|
||||
tabKey: 'alerts',
|
||||
alerts: [],
|
||||
events: [],
|
||||
zones: [],
|
||||
zoneSelected: {},
|
||||
stats: [],
|
||||
statsMap: {},
|
||||
data: {
|
||||
pods: 0,
|
||||
clusters: 0,
|
||||
totalHosts: 0,
|
||||
alertHosts: 0,
|
||||
pools: 0,
|
||||
instances: 0,
|
||||
systemvms: 0,
|
||||
routers: 0
|
||||
},
|
||||
ts: {
|
||||
CPU: 'label.cpu',
|
||||
CPU_CORE: 'label.cpunumber',
|
||||
|
|
@ -159,8 +364,8 @@ export default {
|
|||
MEMORY: 'label.memory',
|
||||
PRIVATE_IP: 'label.management.ips',
|
||||
SECONDARY_STORAGE: 'label.secondary.storage',
|
||||
STORAGE: 'label.storage',
|
||||
STORAGE_ALLOCATED: 'label.primary.storage',
|
||||
STORAGE: 'label.primary.storage.used',
|
||||
STORAGE_ALLOCATED: 'label.primary.storage.allocated',
|
||||
VIRTUAL_NETWORK_PUBLIC_IP: 'label.public.ips',
|
||||
VLAN: 'label.vlan',
|
||||
VIRTUAL_NETWORK_IPV6_SUBNET: 'label.ipv6.subnets'
|
||||
|
|
@ -196,13 +401,10 @@ export default {
|
|||
}
|
||||
return 'normal'
|
||||
},
|
||||
getStrokeColour (value) {
|
||||
if (value >= 80) {
|
||||
return this.$config.theme['@graph-exception-color'] || 'red'
|
||||
}
|
||||
return this.$config.theme['@graph-normal-color'] || 'primary'
|
||||
},
|
||||
displayData (dataType, value) {
|
||||
if (!value) {
|
||||
value = 0
|
||||
}
|
||||
switch (dataType) {
|
||||
case 'CPU':
|
||||
value = parseFloat(value / 1000.0, 10).toFixed(2) + ' GHz'
|
||||
|
|
@ -214,9 +416,9 @@ export default {
|
|||
case 'LOCAL_STORAGE':
|
||||
value = parseFloat(value / (1024 * 1024 * 1024.0), 10).toFixed(2)
|
||||
if (value >= 1024.0) {
|
||||
value = parseFloat(value / 1024.0).toFixed(2) + ' TB'
|
||||
value = parseFloat(value / 1024.0).toFixed(2) + ' TiB'
|
||||
} else {
|
||||
value = value + ' GB'
|
||||
value = value + ' GiB'
|
||||
}
|
||||
break
|
||||
}
|
||||
|
|
@ -224,26 +426,134 @@ export default {
|
|||
},
|
||||
fetchData () {
|
||||
this.listZones()
|
||||
this.listAlerts()
|
||||
this.listEvents()
|
||||
},
|
||||
listCapacity (zone, latest = false) {
|
||||
const params = {
|
||||
zoneid: zone.id,
|
||||
fetchlatest: latest
|
||||
listCapacity (zone, latest = false, additive = false) {
|
||||
this.loading = true
|
||||
api('listCapacity', { zoneid: zone.id, fetchlatest: latest }).then(json => {
|
||||
this.loading = false
|
||||
let stats = []
|
||||
if (json && json.listcapacityresponse && json.listcapacityresponse.capacity) {
|
||||
stats = json.listcapacityresponse.capacity
|
||||
}
|
||||
for (const stat of stats) {
|
||||
if (additive) {
|
||||
for (const [key, value] of Object.entries(stat)) {
|
||||
if (stat.name in this.statsMap) {
|
||||
if (key in this.statsMap[stat.name]) {
|
||||
this.statsMap[stat.name][key] += value
|
||||
} else {
|
||||
this.statsMap[stat.name][key] = value
|
||||
}
|
||||
} else {
|
||||
this.statsMap[stat.name] = { key: value }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.statsMap[stat.name] = stat
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
updateData (zone) {
|
||||
if (!zone.id) {
|
||||
this.statsMap = {}
|
||||
for (const zone of this.zones.slice(1)) {
|
||||
this.listCapacity(zone, true, true)
|
||||
}
|
||||
} else {
|
||||
this.statsMap = {}
|
||||
this.listCapacity(this.zoneSelected, true)
|
||||
}
|
||||
|
||||
this.data = {
|
||||
pods: 0,
|
||||
clusters: 0,
|
||||
totalHosts: 0,
|
||||
alertHosts: 0,
|
||||
pools: 0,
|
||||
instances: 0,
|
||||
systemvms: 0,
|
||||
routers: 0
|
||||
}
|
||||
this.loading = true
|
||||
api('listCapacity', params).then(json => {
|
||||
this.stats = []
|
||||
api('listPods', { zoneid: zone.id }).then(json => {
|
||||
this.loading = false
|
||||
if (json && json.listcapacityresponse && json.listcapacityresponse.capacity) {
|
||||
this.stats = json.listcapacityresponse.capacity
|
||||
this.data.pods = json?.listpodsresponse?.count
|
||||
if (!this.data.pods) {
|
||||
this.data.pods = 0
|
||||
}
|
||||
})
|
||||
api('listClusters', { zoneid: zone.id }).then(json => {
|
||||
this.loading = false
|
||||
this.data.clusters = json?.listclustersresponse?.count
|
||||
if (!this.data.clusters) {
|
||||
this.data.clusters = 0
|
||||
}
|
||||
})
|
||||
api('listHosts', { zoneid: zone.id, listall: true, details: 'min', type: 'routing', page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.totalHosts = json?.listhostsresponse?.count
|
||||
if (!this.data.totalHosts) {
|
||||
this.data.totalHosts = 0
|
||||
}
|
||||
})
|
||||
api('listHosts', { zoneid: zone.id, listall: true, details: 'min', type: 'routing', state: 'alert', page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.alertHosts = json?.listhostsresponse?.count
|
||||
if (!this.data.alertHosts) {
|
||||
this.data.alertHosts = 0
|
||||
}
|
||||
})
|
||||
api('listStoragePools', { zoneid: zone.id }).then(json => {
|
||||
this.loading = false
|
||||
this.data.pools = json?.liststoragepoolsresponse?.count
|
||||
if (!this.data.pools) {
|
||||
this.data.pools = 0
|
||||
}
|
||||
})
|
||||
api('listSystemVms', { zoneid: zone.id }).then(json => {
|
||||
this.loading = false
|
||||
this.data.systemvms = json?.listsystemvmsresponse?.count
|
||||
if (!this.data.systemvms) {
|
||||
this.data.systemvms = 0
|
||||
}
|
||||
})
|
||||
api('listRouters', { zoneid: zone.id, listall: true }).then(json => {
|
||||
this.loading = false
|
||||
this.data.routers = json?.listroutersresponse?.count
|
||||
if (!this.data.routers) {
|
||||
this.data.routers = 0
|
||||
}
|
||||
})
|
||||
api('listVirtualMachines', { zoneid: zone.id, listall: true, projectid: '-1', details: 'min', page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.instances = json?.listvirtualmachinesresponse?.count
|
||||
if (!this.data.instances) {
|
||||
this.data.instances = 0
|
||||
}
|
||||
})
|
||||
},
|
||||
listAlerts () {
|
||||
const params = {
|
||||
page: 1,
|
||||
pagesize: 8,
|
||||
listall: true
|
||||
}
|
||||
this.loading = true
|
||||
api('listAlerts', params).then(json => {
|
||||
this.alerts = []
|
||||
this.loading = false
|
||||
if (json && json.listalertsresponse && json.listalertsresponse.alert) {
|
||||
this.alerts = json.listalertsresponse.alert
|
||||
}
|
||||
})
|
||||
},
|
||||
listEvents () {
|
||||
const params = {
|
||||
page: 1,
|
||||
pagesize: 6,
|
||||
pagesize: 8,
|
||||
listall: true
|
||||
}
|
||||
this.loading = true
|
||||
|
|
@ -269,15 +579,16 @@ export default {
|
|||
if (json && json.listzonesresponse && json.listzonesresponse.zone) {
|
||||
this.zones = json.listzonesresponse.zone
|
||||
if (this.zones.length > 0) {
|
||||
this.zones.splice(0, 0, { name: this.$t('label.all.zone') })
|
||||
this.zoneSelected = this.zones[0]
|
||||
this.listCapacity(this.zones[0])
|
||||
this.updateData(this.zones[0])
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
changeZone (index) {
|
||||
this.zoneSelected = this.zones[index]
|
||||
this.listCapacity(this.zoneSelected)
|
||||
this.updateData(this.zoneSelected)
|
||||
},
|
||||
filterZone (input, option) {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
|
|
@ -290,7 +601,6 @@ export default {
|
|||
.capacity-dashboard {
|
||||
&-wrapper {
|
||||
display: flex;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
&-chart-card-inner {
|
||||
|
|
@ -313,7 +623,7 @@ export default {
|
|||
|
||||
&-button {
|
||||
width: auto;
|
||||
padding-left: 12px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
&-button-icon {
|
||||
|
|
@ -321,21 +631,28 @@ export default {
|
|||
padding: 2px;
|
||||
}
|
||||
|
||||
&-footer {
|
||||
&-title {
|
||||
padding-top: 12px;
|
||||
padding-left: 3px;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-card {
|
||||
width: 100%;
|
||||
min-height: 370px;
|
||||
}
|
||||
|
||||
.dashboard-event {
|
||||
width: 100%;
|
||||
overflow-x:hidden;
|
||||
overflow-y: auto;
|
||||
max-height: 370px;
|
||||
}
|
||||
|
||||
.center {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.dashboard-event {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -16,82 +16,331 @@
|
|||
// under the License.
|
||||
|
||||
<template>
|
||||
<a-row class="usage-dashboard" :gutter="12">
|
||||
<a-col :xl="16" style="padding-left: 0; padding-right: 0;">
|
||||
<a-row>
|
||||
<a-card style="width: 100%">
|
||||
<a-tabs
|
||||
v-if="showProject"
|
||||
:animated="false"
|
||||
@change="onTabChange">
|
||||
<template v-for="tab in $route.meta.tabs" :key="tab.name">
|
||||
<a-tab-pane
|
||||
v-if="'show' in tab ? tab.show(project, $route, $store.getters.userInfo) : true"
|
||||
:tab="$t('label.' + tab.name)"
|
||||
:key="tab.name">
|
||||
<keep-alive>
|
||||
<component
|
||||
:is="tab.component"
|
||||
:resource="project"
|
||||
:loading="loading"
|
||||
:bordered="false"
|
||||
:stats="stats" />
|
||||
</keep-alive>
|
||||
</a-tab-pane>
|
||||
</template>
|
||||
</a-tabs>
|
||||
<a-row :gutter="24" v-else>
|
||||
<a-col
|
||||
class="usage-dashboard-chart-tile"
|
||||
:xs="12"
|
||||
:md="8"
|
||||
v-for="stat in stats"
|
||||
:key="stat.type">
|
||||
<a-card
|
||||
class="usage-dashboard-chart-card"
|
||||
:bordered="false"
|
||||
:loading="loading"
|
||||
:style="stat.bgcolor ? { 'background': stat.bgcolor } : {}">
|
||||
<router-link v-if="stat.path" :to="{ path: stat.path, query: stat.query }">
|
||||
<div
|
||||
class="usage-dashboard-chart-card-inner">
|
||||
<h3>{{ stat.name }}</h3>
|
||||
<h2>
|
||||
<render-icon :icon="stat.icon" />
|
||||
{{ stat.count == undefined ? 0 : stat.count }}
|
||||
</h2>
|
||||
</div>
|
||||
</router-link>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</a-row>
|
||||
</a-col>
|
||||
<a-col :xl="8">
|
||||
<chart-card :loading="loading" >
|
||||
<div class="usage-dashboard-chart-card-inner">
|
||||
<a-button>
|
||||
<router-link :to="{ name: 'event' }">
|
||||
{{ $t('label.view') + ' ' + $t('label.events') }}
|
||||
</router-link>
|
||||
</a-button>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="usage-dashboard-chart-footer">
|
||||
<a-timeline>
|
||||
<a-timeline-item
|
||||
v-for="event in events"
|
||||
:key="event.id"
|
||||
:color="getEventColour(event)">
|
||||
<span :style="{ color: '#999' }"><small>{{ $toLocaleDate(event.created) }}</small></span><br/>
|
||||
<span :style="{ color: '#666' }"><small><router-link :to="{ path: '/event/' + event.id }">{{ event.type }}</router-link></small></span><br/>
|
||||
<resource-label :resourceType="event.resourcetype" :resourceId="event.resourceid" :resourceName="event.resourcename" />
|
||||
<span :style="{ color: '#aaa' }">({{ event.username }}) {{ event.description }}</span>
|
||||
</a-timeline-item>
|
||||
</a-timeline>
|
||||
<a-row class="capacity-dashboard" :gutter="[12,12]">
|
||||
<a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ span: 8 }">
|
||||
<chart-card :loading="loading" class="dashboard-card">
|
||||
<template #title>
|
||||
<div class="center">
|
||||
<h3>
|
||||
<dashboard-outlined /> {{ $t('label.resources') }}
|
||||
<span style="float: right" v-if="showProject">
|
||||
<a-dropdown>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item>
|
||||
<router-link :to="{ path: '/project/' + project.id }">
|
||||
<project-outlined/>
|
||||
{{ $t('label.view') }} {{ $t('label.project') }}
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
<a-menu-item v-if="showProject && ['Admin'].includes($store.getters.userInfo.roletype)">
|
||||
<router-link :to="{ path: '/project/' + project.id, query: { tab: 'limits.configure' } }">
|
||||
<setting-outlined/>
|
||||
{{ $t('label.configure') }} {{ $t('label.project') }} {{ $t('label.limits') }}
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button size="small" type="text">
|
||||
<more-outlined />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</span>
|
||||
</h3>
|
||||
</div>
|
||||
</template>
|
||||
<a-divider style="margin: 6px 0px; border-width: 0px"/>
|
||||
<a-row :gutter="[10, 10]">
|
||||
<a-col :span="12">
|
||||
<router-link :to="{ path: '/vm' }">
|
||||
<a-statistic
|
||||
:title="$t('label.instances')"
|
||||
:value="data.instances"
|
||||
:value-style="{ color: $config.theme['@primary-color'] }">
|
||||
<template #prefix>
|
||||
<cloud-server-outlined/>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<router-link :to="{ path: '/kubernetes' }">
|
||||
<a-statistic
|
||||
:title="$t('label.kubernetes.cluster')"
|
||||
:value="data.kubernetes"
|
||||
:value-style="{ color: $config.theme['@primary-color'] }">
|
||||
<template #prefix>
|
||||
<cluster-outlined/>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<router-link :to="{ path: '/volume' }">
|
||||
<a-statistic
|
||||
:title="$t('label.volumes')"
|
||||
:value="data.volumes"
|
||||
:value-style="{ color: $config.theme['@primary-color'] }">
|
||||
<template #prefix>
|
||||
<hdd-outlined/>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<router-link :to="{ path: '/snapshot' }">
|
||||
<a-statistic
|
||||
:title="$t('label.snapshots')"
|
||||
:value="data.snapshots"
|
||||
:value-style="{ color: $config.theme['@primary-color'] }">
|
||||
<template #prefix>
|
||||
<build-outlined/>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<router-link :to="{ path: '/guestnetwork' }">
|
||||
<a-statistic
|
||||
:title="$t('label.guest.networks')"
|
||||
:value="data.networks"
|
||||
:value-style="{ color: $config.theme['@primary-color'] }">
|
||||
<template #prefix>
|
||||
<apartment-outlined/>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<router-link :to="{ path: '/vpc' }">
|
||||
<a-statistic
|
||||
:title="$t('label.vpcs')"
|
||||
:value="data.vpcs"
|
||||
:value-style="{ color: $config.theme['@primary-color'] }">
|
||||
<template #prefix>
|
||||
<deployment-unit-outlined/>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<router-link :to="{ path: '/publicip' }">
|
||||
<a-statistic
|
||||
:title="$t('label.public.ips')"
|
||||
:value="data.ips"
|
||||
:value-style="{ color: $config.theme['@primary-color'] }">
|
||||
<template #prefix>
|
||||
<environment-outlined/>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<router-link :to="{ path: '/template', query: { templatefilter: 'self', filter: 'self' } }">
|
||||
<a-statistic
|
||||
:title="$t('label.templates')"
|
||||
:value="data.templates"
|
||||
:value-style="{ color: $config.theme['@primary-color'] }">
|
||||
<template #prefix>
|
||||
<picture-outlined/>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ span: 8 }">
|
||||
<chart-card :loading="loading" class="dashboard-card">
|
||||
<template #title>
|
||||
<div class="center">
|
||||
<h3>
|
||||
<cloud-outlined /> {{ $t('label.compute') }}
|
||||
</h3>
|
||||
</div>
|
||||
</template>
|
||||
<a-divider style="margin: 6px 0px; border-width: 0px"/>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<router-link :to="{ path: '/vm', query: { state: 'running', filter: 'running' } }">
|
||||
<a-statistic
|
||||
:title="$t('label.running') + ' ' + $t('label.instances')"
|
||||
:value="data.running"
|
||||
:value-style="{ color: $config.theme['@primary-color'] }">
|
||||
<template #prefix>
|
||||
<status class="status" text="Running"/>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<router-link :to="{ path: '/vm', query: { state: 'stopped', filter: 'stopped' } }">
|
||||
<a-statistic
|
||||
:title="$t('label.stopped') + ' ' + $t('label.instances')"
|
||||
:value="data.stopped"
|
||||
:value-style="{ color: $config.theme['@primary-color'] }">
|
||||
<template #prefix>
|
||||
<status class="status" text="Stopped"/>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</router-link>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-divider style="margin: 1px 0px; border-width: 0px;"/>
|
||||
<div
|
||||
v-for="usageType in ['vm', 'cpu', 'memory', 'project']"
|
||||
:key="usageType">
|
||||
<div v-if="usageType + 'total' in entity">
|
||||
<div>
|
||||
<strong>
|
||||
{{ $t(getLabel(usageType)) }}
|
||||
</strong>
|
||||
<span style="float: right">
|
||||
{{ getValue(usageType, entity[usageType + 'total']) }} {{ $t('label.used') }}
|
||||
</span>
|
||||
</div>
|
||||
<a-progress
|
||||
status="active"
|
||||
:percent="parseFloat(getPercentUsed(entity[usageType + 'total'], entity[usageType + 'limit']))"
|
||||
:format="p => resource[item + 'limit'] !== '-1' && resource[item + 'limit'] !== 'Unlimited' ? p.toFixed(0) + '%' : ''"
|
||||
stroke-color="#52c41a"
|
||||
size="small"
|
||||
/>
|
||||
<br/>
|
||||
<div style="text-align: center">
|
||||
{{ entity[usageType + 'available'] === 'Unlimited' ? $t('label.unlimited') : getValue(usageType, entity[usageType + 'available']) }} {{ $t('label.available') }}
|
||||
{{ entity[usageType + 'limit'] === 'Unlimited' ? '' : (' | ' + getValue(usageType, entity[usageType + 'limit']) + ' ' + $t('label.limit')) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ span: 8 }">
|
||||
<chart-card :loading="loading" class="dashboard-card">
|
||||
<template #title>
|
||||
<div class="center">
|
||||
<h3><hdd-outlined /> {{ $t('label.storage') }}</h3>
|
||||
</div>
|
||||
</template>
|
||||
<a-divider style="margin: 6px 0px; border-width: 0px"/>
|
||||
<div
|
||||
v-for="usageType in ['volume', 'snapshot', 'template', 'primarystorage', 'secondarystorage']"
|
||||
:key="usageType">
|
||||
<div>
|
||||
<div>
|
||||
<strong>
|
||||
{{ $t(getLabel(usageType)) }}
|
||||
</strong>
|
||||
<span style="float: right">
|
||||
{{ getValue(usageType, entity[usageType + 'total']) }} {{ $t('label.used') }}
|
||||
</span>
|
||||
</div>
|
||||
<a-progress
|
||||
status="active"
|
||||
:percent="parseFloat(getPercentUsed(entity[usageType + 'total'], entity[usageType + 'limit']))"
|
||||
:format="p => resource[item + 'limit'] !== '-1' && resource[item + 'limit'] !== 'Unlimited' ? p.toFixed(0) + '%' : ''"
|
||||
stroke-color="#52c41a"
|
||||
size="small"
|
||||
/>
|
||||
<br/>
|
||||
<div style="text-align: center">
|
||||
{{ entity[usageType + 'available'] === 'Unlimited' ? $t('label.unlimited') : getValue(usageType, entity[usageType + 'available']) }} {{ $t('label.available') }}
|
||||
{{ entity[usageType + 'limit'] === 'Unlimited' ? '' : (' | ' + getValue(usageType, entity[usageType + 'limit']) + ' ' + $t('label.limit')) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ span: 8 }" class="dashboard-card">
|
||||
<chart-card :loading="loading" class="dashboard-card">
|
||||
<template #title>
|
||||
<div class="center">
|
||||
<h3><apartment-outlined /> {{ $t('label.network') }}</h3>
|
||||
</div>
|
||||
</template>
|
||||
<a-divider style="margin: 6px 0px; border-width: 0px"/>
|
||||
<div
|
||||
v-for="usageType in ['ip', 'network', 'vpc']"
|
||||
:key="usageType">
|
||||
<div>
|
||||
<div>
|
||||
<strong>
|
||||
{{ $t(getLabel(usageType)) }}
|
||||
</strong>
|
||||
<span style="float: right">
|
||||
{{ getValue(usageType, entity[usageType + 'total']) }} {{ $t('label.used') }}
|
||||
</span>
|
||||
</div>
|
||||
<a-progress
|
||||
status="active"
|
||||
:percent="parseFloat(getPercentUsed(entity[usageType + 'total'], entity[usageType + 'limit']))"
|
||||
:format="p => resource[item + 'limit'] !== '-1' && resource[item + 'limit'] !== 'Unlimited' ? p.toFixed(0) + '%' : ''"
|
||||
stroke-color="#52c41a"
|
||||
size="small"
|
||||
/>
|
||||
<br/>
|
||||
<div style="text-align: center">
|
||||
{{ entity[usageType + 'available'] === 'Unlimited' ? $t('label.unlimited') : getValue(usageType, entity[usageType + 'available']) }} {{ $t('label.available') }}
|
||||
{{ entity[usageType + 'limit'] === 'Unlimited' ? '' : (' | ' + getValue(usageType, entity[usageType + 'limit']) + ' ' + $t('label.limit')) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ span: 8 }" class="dashboard-card">
|
||||
<chart-card :loading="loading" class="dashboard-card">
|
||||
<template #title>
|
||||
<div class="center">
|
||||
<h3><render-icon :icon="$config.userCard.icon" /> {{ $t($config.userCard.title) }}</h3>
|
||||
</div>
|
||||
</template>
|
||||
<a-divider style="margin: 6px 0px; border-width: 0px"/>
|
||||
<a-list item-layout="horizontal" :data-source="$config.userCard.links">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<a-list-item-meta :description="item.text">
|
||||
<template #title>
|
||||
<a :href="item.link" target="_blank"><h4>{{ item.title }}</h4></a>
|
||||
</template>
|
||||
<template #avatar>
|
||||
<a-avatar :style="{ backgroundColor: $config.theme['@primary-color'] }">
|
||||
<template #icon>
|
||||
<render-icon :icon="item.icon" />
|
||||
</template>
|
||||
</a-avatar>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
<a-col :xs="{ span: 24 }" :lg="{ span: 12 }" :xl="{ span: 8 }" :xxl="{ span: 8 }">
|
||||
<chart-card :loading="loading" class="dashboard-card dashboard-event">
|
||||
<template #title>
|
||||
<div class="center">
|
||||
<h3><schedule-outlined /> {{ $t('label.events') }}</h3>
|
||||
</div>
|
||||
</template>
|
||||
<a-divider style="margin: 6px 0px; border-width: 0px"/>
|
||||
<a-timeline>
|
||||
<a-timeline-item
|
||||
v-for="event in events"
|
||||
:key="event.id"
|
||||
:color="getEventColour(event)">
|
||||
<span :style="{ color: '#999' }"><small>{{ $toLocaleDate(event.created) }}</small></span>
|
||||
<span :style="{ color: '#666' }"><small><router-link :to="{ path: '/event/' + event.id }">{{ event.type }}</router-link></small></span><br/>
|
||||
<span>
|
||||
<resource-label :resourceType="event.resourcetype" :resourceId="event.resourceid" :resourceName="event.resourcename" />
|
||||
</span>
|
||||
<span :style="{ color: '#aaa' }">({{ event.username }}) {{ event.description }}</span>
|
||||
</a-timeline-item>
|
||||
</a-timeline>
|
||||
<router-link :to="{ path: '/event' }">
|
||||
<a-button>
|
||||
{{ $t('label.view') }} {{ $t('label.events') }}
|
||||
</a-button>
|
||||
</router-link>
|
||||
</chart-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
|
@ -104,13 +353,15 @@ import store from '@/store'
|
|||
import ChartCard from '@/components/widgets/ChartCard'
|
||||
import UsageDashboardChart from '@/views/dashboard/UsageDashboardChart'
|
||||
import ResourceLabel from '@/components/widgets/ResourceLabel'
|
||||
import Status from '@/components/widgets/Status'
|
||||
|
||||
export default {
|
||||
name: 'UsageDashboard',
|
||||
components: {
|
||||
ChartCard,
|
||||
UsageDashboardChart,
|
||||
ResourceLabel
|
||||
ResourceLabel,
|
||||
Status
|
||||
},
|
||||
props: {
|
||||
resource: {
|
||||
|
|
@ -129,9 +380,29 @@ export default {
|
|||
loading: false,
|
||||
showAction: false,
|
||||
showAddAccount: false,
|
||||
project: {},
|
||||
account: {},
|
||||
events: [],
|
||||
stats: [],
|
||||
project: {}
|
||||
data: {
|
||||
running: 0,
|
||||
stopped: 0,
|
||||
instances: 0,
|
||||
kubernetes: 0,
|
||||
volumes: 0,
|
||||
snapshots: 0,
|
||||
networks: 0,
|
||||
vpcs: 0,
|
||||
ips: 0,
|
||||
templates: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
entity: function () {
|
||||
if (this.showProject) {
|
||||
return this.project
|
||||
}
|
||||
return this.account
|
||||
}
|
||||
},
|
||||
created () {
|
||||
|
|
@ -158,6 +429,9 @@ export default {
|
|||
deep: true,
|
||||
handler (newData, oldData) {
|
||||
this.project = newData
|
||||
if (newData.id) {
|
||||
this.fetchData()
|
||||
}
|
||||
}
|
||||
},
|
||||
'$i18n.global.locale' (to, from) {
|
||||
|
|
@ -168,61 +442,95 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
this.stats = [{}, {}, {}, {}, {}, {}]
|
||||
api('listVirtualMachines', { state: 'Running', listall: true, retrieveonlyresourcecount: true }).then(json => {
|
||||
var count = 0
|
||||
if (json && json.listvirtualmachinesresponse) {
|
||||
count = json.listvirtualmachinesresponse.count
|
||||
if (store.getters.project.id) {
|
||||
this.listProject()
|
||||
} else {
|
||||
this.listAccount()
|
||||
}
|
||||
this.updateData()
|
||||
},
|
||||
listAccount () {
|
||||
this.loading = true
|
||||
api('listAccounts', { id: this.$store.getters.userInfo.accountid }).then(json => {
|
||||
this.loading = false
|
||||
if (json && json.listaccountsresponse && json.listaccountsresponse.account) {
|
||||
this.account = json.listaccountsresponse.account[0]
|
||||
}
|
||||
var tileColor = this.$config.theme['@dashboard-tile-runningvms-bg'] || '#dfe9cc'
|
||||
this.stats.splice(0, 1, { name: this.$t('label.running.vms'), count: count, icon: 'desktop-outlined', bgcolor: tileColor, path: '/vm', query: { state: 'running', filter: 'running' } })
|
||||
})
|
||||
api('listVirtualMachines', { state: 'Stopped', listall: true, retrieveonlyresourcecount: true }).then(json => {
|
||||
var count = 0
|
||||
if (json && json.listvirtualmachinesresponse) {
|
||||
count = json.listvirtualmachinesresponse.count
|
||||
},
|
||||
listProject () {
|
||||
this.loading = true
|
||||
api('listProjects', { id: store.getters.project.id }).then(json => {
|
||||
this.loading = false
|
||||
if (json && json.listprojectsresponse && json.listprojectsresponse.project) {
|
||||
this.project = json.listprojectsresponse.project[0]
|
||||
}
|
||||
var tileColor = this.$config.theme['@dashboard-tile-stoppedvms-bg'] || '#edcbce'
|
||||
this.stats.splice(1, 1, { name: this.$t('label.stopped.vms'), count: count, icon: 'poweroff-outlined', bgcolor: tileColor, path: '/vm', query: { state: 'stopped', filter: 'stopped' } })
|
||||
})
|
||||
api('listVirtualMachines', { listall: true, retrieveonlyresourcecount: true }).then(json => {
|
||||
var count = 0
|
||||
if (json && json.listvirtualmachinesresponse) {
|
||||
count = json.listvirtualmachinesresponse.count
|
||||
}
|
||||
var tileColor = this.$config.theme['@dashboard-tile-totalvms-bg'] || '#ffffff'
|
||||
this.stats.splice(2, 1, { name: this.$t('label.total.vms'), count: count, icon: 'number-outlined', bgcolor: tileColor, path: '/vm' })
|
||||
})
|
||||
api('listVolumes', { listall: true, retrieveonlyresourcecount: true }).then(json => {
|
||||
var count = 0
|
||||
if (json && json.listvolumesresponse) {
|
||||
count = json.listvolumesresponse.count
|
||||
}
|
||||
var tileColor = this.$config.theme['@dashboard-tile-totalvolumes-bg'] || '#ffffff'
|
||||
this.stats.splice(3, 1, { name: this.$t('label.total.volume'), count: count, icon: 'database-outlined', bgcolor: tileColor, path: '/volume' })
|
||||
})
|
||||
api('listNetworks', { listall: true, retrieveonlyresourcecount: true }).then(json => {
|
||||
var count = 0
|
||||
if (json && json.listnetworksresponse) {
|
||||
count = json.listnetworksresponse.count
|
||||
}
|
||||
var tileColor = this.$config.theme['@dashboard-tile-totalnetworks-bg'] || '#ffffff'
|
||||
this.stats.splice(4, 1, { name: this.$t('label.total.network'), count: count, icon: 'apartment-outlined', bgcolor: tileColor, path: '/guestnetwork' })
|
||||
})
|
||||
api('listPublicIpAddresses', { listall: true, retrieveonlyresourcecount: true }).then(json => {
|
||||
var count = 0
|
||||
if (json && json.listpublicipaddressesresponse) {
|
||||
count = json.listpublicipaddressesresponse.count
|
||||
}
|
||||
var tileColor = this.$config.theme['@dashboard-tile-totalips-bg'] || '#ffffff'
|
||||
this.stats.splice(5, 1, { name: this.$t('label.public.ip.addresses'), count: count, icon: 'environment-outlined', bgcolor: tileColor, path: '/publicip' })
|
||||
})
|
||||
},
|
||||
updateData () {
|
||||
this.data = {
|
||||
running: 0,
|
||||
stopped: 0,
|
||||
instances: 0,
|
||||
kubernetes: 0,
|
||||
volumes: 0,
|
||||
snapshots: 0,
|
||||
networks: 0,
|
||||
vpcs: 0,
|
||||
ips: 0,
|
||||
templates: 0
|
||||
}
|
||||
this.listInstances()
|
||||
this.listEvents()
|
||||
this.loading = true
|
||||
api('listKubernetesClusters', { listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.kubernetes = json?.listkubernetesclustersresponse?.count
|
||||
})
|
||||
api('listVolumes', { listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.volumes = json?.listvolumesresponse?.count
|
||||
})
|
||||
api('listSnapshots', { listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.snapshots = json?.listsnapshotsresponse?.count
|
||||
})
|
||||
api('listNetworks', { listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.networks = json?.listnetworksresponse?.count
|
||||
})
|
||||
api('listVPCs', { listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.vpcs = json?.listvpcsresponse?.count
|
||||
})
|
||||
api('listPublicIpAddresses', { listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.ips = json?.listpublicipaddressesresponse?.count
|
||||
})
|
||||
api('listTemplates', { templatefilter: 'self', listall: true, page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.templates = json?.listtemplatesresponse?.count
|
||||
})
|
||||
},
|
||||
listInstances (zone) {
|
||||
this.loading = true
|
||||
api('listVirtualMachines', { listall: true, details: 'min', page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.instances = json?.listvirtualmachinesresponse?.count
|
||||
})
|
||||
api('listVirtualMachines', { listall: true, details: 'min', state: 'running', page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.running = json?.listvirtualmachinesresponse?.count
|
||||
})
|
||||
api('listVirtualMachines', { listall: true, details: 'min', state: 'stopped', page: 1, pagesize: 1 }).then(json => {
|
||||
this.loading = false
|
||||
this.data.stopped = json?.listvirtualmachinesresponse?.count
|
||||
})
|
||||
},
|
||||
listEvents () {
|
||||
const params = {
|
||||
page: 1,
|
||||
pagesize: 6,
|
||||
pagesize: 8,
|
||||
listall: true
|
||||
}
|
||||
this.loading = true
|
||||
|
|
@ -234,6 +542,37 @@ export default {
|
|||
}
|
||||
})
|
||||
},
|
||||
getLabel (usageType) {
|
||||
switch (usageType) {
|
||||
case 'vm':
|
||||
return 'label.instances'
|
||||
case 'cpu':
|
||||
return 'label.cpunumber'
|
||||
case 'memory':
|
||||
return 'label.memory'
|
||||
case 'primarystorage':
|
||||
return 'label.primary.storage'
|
||||
case 'secondarystorage':
|
||||
return 'label.secondary.storage'
|
||||
case 'ip':
|
||||
return 'label.public.ips'
|
||||
}
|
||||
return 'label.' + usageType + 's'
|
||||
},
|
||||
getValue (usageType, value) {
|
||||
switch (usageType) {
|
||||
case 'memory':
|
||||
return parseFloat(value / 1024.0).toFixed(2) + ' GiB'
|
||||
case 'primarystorage':
|
||||
return parseFloat(value).toFixed(2) + ' GiB'
|
||||
case 'secondarystorage':
|
||||
return parseFloat(value).toFixed(2) + ' GiB'
|
||||
}
|
||||
return value
|
||||
},
|
||||
getPercentUsed (total, limit) {
|
||||
return (limit === 'Unlimited') ? 0 : (total / limit) * 100
|
||||
},
|
||||
getEventColour (event) {
|
||||
if (event.level === 'ERROR') {
|
||||
return 'red'
|
||||
|
|
@ -242,13 +581,6 @@ export default {
|
|||
return 'green'
|
||||
}
|
||||
return 'blue'
|
||||
},
|
||||
onTabChange (key) {
|
||||
this.showAddAccount = false
|
||||
|
||||
if (key !== 'Dashboard') {
|
||||
this.showAddAccount = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -276,6 +608,23 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
.dashboard-card {
|
||||
width: 100%;
|
||||
min-height: 420px;
|
||||
}
|
||||
|
||||
.dashboard-event {
|
||||
width: 100%;
|
||||
overflow-x:hidden;
|
||||
overflow-y: scroll;
|
||||
max-height: 420px;
|
||||
}
|
||||
|
||||
.center {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.ant-col-xl-8 {
|
||||
width: 100%;
|
||||
|
|
|
|||
|
|
@ -293,11 +293,11 @@
|
|||
</div>
|
||||
<a-form-item v-if="selectedNetworkOfferingSupportsSourceNat" name="sourcenatipaddress" ref="sourcenatipaddress">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.routerip')" :tooltip="apiParams.sourcenatipaddress.description"/>
|
||||
<tooltip-label :title="$t('label.routerip')" :tooltip="apiParams.sourcenatipaddress?.description"/>
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="form.sourcenatipaddress"
|
||||
:placeholder="apiParams.sourcenatipaddress.description"/>
|
||||
:placeholder="apiParams.sourcenatipaddress?.description"/>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
ref="networkdomain"
|
||||
|
|
|
|||
|
|
@ -106,7 +106,8 @@ export default {
|
|||
fetchActionZoneData () {
|
||||
this.loading = true
|
||||
const params = {}
|
||||
if (this.resource.zoneid && this.$route.name === 'deployVirtualMachine') {
|
||||
console.log(this.resource)
|
||||
if (this.$route.name === 'deployVirtualMachine' && this.resource.zoneid) {
|
||||
params.id = this.resource.zoneid
|
||||
}
|
||||
this.actionZoneLoading = true
|
||||
|
|
|
|||
|
|
@ -157,11 +157,11 @@
|
|||
</a-row>
|
||||
<a-form-item v-if="selectedNetworkOfferingSupportsSourceNat" name="sourcenatipaddress" ref="sourcenatipaddress">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.routerip')" :tooltip="apiParams.sourcenatipaddress.description"/>
|
||||
<tooltip-label :title="$t('label.routerip')" :tooltip="apiParams.sourcenatipaddress?.description"/>
|
||||
</template>
|
||||
<a-input
|
||||
v-model:value="form.sourcenatipaddress"
|
||||
:placeholder="apiParams.sourcenatipaddress.description"/>
|
||||
:placeholder="apiParams.sourcenatipaddress?.description"/>
|
||||
</a-form-item>
|
||||
<a-form-item name="start" ref="start">
|
||||
<template #label>
|
||||
|
|
|
|||
Loading…
Reference in New Issue