lncli: Make output of lncli sane and readable

LIGHT-133, LIGHT-138 Make output of `lncli showrouting table` in
two different formats: table and json.
Instead of sending serialized routing table send list of channels.
This commit is contained in:
BitfuryLightning
2016-08-20 16:49:35 -04:00
parent d764493d25
commit b5f07ede46
5 changed files with 272 additions and 109 deletions

View File

@@ -13,6 +13,9 @@ import (
"github.com/roasbeef/btcd/wire"
"github.com/urfave/cli"
"golang.org/x/net/context"
"github.com/BitfuryLightning/tools/rt"
"github.com/BitfuryLightning/tools/rt/graph/prefix_tree"
"github.com/BitfuryLightning/tools/rt/graph"
)
// TODO(roasbeef): cli logic for supporting both positional and unix style
@@ -525,7 +528,19 @@ func sendPaymentCommand(ctx *cli.Context) error {
var ShowRoutingTableCommand = cli.Command{
Name: "showroutingtable",
Description: "shows routing table for a node",
Action: showRoutingTable,
Usage: "showroutingtable [--table]",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "table",
Usage: "Show the routing table in table format. Print only a few first symbols of id",
},
cli.BoolFlag{
Name: "human",
Usage: "Simplify output to human readable form. Output lightning_id partially. Only work with --table option.",
},
},
Action: showRoutingTable,
}
func showRoutingTable(ctx *cli.Context) error {
@@ -537,7 +552,114 @@ func showRoutingTable(ctx *cli.Context) error {
if err != nil {
return err
}
printRespJson(resp)
// TODO(mkl): maybe it is better to print output directly omitting
// conversion to RoutingTable. This part is not performance critical so
// I think it is ok because it enables code reuse
r := rt.NewRoutingTable()
for _, channel := range resp.Channels {
r.AddChannel(
graph.NewID(channel.Id1),
graph.NewID(channel.Id2),
graph.NewEdgeID(channel.EdgeID),
&rt.ChannelInfo{channel.Capacity, channel.Weight},
)
}
if err != nil {
fmt.Println("Can't unmarshall routing table")
return err
}
if ctx.Bool("table") {
printRTAsTable(r, ctx.Bool("human"))
} else {
printRTAsJSON(r)
}
return nil
}
// Prints routing table in human readable table format
func printRTAsTable(r *rt.RoutingTable, humanForm bool) {
// Minimum length of data part to which name can be shortened
var minLen int
var tmpl string
var lightningIdTree, edgeIdTree prefix_tree.PrefixTree
if humanForm {
tmpl = "%-10v %-10v %-10v %-10v %-10v\n"
minLen = 6
} else {
tmpl = "%-64v %-64v %-66v %-10v %-10v\n"
minLen = 100
}
fmt.Printf(tmpl, "ID1", "ID2", "EdgeID", "Capacity", "Weight")
channels := r.AllChannels()
if humanForm {
// Generate prefix tree for shortcuts
lightningIdTree = prefix_tree.NewPrefixTree()
for _, node := range r.Nodes() {
lightningIdTree.Add(hex.EncodeToString([]byte(node.String())))
}
edgeIdTree = prefix_tree.NewPrefixTree()
for _, channel := range channels {
edgeIdTree.Add(channel.EdgeID.String())
}
}
for _, channel := range channels {
var source, target, edgeId string
sourceHex := hex.EncodeToString([]byte(channel.Id1.String()))
targetHex := hex.EncodeToString([]byte(channel.Id2.String()))
edgeIdRaw := channel.EdgeID.String()
if humanForm {
source = getShortcut(lightningIdTree, sourceHex, minLen)
target = getShortcut(lightningIdTree, targetHex, minLen)
edgeId = getShortcut(edgeIdTree, edgeIdRaw, minLen)
} else {
source = sourceHex
target = targetHex
edgeId = edgeIdRaw
}
fmt.Printf(tmpl, source, target, edgeId, channel.Info.Cpt, channel.Info.Wgt)
}
}
func getShortcut(tree prefix_tree.PrefixTree, s string, minLen int) string {
s1, err := tree.Shortcut(s)
if err != nil || s == s1 {
return s
}
if len(s1) < minLen && minLen < len(s) {
s1 = s[:minLen]
}
shortcut := fmt.Sprintf("%v...", s1)
if len(shortcut) >= len(s) {
shortcut = s
}
return shortcut
}
func printRTAsJSON(r *rt.RoutingTable) {
type ChannelDesc struct {
ID1 string `json:"lightning_id1"`
ID2 string `json:"lightning_id2"`
EdgeId string `json:"edge_id"`
Capacity float64 `json:"capacity"`
Weight float64 `json:"weight"`
}
var channels struct {
Channels []ChannelDesc `json:"channels"`
}
channelsRaw := r.AllChannels()
channels.Channels = make([]ChannelDesc, 0, len(channelsRaw))
for _, channelRaw := range channelsRaw {
sourceHex := hex.EncodeToString([]byte(channelRaw.Id1.String()))
targetHex := hex.EncodeToString([]byte(channelRaw.Id2.String()))
channels.Channels = append(channels.Channels,
ChannelDesc{
ID1: sourceHex,
ID2: targetHex,
EdgeId: channelRaw.EdgeID.String(),
Weight: channelRaw.Info.Weight(),
Capacity: channelRaw.Info.Capacity(),
},
)
}
printRespJson(channels)
}