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 } }) }
+        }
+      ]
     }
   ]
 }

Reply via email to