This is an automated email from the ASF dual-hosted git repository. weizhou pushed a commit to branch 4.20 in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.20 by this push: new d1df418c3c9 UI: Restore AS Numbers and IPv4 Subnets menus (#10580) d1df418c3c9 is described below commit d1df418c3c94c6ae8d1ff80fa2447968175d2ba4 Author: Nicolas Vazquez <nicovazque...@gmail.com> AuthorDate: Fri Apr 11 09:27:32 2025 -0300 UI: Restore AS Numbers and IPv4 Subnets menus (#10580) * UI: Restore AS Numbers menu * Restore IPv4 Subnets menu * Add missing details for network and VPC details and tabs for Isolated networks * Address latest review comments --- ui/src/config/section/network.js | 109 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 101 insertions(+), 8 deletions(-) diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js index fc0a1f7dee6..4e58c7a4cd8 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -48,7 +48,7 @@ export default { return fields }, details: () => { - var fields = ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'broadcasturi', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'aclname', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'egressdefaultpolicy', 'zonename', 'account', 'domainpath', 'associatednetwork', 'associatednetworkid', 'ip6firewall', 'ip6routing', 'ip6routes', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu', 'privatemtu'] + var fields = ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'broadcasturi', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'asnumber', 'aclname', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'egressdefaultpolicy', 'zonename', 'account', 'domainpath', 'associatednetwork', 'associatednetworkid', 'ip4routing', 'ip6firewall', 'ip6routing', 'ip6routes', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu', 'privatemtu'] if (!isAdmin()) { fields = fields.filter(function (e) { return e !== 'broadcasturi' }) } @@ -68,6 +68,14 @@ export default { name: 'egress.rules', component: shallowRef(defineAsyncComponent(() => import('@/views/network/EgressRulesTab.vue'))), show: (record, route, user) => { return record.type === 'Isolated' && !('vpcname' in record) && 'listEgressFirewallRules' in store.getters.apis && (['Admin', 'DomainAdmin'].includes(user.roletype) || record.account === user.account || record.projectid) } + }, { + name: 'bgp.peers', + component: shallowRef(defineAsyncComponent(() => import('@/views/infra/zone/BgpPeersTab.vue'))), + show: (record, route, user) => { return !record.vpcid && ['Admin'].includes(user.roletype) && record.ip4routing === 'Dynamic' } + }, { + name: 'routing.firewall', + component: shallowRef(defineAsyncComponent(() => import('@/views/network/RoutingFirewallRulesTab.vue'))), + show: (record, route, user) => { return record.type === 'Isolated' && record.ip4routing && !('vpcname' in record) && 'listRoutingFirewallRules' in store.getters.apis && (['Admin', 'DomainAdmin'].includes(user.roletype) || record.account === user.account || record.projectid) } }, { name: 'ip.v6.firewall', component: shallowRef(defineAsyncComponent(() => import('@/views/network/Ipv6FirewallRulesTab.vue'))), @@ -75,7 +83,7 @@ export default { }, { name: (record) => { return record.type === 'Shared' ? 'ip.addresses' : 'public.ip.addresses' }, component: shallowRef(defineAsyncComponent(() => import('@/views/network/IpAddressesTab.vue'))), - show: (record, route, user) => { return 'listPublicIpAddresses' in store.getters.apis && (record.type === 'Shared' || (record.type === 'Isolated' && !('vpcname' in record) && (['Admin', 'DomainAdmin'].includes(user.roletype) || record.account === user.account || record.projectid))) } + show: (record, route, user) => { return 'listPublicIpAddresses' in store.getters.apis && (record.type === 'Shared' || (record.type === 'Isolated' && !record.ip4routing && !('vpcname' in record) && (['Admin', 'DomainAdmin'].includes(user.roletype) || record.account === user.account || record.projectid))) } }, { name: 'virtual.routers', component: shallowRef(defineAsyncComponent(() => import('@/views/network/RoutersTab.vue'))), @@ -221,8 +229,8 @@ export default { fields.push(...['domain', 'zonename']) return fields }, - details: ['name', 'id', 'displaytext', 'cidr', 'networkdomain', 'ip6routes', 'ispersistent', 'redundantvpcrouter', 'restartrequired', 'zonename', 'account', 'domain', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu'], - searchFilters: ['name', 'zoneid', 'domainid', 'account', 'tags'], + details: ['name', 'id', 'displaytext', 'cidr', 'networkdomain', 'ip4routing', 'ip4routes', 'ip6routes', 'ispersistent', 'redundantvpcrouter', 'restartrequired', 'zonename', 'account', 'domain', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'publicmtu'], + searchFilters: ['name', 'zoneid', 'domainid', 'account', 'restartrequired', 'tags'], related: [{ name: 'vm', title: 'label.instances', @@ -312,10 +320,7 @@ export default { return false } const listZoneHaveSGEnabled = store.getters.zones.filter(zone => zone.securitygroupsenabled === true) - if (!listZoneHaveSGEnabled || listZoneHaveSGEnabled.length === 0) { - return false - } - return true + return (listZoneHaveSGEnabled && listZoneHaveSGEnabled.length > 0) || store.getters.showSecurityGroups }, actions: [ { @@ -873,6 +878,54 @@ export default { } ] }, + { + name: 'asnumbers', + title: 'label.asnumbers', + icon: 'partition-outlined', + permission: ['listASNumbers'], + show: () => { + if (!store.getters.zones || store.getters.zones.length === 0) { + return false + } + const AdvancedZonesWithRoutedmode = store.getters.zones.filter(zone => zone.routedmodeenabled) + if (isAdmin() && (AdvancedZonesWithRoutedmode && AdvancedZonesWithRoutedmode.length > 0)) { + return true + } + return false + }, + filters: ['all', 'allocatedonly', 'free'], + columns: ['asnumber', 'allocationstate', 'asnrange', 'associatednetworkname', 'vpcname', 'allocated', 'account', 'domain', 'zonename'], + searchFilters: ['zoneid', 'associatednetworkid', 'account', 'domainid'], + resourceType: 'ASNumber', + actions: [ + { + api: 'releaseASNumber', + icon: 'delete-outlined', + label: 'label.action.release.asnumber', + message: 'message.action.release.asnumber', + show: (record) => { return record.allocationstate === 'Allocated' }, + args: ['zoneid', 'asnumber'], + mapping: { + zoneid: { + value: (record) => { return record.zoneid } + }, + asnumber: { + value: (record) => { return record.asnumber } + } + }, + dataView: true, + groupAction: true, + popup: true, + groupShow: (selectedItems, storegetters) => { + return selectedItems.length === 1 && selectedItems[0].allocationstate === 'Allocated' + }, + groupMap: (selectedId, values, records) => { + const record = records.filter(x => { return x.id === selectedId[0] }) + return record + } + } + ] + }, { name: 'privategw', title: 'label.private.gateway', @@ -1397,6 +1450,46 @@ export default { show: (record) => { return (record.allocationstate === 'Allocated') } }], show: isZoneCreated + }, + { + name: 'ipv4subnets', + title: 'label.ipv4.subnets', + icon: 'pic-center-outlined', + permission: ['listIpv4SubnetsForGuestNetwork'], + columns: ['subnet', 'zonename', 'parentsubnet', 'networkname', 'vpcname', 'created', 'allocated'], + details: ['subnet', 'zonename', 'zoneid', 'parentsubnet', 'networkname', 'networkid', 'vpcname', 'vpcid', 'created', 'allocated', 'state'], + searchFilters: ['zoneid'], + show: () => { + if (!store.getters.zones || store.getters.zones.length === 0) { + return false + } + const AdvancedZonesWithRoutedmode = store.getters.zones.filter(zone => zone.routedmodeenabled) + if (isAdmin() && (AdvancedZonesWithRoutedmode && AdvancedZonesWithRoutedmode.length > 0)) { + return true + } + return false + }, + actions: [ + { + api: 'createIpv4SubnetForGuestNetwork', + icon: 'plus-outlined', + label: 'label.add.ipv4.subnet', + listView: true, + popup: true, + component: shallowRef(defineAsyncComponent(() => import('@/views/network/CreateIpv4SubnetForNetwork.vue'))) + }, + { + api: 'deleteIpv4SubnetForGuestNetwork', + icon: 'delete-outlined', + label: 'label.delete.ipv4.subnet', + message: 'message.action.delete.ipv4.subnet', + dataView: true, + show: (record) => { return !record.networkid }, + groupAction: true, + popup: true, + groupMap: (selection) => { return selection.map(x => { return { id: x } }) } + } + ] } ] }