diff --git a/docs/ipam.md b/docs/ipam.md index 35bad67e..d8fcb1e3 100644 --- a/docs/ipam.md +++ b/docs/ipam.md @@ -21,10 +21,15 @@ plugin) uses to request IP addresses. The commands supported by the allocator via the http interface are: - Allocate: request one IP address for a container in a subnet +- Lookup: fetch the previously-allocated IP address for a container in a subnet - Free: return an IP address that is currently allocated - Claim: request a specific IP address for a container (e.g. because it is already using that address) +Each http request either specifies a subnet, or if no subnet is +specified this is taken as a request to allocate in a pre-defined +default subnet. + The allocator also watches via the Docker event mechanism: if a container dies then all IP addresses allocated to that container are freed. @@ -46,10 +51,6 @@ freed. ### The Allocation Process -Each allocation request either specifies a subnet to allocate within, -or if no subnet is specified this is taken as a request to allocate in -a pre-defined default subnet. - When a peer owns some range(s), it can allocate freely from within those ranges to containers on the same machine. If it runs out of space in a subnet (all owned ranges are full), it will ask another diff --git a/ipam/allocator.go b/ipam/allocator.go index f54d5a0c..0949658a 100644 --- a/ipam/allocator.go +++ b/ipam/allocator.go @@ -21,7 +21,7 @@ const ( msgRingUpdate paxosInterval = time.Second * 5 - minSubnetSize = 4 // first and last addresses are excluded, so 2 would be too small + MinSubnetSize = 4 // first and last addresses are excluded, so 2 would be too small ) // operation represents something which Allocator wants to do, but @@ -45,7 +45,6 @@ type operation interface { type Allocator struct { actionChan chan<- func() ourName router.PeerName - defaultSubnet address.CIDR universe address.CIDR // superset of all ranges ring *ring.Ring // information on ranges owned by all peers space space.Space // more detail on ranges owned by us @@ -61,21 +60,16 @@ type Allocator struct { } // NewAllocator creates and initialises a new Allocator -func NewAllocator(ourName router.PeerName, ourUID router.PeerUID, ourNickname string, universeStr string, quorum uint) (*Allocator, error) { - universe, err := parseAndCheckCIDR(universeStr) - if err != nil { - return nil, err - } +func NewAllocator(ourName router.PeerName, ourUID router.PeerUID, ourNickname string, universe address.CIDR, quorum uint) *Allocator { return &Allocator{ - ourName: ourName, - universe: universe, - defaultSubnet: universe, - ring: ring.New(universe.Start, address.Add(universe.Start, universe.Size()), ourName), - owned: make(map[string][]address.Address), - paxos: paxos.NewNode(ourName, ourUID, quorum), - nicknames: map[router.PeerName]string{ourName: ourNickname}, - now: time.Now, - }, nil + ourName: ourName, + universe: universe, + ring: ring.New(universe.Start, address.Add(universe.Start, universe.Size()), ourName), + owned: make(map[string][]address.Address), + paxos: paxos.NewNode(ourName, ourUID, quorum), + nicknames: map[router.PeerName]string{ourName: ourNickname}, + now: time.Now, + } } // Start runs the allocator goroutine @@ -182,23 +176,6 @@ func hasBeenCancelled(cancelChan <-chan bool) func() bool { // Actor client API -func (alloc *Allocator) SetDefaultSubnet(subnetStr string) error { - subnet, err := parseAndCheckCIDR(subnetStr) - if err != nil { - return err - } - resultChan := make(chan error) - alloc.actionChan <- func() { - if !alloc.universe.Overlaps(subnet) { - resultChan <- fmt.Errorf("Default subnet %s out of bounds: %s", subnet, alloc.universe) - return - } - alloc.defaultSubnet = subnet - resultChan <- nil - } - return <-resultChan -} - // Allocate (Sync) - get IP address for container with given name // if there isn't any space we block indefinitely func (alloc *Allocator) Allocate(ident string, subnet address.CIDR, cancelChan <-chan bool) (address.CIDR, error) { @@ -494,20 +471,9 @@ func (alloc *Allocator) actorLoop(actionChan <-chan func()) { // Helper functions -func parseAndCheckCIDR(cidrStr string) (address.CIDR, error) { - _, cidr, err := address.ParseCIDR(cidrStr) - if err != nil { - return address.CIDR{}, err - } - if cidr.Size() < minSubnetSize { - return address.CIDR{}, fmt.Errorf("Allocation range smaller than minimum size %d: %s", minSubnetSize, cidrStr) - } - return cidr, nil -} - func (alloc *Allocator) string() string { var buf bytes.Buffer - fmt.Fprintf(&buf, "Allocator range %s (default subnet %s)", alloc.universe, alloc.defaultSubnet) + fmt.Fprintf(&buf, "Allocator range %s", alloc.universe) if alloc.ring.Empty() { if alloc.paxosTicker != nil { diff --git a/ipam/allocator_test.go b/ipam/allocator_test.go index bf378ad0..d8473c0d 100644 --- a/ipam/allocator_test.go +++ b/ipam/allocator_test.go @@ -32,7 +32,7 @@ func TestAllocFree(t *testing.T) { spaceSize = 62 // 64 IP addresses in /26, minus .0 and .63 ) - alloc := makeAllocatorWithMockGossip(t, "01:00:00:01:00:00", universe, 1) + alloc, subnet := makeAllocatorWithMockGossip(t, "01:00:00:01:00:00", universe, 1) defer alloc.Stop() _, cidr1, _ := address.ParseCIDR(subnet1) _, cidr2, _ := address.ParseCIDR(subnet2) @@ -67,7 +67,7 @@ func TestAllocFree(t *testing.T) { alloc.ContainerDied(container2) alloc.ContainerDied(container3) - wt.AssertEquals(t, alloc.NumFreeAddresses(), address.Offset(spaceSize)) + wt.AssertEquals(t, alloc.NumFreeAddresses(subnet), address.Offset(spaceSize)) } func TestBootstrap(t *testing.T) { @@ -79,11 +79,11 @@ func TestBootstrap(t *testing.T) { peerNameString = "02:00:00:02:00:00" ) - alloc1 := makeAllocatorWithMockGossip(t, ourNameString, testStart1+"/22", 2) + alloc1, subnet := makeAllocatorWithMockGossip(t, ourNameString, testStart1+"/22", 2) defer alloc1.Stop() // Simulate another peer on the gossip network - alloc2 := makeAllocatorWithMockGossip(t, peerNameString, testStart1+"/22", 2) + alloc2, _ := makeAllocatorWithMockGossip(t, peerNameString, testStart1+"/22", 2) defer alloc2.Stop() alloc1.OnGossipBroadcast(alloc2.Encode()) @@ -93,7 +93,7 @@ func TestBootstrap(t *testing.T) { ExpectBroadcastMessage(alloc1, nil) // alloc1 will try to form consensus done := make(chan bool) go func() { - alloc1.Allocate("somecontainer", alloc1.defaultSubnet, nil) + alloc1.Allocate("somecontainer", subnet, nil) done <- true }() time.Sleep(100 * time.Millisecond) @@ -137,7 +137,7 @@ func TestAllocatorClaim(t *testing.T) { testAddr1 = "10.0.3.1/30" // first address allocated should be .1 because .0 is network addr ) - alloc := makeAllocatorWithMockGossip(t, "01:00:00:01:00:00", universe, 1) + alloc, subnet := makeAllocatorWithMockGossip(t, "01:00:00:01:00:00", universe, 1) defer alloc.Stop() alloc.claimRingForTesting() @@ -146,7 +146,7 @@ func TestAllocatorClaim(t *testing.T) { err := alloc.Claim(container3, addr1, nil) wt.AssertNoErr(t, err) // Check we get this address back if we try an allocate - addr3, _ := alloc.Allocate(container3, alloc.defaultSubnet, nil) + addr3, _ := alloc.Allocate(container3, subnet, nil) wt.AssertEqualString(t, addr3.String(), testAddr1, "address") } @@ -170,10 +170,10 @@ func TestCancel(t *testing.T) { router := TestGossipRouter{make(map[router.PeerName]chan gossipMessage), 0.0} - alloc1 := makeAllocator("01:00:00:02:00:00", CIDR, 2) + alloc1, subnet := makeAllocator("01:00:00:02:00:00", CIDR, 2) alloc1.SetInterfaces(router.connect(alloc1.ourName, alloc1)) - alloc2 := makeAllocator("02:00:00:02:00:00", CIDR, 2) + alloc2, subnet := makeAllocator("02:00:00:02:00:00", CIDR, 2) alloc2.SetInterfaces(router.connect(alloc2.ourName, alloc2)) alloc1.claimRingForTesting(alloc1, alloc2) alloc2.claimRingForTesting(alloc1, alloc2) @@ -185,9 +185,9 @@ func TestCancel(t *testing.T) { alloc1.OnGossipBroadcast(alloc2.Encode()) // Get some IPs, so each allocator has some space - res1, _ := alloc1.Allocate("foo", alloc1.defaultSubnet, nil) + res1, _ := alloc1.Allocate("foo", subnet, nil) common.Debug.Printf("res1 = %s", res1.String()) - res2, _ := alloc2.Allocate("bar", alloc2.defaultSubnet, nil) + res2, _ := alloc2.Allocate("bar", subnet, nil) common.Debug.Printf("res2 = %s", res2.String()) if res1 == res2 { wt.Fatalf(t, "Error: got same ips!") @@ -198,13 +198,13 @@ func TestCancel(t *testing.T) { unpause := alloc2.pause() // Use up all the IPs that alloc1 owns, so the allocation after this will prompt a request to alloc2 - for i := 0; alloc1.NumFreeAddresses() > 0; i++ { - alloc1.Allocate(fmt.Sprintf("tmp%d", i), alloc1.defaultSubnet, nil) + for i := 0; alloc1.NumFreeAddresses(subnet) > 0; i++ { + alloc1.Allocate(fmt.Sprintf("tmp%d", i), subnet, nil) } cancelChan := make(chan bool, 1) doneChan := make(chan bool) go func() { - _, ok := alloc1.Allocate("baz", alloc1.defaultSubnet, cancelChan) + _, ok := alloc1.Allocate("baz", subnet, cancelChan) doneChan <- ok == nil }() @@ -226,15 +226,15 @@ func TestGossipShutdown(t *testing.T) { universe = "10.0.3.0/30" ) - alloc := makeAllocatorWithMockGossip(t, "01:00:00:01:00:00", universe, 1) + alloc, subnet := makeAllocatorWithMockGossip(t, "01:00:00:01:00:00", universe, 1) defer alloc.Stop() alloc.claimRingForTesting() - alloc.Allocate(container1, alloc.defaultSubnet, nil) + alloc.Allocate(container1, subnet, nil) alloc.Shutdown() - _, err := alloc.Allocate(container2, alloc.defaultSubnet, nil) // trying to allocate after shutdown should fail + _, err := alloc.Allocate(container2, subnet, nil) // trying to allocate after shutdown should fail wt.AssertFalse(t, err == nil, "no address") CheckAllExpectedMessagesSent(alloc) @@ -244,15 +244,15 @@ func TestTransfer(t *testing.T) { const ( cidr = "10.0.1.7/22" ) - allocs, router := makeNetworkOfAllocators(3, cidr) + allocs, router, subnet := makeNetworkOfAllocators(3, cidr) alloc1 := allocs[0] alloc2 := allocs[1] alloc3 := allocs[2] // This will be 'master' and get the first range - _, err := alloc2.Allocate("foo", alloc2.defaultSubnet, nil) + _, err := alloc2.Allocate("foo", subnet, nil) wt.AssertTrue(t, err == nil, "Failed to get address") - _, err = alloc3.Allocate("bar", alloc3.defaultSubnet, nil) + _, err = alloc3.Allocate("bar", subnet, nil) wt.AssertTrue(t, err == nil, "Failed to get address") router.GossipBroadcast(alloc2.Gossip()) @@ -264,9 +264,9 @@ func TestTransfer(t *testing.T) { wt.AssertSuccess(t, alloc1.AdminTakeoverRanges(alloc2.ourName.String())) wt.AssertSuccess(t, alloc1.AdminTakeoverRanges(alloc3.ourName.String())) - wt.AssertEquals(t, alloc1.NumFreeAddresses(), address.Offset(1022)) + wt.AssertEquals(t, alloc1.NumFreeAddresses(subnet), address.Offset(1022)) - _, err = alloc1.Allocate("foo", alloc1.defaultSubnet, nil) + _, err = alloc1.Allocate("foo", subnet, nil) wt.AssertTrue(t, err == nil, "Failed to get address") alloc1.Stop() } @@ -276,13 +276,13 @@ func TestFakeRouterSimple(t *testing.T) { const ( cidr = "10.0.1.7/22" ) - allocs, _ := makeNetworkOfAllocators(2, cidr) + allocs, _, subnet := makeNetworkOfAllocators(2, cidr) defer stopNetworkOfAllocators(allocs) alloc1 := allocs[0] //alloc2 := allocs[1] - alloc1.Allocate("foo", alloc1.defaultSubnet, nil) + alloc1.Allocate("foo", subnet, nil) } func TestAllocatorFuzz(t *testing.T) { @@ -295,7 +295,7 @@ func TestAllocatorFuzz(t *testing.T) { concurrency = 10 cidr = "10.0.1.7/22" ) - allocs, _ := makeNetworkOfAllocators(nodes, cidr) + allocs, _, subnet := makeNetworkOfAllocators(nodes, cidr) defer stopNetworkOfAllocators(allocs) // Test state @@ -339,7 +339,7 @@ func TestAllocatorFuzz(t *testing.T) { allocIndex := rand.Int31n(nodes) alloc := allocs[allocIndex] //common.Info.Printf("Allocate: asking allocator %d", allocIndex) - addr, err := alloc.Allocate(name, alloc.defaultSubnet, nil) + addr, err := alloc.Allocate(name, subnet, nil) if err != nil { panic(fmt.Sprintf("Could not allocate addr")) @@ -405,7 +405,7 @@ func TestAllocatorFuzz(t *testing.T) { //common.Info.Printf("Asking for %s on allocator %d again", addr, res.alloc) - newAddr, _ := alloc.Allocate(res.name, alloc.defaultSubnet, nil) + newAddr, _ := alloc.Allocate(res.name, subnet, nil) oldAddr, _, _ := address.ParseCIDR(addr) if newAddr.Start != oldAddr { panic(fmt.Sprintf("Got different address for repeat request for %s: %s != %s", res.name, newAddr, oldAddr)) @@ -464,9 +464,9 @@ func TestAllocatorFuzz(t *testing.T) { } func TestGossipSkew(t *testing.T) { - alloc1 := makeAllocatorWithMockGossip(t, "01:00:00:01:00:00", "10.0.1.0/22", 2) + alloc1, _ := makeAllocatorWithMockGossip(t, "01:00:00:01:00:00", "10.0.1.0/22", 2) defer alloc1.Stop() - alloc2 := makeAllocatorWithMockGossip(t, "02:00:00:02:00:00", "10.0.1.0/22", 2) + alloc2, _ := makeAllocatorWithMockGossip(t, "02:00:00:02:00:00", "10.0.1.0/22", 2) alloc2.now = func() time.Time { return time.Now().Add(time.Hour * 2) } defer alloc2.Stop() diff --git a/ipam/http.go b/ipam/http.go index 2026ba1b..cdb858a0 100644 --- a/ipam/http.go +++ b/ipam/http.go @@ -16,7 +16,7 @@ func badRequest(w http.ResponseWriter, err error) { } // HandleHTTP wires up ipams HTTP endpoints to the provided mux. -func (alloc *Allocator) HandleHTTP(router *mux.Router) { +func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CIDR) { router.Methods("PUT").Path("/ip/{id}/{ip}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { closedChan := w.(http.CloseNotifier).CloseNotify() vars := mux.Vars(r) @@ -50,7 +50,7 @@ func (alloc *Allocator) HandleHTTP(router *mux.Router) { }) router.Methods("GET").Path("/ip/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - addr, err := alloc.Lookup(mux.Vars(r)["id"], alloc.defaultSubnet) + addr, err := alloc.Lookup(mux.Vars(r)["id"], defaultSubnet) if err != nil { http.NotFound(w, r) return @@ -84,7 +84,7 @@ func (alloc *Allocator) HandleHTTP(router *mux.Router) { router.Methods("POST").Path("/ip/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { closedChan := w.(http.CloseNotifier).CloseNotify() ident := mux.Vars(r)["id"] - newAddr, err := alloc.Allocate(ident, alloc.defaultSubnet, closedChan) + newAddr, err := alloc.Allocate(ident, defaultSubnet, closedChan) if err != nil { badRequest(w, err) return diff --git a/ipam/http_test.go b/ipam/http_test.go index 19efaa95..68578bf9 100644 --- a/ipam/http_test.go +++ b/ipam/http_test.go @@ -13,6 +13,7 @@ import ( "github.com/gorilla/mux" "github.com/weaveworks/weave/common" + "github.com/weaveworks/weave/ipam/address" wt "github.com/weaveworks/weave/testing" ) @@ -38,12 +39,12 @@ func doHTTP(method string, url string) (resp *http.Response, err error) { return http.DefaultClient.Do(req) } -func listenHTTP(alloc *Allocator) int { +func listenHTTP(alloc *Allocator, subnet address.CIDR) int { router := mux.NewRouter() router.HandleFunc("/status", func(w http.ResponseWriter, r *http.Request) { io.WriteString(w, fmt.Sprintln(alloc)) }) - alloc.HandleHTTP(router) + alloc.HandleHTTP(router, subnet) httpListener, err := net.Listen("tcp", ":0") if err != nil { @@ -79,8 +80,8 @@ func TestHttp(t *testing.T) { testAddr2 = "10.2.0.1/16" ) - alloc := makeAllocatorWithMockGossip(t, "08:00:27:01:c3:9a", universe, 1) - port := listenHTTP(alloc) + alloc, subnet := makeAllocatorWithMockGossip(t, "08:00:27:01:c3:9a", universe, 1) + port := listenHTTP(alloc, subnet) alloc.claimRingForTesting() cidr1a := HTTPPost(t, allocURL(port, testCIDR1, containerID)) @@ -119,9 +120,9 @@ func TestBadHttp(t *testing.T) { testCIDR1 = "10.0.0.0/8" ) - alloc := makeAllocatorWithMockGossip(t, "08:00:27:01:c3:9a", testCIDR1, 1) + alloc, subnet := makeAllocatorWithMockGossip(t, "08:00:27:01:c3:9a", testCIDR1, 1) defer alloc.Stop() - port := listenHTTP(alloc) + port := listenHTTP(alloc, subnet) alloc.claimRingForTesting() cidr1 := HTTPPost(t, allocURL(port, testCIDR1, containerID)) @@ -154,10 +155,10 @@ func impTestHTTPCancel(t *testing.T) { testCIDR1 = "10.0.3.0/29" ) - alloc := makeAllocatorWithMockGossip(t, "08:00:27:01:c3:9a", testCIDR1, 1) + alloc, subnet := makeAllocatorWithMockGossip(t, "08:00:27:01:c3:9a", testCIDR1, 1) defer alloc.Stop() alloc.claimRingForTesting() - port := listenHTTP(alloc) + port := listenHTTP(alloc, subnet) // Stop the alloc so nothing actually works unpause := alloc.pause() diff --git a/ipam/testutils_test.go b/ipam/testutils_test.go index 69569b7e..6d1b892e 100644 --- a/ipam/testutils_test.go +++ b/ipam/testutils_test.go @@ -128,27 +128,29 @@ func CheckAllExpectedMessagesSent(allocs ...*Allocator) { } } -func makeAllocator(name string, cidrStr string, quorum uint) *Allocator { +func makeAllocator(name string, cidrStr string, quorum uint) (*Allocator, address.CIDR) { peername, err := router.PeerNameFromString(name) if err != nil { panic(err) } - alloc, err := NewAllocator(peername, router.PeerUID(rand.Int63()), - "nick-"+name, cidrStr, quorum) + _, cidr, err := address.ParseCIDR(cidrStr) if err != nil { panic(err) } - return alloc + alloc := NewAllocator(peername, router.PeerUID(rand.Int63()), + "nick-"+name, cidr, quorum) + + return alloc, cidr } -func makeAllocatorWithMockGossip(t *testing.T, name string, universeCIDR string, quorum uint) *Allocator { - alloc := makeAllocator(name, universeCIDR, quorum) +func makeAllocatorWithMockGossip(t *testing.T, name string, universeCIDR string, quorum uint) (*Allocator, address.CIDR) { + alloc, subnet := makeAllocator(name, universeCIDR, quorum) gossip := &mockGossipComms{t: t, name: name} alloc.SetInterfaces(gossip) alloc.Start() - return alloc + return alloc, subnet } func (alloc *Allocator) claimRingForTesting(allocs ...*Allocator) { @@ -160,10 +162,10 @@ func (alloc *Allocator) claimRingForTesting(allocs ...*Allocator) { alloc.space.AddRanges(alloc.ring.OwnedRanges()) } -func (alloc *Allocator) NumFreeAddresses() address.Offset { +func (alloc *Allocator) NumFreeAddresses(subnet address.CIDR) address.Offset { resultChan := make(chan address.Offset) alloc.actionChan <- func() { - resultChan <- alloc.space.NumFreeAddressesInRange(alloc.defaultSubnet.Start+1, alloc.defaultSubnet.End()-1) + resultChan <- alloc.space.NumFreeAddressesInRange(subnet.Start+1, subnet.End()-1) } return <-resultChan } @@ -285,13 +287,15 @@ func (client TestGossipRouterClient) GossipBroadcast(update router.GossipData) e return client.router.GossipBroadcast(update) } -func makeNetworkOfAllocators(size int, cidr string) ([]*Allocator, TestGossipRouter) { +func makeNetworkOfAllocators(size int, cidr string) ([]*Allocator, TestGossipRouter, address.CIDR) { gossipRouter := TestGossipRouter{make(map[router.PeerName]chan gossipMessage), 0.0} allocs := make([]*Allocator, size) + var subnet address.CIDR for i := 0; i < size; i++ { - alloc := makeAllocator(fmt.Sprintf("%02d:00:00:02:00:00", i), + var alloc *Allocator + alloc, subnet = makeAllocator(fmt.Sprintf("%02d:00:00:02:00:00", i), cidr, uint(size/2+1)) alloc.SetInterfaces(gossipRouter.connect(alloc.ourName, alloc)) alloc.Start() @@ -300,7 +304,7 @@ func makeNetworkOfAllocators(size int, cidr string) ([]*Allocator, TestGossipRou gossipRouter.GossipBroadcast(allocs[size-1].Gossip()) time.Sleep(1000 * time.Millisecond) - return allocs, gossipRouter + return allocs, gossipRouter, subnet } func stopNetworkOfAllocators(allocs []*Allocator) { diff --git a/prog/weaver/main.go b/prog/weaver/main.go index ee4c403b..9608c4b7 100644 --- a/prog/weaver/main.go +++ b/prog/weaver/main.go @@ -17,6 +17,7 @@ import ( . "github.com/weaveworks/weave/common" "github.com/weaveworks/weave/common/updater" "github.com/weaveworks/weave/ipam" + "github.com/weaveworks/weave/ipam/address" weavenet "github.com/weaveworks/weave/net" weave "github.com/weaveworks/weave/router" ) @@ -139,8 +140,9 @@ func main() { log.Println("Our name is", router.Ourself) var allocator *ipam.Allocator + var defaultSubnet address.CIDR if iprangeCIDR != "" { - allocator = createAllocator(router, apiPath, iprangeCIDR, ipsubnetCIDR, determineQuorum(peerCount, peers)) + allocator, defaultSubnet = createAllocator(router, apiPath, iprangeCIDR, ipsubnetCIDR, determineQuorum(peerCount, peers)) } else if peerCount > 0 { log.Fatal("-initpeercount flag specified without -iprange") } else { @@ -156,7 +158,7 @@ func main() { // so there is no point in doing "weave launch -httpaddr ''". // This is here to support stand-alone use of weaver. if httpAddr != "" { - go handleHTTP(router, httpAddr, allocator) + go handleHTTP(router, httpAddr, allocator, defaultSubnet) } SignalHandlerLoop(router) @@ -196,23 +198,35 @@ func logFrameFunc(debug bool) weave.LogFrameFunc { } } -func createAllocator(router *weave.Router, apiPath string, ipRangeStr string, defaultSubnetStr string, quorum uint) *ipam.Allocator { - allocator, err := ipam.NewAllocator(router.Ourself.Peer.Name, router.Ourself.Peer.UID, router.Ourself.Peer.NickName, ipRangeStr, quorum) +func parseAndCheckCIDR(cidrStr string) address.CIDR { + _, cidr, err := address.ParseCIDR(cidrStr) if err != nil { log.Fatal(err) } - allocator.SetInterfaces(router.NewGossip("IPallocation", allocator)) - allocator.Start() + if cidr.Size() < ipam.MinSubnetSize { + log.Fatalf("Allocation range smaller than minimum size %d: %s", ipam.MinSubnetSize, cidrStr) + } + return cidr +} + +func createAllocator(router *weave.Router, apiPath string, ipRangeStr string, defaultSubnetStr string, quorum uint) (*ipam.Allocator, address.CIDR) { + ipRange := parseAndCheckCIDR(ipRangeStr) + defaultSubnet := ipRange if defaultSubnetStr != "" { - if err = allocator.SetDefaultSubnet(defaultSubnetStr); err != nil { - log.Fatal(err) + defaultSubnet = parseAndCheckCIDR(defaultSubnetStr) + if !ipRange.Overlaps(defaultSubnet) { + log.Fatalf("Default subnet %s out of bounds: %s", defaultSubnet, ipRange) } } - err = updater.Start(apiPath, allocator) + allocator := ipam.NewAllocator(router.Ourself.Peer.Name, router.Ourself.Peer.UID, router.Ourself.Peer.NickName, ipRange, quorum) + + allocator.SetInterfaces(router.NewGossip("IPallocation", allocator)) + allocator.Start() + err := updater.Start(apiPath, allocator) if err != nil { log.Fatal("Unable to start watcher", err) } - return allocator + return allocator, defaultSubnet } // Pick a quorum size heuristically based on the number of peer @@ -235,11 +249,11 @@ func determineQuorum(initPeerCountFlag int, peers []string) uint { return quorum } -func handleHTTP(router *weave.Router, httpAddr string, allocator *ipam.Allocator) { +func handleHTTP(router *weave.Router, httpAddr string, allocator *ipam.Allocator, defaultSubnet address.CIDR) { muxRouter := mux.NewRouter() if allocator != nil { - allocator.HandleHTTP(muxRouter) + allocator.HandleHTTP(muxRouter, defaultSubnet) } muxRouter.Methods("GET").Path("/status").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -247,6 +261,7 @@ func handleHTTP(router *weave.Router, httpAddr string, allocator *ipam.Allocator fmt.Fprintln(w, router.Status()) if allocator != nil { fmt.Fprintln(w, allocator.String()) + fmt.Fprintln(w, "Allocator default subnet:", defaultSubnet) } }) diff --git a/site/ipam.md b/site/ipam.md index b29cdca4..92a0142e 100644 --- a/site/ipam.md +++ b/site/ipam.md @@ -145,11 +145,12 @@ reports on the current status of the weave router and IP allocator: ```` weave router git-8f675f15c0b5 ... -Allocator universe 10.2.0.0/16 (default subnet 10.2.1.0/24) +Allocator universe 10.2.0.0/16 Owned Ranges: 10.2.1.1 -> 96:e9:e2:2e:2d:bc (host1) (v3) 10.2.1.128 -> ea:84:25:9b:31:2e (host2) (v3) 10.2.1.192 -> ea:6c:21:09:cf:f0 (host3) (v9) +Allocator default subnet: 10.2.1.0/24 ```` The first section covers the router; see the [troubleshooting