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/02/03 09:39:22 UTC
[cloudstack-primate] branch master updated: vpc: static routes tab
(#139)
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 a06374b vpc: static routes tab (#139)
a06374b is described below
commit a06374b4d3771379a2e6b366f5b0582be7950deb
Author: Ritchie Vincent <rf...@gmail.com>
AuthorDate: Mon Feb 3 09:39:12 2020 +0000
vpc: static routes tab (#139)
Adds static routes tab for private gateway.
Signed-off-by: Rohit Yadav <ro...@shapeblue.com>
Co-authored-by: Rohit Yadav <ro...@apache.org>
---
src/layouts/ResourceLayout.vue | 4 +-
src/locales/en.json | 2 +
src/views/network/StaticRoutesTab.vue | 322 ++++++++++++++++++++++++++++++++--
3 files changed, 313 insertions(+), 15 deletions(-)
diff --git a/src/layouts/ResourceLayout.vue b/src/layouts/ResourceLayout.vue
index 5382434..4a84f1d 100644
--- a/src/layouts/ResourceLayout.vue
+++ b/src/layouts/ResourceLayout.vue
@@ -18,11 +18,11 @@
<template>
<div class="page-header-index-wide page-header-wrapper-grid-content-main">
<a-row :gutter="12">
- <a-col :md="24" :lg="8" style="margin-bottom: 12px">
+ <a-col :md="24" :lg="7" style="margin-bottom: 12px">
<slot name="left">
</slot>
</a-col>
- <a-col :md="24" :lg="16">
+ <a-col :md="24" :lg="17">
<slot name="right">
</slot>
</a-col>
diff --git a/src/locales/en.json b/src/locales/en.json
index 7869213..6125c41 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -467,6 +467,7 @@
"label.add.primary.storage": "Add Primary Storage",
"label.add.region": "Add Region",
"label.add.role": "Add Role",
+"label.add.route": "Add Route",
"label.add.rule": "Add Rule",
"label.add.secondary.storage": "Add Secondary Storage",
"label.add.security.group": "Add Security Group",
@@ -492,6 +493,7 @@
"label.change.ipaddress": "Change IP address for NIC",
"label.change.service.offering": "Change service offering",
"label.change.value": "Change value",
+"label.cidr.destination.network": "Destination Network CIDR",
"label.configure": "Configure",
"label.configure.ldap": "Configure LDAP",
"label.configure.vpc": "Configure VPC",
diff --git a/src/views/network/StaticRoutesTab.vue b/src/views/network/StaticRoutesTab.vue
index d96d38d..9c79684 100644
--- a/src/views/network/StaticRoutesTab.vue
+++ b/src/views/network/StaticRoutesTab.vue
@@ -16,24 +16,67 @@
// under the License.
<template>
- <a-spin :spinning="fetchLoading">
- Static Routes Stub
- <a-button type="dashed" icon="plus" style="width: 100%" @click="handleOpenModal">Add: button to add static route</a-button>
- <div v-for="(route, index) in routes" :key="index">
- {{ route }}
+ <a-spin :spinning="componentLoading">
+ <div class="new-route">
+ <a-input v-model="newRoute" icon="plus" :placeholder="$t('label.cidr.destination.network')"></a-input>
+ <a-button type="primary" @click="handleAdd">{{ $t('label.add.route') }}</a-button>
</div>
+
+ <div class="list">
+ <div v-for="(route, index) in routes" :key="index" class="list__item">
+ <div class="list__col">
+ <div class="list__label">{{ $t('label.cidr.destination.network') }}</div>
+ <div>{{ route.cidr }}</div>
+ </div>
+ <div class="actions">
+ <a-button shape="round" icon="tag" @click="() => openTagsModal(route)"></a-button>
+ <a-button shape="round" icon="delete" type="danger" @click="() => handleDelete(route)"></a-button>
+ </div>
+ </div>
+ </div>
+
+ <a-modal title="Edit Tags" v-model="tagsModalVisible" :footer="null">
+ <a-spin v-if="tagsLoading"></a-spin>
+
+ <div v-else>
+ <a-form :form="newTagsForm" class="add-tags" @submit="handleAddTag">
+ <div class="add-tags__input">
+ <p class="add-tags__label">{{ $t('key') }}</p>
+ <a-form-item>
+ <a-input v-decorator="['key', { rules: [{ required: true, message: 'Please specify a tag key'}] }]" />
+ </a-form-item>
+ </div>
+ <div class="add-tags__input">
+ <p class="add-tags__label">{{ $t('value') }}</p>
+ <a-form-item>
+ <a-input v-decorator="['value', { rules: [{ required: true, message: 'Please specify a tag value'}] }]" />
+ </a-form-item>
+ </div>
+ <a-button type="primary" html-type="submit">{{ $t('label.add') }}</a-button>
+ </a-form>
+
+ <a-divider style="margin-top: 0;"></a-divider>
+
+ <div class="tags-container">
+ <div class="tags" v-for="(tag, index) in tags" :key="index">
+ <a-tag :key="index" :closable="true" :afterClose="() => handleDeleteTag(tag)">
+ {{ tag.key }} = {{ tag.value }}
+ </a-tag>
+ </div>
+ </div>
+
+ <a-button class="add-tags-done" @click="tagsModalVisible = false" type="primary">{{ $t('OK') }}</a-button>
+ </div>
+
+ </a-modal>
</a-spin>
</template>
<script>
import { api } from '@/api'
-import Status from '@/components/widgets/Status'
export default {
name: 'StaticRoutesTab',
- components: {
- Status
- },
props: {
resource: {
type: Object,
@@ -47,7 +90,13 @@ export default {
data () {
return {
routes: [],
- fetchLoading: false
+ componentLoading: false,
+ selectedRule: null,
+ tagsModalVisible: false,
+ newTagsForm: this.$form.createForm(this),
+ tags: [],
+ tagsLoading: false,
+ newRoute: null
}
},
mounted () {
@@ -62,7 +111,7 @@ export default {
},
methods: {
fetchData () {
- this.fetchLoading = true
+ this.componentLoading = true
api('listStaticRoutes', { gatewayid: this.resource.id }).then(json => {
this.routes = json.liststaticroutesresponse.staticroute
}).catch(error => {
@@ -71,13 +120,260 @@ export default {
description: error.response.headers['x-description']
})
}).finally(() => {
- this.fetchLoading = false
+ this.componentLoading = false
+ })
+ },
+ handleAdd () {
+ if (!this.newRoute) return
+
+ this.componentLoading = true
+ api('createStaticRoute', {
+ cidr: this.newRoute,
+ gatewayid: this.resource.id
+ }).then(response => {
+ this.$pollJob({
+ jobId: response.createstaticrouteresponse.jobid,
+ successMethod: () => {
+ this.fetchData()
+ this.$store.dispatch('AddAsyncJob', {
+ title: 'Successfully added static route',
+ jobid: response.createstaticrouteresponse.jobid,
+ status: 'progress'
+ })
+ this.componentLoading = false
+ this.newRoute = null
+ },
+ errorMessage: 'Failed to add static route',
+ errorMethod: () => {
+ this.fetchData()
+ this.componentLoading = false
+ },
+ loadingMessage: `Adding static route...`,
+ catchMessage: 'Error encountered while fetching async job result',
+ catchMethod: () => {
+ this.fetchData()
+ this.componentLoading = false
+ }
+ })
+ }).catch(error => {
+ this.$notification.error({
+ message: `Error ${error.response.status}`,
+ description: error.response.headers['x-description']
+ })
+ this.fetchData()
+ this.componentLoading = false
+ })
+ },
+ handleDelete (route) {
+ this.componentLoading = true
+ api('deleteStaticRoute', {
+ id: route.id
+ }).then(response => {
+ this.$pollJob({
+ jobId: response.deletestaticrouteresponse.jobid,
+ successMethod: () => {
+ this.fetchData()
+ this.$store.dispatch('AddAsyncJob', {
+ title: 'Successfully deleted static route',
+ jobid: response.deletestaticrouteresponse.jobid,
+ status: 'progress'
+ })
+ this.componentLoading = false
+ },
+ errorMessage: 'Failed to delete static route',
+ errorMethod: () => {
+ this.fetchData()
+ this.componentLoading = false
+ },
+ loadingMessage: `Deleting static route...`,
+ catchMessage: 'Error encountered while fetching async job result',
+ catchMethod: () => {
+ this.fetchData()
+ this.componentLoading = false
+ }
+ })
+ }).catch(error => {
+ this.$notification.error({
+ message: `Error ${error.response.status}`,
+ description: error.response.headers['x-description']
+ })
+ this.fetchData()
+ this.componentLoading = false
+ })
+ },
+ fetchTags (route) {
+ api('listTags', {
+ resourceId: route.id,
+ resourceType: 'StaticRoute',
+ listAll: true
+ }).then(response => {
+ this.tags = response.listtagsresponse.tag
+ }).catch(error => {
+ this.$notification.error({
+ message: `Error ${error.response.status}`,
+ description: error.response.data.errorresponse.errortext
+ })
+ })
+ },
+ handleDeleteTag (tag) {
+ this.tagsLoading = true
+ api('deleteTags', {
+ 'tags[0].key': tag.key,
+ 'tags[0].value': tag.value,
+ resourceIds: this.selectedRule.id,
+ resourceType: 'StaticRoute'
+ }).then(response => {
+ this.$pollJob({
+ jobId: response.deletetagsresponse.jobid,
+ successMessage: `Successfully deleted tag`,
+ successMethod: () => {
+ this.fetchTags(this.selectedRule)
+ this.tagsLoading = false
+ },
+ errorMessage: 'Failed to delete tag',
+ errorMethod: () => {
+ this.fetchTags(this.selectedRule)
+ this.tagsLoading = false
+ },
+ loadingMessage: `Deleting tag...`,
+ catchMessage: 'Error encountered while fetching async job result',
+ catchMethod: () => {
+ this.fetchTags(this.selectedRule)
+ this.tagsLoading = false
+ }
+ })
+ }).catch(error => {
+ this.$notification.error({
+ message: `Error ${error.response.status}`,
+ description: error.response.data.deletetagsresponse.errortext
+ })
+ this.tagsLoading = false
})
+ },
+ handleAddTag (e) {
+ this.tagsLoading = true
+
+ e.preventDefault()
+ this.newTagsForm.validateFields((err, values) => {
+ if (err) {
+ this.tagsLoading = false
+ return
+ }
+
+ api('createTags', {
+ 'tags[0].key': values.key,
+ 'tags[0].value': values.value,
+ resourceIds: this.selectedRule.id,
+ resourceType: 'StaticRoute'
+ }).then(response => {
+ this.$pollJob({
+ jobId: response.createtagsresponse.jobid,
+ successMessage: `Successfully added new tag`,
+ successMethod: () => {
+ this.fetchTags(this.selectedRule)
+ this.tagsLoading = false
+ },
+ errorMessage: 'Failed to add new tag',
+ errorMethod: () => {
+ this.fetchTags(this.selectedRule)
+ this.tagsLoading = false
+ },
+ loadingMessage: `Adding new tag...`,
+ catchMessage: 'Error encountered while fetching async job result',
+ catchMethod: () => {
+ this.fetchTags(this.selectedRule)
+ this.tagsLoading = false
+ }
+ })
+ }).catch(error => {
+ this.$notification.error({
+ message: `Error ${error.response.status}`,
+ description: error.response.data.createtagsresponse.errortext
+ })
+ this.tagsLoading = false
+ })
+ })
+ },
+ openTagsModal (route) {
+ this.selectedRule = route
+ this.newTagsForm.resetFields()
+ this.fetchTags(this.selectedRule)
+ this.tagsModalVisible = true
}
}
}
</script>
-<style lang="less" scoped>
+<style lang="scss" scoped>
+
+ .list {
+ padding-top: 20px;
+
+ &__item {
+ display: flex;
+ justify-content: space-between;
+
+ &:not(:last-child) {
+ margin-bottom: 20px;
+ }
+ }
+
+ &__label {
+ font-weight: bold;
+ }
+
+ }
+
+ .actions {
+ display: flex;
+
+ button {
+ &:not(:last-child) {
+ margin-right: 10px;
+ }
+ }
+
+ }
+
+ .tags {
+ margin-bottom: 10px;
+ }
+ .add-tags {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ &__input {
+ margin-right: 10px;
+ }
+ &__label {
+ margin-bottom: 5px;
+ font-weight: bold;
+ }
+ }
+ .tags-container {
+ display: flex;
+ flex-wrap: wrap;
+ margin-bottom: 10px;
+ }
+ .add-tags-done {
+ display: block;
+ margin-left: auto;
+ }
+
+ .new-route {
+ display: flex;
+ padding-top: 10px;
+
+ input {
+ margin-right: 10px;
+ }
+
+ button {
+ &:not(:last-child) {
+ margin-right: 10px;
+ }
+ }
+
+ }
</style>