You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2020/03/21 20:10:48 UTC
[cloudstack-primate] branch master updated: primate: use a-table
with pagination instead of a-list (#151)
This is an automated email from the ASF dual-hosted git repository.
rohit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack-primate.git
The following commit(s) were added to refs/heads/master by this push:
new df189c8 primate: use a-table with pagination instead of a-list (#151)
df189c8 is described below
commit df189c8f75907282b0f662767e533629efb10073
Author: Ritchie Vincent <rf...@gmail.com>
AuthorDate: Sat Mar 21 20:10:40 2020 +0000
primate: use a-table with pagination instead of a-list (#151)
This converts many components to use a-table with a-pagination than a-list:
* Convert MigrateWizard
* Convert Firewalls
* Convert Port Forwarding
* Convert Load Balancing
* Convert Egress Configure
* Convert IngressEgress Configure
* Convert Dedicate VLAN
* Convert VPC Tiers tab
Signed-off-by: Rohit Yadav <ro...@shapeblue.com>
Co-authored-by: Ritchie Vincent <ri...@ritchievincent.co.uk>
Co-authored-by: Rohit Yadav <ro...@shapeblue.com>
---
src/config/section/network.js | 2 +-
src/locales/en.json | 1 +
src/views/compute/MigrateWizard.vue | 193 +++++++++++--------
src/views/infra/network/DedicatedVLANTab.vue | 123 +++++++-----
.../{EgressConfigure.vue => EgressRulesTab.vue} | 129 +++++++++----
src/views/network/FirewallRules.vue | 121 ++++++++----
src/views/network/IngressEgressRuleConfigure.vue | 104 +++++-----
src/views/network/LoadBalancing.vue | 210 +++++++++++++--------
src/views/network/PortForwarding.vue | 123 ++++++++----
9 files changed, 649 insertions(+), 357 deletions(-)
diff --git a/src/config/section/network.js b/src/config/section/network.js
index f82e610..70bfa92 100644
--- a/src/config/section/network.js
+++ b/src/config/section/network.js
@@ -40,7 +40,7 @@ export default {
component: () => import('@/components/view/DetailsTab.vue')
}, {
name: 'Egress Rules',
- component: () => import('@/views/network/EgressConfigure.vue'),
+ component: () => import('@/views/network/EgressRulesTab.vue'),
show: (record) => { return record.type === 'Isolated' && 'listEgressFirewallRules' in store.getters.apis }
}, {
name: 'Public IP Addresses',
diff --git a/src/locales/en.json b/src/locales/en.json
index 25a0f50..8900a05 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -917,6 +917,7 @@
"snmpPort": "SNMP Port",
"sockettimeout": "Socket Timeout",
"sourcecidr": "Source CIDR",
+"destcidr": "Destination CIDR",
"sourceNat": "Source NAT",
"sourceipaddress": "Source IP Address",
"sourceport": "Source Port",
diff --git a/src/views/compute/MigrateWizard.vue b/src/views/compute/MigrateWizard.vue
index 27f0c13..b99125c 100644
--- a/src/views/compute/MigrateWizard.vue
+++ b/src/views/compute/MigrateWizard.vue
@@ -16,60 +16,64 @@
// under the License.
<template>
- <a-list :dataSource="hosts" itemLayout="vertical" class="list" :loading="loading">
- <div slot="header" class="list__header">
- <a-input-search
- placeholder="Search"
- v-model="searchQuery"
- @search="fetchData" />
- </div>
- <a-list-item
- slot="renderItem"
- slot-scope="host, index"
- class="host-item"
- :class="{ 'host-item--selected' : selectedIndex === index }"
- >
- <div class="host-item__row">
- <div class="host-item__value">
- <span class="host-item__title">{{ $t('name') }}</span>
- {{ host.name }}
- </div>
- <div class="host-item__value host-item__value--small">
- <span class="host-item__title">Suitability</span>
- <a-icon
- class="host-item__suitability-icon"
- type="check-circle"
- theme="twoTone"
- twoToneColor="#52c41a"
- v-if="host.suitableformigration" />
- <a-icon
- class="host-item__suitability-icon"
- type="close-circle"
- theme="twoTone"
- twoToneColor="#f5222d"
- v-else />
- </div>
- <div class="host-item__value host-item__value--full">
- <span class="host-item__title">{{ $t('cpuused') }}</span>
- {{ host.cpuused }}
- </div>
- <div class="host-item__value">
- <span class="host-item__title">{{ $t('memused') }}</span>
- {{ host.memoryused | byteToGigabyte }} GB
- </div>
+ <div class="form">
+ <a-input-search
+ placeholder="Search"
+ v-model="searchQuery"
+ style="margin-bottom: 10px;"
+ @search="fetchData" />
+ <a-table
+ size="small"
+ style="overflow-y: auto"
+ :loading="loading"
+ :columns="columns"
+ :dataSource="hosts"
+ :pagination="false"
+ :rowKey="record => record.id">
+ <div slot="suitability" slot-scope="record">
+ <a-icon
+ class="host-item__suitability-icon"
+ type="check-circle"
+ theme="twoTone"
+ twoToneColor="#52c41a"
+ v-if="record.suitableformigration" />
+ <a-icon
+ class="host-item__suitability-icon"
+ type="close-circle"
+ theme="twoTone"
+ twoToneColor="#f5222d"
+ v-else />
+ </div>
+ <div slot="memused" slot-scope="record">
+ {{ record.memoryused | byteToGigabyte }} GB
+ </div>
+ <template slot="select" slot-scope="record">
<a-radio
class="host-item__radio"
- @click="selectedIndex = index"
- :checked="selectedIndex === index"
- :disabled="!host.suitableformigration"></a-radio>
- </div>
- </a-list-item>
- <div slot="footer" class="list__footer">
- <a-button type="primary" :disabled="selectedIndex === null" @click="submitForm">
- {{ $t('OK') }}
+ @click="selectedHost = record"
+ :checked="record.id === selectedHost.id"
+ :disabled="!record.suitableformigration"></a-radio>
+ </template>
+ </a-table>
+ <a-pagination
+ class="pagination"
+ size="small"
+ :current="page"
+ :pageSize="pageSize"
+ :total="totalCount"
+ :showTotal="total => `Total ${total} items`"
+ :pageSizeOptions="['10', '20', '40', '80', '100']"
+ @change="handleChangePage"
+ @showSizeChange="handleChangePageSize"
+ showSizeChanger/>
+
+ <div style="margin-top: 20px; display: flex; justify-content:flex-end;">
+ <a-button type="primary" :disabled="!selectedHost.id" @click="submitForm">
+ {{ $t('ok') }}
</a-button>
</div>
- </a-list>
+ </div>
+
</template>
<script>
@@ -87,8 +91,33 @@ export default {
return {
loading: true,
hosts: [],
- selectedIndex: null,
- searchQuery: ''
+ selectedHost: {},
+ searchQuery: '',
+ totalCount: 0,
+ page: 1,
+ pageSize: 10,
+ columns: [
+ {
+ title: this.$t('name'),
+ dataIndex: 'name'
+ },
+ {
+ title: this.$t('Suitability'),
+ scopedSlots: { customRender: 'suitability' }
+ },
+ {
+ title: this.$t('cpuused'),
+ dataIndex: 'cpuused'
+ },
+ {
+ title: this.$t('memused'),
+ scopedSlots: { customRender: 'memused' }
+ },
+ {
+ title: this.$t('select'),
+ scopedSlots: { customRender: 'select' }
+ }
+ ]
}
},
mounted () {
@@ -100,20 +129,24 @@ export default {
api('findHostsForMigration', {
virtualmachineid: this.resource.id,
keyword: this.searchQuery,
- page: 1,
- pagesize: 500
+ page: this.page,
+ pagesize: this.pageSize
}).then(response => {
this.hosts = response.findhostsformigrationresponse.host
- this.loading = false
+ this.totalCount = response.findhostsformigrationresponse.count
+ if (this.totalCount > 0) {
+ this.totalCount -= 1
+ }
}).catch(error => {
this.$message.error('Failed to load hosts: ' + error)
+ }).finally(() => {
+ this.loading = false
})
},
submitForm () {
this.loading = true
- const host = this.hosts[this.selectedIndex]
- api(host.requiresStorageMotion ? 'migrateVirtualMachineWithVolume' : 'migrateVirtualMachine', {
- hostid: host.id,
+ api(this.selectedHost.requiresStorageMotion ? 'migrateVirtualMachineWithVolume' : 'migrateVirtualMachine', {
+ hostid: this.selectedHost.id,
virtualmachineid: this.resource.id
}).then(response => {
this.$store.dispatch('AddAsyncJob', {
@@ -141,15 +174,23 @@ export default {
this.$parent.$parent.close()
}).catch(error => {
console.error(error)
- this.$message.error('Failed to migrate host.')
+ this.$message.error(`Failed to migrate VM to host ${this.selectedHost.name}`)
})
+ },
+ handleChangePage (page, pageSize) {
+ this.page = page
+ this.pageSize = pageSize
+ this.fetchData()
+ },
+ handleChangePageSize (currentPage, pageSize) {
+ this.page = currentPage
+ this.pageSize = pageSize
+ this.fetchData()
}
},
filters: {
byteToGigabyte: value => {
- if (!value) return ''
- value = value / Math.pow(10, 9)
- return value.toFixed(2)
+ return (value / Math.pow(10, 9)).toFixed(2)
}
}
}
@@ -157,26 +198,10 @@ export default {
<style scoped lang="scss">
- .list {
- max-height: 95vh;
- width: 95vw;
- overflow-y: scroll;
- margin: -24px;
-
- @media (min-width: 1000px) {
- max-height: 70vh;
- width: 60vw;
- }
-
- &__header,
- &__footer {
- padding-left: 20px;
- padding-right: 20px;
- }
-
- &__footer {
- display: flex;
- justify-content: flex-end;
+ .form {
+ width: 85vw;
+ @media (min-width: 800px) {
+ width: 750px;
}
}
@@ -230,4 +255,8 @@ export default {
}
}
+
+ .pagination {
+ margin-top: 20px;
+ }
</style>
diff --git a/src/views/infra/network/DedicatedVLANTab.vue b/src/views/infra/network/DedicatedVLANTab.vue
index eaa0dc3..48d041d 100644
--- a/src/views/infra/network/DedicatedVLANTab.vue
+++ b/src/views/infra/network/DedicatedVLANTab.vue
@@ -18,41 +18,37 @@
<template>
<a-spin :spinning="fetchLoading">
<a-button type="dashed" icon="plus" style="width: 100%" @click="handleOpenModal">{{ $t('label.dedicate.vlan.vni.range') }}</a-button>
- <a-list class="list">
- <a-list-item v-for="item in items" :key="item.id" class="list__item">
- <div class="list__item-outer-container">
- <div class="list__item-container">
- <div class="list__col">
- <div class="list__label">{{ $t('vlanrange') }}</div>
- <div>{{ item.guestvlanrange }}</div>
- </div>
- <div class="list__col">
- <div class="list__label">{{ $t('domain') }}</div>
- <div>{{ item.domain }}</div>
- </div>
- <div class="list__col">
- <div class="list__label">{{ $t('account') }}</div>
- <div>{{ item.account }}</div>
- </div>
- </div>
- <div class="list__col">
- <div class="list__label">{{ $t('id') }}</div>
- <div>{{ item.id }}</div>
- </div>
- </div>
- <div slot="actions">
- <a-popconfirm
- :title="`${$t('label.delete')}?`"
- @confirm="handleDelete(item)"
- okText="Yes"
- cancelText="No"
- placement="top"
- >
- <a-button icon="delete" type="danger" shape="round"></a-button>
- </a-popconfirm>
- </div>
- </a-list-item>
- </a-list>
+ <a-table
+ size="small"
+ style="overflow-y: auto; margin-top: 20px;"
+ :loading="fetchLoading"
+ :columns="columns"
+ :dataSource="items"
+ :pagination="false"
+ :rowKey="record => record.id">
+ <template slot="actions" slot-scope="record">
+ <a-popconfirm
+ :title="`${$t('label.delete')}?`"
+ @confirm="handleDelete(record)"
+ okText="Yes"
+ cancelText="No"
+ placement="top"
+ >
+ <a-button icon="delete" type="danger" shape="round"></a-button>
+ </a-popconfirm>
+ </template>
+ </a-table>
+ <a-pagination
+ class="pagination"
+ size="small"
+ :current="page"
+ :pageSize="pageSize"
+ :total="totalCount"
+ :showTotal="total => `Total ${total} items`"
+ :pageSizeOptions="['10', '20', '40', '80', '100']"
+ @change="handleChangePage"
+ @showSizeChange="handleChangePageSize"
+ showSizeChanger/>
<a-modal v-model="modal" :title="$t('label.dedicate.vlan.vni.range')" @ok="handleSubmit">
<a-spin :spinning="formLoading">
@@ -147,7 +143,28 @@ export default {
accounts: [],
projects: [],
modal: false,
- selectedScope: 'account'
+ selectedScope: 'account',
+ totalCount: 0,
+ page: 1,
+ pageSize: 10,
+ columns: [
+ {
+ title: this.$t('vlanrange'),
+ dataIndex: 'guestvlanrange'
+ },
+ {
+ title: this.$t('domain'),
+ dataIndex: 'domain'
+ },
+ {
+ title: this.$t('account'),
+ dataIndex: 'account'
+ },
+ {
+ title: this.$t('action'),
+ scopedSlots: { customRender: 'actions' }
+ }
+ ]
}
},
beforeCreate () {
@@ -166,20 +183,23 @@ export default {
methods: {
fetchData () {
this.form.resetFields()
+ this.formLoading = true
api('listDedicatedGuestVlanRanges', {
- physicalnetworkid: this.resource.id
+ physicalnetworkid: this.resource.id,
+ page: this.page,
+ pageSize: this.pageSize
}).then(response => {
- this.items = response.listdedicatedguestvlanrangesresponse.dedicatedguestvlanrange
- ? response.listdedicatedguestvlanrangesresponse.dedicatedguestvlanrange : []
- this.parentFinishLoading()
- this.formLoading = false
+ this.items = response.listdedicatedguestvlanrangesresponse.dedicatedguestvlanrange || []
+ this.totalCount = response.listdedicatedguestvlanrangesresponse.count || 0
}).catch(error => {
this.$notification.error({
message: `Error ${error.response.status}`,
- description: error.response.data.errorresponse.errortext
+ description: error.response.data.errorresponse.errortext,
+ duration: 0
})
- this.parentFinishLoading()
+ }).finally(() => {
this.formLoading = false
+ this.parentFinishLoading()
})
},
fetchDomains () {
@@ -187,8 +207,7 @@ export default {
details: 'min',
listAll: true
}).then(response => {
- this.domains = response.listdomainsresponse.domain
- ? response.listdomainsresponse.domain : []
+ this.domains = response.listdomainsresponse.domain || []
if (this.domains.length > 0) {
this.form.setFieldsValue({
domain: this.domains[0].id
@@ -340,6 +359,16 @@ export default {
this.modal = true
this.formLoading = true
this.fetchDomains()
+ },
+ handleChangePage (page, pageSize) {
+ this.page = page
+ this.pageSize = pageSize
+ this.fetchData()
+ },
+ handleChangePageSize (currentPage, pageSize) {
+ this.page = currentPage
+ this.pageSize = pageSize
+ this.fetchData()
}
}
}
@@ -381,4 +410,8 @@ export default {
}
}
}
+
+.pagination {
+ margin-top: 20px;
+}
</style>
diff --git a/src/views/network/EgressConfigure.vue b/src/views/network/EgressRulesTab.vue
similarity index 70%
rename from src/views/network/EgressConfigure.vue
rename to src/views/network/EgressRulesTab.vue
index 22d9db6..df78788 100644
--- a/src/views/network/EgressConfigure.vue
+++ b/src/views/network/EgressRulesTab.vue
@@ -20,15 +20,15 @@
<div>
<div class="form">
<div class="form__item">
- <div class="form__label">Source CIDR</div>
+ <div class="form__label">{{ $t('sourcecidr') }}</div>
<a-input v-model="newRule.cidrlist"></a-input>
</div>
<div class="form__item">
- <div class="form__label">Destination CIDR</div>
+ <div class="form__label">{{ $t('destcidr') }}</div>
<a-input v-model="newRule.destcidrlist"></a-input>
</div>
<div class="form__item">
- <div class="form__label">Protocol</div>
+ <div class="form__label">{{ $t('protocol') }}</div>
<a-select v-model="newRule.protocol" style="width: 100%;" @change="resetRulePorts">
<a-select-option value="tcp">TCP</a-select-option>
<a-select-option value="udp">UDP</a-select-option>
@@ -37,19 +37,19 @@
</a-select>
</div>
<div v-show="newRule.protocol === 'tcp' || newRule.protocol === 'udp'" class="form__item">
- <div class="form__label">Start Port</div>
+ <div class="form__label">{{ $t('startport') }}</div>
<a-input v-model="newRule.startport"></a-input>
</div>
<div v-show="newRule.protocol === 'tcp' || newRule.protocol === 'udp'" class="form__item">
- <div class="form__label">End Port</div>
+ <div class="form__label">{{ $t('endport') }}</div>
<a-input v-model="newRule.endport"></a-input>
</div>
<div v-show="newRule.protocol === 'icmp'" class="form__item">
- <div class="form__label">ICMP Type</div>
+ <div class="form__label">{{ $t('icmptype') }}</div>
<a-input v-model="newRule.icmptype"></a-input>
</div>
<div v-show="newRule.protocol === 'icmp'" class="form__item">
- <div class="form__label">ICMP Code</div>
+ <div class="form__label">{{ $t('icmpcode') }}</div>
<a-input v-model="newRule.icmpcode"></a-input>
</div>
<div class="form__item">
@@ -60,35 +60,39 @@
<a-divider/>
- <a-list :loading="loading" style="min-height: 25px;">
- <a-list-item v-for="rule in egressRules" :key="rule.id" class="rule">
- <div class="rule-container">
- <div class="rule__item">
- <div class="rule__title">Source CIDR</div>
- <div>{{ rule.cidrlist }}</div>
- </div>
- <div class="rule__item">
- <div class="rule__title">Destination CIDR</div>
- <div>{{ rule.destcidrlist }}</div>
- </div>
- <div class="rule__item">
- <div class="rule__title">Protocol</div>
- <div>{{ rule.protocol | capitalise }}</div>
- </div>
- <div class="rule__item">
- <div class="rule__title">{{ rule.protocol === 'icmp' ? 'ICMP Type' : 'Start Port' }}</div>
- <div>{{ rule.icmptype || rule.startport >= 0 ? rule.icmptype || rule.startport : 'All' }}</div>
- </div>
- <div class="rule__item">
- <div class="rule__title">{{ rule.protocol === 'icmp' ? 'ICMP Code' : 'End Port' }}</div>
- <div>{{ rule.icmpcode || rule.endport >= 0 ? rule.icmpcode || rule.endport : 'All' }}</div>
- </div>
- <div slot="actions">
- <a-button shape="round" type="danger" icon="delete" @click="deleteRule(rule)" />
- </div>
- </div>
- </a-list-item>
- </a-list>
+ <a-table
+ size="small"
+ style="overflow-y: auto"
+ :loading="loading"
+ :columns="columns"
+ :dataSource="egressRules"
+ :pagination="false"
+ :rowKey="record => record.id">
+ <template slot="protocol" slot-scope="record">
+ {{ record.protocol | capitalise }}
+ </template>
+ <template slot="startport" slot-scope="record">
+ {{ record.icmptype || record.startport >= 0 ? record.icmptype || record.startport : 'All' }}
+ </template>
+ <template slot="endport" slot-scope="record">
+ {{ record.icmpcode || record.endport >= 0 ? record.icmpcode || record.endport : 'All' }}
+ </template>
+ <template slot="actions" slot-scope="record">
+ <a-button shape="round" type="danger" icon="delete" @click="deleteRule(record)" />
+ </template>
+ </a-table>
+ <a-pagination
+ class="pagination"
+ size="small"
+ :current="page"
+ :pageSize="pageSize"
+ :total="totalCount"
+ :showTotal="total => `Total ${total} items`"
+ :pageSizeOptions="['10', '20', '40', '80', '100']"
+ @change="handleChangePage"
+ @showSizeChange="handleChangePageSize"
+ showSizeChanger/>
+
</div>
</template>
@@ -96,6 +100,7 @@
import { api } from '@/api'
export default {
+ name: 'EgressRulesTab',
props: {
resource: {
type: Object,
@@ -115,7 +120,36 @@ export default {
icmpcode: null,
startport: null,
endport: null
- }
+ },
+ totalCount: 0,
+ page: 1,
+ pageSize: 10,
+ columns: [
+ {
+ title: this.$t('sourcecidr'),
+ dataIndex: 'cidrlist'
+ },
+ {
+ title: this.$t('destcidr'),
+ dataIndex: 'destcidrlist'
+ },
+ {
+ title: this.$t('protocol'),
+ scopedSlots: { customRender: 'protocol' }
+ },
+ {
+ title: `ICMP Type / Start Port`,
+ scopedSlots: { customRender: 'startport' }
+ },
+ {
+ title: `ICMP Code / End Port`,
+ scopedSlots: { customRender: 'endport' }
+ },
+ {
+ title: this.$t('action'),
+ scopedSlots: { customRender: 'actions' }
+ }
+ ]
}
},
mounted () {
@@ -141,9 +175,13 @@ export default {
this.loading = true
api('listEgressFirewallRules', {
listAll: true,
- networkid: this.resource.id
+ networkid: this.resource.id,
+ page: this.page,
+ pageSize: this.pageSize
}).then(response => {
- this.egressRules = response.listegressfirewallrulesresponse.firewallrule
+ this.egressRules = response.listegressfirewallrulesresponse.firewallrule || []
+ this.totalCount = response.listegressfirewallrulesresponse.count || 0
+ }).finally(() => {
this.loading = false
})
},
@@ -211,6 +249,16 @@ export default {
this.newRule.icmpcode = null
this.newRule.startport = null
this.newRule.endport = null
+ },
+ handleChangePage (page, pageSize) {
+ this.page = page
+ this.pageSize = pageSize
+ this.fetchData()
+ },
+ handleChangePageSize (currentPage, pageSize) {
+ this.page = currentPage
+ this.pageSize = pageSize
+ this.fetchData()
}
}
}
@@ -300,4 +348,7 @@ export default {
}
}
+ .pagination {
+ margin-top: 20px;
+ }
</style>
diff --git a/src/views/network/FirewallRules.vue b/src/views/network/FirewallRules.vue
index a0a2479..76b815e 100644
--- a/src/views/network/FirewallRules.vue
+++ b/src/views/network/FirewallRules.vue
@@ -55,36 +55,41 @@
<a-divider/>
- <a-list :loading="loading" style="min-height: 25px;">
- <a-list-item v-for="rule in firewallRules" :key="rule.id" class="rule">
- <div class="rule-container">
- <div class="rule__item">
- <div class="rule__title">{{ $t('sourcecidr') }}</div>
- <div>{{ rule.cidrlist }}</div>
- </div>
- <div class="rule__item">
- <div class="rule__title">{{ $t('protocol') }}</div>
- <div>{{ rule.protocol | capitalise }}</div>
- </div>
- <div class="rule__item">
- <div class="rule__title">{{ rule.protocol === 'icmp' ? $t('icmptype') : $t('startport') }}</div>
- <div>{{ rule.icmptype || rule.startport >= 0 ? rule.icmptype || rule.startport : $t('all') }}</div>
- </div>
- <div class="rule__item">
- <div class="rule__title">{{ rule.protocol === 'icmp' ? 'ICMP Code' : 'End Port' }}</div>
- <div>{{ rule.icmpcode || rule.endport >= 0 ? rule.icmpcode || rule.endport : $t('all') }}</div>
- </div>
- <div class="rule__item">
- <div class="rule__title">{{ $t('state') }}</div>
- <div>{{ rule.state }}</div>
- </div>
- <div slot="actions">
- <a-button shape="round" icon="tag" class="rule-action" @click="() => openTagsModal(rule.id)" />
- <a-button shape="round" type="danger" icon="delete" class="rule-action" @click="deleteRule(rule)" />
- </div>
+ <a-table
+ size="small"
+ style="overflow-y: auto"
+ :loading="loading"
+ :columns="columns"
+ :dataSource="firewallRules"
+ :pagination="false"
+ :rowKey="record => record.id">
+ <template slot="protocol" slot-scope="record">
+ {{ record.protocol | capitalise }}
+ </template>
+ <template slot="startport" slot-scope="record">
+ {{ record.icmptype || record.startport >= 0 ? record.icmptype || record.startport : $t('all') }}
+ </template>
+ <template slot="endport" slot-scope="record">
+ {{ record.icmpcode || record.endport >= 0 ? record.icmpcode || record.endport : $t('all') }}
+ </template>
+ <template slot="actions" slot-scope="record">
+ <div class="actions">
+ <a-button shape="round" icon="tag" class="rule-action" @click="() => openTagsModal(record.id)" />
+ <a-button shape="round" type="danger" icon="delete" class="rule-action" @click="deleteRule(record)" />
</div>
- </a-list-item>
- </a-list>
+ </template>
+ </a-table>
+ <a-pagination
+ class="pagination"
+ size="small"
+ :current="page"
+ :pageSize="pageSize"
+ :total="totalCount"
+ :showTotal="total => `Total ${total} items`"
+ :pageSizeOptions="['10', '20', '40', '80', '100']"
+ @change="handleChangePage"
+ @showSizeChange="handleChangePageSize"
+ showSizeChanger/>
<a-modal title="Edit Tags" v-model="tagsModalVisible" :footer="null" :afterClose="closeModal">
<div class="add-tags">
@@ -96,7 +101,7 @@
<p class="add-tags__label">{{ $t('value') }}</p>
<a-input v-model="newTag.value"></a-input>
</div>
- <a-button type="primary" @click="() => handleAddTag()">{{ $t('add') }}</a-button>
+ <a-button type="primary" @click="() => handleAddTag()" :loading="addTagLoading">{{ $t('add') }}</a-button>
</div>
<a-divider></a-divider>
@@ -129,6 +134,7 @@ export default {
data () {
return {
loading: true,
+ addTagLoading: false,
firewallRules: [],
newRule: {
protocol: 'tcp',
@@ -145,7 +151,36 @@ export default {
newTag: {
key: null,
value: null
- }
+ },
+ totalCount: 0,
+ page: 1,
+ pageSize: 10,
+ columns: [
+ {
+ title: this.$t('sourcecidr'),
+ dataIndex: 'cidrlist'
+ },
+ {
+ title: this.$t('protocol'),
+ scopedSlots: { customRender: 'protocol' }
+ },
+ {
+ title: `${this.$t('startport')}/${this.$t('icmptype')}`,
+ scopedSlots: { customRender: 'startport' }
+ },
+ {
+ title: `${this.$t('endport')}/${this.$t('icmpcode')}`,
+ scopedSlots: { customRender: 'endport' }
+ },
+ {
+ title: this.$t('state'),
+ dataIndex: 'state'
+ },
+ {
+ title: this.$t('action'),
+ scopedSlots: { customRender: 'actions' }
+ }
+ ]
}
},
mounted () {
@@ -171,9 +206,12 @@ export default {
this.loading = true
api('listFirewallRules', {
listAll: true,
- ipaddressid: this.resource.id
+ ipaddressid: this.resource.id,
+ page: this.page,
+ pageSize: this.pageSize
}).then(response => {
- this.firewallRules = response.listfirewallrulesresponse.firewallrule
+ this.firewallRules = response.listfirewallrulesresponse.firewallrule || []
+ this.totalCount = response.listfirewallrulesresponse.count || 0
}).catch(error => {
this.$notification.error({
message: `Error ${error.response.status}`,
@@ -271,6 +309,7 @@ export default {
})
},
handleAddTag () {
+ this.addTagLoading = true
api('createTags', {
'tags[0].key': this.newTag.key,
'tags[0].value': this.newTag.value,
@@ -305,6 +344,8 @@ export default {
description: error.response.data.createtagsresponse.errortext
})
this.closeModal()
+ }).finally(() => {
+ this.addTagLoading = false
})
},
handleDeleteTag (tag) {
@@ -343,6 +384,16 @@ export default {
})
this.closeModal()
})
+ },
+ handleChangePage (page, pageSize) {
+ this.page = page
+ this.pageSize = pageSize
+ this.fetchData()
+ },
+ handleChangePageSize (currentPage, pageSize) {
+ this.page = currentPage
+ this.pageSize = pageSize
+ this.fetchData()
}
}
}
@@ -434,7 +485,6 @@ export default {
}
.rule-action {
- margin-bottom: 20px;
&:not(:last-of-type) {
margin-right: 10px;
@@ -472,5 +522,8 @@ export default {
display: block;
margin-left: auto;
}
+ .pagination {
+ margin-top: 20px;
+ }
</style>
diff --git a/src/views/network/IngressEgressRuleConfigure.vue b/src/views/network/IngressEgressRuleConfigure.vue
index 24b6cfc..75432e6 100644
--- a/src/views/network/IngressEgressRuleConfigure.vue
+++ b/src/views/network/IngressEgressRuleConfigure.vue
@@ -69,49 +69,33 @@
</div>
</div>
- <a-list>
- <a-list-item v-for="(rule, index) in rules" :key="index" class="list">
- <div class="list__col">
- <div class="list__title">Protocol</div>
- <div>{{ rule.protocol | capitalise }}</div>
+ <a-table
+ size="small"
+ style="overflow-y: auto"
+ :columns="columns"
+ :dataSource="rules"
+ :pagination="false"
+ :rowKey="record => record.id">
+ <template slot="protocol" slot-scope="record">
+ {{ record.protocol | capitalise }}
+ </template>
+ <template slot="account" slot-scope="record">
+ <div v-if="record.account && record.securitygroupname">
+ {{ record.account }} - {{ record.securitygroupname }}
</div>
- <div v-if="rule.startport" class="list__col">
- <div class="list__title">Start Port</div>
- <div>{{ rule.startport }}</div>
- </div>
- <div v-if="rule.endport" class="list__col">
- <div class="list__title">End Port</div>
- <div>{{ rule.endport }}</div>
- </div>
- <div v-if="rule.icmptype" class="list__col">
- <div class="list__title">ICMP Type</div>
- <div>{{ rule.icmptype }}</div>
- </div>
- <div v-if="rule.icmpcode" class="list__col">
- <div class="list__title">ICMP Code</div>
- <div>{{ rule.icmpcode }}</div>
- </div>
- <div v-if="rule.cidr" class="list__col">
- <div class="list__title">CIDR</div>
- <div>{{ rule.cidr }}</div>
- </div>
- <div class="list__col" v-if="rule.account && rule.securitygroupname">
- <div class="list__title">Account, Security Group</div>
- <div>{{ rule.account }} - {{ rule.securitygroupname }}</div>
- </div>
- <div slot="actions" class="actions">
- <a-button shape="round" icon="tag" class="rule-action" @click="() => openTagsModal(rule)" />
- <a-popconfirm
- :title="$t('label.delete') + '?'"
- @confirm="handleDeleteRule(rule)"
- okText="Yes"
- cancelText="No"
- >
- <a-button shape="round" type="danger" icon="delete" class="rule-action" />
- </a-popconfirm>
- </div>
- </a-list-item>
- </a-list>
+ </template>
+ <template slot="actions" slot-scope="record">
+ <a-button shape="round" icon="tag" class="rule-action" @click="() => openTagsModal(record)" />
+ <a-popconfirm
+ :title="$t('label.delete') + '?'"
+ @confirm="handleDeleteRule(record)"
+ okText="Yes"
+ cancelText="No"
+ >
+ <a-button shape="round" type="danger" icon="delete" class="rule-action" />
+ </a-popconfirm>
+ </template>
+ </a-table>
<a-modal title="Edit Tags" v-model="tagsModalVisible" :footer="null" :afterClose="closeModal">
<a-spin v-if="tagsLoading"></a-spin>
@@ -187,7 +171,41 @@ export default {
selectedRule: null,
tagsLoading: false,
addType: 'cidr',
- tabType: null
+ tabType: null,
+ columns: [
+ {
+ title: this.$t('protocol'),
+ scopedSlots: { customRender: 'protocol' }
+ },
+ {
+ title: this.$t('startport'),
+ dataIndex: 'startport'
+ },
+ {
+ title: this.$t('endport'),
+ dataIndex: 'endport'
+ },
+ {
+ title: 'ICMP Type',
+ dataIndex: 'icmptype'
+ },
+ {
+ title: 'ICMP Code',
+ dataIndex: 'icmpcode'
+ },
+ {
+ title: 'CIDR',
+ dataIndex: 'cidr'
+ },
+ {
+ title: 'Account, Security Group',
+ scopedSlots: { customRender: 'account' }
+ },
+ {
+ title: this.$t('action'),
+ scopedSlots: { customRender: 'actions' }
+ }
+ ]
}
},
watch: {
diff --git a/src/views/network/LoadBalancing.vue b/src/views/network/LoadBalancing.vue
index 5f175b6..3a043f5 100644
--- a/src/views/network/LoadBalancing.vue
+++ b/src/views/network/LoadBalancing.vue
@@ -59,90 +59,78 @@
<a-divider />
- <a-list :loading="loading" style="min-height: 25px;">
- <a-list-item v-for="rule in lbRules" :key="rule.id" class="rule custom-ant-list">
- <div class="rule-container">
- <div class="rule__row">
- <div class="rule__item">
- <div class="rule__title">{{ $t('name') }}</div>
- <div>{{ rule.name }}</div>
+ <a-table
+ size="small"
+ style="overflow-y: auto"
+ :loading="loading"
+ :columns="columns"
+ :dataSource="lbRules"
+ :pagination="false"
+ :rowKey="record => record.id">
+ <template slot="algorithm" slot-scope="record">
+ {{ returnAlgorithmName(record.algorithm) }}
+ </template>
+ <template slot="protocol" slot-scope="record">
+ {{ record.protocol | capitalise }}
+ </template>
+ <template slot="stickiness" slot-scope="record">
+ <a-button @click="() => openStickinessModal(record.id)">
+ {{ returnStickinessLabel(record.id) }}
+ </a-button>
+ </template>
+ <template slot="add" slot-scope="record">
+ <a-button type="primary" icon="plus" @click="() => { selectedRule = record; handleOpenAddVMModal() }">
+ {{ $t('add') }}
+ </a-button>
+ </template>
+ <template slot="expandedRowRender" slot-scope="record">
+ <div class="rule-instance-list">
+ <div v-for="instance in record.ruleInstances" :key="instance.loadbalancerruleinstance.id">
+ <div v-for="ip in instance.lbvmipaddresses" :key="ip" class="rule-instance-list__item">
+ <div>
+ <a-icon type="desktop" />
+ <router-link :to="{ path: '/vm/' + record.virtualmachineid }">
+ {{ instance.loadbalancerruleinstance.displayname }}
+ </router-link>
+ </div>
+ <div>{{ ip }}</div>
+ <div><status :text="instance.loadbalancerruleinstance.state" displayText /></div>
+ <a-button
+ size="small"
+ shape="round"
+ type="danger"
+ icon="delete"
+ @click="() => handleDeleteInstanceFromRule(instance, record, ip)" />
</div>
- <div class="rule__item">
- <div class="rule__title">{{ $t('publicport') }}</div>
- <div>{{ rule.publicport }}</div>
- </div>
- <div class="rule__item">
- <div class="rule__title">{{ $t('privateport') }}</div>
- <div>{{ rule.privateport }}</div>
- </div>
- <div class="rule__item">
- <div class="rule__title">{{ $t('algorithm') }}</div>
- <div>{{ returnAlgorithmName(rule.algorithm) }}</div>
- </div>
- </div>
- <div class="rule__row">
- <div class="rule__item">
- <div class="rule__title">{{ $t('protocol') }}</div>
- <div>{{ rule.protocol | capitalise }}</div>
- </div>
- <div class="rule__item">
- <div class="rule__title">{{ $t('state') }}</div>
- <div>{{ rule.state }}</div>
- </div>
- <div class="rule__item">
- <div class="rule__title">{{ $t('label.action.configure.stickiness') }}</div>
- <a-button @click="() => openStickinessModal(rule.id)">
- {{ returnStickinessLabel(rule.id) }}
- </a-button>
- </div>
- <div class="rule__item">
- <div class="rule__title">{{ $t('label.add.VMs') }}</div>
- <a-button type="primary" icon="plus" @click="() => { selectedRule = rule; handleOpenAddVMModal() }">
- {{ $t('add') }}
- </a-button>
- </div>
- </div>
- <div class="rule__row" v-if="rule.ruleInstances">
- <a-collapse :bordered="false" class="rule-instance-collapse">
- <template v-slot:expandIcon="props">
- <a-icon type="caret-right" :rotate="props.isActive ? 90 : 0" />
- </template>
- <a-collapse-panel header="View Instances">
- <div class="rule-instance-list">
- <div v-for="instance in rule.ruleInstances" :key="instance.loadbalancerruleinstance.id">
- <div v-for="ip in instance.lbvmipaddresses" :key="ip" class="rule-instance-list__item">
- <div>
- <a-icon type="desktop" />
- <router-link :to="{ path: '/vm/' + rule.virtualmachineid }"> {{ instance.loadbalancerruleinstance.displayname }}</router-link>
- </div>
- <div>{{ ip }}</div>
- <div><status :text="instance.loadbalancerruleinstance.state" displayText /></div>
- <a-button
- shape="round"
- type="danger"
- icon="delete"
- @click="() => handleDeleteInstanceFromRule(instance, rule, ip)" />
- </div>
- </div>
- </div>
- </a-collapse-panel>
- </a-collapse>
</div>
</div>
- <div class="rule__item">
- <a-button shape="circle" icon="edit" class="rule-action" @click="() => openEditRuleModal(rule)"></a-button>
- <a-button shape="circle" icon="tag" class="rule-action" @click="() => openTagsModal(rule.id)" />
+ </template>
+ <template slot="actions" slot-scope="record">
+ <div class="actions">
+ <a-button size="small" shape="circle" icon="edit" @click="() => openEditRuleModal(record)"></a-button>
+ <a-button size="small" shape="circle" icon="tag" @click="() => openTagsModal(record.id)" />
<a-popconfirm
:title="$t('label.delete') + '?'"
- @confirm="handleDeleteRule(rule)"
+ @confirm="handleDeleteRule(record)"
okText="Yes"
cancelText="No"
>
- <a-button shape="circle" type="danger" icon="delete" class="rule-action" />
+ <a-button size="small" shape="circle" type="danger" icon="delete" />
</a-popconfirm>
</div>
- </a-list-item>
- </a-list>
+ </template>
+ </a-table>
+ <a-pagination
+ class="pagination"
+ size="small"
+ :current="page"
+ :pageSize="pageSize"
+ :total="totalCount"
+ :showTotal="total => `Total ${total} items`"
+ :pageSizeOptions="['10', '20', '40', '80', '100']"
+ @change="handleChangePage"
+ @showSizeChange="handleChangePageSize"
+ showSizeChanger/>
<a-modal title="Edit Tags" v-model="tagsModalVisible" :footer="null" :afterClose="closeModal" class="tags-modal">
<span v-show="tagsModalLoading" class="modal-loading">
@@ -393,7 +381,48 @@ export default {
addVmModalLoading: false,
addVmModalNicLoading: false,
vms: [],
- nics: []
+ nics: [],
+ totalCount: 0,
+ page: 1,
+ pageSize: 10,
+ columns: [
+ {
+ title: this.$t('name'),
+ dataIndex: 'name'
+ },
+ {
+ title: this.$t('publicport'),
+ dataIndex: 'publicport'
+ },
+ {
+ title: this.$t('privateport'),
+ dataIndex: 'privateport'
+ },
+ {
+ title: this.$t('algorithm'),
+ scopedSlots: { customRender: 'algorithm' }
+ },
+ {
+ title: this.$t('protocol'),
+ scopedSlots: { customRender: 'protocol' }
+ },
+ {
+ title: this.$t('state'),
+ dataIndex: 'state'
+ },
+ {
+ title: this.$t('label.action.configure.stickiness'),
+ scopedSlots: { customRender: 'stickiness' }
+ },
+ {
+ title: this.$t('label.add.VMs'),
+ scopedSlots: { customRender: 'add' }
+ },
+ {
+ title: this.$t('action'),
+ scopedSlots: { customRender: 'actions' }
+ }
+ ]
}
},
mounted () {
@@ -421,11 +450,12 @@ export default {
this.stickinessPolicies = []
api('listLoadBalancerRules', {
listAll: true,
- publicipid: this.resource.id
+ publicipid: this.resource.id,
+ page: this.page,
+ pageSize: this.pageSize
}).then(response => {
- this.lbRules = response.listloadbalancerrulesresponse.loadbalancerrule
- ? response.listloadbalancerrulesresponse.loadbalancerrule
- : []
+ this.lbRules = response.listloadbalancerrulesresponse.loadbalancerrule || []
+ this.totalCount = response.listloadbalancerrulesresponse.count || 0
}).then(() => {
if (this.lbRules.length > 0) {
setTimeout(() => {
@@ -1041,6 +1071,16 @@ export default {
this.newRule.virtualmachineid = []
this.newTagsForm.resetFields()
this.stickinessPolicyForm.resetFields()
+ },
+ handleChangePage (page, pageSize) {
+ this.page = page
+ this.pageSize = pageSize
+ this.fetchData()
+ },
+ handleChangePageSize (currentPage, pageSize) {
+ this.page = currentPage
+ this.pageSize = pageSize
+ this.fetchData()
}
}
}
@@ -1370,4 +1410,16 @@ export default {
}
}
+
+ .pagination {
+ margin-top: 20px;
+ }
+
+ .actions {
+ button {
+ &:not(:last-child) {
+ margin-right: 10px;
+ }
+ }
+ }
</style>
diff --git a/src/views/network/PortForwarding.vue b/src/views/network/PortForwarding.vue
index eb196a7..fa55d4b 100644
--- a/src/views/network/PortForwarding.vue
+++ b/src/views/network/PortForwarding.vue
@@ -71,37 +71,47 @@
<a-divider/>
- <a-list :loading="loading" style="min-height: 25px;">
- <a-list-item v-for="rule in portForwardRules" :key="rule.id" class="rule">
- <div class="rule-container">
- <div class="rule__item">
- <div class="rule__title">{{ $t('privateport') }}</div>
- <div>{{ rule.privateport }} - {{ rule.privateendport }}</div>
- </div>
- <div class="rule__item">
- <div class="rule__title">{{ $t('publicport') }}</div>
- <div>{{ rule.publicport }} - {{ rule.publicendport }}</div>
- </div>
- <div class="rule__item">
- <div class="rule__title">{{ $t('protocol') }}</div>
- <div>{{ rule.protocol | capitalise }}</div>
- </div>
- <div class="rule__item">
- <div class="rule__title">{{ $t('state') }}</div>
- <div>{{ rule.state }}</div>
- </div>
- <div class="rule__item">
- <div class="rule__title">{{ $t('vm') }}</div>
- <div class="rule__title"></div>
- <div><a-icon type="desktop"/> <router-link :to="{ path: '/vm/' + rule.virtualmachineid }">{{ rule.virtualmachinename }}</router-link> ({{ rule.vmguestip }})</div>
- </div>
- <div slot="actions">
- <a-button shape="round" icon="tag" class="rule-action" @click="() => openTagsModal(rule.id)" />
- <a-button shape="round" type="danger" icon="delete" class="rule-action" @click="deleteRule(rule)" />
- </div>
+ <a-table
+ size="small"
+ style="overflow-y: auto"
+ :loading="loading"
+ :columns="columns"
+ :dataSource="portForwardRules"
+ :pagination="false"
+ :rowKey="record => record.id">
+ <template slot="privateport" slot-scope="record">
+ {{ record.privateport }} - {{ record.privateendport }}
+ </template>
+ <template slot="publicport" slot-scope="record">
+ {{ record.publicport }} - {{ record.publicendport }}
+ </template>
+ <template slot="protocol" slot-scope="record">
+ {{ record.protocol | capitalise }}
+ </template>
+ <template slot="vm" slot-scope="record">
+ <div><a-icon type="desktop"/>
+ <router-link
+ :to="{ path: '/vm/' + record.virtualmachineid }">
+ {{ record.virtualmachinename }}</router-link> ({{ record.vmguestip }})</div>
+ </template>
+ <template slot="actions" slot-scope="record">
+ <div class="actions">
+ <a-button shape="round" icon="tag" class="rule-action" @click="() => openTagsModal(record.id)" />
+ <a-button shape="round" type="danger" icon="delete" class="rule-action" @click="deleteRule(record)" />
</div>
- </a-list-item>
- </a-list>
+ </template>
+ </a-table>
+ <a-pagination
+ class="pagination"
+ size="small"
+ :current="page"
+ :pageSize="pageSize"
+ :total="totalCount"
+ :showTotal="total => `Total ${total} items`"
+ :pageSizeOptions="['10', '20', '40', '80', '100']"
+ @change="handleChangePage"
+ @showSizeChange="handleChangePageSize"
+ showSizeChanger/>
<a-modal title="Edit Tags" v-model="tagsModalVisible" :footer="null" :afterClose="closeModal">
<span v-show="tagsModalLoading" class="tags-modal-loading">
@@ -227,7 +237,36 @@ export default {
addVmModalLoading: false,
addVmModalNicLoading: false,
vms: [],
- nics: []
+ nics: [],
+ totalCount: 0,
+ page: 1,
+ pageSize: 10,
+ columns: [
+ {
+ title: this.$t('privateport'),
+ scopedSlots: { customRender: 'privateport' }
+ },
+ {
+ title: this.$t('publicport'),
+ scopedSlots: { customRender: 'publicport' }
+ },
+ {
+ title: this.$t('protocol'),
+ scopedSlots: { customRender: 'protocol' }
+ },
+ {
+ title: this.$t('state'),
+ dataIndex: 'state'
+ },
+ {
+ title: this.$t('vm'),
+ scopedSlots: { customRender: 'vm' }
+ },
+ {
+ title: this.$t('action'),
+ scopedSlots: { customRender: 'actions' }
+ }
+ ]
}
},
mounted () {
@@ -253,9 +292,12 @@ export default {
this.loading = true
api('listPortForwardingRules', {
listAll: true,
- ipaddressid: this.resource.id
+ ipaddressid: this.resource.id,
+ page: this.page,
+ pageSize: this.pageSize
}).then(response => {
- this.portForwardRules = response.listportforwardingrulesresponse.portforwardingrule
+ this.portForwardRules = response.listportforwardingrulesresponse.portforwardingrule || []
+ this.totalCount = response.listportforwardingrulesresponse.count || 0
}).catch(error => {
this.$notification.error({
message: `Error ${error.response.status}`,
@@ -487,6 +529,16 @@ export default {
})
this.closeModal()
})
+ },
+ handleChangePage (page, pageSize) {
+ this.page = page
+ this.pageSize = pageSize
+ this.fetchData()
+ },
+ handleChangePageSize (currentPage, pageSize) {
+ this.page = currentPage
+ this.pageSize = pageSize
+ this.fetchData()
}
}
}
@@ -590,7 +642,6 @@ export default {
}
.rule-action {
- margin-bottom: 20px;
&:not(:last-of-type) {
margin-right: 10px;
@@ -672,4 +723,8 @@ export default {
}
+ .pagination {
+ margin-top: 20px;
+ }
+
</style>