Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 23 additions & 12 deletions pkg/cloudprovider/openstack.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"sync"

Expand Down Expand Up @@ -40,18 +41,10 @@ const (
openstackProviderPrefix = "openstack:///"
egressIPTag = "OpenShiftEgressIP"

// NOTE: Capacity is defined on a per interface basis as:
// - IP address capacity for each node, where the capacity is either IP family
// agnostic or not.
// However, in OpenStack, we do have several possible ceilings such as port quotas and max_allowed_address_pairs.
// PortQuotas is the quota for the entire project and it might change based on admin settings. On the other hand,
// the PortQuota value should always be high enough and we should most likely not bother looking it up.
// However, max_allowed_address_pairs defaults to 10. It is only configurable in neutron.conf and there is no way
// to retrieve it through the API. In RHOSP, it defaults to 10, as well. Therefore, our best option is to set this
// to the default value of 10, always; and we might want to document that OSP environments must set
// max_allowed_address_pairs >= 10. For more details, see:
// Default for Neutron's max_allowed_address_pairs, configurable via the
// "max-allowed-address-pairs" key in the kube-cloud-config ConfigMap.
// https://github.com/openstack/neutron/blob/800f863ccc502b334cb2dd79ec54066440e43e27/neutron/conf/extensions/allowedaddresspairs.py#L21
openstackMaxCapacity = 10
openstackDefaultMaxCapacity = 10
)

// OpenStack implements the API wrapper for talking
Expand Down Expand Up @@ -545,6 +538,24 @@ func (o *OpenStack) GetNodeEgressIPConfiguration(node *corev1.Node, cpicIPs sets
return nil, fmt.Errorf("no suitable interface configurations found")
}

// getMaxCapacity reads the "max-allowed-address-pairs" key from the
// kube-cloud-config ConfigMap mounted at cfg.ConfigDir. If the key is
// absent, unreadable, or not a valid positive integer, it returns the
// default of openstackDefaultMaxCapacity (10).
func (o *OpenStack) getMaxCapacity() int {
data, err := os.ReadFile(filepath.Join(o.cfg.ConfigDir, "max-allowed-address-pairs"))
if err != nil {
return openstackDefaultMaxCapacity
}
v, err := strconv.Atoi(strings.TrimSpace(string(data)))
if err != nil || v <= 0 {
klog.Warningf("Ignoring invalid max-allowed-address-pairs value %q, using default %d", string(data), openstackDefaultMaxCapacity)
return openstackDefaultMaxCapacity
}
klog.Infof("Using configured max-allowed-address-pairs: %d", v)
return v
}

// getNeutronPortNodeEgressIPConfiguration renders the NeutronPortNodeEgressIPConfiguration for a given port.
// The interface is keyed by a neutron UUID.
// If multiple IPv4 repectively multiple IPv6 subnets are attached to the same port, throw an error.
Expand Down Expand Up @@ -597,7 +608,7 @@ func (o *OpenStack) getNeutronPortNodeEgressIPConfiguration(p neutronports.Port,
}
}

c := openstackMaxCapacity + cloudPrivateIPsCount - len(p.AllowedAddressPairs)
c := o.getMaxCapacity() + cloudPrivateIPsCount - len(p.AllowedAddressPairs)
return &NodeEgressIPConfiguration{
Interface: p.ID,
IFAddr: ifAddr{
Expand Down
16 changes: 8 additions & 8 deletions pkg/cloudprovider/openstack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ func TestOpenStackPlugin(t *testing.T) {
IPv6: "2000::/64",
},
Capacity: capacity{
IP: ptr.To(openstackMaxCapacity),
IP: ptr.To(openstackDefaultMaxCapacity),
},
},
}
Expand Down Expand Up @@ -812,7 +812,7 @@ func TestGetNodeEgressIPConfiguration(t *testing.T) {
IPv6: "2000::/64",
},
Capacity: capacity{
IP: ptr.To(openstackMaxCapacity),
IP: ptr.To(openstackDefaultMaxCapacity),
},
},
},
Expand Down Expand Up @@ -848,7 +848,7 @@ func TestGetNodeEgressIPConfiguration(t *testing.T) {
IPv6: "2001::/64",
},
Capacity: capacity{
IP: ptr.To(openstackMaxCapacity),
IP: ptr.To(openstackDefaultMaxCapacity),
},
},
},
Expand Down Expand Up @@ -892,7 +892,7 @@ func TestGetNodeEgressIPConfiguration(t *testing.T) {
IPv6: "2000::/64",
},
Capacity: capacity{
IP: ptr.To(openstackMaxCapacity),
IP: ptr.To(openstackDefaultMaxCapacity),
},
},
},
Expand Down Expand Up @@ -939,7 +939,7 @@ func TestGetNodeEgressIPConfiguration(t *testing.T) {
IPv6: "2000::/64",
},
Capacity: capacity{
IP: ptr.To(openstackMaxCapacity),
IP: ptr.To(openstackDefaultMaxCapacity),
},
},
},
Expand All @@ -951,7 +951,7 @@ func TestGetNodeEgressIPConfiguration(t *testing.T) {
IPv6: "2001::/64",
},
Capacity: capacity{
IP: ptr.To(openstackMaxCapacity),
IP: ptr.To(openstackDefaultMaxCapacity),
},
},
},
Expand Down Expand Up @@ -1036,7 +1036,7 @@ func TestGetNeutronPortNodeEgressIPConfiguration(t *testing.T) {
IPv6: "2000::/64",
},
Capacity: capacity{
IP: ptr.To(openstackMaxCapacity - 5), // 5 allowed_address_pairs configured on the port.
IP: ptr.To(openstackDefaultMaxCapacity - 5), // 5 allowed_address_pairs configured on the port.
},
},
},
Expand All @@ -1049,7 +1049,7 @@ func TestGetNeutronPortNodeEgressIPConfiguration(t *testing.T) {
IPv6: "2000::/64",
},
Capacity: capacity{
IP: ptr.To(openstackMaxCapacity - 2), // excluding 2 allowed_address_pairs configured on the port.
IP: ptr.To(openstackDefaultMaxCapacity - 2), // excluding 2 allowed_address_pairs configured on the port.
},
},
// Configure IPs with 3 ips are within neutron subnet, 1 ip outside neutron subnet.
Expand Down