You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by da...@apache.org on 2023/11/03 10:55:42 UTC

(cloudstack) 01/01: Merge remote-tracking branch 'apache/4.18' into main

This is an automated email from the ASF dual-hosted git repository.

dahn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit a15cb81c85d15f91c6e1dee569dc7a55066ceb7d
Merge: 78213da8286 e6f048bc2e8
Author: Daan Hoogland <da...@onecht.net>
AuthorDate: Fri Nov 3 11:55:26 2023 +0100

    Merge remote-tracking branch 'apache/4.18' into main

 agent/src/main/java/com/cloud/agent/Agent.java     |   4 +
 .../main/java/com/cloud/agent/api/PingAnswer.java  |  13 +-
 .../com/cloud/agent/manager/AgentManagerImpl.java  | 148 +++++++++++++--------
 .../kvm/resource/LibvirtComputingResource.java     |   4 +-
 .../kvm/resource/LibvirtDomainXMLParser.java       |  25 +++-
 test/integration/smoke/test_host_ping.py           | 102 ++++++++++++++
 ui/src/components/view/ListView.vue                |  30 +++--
 7 files changed, 250 insertions(+), 76 deletions(-)

diff --cc agent/src/main/java/com/cloud/agent/Agent.java
index b1ec592b9fe,e8558931cbf..9e0ee746c03
--- a/agent/src/main/java/com/cloud/agent/Agent.java
+++ b/agent/src/main/java/com/cloud/agent/Agent.java
@@@ -40,8 -40,7 +40,9 @@@ import java.util.concurrent.atomic.Atom
  
  import javax.naming.ConfigurationException;
  
 +import com.cloud.resource.AgentStatusUpdater;
 +import com.cloud.resource.ResourceStatusUpdater;
+ import com.cloud.agent.api.PingAnswer;
  import com.cloud.utils.NumbersUtil;
  import org.apache.cloudstack.agent.lb.SetupMSListAnswer;
  import org.apache.cloudstack.agent.lb.SetupMSListCommand;
diff --cc ui/src/components/view/ListView.vue
index cafed924e4c,286d9d16d2a..6d0f4121425
--- a/ui/src/components/view/ListView.vue
+++ b/ui/src/components/view/ListView.vue
@@@ -113,95 -82,138 +113,99 @@@
            style="margin-left: 5px"
            :actions="actions"
            :resource="record"
 -          :enabled="quickViewEnabled() && actions.length > 0 && columns && columns[0].dataIndex === 'name' "
 +          :enabled="quickViewEnabled() && actions.length > 0 && columns && columns[0].dataIndex === 'displayname' "
            @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'].includes($route.path.split('/')[1])" style="margin-right: 5px">
 +        <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"/>
 -          <os-logo v-else-if="record.ostypename" :osName="record.ostypename" size="2x" />
 -          <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" />
 +          <user-outlined v-else style="font-size: 16px;" />
          </span>
 -        <span v-else :style="{ 'margin-right': record.ostypename ? '5px' : '0' }">
 -          <os-logo v-if="record.ostypename" :osName="record.ostypename" size="1x" />
 +        <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.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 v-if="record.issourcenat">
 +          &nbsp;
 +          <a-tag>source-nat</a-tag>
          </span>
 -        <span v-else-if="$route.path.startsWith('/globalsetting')">{{ text }}</span>
 -        <span v-else-if="$route.path.startsWith('/preferences')">{{ 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 v-if="record.isstaticnat">
 +          &nbsp;
 +          <a-tag>static-nat</a-tag>
          </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>
 +      </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 :to="{ path: getVmRouteUsingType(record) + record.virtualmachineid }">{{ text }}</router-link>
 +      </template>
 +      <template v-if="column.key === 'volumename'">
 +        <router-link :to="{ path: '/volume/' + record.volumeid }">{{ text }}</router-link>
 +      </template>
 +      <template v-if="column.key === 'size'">
-         <span v-if="text">
++        <span v-if="text && $route.path === '/kubernetes'">
++          {{ text }}
+         </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 v-else-if="text">
 +          {{ parseFloat(parseFloat(text) / 1024.0 / 1024.0 / 1024.0).toFixed(2) }} GiB
          </span>
 -        <span v-else>
 -          <router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ text }}</router-link>
 -          <router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ text }}</router-link>
 +      </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>
 -      </span>
 -    </template>
 -    <template #templatetype="{ text, record }">
 -      <router-link :to="{ path: $route.path + '/' + record.templatetype }">{{ text }}</router-link>
 -    </template>
 -    <template #type="{ text }">
 -      <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 #displayname="{text, record}">
 -      <QuickView
 -        style="margin-left: 5px"
 -        :actions="actions"
 -        :resource="record"
 -        :enabled="quickViewEnabled() && actions.length > 0 && columns && columns[0].dataIndex === 'displayname' "
 -        @exec-action="$parent.execAction"/>
 -      <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
 -    </template>
 -    <template #username="{text, record}">
 -      <span v-if="$showIcon() && !['vm'].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>
 +      </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.length > 0 && columns && columns[0].dataIndex === 'hypervisor' "
-           @exec-action="$parent.execAction"/>
-         <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
-       </span>
++          <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.length > 0 && columns && columns[0].dataIndex === 'hypervisor' "
++            @exec-action="$parent.execAction"/>
++          <router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
++        </span>
++      </template>
        <span v-else>{{ text }}</span>
      </template>
 -    <template #entityid="{ record }" href="javascript:;">
 -      <router-link :to="{ path: generateCommentsPath(record), query: { tab: 'comments' } }">{{ record.entityname }}</router-link>
 -    </template>
 -    <template #entitytype="{ record }" href="javascript:;">
 -      {{ generateHumanReadableEntityType(record) }}
 -    </template>
 -    <template #adminsonly="{ record }" v-if="['Admin'].includes($store.getters.userInfo.roletype)" href="javascript:;">
 -      <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 #ipaddress="{ text, record }" 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">
 -        &nbsp;
 -        <a-tag>source-nat</a-tag>
 -      </span>
 -      <span v-if="record.isstaticnat">
 -        &nbsp;
 -        <a-tag>static-nat</a-tag>
 -      </span>
 -    </template>
 -    <template #ip6address="{ text, record }" href="javascript:;">
 -      <span>{{ ipV6Address(text, record) }}</span>
 -    </template>
 -    <template #publicip="{ text, record }">
 -      <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 #traffictype="{ text }" href="javascript:;">
 -      {{ text }}
 -    </template>
 -    <template #vmname="{ text, record }">
 -      <router-link :to="{ path: createPathBasedOnVmType(record.vmtype, record.virtualmachineid) }">{{ text }}</router-link>
 -    </template>
 -    <template #virtualmachinename="{ text, record }">
 -      <router-link :to="{ path: '/vm/' + record.virtualmachineid }">{{ text }}</router-link>
 -    </template>
 -    <template #volumename="{ text, record }">
 -      <router-link :to="{ path: '/volume/' + record.volumeid }">{{ text }}</router-link>
 -    </template>
 -    <template #size="{ text }">
 -      <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 #physicalsize="{ text }">
 -      <span v-if="text">
 -        {{ isNaN(text) ? text : (parseFloat(parseFloat(text) / 1024.0 / 1024.0 / 1024.0).toFixed(2) + ' GiB') }}
 -      </span>
 -    </template>
 -    <template #physicalnetworkname="{ text, record }">
 -      <router-link :to="{ path: '/physicalnetwork/' + record.physicalnetworkid }">{{ text }}</router-link>
 -    </template>
 -    <template #serviceofferingname="{ text, record }">
 -      <router-link :to="{ path: '/computeoffering/' + record.serviceofferingid }">{{ text }}</router-link>
 -    </template>
 -    <template #hypervisor="{ text, record }">
 -      <span v-if="$route.name === 'hypervisorcapability'">
 +    <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>
@@@ -254,214 -263,193 +258,212 @@@
        <router-link v-if="record.storageid" :to="{ path: '/storagepool/' + record.storageid }">{{ text }}</router-link>
        <span v-else>{{ text }}</span>
      </template>
 -
 -    <template
 -      v-for="(value, name) in thresholdMapping"
 -      :key="name"
 -      #[name]="{ text, record }"
 -      href="javascript:;">
 -      <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 }}
 +    <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>
 -      </span>
 -    </template>
 -
 -    <template #level="{ text, record }">
 -      <router-link :to="{ path: '/event/' + record.id }">{{ text }}</router-link>
 -    </template>
 +      </template>
-     </template>
- 
 +      <template v-if="column.key === 'level'">
 +        <router-link :to="{ path: '/event/' + record.id }">{{ text }}</router-link>
 +      </template>
  
 -    <template #clustername="{ text, record }">
 -      <router-link :to="{ path: '/cluster/' + record.clusterid }">{{ text }}</router-link>
 -    </template>
 -    <template #podname="{ text, record }">
 -      <router-link :to="{ path: '/pod/' + record.podid }">{{ text }}</router-link>
 -    </template>
 -    <template #account="{ text, record }">
 -      <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>
 +      <template v-if="column.key === 'clustername'">
 +        <router-link :to="{ path: '/cluster/' + record.clusterid }">{{ text }}</router-link>
 +      </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>
 -            <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="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>
 +      <template v-if="column.key === 'resource'">
 +        <resource-label :resourceType="record.resourcetype" :resourceId="record.resourceid" :resourceName="record.resourcename" />
 +      </template>
 +      <template v-if="column.key === 'domain'">
 +        <router-link v-if="record.domainid && !record.domainid.toString().includes(',') && $store.getters.userInfo.roletype !== 'User'" :to="{ path: '/domain/' + record.domainid, query: { tab: 'details' } }">{{ text }}</router-link>
          <span v-else>{{ text }}</span>
        </template>
 -    </template>
 -    <template #resource="{ record }">
 -      <resource-label :resourceType="record.resourcetype" :resourceId="record.resourceid" :resourceName="record.resourcename" />
 -    </template>
 -    <template #domain="{ text, record }">
 -      <router-link v-if="record.domainid && !record.domainid.toString().includes(',') && $store.getters.userInfo.roletype !== 'User'" :to="{ path: '/domain/' + record.domainid, query: { tab: 'details' } }">{{ text }}</router-link>
 -      <span v-else>{{ text }}</span>
 -    </template>
 -    <template #domainpath="{ text, record }">
 -      <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 #zone="{ text, record }">
 -      <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 }}</router-link>
 -      <span v-else>{{ text }}</span>
 -    </template>
 -    <template #zonename="{ text, record }">
 -      <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 #rolename="{ text, record }">
 -      <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 #templateversion="{ record }">
 -      <span>  {{ record.version }} </span>
 -    </template>
 -    <template #softwareversion="{ record }">
 -      <span>  {{ record.softwareversion ? record.softwareversion : 'N/A' }} </span>
 -    </template>
 -    <template #access="{ record }">
 -      <status :text="record.readonly ? 'ReadOnly' : 'ReadWrite'" displayText />
 -    </template>
 -    <template #requiresupgrade="{ record }">
 -      <status :text="record.requiresupgrade ? 'warning' : ''" />
 -      {{ record.requiresupgrade ? 'Yes' : 'No' }}
 -    </template>
 -    <template #loadbalancerrule="{ record }">
 -      <span>  {{ record.loadbalancerrule }} </span>
 -    </template>
 -    <template #autoscalingenabled="{ record }">
 -      <status :text="record.autoscalingenabled ? 'Enabled' : 'Disabled'" />
 -      {{ record.autoscalingenabled ? 'Enabled' : 'Disabled' }}
 -    </template>
 -    <template #current="{record}">
 -      <status :text="record.current ? record.current.toString() : 'false'" />
 -    </template>
 -    <template #created="{ text }">
 -      {{ $toLocaleDate(text) }}
 -    </template>
 -    <template #sent="{ text }">
 -      {{ $toLocaleDate(text) }}
 -    </template>
 -    <template #order="{ text, record }">
 -      <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 === '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 }}</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 === 'templateversion'">
 +        <span>  {{ record.version }} </span>
 +      </template>
 +      <template v-if="column.key === 'softwareversion'">
 +        <span>  {{ record.softwareversion ? record.softwareversion : 'N/A' }} </span>
 +      </template>
 +      <template v-if="column.key === 'access'">
 +        <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'].includes(column.key)">
 +        {{ $toLocaleDate(text) }}
 +      </template>
 +      <template v-if="['startdate', 'enddate'].includes(column.key) && ['vm', 'vnfapp'].includes($route.path.split('/')[1])">
 +        {{ getDateAtTimeZone(text, record.timezone) }}
 +      </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 #value="{ text, record }">
 -      <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>
 -      <div v-else style="width: 200px; word-break: break-all">
 -        {{ text }}
 -      </div>
 -    </template>
 -    <template #actions="{ record }">
 -      <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="resetConfig(record)"
 -        v-if="editableValueKey !== record.key"
 -        icon="reload-outlined"
 -        :disabled="!('updateConfiguration' in $store.getters.apis)" />
 +      <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>
 +        <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="resetConfig(record)"
 +          v-if="editableValueKey !== record.key"
 +          icon="reload-outlined"
 +          :disabled="!('updateConfiguration' in $store.getters.apis)" />
 +      </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 === 'vmScheduleActions'">
 +        <tooltip-button
 +          :tooltip="$t('label.edit')"
 +          :disabled="!('updateVMSchedule' in $store.getters.apis)"
 +          icon="edit-outlined"
 +          @onClick="updateVMSchedule(record)" />
 +        <tooltip-button
 +          :tooltip="$t('label.remove')"
 +          :disabled="!('deleteVMSchedule' in $store.getters.apis)"
 +          icon="delete-outlined"
 +          :danger="true"
 +          type="primary"
 +          @onClick="removeVMSchedule(record)" />
 +      </template>
      </template>
 -    <template #tariffActions="{ record }">
 -      <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 #footer>
 +      <span v-if="hasSelected">
 +        {{ `Selected ${selectedRowKeys.length} items` }}
 +      </span>
      </template>
    </a-table>
  </template>