new weave status, weave status ..., and weave report
This commit is contained in:
1 parent
00ecff09f7
commit
fc9a1c8664
27 files changed
+784
-493
No files matched your search
@@ -10,7 +10,6 @@ import (
|
||||
// A subsystem/server/... that can be stopped or queried about the status with a signal
|
||||
type SignalReceiver interface {
|
||||
Stop() error
|
||||
Status() string
|
||||
}
|
||||
|
||||
func SignalHandlerLoop(ss ...SignalReceiver) {
|
||||
@@ -28,10 +27,6 @@ func SignalHandlerLoop(ss ...SignalReceiver) {
|
||||
case syscall.SIGQUIT:
|
||||
stacklen := runtime.Stack(buf, true)
|
||||
Log.Infof("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end", buf[:stacklen])
|
||||
case syscall.SIGUSR1:
|
||||
for _, subsystem := range ss {
|
||||
Log.Infof("=== received SIGUSR1 ===\n*** status...\n%s\n*** end", subsystem.Status())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,20 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Assert test is true, panic otherwise
|
||||
func Assert(test bool) {
|
||||
if !test {
|
||||
panic("Assertion failure")
|
||||
}
|
||||
}
|
||||
|
||||
func ErrorMessages(errors []error) string {
|
||||
var result []string
|
||||
for _, err := range errors {
|
||||
result = append(result, err.Error())
|
||||
}
|
||||
return strings.Join(result, "\n")
|
||||
}
|
||||
@@ -62,10 +62,6 @@ func (g *allocate) Cancel() {
|
||||
g.resultChan <- allocateResult{0, fmt.Errorf("Allocate request for %s cancelled", g.ident)}
|
||||
}
|
||||
|
||||
func (g *allocate) String() string {
|
||||
return fmt.Sprintf("Allocate for %s", g.ident)
|
||||
}
|
||||
|
||||
func (g *allocate) ForContainer(ident string) bool {
|
||||
return g.ident == ident
|
||||
}
|
||||
@@ -33,8 +33,6 @@ type operation interface {
|
||||
|
||||
Cancel()
|
||||
|
||||
String() string
|
||||
|
||||
// Does this operation pertain to the given container id?
|
||||
// Used for tidying up pending operations when containers die.
|
||||
ForContainer(ident string) bool
|
||||
@@ -275,15 +273,6 @@ func (alloc *Allocator) Free(ident string, addrToFree address.Address) error {
|
||||
return <-errChan
|
||||
}
|
||||
|
||||
// Sync.
|
||||
func (alloc *Allocator) String() string {
|
||||
resultChan := make(chan string)
|
||||
alloc.actionChan <- func() {
|
||||
resultChan <- alloc.string()
|
||||
}
|
||||
return <-resultChan
|
||||
}
|
||||
|
||||
// Shutdown (Sync)
|
||||
func (alloc *Allocator) Shutdown() {
|
||||
alloc.infof("Shutdown")
|
||||
@@ -493,30 +482,6 @@ func (alloc *Allocator) actorLoop(actionChan <-chan func()) {
|
||||
|
||||
// Helper functions
|
||||
|
||||
func (alloc *Allocator) string() string {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "Allocator range %s", alloc.universe)
|
||||
|
||||
if alloc.ring.Empty() {
|
||||
if alloc.paxosTicker != nil {
|
||||
fmt.Fprintf(&buf, " awaiting consensus: %s", alloc.paxos.String())
|
||||
}
|
||||
} else {
|
||||
fmt.Fprint(&buf, "\nOwned Ranges:")
|
||||
alloc.ring.FprintWithNicknames(&buf, alloc.nicknames)
|
||||
}
|
||||
if len(alloc.pendingAllocates)+len(alloc.pendingClaims) > 0 {
|
||||
fmt.Fprintf(&buf, "\nPending requests:")
|
||||
for _, op := range alloc.pendingAllocates {
|
||||
fmt.Fprintf(&buf, "\n %s", op.String())
|
||||
}
|
||||
for _, op := range alloc.pendingClaims {
|
||||
fmt.Fprintf(&buf, "\n %s", op.String())
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Ensure we are making progress towards an established ring
|
||||
func (alloc *Allocator) establishRing() {
|
||||
if !alloc.ring.Empty() || alloc.paxosTicker != nil {
|
||||
|
||||
@@ -98,10 +98,6 @@ func (c *claim) Cancel() {
|
||||
c.sendResult(fmt.Errorf("Operation cancelled."))
|
||||
}
|
||||
|
||||
func (c *claim) String() string {
|
||||
return fmt.Sprintf("Claim %s -> %s", c.ident, c.addr.String())
|
||||
}
|
||||
|
||||
func (c *claim) ForContainer(ident string) bool {
|
||||
return c.ident == ident
|
||||
}
|
||||
+9
-6
@@ -1,8 +1,6 @@
|
||||
package paxos
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/weaveworks/weave/router"
|
||||
)
|
||||
|
||||
@@ -252,9 +250,14 @@ func (node *Node) Consensus() (bool, AcceptedValue) {
|
||||
return false, AcceptedValue{}
|
||||
}
|
||||
|
||||
func (node *Node) String() string {
|
||||
if ok, val := node.Consensus(); ok {
|
||||
return fmt.Sprintf("Consensus reached with size %d", len(val.Value))
|
||||
type Status struct {
|
||||
KnownNodes int
|
||||
Quorum uint
|
||||
}
|
||||
|
||||
func NewStatus(node *Node) *Status {
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
return fmt.Sprintf("Nodes known: %d, Quorum size: %d", len(node.knows), node.quorum)
|
||||
return &Status{len(node.knows), node.quorum}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package ipam
|
||||
|
||||
import (
|
||||
"github.com/weaveworks/weave/ipam/paxos"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
)
|
||||
|
||||
type Status struct {
|
||||
Paxos *paxos.Status
|
||||
Range string
|
||||
DefaultSubnet string
|
||||
Entries []EntryStatus
|
||||
PendingClaims []ClaimStatus
|
||||
PendingAllocates []string
|
||||
}
|
||||
|
||||
type EntryStatus struct {
|
||||
Token string
|
||||
Peer string
|
||||
Version uint32
|
||||
}
|
||||
|
||||
type ClaimStatus struct {
|
||||
Ident string
|
||||
Address address.Address
|
||||
}
|
||||
|
||||
func NewStatus(allocator *Allocator, defaultSubnet address.CIDR) *Status {
|
||||
if allocator == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var paxosStatus *paxos.Status
|
||||
if allocator.paxosTicker != nil {
|
||||
paxosStatus = paxos.NewStatus(allocator.paxos)
|
||||
}
|
||||
|
||||
resultChan := make(chan *Status)
|
||||
allocator.actionChan <- func() {
|
||||
resultChan <- &Status{
|
||||
paxosStatus,
|
||||
allocator.universe.String(),
|
||||
defaultSubnet.String(),
|
||||
newEntryStatusSlice(allocator),
|
||||
newClaimStatusSlice(allocator),
|
||||
newAllocateIdentSlice(allocator)}
|
||||
}
|
||||
|
||||
return <-resultChan
|
||||
}
|
||||
|
||||
func newEntryStatusSlice(allocator *Allocator) []EntryStatus {
|
||||
var slice []EntryStatus
|
||||
|
||||
if allocator.ring.Empty() {
|
||||
return slice
|
||||
}
|
||||
|
||||
for _, entry := range allocator.ring.Entries {
|
||||
slice = append(slice, EntryStatus{entry.Token.String(), entry.Peer.String(), entry.Version})
|
||||
}
|
||||
|
||||
return slice
|
||||
}
|
||||
|
||||
func newClaimStatusSlice(allocator *Allocator) []ClaimStatus {
|
||||
var slice []ClaimStatus
|
||||
for _, op := range allocator.pendingClaims {
|
||||
claim := op.(*claim)
|
||||
slice = append(slice, ClaimStatus{claim.ident, claim.addr})
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
||||
func newAllocateIdentSlice(allocator *Allocator) []string {
|
||||
var slice []string
|
||||
for _, op := range allocator.pendingAllocates {
|
||||
allocate := op.(*allocate)
|
||||
slice = append(slice, allocate.ident)
|
||||
}
|
||||
return slice
|
||||
}
|
||||
@@ -185,23 +185,6 @@ func (n *Nameserver) deleteTombstones() {
|
||||
})
|
||||
}
|
||||
|
||||
func (n *Nameserver) String() string {
|
||||
n.RLock()
|
||||
defer n.RUnlock()
|
||||
var buf bytes.Buffer
|
||||
for _, entry := range n.entries {
|
||||
if entry.Tombstone > 0 {
|
||||
continue
|
||||
}
|
||||
containerid := entry.ContainerID
|
||||
if len(containerid) > 12 {
|
||||
containerid = containerid[:12]
|
||||
}
|
||||
fmt.Fprintf(&buf, "%s: %s [%s]\n", containerid, entry.Hostname, entry.Addr.String())
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (n *Nameserver) Gossip() router.GossipData {
|
||||
n.RLock()
|
||||
defer n.RUnlock()
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package nameserver
|
||||
|
||||
type Status struct {
|
||||
Domain string
|
||||
Address string
|
||||
TTL uint32
|
||||
Entries []EntryStatus
|
||||
}
|
||||
|
||||
type EntryStatus struct {
|
||||
Hostname string
|
||||
Origin string
|
||||
ContainerID string
|
||||
Address string
|
||||
Version int
|
||||
Tombstone int64
|
||||
}
|
||||
|
||||
func NewStatus(ns *Nameserver, dnsServer *DNSServer) *Status {
|
||||
if dnsServer == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ns.RLock()
|
||||
defer ns.RUnlock()
|
||||
|
||||
var entryStatusSlice []EntryStatus
|
||||
for _, entry := range ns.entries {
|
||||
entryStatusSlice = append(entryStatusSlice, EntryStatus{
|
||||
entry.Hostname,
|
||||
entry.Origin.String(),
|
||||
entry.ContainerID,
|
||||
entry.Addr.String(),
|
||||
entry.Version,
|
||||
entry.Tombstone})
|
||||
}
|
||||
|
||||
return &Status{
|
||||
dnsServer.domain,
|
||||
dnsServer.address,
|
||||
dnsServer.ttl,
|
||||
entryStatusSlice}
|
||||
}
|
||||
+174
-20
@@ -1,44 +1,198 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
. "github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/ipam"
|
||||
"github.com/weaveworks/weave/nameserver"
|
||||
"github.com/weaveworks/weave/net/address"
|
||||
weave "github.com/weaveworks/weave/router"
|
||||
"net/http"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func HandleHTTP(muxRouter *mux.Router,
|
||||
router *weave.Router,
|
||||
allocator *ipam.Allocator,
|
||||
defaultSubnet address.CIDR,
|
||||
ns *nameserver.Nameserver,
|
||||
dnsserver *nameserver.DNSServer) {
|
||||
var rootTemplate = template.New("root").Funcs(map[string]interface{}{
|
||||
"countDNSEntries": func(entries []nameserver.EntryStatus) int {
|
||||
count := 0
|
||||
for _, entry := range entries {
|
||||
if entry.Tombstone == 0 {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
},
|
||||
"printConnectionCounts": func(conns []weave.LocalConnectionStatus) string {
|
||||
counts := make(map[string]int)
|
||||
for _, conn := range conns {
|
||||
counts[conn.State]++
|
||||
}
|
||||
return printCounts(counts, []string{"established", "pending", "retrying", "failed", "connecting"})
|
||||
},
|
||||
"printPeerConnectionCounts": func(peers []weave.PeerStatus) string {
|
||||
counts := make(map[string]int)
|
||||
for _, peer := range peers {
|
||||
for _, conn := range peer.Connections {
|
||||
if conn.Established {
|
||||
counts["established"]++
|
||||
} else {
|
||||
counts["pending"]++
|
||||
}
|
||||
}
|
||||
}
|
||||
return printCounts(counts, []string{"established", "pending"})
|
||||
},
|
||||
"printState": func(enabled bool) string {
|
||||
if enabled {
|
||||
return "enabled"
|
||||
}
|
||||
return "disabled"
|
||||
},
|
||||
"trimSuffix": strings.TrimSuffix,
|
||||
})
|
||||
|
||||
muxRouter.Methods("GET").Path("/status").Headers("Accept", "application/json").HandlerFunc(
|
||||
// Print counts in a specified order
|
||||
func printCounts(counts map[string]int, keys []string) string {
|
||||
var stringCounts []string
|
||||
for _, key := range keys {
|
||||
if count, ok := counts[key]; ok {
|
||||
stringCounts = append(stringCounts, fmt.Sprintf("%d %s", count, key))
|
||||
}
|
||||
}
|
||||
return strings.Join(stringCounts, ", ")
|
||||
}
|
||||
|
||||
// Strip escaped newlines from template
|
||||
func escape(template string) string {
|
||||
return strings.Replace(template, "\\\n", "", -1)
|
||||
}
|
||||
|
||||
// Define a named template panicking on error
|
||||
func defTemplate(name string, text string) *template.Template {
|
||||
return template.Must(rootTemplate.New(name).Parse(escape(text)))
|
||||
}
|
||||
|
||||
var statusTemplate = defTemplate("status", `\
|
||||
Version: {{.Version}}
|
||||
|
||||
Service: router
|
||||
Protocol: {{.Router.Protocol}} \
|
||||
{{if eq .Router.ProtocolMinVersion .Router.ProtocolMaxVersion}}\
|
||||
{{.Router.ProtocolMaxVersion}}\
|
||||
{{else}}\
|
||||
{{.Router.ProtocolMinVersion}}..{{.Router.ProtocolMaxVersion}}\
|
||||
{{end}}
|
||||
Name: {{.Router.Name}}({{.Router.NickName}})
|
||||
Encryption: {{printState .Router.Encryption}}
|
||||
PeerDiscovery: {{printState .Router.PeerDiscovery}}
|
||||
Targets: {{len .Router.Targets}}
|
||||
Connections: {{len .Router.Connections}}{{with printConnectionCounts .Router.Connections}} ({{.}}){{end}}
|
||||
Peers: {{len .Router.Peers}}{{with printPeerConnectionCounts .Router.Peers}} (with {{.}} connections between them){{end}}
|
||||
{{if .IPAM}}\
|
||||
|
||||
Service: ipam
|
||||
{{if .IPAM.Entries}}\
|
||||
Consensus: achieved
|
||||
{{else if .IPAM.Paxos}}\
|
||||
Consensus: waiting (quorum: {{.IPAM.Paxos.Quorum}}, known: {{.IPAM.Paxos.KnownNodes}})
|
||||
{{else}}\
|
||||
Consensus: deferred
|
||||
{{end}}\
|
||||
Range: {{.IPAM.Range}}
|
||||
DefaultSubnet: {{.IPAM.DefaultSubnet}}
|
||||
{{end}}\
|
||||
{{if .DNS}}\
|
||||
|
||||
Service: dns
|
||||
Domain: {{.DNS.Domain}}
|
||||
TTL: {{.DNS.TTL}}
|
||||
Entries: {{countDNSEntries .DNS.Entries}}
|
||||
{{end}}\
|
||||
`)
|
||||
|
||||
var targetsTemplate = defTemplate("targetsTemplate", `\
|
||||
{{range .Router.Targets}}{{.}}
|
||||
{{end}}\
|
||||
`)
|
||||
|
||||
var connectionsTemplate = defTemplate("connectionsTemplate", `\
|
||||
{{range .Router.Connections}}\
|
||||
{{if .Outbound}}->{{else}}<-{{end}} {{printf "%-21v" .Address}} {{printf "%-11v" .State}} {{.Info}}
|
||||
{{end}}\
|
||||
`)
|
||||
|
||||
var peersTemplate = defTemplate("peers", `\
|
||||
{{range .Router.Peers}}\
|
||||
{{.Name}}({{.NickName}})
|
||||
{{range .Connections}}\
|
||||
{{if .Outbound}}->{{else}}<-{{end}} {{printf "%-21v" .Address}} \
|
||||
{{$nameNickName := printf "%v(%v)" .Name .NickName}}{{printf "%-32v" $nameNickName}} \
|
||||
{{if .Established}}established{{else}}pending{{end}}
|
||||
{{end}}\
|
||||
{{end}}\
|
||||
`)
|
||||
|
||||
var dnsEntriesTemplate = defTemplate("dnsEntries", `\
|
||||
{{$domain := printf ".%v" .DNS.Domain}}\
|
||||
{{range .DNS.Entries}}\
|
||||
{{if eq .Tombstone 0}}\
|
||||
{{$hostname := trimSuffix .Hostname $domain}}\
|
||||
{{printf "%-12v" $hostname}} {{printf "%-15v" .Address}} {{printf "%12.12v" .ContainerID}} {{.Origin}}
|
||||
{{end}}\
|
||||
{{end}}\
|
||||
`)
|
||||
|
||||
type WeaveStatus struct {
|
||||
Version string
|
||||
Router *weave.Status `json:"Router,omitempty"`
|
||||
IPAM *ipam.Status `json:"IPAM,omitempty"`
|
||||
DNS *nameserver.Status `json:"DNS,omitempty"`
|
||||
}
|
||||
|
||||
func HandleHTTP(muxRouter *mux.Router, version string, router *weave.Router, allocator *ipam.Allocator, defaultSubnet address.CIDR, ns *nameserver.Nameserver, dnsserver *nameserver.DNSServer) {
|
||||
status := func() WeaveStatus {
|
||||
return WeaveStatus{
|
||||
version,
|
||||
weave.NewStatus(router),
|
||||
ipam.NewStatus(allocator, defaultSubnet),
|
||||
nameserver.NewStatus(ns, dnsserver)}
|
||||
}
|
||||
muxRouter.Methods("GET").Path("/report").Headers("Accept", "application/json").HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
json, _ := router.StatusJSON(version)
|
||||
json, _ := json.MarshalIndent(status(), "", " ")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(json)
|
||||
})
|
||||
|
||||
muxRouter.Methods("GET").Path("/status").HandlerFunc(
|
||||
muxRouter.Methods("GET").Path("/report").Queries("format", "{format}").HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "weave router", version)
|
||||
fmt.Fprintln(w, router.Status())
|
||||
if allocator != nil {
|
||||
fmt.Fprintln(w, allocator.String())
|
||||
fmt.Fprintln(w, "Allocator default subnet:", defaultSubnet)
|
||||
formatTemplate, err := template.New("format").Parse(mux.Vars(r)["format"])
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
fmt.Fprintln(w, "")
|
||||
if dnsserver == nil {
|
||||
fmt.Fprintln(w, "WeaveDNS is disabled")
|
||||
} else {
|
||||
fmt.Fprintln(w, dnsserver.String())
|
||||
fmt.Fprintln(w, ns.String())
|
||||
if err := formatTemplate.Execute(w, status()); err != nil {
|
||||
http.Error(w, "error during template execution", http.StatusInternalServerError)
|
||||
Log.Error(err)
|
||||
}
|
||||
})
|
||||
|
||||
defHandler := func(path string, template *template.Template) {
|
||||
muxRouter.Methods("GET").Path(path).HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := template.Execute(w, status()); err != nil {
|
||||
http.Error(w, "error during template execution", http.StatusInternalServerError)
|
||||
Log.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defHandler("/status", statusTemplate)
|
||||
defHandler("/status/targets", targetsTemplate)
|
||||
defHandler("/status/connections", connectionsTemplate)
|
||||
defHandler("/status/peers", peersTemplate)
|
||||
defHandler("/status/dns", dnsEntriesTemplate)
|
||||
|
||||
}
|
||||
+13
-28
@@ -193,27 +193,29 @@ func main() {
|
||||
|
||||
router.Start()
|
||||
if errors := router.ConnectionMaker.InitiateConnections(peers, false); len(errors) > 0 {
|
||||
Log.Fatal(errorMessages(errors))
|
||||
Log.Fatal(ErrorMessages(errors))
|
||||
}
|
||||
|
||||
// The weave script always waits for a status call to succeed,
|
||||
// so there is no point in doing "weave launch --http-addr ''".
|
||||
// This is here to support stand-alone use of weaver.
|
||||
if httpAddr != "" {
|
||||
go handleHTTP(router, httpAddr, allocator, defaultSubnet, dockerCli, ns, dnsserver)
|
||||
muxRouter := mux.NewRouter()
|
||||
if allocator != nil {
|
||||
allocator.HandleHTTP(muxRouter, defaultSubnet, dockerCli)
|
||||
}
|
||||
if ns != nil {
|
||||
ns.HandleHTTP(muxRouter)
|
||||
}
|
||||
router.HandleHTTP(muxRouter)
|
||||
HandleHTTP(muxRouter, version, router, allocator, defaultSubnet, ns, dnsserver)
|
||||
http.Handle("/", muxRouter)
|
||||
go listenAndServeHTTP(httpAddr, muxRouter)
|
||||
}
|
||||
|
||||
SignalHandlerLoop(router)
|
||||
}
|
||||
|
||||
func errorMessages(errors []error) string {
|
||||
var result []string
|
||||
for _, err := range errors {
|
||||
result = append(result, err.Error())
|
||||
}
|
||||
return strings.Join(result, "\n")
|
||||
}
|
||||
|
||||
func options() map[string]string {
|
||||
options := make(map[string]string)
|
||||
mflag.Visit(func(f *mflag.Flag) {
|
||||
@@ -304,23 +306,7 @@ func determineQuorum(initPeerCountFlag int, peers []string) uint {
|
||||
return quorum
|
||||
}
|
||||
|
||||
func handleHTTP(router *weave.Router, httpAddr string, allocator *ipam.Allocator, defaultSubnet address.CIDR, docker *docker.Client, ns *nameserver.Nameserver, dnsserver *nameserver.DNSServer) {
|
||||
muxRouter := mux.NewRouter()
|
||||
|
||||
if allocator != nil {
|
||||
allocator.HandleHTTP(muxRouter, defaultSubnet, docker)
|
||||
}
|
||||
|
||||
if ns != nil {
|
||||
ns.HandleHTTP(muxRouter)
|
||||
}
|
||||
|
||||
router.HandleHTTP(muxRouter)
|
||||
|
||||
HandleHTTP(muxRouter, router, allocator, defaultSubnet, ns, dnsserver)
|
||||
|
||||
http.Handle("/", muxRouter)
|
||||
|
||||
func listenAndServeHTTP(httpAddr string, muxRouter *mux.Router) {
|
||||
protocol := "tcp"
|
||||
if strings.HasPrefix(httpAddr, "/") {
|
||||
os.Remove(httpAddr) // in case it's there from last time
|
||||
@@ -330,7 +316,6 @@ func handleHTTP(router *weave.Router, httpAddr string, allocator *ipam.Allocator
|
||||
if err != nil {
|
||||
Log.Fatal("Unable to create http listener socket: ", err)
|
||||
}
|
||||
|
||||
err = http.Serve(l, nil)
|
||||
if err != nil {
|
||||
Log.Fatal("Unable to create http server", err)
|
||||
|
||||
@@ -89,18 +89,6 @@ func (conn *RemoteConnection) ErrorLog(args ...interface{}) {
|
||||
log.Errorln(append(append([]interface{}{}, fmt.Sprintf("->[%s|%s]:", conn.remoteTCPAddr, conn.remote)), args...)...)
|
||||
}
|
||||
|
||||
func (conn *RemoteConnection) String() string {
|
||||
from := "<nil>"
|
||||
if conn.local != nil {
|
||||
from = conn.local.String()
|
||||
}
|
||||
to := "<nil>"
|
||||
if conn.remote != nil {
|
||||
to = conn.remote.String()
|
||||
}
|
||||
return fmt.Sprint("Connection ", from, "->", to)
|
||||
}
|
||||
|
||||
// Does not return anything. If the connection is successful, it will
|
||||
// end up in the local peer's connections map.
|
||||
func StartLocalConnection(connRemote *RemoteConnection, tcpConn *net.TCPConn, udpAddr *net.UDPAddr, router *Router, acceptNewPeer bool) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
@@ -104,58 +103,6 @@ func (cm *ConnectionMaker) Refresh() {
|
||||
cm.actionChan <- func() bool { return true }
|
||||
}
|
||||
|
||||
func (cm *ConnectionMaker) Status() ConnectionMakerStatus {
|
||||
// We need to Refresh first in order to clear out any 'attempting'
|
||||
// connections from cm.targets that have been established since
|
||||
// the last run of cm.checkStateAndAttemptConnections. These
|
||||
// entries are harmless but do represent stale state that we do
|
||||
// not want to report.
|
||||
cm.Refresh()
|
||||
resultChan := make(chan ConnectionMakerStatus, 0)
|
||||
cm.actionChan <- func() bool {
|
||||
status := ConnectionMakerStatus{
|
||||
DirectPeers: []string{},
|
||||
Reconnects: make(map[string]Target),
|
||||
}
|
||||
for peer := range cm.directPeers {
|
||||
status.DirectPeers = append(status.DirectPeers, peer)
|
||||
}
|
||||
|
||||
for address, target := range cm.targets {
|
||||
status.Reconnects[address] = *target
|
||||
}
|
||||
resultChan <- status
|
||||
return false
|
||||
}
|
||||
return <-resultChan
|
||||
}
|
||||
|
||||
type ConnectionMakerStatus struct {
|
||||
DirectPeers []string
|
||||
Reconnects map[string]Target
|
||||
}
|
||||
|
||||
func (status ConnectionMakerStatus) String() string {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprint(&buf, "Direct Peers:")
|
||||
for _, peer := range status.DirectPeers {
|
||||
fmt.Fprintf(&buf, " %s", peer)
|
||||
}
|
||||
fmt.Fprintln(&buf, "\nReconnects:")
|
||||
for address, target := range status.Reconnects {
|
||||
fmt.Fprintf(&buf, "->[%s]", address)
|
||||
if target.lastError != nil {
|
||||
fmt.Fprintf(&buf, " (%s)", target.lastError)
|
||||
}
|
||||
if target.attempting {
|
||||
fmt.Fprintf(&buf, " trying since %v\n", target.tryAfter)
|
||||
} else {
|
||||
fmt.Fprintf(&buf, " next try at %v\n", target.tryAfter)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (cm *ConnectionMaker) queryLoop(actionChan <-chan ConnectionMakerAction) {
|
||||
timer := time.NewTimer(MaxDuration)
|
||||
run := func() { timer.Reset(cm.checkStateAndAttemptConnections()) }
|
||||
|
||||
+2
-10
@@ -3,18 +3,10 @@ package router
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/weaveworks/weave/common"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func errorMessages(errors []error) string {
|
||||
var result []string
|
||||
for _, err := range errors {
|
||||
result = append(result, err.Error())
|
||||
}
|
||||
return strings.Join(result, "\n")
|
||||
}
|
||||
|
||||
func (router *Router) HandleHTTP(muxRouter *mux.Router) {
|
||||
|
||||
muxRouter.Methods("POST").Path("/connect").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -22,7 +14,7 @@ func (router *Router) HandleHTTP(muxRouter *mux.Router) {
|
||||
http.Error(w, fmt.Sprint("unable to parse form: ", err), http.StatusBadRequest)
|
||||
}
|
||||
if errors := router.ConnectionMaker.InitiateConnections(r.Form["peer"], r.FormValue("replace") == "true"); len(errors) > 0 {
|
||||
http.Error(w, errorMessages(errors), http.StatusBadRequest)
|
||||
http.Error(w, common.ErrorMessages(errors), http.StatusBadRequest)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
-116
@@ -1,116 +0,0 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (router *Router) StatusJSON(version string) ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Version string
|
||||
Encryption bool
|
||||
PeerDiscovery bool
|
||||
Name string
|
||||
NickName string
|
||||
Interface string
|
||||
Macs *MacCache
|
||||
Peers *Peers
|
||||
Routes *Routes
|
||||
ConnectionMakerStatus
|
||||
}{version, router.UsingPassword(), router.PeerDiscovery, router.Ourself.Name.String(), router.Ourself.NickName, fmt.Sprintf("%v", router.Iface), router.Macs, router.Peers, router.Routes, router.ConnectionMaker.Status()})
|
||||
}
|
||||
|
||||
func (cache *MacCache) MarshalJSON() ([]byte, error) {
|
||||
type cacheEntry struct {
|
||||
Mac string
|
||||
Name string
|
||||
NickName string
|
||||
LastSeen time.Time
|
||||
}
|
||||
var entries []*cacheEntry
|
||||
for key, entry := range cache.table {
|
||||
entries = append(entries, &cacheEntry{intmac(key).String(), entry.peer.Name.String(), entry.peer.NickName, entry.lastSeen})
|
||||
}
|
||||
return json.Marshal(entries)
|
||||
}
|
||||
|
||||
func (peers *Peers) MarshalJSON() ([]byte, error) {
|
||||
type p struct {
|
||||
Name string
|
||||
NickName string
|
||||
UID PeerUID
|
||||
Version uint64
|
||||
Connections []Connection
|
||||
}
|
||||
var ps []*p
|
||||
peers.ForEach(func(peer *Peer) {
|
||||
var connections []Connection
|
||||
if peer == peers.ourself.Peer {
|
||||
for conn := range peers.ourself.Connections() {
|
||||
connections = append(connections, conn)
|
||||
}
|
||||
} else {
|
||||
// Modifying peer.connections requires a write lock on
|
||||
// Peers, and since we are holding a read lock (due to the
|
||||
// ForEach), access without locking the peer is safe.
|
||||
for _, conn := range peer.connections {
|
||||
connections = append(connections, conn)
|
||||
}
|
||||
}
|
||||
ps = append(ps, &p{peer.Name.String(), peer.NickName, peer.UID, peer.version, connections})
|
||||
})
|
||||
return json.Marshal(ps)
|
||||
}
|
||||
|
||||
func (routes *Routes) MarshalJSON() ([]byte, error) {
|
||||
routes.RLock()
|
||||
defer routes.RUnlock()
|
||||
type uni struct {
|
||||
Dest, Via PeerName
|
||||
}
|
||||
type broad struct {
|
||||
Source PeerName
|
||||
Via []PeerName
|
||||
}
|
||||
var r struct {
|
||||
Unicast []*uni
|
||||
Broadcast []*broad
|
||||
}
|
||||
for name, hop := range routes.unicast {
|
||||
r.Unicast = append(r.Unicast, &uni{name, hop})
|
||||
}
|
||||
for name, hops := range routes.broadcast {
|
||||
r.Broadcast = append(r.Broadcast, &broad{name, hops})
|
||||
}
|
||||
return json.Marshal(r)
|
||||
}
|
||||
|
||||
func (conn *RemoteConnection) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Name string
|
||||
NickName string
|
||||
TCPAddr string
|
||||
}{conn.Remote().Name.String(), conn.Remote().NickName, conn.RemoteTCPAddr()})
|
||||
}
|
||||
|
||||
func (name PeerName) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(name.String())
|
||||
}
|
||||
|
||||
func (target Target) MarshalJSON() ([]byte, error) {
|
||||
t := struct {
|
||||
Attempting bool `json:"Attempting,omitempty"`
|
||||
TryAfter time.Time `json:"TryAfter,omitempty"`
|
||||
TryInterval string `json:"TryInterval,omitempty"`
|
||||
LastError string `json:"LastError,omitempty"`
|
||||
}{
|
||||
Attempting: target.attempting,
|
||||
TryAfter: target.tryAfter,
|
||||
TryInterval: target.tryInterval.String(),
|
||||
}
|
||||
if target.lastError != nil {
|
||||
t.LastError = target.lastError.Error()
|
||||
}
|
||||
return json.Marshal(t)
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -82,16 +80,6 @@ func (cache *MacCache) Delete(peer *Peer) bool {
|
||||
return found
|
||||
}
|
||||
|
||||
func (cache *MacCache) String() string {
|
||||
var buf bytes.Buffer
|
||||
cache.RLock()
|
||||
defer cache.RUnlock()
|
||||
for key, entry := range cache.table {
|
||||
fmt.Fprintf(&buf, "%v -> %s (%v)\n", intmac(key), entry.peer, entry.lastSeen)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (cache *MacCache) setExpiryTimer() {
|
||||
cache.expiryTimer = time.AfterFunc(cache.maxAge/10, func() { cache.expire() })
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package router
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
@@ -170,34 +169,6 @@ func (peers *Peers) GarbageCollect() []*Peer {
|
||||
return removed
|
||||
}
|
||||
|
||||
func (peers *Peers) String() string {
|
||||
var buf bytes.Buffer
|
||||
printConnection := func(conn Connection) {
|
||||
established := ""
|
||||
if !conn.Established() {
|
||||
established = " (unestablished)"
|
||||
}
|
||||
fmt.Fprintf(&buf, " -> %s [%v%s]\n", conn.Remote(), conn.RemoteTCPAddr(), established)
|
||||
}
|
||||
peers.ForEach(func(peer *Peer) {
|
||||
if peer == peers.ourself.Peer {
|
||||
fmt.Fprintln(&buf, peers.ourself.Info())
|
||||
for conn := range peers.ourself.Connections() {
|
||||
printConnection(conn)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintln(&buf, peer.Info())
|
||||
// Modifying peer.connections requires a write lock on
|
||||
// Peers, and since we are holding a read lock (due to the
|
||||
// ForEach), access without locking the peer is safe.
|
||||
for _, conn := range peer.connections {
|
||||
printConnection(conn)
|
||||
}
|
||||
}
|
||||
})
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (peers *Peers) garbageCollect() []*Peer {
|
||||
removed := []*Peer{}
|
||||
peers.ourself.RLock()
|
||||
|
||||
@@ -115,26 +115,6 @@ func (router *Router) UsingPassword() bool {
|
||||
return router.Password != nil
|
||||
}
|
||||
|
||||
func (router *Router) Status() string {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintln(&buf, "Our name is", router.Ourself)
|
||||
fmt.Fprintln(&buf, "Encryption", onOff(router.UsingPassword()))
|
||||
fmt.Fprintln(&buf, "Peer discovery", onOff(router.PeerDiscovery))
|
||||
fmt.Fprintln(&buf, "Sniffing traffic on", router.Iface)
|
||||
fmt.Fprintf(&buf, "MACs:\n%s", router.Macs)
|
||||
fmt.Fprintf(&buf, "Peers:\n%s", router.Peers)
|
||||
fmt.Fprintf(&buf, "Routes:\n%s", router.Routes)
|
||||
fmt.Fprint(&buf, router.ConnectionMaker.Status())
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func onOff(b bool) string {
|
||||
if b {
|
||||
return "on"
|
||||
}
|
||||
return "off"
|
||||
}
|
||||
|
||||
func (router *Router) sniff(pio PacketSourceSink) {
|
||||
log.Println("Sniffing traffic on", router.Iface)
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
)
|
||||
@@ -109,23 +107,6 @@ func (routes *Routes) RandomNeighbours(except PeerName) PeerNameSet {
|
||||
return res
|
||||
}
|
||||
|
||||
func (routes *Routes) String() string {
|
||||
var buf bytes.Buffer
|
||||
routes.RLock()
|
||||
defer routes.RUnlock()
|
||||
fmt.Fprintln(&buf, "unicast:")
|
||||
for name, hop := range routes.unicast {
|
||||
fmt.Fprintf(&buf, "%s -> %s\n", name, hop)
|
||||
}
|
||||
fmt.Fprintln(&buf, "broadcast:")
|
||||
for name, hops := range routes.broadcast {
|
||||
fmt.Fprintf(&buf, "%s -> %v\n", name, hops)
|
||||
}
|
||||
// We don't include the 'all' routes here since they are of
|
||||
// limited utility in troubleshooting
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Request recalculation of the routing table. This is async but can
|
||||
// effectively be made synchronous with a subsequent call to
|
||||
// EnsureRecalculated.
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Status struct {
|
||||
Protocol string
|
||||
ProtocolMinVersion int
|
||||
ProtocolMaxVersion int
|
||||
Encryption bool
|
||||
PeerDiscovery bool
|
||||
Name string
|
||||
NickName string
|
||||
Port int
|
||||
Interface string
|
||||
MACs []MACStatus
|
||||
Peers []PeerStatus
|
||||
UnicastRoutes []UnicastRouteStatus
|
||||
BroadcastRoutes []BroadcastRouteStatus
|
||||
Connections []LocalConnectionStatus
|
||||
Targets []string
|
||||
}
|
||||
|
||||
type MACStatus struct {
|
||||
Mac string
|
||||
Name string
|
||||
NickName string
|
||||
LastSeen time.Time
|
||||
}
|
||||
|
||||
type PeerStatus struct {
|
||||
Name string
|
||||
NickName string
|
||||
UID PeerUID
|
||||
Version uint64
|
||||
Connections []ConnectionStatus
|
||||
}
|
||||
|
||||
type ConnectionStatus struct {
|
||||
Name string
|
||||
NickName string
|
||||
Address string
|
||||
Outbound bool
|
||||
Established bool
|
||||
}
|
||||
|
||||
type UnicastRouteStatus struct {
|
||||
Dest, Via string
|
||||
}
|
||||
|
||||
type BroadcastRouteStatus struct {
|
||||
Source string
|
||||
Via []string
|
||||
}
|
||||
|
||||
type LocalConnectionStatus struct {
|
||||
Address string
|
||||
Outbound bool
|
||||
State string
|
||||
Info string
|
||||
}
|
||||
|
||||
func NewStatus(router *Router) *Status {
|
||||
var ifaceName string
|
||||
if router.Iface != nil {
|
||||
ifaceName = router.Iface.Name
|
||||
}
|
||||
return &Status{
|
||||
Protocol,
|
||||
ProtocolMinVersion,
|
||||
ProtocolMaxVersion,
|
||||
router.UsingPassword(),
|
||||
router.PeerDiscovery,
|
||||
router.Ourself.Name.String(),
|
||||
router.Ourself.NickName,
|
||||
router.Port,
|
||||
ifaceName,
|
||||
NewMACStatusSlice(router.Macs),
|
||||
NewPeerStatusSlice(router.Peers),
|
||||
NewUnicastRouteStatusSlice(router.Routes),
|
||||
NewBroadcastRouteStatusSlice(router.Routes),
|
||||
NewLocalConnectionStatusSlice(router.ConnectionMaker),
|
||||
NewTargetSlice(router.ConnectionMaker)}
|
||||
}
|
||||
|
||||
func NewMACStatusSlice(cache *MacCache) []MACStatus {
|
||||
cache.RLock()
|
||||
defer cache.RUnlock()
|
||||
|
||||
var slice []MACStatus
|
||||
for key, entry := range cache.table {
|
||||
slice = append(slice, MACStatus{
|
||||
intmac(key).String(),
|
||||
entry.peer.Name.String(),
|
||||
entry.peer.NickName,
|
||||
entry.lastSeen})
|
||||
}
|
||||
|
||||
return slice
|
||||
}
|
||||
|
||||
func NewPeerStatusSlice(peers *Peers) []PeerStatus {
|
||||
var slice []PeerStatus
|
||||
|
||||
peers.ForEach(func(peer *Peer) {
|
||||
var connections []ConnectionStatus
|
||||
if peer == peers.ourself.Peer {
|
||||
for conn := range peers.ourself.Connections() {
|
||||
connections = append(connections, newConnectionStatus(conn))
|
||||
}
|
||||
} else {
|
||||
// Modifying peer.connections requires a write lock on
|
||||
// Peers, and since we are holding a read lock (due to the
|
||||
// ForEach), access without locking the peer is safe.
|
||||
for _, conn := range peer.connections {
|
||||
connections = append(connections, newConnectionStatus(conn))
|
||||
}
|
||||
}
|
||||
slice = append(slice, PeerStatus{
|
||||
peer.Name.String(),
|
||||
peer.NickName,
|
||||
peer.UID,
|
||||
peer.version,
|
||||
connections})
|
||||
})
|
||||
|
||||
return slice
|
||||
}
|
||||
|
||||
func newConnectionStatus(c Connection) ConnectionStatus {
|
||||
return ConnectionStatus{
|
||||
c.Remote().Name.String(),
|
||||
c.Remote().NickName,
|
||||
c.RemoteTCPAddr(),
|
||||
c.Outbound(),
|
||||
c.Established()}
|
||||
}
|
||||
|
||||
func NewUnicastRouteStatusSlice(routes *Routes) []UnicastRouteStatus {
|
||||
routes.RLock()
|
||||
defer routes.RUnlock()
|
||||
|
||||
var slice []UnicastRouteStatus
|
||||
for dest, via := range routes.unicast {
|
||||
slice = append(slice, UnicastRouteStatus{dest.String(), via.String()})
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
||||
func NewBroadcastRouteStatusSlice(routes *Routes) []BroadcastRouteStatus {
|
||||
routes.RLock()
|
||||
defer routes.RUnlock()
|
||||
|
||||
var slice []BroadcastRouteStatus
|
||||
for source, via := range routes.broadcast {
|
||||
var hops []string
|
||||
for _, hop := range via {
|
||||
hops = append(hops, hop.String())
|
||||
}
|
||||
slice = append(slice, BroadcastRouteStatus{source.String(), hops})
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
||||
func NewLocalConnectionStatusSlice(cm *ConnectionMaker) []LocalConnectionStatus {
|
||||
// We need to Refresh first in order to clear out any 'attempting'
|
||||
// connections from cm.targets that have been established since
|
||||
// the last run of cm.checkStateAndAttemptConnections. These
|
||||
// entries are harmless but do represent stale state that we do
|
||||
// not want to report.
|
||||
cm.Refresh()
|
||||
resultChan := make(chan []LocalConnectionStatus, 0)
|
||||
cm.actionChan <- func() bool {
|
||||
var slice []LocalConnectionStatus
|
||||
for conn := range cm.ourself.Connections() {
|
||||
state := "pending"
|
||||
if conn.Established() {
|
||||
state = "established"
|
||||
}
|
||||
slice = append(slice, LocalConnectionStatus{conn.RemoteTCPAddr(), conn.Outbound(), state, conn.Remote().String()})
|
||||
}
|
||||
for address, target := range cm.targets {
|
||||
var state, info string
|
||||
switch {
|
||||
case target.lastError == nil:
|
||||
state = "connecting"
|
||||
info = ""
|
||||
case target.attempting:
|
||||
state = "retrying"
|
||||
info = target.lastError.Error()
|
||||
default:
|
||||
state = "failed"
|
||||
info = target.lastError.Error() + ", retry: " + target.tryAfter.String()
|
||||
}
|
||||
slice = append(slice, LocalConnectionStatus{address, true, state, info})
|
||||
}
|
||||
resultChan <- slice
|
||||
return false
|
||||
}
|
||||
return <-resultChan
|
||||
}
|
||||
|
||||
func NewTargetSlice(cm *ConnectionMaker) []string {
|
||||
resultChan := make(chan []string, 0)
|
||||
cm.actionChan <- func() bool {
|
||||
var slice []string
|
||||
for peer := range cm.directPeers {
|
||||
slice = append(slice, peer)
|
||||
}
|
||||
resultChan <- slice
|
||||
return false
|
||||
}
|
||||
return <-resultChan
|
||||
}
|
||||
@@ -498,6 +498,11 @@ be disabled with the `--no-discovery` option to `weave launch`. In
|
||||
this mode, weave will only connect to the addresses specified at
|
||||
launch time and with `weave connect`.
|
||||
|
||||
The list of all hosts that a peer has been asked to connect to with
|
||||
`weave launch` and `weave connect` can be obtained with
|
||||
|
||||
host# weave status targets
|
||||
|
||||
### <a name="container-mobility"></a>Container mobility
|
||||
|
||||
Containers can be moved between hosts without requiring any
|
||||
|
||||
+18
-12
@@ -48,6 +48,17 @@ time, you can give the number of peers like this:
|
||||
|
||||
host3$ weave launch --init-peer-count 3 $HOST2
|
||||
|
||||
The consensus mechanism used to determine a majority transitions
|
||||
through three states: 'deferred', 'waiting' and 'achieved':
|
||||
|
||||
* 'deferred' - no allocation requests or claims have been made yet;
|
||||
consensus is deferred until then
|
||||
* 'waiting' - an attempt to achieve consensus is ongoing, triggered by
|
||||
an allocation or claim request; allocations will block. This state
|
||||
persists until a quorum of peers are able to communicate amongst
|
||||
themselves successfully
|
||||
* 'achieved' - consensus achieved; allocations proceed normally
|
||||
|
||||
### More on `--init-peer-count`
|
||||
|
||||
TL;DR: it isn't a problem to over-estimate by a bit, but if you supply
|
||||
@@ -199,21 +210,16 @@ reports on the current status of the weave router and IP allocator:
|
||||
````
|
||||
...
|
||||
|
||||
Allocator universe 10.2.0.0/16
|
||||
Owned Ranges:
|
||||
10.2.1.1 -> 96:e9:e2:2e:2d:bc (host1) (v3)
|
||||
10.2.1.128 -> ea:84:25:9b:31:2e (host2) (v3)
|
||||
10.2.1.192 -> ea:6c:21:09:cf:f0 (host3) (v9)
|
||||
Allocator default subnet: 10.2.1.0/24
|
||||
Service: ipam
|
||||
Consensus: waiting(quorum: 2, known: 0)
|
||||
Range: [10.32.0.0-10.48.0.0)
|
||||
DefaultSubnet: 10.32.0.0/12
|
||||
|
||||
...
|
||||
````
|
||||
|
||||
The first section covers the router; see the [troubleshooting
|
||||
guide](troubleshooting.html#status-report) for full details.
|
||||
guide](troubleshooting.html#weave-status) for full details.
|
||||
|
||||
The 'Allocator' section summarises the overall position and lists
|
||||
which address ranges have been assigned to which peer. Each range
|
||||
begins at the address shown and ends just before the next address, or
|
||||
wraps around at the end of the subnet. The 'v' number denotes how many
|
||||
times that entry has been updated.
|
||||
The 'Service: ipam' section displays the consensus state as well as
|
||||
the total allocation range and default subnet.
|
||||
+149
-75
@@ -5,6 +5,14 @@ layout: default
|
||||
|
||||
# Troubleshooting Weave
|
||||
|
||||
* [Overall status](#weave-status)
|
||||
* [List connections](#weave-status-connections)
|
||||
* [List peers](#weave-status-peers)
|
||||
* [List DNS entries](#weave-status-dns)
|
||||
* [JSON report](#weave-report)
|
||||
* [List attached containers](#list-attached-containers)
|
||||
* [Snapshot releases](#snapshot-releases)
|
||||
|
||||
Check what version of weave you are running with
|
||||
|
||||
weave version
|
||||
@@ -29,9 +37,6 @@ Another useful debugging technique is to attach standard packet
|
||||
capture and analysis tools, such as tcpdump and wireshark, to the
|
||||
`weave` network bridge on the host.
|
||||
|
||||
One can ask a weave router for a [status report](#status-report) or
|
||||
for a [list of attached containers](#list-attached-containers).
|
||||
|
||||
To stop weave, run
|
||||
|
||||
weave stop
|
||||
@@ -50,100 +55,169 @@ Any running application containers will permanently lose connectivity
|
||||
with the weave network and have to be restarted in order to
|
||||
re-connect.
|
||||
|
||||
### <a name="status-report"></a>Status report
|
||||
### <a name="weave-status"></a>Overall status
|
||||
|
||||
The command
|
||||
|
||||
weave status
|
||||
|
||||
reports on the current status of the weave router and DNS.
|
||||
|
||||
This produces output like:
|
||||
A status summary can be obtained with `weave status`:
|
||||
|
||||
````
|
||||
weave router 0.7.0
|
||||
Our name is 7a:f4:56:87:76:3b(weave01)
|
||||
Encryption off
|
||||
Peer discovery on
|
||||
Sniffing traffic on &{39 65535 ethwe ae:e3:07:9c:8c:d4 up|broadcast|multicast}
|
||||
MACs:
|
||||
ba:8c:b9:dc:e1:c9 -> 7a:f4:56:87:76:3b(weave01) (2014-10-23 16:39:19.482338935 +0000 UTC)
|
||||
ce:15:34:a9:b5:6d -> 7a:f4:56:87:76:3b(weave01) (2014-10-23 16:39:28.257103595 +0000 UTC)
|
||||
7a:61:a2:49:4b:91 -> 7a:f4:56:87:76:3b(weave01) (2014-10-23 16:39:27.482970752 +0000 UTC)
|
||||
9e:95:0c:54:8e:39 -> 7a:16:dd:5b:83:de(weave02) (2014-10-23 16:39:28.795601325 +0000 UTC)
|
||||
72:5f:a4:60:e5:ce -> 7a:16:dd:5b:83:de(weave02) (2014-10-23 16:39:29.575995255 +0000 UTC)
|
||||
Peers:
|
||||
7a:16:dd:5b:83:de(weave02) (v31) (UID 13151318985609435078)
|
||||
-> 7a:f4:56:87:76:3b(weave01) [37.157.33.76:7195]
|
||||
7a:f4:56:87:76:3b(weave01) (v1) (UID 6913268221365110570)
|
||||
-> 7a:16:dd:5b:83:de(weave02) [191.235.147.190:6783]
|
||||
Routes:
|
||||
unicast:
|
||||
7a:f4:56:87:76:3b -> 00:00:00:00:00:00
|
||||
7a:16:dd:5b:83:de -> 7a:16:dd:5b:83:de
|
||||
broadcast:
|
||||
7a:f4:56:87:76:3b -> [7a:16:dd:5b:83:de]
|
||||
7a:16:dd:5b:83:de -> []
|
||||
Direct Peers: 191.235.147.190 192.168.32.1
|
||||
Reconnects:
|
||||
->[192.168.32.1:6783] (dial tcp4 192.168.32.1:6783: connection timed out) next try at 2014-10-23 16:39:50.585932102 +0000 UTC
|
||||
$ weave status
|
||||
Version: 1.1.0
|
||||
|
||||
Service: router
|
||||
Protocol: weave 1..2
|
||||
Name: 4a:0f:f6:ec:1c:93(host1)
|
||||
Encryption: disabled
|
||||
PeerDiscovery: enabled
|
||||
Targets: [192.168.48.14 192.168.48.15]
|
||||
Connections: 5 (1 established, 1 pending, 1 retrying, 1 failed, 1 connecting)
|
||||
Peers: 3 (with 5 established, 1 pending connections betweeen them)
|
||||
|
||||
Service: ipam
|
||||
Consensus: achieved
|
||||
Range: [10.32.0.0-10.48.0.0)
|
||||
DefaultSubnet: 10.32.0.0/12
|
||||
|
||||
Service: dns
|
||||
Domain: weave.local.
|
||||
TTL: 1
|
||||
Entries: 9
|
||||
|
||||
Service: proxy
|
||||
Address: tcp://127.0.0.1:12375
|
||||
````
|
||||
|
||||
The terms used here are explained further at
|
||||
[how it works](how-it-works.html).
|
||||
|
||||
The 'Our name' line identifies the local weave router as a peer in the
|
||||
weave network, displaying the peer name followed by the peer's nickname
|
||||
in parenthesis. The nickname defaults to the name of the host on which
|
||||
the weave container was launched; if desired it can be overriden by
|
||||
supplying the `--nickname` argument to `weave launch`.
|
||||
The 'Version' line shows the weave version.
|
||||
|
||||
The 'Sniffing traffic' line shows details of the virtual ethernet
|
||||
interface that weave is using to receive packets on the local
|
||||
machine.
|
||||
The 'Protocol' line indicates the weave router's inter-peer
|
||||
communication protocol name and supported versions (min..max).
|
||||
|
||||
The 'Name' line identifies the local weave router as a peer in the
|
||||
weave network. The nickname shown in parentheses defaults to the name
|
||||
of the host on which the weave container was launched; if desired it
|
||||
can be overriden by supplying the `--nickname` argument to `weave
|
||||
launch`.
|
||||
|
||||
The 'Encryption' line indicates whether
|
||||
[encryption](features.html#security) is in use for communication
|
||||
between peers.
|
||||
|
||||
The 'Peer discovery' line indicates whether
|
||||
The 'PeerDiscovery' line indicates whether
|
||||
[automatic peer discovery](features.html#dynamic-topologies) is
|
||||
enabled (which is the default).
|
||||
|
||||
The 'MACs' section lists all MAC addresses known to this router. These
|
||||
identify containers in the weave network, as well as points for
|
||||
[host network integration](features.html#host-network-integration). For
|
||||
each MAC the list shows the peer they reside on, and the time when the
|
||||
router last saw some traffic from that MAC. The router forgets
|
||||
addresses which are inactive for longer than 10 minutes.
|
||||
'Targets' is the number of hosts that the local weave router has been
|
||||
asked to connect to in `weave launch` and `weave connect`. The
|
||||
complete list can be obtained with `weave status targets`.
|
||||
|
||||
The 'Peers' section lists all peers known to this router, including
|
||||
itself. Each peer is shown with its name, nickname, version number
|
||||
(incremented on each reconnect) and the UID. Then each line
|
||||
beginning `->` shows another peer that it is connected to, with the
|
||||
IP address and port number of the connection. In the above example,
|
||||
the local router has connected to its peer using address
|
||||
191.235.147.190:6783, and its peer sees the same connection as coming
|
||||
from 37.157.33.76:7195.
|
||||
'Connections' shows the total number connections between the local weave
|
||||
router and other peers, and a break down of that figure by connection
|
||||
state. Further details are available with
|
||||
[`weave status connections`](#weave-status-connections).
|
||||
|
||||
The 'Routes' section summarised the information for deciding how to
|
||||
route packets between peers, which is mostly of interest when the
|
||||
weave network is not fully connected. See the
|
||||
[architecture documentation](https://raw.githubusercontent.com/weaveworks/weave/master/docs/architecture.txt)
|
||||
for a full explanation.
|
||||
'Peers' shows the total number of peers in the network, and the number of
|
||||
connections between them. Further details are available with
|
||||
[`weave status peers`](#weave-status-peers).
|
||||
|
||||
'Direct Peers' lists the hosts which weave was told to connect to in
|
||||
`weave launch` and `weave connect`.
|
||||
There are further sections for the [IP address
|
||||
allocator](ipam.html#troubleshooting),
|
||||
[weaveDNS](weavedns.html#troubleshooting), and [Weave Docker API
|
||||
Proxy](proxy.html#troubleshooting).
|
||||
|
||||
The 'Reconnects' section lists peers that this router is aware of, but is
|
||||
not currently connected to. Each line contains some information about
|
||||
what went wrong the last time; whether it is attempting to connect or
|
||||
is waiting for a while before connecting again.
|
||||
### <a name="weave-status-connections"></a>List connections
|
||||
|
||||
There may also be further sections for the
|
||||
[IP address allocator](ipam.html#troubleshooting),
|
||||
[weaveDNS](weavedns.html#troubleshooting), and
|
||||
[Weave Docker API Proxy](proxy.html#troubleshooting).
|
||||
Detailed information on the local weave router's connections can be
|
||||
obtained with `weave status connections`:
|
||||
|
||||
````
|
||||
$ weave status connections
|
||||
<- 192.168.48.12:33866 established 7e:21:4a:70:2f:45(host2)
|
||||
<- 192.168.48.13:60773 pending 7e:ae:cd:d5:23:8d(host3)
|
||||
-> 192.168.48.14:6783 retrying dial tcp4 192.168.48.14:6783: no route to host
|
||||
-> 192.168.48.15:6783 failed dial tcp4 192.168.48.15:6783: no route to host, retry: 2015-08-06 18:55:38.246910357 +0000 UTC
|
||||
-> 192.168.48.16:6783 connecting
|
||||
````
|
||||
|
||||
The columns are as follows:
|
||||
|
||||
* Connection origination direction (`->` for outbound, `<-` for
|
||||
inbound)
|
||||
* Remote TCP address
|
||||
* Status
|
||||
* `connecting` - first connection attempt in progress
|
||||
* `failed` - TCP connection or UDP heartbeat failed
|
||||
* `retrying` - retry of a previously failed connection attempt in
|
||||
progress; reason for previous failure follows
|
||||
* `pending` - TCP connection up, waiting for confirmation of
|
||||
UDP heartbeat
|
||||
* `established` - TCP connection and corresponding UDP path are up
|
||||
* Info - the remote peer name and nickname for (un)established
|
||||
connections, the failure reason for failed and retrying connection
|
||||
|
||||
### <a name="weave-status-peers"></a>List peers
|
||||
|
||||
Detailed information on peers can be obtained with `weave status
|
||||
peers`:
|
||||
|
||||
````
|
||||
$ weave status peers
|
||||
ce:31:e0:06:45:1a(host1)
|
||||
<- 192.168.48.12:39634 ea:2d:b2:e6:e4:f5(host2) established
|
||||
<- 192.168.48.13:49619 ee:38:33:a7:d9:71(host3) established
|
||||
ea:2d:b2:e6:e4:f5(host2)
|
||||
-> 192.168.48.11:6783 ce:31:e0:06:45:1a(host1) established
|
||||
<- 192.168.48.13:58181 ee:38:33:a7:d9:71(host3) established
|
||||
ee:38:33:a7:d9:71(host3)
|
||||
-> 192.168.48.12:6783 ea:2d:b2:e6:e4:f5(host2) established
|
||||
-> 192.168.48.11:6783 ce:31:e0:06:45:1a(host1) established
|
||||
````
|
||||
|
||||
This lists all peers known to this router, including itself. Each
|
||||
peer is shown with its name and nickname, then each line thereafter
|
||||
shows another peer that it is connected to, with the direction, IP
|
||||
address and port number of the connection. In the above example,
|
||||
`host3` has connected to `host1` at `192.168.48.11:6783`; `host1` sees
|
||||
the `host3` end of the same connection as `192.168.48.13:49619`.
|
||||
|
||||
### <a name="weave-status-dns"></a>List DNS entries
|
||||
|
||||
Detailed information on DNS registrations can be obtained with `weave
|
||||
status dns`:
|
||||
|
||||
````
|
||||
$ weave status dns
|
||||
one 10.32.0.1 eebd81120ee4 4a:0f:f6:ec:1c:93
|
||||
one 10.43.255.255 4fcec78d2a9b 66:c4:47:c6:65:bf
|
||||
one 10.40.0.0 bab69d305cba ba:98:d0:37:4f:1c
|
||||
three 10.32.0.3 7615b6537f74 4a:0f:f6:ec:1c:93
|
||||
three 10.44.0.1 c0b39dc52f8d 66:c4:47:c6:65:bf
|
||||
three 10.40.0.2 8a9c2e2ef00f ba:98:d0:37:4f:1c
|
||||
two 10.32.0.2 83689b8f34e0 4a:0f:f6:ec:1c:93
|
||||
two 10.44.0.0 7edc306cb668 66:c4:47:c6:65:bf
|
||||
two 10.40.0.1 68a5e9c2641b ba:98:d0:37:4f:1c
|
||||
````
|
||||
|
||||
The columns are as follows:
|
||||
|
||||
* Unqualified hostname
|
||||
* IPv4 address
|
||||
* Registering entity identifier (typically a container ID)
|
||||
* Name of peer from which the registration originates
|
||||
|
||||
### <a name="weave-report"></a>JSON report
|
||||
|
||||
$ weave report
|
||||
|
||||
Produces a comprehensive dump of the internal state of the router,
|
||||
IPAM and DNS services in JSON format, including all the information
|
||||
available from the `weave status` commands. You can also supply a
|
||||
Golang text template to `weave report` in a similar fashion to `docker
|
||||
inspect`:
|
||||
|
||||
$ weave report -f '{{{.DNS.Domain}}'
|
||||
weave.local.
|
||||
|
||||
### <a name="list-attached-containers"></a>List attached containers
|
||||
|
||||
|
||||
+10
-10
@@ -247,25 +247,25 @@ DNS:
|
||||
````
|
||||
...
|
||||
|
||||
WeaveDNS (d6:1b:df:42:af:7d)
|
||||
listening on port 53, for domain weave.local.
|
||||
response ttl 30
|
||||
|
||||
1d836dea2c56: foo.weave.local. [10.2.2.1]
|
||||
Service: dns
|
||||
Domain: weave.local.
|
||||
TTL: 1
|
||||
Entries: 9
|
||||
|
||||
...
|
||||
````
|
||||
|
||||
The first section covers the router; see the [troubleshooting
|
||||
guide](troubleshooting.html#status-report) for more detail.
|
||||
guide](troubleshooting.html#weave-status) for more detail.
|
||||
|
||||
The second section is pertinent to weaveDNS, and includes:
|
||||
The 'Service: dns' section is pertinent to weaveDNS, and includes:
|
||||
|
||||
* Port on which the DNS server is listening
|
||||
* The local domain suffix which is being served
|
||||
* The response ttl
|
||||
* The names known to the weaveDNS server. Each entry comprises
|
||||
the container ID, IP address and its fully qualified domain name
|
||||
* The total number of entries
|
||||
|
||||
You may also use `weave status dns` to obtain a [complete
|
||||
dump](troubleshooting.html#weave-status-dns) of all DNS registrations.
|
||||
|
||||
Information on the processing of queries, and the general operation of
|
||||
weaveDNS, can be obtained from the container logs with
|
||||
|
||||
@@ -6,7 +6,8 @@ C1=10.2.1.4
|
||||
C2=10.2.1.7
|
||||
|
||||
direct_peers() {
|
||||
weave_on $1 status | sed -n -e 's|^Direct Peers: \(.*\)|\1|p' | tr -s ' ' '\n'
|
||||
weave_on $1 report -f "{{range .Router.Targets}}{{.}}
|
||||
{{end}}"
|
||||
}
|
||||
|
||||
assert_peers() {
|
||||
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#! /bin/bash
|
||||
|
||||
. ./config.sh
|
||||
|
||||
UNIVERSE=10.2.0.0/16
|
||||
|
||||
start_suite "weave status/report"
|
||||
|
||||
weave_on $HOST1 launch --ipalloc-range $UNIVERSE --name 8a:3e:3e:3e:3e:3e --nickname nicknamington
|
||||
|
||||
check() {
|
||||
assert "weave_on $HOST1 status | grep -oP '(?<= $1: ).*'" "$3"
|
||||
assert "weave_on $HOST1 report -f '$2'" "$3"
|
||||
}
|
||||
|
||||
check "Name" "{{.Router.Name}}({{.Router.NickName}})" "8a:3e:3e:3e:3e:3e(nicknamington)"
|
||||
check "Peers" "{{len .Router.Peers}}" "1"
|
||||
check "DefaultSubnet" "{{.IPAM.DefaultSubnet}}" $UNIVERSE
|
||||
check "Domain" "{{.DNS.Domain}}" "weave.local."
|
||||
|
||||
assert_raises "weave_on $HOST1 status peers | grep nicknamington"
|
||||
weave_on $HOST1 connect 10.2.2.1
|
||||
assert "weave_on $HOST1 status targets" "10.2.2.1"
|
||||
assert "weave_on $HOST1 status connections | tr -s ' ' | cut -d ' ' -f 2" "10.2.2.1:6783"
|
||||
start_container $HOST1 --name test
|
||||
assert "weave_on $HOST1 status dns | tr -s ' ' | cut -d ' ' -f 1" "test"
|
||||
assert_raises "weave_on $HOST1 report | grep nicknamington"
|
||||
|
||||
end_suite
|
||||
@@ -45,7 +45,8 @@ weave env
|
||||
weave config
|
||||
weave connect [--replace] [<peer> ...]
|
||||
weave forget <peer> ...
|
||||
weave status
|
||||
weave status [targets | connections | peers | dns]
|
||||
weave report [-f <format>]
|
||||
weave run [--with-dns | --without-dns] [<addr> ...]
|
||||
<docker run args> ...
|
||||
weave start [<addr> ...] <container_id>
|
||||
@@ -1323,14 +1324,29 @@ case "$COMMAND" in
|
||||
;;
|
||||
status)
|
||||
res=0
|
||||
call_weave GET /status || res=1
|
||||
echo
|
||||
if check_running $PROXY_CONTAINER_NAME 2>/dev/null ; then
|
||||
OMIT_PROXY=
|
||||
STATUS_URL="/status"
|
||||
while [ $# -gt 0 ] ; do
|
||||
OMIT_PROXY=1
|
||||
STATUS_URL="$STATUS_URL/$1"
|
||||
shift
|
||||
done
|
||||
call_weave GET $STATUS_URL || res=1
|
||||
if [ -z "$OMIT_PROXY" ] && check_running $PROXY_CONTAINER_NAME 2>/dev/null ; then
|
||||
echo
|
||||
echo "weave proxy is running"
|
||||
echo " Service: proxy"
|
||||
echo " Address: $(proxy_addr)"
|
||||
fi
|
||||
[ $res -eq 0 ]
|
||||
;;
|
||||
report)
|
||||
if [ $# -gt 0 ] ; then
|
||||
[ $# -eq 2 -a "$1" = "-f" ] || usage
|
||||
call_weave GET /report --get --data-urlencode "format=$2"
|
||||
else
|
||||
call_weave GET /report -H 'Accept: application/json'
|
||||
fi
|
||||
;;
|
||||
run)
|
||||
dns_args "$@"
|
||||
shift $(dns_arg_count "$@")
|
||||
|
||||
Reference in new issue
Block a user