diff --git a/pkg/sdn/api/validation/validation.go b/pkg/sdn/api/validation/validation.go index c709a7157a1a..d20f86cff5d3 100644 --- a/pkg/sdn/api/validation/validation.go +++ b/pkg/sdn/api/validation/validation.go @@ -8,6 +8,7 @@ import ( oapi "github.com/openshift/origin/pkg/api" sdnapi "github.com/openshift/origin/pkg/sdn/api" + sdnplugin "github.com/openshift/origin/pkg/sdn/plugin" ) // ValidateClusterNetwork tests if required fields in the ClusterNetwork are set. @@ -85,9 +86,16 @@ func ValidateClusterNetworkUpdate(obj *sdnapi.ClusterNetwork, old *sdnapi.Cluste func ValidateHostSubnet(hs *sdnapi.HostSubnet) field.ErrorList { allErrs := validation.ValidateObjectMeta(&hs.ObjectMeta, false, oapi.MinimalNameRequirements, field.NewPath("metadata")) - _, _, err := net.ParseCIDR(hs.Subnet) - if err != nil { - allErrs = append(allErrs, field.Invalid(field.NewPath("subnet"), hs.Subnet, err.Error())) + if hs.Subnet == "" { + // check if annotation exists, then let the Subnet field be empty + if _, ok := hs.Annotations[sdnplugin.AssignHostSubnetAnnotation]; !ok { + allErrs = append(allErrs, field.Invalid(field.NewPath("subnet"), hs.Subnet, "Field cannot be empty")) + } + } else { + _, _, err := net.ParseCIDR(hs.Subnet) + if err != nil { + allErrs = append(allErrs, field.Invalid(field.NewPath("subnet"), hs.Subnet, err.Error())) + } } if net.ParseIP(hs.HostIP) == nil { allErrs = append(allErrs, field.Invalid(field.NewPath("hostIP"), hs.HostIP, "invalid IP address")) diff --git a/pkg/sdn/plugin/plugin.go b/pkg/sdn/plugin/plugin.go index 600a1a9a86e8..27163f92937a 100644 --- a/pkg/sdn/plugin/plugin.go +++ b/pkg/sdn/plugin/plugin.go @@ -25,6 +25,7 @@ const ( IngressBandwidthAnnotation string = "kubernetes.io/ingress-bandwidth" EgressBandwidthAnnotation string = "kubernetes.io/egress-bandwidth" AssignMacVlanAnnotation string = "pod.network.openshift.io/assign-macvlan" + AssignHostSubnetAnnotation string = "pod.network.openshift.io/assign-subnet" ) func IsOpenShiftNetworkPlugin(pluginName string) bool { diff --git a/pkg/sdn/plugin/subnets.go b/pkg/sdn/plugin/subnets.go index b3a0abf906eb..54845b90bd7b 100644 --- a/pkg/sdn/plugin/subnets.go +++ b/pkg/sdn/plugin/subnets.go @@ -42,6 +42,7 @@ func (master *OsdnMaster) SubnetStartMaster(clusterNetwork *net.IPNet, hostSubne } go utilwait.Forever(master.watchNodes, 0) + go utilwait.Forever(master.watchSubnets, 0) return nil } @@ -242,6 +243,41 @@ func (node *OsdnNode) initSelfSubnet() error { return nil } +// Only run on the master +// Watch for all hostsubnet events and if one is found with the right annotation, use the SubnetAllocator to dole a real subnet +func (master *OsdnMaster) watchSubnets() { + RunEventQueue(master.osClient, HostSubnets, func(delta cache.Delta) error { + hs := delta.Object.(*osapi.HostSubnet) + name := hs.ObjectMeta.Name + hostIP := hs.HostIP + + log.V(5).Infof("Watch %s event for HostSubnet %q", delta.Type, hs.ObjectMeta.Name) + switch delta.Type { + case cache.Sync, cache.Added, cache.Updated: + if _, ok := hs.Annotations[AssignHostSubnetAnnotation]; ok { + // Delete the annotated hostsubnet and create a new one with an assigned subnet + // We do not update (instead of delete+create) because the watchSubnets on the nodes + // will skip the event if it finds that the hostsubnet has the same host + // And we cannot fix the watchSubnets code for node because it will break migration if + // nodes are upgraded after the master + err := master.osClient.HostSubnets().Delete(name) + if err != nil { + log.Errorf("Error in deleting annotated subnet from master, name: %s, ip %s: %v", name, hostIP, err) + return nil + } + err = master.addNode(name, hostIP) + if err != nil { + log.Errorf("Error creating subnet for node %s, ip %s: %v", name, hostIP, err) + return nil + } + } + case cache.Deleted: + // ignore all deleted hostsubnets + } + return nil + }) +} + // Only run on the nodes func (node *OsdnNode) watchSubnets() { subnets := make(map[string]*osapi.HostSubnet) diff --git a/pkg/util/netutils/subnet_allocator.go b/pkg/util/netutils/subnet_allocator.go index 0baa801bc397..49f5d8774151 100644 --- a/pkg/util/netutils/subnet_allocator.go +++ b/pkg/util/netutils/subnet_allocator.go @@ -3,6 +3,7 @@ package netutils import ( "fmt" "net" + "sync" ) type SubnetAllocator struct { @@ -14,6 +15,7 @@ type SubnetAllocator struct { rightMask uint32 next uint32 allocMap map[string]bool + mutex sync.Mutex } func NewSubnetAllocator(network string, hostBits uint32, inUse []string) (*SubnetAllocator, error) { @@ -85,6 +87,9 @@ func (sna *SubnetAllocator) GetNetwork() (*net.IPNet, error) { numSubnets uint32 numSubnetBits uint32 ) + sna.mutex.Lock() + defer sna.mutex.Unlock() + baseipu := IPToUint32(sna.network.IP) netMaskSize, _ := sna.network.Mask.Size() numSubnetBits = 32 - uint32(netMaskSize) - sna.hostBits @@ -109,6 +114,8 @@ func (sna *SubnetAllocator) GetNetwork() (*net.IPNet, error) { } func (sna *SubnetAllocator) ReleaseNetwork(ipnet *net.IPNet) error { + sna.mutex.Lock() + defer sna.mutex.Unlock() if !sna.network.Contains(ipnet.IP) { return fmt.Errorf("Provided subnet %v doesn't belong to the network %v.", ipnet, sna.network) }