Merge pull request #2165 from weaveworks/issues/2164-less-deleting

Store 'checkAlive' flag in IPAM, so we don't delete non-container IDs
This commit is contained in:
Adam Harrison committed 2016-04-18 11:49:50 +01:00
commit 6ad9dac71a
7 files changed
+123 -74

No files matched your search

+2 -1
View File
@@ -15,6 +15,7 @@ type allocate struct {
resultChan chan<- allocateResult
ident string
r address.CIDR // Subnet we are trying to allocate within
isContainer bool
hasBeenCancelled func() bool
}
@@ -46,7 +47,7 @@ func (g *allocate) Try(alloc *Allocator) bool {
g.ident = addr.String()
}
alloc.debugln("Allocated", addr, "for", g.ident, "in", g.r)
alloc.addOwned(g.ident, address.MakeCIDR(g.r, addr))
alloc.addOwned(g.ident, address.MakeCIDR(g.r, addr), g.isContainer)
g.resultChan <- allocateResult{addr, nil}
return true
}
+57 -31
View File
@@ -41,6 +41,12 @@ type operation interface {
ForContainer(ident string) bool
}
// This type is persisted hence all fields exported
type ownedData struct {
IsContainer bool
Cidrs []address.CIDR
}
// Allocator brings together Ring and space.Set, and does the
// necessary plumbing. Runs as a single-threaded Actor, so no locks
// are used around data structures.
@@ -48,18 +54,18 @@ type Allocator struct {
actionChan chan<- func()
stopChan chan<- struct{}
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
owned map[string][]address.CIDR // who owns what addresses, indexed by container-ID
nicknames map[mesh.PeerName]string // so we can map nicknames for rmpeer
pendingAllocates []operation // held until we get some free space
pendingClaims []operation // held until we know who owns the space
pendingPrimes []operation // held while our ring is empty
dead map[string]time.Time // containers we heard were dead, and when
db db.DB // persistence
gossip mesh.Gossip // our link to the outside world for sending messages
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
owned map[string]ownedData // who owns what addresses, indexed by container-ID
nicknames map[mesh.PeerName]string // so we can map nicknames for rmpeer
pendingAllocates []operation // held until we get some free space
pendingClaims []operation // held until we know who owns the space
pendingPrimes []operation // held while our ring is empty
dead map[string]time.Time // containers we heard were dead, and when
db db.DB // persistence
gossip mesh.Gossip // our link to the outside world for sending messages
paxos paxos.Participant
awaitingConsensus bool
ticker *time.Ticker
@@ -94,7 +100,7 @@ func NewAllocator(config Config) *Allocator {
seed: config.Seed,
universe: config.Universe,
ring: ring.New(config.Universe.Start, config.Universe.End, config.OurName),
owned: make(map[string][]address.CIDR),
owned: make(map[string]ownedData),
db: config.Db,
paxos: participant,
nicknames: map[mesh.PeerName]string{config.OurName: config.OurNickname},
@@ -233,9 +239,15 @@ func (alloc *Allocator) Prime() {
// Allocate (Sync) - get new IP address for container with given name in range
// if there isn't any space in that range we block indefinitely
func (alloc *Allocator) Allocate(ident string, r address.CIDR, hasBeenCancelled func() bool) (address.Address, error) {
func (alloc *Allocator) Allocate(ident string, r address.CIDR, isContainer bool, hasBeenCancelled func() bool) (address.Address, error) {
resultChan := make(chan allocateResult)
op := &allocate{resultChan: resultChan, ident: ident, r: r, hasBeenCancelled: hasBeenCancelled}
op := &allocate{
resultChan: resultChan,
ident: ident,
r: r,
isContainer: isContainer,
hasBeenCancelled: hasBeenCancelled,
}
alloc.doOperation(op, &alloc.pendingAllocates)
result := <-resultChan
return result.addr, result.err
@@ -251,9 +263,15 @@ func (alloc *Allocator) Lookup(ident string, r address.Range) ([]address.CIDR, e
}
// Claim an address that we think we should own (Sync)
func (alloc *Allocator) Claim(ident string, cidr address.CIDR, noErrorOnUnknown bool) error {
func (alloc *Allocator) Claim(ident string, cidr address.CIDR, isContainer, noErrorOnUnknown bool) error {
resultChan := make(chan error)
op := &claim{resultChan: resultChan, ident: ident, cidr: cidr, noErrorOnUnknown: noErrorOnUnknown}
op := &claim{
resultChan: resultChan,
ident: ident,
cidr: cidr,
isContainer: isContainer,
noErrorOnUnknown: noErrorOnUnknown,
}
alloc.doOperation(op, &alloc.pendingClaims)
return <-resultChan
}
@@ -876,8 +894,8 @@ func (alloc *Allocator) loadPersistedData() {
alloc.space.UpdateRanges(alloc.ring.OwnedRanges())
}
if ownedFound {
for _, cidrs := range alloc.owned {
for _, cidr := range cidrs {
for _, d := range alloc.owned {
for _, cidr := range d.Cidrs {
alloc.space.Claim(cidr.Addr)
}
}
@@ -914,8 +932,11 @@ func (alloc *Allocator) hasOwned(ident string) bool {
}
// NB: addr must not be owned by ident already
func (alloc *Allocator) addOwned(ident string, cidr address.CIDR) {
alloc.owned[ident] = append(alloc.owned[ident], cidr)
func (alloc *Allocator) addOwned(ident string, cidr address.CIDR, isContainer bool) {
d := alloc.owned[ident]
d.IsContainer = isContainer
d.Cidrs = append(d.Cidrs, cidr)
alloc.owned[ident] = d
alloc.persistOwned()
}
@@ -923,17 +944,18 @@ func (alloc *Allocator) removeAllOwned(ident string) []address.CIDR {
a := alloc.owned[ident]
delete(alloc.owned, ident)
alloc.persistOwned()
return a
return a.Cidrs
}
func (alloc *Allocator) removeOwned(ident string, addrToFree address.Address) bool {
cidrs, _ := alloc.owned[ident]
for i, ownedCidr := range cidrs {
d := alloc.owned[ident]
for i, ownedCidr := range d.Cidrs {
if ownedCidr.Addr == addrToFree {
if len(cidrs) == 1 {
if len(d.Cidrs) == 1 {
delete(alloc.owned, ident)
} else {
alloc.owned[ident] = append(cidrs[:i], cidrs[i+1:]...)
d.Cidrs = append(d.Cidrs[:i], d.Cidrs[i+1:]...)
alloc.owned[ident] = d
}
alloc.persistOwned()
return true
@@ -944,7 +966,7 @@ func (alloc *Allocator) removeOwned(ident string, addrToFree address.Address) bo
func (alloc *Allocator) ownedInRange(ident string, r address.Range) []address.CIDR {
var c []address.CIDR
for _, cidr := range alloc.owned[ident] {
for _, cidr := range alloc.owned[ident].Cidrs {
if r.Contains(cidr.Addr) {
c = append(c, cidr)
}
@@ -953,8 +975,8 @@ func (alloc *Allocator) ownedInRange(ident string, r address.Range) []address.CI
}
func (alloc *Allocator) findOwner(addr address.Address) string {
for ident, cidrs := range alloc.owned {
for _, candidate := range cidrs {
for ident, d := range alloc.owned {
for _, candidate := range d.Cidrs {
if candidate.Addr == addr {
return ident
}
@@ -966,11 +988,15 @@ func (alloc *Allocator) findOwner(addr address.Address) string {
// For each ID in the 'owned' map, remove the entry if it isn't in the map
func (alloc *Allocator) syncOwned(ids map[string]struct{}) {
changed := false
for ident, cidrs := range alloc.owned {
for ident, d := range alloc.owned {
if !d.IsContainer {
continue
}
if _, found := ids[ident]; !found {
for _, cidr := range cidrs {
for _, cidr := range d.Cidrs {
alloc.space.Free(cidr.Addr)
}
alloc.debugf("Deleting old entry %s: %v", ident, d.Cidrs)
delete(alloc.owned, ident)
changed = true
}
+34 -35
View File
@@ -41,11 +41,11 @@ func TestAllocFree(t *testing.T) {
cidr2, _ := address.ParseCIDR(subnet2)
alloc.claimRingForTesting()
addr1, err := alloc.Allocate(container1, cidr1, returnFalse)
addr1, err := alloc.Allocate(container1, cidr1, true, returnFalse)
require.NoError(t, err)
require.Equal(t, testAddr1, addr1.String(), "address")
addr2, err := alloc.Allocate(container1, cidr2, returnFalse)
addr2, err := alloc.Allocate(container1, cidr2, true, returnFalse)
require.NoError(t, err)
require.Equal(t, testAddr2, addr2.String(), "address")
@@ -54,28 +54,28 @@ func TestAllocFree(t *testing.T) {
require.Equal(t, []address.CIDR{address.MakeCIDR(cidr1, addr1), address.MakeCIDR(cidr2, addr2)}, addrs)
// Ask for another address for a different container and check it's different
addr1b, _ := alloc.Allocate(container2, cidr1, returnFalse)
addr1b, _ := alloc.Allocate(container2, cidr1, true, returnFalse)
if addr1b.String() == testAddr1 {
t.Fatalf("Expected different address but got %s", addr1b.String())
}
// Ask for the first container again and we should get the same addresses again
addr1a, _ := alloc.Allocate(container1, cidr1, returnFalse)
addr1a, _ := alloc.Allocate(container1, cidr1, true, returnFalse)
require.Equal(t, testAddr1, addr1a.String(), "address")
addr2a, _ := alloc.Allocate(container1, cidr2, returnFalse)
addr2a, _ := alloc.Allocate(container1, cidr2, true, returnFalse)
require.Equal(t, testAddr2, addr2a.String(), "address")
// Now delete the first container, and we should get its addresses back
require.NoError(t, alloc.Delete(container1))
addr3, _ := alloc.Allocate(container3, cidr1, returnFalse)
addr3, _ := alloc.Allocate(container3, cidr1, true, returnFalse)
require.Equal(t, testAddr1, addr3.String(), "address")
addr4, _ := alloc.Allocate(container3, cidr2, returnFalse)
addr4, _ := alloc.Allocate(container3, cidr2, true, returnFalse)
require.Equal(t, testAddr2, addr4.String(), "address")
alloc.ContainerDied(container2)
// Resurrect
addr1c, err := alloc.Allocate(container2, cidr1, returnFalse)
addr1c, err := alloc.Allocate(container2, cidr1, true, returnFalse)
require.NoError(t, err)
require.Equal(t, addr1b, addr1c, "address")
@@ -109,7 +109,7 @@ func TestBootstrap(t *testing.T) {
ExpectBroadcastMessage(alloc1, nil) // alloc1 will try to form consensus
done := make(chan bool)
go func() {
alloc1.Allocate("somecontainer", subnet, returnFalse)
alloc1.Allocate("somecontainer", subnet, true, returnFalse)
done <- true
}()
time.Sleep(100 * time.Millisecond)
@@ -170,32 +170,31 @@ func TestAllocatorClaim(t *testing.T) {
addr1, _ := address.ParseCIDR(testAddr1)
// First claim should trigger "dunno, I'm going to wait"
err := alloc.Claim(container3, addr1, true)
err := alloc.Claim(container3, addr1, true, true)
require.NoError(t, err)
// Do one allocate to ensure paxos is all done
alloc.Allocate("unused", subnet, returnFalse)
alloc.Prime()
// Do an allocate on the other peer, which we will try to claim later
addrx, err := allocs[0].Allocate(container1, subnet, returnFalse)
addrx, err := allocs[0].Allocate(container1, subnet, true, returnFalse)
// Now try the claim again
err = alloc.Claim(container3, addr1, true)
err = alloc.Claim(container3, addr1, true, true)
require.NoError(t, err)
// Check we get this address back if we try an allocate
addr3, _ := alloc.Allocate(container3, subnet, returnFalse)
addr3, _ := alloc.Allocate(container3, subnet, true, returnFalse)
require.Equal(t, testAddr1, address.MakeCIDR(subnet, addr3).String(), "address")
// one more claim should still work
err = alloc.Claim(container3, addr1, true)
err = alloc.Claim(container3, addr1, true, true)
require.NoError(t, err)
// claim for a different container should fail
err = alloc.Claim(container1, addr1, true)
err = alloc.Claim(container1, addr1, true, true)
require.Error(t, err)
// claiming the address allocated on the other peer should fail
err = alloc.Claim(container1, address.MakeCIDR(subnet, addrx), true)
err = alloc.Claim(container1, address.MakeCIDR(subnet, addrx), true, true)
require.Error(t, err, "claiming address allocated on other peer should fail")
// Check an address outside of our universe
addr2, _ := address.ParseCIDR(testAddr2)
err = alloc.Claim(container1, addr2, true)
err = alloc.Claim(container1, addr2, true, true)
require.NoError(t, err)
}
@@ -230,9 +229,9 @@ func TestCancel(t *testing.T) {
alloc1.OnGossipBroadcast(alloc2.ourName, alloc2.Encode())
// Get some IPs, so each allocator has some space
res1, _ := alloc1.Allocate("foo", subnet, returnFalse)
res1, _ := alloc1.Allocate("foo", subnet, true, returnFalse)
common.Log.Debugf("res1 = %s", res1.String())
res2, _ := alloc2.Allocate("bar", subnet, returnFalse)
res2, _ := alloc2.Allocate("bar", subnet, true, returnFalse)
common.Log.Debugf("res2 = %s", res2.String())
if res1 == res2 {
require.FailNow(t, "Error: got same ips!")
@@ -244,12 +243,12 @@ func TestCancel(t *testing.T) {
// Use up all the IPs that alloc1 owns, so the allocation after this will prompt a request to alloc2
for i := 0; alloc1.NumFreeAddresses(subnet.HostRange()) > 0; i++ {
alloc1.Allocate(fmt.Sprintf("tmp%d", i), subnet, returnFalse)
alloc1.Allocate(fmt.Sprintf("tmp%d", i), subnet, true, returnFalse)
}
cancelChan := make(chan bool, 1)
doneChan := make(chan bool)
go func() {
_, ok := alloc1.Allocate("baz", subnet,
_, ok := alloc1.Allocate("baz", subnet, true,
func() bool {
select {
case <-cancelChan:
@@ -284,7 +283,7 @@ func TestCancelOnDied(t *testing.T) {
doneChan := make(chan bool)
f := func() {
_, ok := alloc1.Allocate(container1, subnet, returnFalse)
_, ok := alloc1.Allocate(container1, subnet, true, returnFalse)
doneChan <- ok == nil
}
@@ -315,11 +314,11 @@ func TestGossipShutdown(t *testing.T) {
defer alloc.Stop()
alloc.claimRingForTesting()
alloc.Allocate(container1, subnet, returnFalse)
alloc.Allocate(container1, subnet, true, returnFalse)
alloc.Shutdown()
_, err := alloc.Allocate(container2, subnet, returnFalse) // trying to allocate after shutdown should fail
_, err := alloc.Allocate(container2, subnet, true, returnFalse) // trying to allocate after shutdown should fail
require.False(t, err == nil, "no address")
CheckAllExpectedMessagesSent(alloc)
@@ -333,10 +332,10 @@ func TestTransfer(t *testing.T) {
alloc1 := allocs[1]
alloc2 := allocs[2]
_, err := alloc1.Allocate("foo", subnet, returnFalse)
_, err := alloc1.Allocate("foo", subnet, true, returnFalse)
require.True(t, err == nil, "Failed to get address")
_, err = alloc2.Allocate("bar", subnet, returnFalse)
_, err = alloc2.Allocate("bar", subnet, true, returnFalse)
require.True(t, err == nil, "Failed to get address")
// simulation of periodic gossip
@@ -360,7 +359,7 @@ func TestTransfer(t *testing.T) {
require.Equal(t, address.Count(1024), alloc0.NumFreeAddresses(subnet.Range()))
_, err = alloc0.Allocate("foo", subnet, returnFalse)
_, err = alloc0.Allocate("foo", subnet, true, returnFalse)
require.True(t, err == nil, "Failed to get address")
alloc0.Stop()
}
@@ -373,7 +372,7 @@ func TestFakeRouterSimple(t *testing.T) {
alloc1 := allocs[0]
//alloc2 := allocs[1]
_, err := alloc1.Allocate("foo", subnet, returnFalse)
_, err := alloc1.Allocate("foo", subnet, true, returnFalse)
require.NoError(t, err, "Failed to get address")
}
@@ -454,7 +453,7 @@ func TestAllocatorFuzz(t *testing.T) {
allocIndex := rand.Int31n(nodes)
alloc := allocs[allocIndex]
//common.Log.Infof("Allocate: asking allocator %d", allocIndex)
addr, err := alloc.Allocate(name, subnet, returnFalse)
addr, err := alloc.Allocate(name, subnet, true, returnFalse)
if err != nil {
panic(fmt.Sprintf("Could not allocate addr"))
@@ -511,7 +510,7 @@ func TestAllocatorFuzz(t *testing.T) {
//common.Log.Infof("Asking for %s (%s) on allocator %d again", res.name, addr, res.alloc)
newAddr, _ := alloc.Allocate(res.name, subnet, returnFalse)
newAddr, _ := alloc.Allocate(res.name, subnet, true, returnFalse)
oldAddr, _ := address.ParseIP(addr)
if newAddr != oldAddr {
panic(fmt.Sprintf("Got different address for repeat request for %s: %s != %s", res.name, newAddr, oldAddr))
@@ -532,7 +531,7 @@ func TestAllocatorFuzz(t *testing.T) {
addressIndex := rand.Int31n(int32(subnet.Size()))
alloc := allocs[allocIndex]
addr := address.Add(subnet.Addr, address.Offset(addressIndex))
err := alloc.Claim(name, address.MakeCIDR(subnet, addr), true)
err := alloc.Claim(name, address.MakeCIDR(subnet, addr), true, true)
if err == nil {
noteAllocation(allocIndex, name, addr)
}
@@ -598,7 +597,7 @@ func TestSpaceRequest(t *testing.T) {
defer stopNetworkOfAllocators(allocs, router)
alloc1 := allocs[0]
addr, err := alloc1.Allocate(container1, subnet, returnFalse)
addr, err := alloc1.Allocate(container1, subnet, true, returnFalse)
require.Nil(t, err, "")
// free it again so the donation splits the range neatly
err = alloc1.Free(container1, addr)
@@ -610,7 +609,7 @@ func TestSpaceRequest(t *testing.T) {
alloc2.SetInterfaces(router.Connect(alloc2.ourName, alloc2))
alloc2.Start()
defer alloc2.Stop()
alloc2.Allocate(container2, subnet, returnFalse)
alloc2.Allocate(container2, subnet, true, returnFalse)
// Test whether the universe has been split into two equal halves (GH #2009)
require.Equal(t, cidrRanges("10.32.0.0/13"), alloc1.OwnedRanges(), "")
+3 -2
View File
@@ -13,6 +13,7 @@ type claim struct {
resultChan chan<- error
ident string
cidr address.CIDR
isContainer bool
noErrorOnUnknown bool
}
@@ -35,7 +36,7 @@ func (c *claim) Try(alloc *Allocator) bool {
if !alloc.ring.Contains(c.cidr.Addr) {
// Address not within our universe; assume user knows what they are doing
alloc.infof("Address %s claimed by %s - not in our range", c.cidr, c.ident)
alloc.addOwned(c.ident, c.cidr)
alloc.addOwned(c.ident, c.cidr, c.isContainer)
c.sendResult(nil)
return true
}
@@ -79,7 +80,7 @@ func (c *claim) Try(alloc *Allocator) bool {
if c.ident == "_" { // Special "I don't have a unique ID" identifier
c.ident = c.cidr.String()
}
alloc.addOwned(c.ident, c.cidr)
alloc.addOwned(c.ident, c.cidr, c.isContainer)
c.sendResult(nil)
} else {
c.sendResult(err)
+3 -2
View File
@@ -40,7 +40,7 @@ func writeAddresses(w http.ResponseWriter, cidrs []address.CIDR) {
func (alloc *Allocator) handleHTTPAllocate(dockerCli *docker.Client, w http.ResponseWriter, ident string, checkAlive bool, subnet address.CIDR) {
closedChan := w.(http.CloseNotifier).CloseNotify()
addr, err := alloc.Allocate(ident, subnet,
addr, err := alloc.Allocate(ident, subnet, checkAlive,
func() bool {
select {
case <-closedChan:
@@ -74,7 +74,8 @@ func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CID
vars := mux.Vars(r)
if cidr, ok := parseCIDR(w, vars["ip"]+"/"+vars["prefixlen"], false); ok {
noErrorOnUnknown := r.FormValue("noErrorOnUnknown") == "true"
if err := alloc.Claim(vars["id"], cidr, noErrorOnUnknown); err != nil {
checkAlive := r.FormValue("check-alive") == "true"
if err := alloc.Claim(vars["id"], cidr, checkAlive, noErrorOnUnknown); err != nil {
badRequest(w, fmt.Errorf("Unable to claim: %s", err))
return
}
+14
View File
@@ -13,19 +13,26 @@ launch_router_with_db() {
launch_router_with_db $HOST1 $HOST2
launch_router_with_db $HOST2 $HOST1
EXPOSE=$(weave_on $HOST1 expose)
start_container $HOST1 --name=c1
C1=$(container_ip $HOST1 c1)
start_container $HOST2 --name=c2
assert_raises "exec_on $HOST2 c2 $PING $C1"
start_container $HOST2 --name=c3a
C3a=$(container_ip $HOST2 c3a)
stop_weave_on $HOST1
stop_weave_on $HOST2
# Stop one container while weave is down; the address should be freed on launch
docker_on $HOST2 rm -f c3a
# Start just HOST2; if nothing persisted it would form its own ring
launch_router_with_db $HOST2
start_container $HOST2 --name=c3
C3=$(container_ip $HOST2 c3)
assert_raises "[ $C3 != $C1 ]"
assert_raises "[ $C3 = $C3a ]"
# Restart HOST1 and see if it remembers to connect to HOST2
#
@@ -34,4 +41,11 @@ assert_raises "[ $C3 != $C1 ]"
# launch_router_with_db $HOST1
# assert_raises "exec_on $HOST2 c2 $PING $C1"
# Restart the router without going through the reclaim procedure on launch
docker_on $HOST1 restart weave
# Check that weave:expose address hasn't been lost
start_container $HOST1 --name=c4
C4=$(container_ip $HOST1 c4)
assert_raises "[ $C4 != $EXPOSE ]"
end_suite
+10 -3
View File
@@ -1379,6 +1379,12 @@ check_overlap() {
# Claim addresses for a container in IPAM. Expects to be called from
# with_container_addresses.
ipam_reclaim() {
for CIDR in $3 ; do
http_call $HTTP_ADDR PUT "/ip/$1/$CIDR?noErrorOnUnknown=true&?check-alive=true" || true
done
}
ipam_reclaim_no_check_alive() {
for CIDR in $3 ; do
http_call $HTTP_ADDR PUT /ip/$1/$CIDR?noErrorOnUnknown=true || true
done
@@ -1433,7 +1439,7 @@ ipam_cidrs() {
# Assignment of a plain IP address; warn if it clashes but carry on
check_overlap $arg || true
# Abort on failure, but not 4 (=404), which means IPAM is disabled
when_weave_running http_call $HTTP_ADDR PUT /ip/$CONTAINER_ID/$arg || [ $? -eq 4 ] || return 1
when_weave_running http_call $HTTP_ADDR PUT /ip/$CONTAINER_ID/$arg$CHECK_ALIVE || [ $? -eq 4 ] || return 1
fi
ALL_CIDRS="$ALL_CIDRS $arg"
fi
@@ -1740,7 +1746,8 @@ fetch_router_args() {
populate_router() {
if [ -n "$IPRANGE" ] ; then
# Tell the newly-started weave IP allocator about existing weave IPs
with_container_addresses ipam_reclaim weave:expose $(docker ps -q --no-trunc)
with_container_addresses ipam_reclaim_no_check_alive weave:expose
with_container_addresses ipam_reclaim $(docker ps -q --no-trunc)
fi
if [ -z "$NO_DNS_OPT" ] ; then
# Tell the newly-started weaveDNS about existing weave IPs
@@ -2170,7 +2177,7 @@ EOF
RES=$(docker restart $1)
CONTAINER=$(container_id $1)
for CIDR in $ALL_CIDRS ; do
call_weave PUT /ip/$CONTAINER/$CIDR
call_weave PUT /ip/$CONTAINER/$CIDR?check-alive=true
done
with_container_netns_or_die $1 attach $ALL_CIDRS
when_weave_running with_container_fqdn $1 put_dns_fqdn $ALL_CIDRS