Signed-off-by: Flavio Crisciani <flavio.crisciani@docker.com>
| ... | ... |
@@ -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 |
| ... | ... |
@@ -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 |