Browse code

Vendoring of libnetwork

Signed-off-by: Flavio Crisciani <flavio.crisciani@docker.com>

Flavio Crisciani authored on 2017/12/02 04:26:35
Showing 15 changed files
... ...
@@ -30,7 +30,7 @@ github.com/moby/buildkit aaff9d591ef128560018433fe61beb802e149de8
30 30
 github.com/tonistiigi/fsutil dea3a0da73aee887fc02142d995be764106ac5e2
31 31
 
32 32
 #get libnetwork packages
33
-github.com/docker/libnetwork 64ae58878fc8f95e4a167499d654e13fa36abdc7
33
+github.com/docker/libnetwork 9bca9a4a220b158cc94402e0f8c2c7714eb6f503
34 34
 github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
35 35
 github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
36 36
 github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
... ...
@@ -293,11 +293,13 @@ func (c *controller) agentInit(listenAddr, bindAddrOrInterface, advertiseAddr, d
293 293
 			c.Config().Daemon.NetworkControlPlaneMTU, netDBConf.PacketBufferSize)
294 294
 	}
295 295
 	nDB, err := networkdb.New(netDBConf)
296
-
297 296
 	if err != nil {
298 297
 		return err
299 298
 	}
300 299
 
300
+	// Register the diagnose handlers
301
+	c.DiagnoseServer.RegisterHandler(nDB, networkdb.NetDbPaths2Func)
302
+
301 303
 	var cancelList []func()
302 304
 	ch, cancel := nDB.Watch(libnetworkEPTable, "", "")
303 305
 	cancelList = append(cancelList, cancel)
... ...
@@ -436,7 +438,7 @@ func (n *network) Services() map[string]ServiceInfo {
436 436
 	for eid, value := range entries {
437 437
 		var epRec EndpointRecord
438 438
 		nid := n.ID()
439
-		if err := proto.Unmarshal(value.([]byte), &epRec); err != nil {
439
+		if err := proto.Unmarshal(value.Value, &epRec); err != nil {
440 440
 			logrus.Errorf("Unmarshal of libnetworkEPTable failed for endpoint %s in network %s, %v", eid, nid, err)
441 441
 			continue
442 442
 		}
... ...
@@ -461,7 +463,7 @@ func (n *network) Services() map[string]ServiceInfo {
461 461
 		}
462 462
 		entries := agent.networkDB.GetTableByNetwork(table.name, n.id)
463 463
 		for key, value := range entries {
464
-			epID, info := d.DecodeTableEntry(table.name, key, value.([]byte))
464
+			epID, info := d.DecodeTableEntry(table.name, key, value.Value)
465 465
 			if ep, ok := eps[epID]; !ok {
466 466
 				logrus.Errorf("Inconsistent driver and libnetwork state for endpoint %s", epID)
467 467
 			} else {
... ...
@@ -60,6 +60,7 @@ import (
60 60
 	"github.com/docker/libnetwork/cluster"
61 61
 	"github.com/docker/libnetwork/config"
62 62
 	"github.com/docker/libnetwork/datastore"
63
+	"github.com/docker/libnetwork/diagnose"
63 64
 	"github.com/docker/libnetwork/discoverapi"
64 65
 	"github.com/docker/libnetwork/driverapi"
65 66
 	"github.com/docker/libnetwork/drvregistry"
... ...
@@ -133,6 +134,13 @@ type NetworkController interface {
133 133
 
134 134
 	// SetKeys configures the encryption key for gossip and overlay data path
135 135
 	SetKeys(keys []*types.EncryptionKey) error
136
+
137
+	// StartDiagnose start the network diagnose mode
138
+	StartDiagnose(port int)
139
+	// StopDiagnose start the network diagnose mode
140
+	StopDiagnose()
141
+	// IsDiagnoseEnabled returns true if the diagnose is enabled
142
+	IsDiagnoseEnabled() bool
136 143
 }
137 144
 
138 145
 // NetworkWalker is a client provided function which will be used to walk the Networks.
... ...
@@ -167,6 +175,7 @@ type controller struct {
167 167
 	agentStopDone          chan struct{}
168 168
 	keys                   []*types.EncryptionKey
169 169
 	clusterConfigAvailable bool
170
+	DiagnoseServer         *diagnose.Server
170 171
 	sync.Mutex
171 172
 }
172 173
 
... ...
@@ -185,7 +194,9 @@ func New(cfgOptions ...config.Option) (NetworkController, error) {
185 185
 		serviceBindings: make(map[serviceKey]*service),
186 186
 		agentInitDone:   make(chan struct{}),
187 187
 		networkLocker:   locker.New(),
188
+		DiagnoseServer:  diagnose.New(),
188 189
 	}
190
+	c.DiagnoseServer.Init()
189 191
 
190 192
 	if err := c.initStores(); err != nil {
191 193
 		return nil, err
... ...
@@ -1291,3 +1302,28 @@ func (c *controller) Stop() {
1291 1291
 	c.stopExternalKeyListener()
1292 1292
 	osl.GC()
1293 1293
 }
1294
+
1295
+// StartDiagnose start the network diagnose mode
1296
+func (c *controller) StartDiagnose(port int) {
1297
+	c.Lock()
1298
+	if !c.DiagnoseServer.IsDebugEnable() {
1299
+		c.DiagnoseServer.EnableDebug("127.0.0.1", port)
1300
+	}
1301
+	c.Unlock()
1302
+}
1303
+
1304
+// StopDiagnose start the network diagnose mode
1305
+func (c *controller) StopDiagnose() {
1306
+	c.Lock()
1307
+	if c.DiagnoseServer.IsDebugEnable() {
1308
+		c.DiagnoseServer.DisableDebug()
1309
+	}
1310
+	c.Unlock()
1311
+}
1312
+
1313
+// IsDiagnoseEnabled returns true if the diagnose is enabled
1314
+func (c *controller) IsDiagnoseEnabled() bool {
1315
+	c.Lock()
1316
+	defer c.Unlock()
1317
+	return c.DiagnoseServer.IsDebugEnable()
1318
+}
1294 1319
deleted file mode 100644
... ...
@@ -1,133 +0,0 @@
1
-package diagnose
2
-
3
-import (
4
-	"fmt"
5
-	"net"
6
-	"net/http"
7
-	"sync"
8
-
9
-	"github.com/sirupsen/logrus"
10
-)
11
-
12
-// HTTPHandlerFunc TODO
13
-type HTTPHandlerFunc func(interface{}, http.ResponseWriter, *http.Request)
14
-
15
-type httpHandlerCustom struct {
16
-	ctx interface{}
17
-	F   func(interface{}, http.ResponseWriter, *http.Request)
18
-}
19
-
20
-// ServeHTTP TODO
21
-func (h httpHandlerCustom) ServeHTTP(w http.ResponseWriter, r *http.Request) {
22
-	h.F(h.ctx, w, r)
23
-}
24
-
25
-var diagPaths2Func = map[string]HTTPHandlerFunc{
26
-	"/":      notImplemented,
27
-	"/help":  help,
28
-	"/ready": ready,
29
-}
30
-
31
-// Server when the debug is enabled exposes a
32
-// This data structure is protected by the Agent mutex so does not require and additional mutex here
33
-type Server struct {
34
-	sk                net.Listener
35
-	port              int
36
-	mux               *http.ServeMux
37
-	registeredHanders []string
38
-	sync.Mutex
39
-}
40
-
41
-// Init TODO
42
-func (n *Server) Init() {
43
-	n.mux = http.NewServeMux()
44
-
45
-	// Register local handlers
46
-	n.RegisterHandler(n, diagPaths2Func)
47
-}
48
-
49
-// RegisterHandler TODO
50
-func (n *Server) RegisterHandler(ctx interface{}, hdlrs map[string]HTTPHandlerFunc) {
51
-	n.Lock()
52
-	defer n.Unlock()
53
-	for path, fun := range hdlrs {
54
-		n.mux.Handle(path, httpHandlerCustom{ctx, fun})
55
-		n.registeredHanders = append(n.registeredHanders, path)
56
-	}
57
-}
58
-
59
-// EnableDebug opens a TCP socket to debug the passed network DB
60
-func (n *Server) EnableDebug(ip string, port int) {
61
-	n.Lock()
62
-	defer n.Unlock()
63
-
64
-	n.port = port
65
-	logrus.SetLevel(logrus.DebugLevel)
66
-
67
-	if n.sk != nil {
68
-		logrus.Infof("The server is already up and running")
69
-		return
70
-	}
71
-
72
-	logrus.Infof("Starting the server listening on %d for commands", port)
73
-
74
-	// // Create the socket
75
-	// var err error
76
-	// n.sk, err = net.Listen("tcp", listeningAddr)
77
-	// if err != nil {
78
-	// 	log.Fatal(err)
79
-	// }
80
-	//
81
-	// go func() {
82
-	// 	http.Serve(n.sk, n.mux)
83
-	// }()
84
-	http.ListenAndServe(fmt.Sprintf(":%d", port), n.mux)
85
-}
86
-
87
-// DisableDebug stop the dubug and closes the tcp socket
88
-func (n *Server) DisableDebug() {
89
-	n.Lock()
90
-	defer n.Unlock()
91
-	n.sk.Close()
92
-	n.sk = nil
93
-}
94
-
95
-// IsDebugEnable returns true when the debug is enabled
96
-func (n *Server) IsDebugEnable() bool {
97
-	n.Lock()
98
-	defer n.Unlock()
99
-	return n.sk != nil
100
-}
101
-
102
-func notImplemented(ctx interface{}, w http.ResponseWriter, r *http.Request) {
103
-	fmt.Fprintf(w, "URL path: %s no method implemented check /help\n", r.URL.Path)
104
-}
105
-
106
-func help(ctx interface{}, w http.ResponseWriter, r *http.Request) {
107
-	n, ok := ctx.(*Server)
108
-	if ok {
109
-		for _, path := range n.registeredHanders {
110
-			fmt.Fprintf(w, "%s\n", path)
111
-		}
112
-	}
113
-}
114
-
115
-func ready(ctx interface{}, w http.ResponseWriter, r *http.Request) {
116
-	fmt.Fprintf(w, "OK\n")
117
-}
118
-
119
-// DebugHTTPForm TODO
120
-func DebugHTTPForm(r *http.Request) {
121
-	r.ParseForm()
122
-	for k, v := range r.Form {
123
-		logrus.Debugf("Form[%q] = %q\n", k, v)
124
-	}
125
-}
126
-
127
-// HTTPReplyError TODO
128
-func HTTPReplyError(w http.ResponseWriter, message, usage string) {
129
-	fmt.Fprintf(w, "%s\n", message)
130
-	if usage != "" {
131
-		fmt.Fprintf(w, "Usage: %s\n", usage)
132
-	}
133
-}
134 1
new file mode 100644
... ...
@@ -0,0 +1,228 @@
0
+package diagnose
1
+
2
+import (
3
+	"context"
4
+	"encoding/json"
5
+	"fmt"
6
+	"net/http"
7
+	"sync"
8
+	"sync/atomic"
9
+
10
+	stackdump "github.com/docker/docker/pkg/signal"
11
+	"github.com/docker/libnetwork/common"
12
+	"github.com/sirupsen/logrus"
13
+)
14
+
15
+// HTTPHandlerFunc TODO
16
+type HTTPHandlerFunc func(interface{}, http.ResponseWriter, *http.Request)
17
+
18
+type httpHandlerCustom struct {
19
+	ctx interface{}
20
+	F   func(interface{}, http.ResponseWriter, *http.Request)
21
+}
22
+
23
+// ServeHTTP TODO
24
+func (h httpHandlerCustom) ServeHTTP(w http.ResponseWriter, r *http.Request) {
25
+	h.F(h.ctx, w, r)
26
+}
27
+
28
+var diagPaths2Func = map[string]HTTPHandlerFunc{
29
+	"/":          notImplemented,
30
+	"/help":      help,
31
+	"/ready":     ready,
32
+	"/stackdump": stackTrace,
33
+}
34
+
35
+// Server when the debug is enabled exposes a
36
+// This data structure is protected by the Agent mutex so does not require and additional mutex here
37
+type Server struct {
38
+	enable            int32
39
+	srv               *http.Server
40
+	port              int
41
+	mux               *http.ServeMux
42
+	registeredHanders map[string]bool
43
+	sync.Mutex
44
+}
45
+
46
+// New creates a new diagnose server
47
+func New() *Server {
48
+	return &Server{
49
+		registeredHanders: make(map[string]bool),
50
+	}
51
+}
52
+
53
+// Init initialize the mux for the http handling and register the base hooks
54
+func (s *Server) Init() {
55
+	s.mux = http.NewServeMux()
56
+
57
+	// Register local handlers
58
+	s.RegisterHandler(s, diagPaths2Func)
59
+}
60
+
61
+// RegisterHandler allows to register new handlers to the mux and to a specific path
62
+func (s *Server) RegisterHandler(ctx interface{}, hdlrs map[string]HTTPHandlerFunc) {
63
+	s.Lock()
64
+	defer s.Unlock()
65
+	for path, fun := range hdlrs {
66
+		if _, ok := s.registeredHanders[path]; ok {
67
+			continue
68
+		}
69
+		s.mux.Handle(path, httpHandlerCustom{ctx, fun})
70
+		s.registeredHanders[path] = true
71
+	}
72
+}
73
+
74
+// ServeHTTP this is the method called bu the ListenAndServe, and is needed to allow us to
75
+// use our custom mux
76
+func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
77
+	s.mux.ServeHTTP(w, r)
78
+}
79
+
80
+// EnableDebug opens a TCP socket to debug the passed network DB
81
+func (s *Server) EnableDebug(ip string, port int) {
82
+	s.Lock()
83
+	defer s.Unlock()
84
+
85
+	s.port = port
86
+
87
+	if s.enable == 1 {
88
+		logrus.Info("The server is already up and running")
89
+		return
90
+	}
91
+
92
+	logrus.Infof("Starting the diagnose server listening on %d for commands", port)
93
+	srv := &http.Server{Addr: fmt.Sprintf("127.0.0.1:%d", port), Handler: s}
94
+	s.srv = srv
95
+	s.enable = 1
96
+	go func(n *Server) {
97
+		// Ingore ErrServerClosed that is returned on the Shutdown call
98
+		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
99
+			logrus.Errorf("ListenAndServe error: %s", err)
100
+			atomic.SwapInt32(&n.enable, 0)
101
+		}
102
+	}(s)
103
+
104
+}
105
+
106
+// DisableDebug stop the dubug and closes the tcp socket
107
+func (s *Server) DisableDebug() {
108
+	s.Lock()
109
+	defer s.Unlock()
110
+
111
+	s.srv.Shutdown(context.Background())
112
+	s.srv = nil
113
+	s.enable = 0
114
+	logrus.Info("Disabling the diagnose server")
115
+}
116
+
117
+// IsDebugEnable returns true when the debug is enabled
118
+func (s *Server) IsDebugEnable() bool {
119
+	s.Lock()
120
+	defer s.Unlock()
121
+	return s.enable == 1
122
+}
123
+
124
+func notImplemented(ctx interface{}, w http.ResponseWriter, r *http.Request) {
125
+	r.ParseForm()
126
+	_, json := ParseHTTPFormOptions(r)
127
+	rsp := WrongCommand("not implemented", fmt.Sprintf("URL path: %s no method implemented check /help\n", r.URL.Path))
128
+
129
+	// audit logs
130
+	log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()})
131
+	log.Info("command not implemented done")
132
+
133
+	HTTPReply(w, rsp, json)
134
+}
135
+
136
+func help(ctx interface{}, w http.ResponseWriter, r *http.Request) {
137
+	r.ParseForm()
138
+	_, json := ParseHTTPFormOptions(r)
139
+
140
+	// audit logs
141
+	log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()})
142
+	log.Info("help done")
143
+
144
+	n, ok := ctx.(*Server)
145
+	var result string
146
+	if ok {
147
+		for path := range n.registeredHanders {
148
+			result += fmt.Sprintf("%s\n", path)
149
+		}
150
+		HTTPReply(w, CommandSucceed(&StringCmd{Info: result}), json)
151
+	}
152
+}
153
+
154
+func ready(ctx interface{}, w http.ResponseWriter, r *http.Request) {
155
+	r.ParseForm()
156
+	_, json := ParseHTTPFormOptions(r)
157
+
158
+	// audit logs
159
+	log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()})
160
+	log.Info("ready done")
161
+	HTTPReply(w, CommandSucceed(&StringCmd{Info: "OK"}), json)
162
+}
163
+
164
+func stackTrace(ctx interface{}, w http.ResponseWriter, r *http.Request) {
165
+	r.ParseForm()
166
+	_, json := ParseHTTPFormOptions(r)
167
+
168
+	// audit logs
169
+	log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()})
170
+	log.Info("stack trace")
171
+
172
+	path, err := stackdump.DumpStacks("/tmp/")
173
+	if err != nil {
174
+		log.WithError(err).Error("failed to write goroutines dump")
175
+		HTTPReply(w, FailCommand(err), json)
176
+	} else {
177
+		log.Info("stack trace done")
178
+		HTTPReply(w, CommandSucceed(&StringCmd{Info: fmt.Sprintf("goroutine stacks written to %s", path)}), json)
179
+	}
180
+}
181
+
182
+// DebugHTTPForm helper to print the form url parameters
183
+func DebugHTTPForm(r *http.Request) {
184
+	for k, v := range r.Form {
185
+		logrus.Debugf("Form[%q] = %q\n", k, v)
186
+	}
187
+}
188
+
189
+// JSONOutput contains details on JSON output printing
190
+type JSONOutput struct {
191
+	enable      bool
192
+	prettyPrint bool
193
+}
194
+
195
+// ParseHTTPFormOptions easily parse the JSON printing options
196
+func ParseHTTPFormOptions(r *http.Request) (bool, *JSONOutput) {
197
+	_, unsafe := r.Form["unsafe"]
198
+	v, json := r.Form["json"]
199
+	var pretty bool
200
+	if len(v) > 0 {
201
+		pretty = v[0] == "pretty"
202
+	}
203
+	return unsafe, &JSONOutput{enable: json, prettyPrint: pretty}
204
+}
205
+
206
+// HTTPReply helper function that takes care of sending the message out
207
+func HTTPReply(w http.ResponseWriter, r *HTTPResult, j *JSONOutput) (int, error) {
208
+	var response []byte
209
+	if j.enable {
210
+		w.Header().Set("Content-Type", "application/json")
211
+		var err error
212
+		if j.prettyPrint {
213
+			response, err = json.MarshalIndent(r, "", "  ")
214
+			if err != nil {
215
+				response, _ = json.MarshalIndent(FailCommand(err), "", "  ")
216
+			}
217
+		} else {
218
+			response, err = json.Marshal(r)
219
+			if err != nil {
220
+				response, _ = json.Marshal(FailCommand(err))
221
+			}
222
+		}
223
+	} else {
224
+		response = []byte(r.String())
225
+	}
226
+	return fmt.Fprint(w, string(response))
227
+}
0 228
new file mode 100644
... ...
@@ -0,0 +1,122 @@
0
+package diagnose
1
+
2
+import "fmt"
3
+
4
+// StringInterface interface that has to be implemented by messages
5
+type StringInterface interface {
6
+	String() string
7
+}
8
+
9
+// CommandSucceed creates a success message
10
+func CommandSucceed(result StringInterface) *HTTPResult {
11
+	return &HTTPResult{
12
+		Message: "OK",
13
+		Details: result,
14
+	}
15
+}
16
+
17
+// FailCommand creates a failure message with error
18
+func FailCommand(err error) *HTTPResult {
19
+	return &HTTPResult{
20
+		Message: "FAIL",
21
+		Details: &ErrorCmd{Error: err.Error()},
22
+	}
23
+}
24
+
25
+// WrongCommand creates a wrong command response
26
+func WrongCommand(message, usage string) *HTTPResult {
27
+	return &HTTPResult{
28
+		Message: message,
29
+		Details: &UsageCmd{Usage: usage},
30
+	}
31
+}
32
+
33
+// HTTPResult Diagnose Server HTTP result operation
34
+type HTTPResult struct {
35
+	Message string          `json:"message"`
36
+	Details StringInterface `json:"details"`
37
+}
38
+
39
+func (h *HTTPResult) String() string {
40
+	rsp := h.Message
41
+	if h.Details != nil {
42
+		rsp += "\n" + h.Details.String()
43
+	}
44
+	return rsp
45
+}
46
+
47
+// UsageCmd command with usage field
48
+type UsageCmd struct {
49
+	Usage string `json:"usage"`
50
+}
51
+
52
+func (u *UsageCmd) String() string {
53
+	return "Usage: " + u.Usage
54
+}
55
+
56
+// StringCmd command with info string
57
+type StringCmd struct {
58
+	Info string `json:"info"`
59
+}
60
+
61
+func (s *StringCmd) String() string {
62
+	return s.Info
63
+}
64
+
65
+// ErrorCmd command with error
66
+type ErrorCmd struct {
67
+	Error string `json:"error"`
68
+}
69
+
70
+func (e *ErrorCmd) String() string {
71
+	return "Error: " + e.Error
72
+}
73
+
74
+// TableObj network db table object
75
+type TableObj struct {
76
+	Length   int               `json:"size"`
77
+	Elements []StringInterface `json:"entries"`
78
+}
79
+
80
+func (t *TableObj) String() string {
81
+	output := fmt.Sprintf("total entries: %d\n", t.Length)
82
+	for _, e := range t.Elements {
83
+		output += e.String()
84
+	}
85
+	return output
86
+}
87
+
88
+// PeerEntryObj entry in the networkdb peer table
89
+type PeerEntryObj struct {
90
+	Index int    `json:"-"`
91
+	Name  string `json:"-=name"`
92
+	IP    string `json:"ip"`
93
+}
94
+
95
+func (p *PeerEntryObj) String() string {
96
+	return fmt.Sprintf("%d) %s -> %s\n", p.Index, p.Name, p.IP)
97
+}
98
+
99
+// TableEntryObj network db table entry object
100
+type TableEntryObj struct {
101
+	Index int    `json:"-"`
102
+	Key   string `json:"key"`
103
+	Value string `json:"value"`
104
+	Owner string `json:"owner"`
105
+}
106
+
107
+func (t *TableEntryObj) String() string {
108
+	return fmt.Sprintf("%d) k:`%s` -> v:`%s` owner:`%s`\n", t.Index, t.Key, t.Value, t.Owner)
109
+}
110
+
111
+// TableEndpointsResult fully typed message for proper unmarshaling on the client side
112
+type TableEndpointsResult struct {
113
+	TableObj
114
+	Elements []TableEntryObj `json:"entries"`
115
+}
116
+
117
+// TablePeersResult fully typed message for proper unmarshaling on the client side
118
+type TablePeersResult struct {
119
+	TableObj
120
+	Elements []PeerEntryObj `json:"entries"`
121
+}
... ...
@@ -42,6 +42,14 @@ const (
42 42
 	DefaultGatewayV6AuxKey = "DefaultGatewayIPv6"
43 43
 )
44 44
 
45
+type defaultBridgeNetworkConflict struct {
46
+	ID string
47
+}
48
+
49
+func (d defaultBridgeNetworkConflict) Error() string {
50
+	return fmt.Sprintf("Stale default bridge network %s", d.ID)
51
+}
52
+
45 53
 type iptableCleanFunc func() error
46 54
 type iptablesCleanFuncs []iptableCleanFunc
47 55
 
... ...
@@ -137,6 +145,7 @@ type driver struct {
137 137
 	networks       map[string]*bridgeNetwork
138 138
 	store          datastore.DataStore
139 139
 	nlh            *netlink.Handle
140
+	configNetwork  sync.Mutex
140 141
 	sync.Mutex
141 142
 }
142 143
 
... ...
@@ -322,41 +331,6 @@ func (n *bridgeNetwork) isolateNetwork(others []*bridgeNetwork, enable bool) err
322 322
 	return nil
323 323
 }
324 324
 
325
-// Checks whether this network's configuration for the network with this id conflicts with any of the passed networks
326
-func (c *networkConfiguration) conflictsWithNetworks(id string, others []*bridgeNetwork) error {
327
-	for _, nw := range others {
328
-
329
-		nw.Lock()
330
-		nwID := nw.id
331
-		nwConfig := nw.config
332
-		nwBridge := nw.bridge
333
-		nw.Unlock()
334
-
335
-		if nwID == id {
336
-			continue
337
-		}
338
-		// Verify the name (which may have been set by newInterface()) does not conflict with
339
-		// existing bridge interfaces. Ironically the system chosen name gets stored in the config...
340
-		// Basically we are checking if the two original configs were both empty.
341
-		if nwConfig.BridgeName == c.BridgeName {
342
-			return types.ForbiddenErrorf("conflicts with network %s (%s) by bridge name", nwID, nwConfig.BridgeName)
343
-		}
344
-		// If this network config specifies the AddressIPv4, we need
345
-		// to make sure it does not conflict with any previously allocated
346
-		// bridges. This could not be completely caught by the config conflict
347
-		// check, because networks which config does not specify the AddressIPv4
348
-		// get their address and subnet selected by the driver (see electBridgeIPv4())
349
-		if c.AddressIPv4 != nil && nwBridge.bridgeIPv4 != nil {
350
-			if nwBridge.bridgeIPv4.Contains(c.AddressIPv4.IP) ||
351
-				c.AddressIPv4.Contains(nwBridge.bridgeIPv4.IP) {
352
-				return types.ForbiddenErrorf("conflicts with network %s (%s) by ip network", nwID, nwConfig.BridgeName)
353
-			}
354
-		}
355
-	}
356
-
357
-	return nil
358
-}
359
-
360 325
 func (d *driver) configure(option map[string]interface{}) error {
361 326
 	var (
362 327
 		config         *configuration
... ...
@@ -602,11 +576,27 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d
602 602
 		return err
603 603
 	}
604 604
 
605
-	err = config.processIPAM(id, ipV4Data, ipV6Data)
606
-	if err != nil {
605
+	if err = config.processIPAM(id, ipV4Data, ipV6Data); err != nil {
607 606
 		return err
608 607
 	}
609 608
 
609
+	// start the critical section, from this point onward we are dealing with the list of networks
610
+	// so to be consistent we cannot allow that the list changes
611
+	d.configNetwork.Lock()
612
+	defer d.configNetwork.Unlock()
613
+
614
+	// check network conflicts
615
+	if err = d.checkConflict(config); err != nil {
616
+		nerr, ok := err.(defaultBridgeNetworkConflict)
617
+		if !ok {
618
+			return err
619
+		}
620
+		// Got a conflict with a stale default network, clean that up and continue
621
+		logrus.Warn(nerr)
622
+		d.deleteNetwork(nerr.ID)
623
+	}
624
+
625
+	// there is no conflict, now create the network
610 626
 	if err = d.createNetwork(config); err != nil {
611 627
 		return err
612 628
 	}
... ...
@@ -614,34 +604,48 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d
614 614
 	return d.storeUpdate(config)
615 615
 }
616 616
 
617
-func (d *driver) createNetwork(config *networkConfiguration) error {
618
-	var err error
619
-
620
-	defer osl.InitOSContext()()
621
-
617
+func (d *driver) checkConflict(config *networkConfiguration) error {
622 618
 	networkList := d.getNetworks()
623
-	for i, nw := range networkList {
619
+	for _, nw := range networkList {
624 620
 		nw.Lock()
625 621
 		nwConfig := nw.config
626 622
 		nw.Unlock()
627 623
 		if err := nwConfig.Conflicts(config); err != nil {
628 624
 			if config.DefaultBridge {
629 625
 				// We encountered and identified a stale default network
630
-				// We must delete it as libnetwork is the source of thruth
626
+				// We must delete it as libnetwork is the source of truth
631 627
 				// The default network being created must be the only one
632 628
 				// This can happen only from docker 1.12 on ward
633
-				logrus.Infof("Removing stale default bridge network %s (%s)", nwConfig.ID, nwConfig.BridgeName)
634
-				if err := d.DeleteNetwork(nwConfig.ID); err != nil {
635
-					logrus.Warnf("Failed to remove stale default network: %s (%s): %v. Will remove from store.", nwConfig.ID, nwConfig.BridgeName, err)
636
-					d.storeDelete(nwConfig)
637
-				}
638
-				networkList = append(networkList[:i], networkList[i+1:]...)
639
-			} else {
640
-				return types.ForbiddenErrorf("cannot create network %s (%s): conflicts with network %s (%s): %s",
641
-					config.ID, config.BridgeName, nwConfig.ID, nwConfig.BridgeName, err.Error())
629
+				logrus.Infof("Found stale default bridge network %s (%s)", nwConfig.ID, nwConfig.BridgeName)
630
+				return defaultBridgeNetworkConflict{nwConfig.ID}
642 631
 			}
632
+
633
+			return types.ForbiddenErrorf("cannot create network %s (%s): conflicts with network %s (%s): %s",
634
+				config.ID, config.BridgeName, nwConfig.ID, nwConfig.BridgeName, err.Error())
643 635
 		}
644 636
 	}
637
+	return nil
638
+}
639
+
640
+func (d *driver) createNetwork(config *networkConfiguration) error {
641
+	var err error
642
+
643
+	defer osl.InitOSContext()()
644
+
645
+	networkList := d.getNetworks()
646
+
647
+	// Initialize handle when needed
648
+	d.Lock()
649
+	if d.nlh == nil {
650
+		d.nlh = ns.NlHandle()
651
+	}
652
+	d.Unlock()
653
+
654
+	// Create or retrieve the bridge L3 interface
655
+	bridgeIface, err := newInterface(d.nlh, config)
656
+	if err != nil {
657
+		return err
658
+	}
645 659
 
646 660
 	// Create and set network handler in driver
647 661
 	network := &bridgeNetwork{
... ...
@@ -649,6 +653,7 @@ func (d *driver) createNetwork(config *networkConfiguration) error {
649 649
 		endpoints:  make(map[string]*bridgeEndpoint),
650 650
 		config:     config,
651 651
 		portMapper: portmapper.New(d.config.UserlandProxyPath),
652
+		bridge:     bridgeIface,
652 653
 		driver:     d,
653 654
 	}
654 655
 
... ...
@@ -665,35 +670,15 @@ func (d *driver) createNetwork(config *networkConfiguration) error {
665 665
 		}
666 666
 	}()
667 667
 
668
-	// Initialize handle when needed
669
-	d.Lock()
670
-	if d.nlh == nil {
671
-		d.nlh = ns.NlHandle()
672
-	}
673
-	d.Unlock()
674
-
675
-	// Create or retrieve the bridge L3 interface
676
-	bridgeIface, err := newInterface(d.nlh, config)
677
-	if err != nil {
678
-		return err
679
-	}
680
-	network.bridge = bridgeIface
681
-
682
-	// Verify the network configuration does not conflict with previously installed
683
-	// networks. This step is needed now because driver might have now set the bridge
684
-	// name on this config struct. And because we need to check for possible address
685
-	// conflicts, so we need to check against operationa lnetworks.
686
-	if err = config.conflictsWithNetworks(config.ID, networkList); err != nil {
687
-		return err
688
-	}
689
-
668
+	// Add inter-network communication rules.
690 669
 	setupNetworkIsolationRules := func(config *networkConfiguration, i *bridgeInterface) error {
691 670
 		if err := network.isolateNetwork(networkList, true); err != nil {
692
-			if err := network.isolateNetwork(networkList, false); err != nil {
671
+			if err = network.isolateNetwork(networkList, false); err != nil {
693 672
 				logrus.Warnf("Failed on removing the inter-network iptables rules on cleanup: %v", err)
694 673
 			}
695 674
 			return err
696 675
 		}
676
+		// register the cleanup function
697 677
 		network.registerIptCleanFunc(func() error {
698 678
 			nwList := d.getNetworks()
699 679
 			return network.isolateNetwork(nwList, false)
... ...
@@ -767,10 +752,17 @@ func (d *driver) createNetwork(config *networkConfiguration) error {
767 767
 }
768 768
 
769 769
 func (d *driver) DeleteNetwork(nid string) error {
770
+
771
+	d.configNetwork.Lock()
772
+	defer d.configNetwork.Unlock()
773
+
774
+	return d.deleteNetwork(nid)
775
+}
776
+
777
+func (d *driver) deleteNetwork(nid string) error {
770 778
 	var err error
771 779
 
772 780
 	defer osl.InitOSContext()()
773
-
774 781
 	// Get network handler and remove it from driver
775 782
 	d.Lock()
776 783
 	n, ok := d.networks[nid]
... ...
@@ -814,12 +806,6 @@ func (d *driver) DeleteNetwork(nid string) error {
814 814
 		}
815 815
 	}()
816 816
 
817
-	// Sanity check
818
-	if n == nil {
819
-		err = driverapi.ErrNoNetwork(nid)
820
-		return err
821
-	}
822
-
823 817
 	switch config.BridgeIfaceCreator {
824 818
 	case ifaceCreatedByLibnetwork, ifaceCreatorUnknown:
825 819
 		// We only delete the bridge if it was created by the bridge driver and
... ...
@@ -9,7 +9,7 @@ func (n *bridgeNetwork) setupFirewalld(config *networkConfiguration, i *bridgeIn
9 9
 	d.Unlock()
10 10
 
11 11
 	// Sanity check.
12
-	if driverConfig.EnableIPTables == false {
12
+	if !driverConfig.EnableIPTables {
13 13
 		return IPTableCfgError(config.BridgeName)
14 14
 	}
15 15
 
... ...
@@ -696,6 +696,12 @@ func (n *network) initSandbox(restore bool) error {
696 696
 	var nlSock *nl.NetlinkSocket
697 697
 	sbox.InvokeFunc(func() {
698 698
 		nlSock, err = nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_NEIGH)
699
+		if err != nil {
700
+			return
701
+		}
702
+		// set the receive timeout to not remain stuck on the RecvFrom if the fd gets closed
703
+		tv := syscall.NsecToTimeval(soTimeout.Nanoseconds())
704
+		err = nlSock.SetReceiveTimeout(&tv)
699 705
 	})
700 706
 	n.setNetlinkSocket(nlSock)
701 707
 
... ...
@@ -721,6 +727,11 @@ func (n *network) watchMiss(nlSock *nl.NetlinkSocket) {
721 721
 				// The netlink socket got closed, simply exit to not leak this goroutine
722 722
 				return
723 723
 			}
724
+			// When the receive timeout expires the receive will return EAGAIN
725
+			if err == syscall.EAGAIN {
726
+				// we continue here to avoid spam for timeouts
727
+				continue
728
+			}
724 729
 			logrus.Errorf("Failed to receive from netlink: %v ", err)
725 730
 			continue
726 731
 		}
... ...
@@ -5,12 +5,19 @@ package ipvs
5 5
 import (
6 6
 	"net"
7 7
 	"syscall"
8
+	"time"
8 9
 
9 10
 	"fmt"
11
+
10 12
 	"github.com/vishvananda/netlink/nl"
11 13
 	"github.com/vishvananda/netns"
12 14
 )
13 15
 
16
+const (
17
+	netlinkRecvSocketsTimeout = 3 * time.Second
18
+	netlinkSendSocketTimeout  = 30 * time.Second
19
+)
20
+
14 21
 // Service defines an IPVS service in its entirety.
15 22
 type Service struct {
16 23
 	// Virtual service address.
... ...
@@ -82,6 +89,15 @@ func New(path string) (*Handle, error) {
82 82
 	if err != nil {
83 83
 		return nil, err
84 84
 	}
85
+	// Add operation timeout to avoid deadlocks
86
+	tv := syscall.NsecToTimeval(netlinkSendSocketTimeout.Nanoseconds())
87
+	if err := sock.SetSendTimeout(&tv); err != nil {
88
+		return nil, err
89
+	}
90
+	tv = syscall.NsecToTimeval(netlinkRecvSocketsTimeout.Nanoseconds())
91
+	if err := sock.SetReceiveTimeout(&tv); err != nil {
92
+		return nil, err
93
+	}
85 94
 
86 95
 	return &Handle{sock: sock}, nil
87 96
 }
... ...
@@ -203,10 +203,6 @@ func newGenlRequest(familyID int, cmd uint8) *nl.NetlinkRequest {
203 203
 }
204 204
 
205 205
 func execute(s *nl.NetlinkSocket, req *nl.NetlinkRequest, resType uint16) ([][]byte, error) {
206
-	var (
207
-		err error
208
-	)
209
-
210 206
 	if err := s.Send(req); err != nil {
211 207
 		return nil, err
212 208
 	}
... ...
@@ -222,6 +218,13 @@ done:
222 222
 	for {
223 223
 		msgs, err := s.Receive()
224 224
 		if err != nil {
225
+			if s.GetFd() == -1 {
226
+				return nil, fmt.Errorf("Socket got closed on receive")
227
+			}
228
+			if err == syscall.EAGAIN {
229
+				// timeout fired
230
+				continue
231
+			}
225 232
 			return nil, err
226 233
 		}
227 234
 		for _, m := range msgs {
... ...
@@ -72,7 +72,7 @@ func (e *eventDelegate) NotifyLeave(mn *memberlist.Node) {
72 72
 	// If the node instead left because was going down, then it makes sense to just delete all its state
73 73
 	e.nDB.Lock()
74 74
 	defer e.nDB.Unlock()
75
-	e.nDB.deleteNetworkEntriesForNode(mn.Name)
75
+	e.nDB.deleteNodeFromNetworks(mn.Name)
76 76
 	e.nDB.deleteNodeTableEntries(mn.Name)
77 77
 	if n, ok := e.nDB.nodes[mn.Name]; ok {
78 78
 		delete(e.nDB.nodes, mn.Name)
... ...
@@ -401,17 +401,23 @@ func (nDB *NetworkDB) UpdateEntry(tname, nid, key string, value []byte) error {
401 401
 	return nil
402 402
 }
403 403
 
404
+// TableElem elem
405
+type TableElem struct {
406
+	Value []byte
407
+	owner string
408
+}
409
+
404 410
 // GetTableByNetwork walks the networkdb by the give table and network id and
405 411
 // returns a map of keys and values
406
-func (nDB *NetworkDB) GetTableByNetwork(tname, nid string) map[string]interface{} {
407
-	entries := make(map[string]interface{})
412
+func (nDB *NetworkDB) GetTableByNetwork(tname, nid string) map[string]*TableElem {
413
+	entries := make(map[string]*TableElem)
408 414
 	nDB.indexes[byTable].WalkPrefix(fmt.Sprintf("/%s/%s", tname, nid), func(k string, v interface{}) bool {
409 415
 		entry := v.(*entry)
410 416
 		if entry.deleting {
411 417
 			return false
412 418
 		}
413 419
 		key := k[strings.LastIndex(k, "/")+1:]
414
-		entries[key] = entry.value
420
+		entries[key] = &TableElem{Value: entry.value, owner: entry.node}
415 421
 		return false
416 422
 	})
417 423
 	return entries
... ...
@@ -445,7 +451,7 @@ func (nDB *NetworkDB) DeleteEntry(tname, nid, key string) error {
445 445
 	return nil
446 446
 }
447 447
 
448
-func (nDB *NetworkDB) deleteNetworkEntriesForNode(deletedNode string) {
448
+func (nDB *NetworkDB) deleteNodeFromNetworks(deletedNode string) {
449 449
 	for nid, nodes := range nDB.networkNodes {
450 450
 		updatedNodes := make([]string, 0, len(nodes))
451 451
 		for _, node := range nodes {
... ...
@@ -547,7 +553,9 @@ func (nDB *NetworkDB) deleteNodeTableEntries(node string) {
547 547
 
548 548
 		nDB.deleteEntry(nid, tname, key)
549 549
 
550
-		nDB.broadcaster.Write(makeEvent(opDelete, tname, nid, key, oldEntry.value))
550
+		if !oldEntry.deleting {
551
+			nDB.broadcaster.Write(makeEvent(opDelete, tname, nid, key, oldEntry.value))
552
+		}
551 553
 		return false
552 554
 	})
553 555
 }
... ...
@@ -1,17 +1,19 @@
1 1
 package networkdb
2 2
 
3 3
 import (
4
+	"encoding/base64"
4 5
 	"fmt"
5 6
 	"net/http"
6 7
 	"strings"
7 8
 
8
-	stackdump "github.com/docker/docker/pkg/signal"
9
+	"github.com/docker/libnetwork/common"
9 10
 	"github.com/docker/libnetwork/diagnose"
10 11
 	"github.com/sirupsen/logrus"
11 12
 )
12 13
 
13 14
 const (
14 15
 	missingParameter = "missing parameter"
16
+	dbNotAvailable   = "database not available"
15 17
 )
16 18
 
17 19
 // NetDbPaths2Func TODO
... ...
@@ -26,14 +28,21 @@ var NetDbPaths2Func = map[string]diagnose.HTTPHandlerFunc{
26 26
 	"/deleteentry":  dbDeleteEntry,
27 27
 	"/getentry":     dbGetEntry,
28 28
 	"/gettable":     dbGetTable,
29
-	"/dump":         dbStackTrace,
30 29
 }
31 30
 
32 31
 func dbJoin(ctx interface{}, w http.ResponseWriter, r *http.Request) {
33 32
 	r.ParseForm()
34 33
 	diagnose.DebugHTTPForm(r)
34
+	_, json := diagnose.ParseHTTPFormOptions(r)
35
+
36
+	// audit logs
37
+	log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()})
38
+	log.Info("join cluster")
39
+
35 40
 	if len(r.Form["members"]) < 1 {
36
-		diagnose.HTTPReplyError(w, missingParameter, fmt.Sprintf("%s?members=ip1,ip2,...", r.URL.Path))
41
+		rsp := diagnose.WrongCommand(missingParameter, fmt.Sprintf("%s?members=ip1,ip2,...", r.URL.Path))
42
+		log.Error("join cluster failed, wrong input")
43
+		diagnose.HTTPReply(w, rsp, json)
37 44
 		return
38 45
 	}
39 46
 
... ...
@@ -41,51 +50,88 @@ func dbJoin(ctx interface{}, w http.ResponseWriter, r *http.Request) {
41 41
 	if ok {
42 42
 		err := nDB.Join(strings.Split(r.Form["members"][0], ","))
43 43
 		if err != nil {
44
-			fmt.Fprintf(w, "%s error in the DB join %s\n", r.URL.Path, err)
44
+			rsp := diagnose.FailCommand(fmt.Errorf("%s error in the DB join %s", r.URL.Path, err))
45
+			log.WithError(err).Error("join cluster failed")
46
+			diagnose.HTTPReply(w, rsp, json)
45 47
 			return
46 48
 		}
47 49
 
48
-		fmt.Fprintf(w, "OK\n")
50
+		log.Info("join cluster done")
51
+		diagnose.HTTPReply(w, diagnose.CommandSucceed(nil), json)
52
+		return
49 53
 	}
54
+	diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json)
50 55
 }
51 56
 
52 57
 func dbPeers(ctx interface{}, w http.ResponseWriter, r *http.Request) {
53 58
 	r.ParseForm()
54 59
 	diagnose.DebugHTTPForm(r)
60
+	_, json := diagnose.ParseHTTPFormOptions(r)
61
+
62
+	// audit logs
63
+	log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()})
64
+	log.Info("network peers")
65
+
55 66
 	if len(r.Form["nid"]) < 1 {
56
-		diagnose.HTTPReplyError(w, missingParameter, fmt.Sprintf("%s?nid=test", r.URL.Path))
67
+		rsp := diagnose.WrongCommand(missingParameter, fmt.Sprintf("%s?nid=test", r.URL.Path))
68
+		log.Error("network peers failed, wrong input")
69
+		diagnose.HTTPReply(w, rsp, json)
57 70
 		return
58 71
 	}
59 72
 
60 73
 	nDB, ok := ctx.(*NetworkDB)
61 74
 	if ok {
62 75
 		peers := nDB.Peers(r.Form["nid"][0])
63
-		fmt.Fprintf(w, "Network:%s Total peers: %d\n", r.Form["nid"], len(peers))
76
+		rsp := &diagnose.TableObj{Length: len(peers)}
64 77
 		for i, peerInfo := range peers {
65
-			fmt.Fprintf(w, "%d) %s -> %s\n", i, peerInfo.Name, peerInfo.IP)
78
+			rsp.Elements = append(rsp.Elements, &diagnose.PeerEntryObj{Index: i, Name: peerInfo.Name, IP: peerInfo.IP})
66 79
 		}
80
+		log.WithField("response", fmt.Sprintf("%+v", rsp)).Info("network peers done")
81
+		diagnose.HTTPReply(w, diagnose.CommandSucceed(rsp), json)
82
+		return
67 83
 	}
84
+	diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json)
68 85
 }
69 86
 
70 87
 func dbClusterPeers(ctx interface{}, w http.ResponseWriter, r *http.Request) {
88
+	r.ParseForm()
89
+	diagnose.DebugHTTPForm(r)
90
+	_, json := diagnose.ParseHTTPFormOptions(r)
91
+
92
+	// audit logs
93
+	log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()})
94
+	log.Info("cluster peers")
95
+
71 96
 	nDB, ok := ctx.(*NetworkDB)
72 97
 	if ok {
73 98
 		peers := nDB.ClusterPeers()
74
-		fmt.Fprintf(w, "Total peers: %d\n", len(peers))
99
+		rsp := &diagnose.TableObj{Length: len(peers)}
75 100
 		for i, peerInfo := range peers {
76
-			fmt.Fprintf(w, "%d) %s -> %s\n", i, peerInfo.Name, peerInfo.IP)
101
+			rsp.Elements = append(rsp.Elements, &diagnose.PeerEntryObj{Index: i, Name: peerInfo.Name, IP: peerInfo.IP})
77 102
 		}
103
+		log.WithField("response", fmt.Sprintf("%+v", rsp)).Info("cluster peers done")
104
+		diagnose.HTTPReply(w, diagnose.CommandSucceed(rsp), json)
105
+		return
78 106
 	}
107
+	diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json)
79 108
 }
80 109
 
81 110
 func dbCreateEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
82 111
 	r.ParseForm()
83 112
 	diagnose.DebugHTTPForm(r)
113
+	unsafe, json := diagnose.ParseHTTPFormOptions(r)
114
+
115
+	// audit logs
116
+	log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()})
117
+	log.Info("create entry")
118
+
84 119
 	if len(r.Form["tname"]) < 1 ||
85 120
 		len(r.Form["nid"]) < 1 ||
86 121
 		len(r.Form["key"]) < 1 ||
87 122
 		len(r.Form["value"]) < 1 {
88
-		diagnose.HTTPReplyError(w, missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id&key=k&value=v", r.URL.Path))
123
+		rsp := diagnose.WrongCommand(missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id&key=k&value=v", r.URL.Path))
124
+		log.Error("create entry failed, wrong input")
125
+		diagnose.HTTPReply(w, rsp, json)
89 126
 		return
90 127
 	}
91 128
 
... ...
@@ -93,25 +139,48 @@ func dbCreateEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
93 93
 	nid := r.Form["nid"][0]
94 94
 	key := r.Form["key"][0]
95 95
 	value := r.Form["value"][0]
96
+	decodedValue := []byte(value)
97
+	if !unsafe {
98
+		var err error
99
+		decodedValue, err = base64.StdEncoding.DecodeString(value)
100
+		if err != nil {
101
+			log.WithError(err).Error("create entry failed")
102
+			diagnose.HTTPReply(w, diagnose.FailCommand(err), json)
103
+			return
104
+		}
105
+	}
96 106
 
97 107
 	nDB, ok := ctx.(*NetworkDB)
98 108
 	if ok {
99
-		if err := nDB.CreateEntry(tname, nid, key, []byte(value)); err != nil {
100
-			diagnose.HTTPReplyError(w, err.Error(), "")
109
+		if err := nDB.CreateEntry(tname, nid, key, decodedValue); err != nil {
110
+			rsp := diagnose.FailCommand(err)
111
+			diagnose.HTTPReply(w, rsp, json)
112
+			log.WithError(err).Error("create entry failed")
101 113
 			return
102 114
 		}
103
-		fmt.Fprintf(w, "OK\n")
115
+		log.Info("create entry done")
116
+		diagnose.HTTPReply(w, diagnose.CommandSucceed(nil), json)
117
+		return
104 118
 	}
119
+	diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json)
105 120
 }
106 121
 
107 122
 func dbUpdateEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
108 123
 	r.ParseForm()
109 124
 	diagnose.DebugHTTPForm(r)
125
+	unsafe, json := diagnose.ParseHTTPFormOptions(r)
126
+
127
+	// audit logs
128
+	log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()})
129
+	log.Info("update entry")
130
+
110 131
 	if len(r.Form["tname"]) < 1 ||
111 132
 		len(r.Form["nid"]) < 1 ||
112 133
 		len(r.Form["key"]) < 1 ||
113 134
 		len(r.Form["value"]) < 1 {
114
-		diagnose.HTTPReplyError(w, missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id&key=k&value=v", r.URL.Path))
135
+		rsp := diagnose.WrongCommand(missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id&key=k&value=v", r.URL.Path))
136
+		log.Error("update entry failed, wrong input")
137
+		diagnose.HTTPReply(w, rsp, json)
115 138
 		return
116 139
 	}
117 140
 
... ...
@@ -119,24 +188,46 @@ func dbUpdateEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
119 119
 	nid := r.Form["nid"][0]
120 120
 	key := r.Form["key"][0]
121 121
 	value := r.Form["value"][0]
122
+	decodedValue := []byte(value)
123
+	if !unsafe {
124
+		var err error
125
+		decodedValue, err = base64.StdEncoding.DecodeString(value)
126
+		if err != nil {
127
+			log.WithError(err).Error("update entry failed")
128
+			diagnose.HTTPReply(w, diagnose.FailCommand(err), json)
129
+			return
130
+		}
131
+	}
122 132
 
123 133
 	nDB, ok := ctx.(*NetworkDB)
124 134
 	if ok {
125
-		if err := nDB.UpdateEntry(tname, nid, key, []byte(value)); err != nil {
126
-			diagnose.HTTPReplyError(w, err.Error(), "")
135
+		if err := nDB.UpdateEntry(tname, nid, key, decodedValue); err != nil {
136
+			log.WithError(err).Error("update entry failed")
137
+			diagnose.HTTPReply(w, diagnose.FailCommand(err), json)
127 138
 			return
128 139
 		}
129
-		fmt.Fprintf(w, "OK\n")
140
+		log.Info("update entry done")
141
+		diagnose.HTTPReply(w, diagnose.CommandSucceed(nil), json)
142
+		return
130 143
 	}
144
+	diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json)
131 145
 }
132 146
 
133 147
 func dbDeleteEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
134 148
 	r.ParseForm()
135 149
 	diagnose.DebugHTTPForm(r)
150
+	_, json := diagnose.ParseHTTPFormOptions(r)
151
+
152
+	// audit logs
153
+	log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()})
154
+	log.Info("delete entry")
155
+
136 156
 	if len(r.Form["tname"]) < 1 ||
137 157
 		len(r.Form["nid"]) < 1 ||
138 158
 		len(r.Form["key"]) < 1 {
139
-		diagnose.HTTPReplyError(w, missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id&key=k", r.URL.Path))
159
+		rsp := diagnose.WrongCommand(missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id&key=k", r.URL.Path))
160
+		log.Error("delete entry failed, wrong input")
161
+		diagnose.HTTPReply(w, rsp, json)
140 162
 		return
141 163
 	}
142 164
 
... ...
@@ -148,20 +239,32 @@ func dbDeleteEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
148 148
 	if ok {
149 149
 		err := nDB.DeleteEntry(tname, nid, key)
150 150
 		if err != nil {
151
-			diagnose.HTTPReplyError(w, err.Error(), "")
151
+			log.WithError(err).Error("delete entry failed")
152
+			diagnose.HTTPReply(w, diagnose.FailCommand(err), json)
152 153
 			return
153 154
 		}
154
-		fmt.Fprintf(w, "OK\n")
155
+		log.Info("delete entry done")
156
+		diagnose.HTTPReply(w, diagnose.CommandSucceed(nil), json)
157
+		return
155 158
 	}
159
+	diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json)
156 160
 }
157 161
 
158 162
 func dbGetEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
159 163
 	r.ParseForm()
160 164
 	diagnose.DebugHTTPForm(r)
165
+	unsafe, json := diagnose.ParseHTTPFormOptions(r)
166
+
167
+	// audit logs
168
+	log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()})
169
+	log.Info("get entry")
170
+
161 171
 	if len(r.Form["tname"]) < 1 ||
162 172
 		len(r.Form["nid"]) < 1 ||
163 173
 		len(r.Form["key"]) < 1 {
164
-		diagnose.HTTPReplyError(w, missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id&key=k", r.URL.Path))
174
+		rsp := diagnose.WrongCommand(missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id&key=k", r.URL.Path))
175
+		log.Error("get entry failed, wrong input")
176
+		diagnose.HTTPReply(w, rsp, json)
165 177
 		return
166 178
 	}
167 179
 
... ...
@@ -173,18 +276,39 @@ func dbGetEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
173 173
 	if ok {
174 174
 		value, err := nDB.GetEntry(tname, nid, key)
175 175
 		if err != nil {
176
-			diagnose.HTTPReplyError(w, err.Error(), "")
176
+			log.WithError(err).Error("get entry failed")
177
+			diagnose.HTTPReply(w, diagnose.FailCommand(err), json)
177 178
 			return
178 179
 		}
179
-		fmt.Fprintf(w, "key:`%s` value:`%s`\n", key, string(value))
180
+
181
+		var encodedValue string
182
+		if unsafe {
183
+			encodedValue = string(value)
184
+		} else {
185
+			encodedValue = base64.StdEncoding.EncodeToString(value)
186
+		}
187
+
188
+		rsp := &diagnose.TableEntryObj{Key: key, Value: encodedValue}
189
+		log.WithField("response", fmt.Sprintf("%+v", rsp)).Info("update entry done")
190
+		diagnose.HTTPReply(w, diagnose.CommandSucceed(rsp), json)
191
+		return
180 192
 	}
193
+	diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json)
181 194
 }
182 195
 
183 196
 func dbJoinNetwork(ctx interface{}, w http.ResponseWriter, r *http.Request) {
184 197
 	r.ParseForm()
185 198
 	diagnose.DebugHTTPForm(r)
199
+	_, json := diagnose.ParseHTTPFormOptions(r)
200
+
201
+	// audit logs
202
+	log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()})
203
+	log.Info("join network")
204
+
186 205
 	if len(r.Form["nid"]) < 1 {
187
-		diagnose.HTTPReplyError(w, missingParameter, fmt.Sprintf("%s?nid=network_id", r.URL.Path))
206
+		rsp := diagnose.WrongCommand(missingParameter, fmt.Sprintf("%s?nid=network_id", r.URL.Path))
207
+		log.Error("join network failed, wrong input")
208
+		diagnose.HTTPReply(w, rsp, json)
188 209
 		return
189 210
 	}
190 211
 
... ...
@@ -193,18 +317,30 @@ func dbJoinNetwork(ctx interface{}, w http.ResponseWriter, r *http.Request) {
193 193
 	nDB, ok := ctx.(*NetworkDB)
194 194
 	if ok {
195 195
 		if err := nDB.JoinNetwork(nid); err != nil {
196
-			diagnose.HTTPReplyError(w, err.Error(), "")
196
+			log.WithError(err).Error("join network failed")
197
+			diagnose.HTTPReply(w, diagnose.FailCommand(err), json)
197 198
 			return
198 199
 		}
199
-		fmt.Fprintf(w, "OK\n")
200
+		log.Info("join network done")
201
+		diagnose.HTTPReply(w, diagnose.CommandSucceed(nil), json)
202
+		return
200 203
 	}
204
+	diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json)
201 205
 }
202 206
 
203 207
 func dbLeaveNetwork(ctx interface{}, w http.ResponseWriter, r *http.Request) {
204 208
 	r.ParseForm()
205 209
 	diagnose.DebugHTTPForm(r)
210
+	_, json := diagnose.ParseHTTPFormOptions(r)
211
+
212
+	// audit logs
213
+	log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()})
214
+	log.Info("leave network")
215
+
206 216
 	if len(r.Form["nid"]) < 1 {
207
-		diagnose.HTTPReplyError(w, missingParameter, fmt.Sprintf("%s?nid=network_id", r.URL.Path))
217
+		rsp := diagnose.WrongCommand(missingParameter, fmt.Sprintf("%s?nid=network_id", r.URL.Path))
218
+		log.Error("leave network failed, wrong input")
219
+		diagnose.HTTPReply(w, rsp, json)
208 220
 		return
209 221
 	}
210 222
 
... ...
@@ -213,19 +349,31 @@ func dbLeaveNetwork(ctx interface{}, w http.ResponseWriter, r *http.Request) {
213 213
 	nDB, ok := ctx.(*NetworkDB)
214 214
 	if ok {
215 215
 		if err := nDB.LeaveNetwork(nid); err != nil {
216
-			diagnose.HTTPReplyError(w, err.Error(), "")
216
+			log.WithError(err).Error("leave network failed")
217
+			diagnose.HTTPReply(w, diagnose.FailCommand(err), json)
217 218
 			return
218 219
 		}
219
-		fmt.Fprintf(w, "OK\n")
220
+		log.Info("leave network done")
221
+		diagnose.HTTPReply(w, diagnose.CommandSucceed(nil), json)
222
+		return
220 223
 	}
224
+	diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json)
221 225
 }
222 226
 
223 227
 func dbGetTable(ctx interface{}, w http.ResponseWriter, r *http.Request) {
224 228
 	r.ParseForm()
225 229
 	diagnose.DebugHTTPForm(r)
230
+	unsafe, json := diagnose.ParseHTTPFormOptions(r)
231
+
232
+	// audit logs
233
+	log := logrus.WithFields(logrus.Fields{"component": "diagnose", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()})
234
+	log.Info("get table")
235
+
226 236
 	if len(r.Form["tname"]) < 1 ||
227 237
 		len(r.Form["nid"]) < 1 {
228
-		diagnose.HTTPReplyError(w, missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id", r.URL.Path))
238
+		rsp := diagnose.WrongCommand(missingParameter, fmt.Sprintf("%s?tname=table_name&nid=network_id", r.URL.Path))
239
+		log.Error("get table failed, wrong input")
240
+		diagnose.HTTPReply(w, rsp, json)
229 241
 		return
230 242
 	}
231 243
 
... ...
@@ -235,20 +383,26 @@ func dbGetTable(ctx interface{}, w http.ResponseWriter, r *http.Request) {
235 235
 	nDB, ok := ctx.(*NetworkDB)
236 236
 	if ok {
237 237
 		table := nDB.GetTableByNetwork(tname, nid)
238
-		fmt.Fprintf(w, "total elements: %d\n", len(table))
239
-		i := 0
238
+		rsp := &diagnose.TableObj{Length: len(table)}
239
+		var i = 0
240 240
 		for k, v := range table {
241
-			fmt.Fprintf(w, "%d) k:`%s` -> v:`%s`\n", i, k, string(v.([]byte)))
242
-			i++
241
+			var encodedValue string
242
+			if unsafe {
243
+				encodedValue = string(v.Value)
244
+			} else {
245
+				encodedValue = base64.StdEncoding.EncodeToString(v.Value)
246
+			}
247
+			rsp.Elements = append(rsp.Elements,
248
+				&diagnose.TableEntryObj{
249
+					Index: i,
250
+					Key:   k,
251
+					Value: encodedValue,
252
+					Owner: v.owner,
253
+				})
243 254
 		}
255
+		log.WithField("response", fmt.Sprintf("%+v", rsp)).Info("get table done")
256
+		diagnose.HTTPReply(w, diagnose.CommandSucceed(rsp), json)
257
+		return
244 258
 	}
245
-}
246
-
247
-func dbStackTrace(ctx interface{}, w http.ResponseWriter, r *http.Request) {
248
-	path, err := stackdump.DumpStacks("/tmp/")
249
-	if err != nil {
250
-		logrus.WithError(err).Error("failed to write goroutines dump")
251
-	} else {
252
-		fmt.Fprintf(w, "goroutine stacks written to %s", path)
253
-	}
259
+	diagnose.HTTPReply(w, diagnose.FailCommand(fmt.Errorf("%s", dbNotAvailable)), json)
254 260
 }
... ...
@@ -45,7 +45,7 @@ github.com/sirupsen/logrus v1.0.3
45 45
 github.com/stretchr/testify dab07ac62d4905d3e48d17dc549c684ac3b7c15a
46 46
 github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16
47 47
 github.com/ugorji/go f1f1a805ed361a0e078bb537e4ea78cd37dcf065
48
-github.com/vishvananda/netlink bd6d5de5ccef2d66b0a26177928d0d8895d7f969
48
+github.com/vishvananda/netlink b2de5d10e38ecce8607e6b438b6d174f389a004e
49 49
 github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25
50 50
 golang.org/x/crypto 558b6879de74bc843225cde5686419267ff707ca
51 51
 golang.org/x/net 7dcfb8076726a3fdd9353b6b8a1f1b6be6811bd6