-
Notifications
You must be signed in to change notification settings - Fork 50
[NCP] Add features to find and apply available CIDR for LB type subnet #1606
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR improves the NLB (Network Load Balancer) subnet creation logic for the NCP (Naver Cloud Platform) driver by implementing dynamic CIDR allocation to prevent subnet conflicts. Previously, the code used a hardcoded CIDR suffix (.240/28) which could cause conflicts with existing subnets. The new implementation scans existing subnets and allocates an available CIDR block dynamically.
- Implements conflict-free CIDR allocation algorithm for NLB subnets
- Refactors constant names for better consistency (e.g.,
NcpLbType→NcpNetworkLbType) - Removes hardcoded CIDR suffix and adds intelligent CIDR selection logic
| // Find available CIDR for NLB subnet | ||
| availableCIDR, err := nlbHandler.findAvailableCIDRForNLBSubnet(*ncpVPCInfo.VpcNo, *ncpVPCInfo.Ipv4CidrBlock) | ||
| if err != nil { | ||
| newErr := fmt.Errorf("Failed to find available CIDR for NLB subnet: [%v]", err) | ||
| cblogger.Error(newErr.Error()) | ||
| LoggingError(callLogInfo, newErr) | ||
| return irs.NLBInfo{}, newErr | ||
| } |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent indentation: lines 125-132 use spaces while the rest of the file uses tabs. Please convert spaces to tabs to maintain consistency with the codebase style.
| // Find available CIDR for NLB subnet | |
| availableCIDR, err := nlbHandler.findAvailableCIDRForNLBSubnet(*ncpVPCInfo.VpcNo, *ncpVPCInfo.Ipv4CidrBlock) | |
| if err != nil { | |
| newErr := fmt.Errorf("Failed to find available CIDR for NLB subnet: [%v]", err) | |
| cblogger.Error(newErr.Error()) | |
| LoggingError(callLogInfo, newErr) | |
| return irs.NLBInfo{}, newErr | |
| } | |
| // Find available CIDR for NLB subnet | |
| availableCIDR, err := nlbHandler.findAvailableCIDRForNLBSubnet(*ncpVPCInfo.VpcNo, *ncpVPCInfo.Ipv4CidrBlock) | |
| if err != nil { | |
| newErr := fmt.Errorf("Failed to find available CIDR for NLB subnet: [%v]", err) | |
| cblogger.Error(newErr.Error()) | |
| LoggingError(callLogInfo, newErr) | |
| return irs.NLBInfo{}, newErr | |
| } |
| NameId: lbTypeSubnetName, | ||
| }, | ||
| IPv4_CIDR: cidrForNlbSubnet, | ||
| IPv4_CIDR: availableCIDR, |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent whitespace: there's a tab character between the colon and value. Please use consistent spacing to align with other struct field assignments.
| IPv4_CIDR: availableCIDR, | |
| IPv4_CIDR: availableCIDR, |
| cblogger.Info("NCP VPC Cloud Driver: called findAvailableCIDRForNLBSubnet()") | ||
| InitLog() | ||
| callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", vpcId, "findAvailableCIDRForNLBSubnet()") | ||
|
|
||
| if strings.EqualFold(vpcId, "") { | ||
| newErr := fmt.Errorf("Invalid VPC ID") | ||
| cblogger.Error(newErr.Error()) | ||
| LoggingError(callLogInfo, newErr) | ||
| return "", newErr | ||
| } | ||
|
|
||
| // Get existing subnet list | ||
| vpcHandler := NcpVpcVPCHandler{ | ||
| RegionInfo: nlbHandler.RegionInfo, | ||
| VPCClient: nlbHandler.VPCClient, | ||
| } | ||
| subnetInfoList, err := vpcHandler.ListSubnet(&vpcId) | ||
| if err != nil { | ||
| newErr := fmt.Errorf("Failed to Get Subnet List: %v", err) | ||
| cblogger.Error(newErr.Error()) | ||
| LoggingError(callLogInfo, newErr) | ||
| return "", newErr | ||
| } | ||
|
|
||
| // Collect existing subnet CIDRs | ||
| existingCIDRs := make([]string, 0) | ||
| for _, subnet := range subnetInfoList { | ||
| existingCIDRs = append(existingCIDRs, subnet.IPv4_CIDR) | ||
| } | ||
|
|
||
| // Parse VPC CIDR | ||
| _, vpcNet, err := net.ParseCIDR(vpcCIDR) | ||
| if err != nil { | ||
| newErr := fmt.Errorf("Failed to Parse VPC CIDR [%s]: %v", vpcCIDR, err) | ||
| cblogger.Error(newErr.Error()) | ||
| LoggingError(callLogInfo, newErr) | ||
| return "", newErr | ||
| } | ||
|
|
||
| // NLB subnet requires /28 (16 IP addresses) | ||
| nlbSubnetMask := 28 | ||
|
|
||
| // Generate candidate CIDR blocks within the VPC range | ||
| candidateCIDRs, err := nlbHandler.generateCandidateCIDRs(vpcNet, nlbSubnetMask) | ||
| if err != nil { | ||
| newErr := fmt.Errorf("Failed to Generate Candidate CIDRs: %v", err) | ||
| cblogger.Error(newErr.Error()) | ||
| LoggingError(callLogInfo, newErr) | ||
| return "", newErr | ||
| } | ||
|
|
||
| // Find first available CIDR that doesn't conflict with existing subnets | ||
| for _, candidateCIDR := range candidateCIDRs { | ||
| if !nlbHandler.isCIDRConflict(candidateCIDR, existingCIDRs) { | ||
| cblogger.Infof("Found available CIDR for NLB subnet: %s", candidateCIDR) | ||
| return candidateCIDR, nil | ||
| } | ||
| } | ||
|
|
||
| newErr := fmt.Errorf("No available CIDR block found for NLB subnet in VPC %s", vpcCIDR) | ||
| cblogger.Error(newErr.Error()) | ||
| LoggingError(callLogInfo, newErr) | ||
| return "", newErr |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent indentation: lines 1665-1727 use spaces while the rest of the file uses tabs. Please convert all spaces to tabs to maintain consistency with the codebase.
| cblogger.Info("NCP VPC Cloud Driver: called findAvailableCIDRForNLBSubnet()") | |
| InitLog() | |
| callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", vpcId, "findAvailableCIDRForNLBSubnet()") | |
| if strings.EqualFold(vpcId, "") { | |
| newErr := fmt.Errorf("Invalid VPC ID") | |
| cblogger.Error(newErr.Error()) | |
| LoggingError(callLogInfo, newErr) | |
| return "", newErr | |
| } | |
| // Get existing subnet list | |
| vpcHandler := NcpVpcVPCHandler{ | |
| RegionInfo: nlbHandler.RegionInfo, | |
| VPCClient: nlbHandler.VPCClient, | |
| } | |
| subnetInfoList, err := vpcHandler.ListSubnet(&vpcId) | |
| if err != nil { | |
| newErr := fmt.Errorf("Failed to Get Subnet List: %v", err) | |
| cblogger.Error(newErr.Error()) | |
| LoggingError(callLogInfo, newErr) | |
| return "", newErr | |
| } | |
| // Collect existing subnet CIDRs | |
| existingCIDRs := make([]string, 0) | |
| for _, subnet := range subnetInfoList { | |
| existingCIDRs = append(existingCIDRs, subnet.IPv4_CIDR) | |
| } | |
| // Parse VPC CIDR | |
| _, vpcNet, err := net.ParseCIDR(vpcCIDR) | |
| if err != nil { | |
| newErr := fmt.Errorf("Failed to Parse VPC CIDR [%s]: %v", vpcCIDR, err) | |
| cblogger.Error(newErr.Error()) | |
| LoggingError(callLogInfo, newErr) | |
| return "", newErr | |
| } | |
| // NLB subnet requires /28 (16 IP addresses) | |
| nlbSubnetMask := 28 | |
| // Generate candidate CIDR blocks within the VPC range | |
| candidateCIDRs, err := nlbHandler.generateCandidateCIDRs(vpcNet, nlbSubnetMask) | |
| if err != nil { | |
| newErr := fmt.Errorf("Failed to Generate Candidate CIDRs: %v", err) | |
| cblogger.Error(newErr.Error()) | |
| LoggingError(callLogInfo, newErr) | |
| return "", newErr | |
| } | |
| // Find first available CIDR that doesn't conflict with existing subnets | |
| for _, candidateCIDR := range candidateCIDRs { | |
| if !nlbHandler.isCIDRConflict(candidateCIDR, existingCIDRs) { | |
| cblogger.Infof("Found available CIDR for NLB subnet: %s", candidateCIDR) | |
| return candidateCIDR, nil | |
| } | |
| } | |
| newErr := fmt.Errorf("No available CIDR block found for NLB subnet in VPC %s", vpcCIDR) | |
| cblogger.Error(newErr.Error()) | |
| LoggingError(callLogInfo, newErr) | |
| return "", newErr | |
| cblogger.Info("NCP VPC Cloud Driver: called findAvailableCIDRForNLBSubnet()") | |
| InitLog() | |
| callLogInfo := GetCallLogScheme(nlbHandler.RegionInfo.Region, "NETWORKLOADBALANCE", vpcId, "findAvailableCIDRForNLBSubnet()") | |
| if strings.EqualFold(vpcId, "") { | |
| newErr := fmt.Errorf("Invalid VPC ID") | |
| cblogger.Error(newErr.Error()) | |
| LoggingError(callLogInfo, newErr) | |
| return "", newErr | |
| } | |
| // Get existing subnet list | |
| vpcHandler := NcpVpcVPCHandler{ | |
| RegionInfo: nlbHandler.RegionInfo, | |
| VPCClient: nlbHandler.VPCClient, | |
| } | |
| subnetInfoList, err := vpcHandler.ListSubnet(&vpcId) | |
| if err != nil { | |
| newErr := fmt.Errorf("Failed to Get Subnet List: %v", err) | |
| cblogger.Error(newErr.Error()) | |
| LoggingError(callLogInfo, newErr) | |
| return "", newErr | |
| } | |
| // Collect existing subnet CIDRs | |
| existingCIDRs := make([]string, 0) | |
| for _, subnet := range subnetInfoList { | |
| existingCIDRs = append(existingCIDRs, subnet.IPv4_CIDR) | |
| } | |
| // Parse VPC CIDR | |
| _, vpcNet, err := net.ParseCIDR(vpcCIDR) | |
| if err != nil { | |
| newErr := fmt.Errorf("Failed to Parse VPC CIDR [%s]: %v", vpcCIDR, err) | |
| cblogger.Error(newErr.Error()) | |
| LoggingError(callLogInfo, newErr) | |
| return "", newErr | |
| } | |
| // NLB subnet requires /28 (16 IP addresses) | |
| nlbSubnetMask := 28 | |
| // Generate candidate CIDR blocks within the VPC range | |
| candidateCIDRs, err := nlbHandler.generateCandidateCIDRs(vpcNet, nlbSubnetMask) | |
| if err != nil { | |
| newErr := fmt.Errorf("Failed to Generate Candidate CIDRs: %v", err) | |
| cblogger.Error(newErr.Error()) | |
| LoggingError(callLogInfo, newErr) | |
| return "", newErr | |
| } | |
| // Find first available CIDR that doesn't conflict with existing subnets | |
| for _, candidateCIDR := range candidateCIDRs { | |
| if !nlbHandler.isCIDRConflict(candidateCIDR, existingCIDRs) { | |
| cblogger.Infof("Found available CIDR for NLB subnet: %s", candidateCIDR) | |
| return candidateCIDR, nil | |
| } | |
| } | |
| newErr := fmt.Errorf("No available CIDR block found for NLB subnet in VPC %s", vpcCIDR) | |
| cblogger.Error(newErr.Error()) | |
| LoggingError(callLogInfo, newErr) | |
| return "", newErr |
| cblogger.Info("NCP VPC Cloud Driver: called generateCandidateCIDRs()") | ||
|
|
||
| candidates := make([]string, 0) | ||
|
|
||
| // Get VPC network size | ||
| vpcOnes, vpcBits := vpcNet.Mask.Size() | ||
| if vpcOnes > subnetMask { | ||
| return nil, fmt.Errorf("VPC mask /%d is larger than required subnet mask /%d", vpcOnes, subnetMask) | ||
| } | ||
|
|
||
| // Calculate number of possible subnets | ||
| subnetCount := 1 << uint(subnetMask-vpcOnes) | ||
|
|
||
| // Calculate subnet size | ||
| subnetSize := 1 << uint(vpcBits-subnetMask) | ||
|
|
||
| // Generate candidate CIDRs | ||
| baseIP := vpcNet.IP | ||
| for i := 0; i < subnetCount; i++ { | ||
| // Calculate subnet IP | ||
| subnetIP := make(net.IP, len(baseIP)) | ||
| copy(subnetIP, baseIP) | ||
|
|
||
| // Add offset | ||
| offset := i * subnetSize | ||
| addOffset(subnetIP, offset) | ||
|
|
||
| // Check if subnet IP is within VPC range | ||
| if vpcNet.Contains(subnetIP) { | ||
| candidateCIDR := fmt.Sprintf("%s/%d", subnetIP.String(), subnetMask) | ||
| candidates = append(candidates, candidateCIDR) | ||
| } | ||
| } | ||
|
|
||
| cblogger.Infof("Generated %d candidate CIDRs", len(candidates)) | ||
| return candidates, nil |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent indentation: lines 1732-1767 use spaces while the rest of the file uses tabs. Please convert all spaces to tabs to maintain consistency with the codebase.
| cblogger.Info("NCP VPC Cloud Driver: called generateCandidateCIDRs()") | |
| candidates := make([]string, 0) | |
| // Get VPC network size | |
| vpcOnes, vpcBits := vpcNet.Mask.Size() | |
| if vpcOnes > subnetMask { | |
| return nil, fmt.Errorf("VPC mask /%d is larger than required subnet mask /%d", vpcOnes, subnetMask) | |
| } | |
| // Calculate number of possible subnets | |
| subnetCount := 1 << uint(subnetMask-vpcOnes) | |
| // Calculate subnet size | |
| subnetSize := 1 << uint(vpcBits-subnetMask) | |
| // Generate candidate CIDRs | |
| baseIP := vpcNet.IP | |
| for i := 0; i < subnetCount; i++ { | |
| // Calculate subnet IP | |
| subnetIP := make(net.IP, len(baseIP)) | |
| copy(subnetIP, baseIP) | |
| // Add offset | |
| offset := i * subnetSize | |
| addOffset(subnetIP, offset) | |
| // Check if subnet IP is within VPC range | |
| if vpcNet.Contains(subnetIP) { | |
| candidateCIDR := fmt.Sprintf("%s/%d", subnetIP.String(), subnetMask) | |
| candidates = append(candidates, candidateCIDR) | |
| } | |
| } | |
| cblogger.Infof("Generated %d candidate CIDRs", len(candidates)) | |
| return candidates, nil | |
| cblogger.Info("NCP VPC Cloud Driver: called generateCandidateCIDRs()") | |
| candidates := make([]string, 0) | |
| // Get VPC network size | |
| vpcOnes, vpcBits := vpcNet.Mask.Size() | |
| if vpcOnes > subnetMask { | |
| return nil, fmt.Errorf("VPC mask /%d is larger than required subnet mask /%d", vpcOnes, subnetMask) | |
| } | |
| // Calculate number of possible subnets | |
| subnetCount := 1 << uint(subnetMask-vpcOnes) | |
| // Calculate subnet size | |
| subnetSize := 1 << uint(vpcBits-subnetMask) | |
| // Generate candidate CIDRs | |
| baseIP := vpcNet.IP | |
| for i := 0; i < subnetCount; i++ { | |
| // Calculate subnet IP | |
| subnetIP := make(net.IP, len(baseIP)) | |
| copy(subnetIP, baseIP) | |
| // Add offset | |
| offset := i * subnetSize | |
| addOffset(subnetIP, offset) | |
| // Check if subnet IP is within VPC range | |
| if vpcNet.Contains(subnetIP) { | |
| candidateCIDR := fmt.Sprintf("%s/%d", subnetIP.String(), subnetMask) | |
| candidates = append(candidates, candidateCIDR) | |
| } | |
| } | |
| cblogger.Infof("Generated %d candidate CIDRs", len(candidates)) | |
| return candidates, nil |
| for i := len(ip) - 1; i >= 0 && offset > 0; i-- { | ||
| current := int(ip[i]) + offset | ||
| ip[i] = byte(current & 0xFF) | ||
| offset = current >> 8 | ||
| } |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent indentation: lines 1772-1776 use spaces while the rest of the file uses tabs. Please convert all spaces to tabs to maintain consistency with the codebase.
| for i := len(ip) - 1; i >= 0 && offset > 0; i-- { | |
| current := int(ip[i]) + offset | |
| ip[i] = byte(current & 0xFF) | |
| offset = current >> 8 | |
| } | |
| for i := len(ip) - 1; i >= 0 && offset > 0; i-- { | |
| current := int(ip[i]) + offset | |
| ip[i] = byte(current & 0xFF) | |
| offset = current >> 8 | |
| } |
| // Note : Create a 'LOADB' type of subnet ('LB Type' subnet for LB Only) | ||
|
|
||
| // Create a 'LOADB' type of subnet ('LB Type' subnet for LB Only) | ||
| ncpNlbSubnetInfo, err := nlbHandler.creatNcpSubnetForNlbOnly(irs.IID{SystemId: *ncpVPCInfo.VpcNo}, subnetReqInfo) // Waitting time Included |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected spelling of 'Waitting' to 'Waiting'.
| ncpNlbSubnetInfo, err := nlbHandler.creatNcpSubnetForNlbOnly(irs.IID{SystemId: *ncpVPCInfo.VpcNo}, subnetReqInfo) // Waitting time Included | |
| ncpNlbSubnetInfo, err := nlbHandler.creatNcpSubnetForNlbOnly(irs.IID{SystemId: *ncpVPCInfo.VpcNo}, subnetReqInfo) // Waiting time Included |
| } | ||
|
|
||
| // NLB subnet requires /28 (16 IP addresses) | ||
| nlbSubnetMask := 28 |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The hardcoded subnet mask value (28) should be defined as a constant at the package level for maintainability. This follows the pattern used for other NLB-related constants like DefaultThroughputType and LbTypeSubnetDefaultName.
|
|
||
| // Checks if candidate CIDR conflicts with any existing CIDRs | ||
| func (nlbHandler *NcpVpcNLBHandler) isCIDRConflict(candidateCIDR string, existingCIDRs []string) bool { | ||
| cblogger.Debug("Checking CIDR conflict for:", candidateCIDR) |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logging format is inconsistent with cb-log best practices. Use structured logging with proper format string: cblogger.Debugf(\"Checking CIDR conflict for: %s\", candidateCIDR) instead of passing multiple arguments to Debug.
| cblogger.Debug("Checking CIDR conflict for:", candidateCIDR) | |
| cblogger.Debugf("Checking CIDR conflict for: %s", candidateCIDR) |
|
|
||
| _, candidateNet, err := net.ParseCIDR(candidateCIDR) | ||
| if err != nil { | ||
| cblogger.Error("Failed to parse candidate CIDR:", err) |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logging format is inconsistent with cb-log best practices. Use structured logging with proper format string: cblogger.Errorf(\"Failed to parse candidate CIDR: %v\", err) instead of passing multiple arguments to Error.
| cblogger.Error("Failed to parse candidate CIDR:", err) | |
| cblogger.Errorf("Failed to parse candidate CIDR: %v", err) |
| for _, existingCIDR := range existingCIDRs { | ||
| _, existingNet, err := net.ParseCIDR(existingCIDR) | ||
| if err != nil { | ||
| cblogger.Warn("Failed to parse existing CIDR:", existingCIDR, err) |
Copilot
AI
Nov 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logging format is inconsistent with cb-log best practices. Use structured logging with proper format string: cblogger.Warnf(\"Failed to parse existing CIDR %s: %v\", existingCIDR, err) instead of passing multiple arguments to Warn.
| cblogger.Warn("Failed to parse existing CIDR:", existingCIDR, err) | |
| cblogger.Warnf("Failed to parse existing CIDR %s: %v", existingCIDR, err) |
|
Since the above PR has already been merged, inconsistent indentations will be addressed in the next PR. |
[ CB-Tumblebug error message that occurred before modifying ]

[ Example of dynamically assigned LB type subnet range (After modifying) ]
