| // 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-table | |
| size="middle" | |
| :loading="loading" | |
| :columns="isOrderUpdatable() ? columns : columns.filter(x => x.dataIndex !== 'order')" | |
| :dataSource="items" | |
| :rowKey="(record, idx) => record.id || record.name || record.usageType || idx + '-' + Math.random()" | |
| :pagination="false" | |
| :rowSelection="explicitlyAllowRowSelection || enableGroupAction() || $route.name === 'event' ? {selectedRowKeys: selectedRowKeys, onChange: onSelectChange, columnWidth: 30} : null" | |
| :rowClassName="getRowClassName" | |
| @resizeColumn="handleResizeColumn" | |
| :style="{ 'overflow-y': this.$route.name === 'usage' ? 'hidden' : 'auto' }" | |
| > | |
| <template #customFilterDropdown> | |
| <div | |
| style="padding: 8px" | |
| class="filter-dropdown" | |
| > | |
| <a-menu> | |
| <a-menu-item | |
| v-for="(column, idx) in columnKeys" | |
| :key="idx" | |
| @click="updateSelectedColumns(column)" | |
| > | |
| <a-checkbox | |
| :id="idx.toString()" | |
| :checked="selectedColumns.includes(getColumnKey(column))" | |
| /> | |
| {{ $t('label.' + String(getColumnTitle(column)).toLowerCase()) }} | |
| </a-menu-item> | |
| </a-menu> | |
| </div> | |
| </template> | |
| <template #bodyCell="{ column, text, record }"> | |
| <template v-if="['name', 'provider'].includes(column.key)"> | |
| <span | |
| v-if="['vm', 'vnfapp'].includes($route.path.split('/')[1])" | |
| style="margin-right: 5px" | |
| > | |
| <span v-if="record.icon && record.icon.base64image"> | |
| <resource-icon | |
| :image="record.icon.base64image" | |
| size="2x" | |
| /> | |
| </span> | |
| <span v-else-if="record.vmtype === 'sharedfsvm'"> | |
| <file-text-outlined style="font-size: 18px;" /> | |
| </span> | |
| <os-logo v-else :osId="record.ostypeid" :osName="record.osdisplayname" size="xl" /> | |
| </span> | |
| <span style="min-width: 120px"> | |
| <QuickView | |
| style="margin-left: 5px" | |
| :actions="actions" | |
| :resource="record" | |
| :enabled="quickViewEnabled(actions, columns, column.key)" | |
| @exec-action="$parent.execAction" | |
| /> | |
| <span | |
| v-if="$route.path.startsWith('/project')" | |
| style="margin-right: 5px" | |
| > | |
| <tooltip-button | |
| type="dashed" | |
| size="small" | |
| icon="LoginOutlined" | |
| @onClick="changeProject(record)" | |
| /> | |
| </span> | |
| <span v-if="$showIcon() && !['vm', 'vnfapp'].includes($route.path.split('/')[1])" style="margin-right: 5px"> | |
| <resource-icon v-if="$showIcon() && record.icon && record.icon.base64image" :image="record.icon.base64image" size="2x"/> | |
| <os-logo v-else-if="record.ostypename || ['guestoscategory'].includes($route.path.split('/')[1])" :osName="record.ostypename || record.name" size="xl" /> | |
| <render-icon v-else-if="typeof $route.meta.icon ==='string'" style="font-size: 16px;" :icon="$route.meta.icon"/> | |
| <render-icon v-else style="font-size: 16px;" :svgIcon="$route.meta.icon" /> | |
| </span> | |
| <span | |
| v-else | |
| :style="{ 'margin-right': record.ostypename ? '5px' : '0' }" | |
| > | |
| <os-logo | |
| v-if="record.ostypename" | |
| :osName="record.ostypename" | |
| size="xl" | |
| /> | |
| </span> | |
| <span v-if="record.hasannotations"> | |
| <span v-if="record.id"> | |
| <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link> | |
| <router-link :to="{ path: $route.path + '/' + record.id, query: { tab: 'comments' } }"><message-filled | |
| style="padding-left: 10px" | |
| size="small" | |
| /></router-link> | |
| </span> | |
| <router-link | |
| v-else | |
| :to="{ path: $route.path + '/' + record.name }" | |
| >{{ text }}</router-link> | |
| </span> | |
| <span v-else-if="$route.path.startsWith('/globalsetting')">{{ text }}</span> | |
| <span v-else-if="$route.path.startsWith('/alert')"> | |
| <router-link | |
| :to="{ path: $route.path + '/' + record.id }" | |
| v-if="record.id" | |
| >{{ $t(text.toLowerCase()) }}</router-link> | |
| <router-link | |
| :to="{ path: $route.path + '/' + record.name }" | |
| v-else | |
| >{{ $t(text.toLowerCase()) }}</router-link> | |
| </span> | |
| <span v-else-if="$route.path.startsWith('/tungstenfabric')"> | |
| <router-link | |
| :to="{ path: $route.path + '/' + record.id }" | |
| v-if="record.id" | |
| >{{ $t(text.toLowerCase()) }}</router-link> | |
| <router-link | |
| :to="{ path: $route.path + '/' + record.name }" | |
| v-else | |
| >{{ $t(text.toLowerCase()) }}</router-link> | |
| </span> | |
| <span v-else-if="isTungstenPath()"> | |
| <router-link | |
| :to="{ path: $route.path + '/' + record.uuid, query: { zoneid: record.zoneid } }" | |
| v-if="record.uuid && record.zoneid" | |
| >{{ $t(text.toLowerCase()) }}</router-link> | |
| <router-link | |
| :to="{ path: $route.path + '/' + record.uuid, query: { zoneid: $route.query.zoneid } }" | |
| v-else-if="record.uuid && $route.query.zoneid" | |
| >{{ $t(text.toLowerCase()) }}</router-link> | |
| <router-link | |
| :to="{ path: $route.path }" | |
| v-else | |
| >{{ $t(text.toLowerCase()) }}</router-link> | |
| </span> | |
| <span v-else-if="$route.path.startsWith('/guestnetwork') && record.id && record.displaynetwork === false"> | |
| <router-link | |
| :to="{ path: $route.path + '/' + record.id, query: { displaynetwork: false } }" | |
| v-if="record.id" | |
| >{{ $t(text.toLowerCase()) }}</router-link> | |
| </span> | |
| <span v-else-if="$route.path.startsWith('/gpucard') && record.gpucardid"> | |
| <router-link | |
| :to="{ path: '/vgpuprofile/' + record.id }" | |
| v-if="record.id" | |
| >{{ $t(text.toLowerCase()) }}</router-link> | |
| </span> | |
| <span v-else> | |
| <router-link :to="{ path: $route.path + '/' + encodeURIComponent(record.id) }" v-if="record.id">{{ text }}</router-link> | |
| <router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ text }}</router-link> | |
| <span v-if="['guestnetwork','vpc'].includes($route.path.split('/')[1]) && record.restartrequired && !record.vpcid"> | |
| | |
| <a-tooltip> | |
| <template #title>{{ $t('label.restartrequired') }}</template> | |
| <warning-outlined style="color: #f5222d" /> | |
| </a-tooltip> | |
| </span> | |
| <span v-else-if="$route.path.startsWith('/vpncustomergateway')"> | |
| | |
| <a-tooltip | |
| v-if="record.excludedparameters || record.obsoleteparameters" | |
| :title="$t('message.vpn.customer.gateway.contains.excluded.obsolete.parameters')"> | |
| <warning-outlined :style="{ color: $config.theme['@warning-color'] }" /> | |
| </a-tooltip> | |
| </span> | |
| </span> | |
| <span | |
| v-if="record.leaseduration !== undefined" | |
| :style="{ | |
| 'margin-right': '5px', | |
| 'float': 'right'}"> | |
| <a-tooltip> | |
| <template #title>{{ $t('label.remainingdays') + ": " + getRemainingLeaseText(record.leaseduration) }}</template> | |
| <field-time-outlined | |
| :style="{ | |
| color: getLeaseColor(record.leaseduration), | |
| fontSize: '20px' | |
| }"/> | |
| </a-tooltip> | |
| </span> | |
| <span | |
| v-if="['zone', 'host', 'vm', 'computeoffering', 'systemoffering'].includes($route.path.split('/')[1]) && (record?.gputotal > 0 || record?.gpucount > 0)" | |
| style="margin-right: 5px" | |
| > | |
| <a-tag style="margin-left: 5px">{{ $t('label.gpu.enabled') }}</a-tag> | |
| </span> | |
| <span v-if="$route.path.startsWith('/extension') && !record.isuserdefined" style="padding-left: 10px;"> | |
| <a-tag :color="$config.theme['@link-color']">{{ $t('label.inbuilt') }}</a-tag> | |
| </span> | |
| </span> | |
| </template> | |
| <template v-if="column.key === 'templatetype'"> | |
| <router-link :to="{ path: $route.path + '/' + record.templatetype }">{{ text }}</router-link> | |
| </template> | |
| <template v-if="$route.path.startsWith('/dnsserver') && !['name', 'provider', 'state', 'ispublic'].includes(column.key)"> | |
| <span>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'gpu'"> | |
| <span v-if="record.gpucardname && record.vgpuprofilename"> | |
| {{ record?.gpucount > 0 ? record.gpucount + 'x' : '' }} | |
| <router-link v-if="record.gpucardid" :to="{ path: '/gpucard/' + record.gpucardid }">{{ record.gpucardname }}</router-link> | |
| <span v-else>{{ record.gpucardname }}</span> | |
| <router-link v-if="record.vgpuprofileid && record.vgpuprofilename !== 'passthrough'" :to="{ path: '/vgpuprofile/' + record.vgpuprofileid }">{{ record.vgpuprofilename === 'passthrough' ? '' : "(" + record.vgpuprofilename + ")"}}</router-link> | |
| <span v-else>{{ record.vgpuprofilename === 'passthrough' ? '' : "(" + record.vgpuprofilename + ")"}}</span> | |
| </span> | |
| </template> | |
| <template v-if="column.key === 'resolution'"> | |
| <span v-if="record.maxresolutionx && record.maxresolutiony"> | |
| {{ record.maxresolutionx }}x{{ record.maxresolutiony }} | |
| </span> | |
| <span v-else> | |
| {{ text }} | |
| </span> | |
| </template> | |
| <template v-if="column.key === 'templateid'"> | |
| <router-link :to="{ path: '/template/' + record.templateid }">{{ text }}</router-link> | |
| </template> | |
| <template v-if="column.key === 'type'"> | |
| <span | |
| v-if="['USER.LOGIN', 'USER.LOGOUT', 'ROUTER.HEALTH.CHECKS', 'FIREWALL.CLOSE', 'ALERT.SERVICE.DOMAINROUTER'].includes(text)" | |
| >{{ $t(text.toLowerCase()) }}</span> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'schedule'"> | |
| <div v-if="['/snapshotpolicy', '/backupschedule'].some(path => $route.path.endsWith(path))"> | |
| <label class="interval-content"> | |
| <span v-if="record.intervaltype===0 || record.intervaltype==='HOURLY'">{{ record.schedule + $t('label.min.past.hour') }}</span> | |
| <span v-else>{{ record.schedule.split(':')[1] + ':' + record.schedule.split(':')[0] }}</span> | |
| </label> | |
| <span v-if="record.intervaltype===2 || record.intervaltype==='WEEKLY'"> | |
| {{ ` ${$t('label.every')} ${$t(listDayOfWeek[record.schedule.split(':')[2] - 1])}` }} | |
| </span> | |
| <span v-else-if="record.intervaltype===3 || record.intervaltype==='MONTHLY'"> | |
| {{ ` ${$t('label.day')} ${record.schedule.split(':')[2]} ${$t('label.of.month')}` }} | |
| </span> | |
| </div> | |
| <div v-else> | |
| {{ text }} | |
| <br /> | |
| ({{ generateHumanReadableSchedule(text) }}) | |
| </div> | |
| </template> | |
| <template v-if="column.key === 'intervaltype' && ['/snapshotpolicy', '/backupschedule'].some(path => $route.path.endsWith(path))"> | |
| <QuickView | |
| style="margin-right: 8px" | |
| :actions="actions" | |
| :resource="record" | |
| :enabled="quickViewEnabled(actions, columns, column.key)" | |
| @exec-action="$parent.execAction" | |
| /> | |
| <span v-if="record.intervaltype===0"> | |
| <clock-circle-outlined /> | |
| </span> | |
| <span class="custom-icon icon-daily" v-else-if="record.intervaltype===1"> | |
| <calendar-outlined /> | |
| </span> | |
| <span class="custom-icon icon-weekly" v-else-if="record.intervaltype===2"> | |
| <calendar-outlined /> | |
| </span> | |
| <span class="custom-icon icon-monthly" v-else-if="record.intervaltype===3"> | |
| <calendar-outlined /> | |
| </span> | |
| {{ getIntervalTypeText(record.intervaltype) }} | |
| </template> | |
| <template v-if="column.key === 'timezone'"> | |
| <label>{{ getTimeZone(record.timezone) }}</label> | |
| </template> | |
| <template v-if="column.key === 'displayname'"> | |
| <QuickView | |
| style="margin-left: 5px" | |
| :actions="actions" | |
| :resource="record" | |
| :enabled="quickViewEnabled(actions, columns, column.key)" | |
| @exec-action="$parent.execAction" | |
| /> | |
| <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link> | |
| </template> | |
| <template v-if="column.key === 'username'"> | |
| <span | |
| v-if="$showIcon() && !['vm', 'vnfapp'].includes($route.path.split('/')[1])" | |
| style="margin-right: 5px" | |
| > | |
| <resource-icon | |
| v-if="$showIcon() && record.icon && record.icon.base64image" | |
| :image="record.icon.base64image" | |
| size="2x" | |
| /> | |
| <user-outlined | |
| v-else | |
| style="font-size: 16px;" | |
| /> | |
| </span> | |
| <router-link | |
| :to="{ path: $route.path + '/' + record.id }" | |
| v-if="['/accountuser', '/vpnuser'].includes($route.path)" | |
| >{{ text }}</router-link> | |
| <router-link | |
| :to="{ path: '/accountuser', query: { username: record.username, domainid: record.domainid } }" | |
| v-else-if="$store.getters.userInfo.roletype !== 'User'" | |
| >{{ text }}</router-link> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'entityid'"> | |
| <router-link :to="{ path: generateCommentsPath(record), query: { tab: 'comments' } }">{{ record.entityname | |
| }}</router-link> | |
| </template> | |
| <template v-if="column.key === 'entitytype'"> | |
| {{ generateHumanReadableEntityType(record) }} | |
| </template> | |
| <template v-if="column.key === 'adminsonly' && ['Admin'].includes($store.getters.userInfo.roletype)"> | |
| <a-checkbox | |
| :checked="record.adminsonly" | |
| :value="record.id" | |
| v-if="record.userid === $store.getters.userInfo.id" | |
| @change="e => updateAdminsOnly(e)" | |
| /> | |
| <a-checkbox | |
| :checked="record.adminsonly" | |
| disabled | |
| v-else | |
| /> | |
| </template> | |
| <template | |
| v-if="column.key === 'ipaddress'" | |
| href="javascript:;" | |
| > | |
| <router-link | |
| v-if="['/publicip', '/privategw'].includes($route.path)" | |
| :to="{ path: $route.path + '/' + record.id }" | |
| >{{ text }}</router-link> | |
| <span v-else> | |
| <copy-label :label="text" /> | |
| </span> | |
| <span v-if="record.issourcenat"> | |
| | |
| <a-tag>source-nat</a-tag> | |
| </span> | |
| <span v-if="record.isstaticnat"> | |
| | |
| <a-tag>static-nat</a-tag> | |
| </span> | |
| <span v-if="record.issystem"> | |
| | |
| <a-tag>system</a-tag> | |
| </span> | |
| </template> | |
| <template | |
| v-if="column.key === 'ip6address'" | |
| href="javascript:;" | |
| > | |
| <span>{{ ipV6Address(text, record) }}</span> | |
| </template> | |
| <template v-if="column.key === 'publicip'"> | |
| <router-link | |
| v-if="['/autoscalevmgroup'].includes($route.path)" | |
| :to="{ path: '/publicip' + '/' + record.publicipid }" | |
| >{{ text }}</router-link> | |
| <router-link | |
| v-else | |
| :to="{ path: $route.path + '/' + record.id }" | |
| >{{ text }}</router-link> | |
| </template> | |
| <template v-if="column.key === 'traffictype'"> | |
| {{ text }} | |
| </template> | |
| <template v-if="column.key === 'vmname'"> | |
| <router-link :to="{ path: createPathBasedOnVmType(record.vmtype, record.virtualmachineid) }">{{ text | |
| }}</router-link> | |
| </template> | |
| <template v-if="column.key === 'virtualmachinename'"> | |
| <router-link v-if="record.virtualmachineid" :to="{ path: getVmRouteUsingType(record) + record.virtualmachineid }">{{ text }}</router-link> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'volumename'"> | |
| <router-link | |
| v-if="resourceIdToValidLinksMap[record.id]?.volume" | |
| :to="{ path: '/volume/' + record.volumeid }" | |
| >{{ text }}</router-link> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template | |
| v-if="record.clustertype === 'ExternalManaged' && $route.path.split('/')[1] === 'kubernetes' && ['kubernetesversionname', 'cpunumber', 'memory', 'size'].includes(column.key)" | |
| > | |
| <span>{{ text <= | |
| 0 | |
| || | |
| !text | |
| ? 'N/A' | |
| : | |
| text | |
| }}</span | |
| > | |
| </template> | |
| <template v-else-if="['size', 'virtualsize'].includes(column.key)"> | |
| <span v-if="text && $route.path === '/kubernetes'"> | |
| {{ text }} | |
| </span> | |
| <span v-else-if="text"> | |
| {{ parseFloat(parseFloat(text) / 1024.0 / 1024.0 / 1024.0).toFixed(2) }} GiB | |
| </span> | |
| </template> | |
| <template v-if="column.key === 'physicalsize'"> | |
| <span v-if="text"> | |
| {{ isNaN(text) ? text : (parseFloat(parseFloat(text) / 1024.0 / 1024.0 / 1024.0).toFixed(2) + ' GiB') }} | |
| </span> | |
| </template> | |
| <template v-if="column.key === 'videoram'"> | |
| <span v-if="text"> | |
| {{ isNaN(text) ? text : (parseFloat(text) / 1024.0).toFixed(2) + ' GB' }} | |
| </span> | |
| </template> | |
| <template v-if="column.key === 'physicalnetworkname'"> | |
| <router-link :to="{ path: '/physicalnetwork/' + record.physicalnetworkid }">{{ text }}</router-link> | |
| </template> | |
| <template v-if="column.key === 'serviceofferingname'"> | |
| <router-link v-if="$route.path === '/networkoffering'" :to="{ path: '/systemoffering/' + record.serviceofferingid, query:{issystem:'true'} }">{{ text }}</router-link> | |
| <router-link v-else :to="{ path: '/computeoffering/' + record.serviceofferingid }">{{ text }}</router-link> | |
| </template> | |
| <template v-if="column.key === 'hypervisor'"> | |
| <span v-if="$route.name === 'hypervisorcapability'"> | |
| <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link> | |
| </span> | |
| <span v-else-if="$route.name === 'guestoshypervisormapping'"> | |
| <QuickView | |
| style="margin-left: 5px" | |
| :actions="actions" | |
| :resource="record" | |
| :enabled="quickViewEnabled(actions, columns, column.key)" | |
| @exec-action="$parent.execAction" | |
| /> | |
| <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link> | |
| </span> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'osname'"> | |
| <span v-if="$route.name === 'guestos'"> | |
| <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link> | |
| </span> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'oscategoryname'"> | |
| <span v-if="('listOsCategories' in $store.getters.apis) && record.oscategoryid"> | |
| <router-link :to="{ path: '/guestoscategory/' + record.oscategoryid }">{{ text }}</router-link> | |
| </span> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'isuserdefined'"> | |
| <span>{{ text ? $t('label.yes') : $t('label.no') }}</span> | |
| </template> | |
| <template v-if="column.key === 'ispublic'"> | |
| <span>{{ text ? $t('label.yes') : $t('label.no') }}</span> | |
| </template> | |
| <template v-if="column.key === 'state'"> | |
| <status | |
| v-if="$route.path.startsWith('/host')" | |
| :text="getHostState(record)" | |
| displayText | |
| /> | |
| <status | |
| v-else | |
| :text="text ? text : ''" | |
| displayText | |
| :styles="{ 'min-width': '80px' }" | |
| /> | |
| </template> | |
| <template v-if="column.key === 'status'"> | |
| <status | |
| :text="text ? text : ''" | |
| displayText | |
| /> | |
| </template> | |
| <template v-if="column.key === 'compressionstatus'"> | |
| <status :text="text ? text : $t('label.unknown')" displayText /> | |
| </template> | |
| <template v-if="column.key === 'validationstatus'"> | |
| <status :text="text ? text : $t('label.unknown')" displayText /> | |
| </template> | |
| <template v-if="column.key === 'allocationstate'"> | |
| <status | |
| :text="text ? text : ''" | |
| displayText | |
| /> | |
| </template> | |
| <template v-if="column.key === 'redundantstate'"> | |
| <status | |
| v-if="record && record.isredundantrouter" | |
| :text="text ? text : ''" | |
| displayText | |
| /> | |
| <status | |
| v-else | |
| :text="'N/A'" | |
| displayText | |
| :styles="{ 'min-width': '80px' }" | |
| /> | |
| </template> | |
| <template v-if="column.key === 'resourcestate'"> | |
| <status | |
| :text="text ? text : ''" | |
| displayText | |
| /> | |
| </template> | |
| <template v-if="column.key === 'powerstate'"> | |
| <status | |
| :text="text ? text : ''" | |
| displayText | |
| /> | |
| </template> | |
| <template v-if="column.key === 'agentstate'"> | |
| <status | |
| :text="text ? text : ''" | |
| displayText | |
| /> | |
| </template> | |
| <template v-if="column.key === 'availability' && $route.path.startsWith('/extension')"> | |
| <status :text="text ? text : ''" displayText /> | |
| </template> | |
| <template v-if="column.key === 'cpunumber' && $route.path.split('/')[1] !== 'kubernetes'"> | |
| <span>{{ record.serviceofferingdetails?.mincpunumber && record.serviceofferingdetails?.maxcpunumber ? | |
| `${record.serviceofferingdetails.mincpunumber} - ${record.serviceofferingdetails.maxcpunumber}` : | |
| record.cpunumber }}</span> | |
| </template> | |
| <template v-if="column.key === 'memory' && $route.path.split('/')[1] !== 'kubernetes'"> | |
| <span>{{ record.serviceofferingdetails?.minmemory && record.serviceofferingdetails?.maxmemory ? | |
| `${record.serviceofferingdetails.minmemory} - ${record.serviceofferingdetails.maxmemory}` : record.memory | |
| }}</span> | |
| </template> | |
| <template v-if="column.key === 'quotastate'"> | |
| <status | |
| :text="text ? text : ''" | |
| displayText | |
| /> | |
| </template> | |
| <template v-if="column.key === 'vmstate'"> | |
| <status | |
| :text="text ? text : ''" | |
| displayText | |
| vmState | |
| /> | |
| </template> | |
| <template v-if="column.key === 'offerha'"> | |
| {{ text ? $t('state.enabled') : $t('state.disabled')}} | |
| </template> | |
| <template v-if="column.key === 'vlan'"> | |
| <a href="javascript:;"> | |
| <router-link | |
| v-if="$route.path === '/guestvlans'" | |
| :to="{ path: '/guestvlans/' + record.id }" | |
| >{{ text }}</router-link> | |
| </a> | |
| </template> | |
| <template v-if="column.key === 'networkname'"> | |
| <router-link :to="{ path: '/guestnetwork/' + record.networkid }">{{ text }}</router-link> | |
| </template> | |
| <template v-if="column.key === 'guestnetworkname'"> | |
| <span v-if="['/router'].includes($route.path) && record.vpcid"> | |
| <router-link :to="{ path: '/vpc/' + record.vpcid }"> | |
| <deployment-unit-outlined /> | |
| {{ record.vpcname || record.vpcid }} | |
| </router-link> | |
| </span> | |
| <router-link | |
| v-else | |
| :to="{ path: '/guestnetwork/' + record.guestnetworkid }" | |
| > | |
| <apartment-outlined /> | |
| {{ text }} | |
| </router-link> | |
| </template> | |
| <template v-if="column.key === 'guest.networks' && record.network"> | |
| <template | |
| v-for="(item, idx) in record.network" | |
| :key="idx" | |
| > | |
| <router-link :to="{ path: '/guestnetwork/' + item.id }">{{ item.name }}</router-link> | |
| <span v-if="idx < (record.network.length - 1)">, </span> | |
| </template> | |
| </template> | |
| <template v-if="column.key === 'associatednetworkname'"> | |
| <router-link :to="{ path: '/guestnetwork/' + record.associatednetworkid }">{{ text }}</router-link> | |
| </template> | |
| <template v-if="column.key === 'vpcname'"> | |
| <a v-if="record.vpcid"> | |
| <router-link :to="{ path: '/vpc/' + record.vpcid }">{{ text || record.vpcid }}</router-link> | |
| </a> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'subnet'"> | |
| <a href="javascript:;"> | |
| <router-link | |
| v-if="$route.path === '/ipv4subnets'" | |
| :to="{ path: '/ipv4subnets/' + record.id }" | |
| >{{ text }}</router-link> | |
| </a> | |
| </template> | |
| <template v-if="column.key === 'hostname'"> | |
| <router-link | |
| v-if="record.hostid" | |
| :to="{ path: '/host/' + record.hostid }" | |
| >{{ text }}</router-link> | |
| <router-link | |
| v-else-if="record.hostname" | |
| :to="{ path: $route.path + '/' + record.id }" | |
| >{{ text }}</router-link> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'storage'"> | |
| <router-link v-if="record.storageid" :to="{ path: '/storagepool/' + encodeURIComponent(record.storageid) }">{{ text }}</router-link> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template | |
| v-for="(value, name) in thresholdMapping" | |
| :key="name" | |
| > | |
| <template v-if="column.key === name"> | |
| <span> | |
| <span | |
| v-if="record[value.disable]" | |
| class="alert-disable-threshold" | |
| > | |
| {{ text }} | |
| </span> | |
| <span | |
| v-else-if="record[value.notification]" | |
| class="alert-notification-threshold" | |
| > | |
| {{ text }} | |
| </span> | |
| <span | |
| style="padding: 10%;" | |
| v-else | |
| > | |
| {{ text }} | |
| </span> | |
| </span> | |
| </template> | |
| </template> | |
| <template v-if="column.key === 'level'"> | |
| <router-link :to="{ path: '/event/' + record.id }">{{ text }}</router-link> | |
| </template> | |
| <template v-if="column.key === 'usageType'"> | |
| {{ usageTypeMap[record.usagetype] }} | |
| </template> | |
| <template v-if="column.key === 'clustername'"> | |
| <router-link :to="{ path: '/cluster/' + record.clusterid }">{{ text }}</router-link> | |
| </template> | |
| <template v-if="column.key === 'objectstore'"> | |
| <router-link :to="{ path: '/objectstore/' + record.objectstorageid }">{{ text }}</router-link> | |
| </template> | |
| <template v-if="column.key === 'hsmprofile'"> | |
| <router-link v-if="record.hsmprofileid" :to="{ path: '/hsmprofile/' + record.hsmprofileid }">{{ text }}</router-link> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'podname'"> | |
| <router-link :to="{ path: '/pod/' + record.podid }">{{ text }}</router-link> | |
| </template> | |
| <template v-if="column.key === 'account'"> | |
| <template v-if="record.owner"> | |
| <template | |
| v-for="(item, idx) in record.owner" | |
| :key="idx" | |
| > | |
| <span style="margin-right:5px"> | |
| <span v-if="$store.getters.userInfo.roletype !== 'User'"> | |
| <router-link | |
| v-if="'user' in item" | |
| :to="{ path: '/accountuser', query: { username: item.user, domainid: record.domainid }}" | |
| >{{ item.account + '(' + item.user + ')' }}</router-link> | |
| <router-link | |
| v-else | |
| :to="{ path: '/account', query: { name: item.account, domainid: record.domainid, dataView: true } }" | |
| >{{ item.account }}</router-link> | |
| </span> | |
| <span v-else>{{ item.user ? item.account + '(' + item.user + ')' : item.account }}</span> | |
| </span> | |
| </template> | |
| </template> | |
| <template v-if="text"> | |
| <template v-if="!text.startsWith('PrjAcct-')"> | |
| <router-link | |
| v-if="$route.path.startsWith('/quotasummary')" | |
| :to="{ path: `${$route.path}/${record.accountid}` }">{{ text }}</router-link> | |
| <router-link v-else-if="record.accountid" :to="{ path: '/account/' + record.accountid }">{{ text }}</router-link> | |
| <router-link | |
| v-else-if="$store.getters.userInfo.roletype !== 'User'" | |
| :to="{ path: '/account', query: { name: record.account, domainid: record.domainid, dataView: true } }"> | |
| {{ text }} | |
| </router-link> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-else-if="$route.path.startsWith('/quotasummary')"> | |
| <router-link :to="{ path: `${$route.path}/${record.accountid}` }"> | |
| {{ (record.projectname || record.account).concat(' (').concat($t('label.project')).concat(')') }} | |
| </router-link> | |
| </template> | |
| </template> | |
| </template> | |
| <template v-if="column.key === 'resource'"> | |
| <resource-label | |
| :resourceType="record.resourcetype" | |
| :resourceId="record.resourceid" | |
| :resourceName="record.resourcename" | |
| /> | |
| </template> | |
| <template v-if="column.key === 'domain'"> | |
| <span v-if="record.domainid && $store.getters.userInfo.roletype !== 'User'"> | |
| <template v-for="(id, idx) in record.domainid.split(',')" :key="id"> | |
| <router-link :to="{ path: '/domain/' + id, query: { tab: 'details' } }"> | |
| {{ record.domain.split(',')[idx] || id }} | |
| </router-link> | |
| <span v-if="idx < record.domainid.split(',').length - 1">, </span> | |
| </template> | |
| </span> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'domainpath'"> | |
| <router-link | |
| v-if="record.domainid && !record.domainid.includes(',') && $router.resolve('/domain/' + record.domainid).matched[0].redirect !== '/exception/404'" | |
| :to="{ path: '/domain/' + record.domainid, query: { tab: 'details' } }" | |
| >{{ text }}</router-link> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'zone'"> | |
| <router-link | |
| v-if="record.zoneid && !record.zoneid.includes(',') && $router.resolve('/zone/' + record.zoneid).matched[0].redirect !== '/exception/404'" | |
| :to="{ path: '/zone/' + record.zoneid }" | |
| >{{ text || record.zoneid }}</router-link> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'zonename'"> | |
| <router-link | |
| v-if="$router.resolve('/zone/' + record.zoneid).matched[0].redirect !== '/exception/404'" | |
| :to="{ path: '/zone/' + record.zoneid }" | |
| >{{ text }}</router-link> | |
| <router-link | |
| v-else-if="$router.resolve('/zones/' + record.zoneid).matched[0].redirect !== '/exception/404'" | |
| :to="{ path: '/zones/' + record.zoneid }" | |
| >{{ text }}</router-link> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'rolename'"> | |
| <router-link | |
| v-if="record.roleid && $router.resolve('/role/' + record.roleid).matched[0].redirect !== '/exception/404'" | |
| :to="{ path: '/role/' + record.roleid }" | |
| >{{ text }}</router-link> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'project'"> | |
| <router-link | |
| v-if="$router.resolve('/project/' + record.projectid).matched[0].redirect !== '/exception/404'" | |
| :to="{ path: '/project/' + record.projectid }" | |
| >{{ text }}</router-link> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'parentname' && ['snapshot'].includes($route.path.split('/')[1])"> | |
| <router-link | |
| v-if="record.parent && $router.resolve('/snapshot/' + record.parent).matched[0].redirect !== '/exception/404'" | |
| :to="{ path: '/snapshot/' + record.parent }" | |
| >{{ text }}</router-link> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'parentName' && ['vmsnapshot'].includes($route.path.split('/')[1])"> | |
| <router-link | |
| v-if="record.parent && $router.resolve('/vmsnapshot/' + record.parent).matched[0].redirect !== '/exception/404'" | |
| :to="{ path: '/vmsnapshot/' + record.parent }" | |
| >{{ text }}</router-link> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'templateversion'"> | |
| <span> {{ record.version }} </span> | |
| </template> | |
| <template v-if="column.key === 'drsimbalance'"> | |
| <span> {{ record.drsimbalance }} </span> | |
| </template> | |
| <template v-if="column.key === 'softwareversion'"> | |
| <span> {{ record.softwareversion ? record.softwareversion : 'N/A' }} </span> | |
| </template> | |
| <template v-if="column.key === 'readonly'"> | |
| <status | |
| :text="record.readonly ? 'ReadOnly' : 'ReadWrite'" | |
| displayText | |
| /> | |
| </template> | |
| <template v-if="column.key === 'requiresupgrade'"> | |
| <status :text="record.requiresupgrade ? 'warning' : ''" /> | |
| {{ record.requiresupgrade ? 'Yes' : 'No' }} | |
| </template> | |
| <template v-if="column.key === 'loadbalancerrule'"> | |
| <span> {{ record.loadbalancerrule }} </span> | |
| </template> | |
| <template v-if="column.key === 'autoscalingenabled'"> | |
| <status :text="record.autoscalingenabled ? 'Enabled' : 'Disabled'" /> | |
| {{ record.autoscalingenabled ? 'Enabled' : 'Disabled' }} | |
| </template> | |
| <template v-if="column.key === 'current'"> | |
| <status :text="record.current ? record.current.toString() : 'false'" /> | |
| </template> | |
| <template v-if="column.key === 'enabled'"> | |
| <status :text="record.enabled ? record.enabled.toString() : 'false'" /> | |
| {{ record.enabled ? 'Enabled' : 'Disabled' }} | |
| </template> | |
| <template v-if="column.key === 'egressdefaultpolicy'"> | |
| <span> {{ record.egressdefaultpolicy ? 'Allow' : 'Deny' }} </span> | |
| </template> | |
| <template v-if="['created', 'sent', 'removed', 'effectiveDate', 'endDate', 'allocated', 'startdate', 'enddate'].includes(column.key) || (['startdate'].includes(column.key) && ['webhook'].includes($route.path.split('/')[1])) || (column.key === 'allocated' && ['asnumbers', 'publicip', 'ipv4subnets'].includes($route.meta.name) && text)"> | |
| {{ text && $toLocaleDate(text) }} | |
| </template> | |
| <template | |
| v-if="['startdate', 'enddate'].includes(column.key) && ['vm', 'vnfapp', 'autoscalevmgroup'].includes($route.path.split('/')[1])" | |
| > | |
| {{ getDateAtTimeZone(text, record.timezone) }} | |
| </template> | |
| <template v-if="column.key === 'payloadurl'"> | |
| <copy-label :label="text" /> | |
| </template> | |
| <template v-if="column.key === 'usageid'"> | |
| <copy-label :label="text" /> | |
| </template> | |
| <template v-if="column.key === 'eventtype'"> | |
| <router-link | |
| v-if="$router.resolve('/event/' + record.eventid).matched[0].redirect !== '/exception/404'" | |
| :to="{ path: '/event/' + record.eventid }" | |
| >{{ text }}</router-link> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'gpucardname'"> | |
| <router-link | |
| v-if="$router.resolve('/gpucard/' + record.gpucardid).matched[0].redirect !== '/exception/404'" | |
| :to="{ path: '/gpucard/' + record.gpucardid }" | |
| >{{ text }}</router-link> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'managementservername'"> | |
| <router-link | |
| v-if="$router.resolve('/managementserver/' + record.managementserverid).matched[0].redirect !== '/exception/404'" | |
| :to="{ path: '/managementserver/' + record.managementserverid }" | |
| >{{ text }}</router-link> | |
| <router-link | |
| v-else-if="$router.resolve('/managementservers/' + record.managementserverid).matched[0].redirect !== '/exception/404'" | |
| :to="{ path: '/managementservers/' + record.managementserverid }" | |
| >{{ text }}</router-link> | |
| <span v-else>{{ text }}</span> | |
| </template> | |
| <template v-if="column.key === 'payload'"> | |
| <router-link | |
| v-if="$router.resolve('/webhookdeliveries/' + record.id).matched[0].redirect !== '/exception/404'" | |
| :to="{ path: '/webhookdeliveries/' + record.id }" | |
| >{{ getTrimmedText(text, 48) }}</router-link> | |
| <span v-else> {{ getTrimmedText(text, 48) }} </span> | |
| <QuickView | |
| style="margin-left: 5px" | |
| :actions="actions" | |
| :resource="record" | |
| :enabled="quickViewEnabled(actions, columns, column.key)" | |
| @exec-action="$parent.execAction" | |
| /> | |
| </template> | |
| <template v-if="column.key === 'webhookname'"> | |
| <router-link | |
| v-if="$router.resolve('/webhook/' + record.webhookid).matched[0].redirect !== '/exception/404'" | |
| :to="{ path: '/webhook/' + record.webhookid }" | |
| >{{ text }}</router-link> | |
| <span v-else> {{ text }} </span> | |
| </template> | |
| <template v-if="column.key === 'extensionname'"> | |
| <router-link v-if="$router.resolve('/extension/' + record.extensionid).matched[0].redirect !== '/exception/404'" :to="{ path: '/extension/' + record.extensionid }">{{ text }}</router-link> | |
| <span v-else> {{ text + record}} </span> | |
| </template> | |
| <template v-if="column.key === 'success'"> | |
| <status :text="text ? 'success' : 'error'" /> | |
| </template> | |
| <template v-if="column.key === 'response'"> | |
| <span> {{ getTrimmedText(text, 48) }} </span> | |
| </template> | |
| <template | |
| v-if="column.key === 'duration' && ['webhook', 'webhookdeliveries'].includes($route.path.split('/')[1])"> | |
| <span> {{ getDuration(record.startdate, record.enddate) }} </span> | |
| </template> | |
| <template v-if="['startdate', 'enddate'].includes(column.key) && ['usage'].includes($route.path.split('/')[1])"> | |
| {{ $toLocaleDate(text.replace('\'T\'', ' ')) }} | |
| </template> | |
| <template v-if="['isfeatured'].includes(column.key) && ['guestoscategory'].includes($route.path.split('/')[1])"> | |
| {{ record.isfeatured ? $t('label.yes') : $t('label.no') }} | |
| </template> | |
| <template v-if="['agentscount'].includes(column.key)"> | |
| <router-link | |
| v-if="['managementserver'].includes($route.path.split('/')[1]) && $router.resolve('/host').matched[0].redirect !== '/exception/404'" | |
| :to="{ path: '/host', query: { managementserverid: record.id } }"> | |
| {{ text }} | |
| </router-link> | |
| <span v-else> {{ text }} </span> | |
| </template> | |
| <template v-if="column.key === 'order'"> | |
| <div class="shift-btns"> | |
| <a-tooltip | |
| :name="text" | |
| placement="top" | |
| > | |
| <template #title>{{ $t('label.move.to.top') }}</template> | |
| <a-button | |
| shape="round" | |
| @click="moveItemTop(record)" | |
| class="shift-btn" | |
| > | |
| <DoubleLeftOutlined class="shift-btn shift-btn--rotated" /> | |
| </a-button> | |
| </a-tooltip> | |
| <a-tooltip placement="top"> | |
| <template #title>{{ $t('label.move.to.bottom') }}</template> | |
| <a-button | |
| shape="round" | |
| @click="moveItemBottom(record)" | |
| class="shift-btn" | |
| > | |
| <DoubleRightOutlined class="shift-btn shift-btn--rotated" /> | |
| </a-button> | |
| </a-tooltip> | |
| <a-tooltip placement="top"> | |
| <template #title>{{ $t('label.move.up.row') }}</template> | |
| <a-button | |
| shape="round" | |
| @click="moveItemUp(record)" | |
| class="shift-btn" | |
| > | |
| <CaretUpOutlined class="shift-btn" /> | |
| </a-button> | |
| </a-tooltip> | |
| <a-tooltip placement="top"> | |
| <template #title>{{ $t('label.move.down.row') }}</template> | |
| <a-button | |
| shape="round" | |
| @click="moveItemDown(record)" | |
| class="shift-btn" | |
| > | |
| <CaretDownOutlined class="shift-btn" /> | |
| </a-button> | |
| </a-tooltip> | |
| </div> | |
| </template> | |
| <template v-if="column.key === 'value'"> | |
| <a-input | |
| v-if="editableValueKey === record.key" | |
| v-focus="true" | |
| :defaultValue="record.value" | |
| :disabled="!('updateConfiguration' in $store.getters.apis)" | |
| v-model:value="editableValue" | |
| @keydown.esc="editableValueKey = null" | |
| @pressEnter="saveValue(record)" | |
| > | |
| </a-input> | |
| <template v-else-if="['webhook'].includes($route.path.split('/')[1])"> | |
| <span style="word-break: break-all">{{ text }}</span> | |
| <QuickView | |
| style="margin-left: 5px" | |
| :actions="actions" | |
| :resource="record" | |
| :enabled="quickViewEnabled(actions, columns, column.key)" | |
| @exec-action="$parent.execAction" | |
| /> | |
| </template> | |
| <div | |
| v-else | |
| style="width: 200px; word-break: break-all" | |
| > | |
| {{ text }} | |
| </div> | |
| </template> | |
| <template v-if="column.key === 'actions'"> | |
| <tooltip-button | |
| :tooltip="$t('label.edit')" | |
| :disabled="!('updateConfiguration' in $store.getters.apis)" | |
| v-if="editableValueKey !== record.key" | |
| icon="edit-outlined" | |
| @onClick="editValue(record)" | |
| /> | |
| <tooltip-button | |
| :tooltip="$t('label.cancel')" | |
| @onClick="editableValueKey = null" | |
| v-if="editableValueKey === record.key" | |
| iconType="CloseCircleTwoTone" | |
| iconTwoToneColor="#f5222d" | |
| /> | |
| <tooltip-button | |
| :tooltip="$t('label.ok')" | |
| :disabled="!('updateConfiguration' in $store.getters.apis)" | |
| @onClick="saveValue(record)" | |
| v-if="editableValueKey === record.key" | |
| iconType="CheckCircleTwoTone" | |
| iconTwoToneColor="#52c41a" | |
| /> | |
| <tooltip-button | |
| :tooltip="$t('label.reset.config.value')" | |
| @onClick="$resetConfigurationValueConfirm(item, resetConfig)" | |
| v-if="editableValueKey !== record.key" | |
| icon="reload-outlined" | |
| :disabled="!('resetConfiguration' in $store.getters.apis) || record.value === record.defaultvalue" | |
| /> | |
| </template> | |
| <template v-if="column.key === 'gpuDeviceActions'"> | |
| <a-popconfirm | |
| v-if="record.state === 'Disabled'" | |
| :title="`${$t('label.action.enable.gpu.device')}?`" | |
| @confirm="$emit('enable-gpu-device', record)" | |
| :okText="$t('label.yes')" | |
| :cancelText="$t('label.no')" | |
| > | |
| <tooltip-button | |
| :tooltip="$t('label.enable.gpu.device')" | |
| :disabled="!('enableGpuDevice' in $store.getters.apis)" | |
| icon="play-circle-outlined" | |
| /> | |
| </a-popconfirm> | |
| <a-popconfirm | |
| v-else | |
| :title="`${$t('label.action.disable.gpu.device')}?`" | |
| @confirm="$emit('disable-gpu-device', record)" | |
| :okText="$t('label.yes')" | |
| :cancelText="$t('label.no')" | |
| > | |
| <tooltip-button | |
| :tooltip="$t('label.disable.gpu.device')" | |
| :disabled="!('disableGpuDevice' in $store.getters.apis)" | |
| icon="pause-circle-outlined" | |
| /> | |
| </a-popconfirm> | |
| </template> | |
| <template v-if="column.key === 'usageActions'"> | |
| <tooltip-button | |
| :tooltip="$t('label.view')" | |
| icon="search-outlined" | |
| @onClick="$emit('view-usage-record', record)" | |
| /> | |
| <slot></slot> | |
| </template> | |
| <template v-if="column.key === 'scheduleActions'"> | |
| <slot | |
| name="scheduleActions" | |
| :record="record" | |
| ></slot> | |
| </template> | |
| <template v-if="column.key === 'vgpuActions'"> | |
| <slot name="actionButtons" :record="record" :actions="actions"></slot> | |
| </template> | |
| </template> | |
| <template #footer> | |
| <span v-if="hasSelected"> | |
| {{ `Selected ${selectedRowKeys.length} items` }} | |
| </span> | |
| </template> | |
| </a-table> | |
| </template> | |
| <script> | |
| import { getAPI, postAPI } from '@/api' | |
| import OsLogo from '@/components/widgets/OsLogo' | |
| import Status from '@/components/widgets/Status' | |
| import QuickView from '@/components/view/QuickView' | |
| import CopyLabel from '@/components/widgets/CopyLabel' | |
| import TooltipButton from '@/components/widgets/TooltipButton' | |
| import ResourceIcon from '@/components/view/ResourceIcon' | |
| import ResourceLabel from '@/components/widgets/ResourceLabel' | |
| import { createPathBasedOnVmType } from '@/utils/plugins' | |
| import { validateLinks } from '@/utils/links' | |
| import cronstrue from 'cronstrue/i18n' | |
| import moment from 'moment-timezone' | |
| import { timeZoneName } from '@/utils/timezone' | |
| import { FileTextOutlined } from '@ant-design/icons-vue' | |
| export default { | |
| name: 'ListView', | |
| components: { | |
| OsLogo, | |
| Status, | |
| QuickView, | |
| CopyLabel, | |
| TooltipButton, | |
| ResourceIcon, | |
| ResourceLabel, | |
| FileTextOutlined | |
| }, | |
| props: { | |
| columns: { | |
| type: Array, | |
| required: true | |
| }, | |
| columnKeys: { | |
| type: Array, | |
| default: () => [] | |
| }, | |
| selectedColumns: { | |
| type: Array, | |
| default: () => [] | |
| }, | |
| items: { | |
| type: Array, | |
| required: true | |
| }, | |
| loading: { | |
| type: Boolean, | |
| default: false | |
| }, | |
| actions: { | |
| type: Array, | |
| default: () => [] | |
| }, | |
| explicitlyAllowRowSelection: { | |
| type: Boolean, | |
| default: false | |
| }, | |
| currentPage: { | |
| type: Number | |
| }, | |
| pageSize: { | |
| type: Number | |
| } | |
| }, | |
| inject: ['parentFetchData', 'parentToggleLoading'], | |
| data () { | |
| return { | |
| selectedRowKeys: [], | |
| editableValueKey: null, | |
| editableValue: '', | |
| resourceIcon: '', | |
| thresholdMapping: { | |
| cpuused: { | |
| notification: 'cputhreshold', | |
| disable: 'cpudisablethreshold' | |
| }, | |
| cpuallocated: { | |
| notification: 'cpuallocatedthreshold', | |
| disable: 'cpuallocateddisablethreshold' | |
| }, | |
| memoryused: { | |
| notification: 'memorythreshold', | |
| disable: 'memorydisablethreshold' | |
| }, | |
| memoryallocated: { | |
| notification: 'memoryallocatedthreshold', | |
| disable: 'memoryallocateddisablethreshold' | |
| }, | |
| cpuusedghz: { | |
| notification: 'cputhreshold', | |
| disable: 'cpudisablethreshold' | |
| }, | |
| cpuallocatedghz: { | |
| notification: 'cpuallocatedthreshold', | |
| disable: 'cpuallocateddisablethreshold' | |
| }, | |
| memoryusedgb: { | |
| notification: 'memorythreshold', | |
| disable: 'memorydisablethreshold' | |
| }, | |
| memoryallocatedgb: { | |
| notification: 'memoryallocatedthreshold', | |
| disable: 'memoryallocateddisablethreshold' | |
| }, | |
| disksizeusedgb: { | |
| notification: 'storageusagethreshold', | |
| disable: 'storageusagedisablethreshold' | |
| }, | |
| disksizeallocatedgb: { | |
| notification: 'storageallocatedthreshold', | |
| disable: 'storageallocateddisablethreshold' | |
| } | |
| }, | |
| usageTypeMap: {}, | |
| resourceIdToValidLinksMap: {}, | |
| listDayOfWeek: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'] | |
| } | |
| }, | |
| watch: { | |
| items: { | |
| deep: true, | |
| handler (newData, oldData) { | |
| if (newData === oldData) return | |
| this.items.forEach(record => { | |
| this.resourceIdToValidLinksMap[record.id] = validateLinks(this.$router, false, record) | |
| }) | |
| } | |
| } | |
| }, | |
| created () { | |
| this.getUsageTypes() | |
| }, | |
| computed: { | |
| hasSelected () { | |
| return this.selectedRowKeys.length > 0 | |
| } | |
| }, | |
| methods: { | |
| isTungstenPath () { | |
| return ['/tungstennetworkroutertable', '/tungstenpolicy', '/tungsteninterfaceroutertable', | |
| '/tungstenpolicyset', '/tungstenroutingpolicy', '/firewallrule', '/tungstenfirewallpolicy'].includes(this.$route.path) | |
| }, | |
| createPathBasedOnVmType: createPathBasedOnVmType, | |
| quickViewEnabled (actions, columns, key) { | |
| return actions.length > 0 && | |
| (columns && key === columns[0].dataIndex) && | |
| new RegExp(['/vm', '/kubernetes', '/ssh', '/userdata', '/vmgroup', '/instancebootgroup', '/affinitygroup', '/autoscalevmgroup', | |
| '/volume', '/snapshot', '/vmsnapshot', '/backup', | |
| '/guestnetwork', '/vpc', '/vpncustomergateway', '/vnfapp', | |
| '/template', '/iso', | |
| '/project', '/account', 'buckets', 'objectstore', | |
| '/zone', '/pod', '/cluster', '/host', '/storagepool', '/imagestore', '/systemvm', '/router', '/ilbvm', '/annotation', | |
| '/computeoffering', '/systemoffering', '/diskoffering', '/backupoffering', '/networkoffering', '/vpcoffering', | |
| '/tungstenfabric', '/oauthsetting', '/guestos', '/guestoshypervisormapping', '/webhook', 'webhookdeliveries', 'webhookfilters', '/quotatariff', '/sharedfs', | |
| '/ipv4subnets', '/managementserver', '/gpucard', '/gpudevices', '/vgpuprofile', '/extension', '/snapshotpolicy', '/backupschedule', | |
| '/kmskey', '/hsmprofile', '/dnsserver', '/dnszone'].join('|')) | |
| .test(this.$route.path) | |
| }, | |
| enableGroupAction () { | |
| return ['vm', 'alert', 'vmgroup', 'ssh', 'userdata', 'affinitygroup', 'autoscalevmgroup', 'volume', 'snapshot', | |
| 'vmsnapshot', 'backup', 'guestnetwork', 'vpc', 'publicip', 'vpnuser', 'vpncustomergateway', 'vnfapp', | |
| 'project', 'account', 'systemvm', 'router', 'computeoffering', 'systemoffering', | |
| 'diskoffering', 'backupoffering', 'networkoffering', 'vpcoffering', 'ilbvm', 'kubernetes', 'comment', 'buckets', | |
| 'webhook', 'webhookdeliveries', 'sharedfs', 'ipv4subnets', 'asnumbers', 'guestos', 'gpucard', 'gpudevices', 'vgpuprofile', | |
| 'quotatariff'].includes(this.$route.name) | |
| }, | |
| getDateAtTimeZone (date, timezone) { | |
| return date ? moment(date).tz(timezone).format('YYYY-MM-DD HH:mm:ss') : null | |
| }, | |
| getIntervalTypeText (intervaltype) { | |
| const types = { 0: 'HOURLY', 1: 'DAILY', 2: 'WEEKLY', 3: 'MONTHLY' } | |
| return types[intervaltype] || intervaltype | |
| }, | |
| getTimeZone (timeZone) { | |
| return timeZoneName(timeZone) | |
| }, | |
| fetchColumns () { | |
| if (this.isOrderUpdatable()) { | |
| return this.columns | |
| } | |
| return this.columns.filter(x => x.dataIndex !== 'order') | |
| }, | |
| getRowClassName (record, index) { | |
| if (index % 2 === 0) { | |
| return 'light-row' | |
| } | |
| return 'dark-row' | |
| }, | |
| setSelection (selection) { | |
| this.selectedRowKeys = selection | |
| this.$emit('selection-change', this.selectedRowKeys) | |
| }, | |
| resetSelection () { | |
| this.setSelection([]) | |
| }, | |
| onSelectChange (selectedRowKeys, selectedRows) { | |
| this.setSelection(selectedRowKeys) | |
| }, | |
| changeProject (project) { | |
| this.$store.dispatch('SetProject', project) | |
| this.$store.dispatch('ToggleTheme', project.id === undefined ? 'light' : 'dark') | |
| this.$message.success(this.$t('message.switch.to') + ' ' + project.name) | |
| this.$router.push({ name: 'dashboard' }) | |
| }, | |
| saveValue (record) { | |
| postAPI('updateConfiguration', { | |
| name: record.name, | |
| value: this.editableValue | |
| }).then(json => { | |
| this.editableValueKey = null | |
| this.$store.dispatch('RefreshFeatures') | |
| this.$messageConfigSuccess(`${this.$t('message.setting.updated')} ${record.name}`, record) | |
| this.$notifyConfigurationValueChange(json?.updateconfigurationresponse?.configuration || null) | |
| }).catch(error => { | |
| console.error(error) | |
| this.$message.error(this.$t('message.error.save.setting')) | |
| }).finally(() => { | |
| this.$emit('refresh') | |
| }) | |
| }, | |
| resetConfig (item) { | |
| postAPI('resetConfiguration', { | |
| name: item.name | |
| }).then(() => { | |
| this.$messageConfigSuccess(`${this.$t('label.setting')} ${item.name} ${this.$t('label.reset.config.value')}`, item) | |
| }).catch(error => { | |
| console.error(error) | |
| this.$message.error(this.$t('message.error.reset.config')) | |
| this.$notification.error({ | |
| message: this.$t('label.error'), | |
| description: this.$t('message.error.reset.config') | |
| }) | |
| }).finally(() => { | |
| this.$emit('refresh') | |
| }) | |
| }, | |
| editValue (record) { | |
| this.editableValueKey = record.key | |
| this.editableValue = record.value | |
| }, | |
| getUpdateApi () { | |
| let apiCommand = '' | |
| switch (this.$route.name) { | |
| case 'template': | |
| apiCommand = 'updateTemplate' | |
| break | |
| case 'iso': | |
| apiCommand = 'updateIso' | |
| break | |
| case 'zone': | |
| apiCommand = 'updateZone' | |
| break | |
| case 'computeoffering': | |
| case 'systemoffering': | |
| apiCommand = 'updateServiceOffering' | |
| break | |
| case 'diskoffering': | |
| apiCommand = 'updateDiskOffering' | |
| break | |
| case 'networkoffering': | |
| apiCommand = 'updateNetworkOffering' | |
| break | |
| case 'vpcoffering': | |
| apiCommand = 'updateVPCOffering' | |
| break | |
| case 'guestoscategory': | |
| apiCommand = 'updateOsCategory' | |
| break | |
| } | |
| return apiCommand | |
| }, | |
| isOrderUpdatable () { | |
| return this.getUpdateApi() in this.$store.getters.apis | |
| }, | |
| handleUpdateOrder (id, index) { | |
| this.parentToggleLoading() | |
| const apiCommand = this.getUpdateApi() | |
| return new Promise((resolve, reject) => { | |
| postAPI(apiCommand, { | |
| id, | |
| sortKey: index | |
| }).then((response) => { | |
| resolve(response) | |
| }).catch((reason) => { | |
| reject(reason) | |
| }) | |
| }) | |
| }, | |
| updateOrder (data) { | |
| const promises = [] | |
| const previousSortKeys = this.pageSize && this.currentPage ? this.pageSize * (this.currentPage - 1) : 0 | |
| data.forEach((item, index) => { | |
| promises.push(this.handleUpdateOrder(item.id, previousSortKeys + index + 1)) | |
| }) | |
| Promise.all(promises).catch((reason) => { | |
| console.log(reason) | |
| }).finally(() => { | |
| this.parentToggleLoading() | |
| this.parentFetchData() | |
| }) | |
| }, | |
| moveItemUp (record) { | |
| const data = this.items | |
| const index = data.findIndex(item => item.id === record.id) | |
| if (index === 0) return | |
| data.splice(index - 1, 0, data.splice(index, 1)[0]) | |
| this.updateOrder(data) | |
| }, | |
| moveItemDown (record) { | |
| const data = this.items | |
| const index = data.findIndex(item => item.id === record.id) | |
| if (index === data.length - 1) return | |
| data.splice(index + 1, 0, data.splice(index, 1)[0]) | |
| this.updateOrder(data) | |
| }, | |
| moveItemTop (record) { | |
| const data = this.items | |
| const index = data.findIndex(item => item.id === record.id) | |
| if (index === 0) return | |
| data.unshift(data.splice(index, 1)[0]) | |
| this.updateOrder(data) | |
| }, | |
| moveItemBottom (record) { | |
| const data = this.items | |
| const index = data.findIndex(item => item.id === record.id) | |
| if (index === data.length - 1) return | |
| data.push(data.splice(index, 1)[0]) | |
| this.updateOrder(data) | |
| }, | |
| ipV6Address (text, record) { | |
| if (!record || !record.nic || record.nic.length === 0) { | |
| return '' | |
| } | |
| return record.nic.filter(e => { return e.ip6address }).map(e => { return e.ip6address }).join(', ') || text | |
| }, | |
| generateCommentsPath (record) { | |
| if (this.entityTypeToPath(record.entitytype) === 'ssh') { | |
| return '/' + this.entityTypeToPath(record.entitytype) + '/' + record.entityname | |
| } | |
| return '/' + this.entityTypeToPath(record.entitytype) + '/' + record.entityid | |
| }, | |
| generateHumanReadableEntityType (record) { | |
| switch (record.entitytype) { | |
| case 'VM' : return 'Virtual Machine' | |
| case 'HOST' : return 'Host' | |
| case 'VOLUME' : return 'Volume' | |
| case 'SNAPSHOT' : return 'Snapshot' | |
| case 'VM_SNAPSHOT' : return 'VM Snapshot' | |
| case 'INSTANCE_GROUP' : return 'Instance Group' | |
| case 'NETWORK' : return 'Network' | |
| case 'VPC' : return 'VPC' | |
| case 'PUBLIC_IP_ADDRESS' : return 'Public IP Address' | |
| case 'VPN_CUSTOMER_GATEWAY' : return 'VPC Customer Gateway' | |
| case 'TEMPLATE' : return 'Template' | |
| case 'ISO' : return 'ISO' | |
| case 'SSH_KEYPAIR' : return 'SSH Key Pair' | |
| case 'DOMAIN' : return 'Domain' | |
| case 'SERVICE_OFFERING' : return 'Service Offfering' | |
| case 'DISK_OFFERING' : return 'Disk Offering' | |
| case 'NETWORK_OFFERING' : return 'Network Offering' | |
| case 'POD' : return 'Pod' | |
| case 'ZONE' : return 'Zone' | |
| case 'CLUSTER' : return 'Cluster' | |
| case 'PRIMARY_STORAGE' : return 'Primary Storage' | |
| case 'SECONDARY_STORAGE' : return 'Secondary Storage' | |
| case 'VR' : return 'Virtual Router' | |
| case 'SYSTEM_VM' : return 'System VM' | |
| case 'KUBERNETES_CLUSTER': return 'Kubernetes Cluster' | |
| case 'AUTOSCALE_VM_GROUP': return 'AutoScale VM group' | |
| default: return record.entitytype.toLowerCase().replace('_', '') | |
| } | |
| }, | |
| generateHumanReadableSchedule (schedule) { | |
| return cronstrue.toString(schedule, { locale: this.$i18n.locale }) | |
| }, | |
| entityTypeToPath (entitytype) { | |
| switch (entitytype) { | |
| case 'VM' : return 'vm' | |
| case 'HOST' : return 'host' | |
| case 'VOLUME' : return 'volume' | |
| case 'SNAPSHOT' : return 'snapshot' | |
| case 'VM_SNAPSHOT' : return 'vmsnapshot' | |
| case 'INSTANCE_GROUP' : return 'vmgroup' | |
| case 'NETWORK' : return 'guestnetwork' | |
| case 'VPC' : return 'vpc' | |
| case 'PUBLIC_IP_ADDRESS' : return 'publicip' | |
| case 'VPN_CUSTOMER_GATEWAY' : return 'vpncustomergateway' | |
| case 'TEMPLATE' : return 'template' | |
| case 'ISO' : return 'iso' | |
| case 'SSH_KEYPAIR' : return 'ssh' | |
| case 'DOMAIN' : return 'domain' | |
| case 'SERVICE_OFFERING' : return 'computeoffering' | |
| case 'DISK_OFFERING' : return 'diskoffering' | |
| case 'NETWORK_OFFERING' : return 'networkoffering' | |
| case 'POD' : return 'pod' | |
| case 'ZONE' : return 'zone' | |
| case 'CLUSTER' : return 'cluster' | |
| case 'PRIMARY_STORAGE' : return 'storagepool' | |
| case 'SECONDARY_STORAGE' : return 'imagestore' | |
| case 'VR' : return 'router' | |
| case 'SYSTEM_VM' : return 'systemvm' | |
| case 'KUBERNETES_CLUSTER': return 'kubernetes' | |
| case 'AUTOSCALE_VM_GROUP': return 'autoscalevmgroup' | |
| default: return entitytype.toLowerCase().replace('_', '') | |
| } | |
| }, | |
| updateAdminsOnly (e) { | |
| postAPI('updateAnnotationVisibility', { | |
| id: e.target.value, | |
| adminsonly: e.target.checked | |
| }).finally(() => { | |
| const data = this.items | |
| const index = data.findIndex(item => item.id === e.target.value) | |
| const elem = data[index] | |
| elem.adminsonly = e.target.checked | |
| }) | |
| }, | |
| getHostState (host) { | |
| if (host && host.hypervisor === 'KVM' && host.state === 'Up' && host.details && host.details.secured !== 'true') { | |
| return 'Unsecure' | |
| } | |
| return host.state | |
| }, | |
| getColumnKey (name) { | |
| if (typeof name !== 'object' || name === null) { | |
| return name | |
| } | |
| return name.field ?? name.customTitle ?? Object.keys(name)[0] | |
| }, | |
| getColumnTitle (name) { | |
| if (typeof name !== 'object' || name === null) { | |
| return name | |
| } | |
| return name.customTitle ?? name.field ?? Object.keys(name)[0] | |
| }, | |
| handleResizeColumn (w, col) { | |
| col.width = w | |
| }, | |
| updateSelectedColumns (name) { | |
| this.$emit('update-selected-columns', this.getColumnKey(name)) | |
| }, | |
| getVmRouteUsingType (record) { | |
| switch (record.virtualmachinetype) { | |
| case 'DomainRouter' : return '/router/' | |
| case 'ConsoleProxy' : | |
| case 'SecondaryStorageVm': return '/systemvm/' | |
| default: return '/vm/' | |
| } | |
| }, | |
| getTrimmedText (text, length) { | |
| if (!text) { | |
| return '' | |
| } | |
| return (text.length <= length) ? text : (text.substring(0, length - 3) + '...') | |
| }, | |
| getDuration (startdate, enddate) { | |
| if (!startdate || !enddate) { | |
| return '' | |
| } | |
| var duration = Date.parse(enddate) - Date.parse(startdate) | |
| return (duration > 0 ? duration / 1000.0 : 0) + '' | |
| }, | |
| getUsageTypes () { | |
| if (this.$route.path.split('/')[1] === 'usage') { | |
| getAPI('listUsageTypes').then(json => { | |
| if (json && json.listusagetypesresponse && json.listusagetypesresponse.usagetype) { | |
| this.usageTypes = json.listusagetypesresponse.usagetype.map(x => { | |
| return { | |
| id: x.id, | |
| value: x.description | |
| } | |
| }) | |
| this.usageTypeMap = {} | |
| for (var usageType of this.usageTypes) { | |
| this.usageTypeMap[usageType.id] = usageType.value | |
| } | |
| } | |
| }) | |
| } | |
| }, | |
| getRemainingLeaseText (leaseDuration) { | |
| if (leaseDuration > 0) { | |
| return leaseDuration + (leaseDuration === 1 ? ' day' : ' days') | |
| } else if (leaseDuration === 0) { | |
| return 'expiring today' | |
| } else { | |
| return 'over' | |
| } | |
| }, | |
| getLeaseColor (leaseDuration) { | |
| if (leaseDuration >= 7) { | |
| return '#888' | |
| } else if (leaseDuration >= 0) { | |
| return '#ffbf00' | |
| } else { | |
| return '#fd7e14' | |
| } | |
| } | |
| } | |
| } | |
| </script> | |
| <style> | |
| :deep(.ant-table-thead) { | |
| background-color: #f9f9f9; | |
| } | |
| :deep(.ant-table-small) > .ant-table-content > .ant-table-body { | |
| margin: 0; | |
| } | |
| :deep(.ant-table-tbody)>tr>td, :deep(.ant-table-thead)>tr>th { | |
| overflow-wrap: anywhere; | |
| } | |
| .filter-dropdown .ant-menu-vertical { | |
| border: none; | |
| } | |
| .filter-dropdown .ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected { | |
| background-color: transparent; | |
| } | |
| </style> | |
| <style scoped lang="scss"> | |
| .shift-btns { | |
| display: flex; | |
| } | |
| .shift-btn { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| width: 20px; | |
| height: 20px; | |
| font-size: 12px; | |
| &:not(:last-child) { | |
| margin-right: 5px; | |
| } | |
| &--rotated { | |
| font-size: 10px; | |
| transform: rotate(90deg); | |
| } | |
| } | |
| .alert-notification-threshold { | |
| background-color: rgba(255, 231, 175, 0.75); | |
| color: #e87900; | |
| padding: 10%; | |
| } | |
| .alert-disable-threshold { | |
| background-color: rgba(255, 190, 190, 0.75); | |
| color: #f50000; | |
| padding: 10%; | |
| } | |
| .custom-icon:before { | |
| font-size: 8px; | |
| position: absolute; | |
| top: 8px; | |
| left: 3.5px; | |
| color: #000; | |
| font-weight: 700; | |
| line-height: 1.7; | |
| } | |
| .icon-daily:before { | |
| content: "01"; | |
| left: 5px; | |
| top: 7px; | |
| line-height: 1.9; | |
| } | |
| .icon-weekly:before { | |
| content: "1-7"; | |
| left: 3px; | |
| line-height: 1.7; | |
| } | |
| .icon-monthly:before { | |
| content: "***"; | |
| } | |
| </style> |