Skip to content

Commit a234501

Browse files
author
Hoang Nguyen
authored
ui: Add guest IP ranges (#4716)
Fixes #4697
1 parent 1545bf8 commit a234501

5 files changed

Lines changed: 495 additions & 2 deletions

File tree

ui/src/config/section/network.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ export default {
5353
name: 'virtual.routers',
5454
component: () => import('@/views/network/RoutersTab.vue'),
5555
show: (record) => { return (record.type === 'Isolated' || record.type === 'Shared') && 'listRouters' in store.getters.apis }
56+
}, {
57+
name: 'guest.ip.range',
58+
component: () => import('@/views/network/GuestIpRanges.vue'),
59+
show: (record) => { return 'listVlanIpRanges' in store.getters.apis && (record.type === 'Shared' || (record.service && record.service.filter(x => x.name === 'SourceNat').count === 0)) }
5660
}],
5761
actions: [
5862
{

ui/src/views/infra/network/IpRangesTabGuest.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@
3434
:rowKey="record => record.id"
3535
:pagination="false"
3636
>
37+
<template slot="name" slot-scope="text, item">
38+
<router-link :to="{ path: '/guestnetwork/' + item.id }">
39+
{{ text }}
40+
</router-link>
41+
</template>
3742
</a-table>
3843
<a-pagination
3944
class="row-element pagination"
@@ -98,7 +103,8 @@ export default {
98103
columns: [
99104
{
100105
title: this.$t('label.name'),
101-
dataIndex: 'name'
106+
dataIndex: 'name',
107+
scopedSlots: { customRender: 'name' }
102108
},
103109
{
104110
title: this.$t('label.type'),

ui/src/views/infra/network/TrafficTypesTab.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ export default {
9090
}
9191
},
9292
mounted () {
93-
this.fetchData()
93+
if (this.resource.id) {
94+
this.fetchData()
95+
}
9496
},
9597
watch: {
9698
loading (newData, oldData) {
Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
<template>
19+
<a-spin :spinning="loading">
20+
<div class="form-layout">
21+
<div class="form">
22+
<a-form
23+
:form="form"
24+
@submit="handleSubmit"
25+
layout="vertical">
26+
<a-form-item>
27+
<span slot="label">
28+
{{ $t('label.gateway') }}
29+
<a-tooltip :title="apiParams.gateway.description">
30+
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
31+
</a-tooltip>
32+
</span>
33+
<a-input
34+
v-decorator="['gateway', {
35+
rules: [{ required: true, message: $t('message.error.gateway') }]
36+
}]"
37+
:placeholder="apiParams.gateway.description"/>
38+
</a-form-item>
39+
<a-form-item>
40+
<span slot="label">
41+
{{ $t('label.netmask') }}
42+
<a-tooltip :title="apiParams.netmask.description">
43+
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
44+
</a-tooltip>
45+
</span>
46+
<a-input
47+
v-decorator="['netmask', {
48+
rules: [{ required: true, message: $t('message.error.netmask') }]
49+
}]"
50+
:placeholder="apiParams.netmask.description"/>
51+
</a-form-item>
52+
<a-row :gutter="12">
53+
<a-col :md="12" lg="12">
54+
<a-form-item>
55+
<span slot="label">
56+
{{ $t('label.startipv4') }}
57+
<a-tooltip :title="apiParams.startip.description">
58+
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
59+
</a-tooltip>
60+
</span>
61+
<a-input
62+
v-decorator="['startip', {
63+
rules: [
64+
{ required: true, message: $t('message.error.startip') },
65+
{
66+
validator: checkIpFormat,
67+
ipV4: true,
68+
message: $t('message.error.ipv4.address')
69+
}
70+
]
71+
}]"
72+
:placeholder="apiParams.startip.description"/>
73+
</a-form-item>
74+
</a-col>
75+
<a-col :md="12" :lg="12">
76+
<a-form-item>
77+
<span slot="label">
78+
{{ $t('label.endipv4') }}
79+
<a-tooltip :title="apiParams.endip.description">
80+
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
81+
</a-tooltip>
82+
</span>
83+
<a-input
84+
v-decorator="['endip', {
85+
rules: [
86+
{ required: true, message: $t('message.error.endip') },
87+
{
88+
validator: checkIpFormat,
89+
ipV4: true,
90+
message: $t('message.error.ipv4.address')
91+
}
92+
]
93+
}]"
94+
:placeholder="apiParams.endip.description"/>
95+
</a-form-item>
96+
</a-col>
97+
</a-row>
98+
<a-form-item>
99+
<span slot="label">
100+
{{ $t('label.ip6cidr') }}
101+
<a-tooltip :title="apiParams.ip6cidr.description">
102+
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
103+
</a-tooltip>
104+
</span>
105+
<a-input
106+
v-decorator="['ip6cidr']"
107+
:placeholder="apiParams.ip6cidr.description"/>
108+
</a-form-item>
109+
<a-form-item>
110+
<span slot="label">
111+
{{ $t('label.ip6gateway') }}
112+
<a-tooltip :title="apiParams.ip6gateway.description">
113+
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
114+
</a-tooltip>
115+
</span>
116+
<a-input
117+
v-decorator="['ip6gateway']"
118+
:placeholder="apiParams.ip6gateway.description"/>
119+
</a-form-item>
120+
<a-row :gutter="12">
121+
<a-col :md="12" :lg="12">
122+
<a-form-item>
123+
<span slot="label">
124+
{{ $t('label.startipv6') }}
125+
<a-tooltip :title="apiParams.startipv6.description">
126+
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
127+
</a-tooltip>
128+
</span>
129+
<a-input
130+
v-decorator="['startipv6', {
131+
rules: [
132+
{
133+
validator: checkIpFormat,
134+
ipV6: true,
135+
message: $t('message.error.ipv6.address')
136+
}
137+
]
138+
}]"
139+
:placeholder="apiParams.startipv6.description"/>
140+
</a-form-item>
141+
</a-col>
142+
<a-col :md="12" :lg="12">
143+
<a-form-item>
144+
<span slot="label">
145+
{{ $t('label.endipv6') }}
146+
<a-tooltip :title="apiParams.endipv6.description">
147+
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
148+
</a-tooltip>
149+
</span>
150+
<a-input
151+
v-decorator="['endipv6', {
152+
rules: [
153+
{
154+
validator: checkIpFormat,
155+
ipV6: true,
156+
message: $t('message.error.ipv6.address')
157+
}
158+
]
159+
}]"
160+
:placeholder="apiParams.endip.description"/>
161+
</a-form-item>
162+
</a-col>
163+
</a-row>
164+
<div :span="24" class="action-button">
165+
<a-button
166+
:loading="loading"
167+
@click="closeAction">
168+
{{ this.$t('label.cancel') }}
169+
</a-button>
170+
<a-button
171+
:loading="loading"
172+
type="primary"
173+
@click="handleSubmit">
174+
{{ this.$t('label.ok') }}
175+
</a-button>
176+
</div>
177+
</a-form>
178+
</div>
179+
</div>
180+
</a-spin>
181+
</template>
182+
183+
<script>
184+
import { api } from '@/api'
185+
186+
export default {
187+
name: 'CreateVlanIpRange',
188+
props: {
189+
resource: {
190+
type: Object,
191+
required: true
192+
}
193+
},
194+
data () {
195+
return {
196+
loading: false,
197+
ipV4Regex: /^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/i,
198+
ipV6Regex: /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i
199+
}
200+
},
201+
created () {
202+
this.form = this.$form.createForm(this)
203+
this.apiConfig = this.$store.getters.apis.createVlanIpRange || {}
204+
this.apiParams = {}
205+
this.apiConfig.params.forEach(param => {
206+
this.apiParams[param.name] = param
207+
})
208+
},
209+
methods: {
210+
handleSubmit (e) {
211+
e.preventDefault()
212+
213+
this.form.validateFields((err, values) => {
214+
if (err) {
215+
return
216+
}
217+
218+
const params = {}
219+
params.forVirtualNetwork = false
220+
params.networkid = this.resource.id
221+
params.gateway = values.gateway
222+
params.netmask = values.netmask
223+
params.startip = values.startip
224+
params.endip = values.endip
225+
params.ip6cidr = values.ip6cidr
226+
params.ip6gateway = values.ip6gateway
227+
params.startipv6 = values.startipv6
228+
params.endipv6 = values.endipv6
229+
230+
this.loading = true
231+
232+
api('createVlanIpRange', params)
233+
.then(() => {
234+
this.$notification.success({
235+
message: this.$t('message.success.add.iprange')
236+
})
237+
this.closeAction()
238+
this.$emit('refresh-data')
239+
}).catch(error => {
240+
this.$notification.error({
241+
message: `${this.$t('label.error')} ${error.response.status}`,
242+
description: error.response.data.createvlaniprangeresponse
243+
? error.response.data.createvlaniprangeresponse.errortext : error.response.data.errorresponse.errortext,
244+
duration: 0
245+
})
246+
}).finally(() => {
247+
this.loading = false
248+
})
249+
})
250+
},
251+
closeAction () {
252+
this.$emit('close-action')
253+
},
254+
checkIpFormat (rule, value, callback) {
255+
if (!value || value === '') {
256+
callback()
257+
} else if (rule.ipV4 && !this.ipV4Regex.test(value)) {
258+
callback(rule.message)
259+
} else if (rule.ipV6 && !this.ipV6Regex.test(value)) {
260+
callback(rule.message)
261+
} else {
262+
callback()
263+
}
264+
}
265+
}
266+
}
267+
</script>
268+
269+
<style lang="less" scoped>
270+
.form-layout {
271+
width: 60vw;
272+
273+
@media (min-width: 500px) {
274+
width: 450px;
275+
}
276+
}
277+
278+
.action-button {
279+
text-align: right;
280+
281+
button {
282+
margin-right: 5px;
283+
}
284+
}
285+
</style>

0 commit comments

Comments
 (0)