77// This is a Cloud Driver Example for PoC Test.
88//
99// by ETRI, 2022.10.
10+ // Updated by ETRI, 2025.11.
1011
1112package resources
1213
1314import (
1415 "fmt"
16+ "net"
1517 "strconv"
1618 "strings"
1719 "time"
@@ -39,11 +41,11 @@ type NcpVpcNLBHandler struct {
3941
4042const (
4143 // NCP VPC Cloud LB type code : 'APPLICATION' | 'NETWORK' | 'NETWORK_PROXY'
42- NcpLbType string = "NETWORK"
44+ NcpNetworkLbType string = "NETWORK"
4345
4446 // NCP VPC Cloud NLB network type code : 'PUBLIC' | 'PRIVATE' (Default: 'PUBLIC')
45- NcpPublicNlBType string = "PUBLIC"
46- NcpInternalNlBType string = "PRIVATE"
47+ NcpPublicLbType string = "PUBLIC"
48+ NcpInternalLbType string = "PRIVATE"
4749
4850 // NCP LB performance(throughput) type code : 'SMALL' | 'MEDIUM' | 'LARGE' (Default: 'SMALL')
4951 // You can only select 'SMALL' if the LB type is 'NETWORK' and the LB network type is 'PRIVATE'.
@@ -54,7 +56,6 @@ const (
5456 DefaultHealthCheckerInterval int32 = 30 // Min: 5, Max: 300 (seconds). Default: 30 seconds
5557 DefaultHealthCheckerThreshold int32 = 2 // Min: 2, Max: 10. Default: 2
5658
57- LbTypeSubnetDefaultCidr string = ".240/28"
5859 LbTypeSubnetDefaultName string = "ncp-subnet-for-nlb"
5960)
6061
@@ -63,8 +64,7 @@ func init() {
6364 cblogger = cblog .GetLogger ("NCP VPC NLBHandler" )
6465}
6566
66- // Note : Cloud-Barista supports only this case => [ LB : Listener : VMGroup : Health Checker = 1 : 1 : 1 : 1 ]
67- // ------ NLB Management
67+ // Note) Cloud-Barista supports only this case => [ LB : Listener : VMGroup : Health Checker = 1 : 1 : 1 : 1 ]
6868func (nlbHandler * NcpVpcNLBHandler ) CreateNLB (nlbReqInfo irs.NLBInfo ) (createNLB irs.NLBInfo , newErr error ) {
6969 cblogger .Info ("NPC VPC Cloud Driver: called CreateNLB()" )
7070 InitLog ()
@@ -77,10 +77,10 @@ func (nlbHandler *NcpVpcNLBHandler) CreateNLB(nlbReqInfo irs.NLBInfo) (createNLB
7777 return irs.NLBInfo {}, newErr
7878 }
7979
80- // Note!! : NCP VPC LB type code : 'APPLICATION' | 'NETWORK' | 'NETWORK_PROXY'
81- lbType := NcpLbType
80+ // Note) NCP VPC LB type code : 'APPLICATION' | 'NETWORK' | 'NETWORK_PROXY'
81+ networkTypeLb := NcpNetworkLbType
8282
83- // Note!! : Only valid if NcpLbType is Not a 'NETWORK' type.
83+ // Note) Only valid if LB Type is Not a 'NETWORK' type.
8484 // Min : 1, Max : 3600 sec(Dedicated LB : 1 ~ 480000). Default : 60 sec
8585 timeOut := int32 (nlbReqInfo .HealthChecker .Timeout )
8686 if timeOut == - 1 {
@@ -90,12 +90,12 @@ func (nlbHandler *NcpVpcNLBHandler) CreateNLB(nlbReqInfo irs.NLBInfo) (createNLB
9090 return irs.NLBInfo {}, fmt .Errorf ("Invalid Timeout value. Must be a number between 1 and 3600." ) // According to the NCP VPC API document.
9191 }
9292
93- // Note!! : NCP VPC LB network type code : 'PUBLIC' | 'PRIVATE' (Default: 'PUBLIC')
93+ // Note) NCP VPC LB network type code : 'PUBLIC' | 'PRIVATE' (Default: 'PUBLIC')
9494 var lbNetType string
9595 if strings .EqualFold (nlbReqInfo .Type , "PUBLIC" ) || strings .EqualFold (nlbReqInfo .Type , "default" ) || strings .EqualFold (nlbReqInfo .Type , "" ) {
96- lbNetType = NcpPublicNlBType
96+ lbNetType = NcpPublicLbType
9797 } else if strings .EqualFold (nlbReqInfo .Type , "INTERNAL" ) {
98- lbNetType = NcpInternalNlBType
98+ lbNetType = NcpInternalLbType
9999 }
100100
101101 ncpVPCInfo , err := nlbHandler .getNcpVpcInfoWithName (nlbReqInfo .VpcIID .NameId )
@@ -106,7 +106,7 @@ func (nlbHandler *NcpVpcNLBHandler) CreateNLB(nlbReqInfo irs.NLBInfo) (createNLB
106106 return irs.NLBInfo {}, newErr
107107 }
108108
109- // Caution!! : ### Need a Subnet for 'LB Only'('LB Type' Subnet)
109+ // Caution!! : ### Need a Subnet for 'LB Only'('LB Type' Subnet) to create a LB
110110 lbTypeSubnetId , err := nlbHandler .getSubnetIdForNlbOnly (* ncpVPCInfo .VpcNo )
111111 if err != nil {
112112 newErr := fmt .Errorf ("Failed to Get the SubnetId of LB Type subnet : [%v]" , err )
@@ -116,64 +116,68 @@ func (nlbHandler *NcpVpcNLBHandler) CreateNLB(nlbReqInfo irs.NLBInfo) (createNLB
116116 }
117117
118118 cblogger .Infof ("### ncpVPCInfo.Ipv4CidrBlock : [%s]" , * ncpVPCInfo .Ipv4CidrBlock )
119- // VPC IP address ranges : /16~/28 in private IP range (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
120- cidrBlock := strings .Split (* ncpVPCInfo .Ipv4CidrBlock , "." )
121- cidrForNlbSubnet := cidrBlock [0 ] + "." + cidrBlock [1 ] + "." + cidrBlock [2 ] + LbTypeSubnetDefaultCidr
122- // Ex) In case, VpcCIDR : "10.0.0.0/16",
123- // Subnet for VM : "10.0.0.0/28"
124- // LB Type Subnet : "10.0.0.240/28"
125-
126- // ### In case, there is No Subnet for 'LB Only'('LB Type' subnet), Create the 'LB Type' subnet.
119+ // Note) VPC IP address ranges : /16~/28 in private IP range (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
120+
121+ // If LB Type subnet doesn't exist, create it
127122 if strings .EqualFold (lbTypeSubnetId , "" ) {
128- cblogger .Info ("# There is No Subnet for 'LB Only'('LB Type' Subnet), so it will be Created." )
129- // NCP VPC Subnet Name Max length : 30
123+ cblogger .Info ("LB Type subnet does not exist. Creating new subnet with conflict-free CIDR..." )
124+
125+ // Find available CIDR for NLB subnet
126+ availableCIDR , err := nlbHandler .findAvailableCIDRForNLBSubnet (* ncpVPCInfo .VpcNo , * ncpVPCInfo .Ipv4CidrBlock )
127+ if err != nil {
128+ newErr := fmt .Errorf ("Failed to find available CIDR for NLB subnet: [%v]" , err )
129+ cblogger .Error (newErr .Error ())
130+ LoggingError (callLogInfo , newErr )
131+ return irs.NLBInfo {}, newErr
132+ }
133+
134+ // Note) NCP VPC Subnet Name Max length : 30
130135 // LbTypeSubnetDefaultName : "ncp-subnet-for-nlb" => length : 21
131136 lbTypeSubnetName := LbTypeSubnetDefaultName + "-" + randSeq (8 )
132- cblogger .Infof ("### Subnet Name for LB Type subnet : [%s]" , lbTypeSubnetName )
137+ cblogger .Infof ("### Creating LB Type subnet [%s] with CIDR: %s " , lbTypeSubnetName , availableCIDR )
133138
134139 subnetReqInfo := irs.SubnetInfo {
135140 IId : irs.IID {
136141 NameId : lbTypeSubnetName ,
137142 },
138- IPv4_CIDR : cidrForNlbSubnet ,
143+ IPv4_CIDR : availableCIDR ,
139144 }
140- // Note : Create a 'LOADB' type of subnet ('LB Type' subnet for LB Only)
145+
146+ // Create a 'LOADB' type of subnet ('LB Type' subnet for LB Only)
141147 ncpNlbSubnetInfo , err := nlbHandler .creatNcpSubnetForNlbOnly (irs.IID {SystemId : * ncpVPCInfo .VpcNo }, subnetReqInfo ) // Waitting time Included
142148 if err != nil {
143149 newErr := fmt .Errorf ("Failed to Create the 'LB Type' Subnet : [%v]" , err )
144150 cblogger .Error (newErr .Error ())
145151 LoggingError (callLogInfo , newErr )
146152 return irs.NLBInfo {}, newErr
147153 }
148- cblogger .Infof ("' LB type' Subnet ID : [%s]" , * ncpNlbSubnetInfo .SubnetNo )
154+ cblogger .Infof ("Successfully created LB Type subnet ID: [%s]" , * ncpNlbSubnetInfo .SubnetNo )
149155 lbTypeSubnetId = * ncpNlbSubnetInfo .SubnetNo
150156 }
151157
152158 // To Get Subnet No list
153159 subnetNoList := []* string {ncloud .String (lbTypeSubnetId )}
154- // cblogger.Infof("### ID list of 'LB Type' Subnet : ")
155- // spew.Dump(subnetNoList)
156160
157- // Note!! : SubnetNoList[] : Range constraints: Minimum range of 1. Maximum range of 2.
161+ // Note) SubnetNoList[] : Range constraints: Minimum range of 1. Maximum range of 2.
158162 if len (subnetNoList ) < 1 || len (subnetNoList ) > 2 {
159163 newErr := fmt .Errorf ("SubnetNoList range constraints : Min. range of 1. Max. range of 2." )
160164 cblogger .Error (newErr .Error ())
161165 LoggingError (callLogInfo , newErr )
162166 return irs.NLBInfo {}, newErr
163167 }
164168
165- // LB performance(throughput) type code : 'SMALL' | 'MEDIUM' | 'LARGE' (Default: 'SMALL')
169+ // Note) LB performance(throughput) type code : 'SMALL' | 'MEDIUM' | 'LARGE' (Default: 'SMALL')
166170 // You can only select 'SMALL' if the LB type is 'NETWORK' and the LB network type is 'PRIVATE'.
167171 throughputType := DefaultThroughputType
168172 lbReq := vlb.CreateLoadBalancerInstanceRequest {
169173 RegionCode : & nlbHandler .RegionInfo .Region ,
170174 IdleTimeout : & timeOut ,
171175 LoadBalancerNetworkTypeCode : & lbNetType ,
172- LoadBalancerTypeCode : & lbType , // *** Required (Not Optional)
176+ LoadBalancerTypeCode : & networkTypeLb , // *** Required (Not Optional)
173177 LoadBalancerName : & nlbReqInfo .IId .NameId ,
174178 ThroughputTypeCode : & throughputType ,
175- VpcNo : ncpVPCInfo .VpcNo , // *** Required (Not Optional)
176- SubnetNoList : subnetNoList , // *** Required (Not Optional)
179+ VpcNo : ncpVPCInfo .VpcNo , // *** Required (Not Optional)
180+ SubnetNoList : subnetNoList , // *** Required (Not Optional)
177181 }
178182
179183 // ### LoadBalancerSubnetList > PublicIpInstanceNo
@@ -196,7 +200,7 @@ func (nlbHandler *NcpVpcNLBHandler) CreateNLB(nlbReqInfo irs.NLBInfo) (createNLB
196200 }
197201 LoggingInfo (callLogInfo , callLogStart )
198202
199- if * result .TotalRows < 1 {
203+ if len ( result .LoadBalancerInstanceList ) < 1 {
200204 newErr := fmt .Errorf ("Failed to Create New NLB. NLB does Not Exist!!" )
201205 cblogger .Error (newErr .Error ())
202206 LoggingError (callLogInfo , newErr )
@@ -378,7 +382,7 @@ func (nlbHandler *NcpVpcNLBHandler) DeleteNLB(nlbIID irs.IID) (bool, error) {
378382func (nlbHandler * NcpVpcNLBHandler ) AddVMs (nlbIID irs.IID , vmIIDs * []irs.IID ) (irs.VMGroupInfo , error ) {
379383 cblogger .Info ("NCP VPC Cloud Driver: called AddVMs()" )
380384
381- InitLog () // Caution!!
385+ InitLog ()
382386 callLogInfo := GetCallLogScheme (nlbHandler .RegionInfo .Region , "NETWORKLOADBALANCE" , nlbIID .SystemId , "AddVMs()" )
383387
384388 if strings .EqualFold (nlbIID .SystemId , "" ) {
@@ -887,7 +891,7 @@ func (nlbHandler *NcpVpcNLBHandler) CreateListener(nlbId string, nlbReqInfo irs.
887891func (nlbHandler * NcpVpcNLBHandler ) GetListenerInfo (listenerId string , loadBalancerId string ) (* irs.ListenerInfo , error ) {
888892 cblogger .Info ("NCP VPC Cloud Driver: called GetListenerInfo()" )
889893
890- InitLog () // Caution!!
894+ InitLog ()
891895 callLogInfo := GetCallLogScheme (nlbHandler .RegionInfo .Region , "NETWORKLOADBALANCE" , listenerId , "GetListenerInfo()" )
892896
893897 if strings .EqualFold (listenerId , "" ) {
@@ -1020,7 +1024,7 @@ func (nlbHandler *NcpVpcNLBHandler) GetVMGroupInfo(nlb vlb.LoadBalancerInstance)
10201024func (nlbHandler * NcpVpcNLBHandler ) GetHealthCheckerInfo (nlb vlb.LoadBalancerInstance ) (irs.HealthCheckerInfo , error ) {
10211025 cblogger .Info ("NCP VPC Cloud Driver: called GetHealthCheckerInfo()" )
10221026
1023- InitLog () // Caution!!
1027+ InitLog ()
10241028 callLogInfo := GetCallLogScheme (nlbHandler .RegionInfo .Region , "NETWORKLOADBALANCE" , * nlb .LoadBalancerInstanceNo , "GetHealthCheckerInfo()" )
10251029
10261030 if strings .EqualFold (* nlb .VpcNo , "" ) {
@@ -1255,7 +1259,7 @@ func (nlbHandler *NcpVpcNLBHandler) waitForDelNlb(nlbIID irs.IID) (bool, error)
12551259 }
12561260}
12571261
1258- // NCP VPC LoadBalancerInstanceStatusName : Creating, Running, Changing, Terminating, Terminated, Repairing
1262+ // NCP VPC ' LoadBalancerInstanceStatusName' : Creating, Running, Changing, Terminating, Terminated, Repairing
12591263func (nlbHandler * NcpVpcNLBHandler ) getNcpNlbStatus (nlbIID irs.IID ) (string , error ) {
12601264 cblogger .Info ("NCP VPC Cloud Driver: called getNcpNlbStatus()" )
12611265 callLogInfo := GetCallLogScheme (nlbHandler .RegionInfo .Region , "NETWORKLOADBALANCE" , nlbIID .SystemId , "getNcpNlbStatus()" )
@@ -1315,7 +1319,7 @@ func (nlbHandler *NcpVpcNLBHandler) getNcpNlbInfo(nlbIID irs.IID) (*vlb.LoadBala
13151319func (nlbHandler * NcpVpcNLBHandler ) getNcpNlbListWithVpcId (vpcId * string ) ([]* vlb.LoadBalancerInstance , error ) {
13161320 cblogger .Info ("NPC VPC Cloud Driver: called getNcpNlbListWithVpcId()" )
13171321
1318- InitLog () // Caution!!
1322+ InitLog ()
13191323 callLogInfo := GetCallLogScheme (nlbHandler .RegionInfo .Region , "NETWORKLOADBALANCE" , "getNcpNlbListWithVpcId()" , "getNcpNlbListWithVpcId()" )
13201324
13211325 if strings .EqualFold (* vpcId , "" ) {
@@ -1654,3 +1658,153 @@ func (nlbHandler *NcpVpcNLBHandler) ListIID() ([]*irs.IID, error) {
16541658 }
16551659 return iidList , nil
16561660}
1661+
1662+ // Finds an available CIDR block for NLB subnet within the given VPC CIDR
1663+ // that doesn't conflict with existing subnets
1664+ func (nlbHandler * NcpVpcNLBHandler ) findAvailableCIDRForNLBSubnet (vpcId string , vpcCIDR string ) (string , error ) {
1665+ cblogger .Info ("NCP VPC Cloud Driver: called findAvailableCIDRForNLBSubnet()" )
1666+ InitLog ()
1667+ callLogInfo := GetCallLogScheme (nlbHandler .RegionInfo .Region , "NETWORKLOADBALANCE" , vpcId , "findAvailableCIDRForNLBSubnet()" )
1668+
1669+ if strings .EqualFold (vpcId , "" ) {
1670+ newErr := fmt .Errorf ("Invalid VPC ID" )
1671+ cblogger .Error (newErr .Error ())
1672+ LoggingError (callLogInfo , newErr )
1673+ return "" , newErr
1674+ }
1675+
1676+ // Get existing subnet list
1677+ vpcHandler := NcpVpcVPCHandler {
1678+ RegionInfo : nlbHandler .RegionInfo ,
1679+ VPCClient : nlbHandler .VPCClient ,
1680+ }
1681+ subnetInfoList , err := vpcHandler .ListSubnet (& vpcId )
1682+ if err != nil {
1683+ newErr := fmt .Errorf ("Failed to Get Subnet List: %v" , err )
1684+ cblogger .Error (newErr .Error ())
1685+ LoggingError (callLogInfo , newErr )
1686+ return "" , newErr
1687+ }
1688+
1689+ // Collect existing subnet CIDRs
1690+ existingCIDRs := make ([]string , 0 )
1691+ for _ , subnet := range subnetInfoList {
1692+ existingCIDRs = append (existingCIDRs , subnet .IPv4_CIDR )
1693+ }
1694+
1695+ // Parse VPC CIDR
1696+ _ , vpcNet , err := net .ParseCIDR (vpcCIDR )
1697+ if err != nil {
1698+ newErr := fmt .Errorf ("Failed to Parse VPC CIDR [%s]: %v" , vpcCIDR , err )
1699+ cblogger .Error (newErr .Error ())
1700+ LoggingError (callLogInfo , newErr )
1701+ return "" , newErr
1702+ }
1703+
1704+ // NLB subnet requires /28 (16 IP addresses)
1705+ nlbSubnetMask := 28
1706+
1707+ // Generate candidate CIDR blocks within the VPC range
1708+ candidateCIDRs , err := nlbHandler .generateCandidateCIDRs (vpcNet , nlbSubnetMask )
1709+ if err != nil {
1710+ newErr := fmt .Errorf ("Failed to Generate Candidate CIDRs: %v" , err )
1711+ cblogger .Error (newErr .Error ())
1712+ LoggingError (callLogInfo , newErr )
1713+ return "" , newErr
1714+ }
1715+
1716+ // Find first available CIDR that doesn't conflict with existing subnets
1717+ for _ , candidateCIDR := range candidateCIDRs {
1718+ if ! nlbHandler .isCIDRConflict (candidateCIDR , existingCIDRs ) {
1719+ cblogger .Infof ("Found available CIDR for NLB subnet: %s" , candidateCIDR )
1720+ return candidateCIDR , nil
1721+ }
1722+ }
1723+
1724+ newErr := fmt .Errorf ("No available CIDR block found for NLB subnet in VPC %s" , vpcCIDR )
1725+ cblogger .Error (newErr .Error ())
1726+ LoggingError (callLogInfo , newErr )
1727+ return "" , newErr
1728+ }
1729+
1730+ // Generates candidate CIDR blocks within the VPC network
1731+ func (nlbHandler * NcpVpcNLBHandler ) generateCandidateCIDRs (vpcNet * net.IPNet , subnetMask int ) ([]string , error ) {
1732+ cblogger .Info ("NCP VPC Cloud Driver: called generateCandidateCIDRs()" )
1733+
1734+ candidates := make ([]string , 0 )
1735+
1736+ // Get VPC network size
1737+ vpcOnes , vpcBits := vpcNet .Mask .Size ()
1738+ if vpcOnes > subnetMask {
1739+ return nil , fmt .Errorf ("VPC mask /%d is larger than required subnet mask /%d" , vpcOnes , subnetMask )
1740+ }
1741+
1742+ // Calculate number of possible subnets
1743+ subnetCount := 1 << uint (subnetMask - vpcOnes )
1744+
1745+ // Calculate subnet size
1746+ subnetSize := 1 << uint (vpcBits - subnetMask )
1747+
1748+ // Generate candidate CIDRs
1749+ baseIP := vpcNet .IP
1750+ for i := 0 ; i < subnetCount ; i ++ {
1751+ // Calculate subnet IP
1752+ subnetIP := make (net.IP , len (baseIP ))
1753+ copy (subnetIP , baseIP )
1754+
1755+ // Add offset
1756+ offset := i * subnetSize
1757+ addOffset (subnetIP , offset )
1758+
1759+ // Check if subnet IP is within VPC range
1760+ if vpcNet .Contains (subnetIP ) {
1761+ candidateCIDR := fmt .Sprintf ("%s/%d" , subnetIP .String (), subnetMask )
1762+ candidates = append (candidates , candidateCIDR )
1763+ }
1764+ }
1765+
1766+ cblogger .Infof ("Generated %d candidate CIDRs" , len (candidates ))
1767+ return candidates , nil
1768+ }
1769+
1770+ // Adds offset to IP address
1771+ func addOffset (ip net.IP , offset int ) {
1772+ for i := len (ip ) - 1 ; i >= 0 && offset > 0 ; i -- {
1773+ current := int (ip [i ]) + offset
1774+ ip [i ] = byte (current & 0xFF )
1775+ offset = current >> 8
1776+ }
1777+ }
1778+
1779+ // Checks if candidate CIDR conflicts with any existing CIDRs
1780+ func (nlbHandler * NcpVpcNLBHandler ) isCIDRConflict (candidateCIDR string , existingCIDRs []string ) bool {
1781+ cblogger .Debug ("Checking CIDR conflict for:" , candidateCIDR )
1782+
1783+ _ , candidateNet , err := net .ParseCIDR (candidateCIDR )
1784+ if err != nil {
1785+ cblogger .Error ("Failed to parse candidate CIDR:" , err )
1786+ return true
1787+ }
1788+
1789+ for _ , existingCIDR := range existingCIDRs {
1790+ _ , existingNet , err := net .ParseCIDR (existingCIDR )
1791+ if err != nil {
1792+ cblogger .Warn ("Failed to parse existing CIDR:" , existingCIDR , err )
1793+ continue
1794+ }
1795+
1796+ // Check if networks overlap
1797+ if nlbHandler .networksOverlap (candidateNet , existingNet ) {
1798+ cblogger .Debugf ("CIDR conflict detected: %s overlaps with %s" , candidateCIDR , existingCIDR )
1799+ return true
1800+ }
1801+ }
1802+
1803+ return false
1804+ }
1805+
1806+ // Checks if two networks overlap
1807+ func (nlbHandler * NcpVpcNLBHandler ) networksOverlap (net1 , net2 * net.IPNet ) bool {
1808+ // Check if net1 contains net2's network address or vice versa
1809+ return net1 .Contains (net2 .IP ) || net2 .Contains (net1 .IP )
1810+ }
0 commit comments