mirror of https://github.com/apache/cloudstack.git
1659 lines
63 KiB
Vue
1659 lines
63 KiB
Vue
// 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'">
|
|
<span>{{ text }}</span>
|
|
</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 :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 === '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 && !text.startsWith('PrjAcct-')">
|
|
<router-link
|
|
v-if="'quota' in record && $router.resolve(`${$route.path}/${record.account}`).matched[0].redirect !== '/exception/404'"
|
|
:to="{ path: `${$route.path}/${record.account}`, query: { account: record.account, domainid: record.domainid, quota: true } }"
|
|
>{{ text }}</router-link>
|
|
<router-link
|
|
:to="{ path: '/account/' + record.accountid }"
|
|
v-else-if="record.accountid"
|
|
>{{ text }}</router-link>
|
|
<router-link
|
|
:to="{ path: '/account', query: { name: record.account, domainid: record.domainid, dataView: true } }"
|
|
v-else-if="$store.getters.userInfo.roletype !== 'User'"
|
|
>{{ text }}</router-link>
|
|
<span v-else>{{ text }}</span>
|
|
</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="['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 === 'tariffActions'">
|
|
<tooltip-button
|
|
:tooltip="$t('label.edit')"
|
|
v-if="editableValueKey !== record.key"
|
|
:disabled="!('quotaTariffUpdate' in $store.getters.apis)"
|
|
icon="edit-outlined"
|
|
@onClick="editTariffValue(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', '/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'
|
|
].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)
|
|
},
|
|
editTariffValue (record) {
|
|
this.$emit('edit-tariff-action', true, record)
|
|
},
|
|
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>
|