mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
network: Egress, PF, FW, VPN, LB tabs (#84)
Implements the egress, pf, fw, vpn and lb tabs for a guest network (ip). Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com> Co-authored-by: Rohit Yadav <rohit@apache.org>
This commit is contained in:
parent
759921ee11
commit
f3858e5297
@ -2,24 +2,16 @@ addNetworkServiceProvider
|
||||
addResourceDetail
|
||||
addTrafficType
|
||||
assignCertToLoadBalancer
|
||||
assignToLoadBalancerRule
|
||||
authorizeSamlSso
|
||||
authorizeSecurityGroupEgress
|
||||
authorizeSecurityGroupIngress
|
||||
configureInternalLoadBalancerElement
|
||||
configureVirtualRouterElement
|
||||
createEgressFirewallRule
|
||||
createFirewallRule
|
||||
createLBHealthCheckPolicy
|
||||
createLBStickinessPolicy
|
||||
createLoadBalancer
|
||||
createLoadBalancerRule
|
||||
createManagementNetworkIpRange
|
||||
createNetworkACL
|
||||
createNetworkACLList
|
||||
createPhysicalNetwork
|
||||
createPortableIpRange
|
||||
createPortForwardingRule
|
||||
createPrivateGateway
|
||||
createSecondaryStagingStore
|
||||
createSnapshotFromVMSnapshot
|
||||
@ -30,25 +22,17 @@ createVpnConnection
|
||||
createVpnGateway
|
||||
dedicateGuestVlanRange
|
||||
dedicatePublicIpRange
|
||||
deleteAccountFromProject
|
||||
deleteEgressFirewallRule
|
||||
deleteFirewallRule
|
||||
deleteLBHealthCheckPolicy
|
||||
deleteLBStickinessPolicy
|
||||
deleteLdapConfiguration
|
||||
deleteLoadBalancer
|
||||
deleteLoadBalancerRule
|
||||
deleteManagementNetworkIpRange
|
||||
deleteNetworkACL
|
||||
deleteNetworkACLList
|
||||
deleteNetworkServiceProvider
|
||||
deletePhysicalNetwork
|
||||
deletePortableIpRange
|
||||
deletePortForwardingRule
|
||||
deletePrivateGateway
|
||||
deleteProjectInvitation
|
||||
deleteSecondaryStagingStore
|
||||
deleteSnapshotPolicies
|
||||
deleteStaticRoute
|
||||
deleteStorageNetworkIpRange
|
||||
deleteVlanIpRange
|
||||
@ -57,74 +41,47 @@ deleteVpnGateway
|
||||
findStoragePoolsForMigration
|
||||
importLdapUsers
|
||||
ldapCreateAccount
|
||||
linkDomainToLdap
|
||||
listAffinityGroupTypes
|
||||
listAndSwitchSamlAccount
|
||||
listCapabilities
|
||||
listDedicatedClusters
|
||||
listDedicatedGuestVlanRanges
|
||||
listDedicatedHosts
|
||||
listDedicatedPods
|
||||
listDedicatedZones
|
||||
listDeploymentPlanners
|
||||
listEgressFirewallRules
|
||||
listFirewallRules
|
||||
listHostHAProviders
|
||||
listHostTags
|
||||
listHypervisors
|
||||
listIdps
|
||||
listInternalLoadBalancerElements
|
||||
listInternalLoadBalancerVMs
|
||||
listLBHealthCheckPolicies
|
||||
listLBStickinessPolicies
|
||||
listLdapUsers
|
||||
listLoadBalancerRuleInstances
|
||||
listLoadBalancerRules
|
||||
listLoadBalancers
|
||||
listNetworkACLLists
|
||||
listNetworkACLs
|
||||
listNetworkServiceProviders
|
||||
listOsCategories
|
||||
listPortableIpRanges
|
||||
listPortForwardingRules
|
||||
listPrivateGateways
|
||||
listProjectAccounts
|
||||
listProjectInvitations
|
||||
listRegisteredServicePackages
|
||||
listRemoteAccessVpns
|
||||
listResourceLimits
|
||||
listSamlAuthorization
|
||||
listSecondaryStagingStores
|
||||
listSnapshotPolicies
|
||||
listStaticRoutes
|
||||
listStorageNetworkIpRange
|
||||
listStorageProviders
|
||||
listStorageTags
|
||||
listSupportedNetworkServices
|
||||
listTemplateOvfProperties
|
||||
listTemplatePermissions
|
||||
listTrafficTypes
|
||||
listVirtualRouterElements
|
||||
listVlanIpRanges
|
||||
listVmwareDcs
|
||||
listVpnConnections
|
||||
listVpnGateways
|
||||
moveNetworkAclItem
|
||||
releaseDedicatedGuestVlanRange
|
||||
releasePublicIpRange
|
||||
removeFromLoadBalancerRule
|
||||
replaceNetworkACLList
|
||||
resetVpnConnection
|
||||
revokeSecurityGroupEgress
|
||||
revokeSecurityGroupIngress
|
||||
startInternalLoadBalancerVM
|
||||
stopInternalLoadBalancerVM
|
||||
updateLoadBalancerRule
|
||||
updateNetworkACLItem
|
||||
updateNetworkACLList
|
||||
updateNetworkServiceProvider
|
||||
updatePhysicalNetwork
|
||||
updateProjectInvitation
|
||||
updateResourceLimit
|
||||
updateTrafficType
|
||||
updateVpnCustomerGateway
|
||||
|
||||
@ -13,8 +13,8 @@ var loadLabel = function (allFields, fieldDict, prefix) {
|
||||
allFields[fieldId].components.push(prefix)
|
||||
} else {
|
||||
allFields[fieldId] = {
|
||||
'labels': [fieldDict[fieldId].label],
|
||||
'components': [prefix]
|
||||
labels: [fieldDict[fieldId].label],
|
||||
components: [prefix]
|
||||
}
|
||||
}
|
||||
cols = cols + "'" + fieldId + "', "
|
||||
@ -28,8 +28,8 @@ var loadLabel = function (allFields, fieldDict, prefix) {
|
||||
allFields[colId].components.push(prefix)
|
||||
} else {
|
||||
allFields[colId] = {
|
||||
'labels': [columns[colId].label],
|
||||
'components': [prefix]
|
||||
labels: [columns[colId].label],
|
||||
components: [prefix]
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -63,9 +63,9 @@ var loadFields = function (data, prefix) {
|
||||
var curActions = []
|
||||
$.each(Object.keys(acVal), function (idx, acKey) {
|
||||
if (acVal[acKey].createForm) {
|
||||
curActions.push({ 'action': acKey, 'label': acVal[acKey].label, 'keys': acVal[acKey].createForm.fields })
|
||||
curActions.push({ action: acKey, label: acVal[acKey].label, keys: acVal[acKey].createForm.fields })
|
||||
} else {
|
||||
curActions.push({ 'action': acKey, 'label': acVal[acKey].label })
|
||||
curActions.push({ action: acKey, label: acVal[acKey].label })
|
||||
}
|
||||
})
|
||||
countActions = countActions + curActions.length
|
||||
@ -77,5 +77,5 @@ var loadFields = function (data, prefix) {
|
||||
$.extend(actions, recRes.actions)
|
||||
}
|
||||
})
|
||||
return { 'allFields': allFields, 'columnsOrder': columnsOrder, 'actions': actions }
|
||||
return { allFields: allFields, columnsOrder: columnsOrder, actions: actions }
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
</div>
|
||||
<slot name="name">
|
||||
<h4 class="name">
|
||||
{{ resource.displayname || resource.name }}
|
||||
{{ resource.displayname || resource.name || resource.displaytext || resource.hostname || resource.username || resource.ipaddress }}
|
||||
</h4>
|
||||
<console style="margin-left: 10px" :resource="resource" size="default" v-if="resource.id" />
|
||||
</slot>
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
v-for="tab in tabs"
|
||||
:tab="$t(tab.name)"
|
||||
:key="tab.name"
|
||||
v-if="'show' in tab ? tab.show(resource, $route, $store.getters.userInfo) : true">
|
||||
v-if="showHideTab(tab)">
|
||||
<component :is="tab.component" :resource="resource" :loading="loading" :tab="activeTab" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
@ -49,6 +49,7 @@
|
||||
import DetailsTab from '@/components/view/DetailsTab'
|
||||
import InfoCard from '@/components/view/InfoCard'
|
||||
import ResourceLayout from '@/layouts/ResourceLayout'
|
||||
import { api } from '@/api'
|
||||
|
||||
export default {
|
||||
name: 'ResourceView',
|
||||
@ -77,12 +78,45 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
activeTab: ''
|
||||
activeTab: '',
|
||||
networkService: null,
|
||||
vpnEnabled: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
resource: function (newItem, oldItem) {
|
||||
this.resource = newItem
|
||||
if (newItem.id === oldItem.id) return
|
||||
|
||||
if (this.resource.associatednetworkid) {
|
||||
api('listNetworks', { id: this.resource.associatednetworkid }).then(response => {
|
||||
this.networkService = response.listnetworksresponse.network[0]
|
||||
})
|
||||
}
|
||||
|
||||
if (this.resource.id && this.resource.ipaddress) {
|
||||
api('listRemoteAccessVpns', {
|
||||
publicipid: this.resource.id,
|
||||
listAll: true
|
||||
}).then(response => {
|
||||
this.vpnEnabled = response.listremoteaccessvpnsresponse.remoteaccessvpn && response.listremoteaccessvpnsresponse.remoteaccessvpn.length > 0
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onTabChange (key) {
|
||||
this.activeTab = key
|
||||
},
|
||||
showHideTab (tab) {
|
||||
if ('networkServiceFilter' in tab) {
|
||||
return this.networkService && this.networkService.service &&
|
||||
tab.networkServiceFilter(this.networkService.service)
|
||||
} else if ('show' in tab) {
|
||||
return tab.show(this.resource, this.$route, this.$store.getters.userInfo)
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,8 +45,9 @@ export default {
|
||||
name: 'details',
|
||||
component: () => import('@/components/view/DetailsTab.vue')
|
||||
}, {
|
||||
name: 'egress-rules',
|
||||
component: () => import('@/views/network/EgressConfigure.vue')
|
||||
name: 'Egress Rules',
|
||||
component: () => import('@/views/network/EgressConfigure.vue'),
|
||||
show: () => true
|
||||
}],
|
||||
actions: [
|
||||
{
|
||||
@ -227,14 +228,23 @@ export default {
|
||||
columns: ['ipaddress', 'state', 'associatednetworkname', 'virtualmachinename', 'allocated', 'account', 'zonename'],
|
||||
details: ['ipaddress', 'id', 'associatednetworkname', 'virtualmachinename', 'networkid', 'issourcenat', 'isstaticnat', 'virtualmachinename', 'vmipaddress', 'vlan', 'allocated', 'account', 'zonename'],
|
||||
tabs: [{
|
||||
name: 'configure',
|
||||
component: () => import('@/views/network/IpConfigure.vue')
|
||||
}, {
|
||||
name: 'vpn',
|
||||
component: () => import('@/views/network/VpnDetails.vue')
|
||||
}, {
|
||||
name: 'details',
|
||||
component: () => import('@/components/view/DetailsTab.vue')
|
||||
}, {
|
||||
name: 'Firewall',
|
||||
component: () => import('@/views/network/FirewallRules.vue'),
|
||||
networkServiceFilter: networkService => networkService.filter(x => x.name === 'Firewall').length > 0
|
||||
}, {
|
||||
name: 'Port Forwarding',
|
||||
component: () => import('@/views/network/PortForwarding.vue'),
|
||||
networkServiceFilter: networkService => networkService.filter(x => x.name === 'PortForwarding').length > 0
|
||||
}, {
|
||||
name: 'Load Balancing',
|
||||
component: () => import('@/views/network/LoadBalancing.vue'),
|
||||
networkServiceFilter: networkService => networkService.filter(x => x.name === 'Lb').length > 0
|
||||
}, {
|
||||
name: 'VPN',
|
||||
component: () => import('@/views/network/VpnDetails.vue')
|
||||
}],
|
||||
actions: [
|
||||
{
|
||||
@ -244,39 +254,6 @@ export default {
|
||||
listView: true,
|
||||
args: ['networkid']
|
||||
},
|
||||
{
|
||||
api: 'createRemoteAccessVpn',
|
||||
icon: 'link',
|
||||
label: 'Enable Remote Access VPN',
|
||||
dataView: true,
|
||||
args: ['publicipid', 'domainid', 'account'],
|
||||
mapping: {
|
||||
publicipid: {
|
||||
value: (record) => { return record.id }
|
||||
},
|
||||
domainid: {
|
||||
value: (record) => { return record.domainid }
|
||||
},
|
||||
account: {
|
||||
value: (record) => { return record.account }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
api: 'deleteRemoteAccessVpn',
|
||||
icon: 'disconnect',
|
||||
label: 'Disable Remove Access VPN',
|
||||
dataView: true,
|
||||
args: ['publicipid', 'domainid'],
|
||||
mapping: {
|
||||
publicipid: {
|
||||
value: (record) => { return record.id }
|
||||
},
|
||||
domainid: {
|
||||
value: (record) => { return record.domainid }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
api: 'enableStaticNat',
|
||||
icon: 'plus-circle',
|
||||
@ -306,7 +283,7 @@ export default {
|
||||
{
|
||||
api: 'disassociateIpAddress',
|
||||
icon: 'delete',
|
||||
label: 'Delete IP',
|
||||
label: 'Release IP',
|
||||
dataView: true,
|
||||
show: (record) => { return !record.issourcenat }
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
"Accounts": "Accounts",
|
||||
"Affinity Groups": "Affinity Groups",
|
||||
"Alerts": "Alerts",
|
||||
"cancel": "Cancel",
|
||||
"CPU Sockets": "CPU Sockets",
|
||||
"Cloudian Storage": "Cloudian Storage",
|
||||
"Clusters": "Clusters",
|
||||
@ -13,6 +14,7 @@
|
||||
"Dashboard": "Dashboard",
|
||||
"Disk Offerings": "Disk Offerings",
|
||||
"Domains": "Domains",
|
||||
"done": "Done",
|
||||
"Events": "Events",
|
||||
"Global Settings": "Global Settings",
|
||||
"Hosts": "Hosts",
|
||||
@ -77,6 +79,7 @@
|
||||
"agentUsername": "Agent Username",
|
||||
"agentstate": "Agent State",
|
||||
"algorithm": "Algorithm",
|
||||
"all": "All",
|
||||
"allocatediops": "IOPS Allocated",
|
||||
"allocationstate": "Allocation State",
|
||||
"annotation": "Annotation",
|
||||
@ -256,6 +259,7 @@
|
||||
"hypervisortype": "Hypervisor",
|
||||
"hypervisorversion": "Hypervisor version",
|
||||
"hypervnetworklabel": "HyperV Traffic Label",
|
||||
"icmp": "ICMP",
|
||||
"icmpcode": "ICMP Code",
|
||||
"icmptype": "ICMP Type",
|
||||
"id": "ID",
|
||||
@ -340,6 +344,7 @@
|
||||
"label.action.cancel.maintenance.mode": "Cancel Maintenance Mode",
|
||||
"label.action.change.password": "Change Password",
|
||||
"label.action.configure.samlauthorization": "Configure SAML SSO Authorization",
|
||||
"label.action.configure.stickiness": "Stickiness",
|
||||
"label.action.copy.ISO": "Copy ISO",
|
||||
"label.action.copy.template": "Copy Template",
|
||||
"label.action.create.volume": "Create Volume",
|
||||
@ -430,6 +435,8 @@
|
||||
"label.add.OpenDaylight.device": "Add OpenDaylight Controller",
|
||||
"label.add.PA.device": "Add Palo Alto device",
|
||||
"label.add.SRX.device": "Add SRX device",
|
||||
"label.add.VM": "Add VM",
|
||||
"label.add.VMs": "Add VMs",
|
||||
"label.add.VM.to.tier": "Add VM to tier",
|
||||
"label.add.account": "Add Account",
|
||||
"label.add.acl.list": "Add ACL List",
|
||||
@ -460,8 +467,10 @@
|
||||
"label.add.primary.storage": "Add Primary Storage",
|
||||
"label.add.region": "Add Region",
|
||||
"label.add.role": "Add Role",
|
||||
"label.add.rule": "Add Rule",
|
||||
"label.add.secondary.storage": "Add Secondary Storage",
|
||||
"label.add.security.group": "Add Security Group",
|
||||
"label.add.setting": "Add Setting",
|
||||
"label.add.system.service.offering": "Add System Service Offering",
|
||||
"label.add.ucs.manager": "Add UCS Manager",
|
||||
"label.add.user": "Add User",
|
||||
@ -840,6 +849,7 @@
|
||||
"secretkey": "Secret Key",
|
||||
"securityGroups": "Security Groups",
|
||||
"securitygroup": "Security Group",
|
||||
"select": "Select",
|
||||
"sent": "Date",
|
||||
"sentbytes": "Bytes Sent",
|
||||
"server": "Server",
|
||||
@ -872,6 +882,7 @@
|
||||
"snmpCommunity": "SNMP Community",
|
||||
"snmpPort": "SNMP Port",
|
||||
"sockettimeout": "Socket Timeout",
|
||||
"sourcecidr": "Source CIDR",
|
||||
"sourceNat": "Source NAT",
|
||||
"sourceipaddress": "Source IP Address",
|
||||
"sourceport": "Source Port",
|
||||
@ -905,6 +916,7 @@
|
||||
"systemvmtype": "System VM Type",
|
||||
"tags": "Tags",
|
||||
"tariffValue": "Tariff Value",
|
||||
"tcp": "TCP",
|
||||
"template": "Select a template",
|
||||
"templateFileUpload": "Local file",
|
||||
"templateLimit": "Template Limits",
|
||||
@ -926,6 +938,7 @@
|
||||
"traffictype": "Traffic Type",
|
||||
"transportzoneuuid": "Transport Zone Uuid",
|
||||
"type": "Type",
|
||||
"udp": "UDP",
|
||||
"unit": "Usage Unit",
|
||||
"url": "URL",
|
||||
"usageName": "Usage Type",
|
||||
@ -964,6 +977,7 @@
|
||||
"vlanRange": "VLAN/VNI Range",
|
||||
"vlanname": "VLAN",
|
||||
"vlanrange": "VLAN/VNI Range",
|
||||
"vm": "VM",
|
||||
"vmLimit": "Instance Limits",
|
||||
"vmTotal": "Instances",
|
||||
"vmdisplayname": "VM display name",
|
||||
|
||||
@ -45,6 +45,6 @@ export const deviceEnquire = function (callback) {
|
||||
// screen and (max-width: 1087.99px)
|
||||
enquireJs
|
||||
.register('screen and (max-width: 576px)', matchMobile)
|
||||
.register('screen and (min-width: 576px) and (max-width: 1366px)', matchTablet)
|
||||
.register('screen and (min-width: 1367px)', matchDesktop)
|
||||
.register('screen and (min-width: 576px) and (max-width: 1280px)', matchTablet)
|
||||
.register('screen and (min-width: 1281px)', matchDesktop)
|
||||
}
|
||||
|
||||
@ -17,24 +17,287 @@
|
||||
|
||||
<template>
|
||||
<div>
|
||||
TODO: Egress view for isolated network
|
||||
<div>
|
||||
<div class="form">
|
||||
<div class="form__item">
|
||||
<div class="form__label">Source CIDR</div>
|
||||
<a-input v-model="newRule.cidrlist"></a-input>
|
||||
</div>
|
||||
<div class="form__item">
|
||||
<div class="form__label">Destination CIDR</div>
|
||||
<a-input v-model="newRule.destcidrlist"></a-input>
|
||||
</div>
|
||||
<div class="form__item">
|
||||
<div class="form__label">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>
|
||||
<a-select-option value="icmp">ICMP</a-select-option>
|
||||
<a-select-option value="all">All</a-select-option>
|
||||
</a-select>
|
||||
</div>
|
||||
<div v-show="newRule.protocol === 'tcp' || newRule.protocol === 'udp'" class="form__item">
|
||||
<div class="form__label">Start Port</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>
|
||||
<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>
|
||||
<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>
|
||||
<a-input v-model="newRule.icmpcode"></a-input>
|
||||
</div>
|
||||
<div class="form__item">
|
||||
<a-button type="primary" icon="plus" @click="addRule">{{ $t('add') }}</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
|
||||
export default {
|
||||
name: '',
|
||||
components: {
|
||||
props: {
|
||||
resource: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
loading: true,
|
||||
egressRules: [],
|
||||
newRule: {
|
||||
protocol: 'tcp',
|
||||
cidrlist: null,
|
||||
destcidrlist: null,
|
||||
networkid: this.resource.id,
|
||||
icmptype: null,
|
||||
icmpcode: null,
|
||||
startport: null,
|
||||
endport: null
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.fetchData()
|
||||
},
|
||||
filters: {
|
||||
capitalise: val => {
|
||||
if (val === 'all') return 'All'
|
||||
return val.toUpperCase()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
resource: function (newItem, oldItem) {
|
||||
if (!newItem || !newItem.id) {
|
||||
return
|
||||
}
|
||||
this.resource = newItem
|
||||
this.fetchData()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
this.loading = true
|
||||
api('listEgressFirewallRules', {
|
||||
listAll: true,
|
||||
networkid: this.resource.id
|
||||
}).then(response => {
|
||||
this.egressRules = response.listegressfirewallrulesresponse.firewallrule
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
deleteRule (rule) {
|
||||
this.loading = true
|
||||
api('deleteEgressFirewallRule', { id: rule.id }).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.deleteegressfirewallruleresponse.jobid,
|
||||
successMessage: `Successfully removed Egress rule`,
|
||||
successMethod: () => this.fetchData(),
|
||||
errorMessage: 'Removing Egress rule failed',
|
||||
errorMethod: () => this.fetchData(),
|
||||
loadingMessage: `Deleting Egress rule...`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => this.fetchData()
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.errorresponse.errortext
|
||||
})
|
||||
this.fetchData()
|
||||
})
|
||||
},
|
||||
addRule () {
|
||||
this.loading = true
|
||||
api('createEgressFirewallRule', { ...this.newRule }).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.createegressfirewallruleresponse.jobid,
|
||||
successMessage: `Successfully added new Egress rule`,
|
||||
successMethod: () => {
|
||||
this.resetAllRules()
|
||||
this.fetchData()
|
||||
},
|
||||
errorMessage: 'Adding new Egress rule failed',
|
||||
errorMethod: () => {
|
||||
this.resetAllRules()
|
||||
this.fetchData()
|
||||
},
|
||||
loadingMessage: `Adding new Egress rule...`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => {
|
||||
this.resetAllRules()
|
||||
this.fetchData()
|
||||
}
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.createegressfirewallruleresponse.errortext
|
||||
})
|
||||
this.resetAllRules()
|
||||
this.fetchData()
|
||||
})
|
||||
},
|
||||
resetAllRules () {
|
||||
this.newRule.protocol = 'tcp'
|
||||
this.newRule.cidrlist = null
|
||||
this.newRule.destcidrlist = null
|
||||
this.newRule.networkid = this.resource.id
|
||||
this.resetRulePorts()
|
||||
},
|
||||
resetRulePorts () {
|
||||
this.newRule.icmptype = null
|
||||
this.newRule.icmpcode = null
|
||||
this.newRule.startport = null
|
||||
this.newRule.endport = null
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style scoped lang="scss">
|
||||
.rule {
|
||||
|
||||
&-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
margin-right: -20px;
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
|
||||
&__item {
|
||||
padding-right: 20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
@media (min-width: 760px) {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
width: 100%;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.add-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-right: -20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
@media (min-width: 760px) {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.form {
|
||||
display: flex;
|
||||
margin-right: -20px;
|
||||
margin-bottom: 20px;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
|
||||
@media (min-width: 760px) {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
padding-right: 20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
@media (min-width: 760px) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
.ant-select {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
476
ui/src/views/network/FirewallRules.vue
Normal file
476
ui/src/views/network/FirewallRules.vue
Normal file
@ -0,0 +1,476 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<div class="form">
|
||||
<div class="form__item">
|
||||
<div class="form__label">{{ $t('sourcecidr') }}</div>
|
||||
<a-input v-model="newRule.cidrlist"></a-input>
|
||||
</div>
|
||||
<div class="form__item">
|
||||
<div class="form__label">{{ $t('protocol') }}</div>
|
||||
<a-select v-model="newRule.protocol" style="width: 100%;" @change="resetRulePorts">
|
||||
<a-select-option value="tcp">{{ $t('tcp') }}</a-select-option>
|
||||
<a-select-option value="udp">{{ $t('udp') }}</a-select-option>
|
||||
<a-select-option value="icmp">{{ $t('icmp') }}</a-select-option>
|
||||
</a-select>
|
||||
</div>
|
||||
<div v-show="newRule.protocol === 'tcp' || newRule.protocol === 'udp'" class="form__item">
|
||||
<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">{{ $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">{{ $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">{{ $t('icmpcode') }}</div>
|
||||
<a-input v-model="newRule.icmpcode"></a-input>
|
||||
</div>
|
||||
<div class="form__item" style="margin-left: auto;">
|
||||
<a-button type="primary" @click="addRule">{{ $t('add') }}</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
|
||||
<a-modal title="Edit Tags" v-model="tagsModalVisible" :footer="null" :afterClose="closeModal">
|
||||
<div class="add-tags">
|
||||
<div class="add-tags__input">
|
||||
<p class="add-tags__label">{{ $t('key') }}</p>
|
||||
<a-input v-model="newTag.key"></a-input>
|
||||
</div>
|
||||
<div class="add-tags__input">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<a-divider></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('done') }}</a-button>
|
||||
</a-modal>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
resource: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
inject: ['parentFetchData', 'parentToggleLoading'],
|
||||
data () {
|
||||
return {
|
||||
loading: true,
|
||||
firewallRules: [],
|
||||
newRule: {
|
||||
protocol: 'tcp',
|
||||
cidrlist: null,
|
||||
ipaddressid: this.resource.id,
|
||||
icmptype: null,
|
||||
icmpcode: null,
|
||||
startport: null,
|
||||
endport: null
|
||||
},
|
||||
tagsModalVisible: false,
|
||||
selectedRule: null,
|
||||
tags: [],
|
||||
newTag: {
|
||||
key: null,
|
||||
value: null
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.fetchData()
|
||||
},
|
||||
filters: {
|
||||
capitalise: val => {
|
||||
if (val === 'all') return 'All'
|
||||
return val.toUpperCase()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
resource: function (newItem, oldItem) {
|
||||
if (!newItem || !newItem.id) {
|
||||
return
|
||||
}
|
||||
this.resource = newItem
|
||||
this.fetchData()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
this.loading = true
|
||||
api('listFirewallRules', {
|
||||
listAll: true,
|
||||
ipaddressid: this.resource.id
|
||||
}).then(response => {
|
||||
this.firewallRules = response.listfirewallrulesresponse.firewallrule
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.errorresponse.errortext
|
||||
})
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
deleteRule (rule) {
|
||||
this.loading = true
|
||||
api('deleteFirewallRule', { id: rule.id }).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.deletefirewallruleresponse.jobid,
|
||||
successMessage: `Successfully removed Firewall rule`,
|
||||
successMethod: () => this.fetchData(),
|
||||
errorMessage: 'Removing Firewall rule failed',
|
||||
errorMethod: () => this.fetchData(),
|
||||
loadingMessage: `Deleting Firewall rule...`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => this.fetchData()
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.errorresponse.errortext
|
||||
})
|
||||
this.fetchData()
|
||||
})
|
||||
},
|
||||
addRule () {
|
||||
this.loading = true
|
||||
api('createFirewallRule', { ...this.newRule }).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.createfirewallruleresponse.jobid,
|
||||
successMessage: `Successfully added new Firewall rule`,
|
||||
successMethod: () => {
|
||||
this.resetAllRules()
|
||||
this.fetchData()
|
||||
},
|
||||
errorMessage: 'Adding new Firewall rule failed',
|
||||
errorMethod: () => {
|
||||
this.resetAllRules()
|
||||
this.fetchData()
|
||||
},
|
||||
loadingMessage: `Adding new Firewall rule...`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => {
|
||||
this.resetAllRules()
|
||||
this.fetchData()
|
||||
}
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.createfirewallruleresponse.errortext
|
||||
})
|
||||
this.resetAllRules()
|
||||
this.fetchData()
|
||||
})
|
||||
},
|
||||
resetAllRules () {
|
||||
this.newRule.protocol = 'tcp'
|
||||
this.newRule.cidrlist = null
|
||||
this.newRule.networkid = this.resource.id
|
||||
this.resetRulePorts()
|
||||
},
|
||||
resetRulePorts () {
|
||||
this.newRule.icmptype = null
|
||||
this.newRule.icmpcode = null
|
||||
this.newRule.startport = null
|
||||
this.newRule.endport = null
|
||||
},
|
||||
closeModal () {
|
||||
this.selectedRule = null
|
||||
this.tagsModalVisible = false
|
||||
this.newTag.key = null
|
||||
this.newTag.value = null
|
||||
},
|
||||
openTagsModal (id) {
|
||||
this.selectedRule = id
|
||||
this.tagsModalVisible = true
|
||||
api('listTags', {
|
||||
resourceId: id,
|
||||
resourceType: 'FirewallRule',
|
||||
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
|
||||
})
|
||||
this.closeModal()
|
||||
})
|
||||
},
|
||||
handleAddTag () {
|
||||
api('createTags', {
|
||||
'tags[0].key': this.newTag.key,
|
||||
'tags[0].value': this.newTag.value,
|
||||
resourceIds: this.selectedRule,
|
||||
resourceType: 'FirewallRule'
|
||||
}).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.createtagsresponse.jobid,
|
||||
successMessage: `Successfully added tag`,
|
||||
successMethod: () => {
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
this.openTagsModal(this.selectedRule)
|
||||
},
|
||||
errorMessage: 'Failed to add new tag',
|
||||
errorMethod: () => {
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
this.closeModal()
|
||||
},
|
||||
loadingMessage: `Adding tag...`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => {
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
this.closeModal()
|
||||
}
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.createtagsresponse.errortext
|
||||
})
|
||||
this.closeModal()
|
||||
})
|
||||
},
|
||||
handleDeleteTag (tag) {
|
||||
api('deleteTags', {
|
||||
'tags[0].key': tag.key,
|
||||
'tags[0].value': tag.value,
|
||||
resourceIds: this.selectedRule,
|
||||
resourceType: 'FirewallRule'
|
||||
}).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.deletetagsresponse.jobid,
|
||||
successMessage: `Successfully removed tag`,
|
||||
successMethod: () => {
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
this.openTagsModal(this.selectedRule)
|
||||
},
|
||||
errorMessage: 'Failed to remove tag',
|
||||
errorMethod: () => {
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
this.closeModal()
|
||||
},
|
||||
loadingMessage: `Removing tag...`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => {
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
this.closeModal()
|
||||
}
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.deletetagsresponse.errortext
|
||||
})
|
||||
this.closeModal()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.rule {
|
||||
|
||||
&-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
margin-right: -20px;
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
|
||||
&__item {
|
||||
padding-right: 20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
@media (min-width: 760px) {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
width: 100%;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.add-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-right: -20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
@media (min-width: 760px) {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.form {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
margin-right: -20px;
|
||||
flex-direction: column;
|
||||
margin-bottom: 20px;
|
||||
|
||||
@media (min-width: 760px) {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/*flex: 1;*/
|
||||
padding-right: 20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
@media (min-width: 760px) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
.ant-select {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.rule-action {
|
||||
margin-bottom: 20px;
|
||||
|
||||
&:not(:last-of-type) {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.tags {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.add-tags {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
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;
|
||||
}
|
||||
|
||||
</style>
|
||||
@ -107,7 +107,7 @@
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
>
|
||||
<a-button shape="round" type="danger" icon="close-circle" class="rule-action" />
|
||||
<a-button shape="round" type="danger" icon="delete" class="rule-action" />
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
</a-list-item>
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
<template>
|
||||
<div>
|
||||
TODO: IP configure view: firewall, pf, lb
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: '',
|
||||
components: {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
1373
ui/src/views/network/LoadBalancing.vue
Normal file
1373
ui/src/views/network/LoadBalancing.vue
Normal file
File diff suppressed because it is too large
Load Diff
675
ui/src/views/network/PortForwarding.vue
Normal file
675
ui/src/views/network/PortForwarding.vue
Normal file
@ -0,0 +1,675 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<div class="form">
|
||||
<div class="form__item">
|
||||
<div class="form__label">{{ $t('privateport') }}</div>
|
||||
<a-input-group class="form__item__input-container" compact>
|
||||
<a-input
|
||||
v-model="newRule.privateport"
|
||||
placeholder="Start"
|
||||
style="border-right: 0; width: 60px; margin-right: 0;"></a-input>
|
||||
<a-input
|
||||
placeholder="-"
|
||||
disabled
|
||||
style="width: 30px; border-left: 0; border-right: 0; pointer-events: none; backgroundColor: #fff; text-align:
|
||||
center; margin-right: 0;"></a-input>
|
||||
<a-input
|
||||
v-model="newRule.privateendport"
|
||||
placeholder="End"
|
||||
style="border-left: 0; width: 60px; text-align: right; margin-right: 0;"></a-input>
|
||||
</a-input-group>
|
||||
</div>
|
||||
<div class="form__item">
|
||||
<div class="form__label">{{ $t('publicport') }}</div>
|
||||
<a-input-group class="form__item__input-container" compact>
|
||||
<a-input
|
||||
v-model="newRule.publicport"
|
||||
placeholder="Start"
|
||||
style="border-right: 0; width: 60px; margin-right: 0;"></a-input>
|
||||
<a-input
|
||||
placeholder="-"
|
||||
disabled
|
||||
style="width: 30px; border-left: 0; border-right: 0; pointer-events: none; backgroundColor: #fff;
|
||||
text-align: center; margin-right: 0;"></a-input>
|
||||
<a-input
|
||||
v-model="newRule.publicendport"
|
||||
placeholder="End"
|
||||
style="border-left: 0; width: 60px; text-align: right; margin-right: 0;"></a-input>
|
||||
</a-input-group>
|
||||
</div>
|
||||
<div class="form__item">
|
||||
<div class="form__label">{{ $t('protocol') }}</div>
|
||||
<a-select v-model="newRule.protocol" style="width: 100%;">
|
||||
<a-select-option value="tcp">{{ $t('tcp') }}</a-select-option>
|
||||
<a-select-option value="udp">{{ $t('udp') }}</a-select-option>
|
||||
</a-select>
|
||||
</div>
|
||||
<div class="form__item" style="margin-left: auto;">
|
||||
<div class="form__label">{{ $t('label.add.VM') }}</div>
|
||||
<a-button type="primary" @click="openAddVMModal">{{ $t('add') }}</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
|
||||
<a-modal title="Edit Tags" v-model="tagsModalVisible" :footer="null" :afterClose="closeModal">
|
||||
<span v-show="tagsModalLoading" class="tags-modal-loading">
|
||||
<a-icon type="loading"></a-icon>
|
||||
</span>
|
||||
|
||||
<div class="add-tags">
|
||||
<div class="add-tags__input">
|
||||
<p class="add-tags__label">{{ $t('key') }}</p>
|
||||
<a-input v-model="newTag.key"></a-input>
|
||||
</div>
|
||||
<div class="add-tags__input">
|
||||
<p class="add-tags__label">{{ $t('value') }}</p>
|
||||
<a-input v-model="newTag.value"></a-input>
|
||||
</div>
|
||||
<a-button type="primary" @click="() => handleAddTag()">{{ $t('label.add') }}</a-button>
|
||||
</div>
|
||||
|
||||
<a-divider></a-divider>
|
||||
|
||||
<div v-show="!tagsModalLoading" 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('done') }}</a-button>
|
||||
</a-modal>
|
||||
|
||||
<a-modal
|
||||
title="Add VM"
|
||||
v-model="addVmModalVisible"
|
||||
class="vm-modal"
|
||||
width="60vw"
|
||||
@ok="addRule"
|
||||
:okButtonProps="{ props:
|
||||
{disabled: newRule.virtualmachineid === null } }"
|
||||
@cancel="closeModal"
|
||||
>
|
||||
|
||||
<a-icon v-if="addVmModalLoading" type="loading"></a-icon>
|
||||
|
||||
<div v-else>
|
||||
<div class="vm-modal__header">
|
||||
<span style="min-width: 200px;">{{ $t('name') }}</span>
|
||||
<span>{{ $t('instancename') }}</span>
|
||||
<span>{{ $t('displayname') }}</span>
|
||||
<span>{{ $t('ip') }}</span>
|
||||
<span>{{ $t('account') }}</span>
|
||||
<span>{{ $t('zone') }}</span>
|
||||
<span>{{ $t('state') }}</span>
|
||||
<span>{{ $t('select') }}</span>
|
||||
</div>
|
||||
|
||||
<a-radio-group v-model="newRule.virtualmachineid" style="width: 100%;" @change="fetchNics">
|
||||
<div v-for="(vm, index) in vms" :key="index" class="vm-modal__item">
|
||||
|
||||
<span style="min-width: 200px;">
|
||||
<span>
|
||||
{{ vm.name }}
|
||||
</span>
|
||||
<a-icon v-if="addVmModalNicLoading" type="loading"></a-icon>
|
||||
<a-select
|
||||
v-else-if="!addVmModalNicLoading && newRule.virtualmachineid === vm.id"
|
||||
v-model="newRule.vmguestip">
|
||||
<a-select-option v-for="(nic, nicIndex) in nics" :key="nic" :value="nic">
|
||||
{{ nic }}{{ nicIndex === 0 ? ' (Primary)' : null }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</span>
|
||||
<span>{{ vm.instancename }}</span>
|
||||
<span>{{ vm.displayname }}</span>
|
||||
<span></span>
|
||||
<span>{{ vm.account }}</span>
|
||||
<span>{{ vm.zonename }}</span>
|
||||
<span>{{ vm.state }}</span>
|
||||
<a-radio :value="vm.id" />
|
||||
</div>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
|
||||
</a-modal>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
resource: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
inject: ['parentFetchData', 'parentToggleLoading'],
|
||||
data () {
|
||||
return {
|
||||
loading: true,
|
||||
portForwardRules: [],
|
||||
newRule: {
|
||||
protocol: 'tcp',
|
||||
privateport: null,
|
||||
privateendport: null,
|
||||
publicport: null,
|
||||
publicendport: null,
|
||||
openfirewall: false,
|
||||
vmguestip: null,
|
||||
virtualmachineid: null
|
||||
},
|
||||
tagsModalVisible: false,
|
||||
selectedRule: null,
|
||||
tags: [],
|
||||
newTag: {
|
||||
key: null,
|
||||
value: null
|
||||
},
|
||||
tagsModalLoading: false,
|
||||
addVmModalVisible: false,
|
||||
addVmModalLoading: false,
|
||||
addVmModalNicLoading: false,
|
||||
vms: [],
|
||||
nics: []
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.fetchData()
|
||||
},
|
||||
watch: {
|
||||
resource: function (newItem, oldItem) {
|
||||
if (!newItem || !newItem.id) {
|
||||
return
|
||||
}
|
||||
this.resource = newItem
|
||||
this.fetchData()
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
capitalise: val => {
|
||||
if (val === 'all') return 'All'
|
||||
return val.toUpperCase()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
this.loading = true
|
||||
api('listPortForwardingRules', {
|
||||
listAll: true,
|
||||
ipaddressid: this.resource.id
|
||||
}).then(response => {
|
||||
this.portForwardRules = response.listportforwardingrulesresponse.portforwardingrule
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.errorresponse.errortext
|
||||
})
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
deleteRule (rule) {
|
||||
this.loading = true
|
||||
api('deletePortForwardingRule', { id: rule.id }).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.deleteportforwardingruleresponse.jobid,
|
||||
successMessage: `Successfully removed Port Forwarding rule`,
|
||||
successMethod: () => this.fetchData(),
|
||||
errorMessage: 'Removing Port Forwarding rule failed',
|
||||
errorMethod: () => this.fetchData(),
|
||||
loadingMessage: `Deleting Port Forwarding rule...`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => this.fetchData()
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.errorresponse.errortext
|
||||
})
|
||||
this.fetchData()
|
||||
})
|
||||
},
|
||||
addRule () {
|
||||
this.loading = true
|
||||
this.addVmModalVisible = false
|
||||
api('createPortForwardingRule', {
|
||||
...this.newRule,
|
||||
ipaddressid: this.resource.id,
|
||||
networkid: this.resource.associatednetworkid
|
||||
}).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.createportforwardingruleresponse.jobid,
|
||||
successMessage: `Successfully added new Port Forwarding rule`,
|
||||
successMethod: () => {
|
||||
this.closeModal()
|
||||
this.fetchData()
|
||||
},
|
||||
errorMessage: 'Adding new Port Forwarding rule failed',
|
||||
errorMethod: () => {
|
||||
this.closeModal()
|
||||
this.fetchData()
|
||||
},
|
||||
loadingMessage: `Adding new Port Forwarding rule...`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => {
|
||||
this.closeModal()
|
||||
this.fetchData()
|
||||
}
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.createportforwardingruleresponse.errortext
|
||||
})
|
||||
this.closeModal()
|
||||
this.fetchData()
|
||||
})
|
||||
},
|
||||
resetAllRules () {
|
||||
this.newRule.protocol = 'tcp'
|
||||
this.newRule.privateport = null
|
||||
this.newRule.privateendport = null
|
||||
this.newRule.publicport = null
|
||||
this.newRule.publicendport = null
|
||||
this.newRule.openfirewall = false
|
||||
this.newRule.vmguestip = null
|
||||
this.newRule.virtualmachineid = null
|
||||
},
|
||||
resetTagInputs () {
|
||||
this.newTag.key = null
|
||||
this.newTag.value = null
|
||||
},
|
||||
closeModal () {
|
||||
this.selectedRule = null
|
||||
this.tagsModalVisible = false
|
||||
this.addVmModalVisible = false
|
||||
this.newRule.virtualmachineid = null
|
||||
this.addVmModalLoading = false
|
||||
this.addVmModalNicLoading = false
|
||||
this.nics = []
|
||||
this.resetTagInputs()
|
||||
this.resetAllRules()
|
||||
},
|
||||
openTagsModal (id) {
|
||||
this.tagsModalLoading = true
|
||||
this.selectedRule = id
|
||||
this.tagsModalVisible = true
|
||||
this.tags = []
|
||||
this.resetTagInputs()
|
||||
api('listTags', {
|
||||
resourceId: id,
|
||||
resourceType: 'PortForwardingRule',
|
||||
listAll: true
|
||||
}).then(response => {
|
||||
this.tags = response.listtagsresponse.tag
|
||||
this.tagsModalLoading = false
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.errorresponse.errortext
|
||||
})
|
||||
this.closeModal()
|
||||
})
|
||||
},
|
||||
handleAddTag () {
|
||||
this.tagsModalLoading = true
|
||||
api('createTags', {
|
||||
'tags[0].key': this.newTag.key,
|
||||
'tags[0].value': this.newTag.value,
|
||||
resourceIds: this.selectedRule,
|
||||
resourceType: 'PortForwardingRule'
|
||||
}).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.createtagsresponse.jobid,
|
||||
successMessage: `Successfully added tag`,
|
||||
successMethod: () => {
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
this.openTagsModal(this.selectedRule)
|
||||
},
|
||||
errorMessage: 'Failed to add new tag',
|
||||
errorMethod: () => {
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
this.closeModal()
|
||||
},
|
||||
loadingMessage: `Adding tag...`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => {
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
this.closeModal()
|
||||
}
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.createtagsresponse.errortext
|
||||
})
|
||||
this.closeModal()
|
||||
})
|
||||
},
|
||||
handleDeleteTag (tag) {
|
||||
this.tagsModalLoading = true
|
||||
api('deleteTags', {
|
||||
'tags[0].key': tag.key,
|
||||
'tags[0].value': tag.value,
|
||||
resourceIds: this.selectedRule,
|
||||
resourceType: 'PortForwardingRule'
|
||||
}).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.deletetagsresponse.jobid,
|
||||
successMessage: `Successfully removed tag`,
|
||||
successMethod: () => {
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
this.openTagsModal(this.selectedRule)
|
||||
},
|
||||
errorMessage: 'Failed to remove tag',
|
||||
errorMethod: () => {
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
this.closeModal()
|
||||
},
|
||||
loadingMessage: `Removing tag...`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => {
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
this.closeModal()
|
||||
}
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.deletetagsresponse.errortext
|
||||
})
|
||||
this.closeModal()
|
||||
})
|
||||
},
|
||||
openAddVMModal () {
|
||||
this.addVmModalVisible = true
|
||||
this.addVmModalLoading = true
|
||||
api('listVirtualMachines', {
|
||||
listAll: true,
|
||||
page: 1,
|
||||
pagesize: 500,
|
||||
networkid: this.resource.associatednetworkid,
|
||||
account: this.resource.account,
|
||||
domainid: this.resource.domainid
|
||||
}).then(response => {
|
||||
this.vms = response.listvirtualmachinesresponse.virtualmachine
|
||||
this.addVmModalLoading = false
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.errorresponse.errortext
|
||||
})
|
||||
this.closeModal()
|
||||
})
|
||||
},
|
||||
fetchNics (e) {
|
||||
this.addVmModalNicLoading = true
|
||||
api('listNics', {
|
||||
virtualmachineid: e.target.value,
|
||||
networkid: this.resource.associatednetworkid
|
||||
}).then(response => {
|
||||
if (!response.listnicsresponse.nic || response.listnicsresponse.nic.length < 1) return
|
||||
const nic = response.listnicsresponse.nic[0]
|
||||
this.nics.push(nic.ipaddress)
|
||||
if (nic.secondaryip && nic.secondaryip.length > 0) {
|
||||
this.nics.push(...nic.secondaryip.map(ip => ip.ipaddress))
|
||||
}
|
||||
this.newRule.vmguestip = this.nics[0]
|
||||
this.addVmModalNicLoading = false
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.errorresponse.errortext
|
||||
})
|
||||
this.closeModal()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.rule {
|
||||
|
||||
&-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
margin-right: -20px;
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
|
||||
&__item {
|
||||
padding-right: 20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
@media (min-width: 760px) {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
width: 100%;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.add-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-right: -20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
@media (min-width: 760px) {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.form {
|
||||
display: flex;
|
||||
margin-right: -20px;
|
||||
margin-bottom: 20px;
|
||||
flex-direction: column;
|
||||
|
||||
@media (min-width: 760px) {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/*flex: 1;*/
|
||||
padding-right: 20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
@media (min-width: 760px) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
input,
|
||||
.ant-select {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
&__input-container {
|
||||
display: flex;
|
||||
|
||||
input {
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.rule-action {
|
||||
margin-bottom: 20px;
|
||||
|
||||
&:not(:last-of-type) {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.tags {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.add-tags {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
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;
|
||||
}
|
||||
|
||||
.tags-modal-loading {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
z-index: 1;
|
||||
color: #1890ff;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.vm-modal {
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
|
||||
span {
|
||||
flex: 1;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
margin-top: 10px;
|
||||
|
||||
span,
|
||||
label {
|
||||
display: block;
|
||||
flex: 1;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
@ -16,25 +16,190 @@
|
||||
// under the License.
|
||||
|
||||
<template>
|
||||
<div>
|
||||
TODO: VPN configure/detail tab view
|
||||
<div v-if="remoteAccessVpn">
|
||||
<div>
|
||||
<p>Your Remote Access VPN is currently enabled and can be accessed via the IP <strong>{{ remoteAccessVpn.publicip }}</strong></p>
|
||||
<p>Your IPSec pre-shared key is <strong>{{ remoteAccessVpn.presharedkey }}</strong></p>
|
||||
<a-divider/>
|
||||
<a-button><router-link :to="{ path: '/vpnuser'}">Manage VPN Users</router-link></a-button>
|
||||
<a-button style="margin-left: 10px" type="danger" @click="disableVpn = true">Disable VPN</a-button>
|
||||
</div>
|
||||
|
||||
<a-modal v-model="disableVpn" :footer="null" oncancel="disableVpn = false" title="Disable Remove Access VPN">
|
||||
<p>Are you sure you want to disable VPN?</p>
|
||||
|
||||
<a-divider></a-divider>
|
||||
|
||||
<div class="actions">
|
||||
<a-button @click="() => disableVpn = false">Cancel</a-button>
|
||||
<a-button type="primary" @click="handleDisableVpn">Yes</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
</div>
|
||||
<div v-else>
|
||||
<a-button type="primary" @click="enableVpn = true">Enable VPN</a-button>
|
||||
|
||||
<a-modal v-model="enableVpn" :footer="null" onCancel="enableVpn = false" title="Enable Remote Access VPN">
|
||||
<p>Please confirm that you want Remote Access VPN enabled for this IP address.</p>
|
||||
|
||||
<a-divider></a-divider>
|
||||
|
||||
<div class="actions">
|
||||
<a-button @click="() => enableVpn = false">Cancel</a-button>
|
||||
<a-button type="primary" @click="handleCreateVpn">Yes</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
|
||||
export default {
|
||||
name: '',
|
||||
components: {
|
||||
props: {
|
||||
resource: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
remoteAccessVpn: null,
|
||||
enableVpn: false,
|
||||
disableVpn: false
|
||||
}
|
||||
},
|
||||
inject: ['parentFetchData', 'parentToggleLoading'],
|
||||
mounted () {
|
||||
this.fetchData()
|
||||
},
|
||||
watch: {
|
||||
resource: function (newItem, oldItem) {
|
||||
if (!newItem || !newItem.id) {
|
||||
return
|
||||
}
|
||||
this.resource = newItem
|
||||
this.fetchData()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
api('listRemoteAccessVpns', {
|
||||
publicipid: this.resource.id,
|
||||
listAll: true
|
||||
}).then(response => {
|
||||
this.remoteAccessVpn = response.listremoteaccessvpnsresponse.remoteaccessvpn
|
||||
? response.listremoteaccessvpnsresponse.remoteaccessvpn[0] : null
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.errorresponse.errortext
|
||||
})
|
||||
})
|
||||
},
|
||||
handleCreateVpn () {
|
||||
this.parentToggleLoading()
|
||||
this.enableVpn = false
|
||||
api('createRemoteAccessVpn', {
|
||||
publicipid: this.resource.id,
|
||||
domainid: this.resource.domainid,
|
||||
account: this.resource.account
|
||||
}).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.createremoteaccessvpnresponse.jobid,
|
||||
successMethod: result => {
|
||||
const res = result.jobresult.remoteaccessvpn
|
||||
this.$notification.success({
|
||||
message: 'Status',
|
||||
description:
|
||||
`Your Remote Access VPN is currently enabled and can be accessed via the IP ${res.publicip}. Your IPSec pre-shared key is ${res.presharedkey}`,
|
||||
duration: 0
|
||||
})
|
||||
this.fetchData()
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
},
|
||||
errorMessage: 'Failed to enable VPN',
|
||||
errorMethod: () => {
|
||||
this.fetchData()
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
},
|
||||
loadingMessage: `Enabling VPN...`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => {
|
||||
this.fetchData()
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
}
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.createremoteaccessvpnresponse
|
||||
? error.response.data.createremoteaccessvpnresponse.errortext : error.response.data.errorresponse.errortext
|
||||
})
|
||||
this.fetchData()
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
})
|
||||
},
|
||||
handleDisableVpn () {
|
||||
this.parentToggleLoading()
|
||||
this.disableVpn = false
|
||||
api('deleteRemoteAccessVpn', {
|
||||
publicipid: this.resource.id,
|
||||
domainid: this.resource.domainid
|
||||
}).then(response => {
|
||||
this.$pollJob({
|
||||
jobId: response.deleteremoteaccessvpnresponse.jobid,
|
||||
successMessage: 'Successfully disabled VPN',
|
||||
successMethod: () => {
|
||||
this.fetchData()
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
},
|
||||
errorMessage: 'Failed to disable VPN',
|
||||
errorMethod: () => {
|
||||
this.fetchData()
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
},
|
||||
loadingMessage: `Disabling VPN...`,
|
||||
catchMessage: 'Error encountered while fetching async job result',
|
||||
catchMethod: () => {
|
||||
this.fetchData()
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
}
|
||||
})
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: `Error ${error.response.status}`,
|
||||
description: error.response.data.deleteremoteaccessvpnresponse
|
||||
? error.response.data.deleteremoteaccessvpnresponse.errortext : error.response.data.errorresponse.errortext
|
||||
})
|
||||
this.fetchData()
|
||||
this.parentFetchData()
|
||||
this.parentToggleLoading()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style scoped lang="scss">
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
button {
|
||||
&:not(:last-child) {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user