p2p/simulations: add mocker functionality (#15207)
This commit adds mocker functionality to p2p/simulations. A mocker allows to starting/stopping of nodes via the HTTP API.
This commit is contained in:
parent
3da1bf8ca1
commit
fd777bb210
35
p2p/simulations/adapters/state.go
Normal file
35
p2p/simulations/adapters/state.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
package adapters
|
||||||
|
|
||||||
|
type SimStateStore struct {
|
||||||
|
m map[string][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *SimStateStore) Load(s string) ([]byte, error) {
|
||||||
|
return self.m[s], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *SimStateStore) Save(s string, data []byte) error {
|
||||||
|
self.m[s] = data
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSimStateStore() *SimStateStore {
|
||||||
|
return &SimStateStore{
|
||||||
|
make(map[string][]byte),
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
@ -265,6 +266,8 @@ func (c *Client) Send(method, path string, in, out interface{}) error {
|
|||||||
type Server struct {
|
type Server struct {
|
||||||
router *httprouter.Router
|
router *httprouter.Router
|
||||||
network *Network
|
network *Network
|
||||||
|
mockerStop chan struct{} // when set, stops the current mocker
|
||||||
|
mockerMtx sync.Mutex // synchronises access to the mockerStop field
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer returns a new simulation API server
|
// NewServer returns a new simulation API server
|
||||||
@ -278,6 +281,10 @@ func NewServer(network *Network) *Server {
|
|||||||
s.GET("/", s.GetNetwork)
|
s.GET("/", s.GetNetwork)
|
||||||
s.POST("/start", s.StartNetwork)
|
s.POST("/start", s.StartNetwork)
|
||||||
s.POST("/stop", s.StopNetwork)
|
s.POST("/stop", s.StopNetwork)
|
||||||
|
s.POST("/mocker/start", s.StartMocker)
|
||||||
|
s.POST("/mocker/stop", s.StopMocker)
|
||||||
|
s.GET("/mocker", s.GetMockers)
|
||||||
|
s.POST("/reset", s.ResetNetwork)
|
||||||
s.GET("/events", s.StreamNetworkEvents)
|
s.GET("/events", s.StreamNetworkEvents)
|
||||||
s.GET("/snapshot", s.CreateSnapshot)
|
s.GET("/snapshot", s.CreateSnapshot)
|
||||||
s.POST("/snapshot", s.LoadSnapshot)
|
s.POST("/snapshot", s.LoadSnapshot)
|
||||||
@ -318,6 +325,59 @@ func (s *Server) StopNetwork(w http.ResponseWriter, req *http.Request) {
|
|||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartMocker starts the mocker node simulation
|
||||||
|
func (s *Server) StartMocker(w http.ResponseWriter, req *http.Request) {
|
||||||
|
s.mockerMtx.Lock()
|
||||||
|
defer s.mockerMtx.Unlock()
|
||||||
|
if s.mockerStop != nil {
|
||||||
|
http.Error(w, "mocker already running", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mockerType := req.FormValue("mocker-type")
|
||||||
|
mockerFn := LookupMocker(mockerType)
|
||||||
|
if mockerFn == nil {
|
||||||
|
http.Error(w, fmt.Sprintf("unknown mocker type %q", mockerType), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nodeCount, err := strconv.Atoi(req.FormValue("node-count"))
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "invalid node-count provided", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.mockerStop = make(chan struct{})
|
||||||
|
go mockerFn(s.network, s.mockerStop, nodeCount)
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopMocker stops the mocker node simulation
|
||||||
|
func (s *Server) StopMocker(w http.ResponseWriter, req *http.Request) {
|
||||||
|
s.mockerMtx.Lock()
|
||||||
|
defer s.mockerMtx.Unlock()
|
||||||
|
if s.mockerStop == nil {
|
||||||
|
http.Error(w, "stop channel not initialized", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
close(s.mockerStop)
|
||||||
|
s.mockerStop = nil
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMockerList returns a list of available mockers
|
||||||
|
func (s *Server) GetMockers(w http.ResponseWriter, req *http.Request) {
|
||||||
|
|
||||||
|
list := GetMockerList()
|
||||||
|
s.JSON(w, http.StatusOK, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetNetwork resets all properties of a network to its initial (empty) state
|
||||||
|
func (s *Server) ResetNetwork(w http.ResponseWriter, req *http.Request) {
|
||||||
|
s.network.Reset()
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
// StreamNetworkEvents streams network events as a server-sent-events stream
|
// StreamNetworkEvents streams network events as a server-sent-events stream
|
||||||
func (s *Server) StreamNetworkEvents(w http.ResponseWriter, req *http.Request) {
|
func (s *Server) StreamNetworkEvents(w http.ResponseWriter, req *http.Request) {
|
||||||
events := make(chan *Event)
|
events := make(chan *Event)
|
||||||
|
192
p2p/simulations/mocker.go
Normal file
192
p2p/simulations/mocker.go
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package simulations simulates p2p networks.
|
||||||
|
// A mocker simulates starting and stopping real nodes in a network.
|
||||||
|
package simulations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
|
)
|
||||||
|
|
||||||
|
//a map of mocker names to its function
|
||||||
|
var mockerList = map[string]func(net *Network, quit chan struct{}, nodeCount int){
|
||||||
|
"startStop": startStop,
|
||||||
|
"probabilistic": probabilistic,
|
||||||
|
"boot": boot,
|
||||||
|
}
|
||||||
|
|
||||||
|
//Lookup a mocker by its name, returns the mockerFn
|
||||||
|
func LookupMocker(mockerType string) func(net *Network, quit chan struct{}, nodeCount int) {
|
||||||
|
return mockerList[mockerType]
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get a list of mockers (keys of the map)
|
||||||
|
//Useful for frontend to build available mocker selection
|
||||||
|
func GetMockerList() []string {
|
||||||
|
list := make([]string, 0, len(mockerList))
|
||||||
|
for k := range mockerList {
|
||||||
|
list = append(list, k)
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
//The boot mockerFn only connects the node in a ring and doesn't do anything else
|
||||||
|
func boot(net *Network, quit chan struct{}, nodeCount int) {
|
||||||
|
_, err := connectNodesInRing(net, nodeCount)
|
||||||
|
if err != nil {
|
||||||
|
panic("Could not startup node network for mocker")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//The startStop mockerFn stops and starts nodes in a defined period (ticker)
|
||||||
|
func startStop(net *Network, quit chan struct{}, nodeCount int) {
|
||||||
|
nodes, err := connectNodesInRing(net, nodeCount)
|
||||||
|
if err != nil {
|
||||||
|
panic("Could not startup node network for mocker")
|
||||||
|
}
|
||||||
|
tick := time.NewTicker(10 * time.Second)
|
||||||
|
defer tick.Stop()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-quit:
|
||||||
|
log.Info("Terminating simulation loop")
|
||||||
|
return
|
||||||
|
case <-tick.C:
|
||||||
|
id := nodes[rand.Intn(len(nodes))]
|
||||||
|
log.Info("stopping node", "id", id)
|
||||||
|
if err := net.Stop(id); err != nil {
|
||||||
|
log.Error("error stopping node", "id", id, "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-quit:
|
||||||
|
log.Info("Terminating simulation loop")
|
||||||
|
return
|
||||||
|
case <-time.After(3 * time.Second):
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("starting node", "id", id)
|
||||||
|
if err := net.Start(id); err != nil {
|
||||||
|
log.Error("error starting node", "id", id, "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//The probabilistic mocker func has a more probabilistic pattern
|
||||||
|
//(the implementation could probably be improved):
|
||||||
|
//nodes are connected in a ring, then a varying number of random nodes is selected,
|
||||||
|
//mocker then stops and starts them in random intervals, and continues the loop
|
||||||
|
func probabilistic(net *Network, quit chan struct{}, nodeCount int) {
|
||||||
|
nodes, err := connectNodesInRing(net, nodeCount)
|
||||||
|
if err != nil {
|
||||||
|
panic("Could not startup node network for mocker")
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-quit:
|
||||||
|
log.Info("Terminating simulation loop")
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
var lowid, highid int
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
randWait := time.Duration(rand.Intn(5000)+1000) * time.Millisecond
|
||||||
|
rand1 := rand.Intn(nodeCount - 1)
|
||||||
|
rand2 := rand.Intn(nodeCount - 1)
|
||||||
|
if rand1 < rand2 {
|
||||||
|
lowid = rand1
|
||||||
|
highid = rand2
|
||||||
|
} else if rand1 > rand2 {
|
||||||
|
highid = rand1
|
||||||
|
lowid = rand2
|
||||||
|
} else {
|
||||||
|
if rand1 == 0 {
|
||||||
|
rand2 = 9
|
||||||
|
} else if rand1 == 9 {
|
||||||
|
rand1 = 0
|
||||||
|
}
|
||||||
|
lowid = rand1
|
||||||
|
highid = rand2
|
||||||
|
}
|
||||||
|
var steps = highid - lowid
|
||||||
|
wg.Add(steps)
|
||||||
|
for i := lowid; i < highid; i++ {
|
||||||
|
select {
|
||||||
|
case <-quit:
|
||||||
|
log.Info("Terminating simulation loop")
|
||||||
|
return
|
||||||
|
case <-time.After(randWait):
|
||||||
|
}
|
||||||
|
log.Debug(fmt.Sprintf("node %v shutting down", nodes[i]))
|
||||||
|
err := net.Stop(nodes[i])
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("Error stopping node %s", nodes[i]))
|
||||||
|
wg.Done()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go func(id discover.NodeID) {
|
||||||
|
time.Sleep(randWait)
|
||||||
|
err := net.Start(id)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("Error starting node %s", id))
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}(nodes[i])
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//connect nodeCount number of nodes in a ring
|
||||||
|
func connectNodesInRing(net *Network, nodeCount int) ([]discover.NodeID, error) {
|
||||||
|
ids := make([]discover.NodeID, nodeCount)
|
||||||
|
for i := 0; i < nodeCount; i++ {
|
||||||
|
node, err := net.NewNode()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error creating a node! %s", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ids[i] = node.ID()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, id := range ids {
|
||||||
|
if err := net.Start(id); err != nil {
|
||||||
|
log.Error("Error starting a node! %s", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debug(fmt.Sprintf("node %v starting up", id))
|
||||||
|
}
|
||||||
|
for i, id := range ids {
|
||||||
|
peerID := ids[(i+1)%len(ids)]
|
||||||
|
if err := net.Connect(id, peerID); err != nil {
|
||||||
|
log.Error("Error connecting a node to a peer! %s", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids, nil
|
||||||
|
}
|
171
p2p/simulations/mocker_test.go
Normal file
171
p2p/simulations/mocker_test.go
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package simulations simulates p2p networks.
|
||||||
|
// A mokcer simulates starting and stopping real nodes in a network.
|
||||||
|
package simulations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMocker(t *testing.T) {
|
||||||
|
//start the simulation HTTP server
|
||||||
|
_, s := testHTTPServer(t)
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
//create a client
|
||||||
|
client := NewClient(s.URL)
|
||||||
|
|
||||||
|
//start the network
|
||||||
|
err := client.StartNetwork()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not start test network: %s", err)
|
||||||
|
}
|
||||||
|
//stop the network to terminate
|
||||||
|
defer func() {
|
||||||
|
err = client.StopNetwork()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not stop test network: %s", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
//get the list of available mocker types
|
||||||
|
resp, err := http.Get(s.URL + "/mocker")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not get mocker list: %s", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
t.Fatalf("Invalid Status Code received, expected 200, got %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
//check the list is at least 1 in size
|
||||||
|
var mockerlist []string
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&mockerlist)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error decoding JSON mockerlist: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(mockerlist) < 1 {
|
||||||
|
t.Fatalf("No mockers available")
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeCount := 10
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
events := make(chan *Event, 10)
|
||||||
|
var opts SubscribeOpts
|
||||||
|
sub, err := client.SubscribeNetwork(events, opts)
|
||||||
|
defer sub.Unsubscribe()
|
||||||
|
//wait until all nodes are started and connected
|
||||||
|
//store every node up event in a map (value is irrelevant, mimic Set datatype)
|
||||||
|
nodemap := make(map[discover.NodeID]bool)
|
||||||
|
wg.Add(1)
|
||||||
|
nodesComplete := false
|
||||||
|
connCount := 0
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event := <-events:
|
||||||
|
//if the event is a node Up event only
|
||||||
|
if event.Node != nil && event.Node.Up == true {
|
||||||
|
//add the correspondent node ID to the map
|
||||||
|
nodemap[event.Node.Config.ID] = true
|
||||||
|
//this means all nodes got a nodeUp event, so we can continue the test
|
||||||
|
if len(nodemap) == nodeCount {
|
||||||
|
nodesComplete = true
|
||||||
|
//wait for 3s as the mocker will need time to connect the nodes
|
||||||
|
//time.Sleep( 3 *time.Second)
|
||||||
|
}
|
||||||
|
} else if event.Conn != nil && nodesComplete {
|
||||||
|
connCount += 1
|
||||||
|
if connCount == (nodeCount-1)*2 {
|
||||||
|
wg.Done()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case <-time.After(30 * time.Second):
|
||||||
|
wg.Done()
|
||||||
|
t.Fatalf("Timeout waiting for nodes being started up!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
//take the last element of the mockerlist as the default mocker-type to ensure one is enabled
|
||||||
|
mockertype := mockerlist[len(mockerlist)-1]
|
||||||
|
//still, use hardcoded "probabilistic" one if available ;)
|
||||||
|
for _, m := range mockerlist {
|
||||||
|
if m == "probabilistic" {
|
||||||
|
mockertype = m
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//start the mocker with nodeCount number of nodes
|
||||||
|
resp, err = http.PostForm(s.URL+"/mocker/start", url.Values{"mocker-type": {mockertype}, "node-count": {strconv.Itoa(nodeCount)}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not start mocker: %s", err)
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
t.Fatalf("Invalid Status Code received for starting mocker, expected 200, got %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
//check there are nodeCount number of nodes in the network
|
||||||
|
nodes_info, err := client.GetNodes()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not get nodes list: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(nodes_info) != nodeCount {
|
||||||
|
t.Fatalf("Expected %d number of nodes, got: %d", nodeCount, len(nodes_info))
|
||||||
|
}
|
||||||
|
|
||||||
|
//stop the mocker
|
||||||
|
resp, err = http.Post(s.URL+"/mocker/stop", "", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not stop mocker: %s", err)
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
t.Fatalf("Invalid Status Code received for stopping mocker, expected 200, got %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
//reset the network
|
||||||
|
_, err = http.Post(s.URL+"/reset", "", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not reset network: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//now the number of nodes in the network should be zero
|
||||||
|
nodes_info, err = client.GetNodes()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not get nodes list: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(nodes_info) != 0 {
|
||||||
|
t.Fatalf("Expected empty list of nodes, got: %d", len(nodes_info))
|
||||||
|
}
|
||||||
|
}
|
@ -501,6 +501,20 @@ func (self *Network) Shutdown() {
|
|||||||
close(self.quitc)
|
close(self.quitc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Reset resets all network properties:
|
||||||
|
//emtpies the nodes and the connection list
|
||||||
|
func (self *Network) Reset() {
|
||||||
|
self.lock.Lock()
|
||||||
|
defer self.lock.Unlock()
|
||||||
|
|
||||||
|
//re-initialize the maps
|
||||||
|
self.connMap = make(map[string]int)
|
||||||
|
self.nodeMap = make(map[discover.NodeID]int)
|
||||||
|
|
||||||
|
self.Nodes = nil
|
||||||
|
self.Conns = nil
|
||||||
|
}
|
||||||
|
|
||||||
// Node is a wrapper around adapters.Node which is used to track the status
|
// Node is a wrapper around adapters.Node which is used to track the status
|
||||||
// of a node in the network
|
// of a node in the network
|
||||||
type Node struct {
|
type Node struct {
|
||||||
@ -664,6 +678,12 @@ func (self *Network) Load(snap *Snapshot) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, conn := range snap.Conns {
|
for _, conn := range snap.Conns {
|
||||||
|
|
||||||
|
if !self.GetNode(conn.One).Up || !self.GetNode(conn.Other).Up {
|
||||||
|
//in this case, at least one of the nodes of a connection is not up,
|
||||||
|
//so it would result in the snapshot `Load` to fail
|
||||||
|
continue
|
||||||
|
}
|
||||||
if err := self.Connect(conn.One, conn.Other); err != nil {
|
if err := self.Connect(conn.One, conn.Other); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user