Skip to content
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

API to autocreate subnets for external hosts #10621

Merged
merged 1 commit into from
Oct 12, 2016
Merged
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
14 changes: 11 additions & 3 deletions pkg/sdn/api/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't just allowing the subnet field to be empty, it's allowing it to be invalid... that's not what we want is it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed this. The check is made only on empty field.

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"))
Expand Down
1 change: 1 addition & 0 deletions pkg/sdn/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
36 changes: 36 additions & 0 deletions pkg/sdn/plugin/subnets.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -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)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that addNode() is called from watchNodes() and watchSubnets() asynchronously, we need a mutex to synchronize GetNetwork/ReleaseNetwork.

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)
Expand Down
7 changes: 7 additions & 0 deletions pkg/util/netutils/subnet_allocator.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package netutils
import (
"fmt"
"net"
"sync"
)

type SubnetAllocator struct {
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand All @@ -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)
}
Expand Down