Skip to content

Commit

Permalink
json output
Browse files Browse the repository at this point in the history
  • Loading branch information
oncilla committed Apr 15, 2020
1 parent c2bc6d4 commit ba3e9df
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 120 deletions.
4 changes: 3 additions & 1 deletion go/pkg/showpaths/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"options.go",
"config.go",
"showpaths.go",
],
importpath = "github.com/scionproto/scion/go/pkg/showpaths",
visibility = ["//visibility:public"],
deps = [
"//go/lib/addr:go_default_library",
"//go/lib/common:go_default_library",
"//go/lib/sciond:go_default_library",
"//go/lib/sciond/pathprobe:go_default_library",
"//go/lib/serrors:go_default_library",
"//go/lib/snet:go_default_library",
"//go/lib/snet/addrutil:go_default_library",
],
)
54 changes: 54 additions & 0 deletions go/pkg/showpaths/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2020 Anapaya Systems
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package showpaths

import (
"net"

"github.com/scionproto/scion/go/lib/sciond"
)

// DefaultMaxPaths is the maximum number of paths that are displayed by default.
const DefaultMaxPaths = 10

// Config configures the showpath run.
type Config struct {
// Local configures the local IP address to use. If this option is not provided,
// a local IP that can reach SCION hosts is selected with the help of the kernel.
Local net.IP
// SCIOND configures a specific SCION Deamon address.
SCIOND string
// MaxPaths configures the maximum number of displayed paths. If this option is
// not provided, the DefaultMaxPaths is used.
MaxPaths int
// ShowExpiration configures whether the expiration is displayed.
ShowExpiration bool
// Refresh configures whether sciond is queried with the refresh flag.
Refresh bool
// Probe configures whether the path status is probed and displayed.
Probe bool
// JSON configures whether the output is written as json.
JSON bool
}

// InitDefaults initializes the default values.
func (cfg *Config) InitDefaults() {
if cfg.SCIOND == "" {
cfg.SCIOND = sciond.DefaultSCIONDAddress
}
if cfg.MaxPaths == 0 {
cfg.MaxPaths = DefaultMaxPaths
}
}
81 changes: 0 additions & 81 deletions go/pkg/showpaths/options.go

This file was deleted.

74 changes: 60 additions & 14 deletions go/pkg/showpaths/showpaths.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,25 @@ package showpaths

import (
"context"
"encoding/json"
"fmt"
"net"
"os"
"strings"
"time"

"github.com/scionproto/scion/go/lib/addr"
"github.com/scionproto/scion/go/lib/common"
"github.com/scionproto/scion/go/lib/sciond"
"github.com/scionproto/scion/go/lib/sciond/pathprobe"
"github.com/scionproto/scion/go/lib/serrors"
"github.com/scionproto/scion/go/lib/snet"
"github.com/scionproto/scion/go/lib/snet/addrutil"
)

// Run lists the paths to the specified ISD-AS to stdout.
func Run(ctx context.Context, dst addr.IA, options ...Option) error {
opts := invokeOptions(options)

sdConn, err := sciond.NewService(opts.sciond).Connect(ctx)
func Run(ctx context.Context, dst addr.IA, cfg Config) error {
sdConn, err := sciond.NewService(cfg.SCIOND).Connect(ctx)
if err != nil {
return serrors.WrapStr("error connecting to SCIOND", err)
}
Expand All @@ -44,22 +47,21 @@ func Run(ctx context.Context, dst addr.IA, options ...Option) error {
// possibility to have the same functionality, i.e. refresh, fetch all paths.
// https://github.com/scionproto/scion/issues/3348
paths, err := sdConn.Paths(ctx, dst, addr.IA{},
sciond.PathReqFlags{Refresh: opts.refresh, PathCount: uint16(opts.maxPaths)})
sciond.PathReqFlags{Refresh: cfg.Refresh, PathCount: uint16(cfg.MaxPaths)})
if err != nil {
return serrors.WrapStr("failed to retrieve paths from SCIOND", err)
}

fmt.Println("Available paths to", dst)
var pathStatuses map[string]pathprobe.Status
if opts.probe {
localIP := opts.local
var statuses map[string]pathprobe.Status
if cfg.Probe {
localIP := cfg.Local
if localIP == nil {
localIP, err = findDefaultLocalIP(ctx, sdConn)
if err != nil {
return serrors.WrapStr("failed to determine local IP", err)
}
}
pathStatuses, err = pathprobe.Prober{
statuses, err = pathprobe.Prober{
DstIA: dst,
LocalIA: localIA,
LocalIP: localIP,
Expand All @@ -68,18 +70,62 @@ func Run(ctx context.Context, dst addr.IA, options ...Option) error {
serrors.WrapStr("failed to get status", err)
}
}
if cfg.JSON {
return machine(paths, statuses, cfg.ShowExpiration)
}
fmt.Println("Available paths to", dst)
human(paths, statuses, cfg.ShowExpiration)
return nil
}

func human(paths []snet.Path, statuses map[string]pathprobe.Status, showExpiration bool) {
for i, path := range paths {
fmt.Printf("[%2d] %s", i, fmt.Sprintf("%s", path))
if opts.expiration {
if showExpiration {
fmt.Printf(" Expires: %s (%s)", path.Expiry(),
time.Until(path.Expiry()).Truncate(time.Second))
}
if pathStatuses != nil {
fmt.Printf(" Status: %s", pathStatuses[pathprobe.PathKey(path)])
if statuses != nil {
fmt.Printf(" Status: %s", statuses[pathprobe.PathKey(path)])
}
fmt.Printf("\n")
}
return nil
}

func machine(paths []snet.Path, statuses map[string]pathprobe.Status, showExpiration bool) error {
type Hop struct {
IfID common.IFIDType `json:"ifid"`
IA addr.IA `json:"isd_as"`
}
type Path struct {
Fingerprint string `json:"fingerprint"`
Hops []Hop `json:"hops"`
NextHop string `json:"next_hop"`
Expiry time.Time `json:"expiry"`
MTU uint16 `json:"mtu"`
Status string `json:"status,omitempty"`
StatusInfo string `json:"status_info,omitempty"`
}
jpaths := make([]Path, 0, len(paths))
for _, path := range paths {
jpath := Path{
Fingerprint: path.Fingerprint().String()[:16],
NextHop: path.OverlayNextHop().String(),
Expiry: path.Expiry(),
MTU: path.MTU(),
}
for _, hop := range path.Interfaces() {
jpath.Hops = append(jpath.Hops, Hop{IA: hop.IA(), IfID: hop.ID()})
}
if status, ok := statuses[pathprobe.PathKey(path)]; ok {
jpath.Status = strings.ToLower(string(status.Status))
jpath.StatusInfo = status.AdditionalInfo
}
jpaths = append(jpaths, jpath)
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
return enc.Encode(jpaths)
}

// TODO(matzf): this is a simple, hopefully temporary, workaround to not having
Expand Down
2 changes: 1 addition & 1 deletion go/scion/scion.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (

func main() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
fmt.Fprintf(os.Stderr, "Error: %s\n", err)
os.Exit(1)
}
}
Expand Down
29 changes: 18 additions & 11 deletions go/scion/showpaths.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,25 @@ var showpathsFlags struct {
expiration bool
refresh bool
probe bool
json bool
local net.IP
}

var showpathsCmd = &cobra.Command{
Use: "showpaths",
Short: "A clean-slate internet architecture",
Short: "Display paths to a SCION AS",
Args: cobra.ExactArgs(1),
Example: ` scion showpaths 1-ff00:0:110 --probe --expiration
scion showpaths 1-ff00:0:110 --probe --json
scion showpaths 1-ff00:0:110 --local 127.0.0.55`,
Long: `'showpaths' lists available paths between the local and the specified SCION ASe a.
By default, the paths are not probed. As paths might be served from the SCION Deamon's
cache, they might not forward traffic successfully (e.g. if a network link went down).
To list the paths with their health statuses, specify that the paths should be probed
through the flag.
'showpaths' can be instructed to output the paths as json using the the --json flag.
`,
RunE: func(cmd *cobra.Command, args []string) error {
dst, err := addr.IAFromString(args[0])
Expand All @@ -62,17 +68,16 @@ through the flag.

ctx, cancel := context.WithTimeout(context.Background(), showpathsFlags.timeout)
defer cancel()
opts := []showpaths.Option{
showpaths.SCIOND(showpathsFlags.sciond),
showpaths.MaxPaths(showpathsFlags.maxPaths),
showpaths.ShowExpiration(showpathsFlags.expiration),
showpaths.Refresh(showpathsFlags.refresh),
showpaths.Probe(showpathsFlags.probe),
}
if showpathsFlags.local != nil {
opts = append(opts, showpaths.Local(showpathsFlags.local))
cfg := showpaths.Config{
Local: showpathsFlags.local,
SCIOND: showpathsFlags.sciond,
MaxPaths: showpathsFlags.maxPaths,
ShowExpiration: showpathsFlags.expiration,
Refresh: showpathsFlags.refresh,
Probe: showpathsFlags.probe,
JSON: showpathsFlags.json,
}
if err := showpaths.Run(ctx, dst, opts...); err != nil {
if err := showpaths.Run(ctx, dst, cfg); err != nil {
return err
}
return nil
Expand All @@ -92,6 +97,8 @@ func init() {
"Set refresh flag for SCION Deamon path request")
showpathsCmd.Flags().BoolVarP(&showpathsFlags.probe, "probe", "p", false,
"Probe the paths and print the health status")
showpathsCmd.Flags().BoolVarP(&showpathsFlags.probe, "json", "j", false,
"Write the output as machine readable json")
showpathsCmd.Flags().IPVarP(&showpathsFlags.local, "local", "l", nil,
"Optional local IP address to use for probing health checks")
}
23 changes: 11 additions & 12 deletions go/tools/showpaths/paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ var (
maxPaths = flag.Int("maxpaths", 10, "Maximum number of paths")
expiration = flag.Bool("expiration", false, "Show path expiration timestamps")
refresh = flag.Bool("refresh", false, "Set refresh flag for SCIOND path request")
json = flag.Bool("json", false, "Write output as machine readable json")
status = flag.Bool("p", false, "Probe the paths and print out the statuses")
localIPStr = flag.String("local", "", "(Optional) local IP address to use for health checks")
version = flag.Bool("version", false, "Output version information and exit.")
Expand Down Expand Up @@ -66,19 +67,17 @@ func main() {

ctx, cancelF := context.WithTimeout(context.Background(), *timeout)
defer cancelF()

opts := []showpaths.Option{
showpaths.SCIOND(*sciondAddr),
showpaths.MaxPaths(*maxPaths),
showpaths.ShowExpiration(*expiration),
showpaths.Refresh(*refresh),
showpaths.Probe(*status),
}
if localIP != nil {
opts = append(opts, showpaths.Local(localIP))
cfg := showpaths.Config{
Local: localIP,
SCIOND: *sciondAddr,
MaxPaths: *maxPaths,
ShowExpiration: *expiration,
Refresh: *refresh,
Probe: *status,
JSON: *json,
}
if err := showpaths.Run(ctx, dstIA, opts...); err != nil {
fmt.Fprintf(os.Stderr, "Error: %s", err)
if err := showpaths.Run(ctx, dstIA, cfg); err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", err)
os.Exit(1)
}
}
Expand Down

0 comments on commit ba3e9df

Please sign in to comment.