mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-11-04 00:02:37 +01:00 
			
		
		
		
	vpc: static routes tab (#139)
Adds static routes tab for private gateway. Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com> Co-authored-by: Rohit Yadav <rohit@apache.org>
This commit is contained in:
		
							parent
							
								
									ef189cea3f
								
							
						
					
					
						commit
						f7a2428bf3
					
				@ -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>
 | 
			
		||||
 | 
			
		||||
@ -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",
 | 
			
		||||
 | 
			
		||||
@ -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>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user