Allow specification of initial IPAM ring via --ipam-seed

This commit is contained in:
Adam Harrison committed 2016-03-21 09:49:54 +00:00
1 parent 18ddf50b4c
commit e9081c6607
8 files changed
+126 -28

No files matched your search

+6 -3
View File
@@ -9,7 +9,7 @@ import (
)
type DB interface {
Load(string, interface{}) error
Load(string, interface{}) (bool, error)
Save(string, interface{}) error
}
@@ -50,11 +50,13 @@ func NewBoltDB(dbPathname string) (*BoltDB, error) {
return &BoltDB{db: db}, err
}
func (d *BoltDB) Load(ident string, data interface{}) error {
return d.db.View(func(tx *bolt.Tx) error {
func (d *BoltDB) Load(ident string, data interface{}) (bool, error) {
found := true
err := d.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket(topBucket)
v := b.Get([]byte(ident))
if v == nil {
found = false
return nil
}
reader := bytes.NewReader(v)
@@ -62,6 +64,7 @@ func (d *BoltDB) Load(ident string, data interface{}) error {
err := decoder.Decode(data)
return err
})
return found, err
}
func (d *BoltDB) Save(ident string, data interface{}) error {
+39 -18
View File
@@ -47,6 +47,7 @@ type operation interface {
type Allocator struct {
actionChan chan<- func()
ourName mesh.PeerName
seed []mesh.PeerName // optional user supplied ring seed
universe address.Range // superset of all ranges
ring *ring.Ring // information on ranges owned by all peers
space space.Space // more detail on ranges owned by us
@@ -70,6 +71,7 @@ type Config struct {
OurName mesh.PeerName
OurUID mesh.PeerUID
OurNickname string
Seed []mesh.PeerName
Universe address.Range
Quorum uint
Db db.DB
@@ -80,6 +82,7 @@ type Config struct {
func NewAllocator(config Config) *Allocator {
return &Allocator{
ourName: config.OurName,
seed: config.Seed,
universe: config.Universe,
ring: ring.New(config.Universe.Start, config.Universe.End, config.OurName),
owned: make(map[string][]address.CIDR),
@@ -835,31 +838,49 @@ func (alloc *Allocator) persistRing() {
}
func (alloc *Allocator) loadPersistedData() {
checkPeerName := alloc.ourName
if err := alloc.db.Load(nameIdent, &checkPeerName); err != nil {
var checkPeerName mesh.PeerName
nameFound, err := alloc.db.Load(nameIdent, &checkPeerName)
if err != nil {
alloc.fatalf("Error loading persisted peer name: %s", err)
return
}
if checkPeerName != alloc.ourName {
ringFound, err := alloc.db.Load(ringIdent, &alloc.ring)
if err != nil {
alloc.fatalf("Error loading persisted IPAM data: %s", err)
}
ownedFound, err := alloc.db.Load(ownedIdent, &alloc.owned)
if err != nil {
alloc.fatalf("Error loading persisted address data: %s", err)
}
if nameFound {
if checkPeerName == alloc.ourName {
if ringFound {
if len(alloc.seed) != 0 {
alloc.infof("Found persisted IPAM data, ignoring supplied IPAM seed")
}
alloc.space.UpdateRanges(alloc.ring.OwnedRanges())
}
if ownedFound {
for _, cidrs := range alloc.owned {
for _, cidr := range cidrs {
alloc.space.Claim(cidr.Addr)
}
}
}
return
}
alloc.infof("Deleting persisted data for peername %s", checkPeerName)
alloc.persistRing()
alloc.persistOwned()
return
}
if err := alloc.db.Load(ringIdent, &alloc.ring); err != nil {
alloc.fatalf("Error loading persisted ring data: %s", err)
}
if alloc.ring != nil {
alloc.space.UpdateRanges(alloc.ring.OwnedRanges())
}
if err := alloc.db.Load(ownedIdent, &alloc.owned); err != nil {
alloc.fatalf("Error loading persisted address data: %s", err)
}
for _, cidrs := range alloc.owned {
for _, cidr := range cidrs {
alloc.space.Claim(cidr.Addr)
}
if len(alloc.seed) != 0 {
alloc.infof("Initialising with supplied IPAM seed")
alloc.createRing(alloc.seed)
} else {
alloc.infof("Initialising via deferred consensus")
}
}
func (alloc *Allocator) persistOwned() {
+2 -2
View File
@@ -125,8 +125,8 @@ func CheckAllExpectedMessagesSent(allocs ...*Allocator) {
type mockDB struct{}
func (d *mockDB) Load(_ string, _ interface{}) error { return nil }
func (d *mockDB) Save(_ string, _ interface{}) error { return nil }
func (d *mockDB) Load(_ string, _ interface{}) (bool, error) { return false, nil }
func (d *mockDB) Save(_ string, _ interface{}) error { return nil }
func makeAllocator(name string, cidrStr string, quorum uint) (*Allocator, address.CIDR) {
peername, err := mesh.PeerNameFromString(name)
+25 -4
View File
@@ -91,6 +91,7 @@ func main() {
ipsubnetCIDR string
peerCount int
observer bool
ipamSeedStr string
dockerAPI string
peers []string
noDNS bool
@@ -125,6 +126,7 @@ func main() {
mflag.StringVar(&ipsubnetCIDR, []string{"#ipsubnet", "#-ipsubnet", "-ipalloc-default-subnet"}, "", "subnet to allocate within by default, in CIDR notation")
mflag.IntVar(&peerCount, []string{"#initpeercount", "#-initpeercount", "-init-peer-count"}, 0, "number of peers in network (for IP address allocation)")
mflag.BoolVar(&observer, []string{"-observer"}, false, "do not participate in paxos, observe only")
mflag.StringVar(&ipamSeedStr, []string{"-ipam-seed"}, "", "Comma-separated list of peer names amongst which address space is shared initially")
mflag.StringVar(&dockerAPI, []string{"#api", "#-api", "-docker-api"}, defaultDockerHost, "Docker API endpoint")
mflag.BoolVar(&noDNS, []string{"-no-dns"}, false, "disable DNS server")
mflag.StringVar(&dnsConfig.Domain, []string{"-dns-domain"}, nameserver.DefaultDomain, "local domain to server requests for")
@@ -229,11 +231,11 @@ func main() {
allocator *ipam.Allocator
defaultSubnet address.CIDR
)
if observer && peerCount > 0 {
Log.Fatal("At most one of --observer and --init-peer-count must be specified.")
if observer && peerCount > 0 || peerCount > 0 && len(ipamSeedStr) > 0 || len(ipamSeedStr) > 0 && observer {
Log.Fatal("At most one of --observer, --ipam-seed or --init-peer-count may be specified.")
}
if iprangeCIDR != "" {
allocator, defaultSubnet = createAllocator(router.Router, iprangeCIDR, ipsubnetCIDR, determineQuorum(observer, peerCount, peers), db, isKnownPeer)
allocator, defaultSubnet = createAllocator(router.Router, ipamSeedStr, iprangeCIDR, ipsubnetCIDR, determineQuorum(observer, peerCount, peers), db, isKnownPeer)
observeContainers(allocator)
ids, err := dockerCli.AllContainerIDs()
checkFatal(err)
@@ -359,7 +361,8 @@ func parseAndCheckCIDR(cidrStr string) address.CIDR {
return cidr
}
func createAllocator(router *mesh.Router, ipRangeStr string, defaultSubnetStr string, quorum uint, db db.DB, isKnownPeer func(mesh.PeerName) bool) (*ipam.Allocator, address.CIDR) {
func createAllocator(router *mesh.Router, ipamSeedStr, ipRangeStr string, defaultSubnetStr string, quorum uint, db db.DB, isKnownPeer func(mesh.PeerName) bool) (*ipam.Allocator, address.CIDR) {
seed := parseIPAMSeed(ipamSeedStr)
ipRange := parseAndCheckCIDR(ipRangeStr)
defaultSubnet := ipRange
if defaultSubnetStr != "" {
@@ -373,6 +376,7 @@ func createAllocator(router *mesh.Router, ipRangeStr string, defaultSubnetStr st
OurName: router.Ourself.Peer.Name,
OurUID: router.Ourself.Peer.UID,
OurNickname: router.Ourself.Peer.NickName,
Seed: seed,
Universe: ipRange.Range(),
Quorum: quorum,
Db: db,
@@ -469,6 +473,23 @@ func parseTrustedSubnets(trustedSubnetStr string) []*net.IPNet {
return trustedSubnets
}
func parseIPAMSeed(ipamSeed string) []mesh.PeerName {
peerNames := []mesh.PeerName{}
if ipamSeed == "" {
return peerNames
}
for _, peerNameStr := range strings.Split(ipamSeed, ",") {
peerName, err := mesh.PeerNameFromUserInput(peerNameStr)
if err != nil {
Log.Fatal("Unable to parse IPAM seed: ", err)
}
peerNames = append(peerNames, peerName)
}
return peerNames
}
func listenAndServeHTTP(httpAddr string, muxRouter *mux.Router) {
protocol := "tcp"
if strings.HasPrefix(httpAddr, "/") {
+1 -1
View File
@@ -230,7 +230,7 @@ func (router *NetworkRouter) ForgetConnections(peers []string) {
func (router *NetworkRouter) InitialPeers(peers []string) ([]string, error) {
var storedPeers []string
if err := router.db.Load(peersIdent, &storedPeers); err != nil {
if _, err := router.db.Load(peersIdent, &storedPeers); err != nil {
return nil, err
}
+30
View File
@@ -22,6 +22,36 @@ The following automatic IP address managment topics are discussed:
### <a name="initialization"></a>Initializing Peers on a Weave Network
There are two options for initialising IPAM - with a statically
configured seed or automatically via one-off consensus. The two
options have different tradeoffs, so pick the one that suits your
deployment best.
#### <a name="seed"></a>Via Seed
Configuration via seed requires you to provide a list of peer names
(via the `--ipam-seed` parameter) amongst which the address space will
be shared initially - this means that you will also have to specify
peer names explicitly on launch:
host1$ weave launch --name ::1 --ipam-seed ::1,::2,::3
host2$ weave launch --name ::2 --ipam-seed ::1,::2,::3
host3$ weave launch --name ::3 --ipam-seed ::1,::2,::3
In this configuration each peer knows in advance how the address space
has been divided up, and will be able to perform allocations from the
outset even under conditions of partition - no consensus is required.
#### <a name="consensus"></a>Via One-off Consensus
Alternatively, you can let weave determine the seed automatically via
a consensus algorithm; since you don't need to provide it with a list
of peer names anymore you can also let weave generate those randomly
for you too. However, in order for weave to form a single consensus
reliably you must now instead tell each peer how many peers there are
in total either by listing them as target peers or via the
`--init-peer-count` parameter.
Just once, when the first automatic IP address allocation is requested
in the whole network, Weave Net needs a majority of peers to be present in
order to avoid formation of isolated groups, which can lead to
+22
View File
@@ -0,0 +1,22 @@
#! /bin/bash
. ./config.sh
start_suite "Specify initial IPAM seed"
# Launch two disconnected routers
weave_on $HOST1 launch --name ::1 --ipam-seed ::1,::2
weave_on $HOST2 launch --name ::2 --ipam-seed ::1,::2
# Ensure allocations can proceed
assert_raises "timeout 10 cat <( start_container $HOST1 --name c1)"
assert_raises "timeout 10 cat <( start_container $HOST2 --name c2)"
# Connect routers
weave_on $HOST2 connect $HOST1
# Check connectivity
assert_raises "exec_on $HOST1 c1 $PING c2"
assert_raises "exec_on $HOST2 c2 $PING c1"
end_suite
+1
View File
@@ -40,6 +40,7 @@ weave --help | help
weave launch <same arguments as 'weave launch-router'>
launch-router [--password <pass>] [--name <mac>] [--nickname <nickname>]
[--ipam-seed <mac>,...]
[--ipalloc-range <cidr> [--ipalloc-default-subnet <cidr>]]
[--init-peer-count <count>] [--no-discovery]
[--trusted-subnets <cidr>,...] <peer> ...